diff options
author | Otto Moerbeek <otto@cvs.openbsd.org> | 2004-06-09 18:16:06 +0000 |
---|---|---|
committer | Otto Moerbeek <otto@cvs.openbsd.org> | 2004-06-09 18:16:06 +0000 |
commit | 452f0738fff320dd7af70f5c39030b643226878a (patch) | |
tree | d2102647d5ab8d50bf2e8ecc9c241a52717279fb /gnu | |
parent | c238b270c67623d00936cd77a1e88e61fc260eb2 (diff) |
Several potential security problems found and fixed by Stefan Esser &
Sebastian Krahmer.
ok millert@
Diffstat (limited to 'gnu')
-rw-r--r-- | gnu/usr.bin/cvs/lib/xsize.h | 108 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/commit.c | 7 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/cvs.h | 4 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/filesubr.c | 8 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/history.c | 536 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/server.c | 78 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/wrapper.c | 384 |
7 files changed, 857 insertions, 268 deletions
diff --git a/gnu/usr.bin/cvs/lib/xsize.h b/gnu/usr.bin/cvs/lib/xsize.h new file mode 100644 index 00000000000..7634c6d4d5a --- /dev/null +++ b/gnu/usr.bin/cvs/lib/xsize.h @@ -0,0 +1,108 @@ +/* xsize.h -- Checked size_t computations. + + Copyright (C) 2003 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifndef _XSIZE_H +#define _XSIZE_H + +/* Get size_t. */ +#include <stddef.h> + +/* Get SIZE_MAX. */ +#include <limits.h> +#if HAVE_STDINT_H +# include <stdint.h> +#endif + +/* The size of memory objects is often computed through expressions of + type size_t. Example: + void* p = malloc (header_size + n * element_size). + These computations can lead to overflow. When this happens, malloc() + returns a piece of memory that is way too small, and the program then + crashes while attempting to fill the memory. + To avoid this, the functions and macros in this file check for overflow. + The convention is that SIZE_MAX represents overflow. + malloc (SIZE_MAX) is not guaranteed to fail -- think of a malloc + implementation that uses mmap --, it's recommended to use size_overflow_p() + or size_in_bounds_p() before invoking malloc(). + The example thus becomes: + size_t size = xsum (header_size, xtimes (n, element_size)); + void *p = (size_in_bounds_p (size) ? malloc (size) : NULL); +*/ + +/* Convert an arbitrary value >= 0 to type size_t. */ +#define xcast_size_t(N) \ + ((N) <= SIZE_MAX ? (size_t) (N) : SIZE_MAX) + +/* Sum of two sizes, with overflow check. */ +static inline size_t +#if __GNUC__ >= 3 +__attribute__ ((__pure__)) +#endif +xsum (size_t size1, size_t size2) +{ + size_t sum = size1 + size2; + return (sum >= size1 ? sum : SIZE_MAX); +} + +/* Sum of three sizes, with overflow check. */ +static inline size_t +#if __GNUC__ >= 3 +__attribute__ ((__pure__)) +#endif +xsum3 (size_t size1, size_t size2, size_t size3) +{ + return xsum (xsum (size1, size2), size3); +} + +/* Sum of four sizes, with overflow check. */ +static inline size_t +#if __GNUC__ >= 3 +__attribute__ ((__pure__)) +#endif +xsum4 (size_t size1, size_t size2, size_t size3, size_t size4) +{ + return xsum (xsum (xsum (size1, size2), size3), size4); +} + +/* Maximum of two sizes, with overflow check. */ +static inline size_t +#if __GNUC__ >= 3 +__attribute__ ((__pure__)) +#endif +xmax (size_t size1, size_t size2) +{ + /* No explicit check is needed here, because for any n: + max (SIZE_MAX, n) == SIZE_MAX and max (n, SIZE_MAX) == SIZE_MAX. */ + return (size1 >= size2 ? size1 : size2); +} + +/* Multiplication of a count with an element size, with overflow check. + The count must be >= 0 and the element size must be > 0. + This is a macro, not an inline function, so that it works correctly even + when N is of a wider tupe and N > SIZE_MAX. */ +#define xtimes(N, ELSIZE) \ + ((N) <= SIZE_MAX / (ELSIZE) ? (size_t) (N) * (ELSIZE) : SIZE_MAX) + +/* Check for overflow. */ +#define size_overflow_p(SIZE) \ + ((SIZE) == SIZE_MAX) +/* Check against overflow. */ +#define size_in_bounds_p(SIZE) \ + ((SIZE) != SIZE_MAX) + +#endif /* _XSIZE_H */ diff --git a/gnu/usr.bin/cvs/src/commit.c b/gnu/usr.bin/cvs/src/commit.c index 149da7cb74d..0fe9f9425d7 100644 --- a/gnu/usr.bin/cvs/src/commit.c +++ b/gnu/usr.bin/cvs/src/commit.c @@ -472,7 +472,12 @@ commit (argc, argv) operate on, and only work with those files in the future. This saves time--we don't want to search the file system of the working directory twice. */ - find_args.argv = (char **) xmalloc (find_args.argc * sizeof (char **)); + if (size_overflow_p (xtimes (find_args.argc, sizeof (char **)))) + { + find_args.argc = 0; + return 0; + } + find_args.argv = xmalloc (xtimes (find_args.argc, sizeof (char **))); find_args.argc = 0; walklist (find_args.ulist, copy_ulist, &find_args); diff --git a/gnu/usr.bin/cvs/src/cvs.h b/gnu/usr.bin/cvs/src/cvs.h index 961e02c5240..188df983469 100644 --- a/gnu/usr.bin/cvs/src/cvs.h +++ b/gnu/usr.bin/cvs/src/cvs.h @@ -41,6 +41,10 @@ #include "popen.h" #endif +/* Begin GNULIB headers. */ +#include "xsize.h" +/* End GNULIB headers. */ + #ifdef STDC_HEADERS #include <stdlib.h> #else diff --git a/gnu/usr.bin/cvs/src/filesubr.c b/gnu/usr.bin/cvs/src/filesubr.c index 6f7dfd08a82..e41f3a553cc 100644 --- a/gnu/usr.bin/cvs/src/filesubr.c +++ b/gnu/usr.bin/cvs/src/filesubr.c @@ -965,8 +965,14 @@ expand_wild (argc, argv, pargc, pargv) char ***pargv; { int i; + if (size_overflow_p (xtimes (argc, sizeof (char *)))) { + *pargc = 0; + *pargv = NULL; + error (0, 0, "expand_wild: too many arguments"); + return; + } *pargc = argc; - *pargv = (char **) xmalloc (argc * sizeof (char *)); + *pargv = xmalloc (xtimes (argc, sizeof (char *))); for (i = 0; i < argc; ++i) (*pargv)[i] = xstrdup (argv[i]); } diff --git a/gnu/usr.bin/cvs/src/history.c b/gnu/usr.bin/cvs/src/history.c index 7a40b7bd8bd..945b1e116a9 100644 --- a/gnu/usr.bin/cvs/src/history.c +++ b/gnu/usr.bin/cvs/src/history.c @@ -17,6 +17,7 @@ * X is a single character showing the type of event: * T "Tag" cmd. * O "Checkout" cmd. + * E "Export" cmd. * F "Release" cmd. * W "Update" cmd - No User file, Remove from Entries file. * U "Update" cmd - File was checked out over User file. @@ -40,18 +41,18 @@ * command was typed. * T "A" --> New Tag, "D" --> Delete Tag * Otherwise it is the Tag or Date to modify. - * O,F A "" (null field) + * O,F,E A "" (null field) * * rev(s) Revision number or tag. * T The Tag to apply. - * O The Tag or Date, if specified, else "" (null field). + * O,E The Tag or Date, if specified, else "" (null field). * F "" (null field) * W The Tag or Date, if specified, else "" (null field). * U The Revision checked out over the User file. * G,C The Revision(s) involved in merge. * M,A,R RCS Revision affected. * - * argument The module (for [TOUF]) or file (for [WUGCMAR]) affected. + * argument The module (for [TOEUF]) or file (for [WUGCMAR]) affected. * * *** Report categories: "User" and "Since" modifiers apply to all reports. @@ -59,7 +60,7 @@ * * Extract list of record types * - * -e, -x [TOFWUGCMAR] + * -e, -x [TOEFWUGCMAR] * * Extracted records are simply printed, No analysis is performed. * All "field" modifiers apply. -e chooses all types. @@ -130,7 +131,7 @@ * -p repository - Only records in which the "repository" string is a * prefix of the "repos" field are considered. * - * -m modulename - Only records which contain "modulename" in the + * -n modulename - Only records which contain "modulename" in the * "module" field are considered. * * @@ -177,11 +178,7 @@ */ #include "cvs.h" - -#ifndef lint -static const char rcsid[] = "$CVSid: @(#)history.c 1.33 94/09/21 $"; -USE(rcsid); -#endif +#include "savecwd.h" static struct hrec { @@ -194,16 +191,16 @@ static struct hrec char *end; /* Ptr into repository to copy at end of workdir */ char *mod; /* The module within which the file is contained */ time_t date; /* Calculated from date stored in record */ - int idx; /* Index of record, for "stable" sort. */ + long idx; /* Index of record, for "stable" sort. */ } *hrec_head; +static long hrec_idx; -static char *fill_hrec PROTO((char *line, struct hrec * hr)); +static void fill_hrec PROTO((char *line, struct hrec * hr)); static int accept_hrec PROTO((struct hrec * hr, struct hrec * lr)); static int select_hrec PROTO((struct hrec * hr)); static int sort_order PROTO((const PTR l, const PTR r)); static int within PROTO((char *find, char *string)); -static time_t date_and_time PROTO((char *date_str)); static void expand_modules PROTO((void)); static void read_hrecs PROTO((char *fname)); static void report_hrecs PROTO((void)); @@ -211,7 +208,7 @@ static void save_file PROTO((char *dir, char *name, char *module)); static void save_module PROTO((char *module)); static void save_user PROTO((char *name)); -#define ALL_REC_TYPES "TOFWUCGMAR" +#define ALL_REC_TYPES "TOEFWUCGMAR" #define USER_INCREMENT 2 #define FILE_INCREMENT 128 #define MODULE_INCREMENT 5 @@ -233,21 +230,30 @@ static short repos_sort; static short file_sort; static short module_sort; -#ifdef HAVE_RCS5 static short tz_local; static time_t tz_seconds_east_of_GMT; static char *tz_name = "+0000"; -#else -static char tz_name[] = "LT"; -#endif -static time_t since_date; -static char since_rev[20]; /* Maxrev ~= 99.99.99.999 */ -static char since_tag[64]; +char *logHistory = ALL_REC_TYPES; + +/* -r, -t, or -b options, malloc'd. These are "" if the option in + question is not specified or is overridden by another option. The + main reason for using "" rather than NULL is historical. Together + with since_date, these are a mutually exclusive set; one overrides the + others. */ +static char *since_rev; +static char *since_tag; +static char *backto; +/* -D option, or 0 if not specified. RCS format. */ +static char * since_date; + static struct hrec *last_since_tag; -static char backto[128]; static struct hrec *last_backto; -static char rec_types[20]; + +/* Record types to look for, malloc'd. Probably could be statically + allocated, but only if we wanted to check for duplicates more than + we do. */ +static char *rec_types; static int hrec_count; static int hrec_max; @@ -270,6 +276,12 @@ static int mod_count; /* Number of elements used */ static char *histfile; /* Ptr to the history file name */ +/* This is pretty unclear. First of all, separating "flags" vs. + "options" (I think the distinction is that "options" take arguments) + is nonstandard, and not something we do elsewhere in CVS. Second of + all, what does "reports" mean? I think it means that you can only + supply one of those options, but "reports" hardly has that meaning in + a self-explanatory way. */ static const char *const history_usg[] = { "Usage: %s %s [-report] [-flags] [-options args] [files...]\n\n", @@ -278,10 +290,10 @@ static const char *const history_usg[] = " -c Committed (Modified) files\n", " -o Checked out modules\n", " -m <module> Look for specified module (repeatable)\n", - " -x [TOFWUCGMAR] Extract by record type\n", + " -x [TOEFWUCGMAR] Extract by record type\n", + " -e Everything (same as -x, but all record types)\n", " Flags:\n", " -a All users (Default is self)\n", - " -e Everything (same as -x, but all record types)\n", " -l Last modified (committed or modified report)\n", " -w Working directory must match\n", " Options:\n", @@ -354,31 +366,23 @@ sort_order (l, r) return (left->idx - right->idx); } -static time_t -date_and_time (date_str) - char *date_str; -{ - time_t t; - - t = get_date (date_str, (struct timeb *) NULL); - if (t == (time_t) - 1) - error (1, 0, "Can't parse date/time: %s", date_str); - return (t); -} - int history (argc, argv) int argc; char **argv; { int i, c; - char fname[PATH_MAX]; + char *fname; if (argc == -1) usage (history_usg); - optind = 1; - while ((c = getopt (argc, argv, "Tacelow?D:b:f:m:n:p:r:t:u:x:X:z:")) != -1) + since_rev = xstrdup (""); + since_tag = xstrdup (""); + backto = xstrdup (""); + rec_types = xstrdup (""); + optind = 0; + while ((c = getopt (argc, argv, "+Tacelow?D:b:f:m:n:p:r:t:u:x:X:z:")) != -1) { switch (c) { @@ -396,7 +400,8 @@ history (argc, argv) case 'e': report_count++; extract++; - (void) strcpy (rec_types, ALL_REC_TYPES); + free (rec_types); + rec_types = xstrdup (ALL_REC_TYPES); break; case 'l': /* Find Last file record */ last_entry = 1; @@ -409,37 +414,37 @@ history (argc, argv) working = 1; break; case 'X': /* Undocumented debugging flag */ +#ifdef DEBUG histfile = optarg; +#endif break; + case 'D': /* Since specified date */ if (*since_rev || *since_tag || *backto) { error (0, 0, "date overriding rev/tag/backto"); *since_rev = *since_tag = *backto = '\0'; } - since_date = date_and_time (optarg); + since_date = Make_Date (optarg); break; case 'b': /* Since specified file/Repos */ if (since_date || *since_rev || *since_tag) { error (0, 0, "backto overriding date/rev/tag"); *since_rev = *since_tag = '\0'; - since_date = 0; - } - if (strlen (optarg) >= sizeof (backto)) - { - error (0, 0, "backto truncated to %d bytes", - sizeof (backto) - 1); - optarg[sizeof (backto) - 1] = '\0'; + if (since_date != NULL) + free (since_date); + since_date = NULL; } - (void) strcpy (backto, optarg); + free (backto); + backto = xstrdup (optarg); break; case 'f': /* For specified file */ save_file ("", optarg, (char *) NULL); break; case 'm': /* Full module report */ - report_count++; - module_report++; + if (!module_report++) report_count++; + /* fall through */ case 'n': /* Look for specified module */ save_module (optarg); break; @@ -451,18 +456,24 @@ history (argc, argv) { error (0, 0, "rev overriding date/tag/backto"); *since_tag = *backto = '\0'; - since_date = 0; + if (since_date != NULL) + free (since_date); + since_date = NULL; } - (void) strcpy (since_rev, optarg); + free (since_rev); + since_rev = xstrdup (optarg); break; case 't': /* Since specified Tag/Rev */ if (since_date || *since_rev || *backto) { error (0, 0, "tag overriding date/marker/file/repos"); *since_rev = *backto = '\0'; - since_date = 0; + if (since_date != NULL) + free (since_date); + since_date = NULL; } - (void) strcpy (since_tag, optarg); /* tag */ + free (since_tag); + since_tag = xstrdup (optarg); break; case 'u': /* For specified username */ save_user (optarg); @@ -477,12 +488,10 @@ history (argc, argv) if (!strchr (ALL_REC_TYPES, *cp)) error (1, 0, "%c is not a valid report type", *cp); } - (void) strcpy (rec_types, optarg); + free (rec_types); + rec_types = xstrdup (optarg); break; case 'z': -#ifndef HAVE_RCS5 - error (0, 0, "-z not supported with RCS 4"); -#else tz_local = (optarg[0] == 'l' || optarg[0] == 'L') && (optarg[1] == 't' || optarg[1] == 'T') @@ -513,7 +522,6 @@ history (argc, argv) tz_name = optarg; } } -#endif break; case '?': default: @@ -521,16 +529,20 @@ history (argc, argv) break; } } - c = optind; /* Save the handled option count */ + argc -= optind; + argv += optind; + for (i = 0; i < argc; i++) + save_file ("", argv[i], (char *) NULL); + /* ================ Now analyze the arguments a bit */ if (!report_count) v_checkout++; else if (report_count > 1) - error (1, 0, "Only one report type allowed from: \"-Tcomx\"."); + error (1, 0, "Only one report type allowed from: \"-Tcomxe\"."); #ifdef CLIENT_SUPPORT - if (client_active) + if (current_parsed_root->isremote) { struct file_list_str *f1; char **mod; @@ -555,7 +567,7 @@ history (argc, argv) if (histfile) send_arg("-X"); if (since_date) - option_with_arg ("-D", asctime (gmtime (&since_date))); + client_senddate (since_date); if (backto[0] != '\0') option_with_arg ("-b", backto); for (f1 = file_list; f1 < &file_list[file_count]; ++f1) @@ -569,9 +581,9 @@ history (argc, argv) send_arg("-m"); for (mod = mod_list; mod < &mod_list[mod_count]; ++mod) option_with_arg ("-n", *mod); - if (since_rev != NULL) + if (*since_rev) option_with_arg ("-r", since_rev); - if (since_tag != NULL) + if (*since_tag) option_with_arg ("-t", since_tag); for (mod = user_list; mod < &user_list[user_count]; ++mod) option_with_arg ("-u", *mod); @@ -579,8 +591,7 @@ history (argc, argv) option_with_arg ("-x", rec_types); option_with_arg ("-z", tz_name); - if (fprintf (to_server, "history\n") < 0) - error (1, errno, "writing to server"); + send_to_server ("history\012", 0); return get_responses_and_close (); } #endif @@ -594,7 +605,10 @@ history (argc, argv) if (tag_report) { if (!strchr (rec_types, 'T')) + { + rec_types = xrealloc (rec_types, strlen (rec_types) + 5); (void) strcat (rec_types, "T"); + } } else if (extract) { @@ -603,12 +617,14 @@ history (argc, argv) } else if (modified) { - (void) strcpy (rec_types, "MAR"); + free (rec_types); + rec_types = xstrdup ("MAR"); /* * If the user has not specified a date oriented flag ("Since"), sort * by Repository/file before date. Default is "just" date. */ - if (!since_date && !*since_rev && !*since_tag && !*backto) + if (last_entry + || (!since_date && !*since_rev && !*since_tag && !*backto)) { repos_sort++; file_sort++; @@ -622,7 +638,8 @@ history (argc, argv) } else if (module_report) { - (void) strcpy (rec_types, last_entry ? "OMAR" : ALL_REC_TYPES); + free (rec_types); + rec_types = xstrdup (last_entry ? "OMAR" : ALL_REC_TYPES); module_sort++; repos_sort++; file_sort++; @@ -631,11 +648,13 @@ history (argc, argv) else /* Must be "checkout" or default */ { - (void) strcpy (rec_types, "OF"); + free (rec_types); + rec_types = xstrdup ("OF"); /* See comments in "modified" above */ if (!last_entry && user_list) user_sort++; - if (!since_date && !*since_rev && !*since_tag && !*backto) + if (last_entry + || (!since_date && !*since_rev && !*since_tag && !*backto)) file_sort++; } @@ -645,22 +664,35 @@ history (argc, argv) /* If we're looking back to a Tag value, must consider "Tag" records */ if (*since_tag && !strchr (rec_types, 'T')) + { + rec_types = xrealloc (rec_types, strlen (rec_types) + 5); (void) strcat (rec_types, "T"); - - argc -= c; - argv += c; - for (i = 0; i < argc; i++) - save_file ("", argv[i], (char *) NULL); + } if (histfile) - (void) strcpy (fname, histfile); + fname = xstrdup (histfile); else - (void) sprintf (fname, "%s/%s/%s", CVSroot, + { + fname = xmalloc (strlen (current_parsed_root->directory) + sizeof (CVSROOTADM) + + sizeof (CVSROOTADM_HISTORY) + 10); + (void) sprintf (fname, "%s/%s/%s", current_parsed_root->directory, CVSROOTADM, CVSROOTADM_HISTORY); + } read_hrecs (fname); - qsort ((PTR) hrec_head, hrec_count, sizeof (struct hrec), sort_order); + if(hrec_count>0) + { + qsort ((PTR) hrec_head, hrec_count, + sizeof (struct hrec), sort_order); + } report_hrecs (); + free (fname); + if (since_date != NULL) + free (since_date); + free (since_rev); + free (since_tag); + free (backto); + free (rec_types); return (0); } @@ -673,8 +705,9 @@ history_write (type, update_dir, revs, name, repository) char *name; char *repository; { - char fname[PATH_MAX], workdir[PATH_MAX], homedir[PATH_MAX]; - static char username[20]; /* !!! Should be global */ + char *fname; + char *workdir; + char *username = getcaller (); int fd; char *line; char *slash = "", *cp, *cp2, *repos; @@ -684,42 +717,49 @@ history_write (type, update_dir, revs, name, repository) if (logoff) /* History is turned off by cmd line switch */ return; - (void) sprintf (fname, "%s/%s/%s", CVSroot, CVSROOTADM, CVSROOTADM_HISTORY); + if ( strchr(logHistory, type) == NULL ) + return; + fname = xmalloc (strlen (current_parsed_root->directory) + sizeof (CVSROOTADM) + + sizeof (CVSROOTADM_HISTORY) + 3); + (void) sprintf (fname, "%s/%s/%s", current_parsed_root->directory, + CVSROOTADM, CVSROOTADM_HISTORY); /* turn off history logging if the history file does not exist */ if (!isfile (fname)) { logoff = 1; - return; + goto out; } if (trace) -#ifdef SERVER_SUPPORT - fprintf (stderr, "%c-> fopen(%s,a)\n", - (server_active) ? 'S' : ' ', fname); -#else - fprintf (stderr, "-> fopen(%s,a)\n", fname); -#endif + fprintf (stderr, "%s-> fopen(%s,a)\n", + CLIENT_SERVER_STR, fname); if (noexec) - return; - if ((fd = open (fname, O_WRONLY | O_APPEND | O_CREAT, 0666)) < 0) - error (1, errno, "cannot open history file: %s", fname); + goto out; + fd = CVS_OPEN (fname, O_WRONLY | O_APPEND | O_CREAT | OPEN_BINARY, 0666); + if (fd < 0) + { + if (! really_quiet) + { + error (0, errno, "warning: cannot write to history file %s", + fname); + } + goto out; + } repos = Short_Repository (repository); if (!PrCurDir) { - struct passwd *pw; + char *pwdir; - (void) strcpy (username, getcaller ()); + pwdir = get_homedir (); PrCurDir = CurDir; - if (!(pw = (struct passwd *) getpwnam (username))) - error (0, 0, "cannot find own username"); - else + if (pwdir != NULL) { - /* Assumes neither CurDir nor pw->pw_dir ends in '/' */ - i = strlen (pw->pw_dir); - if (!strncmp (CurDir, pw->pw_dir, i)) + /* Assumes neither CurDir nor pwdir ends in '/' */ + i = strlen (pwdir); + if (!strncmp (CurDir, pwdir, i)) { PrCurDir += i; /* Point to '/' separator */ tilde = "~"; @@ -727,13 +767,21 @@ history_write (type, update_dir, revs, name, repository) else { /* Try harder to find a "homedir" */ - if (!getwd (workdir)) - error (1, errno, "can't getwd in history"); - if (chdir (pw->pw_dir) < 0) - error (1, errno, "can't chdir(%s)", pw->pw_dir); - if (!getwd (homedir)) - error (1, errno, "can't getwd in %s", pw->pw_dir); - (void) chdir (workdir); + struct saved_cwd cwd; + char *homedir; + + if (save_cwd (&cwd)) + error_exit (); + + if ( CVS_CHDIR (pwdir) < 0) + error (1, errno, "can't chdir(%s)", pwdir); + homedir = xgetwd (); + if (homedir == NULL) + error (1, errno, "can't getwd in %s", pwdir); + + if (restore_cwd (&cwd, NULL)) + error_exit (); + free_cwd (&cwd); i = strlen (homedir); if (!strncmp (CurDir, homedir, i)) @@ -741,6 +789,7 @@ history_write (type, update_dir, revs, name, repository) PrCurDir += i; /* Point to '/' separator */ tilde = "~"; } + free (homedir); } } } @@ -755,6 +804,8 @@ history_write (type, update_dir, revs, name, repository) else update_dir = ""; + workdir = xmalloc (strlen (tilde) + strlen (PrCurDir) + strlen (slash) + + strlen (update_dir) + 10); (void) sprintf (workdir, "%s%s%s%s", tilde, PrCurDir, slash, update_dir); /* @@ -828,6 +879,9 @@ history_write (type, update_dir, revs, name, repository) free (line); if (close (fd) != 0) error (1, errno, "cannot close history file: %s", fname); + free (workdir); + out: + free (fname); } /* @@ -840,9 +894,13 @@ save_user (name) { if (user_count == user_max) { - user_max += USER_INCREMENT; - user_list = (char **) xrealloc ((char *) user_list, - (int) user_max * sizeof (char *)); + user_max = xsum (user_max, USER_INCREMENT); + if (size_overflow_p (xtimes (user_max, sizeof (char *)))) + { + error (0, 0, "save_user: too many users"); + return; + } + user_list = xrealloc (user_list, xtimes (user_max, sizeof (char *))); } user_list[user_count++] = xstrdup (name); } @@ -870,9 +928,13 @@ save_file (dir, name, module) if (file_count == file_max) { - file_max += FILE_INCREMENT; - file_list = (struct file_list_str *) xrealloc ((char *) file_list, - file_max * sizeof (*fl)); + file_max = xsum (file_max, FILE_INCREMENT); + if (size_overflow_p (xtimes (file_max, sizeof (*fl)))) + { + error (0, 0, "save_file: too many files"); + return; + } + file_list = xrealloc (file_list, xtimes (file_max, sizeof (*fl))); } fl = &file_list[file_count++]; fl->l_file = cp = xmalloc (strlen (dir) + strlen (name) + 2); @@ -911,9 +973,13 @@ save_module (module) { if (mod_count == mod_max) { - mod_max += MODULE_INCREMENT; - mod_list = (char **) xrealloc ((char *) mod_list, - mod_max * sizeof (char *)); + mod_max = xsum (mod_max, MODULE_INCREMENT); + if (size_overflow_p (xtimes (mod_max, sizeof (char *)))) + { + error (0, 0, "save_module: too many modules"); + return; + } + mod_list = xrealloc (mod_list, xtimes (mod_max, sizeof (char *))); } mod_list[mod_count++] = xstrdup (module); } @@ -931,128 +997,162 @@ expand_modules () * * Split it into 7 parts and drop the parts into a "struct hrec". * Return a pointer to the character following the newline. + * */ -#define NEXT_BAR(here) do { while (isspace(*line)) line++; hr->here = line; while ((c = *line++) && c != '|') ; if (!c) return(rtn); *(line - 1) = '\0'; } while (0) +#define NEXT_BAR(here) do { \ + while (isspace(*line)) line++; \ + hr->here = line; \ + while ((c = *line++) && c != '|') ; \ + if (!c) return; line[-1] = '\0'; \ + } while (0) -static char * +static void fill_hrec (line, hr) char *line; struct hrec *hr; { - char *cp, *rtn; + char *cp; int c; - int off; - static int idx = 0; - memset ((char *) hr, 0, sizeof (*hr)); - while (isspace (*line)) - line++; - if (!(rtn = strchr (line, '\n'))) - return (""); - *rtn++ = '\0'; + hr->type = hr->user = hr->dir = hr->repos = hr->rev = hr->file = + hr->end = hr->mod = NULL; + hr->date = -1; + hr->idx = ++hrec_idx; - hr->type = line++; - (void) sscanf (line, "%x", &hr->date); - while (*line && strchr ("0123456789abcdefABCDEF", *line)) + while (isspace ((unsigned char) *line)) line++; - if (*line == '\0') - return (rtn); - line++; + hr->type = line++; + hr->date = strtoul (line, &cp, 16); + if (cp == line || *cp != '|') + return; + line = cp + 1; NEXT_BAR (user); NEXT_BAR (dir); if ((cp = strrchr (hr->dir, '*')) != NULL) { *cp++ = '\0'; - (void) sscanf (cp, "%x", &off); - hr->end = line + off; + hr->end = line + strtoul (cp, NULL, 16); } else hr->end = line - 1; /* A handy pointer to '\0' */ NEXT_BAR (repos); NEXT_BAR (rev); - hr->idx = idx++; - if (strchr ("FOT", *(hr->type))) + if (strchr ("FOET", *(hr->type))) hr->mod = line; - NEXT_BAR (file); /* This returns ptr to next line or final '\0' */ - return (rtn); /* If it falls through, go on to next record */ + NEXT_BAR (file); } + +#ifndef STAT_BLOCKSIZE +#if HAVE_ST_BLKSIZE +#define STAT_BLOCKSIZE(s) (s).st_blksize +#else +#define STAT_BLOCKSIZE(s) (4 * 1024) +#endif +#endif + + /* read_hrecs's job is to read the history file and fill in all the "hrec" * (history record) array elements with the ones we need to print. * * Logic: - * - Read the whole history file into a single buffer. - * - Walk through the buffer, parsing lines out of the buffer. - * 1. Split line into pointer and integer fields in the "next" hrec. - * 2. Apply tests to the hrec to see if it is wanted. - * 3. If it *is* wanted, bump the hrec pointer down by one. + * - Read a block from the file. + * - Walk through the block parsing line into hr records. + * - if the hr isn't used, free its strings, if it is, bump the hrec counter + * - at the end of a block, copy the end of the current block to the start + * of space for the next block, then read in the next block. If we get less + * than the whole block, we're done. */ static void read_hrecs (fname) char *fname; { - char *cp, *cp2; - int i, fd; - struct hrec *hr; + unsigned char *cpstart, *cpend, *cp, *nl; + char *hrline; + int i; + int fd; struct stat st_buf; - if ((fd = open (fname, O_RDONLY)) < 0) + if ((fd = CVS_OPEN (fname, O_RDONLY | OPEN_BINARY)) < 0) error (1, errno, "cannot open history file: %s", fname); if (fstat (fd, &st_buf) < 0) error (1, errno, "can't stat history file"); - /* Exactly enough space for lines data */ - if (!(i = st_buf.st_size)) + if (!(st_buf.st_size)) error (1, 0, "history file is empty"); - cp = xmalloc (i + 2); - if (read (fd, cp, i) != i) - error (1, errno, "cannot read log file"); - (void) close (fd); - - if (*(cp + i - 1) != '\n') - { - *(cp + i) = '\n'; /* Make sure last line ends in '\n' */ - i++; - } - *(cp + i) = '\0'; - for (cp2 = cp; cp2 - cp < i; cp2++) - { - if (*cp2 != '\n' && !isprint (*cp2)) - *cp2 = ' '; - } + cpstart = xmalloc (2 * STAT_BLOCKSIZE(st_buf)); + cpstart[0] = '\0'; + cp = cpend = cpstart; hrec_max = HREC_INCREMENT; - hrec_head = (struct hrec *) xmalloc (hrec_max * sizeof (struct hrec)); + hrec_head = xmalloc (hrec_max * sizeof (struct hrec)); + hrec_idx = 0; - while (*cp) + for (;;) { + for (nl = cp; nl < cpend && *nl != '\n'; nl++) + if (!isprint(*nl)) *nl = ' '; + + if (nl >= cpend) + { + if (nl - cp >= STAT_BLOCKSIZE(st_buf)) + { + error(1, 0, "history line %ld too long (> %lu)", hrec_idx + 1, + (unsigned long) STAT_BLOCKSIZE(st_buf)); + } + if (nl > cp) + memmove (cpstart, cp, nl - cp); + nl = cpstart + (nl - cp); + cp = cpstart; + i = read (fd, nl, STAT_BLOCKSIZE(st_buf)); + if (i > 0) + { + cpend = nl + i; + *cpend = '\0'; + continue; + } + if (i < 0) + error (1, errno, "error reading history file"); + if (nl == cp) break; + error (0, 0, "warning: no newline at end of history file"); + } + *nl = '\0'; + if (hrec_count == hrec_max) { struct hrec *old_head = hrec_head; hrec_max += HREC_INCREMENT; - hrec_head = (struct hrec *) xrealloc ((char *) hrec_head, - hrec_max * sizeof (struct hrec)); - if (hrec_head != old_head) - { - if (last_since_tag) - last_since_tag = hrec_head + (last_since_tag - old_head); - if (last_backto) - last_backto = hrec_head + (last_backto - old_head); - } + hrec_head = xrealloc ((char *) hrec_head, + hrec_max * sizeof (struct hrec)); + if (last_since_tag) + last_since_tag = hrec_head + (last_since_tag - old_head); + if (last_backto) + last_backto = hrec_head + (last_backto - old_head); } - hr = hrec_head + hrec_count; - cp = fill_hrec (cp, hr); /* cp == next line or '\0' at end of buffer */ + /* fill_hrec dates from when history read the entire + history file in one chunk, and then records were pulled out + by pointing to the various parts of this big chunk. This is + why there are ugly hacks here: I don't want to completely + re-write the whole history stuff right now. */ - if (select_hrec (hr)) + hrline = xstrdup ((char *)cp); + fill_hrec (hrline, &hrec_head[hrec_count]); + if (select_hrec (&hrec_head[hrec_count])) hrec_count++; + else + free(hrline); + + cp = nl + 1; } + free (cpstart); + close (fd); /* Special selection problem: If "since_tag" is set, we have saved every * record from the 1st occurrence of "since_tag", when we want to save @@ -1109,10 +1209,18 @@ select_hrec (hr) struct file_list_str *fl; int count; + /* basic validity checking */ + if (!hr->type || !hr->user || !hr->dir || !hr->repos || !hr->rev || + !hr->file || !hr->end) + { + error (0, 0, "warning: history line %ld invalid", hr->idx); + return (0); + } + /* "Since" checking: The argument parser guarantees that only one of the * following four choices is set: * - * 1. If "since_date" is set, it contains a Unix time_t specified on the + * 1. If "since_date" is set, it contains the date specified on the * command line. hr->date fields earlier than "since_date" are ignored. * 2. If "since_rev" is set, it contains either an RCS "dotted" revision * number (which is of limited use) or a symbolic TAG. Each RCS file @@ -1132,16 +1240,29 @@ select_hrec (hr) */ if (since_date) { - if (hr->date < since_date) + char *ourdate = date_from_time_t (hr->date); + count = RCS_datecmp (ourdate, since_date); + free (ourdate); + if (count < 0) return (0); } else if (*since_rev) { Vers_TS *vers; time_t t; - - vers = Version_TS (hr->repos, (char *) NULL, since_rev, (char *) NULL, - hr->file, 1, 0, (List *) NULL, (List *) NULL); + struct file_info finfo; + + memset (&finfo, 0, sizeof finfo); + finfo.file = hr->file; + /* Not used, so don't worry about it. */ + finfo.update_dir = NULL; + finfo.fullname = finfo.file; + finfo.repository = hr->repos; + finfo.entries = NULL; + finfo.rcs = NULL; + + vers = Version_TS (&finfo, (char *) NULL, since_rev, (char *) NULL, + 1, 0); if (vers->vn_rcs) { if ((t = RCS_getrevtime (vers->srcfile, vers->vn_rcs, (char *) 0, 0)) @@ -1213,7 +1334,7 @@ select_hrec (hr) */ if (!strchr (rec_types, *(hr->type))) return (0); - if (!strchr ("TFO", *(hr->type))) /* Don't bother with "file" if "TFO" */ + if (!strchr ("TFOE", *(hr->type))) /* Don't bother with "file" if "TFOE" */ { if (file_list) /* If file_list is null, accept all */ { @@ -1225,7 +1346,7 @@ select_hrec (hr) * the concatenation of the repository and file from hrec. * 3. Else compare the file_list entry against the hrec file. */ - char cmpfile[PATH_MAX]; + char *cmpfile = NULL; if (*(cp = fl->l_file) == '*') { @@ -1241,8 +1362,12 @@ select_hrec (hr) { if (strchr (cp, '/')) { - (void) sprintf (cp2 = cmpfile, "%s/%s", + cmpfile = xmalloc (strlen (hr->repos) + + strlen (hr->file) + + 10); + (void) sprintf (cmpfile, "%s/%s", hr->repos, hr->file); + cp2 = cmpfile; } else { @@ -1255,6 +1380,8 @@ select_hrec (hr) hr->mod = fl->l_module; break; } + if (cmpfile != NULL) + free (cmpfile); } } if (!count) @@ -1313,7 +1440,7 @@ report_hrecs () hr++; for (count = hrec_count; count--; lr = hr, hr++) { - char repos[PATH_MAX]; + char *repos; if (!count) hr = NULL; @@ -1321,7 +1448,7 @@ report_hrecs () continue; ty = *(lr->type); - (void) strcpy (repos, lr->repos); + repos = xstrdup (lr->repos); if ((cp = strrchr (repos, '/')) != NULL) { if (lr->mod && !strcmp (++cp, lr->mod)) @@ -1339,6 +1466,7 @@ report_hrecs () rev_len = i; if (lr->mod && (i = strlen (lr->mod)) > mod_len) mod_len = i; + free (repos); } /* Walk through hrec array setting "lr" (Last Record) to each element. @@ -1352,7 +1480,8 @@ report_hrecs () */ for (lr = hrec_head, hr = (lr + 1); hrec_count--; lr = hr, hr++) { - char workdir[PATH_MAX], repos[PATH_MAX]; + char *workdir; + char *repos; if (!hrec_count) hr = NULL; @@ -1360,19 +1489,19 @@ report_hrecs () continue; ty = *(lr->type); -#ifdef HAVE_RCS5 if (!tz_local) { time_t t = lr->date + tz_seconds_east_of_GMT; tm = gmtime (&t); } else -#endif - tm = localtime (&(lr->date)); - (void) printf ("%c %02d/%02d %02d:%02d %s %-*s", ty, tm->tm_mon + 1, - tm->tm_mday, tm->tm_hour, tm->tm_min, tz_name, - user_len, lr->user); + tm = localtime (&(lr->date)); + + (void) printf ("%c %04d-%02d-%02d %02d:%02d %s %-*s", ty, + tm->tm_year+1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, + tm->tm_min, tz_name, user_len, lr->user); + workdir = xmalloc (strlen (lr->dir) + strlen (lr->end) + 10); (void) sprintf (workdir, "%s%s", lr->dir, lr->end); if ((cp = strrchr (workdir, '/')) != NULL) { @@ -1381,6 +1510,7 @@ report_hrecs () (void) strcpy (cp, "*"); } } + repos = xmalloc (strlen (lr->repos) + 10); (void) strcpy (repos, lr->repos); if ((cp = strrchr (repos, '/')) != NULL) { @@ -1400,11 +1530,13 @@ report_hrecs () (void) printf (" {%s}", workdir); break; case 'F': + case 'E': case 'O': if (lr->rev && *(lr->rev)) (void) printf (" [%s]", lr->rev); (void) printf (" %-*s =%s%-*s %s", repos_len, repos, lr->mod, - mod_len + 1 - strlen (lr->mod), "=", workdir); + mod_len + 1 - (int) strlen (lr->mod), + "=", workdir); break; case 'W': case 'U': @@ -1422,6 +1554,8 @@ report_hrecs () break; } (void) putchar ('\n'); + free (workdir); + free (repos); } } diff --git a/gnu/usr.bin/cvs/src/server.c b/gnu/usr.bin/cvs/src/server.c index 6b9e1f1678c..694dfb056e1 100644 --- a/gnu/usr.bin/cvs/src/server.c +++ b/gnu/usr.bin/cvs/src/server.c @@ -930,7 +930,7 @@ serve_max_dotdot (arg) int i; char *p; - if (lim < 0) + if (lim < 0 || lim > 10000) return; p = malloc (strlen (server_temp_dir) + 2 * lim + 10); if (p == NULL) @@ -1635,8 +1635,7 @@ serve_unchanged (arg) char *cp; char *timefield; - if (error_pending ()) - return; + if (error_pending ()) return; if (outside_dir (arg)) return; @@ -1650,7 +1649,16 @@ serve_unchanged (arg) && strlen (arg) == cp - name && strncmp (arg, name, cp - name) == 0) { - timefield = strchr (cp + 1, '/') + 1; + if (!(timefield = strchr (cp + 1, '/')) || *++timefield == '\0') + { + /* We didn't find the record separator or it is followed by + * the end of the string, so just exit. + */ + if (alloc_pending (80)) + sprintf (pending_error_text, + "E Malformed Entry encountered."); + return; + } /* If the time field is not currently empty, then one of * serve_modified, serve_is_modified, & serve_unchanged were * already called for this file. We would like to ignore the @@ -1697,8 +1705,7 @@ serve_is_modified (arg) /* Have we found this file in "entries" yet. */ int found; - if (error_pending ()) - return; + if (error_pending ()) return; if (outside_dir (arg)) return; @@ -1713,7 +1720,16 @@ serve_is_modified (arg) && strlen (arg) == cp - name && strncmp (arg, name, cp - name) == 0) { - timefield = strchr (cp + 1, '/') + 1; + if (!(timefield = strchr (cp + 1, '/')) || *++timefield == '\0') + { + /* We didn't find the record separator or it is followed by + * the end of the string, so just exit. + */ + if (alloc_pending (80)) + sprintf (pending_error_text, + "E Malformed Entry encountered."); + return; + } /* If the time field is not currently empty, then one of * serve_modified, serve_is_modified, & serve_unchanged were * already called for this file. We would like to ignore the @@ -1798,8 +1814,29 @@ serve_entry (arg) { struct an_entry *p; char *cp; + int i = 0; if (error_pending()) return; - p = (struct an_entry *) malloc (sizeof (struct an_entry)); + + /* Verify that the entry is well-formed. This can avoid problems later. + * At the moment we only check that the Entry contains five slashes in + * approximately the correct locations since some of the code makes + * assumptions about this. + */ + cp = arg; + if (*cp == 'D') cp++; + while (i++ < 5) + { + if (!cp || *cp != '/') + { + if (alloc_pending (80)) + sprintf (pending_error_text, + "E protocol error: Malformed Entry"); + return; + } + cp = strchr (cp + 1, '/'); + } + + p = xmalloc (sizeof (struct an_entry)); if (p == NULL) { pending_error = ENOMEM; @@ -2031,6 +2068,9 @@ serve_notify (arg) { char *cp; + if (!data[0]) + goto error; + if (strchr (data, '+')) goto error; @@ -2162,6 +2202,15 @@ serve_argument (arg) char *p; if (error_pending()) return; + + if (argument_count >= 10000) + { + if (alloc_pending (80)) + sprintf (pending_error_text, + "E Protocol error: too many arguments"); + return; + } + if (argument_vector_size <= argument_count) { @@ -2192,6 +2241,15 @@ serve_argumentx (arg) char *p; if (error_pending()) return; + + if (argument_count <= 1) + { + if (alloc_pending (80)) + sprintf (pending_error_text, + "E Protocol error: called argumentx without prior call to argument"); + return; + } + p = argument_vector[argument_count - 1]; p = realloc (p, strlen (p) + 1 + strlen (arg) + 1); @@ -2549,7 +2607,7 @@ check_command_legal_p (cmd_name) save some code here... -kff */ /* Chop newline by hand, for strcmp()'s sake. */ - if (linebuf[num_red - 1] == '\n') + if (num_red > 0 && linebuf[num_red - 1] == '\n') linebuf[num_red - 1] = '\0'; if (strcmp (linebuf, CVS_Username) == 0) @@ -2604,7 +2662,7 @@ check_command_legal_p (cmd_name) while ((num_red = getline (&linebuf, &linebuf_len, fp)) >= 0) { /* Chop newline by hand, for strcmp()'s sake. */ - if (linebuf[num_red - 1] == '\n') + if (num_red > 0 && linebuf[num_red - 1] == '\n') linebuf[num_red - 1] = '\0'; if (strcmp (linebuf, CVS_Username) == 0) diff --git a/gnu/usr.bin/cvs/src/wrapper.c b/gnu/usr.bin/cvs/src/wrapper.c index 2aafb2f69ca..7dbaa5538a4 100644 --- a/gnu/usr.bin/cvs/src/wrapper.c +++ b/gnu/usr.bin/cvs/src/wrapper.c @@ -1,4 +1,15 @@ +/* This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. */ + #include "cvs.h" +#include "getline.h" /* Original Author: athan@morgan.com <Andrew C. Athan> 2/1/94 @@ -22,6 +33,7 @@ -f from cvs filter value: path to filter -t to cvs filter value: path to filter -m update methodology value: MERGE or COPY + -k default -k rcs option to use on import or add and value is a single-quote delimited value. @@ -34,7 +46,7 @@ typedef struct { char *wildCard; char *tocvsFilter; char *fromcvsFilter; - char *conflictHook; + char *rcsOption; WrapMergeMethod mergeMethod; } WrapperEntry; @@ -44,7 +56,21 @@ static WrapperEntry **wrap_saved_list=NULL; static int wrap_size=0; static int wrap_count=0; static int wrap_tempcount=0; + +/* FIXME: the relationship between wrap_count, wrap_tempcount, + * wrap_saved_count, and wrap_saved_tempcount is not entirely clear; + * it is certainly suspicious that wrap_saved_count is never set to a + * value other than zero! If the variable isn't being used, it should + * be removed. And in general, we should describe how temporary + * vs. permanent wrappers are implemented, and then make sure the + * implementation is actually doing that. + * + * Right now things seem to be working, but that's no guarantee there + * isn't a bug lurking somewhere in the murk. + */ + static int wrap_saved_count=0; + static int wrap_saved_tempcount=0; #define WRAPPER_GROW 8 @@ -58,27 +84,191 @@ void wrap_restore_saved PROTO((void)); void wrap_setup() { - char file[PATH_MAX]; - struct passwd *pw; - - /* Then add entries found in repository, if it exists */ - (void) sprintf (file, "%s/%s/%s", CVSroot, CVSROOTADM, CVSROOTADM_WRAPPER); - if (isfile (file)){ - wrap_add_file(file,0); + /* FIXME-reentrancy: if we do a multithreaded server, will need to + move this to a per-connection data structure, or better yet + think about a cleaner solution. */ + static int wrap_setup_already_done = 0; + char *homedir; + + if (wrap_setup_already_done != 0) + return; + else + wrap_setup_already_done = 1; + +#ifdef CLIENT_SUPPORT + if (!current_parsed_root->isremote) +#endif + { + char *file; + + file = xmalloc (strlen (current_parsed_root->directory) + + sizeof (CVSROOTADM) + + sizeof (CVSROOTADM_WRAPPER) + + 3); + /* Then add entries found in repository, if it exists. */ + (void) sprintf (file, "%s/%s/%s", current_parsed_root->directory, CVSROOTADM, + CVSROOTADM_WRAPPER); + if (isfile (file)) + { + wrap_add_file(file,0); + } + free (file); } - /* Then add entries found in home dir, (if user has one) and file exists */ - if ((pw = (struct passwd *) getpwuid (getuid ())) && pw->pw_dir){ - (void) sprintf (file, "%s/%s", pw->pw_dir, CVSDOTWRAPPER); - if (isfile (file)){ + /* Then add entries found in home dir, (if user has one) and file + exists. */ + homedir = get_homedir (); + /* If we can't find a home directory, ignore ~/.cvswrappers. This may + make tracking down problems a bit of a pain, but on the other + hand it might be obnoxious to complain when CVS will function + just fine without .cvswrappers (and many users won't even know what + .cvswrappers is). */ + if (homedir != NULL) + { + char *file; + + file = xmalloc (strlen (homedir) + sizeof (CVSDOTWRAPPER) + 10); + (void) sprintf (file, "%s/%s", homedir, CVSDOTWRAPPER); + if (isfile (file)) + { wrap_add_file (file, 0); } + free (file); } - /* Then add entries found in CVSWRAPPERS environment variable. */ + /* FIXME: calling wrap_add() below implies that the CVSWRAPPERS + * environment variable contains exactly one "wrapper" -- a line + * of the form + * + * FILENAME_PATTERN FLAG OPTS [ FLAG OPTS ...] + * + * This may disagree with the documentation, which states: + * + * `$CVSWRAPPERS' + * A whitespace-separated list of file name patterns that CVS + * should treat as wrappers. *Note Wrappers::. + * + * Does this mean the environment variable can hold multiple + * wrappers lines? If so, a single call to wrap_add() is + * insufficient. + */ + + /* Then add entries found in CVSWRAPPERS environment variable. */ wrap_add (getenv (WRAPPER_ENV), 0); } +#ifdef CLIENT_SUPPORT +/* Send -W arguments for the wrappers to the server. The command must + be one that accepts them (e.g. update, import). */ +void +wrap_send () +{ + int i; + + for (i = 0; i < wrap_count + wrap_tempcount; ++i) + { + if (wrap_list[i]->tocvsFilter != NULL + || wrap_list[i]->fromcvsFilter != NULL) + /* For greater studliness we would print the offending option + and (more importantly) where we found it. */ + error (0, 0, "\ +-t and -f wrapper options are not supported remotely; ignored"); + if (wrap_list[i]->mergeMethod == WRAP_COPY) + /* For greater studliness we would print the offending option + and (more importantly) where we found it. */ + error (0, 0, "\ +-m wrapper option is not supported remotely; ignored"); + if (wrap_list[i]->rcsOption != NULL) + { + send_to_server ("Argument -W\012Argument ", 0); + send_to_server (wrap_list[i]->wildCard, 0); + send_to_server (" -k '", 0); + send_to_server (wrap_list[i]->rcsOption, 0); + send_to_server ("'\012", 0); + } + } +} +#endif /* CLIENT_SUPPORT */ + +#if defined(SERVER_SUPPORT) || defined(CLIENT_SUPPORT) +/* Output wrapper entries in the format of cvswrappers lines. + * + * This is useful when one side of a client/server connection wants to + * send its wrappers to the other; since the receiving side would like + * to use wrap_add() to incorporate the wrapper, it's best if the + * entry arrives in this format. + * + * The entries are stored in `line', which is allocated here. Caller + * can free() it. + * + * If first_call_p is nonzero, then start afresh. */ +void +wrap_unparse_rcs_options (line, first_call_p) + char **line; + int first_call_p; +{ + /* FIXME-reentrancy: we should design a reentrant interface, like + a callback which gets handed each wrapper (a multithreaded + server being the most concrete reason for this, but the + non-reentrant interface is fairly unnecessary/ugly). */ + static int i; + + if (first_call_p) + i = 0; + + for (; i < wrap_count + wrap_tempcount; ++i) + { + if (wrap_list[i]->rcsOption != NULL) + { + *line = xmalloc (strlen (wrap_list[i]->wildCard) + + strlen ("\t") + + strlen (" -k '") + + strlen (wrap_list[i]->rcsOption) + + strlen ("'") + + 1); /* leave room for '\0' */ + + strcpy (*line, wrap_list[i]->wildCard); + strcat (*line, " -k '"); + strcat (*line, wrap_list[i]->rcsOption); + strcat (*line, "'"); + + /* We're going to miss the increment because we return, so + do it by hand. */ + ++i; + + return; + } + } + + *line = NULL; + return; +} +#endif /* SERVER_SUPPORT || CLIENT_SUPPORT */ + +/* + * Remove fmt str specifier other than %% or %s. And allow + * only max_s %s specifiers + */ +wrap_clean_fmt_str(char *fmt, int max_s) +{ + while (*fmt) { + if (fmt[0] == '%' && fmt[1]) + { + if (fmt[1] == '%') + fmt++; + else + if (fmt[1] == 's' && max_s > 0) + { + max_s--; + fmt++; + } else + *fmt = ' '; + } + fmt++; + } + return; +} + /* * Open a file and read lines, feeding each line to a line parser. Arrange * for keeping a temporary list of wrappers at the end, if the "temp" @@ -90,17 +280,28 @@ wrap_add_file (file, temp) int temp; { FILE *fp; - char line[1024]; - - wrap_restore_saved(); - wrap_kill_temp(); - - /* load the file */ - if (!(fp = fopen (file, "r"))) + char *line = NULL; + size_t line_allocated = 0; + + wrap_restore_saved (); + wrap_kill_temp (); + + /* Load the file. */ + fp = CVS_FOPEN (file, "r"); + if (fp == NULL) + { + if (!existence_error (errno)) + error (0, errno, "cannot open %s", file); return; - while (fgets (line, sizeof (line), fp)) + } + while (getline (&line, &line_allocated, fp) >= 0) wrap_add (line, temp); - (void) fclose (fp); + if (line) + free (line); + if (ferror (fp)) + error (0, errno, "cannot read %s", file); + if (fclose (fp) == EOF) + error (0, errno, "cannot close %s", file); } void @@ -132,13 +333,13 @@ void wrap_free_entry_internal(e) WrapperEntry *e; { - free(e->wildCard); - if(e->tocvsFilter) - free(e->tocvsFilter); - if(e->fromcvsFilter) - free(e->fromcvsFilter); - if(e->conflictHook) - free(e->conflictHook); + free (e->wildCard); + if (e->tocvsFilter) + free (e->tocvsFilter); + if (e->fromcvsFilter) + free (e->fromcvsFilter); + if (e->rcsOption) + free (e->rcsOption); } void @@ -176,9 +377,11 @@ wrap_add (line, isTemp) memset (&e, 0, sizeof(e)); /* Search for the wild card */ - while(*line && isspace(*line)) + while (*line && isspace ((unsigned char) *line)) ++line; - for(temp=line;*line && !isspace(*line);++line) + for (temp = line; + *line && !isspace ((unsigned char) *line); + ++line) ; if(temp==line) return; @@ -208,26 +411,47 @@ wrap_add (line, isTemp) for(temp=++line;*line && (*line!='\'' || line[-1]=='\\');++line) ; - if(line==temp+1) + /* This used to "break;" (ignore the option) if there was a + single character between the single quotes (I'm guessing + that was accidental). Now it "break;"s if there are no + characters. I'm not sure either behavior is particularly + necessary--the current options might not require '' + arguments, but surely some future option legitimately + might. Also I'm not sure that ignoring the option is a + swift way to handle syntax errors in general. */ + if (line==temp) break; ctemp=*line; *line='\0'; switch(opt){ case 'f': + /* Before this is reenabled, need to address the problem in + commit.c (see http://www.cyclic.com/cvs/dev-wrap.txt). */ + error (1, 0, + "-t/-f wrappers not supported by this version of CVS"); + if(e.fromcvsFilter) free(e.fromcvsFilter); - e.fromcvsFilter=xstrdup(temp); + /* FIXME: error message should say where the bad value + came from. */ + e.fromcvsFilter=expand_path (temp, "<wrapper>", 0); + if (!e.fromcvsFilter) + error (1, 0, "Correct above errors first"); break; case 't': + /* Before this is reenabled, need to address the problem in + commit.c (see http://www.cyclic.com/cvs/dev-wrap.txt). */ + error (1, 0, + "-t/-f wrappers not supported by this version of CVS"); + if(e.tocvsFilter) free(e.tocvsFilter); - e.tocvsFilter=xstrdup(temp); - break; - case 'c': - if(e.conflictHook) - free(e.conflictHook); - e.conflictHook=xstrdup(temp); + /* FIXME: error message should say where the bad value + came from. */ + e.tocvsFilter=expand_path (temp, "<wrapper>", 0); + if (!e.tocvsFilter) + error (1, 0, "Correct above errors first"); break; case 'm': if(*temp=='C' || *temp=='c') @@ -235,6 +459,11 @@ wrap_add (line, isTemp) else e.mergeMethod=WRAP_MERGE; break; + case 'k': + if (e.rcsOption) + free (e.rcsOption); + e.rcsOption = xstrdup (temp); + break; default: break; } @@ -269,8 +498,8 @@ wrap_add_entry(e, temp) wrap_list[x]->wildCard=e->wildCard; wrap_list[x]->fromcvsFilter=e->fromcvsFilter; wrap_list[x]->tocvsFilter=e->tocvsFilter; - wrap_list[x]->conflictHook=e->conflictHook; wrap_list[x]->mergeMethod=e->mergeMethod; + wrap_list[x]->rcsOption = e->rcsOption; } /* Return 1 if the given filename is a wrapper filename */ @@ -279,11 +508,11 @@ wrap_name_has (name,has) const char *name; WrapMergeHas has; { - int x,count=wrap_count+wrap_saved_count; + int x,count=wrap_count+wrap_tempcount; char *temp; for(x=0;x<count;++x) - if (fnmatch (wrap_list[x]->wildCard, name, 0) == 0){ + if (CVS_FNMATCH (wrap_list[x]->wildCard, name, 0) == 0){ switch(has){ case WRAP_TOCVS: temp=wrap_list[x]->tocvsFilter; @@ -291,8 +520,8 @@ wrap_name_has (name,has) case WRAP_FROMCVS: temp=wrap_list[x]->fromcvsFilter; break; - case WRAP_CONFLICT: - temp=wrap_list[x]->conflictHook; + case WRAP_RCSOPTION: + temp = wrap_list[x]->rcsOption; break; default: abort (); @@ -305,32 +534,71 @@ wrap_name_has (name,has) return (0); } -WrapperEntry * +static WrapperEntry *wrap_matching_entry PROTO ((const char *)); + +static WrapperEntry * wrap_matching_entry (name) const char *name; { - int x,count=wrap_count+wrap_saved_count; + int x,count=wrap_count+wrap_tempcount; for(x=0;x<count;++x) - if (fnmatch (wrap_list[x]->wildCard, name, 0) == 0) + if (CVS_FNMATCH (wrap_list[x]->wildCard, name, 0) == 0) return wrap_list[x]; return (WrapperEntry *)NULL; } +/* Return the RCS options for FILENAME in a newly malloc'd string. If + ASFLAG, then include "-k" at the beginning (e.g. "-kb"), otherwise + just give the option itself (e.g. "b"). */ +char * +wrap_rcsoption (filename, asflag) + const char *filename; + int asflag; +{ + WrapperEntry *e = wrap_matching_entry (filename); + char *buf; + + if (e == NULL || e->rcsOption == NULL || (*e->rcsOption == '\0')) + return NULL; + + buf = xmalloc (strlen (e->rcsOption) + 3); + if (asflag) + { + strcpy (buf, "-k"); + strcat (buf, e->rcsOption); + } + else + { + strcpy (buf, e->rcsOption); + } + return buf; +} + char * wrap_tocvs_process_file(fileName) const char *fileName; { WrapperEntry *e=wrap_matching_entry(fileName); - static char buf[L_tmpnam+1]; + static char *buf = NULL; + char *args; if(e==NULL || e->tocvsFilter==NULL) return NULL; - tmpnam(buf); + if (buf != NULL) + free (buf); + buf = cvs_temp_name (); + + args = xmalloc (strlen (e->tocvsFilter) + + strlen (fileName) + + strlen (buf)); - run_setup(e->tocvsFilter,fileName,buf); + wrap_clean_fmt_str(e->tocvsFilter, 2); + sprintf (args, e->tocvsFilter, fileName, buf); + run_setup (args); run_exec(RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL|RUN_REALLY ); + free (args); return buf; } @@ -346,17 +614,23 @@ wrap_merge_is_copy (fileName) return 1; } -char * +void wrap_fromcvs_process_file(fileName) const char *fileName; { + char *args; WrapperEntry *e=wrap_matching_entry(fileName); - static char buf[PATH_MAX]; if(e==NULL || e->fromcvsFilter==NULL) - return NULL; + return; + + args = xmalloc (strlen (e->fromcvsFilter) + + strlen (fileName)); - run_setup(e->fromcvsFilter,fileName); + wrap_clean_fmt_str(e->fromcvsFilter, 1); + sprintf (args, e->fromcvsFilter, fileName); + run_setup (args); run_exec(RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL ); - return buf; + free (args); + return; } |