GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
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 |
82 |
cvs_log(LP_TRACE, "cvs_cleanup: removing locks"); |
|
107 |
41 |
worklist_run(&repo_locks, worklist_unlink); |
|
108 |
|||
109 |
41 |
cvs_log(LP_TRACE, "cvs_cleanup: removing temp files"); |
|
110 |
41 |
worklist_run(&temp_files, worklist_unlink); |
|
111 |
|||
112 |
✗✓ | 41 |
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 |
✓✓ | 41 |
if (current_list != NULL) |
122 |
37 |
cvs_ent_close(current_list, ENT_SYNC); |
|
123 |
41 |
} |
|
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 |
82 |
char *cp, *linebuf, *lp; |
|
139 |
|||
140 |
✓✗ | 41 |
if (cmdp->cmd_defargs == NULL) { |
141 |
41 |
*cmd_argv = argv; |
|
142 |
41 |
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 |
41 |
} |
|
180 |
|||
181 |
int |
||
182 |
main(int argc, char **argv) |
||
183 |
{ |
||
184 |
82 |
char *envstr, **cmd_argv, **targv; |
|
185 |
41 |
int i, ret, cmd_argc; |
|
186 |
struct passwd *pw; |
||
187 |
41 |
struct stat st; |
|
188 |
41 |
char fpath[PATH_MAX]; |
|
189 |
|||
190 |
✗✓ | 41 |
if (pledge("stdio rpath wpath cpath fattr getpw proc exec flock", NULL) == -1) |
191 |
err(1, "pledge"); |
||
192 |
|||
193 |
41 |
tzset(); |
|
194 |
|||
195 |
41 |
TAILQ_INIT(&cvs_variables); |
|
196 |
41 |
SLIST_INIT(&repo_locks); |
|
197 |
41 |
SLIST_INIT(&temp_files); |
|
198 |
|||
199 |
41 |
hash_table_init(&created_directories, 100); |
|
200 |
41 |
hash_table_init(&created_cvs_directories, 100); |
|
201 |
|||
202 |
/* check environment so command-line options override it */ |
||
203 |
✗✓ | 41 |
if ((envstr = getenv("CVS_RSH")) != NULL) |
204 |
cvs_rsh = envstr; |
||
205 |
|||
206 |
✓✗✗✓ |
82 |
if (((envstr = getenv("CVSEDITOR")) != NULL) || |
207 |
✓✗ | 41 |
((envstr = getenv("VISUAL")) != NULL) || |
208 |
41 |
((envstr = getenv("EDITOR")) != NULL)) |
|
209 |
cvs_editor = envstr; |
||
210 |
|||
211 |
✗✓ | 41 |
if ((envstr = getenv("CVSREAD")) != NULL) |
212 |
cvs_readonly = 1; |
||
213 |
|||
214 |
✗✓ | 41 |
if ((envstr = getenv("CVSREADONLYFS")) != NULL) { |
215 |
cvs_readonlyfs = 1; |
||
216 |
cvs_nolog = 1; |
||
217 |
} |
||
218 |
|||
219 |
✗✓ | 41 |
if ((cvs_homedir = getenv("HOME")) == NULL) { |
220 |
if ((pw = getpwuid(getuid())) != NULL) |
||
221 |
cvs_homedir = pw->pw_dir; |
||
222 |
} |
||
223 |
|||
224 |
✗✓ | 41 |
if ((envstr = getenv("TMPDIR")) != NULL) |
225 |
cvs_tmpdir = envstr; |
||
226 |
|||
227 |
41 |
ret = cvs_getopt(argc, argv); |
|
228 |
|||
229 |
41 |
argc -= ret; |
|
230 |
41 |
argv += ret; |
|
231 |
✗✓ | 41 |
if (argc == 0) |
232 |
usage(); |
||
233 |
|||
234 |
41 |
cmdp = cvs_findcmd(argv[0]); |
|
235 |
✗✓ | 41 |
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 |
✗✓ | 41 |
if (stat(cvs_tmpdir, &st) == -1) |
250 |
fatal("stat failed on `%s': %s", cvs_tmpdir, strerror(errno)); |
||
251 |
✗✓ | 41 |
else if (!S_ISDIR(st.st_mode)) |
252 |
fatal("`%s' is not valid temporary directory", cvs_tmpdir); |
||
253 |
|||
254 |
✗✓ | 41 |
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 |
41 |
signal(SIGTERM, sighandler); |
|
270 |
41 |
signal(SIGINT, sighandler); |
|
271 |
41 |
signal(SIGHUP, sighandler); |
|
272 |
41 |
signal(SIGABRT, sighandler); |
|
273 |
41 |
signal(SIGALRM, sighandler); |
|
274 |
41 |
signal(SIGPIPE, sighandler); |
|
275 |
|||
276 |
41 |
cvs_cmdop = cmdp->cmd_op; |
|
277 |
|||
278 |
41 |
cmd_argc = cvs_build_cmd(&cmd_argv, argv, argc); |
|
279 |
|||
280 |
41 |
cvs_file_init(); |
|
281 |
|||
282 |
✗✓ | 41 |
if (cvs_cmdop == CVS_OP_SERVER) { |
283 |
cmdp->cmd(cmd_argc, cmd_argv); |
||
284 |
cvs_cleanup(); |
||
285 |
return (0); |
||
286 |
} |
||
287 |
|||
288 |
41 |
cvs_umask = umask(0); |
|
289 |
41 |
umask(cvs_umask); |
|
290 |
|||
291 |
✗✓ | 41 |
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 |
✗✓ | 41 |
if (cvsroot_is_remote()) { |
298 |
cmdp->cmd(cmd_argc, cmd_argv); |
||
299 |
cvs_cleanup(); |
||
300 |
return (0); |
||
301 |
} |
||
302 |
|||
303 |
82 |
(void)xsnprintf(fpath, sizeof(fpath), "%s/%s", |
|
304 |
41 |
current_cvsroot->cr_dir, CVS_PATH_ROOT); |
|
305 |
|||
306 |
✗✓ | 41 |
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 |
✗✓ | 41 |
if (!S_ISDIR(st.st_mode)) |
315 |
fatal("'%s' is not a directory", |
||
316 |
current_cvsroot->cr_dir); |
||
317 |
} |
||
318 |
|||
319 |
✓✓ | 41 |
if (cvs_cmdop != CVS_OP_INIT) { |
320 |
40 |
cvs_parse_configfile(); |
|
321 |
40 |
cvs_parse_modules(); |
|
322 |
40 |
} |
|
323 |
|||
324 |
41 |
cmdp->cmd(cmd_argc, cmd_argv); |
|
325 |
41 |
cvs_cleanup(); |
|
326 |
|||
327 |
41 |
return (0); |
|
328 |
40 |
} |
|
329 |
|||
330 |
int |
||
331 |
cvs_getopt(int argc, char **argv) |
||
332 |
{ |
||
333 |
int ret; |
||
334 |
char *ep; |
||
335 |
82 |
const char *errstr; |
|
336 |
|||
337 |
✓✓ | 178 |
while ((ret = getopt(argc, argv, "b:d:e:flnQqRrs:T:tvwxz:")) != -1) { |
338 |
✓✗✓✗ ✓✗✗✓ ✓✗✗✗ ✗✗✗✗ ✗✗ |
192 |
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 |
17 |
cvs_rootstr = optarg; |
|
349 |
17 |
break; |
|
350 |
case 'e': |
||
351 |
cvs_editor = optarg; |
||
352 |
break; |
||
353 |
case 'f': |
||
354 |
41 |
cvs_readrc = 0; |
|
355 |
41 |
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 |
35 |
verbosity = 0; |
|
365 |
35 |
break; |
|
366 |
case 'q': |
||
367 |
✗✓ | 3 |
if (verbosity > 1) |
368 |
3 |
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 |
41 |
ret = optind; |
|
417 |
41 |
optind = 1; |
|
418 |
41 |
optreset = 1; /* for next call */ |
|
419 |
|||
420 |
41 |
return (ret); |
|
421 |
41 |
} |
|
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 |
} |
Generated by: GCOVR (Version 3.3) |