117721Speter/* 2175261Sobrien * Copyright (C) 1986-2005 The Free Software Foundation, Inc. 317721Speter * 4175261Sobrien * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>, 5175261Sobrien * and others. 6175261Sobrien * 7175261Sobrien * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk 8175261Sobrien * Portions Copyright (C) 1989-1992, Brian Berliner 9175261Sobrien * 1017721Speter * You may distribute under the terms of the GNU General Public License 11128266Speter * as specified in the README file that comes with the CVS source 12128266Speter * distribution. 1317721Speter * 1417721Speter * Modules 1517721Speter * 1617721Speter * Functions for accessing the modules file. 1717721Speter * 1817721Speter * The modules file supports basically three formats of lines: 1917721Speter * key [options] directory files... [ -x directory [files] ] ... 2017721Speter * key [options] directory [ -x directory [files] ] ... 2117721Speter * key -a aliases... 2217721Speter * 2317721Speter * The -a option allows an aliasing step in the parsing of the modules 2417721Speter * file. The "aliases" listed on a line following the -a are 2517721Speter * processed one-by-one, as if they were specified as arguments on the 2617721Speter * command line. 2717721Speter */ 2817721Speter 2954427Speter#include <assert.h> 3017721Speter#include "cvs.h" 3117721Speter#include "savecwd.h" 3217721Speter 3317721Speter 3417721Speter/* Defines related to the syntax of the modules file. */ 3517721Speter 3617721Speter/* Options in modules file. Note that it is OK to use GNU getopt features; 3717721Speter we already are arranging to make sure we are using the getopt distributed 3817721Speter with CVS. */ 39128266Speter#define CVSMODULE_OPTS "+ad:lo:e:s:t:" 4017721Speter 4117721Speter/* Special delimiter. */ 4217721Speter#define CVSMODULE_SPEC '&' 4317721Speter 4417721Speterstruct sortrec 4517721Speter{ 4632785Speter /* Name of the module, malloc'd. */ 4717721Speter char *modname; 4832785Speter /* If Status variable is set, this is either def_status or the malloc'd 4932785Speter name of the status. If Status is not set, the field is left 5032785Speter uninitialized. */ 5117721Speter char *status; 5232785Speter /* Pointer to a malloc'd array which contains (1) the raw contents 5332785Speter of the options and arguments, excluding comments, (2) a '\0', 5432785Speter and (3) the storage for the "comment" field. */ 5517721Speter char *rest; 5617721Speter char *comment; 5717721Speter}; 5817721Speter 5917721Speterstatic int sort_order PROTO((const PTR l, const PTR r)); 6017721Speterstatic void save_d PROTO((char *k, int ks, char *d, int ds)); 6117721Speter 6217721Speter 6317721Speter/* 6417721Speter * Open the modules file, and die if the CVSROOT environment variable 6517721Speter * was not set. If the modules file does not exist, that's fine, and 6617721Speter * a warning message is displayed and a NULL is returned. 6717721Speter */ 6817721SpeterDBM * 6917721Speteropen_module () 7017721Speter{ 7125839Speter char *mfile; 7225839Speter DBM *retval; 7317721Speter 7481404Speter if (current_parsed_root == NULL) 7517721Speter { 7625839Speter error (0, 0, "must set the CVSROOT environment variable"); 7725839Speter error (1, 0, "or specify the '-d' global option"); 7817721Speter } 7981404Speter mfile = xmalloc (strlen (current_parsed_root->directory) 8081404Speter + sizeof (CVSROOTADM) 8181404Speter + sizeof (CVSROOTADM_MODULES) + 3); 8281404Speter (void) sprintf (mfile, "%s/%s/%s", current_parsed_root->directory, 8325839Speter CVSROOTADM, CVSROOTADM_MODULES); 8425839Speter retval = dbm_open (mfile, O_RDONLY, 0666); 8525839Speter free (mfile); 8625839Speter return retval; 8717721Speter} 8817721Speter 8917721Speter/* 9017721Speter * Close the modules file, if the open succeeded, that is 9117721Speter */ 9217721Spetervoid 9317721Speterclose_module (db) 9417721Speter DBM *db; 9517721Speter{ 9617721Speter if (db != NULL) 9717721Speter dbm_close (db); 9817721Speter} 9917721Speter 100128266Speter 101128266Speter 10217721Speter/* 10317721Speter * This is the recursive function that processes a module name. 10417721Speter * It calls back the passed routine for each directory of a module 10517721Speter * It runs the post checkout or post tag proc from the modules file 10617721Speter */ 107128266Speterstatic int 108128266Spetermy_module (db, mname, m_type, msg, callback_proc, where, shorten, 109128266Speter local_specified, run_module_prog, build_dirs, extra_arg, 110128266Speter stack) 11117721Speter DBM *db; 11217721Speter char *mname; 11317721Speter enum mtype m_type; 11417721Speter char *msg; 11517721Speter CALLBACKPROC callback_proc; 11617721Speter char *where; 11717721Speter int shorten; 11817721Speter int local_specified; 11917721Speter int run_module_prog; 12081404Speter int build_dirs; 12117721Speter char *extra_arg; 122128266Speter List *stack; 12317721Speter{ 12417721Speter char *checkout_prog = NULL; 12517721Speter char *export_prog = NULL; 12617721Speter char *tag_prog = NULL; 12717721Speter struct saved_cwd cwd; 12825839Speter int cwd_saved = 0; 12917721Speter char *line; 13017721Speter int modargc; 13117721Speter int xmodargc; 132177391Sobrien char **modargv = NULL; 13366525Speter char **xmodargv = NULL; 13454427Speter /* Found entry from modules file, including options and such. */ 13554427Speter char *value = NULL; 13617721Speter char *mwhere = NULL; 13717721Speter char *mfile = NULL; 13817721Speter char *spec_opt = NULL; 13917721Speter int alias = 0; 14017721Speter datum key, val; 14117721Speter char *cp; 14217721Speter int c, err = 0; 14325839Speter int nonalias_opt = 0; 14417721Speter 14517721Speter#ifdef SERVER_SUPPORT 14625839Speter int restore_server_dir = 0; 14732785Speter char *server_dir_to_restore = NULL; 14817721Speter if (trace) 14925839Speter { 15025839Speter char *buf; 15125839Speter 15225839Speter /* We use cvs_outerr, rather than fprintf to stderr, because 15325839Speter this may be called by server code with error_use_protocol 15425839Speter set. */ 15525839Speter buf = xmalloc (100 15625839Speter + strlen (mname) 15725839Speter + strlen (msg) 15825839Speter + (where ? strlen (where) : 0) 15925839Speter + (extra_arg ? strlen (extra_arg) : 0)); 160128266Speter sprintf (buf, "%s-> my_module (%s, %s, %s, %s)\n", 16154427Speter CLIENT_SERVER_STR, 16217721Speter mname, msg, where ? where : "", 16317721Speter extra_arg ? extra_arg : ""); 16425839Speter cvs_outerr (buf, 0); 16525839Speter free (buf); 16625839Speter } 16717721Speter#endif 16817721Speter 169124793Snectar /* Don't process absolute directories. Anything else could be a security 170124793Snectar * problem. Before this check was put in place: 171124793Snectar * 172124793Snectar * $ cvs -d:fork:/cvsroot co /foo 173124793Snectar * cvs server: warning: cannot make directory CVS in /: Permission denied 174124793Snectar * cvs [server aborted]: cannot make directory /foo: Permission denied 175124793Snectar * $ 176124793Snectar */ 177124793Snectar if (isabsolute (mname)) 178124793Snectar error (1, 0, "Absolute module reference invalid: `%s'", mname); 179124793Snectar 180128266Speter /* Similarly for directories that attempt to step above the root of the 181128266Speter * repository. 182128266Speter */ 183128266Speter if (pathname_levels (mname) > 0) 184128266Speter error (1, 0, "up-level in module reference (`..') invalid: `%s'.", 185128266Speter mname); 186128266Speter 18717721Speter /* if this is a directory to ignore, add it to that list */ 18817721Speter if (mname[0] == '!' && mname[1] != '\0') 18917721Speter { 19017721Speter ign_dir_add (mname+1); 19125839Speter goto do_module_return; 19217721Speter } 19317721Speter 19417721Speter /* strip extra stuff from the module name */ 19525839Speter strip_trailing_slashes (mname); 19617721Speter 19717721Speter /* 19817721Speter * Look up the module using the following scheme: 19917721Speter * 1) look for mname as a module name 20017721Speter * 2) look for mname as a directory 20117721Speter * 3) look for mname as a file 20217721Speter * 4) take mname up to the first slash and look it up as a module name 20317721Speter * (this is for checking out only part of a module) 20417721Speter */ 20517721Speter 20617721Speter /* look it up as a module name */ 20717721Speter key.dptr = mname; 20817721Speter key.dsize = strlen (key.dptr); 20917721Speter if (db != NULL) 21017721Speter val = dbm_fetch (db, key); 21117721Speter else 21217721Speter val.dptr = NULL; 21317721Speter if (val.dptr != NULL) 21417721Speter { 21566525Speter /* copy and null terminate the value */ 21666525Speter value = xmalloc (val.dsize + 1); 21766525Speter memcpy (value, val.dptr, val.dsize); 21866525Speter value[val.dsize] = '\0'; 21917721Speter 22017721Speter /* If the line ends in a comment, strip it off */ 22166525Speter if ((cp = strchr (value, '#')) != NULL) 22266525Speter *cp = '\0'; 22317721Speter else 22466525Speter cp = value + val.dsize; 22517721Speter 22666525Speter /* Always strip trailing spaces */ 22766525Speter while (cp > value && isspace ((unsigned char) *--cp)) 22866525Speter *cp = '\0'; 22966525Speter 23017721Speter mwhere = xstrdup (mname); 23117721Speter goto found; 23217721Speter } 23317721Speter else 23417721Speter { 23525839Speter char *file; 23625839Speter char *attic_file; 23717721Speter char *acp; 23825839Speter int is_found = 0; 23917721Speter 24017721Speter /* check to see if mname is a directory or file */ 24181404Speter file = xmalloc (strlen (current_parsed_root->directory) 24281404Speter + strlen (mname) + sizeof(RCSEXT) + 2); 24381404Speter (void) sprintf (file, "%s/%s", current_parsed_root->directory, mname); 24481404Speter attic_file = xmalloc (strlen (current_parsed_root->directory) 24581404Speter + strlen (mname) 24681404Speter + sizeof (CVSATTIC) + sizeof (RCSEXT) + 3); 24717721Speter if ((acp = strrchr (mname, '/')) != NULL) 24817721Speter { 24917721Speter *acp = '\0'; 25081404Speter (void) sprintf (attic_file, "%s/%s/%s/%s%s", current_parsed_root->directory, 25125839Speter mname, CVSATTIC, acp + 1, RCSEXT); 25217721Speter *acp = '/'; 25317721Speter } 25417721Speter else 255128266Speter (void) sprintf (attic_file, "%s/%s/%s%s", 256128266Speter current_parsed_root->directory, 25725839Speter CVSATTIC, mname, RCSEXT); 25817721Speter 25917721Speter if (isdir (file)) 26017721Speter { 26154427Speter modargv = xmalloc (sizeof (*modargv)); 26254427Speter modargv[0] = xstrdup (mname); 26354427Speter modargc = 1; 26425839Speter is_found = 1; 26517721Speter } 26617721Speter else 26717721Speter { 26817721Speter (void) strcat (file, RCSEXT); 26917721Speter if (isfile (file) || isfile (attic_file)) 27017721Speter { 27117721Speter /* if mname was a file, we have to split it into "dir file" */ 27217721Speter if ((cp = strrchr (mname, '/')) != NULL && cp != mname) 27317721Speter { 27454427Speter modargv = xmalloc (2 * sizeof (*modargv)); 27554427Speter modargv[0] = xmalloc (strlen (mname) + 2); 27654427Speter strncpy (modargv[0], mname, cp - mname); 27754427Speter modargv[0][cp - mname] = '\0'; 27854427Speter modargv[1] = xstrdup (cp + 1); 27954427Speter modargc = 2; 28017721Speter } 28117721Speter else 28217721Speter { 28317721Speter /* 28417721Speter * the only '/' at the beginning or no '/' at all 28517721Speter * means the file we are interested in is in CVSROOT 28617721Speter * itself so the directory should be '.' 28717721Speter */ 28817721Speter if (cp == mname) 28917721Speter { 29017721Speter /* drop the leading / if specified */ 29154427Speter modargv = xmalloc (2 * sizeof (*modargv)); 29254427Speter modargv[0] = xstrdup ("."); 29354427Speter modargv[1] = xstrdup (mname + 1); 29454427Speter modargc = 2; 29517721Speter } 29617721Speter else 29717721Speter { 29817721Speter /* otherwise just copy it */ 29954427Speter modargv = xmalloc (2 * sizeof (*modargv)); 30054427Speter modargv[0] = xstrdup ("."); 30154427Speter modargv[1] = xstrdup (mname); 30254427Speter modargc = 2; 30317721Speter } 30417721Speter } 30525839Speter is_found = 1; 30617721Speter } 30717721Speter } 30825839Speter free (attic_file); 30925839Speter free (file); 31025839Speter 31125839Speter if (is_found) 31254427Speter { 31354427Speter assert (value == NULL); 31454427Speter 31554427Speter /* OK, we have now set up modargv with the actual 31654427Speter file/directory we want to work on. We duplicate a 31754427Speter small amount of code here because the vast majority of 31854427Speter the code after the "found" label does not pertain to 31954427Speter the case where we found a file/directory rather than 32054427Speter finding an entry in the modules file. */ 32154427Speter if (save_cwd (&cwd)) 32254427Speter error_exit (); 32354427Speter cwd_saved = 1; 32454427Speter 32566525Speter err += callback_proc (modargc, modargv, where, mwhere, mfile, 32654427Speter shorten, 32754427Speter local_specified, mname, msg); 32854427Speter 32954427Speter free_names (&modargc, modargv); 33054427Speter 33154427Speter /* cd back to where we started. */ 33254427Speter if (restore_cwd (&cwd, NULL)) 33354427Speter error_exit (); 33454427Speter free_cwd (&cwd); 33554427Speter cwd_saved = 0; 33654427Speter 33754427Speter goto do_module_return; 33854427Speter } 33917721Speter } 34017721Speter 34117721Speter /* look up everything to the first / as a module */ 34217721Speter if (mname[0] != '/' && (cp = strchr (mname, '/')) != NULL) 34317721Speter { 34417721Speter /* Make the slash the new end of the string temporarily */ 34517721Speter *cp = '\0'; 34617721Speter key.dptr = mname; 34717721Speter key.dsize = strlen (key.dptr); 34817721Speter 34917721Speter /* do the lookup */ 35017721Speter if (db != NULL) 35117721Speter val = dbm_fetch (db, key); 35217721Speter else 35317721Speter val.dptr = NULL; 35417721Speter 35517721Speter /* if we found it, clean up the value and life is good */ 35617721Speter if (val.dptr != NULL) 35717721Speter { 35817721Speter char *cp2; 35917721Speter 36066525Speter /* copy and null terminate the value */ 36166525Speter value = xmalloc (val.dsize + 1); 36266525Speter memcpy (value, val.dptr, val.dsize); 36366525Speter value[val.dsize] = '\0'; 36417721Speter 36517721Speter /* If the line ends in a comment, strip it off */ 36666525Speter if ((cp2 = strchr (value, '#')) != NULL) 36766525Speter *cp2 = '\0'; 36866525Speter else 36966525Speter cp2 = value + val.dsize; 37017721Speter 37166525Speter /* Always strip trailing spaces */ 37266525Speter while (cp2 > value && isspace ((unsigned char) *--cp2)) 37366525Speter *cp2 = '\0'; 37466525Speter 37517721Speter /* mwhere gets just the module name */ 37617721Speter mwhere = xstrdup (mname); 37717721Speter mfile = cp + 1; 378175261Sobrien assert (strlen (mfile)); 37917721Speter 38017721Speter /* put the / back in mname */ 38117721Speter *cp = '/'; 38217721Speter 38317721Speter goto found; 38417721Speter } 38517721Speter 38617721Speter /* put the / back in mname */ 38717721Speter *cp = '/'; 38817721Speter } 38917721Speter 39017721Speter /* if we got here, we couldn't find it using our search, so give up */ 39117721Speter error (0, 0, "cannot find module `%s' - ignored", mname); 39217721Speter err++; 39325839Speter goto do_module_return; 39417721Speter 39517721Speter 39617721Speter /* 39717721Speter * At this point, we found what we were looking for in one 39817721Speter * of the many different forms. 39917721Speter */ 40017721Speter found: 40117721Speter 40217721Speter /* remember where we start */ 40317721Speter if (save_cwd (&cwd)) 40425839Speter error_exit (); 40525839Speter cwd_saved = 1; 40617721Speter 40754427Speter assert (value != NULL); 40817721Speter 40917721Speter /* search the value for the special delimiter and save for later */ 41017721Speter if ((cp = strchr (value, CVSMODULE_SPEC)) != NULL) 41117721Speter { 41217721Speter *cp = '\0'; /* null out the special char */ 41317721Speter spec_opt = cp + 1; /* save the options for later */ 41417721Speter 41566525Speter /* strip whitespace if necessary */ 41666525Speter while (cp > value && isspace ((unsigned char) *--cp)) 41766525Speter *cp = '\0'; 41817721Speter } 41917721Speter 42017721Speter /* don't do special options only part of a module was specified */ 42117721Speter if (mfile != NULL) 42217721Speter spec_opt = NULL; 42317721Speter 42417721Speter /* 42517721Speter * value now contains one of the following: 42617721Speter * 1) dir 42717721Speter * 2) dir file 42817721Speter * 3) the value from modules without any special args 42917721Speter * [ args ] dir [file] [file] ... 43017721Speter * or -a module [ module ] ... 43117721Speter */ 43217721Speter 43317721Speter /* Put the value on a line with XXX prepended for getopt to eat */ 43481404Speter line = xmalloc (strlen (value) + 5); 43566525Speter strcpy(line, "XXX "); 43666525Speter strcpy(line + 4, value); 43717721Speter 43817721Speter /* turn the line into an argv[] array */ 43932785Speter line2argv (&xmodargc, &xmodargv, line, " \t"); 44017721Speter free (line); 44117721Speter modargc = xmodargc; 44217721Speter modargv = xmodargv; 44317721Speter 44417721Speter /* parse the args */ 44526065Speter optind = 0; 44617721Speter while ((c = getopt (modargc, modargv, CVSMODULE_OPTS)) != -1) 44717721Speter { 44817721Speter switch (c) 44917721Speter { 45017721Speter case 'a': 45117721Speter alias = 1; 45217721Speter break; 45317721Speter case 'd': 45417721Speter if (mwhere) 45517721Speter free (mwhere); 45617721Speter mwhere = xstrdup (optarg); 45766525Speter nonalias_opt = 1; 45817721Speter break; 45917721Speter case 'l': 46066525Speter local_specified = 1; 46125839Speter nonalias_opt = 1; 46217721Speter break; 46317721Speter case 'o': 46426065Speter if (checkout_prog) 46526065Speter free (checkout_prog); 46626065Speter checkout_prog = xstrdup (optarg); 46766525Speter nonalias_opt = 1; 46817721Speter break; 46917721Speter case 'e': 47026065Speter if (export_prog) 47126065Speter free (export_prog); 47226065Speter export_prog = xstrdup (optarg); 47366525Speter nonalias_opt = 1; 47417721Speter break; 47517721Speter case 't': 47626065Speter if (tag_prog) 47726065Speter free (tag_prog); 47826065Speter tag_prog = xstrdup (optarg); 47966525Speter nonalias_opt = 1; 48017721Speter break; 48117721Speter case '?': 48217721Speter error (0, 0, 48317721Speter "modules file has invalid option for key %s value %s", 48466525Speter key.dptr, value); 48517721Speter err++; 48625839Speter goto do_module_return; 48717721Speter } 48817721Speter } 48917721Speter modargc -= optind; 49017721Speter modargv += optind; 49166525Speter if (modargc == 0 && spec_opt == NULL) 49217721Speter { 49317721Speter error (0, 0, "modules file missing directory for module %s", mname); 49425839Speter ++err; 49525839Speter goto do_module_return; 49617721Speter } 49717721Speter 49825839Speter if (alias && nonalias_opt) 49925839Speter { 50025839Speter /* The documentation has never said it is legal to specify 50125839Speter -a along with another option. And I believe that in the past 50225839Speter CVS has ignored the options other than -a, more or less, in this 50325839Speter situation. */ 50425839Speter error (0, 0, "\ 50525839Speter-a cannot be specified in the modules file along with other options"); 50625839Speter ++err; 50725839Speter goto do_module_return; 50825839Speter } 50925839Speter 51017721Speter /* if this was an alias, call ourselves recursively for each module */ 51117721Speter if (alias) 51217721Speter { 51317721Speter int i; 51417721Speter 51517721Speter for (i = 0; i < modargc; i++) 51617721Speter { 517128266Speter /* 518128266Speter * Recursion check: if an alias module calls itself or a module 519128266Speter * which causes the first to be called again, print an error 520128266Speter * message and stop recursing. 521128266Speter * 522128266Speter * Algorithm: 523128266Speter * 524128266Speter * 1. Check that MNAME isn't in the stack. 525128266Speter * 2. Push MNAME onto the stack. 526128266Speter * 3. Call do_module(). 527128266Speter * 4. Pop MNAME from the stack. 528128266Speter */ 529128266Speter if (stack && findnode (stack, mname)) 53017721Speter error (0, 0, 53117721Speter "module `%s' in modules file contains infinite loop", 53217721Speter mname); 53317721Speter else 534128266Speter { 535128266Speter if (!stack) stack = getlist(); 536128266Speter push_string (stack, mname); 537128266Speter err += my_module (db, modargv[i], m_type, msg, callback_proc, 538128266Speter where, shorten, local_specified, 539128266Speter run_module_prog, build_dirs, extra_arg, 540128266Speter stack); 541128266Speter pop_string (stack); 542128266Speter if (isempty (stack)) dellist (&stack); 543128266Speter } 54417721Speter } 54525839Speter goto do_module_return; 54617721Speter } 54717721Speter 54832785Speter if (mfile != NULL && modargc > 1) 54932785Speter { 55032785Speter error (0, 0, "\ 55132785Spetermodule `%s' is a request for a file in a module which is not a directory", 55232785Speter mname); 55332785Speter ++err; 55432785Speter goto do_module_return; 55532785Speter } 55632785Speter 55717721Speter /* otherwise, process this module */ 55866525Speter if (modargc > 0) 55966525Speter { 56066525Speter err += callback_proc (modargc, modargv, where, mwhere, mfile, shorten, 56166525Speter local_specified, mname, msg); 56266525Speter } 56366525Speter else 56466525Speter { 56566525Speter /* 56666525Speter * we had nothing but special options, so we must 56766525Speter * make the appropriate directory and cd to it 56866525Speter */ 56966525Speter char *dir; 57017721Speter 57181404Speter if (!build_dirs) 57266525Speter goto do_special; 57317721Speter 57466525Speter dir = where ? where : (mwhere ? mwhere : mname); 57566525Speter /* XXX - think about making null repositories at each dir here 57666525Speter instead of just at the bottom */ 57766525Speter make_directories (dir); 578128266Speter if (CVS_CHDIR (dir) < 0) 57966525Speter { 58066525Speter error (0, errno, "cannot chdir to %s", dir); 58166525Speter spec_opt = NULL; 58266525Speter err++; 58366525Speter goto do_special; 58466525Speter } 58566525Speter if (!isfile (CVSADM)) 58666525Speter { 58766525Speter char *nullrepos; 58866525Speter 58966525Speter nullrepos = emptydir_name (); 59066525Speter 59166525Speter Create_Admin (".", dir, 59266525Speter nullrepos, (char *) NULL, (char *) NULL, 0, 0, 1); 59366525Speter if (!noexec) 59466525Speter { 59566525Speter FILE *fp; 59666525Speter 59766525Speter fp = open_file (CVSADM_ENTSTAT, "w+"); 59866525Speter if (fclose (fp) == EOF) 59966525Speter error (1, errno, "cannot close %s", CVSADM_ENTSTAT); 60066525Speter#ifdef SERVER_SUPPORT 60166525Speter if (server_active) 60266525Speter server_set_entstat (dir, nullrepos); 60366525Speter#endif 60466525Speter } 60566525Speter free (nullrepos); 60666525Speter } 60766525Speter } 60866525Speter 60917721Speter /* if there were special include args, process them now */ 61017721Speter 61117721Speter do_special: 61217721Speter 61366525Speter free_names (&xmodargc, xmodargv); 61466525Speter xmodargv = NULL; 61566525Speter 61617721Speter /* blow off special options if -l was specified */ 61717721Speter if (local_specified) 61817721Speter spec_opt = NULL; 61917721Speter 62025839Speter#ifdef SERVER_SUPPORT 62125839Speter /* We want to check out into the directory named by the module. 62225839Speter So we set a global variable which tells the server to glom that 62325839Speter directory name onto the front. A cleaner approach would be some 62425839Speter way of passing it down to the recursive call, through the 62525839Speter callback_proc, to start_recursion, and then into the update_dir in 62625839Speter the struct file_info. That way the "Updating foo" message could 62725839Speter print the actual directory we are checking out into. 62825839Speter 62925839Speter For local CVS, this is handled by the chdir call above 63025839Speter (directly or via the callback_proc). */ 63125839Speter if (server_active && spec_opt != NULL) 63225839Speter { 63325839Speter char *change_to; 63425839Speter 63525839Speter change_to = where ? where : (mwhere ? mwhere : mname); 63625839Speter server_dir_to_restore = server_dir; 63725839Speter restore_server_dir = 1; 63825839Speter server_dir = 63925839Speter xmalloc ((server_dir_to_restore != NULL 64025839Speter ? strlen (server_dir_to_restore) 64125839Speter : 0) 64225839Speter + strlen (change_to) 64325839Speter + 5); 64425839Speter server_dir[0] = '\0'; 64525839Speter if (server_dir_to_restore != NULL) 64625839Speter { 64725839Speter strcat (server_dir, server_dir_to_restore); 64825839Speter strcat (server_dir, "/"); 64925839Speter } 65025839Speter strcat (server_dir, change_to); 65125839Speter } 65225839Speter#endif 65325839Speter 65417721Speter while (spec_opt != NULL) 65517721Speter { 65617721Speter char *next_opt; 65717721Speter 65817721Speter cp = strchr (spec_opt, CVSMODULE_SPEC); 65917721Speter if (cp != NULL) 66017721Speter { 66117721Speter /* save the beginning of the next arg */ 66217721Speter next_opt = cp + 1; 66317721Speter 66417721Speter /* strip whitespace off the end */ 66517721Speter do 66617721Speter *cp = '\0'; 66766525Speter while (cp > spec_opt && isspace ((unsigned char) *--cp)); 66817721Speter } 66917721Speter else 67017721Speter next_opt = NULL; 67117721Speter 67217721Speter /* strip whitespace from front */ 67354427Speter while (isspace ((unsigned char) *spec_opt)) 67417721Speter spec_opt++; 67517721Speter 67617721Speter if (*spec_opt == '\0') 67717721Speter error (0, 0, "Mal-formed %c option for module %s - ignored", 67817721Speter CVSMODULE_SPEC, mname); 67917721Speter else 680128266Speter err += my_module (db, spec_opt, m_type, msg, callback_proc, 681128266Speter (char *) NULL, 0, local_specified, 682128266Speter run_module_prog, build_dirs, extra_arg, 683128266Speter stack); 68417721Speter spec_opt = next_opt; 68517721Speter } 68617721Speter 68725839Speter#ifdef SERVER_SUPPORT 68825839Speter if (server_active && restore_server_dir) 68925839Speter { 69025839Speter free (server_dir); 69125839Speter server_dir = server_dir_to_restore; 69225839Speter } 69325839Speter#endif 69425839Speter 69517721Speter /* cd back to where we started */ 69617721Speter if (restore_cwd (&cwd, NULL)) 69725839Speter error_exit (); 69817721Speter free_cwd (&cwd); 69925839Speter cwd_saved = 0; 70017721Speter 70117721Speter /* run checkout or tag prog if appropriate */ 70217721Speter if (err == 0 && run_module_prog) 70317721Speter { 70417721Speter if ((m_type == TAG && tag_prog != NULL) || 70517721Speter (m_type == CHECKOUT && checkout_prog != NULL) || 70617721Speter (m_type == EXPORT && export_prog != NULL)) 70717721Speter { 70817721Speter /* 70917721Speter * If a relative pathname is specified as the checkout, tag 71017721Speter * or export proc, try to tack on the current "where" value. 71117721Speter * if we can't find a matching program, just punt and use 71217721Speter * whatever is specified in the modules file. 71317721Speter */ 71425839Speter char *real_prog = NULL; 71517721Speter char *prog = (m_type == TAG ? tag_prog : 71617721Speter (m_type == CHECKOUT ? checkout_prog : export_prog)); 71717721Speter char *real_where = (where != NULL ? where : mwhere); 71825839Speter char *expanded_path; 71917721Speter 72017721Speter if ((*prog != '/') && (*prog != '.')) 72117721Speter { 72225839Speter real_prog = xmalloc (strlen (real_where) + strlen (prog) 72325839Speter + 10); 72417721Speter (void) sprintf (real_prog, "%s/%s", real_where, prog); 72517721Speter if (isfile (real_prog)) 72617721Speter prog = real_prog; 72717721Speter } 72817721Speter 72925839Speter /* XXX can we determine the line number for this entry??? */ 73025839Speter expanded_path = expand_path (prog, "modules", 0); 73125839Speter if (expanded_path != NULL) 73225839Speter { 73332785Speter run_setup (expanded_path); 73432785Speter run_arg (real_where); 73517721Speter 73625839Speter if (extra_arg) 73725839Speter run_arg (extra_arg); 73825839Speter 73925839Speter if (!quiet) 74025839Speter { 74125839Speter cvs_output (program_name, 0); 74225839Speter cvs_output (" ", 1); 743128266Speter cvs_output (cvs_cmd_name, 0); 74425839Speter cvs_output (": Executing '", 0); 74525839Speter run_print (stdout); 74625839Speter cvs_output ("'\n", 0); 74766525Speter cvs_flushout (); 74825839Speter } 74925839Speter err += run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL); 75025839Speter free (expanded_path); 75117721Speter } 752175261Sobrien if (real_prog) free (real_prog); 75317721Speter } 75417721Speter } 75517721Speter 75625839Speter do_module_return: 75717721Speter /* clean up */ 75866525Speter if (xmodargv != NULL) 75966525Speter free_names (&xmodargc, xmodargv); 76017721Speter if (mwhere) 76117721Speter free (mwhere); 76226065Speter if (checkout_prog) 76326065Speter free (checkout_prog); 76426065Speter if (export_prog) 76526065Speter free (export_prog); 76626065Speter if (tag_prog) 76726065Speter free (tag_prog); 76825839Speter if (cwd_saved) 76925839Speter free_cwd (&cwd); 77066525Speter if (value != NULL) 77166525Speter free (value); 77217721Speter 77317721Speter return (err); 77417721Speter} 77517721Speter 776128266Speter 777128266Speter 778128266Speter/* External face of do_module so that we can have an internal version which 779128266Speter * accepts a stack argument to track alias recursion. 780128266Speter */ 781128266Speterint 782128266Speterdo_module (db, mname, m_type, msg, callback_proc, where, shorten, 783128266Speter local_specified, run_module_prog, build_dirs, extra_arg) 784128266Speter DBM *db; 785128266Speter char *mname; 786128266Speter enum mtype m_type; 787128266Speter char *msg; 788128266Speter CALLBACKPROC callback_proc; 789128266Speter char *where; 790128266Speter int shorten; 791128266Speter int local_specified; 792128266Speter int run_module_prog; 793128266Speter int build_dirs; 794128266Speter char *extra_arg; 795128266Speter{ 796128266Speter return my_module (db, mname, m_type, msg, callback_proc, where, shorten, 797128266Speter local_specified, run_module_prog, build_dirs, extra_arg, 798128266Speter NULL); 799128266Speter} 800128266Speter 801128266Speter 802128266Speter 80317721Speter/* - Read all the records from the modules database into an array. 80417721Speter - Sort the array depending on what format is desired. 80517721Speter - Print the array in the format desired. 80617721Speter 80717721Speter Currently, there are only two "desires": 80817721Speter 80917721Speter 1. Sort by module name and format the whole entry including switches, 81017721Speter files and the comment field: (Including aliases) 81117721Speter 81217721Speter modulename -s switches, one per line, even if 813128266Speter it has many switches. 81417721Speter Directories and files involved, formatted 81517721Speter to cover multiple lines if necessary. 81617721Speter # Comment, also formatted to cover multiple 81717721Speter # lines if necessary. 81817721Speter 81917721Speter 2. Sort by status field string and print: (*not* including aliases) 82017721Speter 82117721Speter modulename STATUS Directories and files involved, formatted 82217721Speter to cover multiple lines if necessary. 82317721Speter # Comment, also formatted to cover multiple 82417721Speter # lines if necessary. 82517721Speter*/ 82617721Speter 82717721Speterstatic struct sortrec *s_head; 82817721Speter 82917721Speterstatic int s_max = 0; /* Number of elements allocated */ 83017721Speterstatic int s_count = 0; /* Number of elements used */ 83117721Speter 83217721Speterstatic int Status; /* Nonzero if the user is 83317721Speter interested in status 83417721Speter information as well as 83517721Speter module name */ 83617721Speterstatic char def_status[] = "NONE"; 83717721Speter 83817721Speter/* Sort routine for qsort: 83917721Speter - If we want the "Status" field to be sorted, check it first. 84017721Speter - Then compare the "module name" fields. Since they are unique, we don't 84117721Speter have to look further. 84217721Speter*/ 84317721Speterstatic int 84417721Spetersort_order (l, r) 84517721Speter const PTR l; 84617721Speter const PTR r; 84717721Speter{ 84817721Speter int i; 84917721Speter const struct sortrec *left = (const struct sortrec *) l; 85017721Speter const struct sortrec *right = (const struct sortrec *) r; 85117721Speter 85217721Speter if (Status) 85317721Speter { 85417721Speter /* If Sort by status field, compare them. */ 85517721Speter if ((i = strcmp (left->status, right->status)) != 0) 85617721Speter return (i); 85717721Speter } 85817721Speter return (strcmp (left->modname, right->modname)); 85917721Speter} 86017721Speter 86117721Speterstatic void 86217721Spetersave_d (k, ks, d, ds) 86317721Speter char *k; 86417721Speter int ks; 86517721Speter char *d; 86617721Speter int ds; 86717721Speter{ 86817721Speter char *cp, *cp2; 86917721Speter struct sortrec *s_rec; 87017721Speter 87117721Speter if (Status && *d == '-' && *(d + 1) == 'a') 87217721Speter return; /* We want "cvs co -s" and it is an alias! */ 87317721Speter 87417721Speter if (s_count == s_max) 87517721Speter { 87617721Speter s_max += 64; 87717721Speter s_head = (struct sortrec *) xrealloc ((char *) s_head, s_max * sizeof (*s_head)); 87817721Speter } 87917721Speter s_rec = &s_head[s_count]; 88017721Speter s_rec->modname = cp = xmalloc (ks + 1); 88117721Speter (void) strncpy (cp, k, ks); 88217721Speter *(cp + ks) = '\0'; 88317721Speter 88417721Speter s_rec->rest = cp2 = xmalloc (ds + 1); 88517721Speter cp = d; 88617721Speter *(cp + ds) = '\0'; /* Assumes an extra byte at end of static dbm buffer */ 88717721Speter 88854427Speter while (isspace ((unsigned char) *cp)) 88917721Speter cp++; 89017721Speter /* Turn <spaces> into one ' ' -- makes the rest of this routine simpler */ 89117721Speter while (*cp) 89217721Speter { 89354427Speter if (isspace ((unsigned char) *cp)) 89417721Speter { 89517721Speter *cp2++ = ' '; 89654427Speter while (isspace ((unsigned char) *cp)) 89717721Speter cp++; 89817721Speter } 89917721Speter else 90017721Speter *cp2++ = *cp++; 90117721Speter } 90217721Speter *cp2 = '\0'; 90317721Speter 90417721Speter /* Look for the "-s statusvalue" text */ 90517721Speter if (Status) 90617721Speter { 90717721Speter s_rec->status = def_status; 90817721Speter 90917721Speter for (cp = s_rec->rest; (cp2 = strchr (cp, '-')) != NULL; cp = ++cp2) 91017721Speter { 91117721Speter if (*(cp2 + 1) == 's' && *(cp2 + 2) == ' ') 91217721Speter { 91332785Speter char *status_start; 91432785Speter 91532785Speter cp2 += 3; 91632785Speter status_start = cp2; 91732785Speter while (*cp2 != ' ' && *cp2 != '\0') 91817721Speter cp2++; 91932785Speter s_rec->status = xmalloc (cp2 - status_start + 1); 92032785Speter strncpy (s_rec->status, status_start, cp2 - status_start); 92132785Speter s_rec->status[cp2 - status_start] = '\0'; 92217721Speter cp = cp2; 92317721Speter break; 92417721Speter } 92517721Speter } 92617721Speter } 92717721Speter else 92817721Speter cp = s_rec->rest; 92917721Speter 93017721Speter /* Find comment field, clean up on all three sides & compress blanks */ 93117721Speter if ((cp2 = cp = strchr (cp, '#')) != NULL) 93217721Speter { 93317721Speter if (*--cp2 == ' ') 93417721Speter *cp2 = '\0'; 93517721Speter if (*++cp == ' ') 93617721Speter cp++; 93717721Speter s_rec->comment = cp; 93817721Speter } 93917721Speter else 94017721Speter s_rec->comment = ""; 94117721Speter 94217721Speter s_count++; 94317721Speter} 94417721Speter 94517721Speter/* Print out the module database as we know it. If STATUS is 94617721Speter non-zero, print out status information for each module. */ 94717721Speter 94817721Spetervoid 94917721Spetercat_module (status) 95017721Speter int status; 95117721Speter{ 95217721Speter DBM *db; 95317721Speter datum key, val; 95417721Speter int i, c, wid, argc, cols = 80, indent, fill; 95517721Speter int moduleargc; 95617721Speter struct sortrec *s_h; 95717721Speter char *cp, *cp2, **argv; 95825839Speter char **moduleargv; 95917721Speter 96017721Speter Status = status; 96117721Speter 96217721Speter /* Read the whole modules file into allocated records */ 96317721Speter if (!(db = open_module ())) 96417721Speter error (1, 0, "failed to open the modules file"); 96517721Speter 96617721Speter for (key = dbm_firstkey (db); key.dptr != NULL; key = dbm_nextkey (db)) 96717721Speter { 96817721Speter val = dbm_fetch (db, key); 96917721Speter if (val.dptr != NULL) 97017721Speter save_d (key.dptr, key.dsize, val.dptr, val.dsize); 97117721Speter } 97217721Speter 97325839Speter close_module (db); 97425839Speter 97517721Speter /* Sort the list as requested */ 97617721Speter qsort ((PTR) s_head, s_count, sizeof (struct sortrec), sort_order); 97717721Speter 97817721Speter /* 97917721Speter * Run through the sorted array and format the entries 98017721Speter * indent = space for modulename + space for status field 98117721Speter */ 98217721Speter indent = 12 + (status * 12); 98317721Speter fill = cols - (indent + 2); 98417721Speter for (s_h = s_head, i = 0; i < s_count; i++, s_h++) 98517721Speter { 98625839Speter char *line; 98725839Speter 98817721Speter /* Print module name (and status, if wanted) */ 98925839Speter line = xmalloc (strlen (s_h->modname) + 15); 99025839Speter sprintf (line, "%-12s", s_h->modname); 99125839Speter cvs_output (line, 0); 99225839Speter free (line); 99317721Speter if (status) 99417721Speter { 99525839Speter line = xmalloc (strlen (s_h->status) + 15); 99625839Speter sprintf (line, " %-11s", s_h->status); 99725839Speter cvs_output (line, 0); 99825839Speter free (line); 99917721Speter } 100017721Speter 100125839Speter line = xmalloc (strlen (s_h->modname) + strlen (s_h->rest) + 15); 100217721Speter /* Parse module file entry as command line and print options */ 100317721Speter (void) sprintf (line, "%s %s", s_h->modname, s_h->rest); 100432785Speter line2argv (&moduleargc, &moduleargv, line, " \t"); 100517721Speter free (line); 100617721Speter argc = moduleargc; 100717721Speter argv = moduleargv; 100817721Speter 100917721Speter optind = 0; 101017721Speter wid = 0; 101117721Speter while ((c = getopt (argc, argv, CVSMODULE_OPTS)) != -1) 101217721Speter { 101317721Speter if (!status) 101417721Speter { 101517721Speter if (c == 'a' || c == 'l') 101617721Speter { 101725839Speter char buf[5]; 101825839Speter 101925839Speter sprintf (buf, " -%c", c); 102025839Speter cvs_output (buf, 0); 102117721Speter wid += 3; /* Could just set it to 3 */ 102217721Speter } 102317721Speter else 102417721Speter { 102525839Speter char buf[10]; 102625839Speter 102717721Speter if (strlen (optarg) + 4 + wid > (unsigned) fill) 102817721Speter { 102925839Speter int j; 103025839Speter 103125839Speter cvs_output ("\n", 1); 103225839Speter for (j = 0; j < indent; ++j) 103325839Speter cvs_output (" ", 1); 103417721Speter wid = 0; 103517721Speter } 103625839Speter sprintf (buf, " -%c ", c); 103725839Speter cvs_output (buf, 0); 103825839Speter cvs_output (optarg, 0); 103917721Speter wid += strlen (optarg) + 4; 104017721Speter } 104117721Speter } 104217721Speter } 104317721Speter argc -= optind; 104417721Speter argv += optind; 104517721Speter 104617721Speter /* Format and Print all the files and directories */ 104717721Speter for (; argc--; argv++) 104817721Speter { 104917721Speter if (strlen (*argv) + wid > (unsigned) fill) 105017721Speter { 105125839Speter int j; 105225839Speter 105325839Speter cvs_output ("\n", 1); 105425839Speter for (j = 0; j < indent; ++j) 105525839Speter cvs_output (" ", 1); 105617721Speter wid = 0; 105717721Speter } 105825839Speter cvs_output (" ", 1); 105925839Speter cvs_output (*argv, 0); 106017721Speter wid += strlen (*argv) + 1; 106117721Speter } 106225839Speter cvs_output ("\n", 1); 106317721Speter 106417721Speter /* Format the comment field -- save_d (), compressed spaces */ 106517721Speter for (cp2 = cp = s_h->comment; *cp; cp2 = cp) 106617721Speter { 106725839Speter int j; 106825839Speter 106925839Speter for (j = 0; j < indent; ++j) 107025839Speter cvs_output (" ", 1); 107125839Speter cvs_output (" # ", 0); 107217721Speter if (strlen (cp2) < (unsigned) (fill - 2)) 107317721Speter { 107425839Speter cvs_output (cp2, 0); 107525839Speter cvs_output ("\n", 1); 107617721Speter break; 107717721Speter } 107817721Speter cp += fill - 2; 107917721Speter while (*cp != ' ' && cp > cp2) 108017721Speter cp--; 108117721Speter if (cp == cp2) 108217721Speter { 108325839Speter cvs_output (cp2, 0); 108425839Speter cvs_output ("\n", 1); 108517721Speter break; 108617721Speter } 108717721Speter 108817721Speter *cp++ = '\0'; 108925839Speter cvs_output (cp2, 0); 109025839Speter cvs_output ("\n", 1); 109117721Speter } 109217721Speter 109317721Speter free_names(&moduleargc, moduleargv); 109432785Speter /* FIXME-leak: here is where we would free s_h->modname, s_h->rest, 109532785Speter and if applicable, s_h->status. Not exactly a memory leak, 109632785Speter in the sense that we are about to exit(), but may be worth 109732785Speter noting if we ever do a multithreaded server or something of 109832785Speter the sort. */ 109917721Speter } 110032785Speter /* FIXME-leak: as above, here is where we would free s_head. */ 110117721Speter} 1102