xref: /dflybsd-src/contrib/cvs-1.12/src/modules.c (revision 86d7f5d305c6adaa56ff4582ece9859d73106103)
1*86d7f5d3SJohn Marino /*
2*86d7f5d3SJohn Marino  * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
3*86d7f5d3SJohn Marino  *
4*86d7f5d3SJohn Marino  * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
5*86d7f5d3SJohn Marino  *                                  and others.
6*86d7f5d3SJohn Marino  *
7*86d7f5d3SJohn Marino  * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
8*86d7f5d3SJohn Marino  * Portions Copyright (C) 1989-1992, Brian Berliner
9*86d7f5d3SJohn Marino  *
10*86d7f5d3SJohn Marino  *    You may distribute under the terms of the GNU General Public License
11*86d7f5d3SJohn Marino  *    as specified in the README file that comes with the CVS source
12*86d7f5d3SJohn Marino  *    distribution.
13*86d7f5d3SJohn Marino  *
14*86d7f5d3SJohn Marino  * Modules
15*86d7f5d3SJohn Marino  *
16*86d7f5d3SJohn Marino  *	Functions for accessing the modules file.
17*86d7f5d3SJohn Marino  *
18*86d7f5d3SJohn Marino  *	The modules file supports basically three formats of lines:
19*86d7f5d3SJohn Marino  *		key [options] directory files... [ -x directory [files] ] ...
20*86d7f5d3SJohn Marino  *		key [options] directory [ -x directory [files] ] ...
21*86d7f5d3SJohn Marino  *		key -a aliases...
22*86d7f5d3SJohn Marino  *
23*86d7f5d3SJohn Marino  *	The -a option allows an aliasing step in the parsing of the modules
24*86d7f5d3SJohn Marino  *	file.  The "aliases" listed on a line following the -a are
25*86d7f5d3SJohn Marino  *	processed one-by-one, as if they were specified as arguments on the
26*86d7f5d3SJohn Marino  *	command line.
27*86d7f5d3SJohn Marino  */
28*86d7f5d3SJohn Marino 
29*86d7f5d3SJohn Marino #include "cvs.h"
30*86d7f5d3SJohn Marino #include "save-cwd.h"
31*86d7f5d3SJohn Marino 
32*86d7f5d3SJohn Marino 
33*86d7f5d3SJohn Marino /* Defines related to the syntax of the modules file.  */
34*86d7f5d3SJohn Marino 
35*86d7f5d3SJohn Marino /* Options in modules file.  Note that it is OK to use GNU getopt features;
36*86d7f5d3SJohn Marino    we already are arranging to make sure we are using the getopt distributed
37*86d7f5d3SJohn Marino    with CVS.  */
38*86d7f5d3SJohn Marino #define	CVSMODULE_OPTS	"+ad:lo:e:s:t:"
39*86d7f5d3SJohn Marino 
40*86d7f5d3SJohn Marino /* Special delimiter.  */
41*86d7f5d3SJohn Marino #define CVSMODULE_SPEC	'&'
42*86d7f5d3SJohn Marino 
43*86d7f5d3SJohn Marino struct sortrec
44*86d7f5d3SJohn Marino {
45*86d7f5d3SJohn Marino     /* Name of the module, malloc'd.  */
46*86d7f5d3SJohn Marino     char *modname;
47*86d7f5d3SJohn Marino     /* If Status variable is set, this is either def_status or the malloc'd
48*86d7f5d3SJohn Marino        name of the status.  If Status is not set, the field is left
49*86d7f5d3SJohn Marino        uninitialized.  */
50*86d7f5d3SJohn Marino     char *status;
51*86d7f5d3SJohn Marino     /* Pointer to a malloc'd array which contains (1) the raw contents
52*86d7f5d3SJohn Marino        of the options and arguments, excluding comments, (2) a '\0',
53*86d7f5d3SJohn Marino        and (3) the storage for the "comment" field.  */
54*86d7f5d3SJohn Marino     char *rest;
55*86d7f5d3SJohn Marino     char *comment;
56*86d7f5d3SJohn Marino };
57*86d7f5d3SJohn Marino 
58*86d7f5d3SJohn Marino static int sort_order (const void *l, const void *r);
59*86d7f5d3SJohn Marino static void save_d (char *k, int ks, char *d, int ds);
60*86d7f5d3SJohn Marino 
61*86d7f5d3SJohn Marino 
62*86d7f5d3SJohn Marino /*
63*86d7f5d3SJohn Marino  * Open the modules file, and die if the CVSROOT environment variable
64*86d7f5d3SJohn Marino  * was not set.  If the modules file does not exist, that's fine, and
65*86d7f5d3SJohn Marino  * a warning message is displayed and a NULL is returned.
66*86d7f5d3SJohn Marino  */
67*86d7f5d3SJohn Marino DBM *
open_module(void)68*86d7f5d3SJohn Marino open_module (void)
69*86d7f5d3SJohn Marino {
70*86d7f5d3SJohn Marino     char *mfile;
71*86d7f5d3SJohn Marino     DBM *retval;
72*86d7f5d3SJohn Marino 
73*86d7f5d3SJohn Marino     if (current_parsed_root == NULL)
74*86d7f5d3SJohn Marino     {
75*86d7f5d3SJohn Marino 	error (0, 0, "must set the CVSROOT environment variable");
76*86d7f5d3SJohn Marino 	error (1, 0, "or specify the '-d' global option");
77*86d7f5d3SJohn Marino     }
78*86d7f5d3SJohn Marino     mfile = Xasprintf ("%s/%s/%s", current_parsed_root->directory,
79*86d7f5d3SJohn Marino 		       CVSROOTADM, CVSROOTADM_MODULES);
80*86d7f5d3SJohn Marino     retval = dbm_open (mfile, O_RDONLY, 0666);
81*86d7f5d3SJohn Marino     free (mfile);
82*86d7f5d3SJohn Marino     return retval;
83*86d7f5d3SJohn Marino }
84*86d7f5d3SJohn Marino 
85*86d7f5d3SJohn Marino /*
86*86d7f5d3SJohn Marino  * Close the modules file, if the open succeeded, that is
87*86d7f5d3SJohn Marino  */
88*86d7f5d3SJohn Marino void
close_module(DBM * db)89*86d7f5d3SJohn Marino close_module (DBM *db)
90*86d7f5d3SJohn Marino {
91*86d7f5d3SJohn Marino     if (db != NULL)
92*86d7f5d3SJohn Marino 	dbm_close (db);
93*86d7f5d3SJohn Marino }
94*86d7f5d3SJohn Marino 
95*86d7f5d3SJohn Marino 
96*86d7f5d3SJohn Marino 
97*86d7f5d3SJohn Marino /*
98*86d7f5d3SJohn Marino  * This is the recursive function that processes a module name.
99*86d7f5d3SJohn Marino  * It calls back the passed routine for each directory of a module
100*86d7f5d3SJohn Marino  * It runs the post checkout or post tag proc from the modules file
101*86d7f5d3SJohn Marino  */
102*86d7f5d3SJohn Marino int
my_module(DBM * db,char * mname,enum mtype m_type,char * msg,CALLBACKPROC callback_proc,char * where,int shorten,int local_specified,int run_module_prog,int build_dirs,char * extra_arg,List * stack)103*86d7f5d3SJohn Marino my_module (DBM *db, char *mname, enum mtype m_type, char *msg,
104*86d7f5d3SJohn Marino             CALLBACKPROC callback_proc, char *where, int shorten,
105*86d7f5d3SJohn Marino             int local_specified, int run_module_prog, int build_dirs,
106*86d7f5d3SJohn Marino             char *extra_arg, List *stack)
107*86d7f5d3SJohn Marino {
108*86d7f5d3SJohn Marino     char *checkout_prog = NULL;
109*86d7f5d3SJohn Marino     char *export_prog = NULL;
110*86d7f5d3SJohn Marino     char *tag_prog = NULL;
111*86d7f5d3SJohn Marino     struct saved_cwd cwd;
112*86d7f5d3SJohn Marino     int cwd_saved = 0;
113*86d7f5d3SJohn Marino     char *line;
114*86d7f5d3SJohn Marino     int modargc;
115*86d7f5d3SJohn Marino     int xmodargc;
116*86d7f5d3SJohn Marino     char **modargv = NULL;
117*86d7f5d3SJohn Marino     char **xmodargv = NULL;
118*86d7f5d3SJohn Marino     /* Found entry from modules file, including options and such.  */
119*86d7f5d3SJohn Marino     char *value = NULL;
120*86d7f5d3SJohn Marino     char *mwhere = NULL;
121*86d7f5d3SJohn Marino     char *mfile = NULL;
122*86d7f5d3SJohn Marino     char *spec_opt = NULL;
123*86d7f5d3SJohn Marino     char *xvalue = NULL;
124*86d7f5d3SJohn Marino     int alias = 0;
125*86d7f5d3SJohn Marino     datum key, val;
126*86d7f5d3SJohn Marino     char *cp;
127*86d7f5d3SJohn Marino     int c, err = 0;
128*86d7f5d3SJohn Marino     int nonalias_opt = 0;
129*86d7f5d3SJohn Marino 
130*86d7f5d3SJohn Marino #ifdef SERVER_SUPPORT
131*86d7f5d3SJohn Marino     int restore_server_dir = 0;
132*86d7f5d3SJohn Marino     char *server_dir_to_restore = NULL;
133*86d7f5d3SJohn Marino #endif
134*86d7f5d3SJohn Marino 
135*86d7f5d3SJohn Marino     TRACE (TRACE_FUNCTION, "my_module (%s, %s, %s, %s)",
136*86d7f5d3SJohn Marino            mname ? mname : "(null)", msg ? msg : "(null)",
137*86d7f5d3SJohn Marino            where ? where : "NULL", extra_arg ? extra_arg : "NULL");
138*86d7f5d3SJohn Marino 
139*86d7f5d3SJohn Marino     /* Don't process absolute directories.  Anything else could be a security
140*86d7f5d3SJohn Marino      * problem.  Before this check was put in place:
141*86d7f5d3SJohn Marino      *
142*86d7f5d3SJohn Marino      *   $ cvs -d:fork:/cvsroot co /foo
143*86d7f5d3SJohn Marino      *   cvs server: warning: cannot make directory CVS in /: Permission denied
144*86d7f5d3SJohn Marino      *   cvs [server aborted]: cannot make directory /foo: Permission denied
145*86d7f5d3SJohn Marino      *   $
146*86d7f5d3SJohn Marino      */
147*86d7f5d3SJohn Marino     if (ISABSOLUTE (mname))
148*86d7f5d3SJohn Marino 	error (1, 0, "Absolute module reference invalid: `%s'", mname);
149*86d7f5d3SJohn Marino 
150*86d7f5d3SJohn Marino     /* Similarly for directories that attempt to step above the root of the
151*86d7f5d3SJohn Marino      * repository.
152*86d7f5d3SJohn Marino      */
153*86d7f5d3SJohn Marino     if (pathname_levels (mname) > 0)
154*86d7f5d3SJohn Marino 	error (1, 0, "up-level in module reference (`..') invalid: `%s'.",
155*86d7f5d3SJohn Marino                mname);
156*86d7f5d3SJohn Marino 
157*86d7f5d3SJohn Marino     /* if this is a directory to ignore, add it to that list */
158*86d7f5d3SJohn Marino     if (mname[0] == '!' && mname[1] != '\0')
159*86d7f5d3SJohn Marino     {
160*86d7f5d3SJohn Marino 	ign_dir_add (mname+1);
161*86d7f5d3SJohn Marino 	goto do_module_return;
162*86d7f5d3SJohn Marino     }
163*86d7f5d3SJohn Marino 
164*86d7f5d3SJohn Marino     /* strip extra stuff from the module name */
165*86d7f5d3SJohn Marino     strip_trailing_slashes (mname);
166*86d7f5d3SJohn Marino 
167*86d7f5d3SJohn Marino     /*
168*86d7f5d3SJohn Marino      * Look up the module using the following scheme:
169*86d7f5d3SJohn Marino      *	1) look for mname as a module name
170*86d7f5d3SJohn Marino      *	2) look for mname as a directory
171*86d7f5d3SJohn Marino      *	3) look for mname as a file
172*86d7f5d3SJohn Marino      *  4) take mname up to the first slash and look it up as a module name
173*86d7f5d3SJohn Marino      *	   (this is for checking out only part of a module)
174*86d7f5d3SJohn Marino      */
175*86d7f5d3SJohn Marino 
176*86d7f5d3SJohn Marino     /* look it up as a module name */
177*86d7f5d3SJohn Marino     key.dptr = mname;
178*86d7f5d3SJohn Marino     key.dsize = strlen (key.dptr);
179*86d7f5d3SJohn Marino     if (db != NULL)
180*86d7f5d3SJohn Marino 	val = dbm_fetch (db, key);
181*86d7f5d3SJohn Marino     else
182*86d7f5d3SJohn Marino 	val.dptr = NULL;
183*86d7f5d3SJohn Marino     if (val.dptr != NULL)
184*86d7f5d3SJohn Marino     {
185*86d7f5d3SJohn Marino 	/* copy and null terminate the value */
186*86d7f5d3SJohn Marino 	value = xmalloc (val.dsize + 1);
187*86d7f5d3SJohn Marino 	memcpy (value, val.dptr, val.dsize);
188*86d7f5d3SJohn Marino 	value[val.dsize] = '\0';
189*86d7f5d3SJohn Marino 
190*86d7f5d3SJohn Marino 	/* If the line ends in a comment, strip it off */
191*86d7f5d3SJohn Marino 	if ((cp = strchr (value, '#')) != NULL)
192*86d7f5d3SJohn Marino 	    *cp = '\0';
193*86d7f5d3SJohn Marino 	else
194*86d7f5d3SJohn Marino 	    cp = value + val.dsize;
195*86d7f5d3SJohn Marino 
196*86d7f5d3SJohn Marino 	/* Always strip trailing spaces */
197*86d7f5d3SJohn Marino 	while (cp > value && isspace ((unsigned char) *--cp))
198*86d7f5d3SJohn Marino 	    *cp = '\0';
199*86d7f5d3SJohn Marino 
200*86d7f5d3SJohn Marino 	mwhere = xstrdup (mname);
201*86d7f5d3SJohn Marino 	goto found;
202*86d7f5d3SJohn Marino     }
203*86d7f5d3SJohn Marino     else
204*86d7f5d3SJohn Marino     {
205*86d7f5d3SJohn Marino 	char *file;
206*86d7f5d3SJohn Marino 	char *attic_file;
207*86d7f5d3SJohn Marino 	char *acp;
208*86d7f5d3SJohn Marino 	int is_found = 0;
209*86d7f5d3SJohn Marino 
210*86d7f5d3SJohn Marino 	/* check to see if mname is a directory or file */
211*86d7f5d3SJohn Marino 	file = xmalloc (strlen (current_parsed_root->directory)
212*86d7f5d3SJohn Marino 			+ strlen (mname) + sizeof(RCSEXT) + 2);
213*86d7f5d3SJohn Marino 	(void) sprintf (file, "%s/%s", current_parsed_root->directory, mname);
214*86d7f5d3SJohn Marino 	attic_file = xmalloc (strlen (current_parsed_root->directory)
215*86d7f5d3SJohn Marino 			      + strlen (mname)
216*86d7f5d3SJohn Marino 			      + sizeof (CVSATTIC) + sizeof (RCSEXT) + 3);
217*86d7f5d3SJohn Marino 	if ((acp = strrchr (mname, '/')) != NULL)
218*86d7f5d3SJohn Marino 	{
219*86d7f5d3SJohn Marino 	    *acp = '\0';
220*86d7f5d3SJohn Marino 	    (void) sprintf (attic_file, "%s/%s/%s/%s%s", current_parsed_root->directory,
221*86d7f5d3SJohn Marino 			    mname, CVSATTIC, acp + 1, RCSEXT);
222*86d7f5d3SJohn Marino 	    *acp = '/';
223*86d7f5d3SJohn Marino 	}
224*86d7f5d3SJohn Marino 	else
225*86d7f5d3SJohn Marino 	    (void) sprintf (attic_file, "%s/%s/%s%s",
226*86d7f5d3SJohn Marino 	                    current_parsed_root->directory,
227*86d7f5d3SJohn Marino 			    CVSATTIC, mname, RCSEXT);
228*86d7f5d3SJohn Marino 
229*86d7f5d3SJohn Marino 	if (isdir (file))
230*86d7f5d3SJohn Marino 	{
231*86d7f5d3SJohn Marino 	    modargv = xmalloc (sizeof (*modargv));
232*86d7f5d3SJohn Marino 	    modargv[0] = xstrdup (mname);
233*86d7f5d3SJohn Marino 	    modargc = 1;
234*86d7f5d3SJohn Marino 	    is_found = 1;
235*86d7f5d3SJohn Marino 	}
236*86d7f5d3SJohn Marino 	else
237*86d7f5d3SJohn Marino 	{
238*86d7f5d3SJohn Marino 	    (void) strcat (file, RCSEXT);
239*86d7f5d3SJohn Marino 	    if (isfile (file) || isfile (attic_file))
240*86d7f5d3SJohn Marino 	    {
241*86d7f5d3SJohn Marino 		/* if mname was a file, we have to split it into "dir file" */
242*86d7f5d3SJohn Marino 		if ((cp = strrchr (mname, '/')) != NULL && cp != mname)
243*86d7f5d3SJohn Marino 		{
244*86d7f5d3SJohn Marino 		    modargv = xnmalloc (2, sizeof (*modargv));
245*86d7f5d3SJohn Marino 		    modargv[0] = xmalloc (strlen (mname) + 2);
246*86d7f5d3SJohn Marino 		    strncpy (modargv[0], mname, cp - mname);
247*86d7f5d3SJohn Marino 		    modargv[0][cp - mname] = '\0';
248*86d7f5d3SJohn Marino 		    modargv[1] = xstrdup (cp + 1);
249*86d7f5d3SJohn Marino 		    modargc = 2;
250*86d7f5d3SJohn Marino 		}
251*86d7f5d3SJohn Marino 		else
252*86d7f5d3SJohn Marino 		{
253*86d7f5d3SJohn Marino 		    /*
254*86d7f5d3SJohn Marino 		     * the only '/' at the beginning or no '/' at all
255*86d7f5d3SJohn Marino 		     * means the file we are interested in is in CVSROOT
256*86d7f5d3SJohn Marino 		     * itself so the directory should be '.'
257*86d7f5d3SJohn Marino 		     */
258*86d7f5d3SJohn Marino 		    if (cp == mname)
259*86d7f5d3SJohn Marino 		    {
260*86d7f5d3SJohn Marino 			/* drop the leading / if specified */
261*86d7f5d3SJohn Marino 			modargv = xnmalloc (2, sizeof (*modargv));
262*86d7f5d3SJohn Marino 			modargv[0] = xstrdup (".");
263*86d7f5d3SJohn Marino 			modargv[1] = xstrdup (mname + 1);
264*86d7f5d3SJohn Marino 			modargc = 2;
265*86d7f5d3SJohn Marino 		    }
266*86d7f5d3SJohn Marino 		    else
267*86d7f5d3SJohn Marino 		    {
268*86d7f5d3SJohn Marino 			/* otherwise just copy it */
269*86d7f5d3SJohn Marino 			modargv = xnmalloc (2, sizeof (*modargv));
270*86d7f5d3SJohn Marino 			modargv[0] = xstrdup (".");
271*86d7f5d3SJohn Marino 			modargv[1] = xstrdup (mname);
272*86d7f5d3SJohn Marino 			modargc = 2;
273*86d7f5d3SJohn Marino 		    }
274*86d7f5d3SJohn Marino 		}
275*86d7f5d3SJohn Marino 		is_found = 1;
276*86d7f5d3SJohn Marino 	    }
277*86d7f5d3SJohn Marino 	}
278*86d7f5d3SJohn Marino 	free (attic_file);
279*86d7f5d3SJohn Marino 	free (file);
280*86d7f5d3SJohn Marino 
281*86d7f5d3SJohn Marino 	if (is_found)
282*86d7f5d3SJohn Marino 	{
283*86d7f5d3SJohn Marino 	    assert (value == NULL);
284*86d7f5d3SJohn Marino 
285*86d7f5d3SJohn Marino 	    /* OK, we have now set up modargv with the actual
286*86d7f5d3SJohn Marino 	       file/directory we want to work on.  We duplicate a
287*86d7f5d3SJohn Marino 	       small amount of code here because the vast majority of
288*86d7f5d3SJohn Marino 	       the code after the "found" label does not pertain to
289*86d7f5d3SJohn Marino 	       the case where we found a file/directory rather than
290*86d7f5d3SJohn Marino 	       finding an entry in the modules file.  */
291*86d7f5d3SJohn Marino 	    if (save_cwd (&cwd))
292*86d7f5d3SJohn Marino 		error (1, errno, "Failed to save current directory.");
293*86d7f5d3SJohn Marino 	    cwd_saved = 1;
294*86d7f5d3SJohn Marino 
295*86d7f5d3SJohn Marino 	    err += callback_proc (modargc, modargv, where, mwhere, mfile,
296*86d7f5d3SJohn Marino 				  shorten,
297*86d7f5d3SJohn Marino 				  local_specified, mname, msg);
298*86d7f5d3SJohn Marino 
299*86d7f5d3SJohn Marino 	    free_names (&modargc, modargv);
300*86d7f5d3SJohn Marino 
301*86d7f5d3SJohn Marino 	    /* cd back to where we started.  */
302*86d7f5d3SJohn Marino 	    if (restore_cwd (&cwd))
303*86d7f5d3SJohn Marino 		error (1, errno, "Failed to restore current directory, `%s'.",
304*86d7f5d3SJohn Marino 		       cwd.name);
305*86d7f5d3SJohn Marino 	    free_cwd (&cwd);
306*86d7f5d3SJohn Marino 	    cwd_saved = 0;
307*86d7f5d3SJohn Marino 
308*86d7f5d3SJohn Marino 	    goto do_module_return;
309*86d7f5d3SJohn Marino 	}
310*86d7f5d3SJohn Marino     }
311*86d7f5d3SJohn Marino 
312*86d7f5d3SJohn Marino     /* look up everything to the first / as a module */
313*86d7f5d3SJohn Marino     if (mname[0] != '/' && (cp = strchr (mname, '/')) != NULL)
314*86d7f5d3SJohn Marino     {
315*86d7f5d3SJohn Marino 	/* Make the slash the new end of the string temporarily */
316*86d7f5d3SJohn Marino 	*cp = '\0';
317*86d7f5d3SJohn Marino 	key.dptr = mname;
318*86d7f5d3SJohn Marino 	key.dsize = strlen (key.dptr);
319*86d7f5d3SJohn Marino 
320*86d7f5d3SJohn Marino 	/* do the lookup */
321*86d7f5d3SJohn Marino 	if (db != NULL)
322*86d7f5d3SJohn Marino 	    val = dbm_fetch (db, key);
323*86d7f5d3SJohn Marino 	else
324*86d7f5d3SJohn Marino 	    val.dptr = NULL;
325*86d7f5d3SJohn Marino 
326*86d7f5d3SJohn Marino 	/* if we found it, clean up the value and life is good */
327*86d7f5d3SJohn Marino 	if (val.dptr != NULL)
328*86d7f5d3SJohn Marino 	{
329*86d7f5d3SJohn Marino 	    char *cp2;
330*86d7f5d3SJohn Marino 
331*86d7f5d3SJohn Marino 	    /* copy and null terminate the value */
332*86d7f5d3SJohn Marino 	    value = xmalloc (val.dsize + 1);
333*86d7f5d3SJohn Marino 	    memcpy (value, val.dptr, val.dsize);
334*86d7f5d3SJohn Marino 	    value[val.dsize] = '\0';
335*86d7f5d3SJohn Marino 
336*86d7f5d3SJohn Marino 	    /* If the line ends in a comment, strip it off */
337*86d7f5d3SJohn Marino 	    if ((cp2 = strchr (value, '#')) != NULL)
338*86d7f5d3SJohn Marino 		*cp2 = '\0';
339*86d7f5d3SJohn Marino 	    else
340*86d7f5d3SJohn Marino 		cp2 = value + val.dsize;
341*86d7f5d3SJohn Marino 
342*86d7f5d3SJohn Marino 	    /* Always strip trailing spaces */
343*86d7f5d3SJohn Marino 	    while (cp2 > value  &&  isspace ((unsigned char) *--cp2))
344*86d7f5d3SJohn Marino 		*cp2 = '\0';
345*86d7f5d3SJohn Marino 
346*86d7f5d3SJohn Marino 	    /* mwhere gets just the module name */
347*86d7f5d3SJohn Marino 	    mwhere = xstrdup (mname);
348*86d7f5d3SJohn Marino 	    mfile = cp + 1;
349*86d7f5d3SJohn Marino 	    assert (strlen (mfile));
350*86d7f5d3SJohn Marino 
351*86d7f5d3SJohn Marino 	    /* put the / back in mname */
352*86d7f5d3SJohn Marino 	    *cp = '/';
353*86d7f5d3SJohn Marino 
354*86d7f5d3SJohn Marino 	    goto found;
355*86d7f5d3SJohn Marino 	}
356*86d7f5d3SJohn Marino 
357*86d7f5d3SJohn Marino 	/* put the / back in mname */
358*86d7f5d3SJohn Marino 	*cp = '/';
359*86d7f5d3SJohn Marino     }
360*86d7f5d3SJohn Marino 
361*86d7f5d3SJohn Marino     /* if we got here, we couldn't find it using our search, so give up */
362*86d7f5d3SJohn Marino     error (0, 0, "cannot find module `%s' - ignored", mname);
363*86d7f5d3SJohn Marino     err++;
364*86d7f5d3SJohn Marino     goto do_module_return;
365*86d7f5d3SJohn Marino 
366*86d7f5d3SJohn Marino 
367*86d7f5d3SJohn Marino     /*
368*86d7f5d3SJohn Marino      * At this point, we found what we were looking for in one
369*86d7f5d3SJohn Marino      * of the many different forms.
370*86d7f5d3SJohn Marino      */
371*86d7f5d3SJohn Marino   found:
372*86d7f5d3SJohn Marino 
373*86d7f5d3SJohn Marino     /* remember where we start */
374*86d7f5d3SJohn Marino     if (save_cwd (&cwd))
375*86d7f5d3SJohn Marino 	error (1, errno, "Failed to save current directory.");
376*86d7f5d3SJohn Marino     cwd_saved = 1;
377*86d7f5d3SJohn Marino 
378*86d7f5d3SJohn Marino     assert (value != NULL);
379*86d7f5d3SJohn Marino 
380*86d7f5d3SJohn Marino     /* search the value for the special delimiter and save for later */
381*86d7f5d3SJohn Marino     if ((cp = strchr (value, CVSMODULE_SPEC)) != NULL)
382*86d7f5d3SJohn Marino     {
383*86d7f5d3SJohn Marino 	*cp = '\0';			/* null out the special char */
384*86d7f5d3SJohn Marino 	spec_opt = cp + 1;		/* save the options for later */
385*86d7f5d3SJohn Marino 
386*86d7f5d3SJohn Marino 	/* strip whitespace if necessary */
387*86d7f5d3SJohn Marino 	while (cp > value  &&  isspace ((unsigned char) *--cp))
388*86d7f5d3SJohn Marino 	    *cp = '\0';
389*86d7f5d3SJohn Marino     }
390*86d7f5d3SJohn Marino 
391*86d7f5d3SJohn Marino     /* don't do special options only part of a module was specified */
392*86d7f5d3SJohn Marino     if (mfile != NULL)
393*86d7f5d3SJohn Marino 	spec_opt = NULL;
394*86d7f5d3SJohn Marino 
395*86d7f5d3SJohn Marino     /*
396*86d7f5d3SJohn Marino      * value now contains one of the following:
397*86d7f5d3SJohn Marino      *    1) dir
398*86d7f5d3SJohn Marino      *	  2) dir file
399*86d7f5d3SJohn Marino      *    3) the value from modules without any special args
400*86d7f5d3SJohn Marino      *		    [ args ] dir [file] [file] ...
401*86d7f5d3SJohn Marino      *	     or     -a module [ module ] ...
402*86d7f5d3SJohn Marino      */
403*86d7f5d3SJohn Marino 
404*86d7f5d3SJohn Marino     /* Put the value on a line with XXX prepended for getopt to eat */
405*86d7f5d3SJohn Marino     line = Xasprintf ("XXX %s", value);
406*86d7f5d3SJohn Marino 
407*86d7f5d3SJohn Marino     /* turn the line into an argv[] array */
408*86d7f5d3SJohn Marino     line2argv (&xmodargc, &xmodargv, line, " \t");
409*86d7f5d3SJohn Marino     free (line);
410*86d7f5d3SJohn Marino     modargc = xmodargc;
411*86d7f5d3SJohn Marino     modargv = xmodargv;
412*86d7f5d3SJohn Marino 
413*86d7f5d3SJohn Marino     /* parse the args */
414*86d7f5d3SJohn Marino     optind = 0;
415*86d7f5d3SJohn Marino     while ((c = getopt (modargc, modargv, CVSMODULE_OPTS)) != -1)
416*86d7f5d3SJohn Marino     {
417*86d7f5d3SJohn Marino 	switch (c)
418*86d7f5d3SJohn Marino 	{
419*86d7f5d3SJohn Marino 	    case 'a':
420*86d7f5d3SJohn Marino 		alias = 1;
421*86d7f5d3SJohn Marino 		break;
422*86d7f5d3SJohn Marino 	    case 'd':
423*86d7f5d3SJohn Marino 		if (mwhere)
424*86d7f5d3SJohn Marino 		    free (mwhere);
425*86d7f5d3SJohn Marino 		mwhere = xstrdup (optarg);
426*86d7f5d3SJohn Marino 		nonalias_opt = 1;
427*86d7f5d3SJohn Marino 		break;
428*86d7f5d3SJohn Marino 	    case 'l':
429*86d7f5d3SJohn Marino 		local_specified = 1;
430*86d7f5d3SJohn Marino 		nonalias_opt = 1;
431*86d7f5d3SJohn Marino 		break;
432*86d7f5d3SJohn Marino 	    case 'o':
433*86d7f5d3SJohn Marino 		if (checkout_prog)
434*86d7f5d3SJohn Marino 		    free (checkout_prog);
435*86d7f5d3SJohn Marino 		checkout_prog = xstrdup (optarg);
436*86d7f5d3SJohn Marino 		nonalias_opt = 1;
437*86d7f5d3SJohn Marino 		break;
438*86d7f5d3SJohn Marino 	    case 'e':
439*86d7f5d3SJohn Marino 		if (export_prog)
440*86d7f5d3SJohn Marino 		    free (export_prog);
441*86d7f5d3SJohn Marino 		export_prog = xstrdup (optarg);
442*86d7f5d3SJohn Marino 		nonalias_opt = 1;
443*86d7f5d3SJohn Marino 		break;
444*86d7f5d3SJohn Marino 	    case 't':
445*86d7f5d3SJohn Marino 		if (tag_prog)
446*86d7f5d3SJohn Marino 		    free (tag_prog);
447*86d7f5d3SJohn Marino 		tag_prog = xstrdup (optarg);
448*86d7f5d3SJohn Marino 		nonalias_opt = 1;
449*86d7f5d3SJohn Marino 		break;
450*86d7f5d3SJohn Marino 	    case '?':
451*86d7f5d3SJohn Marino 		error (0, 0,
452*86d7f5d3SJohn Marino 		       "modules file has invalid option for key %s value %s",
453*86d7f5d3SJohn Marino 		       key.dptr, value);
454*86d7f5d3SJohn Marino 		err++;
455*86d7f5d3SJohn Marino 		goto do_module_return;
456*86d7f5d3SJohn Marino 	}
457*86d7f5d3SJohn Marino     }
458*86d7f5d3SJohn Marino     modargc -= optind;
459*86d7f5d3SJohn Marino     modargv += optind;
460*86d7f5d3SJohn Marino     if (modargc == 0  &&  spec_opt == NULL)
461*86d7f5d3SJohn Marino     {
462*86d7f5d3SJohn Marino 	error (0, 0, "modules file missing directory for module %s", mname);
463*86d7f5d3SJohn Marino 	++err;
464*86d7f5d3SJohn Marino 	goto do_module_return;
465*86d7f5d3SJohn Marino     }
466*86d7f5d3SJohn Marino 
467*86d7f5d3SJohn Marino     if (alias && nonalias_opt)
468*86d7f5d3SJohn Marino     {
469*86d7f5d3SJohn Marino 	/* The documentation has never said it is valid to specify
470*86d7f5d3SJohn Marino 	   -a along with another option.  And I believe that in the past
471*86d7f5d3SJohn Marino 	   CVS has ignored the options other than -a, more or less, in this
472*86d7f5d3SJohn Marino 	   situation.  */
473*86d7f5d3SJohn Marino 	error (0, 0, "\
474*86d7f5d3SJohn Marino -a cannot be specified in the modules file along with other options");
475*86d7f5d3SJohn Marino 	++err;
476*86d7f5d3SJohn Marino 	goto do_module_return;
477*86d7f5d3SJohn Marino     }
478*86d7f5d3SJohn Marino 
479*86d7f5d3SJohn Marino     /* if this was an alias, call ourselves recursively for each module */
480*86d7f5d3SJohn Marino     if (alias)
481*86d7f5d3SJohn Marino     {
482*86d7f5d3SJohn Marino 	int i;
483*86d7f5d3SJohn Marino 
484*86d7f5d3SJohn Marino 	for (i = 0; i < modargc; i++)
485*86d7f5d3SJohn Marino 	{
486*86d7f5d3SJohn Marino 	    /*
487*86d7f5d3SJohn Marino 	     * Recursion check: if an alias module calls itself or a module
488*86d7f5d3SJohn Marino 	     * which causes the first to be called again, print an error
489*86d7f5d3SJohn Marino 	     * message and stop recursing.
490*86d7f5d3SJohn Marino 	     *
491*86d7f5d3SJohn Marino 	     * Algorithm:
492*86d7f5d3SJohn Marino 	     *
493*86d7f5d3SJohn Marino 	     *   1. Check that MNAME isn't in the stack.
494*86d7f5d3SJohn Marino 	     *   2. Push MNAME onto the stack.
495*86d7f5d3SJohn Marino 	     *   3. Call do_module().
496*86d7f5d3SJohn Marino 	     *   4. Pop MNAME from the stack.
497*86d7f5d3SJohn Marino 	     */
498*86d7f5d3SJohn Marino 	    if (stack && findnode (stack, mname))
499*86d7f5d3SJohn Marino 		error (0, 0,
500*86d7f5d3SJohn Marino 		       "module `%s' in modules file contains infinite loop",
501*86d7f5d3SJohn Marino 		       mname);
502*86d7f5d3SJohn Marino 	    else
503*86d7f5d3SJohn Marino 	    {
504*86d7f5d3SJohn Marino 		if (!stack) stack = getlist();
505*86d7f5d3SJohn Marino 		push_string (stack, mname);
506*86d7f5d3SJohn Marino 		err += my_module (db, modargv[i], m_type, msg, callback_proc,
507*86d7f5d3SJohn Marino                                    where, shorten, local_specified,
508*86d7f5d3SJohn Marino                                    run_module_prog, build_dirs, extra_arg,
509*86d7f5d3SJohn Marino                                    stack);
510*86d7f5d3SJohn Marino 		pop_string (stack);
511*86d7f5d3SJohn Marino 		if (isempty (stack)) dellist (&stack);
512*86d7f5d3SJohn Marino 	    }
513*86d7f5d3SJohn Marino 	}
514*86d7f5d3SJohn Marino 	goto do_module_return;
515*86d7f5d3SJohn Marino     }
516*86d7f5d3SJohn Marino 
517*86d7f5d3SJohn Marino     if (mfile != NULL && modargc > 1)
518*86d7f5d3SJohn Marino     {
519*86d7f5d3SJohn Marino 	error (0, 0, "\
520*86d7f5d3SJohn Marino module `%s' is a request for a file in a module which is not a directory",
521*86d7f5d3SJohn Marino 	       mname);
522*86d7f5d3SJohn Marino 	++err;
523*86d7f5d3SJohn Marino 	goto do_module_return;
524*86d7f5d3SJohn Marino     }
525*86d7f5d3SJohn Marino 
526*86d7f5d3SJohn Marino     /* otherwise, process this module */
527*86d7f5d3SJohn Marino     if (modargc > 0)
528*86d7f5d3SJohn Marino     {
529*86d7f5d3SJohn Marino 	err += callback_proc (modargc, modargv, where, mwhere, mfile, shorten,
530*86d7f5d3SJohn Marino 			      local_specified, mname, msg);
531*86d7f5d3SJohn Marino     }
532*86d7f5d3SJohn Marino     else
533*86d7f5d3SJohn Marino     {
534*86d7f5d3SJohn Marino 	/*
535*86d7f5d3SJohn Marino 	 * we had nothing but special options, so we must
536*86d7f5d3SJohn Marino 	 * make the appropriate directory and cd to it
537*86d7f5d3SJohn Marino 	 */
538*86d7f5d3SJohn Marino 	char *dir;
539*86d7f5d3SJohn Marino 
540*86d7f5d3SJohn Marino 	if (!build_dirs)
541*86d7f5d3SJohn Marino 	    goto do_special;
542*86d7f5d3SJohn Marino 
543*86d7f5d3SJohn Marino 	dir = where ? where : (mwhere ? mwhere : mname);
544*86d7f5d3SJohn Marino 	/* XXX - think about making null repositories at each dir here
545*86d7f5d3SJohn Marino 		 instead of just at the bottom */
546*86d7f5d3SJohn Marino 	make_directories (dir);
547*86d7f5d3SJohn Marino 	if (CVS_CHDIR (dir) < 0)
548*86d7f5d3SJohn Marino 	{
549*86d7f5d3SJohn Marino 	    error (0, errno, "cannot chdir to %s", dir);
550*86d7f5d3SJohn Marino 	    spec_opt = NULL;
551*86d7f5d3SJohn Marino 	    err++;
552*86d7f5d3SJohn Marino 	    goto do_special;
553*86d7f5d3SJohn Marino 	}
554*86d7f5d3SJohn Marino 	if (!isfile (CVSADM))
555*86d7f5d3SJohn Marino 	{
556*86d7f5d3SJohn Marino 	    char *nullrepos;
557*86d7f5d3SJohn Marino 
558*86d7f5d3SJohn Marino 	    nullrepos = emptydir_name ();
559*86d7f5d3SJohn Marino 
560*86d7f5d3SJohn Marino 	    Create_Admin (".", dir, nullrepos, NULL, NULL, 0, 0, 1);
561*86d7f5d3SJohn Marino 	    if (!noexec)
562*86d7f5d3SJohn Marino 	    {
563*86d7f5d3SJohn Marino 		FILE *fp;
564*86d7f5d3SJohn Marino 
565*86d7f5d3SJohn Marino 		fp = xfopen (CVSADM_ENTSTAT, "w+");
566*86d7f5d3SJohn Marino 		if (fclose (fp) == EOF)
567*86d7f5d3SJohn Marino 		    error (1, errno, "cannot close %s", CVSADM_ENTSTAT);
568*86d7f5d3SJohn Marino #ifdef SERVER_SUPPORT
569*86d7f5d3SJohn Marino 		if (server_active)
570*86d7f5d3SJohn Marino 		    server_set_entstat (dir, nullrepos);
571*86d7f5d3SJohn Marino #endif
572*86d7f5d3SJohn Marino 	    }
573*86d7f5d3SJohn Marino 	    free (nullrepos);
574*86d7f5d3SJohn Marino 	}
575*86d7f5d3SJohn Marino     }
576*86d7f5d3SJohn Marino 
577*86d7f5d3SJohn Marino     /* if there were special include args, process them now */
578*86d7f5d3SJohn Marino 
579*86d7f5d3SJohn Marino   do_special:
580*86d7f5d3SJohn Marino 
581*86d7f5d3SJohn Marino     free_names (&xmodargc, xmodargv);
582*86d7f5d3SJohn Marino     xmodargv = NULL;
583*86d7f5d3SJohn Marino 
584*86d7f5d3SJohn Marino     /* blow off special options if -l was specified */
585*86d7f5d3SJohn Marino     if (local_specified)
586*86d7f5d3SJohn Marino 	spec_opt = NULL;
587*86d7f5d3SJohn Marino 
588*86d7f5d3SJohn Marino #ifdef SERVER_SUPPORT
589*86d7f5d3SJohn Marino     /* We want to check out into the directory named by the module.
590*86d7f5d3SJohn Marino        So we set a global variable which tells the server to glom that
591*86d7f5d3SJohn Marino        directory name onto the front.  A cleaner approach would be some
592*86d7f5d3SJohn Marino        way of passing it down to the recursive call, through the
593*86d7f5d3SJohn Marino        callback_proc, to start_recursion, and then into the update_dir in
594*86d7f5d3SJohn Marino        the struct file_info.  That way the "Updating foo" message could
595*86d7f5d3SJohn Marino        print the actual directory we are checking out into.
596*86d7f5d3SJohn Marino 
597*86d7f5d3SJohn Marino        For local CVS, this is handled by the chdir call above
598*86d7f5d3SJohn Marino        (directly or via the callback_proc).  */
599*86d7f5d3SJohn Marino     if (server_active && spec_opt != NULL)
600*86d7f5d3SJohn Marino     {
601*86d7f5d3SJohn Marino 	char *change_to;
602*86d7f5d3SJohn Marino 
603*86d7f5d3SJohn Marino 	change_to = where ? where : (mwhere ? mwhere : mname);
604*86d7f5d3SJohn Marino 	server_dir_to_restore = server_dir;
605*86d7f5d3SJohn Marino 	restore_server_dir = 1;
606*86d7f5d3SJohn Marino 	if (server_dir_to_restore != NULL)
607*86d7f5d3SJohn Marino 	    server_dir = Xasprintf ("%s/%s", server_dir_to_restore, change_to);
608*86d7f5d3SJohn Marino 	else
609*86d7f5d3SJohn Marino 	    server_dir = xstrdup (change_to);
610*86d7f5d3SJohn Marino     }
611*86d7f5d3SJohn Marino #endif
612*86d7f5d3SJohn Marino 
613*86d7f5d3SJohn Marino     while (spec_opt != NULL)
614*86d7f5d3SJohn Marino     {
615*86d7f5d3SJohn Marino 	char *next_opt;
616*86d7f5d3SJohn Marino 
617*86d7f5d3SJohn Marino 	cp = strchr (spec_opt, CVSMODULE_SPEC);
618*86d7f5d3SJohn Marino 	if (cp != NULL)
619*86d7f5d3SJohn Marino 	{
620*86d7f5d3SJohn Marino 	    /* save the beginning of the next arg */
621*86d7f5d3SJohn Marino 	    next_opt = cp + 1;
622*86d7f5d3SJohn Marino 
623*86d7f5d3SJohn Marino 	    /* strip whitespace off the end */
624*86d7f5d3SJohn Marino 	    do
625*86d7f5d3SJohn Marino 		*cp = '\0';
626*86d7f5d3SJohn Marino 	    while (cp > spec_opt  &&  isspace ((unsigned char) *--cp));
627*86d7f5d3SJohn Marino 	}
628*86d7f5d3SJohn Marino 	else
629*86d7f5d3SJohn Marino 	    next_opt = NULL;
630*86d7f5d3SJohn Marino 
631*86d7f5d3SJohn Marino 	/* strip whitespace from front */
632*86d7f5d3SJohn Marino 	while (isspace ((unsigned char) *spec_opt))
633*86d7f5d3SJohn Marino 	    spec_opt++;
634*86d7f5d3SJohn Marino 
635*86d7f5d3SJohn Marino 	if (*spec_opt == '\0')
636*86d7f5d3SJohn Marino 	    error (0, 0, "Mal-formed %c option for module %s - ignored",
637*86d7f5d3SJohn Marino 		   CVSMODULE_SPEC, mname);
638*86d7f5d3SJohn Marino 	else
639*86d7f5d3SJohn Marino 	    err += my_module (db, spec_opt, m_type, msg, callback_proc,
640*86d7f5d3SJohn Marino 			      NULL, 0, local_specified, run_module_prog,
641*86d7f5d3SJohn Marino 			      build_dirs, extra_arg, stack);
642*86d7f5d3SJohn Marino 	spec_opt = next_opt;
643*86d7f5d3SJohn Marino     }
644*86d7f5d3SJohn Marino 
645*86d7f5d3SJohn Marino #ifdef SERVER_SUPPORT
646*86d7f5d3SJohn Marino     if (server_active && restore_server_dir)
647*86d7f5d3SJohn Marino     {
648*86d7f5d3SJohn Marino 	free (server_dir);
649*86d7f5d3SJohn Marino 	server_dir = server_dir_to_restore;
650*86d7f5d3SJohn Marino     }
651*86d7f5d3SJohn Marino #endif
652*86d7f5d3SJohn Marino 
653*86d7f5d3SJohn Marino     /* cd back to where we started */
654*86d7f5d3SJohn Marino     if (restore_cwd (&cwd))
655*86d7f5d3SJohn Marino 	error (1, errno, "Failed to restore current directory, `%s'.",
656*86d7f5d3SJohn Marino 	       cwd.name);
657*86d7f5d3SJohn Marino     free_cwd (&cwd);
658*86d7f5d3SJohn Marino     cwd_saved = 0;
659*86d7f5d3SJohn Marino 
660*86d7f5d3SJohn Marino     /* run checkout or tag prog if appropriate */
661*86d7f5d3SJohn Marino     if (err == 0 && run_module_prog)
662*86d7f5d3SJohn Marino     {
663*86d7f5d3SJohn Marino 	if ((m_type == TAG && tag_prog != NULL) ||
664*86d7f5d3SJohn Marino 	    (m_type == CHECKOUT && checkout_prog != NULL) ||
665*86d7f5d3SJohn Marino 	    (m_type == EXPORT && export_prog != NULL))
666*86d7f5d3SJohn Marino 	{
667*86d7f5d3SJohn Marino 	    /*
668*86d7f5d3SJohn Marino 	     * If a relative pathname is specified as the checkout, tag
669*86d7f5d3SJohn Marino 	     * or export proc, try to tack on the current "where" value.
670*86d7f5d3SJohn Marino 	     * if we can't find a matching program, just punt and use
671*86d7f5d3SJohn Marino 	     * whatever is specified in the modules file.
672*86d7f5d3SJohn Marino 	     */
673*86d7f5d3SJohn Marino 	    char *real_prog = NULL;
674*86d7f5d3SJohn Marino 	    char *prog = (m_type == TAG ? tag_prog :
675*86d7f5d3SJohn Marino 			  (m_type == CHECKOUT ? checkout_prog : export_prog));
676*86d7f5d3SJohn Marino 	    char *real_where = (where != NULL ? where : mwhere);
677*86d7f5d3SJohn Marino 	    char *expanded_path;
678*86d7f5d3SJohn Marino 
679*86d7f5d3SJohn Marino 	    if ((*prog != '/') && (*prog != '.'))
680*86d7f5d3SJohn Marino 	    {
681*86d7f5d3SJohn Marino 		real_prog = Xasprintf ("%s/%s", real_where, prog);
682*86d7f5d3SJohn Marino 		if (isfile (real_prog))
683*86d7f5d3SJohn Marino 		    prog = real_prog;
684*86d7f5d3SJohn Marino 	    }
685*86d7f5d3SJohn Marino 
686*86d7f5d3SJohn Marino 	    /* XXX can we determine the line number for this entry??? */
687*86d7f5d3SJohn Marino 	    expanded_path = expand_path (prog, current_parsed_root->directory,
688*86d7f5d3SJohn Marino 					 false, "modules", 0);
689*86d7f5d3SJohn Marino 	    if (expanded_path != NULL)
690*86d7f5d3SJohn Marino 	    {
691*86d7f5d3SJohn Marino 		run_setup (expanded_path);
692*86d7f5d3SJohn Marino 		run_add_arg (real_where);
693*86d7f5d3SJohn Marino 
694*86d7f5d3SJohn Marino 		if (extra_arg)
695*86d7f5d3SJohn Marino 		    run_add_arg (extra_arg);
696*86d7f5d3SJohn Marino 
697*86d7f5d3SJohn Marino 		if (!quiet)
698*86d7f5d3SJohn Marino 		{
699*86d7f5d3SJohn Marino 		    cvs_output (program_name, 0);
700*86d7f5d3SJohn Marino 		    cvs_output (" ", 1);
701*86d7f5d3SJohn Marino 		    cvs_output (cvs_cmd_name, 0);
702*86d7f5d3SJohn Marino 		    cvs_output (": Executing '", 0);
703*86d7f5d3SJohn Marino 		    run_print (stdout);
704*86d7f5d3SJohn Marino 		    cvs_output ("'\n", 0);
705*86d7f5d3SJohn Marino 		    cvs_flushout ();
706*86d7f5d3SJohn Marino 		}
707*86d7f5d3SJohn Marino 		err += run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
708*86d7f5d3SJohn Marino 		free (expanded_path);
709*86d7f5d3SJohn Marino 	    }
710*86d7f5d3SJohn Marino 	    if (real_prog) free (real_prog);
711*86d7f5d3SJohn Marino 	}
712*86d7f5d3SJohn Marino     }
713*86d7f5d3SJohn Marino 
714*86d7f5d3SJohn Marino  do_module_return:
715*86d7f5d3SJohn Marino     /* clean up */
716*86d7f5d3SJohn Marino     if (xmodargv != NULL)
717*86d7f5d3SJohn Marino 	free_names (&xmodargc, xmodargv);
718*86d7f5d3SJohn Marino     if (mwhere)
719*86d7f5d3SJohn Marino 	free (mwhere);
720*86d7f5d3SJohn Marino     if (checkout_prog)
721*86d7f5d3SJohn Marino 	free (checkout_prog);
722*86d7f5d3SJohn Marino     if (export_prog)
723*86d7f5d3SJohn Marino 	free (export_prog);
724*86d7f5d3SJohn Marino     if (tag_prog)
725*86d7f5d3SJohn Marino 	free (tag_prog);
726*86d7f5d3SJohn Marino     if (cwd_saved)
727*86d7f5d3SJohn Marino 	free_cwd (&cwd);
728*86d7f5d3SJohn Marino     if (value != NULL)
729*86d7f5d3SJohn Marino 	free (value);
730*86d7f5d3SJohn Marino 
731*86d7f5d3SJohn Marino     if (xvalue != NULL)
732*86d7f5d3SJohn Marino 	free (xvalue);
733*86d7f5d3SJohn Marino     return (err);
734*86d7f5d3SJohn Marino }
735*86d7f5d3SJohn Marino 
736*86d7f5d3SJohn Marino 
737*86d7f5d3SJohn Marino 
738*86d7f5d3SJohn Marino /* External face of do_module so that we can have an internal version which
739*86d7f5d3SJohn Marino  * accepts a stack argument to track alias recursion.
740*86d7f5d3SJohn Marino  */
741*86d7f5d3SJohn Marino int
do_module(DBM * db,char * mname,enum mtype m_type,char * msg,CALLBACKPROC callback_proc,char * where,int shorten,int local_specified,int run_module_prog,int build_dirs,char * extra_arg)742*86d7f5d3SJohn Marino do_module (DBM *db, char *mname, enum mtype m_type, char *msg,
743*86d7f5d3SJohn Marino            CALLBACKPROC callback_proc, char *where, int shorten,
744*86d7f5d3SJohn Marino            int local_specified, int run_module_prog, int build_dirs,
745*86d7f5d3SJohn Marino            char *extra_arg)
746*86d7f5d3SJohn Marino {
747*86d7f5d3SJohn Marino     return my_module (db, mname, m_type, msg, callback_proc, where, shorten,
748*86d7f5d3SJohn Marino                        local_specified, run_module_prog, build_dirs, extra_arg,
749*86d7f5d3SJohn Marino                        NULL);
750*86d7f5d3SJohn Marino }
751*86d7f5d3SJohn Marino 
752*86d7f5d3SJohn Marino 
753*86d7f5d3SJohn Marino 
754*86d7f5d3SJohn Marino /* - Read all the records from the modules database into an array.
755*86d7f5d3SJohn Marino    - Sort the array depending on what format is desired.
756*86d7f5d3SJohn Marino    - Print the array in the format desired.
757*86d7f5d3SJohn Marino 
758*86d7f5d3SJohn Marino    Currently, there are only two "desires":
759*86d7f5d3SJohn Marino 
760*86d7f5d3SJohn Marino    1. Sort by module name and format the whole entry including switches,
761*86d7f5d3SJohn Marino       files and the comment field: (Including aliases)
762*86d7f5d3SJohn Marino 
763*86d7f5d3SJohn Marino       modulename	-s switches, one per line, even if
764*86d7f5d3SJohn Marino 			it has many switches.
765*86d7f5d3SJohn Marino 			Directories and files involved, formatted
766*86d7f5d3SJohn Marino 			to cover multiple lines if necessary.
767*86d7f5d3SJohn Marino 			# Comment, also formatted to cover multiple
768*86d7f5d3SJohn Marino 			# lines if necessary.
769*86d7f5d3SJohn Marino 
770*86d7f5d3SJohn Marino    2. Sort by status field string and print:  (*not* including aliases)
771*86d7f5d3SJohn Marino 
772*86d7f5d3SJohn Marino       modulename    STATUS	Directories and files involved, formatted
773*86d7f5d3SJohn Marino 				to cover multiple lines if necessary.
774*86d7f5d3SJohn Marino 				# Comment, also formatted to cover multiple
775*86d7f5d3SJohn Marino 				# lines if necessary.
776*86d7f5d3SJohn Marino */
777*86d7f5d3SJohn Marino 
778*86d7f5d3SJohn Marino static struct sortrec *s_head;
779*86d7f5d3SJohn Marino 
780*86d7f5d3SJohn Marino static int s_max = 0;			/* Number of elements allocated */
781*86d7f5d3SJohn Marino static int s_count = 0;			/* Number of elements used */
782*86d7f5d3SJohn Marino 
783*86d7f5d3SJohn Marino static int Status;		        /* Nonzero if the user is
784*86d7f5d3SJohn Marino 					   interested in status
785*86d7f5d3SJohn Marino 					   information as well as
786*86d7f5d3SJohn Marino 					   module name */
787*86d7f5d3SJohn Marino static char def_status[] = "NONE";
788*86d7f5d3SJohn Marino 
789*86d7f5d3SJohn Marino /* Sort routine for qsort:
790*86d7f5d3SJohn Marino    - If we want the "Status" field to be sorted, check it first.
791*86d7f5d3SJohn Marino    - Then compare the "module name" fields.  Since they are unique, we don't
792*86d7f5d3SJohn Marino      have to look further.
793*86d7f5d3SJohn Marino */
794*86d7f5d3SJohn Marino static int
sort_order(const void * l,const void * r)795*86d7f5d3SJohn Marino sort_order (const void *l, const void *r)
796*86d7f5d3SJohn Marino {
797*86d7f5d3SJohn Marino     int i;
798*86d7f5d3SJohn Marino     const struct sortrec *left = (const struct sortrec *) l;
799*86d7f5d3SJohn Marino     const struct sortrec *right = (const struct sortrec *) r;
800*86d7f5d3SJohn Marino 
801*86d7f5d3SJohn Marino     if (Status)
802*86d7f5d3SJohn Marino     {
803*86d7f5d3SJohn Marino 	/* If Sort by status field, compare them. */
804*86d7f5d3SJohn Marino 	if ((i = strcmp (left->status, right->status)) != 0)
805*86d7f5d3SJohn Marino 	    return (i);
806*86d7f5d3SJohn Marino     }
807*86d7f5d3SJohn Marino     return (strcmp (left->modname, right->modname));
808*86d7f5d3SJohn Marino }
809*86d7f5d3SJohn Marino 
810*86d7f5d3SJohn Marino static void
save_d(char * k,int ks,char * d,int ds)811*86d7f5d3SJohn Marino save_d (char *k, int ks, char *d, int ds)
812*86d7f5d3SJohn Marino {
813*86d7f5d3SJohn Marino     char *cp, *cp2;
814*86d7f5d3SJohn Marino     struct sortrec *s_rec;
815*86d7f5d3SJohn Marino 
816*86d7f5d3SJohn Marino     if (Status && *d == '-' && *(d + 1) == 'a')
817*86d7f5d3SJohn Marino 	return;				/* We want "cvs co -s" and it is an alias! */
818*86d7f5d3SJohn Marino 
819*86d7f5d3SJohn Marino     if (s_count == s_max)
820*86d7f5d3SJohn Marino     {
821*86d7f5d3SJohn Marino 	s_max += 64;
822*86d7f5d3SJohn Marino 	s_head = xnrealloc (s_head, s_max, sizeof (*s_head));
823*86d7f5d3SJohn Marino     }
824*86d7f5d3SJohn Marino     s_rec = &s_head[s_count];
825*86d7f5d3SJohn Marino     s_rec->modname = cp = xmalloc (ks + 1);
826*86d7f5d3SJohn Marino     (void) strncpy (cp, k, ks);
827*86d7f5d3SJohn Marino     *(cp + ks) = '\0';
828*86d7f5d3SJohn Marino 
829*86d7f5d3SJohn Marino     s_rec->rest = cp2 = xmalloc (ds + 1);
830*86d7f5d3SJohn Marino     cp = d;
831*86d7f5d3SJohn Marino     *(cp + ds) = '\0';	/* Assumes an extra byte at end of static dbm buffer */
832*86d7f5d3SJohn Marino 
833*86d7f5d3SJohn Marino     while (isspace ((unsigned char) *cp))
834*86d7f5d3SJohn Marino 	cp++;
835*86d7f5d3SJohn Marino     /* Turn <spaces> into one ' ' -- makes the rest of this routine simpler */
836*86d7f5d3SJohn Marino     while (*cp)
837*86d7f5d3SJohn Marino     {
838*86d7f5d3SJohn Marino 	if (isspace ((unsigned char) *cp))
839*86d7f5d3SJohn Marino 	{
840*86d7f5d3SJohn Marino 	    *cp2++ = ' ';
841*86d7f5d3SJohn Marino 	    while (isspace ((unsigned char) *cp))
842*86d7f5d3SJohn Marino 		cp++;
843*86d7f5d3SJohn Marino 	}
844*86d7f5d3SJohn Marino 	else
845*86d7f5d3SJohn Marino 	    *cp2++ = *cp++;
846*86d7f5d3SJohn Marino     }
847*86d7f5d3SJohn Marino     *cp2 = '\0';
848*86d7f5d3SJohn Marino 
849*86d7f5d3SJohn Marino     /* Look for the "-s statusvalue" text */
850*86d7f5d3SJohn Marino     if (Status)
851*86d7f5d3SJohn Marino     {
852*86d7f5d3SJohn Marino 	s_rec->status = def_status;
853*86d7f5d3SJohn Marino 
854*86d7f5d3SJohn Marino 	for (cp = s_rec->rest; (cp2 = strchr (cp, '-')) != NULL; cp = ++cp2)
855*86d7f5d3SJohn Marino 	{
856*86d7f5d3SJohn Marino 	    if (*(cp2 + 1) == 's' && *(cp2 + 2) == ' ')
857*86d7f5d3SJohn Marino 	    {
858*86d7f5d3SJohn Marino 		char *status_start;
859*86d7f5d3SJohn Marino 
860*86d7f5d3SJohn Marino 		cp2 += 3;
861*86d7f5d3SJohn Marino 		status_start = cp2;
862*86d7f5d3SJohn Marino 		while (*cp2 != ' ' && *cp2 != '\0')
863*86d7f5d3SJohn Marino 		    cp2++;
864*86d7f5d3SJohn Marino 		s_rec->status = xmalloc (cp2 - status_start + 1);
865*86d7f5d3SJohn Marino 		strncpy (s_rec->status, status_start, cp2 - status_start);
866*86d7f5d3SJohn Marino 		s_rec->status[cp2 - status_start] = '\0';
867*86d7f5d3SJohn Marino 		cp = cp2;
868*86d7f5d3SJohn Marino 		break;
869*86d7f5d3SJohn Marino 	    }
870*86d7f5d3SJohn Marino 	}
871*86d7f5d3SJohn Marino     }
872*86d7f5d3SJohn Marino     else
873*86d7f5d3SJohn Marino 	cp = s_rec->rest;
874*86d7f5d3SJohn Marino 
875*86d7f5d3SJohn Marino     /* Find comment field, clean up on all three sides & compress blanks */
876*86d7f5d3SJohn Marino     if ((cp2 = cp = strchr (cp, '#')) != NULL)
877*86d7f5d3SJohn Marino     {
878*86d7f5d3SJohn Marino 	if (*--cp2 == ' ')
879*86d7f5d3SJohn Marino 	    *cp2 = '\0';
880*86d7f5d3SJohn Marino 	if (*++cp == ' ')
881*86d7f5d3SJohn Marino 	    cp++;
882*86d7f5d3SJohn Marino 	s_rec->comment = cp;
883*86d7f5d3SJohn Marino     }
884*86d7f5d3SJohn Marino     else
885*86d7f5d3SJohn Marino 	s_rec->comment = "";
886*86d7f5d3SJohn Marino 
887*86d7f5d3SJohn Marino     s_count++;
888*86d7f5d3SJohn Marino }
889*86d7f5d3SJohn Marino 
890*86d7f5d3SJohn Marino /* Print out the module database as we know it.  If STATUS is
891*86d7f5d3SJohn Marino    non-zero, print out status information for each module. */
892*86d7f5d3SJohn Marino 
893*86d7f5d3SJohn Marino void
cat_module(int status)894*86d7f5d3SJohn Marino cat_module (int status)
895*86d7f5d3SJohn Marino {
896*86d7f5d3SJohn Marino     DBM *db;
897*86d7f5d3SJohn Marino     datum key, val;
898*86d7f5d3SJohn Marino     int i, c, wid, argc, cols = 80, indent, fill;
899*86d7f5d3SJohn Marino     int moduleargc;
900*86d7f5d3SJohn Marino     struct sortrec *s_h;
901*86d7f5d3SJohn Marino     char *cp, *cp2, **argv;
902*86d7f5d3SJohn Marino     char **moduleargv;
903*86d7f5d3SJohn Marino 
904*86d7f5d3SJohn Marino     Status = status;
905*86d7f5d3SJohn Marino 
906*86d7f5d3SJohn Marino     /* Read the whole modules file into allocated records */
907*86d7f5d3SJohn Marino     if (!(db = open_module ()))
908*86d7f5d3SJohn Marino 	error (1, 0, "failed to open the modules file");
909*86d7f5d3SJohn Marino 
910*86d7f5d3SJohn Marino     for (key = dbm_firstkey (db); key.dptr != NULL; key = dbm_nextkey (db))
911*86d7f5d3SJohn Marino     {
912*86d7f5d3SJohn Marino 	val = dbm_fetch (db, key);
913*86d7f5d3SJohn Marino 	if (val.dptr != NULL)
914*86d7f5d3SJohn Marino 	    save_d (key.dptr, key.dsize, val.dptr, val.dsize);
915*86d7f5d3SJohn Marino     }
916*86d7f5d3SJohn Marino 
917*86d7f5d3SJohn Marino     close_module (db);
918*86d7f5d3SJohn Marino 
919*86d7f5d3SJohn Marino     /* Sort the list as requested */
920*86d7f5d3SJohn Marino     qsort ((void *) s_head, s_count, sizeof (struct sortrec), sort_order);
921*86d7f5d3SJohn Marino 
922*86d7f5d3SJohn Marino     /*
923*86d7f5d3SJohn Marino      * Run through the sorted array and format the entries
924*86d7f5d3SJohn Marino      * indent = space for modulename + space for status field
925*86d7f5d3SJohn Marino      */
926*86d7f5d3SJohn Marino     indent = 12 + (status * 12);
927*86d7f5d3SJohn Marino     fill = cols - (indent + 2);
928*86d7f5d3SJohn Marino     for (s_h = s_head, i = 0; i < s_count; i++, s_h++)
929*86d7f5d3SJohn Marino     {
930*86d7f5d3SJohn Marino 	char *line;
931*86d7f5d3SJohn Marino 
932*86d7f5d3SJohn Marino 	/* Print module name (and status, if wanted) */
933*86d7f5d3SJohn Marino 	line = Xasprintf ("%-12s", s_h->modname);
934*86d7f5d3SJohn Marino 	cvs_output (line, 0);
935*86d7f5d3SJohn Marino 	free (line);
936*86d7f5d3SJohn Marino 	if (status)
937*86d7f5d3SJohn Marino 	{
938*86d7f5d3SJohn Marino 	    line = Xasprintf (" %-11s", s_h->status);
939*86d7f5d3SJohn Marino 	    cvs_output (line, 0);
940*86d7f5d3SJohn Marino 	    free (line);
941*86d7f5d3SJohn Marino 	}
942*86d7f5d3SJohn Marino 
943*86d7f5d3SJohn Marino 	/* Parse module file entry as command line and print options */
944*86d7f5d3SJohn Marino 	line = Xasprintf ("%s %s", s_h->modname, s_h->rest);
945*86d7f5d3SJohn Marino 	line2argv (&moduleargc, &moduleargv, line, " \t");
946*86d7f5d3SJohn Marino 	free (line);
947*86d7f5d3SJohn Marino 	argc = moduleargc;
948*86d7f5d3SJohn Marino 	argv = moduleargv;
949*86d7f5d3SJohn Marino 
950*86d7f5d3SJohn Marino 	optind = 0;
951*86d7f5d3SJohn Marino 	wid = 0;
952*86d7f5d3SJohn Marino 	while ((c = getopt (argc, argv, CVSMODULE_OPTS)) != -1)
953*86d7f5d3SJohn Marino 	{
954*86d7f5d3SJohn Marino 	    if (!status)
955*86d7f5d3SJohn Marino 	    {
956*86d7f5d3SJohn Marino 		if (c == 'a' || c == 'l')
957*86d7f5d3SJohn Marino 		{
958*86d7f5d3SJohn Marino 		    char buf[5];
959*86d7f5d3SJohn Marino 
960*86d7f5d3SJohn Marino 		    sprintf (buf, " -%c", c);
961*86d7f5d3SJohn Marino 		    cvs_output (buf, 0);
962*86d7f5d3SJohn Marino 		    wid += 3;		/* Could just set it to 3 */
963*86d7f5d3SJohn Marino 		}
964*86d7f5d3SJohn Marino 		else
965*86d7f5d3SJohn Marino 		{
966*86d7f5d3SJohn Marino 		    char buf[10];
967*86d7f5d3SJohn Marino 
968*86d7f5d3SJohn Marino 		    if (strlen (optarg) + 4 + wid > (unsigned) fill)
969*86d7f5d3SJohn Marino 		    {
970*86d7f5d3SJohn Marino 			int j;
971*86d7f5d3SJohn Marino 
972*86d7f5d3SJohn Marino 			cvs_output ("\n", 1);
973*86d7f5d3SJohn Marino 			for (j = 0; j < indent; ++j)
974*86d7f5d3SJohn Marino 			    cvs_output (" ", 1);
975*86d7f5d3SJohn Marino 			wid = 0;
976*86d7f5d3SJohn Marino 		    }
977*86d7f5d3SJohn Marino 		    sprintf (buf, " -%c ", c);
978*86d7f5d3SJohn Marino 		    cvs_output (buf, 0);
979*86d7f5d3SJohn Marino 		    cvs_output (optarg, 0);
980*86d7f5d3SJohn Marino 		    wid += strlen (optarg) + 4;
981*86d7f5d3SJohn Marino 		}
982*86d7f5d3SJohn Marino 	    }
983*86d7f5d3SJohn Marino 	}
984*86d7f5d3SJohn Marino 	argc -= optind;
985*86d7f5d3SJohn Marino 	argv += optind;
986*86d7f5d3SJohn Marino 
987*86d7f5d3SJohn Marino 	/* Format and Print all the files and directories */
988*86d7f5d3SJohn Marino 	for (; argc--; argv++)
989*86d7f5d3SJohn Marino 	{
990*86d7f5d3SJohn Marino 	    if (strlen (*argv) + wid > (unsigned) fill)
991*86d7f5d3SJohn Marino 	    {
992*86d7f5d3SJohn Marino 		int j;
993*86d7f5d3SJohn Marino 
994*86d7f5d3SJohn Marino 		cvs_output ("\n", 1);
995*86d7f5d3SJohn Marino 		for (j = 0; j < indent; ++j)
996*86d7f5d3SJohn Marino 		    cvs_output (" ", 1);
997*86d7f5d3SJohn Marino 		wid = 0;
998*86d7f5d3SJohn Marino 	    }
999*86d7f5d3SJohn Marino 	    cvs_output (" ", 1);
1000*86d7f5d3SJohn Marino 	    cvs_output (*argv, 0);
1001*86d7f5d3SJohn Marino 	    wid += strlen (*argv) + 1;
1002*86d7f5d3SJohn Marino 	}
1003*86d7f5d3SJohn Marino 	cvs_output ("\n", 1);
1004*86d7f5d3SJohn Marino 
1005*86d7f5d3SJohn Marino 	/* Format the comment field -- save_d (), compressed spaces */
1006*86d7f5d3SJohn Marino 	for (cp2 = cp = s_h->comment; *cp; cp2 = cp)
1007*86d7f5d3SJohn Marino 	{
1008*86d7f5d3SJohn Marino 	    int j;
1009*86d7f5d3SJohn Marino 
1010*86d7f5d3SJohn Marino 	    for (j = 0; j < indent; ++j)
1011*86d7f5d3SJohn Marino 		cvs_output (" ", 1);
1012*86d7f5d3SJohn Marino 	    cvs_output (" # ", 0);
1013*86d7f5d3SJohn Marino 	    if (strlen (cp2) < (unsigned) (fill - 2))
1014*86d7f5d3SJohn Marino 	    {
1015*86d7f5d3SJohn Marino 		cvs_output (cp2, 0);
1016*86d7f5d3SJohn Marino 		cvs_output ("\n", 1);
1017*86d7f5d3SJohn Marino 		break;
1018*86d7f5d3SJohn Marino 	    }
1019*86d7f5d3SJohn Marino 	    cp += fill - 2;
1020*86d7f5d3SJohn Marino 	    while (*cp != ' ' && cp > cp2)
1021*86d7f5d3SJohn Marino 		cp--;
1022*86d7f5d3SJohn Marino 	    if (cp == cp2)
1023*86d7f5d3SJohn Marino 	    {
1024*86d7f5d3SJohn Marino 		cvs_output (cp2, 0);
1025*86d7f5d3SJohn Marino 		cvs_output ("\n", 1);
1026*86d7f5d3SJohn Marino 		break;
1027*86d7f5d3SJohn Marino 	    }
1028*86d7f5d3SJohn Marino 
1029*86d7f5d3SJohn Marino 	    *cp++ = '\0';
1030*86d7f5d3SJohn Marino 	    cvs_output (cp2, 0);
1031*86d7f5d3SJohn Marino 	    cvs_output ("\n", 1);
1032*86d7f5d3SJohn Marino 	}
1033*86d7f5d3SJohn Marino 
1034*86d7f5d3SJohn Marino 	free_names(&moduleargc, moduleargv);
1035*86d7f5d3SJohn Marino 	/* FIXME-leak: here is where we would free s_h->modname, s_h->rest,
1036*86d7f5d3SJohn Marino 	   and if applicable, s_h->status.  Not exactly a memory leak,
1037*86d7f5d3SJohn Marino 	   in the sense that we are about to exit(), but may be worth
1038*86d7f5d3SJohn Marino 	   noting if we ever do a multithreaded server or something of
1039*86d7f5d3SJohn Marino 	   the sort.  */
1040*86d7f5d3SJohn Marino     }
1041*86d7f5d3SJohn Marino     /* FIXME-leak: as above, here is where we would free s_head.  */
1042*86d7f5d3SJohn Marino }
1043