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