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