-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 chromatic via RT wrote: > I can't quite get this to apply cleanly, and I have some concern that > the memory allocated for argv doesn't get freed appropriately. If you > can make the patch apply cleanly, I'm happy to work on the memory parts.
Hi chromatic, I'm not sure if it is really needed to extend this function. When I was examining IO first I was not aware of the opportunities of the NCI. Meanwhile I have written parrot-functions using NCI clib functions to handle reading from programs (on UNIX platforms). Have a look at readFromProgram* methods in attachment. Perhaps I should add these in some way to the parrot runtime library. If you think the first extension I wrote for PIO_unix_pipe() is still useful, I will make the patch apply cleanly. Regards, Kiwi -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.3 (GNU/Linux) Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org iD8DBQFGiMRDHSiAp6bcIcgRAkNaAKCYHrajDJP+YIwDReOEUmr41ziOjwCfUhg0 3pmDXAuwqcFEM2r80ipPybA= =b0SM -----END PGP SIGNATURE-----
=head1 NAME Wicked::IO -- Class for basic IO =cut =head1 SYNOPSIS =cut =head1 DESCRIPTION Wicked::IO handles basic IO operations like checking existance of a file, reading and writing files. All paths used in these operations are interpreted to be relative to a working directory which you can set and get via C<workDir()> method. For most operations you will fetch a singleton Object: .include('include/constructors.pir') ... .singletonObject(io, 'Wicked::IO') olddir = io.workDir('new/', 'workingdir') io.write('foo', 'bar') io.workDir(olddir) But you can create your own instance of Wicked::IO class as well. Each instance has its own private working directory. NOTE: This class should inherit from parrot classes "Os" and "File" =cut =head2 METHODS =cut .include 'include/require.pir' .include 'include/log.pir' .include 'include/constructors.pir' .include 'include/p6regex.pir' .include 'include/normalize_path.pir' .include 'include/modifiers.pir' .include 'include/trace.pir' .sub __onload :load .require('Wicked::Class') $P0 = newclass 'Wicked::IO' addattribute $P0, 'workdir' addattribute $P0, 'os' .end .namespace ['Wicked::IO'] =item C<onInit> Initializes class. =cut .sub onInit :vtable('init') :method startTracedFunction(">> Wicked::IO::__init()\n") $P0 = new .String $P0 = './' setattribute self, 'workdir', $P0 $P0 = new OS setattribute self, 'os', $P0 $P0.umask(0002) endTracedFunction(">> Wicked::IO::__init()\n") .end .sub onInitPmc :vtable('init_pmc') :method .param pmc adverbs self.onInit() .end =item C<workDir([string WORKDIR])> This method sets or gets C<WORKDIR>. =cut .sub workDir :method .param pmc _workdir :slurpy .local string result startTracedFunction(">> Wicked::IO::workDir()\n") $P0 = getattribute self, 'workdir' $I0 = elements _workdir if $I0 goto SET_WORKDIR result = $P0 goto RETURN SET_WORKDIR: .local string wd wd = join '', _workdir wd .= '/' .normalize_path(wd) result = $P0 $P0 = wd RETURN: endTracedFunction("<< Wicked::IO::workDir() set workdir to '", wd, "'\n") .return(result) .end =item C<read(string FILENAME)> This function reads a file and returns its contents. C<FILENAME> will be interpreted to be relative to C<workdir>. No security checks. =cut .sub read :method .param string filename .local pmc fh startTracedFunction(">> Wicked::IO::read('", filename, "')\n") unless filename == '-' goto OPEN_FILE fh = getstdin goto READ_FILE OPEN_FILE: $S0 = self.workDir() $S0 .= filename .normalize_path($S0) fh = open $S0, '<' $I0 = defined fh if $I0 goto READ_FILE $P1 = new_exception("Could not open file '", $S0, "' for read.") throw $P1 READ_FILE: $S1 = '' READ_NEXT: $S0 = read fh, 4096 $I0 = bytelength $S0 unless $I0 > 0 goto DONE $S1 .= $S0 goto READ_NEXT DONE: close fh endTracedFunction("<< Wicked::IO::read('", filename, "')\n") .return($S1) .end =item C<write(string FILENAME, string CONTENT)> This method writes C<CONTENT> to C<FILENAME>. =cut .sub write :method .param string filename .param string content startTracedFunction(">> Wicked::IO::write('", filename, "', <CONTENT>)\n") $S0 = self.workDir() $S0 .= filename .normalize_path($S0) $P0 = open $S0, '>' $I0 = defined $P0 if $I0 goto WRITE $P1 = new_exception("Could not open file '", $S0, "' for write.") throw $P1 WRITE: print $P0, content close $P0 endTracedFunction("<< Wicked::IO::write('", filename, "')\n") .end =item C<append(string FILENAME, string CONTENT)> This method appends C<CONTENT> to C<FILENAME>. =cut .sub append :method .param string filename .param string content startTracedFunction(">> Wicked::IO::append('", filename, "', <CONTENT>)\n") $S0 = self.workDir() $S0 .= filename .normalize_path($S0) APPEND: $P0 = open $S0, '>>' $I0 = defined $P0 if $I0 goto WRITE $P1 = new_exception("Could not open file '", $S0, "'for append.") throw $P1 WRITE: print $P0, content close $P0 endTracedFunction("<< Wicked::IO::append('", filename, "')\n") .end =item C<exists FILENAME> This method checks if C<FILENAME> exists. C<FILENAME> may be a list which will be concatenated internally. =cut .sub exists :method .param pmc fn :slurpy startTracedFunction(">> Wicked::IO::exists()\n") $S0 = self.workDir() $S1 = join '', fn $S0 = $S0 . $S1 $I0 = stat $S0, 0 endTracedFunction("<< Wicked::IO::exists() file '", $S0, "' exists? result=", $I0, "\n") .return($I0) .end =item C<mkdir PATH> This method will create a new directory specified by C<PATH>. C<PATH> may be a list which will be concatenated internally. =cut .sub mkdir :method .param pmc pn :slurpy .local string path path = join '', pn startTracedFunction(">> Wicked::IO::mkdir('", path, "')\n") $S0 = self.workDir() path = $S0 . path MKDIR: .normalize_path(path) log_trace("assert path: ", path, "\n") $P0 = split '/', path $I0 = 0 $I1 = elements $P0 $S1 = '' NEXT_DIR: unless $I0 < $I1 goto END_DIR $S0 = $P0[$I0] inc $I0 $S1 .= $S0 $S1 .= '/' # check if file exists $I2 = stat $S1, 0 unless $I2 goto DO_MKDIR # check if file is a directory $I2 = stat $S1, 2 if $I2 goto NEXT_DIR $P1 = new_exception("'", $S1, "' is no directory") throw $P1 DO_MKDIR: $P1 = getattribute self, 'os' log_trace("mkdir: ", $S1, "\n") $P1.'mkdir'($S1, 504) # this is 770 goto NEXT_DIR END_DIR: endTracedFunction("<< Wicked::IO::mkdir('", path, "')\n") .end =item C<rm PATH> This method removes a file or directory (like unlink()) =cut .sub rm :method .param string path startTracedFunction(">> Wicked::IO::rm('", path, "')\n") $S0 = self.workDir() path = $S0 . path .normalize_path(path) $P0 = getattribute self, 'os' $P0.'rm'(path) endTracedFunction("<< Wicked::IO::rm('", path, "')\n") .end =item C<readDir PATH> This method reads content of a directory. =cut .sub readDir :method .param string path startTracedFunction(">> Wicked::IO::readDir('", path, "')\n") $S0 = self.workDir() path = $S0 . path .normalize_path(path) log_trace("path: ", path, "\n") $P0 = getattribute self, 'os' $P1 = $P0.readdir(path) endTracedFunction("<< Wicked::IO::readDir('", path, "')\n") .return($P1) .end .sub lstat :method .param string path startTracedFunction(">> Wicked::IO::lstat('", path, "')\n") .getMyStringAttribute($S0, 'workdir') path = $S0 . path .normalize_path(path) $P0 = getattribute self, 'os' $P1 = $P0.lstat(path) endTracedFunction("<< Wicked::IO::lstat('", path, "')\n") .return($P1) .end .sub link :method .param string target .param string ref startTracedFunction(">> Wicked::IO::link('", target, "', '", ref, "')\n") .getMyStringAttribute($S0, 'workdir') target = $S0 . target .normalize_path(target) ref = $S0 . ref .normalize_path(ref) $P0 = getattribute self, 'os' $P1 = $P0.link(target, ref) endTracedFunction("<< Wicked::IO::link()\n") .end .include 'datatypes.pasm' .include 'stdio.pasm' =item C<readFromProgram(COMMAND)> OUTPUT = io.readFromProgram(COMMAND) (OUTPUT, RETCODE) = io.readFromProgram(COMMAND) =cut .sub readFromProgram :method .param pmc _cmd :slurpy .local pmc libc, xexecv, xfork, xclose, xdup, xpipe, xread, xwrite, xwaitpid .local int pid .local string cmd .local pmc argv startTracedFunction(">> Wicked::IO::readFromProgram()\n") $N0 = time # change to working directory .local pmc os .local string prev_dir os = getattribute self, 'os' .getMyStringAttribute($S0, 'workdir') # assuming $S0 is normalized. prev_dir = clone $S0 .pggsub(prev_dir, 'no_slashes', '..') log_trace('chdir: ', $S0, "\n") os.chdir($S0) $S10 = os.cwd() log_trace('CWD: ', $S10, "\n") null libc #xfork = dlfunc libc, 'fork', 'iv' xfork = dlfunc libc, 'fork', 'i' xpipe = dlfunc libc, 'pipe', 'ip' xdup = dlfunc libc, 'dup', 'ii' xread = dlfunc libc, 'read', 'iibi' xclose = dlfunc libc, 'close', 'ii' xexecv = dlfunc libc, 'execv', 'itp' xwaitpid = dlfunc libc, 'waitpid', 'ii3i' log_trace("required dlfuncs\n") $P0 = new .ResizablePMCArray push $P0, .DATATYPE_INT push $P0, 2 push $P0, 0 .local pmc fds fds = new .ManagedStruct, $P0 $I0 = xpipe(fds) unless $I0 < 0 goto FORK # error FORK: .local int read_end, write_end read_end = fds[0;0] write_end = fds[0;1] pid = xfork() unless pid goto CHILD PARENT: .local string buf, result .local int bufsize xclose(write_end) bufsize = 4096 # prepare buffer. (if buf is not initialized, it won't be filled.) buf = repeat "\x0", bufsize result = '' READ_NEXT: pin buf $I0 = xread(read_end, buf, bufsize) unpin buf unless $I0 > 0 goto READ_END $S0 = substr buf, 0, $I0 result .= $S0 goto READ_NEXT READ_END: log_trace("Program's output:\n", result, "\n") .local int retcode xclose(read_end) $P0 = new .Integer $I0 = xwaitpid(pid, $P0, 0) retcode = $P0 retcode >>= 8 log_trace("child's status: ", $P0, ", retcode: ", retcode, "\n") log_trace('chdir: ', prev_dir, "\n") os.chdir(prev_dir) $N1 = time $N1 -= $N0 $S99 = join '', _cmd log_info("external program '", $S99, "' took ", $N1, "s\n") endTracedFunction("<< Wicked::IO::readFromProgram()\n") .return(result, retcode) CHILD: xclose(read_end) xclose(.PIO_STDOUT_FILENO) $I0 = xdup(write_end) # parse arguments .require('Wicked::Regexes') $S99 = join '', _cmd $P0 = get_hll_global [ 'Wicked::Regexes' ], 'cmdline' log_trace("cmd: '", $S99, "'\n") $P0 = $P0($S99) # get cmd name cmd = $P0['cmd'] cmd .= "\x0" # set argv .local pmc arr arr = $P0[0] $P0 = new .ResizablePMCArray push $P0, .DATATYPE_CSTR $I0 = arr $I0 += 2 # cmd + args + 1 = 1 + args + 1 push $P0, $I0 push $P0, 0 argv = new .ManagedStruct, $P0 argv[0;0] = cmd .local int i, count .local string s i = 0 count = arr ARG_NEXT: unless i < count goto ARG_END s = arr[i] inc i $S1 = s[0] if $S1 == '"' goto ARG_STRING if $S1 == "'" goto ARG_STRING ARG_SET: # string has to be \0 terminated s .= "\x0" # Yes, we have to use incremented i because argv[0;0] has been set # before this loop argv[0;i] = s goto ARG_NEXT ARG_STRING: $I0 = length s $I0 -= 2 s = substr s, 1, $I0 goto ARG_SET ARG_END: $I0 = xexecv(cmd, argv) .end .sub readFromProgramWithErr :method .param pmc _cmd :slurpy .local pmc libc, xexecv, xfork, xclose, xdup, xpipe, xread, xwrite, xwaitpid .local int pid .local string cmd .local pmc argv startTracedFunction(">> Wicked::IO::readFromProgram()\n") $N0 = time # change to working directory .local pmc os .local string prev_dir os = getattribute self, 'os' .getMyStringAttribute($S0, 'workdir') # assuming $S0 is normalized. prev_dir = clone $S0 .pggsub(prev_dir, 'no_slashes', '..') log_trace('chdir: ', $S0, "\n") os.chdir($S0) $S10 = os.cwd() log_trace('CWD: ', $S10, "\n") null libc #xfork = dlfunc libc, 'fork', 'iv' xfork = dlfunc libc, 'fork', 'i' xpipe = dlfunc libc, 'pipe', 'ip' xdup = dlfunc libc, 'dup', 'ii' xread = dlfunc libc, 'read', 'iibi' xclose = dlfunc libc, 'close', 'ii' xexecv = dlfunc libc, 'execv', 'itp' xwaitpid = dlfunc libc, 'waitpid', 'ii3i' log_trace("required dlfuncs\n") $P0 = new .ResizablePMCArray push $P0, .DATATYPE_INT push $P0, 2 push $P0, 0 .local pmc fds fds = new .ManagedStruct, $P0 $I0 = xpipe(fds) unless $I0 < 0 goto FORK # error FORK: .local int read_end, write_end read_end = fds[0;0] write_end = fds[0;1] pid = xfork() unless pid goto CHILD PARENT: .local string buf, result .local int bufsize xclose(write_end) bufsize = 4096 # prepare buffer. (if buf is not initialized, it won't be filled.) buf = repeat "\x0", bufsize result = '' READ_NEXT: pin buf $I0 = xread(read_end, buf, bufsize) unpin buf unless $I0 > 0 goto READ_END $S0 = substr buf, 0, $I0 result .= $S0 goto READ_NEXT READ_END: log_trace("Program's output:\n", result, "\n") .local int retcode xclose(read_end) $P0 = new .Integer $I0 = xwaitpid(pid, $P0, 0) retcode = $P0 retcode >>= 8 log_trace("child's status: ", $P0, ", retcode: ", retcode, "\n") log_trace('chdir: ', prev_dir, "\n") os.chdir(prev_dir) endTracedFunction("<< Wicked::IO::readFromProgram()\n") $N1 = time $N1 -= $N0 $S99 = join '', _cmd log_info("external program '", $S99, "' took ", $N1, "s\n") .return(result, retcode) CHILD: xclose(read_end) xclose(.PIO_STDOUT_FILENO) xclose(.PIO_STDERR_FILENO) $I0 = xdup(write_end) $I0 = xdup(write_end) # parse arguments .require('Wicked::Regexes') $S99 = join '', _cmd log_trace("cmd: '", $S99, "'\n") $P0 = get_hll_global [ 'Wicked::Regexes' ], 'cmdline' $P0 = $P0($S99) # get cmd name cmd = $P0['cmd'] cmd .= "\x0" # set argv .local pmc arr arr = $P0[0] $P0 = new .ResizablePMCArray push $P0, .DATATYPE_CSTR $I0 = arr $I0 += 2 # cmd + args + 1 = 1 + args + 1 push $P0, $I0 push $P0, 0 argv = new .ManagedStruct, $P0 argv[0;0] = cmd .local int i, count .local string s i = 0 count = arr ARG_NEXT: unless i < count goto ARG_END s = arr[i] inc i $S1 = s[0] if $S1 == '"' goto ARG_STRING if $S1 == "'" goto ARG_STRING ARG_SET: # string has to be \0 terminated s .= "\x0" # Yes, we have to use incremented i because argv[0;0] has been set # before this loop argv[0;i] = s goto ARG_NEXT ARG_STRING: $I0 = length s $I0 -= 2 s = substr s, 1, $I0 goto ARG_SET ARG_END: $I0 = xexecv(cmd, argv) .end .sub readFromProgram_OLD :method .param pmc _cmd :slurpy .local string cmd, result .local pmc pipe startTracedFunction(">> Wicked::IO::readFromProgram()\n") $P0 = getattribute self, 'os' .getMyStringAttribute($S0, 'workdir') # assuming $S0 is normalized. $S1 = clone $S0 .pggsub($S1, 'no_slashes', '..') log_trace('chdir: ', $S0, "\n") $P0.chdir($S0) $S10 = $P0.cwd() log_trace('CWD: ', $S10, "\n") cmd = join '', _cmd log_trace("execute: '", cmd, "' for read\n") pipe = open cmd, '-|' unless_null pipe, READ_PIPE log_trace('pipe NULL: chdir: ', $S1, "\n") $P0.chdir($S1) $P1 = new_exception("Could not execute command '", cmd, "' for read.") throw $P1 READ_PIPE: # pop buf layer (strange!), see parrot's examples/io/pipe2.pir $S10 = pop pipe result = '' READ_NEXT: $S10 = read pipe, 4096 log_trace('S10: ', $S10, "\n") $I0 = bytelength $S10 result .= $S10 if pipe goto READ_NEXT DONE: close pipe log_trace('chdir: ', $S1, "\n") $P0.chdir($S1) endTracedFunction("<< Wicked::Wiki::readFromProgram() output:\n", result, "\n") .return(result) .end =head1 AUTHOR Kay-Uwe 'Kiwi' Hüll L<mailto:[EMAIL PROTECTED]> =cut # vim: et ts=4 syntax=pir foldmethod=syntax
IO.pir.sig
Description: Binary data