• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * File helper functions.
3  *
4  *  Copyright (C) 2001-2007  Peter Johnson
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS''
16  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE
19  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25  * POSSIBILITY OF SUCH DAMAGE.
26  */
27 #include <util.h>
28 
29 /* Need either unistd.h or direct.h to prototype getcwd() and mkdir() */
30 #ifdef HAVE_UNISTD_H
31 #include <unistd.h>
32 #endif
33 
34 #ifdef HAVE_DIRECT_H
35 #include <direct.h>
36 #endif
37 
38 #ifdef _WIN32
39 #include <io.h>
40 #endif
41 
42 #ifdef HAVE_SYS_STAT_H
43 #include <sys/stat.h>
44 #endif
45 
46 #include <ctype.h>
47 #include <errno.h>
48 
49 #include "errwarn.h"
50 #include "file.h"
51 
52 #define BSIZE   8192        /* Fill block size */
53 
54 
55 void
yasm_scanner_initialize(yasm_scanner * s)56 yasm_scanner_initialize(yasm_scanner *s)
57 {
58     s->bot = NULL;
59     s->tok = NULL;
60     s->ptr = NULL;
61     s->cur = NULL;
62     s->lim = NULL;
63     s->top = NULL;
64     s->eof = NULL;
65 }
66 
67 void
yasm_scanner_delete(yasm_scanner * s)68 yasm_scanner_delete(yasm_scanner *s)
69 {
70     if (s->bot) {
71         yasm_xfree(s->bot);
72         s->bot = NULL;
73     }
74 }
75 
76 int
yasm_fill_helper(yasm_scanner * s,unsigned char ** cursor,size_t (* input_func)(void * d,unsigned char * buf,size_t max),void * input_func_data)77 yasm_fill_helper(yasm_scanner *s, unsigned char **cursor,
78                  size_t (*input_func) (void *d, unsigned char *buf,
79                                        size_t max),
80                  void *input_func_data)
81 {
82     size_t cnt;
83     int first = 0;
84 
85     if (s->eof)
86         return 0;
87 
88     cnt = s->tok - s->bot;
89     if (cnt > 0) {
90         memmove(s->bot, s->tok, (size_t)(s->lim - s->tok));
91         s->tok = s->bot;
92         s->ptr -= cnt;
93         *cursor -= cnt;
94         s->lim -= cnt;
95     }
96     if (!s->bot)
97         first = 1;
98     if ((s->top - s->lim) < BSIZE) {
99         unsigned char *buf = yasm_xmalloc((size_t)(s->lim - s->bot) + BSIZE);
100         memcpy(buf, s->tok, (size_t)(s->lim - s->tok));
101         s->tok = buf;
102         s->ptr = &buf[s->ptr - s->bot];
103         *cursor = &buf[*cursor - s->bot];
104         s->lim = &buf[s->lim - s->bot];
105         s->top = &s->lim[BSIZE];
106         if (s->bot)
107             yasm_xfree(s->bot);
108         s->bot = buf;
109     }
110     if ((cnt = input_func(input_func_data, s->lim, BSIZE)) == 0) {
111         s->eof = &s->lim[cnt];
112         *s->eof++ = '\n';
113     }
114     s->lim += cnt;
115     return first;
116 }
117 
118 void
yasm_unescape_cstring(unsigned char * str,size_t * len)119 yasm_unescape_cstring(unsigned char *str, size_t *len)
120 {
121     unsigned char *s = str;
122     unsigned char *o = str;
123     unsigned char t[4];
124 
125     while ((size_t)(s-str)<*len) {
126         if (*s == '\\' && (size_t)(&s[1]-str)<*len) {
127             s++;
128             switch (*s) {
129                 case 'b': *o = '\b'; s++; break;
130                 case 'f': *o = '\f'; s++; break;
131                 case 'n': *o = '\n'; s++; break;
132                 case 'r': *o = '\r'; s++; break;
133                 case 't': *o = '\t'; s++; break;
134                 case 'x':
135                     /* hex escape; grab last two digits */
136                     s++;
137                     while ((size_t)(&s[2]-str)<*len && isxdigit(s[0])
138                            && isxdigit(s[1]) && isxdigit(s[2]))
139                         s++;
140                     if ((size_t)(s-str)<*len && isxdigit(*s)) {
141                         t[0] = *s++;
142                         t[1] = '\0';
143                         t[2] = '\0';
144                         if ((size_t)(s-str)<*len && isxdigit(*s))
145                             t[1] = *s++;
146                         *o = (unsigned char)strtoul((char *)t, NULL, 16);
147                     } else
148                         *o = '\0';
149                     break;
150                 default:
151                     if (isdigit(*s)) {
152                         int warn = 0;
153                         /* octal escape */
154                         if (*s > '7')
155                             warn = 1;
156                         *o = *s++ - '0';
157                         if ((size_t)(s-str)<*len && isdigit(*s)) {
158                             if (*s > '7')
159                                 warn = 1;
160                             *o <<= 3;
161                             *o += *s++ - '0';
162                             if ((size_t)(s-str)<*len && isdigit(*s)) {
163                                 if (*s > '7')
164                                     warn = 1;
165                                 *o <<= 3;
166                                 *o += *s++ - '0';
167                             }
168                         }
169                         if (warn)
170                             yasm_warn_set(YASM_WARN_GENERAL,
171                                           N_("octal value out of range"));
172                     } else
173                         *o = *s++;
174                     break;
175             }
176             o++;
177         } else
178             *o++ = *s++;
179     }
180     *len = o-str;
181 }
182 
183 size_t
yasm__splitpath_unix(const char * path,const char ** tail)184 yasm__splitpath_unix(const char *path, /*@out@*/ const char **tail)
185 {
186     const char *s;
187     s = strrchr(path, '/');
188     if (!s) {
189         /* No head */
190         *tail = path;
191         return 0;
192     }
193     *tail = s+1;
194     /* Strip trailing ./ on path */
195     while ((s-1)>=path && *(s-1) == '.' && *s == '/'
196            && !((s-2)>=path && *(s-2) == '.'))
197         s -= 2;
198     /* Strip trailing slashes on path (except leading) */
199     while (s>path && *s == '/')
200         s--;
201     /* Return length of head */
202     return s-path+1;
203 }
204 
205 size_t
yasm__splitpath_win(const char * path,const char ** tail)206 yasm__splitpath_win(const char *path, /*@out@*/ const char **tail)
207 {
208     const char *basepath = path;
209     const char *s;
210 
211     /* split off drive letter first, if any */
212     if (isalpha(path[0]) && path[1] == ':')
213         basepath += 2;
214 
215     s = basepath;
216     while (*s != '\0')
217         s++;
218     while (s >= basepath && *s != '\\' && *s != '/')
219         s--;
220     if (s < basepath) {
221         *tail = basepath;
222         if (path == basepath)
223             return 0;   /* No head */
224         else
225             return 2;   /* Drive letter is head */
226     }
227     *tail = s+1;
228     /* Strip trailing .\ or ./ on path */
229     while ((s-1)>=basepath && *(s-1) == '.' && (*s == '/' || *s == '\\')
230            && !((s-2)>=basepath && *(s-2) == '.'))
231         s -= 2;
232     /* Strip trailing slashes on path (except leading) */
233     while (s>basepath && (*s == '/' || *s == '\\'))
234         s--;
235     /* Return length of head */
236     return s-path+1;
237 }
238 
239 char *
yasm__getcwd(void)240 yasm__getcwd(void)
241 {
242     char *buf;
243     size_t size;
244 
245     size = 1024;
246     buf = yasm_xmalloc(size);
247 
248     if (getenv("YASM_TEST_SUITE")) {
249         strcpy(buf, "./");
250         return buf;
251     }
252 
253     while (getcwd(buf, size-1) == NULL) {
254         if (errno != ERANGE) {
255             yasm__fatal(N_("could not determine current working directory"));
256             yasm_xfree(buf);
257             return NULL;
258         }
259         size *= 2;
260         buf = yasm_xrealloc(buf, size);
261     }
262 
263     /* append a '/' if not already present */
264     size = strlen(buf);
265     if (buf[size-1] != '\\' && buf[size-1] != '/') {
266         buf[size] = '/';
267         buf[size+1] = '\0';
268     }
269     return buf;
270 }
271 
272 char *
yasm__abspath(const char * path)273 yasm__abspath(const char *path)
274 {
275     char *curdir, *abspath;
276 
277     curdir = yasm__getcwd();
278     abspath = yasm__combpath(curdir, path);
279     yasm_xfree(curdir);
280 
281     return abspath;
282 }
283 
284 char *
yasm__combpath_unix(const char * from,const char * to)285 yasm__combpath_unix(const char *from, const char *to)
286 {
287     const char *tail;
288     size_t pathlen, i, j;
289     char *out;
290 
291     if (to[0] == '/') {
292         /* absolute "to" */
293         out = yasm_xmalloc(strlen(to)+1);
294         /* Combine any double slashes when copying */
295         for (j=0; *to; to++) {
296             if (*to == '/' && *(to+1) == '/')
297                 continue;
298             out[j++] = *to;
299         }
300         out[j++] = '\0';
301         return out;
302     }
303 
304     /* Get path component; note this strips trailing slash */
305     pathlen = yasm__splitpath_unix(from, &tail);
306 
307     out = yasm_xmalloc(pathlen+strlen(to)+2);   /* worst case maximum len */
308 
309     /* Combine any double slashes when copying */
310     for (i=0, j=0; i<pathlen; i++) {
311         if (i<pathlen-1 && from[i] == '/' && from[i+1] == '/')
312             continue;
313         out[j++] = from[i];
314     }
315     pathlen = j;
316 
317     /* Add trailing slash back in */
318     if (pathlen > 0 && out[pathlen-1] != '/')
319         out[pathlen++] = '/';
320 
321     /* Now scan from left to right through "to", stripping off "." and "..";
322      * if we see "..", back up one directory in out unless last directory in
323      * out is also "..".
324      *
325      * Note this does NOT back through ..'s in the "from" path; this is just
326      * as well as that could skip symlinks (e.g. "foo/bar/.." might not be
327      * the same as "foo").
328      */
329     for (;;) {
330         if (to[0] == '.' && to[1] == '/') {
331             to += 2;        /* current directory */
332             while (*to == '/')
333                 to++;           /* strip off any additional slashes */
334         } else if (pathlen == 0)
335             break;          /* no more "from" path left, we're done */
336         else if (to[0] == '.' && to[1] == '.' && to[2] == '/') {
337             if (pathlen >= 3 && out[pathlen-1] == '/' && out[pathlen-2] == '.'
338                 && out[pathlen-3] == '.') {
339                 /* can't ".." against a "..", so we're done. */
340                 break;
341             }
342 
343             to += 3;    /* throw away "../" */
344             while (*to == '/')
345                 to++;           /* strip off any additional slashes */
346 
347             /* and back out last directory in "out" if not already at root */
348             if (pathlen > 1) {
349                 pathlen--;      /* strip off trailing '/' */
350                 while (pathlen > 0 && out[pathlen-1] != '/')
351                     pathlen--;
352             }
353         } else
354             break;
355     }
356 
357     /* Copy "to" to tail of output, and we're done */
358     /* Combine any double slashes when copying */
359     for (j=pathlen; *to; to++) {
360         if (*to == '/' && *(to+1) == '/')
361             continue;
362         out[j++] = *to;
363     }
364     out[j++] = '\0';
365 
366     return out;
367 }
368 
369 char *
yasm__combpath_win(const char * from,const char * to)370 yasm__combpath_win(const char *from, const char *to)
371 {
372     const char *tail;
373     size_t pathlen, i, j;
374     char *out;
375 
376     if ((isalpha(to[0]) && to[1] == ':') || (to[0] == '/' || to[0] == '\\')) {
377         /* absolute or drive letter "to" */
378         out = yasm_xmalloc(strlen(to)+1);
379         /* Combine any double slashes when copying */
380         for (j=0; *to; to++) {
381             if ((*to == '/' || *to == '\\')
382                 && (*(to+1) == '/' || *(to+1) == '\\'))
383                 continue;
384             if (*to == '/')
385                 out[j++] = '\\';
386             else
387                 out[j++] = *to;
388         }
389         out[j++] = '\0';
390         return out;
391     }
392 
393     /* Get path component; note this strips trailing slash */
394     pathlen = yasm__splitpath_win(from, &tail);
395 
396     out = yasm_xmalloc(pathlen+strlen(to)+2);   /* worst case maximum len */
397 
398     /* Combine any double slashes when copying */
399     for (i=0, j=0; i<pathlen; i++) {
400         if (i<pathlen-1 && (from[i] == '/' || from[i] == '\\')
401             && (from[i+1] == '/' || from[i+1] == '\\'))
402             continue;
403         if (from[i] == '/')
404             out[j++] = '\\';
405         else
406             out[j++] = from[i];
407     }
408     pathlen = j;
409 
410     /* Add trailing slash back in, unless it's only a raw drive letter */
411     if (pathlen > 0 && out[pathlen-1] != '\\'
412         && !(pathlen == 2 && isalpha(out[0]) && out[1] == ':'))
413         out[pathlen++] = '\\';
414 
415     /* Now scan from left to right through "to", stripping off "." and "..";
416      * if we see "..", back up one directory in out unless last directory in
417      * out is also "..".
418      *
419      * Note this does NOT back through ..'s in the "from" path; this is just
420      * as well as that could skip symlinks (e.g. "foo/bar/.." might not be
421      * the same as "foo").
422      */
423     for (;;) {
424         if (to[0] == '.' && (to[1] == '/' || to[1] == '\\')) {
425             to += 2;        /* current directory */
426             while (*to == '/' || *to == '\\')
427                 to++;           /* strip off any additional slashes */
428         } else if (pathlen == 0
429                  || (pathlen == 2 && isalpha(out[0]) && out[1] == ':'))
430             break;          /* no more "from" path left, we're done */
431         else if (to[0] == '.' && to[1] == '.'
432                  && (to[2] == '/' || to[2] == '\\')) {
433             if (pathlen >= 3 && out[pathlen-1] == '\\'
434                 && out[pathlen-2] == '.' && out[pathlen-3] == '.') {
435                 /* can't ".." against a "..", so we're done. */
436                 break;
437             }
438 
439             to += 3;    /* throw away "../" (or "..\") */
440             while (*to == '/' || *to == '\\')
441                 to++;           /* strip off any additional slashes */
442 
443             /* and back out last directory in "out" if not already at root */
444             if (pathlen > 1) {
445                 pathlen--;      /* strip off trailing '/' */
446                 while (pathlen > 0 && out[pathlen-1] != '\\')
447                     pathlen--;
448             }
449         } else
450             break;
451     }
452 
453     /* Copy "to" to tail of output, and we're done */
454     /* Combine any double slashes when copying */
455     for (j=pathlen; *to; to++) {
456         if ((*to == '/' || *to == '\\') && (*(to+1) == '/' || *(to+1) == '\\'))
457             continue;
458         if (*to == '/')
459             out[j++] = '\\';
460         else
461             out[j++] = *to;
462     }
463     out[j++] = '\0';
464 
465     return out;
466 }
467 
468 size_t
yasm__createpath_common(const char * path,int win)469 yasm__createpath_common(const char *path, int win)
470 {
471     const char *pp = path, *pe;
472     char *ts, *tp;
473     size_t len, lth;
474 
475     lth = len = strlen(path);
476     ts = tp = (char *) malloc(len + 1);
477     pe = pp + len;
478     while (pe > pp) {
479         if ((win && *pe == '\\') || *pe == '/')
480             break;
481         --pe;
482         --lth;
483     }
484 
485     while (pp <= pe) {
486         if (pp == pe || (win && *pp == '\\') || *pp == '/') {
487 #ifdef _WIN32
488             struct _finddata_t fi;
489             intptr_t h;
490 #elif defined(HAVE_SYS_STAT_H)
491             struct stat fi;
492 #endif
493             *tp = '\0';
494 
495 #ifdef _WIN32
496             h = _findfirst(ts, &fi);
497             if (h != -1) {
498                 if (fi.attrib != _A_SUBDIR) {
499                     _findclose(h);
500                     break;
501                 }
502             } else if (errno == ENOENT) {
503                 if (_mkdir(ts) == -1) {
504                     _findclose(h);
505                     lth = -1;
506                     break;
507                 }
508             }
509             _findclose(h);
510 #elif defined(HAVE_SYS_STAT_H)
511             if (stat(ts, &fi) != -1) {
512                 if (!S_ISDIR(fi.st_mode))
513                     break;
514             } else if (errno == ENOENT) {
515                 if (mkdir(ts, 0755) == -1) {
516                     lth = 0;
517                     break;
518                 }
519             }
520 #else
521             break;
522 #endif
523         }
524         *tp++ = *pp++;
525     }
526     free(ts);
527     return lth;
528 }
529 
530 typedef struct incpath {
531     STAILQ_ENTRY(incpath) link;
532     /*@owned@*/ char *path;
533 } incpath;
534 
535 STAILQ_HEAD(incpath_head, incpath) incpaths = STAILQ_HEAD_INITIALIZER(incpaths);
536 
537 FILE *
yasm_fopen_include(const char * iname,const char * from,const char * mode,char ** oname)538 yasm_fopen_include(const char *iname, const char *from, const char *mode,
539                    char **oname)
540 {
541     FILE *f;
542     char *combine;
543     incpath *np;
544 
545     /* Try directly relative to from first, then each of the include paths */
546     if (from) {
547         combine = yasm__combpath(from, iname);
548         f = fopen(combine, mode);
549         if (f) {
550             if (oname)
551                 *oname = combine;
552             else
553                 yasm_xfree(combine);
554             return f;
555         }
556         yasm_xfree(combine);
557     }
558 
559     STAILQ_FOREACH(np, &incpaths, link) {
560         combine = yasm__combpath(np->path, iname);
561         f = fopen(combine, mode);
562         if (f) {
563             if (oname)
564                 *oname = combine;
565             else
566                 yasm_xfree(combine);
567             return f;
568         }
569         yasm_xfree(combine);
570     }
571 
572     if (oname)
573         *oname = NULL;
574     return NULL;
575 }
576 
577 void
yasm_delete_include_paths(void)578 yasm_delete_include_paths(void)
579 {
580     incpath *n1, *n2;
581 
582     n1 = STAILQ_FIRST(&incpaths);
583     while (n1) {
584         n2 = STAILQ_NEXT(n1, link);
585         yasm_xfree(n1->path);
586         yasm_xfree(n1);
587         n1 = n2;
588     }
589     STAILQ_INIT(&incpaths);
590 }
591 
592 const char *
yasm_get_include_dir(void ** iter)593 yasm_get_include_dir(void **iter)
594 {
595     incpath *p = (incpath *)*iter;
596 
597     if (!p)
598         p = STAILQ_FIRST(&incpaths);
599     else
600         p = STAILQ_NEXT(p, link);
601 
602     *iter = p;
603     if (p)
604         return p->path;
605     else
606         return NULL;
607 }
608 
609 void
yasm_add_include_path(const char * path)610 yasm_add_include_path(const char *path)
611 {
612     incpath *np = yasm_xmalloc(sizeof(incpath));
613     size_t len = strlen(path);
614 
615     np->path = yasm_xmalloc(len+2);
616     memcpy(np->path, path, len+1);
617     /* Add trailing slash if it is missing */
618     if (path[len-1] != '\\' && path[len-1] != '/') {
619         np->path[len] = '/';
620         np->path[len+1] = '\0';
621     }
622 
623     STAILQ_INSERT_TAIL(&incpaths, np, link);
624 }
625 
626 size_t
yasm_fwrite_16_l(unsigned short val,FILE * f)627 yasm_fwrite_16_l(unsigned short val, FILE *f)
628 {
629     if (fputc(val & 0xFF, f) == EOF)
630         return 0;
631     if (fputc((val >> 8) & 0xFF, f) == EOF)
632         return 0;
633     return 1;
634 }
635 
636 size_t
yasm_fwrite_32_l(unsigned long val,FILE * f)637 yasm_fwrite_32_l(unsigned long val, FILE *f)
638 {
639     if (fputc((int)(val & 0xFF), f) == EOF)
640         return 0;
641     if (fputc((int)((val >> 8) & 0xFF), f) == EOF)
642         return 0;
643     if (fputc((int)((val >> 16) & 0xFF), f) == EOF)
644         return 0;
645     if (fputc((int)((val >> 24) & 0xFF), f) == EOF)
646         return 0;
647     return 1;
648 }
649 
650 size_t
yasm_fwrite_16_b(unsigned short val,FILE * f)651 yasm_fwrite_16_b(unsigned short val, FILE *f)
652 {
653     if (fputc((val >> 8) & 0xFF, f) == EOF)
654         return 0;
655     if (fputc(val & 0xFF, f) == EOF)
656         return 0;
657     return 1;
658 }
659 
660 size_t
yasm_fwrite_32_b(unsigned long val,FILE * f)661 yasm_fwrite_32_b(unsigned long val, FILE *f)
662 {
663     if (fputc((int)((val >> 24) & 0xFF), f) == EOF)
664         return 0;
665     if (fputc((int)((val >> 16) & 0xFF), f) == EOF)
666         return 0;
667     if (fputc((int)((val >> 8) & 0xFF), f) == EOF)
668         return 0;
669     if (fputc((int)(val & 0xFF), f) == EOF)
670         return 0;
671     return 1;
672 }
673