/* * Copyright (c) Alex Holden 2000, 2001. * * This code is public domain; you can do whatever you want with it, though I * would prefer you to contribute any bug fixes back to me if possible. * This software comes with NO WARRANTY WHATSOEVER, not even the implied * warranties of merchantability and fitness for a particular purpose. * * getarg.c: A command line parser. * * Okay, so most standard C libraries already have getopt(), and there are * several clones of it floating around with various improvements (some more * bloated and painful to use than others), but I've never found one which is * simple to use, small, portable, and available in the public domain. This is * my attempt to fulfill those design goals. GetArg has the additional * advantage of being thread safe and not modifying the argument list supplied * to it. * * getarg_init() returns a pointer to a getarg_state structure to be used in * subsequent getarg() calls, or NULL if the memory allocation failed when * called with an argument list. * * getarg() searches through the argument list for the specified flag, and * returns a pointer to the argument following the flag, or NULL if the flag * was not found or there was no valid argument string following it. Every time * an argument is found, it is marked as such and will not be returned again, * so if an argument is specified several times, they can all be retrieved by * repeating the getarg() call until a NULL pointer is returned. Note that the * argument list itself is never modified; only a copy of it which is stored in * the getarg_state structure. The short flag '\0' is a special case which * causes getarg() to return the first string which does not begin with the * short argument symbol and is not a long argument. If the longflag is also * set to an empty string (""), unused long arguments will be returned instead. * This can be used to retrieve the arguments which do not have flags * associated with them, and to find invalid long flags. When getarg() runs out * of unused strings it will return a NULL pointer. * * Another special case is when you want to retrieve a long flag or argument * which starts with the short flag character (ie. -flag as opposed to the * usual -f or --flag). The way to do this is to specify a short argument of * GETARG_SHORTFLAG, and a long argument of the string you are interested in * (without the flag character). * * getflag() is similar to getarg(), but returns the number of times the * specified flag is present in the argument list instead. The flags are marked * as used afterwards, so subsequent calls with the same flag will return 0. * The flag '\0' is a special case which causes getflag() to return the first * short argument flag it finds which has not been marked as used. This can be * used to find invalid arguments. When getflag() runs out of unused flags, it * will return 0. * * getarg_destroy() is used to free a getarg_state structure. You should always * call this function instead of using free() directly. * * The shortflag parameter to getarg() and getflag() should always be specified. * It is a single letter which can appear either as an argument on it's own or * as a list of single letter arguments, or the previously discussed special * case '\0' value for finding unused arguments and flags. * * The longflag parameter to getarg() and getflag() can be set to NULL if you * are not interested in looking for a long version of the argument, or it can * be a pointer to a zero terminated ASCII string specifying the long name of * the argument (without the '--' prefix). * * By default, the short argument start symbol is '-', and the long argument * start string is "--", but it is possible to change these at build time (for * example you may want to set the short argument start symbol to '/' on * DOS systems). See getarg.h for details. * * Note that arguments which contain just the short argument start symbol or * the long argument start symbols are not considered to be flags and will * be returned as unattached argument strings by getarg(state, 0, NULL). * * Note that arguments and unused long flags are returned , * you should not modify the contents of the real *argv[] array after the. */ #include #include #include "getarg.h" /* * Creates a new getarg_state structure containing the specified argument list. */ getarg_state *getarg_init(int argc, char *argv[]) { int i, len; getarg_state *state; /* Allocate storage for the structure */ if(!(state = malloc(sizeof(getarg_state)))) return NULL; /* Store the number of arguments in the list */ state->argc = argc - 1; /* Check that there is at least one argument in the list */ if(state->argc) { /* Allocate storage for the argument list */ if(!(state->argv = malloc((argc - 1) * sizeof(char *)))) { /* It failed, so free the state structure and return */ free(state); return NULL; } /* Otherwise, don't bother allocating a list */ } else { state->argv = NULL; return state; } /* Copy the arguments, ignoring the first argument (the program name) */ for(i = 1; i < argc; i++) { len = strlen(argv[i]) + 1; if(!(state->argv[i - 1] = malloc(len))) { /* It failed, so free the list and return */ while(i-- > 1) free(state->argv[i]); free(state->argv); free(state); return NULL; } memcpy(state->argv[i - 1], argv[i], len); } /* Store the address of the real (unmodified) argument list */ state->realargv = argv; /* Return the newly created getarg_state structure */ return state; } /* * Checks that the next argument in the list is valid and returns a pointer * to it, or a NULL pointer if it is not. */ static char *next_arg(getarg_state *state, int index) { int i = index + 1; /* * If we haven't fallen off the end of the argument list, the next * argument isn't an empty string, the next argument isn't a short * argument list (NB. the short argument character on it's own doesn't * count as a short argument list), and the next argument isn't a long * argument (NB. the long argument characters on their own don't count * as a long argument), return a pointer to the next argument. */ if((i < state->argc) && (state->argv[i][0]) && (state->argv[i][0] != GETARG_USEDFLAG) && !((state->argv[i][0] == GETARG_SHORTFLAG) && (state->argv[i][1] != 0)) && !((state->argv[i][0] == GETARG_LONGFLAG0) && (state->argv[i][1] == GETARG_LONGFLAG1) && (state->argv[i][2] != 0))) { return(state->argv[i]); } else return NULL; } /* * The core of GetArg. Implements the semantics of getarg() if ga is 1, or * getflag() if it is 0. cnt is a pointer to the return value when used with * in getflag() mode. */ char *_getarg(getarg_state *state, char shortflag, char *longflag, int ga, int *cnt) { char *p; int i, count = 0; /* Search through every argument */ for(i = 0; i < state->argc; i++) { /* Skip this one if it has already been marked as used */ if(state->argv[i][0] == GETARG_USEDFLAG) continue; /* If this is a short flag */ else if((state->argv[i][0] == GETARG_SHORTFLAG) && (state->argv[i][1] != 0) && !((state->argv[i][0] == GETARG_LONGFLAG0) && (state->argv[i][1] == GETARG_LONGFLAG1))) { /* Handle the special case for long flags which start * with the short flag character */ if((shortflag == GETARG_SHORTFLAG) && longflag && !strcmp(&state->argv[i][1], longflag)) { /* It matches, so mark it as used */ state->argv[i][0] = GETARG_USEDFLAG; /* In getflag() mode, just increment count */ if(!ga) { count++; continue; } /* In getarg() mode, return a pointer to the * the next argument if it is valid. */ if((p = next_arg(state, i))) { *p = GETARG_USEDFLAG; return(state->realargv[i + 2]); } else continue; } /* For each character until the terminating zero */ for(p = &state->argv[i][1]; *p; p++) { /* Skip it if it's been marked as used */ if(*p == GETARG_USEDFLAG) continue; /* If we're in "return all unused flags" mode, * return the character */ if(!ga && !shortflag) { *cnt = *p; *p = GETARG_USEDFLAG; return NULL; } /* Check if the flag matches */ if(*p == shortflag) { /* It does, so mark it as used */ *p = GETARG_USEDFLAG; /* In getflag() mode, just increment * count and continue */ if(!ga) { count++; continue; } /* In getarg() mode, check that this * is the last (or only) flag in the * list, then return a pointer to the * next argument if it is valid */ if((!*++p)&&(p = next_arg(state, i))) { *p = GETARG_USEDFLAG; return(state->realargv[i + 2]); } else return NULL; } } /* Check if this is a long arg */ } else if((state->argv[i][0] == GETARG_LONGFLAG0) && (state->argv[i][1] == GETARG_LONGFLAG1) && (state->argv[i][2] != 0)) { /* If we're not looking for a long argument, continue */ if(!longflag) continue; /* In "return all unused long flags" mode, return it */ if(ga && !shortflag && !longflag[0]) { state->argv[i][0] = GETARG_USEDFLAG; return state->realargv[i + 1]; } /* Check if it matches the one we're looking for */ if(!strcmp(&state->argv[i][2], longflag)) { /* It does, so mark it as used */ state->argv[i][0] = GETARG_USEDFLAG; /* In getflag() mode, just increment count */ if(!ga) { count++; continue; } /* In getarg() mode, return a pointer to the * the next argument if it is valid. */ if((p = next_arg(state, i))) { *p = GETARG_USEDFLAG; return(state->realargv[i + 2]); } else return NULL; } /* This isn't a flag. If we're in "return all unused arguments" * mode, mark it as used and return a pointer to it. */ } else if(ga && !shortflag && !longflag) { state->argv[i][0] = GETARG_USEDFLAG; return(state->realargv[i + 1]); } } /* * We ran out of arguments. In getarg() mode this means we didn't find a * matching argument and should return a NULL pointer. In getflag() mode * this simply means that we have searched through the entire list and * should return the count of times that we found the specified flag. */ if(!ga) *cnt = count; return NULL; } /* * Returns the argument to the specified flag, or NULL if no valid argument was * found. See above for more details. */ char *getarg(getarg_state *state, char shortflag, char *longflag) { return(_getarg(state, shortflag, longflag, 1, NULL)); } /* * Returns the number of times the specified flag was found in the list. See * above for more details. */ int getflag(getarg_state *state, char shortflag, char *longflag) { int ret; _getarg(state, shortflag, longflag, 0, &ret); return ret; } /* * Frees a getarg_state structure and the argument list contained in it. */ void getarg_destroy(getarg_state *state) { int i; /* Check that the argument list exists */ if(state->argv) { /* Free the arguments */ for(i = state->argc - 1; i; i--) free(state->argv[i]); /* Free the argument list */ free(state->argv); } /* Free the state structure */ free(state); }