From Freepascal Amiga wiki
Revision as of 15:07, 15 September 2013 by Molly (talk | contribs) (Paragraph Asynch/OutPut/Error: added an extra unable possibility which intially forgotten)
Jump to navigation Jump to search

This documentation will first of all focus on running a command using the shell and doing so in an asynchrone matter. At the same time it tries to catch the output of that executed command, using the pipefs handler in AROS.

Caveats: There is little documentation at all concerning this topic, let alone for AROS in specific. So, whenever there is some documentation, you have to read that literally and fill in the blanks yourself. Hopefully this documentation is able to fill in some of those blanks.


As can be read in the AutoDocs, the SystemTagList() function provides a way to execute a command via the shell. But what is not so evident, is that there are some caveats as well as some undocumented features that are not very well described.


Let's start with the parameters that can be passed to this function.

The tags

[Insert here explaination about the tags]


Asynchronous piping with AROS using pipefs: can only be done following strict rules, not following those rules simply breaks your code.

First of all in asynchronous mode, you cannot use an exclusive lock on the pipehandle. The ideal situation for reading and writing simultanously from and into the pipehandler would require that the writing end opens the handle in shared write mode, and the recieving reading end should use a shared read mode.

A close inspection at the documentation reveals that this leaves only one single possibilty when using normal DOSOpen functionality, namely opening the writing-end in MODE_READWRITE and the reading recieving end should open the handle in MODE_OLDFILE. At first one would think that the recieving reading end could also open the handle in MODE_READWRITE (which would give the revieving end write acces to the handle as well), but there is a little caveat revealed in the pipe: documentation:

102 Pipes behave in most respects like ordinary files.  Some differences follow:
103 Pipes block for writing (i.e., the write request is suspended) when the
104 pipe's buffer is full, and block for reading when the pipe's buffer is
105 empty.  Thus, pipes are sort of like bounded ram: files.  EOF is returned
106 for reading when the pipe's buffer is empty and no process has the pipe
107 open for writing.

