home *** CD-ROM | disk | FTP | other *** search
/ Chip 1998 February / CHIP_2_98.iso / misc / src / rpm / popt / popt.c next >
C/C++ Source or Header  |  1997-09-17  |  14KB  |  588 lines

  1. #include <errno.h>
  2. #include <ctype.h>
  3. #include <fcntl.h>
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <string.h>
  7. #include <sys/mman.h>
  8. #include <unistd.h>
  9.  
  10. #include "popt.h"
  11.  
  12. struct optionStackEntry {
  13.     int argc;
  14.     char ** argv;
  15.     int next;
  16.     char * nextArg;
  17.     char * nextCharArg;
  18.     struct poptAlias * currAlias;
  19. };
  20.  
  21. struct poptContext_s {
  22.     struct optionStackEntry optionStack[POPT_OPTION_DEPTH], * os;
  23.     char ** leftovers;
  24.     int numLeftovers;
  25.     int nextLeftover;
  26.     struct poptOption * options;
  27.     int restLeftover;
  28.     char * appName;
  29.     struct poptAlias * aliases;
  30.     int numAliases;
  31. };
  32.  
  33. poptContext poptGetContext(char * name ,int argc, char ** argv, 
  34.                struct poptOption * options, int flags) {
  35.     poptContext con = malloc(sizeof(*con));
  36.  
  37.     con->os = con->optionStack;
  38.     con->os->argc = argc;
  39.     con->os->argv = argv;
  40.     con->os->currAlias = NULL;
  41.     con->os->nextCharArg = NULL;
  42.     con->os->nextArg = NULL;
  43.     con->os->next = 1;            /* skip argv[0] */
  44.  
  45.     con->leftovers = malloc(sizeof(char *) * (argc + 1));
  46.     con->numLeftovers = 0;
  47.     con->nextLeftover = 0;
  48.     con->restLeftover = 0;
  49.     con->options = options;
  50.     con->aliases = NULL;
  51.     con->numAliases = 0;
  52.     
  53.     if (!name)
  54.     con->appName = NULL;
  55.     else
  56.     con->appName = strcpy(malloc(strlen(name) + 1), name);
  57.  
  58.     return con;
  59. }
  60.  
  61. void poptResetContext(poptContext con) {
  62.     con->os = con->optionStack;
  63.     con->os->currAlias = NULL;
  64.     con->os->nextCharArg = NULL;
  65.     con->os->nextArg = NULL;
  66.     con->os->next = 1;            /* skip argv[0] */
  67.  
  68.     con->numLeftovers = 0;
  69.     con->nextLeftover = 0;
  70.     con->restLeftover = 0;
  71. }
  72.  
  73. /* returns 'val' element, -1 on last item, POPT_ERROR_* on error */
  74. int poptGetNextOpt(poptContext con) {
  75.     char * optString, * chptr, * localOptString;
  76.     char * longArg = NULL;
  77.     char * origOptString;
  78.     struct poptOption * opt = NULL;
  79.     int done = 0;
  80.     int i;
  81.  
  82.     while (!done) {
  83.     while (!con->os->nextCharArg && con->os->next == con->os->argc 
  84.         && con->os > con->optionStack)
  85.         con->os--;
  86.     if (!con->os->nextCharArg && con->os->next == con->os->argc)
  87.         return -1;
  88.  
  89.     if (!con->os->nextCharArg) {
  90.         
  91.         origOptString = con->os->argv[con->os->next++];
  92.  
  93.         if (con->restLeftover || *origOptString != '-') {
  94.         con->leftovers[con->numLeftovers++] = origOptString;
  95.         continue;
  96.         }
  97.  
  98.         /* Make a copy we can hack at */
  99.         localOptString = optString = 
  100.             strcpy(alloca(strlen(origOptString) + 1), 
  101.             origOptString);
  102.  
  103.         if (!optString[0])
  104.         return POPT_ERROR_BADOPT;
  105.  
  106.         if (optString[1] == '-' && !optString[2]) {
  107.         con->restLeftover = 1;
  108.         continue;
  109.         } else if (optString[1] == '-') {
  110.         optString += 2;
  111.  
  112.         if (!con->os->currAlias || !con->os->currAlias->longName || 
  113.             strcmp(con->os->currAlias->longName, optString)) {
  114.  
  115.             i = con->numAliases - 1;
  116.             while (i >= 0 && (!con->aliases[i].longName ||
  117.             strcmp(con->aliases[i].longName, optString))) i--;
  118.  
  119.             if (i >= 0) {
  120.             if ((con->os - con->optionStack + 1) 
  121.                 == POPT_OPTION_DEPTH)
  122.                 return POPT_ERROR_OPTSTOODEEP;
  123.  
  124.             con->os++;
  125.             con->os->next = 0;
  126.             con->os->nextArg = con->os->nextCharArg = NULL;
  127.             con->os->currAlias = con->aliases + i;
  128.             con->os->argc = con->os->currAlias->argc;
  129.             con->os->argv = con->os->currAlias->argv;
  130.             continue;
  131.             }
  132.         }
  133.  
  134.         chptr = optString;
  135.         while (*chptr && *chptr != '=') chptr++;
  136.         if (*chptr == '=') {
  137.             longArg = origOptString + (chptr - localOptString) + 1;
  138.             *chptr = '\0';
  139.         }
  140.  
  141.         opt = con->options;
  142.         while (opt->longName || opt->shortName) {
  143.             if (opt->longName && !strcmp(optString, opt->longName))
  144.             break;
  145.             opt++;
  146.         }
  147.  
  148.         if (!opt->longName && !opt->shortName) return POPT_ERROR_BADOPT;
  149.         } else 
  150.         con->os->nextCharArg = origOptString + 1;
  151.     }
  152.  
  153.     if (con->os->nextCharArg) {
  154.         origOptString = con->os->nextCharArg;
  155.  
  156.         con->os->nextCharArg = NULL;
  157.  
  158.         if (!con->os->currAlias || *origOptString != 
  159.         con->os->currAlias->shortName) {
  160.  
  161.         i = con->numAliases - 1;
  162.         while (i >= 0 &&
  163.             con->aliases[i].shortName != *origOptString) i--;
  164.  
  165.         if (i >= 0) {
  166.             if ((con->os - con->optionStack + 1) == POPT_OPTION_DEPTH)
  167.             return POPT_ERROR_OPTSTOODEEP;
  168.  
  169.             /* We'll need this on the way out */
  170.             origOptString++;
  171.             if (*origOptString)
  172.             con->os->nextCharArg = origOptString;
  173.  
  174.             con->os++;
  175.             con->os->next = 0;
  176.             con->os->nextArg = con->os->nextCharArg = NULL;
  177.             con->os->currAlias = con->aliases + i;
  178.             con->os->argc = con->os->currAlias->argc;
  179.             con->os->argv = con->os->currAlias->argv;
  180.             continue;
  181.         }
  182.         }
  183.  
  184.         opt = con->options;
  185.         while ((opt->longName || opt->shortName) && 
  186.             *origOptString != opt->shortName) opt++;
  187.         if (!opt->longName && !opt->shortName) return POPT_ERROR_BADOPT;
  188.  
  189.         origOptString++;
  190.         if (*origOptString)
  191.         con->os->nextCharArg = origOptString;
  192.     }
  193.  
  194.     if (opt->arg && opt->argInfo == POPT_ARG_NONE) 
  195.         *((int *)opt->arg) = 1;
  196.     else if (opt->argInfo != POPT_ARG_NONE) {
  197.         if (longArg) {
  198.         con->os->nextArg = longArg;
  199.         } else if (con->os->nextCharArg) {
  200.         con->os->nextArg = con->os->nextCharArg;
  201.         con->os->nextCharArg = NULL;
  202.         } else { 
  203.         while (con->os->next == con->os->argc && 
  204.                con->os > con->optionStack)
  205.             con->os--;
  206.         if (con->os->next == con->os->argc)
  207.             return POPT_ERROR_NOARG;
  208.  
  209.         con->os->nextArg = con->os->argv[con->os->next++];
  210.         }
  211.  
  212.         if (opt->arg) {
  213.         switch (opt->argInfo) {
  214.           case POPT_ARG_STRING:
  215.             *((char **) opt->arg) = con->os->nextArg;
  216.             break;
  217.           default:
  218.             printf("option type not implemented in popt\n");
  219.             exit(1);
  220.         }
  221.         }
  222.     }
  223.  
  224.     if (opt->val) done = 1;
  225.     }
  226.  
  227.     return opt->val;
  228. }
  229.  
  230. char * poptGetOptArg(poptContext con) {
  231.     char * ret = con->os->nextArg;
  232.     con->os->nextArg = NULL;
  233.     return ret;
  234. }
  235.  
  236. char * poptGetArg(poptContext con) {
  237.     if (con->numLeftovers == con->nextLeftover) return NULL;
  238.     return (con->leftovers[con->nextLeftover++]);
  239. }
  240.  
  241. char * poptPeekArg(poptContext con) {
  242.     if (con->numLeftovers == con->nextLeftover) return NULL;
  243.     return (con->leftovers[con->nextLeftover]);
  244. }
  245.  
  246. char ** poptGetArgs(poptContext con) {
  247.     if (con->numLeftovers == con->nextLeftover) return NULL;
  248.  
  249.     /* some apps like [like RPM ;-) ] need this NULL terminated */
  250.     con->leftovers[con->numLeftovers] = NULL;
  251.  
  252.     return (con->leftovers + con->nextLeftover);
  253. }
  254.  
  255. void poptFreeContext(poptContext con) {
  256.     int i;
  257.  
  258.     for (i = 0; i < con->numAliases; i++) {
  259.     free(con->aliases[i].longName);
  260.     free(con->aliases[i].argv);
  261.     }
  262.  
  263.     free(con->leftovers);
  264.     if (con->appName) free(con->appName);
  265.     if (con->aliases) free(con->aliases);
  266.     free(con);
  267. }
  268.  
  269. int poptAddAlias(poptContext con, struct poptAlias newAlias, int flags) {
  270.     int aliasNum = con->numAliases++;
  271.     struct poptAlias * alias;
  272.  
  273.     /* SunOS won't realloc(NULL, ...) */
  274.     if (!con->aliases)
  275.     con->aliases = malloc(sizeof(newAlias) * con->numAliases);
  276.     else
  277.     con->aliases = realloc(con->aliases, 
  278.                    sizeof(newAlias) * con->numAliases);
  279.     alias = con->aliases + aliasNum;
  280.     
  281.     *alias = newAlias;
  282.     if (alias->longName)
  283.     alias->longName = strcpy(malloc(strlen(alias->longName) + 1), 
  284.                     alias->longName);
  285.     else
  286.     alias->longName = NULL;
  287.  
  288.     return 0;
  289. }
  290.  
  291. int poptParseArgvString(char * s, int * argcPtr, char *** argvPtr) {
  292.     char * buf = strcpy(alloca(strlen(s) + 1), s);
  293.     char * bufStart = buf;
  294.     char * src, * dst;
  295.     char quote = '\0';
  296.     int argvAlloced = 5;
  297.     char ** argv = malloc(sizeof(*argv) * argvAlloced);
  298.     char ** argv2;
  299.     int argc = 0;
  300.     int i;
  301.  
  302.     src = s;
  303.     dst = buf;
  304.     argv[argc] = buf;
  305.  
  306.     memset(buf, '\0', strlen(s) + 1);
  307.  
  308.     while (*src) {
  309.     if (quote == *src) {
  310.         quote = '\0';
  311.     } else if (quote) {
  312.         if (*src == '\\') {
  313.         src++;
  314.         if (!*src) {
  315.             free(argv);
  316.             return POPT_ERROR_BADQUOTE;
  317.         }
  318.         if (*src != quote) *buf++ = '\\';
  319.         }
  320.         *buf++ = *src;
  321.     } else if (isspace(*src)) {
  322.         if (*argv[argc]) {
  323.         buf++, argc++;
  324.         if (argc == argvAlloced) {
  325.             argvAlloced += 5;
  326.             argv = realloc(argv, sizeof(*argv) * argvAlloced);
  327.         }
  328.         argv[argc] = buf;
  329.         }
  330.     } else switch (*src) {
  331.       case '"':
  332.       case '\'':
  333.         quote = *src;
  334.         break;
  335.       case '\\':
  336.         src++;
  337.         if (!*src) {
  338.         free(argv);
  339.         return POPT_ERROR_BADQUOTE;
  340.         }
  341.         /* fallthrough */
  342.       default:
  343.         *buf++ = *src;
  344.     }
  345.  
  346.     src++;
  347.     }
  348.  
  349.     if (strlen(argv[argc])) {
  350.     argc++, buf++;
  351.     }
  352.  
  353.     argv2 = (void * )dst = malloc(argc * sizeof(*argv) + (buf - bufStart));
  354.     dst += argc * sizeof(*argv);
  355.     memcpy(argv2, argv, argc * sizeof(*argv));
  356.     memcpy(dst, bufStart, buf - bufStart);
  357.  
  358.     for (i = 0; i < argc; i++) {
  359.     argv2[i] = dst + (argv[i] - bufStart);
  360.     }
  361.  
  362.     free(argv);
  363.  
  364.     *argvPtr = argv2;
  365.     *argcPtr = argc;
  366.  
  367.     return 0;
  368. }
  369.  
  370. static void configLine(poptContext con, char * line) {
  371.     int nameLength = strlen(con->appName);
  372.     char * opt;
  373.     struct poptAlias alias;
  374.     
  375.     if (strncmp(line, con->appName, nameLength)) return;
  376.     line += nameLength;
  377.     if (!*line || !isspace(*line)) return;
  378.     while (*line && isspace(*line)) line++;
  379.  
  380.     if (!strncmp(line, "alias", 5)) {
  381.     line += 5;
  382.     if (!*line || !isspace(*line)) return;
  383.     while (*line && isspace(*line)) line++;
  384.     if (!*line) return;
  385.  
  386.     opt = line;
  387.     while (*line && !isspace(*line)) line++;
  388.     if (!*line) return;
  389.     *line++ = '\0';
  390.     while (*line && isspace(*line)) line++;
  391.     if (!*line) return;
  392.  
  393.     if (!strlen(opt)) return;
  394.  
  395.     if (poptParseArgvString(line, &alias.argc, &alias.argv)) return;
  396.  
  397.     if (opt[0] == '-' && opt[1] == '-') {
  398.         alias.longName = opt + 2;
  399.         alias.shortName = '\0';
  400.         poptAddAlias(con, alias, 0);
  401.     } else if (opt[0] == '-' && !opt[2]) {
  402.         alias.longName = NULL;
  403.         alias.shortName = opt[1];
  404.         poptAddAlias(con, alias, 0);
  405.     }
  406.     }
  407. }
  408.  
  409. int poptReadConfigFile(poptContext con, char * fn) {
  410.     char * file, * chptr, * end;
  411.     char * buf, * dst;
  412.     int fd, rc;
  413.     int fileLength;
  414.  
  415.     fd = open(fn, O_RDONLY);
  416.     if (fd < 0) {
  417.     if (errno == ENOENT)
  418.         return 0;
  419.     else 
  420.         return POPT_ERROR_ERRNO;
  421.     }
  422.  
  423.     fileLength = lseek(fd, 0, SEEK_END);
  424.     lseek(fd, 0, 0);
  425.  
  426.     file = mmap(NULL, fileLength, PROT_READ, MAP_PRIVATE, fd, 0);
  427.     if (file == (void *) -1) {
  428.     rc = errno;
  429.     close(fd);
  430.     errno = rc;
  431.     return POPT_ERROR_ERRNO;
  432.     }
  433.     close(fd);
  434.  
  435.     dst = buf = alloca(fileLength + 1);
  436.  
  437.     chptr = file;
  438.     end = (file + fileLength);
  439.     while (chptr < end) {
  440.     switch (*chptr) {
  441.       case '\n':
  442.         *dst = '\0';
  443.         dst = buf;
  444.         while (*dst && isspace(*dst)) dst++;
  445.         if (*dst && *dst != '#') {
  446.         configLine(con, dst);
  447.         }
  448.         chptr++;
  449.         break;
  450.       case '\\':
  451.         *dst++ = *chptr++;
  452.         if (chptr < end) {
  453.         if (*chptr == '\n') 
  454.             dst--, chptr++;    
  455.             /* \ at the end of a line does not insert a \n */
  456.         else
  457.             *dst++ = *chptr++;
  458.         }
  459.         break;
  460.       default:
  461.         *dst++ = *chptr++;
  462.     }
  463.     }
  464.  
  465.     return 0;
  466. }
  467.  
  468. int poptReadDefaultConfig(poptContext con, int useEnv) {
  469.     char * envName, * envValue;
  470.     char * fn, * home, * chptr;
  471.     int rc, skip;
  472.     struct poptAlias alias;
  473.  
  474.     if (!con->appName) return 0;
  475.  
  476.     rc = poptReadConfigFile(con, "/etc/popt");
  477.     if (rc) return rc;
  478.     if ((home = getenv("HOME"))) {
  479.     fn = alloca(strlen(home) + 20);
  480.     sprintf(fn, "%s/.popt", home);
  481.     rc = poptReadConfigFile(con, fn);
  482.     if (rc) return rc;
  483.     }
  484.  
  485.     envName = alloca(strlen(con->appName) + 20);
  486.     strcpy(envName, con->appName);
  487.     chptr = envName;
  488.     while (*chptr) {
  489.     *chptr = toupper(*chptr);
  490.     chptr++;
  491.     }
  492.     strcat(envName, "_POPT_ALIASES");
  493.  
  494.     if (useEnv && (envValue = getenv(envName))) {
  495.     envValue = strcpy(alloca(strlen(envValue) + 1), envValue);
  496.  
  497.     while (envValue && *envValue) {
  498.         chptr = strchr(envValue, '=');
  499.         if (!chptr) {
  500.         envValue = strchr(envValue, '\n');
  501.         if (envValue) envValue++;
  502.         continue;
  503.         }
  504.  
  505.         *chptr = '\0';
  506.  
  507.         skip = 0;
  508.         if (!strncmp(envValue, "--", 2)) {
  509.         alias.longName = envValue + 2;
  510.         alias.shortName = '\0';
  511.         } else if (*envValue == '-' && strlen(envValue) == 2) {
  512.         alias.longName = NULL;    
  513.         alias.shortName = envValue[1];
  514.         } else {
  515.         skip = 1;
  516.         }
  517.  
  518.         envValue = chptr + 1;
  519.         chptr = strchr(envValue, '\n');
  520.         if (chptr) *chptr = '\0';
  521.  
  522.         if (!skip) {
  523.         poptParseArgvString(envValue, &alias.argc, &alias.argv);
  524.         poptAddAlias(con, alias, 0);
  525.         }
  526.  
  527.         if (chptr)
  528.         envValue = chptr + 1;
  529.         else
  530.         envValue = NULL;
  531.     }
  532.     }
  533.  
  534.     return 0;
  535. }
  536.  
  537. char * poptBadOption(poptContext con, int flags) {
  538.     struct optionStackEntry * os;
  539.  
  540.     if (flags & POPT_BADOPTION_NOALIAS)
  541.     os = con->optionStack;
  542.     else
  543.     os = con->os;
  544.  
  545.     return os->argv[os->next - 1];
  546. }
  547.  
  548. #define POPT_ERROR_NOARG    -10
  549. #define POPT_ERROR_BADOPT    -11
  550. #define POPT_ERROR_OPTSTOODEEP    -13
  551. #define POPT_ERROR_BADQUOTE    -15    /* only from poptParseArgString() */
  552. #define POPT_ERROR_ERRNO    -16    /* only from poptParseArgString() */
  553.  
  554. const char * poptStrerror(const int error) {
  555.     switch (error) {
  556.       case POPT_ERROR_NOARG:
  557.     return "missing argument";
  558.       case POPT_ERROR_BADOPT:
  559.     return "unknown option";
  560.       case POPT_ERROR_OPTSTOODEEP:
  561.     return "aliases nested too deeply";
  562.       case POPT_ERROR_BADQUOTE:
  563.     return "error in paramter quoting";
  564.       case POPT_ERROR_ERRNO:
  565.     return strerror(errno);
  566.       default:
  567.     return "unknown error";
  568.     }
  569. }
  570.  
  571. int poptStuffArgs(poptContext con, char ** argv) {
  572.     int i;
  573.  
  574.     if ((con->os - con->optionStack) == POPT_OPTION_DEPTH)
  575.     return POPT_ERROR_OPTSTOODEEP;
  576.  
  577.     for (i = 0; argv[i]; i++);
  578.  
  579.     con->os++;
  580.     con->os->next = 0;
  581.     con->os->nextArg = con->os->nextCharArg = NULL;
  582.     con->os->currAlias = NULL;
  583.     con->os->argc = i;
  584.     con->os->argv = argv;
  585.  
  586.     return 0;
  587. }
  588.