#include "cvs.h"
#include "getline.h"
#include "watch.h"
#include "edit.h"
#include "fileattr.h"
static int watch_onoff PROTO ((int, char **));
static int setting_default;
static int turning_on;
static int setting_tedit;
static int setting_tunedit;
static int setting_tcommit;
static int onoff_fileproc PROTO ((void *callerdat, struct file_info *finfo));
static int
onoff_fileproc (callerdat, finfo)
void *callerdat;
struct file_info *finfo;
{
fileattr_set (finfo->file, "_watched", turning_on ? "" : NULL);
return 0;
}
static int onoff_filesdoneproc PROTO ((void *, int, char *, char *, List *));
static int
onoff_filesdoneproc (callerdat, err, repository, update_dir, entries)
void *callerdat;
int err;
char *repository;
char *update_dir;
List *entries;
{
if (setting_default)
fileattr_set (NULL, "_watched", turning_on ? "" : NULL);
return err;
}
static int
watch_onoff (argc, argv)
int argc;
char **argv;
{
int c;
int local = 0;
int err;
optind = 0;
while ((c = getopt (argc, argv, "+lR")) != -1)
{
switch (c)
{
case 'l':
local = 1;
break;
case 'R':
local = 0;
break;
case '?':
default:
usage (watch_usage);
break;
}
}
argc -= optind;
argv += optind;
#ifdef CLIENT_SUPPORT
if (client_active)
{
start_server ();
ign_setup ();
if (local)
send_arg ("-l");
send_file_names (argc, argv, SEND_EXPAND_WILD);
send_files (argc, argv, local, 0, SEND_NO_CONTENTS);
send_to_server (turning_on ? "watch-on\012" : "watch-off\012", 0);
return get_responses_and_close ();
}
#endif
setting_default = (argc <= 0);
lock_tree_for_write (argc, argv, local, 0);
err = start_recursion (onoff_fileproc, onoff_filesdoneproc,
(DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL,
argc, argv, local, W_LOCAL, 0, 0, (char *)NULL,
0);
Lock_Cleanup ();
return err;
}
int
watch_on (argc, argv)
int argc;
char **argv;
{
turning_on = 1;
return watch_onoff (argc, argv);
}
int
watch_off (argc, argv)
int argc;
char **argv;
{
turning_on = 0;
return watch_onoff (argc, argv);
}
static int dummy_fileproc PROTO ((void *callerdat, struct file_info *finfo));
static int
dummy_fileproc (callerdat, finfo)
void *callerdat;
struct file_info *finfo;
{
return 0;
}
static int ncheck_fileproc PROTO ((void *callerdat, struct file_info *finfo));
static int
ncheck_fileproc (callerdat, finfo)
void *callerdat;
struct file_info *finfo;
{
int notif_type;
char *filename;
char *val;
char *cp;
char *watches;
FILE *fp;
char *line = NULL;
size_t line_len = 0;
fp = CVS_FOPEN (CVSADM_NOTIFY, "r");
if (fp == NULL)
{
if (!existence_error (errno))
error (0, errno, "cannot open %s", CVSADM_NOTIFY);
return 0;
}
while (getline (&line, &line_len, fp) > 0)
{
notif_type = line[0];
if (notif_type == '\0')
continue;
filename = line + 1;
cp = strchr (filename, '\t');
if (cp == NULL)
continue;
*cp++ = '\0';
val = cp;
cp = strchr (val, '\t');
if (cp == NULL)
continue;
*cp++ = '+';
cp = strchr (cp, '\t');
if (cp == NULL)
continue;
*cp++ = '+';
cp = strchr (cp, '\t');
if (cp == NULL)
continue;
*cp++ = '\0';
watches = cp;
cp = strchr (cp, '\n');
if (cp == NULL)
continue;
*cp = '\0';
notify_do (notif_type, filename, getcaller (), val, watches,
finfo->repository);
}
free (line);
if (ferror (fp))
error (0, errno, "cannot read %s", CVSADM_NOTIFY);
if (fclose (fp) < 0)
error (0, errno, "cannot close %s", CVSADM_NOTIFY);
if ( CVS_UNLINK (CVSADM_NOTIFY) < 0)
error (0, errno, "cannot remove %s", CVSADM_NOTIFY);
return 0;
}
static int send_notifications PROTO ((int, char **, int));
static int
send_notifications (argc, argv, local)
int argc;
char **argv;
int local;
{
int err = 0;
#ifdef CLIENT_SUPPORT
if (client_active)
{
if (strcmp (command_name, "release") != 0)
{
start_server ();
ign_setup ();
}
err += start_recursion (dummy_fileproc, (FILESDONEPROC) NULL,
(DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL,
argc, argv, local, W_LOCAL, 0, 0, (char *)NULL,
0);
send_to_server ("noop\012", 0);
if (strcmp (command_name, "release") == 0)
err += get_server_responses ();
else
err += get_responses_and_close ();
}
else
#endif
{
lock_tree_for_write (argc, argv, local, 0);
err += start_recursion (ncheck_fileproc, (FILESDONEPROC) NULL,
(DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL,
argc, argv, local, W_LOCAL, 0, 0, (char *)NULL,
0);
Lock_Cleanup ();
}
return err;
}
static int edit_fileproc PROTO ((void *callerdat, struct file_info *finfo));
static int
edit_fileproc (callerdat, finfo)
void *callerdat;
struct file_info *finfo;
{
FILE *fp;
time_t now;
char *ascnow;
char *basefilename;
if (noexec)
return 0;
if (!isfile (finfo->file))
{
error (0, 0, "no such file %s; ignored", finfo->fullname);
return 0;
}
fp = open_file (CVSADM_NOTIFY, "a");
(void) time (&now);
ascnow = asctime (gmtime (&now));
ascnow[24] = '\0';
fprintf (fp, "E%s\t%s GMT\t%s\t%s\t", finfo->file,
ascnow, hostname, CurDir);
if (setting_tedit)
fprintf (fp, "E");
if (setting_tunedit)
fprintf (fp, "U");
if (setting_tcommit)
fprintf (fp, "C");
fprintf (fp, "\n");
if (fclose (fp) < 0)
{
if (finfo->update_dir[0] == '\0')
error (0, errno, "cannot close %s", CVSADM_NOTIFY);
else
error (0, errno, "cannot close %s/%s", finfo->update_dir,
CVSADM_NOTIFY);
}
xchmod (finfo->file, 1);
mkdir_if_needed (CVSADM_BASE);
basefilename = xmalloc (10 + sizeof CVSADM_BASE + strlen (finfo->file));
strcpy (basefilename, CVSADM_BASE);
strcat (basefilename, "/");
strcat (basefilename, finfo->file);
copy_file (finfo->file, basefilename);
free (basefilename);
{
Node *node;
node = findnode_fn (finfo->entries, finfo->file);
if (node != NULL)
base_register (finfo, ((Entnode *) node->data)->version);
}
return 0;
}
static const char *const edit_usage[] =
{
"Usage: %s %s [-lR] [files...]\n",
"-l: Local directory only, not recursive\n",
"-R: Process directories recursively\n",
"-a: Specify what actions for temporary watch, one of\n",
" edit,unedit,commit,all,none\n",
"(Specify the --help global option for a list of other help options)\n",
NULL
};
int
edit (argc, argv)
int argc;
char **argv;
{
int local = 0;
int c;
int err;
int a_omitted;
if (argc == -1)
usage (edit_usage);
a_omitted = 1;
setting_tedit = 0;
setting_tunedit = 0;
setting_tcommit = 0;
optind = 0;
while ((c = getopt (argc, argv, "+lRa:")) != -1)
{
switch (c)
{
case 'l':
local = 1;
break;
case 'R':
local = 0;
break;
case 'a':
a_omitted = 0;
if (strcmp (optarg, "edit") == 0)
setting_tedit = 1;
else if (strcmp (optarg, "unedit") == 0)
setting_tunedit = 1;
else if (strcmp (optarg, "commit") == 0)
setting_tcommit = 1;
else if (strcmp (optarg, "all") == 0)
{
setting_tedit = 1;
setting_tunedit = 1;
setting_tcommit = 1;
}
else if (strcmp (optarg, "none") == 0)
{
setting_tedit = 0;
setting_tunedit = 0;
setting_tcommit = 0;
}
else
usage (edit_usage);
break;
case '?':
default:
usage (edit_usage);
break;
}
}
argc -= optind;
argv += optind;
if (a_omitted)
{
setting_tedit = 1;
setting_tunedit = 1;
setting_tcommit = 1;
}
err = start_recursion (edit_fileproc, (FILESDONEPROC) NULL,
(DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL,
argc, argv, local, W_LOCAL, 0, 0, (char *)NULL,
0);
err += send_notifications (argc, argv, local);
return err;
}
static int unedit_fileproc PROTO ((void *callerdat, struct file_info *finfo));
static int
unedit_fileproc (callerdat, finfo)
void *callerdat;
struct file_info *finfo;
{
FILE *fp;
time_t now;
char *ascnow;
char *basefilename;
if (noexec)
return 0;
basefilename = xmalloc (10 + sizeof CVSADM_BASE + strlen (finfo->file));
strcpy (basefilename, CVSADM_BASE);
strcat (basefilename, "/");
strcat (basefilename, finfo->file);
if (!isfile (basefilename))
{
free (basefilename);
return 0;
}
if (xcmp (finfo->file, basefilename) != 0)
{
printf ("%s has been modified; revert changes? ", finfo->fullname);
if (!yesno ())
{
free (basefilename);
return 0;
}
}
rename_file (basefilename, finfo->file);
free (basefilename);
fp = open_file (CVSADM_NOTIFY, "a");
(void) time (&now);
ascnow = asctime (gmtime (&now));
ascnow[24] = '\0';
fprintf (fp, "U%s\t%s GMT\t%s\t%s\t\n", finfo->file,
ascnow, hostname, CurDir);
if (fclose (fp) < 0)
{
if (finfo->update_dir[0] == '\0')
error (0, errno, "cannot close %s", CVSADM_NOTIFY);
else
error (0, errno, "cannot close %s/%s", finfo->update_dir,
CVSADM_NOTIFY);
}
{
char *baserev;
Node *node;
Entnode *entdata;
baserev = base_get (finfo);
node = findnode_fn (finfo->entries, finfo->file);
if (node != NULL)
{
entdata = (Entnode *) node->data;
if (baserev == NULL)
{
error (0, 0, "%s not mentioned in %s", finfo->fullname,
CVSADM_BASEREV);
if (unlink_file (finfo->file) < 0)
error (0, errno, "cannot remove %s", finfo->fullname);
error (0, 0, "run update to complete the unedit");
return 0;
}
Register (finfo->entries, finfo->file, baserev, entdata->timestamp,
entdata->options, entdata->tag, entdata->date,
entdata->conflict);
}
free (baserev);
base_deregister (finfo);
}
xchmod (finfo->file, 0);
return 0;
}
int
unedit (argc, argv)
int argc;
char **argv;
{
int local = 0;
int c;
int err;
if (argc == -1)
usage (edit_usage);
optind = 0;
while ((c = getopt (argc, argv, "+lR")) != -1)
{
switch (c)
{
case 'l':
local = 1;
break;
case 'R':
local = 0;
break;
case '?':
default:
usage (edit_usage);
break;
}
}
argc -= optind;
argv += optind;
err = start_recursion (unedit_fileproc, (FILESDONEPROC) NULL,
(DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL,
argc, argv, local, W_LOCAL, 0, 0, (char *)NULL,
0);
err += send_notifications (argc, argv, local);
return err;
}
void
mark_up_to_date (file)
char *file;
{
char *base;
base = xmalloc (strlen (file) + 80);
strcpy (base, CVSADM_BASE);
strcat (base, "/");
strcat (base, file);
if (unlink_file (base) < 0 && ! existence_error (errno))
error (0, errno, "cannot remove %s", file);
free (base);
}
void
editor_set (filename, editor, val)
char *filename;
char *editor;
char *val;
{
char *edlist;
char *newlist;
edlist = fileattr_get0 (filename, "_editors");
newlist = fileattr_modify (edlist, editor, val, '>', ',');
if (!((edlist == NULL && newlist == NULL)
|| (edlist != NULL
&& newlist != NULL
&& strcmp (edlist, newlist) == 0)))
fileattr_set (filename, "_editors", newlist);
if (edlist != NULL)
free (edlist);
if (newlist != NULL)
free (newlist);
}
struct notify_proc_args {
char *type;
char *who;
char *notifyee;
char *file;
};
static struct notify_proc_args *notify_args;
static int notify_proc PROTO ((char *repository, char *filter));
static int
notify_proc (repository, filter)
char *repository;
char *filter;
{
FILE *pipefp;
char *prog;
char *expanded_prog;
char *p;
char *q;
char *srepos;
struct notify_proc_args *args = notify_args;
srepos = Short_Repository (repository);
prog = xmalloc (strlen (filter) + strlen (args->notifyee) + 1);
for (p = filter, q = prog; *p != '\0'; ++p)
{
if (p[0] == '%')
{
if (p[1] == 's')
{
strcpy (q, args->notifyee);
q += strlen (q);
strcpy (q, p + 2);
q += strlen (q);
break;
}
else
continue;
}
*q++ = *p;
}
*q = '\0';
expanded_prog = expand_path (prog, "notify", 0);
if (!expanded_prog)
{
free (prog);
return 1;
}
pipefp = run_popen (expanded_prog, "w");
if (pipefp == NULL)
{
error (0, errno, "cannot write entry to notify filter: %s", prog);
free (prog);
free (expanded_prog);
return 1;
}
fprintf (pipefp, "%s %s\n---\n", srepos, args->file);
fprintf (pipefp, "Triggered %s watch on %s\n", args->type, repository);
fprintf (pipefp, "By %s\n", args->who);
free (prog);
free (expanded_prog);
return (pclose (pipefp));
}
void
notify_do (type, filename, who, val, watches, repository)
int type;
char *filename;
char *who;
char *val;
char *watches;
char *repository;
{
static struct addremove_args blank;
struct addremove_args args;
char *watchers;
char *p;
char *endp;
char *nextp;
args = blank;
switch (type)
{
case 'E':
editor_set (filename, who, val);
break;
case 'U':
case 'C':
editor_set (filename, who, NULL);
break;
default:
return;
}
watchers = fileattr_get0 (filename, "_watchers");
p = watchers;
while (p != NULL)
{
char *q;
char *endq;
char *nextq;
char *notif;
endp = strchr (p, '>');
if (endp == NULL)
break;
nextp = strchr (p, ',');
if ((size_t)(endp - p) == strlen (who) && strncmp (who, p, endp - p) == 0)
{
p = nextp == NULL ? nextp : nextp + 1;
continue;
}
q = endp + 1;
notif = NULL;
while (q != NULL)
{
endq = strchr (q, '+');
if (endq == NULL || (nextp != NULL && endq > nextp))
{
if (nextp == NULL)
endq = q + strlen (q);
else
endq = nextp;
nextq = NULL;
}
else
nextq = endq + 1;
if (type == 'E' && endq - q == 4 && strncmp ("edit", q, 4) == 0)
{
notif = "edit";
}
else if (type == 'U'
&& endq - q == 6 && strncmp ("unedit", q, 6) == 0)
{
notif = "unedit";
}
else if (type == 'C'
&& endq - q == 6 && strncmp ("commit", q, 6) == 0)
{
notif = "commit";
}
else if (type == 'E'
&& endq - q == 5 && strncmp ("tedit", q, 5) == 0)
{
if (notif == NULL)
notif = "temporary edit";
}
else if (type == 'U'
&& endq - q == 7 && strncmp ("tunedit", q, 7) == 0)
{
if (notif == NULL)
notif = "temporary unedit";
}
else if (type == 'C'
&& endq - q == 7 && strncmp ("tcommit", q, 7) == 0)
{
if (notif == NULL)
notif = "temporary commit";
}
q = nextq;
}
if (nextp != NULL)
++nextp;
if (notif != NULL)
{
struct notify_proc_args args;
size_t len = endp - p;
FILE *fp;
char *usersname;
char *line = NULL;
size_t line_len = 0;
args.notifyee = NULL;
usersname = xmalloc (strlen (CVSroot_directory)
+ sizeof CVSROOTADM
+ sizeof CVSROOTADM_USERS
+ 20);
strcpy (usersname, CVSroot_directory);
strcat (usersname, "/");
strcat (usersname, CVSROOTADM);
strcat (usersname, "/");
strcat (usersname, CVSROOTADM_USERS);
fp = CVS_FOPEN (usersname, "r");
if (fp == NULL && !existence_error (errno))
error (0, errno, "cannot read %s", usersname);
if (fp != NULL)
{
while (getline (&line, &line_len, fp) >= 0)
{
if (strncmp (line, p, len) == 0
&& line[len] == ':')
{
char *cp;
args.notifyee = xstrdup (line + len + 1);
cp = strchr (args.notifyee, ':');
if (cp != NULL)
*cp = '\0';
break;
}
}
if (ferror (fp))
error (0, errno, "cannot read %s", usersname);
if (fclose (fp) < 0)
error (0, errno, "cannot close %s", usersname);
}
free (usersname);
free (line);
if (args.notifyee == NULL)
{
args.notifyee = xmalloc (endp - p + 1);
strncpy (args.notifyee, p, endp - p);
args.notifyee[endp - p] = '\0';
}
notify_args = &args;
args.type = notif;
args.who = who;
args.file = filename;
(void) Parse_Info (CVSROOTADM_NOTIFY, repository, notify_proc, 1);
free (args.notifyee);
}
p = nextp;
}
if (watchers != NULL)
free (watchers);
switch (type)
{
case 'E':
if (*watches == 'E')
{
args.add_tedit = 1;
++watches;
}
if (*watches == 'U')
{
args.add_tunedit = 1;
++watches;
}
if (*watches == 'C')
{
args.add_tcommit = 1;
}
watch_modify_watchers (filename, &args);
break;
case 'U':
case 'C':
args.remove_temp = 1;
watch_modify_watchers (filename, &args);
break;
}
}
#ifdef CLIENT_SUPPORT
void
notify_check (repository, update_dir)
char *repository;
char *update_dir;
{
FILE *fp;
char *line = NULL;
size_t line_len = 0;
if (! server_started)
return;
fp = CVS_FOPEN (CVSADM_NOTIFY, "r");
if (fp == NULL)
{
if (!existence_error (errno))
error (0, errno, "cannot open %s", CVSADM_NOTIFY);
return;
}
while (getline (&line, &line_len, fp) > 0)
{
int notif_type;
char *filename;
char *val;
char *cp;
notif_type = line[0];
if (notif_type == '\0')
continue;
filename = line + 1;
cp = strchr (filename, '\t');
if (cp == NULL)
continue;
*cp++ = '\0';
val = cp;
client_notify (repository, update_dir, filename, notif_type, val);
}
if (line)
free (line);
if (ferror (fp))
error (0, errno, "cannot read %s", CVSADM_NOTIFY);
if (fclose (fp) < 0)
error (0, errno, "cannot close %s", CVSADM_NOTIFY);
}
#endif
static const char *const editors_usage[] =
{
"Usage: %s %s [-lR] [files...]\n",
"\t-l\tProcess this directory only (not recursive).\n",
"\t-R\tProcess directories recursively.\n",
"(Specify the --help global option for a list of other help options)\n",
NULL
};
static int editors_fileproc PROTO ((void *callerdat, struct file_info *finfo));
static int
editors_fileproc (callerdat, finfo)
void *callerdat;
struct file_info *finfo;
{
char *them;
char *p;
them = fileattr_get0 (finfo->file, "_editors");
if (them == NULL)
return 0;
fputs (finfo->fullname, stdout);
p = them;
while (1)
{
putc ('\t', stdout);
while (*p != '>' && *p != '\0')
putc (*p++, stdout);
if (*p == '\0')
{
putc ('\n', stdout);
break;
}
++p;
putc ('\t', stdout);
while (1)
{
while (*p != '+' && *p != ',' && *p != '\0')
putc (*p++, stdout);
if (*p == '\0')
{
putc ('\n', stdout);
goto out;
}
if (*p == ',')
{
++p;
break;
}
++p;
putc ('\t', stdout);
}
putc ('\n', stdout);
}
out:;
return 0;
}
int
editors (argc, argv)
int argc;
char **argv;
{
int local = 0;
int c;
if (argc == -1)
usage (editors_usage);
optind = 0;
while ((c = getopt (argc, argv, "+lR")) != -1)
{
switch (c)
{
case 'l':
local = 1;
break;
case 'R':
local = 0;
break;
case '?':
default:
usage (editors_usage);
break;
}
}
argc -= optind;
argv += optind;
#ifdef CLIENT_SUPPORT
if (client_active)
{
start_server ();
ign_setup ();
if (local)
send_arg ("-l");
send_file_names (argc, argv, SEND_EXPAND_WILD);
send_files (argc, argv, local, 0, SEND_NO_CONTENTS);
send_to_server ("editors\012", 0);
return get_responses_and_close ();
}
#endif
return start_recursion (editors_fileproc, (FILESDONEPROC) NULL,
(DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL,
argc, argv, local, W_LOCAL, 0, 1, (char *)NULL,
0);
}