Annotation of src/usr.bin/cvs/cvs.c, Revision 1.40
1.40 ! jfb 1: /* $OpenBSD: cvs.c,v 1.39 2005/02/03 23:00:42 xsa Exp $ */
1.1 jfb 2: /*
3: * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
1.18 tedu 4: * All rights reserved.
1.1 jfb 5: *
1.18 tedu 6: * Redistribution and use in source and binary forms, with or without
7: * modification, are permitted provided that the following conditions
8: * are met:
1.1 jfb 9: *
1.18 tedu 10: * 1. Redistributions of source code must retain the above copyright
11: * notice, this list of conditions and the following disclaimer.
1.1 jfb 12: * 2. The name of the author may not be used to endorse or promote products
1.18 tedu 13: * derived from this software without specific prior written permission.
1.1 jfb 14: *
15: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
16: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
17: * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
18: * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23: * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
1.18 tedu 24: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1.1 jfb 25: */
26:
27: #include <sys/types.h>
28: #include <sys/wait.h>
29:
30: #include <err.h>
31: #include <pwd.h>
32: #include <errno.h>
33: #include <stdio.h>
1.27 jfb 34: #include <ctype.h>
1.1 jfb 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"
1.8 jfb 43: #include "file.h"
1.1 jfb 44:
45:
46: extern char *__progname;
47:
48:
49: /* verbosity level: 0 = really quiet, 1 = quiet, 2 = verbose */
50: int verbosity = 2;
51:
52: /* compression level used with zlib, 0 meaning no compression taking place */
53: int cvs_compress = 0;
1.17 jfb 54: int cvs_readrc = 1; /* read .cvsrc on startup */
1.1 jfb 55: int cvs_trace = 0;
56: int cvs_nolog = 0;
57: int cvs_readonly = 0;
1.17 jfb 58: int cvs_nocase = 0; /* set to 1 to disable filename case sensitivity */
1.1 jfb 59:
1.17 jfb 60: char *cvs_defargs; /* default global arguments from .cvsrc */
61: char *cvs_command; /* name of the command we are running */
1.6 jfb 62: int cvs_cmdop;
1.1 jfb 63: char *cvs_rootstr;
64: char *cvs_rsh = CVS_RSH_DEFAULT;
65: char *cvs_editor = CVS_EDITOR_DEFAULT;
66:
1.13 krapht 67: char *cvs_msg = NULL;
68:
1.10 jfb 69: /* hierarchy of all the files affected by the command */
70: CVSFILE *cvs_files;
71:
72:
1.27 jfb 73: static TAILQ_HEAD(, cvs_var) cvs_variables;
74:
1.1 jfb 75: /*
76: * Command dispatch table
77: * ----------------------
78: *
79: * The synopsis field should only contain the list of arguments that the
80: * command supports, without the actual command's name.
81: *
1.29 david 82: * Command handlers are expected to return 0 if no error occurred, or one of
1.1 jfb 83: * the values known in sysexits.h in case of an error. In case the error
84: * returned is EX_USAGE, the command's usage string is printed to standard
85: * error before returning.
86: */
87: static struct cvs_cmd {
1.6 jfb 88: int cmd_op;
1.1 jfb 89: char cmd_name[CVS_CMD_MAXNAMELEN];
90: char cmd_alias[CVS_CMD_MAXALIAS][CVS_CMD_MAXNAMELEN];
91: int (*cmd_hdlr)(int, char **);
92: char *cmd_synopsis;
1.13 krapht 93: char *cmd_opts;
1.1 jfb 94: char cmd_descr[CVS_CMD_MAXDESCRLEN];
1.17 jfb 95: char *cmd_defargs;
1.1 jfb 96: } cvs_cdt[] = {
97: {
1.6 jfb 98: CVS_OP_ADD, "add", { "ad", "new" }, cvs_add,
1.37 jfb 99: "[-k opt] [-m msg] file ...",
100: "k:m:",
1.1 jfb 101: "Add a new file/directory to the repository",
1.17 jfb 102: NULL,
1.1 jfb 103: },
104: {
1.36 jfb 105: CVS_OP_ADMIN, "admin", { "adm", "rcs" }, NULL,
1.1 jfb 106: "",
1.13 krapht 107: "",
1.1 jfb 108: "Administration front end for rcs",
1.17 jfb 109: NULL,
1.1 jfb 110: },
111: {
1.19 jfb 112: CVS_OP_ANNOTATE, "annotate", { "ann" }, cvs_annotate,
1.38 jfb 113: "[-flR] [-D date | -r rev] ...",
114: "D:flRr:",
1.1 jfb 115: "Show last revision where each line was modified",
1.17 jfb 116: NULL,
1.1 jfb 117: },
118: {
1.6 jfb 119: CVS_OP_CHECKOUT, "checkout", { "co", "get" }, cvs_checkout,
1.39 xsa 120: "[-AcflNnPpRs] [-D date | -r rev] [-d dir] [-j rev] [-k mode] "
1.38 jfb 121: "[-t id] module ...",
122: "AcD:d:fj:k:lNnPRr:st:",
1.1 jfb 123: "Checkout sources for editing",
1.17 jfb 124: NULL,
1.1 jfb 125: },
126: {
1.6 jfb 127: CVS_OP_COMMIT, "commit", { "ci", "com" }, cvs_commit,
1.4 jfb 128: "[-flR] [-F logfile | -m msg] [-r rev] ...",
1.13 krapht 129: "F:flm:Rr:",
1.1 jfb 130: "Check files into the repository",
1.17 jfb 131: NULL,
1.1 jfb 132: },
133: {
1.6 jfb 134: CVS_OP_DIFF, "diff", { "di", "dif" }, cvs_diff,
1.34 jfb 135: "[-cilNpu] [-D date] [-r rev] ...",
136: "cD:ilNpr:u",
1.1 jfb 137: "Show differences between revisions",
1.17 jfb 138: NULL,
1.1 jfb 139: },
140: {
1.36 jfb 141: CVS_OP_EDIT, "edit", { }, NULL,
1.1 jfb 142: "",
1.13 krapht 143: "",
1.1 jfb 144: "Get ready to edit a watched file",
1.17 jfb 145: NULL,
1.1 jfb 146: },
147: {
1.36 jfb 148: CVS_OP_EDITORS, "editors", { }, NULL,
1.1 jfb 149: "",
1.13 krapht 150: "",
1.1 jfb 151: "See who is editing a watched file",
1.17 jfb 152: NULL,
1.1 jfb 153: },
154: {
1.36 jfb 155: CVS_OP_EXPORT, "export", { "ex", "exp" }, NULL,
1.1 jfb 156: "",
1.13 krapht 157: "",
1.1 jfb 158: "Export sources from CVS, similar to checkout",
1.17 jfb 159: NULL,
1.1 jfb 160: },
161: {
1.6 jfb 162: CVS_OP_HISTORY, "history", { "hi", "his" }, cvs_history,
1.1 jfb 163: "",
1.13 krapht 164: "",
1.1 jfb 165: "Show repository access history",
1.17 jfb 166: NULL,
1.1 jfb 167: },
168: {
1.30 jfb 169: CVS_OP_IMPORT, "import", { "im", "imp" }, cvs_import,
1.13 krapht 170: "[-d] [-b branch] [-I ign] [-k subst] [-m msg] "
171: "repository vendor-tag release-tags ...",
172: "b:dI:k:m:",
1.1 jfb 173: "Import sources into CVS, using vendor branches",
1.17 jfb 174: NULL,
1.1 jfb 175: },
176: {
1.6 jfb 177: CVS_OP_INIT, "init", { }, cvs_init,
1.1 jfb 178: "",
1.13 krapht 179: "",
1.1 jfb 180: "Create a CVS repository if it doesn't exist",
1.17 jfb 181: NULL,
1.1 jfb 182: },
183: #if defined(HAVE_KERBEROS)
184: {
185: "kserver", {}, NULL
186: "",
1.13 krapht 187: "",
1.1 jfb 188: "Start a Kerberos authentication CVS server",
1.17 jfb 189: NULL,
1.1 jfb 190: },
191: #endif
192: {
1.6 jfb 193: CVS_OP_LOG, "log", { "lo" }, cvs_getlog,
1.38 jfb 194: "[-bhlNRt] [-d dates] [-r revisions] [-s states] [-w logins]",
1.13 krapht 195: "",
1.1 jfb 196: "Print out history information for files",
1.17 jfb 197: NULL,
1.1 jfb 198: },
199: {
1.6 jfb 200: -1, "login", {}, NULL,
1.1 jfb 201: "",
1.13 krapht 202: "",
1.1 jfb 203: "Prompt for password for authenticating server",
1.17 jfb 204: NULL,
1.1 jfb 205: },
206: {
1.6 jfb 207: -1, "logout", {}, NULL,
1.1 jfb 208: "",
1.13 krapht 209: "",
1.1 jfb 210: "Removes entry in .cvspass for remote repository",
1.17 jfb 211: NULL,
1.1 jfb 212: },
213: {
1.36 jfb 214: CVS_OP_RDIFF, "rdiff", {}, NULL,
1.1 jfb 215: "",
1.13 krapht 216: "",
1.1 jfb 217: "Create 'patch' format diffs between releases",
1.17 jfb 218: NULL,
1.1 jfb 219: },
220: {
1.36 jfb 221: CVS_OP_RELEASE, "release", {}, NULL,
1.38 jfb 222: "[-d]",
223: "d",
1.1 jfb 224: "Indicate that a Module is no longer in use",
1.17 jfb 225: NULL,
1.1 jfb 226: },
227: {
1.26 xsa 228: CVS_OP_REMOVE, "remove", { "rm", "delete" }, cvs_remove,
1.28 xsa 229: "[-flR] file ...",
1.38 jfb 230: "flR",
1.1 jfb 231: "Remove an entry from the repository",
1.17 jfb 232: NULL,
1.1 jfb 233: },
234: {
1.36 jfb 235: CVS_OP_RLOG, "rlog", {}, NULL,
1.1 jfb 236: "",
1.13 krapht 237: "",
1.1 jfb 238: "Print out history information for a module",
1.17 jfb 239: NULL,
1.1 jfb 240: },
241: {
1.36 jfb 242: CVS_OP_RTAG, "rtag", {}, NULL,
1.1 jfb 243: "",
1.13 krapht 244: "",
1.1 jfb 245: "Add a symbolic tag to a module",
1.17 jfb 246: NULL,
1.1 jfb 247: },
248: {
1.6 jfb 249: CVS_OP_SERVER, "server", {}, cvs_server,
1.1 jfb 250: "",
1.13 krapht 251: "",
1.1 jfb 252: "Server mode",
1.17 jfb 253: NULL,
1.1 jfb 254: },
255: {
1.16 jfb 256: CVS_OP_STATUS, "status", { "st", "stat" }, cvs_status,
1.31 jfb 257: "[-lRv]",
1.38 jfb 258: "lRv",
1.1 jfb 259: "Display status information on checked out files",
1.17 jfb 260: NULL,
1.1 jfb 261: },
262: {
1.22 jfb 263: CVS_OP_TAG, "tag", { "ta", "freeze" }, cvs_tag,
1.38 jfb 264: "[-bcdFflR] [-D date | -r rev] tagname",
265: "bcD:dFflRr:",
1.1 jfb 266: "Add a symbolic tag to checked out version of files",
1.17 jfb 267: NULL,
1.1 jfb 268: },
269: {
1.36 jfb 270: CVS_OP_UNEDIT, "unedit", {}, NULL,
1.1 jfb 271: "",
1.13 krapht 272: "",
1.1 jfb 273: "Undo an edit command",
1.17 jfb 274: NULL,
1.1 jfb 275: },
276: {
1.16 jfb 277: CVS_OP_UPDATE, "update", { "up", "upd" }, cvs_update,
1.39 xsa 278: "[-ACdflPpR] [-D date | -r rev] [-I ign] [-j rev] [-k mode] "
1.38 jfb 279: "[-t id] ...",
1.13 krapht 280: "",
1.1 jfb 281: "Bring work tree in sync with repository",
1.17 jfb 282: NULL,
1.1 jfb 283: },
284: {
1.16 jfb 285: CVS_OP_VERSION, "version", { "ve", "ver" }, cvs_version,
1.13 krapht 286: "", "",
1.1 jfb 287: "Show current CVS version(s)",
1.17 jfb 288: NULL,
1.1 jfb 289: },
290: {
1.36 jfb 291: CVS_OP_WATCH, "watch", {}, NULL,
1.1 jfb 292: "",
1.13 krapht 293: "",
1.1 jfb 294: "Set watches",
1.17 jfb 295: NULL,
1.1 jfb 296: },
297: {
1.36 jfb 298: CVS_OP_WATCHERS, "watchers", {}, NULL,
1.1 jfb 299: "",
1.13 krapht 300: "",
1.1 jfb 301: "See who is watching a file",
1.17 jfb 302: NULL,
1.1 jfb 303: },
304: };
305:
306: #define CVS_NBCMD (sizeof(cvs_cdt)/sizeof(cvs_cdt[0]))
307:
308:
309:
310: void usage (void);
311: void sigchld_hdlr (int);
1.17 jfb 312: void cvs_read_rcfile (void);
1.1 jfb 313: struct cvs_cmd* cvs_findcmd (const char *);
1.17 jfb 314: int cvs_getopt (int, char **);
1.1 jfb 315:
316:
317: /*
318: * usage()
319: *
320: * Display usage information.
321: */
322: void
323: usage(void)
324: {
325: fprintf(stderr,
1.27 jfb 326: "Usage: %s [-flQqtv] [-d root] [-e editor] [-s var=val] [-z level] "
327: "command [...]\n", __progname);
1.1 jfb 328: }
329:
330:
331: int
332: main(int argc, char **argv)
333: {
1.17 jfb 334: char *envstr, *cmd_argv[CVS_CMD_MAXARG], **targv;
335: int i, ret, cmd_argc;
1.1 jfb 336: struct cvs_cmd *cmdp;
337:
1.27 jfb 338: TAILQ_INIT(&cvs_variables);
339:
1.1 jfb 340: if (cvs_log_init(LD_STD, 0) < 0)
341: err(1, "failed to initialize logging");
342:
343: /* by default, be very verbose */
344: (void)cvs_log_filter(LP_FILTER_UNSET, LP_INFO);
345:
346: #ifdef DEBUG
347: (void)cvs_log_filter(LP_FILTER_UNSET, LP_DEBUG);
348: #endif
349:
350: /* check environment so command-line options override it */
351: if ((envstr = getenv("CVS_RSH")) != NULL)
352: cvs_rsh = envstr;
353:
354: if (((envstr = getenv("CVSEDITOR")) != NULL) ||
355: ((envstr = getenv("VISUAL")) != NULL) ||
356: ((envstr = getenv("EDITOR")) != NULL))
357: cvs_editor = envstr;
358:
1.17 jfb 359: ret = cvs_getopt(argc, argv);
360:
361: argc -= ret;
362: argv += ret;
363: if (argc == 0) {
364: usage();
365: exit(EX_USAGE);
366: }
367: cvs_command = argv[0];
368:
369: if (cvs_readrc) {
370: cvs_read_rcfile();
371:
372: if (cvs_defargs != NULL) {
373: targv = cvs_makeargv(cvs_defargs, &i);
374: if (targv == NULL) {
375: cvs_log(LP_ERR,
376: "failed to load default arguments to %s",
377: __progname);
378: exit(EX_OSERR);
379: }
380:
381: cvs_getopt(i, targv);
382: cvs_freeargv(targv, i);
383: free(targv);
384: }
385: }
386:
387: /* setup signal handlers */
388: signal(SIGPIPE, SIG_IGN);
389:
1.40 ! jfb 390: if (cvs_file_init() < 0) {
! 391: cvs_log(LP_ERR, "failed to initialize file support");
! 392: exit(1);
! 393: }
1.17 jfb 394:
395: ret = -1;
396:
397: cmdp = cvs_findcmd(cvs_command);
398: if (cmdp == NULL) {
399: fprintf(stderr, "Unknown command: `%s'\n\n", cvs_command);
400: fprintf(stderr, "CVS commands are:\n");
401: for (i = 0; i < (int)CVS_NBCMD; i++)
402: fprintf(stderr, "\t%-16s%s\n",
403: cvs_cdt[i].cmd_name, cvs_cdt[i].cmd_descr);
404: exit(EX_USAGE);
405: }
406:
407: if (cmdp->cmd_hdlr == NULL) {
408: cvs_log(LP_ERR, "command `%s' not implemented", cvs_command);
409: exit(1);
410: }
411:
412: cvs_cmdop = cmdp->cmd_op;
413:
414: cmd_argc = 0;
415: memset(cmd_argv, 0, sizeof(cmd_argv));
416:
417: cmd_argv[cmd_argc++] = argv[0];
418: if (cmdp->cmd_defargs != NULL) {
419: /* transform into a new argument vector */
420: ret = cvs_getargv(cmdp->cmd_defargs, cmd_argv + 1,
421: CVS_CMD_MAXARG - 1);
422: if (ret < 0) {
423: cvs_log(LP_ERRNO, "failed to generate argument vector "
424: "from default arguments");
425: exit(EX_DATAERR);
426: }
427: cmd_argc += ret;
428: }
429: for (ret = 1; ret < argc; ret++)
430: cmd_argv[cmd_argc++] = argv[ret];
431:
432: ret = (*cmdp->cmd_hdlr)(cmd_argc, cmd_argv);
433: if (ret == EX_USAGE) {
434: fprintf(stderr, "Usage: %s %s %s\n", __progname, cvs_command,
435: cmdp->cmd_synopsis);
436: }
437:
438: if (cvs_files != NULL)
439: cvs_file_free(cvs_files);
1.33 jfb 440: if (cvs_msg != NULL)
441: free(cvs_msg);
1.17 jfb 442:
443: return (ret);
444: }
445:
446:
447: int
448: cvs_getopt(int argc, char **argv)
449: {
450: int ret;
451: char *ep;
452:
1.27 jfb 453: while ((ret = getopt(argc, argv, "b:d:e:fHlnQqrs:tvz:")) != -1) {
1.1 jfb 454: switch (ret) {
1.11 jfb 455: case 'b':
456: /*
457: * We do not care about the bin directory for RCS files
458: * as this program has no dependencies on RCS programs,
459: * so it is only here for backwards compatibility.
460: */
461: cvs_log(LP_NOTICE, "the -b argument is obsolete");
462: break;
1.1 jfb 463: case 'd':
464: cvs_rootstr = optarg;
465: break;
466: case 'e':
467: cvs_editor = optarg;
468: break;
469: case 'f':
1.17 jfb 470: cvs_readrc = 0;
1.1 jfb 471: break;
472: case 'l':
473: cvs_nolog = 1;
474: break;
475: case 'n':
476: break;
477: case 'Q':
478: verbosity = 0;
479: break;
480: case 'q':
481: /* don't override -Q */
482: if (verbosity > 1)
483: verbosity = 1;
484: break;
485: case 'r':
486: cvs_readonly = 1;
487: break;
1.27 jfb 488: case 's':
489: ep = strchr(optarg, '=');
490: if (ep == NULL) {
491: cvs_log(LP_ERR, "no = in variable assignment");
492: exit(EX_USAGE);
493: }
494: *(ep++) = '\0';
495: if (cvs_var_set(optarg, ep) < 0)
496: exit(EX_USAGE);
497: break;
1.1 jfb 498: case 't':
1.24 jfb 499: (void)cvs_log_filter(LP_FILTER_UNSET, LP_TRACE);
1.1 jfb 500: cvs_trace = 1;
501: break;
502: case 'v':
503: printf("%s\n", CVS_VERSION);
504: exit(0);
505: /* NOTREACHED */
1.11 jfb 506: break;
507: case 'x':
508: /*
509: * Kerberos encryption support, kept for compatibility
510: */
1.1 jfb 511: break;
512: case 'z':
1.18 tedu 513: cvs_compress = (int)strtol(optarg, &ep, 10);
1.1 jfb 514: if (*ep != '\0')
515: errx(1, "error parsing compression level");
516: if (cvs_compress < 0 || cvs_compress > 9)
517: errx(1, "gzip compression level must be "
518: "between 0 and 9");
519: break;
520: default:
521: usage();
522: exit(EX_USAGE);
523: }
524: }
525:
1.17 jfb 526: ret = optind;
1.1 jfb 527: optind = 1;
1.17 jfb 528: optreset = 1; /* for next call */
1.12 jfb 529:
1.1 jfb 530: return (ret);
531: }
532:
533:
534: /*
535: * cvs_findcmd()
536: *
537: * Find the entry in the command dispatch table whose name or one of its
538: * aliases matches <cmd>.
539: * Returns a pointer to the command entry on success, NULL on failure.
540: */
541: struct cvs_cmd*
542: cvs_findcmd(const char *cmd)
543: {
544: u_int i, j;
545: struct cvs_cmd *cmdp;
546:
547: cmdp = NULL;
548:
549: for (i = 0; (i < CVS_NBCMD) && (cmdp == NULL); i++) {
550: if (strcmp(cmd, cvs_cdt[i].cmd_name) == 0)
551: cmdp = &cvs_cdt[i];
552: else {
553: for (j = 0; j < CVS_CMD_MAXALIAS; j++) {
554: if (strcmp(cmd, cvs_cdt[i].cmd_alias[j]) == 0) {
555: cmdp = &cvs_cdt[i];
556: break;
557: }
558: }
559: }
560: }
561:
562: return (cmdp);
563: }
564:
565:
566: /*
1.17 jfb 567: * cvs_read_rcfile()
1.1 jfb 568: *
569: * Read the CVS `.cvsrc' file in the user's home directory. If the file
570: * exists, it should contain a list of arguments that should always be given
571: * implicitly to the specified commands.
572: */
573: void
1.17 jfb 574: cvs_read_rcfile(void)
1.1 jfb 575: {
576: char rcpath[MAXPATHLEN], linebuf[128], *lp;
1.23 xsa 577: int linenum = 0;
1.17 jfb 578: size_t len;
1.1 jfb 579: struct cvs_cmd *cmdp;
580: struct passwd *pw;
581: FILE *fp;
582:
583: pw = getpwuid(getuid());
584: if (pw == NULL) {
585: cvs_log(LP_NOTICE, "failed to get user's password entry");
586: return;
587: }
588:
589: snprintf(rcpath, sizeof(rcpath), "%s/%s", pw->pw_dir, CVS_PATH_RC);
590:
591: fp = fopen(rcpath, "r");
592: if (fp == NULL) {
593: if (errno != ENOENT)
594: cvs_log(LP_NOTICE, "failed to open `%s': %s", rcpath,
595: strerror(errno));
596: return;
597: }
598:
599: while (fgets(linebuf, sizeof(linebuf), fp) != NULL) {
1.23 xsa 600: linenum++;
1.17 jfb 601: if ((len = strlen(linebuf)) == 0)
602: continue;
603: if (linebuf[len - 1] != '\n') {
1.23 xsa 604: cvs_log(LP_WARN, "line too long in `%s:%d'", rcpath,
605: linenum);
1.17 jfb 606: break;
607: }
608: linebuf[--len] = '\0';
609:
1.1 jfb 610: lp = strchr(linebuf, ' ');
611: if (lp == NULL)
1.17 jfb 612: continue; /* ignore lines with no arguments */
613: *lp = '\0';
1.1 jfb 614: if (strcmp(linebuf, "cvs") == 0) {
1.17 jfb 615: /*
616: * Global default options. In the case of cvs only,
617: * we keep the 'cvs' string as first argument because
618: * getopt() does not like starting at index 0 for
619: * argument processing.
620: */
621: *lp = ' ';
622: cvs_defargs = strdup(linebuf);
623: if (cvs_defargs == NULL)
624: cvs_log(LP_ERRNO,
625: "failed to copy global arguments");
1.15 deraadt 626: } else {
1.17 jfb 627: lp++;
1.1 jfb 628: cmdp = cvs_findcmd(linebuf);
629: if (cmdp == NULL) {
630: cvs_log(LP_NOTICE,
1.25 xsa 631: "unknown command `%s' in `%s:%d'",
632: linebuf, rcpath, linenum);
1.1 jfb 633: continue;
634: }
1.17 jfb 635:
636: cmdp->cmd_defargs = strdup(lp);
637: if (cmdp->cmd_defargs == NULL)
638: cvs_log(LP_ERRNO,
639: "failed to copy default arguments for %s",
640: cmdp->cmd_name);
1.1 jfb 641: }
642: }
643: if (ferror(fp)) {
1.23 xsa 644: cvs_log(LP_NOTICE, "failed to read line from `%s'", rcpath);
1.1 jfb 645: }
646:
647: (void)fclose(fp);
1.27 jfb 648: }
649:
650:
651: /*
652: * cvs_var_set()
653: *
654: * Set the value of the variable <var> to <val>. If there is no such variable,
655: * a new entry is created, otherwise the old value is overwritten.
656: * Returns 0 on success, or -1 on failure.
657: */
658: int
659: cvs_var_set(const char *var, const char *val)
660: {
661: char *valcp;
662: const char *cp;
663: struct cvs_var *vp;
664:
665: if ((var == NULL) || (*var == '\0')) {
666: cvs_log(LP_ERR, "no variable name");
667: return (-1);
668: }
669:
670: /* sanity check on the name */
671: for (cp = var; *cp != '\0'; cp++)
672: if (!isalnum(*cp) && (*cp != '_')) {
673: cvs_log(LP_ERR,
674: "variable name `%s' contains invalid characters",
675: var);
676: return (-1);
677: }
678:
679: TAILQ_FOREACH(vp, &cvs_variables, cv_link)
680: if (strcmp(vp->cv_name, var) == 0)
681: break;
682:
683: valcp = strdup(val);
684: if (valcp == NULL) {
685: cvs_log(LP_ERRNO, "failed to allocate variable");
686: return (-1);
687: }
688:
689: if (vp == NULL) {
690: vp = (struct cvs_var *)malloc(sizeof(*vp));
691: if (vp == NULL) {
692: cvs_log(LP_ERRNO, "failed to allocate variable");
693: free(valcp);
694: return (-1);
695: }
696: memset(vp, 0, sizeof(*vp));
697:
698: vp->cv_name = strdup(var);
699: if (vp->cv_name == NULL) {
700: cvs_log(LP_ERRNO, "failed to allocate variable");
701: free(valcp);
702: free(vp);
703: return (-1);
704: }
705:
706: TAILQ_INSERT_TAIL(&cvs_variables, vp, cv_link);
707:
708: } else /* free the previous value */
709: free(vp->cv_val);
710:
711: vp->cv_val = valcp;
712:
713: return (0);
714: }
715:
716:
717: /*
718: * cvs_var_set()
719: *
720: * Remove any entry for the variable <var>.
721: * Returns 0 on success, or -1 on failure.
722: */
723: int
724: cvs_var_unset(const char *var)
725: {
726: struct cvs_var *vp;
727:
728: TAILQ_FOREACH(vp, &cvs_variables, cv_link)
729: if (strcmp(vp->cv_name, var) == 0) {
730: TAILQ_REMOVE(&cvs_variables, vp, cv_link);
731: free(vp->cv_name);
732: free(vp->cv_val);
733: free(vp);
734: return (0);
735: }
736:
737: return (-1);
738:
739: }
740:
741:
742: /*
743: * cvs_var_get()
744: *
745: * Get the value associated with the variable <var>. Returns a pointer to the
746: * value string on success, or NULL if the variable does not exist.
747: */
748:
749: const char*
750: cvs_var_get(const char *var)
751: {
752: struct cvs_var *vp;
753:
754: TAILQ_FOREACH(vp, &cvs_variables, cv_link)
755: if (strcmp(vp->cv_name, var) == 0)
756: return (vp->cv_val);
757:
758: return (NULL);
1.1 jfb 759: }