From 98556d4ac9e1cc382f4da894a69bf8f381a1d38d Mon Sep 17 00:00:00 2001 From: "Todd C. Miller" Date: Fri, 12 Dec 2003 14:43:57 +0000 Subject: Add a check for absolute pathnames; from cvs 1.11.10; deraadt@ OK --- gnu/usr.bin/cvs/src/modules.c | 677 ++++++++++++++++++++++++++++-------------- 1 file changed, 447 insertions(+), 230 deletions(-) diff --git a/gnu/usr.bin/cvs/src/modules.c b/gnu/usr.bin/cvs/src/modules.c index 9dcce131473..a4658815d24 100644 --- a/gnu/usr.bin/cvs/src/modules.c +++ b/gnu/usr.bin/cvs/src/modules.c @@ -3,7 +3,7 @@ * Copyright (c) 1989-1992, Brian Berliner * * You may distribute under the terms of the GNU General Public License - * as specified in the README file that comes with the CVS 1.4 kit. + * as specified in the README file that comes with the CVS source distribution. * * Modules * @@ -20,18 +20,32 @@ * command line. */ +#include #include "cvs.h" -#include "save-cwd.h" +#include "savecwd.h" -#ifndef lint -static const char rcsid[] = "$CVSid: @(#)modules.c 1.62 94/09/29 $"; -USE(rcsid); -#endif + +/* Defines related to the syntax of the modules file. */ + +/* Options in modules file. Note that it is OK to use GNU getopt features; + we already are arranging to make sure we are using the getopt distributed + with CVS. */ +#define CVSMODULE_OPTS "+ad:i:lo:e:s:t:u:" +/* Special delimiter. */ +#define CVSMODULE_SPEC '&' + struct sortrec { + /* Name of the module, malloc'd. */ char *modname; + /* If Status variable is set, this is either def_status or the malloc'd + name of the status. If Status is not set, the field is left + uninitialized. */ char *status; + /* Pointer to a malloc'd array which contains (1) the raw contents + of the options and arguments, excluding comments, (2) a '\0', + and (3) the storage for the "comment" field. */ char *rest; char *comment; }; @@ -48,17 +62,22 @@ static void save_d PROTO((char *k, int ks, char *d, int ds)); DBM * open_module () { - char mfile[PATH_MAX]; + char *mfile; + DBM *retval; - if (CVSroot == NULL) + if (current_parsed_root == NULL) { - (void) fprintf (stderr, - "%s: must set the CVSROOT environment variable\n", - program_name); - error (1, 0, "or specify the '-d' option to %s", program_name); + error (0, 0, "must set the CVSROOT environment variable"); + error (1, 0, "or specify the '-d' global option"); } - (void) sprintf (mfile, "%s/%s/%s", CVSroot, CVSROOTADM, CVSROOTADM_MODULES); - return (dbm_open (mfile, O_RDONLY, 0666)); + mfile = xmalloc (strlen (current_parsed_root->directory) + + sizeof (CVSROOTADM) + + sizeof (CVSROOTADM_MODULES) + 3); + (void) sprintf (mfile, "%s/%s/%s", current_parsed_root->directory, + CVSROOTADM, CVSROOTADM_MODULES); + retval = dbm_open (mfile, O_RDONLY, 0666); + free (mfile); + return retval; } /* @@ -78,17 +97,18 @@ close_module (db) * It runs the post checkout or post tag proc from the modules file */ int -do_module (db, mname, m_type, msg, callback_proc, where, - shorten, local_specified, run_module_prog, extra_arg) +do_module (db, mname, m_type, msg, callback_proc, where, shorten, + local_specified, run_module_prog, build_dirs, extra_arg) DBM *db; char *mname; enum mtype m_type; char *msg; - int (*callback_proc) (); + CALLBACKPROC callback_proc; char *where; int shorten; int local_specified; int run_module_prog; + int build_dirs; char *extra_arg; { char *checkin_prog = NULL; @@ -97,43 +117,68 @@ do_module (db, mname, m_type, msg, callback_proc, where, char *tag_prog = NULL; char *update_prog = NULL; struct saved_cwd cwd; - char line[MAXLINELEN]; - char *xmodargv[MAXFILEPERDIR]; + int cwd_saved = 0; + char *line; + int modargc; + int xmodargc; char **modargv; - char *value; - char *zvalue; + char **xmodargv = NULL; + /* Found entry from modules file, including options and such. */ + char *value = NULL; char *mwhere = NULL; char *mfile = NULL; char *spec_opt = NULL; - char xvalue[PATH_MAX]; - int modargc, alias = 0; + char *xvalue = NULL; + int alias = 0; datum key, val; char *cp; int c, err = 0; + int nonalias_opt = 0; #ifdef SERVER_SUPPORT + int restore_server_dir = 0; + char *server_dir_to_restore = NULL; if (trace) - { - fprintf (stderr, "%c-> do_module (%s, %s, %s, %s)\n", - (server_active) ? 'S' : ' ', - mname, msg, where ? where : "", - extra_arg ? extra_arg : ""); - } + { + char *buf; + + /* We use cvs_outerr, rather than fprintf to stderr, because + this may be called by server code with error_use_protocol + set. */ + buf = xmalloc (100 + + strlen (mname) + + strlen (msg) + + (where ? strlen (where) : 0) + + (extra_arg ? strlen (extra_arg) : 0)); + sprintf (buf, "%s-> do_module (%s, %s, %s, %s)\n", + CLIENT_SERVER_STR, + mname, msg, where ? where : "", + extra_arg ? extra_arg : ""); + cvs_outerr (buf, 0); + free (buf); + } #endif - /* remember where we start */ - if (save_cwd (&cwd)) - exit (1); + /* Don't process absolute directories. Anything else could be a security + * problem. Before this check was put in place: + * + * $ cvs -d:fork:/cvsroot co /foo + * cvs server: warning: cannot make directory CVS in /: Permission denied + * cvs [server aborted]: cannot make directory /foo: Permission denied + * $ + */ + if (isabsolute (mname)) + error (1, 0, "Absolute module reference invalid: `%s'", mname); /* if this is a directory to ignore, add it to that list */ if (mname[0] == '!' && mname[1] != '\0') { ign_dir_add (mname+1); - return(err); + goto do_module_return; } /* strip extra stuff from the module name */ - strip_path (mname); + strip_trailing_slashes (mname); /* * Look up the module using the following scheme: @@ -153,52 +198,55 @@ do_module (db, mname, m_type, msg, callback_proc, where, val.dptr = NULL; if (val.dptr != NULL) { - /* null terminate the value XXX - is this space ours? */ - val.dptr[val.dsize] = '\0'; + /* copy and null terminate the value */ + value = xmalloc (val.dsize + 1); + memcpy (value, val.dptr, val.dsize); + value[val.dsize] = '\0'; /* If the line ends in a comment, strip it off */ - if ((cp = strchr (val.dptr, '#')) != NULL) - { - do - *cp-- = '\0'; - while (isspace (*cp)); - } + if ((cp = strchr (value, '#')) != NULL) + *cp = '\0'; else - { - /* Always strip trailing spaces */ - cp = strchr (val.dptr, '\0'); - while (cp > val.dptr && isspace(*--cp)) - *cp = '\0'; - } + cp = value + val.dsize; + + /* Always strip trailing spaces */ + while (cp > value && isspace ((unsigned char) *--cp)) + *cp = '\0'; - value = val.dptr; mwhere = xstrdup (mname); goto found; } else { - char file[PATH_MAX]; - char attic_file[PATH_MAX]; + char *file; + char *attic_file; char *acp; + int is_found = 0; /* check to see if mname is a directory or file */ - - (void) sprintf (file, "%s/%s", CVSroot, mname); + file = xmalloc (strlen (current_parsed_root->directory) + + strlen (mname) + sizeof(RCSEXT) + 2); + (void) sprintf (file, "%s/%s", current_parsed_root->directory, mname); + attic_file = xmalloc (strlen (current_parsed_root->directory) + + strlen (mname) + + sizeof (CVSATTIC) + sizeof (RCSEXT) + 3); if ((acp = strrchr (mname, '/')) != NULL) { *acp = '\0'; - (void) sprintf (attic_file, "%s/%s/%s/%s%s", CVSroot, mname, - CVSATTIC, acp + 1, RCSEXT); + (void) sprintf (attic_file, "%s/%s/%s/%s%s", current_parsed_root->directory, + mname, CVSATTIC, acp + 1, RCSEXT); *acp = '/'; } else - (void) sprintf (attic_file, "%s/%s/%s%s", CVSroot, CVSATTIC, - mname, RCSEXT); + (void) sprintf (attic_file, "%s/%s/%s%s", current_parsed_root->directory, + CVSATTIC, mname, RCSEXT); if (isdir (file)) { - value = mname; - goto found; + modargv = xmalloc (sizeof (*modargv)); + modargv[0] = xstrdup (mname); + modargc = 1; + is_found = 1; } else { @@ -208,12 +256,12 @@ do_module (db, mname, m_type, msg, callback_proc, where, /* if mname was a file, we have to split it into "dir file" */ if ((cp = strrchr (mname, '/')) != NULL && cp != mname) { - char *slashp; - - /* put the ' ' in a copy so we don't mess up the original */ - value = strcpy (xvalue, mname); - slashp = strrchr (value, '/'); - *slashp = ' '; + modargv = xmalloc (2 * sizeof (*modargv)); + modargv[0] = xmalloc (strlen (mname) + 2); + strncpy (modargv[0], mname, cp - mname); + modargv[0][cp - mname] = '\0'; + modargv[1] = xstrdup (cp + 1); + modargc = 2; } else { @@ -225,19 +273,54 @@ do_module (db, mname, m_type, msg, callback_proc, where, if (cp == mname) { /* drop the leading / if specified */ - value = strcpy (xvalue, ". "); - (void) strcat (xvalue, mname + 1); + modargv = xmalloc (2 * sizeof (*modargv)); + modargv[0] = xstrdup ("."); + modargv[1] = xstrdup (mname + 1); + modargc = 2; } else { /* otherwise just copy it */ - value = strcpy (xvalue, ". "); - (void) strcat (xvalue, mname); + modargv = xmalloc (2 * sizeof (*modargv)); + modargv[0] = xstrdup ("."); + modargv[1] = xstrdup (mname); + modargc = 2; } } - goto found; + is_found = 1; } } + free (attic_file); + free (file); + + if (is_found) + { + assert (value == NULL); + + /* OK, we have now set up modargv with the actual + file/directory we want to work on. We duplicate a + small amount of code here because the vast majority of + the code after the "found" label does not pertain to + the case where we found a file/directory rather than + finding an entry in the modules file. */ + if (save_cwd (&cwd)) + error_exit (); + cwd_saved = 1; + + err += callback_proc (modargc, modargv, where, mwhere, mfile, + shorten, + local_specified, mname, msg); + + free_names (&modargc, modargv); + + /* cd back to where we started. */ + if (restore_cwd (&cwd, NULL)) + error_exit (); + free_cwd (&cwd); + cwd_saved = 0; + + goto do_module_return; + } } /* look up everything to the first / as a module */ @@ -259,17 +342,20 @@ do_module (db, mname, m_type, msg, callback_proc, where, { char *cp2; - /* null terminate the value XXX - is this space ours? */ - val.dptr[val.dsize] = '\0'; + /* copy and null terminate the value */ + value = xmalloc (val.dsize + 1); + memcpy (value, val.dptr, val.dsize); + value[val.dsize] = '\0'; /* If the line ends in a comment, strip it off */ - if ((cp2 = strchr (val.dptr, '#')) != NULL) - { - do - *cp2-- = '\0'; - while (isspace (*cp2)); - } - value = val.dptr; + if ((cp2 = strchr (value, '#')) != NULL) + *cp2 = '\0'; + else + cp2 = value + val.dsize; + + /* Always strip trailing spaces */ + while (cp2 > value && isspace ((unsigned char) *--cp2)) + *cp2 = '\0'; /* mwhere gets just the module name */ mwhere = xstrdup (mname); @@ -288,9 +374,7 @@ do_module (db, mname, m_type, msg, callback_proc, where, /* if we got here, we couldn't find it using our search, so give up */ error (0, 0, "cannot find module `%s' - ignored", mname); err++; - if (mwhere) - free (mwhere); - return (err); + goto do_module_return; /* @@ -299,10 +383,12 @@ do_module (db, mname, m_type, msg, callback_proc, where, */ found: - /* copy value to our own string since if we go recursive we'll be - really screwed if we do another dbm lookup */ - zvalue = xstrdup (value); - value = zvalue; + /* remember where we start */ + if (save_cwd (&cwd)) + error_exit (); + cwd_saved = 1; + + assert (value != NULL); /* search the value for the special delimiter and save for later */ if ((cp = strchr (value, CVSMODULE_SPEC)) != NULL) @@ -310,66 +396,9 @@ do_module (db, mname, m_type, msg, callback_proc, where, *cp = '\0'; /* null out the special char */ spec_opt = cp + 1; /* save the options for later */ - if (cp != value) /* strip whitespace if necessary */ - while (isspace (*--cp)) - *cp = '\0'; - - if (cp == value) - { - /* - * we had nothing but special options, so skip arg - * parsing and regular stuff entirely - * - * If there were only special ones though, we must - * make the appropriate directory and cd to it - */ - char *dir; - - /* XXX - XXX - MAJOR HACK - DO NOT SHIP - this needs to - be !pipeout, but we don't know that here yet */ - if (!run_module_prog) - goto out; - - dir = where ? where : mname; - /* XXX - think about making null repositories at each dir here - instead of just at the bottom */ - make_directories (dir); - if (chdir (dir) < 0) - { - error (0, errno, "cannot chdir to %s", dir); - spec_opt = NULL; - err++; - goto out; - } - if (!isfile (CVSADM)) - { - char nullrepos[PATH_MAX]; - - (void) sprintf (nullrepos, "%s/%s/%s", CVSroot, - CVSROOTADM, CVSNULLREPOS); - if (!isfile (nullrepos)) - (void) CVS_MKDIR (nullrepos, 0777); - if (!isdir (nullrepos)) - error (1, 0, "there is no repository %s", nullrepos); - - Create_Admin (".", dir, - nullrepos, (char *) NULL, (char *) NULL); - if (!noexec) - { - FILE *fp; - - fp = open_file (CVSADM_ENTSTAT, "w+"); - if (fclose (fp) == EOF) - error (1, errno, "cannot close %s", CVSADM_ENTSTAT); -#ifdef SERVER_SUPPORT - if (server_active) - server_set_entstat (dir, nullrepos); -#endif - } - } - out: - goto do_special; - } + /* strip whitespace if necessary */ + while (cp > value && isspace ((unsigned char) *--cp)) + *cp = '\0'; } /* don't do special options only part of a module was specified */ @@ -386,14 +415,18 @@ do_module (db, mname, m_type, msg, callback_proc, where, */ /* Put the value on a line with XXX prepended for getopt to eat */ - (void) sprintf (line, "%s %s", "XXX", value); + line = xmalloc (strlen (value) + 5); + strcpy(line, "XXX "); + strcpy(line + 4, value); /* turn the line into an argv[] array */ - line2argv (&modargc, xmodargv, line); + line2argv (&xmodargc, &xmodargv, line, " \t"); + free (line); + modargc = xmodargc; modargv = xmodargv; /* parse the args */ - optind = 1; + optind = 0; while ((c = getopt (modargc, modargv, CVSMODULE_OPTS)) != -1) { switch (c) @@ -405,44 +438,69 @@ do_module (db, mname, m_type, msg, callback_proc, where, if (mwhere) free (mwhere); mwhere = xstrdup (optarg); + nonalias_opt = 1; break; case 'i': - checkin_prog = optarg; + if (checkin_prog) + free (checkin_prog); + checkin_prog = xstrdup (optarg); + nonalias_opt = 1; break; case 'l': local_specified = 1; + nonalias_opt = 1; + break; case 'o': - checkout_prog = optarg; + if (checkout_prog) + free (checkout_prog); + checkout_prog = xstrdup (optarg); + nonalias_opt = 1; break; case 'e': - export_prog = optarg; + if (export_prog) + free (export_prog); + export_prog = xstrdup (optarg); + nonalias_opt = 1; break; case 't': - tag_prog = optarg; + if (tag_prog) + free (tag_prog); + tag_prog = xstrdup (optarg); + nonalias_opt = 1; break; case 'u': - update_prog = optarg; + if (update_prog) + free (update_prog); + update_prog = xstrdup (optarg); + nonalias_opt = 1; break; case '?': error (0, 0, "modules file has invalid option for key %s value %s", - key.dptr, val.dptr); + key.dptr, value); err++; - if (mwhere) - free (mwhere); - free (zvalue); - return (err); + goto do_module_return; } } modargc -= optind; modargv += optind; - if (modargc == 0) + if (modargc == 0 && spec_opt == NULL) { error (0, 0, "modules file missing directory for module %s", mname); - if (mwhere) - free (mwhere); - free (zvalue); - return (++err); + ++err; + goto do_module_return; + } + + if (alias && nonalias_opt) + { + /* The documentation has never said it is legal to specify + -a along with another option. And I believe that in the past + CVS has ignored the options other than -a, more or less, in this + situation. */ + error (0, 0, "\ +-a cannot be specified in the modules file along with other options"); + ++err; + goto do_module_return; } /* if this was an alias, call ourselves recursively for each module */ @@ -459,29 +517,117 @@ do_module (db, mname, m_type, msg, callback_proc, where, else err += do_module (db, modargv[i], m_type, msg, callback_proc, where, shorten, local_specified, - run_module_prog, extra_arg); + run_module_prog, build_dirs, extra_arg); } - if (mwhere) - free (mwhere); - free (zvalue); - return (err); + goto do_module_return; + } + + if (mfile != NULL && modargc > 1) + { + error (0, 0, "\ +module `%s' is a request for a file in a module which is not a directory", + mname); + ++err; + goto do_module_return; } /* otherwise, process this module */ - err += callback_proc (&modargc, modargv, where, mwhere, mfile, shorten, - local_specified, mname, msg); + if (modargc > 0) + { + err += callback_proc (modargc, modargv, where, mwhere, mfile, shorten, + local_specified, mname, msg); + } + else + { + /* + * we had nothing but special options, so we must + * make the appropriate directory and cd to it + */ + char *dir; - /* clean up */ - free_names (&modargc, modargv); + if (!build_dirs) + goto do_special; + + dir = where ? where : (mwhere ? mwhere : mname); + /* XXX - think about making null repositories at each dir here + instead of just at the bottom */ + make_directories (dir); + if ( CVS_CHDIR (dir) < 0) + { + error (0, errno, "cannot chdir to %s", dir); + spec_opt = NULL; + err++; + goto do_special; + } + if (!isfile (CVSADM)) + { + char *nullrepos; + + nullrepos = emptydir_name (); + + Create_Admin (".", dir, + nullrepos, (char *) NULL, (char *) NULL, 0, 0, 1); + if (!noexec) + { + FILE *fp; + + fp = open_file (CVSADM_ENTSTAT, "w+"); + if (fclose (fp) == EOF) + error (1, errno, "cannot close %s", CVSADM_ENTSTAT); +#ifdef SERVER_SUPPORT + if (server_active) + server_set_entstat (dir, nullrepos); +#endif + } + free (nullrepos); + } + } /* if there were special include args, process them now */ do_special: + free_names (&xmodargc, xmodargv); + xmodargv = NULL; + /* blow off special options if -l was specified */ if (local_specified) spec_opt = NULL; +#ifdef SERVER_SUPPORT + /* We want to check out into the directory named by the module. + So we set a global variable which tells the server to glom that + directory name onto the front. A cleaner approach would be some + way of passing it down to the recursive call, through the + callback_proc, to start_recursion, and then into the update_dir in + the struct file_info. That way the "Updating foo" message could + print the actual directory we are checking out into. + + For local CVS, this is handled by the chdir call above + (directly or via the callback_proc). */ + if (server_active && spec_opt != NULL) + { + char *change_to; + + change_to = where ? where : (mwhere ? mwhere : mname); + server_dir_to_restore = server_dir; + restore_server_dir = 1; + server_dir = + xmalloc ((server_dir_to_restore != NULL + ? strlen (server_dir_to_restore) + : 0) + + strlen (change_to) + + 5); + server_dir[0] = '\0'; + if (server_dir_to_restore != NULL) + { + strcat (server_dir, server_dir_to_restore); + strcat (server_dir, "/"); + } + strcat (server_dir, change_to); + } +#endif + while (spec_opt != NULL) { char *next_opt; @@ -495,13 +641,13 @@ do_module (db, mname, m_type, msg, callback_proc, where, /* strip whitespace off the end */ do *cp = '\0'; - while (isspace (*--cp)); + while (cp > spec_opt && isspace ((unsigned char) *--cp)); } else next_opt = NULL; /* strip whitespace from front */ - while (isspace (*spec_opt)) + while (isspace ((unsigned char) *spec_opt)) spec_opt++; if (*spec_opt == '\0') @@ -510,10 +656,18 @@ do_module (db, mname, m_type, msg, callback_proc, where, else err += do_module (db, spec_opt, m_type, msg, callback_proc, (char *) NULL, 0, local_specified, - run_module_prog, extra_arg); + run_module_prog, build_dirs, extra_arg); spec_opt = next_opt; } +#ifdef SERVER_SUPPORT + if (server_active && restore_server_dir) + { + free (server_dir); + server_dir = server_dir_to_restore; + } +#endif + /* write out the checkin/update prog files if necessary */ #ifdef SERVER_SUPPORT if (err == 0 && !noexec && m_type == CHECKOUT && server_expanding) @@ -547,8 +701,9 @@ do_module (db, mname, m_type, msg, callback_proc, where, /* cd back to where we started */ if (restore_cwd (&cwd, NULL)) - exit (1); + error_exit (); free_cwd (&cwd); + cwd_saved = 0; /* run checkout or tag prog if appropriate */ if (err == 0 && run_module_prog) @@ -563,38 +718,71 @@ do_module (db, mname, m_type, msg, callback_proc, where, * if we can't find a matching program, just punt and use * whatever is specified in the modules file. */ - char real_prog[PATH_MAX]; + char *real_prog = NULL; char *prog = (m_type == TAG ? tag_prog : (m_type == CHECKOUT ? checkout_prog : export_prog)); char *real_where = (where != NULL ? where : mwhere); + char *expanded_path; if ((*prog != '/') && (*prog != '.')) { + real_prog = xmalloc (strlen (real_where) + strlen (prog) + + 10); (void) sprintf (real_prog, "%s/%s", real_where, prog); if (isfile (real_prog)) prog = real_prog; } - run_setup ("%s %s", prog, real_where); - if (extra_arg) - run_arg (extra_arg); - - if (!quiet) + /* XXX can we determine the line number for this entry??? */ + expanded_path = expand_path (prog, "modules", 0); + if (expanded_path != NULL) { - (void) printf ("%s %s: Executing '", program_name, - command_name); - run_print (stdout); - (void) printf ("'\n"); + run_setup (expanded_path); + run_arg (real_where); + + if (extra_arg) + run_arg (extra_arg); + + if (!quiet) + { + cvs_output (program_name, 0); + cvs_output (" ", 1); + cvs_output (command_name, 0); + cvs_output (": Executing '", 0); + run_print (stdout); + cvs_output ("'\n", 0); + cvs_flushout (); + } + err += run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL); + free (expanded_path); } - err += run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL); + free (real_prog); } } + do_module_return: /* clean up */ + if (xmodargv != NULL) + free_names (&xmodargc, xmodargv); if (mwhere) free (mwhere); - free (zvalue); - + if (checkin_prog) + free (checkin_prog); + if (checkout_prog) + free (checkout_prog); + if (export_prog) + free (export_prog); + if (tag_prog) + free (tag_prog); + if (update_prog) + free (update_prog); + if (cwd_saved) + free_cwd (&cwd); + if (value != NULL) + free (value); + + if (xvalue != NULL) + free (xvalue); return (err); } @@ -683,15 +871,15 @@ save_d (k, ks, d, ds) cp = d; *(cp + ds) = '\0'; /* Assumes an extra byte at end of static dbm buffer */ - while (isspace (*cp)) + while (isspace ((unsigned char) *cp)) cp++; /* Turn into one ' ' -- makes the rest of this routine simpler */ while (*cp) { - if (isspace (*cp)) + if (isspace ((unsigned char) *cp)) { *cp2++ = ' '; - while (isspace (*cp)) + while (isspace ((unsigned char) *cp)) cp++; } else @@ -704,15 +892,19 @@ save_d (k, ks, d, ds) { s_rec->status = def_status; - /* Minor kluge, but general enough to maintain */ for (cp = s_rec->rest; (cp2 = strchr (cp, '-')) != NULL; cp = ++cp2) { if (*(cp2 + 1) == 's' && *(cp2 + 2) == ' ') { - s_rec->status = (cp2 += 3); - while (*cp2 != ' ') + char *status_start; + + cp2 += 3; + status_start = cp2; + while (*cp2 != ' ' && *cp2 != '\0') cp2++; - *cp2++ = '\0'; + s_rec->status = xmalloc (cp2 - status_start + 1); + strncpy (s_rec->status, status_start, cp2 - status_start); + s_rec->status[cp2 - status_start] = '\0'; cp = cp2; break; } @@ -749,23 +941,7 @@ cat_module (status) int moduleargc; struct sortrec *s_h; char *cp, *cp2, **argv; - char line[MAXLINELEN], *moduleargv[MAXFILEPERDIR]; - -#ifdef sun -#ifdef TIOCGSIZE - struct ttysize ts; - - (void) ioctl (0, TIOCGSIZE, &ts); - cols = ts.ts_cols; -#endif -#else -#ifdef TIOCGWINSZ - struct winsize ws; - - (void) ioctl (0, TIOCGWINSZ, &ws); - cols = ws.ws_col; -#endif -#endif + char **moduleargv; Status = status; @@ -780,6 +956,8 @@ cat_module (status) save_d (key.dptr, key.dsize, val.dptr, val.dsize); } + close_module (db); + /* Sort the list as requested */ qsort ((PTR) s_head, s_count, sizeof (struct sortrec), sort_order); @@ -791,22 +969,30 @@ cat_module (status) fill = cols - (indent + 2); for (s_h = s_head, i = 0; i < s_count; i++, s_h++) { + char *line; + /* Print module name (and status, if wanted) */ - (void) printf ("%-12s", s_h->modname); + line = xmalloc (strlen (s_h->modname) + 15); + sprintf (line, "%-12s", s_h->modname); + cvs_output (line, 0); + free (line); if (status) { - (void) printf (" %-11s", s_h->status); - if (s_h->status != def_status) - *(s_h->status + strlen (s_h->status)) = ' '; + line = xmalloc (strlen (s_h->status) + 15); + sprintf (line, " %-11s", s_h->status); + cvs_output (line, 0); + free (line); } + line = xmalloc (strlen (s_h->modname) + strlen (s_h->rest) + 15); /* Parse module file entry as command line and print options */ (void) sprintf (line, "%s %s", s_h->modname, s_h->rest); - line2argv (&moduleargc, moduleargv, line); + line2argv (&moduleargc, &moduleargv, line, " \t"); + free (line); argc = moduleargc; argv = moduleargv; - optind = 1; + optind = 0; wid = 0; while ((c = getopt (argc, argv, CVSMODULE_OPTS)) != -1) { @@ -814,17 +1000,28 @@ cat_module (status) { if (c == 'a' || c == 'l') { - (void) printf (" -%c", c); + char buf[5]; + + sprintf (buf, " -%c", c); + cvs_output (buf, 0); wid += 3; /* Could just set it to 3 */ } else { + char buf[10]; + if (strlen (optarg) + 4 + wid > (unsigned) fill) { - (void) printf ("\n%*s", indent, ""); + int j; + + cvs_output ("\n", 1); + for (j = 0; j < indent; ++j) + cvs_output (" ", 1); wid = 0; } - (void) printf (" -%c %s", c, optarg); + sprintf (buf, " -%c ", c); + cvs_output (buf, 0); + cvs_output (optarg, 0); wid += strlen (optarg) + 4; } } @@ -837,21 +1034,31 @@ cat_module (status) { if (strlen (*argv) + wid > (unsigned) fill) { - (void) printf ("\n%*s", indent, ""); + int j; + + cvs_output ("\n", 1); + for (j = 0; j < indent; ++j) + cvs_output (" ", 1); wid = 0; } - (void) printf (" %s", *argv); + cvs_output (" ", 1); + cvs_output (*argv, 0); wid += strlen (*argv) + 1; } - (void) printf ("\n"); + cvs_output ("\n", 1); /* Format the comment field -- save_d (), compressed spaces */ for (cp2 = cp = s_h->comment; *cp; cp2 = cp) { - (void) printf ("%*s # ", indent, ""); + int j; + + for (j = 0; j < indent; ++j) + cvs_output (" ", 1); + cvs_output (" # ", 0); if (strlen (cp2) < (unsigned) (fill - 2)) { - (void) printf ("%s\n", cp2); + cvs_output (cp2, 0); + cvs_output ("\n", 1); break; } cp += fill - 2; @@ -859,12 +1066,22 @@ cat_module (status) cp--; if (cp == cp2) { - (void) printf ("%s\n", cp2); + cvs_output (cp2, 0); + cvs_output ("\n", 1); break; } *cp++ = '\0'; - (void) printf ("%s\n", cp2); + cvs_output (cp2, 0); + cvs_output ("\n", 1); } + + free_names(&moduleargc, moduleargv); + /* FIXME-leak: here is where we would free s_h->modname, s_h->rest, + and if applicable, s_h->status. Not exactly a memory leak, + in the sense that we are about to exit(), but may be worth + noting if we ever do a multithreaded server or something of + the sort. */ } + /* FIXME-leak: as above, here is where we would free s_head. */ } -- cgit v1.2.3