1 /***********************************************************************
2 * *
3 * This software is part of the ast package *
4 * Copyright (c) 1985-2011 AT&T Intellectual Property *
5 * and is licensed under the *
6 * Eclipse Public License, Version 1.0 *
7 * by AT&T Intellectual Property *
8 * *
9 * A copy of the License is available at *
10 * http://www.eclipse.org/org/documents/epl-v10.html *
11 * (with md5 checksum b35adb5213ca9657e911e9befb180842) *
12 * *
13 * Information and Software Systems Research *
14 * AT&T Research *
15 * Florham Park NJ *
16 * *
17 * Glenn Fowler <glenn.s.fowler@gmail.com> *
18 * David Korn <dgkorn@gmail.com> *
19 * Phong Vo <phongvo@gmail.com> *
20 * *
21 ***********************************************************************/
22 //
23 // Glenn Fowler
24 // AT&T Research
25 //
26 // Return 1 if path exisis
27 // Maintains a cache to minimize stat(2) calls
28 // Path is modified in-place but restored on return
29 // Path components checked in pairs to cut stat()'s
30 // in half by checking ENOTDIR vs. ENOENT
31 // Case ignorance infection unavoidable here
32 //
33 #include "config_ast.h" // IWYU pragma: keep
34
35 #include <errno.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <sys/stat.h>
39
40 #include "ast.h"
41
42 // This tree caches name and modes of files and directories
43 typedef struct Tree_s {
44 // `next` points to next subdirectory of parent directory
45 struct Tree_s *next;
46 // If current node is a directory, `name` and `modes` of it's subdirectories and files are
47 // cached under this tree
48 struct Tree_s *tree;
49 // Modes can be PATH_READ, PATH_WRITE and PATH_EXECUTE
50 int mode;
51 char name[1];
52 } Tree_t;
53
pathexists(char * path,int mode)54 int pathexists(char *path, int mode) {
55 // Path is splitted by '/''s and each component is processed seaprately
56 // This variable points to current component of path that is being processed
57 char *current_path_component;
58
59 // Next path component
60 char *next_path_component;
61
62 // Points to next slash in path
63 char *next_slash;
64
65 // Points to parent directory
66 Tree_t *parent_tree;
67 Tree_t *current_tree;
68
69 int c;
70 int cc;
71 int x;
72 struct stat statbuf;
73
74 static Tree_t tree;
75
76 current_tree = &tree;
77 // If path starts with `/`, initialize next slash character after first character
78 next_slash = (c = *path) == '/' ? path + 1 : path;
79 while (c) {
80 parent_tree = current_tree;
81 // Try to search for next slash character
82 for (current_path_component = next_slash; *next_slash && *next_slash != '/'; next_slash++) {
83 ;
84 }
85
86 // Save value pointed by `next_slash` variable
87 c = *next_slash;
88 // and put a null character there to mark end of path, so `foo/bar/baz` becomes `foo`
89 *next_slash = 0;
90 for (current_tree = parent_tree->tree;
91 current_tree && strcmp(current_path_component, current_tree->name);
92 current_tree = current_tree->next) {
93 ;
94 }
95
96 // Path with name `current_path_name` does not exist in tree, time to create a new node.
97 if (!current_tree) {
98 current_tree = calloc(1, sizeof(Tree_t) + strlen(current_path_component));
99 if (!current_tree) {
100 *next_slash = c;
101 return 0;
102 }
103 strcpy(current_tree->name, current_path_component);
104 current_tree->next = parent_tree->tree;
105 parent_tree->tree = current_tree;
106
107 // If `c` is set, we are not at end of path
108 if (c) {
109 *next_slash = c;
110 for (current_path_component = next_path_component = next_slash + 1;
111 *next_path_component && *next_path_component != '/'; next_path_component++) {
112 ;
113 }
114 cc = *next_path_component;
115 *next_path_component = 0;
116 } else {
117 next_path_component = 0;
118 }
119 x = stat(path, &statbuf);
120
121 // Create a new node for subdirectory (or file)
122 if (next_path_component) {
123 Tree_t *new_tree;
124 next_slash = next_path_component;
125 c = cc;
126 if (!x || errno == ENOENT) current_tree->mode = PATH_READ | PATH_EXECUTE;
127 new_tree = calloc(1, sizeof(Tree_t) + strlen(current_path_component));
128 if (!new_tree) {
129 *next_slash = c;
130 return 0;
131 }
132 strcpy(new_tree->name, current_path_component);
133 new_tree->next = current_tree->tree;
134 current_tree->tree = new_tree;
135 // Set new node as current node
136 current_tree = new_tree;
137 }
138 if (x) {
139 *next_slash = c;
140 return 0;
141 }
142 if (statbuf.st_mode & (S_IRUSR | S_IRGRP | S_IROTH)) current_tree->mode |= PATH_READ;
143 if (statbuf.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) current_tree->mode |= PATH_WRITE;
144 if (statbuf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) current_tree->mode |= PATH_EXECUTE;
145 if (!S_ISDIR(statbuf.st_mode)) current_tree->mode |= PATH_REGULAR;
146 }
147
148 // Restore `/` character
149 *next_slash++ = c;
150 if (!current_tree->mode || (c && (current_tree->mode & PATH_REGULAR))) return 0;
151 }
152 mode &= (PATH_READ | PATH_WRITE | PATH_EXECUTE | PATH_REGULAR);
153 return (current_tree->mode & mode) == mode;
154 }
155