Something went wrong. Try again.
javascript for 🐜's, a tiny runtime with big ambitions antjs.org
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891#include <compat.h> // IWYU pragma: keep
#include <ctype.h>#include <stdio.h>#include <stdlib.h>#include <string.h>
#include "ant.h"#include "errors.h"#include "internal.h"#include "modules/symbol.h"
#ifndef PATH_MAX#define PATH_MAX 4096#endif
#ifdef _WIN32#define PATH_SEP '\\'#define PATH_SEP_STR "\\"#define PATH_DELIMITER ';'#else#define PATH_SEP '/'#define PATH_SEP_STR "/"#define PATH_DELIMITER ':'#endif
typedef enum { PATH_STYLE_POSIX = 1, PATH_STYLE_WIN32 = 2,} path_style_t;
static path_style_t path_host_style(void) {#ifdef _WIN32 return PATH_STYLE_WIN32;#else return PATH_STYLE_POSIX;#endif}
static path_style_t path_current_style(ant_t *js) { ant_value_t data = js_get_slot(js->current_func, SLOT_DATA); if (vtype(data) == T_NUM) { int style = (int)js_getnum(data); if (style == PATH_STYLE_WIN32) return PATH_STYLE_WIN32; if (style == PATH_STYLE_POSIX) return PATH_STYLE_POSIX; } return path_host_style();}
static bool path_style_is_windows(path_style_t style) { return style == PATH_STYLE_WIN32;}
static bool path_is_sep(path_style_t style, char ch) { if (style == PATH_STYLE_WIN32) return ch == '\\' || ch == '/'; return ch == '/';}
static char path_sep_char(path_style_t style) { return style == PATH_STYLE_WIN32 ? '\\' : '/';}
static const char *path_sep_str(path_style_t style) { return style == PATH_STYLE_WIN32 ? "\\" : "/";}
static const char *path_delimiter_str(path_style_t style) { return style == PATH_STYLE_WIN32 ? ";" : ":";}
static bool path_has_drive_letter(const char *path, size_t len) { return len >= 2 && isalpha((unsigned char)path[0]) && path[1] == ':';}
static bool path_is_absolute_style(path_style_t style, const char *path, size_t len) { if (!path || len == 0) return false; if (style == PATH_STYLE_WIN32) { if (path_is_sep(style, path[0])) return true; return len >= 3 && path_has_drive_letter(path, len) && path_is_sep(style, path[2]); } return path[0] == '/';}
static char *path_normalize_separators(path_style_t style, const char *path, size_t len) { char *result = malloc(len + 1); if (!result) return NULL;
for (size_t i = 0; i < len; i++) { if (style == PATH_STYLE_WIN32 && (path[i] == '\\' || path[i] == '/')) result[i] = '\\'; else result[i] = path[i]; }
result[len] = '\0'; return result;}
static size_t path_root_length(path_style_t style, const char *path, size_t len) { if (!path || len == 0) return 0; if (style != PATH_STYLE_WIN32) return path[0] == '/' ? 1 : 0;
if (len >= 2 && path_is_sep(style, path[0]) && path_is_sep(style, path[1])) { size_t i = 2; int parts = 0;
while (i < len) { while (i < len && path_is_sep(style, path[i])) i++; size_t start = i; while (i < len && !path_is_sep(style, path[i])) i++; if (i == start) continue; parts++; if (parts == 2) { while (i < len && path_is_sep(style, path[i])) i++; return i; }} return 2; }
if (path_has_drive_letter(path, len)) return (len >= 3 && path_is_sep(style, path[2])) ? 3 : 2;
return path_is_sep(style, path[0]) ? 1 : 0;}
static bool path_is_drive_relative(path_style_t style, const char *path, size_t len) { return style == PATH_STYLE_WIN32 && path_has_drive_letter(path, len) && !(len >= 3 && path_is_sep(style, path[2]));}
typedef struct { char *from_norm; char *to_norm; char **from_segs; char **to_segs; size_t *from_lens; size_t *to_lens; int from_count; int to_count; size_t from_root_len; size_t to_root_len; int common; char *result; size_t result_cap; size_t pos;} path_relative_ctx_t;
static size_t path_trimmed_end(path_style_t style, const char *path, size_t len) { size_t root_len = path_root_length(style, path, len);
while (len > root_len && path_is_sep(style, path[len - 1])) len--; return len;}
static size_t path_basename_start(path_style_t style, const char *path, size_t len) { size_t root_len = path_root_length(style, path, len); size_t end = path_trimmed_end(style, path, len); size_t start = end;
while (start > root_len && !path_is_sep(style, path[start - 1])) start--; return start;}
static ant_value_t path_make_string(ant_t *js, const char *src, size_t len) { return js_mkstr(js, src, len);}
static ant_value_t builtin_path_basename(ant_t *js, ant_value_t *args, int nargs) { path_style_t style = path_current_style(js); if (nargs < 1) return js_mkerr(js, "basename() requires a path argument"); if (vtype(args[0]) != T_STR) return js_mkerr(js, "basename() path must be a string"); size_t path_len; char *path = js_getstr(js, args[0], &path_len); if (!path || path_len == 0) return js_mkstr(js, "", 0);
size_t end = path_trimmed_end(style, path, path_len); size_t start = path_basename_start(style, path, path_len); const char *base = path + start; size_t base_len = end > start ? end - start : 0; if (nargs >= 2 && vtype(args[1]) == T_STR) { size_t ext_len; char *ext = js_getstr(js, args[1], &ext_len); if (ext && ext_len > 0 && base_len >= ext_len) { if (memcmp(base + base_len - ext_len, ext, ext_len) == 0) base_len -= ext_len; } } return path_make_string(js, base, base_len);}
// path.dirname(path)static ant_value_t builtin_path_dirname(ant_t *js, ant_value_t *args, int nargs) { path_style_t style = path_current_style(js); if (nargs < 1) return js_mkerr(js, "dirname() requires a path argument"); if (vtype(args[0]) != T_STR) return js_mkerr(js, "dirname() path must be a string"); size_t path_len; char *path = js_getstr(js, args[0], &path_len); if (!path || path_len == 0) return js_mkstr(js, ".", 1); char *normalized = path_normalize_separators(style, path, path_len); size_t root_len = 0; size_t end = 0; size_t cut = 0; if (!normalized) return js_mkerr(js, "Out of memory"); root_len = path_root_length(style, normalized, path_len); end = path_trimmed_end(style, normalized, path_len); cut = end;
while (cut > root_len && !path_is_sep(style, normalized[cut - 1])) cut--; while (cut > root_len && path_is_sep(style, normalized[cut - 1])) cut--;
if (cut == 0) { free(normalized); return js_mkstr(js, ".", 1); }
if (cut < root_len) cut = root_len;
ant_value_t result = path_make_string(js, normalized, cut); free(normalized); return result;}
// path.extname(path)static ant_value_t builtin_path_extname(ant_t *js, ant_value_t *args, int nargs) { path_style_t style = path_current_style(js); if (nargs < 1) return js_mkerr(js, "extname() requires a path argument"); if (vtype(args[0]) != T_STR) return js_mkerr(js, "extname() path must be a string"); size_t path_len; char *path = js_getstr(js, args[0], &path_len); if (!path || path_len == 0) return js_mkstr(js, "", 0);
size_t start = path_basename_start(style, path, path_len); size_t end = path_trimmed_end(style, path, path_len); const char *base = path + start; size_t base_len = end > start ? end - start : 0;
for (size_t i = base_len; i > 0; i--) { if (base[i - 1] != '.') continue; if (i - 1 == 0) break; return path_make_string(js, base + i - 1, base_len - (i - 1)); }
return js_mkstr(js, "", 0);}
static int is_dotdot(const char *seg, size_t len) { return len == 2 && seg[0] == '.' && seg[1] == '.';}
static char* normalize_path_full(path_style_t style, const char *path, size_t path_len) { char *normalized = NULL; char **segments = NULL; size_t *seg_lens = NULL; char *result = NULL; char sep = path_sep_char(style); size_t root_len = 0; bool drive_relative = false; if (path_len == 0) return strdup("."); normalized = path_normalize_separators(style, path, path_len); if (!normalized) goto fail; segments = malloc(path_len * sizeof(char*)); seg_lens = malloc(path_len * sizeof(size_t)); if (!segments || !seg_lens) goto fail; root_len = path_root_length(style, normalized, path_len); drive_relative = path_is_drive_relative(style, normalized, path_len); int seg_count = 0; char *start = normalized + root_len; char *seg_start = start; for (char *p = start; ; p++) { if (!path_is_sep(style, *p) && *p != '\0') continue; size_t len = p - seg_start; if (len == 0 || (len == 1 && seg_start[0] == '.')) goto next; if (is_dotdot(seg_start, len)) { if (seg_count > 0 && !is_dotdot(segments[seg_count - 1], seg_lens[seg_count - 1])) seg_count--; else if (root_len == 0 || drive_relative) goto add_segment; goto next; } add_segment: segments[seg_count] = seg_start; seg_lens[seg_count] = len; seg_count++; next: if (*p == '\0') break; seg_start = p + 1; } size_t result_len = root_len; for (int i = 0; i < seg_count; i++) { result_len += seg_lens[i]; if (i < seg_count - 1) result_len++; } if (result_len == 0) result_len = 1; result = malloc(result_len + 1); if (!result) goto fail; size_t pos = 0; if (root_len > 0) { memcpy(result + pos, normalized, root_len); pos += root_len; } for (int i = 0; i < seg_count; i++) { if (pos > 0 && result[pos - 1] != sep && !(drive_relative && pos == root_len)) result[pos++] = sep; memcpy(result + pos, segments[i], seg_lens[i]); pos += seg_lens[i]; } if (pos == 0) result[pos++] = '.'; if (path_len > root_len && path_is_sep(style, path[path_len - 1]) && pos > 0 && !path_is_sep(style, result[pos - 1])) { result = realloc(result, pos + 2); if (!result) goto fail; result[pos++] = sep; } result[pos] = '\0'; free(segments); free(seg_lens); free(normalized); return result;
fail: free(segments); free(seg_lens); free(normalized); return NULL;}
// path.normalize(path)static ant_value_t builtin_path_normalize(ant_t *js, ant_value_t *args, int nargs) { path_style_t style = path_current_style(js); if (nargs < 1) return js_mkerr(js, "normalize() requires a path argument"); if (vtype(args[0]) != T_STR) return js_mkerr(js, "normalize() path must be a string"); size_t path_len; char *path = js_getstr(js, args[0], &path_len); if (!path || path_len == 0) return js_mkstr(js, ".", 1); char *result = normalize_path_full(style, path, path_len); if (!result) return js_mkerr(js, "Out of memory"); ant_value_t ret = js_mkstr(js, result, strlen(result)); free(result); return ret;}
// path.join(...paths)static ant_value_t builtin_path_join(ant_t *js, ant_value_t *args, int nargs) { path_style_t style = path_current_style(js); char sep = path_sep_char(style); if (nargs < 1) return js_mkstr(js, ".", 1); size_t total_len = 0; char **segments = malloc(nargs * sizeof(char*)); size_t *lengths = malloc(nargs * sizeof(size_t)); if (!segments || !lengths) { free(segments); free(lengths); return js_mkerr(js, "Out of memory"); } int valid_segments = 0; for (int i = 0; i < nargs; i++) { if (vtype(args[i]) == T_STR) { segments[valid_segments] = js_getstr(js, args[i], &lengths[valid_segments]); if (segments[valid_segments] && lengths[valid_segments] > 0) { total_len += lengths[valid_segments] + 1; // +1 for separator valid_segments++; }}} if (valid_segments == 0) { free(segments); free(lengths); return js_mkstr(js, ".", 1); } char *result = malloc(total_len + 1); if (!result) { free(segments); free(lengths); return js_mkerr(js, "Out of memory"); } size_t pos = 0; for (int i = 0; i < valid_segments; i++) { if (i > 0 && pos > 0 && result[pos - 1] != sep) { result[pos++] = sep; } size_t start = 0; if (i > 0 && path_is_sep(style, segments[i][0])) start = 1; size_t seg_len = lengths[i]; while (seg_len > start + 1 && path_is_sep(style, segments[i][seg_len - 1])) seg_len--; if (seg_len > start) { memcpy(result + pos, segments[i] + start, seg_len - start); pos += seg_len - start; } } if ( valid_segments > 0 && lengths[valid_segments - 1] > 0 && path_is_sep(style, segments[valid_segments - 1][lengths[valid_segments - 1] - 1]) && pos > 0 && !path_is_sep(style, result[pos - 1]) ) result[pos++] = sep; result[pos] = '\0'; char *normalized = normalize_path_full(style, result, pos);
free(result); free(segments); free(lengths); if (!normalized) return js_mkerr(js, "Out of memory"); ant_value_t ret = js_mkstr(js, normalized, strlen(normalized)); free(normalized); return ret;}
// path.resolve(...paths)static ant_value_t builtin_path_resolve(ant_t *js, ant_value_t *args, int nargs) { path_style_t style = path_current_style(js); char cwd[PATH_MAX]; char *cwd_norm = NULL; char *joined = NULL; char *normalized = NULL; char drive_prefix[3] = {0}; bool saw_absolute = false; char sep = path_sep_char(style);
if (getcwd(cwd, sizeof(cwd)) == NULL) return js_mkerr(js, "Failed to get current working directory");
cwd_norm = path_normalize_separators(style, cwd, strlen(cwd)); if (!cwd_norm) return js_mkerr(js, "Out of memory");
for (int i = nargs - 1; i >= 0; i--) { char *next = NULL; size_t seg_len = 0; size_t joined_len = 0; size_t next_len = 0; char *segment = NULL; size_t prefix_len = 0;
if (vtype(args[i]) != T_STR) continue;
segment = js_getstr(js, args[i], &seg_len); if (!segment || seg_len == 0) continue;
if (style == PATH_STYLE_WIN32 && path_is_drive_relative(style, segment, seg_len)) { if (drive_prefix[0] == '\0') { drive_prefix[0] = segment[0]; drive_prefix[1] = ':'; drive_prefix[2] = '\0'; } prefix_len = 2; }
if (!joined) { joined = strndup(segment + prefix_len, seg_len - prefix_len); if (!joined) { free(cwd_norm); return js_mkerr(js, "Out of memory"); } } else { joined_len = strlen(joined); next_len = (seg_len - prefix_len) + 1 + joined_len + 1; next = malloc(next_len); if (!next) { free(cwd_norm); free(joined); return js_mkerr(js, "Out of memory"); }
snprintf(next, next_len, "%.*s%c%s", (int)(seg_len - prefix_len), segment + prefix_len, sep, joined); free(joined); joined = next; }
if (path_is_absolute_style(style, segment, seg_len)) { saw_absolute = true; break; } }
if (!joined) { if (style == PATH_STYLE_WIN32 && drive_prefix[0] != '\0') { size_t cwd_len = strlen(cwd_norm); joined = malloc(2 + cwd_len + 1); if (!joined) { free(cwd_norm); return js_mkerr(js, "Out of memory"); } snprintf(joined, 2 + cwd_len + 1, "%s%s", drive_prefix, cwd_norm); } else { joined = strdup(cwd_norm); if (!joined) { free(cwd_norm); return js_mkerr(js, "Out of memory"); } } } else if (!saw_absolute) { size_t cwd_len = strlen(cwd_norm); size_t joined_len = strlen(joined); size_t prefix_len = (style == PATH_STYLE_WIN32 && drive_prefix[0] != '\0') ? 2 : 0; char *next = malloc(prefix_len + cwd_len + 1 + joined_len + 1); if (!next) { free(cwd_norm); free(joined); return js_mkerr(js, "Out of memory"); }
if (prefix_len > 0) snprintf(next, prefix_len + cwd_len + 1 + joined_len + 1, "%s%s%c%s", drive_prefix, cwd_norm, sep, joined); else snprintf(next, prefix_len + cwd_len + 1 + joined_len + 1, "%s%c%s", cwd_norm, sep, joined); free(joined); joined = next; }
normalized = normalize_path_full(style, joined, strlen(joined)); free(cwd_norm); free(joined); if (!normalized) return js_mkerr(js, "Out of memory");
ant_value_t ret = js_mkstr(js, normalized, strlen(normalized)); free(normalized); return ret;}
// path.relative(from, to)static ant_value_t builtin_path_relative(ant_t *js, ant_value_t *args, int nargs) { path_style_t style = path_current_style(js); path_relative_ctx_t rel = {0}; char sep = path_sep_char(style); if (nargs < 2) return js_mkerr(js, "relative() requires from and to arguments"); if (vtype(args[0]) != T_STR) return js_mkerr(js, "relative() from must be a string"); if (vtype(args[1]) != T_STR) return js_mkerr(js, "relative() to must be a string"); size_t from_len, to_len; char *from = js_getstr(js, args[0], &from_len); char *to = js_getstr(js, args[1], &to_len); if (!from || !to) return js_mkerr(js, "Failed to get arguments"); if (from_len == to_len && strncmp(from, to, from_len) == 0) return js_mkstr(js, "", 0);
rel.from_norm = normalize_path_full(style, from, from_len); rel.to_norm = normalize_path_full(style, to, to_len); if (!rel.from_norm || !rel.to_norm) goto relative_fail;
rel.from_root_len = path_root_length(style, rel.from_norm, strlen(rel.from_norm)); rel.to_root_len = path_root_length(style, rel.to_norm, strlen(rel.to_norm));
if (style == PATH_STYLE_WIN32) { if (rel.from_root_len != rel.to_root_len || strncasecmp(rel.from_norm, rel.to_norm, rel.from_root_len) != 0) { ant_value_t out = js_mkstr(js, rel.to_norm, strlen(rel.to_norm)); free(rel.from_norm); free(rel.to_norm); return out; } } else if (rel.from_root_len != rel.to_root_len || strncmp(rel.from_norm, rel.to_norm, rel.from_root_len) != 0) { ant_value_t out = js_mkstr(js, rel.to_norm, strlen(rel.to_norm)); free(rel.from_norm); free(rel.to_norm); return out; }
rel.from_segs = malloc(strlen(rel.from_norm) * sizeof(char *)); rel.to_segs = malloc(strlen(rel.to_norm) * sizeof(char *)); rel.from_lens = malloc(strlen(rel.from_norm) * sizeof(size_t)); rel.to_lens = malloc(strlen(rel.to_norm) * sizeof(size_t)); if (!rel.from_segs || !rel.to_segs || !rel.from_lens || !rel.to_lens) goto relative_fail;
for (size_t i = rel.from_root_len, start = rel.from_root_len;; i++) { if (rel.from_norm[i] != '\0' && !path_is_sep(style, rel.from_norm[i])) continue; if (i > start) { rel.from_segs[rel.from_count] = rel.from_norm + start; rel.from_lens[rel.from_count++] = i - start; } if (rel.from_norm[i] == '\0') break; start = i + 1; }
for (size_t i = rel.to_root_len, start = rel.to_root_len;; i++) { if (rel.to_norm[i] != '\0' && !path_is_sep(style, rel.to_norm[i])) continue; if (i > start) { rel.to_segs[rel.to_count] = rel.to_norm + start; rel.to_lens[rel.to_count++] = i - start; } if (rel.to_norm[i] == '\0') break; start = i + 1; }
while (rel.common < rel.from_count && rel.common < rel.to_count) { bool equal = false; if (rel.from_lens[rel.common] == rel.to_lens[rel.common]) { equal = style == PATH_STYLE_WIN32 ? strncasecmp(rel.from_segs[rel.common], rel.to_segs[rel.common], rel.from_lens[rel.common]) == 0 : strncmp(rel.from_segs[rel.common], rel.to_segs[rel.common], rel.from_lens[rel.common]) == 0; } if (!equal) break; rel.common++; }
rel.result_cap = strlen(rel.to_norm) + (size_t)(rel.from_count - rel.common) * 3 + 2; rel.result = malloc(rel.result_cap); if (!rel.result) goto relative_fail;
for (int i = rel.common; i < rel.from_count; i++) { if (rel.pos > 0) rel.result[rel.pos++] = sep; rel.result[rel.pos++] = '.'; rel.result[rel.pos++] = '.'; }
for (int i = rel.common; i < rel.to_count; i++) { if (rel.pos > 0) rel.result[rel.pos++] = sep; memcpy(rel.result + rel.pos, rel.to_segs[i], rel.to_lens[i]); rel.pos += rel.to_lens[i]; }
if (rel.pos == 0) rel.result[rel.pos++] = '.'; rel.result[rel.pos] = '\0';
ant_value_t out = js_mkstr(js, rel.result, rel.pos); free(rel.result); free(rel.from_norm); free(rel.to_norm); free(rel.from_segs); free(rel.to_segs); free(rel.from_lens); free(rel.to_lens); return out;
relative_fail: free(rel.result); free(rel.from_norm); free(rel.to_norm); free(rel.from_segs); free(rel.to_segs); free(rel.from_lens); free(rel.to_lens); return js_mkerr(js, "Out of memory");}
// path.isAbsolute(path)static ant_value_t builtin_path_isAbsolute(ant_t *js, ant_value_t *args, int nargs) { path_style_t style = path_current_style(js); if (nargs < 1) return js_mkerr(js, "isAbsolute() requires a path argument"); if (vtype(args[0]) != T_STR) return js_mkerr(js, "isAbsolute() path must be a string"); size_t path_len; char *path = js_getstr(js, args[0], &path_len); if (!path || path_len == 0) return js_false;
return js_bool(path_is_absolute_style(style, path, path_len));}
// path.parse(path)static ant_value_t builtin_path_parse(ant_t *js, ant_value_t *args, int nargs) { path_style_t style = path_current_style(js); if (nargs < 1) return js_mkerr(js, "parse() requires a path argument"); if (vtype(args[0]) != T_STR) return js_mkerr(js, "parse() path must be a string"); size_t path_len; char *path = js_getstr(js, args[0], &path_len); if (!path) return js_mkerr(js, "Failed to get path string"); ant_value_t result = js_mkobj(js); char *normalized = path_normalize_separators(style, path, path_len); size_t root_len = 0; size_t start = 0; size_t end = 0; const char *base = NULL; size_t base_len = 0; if (!normalized) return js_mkerr(js, "Out of memory");
root_len = path_root_length(style, normalized, path_len); js_set(js, result, "root", path_make_string(js, normalized, root_len));
end = path_trimmed_end(style, normalized, path_len); start = path_basename_start(style, normalized, path_len); base = normalized + start; base_len = end > start ? end - start : 0;
if (start == 0) js_set(js, result, "dir", js_mkstr(js, ".", 1)); else { size_t dir_len = start; while (dir_len > root_len && path_is_sep(style, normalized[dir_len - 1])) dir_len--; js_set(js, result, "dir", path_make_string(js, normalized, dir_len)); }
js_set(js, result, "base", path_make_string(js, base, base_len));
for (size_t i = base_len; i > 0; i--) { if (base[i - 1] != '.') continue; if (i - 1 == 0) break; js_set(js, result, "ext", path_make_string(js, base + i - 1, base_len - (i - 1))); js_set(js, result, "name", path_make_string(js, base, i - 1)); free(normalized); return result; }
js_set(js, result, "ext", js_mkstr(js, "", 0)); js_set(js, result, "name", path_make_string(js, base, base_len)); free(normalized); return result;}
// path.format(pathObject)static ant_value_t builtin_path_format(ant_t *js, ant_value_t *args, int nargs) { path_style_t style = path_current_style(js); char sep = path_sep_char(style); if (nargs < 1) return js_mkerr(js, "format() requires a path object argument"); if (!is_special_object(args[0])) return js_mkerr(js, "format() argument must be an object"); ant_value_t obj = args[0]; ant_value_t dir_val = js_get(js, obj, "dir"); ant_value_t root_val = js_get(js, obj, "root"); ant_value_t base_val = js_get(js, obj, "base"); ant_value_t name_val = js_get(js, obj, "name"); ant_value_t ext_val = js_get(js, obj, "ext"); char result[PATH_MAX] = {0}; size_t pos = 0; if (vtype(dir_val) == T_STR) { size_t len; char *str = js_getstr(js, dir_val, &len); if (str && len > 0 && pos + len < PATH_MAX) { memcpy(result + pos, str, len); pos += len; if (result[pos - 1] != sep && pos < PATH_MAX - 1) { result[pos++] = sep; } } } else if (vtype(root_val) == T_STR) { size_t len; char *str = js_getstr(js, root_val, &len); if (str && len > 0 && pos + len < PATH_MAX) { memcpy(result + pos, str, len); pos += len; } } if (vtype(base_val) == T_STR) { size_t len; char *str = js_getstr(js, base_val, &len); if (str && len > 0 && pos + len < PATH_MAX) { memcpy(result + pos, str, len); pos += len; } } else { if (vtype(name_val) == T_STR) { size_t len; char *str = js_getstr(js, name_val, &len); if (str && len > 0 && pos + len < PATH_MAX) { memcpy(result + pos, str, len); pos += len; } } if (vtype(ext_val) == T_STR) { size_t len; char *str = js_getstr(js, ext_val, &len); if (str && len > 0 && pos + len < PATH_MAX) { memcpy(result + pos, str, len); pos += len; } } } return js_mkstr(js, result, pos);}
typedef struct { ant_value_t basename; ant_value_t dirname; ant_value_t extname; ant_value_t join; ant_value_t normalize; ant_value_t resolve; ant_value_t relative; ant_value_t isAbsolute; ant_value_t parse; ant_value_t format;} path_api_t;
static path_api_t path_build_api_for_style(ant_t *js, path_style_t style) {ant_value_t style_value = js_mknum((double)style);return (path_api_t){ .basename = js_heavy_mkfun(js, builtin_path_basename, style_value), .dirname = js_heavy_mkfun(js, builtin_path_dirname, style_value), .extname = js_heavy_mkfun(js, builtin_path_extname, style_value), .join = js_heavy_mkfun(js, builtin_path_join, style_value), .normalize = js_heavy_mkfun(js, builtin_path_normalize, style_value), .resolve = js_heavy_mkfun(js, builtin_path_resolve, style_value), .relative = js_heavy_mkfun(js, builtin_path_relative, style_value), .isAbsolute = js_heavy_mkfun(js, builtin_path_isAbsolute, style_value), .parse = js_heavy_mkfun(js, builtin_path_parse, style_value), .format = js_heavy_mkfun(js, builtin_path_format, style_value)};}
static void path_apply_api(ant_t *js, ant_value_t target, const path_api_t *api) { js_set(js, target, "basename", api->basename); js_set(js, target, "dirname", api->dirname); js_set(js, target, "extname", api->extname); js_set(js, target, "join", api->join); js_set(js, target, "normalize", api->normalize); js_set(js, target, "resolve", api->resolve); js_set(js, target, "relative", api->relative); js_set(js, target, "isAbsolute", api->isAbsolute); js_set(js, target, "parse", api->parse); js_set(js, target, "format", api->format);}
static ant_value_t path_make_variant(ant_t *js, const path_api_t *api, path_style_t style) { ant_value_t variant = js_mkobj(js); path_apply_api(js, variant, api); js_set(js, variant, "sep", js_mkstr(js, path_sep_str(style), 1)); js_set(js, variant, "delimiter", js_mkstr(js, path_delimiter_str(style), 1)); js_set(js, variant, "default", variant); return variant;}
ant_value_t path_library(ant_t *js) { path_api_t api = path_build_api_for_style(js, path_host_style()); path_api_t posix_api = path_build_api_for_style(js, PATH_STYLE_POSIX); path_api_t win32_api = path_build_api_for_style(js, PATH_STYLE_WIN32); ant_value_t lib = path_make_variant(js, &api, path_host_style()); ant_value_t posix = path_make_variant(js, &posix_api, PATH_STYLE_POSIX); ant_value_t win32 = path_make_variant(js, &win32_api, PATH_STYLE_WIN32);
js_set(js, lib, "posix", posix); js_set(js, lib, "win32", win32); js_set_sym(js, lib, get_toStringTag_sym(), js_mkstr(js, "path", 4)); js_set(js, lib, "default", lib); return lib;}
ant_value_t path_posix_library(ant_t *js) { path_api_t api = path_build_api_for_style(js, PATH_STYLE_POSIX); ant_value_t lib = path_make_variant(js, &api, PATH_STYLE_POSIX); js_set_sym(js, lib, get_toStringTag_sym(), js_mkstr(js, "path", 4)); return lib;}
ant_value_t path_win32_library(ant_t *js) { path_api_t api = path_build_api_for_style(js, PATH_STYLE_WIN32); ant_value_t lib = path_make_variant(js, &api, PATH_STYLE_WIN32); js_set_sym(js, lib, get_toStringTag_sym(), js_mkstr(js, "path", 4)); return lib;}