/contrib/cvs/src/modules.c
https://bitbucket.org/freebsd/freebsd-head/ · C · 1101 lines · 745 code · 124 blank · 232 comment · 196 complexity · fcd61dd82fda962cb54e95c59bd29c32 MD5 · raw file
- /*
- * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
- *
- * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
- * and others.
- *
- * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
- * Portions Copyright (C) 1989-1992, Brian Berliner
- *
- * You may distribute under the terms of the GNU General Public License
- * as specified in the README file that comes with the CVS source
- * distribution.
- *
- * Modules
- *
- * Functions for accessing the modules file.
- *
- * The modules file supports basically three formats of lines:
- * key [options] directory files... [ -x directory [files] ] ...
- * key [options] directory [ -x directory [files] ] ...
- * key -a aliases...
- *
- * The -a option allows an aliasing step in the parsing of the modules
- * file. The "aliases" listed on a line following the -a are
- * processed one-by-one, as if they were specified as arguments on the
- * command line.
- */
- #include <assert.h>
- #include "cvs.h"
- #include "savecwd.h"
- /* Defines related to the syntax of the modules file. */
- /* Options in modules file. Note that it is OK to use GNU getopt features;
- we already are arranging to make sure we are using the getopt distributed
- with CVS. */
- #define CVSMODULE_OPTS "+ad:lo:e:s:t:"
- /* Special delimiter. */
- #define CVSMODULE_SPEC '&'
- struct sortrec
- {
- /* Name of the module, malloc'd. */
- char *modname;
- /* If Status variable is set, this is either def_status or the malloc'd
- name of the status. If Status is not set, the field is left
- uninitialized. */
- char *status;
- /* Pointer to a malloc'd array which contains (1) the raw contents
- of the options and arguments, excluding comments, (2) a '\0',
- and (3) the storage for the "comment" field. */
- char *rest;
- char *comment;
- };
- static int sort_order PROTO((const PTR l, const PTR r));
- static void save_d PROTO((char *k, int ks, char *d, int ds));
- /*
- * Open the modules file, and die if the CVSROOT environment variable
- * was not set. If the modules file does not exist, that's fine, and
- * a warning message is displayed and a NULL is returned.
- */
- DBM *
- open_module ()
- {
- char *mfile;
- DBM *retval;
- if (current_parsed_root == NULL)
- {
- error (0, 0, "must set the CVSROOT environment variable");
- error (1, 0, "or specify the '-d' global option");
- }
- mfile = xmalloc (strlen (current_parsed_root->directory)
- + sizeof (CVSROOTADM)
- + sizeof (CVSROOTADM_MODULES) + 3);
- (void) sprintf (mfile, "%s/%s/%s", current_parsed_root->directory,
- CVSROOTADM, CVSROOTADM_MODULES);
- retval = dbm_open (mfile, O_RDONLY, 0666);
- free (mfile);
- return retval;
- }
- /*
- * Close the modules file, if the open succeeded, that is
- */
- void
- close_module (db)
- DBM *db;
- {
- if (db != NULL)
- dbm_close (db);
- }
- /*
- * This is the recursive function that processes a module name.
- * It calls back the passed routine for each directory of a module
- * It runs the post checkout or post tag proc from the modules file
- */
- static int
- my_module (db, mname, m_type, msg, callback_proc, where, shorten,
- local_specified, run_module_prog, build_dirs, extra_arg,
- stack)
- DBM *db;
- char *mname;
- enum mtype m_type;
- char *msg;
- CALLBACKPROC callback_proc;
- char *where;
- int shorten;
- int local_specified;
- int run_module_prog;
- int build_dirs;
- char *extra_arg;
- List *stack;
- {
- char *checkout_prog = NULL;
- char *export_prog = NULL;
- char *tag_prog = NULL;
- struct saved_cwd cwd;
- int cwd_saved = 0;
- char *line;
- int modargc;
- int xmodargc;
- char **modargv = NULL;
- char **xmodargv = NULL;
- /* Found entry from modules file, including options and such. */
- char *value = NULL;
- char *mwhere = NULL;
- char *mfile = NULL;
- char *spec_opt = NULL;
- int alias = 0;
- datum key, val;
- char *cp;
- int c, err = 0;
- int nonalias_opt = 0;
- #ifdef SERVER_SUPPORT
- int restore_server_dir = 0;
- char *server_dir_to_restore = NULL;
- if (trace)
- {
- char *buf;
- /* We use cvs_outerr, rather than fprintf to stderr, because
- this may be called by server code with error_use_protocol
- set. */
- buf = xmalloc (100
- + strlen (mname)
- + strlen (msg)
- + (where ? strlen (where) : 0)
- + (extra_arg ? strlen (extra_arg) : 0));
- sprintf (buf, "%s-> my_module (%s, %s, %s, %s)\n",
- CLIENT_SERVER_STR,
- mname, msg, where ? where : "",
- extra_arg ? extra_arg : "");
- cvs_outerr (buf, 0);
- free (buf);
- }
- #endif
- /* Don't process absolute directories. Anything else could be a security
- * problem. Before this check was put in place:
- *
- * $ cvs -d:fork:/cvsroot co /foo
- * cvs server: warning: cannot make directory CVS in /: Permission denied
- * cvs [server aborted]: cannot make directory /foo: Permission denied
- * $
- */
- if (isabsolute (mname))
- error (1, 0, "Absolute module reference invalid: `%s'", mname);
- /* Similarly for directories that attempt to step above the root of the
- * repository.
- */
- if (pathname_levels (mname) > 0)
- error (1, 0, "up-level in module reference (`..') invalid: `%s'.",
- mname);
- /* if this is a directory to ignore, add it to that list */
- if (mname[0] == '!' && mname[1] != '\0')
- {
- ign_dir_add (mname+1);
- goto do_module_return;
- }
- /* strip extra stuff from the module name */
- strip_trailing_slashes (mname);
- /*
- * Look up the module using the following scheme:
- * 1) look for mname as a module name
- * 2) look for mname as a directory
- * 3) look for mname as a file
- * 4) take mname up to the first slash and look it up as a module name
- * (this is for checking out only part of a module)
- */
- /* look it up as a module name */
- key.dptr = mname;
- key.dsize = strlen (key.dptr);
- if (db != NULL)
- val = dbm_fetch (db, key);
- else
- val.dptr = NULL;
- if (val.dptr != NULL)
- {
- /* copy and null terminate the value */
- value = xmalloc (val.dsize + 1);
- memcpy (value, val.dptr, val.dsize);
- value[val.dsize] = '\0';
- /* If the line ends in a comment, strip it off */
- if ((cp = strchr (value, '#')) != NULL)
- *cp = '\0';
- else
- cp = value + val.dsize;
- /* Always strip trailing spaces */
- while (cp > value && isspace ((unsigned char) *--cp))
- *cp = '\0';
- mwhere = xstrdup (mname);
- goto found;
- }
- else
- {
- char *file;
- char *attic_file;
- char *acp;
- int is_found = 0;
- /* check to see if mname is a directory or file */
- file = xmalloc (strlen (current_parsed_root->directory)
- + strlen (mname) + sizeof(RCSEXT) + 2);
- (void) sprintf (file, "%s/%s", current_parsed_root->directory, mname);
- attic_file = xmalloc (strlen (current_parsed_root->directory)
- + strlen (mname)
- + sizeof (CVSATTIC) + sizeof (RCSEXT) + 3);
- if ((acp = strrchr (mname, '/')) != NULL)
- {
- *acp = '\0';
- (void) sprintf (attic_file, "%s/%s/%s/%s%s", current_parsed_root->directory,
- mname, CVSATTIC, acp + 1, RCSEXT);
- *acp = '/';
- }
- else
- (void) sprintf (attic_file, "%s/%s/%s%s",
- current_parsed_root->directory,
- CVSATTIC, mname, RCSEXT);
- if (isdir (file))
- {
- modargv = xmalloc (sizeof (*modargv));
- modargv[0] = xstrdup (mname);
- modargc = 1;
- is_found = 1;
- }
- else
- {
- (void) strcat (file, RCSEXT);
- if (isfile (file) || isfile (attic_file))
- {
- /* if mname was a file, we have to split it into "dir file" */
- if ((cp = strrchr (mname, '/')) != NULL && cp != mname)
- {
- modargv = xmalloc (2 * sizeof (*modargv));
- modargv[0] = xmalloc (strlen (mname) + 2);
- strncpy (modargv[0], mname, cp - mname);
- modargv[0][cp - mname] = '\0';
- modargv[1] = xstrdup (cp + 1);
- modargc = 2;
- }
- else
- {
- /*
- * the only '/' at the beginning or no '/' at all
- * means the file we are interested in is in CVSROOT
- * itself so the directory should be '.'
- */
- if (cp == mname)
- {
- /* drop the leading / if specified */
- modargv = xmalloc (2 * sizeof (*modargv));
- modargv[0] = xstrdup (".");
- modargv[1] = xstrdup (mname + 1);
- modargc = 2;
- }
- else
- {
- /* otherwise just copy it */
- modargv = xmalloc (2 * sizeof (*modargv));
- modargv[0] = xstrdup (".");
- modargv[1] = xstrdup (mname);
- modargc = 2;
- }
- }
- is_found = 1;
- }
- }
- free (attic_file);
- free (file);
- if (is_found)
- {
- assert (value == NULL);
- /* OK, we have now set up modargv with the actual
- file/directory we want to work on. We duplicate a
- small amount of code here because the vast majority of
- the code after the "found" label does not pertain to
- the case where we found a file/directory rather than
- finding an entry in the modules file. */
- if (save_cwd (&cwd))
- error_exit ();
- cwd_saved = 1;
- err += callback_proc (modargc, modargv, where, mwhere, mfile,
- shorten,
- local_specified, mname, msg);
- free_names (&modargc, modargv);
- /* cd back to where we started. */
- if (restore_cwd (&cwd, NULL))
- error_exit ();
- free_cwd (&cwd);
- cwd_saved = 0;
- goto do_module_return;
- }
- }
- /* look up everything to the first / as a module */
- if (mname[0] != '/' && (cp = strchr (mname, '/')) != NULL)
- {
- /* Make the slash the new end of the string temporarily */
- *cp = '\0';
- key.dptr = mname;
- key.dsize = strlen (key.dptr);
- /* do the lookup */
- if (db != NULL)
- val = dbm_fetch (db, key);
- else
- val.dptr = NULL;
- /* if we found it, clean up the value and life is good */
- if (val.dptr != NULL)
- {
- char *cp2;
- /* copy and null terminate the value */
- value = xmalloc (val.dsize + 1);
- memcpy (value, val.dptr, val.dsize);
- value[val.dsize] = '\0';
- /* If the line ends in a comment, strip it off */
- if ((cp2 = strchr (value, '#')) != NULL)
- *cp2 = '\0';
- else
- cp2 = value + val.dsize;
- /* Always strip trailing spaces */
- while (cp2 > value && isspace ((unsigned char) *--cp2))
- *cp2 = '\0';
- /* mwhere gets just the module name */
- mwhere = xstrdup (mname);
- mfile = cp + 1;
- assert (strlen (mfile));
- /* put the / back in mname */
- *cp = '/';
- goto found;
- }
- /* put the / back in mname */
- *cp = '/';
- }
- /* if we got here, we couldn't find it using our search, so give up */
- error (0, 0, "cannot find module `%s' - ignored", mname);
- err++;
- goto do_module_return;
- /*
- * At this point, we found what we were looking for in one
- * of the many different forms.
- */
- found:
- /* remember where we start */
- if (save_cwd (&cwd))
- error_exit ();
- cwd_saved = 1;
- assert (value != NULL);
- /* search the value for the special delimiter and save for later */
- if ((cp = strchr (value, CVSMODULE_SPEC)) != NULL)
- {
- *cp = '\0'; /* null out the special char */
- spec_opt = cp + 1; /* save the options for later */
- /* strip whitespace if necessary */
- while (cp > value && isspace ((unsigned char) *--cp))
- *cp = '\0';
- }
- /* don't do special options only part of a module was specified */
- if (mfile != NULL)
- spec_opt = NULL;
- /*
- * value now contains one of the following:
- * 1) dir
- * 2) dir file
- * 3) the value from modules without any special args
- * [ args ] dir [file] [file] ...
- * or -a module [ module ] ...
- */
- /* Put the value on a line with XXX prepended for getopt to eat */
- line = xmalloc (strlen (value) + 5);
- strcpy(line, "XXX ");
- strcpy(line + 4, value);
- /* turn the line into an argv[] array */
- line2argv (&xmodargc, &xmodargv, line, " \t");
- free (line);
- modargc = xmodargc;
- modargv = xmodargv;
- /* parse the args */
- optind = 0;
- while ((c = getopt (modargc, modargv, CVSMODULE_OPTS)) != -1)
- {
- switch (c)
- {
- case 'a':
- alias = 1;
- break;
- case 'd':
- if (mwhere)
- free (mwhere);
- mwhere = xstrdup (optarg);
- nonalias_opt = 1;
- break;
- case 'l':
- local_specified = 1;
- nonalias_opt = 1;
- break;
- case 'o':
- if (checkout_prog)
- free (checkout_prog);
- checkout_prog = xstrdup (optarg);
- nonalias_opt = 1;
- break;
- case 'e':
- if (export_prog)
- free (export_prog);
- export_prog = xstrdup (optarg);
- nonalias_opt = 1;
- break;
- case 't':
- if (tag_prog)
- free (tag_prog);
- tag_prog = xstrdup (optarg);
- nonalias_opt = 1;
- break;
- case '?':
- error (0, 0,
- "modules file has invalid option for key %s value %s",
- key.dptr, value);
- err++;
- goto do_module_return;
- }
- }
- modargc -= optind;
- modargv += optind;
- if (modargc == 0 && spec_opt == NULL)
- {
- error (0, 0, "modules file missing directory for module %s", mname);
- ++err;
- goto do_module_return;
- }
- if (alias && nonalias_opt)
- {
- /* The documentation has never said it is legal to specify
- -a along with another option. And I believe that in the past
- CVS has ignored the options other than -a, more or less, in this
- situation. */
- error (0, 0, "\
- -a cannot be specified in the modules file along with other options");
- ++err;
- goto do_module_return;
- }
- /* if this was an alias, call ourselves recursively for each module */
- if (alias)
- {
- int i;
- for (i = 0; i < modargc; i++)
- {
- /*
- * Recursion check: if an alias module calls itself or a module
- * which causes the first to be called again, print an error
- * message and stop recursing.
- *
- * Algorithm:
- *
- * 1. Check that MNAME isn't in the stack.
- * 2. Push MNAME onto the stack.
- * 3. Call do_module().
- * 4. Pop MNAME from the stack.
- */
- if (stack && findnode (stack, mname))
- error (0, 0,
- "module `%s' in modules file contains infinite loop",
- mname);
- else
- {
- if (!stack) stack = getlist();
- push_string (stack, mname);
- err += my_module (db, modargv[i], m_type, msg, callback_proc,
- where, shorten, local_specified,
- run_module_prog, build_dirs, extra_arg,
- stack);
- pop_string (stack);
- if (isempty (stack)) dellist (&stack);
- }
- }
- goto do_module_return;
- }
- if (mfile != NULL && modargc > 1)
- {
- error (0, 0, "\
- module `%s' is a request for a file in a module which is not a directory",
- mname);
- ++err;
- goto do_module_return;
- }
- /* otherwise, process this module */
- if (modargc > 0)
- {
- err += callback_proc (modargc, modargv, where, mwhere, mfile, shorten,
- local_specified, mname, msg);
- }
- else
- {
- /*
- * we had nothing but special options, so we must
- * make the appropriate directory and cd to it
- */
- char *dir;
- if (!build_dirs)
- goto do_special;
- dir = where ? where : (mwhere ? mwhere : mname);
- /* XXX - think about making null repositories at each dir here
- instead of just at the bottom */
- make_directories (dir);
- if (CVS_CHDIR (dir) < 0)
- {
- error (0, errno, "cannot chdir to %s", dir);
- spec_opt = NULL;
- err++;
- goto do_special;
- }
- if (!isfile (CVSADM))
- {
- char *nullrepos;
- nullrepos = emptydir_name ();
- Create_Admin (".", dir,
- nullrepos, (char *) NULL, (char *) NULL, 0, 0, 1);
- if (!noexec)
- {
- FILE *fp;
- fp = open_file (CVSADM_ENTSTAT, "w+");
- if (fclose (fp) == EOF)
- error (1, errno, "cannot close %s", CVSADM_ENTSTAT);
- #ifdef SERVER_SUPPORT
- if (server_active)
- server_set_entstat (dir, nullrepos);
- #endif
- }
- free (nullrepos);
- }
- }
- /* if there were special include args, process them now */
- do_special:
- free_names (&xmodargc, xmodargv);
- xmodargv = NULL;
- /* blow off special options if -l was specified */
- if (local_specified)
- spec_opt = NULL;
- #ifdef SERVER_SUPPORT
- /* We want to check out into the directory named by the module.
- So we set a global variable which tells the server to glom that
- directory name onto the front. A cleaner approach would be some
- way of passing it down to the recursive call, through the
- callback_proc, to start_recursion, and then into the update_dir in
- the struct file_info. That way the "Updating foo" message could
- print the actual directory we are checking out into.
- For local CVS, this is handled by the chdir call above
- (directly or via the callback_proc). */
- if (server_active && spec_opt != NULL)
- {
- char *change_to;
- change_to = where ? where : (mwhere ? mwhere : mname);
- server_dir_to_restore = server_dir;
- restore_server_dir = 1;
- server_dir =
- xmalloc ((server_dir_to_restore != NULL
- ? strlen (server_dir_to_restore)
- : 0)
- + strlen (change_to)
- + 5);
- server_dir[0] = '\0';
- if (server_dir_to_restore != NULL)
- {
- strcat (server_dir, server_dir_to_restore);
- strcat (server_dir, "/");
- }
- strcat (server_dir, change_to);
- }
- #endif
- while (spec_opt != NULL)
- {
- char *next_opt;
- cp = strchr (spec_opt, CVSMODULE_SPEC);
- if (cp != NULL)
- {
- /* save the beginning of the next arg */
- next_opt = cp + 1;
- /* strip whitespace off the end */
- do
- *cp = '\0';
- while (cp > spec_opt && isspace ((unsigned char) *--cp));
- }
- else
- next_opt = NULL;
- /* strip whitespace from front */
- while (isspace ((unsigned char) *spec_opt))
- spec_opt++;
- if (*spec_opt == '\0')
- error (0, 0, "Mal-formed %c option for module %s - ignored",
- CVSMODULE_SPEC, mname);
- else
- err += my_module (db, spec_opt, m_type, msg, callback_proc,
- (char *) NULL, 0, local_specified,
- run_module_prog, build_dirs, extra_arg,
- stack);
- spec_opt = next_opt;
- }
- #ifdef SERVER_SUPPORT
- if (server_active && restore_server_dir)
- {
- free (server_dir);
- server_dir = server_dir_to_restore;
- }
- #endif
- /* cd back to where we started */
- if (restore_cwd (&cwd, NULL))
- error_exit ();
- free_cwd (&cwd);
- cwd_saved = 0;
- /* run checkout or tag prog if appropriate */
- if (err == 0 && run_module_prog)
- {
- if ((m_type == TAG && tag_prog != NULL) ||
- (m_type == CHECKOUT && checkout_prog != NULL) ||
- (m_type == EXPORT && export_prog != NULL))
- {
- /*
- * If a relative pathname is specified as the checkout, tag
- * or export proc, try to tack on the current "where" value.
- * if we can't find a matching program, just punt and use
- * whatever is specified in the modules file.
- */
- char *real_prog = NULL;
- char *prog = (m_type == TAG ? tag_prog :
- (m_type == CHECKOUT ? checkout_prog : export_prog));
- char *real_where = (where != NULL ? where : mwhere);
- char *expanded_path;
- if ((*prog != '/') && (*prog != '.'))
- {
- real_prog = xmalloc (strlen (real_where) + strlen (prog)
- + 10);
- (void) sprintf (real_prog, "%s/%s", real_where, prog);
- if (isfile (real_prog))
- prog = real_prog;
- }
- /* XXX can we determine the line number for this entry??? */
- expanded_path = expand_path (prog, "modules", 0);
- if (expanded_path != NULL)
- {
- run_setup (expanded_path);
- run_arg (real_where);
- if (extra_arg)
- run_arg (extra_arg);
- if (!quiet)
- {
- cvs_output (program_name, 0);
- cvs_output (" ", 1);
- cvs_output (cvs_cmd_name, 0);
- cvs_output (": Executing '", 0);
- run_print (stdout);
- cvs_output ("'\n", 0);
- cvs_flushout ();
- }
- err += run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
- free (expanded_path);
- }
- if (real_prog) free (real_prog);
- }
- }
- do_module_return:
- /* clean up */
- if (xmodargv != NULL)
- free_names (&xmodargc, xmodargv);
- if (mwhere)
- free (mwhere);
- if (checkout_prog)
- free (checkout_prog);
- if (export_prog)
- free (export_prog);
- if (tag_prog)
- free (tag_prog);
- if (cwd_saved)
- free_cwd (&cwd);
- if (value != NULL)
- free (value);
- return (err);
- }
- /* External face of do_module so that we can have an internal version which
- * accepts a stack argument to track alias recursion.
- */
- int
- do_module (db, mname, m_type, msg, callback_proc, where, shorten,
- local_specified, run_module_prog, build_dirs, extra_arg)
- DBM *db;
- char *mname;
- enum mtype m_type;
- char *msg;
- CALLBACKPROC callback_proc;
- char *where;
- int shorten;
- int local_specified;
- int run_module_prog;
- int build_dirs;
- char *extra_arg;
- {
- return my_module (db, mname, m_type, msg, callback_proc, where, shorten,
- local_specified, run_module_prog, build_dirs, extra_arg,
- NULL);
- }
- /* - Read all the records from the modules database into an array.
- - Sort the array depending on what format is desired.
- - Print the array in the format desired.
- Currently, there are only two "desires":
- 1. Sort by module name and format the whole entry including switches,
- files and the comment field: (Including aliases)
- modulename -s switches, one per line, even if
- it has many switches.
- Directories and files involved, formatted
- to cover multiple lines if necessary.
- # Comment, also formatted to cover multiple
- # lines if necessary.
- 2. Sort by status field string and print: (*not* including aliases)
- modulename STATUS Directories and files involved, formatted
- to cover multiple lines if necessary.
- # Comment, also formatted to cover multiple
- # lines if necessary.
- */
- static struct sortrec *s_head;
- static int s_max = 0; /* Number of elements allocated */
- static int s_count = 0; /* Number of elements used */
- static int Status; /* Nonzero if the user is
- interested in status
- information as well as
- module name */
- static char def_status[] = "NONE";
- /* Sort routine for qsort:
- - If we want the "Status" field to be sorted, check it first.
- - Then compare the "module name" fields. Since they are unique, we don't
- have to look further.
- */
- static int
- sort_order (l, r)
- const PTR l;
- const PTR r;
- {
- int i;
- const struct sortrec *left = (const struct sortrec *) l;
- const struct sortrec *right = (const struct sortrec *) r;
- if (Status)
- {
- /* If Sort by status field, compare them. */
- if ((i = strcmp (left->status, right->status)) != 0)
- return (i);
- }
- return (strcmp (left->modname, right->modname));
- }
- static void
- save_d (k, ks, d, ds)
- char *k;
- int ks;
- char *d;
- int ds;
- {
- char *cp, *cp2;
- struct sortrec *s_rec;
- if (Status && *d == '-' && *(d + 1) == 'a')
- return; /* We want "cvs co -s" and it is an alias! */
- if (s_count == s_max)
- {
- s_max += 64;
- s_head = (struct sortrec *) xrealloc ((char *) s_head, s_max * sizeof (*s_head));
- }
- s_rec = &s_head[s_count];
- s_rec->modname = cp = xmalloc (ks + 1);
- (void) strncpy (cp, k, ks);
- *(cp + ks) = '\0';
- s_rec->rest = cp2 = xmalloc (ds + 1);
- cp = d;
- *(cp + ds) = '\0'; /* Assumes an extra byte at end of static dbm buffer */
- while (isspace ((unsigned char) *cp))
- cp++;
- /* Turn <spaces> into one ' ' -- makes the rest of this routine simpler */
- while (*cp)
- {
- if (isspace ((unsigned char) *cp))
- {
- *cp2++ = ' ';
- while (isspace ((unsigned char) *cp))
- cp++;
- }
- else
- *cp2++ = *cp++;
- }
- *cp2 = '\0';
- /* Look for the "-s statusvalue" text */
- if (Status)
- {
- s_rec->status = def_status;
- for (cp = s_rec->rest; (cp2 = strchr (cp, '-')) != NULL; cp = ++cp2)
- {
- if (*(cp2 + 1) == 's' && *(cp2 + 2) == ' ')
- {
- char *status_start;
- cp2 += 3;
- status_start = cp2;
- while (*cp2 != ' ' && *cp2 != '\0')
- cp2++;
- s_rec->status = xmalloc (cp2 - status_start + 1);
- strncpy (s_rec->status, status_start, cp2 - status_start);
- s_rec->status[cp2 - status_start] = '\0';
- cp = cp2;
- break;
- }
- }
- }
- else
- cp = s_rec->rest;
- /* Find comment field, clean up on all three sides & compress blanks */
- if ((cp2 = cp = strchr (cp, '#')) != NULL)
- {
- if (*--cp2 == ' ')
- *cp2 = '\0';
- if (*++cp == ' ')
- cp++;
- s_rec->comment = cp;
- }
- else
- s_rec->comment = "";
- s_count++;
- }
- /* Print out the module database as we know it. If STATUS is
- non-zero, print out status information for each module. */
- void
- cat_module (status)
- int status;
- {
- DBM *db;
- datum key, val;
- int i, c, wid, argc, cols = 80, indent, fill;
- int moduleargc;
- struct sortrec *s_h;
- char *cp, *cp2, **argv;
- char **moduleargv;
- Status = status;
- /* Read the whole modules file into allocated records */
- if (!(db = open_module ()))
- error (1, 0, "failed to open the modules file");
- for (key = dbm_firstkey (db); key.dptr != NULL; key = dbm_nextkey (db))
- {
- val = dbm_fetch (db, key);
- if (val.dptr != NULL)
- save_d (key.dptr, key.dsize, val.dptr, val.dsize);
- }
- close_module (db);
- /* Sort the list as requested */
- qsort ((PTR) s_head, s_count, sizeof (struct sortrec), sort_order);
- /*
- * Run through the sorted array and format the entries
- * indent = space for modulename + space for status field
- */
- indent = 12 + (status * 12);
- fill = cols - (indent + 2);
- for (s_h = s_head, i = 0; i < s_count; i++, s_h++)
- {
- char *line;
- /* Print module name (and status, if wanted) */
- line = xmalloc (strlen (s_h->modname) + 15);
- sprintf (line, "%-12s", s_h->modname);
- cvs_output (line, 0);
- free (line);
- if (status)
- {
- line = xmalloc (strlen (s_h->status) + 15);
- sprintf (line, " %-11s", s_h->status);
- cvs_output (line, 0);
- free (line);
- }
- line = xmalloc (strlen (s_h->modname) + strlen (s_h->rest) + 15);
- /* Parse module file entry as command line and print options */
- (void) sprintf (line, "%s %s", s_h->modname, s_h->rest);
- line2argv (&moduleargc, &moduleargv, line, " \t");
- free (line);
- argc = moduleargc;
- argv = moduleargv;
- optind = 0;
- wid = 0;
- while ((c = getopt (argc, argv, CVSMODULE_OPTS)) != -1)
- {
- if (!status)
- {
- if (c == 'a' || c == 'l')
- {
- char buf[5];
- sprintf (buf, " -%c", c);
- cvs_output (buf, 0);
- wid += 3; /* Could just set it to 3 */
- }
- else
- {
- char buf[10];
- if (strlen (optarg) + 4 + wid > (unsigned) fill)
- {
- int j;
- cvs_output ("\n", 1);
- for (j = 0; j < indent; ++j)
- cvs_output (" ", 1);
- wid = 0;
- }
- sprintf (buf, " -%c ", c);
- cvs_output (buf, 0);
- cvs_output (optarg, 0);
- wid += strlen (optarg) + 4;
- }
- }
- }
- argc -= optind;
- argv += optind;
- /* Format and Print all the files and directories */
- for (; argc--; argv++)
- {
- if (strlen (*argv) + wid > (unsigned) fill)
- {
- int j;
- cvs_output ("\n", 1);
- for (j = 0; j < indent; ++j)
- cvs_output (" ", 1);
- wid = 0;
- }
- cvs_output (" ", 1);
- cvs_output (*argv, 0);
- wid += strlen (*argv) + 1;
- }
- cvs_output ("\n", 1);
- /* Format the comment field -- save_d (), compressed spaces */
- for (cp2 = cp = s_h->comment; *cp; cp2 = cp)
- {
- int j;
- for (j = 0; j < indent; ++j)
- cvs_output (" ", 1);
- cvs_output (" # ", 0);
- if (strlen (cp2) < (unsigned) (fill - 2))
- {
- cvs_output (cp2, 0);
- cvs_output ("\n", 1);
- break;
- }
- cp += fill - 2;
- while (*cp != ' ' && cp > cp2)
- cp--;
- if (cp == cp2)
- {
- cvs_output (cp2, 0);
- cvs_output ("\n", 1);
- break;
- }
- *cp++ = '\0';
- cvs_output (cp2, 0);
- cvs_output ("\n", 1);
- }
- free_names(&moduleargc, moduleargv);
- /* FIXME-leak: here is where we would free s_h->modname, s_h->rest,
- and if applicable, s_h->status. Not exactly a memory leak,
- in the sense that we are about to exit(), but may be worth
- noting if we ever do a multithreaded server or something of
- the sort. */
- }
- /* FIXME-leak: as above, here is where we would free s_head. */
- }