rpm
4.5
|
00001 /*@-boundsread@*/ 00002 /*@-sysunrecog -noeffectuncon -nullpass -sizeoftype -unrecog -usereleased @*/ 00003 /*@-compdef -compmempass -dependenttrans -retalias @*/ 00004 /*- 00005 * Copyright (c) 1990, 1993, 1994 00006 * The Regents of the University of California. All rights reserved. 00007 * 00008 * Redistribution and use in source and binary forms, with or without 00009 * modification, are permitted provided that the following conditions 00010 * are met: 00011 * 1. Redistributions of source code must retain the above copyright 00012 * notice, this list of conditions and the following disclaimer. 00013 * 2. Redistributions in binary form must reproduce the above copyright 00014 * notice, this list of conditions and the following disclaimer in the 00015 * documentation and/or other materials provided with the distribution. 00016 * 4. Neither the name of the University nor the names of its contributors 00017 * may be used to endorse or promote products derived from this software 00018 * without specific prior written permission. 00019 * 00020 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 00021 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 00022 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 00023 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 00024 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 00025 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 00026 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 00027 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 00028 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 00029 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 00030 * SUCH DAMAGE. 00031 */ 00032 00033 #if defined(LIBC_SCCS) && !defined(lint) 00034 static char sccsid[] = "@(#)fts.c 8.6 (Berkeley) 8/14/94"; 00035 #endif /* LIBC_SCCS and not lint */ 00036 00037 #if defined(_LIBC) 00038 #include <sys/param.h> 00039 #include <include/sys/stat.h> 00040 #include <fcntl.h> 00041 #include <dirent.h> 00042 #include <errno.h> 00043 #include <fts.h> 00044 #include <stdlib.h> 00045 #include <string.h> 00046 #include <unistd.h> 00047 #else 00048 #if defined(hpux) || defined(__hpux) 00049 # define _INCLUDE_POSIX_SOURCE 00050 # define __errno_location() (&errno) 00051 # define dirfd(dirp) -1 00052 # define stat64 stat 00053 # define _STAT_VER 0 00054 # define __fxstat64(_stat_ver, _fd, _sbp) fstat((_fd), (_sbp)) 00055 #endif 00056 #if defined(sun) 00057 # define __errno_location() (&errno) 00058 # define dirfd(dirp) -1 00059 # define _STAT_VER 0 00060 # define __fxstat64(_stat_ver, _fd, _sbp) fstat((_fd), (_sbp)) 00061 #endif 00062 #if defined(__APPLE__) 00063 # define __errno_location() (__error()) 00064 # define stat64 stat 00065 # define _STAT_VER 0 00066 # define __fxstat64(_stat_ver, _fd, _sbp) fstat((_fd), (_sbp)) 00067 #endif 00068 #if defined(__FreeBSD__) 00069 # define __errno_location() (&errno) 00070 # define stat64 stat 00071 # define __fxstat64(_stat_ver, _fd, _sbp) fstat((_fd), (_sbp)) 00072 #endif 00073 #include "system.h" 00074 #include "fts.h" 00075 #include "rpmio.h" 00076 #include "rpmurl.h" 00077 # define __set_errno(val) (*__errno_location ()) = (val) 00078 # define __open open 00079 # define __close close 00080 # define __fchdir fchdir 00081 #endif 00082 00083 #if !defined(USHRT_MAX) 00084 #define USHRT_MAX 65535 00085 #endif 00086 00087 /* Largest alignment size needed, minus one. 00088 Usually long double is the worst case. */ 00089 #ifndef ALIGNBYTES 00090 #if defined __GNUC__ && __GNUC__ >= 2 00091 # define alignof(TYPE) __alignof__ (TYPE) 00092 #else 00093 # define alignof(TYPE) \ 00094 ((int) &((struct { char dummy1; TYPE dummy2; } *) 0)->dummy2) 00095 #endif 00096 #define ALIGNBYTES (alignof(long double) - 1) 00097 #endif 00098 /* Align P to that size. */ 00099 #ifndef ALIGN 00100 #define ALIGN(p) (((unsigned long int) (p) + ALIGNBYTES) & ~ALIGNBYTES) 00101 #endif 00102 00103 /*@unchecked@*/ 00104 static int _fts_debug = 0; 00105 00106 /*@only@*/ /*@null@*/ 00107 static FTSENT * fts_alloc(FTS * sp, const char * name, int namelen) 00108 /*@*/; 00109 /*@null@*/ 00110 static FTSENT * fts_build(FTS * sp, int type) 00111 /*@globals fileSystem, internalState @*/ 00112 /*@modifies *sp, fileSystem, internalState @*/; 00113 static void fts_lfree(/*@only@*/ FTSENT * head) 00114 /*@modifies head @*/; 00115 static void fts_load(FTS * sp, FTSENT * p) 00116 /*@modifies *sp, *p @*/; 00117 static size_t fts_maxarglen(char * const * argv) 00118 /*@*/; 00119 static void fts_padjust(FTS * sp, FTSENT * head) 00120 /*@modifies *sp, *head @*/; 00121 static int fts_palloc(FTS * sp, size_t more) 00122 /*@modifies *sp @*/; 00123 static FTSENT * fts_sort(FTS * sp, /*@returned@*/ FTSENT * head, int nitems) 00124 /*@modifies *sp @*/; 00125 static u_short fts_stat(FTS * sp, FTSENT * p, int follow) 00126 /*@modifies *p @*/; 00127 static int fts_safe_changedir(FTS * sp, FTSENT * p, int fd, 00128 const char * path) 00129 /*@globals fileSystem, internalState @*/ 00130 /*@modifies fileSystem, internalState @*/; 00131 00132 #define ISDOT(a) (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2]))) 00133 00134 #define CLR(opt) (sp->fts_options &= ~(opt)) 00135 #define ISSET(opt) (sp->fts_options & (opt)) 00136 #define SET(opt) (sp->fts_options |= (opt)) 00137 00138 #define FCHDIR(sp, fd) (!ISSET(FTS_NOCHDIR) && __fchdir(fd)) 00139 00140 /* fts_build flags */ 00141 #define BCHILD 1 /* fts_children */ 00142 #define BNAMES 2 /* fts_children, names only */ 00143 #define BREAD 3 /* fts_read */ 00144 00145 FTS * 00146 Fts_open(char * const * argv, int options, 00147 int (*compar) (const FTSENT **, const FTSENT **)) 00148 { 00149 register FTS *sp; 00150 register FTSENT *p, *root; 00151 register int nitems; 00152 FTSENT *parent = NULL; 00153 FTSENT *tmp = NULL; 00154 size_t len; 00155 00156 /*@-formattype -modfilesys@*/ 00157 if (_fts_debug) 00158 fprintf(stderr, "*** Fts_open(%p, 0x%x, %p)\n", argv, options, compar); 00159 /*@=formattype =modfilesys@*/ 00160 00161 /* Options check. */ 00162 if (options & ~FTS_OPTIONMASK) { 00163 /*@-boundswrite@*/ 00164 __set_errno (EINVAL); 00165 /*@=boundswrite@*/ 00166 return (NULL); 00167 } 00168 00169 /* Allocate/initialize the stream */ 00170 if ((sp = malloc((u_int)sizeof(*sp))) == NULL) 00171 return (NULL); 00172 memset(sp, 0, sizeof(*sp)); 00173 sp->fts_compar = (int (*) (const void *, const void *)) compar; 00174 sp->fts_opendir = Opendir; 00175 sp->fts_readdir = Readdir; 00176 sp->fts_closedir = Closedir; 00177 sp->fts_stat = Stat; 00178 sp->fts_lstat = Lstat; 00179 sp->fts_options = options; 00180 00181 /* Logical walks turn on NOCHDIR; symbolic links are too hard. */ 00182 if (ISSET(FTS_LOGICAL)) 00183 SET(FTS_NOCHDIR); 00184 00185 /* 00186 * Start out with 1K of path space, and enough, in any case, 00187 * to hold the user's paths. 00188 */ 00189 #ifndef MAXPATHLEN 00190 #define MAXPATHLEN 1024 00191 #endif 00192 len = fts_maxarglen(argv); 00193 if (len < MAXPATHLEN) 00194 len = MAXPATHLEN; 00195 if (fts_palloc(sp, len)) 00196 goto mem1; 00197 00198 /* Allocate/initialize root's parent. */ 00199 /*@-branchstate@*/ 00200 if (*argv != NULL) { 00201 if ((parent = fts_alloc(sp, "", 0)) == NULL) 00202 goto mem2; 00203 parent->fts_level = FTS_ROOTPARENTLEVEL; 00204 } 00205 /*@=branchstate@*/ 00206 00207 /* Allocate/initialize root(s). */ 00208 for (root = NULL, nitems = 0; *argv != NULL; ++argv, ++nitems) { 00209 /* Don't allow zero-length paths. */ 00210 if ((len = strlen(*argv)) == 0) { 00211 /*@-boundswrite@*/ 00212 __set_errno (ENOENT); 00213 /*@=boundswrite@*/ 00214 goto mem3; 00215 } 00216 00217 /* Use fchdir(2) speedup only if local DASDI. */ 00218 switch (urlIsURL(*argv)) { 00219 case URL_IS_DASH: 00220 case URL_IS_HKP: 00221 /*@-boundswrite@*/ 00222 __set_errno (ENOENT); 00223 /*@=boundswrite@*/ 00224 goto mem3; 00225 /*@notreached@*/ /*@switchbreak@*/ break; 00226 case URL_IS_HTTPS: 00227 case URL_IS_HTTP: 00228 case URL_IS_FTP: 00229 SET(FTS_NOCHDIR); 00230 /*@switchbreak@*/ break; 00231 case URL_IS_UNKNOWN: 00232 case URL_IS_PATH: 00233 /*@switchbreak@*/ break; 00234 } 00235 00236 p = fts_alloc(sp, *argv, len); 00237 if (p == NULL) 00238 goto mem3; 00239 p->fts_level = FTS_ROOTLEVEL; 00240 p->fts_parent = parent; 00241 p->fts_accpath = p->fts_name; 00242 p->fts_info = fts_stat(sp, p, ISSET(FTS_COMFOLLOW)); 00243 00244 /* Command-line "." and ".." are real directories. */ 00245 if (p->fts_info == FTS_DOT) 00246 p->fts_info = FTS_D; 00247 00248 /* 00249 * If comparison routine supplied, traverse in sorted 00250 * order; otherwise traverse in the order specified. 00251 */ 00252 if (compar) { 00253 p->fts_link = root; 00254 root = p; 00255 } else { 00256 p->fts_link = NULL; 00257 if (root == NULL) 00258 tmp = root = p; 00259 else { 00260 if (tmp != NULL) /* XXX can't happen */ 00261 tmp->fts_link = p; 00262 tmp = p; 00263 } 00264 } 00265 } 00266 /*@-branchstate@*/ 00267 if (compar && nitems > 1) 00268 root = fts_sort(sp, root, nitems); 00269 /*@=branchstate@*/ 00270 00271 /* 00272 * Allocate a dummy pointer and make fts_read think that we've just 00273 * finished the node before the root(s); set p->fts_info to FTS_INIT 00274 * so that everything about the "current" node is ignored. 00275 */ 00276 if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL) 00277 goto mem3; 00278 sp->fts_cur->fts_link = root; 00279 sp->fts_cur->fts_info = FTS_INIT; 00280 00281 /* 00282 * If using chdir(2), grab a file descriptor pointing to dot to ensure 00283 * that we can get back here; this could be avoided for some paths, 00284 * but almost certainly not worth the effort. Slashes, symbolic links, 00285 * and ".." are all fairly nasty problems. Note, if we can't get the 00286 * descriptor we run anyway, just more slowly. 00287 */ 00288 if (!ISSET(FTS_NOCHDIR) 00289 && (sp->fts_rfd = __open(".", O_RDONLY, 0)) < 0) 00290 SET(FTS_NOCHDIR); 00291 00292 return (sp); 00293 00294 mem3: fts_lfree(root); 00295 free(parent); 00296 mem2: free(sp->fts_path); 00297 mem1: free(sp); 00298 return (NULL); 00299 } 00300 00301 static void 00302 fts_load(FTS * sp, FTSENT * p) 00303 { 00304 register int len; 00305 register char *cp; 00306 00307 /* 00308 * Load the stream structure for the next traversal. Since we don't 00309 * actually enter the directory until after the preorder visit, set 00310 * the fts_accpath field specially so the chdir gets done to the right 00311 * place and the user can access the first node. From fts_open it's 00312 * known that the path will fit. 00313 */ 00314 len = p->fts_pathlen = p->fts_namelen; 00315 /*@-boundswrite@*/ 00316 memmove(sp->fts_path, p->fts_name, len + 1); 00317 if ((cp = strrchr(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) { 00318 len = strlen(++cp); 00319 memmove(p->fts_name, cp, len + 1); 00320 p->fts_namelen = len; 00321 } 00322 p->fts_accpath = p->fts_path = sp->fts_path; 00323 sp->fts_dev = p->fts_dev; 00324 /*@=boundswrite@*/ 00325 } 00326 00327 int 00328 Fts_close(FTS * sp) 00329 { 00330 register FTSENT *freep, *p; 00331 int saved_errno; 00332 00333 if (sp == NULL) 00334 return 0; 00335 00336 /* 00337 * This still works if we haven't read anything -- the dummy structure 00338 * points to the root list, so we step through to the end of the root 00339 * list which has a valid parent pointer. 00340 */ 00341 /*@-branchstate@*/ 00342 if (sp->fts_cur) { 00343 for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) { 00344 freep = p; 00345 p = p->fts_link != NULL ? p->fts_link : p->fts_parent; 00346 free(freep); 00347 } 00348 free(p); 00349 } 00350 /*@=branchstate@*/ 00351 00352 /* Free up child linked list, sort array, path buffer. */ 00353 if (sp->fts_child) 00354 fts_lfree(sp->fts_child); 00355 if (sp->fts_array) 00356 free(sp->fts_array); 00357 free(sp->fts_path); 00358 00359 /* Return to original directory, save errno if necessary. */ 00360 if (!ISSET(FTS_NOCHDIR)) { 00361 saved_errno = __fchdir(sp->fts_rfd) ? errno : 0; 00362 (void)__close(sp->fts_rfd); 00363 00364 /* Set errno and return. */ 00365 if (saved_errno != 0) { 00366 /* Free up the stream pointer. */ 00367 free(sp); 00368 /*@-boundswrite@*/ 00369 __set_errno (saved_errno); 00370 /*@=boundswrite@*/ 00371 return (-1); 00372 } 00373 } 00374 00375 /* Free up the stream pointer. */ 00376 free(sp); 00377 return (0); 00378 } 00379 00380 /* 00381 * Special case of "/" at the end of the path so that slashes aren't 00382 * appended which would cause paths to be written as "....//foo". 00383 */ 00384 #define NAPPEND(p) \ 00385 (p->fts_path[p->fts_pathlen - 1] == '/' \ 00386 ? p->fts_pathlen - 1 : p->fts_pathlen) 00387 00388 FTSENT * 00389 Fts_read(FTS * sp) 00390 { 00391 register FTSENT *p; 00392 register FTSENT *tmp; 00393 register int instr; 00394 register char *t; 00395 int saved_errno; 00396 00397 /* If finished or unrecoverable error, return NULL. */ 00398 if (sp == NULL || sp->fts_cur == NULL || ISSET(FTS_STOP)) 00399 return (NULL); 00400 00401 /* Set current node pointer. */ 00402 p = sp->fts_cur; 00403 00404 /* Save and zero out user instructions. */ 00405 instr = p->fts_instr; 00406 p->fts_instr = FTS_NOINSTR; 00407 00408 /* Any type of file may be re-visited; re-stat and re-turn. */ 00409 if (instr == FTS_AGAIN) { 00410 p->fts_info = fts_stat(sp, p, 0); 00411 return (p); 00412 } 00413 00414 /* 00415 * Following a symlink -- SLNONE test allows application to see 00416 * SLNONE and recover. If indirecting through a symlink, have 00417 * keep a pointer to current location. If unable to get that 00418 * pointer, follow fails. 00419 */ 00420 if (instr == FTS_FOLLOW && 00421 (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) { 00422 p->fts_info = fts_stat(sp, p, 1); 00423 if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) { 00424 if ((p->fts_symfd = __open(".", O_RDONLY, 0)) < 0) { 00425 p->fts_errno = errno; 00426 p->fts_info = FTS_ERR; 00427 } else 00428 p->fts_flags |= FTS_SYMFOLLOW; 00429 } 00430 return (p); 00431 } 00432 00433 /* Directory in pre-order. */ 00434 if (p->fts_info == FTS_D) { 00435 /* If skipped or crossed mount point, do post-order visit. */ 00436 if (instr == FTS_SKIP || 00437 (ISSET(FTS_XDEV) && p->fts_dev != sp->fts_dev)) { 00438 if (p->fts_flags & FTS_SYMFOLLOW) 00439 (void)__close(p->fts_symfd); 00440 if (sp->fts_child) { 00441 fts_lfree(sp->fts_child); 00442 sp->fts_child = NULL; 00443 } 00444 p->fts_info = FTS_DP; 00445 return (p); 00446 } 00447 00448 /* Rebuild if only read the names and now traversing. */ 00449 if (sp->fts_child != NULL && ISSET(FTS_NAMEONLY)) { 00450 CLR(FTS_NAMEONLY); 00451 fts_lfree(sp->fts_child); 00452 sp->fts_child = NULL; 00453 } 00454 00455 /* 00456 * Cd to the subdirectory. 00457 * 00458 * If have already read and now fail to chdir, whack the list 00459 * to make the names come out right, and set the parent errno 00460 * so the application will eventually get an error condition. 00461 * Set the FTS_DONTCHDIR flag so that when we logically change 00462 * directories back to the parent we don't do a chdir. 00463 * 00464 * If haven't read do so. If the read fails, fts_build sets 00465 * FTS_STOP or the fts_info field of the node. 00466 */ 00467 if (sp->fts_child != NULL) { 00468 if (fts_safe_changedir(sp, p, -1, p->fts_accpath)) { 00469 p->fts_errno = errno; 00470 p->fts_flags |= FTS_DONTCHDIR; 00471 for (p = sp->fts_child; p != NULL; 00472 p = p->fts_link) 00473 p->fts_accpath = 00474 p->fts_parent->fts_accpath; 00475 } 00476 } else if ((sp->fts_child = fts_build(sp, BREAD)) == NULL) { 00477 if (ISSET(FTS_STOP)) 00478 return (NULL); 00479 return (p); 00480 } 00481 p = sp->fts_child; 00482 sp->fts_child = NULL; 00483 sp->fts_cur = p; 00484 goto name; 00485 } 00486 00487 /* Move to the next node on this level. */ 00488 /*@-boundswrite@*/ 00489 next: tmp = p; 00490 if ((p = p->fts_link) != NULL) { 00491 sp->fts_cur = p; 00492 free(tmp); 00493 00494 /* 00495 * If reached the top, return to the original directory (or 00496 * the root of the tree), and load the paths for the next root. 00497 */ 00498 if (p->fts_level == FTS_ROOTLEVEL) { 00499 if (FCHDIR(sp, sp->fts_rfd)) { 00500 SET(FTS_STOP); 00501 return (NULL); 00502 } 00503 fts_load(sp, p); 00504 return (p); 00505 } 00506 00507 /* 00508 * User may have called fts_set on the node. If skipped, 00509 * ignore. If followed, get a file descriptor so we can 00510 * get back if necessary. 00511 */ 00512 if (p->fts_instr == FTS_SKIP) 00513 goto next; 00514 /*@-branchstate@*/ 00515 if (p->fts_instr == FTS_FOLLOW) { 00516 p->fts_info = fts_stat(sp, p, 1); 00517 if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) { 00518 if ((p->fts_symfd = 00519 __open(".", O_RDONLY, 0)) < 0) { 00520 p->fts_errno = errno; 00521 p->fts_info = FTS_ERR; 00522 } else 00523 p->fts_flags |= FTS_SYMFOLLOW; 00524 } 00525 p->fts_instr = FTS_NOINSTR; 00526 } 00527 /*@=branchstate@*/ 00528 00529 name: t = sp->fts_path + NAPPEND(p->fts_parent); 00530 *t++ = '/'; 00531 memmove(t, p->fts_name, p->fts_namelen + 1); 00532 return (p); 00533 } 00534 00535 /* Move up to the parent node. */ 00536 p = tmp->fts_parent; 00537 sp->fts_cur = p; 00538 free(tmp); 00539 00540 if (p->fts_level == FTS_ROOTPARENTLEVEL) { 00541 /* 00542 * Done; free everything up and set errno to 0 so the user 00543 * can distinguish between error and EOF. 00544 */ 00545 free(p); 00546 __set_errno (0); 00547 return (sp->fts_cur = NULL); 00548 } 00549 00550 /* NUL terminate the pathname. */ 00551 sp->fts_path[p->fts_pathlen] = '\0'; 00552 /*@=boundswrite@*/ 00553 00554 /* 00555 * Return to the parent directory. If at a root node or came through 00556 * a symlink, go back through the file descriptor. Otherwise, cd up 00557 * one directory. 00558 */ 00559 if (p->fts_level == FTS_ROOTLEVEL) { 00560 if (FCHDIR(sp, sp->fts_rfd)) { 00561 SET(FTS_STOP); 00562 return (NULL); 00563 } 00564 } else if (p->fts_flags & FTS_SYMFOLLOW) { 00565 if (FCHDIR(sp, p->fts_symfd)) { 00566 saved_errno = errno; 00567 (void)__close(p->fts_symfd); 00568 /*@-boundswrite@*/ 00569 __set_errno (saved_errno); 00570 /*@=boundswrite@*/ 00571 SET(FTS_STOP); 00572 return (NULL); 00573 } 00574 (void)__close(p->fts_symfd); 00575 } else if (!(p->fts_flags & FTS_DONTCHDIR) && 00576 fts_safe_changedir(sp, p->fts_parent, -1, "..")) { 00577 SET(FTS_STOP); 00578 return (NULL); 00579 } 00580 p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP; 00581 return (p); 00582 } 00583 00584 /* 00585 * Fts_set takes the stream as an argument although it's not used in this 00586 * implementation; it would be necessary if anyone wanted to add global 00587 * semantics to fts using fts_set. An error return is allowed for similar 00588 * reasons. 00589 */ 00590 int 00591 Fts_set(/*@unused@*/ FTS * sp, FTSENT * p, int instr) 00592 { 00593 /*@-modfilesys@*/ 00594 if (_fts_debug) 00595 fprintf(stderr, "*** Fts_set(%p, %p, 0x%x)\n", sp, p, instr); 00596 /*@=modfilesys@*/ 00597 00598 if (instr != 0 && instr != FTS_AGAIN && instr != FTS_FOLLOW && 00599 instr != FTS_NOINSTR && instr != FTS_SKIP) { 00600 /*@-boundswrite@*/ 00601 __set_errno (EINVAL); 00602 /*@=boundswrite@*/ 00603 return (1); 00604 } 00605 p->fts_instr = instr; 00606 return (0); 00607 } 00608 00609 FTSENT * 00610 Fts_children(FTS * sp, int instr) 00611 { 00612 register FTSENT *p; 00613 int fd; 00614 00615 /*@-modfilesys@*/ 00616 if (_fts_debug) 00617 fprintf(stderr, "*** Fts_children(%p, 0x%x)\n", sp, instr); 00618 /*@=modfilesys@*/ 00619 00620 if (instr != 0 && instr != FTS_NAMEONLY) { 00621 /*@-boundswrite@*/ 00622 __set_errno (EINVAL); 00623 /*@=boundswrite@*/ 00624 return (NULL); 00625 } 00626 00627 /* Set current node pointer. */ 00628 p = sp->fts_cur; 00629 00630 /* 00631 * Errno set to 0 so user can distinguish empty directory from 00632 * an error. 00633 */ 00634 /*@-boundswrite@*/ 00635 __set_errno (0); 00636 /*@=boundswrite@*/ 00637 00638 /* Fatal errors stop here. */ 00639 if (ISSET(FTS_STOP)) 00640 return (NULL); 00641 00642 /* Return logical hierarchy of user's arguments. */ 00643 if (p->fts_info == FTS_INIT) 00644 return (p->fts_link); 00645 00646 /* 00647 * If not a directory being visited in pre-order, stop here. Could 00648 * allow FTS_DNR, assuming the user has fixed the problem, but the 00649 * same effect is available with FTS_AGAIN. 00650 */ 00651 if (p->fts_info != FTS_D /* && p->fts_info != FTS_DNR */) 00652 return (NULL); 00653 00654 /* Free up any previous child list. */ 00655 if (sp->fts_child != NULL) 00656 fts_lfree(sp->fts_child); 00657 00658 if (instr == FTS_NAMEONLY) { 00659 SET(FTS_NAMEONLY); 00660 instr = BNAMES; 00661 } else 00662 instr = BCHILD; 00663 00664 /* 00665 * If using chdir on a relative path and called BEFORE fts_read does 00666 * its chdir to the root of a traversal, we can lose -- we need to 00667 * chdir into the subdirectory, and we don't know where the current 00668 * directory is, so we can't get back so that the upcoming chdir by 00669 * fts_read will work. 00670 */ 00671 if (p->fts_level != FTS_ROOTLEVEL || p->fts_accpath[0] == '/' || 00672 ISSET(FTS_NOCHDIR)) 00673 return (sp->fts_child = fts_build(sp, instr)); 00674 00675 if ((fd = __open(".", O_RDONLY, 0)) < 0) 00676 return (NULL); 00677 sp->fts_child = fts_build(sp, instr); 00678 if (__fchdir(fd)) 00679 return (NULL); 00680 (void)__close(fd); 00681 return (sp->fts_child); 00682 } 00683 00684 /* 00685 * This is the tricky part -- do not casually change *anything* in here. The 00686 * idea is to build the linked list of entries that are used by fts_children 00687 * and fts_read. There are lots of special cases. 00688 * 00689 * The real slowdown in walking the tree is the stat calls. If FTS_NOSTAT is 00690 * set and it's a physical walk (so that symbolic links can't be directories), 00691 * we can do things quickly. First, if it's a 4.4BSD file system, the type 00692 * of the file is in the directory entry. Otherwise, we assume that the number 00693 * of subdirectories in a node is equal to the number of links to the parent. 00694 * The former skips all stat calls. The latter skips stat calls in any leaf 00695 * directories and for any files after the subdirectories in the directory have 00696 * been found, cutting the stat calls by about 2/3. 00697 */ 00698 static FTSENT * 00699 fts_build(FTS * sp, int type) 00700 { 00701 register struct dirent *dp; 00702 register FTSENT *p, *head; 00703 register int nitems; 00704 FTSENT *cur, *tail; 00705 DIR *dirp; 00706 void *oldaddr; 00707 int cderrno, descend, len, level, nlinks, saved_errno, 00708 nostat, doadjust; 00709 size_t maxlen; 00710 char *cp; 00711 00712 /* Set current node pointer. */ 00713 cur = sp->fts_cur; 00714 00715 /* 00716 * Open the directory for reading. If this fails, we're done. 00717 * If being called from fts_read, set the fts_info field. 00718 */ 00719 #if defined FTS_WHITEOUT && 0 00720 if (ISSET(FTS_WHITEOUT)) 00721 oflag = DTF_NODUP|DTF_REWIND; 00722 else 00723 oflag = DTF_HIDEW|DTF_NODUP|DTF_REWIND; 00724 #else 00725 # define __opendir2(path, flag) (*sp->fts_opendir) (path) 00726 #endif 00727 if ((dirp = __opendir2(cur->fts_accpath, oflag)) == NULL) { 00728 if (type == BREAD) { 00729 cur->fts_info = FTS_DNR; 00730 cur->fts_errno = errno; 00731 } 00732 return (NULL); 00733 } 00734 00735 /* 00736 * Nlinks is the number of possible entries of type directory in the 00737 * directory if we're cheating on stat calls, 0 if we're not doing 00738 * any stat calls at all, -1 if we're doing stats on everything. 00739 */ 00740 if (type == BNAMES) { 00741 nlinks = 0; 00742 /* Be quiet about nostat, GCC. */ 00743 nostat = 0; 00744 } else if (ISSET(FTS_NOSTAT) && ISSET(FTS_PHYSICAL)) { 00745 nlinks = cur->fts_nlink - (ISSET(FTS_SEEDOT) ? 0 : 2); 00746 nostat = 1; 00747 } else { 00748 nlinks = -1; 00749 nostat = 0; 00750 } 00751 00752 #ifdef notdef 00753 (void)printf("nlinks == %d (cur: %d)\n", nlinks, cur->fts_nlink); 00754 (void)printf("NOSTAT %d PHYSICAL %d SEEDOT %d\n", 00755 ISSET(FTS_NOSTAT), ISSET(FTS_PHYSICAL), ISSET(FTS_SEEDOT)); 00756 #endif 00757 /* 00758 * If we're going to need to stat anything or we want to descend 00759 * and stay in the directory, chdir. If this fails we keep going, 00760 * but set a flag so we don't chdir after the post-order visit. 00761 * We won't be able to stat anything, but we can still return the 00762 * names themselves. Note, that since fts_read won't be able to 00763 * chdir into the directory, it will have to return different path 00764 * names than before, i.e. "a/b" instead of "b". Since the node 00765 * has already been visited in pre-order, have to wait until the 00766 * post-order visit to return the error. There is a special case 00767 * here, if there was nothing to stat then it's not an error to 00768 * not be able to stat. This is all fairly nasty. If a program 00769 * needed sorted entries or stat information, they had better be 00770 * checking FTS_NS on the returned nodes. 00771 */ 00772 cderrno = 0; 00773 if (nlinks || type == BREAD) { 00774 if (fts_safe_changedir(sp, cur, dirfd(dirp), NULL)) { 00775 if (nlinks && type == BREAD) 00776 cur->fts_errno = errno; 00777 cur->fts_flags |= FTS_DONTCHDIR; 00778 descend = 0; 00779 cderrno = errno; 00780 (void) (*sp->fts_closedir) (dirp); 00781 dirp = NULL; 00782 } else 00783 descend = 1; 00784 } else 00785 descend = 0; 00786 00787 /* 00788 * Figure out the max file name length that can be stored in the 00789 * current path -- the inner loop allocates more path as necessary. 00790 * We really wouldn't have to do the maxlen calculations here, we 00791 * could do them in fts_read before returning the path, but it's a 00792 * lot easier here since the length is part of the dirent structure. 00793 * 00794 * If not changing directories set a pointer so that can just append 00795 * each new name into the path. 00796 */ 00797 len = NAPPEND(cur); 00798 if (ISSET(FTS_NOCHDIR)) { 00799 cp = sp->fts_path + len; 00800 /*@-boundswrite@*/ 00801 *cp++ = '/'; 00802 /*@=boundswrite@*/ 00803 } else { 00804 /* GCC, you're too verbose. */ 00805 cp = NULL; 00806 } 00807 len++; 00808 maxlen = sp->fts_pathlen - len; 00809 00810 level = cur->fts_level + 1; 00811 00812 /* Read the directory, attaching each entry to the `link' pointer. */ 00813 doadjust = 0; 00814 for (head = tail = NULL, nitems = 0; 00815 dirp && (dp = (*sp->fts_readdir) (dirp));) 00816 { 00817 if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name)) 00818 continue; 00819 00820 if ((p = fts_alloc(sp, dp->d_name, (int)_D_EXACT_NAMLEN (dp))) == NULL) 00821 goto mem1; 00822 if (_D_EXACT_NAMLEN (dp) >= maxlen) {/* include space for NUL */ 00823 oldaddr = sp->fts_path; 00824 if (fts_palloc(sp, _D_EXACT_NAMLEN (dp) + len + 1)) { 00825 /* 00826 * No more memory for path or structures. Save 00827 * errno, free up the current structure and the 00828 * structures already allocated. 00829 */ 00830 mem1: saved_errno = errno; 00831 if (p) 00832 free(p); 00833 fts_lfree(head); 00834 (void) (*sp->fts_closedir) (dirp); 00835 cur->fts_info = FTS_ERR; 00836 SET(FTS_STOP); 00837 __set_errno (saved_errno); 00838 return (NULL); 00839 } 00840 /* Did realloc() change the pointer? */ 00841 if (oldaddr != sp->fts_path) { 00842 doadjust = 1; 00843 if (ISSET(FTS_NOCHDIR)) 00844 cp = sp->fts_path + len; 00845 } 00846 maxlen = sp->fts_pathlen - len; 00847 } 00848 00849 if (len + _D_EXACT_NAMLEN (dp) >= USHRT_MAX) { 00850 /* 00851 * In an FTSENT, fts_pathlen is a u_short so it is 00852 * possible to wraparound here. If we do, free up 00853 * the current structure and the structures already 00854 * allocated, then error out with ENAMETOOLONG. 00855 */ 00856 free(p); 00857 fts_lfree(head); 00858 (void) (*sp->fts_closedir) (dirp); 00859 cur->fts_info = FTS_ERR; 00860 SET(FTS_STOP); 00861 __set_errno (ENAMETOOLONG); 00862 return (NULL); 00863 } 00864 p->fts_level = level; 00865 p->fts_parent = sp->fts_cur; 00866 p->fts_pathlen = len + _D_EXACT_NAMLEN (dp); 00867 00868 #if defined FTS_WHITEOUT && 0 00869 if (dp->d_type == DT_WHT) 00870 p->fts_flags |= FTS_ISW; 00871 #endif 00872 00873 #if 0 00874 /* 00875 * Unreachable code. cderrno is only ever set to a nonnull 00876 * value if dirp is closed at the same time. But then we 00877 * cannot enter this loop. 00878 */ 00879 if (cderrno) { 00880 if (nlinks) { 00881 p->fts_info = FTS_NS; 00882 p->fts_errno = cderrno; 00883 } else 00884 p->fts_info = FTS_NSOK; 00885 p->fts_accpath = cur->fts_accpath; 00886 } else 00887 #endif 00888 if (nlinks == 0 00889 #if defined DT_DIR && defined _DIRENT_HAVE_D_TYPE 00890 || (nostat && 00891 dp->d_type != DT_DIR && dp->d_type != DT_UNKNOWN) 00892 #endif 00893 ) { 00894 p->fts_accpath = 00895 ISSET(FTS_NOCHDIR) ? p->fts_path : p->fts_name; 00896 p->fts_info = FTS_NSOK; 00897 } else { 00898 /* Build a file name for fts_stat to stat. */ 00899 if (ISSET(FTS_NOCHDIR)) { 00900 p->fts_accpath = p->fts_path; 00901 memmove(cp, p->fts_name, p->fts_namelen + 1); 00902 } else 00903 p->fts_accpath = p->fts_name; 00904 /* Stat it. */ 00905 p->fts_info = fts_stat(sp, p, 0); 00906 00907 /* Decrement link count if applicable. */ 00908 if (nlinks > 0 && (p->fts_info == FTS_D || 00909 p->fts_info == FTS_DC || p->fts_info == FTS_DOT)) 00910 --nlinks; 00911 } 00912 00913 /* We walk in directory order so "ls -f" doesn't get upset. */ 00914 p->fts_link = NULL; 00915 if (head == NULL) 00916 head = tail = p; 00917 else { 00918 tail->fts_link = p; 00919 tail = p; 00920 } 00921 ++nitems; 00922 } 00923 if (dirp) 00924 (void) (*sp->fts_closedir) (dirp); 00925 00926 /* 00927 * If realloc() changed the address of the path, adjust the 00928 * addresses for the rest of the tree and the dir list. 00929 */ 00930 if (doadjust) 00931 fts_padjust(sp, head); 00932 00933 /* 00934 * If not changing directories, reset the path back to original 00935 * state. 00936 */ 00937 if (ISSET(FTS_NOCHDIR)) { 00938 if (len == sp->fts_pathlen || nitems == 0) 00939 --cp; 00940 /*@-boundswrite@*/ 00941 if (cp != NULL) /* XXX can't happen */ 00942 *cp = '\0'; 00943 /*@=boundswrite@*/ 00944 } 00945 00946 /* 00947 * If descended after called from fts_children or after called from 00948 * fts_read and nothing found, get back. At the root level we use 00949 * the saved fd; if one of fts_open()'s arguments is a relative path 00950 * to an empty directory, we wind up here with no other way back. If 00951 * can't get back, we're done. 00952 */ 00953 if (descend && (type == BCHILD || !nitems) && 00954 (cur->fts_level == FTS_ROOTLEVEL ? 00955 FCHDIR(sp, sp->fts_rfd) : 00956 fts_safe_changedir(sp, cur->fts_parent, -1, ".."))) { 00957 cur->fts_info = FTS_ERR; 00958 SET(FTS_STOP); 00959 fts_lfree(head); 00960 return (NULL); 00961 } 00962 00963 /* If didn't find anything, return NULL. */ 00964 if (!nitems) { 00965 if (type == BREAD) 00966 cur->fts_info = FTS_DP; 00967 fts_lfree(head); 00968 return (NULL); 00969 } 00970 00971 /* Sort the entries. */ 00972 if (sp->fts_compar && nitems > 1) 00973 head = fts_sort(sp, head, nitems); 00974 return (head); 00975 } 00976 00977 static u_short 00978 fts_stat(FTS * sp, FTSENT * p, int follow) 00979 { 00980 register FTSENT *t; 00981 register dev_t dev; 00982 register ino_t ino; 00983 struct stat *sbp, sb; 00984 int saved_errno; 00985 00986 /* If user needs stat info, stat buffer already allocated. */ 00987 sbp = ISSET(FTS_NOSTAT) ? &sb : p->fts_statp; 00988 00989 #if defined FTS_WHITEOUT && 0 00990 /* check for whiteout */ 00991 if (p->fts_flags & FTS_ISW) { 00992 if (sbp != &sb) { 00993 memset(sbp, '\0', sizeof (*sbp)); 00994 sbp->st_mode = S_IFWHT; 00995 } 00996 return (FTS_W); 00997 } 00998 #endif 00999 01000 /* 01001 * If doing a logical walk, or application requested FTS_FOLLOW, do 01002 * a stat(2). If that fails, check for a non-existent symlink. If 01003 * fail, set the errno from the stat call. 01004 */ 01005 if (ISSET(FTS_LOGICAL) || follow) { 01006 if ((*sp->fts_stat) (p->fts_accpath, sbp)) { 01007 saved_errno = errno; 01008 if (!(*sp->fts_lstat) (p->fts_accpath, sbp)) { 01009 /*@-boundswrite@*/ 01010 __set_errno (0); 01011 /*@=boundswrite@*/ 01012 return (FTS_SLNONE); 01013 } 01014 p->fts_errno = saved_errno; 01015 goto err; 01016 } 01017 } else if ((*sp->fts_lstat) (p->fts_accpath, sbp)) { 01018 p->fts_errno = errno; 01019 /*@-boundswrite@*/ 01020 err: memset(sbp, 0, sizeof(*sbp)); 01021 /*@=boundswrite@*/ 01022 return (FTS_NS); 01023 } 01024 01025 if (S_ISDIR(sbp->st_mode)) { 01026 /* 01027 * Set the device/inode. Used to find cycles and check for 01028 * crossing mount points. Also remember the link count, used 01029 * in fts_build to limit the number of stat calls. It is 01030 * understood that these fields are only referenced if fts_info 01031 * is set to FTS_D. 01032 */ 01033 dev = p->fts_dev = sbp->st_dev; 01034 ino = p->fts_ino = sbp->st_ino; 01035 p->fts_nlink = sbp->st_nlink; 01036 01037 if (ISDOT(p->fts_name)) 01038 return (FTS_DOT); 01039 01040 /* 01041 * Cycle detection is done by brute force when the directory 01042 * is first encountered. If the tree gets deep enough or the 01043 * number of symbolic links to directories is high enough, 01044 * something faster might be worthwhile. 01045 */ 01046 for (t = p->fts_parent; 01047 t->fts_level >= FTS_ROOTLEVEL; t = t->fts_parent) 01048 if (ino == t->fts_ino && dev == t->fts_dev) { 01049 p->fts_cycle = t; 01050 return (FTS_DC); 01051 } 01052 return (FTS_D); 01053 } 01054 if (S_ISLNK(sbp->st_mode)) 01055 return (FTS_SL); 01056 if (S_ISREG(sbp->st_mode)) 01057 return (FTS_F); 01058 return (FTS_DEFAULT); 01059 } 01060 01061 static FTSENT * 01062 fts_sort(FTS * sp, FTSENT * head, int nitems) 01063 { 01064 register FTSENT **ap, *p; 01065 01066 /* 01067 * Construct an array of pointers to the structures and call qsort(3). 01068 * Reassemble the array in the order returned by qsort. If unable to 01069 * sort for memory reasons, return the directory entries in their 01070 * current order. Allocate enough space for the current needs plus 01071 * 40 so don't realloc one entry at a time. 01072 */ 01073 if (nitems > sp->fts_nitems) { 01074 struct _ftsent **a; 01075 01076 sp->fts_nitems = nitems + 40; 01077 if ((a = realloc(sp->fts_array, 01078 (size_t)(sp->fts_nitems * sizeof(*sp->fts_array)))) == NULL) 01079 { 01080 free(sp->fts_array); 01081 sp->fts_array = NULL; 01082 sp->fts_nitems = 0; 01083 return (head); 01084 } 01085 sp->fts_array = a; 01086 } 01087 /*@-boundswrite@*/ 01088 for (ap = sp->fts_array, p = head; p != NULL; p = p->fts_link) 01089 *ap++ = p; 01090 qsort((void *)sp->fts_array, nitems, sizeof(*sp->fts_array), 01091 sp->fts_compar); 01092 for (head = *(ap = sp->fts_array); --nitems; ++ap) 01093 ap[0]->fts_link = ap[1]; 01094 ap[0]->fts_link = NULL; 01095 /*@=boundswrite@*/ 01096 return (head); 01097 } 01098 01099 static FTSENT * 01100 fts_alloc(FTS * sp, const char * name, int namelen) 01101 { 01102 register FTSENT *p; 01103 size_t len; 01104 01105 /* 01106 * The file name is a variable length array and no stat structure is 01107 * necessary if the user has set the nostat bit. Allocate the FTSENT 01108 * structure, the file name and the stat structure in one chunk, but 01109 * be careful that the stat structure is reasonably aligned. Since the 01110 * fts_name field is declared to be of size 1, the fts_name pointer is 01111 * namelen + 2 before the first possible address of the stat structure. 01112 */ 01113 len = sizeof(*p) + namelen; 01114 if (!ISSET(FTS_NOSTAT)) 01115 len += sizeof(*p->fts_statp) + ALIGNBYTES; 01116 if ((p = malloc(len)) == NULL) 01117 return (NULL); 01118 01119 /* Copy the name and guarantee NUL termination. */ 01120 /*@-boundswrite@*/ 01121 memmove(p->fts_name, name, namelen); 01122 p->fts_name[namelen] = '\0'; 01123 /*@=boundswrite@*/ 01124 01125 if (!ISSET(FTS_NOSTAT)) 01126 p->fts_statp = (struct stat *)ALIGN(p->fts_name + namelen + 2); 01127 p->fts_namelen = namelen; 01128 p->fts_path = sp->fts_path; 01129 p->fts_errno = 0; 01130 p->fts_flags = 0; 01131 p->fts_instr = FTS_NOINSTR; 01132 p->fts_number = 0; 01133 p->fts_pointer = NULL; 01134 return (p); 01135 } 01136 01137 static void 01138 fts_lfree(FTSENT * head) 01139 { 01140 register FTSENT *p; 01141 01142 /* Free a linked list of structures. */ 01143 /*@-branchstate@*/ 01144 while ((p = head)) { 01145 head = head->fts_link; 01146 free(p); 01147 } 01148 /*@=branchstate@*/ 01149 } 01150 01151 /* 01152 * Allow essentially unlimited paths; find, rm, ls should all work on any tree. 01153 * Most systems will allow creation of paths much longer than MAXPATHLEN, even 01154 * though the kernel won't resolve them. Add the size (not just what's needed) 01155 * plus 256 bytes so don't realloc the path 2 bytes at a time. 01156 */ 01157 static int 01158 fts_palloc(FTS * sp, size_t more) 01159 { 01160 char *p; 01161 01162 sp->fts_pathlen += more + 256; 01163 /* 01164 * Check for possible wraparound. In an FTS, fts_pathlen is 01165 * a signed int but in an FTSENT it is an unsigned short. 01166 * We limit fts_pathlen to USHRT_MAX to be safe in both cases. 01167 */ 01168 if (sp->fts_pathlen < 0 || sp->fts_pathlen >= USHRT_MAX) { 01169 if (sp->fts_path) 01170 free(sp->fts_path); 01171 sp->fts_path = NULL; 01172 /*@-boundswrite@*/ 01173 __set_errno (ENAMETOOLONG); 01174 /*@=boundswrite@*/ 01175 return (1); 01176 } 01177 p = realloc(sp->fts_path, sp->fts_pathlen); 01178 if (p == NULL) { 01179 free(sp->fts_path); 01180 sp->fts_path = NULL; 01181 return 1; 01182 } 01183 sp->fts_path = p; 01184 return 0; 01185 } 01186 01187 /* 01188 * When the path is realloc'd, have to fix all of the pointers in structures 01189 * already returned. 01190 */ 01191 static void 01192 fts_padjust(FTS * sp, FTSENT * head) 01193 { 01194 FTSENT *p; 01195 char *addr = sp->fts_path; 01196 01197 #define ADJUST(p) do { \ 01198 if ((p)->fts_accpath != (p)->fts_name) { \ 01199 (p)->fts_accpath = \ 01200 (char *)addr + ((p)->fts_accpath - (p)->fts_path); \ 01201 } \ 01202 (p)->fts_path = addr; \ 01203 } while (0) 01204 /* Adjust the current set of children. */ 01205 for (p = sp->fts_child; p != NULL; p = p->fts_link) 01206 ADJUST(p); 01207 01208 /* Adjust the rest of the tree, including the current level. */ 01209 for (p = head; p->fts_level >= FTS_ROOTLEVEL;) { 01210 ADJUST(p); 01211 p = p->fts_link ? p->fts_link : p->fts_parent; 01212 } 01213 } 01214 01215 static size_t 01216 fts_maxarglen(char * const * argv) 01217 { 01218 size_t len, max; 01219 01220 for (max = 0; *argv; ++argv) 01221 if ((len = strlen(*argv)) > max) 01222 max = len; 01223 return (max + 1); 01224 } 01225 01226 /* 01227 * Change to dir specified by fd or p->fts_accpath without getting 01228 * tricked by someone changing the world out from underneath us. 01229 * Assumes p->fts_dev and p->fts_ino are filled in. 01230 */ 01231 static int 01232 fts_safe_changedir(FTS * sp, FTSENT * p, int fd, const char * path) 01233 { 01234 int ret, oerrno, newfd; 01235 struct stat64 sb; 01236 01237 newfd = fd; 01238 if (ISSET(FTS_NOCHDIR)) 01239 return (0); 01240 if (fd < 0 && (newfd = __open(path, O_RDONLY, 0)) < 0) 01241 return (-1); 01242 if (__fxstat64(_STAT_VER, newfd, &sb)) { 01243 ret = -1; 01244 goto bail; 01245 } 01246 if (p->fts_dev != sb.st_dev || p->fts_ino != sb.st_ino) { 01247 /*@-boundswrite@*/ 01248 __set_errno (ENOENT); /* disinformation */ 01249 /*@=boundswrite@*/ 01250 ret = -1; 01251 goto bail; 01252 } 01253 ret = __fchdir(newfd); 01254 bail: 01255 oerrno = errno; 01256 if (fd < 0) 01257 (void)__close(newfd); 01258 /*@-boundswrite@*/ 01259 __set_errno (oerrno); 01260 /*@=boundswrite@*/ 01261 return (ret); 01262 } 01263 /*@=compdef =compmempass =dependenttrans =retalias @*/ 01264 /*@=sysunrecog =noeffectuncon =nullpass =sizeoftype =unrecog =usereleased @*/ 01265 /*@=boundsread@*/