1996-08-20 23:46:10 +00:00
|
|
|
|
/* Implementation for "cvs edit", "cvs watch on", and related commands
|
|
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
|
the Free Software Foundation; either version 2, or (at your option)
|
|
|
|
|
any later version.
|
|
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
1997-05-15 22:46:24 +00:00
|
|
|
|
GNU General Public License for more details. */
|
1996-08-20 23:46:10 +00:00
|
|
|
|
|
|
|
|
|
#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;
|
|
|
|
|
|
1997-05-15 22:46:24 +00:00
|
|
|
|
static int onoff_fileproc PROTO ((void *callerdat, struct file_info *finfo));
|
1996-08-20 23:46:10 +00:00
|
|
|
|
|
|
|
|
|
static int
|
1997-05-15 22:46:24 +00:00
|
|
|
|
onoff_fileproc (callerdat, finfo)
|
|
|
|
|
void *callerdat;
|
1996-08-20 23:46:10 +00:00
|
|
|
|
struct file_info *finfo;
|
|
|
|
|
{
|
|
|
|
|
fileattr_set (finfo->file, "_watched", turning_on ? "" : NULL);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
1997-05-15 22:46:24 +00:00
|
|
|
|
static int onoff_filesdoneproc PROTO ((void *, int, char *, char *, List *));
|
1996-08-20 23:46:10 +00:00
|
|
|
|
|
|
|
|
|
static int
|
1997-05-15 22:46:24 +00:00
|
|
|
|
onoff_filesdoneproc (callerdat, err, repository, update_dir, entries)
|
|
|
|
|
void *callerdat;
|
1996-08-20 23:46:10 +00:00
|
|
|
|
int err;
|
|
|
|
|
char *repository;
|
|
|
|
|
char *update_dir;
|
1997-05-15 22:46:24 +00:00
|
|
|
|
List *entries;
|
1996-08-20 23:46:10 +00:00
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
|
1997-05-23 14:48:05 +00:00
|
|
|
|
optind = 0;
|
1997-05-15 22:46:24 +00:00
|
|
|
|
while ((c = getopt (argc, argv, "+lR")) != -1)
|
1996-08-20 23:46:10 +00:00
|
|
|
|
{
|
|
|
|
|
switch (c)
|
|
|
|
|
{
|
|
|
|
|
case 'l':
|
|
|
|
|
local = 1;
|
|
|
|
|
break;
|
1997-05-15 22:46:24 +00:00
|
|
|
|
case 'R':
|
|
|
|
|
local = 0;
|
|
|
|
|
break;
|
1996-08-20 23:46:10 +00:00
|
|
|
|
case '?':
|
|
|
|
|
default:
|
|
|
|
|
usage (watch_usage);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
argc -= optind;
|
|
|
|
|
argv += optind;
|
|
|
|
|
|
|
|
|
|
#ifdef CLIENT_SUPPORT
|
2001-08-10 09:43:22 +00:00
|
|
|
|
if (current_parsed_root->isremote)
|
1996-08-20 23:46:10 +00:00
|
|
|
|
{
|
|
|
|
|
start_server ();
|
|
|
|
|
|
|
|
|
|
ign_setup ();
|
|
|
|
|
|
|
|
|
|
if (local)
|
|
|
|
|
send_arg ("-l");
|
1997-05-15 22:46:24 +00:00
|
|
|
|
send_files (argc, argv, local, 0, SEND_NO_CONTENTS);
|
1999-12-11 12:24:21 +00:00
|
|
|
|
send_file_names (argc, argv, SEND_EXPAND_WILD);
|
1996-08-20 23:46:10 +00:00
|
|
|
|
send_to_server (turning_on ? "watch-on\012" : "watch-off\012", 0);
|
|
|
|
|
return get_responses_and_close ();
|
|
|
|
|
}
|
|
|
|
|
#endif /* CLIENT_SUPPORT */
|
|
|
|
|
|
|
|
|
|
setting_default = (argc <= 0);
|
|
|
|
|
|
2001-08-10 09:43:22 +00:00
|
|
|
|
lock_tree_for_write (argc, argv, local, W_LOCAL, 0);
|
1996-08-20 23:46:10 +00:00
|
|
|
|
|
|
|
|
|
err = start_recursion (onoff_fileproc, onoff_filesdoneproc,
|
1997-05-15 22:46:24 +00:00
|
|
|
|
(DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL,
|
1996-08-20 23:46:10 +00:00
|
|
|
|
argc, argv, local, W_LOCAL, 0, 0, (char *)NULL,
|
1997-05-15 22:46:24 +00:00
|
|
|
|
0);
|
1996-08-20 23:46:10 +00:00
|
|
|
|
|
1997-05-15 22:46:24 +00:00
|
|
|
|
Lock_Cleanup ();
|
1996-08-20 23:46:10 +00:00
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
1997-05-15 22:46:24 +00:00
|
|
|
|
static int dummy_fileproc PROTO ((void *callerdat, struct file_info *finfo));
|
1996-08-20 23:46:10 +00:00
|
|
|
|
|
|
|
|
|
static int
|
1997-05-15 22:46:24 +00:00
|
|
|
|
dummy_fileproc (callerdat, finfo)
|
|
|
|
|
void *callerdat;
|
1996-08-20 23:46:10 +00:00
|
|
|
|
struct file_info *finfo;
|
|
|
|
|
{
|
|
|
|
|
/* This is a pretty hideous hack, but the gist of it is that recurse.c
|
|
|
|
|
won't call notify_check unless there is a fileproc, so we can't just
|
|
|
|
|
pass NULL for fileproc. */
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
1997-05-15 22:46:24 +00:00
|
|
|
|
static int ncheck_fileproc PROTO ((void *callerdat, struct file_info *finfo));
|
1996-08-20 23:46:10 +00:00
|
|
|
|
|
|
|
|
|
/* Check for and process notifications. Local only. I think that doing
|
|
|
|
|
this as a fileproc is the only way to catch all the
|
|
|
|
|
cases (e.g. foo/bar.c), even though that means checking over and over
|
|
|
|
|
for the same CVSADM_NOTIFY file which we removed the first time we
|
|
|
|
|
processed the directory. */
|
|
|
|
|
|
|
|
|
|
static int
|
1997-05-15 22:46:24 +00:00
|
|
|
|
ncheck_fileproc (callerdat, finfo)
|
|
|
|
|
void *callerdat;
|
1996-08-20 23:46:10 +00:00
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
/* We send notifications even if noexec. I'm not sure which behavior
|
|
|
|
|
is most sensible. */
|
|
|
|
|
|
1997-05-15 22:46:24 +00:00
|
|
|
|
fp = CVS_FOPEN (CVSADM_NOTIFY, "r");
|
1996-08-20 23:46:10 +00:00
|
|
|
|
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);
|
|
|
|
|
|
1997-05-15 22:46:24 +00:00
|
|
|
|
if ( CVS_UNLINK (CVSADM_NOTIFY) < 0)
|
1996-08-20 23:46:10 +00:00
|
|
|
|
error (0, errno, "cannot remove %s", CVSADM_NOTIFY);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int send_notifications PROTO ((int, char **, int));
|
|
|
|
|
|
|
|
|
|
/* Look through the CVSADM_NOTIFY file and process each item there
|
|
|
|
|
accordingly. */
|
|
|
|
|
static int
|
|
|
|
|
send_notifications (argc, argv, local)
|
|
|
|
|
int argc;
|
|
|
|
|
char **argv;
|
|
|
|
|
int local;
|
|
|
|
|
{
|
|
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
|
|
#ifdef CLIENT_SUPPORT
|
|
|
|
|
/* OK, we've done everything which needs to happen on the client side.
|
|
|
|
|
Now we can try to contact the server; if we fail, then the
|
|
|
|
|
notifications stay in CVSADM_NOTIFY to be sent next time. */
|
2001-08-10 09:43:22 +00:00
|
|
|
|
if (current_parsed_root->isremote)
|
1996-08-20 23:46:10 +00:00
|
|
|
|
{
|
|
|
|
|
if (strcmp (command_name, "release") != 0)
|
|
|
|
|
{
|
|
|
|
|
start_server ();
|
|
|
|
|
ign_setup ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err += start_recursion (dummy_fileproc, (FILESDONEPROC) NULL,
|
1997-05-15 22:46:24 +00:00
|
|
|
|
(DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL,
|
1996-08-20 23:46:10 +00:00
|
|
|
|
argc, argv, local, W_LOCAL, 0, 0, (char *)NULL,
|
1997-05-15 22:46:24 +00:00
|
|
|
|
0);
|
1996-08-20 23:46:10 +00:00
|
|
|
|
|
|
|
|
|
send_to_server ("noop\012", 0);
|
|
|
|
|
if (strcmp (command_name, "release") == 0)
|
|
|
|
|
err += get_server_responses ();
|
|
|
|
|
else
|
|
|
|
|
err += get_responses_and_close ();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
#endif
|
|
|
|
|
{
|
|
|
|
|
/* Local. */
|
|
|
|
|
|
2001-08-10 09:43:22 +00:00
|
|
|
|
lock_tree_for_write (argc, argv, local, W_LOCAL, 0);
|
1996-08-20 23:46:10 +00:00
|
|
|
|
err += start_recursion (ncheck_fileproc, (FILESDONEPROC) NULL,
|
1997-05-15 22:46:24 +00:00
|
|
|
|
(DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL,
|
1996-08-20 23:46:10 +00:00
|
|
|
|
argc, argv, local, W_LOCAL, 0, 0, (char *)NULL,
|
1997-05-15 22:46:24 +00:00
|
|
|
|
0);
|
|
|
|
|
Lock_Cleanup ();
|
1996-08-20 23:46:10 +00:00
|
|
|
|
}
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
1997-05-15 22:46:24 +00:00
|
|
|
|
static int edit_fileproc PROTO ((void *callerdat, struct file_info *finfo));
|
1996-08-20 23:46:10 +00:00
|
|
|
|
|
|
|
|
|
static int
|
1997-05-15 22:46:24 +00:00
|
|
|
|
edit_fileproc (callerdat, finfo)
|
|
|
|
|
void *callerdat;
|
1996-08-20 23:46:10 +00:00
|
|
|
|
struct file_info *finfo;
|
|
|
|
|
{
|
|
|
|
|
FILE *fp;
|
|
|
|
|
time_t now;
|
|
|
|
|
char *ascnow;
|
|
|
|
|
char *basefilename;
|
|
|
|
|
|
|
|
|
|
if (noexec)
|
|
|
|
|
return 0;
|
|
|
|
|
|
1998-01-26 03:09:57 +00:00
|
|
|
|
/* This is a somewhat screwy way to check for this, because it
|
|
|
|
|
doesn't help errors other than the nonexistence of the file
|
|
|
|
|
(e.g. permissions problems). It might be better to rearrange
|
|
|
|
|
the code so that CVSADM_NOTIFY gets written only after the
|
|
|
|
|
various actions succeed (but what if only some of them
|
|
|
|
|
succeed). */
|
|
|
|
|
if (!isfile (finfo->file))
|
|
|
|
|
{
|
|
|
|
|
error (0, 0, "no such file %s; ignored", finfo->fullname);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
1996-08-20 23:46:10 +00:00
|
|
|
|
fp = open_file (CVSADM_NOTIFY, "a");
|
|
|
|
|
|
|
|
|
|
(void) time (&now);
|
|
|
|
|
ascnow = asctime (gmtime (&now));
|
|
|
|
|
ascnow[24] = '\0';
|
2001-08-10 09:43:22 +00:00
|
|
|
|
/* Fix non-standard format. */
|
|
|
|
|
if (ascnow[8] == '0') ascnow[8] = ' ';
|
1996-08-20 23:46:10 +00:00
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
/* Now stash the file away in CVSADM so that unedit can revert even if
|
|
|
|
|
it can't communicate with the server. We stash away a writable
|
|
|
|
|
copy so that if the user removes the working file, then restores it
|
|
|
|
|
with "cvs update" (which clears _editors but does not update
|
|
|
|
|
CVSADM_BASE), then a future "cvs edit" can still win. */
|
1997-05-15 22:46:24 +00:00
|
|
|
|
/* Could save a system call by only calling mkdir_if_needed if
|
|
|
|
|
trying to create the output file fails. But copy_file isn't
|
|
|
|
|
set up to facilitate that. */
|
|
|
|
|
mkdir_if_needed (CVSADM_BASE);
|
1996-08-20 23:46:10 +00:00
|
|
|
|
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);
|
|
|
|
|
|
1998-01-26 03:09:57 +00:00
|
|
|
|
{
|
|
|
|
|
Node *node;
|
|
|
|
|
|
|
|
|
|
node = findnode_fn (finfo->entries, finfo->file);
|
|
|
|
|
if (node != NULL)
|
|
|
|
|
base_register (finfo, ((Entnode *) node->data)->version);
|
|
|
|
|
}
|
|
|
|
|
|
1996-08-20 23:46:10 +00:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char *const edit_usage[] =
|
|
|
|
|
{
|
1997-05-15 22:46:24 +00:00
|
|
|
|
"Usage: %s %s [-lR] [files...]\n",
|
1996-08-20 23:46:10 +00:00
|
|
|
|
"-l: Local directory only, not recursive\n",
|
1997-05-15 22:46:24 +00:00
|
|
|
|
"-R: Process directories recursively\n",
|
1996-08-20 23:46:10 +00:00
|
|
|
|
"-a: Specify what actions for temporary watch, one of\n",
|
1997-05-15 22:46:24 +00:00
|
|
|
|
" edit,unedit,commit,all,none\n",
|
1998-01-26 03:09:57 +00:00
|
|
|
|
"(Specify the --help global option for a list of other help options)\n",
|
1996-08-20 23:46:10 +00:00
|
|
|
|
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;
|
1997-05-23 14:48:05 +00:00
|
|
|
|
optind = 0;
|
1997-05-15 22:46:24 +00:00
|
|
|
|
while ((c = getopt (argc, argv, "+lRa:")) != -1)
|
1996-08-20 23:46:10 +00:00
|
|
|
|
{
|
|
|
|
|
switch (c)
|
|
|
|
|
{
|
|
|
|
|
case 'l':
|
|
|
|
|
local = 1;
|
|
|
|
|
break;
|
1997-05-15 22:46:24 +00:00
|
|
|
|
case 'R':
|
|
|
|
|
local = 0;
|
|
|
|
|
break;
|
1996-08-20 23:46:10 +00:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2000-10-02 06:33:59 +00:00
|
|
|
|
if (strpbrk (hostname, "+,>;=\t\n") != NULL)
|
|
|
|
|
error (1, 0,
|
|
|
|
|
"host name (%s) contains an invalid character (+,>;=\\t\\n)",
|
|
|
|
|
hostname);
|
|
|
|
|
if (strpbrk (CurDir, "+,>;=\t\n") != NULL)
|
|
|
|
|
error (1, 0,
|
|
|
|
|
"current directory (%s) contains an invalid character (+,>;=\\t\\n)",
|
|
|
|
|
CurDir);
|
|
|
|
|
|
1996-08-20 23:46:10 +00:00
|
|
|
|
/* No need to readlock since we aren't doing anything to the
|
|
|
|
|
repository. */
|
|
|
|
|
err = start_recursion (edit_fileproc, (FILESDONEPROC) NULL,
|
1997-05-15 22:46:24 +00:00
|
|
|
|
(DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL,
|
1996-08-20 23:46:10 +00:00
|
|
|
|
argc, argv, local, W_LOCAL, 0, 0, (char *)NULL,
|
1997-05-15 22:46:24 +00:00
|
|
|
|
0);
|
1996-08-20 23:46:10 +00:00
|
|
|
|
|
|
|
|
|
err += send_notifications (argc, argv, local);
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
1997-05-15 22:46:24 +00:00
|
|
|
|
static int unedit_fileproc PROTO ((void *callerdat, struct file_info *finfo));
|
1996-08-20 23:46:10 +00:00
|
|
|
|
|
|
|
|
|
static int
|
1997-05-15 22:46:24 +00:00
|
|
|
|
unedit_fileproc (callerdat, finfo)
|
|
|
|
|
void *callerdat;
|
1996-08-20 23:46:10 +00:00
|
|
|
|
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))
|
|
|
|
|
{
|
|
|
|
|
/* This file apparently was never cvs edit'd (e.g. we are uneditting
|
|
|
|
|
a directory where only some of the files were cvs edit'd. */
|
|
|
|
|
free (basefilename);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (xcmp (finfo->file, basefilename) != 0)
|
|
|
|
|
{
|
|
|
|
|
printf ("%s has been modified; revert changes? ", finfo->fullname);
|
|
|
|
|
if (!yesno ())
|
|
|
|
|
{
|
|
|
|
|
/* "no". */
|
|
|
|
|
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';
|
2001-08-10 09:43:22 +00:00
|
|
|
|
/* Fix non-standard format. */
|
|
|
|
|
if (ascnow[8] == '0') ascnow[8] = ' ';
|
1996-08-20 23:46:10 +00:00
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
1998-01-26 03:09:57 +00:00
|
|
|
|
/* Now update the revision number in CVS/Entries from CVS/Baserev.
|
|
|
|
|
The basic idea here is that we are reverting to the revision
|
|
|
|
|
that the user edited. If we wanted "cvs update" to update
|
|
|
|
|
CVS/Base as we go along (so that an unedit could revert to the
|
|
|
|
|
current repository revision), we would need:
|
|
|
|
|
|
|
|
|
|
update (or all send_files?) (client) needs to send revision in
|
|
|
|
|
new Entry-base request. update (server/local) needs to check
|
|
|
|
|
revision against repository and send new Update-base response
|
|
|
|
|
(like Update-existing in that the file already exists. While
|
|
|
|
|
we are at it, might try to clean up the syntax by having the
|
|
|
|
|
mode only in a "Mode" response, not in the Update-base itself). */
|
|
|
|
|
{
|
|
|
|
|
char *baserev;
|
|
|
|
|
Node *node;
|
|
|
|
|
Entnode *entdata;
|
|
|
|
|
|
|
|
|
|
baserev = base_get (finfo);
|
|
|
|
|
node = findnode_fn (finfo->entries, finfo->file);
|
|
|
|
|
/* The case where node is NULL probably should be an error or
|
|
|
|
|
something, but I don't want to think about it too hard right
|
|
|
|
|
now. */
|
|
|
|
|
if (node != NULL)
|
|
|
|
|
{
|
|
|
|
|
entdata = (Entnode *) node->data;
|
1999-03-18 09:21:42 +00:00
|
|
|
|
if (baserev == NULL)
|
|
|
|
|
{
|
|
|
|
|
/* This can only happen if the CVS/Baserev file got
|
|
|
|
|
corrupted. We suspect it might be possible if the
|
|
|
|
|
user interrupts CVS, although I haven't verified
|
|
|
|
|
that. */
|
|
|
|
|
error (0, 0, "%s not mentioned in %s", finfo->fullname,
|
|
|
|
|
CVSADM_BASEREV);
|
|
|
|
|
|
|
|
|
|
/* Since we don't know what revision the file derives from,
|
|
|
|
|
keeping it around would be asking for trouble. */
|
|
|
|
|
if (unlink_file (finfo->file) < 0)
|
|
|
|
|
error (0, errno, "cannot remove %s", finfo->fullname);
|
|
|
|
|
|
|
|
|
|
/* This is cheesy, in a sense; why shouldn't we do the
|
|
|
|
|
update for the user? However, doing that would require
|
|
|
|
|
contacting the server, so maybe this is OK. */
|
|
|
|
|
error (0, 0, "run update to complete the unedit");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
1998-01-26 03:09:57 +00:00
|
|
|
|
Register (finfo->entries, finfo->file, baserev, entdata->timestamp,
|
|
|
|
|
entdata->options, entdata->tag, entdata->date,
|
|
|
|
|
entdata->conflict);
|
|
|
|
|
}
|
|
|
|
|
free (baserev);
|
|
|
|
|
base_deregister (finfo);
|
|
|
|
|
}
|
|
|
|
|
|
1996-08-20 23:46:10 +00:00
|
|
|
|
xchmod (finfo->file, 0);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2000-10-02 06:33:59 +00:00
|
|
|
|
static const char *const unedit_usage[] =
|
|
|
|
|
{
|
|
|
|
|
"Usage: %s %s [-lR] [files...]\n",
|
|
|
|
|
"-l: Local directory only, not recursive\n",
|
|
|
|
|
"-R: Process directories recursively\n",
|
|
|
|
|
"(Specify the --help global option for a list of other help options)\n",
|
|
|
|
|
NULL
|
|
|
|
|
};
|
|
|
|
|
|
1996-08-20 23:46:10 +00:00
|
|
|
|
int
|
|
|
|
|
unedit (argc, argv)
|
|
|
|
|
int argc;
|
|
|
|
|
char **argv;
|
|
|
|
|
{
|
|
|
|
|
int local = 0;
|
|
|
|
|
int c;
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
if (argc == -1)
|
2000-10-02 06:33:59 +00:00
|
|
|
|
usage (unedit_usage);
|
1996-08-20 23:46:10 +00:00
|
|
|
|
|
1997-05-23 14:48:05 +00:00
|
|
|
|
optind = 0;
|
1997-05-15 22:46:24 +00:00
|
|
|
|
while ((c = getopt (argc, argv, "+lR")) != -1)
|
1996-08-20 23:46:10 +00:00
|
|
|
|
{
|
|
|
|
|
switch (c)
|
|
|
|
|
{
|
|
|
|
|
case 'l':
|
|
|
|
|
local = 1;
|
|
|
|
|
break;
|
1997-05-15 22:46:24 +00:00
|
|
|
|
case 'R':
|
|
|
|
|
local = 0;
|
|
|
|
|
break;
|
1996-08-20 23:46:10 +00:00
|
|
|
|
case '?':
|
|
|
|
|
default:
|
2000-10-02 06:33:59 +00:00
|
|
|
|
usage (unedit_usage);
|
1996-08-20 23:46:10 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
argc -= optind;
|
|
|
|
|
argv += optind;
|
|
|
|
|
|
|
|
|
|
/* No need to readlock since we aren't doing anything to the
|
|
|
|
|
repository. */
|
|
|
|
|
err = start_recursion (unedit_fileproc, (FILESDONEPROC) NULL,
|
1997-05-15 22:46:24 +00:00
|
|
|
|
(DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL,
|
1996-08-20 23:46:10 +00:00
|
|
|
|
argc, argv, local, W_LOCAL, 0, 0, (char *)NULL,
|
1997-05-15 22:46:24 +00:00
|
|
|
|
0);
|
1996-08-20 23:46:10 +00:00
|
|
|
|
|
|
|
|
|
err += send_notifications (argc, argv, local);
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
mark_up_to_date (file)
|
|
|
|
|
char *file;
|
|
|
|
|
{
|
|
|
|
|
char *base;
|
|
|
|
|
|
|
|
|
|
/* The file is up to date, so we better get rid of an out of
|
|
|
|
|
date file in CVSADM_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 the attributes is unchanged, don't rewrite the attribute file. */
|
|
|
|
|
if (!((edlist == NULL && newlist == NULL)
|
|
|
|
|
|| (edlist != NULL
|
|
|
|
|
&& newlist != NULL
|
|
|
|
|
&& strcmp (edlist, newlist) == 0)))
|
|
|
|
|
fileattr_set (filename, "_editors", newlist);
|
1998-01-26 03:09:57 +00:00
|
|
|
|
if (edlist != NULL)
|
|
|
|
|
free (edlist);
|
1996-08-20 23:46:10 +00:00
|
|
|
|
if (newlist != NULL)
|
|
|
|
|
free (newlist);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct notify_proc_args {
|
|
|
|
|
/* What kind of notification, "edit", "tedit", etc. */
|
|
|
|
|
char *type;
|
|
|
|
|
/* User who is running the command which causes notification. */
|
|
|
|
|
char *who;
|
|
|
|
|
/* User to be notified. */
|
|
|
|
|
char *notifyee;
|
|
|
|
|
/* File. */
|
|
|
|
|
char *file;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* Pass as a static until we get around to fixing Parse_Info to pass along
|
|
|
|
|
a void * where we can stash it. */
|
|
|
|
|
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);
|
|
|
|
|
/* Copy FILTER to PROG, replacing the first occurrence of %s with
|
|
|
|
|
the notifyee. We only allocated enough memory for one %s, and I doubt
|
|
|
|
|
there is a need for more. */
|
|
|
|
|
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';
|
|
|
|
|
|
|
|
|
|
/* FIXME: why are we calling expand_proc? Didn't we already
|
|
|
|
|
expand it in Parse_Info, before passing it to notify_proc? */
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
/* Lots more potentially useful information we could add here; see
|
|
|
|
|
logfile_write for inspiration. */
|
|
|
|
|
|
|
|
|
|
free (prog);
|
|
|
|
|
free (expanded_prog);
|
|
|
|
|
return (pclose (pipefp));
|
|
|
|
|
}
|
|
|
|
|
|
1999-12-11 12:24:21 +00:00
|
|
|
|
/* FIXME: this function should have a way to report whether there was
|
|
|
|
|
an error so that server.c can know whether to report Notified back
|
|
|
|
|
to the client. */
|
1996-08-20 23:46:10 +00:00
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
/* Initialize fields to 0, NULL, or 0.0. */
|
|
|
|
|
args = blank;
|
|
|
|
|
switch (type)
|
|
|
|
|
{
|
|
|
|
|
case 'E':
|
1999-12-11 12:24:21 +00:00
|
|
|
|
if (strpbrk (val, ",>;=\n") != NULL)
|
|
|
|
|
{
|
|
|
|
|
error (0, 0, "invalid character in editor value");
|
|
|
|
|
return;
|
|
|
|
|
}
|
1996-08-20 23:46:10 +00:00
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
/* Don't notify user of their own changes. Would perhaps
|
|
|
|
|
be better to check whether it is the same working
|
|
|
|
|
directory, not the same user, but that is hairy. */
|
|
|
|
|
p = nextp == NULL ? nextp : nextp + 1;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Now we point q at a string which looks like
|
|
|
|
|
"edit+unedit+commit,"... and walk down it. */
|
|
|
|
|
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 there is a temporary and a regular watch, send a single
|
|
|
|
|
notification, for the regular watch. */
|
|
|
|
|
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;
|
2001-08-10 09:43:22 +00:00
|
|
|
|
usersname = xmalloc (strlen (current_parsed_root->directory)
|
1996-08-20 23:46:10 +00:00
|
|
|
|
+ sizeof CVSROOTADM
|
|
|
|
|
+ sizeof CVSROOTADM_USERS
|
|
|
|
|
+ 20);
|
2001-08-10 09:43:22 +00:00
|
|
|
|
strcpy (usersname, current_parsed_root->directory);
|
1996-08-20 23:46:10 +00:00
|
|
|
|
strcat (usersname, "/");
|
|
|
|
|
strcat (usersname, CVSROOTADM);
|
|
|
|
|
strcat (usersname, "/");
|
|
|
|
|
strcat (usersname, CVSROOTADM_USERS);
|
1997-05-15 22:46:24 +00:00
|
|
|
|
fp = CVS_FOPEN (usersname, "r");
|
1996-08-20 23:46:10 +00:00
|
|
|
|
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);
|
1999-12-11 12:24:21 +00:00
|
|
|
|
|
|
|
|
|
/* There may or may not be more
|
|
|
|
|
colon-separated fields added to this in the
|
|
|
|
|
future; in any case, we ignore them right
|
|
|
|
|
now, and if there are none we make sure to
|
|
|
|
|
chop off the final newline, if any. */
|
|
|
|
|
cp = strpbrk (args.notifyee, ":\n");
|
|
|
|
|
|
1996-08-20 23:46:10 +00:00
|
|
|
|
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);
|
1999-12-11 12:24:21 +00:00
|
|
|
|
if (line != NULL)
|
|
|
|
|
free (line);
|
1996-08-20 23:46:10 +00:00
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
/* Check and send notifications. This is only for the client. */
|
|
|
|
|
void
|
|
|
|
|
notify_check (repository, update_dir)
|
|
|
|
|
char *repository;
|
|
|
|
|
char *update_dir;
|
|
|
|
|
{
|
|
|
|
|
FILE *fp;
|
|
|
|
|
char *line = NULL;
|
|
|
|
|
size_t line_len = 0;
|
|
|
|
|
|
|
|
|
|
if (! server_started)
|
|
|
|
|
/* We are in the midst of a command which is not to talk to
|
|
|
|
|
the server (e.g. the first phase of a cvs edit). Just chill
|
|
|
|
|
out, we'll catch the notifications on the flip side. */
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/* We send notifications even if noexec. I'm not sure which behavior
|
|
|
|
|
is most sensible. */
|
|
|
|
|
|
1997-05-15 22:46:24 +00:00
|
|
|
|
fp = CVS_FOPEN (CVSADM_NOTIFY, "r");
|
1996-08-20 23:46:10 +00:00
|
|
|
|
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);
|
|
|
|
|
}
|
1997-05-15 22:46:24 +00:00
|
|
|
|
if (line)
|
|
|
|
|
free (line);
|
1996-08-20 23:46:10 +00:00
|
|
|
|
if (ferror (fp))
|
|
|
|
|
error (0, errno, "cannot read %s", CVSADM_NOTIFY);
|
|
|
|
|
if (fclose (fp) < 0)
|
|
|
|
|
error (0, errno, "cannot close %s", CVSADM_NOTIFY);
|
|
|
|
|
|
|
|
|
|
/* Leave the CVSADM_NOTIFY file there, until the server tells us it
|
|
|
|
|
has dealt with it. */
|
|
|
|
|
}
|
|
|
|
|
#endif /* CLIENT_SUPPORT */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static const char *const editors_usage[] =
|
|
|
|
|
{
|
1997-05-15 22:46:24 +00:00
|
|
|
|
"Usage: %s %s [-lR] [files...]\n",
|
|
|
|
|
"\t-l\tProcess this directory only (not recursive).\n",
|
|
|
|
|
"\t-R\tProcess directories recursively.\n",
|
1998-01-26 03:09:57 +00:00
|
|
|
|
"(Specify the --help global option for a list of other help options)\n",
|
1996-08-20 23:46:10 +00:00
|
|
|
|
NULL
|
|
|
|
|
};
|
|
|
|
|
|
1997-05-15 22:46:24 +00:00
|
|
|
|
static int editors_fileproc PROTO ((void *callerdat, struct file_info *finfo));
|
1996-08-20 23:46:10 +00:00
|
|
|
|
|
|
|
|
|
static int
|
1997-05-15 22:46:24 +00:00
|
|
|
|
editors_fileproc (callerdat, finfo)
|
|
|
|
|
void *callerdat;
|
1996-08-20 23:46:10 +00:00
|
|
|
|
struct file_info *finfo;
|
|
|
|
|
{
|
|
|
|
|
char *them;
|
|
|
|
|
char *p;
|
|
|
|
|
|
|
|
|
|
them = fileattr_get0 (finfo->file, "_editors");
|
|
|
|
|
if (them == NULL)
|
|
|
|
|
return 0;
|
|
|
|
|
|
1999-12-11 12:24:21 +00:00
|
|
|
|
cvs_output (finfo->fullname, 0);
|
1996-08-20 23:46:10 +00:00
|
|
|
|
|
|
|
|
|
p = them;
|
|
|
|
|
while (1)
|
|
|
|
|
{
|
1999-12-11 12:24:21 +00:00
|
|
|
|
cvs_output ("\t", 1);
|
1996-08-20 23:46:10 +00:00
|
|
|
|
while (*p != '>' && *p != '\0')
|
1999-12-11 12:24:21 +00:00
|
|
|
|
cvs_output (p++, 1);
|
1996-08-20 23:46:10 +00:00
|
|
|
|
if (*p == '\0')
|
|
|
|
|
{
|
|
|
|
|
/* Only happens if attribute is misformed. */
|
1999-12-11 12:24:21 +00:00
|
|
|
|
cvs_output ("\n", 1);
|
1996-08-20 23:46:10 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
++p;
|
1999-12-11 12:24:21 +00:00
|
|
|
|
cvs_output ("\t", 1);
|
1996-08-20 23:46:10 +00:00
|
|
|
|
while (1)
|
|
|
|
|
{
|
|
|
|
|
while (*p != '+' && *p != ',' && *p != '\0')
|
1999-12-11 12:24:21 +00:00
|
|
|
|
cvs_output (p++, 1);
|
1996-08-20 23:46:10 +00:00
|
|
|
|
if (*p == '\0')
|
|
|
|
|
{
|
1999-12-11 12:24:21 +00:00
|
|
|
|
cvs_output ("\n", 1);
|
1996-08-20 23:46:10 +00:00
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
if (*p == ',')
|
|
|
|
|
{
|
|
|
|
|
++p;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
++p;
|
1999-12-11 12:24:21 +00:00
|
|
|
|
cvs_output ("\t", 1);
|
1996-08-20 23:46:10 +00:00
|
|
|
|
}
|
1999-12-11 12:24:21 +00:00
|
|
|
|
cvs_output ("\n", 1);
|
1996-08-20 23:46:10 +00:00
|
|
|
|
}
|
|
|
|
|
out:;
|
2000-10-02 06:33:59 +00:00
|
|
|
|
free (them);
|
1996-08-20 23:46:10 +00:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
editors (argc, argv)
|
|
|
|
|
int argc;
|
|
|
|
|
char **argv;
|
|
|
|
|
{
|
|
|
|
|
int local = 0;
|
|
|
|
|
int c;
|
|
|
|
|
|
|
|
|
|
if (argc == -1)
|
|
|
|
|
usage (editors_usage);
|
|
|
|
|
|
1997-05-23 14:48:05 +00:00
|
|
|
|
optind = 0;
|
1997-05-15 22:46:24 +00:00
|
|
|
|
while ((c = getopt (argc, argv, "+lR")) != -1)
|
1996-08-20 23:46:10 +00:00
|
|
|
|
{
|
|
|
|
|
switch (c)
|
|
|
|
|
{
|
|
|
|
|
case 'l':
|
|
|
|
|
local = 1;
|
|
|
|
|
break;
|
1997-05-15 22:46:24 +00:00
|
|
|
|
case 'R':
|
|
|
|
|
local = 0;
|
|
|
|
|
break;
|
1996-08-20 23:46:10 +00:00
|
|
|
|
case '?':
|
|
|
|
|
default:
|
|
|
|
|
usage (editors_usage);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
argc -= optind;
|
|
|
|
|
argv += optind;
|
|
|
|
|
|
|
|
|
|
#ifdef CLIENT_SUPPORT
|
2001-08-10 09:43:22 +00:00
|
|
|
|
if (current_parsed_root->isremote)
|
1996-08-20 23:46:10 +00:00
|
|
|
|
{
|
|
|
|
|
start_server ();
|
|
|
|
|
ign_setup ();
|
|
|
|
|
|
|
|
|
|
if (local)
|
|
|
|
|
send_arg ("-l");
|
1997-05-15 22:46:24 +00:00
|
|
|
|
send_files (argc, argv, local, 0, SEND_NO_CONTENTS);
|
1999-12-11 12:24:21 +00:00
|
|
|
|
send_file_names (argc, argv, SEND_EXPAND_WILD);
|
1996-08-20 23:46:10 +00:00
|
|
|
|
send_to_server ("editors\012", 0);
|
|
|
|
|
return get_responses_and_close ();
|
|
|
|
|
}
|
|
|
|
|
#endif /* CLIENT_SUPPORT */
|
|
|
|
|
|
|
|
|
|
return start_recursion (editors_fileproc, (FILESDONEPROC) NULL,
|
1997-05-15 22:46:24 +00:00
|
|
|
|
(DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL,
|
1996-08-20 23:46:10 +00:00
|
|
|
|
argc, argv, local, W_LOCAL, 0, 1, (char *)NULL,
|
1997-05-15 22:46:24 +00:00
|
|
|
|
0);
|
1996-08-20 23:46:10 +00:00
|
|
|
|
}
|