[comp.sys.amiga] Getopt for AmigaDOS

peter@sugar.UUCP (Peter da Silva) (08/03/87)

Whereas it is my fixed belief that conforming to the standards of an
operating system be desirable, unless those standards are unusable,
and...

Whereas the Amiga CLI uses keyword flags to specify options and
arguments (albeit not entirely uniformly: "opt a" on one command
means "all" on another)...

Whereas this standard is not UNIX-like, but is also not unweildy...

I would like to request that someone who has already done some work along
these lines (to avoid reinventing the wheel) provide a command parser in
the spirit of getopt (perhaps even one that allows programs to run
in both fashions). Failing this, I'd like to see if there's enough interest
out there for me to do so. It would make a nice pair with my Workbench
File Requestor...
-- 
-- Peter da Silva `-_-' ...!seismo!soma!uhnix1!sugar!peter (I said, NO PHOTOS!)

pete@violet.berkeley.edu (08/22/87)

 peter@sugar.UUCP (Peter da Silva) writes:
>
> Whereas it is my fixed belief that conforming to the standards of an
> operating system be desirable, unless those standards are unusable,
> and...
> Whereas the Amiga CLI uses keyword flags to specify options and
> arguments (albeit not entirely uniformly: "opt a" on one command
> means "all" on another)...
> .....
> I would like to request that someone who has already done some work along
> these lines (to avoid reinventing the wheel) provide a command parser in
> the spirit of getopt (perhaps even one that allows programs to run
> in both fashions). Failing this, I'd like to see if there's enough interest
> out there for me to do so. It would make a nice pair with my Workbench
> File Requestor...
> -- Peter da Silva `-_-' ...!seismo!soma!uhnix1!sugar!peter (I said, NO PHOTOS!)

I'm not sure how relevant it is, but I've just finished a general purpose
string and filename matching utility that extends the basic AmigaDOS-style
matching considerably.  This ended up with quite a few command line
keywords (eleven at current count!) and these are almost trivially decoded
using the pattern matching algorithm itself and a SINGLE pattern.  The same
method could be used by any program that didn't mind the space taken up by
the match procedure.

This might be a problem: the Lattice 3.10 object of this module takes up
about 2600 bytes (with about 1.5K of stack space while matching is going
on).  I hope it will compile under Manx without trouble -- I've tried to
keep to vanilla C -- and it probably would be shorter there, but eventually
it is probably worth the trip to Assembly.

In case you're interested, though, here's how it works.  The key addition
which lets you identify multiple keywords with a single match is the
"slice-mark" character.  The procedure returns a vector indicating those
slice-marks encountered in a successful match; the position of the slice
both in the pattern and the matched string is returned.  As you know where
a mark is in the supplied pattern, you can directly identify which keyword
it is associated with, and use a "switch" statement to select the action.

                            ++++++++++

Here is an abbreviated version of the command line argument checking
function in the program:


  /*
   * Argument Recognition Section
   *
   * checkarg --  matches the argument against argpat, and takes the
   *              appropriate action if there is a match.
   *              Note how it uses the slicing information returned
   *              by SMatch as a convenient way of determining the
   *              actual meaning of the argument.
   */

  /* First, here's the pattern with the keywords...*/
  char argpat[] =
     "NOCASE^|CASE^|(FILES|F)^|STRING^"; /* etc....*/

  /* these are the positions of the corresponding slices in the pattern: */
  #define NOCASE 7
  #define CASE 13
  #define FILES 24
  #define STRING 32

  /* the auxiliary vector for the pattern -- filled by CmplPat: */
  UBYTE argaux[33]; /* lengthen as necessary! */

  checkarg(pargc,pargv) int *pargc; char **pargv[];
  {
    char argcuts[33], upperarg[256], *cutp = argcuts;

    if (!*pargc) return FALSE; /* don't run off the end... */

    CmplPat(argpat, argaux); /* ...pure laziness
                                -- I could use a precompiled pattern */

    uppercase(**pargv, upperarg); /* I used my own short procedure
                                     to change strings to upper case */

    if (SMatch(argpat, argaux, upperarg, argcuts)) {
        while (!cutp[1]) cutp += 2; /* skip phony subpatterns (e.g. "F") */
            /* the above is necessary for reasons I won't explain here */
        switch((int)(*cutp)) {
        case NOCASE: casesens = FALSE;
                     break;
        case CASE:   casesens = TRUE;
                     break;
        case FILES:  matchmode = MATCH_NAMES;
                     break;
        case STRING: matchmode = MATCH_IMMED;
                     break;
        /*... more as needed... (and as heavy-duty as you want...) */
        }
        (*pargv)++; /* move past this argument */
        (*pargc)--;
        return TRUE; /* say we found a keyword */
    }
    return FALSE; /* not a keyword -- do something else */
  }


Some short explanation regarding the pattern match:  A pattern must first
be "compiled"  ( with CmplPat() ) to generate an auxiliary control vector
("argaux" in this case).  A given pattern only has to be compiled once, and
in fact it is fairly trivial to work out the control vector of a pattern
like that by hand, but because I was changing it often and the overhead is
negligible here, I just do it each time the function is called.

The match routine -- called SMatch() here to distinguish it from a simpler
version that lacks slicing -- takes as arguments the pattern, its auxiliary
vector, the string to be matched, and another vector in which the slice
references will be returned.  For each reference you get a pair of bytes
in the slice vector ("argcuts"); the first is the position of the mark in
the pattern (the first character is 1, not 0); the second is the position
in the matched string. We don't care about the latter here, except there
are some situations in which you can see a phony slice with a zero string
position. The mysterious "while" statement takes care of that.

There's an overall limit of 255 characters in the pattern, because byte
size offsets are used.  Beyond that you'd just have to add a second pattern
and match call.

                            +++++++++++

The match algorithm was developed from Martin Richards' BCPL original, and
I consider it to be in the public domain.  It's a bit long to post here
though (at least to my tastes).  The whole String Matching Utility package
is certainly too long.  If I believed in the reality of comp.sources.amiga
I'd send it there, but you'll probably see it first on a Fish Disk.  I hope
to get it off to him RSN.

BTW the whole thing was sparked by my desire a) to have a better string
search program that would be flexible in the kind of output it generated,
and b) to be able to do "impossible" things like:

            rename #?.c as old_#?.c                  (!!)

The Utility handles both these areas quite well for a first shot (I
think!), though its command lines can get a little intricate -- it's best
within command scripts.

                                        -- Pete Goodeve --