[233] | 1 | /* |
---|
| 2 | * $Source: /afs/dev.mit.edu/source/repository/athena/etc/track/stamp.c,v $ |
---|
[1252] | 3 | * $Header: /afs/dev.mit.edu/source/repository/athena/etc/track/stamp.c,v 4.8 1988-06-21 19:44:36 don Exp $ |
---|
[233] | 4 | * |
---|
| 5 | * $Log: not supported by cvs2svn $ |
---|
[1252] | 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 | * |
---|
[1244] | 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 | * |
---|
[1177] | 17 | * Revision 4.4 88/05/26 13:59:55 don |
---|
| 18 | * cosmetics in sort_entries(): fixed indentation, and added comments. |
---|
| 19 | * |
---|
[1144] | 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 | * |
---|
[1142] | 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 | * |
---|
[1138] | 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 | * |
---|
[1107] | 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 | * |
---|
[1090] | 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 | * |
---|
[1075] | 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 | * |
---|
[1053] | 57 | * Revision 2.4 88/01/29 18:24:02 don |
---|
| 58 | * bug fixes. also, now track can update the root. |
---|
| 59 | * |
---|
[1005] | 60 | * Revision 2.3 87/12/03 19:50:02 don |
---|
| 61 | * moved SIGN macro to track.h. |
---|
| 62 | * |
---|
[917] | 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 | * |
---|
[914] | 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 | * |
---|
[909] | 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 | * |
---|
[908] | 81 | * Revision 1.1 87/02/12 21:15:36 rfrench |
---|
| 82 | * Initial revision |
---|
| 83 | * |
---|
[233] | 84 | */ |
---|
| 85 | |
---|
| 86 | #ifndef lint |
---|
[1252] | 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 $"; |
---|
[233] | 88 | #endif lint |
---|
| 89 | |
---|
| 90 | #include "mit-copyright.h" |
---|
| 91 | |
---|
| 92 | #include "track.h" |
---|
| 93 | |
---|
[908] | 94 | /* XXX |
---|
[1075] | 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). |
---|
[908] | 103 | */ |
---|
[1075] | 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 | "", |
---|
[1090] | 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 */ |
---|
[1075] | 121 | "", /* S_IFLNK */ |
---|
| 122 | "", /* S_IFSOCK */ |
---|
| 123 | "" /* S_IFMT */ |
---|
| 124 | }; |
---|
| 125 | |
---|
[908] | 126 | char type_char[] = " cdbfls*89ABCDEF"; |
---|
| 127 | |
---|
[233] | 128 | /* |
---|
[908] | 129 | * Place a time-stamp line in the format: |
---|
[233] | 130 | * type file uid.gid.mode.time |
---|
| 131 | * or a suitable derivate thereof |
---|
| 132 | */ |
---|
| 133 | |
---|
[1075] | 134 | write_statline( path, c) |
---|
| 135 | char **path; struct currentness *c; |
---|
[233] | 136 | { |
---|
[1090] | 137 | char *format, *linebuf, *name; |
---|
| 138 | int same_name; |
---|
[1075] | 139 | unsigned int type; |
---|
| 140 | struct stat fromstat, *s; |
---|
| 141 | unsigned size, curr1 = 0, extra = 0; |
---|
[233] | 142 | |
---|
[908] | 143 | if ( cur_line >= maxlines) { |
---|
| 144 | maxlines += MAXLINES; |
---|
| 145 | size = maxlines * sizeof statfilebufs[0]; |
---|
[914] | 146 | statfilebufs = |
---|
| 147 | (Statline *) ( cur_line ? |
---|
| 148 | realloc( (char *) statfilebufs, size) |
---|
| 149 | : malloc( size)); |
---|
[908] | 150 | if ( ! statfilebufs) { |
---|
| 151 | sprintf( errmsg, "alloc failed: %d statfile lines\n", |
---|
| 152 | cur_line); |
---|
| 153 | do_panic(); |
---|
| 154 | } |
---|
| 155 | } |
---|
[1075] | 156 | /* |
---|
| 157 | * set up type-dependent currency data: |
---|
[1005] | 158 | */ |
---|
[233] | 159 | |
---|
[1075] | 160 | s = &c->sbuf; |
---|
[1005] | 161 | |
---|
[1075] | 162 | switch( type = TYPE( *s)) { |
---|
[908] | 163 | case S_IFREG: |
---|
[1075] | 164 | curr1 = c->cksum; |
---|
| 165 | extra = TIME( *s); |
---|
| 166 | break; |
---|
| 167 | case S_IFLNK: |
---|
| 168 | curr1 = (unsigned int) c->link; |
---|
| 169 | break; |
---|
[908] | 170 | case S_IFDIR: |
---|
[1075] | 171 | curr1 = 0; |
---|
[908] | 172 | break; |
---|
| 173 | case S_IFBLK: |
---|
[1244] | 174 | curr1 = RDEV( *s); |
---|
[1075] | 175 | break; |
---|
[908] | 176 | case S_IFCHR: |
---|
[1244] | 177 | curr1 = RDEV( *s); |
---|
[908] | 178 | break; |
---|
[1177] | 179 | case S_IFSOCK: |
---|
| 180 | sprintf( errmsg, "can't track socket %s.\n", path[ ROOT]); |
---|
| 181 | do_gripe(); |
---|
| 182 | return( type); |
---|
[908] | 183 | case S_IFMT: |
---|
| 184 | default: |
---|
[1177] | 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]); |
---|
[233] | 188 | do_panic(); |
---|
| 189 | } |
---|
[1075] | 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 | */ |
---|
[1090] | 204 | same_name = ! strcmp( path[ NAME], c->name); |
---|
[1075] | 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 | |
---|
[1090] | 211 | sprintf( linebuf, format, name, same_name ? '=' : '~' , curr1, |
---|
[1075] | 212 | UID( *s), GID( *s), MODE( *s), extra); |
---|
| 213 | |
---|
| 214 | cur_line++; |
---|
| 215 | |
---|
[908] | 216 | if ( verboseflag) |
---|
| 217 | fputs( linebuf, stderr); |
---|
[233] | 218 | |
---|
[1090] | 219 | if ( same_name); |
---|
| 220 | else if ( ! (*statf)( path[ ROOT], &fromstat)) |
---|
| 221 | type = TYPE( fromstat); |
---|
| 222 | else { |
---|
[1075] | 223 | sprintf( errmsg, "(write_statline) can't %s %s\n", |
---|
| 224 | statn, path[ ROOT]); |
---|
| 225 | do_panic(); |
---|
| 226 | } |
---|
[1090] | 227 | return( type); |
---|
[908] | 228 | } |
---|
| 229 | |
---|
[1090] | 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 | |
---|
[908] | 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 | */ |
---|
[914] | 258 | qsort( (char *) statfilebufs, cur_line, |
---|
| 259 | sizeof( statfilebufs[ 0]), strcmp); |
---|
[908] | 260 | |
---|
| 261 | for ( i = 0; i < cur_line; i++) { |
---|
| 262 | fputs( statfilebufs[ i].line, statfile); |
---|
[233] | 263 | } |
---|
[908] | 264 | } |
---|
[233] | 265 | |
---|
[908] | 266 | sort_entries() { |
---|
[1090] | 267 | char *tail; |
---|
| 268 | Table list; |
---|
| 269 | Entry *A, *C; |
---|
| 270 | int i, j; |
---|
[908] | 271 | |
---|
[1090] | 272 | list.table = NULL; |
---|
| 273 | list.shift = 0; |
---|
| 274 | |
---|
[908] | 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); |
---|
[1005] | 280 | |
---|
| 281 | /* for each entry's fromfile (call it A), |
---|
| 282 | * look for A's children amongst the subsequent entries, |
---|
[1090] | 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. |
---|
[1005] | 286 | */ |
---|
[1090] | 287 | for ( A = &entries[ i = 1 ]; i < entrycnt; A = &entries[ ++i]) { |
---|
| 288 | for ( C = &entries[ j = i+1]; j < entrycnt; C = &entries[ ++j]) { |
---|
[1144] | 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 | } |
---|
[1005] | 317 | } |
---|
[233] | 318 | } |
---|
| 319 | |
---|
| 320 | /* |
---|
[908] | 321 | * Decode a statfile line into its individual fields. |
---|
[1244] | 322 | * setup TYPE(), UID(), GID(), MODE(), TIME(), & RDEV() contents. |
---|
[233] | 323 | */ |
---|
| 324 | |
---|
[1075] | 325 | char * |
---|
| 326 | dec_statfile( line, c) |
---|
| 327 | char *line; struct currentness *c; |
---|
[233] | 328 | { |
---|
[1075] | 329 | struct stat *s; |
---|
[1090] | 330 | int *extra = 0; |
---|
| 331 | char *end, *format, *name, same_name, type; |
---|
[1075] | 332 | int dummy, *curr1 = &dummy, d = 0, u = 0, g = 0, m = 0; |
---|
[908] | 333 | |
---|
[914] | 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 | |
---|
[1090] | 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 | */ |
---|
[1075] | 350 | type = *line++; |
---|
| 351 | name = line; |
---|
| 352 | line = index( line, ' '); |
---|
[1090] | 353 | if ( ! line) { |
---|
[1138] | 354 | sprintf( errmsg, "garbled statfile: bad line =\n%s\n", line); |
---|
| 355 | sprintf( errmsg, "line has only one field\n"); |
---|
[1090] | 356 | do_panic(); |
---|
| 357 | } |
---|
[1075] | 358 | *line++ = '\0'; |
---|
[908] | 359 | |
---|
[1075] | 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 | } |
---|
[1090] | 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: |
---|
[1138] | 383 | sprintf( errmsg, "garbled statfile: bad line =\n%s\n", line); |
---|
| 384 | sprintf( errmsg, "bad equality flag = %c\n", same_name); |
---|
[1090] | 385 | do_panic(); |
---|
| 386 | } |
---|
| 387 | *c->link = '\0'; |
---|
[1075] | 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) { |
---|
[908] | 398 | case 'f': |
---|
[1075] | 399 | s->st_mode = S_IFREG; |
---|
[1090] | 400 | curr1 = ( int*)&c->cksum; |
---|
| 401 | extra = ( int*)&s->st_mtime; |
---|
[908] | 402 | break; |
---|
[914] | 403 | case 'l': /* more common than dir's */ |
---|
[1075] | 404 | s->st_mode = S_IFLNK; |
---|
[1090] | 405 | if ( end = index( line, '\n')) |
---|
| 406 | *end = '\0'; |
---|
| 407 | else { |
---|
[1138] | 408 | sprintf( errmsg, "garbled statfile: bad line =\n%s\n", |
---|
| 409 | line); |
---|
| 410 | sprintf( errmsg, "line doesn't end with a newline\n"); |
---|
[1090] | 411 | do_panic(); |
---|
| 412 | } |
---|
| 413 | if ( !*line) fake_link( fromroot, c->name, c); |
---|
| 414 | else strcpy( c->link, line); |
---|
[908] | 415 | break; |
---|
| 416 | case 'd': |
---|
[1075] | 417 | s->st_mode = S_IFDIR; |
---|
| 418 | curr1 = &dummy; |
---|
[908] | 419 | break; |
---|
| 420 | case 'b': |
---|
[1075] | 421 | s->st_mode = S_IFBLK; |
---|
| 422 | curr1 = &d; |
---|
[908] | 423 | break; |
---|
| 424 | case 'c': |
---|
[1075] | 425 | s->st_mode = S_IFCHR; |
---|
| 426 | curr1 = &d; |
---|
[908] | 427 | break; |
---|
| 428 | default: |
---|
[1138] | 429 | sprintf( errmsg, "garbled statfile: bad line =\n%s\n", line); |
---|
| 430 | sprintf( errmsg, "first char isn't a file-type [fldbc]\n"); |
---|
[908] | 431 | do_panic(); |
---|
[233] | 432 | } |
---|
[1075] | 433 | /* if we've already parsed the line, |
---|
| 434 | * as in S_IFLNK case, skip the sscanf call: |
---|
[1005] | 435 | */ |
---|
[1075] | 436 | if ( *(format = read_formats[ s->st_mode >> 13])) |
---|
[1090] | 437 | sscanf( line, format, curr1, &u, &g, &m, extra); |
---|
[1075] | 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); |
---|
[908] | 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 | |
---|
[1252] | 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 | |
---|
[909] | 457 | int cur_ent = 0; /* not global! index into entries[]. */ |
---|
| 458 | |
---|
[908] | 459 | init_next_match() { |
---|
[1252] | 460 | prev_ent = 0; |
---|
[908] | 461 | cur_ent = 1; |
---|
| 462 | } |
---|
[1005] | 463 | |
---|
[908] | 464 | int |
---|
[1005] | 465 | get_next_match( name) |
---|
| 466 | char *name; |
---|
| 467 | { |
---|
| 468 | char key[ LINELEN]; |
---|
[908] | 469 | |
---|
[1005] | 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 */ |
---|
[233] | 480 | } |
---|
[908] | 481 | |
---|
[1005] | 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 |
---|
[1075] | 493 | * /a/b/c < is greater than /a/b/a |
---|
[1005] | 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 | */ |
---|
[908] | 501 | int |
---|
[1005] | 502 | last_match( path, entnum) char *path; int entnum; { |
---|
| 503 | int i; |
---|
| 504 | char key[ LINELEN]; |
---|
[908] | 505 | |
---|
[1005] | 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 */ |
---|
[908] | 512 | } |
---|
[1005] | 513 | break; /* quit loop */ |
---|
[908] | 514 | } |
---|
[1005] | 515 | return( entnum); |
---|
[908] | 516 | } |
---|
| 517 | |
---|
[1005] | 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 | |
---|
[1075] | 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; |
---|
[1090] | 544 | Entry *e; |
---|
[1075] | 545 | static int xref_flag = 0; |
---|
[908] | 546 | |
---|
[1075] | 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. |
---|
[1005] | 551 | */ |
---|
| 552 | |
---|
[1075] | 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 | */ |
---|
[1090] | 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); |
---|
[1075] | 568 | } |
---|
[908] | 569 | } |
---|
[1075] | 570 | currency_buf.sbuf.st_mode = S_IFMT; /* kill short-term data. */ |
---|
| 571 | |
---|
[1252] | 572 | if ( prev_ent != entnum) { |
---|
| 573 | prev_ent = entnum; |
---|
[1075] | 574 | |
---|
| 575 | /* a subtler, longer search would set this flag less often. |
---|
| 576 | */ |
---|
[1090] | 577 | if ( ! writeflag) |
---|
| 578 | xref_flag = strncmp( entries[ entnum].cmpfile, |
---|
| 579 | entries[ entnum].tofile, |
---|
| 580 | strlen( entries[ entnum].tofile)); |
---|
[1075] | 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 | } |
---|
[1090] | 593 | if ( updated( entry_currency, NULL)) |
---|
[1075] | 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); |
---|
[908] | 610 | } |
---|
| 611 | |
---|
[1090] | 612 | /* these routines handle a stack of pointers into a character-string, |
---|
[908] | 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. |
---|
[1005] | 616 | * subsequent elements point to terminal substrings of the pathname. |
---|
| 617 | * a slash precedes each element. |
---|
[908] | 618 | */ |
---|
[1005] | 619 | #define COUNT(p) (*(int*)p[CNT]) |
---|
[908] | 620 | char ** |
---|
[1005] | 621 | initpath( name) char *name; { |
---|
[908] | 622 | char **p; |
---|
| 623 | |
---|
[1005] | 624 | /* for each stack-element, alloc a pointer |
---|
| 625 | * and 15 chars for a filename: |
---|
| 626 | */ |
---|
[908] | 627 | p = (char **) malloc( stackmax * sizeof NULL); |
---|
| 628 | p[ CNT] = (char *) malloc( stackmax * 15 + sizeof ((int) 0)); |
---|
[1005] | 629 | COUNT( p) = 1; |
---|
| 630 | p[ ROOT] = p[ CNT] + sizeof ((int) 1); |
---|
| 631 | strcpy( p[ ROOT], name); |
---|
| 632 | p[ NAME] = p[ ROOT] + strlen( name); |
---|
[908] | 633 | return( p); |
---|
| 634 | } |
---|
| 635 | int |
---|
| 636 | pushpath( p, name) char **p; char *name; { |
---|
[1005] | 637 | char *top; |
---|
| 638 | |
---|
[908] | 639 | if ( ! p) return( -1); |
---|
[1005] | 640 | if ( ++COUNT( p) >= stackmax) { |
---|
| 641 | sprintf( errmsg, "%s\n%s\n%s %d.\n", |
---|
[908] | 642 | "path stack overflow: directory too deep:", p[ ROOT], |
---|
| 643 | "use -S option, with value >", stackmax); |
---|
| 644 | do_panic(); |
---|
| 645 | } |
---|
[1053] | 646 | if ( *name) *p[ COUNT( p)]++ = '/'; |
---|
[1005] | 647 | top = p[ COUNT( p)]; |
---|
| 648 | strcpy( top, name); |
---|
| 649 | p[ COUNT( p) + 1] = top + strlen( top); |
---|
| 650 | return( COUNT( p)); |
---|
[908] | 651 | } |
---|
| 652 | poppath( p) char **p; { |
---|
[1053] | 653 | if ( ! p) return; |
---|
| 654 | else if ( 1 >= COUNT( p)) { |
---|
[1005] | 655 | sprintf(errmsg,"can't pop root from path-stack"); |
---|
| 656 | do_panic(); |
---|
| 657 | } |
---|
[1053] | 658 | else if ( *p[ COUNT( p)]) p[ COUNT( p)]--; |
---|
| 659 | /* non-null last elt; remove its initial slash */ |
---|
| 660 | *p[ COUNT( p)--] = '\0'; |
---|
[1005] | 661 | return; |
---|
[908] | 662 | } |
---|