/* * Call any postadmin procs. */ static int admin_filesdoneproc (void *callerdat, int err, const char *repository, const char *update_dir, List *entries) { TRACE (TRACE_FUNCTION, "admin_filesdoneproc (%d, %s, %s)", err, repository, update_dir); Parse_Info (CVSROOTADM_POSTADMIN, repository, postadmin_proc, PIOPT_ALL, NULL); return err; }
/* 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 attribute. unlike the do_editor function, this is independant of the running of an editor for getting a message. */ void do_verify (char **messagep, const char *repository, List *changes) { int err; struct verifymsg_proc_data data; struct stat post_stbuf; if (current_parsed_root->isremote) /* The verification will happen on the server. */ return; /* FIXME? Do we really want to skip this on noexec? What do we do for the other administrative files? */ /* EXPLAIN: Why do we check for repository == NULL here? */ if (noexec || repository == NULL) return; /* Get the name of the verification script to run */ data.message = *messagep; data.fname = NULL; data.changes = changes; if ((err = Parse_Info (CVSROOTADM_VERIFYMSG, repository, verifymsg_proc, 0, &data)) != 0) { int saved_errno = errno; /* Since following error() exits, delete the temp file now. */ if (data.fname != NULL && unlink_file( data.fname ) < 0) error (0, errno, "cannot remove %s", data.fname); free (data.fname); errno = saved_errno; error (1, err == -1 ? errno : 0, "Message verification failed"); } /* Return if no temp file was created. That means that we didn't call any * verifymsg scripts. */ if (data.fname == NULL) return; /* Get the mod time and size of the possibly new log message * in always and stat modes. */ if (config->RereadLogAfterVerify == LOGMSG_REREAD_ALWAYS || config->RereadLogAfterVerify == LOGMSG_REREAD_STAT) { if(stat (data.fname, &post_stbuf) != 0) error (1, errno, "cannot find size of temp file %s", data.fname); } /* And reread the log message in `always' mode or in `stat' mode when it's * changed. */ if (config->RereadLogAfterVerify == LOGMSG_REREAD_ALWAYS || (config->RereadLogAfterVerify == LOGMSG_REREAD_STAT && (data.pre_stbuf.st_mtime != post_stbuf.st_mtime || data.pre_stbuf.st_size != post_stbuf.st_size))) { /* put the entire message back into the *messagep variable */ if (*messagep) free (*messagep); if (post_stbuf.st_size == 0) *messagep = NULL; else { char *line = NULL; int line_length; size_t line_chars_allocated = 0; char *p; FILE *fp; fp = xfopen (data.fname, "r"); /* On NT, we might read less than st_size bytes, but we won't read more. So this works. */ p = *messagep = (char *) xmalloc (post_stbuf.st_size + 1); *messagep[0] = '\0'; for (;;) { line_length = getline( &line, &line_chars_allocated, fp); if (line_length == -1) { if (ferror (fp)) /* Fail in this case because otherwise we will have no * log message */ error (1, errno, "cannot read %s", data.fname); break; } if (strncmp (line, CVSEDITPREFIX, CVSEDITPREFIXLEN) == 0) continue; (void) strcpy (p, line); p += line_length; } if (line) free (line); if (fclose (fp) < 0) error (0, errno, "warning: cannot close %s", data.fname); } } /* Delete the temp file */ if (unlink_file (data.fname) < 0) error (0, errno, "cannot remove `%s'", data.fname); free (data.fname); }
/* * Builds a temporary file using setup_tmpfile() and invokes the user's * editor on the file. The header garbage in the resultant file is then * stripped and the log message is stored in the "message" argument. * * If REPOSITORY is non-NULL, process rcsinfo for that repository; if it * is NULL, use the CVSADM_TEMPLATE file instead. REPOSITORY should be * NULL when running in client mode. * * GLOBALS * Editor Set to a default value by configure and overridable using the * -e option to the CVS executable. */ void do_editor (const char *dir, char **messagep, const char *repository, List *changes) { static int reuse_log_message = 0; char *line; int line_length; size_t line_chars_allocated; char *fname; struct stat pre_stbuf, post_stbuf; int retcode = 0; assert (!current_parsed_root->isremote != !repository); if (noexec || reuse_log_message) return; /* Abort before creation of the temp file if no editor is defined. */ if (strcmp (Editor, "") == 0) error(1, 0, "no editor defined, must use -e or -m"); again: /* Create a temporary file. */ if( ( fp = cvs_temp_file( &fname ) ) == NULL ) error( 1, errno, "cannot create temporary file" ); if (*messagep) { (void) fputs (*messagep, fp); if ((*messagep)[0] == '\0' || (*messagep)[strlen (*messagep) - 1] != '\n') (void) fprintf (fp, "\n"); } else (void) fprintf (fp, "\n"); if (repository != NULL) /* tack templates on if necessary */ (void) Parse_Info (CVSROOTADM_RCSINFO, repository, rcsinfo_proc, PIOPT_ALL, NULL); 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, CVSEDITPREFIXLEN, CVSEDITPREFIX, CVSEDITPREFIX); if (dir != NULL && *dir) (void) fprintf (fp, "%sCommitting in %s\n%s\n", CVSEDITPREFIX, dir, CVSEDITPREFIX); if (changes != NULL) setup_tmpfile (fp, CVSEDITPREFIX, changes); (void) fprintf (fp, "%s----------------------------------------------------------------------\n", CVSEDITPREFIX); /* finish off the temp file */ if (fclose (fp) == EOF) error (1, errno, "%s", fname); if (stat (fname, &pre_stbuf) == -1) pre_stbuf.st_mtime = 0; /* run the editor */ run_setup (Editor); run_add_arg (fname); if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL | RUN_SIGIGNORE | RUN_UNSETXID)) != 0) error (0, retcode == -1 ? errno : 0, "warning: editor session failed"); /* put the entire message back into the *messagep variable */ fp = xfopen (fname, "r"); if (*messagep) free (*messagep); if (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'; } line = NULL; line_chars_allocated = 0; if (*messagep) { size_t message_len = post_stbuf.st_size + 1; size_t offset = 0; while (1) { 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; if (offset + line_length >= message_len) expand_string (messagep, &message_len, offset + line_length + 1); (void) strcpy (*messagep + offset, line); offset += line_length; } } if (fclose (fp) < 0) error (0, errno, "warning: cannot close %s", fname); /* canonicalize emply messages */ if (*messagep != NULL && (**messagep == '\0' || strcmp (*messagep, "\n") == 0)) { free (*messagep); *messagep = NULL; } if (pre_stbuf.st_mtime == post_stbuf.st_mtime || *messagep == NULL) { for (;;) { (void) printf ("\nLog message unchanged or not specified\n"); (void) printf ("a)bort, c)ontinue, e)dit, !)reuse this message unchanged for remaining dirs\n"); (void) printf ("Action: (abort) "); (void) fflush (stdout); 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 == 'a' || *line == 'A') { if (unlink_file (fname) < 0) error (0, errno, "warning: cannot remove temp file %s", fname); error (1, 0, "aborted by user"); } if (*line == 'c' || *line == 'C') break; if (*line == 'e' || *line == 'E') goto again; if (*line == '!') { reuse_log_message = 1; break; } (void) printf ("Unknown input\n"); } } if (line) free (line); if (unlink_file (fname) < 0) error (0, errno, "warning: cannot remove temp file %s", fname); free (fname); }