xref: /dflybsd-src/contrib/cvs-1.12/src/watch.c (revision 86d7f5d305c6adaa56ff4582ece9859d73106103)
186d7f5d3SJohn Marino /* Implementation for "cvs watch add", "cvs watchers", and related commands
286d7f5d3SJohn Marino 
386d7f5d3SJohn Marino    This program is free software; you can redistribute it and/or modify
486d7f5d3SJohn Marino    it under the terms of the GNU General Public License as published by
586d7f5d3SJohn Marino    the Free Software Foundation; either version 2, or (at your option)
686d7f5d3SJohn Marino    any later version.
786d7f5d3SJohn Marino 
886d7f5d3SJohn Marino    This program is distributed in the hope that it will be useful,
986d7f5d3SJohn Marino    but WITHOUT ANY WARRANTY; without even the implied warranty of
1086d7f5d3SJohn Marino    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1186d7f5d3SJohn Marino    GNU General Public License for more details.  */
1286d7f5d3SJohn Marino 
1386d7f5d3SJohn Marino #include "cvs.h"
1486d7f5d3SJohn Marino #include "edit.h"
1586d7f5d3SJohn Marino #include "fileattr.h"
1686d7f5d3SJohn Marino #include "watch.h"
1786d7f5d3SJohn Marino 
1886d7f5d3SJohn Marino const char *const watch_usage[] =
1986d7f5d3SJohn Marino {
2086d7f5d3SJohn Marino     "Usage: %s %s {on|off|add|remove} [-lR] [-a <action>]... [<path>]...\n",
2186d7f5d3SJohn Marino     "on/off: Turn on/off read-only checkouts of files.\n",
2286d7f5d3SJohn Marino     "add/remove: Add or remove notification on actions.\n",
2386d7f5d3SJohn Marino     "-l (on/off/add/remove): Local directory only, not recursive.\n",
2486d7f5d3SJohn Marino     "-R (on/off/add/remove): Process directories recursively (default).\n",
2586d7f5d3SJohn Marino     "-a (add/remove): Specify what actions, one of: `edit', `unedit',\n",
2686d7f5d3SJohn Marino     "                 `commit', `all', or `none' (defaults to `all').\n",
2786d7f5d3SJohn Marino     "(Specify the --help global option for a list of other help options.)\n",
2886d7f5d3SJohn Marino     NULL
2986d7f5d3SJohn Marino };
3086d7f5d3SJohn Marino 
3186d7f5d3SJohn Marino static struct addremove_args the_args;
3286d7f5d3SJohn Marino 
3386d7f5d3SJohn Marino void
watch_modify_watchers(const char * file,struct addremove_args * what)3486d7f5d3SJohn Marino watch_modify_watchers (const char *file, struct addremove_args *what)
3586d7f5d3SJohn Marino {
3686d7f5d3SJohn Marino     char *curattr = fileattr_get0 (file, "_watchers");
3786d7f5d3SJohn Marino     char *p;
3886d7f5d3SJohn Marino     char *pend;
3986d7f5d3SJohn Marino     char *nextp;
4086d7f5d3SJohn Marino     char *who;
4186d7f5d3SJohn Marino     int who_len;
4286d7f5d3SJohn Marino     char *mycurattr;
4386d7f5d3SJohn Marino     char *mynewattr;
4486d7f5d3SJohn Marino     size_t mynewattr_size;
4586d7f5d3SJohn Marino 
4686d7f5d3SJohn Marino     int add_edit_pending;
4786d7f5d3SJohn Marino     int add_unedit_pending;
4886d7f5d3SJohn Marino     int add_commit_pending;
4986d7f5d3SJohn Marino     int remove_edit_pending;
5086d7f5d3SJohn Marino     int remove_unedit_pending;
5186d7f5d3SJohn Marino     int remove_commit_pending;
5286d7f5d3SJohn Marino     int add_tedit_pending;
5386d7f5d3SJohn Marino     int add_tunedit_pending;
5486d7f5d3SJohn Marino     int add_tcommit_pending;
5586d7f5d3SJohn Marino 
5686d7f5d3SJohn Marino     TRACE( TRACE_FUNCTION, "modify_watchers ( %s )", file );
5786d7f5d3SJohn Marino 
5886d7f5d3SJohn Marino     who = getcaller ();
5986d7f5d3SJohn Marino     who_len = strlen (who);
6086d7f5d3SJohn Marino 
6186d7f5d3SJohn Marino     /* Look for current watcher types for this user.  */
6286d7f5d3SJohn Marino     mycurattr = NULL;
6386d7f5d3SJohn Marino     if (curattr != NULL)
6486d7f5d3SJohn Marino     {
6586d7f5d3SJohn Marino 	p = curattr;
6686d7f5d3SJohn Marino 	while (1) {
6786d7f5d3SJohn Marino 	    if (strncmp (who, p, who_len) == 0
6886d7f5d3SJohn Marino 		&& p[who_len] == '>')
6986d7f5d3SJohn Marino 	    {
7086d7f5d3SJohn Marino 		/* Found this user.  */
7186d7f5d3SJohn Marino 		mycurattr = p + who_len + 1;
7286d7f5d3SJohn Marino 	    }
7386d7f5d3SJohn Marino 	    p = strchr (p, ',');
7486d7f5d3SJohn Marino 	    if (p == NULL)
7586d7f5d3SJohn Marino 		break;
7686d7f5d3SJohn Marino 	    ++p;
7786d7f5d3SJohn Marino 	}
7886d7f5d3SJohn Marino     }
7986d7f5d3SJohn Marino     if (mycurattr != NULL)
8086d7f5d3SJohn Marino     {
8186d7f5d3SJohn Marino 	mycurattr = xstrdup (mycurattr);
8286d7f5d3SJohn Marino 	p = strchr (mycurattr, ',');
8386d7f5d3SJohn Marino 	if (p != NULL)
8486d7f5d3SJohn Marino 	    *p = '\0';
8586d7f5d3SJohn Marino     }
8686d7f5d3SJohn Marino 
8786d7f5d3SJohn Marino     /* Now copy mycurattr to mynewattr, making the requisite modifications.
8886d7f5d3SJohn Marino        Note that we add a dummy '+' to the start of mynewattr, to reduce
8986d7f5d3SJohn Marino        special cases (but then we strip it off when we are done).  */
9086d7f5d3SJohn Marino 
9186d7f5d3SJohn Marino     mynewattr_size = sizeof "+edit+unedit+commit+tedit+tunedit+tcommit";
9286d7f5d3SJohn Marino     if (mycurattr != NULL)
9386d7f5d3SJohn Marino 	mynewattr_size += strlen (mycurattr);
9486d7f5d3SJohn Marino     mynewattr = xmalloc (mynewattr_size);
9586d7f5d3SJohn Marino     mynewattr[0] = '\0';
9686d7f5d3SJohn Marino 
9786d7f5d3SJohn Marino     add_edit_pending = what->adding && what->edit;
9886d7f5d3SJohn Marino     add_unedit_pending = what->adding && what->unedit;
9986d7f5d3SJohn Marino     add_commit_pending = what->adding && what->commit;
10086d7f5d3SJohn Marino     remove_edit_pending = !what->adding && what->edit;
10186d7f5d3SJohn Marino     remove_unedit_pending = !what->adding && what->unedit;
10286d7f5d3SJohn Marino     remove_commit_pending = !what->adding && what->commit;
10386d7f5d3SJohn Marino     add_tedit_pending = what->add_tedit;
10486d7f5d3SJohn Marino     add_tunedit_pending = what->add_tunedit;
10586d7f5d3SJohn Marino     add_tcommit_pending = what->add_tcommit;
10686d7f5d3SJohn Marino 
10786d7f5d3SJohn Marino     /* Copy over existing watch types, except those to be removed.  */
10886d7f5d3SJohn Marino     p = mycurattr;
10986d7f5d3SJohn Marino     while (p != NULL)
11086d7f5d3SJohn Marino     {
11186d7f5d3SJohn Marino 	pend = strchr (p, '+');
11286d7f5d3SJohn Marino 	if (pend == NULL)
11386d7f5d3SJohn Marino 	{
11486d7f5d3SJohn Marino 	    pend = p + strlen (p);
11586d7f5d3SJohn Marino 	    nextp = NULL;
11686d7f5d3SJohn Marino 	}
11786d7f5d3SJohn Marino 	else
11886d7f5d3SJohn Marino 	    nextp = pend + 1;
11986d7f5d3SJohn Marino 
12086d7f5d3SJohn Marino 	/* Process this item.  */
12186d7f5d3SJohn Marino 	if (pend - p == 4 && strncmp ("edit", p, 4) == 0)
12286d7f5d3SJohn Marino 	{
12386d7f5d3SJohn Marino 	    if (!remove_edit_pending)
12486d7f5d3SJohn Marino 		strcat (mynewattr, "+edit");
12586d7f5d3SJohn Marino 	    add_edit_pending = 0;
12686d7f5d3SJohn Marino 	}
12786d7f5d3SJohn Marino 	else if (pend - p == 6 && strncmp ("unedit", p, 6) == 0)
12886d7f5d3SJohn Marino 	{
12986d7f5d3SJohn Marino 	    if (!remove_unedit_pending)
13086d7f5d3SJohn Marino 		strcat (mynewattr, "+unedit");
13186d7f5d3SJohn Marino 	    add_unedit_pending = 0;
13286d7f5d3SJohn Marino 	}
13386d7f5d3SJohn Marino 	else if (pend - p == 6 && strncmp ("commit", p, 6) == 0)
13486d7f5d3SJohn Marino 	{
13586d7f5d3SJohn Marino 	    if (!remove_commit_pending)
13686d7f5d3SJohn Marino 		strcat (mynewattr, "+commit");
13786d7f5d3SJohn Marino 	    add_commit_pending = 0;
13886d7f5d3SJohn Marino 	}
13986d7f5d3SJohn Marino 	else if (pend - p == 5 && strncmp ("tedit", p, 5) == 0)
14086d7f5d3SJohn Marino 	{
14186d7f5d3SJohn Marino 	    if (!what->remove_temp)
14286d7f5d3SJohn Marino 		strcat (mynewattr, "+tedit");
14386d7f5d3SJohn Marino 	    add_tedit_pending = 0;
14486d7f5d3SJohn Marino 	}
14586d7f5d3SJohn Marino 	else if (pend - p == 7 && strncmp ("tunedit", p, 7) == 0)
14686d7f5d3SJohn Marino 	{
14786d7f5d3SJohn Marino 	    if (!what->remove_temp)
14886d7f5d3SJohn Marino 		strcat (mynewattr, "+tunedit");
14986d7f5d3SJohn Marino 	    add_tunedit_pending = 0;
15086d7f5d3SJohn Marino 	}
15186d7f5d3SJohn Marino 	else if (pend - p == 7 && strncmp ("tcommit", p, 7) == 0)
15286d7f5d3SJohn Marino 	{
15386d7f5d3SJohn Marino 	    if (!what->remove_temp)
15486d7f5d3SJohn Marino 		strcat (mynewattr, "+tcommit");
15586d7f5d3SJohn Marino 	    add_tcommit_pending = 0;
15686d7f5d3SJohn Marino 	}
15786d7f5d3SJohn Marino 	else
15886d7f5d3SJohn Marino 	{
15986d7f5d3SJohn Marino 	    char *mp;
16086d7f5d3SJohn Marino 
16186d7f5d3SJohn Marino 	    /* Copy over any unrecognized watch types, for future
16286d7f5d3SJohn Marino 	       expansion.  */
16386d7f5d3SJohn Marino 	    mp = mynewattr + strlen (mynewattr);
16486d7f5d3SJohn Marino 	    *mp++ = '+';
16586d7f5d3SJohn Marino 	    strncpy (mp, p, pend - p);
16686d7f5d3SJohn Marino 	    *(mp + (pend - p)) = '\0';
16786d7f5d3SJohn Marino 	}
16886d7f5d3SJohn Marino 
16986d7f5d3SJohn Marino 	/* Set up for next item.  */
17086d7f5d3SJohn Marino 	p = nextp;
17186d7f5d3SJohn Marino     }
17286d7f5d3SJohn Marino 
17386d7f5d3SJohn Marino     /* Add in new watch types.  */
17486d7f5d3SJohn Marino     if (add_edit_pending)
17586d7f5d3SJohn Marino 	strcat (mynewattr, "+edit");
17686d7f5d3SJohn Marino     if (add_unedit_pending)
17786d7f5d3SJohn Marino 	strcat (mynewattr, "+unedit");
17886d7f5d3SJohn Marino     if (add_commit_pending)
17986d7f5d3SJohn Marino 	strcat (mynewattr, "+commit");
18086d7f5d3SJohn Marino     if (add_tedit_pending)
18186d7f5d3SJohn Marino 	strcat (mynewattr, "+tedit");
18286d7f5d3SJohn Marino     if (add_tunedit_pending)
18386d7f5d3SJohn Marino 	strcat (mynewattr, "+tunedit");
18486d7f5d3SJohn Marino     if (add_tcommit_pending)
18586d7f5d3SJohn Marino 	strcat (mynewattr, "+tcommit");
18686d7f5d3SJohn Marino 
18786d7f5d3SJohn Marino     {
18886d7f5d3SJohn Marino 	char *curattr_new;
18986d7f5d3SJohn Marino 
19086d7f5d3SJohn Marino 	curattr_new =
19186d7f5d3SJohn Marino 	  fileattr_modify (curattr,
19286d7f5d3SJohn Marino 			   who,
19386d7f5d3SJohn Marino 			   mynewattr[0] == '\0' ? NULL : mynewattr + 1,
19486d7f5d3SJohn Marino 			   '>',
19586d7f5d3SJohn Marino 			   ',');
19686d7f5d3SJohn Marino 	/* If the attribute is unchanged, don't rewrite the attribute file.  */
19786d7f5d3SJohn Marino 	if (!((curattr_new == NULL && curattr == NULL)
19886d7f5d3SJohn Marino 	      || (curattr_new != NULL
19986d7f5d3SJohn Marino 		  && curattr != NULL
20086d7f5d3SJohn Marino 		  && strcmp (curattr_new, curattr) == 0)))
20186d7f5d3SJohn Marino 	    fileattr_set (file,
20286d7f5d3SJohn Marino 			  "_watchers",
20386d7f5d3SJohn Marino 			  curattr_new);
20486d7f5d3SJohn Marino 	if (curattr_new != NULL)
20586d7f5d3SJohn Marino 	    free (curattr_new);
20686d7f5d3SJohn Marino     }
20786d7f5d3SJohn Marino 
20886d7f5d3SJohn Marino     if (curattr != NULL)
20986d7f5d3SJohn Marino 	free (curattr);
21086d7f5d3SJohn Marino     if (mycurattr != NULL)
21186d7f5d3SJohn Marino 	free (mycurattr);
21286d7f5d3SJohn Marino     if (mynewattr != NULL)
21386d7f5d3SJohn Marino 	free (mynewattr);
21486d7f5d3SJohn Marino }
21586d7f5d3SJohn Marino 
21686d7f5d3SJohn Marino static int addremove_fileproc (void *callerdat,
21786d7f5d3SJohn Marino 				      struct file_info *finfo);
21886d7f5d3SJohn Marino 
21986d7f5d3SJohn Marino static int
addremove_fileproc(void * callerdat,struct file_info * finfo)22086d7f5d3SJohn Marino addremove_fileproc (void *callerdat, struct file_info *finfo)
22186d7f5d3SJohn Marino {
22286d7f5d3SJohn Marino     watch_modify_watchers (finfo->file, &the_args);
22386d7f5d3SJohn Marino     return 0;
22486d7f5d3SJohn Marino }
22586d7f5d3SJohn Marino 
addremove_filesdoneproc(void * callerdat,int err,const char * repository,const char * update_dir,List * entries)22686d7f5d3SJohn Marino static int addremove_filesdoneproc (void * callerdat, int err, const char * repository,
22786d7f5d3SJohn Marino                                            const char *update_dir, List * entries)
22886d7f5d3SJohn Marino {
22986d7f5d3SJohn Marino     int set_default = the_args.setting_default;
23086d7f5d3SJohn Marino     int dir_check = 0;
23186d7f5d3SJohn Marino 
23286d7f5d3SJohn Marino     while ( !set_default && dir_check < the_args.num_dirs )
23386d7f5d3SJohn Marino     {
23486d7f5d3SJohn Marino 	/* If we are recursing, then just see if the first part of update_dir
23586d7f5d3SJohn Marino 	   matches any of the specified directories. Otherwise, it must be an exact
23686d7f5d3SJohn Marino 	   match. */
23786d7f5d3SJohn Marino 	if ( the_args.local )
23886d7f5d3SJohn Marino 	    set_default = strcmp( update_dir, the_args.dirs[ dir_check ] )==0;
23986d7f5d3SJohn Marino 	else
24086d7f5d3SJohn Marino 	    set_default = strncmp( update_dir, the_args.dirs[ dir_check ], strlen( the_args.dirs[ dir_check ] ) ) == 0;
24186d7f5d3SJohn Marino 	dir_check++;
24286d7f5d3SJohn Marino     }
24386d7f5d3SJohn Marino 
24486d7f5d3SJohn Marino     if (set_default)
24586d7f5d3SJohn Marino 	watch_modify_watchers (NULL, &the_args);
24686d7f5d3SJohn Marino     return err;
24786d7f5d3SJohn Marino }
24886d7f5d3SJohn Marino 
24986d7f5d3SJohn Marino 
25086d7f5d3SJohn Marino static int
watch_addremove(int argc,char ** argv)25186d7f5d3SJohn Marino watch_addremove (int argc, char **argv)
25286d7f5d3SJohn Marino {
25386d7f5d3SJohn Marino     int c;
25486d7f5d3SJohn Marino     int err;
25586d7f5d3SJohn Marino     int a_omitted;
25686d7f5d3SJohn Marino     int arg_index;
25786d7f5d3SJohn Marino     int max_dirs;
25886d7f5d3SJohn Marino 
25986d7f5d3SJohn Marino     a_omitted = 1;
26086d7f5d3SJohn Marino     the_args.commit = 0;
26186d7f5d3SJohn Marino     the_args.edit = 0;
26286d7f5d3SJohn Marino     the_args.unedit = 0;
26386d7f5d3SJohn Marino     the_args.num_dirs = 0;
26486d7f5d3SJohn Marino     the_args.dirs = NULL;
26586d7f5d3SJohn Marino     the_args.local = 0;
26686d7f5d3SJohn Marino 
26786d7f5d3SJohn Marino     optind = 0;
26886d7f5d3SJohn Marino     while ((c = getopt (argc, argv, "+lRa:")) != -1)
26986d7f5d3SJohn Marino     {
27086d7f5d3SJohn Marino 	switch (c)
27186d7f5d3SJohn Marino 	{
27286d7f5d3SJohn Marino 	    case 'l':
27386d7f5d3SJohn Marino 		the_args.local = 1;
27486d7f5d3SJohn Marino 		break;
27586d7f5d3SJohn Marino 	    case 'R':
27686d7f5d3SJohn Marino 		the_args.local = 0;
27786d7f5d3SJohn Marino 		break;
27886d7f5d3SJohn Marino 	    case 'a':
27986d7f5d3SJohn Marino 		a_omitted = 0;
28086d7f5d3SJohn Marino 		if (strcmp (optarg, "edit") == 0)
28186d7f5d3SJohn Marino 		    the_args.edit = 1;
28286d7f5d3SJohn Marino 		else if (strcmp (optarg, "unedit") == 0)
28386d7f5d3SJohn Marino 		    the_args.unedit = 1;
28486d7f5d3SJohn Marino 		else if (strcmp (optarg, "commit") == 0)
28586d7f5d3SJohn Marino 		    the_args.commit = 1;
28686d7f5d3SJohn Marino 		else if (strcmp (optarg, "all") == 0)
28786d7f5d3SJohn Marino 		{
28886d7f5d3SJohn Marino 		    the_args.edit = 1;
28986d7f5d3SJohn Marino 		    the_args.unedit = 1;
29086d7f5d3SJohn Marino 		    the_args.commit = 1;
29186d7f5d3SJohn Marino 		}
29286d7f5d3SJohn Marino 		else if (strcmp (optarg, "none") == 0)
29386d7f5d3SJohn Marino 		{
29486d7f5d3SJohn Marino 		    the_args.edit = 0;
29586d7f5d3SJohn Marino 		    the_args.unedit = 0;
29686d7f5d3SJohn Marino 		    the_args.commit = 0;
29786d7f5d3SJohn Marino 		}
29886d7f5d3SJohn Marino 		else
29986d7f5d3SJohn Marino 		    usage (watch_usage);
30086d7f5d3SJohn Marino 		break;
30186d7f5d3SJohn Marino 	    case '?':
30286d7f5d3SJohn Marino 	    default:
30386d7f5d3SJohn Marino 		usage (watch_usage);
30486d7f5d3SJohn Marino 		break;
30586d7f5d3SJohn Marino 	}
30686d7f5d3SJohn Marino     }
30786d7f5d3SJohn Marino     argc -= optind;
30886d7f5d3SJohn Marino     argv += optind;
30986d7f5d3SJohn Marino 
31086d7f5d3SJohn Marino     the_args.num_dirs = 0;
31186d7f5d3SJohn Marino     max_dirs = 4; /* Arbitrary choice. */
31286d7f5d3SJohn Marino     the_args.dirs = xmalloc( sizeof( const char * ) * max_dirs );
31386d7f5d3SJohn Marino 
31486d7f5d3SJohn Marino     TRACE (TRACE_FUNCTION, "watch_addremove (%d)", argc);
31586d7f5d3SJohn Marino     for ( arg_index=0; arg_index<argc; ++arg_index )
31686d7f5d3SJohn Marino     {
31786d7f5d3SJohn Marino 	TRACE( TRACE_FUNCTION, "\t%s", argv[ arg_index ]);
31886d7f5d3SJohn Marino 	if ( isdir( argv[ arg_index ] ) )
31986d7f5d3SJohn Marino 	{
32086d7f5d3SJohn Marino 	    if ( the_args.num_dirs >= max_dirs )
32186d7f5d3SJohn Marino 	    {
32286d7f5d3SJohn Marino 		max_dirs *= 2;
32386d7f5d3SJohn Marino 		the_args.dirs = (const char ** )xrealloc( (void *)the_args.dirs, max_dirs );
32486d7f5d3SJohn Marino 	    }
32586d7f5d3SJohn Marino 	    the_args.dirs[ the_args.num_dirs++ ] = argv[ arg_index ];
32686d7f5d3SJohn Marino 	}
32786d7f5d3SJohn Marino     }
32886d7f5d3SJohn Marino 
32986d7f5d3SJohn Marino     if (a_omitted)
33086d7f5d3SJohn Marino     {
33186d7f5d3SJohn Marino 	the_args.edit = 1;
33286d7f5d3SJohn Marino 	the_args.unedit = 1;
33386d7f5d3SJohn Marino 	the_args.commit = 1;
33486d7f5d3SJohn Marino     }
33586d7f5d3SJohn Marino 
33686d7f5d3SJohn Marino #ifdef CLIENT_SUPPORT
33786d7f5d3SJohn Marino     if (current_parsed_root->isremote)
33886d7f5d3SJohn Marino     {
33986d7f5d3SJohn Marino 	start_server ();
34086d7f5d3SJohn Marino 	ign_setup ();
34186d7f5d3SJohn Marino 
34286d7f5d3SJohn Marino 	if (the_args.local)
34386d7f5d3SJohn Marino 	    send_arg ("-l");
34486d7f5d3SJohn Marino 	/* FIXME: copes poorly with "all" if server is extended to have
34586d7f5d3SJohn Marino 	   new watch types and client is still running an old version.  */
34686d7f5d3SJohn Marino 	if (the_args.edit)
34786d7f5d3SJohn Marino 	    option_with_arg ("-a", "edit");
34886d7f5d3SJohn Marino 	if (the_args.unedit)
34986d7f5d3SJohn Marino 	    option_with_arg ("-a", "unedit");
35086d7f5d3SJohn Marino 	if (the_args.commit)
35186d7f5d3SJohn Marino 	    option_with_arg ("-a", "commit");
35286d7f5d3SJohn Marino 	if (!the_args.edit && !the_args.unedit && !the_args.commit)
35386d7f5d3SJohn Marino 	    option_with_arg ("-a", "none");
35486d7f5d3SJohn Marino 	send_arg ("--");
35586d7f5d3SJohn Marino 	send_files (argc, argv, the_args.local, 0, SEND_NO_CONTENTS);
35686d7f5d3SJohn Marino 	send_file_names (argc, argv, SEND_EXPAND_WILD);
35786d7f5d3SJohn Marino 	send_to_server (the_args.adding ?
35886d7f5d3SJohn Marino                         "watch-add\012" : "watch-remove\012",
35986d7f5d3SJohn Marino                         0);
36086d7f5d3SJohn Marino 	return get_responses_and_close ();
36186d7f5d3SJohn Marino     }
36286d7f5d3SJohn Marino #endif /* CLIENT_SUPPORT */
36386d7f5d3SJohn Marino 
36486d7f5d3SJohn Marino     the_args.setting_default = (argc <= 0);
36586d7f5d3SJohn Marino 
36686d7f5d3SJohn Marino     lock_tree_promotably (argc, argv, the_args.local, W_LOCAL, 0);
36786d7f5d3SJohn Marino 
36886d7f5d3SJohn Marino     err = start_recursion
36986d7f5d3SJohn Marino 	(addremove_fileproc, addremove_filesdoneproc, NULL, NULL, NULL,
37086d7f5d3SJohn Marino 	 argc, argv, the_args.local, W_LOCAL, 0, CVS_LOCK_WRITE,
37186d7f5d3SJohn Marino 	 NULL, 1, NULL);
37286d7f5d3SJohn Marino 
37386d7f5d3SJohn Marino     Lock_Cleanup ();
37486d7f5d3SJohn Marino     free( (void *)the_args.dirs );
37586d7f5d3SJohn Marino     the_args.dirs = NULL;
37686d7f5d3SJohn Marino 
37786d7f5d3SJohn Marino     return err;
37886d7f5d3SJohn Marino }
37986d7f5d3SJohn Marino 
38086d7f5d3SJohn Marino 
38186d7f5d3SJohn Marino 
38286d7f5d3SJohn Marino int
watch_add(int argc,char ** argv)38386d7f5d3SJohn Marino watch_add (int argc, char **argv)
38486d7f5d3SJohn Marino {
38586d7f5d3SJohn Marino     the_args.adding = 1;
38686d7f5d3SJohn Marino     return watch_addremove (argc, argv);
38786d7f5d3SJohn Marino }
38886d7f5d3SJohn Marino 
38986d7f5d3SJohn Marino int
watch_remove(int argc,char ** argv)39086d7f5d3SJohn Marino watch_remove (int argc, char **argv)
39186d7f5d3SJohn Marino {
39286d7f5d3SJohn Marino     the_args.adding = 0;
39386d7f5d3SJohn Marino     return watch_addremove (argc, argv);
39486d7f5d3SJohn Marino }
39586d7f5d3SJohn Marino 
39686d7f5d3SJohn Marino int
watch(int argc,char ** argv)39786d7f5d3SJohn Marino watch (int argc, char **argv)
39886d7f5d3SJohn Marino {
39986d7f5d3SJohn Marino     if (argc <= 1)
40086d7f5d3SJohn Marino 	usage (watch_usage);
40186d7f5d3SJohn Marino     if (strcmp (argv[1], "on") == 0)
40286d7f5d3SJohn Marino     {
40386d7f5d3SJohn Marino 	--argc;
40486d7f5d3SJohn Marino 	++argv;
40586d7f5d3SJohn Marino 	return watch_on (argc, argv);
40686d7f5d3SJohn Marino     }
40786d7f5d3SJohn Marino     else if (strcmp (argv[1], "off") == 0)
40886d7f5d3SJohn Marino     {
40986d7f5d3SJohn Marino 	--argc;
41086d7f5d3SJohn Marino 	++argv;
41186d7f5d3SJohn Marino 	return watch_off (argc, argv);
41286d7f5d3SJohn Marino     }
41386d7f5d3SJohn Marino     else if (strcmp (argv[1], "add") == 0)
41486d7f5d3SJohn Marino     {
41586d7f5d3SJohn Marino 	--argc;
41686d7f5d3SJohn Marino 	++argv;
41786d7f5d3SJohn Marino 	return watch_add (argc, argv);
41886d7f5d3SJohn Marino     }
41986d7f5d3SJohn Marino     else if (strcmp (argv[1], "remove") == 0)
42086d7f5d3SJohn Marino     {
42186d7f5d3SJohn Marino 	--argc;
42286d7f5d3SJohn Marino 	++argv;
42386d7f5d3SJohn Marino 	return watch_remove (argc, argv);
42486d7f5d3SJohn Marino     }
42586d7f5d3SJohn Marino     else
42686d7f5d3SJohn Marino 	usage (watch_usage);
42786d7f5d3SJohn Marino     return 0;
42886d7f5d3SJohn Marino }
42986d7f5d3SJohn Marino 
43086d7f5d3SJohn Marino static const char *const watchers_usage[] =
43186d7f5d3SJohn Marino {
43286d7f5d3SJohn Marino     "Usage: %s %s [-lR] [<file>]...\n",
43386d7f5d3SJohn Marino     "-l\tProcess this directory only (not recursive).\n",
43486d7f5d3SJohn Marino     "-R\tProcess directories recursively (default).\n",
43586d7f5d3SJohn Marino     "(Specify the --help global option for a list of other help options.)\n",
43686d7f5d3SJohn Marino     NULL
43786d7f5d3SJohn Marino };
43886d7f5d3SJohn Marino 
43986d7f5d3SJohn Marino static int watchers_fileproc (void *callerdat,
44086d7f5d3SJohn Marino 				     struct file_info *finfo);
44186d7f5d3SJohn Marino 
44286d7f5d3SJohn Marino static int
watchers_fileproc(void * callerdat,struct file_info * finfo)44386d7f5d3SJohn Marino watchers_fileproc (void *callerdat, struct file_info *finfo)
44486d7f5d3SJohn Marino {
44586d7f5d3SJohn Marino     char *them;
44686d7f5d3SJohn Marino     char *p;
44786d7f5d3SJohn Marino 
44886d7f5d3SJohn Marino     them = fileattr_get0 (finfo->file, "_watchers");
44986d7f5d3SJohn Marino     if (them == NULL)
45086d7f5d3SJohn Marino 	return 0;
45186d7f5d3SJohn Marino 
45286d7f5d3SJohn Marino     cvs_output (finfo->fullname, 0);
45386d7f5d3SJohn Marino 
45486d7f5d3SJohn Marino     p = them;
45586d7f5d3SJohn Marino     while (1)
45686d7f5d3SJohn Marino     {
45786d7f5d3SJohn Marino 	cvs_output ("\t", 1);
45886d7f5d3SJohn Marino 	while (*p != '>' && *p != '\0')
45986d7f5d3SJohn Marino 	    cvs_output (p++, 1);
46086d7f5d3SJohn Marino 	if (*p == '\0')
46186d7f5d3SJohn Marino 	{
46286d7f5d3SJohn Marino 	    /* Only happens if attribute is misformed.  */
46386d7f5d3SJohn Marino 	    cvs_output ("\n", 1);
46486d7f5d3SJohn Marino 	    break;
46586d7f5d3SJohn Marino 	}
46686d7f5d3SJohn Marino 	++p;
46786d7f5d3SJohn Marino 	cvs_output ("\t", 1);
46886d7f5d3SJohn Marino 	while (1)
46986d7f5d3SJohn Marino 	{
47086d7f5d3SJohn Marino 	    while (*p != '+' && *p != ',' && *p != '\0')
47186d7f5d3SJohn Marino 		cvs_output (p++, 1);
47286d7f5d3SJohn Marino 	    if (*p == '\0')
47386d7f5d3SJohn Marino 	    {
47486d7f5d3SJohn Marino 		cvs_output ("\n", 1);
47586d7f5d3SJohn Marino 		goto out;
47686d7f5d3SJohn Marino 	    }
47786d7f5d3SJohn Marino 	    if (*p == ',')
47886d7f5d3SJohn Marino 	    {
47986d7f5d3SJohn Marino 		++p;
48086d7f5d3SJohn Marino 		break;
48186d7f5d3SJohn Marino 	    }
48286d7f5d3SJohn Marino 	    ++p;
48386d7f5d3SJohn Marino 	    cvs_output ("\t", 1);
48486d7f5d3SJohn Marino 	}
48586d7f5d3SJohn Marino 	cvs_output ("\n", 1);
48686d7f5d3SJohn Marino     }
48786d7f5d3SJohn Marino   out:;
48886d7f5d3SJohn Marino     free (them);
48986d7f5d3SJohn Marino     return 0;
49086d7f5d3SJohn Marino }
49186d7f5d3SJohn Marino 
49286d7f5d3SJohn Marino int
watchers(int argc,char ** argv)49386d7f5d3SJohn Marino watchers (int argc, char **argv)
49486d7f5d3SJohn Marino {
49586d7f5d3SJohn Marino     int local = 0;
49686d7f5d3SJohn Marino     int c;
49786d7f5d3SJohn Marino 
49886d7f5d3SJohn Marino     if (argc == -1)
49986d7f5d3SJohn Marino 	usage (watchers_usage);
50086d7f5d3SJohn Marino 
50186d7f5d3SJohn Marino     optind = 0;
50286d7f5d3SJohn Marino     while ((c = getopt (argc, argv, "+lR")) != -1)
50386d7f5d3SJohn Marino     {
50486d7f5d3SJohn Marino 	switch (c)
50586d7f5d3SJohn Marino 	{
50686d7f5d3SJohn Marino 	    case 'l':
50786d7f5d3SJohn Marino 		local = 1;
50886d7f5d3SJohn Marino 		break;
50986d7f5d3SJohn Marino 	    case 'R':
51086d7f5d3SJohn Marino 		local = 0;
51186d7f5d3SJohn Marino 		break;
51286d7f5d3SJohn Marino 	    case '?':
51386d7f5d3SJohn Marino 	    default:
51486d7f5d3SJohn Marino 		usage (watchers_usage);
51586d7f5d3SJohn Marino 		break;
51686d7f5d3SJohn Marino 	}
51786d7f5d3SJohn Marino     }
51886d7f5d3SJohn Marino     argc -= optind;
51986d7f5d3SJohn Marino     argv += optind;
52086d7f5d3SJohn Marino 
52186d7f5d3SJohn Marino #ifdef CLIENT_SUPPORT
52286d7f5d3SJohn Marino     if (current_parsed_root->isremote)
52386d7f5d3SJohn Marino     {
52486d7f5d3SJohn Marino 	start_server ();
52586d7f5d3SJohn Marino 	ign_setup ();
52686d7f5d3SJohn Marino 
52786d7f5d3SJohn Marino 	if (local)
52886d7f5d3SJohn Marino 	    send_arg ("-l");
52986d7f5d3SJohn Marino 	send_arg ("--");
53086d7f5d3SJohn Marino 	send_files (argc, argv, local, 0, SEND_NO_CONTENTS);
53186d7f5d3SJohn Marino 	send_file_names (argc, argv, SEND_EXPAND_WILD);
53286d7f5d3SJohn Marino 	send_to_server ("watchers\012", 0);
53386d7f5d3SJohn Marino 	return get_responses_and_close ();
53486d7f5d3SJohn Marino     }
53586d7f5d3SJohn Marino #endif /* CLIENT_SUPPORT */
53686d7f5d3SJohn Marino 
53786d7f5d3SJohn Marino     return start_recursion (watchers_fileproc, NULL, NULL,
53886d7f5d3SJohn Marino 			    NULL, NULL, argc, argv, local, W_LOCAL, 0,
53986d7f5d3SJohn Marino 			    CVS_LOCK_READ, NULL, 1, NULL);
54086d7f5d3SJohn Marino }
541