1 //
2 // aegis - project change supervisor
3 // Copyright (C) 1997, 1999, 2002-2006, 2008, 2011, 2012 Peter Miller
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 3 of the License, or (at
8 // your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 // General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program. If not, see <http://www.gnu.org/licenses/>.
17 //
18 
19 #include <common/ac/errno.h>
20 #include <common/ac/sys/types.h>
21 #include <common/ac/sys/stat.h>
22 
23 #include <common/str_list.h>
24 #include <common/trace.h>
25 #include <libaegis/glue.h>
26 #include <libaegis/os.h>
27 #include <libaegis/sub.h>
28 
29 #include <aefind/cmdline.h>
30 #include <aefind/descend.h>
31 #include <aefind/tree.h>
32 
33 
34 string_ty *
stat_stack(string_ty * path,struct stat * st)35 stat_stack(string_ty *path, struct stat *st)
36 {
37     size_t          j;
38     string_ty       *dir;
39     string_ty       *resolved_path;
40     sub_context_ty  *scp;
41 
42     for (j = 0; ; ++j)
43     {
44         dir = stack_nth(j);
45         if (!dir)
46             break;
47         resolved_path = os_path_cat(dir, path);
48 
49 #ifdef S_IFLNK
50         if (!glue_lstat(resolved_path->str_text, st))
51             return resolved_path;
52 
53         if (errno != ENOENT)
54         {
55             int             errno_old;
56 
57             errno_old = errno;
58             scp = sub_context_new();
59             sub_errno_setx(scp, errno_old);
60             sub_var_set_string(scp, "File_Name", resolved_path);
61             fatal_intl(scp, i18n("lstat $filename: $errno"));
62             // NOTREACHED
63         }
64 #else
65         if (!glue_stat(resolved_path->str_text, st))
66             return resolved_path;
67 
68         if (errno != ENOENT)
69         {
70             int             errno_old;
71 
72             errno_old = errno;
73             scp = sub_context_new();
74             sub_errno_setx(scp, errno_old);
75             sub_var_set_string(scp, "File_Name", resolved_path);
76             fatal_intl(scp, i18n("stat $filename: $errno"));
77             // NOTREACHED
78         }
79 #endif
80         str_free(resolved_path);
81     }
82     scp = sub_context_new();
83     sub_errno_setx(scp, ENOENT);
84     sub_var_set_string(scp, "File_Name", path);
85     fatal_intl(scp, i18n("stat $filename: $errno"));
86     // NOTREACHED
87     return 0;
88 }
89 
90 
91 static void
readdir_stack(string_ty * path,string_list_ty * result)92 readdir_stack(string_ty *path, string_list_ty *result)
93 {
94     size_t          j;
95     string_ty       *s;
96     string_ty       *dir;
97 
98     result->clear();
99     for (j = 0; ; ++j)
100     {
101         dir = stack_nth(j);
102         if (!dir)
103             break;
104         s = os_path_cat(dir, path);
105         if (read_whole_dir__wla(s->str_text, result))
106         {
107             sub_context_ty  *scp;
108             int             errno_old;
109 
110             errno_old = errno;
111             if (errno_old == ENOENT)
112             {
113                 str_free(s);
114                 continue;
115             }
116             scp = sub_context_new();
117             sub_errno_setx(scp, errno_old);
118             sub_var_set_string(scp, "File_Name", path);
119             fatal_intl(scp, i18n("read $filename: $errno"));
120             // NOTREACHED
121         }
122         str_free(s);
123     }
124 }
125 
126 
127 void
descend(string_ty * path,int resolve,descend_callback_ty callback,void * arg)128 descend(string_ty *path, int resolve, descend_callback_ty callback, void *arg)
129 {
130     struct stat     st;
131     size_t          j;
132     string_ty       *s;
133     string_ty       *resolved_path;
134 
135     trace(("descend(path = %08lX, callback = %08lX, arg = %08lX)\n{\n",
136         (long)path, (long)callback, (long)arg));
137     trace_string(path->str_text);
138     resolved_path = stat_stack(path, &st);
139     if ((st.st_mode & S_IFMT) == S_IFDIR)
140     {
141         trace(("mark\n"));
142         callback
143         (
144             arg,
145             descend_message_dir_before,
146             path,
147             (resolve ? resolved_path : path),
148             resolved_path,
149             &st
150         );
151         trace(("mark\n"));
152         string_list_ty wl;
153         readdir_stack(path, &wl);
154         for (j = 0; j < wl.nstrings; ++j)
155         {
156             s = os_path_cat(path, wl.string[j]);
157             if (!stack_eliminate(s))
158                 descend(s, resolve, callback, arg);
159             str_free(s);
160         }
161         trace(("mark\n"));
162         callback
163         (
164             arg,
165             descend_message_dir_after,
166             path,
167             (resolve ? resolved_path : path),
168             resolved_path,
169             &st
170         );
171     }
172     else
173     {
174         trace(("mark\n"));
175         callback
176         (
177             arg,
178             descend_message_file,
179             path,
180             (resolve ? resolved_path : path),
181             resolved_path,
182             &st
183         );
184     }
185     str_free(resolved_path);
186     trace(("}\n"));
187 }
188 
189 
190 // vim: set ts=8 sw=4 et :
191