home *** CD-ROM | disk | FTP | other *** search
- /*
- * Copyright (c) 1992, Brian Berliner and Jeff Polk
- * 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 1.3 kit.
- *
- * 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 "cvs.h"
-
- #ifndef lint
- static char rcsid[] = "@(#)modules.c 1.57 92/04/10";
- #endif
-
- struct sortrec
- {
- char *modname;
- char *status;
- char *rest;
- char *comment;
- };
-
- #if __STDC__
- static int sort_order (CONST PTR l, CONST PTR r);
- static void save_d (char *k, int ks, char *d, int ds);
- #else
- static int sort_order ();
- static void save_d ();
- #endif /* __STDC__ */
-
-
- /*
- * 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[PATH_MAX];
-
- if (CVSroot == NULL)
- {
- (void) fprintf (stderr,
- "%s: must set the CVSROOT environment variable\n",
- program_name);
- error (1, 0, "or specify the '-d' option to %s", program_name);
- }
- (void) sprintf (mfile, "%s/%s/%s", CVSroot, CVSROOTADM, CVSROOTADM_MODULES);
- return (dbm_open (mfile, O_RDONLY, 0666));
- }
-
- /*
- * 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
- */
- int
- do_module (db, mname, m_type, msg, callback_proc, where,
- shorten, local_specified, run_module_prog, extra_arg)
- DBM *db;
- char *mname;
- enum mtype m_type;
- char *msg;
- int (*callback_proc) ();
- char *where;
- int shorten;
- int local_specified;
- int run_module_prog;
- char *extra_arg;
- {
- char *checkin_prog = NULL;
- char *checkout_prog = NULL;
- char *tag_prog = NULL;
- char *update_prog = NULL;
- char cwd[PATH_MAX];
- char line[MAXLINELEN];
- char *xmodargv[MAXFILEPERDIR];
- char **modargv;
- char *value;
- char *zvalue;
- char *mwhere = NULL;
- char *mfile = NULL;
- char *spec_opt = NULL;
- char xvalue[PATH_MAX];
- int modargc, alias = 0;
- datum key, val;
- char *cp;
- int c, err = 0;
-
- /* remember where we start */
- if (getwd (cwd) == NULL)
- error (1, 0, "cannot get current working directory: %s", cwd);
-
- /* strip extra stuff from the module name */
- strip_path (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)
- {
- /* null terminate the value XXX - is this space ours? */
- val.dptr[val.dsize] = '\0';
-
- /* If the line ends in a comment, strip it off */
- if ((cp = index (val.dptr, '#')) != NULL)
- {
- do
- *cp-- = '\0';
- while (isspace (*cp));
- }
- value = val.dptr;
- mwhere = xstrdup (mname);
- goto found;
- }
- else
- {
- char file[PATH_MAX];
- char attic_file[PATH_MAX];
- char *acp;
-
- /* check to see if mname is a directory or file */
-
- (void) sprintf (file, "%s/%s", CVSroot, mname);
- if ((acp = rindex (mname, '/')) != NULL)
- {
- *acp = '\0';
- (void) sprintf (attic_file, "%s/%s/%s/%s%s", CVSroot, mname,
- CVSATTIC, acp + 1, RCSEXT);
- *acp = '/';
- }
- else
- (void) sprintf (attic_file, "%s/%s/%s%s", CVSroot, CVSATTIC,
- mname, RCSEXT);
-
- if (isdir (file))
- {
- value = mname;
- goto found;
- }
- 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 = rindex (mname, '/')) != NULL && cp != mname)
- {
- char *slashp;
-
- /* put the ' ' in a copy so we don't mess up the original */
- value = strcpy (xvalue, mname);
- slashp = rindex (value, '/');
- *slashp = ' ';
- }
- 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 */
- value = strcpy (xvalue, ". ");
- (void) strcat (xvalue, mname + 1);
- }
- else
- {
- /* otherwise just copy it */
- value = strcpy (xvalue, ". ");
- (void) strcat (xvalue, mname);
- }
- }
- goto found;
- }
- }
- }
-
- /* look up everything to the first / as a module */
- if (mname[0] != '/' && (cp = index (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;
-
- /* null terminate the value XXX - is this space ours? */
- val.dptr[val.dsize] = '\0';
-
- /* If the line ends in a comment, strip it off */
- if ((cp2 = index (val.dptr, '#')) != NULL)
- {
- do
- *cp2-- = '\0';
- while (isspace (*cp2));
- }
- value = val.dptr;
-
- /* mwhere gets just the module name */
- mwhere = xstrdup (mname);
- mfile = cp + 1;
-
- /* 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++;
- if (mwhere)
- free (mwhere);
- return (err);
-
-
- /*
- * At this point, we found what we were looking for in one
- * of the many different forms.
- */
- found:
-
- /* copy value to our own string since if we go recursive we'll be
- really screwed if we do another dbm lookup */
- zvalue = xstrdup (value);
- value = zvalue;
-
- /* search the value for the special delimiter and save for later */
- if ((cp = index (value, CVSMODULE_SPEC)) != NULL)
- {
- *cp = '\0'; /* null out the special char */
- spec_opt = cp + 1; /* save the options for later */
-
- if (cp != value) /* strip whitespace if necessary */
- while (isspace (*--cp))
- *cp = '\0';
-
- if (cp == value)
- {
- /*
- * we had nothing but special options, so skip arg
- * parsing and regular stuff entirely
- *
- * If there were only special ones though, we must
- * make the appropriate directory and cd to it
- */
- char *dir;
-
- /* XXX - XXX - MAJOR HACK - DO NOT SHIP - this needs to
- be !pipeout, but we don't know that here yet */
- if (!run_module_prog)
- goto out;
-
- dir = where ? where : mname;
- /* XXX - think about making null repositories at each dir here
- instead of just at the bottom */
- make_directories (dir);
- if (chdir (dir) < 0)
- {
- error (0, errno, "cannot chdir to %s", dir);
- spec_opt = NULL;
- err++;
- goto out;
- }
- if (!isfile (CVSADM) && !isfile (OCVSADM))
- {
- char nullrepos[PATH_MAX];
-
- (void) sprintf (nullrepos, "%s/%s/%s", CVSroot,
- CVSROOTADM, CVSNULLREPOS);
- if (!isfile (nullrepos))
- (void) mkdir (nullrepos, 0777);
- Create_Admin (".", nullrepos, (char *) NULL, (char *) NULL);
- if (!noexec)
- {
- FILE *fp;
-
- fp = open_file (CVSADM_ENTSTAT, "w+");
- if (fclose (fp) == EOF)
- error (1, errno, "cannot close %s", CVSADM_ENTSTAT);
- }
- }
- out:
- goto do_special;
- }
- }
-
- /* 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 */
- (void) sprintf (line, "%s %s", "XXX", value);
-
- /* turn the line into an argv[] array */
- line2argv (&modargc, xmodargv, line);
- modargv = xmodargv;
-
- /* parse the args */
- optind = 1;
- while ((c = gnu_getopt (modargc, modargv, CVSMODULE_OPTS)) != -1)
- {
- switch (c)
- {
- case 'a':
- alias = 1;
- break;
- case 'd':
- if (mwhere)
- free (mwhere);
- mwhere = xstrdup (optarg);
- break;
- case 'i':
- checkin_prog = optarg;
- break;
- case 'l':
- local_specified = 1;
- case 'o':
- checkout_prog = optarg;
- break;
- case 't':
- tag_prog = optarg;
- break;
- case 'u':
- update_prog = optarg;
- break;
- case '?':
- error (0, 0,
- "modules file has invalid option for key %s value %s",
- key.dptr, val.dptr);
- err++;
- if (mwhere)
- free (mwhere);
- free (zvalue);
- return (err);
- }
- }
- modargc -= optind;
- modargv += optind;
- if (modargc == 0)
- {
- error (0, 0, "modules file missing directory for module %s", mname);
- if (mwhere)
- free (mwhere);
- free (zvalue);
- return (++err);
- }
-
- /* if this was an alias, call ourselves recursively for each module */
- if (alias)
- {
- int i;
-
- for (i = 0; i < modargc; i++)
- err += do_module (db, modargv[i], m_type, msg, callback_proc,
- where, shorten, local_specified,
- run_module_prog, extra_arg);
- if (mwhere)
- free (mwhere);
- free (zvalue);
- return (err);
- }
-
- /* otherwise, process this module */
- err += callback_proc (&modargc, modargv, where, mwhere, mfile, shorten,
- local_specified, mname, msg);
-
- /* clean up */
- free_names (&modargc, modargv);
-
- /* if there were special include args, process them now */
-
- do_special:
-
- /* blow off special options if -l was specified */
- if (local_specified)
- spec_opt = NULL;
-
- while (spec_opt != NULL)
- {
- char *next_opt;
-
- cp = index (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 (isspace (*--cp));
- }
- else
- next_opt = NULL;
-
- /* strip whitespace from front */
- while (isspace (*spec_opt))
- spec_opt++;
-
- if (*spec_opt == '\0')
- error (0, 0, "Mal-formed %c option for module %s - ignored",
- CVSMODULE_SPEC, mname);
- else
- err += do_module (db, spec_opt, m_type, msg, callback_proc,
- (char *) NULL, 0, local_specified,
- run_module_prog, extra_arg);
- spec_opt = next_opt;
- }
-
- /* write out the checkin/update prog files if necessary */
- if (err == 0 && !noexec && m_type == CHECKOUT && run_module_prog)
- {
- FILE *fp;
-
- if (checkin_prog != NULL)
- {
- fp = open_file (CVSADM_CIPROG, "w+");
- (void) fprintf (fp, "%s\n", checkin_prog);
- if (fclose (fp) == EOF)
- error (1, errno, "cannot close %s", CVSADM_CIPROG);
- }
- if (update_prog != NULL)
- {
- fp = open_file (CVSADM_UPROG, "w+");
- (void) fprintf (fp, "%s\n", update_prog);
- if (fclose (fp) == EOF)
- error (1, errno, "cannot close %s", CVSADM_UPROG);
- }
- }
-
- /* cd back to where we started */
- if (chdir (cwd) < 0)
- error (1, errno, "failed chdir to %s!", cwd);
-
- /* 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))
- {
- /*
- * If a relative pathname is specified as the checkout or
- * tag 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[PATH_MAX];
- char *prog = (m_type == TAG ? tag_prog : checkout_prog);
- char *real_where = (where != NULL ? where : mwhere);
-
- if ((*prog != '/') && (*prog != '.'))
- {
- (void) sprintf (real_prog, "%s/%s", real_where, prog);
- if (isfile (real_prog))
- prog = real_prog;
- }
-
- run_setup ("%s %s", prog, real_where);
- if (extra_arg)
- run_arg (extra_arg);
-
- if (!quiet)
- {
- (void) printf ("%s %s: Executing '", program_name,
- command_name);
- run_print (stdout);
- (void) printf ("'\n");
- }
- err += run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
- }
- }
-
- /* clean up */
- if (mwhere)
- free (mwhere);
- free (zvalue);
-
- return (err);
- }
-
- /* - 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
- -i 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;
- 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 (*cp))
- cp++;
- /* Turn <spaces> into one ' ' -- makes the rest of this routine simpler */
- while (*cp)
- {
- if (isspace (*cp))
- {
- *cp2++ = ' ';
- while (isspace (*cp))
- cp++;
- }
- else
- *cp2++ = *cp++;
- }
- *cp2 = '\0';
-
- /* Look for the "-s statusvalue" text */
- if (Status)
- {
- s_rec->status = def_status;
-
- /* Minor kluge, but general enough to maintain */
- for (cp = s_rec->rest; (cp2 = index (cp, '-')) != NULL; cp = ++cp2)
- {
- if (*(cp2 + 1) == 's' && *(cp2 + 2) == ' ')
- {
- s_rec->status = (cp2 += 3);
- while (*cp2 != ' ')
- cp2++;
- *cp2++ = '\0';
- cp = cp2;
- break;
- }
- }
- }
- else
- cp = s_rec->rest;
-
- /* Find comment field, clean up on all three sides & compress blanks */
- if ((cp2 = cp = index (cp, '#')) != NULL)
- {
- if (*--cp2 == ' ')
- *cp2 = '\0';
- if (*++cp == ' ')
- cp++;
- s_rec->comment = cp;
- }
- else
- s_rec->comment = "";
-
- s_count++;
- }
-
- 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 line[MAXLINELEN], *moduleargv[MAXFILEPERDIR];
-
- #ifdef sun
- #ifdef TIOCGSIZE
- struct ttysize ts;
-
- (void) ioctl (0, TIOCGSIZE, &ts);
- cols = ts.ts_cols;
- #endif
- #else
- #ifdef TIOCGWINSZ
- struct winsize ws;
-
- (void) ioctl (0, TIOCGWINSZ, &ws);
- cols = ws.ws_col;
- #endif
- #endif
-
- 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);
- }
-
- /* 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++)
- {
- /* Print module name (and status, if wanted) */
- (void) printf ("%-12s", s_h->modname);
- if (status)
- {
- (void) printf (" %-11s", s_h->status);
- if (s_h->status != def_status)
- *(s_h->status + strlen (s_h->status)) = ' ';
- }
-
- /* 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);
- argc = moduleargc;
- argv = moduleargv;
-
- optind = 1;
- wid = 0;
- while ((c = gnu_getopt (argc, argv, CVSMODULE_OPTS)) != -1)
- {
- if (!status)
- {
- if (c == 'a')
- {
- (void) printf (" -a");
- wid += 3; /* Could just set it to 3 */
- }
- else
- {
- if (strlen (optarg) + 4 + wid > (unsigned) fill)
- {
- (void) printf ("\n%*s", indent, "");
- wid = 0;
- }
- (void) printf (" -%c %s", c, optarg);
- 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)
- {
- (void) printf ("\n%*s", indent, "");
- wid = 0;
- }
- (void) printf (" %s", *argv);
- wid += strlen (*argv) + 1;
- }
- (void) printf ("\n");
-
- /* Format the comment field -- save_d (), compressed spaces */
- for (cp2 = cp = s_h->comment; *cp; cp2 = cp)
- {
- (void) printf ("%*s # ", indent, "");
- if (strlen (cp2) < (unsigned) (fill - 2))
- {
- (void) printf ("%s\n", cp2);
- break;
- }
- cp += fill - 2;
- while (*cp != ' ' && cp > cp2)
- cp--;
- if (cp == cp2)
- {
- (void) printf ("%s\n", cp2);
- break;
- }
-
- *cp++ = '\0';
- (void) printf ("%s\n", cp2);
- }
- }
- }
-