diff options
author | peter <peter@FreeBSD.org> | 1996-08-20 23:46:10 +0000 |
---|---|---|
committer | peter <peter@FreeBSD.org> | 1996-08-20 23:46:10 +0000 |
commit | 8982e501c77217c860f79bba431f46a62b607a21 (patch) | |
tree | 70187fdf5be4cbefd0baf46bddac7e5e32c13c24 /contrib/cvs/src/modules.c | |
parent | 01ee40fd6a76f6ff7ef247fc1b2cf6e337f216c5 (diff) | |
download | FreeBSD-src-8982e501c77217c860f79bba431f46a62b607a21.zip FreeBSD-src-8982e501c77217c860f79bba431f46a62b607a21.tar.gz |
Import of slightly trimmed cvs-1.8 distribution. Generated files
and non-unix code has been left out.
Diffstat (limited to 'contrib/cvs/src/modules.c')
-rw-r--r-- | contrib/cvs/src/modules.c | 900 |
1 files changed, 900 insertions, 0 deletions
diff --git a/contrib/cvs/src/modules.c b/contrib/cvs/src/modules.c new file mode 100644 index 0000000..0e07c0b --- /dev/null +++ b/contrib/cvs/src/modules.c @@ -0,0 +1,900 @@ +/* + * 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); + } +} |