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