/* * File helper functions. * * Copyright (C) 2001-2007 Peter Johnson * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include /* Need either unistd.h or direct.h to prototype getcwd() and mkdir() */ #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_DIRECT_H #include #endif #ifdef _WIN32 #include #endif #ifdef HAVE_SYS_STAT_H #include #endif #include #include #include "errwarn.h" #include "file.h" #define BSIZE 8192 /* Fill block size */ void yasm_scanner_initialize(yasm_scanner *s) { s->bot = NULL; s->tok = NULL; s->ptr = NULL; s->cur = NULL; s->lim = NULL; s->top = NULL; s->eof = NULL; } void yasm_scanner_delete(yasm_scanner *s) { if (s->bot) { yasm_xfree(s->bot); s->bot = NULL; } } 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) { size_t cnt; int first = 0; if (s->eof) return 0; cnt = s->tok - s->bot; if (cnt > 0) { memmove(s->bot, s->tok, (size_t)(s->lim - s->tok)); s->tok = s->bot; s->ptr -= cnt; *cursor -= cnt; s->lim -= cnt; } if (!s->bot) first = 1; if ((s->top - s->lim) < BSIZE) { unsigned char *buf = yasm_xmalloc((size_t)(s->lim - s->bot) + BSIZE); memcpy(buf, s->tok, (size_t)(s->lim - s->tok)); s->tok = buf; s->ptr = &buf[s->ptr - s->bot]; *cursor = &buf[*cursor - s->bot]; s->lim = &buf[s->lim - s->bot]; s->top = &s->lim[BSIZE]; if (s->bot) yasm_xfree(s->bot); s->bot = buf; } if ((cnt = input_func(input_func_data, s->lim, BSIZE)) == 0) { s->eof = &s->lim[cnt]; *s->eof++ = '\n'; } s->lim += cnt; return first; } void yasm_unescape_cstring(unsigned char *str, size_t *len) { unsigned char *s = str; unsigned char *o = str; unsigned char t[4]; while ((size_t)(s-str)<*len) { if (*s == '\\' && (size_t)(&s[1]-str)<*len) { s++; switch (*s) { case 'b': *o = '\b'; s++; break; case 'f': *o = '\f'; s++; break; case 'n': *o = '\n'; s++; break; case 'r': *o = '\r'; s++; break; case 't': *o = '\t'; s++; break; case 'x': /* hex escape; grab last two digits */ s++; while ((size_t)(&s[2]-str)<*len && isxdigit(s[0]) && isxdigit(s[1]) && isxdigit(s[2])) s++; if ((size_t)(s-str)<*len && isxdigit(*s)) { t[0] = *s++; t[1] = '\0'; t[2] = '\0'; if ((size_t)(s-str)<*len && isxdigit(*s)) t[1] = *s++; *o = (unsigned char)strtoul((char *)t, NULL, 16); } else *o = '\0'; break; default: if (isdigit(*s)) { int warn = 0; /* octal escape */ if (*s > '7') warn = 1; *o = *s++ - '0'; if ((size_t)(s-str)<*len && isdigit(*s)) { if (*s > '7') warn = 1; *o <<= 3; *o += *s++ - '0'; if ((size_t)(s-str)<*len && isdigit(*s)) { if (*s > '7') warn = 1; *o <<= 3; *o += *s++ - '0'; } } if (warn) yasm_warn_set(YASM_WARN_GENERAL, N_("octal value out of range")); } else *o = *s++; break; } o++; } else *o++ = *s++; } *len = o-str; } size_t yasm__splitpath_unix(const char *path, /*@out@*/ const char **tail) { const char *s; s = strrchr(path, '/'); if (!s) { /* No head */ *tail = path; return 0; } *tail = s+1; /* Strip trailing ./ on path */ while ((s-1)>=path && *(s-1) == '.' && *s == '/' && !((s-2)>=path && *(s-2) == '.')) s -= 2; /* Strip trailing slashes on path (except leading) */ while (s>path && *s == '/') s--; /* Return length of head */ return s-path+1; } size_t yasm__splitpath_win(const char *path, /*@out@*/ const char **tail) { const char *basepath = path; const char *s; /* split off drive letter first, if any */ if (isalpha(path[0]) && path[1] == ':') basepath += 2; s = basepath; while (*s != '\0') s++; while (s >= basepath && *s != '\\' && *s != '/') s--; if (s < basepath) { *tail = basepath; if (path == basepath) return 0; /* No head */ else return 2; /* Drive letter is head */ } *tail = s+1; /* Strip trailing .\ or ./ on path */ while ((s-1)>=basepath && *(s-1) == '.' && (*s == '/' || *s == '\\') && !((s-2)>=basepath && *(s-2) == '.')) s -= 2; /* Strip trailing slashes on path (except leading) */ while (s>basepath && (*s == '/' || *s == '\\')) s--; /* Return length of head */ return s-path+1; } char * yasm__getcwd(void) { char *buf; size_t size; size = 1024; buf = yasm_xmalloc(size); if (getenv("YASM_TEST_SUITE")) { strcpy(buf, "./"); return buf; } while (getcwd(buf, size-1) == NULL) { if (errno != ERANGE) { yasm__fatal(N_("could not determine current working directory")); yasm_xfree(buf); return NULL; } size *= 2; buf = yasm_xrealloc(buf, size); } /* append a '/' if not already present */ size = strlen(buf); if (buf[size-1] != '\\' && buf[size-1] != '/') { buf[size] = '/'; buf[size+1] = '\0'; } return buf; } char * yasm__abspath(const char *path) { char *curdir, *abspath; curdir = yasm__getcwd(); abspath = yasm__combpath(curdir, path); yasm_xfree(curdir); return abspath; } char * yasm__combpath_unix(const char *from, const char *to) { const char *tail; size_t pathlen, i, j; char *out; if (to[0] == '/') { /* absolute "to" */ out = yasm_xmalloc(strlen(to)+1); /* Combine any double slashes when copying */ for (j=0; *to; to++) { if (*to == '/' && *(to+1) == '/') continue; out[j++] = *to; } out[j++] = '\0'; return out; } /* Get path component; note this strips trailing slash */ pathlen = yasm__splitpath_unix(from, &tail); out = yasm_xmalloc(pathlen+strlen(to)+2); /* worst case maximum len */ /* Combine any double slashes when copying */ for (i=0, j=0; i 0 && out[pathlen-1] != '/') out[pathlen++] = '/'; /* Now scan from left to right through "to", stripping off "." and ".."; * if we see "..", back up one directory in out unless last directory in * out is also "..". * * Note this does NOT back through ..'s in the "from" path; this is just * as well as that could skip symlinks (e.g. "foo/bar/.." might not be * the same as "foo"). */ for (;;) { if (to[0] == '.' && to[1] == '/') { to += 2; /* current directory */ while (*to == '/') to++; /* strip off any additional slashes */ } else if (pathlen == 0) break; /* no more "from" path left, we're done */ else if (to[0] == '.' && to[1] == '.' && to[2] == '/') { if (pathlen >= 3 && out[pathlen-1] == '/' && out[pathlen-2] == '.' && out[pathlen-3] == '.') { /* can't ".." against a "..", so we're done. */ break; } to += 3; /* throw away "../" */ while (*to == '/') to++; /* strip off any additional slashes */ /* and back out last directory in "out" if not already at root */ if (pathlen > 1) { pathlen--; /* strip off trailing '/' */ while (pathlen > 0 && out[pathlen-1] != '/') pathlen--; } } else break; } /* Copy "to" to tail of output, and we're done */ /* Combine any double slashes when copying */ for (j=pathlen; *to; to++) { if (*to == '/' && *(to+1) == '/') continue; out[j++] = *to; } out[j++] = '\0'; return out; } char * yasm__combpath_win(const char *from, const char *to) { const char *tail; size_t pathlen, i, j; char *out; if ((isalpha(to[0]) && to[1] == ':') || (to[0] == '/' || to[0] == '\\')) { /* absolute or drive letter "to" */ out = yasm_xmalloc(strlen(to)+1); /* Combine any double slashes when copying */ for (j=0; *to; to++) { if ((*to == '/' || *to == '\\') && (*(to+1) == '/' || *(to+1) == '\\')) continue; if (*to == '/') out[j++] = '\\'; else out[j++] = *to; } out[j++] = '\0'; return out; } /* Get path component; note this strips trailing slash */ pathlen = yasm__splitpath_win(from, &tail); out = yasm_xmalloc(pathlen+strlen(to)+2); /* worst case maximum len */ /* Combine any double slashes when copying */ for (i=0, j=0; i 0 && out[pathlen-1] != '\\' && !(pathlen == 2 && isalpha(out[0]) && out[1] == ':')) out[pathlen++] = '\\'; /* Now scan from left to right through "to", stripping off "." and ".."; * if we see "..", back up one directory in out unless last directory in * out is also "..". * * Note this does NOT back through ..'s in the "from" path; this is just * as well as that could skip symlinks (e.g. "foo/bar/.." might not be * the same as "foo"). */ for (;;) { if (to[0] == '.' && (to[1] == '/' || to[1] == '\\')) { to += 2; /* current directory */ while (*to == '/' || *to == '\\') to++; /* strip off any additional slashes */ } else if (pathlen == 0 || (pathlen == 2 && isalpha(out[0]) && out[1] == ':')) break; /* no more "from" path left, we're done */ else if (to[0] == '.' && to[1] == '.' && (to[2] == '/' || to[2] == '\\')) { if (pathlen >= 3 && out[pathlen-1] == '\\' && out[pathlen-2] == '.' && out[pathlen-3] == '.') { /* can't ".." against a "..", so we're done. */ break; } to += 3; /* throw away "../" (or "..\") */ while (*to == '/' || *to == '\\') to++; /* strip off any additional slashes */ /* and back out last directory in "out" if not already at root */ if (pathlen > 1) { pathlen--; /* strip off trailing '/' */ while (pathlen > 0 && out[pathlen-1] != '\\') pathlen--; } } else break; } /* Copy "to" to tail of output, and we're done */ /* Combine any double slashes when copying */ for (j=pathlen; *to; to++) { if ((*to == '/' || *to == '\\') && (*(to+1) == '/' || *(to+1) == '\\')) continue; if (*to == '/') out[j++] = '\\'; else out[j++] = *to; } out[j++] = '\0'; return out; } size_t yasm__createpath_common(const char *path, int win) { const char *pp = path, *pe; char *ts, *tp; size_t len, lth; lth = len = strlen(path); ts = tp = (char *) malloc(len + 1); pe = pp + len; while (pe > pp) { if ((win && *pe == '\\') || *pe == '/') break; --pe; --lth; } while (pp <= pe) { if (pp == pe || (win && *pp == '\\') || *pp == '/') { #ifdef _WIN32 struct _finddata_t fi; intptr_t h; #elif defined(HAVE_SYS_STAT_H) struct stat fi; #endif *tp = '\0'; #ifdef _WIN32 h = _findfirst(ts, &fi); if (h != -1) { if (fi.attrib != _A_SUBDIR) { _findclose(h); break; } } else if (errno == ENOENT) { if (_mkdir(ts) == -1) { _findclose(h); lth = -1; break; } } _findclose(h); #elif defined(HAVE_SYS_STAT_H) if (stat(ts, &fi) != -1) { if (!S_ISDIR(fi.st_mode)) break; } else if (errno == ENOENT) { if (mkdir(ts, 0755) == -1) { lth = 0; break; } } #else break; #endif } *tp++ = *pp++; } free(ts); return lth; } typedef struct incpath { STAILQ_ENTRY(incpath) link; /*@owned@*/ char *path; } incpath; STAILQ_HEAD(incpath_head, incpath) incpaths = STAILQ_HEAD_INITIALIZER(incpaths); FILE * yasm_fopen_include(const char *iname, const char *from, const char *mode, char **oname) { FILE *f; char *combine; incpath *np; /* Try directly relative to from first, then each of the include paths */ if (from) { combine = yasm__combpath(from, iname); f = fopen(combine, mode); if (f) { if (oname) *oname = combine; else yasm_xfree(combine); return f; } yasm_xfree(combine); } STAILQ_FOREACH(np, &incpaths, link) { combine = yasm__combpath(np->path, iname); f = fopen(combine, mode); if (f) { if (oname) *oname = combine; else yasm_xfree(combine); return f; } yasm_xfree(combine); } if (oname) *oname = NULL; return NULL; } void yasm_delete_include_paths(void) { incpath *n1, *n2; n1 = STAILQ_FIRST(&incpaths); while (n1) { n2 = STAILQ_NEXT(n1, link); yasm_xfree(n1->path); yasm_xfree(n1); n1 = n2; } STAILQ_INIT(&incpaths); } const char * yasm_get_include_dir(void **iter) { incpath *p = (incpath *)*iter; if (!p) p = STAILQ_FIRST(&incpaths); else p = STAILQ_NEXT(p, link); *iter = p; if (p) return p->path; else return NULL; } void yasm_add_include_path(const char *path) { incpath *np = yasm_xmalloc(sizeof(incpath)); size_t len = strlen(path); np->path = yasm_xmalloc(len+2); memcpy(np->path, path, len+1); /* Add trailing slash if it is missing */ if (path[len-1] != '\\' && path[len-1] != '/') { np->path[len] = '/'; np->path[len+1] = '\0'; } STAILQ_INSERT_TAIL(&incpaths, np, link); } size_t yasm_fwrite_16_l(unsigned short val, FILE *f) { if (fputc(val & 0xFF, f) == EOF) return 0; if (fputc((val >> 8) & 0xFF, f) == EOF) return 0; return 1; } size_t yasm_fwrite_32_l(unsigned long val, FILE *f) { if (fputc((int)(val & 0xFF), f) == EOF) return 0; if (fputc((int)((val >> 8) & 0xFF), f) == EOF) return 0; if (fputc((int)((val >> 16) & 0xFF), f) == EOF) return 0; if (fputc((int)((val >> 24) & 0xFF), f) == EOF) return 0; return 1; } size_t yasm_fwrite_16_b(unsigned short val, FILE *f) { if (fputc((val >> 8) & 0xFF, f) == EOF) return 0; if (fputc(val & 0xFF, f) == EOF) return 0; return 1; } size_t yasm_fwrite_32_b(unsigned long val, FILE *f) { if (fputc((int)((val >> 24) & 0xFF), f) == EOF) return 0; if (fputc((int)((val >> 16) & 0xFF), f) == EOF) return 0; if (fputc((int)((val >> 8) & 0xFF), f) == EOF) return 0; if (fputc((int)(val & 0xFF), f) == EOF) return 0; return 1; }