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