2 * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
4 * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
7 * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
8 * Portions Copyright (C) 1989-1992, Brian Berliner
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.
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,
25 static int update_logfile_proc (const char *repository, const char *filter,
27 static void setup_tmpfile (FILE * xfp, char *xprefix, List * changes);
28 static int verifymsg_proc (const char *repository, const char *script,
34 struct verifymsg_proc_data
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.
43 /* The initial message text to be verified.
46 /* The initial stats of the temp file so we can tell that the temp file has
47 * been changed when RereadLogAfterVerify is STAT.
49 struct stat pre_stbuf;
50 /* The list of files being changed, with new and old version numbers.
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. */
63 setup_tmpfile (FILE *xfp, char *xprefix, List *changes)
70 if (walklist (changes, find_type, NULL) != 0)
72 (void) fprintf (fp, "%sModified Files:\n", prefix);
74 (void) walklist (changes, fmt_proc, NULL);
75 (void) fprintf (fp, "\n");
83 if (walklist (changes, find_type, NULL) != 0)
85 (void) fprintf (fp, "%sAdded Files:\n", prefix);
87 (void) walklist (changes, fmt_proc, NULL);
88 (void) fprintf (fp, "\n");
96 if (walklist (changes, find_type, NULL) != 0)
98 (void) fprintf (fp, "%sRemoved Files:\n", prefix);
100 (void) walklist (changes, fmt_proc, NULL);
101 (void) fprintf (fp, "\n");
111 * Looks for nodes of a specified type and returns 1 if found
114 find_type (Node *p, void *closure)
116 struct logfile_info *li = p->data;
118 if (li->type == type)
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
130 fmt_proc (Node *p, void *closure)
132 struct logfile_info *li;
135 if (li->type == type)
139 : tag == NULL || strcmp (tag, li->tag) != 0)
142 (void) fprintf (fp, "\n");
143 (void) fputs (prefix, fp);
144 col = strlen (prefix);
147 (void) fprintf (fp, " ");
152 (void) fprintf (fp, "No tag");
154 (void) fprintf (fp, "Tag: %s", li->tag);
158 tag = xstrdup (li->tag);
160 /* Force a new line. */
166 (void) fprintf (fp, "%s\t", prefix);
169 else if (col > 8 && (col + (int) strlen (p->key)) > 70)
171 (void) fprintf (fp, "\n%s\t", prefix);
174 (void) fprintf (fp, "%s ", p->key);
175 col += strlen (p->key) + 1;
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.
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.
190 * Editor Set to a default value by configure and overridable using the
191 * -e option to the CVS executable.
194 do_editor (const char *dir, char **messagep, const char *repository,
197 static int reuse_log_message = 0;
200 size_t line_chars_allocated;
202 struct stat pre_stbuf, post_stbuf;
205 assert (!current_parsed_root->isremote != !repository);
207 if (noexec || reuse_log_message)
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");
215 /* Create a temporary file. */
216 if( ( fp = cvs_temp_file( &fname ) ) == NULL )
217 error( 1, errno, "cannot create temporary file" );
221 (void) fputs (*messagep, fp);
223 if ((*messagep)[0] == '\0' ||
224 (*messagep)[strlen (*messagep) - 1] != '\n')
225 (void) fprintf (fp, "\n");
228 if (repository != NULL)
229 /* tack templates on if necessary */
230 (void) Parse_Info (CVSROOTADM_RCSINFO, repository, rcsinfo_proc,
240 tfp = CVS_FOPEN (CVSADM_TEMPLATE, "rb");
243 if (!existence_error (errno))
244 error (1, errno, "cannot read %s", CVSADM_TEMPLATE);
251 n = fread (buf, 1, sizeof buf, tfp);
255 n = fwrite (p, 1, nwrite, fp);
260 error (1, errno, "cannot read %s", CVSADM_TEMPLATE);
262 if (fclose (tfp) < 0)
263 error (0, errno, "cannot close %s", CVSADM_TEMPLATE);
268 "%s----------------------------------------------------------------------\n",
271 "%sEnter Log. Lines beginning with `%.*s' are removed automatically\n%s\n",
272 CVSEDITPREFIX, CVSEDITPREFIXLEN, CVSEDITPREFIX,
274 if (dir != NULL && *dir)
275 (void) fprintf (fp, "%sCommitting in %s\n%s\n", CVSEDITPREFIX,
278 setup_tmpfile (fp, CVSEDITPREFIX, changes);
280 "%s----------------------------------------------------------------------\n",
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;
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");
296 /* put the entire message back into the *messagep variable */
298 fp = xfopen (fname, "r");
303 if (stat (fname, &post_stbuf) != 0)
304 error (1, errno, "cannot find size of temp file %s", fname);
306 if (post_stbuf.st_size == 0)
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';
317 line_chars_allocated = 0;
321 size_t message_len = post_stbuf.st_size + 1;
325 line_length = getline (&line, &line_chars_allocated, fp);
326 if (line_length == -1)
329 error (0, errno, "warning: cannot read %s", fname);
332 if (strncmp (line, CVSEDITPREFIX, CVSEDITPREFIXLEN) == 0)
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;
342 error (0, errno, "warning: cannot close %s", fname);
344 /* canonicalize emply messages */
345 if (*messagep != NULL &&
346 (**messagep == '\0' || strcmp (*messagep, "\n") == 0))
352 if (pre_stbuf.st_mtime == post_stbuf.st_mtime || *messagep == NULL)
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);
363 error (0, errno, "cannot read from stdin");
364 if (unlink_file (fname) < 0)
366 "warning: cannot remove temp file %s", fname);
367 error (1, 0, "aborting");
369 else if (line_length == 0
370 || *line == '\n' || *line == 'c' || *line == 'C')
372 if (*line == 'a' || *line == 'A')
374 if (unlink_file (fname) < 0)
375 error (0, errno, "warning: cannot remove temp file %s", fname);
376 error (1, 0, "aborted by user");
378 if (*line == 'e' || *line == 'E')
382 reuse_log_message = 1;
385 (void) printf ("Unknown input\n");
390 if (unlink_file (fname) < 0)
391 error (0, errno, "warning: cannot remove temp file %s", fname);
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.
401 do_verify (char **messagep, const char *repository, List *changes)
404 struct verifymsg_proc_data data;
405 struct stat post_stbuf;
407 if (current_parsed_root->isremote)
408 /* The verification will happen on the server. */
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)
417 /* Get the name of the verification script to run */
419 data.message = *messagep;
421 data.changes = changes;
422 if ((err = Parse_Info (CVSROOTADM_VERIFYMSG, repository,
423 verifymsg_proc, 0, &data)) != 0)
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);
432 error (1, err == -1 ? errno : 0, "Message verification failed");
435 /* Return if no temp file was created. That means that we didn't call any
438 if (data.fname == NULL)
441 /* Get the mod time and size of the possibly new log message
442 * in always and stat modes.
444 if (config->RereadLogAfterVerify == LOGMSG_REREAD_ALWAYS ||
445 config->RereadLogAfterVerify == LOGMSG_REREAD_STAT)
447 if(stat (data.fname, &post_stbuf) != 0)
448 error (1, errno, "cannot find size of temp file %s", data.fname);
451 /* And reread the log message in `always' mode or in `stat' mode when it's
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)))
459 /* put the entire message back into the *messagep variable */
461 if (*messagep) free (*messagep);
463 if (post_stbuf.st_size == 0)
469 size_t line_chars_allocated = 0;
473 fp = xfopen (data.fname, "r");
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);
482 line_length = getline( &line,
483 &line_chars_allocated,
485 if (line_length == -1)
488 /* Fail in this case because otherwise we will have no
491 error (1, errno, "cannot read %s", data.fname);
494 if (strncmp (line, CVSEDITPREFIX, CVSEDITPREFIXLEN) == 0)
496 (void) strcpy (p, line);
499 if (line) free (line);
501 error (0, errno, "warning: cannot close %s", data.fname);
504 /* Delete the temp file */
505 if (unlink_file (data.fname) < 0)
506 error (0, errno, "cannot remove `%s'", data.fname);
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
519 rcsinfo_proc (const char *repository, const char *template, void *closure)
521 static char *last_template;
524 /* nothing to do if the last one included is the same as this one */
525 if (last_template && strcmp (last_template, template) == 0)
528 free (last_template);
529 last_template = xstrdup (template);
531 if ((tfp = CVS_FOPEN (template, "r")) != NULL)
534 size_t line_chars_allocated = 0;
536 while (getline (&line, &line_chars_allocated, tfp) >= 0)
537 (void) fputs (line, fp);
539 error (0, errno, "warning: cannot read %s", template);
540 if (fclose (tfp) < 0)
541 error (0, errno, "warning: cannot close %s", template);
548 error (0, errno, "Couldn't open rcsinfo template file %s", template);
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.
568 Update_Logfile (const char *repository, const char *xmessage, FILE *xlogfp,
573 /* nothing to do if the list is empty */
574 if (xchanges == NULL || xchanges->list->next == xchanges->list)
577 /* set up vars for update_logfile_proc */
578 ud.message = xmessage;
580 ud.changes = xchanges;
582 /* call Parse_Info to do the actual logfile updates */
583 (void) Parse_Info (CVSROOTADM_LOGINFO, repository, update_logfile_proc,
590 * callback proc to actually do the logfile write from Update_Logfile
593 update_logfile_proc (const char *repository, const char *filter, void *closure)
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,
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.
609 * closure will be a struct format_cmdline_walklist_closure
610 * where closure is undefined.
613 logmsg_list_to_args_proc (Node *p, void *closure)
615 struct format_cmdline_walklist_closure *c = closure;
616 struct logfile_info *li;
622 if (p->data == NULL) return 1;
626 /* foreach requested attribute */
636 arg = li->tag ? li->tag : "";
640 arg = li->rev_old ? li->rev_old : "NONE";
644 arg = li->rev_new ? li->rev_new : "NONE";
647 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
650 /* The old deafult was to print the empty string for
656 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
658 "Unknown format character or not a list attribute: %c", f[-1]);
662 /* copy the attribute into an argument */
663 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
670 expand_string (c->buf, c->length,
671 doff + strlen (c->srepos) + 1);
673 strncpy (d, c->srepos, strlen (c->srepos));
674 d += strlen (c->srepos);
679 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
683 arg = cmdlineescape (c->quotes, arg);
687 arg = cmdlinequote ('"', arg);
691 expand_string (c->buf, c->length, doff + strlen (arg));
693 strncpy (d, arg, strlen (arg));
695 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
697 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
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.
704 expand_string (c->buf, c->length, doff + 1);
706 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
707 if (c->onearg && *f) *d++ = ',';
709 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
712 /* correct our original pointer into the buff */
720 * Writes some stuff to the logfile "filter" and returns the status of the
724 logfile_write (const char *repository, const char *filter, const char *message,
725 FILE *logfp, List *changes)
732 const char *srepos = Short_Repository (repository);
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:
740 <repository-name> <file1> <file2> <file3> ...
742 Each file was either a new directory/import (T_TITLE), or a
743 added (T_ADDED), modified (T_MODIFIED), or removed (T_REMOVED)
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).
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:
758 V = old version number (pre-checkin)
759 v = new version number (post-checkin)
761 For example, valid format strings are:
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)).
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
782 /u/src/master ChangeLog,1.1,1.2 Makefile,1.3,1.4 foo.c,1.12,1.13
784 Why this duplicates the old behavior when the format string is
785 `%s' is left as an exercise for the reader. */
790 * %{sVv} = file name, old revision (precommit), new revision (postcommit)
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
797 cmdline = format_cmdline (
798 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
799 !config->UseNewInfoFmtStrings, srepos,
800 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
802 "c", "s", cvs_cmd_name,
803 #ifdef SERVER_SUPPORT
804 "R", "s", referrer ? referrer->original : "NONE",
805 #endif /* SERVER_SUPPORT */
807 "r", "s", current_parsed_root->directory,
809 logmsg_list_to_args_proc, (void *) NULL,
811 if (!cmdline || !strlen (cmdline))
813 if (cmdline) free (cmdline);
814 error (0, 0, "logmsg proc resolved to the empty string!");
818 if ((pipefp = run_popen (cmdline, "w")) == NULL)
821 error (0, 0, "cannot write entry to log filter: %s", cmdline);
825 (void) fprintf (pipefp, "Update of %s\n", repository);
826 (void) fprintf (pipefp, "In directory %s:", hostname);
829 fprintf (pipefp, "<cannot get working directory: %s>\n\n",
833 fprintf (pipefp, "%s\n\n", cp);
837 setup_tmpfile (pipefp, "", changes);
838 (void) fprintf (pipefp, "Log Message:\n%s\n", (message) ? message : "");
841 (void) fprintf (pipefp, "Status:\n");
843 while ((c = getc (logfp)) != EOF)
844 (void) putc (c, pipefp);
847 pipestatus = pclose (pipefp);
848 return ((pipestatus == -1) || (pipestatus == 127)) ? 1 : 0;
853 /* This routine is called by Parse_Info. It runs the
854 * message verification script.
857 verifymsg_proc (const char *repository, const char *script, void *closure)
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);
866 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
867 if (!strchr (script, '%'))
870 "warning: verifymsg line doesn't contain any format strings:\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);
876 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
878 /* If we don't already have one, open a temporary file, write the message
879 * to the temp file, and close the file.
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.
885 if (vpd->fname == NULL)
888 if ((fp = cvs_temp_file (&(vpd->fname))) == NULL)
889 error (1, errno, "cannot create temporary file %s", vpd->fname);
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')
897 if (fclose (fp) == EOF)
898 error (1, errno, "%s", vpd->fname);
900 if (config->RereadLogAfterVerify == LOGMSG_REREAD_STAT)
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);
907 * See if we need to sleep before running the verification
908 * script to avoid time-stamp races.
910 sleep_past (vpd->pre_stbuf.st_mtime);
912 } /* if (vpd->fname == NULL) */
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
919 verifymsg_script = format_cmdline (
920 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
922 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
924 "c", "s", cvs_cmd_name,
925 #ifdef SERVER_SUPPORT
927 ? referrer->original : "NONE",
928 #endif /* SERVER_SUPPORT */
931 current_parsed_root->directory,
932 "l", "s", vpd->fname,
933 "sV", ",", vpd->changes,
934 logmsg_list_to_args_proc, (void *) NULL,
937 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
938 if (newscript) free (newscript);
939 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
941 if (!verifymsg_script || !strlen (verifymsg_script))
943 if (verifymsg_script) free (verifymsg_script);
944 verifymsg_script = NULL;
945 error (0, 0, "verifymsg proc resolved to the empty string!");
949 run_setup (verifymsg_script);
951 free (verifymsg_script);
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
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
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
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.
975 return abs (run_exec (RUN_TTY, RUN_TTY, RUN_TTY,
976 RUN_NORMAL | RUN_SIGIGNORE));