modules.c revision 17721
1/*
2 *    Copyright (c) 1992, Brian Berliner and Jeff Polk
3 *    Copyright (c) 1989-1992, Brian Berliner
4 *
5 *    You may distribute under the terms of the GNU General Public License
6 *    as specified in the README file that comes with the CVS 1.4 kit.
7 *
8 * Modules
9 *
10 *	Functions for accessing the modules file.
11 *
12 *	The modules file supports basically three formats of lines:
13 *		key [options] directory files... [ -x directory [files] ] ...
14 *		key [options] directory [ -x directory [files] ] ...
15 *		key -a aliases...
16 *
17 *	The -a option allows an aliasing step in the parsing of the modules
18 *	file.  The "aliases" listed on a line following the -a are
19 *	processed one-by-one, as if they were specified as arguments on the
20 *	command line.
21 */
22
23#include "cvs.h"
24#include "savecwd.h"
25
26
27/* Defines related to the syntax of the modules file.  */
28
29/* Options in modules file.  Note that it is OK to use GNU getopt features;
30   we already are arranging to make sure we are using the getopt distributed
31   with CVS.  */
32#define	CVSMODULE_OPTS	"+ad:i:lo:e:s:t:u:"
33
34/* Special delimiter.  */
35#define CVSMODULE_SPEC	'&'
36
37struct sortrec
38{
39    char *modname;
40    char *status;
41    char *rest;
42    char *comment;
43};
44
45static int sort_order PROTO((const PTR l, const PTR r));
46static void save_d PROTO((char *k, int ks, char *d, int ds));
47
48
49/*
50 * Open the modules file, and die if the CVSROOT environment variable
51 * was not set.  If the modules file does not exist, that's fine, and
52 * a warning message is displayed and a NULL is returned.
53 */
54DBM *
55open_module ()
56{
57    char mfile[PATH_MAX];
58
59    if (CVSroot == NULL)
60    {
61	(void) fprintf (stderr,
62			"%s: must set the CVSROOT environment variable\n",
63			program_name);
64	error (1, 0, "or specify the '-d' option to %s", program_name);
65    }
66    (void) sprintf (mfile, "%s/%s/%s", CVSroot, CVSROOTADM, CVSROOTADM_MODULES);
67    return (dbm_open (mfile, O_RDONLY, 0666));
68}
69
70/*
71 * Close the modules file, if the open succeeded, that is
72 */
73void
74close_module (db)
75    DBM *db;
76{
77    if (db != NULL)
78	dbm_close (db);
79}
80
81/*
82 * This is the recursive function that processes a module name.
83 * It calls back the passed routine for each directory of a module
84 * It runs the post checkout or post tag proc from the modules file
85 */
86int
87do_module (db, mname, m_type, msg, callback_proc, where,
88	   shorten, local_specified, run_module_prog, extra_arg)
89    DBM *db;
90    char *mname;
91    enum mtype m_type;
92    char *msg;
93    CALLBACKPROC callback_proc;
94    char *where;
95    int shorten;
96    int local_specified;
97    int run_module_prog;
98    char *extra_arg;
99{
100    char *checkin_prog = NULL;
101    char *checkout_prog = NULL;
102    char *export_prog = NULL;
103    char *tag_prog = NULL;
104    char *update_prog = NULL;
105    struct saved_cwd cwd;
106    char *line;
107    int modargc;
108    int xmodargc;
109    char **modargv;
110    char *xmodargv[MAXFILEPERDIR];
111    char *value;
112    char *zvalue;
113    char *mwhere = NULL;
114    char *mfile = NULL;
115    char *spec_opt = NULL;
116    char xvalue[PATH_MAX];
117    int alias = 0;
118    datum key, val;
119    char *cp;
120    int c, err = 0;
121
122#ifdef SERVER_SUPPORT
123    if (trace)
124      {
125	fprintf (stderr, "%s%c-> do_module (%s, %s, %s, %s)\n",
126		 error_use_protocol ? "E " : "",
127		 (server_active) ? 'S' : ' ',
128		 mname, msg, where ? where : "",
129		 extra_arg ? extra_arg : "");
130      }
131#endif
132
133    /* if this is a directory to ignore, add it to that list */
134    if (mname[0] == '!' && mname[1] != '\0')
135    {
136	ign_dir_add (mname+1);
137	return(err);
138    }
139
140    /* strip extra stuff from the module name */
141    strip_path (mname);
142
143    /*
144     * Look up the module using the following scheme:
145     *	1) look for mname as a module name
146     *	2) look for mname as a directory
147     *	3) look for mname as a file
148     *  4) take mname up to the first slash and look it up as a module name
149     *	   (this is for checking out only part of a module)
150     */
151
152    /* look it up as a module name */
153    key.dptr = mname;
154    key.dsize = strlen (key.dptr);
155    if (db != NULL)
156	val = dbm_fetch (db, key);
157    else
158	val.dptr = NULL;
159    if (val.dptr != NULL)
160    {
161	/* null terminate the value  XXX - is this space ours? */
162	val.dptr[val.dsize] = '\0';
163
164	/* If the line ends in a comment, strip it off */
165	if ((cp = strchr (val.dptr, '#')) != NULL)
166	{
167	    do
168		*cp-- = '\0';
169	    while (isspace (*cp));
170	}
171	else
172	{
173	    /* Always strip trailing spaces */
174	    cp = strchr (val.dptr, '\0');
175	    while (cp > val.dptr && isspace(*--cp))
176		*cp = '\0';
177	}
178
179	value = val.dptr;
180	mwhere = xstrdup (mname);
181	goto found;
182    }
183    else
184    {
185	char file[PATH_MAX];
186	char attic_file[PATH_MAX];
187	char *acp;
188
189	/* check to see if mname is a directory or file */
190
191	(void) sprintf (file, "%s/%s", CVSroot, mname);
192	if ((acp = strrchr (mname, '/')) != NULL)
193	{
194	    *acp = '\0';
195	    (void) sprintf (attic_file, "%s/%s/%s/%s%s", CVSroot, mname,
196			    CVSATTIC, acp + 1, RCSEXT);
197	    *acp = '/';
198	}
199	else
200	    (void) sprintf (attic_file, "%s/%s/%s%s", CVSroot, CVSATTIC,
201			    mname, RCSEXT);
202
203	if (isdir (file))
204	{
205	    value = mname;
206	    goto found;
207	}
208	else
209	{
210	    (void) strcat (file, RCSEXT);
211	    if (isfile (file) || isfile (attic_file))
212	    {
213		/* if mname was a file, we have to split it into "dir file" */
214		if ((cp = strrchr (mname, '/')) != NULL && cp != mname)
215		{
216		    char *slashp;
217
218		    /* put the ' ' in a copy so we don't mess up the original */
219		    value = strcpy (xvalue, mname);
220		    slashp = strrchr (value, '/');
221		    *slashp = ' ';
222		}
223		else
224		{
225		    /*
226		     * the only '/' at the beginning or no '/' at all
227		     * means the file we are interested in is in CVSROOT
228		     * itself so the directory should be '.'
229		     */
230		    if (cp == mname)
231		    {
232			/* drop the leading / if specified */
233			value = strcpy (xvalue, ". ");
234			(void) strcat (xvalue, mname + 1);
235		    }
236		    else
237		    {
238			/* otherwise just copy it */
239			value = strcpy (xvalue, ". ");
240			(void) strcat (xvalue, mname);
241		    }
242		}
243		goto found;
244	    }
245	}
246    }
247
248    /* look up everything to the first / as a module */
249    if (mname[0] != '/' && (cp = strchr (mname, '/')) != NULL)
250    {
251	/* Make the slash the new end of the string temporarily */
252	*cp = '\0';
253	key.dptr = mname;
254	key.dsize = strlen (key.dptr);
255
256	/* do the lookup */
257	if (db != NULL)
258	    val = dbm_fetch (db, key);
259	else
260	    val.dptr = NULL;
261
262	/* if we found it, clean up the value and life is good */
263	if (val.dptr != NULL)
264	{
265	    char *cp2;
266
267	    /* null terminate the value XXX - is this space ours? */
268	    val.dptr[val.dsize] = '\0';
269
270	    /* If the line ends in a comment, strip it off */
271	    if ((cp2 = strchr (val.dptr, '#')) != NULL)
272	    {
273		do
274		    *cp2-- = '\0';
275		while (isspace (*cp2));
276	    }
277	    value = val.dptr;
278
279	    /* mwhere gets just the module name */
280	    mwhere = xstrdup (mname);
281	    mfile = cp + 1;
282
283	    /* put the / back in mname */
284	    *cp = '/';
285
286	    goto found;
287	}
288
289	/* put the / back in mname */
290	*cp = '/';
291    }
292
293    /* if we got here, we couldn't find it using our search, so give up */
294    error (0, 0, "cannot find module `%s' - ignored", mname);
295    err++;
296    if (mwhere)
297	free (mwhere);
298    return (err);
299
300
301    /*
302     * At this point, we found what we were looking for in one
303     * of the many different forms.
304     */
305  found:
306
307    /* remember where we start */
308    if (save_cwd (&cwd))
309	exit (EXIT_FAILURE);
310
311    /* copy value to our own string since if we go recursive we'll be
312       really screwed if we do another dbm lookup */
313    zvalue = xstrdup (value);
314    value = zvalue;
315
316    /* search the value for the special delimiter and save for later */
317    if ((cp = strchr (value, CVSMODULE_SPEC)) != NULL)
318    {
319	*cp = '\0';			/* null out the special char */
320	spec_opt = cp + 1;		/* save the options for later */
321
322	if (cp != value)		/* strip whitespace if necessary */
323	    while (isspace (*--cp))
324		*cp = '\0';
325
326	if (cp == value)
327	{
328	    /*
329	     * we had nothing but special options, so skip arg
330	     * parsing and regular stuff entirely
331	     *
332	     * If there were only special ones though, we must
333	     * make the appropriate directory and cd to it
334	     */
335	    char *dir;
336
337	    /* XXX - XXX - MAJOR HACK - DO NOT SHIP - this needs to
338	       be !pipeout, but we don't know that here yet */
339	    if (!run_module_prog)
340		goto out;
341
342	    dir = where ? where : mname;
343	    /* XXX - think about making null repositories at each dir here
344		     instead of just at the bottom */
345	    make_directories (dir);
346	    if (chdir (dir) < 0)
347	    {
348		error (0, errno, "cannot chdir to %s", dir);
349		spec_opt = NULL;
350		err++;
351		goto out;
352	    }
353	    if (!isfile (CVSADM))
354	    {
355		char nullrepos[PATH_MAX];
356
357		(void) sprintf (nullrepos, "%s/%s/%s", CVSroot,
358				CVSROOTADM, CVSNULLREPOS);
359		if (!isfile (nullrepos))
360		{
361		    mode_t omask;
362		    omask = umask (cvsumask);
363		    (void) CVS_MKDIR (nullrepos, 0777);
364		    (void) umask (omask);
365		}
366		if (!isdir (nullrepos))
367		    error (1, 0, "there is no repository %s", nullrepos);
368
369		Create_Admin (".", dir,
370			      nullrepos, (char *) NULL, (char *) NULL);
371		if (!noexec)
372		{
373		    FILE *fp;
374
375		    fp = open_file (CVSADM_ENTSTAT, "w+");
376		    if (fclose (fp) == EOF)
377			error (1, errno, "cannot close %s", CVSADM_ENTSTAT);
378#ifdef SERVER_SUPPORT
379		    if (server_active)
380			server_set_entstat (dir, nullrepos);
381#endif
382		}
383	    }
384	  out:
385	    goto do_special;
386	}
387    }
388
389    /* don't do special options only part of a module was specified */
390    if (mfile != NULL)
391	spec_opt = NULL;
392
393    /*
394     * value now contains one of the following:
395     *    1) dir
396     *	  2) dir file
397     *    3) the value from modules without any special args
398     *		    [ args ] dir [file] [file] ...
399     *	     or     -a module [ module ] ...
400     */
401
402    /* Put the value on a line with XXX prepended for getopt to eat */
403    line = xmalloc (strlen (value) + 10);
404    (void) sprintf (line, "%s %s", "XXX", value);
405
406    /* turn the line into an argv[] array */
407    line2argv (&xmodargc, xmodargv, line);
408    free (line);
409    modargc = xmodargc;
410    modargv = xmodargv;
411
412    /* parse the args */
413    optind = 1;
414    while ((c = getopt (modargc, modargv, CVSMODULE_OPTS)) != -1)
415    {
416	switch (c)
417	{
418	    case 'a':
419		alias = 1;
420		break;
421	    case 'd':
422		if (mwhere)
423		    free (mwhere);
424		mwhere = xstrdup (optarg);
425		break;
426	    case 'i':
427		checkin_prog = optarg;
428		break;
429	    case 'l':
430		local_specified = 1;
431		break;
432	    case 'o':
433		checkout_prog = optarg;
434		break;
435	    case 'e':
436		export_prog = optarg;
437		break;
438	    case 't':
439		tag_prog = optarg;
440		break;
441	    case 'u':
442		update_prog = optarg;
443		break;
444	    case '?':
445		error (0, 0,
446		       "modules file has invalid option for key %s value %s",
447		       key.dptr, val.dptr);
448		err++;
449		if (mwhere)
450		    free (mwhere);
451		free (zvalue);
452		free_cwd (&cwd);
453		return (err);
454	}
455    }
456    modargc -= optind;
457    modargv += optind;
458    if (modargc == 0)
459    {
460	error (0, 0, "modules file missing directory for module %s", mname);
461	if (mwhere)
462	    free (mwhere);
463	free (zvalue);
464	free_cwd (&cwd);
465	return (++err);
466    }
467
468    /* if this was an alias, call ourselves recursively for each module */
469    if (alias)
470    {
471	int i;
472
473	for (i = 0; i < modargc; i++)
474	{
475	    if (strcmp (mname, modargv[i]) == 0)
476		error (0, 0,
477		       "module `%s' in modules file contains infinite loop",
478		       mname);
479	    else
480		err += do_module (db, modargv[i], m_type, msg, callback_proc,
481				  where, shorten, local_specified,
482				  run_module_prog, extra_arg);
483	}
484	if (mwhere)
485	    free (mwhere);
486	free (zvalue);
487	free_cwd (&cwd);
488	return (err);
489    }
490
491    /* otherwise, process this module */
492    err += callback_proc (&modargc, modargv, where, mwhere, mfile, shorten,
493			  local_specified, mname, msg);
494
495#if 0
496    /* FIXME: I've fixed this so that the correct arguments are called,
497       but now this fails because there is code below this point that
498       uses optarg values extracted from the arg vector. */
499    free_names (&xmodargc, xmodargv);
500#endif
501
502    /* if there were special include args, process them now */
503
504  do_special:
505
506    /* blow off special options if -l was specified */
507    if (local_specified)
508	spec_opt = NULL;
509
510    while (spec_opt != NULL)
511    {
512	char *next_opt;
513
514	cp = strchr (spec_opt, CVSMODULE_SPEC);
515	if (cp != NULL)
516	{
517	    /* save the beginning of the next arg */
518	    next_opt = cp + 1;
519
520	    /* strip whitespace off the end */
521	    do
522		*cp = '\0';
523	    while (isspace (*--cp));
524	}
525	else
526	    next_opt = NULL;
527
528	/* strip whitespace from front */
529	while (isspace (*spec_opt))
530	    spec_opt++;
531
532	if (*spec_opt == '\0')
533	    error (0, 0, "Mal-formed %c option for module %s - ignored",
534		   CVSMODULE_SPEC, mname);
535	else
536	    err += do_module (db, spec_opt, m_type, msg, callback_proc,
537			      (char *) NULL, 0, local_specified,
538			      run_module_prog, extra_arg);
539	spec_opt = next_opt;
540    }
541
542    /* write out the checkin/update prog files if necessary */
543#ifdef SERVER_SUPPORT
544    if (err == 0 && !noexec && m_type == CHECKOUT && server_expanding)
545    {
546	if (checkin_prog != NULL)
547	    server_prog (where ? where : mname, checkin_prog, PROG_CHECKIN);
548	if (update_prog != NULL)
549	    server_prog (where ? where : mname, update_prog, PROG_UPDATE);
550    }
551    else
552#endif
553    if (err == 0 && !noexec && m_type == CHECKOUT && run_module_prog)
554    {
555	FILE *fp;
556
557	if (checkin_prog != NULL)
558	{
559	    fp = open_file (CVSADM_CIPROG, "w+");
560	    (void) fprintf (fp, "%s\n", checkin_prog);
561	    if (fclose (fp) == EOF)
562		error (1, errno, "cannot close %s", CVSADM_CIPROG);
563	}
564	if (update_prog != NULL)
565	{
566	    fp = open_file (CVSADM_UPROG, "w+");
567	    (void) fprintf (fp, "%s\n", update_prog);
568	    if (fclose (fp) == EOF)
569		error (1, errno, "cannot close %s", CVSADM_UPROG);
570	}
571    }
572
573    /* cd back to where we started */
574    if (restore_cwd (&cwd, NULL))
575	exit (EXIT_FAILURE);
576    free_cwd (&cwd);
577
578    /* run checkout or tag prog if appropriate */
579    if (err == 0 && run_module_prog)
580    {
581	if ((m_type == TAG && tag_prog != NULL) ||
582	    (m_type == CHECKOUT && checkout_prog != NULL) ||
583	    (m_type == EXPORT && export_prog != NULL))
584	{
585	    /*
586	     * If a relative pathname is specified as the checkout, tag
587	     * or export proc, try to tack on the current "where" value.
588	     * if we can't find a matching program, just punt and use
589	     * whatever is specified in the modules file.
590	     */
591	    char real_prog[PATH_MAX];
592	    char *prog = (m_type == TAG ? tag_prog :
593			  (m_type == CHECKOUT ? checkout_prog : export_prog));
594	    char *real_where = (where != NULL ? where : mwhere);
595
596	    if ((*prog != '/') && (*prog != '.'))
597	    {
598		(void) sprintf (real_prog, "%s/%s", real_where, prog);
599		if (isfile (real_prog))
600		    prog = real_prog;
601	    }
602
603	    run_setup ("%s %s", prog, real_where);
604	    if (extra_arg)
605		run_arg (extra_arg);
606
607	    if (!quiet)
608	    {
609		(void) printf ("%s %s: Executing '", program_name,
610			       command_name);
611		run_print (stdout);
612		(void) printf ("'\n");
613	    }
614	    err += run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
615	}
616    }
617
618    /* clean up */
619    if (mwhere)
620	free (mwhere);
621    free (zvalue);
622
623    return (err);
624}
625
626/* - Read all the records from the modules database into an array.
627   - Sort the array depending on what format is desired.
628   - Print the array in the format desired.
629
630   Currently, there are only two "desires":
631
632   1. Sort by module name and format the whole entry including switches,
633      files and the comment field: (Including aliases)
634
635      modulename	-s switches, one per line, even if
636			-i it has many switches.
637			Directories and files involved, formatted
638			to cover multiple lines if necessary.
639			# Comment, also formatted to cover multiple
640			# lines if necessary.
641
642   2. Sort by status field string and print:  (*not* including aliases)
643
644      modulename    STATUS	Directories and files involved, formatted
645				to cover multiple lines if necessary.
646				# Comment, also formatted to cover multiple
647				# lines if necessary.
648*/
649
650static struct sortrec *s_head;
651
652static int s_max = 0;			/* Number of elements allocated */
653static int s_count = 0;			/* Number of elements used */
654
655static int Status;		        /* Nonzero if the user is
656					   interested in status
657					   information as well as
658					   module name */
659static char def_status[] = "NONE";
660
661/* Sort routine for qsort:
662   - If we want the "Status" field to be sorted, check it first.
663   - Then compare the "module name" fields.  Since they are unique, we don't
664     have to look further.
665*/
666static int
667sort_order (l, r)
668    const PTR l;
669    const PTR r;
670{
671    int i;
672    const struct sortrec *left = (const struct sortrec *) l;
673    const struct sortrec *right = (const struct sortrec *) r;
674
675    if (Status)
676    {
677	/* If Sort by status field, compare them. */
678	if ((i = strcmp (left->status, right->status)) != 0)
679	    return (i);
680    }
681    return (strcmp (left->modname, right->modname));
682}
683
684static void
685save_d (k, ks, d, ds)
686    char *k;
687    int ks;
688    char *d;
689    int ds;
690{
691    char *cp, *cp2;
692    struct sortrec *s_rec;
693
694    if (Status && *d == '-' && *(d + 1) == 'a')
695	return;				/* We want "cvs co -s" and it is an alias! */
696
697    if (s_count == s_max)
698    {
699	s_max += 64;
700	s_head = (struct sortrec *) xrealloc ((char *) s_head, s_max * sizeof (*s_head));
701    }
702    s_rec = &s_head[s_count];
703    s_rec->modname = cp = xmalloc (ks + 1);
704    (void) strncpy (cp, k, ks);
705    *(cp + ks) = '\0';
706
707    s_rec->rest = cp2 = xmalloc (ds + 1);
708    cp = d;
709    *(cp + ds) = '\0';	/* Assumes an extra byte at end of static dbm buffer */
710
711    while (isspace (*cp))
712	cp++;
713    /* Turn <spaces> into one ' ' -- makes the rest of this routine simpler */
714    while (*cp)
715    {
716	if (isspace (*cp))
717	{
718	    *cp2++ = ' ';
719	    while (isspace (*cp))
720		cp++;
721	}
722	else
723	    *cp2++ = *cp++;
724    }
725    *cp2 = '\0';
726
727    /* Look for the "-s statusvalue" text */
728    if (Status)
729    {
730	s_rec->status = def_status;
731
732	/* Minor kluge, but general enough to maintain */
733	for (cp = s_rec->rest; (cp2 = strchr (cp, '-')) != NULL; cp = ++cp2)
734	{
735	    if (*(cp2 + 1) == 's' && *(cp2 + 2) == ' ')
736	    {
737		s_rec->status = (cp2 += 3);
738		while (*cp2 != ' ')
739		    cp2++;
740		*cp2++ = '\0';
741		cp = cp2;
742		break;
743	    }
744	}
745    }
746    else
747	cp = s_rec->rest;
748
749    /* Find comment field, clean up on all three sides & compress blanks */
750    if ((cp2 = cp = strchr (cp, '#')) != NULL)
751    {
752	if (*--cp2 == ' ')
753	    *cp2 = '\0';
754	if (*++cp == ' ')
755	    cp++;
756	s_rec->comment = cp;
757    }
758    else
759	s_rec->comment = "";
760
761    s_count++;
762}
763
764/* Print out the module database as we know it.  If STATUS is
765   non-zero, print out status information for each module. */
766
767void
768cat_module (status)
769    int status;
770{
771    DBM *db;
772    datum key, val;
773    int i, c, wid, argc, cols = 80, indent, fill;
774    int moduleargc;
775    struct sortrec *s_h;
776    char *cp, *cp2, **argv;
777    char *line;
778    char *moduleargv[MAXFILEPERDIR];
779
780#ifdef sun
781#ifdef TIOCGSIZE
782    struct ttysize ts;
783
784    (void) ioctl (0, TIOCGSIZE, &ts);
785    cols = ts.ts_cols;
786#endif
787#else
788#ifdef TIOCGWINSZ
789    struct winsize ws;
790
791    (void) ioctl (0, TIOCGWINSZ, &ws);
792    cols = ws.ws_col;
793#endif
794#endif
795
796    Status = status;
797
798    /* Read the whole modules file into allocated records */
799    if (!(db = open_module ()))
800	error (1, 0, "failed to open the modules file");
801
802    for (key = dbm_firstkey (db); key.dptr != NULL; key = dbm_nextkey (db))
803    {
804	val = dbm_fetch (db, key);
805	if (val.dptr != NULL)
806	    save_d (key.dptr, key.dsize, val.dptr, val.dsize);
807    }
808
809    /* Sort the list as requested */
810    qsort ((PTR) s_head, s_count, sizeof (struct sortrec), sort_order);
811
812    /*
813     * Run through the sorted array and format the entries
814     * indent = space for modulename + space for status field
815     */
816    indent = 12 + (status * 12);
817    fill = cols - (indent + 2);
818    for (s_h = s_head, i = 0; i < s_count; i++, s_h++)
819    {
820	/* Print module name (and status, if wanted) */
821	(void) printf ("%-12s", s_h->modname);
822	if (status)
823	{
824	    (void) printf (" %-11s", s_h->status);
825	    if (s_h->status != def_status)
826		*(s_h->status + strlen (s_h->status)) = ' ';
827	}
828
829	/* Parse module file entry as command line and print options */
830	line = xmalloc (strlen (s_h->modname) + strlen (s_h->rest) + 10);
831	(void) sprintf (line, "%s %s", s_h->modname, s_h->rest);
832	line2argv (&moduleargc, moduleargv, line);
833	free (line);
834	argc = moduleargc;
835	argv = moduleargv;
836
837	optind = 0;
838	wid = 0;
839	while ((c = getopt (argc, argv, CVSMODULE_OPTS)) != -1)
840	{
841	    if (!status)
842	    {
843		if (c == 'a' || c == 'l')
844		{
845		    (void) printf (" -%c", c);
846		    wid += 3;		/* Could just set it to 3 */
847		}
848		else
849		{
850		    if (strlen (optarg) + 4 + wid > (unsigned) fill)
851		    {
852			(void) printf ("\n%*s", indent, "");
853			wid = 0;
854		    }
855		    (void) printf (" -%c %s", c, optarg);
856		    wid += strlen (optarg) + 4;
857		}
858	    }
859	}
860	argc -= optind;
861	argv += optind;
862
863	/* Format and Print all the files and directories */
864	for (; argc--; argv++)
865	{
866	    if (strlen (*argv) + wid > (unsigned) fill)
867	    {
868		(void) printf ("\n%*s", indent, "");
869		wid = 0;
870	    }
871	    (void) printf (" %s", *argv);
872	    wid += strlen (*argv) + 1;
873	}
874	(void) printf ("\n");
875
876	/* Format the comment field -- save_d (), compressed spaces */
877	for (cp2 = cp = s_h->comment; *cp; cp2 = cp)
878	{
879	    (void) printf ("%*s # ", indent, "");
880	    if (strlen (cp2) < (unsigned) (fill - 2))
881	    {
882		(void) printf ("%s\n", cp2);
883		break;
884	    }
885	    cp += fill - 2;
886	    while (*cp != ' ' && cp > cp2)
887		cp--;
888	    if (cp == cp2)
889	    {
890		(void) printf ("%s\n", cp2);
891		break;
892	    }
893
894	    *cp++ = '\0';
895	    (void) printf ("%s\n", cp2);
896	}
897
898	free_names(&moduleargc, moduleargv);
899    }
900}
901