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