Which, in case one would open the receiving end in MODE_READWRITE, would render the pipe-handle unusable as the last read-command done on the pipe-handle would let that read-command wait forever until no-one (in this case the receiving-end itself) has the handle open for writing. The only way that somewhat solves this, is using the NP_ExitCode tag and in that called code, close the pipe-handle of the receiving end (in order to let the receiver's last read command 'unlock' so it can continue. But by doing so, it would also make the receiver's routine useless as it has no acces to the pipe-handler anymore. Besides that, the last read done by the recieving-end with the last read on the handle would also contain garbled values towards the end of what the last read tells that was possible to read (as some functions return how many bytes/characters were read).

How to use SystemTagList() in practise

Executing shell commands using SystemTagList():


[insert explanation here] [insert example here]

Synchrone and hidden

[insert explanation here] [insert example here]

Synchrone, hidden and catching Output

[insert explanation here] [insert example here]

Synchrone, hidden, catching Output as well as Errors

[insert explanation here] [insert example here]

Executing Asynchrone and catching Output.

[insert explanation here]

[paste example here]

  Program RunCMDoo;
    Name   : RunCMDoo V0.1
    Target : AROS ABIv0/i386
    Author : n/a
    Date   : 2013-09-15
    Goal   : Run a command using SystemTagList() and catch its output
    Usage  : RunCMDoo Command "[parameter1 parameter2 parameterN]"
    exec, amigados, utility, tagsarray;
    BPTR              = LongInt;  // Quick fix to compensate for pointer
    TRCMode           =
      rcm_output,     // Only use SYS_Output
      rcm_combined,   // Use Sys_Output and Sys_Error using the same handle.
                      // (impossible using SystemTagList() ?)
      rcm_both        // use SYS_Output and Sys_Error both using their own handle.
                      // (currently   bugs)
    CommandHasEnded   : boolean = false;
    CommandExitCode   : longint = 0;
    CommandSegList    : BPTR    = 0;
    OutPipeRead       : BPTR;
    OutPipeWrite      : BPTR;
    OutPipeName       = 'PIPEFS:CmdOut';  // Name should be randomized or use * (* = untested)
  Procedure CMDExitCode(retcode: LongInt; SegList: BPTR); cdecl;
    Writeln('Enter - MyExitCode()');
    CommandHasEnded := true;
    CommandExitCode := retcode;
    CommandSegList  := SegList;
    Writeln('MyExitCode(): exitcode =', CommandExitCode);
    Writeln('MyExitCode(): seglist  =', CommandSegList);
    Writeln('Leave - MyExitCode()');
  Procedure RunCMDOutput(CommandToRun: String);
    TagsList       : TTagsList = nil;
    Tags           : pTagItem;
    res            : LongInt;
    nread          : LongInt;
    OutPipeBuffer  : packed array[0..255] of char;
    OutPipeWrite  := DosOpen( OutPipeName , MODE_READWRITE);
    if (OutPipeWrite <> 0) then
        LONG(SYS_Input)       , nil,
        LONG(SYS_Output)      , OutPipeWrite,
        LONG(NP_CloseOutput)  , 1,
        LONG(SYS_Error)       , nil,
        LONG(SYS_Asynch)      , 1,
        LONG(SYS_BackGround)  , 1,
        LONG(NP_ExitCode)     , @CMDExitCode,
      Tags := GetTagPtr(TagsList);
      writeln('RUNCMD(): Executing SystemTagList()');
      res := SystemTagList(CommandToRun, Tags);
      If (res <> -1) then
        writeln('RUNCMD(): SystemTagList() returned value ', res);
        writeln('RUNCMD(): opening OutPipe for reading');
        OutPipeRead   := DosOpen( OutPipeName   , MODE_OLDFILE);
        if (OutPipeRead <> 0) then
          writeln('RUNCMD(): entering main loop for reading data from OutPipe');
          while true do
              writeln('RUNCMD(): start a buffer read from OutPipe');
              nread := DosRead(OutPipeRead, @OutPipeBuffer[0], 255);
              // -1 = error, 0 = EOF and >0 = number of bytes actually read.
              if (nread <> -1) then
                writeln('RUNCMD(): buffer read from OutPipe was succesfull');
                OutPipeBuffer[nread] := #0;
                if (nread < 255) then break;
                writeln('RUNCMD(): ERROR - buffer read failed, IoErr() = ', IoErr);
              // Safety check ?
              if CommandHasEnded then Break;
          writeln('RUNCMD(): exiting main loop that read data from OutPipe');
          // Close our Output Pipe reader.
        else writeln('RUNCMD(): ERROR - Failed to open pipe for read acces');
      else writeln('RUNCMD(): ERROR - Failed to execute command');
      // close outputwrite when error occurend when executing systemtags()
  Procedure RunCommand(CommandToRun: String; RCMode: TRCMode);
    writeln('enter - runcommmand');
    case RCMode of
      rcm_output   : RunCMDOutput(CommandToRun);
  //    rcm_combined : RunCMDCombined(CommandToRun);
  //    rcm_Both     : RunCMDBoth(CommandToRun);
    end; // case;
    writeln('leave - runcommmand');
   i : Integer;
   S : String = '';
    If paramcount > 0 then
      for i := 1 to paramcount
        do S := S + Paramstr(i) + ' ';
      Writeln('Trying to execute command "',S,'"');
      RunCommand(S, rcm_Output);
    else     // Show usage/examples
      Writeln('RunCMDoo v0.1');
      Writeln('  RunCMDoo Command [Parameter1 Paramere2 ParamterN]');
      Writeln('  RunCMDoo LD --help');
      Writeln('  RunCMDoo LD --wrong_parameter_on_purpose');
      Writeln('  RunCMDoo LD -v');
      Writeln('  RunCMDoo dir ram:#?');

Executing Asynchrone and catching Output as well as Error

[insert explanation here]

Current problems: Unable to determine which pipe needs to be read first, so in the end one would always end up in a deadlock because not knowing what comes first OutPut or Error. Choosing the wrong one would make you wait forever and can only be 'broken' by reading the other pipe (flushing did not help).

Unable to:

  • use WaitForChar() as pipes are not in raw mode
  • seems not possible to change the mode using SetMode() (returns error)
  • unable to use fib for size of pipe as a pipe-file is always zero

[insert example here]