source: trunk/athena/etc/track/except.c @ 1469

Revision 1469, 10.4 KB checked in by don, 36 years ago (diff)
bellcore copyright.
Line 
1/*
2 *      $Source: /afs/dev.mit.edu/source/repository/athena/etc/track/except.c,v $
3 *      $Header: /afs/dev.mit.edu/source/repository/athena/etc/track/except.c,v 4.4 1988-09-19 18:07:33 don Exp $
4 *
5 *      $Log: not supported by cvs2svn $
6 * Revision 4.3  88/06/21  19:45:53  don
7 * fixed a bug in store(), and made gettail() NOT delete hash-table matches,
8 * to support link-first updates.
9 *
10 * Revision 4.2  88/06/10  14:43:55  don
11 * moved incl_devs (-d option) control to gettail() from update_files();
12 *
13 * Revision 4.1  88/05/17  19:00:31  don
14 * fixed another bug in GLOBAL handling, by simplifying pattern-list
15 * traversal. now, global-list is chained onto end of each entry's list.
16 *
17 * Revision 4.0  88/04/14  16:42:35  don
18 * this version is not compatible with prior versions.
19 * it offers, chiefly, link-exporting, i.e., "->" systax in exception-lists.
20 * it also offers sped-up exception-checking, via hash-tables.
21 * a bug remains in -nopullflag support: if the entry's to-name top-level
22 * dir doesn't exist, update_file doesn't get over it.
23 * the fix should be put into the updated() routine, or possibly dec_entry().
24 *
25 * Revision 3.0  88/03/09  13:53:42  don
26 * no changes.
27 *
28 * Revision 2.4  88/01/29  18:22:53  don
29 * bug fixes. also, now track can update the root.
30 *
31 * Revision 2.3  87/12/03  20:36:59  don
32 * fixed a portability bug in FASTEQ macro, and made yacc sort each
33 * exceptions-list, so that goodname can run faster.
34 *
35 * Revision 2.2  87/12/03  17:33:54  don
36 * fixed lint warnings.
37 *
38 * Revision 2.2  87/12/01  20:22:31  don
39 * fixed a bug in match(): it was usually compiling exception-names,
40 * even if they weren't regexp's. this seems to have led to some bogus
41 * matches. at least, "track -w" doesn't miss files anymore.
42 * this bug was created during the rewrite.
43 *
44 * Revision 2.1  87/12/01  16:41:36  don
45 * fixed bugs in readstat's traversal of entries] and statfile:
46 * cur_ent is no longer global, but is now part of get_next_match's
47 * state. also, last_match() was causing entries[]'s last element to be
48 * skipped.
49 *
50 * Revision 2.0  87/11/30  15:19:17  don
51 * general rewrite; got rid of stamp data-type, with its attendant garbage,
52 * cleaned up pathname-handling. readstat & writestat now sort overything
53 * by pathname, which simplifies traversals/lookup. should be comprehensible
54 * now.
55 *
56 * Revision 1.1  87/02/12  21:14:36  rfrench
57 * Initial revision
58 *
59 */
60
61#ifndef lint
62static char *rcsid_header_h = "$Header: /afs/dev.mit.edu/source/repository/athena/etc/track/except.c,v 4.4 1988-09-19 18:07:33 don Exp $";
63#endif lint
64
65#include "mit-copyright.h"
66#include "bellcore-copyright.h"
67
68#include "track.h"
69
70/*
71**      routine to implement exception lists:
72*/
73
74gettail( pathp, type, entnum)
75char **pathp;
76unsigned short type;
77int entnum;
78{
79        static char exc_dir[ LINELEN] = "";
80        List_element **except, *q;
81        char retval = NORMALCASE, *tail, *wholename;
82        unsigned int k;
83        Entry *e = &entries[ entnum], *g = &entries[ 0];
84
85        if (( type == S_IFBLK || type == S_IFCHR) && ! incl_devs)
86                return( DONT_TRACK);
87
88        /* strip fromfile from path:
89         * path   == fromfile/tail
90         * keylen == strlen( fromfile).
91         */
92        wholename = *pathp;
93        *pathp += e->keylen;
94
95        if ( ! **pathp) return( NORMALCASE);
96        while( *++*pathp == '/');       
97
98        /* if exc_dir is not empty, then it holds the most recently excepted
99         * subdir of the current entry. if the wholename starts with exc_dir,
100         * then we count it as an exception, too.
101         * excepted links are handled this way, too, in case they target
102         * directories. exc_dir is a speed hack.
103         */
104        if ( ! *exc_dir);
105        else if ( strncmp( wholename, exc_dir, strlen( exc_dir)))
106                *exc_dir = '\0';
107        else    return( DONT_TRACK);
108
109        k = hash( tail = *pathp);
110
111        if ( e->names.table && ( except = lookup( tail, k, &e->names))) {
112                retval = FLAG( *except);
113        }
114        else if ( g->names.table && ( except = lookup( tail, k, &g->names))) {
115                retval = FLAG( *except);
116        }
117        /*      compare the tail with each pattern in both entries' lists:
118         *      the global list ( g->patterns),
119         *      and the current entry's exception patterns.
120         *      the global list is chained to the end of e->patterns,
121         *      during subscription-list parsing.
122         */
123        else for( q = e->patterns; q; q = NEXT( q))
124                if ( match( TEXT( q), tail)) {
125                        retval = FLAG( q);
126                        break;
127                }
128        /* now, retval has been computed.
129         * if it's not NORMALCASE, we've had a match:
130         * either tail is to be a link to the exporter's filesystem,
131         * or tail is an exception. in either case, if tail is  a dir or
132         * a link to a dir, we don't want to update tail's children.
133         */
134        if ( retval == NORMALCASE);
135        else if ( type == S_IFLNK || type == S_IFDIR)
136                sprintf( exc_dir, "%s/", wholename);
137
138        return( retval);
139}
140
141List_element **
142lookup( name, hashval, ht) char *name; unsigned long hashval; Table *ht; {
143        List_element **ptop;
144
145        /* *ptop is the first element of an alphabetically-sorted
146         * linked-list of relative pathnames.
147         * for speed, we access each list-elt via the beginning of its string;
148         * the elt's first 4 bytes hold the addr of the next list-elt.
149         */
150        for ( ptop = &ht->table[ hashval >> ht->shift];
151             *ptop;
152              ptop = PNEXT( *ptop)) {
153                switch ( SIGN( strcmp( *ptop, name))) {
154                case -1: continue;
155                case 1:  break;
156                case 0:  return( ptop);
157                }
158                break;
159        }
160        return( NULL);
161}
162
163list2hashtable( p) Table *p; {
164        List_element *names, *wordp;
165        unsigned int tbl_size;
166
167        if ( ! p->shift) {
168                sprintf(errmsg,"parser error: non-empty table has 0 count\n");
169                do_panic();
170        }
171        /* convert names list to hash-table,
172         * using count of list-elements to allocate table:
173         * size is power of 2, s.t. table is at most 2/3 full.
174         * XXX: store hashing shift in caller's copy of list-count.
175         *      parser stored the list-count as a negative #,
176         *      to tell justshow() how to dump.
177         */
178        p->shift *= -1;
179        p->shift = 31 - log2( p->shift + ( p->shift >> 1));
180        names    = (List_element *) p->table;
181        tbl_size = (unsigned) 0x80000000 >> p->shift - 1;
182        if ( ! tbl_size)
183                sprintf( errmsg, "(list2hashtable) null hash table request\n");
184
185        p->table = (List_element **) calloc( tbl_size, sizeof( List_element*));
186        if ( ! p->table) {
187                sprintf( errmsg, "(list2hashtable) calloc( %d, %d) failed.\n",
188                         tbl_size, sizeof( List_element *));
189                do_panic();
190        }
191        do {
192            wordp = names;
193            names = NEXT( names);
194            store( wordp, p);
195        } while ( names);
196        return;
197}
198/* get integer-part of log(base2) of the table-length,
199 * by doing a binary-search for the high-order bit:
200 */
201unsigned short mask[] =
202{       0xffff,0xfffe,0xfffc,0xfff8,0xfff0,0xffe0,0xffc0,0xff80,
203        0xff00,0xfe00,0xfc00,0xf800,0xf000,0xe000,0xc000,0x8000};
204log2( len) unsigned short len; {
205        int i, n = 0;
206        for ( i = 16; i >>= 1; ) if ( len & mask[ n + i]) n += i;
207        return( n);
208}
209
210store( list_elt, p) List_element *list_elt; Table *p; {
211        List_element **collisionp, **nextp;
212
213        collisionp = &p->table[ hash( TEXT( list_elt)) >> p->shift];
214        if ( ! *collisionp) {
215            *collisionp = list_elt;     /* store list-elt in hash table */
216            NEXT( list_elt) = NULL;
217            return;
218        }
219        /* keep list of colliding elt's in alphabetical order.
220         * if list_elt is already present in the table,
221         * ensure that FORCE_LINK overrides DONT_TRACK.
222         */
223        for ( nextp = collisionp; *nextp; nextp = PNEXT( *nextp)) {
224            switch( SIGN( strcmp( *nextp, list_elt))) {
225            case -1: continue;
226            case  1: break;                             /* switch */
227            case  0: FLAG( *nextp) |= FLAG( list_elt);
228                     return; /* infequent case last */
229            }
230            break;      /* loop */
231        }
232        NEXT( list_elt) = *nextp;
233        *nextp = list_elt;
234        return;
235}
236List_element *
237add_list_elt( str, flag, top) char *str; char flag; List_element **top; {
238        List_element *p;
239
240        /* this alloc will waste some char's,
241         * since List_element{}'s header is padded to end on a
242         * fullword-boundary.
243         */
244        p = (List_element *) malloc( sizeof( List_element) + strlen( str));
245        if ( !p) {
246                sprintf( errmsg, "(add_list_element) malloc( %d) failed.\n",
247                         sizeof( List_element) + strlen( str));
248                do_panic();
249        }
250        p = (List_element *) p->first_char;
251
252        if ( top) {
253                NEXT( p) = *top;
254                *top = p;
255        }
256        else NEXT( p) = NULL;
257        FLAG( p)   = flag;
258        strcpy( p, str);
259        return( p);
260}
261#define RATIO ((unsigned)(0.6125423371 * 0x80000000))
262/* this is a multiplicative hash code. for an explanation of RATIO,
263 * see T. A. Standish, Data Structure Techniques, p161-3, or
264 * Knuth, Art of Comp. Prog., v3, p512.
265 * the shifting makes a 17-bit key from several 6-bit bytes.
266 * ( for alphabetic ASCII, the top 2 bits of each byte are redundant. )
267 * the numbers 4, 5 & 12 are carefully chosen, from trial & error.
268 * this hash code has the following advantages over mod-prime hash:
269 * collides slightly less frequently, especially when table is overfull;
270 * doesn't need prime-sized table, so table is easier to build.
271 */
272unsigned long
273hash( p) char *p; {
274        unsigned long key = 0;
275        short s = 4;
276        for ( ; *p ; s += 5) key ^= ( *p++ & 077) << s % 12;
277        return( key * RATIO);
278}
279char *
280next_def_except() {
281        static char *g_except[] = DEF_EXCEPT; /* default GLOBAL exceptions */
282        static int next = 0;
283
284        if ( next < sizeof( g_except) / 4)
285                return ( g_except[ next++]);
286        else return( NULL);
287}
288
289/*
290**      if there is a match, then return(1) else return(0)
291*/
292match( p, fname) char *p; char *fname;
293{
294        /* all our regexp's begin with ^,
295         * because re_conv() makes them.
296         */
297        if ( *p != '^') return( 0);
298        if ( re_comp( p)) {
299                sprintf(errmsg, "%s bad regular expression\n", re_comp( p));
300                do_panic();
301        }
302        switch( re_exec( fname)) {
303        case 0: return( 0);             /* most frequent case */
304        case 1: return( 1);
305        case -1: sprintf( errmsg, "%s bad regexp\n", p);
306                 do_panic();
307        }
308        sprintf( errmsg, "bad value from re_exec\n");
309        do_panic();
310        return( -1);
311}
312
313file_pat( ptr)
314char *ptr;
315{
316        return( (index(ptr,'*') ||
317                 index(ptr,'[') ||
318                 index(ptr,']') ||
319                 index(ptr,'?')));
320}
321
322#define FASTEQ( a, b) (*(a) == *(b) && a[1] == b[1])
323
324duplicate( word, entnum) char *word; int entnum; {
325        List_element *re;
326
327        for ( re = entries[ entnum].patterns; re; re = NEXT( re)) {
328                    if ( FASTEQ( word, TEXT( re)) &&
329                       ! strcmp( word, TEXT( re)))
330                        return( 1);
331        }
332        /* check the global exceptions list
333         */
334        if ( entnum) return( duplicate( word, 0));
335        return( 0);
336}
337
338/*
339**      convert shell type regular expressions to ex style form
340*/
341char *re_conv(from)
342char *from;
343{
344        static char tmp[LINELEN];
345        char *to;
346
347        to = tmp;
348        *to++ = '^';
349        while(*from) {
350                switch (*from) {
351                case '.':
352                        *to++ = '\\';
353                        *to++ = '.';
354                        from++;
355                        break;
356
357                case '*':
358                        *to++ = '.';
359                        *to++ = '*';
360                        from++;
361                        break;
362
363                case '?':
364                        *to++ = '.';
365                        from++;
366                        break;
367                       
368                default:
369                        *to++ = *from++;
370                        break;
371                }
372        }
373
374        *to++ = '$';
375        *to++ = '\0';
376        return(tmp);
377}
Note: See TracBrowser for help on using the repository browser.