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 distribution.
12 *
13 * This is the main C driver for the CVS system.
14 *
15 * Credit to Dick Grune, Vrije Universiteit, Amsterdam, for writing
16 * the shell-script CVS system that this is based on.
17 *
18 */
19
20 #include "cvs.h"
21
22 #include "closeout.h"
23 #include "setenv.h"
24 #include "strftime.h"
25 #include "xgethostname.h"
26
27 #ifdef USE_LIBBSD
28 uint32_t arc4random(void);
29 #endif
30
31 __RCSID("$MirOS: src/gnu/usr.bin/cvs/src/main.c,v 1.15 2012/02/07 22:43:03 tg Exp $");
32
33 const char *program_name;
34 const char *program_path;
35 const char *cvs_cmd_name;
36
37 const char *global_session_id; /* Random session ID */
38
39 char *hostname;
40 /* FIXME: Perhaps this should be renamed original_hostname or the like? */
41 char *server_hostname;
42
43 int use_editor = 1;
44 int use_cvsrc = 1;
45 int cvswrite = !CVSREAD_DFLT;
46 int really_quiet = 0;
47 int quiet = 0;
48 int trace = 0;
49 int noexec = 0;
50 int readonlyfs = 0;
51 int logoff = 0;
52
53
54
55 /***
56 ***
57 *** CVSROOT/config options
58 ***
59 ***/
60 struct config *config;
61
62
63
64 mode_t cvsumask = UMASK_DFLT;
65
66 char *CurDir;
67
68 /*
69 * Defaults, for the environment variables that are not set
70 */
71 char *Editor = EDITOR_DFLT;
72
73
74
75 /* Temp dir stuff. */
76
77 /* Temp dir, if set by the user. */
78 static char *tmpdir_cmdline;
79
80
81
82 /* Returns in order of precedence:
83 *
84 * 1. Temp dir as set via the command line.
85 * 2. Temp dir as set in CVSROOT/config.
86 * 3. Temp dir as set in $TMPDIR env var.
87 * 4. Contents of TMPDIR_DFLT preprocessor macro.
88 *
89 * ERRORS
90 * It is a fatal error if this function would otherwise return NULL or an
91 * empty string.
92 */
93 const char *
get_cvs_tmp_dir(void)94 get_cvs_tmp_dir (void)
95 {
96 const char *retval;
97 if (tmpdir_cmdline) retval = tmpdir_cmdline;
98 else if (config && config->TmpDir) retval = config->TmpDir;
99 else retval = get_system_temp_dir ();
100 if (!retval) retval = TMPDIR_DFLT;
101
102 if (!retval || !*retval) error (1, 0, "No temp dir specified.");
103
104 return retval;
105 }
106
107
108
109 /* When our working directory contains subdirectories with different
110 values in CVS/Root files, we maintain a list of them. */
111 List *root_directories = NULL;
112
113 static const struct cmd
114 {
115 const char *fullname; /* Full name of the function (e.g. "commit") */
116
117 /* Synonyms for the command, nick1 and nick2. We supply them
118 mostly for two reasons: (1) CVS has always supported them, and
119 we need to maintain compatibility, (2) if there is a need for a
120 version which is shorter than the fullname, for ease in typing.
121 Synonyms have the disadvantage that people will see "new" and
122 then have to think about it, or look it up, to realize that is
123 the operation they know as "add". Also, this means that one
124 cannot create a command "cvs new" with a different meaning. So
125 new synonyms are probably best used sparingly, and where used
126 should be abbreviations of the fullname (preferably consisting
127 of the first 2 or 3 or so letters).
128
129 One thing that some systems do is to recognize any unique
130 abbreviation, for example "annotat" "annota", etc., for
131 "annotate". The problem with this is that scripts and user
132 habits will expect a certain abbreviation to be unique, and in
133 a future release of CVS it may not be. So it is better to
134 accept only an explicit list of abbreviations and plan on
135 supporting them in the future as well as now. */
136
137 const char *nick1;
138 const char *nick2;
139
140 int (*func) (int, char **); /* Function takes (argc, argv) arguments. */
141 unsigned long attr; /* Attributes. */
142 } cmds[] =
143
144 {
145 { "add", "ad", "new", add, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
146 { "admin", "adm", "rcs", admin, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
147 { "annotate", "ann", NULL, annotate, CVS_CMD_USES_WORK_DIR },
148 { "checkout", "co", "get", checkout, 0 },
149 { "commit", "ci", "com", commit, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
150 { "diff", "di", "dif", diff, CVS_CMD_USES_WORK_DIR },
151 { "edit", NULL, NULL, edit, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
152 { "editors", NULL, NULL, editors, CVS_CMD_USES_WORK_DIR },
153 { "export", "exp", "ex", checkout, CVS_CMD_USES_WORK_DIR },
154 { "history", "hi", "his", history, CVS_CMD_USES_WORK_DIR },
155 { "import", "im", "imp", import, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR | CVS_CMD_IGNORE_ADMROOT},
156 { "init", NULL, NULL, init, CVS_CMD_MODIFIES_REPOSITORY },
157 #if defined (HAVE_KERBEROS) && defined (SERVER_SUPPORT)
158 { "kserver", NULL, NULL, server, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR }, /* placeholder */
159 #endif
160 { "log", "lo", NULL, cvslog, CVS_CMD_USES_WORK_DIR },
161 #ifdef AUTH_CLIENT_SUPPORT
162 { "login", "logon", "lgn", login, 0 },
163 { "logout", NULL, NULL, logout, 0 },
164 #endif /* AUTH_CLIENT_SUPPORT */
165 { "ls", "dir", "list", ls, 0 },
166 #if (defined(AUTH_SERVER_SUPPORT) || defined (HAVE_GSSAPI)) && defined(SERVER_SUPPORT)
167 { "pserver", NULL, NULL, server, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR }, /* placeholder */
168 #endif
169 { "rannotate","rann", "ra", annotate, 0 },
170 { "rdiff", "patch", "pa", patch, 0 },
171 { "release", "re", "rel", release, CVS_CMD_MODIFIES_REPOSITORY },
172 { "remove", "rm", "delete", cvsremove, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
173 { "rlog", "rl", NULL, cvslog, 0 },
174 { "rls", "rdir", "rlist", ls, 0 },
175 { "rtag", "rt", "rfreeze", cvstag, CVS_CMD_MODIFIES_REPOSITORY },
176 #ifdef SERVER_SUPPORT
177 { "server", NULL, NULL, server, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
178 #endif
179 { "suck", NULL, NULL, suck, 0 },
180 { "status", "st", "stat", cvsstatus, CVS_CMD_USES_WORK_DIR },
181 { "tag", "ta", "freeze", cvstag, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
182 { "unedit", NULL, NULL, unedit, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
183 { "update", "up", "upd", update, CVS_CMD_USES_WORK_DIR },
184 { "version", "ve", "ver", version, 0 },
185 { "watch", NULL, NULL, watch, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
186 { "watchers", NULL, NULL, watchers, CVS_CMD_USES_WORK_DIR },
187 { NULL, NULL, NULL, NULL, 0 },
188 };
189
190 static const char *const usg[] =
191 {
192 /* CVS usage messages never have followed the GNU convention of
193 putting metavariables in uppercase. I don't know whether that
194 is a good convention or not, but if it changes it would have to
195 change in all the usage messages. For now, they consistently
196 use lowercase, as far as I know. Punctuation is pretty funky,
197 though. Sometimes they use none, as here. Sometimes they use
198 single quotes (not the TeX-ish `' stuff), as in --help-options.
199 Sometimes they use double quotes, as in cvs -H add.
200
201 Most (not all) of the usage messages seem to have periods at
202 the end of each line. I haven't tried to duplicate this style
203 in --help as it is a rather different format from the rest. */
204
205 "Usage: %s [cvs-options] command [command-options-and-arguments]\n",
206 " where cvs-options are -q, -n, etc.\n",
207 " (specify --help-options for a list of options)\n",
208 " where command is add, admin, etc.\n",
209 " (specify --help-commands for a list of commands\n",
210 " or --help-synonyms for a list of command synonyms)\n",
211 " where command-options-and-arguments depend on the specific command\n",
212 " (specify -H followed by a command name for command-specific help)\n",
213 " Specify --help to receive this message\n",
214 "\n",
215
216 /* Some people think that a bug-reporting address should go here. IMHO,
217 the web sites are better because anything else is very likely to go
218 obsolete in the years between a release and when someone might be
219 reading this help. Besides, we could never adequately discuss
220 bug reporting in a concise enough way to put in a help message. */
221
222 /* I was going to put this at the top, but usage() wants the %s to
223 be in the first line. */
224 "The Concurrent Versions System (CVS) is a tool for version control.\n",
225 /* I really don't think I want to try to define "version control"
226 in one line. I'm not sure one can get more concise than the
227 paragraph in ../cvs.spec without assuming the reader knows what
228 version control means. */
229
230 "For CVS updates and additional information, see\n",
231 " the CVS home page at http://www.nongnu.org/cvs/ or\n",
232 " the CVSNT home page at http://www.cvsnt.org/\n",
233 NULL,
234 };
235
236 static const char *const cmd_usage[] =
237 {
238 "CVS commands are:\n",
239 " add Add a new file/directory to the repository\n",
240 " admin Administration front end for rcs\n",
241 " annotate Show last revision where each line was modified\n",
242 " checkout Checkout sources for editing\n",
243 " commit Check files into the repository\n",
244 " diff Show differences between revisions\n",
245 " edit Get ready to edit a watched file\n",
246 " editors See who is editing a watched file\n",
247 " export Export sources from CVS, similar to checkout\n",
248 " history Show repository access history\n",
249 " import Import sources into CVS, using vendor branches\n",
250 " init Create a CVS repository if it doesn't exist\n",
251 #if defined (HAVE_KERBEROS) && defined (SERVER_SUPPORT)
252 " kserver Kerberos server mode\n",
253 #endif
254 " log Print out history information for files\n",
255 #ifdef AUTH_CLIENT_SUPPORT
256 " login Prompt for password for authenticating server\n",
257 " logout Removes entry in .cvspass for remote repository\n",
258 #endif /* AUTH_CLIENT_SUPPORT */
259 " ls List files available from CVS\n",
260 #if (defined(AUTH_SERVER_SUPPORT) || defined (HAVE_GSSAPI)) && defined(SERVER_SUPPORT)
261 " pserver Password server mode\n",
262 #endif
263 " rannotate Show last revision where each line of module was modified\n",
264 " rdiff Create 'patch' format diffs between releases\n",
265 " release Indicate that a Module is no longer in use\n",
266 " remove Remove an entry from the repository\n",
267 " rlog Print out history information for a module\n",
268 " rls List files in a module\n",
269 " rtag Add a symbolic tag to a module\n",
270 #ifdef SERVER_SUPPORT
271 " server Server mode\n",
272 #endif
273 " status Display status information on checked out files\n",
274 " tag Add a symbolic tag to checked out version of files\n",
275 " unedit Undo an edit command\n",
276 " update Bring work tree in sync with repository\n",
277 " version Show current CVS version(s)\n",
278 " watch Set watches\n",
279 " watchers See who is watching a file\n",
280 "(Specify the --help option for a list of other help options)\n",
281 NULL,
282 };
283
284 static const char *const opt_usage[] =
285 {
286 /* Omit -b because it is just for compatibility. */
287 "CVS global options (specified before the command name) are:\n",
288 " -H Displays usage information for command.\n",
289 " -Q Cause CVS to be really quiet.\n",
290 " -q Cause CVS to be somewhat quiet.\n",
291 " -r Make checked-out files read-only.\n",
292 " -w Make checked-out files read-write (default).\n",
293 " -g Force group-write permissions on checked-out files.\n",
294 " -n Do not execute anything that will change the disk.\n",
295 " -t Show trace of program execution (repeat for more\n",
296 " verbosity) -- try with -n.\n",
297 " -R Assume repository is read-only, such as CDROM\n",
298 " -v CVS version and copyright.\n",
299 " -T tmpdir Use 'tmpdir' for temporary files.\n",
300 " -e editor Use 'editor' for editing log information.\n",
301 " -d CVS_root Overrides $CVSROOT as the root of the CVS tree.\n",
302 " -f Do not use the ~/.cvsrc file.\n",
303 #ifdef CLIENT_SUPPORT
304 " -z # Request compression level '#' for net traffic.\n",
305 #ifdef ENCRYPTION
306 " -x Encrypt all net traffic.\n",
307 #endif
308 " -a Authenticate all net traffic.\n",
309 #endif
310 " -s VAR=VAL Set CVS user variable.\n",
311 "(Specify the --help option for a list of other help options)\n",
312 NULL
313 };
314
315
316 static int
set_root_directory(Node * p,void * ignored)317 set_root_directory (Node *p, void *ignored)
318 {
319 if (current_parsed_root == NULL && p->data != NULL)
320 {
321 current_parsed_root = p->data;
322 original_parsed_root = current_parsed_root;
323 return 1;
324 }
325 return 0;
326 }
327
328
329 static const char * const*
cmd_synonyms(void)330 cmd_synonyms (void)
331 {
332 char ** synonyms;
333 char ** line;
334 const struct cmd *c = &cmds[0];
335 /* Three more for title, "specify --help" line, and NULL. */
336 int numcmds = 3;
337
338 while (c->fullname != NULL)
339 {
340 numcmds++;
341 c++;
342 }
343
344 synonyms = xnmalloc (numcmds, sizeof(char *));
345 line = synonyms;
346 *line++ = "CVS command synonyms are:\n";
347 for (c = &cmds[0]; c->fullname != NULL; c++)
348 {
349 if (c->nick1 || c->nick2)
350 {
351 *line = Xasprintf (" %-12s %s %s\n", c->fullname,
352 c->nick1 ? c->nick1 : "",
353 c->nick2 ? c->nick2 : "");
354 line++;
355 }
356 }
357 *line++ = "(Specify the --help option for a list of other help options)\n";
358 *line = NULL;
359
360 return (const char * const*) synonyms; /* will never be freed */
361 }
362
363
364
365 unsigned long int
lookup_command_attribute(const char * cmd_name)366 lookup_command_attribute (const char *cmd_name)
367 {
368 const struct cmd *cm;
369
370 for (cm = cmds; cm->fullname; cm++)
371 {
372 if (strcmp (cmd_name, cm->fullname) == 0)
373 break;
374 }
375 if (!cm->fullname)
376 error (1, 0, "unknown command: %s", cmd_name);
377 return cm->attr;
378 }
379
380
381
382 /*
383 * Exit with an error code and an informative message about the signal
384 * received. This function, by virtue of causing an actual call to exit(),
385 * causes all the atexit() handlers to be called.
386 *
387 * INPUTS
388 * sig The signal recieved.
389 *
390 * ERRORS
391 * The cleanup routines registered via atexit() and the error function
392 * itself can potentially change the exit status. They shouldn't do this
393 * unless they encounter problems doing their own jobs.
394 *
395 * RETURNS
396 * Nothing. This function will always exit. It should exit with an exit
397 * status of 1, but might not, as noted in the ERRORS section above.
398 */
399 #ifndef DONT_USE_SIGNALS
400 static RETSIGTYPE main_cleanup (int) __attribute__ ((__noreturn__));
401 #endif /* DONT_USE_SIGNALS */
402 static RETSIGTYPE
main_cleanup(int sig)403 main_cleanup (int sig)
404 {
405 #ifndef DONT_USE_SIGNALS
406 const char *name;
407 char temp[10];
408
409 switch (sig)
410 {
411 #ifdef SIGABRT
412 case SIGABRT:
413 name = "abort";
414 break;
415 #endif
416 #ifdef SIGHUP
417 case SIGHUP:
418 name = "hangup";
419 break;
420 #endif
421 #ifdef SIGINT
422 case SIGINT:
423 name = "interrupt";
424 break;
425 #endif
426 #ifdef SIGQUIT
427 case SIGQUIT:
428 name = "quit";
429 break;
430 #endif
431 #ifdef SIGPIPE
432 case SIGPIPE:
433 name = "broken pipe";
434 break;
435 #endif
436 #ifdef SIGTERM
437 case SIGTERM:
438 name = "termination";
439 break;
440 #endif
441 default:
442 /* This case should never be reached, because we list above all
443 the signals for which we actually establish a signal handler. */
444 sprintf (temp, "%d", sig);
445 name = temp;
446 break;
447 }
448
449 /* This always exits, which will cause our exit handlers to be called. */
450 error (1, 0, "received %s signal", name);
451 /* but make the exit explicit to silence warnings when gcc processes the
452 * noreturn attribute.
453 */
454 exit (EXIT_FAILURE);
455 #endif /* !DONT_USE_SIGNALS */
456 }
457
458
459
460 /* From server.c.
461 *
462 * When !defined ALLOW_CONFIG_OVERRIDE, this will never have any value but
463 * NULL.
464 */
465 extern char *gConfigPath;
466
467
468
469
470 enum {RANDOM_BYTES = 8};
471 enum {COMMITID_RAW_SIZE = (sizeof(time_t) + RANDOM_BYTES)};
472
473 static char const alphabet[62] =
474 "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
475
476
477 int
main(int argc,char ** argv)478 main (int argc, char **argv)
479 {
480 cvsroot_t *CVSroot_parsed = NULL;
481 bool cvsroot_update_env = true;
482 char *cp, *end;
483 const struct cmd *cm;
484 int c, err = 0;
485 int free_Editor = 0;
486
487 int help = 0; /* Has the user asked for help? This
488 lets us support the `cvs -H cmd'
489 convention to give help for cmd. */
490 static const char short_options[] = "+QqrwgtnRvb:T:e:d:Hfz:s:xal";
491 static struct option long_options[] =
492 {
493 {"help", 0, NULL, 'H'},
494 {"version", 0, NULL, 'v'},
495 {"help-commands", 0, NULL, 1},
496 {"help-synonyms", 0, NULL, 2},
497 {"help-options", 0, NULL, 4},
498 #ifdef SERVER_SUPPORT
499 {"allow-root", required_argument, NULL, 3},
500 #endif /* SERVER_SUPPORT */
501 {0, 0, 0, 0}
502 };
503 /* `getopt_long' stores the option index here, but right now we
504 don't use it. */
505 int option_index = 0;
506
507 #ifdef SYSTEM_INITIALIZE
508 /* Hook for OS-specific behavior, for example socket subsystems on
509 NT and OS2 or dealing with windows and arguments on Mac. */
510 SYSTEM_INITIALIZE (&argc, &argv);
511 #endif
512
513 #ifdef SYSTEM_CLEANUP
514 /* Hook for OS-specific behavior, for example socket subsystems on
515 NT and OS2 or dealing with windows and arguments on Mac. */
516 cleanup_register (SYSTEM_CLEANUP);
517 #endif
518
519 #ifdef HAVE_TZSET
520 /* On systems that have tzset (which is almost all the ones I know
521 of), it's a good idea to call it. */
522 tzset ();
523 #endif
524
525 /*
526 * Just save the last component of the path for error messages
527 */
528 program_path = xstrdup (argv[0]);
529 #ifdef ARGV0_NOT_PROGRAM_NAME
530 /* On some systems, e.g. VMS, argv[0] is not the name of the command
531 which the user types to invoke the program. */
532 program_name = "cvs";
533 #else
534 program_name = last_component (argv[0]);
535 #endif
536
537 /*
538 * Query the environment variables up-front, so that
539 * they can be overridden by command line arguments
540 */
541 if ((cp = getenv (EDITOR1_ENV)) != NULL)
542 Editor = cp;
543 else if ((cp = getenv (EDITOR2_ENV)) != NULL)
544 Editor = cp;
545 else if ((cp = getenv (EDITOR3_ENV)) != NULL)
546 Editor = cp;
547 if (getenv (CVSREAD_ENV) != NULL)
548 cvswrite = 0;
549 if (getenv (CVSREADONLYFS_ENV) != NULL) {
550 readonlyfs = 1;
551 logoff = 1;
552 }
553
554 /* Set this to 0 to force getopt initialization. getopt() sets
555 this to 1 internally. */
556 optind = 0;
557
558 /* We have to parse the options twice because else there is no
559 chance to avoid reading the global options from ".cvsrc". Set
560 opterr to 0 for avoiding error messages about invalid options.
561 */
562 opterr = 0;
563
564 while ((c = getopt_long
565 (argc, argv, short_options, long_options, &option_index))
566 != EOF)
567 {
568 if (c == 'f')
569 use_cvsrc = 0;
570 }
571
572 #ifdef SERVER_SUPPORT
573 /* Don't try and read a .cvsrc file if we are a server. */
574 if (optind < argc
575 && (false
576 # if defined (AUTH_SERVER_SUPPORT) || defined (HAVE_GSSAPI)
577 || !strcmp (argv[optind], "pserver")
578 # endif
579 # ifdef HAVE_KERBEROS
580 || !strcmp (argv[optind], "kserver")
581 # endif /* HAVE_KERBEROS */
582 || !strcmp (argv[optind], "server")))
583 {
584 /* Avoid any .cvsrc file. */
585 use_cvsrc = 0;
586 /* Pre-parse the server options to get the config path. */
587 cvs_cmd_name = argv[optind];
588 parseServerOptions (argc - optind, argv + optind);
589 }
590 #endif /* SERVER_SUPPORT */
591
592 /*
593 * Scan cvsrc file for global options.
594 */
595 if (use_cvsrc)
596 read_cvsrc (&argc, &argv, "cvs");
597
598 optind = 0;
599 opterr = 1;
600
601 while ((c = getopt_long
602 (argc, argv, short_options, long_options, &option_index))
603 != EOF)
604 {
605 switch (c)
606 {
607 case 1:
608 /* --help-commands */
609 usage (cmd_usage);
610 break;
611 case 2:
612 /* --help-synonyms */
613 usage (cmd_synonyms());
614 break;
615 case 4:
616 /* --help-options */
617 usage (opt_usage);
618 break;
619 #ifdef SERVER_SUPPORT
620 case 3:
621 /* --allow-root */
622 root_allow_add (optarg, gConfigPath);
623 break;
624 #endif /* SERVER_SUPPORT */
625 case 'Q':
626 really_quiet = 1;
627 /* FALL THROUGH */
628 case 'q':
629 quiet = 1;
630 break;
631 case 'r':
632 cvswrite = 0;
633 break;
634 case 'w':
635 cvswrite = 1;
636 break;
637 case 'g':
638 /*
639 * Force full write permissions for the group.
640 * See the user's manual for details and dangers.
641 */
642 umask(umask(S_IRWXG|S_IRWXO) & S_IRWXO);
643 break;
644 case 't':
645 trace++;
646 break;
647 case 'R':
648 readonlyfs = -1;
649 logoff = 1;
650 break;
651 case 'n':
652 noexec = 1;
653 logoff = 1;
654 break;
655 case 'l':
656 /* no-op to simply ignore the old -l option */
657 break;
658 case 'v':
659 (void) fputs ("\n", stdout);
660 version (0, NULL);
661 (void) fputs ("\n", stdout);
662 (void) fputs ("\
663 Copyright (C) 2005 Free Software Foundation, Inc.\n\
664 \n\
665 Portions contributed by Thorsten Glaser for the MirOS Project.\n\
666 Senior active maintainers include Larry Jones, Derek R. Price,\n\
667 and Mark D. Baushke. Please see the AUTHORS and README files from the CVS\n\
668 distribution kit for a complete list of contributors and copyrights.\n",
669 stdout);
670 (void) fputs ("\n", stdout);
671 (void) fputs ("CVS may be copied only under the terms of the GNU General Public License,\n", stdout);
672 (void) fputs ("a copy of which can be found with the CVS distribution kit.\n", stdout);
673 (void) fputs ("\n", stdout);
674
675 (void) fputs ("Specify the --help option for further information about CVS\n", stdout);
676
677 exit (0);
678 break;
679 case 'b':
680 /* This option used to specify the directory for RCS
681 executables. But since we don't run them any more,
682 this is a noop. Silently ignore it so that .cvsrc
683 and scripts and inetd.conf and such can work with
684 either new or old CVS. */
685 break;
686 case 'T':
687 if (tmpdir_cmdline) free (tmpdir_cmdline);
688 tmpdir_cmdline = xstrdup (optarg);
689 break;
690 case 'e':
691 if (free_Editor) free (Editor);
692 Editor = xstrdup (optarg);
693 free_Editor = 1;
694 break;
695 case 'd':
696 if (CVSroot_cmdline != NULL)
697 free (CVSroot_cmdline);
698 CVSroot_cmdline = xstrdup (optarg);
699 break;
700 case 'H':
701 help = 1;
702 break;
703 case 'f':
704 use_cvsrc = 0; /* unnecessary, since we've done it above */
705 break;
706 case 'z':
707 #ifdef CLIENT_SUPPORT
708 gzip_level = strtol (optarg, &end, 10);
709 if (*end != '\0' || gzip_level < 0 || gzip_level > 9)
710 error (1, 0,
711 "gzip compression level must be between 0 and 9");
712 #endif /* CLIENT_SUPPORT */
713 /* If no CLIENT_SUPPORT, we just silently ignore the gzip
714 * level, so that users can have it in their .cvsrc and not
715 * cause any trouble.
716 *
717 * We still parse the argument to -z for correctness since
718 * one user complained of being bitten by a run of
719 * `cvs -z -n up' which read -n as the argument to -z without
720 * complaining. */
721 break;
722 case 's':
723 variable_set (optarg);
724 break;
725 case 'x':
726 #ifdef CLIENT_SUPPORT
727 cvsencrypt = 1;
728 #endif /* CLIENT_SUPPORT */
729 /* If no CLIENT_SUPPORT, ignore -x, so that users can
730 have it in their .cvsrc and not cause any trouble.
731 If no ENCRYPTION, we still accept -x, but issue an
732 error if we are being run as a client. */
733 break;
734 case 'a':
735 #ifdef CLIENT_SUPPORT
736 cvsauthenticate = 1;
737 #endif
738 /* If no CLIENT_SUPPORT, ignore -a, so that users can
739 have it in their .cvsrc and not cause any trouble.
740 We will issue an error later if stream
741 authentication is not supported. */
742 break;
743 case '?':
744 default:
745 usage (usg);
746 }
747 }
748
749 argc -= optind;
750 argv += optind;
751 if (argc < 1)
752 usage (usg);
753
754 /* Calculate the cvs global session ID */
755
756 global_session_id = Xasprintf("1%010llX%04X%04X",
757 (unsigned long long)time(NULL),
758 (int)(getpid() & 0xFFFF), (int)(arc4random() & 0xFFFF));
759
760 TRACE (TRACE_FUNCTION, "main: Session ID is %s", global_session_id);
761
762 /* Look up the command name. */
763
764 cvs_cmd_name = argv[0];
765 for (cm = cmds; cm->fullname; cm++)
766 {
767 if (cm->nick1 && !strcmp (cvs_cmd_name, cm->nick1))
768 break;
769 if (cm->nick2 && !strcmp (cvs_cmd_name, cm->nick2))
770 break;
771 if (!strcmp (cvs_cmd_name, cm->fullname))
772 break;
773 }
774
775 if (!cm->fullname)
776 {
777 fprintf (stderr, "Unknown command: `%s'\n\n", cvs_cmd_name);
778 usage (cmd_usage);
779 }
780 else
781 cvs_cmd_name = cm->fullname; /* Global pointer for later use */
782
783 if (help)
784 {
785 argc = -1; /* some functions only check for this */
786 err = (*(cm->func)) (argc, argv);
787 }
788 else
789 {
790 /* The user didn't ask for help, so go ahead and authenticate,
791 set up CVSROOT, and the rest of it. */
792
793 short int lock_cleanup_setup = 0;
794
795 /* The UMASK environment variable isn't handled with the
796 others above, since we don't want to signal errors if the
797 user has asked for help. This won't work if somebody adds
798 a command-line flag to set the umask, since we'll have to
799 parse it before we get here. */
800
801 if ((cp = getenv (CVSUMASK_ENV)) != NULL)
802 {
803 /* FIXME: Should be accepting symbolic as well as numeric mask. */
804 cvsumask = strtol (cp, &end, 8) & 0777;
805 if (*end != '\0')
806 error (1, errno, "invalid umask value in %s (%s)",
807 CVSUMASK_ENV, cp);
808 }
809
810 /* HOSTNAME & SERVER_HOSTNAME need to be set before they are
811 * potentially used in gserver_authenticate_connection() (called from
812 * pserver_authenticate_connection, below).
813 */
814 hostname = xgethostname ();
815 if (!hostname)
816 {
817 error (0, errno,
818 "xgethostname () returned NULL, using \"localhost\"");
819 hostname = xstrdup ("localhost");
820 }
821
822 /* Keep track of this separately since the client can change
823 * HOSTNAME on the server.
824 */
825 server_hostname = xstrdup (hostname);
826
827 #ifdef SERVER_SUPPORT
828
829 # ifdef HAVE_KERBEROS
830 /* If we are invoked with a single argument "kserver", then we are
831 running as Kerberos server as root. Do the authentication as
832 the very first thing, to minimize the amount of time we are
833 running as root. */
834 if (strcmp (cvs_cmd_name, "kserver") == 0)
835 {
836 kserver_authenticate_connection ();
837
838 /* Pretend we were invoked as a plain server. */
839 cvs_cmd_name = "server";
840 }
841 # endif /* HAVE_KERBEROS */
842
843 # if defined (AUTH_SERVER_SUPPORT) || defined (HAVE_GSSAPI)
844 if (strcmp (cvs_cmd_name, "pserver") == 0)
845 {
846 /* The reason that --allow-root is not a command option
847 is mainly that it seems easier to make it a global option. */
848
849 /* Gets username and password from client, authenticates, then
850 switches to run as that user and sends an ACK back to the
851 client. */
852 pserver_authenticate_connection ();
853
854 /* Pretend we were invoked as a plain server. */
855 cvs_cmd_name = "server";
856 }
857 # endif /* AUTH_SERVER_SUPPORT || HAVE_GSSAPI */
858 #endif /* SERVER_SUPPORT */
859
860 server_active = strcmp (cvs_cmd_name, "server") == 0;
861
862 #ifdef SERVER_SUPPORT
863 if (server_active)
864 {
865 /* This is only used for writing into the history file. For
866 remote connections, it might be nice to have hostname
867 and/or remote path, on the other hand I'm not sure whether
868 it is worth the trouble. */
869 CurDir = xstrdup ("<remote>");
870 cleanup_register (server_cleanup);
871 }
872 else
873 #endif
874 {
875 cleanup_register (close_stdout);
876 CurDir = xgetcwd ();
877 if (CurDir == NULL)
878 error (1, errno, "cannot get working directory");
879 }
880
881 {
882 char *val;
883 /* XXX pid < 10^32 */
884 val = Xasprintf ("%ld", (long) getpid ());
885 setenv (CVS_PID_ENV, val, 1);
886 free (val);
887 }
888
889 /* make sure we clean up on error */
890 signals_register (main_cleanup);
891
892 #ifdef KLUDGE_FOR_WNT_TESTSUITE
893 /* Probably the need for this will go away at some point once
894 we call fflush enough places (e.g. fflush (stdout) in
895 cvs_outerr). */
896 (void) setvbuf (stdout, NULL, _IONBF, 0);
897 (void) setvbuf (stderr, NULL, _IONBF, 0);
898 #endif /* KLUDGE_FOR_WNT_TESTSUITE */
899
900 if (use_cvsrc)
901 read_cvsrc (&argc, &argv, cvs_cmd_name);
902
903 /* Fiddling with CVSROOT doesn't make sense if we're running
904 * in server mode, since the client will send the repository
905 * directory after the connection is made.
906 */
907 if (!server_active)
908 {
909 /* First check if a root was set via the command line. */
910 if (CVSroot_cmdline)
911 {
912 if (!(CVSroot_parsed = parse_cvsroot (CVSroot_cmdline)))
913 error (1, 0, "Bad CVSROOT: `%s'.", CVSroot_cmdline);
914 }
915
916 /* See if we are able to find a 'better' value for CVSroot
917 * in the CVSADM_ROOT directory.
918 *
919 * "cvs import" shouldn't check CVS/Root; in general it
920 * ignores CVS directories and CVS/Root is likely to
921 * specify a different repository than the one we are
922 * importing to, but if this is not import and no root was
923 * specified on the command line, set the root from the
924 * CVS/Root file.
925 */
926 if (!CVSroot_parsed
927 && !(cm->attr & CVS_CMD_IGNORE_ADMROOT)
928 )
929 CVSroot_parsed = Name_Root (NULL, NULL);
930
931 /* Now, if there is no root on the command line and we didn't find
932 * one in a file, set it via the $CVSROOT env var.
933 */
934 if (!CVSroot_parsed)
935 {
936 char *tmp = getenv (CVSROOT_ENV);
937 if (tmp)
938 {
939 if (!(CVSroot_parsed = parse_cvsroot (tmp)))
940 error (1, 0, "Bad CVSROOT: `%s'.", tmp);
941 cvsroot_update_env = false;
942 }
943 }
944
945 #ifdef CVSROOT_DFLT
946 if (!CVSroot_parsed)
947 {
948 if (!(CVSroot_parsed = parse_cvsroot (CVSROOT_DFLT)))
949 error (1, 0, "Bad CVSROOT: `%s'.", CVSROOT_DFLT);
950 }
951 #endif /* CVSROOT_DFLT */
952
953 /* Now we've reconciled CVSROOT from the command line, the
954 CVS/Root file, and the environment variable. Do the
955 last sanity checks on the variable. */
956 if (!CVSroot_parsed && cm->func != version)
957 {
958 error (0, 0,
959 "No CVSROOT specified! Please use the `-d' option");
960 error (1, 0,
961 "or set the %s environment variable.", CVSROOT_ENV);
962 }
963 }
964
965 /* Here begins the big loop over unique cvsroot values. We
966 need to call do_recursion once for each unique value found
967 in CVS/Root. Prime the list with the current value. */
968
969 /* Create the list. */
970 assert (root_directories == NULL);
971 root_directories = getlist ();
972
973 /* Prime it. */
974 if (CVSroot_parsed)
975 {
976 Node *n;
977 n = getnode ();
978 n->type = NT_UNKNOWN;
979 n->key = xstrdup (CVSroot_parsed->original);
980 n->data = CVSroot_parsed;
981
982 if (addnode (root_directories, n))
983 error (1, 0, "cannot add initial CVSROOT %s", n->key);
984 }
985
986 assert (current_parsed_root == NULL);
987
988 /* Handle running 'cvs version' with no CVSROOT. */
989
990 if (cm->func == version && !CVSroot_parsed)
991 server_active = !0;
992
993 /* If we're running the server, we want to execute this main
994 loop once and only once (we won't be serving multiple roots
995 from this connection, so there's no need to do it more than
996 once). To get out of the loop, we perform a "break" at the
997 end of things. */
998
999 while (server_active ||
1000 walklist (root_directories, set_root_directory, NULL))
1001 {
1002 /* Fiddling with CVSROOT doesn't make sense if we're running
1003 in server mode, since the client will send the repository
1004 directory after the connection is made. */
1005
1006 if (!server_active)
1007 {
1008 /* Now we're 100% sure that we have a valid CVSROOT
1009 variable. Parse it to see if we're supposed to do
1010 remote accesses or use a special access method. */
1011
1012 TRACE (TRACE_FUNCTION,
1013 "main loop with CVSROOT=%s",
1014 current_parsed_root ? current_parsed_root->directory
1015 : "(null)");
1016
1017 /*
1018 * Check to see if the repository exists.
1019 */
1020 if (!current_parsed_root->isremote)
1021 {
1022 char *path;
1023 int save_errno;
1024
1025 path = Xasprintf ("%s/%s", current_parsed_root->directory,
1026 CVSROOTADM);
1027 if (!isaccessible (path, R_OK | X_OK))
1028 {
1029 save_errno = errno;
1030 /* If this is "cvs init", the root need not exist yet.
1031 */
1032 if (strcmp (cvs_cmd_name, "init"))
1033 error (1, save_errno, "%s", path);
1034 }
1035 free (path);
1036 }
1037
1038 /* Update the CVSROOT environment variable. */
1039 if (cvsroot_update_env)
1040 setenv (CVSROOT_ENV, current_parsed_root->original, 1);
1041 }
1042
1043 /* Parse the CVSROOT/config file, but only for local. For the
1044 server, we parse it after we know $CVSROOT. For the
1045 client, it doesn't get parsed at all, obviously. The
1046 presence of the parse_config call here is not meant to
1047 predetermine whether CVSROOT/config overrides things from
1048 read_cvsrc and other such places or vice versa. That sort
1049 of thing probably needs more thought. */
1050 if (!server_active && !current_parsed_root->isremote)
1051 {
1052 /* If there was an error parsing the config file, parse_config
1053 already printed an error. We keep going. Why? Because
1054 if we didn't, then there would be no way to check in a new
1055 CVSROOT/config file to fix the broken one! */
1056 if (config) free_config (config);
1057 config = parse_config (current_parsed_root->directory, NULL);
1058
1059 /* Can set TMPDIR in the environment if necessary now, since
1060 * if it was set in config, we now know it.
1061 */
1062 push_env_temp_dir ();
1063 }
1064
1065 #ifdef CLIENT_SUPPORT
1066 /* Need to check for current_parsed_root != NULL here since
1067 * we could still be in server mode before the server function
1068 * gets called below and sets the root
1069 */
1070 if (current_parsed_root != NULL && current_parsed_root->isremote)
1071 {
1072 /* Create a new list for directory names that we've
1073 sent to the server. */
1074 if (dirs_sent_to_server != NULL)
1075 dellist (&dirs_sent_to_server);
1076 dirs_sent_to_server = getlist ();
1077 }
1078 #endif
1079
1080 if (
1081 #ifdef SERVER_SUPPORT
1082 /* Don't worry about lock_cleanup_setup when the server is
1083 * active since we can only go through this loop once in that
1084 * case anyhow.
1085 */
1086 server_active ||
1087 #endif
1088 (
1089 #ifdef CLIENT_SUPPORT
1090 !current_parsed_root->isremote &&
1091 #endif
1092 !lock_cleanup_setup))
1093 {
1094 /* Set up to clean up any locks we might create on exit. */
1095 cleanup_register (Lock_Cleanup);
1096 lock_cleanup_setup = 1;
1097 }
1098
1099 /* Call our worker function. */
1100 err = (*(cm->func)) (argc, argv);
1101
1102 /* Mark this root directory as done. When the server is
1103 active, our list will be empty -- don't try and
1104 remove it from the list. */
1105
1106 if (!server_active)
1107 {
1108 Node *n = findnode (root_directories,
1109 original_parsed_root->original);
1110 assert (n != NULL);
1111 assert (n->data != NULL);
1112 n->data = NULL;
1113 current_parsed_root = NULL;
1114 }
1115
1116 if (server_active)
1117 break;
1118 } /* end of loop for cvsroot values */
1119
1120 dellist (&root_directories);
1121 } /* end of stuff that gets done if the user DOESN'T ask for help */
1122
1123 root_allow_free ();
1124
1125 /* This is exit rather than return because apparently that keeps
1126 some tools which check for memory leaks happier. */
1127 exit (err ? EXIT_FAILURE : 0);
1128 /* Keep picky/stupid compilers (e.g. Visual C++ 5.0) happy. */
1129 return 0;
1130 }
1131
1132
1133
1134 char *
Make_Date(const char * rawdate)1135 Make_Date (const char *rawdate)
1136 {
1137 struct timespec t;
1138
1139 if (!get_date (&t, rawdate, NULL))
1140 error (1, 0, "Can't parse date/time: `%s'", rawdate);
1141
1142 /* Truncate nanoseconds. */
1143 return date_from_time_t (t.tv_sec);
1144 }
1145
1146
1147
1148 /* Parse a string of the form TAG[:DATE], where TAG could be the empty string.
1149 *
1150 * INPUTS
1151 * input The string to be parsed.
1152 *
1153 * OUTPUTS
1154 * tag The tag found, if any. If TAG is the empty string, then leave
1155 * this value unchanged.
1156 * date The date found, if any. If DATE is the empty string or is
1157 * missing, leave this value unchanged.
1158 *
1159 * NOTES
1160 * If either TAG or DATE is replaced for output, the previous value is freed.
1161 *
1162 * ERRORS
1163 * If either TAG or DATE cannot be parsed, then this function will exit with
1164 * a fatal error message.
1165 *
1166 * RETURNS
1167 * Nothing.
1168 */
1169 void
parse_tagdate(char ** tag,char ** date,const char * input)1170 parse_tagdate (char **tag, char **date, const char *input)
1171 {
1172 char *p;
1173
1174 TRACE (TRACE_FUNCTION, "parse_tagdate (%s, %s, %s)",
1175 *tag ? *tag : "(null)", *date ? *date : "(null)",
1176 input);
1177
1178 if ((p = strchr (input, ':')))
1179 {
1180 /* Parse the tag. */
1181 if (p - input)
1182 {
1183 /* The tag has > 0 length. */
1184 if (*tag) free (*tag);
1185 *tag = xmalloc (p - input + 1);
1186 strncpy (*tag, input, p - input);
1187 (*tag)[p - input] = '\0';
1188 }
1189
1190 /* Parse the date. */
1191 if (*++p)
1192 {
1193 if (*date) free (*date);
1194 *date = strcmp (p, "BASE") ? Make_Date (p) : xstrdup (p);
1195 }
1196 }
1197 else if (strlen (input))
1198 {
1199 /* The tag has > 0 length. */
1200 if (*tag) free (*tag);
1201 *tag = xstrdup (input);
1202 }
1203
1204 TRACE (TRACE_DATA, "parse_tagdate: got tag = `%s', date = `%s'",
1205 *tag ? *tag : "(null)", *date ? *date : "(null)");
1206 }
1207
1208
1209
1210 /* Convert a time_t to an RCS format date. This is mainly for the
1211 use of "cvs history", because the CVSROOT/history file contains
1212 time_t format dates; most parts of CVS will want to avoid using
1213 time_t's directly, and instead use RCS_datecmp, Make_Date, &c.
1214 Assuming that the time_t is in GMT (as it generally should be),
1215 then the result will be in GMT too.
1216
1217 Returns a newly malloc'd string. */
1218
1219 char *
date_from_time_t(time_t unixtime)1220 date_from_time_t (time_t unixtime)
1221 {
1222 struct tm *ftm;
1223 char date[MAXDATELEN];
1224 char *ret;
1225
1226 ftm = gmtime (&unixtime);
1227 if (ftm == NULL)
1228 /* This is a system, like VMS, where the system clock is in local
1229 time. Hopefully using localtime here matches the "zero timezone"
1230 hack I added to get_date (get_date of course being the relevant
1231 issue for Make_Date, and for history.c too I think). */
1232 ftm = localtime (&unixtime);
1233
1234 (void) sprintf (date, DATEFORM,
1235 (long)ftm->tm_year + (ftm->tm_year < 100 ? 0L : 1900L),
1236 ftm->tm_mon + 1, ftm->tm_mday, ftm->tm_hour,
1237 ftm->tm_min, ftm->tm_sec);
1238 ret = xstrdup (date);
1239 return ret;
1240 }
1241
1242
1243
1244 /* Convert a date to RFC822/1123 format. This is used in contexts like
1245 dates to send in the protocol; it should not vary based on locale or
1246 other such conventions for users. We should have another routine which
1247 does that kind of thing.
1248
1249 The SOURCE date is in our internal RCS format. DEST should point to
1250 storage managed by the caller, at least MAXDATELEN characters. */
1251 void
date_to_internet(char * dest,const char * source)1252 date_to_internet (char *dest, const char *source)
1253 {
1254 struct tm date;
1255
1256 date_to_tm (&date, source);
1257 tm_to_internet (dest, &date);
1258 }
1259
1260
1261
1262 void
date_to_tm(struct tm * dest,const char * source)1263 date_to_tm (struct tm *dest, const char *source)
1264 {
1265 int y;
1266 if (sscanf (source, SDATEFORM,
1267 &y, &dest->tm_mon, &dest->tm_mday,
1268 &dest->tm_hour, &dest->tm_min, &dest->tm_sec)
1269 != 6)
1270 /* Is there a better way to handle errors here? I made this
1271 non-fatal in case we are called from the code which can't
1272 deal with fatal errors. */
1273 error (0, 0, "internal error: bad date %s", source);
1274
1275 dest->tm_year = y - ((y > 100) ? 1900 : 0);
1276 dest->tm_mon -= 1;
1277 }
1278
1279
1280
1281 /* Convert a date to RFC822/1123 format. This is used in contexts like
1282 dates to send in the protocol; it should not vary based on locale or
1283 other such conventions for users. We should have another routine which
1284 does that kind of thing.
1285
1286 The SOURCE date is a pointer to a struct tm. DEST should point to
1287 storage managed by the caller, at least MAXDATELEN characters. */
1288 void
tm_to_internet(char * dest,const struct tm * source)1289 tm_to_internet (char *dest, const struct tm *source)
1290 {
1291 /* Just to reiterate, these strings are from RFC822 and do not vary
1292 according to locale. */
1293 static const char *const month_names[] =
1294 {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
1295 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
1296
1297 sprintf (dest, "%d %s %ld %02d:%02d:%02d -0000", source->tm_mday,
1298 source->tm_mon < 0 || source->tm_mon > 11
1299 ? "???" : month_names[source->tm_mon],
1300 (long)source->tm_year + 1900, source->tm_hour, source->tm_min,
1301 source->tm_sec);
1302 }
1303
1304
1305
1306 /*
1307 * Format a date for the current locale.
1308 *
1309 * INPUT
1310 * UNIXTIME The UNIX seconds since the epoch.
1311 *
1312 * RETURNS
1313 * If my_strftime() encounters an error, this function can return NULL.
1314 *
1315 * Otherwise, returns a date string in ISO8601 format, e.g.:
1316 *
1317 * 2004-04-29 13:24:22 -0700
1318 *
1319 * It is the responsibility of the caller to return of this string.
1320 */
1321 static char *
format_time_t(time_t unixtime)1322 format_time_t (time_t unixtime)
1323 {
1324 static char buf[sizeof ("yyyy-mm-dd HH:MM:SS -HHMM")];
1325 /* Convert to a time in the local time zone. */
1326 struct tm ltm = *(localtime (&unixtime));
1327
1328 if (!my_strftime (buf, sizeof (buf), "%Y-%m-%d %H:%M:%S %z", <m, 0, 0))
1329 return NULL;
1330
1331 return xstrdup (buf);
1332 }
1333
1334
1335
1336 /* Like format_time_t(), but return time in UTC.
1337 */
1338 char *
gmformat_time_t(time_t unixtime)1339 gmformat_time_t (time_t unixtime)
1340 {
1341 static char buf[sizeof ("yyyy-mm-dd HH:MM:SS -HHMM")];
1342 /* Convert to a time in the local time zone. */
1343 struct tm ltm = *(gmtime (&unixtime));
1344
1345 if (!my_strftime (buf, sizeof (buf), "%Y-%m-%d %H:%M:%S %z", <m, 0, 0))
1346 return NULL;
1347
1348 return xstrdup (buf);
1349 }
1350
1351
1352
1353 /* Format a date in the local timezone using format_time_t() given a date from
1354 * an arbitrary timezone in a string.
1355 *
1356 * INPUT
1357 * DATESTR A string that looks like anything get_date() can parse, e.g.:
1358 *
1359 * 2004-04-29 20:24:22
1360 *
1361 * ERRORS
1362 * As get_date() & format_time_t(). Prints a warning if either provide
1363 * error return values. See RETURNS.
1364 *
1365 * RETURNS
1366 * A freshly allocated string that is a copy of the input string if either
1367 * get_date() or format_time_t() encounter an error and as format_time_t()
1368 * otherwise.
1369 */
1370 char *
format_date_alloc(char * datestr)1371 format_date_alloc (char *datestr)
1372 {
1373 struct timespec t;
1374 char *buf;
1375
1376 TRACE (TRACE_FUNCTION, "format_date (%s)", datestr);
1377
1378 /* Convert the date string to seconds since the epoch. */
1379 if (!get_date (&t, datestr, NULL))
1380 {
1381 error (0, 0, "Can't parse date/time: `%s'.", datestr);
1382 goto as_is;
1383 }
1384
1385 /* Get the time into a string, truncating any nanoseconds returned by
1386 * getdate.
1387 */
1388 if ((buf = format_time_t (t.tv_sec)) == NULL)
1389 {
1390 error (0, 0, "Unable to reformat date `%s'.", datestr);
1391 goto as_is;
1392 }
1393
1394 return buf;
1395
1396 as_is:
1397 return xstrdup (datestr);
1398 }
1399
1400
1401
1402 void
usage(register const char * const * cpp)1403 usage (register const char *const *cpp)
1404 {
1405 (void) fprintf (stderr, *cpp++, program_name, cvs_cmd_name);
1406 for (; *cpp; cpp++)
1407 (void) fprintf (stderr, "%s", *cpp);
1408 exit (EXIT_FAILURE);
1409 }
1410
1411 /* vim:tabstop=8:shiftwidth=4
1412 */
1413