source: trunk/athena/etc/track/stamp.c @ 13492

Revision 13492, 15.1 KB checked in by danw, 25 years ago (diff)
fix warnings to make Irix n32 cc happy.
Line 
1/*
2 *      $Id: stamp.c,v 4.14 1999-08-13 00:15:12 danw Exp $
3 */
4
5#ifndef lint
6static 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
13extern char *mode_to_char(), *mode_to_fmt(), *mode_to_rfmt();
14void poppath();
15
16char 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
24write_statline( path, c)
25char **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
124void 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
146int stat_cmp( ain, bin)
147const 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
166void 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
177void 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
236char *
237dec_statfile( line, c)
238char *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 */
366int prev_ent = 0;;
367
368int cur_ent = 0;        /* not global! index into entries[]. */
369
370void
371init_next_match() {
372        prev_ent = 0;
373        cur_ent = 1;
374}
375
376int
377get_next_match( name)
378char *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 */
413int
414last_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 */
434int
435keyncmp( 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
451struct currentness *
452dec_entry( entnum, fr, to, cmp, tail)
453int 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( &currency_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, &currency_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, &currency_buf);
509                poppath( cmp);
510                return( &currency_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])
527char **
528initpath( 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}
542int
543pushpath( 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}
559void
560poppath( 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}
Note: See TracBrowser for help on using the repository browser.