-----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

Attachment: IO.pir.sig
Description: Binary data

Reply via email to