xref: /netbsd-src/external/gpl2/xcvs/dist/src/watch.c (revision 5a6c14c844c4c665da5632061aebde7bb2cb5766)
1 /* Implementation for "cvs watch add", "cvs watchers", and related commands
2 
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License as published by
5    the Free Software Foundation; either version 2, or (at your option)
6    any later version.
7 
8    This program is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11    GNU General Public License for more details.  */
12 #include <sys/cdefs.h>
13 __RCSID("$NetBSD: watch.c,v 1.3 2016/05/17 14:00:09 christos Exp $");
14 
15 #include "cvs.h"
16 #include "edit.h"
17 #include "fileattr.h"
18 #include "watch.h"
19 
20 const char *const watch_usage[] =
21 {
22     "Usage: %s %s {on|off|add|remove} [-lR] [-a <action>]... [<path>]...\n",
23     "on/off: Turn on/off read-only checkouts of files.\n",
24     "add/remove: Add or remove notification on actions.\n",
25     "-l (on/off/add/remove): Local directory only, not recursive.\n",
26     "-R (on/off/add/remove): Process directories recursively (default).\n",
27     "-a (add/remove): Specify what actions, one of: `edit', `unedit',\n",
28     "                 `commit', `all', or `none' (defaults to `all').\n",
29     "(Specify the --help global option for a list of other help options.)\n",
30     NULL
31 };
32 
33 static struct addremove_args the_args;
34 
35 void
watch_modify_watchers(const char * file,struct addremove_args * what)36 watch_modify_watchers (const char *file, struct addremove_args *what)
37 {
38     char *curattr = fileattr_get0 (file, "_watchers");
39     char *p;
40     char *pend;
41     char *nextp;
42     char *who;
43     int who_len;
44     char *mycurattr;
45     char *mynewattr;
46     size_t mynewattr_size;
47 
48     int add_edit_pending;
49     int add_unedit_pending;
50     int add_commit_pending;
51     int remove_edit_pending;
52     int remove_unedit_pending;
53     int remove_commit_pending;
54     int add_tedit_pending;
55     int add_tunedit_pending;
56     int add_tcommit_pending;
57 
58     TRACE( TRACE_FUNCTION, "modify_watchers ( %s )", file );
59 
60     who = getcaller ();
61     who_len = strlen (who);
62 
63     /* Look for current watcher types for this user.  */
64     mycurattr = NULL;
65     if (curattr != NULL)
66     {
67 	p = curattr;
68 	while (1) {
69 	    if (strncmp (who, p, who_len) == 0
70 		&& p[who_len] == '>')
71 	    {
72 		/* Found this user.  */
73 		mycurattr = p + who_len + 1;
74 	    }
75 	    p = strchr (p, ',');
76 	    if (p == NULL)
77 		break;
78 	    ++p;
79 	}
80     }
81     if (mycurattr != NULL)
82     {
83 	mycurattr = xstrdup (mycurattr);
84 	p = strchr (mycurattr, ',');
85 	if (p != NULL)
86 	    *p = '\0';
87     }
88 
89     /* Now copy mycurattr to mynewattr, making the requisite modifications.
90        Note that we add a dummy '+' to the start of mynewattr, to reduce
91        special cases (but then we strip it off when we are done).  */
92 
93     mynewattr_size = sizeof "+edit+unedit+commit+tedit+tunedit+tcommit";
94     if (mycurattr != NULL)
95 	mynewattr_size += strlen (mycurattr);
96     mynewattr = xmalloc (mynewattr_size);
97     mynewattr[0] = '\0';
98 
99     add_edit_pending = what->adding && what->edit;
100     add_unedit_pending = what->adding && what->unedit;
101     add_commit_pending = what->adding && what->commit;
102     remove_edit_pending = !what->adding && what->edit;
103     remove_unedit_pending = !what->adding && what->unedit;
104     remove_commit_pending = !what->adding && what->commit;
105     add_tedit_pending = what->add_tedit;
106     add_tunedit_pending = what->add_tunedit;
107     add_tcommit_pending = what->add_tcommit;
108 
109     /* Copy over existing watch types, except those to be removed.  */
110     p = mycurattr;
111     while (p != NULL)
112     {
113 	pend = strchr (p, '+');
114 	if (pend == NULL)
115 	{
116 	    pend = p + strlen (p);
117 	    nextp = NULL;
118 	}
119 	else
120 	    nextp = pend + 1;
121 
122 	/* Process this item.  */
123 	if (pend - p == 4 && strncmp ("edit", p, 4) == 0)
124 	{
125 	    if (!remove_edit_pending)
126 		strcat (mynewattr, "+edit");
127 	    add_edit_pending = 0;
128 	}
129 	else if (pend - p == 6 && strncmp ("unedit", p, 6) == 0)
130 	{
131 	    if (!remove_unedit_pending)
132 		strcat (mynewattr, "+unedit");
133 	    add_unedit_pending = 0;
134 	}
135 	else if (pend - p == 6 && strncmp ("commit", p, 6) == 0)
136 	{
137 	    if (!remove_commit_pending)
138 		strcat (mynewattr, "+commit");
139 	    add_commit_pending = 0;
140 	}
141 	else if (pend - p == 5 && strncmp ("tedit", p, 5) == 0)
142 	{
143 	    if (!what->remove_temp)
144 		strcat (mynewattr, "+tedit");
145 	    add_tedit_pending = 0;
146 	}
147 	else if (pend - p == 7 && strncmp ("tunedit", p, 7) == 0)
148 	{
149 	    if (!what->remove_temp)
150 		strcat (mynewattr, "+tunedit");
151 	    add_tunedit_pending = 0;
152 	}
153 	else if (pend - p == 7 && strncmp ("tcommit", p, 7) == 0)
154 	{
155 	    if (!what->remove_temp)
156 		strcat (mynewattr, "+tcommit");
157 	    add_tcommit_pending = 0;
158 	}
159 	else
160 	{
161 	    char *mp;
162 
163 	    /* Copy over any unrecognized watch types, for future
164 	       expansion.  */
165 	    mp = mynewattr + strlen (mynewattr);
166 	    *mp++ = '+';
167 	    strncpy (mp, p, pend - p);
168 	    *(mp + (pend - p)) = '\0';
169 	}
170 
171 	/* Set up for next item.  */
172 	p = nextp;
173     }
174 
175     /* Add in new watch types.  */
176     if (add_edit_pending)
177 	strcat (mynewattr, "+edit");
178     if (add_unedit_pending)
179 	strcat (mynewattr, "+unedit");
180     if (add_commit_pending)
181 	strcat (mynewattr, "+commit");
182     if (add_tedit_pending)
183 	strcat (mynewattr, "+tedit");
184     if (add_tunedit_pending)
185 	strcat (mynewattr, "+tunedit");
186     if (add_tcommit_pending)
187 	strcat (mynewattr, "+tcommit");
188 
189     {
190 	char *curattr_new;
191 
192 	curattr_new =
193 	  fileattr_modify (curattr,
194 			   who,
195 			   mynewattr[0] == '\0' ? NULL : mynewattr + 1,
196 			   '>',
197 			   ',');
198 	/* If the attribute is unchanged, don't rewrite the attribute file.  */
199 	if (!((curattr_new == NULL && curattr == NULL)
200 	      || (curattr_new != NULL
201 		  && curattr != NULL
202 		  && strcmp (curattr_new, curattr) == 0)))
203 	    fileattr_set (file,
204 			  "_watchers",
205 			  curattr_new);
206 	if (curattr_new != NULL)
207 	    free (curattr_new);
208     }
209 
210     if (curattr != NULL)
211 	free (curattr);
212     if (mycurattr != NULL)
213 	free (mycurattr);
214     if (mynewattr != NULL)
215 	free (mynewattr);
216 }
217 
218 static int addremove_fileproc (void *callerdat,
219 				      struct file_info *finfo);
220 
221 static int
addremove_fileproc(void * callerdat,struct file_info * finfo)222 addremove_fileproc (void *callerdat, struct file_info *finfo)
223 {
224     watch_modify_watchers (finfo->file, &the_args);
225     return 0;
226 }
227 
addremove_filesdoneproc(void * callerdat,int err,const char * repository,const char * update_dir,List * entries)228 static int addremove_filesdoneproc (void * callerdat, int err, const char * repository,
229                                            const char *update_dir, List * entries)
230 {
231     int set_default = the_args.setting_default;
232     int dir_check = 0;
233 
234     while ( !set_default && dir_check < the_args.num_dirs )
235     {
236 	/* If we are recursing, then just see if the first part of update_dir
237 	   matches any of the specified directories. Otherwise, it must be an exact
238 	   match. */
239 	if ( the_args.local )
240 	    set_default = strcmp( update_dir, the_args.dirs[ dir_check ] )==0;
241 	else
242 	    set_default = strncmp( update_dir, the_args.dirs[ dir_check ], strlen( the_args.dirs[ dir_check ] ) ) == 0;
243 	dir_check++;
244     }
245 
246     if (set_default)
247 	watch_modify_watchers (NULL, &the_args);
248     return err;
249 }
250 
251 
252 static int
watch_addremove(int argc,char ** argv)253 watch_addremove (int argc, char **argv)
254 {
255     int c;
256     int err;
257     int a_omitted;
258     int arg_index;
259     int max_dirs;
260 
261     a_omitted = 1;
262     the_args.commit = 0;
263     the_args.edit = 0;
264     the_args.unedit = 0;
265     the_args.num_dirs = 0;
266     the_args.dirs = NULL;
267     the_args.local = 0;
268 
269     getoptreset ();
270     while ((c = getopt (argc, argv, "+lRa:")) != -1)
271     {
272 	switch (c)
273 	{
274 	    case 'l':
275 		the_args.local = 1;
276 		break;
277 	    case 'R':
278 		the_args.local = 0;
279 		break;
280 	    case 'a':
281 		a_omitted = 0;
282 		if (strcmp (optarg, "edit") == 0)
283 		    the_args.edit = 1;
284 		else if (strcmp (optarg, "unedit") == 0)
285 		    the_args.unedit = 1;
286 		else if (strcmp (optarg, "commit") == 0)
287 		    the_args.commit = 1;
288 		else if (strcmp (optarg, "all") == 0)
289 		{
290 		    the_args.edit = 1;
291 		    the_args.unedit = 1;
292 		    the_args.commit = 1;
293 		}
294 		else if (strcmp (optarg, "none") == 0)
295 		{
296 		    the_args.edit = 0;
297 		    the_args.unedit = 0;
298 		    the_args.commit = 0;
299 		}
300 		else
301 		    usage (watch_usage);
302 		break;
303 	    case '?':
304 	    default:
305 		usage (watch_usage);
306 		break;
307 	}
308     }
309     argc -= optind;
310     argv += optind;
311 
312     the_args.num_dirs = 0;
313     max_dirs = 4; /* Arbitrary choice. */
314     the_args.dirs = xmalloc( sizeof( const char * ) * max_dirs );
315 
316     TRACE (TRACE_FUNCTION, "watch_addremove (%d)", argc);
317     for ( arg_index=0; arg_index<argc; ++arg_index )
318     {
319 	TRACE( TRACE_FUNCTION, "\t%s", argv[ arg_index ]);
320 	if ( isdir( argv[ arg_index ] ) )
321 	{
322 	    if ( the_args.num_dirs >= max_dirs )
323 	    {
324 		max_dirs *= 2;
325 		the_args.dirs = (const char ** )xrealloc( (void *)the_args.dirs, max_dirs );
326 	    }
327 	    the_args.dirs[ the_args.num_dirs++ ] = argv[ arg_index ];
328 	}
329     }
330 
331     if (a_omitted)
332     {
333 	the_args.edit = 1;
334 	the_args.unedit = 1;
335 	the_args.commit = 1;
336     }
337 
338 #ifdef CLIENT_SUPPORT
339     if (current_parsed_root->isremote)
340     {
341 	start_server ();
342 	ign_setup ();
343 
344 	if (the_args.local)
345 	    send_arg ("-l");
346 	/* FIXME: copes poorly with "all" if server is extended to have
347 	   new watch types and client is still running an old version.  */
348 	if (the_args.edit)
349 	    option_with_arg ("-a", "edit");
350 	if (the_args.unedit)
351 	    option_with_arg ("-a", "unedit");
352 	if (the_args.commit)
353 	    option_with_arg ("-a", "commit");
354 	if (!the_args.edit && !the_args.unedit && !the_args.commit)
355 	    option_with_arg ("-a", "none");
356 	send_arg ("--");
357 	send_files (argc, argv, the_args.local, 0, SEND_NO_CONTENTS);
358 	send_file_names (argc, argv, SEND_EXPAND_WILD);
359 	send_to_server (the_args.adding ?
360                         "watch-add\012" : "watch-remove\012",
361                         0);
362 	return get_responses_and_close ();
363     }
364 #endif /* CLIENT_SUPPORT */
365 
366     the_args.setting_default = (argc <= 0);
367 
368     lock_tree_promotably (argc, argv, the_args.local, W_LOCAL, 0);
369 
370     err = start_recursion
371 	(addremove_fileproc, addremove_filesdoneproc, NULL, NULL, NULL,
372 	 argc, argv, the_args.local, W_LOCAL, 0, CVS_LOCK_WRITE,
373 	 NULL, 1, NULL);
374 
375     Lock_Cleanup ();
376     free( (void *)the_args.dirs );
377     the_args.dirs = NULL;
378 
379     return err;
380 }
381 
382 
383 
384 int
watch_add(int argc,char ** argv)385 watch_add (int argc, char **argv)
386 {
387     the_args.adding = 1;
388     return watch_addremove (argc, argv);
389 }
390 
391 int
watch_remove(int argc,char ** argv)392 watch_remove (int argc, char **argv)
393 {
394     the_args.adding = 0;
395     return watch_addremove (argc, argv);
396 }
397 
398 int
watch(int argc,char ** argv)399 watch (int argc, char **argv)
400 {
401     if (argc <= 1)
402 	usage (watch_usage);
403     if (strcmp (argv[1], "on") == 0)
404     {
405 	--argc;
406 	++argv;
407 	return watch_on (argc, argv);
408     }
409     else if (strcmp (argv[1], "off") == 0)
410     {
411 	--argc;
412 	++argv;
413 	return watch_off (argc, argv);
414     }
415     else if (strcmp (argv[1], "add") == 0)
416     {
417 	--argc;
418 	++argv;
419 	return watch_add (argc, argv);
420     }
421     else if (strcmp (argv[1], "remove") == 0)
422     {
423 	--argc;
424 	++argv;
425 	return watch_remove (argc, argv);
426     }
427     else
428 	usage (watch_usage);
429     return 0;
430 }
431 
432 static const char *const watchers_usage[] =
433 {
434     "Usage: %s %s [-lR] [<file>]...\n",
435     "-l\tProcess this directory only (not recursive).\n",
436     "-R\tProcess directories recursively (default).\n",
437     "(Specify the --help global option for a list of other help options.)\n",
438     NULL
439 };
440 
441 static int watchers_fileproc (void *callerdat,
442 				     struct file_info *finfo);
443 
444 static int
watchers_fileproc(void * callerdat,struct file_info * finfo)445 watchers_fileproc (void *callerdat, struct file_info *finfo)
446 {
447     char *them;
448     char *p;
449 
450     them = fileattr_get0 (finfo->file, "_watchers");
451     if (them == NULL)
452 	return 0;
453 
454     cvs_output (finfo->fullname, 0);
455 
456     p = them;
457     while (1)
458     {
459 	cvs_output ("\t", 1);
460 	while (*p != '>' && *p != '\0')
461 	    cvs_output (p++, 1);
462 	if (*p == '\0')
463 	{
464 	    /* Only happens if attribute is misformed.  */
465 	    cvs_output ("\n", 1);
466 	    break;
467 	}
468 	++p;
469 	cvs_output ("\t", 1);
470 	while (1)
471 	{
472 	    while (*p != '+' && *p != ',' && *p != '\0')
473 		cvs_output (p++, 1);
474 	    if (*p == '\0')
475 	    {
476 		cvs_output ("\n", 1);
477 		goto out;
478 	    }
479 	    if (*p == ',')
480 	    {
481 		++p;
482 		break;
483 	    }
484 	    ++p;
485 	    cvs_output ("\t", 1);
486 	}
487 	cvs_output ("\n", 1);
488     }
489   out:;
490     free (them);
491     return 0;
492 }
493 
494 int
watchers(int argc,char ** argv)495 watchers (int argc, char **argv)
496 {
497     int local = 0;
498     int c;
499 
500     if (argc == -1)
501 	usage (watchers_usage);
502 
503     getoptreset ();
504     while ((c = getopt (argc, argv, "+lR")) != -1)
505     {
506 	switch (c)
507 	{
508 	    case 'l':
509 		local = 1;
510 		break;
511 	    case 'R':
512 		local = 0;
513 		break;
514 	    case '?':
515 	    default:
516 		usage (watchers_usage);
517 		break;
518 	}
519     }
520     argc -= optind;
521     argv += optind;
522 
523 #ifdef CLIENT_SUPPORT
524     if (current_parsed_root->isremote)
525     {
526 	start_server ();
527 	ign_setup ();
528 
529 	if (local)
530 	    send_arg ("-l");
531 	send_arg ("--");
532 	send_files (argc, argv, local, 0, SEND_NO_CONTENTS);
533 	send_file_names (argc, argv, SEND_EXPAND_WILD);
534 	send_to_server ("watchers\012", 0);
535 	return get_responses_and_close ();
536     }
537 #endif /* CLIENT_SUPPORT */
538 
539     return start_recursion (watchers_fileproc, NULL, NULL,
540 			    NULL, NULL, argc, argv, local, W_LOCAL, 0,
541 			    CVS_LOCK_READ, NULL, 1, NULL);
542 }
543