Annotation of src/usr.bin/cvs/cvs.c, Revision 1.2
1.1 jfb 1: #define DEBUG
2: /* $OpenBSD$ */
3: /*
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/types.h>
29: #include <sys/wait.h>
30:
31: #include <err.h>
32: #include <pwd.h>
33: #include <errno.h>
34: #include <stdio.h>
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"
43:
44:
45: extern char *__progname;
46:
47:
48: /* verbosity level: 0 = really quiet, 1 = quiet, 2 = verbose */
49: int verbosity = 2;
50:
51:
52:
53: /* compression level used with zlib, 0 meaning no compression taking place */
54: int cvs_compress = 0;
55: int cvs_trace = 0;
56: int cvs_nolog = 0;
57: int cvs_readonly = 0;
58:
59: /* name of the command we are running */
60: char *cvs_command;
61: char *cvs_rootstr;
62: char *cvs_rsh = CVS_RSH_DEFAULT;
63: char *cvs_editor = CVS_EDITOR_DEFAULT;
64:
65: struct cvsroot *cvs_root = NULL;
66:
67:
68: /*
69: * Command dispatch table
70: * ----------------------
71: *
72: * The synopsis field should only contain the list of arguments that the
73: * command supports, without the actual command's name.
74: *
75: * Command handlers are expected to return 0 if no error occured, or one of
76: * the values known in sysexits.h in case of an error. In case the error
77: * returned is EX_USAGE, the command's usage string is printed to standard
78: * error before returning.
79: */
80:
81: static struct cvs_cmd {
82: char cmd_name[CVS_CMD_MAXNAMELEN];
83: char cmd_alias[CVS_CMD_MAXALIAS][CVS_CMD_MAXNAMELEN];
84: int (*cmd_hdlr)(int, char **);
85: char *cmd_synopsis;
86: char cmd_descr[CVS_CMD_MAXDESCRLEN];
87: } cvs_cdt[] = {
88: {
89: "add", { "ad", "new" }, cvs_add,
90: "[-m msg] file ...",
91: "Add a new file/directory to the repository",
92: },
93: {
94: "admin", { "adm", "rcs" }, NULL,
95: "",
96: "Administration front end for rcs",
97: },
98: {
99: "annotate", { "ann" }, NULL,
100: "",
101: "Show last revision where each line was modified",
102: },
103: {
104: "checkout", { "co", "get" }, NULL,
105: "",
106: "Checkout sources for editing",
107: },
108: {
109: "commit", { "ci", "com" }, cvs_commit,
110: "",
111: "Check files into the repository",
112: },
113: {
114: "diff", { "di", "dif" }, cvs_diff,
115: "",
116: "Show differences between revisions",
117: },
118: {
119: "edit", { }, NULL,
120: "",
121: "Get ready to edit a watched file",
122: },
123: {
124: "editors", { }, NULL,
125: "",
126: "See who is editing a watched file",
127: },
128: {
129: "export", { "ex", "exp" }, NULL,
130: "",
131: "Export sources from CVS, similar to checkout",
132: },
133: {
134: "history", { "hi", "his" }, cvs_history,
135: "",
136: "Show repository access history",
137: },
138: {
139: "import", { "im", "imp" }, NULL,
140: "",
141: "Import sources into CVS, using vendor branches",
142: },
143: {
144: "init", { }, cvs_init,
145: "",
146: "Create a CVS repository if it doesn't exist",
147: },
148: #if defined(HAVE_KERBEROS)
149: {
150: "kserver", {}, NULL
151: "",
152: "Start a Kerberos authentication CVS server",
153: },
154: #endif
155: {
156: "log", { "lo" }, cvs_getlog,
157: "",
158: "Print out history information for files",
159: },
160: {
161: "login", {}, NULL,
162: "",
163: "Prompt for password for authenticating server",
164: },
165: {
166: "logout", {}, NULL,
167: "",
168: "Removes entry in .cvspass for remote repository",
169: },
170: {
171: "rdiff", {}, NULL,
172: "",
173: "Create 'patch' format diffs between releases",
174: },
175: {
176: "release", {}, NULL,
177: "",
178: "Indicate that a Module is no longer in use",
179: },
180: {
181: "remove", {}, NULL,
182: "",
183: "Remove an entry from the repository",
184: },
185: {
186: "rlog", {}, NULL,
187: "",
188: "Print out history information for a module",
189: },
190: {
191: "rtag", {}, NULL,
192: "",
193: "Add a symbolic tag to a module",
194: },
195: {
196: "server", {}, cvs_server,
197: "",
198: "Server mode",
199: },
200: {
201: "status", {}, NULL,
202: "",
203: "Display status information on checked out files",
204: },
205: {
206: "tag", { "ta", }, NULL,
207: "",
208: "Add a symbolic tag to checked out version of files",
209: },
210: {
211: "unedit", {}, NULL,
212: "",
213: "Undo an edit command",
214: },
215: {
216: "update", {}, cvs_update,
217: "",
218: "Bring work tree in sync with repository",
219: },
220: {
221: "version", {}, cvs_version,
222: "",
223: "Show current CVS version(s)",
224: },
225: {
226: "watch", {}, NULL,
227: "",
228: "Set watches",
229: },
230: {
231: "watchers", {}, NULL,
232: "",
233: "See who is watching a file",
234: },
235: };
236:
237: #define CVS_NBCMD (sizeof(cvs_cdt)/sizeof(cvs_cdt[0]))
238:
239:
240:
241: void usage (void);
242: void sigchld_hdlr (int);
243: void cvs_readrc (void);
244: struct cvs_cmd* cvs_findcmd (const char *);
245:
246:
247:
248: /*
249: * sigchld_hdlr()
250: *
251: * Handler for the SIGCHLD signal, which can be received in case we are
252: * running a remote server and it dies.
253: */
254:
255: void
256: sigchld_hdlr(int signo)
257: {
258: int status;
259: pid_t pid;
260:
261: if ((pid = wait(&status)) == -1) {
262: }
263: }
264:
265:
266: /*
267: * usage()
268: *
269: * Display usage information.
270: */
271:
272: void
273: usage(void)
274: {
275: fprintf(stderr,
276: "Usage: %s [-lQqtv] [-d root] [-e editor] [-z level] "
277: "command [options] ...\n",
278: __progname);
279: }
280:
281:
282: int
283: main(int argc, char **argv)
284: {
285: char *envstr, *ep;
286: int ret;
287: u_int i, readrc;
288: struct cvs_cmd *cmdp;
289:
290: readrc = 1;
291:
292: if (cvs_log_init(LD_STD, 0) < 0)
293: err(1, "failed to initialize logging");
294:
295: /* by default, be very verbose */
296: (void)cvs_log_filter(LP_FILTER_UNSET, LP_INFO);
297:
298: #ifdef DEBUG
299: (void)cvs_log_filter(LP_FILTER_UNSET, LP_DEBUG);
300: #endif
301:
302: /* check environment so command-line options override it */
303: if ((envstr = getenv("CVS_RSH")) != NULL)
304: cvs_rsh = envstr;
305:
306: if (((envstr = getenv("CVSEDITOR")) != NULL) ||
307: ((envstr = getenv("VISUAL")) != NULL) ||
308: ((envstr = getenv("EDITOR")) != NULL))
309: cvs_editor = envstr;
310:
311: while ((ret = getopt(argc, argv, "d:e:fHlnQqrtvz:")) != -1) {
312: switch (ret) {
313: case 'd':
314: cvs_rootstr = optarg;
315: break;
316: case 'e':
317: cvs_editor = optarg;
318: break;
319: case 'f':
320: readrc = 0;
321: break;
322: case 'l':
323: cvs_nolog = 1;
324: break;
325: case 'n':
326: break;
327: case 'Q':
328: verbosity = 0;
329: break;
330: case 'q':
331: /* don't override -Q */
332: if (verbosity > 1)
333: verbosity = 1;
334: break;
335: case 'r':
336: cvs_readonly = 1;
337: break;
338: case 't':
339: cvs_trace = 1;
340: break;
341: case 'v':
342: printf("%s\n", CVS_VERSION);
343: exit(0);
344: /* NOTREACHED */
345: break;
346: case 'z':
347: cvs_compress = (int)strtol(optarg, &ep, 10);
348: if (*ep != '\0')
349: errx(1, "error parsing compression level");
350: if (cvs_compress < 0 || cvs_compress > 9)
351: errx(1, "gzip compression level must be "
352: "between 0 and 9");
353: break;
354: default:
355: usage();
356: exit(EX_USAGE);
357: }
358: }
359:
360: argc -= optind;
361: argv += optind;
362:
363: /* reset getopt() for use by commands */
364: optind = 1;
365: optreset = 1;
366:
367: if (argc == 0) {
368: usage();
369: exit(EX_USAGE);
370: }
371:
372: /* setup signal handlers */
373: signal(SIGCHLD, sigchld_hdlr);
374:
1.2 ! jfb 375: cvs_file_init();
! 376:
1.1 jfb 377: if (readrc)
378: cvs_readrc();
379:
380: cvs_command = argv[0];
381: ret = -1;
382:
383: cmdp = cvs_findcmd(cvs_command);
384: if (cmdp == NULL) {
385: fprintf(stderr, "Unknown command: `%s'\n\n", cvs_command);
386: fprintf(stderr, "CVS commands are:\n");
387: for (i = 0; i < CVS_NBCMD; i++)
388: fprintf(stderr, "\t%-16s%s\n",
389: cvs_cdt[i].cmd_name, cvs_cdt[i].cmd_descr);
390: exit(EX_USAGE);
391: }
392:
393: if (cmdp->cmd_hdlr == NULL) {
394: cvs_log(LP_ERR, "command `%s' not implemented", cvs_command);
395: exit(1);
396: }
397:
398: ret = (*cmdp->cmd_hdlr)(argc, argv);
399: if (ret == EX_USAGE) {
400: fprintf(stderr, "Usage: %s %s %s\n", __progname, cvs_command,
401: cmdp->cmd_synopsis);
402: }
403:
404: return (ret);
405: }
406:
407:
408: /*
409: * cvs_findcmd()
410: *
411: * Find the entry in the command dispatch table whose name or one of its
412: * aliases matches <cmd>.
413: * Returns a pointer to the command entry on success, NULL on failure.
414: */
415:
416: struct cvs_cmd*
417: cvs_findcmd(const char *cmd)
418: {
419: u_int i, j;
420: struct cvs_cmd *cmdp;
421:
422: cmdp = NULL;
423:
424: for (i = 0; (i < CVS_NBCMD) && (cmdp == NULL); i++) {
425: if (strcmp(cmd, cvs_cdt[i].cmd_name) == 0)
426: cmdp = &cvs_cdt[i];
427: else {
428: for (j = 0; j < CVS_CMD_MAXALIAS; j++) {
429: if (strcmp(cmd, cvs_cdt[i].cmd_alias[j]) == 0) {
430: cmdp = &cvs_cdt[i];
431: break;
432: }
433: }
434: }
435: }
436:
437: return (cmdp);
438: }
439:
440:
441: /*
442: * cvs_readrc()
443: *
444: * Read the CVS `.cvsrc' file in the user's home directory. If the file
445: * exists, it should contain a list of arguments that should always be given
446: * implicitly to the specified commands.
447: */
448:
449: void
450: cvs_readrc(void)
451: {
452: char rcpath[MAXPATHLEN], linebuf[128], *lp;
453: struct cvs_cmd *cmdp;
454: struct passwd *pw;
455: FILE *fp;
456:
457: pw = getpwuid(getuid());
458: if (pw == NULL) {
459: cvs_log(LP_NOTICE, "failed to get user's password entry");
460: return;
461: }
462:
463: snprintf(rcpath, sizeof(rcpath), "%s/%s", pw->pw_dir, CVS_PATH_RC);
464:
465: fp = fopen(rcpath, "r");
466: if (fp == NULL) {
467: if (errno != ENOENT)
468: cvs_log(LP_NOTICE, "failed to open `%s': %s", rcpath,
469: strerror(errno));
470: return;
471: }
472:
473: while (fgets(linebuf, sizeof(linebuf), fp) != NULL) {
474: lp = strchr(linebuf, ' ');
475:
476: /* ignore lines with no arguments */
477: if (lp == NULL)
478: continue;
479:
480: *(lp++) = '\0';
481: if (strcmp(linebuf, "cvs") == 0) {
482: /* global options */
483: }
484: else {
485: cmdp = cvs_findcmd(linebuf);
486: if (cmdp == NULL) {
487: cvs_log(LP_NOTICE,
488: "unknown command `%s' in cvsrc",
489: linebuf);
490: continue;
491: }
492: }
493: }
494: if (ferror(fp)) {
495: cvs_log(LP_NOTICE, "failed to read line from cvsrc");
496: }
497:
498: (void)fclose(fp);
499: }