Annotation of src/usr.bin/cvs/cvs.c, Revision 1.1
1.1 ! jfb 1: #define DEBUG
! 2: /* $OpenBSD$ */
! 3: /*
! 4: * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
! 5: * All rights reserved.
! 6: *
! 7: * Redistribution and use in source and binary forms, with or without
! 8: * modification, are permitted provided that the following conditions
! 9: * are met:
! 10: *
! 11: * 1. Redistributions of source code must retain the above copyright
! 12: * notice, this list of conditions and the following disclaimer.
! 13: * 2. The name of the author may not be used to endorse or promote products
! 14: * derived from this software without specific prior written permission.
! 15: *
! 16: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
! 17: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
! 18: * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
! 19: * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
! 20: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
! 21: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
! 22: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
! 23: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
! 24: * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
! 25: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
! 26: */
! 27:
! 28: #include <sys/types.h>
! 29: #include <sys/wait.h>
! 30:
! 31: #include <err.h>
! 32: #include <pwd.h>
! 33: #include <errno.h>
! 34: #include <stdio.h>
! 35: #include <stdlib.h>
! 36: #include <unistd.h>
! 37: #include <signal.h>
! 38: #include <string.h>
! 39: #include <sysexits.h>
! 40:
! 41: #include "cvs.h"
! 42: #include "log.h"
! 43:
! 44:
! 45: extern char *__progname;
! 46:
! 47:
! 48: /* verbosity level: 0 = really quiet, 1 = quiet, 2 = verbose */
! 49: int verbosity = 2;
! 50:
! 51:
! 52:
! 53: /* compression level used with zlib, 0 meaning no compression taking place */
! 54: int cvs_compress = 0;
! 55: int cvs_trace = 0;
! 56: int cvs_nolog = 0;
! 57: int cvs_readonly = 0;
! 58:
! 59: /* name of the command we are running */
! 60: char *cvs_command;
! 61: char *cvs_rootstr;
! 62: char *cvs_rsh = CVS_RSH_DEFAULT;
! 63: char *cvs_editor = CVS_EDITOR_DEFAULT;
! 64:
! 65: struct cvsroot *cvs_root = NULL;
! 66:
! 67:
! 68: /*
! 69: * Command dispatch table
! 70: * ----------------------
! 71: *
! 72: * The synopsis field should only contain the list of arguments that the
! 73: * command supports, without the actual command's name.
! 74: *
! 75: * Command handlers are expected to return 0 if no error occured, or one of
! 76: * the values known in sysexits.h in case of an error. In case the error
! 77: * returned is EX_USAGE, the command's usage string is printed to standard
! 78: * error before returning.
! 79: */
! 80:
! 81: static struct cvs_cmd {
! 82: char cmd_name[CVS_CMD_MAXNAMELEN];
! 83: char cmd_alias[CVS_CMD_MAXALIAS][CVS_CMD_MAXNAMELEN];
! 84: int (*cmd_hdlr)(int, char **);
! 85: char *cmd_synopsis;
! 86: char cmd_descr[CVS_CMD_MAXDESCRLEN];
! 87: } cvs_cdt[] = {
! 88: {
! 89: "add", { "ad", "new" }, cvs_add,
! 90: "[-m msg] file ...",
! 91: "Add a new file/directory to the repository",
! 92: },
! 93: {
! 94: "admin", { "adm", "rcs" }, NULL,
! 95: "",
! 96: "Administration front end for rcs",
! 97: },
! 98: {
! 99: "annotate", { "ann" }, NULL,
! 100: "",
! 101: "Show last revision where each line was modified",
! 102: },
! 103: {
! 104: "checkout", { "co", "get" }, NULL,
! 105: "",
! 106: "Checkout sources for editing",
! 107: },
! 108: {
! 109: "commit", { "ci", "com" }, cvs_commit,
! 110: "",
! 111: "Check files into the repository",
! 112: },
! 113: {
! 114: "diff", { "di", "dif" }, cvs_diff,
! 115: "",
! 116: "Show differences between revisions",
! 117: },
! 118: {
! 119: "edit", { }, NULL,
! 120: "",
! 121: "Get ready to edit a watched file",
! 122: },
! 123: {
! 124: "editors", { }, NULL,
! 125: "",
! 126: "See who is editing a watched file",
! 127: },
! 128: {
! 129: "export", { "ex", "exp" }, NULL,
! 130: "",
! 131: "Export sources from CVS, similar to checkout",
! 132: },
! 133: {
! 134: "history", { "hi", "his" }, cvs_history,
! 135: "",
! 136: "Show repository access history",
! 137: },
! 138: {
! 139: "import", { "im", "imp" }, NULL,
! 140: "",
! 141: "Import sources into CVS, using vendor branches",
! 142: },
! 143: {
! 144: "init", { }, cvs_init,
! 145: "",
! 146: "Create a CVS repository if it doesn't exist",
! 147: },
! 148: #if defined(HAVE_KERBEROS)
! 149: {
! 150: "kserver", {}, NULL
! 151: "",
! 152: "Start a Kerberos authentication CVS server",
! 153: },
! 154: #endif
! 155: {
! 156: "log", { "lo" }, cvs_getlog,
! 157: "",
! 158: "Print out history information for files",
! 159: },
! 160: {
! 161: "login", {}, NULL,
! 162: "",
! 163: "Prompt for password for authenticating server",
! 164: },
! 165: {
! 166: "logout", {}, NULL,
! 167: "",
! 168: "Removes entry in .cvspass for remote repository",
! 169: },
! 170: {
! 171: "rdiff", {}, NULL,
! 172: "",
! 173: "Create 'patch' format diffs between releases",
! 174: },
! 175: {
! 176: "release", {}, NULL,
! 177: "",
! 178: "Indicate that a Module is no longer in use",
! 179: },
! 180: {
! 181: "remove", {}, NULL,
! 182: "",
! 183: "Remove an entry from the repository",
! 184: },
! 185: {
! 186: "rlog", {}, NULL,
! 187: "",
! 188: "Print out history information for a module",
! 189: },
! 190: {
! 191: "rtag", {}, NULL,
! 192: "",
! 193: "Add a symbolic tag to a module",
! 194: },
! 195: {
! 196: "server", {}, cvs_server,
! 197: "",
! 198: "Server mode",
! 199: },
! 200: {
! 201: "status", {}, NULL,
! 202: "",
! 203: "Display status information on checked out files",
! 204: },
! 205: {
! 206: "tag", { "ta", }, NULL,
! 207: "",
! 208: "Add a symbolic tag to checked out version of files",
! 209: },
! 210: {
! 211: "unedit", {}, NULL,
! 212: "",
! 213: "Undo an edit command",
! 214: },
! 215: {
! 216: "update", {}, cvs_update,
! 217: "",
! 218: "Bring work tree in sync with repository",
! 219: },
! 220: {
! 221: "version", {}, cvs_version,
! 222: "",
! 223: "Show current CVS version(s)",
! 224: },
! 225: {
! 226: "watch", {}, NULL,
! 227: "",
! 228: "Set watches",
! 229: },
! 230: {
! 231: "watchers", {}, NULL,
! 232: "",
! 233: "See who is watching a file",
! 234: },
! 235: };
! 236:
! 237: #define CVS_NBCMD (sizeof(cvs_cdt)/sizeof(cvs_cdt[0]))
! 238:
! 239:
! 240:
! 241: void usage (void);
! 242: void sigchld_hdlr (int);
! 243: void cvs_readrc (void);
! 244: struct cvs_cmd* cvs_findcmd (const char *);
! 245:
! 246:
! 247:
! 248: /*
! 249: * sigchld_hdlr()
! 250: *
! 251: * Handler for the SIGCHLD signal, which can be received in case we are
! 252: * running a remote server and it dies.
! 253: */
! 254:
! 255: void
! 256: sigchld_hdlr(int signo)
! 257: {
! 258: int status;
! 259: pid_t pid;
! 260:
! 261: if ((pid = wait(&status)) == -1) {
! 262: }
! 263: }
! 264:
! 265:
! 266: /*
! 267: * usage()
! 268: *
! 269: * Display usage information.
! 270: */
! 271:
! 272: void
! 273: usage(void)
! 274: {
! 275: fprintf(stderr,
! 276: "Usage: %s [-lQqtv] [-d root] [-e editor] [-z level] "
! 277: "command [options] ...\n",
! 278: __progname);
! 279: }
! 280:
! 281:
! 282: int
! 283: main(int argc, char **argv)
! 284: {
! 285: char *envstr, *ep;
! 286: int ret;
! 287: u_int i, readrc;
! 288: struct cvs_cmd *cmdp;
! 289:
! 290: readrc = 1;
! 291:
! 292: if (cvs_log_init(LD_STD, 0) < 0)
! 293: err(1, "failed to initialize logging");
! 294:
! 295: /* by default, be very verbose */
! 296: (void)cvs_log_filter(LP_FILTER_UNSET, LP_INFO);
! 297:
! 298: #ifdef DEBUG
! 299: (void)cvs_log_filter(LP_FILTER_UNSET, LP_DEBUG);
! 300: #endif
! 301:
! 302: /* check environment so command-line options override it */
! 303: if ((envstr = getenv("CVS_RSH")) != NULL)
! 304: cvs_rsh = envstr;
! 305:
! 306: if (((envstr = getenv("CVSEDITOR")) != NULL) ||
! 307: ((envstr = getenv("VISUAL")) != NULL) ||
! 308: ((envstr = getenv("EDITOR")) != NULL))
! 309: cvs_editor = envstr;
! 310:
! 311: while ((ret = getopt(argc, argv, "d:e:fHlnQqrtvz:")) != -1) {
! 312: switch (ret) {
! 313: case 'd':
! 314: cvs_rootstr = optarg;
! 315: break;
! 316: case 'e':
! 317: cvs_editor = optarg;
! 318: break;
! 319: case 'f':
! 320: readrc = 0;
! 321: break;
! 322: case 'l':
! 323: cvs_nolog = 1;
! 324: break;
! 325: case 'n':
! 326: break;
! 327: case 'Q':
! 328: verbosity = 0;
! 329: break;
! 330: case 'q':
! 331: /* don't override -Q */
! 332: if (verbosity > 1)
! 333: verbosity = 1;
! 334: break;
! 335: case 'r':
! 336: cvs_readonly = 1;
! 337: break;
! 338: case 't':
! 339: cvs_trace = 1;
! 340: break;
! 341: case 'v':
! 342: printf("%s\n", CVS_VERSION);
! 343: exit(0);
! 344: /* NOTREACHED */
! 345: break;
! 346: case 'z':
! 347: cvs_compress = (int)strtol(optarg, &ep, 10);
! 348: if (*ep != '\0')
! 349: errx(1, "error parsing compression level");
! 350: if (cvs_compress < 0 || cvs_compress > 9)
! 351: errx(1, "gzip compression level must be "
! 352: "between 0 and 9");
! 353: break;
! 354: default:
! 355: usage();
! 356: exit(EX_USAGE);
! 357: }
! 358: }
! 359:
! 360: argc -= optind;
! 361: argv += optind;
! 362:
! 363: /* reset getopt() for use by commands */
! 364: optind = 1;
! 365: optreset = 1;
! 366:
! 367: if (argc == 0) {
! 368: usage();
! 369: exit(EX_USAGE);
! 370: }
! 371:
! 372: /* setup signal handlers */
! 373: signal(SIGCHLD, sigchld_hdlr);
! 374:
! 375: if (readrc)
! 376: cvs_readrc();
! 377:
! 378: cvs_command = argv[0];
! 379: ret = -1;
! 380:
! 381: cmdp = cvs_findcmd(cvs_command);
! 382: if (cmdp == NULL) {
! 383: fprintf(stderr, "Unknown command: `%s'\n\n", cvs_command);
! 384: fprintf(stderr, "CVS commands are:\n");
! 385: for (i = 0; i < CVS_NBCMD; i++)
! 386: fprintf(stderr, "\t%-16s%s\n",
! 387: cvs_cdt[i].cmd_name, cvs_cdt[i].cmd_descr);
! 388: exit(EX_USAGE);
! 389: }
! 390:
! 391: if (cmdp->cmd_hdlr == NULL) {
! 392: cvs_log(LP_ERR, "command `%s' not implemented", cvs_command);
! 393: exit(1);
! 394: }
! 395:
! 396: ret = (*cmdp->cmd_hdlr)(argc, argv);
! 397: if (ret == EX_USAGE) {
! 398: fprintf(stderr, "Usage: %s %s %s\n", __progname, cvs_command,
! 399: cmdp->cmd_synopsis);
! 400: }
! 401:
! 402: return (ret);
! 403: }
! 404:
! 405:
! 406: /*
! 407: * cvs_findcmd()
! 408: *
! 409: * Find the entry in the command dispatch table whose name or one of its
! 410: * aliases matches <cmd>.
! 411: * Returns a pointer to the command entry on success, NULL on failure.
! 412: */
! 413:
! 414: struct cvs_cmd*
! 415: cvs_findcmd(const char *cmd)
! 416: {
! 417: u_int i, j;
! 418: struct cvs_cmd *cmdp;
! 419:
! 420: cmdp = NULL;
! 421:
! 422: for (i = 0; (i < CVS_NBCMD) && (cmdp == NULL); i++) {
! 423: if (strcmp(cmd, cvs_cdt[i].cmd_name) == 0)
! 424: cmdp = &cvs_cdt[i];
! 425: else {
! 426: for (j = 0; j < CVS_CMD_MAXALIAS; j++) {
! 427: if (strcmp(cmd, cvs_cdt[i].cmd_alias[j]) == 0) {
! 428: cmdp = &cvs_cdt[i];
! 429: break;
! 430: }
! 431: }
! 432: }
! 433: }
! 434:
! 435: return (cmdp);
! 436: }
! 437:
! 438:
! 439: /*
! 440: * cvs_readrc()
! 441: *
! 442: * Read the CVS `.cvsrc' file in the user's home directory. If the file
! 443: * exists, it should contain a list of arguments that should always be given
! 444: * implicitly to the specified commands.
! 445: */
! 446:
! 447: void
! 448: cvs_readrc(void)
! 449: {
! 450: char rcpath[MAXPATHLEN], linebuf[128], *lp;
! 451: struct cvs_cmd *cmdp;
! 452: struct passwd *pw;
! 453: FILE *fp;
! 454:
! 455: pw = getpwuid(getuid());
! 456: if (pw == NULL) {
! 457: cvs_log(LP_NOTICE, "failed to get user's password entry");
! 458: return;
! 459: }
! 460:
! 461: snprintf(rcpath, sizeof(rcpath), "%s/%s", pw->pw_dir, CVS_PATH_RC);
! 462:
! 463: fp = fopen(rcpath, "r");
! 464: if (fp == NULL) {
! 465: if (errno != ENOENT)
! 466: cvs_log(LP_NOTICE, "failed to open `%s': %s", rcpath,
! 467: strerror(errno));
! 468: return;
! 469: }
! 470:
! 471: while (fgets(linebuf, sizeof(linebuf), fp) != NULL) {
! 472: lp = strchr(linebuf, ' ');
! 473:
! 474: /* ignore lines with no arguments */
! 475: if (lp == NULL)
! 476: continue;
! 477:
! 478: *(lp++) = '\0';
! 479: if (strcmp(linebuf, "cvs") == 0) {
! 480: /* global options */
! 481: }
! 482: else {
! 483: cmdp = cvs_findcmd(linebuf);
! 484: if (cmdp == NULL) {
! 485: cvs_log(LP_NOTICE,
! 486: "unknown command `%s' in cvsrc",
! 487: linebuf);
! 488: continue;
! 489: }
! 490: }
! 491: }
! 492: if (ferror(fp)) {
! 493: cvs_log(LP_NOTICE, "failed to read line from cvsrc");
! 494: }
! 495:
! 496: (void)fclose(fp);
! 497: }