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