Home | History | Annotate | Download | only in libyasm
      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
     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
     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
     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
    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
    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
    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 *
    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 *
    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 *
    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 *
    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
    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 *
    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
    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 *
    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
    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
    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
    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
    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
    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