1 /* Implementation for "cvs edit", "cvs watch on", and related commands
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)
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. */
20 static int watch_onoff (int, char **);
22 static bool check_edited
= false;
23 static int setting_default
;
24 static int turning_on
;
26 static bool setting_tedit
;
27 static bool setting_tunedit
;
28 static bool setting_tcommit
;
33 onoff_fileproc (void *callerdat
, struct file_info
*finfo
)
35 fileattr_get0 (finfo
->file
, "_watched");
36 fileattr_set (finfo
->file
, "_watched", turning_on
? "" : NULL
);
43 onoff_filesdoneproc (void *callerdat
, int err
, const char *repository
,
44 const char *update_dir
, List
*entries
)
48 fileattr_get0 (NULL
, "_watched");
49 fileattr_set (NULL
, "_watched", turning_on
? "" : NULL
);
57 watch_onoff (int argc
, char **argv
)
64 while ((c
= getopt (argc
, argv
, "+lR")) != -1)
84 if (current_parsed_root
->isremote
)
93 send_files (argc
, argv
, local
, 0, SEND_NO_CONTENTS
);
94 send_file_names (argc
, argv
, SEND_EXPAND_WILD
);
95 send_to_server (turning_on
? "watch-on\012" : "watch-off\012", 0);
96 return get_responses_and_close ();
98 #endif /* CLIENT_SUPPORT */
100 setting_default
= (argc
<= 0);
102 lock_tree_promotably (argc
, argv
, local
, W_LOCAL
, 0);
104 err
= start_recursion (onoff_fileproc
, onoff_filesdoneproc
, NULL
, NULL
,
105 NULL
, argc
, argv
, local
, W_LOCAL
, 0, CVS_LOCK_WRITE
,
113 watch_on (int argc
, char **argv
)
116 return watch_onoff (argc
, argv
);
120 watch_off (int argc
, char **argv
)
123 return watch_onoff (argc
, argv
);
129 dummy_fileproc (void *callerdat
, struct file_info
*finfo
)
131 /* This is a pretty hideous hack, but the gist of it is that recurse.c
132 won't call notify_check unless there is a fileproc, so we can't just
133 pass NULL for fileproc. */
139 /* Check for and process notifications. Local only. I think that doing
140 this as a fileproc is the only way to catch all the
141 cases (e.g. foo/bar.c), even though that means checking over and over
142 for the same CVSADM_NOTIFY file which we removed the first time we
143 processed the directory. */
145 ncheck_fileproc (void *callerdat
, struct file_info
*finfo
)
157 /* We send notifications even if noexec. I'm not sure which behavior
160 fp
= CVS_FOPEN (CVSADM_NOTIFY
, "r");
163 if (!existence_error (errno
))
164 error (0, errno
, "cannot open %s", CVSADM_NOTIFY
);
168 while (getline (&line
, &line_len
, fp
) > 0)
170 notif_type
= line
[0];
171 if (notif_type
== '\0')
174 cp
= strchr (filename
, '\t');
179 cp
= strchr (val
, '\t');
183 cp
= strchr (cp
, '\t');
187 cp
= strchr (cp
, '\t');
192 cp
= strchr (cp
, '\n');
197 notify_do (notif_type
, filename
, finfo
->update_dir
, getcaller (), val
,
198 watches
, finfo
->repository
);
203 error (0, errno
, "cannot read %s", CVSADM_NOTIFY
);
205 error (0, errno
, "cannot close %s", CVSADM_NOTIFY
);
207 if ( CVS_UNLINK (CVSADM_NOTIFY
) < 0)
208 error (0, errno
, "cannot remove %s", CVSADM_NOTIFY
);
215 /* Look through the CVSADM_NOTIFY file and process each item there
218 send_notifications (int argc
, char **argv
, int local
)
222 #ifdef CLIENT_SUPPORT
223 /* OK, we've done everything which needs to happen on the client side.
224 Now we can try to contact the server; if we fail, then the
225 notifications stay in CVSADM_NOTIFY to be sent next time. */
226 if (current_parsed_root
->isremote
)
228 if (strcmp (cvs_cmd_name
, "release") != 0)
234 err
+= start_recursion (dummy_fileproc
, NULL
, NULL
, NULL
, NULL
, argc
,
235 argv
, local
, W_LOCAL
, 0, 0, NULL
, 0, NULL
);
237 send_to_server ("noop\012", 0);
238 if (strcmp (cvs_cmd_name
, "release") == 0)
239 err
+= get_server_responses ();
241 err
+= get_responses_and_close ();
248 err
+= start_recursion (ncheck_fileproc
, NULL
, NULL
, NULL
, NULL
, argc
,
249 argv
, local
, W_LOCAL
, 0, CVS_LOCK_WRITE
, NULL
,
258 void editors_output (const char *fullname
, const char *p
)
260 cvs_output (fullname
, 0);
264 cvs_output ("\t", 1);
265 while (*p
!= '>' && *p
!= '\0')
269 /* Only happens if attribute is misformed. */
270 cvs_output ("\n", 1);
274 cvs_output ("\t", 1);
277 while (*p
!= '+' && *p
!= ',' && *p
!= '\0')
281 cvs_output ("\n", 1);
290 cvs_output ("\t", 1);
292 cvs_output ("\n", 1);
298 static int find_editors_and_output (struct file_info
*finfo
)
302 them
= fileattr_get0 (finfo
->file
, "_editors");
306 editors_output (finfo
->fullname
, them
);
313 /* Handle the client-side details of editing a file.
315 * These args could be const but this needs to fit the call_in_directory API.
318 edit_file (void *data
, List
*ent_list
, const char *short_pathname
,
319 const char *filename
)
322 struct file_info finfo
;
325 xchmod (filename
, 1);
327 mkdir_if_needed (CVSADM_BASE
);
328 basefilename
= Xasprintf ("%s/%s", CVSADM_BASE
, filename
);
329 copy_file (filename
, basefilename
);
332 node
= findnode_fn (ent_list
, filename
);
335 finfo
.file
= filename
;
336 finfo
.fullname
= short_pathname
;
337 finfo
.update_dir
= dir_name (short_pathname
);
338 base_register (&finfo
, ((Entnode
*) node
->data
)->version
);
339 free ((char *)finfo
.update_dir
);
346 edit_fileproc (void *callerdat
, struct file_info
*finfo
)
353 #if defined (CLIENT_SUPPORT)
354 assert (!(current_parsed_root
->isremote
&& check_edited
));
355 #else /* !CLIENT_SUPPORT */
356 assert (!check_edited
);
357 #endif /* CLIENT_SUPPORT */
362 vers
= Version_TS (finfo
, NULL
, NULL
, NULL
, 1, 0);
366 error (0, 0, "no such file %s; ignored", finfo
->fullname
);
370 #ifdef CLIENT_SUPPORT
371 if (!current_parsed_root
->isremote
)
372 #endif /* CLIENT_SUPPORT */
374 char *editors
= fileattr_get0 (finfo
->file
, "_editors");
379 /* In the !CHECK_EDIT case, this message is printed by
383 editors_output (finfo
->fullname
, editors
);
384 /* Now warn the user if we skip the file, then return. */
386 error (0, 0, "Skipping file `%s' due to existing editors.",
394 fp
= xfopen (CVSADM_NOTIFY
, "a");
397 ascnow
= asctime (gmtime (&now
));
399 /* Fix non-standard format. */
400 if (ascnow
[8] == '0') ascnow
[8] = ' ';
401 fprintf (fp
, "E%s\t%s -0000\t%s\t%s\t", finfo
->file
,
402 ascnow
, hostname
, CurDir
);
413 if (finfo
->update_dir
[0] == '\0')
414 error (0, errno
, "cannot close %s", CVSADM_NOTIFY
);
416 error (0, errno
, "cannot close %s/%s", finfo
->update_dir
,
420 /* Now stash the file away in CVSADM so that unedit can revert even if
421 it can't communicate with the server. We stash away a writable
422 copy so that if the user removes the working file, then restores it
423 with "cvs update" (which clears _editors but does not update
424 CVSADM_BASE), then a future "cvs edit" can still win. */
425 /* Could save a system call by only calling mkdir_if_needed if
426 trying to create the output file fails. But copy_file isn't
427 set up to facilitate that. */
428 #ifdef SERVER_SUPPORT
430 server_edit_file (finfo
);
432 #endif /* SERVER_SUPPORT */
433 edit_file (NULL
, finfo
->entries
, finfo
->fullname
, finfo
->file
);
438 static const char *const edit_usage
[] =
440 "Usage: %s %s [-lRcf] [-a <action>]... [<file>]...\n",
441 "-l\tLocal directory only, not recursive.\n",
442 "-R\tProcess directories recursively (default).\n",
443 "-a\tSpecify action to register for temporary watch, one of:\n",
444 " \t`edit', `unedit', `commit', `all', `none' (defaults to `all').\n",
445 "-c\tCheck for <file>s edited by others and abort if found.\n",
446 "-f\tAllow edit if <file>s are edited by others (default).\n",
447 "(Specify the --help global option for a list of other help options.)\n",
452 edit (int argc
, char **argv
)
457 bool a_omitted
, a_all
, a_none
;
465 setting_tedit
= false;
466 setting_tunedit
= false;
467 setting_tcommit
= false;
469 while ((c
= getopt (argc
, argv
, "+cflRa:")) != -1)
477 check_edited
= false;
487 if (strcmp (optarg
, "edit") == 0)
488 setting_tedit
= true;
489 else if (strcmp (optarg
, "unedit") == 0)
490 setting_tunedit
= true;
491 else if (strcmp (optarg
, "commit") == 0)
492 setting_tcommit
= true;
493 else if (strcmp (optarg
, "all") == 0)
497 setting_tedit
= true;
498 setting_tunedit
= true;
499 setting_tcommit
= true;
501 else if (strcmp (optarg
, "none") == 0)
505 setting_tedit
= false;
506 setting_tunedit
= false;
507 setting_tcommit
= false;
521 if (strpbrk (hostname
, "+,>;=\t\n") != NULL
)
523 "host name (%s) contains an invalid character (+,>;=\\t\\n)",
525 if (strpbrk (CurDir
, "+,>;=\t\n") != NULL
)
527 "current directory (%s) contains an invalid character (+,>;=\\t\\n)",
530 #ifdef CLIENT_SUPPORT
531 if (check_edited
&& current_parsed_root
->isremote
)
533 /* When CHECK_EDITED, we might as well contact the server and let it do
534 * the work since we don't want an edit unless we know it is safe.
536 * When !CHECK_EDITED, we set up notifications and then attempt to
537 * contact the server in order to allow disconnected edits.
541 if (!supported_request ("edit"))
542 error (1, 0, "Server does not support enforced advisory locks.");
546 send_to_server ("Hostname ", 0);
547 send_to_server (hostname
, 0);
548 send_to_server ("\012", 1);
549 send_to_server ("LocalDir ", 0);
550 send_to_server (CurDir
, 0);
551 send_to_server ("\012", 1);
559 option_with_arg ("-a", "all");
561 option_with_arg ("-a", "none");
565 option_with_arg ("-a", "edit");
567 option_with_arg ("-a", "unedit");
569 option_with_arg ("-a", "commit");
573 send_files (argc
, argv
, local
, 0, SEND_NO_CONTENTS
);
574 send_file_names (argc
, argv
, SEND_EXPAND_WILD
);
575 send_to_server ("edit\012", 0);
576 return get_responses_and_close ();
578 #endif /* CLIENT_SUPPORT */
580 /* Now, either SERVER_ACTIVE, local mode, or !CHECK_EDITED. */
584 setting_tedit
= true;
585 setting_tunedit
= true;
586 setting_tcommit
= true;
589 TRACE (TRACE_DATA
, "edit(): EUC: %d%d%d edit-check: %d",
590 setting_tedit
, setting_tunedit
, setting_tcommit
, check_edited
);
592 err
= start_recursion (edit_fileproc
, NULL
, NULL
, NULL
, NULL
, argc
, argv
,
593 local
, W_LOCAL
, 0, 0, NULL
, 0, NULL
);
595 err
+= send_notifications (argc
, argv
, local
);
600 static int unedit_fileproc (void *callerdat
, struct file_info
*finfo
);
603 unedit_fileproc (void *callerdat
, struct file_info
*finfo
)
608 char *basefilename
= NULL
;
613 basefilename
= Xasprintf ("%s/%s", CVSADM_BASE
, finfo
->file
);
614 if (!isfile (basefilename
))
616 /* This file apparently was never cvs edit'd (e.g. we are uneditting
617 a directory where only some of the files were cvs edit'd. */
622 if (xcmp (finfo
->file
, basefilename
) != 0)
624 printf ("%s has been modified; revert changes? ", finfo
->fullname
);
634 rename_file (basefilename
, finfo
->file
);
637 fp
= xfopen (CVSADM_NOTIFY
, "a");
640 ascnow
= asctime (gmtime (&now
));
642 /* Fix non-standard format. */
643 if (ascnow
[8] == '0') ascnow
[8] = ' ';
644 fprintf (fp
, "U%s\t%s -0000\t%s\t%s\t\n", finfo
->file
,
645 ascnow
, hostname
, CurDir
);
649 if (finfo
->update_dir
[0] == '\0')
650 error (0, errno
, "cannot close %s", CVSADM_NOTIFY
);
652 error (0, errno
, "cannot close %s/%s", finfo
->update_dir
,
656 /* Now update the revision number in CVS/Entries from CVS/Baserev.
657 The basic idea here is that we are reverting to the revision
658 that the user edited. If we wanted "cvs update" to update
659 CVS/Base as we go along (so that an unedit could revert to the
660 current repository revision), we would need:
662 update (or all send_files?) (client) needs to send revision in
663 new Entry-base request. update (server/local) needs to check
664 revision against repository and send new Update-base response
665 (like Update-existing in that the file already exists. While
666 we are at it, might try to clean up the syntax by having the
667 mode only in a "Mode" response, not in the Update-base itself). */
673 baserev
= base_get (finfo
);
674 node
= findnode_fn (finfo
->entries
, finfo
->file
);
675 /* The case where node is NULL probably should be an error or
676 something, but I don't want to think about it too hard right
680 entdata
= node
->data
;
683 /* This can only happen if the CVS/Baserev file got
684 corrupted. We suspect it might be possible if the
685 user interrupts CVS, although I haven't verified
687 error (0, 0, "%s not mentioned in %s", finfo
->fullname
,
690 /* Since we don't know what revision the file derives from,
691 keeping it around would be asking for trouble. */
692 if (unlink_file (finfo
->file
) < 0)
693 error (0, errno
, "cannot remove %s", finfo
->fullname
);
695 /* This is cheesy, in a sense; why shouldn't we do the
696 update for the user? However, doing that would require
697 contacting the server, so maybe this is OK. */
698 error (0, 0, "run update to complete the unedit");
701 Register (finfo
->entries
, finfo
->file
, baserev
, entdata
->timestamp
,
702 entdata
->options
, entdata
->tag
, entdata
->date
,
706 base_deregister (finfo
);
709 xchmod (finfo
->file
, 0);
713 static const char *const unedit_usage
[] =
715 "Usage: %s %s [-lR] [<file>]...\n",
716 "-l\tLocal directory only, not recursive.\n",
717 "-R\tProcess directories recursively (default).\n",
718 "(Specify the --help global option for a list of other help options.)\n",
723 unedit (int argc
, char **argv
)
730 usage (unedit_usage
);
733 while ((c
= getopt (argc
, argv
, "+lR")) != -1)
745 usage (unedit_usage
);
752 /* No need to readlock since we aren't doing anything to the
754 err
= start_recursion (unedit_fileproc
, NULL
, NULL
, NULL
, NULL
, argc
, argv
,
755 local
, W_LOCAL
, 0, 0, NULL
, 0, NULL
);
757 err
+= send_notifications (argc
, argv
, local
);
765 mark_up_to_date (const char *file
)
769 /* The file is up to date, so we better get rid of an out of
770 date file in CVSADM_BASE. */
771 base
= Xasprintf ("%s/%s", CVSADM_BASE
, file
);
772 if (unlink_file (base
) < 0 && ! existence_error (errno
))
773 error (0, errno
, "cannot remove %s", file
);
780 editor_set (const char *filename
, const char *editor
, const char *val
)
785 edlist
= fileattr_get0 (filename
, "_editors");
786 newlist
= fileattr_modify (edlist
, editor
, val
, '>', ',');
787 /* If the attributes is unchanged, don't rewrite the attribute file. */
788 if (!((edlist
== NULL
&& newlist
== NULL
)
791 && strcmp (edlist
, newlist
) == 0)))
792 fileattr_set (filename
, "_editors", newlist
);
799 struct notify_proc_args
{
800 /* What kind of notification, "edit", "tedit", etc. */
802 /* User who is running the command which causes notification. */
804 /* User to be notified. */
805 const char *notifyee
;
813 notify_proc (const char *repository
, const char *filter
, void *closure
)
817 const char *srepos
= Short_Repository (repository
);
818 struct notify_proc_args
*args
= closure
;
821 * Cast any NULL arguments as appropriate pointers as this is an
822 * stdarg function and we need to be certain the caller gets what
825 cmdline
= format_cmdline (
826 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
828 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
830 "c", "s", cvs_cmd_name
,
831 #ifdef SERVER_SUPPORT
832 "R", "s", referrer
? referrer
->original
: "NONE",
833 #endif /* SERVER_SUPPORT */
835 "r", "s", current_parsed_root
->directory
,
836 "s", "s", args
->notifyee
,
838 if (!cmdline
|| !strlen (cmdline
))
840 if (cmdline
) free (cmdline
);
841 error (0, 0, "pretag proc resolved to the empty string!");
845 pipefp
= run_popen (cmdline
, "w");
848 error (0, errno
, "cannot write entry to notify filter: %s", cmdline
);
853 fprintf (pipefp
, "%s %s\n---\n", srepos
, args
->file
);
854 fprintf (pipefp
, "Triggered %s watch on %s\n", args
->type
, repository
);
855 fprintf (pipefp
, "By %s\n", args
->who
);
857 /* Lots more potentially useful information we could add here; see
858 logfile_write for inspiration. */
861 return pclose (pipefp
);
866 /* FIXME: this function should have a way to report whether there was
867 an error so that server.c can know whether to report Notified back
870 notify_do (int type
, const char *filename
, const char *update_dir
,
871 const char *who
, const char *val
, const char *watches
,
872 const char *repository
)
874 static struct addremove_args blank
;
875 struct addremove_args args
;
881 /* Print out information on current editors if we were called during an
884 if (type
== 'E' && !check_edited
&& !quiet
)
886 char *editors
= fileattr_get0 (filename
, "_editors");
889 /* In the CHECK_EDIT case, this message is printed by
890 * edit_check. It needs to be done here too since files
891 * which are found to be edited when CHECK_EDIT are not
892 * added to the notify list.
895 if (update_dir
&& *update_dir
)
896 tmp
= Xasprintf ("%s/%s", update_dir
, filename
);
900 editors_output (tmp
, editors
);
902 if (update_dir
&& *update_dir
) free ((char *)tmp
);
907 /* Initialize fields to 0, NULL, or 0.0. */
912 if (strpbrk (val
, ",>;=\n") != NULL
)
914 error (0, 0, "invalid character in editor value");
917 editor_set (filename
, who
, val
);
921 editor_set (filename
, who
, NULL
);
927 watchers
= fileattr_get0 (filename
, "_watchers");
936 endp
= strchr (p
, '>');
939 nextp
= strchr (p
, ',');
941 if ((size_t)(endp
- p
) == strlen (who
) && strncmp (who
, p
, endp
- p
) == 0)
943 /* Don't notify user of their own changes. Would perhaps
944 be better to check whether it is the same working
945 directory, not the same user, but that is hairy. */
946 p
= nextp
== NULL
? nextp
: nextp
+ 1;
950 /* Now we point q at a string which looks like
951 "edit+unedit+commit,"... and walk down it. */
956 endq
= strchr (q
, '+');
957 if (endq
== NULL
|| (nextp
!= NULL
&& endq
> nextp
))
960 endq
= q
+ strlen (q
);
968 /* If there is a temporary and a regular watch, send a single
969 notification, for the regular watch. */
970 if (type
== 'E' && endq
- q
== 4 && strncmp ("edit", q
, 4) == 0)
975 && endq
- q
== 6 && strncmp ("unedit", q
, 6) == 0)
980 && endq
- q
== 6 && strncmp ("commit", q
, 6) == 0)
985 && endq
- q
== 5 && strncmp ("tedit", q
, 5) == 0)
988 notif
= "temporary edit";
991 && endq
- q
== 7 && strncmp ("tunedit", q
, 7) == 0)
994 notif
= "temporary unedit";
997 && endq
- q
== 7 && strncmp ("tcommit", q
, 7) == 0)
1000 notif
= "temporary commit";
1009 struct notify_proc_args args
;
1010 size_t len
= endp
- p
;
1014 size_t line_len
= 0;
1016 args
.notifyee
= NULL
;
1017 usersname
= Xasprintf ("%s/%s/%s", current_parsed_root
->directory
,
1018 CVSROOTADM
, CVSROOTADM_USERS
);
1019 fp
= CVS_FOPEN (usersname
, "r");
1020 if (fp
== NULL
&& !existence_error (errno
))
1021 error (0, errno
, "cannot read %s", usersname
);
1024 while (getline (&line
, &line_len
, fp
) >= 0)
1026 if (strncmp (line
, p
, len
) == 0
1027 && line
[len
] == ':')
1030 args
.notifyee
= xstrdup (line
+ len
+ 1);
1032 /* There may or may not be more
1033 colon-separated fields added to this in the
1034 future; in any case, we ignore them right
1035 now, and if there are none we make sure to
1036 chop off the final newline, if any. */
1037 cp
= strpbrk (args
.notifyee
, ":\n");
1045 error (0, errno
, "cannot read %s", usersname
);
1046 if (fclose (fp
) < 0)
1047 error (0, errno
, "cannot close %s", usersname
);
1053 if (args
.notifyee
== NULL
)
1056 tmp
= xmalloc (endp
- p
+ 1);
1057 strncpy (tmp
, p
, endp
- p
);
1058 tmp
[endp
- p
] = '\0';
1059 args
.notifyee
= tmp
;
1064 args
.file
= filename
;
1066 (void) Parse_Info (CVSROOTADM_NOTIFY
, repository
, notify_proc
,
1068 /* It's okay to cast out the const for the free() below since we
1069 * just allocated this a few lines above. The const was for
1072 free ((char *)args
.notifyee
);
1077 if (watchers
!= NULL
)
1083 if (*watches
== 'E')
1088 if (*watches
== 'U')
1090 args
.add_tunedit
= 1;
1093 if (*watches
== 'C')
1095 args
.add_tcommit
= 1;
1097 watch_modify_watchers (filename
, &args
);
1101 args
.remove_temp
= 1;
1102 watch_modify_watchers (filename
, &args
);
1109 #ifdef CLIENT_SUPPORT
1110 /* Check and send notifications. This is only for the client. */
1112 notify_check (const char *repository
, const char *update_dir
)
1116 size_t line_len
= 0;
1118 if (!server_started
)
1119 /* We are in the midst of a command which is not to talk to
1120 the server (e.g. the first phase of a cvs edit). Just chill
1121 out, we'll catch the notifications on the flip side. */
1124 /* We send notifications even if noexec. I'm not sure which behavior
1125 is most sensible. */
1127 fp
= CVS_FOPEN (CVSADM_NOTIFY
, "r");
1130 if (!existence_error (errno
))
1131 error (0, errno
, "cannot open %s", CVSADM_NOTIFY
);
1134 while (getline (&line
, &line_len
, fp
) > 0)
1141 notif_type
= line
[0];
1142 if (notif_type
== '\0')
1144 filename
= line
+ 1;
1145 cp
= strchr (filename
, '\t');
1151 client_notify (repository
, update_dir
, filename
, notif_type
, val
);
1156 error (0, errno
, "cannot read %s", CVSADM_NOTIFY
);
1157 if (fclose (fp
) < 0)
1158 error (0, errno
, "cannot close %s", CVSADM_NOTIFY
);
1160 /* Leave the CVSADM_NOTIFY file there, until the server tells us it
1161 has dealt with it. */
1163 #endif /* CLIENT_SUPPORT */
1166 static const char *const editors_usage
[] =
1168 "Usage: %s %s [-lR] [<file>]...\n",
1169 "-l\tProcess this directory only (not recursive).\n",
1170 "-R\tProcess directories recursively (default).\n",
1171 "(Specify the --help global option for a list of other help options.)\n",
1178 editors_fileproc (void *callerdat
, struct file_info
*finfo
)
1180 return find_editors_and_output (finfo
);
1186 editors (int argc
, char **argv
)
1192 usage (editors_usage
);
1195 while ((c
= getopt (argc
, argv
, "+lR")) != -1)
1207 usage (editors_usage
);
1214 #ifdef CLIENT_SUPPORT
1215 if (current_parsed_root
->isremote
)
1223 send_files (argc
, argv
, local
, 0, SEND_NO_CONTENTS
);
1224 send_file_names (argc
, argv
, SEND_EXPAND_WILD
);
1225 send_to_server ("editors\012", 0);
1226 return get_responses_and_close ();
1228 #endif /* CLIENT_SUPPORT */
1230 return start_recursion (editors_fileproc
, NULL
, NULL
, NULL
, NULL
,
1231 argc
, argv
, local
, W_LOCAL
, 0, CVS_LOCK_READ
, NULL
,