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