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