/***************************************************************************** * myopt.c * * DESCRIPTION * This file contains some code to help interface with getopt_long() * * HISTORY * 6/2007 Arthur Taylor (MDL): Created. * * NOTES ****************************************************************************/ #include #include #include "libaat.h" /***************************************************************************** * myUsage() -- Arthur Taylor / MDL * * PURPOSE * This ouputs a Gnu usage message with getopt (as opposed to argp). For * more info, see: * http://www.cs.utah.edu/dept/old/texinfo/standards/standards.html#SEC22 * http://www.gnu.org/software/libc/manual/html_node/Argp.html#Argp * * ARGUMENTS * name = Name of the program (Input) * argsDoc = Documentation on how to call the program (Input) * doc = Main documentation for the program. A \v separates the text * into before and after the optlist (Input) * optLong = The long options used with getopt_long (Input) * optHelp = Supplemental to optLong, describes each option. A \v at the * beginning of an optHelp[].doc means no 'long option' (Typically * used as a place holder). A 0 for optHelp[].val means no 'short * option'. (Input) * * RETURNS: int * 0 ok * -1 ran out of optHelp[] array elements * * HISTORY * 6/2007 Arthur Taylor (MDL): Created. * 9/2007 AAT (MDL): Modified doc to be 2d because of ISO limit of 509 char * * NOTES ****************************************************************************/ int myUsage(const char *name, const char *argsDoc, char *doc[], const struct option *optLong, const optHelpType * optHelp) { /* char *docPtr; */ /* Pointer to \v in doc (if there is one) */ size_t len; /* Length of current long option. Used to determine * if we can fit everything on one line. */ char *optDoc; /* Current long option documentation. */ char *ptr; /* Pointer to \n in optDoc (if there is one) */ printf("Usage %s %s\n", name, argsDoc); while (*doc != NULL) { if ((*doc)[0] == '\v') { break; } puts(*doc); ++doc; } /* if ((docPtr = strchr(doc, '\v')) == NULL) { puts(doc); } else { while (doc != docPtr) { putc(*(doc++), stdout); } putc('\n', stdout); ++doc; } */ while ((optLong->name != NULL) && (optHelp->val != -1)) { /* Determine if there is a short option name. */ if (optHelp->val == 0) { printf(" "); } else { printf(" -%c,", optHelp->val); } /* Determine if there is a long option name. */ optDoc = optHelp->doc; if (optDoc[0] == '\v') { printf(" "); len = 0; ++optDoc; } else { printf(" --%s", optLong->name); len = strlen(optLong->name); ++optLong; } ++optHelp; /* Print the spacing needed to get to the optDoc area. */ if (len > 21) { printf("\n%30s", " "); } else { printf("%*s", (int) (21 - len), " "); } /* Print the optDoc area. */ while ((ptr = strchr(optDoc, '\n')) != NULL) { while (optDoc != ptr) { putc(*(optDoc++), stdout); } printf("\n%31s", " "); ++optDoc; } printf("%s\n", optDoc); } if (optLong->name != NULL) { myWarn_Err1Arg("Need more elements in optHelp[] array\n"); return -1; } if (*doc != NULL) { puts((*doc) + 1); ++doc; } while (*doc != NULL) { puts(*doc); ++doc; } /* if (docPtr != NULL) { puts(doc); } */ return 0; } /***************************************************************************** * swap1() (Private) -- Arthur Taylor / MDL * * PURPOSE * Take item in location st, store it in location pl, and move items * that were in range of [pl to (st - 1)] to [pl + 1 to st] * * ARGUMENTS * argv = The command line options to permute (Input/Output) * st = Location of item to shift. (Input) * pl = Destination of item to shift. (Input) * * RETURNS: void * * HISTORY * 8/2007 Arthur Taylor (MDL): Created. * * NOTES * Side effect: argv is permuted. ****************************************************************************/ static void swap1(char **argv, int st, int pl) { char *t1; /* temporary storage. */ int i; /* Used to loop over argv */ #ifdef DEBUG if (st <= pl) { fprintf(stderr, "Bad usage?\n"); return; } #endif t1 = argv[st]; for (i = st - 1; i >= pl; --i) { argv[i + 1] = argv[i]; } argv[pl] = t1; } /***************************************************************************** * swap2() (Private) -- Arthur Taylor / MDL * * PURPOSE * Take items in location st, st + 1, store it in location pl, pl + 1 and * move items that were in [pl to (st - 1)] to [pl + 2] to (st + 1)]. * * ARGUMENTS * argv = The command line options to permute (Input/Output) * st = Location of item to shift. (Input) * pl = Destination of item to shift. (Input) * * RETURNS: void * * HISTORY * 8/2007 Arthur Taylor (MDL): Created. * * NOTES * Side effect: argv is permuted. ****************************************************************************/ static void swap2(char **argv, int st, int pl) { char *t1; /* temporary storage. */ char *t2; /* temporary storage. */ int i; /* Used to loop over argv */ #ifdef DEBUG if (st <= pl) { fprintf(stderr, "Bad usage?\n"); return; } #endif t1 = argv[st]; t2 = argv[st + 1]; for (i = st - 1; i >= pl; --i) { argv[i + 2] = argv[i]; } argv[pl] = t1; argv[pl + 1] = t2; } /***************************************************************************** * myGetOpt() -- Arthur Taylor / MDL * * PURPOSE * Decode options from argv. A -- indicates a long option, or end of * options. A - indicated first a long option. If it doesn't match the long * options, then the - indicates a set of short options. For example, '-foo' * means first look for long option "foo", then short options 'f', 'o', 'o'. * If a option has an argument, a pointer to it is stored in optarg. * After all options are found, gl.optind points to the next index in argv. * * For more info see: * http://www.gnu.org/software/libc/manual/html_node/Getopt.html * Similar to getopt_long_only except it doesn't use global variables. * * Uses 2 static variables... * f_first : true for first call (so it init gl.optind to 1) false after. * nextChar : location in short option we are working on. * * ARGUMENTS * argc = The number of command line options (Input) * argv = The command line options (Input) * optShort = The short options. A ':' means a required argument, a '::' * means an optional argument. (Input) * optLong = The long options (Input) * gl = The return values: (Output) * (char *) optarg => any arguments associated with option. * (int) optind => index to point after options in argv * (int) optopt => value of bad option if error with optShort * (int) index => index value of current option in optLong or -1 * * RETURNS: int * -1 = found all options. * 0 = Set a long option flag (optLong.flag != NULL), * or optLong.flag == NULL, and optLong.val = 0. * '?' = unrecognized option. * 1..255 = val of the short option, or if optLong.flag == NULL then value * of the optLong.val. * * HISTORY * 8/2007 Arthur Taylor (MDL): Created. * * NOTES * 1) Side effect: argv can get permuted. ****************************************************************************/ int myGetOpt(int argc, char *const *argv, const char *optShort, const struct option *optLong, struct getOptRet *gl) { int firstNonopt; /* First non-option found in the argv list. */ int f_type; /* 0 if non-option, 1 if one -, 2 if two --. */ int f_optFound; /* True if we have matched a long option. */ char *ptr; /* ptr to end of (long) option or location in short * option list of match. */ const struct option *p; /* Helps walk through optLong list. */ int ans; /* The value to return */ static int nextChar = 0; /* Next short option character to parse. */ static char f_first = 1; /* Used to init optind. */ /* Set up default return values. */ gl->optarg = NULL; if (f_first) { gl->optind = 1; f_first = 0; } gl->index = -1; gl->optopt = 0; /* Search for an option */ firstNonopt = -1; f_type = 0; myAssert (gl->optind >= 1); for (; gl->optind < argc; ++gl->optind) { /* See if we have an option. */ if ((argv[gl->optind][0] == '-') && (argv[gl->optind][1] != '\0')) { /* See if we have a long option. */ if (argv[gl->optind][1] == '-') { f_type = 2; } else { /* Found short (but possibly long) option. */ f_type = 1; } break; } else { /* Found non-option. */ if (firstNonopt == -1) { firstNonopt = gl->optind; } } } /* Handle the cases where we either didn't have an option, or have hit the * end of the options. */ if (f_type == 2) { /* see if we have the end of options. */ if ((argv[gl->optind][2] == '\0')) { ans = -1; if (firstNonopt != -1) { /* insert gl->optind to just before firstNonopt */ swap1((char **)argv, gl->optind, firstNonopt); gl->optind = firstNonopt; } gl->optind++; return ans; } else if (optLong == NULL) { /* Error because there are no long options, but we have one. */ if (gl->opterr) { fprintf(stderr, "unknown option %s\n", argv[gl->optind]); } gl->optopt = '-'; ans = '?'; if (firstNonopt != -1) { /* insert gl->optind to just before firstNonopt */ swap1((char **)argv, gl->optind, firstNonopt); gl->optind = firstNonopt; } gl->optind++; return ans; } } else if (f_type == 0) { /* We didn't see any options. */ if (firstNonopt != -1) { gl->optind = firstNonopt; } return -1; } /* Handle the long option if we have one. */ if ((optLong != NULL) && (nextChar == 0)) { /* Find end of possible long option name ('\0' or '='). */ for (ptr = argv[gl->optind] + f_type; *ptr && *ptr != '='; ++ptr) { } /* Seach long option list to see if we have an identical match. */ f_optFound = 0; for (p = optLong, gl->index = 0; p->name; ++p, ++gl->index) { if (!strncmp(p->name, argv[gl->optind] + f_type, ptr - (argv[gl->optind] + f_type))) { if ((unsigned int)(ptr - (argv[gl->optind] + f_type)) == (unsigned int)strlen(p->name)) { /* Identical match */ f_optFound = 1; break; } } } /* Handle option and return correct value. */ if (f_optFound) { /* Determine the correct return values. */ if (p->flag != NULL) { *(p->flag) = p->val; ans = 0; } else { ans = p->val; } /* Handle any arguments. */ if (p->has_arg == no_argument) { } else if (*ptr == '=') { gl->optarg = ptr + 1; } else if (gl->optind + 1 >= argc) { /* Argument not found? */ if (p->has_arg == required_argument) { if (gl->opterr) { fprintf(stderr, "option requres an argument -- %s\n", p->name); } ans = '?'; } } else { gl->optarg = argv[gl->optind + 1]; if (firstNonopt != -1) { /* insert gl->optind to just before firstNonopt */ swap2((char **)argv, gl->optind, firstNonopt); gl->optind = firstNonopt; } /* Following is to use up the option and argument */ gl->optind += 2; return ans; } if (firstNonopt != -1) { /* insert gl->optind to just before firstNonopt */ swap1((char **)argv, gl->optind, firstNonopt); gl->optind = firstNonopt; } gl->optind++; return ans; } /* Determine if there is no way to have a short option. */ if (f_type == 2) { if (gl->opterr) { fprintf(stderr, "unknown option -- %s\n", argv[gl->optind] + 2); } gl->optind++; return '?'; } } /* We are now dealing with a short option. */ if ((ptr = strchr(optShort, argv[gl->optind][1 + nextChar])) == NULL) { if (gl->opterr) { fprintf(stderr, "unknown option - %c\n", argv[gl->optind][1 + nextChar]); } gl->optopt = argv[gl->optind][1 + nextChar]; nextChar++; if (argv[gl->optind][1 + nextChar] == '\0') { nextChar = 0; gl->optind++; } return '?'; } /* Found short option. */ ans = *ptr; /* Deal with optional / required arguments */ if (*(ptr + 1) == ':') { if (argv[gl->optind][2 + nextChar] != '\0') { gl->optarg = argv[gl->optind] + 2 + nextChar; } else if (gl->optind + 1 >= argc) { /* Argument not found? */ if (*(ptr + 2) != ':') { if (gl->opterr) { fprintf(stderr, "option requres an argument -- %c\n", ans); } ans = '?'; } } else { gl->optarg = argv[gl->optind + 1]; if (firstNonopt != -1) { /* insert gl->optind to just before firstNonopt */ swap2((char **)argv, gl->optind, firstNonopt); gl->optind = firstNonopt; } /* Following is to use up the option and argument */ gl->optind += 2; nextChar = 0; return ans; } if (firstNonopt != -1) { /* insert gl->optind to just before firstNonopt */ swap1((char **)argv, gl->optind, firstNonopt); gl->optind = firstNonopt; } gl->optind++; nextChar = 0; return ans; } /* Increment to next location in short option list. */ nextChar++; if (argv[gl->optind][1 + nextChar] == '\0') { nextChar = 0; if (firstNonopt != -1) { /* insert gl->optind to just before firstNonopt */ swap1((char **)argv, gl->optind, firstNonopt); gl->optind = firstNonopt; } gl->optind++; } else { /* Since we didn't swap, we want to make sure that we don't skip * any early non-options when we go back to look at more short options */ if (firstNonopt != -1) { gl->optind = firstNonopt; } } return ans; } /***************************************************************************** * main() (Private) -- Arthur Taylor / MDL * * PURPOSE * To test the myGetOpt routines, to make sure that they function correctly * Focused on long options. * * ARGUMENTS * argc = The number of arguments on the command line. (Input) * argv = The arguments on the command line. (Input) * * RETURNS: int * * HISTORY * 8/2007 AAT (MDL): Commented * * NOTES ****************************************************************************/ #ifdef DEBUG_MYOPT_LONG int main(int argc, char **argv) { int c; /* Flag set by `--verbose'. */ static int verbose_flag; static char optShort[] = "abc:d:f:"; static struct option optLong[] = { /* These options set a flag. */ {"verbose", no_argument, &verbose_flag, 1}, {"brief", no_argument, &verbose_flag, 0}, /* These options don't set a flag. We distinguish them by their * indices. */ {"add", no_argument, 0, 'a'}, {"append", no_argument, 0, 'b'}, {"delete", required_argument, 0, 'd'}, {"create", required_argument, 0, 'c'}, {"file", required_argument, 0, 'f'}, {0, 0, 0, 0} }; struct getOptRet getOp; getOp.opterr = 1; while ((c = myGetOpt(argc, argv, optShort, optLong, &getOp)) != -1) { switch (c) { case 0: /* If this option set a flag, do nothing else now. */ if (optLong[getOp.index].flag != NULL) { break; } printf("option %s", optLong[getOp.index].name); if (getOp.optarg) { printf(" with arg %s", getOp.optarg); } printf("\n"); break; case 'a': puts("option -a\n"); break; case 'b': puts("option -b\n"); break; case 'c': printf("option -c with value `%s'\n", getOp.optarg); break; case 'd': printf("option -d with value `%s'\n", getOp.optarg); break; case 'f': printf("option -f with value `%s'\n", getOp.optarg); break; case '?': /* getopt_long already printed an error message. */ break; default: abort(); } } if (verbose_flag) { puts("verbose flag is set"); } /* Print any remaining command line arguments (not options). */ if (getOp.optind < argc) { printf("non-option ARGV-elements: "); while (getOp.optind < argc) { printf("%s ", argv[getOp.optind++]); } putchar('\n'); } exit(0); } #endif /***************************************************************************** * main() (Private) -- Arthur Taylor / MDL * * PURPOSE * To test the myGetOpt routines, to make sure that they function correctly * Focused on short options. * * ARGUMENTS * argc = The number of arguments on the command line. (Input) * argv = The arguments on the command line. (Input) * * RETURNS: int * * HISTORY * 8/2007 AAT (MDL): Commented * * NOTES ****************************************************************************/ #ifdef DEBUG_MYOPT_SHORT int main (int argc, char **argv) { int aflag = 0; int bflag = 0; char *cvalue = NULL; int index; int c; struct getOptRet getOp; getOp.opterr = 0; while ((c = myGetOpt(argc, argv, "abc:", NULL, &getOp)) != -1) { switch (c) { case 'a': aflag = 1; break; case 'b': bflag = 1; break; case 'c': cvalue = getOp.optarg; break; case '?': if (getOp.optopt == 'c') fprintf (stderr, "Option -%c requires an argument.\n", getOp.optopt); else if (isprint (getOp.optopt)) fprintf (stderr, "Unknown option `-%c'.\n", getOp.optopt); else fprintf (stderr, "Unknown option character `\\x%x'.\n", getOp.optopt); return 1; default: abort (); } } printf ("aflag = %d, bflag = %d, cvalue = %s\n", aflag, bflag, cvalue); for (index = getOp.optind; index < argc; index++) printf ("Non-option argument %s\n", argv[index]); return 0; } #endif