home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / cvs-1.8.7-src.tgz / tar.out / fsf / cvs / src / modules.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  22KB  |  905 lines

  1. /*
  2.  *    Copyright (c) 1992, Brian Berliner and Jeff Polk
  3.  *    Copyright (c) 1989-1992, Brian Berliner
  4.  *
  5.  *    You may distribute under the terms of the GNU General Public License
  6.  *    as specified in the README file that comes with the CVS 1.4 kit.
  7.  *
  8.  * Modules
  9.  *
  10.  *    Functions for accessing the modules file.
  11.  *
  12.  *    The modules file supports basically three formats of lines:
  13.  *        key [options] directory files... [ -x directory [files] ] ...
  14.  *        key [options] directory [ -x directory [files] ] ...
  15.  *        key -a aliases...
  16.  *
  17.  *    The -a option allows an aliasing step in the parsing of the modules
  18.  *    file.  The "aliases" listed on a line following the -a are
  19.  *    processed one-by-one, as if they were specified as arguments on the
  20.  *    command line.
  21.  */
  22.  
  23. #include "cvs.h"
  24. #include "savecwd.h"
  25.  
  26.  
  27. /* Defines related to the syntax of the modules file.  */
  28.  
  29. /* Options in modules file.  Note that it is OK to use GNU getopt features;
  30.    we already are arranging to make sure we are using the getopt distributed
  31.    with CVS.  */
  32. #define    CVSMODULE_OPTS    "+ad:i:lo:e:s:t:u:"
  33.  
  34. /* Special delimiter.  */
  35. #define CVSMODULE_SPEC    '&'
  36.  
  37. struct sortrec
  38. {
  39.     char *modname;
  40.     char *status;
  41.     char *rest;
  42.     char *comment;
  43. };
  44.  
  45. static int sort_order PROTO((const PTR l, const PTR r));
  46. static void save_d PROTO((char *k, int ks, char *d, int ds));
  47.  
  48.  
  49. /*
  50.  * Open the modules file, and die if the CVSROOT environment variable
  51.  * was not set.  If the modules file does not exist, that's fine, and
  52.  * a warning message is displayed and a NULL is returned.
  53.  */
  54. DBM *
  55. open_module ()
  56. {
  57.     char mfile[PATH_MAX];
  58.  
  59.     if (CVSroot_original == NULL)
  60.     {
  61.     (void) fprintf (stderr, 
  62.             "%s: must set the CVSROOT environment variable\n",
  63.             program_name);
  64.     error (1, 0, "or specify the '-d' option to %s", program_name);
  65.     }
  66.     (void) sprintf (mfile, "%s/%s/%s", CVSroot_directory,
  67.             CVSROOTADM, CVSROOTADM_MODULES);
  68.     return (dbm_open (mfile, O_RDONLY, 0666));
  69. }
  70.  
  71. /*
  72.  * Close the modules file, if the open succeeded, that is
  73.  */
  74. void
  75. close_module (db)
  76.     DBM *db;
  77. {
  78.     if (db != NULL)
  79.     dbm_close (db);
  80. }
  81.  
  82. /*
  83.  * This is the recursive function that processes a module name.
  84.  * It calls back the passed routine for each directory of a module
  85.  * It runs the post checkout or post tag proc from the modules file
  86.  */
  87. int
  88. do_module (db, mname, m_type, msg, callback_proc, where,
  89.        shorten, local_specified, run_module_prog, extra_arg)
  90.     DBM *db;
  91.     char *mname;
  92.     enum mtype m_type;
  93.     char *msg;
  94.     CALLBACKPROC callback_proc;
  95.     char *where;
  96.     int shorten;
  97.     int local_specified;
  98.     int run_module_prog;
  99.     char *extra_arg;
  100. {
  101.     char *checkin_prog = NULL;
  102.     char *checkout_prog = NULL;
  103.     char *export_prog = NULL;
  104.     char *tag_prog = NULL;
  105.     char *update_prog = NULL;
  106.     struct saved_cwd cwd;
  107.     char *line;
  108.     int modargc;
  109.     int xmodargc;
  110.     char **modargv;
  111.     char *xmodargv[MAXFILEPERDIR];
  112.     char *value;
  113.     char *zvalue;
  114.     char *mwhere = NULL;
  115.     char *mfile = NULL;
  116.     char *spec_opt = NULL;
  117.     char xvalue[PATH_MAX];
  118.     int alias = 0;
  119.     datum key, val;
  120.     char *cp;
  121.     int c, err = 0;
  122.  
  123. #ifdef SERVER_SUPPORT
  124.     if (trace)
  125.     {
  126.     char *buf;
  127.  
  128.     /* We use cvs_outerr, rather than fprintf to stderr, because
  129.        this may be called by server code with error_use_protocol
  130.        set.  */
  131.     buf = xmalloc (100
  132.                + strlen (mname)
  133.                + strlen (msg)
  134.                + (where ? strlen (where) : 0)
  135.                + (extra_arg ? strlen (extra_arg) : 0));
  136.     sprintf (buf, "%c-> do_module (%s, %s, %s, %s)\n",
  137.          (server_active) ? 'S' : ' ',
  138.          mname, msg, where ? where : "",
  139.          extra_arg ? extra_arg : "");
  140.     cvs_outerr (buf, 0);
  141.     free (buf);
  142.     }
  143. #endif
  144.  
  145.     /* if this is a directory to ignore, add it to that list */
  146.     if (mname[0] == '!' && mname[1] != '\0')
  147.     {
  148.     ign_dir_add (mname+1);
  149.     return(err);
  150.     }
  151.  
  152.     /* strip extra stuff from the module name */
  153.     strip_path (mname);
  154.  
  155.     /*
  156.      * Look up the module using the following scheme:
  157.      *    1) look for mname as a module name
  158.      *    2) look for mname as a directory
  159.      *    3) look for mname as a file
  160.      *  4) take mname up to the first slash and look it up as a module name
  161.      *       (this is for checking out only part of a module)
  162.      */
  163.  
  164.     /* look it up as a module name */
  165.     key.dptr = mname;
  166.     key.dsize = strlen (key.dptr);
  167.     if (db != NULL)
  168.     val = dbm_fetch (db, key);
  169.     else
  170.     val.dptr = NULL;
  171.     if (val.dptr != NULL)
  172.     {
  173.     /* null terminate the value  XXX - is this space ours? */
  174.     val.dptr[val.dsize] = '\0';
  175.  
  176.     /* If the line ends in a comment, strip it off */
  177.     if ((cp = strchr (val.dptr, '#')) != NULL)
  178.     {
  179.         do
  180.         *cp-- = '\0';
  181.         while (isspace (*cp));
  182.     }
  183.     else
  184.     {
  185.         /* Always strip trailing spaces */
  186.         cp = strchr (val.dptr, '\0');
  187.         while (cp > val.dptr && isspace(*--cp))
  188.         *cp = '\0';
  189.     }
  190.  
  191.     value = val.dptr;
  192.     mwhere = xstrdup (mname);
  193.     goto found;
  194.     }
  195.     else
  196.     {
  197.     char file[PATH_MAX];
  198.     char attic_file[PATH_MAX];
  199.     char *acp;
  200.  
  201.     /* check to see if mname is a directory or file */
  202.  
  203.     (void) sprintf (file, "%s/%s", CVSroot_directory, mname);
  204.     if ((acp = strrchr (mname, '/')) != NULL)
  205.     {
  206.         *acp = '\0';
  207.         (void) sprintf (attic_file, "%s/%s/%s/%s%s", CVSroot_directory,
  208.                 mname, CVSATTIC, acp + 1, RCSEXT);
  209.         *acp = '/';
  210.     }
  211.     else
  212.         (void) sprintf (attic_file, "%s/%s/%s%s", CVSroot_directory,
  213.                 CVSATTIC, mname, RCSEXT);
  214.  
  215.     if (isdir (file))
  216.     {
  217.         value = mname;
  218.         goto found;
  219.     }
  220.     else
  221.     {
  222.         (void) strcat (file, RCSEXT);
  223.         if (isfile (file) || isfile (attic_file))
  224.         {
  225.         /* if mname was a file, we have to split it into "dir file" */
  226.         if ((cp = strrchr (mname, '/')) != NULL && cp != mname)
  227.         {
  228.             char *slashp;
  229.  
  230.             /* put the ' ' in a copy so we don't mess up the original */
  231.             value = strcpy (xvalue, mname);
  232.             slashp = strrchr (value, '/');
  233.             *slashp = ' ';
  234.         }
  235.         else
  236.         {
  237.             /*
  238.              * the only '/' at the beginning or no '/' at all
  239.              * means the file we are interested in is in CVSROOT
  240.              * itself so the directory should be '.'
  241.              */
  242.             if (cp == mname)
  243.             {
  244.             /* drop the leading / if specified */
  245.             value = strcpy (xvalue, ". ");
  246.             (void) strcat (xvalue, mname + 1);
  247.             }
  248.             else
  249.             {
  250.             /* otherwise just copy it */
  251.             value = strcpy (xvalue, ". ");
  252.             (void) strcat (xvalue, mname);
  253.             }
  254.         }
  255.         goto found;
  256.         }
  257.     }
  258.     }
  259.  
  260.     /* look up everything to the first / as a module */
  261.     if (mname[0] != '/' && (cp = strchr (mname, '/')) != NULL)
  262.     {
  263.     /* Make the slash the new end of the string temporarily */
  264.     *cp = '\0';
  265.     key.dptr = mname;
  266.     key.dsize = strlen (key.dptr);
  267.  
  268.     /* do the lookup */
  269.     if (db != NULL)
  270.         val = dbm_fetch (db, key);
  271.     else
  272.         val.dptr = NULL;
  273.  
  274.     /* if we found it, clean up the value and life is good */
  275.     if (val.dptr != NULL)
  276.     {
  277.         char *cp2;
  278.  
  279.         /* null terminate the value XXX - is this space ours? */
  280.         val.dptr[val.dsize] = '\0';
  281.  
  282.         /* If the line ends in a comment, strip it off */
  283.         if ((cp2 = strchr (val.dptr, '#')) != NULL)
  284.         {
  285.         do
  286.             *cp2-- = '\0';
  287.         while (isspace (*cp2));
  288.         }
  289.         value = val.dptr;
  290.  
  291.         /* mwhere gets just the module name */
  292.         mwhere = xstrdup (mname);
  293.         mfile = cp + 1;
  294.  
  295.         /* put the / back in mname */
  296.         *cp = '/';
  297.  
  298.         goto found;
  299.     }
  300.  
  301.     /* put the / back in mname */
  302.     *cp = '/';
  303.     }
  304.  
  305.     /* if we got here, we couldn't find it using our search, so give up */
  306.     error (0, 0, "cannot find module `%s' - ignored", mname);
  307.     err++;
  308.     if (mwhere)
  309.     free (mwhere);
  310.     return (err);
  311.  
  312.  
  313.     /*
  314.      * At this point, we found what we were looking for in one
  315.      * of the many different forms.
  316.      */
  317.   found:
  318.  
  319.     /* remember where we start */
  320.     if (save_cwd (&cwd))
  321.     exit (EXIT_FAILURE);
  322.  
  323.     /* copy value to our own string since if we go recursive we'll be
  324.        really screwed if we do another dbm lookup */
  325.     zvalue = xstrdup (value);
  326.     value = zvalue;
  327.  
  328.     /* search the value for the special delimiter and save for later */
  329.     if ((cp = strchr (value, CVSMODULE_SPEC)) != NULL)
  330.     {
  331.     *cp = '\0';            /* null out the special char */
  332.     spec_opt = cp + 1;        /* save the options for later */
  333.  
  334.     if (cp != value)        /* strip whitespace if necessary */
  335.         while (isspace (*--cp))
  336.         *cp = '\0';
  337.  
  338.     if (cp == value)
  339.     {
  340.         /*
  341.          * we had nothing but special options, so skip arg
  342.          * parsing and regular stuff entirely
  343.          *
  344.          * If there were only special ones though, we must
  345.          * make the appropriate directory and cd to it
  346.          */
  347.         char *dir;
  348.  
  349.         /* XXX - XXX - MAJOR HACK - DO NOT SHIP - this needs to
  350.            be !pipeout, but we don't know that here yet */
  351.         if (!run_module_prog)
  352.         goto out;
  353.  
  354.         dir = where ? where : mname;
  355.         /* XXX - think about making null repositories at each dir here
  356.              instead of just at the bottom */
  357.         make_directories (dir);
  358.         if ( CVS_CHDIR (dir) < 0)
  359.         {
  360.         error (0, errno, "cannot chdir to %s", dir);
  361.         spec_opt = NULL;
  362.         err++;
  363.         goto out;
  364.         }
  365.         if (!isfile (CVSADM))
  366.         {
  367.         char nullrepos[PATH_MAX];
  368.  
  369.         (void) sprintf (nullrepos, "%s/%s/%s", CVSroot_directory,
  370.                 CVSROOTADM, CVSNULLREPOS);
  371.         if (!isfile (nullrepos))
  372.         {
  373.             mode_t omask;
  374.             omask = umask (cvsumask);
  375.             (void) CVS_MKDIR (nullrepos, 0777);
  376.             (void) umask (omask);
  377.         }
  378.         if (!isdir (nullrepos))
  379.             error (1, 0, "there is no repository %s", nullrepos);
  380.  
  381.         Create_Admin (".", dir,
  382.                   nullrepos, (char *) NULL, (char *) NULL);
  383.         if (!noexec)
  384.         {
  385.             FILE *fp;
  386.  
  387.             fp = open_file (CVSADM_ENTSTAT, "w+");
  388.             if (fclose (fp) == EOF)
  389.             error (1, errno, "cannot close %s", CVSADM_ENTSTAT);
  390. #ifdef SERVER_SUPPORT
  391.             if (server_active)
  392.             server_set_entstat (dir, nullrepos);
  393. #endif
  394.         }
  395.         }
  396.       out:
  397.         goto do_special;
  398.     }
  399.     }
  400.  
  401.     /* don't do special options only part of a module was specified */
  402.     if (mfile != NULL)
  403.     spec_opt = NULL;
  404.  
  405.     /*
  406.      * value now contains one of the following:
  407.      *    1) dir
  408.      *      2) dir file
  409.      *    3) the value from modules without any special args
  410.      *            [ args ] dir [file] [file] ...
  411.      *         or     -a module [ module ] ...
  412.      */
  413.  
  414.     /* Put the value on a line with XXX prepended for getopt to eat */
  415.     line = xmalloc (strlen (value) + 10);
  416.     (void) sprintf (line, "%s %s", "XXX", value);
  417.  
  418.     /* turn the line into an argv[] array */
  419.     line2argv (&xmodargc, xmodargv, line);
  420.     free (line);
  421.     modargc = xmodargc;
  422.     modargv = xmodargv;
  423.  
  424.     /* parse the args */
  425.     optind = 1;
  426.     while ((c = getopt (modargc, modargv, CVSMODULE_OPTS)) != -1)
  427.     {
  428.     switch (c)
  429.     {
  430.         case 'a':
  431.         alias = 1;
  432.         break;
  433.         case 'd':
  434.         if (mwhere)
  435.             free (mwhere);
  436.         mwhere = xstrdup (optarg);
  437.         break;
  438.         case 'i':
  439.         checkin_prog = optarg;
  440.         break;
  441.         case 'l':
  442.         local_specified = 1;
  443.         break;
  444.         case 'o':
  445.         checkout_prog = optarg;
  446.         break;
  447.         case 'e':
  448.         export_prog = optarg;
  449.         break;
  450.         case 't':
  451.         tag_prog = optarg;
  452.         break;
  453.         case 'u':
  454.         update_prog = optarg;
  455.         break;
  456.         case '?':
  457.         error (0, 0,
  458.                "modules file has invalid option for key %s value %s",
  459.                key.dptr, val.dptr);
  460.         err++;
  461.         if (mwhere)
  462.             free (mwhere);
  463.         free (zvalue);
  464.         free_cwd (&cwd);
  465.         return (err);
  466.     }
  467.     }
  468.     modargc -= optind;
  469.     modargv += optind;
  470.     if (modargc == 0)
  471.     {
  472.     error (0, 0, "modules file missing directory for module %s", mname);
  473.     if (mwhere)
  474.         free (mwhere);
  475.     free (zvalue);
  476.     free_cwd (&cwd);
  477.     return (++err);
  478.     }
  479.  
  480.     /* if this was an alias, call ourselves recursively for each module */
  481.     if (alias)
  482.     {
  483.     int i;
  484.  
  485.     for (i = 0; i < modargc; i++)
  486.     {
  487.         if (strcmp (mname, modargv[i]) == 0)
  488.         error (0, 0,
  489.                "module `%s' in modules file contains infinite loop",
  490.                mname);
  491.         else
  492.         err += do_module (db, modargv[i], m_type, msg, callback_proc,
  493.                   where, shorten, local_specified,
  494.                   run_module_prog, extra_arg);
  495.     }
  496.     if (mwhere)
  497.         free (mwhere);
  498.     free (zvalue);
  499.     free_cwd (&cwd);
  500.     return (err);
  501.     }
  502.  
  503.     /* otherwise, process this module */
  504.     err += callback_proc (&modargc, modargv, where, mwhere, mfile, shorten,
  505.               local_specified, mname, msg);
  506.  
  507. #if 0
  508.     /* FIXME: I've fixed this so that the correct arguments are called, 
  509.        but now this fails because there is code below this point that
  510.        uses optarg values extracted from the arg vector. */
  511.     free_names (&xmodargc, xmodargv);
  512. #endif
  513.  
  514.     /* if there were special include args, process them now */
  515.  
  516.   do_special:
  517.  
  518.     /* blow off special options if -l was specified */
  519.     if (local_specified)
  520.     spec_opt = NULL;
  521.  
  522.     while (spec_opt != NULL)
  523.     {
  524.     char *next_opt;
  525.  
  526.     cp = strchr (spec_opt, CVSMODULE_SPEC);
  527.     if (cp != NULL)
  528.     {
  529.         /* save the beginning of the next arg */
  530.         next_opt = cp + 1;
  531.  
  532.         /* strip whitespace off the end */
  533.         do
  534.         *cp = '\0';
  535.         while (isspace (*--cp));
  536.     }
  537.     else
  538.         next_opt = NULL;
  539.  
  540.     /* strip whitespace from front */
  541.     while (isspace (*spec_opt))
  542.         spec_opt++;
  543.  
  544.     if (*spec_opt == '\0')
  545.         error (0, 0, "Mal-formed %c option for module %s - ignored",
  546.            CVSMODULE_SPEC, mname);
  547.     else
  548.         err += do_module (db, spec_opt, m_type, msg, callback_proc,
  549.                   (char *) NULL, 0, local_specified,
  550.                   run_module_prog, extra_arg);
  551.     spec_opt = next_opt;
  552.     }
  553.  
  554.     /* write out the checkin/update prog files if necessary */
  555. #ifdef SERVER_SUPPORT
  556.     if (err == 0 && !noexec && m_type == CHECKOUT && server_expanding)
  557.     {
  558.     if (checkin_prog != NULL)
  559.         server_prog (where ? where : mname, checkin_prog, PROG_CHECKIN);
  560.     if (update_prog != NULL)
  561.         server_prog (where ? where : mname, update_prog, PROG_UPDATE);
  562.     }
  563.     else
  564. #endif
  565.     if (err == 0 && !noexec && m_type == CHECKOUT && run_module_prog)
  566.     {
  567.     FILE *fp;
  568.  
  569.     if (checkin_prog != NULL)
  570.     {
  571.         fp = open_file (CVSADM_CIPROG, "w+");
  572.         (void) fprintf (fp, "%s\n", checkin_prog);
  573.         if (fclose (fp) == EOF)
  574.         error (1, errno, "cannot close %s", CVSADM_CIPROG);
  575.     }
  576.     if (update_prog != NULL)
  577.     {
  578.         fp = open_file (CVSADM_UPROG, "w+");
  579.         (void) fprintf (fp, "%s\n", update_prog);
  580.         if (fclose (fp) == EOF)
  581.         error (1, errno, "cannot close %s", CVSADM_UPROG);
  582.     }
  583.     }
  584.  
  585.     /* cd back to where we started */
  586.     if (restore_cwd (&cwd, NULL))
  587.     exit (EXIT_FAILURE);
  588.     free_cwd (&cwd);
  589.  
  590.     /* run checkout or tag prog if appropriate */
  591.     if (err == 0 && run_module_prog)
  592.     {
  593.     if ((m_type == TAG && tag_prog != NULL) ||
  594.         (m_type == CHECKOUT && checkout_prog != NULL) ||
  595.         (m_type == EXPORT && export_prog != NULL))
  596.     {
  597.         /*
  598.          * If a relative pathname is specified as the checkout, tag
  599.          * or export proc, try to tack on the current "where" value.
  600.          * if we can't find a matching program, just punt and use
  601.          * whatever is specified in the modules file.
  602.          */
  603.         char real_prog[PATH_MAX];
  604.         char *prog = (m_type == TAG ? tag_prog :
  605.               (m_type == CHECKOUT ? checkout_prog : export_prog));
  606.         char *real_where = (where != NULL ? where : mwhere);
  607.         char *expanded_path;
  608.  
  609.         if ((*prog != '/') && (*prog != '.'))
  610.         {
  611.         (void) sprintf (real_prog, "%s/%s", real_where, prog);
  612.         if (isfile (real_prog))
  613.             prog = real_prog;
  614.         }
  615.  
  616.         /* XXX can we determine the line number for this entry??? */
  617.         expanded_path = expand_path (prog, "modules", 0);
  618.         if (expanded_path != NULL)
  619.         {
  620.         run_setup ("%s %s", expanded_path, real_where);
  621.  
  622.         if (extra_arg)
  623.             run_arg (extra_arg);
  624.  
  625.         if (!quiet)
  626.         {
  627.             (void) printf ("%s %s: Executing '", program_name,
  628.                    command_name);
  629.             run_print (stdout);
  630.             (void) printf ("'\n");
  631.         }
  632.         err += run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
  633.         free (expanded_path);
  634.         }
  635.     }
  636.     }
  637.  
  638.     /* clean up */
  639.     if (mwhere)
  640.     free (mwhere);
  641.     free (zvalue);
  642.  
  643.     return (err);
  644. }
  645.  
  646. /* - Read all the records from the modules database into an array.
  647.    - Sort the array depending on what format is desired.
  648.    - Print the array in the format desired.
  649.  
  650.    Currently, there are only two "desires":
  651.  
  652.    1. Sort by module name and format the whole entry including switches,
  653.       files and the comment field: (Including aliases)
  654.  
  655.       modulename    -s switches, one per line, even if
  656.             -i it has many switches.
  657.             Directories and files involved, formatted
  658.             to cover multiple lines if necessary.
  659.             # Comment, also formatted to cover multiple
  660.             # lines if necessary.
  661.  
  662.    2. Sort by status field string and print:  (*not* including aliases)
  663.  
  664.       modulename    STATUS    Directories and files involved, formatted
  665.                 to cover multiple lines if necessary.
  666.                 # Comment, also formatted to cover multiple
  667.                 # lines if necessary.
  668. */
  669.  
  670. static struct sortrec *s_head;
  671.  
  672. static int s_max = 0;            /* Number of elements allocated */
  673. static int s_count = 0;            /* Number of elements used */
  674.  
  675. static int Status;                /* Nonzero if the user is
  676.                        interested in status
  677.                        information as well as
  678.                        module name */
  679. static char def_status[] = "NONE";
  680.  
  681. /* Sort routine for qsort:
  682.    - If we want the "Status" field to be sorted, check it first.
  683.    - Then compare the "module name" fields.  Since they are unique, we don't
  684.      have to look further.
  685. */
  686. static int
  687. sort_order (l, r)
  688.     const PTR l;
  689.     const PTR r;
  690. {
  691.     int i;
  692.     const struct sortrec *left = (const struct sortrec *) l;
  693.     const struct sortrec *right = (const struct sortrec *) r;
  694.  
  695.     if (Status)
  696.     {
  697.     /* If Sort by status field, compare them. */
  698.     if ((i = strcmp (left->status, right->status)) != 0)
  699.         return (i);
  700.     }
  701.     return (strcmp (left->modname, right->modname));
  702. }
  703.  
  704. static void
  705. save_d (k, ks, d, ds)
  706.     char *k;
  707.     int ks;
  708.     char *d;
  709.     int ds;
  710. {
  711.     char *cp, *cp2;
  712.     struct sortrec *s_rec;
  713.  
  714.     if (Status && *d == '-' && *(d + 1) == 'a')
  715.     return;                /* We want "cvs co -s" and it is an alias! */
  716.  
  717.     if (s_count == s_max)
  718.     {
  719.     s_max += 64;
  720.     s_head = (struct sortrec *) xrealloc ((char *) s_head, s_max * sizeof (*s_head));
  721.     }
  722.     s_rec = &s_head[s_count];
  723.     s_rec->modname = cp = xmalloc (ks + 1);
  724.     (void) strncpy (cp, k, ks);
  725.     *(cp + ks) = '\0';
  726.  
  727.     s_rec->rest = cp2 = xmalloc (ds + 1);
  728.     cp = d;
  729.     *(cp + ds) = '\0';    /* Assumes an extra byte at end of static dbm buffer */
  730.  
  731.     while (isspace (*cp))
  732.     cp++;
  733.     /* Turn <spaces> into one ' ' -- makes the rest of this routine simpler */
  734.     while (*cp)
  735.     {
  736.     if (isspace (*cp))
  737.     {
  738.         *cp2++ = ' ';
  739.         while (isspace (*cp))
  740.         cp++;
  741.     }
  742.     else
  743.         *cp2++ = *cp++;
  744.     }
  745.     *cp2 = '\0';
  746.  
  747.     /* Look for the "-s statusvalue" text */
  748.     if (Status)
  749.     {
  750.     s_rec->status = def_status;
  751.  
  752.     /* Minor kluge, but general enough to maintain */
  753.     for (cp = s_rec->rest; (cp2 = strchr (cp, '-')) != NULL; cp = ++cp2)
  754.     {
  755.         if (*(cp2 + 1) == 's' && *(cp2 + 2) == ' ')
  756.         {
  757.         s_rec->status = (cp2 += 3);
  758.         while (*cp2 != ' ')
  759.             cp2++;
  760.         *cp2++ = '\0';
  761.         cp = cp2;
  762.         break;
  763.         }
  764.     }
  765.     }
  766.     else
  767.     cp = s_rec->rest;
  768.  
  769.     /* Find comment field, clean up on all three sides & compress blanks */
  770.     if ((cp2 = cp = strchr (cp, '#')) != NULL)
  771.     {
  772.     if (*--cp2 == ' ')
  773.         *cp2 = '\0';
  774.     if (*++cp == ' ')
  775.         cp++;
  776.     s_rec->comment = cp;
  777.     }
  778.     else
  779.     s_rec->comment = "";
  780.  
  781.     s_count++;
  782. }
  783.  
  784. /* Print out the module database as we know it.  If STATUS is
  785.    non-zero, print out status information for each module. */
  786.  
  787. void
  788. cat_module (status)
  789.     int status;
  790. {
  791.     DBM *db;
  792.     datum key, val;
  793.     int i, c, wid, argc, cols = 80, indent, fill;
  794.     int moduleargc;
  795.     struct sortrec *s_h;
  796.     char *cp, *cp2, **argv;
  797.     char *line;
  798.     char *moduleargv[MAXFILEPERDIR];
  799.  
  800.     Status = status;
  801.  
  802.     /* Read the whole modules file into allocated records */
  803.     if (!(db = open_module ()))
  804.     error (1, 0, "failed to open the modules file");
  805.  
  806.     for (key = dbm_firstkey (db); key.dptr != NULL; key = dbm_nextkey (db))
  807.     {
  808.     val = dbm_fetch (db, key);
  809.     if (val.dptr != NULL)
  810.         save_d (key.dptr, key.dsize, val.dptr, val.dsize);
  811.     }
  812.  
  813.     /* Sort the list as requested */
  814.     qsort ((PTR) s_head, s_count, sizeof (struct sortrec), sort_order);
  815.  
  816.     /*
  817.      * Run through the sorted array and format the entries
  818.      * indent = space for modulename + space for status field
  819.      */
  820.     indent = 12 + (status * 12);
  821.     fill = cols - (indent + 2);
  822.     for (s_h = s_head, i = 0; i < s_count; i++, s_h++)
  823.     {
  824.     /* Print module name (and status, if wanted) */
  825.     (void) printf ("%-12s", s_h->modname);
  826.     if (status)
  827.     {
  828.         (void) printf (" %-11s", s_h->status);
  829.         if (s_h->status != def_status)
  830.         *(s_h->status + strlen (s_h->status)) = ' ';
  831.     }
  832.  
  833.     /* Parse module file entry as command line and print options */
  834.     line = xmalloc (strlen (s_h->modname) + strlen (s_h->rest) + 10);
  835.     (void) sprintf (line, "%s %s", s_h->modname, s_h->rest);
  836.     line2argv (&moduleargc, moduleargv, line);
  837.     free (line);
  838.     argc = moduleargc;
  839.     argv = moduleargv;
  840.  
  841.     optind = 0;
  842.     wid = 0;
  843.     while ((c = getopt (argc, argv, CVSMODULE_OPTS)) != -1)
  844.     {
  845.         if (!status)
  846.         {
  847.         if (c == 'a' || c == 'l')
  848.         {
  849.             (void) printf (" -%c", c);
  850.             wid += 3;        /* Could just set it to 3 */
  851.         }
  852.         else
  853.         {
  854.             if (strlen (optarg) + 4 + wid > (unsigned) fill)
  855.             {
  856.             (void) printf ("\n%*s", indent, "");
  857.             wid = 0;
  858.             }
  859.             (void) printf (" -%c %s", c, optarg);
  860.             wid += strlen (optarg) + 4;
  861.         }
  862.         }
  863.     }
  864.     argc -= optind;
  865.     argv += optind;
  866.  
  867.     /* Format and Print all the files and directories */
  868.     for (; argc--; argv++)
  869.     {
  870.         if (strlen (*argv) + wid > (unsigned) fill)
  871.         {
  872.         (void) printf ("\n%*s", indent, "");
  873.         wid = 0;
  874.         }
  875.         (void) printf (" %s", *argv);
  876.         wid += strlen (*argv) + 1;
  877.     }
  878.     (void) printf ("\n");
  879.  
  880.     /* Format the comment field -- save_d (), compressed spaces */
  881.     for (cp2 = cp = s_h->comment; *cp; cp2 = cp)
  882.     {
  883.         (void) printf ("%*s # ", indent, "");
  884.         if (strlen (cp2) < (unsigned) (fill - 2))
  885.         {
  886.         (void) printf ("%s\n", cp2);
  887.         break;
  888.         }
  889.         cp += fill - 2;
  890.         while (*cp != ' ' && cp > cp2)
  891.         cp--;
  892.         if (cp == cp2)
  893.         {
  894.         (void) printf ("%s\n", cp2);
  895.         break;
  896.         }
  897.  
  898.         *cp++ = '\0';
  899.         (void) printf ("%s\n", cp2);
  900.     }
  901.  
  902.     free_names(&moduleargc, moduleargv);
  903.     }
  904. }
  905.