Annotation of src/usr.bin/cvs/cvs.c, Revision 1.39
1.39 ! xsa 1: /* $OpenBSD: cvs.c,v 1.38 2005/01/24 18:48:23 jfb 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:
390: cvs_file_init();
391:
392: ret = -1;
393:
394: cmdp = cvs_findcmd(cvs_command);
395: if (cmdp == NULL) {
396: fprintf(stderr, "Unknown command: `%s'\n\n", cvs_command);
397: fprintf(stderr, "CVS commands are:\n");
398: for (i = 0; i < (int)CVS_NBCMD; i++)
399: fprintf(stderr, "\t%-16s%s\n",
400: cvs_cdt[i].cmd_name, cvs_cdt[i].cmd_descr);
401: exit(EX_USAGE);
402: }
403:
404: if (cmdp->cmd_hdlr == NULL) {
405: cvs_log(LP_ERR, "command `%s' not implemented", cvs_command);
406: exit(1);
407: }
408:
409: cvs_cmdop = cmdp->cmd_op;
410:
411: cmd_argc = 0;
412: memset(cmd_argv, 0, sizeof(cmd_argv));
413:
414: cmd_argv[cmd_argc++] = argv[0];
415: if (cmdp->cmd_defargs != NULL) {
416: /* transform into a new argument vector */
417: ret = cvs_getargv(cmdp->cmd_defargs, cmd_argv + 1,
418: CVS_CMD_MAXARG - 1);
419: if (ret < 0) {
420: cvs_log(LP_ERRNO, "failed to generate argument vector "
421: "from default arguments");
422: exit(EX_DATAERR);
423: }
424: cmd_argc += ret;
425: }
426: for (ret = 1; ret < argc; ret++)
427: cmd_argv[cmd_argc++] = argv[ret];
428:
429: ret = (*cmdp->cmd_hdlr)(cmd_argc, cmd_argv);
430: if (ret == EX_USAGE) {
431: fprintf(stderr, "Usage: %s %s %s\n", __progname, cvs_command,
432: cmdp->cmd_synopsis);
433: }
434:
435: if (cvs_files != NULL)
436: cvs_file_free(cvs_files);
1.33 jfb 437: if (cvs_msg != NULL)
438: free(cvs_msg);
1.17 jfb 439:
440: return (ret);
441: }
442:
443:
444: int
445: cvs_getopt(int argc, char **argv)
446: {
447: int ret;
448: char *ep;
449:
1.27 jfb 450: while ((ret = getopt(argc, argv, "b:d:e:fHlnQqrs:tvz:")) != -1) {
1.1 jfb 451: switch (ret) {
1.11 jfb 452: case 'b':
453: /*
454: * We do not care about the bin directory for RCS files
455: * as this program has no dependencies on RCS programs,
456: * so it is only here for backwards compatibility.
457: */
458: cvs_log(LP_NOTICE, "the -b argument is obsolete");
459: break;
1.1 jfb 460: case 'd':
461: cvs_rootstr = optarg;
462: break;
463: case 'e':
464: cvs_editor = optarg;
465: break;
466: case 'f':
1.17 jfb 467: cvs_readrc = 0;
1.1 jfb 468: break;
469: case 'l':
470: cvs_nolog = 1;
471: break;
472: case 'n':
473: break;
474: case 'Q':
475: verbosity = 0;
476: break;
477: case 'q':
478: /* don't override -Q */
479: if (verbosity > 1)
480: verbosity = 1;
481: break;
482: case 'r':
483: cvs_readonly = 1;
484: break;
1.27 jfb 485: case 's':
486: ep = strchr(optarg, '=');
487: if (ep == NULL) {
488: cvs_log(LP_ERR, "no = in variable assignment");
489: exit(EX_USAGE);
490: }
491: *(ep++) = '\0';
492: if (cvs_var_set(optarg, ep) < 0)
493: exit(EX_USAGE);
494: break;
1.1 jfb 495: case 't':
1.24 jfb 496: (void)cvs_log_filter(LP_FILTER_UNSET, LP_TRACE);
1.1 jfb 497: cvs_trace = 1;
498: break;
499: case 'v':
500: printf("%s\n", CVS_VERSION);
501: exit(0);
502: /* NOTREACHED */
1.11 jfb 503: break;
504: case 'x':
505: /*
506: * Kerberos encryption support, kept for compatibility
507: */
1.1 jfb 508: break;
509: case 'z':
1.18 tedu 510: cvs_compress = (int)strtol(optarg, &ep, 10);
1.1 jfb 511: if (*ep != '\0')
512: errx(1, "error parsing compression level");
513: if (cvs_compress < 0 || cvs_compress > 9)
514: errx(1, "gzip compression level must be "
515: "between 0 and 9");
516: break;
517: default:
518: usage();
519: exit(EX_USAGE);
520: }
521: }
522:
1.17 jfb 523: ret = optind;
1.1 jfb 524: optind = 1;
1.17 jfb 525: optreset = 1; /* for next call */
1.12 jfb 526:
1.1 jfb 527: return (ret);
528: }
529:
530:
531: /*
532: * cvs_findcmd()
533: *
534: * Find the entry in the command dispatch table whose name or one of its
535: * aliases matches <cmd>.
536: * Returns a pointer to the command entry on success, NULL on failure.
537: */
538: struct cvs_cmd*
539: cvs_findcmd(const char *cmd)
540: {
541: u_int i, j;
542: struct cvs_cmd *cmdp;
543:
544: cmdp = NULL;
545:
546: for (i = 0; (i < CVS_NBCMD) && (cmdp == NULL); i++) {
547: if (strcmp(cmd, cvs_cdt[i].cmd_name) == 0)
548: cmdp = &cvs_cdt[i];
549: else {
550: for (j = 0; j < CVS_CMD_MAXALIAS; j++) {
551: if (strcmp(cmd, cvs_cdt[i].cmd_alias[j]) == 0) {
552: cmdp = &cvs_cdt[i];
553: break;
554: }
555: }
556: }
557: }
558:
559: return (cmdp);
560: }
561:
562:
563: /*
1.17 jfb 564: * cvs_read_rcfile()
1.1 jfb 565: *
566: * Read the CVS `.cvsrc' file in the user's home directory. If the file
567: * exists, it should contain a list of arguments that should always be given
568: * implicitly to the specified commands.
569: */
570: void
1.17 jfb 571: cvs_read_rcfile(void)
1.1 jfb 572: {
573: char rcpath[MAXPATHLEN], linebuf[128], *lp;
1.23 xsa 574: int linenum = 0;
1.17 jfb 575: size_t len;
1.1 jfb 576: struct cvs_cmd *cmdp;
577: struct passwd *pw;
578: FILE *fp;
579:
580: pw = getpwuid(getuid());
581: if (pw == NULL) {
582: cvs_log(LP_NOTICE, "failed to get user's password entry");
583: return;
584: }
585:
586: snprintf(rcpath, sizeof(rcpath), "%s/%s", pw->pw_dir, CVS_PATH_RC);
587:
588: fp = fopen(rcpath, "r");
589: if (fp == NULL) {
590: if (errno != ENOENT)
591: cvs_log(LP_NOTICE, "failed to open `%s': %s", rcpath,
592: strerror(errno));
593: return;
594: }
595:
596: while (fgets(linebuf, sizeof(linebuf), fp) != NULL) {
1.23 xsa 597: linenum++;
1.17 jfb 598: if ((len = strlen(linebuf)) == 0)
599: continue;
600: if (linebuf[len - 1] != '\n') {
1.23 xsa 601: cvs_log(LP_WARN, "line too long in `%s:%d'", rcpath,
602: linenum);
1.17 jfb 603: break;
604: }
605: linebuf[--len] = '\0';
606:
1.1 jfb 607: lp = strchr(linebuf, ' ');
608: if (lp == NULL)
1.17 jfb 609: continue; /* ignore lines with no arguments */
610: *lp = '\0';
1.1 jfb 611: if (strcmp(linebuf, "cvs") == 0) {
1.17 jfb 612: /*
613: * Global default options. In the case of cvs only,
614: * we keep the 'cvs' string as first argument because
615: * getopt() does not like starting at index 0 for
616: * argument processing.
617: */
618: *lp = ' ';
619: cvs_defargs = strdup(linebuf);
620: if (cvs_defargs == NULL)
621: cvs_log(LP_ERRNO,
622: "failed to copy global arguments");
1.15 deraadt 623: } else {
1.17 jfb 624: lp++;
1.1 jfb 625: cmdp = cvs_findcmd(linebuf);
626: if (cmdp == NULL) {
627: cvs_log(LP_NOTICE,
1.25 xsa 628: "unknown command `%s' in `%s:%d'",
629: linebuf, rcpath, linenum);
1.1 jfb 630: continue;
631: }
1.17 jfb 632:
633: cmdp->cmd_defargs = strdup(lp);
634: if (cmdp->cmd_defargs == NULL)
635: cvs_log(LP_ERRNO,
636: "failed to copy default arguments for %s",
637: cmdp->cmd_name);
1.1 jfb 638: }
639: }
640: if (ferror(fp)) {
1.23 xsa 641: cvs_log(LP_NOTICE, "failed to read line from `%s'", rcpath);
1.1 jfb 642: }
643:
644: (void)fclose(fp);
1.27 jfb 645: }
646:
647:
648: /*
649: * cvs_var_set()
650: *
651: * Set the value of the variable <var> to <val>. If there is no such variable,
652: * a new entry is created, otherwise the old value is overwritten.
653: * Returns 0 on success, or -1 on failure.
654: */
655: int
656: cvs_var_set(const char *var, const char *val)
657: {
658: char *valcp;
659: const char *cp;
660: struct cvs_var *vp;
661:
662: if ((var == NULL) || (*var == '\0')) {
663: cvs_log(LP_ERR, "no variable name");
664: return (-1);
665: }
666:
667: /* sanity check on the name */
668: for (cp = var; *cp != '\0'; cp++)
669: if (!isalnum(*cp) && (*cp != '_')) {
670: cvs_log(LP_ERR,
671: "variable name `%s' contains invalid characters",
672: var);
673: return (-1);
674: }
675:
676: TAILQ_FOREACH(vp, &cvs_variables, cv_link)
677: if (strcmp(vp->cv_name, var) == 0)
678: break;
679:
680: valcp = strdup(val);
681: if (valcp == NULL) {
682: cvs_log(LP_ERRNO, "failed to allocate variable");
683: return (-1);
684: }
685:
686: if (vp == NULL) {
687: vp = (struct cvs_var *)malloc(sizeof(*vp));
688: if (vp == NULL) {
689: cvs_log(LP_ERRNO, "failed to allocate variable");
690: free(valcp);
691: return (-1);
692: }
693: memset(vp, 0, sizeof(*vp));
694:
695: vp->cv_name = strdup(var);
696: if (vp->cv_name == NULL) {
697: cvs_log(LP_ERRNO, "failed to allocate variable");
698: free(valcp);
699: free(vp);
700: return (-1);
701: }
702:
703: TAILQ_INSERT_TAIL(&cvs_variables, vp, cv_link);
704:
705: } else /* free the previous value */
706: free(vp->cv_val);
707:
708: vp->cv_val = valcp;
709:
710: return (0);
711: }
712:
713:
714: /*
715: * cvs_var_set()
716: *
717: * Remove any entry for the variable <var>.
718: * Returns 0 on success, or -1 on failure.
719: */
720: int
721: cvs_var_unset(const char *var)
722: {
723: struct cvs_var *vp;
724:
725: TAILQ_FOREACH(vp, &cvs_variables, cv_link)
726: if (strcmp(vp->cv_name, var) == 0) {
727: TAILQ_REMOVE(&cvs_variables, vp, cv_link);
728: free(vp->cv_name);
729: free(vp->cv_val);
730: free(vp);
731: return (0);
732: }
733:
734: return (-1);
735:
736: }
737:
738:
739: /*
740: * cvs_var_get()
741: *
742: * Get the value associated with the variable <var>. Returns a pointer to the
743: * value string on success, or NULL if the variable does not exist.
744: */
745:
746: const char*
747: cvs_var_get(const char *var)
748: {
749: struct cvs_var *vp;
750:
751: TAILQ_FOREACH(vp, &cvs_variables, cv_link)
752: if (strcmp(vp->cv_name, var) == 0)
753: return (vp->cv_val);
754:
755: return (NULL);
1.1 jfb 756: }