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