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