handle filesystems where lowercase(file)==file more gracefully
[swftools.git] / lib / as3 / files.c
1 /* files.c
2
3    Extension module for the rfxswf library.
4    Part of the swftools package.
5
6    Copyright (c) 2008 Matthias Kramm <kramm@quiss.org>
7  
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
21
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <stdarg.h>
25 #include <memory.h>
26 #include <errno.h>
27 #include "files.h"
28 #include "common.h"
29 #include "tokenizer.h"
30 #include "../os.h"
31
32 static int verbose = 0;
33 static void dbg(const char*format, ...)
34 {
35     char buf[1024];
36     int l;
37     va_list arglist;
38     if(!verbose)
39         return;
40     va_start(arglist, format);
41     vsnprintf(buf, sizeof(buf)-1, format, arglist);
42     va_end(arglist);
43     l = strlen(buf);
44     while(l && buf[l-1]=='\n') {
45         buf[l-1] = 0;
46         l--;
47     }
48     printf("(includefilehandler) ");
49     printf("%s\n", buf);
50     fflush(stdout);
51 }
52
53
54 int current_line=1;
55 int current_column=0;
56 char* current_filename=0;
57 char* current_filename_short=0;
58 char* current_filename_long=0;
59 include_dir_t* current_include_dirs=0;
60
61 #define MAX_INCLUDE_DEPTH 16
62
63 void*include_stack[MAX_INCLUDE_DEPTH];
64 int line_stack[MAX_INCLUDE_DEPTH];
65 int column_stack[MAX_INCLUDE_DEPTH];
66 char* filename_stack[MAX_INCLUDE_DEPTH];
67 char* shortfilename_stack[MAX_INCLUDE_DEPTH];
68 char* longfilename_stack[MAX_INCLUDE_DEPTH];
69 include_dir_t* includedir_stack[MAX_INCLUDE_DEPTH];
70 int include_stack_ptr = 0;
71
72 void add_include_dir(char*dir)
73 {
74     include_dir_t*d = malloc(sizeof(include_dir_t));
75     memset(d , 0, sizeof(include_dir_t));
76     d->path = strdup(dir);
77     d->next = current_include_dirs;
78     current_include_dirs = d;
79 }
80
81 void del_include_dirs(include_dir_t*d, include_dir_t*d2)
82 {
83     while(d && d!=d2) {
84         include_dir_t*next = d->next;
85         free(d->path);d->path=0;
86         d->next = 0;
87         free(d);
88         d = next;
89     }
90 }
91
92 char*get_path(const char*file)
93 {
94     char*path = strdup(file);
95     char*r1 = strrchr(path, '/');
96     char*r2 = strrchr(path, '\\');
97     if(r1<r2) {
98         *r2=0;
99         return path;
100     } else if(r1>r2) {
101         *r1=0;
102         return path;
103     } else {
104         return strdup(".");
105     }
106 }
107
108 char is_absolute(const char*filename) 
109 {
110     if(!filename || !filename[0])
111         return 0;
112     if(filename[0]=='/' || filename[0]=='\\')
113         return 1;
114     if(filename[1]==':' && filename[2]=='/')
115         return 1;
116     if(filename[1]==':' && filename[2]=='\\')
117         return 1;
118     return 0;
119 }
120
121 char* filename_to_lowercase(const char*name)
122 {
123     char*n = strdup(name);
124     //char*x1 = strrchr(name, '/');
125     //char*x2 = strrchr(name, '\\');
126     char*s = n;
127     //if(x1+1>s) s=x1+1;
128     //if(x2+1>s) s=x2+1;
129     while(*s) {
130         /* FIXME: what we probably should do here is use libc's tolower().
131            I don't really know yet, though, how Windows (or MacOS X) handles
132            lowercasing of Unicode filenames */
133         if(*s>='A' && *s<='Z')
134             *s += 'a'-'A';
135         s++;
136     }
137     return n;
138 }
139
140 char* normalize_path(const char*path)
141 {
142     char*n = 0, *d = 0;
143     if(!is_absolute(path)) {
144         char buf[512];
145         char*c = getcwd(buf,512);
146         int l = strlen(buf);
147         d = n = malloc(l+strlen(path)+10);
148         strcpy(n, buf);d += l;
149         if(!l || n[l-1]!=path_seperator) {
150             *d=path_seperator;d++;
151         }
152     } else {
153         d = n = strdup(path);
154     }
155     const char*s=path;
156     char init = 1;
157
158     while(*s) {
159         if(init && s[0] == '.' && (s[1]==path_seperator || s[1]=='\0')) {
160             if(!s[1]) break;
161             s+=2;
162             init=1;
163             continue;
164         }
165         if(init && s[0] == '.' && s[1] == '.' && (s[2] == path_seperator || s[2]=='\0')) {
166             // step one down
167             char*last = 0;
168             if(d<=n) 
169                 return 0;
170             *--d = 0;
171             if(!(last=strrchr(n, path_seperator))) {
172                 return 0;
173             }
174             d = last+1;
175             if(!s[2]) break;
176             s+=3;
177             init=1;
178             continue;
179         }
180
181         *d = *s;
182         if(*s==path_seperator) init=1;
183         else init=0;
184         d++;s++;
185     }
186     if(d!=n && d[-1]==path_seperator) 
187         d--;
188     *d = 0;
189  
190 #ifdef LOWERCASE_UPPERCASE
191     n = filename_to_lowercase(n);
192 #endif
193     
194     return n;
195 }
196 static void testnormalize()
197 {
198 #define TEST(x) {printf("%s -> %s\n", (x), normalize_path(x));}
199     TEST(".");
200     TEST("../as3");
201     TEST("../as3/");
202     TEST("../as3/parser.y");
203     TEST("../as3/ok/../ok/scope.as");
204     TEST("ok/scope.as");
205     TEST("ok/./scope.as");
206     TEST("./ok/scope.as");
207     TEST("./");
208     TEST("/tmp/");
209     TEST("/./tmp/");
210     TEST("../");
211     TEST("/");
212     TEST("/tmp");
213     TEST("/tmp/../usr/");
214 }
215
216 char* concat_paths(const char*base, const char*add)
217 {
218     int l1 = strlen(base);
219     int l2 = strlen(add);
220     int pos = 0;
221     char*n = 0;
222     while(l1 && base[l1-1] == path_seperator)
223         l1--;
224     while(pos < l2 && add[pos] == path_seperator)
225         pos++;
226     n = (char*)malloc(l1 + (l2-pos) + 2);
227     memcpy(n,base,l1);
228     n[l1]=path_seperator;
229     memcpy(&n[l1+1],&add[pos],l2-pos+1);
230     return n;
231 }
232
233 char*find_file(const char*filename, char error)
234 {
235     include_dir_t*i = current_include_dirs;
236     FILE*fi = 0;
237     if(is_absolute(filename)) {
238         FILE*fi = fopen(filename, "rb");
239         if(fi) {
240             fclose(fi);
241             return strdup(filename);
242         }
243     } else {
244         if(!i && error) {
245             as3_warning("Include directory stack is empty, while looking for file %s", filename);
246         }
247         while(i) {
248             char*p = concat_paths(i->path, filename);
249             fi = fopen(p, "rb");
250             if(fi) {
251                 fclose(fi);
252                 return p;
253             } else {
254                 free(p);
255             }
256             i = i->next;
257         }
258     }
259     if(!error) {
260         return 0;
261     }
262
263     as3_error("Couldn't find file %s", filename);
264     i = current_include_dirs;
265     while(i) {
266         fprintf(stderr, "include dir: %s\n", i->path);
267         i = i->next;
268     }
269     return 0;
270 }
271
272 void enter_file(const char*name, const char*filename, void*state)
273 {
274     if(include_stack_ptr >= MAX_INCLUDE_DEPTH) {
275         as3_error("Includes nested too deeply");
276         exit(1);
277     }
278     include_stack[include_stack_ptr] = state;
279     line_stack[include_stack_ptr] = current_line;
280     column_stack[include_stack_ptr] = current_column;
281     shortfilename_stack[include_stack_ptr] = current_filename_short;
282     longfilename_stack[include_stack_ptr] = current_filename_long;
283     filename_stack[include_stack_ptr] = current_filename;
284     includedir_stack[include_stack_ptr] = current_include_dirs;
285     
286     /*char*dir = get_path(filename);
287     add_include_dir(dir);
288     free(dir);*/
289
290     include_stack_ptr++;
291     
292     dbg("entering file %s", filename);
293
294     current_line=1;
295     current_column=0;
296     current_filename = strdup(name);
297     current_filename_short = strdup(name);
298     current_filename_long = strdup(filename);
299 }
300
301 FILE*enter_file2(const char*name, const char*filename, void*state)
302 {
303     enter_file(name, filename, state);
304     FILE*fi = fopen(filename, "rb");
305     if(!fi) {
306         as3_error("Couldn't find file %s: %s", filename, strerror(errno));
307     }
308     return fi;
309 }
310
311 void* leave_file()
312 {
313     dbg("leaving file %s", current_filename);
314     if(--include_stack_ptr<=0) {
315         return 0;
316     } else {
317         free(current_filename);current_filename = filename_stack[include_stack_ptr];
318         free(current_filename_short);current_filename_short = shortfilename_stack[include_stack_ptr];
319         free(current_filename_long);current_filename_long = longfilename_stack[include_stack_ptr];
320         current_column = column_stack[include_stack_ptr];
321         current_line = line_stack[include_stack_ptr];
322         del_include_dirs(includedir_stack[include_stack_ptr], current_include_dirs);
323         current_include_dirs = includedir_stack[include_stack_ptr];
324         return include_stack[include_stack_ptr];
325     }
326 }
327