diff options
Diffstat (limited to 'gnu/usr.bin/cvs/src')
-rw-r--r-- | gnu/usr.bin/cvs/src/checkout.c | 71 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/commit.c | 12 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/cvs.h | 7 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/ignore.c | 8 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/lock.c | 2 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/main.c | 65 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/patch.c | 13 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/rcs.c | 384 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/rcscmds.c | 2 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/server.c | 141 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/update.c | 75 |
11 files changed, 545 insertions, 235 deletions
diff --git a/gnu/usr.bin/cvs/src/checkout.c b/gnu/usr.bin/cvs/src/checkout.c index 737cbdc8ea1..e36e729b819 100644 --- a/gnu/usr.bin/cvs/src/checkout.c +++ b/gnu/usr.bin/cvs/src/checkout.c @@ -44,10 +44,12 @@ static int safe_location PROTO((void)); static const char *const checkout_usage[] = { - "Usage:\n %s %s [-ANPcflnps] [-r rev | -D date] [-d dir] [-k kopt] modules...\n", + "Usage:\n %s %s [-ANPRcflnps] [-r rev | -D date] [-d dir]\n", + " [-j rev1] [-j rev2] [-k kopt] modules...\n", "\t-A\tReset any sticky tags/date/kopts.\n", "\t-N\tDon't shorten module paths if -d specified.\n", "\t-P\tPrune empty directories.\n", + "\t-R\tProcess directories recursively.\n", "\t-c\t\"cat\" the module database.\n", "\t-f\tForce a head revision match if tag/date not found.\n", "\t-l\tLocal directory only, not recursive\n", @@ -64,14 +66,15 @@ static const char *const checkout_usage[] = static const char *const export_usage[] = { - "Usage: %s %s [-NPfln] [-r rev | -D date] [-d dir] [-k kopt] module...\n", + "Usage: %s %s [-NRfln] [-r rev | -D date] [-d dir] [-k kopt] module...\n", "\t-N\tDon't shorten module paths if -d specified.\n", "\t-f\tForce a head revision match if tag/date not found.\n", "\t-l\tLocal directory only, not recursive\n", + "\t-R\tProcess directories recursively (default).\n", "\t-n\tDo not run module program (if any).\n", - "\t-r rev\tCheck out revision or tag.\n", - "\t-D date\tCheck out revisions as of date.\n", - "\t-d dir\tCheck out into dir instead of module name.\n", + "\t-r rev\tExport revision or tag.\n", + "\t-D date\tExport revisions as of date.\n", + "\t-d dir\tExport into dir instead of module name.\n", "\t-k kopt\tUse RCS kopt -k option on checkout.\n", NULL }; @@ -132,7 +135,7 @@ checkout (argc, argv) ign_setup (); wrap_setup (); - optind = 1; + optind = 0; while ((c = getopt (argc, argv, valid_options)) != -1) { switch (c) @@ -218,8 +221,11 @@ checkout (argc, argv) if (shorten == -1) shorten = 0; - if ((!(cat + status) && argc == 0) || ((cat + status) && argc != 0)) - usage (valid_usage); + if ((cat || status) && argc != 0) + error (1, 0, "-c and -s must not get any arguments"); + + if (!(cat || status) && argc == 0) + error (1, 0, "must specify at least one module or directory"); if (where && pipeout) error (1, 0, "-d and -p are mutually exclusive"); @@ -227,20 +233,10 @@ checkout (argc, argv) if (strcmp (command_name, "export") == 0) { if (!tag && !date) - { - error (0, 0, "must specify a tag or date"); - usage (valid_usage); - } + error (1, 0, "must specify a tag or date"); + if (tag && isdigit (tag[0])) error (1, 0, "tag `%s' must be a symbolic tag", tag); -/* - * mhy 950615: -kv doesn't work for binaries with RCS keywords. - * Instead use the default provided in the RCS file (-ko for binaries). - */ -#if 0 - if (!options) - options = RCS_check_kflag ("v");/* -kv is default */ -#endif } if (!safe_location()) { @@ -276,10 +272,14 @@ checkout (argc, argv) client_expand_modules (argc, argv, local); } - if (!run_module_prog) send_arg ("-n"); - if (local) send_arg ("-l"); - if (pipeout) send_arg ("-p"); - if (!force_tag_match) send_arg ("-f"); + if (!run_module_prog) + send_arg ("-n"); + if (local) + send_arg ("-l"); + if (pipeout) + send_arg ("-p"); + if (!force_tag_match) + send_arg ("-f"); if (aflag) send_arg("-A"); if (!shorten) @@ -290,16 +290,10 @@ checkout (argc, argv) if (cat) send_arg("-c"); if (where != NULL) - { option_with_arg ("-d", where); - } if (status) send_arg("-s"); - /* Why not send -k for export? This would appear to make - remote export differ from local export. FIXME. */ - if (strcmp (command_name, "export") != 0 - && options != NULL - && options[0] != '\0') + if (options != NULL && options[0] != '\0') send_arg (options); option_with_arg ("-r", tag); if (date) @@ -332,6 +326,8 @@ checkout (argc, argv) if (cat || status) { cat_module (status); + if (options) + free (options); return (0); } db = open_module (); @@ -343,8 +339,6 @@ checkout (argc, argv) */ if (argc > 1 && where != NULL) { - char *repository; - (void) CVS_MKDIR (where, 0777); if ( CVS_CHDIR (where) < 0) error (1, errno, "cannot chdir to %s", where); @@ -352,6 +346,8 @@ checkout (argc, argv) where = (char *) NULL; if (!isfile (CVSADM)) { + char *repository; + repository = xmalloc (strlen (CVSroot_directory) + 80); (void) sprintf (repository, "%s/%s/%s", CVSroot_directory, CVSROOTADM, CVSNULLREPOS); @@ -381,8 +377,8 @@ checkout (argc, argv) server_set_entstat (preload_update_dir, repository); #endif } + free (repository); } - free (repository); } /* If we will be calling history_write, work out the name to pass @@ -429,6 +425,8 @@ checkout (argc, argv) where, shorten, local, run_module_prog, (char *) NULL); close_module (db); + if (options) + free (options); return (err); } @@ -674,6 +672,10 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten, */ for (i = 1; i < *pargc; i++)/* free the old ones */ free (argv[i]); + /* FIXME: Normally one has to realloc argv to make sure + argv[1] exists. But this argv does not points to the + beginning of an allocated block. For now we allocate + at least 4 entries for argv (in line2argv). */ argv[1] = xstrdup (mfile); /* set up the new one */ *pargc = 2; @@ -983,6 +985,7 @@ internal error: %s doesn't start with %s in checkout_proc", free (line); } freevers_ts (&vers); + freercsnode (&finfo.rcs); } Entries_Close (entries); diff --git a/gnu/usr.bin/cvs/src/commit.c b/gnu/usr.bin/cvs/src/commit.c index 9d13391950b..c43e35f3538 100644 --- a/gnu/usr.bin/cvs/src/commit.c +++ b/gnu/usr.bin/cvs/src/commit.c @@ -332,7 +332,7 @@ commit (argc, argv) } #endif /* CVS_BADROOT */ - optind = 1; + optind = 0; while ((c = getopt (argc, argv, "+nlRm:fF:r:")) != -1) { switch (c) @@ -546,6 +546,16 @@ commit (argc, argv) previous versions of client/server CVS, but it probably is a Good Thing, or at least Not Such A Bad Thing. */ send_file_names (find_args.argc, find_args.argv, 0); + + /* FIXME: This whole find_args.force/SEND_FORCE business is a + kludge. It would seem to be a server bug that we have to + say that files are modified when they are not. This makes + "cvs commit -r 2" across a whole bunch of files a very slow + operation (and it isn't documented in cvsclient.texi). I + haven't looked at the server code carefully enough to be + _sure_ why this is needed, but if it is because RCS_CI + wants the file to exist, then it would be relatively simple + (but not trivial) to fix in the server. */ send_files (find_args.argc, find_args.argv, local, 0, find_args.force ? SEND_FORCE : 0); diff --git a/gnu/usr.bin/cvs/src/cvs.h b/gnu/usr.bin/cvs/src/cvs.h index d8a3ca3f9cb..a141ccf7580 100644 --- a/gnu/usr.bin/cvs/src/cvs.h +++ b/gnu/usr.bin/cvs/src/cvs.h @@ -423,10 +423,16 @@ void Subdir_Register PROTO((List *, const char *, const char *)); void Subdir_Deregister PROTO((List *, const char *, const char *)); char *Make_Date PROTO((char *rawdate)); char *Name_Repository PROTO((char *dir, char *update_dir)); + + char *Name_Root PROTO((char *dir, char *update_dir)); int parse_cvsroot PROTO((char *CVSroot)); void set_local_cvsroot PROTO((char *dir)); void Create_Root PROTO((char *dir, char *rootdir)); +void root_allow_add PROTO ((char *)); +void root_allow_free PROTO ((void)); +int root_allow_ok PROTO ((char *)); + int same_directories PROTO((char *dir1, char *dir2)); char *Short_Repository PROTO((char *repository)); char *gca PROTO((char *rev1, char *rev2)); @@ -438,6 +444,7 @@ void *xrealloc PROTO((void *ptr, size_t bytes)); void expand_string PROTO ((char **, size_t *, size_t)); char *xstrdup PROTO((const char *str)); void strip_trailing_newlines PROTO((char *str)); +int pathname_levels PROTO ((char *path)); typedef int (*CALLPROC) PROTO((char *repository, char *value)); int Parse_Info PROTO((char *infofile, char *repository, CALLPROC callproc, int all)); diff --git a/gnu/usr.bin/cvs/src/ignore.c b/gnu/usr.bin/cvs/src/ignore.c index 91b015d94d9..82fd84a6730 100644 --- a/gnu/usr.bin/cvs/src/ignore.c +++ b/gnu/usr.bin/cvs/src/ignore.c @@ -269,7 +269,9 @@ ign_name (name) { /* We do a case-insensitive match by calling fnmatch on copies of the pattern and the name which have been converted to - lowercase. */ + lowercase. FIXME: would be much cleaner to just unify this + with the other case-insensitive fnmatch stuff (FOLD_FN_CHAR + in lib/fnmatch.c; os2_fnmatch in emx/system.c). */ char *name_lower; char *pat_lower; char *p; @@ -282,7 +284,7 @@ ign_name (name) pat_lower = xstrdup (*cpp++); for (p = pat_lower; *p != '\0'; ++p) *p = tolower (*p); - if (fnmatch (pat_lower, name_lower, 0) == 0) + if (CVS_FNMATCH (pat_lower, name_lower, 0) == 0) goto matched; free (pat_lower); } @@ -296,7 +298,7 @@ ign_name (name) else { while (*cpp) - if (fnmatch (*cpp++, name, 0) == 0) + if (CVS_FNMATCH (*cpp++, name, 0) == 0) return 1; return 0; } diff --git a/gnu/usr.bin/cvs/src/lock.c b/gnu/usr.bin/cvs/src/lock.c index 1ff6864e4d7..c47943c1e88 100644 --- a/gnu/usr.bin/cvs/src/lock.c +++ b/gnu/usr.bin/cvs/src/lock.c @@ -485,7 +485,7 @@ again: errno = 0; while ((dp = readdir (dirp)) != NULL) { - if (fnmatch (CVSRFLPAT, dp->d_name, 0) == 0) + if (CVS_FNMATCH (CVSRFLPAT, dp->d_name, 0) == 0) { #ifdef CVS_FUDGELOCKS time_t now; diff --git a/gnu/usr.bin/cvs/src/main.c b/gnu/usr.bin/cvs/src/main.c index 8b5a7bb81b7..046bf9c3f8b 100644 --- a/gnu/usr.bin/cvs/src/main.c +++ b/gnu/usr.bin/cvs/src/main.c @@ -163,7 +163,7 @@ static const char *const cmd_usage[] = " annotate Show last revision where each line was modified\n", " checkout Checkout sources for editing\n", " commit Check files into the repository\n", - " diff Run diffs between revisions\n", + " diff Show differences between revisions\n", " edit Get ready to edit a watched file\n", " editors See who is editing a watched file\n", " export Export sources from CVS, similar to checkout\n", @@ -331,17 +331,23 @@ main (argc, argv) const struct cmd *cm; int c, err = 0; int rcsbin_update_env, tmpdir_update_env, cvs_update_env; + int free_CVSroot = 0; + int free_Editor = 0; + int free_Tmpdir = 0; + int free_Rcsbin = 0; + int help = 0; /* Has the user asked for help? This lets us support the `cvs -H cmd' convention to give help for cmd. */ static struct option long_options[] = - { + { {"help", 0, NULL, 'H'}, {"version", 0, NULL, 'v'}, {"help-commands", 0, NULL, 1}, {"help-synonyms", 0, NULL, 2}, + {"allow-root", required_argument, NULL, 3}, {0, 0, 0, 0} - }; + }; /* `getopt_long' stores the option index here, but right now we don't use it. */ int option_index = 0; @@ -414,11 +420,9 @@ main (argc, argv) CVSUMASK_ENV, cp); } - /* I'm not sure whether this needs to be 1 instead of 0 anymore. Using - 1 used to accomplish what passing "+" as the first character to - the option string does, but that reason doesn't exist anymore. */ - optind = 1; - + /* Set this to 0 to force getopt initialization. getopt() sets + this to 1 internally. */ + optind = 0; /* We have to parse the options twice because else there is no chance to avoid reading the global options from ".cvsrc". Set @@ -440,15 +444,15 @@ main (argc, argv) if (use_cvsrc) read_cvsrc (&argc, &argv, "cvs"); - optind = 1; + optind = 0; opterr = 1; while ((c = getopt_long (argc, argv, "+Qqrwtnlvb:T:e:d:Hfz:s:x", long_options, &option_index)) != EOF) - { + { switch (c) - { + { case 1: /* --help-commands */ usage (cmd_usage); @@ -457,6 +461,10 @@ main (argc, argv) /* --help-synonyms */ usage (cmd_synonyms()); break; + case 3: + /* --allow-root */ + root_allow_add (optarg); + break; case 'Q': really_quiet = TRUE; /* FALL THROUGH */ @@ -491,18 +499,22 @@ main (argc, argv) exit (0); break; case 'b': - Rcsbin = optarg; + Rcsbin = xstrdup (optarg); + free_Rcsbin = 1; rcsbin_update_env = 1; /* need to update environment */ break; case 'T': - Tmpdir = optarg; + Tmpdir = xstrdup (optarg); + free_Tmpdir = 1; tmpdir_update_env = 1; /* need to update environment */ break; case 'e': - Editor = optarg; + Editor = xstrdup (optarg); + free_Editor = 1; break; case 'd': - CVSroot = optarg; + CVSroot = xstrdup (optarg); + free_CVSroot = 1; cvs_update_env = 1; /* need to update environment */ break; case 'H': @@ -610,6 +622,12 @@ main (argc, argv) #if defined(AUTH_SERVER_SUPPORT) && defined(SERVER_SUPPORT) if (strcmp (command_name, "pserver") == 0) { + /* The reason that --allow-root is not a command option + is mainly the comment in server() about how argc,argv + might be from .cvsrc. I'm not sure about that, and + I'm not sure it is only true of command options, but + it seems easier to make it a global option. */ + /* Gets username and password from client, authenticates, then switches to run as that user and sends an ACK back to the client. */ @@ -877,15 +895,26 @@ main (argc, argv) Lock_Cleanup (); + free (program_path); + if (free_CVSroot) + free (CVSroot); + if (free_Editor) + free (Editor); + if (free_Tmpdir) + free (Tmpdir); + if (free_Rcsbin) + free (Rcsbin); + root_allow_free (); + #ifdef SYSTEM_CLEANUP /* Hook for OS-specific behavior, for example socket subsystems on NT and OS2 or dealing with windows and arguments on Mac. */ SYSTEM_CLEANUP (); #endif - if (err) - return (EXIT_FAILURE); - return 0; + /* This is exit rather than return because apparently that keeps + some tools which check for memory leaks happier. */ + exit (err ? EXIT_FAILURE : 0); } char * diff --git a/gnu/usr.bin/cvs/src/patch.c b/gnu/usr.bin/cvs/src/patch.c index ac82e4b65b9..194999be651 100644 --- a/gnu/usr.bin/cvs/src/patch.c +++ b/gnu/usr.bin/cvs/src/patch.c @@ -42,10 +42,11 @@ static int unidiff = 0; static const char *const patch_usage[] = { - "Usage: %s %s [-fl] [-c|-u] [-s|-t] [-V %%d]\n", + "Usage: %s %s [-flR] [-c|-u] [-s|-t] [-V %%d]\n", " -r rev|-D date [-r rev2 | -D date2] modules...\n", "\t-f\tForce a head revision match if tag/date not found.\n", "\t-l\tLocal directory only, not recursive\n", + "\t-R\tProcess directories recursively.\n", "\t-c\tContext diffs (default)\n", "\t-u\tUnidiff format.\n", "\t-s\tShort patch - one liner per file.\n", @@ -69,7 +70,7 @@ patch (argc, argv) if (argc == -1) usage (patch_usage); - optind = 1; + optind = 0; while ((c = getopt (argc, argv, "+V:k:cuftsQqlRD:r:")) != -1) { switch (c) @@ -513,7 +514,9 @@ patch_fileproc (callerdat, finfo) memset ((char *) &t, 0, sizeof (t)); if ((t.actime = t.modtime = RCS_getrevtime (rcsfile, vers_tag, (char *) 0, 0)) != -1) - (void) utime (tmpfile1, &t); + /* I believe this timestamp only affects the dates in our diffs, + and therefore should be on the server, not the client. */ + (void) utime (tmpfile1, &t); } else if (toptwo_diffs) { @@ -535,7 +538,9 @@ patch_fileproc (callerdat, finfo) } if ((t.actime = t.modtime = RCS_getrevtime (rcsfile, vers_head, (char *) 0, 0)) != -1) - (void) utime (tmpfile2, &t); + /* I believe this timestamp only affects the dates in our diffs, + and therefore should be on the server, not the client. */ + (void) utime (tmpfile2, &t); } run_setup ("%s -%c", DIFF, unidiff ? 'u' : 'c'); run_arg (tmpfile1); diff --git a/gnu/usr.bin/cvs/src/rcs.c b/gnu/usr.bin/cvs/src/rcs.c index a56f312c498..bc8ed0818ad 100644 --- a/gnu/usr.bin/cvs/src/rcs.c +++ b/gnu/usr.bin/cvs/src/rcs.c @@ -801,6 +801,8 @@ rcsvers_delproc (p) free (rnode->next); if (rnode->author != (char *) NULL) free (rnode->author); + if (rnode->state != (char *) NULL) + free (rnode->state); if (rnode->other != (List *) NULL) dellist (&rnode->other); free ((char *) rnode); @@ -1046,8 +1048,13 @@ getrcsrev (fp, revp) do { c = getc (fp); if (c == EOF) + { /* FIXME: should be including filename in error message. */ - error (1, errno, "cannot read rcs file"); + if (ferror (fp)) + error (1, errno, "cannot read rcs file"); + else + error (1, 0, "unexpected end of file reading rcs file"); + } } while (whitespace (c)); if (!(isdigit (c) || c == '.')) @@ -1071,7 +1078,10 @@ getrcsrev (fp, revp) if (c == EOF) { /* FIXME: should be including filename in error message. */ - error (1, errno, "cannot read rcs file"); + if (ferror (fp)) + error (1, errno, "cannot read rcs file"); + else + error (1, 0, "unexpected end of file reading rcs file"); } } @@ -2668,7 +2678,7 @@ expand_keywords (rcs, ver, name, log, loglen, expand, buf, len, retbuf, retlen) /* Now SUB contains a string which is to replace the string from SRCH to S. SUBLEN is the length of SUB. */ - if (sublen == s - srch) + if (srch + sublen == s) { memcpy (srch, sub, sublen); free (sub); @@ -2697,7 +2707,7 @@ expand_keywords (rcs, ver, name, log, loglen, expand, buf, len, retbuf, retlen) else { assert (srch >= ebuf_last->data); - assert (srch - ebuf_last->data <= ebuf_last->len); + assert (srch <= ebuf_last->data + ebuf_last->len); ebuf_len -= ebuf_last->len - (srch - ebuf_last->data); ebuf_last->len = srch - ebuf_last->data; } @@ -3432,16 +3442,18 @@ linevector_init (vec) vec->vector = NULL; } -static void linevector_add PROTO ((struct linevector *vec, char *text, - size_t len, RCSVers *vers, - unsigned int pos)); +static int linevector_add PROTO ((struct linevector *vec, char *text, + size_t len, RCSVers *vers, + unsigned int pos)); /* Given some text TEXT, add each of its lines to VEC before line POS (where line 0 is the first line). The last line in TEXT may or may not be \n terminated. All \n in TEXT are changed to \0 (FIXME: I don't think this is needed, or used, now that we have the ->len - field). Set the version for each of the new lines to VERS. */ -static void + field). Set the version for each of the new lines to VERS. This + function returns non-zero for success. It returns zero if the line + number is out of range. */ +static int linevector_add (vec, text, len, vers, pos) struct linevector *vec; char *text; @@ -3456,7 +3468,7 @@ linevector_add (vec, text, len, vers, pos) struct line *lines; if (len == 0) - return; + return 1; textend = text + len; @@ -3484,7 +3496,7 @@ linevector_add (vec, text, len, vers, pos) vec->vector[i] = vec->vector[i - nnew]; if (pos > vec->nlines) - error (1, 0, "invalid rcs file: line to add out of range"); + return 0; /* Actually add the lines, to LINES and VEC->VECTOR. */ i = pos; @@ -3510,6 +3522,8 @@ linevector_add (vec, text, len, vers, pos) } lines[i - pos - 1].len = p - lines[i - pos - 1].text; vec->nlines += nnew; + + return 1; } static void linevector_delete PROTO ((struct linevector *, unsigned int, @@ -3589,6 +3603,214 @@ month_printname (month) return (char *)months[mnum - 1]; } +static int +apply_rcs_changes PROTO ((struct linevector *, const char *, size_t, + const char *, RCSVers *, RCSVers *)); + +/* Apply changes to the line vector LINES. DIFFBUF is a buffer of + length DIFFLEN holding the change text from an RCS file (the output + of diff -n). NAME is used in error messages. The VERS field of + any line added is set to ADDVERS. The VERS field of any line + deleted is set to DELVERS, unless DELVERS is NULL, in which case + the VERS field of deleted lines is unchanged. The function returns + non-zero if the change text is applied successfully. It returns + zero if the change text does not appear to apply to LINES (e.g., a + line number is invalid). If the change text is improperly + formatted (e.g., it is not the output of diff -n), the function + calls error with a status of 1, causing the program to exit. */ + +static int +apply_rcs_changes (lines, diffbuf, difflen, name, addvers, delvers) + struct linevector *lines; + const char *diffbuf; + size_t difflen; + const char *name; + RCSVers *addvers; + RCSVers *delvers; +{ + const char *p; + const char *q; + int op; + /* The RCS format throws us for a loop in that the deltafrags (if + we define a deltafrag as an add or a delete) need to be applied + in reverse order. So we stick them into a linked list. */ + struct deltafrag { + enum {ADD, DELETE} type; + unsigned long pos; + unsigned long nlines; + char *new_lines; + size_t len; + struct deltafrag *next; + }; + struct deltafrag *dfhead; + struct deltafrag *df; + + dfhead = NULL; + for (p = diffbuf; p != NULL && p < diffbuf + difflen; ) + { + op = *p++; + if (op != 'a' && op != 'd') + /* Can't just skip over the deltafrag, because the value + of op determines the syntax. */ + error (1, 0, "unrecognized operation '%c' in %s", op, name); + df = (struct deltafrag *) xmalloc (sizeof (struct deltafrag)); + df->next = dfhead; + dfhead = df; + df->pos = strtoul (p, (char **) &q, 10); + + if (p == q) + error (1, 0, "number expected in %s", name); + p = q; + if (*p++ != ' ') + error (1, 0, "space expected in %s", name); + df->nlines = strtoul (p, (char **) &q, 10); + if (p == q) + error (1, 0, "number expected in %s", name); + p = q; + if (*p++ != '\012') + error (1, 0, "linefeed expected in %s", name); + + if (op == 'a') + { + unsigned int i; + + df->type = ADD; + i = df->nlines; + /* The text we want is the number of lines specified, or + until the end of the value, whichever comes first (it + will be the former except in the case where we are + adding a line which does not end in newline). */ + for (q = p; i != 0; ++q) + if (*q == '\n') + --i; + else if (q == diffbuf + difflen) + { + if (i != 1) + error (1, 0, "premature end of change in %s", name); + else + break; + } + + /* Copy the text we are adding into allocated space. */ + df->new_lines = block_alloc (q - p); + memcpy (df->new_lines, p, q - p); + df->len = q - p; + + p = q; + } + else + { + /* Correct for the fact that line numbers in RCS files + start with 1. */ + --df->pos; + + assert (op == 'd'); + df->type = DELETE; + } + } + + for (df = dfhead; df != NULL;) + { + unsigned int ln; + + switch (df->type) + { + case ADD: + if (! linevector_add (lines, df->new_lines, df->len, addvers, + df->pos)) + return 0; + break; + case DELETE: + if (df->pos > lines->nlines + || df->pos + df->nlines > lines->nlines) + return 0; + if (delvers != NULL) + for (ln = df->pos; ln < df->pos + df->nlines; ++ln) + lines->vector[ln]->vers = delvers; + linevector_delete (lines, df->pos, df->nlines); + break; + } + df = df->next; + free (dfhead); + dfhead = df; + } + + return 1; +} + +/* Apply an RCS change text to a buffer. The function name starts + with rcs rather than RCS because this does not take an RCSNode + argument. NAME is used in error messages. TEXTBUF is the text + buffer to change, and TEXTLEN is the size. DIFFBUF and DIFFLEN are + the change buffer and size. The new buffer is returned in *RETBUF + and *RETLEN. The new buffer is allocated by xmalloc. The function + changes the contents of TEXTBUF. This function returns 1 for + success. On failure, it calls error and returns 0. */ + +int +rcs_change_text (name, textbuf, textlen, diffbuf, difflen, retbuf, retlen) + const char *name; + char *textbuf; + size_t textlen; + const char *diffbuf; + size_t difflen; + char **retbuf; + size_t *retlen; +{ + struct linevector lines; + int ret; + + *retbuf = NULL; + *retlen = 0; + + linevector_init (&lines); + + if (! linevector_add (&lines, textbuf, textlen, NULL, 0)) + error (1, 0, "cannot initialize line vector"); + + if (! apply_rcs_changes (&lines, diffbuf, difflen, name, NULL, NULL)) + { + error (0, 0, "invalid change text in %s", name); + ret = 0; + } + else + { + char *p; + size_t n; + unsigned int ln; + + n = 0; + for (ln = 0; ln < lines.nlines; ++ln) + /* 1 for \n */ + n += lines.vector[ln]->len + 1; + + p = xmalloc (n); + *retbuf = p; + + for (ln = 0; ln < lines.nlines; ++ln) + { + memcpy (p, lines.vector[ln]->text, lines.vector[ln]->len); + p += lines.vector[ln]->len; + if (lines.vector[ln]->has_newline) + *p++ = '\n'; + } + + *retlen = p - *retbuf; + assert (*retlen <= n); + + ret = 1; + } + + linevector_free (&lines); + + /* Note that this assumes that we have not called from anything + else which uses the block vectors. FIXME: We could fix this by + saving and restoring the state of the block allocation code. */ + block_free (); + + return ret; +} + /* Walk the deltas in RCS to get to revision VERSION. If OP is RCS_ANNOTATE, then write annotations using cvs_output. @@ -3731,132 +3953,18 @@ RCS_deltas (rcs, fp, version, op, text, len, log, loglen) p = block_alloc (vallen); memcpy (p, value, vallen); - linevector_add (&curlines, p, vallen, NULL, 0); + if (! linevector_add (&curlines, p, vallen, NULL, 0)) + error (1, 0, "invalid rcs file %s", rcs->path); + ishead = 0; } else if (isnext) { - char *p; - char *q; - int op; - /* The RCS format throws us for a loop in that the - deltafrags (if we define a deltafrag as an - add or a delete) need to be applied in reverse - order. So we stick them into a linked list. */ - struct deltafrag { - enum {ADD, DELETE} type; - unsigned long pos; - unsigned long nlines; - char *new_lines; - size_t len; - struct deltafrag *next; - }; - struct deltafrag *dfhead; - struct deltafrag *df; - - dfhead = NULL; - for (p = value; p != NULL && p < value + vallen; ) - { - op = *p++; - if (op != 'a' && op != 'd') - /* Can't just skip over the deltafrag, because - the value of op determines the syntax. */ - error (1, 0, "unrecognized operation '%c' in %s", - op, rcs->path); - df = (struct deltafrag *) - xmalloc (sizeof (struct deltafrag)); - df->next = dfhead; - dfhead = df; - df->pos = strtoul (p, &q, 10); - - if (p == q) - error (1, 0, "number expected in %s", - rcs->path); - p = q; - if (*p++ != ' ') - error (1, 0, "space expected in %s", - rcs->path); - df->nlines = strtoul (p, &q, 10); - if (p == q) - error (1, 0, "number expected in %s", - rcs->path); - p = q; - if (*p++ != '\012') - error (1, 0, "linefeed expected in %s", - rcs->path); - - if (op == 'a') - { - unsigned int i; - - df->type = ADD; - i = df->nlines; - /* The text we want is the number of lines - specified, or until the end of the value, - whichever comes first (it will be the former - except in the case where we are adding a line - which does not end in newline). */ - for (q = p; i != 0; ++q) - if (*q == '\n') - --i; - else if (q == value + vallen) - { - if (i != 1) - error (1, 0, "\ -invalid rcs file %s: premature end of value", - rcs->path); - else - break; - } - - /* Copy the text we are adding into allocated - space. */ - df->new_lines = block_alloc (q - p); - memcpy (df->new_lines, p, q - p); - df->len = q - p; - - p = q; - } - else - { - /* Correct for the fact that line numbers in RCS - files start with 1. */ - --df->pos; - - assert (op == 'd'); - df->type = DELETE; - } - } - for (df = dfhead; df != NULL;) - { - unsigned int ln; - - switch (df->type) - { - case ADD: - linevector_add (&curlines, df->new_lines, - df->len, - onbranch ? vers : NULL, - df->pos); - break; - case DELETE: - if (df->pos > curlines.nlines - || df->pos + df->nlines > curlines.nlines) - error (1, 0, "\ -invalid rcs file %s (`d' operand out of range)", - rcs->path); - if (! onbranch) - for (ln = df->pos; - ln < df->pos + df->nlines; - ++ln) - curlines.vector[ln]->vers = prev_vers; - linevector_delete (&curlines, df->pos, df->nlines); - break; - } - df = df->next; - free (dfhead); - dfhead = df; - } + if (! apply_rcs_changes (&curlines, value, vallen, + rcs->path, + onbranch ? vers : NULL, + onbranch ? NULL : prev_vers)) + error (1, 0, "invalid change text in %s", rcs->path); } break; } @@ -3947,6 +4055,8 @@ invalid rcs file %s (`d' operand out of range)", break; } while (next != NULL); + free (branchversion); + if (fclose (fp) < 0) error (0, errno, "cannot close %s", rcs->path); @@ -4095,8 +4205,9 @@ annotate_fileproc (callerdat, finfo) static const char *const annotate_usage[] = { - "Usage: %s %s [-lf] [-r rev|-D date] [files...]\n", + "Usage: %s %s [-lRf] [-r rev|-D date] [files...]\n", "\t-l\tLocal directory only, no recursion.\n", + "\t-R\tProcess directories recursively.\n", "\t-f\tUse head revision if tag/date not found.\n", "\t-r rev\tAnnotate file as of specified revision/tag.\n", "\t-D date\tAnnotate file as of specified date.\n", @@ -4118,13 +4229,16 @@ annotate (argc, argv) usage (annotate_usage); optind = 0; - while ((c = getopt (argc, argv, "+lr:D:f")) != -1) + while ((c = getopt (argc, argv, "+lr:D:fR")) != -1) { switch (c) { case 'l': local = 1; break; + case 'R': + local = 0; + break; case 'r': tag = optarg; break; diff --git a/gnu/usr.bin/cvs/src/rcscmds.c b/gnu/usr.bin/cvs/src/rcscmds.c index 1f93fcfb94e..1f356cfd8f8 100644 --- a/gnu/usr.bin/cvs/src/rcscmds.c +++ b/gnu/usr.bin/cvs/src/rcscmds.c @@ -199,7 +199,7 @@ RCS_checkin (rcsfile, workfile, message, rev, flags) if (!existence_error (errno)) error (0, errno, "warning: cannot stat %s", rcsfile); } - run_setup ("%s%s -x,v/ -f %s%s", Rcsbin, RCS_CI, + run_setup ("%s%s -x,v/ -w%s -f %s%s", Rcsbin, RCS_CI, getcaller (), rev ? "-r" : "", rev ? rev : ""); if (flags & RCS_FLAGS_DEAD) run_arg ("-sdead"); diff --git a/gnu/usr.bin/cvs/src/server.c b/gnu/usr.bin/cvs/src/server.c index 34b6906440b..ea40a4c8901 100644 --- a/gnu/usr.bin/cvs/src/server.c +++ b/gnu/usr.bin/cvs/src/server.c @@ -89,7 +89,7 @@ static Key_schedule sched; the same as the system username the server eventually switches to run as. CVS_Username gets set iff password authentication is successful. */ -static char *CVS_Username = NULL; +char *CVS_Username = NULL; /* Used to check that same repos is transmitted in pserver auth and in later CVS protocol. Exported because root.c also uses. */ @@ -567,6 +567,28 @@ Sorry, you don't have read/write access to the history file %s", path); #endif parseopts(CVSroot_directory); } + +static int max_dotdot_limit = 0; + +/* Is this pathname OK to recurse into when we are running as the server? + If not, call error() with a fatal error. */ +void +server_pathname_check (path) + char *path; +{ + /* An absolute pathname is almost surely a path on the *client* machine, + and is unlikely to do us any good here. It also is probably capable + of being a security hole in the anonymous readonly case. */ + if (isabsolute (path)) + error (1, 0, "absolute pathname `%s' illegal for server", path); + if (pathname_levels (path) > max_dotdot_limit) + { + /* Similar to the isabsolute case in security implications. */ + error (0, 0, "protocol error: `%s' contains more leading ..", path); + error (1, 0, "than the %d which Max-dotdot specified", + max_dotdot_limit); + } +} /* * Add as many directories to the temp directory as the client tells us it @@ -595,6 +617,7 @@ serve_max_dotdot (arg) if (server_temp_dir != orig_server_temp_dir) free (server_temp_dir); server_temp_dir = p; + max_dotdot_limit = lim; } static char *dir_name; @@ -615,6 +638,19 @@ dirswitch (dir, repos) if (dir_name != NULL) free (dir_name); + /* Check for a trailing '/'. This is not ISDIRSEP because \ in the + protocol is an ordinary character, not a directory separator (of + course, it is perhaps unwise to use it in directory names, but that + is another issue). */ + if (strlen (dir) > 0 + && dir[strlen (dir) - 1] == '/') + { + if (alloc_pending (80 + strlen (dir))) + sprintf (pending_error_text, + "E protocol error: illegal directory syntax in %s", dir); + return; + } + dir_name = malloc (strlen (server_temp_dir) + strlen (dir) + 40); if (dir_name == NULL) { @@ -1798,16 +1834,7 @@ check_command_legal_p (cmd_name) linebuf[num_red - 1] = '\0'; if (strcmp (linebuf, CVS_Username) == 0) - { - free (linebuf); - linebuf = NULL; - linebuf_len = 0; goto handle_illegal; - } - /* else */ - free (linebuf); - linebuf = NULL; - linebuf_len = 0; } /* If not listed specifically as a reader, then this user @@ -1835,6 +1862,8 @@ check_command_legal_p (cmd_name) { /* writers file does not exist, so everyone is a writer, by default */ + if (linebuf) + free (linebuf); return 1; } @@ -1849,27 +1878,24 @@ check_command_legal_p (cmd_name) if (strcmp (linebuf, CVS_Username) == 0) { - free (linebuf); - linebuf = NULL; - linebuf_len = 0; found_it = 1; break; } - /* else */ - free (linebuf); - linebuf = NULL; - linebuf_len = 0; } if (found_it) { fclose (fp); + if (linebuf) + free (linebuf); return 1; } else /* writers file exists, but this user not listed in it */ { handle_illegal: fclose (fp); + if (linebuf) + free (linebuf); return 0; } } @@ -2452,7 +2478,6 @@ server_pause_check() { int paused = 0; char buf[1]; - int n; while (read (flowcontrol_pipe[0], buf, 1) == 1) { @@ -3049,6 +3074,42 @@ server_copy_file (file, update_dir, repository, newfile) /* See server.h for description. */ void +server_modtime (finfo, vers_ts) + struct file_info *finfo; + Vers_TS *vers_ts; +{ + char date[MAXDATELEN]; + int year, month, day, hour, minute, second; + /* Note that these strings are specified in RFC822 and do not vary + according to locale. */ + static const char *const month_names[] = + {"Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + + if (!supported_response ("Mod-time")) + return; + + /* The only hard part about this routine is converting the date + formats. In terms of functionality it all boils down to the + call to RCS_getrevtime. */ + if (RCS_getrevtime (finfo->rcs, vers_ts->vn_rcs, date, 0) == (time_t) -1) + /* FIXME? should we be printing some kind of warning? For one + thing I'm not 100% sure whether this happens in non-error + circumstances. */ + return; + + sscanf (date, SDATEFORM, &year, &month, &day, &hour, &minute, &second); + sprintf (date, "%d %s %d %d:%d:%d -0000", day, + month < 1 || month > 12 ? "???" : month_names[month - 1], + year, hour, minute, second); + buf_output0 (protocol, "Mod-time "); + buf_output0 (protocol, date); + buf_output0 (protocol, "\n"); +} + +/* See server.h for description. */ + +void server_updated (finfo, vers, updated, file_info, checksum) struct file_info *finfo; Vers_TS *vers; @@ -3136,6 +3197,8 @@ server_updated (finfo, vers, updated, file_info, checksum) buf_output0 (protocol, "Merged "); else if (updated == SERVER_PATCHED) buf_output0 (protocol, "Patched "); + else if (updated == SERVER_RCS_DIFF) + buf_output0 (protocol, "Rcs-diff "); else abort (); output_dir (finfo->update_dir, finfo->repository); @@ -3233,7 +3296,9 @@ server_updated (finfo, vers, updated, file_info, checksum) * not up-to-date, and we indicate that by leaving the file there. * I'm thinking of cases like "cvs update foo/foo.c foo". */ - if ((updated == SERVER_UPDATED || updated == SERVER_PATCHED) + if ((updated == SERVER_UPDATED + || updated == SERVER_PATCHED + || updated == SERVER_RCS_DIFF) /* But if we are joining, we'll need the file when we call join_file. */ && !joining ()) @@ -3271,6 +3336,14 @@ server_updated (finfo, vers, updated, file_info, checksum) done:; } +/* Return whether we should send patches in RCS format. */ + +int +server_use_rcs_diff () +{ + return supported_response ("Rcs-diff"); +} + void server_set_entstat (update_dir, repository) char *update_dir; @@ -3621,6 +3694,19 @@ serve_update_prog (arg) char *arg; { FILE *f; + + /* Before we do anything we need to make sure we are not in readonly + mode. */ + if (!check_command_legal_p ("commit")) + { + /* I might be willing to make this a warning, except we lack the + machinery to do so. */ + if (alloc_pending (80)) + sprintf (pending_error_text, "\ +E Flag -u in modules not allowed in readonly mode"); + return; + } + f = CVS_FOPEN (CVSADM_UPROG, "w+"); if (f == NULL) { @@ -4318,8 +4404,6 @@ check_repository_password (username, password, repository, host_user_ptr) found_it = 1; break; } - free (linebuf); - linebuf = NULL; } if (ferror (fp)) error (0, errno, "cannot read %s", filename); @@ -4340,9 +4424,7 @@ check_repository_password (username, password, repository, host_user_ptr) if (strcmp (found_password, crypt (password, found_password)) == 0) { /* Give host_user_ptr permanent storage. */ - *host_user_ptr = xmalloc (strlen (host_user_tmp) + 1); - strcpy (*host_user_ptr, host_user_tmp); - + *host_user_ptr = xstrdup (host_user_tmp); retval = 1; } else @@ -4358,6 +4440,8 @@ check_repository_password (username, password, repository, host_user_ptr) } free (filename); + if (linebuf) + free (linebuf); return retval; } @@ -4572,6 +4656,14 @@ pserver_authenticate_connection () { error (1, 0, "bad auth protocol end: %s", tmp); } + if (!root_allow_ok (repository)) + /* At least for the moment I'm going to do the paranoid + security thing and not tell them how it failed. I'm not + sure that is a good idea; it is a real pain when one needs + to track down what is going on for legitimate reasons. + The other issue is that the protocol doesn't really have + a good way for anything other than I HATE YOU. */ + goto i_hate_you; /* We need the real cleartext before we hash it. */ descrambled_password = descramble (password); @@ -4585,6 +4677,7 @@ pserver_authenticate_connection () } else { + i_hate_you: printf ("I HATE YOU\n"); fflush (stdout); /* I'm doing this manually rather than via error_exit () diff --git a/gnu/usr.bin/cvs/src/update.c b/gnu/usr.bin/cvs/src/update.c index 2dd6e0ff22f..82886b7a3e9 100644 --- a/gnu/usr.bin/cvs/src/update.c +++ b/gnu/usr.bin/cvs/src/update.c @@ -92,6 +92,7 @@ static int update_prune_dirs = 0; static int pipeout = 0; #ifdef SERVER_SUPPORT static int patches = 0; +static int rcs_diff_patches = 0; #endif static List *ignlist = (List *) NULL; static time_t last_register_time; @@ -134,7 +135,7 @@ update (argc, argv) wrap_setup (); /* parse the args */ - optind = 1; + optind = 0; while ((c = getopt (argc, argv, "+ApPflRQqduk:r:D:j:I:W:")) != -1) { switch (c) @@ -200,7 +201,10 @@ update (argc, argv) case 'u': #ifdef SERVER_SUPPORT if (server_active) + { patches = 1; + rcs_diff_patches = server_use_rcs_diff (); + } else #endif usage (update_usage); @@ -555,10 +559,31 @@ update_fileproc (callerdat, finfo) else { if (wrap_merge_is_copy (finfo->file)) +#if 0 + /* Look, we can't clobber the user's file. We + know it is modified and we're going to + overwrite their mod? Puh-leeze. The + correct behavior is probably something like + what merge_file does for -kb, which is to + give the users both files and tell them + what the two filenames are. Of course, -m + in wrappers needs to be documented *much* + better. Anyway, until then, make this a + fatal error. */ + /* Should we be warning the user that we are * overwriting the user's copy of the file? */ retval = checkout_file (finfo, vers, 0); +#else + { + error (0, 0, "A -m 'COPY' wrapper is specified"); + error (0, 0, "but file %s needs merge", + finfo->fullname); + error (1, 0, "\ +You probably want to avoid -m 'COPY' wrappers"); +#endif + } else retval = merge_file (finfo, vers); } @@ -626,8 +651,10 @@ update_fileproc (callerdat, finfo) { if (server_active && retval == 0) server_updated (finfo, vers, - SERVER_PATCHED, &file_info, - checksum); + (rcs_diff_patches + ? SERVER_RCS_DIFF + : SERVER_PATCHED), + &file_info, checksum); break; } } @@ -1242,7 +1269,7 @@ struct patch_file_data int final_nl; }; -/* Patch a file. Runs rcsdiff. This is only done when running as the +/* Patch a file. Runs diff. This is only done when running as the * server. The hope is that the diff will be smaller than the file * itself. */ @@ -1356,16 +1383,36 @@ patch_file (finfo, vers_ts, docheckout, file_info, checksum) retcode = 0; if (! fail) { - /* FIXME: This whole thing with diff/patch is rather more - convoluted than necessary (lots of forks and execs, need to - worry about versions of diff and patch, etc.). Also, we - send context lines which aren't needed (in the rare case in - which the diff doesn't apply, the checksum would catches it). - Solution perhaps is to librarify the RCS routines which apply - deltas or something equivalent. */ - /* This is -c, not -u, because we have no way of knowing which - DIFF is in use. */ - run_setup ("%s -c %s %s", DIFF, file1, file2); + const char *diff_options; + + /* FIXME: It might be better to come up with a diff library + which can be shared with the diffutils. */ + /* If the client does not support the Rcs-diff command, we + send a context diff, and the client must invoke patch. + That approach was problematical for various reasons. The + new approach only requires running diff in the server; the + client can handle everything without invoking an external + program. */ + if (! rcs_diff_patches) + { + /* We use -c, not -u, because we have no way of knowing + which DIFF is in use. */ + diff_options = "-c"; + } + else + { + /* FIXME: We should use -a if diff supports it. We should + probably just copy over most or all of the diff + handling in the RCS configure script. */ + /* IMHO, we shouldn't copy over anything which even + vaguely resembles the RCS configure script. That kind of + thing tends to be ugly, slow, and fragile. It also is a + a support headache for CVS to behave differently in subtle + ways based on whether it was installed correctly. Instead we + should come up with a diff library. -kingdon, Apr 1997. */ + diff_options = "-n"; + } + run_setup ("%s %s %s %s", DIFF, diff_options, file1, file2); /* A retcode of 0 means no differences. 1 means some differences. */ if ((retcode = run_exec (RUN_TTY, finfo->file, RUN_TTY, RUN_NORMAL)) != 0 |