Go to the first, previous, next, last section, table of contents.


Adding to the shell

The code that makes up `sdsi2sh' isn't the prettiest. This is because sdsi2sh was originally conceived more as a tool for testing, and not as a prime-time program. Its command parsing, type checking, and symbol table management are all very quick-and-dirty. For these reasons, its actual design is not documented very much here.(7)

However, one goal of sdsi2sh was to make it very easy to add new commands to it. The file sdsi2sh-commands.c contains all of the functions that implement the various shell commands. Every function that implements a command has the same prototype: something * function-name(something *);

A something is the data structure used to pass values of many types within the shell. A something can represent a SDSI S-expression, bytestring, or an integer. somethings can made into a linked list; this is how multiple arguments are passed into a command function. Command functions optionally return a single something containing the result of the command. A something indicates what type of thing it actually represents, and includes the value of that thing.

Adding a new command to the shell involves the following tasks:

  1. Decide on the name for the new command, and the name of the C function that will implement it. Add a new prototype declaration using the FUNC macro to the top of sdsi2sh-commands.c using the name of the new function.
  2. Add a new command declaration to the commands array. commands is an initialized array of elements of type command - a data structure that holds a command declaration. An example element of the commands array is for the sign command:
    /* sign: */
    { "sign", do_sign, "oI",
       "signs an object",
       "usage: sign <object> [<include-key-as-hash-flag>] - returns your \n" \
       "  signature on <object>.  if <include-key-as-hash-flag> is given,\n" \
       "  includes the verification key as a hash instead of outright.\n" }
    
    The fields of a command structure are, in order:
    1. The name of the command.
    2. The name of the function implementing the command.
    3. The command's argument list description. Its contents are described below.
    4. A short explanation of the command.
    5. Full help for the command.
  3. Write the C function implementing the command. These functions are typically short, since all most of them do is extract pointers to the different C structures in the linked list of argument somethings passed in, hand them to a library function, and package the return value back into a something. The function implementing the shell's sign command is a good example of this:
    /* the "sign" command: */
    something * do_sign(args)
         something *args;
    {
      something *ret;
      ret = new_thing(NULL);
      ret->what = AN_SEXP;
      sdsi2_signature_compute(&args->the_sexp, &ret->the_sexp.signature, 
          &speaker_private_key, &speaker_public_key, args->next->the_int);
      return(ret);
    }
    

A command's argument list description (given in the third element of its commands element) is a string where each character represents an argument, and indicates the type of something that argument must be. The valid characters are:

o
SDSI S-expression
s
SDSI bytestring
i
integer

The case of an o, s, or i is significant. If a letter is capitalized, this causes an empty object (or, in the case of an integer, the value zero) to be passed in as the corresponding argument if none is specified on the command line - in other words, a capitalized argument type specifies that an argument is optional, and defaults to a suitable "empty" value.

In an argument list description, the first optional argument must be followed by all optional arguments. It is impossible to follow an optional argument with any mandatory arguments.


Go to the first, previous, next, last section, table of contents.