Something went wrong. Try again.
javascript for 🐜's, a tiny runtime with big ambitions antjs.org
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836#include <compat.h> // IWYU pragma: keep
#include <uv.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <stdbool.h>#include <sys/stat.h>#include <fcntl.h>#include <uthash.h>#include <utarray.h>#include <errno.h>
#include "ant.h"#include "ptr.h"#include "utf8.h"#include "utils.h"#include "base64.h"#include "errors.h"#include "watch.h"#include "internal.h"#include "runtime.h"#include "descriptors.h"
#include "gc/roots.h"#include "gc/modules.h"#include "silver/engine.h"
#include "modules/fs.h"#include "modules/date.h"#include "modules/buffer.h"#include "modules/events.h"#include "modules/stream.h"#include "modules/symbol.h"#include "modules/url.h"
typedef enum { FS_ENC_NONE = 0, FS_ENC_UTF8, FS_ENC_UTF16LE, FS_ENC_LATIN1, FS_ENC_BASE64, FS_ENC_BASE64URL, FS_ENC_HEX, FS_ENC_ASCII,} fs_encoding_t;
typedef enum { FS_OP_READ, FS_OP_WRITE, FS_OP_UNLINK, FS_OP_MKDIR, FS_OP_RMDIR, FS_OP_STAT, FS_OP_READ_BYTES, FS_OP_EXISTS, FS_OP_READDIR, FS_OP_ACCESS, FS_OP_REALPATH, FS_OP_WRITE_FD, FS_OP_READ_FD, FS_OP_OPEN, FS_OP_CLOSE, FS_OP_MKDTEMP, FS_OP_CHMOD, FS_OP_RENAME} fs_op_type_t;
typedef struct fs_request_s { uv_fs_t uv_req; ant_t *js; ant_value_t promise; ant_value_t target_buffer; ant_value_t callback_fn;
char *path; char *path2; char *data; char *error_msg; size_t data_len; size_t buf_offset;
fs_op_type_t op_type; fs_encoding_t encoding; uv_file fd;
int completed; int failed; int recursive; int error_code; int with_file_types;} fs_request_t;
typedef enum { FS_WATCH_MODE_EVENT = 0, FS_WATCH_MODE_STAT} fs_watch_mode_t;
typedef union { uv_fs_event_t event; uv_fs_poll_t poll;} fs_watcher_handle_t;
typedef struct fs_watcher_s { ant_t *js; ant_value_t obj; ant_value_t callback; fs_watcher_handle_t handle; uv_stat_t last_stat; struct fs_watcher_s *next_active; char *path; bool last_stat_valid; bool in_active_list; bool closing; bool handle_closed; bool finalized; bool persistent; fs_watch_mode_t mode;} fs_watcher_t;
typedef struct { bool persistent; bool recursive; ant_value_t listener;} fs_watch_options_t;
typedef struct { bool persistent; unsigned int interval_ms; ant_value_t listener;} fs_watchfile_options_t;
typedef struct { mode_t mode; double size, uid, gid; double atime_ms, mtime_ms, ctime_ms, birthtime_ms;} fs_stat_fields_t;
static ant_value_t g_dirent_proto = 0;static ant_value_t g_fswatcher_proto = 0;static ant_value_t g_fswatcher_ctor = 0;static ant_value_t g_filehandle_proto = 0;static ant_value_t g_readstream_proto = 0;static ant_value_t g_readstream_ctor = 0;static ant_value_t g_writestream_proto = 0;static ant_value_t g_writestream_ctor = 0;
static fs_watcher_t *active_watchers = NULL;static UT_array *pending_requests = NULL;
enum { FS_WATCHER_NATIVE_TAG = 0x46535754u, // FSWT FS_FILEHANDLE_NATIVE_TAG = 0x46534648u // FSFH};
static fs_watcher_t *fs_watcher_data(ant_value_t value) { if (!js_check_native_tag(value, FS_WATCHER_NATIVE_TAG)) return NULL; return (fs_watcher_t *)js_get_native_ptr(value);}
static void fs_add_active_watcher(fs_watcher_t *watcher) { if (!watcher || watcher->in_active_list) return; watcher->next_active = active_watchers; active_watchers = watcher; watcher->in_active_list = true;}
static void fs_remove_active_watcher(fs_watcher_t *watcher) { fs_watcher_t **it = NULL; if (!watcher || !watcher->in_active_list) return;
for (it = &active_watchers; *it; it = &(*it)->next_active) { if (*it != watcher) continue; *it = watcher->next_active; watcher->next_active = NULL; watcher->in_active_list = false; return; }}
static ant_value_t fs_call_value( ant_t *js, ant_value_t fn, ant_value_t this_val, ant_value_t *args, int nargs) { ant_value_t saved_this = js->this_val; ant_value_t result = js_mkundef();
js->this_val = this_val; if (vtype(fn) == T_CFUNC) result = js_as_cfunc(fn)(js, args, nargs); else result = sv_vm_call(js->vm, js, fn, this_val, args, nargs, NULL, false); js->this_val = saved_this; return result;}
static int parse_open_flags(ant_t *js, ant_value_t arg);static ant_value_t fs_coerce_path(ant_t *js, ant_value_t arg);
static ant_value_t fs_mk_errno_error( ant_t *js, int err_num, const char *syscall, const char *path, const char *dest);
static ant_value_t fs_mk_uv_error( ant_t *js, int uv_code, const char *syscall, const char *path, const char *dest);
static bool fs_parse_mode(ant_t *js, ant_value_t arg, mode_t *out) { if (vtype(arg) == T_NUM) { double mode = js_getnum(arg); if (mode < 0) return false; *out = (mode_t)mode; return true; }
if (vtype(arg) != T_STR) return false;
size_t len = 0; const char *str = js_getstr(js, arg, &len); if (!str) return false;
size_t start = 0; if (len > 2 && str[0] == '0' && (str[1] == 'o' || str[1] == 'O')) start = 2; if (start == len) return false;
mode_t mode = 0; for (size_t i = start; i < len; i++) { if (str[i] < '0' || str[i] > '7') return false; mode = (mode_t)((mode << 3) + (str[i] - '0')); }
*out = mode; return true;}
static ant_value_t fs_stream_error(ant_t *js, ant_value_t stream_obj, const char *op, int uv_code) { ant_value_t props = js_mkobj(js); ant_value_t path_val = js_get(js, stream_obj, "path"); const char *code = uv_err_name(uv_code);
if (code) js_set(js, props, "code", js_mkstr(js, code, strlen(code))); js_set(js, props, "errno", js_mknum((double)uv_code)); if (vtype(path_val) == T_STR) js_set(js, props, "path", path_val); return js_mkerr_props(js, JS_ERR_TYPE, props, "%s failed: %s", op, uv_strerror(uv_code));}
static ant_value_t fs_stream_push_chunk(ant_t *js, ant_value_t stream_obj, ant_value_t chunk) { return stream_readable_push(js, stream_obj, chunk, js_mkundef());}
static ant_value_t fs_stream_callback(ant_t *js, ant_value_t callback, ant_value_t value) { if (!is_callable(callback)) return js_mkundef(); return fs_call_value(js, callback, js_mkundef(), &value, 1);}
static int fs_stream_close_fd_sync(ant_t *js, ant_value_t stream_obj) { ant_value_t fd_val = js_get(js, stream_obj, "fd"); uv_fs_t req; int result = 0;
if (vtype(fd_val) != T_NUM) { js_set(js, stream_obj, "pending", js_false); js_set(js, stream_obj, "closed", js_true); return 0; }
result = uv_fs_close(uv_default_loop(), &req, (uv_file)js_getnum(fd_val), NULL); uv_fs_req_cleanup(&req);
js_set(js, stream_obj, "fd", js_mknull()); js_set(js, stream_obj, "pending", js_false); js_set(js, stream_obj, "closed", js_true); return result;}
static int fs_stream_open_fd_sync(ant_t *js, ant_value_t stream_obj) { ant_value_t fd_val = js_get(js, stream_obj, "fd"); ant_value_t path_val = js_get(js, stream_obj, "path"); ant_value_t mode_val = js_get(js, stream_obj, "mode"); ant_value_t flags_val = js_get_slot(stream_obj, SLOT_FS_FLAGS); size_t path_len = 0; const char *path = NULL; char *path_copy = NULL; uv_fs_t req; int flags = 0; int mode = 0666; int result = 0;
if (vtype(fd_val) == T_NUM) return (int)js_getnum(fd_val); if (vtype(path_val) != T_STR) return UV_EINVAL;
path = js_getstr(js, path_val, &path_len); if (!path) return UV_EINVAL;
path_copy = strndup(path, path_len); if (!path_copy) return UV_ENOMEM;
flags = (vtype(flags_val) == T_NUM) ? (int)js_getnum(flags_val) : O_RDONLY; if (vtype(mode_val) == T_NUM) mode = (int)js_getnum(mode_val);
result = uv_fs_open(uv_default_loop(), &req, path_copy, flags, mode, NULL); uv_fs_req_cleanup(&req); free(path_copy);
if (result < 0) return result;
js_set(js, stream_obj, "fd", js_mknum((double)result)); js_set(js, stream_obj, "pending", js_false); js_set(js, stream_obj, "closed", js_false);
ant_value_t open_arg = js_mknum((double)result); eventemitter_emit_args(js, stream_obj, "open", &open_arg, 1); eventemitter_emit_args(js, stream_obj, "ready", NULL, 0);
return result;}
static ant_value_t fs_stream_destroy(ant_t *js, ant_value_t *args, int nargs) { ant_value_t stream_obj = js_getthis(js); ant_value_t err = nargs > 0 ? args[0] : js_mknull(); ant_value_t callback = nargs > 1 ? args[1] : js_mkundef(); int result = fs_stream_close_fd_sync(js, stream_obj);
if (result < 0 && (is_null(err) || is_undefined(err))) err = fs_stream_error(js, stream_obj, "close", result); if (is_undefined(err)) err = js_mknull();
return fs_stream_callback(js, callback, err);}
static ant_value_t fs_stream_close(ant_t *js, ant_value_t *args, int nargs) { ant_value_t stream_obj = js_getthis(js);
if (nargs > 0 && is_callable(args[0])) { eventemitter_add_listener(js, stream_obj, "close", args[0], true); if (js_truthy(js, js_get(js, stream_obj, "closed"))) fs_call_value(js, args[0], js_mkundef(), NULL, 0); }
if (!js_truthy(js, js_get(js, stream_obj, "destroyed"))) { ant_value_t destroy_fn = js_getprop_fallback(js, stream_obj, "destroy"); if (is_callable(destroy_fn)) fs_call_value(js, destroy_fn, stream_obj, NULL, 0); }
return stream_obj;}
static ant_value_t fs_readstream__read(ant_t *js, ant_value_t *args, int nargs) { ant_value_t stream_obj = js_getthis(js); ant_value_t pos_val = js_get(js, stream_obj, "pos"); ant_value_t end_val = js_get(js, stream_obj, "end"); ant_value_t bytes_read_val = js_get(js, stream_obj, "bytesRead"); int fd = fs_stream_open_fd_sync(js, stream_obj); int64_t pos = (vtype(pos_val) == T_NUM) ? (int64_t)js_getnum(pos_val) : 0; int64_t end = (vtype(end_val) == T_NUM) ? (int64_t)js_getnum(end_val) : -1; size_t want = 16384; bool reached_eof = false;
if (fd < 0) { ant_value_t err = fs_stream_error(js, stream_obj, "open", fd); ant_value_t destroy_fn = js_getprop_fallback(js, stream_obj, "destroy"); if (is_callable(destroy_fn)) fs_call_value(js, destroy_fn, stream_obj, &err, 1); return js_mkundef(); }
if (nargs > 0 && vtype(args[0]) == T_NUM && js_getnum(args[0]) > 0) want = (size_t)js_getnum(args[0]);
if (end >= 0) { if (pos > end) { if (js_truthy(js, js_get(js, stream_obj, "autoClose"))) fs_stream_close_fd_sync(js, stream_obj); return fs_stream_push_chunk(js, stream_obj, js_mknull()); } if ((int64_t)want > (end - pos + 1)) want = (size_t)(end - pos + 1); }
ArrayBufferData *ab = create_array_buffer_data(want); if (!ab) { ant_value_t err = js_mkerr(js, "Failed to allocate ReadStream buffer"); ant_value_t destroy_fn = js_getprop_fallback(js, stream_obj, "destroy"); if (is_callable(destroy_fn)) fs_call_value(js, destroy_fn, stream_obj, &err, 1); return js_mkundef(); }
uv_fs_t req; uv_buf_t buf = uv_buf_init((char *)ab->data, (unsigned int)want); int result = uv_fs_read(uv_default_loop(), &req, fd, &buf, 1, pos, NULL); uv_fs_req_cleanup(&req);
if (result < 0) { ant_value_t err = fs_stream_error(js, stream_obj, "read", result); ant_value_t destroy_fn = js_getprop_fallback(js, stream_obj, "destroy"); free_array_buffer_data(ab); if (is_callable(destroy_fn)) fs_call_value(js, destroy_fn, stream_obj, &err, 1); return js_mkundef(); }
if (result == 0) { free_array_buffer_data(ab); if (js_truthy(js, js_get(js, stream_obj, "autoClose"))) (void)fs_stream_close_fd_sync(js, stream_obj); return fs_stream_push_chunk(js, stream_obj, js_mknull()); }
ant_value_t chunk = create_typed_array(js, TYPED_ARRAY_UINT8, ab, 0, (size_t)result, "Buffer"); if (vtype(chunk) == T_ERR) { free_array_buffer_data(ab); return chunk; }
if (result < (int)want) reached_eof = true; if (end >= 0 && (pos + result - 1) >= end) reached_eof = true;
js_set(js, stream_obj, "pos", js_mknum((double)(pos + result))); js_set(js, stream_obj, "bytesRead", js_mknum( (vtype(bytes_read_val) == T_NUM ? js_getnum(bytes_read_val) : 0.0) + (double)result )); fs_stream_push_chunk(js, stream_obj, chunk);
if (reached_eof) { if (js_truthy(js, js_get(js, stream_obj, "autoClose"))) fs_stream_close_fd_sync(js, stream_obj); return fs_stream_push_chunk(js, stream_obj, js_mknull()); }
return js_mkundef();}
static ant_value_t fs_writestream__write(ant_t *js, ant_value_t *args, int nargs) { ant_value_t stream_obj = js_getthis(js); ant_value_t callback = nargs > 2 ? args[2] : js_mkundef(); ant_value_t pos_val = js_get(js, stream_obj, "pos"); ant_value_t bytes_written_val = js_get(js, stream_obj, "bytesWritten"); const uint8_t *bytes = NULL; size_t len = 0; int fd = fs_stream_open_fd_sync(js, stream_obj); int64_t pos = (vtype(pos_val) == T_NUM) ? (int64_t)js_getnum(pos_val) : -1; size_t offset = 0;
if (fd < 0) return fs_stream_callback(js, callback, fs_stream_error(js, stream_obj, "open", fd));
if (vtype(args[0]) == T_STR) { bytes = (const uint8_t *)js_getstr(js, args[0], &len); } else if (!buffer_source_get_bytes(js, args[0], &bytes, &len)) { return fs_stream_callback(js, callback, js_mkerr(js, "WriteStream chunk must be a string or ArrayBufferView")); }
while (offset < len) { uv_fs_t req; uv_buf_t buf = uv_buf_init((char *)(bytes + offset), (unsigned int)(len - offset)); int result = uv_fs_write(uv_default_loop(), &req, fd, &buf, 1, pos, NULL); uv_fs_req_cleanup(&req);
if (result < 0) { if (js_truthy(js, js_get(js, stream_obj, "autoClose"))) fs_stream_close_fd_sync(js, stream_obj); return fs_stream_callback(js, callback, fs_stream_error(js, stream_obj, "write", result)); } if (result == 0) { if (js_truthy(js, js_get(js, stream_obj, "autoClose"))) fs_stream_close_fd_sync(js, stream_obj); return fs_stream_callback(js, callback, js_mkerr(js, "write failed: short write")); }
offset += (size_t)result; if (pos >= 0) pos += result; }
if (pos >= 0) js_set(js, stream_obj, "pos", js_mknum((double)pos)); js_set(js, stream_obj, "bytesWritten", js_mknum( (vtype(bytes_written_val) == T_NUM ? js_getnum(bytes_written_val) : 0.0) + (double)offset )); return fs_stream_callback(js, callback, js_mknull());}
static ant_value_t fs_writestream__final(ant_t *js, ant_value_t *args, int nargs) { ant_value_t stream_obj = js_getthis(js); ant_value_t callback = nargs > 0 ? args[0] : js_mkundef(); ant_value_t value = js_mknull();
if (js_truthy(js, js_get(js, stream_obj, "autoClose"))) { int result = fs_stream_close_fd_sync(js, stream_obj); if (result < 0) value = fs_stream_error(js, stream_obj, "close", result); }
return fs_stream_callback(js, callback, value);}
static ant_value_t fs_create_readstream_impl(ant_t *js, ant_value_t path_arg, ant_value_t options_arg, ant_value_t proto) { ant_value_t path_val = fs_coerce_path(js, path_arg); ant_value_t options = is_object_type(options_arg) ? options_arg : js_mkobj(js); ant_value_t stream_options = js_mkobj(js); ant_value_t hwm = js_get(js, options, "highWaterMark"); ant_value_t flags_raw = js_get(js, options, "flags"); ant_value_t fd_val = js_get(js, options, "fd"); ant_value_t start_val = js_get(js, options, "start"); ant_value_t end_val = js_get(js, options, "end"); ant_value_t mode_val = js_get(js, options, "mode"); ant_value_t auto_close_val = js_get(js, options, "autoClose"); ant_value_t emit_close_val = js_get(js, options, "emitClose"); ant_value_t stream_obj = 0; int flags = parse_open_flags(js, is_undefined(flags_raw) ? js_mkstr(js, "r", 1) : flags_raw); if (vtype(path_val) != T_STR) return js_mkerr(js, "ReadStream path must be a string"); if (vtype(hwm) == T_NUM && js_getnum(hwm) > 0) js_set(js, stream_options, "highWaterMark", hwm);
stream_obj = stream_construct_readable(js, proto, stream_options); if (is_err(stream_obj)) return stream_obj;
js_set(js, stream_obj, "_read", js_mkfun(fs_readstream__read)); js_set(js, stream_obj, "_destroy", js_mkfun(fs_stream_destroy)); js_set(js, stream_obj, "path", path_val); js_set(js, stream_obj, "flags", is_undefined(flags_raw) ? js_mkstr(js, "r", 1) : flags_raw); js_set(js, stream_obj, "mode", vtype(mode_val) == T_NUM ? mode_val : js_mknum(0666)); js_set(js, stream_obj, "fd", vtype(fd_val) == T_NUM ? fd_val : js_mkundef()); js_set(js, stream_obj, "pending", js_bool(vtype(fd_val) != T_NUM)); js_set(js, stream_obj, "closed", js_false); js_set(js, stream_obj, "autoClose", is_undefined(auto_close_val) ? js_true : js_bool(js_truthy(js, auto_close_val))); js_set(js, stream_obj, "emitClose", is_undefined(emit_close_val) ? js_true : js_bool(js_truthy(js, emit_close_val))); js_set(js, stream_obj, "bytesRead", js_mknum(0)); js_set(js, stream_obj, "start", vtype(start_val) == T_NUM ? start_val : js_mkundef()); js_set(js, stream_obj, "end", vtype(end_val) == T_NUM ? end_val : js_mkundef()); js_set(js, stream_obj, "pos", js_mknum(vtype(start_val) == T_NUM ? js_getnum(start_val) : 0.0)); js_set_slot(stream_obj, SLOT_FS_FLAGS, js_mknum((double)flags)); return stream_obj;}
static ant_value_t fs_create_writestream_impl(ant_t *js, ant_value_t path_arg, ant_value_t options_arg, ant_value_t proto) { ant_value_t path_val = fs_coerce_path(js, path_arg); ant_value_t options = is_object_type(options_arg) ? options_arg : js_mkobj(js); ant_value_t stream_options = js_mkobj(js); ant_value_t flags_raw = js_get(js, options, "flags"); ant_value_t fd_val = js_get(js, options, "fd"); ant_value_t start_val = js_get(js, options, "start"); ant_value_t mode_val = js_get(js, options, "mode"); ant_value_t auto_close_val = js_get(js, options, "autoClose"); ant_value_t emit_close_val = js_get(js, options, "emitClose"); ant_value_t hwm = js_get(js, options, "highWaterMark"); ant_value_t stream_obj = 0; int flags = parse_open_flags(js, is_undefined(flags_raw) ? js_mkstr(js, "w", 1) : flags_raw); double start_pos = (vtype(start_val) == T_NUM) ? js_getnum(start_val) : ((flags & O_APPEND) ? -1.0 : 0.0);
if (vtype(path_val) != T_STR) return js_mkerr(js, "WriteStream path must be a string"); if (vtype(hwm) == T_NUM && js_getnum(hwm) > 0) js_set(js, stream_options, "highWaterMark", hwm);
stream_obj = stream_construct_writable(js, proto, stream_options); if (is_err(stream_obj)) return stream_obj;
js_set(js, stream_obj, "_write", js_mkfun(fs_writestream__write)); js_set(js, stream_obj, "_final", js_mkfun(fs_writestream__final)); js_set(js, stream_obj, "_destroy", js_mkfun(fs_stream_destroy)); js_set(js, stream_obj, "path", path_val); js_set(js, stream_obj, "flags", is_undefined(flags_raw) ? js_mkstr(js, "w", 1) : flags_raw); js_set(js, stream_obj, "mode", vtype(mode_val) == T_NUM ? mode_val : js_mknum(0666)); js_set(js, stream_obj, "fd", vtype(fd_val) == T_NUM ? fd_val : js_mkundef()); js_set(js, stream_obj, "pending", js_bool(vtype(fd_val) != T_NUM)); js_set(js, stream_obj, "closed", js_false); js_set(js, stream_obj, "autoClose", is_undefined(auto_close_val) ? js_true : js_bool(js_truthy(js, auto_close_val))); js_set(js, stream_obj, "emitClose", is_undefined(emit_close_val) ? js_true : js_bool(js_truthy(js, emit_close_val))); js_set(js, stream_obj, "bytesWritten", js_mknum(0)); js_set(js, stream_obj, "start", vtype(start_val) == T_NUM ? start_val : js_mkundef()); js_set(js, stream_obj, "pos", js_mknum(start_pos)); js_set_slot(stream_obj, SLOT_FS_FLAGS, js_mknum((double)flags)); return stream_obj;}
static ant_value_t js_readstream_ctor(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "ReadStream() requires a path argument"); return fs_create_readstream_impl(js, args[0], nargs > 1 ? args[1] : js_mkundef(), g_readstream_proto);}
static ant_value_t js_writestream_ctor(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "WriteStream() requires a path argument"); return fs_create_writestream_impl(js, args[0], nargs > 1 ? args[1] : js_mkundef(), g_writestream_proto);}
static ant_value_t builtin_fs_createReadStream(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "createReadStream() requires a path argument"); return fs_create_readstream_impl(js, args[0], nargs > 1 ? args[1] : js_mkundef(), g_readstream_proto);}
static ant_value_t builtin_fs_createWriteStream(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "createWriteStream() requires a path argument"); return fs_create_writestream_impl(js, args[0], nargs > 1 ? args[1] : js_mkundef(), g_writestream_proto);}
static void fs_init_stream_constructors(ant_t *js) { if (g_readstream_ctor && g_writestream_ctor) return;
stream_init_constructors(js);
g_readstream_proto = js_mkobj(js); js_set_proto_init(g_readstream_proto, stream_readable_prototype(js)); js_set(js, g_readstream_proto, "close", js_mkfun(fs_stream_close)); js_set_sym(js, g_readstream_proto, get_toStringTag_sym(), js_mkstr(js, "ReadStream", 10)); g_readstream_ctor = js_make_ctor(js, js_readstream_ctor, g_readstream_proto, "ReadStream", 10); js_set_proto_init(g_readstream_ctor, stream_readable_constructor(js));
g_writestream_proto = js_mkobj(js); js_set_proto_init(g_writestream_proto, stream_writable_prototype(js)); js_set(js, g_writestream_proto, "close", js_mkfun(fs_stream_close)); js_set_sym(js, g_writestream_proto, get_toStringTag_sym(), js_mkstr(js, "WriteStream", 11)); g_writestream_ctor = js_make_ctor(js, js_writestream_ctor, g_writestream_proto, "WriteStream", 11); js_set_proto_init(g_writestream_ctor, stream_writable_constructor(js));}
static ant_value_t fs_make_date(ant_t *js, double ms) { ant_value_t obj = js_mkobj(js); ant_value_t date_proto = js_get_ctor_proto(js, "Date", 4); if (is_object_type(date_proto)) js_set_proto_init(obj, date_proto); js_set_slot(obj, SLOT_DATA, tov(ms)); js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_DATE)); return obj;}
static ant_value_t fs_stats_object_new(ant_t *js, const fs_stat_fields_t *f) { ant_value_t stat_obj = js_mkobj(js); ant_value_t proto = js_get_ctor_proto(js, "Stats", 5);
if (is_object_type(proto) || is_special_object(proto)) js_set_proto_init(stat_obj, proto);
js_set_slot(stat_obj, SLOT_DATA, js_mknum((double)f->mode)); js_set(js, stat_obj, "size", js_mknum(f->size)); js_set(js, stat_obj, "mode", js_mknum((double)f->mode)); js_set(js, stat_obj, "uid", js_mknum(f->uid)); js_set(js, stat_obj, "gid", js_mknum(f->gid));
js_set(js, stat_obj, "atimeMs", js_mknum(f->atime_ms)); js_set(js, stat_obj, "mtimeMs", js_mknum(f->mtime_ms)); js_set(js, stat_obj, "ctimeMs", js_mknum(f->ctime_ms)); js_set(js, stat_obj, "birthtimeMs", js_mknum(f->birthtime_ms));
js_set(js, stat_obj, "atime", fs_make_date(js, f->atime_ms)); js_set(js, stat_obj, "mtime", fs_make_date(js, f->mtime_ms)); js_set(js, stat_obj, "ctime", fs_make_date(js, f->ctime_ms)); js_set(js, stat_obj, "birthtime", fs_make_date(js, f->birthtime_ms)); return stat_obj;}
static double uv_ts_to_ms(uv_timespec_t ts) { return (double)ts.tv_sec * 1000.0 + (double)ts.tv_nsec / 1e6;}
static double posix_ts_to_ms(struct timespec ts) { return (double)ts.tv_sec * 1000.0 + (double)ts.tv_nsec / 1e6;}
#ifdef _WIN32 #define POSIX_ATIME_MS(st) ((double)(st)->st_atime * 1000.0) #define POSIX_MTIME_MS(st) ((double)(st)->st_mtime * 1000.0) #define POSIX_CTIME_MS(st) ((double)(st)->st_ctime * 1000.0) #define POSIX_BIRTH_MS(st) 0.0#elif defined(__APPLE__) #define POSIX_ATIME_MS(st) posix_ts_to_ms((st)->st_atimespec) #define POSIX_MTIME_MS(st) posix_ts_to_ms((st)->st_mtimespec) #define POSIX_CTIME_MS(st) posix_ts_to_ms((st)->st_ctimespec) #define POSIX_BIRTH_MS(st) posix_ts_to_ms((st)->st_birthtimespec)#elif defined(__linux__) #define POSIX_ATIME_MS(st) posix_ts_to_ms((st)->st_atim) #define POSIX_MTIME_MS(st) posix_ts_to_ms((st)->st_mtim) #define POSIX_CTIME_MS(st) posix_ts_to_ms((st)->st_ctim) #define POSIX_BIRTH_MS(st) 0.0#else #define POSIX_ATIME_MS(st) posix_ts_to_ms((st)->st_atim) #define POSIX_MTIME_MS(st) posix_ts_to_ms((st)->st_mtim) #define POSIX_CTIME_MS(st) posix_ts_to_ms((st)->st_ctim) #define POSIX_BIRTH_MS(st) 0.0#endif
static const fs_stat_fields_t fs_stat_fields_zero = {0};
static ant_value_t fs_stats_object_from_uv(ant_t *js, const uv_stat_t *st) { if (!st) return fs_stats_object_new(js, &fs_stat_fields_zero); return fs_stats_object_new(js, &(fs_stat_fields_t){ .mode = (mode_t)st->st_mode, .size = (double)st->st_size, .uid = (double)st->st_uid, .gid = (double)st->st_gid, .atime_ms = uv_ts_to_ms(st->st_atim), .mtime_ms = uv_ts_to_ms(st->st_mtim), .ctime_ms = uv_ts_to_ms(st->st_ctim), .birthtime_ms = uv_ts_to_ms(st->st_birthtim), });}
static ant_value_t fs_stats_object_from_posix(ant_t *js, const struct stat *st) { if (!st) return fs_stats_object_new(js, &fs_stat_fields_zero); return fs_stats_object_new(js, &(fs_stat_fields_t){ .mode = st->st_mode, .size = (double)st->st_size, .uid = (double)st->st_uid, .gid = (double)st->st_gid, .atime_ms = POSIX_ATIME_MS(st), .mtime_ms = POSIX_MTIME_MS(st), .ctime_ms = POSIX_CTIME_MS(st), .birthtime_ms = POSIX_BIRTH_MS(st), });}
static bool fs_stat_path_sync(const char *path, uv_stat_t *out) { uv_fs_t req; int rc = 0;
if (!path || !out) return false; memset(out, 0, sizeof(*out));
rc = uv_fs_stat(NULL, &req, path, NULL); if (rc < 0) { uv_fs_req_cleanup(&req); return false; }
*out = req.statbuf; uv_fs_req_cleanup(&req); return true;}
static const char *fs_watch_basename(const char *path) { const char *name = NULL;
if (!path || !*path) return NULL; name = strrchr(path, '/');#ifdef _WIN32 { const char *alt = strrchr(path, '\\'); if (!name || (alt && alt > name)) name = alt; }#endif return name ? name + 1 : path;}
static ant_value_t fs_watch_error(ant_t *js, int status, const char *path) { ant_value_t props = js_mkobj(js); const char *code = uv_err_name(status); js_set(js, props, "code", js_mkstr(js, code, strlen(code))); return js_mkerr_props( js, JS_ERR_TYPE, props, "%s: %s", path ? path : "watch", uv_strerror(status) );}
static void fs_watcher_free(fs_watcher_t *watcher) { if (!watcher) return; free(watcher->path); free(watcher);}
static uv_handle_t *fs_watcher_uv_handle(fs_watcher_t *watcher) { if (!watcher) return NULL; if (watcher->mode == FS_WATCH_MODE_STAT) return (uv_handle_t *)&watcher->handle.poll; return (uv_handle_t *)&watcher->handle.event;}
static void fs_watcher_on_handle_closed(uv_handle_t *handle) { fs_watcher_t *watcher = (fs_watcher_t *)handle->data;
if (!watcher) return; watcher->handle_closed = true; watcher->closing = false; if (watcher->finalized) fs_watcher_free(watcher);}
static void fs_watcher_close_native(fs_watcher_t *watcher) { uv_handle_t *handle = NULL;
if (!watcher) return; if (watcher->closing || watcher->handle_closed) return;
handle = fs_watcher_uv_handle(watcher); if (!handle) return;
fs_remove_active_watcher(watcher); if (watcher->mode == FS_WATCH_MODE_STAT) uv_fs_poll_stop(&watcher->handle.poll); else ant_watch_stop(&watcher->handle.event); watcher->closing = true; uv_close(handle, fs_watcher_on_handle_closed);}
static void fs_watcher_emit_error(fs_watcher_t *watcher, int status) { ant_value_t args[1];
if (!watcher || vtype(watcher->obj) != T_OBJ) return; args[0] = fs_watch_error(watcher->js, status, watcher->path); eventemitter_emit_args(watcher->js, watcher->obj, "error", args, 1);}
static void fs_watcher_emit_change(fs_watcher_t *watcher, const char *filename, int events) { ant_value_t args[2]; const char *event_name = "change"; const char *name = filename;
if (!watcher || vtype(watcher->obj) != T_OBJ) return; if ((events & UV_RENAME) != 0) event_name = "rename"; if (!name || !*name) name = fs_watch_basename(watcher->path);
args[0] = js_mkstr(watcher->js, event_name, strlen(event_name)); args[1] = name ? js_mkstr(watcher->js, name, strlen(name)) : js_mkundef(); eventemitter_emit_args(watcher->js, watcher->obj, "change", args, 2);}
static void fs_watcher_invoke_watchfile_stats( fs_watcher_t *watcher, const uv_stat_t *curr, const uv_stat_t *prev) { uv_stat_t curr_stat; uv_stat_t prev_stat; ant_value_t args[2];
if (!watcher || !is_callable(watcher->callback)) return;
memset(&curr_stat, 0, sizeof(curr_stat)); memset(&prev_stat, 0, sizeof(prev_stat)); if (curr) curr_stat = *curr; if (prev) prev_stat = *prev;
watcher->last_stat = curr_stat; watcher->last_stat_valid = curr != NULL;
args[0] = fs_stats_object_from_uv(watcher->js, &curr_stat); args[1] = fs_stats_object_from_uv(watcher->js, &prev_stat); fs_call_value(watcher->js, watcher->callback, js_mkundef(), args, 2);}
static void fs_watcher_on_event( uv_fs_event_t *handle, const char *filename, int events, int status) { fs_watcher_t *watcher = (fs_watcher_t *)handle->data;
if (!watcher) return; if (status < 0) { fs_watcher_emit_error(watcher, status); return; }
if ((events & (UV_CHANGE | UV_RENAME)) == 0) return; fs_watcher_emit_change(watcher, filename, events);}
static void fs_watcher_on_poll( uv_fs_poll_t *handle, int status, const uv_stat_t *prev, const uv_stat_t *curr) { fs_watcher_t *watcher = (fs_watcher_t *)handle->data; uv_stat_t missing = {0};
if (!watcher) return; if (status < 0 && status != UV_ENOENT) return; if (status == UV_ENOENT) { if (watcher->last_stat_valid) fs_watcher_invoke_watchfile_stats(watcher, &missing, &watcher->last_stat); else fs_watcher_invoke_watchfile_stats(watcher, &missing, &missing); return; }
fs_watcher_invoke_watchfile_stats(watcher, curr, prev);}
static ant_value_t js_fswatcher_close(ant_t *js, ant_value_t *args, int nargs) { fs_watcher_t *watcher = fs_watcher_data(js->this_val);
if (!watcher) return js->this_val; fs_watcher_close_native(watcher); return js->this_val;}
static ant_value_t js_fswatcher_ref(ant_t *js, ant_value_t *args, int nargs) { fs_watcher_t *watcher = fs_watcher_data(js->this_val); uv_handle_t *handle = NULL;
if (!watcher || watcher->handle_closed) return js->this_val; handle = fs_watcher_uv_handle(watcher); if (handle) uv_ref(handle); watcher->persistent = true; return js->this_val;}
static ant_value_t js_fswatcher_unref(ant_t *js, ant_value_t *args, int nargs) { fs_watcher_t *watcher = fs_watcher_data(js->this_val); uv_handle_t *handle = NULL;
if (!watcher || watcher->handle_closed) return js->this_val; handle = fs_watcher_uv_handle(watcher); if (handle) uv_unref(handle); watcher->persistent = false; return js->this_val;}
static ant_value_t js_fswatcher_ctor(ant_t *js, ant_value_t *args, int nargs) { return js_mkerr_typed(js, JS_ERR_TYPE, "FSWatcher cannot be constructed directly");}
static void fs_watcher_finalize(ant_t *js, ant_object_t *obj) { fs_watcher_t *watcher = NULL; if (!obj) return;
watcher = (fs_watcher_t *)obj->native.ptr; obj->native.ptr = NULL; if (!watcher) return;
watcher->finalized = true; fs_watcher_close_native(watcher); if (watcher->handle_closed) fs_watcher_free(watcher);}
static void fs_init_watch_constructors(ant_t *js) { ant_value_t events = 0; ant_value_t ee_ctor = 0; ant_value_t ee_proto = 0;
if (g_fswatcher_proto && g_fswatcher_ctor) return;
events = events_library(js); ee_ctor = js_get(js, events, "EventEmitter"); ee_proto = js_get(js, ee_ctor, "prototype");
g_fswatcher_proto = js_mkobj(js); js_set_proto_init(g_fswatcher_proto, ee_proto); js_set(js, g_fswatcher_proto, "close", js_mkfun(js_fswatcher_close)); js_set(js, g_fswatcher_proto, "ref", js_mkfun(js_fswatcher_ref)); js_set(js, g_fswatcher_proto, "unref", js_mkfun(js_fswatcher_unref)); js_set_sym(js, g_fswatcher_proto, get_toStringTag_sym(), js_mkstr(js, "FSWatcher", 9)); g_fswatcher_ctor = js_make_ctor(js, js_fswatcher_ctor, g_fswatcher_proto, "FSWatcher", 9);}
static bool fs_parse_watch_options(ant_t *js, ant_value_t *args, int nargs, fs_watch_options_t *out) { ant_value_t options = js_mkundef(); ant_value_t persistent_val = js_mkundef();
if (!out) return false;
memset(out, 0, sizeof(*out)); out->persistent = true; out->listener = js_mkundef();
if (nargs > 1) { if (is_callable(args[1])) out->listener = args[1]; else options = args[1]; }
if (nargs > 2 && is_callable(args[2])) out->listener = args[2]; if (vtype(options) == T_UNDEF || vtype(options) == T_NULL || vtype(options) == T_STR) return true; if (vtype(options) != T_OBJ) return false;
persistent_val = js_get(js, options, "persistent"); if (vtype(persistent_val) != T_UNDEF) out->persistent = js_truthy(js, persistent_val); out->recursive = js_truthy(js, js_get(js, options, "recursive")); return true;}
static bool fs_parse_watchfile_options( ant_t *js, ant_value_t *args, int nargs, fs_watchfile_options_t *out) { ant_value_t options = js_mkundef(); ant_value_t persistent_val = js_mkundef();
if (!out) return false;
memset(out, 0, sizeof(*out)); out->persistent = true; out->interval_ms = 5007; out->listener = js_mkundef();
if (nargs > 1) { if (is_callable(args[1])) { out->listener = args[1]; return true; } options = args[1]; }
if (nargs > 2 && is_callable(args[2])) out->listener = args[2]; if (!is_callable(out->listener)) return false; if (vtype(options) == T_UNDEF || vtype(options) == T_NULL) return true; if (vtype(options) != T_OBJ) return false;
persistent_val = js_get(js, options, "persistent"); if (vtype(persistent_val) != T_UNDEF) out->persistent = js_truthy(js, persistent_val); { ant_value_t interval_val = js_get(js, options, "interval"); if (vtype(interval_val) == T_NUM && js_getnum(interval_val) > 0) out->interval_ms = (unsigned int)js_getnum(interval_val); } return true;}
static fs_watcher_t *fs_watcher_new(ant_t *js, fs_watch_mode_t mode) { fs_watcher_t *watcher = calloc(1, sizeof(*watcher));
if (!watcher) return NULL; watcher->js = js; watcher->obj = js_mkundef(); watcher->callback = js_mkundef(); watcher->persistent = true; watcher->mode = mode; return watcher;}
static int fs_watcher_start_event(fs_watcher_t *watcher, const char *path, bool persistent, bool recursive) { unsigned int flags = 0;
if (!watcher || !path) return UV_EINVAL;#ifdef UV_FS_EVENT_RECURSIVE if (recursive) flags |= UV_FS_EVENT_RECURSIVE;#else (void)recursive;#endif
watcher->persistent = persistent; return ant_watch_start( uv_default_loop(), &watcher->handle.event, path, fs_watcher_on_event, watcher, flags, &watcher->path );}
static int fs_watcher_start_poll(fs_watcher_t *watcher, const char *path, bool persistent, unsigned int interval_ms) { int rc = 0;
if (!watcher || !path) return UV_EINVAL;
watcher->path = ant_watch_resolve_path(path); if (!watcher->path) return UV_ENOMEM;
rc = uv_fs_poll_init(uv_default_loop(), &watcher->handle.poll); if (rc != 0) goto fail;
watcher->handle.poll.data = watcher; rc = uv_fs_poll_start(&watcher->handle.poll, fs_watcher_on_poll, watcher->path, interval_ms); if (rc != 0) goto fail;
watcher->persistent = persistent; return 0;
fail: free(watcher->path); watcher->path = NULL; return rc;}
static ant_value_t fs_watcher_make_object(ant_t *js, fs_watcher_t *watcher) { ant_value_t obj = 0;
if (!watcher) return js_mkerr(js, "Out of memory"); fs_init_watch_constructors(js);
obj = js_mkobj(js); js_set_proto_init(obj, g_fswatcher_proto); js_set_native_ptr(obj, watcher); js_set_native_tag(obj, FS_WATCHER_NATIVE_TAG); js_set_finalizer(obj, fs_watcher_finalize); watcher->obj = obj; return obj;}
static void fs_request_fail(fs_request_t *req, int uv_code) { req->failed = 1; req->error_code = uv_code; if (req->error_msg) free(req->error_msg); req->error_msg = strdup(uv_strerror(uv_code));}
static ant_value_t fs_coerce_file_url_path(ant_t *js, ant_value_t arg) { ant_value_t href = js_getprop_fallback(js, arg, "href"); if (vtype(href) != T_STR) return js_mkundef();
const char *href_str = js_getstr(js, href, NULL); if (!href_str) return js_mkundef();
url_state_t parsed = {0}; if (parse_url_to_state(href_str, NULL, &parsed) != 0) return js_mkundef();
ant_value_t path = js_mkundef(); if (parsed.protocol && strcmp(parsed.protocol, "file:") == 0) { char *decoded = url_decode_component(parsed.pathname); if (decoded) { path = js_mkstr(js, decoded, strlen(decoded)); free(decoded); }}
url_state_clear(&parsed); return path;}
static ant_value_t fs_coerce_path(ant_t *js, ant_value_t arg) { if (vtype(arg) == T_STR) return arg; if (!is_object_type(arg)) return js_mkundef();
ant_value_t path = fs_coerce_file_url_path(js, arg); if (!is_undefined(path)) return path;
path = js_get(js, arg, "pathname"); if (vtype(path) == T_STR) return path;
return js_mkundef();}
static int fs_remove_path_recursive(const char *path, bool recursive, bool force) { uv_fs_t req; int result = uv_fs_lstat(NULL, &req, path, NULL); if (result < 0) { uv_fs_req_cleanup(&req); return (force && result == UV_ENOENT) ? 0 : result; }
uv_stat_t statbuf = req.statbuf; uv_fs_req_cleanup(&req);
if ((statbuf.st_mode & S_IFMT) != S_IFDIR) { result = uv_fs_unlink(NULL, &req, path, NULL); uv_fs_req_cleanup(&req); return (force && result == UV_ENOENT) ? 0 : result; }
if (!recursive) return UV_EISDIR; result = uv_fs_scandir(NULL, &req, path, 0, NULL); if (result < 0) { uv_fs_req_cleanup(&req); return (force && result == UV_ENOENT) ? 0 : result; }
for (;;) { uv_dirent_t entry; result = uv_fs_scandir_next(&req, &entry); if (result == UV_EOF) break; if (result < 0) { uv_fs_req_cleanup(&req); return result; } size_t path_len = strlen(path); size_t name_len = strlen(entry.name); bool needs_sep = path_len > 0 && path[path_len - 1] != '/'; char *child = malloc(path_len + (needs_sep ? 1u : 0u) + name_len + 1u); if (!child) { uv_fs_req_cleanup(&req); return UV_ENOMEM; } memcpy(child, path, path_len); if (needs_sep) child[path_len++] = '/'; memcpy(child + path_len, entry.name, name_len + 1u); result = fs_remove_path_recursive(child, true, force); free(child); if (result < 0) { uv_fs_req_cleanup(&req); return result; } }
uv_fs_req_cleanup(&req); result = uv_fs_rmdir(NULL, &req, path, NULL); uv_fs_req_cleanup(&req); return (force && result == UV_ENOENT) ? 0 : result;}
static bool fs_parse_rm_options(ant_t *js, ant_value_t options, bool *recursive_out, bool *force_out) { if (recursive_out) *recursive_out = false; if (force_out) *force_out = false;
if (vtype(options) == T_UNDEF || vtype(options) == T_NULL) return true; if (vtype(options) != T_OBJ) return false;
if (recursive_out) *recursive_out = js_truthy(js, js_get(js, options, "recursive")); if (force_out) *force_out = js_truthy(js, js_get(js, options, "force")); return true;}
static ant_value_t fs_rm_impl(ant_t *js, ant_value_t *args, int nargs, bool return_promise) { ant_value_t promise = 0; ant_value_t path_val; size_t path_len = 0; const char *path = NULL; bool recursive = false; bool force = false; int result;
if (return_promise) promise = js_mkpromise(js); if (nargs < 1) { ant_value_t err = js_mkerr(js, "rm() requires a path argument"); if (!return_promise) return err; js_reject_promise(js, promise, err); return promise; }
path_val = fs_coerce_path(js, args[0]); if (vtype(path_val) != T_STR) { ant_value_t err = js_mkerr(js, "rm() path must be a string"); if (!return_promise) return err; js_reject_promise(js, promise, err); return promise; }
path = js_getstr(js, path_val, &path_len); if (!path) { ant_value_t err = js_mkerr(js, "Failed to get path string"); if (!return_promise) return err; js_reject_promise(js, promise, err); return promise; }
if (!fs_parse_rm_options(js, nargs >= 2 ? args[1] : js_mkundef(), &recursive, &force)) { ant_value_t err = js_mkerr_typed(js, JS_ERR_TYPE, "rm() options must be an object"); if (!return_promise) return err; js_reject_promise(js, promise, err); return promise; }
result = fs_remove_path_recursive(path, recursive, force); if (result < 0) { ant_value_t err = js_mkerr(js, "%s", uv_strerror(result)); if (!return_promise) return err; js_reject_promise(js, promise, err); return promise; }
if (!return_promise) return js_mkundef(); js_resolve_promise(js, promise, js_mkundef()); return promise;}
static fs_encoding_t parse_encoding(ant_t *js, ant_value_t arg) { size_t len; const char *str = NULL;
if (vtype(arg) == T_STR) str = js_getstr(js, arg, &len); else if (vtype(arg) == T_OBJ) { ant_value_t enc = js_get(js, arg, "encoding"); if (vtype(enc) == T_STR) str = js_getstr(js, enc, &len); }
if (!str) return FS_ENC_NONE;
if (len == 4 && memcmp(str, "utf8", 4) == 0) return FS_ENC_UTF8; if (len == 5 && memcmp(str, "utf-8", 5) == 0) return FS_ENC_UTF8; if (len == 7 && memcmp(str, "utf16le", 7) == 0) return FS_ENC_UTF16LE; if (len == 4 && memcmp(str, "ucs2", 4) == 0) return FS_ENC_UTF16LE; if (len == 5 && memcmp(str, "ucs-2", 5) == 0) return FS_ENC_UTF16LE; if (len == 6 && memcmp(str, "latin1", 6) == 0) return FS_ENC_LATIN1; if (len == 6 && memcmp(str, "binary", 6) == 0) return FS_ENC_LATIN1; if (len == 6 && memcmp(str, "base64", 6) == 0) return FS_ENC_BASE64; if (len == 9 && memcmp(str, "base64url", 9) == 0) return FS_ENC_BASE64URL; if (len == 3 && memcmp(str, "hex", 3) == 0) return FS_ENC_HEX; if (len == 5 && memcmp(str, "ascii", 5) == 0) return FS_ENC_ASCII;
return FS_ENC_NONE;}
static ant_value_t encode_data(ant_t *js, const char *data, size_t len, fs_encoding_t enc) { switch (enc) { case FS_ENC_UTF8: case FS_ENC_LATIN1: case FS_ENC_ASCII: return js_mkstr(js, data, len);
case FS_ENC_HEX: { char *out = malloc(len * 2); if (!out) return js_mkerr(js, "Out of memory"); for (size_t i = 0; i < len; i++) { out[i * 2] = hex_char((unsigned char)data[i] >> 4); out[i * 2 + 1] = hex_char(data[i]); } ant_value_t result = js_mkstr(js, out, len * 2); free(out); return result; }
case FS_ENC_BASE64: { size_t out_len; char *out = ant_base64_encode((const uint8_t *)data, len, &out_len); if (!out) return js_mkerr(js, "Out of memory"); ant_value_t result = js_mkstr(js, out, out_len); free(out); return result; }
case FS_ENC_BASE64URL: { size_t out_len; char *out = ant_base64_encode((const uint8_t *)data, len, &out_len); if (!out) return js_mkerr(js, "Out of memory"); for (size_t i = 0; i < out_len; i++) { if (out[i] == '+') out[i] = '-'; else if (out[i] == '/') out[i] = '_'; } while (out_len > 0 && out[out_len - 1] == '=') out_len--; ant_value_t result = js_mkstr(js, out, out_len); free(out); return result; }
case FS_ENC_UTF16LE: { const unsigned char *s = (const unsigned char *)data; char *out = malloc((len / 2) * 4); if (!out) return js_mkerr(js, "Out of memory"); size_t j = 0; for (size_t i = 0; i + 1 < len; i += 2) { uint32_t cp = s[i] | (s[i + 1] << 8); bool is_hi = cp >= 0xD800 && cp <= 0xDBFF && i + 3 < len; uint32_t lo = is_hi ? s[i + 2] | (s[i + 3] << 8) : 0; bool is_pair = is_hi && lo >= 0xDC00 && lo <= 0xDFFF; if (is_pair) { cp = 0x10000 + ((cp - 0xD800) << 10) + (lo - 0xDC00); i += 2; } j += utf8_encode(cp, out + j); } ant_value_t result = js_mkstr(js, out, j); free(out); return result; }
default: return js_mkstr(js, data, len); }}
static void free_fs_request(fs_request_t *req) { if (!req) return;
if (req->path) free(req->path); if (req->path2) free(req->path2); if (req->data) free(req->data); if (req->error_msg) free(req->error_msg); uv_fs_req_cleanup(&req->uv_req); free(req);}
static ant_value_t fs_rejected_promise(ant_t *js, ant_value_t err) { ant_value_t promise = js_mkpromise(js); js_reject_promise(js, promise, err); return promise;}
static ant_value_t fs_resolved_promise(ant_t *js, ant_value_t value) { ant_value_t promise = js_mkpromise(js); js_resolve_promise(js, promise, value); return promise;}
static ant_value_t fs_filehandle_require_this(ant_t *js) { ant_value_t this_obj = js_getthis(js); if (!is_object_type(this_obj) || !js_check_native_tag(this_obj, FS_FILEHANDLE_NATIVE_TAG)) return js_mkerr_typed(js, JS_ERR_TYPE, "FileHandle method called on incompatible receiver"); return this_obj;}
static ant_value_t fs_filehandle_fd_value(ant_t *js, ant_value_t handle_obj) { ant_value_t fd_val = js_get_slot(handle_obj, SLOT_DATA); if (vtype(fd_val) != T_NUM) fd_val = js_get(js, handle_obj, "fd"); return fd_val;}
static ant_value_t fs_filehandle_get_fd(ant_t *js, ant_value_t handle_obj) { ant_value_t fd_val = fs_filehandle_fd_value(js, handle_obj); if (vtype(fd_val) != T_NUM || js_getnum(fd_val) < 0) return js_mkerr_typed(js, JS_ERR_TYPE, "FileHandle is closed"); return fd_val;}
static void fs_filehandle_mark_closed(ant_t *js, ant_value_t handle_obj) { js_set_slot(handle_obj, SLOT_DATA, js_mkundef()); js_set(js, handle_obj, "fd", js_mknum(-1));}
static ant_value_t builtin_fs_filehandle_close(ant_t *js, ant_value_t *args, int nargs) { ant_value_t handle_obj = fs_filehandle_require_this(js); if (is_err(handle_obj)) return fs_rejected_promise(js, handle_obj);
ant_value_t fd_val = fs_filehandle_fd_value(js, handle_obj); if (vtype(fd_val) != T_NUM || js_getnum(fd_val) < 0) return fs_resolved_promise(js, js_mkundef()); uv_fs_t req; int result = uv_fs_close(uv_default_loop(), &req, (uv_file)(int)js_getnum(fd_val), NULL); uv_fs_req_cleanup(&req); if (result < 0) return fs_rejected_promise(js, fs_mk_uv_error(js, result, "close", NULL, NULL));
fs_filehandle_mark_closed(js, handle_obj); return fs_resolved_promise(js, js_mkundef());}
static ant_value_t builtin_fs_filehandle_stat(ant_t *js, ant_value_t *args, int nargs) { ant_value_t handle_obj = fs_filehandle_require_this(js); if (is_err(handle_obj)) return fs_rejected_promise(js, handle_obj);
ant_value_t fd_val = fs_filehandle_get_fd(js, handle_obj); if (is_err(fd_val)) return fs_rejected_promise(js, fd_val);
uv_fs_t req; int result = uv_fs_fstat(NULL, &req, (uv_file)(int)js_getnum(fd_val), NULL); if (result < 0) { ant_value_t err = fs_mk_uv_error(js, result, "fstat", NULL, NULL); uv_fs_req_cleanup(&req); return fs_rejected_promise(js, err); }
ant_value_t stat_obj = fs_stats_object_from_uv(js, &req.statbuf); uv_fs_req_cleanup(&req); return fs_resolved_promise(js, stat_obj);}
static ant_value_t builtin_fs_filehandle_sync(ant_t *js, ant_value_t *args, int nargs) { ant_value_t handle_obj = fs_filehandle_require_this(js); if (is_err(handle_obj)) return fs_rejected_promise(js, handle_obj);
ant_value_t fd_val = fs_filehandle_get_fd(js, handle_obj); if (is_err(fd_val)) return fs_rejected_promise(js, fd_val);
uv_fs_t req; int result = uv_fs_fsync(uv_default_loop(), &req, (uv_file)(int)js_getnum(fd_val), NULL); uv_fs_req_cleanup(&req); if (result < 0) return fs_rejected_promise(js, fs_mk_uv_error(js, result, "fsync", NULL, NULL));
return fs_resolved_promise(js, js_mkundef());}
static ant_value_t builtin_fs_filehandle_read(ant_t *js, ant_value_t *args, int nargs) { ant_value_t handle_obj = fs_filehandle_require_this(js); if (is_err(handle_obj)) return fs_rejected_promise(js, handle_obj);
ant_value_t fd_val = fs_filehandle_get_fd(js, handle_obj); if (is_err(fd_val)) return fs_rejected_promise(js, fd_val); if (nargs < 1) return fs_rejected_promise(js, js_mkerr(js, "FileHandle.read() requires a buffer argument"));
TypedArrayData *ta_data = buffer_get_typedarray_data(args[0]); if (!ta_data || !ta_data->buffer || !ta_data->buffer->data) return fs_rejected_promise(js, js_mkerr(js, "FileHandle.read() buffer must be a Buffer or TypedArray"));
size_t buf_len = ta_data->byte_length; size_t offset = 0; size_t length = buf_len; int64_t position = -1;
if (nargs >= 2 && vtype(args[1]) == T_NUM) offset = (size_t)js_getnum(args[1]); if (nargs >= 3 && vtype(args[2]) == T_NUM) length = (size_t)js_getnum(args[2]); if (nargs >= 4 && vtype(args[3]) == T_NUM) position = (int64_t)js_getnum(args[3]);
if (offset > buf_len) return fs_rejected_promise(js, js_mkerr(js, "offset is out of bounds")); if (offset + length > buf_len) return fs_rejected_promise(js, js_mkerr(js, "length extends beyond buffer"));
uint8_t *buf_data = ta_data->buffer->data + ta_data->byte_offset; uv_buf_t buf = uv_buf_init((char *)(buf_data + offset), (unsigned int)length); uv_fs_t req; int result = uv_fs_read(uv_default_loop(), &req, (uv_file)(int)js_getnum(fd_val), &buf, 1, position, NULL); uv_fs_req_cleanup(&req); if (result < 0) return fs_rejected_promise(js, fs_mk_uv_error(js, result, "read", NULL, NULL));
ant_value_t out = js_mkobj(js); js_set(js, out, "bytesRead", js_mknum((double)result)); js_set(js, out, "buffer", args[0]); return fs_resolved_promise(js, out);}
static ant_value_t builtin_fs_filehandle_write(ant_t *js, ant_value_t *args, int nargs) { ant_value_t handle_obj = fs_filehandle_require_this(js); if (is_err(handle_obj)) return fs_rejected_promise(js, handle_obj);
ant_value_t fd_val = fs_filehandle_get_fd(js, handle_obj); if (is_err(fd_val)) return fs_rejected_promise(js, fd_val); if (nargs < 1) return fs_rejected_promise(js, js_mkerr(js, "FileHandle.write() requires a data argument"));
const char *write_data = NULL; size_t write_len = 0; int64_t position = -1;
if (vtype(args[0]) == T_STR) { write_data = js_getstr(js, args[0], &write_len); if (!write_data) return fs_rejected_promise(js, js_mkerr(js, "Failed to get string")); if (nargs >= 2 && vtype(args[1]) == T_NUM) position = (int64_t)js_getnum(args[1]); } else { TypedArrayData *ta_data = buffer_get_typedarray_data(args[0]); if (!ta_data || !ta_data->buffer || !ta_data->buffer->data) return fs_rejected_promise(js, js_mkerr(js, "FileHandle.write() data must be a Buffer, TypedArray, DataView, or string")); uint8_t *buf_data = ta_data->buffer->data + ta_data->byte_offset; size_t buf_len = ta_data->byte_length; size_t offset = 0; size_t length = buf_len;
if (nargs >= 2) { if (vtype(args[1]) == T_OBJ) { ant_value_t off_val = js_get(js, args[1], "offset"); ant_value_t len_val = js_get(js, args[1], "length"); ant_value_t pos_val = js_get(js, args[1], "position"); if (vtype(off_val) == T_NUM) offset = (size_t)js_getnum(off_val); if (vtype(len_val) == T_NUM) length = (size_t)js_getnum(len_val); else length = buf_len - offset; if (vtype(pos_val) == T_NUM) position = (int64_t)js_getnum(pos_val); } else if (vtype(args[1]) == T_NUM) { offset = (size_t)js_getnum(args[1]); length = buf_len - offset; if (nargs >= 3 && vtype(args[2]) == T_NUM) length = (size_t)js_getnum(args[2]); if (nargs >= 4 && vtype(args[3]) == T_NUM) position = (int64_t)js_getnum(args[3]); }}
if (offset > buf_len) return fs_rejected_promise(js, js_mkerr(js, "offset is out of bounds")); if (offset + length > buf_len) return fs_rejected_promise(js, js_mkerr(js, "length extends beyond buffer")); write_data = (const char *)(buf_data + offset); write_len = length; }
uv_buf_t buf = uv_buf_init((char *)write_data, (unsigned int)write_len); uv_fs_t req; int result = uv_fs_write(uv_default_loop(), &req, (uv_file)(int)js_getnum(fd_val), &buf, 1, position, NULL); uv_fs_req_cleanup(&req); if (result < 0) return fs_rejected_promise(js, fs_mk_uv_error(js, result, "write", NULL, NULL));
ant_value_t out = js_mkobj(js); js_set(js, out, "bytesWritten", js_mknum((double)result)); js_set(js, out, "buffer", args[0]); return fs_resolved_promise(js, out);}
static ant_value_t builtin_fs_filehandle_writeFile(ant_t *js, ant_value_t *args, int nargs) { ant_value_t handle_obj = fs_filehandle_require_this(js); if (is_err(handle_obj)) return fs_rejected_promise(js, handle_obj);
ant_value_t fd_val = fs_filehandle_get_fd(js, handle_obj); if (is_err(fd_val)) return fs_rejected_promise(js, fd_val); if (nargs < 1) return fs_rejected_promise(js, js_mkerr(js, "FileHandle.writeFile() requires data"));
const char *data = NULL; size_t len = 0;
if (vtype(args[0]) == T_STR) { data = js_getstr(js, args[0], &len); if (!data) return fs_rejected_promise(js, js_mkerr(js, "Failed to get string")); } else { TypedArrayData *ta_data = buffer_get_typedarray_data(args[0]); if (!ta_data || !ta_data->buffer || !ta_data->buffer->data) return fs_rejected_promise(js, js_mkerr(js, "FileHandle.writeFile() data must be a Buffer, TypedArray, DataView, or string")); data = (const char *)(ta_data->buffer->data + ta_data->byte_offset); len = ta_data->byte_length; }
size_t written = 0; while (written < len) { uv_buf_t buf = uv_buf_init((char *)(data + written), (unsigned int)(len - written)); uv_fs_t req; int result = uv_fs_write(uv_default_loop(), &req, (uv_file)(int)js_getnum(fd_val), &buf, 1, -1, NULL); uv_fs_req_cleanup(&req); if (result < 0) return fs_rejected_promise(js, fs_mk_uv_error(js, result, "write", NULL, NULL)); if (result == 0) return fs_rejected_promise(js, js_mkerr(js, "short write")); written += (size_t)result; }
return fs_resolved_promise(js, js_mkundef());}
static void fs_init_filehandle_proto(ant_t *js) { if (is_object_type(g_filehandle_proto)) return; g_filehandle_proto = js_mkobj(js); js_set_native_tag(g_filehandle_proto, FS_FILEHANDLE_NATIVE_TAG); js_set(js, g_filehandle_proto, "close", js_mkfun(builtin_fs_filehandle_close)); js_set(js, g_filehandle_proto, "stat", js_mkfun(builtin_fs_filehandle_stat)); js_set(js, g_filehandle_proto, "sync", js_mkfun(builtin_fs_filehandle_sync)); js_set(js, g_filehandle_proto, "read", js_mkfun(builtin_fs_filehandle_read)); js_set(js, g_filehandle_proto, "write", js_mkfun(builtin_fs_filehandle_write)); js_set(js, g_filehandle_proto, "writeFile", js_mkfun(builtin_fs_filehandle_writeFile)); js_set_sym(js, g_filehandle_proto, get_toStringTag_sym(), js_mkstr(js, "FileHandle", 10));}
static ant_value_t fs_make_filehandle(ant_t *js, int fd) { fs_init_filehandle_proto(js); ant_value_t handle = js_mkobj(js); js_set_native_tag(handle, FS_FILEHANDLE_NATIVE_TAG); js_set_proto_init(handle, g_filehandle_proto); js_set_slot(handle, SLOT_DATA, js_mknum((double)fd)); js_set(js, handle, "fd", js_mknum((double)fd)); return handle;}
static void remove_pending_request(fs_request_t *req) { if (!req || !pending_requests) return; unsigned int len = utarray_len(pending_requests); for (unsigned int i = 0; i < len; i++) { if (*(fs_request_t**)utarray_eltptr(pending_requests, i) != req) continue; utarray_erase(pending_requests, i, 1); break; }}
static ant_value_t fs_read_to_uint8array(ant_t *js, const char *data, size_t len) { ArrayBufferData *ab = create_array_buffer_data(len); if (!ab) return js_mkundef(); memcpy(ab->data, data, len); ant_value_t result = create_typed_array(js, TYPED_ARRAY_UINT8, ab, 0, len, "Buffer"); if (vtype(result) == T_ERR) free_array_buffer_data(ab); return result;}
static void complete_request(fs_request_t *req) { if (req->failed) { const char *err_msg = req->error_msg ? req->error_msg : "Unknown error"; ant_value_t props = js_mkobj(req->js); ant_value_t reject_value;
if (req->error_code) { const char *code = uv_err_name(req->error_code); js_set(req->js, props, "code", js_mkstr(req->js, code, strlen(code))); js_set(req->js, props, "errno", js_mknum((double)req->error_code)); if (req->path) js_set(req->js, props, "path", js_mkstr(req->js, req->path, strlen(req->path))); if (req->path2) js_set(req->js, props, "dest", js_mkstr(req->js, req->path2, strlen(req->path2))); reject_value = js_mkerr_props(req->js, JS_ERR_TYPE, props, "%s", err_msg); } else reject_value = js_mkerr(req->js, "%s", err_msg);
if (is_err(reject_value)) { reject_value = req->js->thrown_exists ? req->js->thrown_value : js_mkundef(); if (!req->js->thrown_exists) reject_value = js_mkstr(req->js, err_msg, strlen(err_msg)); req->js->thrown_exists = false; req->js->thrown_value = js_mkundef(); } js_reject_promise(req->js, req->promise, reject_value); } else { ant_value_t result = js_mkundef(); if (req->op_type == FS_OP_READ && req->data) { if (req->encoding != FS_ENC_NONE) result = encode_data(req->js, req->data, req->data_len, req->encoding); else result = fs_read_to_uint8array(req->js, req->data, req->data_len); } else if (req->op_type == FS_OP_READ_BYTES && req->data) result = js_mkstr(req->js, req->data, req->data_len); else if (req->op_type == FS_OP_REALPATH && req->data) result = js_mkstr(req->js, req->data, req->data_len); else if (req->op_type == FS_OP_MKDTEMP && req->data) result = js_mkstr(req->js, req->data, req->data_len); js_resolve_promise(req->js, req->promise, result); } remove_pending_request(req); free_fs_request(req);}
static void on_read_complete(uv_fs_t *uv_req) { fs_request_t *req = (fs_request_t *)uv_req->data; if (uv_req->result < 0) { fs_request_fail(req, (int)uv_req->result); req->completed = 1; complete_request(req); return; } req->data_len = uv_req->result; uv_fs_t close_req; uv_fs_close(uv_default_loop(), &close_req, req->fd, NULL); uv_fs_req_cleanup(&close_req); req->completed = 1; complete_request(req);}
static void on_open_for_read(uv_fs_t *uv_req) { fs_request_t *req = (fs_request_t *)uv_req->data; if (uv_req->result < 0) { fs_request_fail(req, (int)uv_req->result); req->completed = 1; complete_request(req); return; } req->fd = (int)uv_req->result; uv_fs_req_cleanup(uv_req); uv_fs_t stat_req; int stat_result = uv_fs_fstat(uv_default_loop(), &stat_req, req->fd, NULL); if (stat_result < 0) { fs_request_fail(req, stat_result); req->completed = 1; uv_fs_t close_req; uv_fs_close(uv_default_loop(), &close_req, req->fd, NULL); uv_fs_req_cleanup(&close_req); complete_request(req); return; } size_t file_size = stat_req.statbuf.st_size; uv_fs_req_cleanup(&stat_req); size_t alloc_size = (req->op_type == FS_OP_READ) ? file_size + 1 : file_size; req->data = malloc(alloc_size); if (!req->data) { req->failed = 1; req->error_msg = strdup("Out of memory"); req->completed = 1; uv_fs_t close_req; uv_fs_close(uv_default_loop(), &close_req, req->fd, NULL); uv_fs_req_cleanup(&close_req); complete_request(req); return; } uv_buf_t buf = uv_buf_init(req->data, (unsigned int)file_size); int read_result = uv_fs_read(uv_default_loop(), uv_req, req->fd, &buf, 1, 0, on_read_complete); if (read_result < 0) { fs_request_fail(req, read_result); req->completed = 1; uv_fs_t close_req; uv_fs_close(uv_default_loop(), &close_req, req->fd, NULL); uv_fs_req_cleanup(&close_req); complete_request(req); return; }}
static void on_write_complete(uv_fs_t *uv_req) { fs_request_t *req = (fs_request_t *)uv_req->data; if (uv_req->result < 0) { fs_request_fail(req, (int)uv_req->result); } uv_fs_t close_req; uv_fs_close(uv_default_loop(), &close_req, req->fd, NULL); uv_fs_req_cleanup(&close_req); req->completed = 1; complete_request(req);}
static void on_open_for_write(uv_fs_t *uv_req) { fs_request_t *req = (fs_request_t *)uv_req->data; if (uv_req->result < 0) { fs_request_fail(req, (int)uv_req->result); req->completed = 1; complete_request(req); return; } req->fd = (int)uv_req->result; uv_fs_req_cleanup(uv_req); uv_buf_t buf = uv_buf_init(req->data, (unsigned int)req->data_len); int write_result = uv_fs_write(uv_default_loop(), uv_req, req->fd, &buf, 1, 0, on_write_complete); if (write_result < 0) { fs_request_fail(req, write_result); req->completed = 1; uv_fs_t close_req; uv_fs_close(uv_default_loop(), &close_req, req->fd, NULL); uv_fs_req_cleanup(&close_req); complete_request(req); return; }}
static void on_unlink_complete(uv_fs_t *uv_req) { fs_request_t *req = (fs_request_t *)uv_req->data; if (uv_req->result < 0) { fs_request_fail(req, (int)uv_req->result); } uv_fs_req_cleanup(uv_req); req->completed = 1; complete_request(req);}
static void on_rename_complete(uv_fs_t *uv_req) { fs_request_t *req = (fs_request_t *)uv_req->data;
if (uv_req->result < 0) { fs_request_fail(req, (int)uv_req->result); }
uv_fs_req_cleanup(uv_req); req->completed = 1; complete_request(req);}
static int mkdirp(const char *path, mode_t mode) { int result = -1; char *tmp = strdup(path); if (!tmp) goto done;
size_t len = strlen(tmp); if (len > 0 && tmp[len - 1] == '/') tmp[len - 1] = '\0';
for (char *p = tmp + 1; *p; p++) { if (*p != '/') continue; *p = '\0';#ifdef _WIN32 _mkdir(tmp);#else mkdir(tmp, mode);#endif *p = '/'; }
#ifdef _WIN32 result = _mkdir(tmp);#else result = mkdir(tmp, mode);#endif if (result != 0 && errno == EEXIST) result = 0;
free(tmp);done: return result;}
static void on_mkdir_complete(uv_fs_t *uv_req) { fs_request_t *req = (fs_request_t *)uv_req->data; if (uv_req->result < 0) { if (!req->recursive || uv_req->result != UV_EEXIST) { fs_request_fail(req, (int)uv_req->result); }} uv_fs_req_cleanup(uv_req); req->completed = 1; complete_request(req);}
static void on_rmdir_complete(uv_fs_t *uv_req) { fs_request_t *req = (fs_request_t *)uv_req->data; if (uv_req->result < 0) { fs_request_fail(req, (int)uv_req->result); } uv_fs_req_cleanup(uv_req); req->completed = 1; complete_request(req);}
static void on_stat_complete(uv_fs_t *uv_req) { fs_request_t *req = (fs_request_t *)uv_req->data; if (uv_req->result < 0) { fs_request_fail(req, (int)uv_req->result); req->completed = 1; complete_request(req); return; } ant_value_t stat_obj = fs_stats_object_from_uv(req->js, &uv_req->statbuf); req->completed = 1; js_resolve_promise(req->js, req->promise, stat_obj); remove_pending_request(req); free_fs_request(req);}
static void on_realpath_complete(uv_fs_t *uv_req) { fs_request_t *req = (fs_request_t *)uv_req->data;
if (uv_req->result < 0 || !uv_req->ptr) { fs_request_fail(req, (int)uv_req->result); req->completed = 1; complete_request(req); return; }
req->data = strdup((const char *)uv_req->ptr); if (!req->data) { req->failed = 1; req->error_msg = strdup("Out of memory"); req->completed = 1; complete_request(req); return; }
req->data_len = strlen(req->data); req->completed = 1; complete_request(req);}
static ant_value_t create_dirent_object(ant_t *js, const char *name, size_t name_len, uv_dirent_type_t type) { ant_value_t obj = js_newobj(js); js_set_proto(obj, g_dirent_proto); js_set(js, obj, "name", js_mkstr(js, name, name_len)); js_set_slot(obj, SLOT_DATA, tov((double)type)); return obj;}
static void on_mkdtemp_complete(uv_fs_t *uv_req) { fs_request_t *req = (fs_request_t *)uv_req->data;
if (uv_req->result < 0) { fs_request_fail(req, (int)uv_req->result); req->completed = 1; complete_request(req); uv_fs_req_cleanup(uv_req); return; }
req->data = strdup(uv_req->path); if (!req->data) { req->failed = 1; req->error_msg = strdup("Out of memory"); req->completed = 1; complete_request(req); uv_fs_req_cleanup(uv_req); return; }
req->data_len = strlen(req->data); req->completed = 1; uv_fs_req_cleanup(uv_req); complete_request(req);}
static void on_exists_complete(uv_fs_t *uv_req) { fs_request_t *req = (fs_request_t *)uv_req->data; ant_value_t result = js_bool(uv_req->result >= 0); req->completed = 1; js_resolve_promise(req->js, req->promise, result); remove_pending_request(req); free_fs_request(req);}
static void on_access_complete(uv_fs_t *uv_req) { fs_request_t *req = (fs_request_t *)uv_req->data; if (uv_req->result < 0) { fs_request_fail(req, (int)uv_req->result); req->completed = 1; complete_request(req); return; } req->completed = 1; js_resolve_promise(req->js, req->promise, js_mkundef()); remove_pending_request(req); free_fs_request(req);}
static void on_chmod_complete(uv_fs_t *uv_req) { fs_request_t *req = (fs_request_t *)uv_req->data;
if (uv_req->result < 0) { fs_request_fail(req, (int)uv_req->result); req->completed = 1; complete_request(req); return; }
req->completed = 1; js_resolve_promise(req->js, req->promise, js_mkundef()); remove_pending_request(req); free_fs_request(req);}
static void on_readdir_complete(uv_fs_t *uv_req) { fs_request_t *req = (fs_request_t *)uv_req->data; if (uv_req->result < 0) { fs_request_fail(req, (int)uv_req->result); req->completed = 1; complete_request(req); return; } ant_value_t arr = js_mkarr(req->js); uv_dirent_t dirent; while (uv_fs_scandir_next(uv_req, &dirent) != UV_EOF) { if (req->with_file_types) { ant_value_t entry = create_dirent_object(req->js, dirent.name, strlen(dirent.name), dirent.type); js_arr_push(req->js, arr, entry); } else { ant_value_t name = js_mkstr(req->js, dirent.name, strlen(dirent.name)); js_arr_push(req->js, arr, name); }} req->completed = 1; js_resolve_promise(req->js, req->promise, arr); remove_pending_request(req); free_fs_request(req);}
static ant_value_t builtin_fs_readFileSync(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "readFileSync() requires a path argument"); ant_value_t path_val = fs_coerce_path(js, args[0]); if (vtype(path_val) != T_STR) return js_mkerr(js, "readFileSync() path must be a string or URL"); size_t path_len; char *path = js_getstr(js, path_val, &path_len); if (!path) return js_mkerr(js, "Failed to get path string"); char *path_cstr = strndup(path, path_len); if (!path_cstr) return js_mkerr(js, "Out of memory"); FILE *file = fopen(path_cstr, "rb"); if (!file) { ant_value_t err = fs_mk_errno_error(js, errno, "open", path_cstr, NULL); free(path_cstr); return err; } fseek(file, 0, SEEK_END); long file_size = ftell(file); fseek(file, 0, SEEK_SET); if (file_size < 0) { fclose(file); free(path_cstr); return js_mkerr(js, "Failed to get file size"); } char *data = malloc(file_size + 1); if (!data) { fclose(file); free(path_cstr); return js_mkerr(js, "Out of memory"); } size_t bytes_read = fread(data, 1, file_size, file); fclose(file); free(path_cstr); if (bytes_read != (size_t)file_size) { free(data); return js_mkerr(js, "Failed to read entire file"); } fs_encoding_t enc = (nargs > 1) ? parse_encoding(js, args[1]) : FS_ENC_NONE; ant_value_t result = (enc != FS_ENC_NONE) ? encode_data(js, data, file_size, enc) : fs_read_to_uint8array(js, data, file_size); free(data); return result;}
static ant_value_t builtin_fs_readBytesSync(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "readBytesSync() requires a path argument"); if (vtype(args[0]) != T_STR) return js_mkerr(js, "readBytesSync() 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"); char *path_cstr = strndup(path, path_len); if (!path_cstr) return js_mkerr(js, "Out of memory"); FILE *file = fopen(path_cstr, "rb"); if (!file) { ant_value_t err = fs_mk_errno_error(js, errno, "open", path_cstr, NULL); free(path_cstr); return err; } fseek(file, 0, SEEK_END); long file_size = ftell(file); fseek(file, 0, SEEK_SET); if (file_size < 0) { fclose(file); free(path_cstr); return js_mkerr(js, "Failed to get file size"); } char *data = malloc(file_size); if (!data) { fclose(file); free(path_cstr); return js_mkerr(js, "Out of memory"); } size_t bytes_read = fread(data, 1, file_size, file); fclose(file); free(path_cstr); if (bytes_read != (size_t)file_size) { free(data); return js_mkerr(js, "Failed to read entire file"); } ant_value_t result = js_mkstr(js, data, file_size); free(data); return result;}
static ant_value_t builtin_fs_readFile(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "readFile() requires a path argument"); ant_value_t path_val = fs_coerce_path(js, args[0]); if (vtype(path_val) != T_STR) return js_mkerr(js, "readFile() path must be a string or URL"); size_t path_len; char *path = js_getstr(js, path_val, &path_len); if (!path) return js_mkerr(js, "Failed to get path string"); fs_request_t *req = calloc(1, sizeof(fs_request_t)); if (!req) return js_mkerr(js, "Out of memory"); req->js = js; req->op_type = FS_OP_READ; req->encoding = (nargs > 1) ? parse_encoding(js, args[1]) : FS_ENC_NONE; req->promise = js_mkpromise(js); req->path = strndup(path, path_len); req->uv_req.data = req; utarray_push_back(pending_requests, &req); int result = uv_fs_open(uv_default_loop(), &req->uv_req, req->path, O_RDONLY, 0, on_open_for_read); if (result < 0) { fs_request_fail(req, result); req->completed = 1; complete_request(req); } return req->promise;}
static ant_value_t builtin_fs_readBytes(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "readBytes() requires a path argument"); if (vtype(args[0]) != T_STR) return js_mkerr(js, "readBytes() 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"); fs_request_t *req = calloc(1, sizeof(fs_request_t)); if (!req) return js_mkerr(js, "Out of memory"); req->js = js; req->op_type = FS_OP_READ_BYTES; req->promise = js_mkpromise(js); req->path = strndup(path, path_len); req->uv_req.data = req; utarray_push_back(pending_requests, &req); int result = uv_fs_open(uv_default_loop(), &req->uv_req, req->path, O_RDONLY, 0, on_open_for_read); if (result < 0) { fs_request_fail(req, result); req->completed = 1; complete_request(req); } return req->promise;}
static ant_value_t fs_write_file_sync_impl( ant_t *js, ant_value_t *args, int nargs, const char *fn_name, const char *mode) { if (nargs < 2) return js_mkerr(js, "%s() requires path and data arguments", fn_name); if (vtype(args[0]) != T_STR) return js_mkerr(js, "%s() path must be a string", fn_name); if (vtype(args[1]) != T_STR) return js_mkerr(js, "%s() data must be a string", fn_name);
size_t path_len, data_len; char *path = js_getstr(js, args[0], &path_len); char *data = js_getstr(js, args[1], &data_len); if (!path || !data) return js_mkerr(js, "Failed to get arguments");
char *path_cstr = strndup(path, path_len); if (!path_cstr) return js_mkerr(js, "Out of memory");
FILE *file = fopen(path_cstr, mode); if (!file) { ant_value_t err = fs_mk_errno_error(js, errno, "open", path_cstr, NULL); free(path_cstr); return err; }
size_t bytes_written = fwrite(data, 1, data_len, file); fclose(file); free(path_cstr);
if (bytes_written != data_len) { return js_mkerr(js, "Failed to write entire file"); }
return js_mkundef();}
static ant_value_t builtin_fs_writeFileSync(ant_t *js, ant_value_t *args, int nargs) { return fs_write_file_sync_impl(js, args, nargs, "writeFileSync", "wb");}
static ant_value_t builtin_fs_copyFileSync(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 2) return js_mkerr(js, "copyFileSync() requires src and dest arguments"); if (vtype(args[0]) != T_STR) return js_mkerr(js, "copyFileSync() src must be a string"); if (vtype(args[1]) != T_STR) return js_mkerr(js, "copyFileSync() dest must be a string"); size_t src_len, dest_len; char *src = js_getstr(js, args[0], &src_len); char *dest = js_getstr(js, args[1], &dest_len); if (!src || !dest) return js_mkerr(js, "Failed to get arguments"); char *src_cstr = strndup(src, src_len); char *dest_cstr = strndup(dest, dest_len); if (!src_cstr || !dest_cstr) { free(src_cstr); free(dest_cstr); return js_mkerr(js, "Out of memory"); } FILE *in = fopen(src_cstr, "rb"); if (!in) { ant_value_t err = fs_mk_errno_error(js, errno, "copyfile", src_cstr, dest_cstr); free(src_cstr); free(dest_cstr); return err; } FILE *out = fopen(dest_cstr, "wb"); if (!out) { ant_value_t err = fs_mk_errno_error(js, errno, "copyfile", src_cstr, dest_cstr); fclose(in); free(src_cstr); free(dest_cstr); return err; } char buf[8192]; size_t n; while ((n = fread(buf, 1, sizeof(buf), in)) > 0) { if (fwrite(buf, 1, n, out) != n) { ant_value_t err = fs_mk_errno_error(js, errno ? errno : EIO, "copyfile", src_cstr, dest_cstr); fclose(in); fclose(out); free(src_cstr); free(dest_cstr); return err; }} fclose(in); fclose(out); free(src_cstr); free(dest_cstr);
return js_mkundef();}
typedef struct { bool recursive; bool force; bool error_on_exist;} fs_cp_options_t;
static void fs_parse_cp_options(ant_t *js, ant_value_t value, fs_cp_options_t *opts) { opts->recursive = false; opts->force = true; opts->error_on_exist = false;
if (vtype(value) != T_OBJ) return;
ant_value_t recursive = js_get(js, value, "recursive"); ant_value_t force = js_get(js, value, "force"); ant_value_t error_on_exist = js_get(js, value, "errorOnExist");
if (!is_undefined(recursive)) opts->recursive = js_truthy(js, recursive); if (!is_undefined(force)) opts->force = js_truthy(js, force); if (!is_undefined(error_on_exist)) opts->error_on_exist = js_truthy(js, error_on_exist);}
static char *fs_join_path(const char *base, const char *name) { size_t base_len = strlen(base); size_t name_len = strlen(name); bool need_sep = base_len > 0 && base[base_len - 1] != '/'; size_t total_len = base_len + (need_sep ? 1 : 0) + name_len;
char *joined = calloc(total_len + 1, 1); if (!joined) return NULL;
memcpy(joined, base, base_len); if (need_sep) joined[base_len++] = '/'; memcpy(joined + base_len, name, name_len); joined[total_len] = '\0'; return joined;}
static ant_value_t fs_copy_file_sync_impl( ant_t *js, const char *src_cstr, const char *dest_cstr, const fs_cp_options_t *opts, const char *op_name) { struct stat dest_st; if (!opts->force && stat(dest_cstr, &dest_st) == 0) { if (opts->error_on_exist) return fs_mk_errno_error(js, EEXIST, op_name, src_cstr, dest_cstr); return js_mkundef(); }
FILE *in = fopen(src_cstr, "rb"); if (!in) return fs_mk_errno_error(js, errno, op_name, src_cstr, dest_cstr);
FILE *out = fopen(dest_cstr, "wb"); if (!out) { ant_value_t err = fs_mk_errno_error(js, errno, op_name, src_cstr, dest_cstr); fclose(in); return err; }
char buf[8192]; size_t n = 0; while ((n = fread(buf, 1, sizeof(buf), in)) > 0) { if (fwrite(buf, 1, n, out) != n) { ant_value_t err = fs_mk_errno_error(js, errno ? errno : EIO, op_name, src_cstr, dest_cstr); fclose(in); fclose(out); return err; }}
fclose(in); fclose(out); return js_mkundef();}
static ant_value_t builtin_fs_copyFile(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 2) return js_mkerr(js, "copyFile() requires src and dest arguments"); if (vtype(args[0]) != T_STR) return js_mkerr(js, "copyFile() src must be a string"); if (vtype(args[1]) != T_STR) return js_mkerr(js, "copyFile() dest must be a string");
size_t src_len = 0, dest_len = 0; const char *src = js_getstr(js, args[0], &src_len); const char *dest = js_getstr(js, args[1], &dest_len); if (!src || !dest) return js_mkerr(js, "Failed to get arguments");
char *src_cstr = strndup(src, src_len); char *dest_cstr = strndup(dest, dest_len); if (!src_cstr || !dest_cstr) { free(src_cstr); free(dest_cstr); return js_mkerr(js, "Out of memory"); }
fs_cp_options_t opts = { .recursive = false, .force = true, .error_on_exist = false, };
ant_value_t promise = js_mkpromise(js); ant_value_t result = fs_copy_file_sync_impl(js, src_cstr, dest_cstr, &opts, "copyFile"); free(src_cstr); free(dest_cstr);
if (is_err(result)) js_reject_promise(js, promise, result); else js_resolve_promise(js, promise, js_mkundef()); return promise;}
static ant_value_t fs_copy_path_sync_impl( ant_t *js, const char *src_cstr, const char *dest_cstr, const fs_cp_options_t *opts, const char *op_name) { struct stat src_st; if (stat(src_cstr, &src_st) != 0) return fs_mk_errno_error(js, errno, op_name, src_cstr, dest_cstr);
if ((src_st.st_mode & S_IFMT) == S_IFDIR) { if (!opts->recursive) { return js_mkerr(js, "%s() recursive option is required to copy directories", op_name); }
struct stat dest_st; if (stat(dest_cstr, &dest_st) != 0) { if (errno != ENOENT) return fs_mk_errno_error(js, errno, op_name, src_cstr, dest_cstr); #ifdef _WIN32 if (_mkdir(dest_cstr) != 0) return fs_mk_errno_error(js, errno, op_name, src_cstr, dest_cstr); #else if (mkdir(dest_cstr, (mode_t)(src_st.st_mode & 0777)) != 0) { return fs_mk_errno_error(js, errno, op_name, src_cstr, dest_cstr); } #endif } else if ((dest_st.st_mode & S_IFMT) != S_IFDIR) { return fs_mk_errno_error(js, EEXIST, op_name, src_cstr, dest_cstr); }
uv_fs_t req; int rc = uv_fs_scandir(NULL, &req, src_cstr, 0, NULL); if (rc < 0) { ant_value_t err = fs_mk_uv_error(js, rc, "scandir", src_cstr, dest_cstr); uv_fs_req_cleanup(&req); return err; }
uv_dirent_t dirent; ant_value_t result = js_mkundef(); while (uv_fs_scandir_next(&req, &dirent) != UV_EOF) { if ( strcmp(dirent.name, ".") == 0 || strcmp(dirent.name, "..") == 0 ) continue; char *child_src = fs_join_path(src_cstr, dirent.name); char *child_dest = fs_join_path(dest_cstr, dirent.name); if (!child_src || !child_dest) { free(child_src); free(child_dest); result = js_mkerr(js, "Out of memory"); break; } result = fs_copy_path_sync_impl(js, child_src, child_dest, opts, op_name); free(child_src); free(child_dest); if (is_err(result)) break; } uv_fs_req_cleanup(&req); return result; }
return fs_copy_file_sync_impl(js, src_cstr, dest_cstr, opts, op_name);}
static ant_value_t fs_cp_sync_common( ant_t *js, ant_value_t *args, int nargs, const char *fn_name) { if (nargs < 2) return js_mkerr(js, "%s() requires src and dest arguments", fn_name); if (vtype(args[0]) != T_STR) return js_mkerr(js, "%s() src must be a string", fn_name); if (vtype(args[1]) != T_STR) return js_mkerr(js, "%s() dest must be a string", fn_name);
size_t src_len = 0, dest_len = 0; const char *src = js_getstr(js, args[0], &src_len); const char *dest = js_getstr(js, args[1], &dest_len); if (!src || !dest) return js_mkerr(js, "Failed to get arguments");
fs_cp_options_t opts; fs_parse_cp_options(js, nargs >= 3 ? args[2] : js_mkundef(), &opts);
char *src_cstr = strndup(src, src_len); char *dest_cstr = strndup(dest, dest_len); if (!src_cstr || !dest_cstr) { free(src_cstr); free(dest_cstr); return js_mkerr(js, "Out of memory"); }
ant_value_t result = fs_copy_path_sync_impl(js, src_cstr, dest_cstr, &opts, fn_name); free(src_cstr); free(dest_cstr); return result;}
static ant_value_t builtin_fs_cpSync(ant_t *js, ant_value_t *args, int nargs) { return fs_cp_sync_common(js, args, nargs, "cpSync");}
static ant_value_t builtin_fs_cp(ant_t *js, ant_value_t *args, int nargs) { ant_value_t promise = js_mkpromise(js); ant_value_t result = fs_cp_sync_common(js, args, nargs, "cp"); if (is_err(result)) js_reject_promise(js, promise, result); else js_resolve_promise(js, promise, js_mkundef()); return promise;}
static ant_value_t builtin_fs_renameSync(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 2) return js_mkerr(js, "renameSync() requires oldPath and newPath arguments"); if (vtype(args[0]) != T_STR) return js_mkerr(js, "renameSync() oldPath must be a string"); if (vtype(args[1]) != T_STR) return js_mkerr(js, "renameSync() newPath must be a string"); size_t old_len, new_len; char *old_path = js_getstr(js, args[0], &old_len); char *new_path = js_getstr(js, args[1], &new_len); if (!old_path || !new_path) return js_mkerr(js, "Failed to get arguments"); char *old_cstr = strndup(old_path, old_len); char *new_cstr = strndup(new_path, new_len); if (!old_cstr || !new_cstr) { free(old_cstr); free(new_cstr); return js_mkerr(js, "Out of memory"); } int result = rename(old_cstr, new_cstr); if (result != 0) { ant_value_t err = fs_mk_errno_error(js, errno, "rename", old_cstr, new_cstr); free(old_cstr); free(new_cstr); return err; } free(old_cstr); free(new_cstr); return js_mkundef();}
static ant_value_t builtin_fs_rename(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 2) return js_mkerr(js, "rename() requires oldPath and newPath arguments"); if (vtype(args[0]) != T_STR) return js_mkerr(js, "rename() oldPath must be a string"); if (vtype(args[1]) != T_STR) return js_mkerr(js, "rename() newPath must be a string");
size_t old_len = 0, new_len = 0; const char *old_path = js_getstr(js, args[0], &old_len); const char *new_path = js_getstr(js, args[1], &new_len); if (!old_path || !new_path) return js_mkerr(js, "Failed to get arguments");
fs_request_t *req = calloc(1, sizeof(fs_request_t)); if (!req) return js_mkerr(js, "Out of memory");
req->js = js; req->op_type = FS_OP_RENAME; req->promise = js_mkpromise(js); req->path = strndup(old_path, old_len); req->path2 = strndup(new_path, new_len); req->uv_req.data = req;
if (!req->path || !req->path2) { free_fs_request(req); return js_mkerr(js, "Out of memory"); }
utarray_push_back(pending_requests, &req); int result = uv_fs_rename(uv_default_loop(), &req->uv_req, req->path, req->path2, on_rename_complete);
if (result < 0) { fs_request_fail(req, result); req->completed = 1; complete_request(req); }
return req->promise;}
static double fs_time_arg_to_seconds(ant_t *js, ant_value_t v) { if (is_date_instance(v)) { ant_value_t t = js_get_slot(js_as_obj(v), SLOT_DATA); return (vtype(t) == T_NUM) ? js_getnum(t) / 1000.0 : 0.0; } return js_to_number(js, v);}
static ant_value_t builtin_fs_utimesSync(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 3) return js_mkerr(js, "utimesSync() requires path, atime, and mtime"); if (vtype(args[0]) != T_STR) return js_mkerr(js, "utimesSync() path must be a string");
const char *path = js_str(js, args[0]); double atime = fs_time_arg_to_seconds(js, args[1]); double mtime = fs_time_arg_to_seconds(js, args[2]);
uv_fs_t req; int rc = uv_fs_utime(NULL, &req, path, atime, mtime, NULL); uv_fs_req_cleanup(&req);
if (rc < 0) return js_mkerr(js, "utimesSync: %s", uv_strerror(rc)); return js_mkundef();}
static ant_value_t builtin_fs_utimes(ant_t *js, ant_value_t *args, int nargs) { return builtin_fs_utimesSync(js, args, nargs);}
static ant_value_t builtin_fs_futimesSync(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 3) return js_mkerr(js, "futimesSync() requires fd, atime, and mtime"); int fd = (int)js_to_number(js, args[0]); double atime = fs_time_arg_to_seconds(js, args[1]); double mtime = fs_time_arg_to_seconds(js, args[2]);
uv_fs_t req; int rc = uv_fs_futime(NULL, &req, fd, atime, mtime, NULL); uv_fs_req_cleanup(&req);
if (rc < 0) return js_mkerr(js, "futimesSync: %s", uv_strerror(rc)); return js_mkundef();}
static ant_value_t builtin_fs_futimes(ant_t *js, ant_value_t *args, int nargs) { return builtin_fs_futimesSync(js, args, nargs);}
static ant_value_t builtin_fs_appendFileSync(ant_t *js, ant_value_t *args, int nargs) { return fs_write_file_sync_impl(js, args, nargs, "appendFileSync", "ab");}
static ant_value_t builtin_fs_appendFile(ant_t *js, ant_value_t *args, int nargs) { ant_value_t promise = js_mkpromise(js); ant_value_t result = fs_write_file_sync_impl(js, args, nargs, "appendFile", "ab"); if (is_err(result)) js_reject_promise(js, promise, result); else js_resolve_promise(js, promise, js_mkundef()); return promise;}
static ant_value_t builtin_fs_writeFile(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 2) return js_mkerr(js, "writeFile() requires path and data arguments"); if (vtype(args[0]) != T_STR) return js_mkerr(js, "writeFile() path must be a string"); if (vtype(args[1]) != T_STR) return js_mkerr(js, "writeFile() data must be a string"); size_t path_len, data_len; char *path = js_getstr(js, args[0], &path_len); char *data = js_getstr(js, args[1], &data_len); if (!path || !data) return js_mkerr(js, "Failed to get arguments"); fs_request_t *req = calloc(1, sizeof(fs_request_t)); if (!req) return js_mkerr(js, "Out of memory"); req->js = js; req->op_type = FS_OP_WRITE; req->promise = js_mkpromise(js); req->path = strndup(path, path_len); req->data = malloc(data_len); if (!req->data) { free(req->path); free(req); return js_mkerr(js, "Out of memory"); } memcpy(req->data, data, data_len); req->data_len = data_len; req->uv_req.data = req; utarray_push_back(pending_requests, &req); int result = uv_fs_open(uv_default_loop(), &req->uv_req, req->path, O_WRONLY | O_CREAT | O_TRUNC, 0644, on_open_for_write); if (result < 0) { fs_request_fail(req, result); req->completed = 1; complete_request(req); } return req->promise;}
static ant_value_t builtin_fs_unlinkSync(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "unlinkSync() requires a path argument"); if (vtype(args[0]) != T_STR) return js_mkerr(js, "unlinkSync() 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"); char *path_cstr = strndup(path, path_len); if (!path_cstr) return js_mkerr(js, "Out of memory"); int result = unlink(path_cstr); if (result != 0) { ant_value_t err = fs_mk_errno_error(js, errno, "unlink", path_cstr, NULL); free(path_cstr); return err; } free(path_cstr); return js_mkundef();}
static ant_value_t builtin_fs_unlink(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "unlink() requires a path argument"); if (vtype(args[0]) != T_STR) return js_mkerr(js, "unlink() 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"); fs_request_t *req = calloc(1, sizeof(fs_request_t)); if (!req) return js_mkerr(js, "Out of memory"); req->js = js; req->op_type = FS_OP_UNLINK; req->promise = js_mkpromise(js); req->path = strndup(path, path_len); req->uv_req.data = req; utarray_push_back(pending_requests, &req); int result = uv_fs_unlink(uv_default_loop(), &req->uv_req, req->path, on_unlink_complete); if (result < 0) { fs_request_fail(req, result); req->completed = 1; complete_request(req); } return req->promise;}
static ant_value_t builtin_fs_mkdirSync(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "mkdirSync() requires a path argument"); if (vtype(args[0]) != T_STR) return js_mkerr(js, "mkdirSync() 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"); int mode = 0755; int recursive = 0; if (nargs < 2) goto do_mkdir; switch (vtype(args[1])) { case T_NUM: mode = (int)js_getnum(args[1]); break; case T_OBJ: { ant_value_t opt = args[1]; recursive = js_get(js, opt, "recursive") == js_true; ant_value_t mode_val = js_get(js, opt, "mode"); if (vtype(mode_val) == T_NUM) mode = (int)js_getnum(mode_val); break; } } do_mkdir: char *path_cstr = strndup(path, path_len); if (!path_cstr) return js_mkerr(js, "Out of memory"); int result; if (recursive) { result = mkdirp(path_cstr, (mode_t)mode); } else {#ifdef _WIN32 (void)mode; result = _mkdir(path_cstr);#else result = mkdir(path_cstr, (mode_t)mode);#endif } if (result != 0) { ant_value_t err = fs_mk_errno_error(js, errno, "mkdir", path_cstr, NULL); free(path_cstr); return err; } free(path_cstr); return js_mkundef();}
static ant_value_t builtin_fs_mkdir(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "mkdir() requires a path argument"); if (vtype(args[0]) != T_STR) return js_mkerr(js, "mkdir() 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"); int mode = 0755; int recursive = 0; if (nargs >= 2) { switch (vtype(args[1])) { case T_NUM: mode = (int)js_getnum(args[1]); break; case T_OBJ: { ant_value_t opt = args[1]; recursive = js_get(js, opt, "recursive") == js_true; ant_value_t mode_val = js_get(js, opt, "mode"); if (vtype(mode_val) == T_NUM) mode = (int)js_getnum(mode_val); break; }} }
if (recursive) { ant_value_t promise = js_mkpromise(js); char *path_cstr = strndup(path, path_len); if (!path_cstr) return js_mkerr(js, "Out of memory"); int result = mkdirp(path_cstr, (mode_t)mode); free(path_cstr); if (result != 0) { js_reject_promise(js, promise, js_mkerr(js, "Failed to create directory: %s", strerror(errno))); } else js_resolve_promise(js, promise, js_mkundef()); return promise; }
fs_request_t *req = calloc(1, sizeof(fs_request_t)); if (!req) return js_mkerr(js, "Out of memory");
req->js = js; req->op_type = FS_OP_MKDIR; req->promise = js_mkpromise(js); req->path = strndup(path, path_len); req->recursive = recursive; req->uv_req.data = req; utarray_push_back(pending_requests, &req); int result = uv_fs_mkdir(uv_default_loop(), &req->uv_req, req->path, mode, on_mkdir_complete); if (result < 0) { fs_request_fail(req, result); req->completed = 1; complete_request(req); } return req->promise;}
static ant_value_t builtin_fs_mkdtempSync(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1 || vtype(args[0]) != T_STR) return js_mkerr(js, "mkdtempSync() requires a prefix string");
size_t prefix_len; const char *prefix = js_getstr(js, args[0], &prefix_len); if (!prefix) return js_mkerr(js, "Failed to get prefix string");
size_t tpl_len = prefix_len + 6; char *tpl = malloc(tpl_len + 1); if (!tpl) return js_mkerr(js, "Out of memory");
memcpy(tpl, prefix, prefix_len); memcpy(tpl + prefix_len, "XXXXXX", 6); tpl[tpl_len] = '\0';
char *result = mkdtemp(tpl); if (!result) { free(tpl); return js_mkerr(js, "mkdtempSync failed: %s", strerror(errno)); }
ant_value_t ret = js_mkstr(js, result, strlen(result)); free(tpl); return ret;}
static ant_value_t builtin_fs_mkdtemp(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1 || vtype(args[0]) != T_STR) return js_mkerr(js, "mkdtemp() requires a prefix string");
size_t prefix_len; const char *prefix = js_getstr(js, args[0], &prefix_len); if (!prefix) return js_mkerr(js, "Failed to get prefix string");
size_t tpl_len = prefix_len + 6; char *tpl = malloc(tpl_len + 1); if (!tpl) return js_mkerr(js, "Out of memory");
memcpy(tpl, prefix, prefix_len); memcpy(tpl + prefix_len, "XXXXXX", 6); tpl[tpl_len] = '\0';
fs_request_t *req = calloc(1, sizeof(fs_request_t)); if (!req) { free(tpl); return js_mkerr(js, "Out of memory"); }
req->js = js; req->op_type = FS_OP_MKDTEMP; req->promise = js_mkpromise(js); req->path = tpl; req->uv_req.data = req;
utarray_push_back(pending_requests, &req); int result = uv_fs_mkdtemp(uv_default_loop(), &req->uv_req, req->path, on_mkdtemp_complete);
if (result < 0) { fs_request_fail(req, result); req->completed = 1; complete_request(req); }
return req->promise;}
static ant_value_t builtin_fs_rmdirSync(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "rmdirSync() requires a path argument"); if (vtype(args[0]) != T_STR) return js_mkerr(js, "rmdirSync() 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"); char *path_cstr = strndup(path, path_len); if (!path_cstr) return js_mkerr(js, "Out of memory"); #ifdef _WIN32 int result = _rmdir(path_cstr);#else int result = rmdir(path_cstr);#endif if (result != 0) { ant_value_t err = fs_mk_errno_error(js, errno, "rmdir", path_cstr, NULL); free(path_cstr); return err; } free(path_cstr); return js_mkundef();}
static ant_value_t builtin_fs_rmdir(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "rmdir() requires a path argument"); if (vtype(args[0]) != T_STR) return js_mkerr(js, "rmdir() 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"); fs_request_t *req = calloc(1, sizeof(fs_request_t)); if (!req) return js_mkerr(js, "Out of memory"); req->js = js; req->op_type = FS_OP_RMDIR; req->promise = js_mkpromise(js); req->path = strndup(path, path_len); req->uv_req.data = req; utarray_push_back(pending_requests, &req); int result = uv_fs_rmdir(uv_default_loop(), &req->uv_req, req->path, on_rmdir_complete); if (result < 0) { fs_request_fail(req, result); req->completed = 1; complete_request(req); } return req->promise;}
static ant_value_t builtin_fs_rmSync(ant_t *js, ant_value_t *args, int nargs) { return fs_rm_impl(js, args, nargs, false);}
static ant_value_t builtin_fs_rm(ant_t *js, ant_value_t *args, int nargs) { return fs_rm_impl(js, args, nargs, true);}
static ant_value_t dirent_isFile(ant_t *js, ant_value_t *args, int nargs) { ant_value_t this = js_getthis(js); ant_value_t type_val = js_get_slot(this, SLOT_DATA); if (vtype(type_val) != T_NUM) return js_false; return js_bool((int)js_getnum(type_val) == UV_DIRENT_FILE);}
static ant_value_t dirent_isDirectory(ant_t *js, ant_value_t *args, int nargs) { ant_value_t this = js_getthis(js); ant_value_t type_val = js_get_slot(this, SLOT_DATA); if (vtype(type_val) != T_NUM) return js_false; return js_bool((int)js_getnum(type_val) == UV_DIRENT_DIR);}
static ant_value_t dirent_isSymbolicLink(ant_t *js, ant_value_t *args, int nargs) { ant_value_t this = js_getthis(js); ant_value_t type_val = js_get_slot(this, SLOT_DATA); if (vtype(type_val) != T_NUM) return js_false; return js_bool((int)js_getnum(type_val) == UV_DIRENT_LINK);}
static ant_value_t dirent_isBlockDevice(ant_t *js, ant_value_t *args, int nargs) { ant_value_t this = js_getthis(js); ant_value_t type_val = js_get_slot(this, SLOT_DATA); if (vtype(type_val) != T_NUM) return js_false; return js_bool((int)js_getnum(type_val) == UV_DIRENT_BLOCK);}
static ant_value_t dirent_isCharacterDevice(ant_t *js, ant_value_t *args, int nargs) { ant_value_t this = js_getthis(js); ant_value_t type_val = js_get_slot(this, SLOT_DATA); if (vtype(type_val) != T_NUM) return js_false; return js_bool((int)js_getnum(type_val) == UV_DIRENT_CHAR);}
static ant_value_t dirent_isFIFO(ant_t *js, ant_value_t *args, int nargs) { ant_value_t this = js_getthis(js); ant_value_t type_val = js_get_slot(this, SLOT_DATA); if (vtype(type_val) != T_NUM) return js_false; return js_bool((int)js_getnum(type_val) == UV_DIRENT_FIFO);}
static ant_value_t dirent_isSocket(ant_t *js, ant_value_t *args, int nargs) { ant_value_t this = js_getthis(js); ant_value_t type_val = js_get_slot(this, SLOT_DATA); if (vtype(type_val) != T_NUM) return js_false; return js_bool((int)js_getnum(type_val) == UV_DIRENT_SOCKET);}
static ant_value_t stat_isFile(ant_t *js, ant_value_t *args, int nargs) { ant_value_t this = js_getthis(js); ant_value_t mode_val = js_get_slot(this, SLOT_DATA); if (vtype(mode_val) != T_NUM) return js_false; mode_t mode = (mode_t)js_getnum(mode_val); return js_bool(S_ISREG(mode));}
static ant_value_t stat_isDirectory(ant_t *js, ant_value_t *args, int nargs) { ant_value_t this = js_getthis(js); ant_value_t mode_val = js_get_slot(this, SLOT_DATA); if (vtype(mode_val) != T_NUM) return js_false; mode_t mode = (mode_t)js_getnum(mode_val); return js_bool(S_ISDIR(mode));}
static ant_value_t stat_isSymbolicLink(ant_t *js, ant_value_t *args, int nargs) { ant_value_t this = js_getthis(js); ant_value_t mode_val = js_get_slot(this, SLOT_DATA); if (vtype(mode_val) != T_NUM) return js_false; mode_t mode = (mode_t)js_getnum(mode_val); return js_bool(S_ISLNK(mode));}
static ant_value_t create_stats_object(ant_t *js, struct stat *st) { return fs_stats_object_from_posix(js, st);}
static const char *errno_to_code(int err_num) {switch (err_num) { case ENOENT: return "ENOENT"; case EACCES: return "EACCES"; case ENOTDIR: return "ENOTDIR"; case ELOOP: return "ELOOP"; case ENAMETOOLONG: return "ENAMETOOLONG"; case EOVERFLOW: return "EOVERFLOW"; case EROFS: return "EROFS"; case ETXTBSY: return "ETXTBSY"; case EEXIST: return "EEXIST"; case ENOTEMPTY: return "ENOTEMPTY"; case EISDIR: return "EISDIR"; case EBUSY: return "EBUSY"; case EINVAL: return "EINVAL"; case EPERM: return "EPERM"; case EIO: return "EIO"; default: return "UNKNOWN";}}
static ant_value_t fs_mk_syscall_error( ant_t *js, const char *code, double err_num, const char *message, const char *syscall, const char *path, const char *dest) { ant_value_t props = js_mkobj(js);
if (!code) code = "UNKNOWN"; if (!message) message = "Unknown error";
js_set(js, props, "code", js_mkstr(js, code, strlen(code))); js_set(js, props, "errno", js_mknum(err_num)); if (syscall) js_set(js, props, "syscall", js_mkstr(js, syscall, strlen(syscall))); if (path) js_set(js, props, "path", js_mkstr(js, path, strlen(path))); if (dest) js_set(js, props, "dest", js_mkstr(js, dest, strlen(dest)));
if (path && dest) return js_mkerr_props( js, JS_ERR_GENERIC, props, "%s: %s, %s '%s' -> '%s'", code, message, syscall, path, dest ); if (path) return js_mkerr_props( js, JS_ERR_GENERIC, props, "%s: %s, %s '%s'", code, message, syscall, path ); return js_mkerr_props( js, JS_ERR_GENERIC, props, "%s: %s, %s", code, message, syscall );}
static ant_value_t fs_mk_errno_error( ant_t *js, int err_num, const char *syscall, const char *path, const char *dest) { int uv_code = uv_translate_sys_error(err_num); if (uv_code == 0) uv_code = -err_num; return fs_mk_syscall_error( js, errno_to_code(err_num), (double)uv_code, uv_strerror(uv_code), syscall, path, dest );}
static ant_value_t fs_mk_uv_error( ant_t *js, int uv_code, const char *syscall, const char *path, const char *dest) { return fs_mk_syscall_error( js, uv_err_name(uv_code), (double)uv_code, uv_strerror(uv_code), syscall, path, dest );}
static ant_value_t builtin_fs_statSync(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "statSync() requires a path argument"); if (vtype(args[0]) != T_STR) return js_mkerr(js, "statSync() 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"); char *path_cstr = strndup(path, path_len); if (!path_cstr) return js_mkerr(js, "Out of memory"); struct stat st; int result = stat(path_cstr, &st); if (result != 0) { ant_value_t err = fs_mk_errno_error(js, errno, "stat", path_cstr, NULL); free(path_cstr); return err; } free(path_cstr); return create_stats_object(js, &st);}
static ant_value_t builtin_fs_stat(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "stat() requires a path argument"); if (vtype(args[0]) != T_STR) return js_mkerr(js, "stat() 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"); fs_request_t *req = calloc(1, sizeof(fs_request_t)); if (!req) return js_mkerr(js, "Out of memory"); req->js = js; req->op_type = FS_OP_STAT; req->promise = js_mkpromise(js); req->path = strndup(path, path_len); req->uv_req.data = req; utarray_push_back(pending_requests, &req); int result = uv_fs_stat(uv_default_loop(), &req->uv_req, req->path, on_stat_complete); if (result < 0) { fs_request_fail(req, result); req->completed = 1; complete_request(req); } return req->promise;}
static ant_value_t builtin_fs_lstatSync(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "lstatSync() requires a path argument"); if (vtype(args[0]) != T_STR) return js_mkerr(js, "lstatSync() 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"); char *path_cstr = strndup(path, path_len); if (!path_cstr) return js_mkerr(js, "Out of memory"); uv_fs_t req; int result = uv_fs_lstat(NULL, &req, path_cstr, NULL);
if (result < 0) { ant_value_t err = fs_mk_uv_error(js, result, "lstat", path_cstr, NULL); uv_fs_req_cleanup(&req); free(path_cstr); return err; }
ant_value_t stat_obj = fs_stats_object_from_uv(js, &req.statbuf); uv_fs_req_cleanup(&req); free(path_cstr); return stat_obj;}
static ant_value_t builtin_fs_lstat(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "lstat() requires a path argument"); if (vtype(args[0]) != T_STR) return js_mkerr(js, "lstat() 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"); fs_request_t *req = calloc(1, sizeof(fs_request_t)); if (!req) return js_mkerr(js, "Out of memory"); req->js = js; req->op_type = FS_OP_STAT; req->promise = js_mkpromise(js); req->path = strndup(path, path_len); req->uv_req.data = req; utarray_push_back(pending_requests, &req); int result = uv_fs_lstat(uv_default_loop(), &req->uv_req, req->path, on_stat_complete); if (result < 0) { fs_request_fail(req, result); req->completed = 1; complete_request(req); } return req->promise;}
static ant_value_t builtin_fs_fstatSync(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "fstatSync() requires an fd argument"); if (vtype(args[0]) != T_NUM) return js_mkerr(js, "fstatSync() fd must be a number");
uv_file fd = (uv_file)(int)js_getnum(args[0]); uv_fs_t req; int result = uv_fs_fstat(NULL, &req, fd, NULL);
if (result < 0) { ant_value_t err = fs_mk_uv_error(js, result, "fstat", NULL, NULL); uv_fs_req_cleanup(&req); return err; }
ant_value_t stat_obj = fs_stats_object_from_uv(js, &req.statbuf); uv_fs_req_cleanup(&req); return stat_obj;}
static ant_value_t builtin_fs_fstat(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "fstat() requires an fd argument"); if (vtype(args[0]) != T_NUM) return js_mkerr(js, "fstat() fd must be a number");
fs_request_t *req = calloc(1, sizeof(fs_request_t)); if (!req) return js_mkerr(js, "Out of memory");
req->js = js; req->op_type = FS_OP_STAT; req->promise = js_mkpromise(js); req->fd = (uv_file)(int)js_getnum(args[0]); req->uv_req.data = req;
utarray_push_back(pending_requests, &req); int result = uv_fs_fstat(uv_default_loop(), &req->uv_req, req->fd, on_stat_complete);
if (result < 0) { fs_request_fail(req, result); req->completed = 1; complete_request(req); }
return req->promise;}
static ant_value_t builtin_fs_existsSync(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "existsSync() requires a path argument"); if (vtype(args[0]) != T_STR) return js_mkerr(js, "existsSync() 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"); char *path_cstr = strndup(path, path_len); if (!path_cstr) return js_mkerr(js, "Out of memory"); struct stat st; int result = stat(path_cstr, &st); free(path_cstr); return js_bool(result == 0);}
static ant_value_t builtin_fs_exists(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "exists() requires a path argument"); if (vtype(args[0]) != T_STR) return js_mkerr(js, "exists() 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"); fs_request_t *req = calloc(1, sizeof(fs_request_t)); if (!req) return js_mkerr(js, "Out of memory"); req->js = js; req->op_type = FS_OP_EXISTS; req->promise = js_mkpromise(js); req->path = strndup(path, path_len); req->uv_req.data = req; utarray_push_back(pending_requests, &req); int result = uv_fs_stat(uv_default_loop(), &req->uv_req, req->path, on_exists_complete); if (result < 0) { req->completed = 1; js_resolve_promise(req->js, req->promise, js_false); remove_pending_request(req); free_fs_request(req); } return req->promise;}
static ant_value_t builtin_fs_accessSync(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "accessSync() requires a path argument"); if (vtype(args[0]) != T_STR) return js_mkerr(js, "accessSync() 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"); int mode = F_OK; if (nargs >= 2 && vtype(args[1]) == T_NUM) { mode = (int)js_getnum(args[1]); } char *path_cstr = strndup(path, path_len); if (!path_cstr) return js_mkerr(js, "Out of memory"); int result = access(path_cstr, mode); if (result != 0) { ant_value_t err = fs_mk_errno_error(js, errno, "access", path_cstr, NULL); free(path_cstr); return err; } free(path_cstr); return js_mkundef();}
static ant_value_t builtin_fs_chmodSync(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "chmodSync() requires a path argument"); if (nargs < 2) return js_mkerr(js, "chmodSync() requires a mode argument");
ant_value_t path_val = fs_coerce_path(js, args[0]); if (vtype(path_val) != T_STR) return js_mkerr(js, "chmodSync() path must be a string or URL");
mode_t mode = 0; if (!fs_parse_mode(js, args[1], &mode)) { return js_mkerr(js, "chmodSync() mode must be a number or octal string"); }
size_t path_len = 0; char *path = js_getstr(js, path_val, &path_len); if (!path) return js_mkerr(js, "Failed to get path string");
char *path_cstr = strndup(path, path_len); if (!path_cstr) return js_mkerr(js, "Out of memory");
uv_fs_t req; int result = uv_fs_chmod(uv_default_loop(), &req, path_cstr, mode, NULL);
if (result < 0) { ant_value_t err = fs_mk_uv_error(js, result, "chmod", path_cstr, NULL); uv_fs_req_cleanup(&req); free(path_cstr); return err; }
uv_fs_req_cleanup(&req); free(path_cstr); return js_mkundef();}
static ant_value_t builtin_fs_chmod(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "chmod() requires a path argument"); if (nargs < 2) return js_mkerr(js, "chmod() requires a mode argument");
ant_value_t path_val = fs_coerce_path(js, args[0]); if (vtype(path_val) != T_STR) return js_mkerr(js, "chmod() path must be a string or URL");
mode_t mode = 0; if (!fs_parse_mode(js, args[1], &mode)) { return js_mkerr(js, "chmod() mode must be a number or octal string"); }
size_t path_len = 0; char *path = js_getstr(js, path_val, &path_len); if (!path) return js_mkerr(js, "Failed to get path string");
fs_request_t *req = calloc(1, sizeof(fs_request_t)); if (!req) return js_mkerr(js, "Out of memory");
req->js = js; req->op_type = FS_OP_CHMOD; req->promise = js_mkpromise(js); req->path = strndup(path, path_len); req->uv_req.data = req;
if (!req->path) { free_fs_request(req); return js_mkerr(js, "Out of memory"); }
utarray_push_back(pending_requests, &req); int result = uv_fs_chmod(uv_default_loop(), &req->uv_req, req->path, mode, on_chmod_complete);
if (result < 0) { fs_request_fail(req, result); req->completed = 1; complete_request(req); }
return req->promise;}
static ant_value_t builtin_fs_access(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "access() requires a path argument"); if (vtype(args[0]) != T_STR) return js_mkerr(js, "access() 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"); int mode = F_OK; if (nargs >= 2 && vtype(args[1]) == T_NUM) { mode = (int)js_getnum(args[1]); } fs_request_t *req = calloc(1, sizeof(fs_request_t)); if (!req) return js_mkerr(js, "Out of memory"); req->js = js; req->op_type = FS_OP_ACCESS; req->promise = js_mkpromise(js); req->path = strndup(path, path_len); req->uv_req.data = req; utarray_push_back(pending_requests, &req); int result = uv_fs_access(uv_default_loop(), &req->uv_req, req->path, mode, on_access_complete); if (result < 0) { fs_request_fail(req, result); req->completed = 1; complete_request(req); } return req->promise;}
static ant_value_t builtin_fs_realpathSync(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "realpathSync() requires a path argument");
ant_value_t path_val = fs_coerce_path(js, args[0]); if (vtype(path_val) != T_STR) return js_mkerr(js, "realpathSync() path must be a string");
size_t path_len = 0; const char *path = js_getstr(js, path_val, &path_len); if (!path) return js_mkerr(js, "Failed to get path string");
char *resolved = realpath(path, NULL); if (!resolved) return js_mkerr(js, "realpathSync failed for '%s'", path);
ant_value_t result = js_mkstr(js, resolved, strlen(resolved)); free(resolved); return result;}
static ant_value_t builtin_fs_realpath(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "realpath() requires a path argument");
ant_value_t path_val = fs_coerce_path(js, args[0]); if (vtype(path_val) != T_STR) return js_mkerr(js, "realpath() path must be a string");
size_t path_len = 0; const char *path = js_getstr(js, path_val, &path_len); if (!path) return js_mkerr(js, "Failed to get path string");
fs_request_t *req = calloc(1, sizeof(fs_request_t)); if (!req) return js_mkerr(js, "Out of memory");
req->js = js; req->op_type = FS_OP_REALPATH; req->promise = js_mkpromise(js); req->path = strndup(path, path_len); req->uv_req.data = req;
if (!req->path) { free_fs_request(req); return js_mkerr(js, "Out of memory"); }
utarray_push_back(pending_requests, &req); int result = uv_fs_realpath(uv_default_loop(), &req->uv_req, req->path, on_realpath_complete);
if (result < 0) { fs_request_fail(req, result); req->completed = 1; complete_request(req); }
return req->promise;}
static ant_value_t builtin_fs_readlinkSync(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "readlinkSync() requires a path argument");
ant_value_t path_val = fs_coerce_path(js, args[0]); if (vtype(path_val) != T_STR) return js_mkerr(js, "readlinkSync() path must be a string");
size_t path_len = 0; const char *path = js_getstr(js, path_val, &path_len); if (!path) return js_mkerr(js, "Failed to get path string");
char *path_cstr = strndup(path, path_len); if (!path_cstr) return js_mkerr(js, "Out of memory");
uv_fs_t req; int result = uv_fs_readlink(NULL, &req, path_cstr, NULL);
if (result < 0 || !req.ptr) { ant_value_t err = fs_mk_uv_error(js, result, "readlink", path_cstr, NULL); uv_fs_req_cleanup(&req); free(path_cstr); return err; }
ant_value_t link = js_mkstr(js, (const char *)req.ptr, strlen((const char *)req.ptr)); uv_fs_req_cleanup(&req); free(path_cstr); return link;}
static ant_value_t builtin_fs_readlink(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "readlink() requires a path argument");
ant_value_t path_val = fs_coerce_path(js, args[0]); if (vtype(path_val) != T_STR) return js_mkerr(js, "readlink() path must be a string");
size_t path_len = 0; const char *path = js_getstr(js, path_val, &path_len); if (!path) return js_mkerr(js, "Failed to get path string");
fs_request_t *req = calloc(1, sizeof(fs_request_t)); if (!req) return js_mkerr(js, "Out of memory");
req->js = js; req->op_type = FS_OP_REALPATH; req->promise = js_mkpromise(js); req->path = strndup(path, path_len); req->uv_req.data = req;
if (!req->path) { free_fs_request(req); return js_mkerr(js, "Out of memory"); }
utarray_push_back(pending_requests, &req); int result = uv_fs_readlink(uv_default_loop(), &req->uv_req, req->path, on_realpath_complete);
if (result < 0) { fs_request_fail(req, result); req->completed = 1; complete_request(req); }
return req->promise;}
static ant_value_t builtin_fs_readdirSync(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "readdirSync() requires a path argument"); if (vtype(args[0]) != T_STR) return js_mkerr(js, "readdirSync() path must be a string"); bool with_file_types = false; if (nargs >= 2 && vtype(args[1]) == T_OBJ) { ant_value_t wft = js_get(js, args[1], "withFileTypes"); with_file_types = js_truthy(js, wft); }
size_t path_len; char *path = js_getstr(js, args[0], &path_len); if (!path) return js_mkerr(js, "Failed to get path string"); char *path_cstr = strndup(path, path_len); if (!path_cstr) return js_mkerr(js, "Out of memory"); uv_fs_t req; int result = uv_fs_scandir(NULL, &req, path_cstr, 0, NULL); if (result < 0) { ant_value_t err = fs_mk_uv_error(js, result, "scandir", path_cstr, NULL); uv_fs_req_cleanup(&req); free(path_cstr); return err; } free(path_cstr); ant_value_t arr = js_mkarr(js); uv_dirent_t dirent; while (uv_fs_scandir_next(&req, &dirent) != UV_EOF) { if (with_file_types) { ant_value_t entry = create_dirent_object(js, dirent.name, strlen(dirent.name), dirent.type); js_arr_push(js, arr, entry); } else { ant_value_t name = js_mkstr(js, dirent.name, strlen(dirent.name)); js_arr_push(js, arr, name); }} uv_fs_req_cleanup(&req); return arr;}
static ant_value_t builtin_fs_readdir(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "readdir() requires a path argument"); if (vtype(args[0]) != T_STR) return js_mkerr(js, "readdir() path must be a string"); bool with_file_types = false; if (nargs >= 2 && vtype(args[1]) == T_OBJ) { ant_value_t wft = js_get(js, args[1], "withFileTypes"); with_file_types = js_truthy(js, wft); }
size_t path_len; char *path = js_getstr(js, args[0], &path_len); if (!path) return js_mkerr(js, "Failed to get path string"); fs_request_t *req = calloc(1, sizeof(fs_request_t)); if (!req) return js_mkerr(js, "Out of memory"); req->js = js; req->op_type = FS_OP_READDIR; req->with_file_types = with_file_types; req->promise = js_mkpromise(js); req->path = strndup(path, path_len); req->uv_req.data = req; utarray_push_back(pending_requests, &req); int result = uv_fs_scandir(uv_default_loop(), &req->uv_req, req->path, 0, on_readdir_complete); if (result < 0) { fs_request_fail(req, result); req->completed = 1; complete_request(req); } return req->promise;}
static void on_write_fd_complete(uv_fs_t *uv_req) { fs_request_t *req = (fs_request_t *)uv_req->data; if (uv_req->result < 0) { fs_request_fail(req, (int)uv_req->result); req->completed = 1; complete_request(req); return; } req->completed = 1; js_resolve_promise(req->js, req->promise, js_mknum((double)uv_req->result)); remove_pending_request(req); free_fs_request(req);}
static void on_read_fd_complete(uv_fs_t *uv_req) { fs_request_t *req = (fs_request_t *)uv_req->data;
if (uv_req->result < 0) { if (is_callable(req->callback_fn)) { ant_value_t err = js_mkerr(req->js, "read failed: %s", uv_strerror((int)uv_req->result)); ant_value_t cb_args[1] = { err }; fs_call_value(req->js, req->callback_fn, js_mkundef(), cb_args, 1); remove_pending_request(req); free_fs_request(req); return; } fs_request_fail(req, (int)uv_req->result); req->completed = 1; complete_request(req); return; }
ssize_t bytes_read = uv_req->result;
if (req->data && bytes_read > 0) { TypedArrayData *ta = buffer_get_typedarray_data(req->target_buffer); if (ta && ta->buffer && ta->buffer->data) { uint8_t *dest = ta->buffer->data + ta->byte_offset + req->buf_offset; memcpy(dest, req->data, (size_t)bytes_read); } }
if (is_callable(req->callback_fn)) { ant_value_t cb_args[3] = { js_mknull(), js_mknum((double)bytes_read), req->target_buffer }; fs_call_value(req->js, req->callback_fn, js_mkundef(), cb_args, 3); remove_pending_request(req); free_fs_request(req); return; }
req->completed = 1; js_resolve_promise(req->js, req->promise, js_mknum((double)bytes_read)); remove_pending_request(req); free_fs_request(req);}
static ant_value_t builtin_fs_read_fd(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 2) return js_mkerr(js, "read() requires fd and buffer arguments"); if (vtype(args[0]) != T_NUM) return js_mkerr(js, "read() fd must be a number");
int fd = (int)js_getnum(args[0]);
ant_value_t buf_arg = args[1]; TypedArrayData *ta_data = buffer_get_typedarray_data(buf_arg); if (!ta_data || !ta_data->buffer || !ta_data->buffer->data) return js_mkerr(js, "read() buffer argument must be a Buffer or TypedArray");
size_t buf_len = ta_data->byte_length; size_t offset = 0; size_t length = buf_len; int64_t position = -1;
ant_value_t callback = js_mkundef(); int data_nargs = nargs; if (nargs >= 3 && is_callable(args[nargs - 1])) { callback = args[nargs - 1]; data_nargs = nargs - 1; }
if (data_nargs >= 3 && vtype(args[2]) == T_NUM) offset = (size_t)js_getnum(args[2]); if (data_nargs >= 4 && vtype(args[3]) == T_NUM) length = (size_t)js_getnum(args[3]); if (data_nargs >= 5 && vtype(args[4]) == T_NUM) position = (int64_t)js_getnum(args[4]);
if (offset > buf_len) return js_mkerr(js, "read() offset out of bounds"); if (offset + length > buf_len) return js_mkerr(js, "read() length extends beyond buffer");
fs_request_t *req = calloc(1, sizeof(fs_request_t)); if (!req) return js_mkerr(js, "Out of memory");
req->js = js; req->op_type = FS_OP_READ_FD; req->promise = js_mkpromise(js); req->fd = fd; req->target_buffer = buf_arg; req->buf_offset = offset; req->data_len = length; req->callback_fn = callback; req->data = malloc(length > 0 ? length : 1); if (!req->data) { free(req); return js_mkerr(js, "Out of memory"); } req->uv_req.data = req;
utarray_push_back(pending_requests, &req);
uv_buf_t buf = uv_buf_init(req->data, (unsigned int)length); int result = uv_fs_read(uv_default_loop(), &req->uv_req, req->fd, &buf, 1, position, on_read_fd_complete);
if (result < 0) { fs_request_fail(req, result); req->completed = 1; complete_request(req); }
return req->promise;}
static ant_value_t builtin_fs_readSync(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 2) return js_mkerr(js, "readSync() requires fd and buffer arguments"); if (vtype(args[0]) != T_NUM) return js_mkerr(js, "readSync() fd must be a number"); int fd = (int)js_getnum(args[0]); TypedArrayData *ta_data = buffer_get_typedarray_data(args[1]); if (!ta_data || !ta_data->buffer || !ta_data->buffer->data) return js_mkerr(js, "readSync() second argument must be a Buffer, TypedArray, or DataView"); uint8_t *buf_data = ta_data->buffer->data + ta_data->byte_offset; size_t buf_len = ta_data->byte_length; size_t offset = 0; size_t length = buf_len; int64_t position = -1; if (nargs >= 3) { if (vtype(args[2]) == T_OBJ) { ant_value_t off_val = js_get(js, args[2], "offset"); ant_value_t len_val = js_get(js, args[2], "length"); ant_value_t pos_val = js_get(js, args[2], "position"); if (vtype(off_val) == T_NUM) offset = (size_t)js_getnum(off_val); if (vtype(len_val) == T_NUM) length = (size_t)js_getnum(len_val); else length = buf_len - offset; if (vtype(pos_val) == T_NUM) position = (int64_t)js_getnum(pos_val); } else if (vtype(args[2]) == T_NUM) { offset = (size_t)js_getnum(args[2]); length = buf_len - offset; if (nargs >= 4 && vtype(args[3]) == T_NUM) length = (size_t)js_getnum(args[3]); if (nargs >= 5 && vtype(args[4]) == T_NUM) position = (int64_t)js_getnum(args[4]); }} if (offset > buf_len) return js_mkerr(js, "offset is out of bounds"); if (offset + length > buf_len) return js_mkerr(js, "length extends beyond buffer"); uv_fs_t req; uv_buf_t buf = uv_buf_init((char *)(buf_data + offset), (unsigned int)length); int result = uv_fs_read(uv_default_loop(), &req, fd, &buf, 1, position, NULL); uv_fs_req_cleanup(&req); if (result < 0) return js_mkerr(js, "readSync failed: %s", uv_strerror(result)); return js_mknum((double)result);}
static ant_value_t builtin_fs_writeSync(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 2) return js_mkerr(js, "writeSync() requires fd and data arguments"); if (vtype(args[0]) != T_NUM) return js_mkerr(js, "writeSync() fd must be a number"); int fd = (int)js_getnum(args[0]); if (vtype(args[1]) == T_STR) { size_t str_len; const char *str = js_getstr(js, args[1], &str_len); if (!str) return js_mkerr(js, "Failed to get string"); int64_t position = -1; if (nargs >= 3 && vtype(args[2]) == T_NUM) position = (int64_t)js_getnum(args[2]); uv_fs_t req; uv_buf_t buf = uv_buf_init((char *)str, (unsigned int)str_len); int result = uv_fs_write(uv_default_loop(), &req, fd, &buf, 1, position, NULL); uv_fs_req_cleanup(&req); if (result < 0) return js_mkerr(js, "writeSync failed: %s", uv_strerror(result)); return js_mknum((double)result); } TypedArrayData *ta_data = buffer_get_typedarray_data(args[1]); if (!ta_data || !ta_data->buffer || !ta_data->buffer->data) return js_mkerr(js, "writeSync() second argument must be a Buffer, TypedArray, DataView, or string"); uint8_t *buf_data = ta_data->buffer->data + ta_data->byte_offset; size_t buf_len = ta_data->byte_length; size_t offset = 0; size_t length = buf_len; int64_t position = -1; if (nargs >= 3) { if (vtype(args[2]) == T_OBJ) { ant_value_t off_val = js_get(js, args[2], "offset"); ant_value_t len_val = js_get(js, args[2], "length"); ant_value_t pos_val = js_get(js, args[2], "position"); if (vtype(off_val) == T_NUM) offset = (size_t)js_getnum(off_val); if (vtype(len_val) == T_NUM) length = (size_t)js_getnum(len_val); else length = buf_len - offset; if (vtype(pos_val) == T_NUM) position = (int64_t)js_getnum(pos_val); } else if (vtype(args[2]) == T_NUM) { offset = (size_t)js_getnum(args[2]); length = buf_len - offset; if (nargs >= 4 && vtype(args[3]) == T_NUM) length = (size_t)js_getnum(args[3]); if (nargs >= 5 && vtype(args[4]) == T_NUM) position = (int64_t)js_getnum(args[4]); } } if (offset > buf_len) return js_mkerr(js, "offset is out of bounds"); if (offset + length > buf_len) return js_mkerr(js, "length extends beyond buffer"); uv_fs_t req; uv_buf_t buf = uv_buf_init((char *)(buf_data + offset), (unsigned int)length); int result = uv_fs_write(uv_default_loop(), &req, fd, &buf, 1, position, NULL); uv_fs_req_cleanup(&req); if (result < 0) return js_mkerr(js, "writeSync failed: %s", uv_strerror(result)); return js_mknum((double)result);}
static ant_value_t builtin_fs_write_fd(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 2) return js_mkerr(js, "write() requires fd and data arguments"); if (vtype(args[0]) != T_NUM) return js_mkerr(js, "write() fd must be a number"); int fd = (int)js_getnum(args[0]); const char *write_data; size_t write_len; int64_t position = -1; if (vtype(args[1]) == T_STR) { size_t str_len; const char *str = js_getstr(js, args[1], &str_len); if (!str) return js_mkerr(js, "Failed to get string"); if (nargs >= 3 && vtype(args[2]) == T_NUM) position = (int64_t)js_getnum(args[2]); write_data = str; write_len = str_len; } else { TypedArrayData *ta_data = buffer_get_typedarray_data(args[1]); if (!ta_data || !ta_data->buffer || !ta_data->buffer->data) return js_mkerr(js, "write() second argument must be a Buffer, TypedArray, DataView, or string"); uint8_t *buf_data = ta_data->buffer->data + ta_data->byte_offset; size_t buf_len = ta_data->byte_length; size_t offset = 0; size_t length = buf_len; if (nargs >= 3) { if (vtype(args[2]) == T_OBJ) { ant_value_t off_val = js_get(js, args[2], "offset"); ant_value_t len_val = js_get(js, args[2], "length"); ant_value_t pos_val = js_get(js, args[2], "position"); if (vtype(off_val) == T_NUM) offset = (size_t)js_getnum(off_val); if (vtype(len_val) == T_NUM) length = (size_t)js_getnum(len_val); else length = buf_len - offset; if (vtype(pos_val) == T_NUM) position = (int64_t)js_getnum(pos_val); } else if (vtype(args[2]) == T_NUM) { offset = (size_t)js_getnum(args[2]); length = buf_len - offset; if (nargs >= 4 && vtype(args[3]) == T_NUM) length = (size_t)js_getnum(args[3]); if (nargs >= 5 && vtype(args[4]) == T_NUM) position = (int64_t)js_getnum(args[4]); }} if (offset > buf_len) return js_mkerr(js, "offset is out of bounds"); if (offset + length > buf_len) return js_mkerr(js, "length extends beyond buffer"); write_data = (const char *)(buf_data + offset); write_len = length; } fs_request_t *req = calloc(1, sizeof(fs_request_t)); if (!req) return js_mkerr(js, "Out of memory"); req->js = js; req->op_type = FS_OP_WRITE_FD; req->promise = js_mkpromise(js); req->fd = fd; req->data = malloc(write_len); if (!req->data) { free(req); return js_mkerr(js, "Out of memory"); } memcpy(req->data, write_data, write_len); req->data_len = write_len; req->uv_req.data = req; utarray_push_back(pending_requests, &req); uv_buf_t buf = uv_buf_init(req->data, (unsigned int)req->data_len); int result = uv_fs_write(uv_default_loop(), &req->uv_req, req->fd, &buf, 1, position, on_write_fd_complete); if (result < 0) { fs_request_fail(req, result); req->completed = 1; complete_request(req); } return req->promise;}
static ant_value_t builtin_fs_writevSync(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 2) return js_mkerr(js, "writevSync() requires fd and buffers arguments"); if (vtype(args[0]) != T_NUM) return js_mkerr(js, "writevSync() fd must be a number"); int fd = (int)js_getnum(args[0]); ant_offset_t arr_len = js_arr_len(js, args[1]); if (arr_len == 0) return js_mknum(0); int64_t position = -1; if (nargs >= 3 && vtype(args[2]) == T_NUM) position = (int64_t)js_getnum(args[2]); uv_buf_t *bufs = calloc((size_t)arr_len, sizeof(uv_buf_t)); if (!bufs) return js_mkerr(js, "Out of memory"); for (ant_offset_t i = 0; i < arr_len; i++) { ant_value_t item = js_arr_get(js, args[1], i); TypedArrayData *ta = buffer_get_typedarray_data(item); if (!ta || !ta->buffer || !ta->buffer->data) { free(bufs); return js_mkerr(js, "writevSync() buffers must contain ArrayBufferViews"); } bufs[i] = uv_buf_init((char *)(ta->buffer->data + ta->byte_offset), (unsigned int)ta->byte_length); } uv_fs_t req; int result = uv_fs_write(uv_default_loop(), &req, fd, bufs, (unsigned int)arr_len, position, NULL); uv_fs_req_cleanup(&req); free(bufs); if (result < 0) return js_mkerr(js, "writevSync failed: %s", uv_strerror(result)); return js_mknum((double)result);}
static ant_value_t builtin_fs_writev_fd(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 2) return js_mkerr(js, "writev() requires fd and buffers arguments"); if (vtype(args[0]) != T_NUM) return js_mkerr(js, "writev() fd must be a number"); int fd = (int)js_getnum(args[0]); ant_offset_t arr_len = js_arr_len(js, args[1]); if (arr_len == 0) { ant_value_t promise = js_mkpromise(js); js_resolve_promise(js, promise, js_mknum(0)); return promise; } int64_t position = -1; if (nargs >= 3 && vtype(args[2]) == T_NUM) position = (int64_t)js_getnum(args[2]); size_t total_len = 0; for (ant_offset_t i = 0; i < arr_len; i++) { ant_value_t item = js_arr_get(js, args[1], i); TypedArrayData *ta = buffer_get_typedarray_data(item); if (!ta || !ta->buffer || !ta->buffer->data) return js_mkerr(js, "writev() buffers must contain ArrayBufferViews"); total_len += ta->byte_length; } fs_request_t *req = calloc(1, sizeof(fs_request_t)); if (!req) return js_mkerr(js, "Out of memory"); req->data = malloc(total_len); if (!req->data) { free(req); return js_mkerr(js, "Out of memory"); } size_t off = 0; for (ant_offset_t i = 0; i < arr_len; i++) { ant_value_t item = js_arr_get(js, args[1], i); TypedArrayData *ta = buffer_get_typedarray_data(item); memcpy(req->data + off, ta->buffer->data + ta->byte_offset, ta->byte_length); off += ta->byte_length; } req->js = js; req->op_type = FS_OP_WRITE_FD; req->promise = js_mkpromise(js); req->fd = fd; req->data_len = total_len; req->uv_req.data = req; utarray_push_back(pending_requests, &req); uv_buf_t buf = uv_buf_init(req->data, (unsigned int)total_len); int result = uv_fs_write(uv_default_loop(), &req->uv_req, req->fd, &buf, 1, position, on_write_fd_complete); if (result < 0) { fs_request_fail(req, result); req->completed = 1; complete_request(req); } return req->promise;}
static int parse_open_flags(ant_t *js, ant_value_t arg) { if (vtype(arg) == T_NUM) return (int)js_getnum(arg); if (vtype(arg) != T_STR) return O_RDONLY; size_t len; const char *str = js_getstr(js, arg, &len); if (!str) return O_RDONLY; if (len == 1 && str[0] == 'r') return O_RDONLY; if (len == 2 && memcmp(str, "r+", 2) == 0) return O_RDWR; if (len == 1 && str[0] == 'w') return O_WRONLY | O_CREAT | O_TRUNC; if (len == 2 && memcmp(str, "wx", 2) == 0) return O_WRONLY | O_CREAT | O_TRUNC | O_EXCL; if (len == 2 && memcmp(str, "w+", 2) == 0) return O_RDWR | O_CREAT | O_TRUNC; if (len == 3 && memcmp(str, "wx+", 3) == 0) return O_RDWR | O_CREAT | O_TRUNC | O_EXCL; if (len == 1 && str[0] == 'a') return O_WRONLY | O_CREAT | O_APPEND; if (len == 2 && memcmp(str, "ax", 2) == 0) return O_WRONLY | O_CREAT | O_APPEND | O_EXCL; if (len == 2 && memcmp(str, "a+", 2) == 0) return O_RDWR | O_CREAT | O_APPEND; if (len == 3 && memcmp(str, "ax+", 3) == 0) return O_RDWR | O_CREAT | O_APPEND | O_EXCL; return O_RDONLY;}
static ant_value_t builtin_fs_openSync(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "openSync() requires a path argument"); if (vtype(args[0]) != T_STR) return js_mkerr(js, "openSync() 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"); int flags = (nargs >= 2) ? parse_open_flags(js, args[1]) : O_RDONLY; int mode = (nargs >= 3 && vtype(args[2]) == T_NUM) ? (int)js_getnum(args[2]) : 0666; char *path_cstr = strndup(path, path_len); if (!path_cstr) return js_mkerr(js, "Out of memory"); uv_fs_t req; int result = uv_fs_open(uv_default_loop(), &req, path_cstr, flags, mode, NULL); if (result < 0) { ant_value_t err = fs_mk_uv_error(js, result, "open", path_cstr, NULL); uv_fs_req_cleanup(&req); free(path_cstr); return err; } uv_fs_req_cleanup(&req); free(path_cstr); return js_mknum((double)result);}
static ant_value_t builtin_fs_closeSync(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "closeSync() requires a fd argument"); if (vtype(args[0]) != T_NUM) return js_mkerr(js, "closeSync() fd must be a number"); int fd = (int)js_getnum(args[0]); uv_fs_t req; int result = uv_fs_close(uv_default_loop(), &req, fd, NULL); uv_fs_req_cleanup(&req); if (result < 0) return js_mkerr(js, "closeSync failed: %s", uv_strerror(result)); return js_mkundef();}
static void on_open_fd_complete(uv_fs_t *uv_req) { fs_request_t *req = (fs_request_t *)uv_req->data; if (uv_req->result < 0) { fs_request_fail(req, (int)uv_req->result); req->completed = 1; complete_request(req); return; } req->completed = 1; js_resolve_promise(req->js, req->promise, js_mknum((double)uv_req->result)); remove_pending_request(req); free_fs_request(req);}
static void on_open_filehandle_complete(uv_fs_t *uv_req) { fs_request_t *req = (fs_request_t *)uv_req->data;
if (uv_req->result < 0) { fs_request_fail(req, (int)uv_req->result); req->completed = 1; complete_request(req); return; }
req->completed = 1; js_resolve_promise( req->js, req->promise, fs_make_filehandle(req->js, (int)uv_req->result) ); remove_pending_request(req); free_fs_request(req);}
static ant_value_t builtin_fs_open_fd(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "open() requires a path argument"); if (vtype(args[0]) != T_STR) return js_mkerr(js, "open() 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"); int flags = (nargs >= 2) ? parse_open_flags(js, args[1]) : O_RDONLY; int mode = (nargs >= 3 && vtype(args[2]) == T_NUM) ? (int)js_getnum(args[2]) : 0666; fs_request_t *req = calloc(1, sizeof(fs_request_t)); if (!req) return js_mkerr(js, "Out of memory"); req->js = js; req->op_type = FS_OP_OPEN; req->promise = js_mkpromise(js); req->path = strndup(path, path_len); req->uv_req.data = req; utarray_push_back(pending_requests, &req); int result = uv_fs_open(uv_default_loop(), &req->uv_req, req->path, flags, mode, on_open_fd_complete); if (result < 0) { fs_request_fail(req, result); req->completed = 1; complete_request(req); } return req->promise;}
static ant_value_t builtin_fs_open_filehandle(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "open() requires a path argument"); if (vtype(args[0]) != T_STR) return js_mkerr(js, "open() 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");
int flags = (nargs >= 2) ? parse_open_flags(js, args[1]) : O_RDONLY; int mode = (nargs >= 3 && vtype(args[2]) == T_NUM) ? (int)js_getnum(args[2]) : 0666;
fs_request_t *req = calloc(1, sizeof(fs_request_t)); if (!req) return js_mkerr(js, "Out of memory");
req->js = js; req->op_type = FS_OP_OPEN; req->promise = js_mkpromise(js); req->path = strndup(path, path_len); req->uv_req.data = req;
utarray_push_back(pending_requests, &req); int result = uv_fs_open( uv_default_loop(), &req->uv_req, req->path, flags, mode, on_open_filehandle_complete );
if (result < 0) { fs_request_fail(req, result); req->completed = 1; complete_request(req); }
return req->promise;}
static void on_close_fd_complete(uv_fs_t *uv_req) { fs_request_t *req = (fs_request_t *)uv_req->data; if (uv_req->result < 0) { fs_request_fail(req, (int)uv_req->result); req->completed = 1; complete_request(req); return; } req->completed = 1; js_resolve_promise(req->js, req->promise, js_mkundef()); remove_pending_request(req); free_fs_request(req);}
static ant_value_t builtin_fs_close_fd(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "close() requires a fd argument"); if (vtype(args[0]) != T_NUM) return js_mkerr(js, "close() fd must be a number"); int fd = (int)js_getnum(args[0]); fs_request_t *req = calloc(1, sizeof(fs_request_t)); if (!req) return js_mkerr(js, "Out of memory"); req->js = js; req->op_type = FS_OP_CLOSE; req->promise = js_mkpromise(js); req->fd = fd; req->uv_req.data = req; utarray_push_back(pending_requests, &req); int result = uv_fs_close(uv_default_loop(), &req->uv_req, req->fd, on_close_fd_complete); if (result < 0) { fs_request_fail(req, result); req->completed = 1; complete_request(req); } return req->promise;}
static ant_value_t builtin_fs_watch(ant_t *js, ant_value_t *args, int nargs) { ant_value_t path_val = 0; ant_value_t watcher_obj = 0; fs_watch_options_t opts; fs_watcher_t *watcher = NULL; uv_handle_t *handle = NULL; const char *path = NULL; size_t path_len = 0; int rc = 0;
if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "watch() requires a path argument"); if (!fs_parse_watch_options(js, args, nargs, &opts)) return js_mkerr_typed(js, JS_ERR_TYPE, "watch() options must be a string, object, or callback");
path_val = fs_coerce_path(js, args[0]); if (vtype(path_val) != T_STR) return js_mkerr_typed(js, JS_ERR_TYPE, "watch() path must be a string or URL");
path = js_getstr(js, path_val, &path_len); if (!path || path_len == 0) return js_mkerr(js, "watch() path must not be empty");
watcher = fs_watcher_new(js, FS_WATCH_MODE_EVENT); if (!watcher) return js_mkerr(js, "Out of memory");
watcher_obj = fs_watcher_make_object(js, watcher); if (is_err(watcher_obj)) { fs_watcher_free(watcher); return watcher_obj; }
rc = fs_watcher_start_event(watcher, path, opts.persistent, opts.recursive); if (rc != 0) { js_set_native_ptr(watcher_obj, NULL); fs_watcher_free(watcher); return fs_watch_error(js, rc, path); }
fs_add_active_watcher(watcher); if (!opts.persistent) { handle = fs_watcher_uv_handle(watcher); if (handle) uv_unref(handle); } if (is_callable(opts.listener)) eventemitter_add_listener(js, watcher_obj, "change", opts.listener, false); return watcher_obj;}
static ant_value_t builtin_fs_watchFile(ant_t *js, ant_value_t *args, int nargs) { ant_value_t path_val = 0; fs_watchfile_options_t opts; fs_watcher_t *watcher = NULL; uv_handle_t *handle = NULL; const char *path = NULL; size_t path_len = 0; int rc = 0;
if (nargs < 2) return js_mkerr_typed(js, JS_ERR_TYPE, "watchFile() requires a path and listener"); if (!fs_parse_watchfile_options(js, args, nargs, &opts)) return js_mkerr_typed(js, JS_ERR_TYPE, "watchFile() requires a listener callback");
path_val = fs_coerce_path(js, args[0]); if (vtype(path_val) != T_STR) return js_mkerr_typed(js, JS_ERR_TYPE, "watchFile() path must be a string or URL");
path = js_getstr(js, path_val, &path_len); if (!path || path_len == 0) return js_mkerr(js, "watchFile() path must not be empty");
watcher = fs_watcher_new(js, FS_WATCH_MODE_STAT); if (!watcher) return js_mkerr(js, "Out of memory");
watcher->callback = opts.listener; watcher->last_stat_valid = fs_stat_path_sync(path, &watcher->last_stat);
rc = fs_watcher_start_poll(watcher, path, opts.persistent, opts.interval_ms); if (rc != 0) { fs_watcher_free(watcher); return fs_watch_error(js, rc, path); }
fs_add_active_watcher(watcher); if (!opts.persistent) { handle = fs_watcher_uv_handle(watcher); if (handle) uv_unref(handle); } return js_mkundef();}
static ant_value_t builtin_fs_unwatchFile(ant_t *js, ant_value_t *args, int nargs) { ant_value_t path_val = 0; ant_value_t listener = js_mkundef(); char *resolved = NULL; fs_watcher_t *watcher = NULL; fs_watcher_t *next = NULL; const char *path = NULL; size_t path_len = 0;
if (nargs < 1) return js_mkundef();
path_val = fs_coerce_path(js, args[0]); if (vtype(path_val) != T_STR) return js_mkundef();
path = js_getstr(js, path_val, &path_len); if (!path || path_len == 0) return js_mkundef(); if (nargs > 1 && is_callable(args[1])) listener = args[1];
resolved = ant_watch_resolve_path(path); if (!resolved) return js_mkundef();
for (watcher = active_watchers; watcher; watcher = next) { next = watcher->next_active; if (watcher->mode != FS_WATCH_MODE_STAT) continue; if (!watcher->path || strcmp(watcher->path, resolved) != 0) continue; if (is_callable(listener) && watcher->callback != listener) continue; fs_watcher_close_native(watcher); }
free(resolved); return js_mkundef();}
void init_fs_module(void) { utarray_new(pending_requests, &ut_ptr_icd); ant_t *js = rt->js; ant_value_t glob = js->global; ant_value_t stats_ctor = js_mkobj(js); ant_value_t stats_proto = js_mkobj(js); js_set(js, stats_proto, "isFile", js_mkfun(stat_isFile)); js_set(js, stats_proto, "isDirectory", js_mkfun(stat_isDirectory)); js_set(js, stats_proto, "isSymbolicLink", js_mkfun(stat_isSymbolicLink)); js_set_sym(js, stats_proto, get_toStringTag_sym(), js_mkstr(js, "Stats", 5)); js_mkprop_fast(js, stats_ctor, "prototype", 9, stats_proto); js_mkprop_fast(js, stats_ctor, "name", 4, js_mkstr(js, "Stats", 5)); js_set_descriptor(js, stats_ctor, "name", 4, 0); js_set(js, glob, "Stats", js_obj_to_func(stats_ctor));
g_dirent_proto = js_mkobj(js); js_set(js, g_dirent_proto, "isFile", js_mkfun(dirent_isFile)); js_set(js, g_dirent_proto, "isDirectory", js_mkfun(dirent_isDirectory)); js_set(js, g_dirent_proto, "isSymbolicLink", js_mkfun(dirent_isSymbolicLink)); js_set(js, g_dirent_proto, "isBlockDevice", js_mkfun(dirent_isBlockDevice)); js_set(js, g_dirent_proto, "isCharacterDevice", js_mkfun(dirent_isCharacterDevice)); js_set(js, g_dirent_proto, "isFIFO", js_mkfun(dirent_isFIFO)); js_set(js, g_dirent_proto, "isSocket", js_mkfun(dirent_isSocket)); js_set_sym(js, g_dirent_proto, get_toStringTag_sym(), js_mkstr(js, "Dirent", 6)); gc_register_root(&g_dirent_proto);}
static ant_value_t fs_callback_success_handler(ant_t *js, ant_value_t *args, int nargs) { ant_value_t ctx = js_get_slot(js_getcurrentfunc(js), SLOT_DATA); ant_value_t callback = js_get_slot(ctx, SLOT_DATA);
if (!is_callable(callback)) return js_mkundef();
if (js_truthy(js, js_get(js, ctx, "existsStyle"))) { ant_value_t cb_args[1] = { nargs > 0 ? args[0] : js_false }; fs_call_value(js, callback, js_mkundef(), cb_args, 1); return js_mkundef(); }
ant_value_t cb_args[2] = { js_mknull(), nargs > 0 ? args[0] : js_mkundef() }; fs_call_value(js, callback, js_mkundef(), cb_args, 2); return js_mkundef();}
static ant_value_t fs_callback_error_handler(ant_t *js, ant_value_t *args, int nargs) { ant_value_t ctx = js_get_slot(js_getcurrentfunc(js), SLOT_DATA); ant_value_t callback = js_get_slot(ctx, SLOT_DATA); ant_value_t cb_args[1];
if (!is_callable(callback)) return js_mkundef();
if (js_truthy(js, js_get(js, ctx, "existsStyle"))) { cb_args[0] = js_false; fs_call_value(js, callback, js_mkundef(), cb_args, 1); return js_mkundef(); }
cb_args[0] = nargs > 0 ? args[0] : js_mkundef(); fs_call_value(js, callback, js_mkundef(), cb_args, 1); return js_mkundef();}
static void fs_callback_emit_success( ant_t *js, ant_value_t callback, bool exists_style, ant_value_t value) { if (exists_style) { ant_value_t cb_args[1] = { value }; fs_call_value(js, callback, js_mkundef(), cb_args, 1); return; }
ant_value_t cb_args[2] = { js_mknull(), value }; fs_call_value(js, callback, js_mkundef(), cb_args, 2);}
static void fs_callback_emit_error( ant_t *js, ant_value_t callback, bool exists_style, ant_value_t error) { ant_value_t cb_args[1];
cb_args[0] = exists_style ? js_false : error; fs_call_value(js, callback, js_mkundef(), cb_args, 1);}
static ant_value_t fs_callback_attach_promise( ant_t *js, ant_value_t promise, ant_value_t callback, bool exists_style) { GC_ROOT_SAVE(root_mark, js); ant_value_t success_fn = js_mkundef(); ant_value_t error_fn = js_mkundef();
GC_ROOT_PIN(js, promise); GC_ROOT_PIN(js, callback);
ant_value_t success_ctx = js_mkobj(js); GC_ROOT_PIN(js, success_ctx); ant_value_t error_ctx = js_mkobj(js); GC_ROOT_PIN(js, error_ctx);
js_set_slot(success_ctx, SLOT_DATA, callback); js_set_slot(error_ctx, SLOT_DATA, callback); if (exists_style) { js_set(js, success_ctx, "existsStyle", js_true); js_set(js, error_ctx, "existsStyle", js_true); }
success_fn = js_heavy_mkfun(js, fs_callback_success_handler, success_ctx); GC_ROOT_PIN(js, success_fn); error_fn = js_heavy_mkfun(js, fs_callback_error_handler, error_ctx); GC_ROOT_PIN(js, error_fn);
js_promise_then(js, promise, success_fn, error_fn); GC_ROOT_RESTORE(js, root_mark); return js_mkundef();}
static ant_value_t fs_callback_wrapper_call(ant_t *js, ant_value_t *args, int nargs) { GC_ROOT_SAVE(root_mark, js); ant_value_t wrapper = js_getcurrentfunc(js); ant_value_t config = js_get_slot(wrapper, SLOT_DATA); ant_value_t original = js_get_slot(config, SLOT_DATA); ant_value_t callback = js_mkundef(); ant_value_t result = js_mkundef(); ant_value_t this_arg = js_getthis(js); ant_value_t ex = js_mkundef(); bool exists_style = false; int call_nargs = nargs;
GC_ROOT_PIN(js, original); GC_ROOT_PIN(js, this_arg);
exists_style = js_truthy(js, js_get(js, config, "existsStyle")); if (nargs > 0 && is_callable(args[nargs - 1])) { callback = args[nargs - 1]; GC_ROOT_PIN(js, callback); call_nargs--; }
result = fs_call_value(js, original, this_arg, args, call_nargs); GC_ROOT_PIN(js, result);
if (!is_callable(callback)) { GC_ROOT_RESTORE(js, root_mark); return result; }
if (is_err(result) || js->thrown_exists) { ex = js->thrown_exists ? js->thrown_value : result; GC_ROOT_PIN(js, ex); js->thrown_exists = false; js->thrown_value = js_mkundef(); js->thrown_stack = js_mkundef(); fs_callback_emit_error(js, callback, exists_style, ex); GC_ROOT_RESTORE(js, root_mark); return js_mkundef(); }
if (vtype(result) != T_PROMISE) { fs_callback_emit_success(js, callback, exists_style, result); GC_ROOT_RESTORE(js, root_mark); return js_mkundef(); }
ant_value_t attach_result = fs_callback_attach_promise(js, result, callback, exists_style); GC_ROOT_RESTORE(js, root_mark); return attach_result;}
static ant_value_t fs_make_callback_wrapper(ant_t *js, ant_value_t original, bool exists_style) { ant_value_t config = js_mkobj(js);
js_set_slot(config, SLOT_DATA, original); if (exists_style) js_set(js, config, "existsStyle", js_true); return js_heavy_mkfun(js, fs_callback_wrapper_call, config);}
static void fs_set_promise_methods(ant_t *js, ant_value_t lib) { js_set(js, lib, "appendFile", js_mkfun(builtin_fs_appendFile)); js_set(js, lib, "cp", js_mkfun(builtin_fs_cp)); js_set(js, lib, "copyFile", js_mkfun(builtin_fs_copyFile)); js_set(js, lib, "readFile", js_mkfun(builtin_fs_readFile)); js_set(js, lib, "open", js_mkfun(builtin_fs_open_filehandle)); js_set(js, lib, "close", js_mkfun(builtin_fs_close_fd)); js_set(js, lib, "writeFile", js_mkfun(builtin_fs_writeFile)); js_set(js, lib, "write", js_mkfun(builtin_fs_write_fd)); js_set(js, lib, "writev", js_mkfun(builtin_fs_writev_fd)); js_set(js, lib, "rename", js_mkfun(builtin_fs_rename)); js_set(js, lib, "rm", js_mkfun(builtin_fs_rm)); js_set(js, lib, "unlink", js_mkfun(builtin_fs_unlink)); js_set(js, lib, "mkdir", js_mkfun(builtin_fs_mkdir)); js_set(js, lib, "mkdtemp", js_mkfun(builtin_fs_mkdtemp)); js_set(js, lib, "rmdir", js_mkfun(builtin_fs_rmdir)); js_set(js, lib, "stat", js_mkfun(builtin_fs_stat)); js_set(js, lib, "lstat", js_mkfun(builtin_fs_lstat)); js_set(js, lib, "fstat", js_mkfun(builtin_fs_fstat)); js_set(js, lib, "utimes", js_mkfun(builtin_fs_utimes)); js_set(js, lib, "futimes", js_mkfun(builtin_fs_futimes)); js_set(js, lib, "exists", js_mkfun(builtin_fs_exists)); js_set(js, lib, "access", js_mkfun(builtin_fs_access)); js_set(js, lib, "chmod", js_mkfun(builtin_fs_chmod)); js_set(js, lib, "readdir", js_mkfun(builtin_fs_readdir)); js_set(js, lib, "realpath", js_mkfun(builtin_fs_realpath)); js_set(js, lib, "readlink", js_mkfun(builtin_fs_readlink));}
static void fs_set_callback_compatible_methods(ant_t *js, ant_value_t lib) { ant_value_t realpath = fs_make_callback_wrapper(js, js_mkfun(builtin_fs_realpath), false);
js_set(js, lib, "appendFile", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_appendFile), false)); js_set(js, lib, "cp", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_cp), false)); js_set(js, lib, "copyFile", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_copyFile), false)); js_set(js, lib, "readFile", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_readFile), false)); js_set(js, lib, "open", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_open_fd), false)); js_set(js, lib, "close", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_close_fd), false)); js_set(js, lib, "writeFile", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_writeFile), false)); js_set(js, lib, "write", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_write_fd), false)); js_set(js, lib, "writev", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_writev_fd), false)); js_set(js, lib, "rename", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_rename), false)); js_set(js, lib, "rm", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_rm), false)); js_set(js, lib, "unlink", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_unlink), false)); js_set(js, lib, "mkdir", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_mkdir), false)); js_set(js, lib, "mkdtemp", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_mkdtemp), false)); js_set(js, lib, "rmdir", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_rmdir), false)); js_set(js, lib, "stat", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_stat), false)); js_set(js, lib, "lstat", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_lstat), false)); js_set(js, lib, "fstat", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_fstat), false)); js_set(js, lib, "utimes", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_utimes), false)); js_set(js, lib, "futimes", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_futimes), false)); js_set(js, lib, "exists", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_exists), true)); js_set(js, lib, "access", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_access), false)); js_set(js, lib, "chmod", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_chmod), false)); js_set(js, lib, "readdir", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_readdir), false)); js_set(js, lib, "readlink", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_readlink), false));
js_set(js, realpath, "native", realpath); js_set(js, lib, "realpath", realpath);}
static ant_value_t fs_make_constants(ant_t *js) { ant_value_t constants = js_newobj(js); js_set(js, constants, "F_OK", js_mknum(F_OK)); js_set(js, constants, "R_OK", js_mknum(R_OK)); js_set(js, constants, "W_OK", js_mknum(W_OK)); js_set(js, constants, "X_OK", js_mknum(X_OK)); js_set(js, constants, "O_RDONLY", js_mknum(O_RDONLY)); js_set(js, constants, "O_WRONLY", js_mknum(O_WRONLY)); js_set(js, constants, "O_RDWR", js_mknum(O_RDWR)); js_set(js, constants, "O_CREAT", js_mknum(O_CREAT)); js_set(js, constants, "O_EXCL", js_mknum(O_EXCL)); js_set(js, constants, "O_TRUNC", js_mknum(O_TRUNC)); js_set(js, constants, "O_APPEND", js_mknum(O_APPEND));#ifdef O_SYMLINK js_set(js, constants, "O_SYMLINK", js_mknum(O_SYMLINK));#endif#ifdef O_NOFOLLOW js_set(js, constants, "O_NOFOLLOW", js_mknum(O_NOFOLLOW));#endif#ifdef S_IFMT js_set(js, constants, "S_IFMT", js_mknum(S_IFMT));#endif#ifdef S_IFREG js_set(js, constants, "S_IFREG", js_mknum(S_IFREG));#endif#ifdef S_IFDIR js_set(js, constants, "S_IFDIR", js_mknum(S_IFDIR));#endif#ifdef S_IFLNK js_set(js, constants, "S_IFLNK", js_mknum(S_IFLNK));#endif return constants;}
static ant_value_t builtin_fs_promises_getter(ant_t *js, ant_value_t *args, int nargs) { ant_value_t getter_fn = js_getcurrentfunc(js); ant_value_t cached = js_get_slot(getter_fn, SLOT_DATA); if (is_object_type(cached)) return cached;
ant_value_t promises = fs_promises_library(js); js_set_slot(getter_fn, SLOT_DATA, promises); return promises;}
ant_value_t fs_library(ant_t *js) { ant_value_t lib = js_mkobj(js); ant_value_t realpath_sync = js_heavy_mkfun(js, builtin_fs_realpathSync, js_mkundef()); fs_set_callback_compatible_methods(js, lib); fs_init_watch_constructors(js); fs_init_stream_constructors(js);
js_set(js, lib, "read", js_mkfun(builtin_fs_read_fd)); js_set(js, lib, "readFileSync", js_mkfun(builtin_fs_readFileSync)); js_set(js, lib, "readSync", js_mkfun(builtin_fs_readSync)); js_set(js, lib, "stream", js_mkfun(builtin_fs_readBytes)); js_set(js, lib, "createReadStream", js_mkfun(builtin_fs_createReadStream)); js_set(js, lib, "createWriteStream", js_mkfun(builtin_fs_createWriteStream)); js_set(js, lib, "openSync", js_mkfun(builtin_fs_openSync)); js_set(js, lib, "closeSync", js_mkfun(builtin_fs_closeSync)); js_set(js, lib, "writeFileSync", js_mkfun(builtin_fs_writeFileSync)); js_set(js, lib, "writeSync", js_mkfun(builtin_fs_writeSync)); js_set(js, lib, "writevSync", js_mkfun(builtin_fs_writevSync)); js_set(js, lib, "appendFileSync", js_mkfun(builtin_fs_appendFileSync)); js_set(js, lib, "cpSync", js_mkfun(builtin_fs_cpSync)); js_set(js, lib, "copyFileSync", js_mkfun(builtin_fs_copyFileSync)); js_set(js, lib, "renameSync", js_mkfun(builtin_fs_renameSync)); js_set(js, lib, "rmSync", js_mkfun(builtin_fs_rmSync)); js_set(js, lib, "unlinkSync", js_mkfun(builtin_fs_unlinkSync)); js_set(js, lib, "mkdirSync", js_mkfun(builtin_fs_mkdirSync)); js_set(js, lib, "mkdtempSync", js_mkfun(builtin_fs_mkdtempSync)); js_set(js, lib, "rmdirSync", js_mkfun(builtin_fs_rmdirSync)); js_set(js, lib, "statSync", js_mkfun(builtin_fs_statSync)); js_set(js, lib, "lstatSync", js_mkfun(builtin_fs_lstatSync)); js_set(js, lib, "fstatSync", js_mkfun(builtin_fs_fstatSync)); js_set(js, lib, "utimesSync", js_mkfun(builtin_fs_utimesSync)); js_set(js, lib, "futimesSync", js_mkfun(builtin_fs_futimesSync)); js_set(js, lib, "existsSync", js_mkfun(builtin_fs_existsSync)); js_set(js, lib, "accessSync", js_mkfun(builtin_fs_accessSync)); js_set(js, lib, "chmodSync", js_mkfun(builtin_fs_chmodSync)); js_set(js, lib, "readdirSync", js_mkfun(builtin_fs_readdirSync)); js_set(js, lib, "realpathSync", realpath_sync); js_set(js, lib, "readlinkSync", js_mkfun(builtin_fs_readlinkSync)); js_set(js, lib, "watch", js_mkfun(builtin_fs_watch)); js_set(js, lib, "watchFile", js_mkfun(builtin_fs_watchFile)); js_set(js, lib, "unwatchFile", js_mkfun(builtin_fs_unwatchFile)); js_set(js, lib, "FSWatcher", g_fswatcher_ctor); js_set(js, lib, "ReadStream", g_readstream_ctor); js_set(js, lib, "WriteStream", g_writestream_ctor); js_set(js, realpath_sync, "native", realpath_sync); js_set_getter_desc( js, lib, "promises", 8, js_heavy_mkfun(js, builtin_fs_promises_getter, js_mkundef()), JS_DESC_E | JS_DESC_C ); js_set(js, lib, "constants", fs_make_constants(js)); js_set_sym(js, lib, get_toStringTag_sym(), js_mkstr(js, "fs", 2)); return lib;}
ant_value_t fs_promises_library(ant_t *js) { ant_value_t lib = js_mkobj(js);
fs_set_promise_methods(js, lib); js_set(js, lib, "constants", fs_make_constants(js));
js_set_sym(js, lib, get_toStringTag_sym(), js_mkstr(js, "fs/promises", 11)); return lib;}
ant_value_t fs_constants_library(ant_t *js) { ant_value_t constants = fs_make_constants(js); js_set(js, constants, "default", constants); js_set_slot_wb(js, constants, SLOT_DEFAULT, constants); js_set_sym(js, constants, get_toStringTag_sym(), js_mkstr(js, "constants", 9)); return constants;}
int has_pending_fs_ops(void) { return pending_requests && utarray_len(pending_requests) > 0;}
void gc_mark_fs(ant_t *js, gc_mark_fn mark) { fs_watcher_t *watcher = NULL;
if (g_fswatcher_proto) mark(js, g_fswatcher_proto); if (g_fswatcher_ctor) mark(js, g_fswatcher_ctor); if (g_filehandle_proto) mark(js, g_filehandle_proto); if (g_readstream_proto) mark(js, g_readstream_proto); if (g_readstream_ctor) mark(js, g_readstream_ctor); if (g_writestream_proto) mark(js, g_writestream_proto); if (g_writestream_ctor) mark(js, g_writestream_ctor); if (!pending_requests) return; unsigned int len = utarray_len(pending_requests); for (unsigned int i = 0; i < len; i++) { fs_request_t **reqp = (fs_request_t **)utarray_eltptr(pending_requests, i); if (reqp && *reqp) { mark(js, (*reqp)->promise); if (is_object_type((*reqp)->target_buffer)) mark(js, (*reqp)->target_buffer); if (is_callable((*reqp)->callback_fn)) mark(js, (*reqp)->callback_fn); }}
for (watcher = active_watchers; watcher; watcher = watcher->next_active) { if (vtype(watcher->obj) == T_OBJ) mark(js, watcher->obj); if (vtype(watcher->callback) != T_UNDEF) mark(js, watcher->callback); }}