Merge from vendor branch BINUTILS:
[dragonfly.git] / contrib / cvs-1.12 / src / logmsg.c
1 /*
2  * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
3  *
4  * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
5  *                                  and others.
6  *
7  * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
8  * Portions Copyright (C) 1989-1992, Brian Berliner
9  * 
10  * You may distribute under the terms of the GNU General Public License as
11  * specified in the README file that comes with the CVS source distribution.
12  */
13
14
15 #include "cvs.h"
16 #include "getline.h"
17
18 static int find_type (Node * p, void *closure);
19 static int fmt_proc (Node * p, void *closure);
20 static int logfile_write (const char *repository, const char *filter,
21                           const char *message, FILE * logfp, List * changes);
22 static int logmsg_list_to_args_proc (Node *p, void *closure);
23 static int rcsinfo_proc (const char *repository, const char *template,
24                          void *closure );
25 static int update_logfile_proc (const char *repository, const char *filter,
26                                 void *closure);
27 static void setup_tmpfile (FILE * xfp, char *xprefix, List * changes);
28 static int verifymsg_proc (const char *repository, const char *script,
29                            void *closure );
30
31 static FILE *fp;
32 static Ctype type;
33
34 struct verifymsg_proc_data
35 {
36     /* The name of the temp file storing the log message to be verified.  This
37      * is initially NULL and verifymsg_proc() writes message into it so that it
38      * can be shared when multiple verifymsg scripts exist.  do_verify() is
39      * responsible for rereading the message from the file when
40      * RereadLogAfterVerify is in effect and the file has changed.
41      */
42     char *fname;
43     /* The initial message text to be verified.
44      */
45     char *message;
46     /* The initial stats of the temp file so we can tell that the temp file has
47      * been changed when RereadLogAfterVerify is STAT.
48      */
49     struct stat pre_stbuf;
50    /* The list of files being changed, with new and old version numbers.
51     */
52    List *changes;
53 };
54
55 /*
56  * Puts a standard header on the output which is either being prepared for an
57  * editor session, or being sent to a logfile program.  The modified, added,
58  * and removed files are included (if any) and formatted to look pretty. */
59 static char *prefix;
60 static int col;
61 static char *tag;
62 static void
63 setup_tmpfile (FILE *xfp, char *xprefix, List *changes)
64 {
65     /* set up statics */
66     fp = xfp;
67     prefix = xprefix;
68
69     type = T_MODIFIED;
70     if (walklist (changes, find_type, NULL) != 0)
71     {
72         (void) fprintf (fp, "%sModified Files:\n", prefix);
73         col = 0;
74         (void) walklist (changes, fmt_proc, NULL);
75         (void) fprintf (fp, "\n");
76         if (tag != NULL)
77         {
78             free (tag);
79             tag = NULL;
80         }
81     }
82     type = T_ADDED;
83     if (walklist (changes, find_type, NULL) != 0)
84     {
85         (void) fprintf (fp, "%sAdded Files:\n", prefix);
86         col = 0;
87         (void) walklist (changes, fmt_proc, NULL);
88         (void) fprintf (fp, "\n");
89         if (tag != NULL)
90         {
91             free (tag);
92             tag = NULL;
93         }
94     }
95     type = T_REMOVED;
96     if (walklist (changes, find_type, NULL) != 0)
97     {
98         (void) fprintf (fp, "%sRemoved Files:\n", prefix);
99         col = 0;
100         (void) walklist (changes, fmt_proc, NULL);
101         (void) fprintf (fp, "\n");
102         if (tag != NULL)
103         {
104             free (tag);
105             tag = NULL;
106         }
107     }
108 }
109
110 /*
111  * Looks for nodes of a specified type and returns 1 if found
112  */
113 static int
114 find_type (Node *p, void *closure)
115 {
116     struct logfile_info *li = p->data;
117
118     if (li->type == type)
119         return (1);
120     else
121         return (0);
122 }
123
124 /*
125  * Breaks the files list into reasonable sized lines to avoid line wrap...
126  * all in the name of pretty output.  It only works on nodes whose types
127  * match the one we're looking for
128  */
129 static int
130 fmt_proc (Node *p, void *closure)
131 {
132     struct logfile_info *li;
133
134     li = p->data;
135     if (li->type == type)
136     {
137         if (li->tag == NULL
138             ? tag != NULL
139             : tag == NULL || strcmp (tag, li->tag) != 0)
140         {
141             if (col > 0)
142                 (void) fprintf (fp, "\n");
143             (void) fputs (prefix, fp);
144             col = strlen (prefix);
145             while (col < 6)
146             {
147                 (void) fprintf (fp, " ");
148                 ++col;
149             }
150
151             if (li->tag == NULL)
152                 (void) fprintf (fp, "No tag");
153             else
154                 (void) fprintf (fp, "Tag: %s", li->tag);
155
156             if (tag != NULL)
157                 free (tag);
158             tag = xstrdup (li->tag);
159
160             /* Force a new line.  */
161             col = 70;
162         }
163
164         if (col == 0)
165         {
166             (void) fprintf (fp, "%s\t", prefix);
167             col = 8;
168         }
169         else if (col > 8 && (col + (int) strlen (p->key)) > 70)
170         {
171             (void) fprintf (fp, "\n%s\t", prefix);
172             col = 8;
173         }
174         (void) fprintf (fp, "%s ", p->key);
175         col += strlen (p->key) + 1;
176     }
177     return (0);
178 }
179
180 /*
181  * Builds a temporary file using setup_tmpfile() and invokes the user's
182  * editor on the file.  The header garbage in the resultant file is then
183  * stripped and the log message is stored in the "message" argument.
184  * 
185  * If REPOSITORY is non-NULL, process rcsinfo for that repository; if it
186  * is NULL, use the CVSADM_TEMPLATE file instead.  REPOSITORY should be
187  * NULL when running in client mode.
188  *
189  * GLOBALS
190  *   Editor     Set to a default value by configure and overridable using the
191  *              -e option to the CVS executable.
192  */
193 void
194 do_editor (const char *dir, char **messagep, const char *repository,
195            List *changes)
196 {
197     static int reuse_log_message = 0;
198     char *line;
199     int line_length;
200     size_t line_chars_allocated;
201     char *fname;
202     struct stat pre_stbuf, post_stbuf;
203     int retcode = 0;
204
205     assert (!current_parsed_root->isremote != !repository);
206
207     if (noexec || reuse_log_message)
208         return;
209
210     /* Abort before creation of the temp file if no editor is defined. */
211     if (strcmp (Editor, "") == 0)
212         error(1, 0, "no editor defined, must use -e or -m");
213
214   again:
215     /* Create a temporary file.  */
216     if( ( fp = cvs_temp_file( &fname ) ) == NULL )
217         error( 1, errno, "cannot create temporary file" );
218
219     if (*messagep)
220     {
221         (void) fputs (*messagep, fp);
222
223         if ((*messagep)[0] == '\0' ||
224             (*messagep)[strlen (*messagep) - 1] != '\n')
225             (void) fprintf (fp, "\n");
226     }
227
228     if (repository != NULL)
229         /* tack templates on if necessary */
230         (void) Parse_Info (CVSROOTADM_RCSINFO, repository, rcsinfo_proc,
231                 PIOPT_ALL, NULL);
232     else
233     {
234         FILE *tfp;
235         char buf[1024];
236         size_t n;
237         size_t nwrite;
238
239         /* Why "b"?  */
240         tfp = CVS_FOPEN (CVSADM_TEMPLATE, "rb");
241         if (tfp == NULL)
242         {
243             if (!existence_error (errno))
244                 error (1, errno, "cannot read %s", CVSADM_TEMPLATE);
245         }
246         else
247         {
248             while (!feof (tfp))
249             {
250                 char *p = buf;
251                 n = fread (buf, 1, sizeof buf, tfp);
252                 nwrite = n;
253                 while (nwrite > 0)
254                 {
255                     n = fwrite (p, 1, nwrite, fp);
256                     nwrite -= n;
257                     p += n;
258                 }
259                 if (ferror (tfp))
260                     error (1, errno, "cannot read %s", CVSADM_TEMPLATE);
261             }
262             if (fclose (tfp) < 0)
263                 error (0, errno, "cannot close %s", CVSADM_TEMPLATE);
264         }
265     }
266
267     (void) fprintf (fp,
268   "%s----------------------------------------------------------------------\n",
269                     CVSEDITPREFIX);
270     (void) fprintf (fp,
271   "%sEnter Log.  Lines beginning with `%.*s' are removed automatically\n%s\n",
272                     CVSEDITPREFIX, CVSEDITPREFIXLEN, CVSEDITPREFIX,
273                     CVSEDITPREFIX);
274     if (dir != NULL && *dir)
275         (void) fprintf (fp, "%sCommitting in %s\n%s\n", CVSEDITPREFIX,
276                         dir, CVSEDITPREFIX);
277     if (changes != NULL)
278         setup_tmpfile (fp, CVSEDITPREFIX, changes);
279     (void) fprintf (fp,
280   "%s----------------------------------------------------------------------\n",
281                     CVSEDITPREFIX);
282
283     /* finish off the temp file */
284     if (fclose (fp) == EOF)
285         error (1, errno, "%s", fname);
286     if (stat (fname, &pre_stbuf) == -1)
287         pre_stbuf.st_mtime = 0;
288
289     /* run the editor */
290     run_setup (Editor);
291     run_add_arg (fname);
292     if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY,
293                              RUN_NORMAL | RUN_SIGIGNORE)) != 0)
294         error (0, retcode == -1 ? errno : 0, "warning: editor session failed");
295
296     /* put the entire message back into the *messagep variable */
297
298     fp = xfopen (fname, "r");
299
300     if (*messagep)
301         free (*messagep);
302
303     if (stat (fname, &post_stbuf) != 0)
304             error (1, errno, "cannot find size of temp file %s", fname);
305
306     if (post_stbuf.st_size == 0)
307         *messagep = NULL;
308     else
309     {
310         /* On NT, we might read less than st_size bytes, but we won't
311            read more.  So this works.  */
312         *messagep = (char *) xmalloc (post_stbuf.st_size + 1);
313         (*messagep)[0] = '\0';
314     }
315
316     line = NULL;
317     line_chars_allocated = 0;
318
319     if (*messagep)
320     {
321         size_t message_len = post_stbuf.st_size + 1;
322         size_t offset = 0;
323         while (1)
324         {
325             line_length = getline (&line, &line_chars_allocated, fp);
326             if (line_length == -1)
327             {
328                 if (ferror (fp))
329                     error (0, errno, "warning: cannot read %s", fname);
330                 break;
331             }
332             if (strncmp (line, CVSEDITPREFIX, CVSEDITPREFIXLEN) == 0)
333                 continue;
334             if (offset + line_length >= message_len)
335                 expand_string (messagep, &message_len,
336                                 offset + line_length + 1);
337             (void) strcpy (*messagep + offset, line);
338             offset += line_length;
339         }
340     }
341     if (fclose (fp) < 0)
342         error (0, errno, "warning: cannot close %s", fname);
343
344     /* canonicalize emply messages */
345     if (*messagep != NULL &&
346         (**messagep == '\0' || strcmp (*messagep, "\n") == 0))
347     {
348         free (*messagep);
349         *messagep = NULL;
350     }
351
352     if (pre_stbuf.st_mtime == post_stbuf.st_mtime || *messagep == NULL)
353     {
354         for (;;)
355         {
356             (void) printf ("\nLog message unchanged or not specified\n");
357             (void) printf ("a)bort, c)ontinue, e)dit, !)reuse this message unchanged for remaining dirs\n");
358             (void) printf ("Action: (continue) ");
359             (void) fflush (stdout);
360             line_length = getline (&line, &line_chars_allocated, stdin);
361             if (line_length < 0)
362             {
363                 error (0, errno, "cannot read from stdin");
364                 if (unlink_file (fname) < 0)
365                     error (0, errno,
366                            "warning: cannot remove temp file %s", fname);
367                 error (1, 0, "aborting");
368             }
369             else if (line_length == 0
370                      || *line == '\n' || *line == 'c' || *line == 'C')
371                 break;
372             if (*line == 'a' || *line == 'A')
373                 {
374                     if (unlink_file (fname) < 0)
375                         error (0, errno, "warning: cannot remove temp file %s", fname);
376                     error (1, 0, "aborted by user");
377                 }
378             if (*line == 'e' || *line == 'E')
379                 goto again;
380             if (*line == '!')
381             {
382                 reuse_log_message = 1;
383                 break;
384             }
385             (void) printf ("Unknown input\n");
386         }
387     }
388     if (line)
389         free (line);
390     if (unlink_file (fname) < 0)
391         error (0, errno, "warning: cannot remove temp file %s", fname);
392     free (fname);
393 }
394
395 /* Runs the user-defined verification script as part of the commit or import 
396    process.  This verification is meant to be run whether or not the user 
397    included the -m attribute.  unlike the do_editor function, this is 
398    independant of the running of an editor for getting a message.
399  */
400 void
401 do_verify (char **messagep, const char *repository, List *changes)
402 {
403     int err;
404     struct verifymsg_proc_data data;
405     struct stat post_stbuf;
406
407     if (current_parsed_root->isremote)
408         /* The verification will happen on the server.  */
409         return;
410
411     /* FIXME? Do we really want to skip this on noexec?  What do we do
412        for the other administrative files?  */
413     /* EXPLAIN: Why do we check for repository == NULL here? */
414     if (noexec || repository == NULL)
415         return;
416
417     /* Get the name of the verification script to run  */
418
419     data.message = *messagep;
420     data.fname = NULL;
421     data.changes = changes;
422     if ((err = Parse_Info (CVSROOTADM_VERIFYMSG, repository,
423                           verifymsg_proc, 0, &data)) != 0)
424     {
425         int saved_errno = errno;
426         /* Since following error() exits, delete the temp file now.  */
427         if (data.fname != NULL && unlink_file( data.fname ) < 0)
428             error (0, errno, "cannot remove %s", data.fname);
429         free (data.fname);
430
431         errno = saved_errno;
432         error (1, err == -1 ? errno : 0, "Message verification failed");
433     }
434
435     /* Return if no temp file was created.  That means that we didn't call any
436      * verifymsg scripts.
437      */
438     if (data.fname == NULL)
439         return;
440
441     /* Get the mod time and size of the possibly new log message
442      * in always and stat modes.
443      */
444     if (config->RereadLogAfterVerify == LOGMSG_REREAD_ALWAYS ||
445         config->RereadLogAfterVerify == LOGMSG_REREAD_STAT)
446     {
447         if(stat (data.fname, &post_stbuf) != 0)
448             error (1, errno, "cannot find size of temp file %s", data.fname);
449     }
450
451     /* And reread the log message in `always' mode or in `stat' mode when it's
452      * changed.
453      */
454     if (config->RereadLogAfterVerify == LOGMSG_REREAD_ALWAYS ||
455         (config->RereadLogAfterVerify == LOGMSG_REREAD_STAT &&
456           (data.pre_stbuf.st_mtime != post_stbuf.st_mtime ||
457             data.pre_stbuf.st_size != post_stbuf.st_size)))
458     {
459         /* put the entire message back into the *messagep variable */
460
461         if (*messagep) free (*messagep);
462
463         if (post_stbuf.st_size == 0)
464             *messagep = NULL;
465         else
466         {
467             char *line = NULL;
468             int line_length;
469             size_t line_chars_allocated = 0;
470             char *p;
471             FILE *fp;
472
473             fp = xfopen (data.fname, "r");
474
475             /* On NT, we might read less than st_size bytes,
476                but we won't read more.  So this works.  */
477             p = *messagep = (char *) xmalloc (post_stbuf.st_size + 1);
478             *messagep[0] = '\0';
479
480             for (;;)
481             {
482                 line_length = getline( &line,
483                                        &line_chars_allocated,
484                                        fp);
485                 if (line_length == -1)
486                 {
487                     if (ferror (fp))
488                         /* Fail in this case because otherwise we will have no
489                          * log message
490                          */
491                         error (1, errno, "cannot read %s", data.fname);
492                     break;
493                 }
494                 if (strncmp (line, CVSEDITPREFIX, CVSEDITPREFIXLEN) == 0)
495                     continue;
496                 (void) strcpy (p, line);
497                 p += line_length;
498             }
499             if (line) free (line);
500             if (fclose (fp) < 0)
501                 error (0, errno, "warning: cannot close %s", data.fname);
502         }
503     }
504     /* Delete the temp file  */
505     if (unlink_file (data.fname) < 0)
506         error (0, errno, "cannot remove `%s'", data.fname);
507     free (data.fname);
508 }
509
510
511
512 /*
513  * callback proc for Parse_Info for rcsinfo templates this routine basically
514  * copies the matching template onto the end of the tempfile we are setting
515  * up
516  */
517 /* ARGSUSED */
518 static int
519 rcsinfo_proc (const char *repository, const char *template, void *closure)
520 {
521     static char *last_template;
522     FILE *tfp;
523
524     /* nothing to do if the last one included is the same as this one */
525     if (last_template && strcmp (last_template, template) == 0)
526         return (0);
527     if (last_template)
528         free (last_template);
529     last_template = xstrdup (template);
530
531     if ((tfp = CVS_FOPEN (template, "r")) != NULL)
532     {
533         char *line = NULL;
534         size_t line_chars_allocated = 0;
535
536         while (getline (&line, &line_chars_allocated, tfp) >= 0)
537             (void) fputs (line, fp);
538         if (ferror (tfp))
539             error (0, errno, "warning: cannot read %s", template);
540         if (fclose (tfp) < 0)
541             error (0, errno, "warning: cannot close %s", template);
542         if (line)
543             free (line);
544         return (0);
545     }
546     else
547     {
548         error (0, errno, "Couldn't open rcsinfo template file %s", template);
549         return (1);
550     }
551 }
552
553 /*
554  * Uses setup_tmpfile() to pass the updated message on directly to any
555  * logfile programs that have a regular expression match for the checked in
556  * directory in the source repository.  The log information is fed into the
557  * specified program as standard input.
558  */
559 struct ulp_data {
560     FILE *logfp;
561     const char *message;
562     List *changes;
563 };
564
565
566
567 void
568 Update_Logfile (const char *repository, const char *xmessage, FILE *xlogfp,
569                 List *xchanges)
570 {
571     struct ulp_data ud;
572
573     /* nothing to do if the list is empty */
574     if (xchanges == NULL || xchanges->list->next == xchanges->list)
575         return;
576
577     /* set up vars for update_logfile_proc */
578     ud.message = xmessage;
579     ud.logfp = xlogfp;
580     ud.changes = xchanges;
581
582     /* call Parse_Info to do the actual logfile updates */
583     (void) Parse_Info (CVSROOTADM_LOGINFO, repository, update_logfile_proc,
584                        PIOPT_ALL, &ud);
585 }
586
587
588
589 /*
590  * callback proc to actually do the logfile write from Update_Logfile
591  */
592 static int
593 update_logfile_proc (const char *repository, const char *filter, void *closure)
594 {
595     struct ulp_data *udp = closure;
596     TRACE (TRACE_FUNCTION, "update_logfile_proc(%s,%s)", repository, filter);
597     return logfile_write (repository, filter, udp->message, udp->logfp,
598                           udp->changes);
599 }
600
601
602
603 /* static int
604  * logmsg_list_to_args_proc( Node *p, void *closure )
605  * This function is intended to be passed into walklist() with a list of tags
606  * (nodes in the same format as pretag_list_proc() accepts - p->key = tagname
607  * and p->data = a revision.
608  *
609  * closure will be a struct format_cmdline_walklist_closure
610  * where closure is undefined.
611  */
612 static int
613 logmsg_list_to_args_proc (Node *p, void *closure)
614 {
615     struct format_cmdline_walklist_closure *c = closure;
616     struct logfile_info *li;
617     char *arg = NULL;
618     const char *f;
619     char *d;
620     size_t doff;
621
622     if (p->data == NULL) return 1;
623
624     f = c->format;
625     d = *c->d;
626     /* foreach requested attribute */
627     while (*f)
628     {
629         switch (*f++)
630         {
631             case 's':
632                 arg = p->key;
633                 break;
634             case 'T':
635                 li = p->data;
636                 arg = li->tag ? li->tag : "";
637                 break;
638             case 'V':
639                 li = p->data;
640                 arg = li->rev_old ? li->rev_old : "NONE";
641                 break;
642             case 'v':
643                 li = p->data;
644                 arg = li->rev_new ? li->rev_new : "NONE";
645                 break;
646             default:
647 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
648                 if (c->onearg)
649                 {
650                     /* The old deafult was to print the empty string for
651                      * unknown args.
652                      */
653                     arg = "\0";
654                 }
655                 else
656 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
657                     error (1, 0,
658                            "Unknown format character or not a list attribute: %c", f[-1]);
659                 /* NOTREACHED */
660                 break;
661         }
662         /* copy the attribute into an argument */
663 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
664         if (c->onearg)
665         {
666             if (c->firstpass)
667             {
668                 c->firstpass = 0;
669                 doff = d - *c->buf;
670                 expand_string (c->buf, c->length,
671                                doff + strlen (c->srepos) + 1);
672                 d = *c->buf + doff;
673                 strncpy (d, c->srepos, strlen (c->srepos));
674                 d += strlen (c->srepos);
675                 *d++ = ' ';
676             }
677         }
678         else /* c->onearg */
679 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
680         {
681             if (c->quotes)
682             {
683                 arg = cmdlineescape (c->quotes, arg);
684             }
685             else
686             {
687                 arg = cmdlinequote ('"', arg);
688             }
689         } /* !c->onearg */
690         doff = d - *c->buf;
691         expand_string (c->buf, c->length, doff + strlen (arg));
692         d = *c->buf + doff;
693         strncpy (d, arg, strlen (arg));
694         d += strlen (arg);
695 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
696         if (!c->onearg)
697 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
698             free (arg);
699
700         /* Always put the extra space on.  we'll have to back up a char
701          * when we're done, but that seems most efficient.
702          */
703         doff = d - *c->buf;
704         expand_string (c->buf, c->length, doff + 1);
705         d = *c->buf + doff;
706 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
707         if (c->onearg && *f) *d++ = ',';
708         else
709 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
710             *d++ = ' ';
711     }
712     /* correct our original pointer into the buff */
713     *c->d = d;
714     return 0;
715 }
716
717
718
719 /*
720  * Writes some stuff to the logfile "filter" and returns the status of the
721  * filter program.
722  */
723 static int
724 logfile_write (const char *repository, const char *filter, const char *message,
725                FILE *logfp, List *changes)
726 {
727     char *cmdline;
728     FILE *pipefp;
729     char *cp;
730     int c;
731     int pipestatus;
732     const char *srepos = Short_Repository (repository);
733
734     assert (repository);
735
736     /* The user may specify a format string as part of the filter.
737        Originally, `%s' was the only valid string.  The string that
738        was substituted for it was:
739
740          <repository-name> <file1> <file2> <file3> ...
741
742        Each file was either a new directory/import (T_TITLE), or a
743        added (T_ADDED), modified (T_MODIFIED), or removed (T_REMOVED)
744        file.
745
746        It is desirable to preserve that behavior so lots of commitlog
747        scripts won't die when they get this new code.  At the same
748        time, we'd like to pass other information about the files (like
749        version numbers, statuses, or checkin times).
750
751        The solution is to allow a format string that allows us to
752        specify those other pieces of information.  The format string
753        will be composed of `%' followed by a single format character,
754        or followed by a set of format characters surrounded by `{' and
755        `}' as separators.  The format characters are:
756
757          s = file name
758          V = old version number (pre-checkin)
759          v = new version number (post-checkin)
760
761        For example, valid format strings are:
762
763          %{}
764          %s
765          %{s}
766          %{sVv}
767
768        There's no reason that more items couldn't be added (like
769        modification date or file status [added, modified, updated,
770        etc.]) -- the code modifications would be minimal (logmsg.c
771        (title_proc) and commit.c (check_fileproc)).
772
773        The output will be a string of tokens separated by spaces.  For
774        backwards compatibility, the the first token will be the
775        repository name.  The rest of the tokens will be
776        comma-delimited lists of the information requested in the
777        format string.  For example, if `/u/src/master' is the
778        repository, `%{sVv}' is the format string, and three files
779        (ChangeLog, Makefile, foo.c) were modified, the output might
780        be:
781
782          /u/src/master ChangeLog,1.1,1.2 Makefile,1.3,1.4 foo.c,1.12,1.13
783
784        Why this duplicates the old behavior when the format string is
785        `%s' is left as an exercise for the reader. */
786
787     /* %c = cvs_cmd_name
788      * %p = shortrepos
789      * %r = repository
790      * %{sVv} = file name, old revision (precommit), new revision (postcommit)
791      */
792     /*
793      * Cast any NULL arguments as appropriate pointers as this is an
794      * stdarg function and we need to be certain the caller gets what
795      * is expected.
796      */
797     cmdline = format_cmdline (
798 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
799                               !config->UseNewInfoFmtStrings, srepos,
800 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
801                               filter,
802                               "c", "s", cvs_cmd_name,
803 #ifdef SERVER_SUPPORT
804                               "R", "s", referrer ? referrer->original : "NONE",
805 #endif /* SERVER_SUPPORT */
806                               "p", "s", srepos,
807                               "r", "s", current_parsed_root->directory,
808                               "sVv", ",", changes,
809                               logmsg_list_to_args_proc, (void *) NULL,
810                               (char *) NULL);
811     if (!cmdline || !strlen (cmdline))
812     {
813         if (cmdline) free (cmdline);
814         error (0, 0, "logmsg proc resolved to the empty string!");
815         return 1;
816     }
817
818     if ((pipefp = run_popen (cmdline, "w")) == NULL)
819     {
820         if (!noexec)
821             error (0, 0, "cannot write entry to log filter: %s", cmdline);
822         free (cmdline);
823         return 1;
824     }
825     (void) fprintf (pipefp, "Update of %s\n", repository);
826     (void) fprintf (pipefp, "In directory %s:", hostname);
827     cp = xgetcwd ();
828     if (cp == NULL)
829         fprintf (pipefp, "<cannot get working directory: %s>\n\n",
830                  strerror (errno));
831     else
832     {
833         fprintf (pipefp, "%s\n\n", cp);
834         free (cp);
835     }
836
837     setup_tmpfile (pipefp, "", changes);
838     (void) fprintf (pipefp, "Log Message:\n%s\n", (message) ? message : "");
839     if (logfp)
840     {
841         (void) fprintf (pipefp, "Status:\n");
842         rewind (logfp);
843         while ((c = getc (logfp)) != EOF)
844             (void) putc (c, pipefp);
845     }
846     free (cmdline);
847     pipestatus = pclose (pipefp);
848     return ((pipestatus == -1) || (pipestatus == 127)) ? 1 : 0;
849 }
850
851
852
853 /*  This routine is called by Parse_Info.  It runs the
854  *  message verification script.
855  */
856 static int
857 verifymsg_proc (const char *repository, const char *script, void *closure)
858 {
859     char *verifymsg_script;
860 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
861     char *newscript = NULL;
862 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
863     struct verifymsg_proc_data *vpd = closure;
864     const char *srepos = Short_Repository (repository);
865
866 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
867     if (!strchr (script, '%'))
868     {
869         error (0, 0,
870                "warning: verifymsg line doesn't contain any format strings:\n"
871                "    \"%s\"\n"
872                "Appending default format string (\" %%l\"), but be aware that this usage is\n"
873                "deprecated.", script);
874         script = newscript = Xasprintf ("%s %%l", script);
875     }
876 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
877
878     /* If we don't already have one, open a temporary file, write the message
879      * to the temp file, and close the file.
880      *
881      * We do this here so that we only create the file when there is a
882      * verifymsg script specified and we only create it once when there is
883      * more than one verifymsg script specified.
884      */
885     if (vpd->fname == NULL)
886     {
887         FILE *fp;
888         if ((fp = cvs_temp_file (&(vpd->fname))) == NULL)
889             error (1, errno, "cannot create temporary file %s", vpd->fname);
890
891         if (vpd->message != NULL)
892             fputs (vpd->message, fp);
893         if (vpd->message == NULL ||
894             (vpd->message)[0] == '\0' ||
895             (vpd->message)[strlen (vpd->message) - 1] != '\n')
896             putc ('\n', fp);
897         if (fclose (fp) == EOF)
898             error (1, errno, "%s", vpd->fname);
899
900         if (config->RereadLogAfterVerify == LOGMSG_REREAD_STAT)
901         {
902             /* Remember the status of the temp file for later */
903             if (stat (vpd->fname, &(vpd->pre_stbuf)) != 0)
904                 error (1, errno, "cannot stat temp file %s", vpd->fname);
905
906             /*
907              * See if we need to sleep before running the verification
908              * script to avoid time-stamp races.
909              */
910             sleep_past (vpd->pre_stbuf.st_mtime);
911         }
912     } /* if (vpd->fname == NULL) */
913
914     /*
915      * Cast any NULL arguments as appropriate pointers as this is an
916      * stdarg function and we need to be certain the caller gets what
917      * is expected.
918      */
919     verifymsg_script = format_cmdline (
920 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
921                                        false, srepos,
922 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
923                                        script,
924                                        "c", "s", cvs_cmd_name,
925 #ifdef SERVER_SUPPORT
926                                        "R", "s", referrer
927                                        ? referrer->original : "NONE",
928 #endif /* SERVER_SUPPORT */
929                                        "p", "s", srepos,
930                                        "r", "s",
931                                        current_parsed_root->directory,
932                                        "l", "s", vpd->fname,
933                                        "sV", ",", vpd->changes,
934                                        logmsg_list_to_args_proc, (void *) NULL,
935                                        (char *) NULL);
936
937 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
938     if (newscript) free (newscript);
939 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
940
941     if (!verifymsg_script || !strlen (verifymsg_script))
942     {
943         if (verifymsg_script) free (verifymsg_script);
944         verifymsg_script = NULL;
945         error (0, 0, "verifymsg proc resolved to the empty string!");
946         return 1;
947     }
948
949     run_setup (verifymsg_script);
950
951     free (verifymsg_script);
952
953     /* FIXME - because run_exec can return negative values and Parse_Info adds
954      * the values of each call to this function to get a total error, we are
955      * calling abs on the value of run_exec to ensure two errors do not sum to
956      * zero.
957      *
958      * The only REALLY obnoxious thing about this, I guess, is that a -1 return
959      * code from run_exec can mean we failed to call the process for some
960      * reason and should care about errno or that the process we called
961      * returned -1 and the value of errno is undefined.  In other words,
962      * run_exec should probably be rewritten to have two return codes.  one
963      * which is its own exit status and one which is the child process's.  So
964      * there.  :P
965      *
966      * Once run_exec is returning two error codes, we should probably be
967      * failing here with an error message including errno when we get the
968      * return code which means we care about errno, in case you missed that
969      * little tidbit.
970      *
971      * I do happen to know we just fail for a non-zero value anyway and I
972      * believe the docs actually state that if the verifymsg_proc returns a
973      * "non-zero" value we will fail.
974      */
975     return abs (run_exec (RUN_TTY, RUN_TTY, RUN_TTY,
976                           RUN_NORMAL | RUN_SIGIGNORE));
977 }