#include "cvs.h"
#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 *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;
static char *editinfo_editor;
static char *verifymsg_script;
static Ctype type;
static char *prefix;
static int col;
static char *tag;
static void
setup_tmpfile (xfp, xprefix, changes)
FILE *xfp;
char *xprefix;
List *changes;
{
fp = xfp;
prefix = xprefix;
type = T_MODIFIED;
if (walklist (changes, find_type, NULL) != 0)
{
(void) fprintf (fp, "%sModified Files:\n", prefix);
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);
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);
col = 0;
(void) walklist (changes, fmt_proc, NULL);
(void) fprintf (fp, "\n");
if (tag != NULL)
{
free (tag);
tag = NULL;
}
}
}
static int
find_type (p, closure)
Node *p;
void *closure;
{
struct logfile_info *li;
li = (struct logfile_info *) p->data;
if (li->type == type)
return (1);
else
return (0);
}
static int
fmt_proc (p, closure)
Node *p;
void *closure;
{
struct logfile_info *li;
li = (struct logfile_info *) p->data;
if (li->type == type)
{
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);
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;
}
(void) fprintf (fp, "%s ", p->key);
col += strlen (p->key) + 1;
}
return (0);
}
void
do_editor (dir, messagep, repository, changes)
char *dir;
char **messagep;
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;
char *p;
if (noexec || reuse_log_message)
return;
if (strcmp (Editor, "") == 0 && !editinfo_editor)
error(1, 0, "no editor defined, must use -e or -m");
fname = cvs_temp_name ();
again:
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)[0] == '\0' ||
(*messagep)[strlen (*messagep) - 1] != '\n')
(void) fprintf (fp, "\n");
}
else
(void) fprintf (fp, "\n");
if (repository != NULL)
(void) Parse_Info (CVSROOTADM_RCSINFO, repository, rcsinfo_proc, 1);
else
{
FILE *tfp;
char buf[1024];
char *p;
size_t n;
size_t nwrite;
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))
{
n = fread (buf, 1, sizeof buf, tfp);
nwrite = n;
p = buf;
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);
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 (client_active)
;
else
#endif
if (repository != NULL)
(void) Parse_Info (CVSROOTADM_EDITINFO, repository, editinfo_proc, 0);
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)
error (editinfo_editor ? 1 : 0, retcode == -1 ? errno : 0,
editinfo_editor ? "Logfile verification failed" :
"warning: editor session failed");
fp = open_file (fname, "r");
if (*messagep)
free (*messagep);
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
{
*messagep = (char *) xmalloc (post_stbuf.st_size + 1);
*messagep[0] = '\0';
}
line = NULL;
line_chars_allocated = 0;
if (*messagep)
{
p = *messagep;
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;
(void) strcpy (p, line);
p += line_length;
}
}
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)
{
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: (continue) ");
(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 == 'c' || *line == 'C')
break;
if (*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 == '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);
}
void
do_verify (message, repository)
char *message;
char *repository;
{
FILE *fp;
char *fname;
int retcode = 0;
#ifdef CLIENT_SUPPORT
if (client_active)
return;
#endif
if (noexec)
return;
if (message == NULL)
{
cvs_output ("No message to verify\n", 0);
return;
}
fname = cvs_temp_name ();
fp = fopen (fname, "w");
if (fp == 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);
if (repository != NULL)
(void) Parse_Info (CVSROOTADM_VERIFYMSG, repository,
verifymsg_proc, 0);
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)
{
unlink_file (fname);
error (1, retcode == -1 ? errno : 0,
"Message verification failed");
}
}
unlink_file (fname);
free (fname);
}
}
static int
rcsinfo_proc (repository, template)
char *repository;
char *template;
{
static char *last_template;
FILE *tfp;
if (last_template && strcmp (last_template, template) == 0)
return (0);
if (last_template)
free (last_template);
last_template = xstrdup (template);
if ((tfp = CVS_FOPEN (template, "r")) != NULL)
{
char *line = NULL;
size_t line_chars_allocated = 0;
while (getline (&line, &line_chars_allocated, tfp) >= 0)
(void) fputs (line, fp);
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, errno, "Couldn't open rcsinfo template file %s", template);
return (1);
}
}
static FILE *logfp;
static char *message;
static List *changes;
void
Update_Logfile (repository, xmessage, xlogfp, xchanges)
char *repository;
char *xmessage;
FILE *xlogfp;
List *xchanges;
{
if (xchanges == NULL || xchanges->list->next == xchanges->list)
return;
message = xmessage;
logfp = xlogfp;
changes = xchanges;
(void) Parse_Info (CVSROOTADM_LOGINFO, repository, update_logfile_proc, 1);
}
static int
update_logfile_proc (repository, filter)
char *repository;
char *filter;
{
return (logfile_write (repository, filter, message, logfp, changes));
}
static int
title_proc (p, closure)
Node *p;
void *closure;
{
struct logfile_info *li;
char *c;
li = (struct logfile_info *) p->data;
if (li->type == type)
{
str_list = xrealloc (str_list, strlen (str_list) + 5);
(void) strcat (str_list, " ");
if (li->type == T_TITLE)
{
str_list = xrealloc (str_list,
strlen (str_list) + strlen (p->key) + 5);
(void) strcat (str_list, p->key);
}
else
{
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;
}
if (*(c + 1) != '\0')
{
str_list = xrealloc (str_list, strlen (str_list) + 5);
(void) strcat (str_list, ",");
}
}
}
}
return (0);
}
static int
logfile_write (repository, filter, message, logfp, changes)
char *repository;
char *filter;
char *message;
FILE *logfp;
List *changes;
{
FILE *pipefp;
char *prog;
char *cp;
int c;
int pipestatus;
char *fmt_percent;
fmt_percent = strchr (filter, '%');
if (fmt_percent)
{
int len;
char *srepos;
char *fmt_begin, *fmt_end;
char *fmt_continue;
if ((*(fmt_percent + 1) == ' ') || (*(fmt_percent + 1) == '\0'))
{
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) == '{')
{
fmt_begin = fmt_percent + 2;
fmt_end = strchr (fmt_begin, '}');
if (fmt_end)
{
fmt_continue = fmt_end + 1;
}
else
{
error (0, 0, "loginfo: '}' missing");
fmt_end = fmt_begin + strlen (fmt_begin);
fmt_continue = fmt_end;
}
}
else
{
fmt_begin = fmt_percent + 1;
fmt_end = fmt_begin + 1;
fmt_continue = fmt_end;
}
len = fmt_end - fmt_begin;
str_list_format = xmalloc (sizeof (char) * (len + 1));
strncpy (str_list_format, fmt_begin, len);
str_list_format[len] = '\0';
if (!str_list)
str_list = xmalloc (1);
str_list[0] = '\0';
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);
srepos = Short_Repository (repository);
prog = xmalloc ((fmt_percent - filter) + strlen (srepos)
+ strlen (str_list) + strlen (fmt_continue)
+ 10);
(void) strncpy (prog, filter, fmt_percent - filter);
prog[fmt_percent - filter] = '\0';
(void) strcat (prog, "'");
(void) strcat (prog, srepos);
(void) strcat (prog, str_list);
(void) strcat (prog, "'");
(void) strcat (prog, fmt_continue);
free (str_list);
str_list = (char *) NULL;
}
else
{
prog = xstrdup (filter);
}
if ((pipefp = run_popen (prog, "w")) == NULL)
{
if (!noexec)
error (0, 0, "cannot write entry to log filter: %s", prog);
free (prog);
return (1);
}
(void) fprintf (pipefp, "Update of %s\n", repository);
(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)
{
(void) fprintf (pipefp, "Status:\n");
rewind (logfp);
while ((c = getc (logfp)) != EOF)
(void) putc ((char) c, pipefp);
}
free (prog);
pipestatus = pclose (pipefp);
return ((pipestatus == -1) || (pipestatus == 127)) ? 1 : 0;
}
static int
editinfo_proc(repository, editor)
char *repository;
char *editor;
{
if (editinfo_editor && strcmp (editinfo_editor, editor) == 0)
return (0);
if (editinfo_editor)
free (editinfo_editor);
editinfo_editor = xstrdup (editor);
return (0);
}
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);
}