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