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