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 (void) fprintf (fp
, "\n");
230 if (repository
!= NULL
)
231 /* tack templates on if necessary */
232 (void) Parse_Info (CVSROOTADM_RCSINFO
, repository
, rcsinfo_proc
,
242 tfp
= CVS_FOPEN (CVSADM_TEMPLATE
, "rb");
245 if (!existence_error (errno
))
246 error (1, errno
, "cannot read %s", CVSADM_TEMPLATE
);
253 n
= fread (buf
, 1, sizeof buf
, tfp
);
257 n
= fwrite (p
, 1, nwrite
, fp
);
262 error (1, errno
, "cannot read %s", CVSADM_TEMPLATE
);
264 if (fclose (tfp
) < 0)
265 error (0, errno
, "cannot close %s", CVSADM_TEMPLATE
);
270 "%s----------------------------------------------------------------------\n",
273 "%sEnter Log. Lines beginning with `%.*s' are removed automatically\n%s\n",
274 CVSEDITPREFIX
, CVSEDITPREFIXLEN
, CVSEDITPREFIX
,
276 if (dir
!= NULL
&& *dir
)
277 (void) fprintf (fp
, "%sCommitting in %s\n%s\n", CVSEDITPREFIX
,
280 setup_tmpfile (fp
, CVSEDITPREFIX
, changes
);
282 "%s----------------------------------------------------------------------\n",
285 /* finish off the temp file */
286 if (fclose (fp
) == EOF
)
287 error (1, errno
, "%s", fname
);
288 if (stat (fname
, &pre_stbuf
) == -1)
289 pre_stbuf
.st_mtime
= 0;
294 if ((retcode
= run_exec (RUN_TTY
, RUN_TTY
, RUN_TTY
,
295 RUN_NORMAL
| RUN_SIGIGNORE
| RUN_UNSETXID
)) != 0)
296 error (0, retcode
== -1 ? errno
: 0, "warning: editor session failed");
298 /* put the entire message back into the *messagep variable */
300 fp
= xfopen (fname
, "r");
305 if (stat (fname
, &post_stbuf
) != 0)
306 error (1, errno
, "cannot find size of temp file %s", fname
);
308 if (post_stbuf
.st_size
== 0)
312 /* On NT, we might read less than st_size bytes, but we won't
313 read more. So this works. */
314 *messagep
= (char *) xmalloc (post_stbuf
.st_size
+ 1);
315 (*messagep
)[0] = '\0';
319 line_chars_allocated
= 0;
323 size_t message_len
= post_stbuf
.st_size
+ 1;
327 line_length
= getline (&line
, &line_chars_allocated
, fp
);
328 if (line_length
== -1)
331 error (0, errno
, "warning: cannot read %s", fname
);
334 if (strncmp (line
, CVSEDITPREFIX
, CVSEDITPREFIXLEN
) == 0)
336 if (offset
+ line_length
>= message_len
)
337 expand_string (messagep
, &message_len
,
338 offset
+ line_length
+ 1);
339 (void) strcpy (*messagep
+ offset
, line
);
340 offset
+= line_length
;
344 error (0, errno
, "warning: cannot close %s", fname
);
346 /* canonicalize emply messages */
347 if (*messagep
!= NULL
&&
348 (**messagep
== '\0' || strcmp (*messagep
, "\n") == 0))
354 if (pre_stbuf
.st_mtime
== post_stbuf
.st_mtime
|| *messagep
== NULL
)
358 (void) printf ("\nLog message unchanged or not specified\n");
359 (void) printf ("a)bort, c)ontinue, e)dit, !)reuse this message unchanged for remaining dirs\n");
360 (void) printf ("Action: (continue) ");
361 (void) fflush (stdout
);
362 line_length
= getline (&line
, &line_chars_allocated
, stdin
);
365 error (0, errno
, "cannot read from stdin");
366 if (unlink_file (fname
) < 0)
368 "warning: cannot remove temp file %s", fname
);
369 error (1, 0, "aborting");
371 else if (line_length
== 0
372 || *line
== '\n' || *line
== 'c' || *line
== 'C')
374 if (*line
== 'a' || *line
== 'A')
376 if (unlink_file (fname
) < 0)
377 error (0, errno
, "warning: cannot remove temp file %s", fname
);
378 error (1, 0, "aborted by user");
380 if (*line
== 'e' || *line
== 'E')
384 reuse_log_message
= 1;
387 (void) printf ("Unknown input\n");
392 if (unlink_file (fname
) < 0)
393 error (0, errno
, "warning: cannot remove temp file %s", fname
);
397 /* Runs the user-defined verification script as part of the commit or import
398 process. This verification is meant to be run whether or not the user
399 included the -m attribute. unlike the do_editor function, this is
400 independant of the running of an editor for getting a message.
403 do_verify (char **messagep
, const char *repository
, List
*changes
)
406 struct verifymsg_proc_data data
;
407 struct stat post_stbuf
;
409 if (current_parsed_root
->isremote
)
410 /* The verification will happen on the server. */
413 /* FIXME? Do we really want to skip this on noexec? What do we do
414 for the other administrative files? */
415 /* EXPLAIN: Why do we check for repository == NULL here? */
416 if (noexec
|| repository
== NULL
)
419 /* Get the name of the verification script to run */
421 data
.message
= *messagep
;
423 data
.changes
= changes
;
424 if ((err
= Parse_Info (CVSROOTADM_VERIFYMSG
, repository
,
425 verifymsg_proc
, 0, &data
)) != 0)
427 int saved_errno
= errno
;
428 /* Since following error() exits, delete the temp file now. */
429 if (data
.fname
!= NULL
&& unlink_file( data
.fname
) < 0)
430 error (0, errno
, "cannot remove %s", data
.fname
);
434 error (1, err
== -1 ? errno
: 0, "Message verification failed");
437 /* Return if no temp file was created. That means that we didn't call any
440 if (data
.fname
== NULL
)
443 /* Get the mod time and size of the possibly new log message
444 * in always and stat modes.
446 if (config
->RereadLogAfterVerify
== LOGMSG_REREAD_ALWAYS
||
447 config
->RereadLogAfterVerify
== LOGMSG_REREAD_STAT
)
449 if(stat (data
.fname
, &post_stbuf
) != 0)
450 error (1, errno
, "cannot find size of temp file %s", data
.fname
);
453 /* And reread the log message in `always' mode or in `stat' mode when it's
456 if (config
->RereadLogAfterVerify
== LOGMSG_REREAD_ALWAYS
||
457 (config
->RereadLogAfterVerify
== LOGMSG_REREAD_STAT
&&
458 (data
.pre_stbuf
.st_mtime
!= post_stbuf
.st_mtime
||
459 data
.pre_stbuf
.st_size
!= post_stbuf
.st_size
)))
461 /* put the entire message back into the *messagep variable */
463 if (*messagep
) free (*messagep
);
465 if (post_stbuf
.st_size
== 0)
471 size_t line_chars_allocated
= 0;
475 fp
= xfopen (data
.fname
, "r");
477 /* On NT, we might read less than st_size bytes,
478 but we won't read more. So this works. */
479 p
= *messagep
= (char *) xmalloc (post_stbuf
.st_size
+ 1);
484 line_length
= getline( &line
,
485 &line_chars_allocated
,
487 if (line_length
== -1)
490 /* Fail in this case because otherwise we will have no
493 error (1, errno
, "cannot read %s", data
.fname
);
496 if (strncmp (line
, CVSEDITPREFIX
, CVSEDITPREFIXLEN
) == 0)
498 (void) strcpy (p
, line
);
501 if (line
) free (line
);
503 error (0, errno
, "warning: cannot close %s", data
.fname
);
506 /* Delete the temp file */
507 if (unlink_file (data
.fname
) < 0)
508 error (0, errno
, "cannot remove `%s'", data
.fname
);
515 * callback proc for Parse_Info for rcsinfo templates this routine basically
516 * copies the matching template onto the end of the tempfile we are setting
521 rcsinfo_proc (const char *repository
, const char *template, void *closure
)
523 static char *last_template
;
526 /* nothing to do if the last one included is the same as this one */
527 if (last_template
&& strcmp (last_template
, template) == 0)
530 free (last_template
);
531 last_template
= xstrdup (template);
533 if ((tfp
= CVS_FOPEN (template, "r")) != NULL
)
536 size_t line_chars_allocated
= 0;
538 while (getline (&line
, &line_chars_allocated
, tfp
) >= 0)
539 (void) fputs (line
, fp
);
541 error (0, errno
, "warning: cannot read %s", template);
542 if (fclose (tfp
) < 0)
543 error (0, errno
, "warning: cannot close %s", template);
550 error (0, errno
, "Couldn't open rcsinfo template file %s", template);
556 * Uses setup_tmpfile() to pass the updated message on directly to any
557 * logfile programs that have a regular expression match for the checked in
558 * directory in the source repository. The log information is fed into the
559 * specified program as standard input.
570 Update_Logfile (const char *repository
, const char *xmessage
, FILE *xlogfp
,
575 /* nothing to do if the list is empty */
576 if (xchanges
== NULL
|| xchanges
->list
->next
== xchanges
->list
)
579 /* set up vars for update_logfile_proc */
580 ud
.message
= xmessage
;
582 ud
.changes
= xchanges
;
584 /* call Parse_Info to do the actual logfile updates */
585 (void) Parse_Info (CVSROOTADM_LOGINFO
, repository
, update_logfile_proc
,
592 * callback proc to actually do the logfile write from Update_Logfile
595 update_logfile_proc (const char *repository
, const char *filter
, void *closure
)
597 struct ulp_data
*udp
= closure
;
598 TRACE (TRACE_FUNCTION
, "update_logfile_proc(%s,%s)", repository
, filter
);
599 return logfile_write (repository
, filter
, udp
->message
, udp
->logfp
,
606 * logmsg_list_to_args_proc( Node *p, void *closure )
607 * This function is intended to be passed into walklist() with a list of tags
608 * (nodes in the same format as pretag_list_proc() accepts - p->key = tagname
609 * and p->data = a revision.
611 * closure will be a struct format_cmdline_walklist_closure
612 * where closure is undefined.
615 logmsg_list_to_args_proc (Node
*p
, void *closure
)
617 struct format_cmdline_walklist_closure
*c
= closure
;
618 struct logfile_info
*li
;
624 if (p
->data
== NULL
) return 1;
628 /* foreach requested attribute */
639 arg
= li
->tag
? li
->tag
: "";
643 arg
= li
->rev_old
? li
->rev_old
: "NONE";
647 arg
= li
->rev_new
? li
->rev_new
: "NONE";
650 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
653 /* The old deafult was to print the empty string for
659 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
661 "Unknown format character or not a list attribute: %c", f
[-1]);
665 /* copy the attribute into an argument */
666 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
673 expand_string (c
->buf
, c
->length
,
674 doff
+ strlen (c
->srepos
) + 1);
676 strncpy (d
, c
->srepos
, strlen (c
->srepos
));
677 d
+= strlen (c
->srepos
);
682 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
686 arg
= cmdlineescape (c
->quotes
, arg
);
690 arg
= cmdlinequote ('"', arg
);
694 expand_string (c
->buf
, c
->length
, doff
+ strlen (arg
));
696 strncpy (d
, arg
, strlen (arg
));
698 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
700 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
703 /* Always put the extra space on. we'll have to back up a char
704 * when we're done, but that seems most efficient.
707 expand_string (c
->buf
, c
->length
, doff
+ 1);
709 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
710 if (c
->onearg
&& *f
) *d
++ = ',';
712 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
715 /* correct our original pointer into the buff */
723 * Writes some stuff to the logfile "filter" and returns the status of the
727 logfile_write (const char *repository
, const char *filter
, const char *message
,
728 FILE *logfp
, List
*changes
)
735 const char *srepos
= Short_Repository (repository
);
739 /* The user may specify a format string as part of the filter.
740 Originally, `%s' was the only valid string. The string that
741 was substituted for it was:
743 <repository-name> <file1> <file2> <file3> ...
745 Each file was either a new directory/import (T_TITLE), or a
746 added (T_ADDED), modified (T_MODIFIED), or removed (T_REMOVED)
749 It is desirable to preserve that behavior so lots of commitlog
750 scripts won't die when they get this new code. At the same
751 time, we'd like to pass other information about the files (like
752 version numbers, statuses, or checkin times).
754 The solution is to allow a format string that allows us to
755 specify those other pieces of information. The format string
756 will be composed of `%' followed by a single format character,
757 or followed by a set of format characters surrounded by `{' and
758 `}' as separators. The format characters are:
761 V = old version number (pre-checkin)
762 v = new version number (post-checkin)
764 For example, valid format strings are:
771 There's no reason that more items couldn't be added (like
772 modification date or file status [added, modified, updated,
773 etc.]) -- the code modifications would be minimal (logmsg.c
774 (title_proc) and commit.c (check_fileproc)).
776 The output will be a string of tokens separated by spaces. For
777 backwards compatibility, the the first token will be the
778 repository name. The rest of the tokens will be
779 comma-delimited lists of the information requested in the
780 format string. For example, if `/u/src/master' is the
781 repository, `%{sVv}' is the format string, and three files
782 (ChangeLog, Makefile, foo.c) were modified, the output might
785 /u/src/master ChangeLog,1.1,1.2 Makefile,1.3,1.4 foo.c,1.12,1.13
787 Why this duplicates the old behavior when the format string is
788 `%s' is left as an exercise for the reader. */
793 * %{sVv} = file name, old revision (precommit), new revision (postcommit)
796 * Cast any NULL arguments as appropriate pointers as this is an
797 * stdarg function and we need to be certain the caller gets what
800 cmdline
= format_cmdline (
801 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
802 !config
->UseNewInfoFmtStrings
, srepos
,
803 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
805 "c", "s", cvs_cmd_name
,
806 #ifdef SERVER_SUPPORT
807 "R", "s", referrer
? referrer
->original
: "NONE",
808 #endif /* SERVER_SUPPORT */
810 "r", "s", current_parsed_root
->directory
,
812 logmsg_list_to_args_proc
, (void *) NULL
,
814 if (!cmdline
|| !strlen (cmdline
))
816 if (cmdline
) free (cmdline
);
817 error (0, 0, "logmsg proc resolved to the empty string!");
821 if ((pipefp
= run_popen (cmdline
, "w")) == NULL
)
824 error (0, 0, "cannot write entry to log filter: %s", cmdline
);
828 (void) fprintf (pipefp
, "Update of %s\n", repository
);
829 (void) fprintf (pipefp
, "In directory %s:", hostname
);
832 fprintf (pipefp
, "<cannot get working directory: %s>\n\n",
836 fprintf (pipefp
, "%s\n\n", cp
);
840 setup_tmpfile (pipefp
, "", changes
);
841 (void) fprintf (pipefp
, "Log Message:\n%s\n", (message
) ? message
: "");
844 (void) fprintf (pipefp
, "Status:\n");
846 while ((c
= getc (logfp
)) != EOF
)
847 (void) putc (c
, pipefp
);
850 pipestatus
= pclose (pipefp
);
851 return ((pipestatus
== -1) || (pipestatus
== 127)) ? 1 : 0;
856 /* This routine is called by Parse_Info. It runs the
857 * message verification script.
860 verifymsg_proc (const char *repository
, const char *script
, void *closure
)
862 char *verifymsg_script
;
863 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
864 char *newscript
= NULL
;
865 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
866 struct verifymsg_proc_data
*vpd
= closure
;
867 const char *srepos
= Short_Repository (repository
);
869 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
870 if (!strchr (script
, '%'))
873 "warning: verifymsg line doesn't contain any format strings:\n"
875 "Appending default format string (\" %%l\"), but be aware that this usage is\n"
876 "deprecated.", script
);
877 script
= newscript
= Xasprintf ("%s %%l", script
);
879 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
881 /* If we don't already have one, open a temporary file, write the message
882 * to the temp file, and close the file.
884 * We do this here so that we only create the file when there is a
885 * verifymsg script specified and we only create it once when there is
886 * more than one verifymsg script specified.
888 if (vpd
->fname
== NULL
)
891 if ((fp
= cvs_temp_file (&(vpd
->fname
))) == NULL
)
892 error (1, errno
, "cannot create temporary file %s", vpd
->fname
);
894 if (vpd
->message
!= NULL
)
895 fputs (vpd
->message
, fp
);
896 if (vpd
->message
== NULL
||
897 (vpd
->message
)[0] == '\0' ||
898 (vpd
->message
)[strlen (vpd
->message
) - 1] != '\n')
900 if (fclose (fp
) == EOF
)
901 error (1, errno
, "%s", vpd
->fname
);
903 if (config
->RereadLogAfterVerify
== LOGMSG_REREAD_STAT
)
905 /* Remember the status of the temp file for later */
906 if (stat (vpd
->fname
, &(vpd
->pre_stbuf
)) != 0)
907 error (1, errno
, "cannot stat temp file %s", vpd
->fname
);
910 * See if we need to sleep before running the verification
911 * script to avoid time-stamp races.
913 sleep_past (vpd
->pre_stbuf
.st_mtime
);
915 } /* if (vpd->fname == NULL) */
918 * Cast any NULL arguments as appropriate pointers as this is an
919 * stdarg function and we need to be certain the caller gets what
922 verifymsg_script
= format_cmdline (
923 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
925 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
927 "c", "s", cvs_cmd_name
,
928 #ifdef SERVER_SUPPORT
930 ? referrer
->original
: "NONE",
931 #endif /* SERVER_SUPPORT */
934 current_parsed_root
->directory
,
935 "l", "s", vpd
->fname
,
936 "sV", ",", vpd
->changes
,
937 logmsg_list_to_args_proc
, (void *) NULL
,
940 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
941 if (newscript
) free (newscript
);
942 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
944 if (!verifymsg_script
|| !strlen (verifymsg_script
))
946 if (verifymsg_script
) free (verifymsg_script
);
947 verifymsg_script
= NULL
;
948 error (0, 0, "verifymsg proc resolved to the empty string!");
952 run_setup (verifymsg_script
);
954 free (verifymsg_script
);
956 /* FIXME - because run_exec can return negative values and Parse_Info adds
957 * the values of each call to this function to get a total error, we are
958 * calling abs on the value of run_exec to ensure two errors do not sum to
961 * The only REALLY obnoxious thing about this, I guess, is that a -1 return
962 * code from run_exec can mean we failed to call the process for some
963 * reason and should care about errno or that the process we called
964 * returned -1 and the value of errno is undefined. In other words,
965 * run_exec should probably be rewritten to have two return codes. one
966 * which is its own exit status and one which is the child process's. So
969 * Once run_exec is returning two error codes, we should probably be
970 * failing here with an error message including errno when we get the
971 * return code which means we care about errno, in case you missed that
974 * I do happen to know we just fail for a non-zero value anyway and I
975 * believe the docs actually state that if the verifymsg_proc returns a
976 * "non-zero" value we will fail.
978 return abs (run_exec (RUN_TTY
, RUN_TTY
, RUN_TTY
,
979 RUN_NORMAL
| RUN_SIGIGNORE
));