1 | /* |
---|
2 | * $Source: /afs/dev.mit.edu/source/repository/athena/etc/track/stamp.c,v $ |
---|
3 | * $Header: /afs/dev.mit.edu/source/repository/athena/etc/track/stamp.c,v 4.8 1988-06-21 19:44:36 don Exp $ |
---|
4 | * |
---|
5 | * $Log: not supported by cvs2svn $ |
---|
6 | * Revision 4.7 88/06/20 18:57:38 don |
---|
7 | * support for stamp.c version 4.4 : |
---|
8 | * this version's dec_entry has less static state, because it |
---|
9 | * detects that the entry hasn't been processed before, in a better way. |
---|
10 | * |
---|
11 | * Revision 4.6 88/06/10 15:58:39 don |
---|
12 | * changed DEV usage to RDEV. |
---|
13 | * |
---|
14 | * Revision 4.5 88/06/10 14:29:57 don |
---|
15 | * better error-handling on encountering sockets. |
---|
16 | * |
---|
17 | * Revision 4.4 88/05/26 13:59:55 don |
---|
18 | * cosmetics in sort_entries(): fixed indentation, and added comments. |
---|
19 | * |
---|
20 | * Revision 4.3 88/05/25 21:25:02 don |
---|
21 | * fixed a bug in sort_entries: only one descendant was getting added |
---|
22 | * to each entry's exception-list. |
---|
23 | * |
---|
24 | * Revision 4.2 88/05/24 17:41:08 don |
---|
25 | * beefed up garbled-statfile messages, to aid in finding a bug. |
---|
26 | * the bug is a garbled-statfile message at the end of an update. |
---|
27 | * this is intermittent. |
---|
28 | * |
---|
29 | * Revision 4.1 88/05/04 18:14:26 shanzer |
---|
30 | * fixed a bug in sort_entries(); its augmentation of a parent-entry's |
---|
31 | * exception-list didn't always work. -don |
---|
32 | * |
---|
33 | * Revision 4.0 88/04/14 16:43:00 don |
---|
34 | * this version is not compatible with prior versions. |
---|
35 | * it offers, chiefly, link-exporting, i.e., "->" systax in exception-lists. |
---|
36 | * it also offers sped-up exception-checking, via hash-tables. |
---|
37 | * a bug remains in -nopullflag support: if the entry's to-name top-level |
---|
38 | * dir doesn't exist, update_file doesn't get over it. |
---|
39 | * the fix should be put into the updated() routine, or possibly dec_entry(). |
---|
40 | * |
---|
41 | * Revision 3.0 88/03/09 13:17:47 don |
---|
42 | * this version is incompatible with prior versions. it offers: |
---|
43 | * 1) checksum-handling for regular files, to detect filesystem corruption. |
---|
44 | * 2) more concise & readable "updating" messages & error messages. |
---|
45 | * 3) better update-simulation when nopullflag is set. |
---|
46 | * 4) more support for non-default comparison-files. |
---|
47 | * finally, the "currentness" data-structure has replaced the statbufs |
---|
48 | * used before, so that the notion of currency is more readily extensible. |
---|
49 | * note: the statfile format has been changed. |
---|
50 | * |
---|
51 | * Revision 2.5 88/02/23 19:21:36 don |
---|
52 | * fixed pushpath() & poppath() so that pushing "" onto a path-stack |
---|
53 | * doesn't push a '/' as well. for example, pushpath( "/bin", "") should |
---|
54 | * yield "/bin", instead of "/bin/". this was causing findparent() to fail |
---|
55 | * when track needs to create bin under the subsciber's mount-point. |
---|
56 | * |
---|
57 | * Revision 2.4 88/01/29 18:24:02 don |
---|
58 | * bug fixes. also, now track can update the root. |
---|
59 | * |
---|
60 | * Revision 2.3 87/12/03 19:50:02 don |
---|
61 | * moved SIGN macro to track.h. |
---|
62 | * |
---|
63 | * Revision 2.2 87/12/03 17:31:15 don |
---|
64 | * fixed rt-port bug in dec_statfile's use of sscanf(): |
---|
65 | * can't fill a short int directly, because sscanf will interpret it |
---|
66 | * as a long-int, and on the rt, short* gets converted to int* via |
---|
67 | * truncation! |
---|
68 | * |
---|
69 | * Revision 2.1 87/12/01 16:44:54 don |
---|
70 | * fixed bugs in readstat's traversal of entries] and statfile: |
---|
71 | * cur_ent is no longer global, but is now part of get_next_match's |
---|
72 | * state. also, last_match() was causing entries[]'s last element to be |
---|
73 | * skipped. |
---|
74 | * |
---|
75 | * Revision 2.0 87/11/30 15:19:43 don |
---|
76 | * general rewrite; got rid of stamp data-type, with its attendant garbage, |
---|
77 | * cleaned up pathname-handling. readstat & writestat now sort overything |
---|
78 | * by pathname, which simplifies traversals/lookup. should be comprehensible |
---|
79 | * now. |
---|
80 | * |
---|
81 | * Revision 1.1 87/02/12 21:15:36 rfrench |
---|
82 | * Initial revision |
---|
83 | * |
---|
84 | */ |
---|
85 | |
---|
86 | #ifndef lint |
---|
87 | static char *rcsid_header_h = "$Header: /afs/dev.mit.edu/source/repository/athena/etc/track/stamp.c,v 4.8 1988-06-21 19:44:36 don Exp $"; |
---|
88 | #endif lint |
---|
89 | |
---|
90 | #include "mit-copyright.h" |
---|
91 | |
---|
92 | #include "track.h" |
---|
93 | |
---|
94 | /* XXX |
---|
95 | * convert right-shifted st_mode type-bits to corresponding formats: |
---|
96 | * S_IFCHR = 0020000 gets mapped to elt 1 of the array, |
---|
97 | * S_IFDIR = 0040000 => elt 2 of the array, |
---|
98 | * S_IFBLK = 0060000 => elt 3 of the array, |
---|
99 | * S_IFREG = 0100000 => elt 4 of the array, |
---|
100 | * S_IFLNK = 0120000 => elt 5 of the array, |
---|
101 | * S_IFSOCK= 0140000 => elt 6 of the array, ( only for error messagess), |
---|
102 | * S_IFMT = 0170000 => elt 7 of the array, ( dropping 1 bit). |
---|
103 | */ |
---|
104 | static char *write_formats[] = { |
---|
105 | "*ERROR (write_statline): %s's file type is 0.\n", |
---|
106 | "c%s %c%d(%d.%d.%o)\n", /* S_IFCHR */ |
---|
107 | "d%s %c%d(%d.%d.%o)\n", /* S_IFDIR */ |
---|
108 | "b%s %c%d(%d.%d.%o)\n", /* S_IFBLK */ |
---|
109 | "f%s %c%x(%d.%d.%o)%ld\n", /* S_IFREG */ |
---|
110 | "l%s %c%s\n", /* S_IFLNK */ |
---|
111 | "*ERROR (write_statline): can't track socket %s.\n", |
---|
112 | "*ERROR (write_statline): bad type S_IFMT %s.\n" |
---|
113 | }; |
---|
114 | |
---|
115 | static char *read_formats[] = { |
---|
116 | "", |
---|
117 | "%d(%d.%d.%o)\n", /* S_IFCHR */ |
---|
118 | "%d(%d.%d.%o)\n", /* S_IFDIR */ |
---|
119 | "%d(%d.%d.%o)\n", /* S_IFBLK */ |
---|
120 | "%x(%d.%d.%o)%ld\n", /* S_IFREG */ |
---|
121 | "", /* S_IFLNK */ |
---|
122 | "", /* S_IFSOCK */ |
---|
123 | "" /* S_IFMT */ |
---|
124 | }; |
---|
125 | |
---|
126 | char type_char[] = " cdbfls*89ABCDEF"; |
---|
127 | |
---|
128 | /* |
---|
129 | * Place a time-stamp line in the format: |
---|
130 | * type file uid.gid.mode.time |
---|
131 | * or a suitable derivate thereof |
---|
132 | */ |
---|
133 | |
---|
134 | write_statline( path, c) |
---|
135 | char **path; struct currentness *c; |
---|
136 | { |
---|
137 | char *format, *linebuf, *name; |
---|
138 | int same_name; |
---|
139 | unsigned int type; |
---|
140 | struct stat fromstat, *s; |
---|
141 | unsigned size, curr1 = 0, extra = 0; |
---|
142 | |
---|
143 | if ( cur_line >= maxlines) { |
---|
144 | maxlines += MAXLINES; |
---|
145 | size = maxlines * sizeof statfilebufs[0]; |
---|
146 | statfilebufs = |
---|
147 | (Statline *) ( cur_line ? |
---|
148 | realloc( (char *) statfilebufs, size) |
---|
149 | : malloc( size)); |
---|
150 | if ( ! statfilebufs) { |
---|
151 | sprintf( errmsg, "alloc failed: %d statfile lines\n", |
---|
152 | cur_line); |
---|
153 | do_panic(); |
---|
154 | } |
---|
155 | } |
---|
156 | /* |
---|
157 | * set up type-dependent currency data: |
---|
158 | */ |
---|
159 | |
---|
160 | s = &c->sbuf; |
---|
161 | |
---|
162 | switch( type = TYPE( *s)) { |
---|
163 | case S_IFREG: |
---|
164 | curr1 = c->cksum; |
---|
165 | extra = TIME( *s); |
---|
166 | break; |
---|
167 | case S_IFLNK: |
---|
168 | curr1 = (unsigned int) c->link; |
---|
169 | break; |
---|
170 | case S_IFDIR: |
---|
171 | curr1 = 0; |
---|
172 | break; |
---|
173 | case S_IFBLK: |
---|
174 | curr1 = RDEV( *s); |
---|
175 | break; |
---|
176 | case S_IFCHR: |
---|
177 | curr1 = RDEV( *s); |
---|
178 | break; |
---|
179 | case S_IFSOCK: |
---|
180 | sprintf( errmsg, "can't track socket %s.\n", path[ ROOT]); |
---|
181 | do_gripe(); |
---|
182 | return( type); |
---|
183 | case S_IFMT: |
---|
184 | default: |
---|
185 | sprintf( errmsg, |
---|
186 | "bad type for inode %d, pathname %s.\n\tapparent type = %c\n", |
---|
187 | c->sbuf.st_ino, path[ ROOT], type_char[ type >> 13]); |
---|
188 | do_panic(); |
---|
189 | } |
---|
190 | /* set up name & sortkey: |
---|
191 | * root is a special case: |
---|
192 | * its "relative path" is "", which dec_statfile() can't read. |
---|
193 | */ |
---|
194 | name = path[ NAME]; |
---|
195 | if ( !*name) name = "/"; |
---|
196 | |
---|
197 | KEYCPY( statfilebufs[ cur_line].sortkey, name); |
---|
198 | |
---|
199 | linebuf = statfilebufs[ cur_line].line; |
---|
200 | |
---|
201 | /* if this entry's fromfile != cmpfile, |
---|
202 | * the subscribing machine needs to know: |
---|
203 | */ |
---|
204 | same_name = ! strcmp( path[ NAME], c->name); |
---|
205 | |
---|
206 | /* to choose printing format, convert type-bits to array-index: |
---|
207 | * the formats specify 3-7 arguments, according to type: |
---|
208 | */ |
---|
209 | format = write_formats[ type >> 13]; |
---|
210 | |
---|
211 | sprintf( linebuf, format, name, same_name ? '=' : '~' , curr1, |
---|
212 | UID( *s), GID( *s), MODE( *s), extra); |
---|
213 | |
---|
214 | cur_line++; |
---|
215 | |
---|
216 | if ( verboseflag) |
---|
217 | fputs( linebuf, stderr); |
---|
218 | |
---|
219 | if ( same_name); |
---|
220 | else if ( ! (*statf)( path[ ROOT], &fromstat)) |
---|
221 | type = TYPE( fromstat); |
---|
222 | else { |
---|
223 | sprintf( errmsg, "(write_statline) can't %s %s\n", |
---|
224 | statn, path[ ROOT]); |
---|
225 | do_panic(); |
---|
226 | } |
---|
227 | return( type); |
---|
228 | } |
---|
229 | |
---|
230 | fake_link( root, name, c) char *root, *name; struct currentness *c; { |
---|
231 | |
---|
232 | /* it is difficult to fool write_statline(), |
---|
233 | * update_file(), and curr_diff() all at once. |
---|
234 | * write_statline() can't take a normal currency in this case, |
---|
235 | * because it can't know the subscriber's fromroot. |
---|
236 | * curr_diff() needs to see a normal link's currency, |
---|
237 | * or else unneccessary link-updates will occur. |
---|
238 | * update_file() can use a normal currency to make a link. |
---|
239 | */ |
---|
240 | if ( name != c->name) /* speed hack for dec_statfile() */ |
---|
241 | strcpy( c->name, name); |
---|
242 | |
---|
243 | if ( *root) |
---|
244 | sprintf( c->link, "%s/%s", root, name); |
---|
245 | else *c->link = '\0'; /* special case for write_statline() */ |
---|
246 | |
---|
247 | c->cksum = 0; |
---|
248 | clear_stat( &c->sbuf); |
---|
249 | c->sbuf.st_mode = S_IFLNK; |
---|
250 | } |
---|
251 | |
---|
252 | sort_stat() { |
---|
253 | int i; |
---|
254 | |
---|
255 | /* NOTE: this qsort call assumes that each statfilebufs[] element |
---|
256 | * begins with a sortkey as its first field. |
---|
257 | */ |
---|
258 | qsort( (char *) statfilebufs, cur_line, |
---|
259 | sizeof( statfilebufs[ 0]), strcmp); |
---|
260 | |
---|
261 | for ( i = 0; i < cur_line; i++) { |
---|
262 | fputs( statfilebufs[ i].line, statfile); |
---|
263 | } |
---|
264 | } |
---|
265 | |
---|
266 | sort_entries() { |
---|
267 | char *tail; |
---|
268 | Table list; |
---|
269 | Entry *A, *C; |
---|
270 | int i, j; |
---|
271 | |
---|
272 | list.table = NULL; |
---|
273 | list.shift = 0; |
---|
274 | |
---|
275 | /* NOTE: we assume that each entry begins with a sortkey string. |
---|
276 | * don't include entries[ 0] in the sort: |
---|
277 | */ |
---|
278 | qsort( (char *)& entries[ 1], entrycnt - 1, |
---|
279 | sizeof( entries[ 1]), strcmp); |
---|
280 | |
---|
281 | /* for each entry's fromfile (call it A), |
---|
282 | * look for A's children amongst the subsequent entries, |
---|
283 | * and add any that you find to A's exception-table. |
---|
284 | * note that this may overfill the hash-table; in this event, |
---|
285 | * we accept the performance-hit, and don't try to rehash. |
---|
286 | */ |
---|
287 | for ( A = &entries[ i = 1 ]; i < entrycnt; A = &entries[ ++i]) { |
---|
288 | for ( C = &entries[ j = i+1]; j < entrycnt; C = &entries[ ++j]) { |
---|
289 | switch( keyncmp( C->sortkey, i)) { |
---|
290 | case 1: break; /* get next A */ |
---|
291 | case 0: tail = C->fromfile + A->keylen; |
---|
292 | while( '/' == *tail) tail++; |
---|
293 | if ( A->names.table) |
---|
294 | store( add_list_elt( tail, DONT_TRACK, NULL), |
---|
295 | &A->names); |
---|
296 | else add_list_elt( tail, DONT_TRACK, LIST( list)); |
---|
297 | case -1: continue; /* unlikely */ |
---|
298 | } |
---|
299 | break; /* get next A */ |
---|
300 | } |
---|
301 | /* if A doesn't already have an exception-list of names, |
---|
302 | * then we've accumulated the list of descendant-entries |
---|
303 | * in the list{} structure; convert it to a hash-table for A: |
---|
304 | */ |
---|
305 | if ( ! list.table); |
---|
306 | else if ( ! A->names.table) { |
---|
307 | A->names.table = list.table; |
---|
308 | A->names.shift = list.shift; |
---|
309 | list.table = NULL; |
---|
310 | list.shift = 0; |
---|
311 | list2hashtable( &A->names); |
---|
312 | } |
---|
313 | else { |
---|
314 | sprintf(errmsg, "sort_entries: internal error\n"); |
---|
315 | do_panic(); |
---|
316 | } |
---|
317 | } |
---|
318 | } |
---|
319 | |
---|
320 | /* |
---|
321 | * Decode a statfile line into its individual fields. |
---|
322 | * setup TYPE(), UID(), GID(), MODE(), TIME(), & RDEV() contents. |
---|
323 | */ |
---|
324 | |
---|
325 | char * |
---|
326 | dec_statfile( line, c) |
---|
327 | char *line; struct currentness *c; |
---|
328 | { |
---|
329 | struct stat *s; |
---|
330 | int *extra = 0; |
---|
331 | char *end, *format, *name, same_name, type; |
---|
332 | int dummy, *curr1 = &dummy, d = 0, u = 0, g = 0, m = 0; |
---|
333 | |
---|
334 | /* these long-int temps are necessary for pc/rt compatibility: |
---|
335 | * sscanf cannot scan into a short, though it may sometimes succeed |
---|
336 | * in doing so. the difficulty is that it can't know about the |
---|
337 | * target-integer's length, so it assumes that it's long. |
---|
338 | * since the rt will truncate a short's addr, in order to treat |
---|
339 | * it as a long, sscanf's data will often get lost. |
---|
340 | * the solution is to give scanf longs, and then to convert these |
---|
341 | * longs to shorts, explicitly. |
---|
342 | */ |
---|
343 | |
---|
344 | /* for speed, we laboriously parse the type-independent part |
---|
345 | * of the statline without calling sscanf(). |
---|
346 | * thus, we avoid copying the pathname strings around, |
---|
347 | * since the caller can re-use the originals, |
---|
348 | * once they're broken out of the line-format. |
---|
349 | */ |
---|
350 | type = *line++; |
---|
351 | name = line; |
---|
352 | line = index( line, ' '); |
---|
353 | if ( ! line) { |
---|
354 | sprintf( errmsg, "garbled statfile: bad line =\n%s\n", line); |
---|
355 | sprintf( errmsg, "line has only one field\n"); |
---|
356 | do_panic(); |
---|
357 | } |
---|
358 | *line++ = '\0'; |
---|
359 | |
---|
360 | /* in the statfile, which contains only relative pathnames, |
---|
361 | * "/" is the only pathname which can begin with a slash. |
---|
362 | * in entries[], the root appears as "", which is more natural, |
---|
363 | * because "" is "/"'s pathname relative to the mount-point fromroot. |
---|
364 | * and because pushpath() prepends slashes in the right places, anyway. |
---|
365 | * "" is hard to read with sscanf(), so we handle "/" specially: |
---|
366 | */ |
---|
367 | if ( '/' != *name); |
---|
368 | else if ( ! name[ 1]) *name = '\0'; |
---|
369 | else { |
---|
370 | sprintf(errmsg, "statfile passed an absolute pathname: %s\n", |
---|
371 | name); |
---|
372 | do_gripe(); |
---|
373 | } |
---|
374 | /* the subscriber needs to know whether the currency-data |
---|
375 | * came from the remote fromfile or from the remote cmpfile. |
---|
376 | */ |
---|
377 | switch( same_name = *line++) { |
---|
378 | case '=': strcpy( c->name, name); |
---|
379 | break; |
---|
380 | case '~': *c->name = '\0'; |
---|
381 | break; |
---|
382 | default: |
---|
383 | sprintf( errmsg, "garbled statfile: bad line =\n%s\n", line); |
---|
384 | sprintf( errmsg, "bad equality flag = %c\n", same_name); |
---|
385 | do_panic(); |
---|
386 | } |
---|
387 | *c->link = '\0'; |
---|
388 | c->cksum = 0; |
---|
389 | |
---|
390 | /* |
---|
391 | * set up scanf arg's for type-dependent currency-data: |
---|
392 | */ |
---|
393 | |
---|
394 | s = &c->sbuf; |
---|
395 | clear_stat( s); |
---|
396 | |
---|
397 | switch( type) { |
---|
398 | case 'f': |
---|
399 | s->st_mode = S_IFREG; |
---|
400 | curr1 = ( int*)&c->cksum; |
---|
401 | extra = ( int*)&s->st_mtime; |
---|
402 | break; |
---|
403 | case 'l': /* more common than dir's */ |
---|
404 | s->st_mode = S_IFLNK; |
---|
405 | if ( end = index( line, '\n')) |
---|
406 | *end = '\0'; |
---|
407 | else { |
---|
408 | sprintf( errmsg, "garbled statfile: bad line =\n%s\n", |
---|
409 | line); |
---|
410 | sprintf( errmsg, "line doesn't end with a newline\n"); |
---|
411 | do_panic(); |
---|
412 | } |
---|
413 | if ( !*line) fake_link( fromroot, c->name, c); |
---|
414 | else strcpy( c->link, line); |
---|
415 | break; |
---|
416 | case 'd': |
---|
417 | s->st_mode = S_IFDIR; |
---|
418 | curr1 = &dummy; |
---|
419 | break; |
---|
420 | case 'b': |
---|
421 | s->st_mode = S_IFBLK; |
---|
422 | curr1 = &d; |
---|
423 | break; |
---|
424 | case 'c': |
---|
425 | s->st_mode = S_IFCHR; |
---|
426 | curr1 = &d; |
---|
427 | break; |
---|
428 | default: |
---|
429 | sprintf( errmsg, "garbled statfile: bad line =\n%s\n", line); |
---|
430 | sprintf( errmsg, "first char isn't a file-type [fldbc]\n"); |
---|
431 | do_panic(); |
---|
432 | } |
---|
433 | /* if we've already parsed the line, |
---|
434 | * as in S_IFLNK case, skip the sscanf call: |
---|
435 | */ |
---|
436 | if ( *(format = read_formats[ s->st_mode >> 13])) |
---|
437 | sscanf( line, format, curr1, &u, &g, &m, extra); |
---|
438 | |
---|
439 | s->st_uid = (short) u; |
---|
440 | s->st_gid = (short) g; |
---|
441 | s->st_mode |= (short) m & 07777; |
---|
442 | s->st_rdev = (short) d; |
---|
443 | return( name); |
---|
444 | } |
---|
445 | |
---|
446 | /* the match abstractions implement a synchronized traversal |
---|
447 | * of the entries[] array & the statfile. both sets of data |
---|
448 | * are sorted by strcmp() on their sortkeys, which are made |
---|
449 | * via the KEYCPY macro. |
---|
450 | */ |
---|
451 | |
---|
452 | /* XXX: not the best, but not the worst approach either. |
---|
453 | * this allows main() to call readstat() twice, with correct initialization. |
---|
454 | */ |
---|
455 | int prev_ent = 0;; |
---|
456 | |
---|
457 | int cur_ent = 0; /* not global! index into entries[]. */ |
---|
458 | |
---|
459 | init_next_match() { |
---|
460 | prev_ent = 0; |
---|
461 | cur_ent = 1; |
---|
462 | } |
---|
463 | |
---|
464 | int |
---|
465 | get_next_match( name) |
---|
466 | char *name; |
---|
467 | { |
---|
468 | char key[ LINELEN]; |
---|
469 | |
---|
470 | KEYCPY( key, name); |
---|
471 | |
---|
472 | for ( ; cur_ent < entrycnt; cur_ent++) |
---|
473 | switch ( keyncmp( key, cur_ent)) { |
---|
474 | case 0: return( cur_ent); |
---|
475 | case -1: return( 0); /* advance name's key */ |
---|
476 | case 1: continue; /* advance cur_ent's key */ |
---|
477 | } |
---|
478 | |
---|
479 | return( -1); /* out of entries */ |
---|
480 | } |
---|
481 | |
---|
482 | /* last_match: |
---|
483 | * we know that path begins with entnum's fromfile, |
---|
484 | * but path may also match other entries. |
---|
485 | * for example, the path /a/b/c matches the entries /a & /a/b. |
---|
486 | * we want the longest entry that matches. |
---|
487 | * look-ahead to find last sub-list entry that matches |
---|
488 | * the current pathname: this works because by keyncmp, |
---|
489 | * / is greater than /Z... |
---|
490 | * | matches /a |
---|
491 | * | is greater than /a/a |
---|
492 | * \ matches /a/b |
---|
493 | * /a/b/c < is greater than /a/b/a |
---|
494 | * / matches /a/b/c |
---|
495 | * | is less than /a/b/c/a |
---|
496 | * | is less than /a/b/d |
---|
497 | * \ is less than /a/c... |
---|
498 | * NOTE that the right-hand column is sorted by key, |
---|
499 | * and that the last match is the longest. |
---|
500 | */ |
---|
501 | int |
---|
502 | last_match( path, entnum) char *path; int entnum; { |
---|
503 | int i; |
---|
504 | char key[ LINELEN]; |
---|
505 | |
---|
506 | KEYCPY( key, path); |
---|
507 | for ( i = entnum + 1; i < entrycnt; i++) { |
---|
508 | switch ( keyncmp( key, i)) { |
---|
509 | case -1: break; /* quit at /a/b/d */ |
---|
510 | case 0: entnum = i; /* remember /a or /a/b */ |
---|
511 | case 1: continue; /* skip over /a/a */ |
---|
512 | } |
---|
513 | break; /* quit loop */ |
---|
514 | } |
---|
515 | return( entnum); |
---|
516 | } |
---|
517 | |
---|
518 | /* compare the path r with i's sortkey in the following way: |
---|
519 | * if the key is a subpath of r, return 0, as a match. |
---|
520 | * if the key is < or > r, return 1 or -1 respectively. |
---|
521 | */ |
---|
522 | int |
---|
523 | keyncmp( r, i) char *r; int i; { |
---|
524 | char *l; |
---|
525 | int diff, n; |
---|
526 | |
---|
527 | l = entries[ i].sortkey; |
---|
528 | n = entries[ i].keylen; |
---|
529 | |
---|
530 | diff = SIGN( strncmp( r, l, n)); |
---|
531 | |
---|
532 | /* if diff == 0 & n != 0 ( l isn't root) & |
---|
533 | * r[n] == '\0' or '\001', we have a match. |
---|
534 | * if n == 0, l is root, which matches everything. |
---|
535 | */ |
---|
536 | return( diff? diff: n ? ( (unsigned) r[n] > '\001') : 0); /* XXX */ |
---|
537 | } |
---|
538 | |
---|
539 | struct currentness * |
---|
540 | dec_entry( entnum, fr, to, cmp, tail) |
---|
541 | int entnum; char *fr[], *to[], *cmp[], *tail; { |
---|
542 | static struct currentness currency_buf, *entry_currency; |
---|
543 | int i; |
---|
544 | Entry *e; |
---|
545 | static int xref_flag = 0; |
---|
546 | |
---|
547 | /* this routine's main purpose is to transfer entnum's contents |
---|
548 | * to the paths fr, to, & cmp. |
---|
549 | * for efficiency and data-hiding reasons, we maintain various static |
---|
550 | * data, including the currentness data for readstat's last update. |
---|
551 | */ |
---|
552 | |
---|
553 | /* we avoid calling get_currentness(cmp) redundantly, but not just |
---|
554 | * for efficiency reasons: this helps our update-simulation for |
---|
555 | * nopullflag-support. see update_file(). |
---|
556 | * each cmpfile may get updated at most once; |
---|
557 | * if it does, update_file() will mark its currency "out-of-date". |
---|
558 | * xref_flag is set if any entry uses another entry's file |
---|
559 | * as a cmpfile. in this case, we have to search entries[] |
---|
560 | * to see if what we've modified is another entry's cmpfile. |
---|
561 | * if so, propagate the "out-of-date" mark to that entry. |
---|
562 | */ |
---|
563 | if ( xref_flag && updated( ¤cy_buf, NULL)) { |
---|
564 | for ( e = &entries[ i = 1]; i <= entrycnt; e = &entries[ ++i]) { |
---|
565 | if ( updated( &e->currency, NULL) || |
---|
566 | strcmp( e->cmpfile, currency_buf.name)); |
---|
567 | else updated( &e->currency, ¤cy_buf); |
---|
568 | } |
---|
569 | } |
---|
570 | currency_buf.sbuf.st_mode = S_IFMT; /* kill short-term data. */ |
---|
571 | |
---|
572 | if ( prev_ent != entnum) { |
---|
573 | prev_ent = entnum; |
---|
574 | |
---|
575 | /* a subtler, longer search would set this flag less often. |
---|
576 | */ |
---|
577 | if ( ! writeflag) |
---|
578 | xref_flag = strncmp( entries[ entnum].cmpfile, |
---|
579 | entries[ entnum].tofile, |
---|
580 | strlen( entries[ entnum].tofile)); |
---|
581 | |
---|
582 | poppath( fr); pushpath( fr, entries[ entnum].fromfile); |
---|
583 | poppath( to); pushpath( to, entries[ entnum].tofile); |
---|
584 | poppath(cmp); pushpath( cmp, entries[ entnum].cmpfile); |
---|
585 | |
---|
586 | /* this function-var is global, and used generally. |
---|
587 | */ |
---|
588 | statf = entries[ entnum].followlink ? stat : lstat; |
---|
589 | statn = entries[ entnum].followlink ? "stat" : "lstat"; |
---|
590 | |
---|
591 | entry_currency = &entries[ entnum].currency; |
---|
592 | } |
---|
593 | if ( updated( entry_currency, NULL)) |
---|
594 | get_currentness( cmp, entry_currency); |
---|
595 | |
---|
596 | if ( ! tail || ! *tail ) |
---|
597 | return( entry_currency); |
---|
598 | |
---|
599 | if ( S_IFDIR == TYPE( entry_currency->sbuf)) { /* usual case */ |
---|
600 | pushpath( cmp, tail); |
---|
601 | get_currentness( cmp, ¤cy_buf); |
---|
602 | poppath( cmp); |
---|
603 | return( ¤cy_buf); |
---|
604 | } |
---|
605 | /* rarely, a directory may have a non-dir as its cmpfile. |
---|
606 | * if the entry's cmpfile isn't a dir, then cmp[ ROOT], |
---|
607 | * is the comparison-file for each of the tofile's dependents. |
---|
608 | */ |
---|
609 | return( entry_currency); |
---|
610 | } |
---|
611 | |
---|
612 | /* these routines handle a stack of pointers into a character-string, |
---|
613 | * which typically is a UNIX pathname. |
---|
614 | * the first element CNT of the stack points to a depth-counter. |
---|
615 | * the second element ROOT points to the beginning of the pathname string. |
---|
616 | * subsequent elements point to terminal substrings of the pathname. |
---|
617 | * a slash precedes each element. |
---|
618 | */ |
---|
619 | #define COUNT(p) (*(int*)p[CNT]) |
---|
620 | char ** |
---|
621 | initpath( name) char *name; { |
---|
622 | char **p; |
---|
623 | |
---|
624 | /* for each stack-element, alloc a pointer |
---|
625 | * and 15 chars for a filename: |
---|
626 | */ |
---|
627 | p = (char **) malloc( stackmax * sizeof NULL); |
---|
628 | p[ CNT] = (char *) malloc( stackmax * 15 + sizeof ((int) 0)); |
---|
629 | COUNT( p) = 1; |
---|
630 | p[ ROOT] = p[ CNT] + sizeof ((int) 1); |
---|
631 | strcpy( p[ ROOT], name); |
---|
632 | p[ NAME] = p[ ROOT] + strlen( name); |
---|
633 | return( p); |
---|
634 | } |
---|
635 | int |
---|
636 | pushpath( p, name) char **p; char *name; { |
---|
637 | char *top; |
---|
638 | |
---|
639 | if ( ! p) return( -1); |
---|
640 | if ( ++COUNT( p) >= stackmax) { |
---|
641 | sprintf( errmsg, "%s\n%s\n%s %d.\n", |
---|
642 | "path stack overflow: directory too deep:", p[ ROOT], |
---|
643 | "use -S option, with value >", stackmax); |
---|
644 | do_panic(); |
---|
645 | } |
---|
646 | if ( *name) *p[ COUNT( p)]++ = '/'; |
---|
647 | top = p[ COUNT( p)]; |
---|
648 | strcpy( top, name); |
---|
649 | p[ COUNT( p) + 1] = top + strlen( top); |
---|
650 | return( COUNT( p)); |
---|
651 | } |
---|
652 | poppath( p) char **p; { |
---|
653 | if ( ! p) return; |
---|
654 | else if ( 1 >= COUNT( p)) { |
---|
655 | sprintf(errmsg,"can't pop root from path-stack"); |
---|
656 | do_panic(); |
---|
657 | } |
---|
658 | else if ( *p[ COUNT( p)]) p[ COUNT( p)]--; |
---|
659 | /* non-null last elt; remove its initial slash */ |
---|
660 | *p[ COUNT( p)--] = '\0'; |
---|
661 | return; |
---|
662 | } |
---|