home *** CD-ROM | disk | FTP | other *** search
- /*
- * FILE
- * clop.c
- *
- * DESCRIPTION
- * Command Line Option Parser
- *
- * AUTHOR
- * Anders 'ALi' Lindgren
- * Mälarblick 8
- * S-161 51 Bromma
- * Sweden
- *
- * TODO
- * float (Conditional compilation)
- * New infirmation passing system. (Rename clop_list & clop_array).
- * move getargs to its own file.
- * En global Escape-flagga?
- * Bättre ALiLib-interfacing. (Flytta Clop.h, stdlib.h etc.)
- * En clop-error-meddelande rutin. Den skall returnera en pekare till
- * en (statisk) sträng som innehåller ett clop-felmeddelande. Rutinen
- * skall vara fristående så att man slipper strängarna om man inte
- * vill ha dom.
- */
-
- #include <proto/exec.h>
-
- #include "stdlib.h"
- #include "string.h"
-
- #include "clop.h"
-
- #ifndef NULL
- #define NULL 0L
- #endif
-
-
- /*
- * DEFINITION
- * ESCAPE
- *
- * DESCRIPTION
- * Special character escape.
- * Example: "allan\"hej" -> allan"hej
- */
-
- #define ESCAPE '\\'
-
-
- /*
- * DEFINITION
- * isdigit
- *
- * DESCRIPTION
- * Check if it's a digit.
- * Why don't use the one in ctype.h? It's to long for just one test.
- */
-
- #define isdigit(x) ( ((x) >= '0') && ((x) <= '9') )
-
-
- /*
- * FORWARD REFERENCES
- */
-
- extern int _catol(unsigned char *, long, long *);
- extern int _getargs(unsigned char *, unsigned char * *);
-
-
- /*
- * FUNCTION
- * clop()
- *
- * DESCRIPTION
- * Command Line Option Parser
- *
- * This is the frontend of this module. It accepts a clop_array
- * prefilled with options, and a clop_list with nothing but
- * error information at the moment. And of course, it accepts
- * a zero-terminated line with options.
- *
- * RETURNS
- * A pointer to the first argument which isn't an option, or
- * a NULL if an error occured. If there is only options on
- * the line, a pointer to the terminating NIL is returned.
- */
-
- unsigned char * __stdargs
- clop(unsigned char * line, struct clop_list * clop_list,
- struct clop_array * clop_array)
- {
- register int i;
- register unsigned char ch;
- unsigned char * start_of_args;
- int k,j,m;
-
- i = 0;
- start_of_args = line;
-
- while (1) {
-
- do { /* Skip blanks. */
- ch = line[i++];
- } while (ch && (ch == ' '));
-
- start_of_args = & line[i-1];
-
- if (ch) { /* If not end of line */
-
- if (ch == '-') { /* Option? */
- if (ch = line[i++]) {
-
- k = -1;
- for (j=0 ; clop_array[j].type != CLOPT_END ; j++) {
- if (clop_array[j].option == ch) {
- k=j;
- }
- }
-
- if (k != -1) { /* Existing option */
-
- /*
- * Check if an option which only can be
- * present once is present several times.
- */
- if ( (clop_array[k].inflags & CLOPIF_SINGLE) &&
- (clop_array[k].outflags & CLOPOF_PRESENT) ) {
- clop_list->error = CLOPE_MULTIPLE_SINGLE;
- clop_list->failing_opt = ch;
- return(NULL);
- }
-
- clop_array[k].outflags |= CLOPOF_PRESENT;
-
- switch(clop_array[k].type) {
- case CLOPT_NUMBER:
- case CLOPT_UNUMBER:
- /*
- * Note that the same code is used for both
- * signed and unsigned number. This can be
- * done since value is an union, and "number"
- * and "unumber" actiually has the same
- * storage area.
- */
- if ( (m = _catol(& line[i], clop_array[k].type,
- &clop_array[k].value.number)
- ) != -1 ) {
- i += m;
- } else {
- clop_list->error = CLOPE_ILLEGAL_NUMBER;
- clop_list->failing_opt = ch;
- return(NULL);
- }
- break;
-
- case CLOPT_STRING:
-
- /*
- * If this option was supplied several
- * times, the last one rules (and the
- * previous strings are released.)
- * Why? Well, you could supply a env:
- * default option line, and then the user
- * could replace that value with his own.
- */
- if ( clop_array[k].value.string ) {
- if ( clop_array[k].outflags & CLOPOF_ALLOCATED ) {
- free( clop_array[k].value.string );
- clop_array[k].outflags &= ~CLOPOF_ALLOCATED;
- }
- clop_array[k].value.string = NULL;
- }
-
- if ( (m = _getargs(& line[i], & clop_array[k].
- value.string) ) != -1 ) {
- i += m;
- clop_array[k].outflags |= CLOPOF_ALLOCATED;
- } else {
- clop_list->error = CLOPE_ILLEGAL_STRING;
- clop_list->failing_opt = ch;
- return(NULL);
- }
- break;
-
- case CLOPT_BOOL:
- switch(line[i]) {
- case ' ':
- case '\0':
- clop_array[k].value.bool =
- ! clop_array[k].value.bool;
- break;
-
- case '0':
- case '1':
- clop_array[k].value.bool = line[i++]-'0';
-
- if ((line[i] != ' ') && (line[i] != '\0')) {
- clop_list->error = CLOPE_ILLEGAL_BOOL;
- clop_list->failing_opt = ch;
- return(NULL);
- }
- break;
-
- default:
- clop_list->error = CLOPE_ILLEGAL_BOOL;
- clop_list->failing_opt = ch;
- return(NULL);
- } /* Bool switch */
-
- break;
-
- default:
- clop_list->error = CLOPE_UNKNOWN_TYPE;
- clop_list->failing_opt = ch;
- } /* switch */
-
- }
- else {
- /* option not found */
- clop_list->error = CLOPE_UNKNOWN_OPTION;
- clop_list->failing_opt = ch;
- return(NULL);
- }
-
- } /* The line was ended by a "-" */
- else {
- return(start_of_args);
- }
-
- } /* No more options. */
- else {
- return(start_of_args);
- }
-
- } /* End of the line. Return a pointer to the last '\0' */
- else {
- return(start_of_args);
- }
- } /* while (1) */
- }
-
-
- /*
- * FUNCTION
- * _catol
- *
- * RETURNS
- * The length of the string processed.
- *
- * DESCRIPTION
- * Convert an ascii string to a long, or a unsigned long.
- *
- * "type" is set to the current type (NUMBER or UNUMBER).
- *
- * "value" is declared as a long, but it can also work as
- * a unsiged long.
- *
- * RETURNS
- * The number of characters processed, or -1 if an error occured.
- *
- * NOTE
- * No overflow chech is made, but I don't really think it's necesary.
- */
-
- static int
- _catol(unsigned char * string, long type, long * value)
- {
- long number = 0;
- int i = 0;
- unsigned char ch;
- int negative = 0;
-
- /* Check minus sign */
- if ( (string[0] == '-') && (type == CLOPT_NUMBER) ) {
- negative = 1;
- i=1;
- }
-
- ch = string[i++];
- if ( isdigit(ch) ) {
-
- do {
- number = ((number << 3) + (number << 1)) + ch - '0';
- ch = string[i++];
- } while ( isdigit(ch) );
-
- if ( (ch == '\0') || (ch == ' ') ) {
-
- if (negative) {
- number = -number;
- }
-
- (* value) = number;
- return(i-1);
- }
- }
- return(-1); /* Error */
- }
-
-
- /*
- * FUNCTION
- * _getargs
- *
- * DESCRIPTION
- * Get Argument String
- * This function gets an argument string. It can handle quotes etc.
- *
- * RETURNS
- * the number of characters parsed, or -1 if an error occured.
- */
-
- int
- _getargs(unsigned char * str, unsigned char * * destptr)
- {
- unsigned char tmpbuf[256];
-
- unsigned char ch;
- long quoted = 0;
- int s = 0; /* source index */
- int d = 0; /* dest index */
- int quit = 0;
-
- while ( str[s] == ' ') { /* Skip initial blanks */
- s++;
- }
-
- if (str[s] == '"') { /* String quoted? */
- quoted = 1;
- s++;
- }
-
- while(! quit) {
- switch (ch = str[s++]) {
- case '\0':
- quit = 1;
- break;
-
- case ' ':
- if (quoted) {
- tmpbuf[d++] = ' ';
- }
- else {
- quit = 1;
- }
- break;
-
- case '"':
- if (quoted) {
- quit = 1;
- }
- else {
- tmpbuf[d++] = '"';
- }
- break;
-
- case ESCAPE:
- if ( (ch = str[s++]) == '\0' ) {
- tmpbuf[d++] = ESCAPE;
- quit = 1;
- }
- /* Fall through */
-
- default:
- tmpbuf[d++] = ch;
- }
- } /* while */
-
- if ((* destptr) = (unsigned char *)malloc(d+1)) {
- memcpy((void *)* destptr, (void *)tmpbuf, d);
- (* destptr)[d] = '\0';
- return(s); /* Return the parsed length */
- }
-
- return(-1);
- }
-