[24423] trunk/base/src/pextlib1.0/fs-traverse.c

source_changes at macosforge.org source_changes at macosforge.org
Tue Apr 24 00:37:46 PDT 2007


Revision: 24423
          http://trac.macosforge.org/projects/macports/changeset/24423
Author:   eridius at macports.org
Date:     2007-04-24 00:37:46 -0700 (Tue, 24 Apr 2007)

Log Message:
-----------
Switch to using the fts(3) family of functions instead of this opendir/readdir stuff.
Makes for much cleaner code, on 10.3 it will allow file deletion during traversal, and it has proper symbolic link behaviour.
Fixes #11839

Modified Paths:
--------------
    trunk/base/src/pextlib1.0/fs-traverse.c

Modified: trunk/base/src/pextlib1.0/fs-traverse.c
===================================================================
--- trunk/base/src/pextlib1.0/fs-traverse.c	2007-04-24 07:36:53 UTC (rev 24422)
+++ trunk/base/src/pextlib1.0/fs-traverse.c	2007-04-24 07:37:46 UTC (rev 24423)
@@ -49,9 +49,8 @@
 #include <sys/stat.h>
 #endif
 
-#if HAVE_DIRENT_H
-#include <dirent.h>
-#endif
+#include <fts.h>
+#include <errno.h>
 
 #if HAVE_LIMITS_H
 #include <limits.h>
@@ -59,7 +58,7 @@
 
 #include <tcl.h>
 
-static int do_traverse(Tcl_Interp *interp, int flags, char *target, char *varname, char *body);
+static int do_traverse(Tcl_Interp *interp, int flags, char * CONST *targets, char *varname, Tcl_Obj *body);
 
 #define F_DEPTH 0x1
 #define F_IGNORE_ERRORS 0x2
