Implement CLOCK_MONOTONIC using getnanouptime(), which in DragonFly is
[dragonfly.git] / contrib / cvs-1.12.9 / 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 addremove_filesdoneproc (void *callerdat, int err, const char *repository,
229                          const char *update_dir, List *entries)
230 {
231     if (the_args.setting_default)
232         watch_modify_watchers (NULL, &the_args);
233     return err;
234 }
235
236
237
238 static int watch_addremove (int argc, char **argv);
239
240 static int
241 watch_addremove (int argc, char **argv)
242 {
243     int c;
244     int local = 0;
245     int err;
246     int a_omitted;
247
248     a_omitted = 1;
249     the_args.commit = 0;
250     the_args.edit = 0;
251     the_args.unedit = 0;
252     optind = 0;
253     while ((c = getopt (argc, argv, "+lRa:")) != -1)
254     {
255         switch (c)
256         {
257             case 'l':
258                 local = 1;
259                 break;
260             case 'R':
261                 local = 0;
262                 break;
263             case 'a':
264                 a_omitted = 0;
265                 if (strcmp (optarg, "edit") == 0)
266                     the_args.edit = 1;
267                 else if (strcmp (optarg, "unedit") == 0)
268                     the_args.unedit = 1;
269                 else if (strcmp (optarg, "commit") == 0)
270                     the_args.commit = 1;
271                 else if (strcmp (optarg, "all") == 0)
272                 {
273                     the_args.edit = 1;
274                     the_args.unedit = 1;
275                     the_args.commit = 1;
276                 }
277                 else if (strcmp (optarg, "none") == 0)
278                 {
279                     the_args.edit = 0;
280                     the_args.unedit = 0;
281                     the_args.commit = 0;
282                 }
283                 else
284                     usage (watch_usage);
285                 break;
286             case '?':
287             default:
288                 usage (watch_usage);
289                 break;
290         }
291     }
292     argc -= optind;
293     argv += optind;
294
295     if (a_omitted)
296     {
297         the_args.edit = 1;
298         the_args.unedit = 1;
299         the_args.commit = 1;
300     }
301
302 #ifdef CLIENT_SUPPORT
303     if (current_parsed_root->isremote)
304     {
305         start_server ();
306         ign_setup ();
307
308         if (local)
309             send_arg ("-l");
310         /* FIXME: copes poorly with "all" if server is extended to have
311            new watch types and client is still running an old version.  */
312         if (the_args.edit)
313             option_with_arg ("-a", "edit");
314         if (the_args.unedit)
315             option_with_arg ("-a", "unedit");
316         if (the_args.commit)
317             option_with_arg ("-a", "commit");
318         if (!the_args.edit && !the_args.unedit && !the_args.commit)
319             option_with_arg ("-a", "none");
320         send_arg ("--");
321         send_files (argc, argv, local, 0, SEND_NO_CONTENTS);
322         send_file_names (argc, argv, SEND_EXPAND_WILD);
323         send_to_server (the_args.adding ?
324                         "watch-add\012" : "watch-remove\012",
325                         0);
326         return get_responses_and_close ();
327     }
328 #endif /* CLIENT_SUPPORT */
329
330     the_args.setting_default = (argc <= 0);
331
332     lock_tree_promotably (argc, argv, local, W_LOCAL, 0);
333
334     err = start_recursion
335         (addremove_fileproc, addremove_filesdoneproc,
336          NULL, NULL, NULL,
337          argc, argv, local, W_LOCAL, 0, CVS_LOCK_WRITE,
338          NULL, 1, NULL);
339
340     Lock_Cleanup ();
341     return err;
342 }
343
344 int
345 watch_add (int argc, char **argv)
346 {
347     the_args.adding = 1;
348     return watch_addremove (argc, argv);
349 }
350
351 int
352 watch_remove (int argc, char **argv)
353 {
354     the_args.adding = 0;
355     return watch_addremove (argc, argv);
356 }
357
358 int
359 watch (int argc, char **argv)
360 {
361     if (argc <= 1)
362         usage (watch_usage);
363     if (strcmp (argv[1], "on") == 0)
364     {
365         --argc;
366         ++argv;
367         return watch_on (argc, argv);
368     }
369     else if (strcmp (argv[1], "off") == 0)
370     {
371         --argc;
372         ++argv;
373         return watch_off (argc, argv);
374     }
375     else if (strcmp (argv[1], "add") == 0)
376     {
377         --argc;
378         ++argv;
379         return watch_add (argc, argv);
380     }
381     else if (strcmp (argv[1], "remove") == 0)
382     {
383         --argc;
384         ++argv;
385         return watch_remove (argc, argv);
386     }
387     else
388         usage (watch_usage);
389     return 0;
390 }
391
392 static const char *const watchers_usage[] =
393 {
394     "Usage: %s %s [-lR] [files...]\n",
395     "\t-l\tProcess this directory only (not recursive).\n",
396     "\t-R\tProcess directories recursively.\n",
397     "(Specify the --help global option for a list of other help options)\n",
398     NULL
399 };
400
401 static int watchers_fileproc (void *callerdat,
402                                      struct file_info *finfo);
403
404 static int
405 watchers_fileproc (void *callerdat, struct file_info *finfo)
406 {
407     char *them;
408     char *p;
409
410     them = fileattr_get0 (finfo->file, "_watchers");
411     if (them == NULL)
412         return 0;
413
414     cvs_output (finfo->fullname, 0);
415
416     p = them;
417     while (1)
418     {
419         cvs_output ("\t", 1);
420         while (*p != '>' && *p != '\0')
421             cvs_output (p++, 1);
422         if (*p == '\0')
423         {
424             /* Only happens if attribute is misformed.  */
425             cvs_output ("\n", 1);
426             break;
427         }
428         ++p;
429         cvs_output ("\t", 1);
430         while (1)
431         {
432             while (*p != '+' && *p != ',' && *p != '\0')
433                 cvs_output (p++, 1);
434             if (*p == '\0')
435             {
436                 cvs_output ("\n", 1);
437                 goto out;
438             }
439             if (*p == ',')
440             {
441                 ++p;
442                 break;
443             }
444             ++p;
445             cvs_output ("\t", 1);
446         }
447         cvs_output ("\n", 1);
448     }
449   out:;
450     free (them);
451     return 0;
452 }
453
454 int
455 watchers (int argc, char **argv)
456 {
457     int local = 0;
458     int c;
459
460     if (argc == -1)
461         usage (watchers_usage);
462
463     optind = 0;
464     while ((c = getopt (argc, argv, "+lR")) != -1)
465     {
466         switch (c)
467         {
468             case 'l':
469                 local = 1;
470                 break;
471             case 'R':
472                 local = 0;
473                 break;
474             case '?':
475             default:
476                 usage (watchers_usage);
477                 break;
478         }
479     }
480     argc -= optind;
481     argv += optind;
482
483 #ifdef CLIENT_SUPPORT
484     if (current_parsed_root->isremote)
485     {
486         start_server ();
487         ign_setup ();
488
489         if (local)
490             send_arg ("-l");
491         send_arg ("--");
492         send_files (argc, argv, local, 0, SEND_NO_CONTENTS);
493         send_file_names (argc, argv, SEND_EXPAND_WILD);
494         send_to_server ("watchers\012", 0);
495         return get_responses_and_close ();
496     }
497 #endif /* CLIENT_SUPPORT */
498
499     return start_recursion
500         ( watchers_fileproc, (FILESDONEPROC) NULL,
501           (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL,
502           argc, argv, local, W_LOCAL, 0, CVS_LOCK_READ,
503           (char *) NULL, 1, (char *) NULL );
504 }