386BSD 0.1 development
[unix-history] / usr / othersrc / public / cvs-1.3 / src / recurse.c
CommitLineData
210114b7
WJ
1/*
2 * Copyright (c) 1992, Brian Berliner and Jeff Polk
3 *
4 * You may distribute under the terms of the GNU General Public License as
5 * specified in the README file that comes with the CVS 1.3 kit.
6 *
7 * General recursion handler
8 *
9 */
10
11#include "cvs.h"
12
13#ifndef lint
14static char rcsid[] = "@(#)recurse.c 1.22 92/04/10";
15#endif
16
17#if __STDC__
18static int do_dir_proc (Node * p);
19static int do_file_proc (Node * p);
20static void addlist (List ** listp, char *key);
21#else
22static int do_file_proc ();
23static int do_dir_proc ();
24static void addlist ();
25#endif /* __STDC__ */
26
27
28/*
29 * Local static versions eliminates the need for globals
30 */
31static int (*fileproc) ();
32static int (*filesdoneproc) ();
33static Dtype (*direntproc) ();
34static int (*dirleaveproc) ();
35static int which;
36static Dtype flags;
37static int aflag;
38static int readlock;
39static int dosrcs;
40static char update_dir[PATH_MAX];
41static char *repository = NULL;
42static List *entries = NULL;
43static List *srcfiles = NULL;
44static List *filelist = NULL;
45static List *dirlist = NULL;
46
47/*
48 * Called to start a recursive command Command line arguments are processed
49 * if present, otherwise the local directory is processed.
50 */
51int
52start_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc,
53 argc, argv, local, which, aflag, readlock,
54 update_preload, dosrcs)
55 int (*fileproc) ();
56 int (*filesdoneproc) ();
57 Dtype (*direntproc) ();
58 int (*dirleaveproc) ();
59 int argc;
60 char *argv[];
61 int local;
62 int which;
63 int aflag;
64 int readlock;
65 char *update_preload;
66 int dosrcs;
67{
68 int i, err = 0;
69 Dtype flags;
70
71 if (update_preload == NULL)
72 update_dir[0] = '\0';
73 else
74 (void) strcpy (update_dir, update_preload);
75
76 if (local)
77 flags = R_SKIP_DIRS;
78 else
79 flags = R_PROCESS;
80
81 /* clean up from any previous calls to start_recursion */
82 if (repository)
83 {
84 free (repository);
85 repository = (char *) NULL;
86 }
87 if (entries)
88 dellist (&entries);
89 if (srcfiles)
90 dellist (&srcfiles);
91 if (filelist)
92 dellist (&filelist);
93 if (dirlist)
94 dellist (&dirlist);
95
96 if (argc == 0)
97 {
98
99 /*
100 * There were no arguments, so we'll probably just recurse. The
101 * exception to the rule is when we are called from a directory
102 * without any CVS administration files. That has always meant to
103 * process each of the sub-directories, so we pretend like we were
104 * called with the list of sub-dirs of the current dir as args
105 */
106 if ((which & W_LOCAL) && !isdir (CVSADM) && !isdir (OCVSADM))
107 dirlist = Find_Dirs ((char *) NULL, W_LOCAL);
108 else
109 addlist (&dirlist, ".");
110
111 err += do_recursion (fileproc, filesdoneproc, direntproc,
112 dirleaveproc, flags, which, aflag,
113 readlock, dosrcs);
114 }
115 else
116 {
117
118 /*
119 * There were arguments, so we have to handle them by hand. To do
120 * that, we set up the filelist and dirlist with the arguments and
121 * call do_recursion. do_recursion recognizes the fact that the
122 * lists are non-null when it starts and doesn't update them
123 */
124
125 /* look for args with /-s in them */
126 for (i = 0; i < argc; i++)
127 if (index (argv[i], '/') != NULL)
128 break;
129
130 /* if we didn't find any hard one's, do it the easy way */
131 if (i == argc)
132 {
133 /* set up the lists */
134 for (i = 0; i < argc; i++)
135 {
136 if (isdir (argv[i]))
137 addlist (&dirlist, argv[i]);
138 else
139 {
140 if (isdir (CVSADM) || isdir (OCVSADM))
141 {
142 char *repos;
143 char tmp[PATH_MAX];
144
145 repos = Name_Repository ((char *) NULL, update_dir);
146 (void) sprintf (tmp, "%s/%s", repos, argv[i]);
147 if (isdir (tmp))
148 addlist (&dirlist, argv[i]);
149 else
150 addlist (&filelist, argv[i]);
151 free (repos);
152 }
153 else
154 addlist (&filelist, argv[i]);
155 }
156 }
157
158 /* we aren't recursive if no directories were specified */
159 if (dirlist == NULL)
160 local = 1;
161
162 /* process the lists */
163 err += do_recursion (fileproc, filesdoneproc, direntproc,
164 dirleaveproc, flags, which, aflag,
165 readlock, dosrcs);
166 }
167 /* otherwise - do it the hard way */
168 else
169 {
170 char *cp;
171 char *dir = (char *) NULL;
172 char *comp = (char *) NULL;
173 char *oldupdate = (char *) NULL;
174 char savewd[PATH_MAX];
175
176 if (getwd (savewd) == NULL)
177 error (1, 0, "could not get working directory: %s", savewd);
178
179 for (i = 0; i < argc; i++)
180 {
181 /* split the arg into the dir and component parts */
182 dir = xstrdup (argv[i]);
183 if ((cp = rindex (dir, '/')) != NULL)
184 {
185 *cp = '\0';
186 comp = xstrdup (cp + 1);
187 oldupdate = xstrdup (update_dir);
188 if (update_dir[0] != '\0')
189 (void) strcat (update_dir, "/");
190 (void) strcat (update_dir, dir);
191 }
192 else
193 {
194 comp = xstrdup (dir);
195 if (dir)
196 free (dir);
197 dir = (char *) NULL;
198 }
199
200 /* chdir to the appropriate place if necessary */
201 if (dir && chdir (dir) < 0)
202 error (1, errno, "could not chdir to %s", dir);
203
204 /* set up the list */
205 if (isdir (comp))
206 addlist (&dirlist, comp);
207 else
208 {
209 if (isdir (CVSADM) || isdir (OCVSADM))
210 {
211 char *repos;
212 char tmp[PATH_MAX];
213
214 repos = Name_Repository ((char *) NULL, update_dir);
215 (void) sprintf (tmp, "%s/%s", repos, comp);
216 if (isdir (tmp))
217 addlist (&dirlist, comp);
218 else
219 addlist (&filelist, comp);
220 free (repos);
221 }
222 else
223 addlist (&filelist, comp);
224 }
225
226 /* do the recursion */
227 err += do_recursion (fileproc, filesdoneproc, direntproc,
228 dirleaveproc, flags, which,
229 aflag, readlock, dosrcs);
230
231 /* chdir back and fix update_dir if necessary */
232 if (dir && chdir (savewd) < 0)
233 error (1, errno, "could not chdir to %s", dir);
234 if (oldupdate)
235 {
236 (void) strcpy (update_dir, oldupdate);
237 free (oldupdate);
238 }
239
240 }
241 if (dir)
242 free (dir);
243 if (comp)
244 free (comp);
245 }
246 }
247 return (err);
248}
249
250/*
251 * Implement the recursive policies on the local directory. This may be
252 * called directly, or may be called by start_recursion
253 */
254int
255do_recursion (xfileproc, xfilesdoneproc, xdirentproc, xdirleaveproc,
256 xflags, xwhich, xaflag, xreadlock, xdosrcs)
257 int (*xfileproc) ();
258 int (*xfilesdoneproc) ();
259 Dtype (*xdirentproc) ();
260 int (*xdirleaveproc) ();
261 Dtype xflags;
262 int xwhich;
263 int xaflag;
264 int xreadlock;
265 int xdosrcs;
266{
267 int err = 0;
268 int dodoneproc = 1;
269 char *srepository;
270
271 /* do nothing if told */
272 if (xflags == R_SKIP_ALL)
273 return (0);
274
275 /* set up the static vars */
276 fileproc = xfileproc;
277 filesdoneproc = xfilesdoneproc;
278 direntproc = xdirentproc;
279 dirleaveproc = xdirleaveproc;
280 flags = xflags;
281 which = xwhich;
282 aflag = xaflag;
283 readlock = noexec ? 0 : xreadlock;
284 dosrcs = xdosrcs;
285
286 /*
287 * Fill in repository with the current repository
288 */
289 if (which & W_LOCAL)
290 {
291 if (isdir (CVSADM) || isdir (OCVSADM))
292 repository = Name_Repository ((char *) NULL, update_dir);
293 else
294 repository = NULL;
295 }
296 else
297 {
298 repository = xmalloc (PATH_MAX);
299 (void) getwd (repository);
300 }
301 srepository = repository; /* remember what to free */
302
303 /*
304 * The filesdoneproc needs to be called for each directory where files
305 * processed, or each directory that is processed by a call where no
306 * directories were passed in. In fact, the only time we don't want to
307 * call back the filesdoneproc is when we are processing directories that
308 * were passed in on the command line (or in the special case of `.' when
309 * we were called with no args
310 */
311 if (dirlist != NULL && filelist == NULL)
312 dodoneproc = 0;
313
314 /*
315 * If filelist or dirlist is already set, we don't look again. Otherwise,
316 * find the files and directories
317 */
318 if (filelist == NULL && dirlist == NULL)
319 {
320 /* both lists were NULL, so start from scratch */
321 if (fileproc != NULL && flags != R_SKIP_FILES)
322 {
323 int lwhich = which;
324
325 /* be sure to look in the attic if we have sticky tags/date */
326 if ((lwhich & W_ATTIC) == 0)
327 if (isreadable (CVSADM_TAG))
328 lwhich |= W_ATTIC;
329
330 /* find the files and fill in entries if appropriate */
331 filelist = Find_Names (repository, lwhich, aflag, &entries);
332 }
333
334 /* find sub-directories if we will recurse */
335 if (flags != R_SKIP_DIRS)
336 dirlist = Find_Dirs (repository, which);
337 }
338 else
339 {
340 /* something was passed on the command line */
341 if (filelist != NULL && fileproc != NULL)
342 {
343 /* we will process files, so pre-parse entries */
344 if (which & W_LOCAL)
345 entries = ParseEntries (aflag);
346 }
347 }
348
349 /* process the files (if any) */
350 if (filelist != NULL)
351 {
352 /* read lock it if necessary */
353 if (readlock && repository && Reader_Lock (repository) != 0)
354 error (1, 0, "read lock failed - giving up");
355
356 /* pre-parse the source files */
357 if (dosrcs && repository)
358 srcfiles = RCS_parsefiles (filelist, repository);
359 else
360 srcfiles = (List *) NULL;
361
362 /* process the files */
363 err += walklist (filelist, do_file_proc);
364
365 /* unlock it */
366 if (readlock)
367 Lock_Cleanup ();
368
369 /* clean up */
370 dellist (&filelist);
371 dellist (&srcfiles);
372 dellist (&entries);
373 }
374
375 /* call-back files done proc (if any) */
376 if (dodoneproc && filesdoneproc != NULL)
377 err = filesdoneproc (err, repository, update_dir[0] ? update_dir : ".");
378
379 /* process the directories (if necessary) */
380 if (dirlist != NULL)
381 err += walklist (dirlist, do_dir_proc);
382#ifdef notdef
383 else if (dirleaveproc != NULL)
384 err += dirleaveproc(".", err, ".");
385#endif
386 dellist (&dirlist);
387
388 /* free the saved copy of the pointer if necessary */
389 if (srepository)
390 {
391 (void) free (srepository);
392 repository = (char *) NULL;
393 }
394
395 return (err);
396}
397
398/*
399 * Process each of the files in the list with the callback proc
400 */
401static int
402do_file_proc (p)
403 Node *p;
404{
405 if (fileproc != NULL)
406 return (fileproc (p->key, update_dir, repository, entries, srcfiles));
407 else
408 return (0);
409}
410
411/*
412 * Process each of the directories in the list (recursing as we go)
413 */
414static int
415do_dir_proc (p)
416 Node *p;
417{
418 char *dir = p->key;
419 char savewd[PATH_MAX];
420 char newrepos[PATH_MAX];
421 List *sdirlist;
422 char *srepository;
423 char *cp;
424 Dtype dir_return = R_PROCESS;
425 int stripped_dot = 0;
426 int err = 0;
427
428 /* set up update_dir - skip dots if not at start */
429 if (strcmp (dir, ".") != 0)
430 {
431 if (update_dir[0] != '\0')
432 {
433 (void) strcat (update_dir, "/");
434 (void) strcat (update_dir, dir);
435 }
436 else
437 (void) strcpy (update_dir, dir);
438
439 /*
440 * Here we need a plausible repository name for the sub-directory. We
441 * create one by concatenating the new directory name onto the
442 * previous repository name. The only case where the name should be
443 * used is in the case where we are creating a new sub-directory for
444 * update -d and in that case the generated name will be correct.
445 */
446 if (repository == NULL)
447 newrepos[0] = '\0';
448 else
449 (void) sprintf (newrepos, "%s/%s", repository, dir);
450 }
451 else
452 {
453 if (update_dir[0] == '\0')
454 (void) strcpy (update_dir, dir);
455
456 if (repository == NULL)
457 newrepos[0] = '\0';
458 else
459 (void) strcpy (newrepos, repository);
460 }
461
462 /* call-back dir entry proc (if any) */
463 if (direntproc != NULL)
464 dir_return = direntproc (dir, newrepos, update_dir);
465
466 /* only process the dir if the return code was 0 */
467 if (dir_return != R_SKIP_ALL)
468 {
469 /* save our current directory and static vars */
470 if (getwd (savewd) == NULL)
471 error (1, 0, "could not get working directory: %s", savewd);
472 sdirlist = dirlist;
473 srepository = repository;
474 dirlist = NULL;
475
476 /* cd to the sub-directory */
477 if (chdir (dir) < 0)
478 error (1, errno, "could not chdir to %s", dir);
479
480 /* honor the global SKIP_DIRS (a.k.a. local) */
481 if (flags == R_SKIP_DIRS)
482 dir_return = R_SKIP_DIRS;
483
484 /* remember if the `.' will be stripped for subsequent dirs */
485 if (strcmp (update_dir, ".") == 0)
486 {
487 update_dir[0] = '\0';
488 stripped_dot = 1;
489 }
490
491 /* make the recursive call */
492 err += do_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc,
493 dir_return, which, aflag, readlock, dosrcs);
494
495 /* put the `.' back if necessary */
496 if (stripped_dot)
497 (void) strcpy (update_dir, ".");
498
499 /* call-back dir leave proc (if any) */
500 if (dirleaveproc != NULL)
501 err = dirleaveproc (dir, err, update_dir);
502
503 /* get back to where we started and restore state vars */
504 if (chdir (savewd) < 0)
505 error (1, errno, "could not chdir to %s", savewd);
506 dirlist = sdirlist;
507 repository = srepository;
508 }
509
510 /* put back update_dir */
511 if ((cp = rindex (update_dir, '/')) != NULL)
512 *cp = '\0';
513 else
514 update_dir[0] = '\0';
515
516 return (err);
517}
518
519/*
520 * Add a node to a list allocating the list if necessary
521 */
522static void
523addlist (listp, key)
524 List **listp;
525 char *key;
526{
527 Node *p;
528
529 if (*listp == NULL)
530 *listp = getlist ();
531 p = getnode ();
532 p->type = FILES;
533 p->key = xstrdup (key);
534 (void) addnode (*listp, p);
535}