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