home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / languages / tcl / tk3.3b1 / tkArgv.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-06-16  |  11.9 KB  |  445 lines

  1. /*
  2.  * tkArgv.c --
  3.  *
  4.  *    This file contains a procedure that handles table-based
  5.  *    argv-argc parsing.
  6.  *
  7.  * Copyright (c) 1990-1993 The Regents of the University of California.
  8.  * All rights reserved.
  9.  *
  10.  * Permission is hereby granted, without written agreement and without
  11.  * license or royalty fees, to use, copy, modify, and distribute this
  12.  * software and its documentation for any purpose, provided that the
  13.  * above copyright notice and the following two paragraphs appear in
  14.  * all copies of this software.
  15.  * 
  16.  * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
  17.  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
  18.  * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
  19.  * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  20.  *
  21.  * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
  22.  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
  23.  * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
  24.  * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
  25.  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  26.  */
  27.  
  28. #ifndef lint
  29. static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkArgv.c,v 1.15 93/06/16 17:16:19 ouster Exp $ SPRITE (Berkeley)";
  30. #endif
  31.  
  32. #include "tkConfig.h"
  33. #include "tk.h"
  34.  
  35. /*
  36.  * Default table of argument descriptors.  These are normally available
  37.  * in every application.
  38.  */
  39.  
  40. static Tk_ArgvInfo defaultTable[] = {
  41.     {"-help",    TK_ARGV_HELP,    (char *) NULL,    (char *) NULL,
  42.     "Print summary of command-line options and abort"},
  43.     {NULL,    TK_ARGV_END,    (char *) NULL,    (char *) NULL,
  44.     (char *) NULL}
  45. };
  46.  
  47. /*
  48.  * Forward declarations for procedures defined in this file:
  49.  */
  50.  
  51. static void    PrintUsage _ANSI_ARGS_((Tcl_Interp *interp,
  52.             Tk_ArgvInfo *argTable, int flags));
  53.  
  54. /*
  55.  *----------------------------------------------------------------------
  56.  *
  57.  * Tk_ParseArgv --
  58.  *
  59.  *    Process an argv array according to a table of expected
  60.  *    command-line options.  See the manual page for more details.
  61.  *
  62.  * Results:
  63.  *    The return value is a standard Tcl return value.  If an
  64.  *    error occurs then an error message is left in interp->result.
  65.  *    Under normal conditions, both *argcPtr and *argv are modified
  66.  *    to return the arguments that couldn't be processed here (they
  67.  *    didn't match the option table, or followed an TK_ARGV_REST
  68.  *    argument).
  69.  *
  70.  * Side effects:
  71.  *    Variables may be modified, resources may be entered for tkwin,
  72.  *    or procedures may be called.  It all depends on the arguments
  73.  *    and their entries in argTable.  See the user documentation
  74.  *    for details.
  75.  *
  76.  *----------------------------------------------------------------------
  77.  */
  78.  
  79. int
  80. Tk_ParseArgv(interp, tkwin, argcPtr, argv, argTable, flags)
  81.     Tcl_Interp *interp;        /* Place to store error message. */
  82.     Tk_Window tkwin;        /* Window to use for setting Tk options.
  83.                  * NULL means ignore Tk option specs. */
  84.     int *argcPtr;        /* Number of arguments in argv.  Modified
  85.                  * to hold # args left in argv at end. */
  86.     char **argv;        /* Array of arguments.  Modified to hold
  87.                  * those that couldn't be processed here. */
  88.     Tk_ArgvInfo *argTable;    /* Array of option descriptions */
  89.     int flags;            /* Or'ed combination of various flag bits,
  90.                  * such as TK_ARGV_NO_DEFAULTS. */
  91. {
  92.     register Tk_ArgvInfo *infoPtr;
  93.                 /* Pointer to the current entry in the
  94.                  * table of argument descriptions. */
  95.     Tk_ArgvInfo *matchPtr;    /* Descriptor that matches current argument. */
  96.     char *curArg;        /* Current argument */
  97.     register char c;        /* Second character of current arg (used for
  98.                  * quick check for matching;  use 2nd char.
  99.                  * because first char. will almost always
  100.                  * be '-'). */
  101.     int srcIndex;        /* Location from which to read next argument
  102.                  * from argv. */
  103.     int dstIndex;        /* Index into argv to which next unused
  104.                  * argument should be copied (never greater
  105.                  * than srcIndex). */
  106.     int argc;            /* # arguments in argv still to process. */
  107.     int length;            /* Number of characters in current argument. */
  108.     int i;
  109.  
  110.     if (flags & TK_ARGV_DONT_SKIP_FIRST_ARG) {
  111.     srcIndex = dstIndex = 0;
  112.     argc = *argcPtr;
  113.     } else {
  114.     srcIndex = dstIndex = 1;
  115.     argc = *argcPtr-1;
  116.     }
  117.  
  118.     while (argc > 0) {
  119.     curArg = argv[srcIndex];
  120.     srcIndex++;
  121.     argc--;
  122.     c = curArg[1];
  123.     length = strlen(curArg);
  124.  
  125.     /*
  126.      * Loop throught the argument descriptors searching for one with
  127.      * the matching key string.  If found, leave a pointer to it in
  128.      * matchPtr.
  129.      */
  130.  
  131.     matchPtr = NULL;
  132.     for (i = 0; i < 2; i++) {
  133.         if (i == 0) {
  134.         infoPtr = argTable;
  135.         } else {
  136.         infoPtr = defaultTable;
  137.         }
  138.         for (; (infoPtr != NULL) && (infoPtr->type != TK_ARGV_END);
  139.             infoPtr++) {
  140.          if (infoPtr->key == NULL) {
  141.              continue;
  142.          }
  143.          if ((infoPtr->key[1] != c)
  144.              || (strncmp(infoPtr->key, curArg, length) != 0)) {
  145.              continue;
  146.          }
  147.          if ((tkwin == NULL)
  148.              && ((infoPtr->type == TK_ARGV_CONST_OPTION)
  149.              || (infoPtr->type == TK_ARGV_OPTION_VALUE)
  150.              || (infoPtr->type == TK_ARGV_OPTION_NAME_VALUE))) {
  151.              continue;
  152.          }
  153.          if (infoPtr->key[length] == 0) {
  154.              matchPtr = infoPtr;
  155.              goto gotMatch;
  156.          }
  157.          if (flags & TK_ARGV_NO_ABBREV) {
  158.              continue;
  159.          }
  160.          if (matchPtr != NULL) {
  161.              Tcl_AppendResult(interp, "ambiguous option \"", curArg,
  162.                  "\"", (char *) NULL);
  163.              return TCL_ERROR;
  164.          }
  165.          matchPtr = infoPtr;
  166.         }
  167.     }
  168.     if (matchPtr == NULL) {
  169.  
  170.         /*
  171.          * Unrecognized argument.  Just copy it down, unless the caller
  172.          * prefers an error to be registered.
  173.          */
  174.  
  175.         if (flags & TK_ARGV_NO_LEFTOVERS) {
  176.         Tcl_AppendResult(interp, "unrecognized argument \"",
  177.             curArg, "\"", (char *) NULL);
  178.         return TCL_ERROR;
  179.         }
  180.         argv[dstIndex] = curArg;
  181.         dstIndex++;
  182.         continue;
  183.     }
  184.  
  185.     /*
  186.      * Take the appropriate action based on the option type
  187.      */
  188.  
  189.     gotMatch:
  190.     infoPtr = matchPtr;
  191.     switch (infoPtr->type) {
  192.         case TK_ARGV_CONSTANT:
  193.         *((int *) infoPtr->dst) = (int) infoPtr->src;
  194.         break;
  195.         case TK_ARGV_INT:
  196.         if (argc == 0) {
  197.             goto missingArg;
  198.         } else {
  199.             char *endPtr;
  200.  
  201.             *((int *) infoPtr->dst) =
  202.                 strtol(argv[srcIndex], &endPtr, 0);
  203.             if ((endPtr == argv[srcIndex]) || (*endPtr != 0)) {
  204.             Tcl_AppendResult(interp, "expected integer argument ",
  205.                 "for \"", infoPtr->key, "\" but got \"",
  206.                 argv[srcIndex], "\"", (char *) NULL);
  207.             return TCL_ERROR;
  208.             }
  209.             srcIndex++;
  210.             argc--;
  211.         }
  212.         break;
  213.         case TK_ARGV_STRING:
  214.         if (argc == 0) {
  215.             goto missingArg;
  216.         } else {
  217.             *((char **)infoPtr->dst) = argv[srcIndex];
  218.             srcIndex++;
  219.             argc--;
  220.         }
  221.         break;
  222.         case TK_ARGV_UID:
  223.         if (argc == 0) {
  224.             goto missingArg;
  225.         } else {
  226.             *((Tk_Uid *)infoPtr->dst) = Tk_GetUid(argv[srcIndex]);
  227.             srcIndex++;
  228.             argc--;
  229.         }
  230.         break;
  231.         case TK_ARGV_REST:
  232.         *((int *) infoPtr->dst) = dstIndex;
  233.         goto argsDone;
  234.         case TK_ARGV_FLOAT:
  235.         if (argc == 0) {
  236.             goto missingArg;
  237.         } else {
  238.             char *endPtr;
  239.  
  240.             *((double *) infoPtr->dst) =
  241.                 strtod(argv[srcIndex], &endPtr);
  242.             if ((endPtr == argv[srcIndex]) || (*endPtr != 0)) {
  243.             Tcl_AppendResult(interp, "expected floating-point ",
  244.                 "argument for \"", infoPtr->key,
  245.                 "\" but got \"", argv[srcIndex], "\"",
  246.                 (char *) NULL);
  247.             return TCL_ERROR;
  248.             }
  249.             srcIndex++;
  250.             argc--;
  251.         }
  252.         break;
  253.         case TK_ARGV_FUNC: {
  254.         int (*handlerProc)();
  255.  
  256.         handlerProc = (int (*)())infoPtr->src;
  257.         
  258.         if ((*handlerProc)(infoPtr->dst, infoPtr->key,
  259.             argv[srcIndex])) {
  260.             srcIndex += 1;
  261.             argc -= 1;
  262.         }
  263.         break;
  264.         }
  265.         case TK_ARGV_GENFUNC: {
  266.         int        (*handlerProc)();
  267.  
  268.         handlerProc = (int (*)())infoPtr->src;
  269.  
  270.         argc = (*handlerProc)(infoPtr->dst, interp, infoPtr->key,
  271.             argc, argv+srcIndex);
  272.         if (argc < 0) {
  273.             return TCL_ERROR;
  274.         }
  275.         break;
  276.         }
  277.         case TK_ARGV_HELP:
  278.         PrintUsage (interp, argTable, flags);
  279.         return TCL_ERROR;
  280.         case TK_ARGV_CONST_OPTION:
  281.         Tk_AddOption(tkwin, infoPtr->dst, infoPtr->src,
  282.             TK_INTERACTIVE_PRIO);
  283.         break;
  284.         case TK_ARGV_OPTION_VALUE:
  285.         if (argc < 1) {
  286.             goto missingArg;
  287.         }
  288.         Tk_AddOption(tkwin, infoPtr->dst, argv[srcIndex],
  289.             TK_INTERACTIVE_PRIO);
  290.         srcIndex++;
  291.         argc--;
  292.         break;
  293.         case TK_ARGV_OPTION_NAME_VALUE:
  294.         if (argc < 2) {
  295.             Tcl_AppendResult(interp, "\"", curArg,
  296.                 "\" option requires two following arguments",
  297.                 (char *) NULL);
  298.             return TCL_ERROR;
  299.         }
  300.         Tk_AddOption(tkwin, argv[srcIndex], argv[srcIndex+1],
  301.             TK_INTERACTIVE_PRIO);
  302.         srcIndex += 2;
  303.         argc -= 2;
  304.         break;
  305.         default:
  306.         sprintf(interp->result, "bad argument type %d in Tk_ArgvInfo",
  307.             infoPtr->type);
  308.         return TCL_ERROR;
  309.     }
  310.     }
  311.  
  312.     /*
  313.      * If we broke out of the loop because of an OPT_REST argument,
  314.      * copy the remaining arguments down.
  315.      */
  316.  
  317.     argsDone:
  318.     while (argc) {
  319.     argv[dstIndex] = argv[srcIndex];
  320.     srcIndex++;
  321.     dstIndex++;
  322.     argc--;
  323.     }
  324.     argv[dstIndex] = (char *) NULL;
  325.     *argcPtr = dstIndex;
  326.     return TCL_OK;
  327.  
  328.     missingArg:
  329.     Tcl_AppendResult(interp, "\"", curArg,
  330.         "\" option requires an additional argument", (char *) NULL);
  331.     return TCL_ERROR;
  332. }
  333.  
  334. /*
  335.  *----------------------------------------------------------------------
  336.  *
  337.  * PrintUsage --
  338.  *
  339.  *    Generate a help string describing command-line options.
  340.  *
  341.  * Results:
  342.  *    Interp->result will be modified to hold a help string
  343.  *    describing all the options in argTable, plus all those
  344.  *    in the default table unless TK_ARGV_NO_DEFAULTS is
  345.  *    specified in flags.
  346.  *
  347.  * Side effects:
  348.  *    None.
  349.  *
  350.  *----------------------------------------------------------------------
  351.  */
  352.  
  353. static void
  354. PrintUsage(interp, argTable, flags)
  355.     Tcl_Interp *interp;        /* Place information in this interp's
  356.                  * result area. */
  357.     Tk_ArgvInfo *argTable;    /* Array of command-specific argument
  358.                  * descriptions. */
  359.     int flags;            /* If the TK_ARGV_NO_DEFAULTS bit is set
  360.                  * in this word, then don't generate
  361.                  * information for default options. */
  362. {
  363.     register Tk_ArgvInfo *infoPtr;
  364.     int width, i, numSpaces;
  365. #define NUM_SPACES 20
  366.     static char spaces[] = "                    ";
  367.     char tmp[30];
  368.  
  369.     /*
  370.      * First, compute the width of the widest option key, so that we
  371.      * can make everything line up.
  372.      */
  373.  
  374.     width = 4;
  375.     for (i = 0; i < 2; i++) {
  376.     for (infoPtr = i ? defaultTable : argTable;
  377.         infoPtr->type != TK_ARGV_END; infoPtr++) {
  378.         int length;
  379.         if (infoPtr->key == NULL) {
  380.         continue;
  381.         }
  382.         length = strlen(infoPtr->key);
  383.         if (length > width) {
  384.         width = length;
  385.         }
  386.     }
  387.     }
  388.  
  389.     Tcl_AppendResult(interp, "Command-specific options:", (char *) NULL);
  390.     for (i = 0; ; i++) {
  391.     for (infoPtr = i ? defaultTable : argTable;
  392.         infoPtr->type != TK_ARGV_END; infoPtr++) {
  393.         if ((infoPtr->type == TK_ARGV_HELP) && (infoPtr->key == NULL)) {
  394.         Tcl_AppendResult(interp, "\n", infoPtr->help, (char *) NULL);
  395.         continue;
  396.         }
  397.         Tcl_AppendResult(interp, "\n ", infoPtr->key, ":", (char *) NULL);
  398.         numSpaces = width + 1 - strlen(infoPtr->key);
  399.         while (numSpaces > 0) {
  400.         if (numSpaces >= NUM_SPACES) {
  401.             Tcl_AppendResult(interp, spaces, (char *) NULL);
  402.         } else {
  403.             Tcl_AppendResult(interp, spaces+NUM_SPACES-numSpaces,
  404.                 (char *) NULL);
  405.         }
  406.         numSpaces -= NUM_SPACES;
  407.         }
  408.         Tcl_AppendResult(interp, infoPtr->help, (char *) NULL);
  409.         switch (infoPtr->type) {
  410.         case TK_ARGV_INT: {
  411.             sprintf(tmp, "%d", *((int *) infoPtr->dst));
  412.             Tcl_AppendResult(interp, "\n\t\tDefault value: ",
  413.                 tmp, (char *) NULL);
  414.             break;
  415.         }
  416.         case TK_ARGV_FLOAT: {
  417.             sprintf(tmp, "%g", *((double *) infoPtr->dst));
  418.             Tcl_AppendResult(interp, "\n\t\tDefault value: ",
  419.                 tmp, (char *) NULL);
  420.             break;
  421.         }
  422.         case TK_ARGV_STRING: {
  423.             char *string;
  424.  
  425.             string = *((char **) infoPtr->dst);
  426.             if (string != NULL) {
  427.             Tcl_AppendResult(interp, "\n\t\tDefault value: \"",
  428.                 string, "\"", (char *) NULL);
  429.             }
  430.             break;
  431.         }
  432.         default: {
  433.             break;
  434.         }
  435.         }
  436.     }
  437.  
  438.     if ((flags & TK_ARGV_NO_DEFAULTS) || (i > 0)) {
  439.         break;
  440.     }
  441.     Tcl_AppendResult(interp, "\nGeneric options for all commands:",
  442.         (char *) NULL);
  443.     }
  444. }
  445.