summaryrefslogtreecommitdiff
path: root/gnu
diff options
context:
space:
mode:
authorOtto Moerbeek <otto@cvs.openbsd.org>2004-06-09 18:16:06 +0000
committerOtto Moerbeek <otto@cvs.openbsd.org>2004-06-09 18:16:06 +0000
commit452f0738fff320dd7af70f5c39030b643226878a (patch)
treed2102647d5ab8d50bf2e8ecc9c241a52717279fb /gnu
parentc238b270c67623d00936cd77a1e88e61fc260eb2 (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.h108
-rw-r--r--gnu/usr.bin/cvs/src/commit.c7
-rw-r--r--gnu/usr.bin/cvs/src/cvs.h4
-rw-r--r--gnu/usr.bin/cvs/src/filesubr.c8
-rw-r--r--gnu/usr.bin/cvs/src/history.c536
-rw-r--r--gnu/usr.bin/cvs/src/server.c78
-rw-r--r--gnu/usr.bin/cvs/src/wrapper.c384
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;
}