PageRenderTime 125ms CodeModel.GetById 49ms app.highlight 68ms RepoModel.GetById 1ms app.codeStats 0ms

/contrib/cvs/src/modules.c

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