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