@@ -69,7 +68,7 @@
 FsTraverseCmd(ClientData clientData UNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
 {
     char *varname;
-    char *body;
+    Tcl_Obj *body;
     int flags = 0;
     int rval = TCL_OK;
     Tcl_Obj *listPtr;
@@ -107,91 +106,84 @@
     listPtr = *objv;
     ++objv, --objc;
     
-    body = Tcl_GetString(*objv);
+    body = *objv;
     
     if ((rval = Tcl_ListObjGetElements(interp, listPtr, &lobjc, &lobjv)) == TCL_OK) {
+        char **entries = calloc(objc, sizeof(char *));
+        char **iter = (char **)entries;
         while (lobjc) {
-            char *target = Tcl_GetString(*lobjv);
-            ++lobjv, --lobjc;
-            
-            if ((rval = do_traverse(interp, flags, target, varname, body)) == TCL_CONTINUE) {
-                rval = TCL_OK;
-                continue;
-            } else if (rval == TCL_BREAK) {
-                rval = TCL_OK;
-                break;
-            } else if (rval != TCL_OK) {
-                break;
-            }
+            *iter++ = Tcl_GetString(*lobjv);
+            --lobjc, ++lobjv;
         }
+        rval = do_traverse(interp, flags, entries, varname, body);
+        free(entries);
     }
     return rval;
 }
 
 static int
-do_traverse(Tcl_Interp *interp, int flags, char *target, char *varname, char *body)
+do_traverse(Tcl_Interp *interp, int flags, char * CONST *targets, char *varname, Tcl_Obj *body)
 {
-    DIR *dirp;
-    struct dirent *dp;
     int rval = TCL_OK;
-    struct stat sb;
+    FTS *root_fts;
+    FTSENT *ent;
     
-    /* No permission? */
-    if (lstat(target, &sb) != 0) {
-        if (flags & F_IGNORE_ERRORS) {
-            return TCL_OK;
-        } else {
-            Tcl_ResetResult(interp);
-            Tcl_AppendResult(interp, "no permission to access file/folder `", target, "'", NULL);
-            return TCL_ERROR;
-        }
-    }
+    root_fts = fts_open(targets, FTS_PHYSICAL | FTS_COMFOLLOW | FTS_NOCHDIR | FTS_XDEV, NULL);
     
-    /* Handle files now, or directories if !depth */
-    if (!(flags & F_DEPTH) || !(sb.st_mode & S_IFDIR)) {
-        Tcl_SetVar(interp, varname, target, 0);
-        if ((rval = Tcl_EvalEx(interp, body, -1, 0)) != TCL_OK) {
-            return rval;
-        }
-    }
-    
-    /* Handle directories */
-    if (sb.st_mode & S_IFDIR) {
-        if ((dirp = opendir(target)) == NULL) {
-            if (flags & F_IGNORE_ERRORS) {
-                return TCL_OK;
-            } else {
-                Tcl_ResetResult(interp);
-                Tcl_AppendResult(interp, "could not open directory `", target, "'", NULL);
-                return TCL_ERROR;
-            }
-        }
-        
-        while ((dp = readdir(dirp)) != NULL) {
-            char tmp_path[PATH_MAX];
-
-            if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
-                continue;
-            strcpy(tmp_path, target);
-            strcat(tmp_path, "/");
-            strcat(tmp_path, dp->d_name);
-
-            if ((rval = do_traverse(interp, flags, tmp_path, varname, body)) == TCL_CONTINUE) {
-                rval = TCL_OK;
-                continue;
-            } else if (rval != TCL_OK) {
+    while ((ent = fts_read(root_fts)) != NULL) {
+        switch (ent->fts_info) {
+            case FTS_D:  /* directory in pre-order */
+            case FTS_DP: /* directory in post-order*/
+            {
+                if (!(flags & F_DEPTH) != !(ent->fts_info == FTS_D)) {
+                    Tcl_SetVar(interp, varname, ent->fts_path, 0);
+                    if ((rval = Tcl_EvalObjEx(interp, body, 0)) == TCL_CONTINUE) {
+                        fts_set(root_fts, ent, FTS_SKIP);
+                    } else if (rval == TCL_BREAK) {
+                        fts_close(root_fts);
+                        return TCL_OK;
+                    } else if (rval != TCL_OK) {
+                        fts_close(root_fts);
+                        return rval;
+                    }
+                }
                 break;
             }
-        }
-        (void)closedir(dirp);
-        
-        /* Handle directory now if depth */
-        if (flags & F_DEPTH) {
-            Tcl_SetVar(interp, varname, target, 0);
-            if ((rval = Tcl_EvalEx(interp, body, -1, 0)) != TCL_OK) {
-                return rval;
+            case FTS_F:   /* regular file */
+            case FTS_SL:  /* symbolic link */
+            case FTS_SLNONE: /* symbolic link with non-existant target */
+            case FTS_DEFAULT: /* file type not otherwise handled (e.g., fifo) */
+            {
+                Tcl_SetVar(interp, varname, ent->fts_path, 0);
+                if ((rval = Tcl_EvalObjEx(interp, body, 0)) == TCL_CONTINUE) {
+                    fts_set(root_fts, ent, FTS_SKIP); /* probably useless on files/symlinks */
+                } else if (rval == TCL_BREAK) {
+                    fts_close(root_fts);
+                    return TCL_OK;
+                } else if (rval != TCL_OK) {
+                    fts_close(root_fts);
+                    return rval;
+                }
             }
+            case FTS_DC:  /* directory that causes a cycle */
+                break;    /* ignore it */
+            case FTS_DNR: /* directory that cannot be read */
+            case FTS_ERR: /* error return */
+            case FTS_NS:  /* file with no stat(2) information */
+            {
+                if (!(flags & F_IGNORE_ERRORS)) {
+                    Tcl_SetErrno(ent->fts_errno);
+                    Tcl_SetResult(interp, (char *)Tcl_PosixError(interp),  TCL_STATIC);
+                    fts_close(root_fts);
+                    return TCL_ERROR;
+                }
+            }
         }
     }
-    return rval;
+    /* check errno before calling fts_close in case it sets errno to 0 on success */
+    if (errno != 0 || (fts_close(root_fts) != 0 && !(flags & F_IGNORE_ERRORS))) {
+        Tcl_SetResult(interp, (char *)Tcl_PosixError(interp), TCL_STATIC);
+        return TCL_ERROR;
+    }
+    return TCL_OK;
 }

-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.macosforge.org/pipermail/macports-changes/attachments/20070424/0541d802/attachment.html


More information about the macports-changes mailing list