1 | /* |
---|
2 | * $Id: stamp.c,v 4.14 1999-08-13 00:15:12 danw Exp $ |
---|
3 | */ |
---|
4 | |
---|
5 | #ifndef lint |
---|
6 | static char *rcsid_header_h = "$Id: stamp.c,v 4.14 1999-08-13 00:15:12 danw Exp $"; |
---|
7 | #endif |
---|
8 | |
---|
9 | #include "mit-copyright.h" |
---|
10 | |
---|
11 | #include "track.h" |
---|
12 | |
---|
13 | extern char *mode_to_char(), *mode_to_fmt(), *mode_to_rfmt(); |
---|
14 | void poppath(); |
---|
15 | |
---|
16 | char type_char[] = " cdbfls*89ABCDEF"; |
---|
17 | |
---|
18 | /* |
---|
19 | * Place a time-stamp line in the format: |
---|
20 | * type file uid.gid.mode.time |
---|
21 | * or a suitable derivate thereof |
---|
22 | */ |
---|
23 | |
---|
24 | write_statline( path, c) |
---|
25 | char **path; struct currentness *c; |
---|
26 | { |
---|
27 | char *format, *linebuf, *name; |
---|
28 | int same_name; |
---|
29 | unsigned int type; |
---|
30 | struct stat fromstat, *s; |
---|
31 | unsigned size, curr1 = 0, extra = 0, len = 0; |
---|
32 | |
---|
33 | if ( cur_line >= maxlines) { |
---|
34 | maxlines += MAXLINES; |
---|
35 | size = maxlines * sizeof statfilebufs[0]; |
---|
36 | statfilebufs = ( cur_line ? |
---|
37 | realloc( statfilebufs, size) : |
---|
38 | malloc( size)); |
---|
39 | if ( ! statfilebufs) { |
---|
40 | sprintf( errmsg, "alloc failed: %d statfile lines\n", |
---|
41 | cur_line); |
---|
42 | do_panic(); |
---|
43 | } |
---|
44 | } |
---|
45 | /* |
---|
46 | * set up type-dependent currency data: |
---|
47 | */ |
---|
48 | |
---|
49 | s = &c->sbuf; |
---|
50 | |
---|
51 | switch( type = TYPE( *s)) { |
---|
52 | case S_IFREG: |
---|
53 | curr1 = c->cksum; |
---|
54 | extra = TIME( *s); |
---|
55 | break; |
---|
56 | case S_IFLNK: |
---|
57 | curr1 = (unsigned int) c->link; |
---|
58 | len = strlen(c->link); |
---|
59 | break; |
---|
60 | case S_IFDIR: |
---|
61 | curr1 = 0; |
---|
62 | break; |
---|
63 | case S_IFBLK: |
---|
64 | curr1 = RDEV( *s); |
---|
65 | break; |
---|
66 | case S_IFCHR: |
---|
67 | curr1 = RDEV( *s); |
---|
68 | break; |
---|
69 | case S_IFSOCK: |
---|
70 | sprintf( errmsg, "can't track socket %s.\n", path[ ROOT]); |
---|
71 | do_gripe(); |
---|
72 | return( type); |
---|
73 | case S_IFMT: |
---|
74 | default: |
---|
75 | sprintf( errmsg, |
---|
76 | "bad type for inode %ul, pathname %s.\n\tapparent type = %c\n", |
---|
77 | (unsigned long)c->sbuf.st_ino, path[ ROOT], |
---|
78 | mode_to_string(type)); |
---|
79 | do_panic(); |
---|
80 | } |
---|
81 | /* set up name & sortkey: |
---|
82 | * root is a special case: |
---|
83 | * its "relative path" is "", which dec_statfile() can't read. |
---|
84 | */ |
---|
85 | name = path[ NAME]; |
---|
86 | if ( !*name) name = "/"; |
---|
87 | |
---|
88 | linebuf = statfilebufs[ cur_line] = malloc( strlen( name) + len + 100); |
---|
89 | if ( !linebuf) { |
---|
90 | sprintf( errmsg, "malloc failed: %d statfile lines\n", |
---|
91 | cur_line); |
---|
92 | do_panic(); |
---|
93 | } |
---|
94 | |
---|
95 | /* if this entry's fromfile != cmpfile, |
---|
96 | * the subscribing machine needs to know: |
---|
97 | */ |
---|
98 | same_name = ! strcmp( path[ NAME], c->name); |
---|
99 | |
---|
100 | /* to choose printing format, convert type-bits to array-index: |
---|
101 | * the formats specify 3-7 arguments, according to type: |
---|
102 | */ |
---|
103 | format = mode_to_fmt(type); |
---|
104 | |
---|
105 | sprintf( linebuf, format, name, same_name ? '=' : '~' , curr1, |
---|
106 | UID( *s), GID( *s), MODE( *s), extra); |
---|
107 | |
---|
108 | cur_line++; |
---|
109 | |
---|
110 | if ( verboseflag) |
---|
111 | fputs( linebuf, stderr); |
---|
112 | |
---|
113 | if ( same_name); |
---|
114 | else if ( ! lstat( path[ ROOT], &fromstat)) |
---|
115 | type = TYPE( fromstat); |
---|
116 | else { |
---|
117 | sprintf( errmsg, "(write_statline) can't lstat %s\n", |
---|
118 | path[ ROOT]); |
---|
119 | do_panic(); |
---|
120 | } |
---|
121 | return( type); |
---|
122 | } |
---|
123 | |
---|
124 | void fake_link( root, name, c) char *root, *name; struct currentness *c; { |
---|
125 | |
---|
126 | /* it is difficult to fool write_statline(), |
---|
127 | * update_file(), and curr_diff() all at once. |
---|
128 | * write_statline() can't take a normal currency in this case, |
---|
129 | * because it can't know the subscriber's fromroot. |
---|
130 | * curr_diff() needs to see a normal link's currency, |
---|
131 | * or else unneccessary link-updates will occur. |
---|
132 | * update_file() can use a normal currency to make a link. |
---|
133 | */ |
---|
134 | if ( name != c->name) /* speed hack for dec_statfile() */ |
---|
135 | strcpy( c->name, name); |
---|
136 | |
---|
137 | if ( *root) |
---|
138 | sprintf( c->link, "%s/%s", root, name); |
---|
139 | else *c->link = '\0'; /* special case for write_statline() */ |
---|
140 | |
---|
141 | c->cksum = 0; |
---|
142 | clear_stat( &c->sbuf); |
---|
143 | c->sbuf.st_mode = S_IFLNK; |
---|
144 | } |
---|
145 | |
---|
146 | int stat_cmp( ain, bin) |
---|
147 | const void *ain, *bin; |
---|
148 | { |
---|
149 | const char *a, *b; |
---|
150 | char c, d; |
---|
151 | |
---|
152 | a = *(const char **)ain; |
---|
153 | b = *(const char **)bin; |
---|
154 | /* Skip over first character which refers to file type */ |
---|
155 | a++; b++; |
---|
156 | while ( *a != ' ' && *b != ' ') { |
---|
157 | c = (*a == '/') ? '\001' : *a; a++; |
---|
158 | d = (*b == '/') ? '\001' : *b; b++; |
---|
159 | if ( c != d) |
---|
160 | return( c - d); |
---|
161 | } |
---|
162 | return( *a == ' ' ? -1 : 1); |
---|
163 | } |
---|
164 | |
---|
165 | |
---|
166 | void sort_stat() { |
---|
167 | int i; |
---|
168 | |
---|
169 | qsort( (char *) statfilebufs, cur_line, |
---|
170 | sizeof( statfilebufs[ 0]), stat_cmp); |
---|
171 | |
---|
172 | for ( i = 0; i < cur_line; i++) { |
---|
173 | fputs( statfilebufs[ i], statfile); |
---|
174 | } |
---|
175 | } |
---|
176 | |
---|
177 | void sort_entries() { |
---|
178 | char *tail; |
---|
179 | Table list; |
---|
180 | Entry *A, *C; |
---|
181 | int i, j; |
---|
182 | |
---|
183 | list.table = NULL; |
---|
184 | list.shift = 0; |
---|
185 | |
---|
186 | /* NOTE: we assume that each entry begins with a sortkey string. |
---|
187 | * don't include entries[ 0] in the sort: |
---|
188 | */ |
---|
189 | qsort( (char *)& entries[ 1], entrycnt - 1, |
---|
190 | sizeof( entries[ 1]), strcmp); |
---|
191 | |
---|
192 | /* for each entry's fromfile (call it A), |
---|
193 | * look for A's children amongst the subsequent entries, |
---|
194 | * and add any that you find to A's exception-table. |
---|
195 | * note that this may overfill the hash-table; in this event, |
---|
196 | * we accept the performance-hit, and don't try to rehash. |
---|
197 | */ |
---|
198 | for ( A = &entries[ i = 1 ]; i < entrycnt; A = &entries[ ++i]) { |
---|
199 | for ( C = &entries[ j = i+1]; j < entrycnt; C = &entries[ ++j]) { |
---|
200 | switch( keyncmp( C->sortkey, i)) { |
---|
201 | case 1: break; /* get next A */ |
---|
202 | case 0: tail = C->fromfile + A->keylen; |
---|
203 | while( '/' == *tail) tail++; |
---|
204 | if ( A->names.table) |
---|
205 | store( add_list_elt( tail, DONT_TRACK, NULL), |
---|
206 | &A->names); |
---|
207 | else add_list_elt( tail, DONT_TRACK, LIST( list)); |
---|
208 | case -1: continue; /* unlikely */ |
---|
209 | } |
---|
210 | break; /* get next A */ |
---|
211 | } |
---|
212 | /* if A doesn't already have an exception-list of names, |
---|
213 | * then we've accumulated the list of descendant-entries |
---|
214 | * in the list{} structure; convert it to a hash-table for A: |
---|
215 | */ |
---|
216 | if ( ! list.table); |
---|
217 | else if ( ! A->names.table) { |
---|
218 | A->names.table = list.table; |
---|
219 | A->names.shift = list.shift; |
---|
220 | list.table = NULL; |
---|
221 | list.shift = 0; |
---|
222 | list2hashtable( &A->names); |
---|
223 | } |
---|
224 | else { |
---|
225 | sprintf(errmsg, "sort_entries: internal error\n"); |
---|
226 | do_panic(); |
---|
227 | } |
---|
228 | } |
---|
229 | } |
---|
230 | |
---|
231 | /* |
---|
232 | * Decode a statfile line into its individual fields. |
---|
233 | * setup TYPE(), UID(), GID(), MODE(), TIME(), & RDEV() contents. |
---|
234 | */ |
---|
235 | |
---|
236 | char * |
---|
237 | dec_statfile( line, c) |
---|
238 | char *line; struct currentness *c; |
---|
239 | { |
---|
240 | struct stat *s; |
---|
241 | int *extra = 0; |
---|
242 | char *end, *format, *name, same_name, type; |
---|
243 | int dummy, *curr1 = &dummy, d = 0, u = 0, g = 0, m = 0; |
---|
244 | |
---|
245 | /* these long-int temps are necessary for pc/rt compatibility: |
---|
246 | * sscanf cannot scan into a short, though it may sometimes succeed |
---|
247 | * in doing so. the difficulty is that it can't know about the |
---|
248 | * target-integer's length, so it assumes that it's long. |
---|
249 | * since the rt will truncate a short's addr, in order to treat |
---|
250 | * it as a long, sscanf's data will often get lost. |
---|
251 | * the solution is to give scanf longs, and then to convert these |
---|
252 | * longs to shorts, explicitly. |
---|
253 | */ |
---|
254 | |
---|
255 | /* for speed, we laboriously parse the type-independent part |
---|
256 | * of the statline without calling sscanf(). |
---|
257 | * thus, we avoid copying the pathname strings around, |
---|
258 | * since the caller can re-use the originals, |
---|
259 | * once they're broken out of the line-format. |
---|
260 | */ |
---|
261 | type = *line++; |
---|
262 | name = line; |
---|
263 | line = strchr( line, ' '); |
---|
264 | if ( ! line) { |
---|
265 | sprintf( errmsg, "garbled statfile: bad line =\n%s\n", line); |
---|
266 | sprintf( errmsg, "line has only one field\n"); |
---|
267 | do_panic(); |
---|
268 | } |
---|
269 | *line++ = '\0'; |
---|
270 | |
---|
271 | /* in the statfile, which contains only relative pathnames, |
---|
272 | * "/" is the only pathname which can begin with a slash. |
---|
273 | * in entries[], the root appears as "", which is more natural, |
---|
274 | * because "" is "/"'s pathname relative to the mount-point fromroot. |
---|
275 | * and because pushpath() prepends slashes in the right places, anyway. |
---|
276 | * "" is hard to read with sscanf(), so we handle "/" specially: |
---|
277 | */ |
---|
278 | if ( '/' != *name); |
---|
279 | else if ( ! name[ 1]) *name = '\0'; |
---|
280 | else { |
---|
281 | sprintf(errmsg, "statfile passed an absolute pathname: %s\n", |
---|
282 | name); |
---|
283 | do_gripe(); |
---|
284 | } |
---|
285 | /* the subscriber needs to know whether the currency-data |
---|
286 | * came from the remote fromfile or from the remote cmpfile. |
---|
287 | */ |
---|
288 | switch( same_name = *line++) { |
---|
289 | case '=': strcpy( c->name, name); |
---|
290 | break; |
---|
291 | case '~': *c->name = '\0'; |
---|
292 | break; |
---|
293 | default: |
---|
294 | sprintf( errmsg, "garbled statfile: bad line =\n%s\n", line); |
---|
295 | sprintf( errmsg, "bad equality flag = %c\n", same_name); |
---|
296 | do_panic(); |
---|
297 | } |
---|
298 | *c->link = '\0'; |
---|
299 | c->cksum = 0; |
---|
300 | |
---|
301 | /* |
---|
302 | * set up scanf arg's for type-dependent currency-data: |
---|
303 | */ |
---|
304 | |
---|
305 | s = &c->sbuf; |
---|
306 | clear_stat( s); |
---|
307 | |
---|
308 | switch( type) { |
---|
309 | case 'f': |
---|
310 | s->st_mode = S_IFREG; |
---|
311 | curr1 = ( int*)&c->cksum; |
---|
312 | extra = ( int*)&s->st_mtime; |
---|
313 | break; |
---|
314 | case 'l': /* more common than dir's */ |
---|
315 | s->st_mode = S_IFLNK; |
---|
316 | if ( end = strchr( line, '\n')) |
---|
317 | *end = '\0'; |
---|
318 | else { |
---|
319 | sprintf( errmsg, "garbled statfile: bad line =\n%s\n", |
---|
320 | line); |
---|
321 | sprintf( errmsg, "line doesn't end with a newline\n"); |
---|
322 | do_panic(); |
---|
323 | } |
---|
324 | if ( !*line) fake_link( fromroot, c->name, c); |
---|
325 | else strcpy( c->link, line); |
---|
326 | break; |
---|
327 | case 'd': |
---|
328 | s->st_mode = S_IFDIR; |
---|
329 | curr1 = &dummy; |
---|
330 | break; |
---|
331 | case 'b': |
---|
332 | s->st_mode = S_IFBLK; |
---|
333 | curr1 = &d; |
---|
334 | break; |
---|
335 | case 'c': |
---|
336 | s->st_mode = S_IFCHR; |
---|
337 | curr1 = &d; |
---|
338 | break; |
---|
339 | default: |
---|
340 | sprintf( errmsg, "garbled statfile: bad line =\n%s\n", line); |
---|
341 | sprintf( errmsg, "first char isn't a file-type [fldbc]\n"); |
---|
342 | do_panic(); |
---|
343 | } |
---|
344 | /* if we've already parsed the line, |
---|
345 | * as in S_IFLNK case, skip the sscanf call: |
---|
346 | */ |
---|
347 | if ( *(format = mode_to_rfmt(s->st_mode))) |
---|
348 | sscanf( line, format, curr1, &u, &g, &m, extra); |
---|
349 | |
---|
350 | s->st_uid = (short) u; |
---|
351 | s->st_gid = (short) g; |
---|
352 | s->st_mode |= (short) m & 07777; |
---|
353 | s->st_rdev = (short) d; |
---|
354 | return( name); |
---|
355 | } |
---|
356 | |
---|
357 | /* the match abstractions implement a synchronized traversal |
---|
358 | * of the entries[] array & the statfile. both sets of data |
---|
359 | * are sorted by strcmp() on their sortkeys, which are made |
---|
360 | * via the KEYCPY macro. |
---|
361 | */ |
---|
362 | |
---|
363 | /* XXX: not the best, but not the worst approach either. |
---|
364 | * this allows main() to call readstat() twice, with correct initialization. |
---|
365 | */ |
---|
366 | int prev_ent = 0;; |
---|
367 | |
---|
368 | int cur_ent = 0; /* not global! index into entries[]. */ |
---|
369 | |
---|
370 | void |
---|
371 | init_next_match() { |
---|
372 | prev_ent = 0; |
---|
373 | cur_ent = 1; |
---|
374 | } |
---|
375 | |
---|
376 | int |
---|
377 | get_next_match( name) |
---|
378 | char *name; |
---|
379 | { |
---|
380 | char key[ LINELEN]; |
---|
381 | |
---|
382 | KEYCPY( key, name); |
---|
383 | |
---|
384 | for ( ; cur_ent < entrycnt; cur_ent++) |
---|
385 | switch ( keyncmp( key, cur_ent)) { |
---|
386 | case 0: return( cur_ent); |
---|
387 | case -1: return( 0); /* advance name's key */ |
---|
388 | case 1: continue; /* advance cur_ent's key */ |
---|
389 | } |
---|
390 | |
---|
391 | return( -1); /* out of entries */ |
---|
392 | } |
---|
393 | |
---|
394 | /* last_match: |
---|
395 | * we know that path begins with entnum's fromfile, |
---|
396 | * but path may also match other entries. |
---|
397 | * for example, the path /a/b/c matches the entries /a & /a/b. |
---|
398 | * we want the longest entry that matches. |
---|
399 | * look-ahead to find last sub-list entry that matches |
---|
400 | * the current pathname: this works because by keyncmp, |
---|
401 | * / is greater than /Z... |
---|
402 | * | matches /a |
---|
403 | * | is greater than /a/a |
---|
404 | * \ matches /a/b |
---|
405 | * /a/b/c < is greater than /a/b/a |
---|
406 | * / matches /a/b/c |
---|
407 | * | is less than /a/b/c/a |
---|
408 | * | is less than /a/b/d |
---|
409 | * \ is less than /a/c... |
---|
410 | * NOTE that the right-hand column is sorted by key, |
---|
411 | * and that the last match is the longest. |
---|
412 | */ |
---|
413 | int |
---|
414 | last_match( path, entnum) char *path; int entnum; { |
---|
415 | int i; |
---|
416 | char key[ LINELEN]; |
---|
417 | |
---|
418 | KEYCPY( key, path); |
---|
419 | for ( i = entnum + 1; i < entrycnt; i++) { |
---|
420 | switch ( keyncmp( key, i)) { |
---|
421 | case -1: break; /* quit at /a/b/d */ |
---|
422 | case 0: entnum = i; /* remember /a or /a/b */ |
---|
423 | case 1: continue; /* skip over /a/a */ |
---|
424 | } |
---|
425 | break; /* quit loop */ |
---|
426 | } |
---|
427 | return( entnum); |
---|
428 | } |
---|
429 | |
---|
430 | /* compare the path r with i's sortkey in the following way: |
---|
431 | * if the key is a subpath of r, return 0, as a match. |
---|
432 | * if the key is < or > r, return 1 or -1 respectively. |
---|
433 | */ |
---|
434 | int |
---|
435 | keyncmp( r, i) char *r; int i; { |
---|
436 | char *l; |
---|
437 | int diff, n; |
---|
438 | |
---|
439 | l = entries[ i].sortkey; |
---|
440 | n = entries[ i].keylen; |
---|
441 | |
---|
442 | diff = SIGN( strncmp( r, l, n)); |
---|
443 | |
---|
444 | /* if diff == 0 & n != 0 ( l isn't root) & |
---|
445 | * r[n] == '\0' or '\001', we have a match. |
---|
446 | * if n == 0, l is root, which matches everything. |
---|
447 | */ |
---|
448 | return( diff? diff: n ? ( (unsigned) r[n] > '\001') : 0); /* XXX */ |
---|
449 | } |
---|
450 | |
---|
451 | struct currentness * |
---|
452 | dec_entry( entnum, fr, to, cmp, tail) |
---|
453 | int entnum; char *fr[], *to[], *cmp[], *tail; { |
---|
454 | static struct currentness currency_buf, *entry_currency; |
---|
455 | int i; |
---|
456 | Entry *e; |
---|
457 | static int xref_flag = 0; |
---|
458 | |
---|
459 | /* this routine's main purpose is to transfer entnum's contents |
---|
460 | * to the paths fr, to, & cmp. |
---|
461 | * for efficiency and data-hiding reasons, we maintain various static |
---|
462 | * data, including the currentness data for readstat's last update. |
---|
463 | */ |
---|
464 | |
---|
465 | /* we avoid calling get_currentness(cmp) redundantly, but not just |
---|
466 | * for efficiency reasons: this helps our update-simulation for |
---|
467 | * nopullflag-support. see update_file(). |
---|
468 | * each cmpfile may get updated at most once; |
---|
469 | * if it does, update_file() will mark its currency "out-of-date". |
---|
470 | * xref_flag is set if any entry uses another entry's file |
---|
471 | * as a cmpfile. in this case, we have to search entries[] |
---|
472 | * to see if what we've modified is another entry's cmpfile. |
---|
473 | * if so, propagate the "out-of-date" mark to that entry. |
---|
474 | */ |
---|
475 | if ( xref_flag && updated( ¤cy_buf, NULL)) { |
---|
476 | for ( e = &entries[ i = 1]; i <= entrycnt; e = &entries[ ++i]) { |
---|
477 | if ( updated( &e->currency, NULL) || |
---|
478 | strcmp( e->cmpfile, currency_buf.name)); |
---|
479 | else updated( &e->currency, ¤cy_buf); |
---|
480 | } |
---|
481 | } |
---|
482 | currency_buf.sbuf.st_mode = S_IFMT; /* kill short-term data. */ |
---|
483 | |
---|
484 | if ( prev_ent != entnum) { |
---|
485 | prev_ent = entnum; |
---|
486 | |
---|
487 | /* a subtler, longer search would set this flag less often. |
---|
488 | */ |
---|
489 | if ( ! writeflag) |
---|
490 | xref_flag = strncmp( entries[ entnum].cmpfile, |
---|
491 | entries[ entnum].tofile, |
---|
492 | strlen( entries[ entnum].tofile)); |
---|
493 | |
---|
494 | poppath( fr); pushpath( fr, entries[ entnum].fromfile); |
---|
495 | poppath( to); pushpath( to, entries[ entnum].tofile); |
---|
496 | poppath(cmp); pushpath( cmp, entries[ entnum].cmpfile); |
---|
497 | |
---|
498 | entry_currency = &entries[ entnum].currency; |
---|
499 | } |
---|
500 | if ( updated( entry_currency, NULL)) |
---|
501 | get_currentness( cmp, entry_currency); |
---|
502 | |
---|
503 | if ( ! tail || ! *tail ) |
---|
504 | return( entry_currency); |
---|
505 | |
---|
506 | if ( S_IFDIR == TYPE( entry_currency->sbuf)) { /* usual case */ |
---|
507 | pushpath( cmp, tail); |
---|
508 | get_currentness( cmp, ¤cy_buf); |
---|
509 | poppath( cmp); |
---|
510 | return( ¤cy_buf); |
---|
511 | } |
---|
512 | /* rarely, a directory may have a non-dir as its cmpfile. |
---|
513 | * if the entry's cmpfile isn't a dir, then cmp[ ROOT], |
---|
514 | * is the comparison-file for each of the tofile's dependents. |
---|
515 | */ |
---|
516 | return( entry_currency); |
---|
517 | } |
---|
518 | |
---|
519 | /* these routines handle a stack of pointers into a character-string, |
---|
520 | * which typically is a UNIX pathname. |
---|
521 | * the first element CNT of the stack points to a depth-counter. |
---|
522 | * the second element ROOT points to the beginning of the pathname string. |
---|
523 | * subsequent elements point to terminal substrings of the pathname. |
---|
524 | * a slash precedes each element. |
---|
525 | */ |
---|
526 | #define COUNT(p) (*(int*)p[CNT]) |
---|
527 | char ** |
---|
528 | initpath( name) char *name; { |
---|
529 | char **p; |
---|
530 | |
---|
531 | /* for each stack-element, alloc a pointer |
---|
532 | * and 15 chars for a filename: |
---|
533 | */ |
---|
534 | p = (char **) malloc( stackmax * sizeof NULL); |
---|
535 | p[ CNT] = (char *) malloc( stackmax * 15 + sizeof ((int) 0)); |
---|
536 | COUNT( p) = 1; |
---|
537 | p[ ROOT] = p[ CNT] + sizeof ((int) 1); |
---|
538 | strcpy( p[ ROOT], name); |
---|
539 | p[ NAME] = p[ ROOT] + strlen( name); |
---|
540 | return( p); |
---|
541 | } |
---|
542 | int |
---|
543 | pushpath( p, name) char **p; char *name; { |
---|
544 | char *top; |
---|
545 | |
---|
546 | if ( ! p) return( -1); |
---|
547 | if ( ++COUNT( p) >= stackmax) { |
---|
548 | sprintf( errmsg, "%s\n%s\n%s %d.\n", |
---|
549 | "path stack overflow: directory too deep:", p[ ROOT], |
---|
550 | "use -S option, with value >", stackmax); |
---|
551 | do_panic(); |
---|
552 | } |
---|
553 | if ( *name) *p[ COUNT( p)]++ = '/'; |
---|
554 | top = p[ COUNT( p)]; |
---|
555 | strcpy( top, name); |
---|
556 | p[ COUNT( p) + 1] = top + strlen( top); |
---|
557 | return( COUNT( p)); |
---|
558 | } |
---|
559 | void |
---|
560 | poppath( p) char **p; { |
---|
561 | if ( ! p) return; |
---|
562 | else if ( 1 >= COUNT( p)) { |
---|
563 | sprintf(errmsg,"can't pop root from path-stack"); |
---|
564 | do_panic(); |
---|
565 | } |
---|
566 | else if ( *p[ COUNT( p)]) p[ COUNT( p)]--; |
---|
567 | /* non-null last elt; remove its initial slash */ |
---|
568 | *p[ COUNT( p)--] = '\0'; |
---|
569 | return; |
---|
570 | } |
---|