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
watch_modify_watchers(file,what)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
addremove_fileproc(callerdat,finfo)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
addremove_filesdoneproc(callerdat,err,repository,update_dir,entries)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
watch_addremove(argc,argv)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
watch_add(argc,argv)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
watch_remove(argc,argv)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
watch(argc,argv)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
watchers_fileproc(callerdat,finfo)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
watchers(argc,argv)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