mirror of https://github.com/yasm/yasm.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
584 lines
14 KiB
584 lines
14 KiB
/* |
|
* File helper functions. |
|
* |
|
* Copyright (C) 2001-2006 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 <util.h> |
|
/*@unused@*/ RCSID("$Id$"); |
|
|
|
/* Need either unistd.h or direct.h (on Windows) to prototype getcwd() */ |
|
#ifdef HAVE_UNISTD_H |
|
#include <unistd.h> |
|
#elif defined(WIN32) || defined(_WIN32) |
|
#include <direct.h> |
|
#endif |
|
|
|
#include <ctype.h> |
|
|
|
#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; |
|
} |
|
|
|
/* FIXME: dumb way for now */ |
|
char * |
|
yasm__abspath_unix(const char *path) |
|
{ |
|
char *curdir, *abspath; |
|
static const char pathsep[2] = "/"; |
|
|
|
curdir = getcwd(NULL, 0); |
|
|
|
abspath = yasm_xmalloc(strlen(curdir) + strlen(path) + 2); |
|
strcpy(abspath, curdir); |
|
strcat(abspath, pathsep); |
|
strcat(abspath, path); |
|
|
|
free(curdir); |
|
|
|
return abspath; |
|
} |
|
|
|
/* FIXME: dumb way for now */ |
|
char * |
|
yasm__abspath_win(const char *path) |
|
{ |
|
char *curdir, *abspath, *ch; |
|
static const char pathsep[2] = "\\"; |
|
|
|
curdir = getcwd(NULL, 0); |
|
|
|
abspath = yasm_xmalloc(strlen(curdir) + strlen(path) + 2); |
|
strcpy(abspath, curdir); |
|
strcat(abspath, pathsep); |
|
strcat(abspath, path); |
|
|
|
free(curdir); |
|
|
|
/* Replace all / with \ */ |
|
ch = abspath; |
|
while (*ch) { |
|
if (*ch == '/') |
|
*ch = '\\'; |
|
ch++; |
|
} |
|
|
|
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<pathlen; i++) { |
|
if (i<pathlen-1 && from[i] == '/' && from[i+1] == '/') |
|
continue; |
|
out[j++] = from[i]; |
|
} |
|
pathlen = j; |
|
|
|
/* Add trailing slash back in */ |
|
if (pathlen > 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<pathlen; i++) { |
|
if (i<pathlen-1 && (from[i] == '/' || from[i] == '\\') |
|
&& (from[i+1] == '/' || from[i+1] == '\\')) |
|
continue; |
|
if (from[i] == '/') |
|
out[j++] = '\\'; |
|
else |
|
out[j++] = from[i]; |
|
} |
|
pathlen = j; |
|
|
|
/* Add trailing slash back in, unless it's only a raw drive letter */ |
|
if (pathlen > 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; |
|
} |
|
|
|
typedef struct incpath { |
|
STAILQ_ENTRY(incpath) link; |
|
/*@owned@*/ char *path; |
|
} incpath; |
|
|
|
STAILQ_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); |
|
} |
|
|
|
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; |
|
}
|
|
|