home *** CD-ROM | disk | FTP | other *** search
/ TOS Silver 2000 / TOS Silver 2000.iso / programm / MM2_DEV / DOC / ARGV.TXT next >
Encoding:
Text File  |  1990-05-03  |  19.6 KB  |  473 lines

  1. INFO-ATARI16 Digest         Thu,  2 Nov 89       Volume 89 : Issue 595
  2.  
  3. Today's Topics:
  4.                  GEMDOS Extended Argument Spec (LONG)
  5. ----------------------------------------------------------------------
  6.  
  7. Date: 1 Nov 89 19:18:30 GMT
  8. From: imagen!atari!kbad@ucbvax.Berkeley.EDU  (Ken Badertscher)
  9. Subject: GEMDOS Extended Argument Spec (LONG)
  10.  
  11. GEMDOS EXTENDED ARGUMENT (ARGV) SPECIFICATION
  12.  
  13. Introduction
  14.  
  15. The Pexec() function of GEMDOS allows a program to pass to a child
  16. process a command line up to 125 characters long, with arguments
  17. separated by spaces.  No provision is made in GEMDOS for the child to
  18. know its own name.  This makes it difficult for C programs to correctly
  19. fill in argv[0], the standard place where a C program finds the command
  20. which invoked it.  Because the command line arguments are separated by
  21. spaces, it is difficult to pass an argument with an embedded space.
  22. This document will specify a method of passing arguments which allows
  23. arbitrary argument length, embedded spaces, and support for argv[0].
  24.  
  25. Standard Argument Passing
  26.  
  27. The Pexec Cookbook specifies how to use Pexec() to launch a child
  28. process, passing a command tail (argument string) and an environment.
  29. Before getting into the extended argument scheme, let's review how
  30. arguments are normally passed to a child.
  31.  
  32. A parent process builds a command line into an argument string - a null
  33. terminated string whose first byte contains the length of the rest of
  34. the string - and its address is passed as one of the arguments to
  35. Pexec().  GEMDOS copies this argument string to the basepage which it
  36. creates for the child.  Thus the parent is responsible for gathering
  37. all the child's arguments into one string.  This is normally handled by
  38. a library exec() function.  The child is responsible for parsing the
  39. string of space-separated arguments back into an array of strings.
  40. This parsing is normally handled by the child's startup code.
  41.  
  42. Evolution
  43.  
  44. Several methods of bypassing the limits imposed by Pexec() have been
  45. used by GEMDOS programs.  Some allow a user to specify a file on the
  46. command line which contains the rest of the arguments.  Others get a
  47. pointer to the arguments, or the arguments themselves, from the
  48. environment string.  Most MS-DOS programs use a command file for the
  49. extra arguments.  This can be inconvenient for a user, cluttering the
  50. file system with command files, and making the operation of batch files
  51. and makefiles more confusing.
  52.  
  53. Several "standards" have arisen on the ST which use the environment to
  54. pass arguments.  While more convenient than command files, these
  55. standards have other problems.  Some rely on sharing memory between
  56. parent and child processes.  Some take advantage of undocumented
  57. features of the operating system to get argv[0].  Others give the
  58. child process no way to validate that the arguments it finds are
  59. intended for it.
  60.  
  61. Rationale
  62.  
  63. In order to pass more than the standard 125 characters worth of
  64. arguments to a child, or to let the child find its name, the parent
  65. must place the extra information in a place where the child can access
  66. it safely and legally.  The most convenient place is in the child's
  67. environment string.  An environment string is a series of
  68. null-terminated strings of the format "VARIABLE=value" (e.g.
  69. PATH=c:\bin,c:\etc, or ShellP=YES).   The last null-terminated string
  70. in the environment is followed by a zero byte, thus two consecutive
  71. nulls indicates the end of the environment.   The environment is
  72. allocated for the child by GEMDOS, it is owned by the child, and its
  73. contents can be specified by the parent.
  74.  
  75. The child must have some way of knowing that the arguments which
  76. it finds in its environment are intended for it.  The child may have
  77. been invoked by a parent which does not conform to this specification.
  78. Such a parent would leave _its_ arguments in the environment, and could
  79. pass that environment on to the child.  The child would mistakenly
  80. interpret its parent's arguments as its own.
  81.  
  82. Placing arguments in the environment passed to the child gets around
  83. all of the command line limits of the standard Pexec() command tail.
  84. Because there is no limit on the length of the environment, arbitrary
  85. length arguments are supported.  Arguments placed in the environment
  86. are null terminated, so they may contain spaces.  A parent can also
  87. place the name of the command with which it invokes the child in the
  88. child's environment, providing support for argv[0].  Validation of the
  89. extended arguments can be placed in the standard Pexec() command line,
  90. by assigning a special meaning to an invalid length byte.
  91.  
  92. The GEMDOS Extended Argument Specification
  93.  
  94. This specification uses the convention that the presence of an
  95. environment variable named ARGV (all upper case) indicates that extended
  96. arguments are being passed to the child in its environment.  This means
  97. that ARGV is a "boolean" environment variable.  For the purpose of this
  98. specification, its value is not significant, but its presence indicates
  99. that the strings following it are the arguments for the child.
  100. Implementations of this specification are free to give the ARGV
  101. environment variable any value.  The ARGV environment variable must be
  102. the last one in the environment passed to the child, so that the child
  103. can truncate its environment at that point, and treat everything before
  104. the ARGV as environment, and everything after it as arguments.
  105.  
  106. The first argument to the child (argv[0]) is the first string in the
  107. environment after the ARGV variable.  This argument is the "pathname"
  108. parameter passed by the parent to Pexec().  The remaining arguments are
  109. those that the child would normally find in the command tail in its
  110. basepage.  Even if all of the arguments would normally fit in a child's
  111. command tail, the parent should set up the arguments in the environment
  112. to take advantage of the benefits of this extended argument scheme.
  113.  
  114. As many arguments as will fit in the command tail will be passed there
  115. as well as in the environment, to support non-conforming programs.  As
  116. a flag that arguments are also in the environment, the length byte of
  117. the command tail will be 127 (hex 7f).  Non-conforming programs should
  118. not have a problem with this length byte, because it is longer than the
  119. maximum 125 bytes allowed by Pexec().
  120.  
  121. As an aside, the Pexec Cookbook erroneously implies that a command line
  122. can be 126 or 127 characters long.  In fact, GEMDOS only copies to the
  123. child's basepage up to 125 bytes, or until it encounters a null, from
  124. the argument string passed to Pexec().  It ignores the length byte,
  125. placing a null at the same place it found one or at the 126th byte if
  126. no null is found.  This has several implications: the length byte is
  127. not validated by GEMDOS (necessitating validation in the child's
  128. startup code, but also making this extended argument spec possible),
  129. and the null terminator _can_ be located after the end of the real
  130. command tail (the Desktop places a CR character after the command tail
  131. and before the null).  The ARGSTART.S startup code listing below
  132. demonstrates how to correctly validate and parse a GEMDOS command tail.
  133.  
  134. A child which finds an ARGV environment variable can use the command
  135. tail length byte value of 127 to validate that the arguments following
  136. the variable are valid, and not just left over from a non-conforming
  137. parent which left its own ARGV arguments in the environment.
  138.  
  139. Because the strings in the environment following an ARGV variable are
  140. not environment variables, a child should truncate its own environment
  141. at the ARGV variable by changing the 'A' to a null.
  142.  
  143. Implementation: Parental Responsibilities
  144.  
  145. To pass arguments in the environment, a parent must create an
  146. environment string for the child.  This can be achieved by first
  147. allocating as much space as is used in the parent's own environment,
  148. plus enough room for the ARGV variable and the arguments to the child,
  149. and then copying the parent's environment to the newly allocated area.
  150. Next, the ARGV variable must be appended, since it must be the last
  151. variable in the child's environment string.  Following the ARGV variable
  152. is the null-terminated pathname of the child as passed to Pexec(), then
  153. the null-terminated arguments to the child, followed by a final null
  154. byte indicating the end of the environment.
  155.  
  156. After setting up the arguments in the environment, the parent must
  157. place as many arguments as it can fit in the command tail it passes
  158. to Pexec().  This way, a child which does not conform to this
  159. specification can still get arguments from the command tail in its
  160. basepage.  When placing arguments in the environment, the parent must
  161. set the first (length) byte of the command tail to 127 (hex 7f),
  162. validating the arguments in the environment.
  163.  
  164. Here is an example execv() library routine in C.  It uses three local
  165. utility routines, e_strlen(), e_strcpy(), and str0cpy() for getting
  166. environment size and copying strings into the environment created for
  167. the child.
  168.  
  169.  
  170. /* EXECV.C - example execv() library routine
  171.  * ================================================================
  172.  * 890910 kbad
  173.  */
  174.  
  175. long Malloc( long nbytes );
  176. long Pexec( short mode, char *filename, char *tail, char *env );
  177. long Mfree( void *address );
  178.  
  179. /* Return the total length of the characters and null terminators in
  180.  *   an array of strings.
  181.  * `strings' is an array of pointers to strings, with a null pointer
  182.  *   as the last element.
  183.  */
  184. static long
  185. e_strlen( char *strings[] )
  186. {
  187.     char    *pstring;
  188.     long    length = 0;
  189.  
  190.     while( *strings != 0 ) {        /* Until reaching null pointer,    */
  191.     pstring = *strings++;        /* get a string pointer,        */
  192.     do {                /* find the length of this string,  */
  193.         ++length;            /* using do-while to count the    */
  194.     } while( *pstring++ != 0 ); /* null terminator.            */
  195.     }
  196.     return length;            /* Return total length of all strings */
  197. }
  198.  
  199. /* Copy a string, including the null terminator, and return a pointer
  200.  * to the end of the destination string.
  201.  */
  202. static char *
  203. str0cpy( char *dest, char *source )
  204. {
  205.     do { /* use do-while to include null terminator */
  206.     *dest++ = *source;
  207.     } while( *source++ != 0 );
  208.     return dest;
  209. }
  210.  
  211. /* Copy an array of strings into an environment string, and return a pointer
  212.  * to the end of the environment string.
  213.  * `strings' is an array of pointers to strings with a null pointer
  214.  *   as the last element.
  215.  * `envstring' points to the environment string.
  216.  */
  217. static char *
  218. e_strcpy( char *envstring, char *strings[] )
  219. {
  220.     while( *strings != 0 ) {
  221.     envstring = str0cpy( envstring, *strings );
  222.     ++strings;
  223.     }
  224.     return envstring;            /* Return end of environment string */
  225. }
  226.  
  227.  
  228. /* Run a program, passing it arguments according to the
  229.  * GEMDOS Extended Argument Spec.
  230.  *
  231.  * `childname' is the relative path\filename of the child to execute.
  232.  * `args' is an array of pointers to strings to be used as arguments
  233.  *   to the child.  The last array element must be a null pointer.
  234.  * `environ' is a global array of pointers to strings
  235.  *   which make up the caller's environment.
  236.  */
  237. long
  238. execv( char *childname, char *args[] )
  239. {
  240.     long    envsize, ret;
  241.     char    *parg, *penvargs, *childenv, *pchildenv;
  242.     short    lentail;
  243.     char    argch, tail[128], *ptail;
  244. static  char    argvar[] = "ARGV=";
  245. extern  char    *environ[];
  246.  
  247. /*
  248.  * Find out how much memory we'll need for the child's environment
  249.  */
  250.     envsize = e_strlen( environ );    /* length of environment    */
  251.     envsize += e_strlen( args );    /* plus command tail args    */
  252. /* plus length of argv[0] */
  253.     parg = childname;
  254.     do { /* use do-while to include null terminator */
  255.     ++envsize;
  256.     } while( *parg++ != 0 );
  257. /* plus length of ARGV environment variable and final null */
  258.     envsize += 7;
  259.     envsize += envsize & 1; /* even # of bytes */
  260. /*
  261.  * Allocate and fill in the child's environment
  262.  */
  263.     ret = Malloc( envsize );
  264.     if( ret < 0 )
  265.     return ret; /* Malloc error */
  266.     childenv = (char *)ret;
  267.     pchildenv = e_strcpy( childenv, environ );     /* copy caller environment */
  268.     pchildenv = str0cpy( pchildenv, argvar );     /* append ARGV variable */
  269.     pchildenv = str0cpy( pchildenv, childname ); /* append argv[0] */
  270.     penvargs = pchildenv;             /* save start of args */
  271.     pchildenv = e_strcpy( pchildenv, args );     /* append args */
  272.     *pchildenv = 0;                 /* terminate environment */
  273. /* put as much in the command tail as will fit */
  274.     lentail = 0;
  275.     ptail = &tail[1];
  276.     while( (lentail < 126) && (penvargs < pchildenv) ) {
  277.     argch = *penvargs++;
  278.     if( argch == 0 ) {
  279.         *ptail++ = ' ';
  280.     } else {
  281.         *ptail++ = argch;
  282.     }
  283.     }
  284. /* terminate command tail and validate ARGV */
  285.     *ptail = 0;
  286.     tail[0] = 127;
  287. /*
  288.  * Execute child, returning the return code from Pexec()
  289.  */
  290.     ret = Pexec( 0, childname, tail, childenv );
  291.     Mfree( childenv );
  292.     return ret;
  293. }
  294. /* End of execv() example code */
  295.  
  296.  
  297. Implementation: Prenatal Responsibilities
  298.  
  299. A program's startup code must handle getting extended arguments out of
  300. the environment.  The startup code should get the basepage pointer off
  301. the stack, then get the environment pointer from the basepage, and
  302. search the environment for "ARGV=".  If "ARGV=" is found, the command
  303. line length byte in the basepage is checked.  If the command line
  304. length byte is 127, then the arguments in the environment are valid.
  305. The first argument begins after the first null following the "ARGV=".
  306. It is important not to assume that the null follows immediately after
  307. the "ARGV=", because some implementations may assign a value to the
  308. ARGV environment variable.  After setting up an array of pointers to the
  309. arguments, the startup code should set the 'A' of the "ARGV" variable
  310. to null, thus separating the environment from the argument strings
  311. (remember: a double null terminates the environment).
  312.  
  313. Here is some example C startup code which shows how a child could
  314. look for arguments in its environment:
  315.  
  316. * ARGSTART.S - example C startup code
  317. * using GEMDOS Extended Argument Specification
  318. * ================================================================
  319. * 890910 kbad
  320.  
  321. .globl        _main        ; external, C entry point
  322. .globl        _argv0        ; external, name used for argv[0] if no ARGV
  323. .globl        _stksize    ; external, size of application stack
  324. .globl        _basepage    ; allocated here, -> program's basepage
  325. .globl        _environ    ; allocated here, -> envp[]
  326. .globl        _argvecs    ; allocated here, -> argv[]
  327. .globl        _stklimit    ; allocated here, -> lower limit of stack
  328. .BSS
  329. _basepage:    ds.l    1
  330. _environ:    ds.l    1
  331. _argvecs:    ds.l    1
  332. _stklimit:    ds.l    1
  333. .TEXT
  334. _start:
  335.     move.l    4(sp),a5    ; get basepage
  336.     move.l    a5,_basepage    ; save it
  337.     move.l    24(a5),a0    ; bss base
  338.     add.l    28(a5),a0    ; plus bss size = envp[] base
  339.     move.l    a0,_environ    ; save start of envp[]
  340.     move.l    a0,a1        ; start of env/arg vectors
  341.     move.l    44(a5),a2    ; basepage environment pointer
  342.     tst.b    (a2)        ; empty environment?
  343.     beq.s    nargv        ; yes, no envp[]
  344.  
  345.     lea.l    (sp),a4        ; use dummy return pc on stack for ARGV test
  346. * --- fill in the envp[] array
  347. nxenv:    move.l    a2,(a1)+    ; envp[n]
  348.     move.l    a2,a3
  349. nxen1:    tst.b    (a2)+
  350.     bne.s    nxen1        ; get the end of this variable
  351.     tst.b    (a2)        ; end of env?
  352.     beq.s    xenv
  353. * --- check for ARGV
  354.     move.b    (a3)+,-(a4)    ; get 1st 4 bytes of this var
  355.     move.b    (a3)+,-(a4)
  356.     move.b    (a3)+,-(a4)
  357.     move.b    (a3)+,-(a4)
  358.     cmp.l    #'VGRA',(a4)+    ; is it ARGV?
  359.     bne.s    nxenv
  360.     cmp.b    #'=',(a3)    ; is it ARGV=?
  361.     bne.s    nxenv
  362.     clr.b    -4(a3)        ; ARGV marks the end of our environment
  363.     cmp.b    #127,$80(a5)    ; command line validation?
  364.     bne.s    nargv        ; nope... and we're done with the env.
  365. * --- got an ARGV=, create argv[] array
  366.     clr.l    (a1)+        ; terminate envp[]
  367.     move.l    a1,_argvecs    ; save base of argv[]
  368. nxarg:    move.l    a2,(a1)+    ; argv[n]
  369. nxar1:    tst.b    (a2)+
  370.     bne.s    nxar1
  371.     tst.b    (a2)
  372.     bne.s    nxarg
  373. * --- end of environment
  374. xenv:    move.l    _argvecs,d0    ; if we got an argv[]
  375.     bne.s    argok        ; don't parse command tail
  376. * --- No ARGV, parse the command tail
  377. * NOTE: This code parses the command tail IN PLACE.  This can cause problems
  378. *       because the default DTA set up by GEMDOS for a program is located
  379. *       in the command tail part of the basepage.  You should use Fsetdta()
  380. *       to set up your own DTA before performing any operations which could
  381. *       use the DTA if you want to preserve the arguments in the command tail.
  382. nargv:    clr.l    (a1)+        ; terminate envp[]
  383.     move.l    a1,_argvecs    ; base of argv[]
  384.     move.l    #_argv0,(a1)+    ; default name for argv[0]
  385.     lea    128(a5),a2    ; command tail
  386.     move.b    (a2)+,d2    ; length byte
  387.     ext    d2
  388.     moveq    #125,d1        ; validate length
  389.     cmp    d1,d2
  390.     bcs.s    valen
  391.     move    d1,d2        ; if invalid length, copy all of tail
  392. valen:    clr.b    0(a2,d2)    ; null tail because desktop inserts <cr>
  393.     moveq    #' ',d1        ; space terminator
  394. get1:    move.b    (a2)+,d2    ; null byte?
  395.     beq.s    argok        ; if so, we're done
  396.     cmp.b    d1,d2        ; strip leading spaces
  397.     beq.s    get1
  398.     subq    #1,a2        ; unstrip start char
  399.     move.l    a2,(a1)+    ; and store that arg
  400. get2:    move.b    (a2)+,d2    ; next char
  401.     beq.s    argok        ; if null, we're done
  402.     cmp.b    d1,d2        ; if not space...
  403.     bne.s    get2        ; keep looking
  404.     clr.b    -1(a2)        ; terminate argv[argc] in the command tail
  405.     bra.s    get1        ; get next arg
  406. argok:    clr.l    (a1)+        ; terminate argv[]
  407. * --- allocate stack
  408.     move.l    a1,_stklimit    ; end of env/arg vectors is stack limit
  409.     add.l    _stksize,a1    ; allocate _stksize bytes of stack
  410.     move.l    a1,sp        ; set initial stack pointer
  411. * --- release unused memory
  412.     sub.l    a5,a1        ; size to keep
  413.     move.l    a1,-(sp)
  414.     move.l    a5,-(sp)    ; base of block to shrink
  415.     pea    $4a0000        ; Mshrink fn code + junk word of 0
  416.     trap    #1
  417.     lea    12(sp),sp    ; pop args
  418. *
  419. * Everything beyond here depends on implementation.
  420. * At this point, _environ points to envp[], _argvecs points to argv[],
  421. * and _stklimit points to the end of the argv array.  Thus argc can
  422. * be calculated as ((_stklimit-_argvecs)/4)-1.
  423. * _main could be invoked as follows:
  424. *
  425.     move.l    a5,-(sp)    ; basepage
  426.     move.l    _environ,-(sp)    ; envp[]
  427.     move.l    _argvecs,-(sp)    ; argv[]
  428.     move.l    _stklimit,d0    ; 4 bytes past end of argv[]
  429.     sub.l    (sp),d0        ; (argc+1) * sizeof( char * )
  430.     asr.l    #2,d0        ; argc+1
  431.     subq    #1,d0        ; argc
  432.     move    d0,-(sp)
  433.     jsr    _main        ; call mainline
  434.     lea    14(sp),sp    ; pop args
  435.  
  436.  
  437. A Final Note
  438.  
  439. This specification was formulated with careful deliberation, and with
  440. input from several companies and developers who have created
  441. development tools for GEMDOS.  The Mark Williams extended argument
  442. passing scheme was the main influence for this specification, because
  443. it has been in use, and supported by Mark Williams and other companies
  444. for several years.  This specification is very similar to the Mark
  445. Williams scheme, with the following important exceptions:
  446.  
  447. 1) Under the specification, the arguments after the ARGV environment
  448. variable may be validated by checking the command tail length byte.
  449. The Mark Williams execve() library function uses the command tail
  450. length byte as a telltale, but it is not checked by the crts0 startup
  451. code.  This validation is important for the reasons mentioned in the
  452. Rationale section above.
  453.  
  454. 2) The specification allows the ARGV environment variable to take on any
  455. value.  Mark Williams uses the value of ARGV as an iovector, which is
  456. described in the Mark Williams documentation.  The iovector should no
  457. longer be needed, as its primary purpose was to simplify the MWC
  458. implementation of the C library function isatty().
  459.  
  460. 3) Some versions of the MWC startup code do not require the ARGV= to
  461. have an `='.  Because ARGV is an actual environment variable in the
  462. specification, the equals character is required.
  463. --
  464.    |||   Ken Badertscher  (ames!atari!kbad)
  465.    |||   Atari R&D System Software Engine
  466.   / | \  #include <disclaimer>
  467.  
  468. ------------------------------
  469.  
  470. End of INFO-ATARI16 Digest V89 Issue #595
  471. *****************************************
  472.  
  473.