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.4 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"
- #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:i:lo:e:s:t:u:"
-
- /* Special delimiter. */
- #define CVSMODULE_SPEC '&'
-
- struct sortrec
- {
- char *modname;
- char *status;
- 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[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;
- CALLBACKPROC 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 *export_prog = NULL;
- char *tag_prog = NULL;
- char *update_prog = NULL;
- struct saved_cwd cwd;
- char *line;
- int modargc;
- int xmodargc;
- char **modargv;
- char *xmodargv[MAXFILEPERDIR];
- char *value;
- char *zvalue;
- char *mwhere = NULL;
- char *mfile = NULL;
- char *spec_opt = NULL;
- char xvalue[PATH_MAX];
- int alias = 0;
- datum key, val;
- char *cp;
- int c, err = 0;
-
- #ifdef SERVER_SUPPORT
- if (trace)
- {
- fprintf (stderr, "%s%c-> do_module (%s, %s, %s, %s)\n",
- error_use_protocol ? "E " : "",
- (server_active) ? 'S' : ' ',
- mname, msg, where ? where : "",
- extra_arg ? extra_arg : "");
- }
- #endif
-
- /* if this is a directory to ignore, add it to that list */
- if (mname[0] == '!' && mname[1] != '\0')
- {
- ign_dir_add (mname+1);
- return(err);
- }
-
- /* 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 = strchr (val.dptr, '#')) != NULL)
- {
- do
- *cp-- = '\0';
- while (isspace (*cp));
- }
- else
- {
- /* Always strip trailing spaces */
- cp = strchr (val.dptr, '\0');
- while (cp > val.dptr && isspace(*--cp))
- *cp = '\0';
- }
-
- 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 = strrchr (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 = strrchr (mname, '/')) != NULL && cp != mname)
- {
- char *slashp;
-
- /* put the ' ' in a copy so we don't mess up the original */
- value = strcpy (xvalue, mname);
- slashp = strrchr (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 = 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;
-
- /* 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 = strchr (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:
-
- /* remember where we start */
- if (save_cwd (&cwd))
- exit (EXIT_FAILURE);
-
- /* 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 = strchr (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))
- {
- char nullrepos[PATH_MAX];
-
- (void) sprintf (nullrepos, "%s/%s/%s", CVSroot,
- CVSROOTADM, CVSNULLREPOS);
- if (!isfile (nullrepos))
- {
- mode_t omask;
- omask = umask (cvsumask);
- (void) CVS_MKDIR (nullrepos, 0777);
- (void) umask (omask);
- }
- if (!isdir (nullrepos))
- error (1, 0, "there is no repository %s", nullrepos);
-
- Create_Admin (".", dir,
- 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);
- #ifdef SERVER_SUPPORT
- if (server_active)
- server_set_entstat (dir, nullrepos);
- #endif
- }
- }
- 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 */
- line = xmalloc (strlen (value) + 10);
- (void) sprintf (line, "%s %s", "XXX", value);
-
- /* turn the line into an argv[] array */
- line2argv (&xmodargc, xmodargv, line);
- free (line);
- modargc = xmodargc;
- modargv = xmodargv;
-
- /* parse the args */
- optind = 1;
- while ((c = 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;
- break;
- case 'o':
- checkout_prog = optarg;
- break;
- case 'e':
- export_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);
- free_cwd (&cwd);
- 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);
- free_cwd (&cwd);
- return (++err);
- }
-
- /* if this was an alias, call ourselves recursively for each module */
- if (alias)
- {
- int i;
-
- for (i = 0; i < modargc; i++)
- {
- if (strcmp (mname, modargv[i]) == 0)
- error (0, 0,
- "module `%s' in modules file contains infinite loop",
- mname);
- else
- 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);
- free_cwd (&cwd);
- return (err);
- }
-
- /* otherwise, process this module */
- err += callback_proc (&modargc, modargv, where, mwhere, mfile, shorten,
- local_specified, mname, msg);
-
- #if 0
- /* FIXME: I've fixed this so that the correct arguments are called,
- but now this fails because there is code below this point that
- uses optarg values extracted from the arg vector. */
- free_names (&xmodargc, xmodargv);
- #endif
-
- /* 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 = 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 (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 */
- #ifdef SERVER_SUPPORT
- if (err == 0 && !noexec && m_type == CHECKOUT && server_expanding)
- {
- if (checkin_prog != NULL)
- server_prog (where ? where : mname, checkin_prog, PROG_CHECKIN);
- if (update_prog != NULL)
- server_prog (where ? where : mname, update_prog, PROG_UPDATE);
- }
- else
- #endif
- 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 (restore_cwd (&cwd, NULL))
- exit (EXIT_FAILURE);
- free_cwd (&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) ||
- (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[PATH_MAX];
- char *prog = (m_type == TAG ? tag_prog :
- (m_type == CHECKOUT ? checkout_prog : export_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; /* 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 (*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 = strchr (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 = 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 *line;
- char *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 */
- line = xmalloc (strlen (s_h->modname) + strlen (s_h->rest) + 10);
- (void) sprintf (line, "%s %s", s_h->modname, s_h->rest);
- line2argv (&moduleargc, moduleargv, line);
- 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')
- {
- (void) printf (" -%c", c);
- 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);
- }
-
- free_names(&moduleargc, moduleargv);
- }
- }
-