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 * 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 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* 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 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 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 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 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 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 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 * 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 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 * 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 * 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 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 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 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 * 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 * 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 * 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 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 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