1*2f43a3f5Sderaadt /* $OpenBSD: cvs.c,v 1.160 2021/01/27 07:18:16 deraadt Exp $ */
26c121f58Sjfb /*
380f6ca9bSjoris * Copyright (c) 2006, 2007 Joris Vink <joris@openbsd.org>
46c121f58Sjfb * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
56c121f58Sjfb * All rights reserved.
66c121f58Sjfb *
76c121f58Sjfb * Redistribution and use in source and binary forms, with or without
86c121f58Sjfb * modification, are permitted provided that the following conditions
96c121f58Sjfb * are met:
106c121f58Sjfb *
116c121f58Sjfb * 1. Redistributions of source code must retain the above copyright
126c121f58Sjfb * notice, this list of conditions and the following disclaimer.
136c121f58Sjfb * 2. The name of the author may not be used to endorse or promote products
146c121f58Sjfb * derived from this software without specific prior written permission.
156c121f58Sjfb *
166c121f58Sjfb * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
176c121f58Sjfb * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
186c121f58Sjfb * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
196c121f58Sjfb * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
206c121f58Sjfb * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
216c121f58Sjfb * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
226c121f58Sjfb * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
236c121f58Sjfb * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
246c121f58Sjfb * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
256c121f58Sjfb * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
266c121f58Sjfb */
276c121f58Sjfb
281f8531bdSotto #include <sys/stat.h>
291f8531bdSotto
301f8531bdSotto #include <ctype.h>
311f8531bdSotto #include <errno.h>
321f8531bdSotto #include <pwd.h>
331f8531bdSotto #include <stdlib.h>
341f8531bdSotto #include <string.h>
351e29f7ddSxsa #include <time.h>
361f8531bdSotto #include <unistd.h>
377f453672Sderaadt #include <err.h>
386c121f58Sjfb
396c121f58Sjfb #include "cvs.h"
409fac60a5Sjoris #include "remote.h"
418fbb14c0Sjoris #include "hash.h"
426c121f58Sjfb
436c121f58Sjfb extern char *__progname;
446c121f58Sjfb
456c121f58Sjfb /* verbosity level: 0 = really quiet, 1 = quiet, 2 = verbose */
4660c775bfSjoris int verbosity = 2;
476c121f58Sjfb
486c121f58Sjfb /* compression level used with zlib, 0 meaning no compression taking place */
496c121f58Sjfb int cvs_compress = 0;
50ca1d3256Sjfb int cvs_readrc = 1; /* read .cvsrc on startup */
516c121f58Sjfb int cvs_trace = 0;
526c121f58Sjfb int cvs_nolog = 0;
536c121f58Sjfb int cvs_readonly = 0;
543dcb84b2Sxsa int cvs_readonlyfs = 0;
55ca1d3256Sjfb int cvs_nocase = 0; /* set to 1 to disable filename case sensitivity */
560bda20e2Sxsa int cvs_noexec = 0; /* set to 1 to disable disk operations (-n option) */
573ad3fb45Sjoris int cvs_cmdop;
5818911d1eSjoris int cvs_umask = CVS_UMASK_DEFAULT;
599fac60a5Sjoris int cvs_server_active = 0;
603ad3fb45Sjoris
6118911d1eSjoris char *cvs_tagname = NULL;
62ca1d3256Sjfb char *cvs_defargs; /* default global arguments from .cvsrc */
636c121f58Sjfb char *cvs_rootstr;
646c121f58Sjfb char *cvs_rsh = CVS_RSH_DEFAULT;
656c121f58Sjfb char *cvs_editor = CVS_EDITOR_DEFAULT;
6658cbd44dSxsa char *cvs_homedir = NULL;
67e5cbdd76Sxsa char *cvs_tmpdir = CVS_TMPDIR_DEFAULT;
68cf397ae7Skrapht
693ad3fb45Sjoris struct cvsroot *current_cvsroot = NULL;
709d091990Stobias struct cvs_cmd *cmdp; /* struct of command we are running */
7193231021Sjfb
723ad3fb45Sjoris int cvs_getopt(int, char **);
7345357ec5Sxsa __dead void usage(void);
745db46beeSjoris static void cvs_read_rcfile(void);
756c121f58Sjfb
76*2f43a3f5Sderaadt struct cvs_varhead cvs_variables;
77*2f43a3f5Sderaadt
787a9e6d11Sray struct wklhead temp_files;
793ad3fb45Sjoris
803ad3fb45Sjoris void sighandler(int);
813ad3fb45Sjoris volatile sig_atomic_t cvs_quit = 0;
823ad3fb45Sjoris volatile sig_atomic_t sig_received = 0;
833ad3fb45Sjoris
847c1a09a6Sjoris extern CVSENTRIES *current_list;
857c1a09a6Sjoris
868fbb14c0Sjoris struct hash_table created_directories;
878fbb14c0Sjoris struct hash_table created_cvs_directories;
888fbb14c0Sjoris
893ad3fb45Sjoris void
sighandler(int sig)903ad3fb45Sjoris sighandler(int sig)
913ad3fb45Sjoris {
923ad3fb45Sjoris sig_received = sig;
933ad3fb45Sjoris
943ad3fb45Sjoris switch (sig) {
953ad3fb45Sjoris case SIGINT:
963ad3fb45Sjoris case SIGTERM:
97d1d63b6eSjoris case SIGPIPE:
983ad3fb45Sjoris cvs_quit = 1;
993ad3fb45Sjoris break;
1003ad3fb45Sjoris default:
1013ad3fb45Sjoris break;
1023ad3fb45Sjoris }
1033ad3fb45Sjoris }
1043ad3fb45Sjoris
1053ad3fb45Sjoris void
cvs_cleanup(void)1063ad3fb45Sjoris cvs_cleanup(void)
1073ad3fb45Sjoris {
1083ad3fb45Sjoris cvs_log(LP_TRACE, "cvs_cleanup: removing locks");
1097a9e6d11Sray worklist_run(&repo_locks, worklist_unlink);
1103ad3fb45Sjoris
1113ad3fb45Sjoris cvs_log(LP_TRACE, "cvs_cleanup: removing temp files");
1127a9e6d11Sray worklist_run(&temp_files, worklist_unlink);
1139fac60a5Sjoris
114f7a8994eSray if (cvs_server_path != NULL) {
1159fac60a5Sjoris if (cvs_rmdir(cvs_server_path) == -1)
1169fac60a5Sjoris cvs_log(LP_ERR,
1179fac60a5Sjoris "warning: failed to remove server directory: %s",
1189fac60a5Sjoris cvs_server_path);
119397ddb8aSnicm free(cvs_server_path);
120f7a8994eSray cvs_server_path = NULL;
1219fac60a5Sjoris }
1227c1a09a6Sjoris
1237c1a09a6Sjoris if (current_list != NULL)
1247c1a09a6Sjoris cvs_ent_close(current_list, ENT_SYNC);
1253ad3fb45Sjoris }
1263ad3fb45Sjoris
12745357ec5Sxsa __dead void
usage(void)1286c121f58Sjfb usage(void)
1296c121f58Sjfb {
13045357ec5Sxsa (void)fprintf(stderr,
13160c775bfSjoris "usage: %s [-flnQqRrtvw] [-d root] [-e editor] [-s var=val]\n"
13203cb09c0Ssobrado " [-T tmpdir] [-z level] command ...\n", __progname);
13345357ec5Sxsa exit(1);
1346c121f58Sjfb }
1356c121f58Sjfb
1366c121f58Sjfb int
cvs_build_cmd(char *** cmd_argv,char ** argv,int argc)13710283864Stobias cvs_build_cmd(char ***cmd_argv, char **argv, int argc)
13810283864Stobias {
13910283864Stobias int cmd_argc, i, cur;
14010283864Stobias char *cp, *linebuf, *lp;
14110283864Stobias
14210283864Stobias if (cmdp->cmd_defargs == NULL) {
14310283864Stobias *cmd_argv = argv;
14410283864Stobias return argc;
14510283864Stobias }
14610283864Stobias
14710283864Stobias cur = argc + 2;
14810283864Stobias cmd_argc = 0;
14910283864Stobias *cmd_argv = xcalloc(cur, sizeof(char *));
15010283864Stobias (*cmd_argv)[cmd_argc++] = argv[0];
15110283864Stobias
15210283864Stobias linebuf = xstrdup(cmdp->cmd_defargs);
15310283864Stobias for (lp = linebuf; lp != NULL;) {
15410283864Stobias cp = strsep(&lp, " \t\b\f\n\r\t\v");
15510283864Stobias if (cp == NULL)
15610283864Stobias break;
15710283864Stobias if (*cp == '\0')
15810283864Stobias continue;
15910283864Stobias
16010283864Stobias if (cmd_argc == cur) {
16110283864Stobias cur += 8;
162caa2ffb0Sderaadt *cmd_argv = xreallocarray(*cmd_argv, cur,
16310283864Stobias sizeof(char *));
16410283864Stobias }
16510283864Stobias
16610283864Stobias (*cmd_argv)[cmd_argc++] = cp;
16710283864Stobias }
16810283864Stobias
16910283864Stobias if (cmd_argc + argc > cur) {
17010283864Stobias cur = cmd_argc + argc + 1;
171caa2ffb0Sderaadt *cmd_argv = xreallocarray(*cmd_argv, cur,
17210283864Stobias sizeof(char *));
17310283864Stobias }
17410283864Stobias
17510283864Stobias for (i = 1; i < argc; i++)
17610283864Stobias (*cmd_argv)[cmd_argc++] = argv[i];
17710283864Stobias
17810283864Stobias (*cmd_argv)[cmd_argc] = NULL;
17910283864Stobias
18010283864Stobias return cmd_argc;
18110283864Stobias }
18210283864Stobias
18310283864Stobias int
main(int argc,char ** argv)1846c121f58Sjfb main(int argc, char **argv)
1856c121f58Sjfb {
18610283864Stobias char *envstr, **cmd_argv, **targv;
187ca1d3256Sjfb int i, ret, cmd_argc;
18858cbd44dSxsa struct passwd *pw;
189e5cbdd76Sxsa struct stat st;
190b9fc9a72Sderaadt char fpath[PATH_MAX];
1916c121f58Sjfb
192fed64a08Stb if (pledge("stdio rpath wpath cpath fattr getpw proc exec", NULL) == -1)
1937f453672Sderaadt err(1, "pledge");
1947f453672Sderaadt
195b1df6b0eSjoris tzset();
196b1df6b0eSjoris
1972d540844Sjfb TAILQ_INIT(&cvs_variables);
1983ad3fb45Sjoris SLIST_INIT(&repo_locks);
1993ad3fb45Sjoris SLIST_INIT(&temp_files);
2006c121f58Sjfb
2018fbb14c0Sjoris hash_table_init(&created_directories, 100);
2028fbb14c0Sjoris hash_table_init(&created_cvs_directories, 100);
2038fbb14c0Sjoris
2046c121f58Sjfb /* check environment so command-line options override it */
2056c121f58Sjfb if ((envstr = getenv("CVS_RSH")) != NULL)
2066c121f58Sjfb cvs_rsh = envstr;
2076c121f58Sjfb
2086c121f58Sjfb if (((envstr = getenv("CVSEDITOR")) != NULL) ||
2096c121f58Sjfb ((envstr = getenv("VISUAL")) != NULL) ||
2106c121f58Sjfb ((envstr = getenv("EDITOR")) != NULL))
2116c121f58Sjfb cvs_editor = envstr;
2126c121f58Sjfb
213c47e341fSxsa if ((envstr = getenv("CVSREAD")) != NULL)
214c47e341fSxsa cvs_readonly = 1;
215c47e341fSxsa
2163dcb84b2Sxsa if ((envstr = getenv("CVSREADONLYFS")) != NULL) {
2173dcb84b2Sxsa cvs_readonlyfs = 1;
2183dcb84b2Sxsa cvs_nolog = 1;
2193dcb84b2Sxsa }
2203dcb84b2Sxsa
22158cbd44dSxsa if ((cvs_homedir = getenv("HOME")) == NULL) {
222c5440612Stobias if ((pw = getpwuid(getuid())) != NULL)
22358cbd44dSxsa cvs_homedir = pw->pw_dir;
22458cbd44dSxsa }
22558cbd44dSxsa
226e5cbdd76Sxsa if ((envstr = getenv("TMPDIR")) != NULL)
227e5cbdd76Sxsa cvs_tmpdir = envstr;
228e5cbdd76Sxsa
229ca1d3256Sjfb ret = cvs_getopt(argc, argv);
230ca1d3256Sjfb
231ca1d3256Sjfb argc -= ret;
232ca1d3256Sjfb argv += ret;
23345357ec5Sxsa if (argc == 0)
234ca1d3256Sjfb usage();
235e5cbdd76Sxsa
2369d091990Stobias cmdp = cvs_findcmd(argv[0]);
237786b9ba3Stobias if (cmdp == NULL) {
2389d091990Stobias fprintf(stderr, "Unknown command: `%s'\n\n", argv[0]);
239786b9ba3Stobias fprintf(stderr, "CVS commands are:\n");
240786b9ba3Stobias for (i = 0; cvs_cdt[i] != NULL; i++)
241786b9ba3Stobias fprintf(stderr, "\t%-16s%s\n",
242786b9ba3Stobias cvs_cdt[i]->cmd_name, cvs_cdt[i]->cmd_descr);
243786b9ba3Stobias exit(1);
244786b9ba3Stobias }
245786b9ba3Stobias
24696b1d6b8Stobias /*
24796b1d6b8Stobias * check the tmp dir, either specified through
24896b1d6b8Stobias * the environment variable TMPDIR, or via
24996b1d6b8Stobias * the global option -T <dir>
25096b1d6b8Stobias */
25196b1d6b8Stobias if (stat(cvs_tmpdir, &st) == -1)
25296b1d6b8Stobias fatal("stat failed on `%s': %s", cvs_tmpdir, strerror(errno));
25396b1d6b8Stobias else if (!S_ISDIR(st.st_mode))
25496b1d6b8Stobias fatal("`%s' is not valid temporary directory", cvs_tmpdir);
25596b1d6b8Stobias
256c5440612Stobias if (cvs_readrc == 1 && cvs_homedir != NULL) {
257ca1d3256Sjfb cvs_read_rcfile();
258ca1d3256Sjfb
259ca1d3256Sjfb if (cvs_defargs != NULL) {
260fa2a9dfeSxsa if ((targv = cvs_makeargv(cvs_defargs, &i)) == NULL)
261fa2a9dfeSxsa fatal("failed to load default arguments to %s",
262ca1d3256Sjfb __progname);
263ca1d3256Sjfb
264ca1d3256Sjfb cvs_getopt(i, targv);
265ca1d3256Sjfb cvs_freeargv(targv, i);
266397ddb8aSnicm free(targv);
267ca1d3256Sjfb }
268ca1d3256Sjfb }
269ca1d3256Sjfb
270ca1d3256Sjfb /* setup signal handlers */
2713ad3fb45Sjoris signal(SIGTERM, sighandler);
2723ad3fb45Sjoris signal(SIGINT, sighandler);
2733ad3fb45Sjoris signal(SIGHUP, sighandler);
2743ad3fb45Sjoris signal(SIGABRT, sighandler);
2753ad3fb45Sjoris signal(SIGALRM, sighandler);
2763ad3fb45Sjoris signal(SIGPIPE, sighandler);
277ca1d3256Sjfb
278ca1d3256Sjfb cvs_cmdop = cmdp->cmd_op;
279ca1d3256Sjfb
28010283864Stobias cmd_argc = cvs_build_cmd(&cmd_argv, argv, argc);
281ca1d3256Sjfb
2823ad3fb45Sjoris cvs_file_init();
2833ad3fb45Sjoris
2849fac60a5Sjoris if (cvs_cmdop == CVS_OP_SERVER) {
285b0d19690Stobias cmdp->cmd(cmd_argc, cmd_argv);
286b0d19690Stobias cvs_cleanup();
287b0d19690Stobias return (0);
2889fac60a5Sjoris }
2899fac60a5Sjoris
29088b7ad1cStobias cvs_umask = umask(0);
29188b7ad1cStobias umask(cvs_umask);
29288b7ad1cStobias
2933ad3fb45Sjoris if ((current_cvsroot = cvsroot_get(".")) == NULL) {
294a06e2578Sxsa cvs_log(LP_ERR,
2953ad3fb45Sjoris "No CVSROOT specified! Please use the '-d' option");
2969a192d08Sdavid fatal("or set the CVSROOT environment variable.");
297ca1d3256Sjfb }
298ca1d3256Sjfb
2994dcde513Sjoris if (cvsroot_is_remote()) {
3009fac60a5Sjoris cmdp->cmd(cmd_argc, cmd_argv);
3019fac60a5Sjoris cvs_cleanup();
3029fac60a5Sjoris return (0);
3039fac60a5Sjoris }
304ca1d3256Sjfb
305e40de241Sxsa (void)xsnprintf(fpath, sizeof(fpath), "%s/%s",
306e40de241Sxsa current_cvsroot->cr_dir, CVS_PATH_ROOT);
307977ee386Sxsa
308db68708cSxsa if (stat(fpath, &st) == -1 && cvs_cmdop != CVS_OP_INIT) {
309b8b35aa3Sjoris if (errno == ENOENT)
31096276484Sjoris fatal("repository '%s' does not exist",
311b8b35aa3Sjoris current_cvsroot->cr_dir);
312b8b35aa3Sjoris else
313b8b35aa3Sjoris fatal("%s: %s", current_cvsroot->cr_dir,
314b8b35aa3Sjoris strerror(errno));
315b8b35aa3Sjoris } else {
316b8b35aa3Sjoris if (!S_ISDIR(st.st_mode))
317b8b35aa3Sjoris fatal("'%s' is not a directory",
318b8b35aa3Sjoris current_cvsroot->cr_dir);
319b8b35aa3Sjoris }
320b8b35aa3Sjoris
32175bebbccSjoris if (cvs_cmdop != CVS_OP_INIT) {
32218911d1eSjoris cvs_parse_configfile();
32375bebbccSjoris cvs_parse_modules();
32475bebbccSjoris }
32518911d1eSjoris
3263ad3fb45Sjoris cmdp->cmd(cmd_argc, cmd_argv);
3273ad3fb45Sjoris cvs_cleanup();
3283ad3fb45Sjoris
3293ad3fb45Sjoris return (0);
330ca1d3256Sjfb }
331ca1d3256Sjfb
332ca1d3256Sjfb int
cvs_getopt(int argc,char ** argv)333ca1d3256Sjfb cvs_getopt(int argc, char **argv)
334ca1d3256Sjfb {
335ca1d3256Sjfb int ret;
336ca1d3256Sjfb char *ep;
337ff82c0d8Sjoris const char *errstr;
338ca1d3256Sjfb
33960c775bfSjoris while ((ret = getopt(argc, argv, "b:d:e:flnQqRrs:T:tvwxz:")) != -1) {
3406c121f58Sjfb switch (ret) {
341c68e1832Sjfb case 'b':
342c68e1832Sjfb /*
343c68e1832Sjfb * We do not care about the bin directory for RCS files
344c68e1832Sjfb * as this program has no dependencies on RCS programs,
345c68e1832Sjfb * so it is only here for backwards compatibility.
346c68e1832Sjfb */
347c68e1832Sjfb cvs_log(LP_NOTICE, "the -b argument is obsolete");
348c68e1832Sjfb break;
3496c121f58Sjfb case 'd':
3506c121f58Sjfb cvs_rootstr = optarg;
3516c121f58Sjfb break;
3526c121f58Sjfb case 'e':
3536c121f58Sjfb cvs_editor = optarg;
3546c121f58Sjfb break;
3556c121f58Sjfb case 'f':
356ca1d3256Sjfb cvs_readrc = 0;
3576c121f58Sjfb break;
3586c121f58Sjfb case 'l':
3596c121f58Sjfb cvs_nolog = 1;
3606c121f58Sjfb break;
3616c121f58Sjfb case 'n':
3620bda20e2Sxsa cvs_noexec = 1;
3636c1db92dSxsa cvs_nolog = 1;
3646c121f58Sjfb break;
3656c121f58Sjfb case 'Q':
3666c121f58Sjfb verbosity = 0;
3676c121f58Sjfb break;
3686c121f58Sjfb case 'q':
36960c775bfSjoris if (verbosity > 1)
37060c775bfSjoris verbosity = 1;
3716c121f58Sjfb break;
3723dcb84b2Sxsa case 'R':
3733dcb84b2Sxsa cvs_readonlyfs = 1;
3743dcb84b2Sxsa cvs_nolog = 1;
3753dcb84b2Sxsa break;
3766c121f58Sjfb case 'r':
3776c121f58Sjfb cvs_readonly = 1;
3786c121f58Sjfb break;
3792d540844Sjfb case 's':
3802d540844Sjfb ep = strchr(optarg, '=');
3812d540844Sjfb if (ep == NULL) {
3822d540844Sjfb cvs_log(LP_ERR, "no = in variable assignment");
3833ad3fb45Sjoris exit(1);
3842d540844Sjfb }
3852d540844Sjfb *(ep++) = '\0';
3862d540844Sjfb if (cvs_var_set(optarg, ep) < 0)
3873ad3fb45Sjoris exit(1);
3882d540844Sjfb break;
389e5cbdd76Sxsa case 'T':
390e5cbdd76Sxsa cvs_tmpdir = optarg;
391e5cbdd76Sxsa break;
3926c121f58Sjfb case 't':
3936c121f58Sjfb cvs_trace = 1;
3946c121f58Sjfb break;
3956c121f58Sjfb case 'v':
3966c121f58Sjfb printf("%s\n", CVS_VERSION);
3976c121f58Sjfb exit(0);
3986c121f58Sjfb /* NOTREACHED */
3990e60680aSxsa case 'w':
4000e60680aSxsa cvs_readonly = 0;
4010e60680aSxsa break;
402c68e1832Sjfb case 'x':
403c68e1832Sjfb /*
404c68e1832Sjfb * Kerberos encryption support, kept for compatibility
405c68e1832Sjfb */
406c68e1832Sjfb break;
4076c121f58Sjfb case 'z':
408ff82c0d8Sjoris cvs_compress = strtonum(optarg, 0, 9, &errstr);
409ff82c0d8Sjoris if (errstr != NULL)
410ff82c0d8Sjoris fatal("cvs_compress: %s", errstr);
4116c121f58Sjfb break;
4126c121f58Sjfb default:
4136c121f58Sjfb usage();
41445357ec5Sxsa /* NOTREACHED */
4156c121f58Sjfb }
4166c121f58Sjfb }
4176c121f58Sjfb
418ca1d3256Sjfb ret = optind;
4196c121f58Sjfb optind = 1;
420ca1d3256Sjfb optreset = 1; /* for next call */
42176a571ccSjfb
4226c121f58Sjfb return (ret);
4236c121f58Sjfb }
4246c121f58Sjfb
4256c121f58Sjfb /*
426ca1d3256Sjfb * cvs_read_rcfile()
4276c121f58Sjfb *
4286c121f58Sjfb * Read the CVS `.cvsrc' file in the user's home directory. If the file
4296c121f58Sjfb * exists, it should contain a list of arguments that should always be given
4306c121f58Sjfb * implicitly to the specified commands.
4316c121f58Sjfb */
4325db46beeSjoris static void
cvs_read_rcfile(void)433ca1d3256Sjfb cvs_read_rcfile(void)
4346c121f58Sjfb {
435b9fc9a72Sderaadt char rcpath[PATH_MAX], *buf, *lbuf, *lp, *p;
43683bac5fbStobias int cmd_parsed, cvs_parsed, i, linenum;
4371f51c0c7Stobias size_t len, pos;
4389d091990Stobias struct cvs_cmd *tcmdp;
4396c121f58Sjfb FILE *fp;
4406c121f58Sjfb
441e40de241Sxsa linenum = 0;
442e40de241Sxsa
443b9fc9a72Sderaadt i = snprintf(rcpath, PATH_MAX, "%s/%s", cvs_homedir, CVS_PATH_RC);
444b9fc9a72Sderaadt if (i < 0 || i >= PATH_MAX) {
4459fe3180aSxsa cvs_log(LP_ERRNO, "%s", rcpath);
44627b85f85Sxsa return;
44727b85f85Sxsa }
4486c121f58Sjfb
4496c121f58Sjfb fp = fopen(rcpath, "r");
4506c121f58Sjfb if (fp == NULL) {
4516c121f58Sjfb if (errno != ENOENT)
4526c121f58Sjfb cvs_log(LP_NOTICE, "failed to open `%s': %s", rcpath,
4536c121f58Sjfb strerror(errno));
4546c121f58Sjfb return;
4556c121f58Sjfb }
4566c121f58Sjfb
45783bac5fbStobias cmd_parsed = cvs_parsed = 0;
458a1013731Stobias lbuf = NULL;
459a1013731Stobias while ((buf = fgetln(fp, &len)) != NULL) {
460a1013731Stobias if (buf[len - 1] == '\n') {
461a1013731Stobias buf[len - 1] = '\0';
462a1013731Stobias } else {
463a1013731Stobias lbuf = xmalloc(len + 1);
464a1013731Stobias memcpy(lbuf, buf, len);
465a1013731Stobias lbuf[len] = '\0';
466a1013731Stobias buf = lbuf;
467ca1d3256Sjfb }
468a1013731Stobias
469a1013731Stobias linenum++;
4706c121f58Sjfb
471099a59c1Sjoris /* skip any whitespaces */
472a1013731Stobias p = buf;
473099a59c1Sjoris while (*p == ' ')
474d30bb22aSderaadt p++;
475099a59c1Sjoris
476c13d99f3Stobias /*
477c13d99f3Stobias * Allow comments.
478c13d99f3Stobias * GNU cvs stops parsing a line if it encounters a \t
479c13d99f3Stobias * in front of a command, stick at this behaviour for
480c13d99f3Stobias * compatibility.
481c13d99f3Stobias */
482c13d99f3Stobias if (*p == '#' || *p == '\t')
483099a59c1Sjoris continue;
484099a59c1Sjoris
4851f51c0c7Stobias pos = strcspn(p, " \t");
4861f51c0c7Stobias if (pos == strlen(p)) {
487c70dac2bStobias lp = NULL;
4881f51c0c7Stobias } else {
4891f51c0c7Stobias lp = p + pos;
490ca1d3256Sjfb *lp = '\0';
4911f51c0c7Stobias }
4921f51c0c7Stobias
49383bac5fbStobias if (strcmp(p, "cvs") == 0 && !cvs_parsed) {
494ca1d3256Sjfb /*
495ca1d3256Sjfb * Global default options. In the case of cvs only,
496ca1d3256Sjfb * we keep the 'cvs' string as first argument because
497ca1d3256Sjfb * getopt() does not like starting at index 0 for
498ca1d3256Sjfb * argument processing.
499ca1d3256Sjfb */
500c70dac2bStobias if (lp != NULL) {
501ca1d3256Sjfb *lp = ' ';
5020450b43bSjoris cvs_defargs = xstrdup(p);
503c70dac2bStobias }
50483bac5fbStobias cvs_parsed = 1;
5053917c9bfSderaadt } else {
50683bac5fbStobias tcmdp = cvs_findcmd(p);
50783bac5fbStobias if (tcmdp == NULL && verbosity == 2)
5086c121f58Sjfb cvs_log(LP_NOTICE,
509e14657bfSxsa "unknown command `%s' in `%s:%d'",
510099a59c1Sjoris p, rcpath, linenum);
51183bac5fbStobias
51283bac5fbStobias if (tcmdp != cmdp || cmd_parsed)
5136c121f58Sjfb continue;
514ca1d3256Sjfb
515c70dac2bStobias if (lp != NULL) {
516c70dac2bStobias lp++;
5170450b43bSjoris cmdp->cmd_defargs = xstrdup(lp);
518c70dac2bStobias }
51983bac5fbStobias cmd_parsed = 1;
5206c121f58Sjfb }
5216c121f58Sjfb }
522397ddb8aSnicm free(lbuf);
5233ad3fb45Sjoris
5246c121f58Sjfb if (ferror(fp)) {
5253b801bd8Sxsa cvs_log(LP_NOTICE, "failed to read line from `%s'", rcpath);
5266c121f58Sjfb }
5276c121f58Sjfb
5286c121f58Sjfb (void)fclose(fp);
5296c121f58Sjfb }
5302d540844Sjfb
5312d540844Sjfb /*
5322d540844Sjfb * cvs_var_set()
5332d540844Sjfb *
5342d540844Sjfb * Set the value of the variable <var> to <val>. If there is no such variable,
5352d540844Sjfb * a new entry is created, otherwise the old value is overwritten.
5362d540844Sjfb * Returns 0 on success, or -1 on failure.
5372d540844Sjfb */
5382d540844Sjfb int
cvs_var_set(const char * var,const char * val)5392d540844Sjfb cvs_var_set(const char *var, const char *val)
5402d540844Sjfb {
5412d540844Sjfb const char *cp;
5422d540844Sjfb struct cvs_var *vp;
5432d540844Sjfb
544d593696fSderaadt if (var == NULL || *var == '\0') {
5452d540844Sjfb cvs_log(LP_ERR, "no variable name");
5462d540844Sjfb return (-1);
5472d540844Sjfb }
5482d540844Sjfb
5492d540844Sjfb /* sanity check on the name */
5502d540844Sjfb for (cp = var; *cp != '\0'; cp++)
551f6ac027fSokan if (!isalnum((unsigned char)*cp) && (*cp != '_')) {
5522d540844Sjfb cvs_log(LP_ERR,
5532d540844Sjfb "variable name `%s' contains invalid characters",
5542d540844Sjfb var);
5552d540844Sjfb return (-1);
5562d540844Sjfb }
5572d540844Sjfb
5582d540844Sjfb TAILQ_FOREACH(vp, &cvs_variables, cv_link)
5592d540844Sjfb if (strcmp(vp->cv_name, var) == 0)
5602d540844Sjfb break;
5612d540844Sjfb
5622d540844Sjfb if (vp == NULL) {
5633b4c5c25Sray vp = xcalloc(1, sizeof(*vp));
5642d540844Sjfb
5650450b43bSjoris vp->cv_name = xstrdup(var);
5662d540844Sjfb TAILQ_INSERT_TAIL(&cvs_variables, vp, cv_link);
5672d540844Sjfb
5682d540844Sjfb } else /* free the previous value */
569397ddb8aSnicm free(vp->cv_val);
5702d540844Sjfb
571b794e5f5Stobias vp->cv_val = xstrdup(val);
5722d540844Sjfb
5732d540844Sjfb return (0);
5742d540844Sjfb }
5752d540844Sjfb
5762d540844Sjfb /*
5772b2aca23Sotto * cvs_var_unset()
5782d540844Sjfb *
5792d540844Sjfb * Remove any entry for the variable <var>.
5802d540844Sjfb * Returns 0 on success, or -1 on failure.
5812d540844Sjfb */
5822d540844Sjfb int
cvs_var_unset(const char * var)5832d540844Sjfb cvs_var_unset(const char *var)
5842d540844Sjfb {
5852d540844Sjfb struct cvs_var *vp;
5862d540844Sjfb
5872d540844Sjfb TAILQ_FOREACH(vp, &cvs_variables, cv_link)
5882d540844Sjfb if (strcmp(vp->cv_name, var) == 0) {
5892d540844Sjfb TAILQ_REMOVE(&cvs_variables, vp, cv_link);
590397ddb8aSnicm free(vp->cv_name);
591397ddb8aSnicm free(vp->cv_val);
592397ddb8aSnicm free(vp);
5932d540844Sjfb return (0);
5942d540844Sjfb }
5952d540844Sjfb
5962d540844Sjfb return (-1);
5972d540844Sjfb }
5982d540844Sjfb
5992d540844Sjfb /*
6002d540844Sjfb * cvs_var_get()
6012d540844Sjfb *
6022d540844Sjfb * Get the value associated with the variable <var>. Returns a pointer to the
6032d540844Sjfb * value string on success, or NULL if the variable does not exist.
6042d540844Sjfb */
6052d540844Sjfb
6062d540844Sjfb const char *
cvs_var_get(const char * var)6072d540844Sjfb cvs_var_get(const char *var)
6082d540844Sjfb {
6092d540844Sjfb struct cvs_var *vp;
6102d540844Sjfb
6112d540844Sjfb TAILQ_FOREACH(vp, &cvs_variables, cv_link)
6122d540844Sjfb if (strcmp(vp->cv_name, var) == 0)
6132d540844Sjfb return (vp->cv_val);
6142d540844Sjfb
6152d540844Sjfb return (NULL);
6162d540844Sjfb }
617