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