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
55void
56yasm_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
67void
68yasm_scanner_delete(yasm_scanner *s)
69{
70    if (s->bot) {
71        yasm_xfree(s->bot);
72        s->bot = NULL;
73    }
74}
75
76int
77yasm_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
118void
119yasm_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
183size_t
184yasm__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
205size_t
206yasm__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
239char *
240yasm__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
272char *
273yasm__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
284char *
285yasm__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
369char *
370yasm__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
468size_t
469yasm__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
530typedef struct incpath {
531    STAILQ_ENTRY(incpath) link;
532    /*@owned@*/ char *path;
533} incpath;
534
535STAILQ_HEAD(incpath_head, incpath) incpaths = STAILQ_HEAD_INITIALIZER(incpaths);
536
537FILE *
538yasm_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
577void
578yasm_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
592const char *
593yasm_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
609void
610yasm_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
626size_t
627yasm_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
636size_t
637yasm_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
650size_t
651yasm_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
660size_t
661yasm_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