summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFederico G. Schwindt <fgsch@cvs.openbsd.org>2001-11-06 05:31:52 +0000
committerFederico G. Schwindt <fgsch@cvs.openbsd.org>2001-11-06 05:31:52 +0000
commit522fffa1c13d110725c9e13b90f03d23365cc2c7 (patch)
treebfc22a238be7f544713b5ae2e6ee4bbb6856081f
parent7ed40ddd4c0de1c83a899df441ecd68d2f80c17d (diff)
start editor in an empty line as used to be; deraadt@ agrees.
-rw-r--r--gnu/usr.bin/cvs/src/logmsg.c648
1 files changed, 538 insertions, 110 deletions
diff --git a/gnu/usr.bin/cvs/src/logmsg.c b/gnu/usr.bin/cvs/src/logmsg.c
index f39dd31e888..679d87ee585 100644
--- a/gnu/usr.bin/cvs/src/logmsg.c
+++ b/gnu/usr.bin/cvs/src/logmsg.c
@@ -3,30 +3,28 @@
* 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.
+ * specified in the README file that comes with the CVS source distribution.
*/
#include "cvs.h"
-
-#ifndef lint
-static const char rcsid[] = "$CVSid: @(#)logmsg.c 1.48 94/09/29 $";
-USE(rcsid);
-#endif
+#include "getline.h"
static int find_type PROTO((Node * p, void *closure));
static int fmt_proc PROTO((Node * p, void *closure));
-static int logfile_write PROTO((char *repository, char *filter, char *title,
- char *message, char *revision, FILE * logfp,
- List * changes));
+static int logfile_write PROTO((char *repository, char *filter,
+ char *message, FILE * logfp, List * changes));
static int rcsinfo_proc PROTO((char *repository, char *template));
static int title_proc PROTO((Node * p, void *closure));
static int update_logfile_proc PROTO((char *repository, char *filter));
static void setup_tmpfile PROTO((FILE * xfp, char *xprefix, List * changes));
static int editinfo_proc PROTO((char *repository, char *template));
+static int verifymsg_proc PROTO((char *repository, char *script));
static FILE *fp;
static char *str_list;
+static char *str_list_format; /* The format for str_list's contents. */
static char *editinfo_editor;
+static char *verifymsg_script;
static Ctype type;
/*
@@ -35,6 +33,7 @@ static Ctype type;
* and removed files are included (if any) and formatted to look pretty. */
static char *prefix;
static int col;
+static char *tag;
static void
setup_tmpfile (xfp, xprefix, changes)
FILE *xfp;
@@ -49,28 +48,40 @@ setup_tmpfile (xfp, xprefix, changes)
if (walklist (changes, find_type, NULL) != 0)
{
(void) fprintf (fp, "%sModified Files:\n", prefix);
- (void) fprintf (fp, "%s\t", prefix);
- col = 8;
+ col = 0;
(void) walklist (changes, fmt_proc, NULL);
(void) fprintf (fp, "\n");
+ if (tag != NULL)
+ {
+ free (tag);
+ tag = NULL;
+ }
}
type = T_ADDED;
if (walklist (changes, find_type, NULL) != 0)
{
(void) fprintf (fp, "%sAdded Files:\n", prefix);
- (void) fprintf (fp, "%s\t", prefix);
- col = 8;
+ col = 0;
(void) walklist (changes, fmt_proc, NULL);
(void) fprintf (fp, "\n");
+ if (tag != NULL)
+ {
+ free (tag);
+ tag = NULL;
+ }
}
type = T_REMOVED;
if (walklist (changes, find_type, NULL) != 0)
{
(void) fprintf (fp, "%sRemoved Files:\n", prefix);
- (void) fprintf (fp, "%s\t", prefix);
- col = 8;
+ col = 0;
(void) walklist (changes, fmt_proc, NULL);
(void) fprintf (fp, "\n");
+ if (tag != NULL)
+ {
+ free (tag);
+ tag = NULL;
+ }
}
}
@@ -82,7 +93,10 @@ find_type (p, closure)
Node *p;
void *closure;
{
- if (p->data == (char *) type)
+ struct logfile_info *li;
+
+ li = (struct logfile_info *) p->data;
+ if (li->type == type)
return (1);
else
return (0);
@@ -98,9 +112,44 @@ fmt_proc (p, closure)
Node *p;
void *closure;
{
- if (p->data == (char *) type)
+ struct logfile_info *li;
+
+ li = (struct logfile_info *) p->data;
+ if (li->type == type)
{
- if ((col + (int) strlen (p->key)) > 70)
+ if (li->tag == NULL
+ ? tag != NULL
+ : tag == NULL || strcmp (tag, li->tag) != 0)
+ {
+ if (col > 0)
+ (void) fprintf (fp, "\n");
+ (void) fprintf (fp, "%s", prefix);
+ col = strlen (prefix);
+ while (col < 6)
+ {
+ (void) fprintf (fp, " ");
+ ++col;
+ }
+
+ if (li->tag == NULL)
+ (void) fprintf (fp, "No tag");
+ else
+ (void) fprintf (fp, "Tag: %s", li->tag);
+
+ if (tag != NULL)
+ free (tag);
+ tag = xstrdup (li->tag);
+
+ /* Force a new line. */
+ col = 70;
+ }
+
+ if (col == 0)
+ {
+ (void) fprintf (fp, "%s\t", prefix);
+ col = 8;
+ }
+ else if (col > 8 && (col + (int) strlen (p->key)) > 70)
{
(void) fprintf (fp, "\n%s\t", prefix);
col = 8;
@@ -116,10 +165,8 @@ fmt_proc (p, closure)
* editor on the file. The header garbage in the resultant file is then
* stripped and the log message is stored in the "message" argument.
*
- * rcsinfo - is the name of a file containing lines tacked onto the end of the
- * RCS info offered to the user for editing. If specified, the '-m' flag to
- * "commit" is disabled -- users are forced to run the editor.
- *
+ * If REPOSITORY is non-NULL, process rcsinfo for that repository; if it
+ * is NULL, use the CVSADM_TEMPLATE file instead.
*/
void
do_editor (dir, messagep, repository, changes)
@@ -129,24 +176,35 @@ do_editor (dir, messagep, repository, changes)
List *changes;
{
static int reuse_log_message = 0;
- char line[MAXLINELEN], fname[L_tmpnam+1];
+ char *line;
+ int line_length;
+ size_t line_chars_allocated;
+ char *fname;
struct stat pre_stbuf, post_stbuf;
int retcode = 0;
if (noexec || reuse_log_message)
return;
+ /* Abort creation of temp file if no editor is defined */
+ if (strcmp (Editor, "") == 0 && !editinfo_editor)
+ error(1, 0, "no editor defined, must use -e or -m");
+
/* Create a temporary file */
- (void) tmpnam (fname);
+ /* FIXME - It's possible we should be relying on cvs_temp_file to open
+ * the file here - we get race conditions otherwise.
+ */
+ fname = cvs_temp_name ();
again:
- if ((fp = fopen (fname, "w+")) == NULL)
+ if ((fp = CVS_FOPEN (fname, "w+")) == NULL)
error (1, 0, "cannot create temporary file %s", fname);
if (*messagep)
{
(void) fprintf (fp, "%s", *messagep);
- if ((*messagep)[strlen (*messagep) - 1] != '\n')
+ if ((*messagep)[0] == '\0' ||
+ (*messagep)[strlen (*messagep) - 1] != '\n')
(void) fprintf (fp, "\n");
}
else
@@ -155,13 +213,48 @@ do_editor (dir, messagep, repository, changes)
if (repository != NULL)
/* tack templates on if necessary */
(void) Parse_Info (CVSROOTADM_RCSINFO, repository, rcsinfo_proc, 1);
+ else
+ {
+ FILE *tfp;
+ char buf[1024];
+ size_t n;
+ size_t nwrite;
+
+ /* Why "b"? */
+ tfp = CVS_FOPEN (CVSADM_TEMPLATE, "rb");
+ if (tfp == NULL)
+ {
+ if (!existence_error (errno))
+ error (1, errno, "cannot read %s", CVSADM_TEMPLATE);
+ }
+ else
+ {
+ while (!feof (tfp))
+ {
+ char *p = buf;
+ n = fread (buf, 1, sizeof buf, tfp);
+ nwrite = n;
+ while (nwrite > 0)
+ {
+ n = fwrite (p, 1, nwrite, fp);
+ nwrite -= n;
+ p += n;
+ }
+ if (ferror (tfp))
+ error (1, errno, "cannot read %s", CVSADM_TEMPLATE);
+ }
+ if (fclose (tfp) < 0)
+ error (0, errno, "cannot close %s", CVSADM_TEMPLATE);
+ }
+ }
(void) fprintf (fp,
"%s----------------------------------------------------------------------\n",
CVSEDITPREFIX);
(void) fprintf (fp,
- "%sEnter Log. Lines beginning with `%s' are removed automatically\n%s\n",
- CVSEDITPREFIX, CVSEDITPREFIX, CVSEDITPREFIX);
+ "%sEnter Log. Lines beginning with `%.*s' are removed automatically\n%s\n",
+ CVSEDITPREFIX, CVSEDITPREFIXLEN, CVSEDITPREFIX,
+ CVSEDITPREFIX);
if (dir != NULL && *dir)
(void) fprintf (fp, "%sCommitting in %s\n%s\n", CVSEDITPREFIX,
dir, CVSEDITPREFIX);
@@ -172,18 +265,24 @@ do_editor (dir, messagep, repository, changes)
CVSEDITPREFIX);
/* finish off the temp file */
- (void) fclose (fp);
- if (stat (fname, &pre_stbuf) == -1)
+ if (fclose (fp) == EOF)
+ error (1, errno, "%s", fname);
+ if ( CVS_STAT (fname, &pre_stbuf) == -1)
pre_stbuf.st_mtime = 0;
if (editinfo_editor)
free (editinfo_editor);
editinfo_editor = (char *) NULL;
+#ifdef CLIENT_SUPPORT
+ if (current_parsed_root->isremote)
+ ; /* nothing, leave editinfo_editor NULL */
+ else
+#endif
if (repository != NULL)
(void) Parse_Info (CVSROOTADM_EDITINFO, repository, editinfo_proc, 0);
/* run the editor */
- run_setup ("%s", editinfo_editor ? editinfo_editor : Editor);
+ run_setup (editinfo_editor ? editinfo_editor : Editor);
run_arg (fname);
if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY,
RUN_NORMAL | RUN_SIGIGNORE)) != 0)
@@ -198,30 +297,47 @@ do_editor (dir, messagep, repository, changes)
if (*messagep)
free (*messagep);
- if (stat (fname, &post_stbuf) != 0)
+ if ( CVS_STAT (fname, &post_stbuf) != 0)
error (1, errno, "cannot find size of temp file %s", fname);
if (post_stbuf.st_size == 0)
*messagep = NULL;
else
{
+ /* On NT, we might read less than st_size bytes, but we won't
+ read more. So this works. */
*messagep = (char *) xmalloc (post_stbuf.st_size + 1);
*messagep[0] = '\0';
}
-/* !!! XXX FIXME: fgets is broken. This should not have any line
- length limits. */
+ line = NULL;
+ line_chars_allocated = 0;
if (*messagep)
{
- while (fgets (line, sizeof (line), fp) != NULL)
+ size_t message_len = post_stbuf.st_size + 1;
+ size_t offset = 0;
+ while (1)
{
- if (strncmp (line, CVSEDITPREFIX, sizeof (CVSEDITPREFIX) - 1) == 0)
+ line_length = getline (&line, &line_chars_allocated, fp);
+ if (line_length == -1)
+ {
+ if (ferror (fp))
+ error (0, errno, "warning: cannot read %s", fname);
+ break;
+ }
+ if (strncmp (line, CVSEDITPREFIX, CVSEDITPREFIXLEN) == 0)
continue;
- (void) strcat (*messagep, line);
+ if (offset + line_length >= message_len)
+ expand_string (messagep, &message_len,
+ offset + line_length + 1);
+ (void) strcpy (*messagep + offset, line);
+ offset += line_length;
}
}
- (void) fclose (fp);
+ if (fclose (fp) < 0)
+ error (0, errno, "warning: cannot close %s", fname);
+
if (pre_stbuf.st_mtime == post_stbuf.st_mtime ||
*messagep == NULL ||
strcmp (*messagep, "\n") == 0)
@@ -232,12 +348,24 @@ do_editor (dir, messagep, repository, changes)
(void) printf ("a)bort, c)ontinue, e)dit, !)reuse this message unchanged for remaining dirs\n");
(void) printf ("Action: (continue) ");
(void) fflush (stdout);
- *line = '\0';
- (void) fgets (line, sizeof (line), stdin);
- if (*line == '\0' || *line == '\n' || *line == 'c' || *line == 'C')
+ line_length = getline (&line, &line_chars_allocated, stdin);
+ if (line_length < 0)
+ {
+ error (0, errno, "cannot read from stdin");
+ if (unlink_file (fname) < 0)
+ error (0, errno,
+ "warning: cannot remove temp file %s", fname);
+ error (1, 0, "aborting");
+ }
+ else if (line_length == 0
+ || *line == '\n' || *line == 'c' || *line == 'C')
break;
if (*line == 'a' || *line == 'A')
- error (1, 0, "aborted by user");
+ {
+ if (unlink_file (fname) < 0)
+ error (0, errno, "warning: cannot remove temp file %s", fname);
+ error (1, 0, "aborted by user");
+ }
if (*line == 'e' || *line == 'E')
goto again;
if (*line == '!')
@@ -248,7 +376,91 @@ do_editor (dir, messagep, repository, changes)
(void) printf ("Unknown input\n");
}
}
- (void) unlink_file (fname);
+ if (line)
+ free (line);
+ if (unlink_file (fname) < 0)
+ error (0, errno, "warning: cannot remove temp file %s", fname);
+ free (fname);
+}
+
+/* Runs the user-defined verification script as part of the commit or import
+ process. This verification is meant to be run whether or not the user
+ included the -m atribute. unlike the do_editor function, this is
+ independant of the running of an editor for getting a message.
+ */
+void
+do_verify (message, repository)
+ char *message;
+ char *repository;
+{
+ FILE *fp;
+ char *fname;
+ int retcode = 0;
+
+#ifdef CLIENT_SUPPORT
+ if (current_parsed_root->isremote)
+ /* The verification will happen on the server. */
+ return;
+#endif
+
+ /* FIXME? Do we really want to skip this on noexec? What do we do
+ for the other administrative files? */
+ if (noexec)
+ return;
+
+ /* If there's no message, then we have nothing to verify. Can this
+ case happen? And if so why would we print a message? */
+ if (message == NULL)
+ {
+ cvs_output ("No message to verify\n", 0);
+ return;
+ }
+
+ /* open a temporary file, write the message to the
+ temp file, and close the file. */
+
+ if ((fp = cvs_temp_file (&fname)) == NULL)
+ error (1, errno, "cannot create temporary file %s", fname);
+ else
+ {
+ fprintf (fp, "%s", message);
+ if ((message)[0] == '\0' ||
+ (message)[strlen (message) - 1] != '\n')
+ (void) fprintf (fp, "%s", "\n");
+ if (fclose (fp) == EOF)
+ error (1, errno, "%s", fname);
+
+ /* Get the name of the verification script to run */
+
+ if (repository != NULL)
+ (void) Parse_Info (CVSROOTADM_VERIFYMSG, repository,
+ verifymsg_proc, 0);
+
+ /* Run the verification script */
+
+ if (verifymsg_script)
+ {
+ run_setup (verifymsg_script);
+ run_arg (fname);
+ if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY,
+ RUN_NORMAL | RUN_SIGIGNORE)) != 0)
+ {
+ /* Since following error() exits, delete the temp file
+ now. */
+ if (unlink_file (fname) < 0)
+ error (0, errno, "cannot remove %s", fname);
+
+ error (1, retcode == -1 ? errno : 0,
+ "Message verification failed");
+ }
+ }
+
+ /* Delete the temp file */
+
+ if (unlink_file (fname) < 0)
+ error (0, errno, "cannot remove %s", fname);
+ free (fname);
+ }
}
/*
@@ -264,7 +476,6 @@ rcsinfo_proc (repository, template)
{
static char *last_template;
FILE *tfp;
- char line[MAXLINELEN];
/* nothing to do if the last one included is the same as this one */
if (last_template && strcmp (last_template, template) == 0)
@@ -273,16 +484,24 @@ rcsinfo_proc (repository, template)
free (last_template);
last_template = xstrdup (template);
- if ((tfp = fopen (template, "r")) != NULL)
+ if ((tfp = CVS_FOPEN (template, "r")) != NULL)
{
- while (fgets (line, sizeof (line), tfp) != NULL)
+ char *line = NULL;
+ size_t line_chars_allocated = 0;
+
+ while (getline (&line, &line_chars_allocated, tfp) >= 0)
(void) fputs (line, fp);
- (void) fclose (tfp);
+ if (ferror (tfp))
+ error (0, errno, "warning: cannot read %s", template);
+ if (fclose (tfp) < 0)
+ error (0, errno, "warning: cannot close %s", template);
+ if (line)
+ free (line);
return (0);
}
else
{
- error (0, 0, "Couldn't open rcsinfo template file %s", template);
+ error (0, errno, "Couldn't open rcsinfo template file %s", template);
return (1);
}
}
@@ -293,60 +512,28 @@ rcsinfo_proc (repository, template)
* directory in the source repository. The log information is fed into the
* specified program as standard input.
*/
-static char *title;
static FILE *logfp;
static char *message;
-static char *revision;
static List *changes;
void
-Update_Logfile (repository, xmessage, xrevision, xlogfp, xchanges)
+Update_Logfile (repository, xmessage, xlogfp, xchanges)
char *repository;
char *xmessage;
- char *xrevision;
FILE *xlogfp;
List *xchanges;
{
- char *srepos;
-
/* nothing to do if the list is empty */
if (xchanges == NULL || xchanges->list->next == xchanges->list)
return;
/* set up static vars for update_logfile_proc */
message = xmessage;
- revision = xrevision;
logfp = xlogfp;
changes = xchanges;
- /* figure out a good title string */
- srepos = Short_Repository (repository);
-
- /* allocate a chunk of memory to hold the title string */
- if (!str_list)
- str_list = xmalloc (MAXLISTLEN);
- str_list[0] = '\0';
-
- type = T_TITLE;
- (void) walklist (changes, title_proc, NULL);
- type = T_ADDED;
- (void) walklist (changes, title_proc, NULL);
- type = T_MODIFIED;
- (void) walklist (changes, title_proc, NULL);
- type = T_REMOVED;
- (void) walklist (changes, title_proc, NULL);
- title = xmalloc (strlen (srepos) + strlen (str_list) + 1 + 2); /* for 's */
- (void) sprintf (title, "'%s%s'", srepos, str_list);
-
- /* to be nice, free up this chunk of memory */
- free (str_list);
- str_list = (char *) NULL;
-
/* call Parse_Info to do the actual logfile updates */
(void) Parse_Info (CVSROOTADM_LOGINFO, repository, update_logfile_proc, 1);
-
- /* clean up */
- free (title);
}
/*
@@ -357,59 +544,276 @@ update_logfile_proc (repository, filter)
char *repository;
char *filter;
{
- return (logfile_write (repository, filter, title, message, revision,
- logfp, changes));
+ return (logfile_write (repository, filter, message, logfp, changes));
}
/*
- * concatenate each name onto str_list
+ * concatenate each filename/version onto str_list
*/
static int
title_proc (p, closure)
Node *p;
void *closure;
{
- if (p->data == (char *) type)
+ struct logfile_info *li;
+ char *c;
+
+ li = (struct logfile_info *) p->data;
+ if (li->type == type)
{
+ /* Until we decide on the correct logging solution when we add
+ directories or perform imports, T_TITLE nodes will only
+ tack on the name provided, regardless of the format string.
+ You can verify that this assumption is safe by checking the
+ code in add.c (add_directory) and import.c (import). */
+
+ str_list = xrealloc (str_list, strlen (str_list) + 5);
(void) strcat (str_list, " ");
- (void) strcat (str_list, p->key);
+
+ if (li->type == T_TITLE)
+ {
+ str_list = xrealloc (str_list,
+ strlen (str_list) + strlen (p->key) + 5);
+ (void) strcat (str_list, p->key);
+ }
+ else
+ {
+ /* All other nodes use the format string. */
+
+ for (c = str_list_format; *c != '\0'; c++)
+ {
+ switch (*c)
+ {
+ case 's':
+ str_list =
+ xrealloc (str_list,
+ strlen (str_list) + strlen (p->key) + 5);
+ (void) strcat (str_list, p->key);
+ break;
+ case 'V':
+ str_list =
+ xrealloc (str_list,
+ (strlen (str_list)
+ + (li->rev_old ? strlen (li->rev_old) : 0)
+ + 10)
+ );
+ (void) strcat (str_list, (li->rev_old
+ ? li->rev_old : "NONE"));
+ break;
+ case 'v':
+ str_list =
+ xrealloc (str_list,
+ (strlen (str_list)
+ + (li->rev_new ? strlen (li->rev_new) : 0)
+ + 10)
+ );
+ (void) strcat (str_list, (li->rev_new
+ ? li->rev_new : "NONE"));
+ break;
+ /* All other characters, we insert an empty field (but
+ we do put in the comma separating it from other
+ fields). This way if future CVS versions add formatting
+ characters, one can write a loginfo file which at least
+ won't blow up on an old CVS. */
+ }
+ if (*(c + 1) != '\0')
+ {
+ str_list = xrealloc (str_list, strlen (str_list) + 5);
+ (void) strcat (str_list, ",");
+ }
+ }
+ }
}
return (0);
}
/*
- * Since some systems don't define this...
- */
-#ifndef MAXHOSTNAMELEN
-#define MAXHOSTNAMELEN 256
-#endif
-
-/*
* Writes some stuff to the logfile "filter" and returns the status of the
* filter program.
*/
static int
-logfile_write (repository, filter, title, message, revision, logfp, changes)
+logfile_write (repository, filter, message, logfp, changes)
char *repository;
char *filter;
- char *title;
char *message;
- char *revision;
FILE *logfp;
List *changes;
{
- char cwd[PATH_MAX];
- FILE *pipefp, *Popen ();
- char *prog = xmalloc (MAXPROGLEN);
+ FILE *pipefp;
+ char *prog;
char *cp;
int c;
+ int pipestatus;
+ char *fmt_percent; /* the location of the percent sign
+ that starts the format string. */
+
+ /* The user may specify a format string as part of the filter.
+ Originally, `%s' was the only valid string. The string that
+ was substituted for it was:
+
+ <repository-name> <file1> <file2> <file3> ...
+
+ Each file was either a new directory/import (T_TITLE), or a
+ added (T_ADDED), modified (T_MODIFIED), or removed (T_REMOVED)
+ file.
+
+ It is desirable to preserve that behavior so lots of commitlog
+ scripts won't die when they get this new code. At the same
+ time, we'd like to pass other information about the files (like
+ version numbers, statuses, or checkin times).
+
+ The solution is to allow a format string that allows us to
+ specify those other pieces of information. The format string
+ will be composed of `%' followed by a single format character,
+ or followed by a set of format characters surrounded by `{' and
+ `}' as separators. The format characters are:
+
+ s = file name
+ V = old version number (pre-checkin)
+ v = new version number (post-checkin)
+
+ For example, valid format strings are:
+
+ %{}
+ %s
+ %{s}
+ %{sVv}
+
+ There's no reason that more items couldn't be added (like
+ modification date or file status [added, modified, updated,
+ etc.]) -- the code modifications would be minimal (logmsg.c
+ (title_proc) and commit.c (check_fileproc)).
+
+ The output will be a string of tokens separated by spaces. For
+ backwards compatibility, the the first token will be the
+ repository name. The rest of the tokens will be
+ comma-delimited lists of the information requested in the
+ format string. For example, if `/u/src/master' is the
+ repository, `%{sVv}' is the format string, and three files
+ (ChangeLog, Makefile, foo.c) were modified, the output might
+ be:
+
+ /u/src/master ChangeLog,1.1,1.2 Makefile,1.3,1.4 foo.c,1.12,1.13
+
+ Why this duplicates the old behavior when the format string is
+ `%s' is left as an exercise for the reader. */
+
+ fmt_percent = strchr (filter, '%');
+ if (fmt_percent)
+ {
+ int len;
+ char *srepos;
+ char *fmt_begin, *fmt_end; /* beginning and end of the
+ format string specified in
+ filter. */
+ char *fmt_continue; /* where the string continues
+ after the format string (we
+ might skip a '}') somewhere
+ in there... */
+
+ /* Grab the format string. */
+
+ if ((*(fmt_percent + 1) == ' ') || (*(fmt_percent + 1) == '\0'))
+ {
+ /* The percent stands alone. This is an error. We could
+ be treating ' ' like any other formatting character, but
+ using it as a formatting character seems like it would be
+ a mistake. */
+
+ /* Would be nice to also be giving the line number. */
+ error (0, 0, "loginfo: '%%' not followed by formatting character");
+ fmt_begin = fmt_percent + 1;
+ fmt_end = fmt_begin;
+ fmt_continue = fmt_begin;
+ }
+ else if (*(fmt_percent + 1) == '{')
+ {
+ /* The percent has a set of characters following it. */
- /* XXX <woods@web.net> -- this is gross, ugly, and a hack! FIXME! */
- /*
- * A maximum of 6 %s arguments are supported in the filter
- */
- (void) sprintf (prog, filter, title, title, title, title, title, title);
- if ((pipefp = Popen (prog, "w")) == NULL)
+ fmt_begin = fmt_percent + 2;
+ fmt_end = strchr (fmt_begin, '}');
+ if (fmt_end)
+ {
+ /* Skip over the '}' character. */
+
+ fmt_continue = fmt_end + 1;
+ }
+ else
+ {
+ /* There was no close brace -- assume that format
+ string continues to the end of the line. */
+
+ /* Would be nice to also be giving the line number. */
+ error (0, 0, "loginfo: '}' missing");
+ fmt_end = fmt_begin + strlen (fmt_begin);
+ fmt_continue = fmt_end;
+ }
+ }
+ else
+ {
+ /* The percent has a single character following it. FIXME:
+ %% should expand to a regular percent sign. */
+
+ fmt_begin = fmt_percent + 1;
+ fmt_end = fmt_begin + 1;
+ fmt_continue = fmt_end;
+ }
+
+ len = fmt_end - fmt_begin;
+ str_list_format = xmalloc (len + 1);
+ strncpy (str_list_format, fmt_begin, len);
+ str_list_format[len] = '\0';
+
+ /* Allocate an initial chunk of memory. As we build up the string
+ we will realloc it. */
+ if (!str_list)
+ str_list = xmalloc (1);
+ str_list[0] = '\0';
+
+ /* Add entries to the string. Don't bother looking for
+ entries if the format string is empty. */
+
+ if (str_list_format[0] != '\0')
+ {
+ type = T_TITLE;
+ (void) walklist (changes, title_proc, NULL);
+ type = T_ADDED;
+ (void) walklist (changes, title_proc, NULL);
+ type = T_MODIFIED;
+ (void) walklist (changes, title_proc, NULL);
+ type = T_REMOVED;
+ (void) walklist (changes, title_proc, NULL);
+ }
+
+ free (str_list_format);
+
+ /* Construct the final string. */
+
+ srepos = Short_Repository (repository);
+
+ prog = cp = xmalloc ((fmt_percent - filter) + 2 * strlen (srepos)
+ + 2 * strlen (str_list) + strlen (fmt_continue)
+ + 10);
+ (void) memcpy (cp, filter, fmt_percent - filter);
+ cp += fmt_percent - filter;
+ *cp++ = '"';
+ cp = shell_escape (cp, srepos);
+ cp = shell_escape (cp, str_list);
+ *cp++ = '"';
+ (void) strcpy (cp, fmt_continue);
+
+ /* To be nice, free up some memory. */
+
+ free (str_list);
+ str_list = (char *) NULL;
+ }
+ else
+ {
+ /* There's no format string. */
+ prog = xstrdup (filter);
+ }
+
+ if ((pipefp = run_popen (prog, "w")) == NULL)
{
if (!noexec)
error (0, 0, "cannot write entry to log filter: %s", prog);
@@ -417,10 +821,17 @@ logfile_write (repository, filter, title, message, revision, logfp, changes)
return (1);
}
(void) fprintf (pipefp, "Update of %s\n", repository);
- (void) fprintf (pipefp, "In directory %s:%s\n\n", hostname,
- ((cp = getwd (cwd)) != NULL) ? cp : cwd);
- if (revision && *revision)
- (void) fprintf (pipefp, "Revision/Branch: %s\n\n", revision);
+ (void) fprintf (pipefp, "In directory %s:", hostname);
+ cp = xgetwd ();
+ if (cp == NULL)
+ fprintf (pipefp, "<cannot get working directory: %s>\n\n",
+ strerror (errno));
+ else
+ {
+ fprintf (pipefp, "%s\n\n", cp);
+ free (cp);
+ }
+
setup_tmpfile (pipefp, "", changes);
(void) fprintf (pipefp, "Log Message:\n%s\n", message);
if (logfp != (FILE *) 0)
@@ -431,7 +842,8 @@ logfile_write (repository, filter, title, message, revision, logfp, changes)
(void) putc ((char) c, pipefp);
}
free (prog);
- return (pclose (pipefp));
+ pipestatus = pclose (pipefp);
+ return ((pipestatus == -1) || (pipestatus == 127)) ? 1 : 0;
}
/*
@@ -457,3 +869,19 @@ editinfo_proc(repository, editor)
editinfo_editor = xstrdup (editor);
return (0);
}
+
+/* This routine is calld by Parse_Info. it asigns the name of the
+ * message verification script to the global variable verify_script
+ */
+static int
+verifymsg_proc (repository, script)
+ char *repository;
+ char *script;
+{
+ if (verifymsg_script && strcmp (verifymsg_script, script) == 0)
+ return (0);
+ if (verifymsg_script)
+ free (verifymsg_script);
+ verifymsg_script = xstrdup (script);
+ return (0);
+}