MIRROR: javascript for 馃悳's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at mir/inline-method 890 lines 28 kB view raw
1#include <compat.h> // IWYU pragma: keep 2 3#include <ctype.h> 4#include <stdio.h> 5#include <stdlib.h> 6#include <string.h> 7 8#include "ant.h" 9#include "errors.h" 10#include "internal.h" 11#include "modules/symbol.h" 12 13#ifndef PATH_MAX 14#define PATH_MAX 4096 15#endif 16 17#ifdef _WIN32 18#define PATH_SEP '\\' 19#define PATH_SEP_STR "\\" 20#define PATH_DELIMITER ';' 21#else 22#define PATH_SEP '/' 23#define PATH_SEP_STR "/" 24#define PATH_DELIMITER ':' 25#endif 26 27typedef enum { 28 PATH_STYLE_POSIX = 1, 29 PATH_STYLE_WIN32 = 2, 30} path_style_t; 31 32static path_style_t path_host_style(void) { 33#ifdef _WIN32 34 return PATH_STYLE_WIN32; 35#else 36 return PATH_STYLE_POSIX; 37#endif 38} 39 40static path_style_t path_current_style(ant_t *js) { 41 ant_value_t data = js_get_slot(js->current_func, SLOT_DATA); 42 if (vtype(data) == T_NUM) { 43 int style = (int)js_getnum(data); 44 if (style == PATH_STYLE_WIN32) return PATH_STYLE_WIN32; 45 if (style == PATH_STYLE_POSIX) return PATH_STYLE_POSIX; 46 } 47 return path_host_style(); 48} 49 50static bool path_style_is_windows(path_style_t style) { 51 return style == PATH_STYLE_WIN32; 52} 53 54static bool path_is_sep(path_style_t style, char ch) { 55 if (style == PATH_STYLE_WIN32) return ch == '\\' || ch == '/'; 56 return ch == '/'; 57} 58 59static char path_sep_char(path_style_t style) { 60 return style == PATH_STYLE_WIN32 ? '\\' : '/'; 61} 62 63static const char *path_sep_str(path_style_t style) { 64 return style == PATH_STYLE_WIN32 ? "\\" : "/"; 65} 66 67static const char *path_delimiter_str(path_style_t style) { 68 return style == PATH_STYLE_WIN32 ? ";" : ":"; 69} 70 71static bool path_has_drive_letter(const char *path, size_t len) { 72 return len >= 2 && isalpha((unsigned char)path[0]) && path[1] == ':'; 73} 74 75static bool path_is_absolute_style(path_style_t style, const char *path, size_t len) { 76 if (!path || len == 0) return false; 77 if (style == PATH_STYLE_WIN32) { 78 if (path_is_sep(style, path[0])) return true; 79 return len >= 3 && path_has_drive_letter(path, len) && path_is_sep(style, path[2]); 80 } 81 return path[0] == '/'; 82} 83 84static char *path_normalize_separators(path_style_t style, const char *path, size_t len) { 85 char *result = malloc(len + 1); 86 if (!result) return NULL; 87 88 for (size_t i = 0; i < len; i++) { 89 if (style == PATH_STYLE_WIN32 && (path[i] == '\\' || path[i] == '/')) 90 result[i] = '\\'; 91 else result[i] = path[i]; 92 } 93 94 result[len] = '\0'; 95 return result; 96} 97 98static size_t path_root_length(path_style_t style, const char *path, size_t len) { 99 if (!path || len == 0) return 0; 100 if (style != PATH_STYLE_WIN32) return path[0] == '/' ? 1 : 0; 101 102 if (len >= 2 && path_is_sep(style, path[0]) && path_is_sep(style, path[1])) { 103 size_t i = 2; 104 int parts = 0; 105 106 while (i < len) { 107 while (i < len && path_is_sep(style, path[i])) i++; 108 109 size_t start = i; 110 while (i < len && !path_is_sep(style, path[i])) i++; 111 112 if (i == start) continue; 113 parts++; 114 115 if (parts == 2) { 116 while (i < len && path_is_sep(style, path[i])) i++; 117 return i; 118 }} 119 120 return 2; 121 } 122 123 if (path_has_drive_letter(path, len)) 124 return (len >= 3 && path_is_sep(style, path[2])) ? 3 : 2; 125 126 return path_is_sep(style, path[0]) ? 1 : 0; 127} 128 129static bool path_is_drive_relative(path_style_t style, const char *path, size_t len) { 130 return style == PATH_STYLE_WIN32 && path_has_drive_letter(path, len) && !(len >= 3 && path_is_sep(style, path[2])); 131} 132 133typedef struct { 134 char *from_norm; 135 char *to_norm; 136 char **from_segs; 137 char **to_segs; 138 size_t *from_lens; 139 size_t *to_lens; 140 int from_count; 141 int to_count; 142 size_t from_root_len; 143 size_t to_root_len; 144 int common; 145 char *result; 146 size_t result_cap; 147 size_t pos; 148} path_relative_ctx_t; 149 150static size_t path_trimmed_end(path_style_t style, const char *path, size_t len) { 151 size_t root_len = path_root_length(style, path, len); 152 153 while (len > root_len && path_is_sep(style, path[len - 1])) len--; 154 return len; 155} 156 157static size_t path_basename_start(path_style_t style, const char *path, size_t len) { 158 size_t root_len = path_root_length(style, path, len); 159 size_t end = path_trimmed_end(style, path, len); 160 size_t start = end; 161 162 while (start > root_len && !path_is_sep(style, path[start - 1])) start--; 163 return start; 164} 165 166static ant_value_t path_make_string(ant_t *js, const char *src, size_t len) { 167 return js_mkstr(js, src, len); 168} 169 170static ant_value_t builtin_path_basename(ant_t *js, ant_value_t *args, int nargs) { 171 path_style_t style = path_current_style(js); 172 if (nargs < 1) return js_mkerr(js, "basename() requires a path argument"); 173 if (vtype(args[0]) != T_STR) return js_mkerr(js, "basename() path must be a string"); 174 175 size_t path_len; 176 char *path = js_getstr(js, args[0], &path_len); 177 if (!path || path_len == 0) return js_mkstr(js, "", 0); 178 179 size_t end = path_trimmed_end(style, path, path_len); 180 size_t start = path_basename_start(style, path, path_len); 181 const char *base = path + start; 182 size_t base_len = end > start ? end - start : 0; 183 184 if (nargs >= 2 && vtype(args[1]) == T_STR) { 185 size_t ext_len; 186 char *ext = js_getstr(js, args[1], &ext_len); 187 188 if (ext && ext_len > 0 && base_len >= ext_len) { 189 if (memcmp(base + base_len - ext_len, ext, ext_len) == 0) base_len -= ext_len; 190 } 191 } 192 193 return path_make_string(js, base, base_len); 194} 195 196// path.dirname(path) 197static ant_value_t builtin_path_dirname(ant_t *js, ant_value_t *args, int nargs) { 198 path_style_t style = path_current_style(js); 199 200 if (nargs < 1) return js_mkerr(js, "dirname() requires a path argument"); 201 if (vtype(args[0]) != T_STR) return js_mkerr(js, "dirname() path must be a string"); 202 203 size_t path_len; 204 char *path = js_getstr(js, args[0], &path_len); 205 206 if (!path || path_len == 0) return js_mkstr(js, ".", 1); 207 char *normalized = path_normalize_separators(style, path, path_len); 208 209 size_t root_len = 0; 210 size_t end = 0; 211 size_t cut = 0; 212 213 if (!normalized) return js_mkerr(js, "Out of memory"); 214 root_len = path_root_length(style, normalized, path_len); 215 end = path_trimmed_end(style, normalized, path_len); 216 cut = end; 217 218 while (cut > root_len && !path_is_sep(style, normalized[cut - 1])) cut--; 219 while (cut > root_len && path_is_sep(style, normalized[cut - 1])) cut--; 220 221 if (cut == 0) { 222 free(normalized); 223 return js_mkstr(js, ".", 1); 224 } 225 226 if (cut < root_len) cut = root_len; 227 228 ant_value_t result = path_make_string(js, normalized, cut); 229 free(normalized); 230 return result; 231} 232 233// path.extname(path) 234static ant_value_t builtin_path_extname(ant_t *js, ant_value_t *args, int nargs) { 235 path_style_t style = path_current_style(js); 236 237 if (nargs < 1) return js_mkerr(js, "extname() requires a path argument"); 238 if (vtype(args[0]) != T_STR) return js_mkerr(js, "extname() path must be a string"); 239 240 size_t path_len; 241 char *path = js_getstr(js, args[0], &path_len); 242 if (!path || path_len == 0) return js_mkstr(js, "", 0); 243 244 size_t start = path_basename_start(style, path, path_len); 245 size_t end = path_trimmed_end(style, path, path_len); 246 const char *base = path + start; 247 size_t base_len = end > start ? end - start : 0; 248 249 for (size_t i = base_len; i > 0; i--) { 250 if (base[i - 1] != '.') continue; 251 if (i - 1 == 0) break; 252 return path_make_string(js, base + i - 1, base_len - (i - 1)); 253 } 254 255 return js_mkstr(js, "", 0); 256} 257 258static int is_dotdot(const char *seg, size_t len) { 259 return len == 2 && seg[0] == '.' && seg[1] == '.'; 260} 261 262static char* normalize_path_full(path_style_t style, const char *path, size_t path_len) { 263 char *normalized = NULL; 264 char **segments = NULL; 265 size_t *seg_lens = NULL; 266 267 char *result = NULL; 268 char sep = path_sep_char(style); 269 270 size_t root_len = 0; 271 bool drive_relative = false; 272 273 if (path_len == 0) return strdup("."); 274 275 normalized = path_normalize_separators(style, path, path_len); 276 if (!normalized) goto fail; 277 278 segments = malloc(path_len * sizeof(char*)); 279 seg_lens = malloc(path_len * sizeof(size_t)); 280 if (!segments || !seg_lens) goto fail; 281 282 root_len = path_root_length(style, normalized, path_len); 283 drive_relative = path_is_drive_relative(style, normalized, path_len); 284 int seg_count = 0; 285 286 char *start = normalized + root_len; 287 char *seg_start = start; 288 289 for (char *p = start; ; p++) { 290 if (!path_is_sep(style, *p) && *p != '\0') continue; 291 292 size_t len = p - seg_start; 293 if (len == 0 || (len == 1 && seg_start[0] == '.')) goto next; 294 295 if (is_dotdot(seg_start, len)) { 296 if (seg_count > 0 && !is_dotdot(segments[seg_count - 1], seg_lens[seg_count - 1])) seg_count--; 297 else if (root_len == 0 || drive_relative) goto add_segment; 298 goto next; 299 } 300 301 add_segment: 302 segments[seg_count] = seg_start; 303 seg_lens[seg_count] = len; 304 seg_count++; 305 306 next: 307 if (*p == '\0') break; 308 seg_start = p + 1; 309 } 310 311 size_t result_len = root_len; 312 for (int i = 0; i < seg_count; i++) { 313 result_len += seg_lens[i]; 314 if (i < seg_count - 1) result_len++; 315 } 316 if (result_len == 0) result_len = 1; 317 318 result = malloc(result_len + 1); 319 if (!result) goto fail; 320 321 size_t pos = 0; 322 if (root_len > 0) { 323 memcpy(result + pos, normalized, root_len); 324 pos += root_len; 325 } 326 327 for (int i = 0; i < seg_count; i++) { 328 if (pos > 0 && result[pos - 1] != sep && !(drive_relative && pos == root_len)) 329 result[pos++] = sep; 330 memcpy(result + pos, segments[i], seg_lens[i]); 331 pos += seg_lens[i]; 332 } 333 334 if (pos == 0) result[pos++] = '.'; 335 if (path_len > root_len && path_is_sep(style, path[path_len - 1]) 336 && pos > 0 && !path_is_sep(style, result[pos - 1])) { 337 result = realloc(result, pos + 2); 338 if (!result) goto fail; 339 result[pos++] = sep; 340 } 341 342 result[pos] = '\0'; 343 free(segments); 344 free(seg_lens); 345 free(normalized); 346 347 return result; 348 349fail: 350 free(segments); 351 free(seg_lens); 352 free(normalized); 353 return NULL; 354} 355 356// path.normalize(path) 357static ant_value_t builtin_path_normalize(ant_t *js, ant_value_t *args, int nargs) { 358 path_style_t style = path_current_style(js); 359 if (nargs < 1) return js_mkerr(js, "normalize() requires a path argument"); 360 if (vtype(args[0]) != T_STR) return js_mkerr(js, "normalize() path must be a string"); 361 362 size_t path_len; 363 char *path = js_getstr(js, args[0], &path_len); 364 if (!path || path_len == 0) return js_mkstr(js, ".", 1); 365 366 char *result = normalize_path_full(style, path, path_len); 367 if (!result) return js_mkerr(js, "Out of memory"); 368 369 ant_value_t ret = js_mkstr(js, result, strlen(result)); 370 free(result); 371 return ret; 372} 373 374// path.join(...paths) 375static ant_value_t builtin_path_join(ant_t *js, ant_value_t *args, int nargs) { 376 path_style_t style = path_current_style(js); 377 char sep = path_sep_char(style); 378 if (nargs < 1) return js_mkstr(js, ".", 1); 379 380 size_t total_len = 0; 381 char **segments = malloc(nargs * sizeof(char*)); 382 size_t *lengths = malloc(nargs * sizeof(size_t)); 383 384 if (!segments || !lengths) { 385 free(segments); 386 free(lengths); 387 return js_mkerr(js, "Out of memory"); 388 } 389 390 int valid_segments = 0; 391 392 for (int i = 0; i < nargs; i++) { 393 if (vtype(args[i]) == T_STR) { 394 segments[valid_segments] = js_getstr(js, args[i], &lengths[valid_segments]); 395 if (segments[valid_segments] && lengths[valid_segments] > 0) { 396 total_len += lengths[valid_segments] + 1; // +1 for separator 397 valid_segments++; 398 }}} 399 400 if (valid_segments == 0) { 401 free(segments); 402 free(lengths); 403 return js_mkstr(js, ".", 1); 404 } 405 406 char *result = malloc(total_len + 1); 407 if (!result) { 408 free(segments); 409 free(lengths); 410 return js_mkerr(js, "Out of memory"); 411 } 412 413 size_t pos = 0; 414 for (int i = 0; i < valid_segments; i++) { 415 if (i > 0 && pos > 0 && result[pos - 1] != sep) { 416 result[pos++] = sep; 417 } 418 419 size_t start = 0; 420 if (i > 0 && path_is_sep(style, segments[i][0])) start = 1; 421 422 size_t seg_len = lengths[i]; 423 while (seg_len > start + 1 && path_is_sep(style, segments[i][seg_len - 1])) seg_len--; 424 425 if (seg_len > start) { 426 memcpy(result + pos, segments[i] + start, seg_len - start); 427 pos += seg_len - start; 428 } 429 } 430 431 if ( 432 valid_segments > 0 && lengths[valid_segments - 1] > 0 433 && path_is_sep(style, segments[valid_segments - 1][lengths[valid_segments - 1] - 1]) 434 && pos > 0 && !path_is_sep(style, result[pos - 1]) 435 ) result[pos++] = sep; 436 437 result[pos] = '\0'; 438 char *normalized = normalize_path_full(style, result, pos); 439 440 free(result); 441 free(segments); 442 free(lengths); 443 444 if (!normalized) return js_mkerr(js, "Out of memory"); 445 ant_value_t ret = js_mkstr(js, normalized, strlen(normalized)); 446 free(normalized); 447 448 return ret; 449} 450 451// path.resolve(...paths) 452static ant_value_t builtin_path_resolve(ant_t *js, ant_value_t *args, int nargs) { 453 path_style_t style = path_current_style(js); 454 455 char cwd[PATH_MAX]; 456 char *cwd_norm = NULL; 457 char *joined = NULL; 458 char *normalized = NULL; 459 char drive_prefix[3] = {0}; 460 461 bool saw_absolute = false; 462 char sep = path_sep_char(style); 463 464 if (getcwd(cwd, sizeof(cwd)) == NULL) 465 return js_mkerr(js, "Failed to get current working directory"); 466 467 cwd_norm = path_normalize_separators(style, cwd, strlen(cwd)); 468 if (!cwd_norm) return js_mkerr(js, "Out of memory"); 469 470 for (int i = nargs - 1; i >= 0; i--) { 471 char *next = NULL; 472 size_t seg_len = 0; 473 size_t joined_len = 0; 474 size_t next_len = 0; 475 char *segment = NULL; 476 size_t prefix_len = 0; 477 478 if (vtype(args[i]) != T_STR) continue; 479 480 segment = js_getstr(js, args[i], &seg_len); 481 if (!segment || seg_len == 0) continue; 482 483 if (style == PATH_STYLE_WIN32 && path_is_drive_relative(style, segment, seg_len)) { 484 if (drive_prefix[0] == '\0') { 485 drive_prefix[0] = segment[0]; 486 drive_prefix[1] = ':'; 487 drive_prefix[2] = '\0'; 488 } 489 prefix_len = 2; 490 } 491 492 if (!joined) { 493 joined = strndup(segment + prefix_len, seg_len - prefix_len); 494 if (!joined) { 495 free(cwd_norm); 496 return js_mkerr(js, "Out of memory"); 497 } 498 } else { 499 joined_len = strlen(joined); 500 next_len = (seg_len - prefix_len) + 1 + joined_len + 1; 501 next = malloc(next_len); 502 if (!next) { 503 free(cwd_norm); 504 free(joined); 505 return js_mkerr(js, "Out of memory"); 506 } 507 508 snprintf(next, next_len, "%.*s%c%s", (int)(seg_len - prefix_len), segment + prefix_len, sep, joined); 509 free(joined); 510 joined = next; 511 } 512 513 if (path_is_absolute_style(style, segment, seg_len)) { 514 saw_absolute = true; 515 break; 516 } 517 } 518 519 if (!joined) { 520 if (style == PATH_STYLE_WIN32 && drive_prefix[0] != '\0') { 521 size_t cwd_len = strlen(cwd_norm); 522 joined = malloc(2 + cwd_len + 1); 523 if (!joined) { 524 free(cwd_norm); 525 return js_mkerr(js, "Out of memory"); 526 } 527 snprintf(joined, 2 + cwd_len + 1, "%s%s", drive_prefix, cwd_norm); 528 } else { 529 joined = strdup(cwd_norm); 530 if (!joined) { 531 free(cwd_norm); 532 return js_mkerr(js, "Out of memory"); 533 } 534 } 535 } else if (!saw_absolute) { 536 size_t cwd_len = strlen(cwd_norm); 537 size_t joined_len = strlen(joined); 538 size_t prefix_len = (style == PATH_STYLE_WIN32 && drive_prefix[0] != '\0') ? 2 : 0; 539 char *next = malloc(prefix_len + cwd_len + 1 + joined_len + 1); 540 if (!next) { 541 free(cwd_norm); 542 free(joined); 543 return js_mkerr(js, "Out of memory"); 544 } 545 546 if (prefix_len > 0) 547 snprintf(next, prefix_len + cwd_len + 1 + joined_len + 1, "%s%s%c%s", drive_prefix, cwd_norm, sep, joined); 548 else snprintf(next, prefix_len + cwd_len + 1 + joined_len + 1, "%s%c%s", cwd_norm, sep, joined); 549 free(joined); 550 joined = next; 551 } 552 553 normalized = normalize_path_full(style, joined, strlen(joined)); 554 free(cwd_norm); 555 free(joined); 556 if (!normalized) return js_mkerr(js, "Out of memory"); 557 558 ant_value_t ret = js_mkstr(js, normalized, strlen(normalized)); 559 free(normalized); 560 return ret; 561} 562 563// path.relative(from, to) 564static ant_value_t builtin_path_relative(ant_t *js, ant_value_t *args, int nargs) { 565 path_style_t style = path_current_style(js); 566 path_relative_ctx_t rel = {0}; 567 568 char sep = path_sep_char(style); 569 if (nargs < 2) return js_mkerr(js, "relative() requires from and to arguments"); 570 if (vtype(args[0]) != T_STR) return js_mkerr(js, "relative() from must be a string"); 571 if (vtype(args[1]) != T_STR) return js_mkerr(js, "relative() to must be a string"); 572 573 size_t from_len, to_len; 574 char *from = js_getstr(js, args[0], &from_len); 575 char *to = js_getstr(js, args[1], &to_len); 576 577 if (!from || !to) return js_mkerr(js, "Failed to get arguments"); 578 if (from_len == to_len && strncmp(from, to, from_len) == 0) return js_mkstr(js, "", 0); 579 580 rel.from_norm = normalize_path_full(style, from, from_len); 581 rel.to_norm = normalize_path_full(style, to, to_len); 582 if (!rel.from_norm || !rel.to_norm) goto relative_fail; 583 584 rel.from_root_len = path_root_length(style, rel.from_norm, strlen(rel.from_norm)); 585 rel.to_root_len = path_root_length(style, rel.to_norm, strlen(rel.to_norm)); 586 587 if (style == PATH_STYLE_WIN32) { 588 if (rel.from_root_len != rel.to_root_len || strncasecmp(rel.from_norm, rel.to_norm, rel.from_root_len) != 0) { 589 ant_value_t out = js_mkstr(js, rel.to_norm, strlen(rel.to_norm)); 590 free(rel.from_norm); 591 free(rel.to_norm); 592 return out; 593 } 594 } else if (rel.from_root_len != rel.to_root_len || strncmp(rel.from_norm, rel.to_norm, rel.from_root_len) != 0) { 595 ant_value_t out = js_mkstr(js, rel.to_norm, strlen(rel.to_norm)); 596 free(rel.from_norm); 597 free(rel.to_norm); 598 return out; 599 } 600 601 rel.from_segs = malloc(strlen(rel.from_norm) * sizeof(char *)); 602 rel.to_segs = malloc(strlen(rel.to_norm) * sizeof(char *)); 603 rel.from_lens = malloc(strlen(rel.from_norm) * sizeof(size_t)); 604 rel.to_lens = malloc(strlen(rel.to_norm) * sizeof(size_t)); 605 if (!rel.from_segs || !rel.to_segs || !rel.from_lens || !rel.to_lens) goto relative_fail; 606 607 for (size_t i = rel.from_root_len, start = rel.from_root_len;; i++) { 608 if (rel.from_norm[i] != '\0' && !path_is_sep(style, rel.from_norm[i])) continue; 609 if (i > start) { 610 rel.from_segs[rel.from_count] = rel.from_norm + start; 611 rel.from_lens[rel.from_count++] = i - start; 612 } 613 if (rel.from_norm[i] == '\0') break; 614 start = i + 1; 615 } 616 617 for (size_t i = rel.to_root_len, start = rel.to_root_len;; i++) { 618 if (rel.to_norm[i] != '\0' && !path_is_sep(style, rel.to_norm[i])) continue; 619 if (i > start) { 620 rel.to_segs[rel.to_count] = rel.to_norm + start; 621 rel.to_lens[rel.to_count++] = i - start; 622 } 623 if (rel.to_norm[i] == '\0') break; 624 start = i + 1; 625 } 626 627 while (rel.common < rel.from_count && rel.common < rel.to_count) { 628 bool equal = false; 629 if (rel.from_lens[rel.common] == rel.to_lens[rel.common]) { 630 equal = style == PATH_STYLE_WIN32 631 ? strncasecmp(rel.from_segs[rel.common], rel.to_segs[rel.common], rel.from_lens[rel.common]) == 0 632 : strncmp(rel.from_segs[rel.common], rel.to_segs[rel.common], rel.from_lens[rel.common]) == 0; 633 } 634 if (!equal) break; 635 rel.common++; 636 } 637 638 rel.result_cap = strlen(rel.to_norm) + (size_t)(rel.from_count - rel.common) * 3 + 2; 639 rel.result = malloc(rel.result_cap); 640 if (!rel.result) goto relative_fail; 641 642 for (int i = rel.common; i < rel.from_count; i++) { 643 if (rel.pos > 0) rel.result[rel.pos++] = sep; 644 rel.result[rel.pos++] = '.'; 645 rel.result[rel.pos++] = '.'; 646 } 647 648 for (int i = rel.common; i < rel.to_count; i++) { 649 if (rel.pos > 0) rel.result[rel.pos++] = sep; 650 memcpy(rel.result + rel.pos, rel.to_segs[i], rel.to_lens[i]); 651 rel.pos += rel.to_lens[i]; 652 } 653 654 if (rel.pos == 0) rel.result[rel.pos++] = '.'; 655 rel.result[rel.pos] = '\0'; 656 657 ant_value_t out = js_mkstr(js, rel.result, rel.pos); 658 free(rel.result); 659 free(rel.from_norm); 660 free(rel.to_norm); 661 free(rel.from_segs); 662 free(rel.to_segs); 663 free(rel.from_lens); 664 free(rel.to_lens); 665 return out; 666 667relative_fail: 668 free(rel.result); 669 free(rel.from_norm); 670 free(rel.to_norm); 671 free(rel.from_segs); 672 free(rel.to_segs); 673 free(rel.from_lens); 674 free(rel.to_lens); 675 return js_mkerr(js, "Out of memory"); 676} 677 678// path.isAbsolute(path) 679static ant_value_t builtin_path_isAbsolute(ant_t *js, ant_value_t *args, int nargs) { 680 path_style_t style = path_current_style(js); 681 if (nargs < 1) return js_mkerr(js, "isAbsolute() requires a path argument"); 682 if (vtype(args[0]) != T_STR) return js_mkerr(js, "isAbsolute() path must be a string"); 683 684 size_t path_len; 685 char *path = js_getstr(js, args[0], &path_len); 686 if (!path || path_len == 0) return js_false; 687 688 return js_bool(path_is_absolute_style(style, path, path_len)); 689} 690 691// path.parse(path) 692static ant_value_t builtin_path_parse(ant_t *js, ant_value_t *args, int nargs) { 693 path_style_t style = path_current_style(js); 694 if (nargs < 1) return js_mkerr(js, "parse() requires a path argument"); 695 if (vtype(args[0]) != T_STR) return js_mkerr(js, "parse() path must be a string"); 696 697 size_t path_len; 698 char *path = js_getstr(js, args[0], &path_len); 699 if (!path) return js_mkerr(js, "Failed to get path string"); 700 701 ant_value_t result = js_mkobj(js); 702 char *normalized = path_normalize_separators(style, path, path_len); 703 size_t root_len = 0; 704 size_t start = 0; 705 size_t end = 0; 706 const char *base = NULL; 707 size_t base_len = 0; 708 if (!normalized) return js_mkerr(js, "Out of memory"); 709 710 root_len = path_root_length(style, normalized, path_len); 711 js_set(js, result, "root", path_make_string(js, normalized, root_len)); 712 713 end = path_trimmed_end(style, normalized, path_len); 714 start = path_basename_start(style, normalized, path_len); 715 base = normalized + start; 716 base_len = end > start ? end - start : 0; 717 718 if (start == 0) js_set(js, result, "dir", js_mkstr(js, ".", 1)); 719 else { 720 size_t dir_len = start; 721 while (dir_len > root_len && path_is_sep(style, normalized[dir_len - 1])) dir_len--; 722 js_set(js, result, "dir", path_make_string(js, normalized, dir_len)); 723 } 724 725 js_set(js, result, "base", path_make_string(js, base, base_len)); 726 727 for (size_t i = base_len; i > 0; i--) { 728 if (base[i - 1] != '.') continue; 729 if (i - 1 == 0) break; 730 js_set(js, result, "ext", path_make_string(js, base + i - 1, base_len - (i - 1))); 731 js_set(js, result, "name", path_make_string(js, base, i - 1)); 732 free(normalized); 733 return result; 734 } 735 736 js_set(js, result, "ext", js_mkstr(js, "", 0)); 737 js_set(js, result, "name", path_make_string(js, base, base_len)); 738 free(normalized); 739 return result; 740} 741 742// path.format(pathObject) 743static ant_value_t builtin_path_format(ant_t *js, ant_value_t *args, int nargs) { 744 path_style_t style = path_current_style(js); 745 746 char sep = path_sep_char(style); 747 if (nargs < 1) return js_mkerr(js, "format() requires a path object argument"); 748 if (!is_special_object(args[0])) return js_mkerr(js, "format() argument must be an object"); 749 750 ant_value_t obj = args[0]; 751 752 ant_value_t dir_val = js_get(js, obj, "dir"); 753 ant_value_t root_val = js_get(js, obj, "root"); 754 ant_value_t base_val = js_get(js, obj, "base"); 755 ant_value_t name_val = js_get(js, obj, "name"); 756 ant_value_t ext_val = js_get(js, obj, "ext"); 757 758 char result[PATH_MAX] = {0}; 759 size_t pos = 0; 760 761 if (vtype(dir_val) == T_STR) { 762 size_t len; 763 char *str = js_getstr(js, dir_val, &len); 764 if (str && len > 0 && pos + len < PATH_MAX) { 765 memcpy(result + pos, str, len); 766 pos += len; 767 if (result[pos - 1] != sep && pos < PATH_MAX - 1) { 768 result[pos++] = sep; 769 } 770 } 771 } else if (vtype(root_val) == T_STR) { 772 size_t len; 773 char *str = js_getstr(js, root_val, &len); 774 if (str && len > 0 && pos + len < PATH_MAX) { 775 memcpy(result + pos, str, len); 776 pos += len; 777 } 778 } 779 780 if (vtype(base_val) == T_STR) { 781 size_t len; 782 char *str = js_getstr(js, base_val, &len); 783 if (str && len > 0 && pos + len < PATH_MAX) { 784 memcpy(result + pos, str, len); 785 pos += len; 786 } 787 } else { 788 if (vtype(name_val) == T_STR) { 789 size_t len; 790 char *str = js_getstr(js, name_val, &len); 791 if (str && len > 0 && pos + len < PATH_MAX) { 792 memcpy(result + pos, str, len); 793 pos += len; 794 } 795 } 796 if (vtype(ext_val) == T_STR) { 797 size_t len; 798 char *str = js_getstr(js, ext_val, &len); 799 if (str && len > 0 && pos + len < PATH_MAX) { 800 memcpy(result + pos, str, len); 801 pos += len; 802 } 803 } 804 } 805 806 return js_mkstr(js, result, pos); 807} 808 809typedef struct { 810 ant_value_t basename; 811 ant_value_t dirname; 812 ant_value_t extname; 813 ant_value_t join; 814 ant_value_t normalize; 815 ant_value_t resolve; 816 ant_value_t relative; 817 ant_value_t isAbsolute; 818 ant_value_t parse; 819 ant_value_t format; 820} path_api_t; 821 822static path_api_t path_build_api_for_style(ant_t *js, path_style_t style) { 823ant_value_t style_value = js_mknum((double)style); 824return (path_api_t){ 825 .basename = js_heavy_mkfun(js, builtin_path_basename, style_value), 826 .dirname = js_heavy_mkfun(js, builtin_path_dirname, style_value), 827 .extname = js_heavy_mkfun(js, builtin_path_extname, style_value), 828 .join = js_heavy_mkfun(js, builtin_path_join, style_value), 829 .normalize = js_heavy_mkfun(js, builtin_path_normalize, style_value), 830 .resolve = js_heavy_mkfun(js, builtin_path_resolve, style_value), 831 .relative = js_heavy_mkfun(js, builtin_path_relative, style_value), 832 .isAbsolute = js_heavy_mkfun(js, builtin_path_isAbsolute, style_value), 833 .parse = js_heavy_mkfun(js, builtin_path_parse, style_value), 834 .format = js_heavy_mkfun(js, builtin_path_format, style_value) 835};} 836 837static void path_apply_api(ant_t *js, ant_value_t target, const path_api_t *api) { 838 js_set(js, target, "basename", api->basename); 839 js_set(js, target, "dirname", api->dirname); 840 js_set(js, target, "extname", api->extname); 841 js_set(js, target, "join", api->join); 842 js_set(js, target, "normalize", api->normalize); 843 js_set(js, target, "resolve", api->resolve); 844 js_set(js, target, "relative", api->relative); 845 js_set(js, target, "isAbsolute", api->isAbsolute); 846 js_set(js, target, "parse", api->parse); 847 js_set(js, target, "format", api->format); 848} 849 850static ant_value_t path_make_variant(ant_t *js, const path_api_t *api, path_style_t style) { 851 ant_value_t variant = js_mkobj(js); 852 path_apply_api(js, variant, api); 853 854 js_set(js, variant, "sep", js_mkstr(js, path_sep_str(style), 1)); 855 js_set(js, variant, "delimiter", js_mkstr(js, path_delimiter_str(style), 1)); 856 js_set(js, variant, "default", variant); 857 858 return variant; 859} 860 861ant_value_t path_library(ant_t *js) { 862 path_api_t api = path_build_api_for_style(js, path_host_style()); 863 path_api_t posix_api = path_build_api_for_style(js, PATH_STYLE_POSIX); 864 path_api_t win32_api = path_build_api_for_style(js, PATH_STYLE_WIN32); 865 866 ant_value_t lib = path_make_variant(js, &api, path_host_style()); 867 ant_value_t posix = path_make_variant(js, &posix_api, PATH_STYLE_POSIX); 868 ant_value_t win32 = path_make_variant(js, &win32_api, PATH_STYLE_WIN32); 869 870 js_set(js, lib, "posix", posix); 871 js_set(js, lib, "win32", win32); 872 js_set_sym(js, lib, get_toStringTag_sym(), js_mkstr(js, "path", 4)); 873 js_set(js, lib, "default", lib); 874 875 return lib; 876} 877 878ant_value_t path_posix_library(ant_t *js) { 879 path_api_t api = path_build_api_for_style(js, PATH_STYLE_POSIX); 880 ant_value_t lib = path_make_variant(js, &api, PATH_STYLE_POSIX); 881 js_set_sym(js, lib, get_toStringTag_sym(), js_mkstr(js, "path", 4)); 882 return lib; 883} 884 885ant_value_t path_win32_library(ant_t *js) { 886 path_api_t api = path_build_api_for_style(js, PATH_STYLE_WIN32); 887 ant_value_t lib = path_make_variant(js, &api, PATH_STYLE_WIN32); 888 js_set_sym(js, lib, get_toStringTag_sym(), js_mkstr(js, "path", 4)); 889 return lib; 890}