1 |
|
|
/* $OpenBSD: cvs.c,v 1.159 2017/06/01 08:08:24 joris Exp $ */ |
2 |
|
|
/* |
3 |
|
|
* Copyright (c) 2006, 2007 Joris Vink <joris@openbsd.org> |
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/stat.h> |
29 |
|
|
|
30 |
|
|
#include <ctype.h> |
31 |
|
|
#include <errno.h> |
32 |
|
|
#include <pwd.h> |
33 |
|
|
#include <stdlib.h> |
34 |
|
|
#include <string.h> |
35 |
|
|
#include <time.h> |
36 |
|
|
#include <unistd.h> |
37 |
|
|
#include <err.h> |
38 |
|
|
|
39 |
|
|
#include "cvs.h" |
40 |
|
|
#include "remote.h" |
41 |
|
|
#include "hash.h" |
42 |
|
|
|
43 |
|
|
extern char *__progname; |
44 |
|
|
|
45 |
|
|
/* verbosity level: 0 = really quiet, 1 = quiet, 2 = verbose */ |
46 |
|
|
int verbosity = 2; |
47 |
|
|
|
48 |
|
|
/* compression level used with zlib, 0 meaning no compression taking place */ |
49 |
|
|
int cvs_compress = 0; |
50 |
|
|
int cvs_readrc = 1; /* read .cvsrc on startup */ |
51 |
|
|
int cvs_trace = 0; |
52 |
|
|
int cvs_nolog = 0; |
53 |
|
|
int cvs_readonly = 0; |
54 |
|
|
int cvs_readonlyfs = 0; |
55 |
|
|
int cvs_nocase = 0; /* set to 1 to disable filename case sensitivity */ |
56 |
|
|
int cvs_noexec = 0; /* set to 1 to disable disk operations (-n option) */ |
57 |
|
|
int cvs_cmdop; |
58 |
|
|
int cvs_umask = CVS_UMASK_DEFAULT; |
59 |
|
|
int cvs_server_active = 0; |
60 |
|
|
|
61 |
|
|
char *cvs_tagname = NULL; |
62 |
|
|
char *cvs_defargs; /* default global arguments from .cvsrc */ |
63 |
|
|
char *cvs_rootstr; |
64 |
|
|
char *cvs_rsh = CVS_RSH_DEFAULT; |
65 |
|
|
char *cvs_editor = CVS_EDITOR_DEFAULT; |
66 |
|
|
char *cvs_homedir = NULL; |
67 |
|
|
char *cvs_tmpdir = CVS_TMPDIR_DEFAULT; |
68 |
|
|
|
69 |
|
|
struct cvsroot *current_cvsroot = NULL; |
70 |
|
|
struct cvs_cmd *cmdp; /* struct of command we are running */ |
71 |
|
|
|
72 |
|
|
int cvs_getopt(int, char **); |
73 |
|
|
__dead void usage(void); |
74 |
|
|
static void cvs_read_rcfile(void); |
75 |
|
|
|
76 |
|
|
struct wklhead temp_files; |
77 |
|
|
|
78 |
|
|
void sighandler(int); |
79 |
|
|
volatile sig_atomic_t cvs_quit = 0; |
80 |
|
|
volatile sig_atomic_t sig_received = 0; |
81 |
|
|
|
82 |
|
|
extern CVSENTRIES *current_list; |
83 |
|
|
|
84 |
|
|
struct hash_table created_directories; |
85 |
|
|
struct hash_table created_cvs_directories; |
86 |
|
|
|
87 |
|
|
void |
88 |
|
|
sighandler(int sig) |
89 |
|
|
{ |
90 |
|
|
sig_received = sig; |
91 |
|
|
|
92 |
|
|
switch (sig) { |
93 |
|
|
case SIGINT: |
94 |
|
|
case SIGTERM: |
95 |
|
|
case SIGPIPE: |
96 |
|
|
cvs_quit = 1; |
97 |
|
|
break; |
98 |
|
|
default: |
99 |
|
|
break; |
100 |
|
|
} |
101 |
|
|
} |
102 |
|
|
|
103 |
|
|
void |
104 |
|
|
cvs_cleanup(void) |
105 |
|
|
{ |
106 |
|
|
cvs_log(LP_TRACE, "cvs_cleanup: removing locks"); |
107 |
|
|
worklist_run(&repo_locks, worklist_unlink); |
108 |
|
|
|
109 |
|
|
cvs_log(LP_TRACE, "cvs_cleanup: removing temp files"); |
110 |
|
|
worklist_run(&temp_files, worklist_unlink); |
111 |
|
|
|
112 |
|
|
if (cvs_server_path != NULL) { |
113 |
|
|
if (cvs_rmdir(cvs_server_path) == -1) |
114 |
|
|
cvs_log(LP_ERR, |
115 |
|
|
"warning: failed to remove server directory: %s", |
116 |
|
|
cvs_server_path); |
117 |
|
|
free(cvs_server_path); |
118 |
|
|
cvs_server_path = NULL; |
119 |
|
|
} |
120 |
|
|
|
121 |
|
|
if (current_list != NULL) |
122 |
|
|
cvs_ent_close(current_list, ENT_SYNC); |
123 |
|
|
} |
124 |
|
|
|
125 |
|
|
__dead void |
126 |
|
|
usage(void) |
127 |
|
|
{ |
128 |
|
|
(void)fprintf(stderr, |
129 |
|
|
"usage: %s [-flnQqRrtvw] [-d root] [-e editor] [-s var=val]\n" |
130 |
|
|
" [-T tmpdir] [-z level] command ...\n", __progname); |
131 |
|
|
exit(1); |
132 |
|
|
} |
133 |
|
|
|
134 |
|
|
int |
135 |
|
|
cvs_build_cmd(char ***cmd_argv, char **argv, int argc) |
136 |
|
|
{ |
137 |
|
|
int cmd_argc, i, cur; |
138 |
|
|
char *cp, *linebuf, *lp; |
139 |
|
|
|
140 |
|
|
if (cmdp->cmd_defargs == NULL) { |
141 |
|
|
*cmd_argv = argv; |
142 |
|
|
return argc; |
143 |
|
|
} |
144 |
|
|
|
145 |
|
|
cur = argc + 2; |
146 |
|
|
cmd_argc = 0; |
147 |
|
|
*cmd_argv = xcalloc(cur, sizeof(char *)); |
148 |
|
|
(*cmd_argv)[cmd_argc++] = argv[0]; |
149 |
|
|
|
150 |
|
|
linebuf = xstrdup(cmdp->cmd_defargs); |
151 |
|
|
for (lp = linebuf; lp != NULL;) { |
152 |
|
|
cp = strsep(&lp, " \t\b\f\n\r\t\v"); |
153 |
|
|
if (cp == NULL) |
154 |
|
|
break; |
155 |
|
|
if (*cp == '\0') |
156 |
|
|
continue; |
157 |
|
|
|
158 |
|
|
if (cmd_argc == cur) { |
159 |
|
|
cur += 8; |
160 |
|
|
*cmd_argv = xreallocarray(*cmd_argv, cur, |
161 |
|
|
sizeof(char *)); |
162 |
|
|
} |
163 |
|
|
|
164 |
|
|
(*cmd_argv)[cmd_argc++] = cp; |
165 |
|
|
} |
166 |
|
|
|
167 |
|
|
if (cmd_argc + argc > cur) { |
168 |
|
|
cur = cmd_argc + argc + 1; |
169 |
|
|
*cmd_argv = xreallocarray(*cmd_argv, cur, |
170 |
|
|
sizeof(char *)); |
171 |
|
|
} |
172 |
|
|
|
173 |
|
|
for (i = 1; i < argc; i++) |
174 |
|
|
(*cmd_argv)[cmd_argc++] = argv[i]; |
175 |
|
|
|
176 |
|
|
(*cmd_argv)[cmd_argc] = NULL; |
177 |
|
|
|
178 |
|
|
return cmd_argc; |
179 |
|
|
} |
180 |
|
|
|
181 |
|
|
int |
182 |
|
|
main(int argc, char **argv) |
183 |
|
|
{ |
184 |
|
|
char *envstr, **cmd_argv, **targv; |
185 |
|
|
int i, ret, cmd_argc; |
186 |
|
|
struct passwd *pw; |
187 |
|
|
struct stat st; |
188 |
|
|
char fpath[PATH_MAX]; |
189 |
|
|
|
190 |
|
|
if (pledge("stdio rpath wpath cpath fattr getpw proc exec flock", NULL) == -1) |
191 |
|
|
err(1, "pledge"); |
192 |
|
|
|
193 |
|
|
tzset(); |
194 |
|
|
|
195 |
|
|
TAILQ_INIT(&cvs_variables); |
196 |
|
|
SLIST_INIT(&repo_locks); |
197 |
|
|
SLIST_INIT(&temp_files); |
198 |
|
|
|
199 |
|
|
hash_table_init(&created_directories, 100); |
200 |
|
|
hash_table_init(&created_cvs_directories, 100); |
201 |
|
|
|
202 |
|
|
/* check environment so command-line options override it */ |
203 |
|
|
if ((envstr = getenv("CVS_RSH")) != NULL) |
204 |
|
|
cvs_rsh = envstr; |
205 |
|
|
|
206 |
|
|
if (((envstr = getenv("CVSEDITOR")) != NULL) || |
207 |
|
|
((envstr = getenv("VISUAL")) != NULL) || |
208 |
|
|
((envstr = getenv("EDITOR")) != NULL)) |
209 |
|
|
cvs_editor = envstr; |
210 |
|
|
|
211 |
|
|
if ((envstr = getenv("CVSREAD")) != NULL) |
212 |
|
|
cvs_readonly = 1; |
213 |
|
|
|
214 |
|
|
if ((envstr = getenv("CVSREADONLYFS")) != NULL) { |
215 |
|
|
cvs_readonlyfs = 1; |
216 |
|
|
cvs_nolog = 1; |
217 |
|
|
} |
218 |
|
|
|
219 |
|
|
if ((cvs_homedir = getenv("HOME")) == NULL) { |
220 |
|
|
if ((pw = getpwuid(getuid())) != NULL) |
221 |
|
|
cvs_homedir = pw->pw_dir; |
222 |
|
|
} |
223 |
|
|
|
224 |
|
|
if ((envstr = getenv("TMPDIR")) != NULL) |
225 |
|
|
cvs_tmpdir = envstr; |
226 |
|
|
|
227 |
|
|
ret = cvs_getopt(argc, argv); |
228 |
|
|
|
229 |
|
|
argc -= ret; |
230 |
|
|
argv += ret; |
231 |
|
|
if (argc == 0) |
232 |
|
|
usage(); |
233 |
|
|
|
234 |
|
|
cmdp = cvs_findcmd(argv[0]); |
235 |
|
|
if (cmdp == NULL) { |
236 |
|
|
fprintf(stderr, "Unknown command: `%s'\n\n", argv[0]); |
237 |
|
|
fprintf(stderr, "CVS commands are:\n"); |
238 |
|
|
for (i = 0; cvs_cdt[i] != NULL; i++) |
239 |
|
|
fprintf(stderr, "\t%-16s%s\n", |
240 |
|
|
cvs_cdt[i]->cmd_name, cvs_cdt[i]->cmd_descr); |
241 |
|
|
exit(1); |
242 |
|
|
} |
243 |
|
|
|
244 |
|
|
/* |
245 |
|
|
* check the tmp dir, either specified through |
246 |
|
|
* the environment variable TMPDIR, or via |
247 |
|
|
* the global option -T <dir> |
248 |
|
|
*/ |
249 |
|
|
if (stat(cvs_tmpdir, &st) == -1) |
250 |
|
|
fatal("stat failed on `%s': %s", cvs_tmpdir, strerror(errno)); |
251 |
|
|
else if (!S_ISDIR(st.st_mode)) |
252 |
|
|
fatal("`%s' is not valid temporary directory", cvs_tmpdir); |
253 |
|
|
|
254 |
|
|
if (cvs_readrc == 1 && cvs_homedir != NULL) { |
255 |
|
|
cvs_read_rcfile(); |
256 |
|
|
|
257 |
|
|
if (cvs_defargs != NULL) { |
258 |
|
|
if ((targv = cvs_makeargv(cvs_defargs, &i)) == NULL) |
259 |
|
|
fatal("failed to load default arguments to %s", |
260 |
|
|
__progname); |
261 |
|
|
|
262 |
|
|
cvs_getopt(i, targv); |
263 |
|
|
cvs_freeargv(targv, i); |
264 |
|
|
free(targv); |
265 |
|
|
} |
266 |
|
|
} |
267 |
|
|
|
268 |
|
|
/* setup signal handlers */ |
269 |
|
|
signal(SIGTERM, sighandler); |
270 |
|
|
signal(SIGINT, sighandler); |
271 |
|
|
signal(SIGHUP, sighandler); |
272 |
|
|
signal(SIGABRT, sighandler); |
273 |
|
|
signal(SIGALRM, sighandler); |
274 |
|
|
signal(SIGPIPE, sighandler); |
275 |
|
|
|
276 |
|
|
cvs_cmdop = cmdp->cmd_op; |
277 |
|
|
|
278 |
|
|
cmd_argc = cvs_build_cmd(&cmd_argv, argv, argc); |
279 |
|
|
|
280 |
|
|
cvs_file_init(); |
281 |
|
|
|
282 |
|
|
if (cvs_cmdop == CVS_OP_SERVER) { |
283 |
|
|
cmdp->cmd(cmd_argc, cmd_argv); |
284 |
|
|
cvs_cleanup(); |
285 |
|
|
return (0); |
286 |
|
|
} |
287 |
|
|
|
288 |
|
|
cvs_umask = umask(0); |
289 |
|
|
umask(cvs_umask); |
290 |
|
|
|
291 |
|
|
if ((current_cvsroot = cvsroot_get(".")) == NULL) { |
292 |
|
|
cvs_log(LP_ERR, |
293 |
|
|
"No CVSROOT specified! Please use the '-d' option"); |
294 |
|
|
fatal("or set the CVSROOT environment variable."); |
295 |
|
|
} |
296 |
|
|
|
297 |
|
|
if (cvsroot_is_remote()) { |
298 |
|
|
cmdp->cmd(cmd_argc, cmd_argv); |
299 |
|
|
cvs_cleanup(); |
300 |
|
|
return (0); |
301 |
|
|
} |
302 |
|
|
|
303 |
|
|
(void)xsnprintf(fpath, sizeof(fpath), "%s/%s", |
304 |
|
|
current_cvsroot->cr_dir, CVS_PATH_ROOT); |
305 |
|
|
|
306 |
|
|
if (stat(fpath, &st) == -1 && cvs_cmdop != CVS_OP_INIT) { |
307 |
|
|
if (errno == ENOENT) |
308 |
|
|
fatal("repository '%s' does not exist", |
309 |
|
|
current_cvsroot->cr_dir); |
310 |
|
|
else |
311 |
|
|
fatal("%s: %s", current_cvsroot->cr_dir, |
312 |
|
|
strerror(errno)); |
313 |
|
|
} else { |
314 |
|
|
if (!S_ISDIR(st.st_mode)) |
315 |
|
|
fatal("'%s' is not a directory", |
316 |
|
|
current_cvsroot->cr_dir); |
317 |
|
|
} |
318 |
|
|
|
319 |
|
|
if (cvs_cmdop != CVS_OP_INIT) { |
320 |
|
|
cvs_parse_configfile(); |
321 |
|
|
cvs_parse_modules(); |
322 |
|
|
} |
323 |
|
|
|
324 |
|
|
cmdp->cmd(cmd_argc, cmd_argv); |
325 |
|
|
cvs_cleanup(); |
326 |
|
|
|
327 |
|
|
return (0); |
328 |
|
|
} |
329 |
|
|
|
330 |
|
|
int |
331 |
|
|
cvs_getopt(int argc, char **argv) |
332 |
|
|
{ |
333 |
|
|
int ret; |
334 |
|
|
char *ep; |
335 |
|
|
const char *errstr; |
336 |
|
|
|
337 |
|
|
while ((ret = getopt(argc, argv, "b:d:e:flnQqRrs:T:tvwxz:")) != -1) { |
338 |
|
|
switch (ret) { |
339 |
|
|
case 'b': |
340 |
|
|
/* |
341 |
|
|
* We do not care about the bin directory for RCS files |
342 |
|
|
* as this program has no dependencies on RCS programs, |
343 |
|
|
* so it is only here for backwards compatibility. |
344 |
|
|
*/ |
345 |
|
|
cvs_log(LP_NOTICE, "the -b argument is obsolete"); |
346 |
|
|
break; |
347 |
|
|
case 'd': |
348 |
|
|
cvs_rootstr = optarg; |
349 |
|
|
break; |
350 |
|
|
case 'e': |
351 |
|
|
cvs_editor = optarg; |
352 |
|
|
break; |
353 |
|
|
case 'f': |
354 |
|
|
cvs_readrc = 0; |
355 |
|
|
break; |
356 |
|
|
case 'l': |
357 |
|
|
cvs_nolog = 1; |
358 |
|
|
break; |
359 |
|
|
case 'n': |
360 |
|
|
cvs_noexec = 1; |
361 |
|
|
cvs_nolog = 1; |
362 |
|
|
break; |
363 |
|
|
case 'Q': |
364 |
|
|
verbosity = 0; |
365 |
|
|
break; |
366 |
|
|
case 'q': |
367 |
|
|
if (verbosity > 1) |
368 |
|
|
verbosity = 1; |
369 |
|
|
break; |
370 |
|
|
case 'R': |
371 |
|
|
cvs_readonlyfs = 1; |
372 |
|
|
cvs_nolog = 1; |
373 |
|
|
break; |
374 |
|
|
case 'r': |
375 |
|
|
cvs_readonly = 1; |
376 |
|
|
break; |
377 |
|
|
case 's': |
378 |
|
|
ep = strchr(optarg, '='); |
379 |
|
|
if (ep == NULL) { |
380 |
|
|
cvs_log(LP_ERR, "no = in variable assignment"); |
381 |
|
|
exit(1); |
382 |
|
|
} |
383 |
|
|
*(ep++) = '\0'; |
384 |
|
|
if (cvs_var_set(optarg, ep) < 0) |
385 |
|
|
exit(1); |
386 |
|
|
break; |
387 |
|
|
case 'T': |
388 |
|
|
cvs_tmpdir = optarg; |
389 |
|
|
break; |
390 |
|
|
case 't': |
391 |
|
|
cvs_trace = 1; |
392 |
|
|
break; |
393 |
|
|
case 'v': |
394 |
|
|
printf("%s\n", CVS_VERSION); |
395 |
|
|
exit(0); |
396 |
|
|
/* NOTREACHED */ |
397 |
|
|
case 'w': |
398 |
|
|
cvs_readonly = 0; |
399 |
|
|
break; |
400 |
|
|
case 'x': |
401 |
|
|
/* |
402 |
|
|
* Kerberos encryption support, kept for compatibility |
403 |
|
|
*/ |
404 |
|
|
break; |
405 |
|
|
case 'z': |
406 |
|
|
cvs_compress = strtonum(optarg, 0, 9, &errstr); |
407 |
|
|
if (errstr != NULL) |
408 |
|
|
fatal("cvs_compress: %s", errstr); |
409 |
|
|
break; |
410 |
|
|
default: |
411 |
|
|
usage(); |
412 |
|
|
/* NOTREACHED */ |
413 |
|
|
} |
414 |
|
|
} |
415 |
|
|
|
416 |
|
|
ret = optind; |
417 |
|
|
optind = 1; |
418 |
|
|
optreset = 1; /* for next call */ |
419 |
|
|
|
420 |
|
|
return (ret); |
421 |
|
|
} |
422 |
|
|
|
423 |
|
|
/* |
424 |
|
|
* cvs_read_rcfile() |
425 |
|
|
* |
426 |
|
|
* Read the CVS `.cvsrc' file in the user's home directory. If the file |
427 |
|
|
* exists, it should contain a list of arguments that should always be given |
428 |
|
|
* implicitly to the specified commands. |
429 |
|
|
*/ |
430 |
|
|
static void |
431 |
|
|
cvs_read_rcfile(void) |
432 |
|
|
{ |
433 |
|
|
char rcpath[PATH_MAX], *buf, *lbuf, *lp, *p; |
434 |
|
|
int cmd_parsed, cvs_parsed, i, linenum; |
435 |
|
|
size_t len, pos; |
436 |
|
|
struct cvs_cmd *tcmdp; |
437 |
|
|
FILE *fp; |
438 |
|
|
|
439 |
|
|
linenum = 0; |
440 |
|
|
|
441 |
|
|
i = snprintf(rcpath, PATH_MAX, "%s/%s", cvs_homedir, CVS_PATH_RC); |
442 |
|
|
if (i < 0 || i >= PATH_MAX) { |
443 |
|
|
cvs_log(LP_ERRNO, "%s", rcpath); |
444 |
|
|
return; |
445 |
|
|
} |
446 |
|
|
|
447 |
|
|
fp = fopen(rcpath, "r"); |
448 |
|
|
if (fp == NULL) { |
449 |
|
|
if (errno != ENOENT) |
450 |
|
|
cvs_log(LP_NOTICE, "failed to open `%s': %s", rcpath, |
451 |
|
|
strerror(errno)); |
452 |
|
|
return; |
453 |
|
|
} |
454 |
|
|
|
455 |
|
|
cmd_parsed = cvs_parsed = 0; |
456 |
|
|
lbuf = NULL; |
457 |
|
|
while ((buf = fgetln(fp, &len)) != NULL) { |
458 |
|
|
if (buf[len - 1] == '\n') { |
459 |
|
|
buf[len - 1] = '\0'; |
460 |
|
|
} else { |
461 |
|
|
lbuf = xmalloc(len + 1); |
462 |
|
|
memcpy(lbuf, buf, len); |
463 |
|
|
lbuf[len] = '\0'; |
464 |
|
|
buf = lbuf; |
465 |
|
|
} |
466 |
|
|
|
467 |
|
|
linenum++; |
468 |
|
|
|
469 |
|
|
/* skip any whitespaces */ |
470 |
|
|
p = buf; |
471 |
|
|
while (*p == ' ') |
472 |
|
|
p++; |
473 |
|
|
|
474 |
|
|
/* |
475 |
|
|
* Allow comments. |
476 |
|
|
* GNU cvs stops parsing a line if it encounters a \t |
477 |
|
|
* in front of a command, stick at this behaviour for |
478 |
|
|
* compatibility. |
479 |
|
|
*/ |
480 |
|
|
if (*p == '#' || *p == '\t') |
481 |
|
|
continue; |
482 |
|
|
|
483 |
|
|
pos = strcspn(p, " \t"); |
484 |
|
|
if (pos == strlen(p)) { |
485 |
|
|
lp = NULL; |
486 |
|
|
} else { |
487 |
|
|
lp = p + pos; |
488 |
|
|
*lp = '\0'; |
489 |
|
|
} |
490 |
|
|
|
491 |
|
|
if (strcmp(p, "cvs") == 0 && !cvs_parsed) { |
492 |
|
|
/* |
493 |
|
|
* Global default options. In the case of cvs only, |
494 |
|
|
* we keep the 'cvs' string as first argument because |
495 |
|
|
* getopt() does not like starting at index 0 for |
496 |
|
|
* argument processing. |
497 |
|
|
*/ |
498 |
|
|
if (lp != NULL) { |
499 |
|
|
*lp = ' '; |
500 |
|
|
cvs_defargs = xstrdup(p); |
501 |
|
|
} |
502 |
|
|
cvs_parsed = 1; |
503 |
|
|
} else { |
504 |
|
|
tcmdp = cvs_findcmd(p); |
505 |
|
|
if (tcmdp == NULL && verbosity == 2) |
506 |
|
|
cvs_log(LP_NOTICE, |
507 |
|
|
"unknown command `%s' in `%s:%d'", |
508 |
|
|
p, rcpath, linenum); |
509 |
|
|
|
510 |
|
|
if (tcmdp != cmdp || cmd_parsed) |
511 |
|
|
continue; |
512 |
|
|
|
513 |
|
|
if (lp != NULL) { |
514 |
|
|
lp++; |
515 |
|
|
cmdp->cmd_defargs = xstrdup(lp); |
516 |
|
|
} |
517 |
|
|
cmd_parsed = 1; |
518 |
|
|
} |
519 |
|
|
} |
520 |
|
|
free(lbuf); |
521 |
|
|
|
522 |
|
|
if (ferror(fp)) { |
523 |
|
|
cvs_log(LP_NOTICE, "failed to read line from `%s'", rcpath); |
524 |
|
|
} |
525 |
|
|
|
526 |
|
|
(void)fclose(fp); |
527 |
|
|
} |
528 |
|
|
|
529 |
|
|
/* |
530 |
|
|
* cvs_var_set() |
531 |
|
|
* |
532 |
|
|
* Set the value of the variable <var> to <val>. If there is no such variable, |
533 |
|
|
* a new entry is created, otherwise the old value is overwritten. |
534 |
|
|
* Returns 0 on success, or -1 on failure. |
535 |
|
|
*/ |
536 |
|
|
int |
537 |
|
|
cvs_var_set(const char *var, const char *val) |
538 |
|
|
{ |
539 |
|
|
const char *cp; |
540 |
|
|
struct cvs_var *vp; |
541 |
|
|
|
542 |
|
|
if (var == NULL || *var == '\0') { |
543 |
|
|
cvs_log(LP_ERR, "no variable name"); |
544 |
|
|
return (-1); |
545 |
|
|
} |
546 |
|
|
|
547 |
|
|
/* sanity check on the name */ |
548 |
|
|
for (cp = var; *cp != '\0'; cp++) |
549 |
|
|
if (!isalnum((unsigned char)*cp) && (*cp != '_')) { |
550 |
|
|
cvs_log(LP_ERR, |
551 |
|
|
"variable name `%s' contains invalid characters", |
552 |
|
|
var); |
553 |
|
|
return (-1); |
554 |
|
|
} |
555 |
|
|
|
556 |
|
|
TAILQ_FOREACH(vp, &cvs_variables, cv_link) |
557 |
|
|
if (strcmp(vp->cv_name, var) == 0) |
558 |
|
|
break; |
559 |
|
|
|
560 |
|
|
if (vp == NULL) { |
561 |
|
|
vp = xcalloc(1, sizeof(*vp)); |
562 |
|
|
|
563 |
|
|
vp->cv_name = xstrdup(var); |
564 |
|
|
TAILQ_INSERT_TAIL(&cvs_variables, vp, cv_link); |
565 |
|
|
|
566 |
|
|
} else /* free the previous value */ |
567 |
|
|
free(vp->cv_val); |
568 |
|
|
|
569 |
|
|
vp->cv_val = xstrdup(val); |
570 |
|
|
|
571 |
|
|
return (0); |
572 |
|
|
} |
573 |
|
|
|
574 |
|
|
/* |
575 |
|
|
* cvs_var_unset() |
576 |
|
|
* |
577 |
|
|
* Remove any entry for the variable <var>. |
578 |
|
|
* Returns 0 on success, or -1 on failure. |
579 |
|
|
*/ |
580 |
|
|
int |
581 |
|
|
cvs_var_unset(const char *var) |
582 |
|
|
{ |
583 |
|
|
struct cvs_var *vp; |
584 |
|
|
|
585 |
|
|
TAILQ_FOREACH(vp, &cvs_variables, cv_link) |
586 |
|
|
if (strcmp(vp->cv_name, var) == 0) { |
587 |
|
|
TAILQ_REMOVE(&cvs_variables, vp, cv_link); |
588 |
|
|
free(vp->cv_name); |
589 |
|
|
free(vp->cv_val); |
590 |
|
|
free(vp); |
591 |
|
|
return (0); |
592 |
|
|
} |
593 |
|
|
|
594 |
|
|
return (-1); |
595 |
|
|
} |
596 |
|
|
|
597 |
|
|
/* |
598 |
|
|
* cvs_var_get() |
599 |
|
|
* |
600 |
|
|
* Get the value associated with the variable <var>. Returns a pointer to the |
601 |
|
|
* value string on success, or NULL if the variable does not exist. |
602 |
|
|
*/ |
603 |
|
|
|
604 |
|
|
const char * |
605 |
|
|
cvs_var_get(const char *var) |
606 |
|
|
{ |
607 |
|
|
struct cvs_var *vp; |
608 |
|
|
|
609 |
|
|
TAILQ_FOREACH(vp, &cvs_variables, cv_link) |
610 |
|
|
if (strcmp(vp->cv_name, var) == 0) |
611 |
|
|
return (vp->cv_val); |
612 |
|
|
|
613 |
|
|
return (NULL); |
614 |
|
|
} |