MIRROR: javascript for 🐜's, a tiny runtime with big ambitions
1#include <compat.h> // IWYU pragma: keep
2
3#include <pkg.h>
4#include <stdio.h>
5#include <stdlib.h>
6#include <string.h>
7#include <unistd.h>
8#include <sys/stat.h>
9#include <dirent.h>
10#include <time.h>
11#include <argtable3.h>
12#include <yyjson.h>
13
14#include "cli/pkg.h"
15#include "cli/version.h"
16
17#include "utils.h"
18#include "progress.h"
19#include "modules/io.h"
20
21// migrate this file to crprintf for colors
22
23bool pkg_verbose = false;
24
25static void print_bin_callback(const char *name, void *user_data);
26
27static void progress_callback(void *user_data, pkg_phase_t phase, uint32_t current, uint32_t total, const char *message) {
28 progress_t *progress = (progress_t *)user_data;
29 if (!progress || !message || !message[0]) return;
30
31 const char *icon;
32 switch (phase) {
33 case PKG_PHASE_RESOLVING: icon = "🔍"; break;
34 case PKG_PHASE_FETCHING: icon = "🚚"; break;
35 case PKG_PHASE_EXTRACTING: icon = "📦"; break;
36 case PKG_PHASE_LINKING: icon = "🔗"; break;
37 case PKG_PHASE_CACHING: icon = "💾"; break;
38 case PKG_PHASE_POSTINSTALL: icon = "⚙️ "; break;
39 default: icon = "📦"; break;
40 }
41
42 char msg[PROGRESS_MSG_SIZE];
43 if (total > 0) snprintf(msg, sizeof(msg), "%s %s [%u/%u]", icon, message, current, total);
44 else if (current > 0) snprintf(msg, sizeof(msg), "%s %s [%u]", icon, message, current);
45 else snprintf(msg, sizeof(msg), "%s %s", icon, message);
46
47 progress_update(progress, msg);
48}
49
50static void print_latest_available_hint(pkg_context_t *ctx, const char *pkg_name, const char *installed_version) {
51 if (!ctx || !pkg_name || !installed_version || !pkg_name[0] || !installed_version[0]) return;
52
53 char latest[64];
54 if (pkg_get_latest_available_version(ctx, pkg_name, installed_version, latest, sizeof(latest)) <= 0) return;
55 printf(" %s(v%s available)%s", C_BLUE, latest, C_RESET);
56}
57
58static void print_added_packages(pkg_context_t *ctx) {
59 uint32_t count = pkg_get_added_count(ctx);
60 uint32_t printed = 0;
61 if (count > 0) fputc('\n', stdout);
62
63 for (uint32_t i = 0; i < count; i++) {
64 pkg_added_package_t pkg;
65 if (pkg_get_added_package(ctx, i, &pkg) == PKG_OK && pkg.direct) {
66 printf("%s+%s %s%s%s@%s%s%s",
67 C_GREEN, C_RESET,
68 C_BOLD, pkg.name, C_RESET,
69 C_DIM, pkg.version, C_RESET
70 );
71 print_latest_available_hint(ctx, pkg.name, pkg.version);
72 fputc('\n', stdout); printed++;
73 }
74 }
75
76 if (printed > 0) fputc('\n', stdout);
77}
78
79static uint64_t timespec_diff_ms(struct timespec *start, struct timespec *end) {
80 int64_t sec = end->tv_sec - start->tv_sec;
81 int64_t nsec = end->tv_nsec - start->tv_nsec;
82 if (nsec < 0) { sec--; nsec += 1000000000; }
83 return (uint64_t)sec * 1000 + (uint64_t)nsec / 1000000;
84}
85
86static void print_elapsed(uint64_t elapsed_ms) {
87 fputs(C_BOLD, stdout);
88 if (elapsed_ms < 1000) {
89 printf("%llums", (unsigned long long)elapsed_ms);
90 } else printf("%.2fs", (double)elapsed_ms / 1000.0);
91 fputs(C_RESET, stdout);
92}
93
94static void print_install_header(const char *cmd) {
95 const char *version = ant_semver();
96
97 printf("%sant %s%s v%s %s(%s)%s\n",
98 C_BOLD, cmd, C_RESET, version,
99 C_DIM, ANT_GIT_HASH, C_RESET
100 );
101}
102
103static void print_bin_callback(const char *name, void *user_data) {
104 (void)user_data;
105 printf(" %s-%s %s\n", C_DIM, C_RESET, name);
106}
107
108static void prompt_with_default(const char *prompt, const char *def, char *buf, size_t buf_size) {
109 if (def && def[0]) {
110 printf("%s%s%s %s(%s)%s: ", C_CYAN, prompt, C_RESET, C_DIM, def, C_RESET);
111 } else printf("%s%s%s: ", C_CYAN, prompt, C_RESET);
112 fflush(stdout);
113
114 if (fgets(buf, (int)buf_size, stdin)) {
115 size_t len = strlen(buf);
116 if (len > 0 && buf[len - 1] == '\n') buf[len - 1] = '\0';
117 }
118
119 if (buf[0] == '\0' && def) {
120 strncpy(buf, def, buf_size - 1);
121 buf[buf_size - 1] = '\0';
122 }
123}
124
125static void print_direct_installed_packages(pkg_context_t *ctx) {
126 if (!ctx) return;
127
128 fputc('\n', stdout);
129
130 uint32_t added_count = pkg_get_added_count(ctx);
131 for (uint32_t i = 0; i < added_count; i++) {
132 pkg_added_package_t pkg;
133 if (pkg_get_added_package(ctx, i, &pkg) != PKG_OK || !pkg.direct) continue;
134
135 int bin_count = pkg_list_package_bins("node_modules", pkg.name, NULL, NULL);
136 printf("%sinstalled%s %s%s@%s%s",
137 C_GREEN, C_RESET,
138 C_BOLD, pkg.name, pkg.version, C_RESET);
139 print_latest_available_hint(ctx, pkg.name, pkg.version);
140 if (bin_count > 0) {
141 printf(" with binaries:\n");
142 pkg_list_package_bins("node_modules", pkg.name, print_bin_callback, NULL);
143 } else fputc('\n', stdout);
144 }
145}
146
147static void print_add_summary(pkg_context_t *ctx, const pkg_install_result_t *result, bool include_done_suffix) {
148 if (!ctx || !result) return;
149
150 if (result->packages_installed > 0) {
151 print_direct_installed_packages(ctx);
152
153 printf("\n%s%u%s package%s installed %s[%s",
154 C_GREEN, result->packages_installed, C_RESET,
155 result->packages_installed == 1 ? "" : "s",
156 C_DIM, C_RESET);
157 print_elapsed(result->elapsed_ms);
158 printf("%s]%s", C_DIM, C_RESET);
159 if (include_done_suffix) printf(" done");
160
161 fputc('\n', stdout);
162 return;
163 }
164
165 printf("\n%sChecked%s %s%u%s installs across %s%u%s packages %s(no changes)%s %s[%s",
166 C_DIM, C_RESET,
167 C_GREEN, result->packages_installed + result->packages_skipped, C_RESET,
168 C_GREEN, result->package_count, C_RESET,
169 C_DIM, C_RESET,
170 C_DIM, C_RESET);
171 print_elapsed(result->elapsed_ms);
172 printf("%s]%s\n", C_DIM, C_RESET);
173}
174
175typedef struct {
176 const char *target;
177 int count;
178} why_ctx_t;
179
180static void print_why_callback(const char *name, const char *version, const char *constraint, pkg_dep_type_t dep_type, void *user_data) {
181 why_ctx_t *ctx = (why_ctx_t *)user_data;
182
183 if (strcmp(name, "package.json") == 0) {
184 const char *type_str = dep_type.dev ? "devDependencies" : "dependencies";
185 printf(" %s└%s %s%s%s %s(%s)%s\n",
186 C_DIM, C_RESET,
187 C_GREEN, name, C_RESET,
188 C_DIM, type_str, C_RESET);
189 } else {
190 const char *type_str = dep_type.peer ? "peer" : (dep_type.dev ? "dev" : (dep_type.optional ? "optional" : ""));
191 if (type_str[0]) {
192 printf(" %s└%s %s %s%s%s@%s%s%s %s\"%s\"%s\n",
193 C_DIM, C_RESET,
194 type_str,
195 C_BOLD, name, C_RESET,
196 C_DIM, version, C_RESET,
197 C_CYAN, constraint, C_RESET);
198 } else {
199 printf(" %s└%s %s%s%s@%s%s%s %s\"%s\"%s\n",
200 C_DIM, C_RESET,
201 C_BOLD, name, C_RESET,
202 C_DIM, version, C_RESET,
203 C_CYAN, constraint, C_RESET);
204 }
205 }
206
207 ctx->count++;
208}
209
210static void print_script(const char *name, const char *command, void *ud) {
211 (void)ud;
212 if (strlen(command) > 50) {
213 printf(" %-15s %.47s...\n", name, command);
214 } else {
215 printf(" %-15s %s\n", name, command);
216 }
217}
218
219static void print_bin_name(const char *name, void *ud) {
220 (void)ud;
221 printf(" %s\n", name);
222}
223
224static void print_pkg_error(pkg_context_t *ctx) {
225 const char *msg = pkg_error_string(ctx);
226 if (!msg || !msg[0]) {
227 fprintf(stderr, "Error: unknown error\n");
228 return;
229 }
230 if (strncmp(msg, "error:", 6) == 0) {
231 fprintf(stderr, "%s\n", msg);
232 } else fprintf(stderr, "Error: %s\n", msg);
233}
234
235static size_t package_name_from_spec(const char *spec, char *out, size_t out_size) {
236 if (!spec || !out || out_size == 0) return 0;
237
238 const char *split = NULL;
239 if (spec[0] == '@') {
240 split = strchr(spec + 1, '@');
241 } else split = strchr(spec, '@');
242
243 size_t len = split ? (size_t)(split - spec) : strlen(spec);
244 if (len == 0 || len >= out_size) return 0;
245
246 memcpy(out, spec, len);
247 out[len] = '\0';
248 return len;
249}
250
251static bool pkg_json_has_dep(yyjson_val *root, const char *section, const char *name) {
252 yyjson_val *deps = yyjson_obj_get(root, section);
253 if (!deps || !yyjson_is_obj(deps)) return false;
254 return yyjson_obj_get(deps, name) != NULL;
255}
256
257static int classify_update_specs(
258 const char *const *package_specs,
259 int count,
260 const char ***deps_specs_out,
261 int *deps_count_out,
262 const char ***dev_specs_out,
263 int *dev_count_out
264) {
265 *deps_specs_out = NULL;
266 *deps_count_out = 0;
267 *dev_specs_out = NULL;
268 *dev_count_out = 0;
269
270 yyjson_read_err err;
271 yyjson_doc *doc = yyjson_read_file("package.json", 0, NULL, &err);
272 if (!doc) {
273 fprintf(stderr, "Error: No package.json found\n");
274 return EXIT_FAILURE;
275 }
276
277 yyjson_val *root = yyjson_doc_get_root(doc);
278 if (!root || !yyjson_is_obj(root)) {
279 yyjson_doc_free(doc);
280 fprintf(stderr, "Error: Invalid package.json format\n");
281 return EXIT_FAILURE;
282 }
283
284 const char **deps_specs = try_oom((size_t)count * sizeof(char *));
285 const char **dev_specs = try_oom((size_t)count * sizeof(char *));
286 if (!deps_specs || !dev_specs) {
287 free((void *)deps_specs);
288 free((void *)dev_specs);
289 yyjson_doc_free(doc);
290 fprintf(stderr, "Error: out of memory\n");
291 return EXIT_FAILURE;
292 }
293
294 int deps_count = 0;
295 int dev_count = 0;
296 for (int i = 0; i < count; i++) {
297 char pkg_name[512];
298 if (package_name_from_spec(package_specs[i], pkg_name, sizeof(pkg_name)) == 0) {
299 free((void *)deps_specs);
300 free((void *)dev_specs);
301 yyjson_doc_free(doc);
302 fprintf(stderr, "Error: Invalid package spec '%s'\n", package_specs[i]);
303 return EXIT_FAILURE;
304 }
305
306 bool in_deps = pkg_json_has_dep(root, "dependencies", pkg_name);
307 bool in_dev = pkg_json_has_dep(root, "devDependencies", pkg_name);
308 if (in_deps) deps_specs[deps_count++] = package_specs[i];
309 else if (in_dev) dev_specs[dev_count++] = package_specs[i];
310 else deps_specs[deps_count++] = package_specs[i];
311 }
312
313 yyjson_doc_free(doc);
314 *deps_specs_out = deps_specs;
315 *deps_count_out = deps_count;
316 *dev_specs_out = dev_specs;
317 *dev_count_out = dev_count;
318 return EXIT_SUCCESS;
319}
320
321bool pkg_script_exists(const char *package_json_path, const char *script_name) {
322 char script_cmd[4096];
323 return pkg_get_script(package_json_path, script_name, script_cmd, sizeof(script_cmd)) >= 0;
324}
325
326static const char *get_global_dir(void) {
327 static char global_dir[4096] = {0};
328 if (global_dir[0] == '\0') {
329 const char *home = getenv("HOME");
330 if (home) snprintf(global_dir, sizeof(global_dir), "%s/.ant/pkg/global", home);
331 }
332 return global_dir;
333}
334
335static int cmd_add_global(const char *const *package_specs, int count) {
336 print_install_header("add -g");
337
338 char resolve_msg[64];
339 snprintf(resolve_msg, sizeof(resolve_msg), "🔍 Resolving [%d/%d]", count, count);
340
341 progress_t progress;
342 if (!pkg_verbose) progress_start(&progress, resolve_msg);
343
344 pkg_options_t opts = {
345 .progress_callback = pkg_verbose ? NULL : progress_callback,
346 .user_data = pkg_verbose ? NULL : &progress,
347 .verbose = pkg_verbose
348 };
349 pkg_context_t *ctx = pkg_init(&opts);
350 if (!ctx) {
351 if (!pkg_verbose) progress_stop(&progress);
352 fprintf(stderr, "Error: Failed to initialize package manager\n");
353 return EXIT_FAILURE;
354 }
355
356 pkg_error_t err = pkg_add_global_many(ctx, package_specs, (uint32_t)count);
357 if (!pkg_verbose) progress_stop(&progress);
358
359 if (err != PKG_OK) {
360 print_pkg_error(ctx);
361 pkg_free(ctx);
362 return EXIT_FAILURE;
363 }
364
365 pkg_install_result_t result;
366 if (pkg_get_install_result(ctx, &result) == PKG_OK) {
367 for (int i = 0; i < count; i++) {
368 printf("\n%sinstalled globally%s %s%s%s\n",
369 C_GREEN, C_RESET, C_BOLD, package_specs[i], C_RESET);
370 }
371 printf(" %s(binaries linked to ~/.ant/bin)%s\n", C_DIM, C_RESET);
372 printf("\n%s[%s", C_DIM, C_RESET);
373 print_elapsed(result.elapsed_ms);
374 printf("%s]%s done\n", C_DIM, C_RESET);
375 }
376
377 pkg_free(ctx);
378 return EXIT_SUCCESS;
379}
380
381static int cmd_remove_global(const char *package_name) {
382 print_install_header("remove -g");
383
384 progress_t progress;
385 if (!pkg_verbose) progress_start(&progress, "🔍 Resolving");
386
387 pkg_options_t opts = {
388 .progress_callback = pkg_verbose ? NULL : progress_callback,
389 .user_data = pkg_verbose ? NULL : &progress,
390 .verbose = pkg_verbose
391 };
392 pkg_context_t *ctx = pkg_init(&opts);
393 if (!ctx) {
394 if (!pkg_verbose) progress_stop(&progress);
395 fprintf(stderr, "Error: Failed to initialize package manager\n");
396 return EXIT_FAILURE;
397 }
398
399 pkg_error_t err = pkg_remove_global(ctx, package_name);
400 if (!pkg_verbose) progress_stop(&progress);
401
402 if (err == PKG_NOT_FOUND) {
403 printf("\nPackage '%s' not found in global dependencies\n", package_name);
404 pkg_free(ctx);
405 return EXIT_SUCCESS;
406 }
407
408 if (err != PKG_OK) {
409 print_pkg_error(ctx);
410 pkg_free(ctx);
411 return EXIT_FAILURE;
412 }
413
414 printf("\n%s-%s Removed globally: %s%s%s\n", C_RED, C_RESET, C_BOLD, package_name, C_RESET);
415
416 pkg_free(ctx);
417 return EXIT_SUCCESS;
418}
419
420static int cmd_install(void) {
421 print_install_header("install");
422
423 progress_t progress;
424
425 if (!pkg_verbose) {
426 progress_start(&progress, "🔍 Resolving [1/1]");
427 }
428
429 pkg_options_t opts = {
430 .progress_callback = pkg_verbose ? NULL : progress_callback,
431 .user_data = pkg_verbose ? NULL : &progress,
432 .verbose = pkg_verbose
433 };
434 pkg_context_t *ctx = pkg_init(&opts);
435 if (!ctx) {
436 fprintf(stderr, "Error: Failed to initialize package manager\n");
437 return EXIT_FAILURE;
438 }
439
440 struct stat st;
441 bool needs_resolve = (stat("ant.lockb", &st) != 0);
442
443 if (needs_resolve) {
444 if (stat("package.json", &st) != 0) {
445 if (!pkg_verbose) { progress_stop(&progress); }
446 fprintf(stderr, "Error: No package.json found\n");
447 pkg_free(ctx);
448 return EXIT_FAILURE;
449 }
450
451 pkg_error_t err = pkg_resolve_and_install(ctx, "package.json", "ant.lockb", "node_modules");
452 if (err != PKG_OK) {
453 if (!pkg_verbose) { progress_stop(&progress); }
454 print_pkg_error(ctx);
455 pkg_free(ctx);
456 return EXIT_FAILURE;
457 }
458 } else {
459 pkg_error_t err = pkg_install(ctx, "package.json", "ant.lockb", "node_modules");
460 if (err != PKG_OK) {
461 if (!pkg_verbose) { progress_stop(&progress); }
462 print_pkg_error(ctx);
463 pkg_free(ctx);
464 return EXIT_FAILURE;
465 }
466 }
467
468 if (!pkg_verbose) {
469 progress_stop(&progress);
470
471 }
472
473 pkg_install_result_t result;
474 if (pkg_get_install_result(ctx, &result) == PKG_OK) {
475 if (result.packages_installed > 0) {
476 print_added_packages(ctx);
477 printf("%s%u%s package%s installed",
478 C_GREEN, result.packages_installed, C_RESET,
479 result.packages_installed == 1 ? "" : "s");
480 if (result.cache_hits > 0) {
481 printf(" %s(%u cached)%s", C_DIM, result.cache_hits, C_RESET);
482 }
483 printf(" %s[%s", C_DIM, C_RESET);
484 print_elapsed(result.elapsed_ms);
485 printf("%s]%s\n", C_DIM, C_RESET);
486 } else {
487 printf("\n%sChecked%s %s%u%s installs across %s%u%s packages %s(no changes)%s %s[%s",
488 C_DIM, C_RESET,
489 C_GREEN, result.packages_installed + result.packages_skipped, C_RESET,
490 C_GREEN, result.package_count, C_RESET,
491 C_DIM, C_RESET,
492 C_DIM, C_RESET);
493 print_elapsed(result.elapsed_ms);
494 printf("%s]%s\n", C_DIM, C_RESET);
495 }
496 }
497
498 if (pkg_discover_lifecycle_scripts(ctx, "node_modules") == PKG_OK) {
499 uint32_t script_count = pkg_get_lifecycle_script_count(ctx);
500 if (script_count > 0) {
501 printf("\n%s%u%s package%s need%s to run lifecycle scripts:\n",
502 C_YELLOW, script_count, C_RESET,
503 script_count == 1 ? "" : "s",
504 script_count == 1 ? "s" : "");
505
506 for (uint32_t i = 0; i < script_count; i++) {
507 pkg_lifecycle_script_t script;
508 if (pkg_get_lifecycle_script(ctx, i, &script) == PKG_OK) {
509 printf(" %s•%s %s%s%s %s(%s)%s\n",
510 C_DIM, C_RESET,
511 C_CYAN, script.name, C_RESET,
512 C_DIM, script.script, C_RESET);
513 }
514 }
515
516 printf("\nRun: %sant trust <pkg>%s or %sant trust --all%s\n", C_DIM, C_RESET, C_DIM, C_RESET);
517 }
518 }
519
520 pkg_free(ctx);
521 return EXIT_SUCCESS;
522}
523
524static int cmd_update(void) {
525 print_install_header("update");
526
527 progress_t progress;
528
529 if (!pkg_verbose) {
530 progress_start(&progress, "🔍 Resolving [1/1]");
531 }
532
533 pkg_options_t opts = {
534 .progress_callback = pkg_verbose ? NULL : progress_callback,
535 .user_data = pkg_verbose ? NULL : &progress,
536 .verbose = pkg_verbose
537 };
538 pkg_context_t *ctx = pkg_init(&opts);
539 if (!ctx) {
540 fprintf(stderr, "Error: Failed to initialize package manager\n");
541 return EXIT_FAILURE;
542 }
543
544 struct stat st;
545 if (stat("package.json", &st) != 0) {
546 if (!pkg_verbose) progress_stop(&progress);
547 fprintf(stderr, "Error: No package.json found\n");
548 pkg_free(ctx);
549 return EXIT_FAILURE;
550 }
551
552 pkg_error_t err = pkg_resolve_and_install(ctx, "package.json", "ant.lockb", "node_modules");
553 if (err != PKG_OK) {
554 if (!pkg_verbose) progress_stop(&progress);
555 print_pkg_error(ctx);
556 pkg_free(ctx);
557 return EXIT_FAILURE;
558 }
559
560 if (!pkg_verbose) {
561 progress_stop(&progress);
562 }
563
564 pkg_install_result_t result;
565 if (pkg_get_install_result(ctx, &result) == PKG_OK) {
566 if (result.packages_installed > 0) {
567 print_added_packages(ctx);
568 printf("%s%u%s package%s installed",
569 C_GREEN, result.packages_installed, C_RESET,
570 result.packages_installed == 1 ? "" : "s");
571 if (result.cache_hits > 0) {
572 printf(" %s(%u cached)%s", C_DIM, result.cache_hits, C_RESET);
573 }
574 printf(" %s[%s", C_DIM, C_RESET);
575 print_elapsed(result.elapsed_ms);
576 printf("%s]%s\n", C_DIM, C_RESET);
577 } else {
578 printf("\n%sChecked%s %s%u%s installs across %s%u%s packages %s(no changes)%s %s[%s",
579 C_DIM, C_RESET,
580 C_GREEN, result.packages_installed + result.packages_skipped, C_RESET,
581 C_GREEN, result.package_count, C_RESET,
582 C_DIM, C_RESET,
583 C_DIM, C_RESET);
584 print_elapsed(result.elapsed_ms);
585 printf("%s]%s\n", C_DIM, C_RESET);
586 }
587 }
588
589 if (pkg_discover_lifecycle_scripts(ctx, "node_modules") == PKG_OK) {
590 uint32_t script_count = pkg_get_lifecycle_script_count(ctx);
591 if (script_count > 0) {
592 printf("\n%s%u%s package%s need%s to run lifecycle scripts:\n",
593 C_YELLOW, script_count, C_RESET,
594 script_count == 1 ? "" : "s",
595 script_count == 1 ? "s" : "");
596
597 for (uint32_t i = 0; i < script_count; i++) {
598 pkg_lifecycle_script_t script;
599 if (pkg_get_lifecycle_script(ctx, i, &script) == PKG_OK) {
600 printf(" %s•%s %s%s%s %s(%s)%s\n",
601 C_DIM, C_RESET,
602 C_CYAN, script.name, C_RESET,
603 C_DIM, script.script, C_RESET);
604 }
605 }
606
607 printf("\nRun: %sant trust <pkg>%s or %sant trust --all%s\n", C_DIM, C_RESET, C_DIM, C_RESET);
608 }
609 }
610
611 pkg_free(ctx);
612 return EXIT_SUCCESS;
613}
614
615static int cmd_update_many(const char *const *package_specs, int count) {
616 print_install_header("update");
617
618 const char **deps_specs = NULL;
619 const char **dev_specs = NULL;
620 int deps_count = 0;
621 int dev_count = 0;
622 if (classify_update_specs(package_specs, count, &deps_specs, &deps_count, &dev_specs, &dev_count) != EXIT_SUCCESS) {
623 return EXIT_FAILURE;
624 }
625
626 char resolve_msg[64];
627 snprintf(resolve_msg, sizeof(resolve_msg), "🔍 Resolving [%d/%d]", count, count);
628
629 progress_t progress;
630 if (!pkg_verbose) {
631 progress_start(&progress, resolve_msg);
632 }
633
634 pkg_options_t opts = {
635 .progress_callback = pkg_verbose ? NULL : progress_callback,
636 .user_data = pkg_verbose ? NULL : &progress,
637 .verbose = pkg_verbose
638 };
639 pkg_context_t *ctx = pkg_init(&opts);
640 if (!ctx) {
641 free((void *)deps_specs);
642 free((void *)dev_specs);
643 if (!pkg_verbose) progress_stop(&progress);
644 fprintf(stderr, "Error: Failed to initialize package manager\n");
645 return EXIT_FAILURE;
646 }
647
648 pkg_error_t err = PKG_OK;
649 if (deps_count > 0) {
650 err = pkg_add_many(ctx, "package.json", deps_specs, (uint32_t)deps_count, false);
651 }
652 if (err == PKG_OK && dev_count > 0) {
653 err = pkg_add_many(ctx, "package.json", dev_specs, (uint32_t)dev_count, true);
654 }
655 free((void *)deps_specs);
656 free((void *)dev_specs);
657
658 if (err != PKG_OK) {
659 if (!pkg_verbose) progress_stop(&progress);
660 print_pkg_error(ctx);
661 pkg_free(ctx);
662 return EXIT_FAILURE;
663 }
664
665 err = pkg_resolve_and_install(ctx, "package.json", "ant.lockb", "node_modules");
666 if (err != PKG_OK) {
667 if (!pkg_verbose) progress_stop(&progress);
668 print_pkg_error(ctx);
669 pkg_free(ctx);
670 return EXIT_FAILURE;
671 }
672
673 if (!pkg_verbose) progress_stop(&progress);
674
675 pkg_install_result_t result;
676 if (pkg_get_install_result(ctx, &result) == PKG_OK) {
677 print_add_summary(ctx, &result, true);
678 }
679
680 pkg_free(ctx);
681 return EXIT_SUCCESS;
682}
683
684static int cmd_add(const char *const *package_specs, int count, bool dev) {
685 print_install_header(dev ? "add -D" : "add");
686
687 char resolve_msg[64];
688 snprintf(resolve_msg, sizeof(resolve_msg), "🔍 Resolving [%d/%d]", count, count);
689
690 progress_t progress;
691 if (!pkg_verbose) {
692 progress_start(&progress, resolve_msg);
693 }
694
695 pkg_options_t opts = {
696 .progress_callback = pkg_verbose ? NULL : progress_callback,
697 .user_data = pkg_verbose ? NULL : &progress,
698 .verbose = pkg_verbose
699 };
700 pkg_context_t *ctx = pkg_init(&opts);
701 if (!ctx) {
702 fprintf(stderr, "Error: Failed to initialize package manager\n");
703 return EXIT_FAILURE;
704 }
705
706 pkg_error_t err = pkg_add_many(ctx, "package.json", package_specs, (uint32_t)count, dev);
707 if (err != PKG_OK) {
708 if (!pkg_verbose) { progress_stop(&progress); }
709 print_pkg_error(ctx);
710 pkg_free(ctx);
711 return EXIT_FAILURE;
712 }
713
714 err = pkg_resolve_and_install(ctx, "package.json", "ant.lockb", "node_modules");
715 if (err != PKG_OK) {
716 if (!pkg_verbose) { progress_stop(&progress); }
717 print_pkg_error(ctx);
718 pkg_free(ctx);
719 return EXIT_FAILURE;
720 }
721
722 if (!pkg_verbose) progress_stop(&progress);
723
724 pkg_install_result_t result;
725 if (pkg_get_install_result(ctx, &result) == PKG_OK) {
726 print_add_summary(ctx, &result, false);
727 }
728
729 pkg_free(ctx);
730 return EXIT_SUCCESS;
731}
732
733static int cmd_remove(const char *package_name) {
734 print_install_header("remove");
735 progress_t progress;
736
737 if (!pkg_verbose) {
738 progress_start(&progress, "🔍 Resolving");
739 }
740
741 pkg_options_t opts = {
742 .progress_callback = pkg_verbose ? NULL : progress_callback,
743 .user_data = pkg_verbose ? NULL : &progress,
744 .verbose = pkg_verbose
745 };
746 pkg_context_t *ctx = pkg_init(&opts);
747 if (!ctx) {
748 fprintf(stderr, "Error: Failed to initialize package manager\n");
749 return EXIT_FAILURE;
750 }
751
752 pkg_error_t err = pkg_remove(ctx, "package.json", package_name);
753 if (err != PKG_OK && err != PKG_NOT_FOUND) {
754 if (!pkg_verbose) { progress_stop(&progress); }
755 print_pkg_error(ctx);
756 pkg_free(ctx);
757 return EXIT_FAILURE;
758 }
759
760 if (err == PKG_NOT_FOUND) {
761 if (!pkg_verbose) { progress_stop(&progress); }
762 printf("\n%s[%s", C_DIM, C_RESET);
763 printf("%s0ms%s", C_BOLD, C_RESET);
764 printf("%s]%s done\n", C_DIM, C_RESET);
765 pkg_free(ctx);
766 return EXIT_SUCCESS;
767 }
768
769 err = pkg_resolve_and_install(ctx, "package.json", "ant.lockb", "node_modules");
770 if (err != PKG_OK) {
771 if (!pkg_verbose) { progress_stop(&progress); }
772 print_pkg_error(ctx);
773 pkg_free(ctx);
774 return EXIT_FAILURE;
775 }
776
777 if (!pkg_verbose) {
778 progress_stop(&progress);
779
780 }
781
782 pkg_install_result_t result;
783 if (pkg_get_install_result(ctx, &result) == PKG_OK) {
784 printf("\n%s%u%s package%s installed %s[%s",
785 C_GREEN, result.packages_installed, C_RESET,
786 result.packages_installed == 1 ? "" : "s",
787 C_DIM, C_RESET);
788 print_elapsed(result.elapsed_ms);
789 printf("%s]%s\n", C_DIM, C_RESET);
790 }
791
792 printf("%s-%s Removed: %s%s%s\n", C_RED, C_RESET, C_BOLD, package_name, C_RESET);
793 pkg_free(ctx);
794
795 return EXIT_SUCCESS;
796}
797
798static int cmd_trust(const char **pkgs, int count, bool all) {
799 print_install_header("trust");
800
801 struct timespec start_time;
802 clock_gettime(CLOCK_MONOTONIC, &start_time);
803
804 pkg_options_t opts = { .verbose = pkg_verbose };
805 pkg_context_t *ctx = pkg_init(&opts);
806 if (!ctx) {
807 fprintf(stderr, "Error: Failed to initialize package manager\n");
808 return EXIT_FAILURE;
809 }
810
811 if (pkg_discover_lifecycle_scripts(ctx, "node_modules") != PKG_OK) {
812 fprintf(stderr, "Error: Failed to scan node_modules\n");
813 pkg_free(ctx);
814 return EXIT_FAILURE;
815 }
816
817 uint32_t script_count = pkg_get_lifecycle_script_count(ctx);
818 if (script_count == 0) {
819 printf("No packages need lifecycle scripts to run.\n");
820 pkg_free(ctx);
821 return EXIT_SUCCESS;
822 }
823
824 const char **to_run = NULL;
825 uint32_t to_run_count = 0;
826
827 if (all) {
828 to_run = try_oom(script_count * sizeof(char *));
829 if (to_run) {
830 for (uint32_t i = 0; i < script_count; i++) {
831 pkg_lifecycle_script_t script;
832 if (pkg_get_lifecycle_script(ctx, i, &script) == PKG_OK) {
833 to_run[to_run_count++] = script.name;
834 }
835 }
836 }
837 } else if (count > 0) {
838 to_run = try_oom(count * sizeof(char *));
839 if (to_run) {
840 for (int i = 0; i < count; i++) {
841 bool found = false;
842 for (uint32_t j = 0; j < script_count; j++) {
843 pkg_lifecycle_script_t script;
844 if (pkg_get_lifecycle_script(ctx, j, &script) == PKG_OK) {
845 if (strcmp(pkgs[i], script.name) == 0) {
846 to_run[to_run_count++] = script.name;
847 found = true; break;
848 }
849 }
850 }
851 if (!found) fprintf(stderr, "Warning: %s has no pending lifecycle script\n", pkgs[i]);
852 }
853 }
854 } else {
855 printf("%s%u%s package%s with lifecycle scripts:\n",
856 C_YELLOW, script_count, C_RESET,
857 script_count == 1 ? "" : "s");
858
859 for (uint32_t i = 0; i < script_count; i++) {
860 pkg_lifecycle_script_t script;
861 if (pkg_get_lifecycle_script(ctx, i, &script) == PKG_OK) {
862 printf(" %s•%s %s%s%s %s(%s)%s\n",
863 C_DIM, C_RESET,
864 C_CYAN, script.name, C_RESET,
865 C_DIM, script.script, C_RESET);
866 }
867 }
868 printf("\nRun: %sant trust <pkg>%s or %sant trust --all%s\n", C_DIM, C_RESET, C_DIM, C_RESET);
869 pkg_free(ctx);
870 return EXIT_SUCCESS;
871 }
872
873 if (to_run && to_run_count > 0) {
874 if (pkg_verbose) {
875 printf("[trust] adding %u packages to trustedDependencies\n", to_run_count);
876 for (uint32_t i = 0; i < to_run_count; i++) {
877 printf("[trust] %s\n", to_run[i]);
878 }
879 }
880 pkg_error_t add_err = pkg_add_trusted_dependencies("package.json", to_run, to_run_count);
881 if (add_err == PKG_OK) {
882 printf("Added %s%u%s package%s to %strustedDependencies%s in package.json\n",
883 C_GREEN, to_run_count, C_RESET,
884 to_run_count == 1 ? "" : "s",
885 C_BOLD, C_RESET);
886 } else {
887 if (pkg_verbose) printf("[trust] failed to add trustedDependencies: error %d\n", add_err);
888 }
889
890 printf("Running lifecycle scripts for %s%u%s package%s...\n",
891 C_GREEN, to_run_count, C_RESET,
892 to_run_count == 1 ? "" : "s");
893 pkg_run_postinstall(ctx, "node_modules", to_run, to_run_count);
894
895 struct timespec end_time;
896 clock_gettime(CLOCK_MONOTONIC, &end_time);
897 uint64_t elapsed_ms = timespec_diff_ms(&start_time, &end_time);
898
899 printf("\n%s%u%s package%s trusted %s[%s",
900 C_GREEN, to_run_count, C_RESET,
901 to_run_count == 1 ? "" : "s",
902 C_DIM, C_RESET);
903 print_elapsed(elapsed_ms);
904 printf("%s]%s\n", C_DIM, C_RESET);
905 free((void *)to_run);
906 }
907
908 pkg_free(ctx);
909 return EXIT_SUCCESS;
910}
911
912static int cmd_init(void) {
913 FILE *fp = fopen("package.json", "r");
914 if (fp) {
915 fclose(fp);
916 fprintf(stderr, "Error: package.json already exists\n");
917 return EXIT_FAILURE;
918 }
919
920 char cwd[PATH_MAX];
921 const char *default_name = "my-project";
922 if (getcwd(cwd, sizeof(cwd))) {
923 char *base = strrchr(cwd, '/');
924 if (base && base[1]) default_name = base + 1;
925 }
926
927 bool interactive = isatty(fileno(stdin));
928
929 char name[256] = {0};
930 char version[64] = {0};
931 char entry[256] = {0};
932
933 if (interactive) {
934 printf("%sant init%s\n\n", C_BOLD, C_RESET);
935
936 prompt_with_default("package name", default_name, name, sizeof(name));
937 prompt_with_default("version", "1.0.0", version, sizeof(version));
938 prompt_with_default("entry point", "index.js", entry, sizeof(entry));
939
940 fputc('\n', stdout);
941 } else {
942 strncpy(name, default_name, sizeof(name) - 1);
943 strncpy(version, "1.0.0", sizeof(version) - 1);
944 strncpy(entry, "index.js", sizeof(entry) - 1);
945 }
946
947 fp = fopen("package.json", "w");
948 if (!fp) {
949 fprintf(stderr, "Error: Could not create package.json\n");
950 return EXIT_FAILURE;
951 }
952
953 yyjson_mut_doc *doc = yyjson_mut_doc_new(NULL);
954 yyjson_mut_val *root = yyjson_mut_obj(doc);
955 yyjson_mut_doc_set_root(doc, root);
956
957 yyjson_mut_obj_add_str(doc, root, "name", name);
958 yyjson_mut_obj_add_str(doc, root, "version", version);
959 yyjson_mut_obj_add_str(doc, root, "type", "module");
960 yyjson_mut_obj_add_str(doc, root, "main", entry);
961
962 yyjson_mut_val *scripts = yyjson_mut_obj_add_obj(doc, root, "scripts");
963 char start_cmd[300];
964 snprintf(start_cmd, sizeof(start_cmd), "ant %s", entry);
965 yyjson_mut_obj_add_str(doc, scripts, "start", start_cmd);
966
967 yyjson_mut_obj_add_obj(doc, root, "dependencies");
968 yyjson_mut_obj_add_obj(doc, root, "devDependencies");
969
970 size_t len; char *json_str = yyjson_mut_write(
971 doc, YYJSON_WRITE_PRETTY_TWO_SPACES
972 | YYJSON_WRITE_ESCAPE_UNICODE, &len
973 );
974
975 if (json_str) {
976 fwrite(json_str, 1, len, fp);
977 free(json_str);
978 }
979
980 yyjson_mut_doc_free(doc);
981 fclose(fp);
982
983 printf("%s+%s Created %spackage.json%s\n", C_GREEN, C_RESET, C_BOLD, C_RESET);
984 return EXIT_SUCCESS;
985}
986
987static int cmd_why(const char *package_name) {
988 struct stat st;
989 if (stat("ant.lockb", &st) != 0) {
990 fprintf(stderr, "Error: No lockfile found. Run 'ant install' first.\n");
991 return EXIT_FAILURE;
992 }
993
994 pkg_why_info_t info;
995 if (pkg_why_info("ant.lockb", package_name, &info) < 0) {
996 fprintf(stderr, "Error: Failed to read lockfile\n");
997 return EXIT_FAILURE;
998 }
999
1000 if (!info.found) {
1001 printf("\n%s%s%s is not installed\n\n", C_BOLD, package_name, C_RESET);
1002 return EXIT_SUCCESS;
1003 }
1004
1005 const char *type_label = info.is_peer ? " peer" : (info.is_dev ? " dev" : "");
1006 printf("\n%s%s%s@%s%s%s%s%s%s\n", C_BOLD, package_name, C_RESET, C_DIM, info.target_version, C_RESET, C_YELLOW, type_label, C_RESET);
1007
1008 why_ctx_t ctx = { .target = package_name, .count = 0 };
1009 int result = pkg_why("ant.lockb", package_name, print_why_callback, &ctx);
1010
1011 if (result < 0) {
1012 fprintf(stderr, "Error: Failed to read lockfile\n");
1013 return EXIT_FAILURE;
1014 }
1015
1016 if (ctx.count == 0) {
1017 printf(" %s(no dependents)%s\n", C_DIM, C_RESET);
1018 }
1019
1020 fputc('\n', stdout);
1021 return EXIT_SUCCESS;
1022}
1023
1024int pkg_cmd_init(int argc, char **argv) {
1025 struct arg_lit *help = arg_lit0("h", "help", "display help");
1026 struct arg_end *end = arg_end(5);
1027
1028 void *argtable[] = { help, end };
1029 int nerrors = arg_parse(argc, argv, argtable);
1030
1031 int exitcode = EXIT_SUCCESS;
1032 if (help->count > 0) {
1033 printf("Usage: ant init\n\n");
1034 printf("Create a new package.json\n");
1035 } else if (nerrors > 0) {
1036 arg_print_errors(stdout, end, "ant init");
1037 exitcode = EXIT_FAILURE;
1038 } else {
1039 exitcode = cmd_init();
1040 }
1041
1042 arg_freetable(argtable, sizeof(argtable)/sizeof(argtable[0]));
1043 return exitcode;
1044}
1045
1046int pkg_cmd_install(int argc, char **argv) {
1047 struct arg_str *pkgs = arg_strn(NULL, NULL, "<package[@version]>", 0, 100, NULL);
1048 struct arg_lit *global = arg_lit0("g", "global", "install globally");
1049 struct arg_lit *dev = arg_lit0("D", "save-dev", "add as devDependency");
1050 struct arg_lit *help = arg_lit0("h", "help", "display help");
1051 struct arg_end *end = arg_end(5);
1052
1053 void *argtable[] = { pkgs, global, dev, help, end };
1054 int nerrors = arg_parse(argc, argv, argtable);
1055
1056 int exitcode = EXIT_SUCCESS;
1057 if (help->count > 0) {
1058 printf("Usage: ant install [packages...] [-g] [-D] [--verbose]\n\n");
1059 printf("Install from lockfile, or add packages if specified.\n");
1060 printf("\nOptions:\n -g, --global Install globally to ~/.ant/pkg/global\n");
1061 printf(" -D, --save-dev Add as devDependency\n");
1062 } else if (nerrors > 0) {
1063 arg_print_errors(stdout, end, "ant install");
1064 exitcode = EXIT_FAILURE;
1065 } else if (pkgs->count == 0) {
1066 exitcode = cmd_install();
1067 } else {
1068 bool is_dev = dev->count > 0;
1069 exitcode = global->count > 0
1070 ? cmd_add_global(pkgs->sval, pkgs->count)
1071 : cmd_add(pkgs->sval, pkgs->count, is_dev);
1072 }
1073
1074 arg_freetable(argtable, sizeof(argtable)/sizeof(argtable[0]));
1075 return exitcode;
1076}
1077
1078int pkg_cmd_update(int argc, char **argv) {
1079 struct arg_str *pkgs = arg_strn(NULL, NULL, "<package[@version]>", 0, 100, NULL);
1080 struct arg_lit *help = arg_lit0("h", "help", "display help");
1081 struct arg_end *end = arg_end(5);
1082
1083 void *argtable[] = { pkgs, help, end };
1084 int nerrors = arg_parse(argc, argv, argtable);
1085
1086 int exitcode = EXIT_SUCCESS;
1087 if (help->count > 0) {
1088 printf("Usage: ant update [packages...] [--verbose]\n\n");
1089 printf("Re-resolve all dependencies, or upgrade specific packages in place.\n");
1090 } else if (nerrors > 0) {
1091 arg_print_errors(stdout, end, "ant update");
1092 exitcode = EXIT_FAILURE;
1093 } else {
1094 exitcode = pkgs->count > 0 ? cmd_update_many(pkgs->sval, pkgs->count) : cmd_update();
1095 }
1096
1097 arg_freetable(argtable, sizeof(argtable)/sizeof(argtable[0]));
1098 return exitcode;
1099}
1100
1101int pkg_cmd_add(int argc, char **argv) {
1102 struct arg_str *pkgs = arg_strn(NULL, NULL, "<package[@version]>", 1, 100, NULL);
1103 struct arg_lit *global = arg_lit0("g", "global", "install globally");
1104 struct arg_lit *dev = arg_lit0("D", "save-dev", "add as devDependency");
1105 struct arg_lit *help = arg_lit0("h", "help", "display help");
1106 struct arg_end *end = arg_end(5);
1107
1108 void *argtable[] = { pkgs, global, dev, help, end };
1109 int nerrors = arg_parse(argc, argv, argtable);
1110
1111 int exitcode = EXIT_SUCCESS;
1112 if (help->count > 0) {
1113 printf("Usage: ant add <package[@version]>... [options]\n\n");
1114 printf("Add packages to dependencies.\n");
1115 printf("\nOptions:\n -g, --global Install globally to ~/.ant/pkg/global\n");
1116 printf(" -D, --save-dev Add as devDependency\n");
1117 } else if (nerrors > 0) {
1118 arg_print_errors(stdout, end, "ant add");
1119 exitcode = EXIT_FAILURE;
1120 } else {
1121 bool is_dev = dev->count > 0;
1122 exitcode = global->count > 0
1123 ? cmd_add_global(pkgs->sval, pkgs->count)
1124 : cmd_add(pkgs->sval, pkgs->count, is_dev);
1125 }
1126
1127 arg_freetable(argtable, sizeof(argtable)/sizeof(argtable[0]));
1128 return exitcode;
1129}
1130
1131int pkg_cmd_remove(int argc, char **argv) {
1132 struct arg_str *pkgs = arg_strn(NULL, NULL, "<package>", 1, 100, NULL);
1133 struct arg_lit *global = arg_lit0("g", "global", "remove from global packages");
1134 struct arg_lit *help = arg_lit0("h", "help", "display help");
1135 struct arg_end *end = arg_end(5);
1136
1137 void *argtable[] = { pkgs, global, help, end };
1138 int nerrors = arg_parse(argc, argv, argtable);
1139
1140 int exitcode = EXIT_SUCCESS;
1141 if (help->count > 0) {
1142 printf("Usage: ant remove <package>... [-g]\n\n");
1143 printf("Remove packages from dependencies.\n");
1144 printf("\nOptions:\n -g, --global Remove from global packages\n");
1145 } else if (nerrors > 0) {
1146 arg_print_errors(stdout, end, "ant remove");
1147 exitcode = EXIT_FAILURE;
1148 } else {
1149 for (int i = 0; i < pkgs->count && exitcode == EXIT_SUCCESS; i++) {
1150 exitcode = global->count > 0 ? cmd_remove_global(pkgs->sval[i]) : cmd_remove(pkgs->sval[i]);
1151 }
1152 }
1153
1154 arg_freetable(argtable, sizeof(argtable)/sizeof(argtable[0]));
1155 return exitcode;
1156}
1157
1158int pkg_cmd_trust(int argc, char **argv) {
1159 struct arg_str *pkgs = arg_strn(NULL, NULL, "<package>", 0, 100, NULL);
1160 struct arg_lit *all = arg_lit0("a", "all", "trust all packages with lifecycle scripts");
1161 struct arg_lit *help = arg_lit0("h", "help", "display help");
1162 struct arg_end *end = arg_end(5);
1163
1164 void *argtable[] = { pkgs, all, help, end };
1165 int nerrors = arg_parse(argc, argv, argtable);
1166
1167 int exitcode = EXIT_SUCCESS;
1168 if (help->count > 0) {
1169 printf("Usage: ant trust [packages...] [--all]\n\n");
1170 printf("Run lifecycle scripts for packages.\n");
1171 printf(" --all, -a Trust and run all pending lifecycle scripts\n");
1172 } else if (nerrors > 0) {
1173 arg_print_errors(stdout, end, "ant trust");
1174 exitcode = EXIT_FAILURE;
1175 } else {
1176 exitcode = cmd_trust(pkgs->sval, pkgs->count, all->count > 0);
1177 }
1178
1179 arg_freetable(argtable, sizeof(argtable)/sizeof(argtable[0]));
1180 return exitcode;
1181}
1182
1183int pkg_cmd_run(int argc, char **argv) {
1184 if (argc < 2) {
1185 printf("Usage: ant run <script> [args...]\n\n");
1186 printf("Run a script from package.json\n\n");
1187 printf("Available scripts:\n");
1188
1189 int count = pkg_list_scripts("package.json", NULL, NULL);
1190 if (count < 0) {
1191 printf(" (no package.json found)\n");
1192 } else if (count == 0) {
1193 printf(" (no scripts defined)\n");
1194 } else {
1195 pkg_list_scripts("package.json", print_script, NULL);
1196 }
1197 return EXIT_SUCCESS;
1198 }
1199
1200 const char *script_name = argv[1];
1201
1202 char script_cmd[4096];
1203 int script_len = pkg_get_script("package.json", script_name, script_cmd, sizeof(script_cmd));
1204 if (script_len < 0) {
1205 fprintf(stderr, "Error: script '%s' not found in package.json\n", script_name);
1206 fprintf(stderr, "Try 'ant run' to list available scripts.\n");
1207 return EXIT_FAILURE;
1208 }
1209
1210 char extra_args[4096] = {0};
1211 int extra_args_len = 0;
1212
1213 int arg_start = 2;
1214 if (arg_start < argc && strcmp(argv[arg_start], "--") == 0) arg_start++;
1215
1216 for (int i = arg_start; i < argc; i++) {
1217 if (extra_args_len > 0) extra_args[extra_args_len++] = ' ';
1218 size_t arg_len = strlen(argv[i]);
1219 if ((size_t)extra_args_len + arg_len < sizeof(extra_args) - 1) {
1220 memcpy(extra_args + extra_args_len, argv[i], arg_len);
1221 extra_args_len += (int)arg_len;
1222 }
1223 }
1224
1225 extra_args[extra_args_len] = '\0';
1226 printf("%s$%s %s%s%s", C_MAGENTA, C_RESET, C_BOLD, script_cmd, C_RESET);
1227
1228 if (extra_args_len > 0) printf(" %s", extra_args);
1229 fputc('\n', stdout);
1230
1231 pkg_script_result_t result = {0};
1232 pkg_error_t err = pkg_run_script(
1233 "package.json", script_name, "node_modules",
1234 extra_args_len > 0 ? extra_args : NULL,
1235 &result
1236 );
1237
1238 if (err != PKG_OK) {
1239 if (err == PKG_NOT_FOUND) {
1240 fprintf(stderr, "Error: script '%s' not found\n", script_name);
1241 } else {
1242 fprintf(stderr, "Error: failed to run script '%s'\n", script_name);
1243 }
1244 return EXIT_FAILURE;
1245 }
1246
1247 if (result.signal != 0) {
1248 fprintf(stderr, "Script '%s' killed by signal %d\n", script_name, result.signal);
1249 return 128 + result.signal;
1250 }
1251
1252 return result.exit_code;
1253}
1254
1255int pkg_cmd_exec(int argc, char **argv) {
1256 if (argc < 2) {
1257 printf("Usage: ant x [--ant] <command> [args...]\n\n");
1258 printf("Run a command from node_modules/.bin or download temporarily\n\n");
1259 printf("Options:\n");
1260 printf(" --ant Run with ant instead of node\n\n");
1261 printf("Available commands:\n");
1262
1263 int count = pkg_list_bins("node_modules", NULL, NULL);
1264 if (count < 0) printf(" (no binaries found - run 'ant install' first)\n");
1265 else if (count == 0) printf(" (no binaries installed)\n");
1266 else pkg_list_bins("node_modules", print_bin_name, NULL);
1267
1268 return EXIT_SUCCESS;
1269 }
1270
1271 bool use_ant = false;
1272 int cmd_idx = 1;
1273
1274 if (strcmp(argv[1], "--ant") == 0) {
1275 use_ant = true;
1276 cmd_idx = 2;
1277 if (argc < 3) {
1278 fprintf(stderr, "Error: missing command after --ant\n");
1279 return EXIT_FAILURE;
1280 }
1281 }
1282
1283 const char *cmd_name = argv[cmd_idx]; char bin_path[4096];
1284 int path_len = pkg_get_bin_path("node_modules", cmd_name, bin_path, sizeof(bin_path));
1285
1286 if (path_len < 0) {
1287 const char *global_dir = get_global_dir();
1288 if (global_dir[0]) {
1289 char global_nm[4096];
1290 snprintf(global_nm, sizeof(global_nm), "%s/node_modules", global_dir);
1291 path_len = pkg_get_bin_path(global_nm, cmd_name, bin_path, sizeof(bin_path));
1292 }
1293 }
1294
1295 if (path_len < 0) {
1296 progress_t progress;
1297 bool show_progress = !pkg_verbose;
1298
1299 if (show_progress) {
1300 char msg[256];
1301 snprintf(msg, sizeof(msg), "🔍 Resolving %s", cmd_name);
1302 progress_start(&progress, msg);
1303 }
1304
1305 pkg_options_t opts = {
1306 .progress_callback = show_progress ? progress_callback : NULL,
1307 .user_data = show_progress ? &progress : NULL,
1308 .verbose = pkg_verbose
1309 };
1310
1311 pkg_context_t *ctx = pkg_init(&opts);
1312 if (!ctx) {
1313 if (show_progress) progress_stop(&progress);
1314 fprintf(stderr, "Error: Failed to initialize package manager\n");
1315 return EXIT_FAILURE;
1316 }
1317
1318 pkg_error_t err = pkg_exec_temp(ctx, cmd_name, bin_path, sizeof(bin_path));
1319 if (show_progress) progress_stop(&progress);
1320
1321 if (err != PKG_OK) {
1322 const char *err_msg = pkg_error_string(ctx);
1323 if (err_msg && err_msg[0]) {
1324 fprintf(stderr, "Error: %s\n", err_msg);
1325 } else {
1326 fprintf(stderr, "Error: '%s' not found\n", cmd_name);
1327 }
1328 pkg_free(ctx);
1329 return EXIT_FAILURE;
1330 }
1331 pkg_free(ctx);
1332 }
1333
1334 int arg_offset = cmd_idx + 1;
1335 int extra = use_ant ? 2 : 1;
1336 int new_argc = argc - arg_offset + extra;
1337
1338 char **exec_argv = try_oom(sizeof(char*) * (new_argc + 1));
1339 if (!exec_argv) {
1340 fprintf(stderr, "Error: out of memory\n");
1341 return EXIT_FAILURE;
1342 }
1343
1344 int idx = 0;
1345 if (use_ant) exec_argv[idx++] = (char *)"ant";
1346 exec_argv[idx++] = bin_path;
1347
1348 for (int i = arg_offset; i < argc; i++) exec_argv[idx++] = argv[i];
1349 exec_argv[idx] = NULL;
1350
1351 const char *cmd = use_ant ? "ant" : bin_path;
1352 execvp(cmd, exec_argv);
1353 free(exec_argv);
1354
1355 if (use_ant) fprintf(stderr, "Error: failed to execute 'ant %s' - is ant installed?\n", bin_path);
1356 else fprintf(stderr, "Error: failed to execute '%s'\n", bin_path);
1357
1358 return EXIT_FAILURE;
1359}
1360
1361int pkg_cmd_why(int argc, char **argv) {
1362 struct arg_str *pkg = arg_str1(NULL, NULL, "<package>", "package name to query");
1363 struct arg_lit *help = arg_lit0("h", "help", "display help");
1364 struct arg_end *end = arg_end(5);
1365
1366 void *argtable[] = { pkg, help, end };
1367 int nerrors = arg_parse(argc, argv, argtable);
1368
1369 int exitcode = EXIT_SUCCESS;
1370 if (help->count > 0) {
1371 printf("Usage: ant why <package>\n\n");
1372 printf("Show which packages depend on the given package.\n");
1373 } else if (nerrors > 0) {
1374 arg_print_errors(stdout, end, "ant why");
1375 exitcode = EXIT_FAILURE;
1376 } else {
1377 exitcode = cmd_why(pkg->sval[0]);
1378 }
1379
1380 arg_freetable(argtable, sizeof(argtable)/sizeof(argtable[0]));
1381 return exitcode;
1382}
1383
1384static const char *format_size(uint64_t bytes, char *buf, size_t buf_size) {
1385 if (bytes >= 1024ULL * 1024 * 1024) snprintf(buf, buf_size, "%.2f GB", (double)bytes / (1024.0 * 1024.0 * 1024.0));
1386 else if (bytes >= 1024 * 1024) snprintf(buf, buf_size, "%.2f MB", (double)bytes / (1024.0 * 1024.0));
1387 else if (bytes >= 1024) snprintf(buf, buf_size, "%.2f KB", (double)bytes / 1024.0);
1388 else snprintf(buf, buf_size, "%llu B", (unsigned long long)bytes);
1389 return buf;
1390}
1391
1392static int cmd_info(const char *package_spec) {
1393 pkg_options_t opts = { .verbose = false };
1394 pkg_context_t *ctx = pkg_init(&opts);
1395 if (!ctx) {
1396 fprintf(stderr, "Error: Failed to initialize package manager\n");
1397 return EXIT_FAILURE;
1398 }
1399
1400 pkg_info_t info;
1401 pkg_error_t err = pkg_info(ctx, package_spec, &info);
1402 if (err != PKG_OK) {
1403 print_pkg_error(ctx);
1404 pkg_free(ctx);
1405 return EXIT_FAILURE;
1406 }
1407
1408 char size_buf[32];
1409
1410 printf("%s%s%s%s@%s%s%s%s%s", C_BLUE, C_UL, info.name, C_UL_OFF, C_BLUE, C_BOLD, C_UL, info.version, C_RESET);
1411 if (info.license[0]) printf(" | %s%s%s", C_CYAN, info.license, C_RESET);
1412 printf(" | deps: %u | versions: %u\n", info.dep_count, info.version_count);
1413
1414 if (info.description[0]) printf("%s\n", info.description);
1415 if (info.homepage[0]) printf("%s%s%s\n", C_BLUE, info.homepage, C_RESET);
1416 if (info.keywords[0]) printf("keywords: %s\n", info.keywords);
1417
1418 uint32_t dep_count = pkg_info_dependency_count(ctx);
1419 if (dep_count > 0) {
1420 printf("\n%sdependencies%s (%u):\n", C_BOLD, C_RESET, dep_count);
1421 for (uint32_t i = 0; i < dep_count; i++) {
1422 pkg_dependency_t dep;
1423 if (pkg_info_get_dependency(ctx, i, &dep) == PKG_OK) {
1424 printf("- %s%s%s: %s\n", C_CYAN, dep.name, C_RESET, dep.version);
1425 }
1426 }
1427 }
1428
1429 printf("\n%sdist%s\n", C_BOLD, C_RESET);
1430 if (info.tarball[0]) printf(" %s.tarball:%s %s\n", C_DIM, C_RESET, info.tarball);
1431 if (info.shasum[0]) printf(" %s.shasum:%s %s%s%s\n", C_DIM, C_RESET, C_GREEN, info.shasum, C_RESET);
1432 if (info.integrity[0]) printf(" %s.integrity:%s %s%s%s\n", C_DIM, C_RESET, C_GREEN, info.integrity, C_RESET);
1433 if (info.unpacked_size > 0) printf(" %s.unpackedSize:%s %s%s%s\n", C_DIM, C_RESET, C_BLUE, format_size(info.unpacked_size, size_buf, sizeof(size_buf)), C_RESET);
1434
1435 uint32_t tag_count = pkg_info_dist_tag_count(ctx);
1436 if (tag_count > 0) {
1437 printf("\n%sdist-tags:%s\n", C_BOLD, C_RESET);
1438 for (uint32_t i = 0; i < tag_count; i++) {
1439 pkg_dist_tag_t tag;
1440 if (pkg_info_get_dist_tag(ctx, i, &tag) == PKG_OK) {
1441 const char *tag_color = C_MAGENTA;
1442 if (strcmp(tag.tag, "beta") == 0) tag_color = C_BLUE;
1443 else if (strcmp(tag.tag, "latest") == 0) tag_color = C_CYAN;
1444 printf("%s%s%s: %s\n", tag_color, tag.tag, C_RESET, tag.version);
1445 }
1446 }
1447 }
1448
1449 uint32_t maint_count = pkg_info_maintainer_count(ctx);
1450 if (maint_count > 0) {
1451 printf("\n%smaintainers:%s\n", C_BOLD, C_RESET);
1452 for (uint32_t i = 0; i < maint_count; i++) {
1453 pkg_maintainer_t maint;
1454 if (pkg_info_get_maintainer(ctx, i, &maint) == PKG_OK) {
1455 printf("- %s", maint.name);
1456 if (maint.email[0]) printf(" <%s>", maint.email);
1457 fputc('\n', stdout);
1458 }
1459 }
1460 }
1461
1462 if (info.published[0]) printf("\n%sPublished:%s %s\n", C_BOLD, C_RESET, info.published);
1463
1464 pkg_free(ctx);
1465 return EXIT_SUCCESS;
1466}
1467
1468int pkg_cmd_info(int argc, char **argv) {
1469 struct arg_str *pkg = arg_str1(NULL, NULL, "<package[@version]>", "package to look up");
1470 struct arg_lit *help = arg_lit0("h", "help", "display help");
1471 struct arg_end *end = arg_end(5);
1472
1473 void *argtable[] = { pkg, help, end };
1474 int nerrors = arg_parse(argc, argv, argtable);
1475
1476 int exitcode = EXIT_SUCCESS;
1477 if (help->count > 0) {
1478 printf("Usage: ant info <package[@version]>\n\n");
1479 printf("Show package information from the npm registry.\n");
1480 } else if (nerrors > 0) {
1481 arg_print_errors(stdout, end, "ant info");
1482 exitcode = EXIT_FAILURE;
1483 } else {
1484 exitcode = cmd_info(pkg->sval[0]);
1485 }
1486
1487 arg_freetable(argtable, sizeof(argtable)/sizeof(argtable[0]));
1488 return exitcode;
1489}
1490
1491typedef struct {
1492 int count;
1493 bool show_path;
1494 const char *nm_path;
1495} ls_ctx_t;
1496
1497static void print_ls_package(const char *name, void *user_data) {
1498 ls_ctx_t *ctx = (ls_ctx_t *)user_data;
1499
1500 char pkg_json_path[4096];
1501 snprintf(pkg_json_path, sizeof(pkg_json_path), "%s/%s/package.json", ctx->nm_path, name);
1502
1503 FILE *f = fopen(pkg_json_path, "r");
1504 if (!f) {
1505 printf(" %s%s%s\n", C_BOLD, name, C_RESET);
1506 ctx->count++;
1507 return;
1508 }
1509
1510 char buf[8192];
1511 size_t len = fread(buf, 1, sizeof(buf) - 1, f);
1512 fclose(f);
1513 buf[len] = '\0';
1514
1515 const char *version = "?";
1516 char version_buf[64] = {0};
1517
1518 char *ver_key = strstr(buf, "\"version\"");
1519 if (ver_key) {
1520 char *colon = strchr(ver_key, ':');
1521 if (colon) {
1522 char *quote1 = strchr(colon, '"');
1523 if (quote1) {
1524 char *quote2 = strchr(quote1 + 1, '"');
1525 if (quote2) {
1526 size_t vlen = (size_t)(quote2 - quote1 - 1);
1527 if (vlen < sizeof(version_buf)) {
1528 memcpy(version_buf, quote1 + 1, vlen);
1529 version_buf[vlen] = '\0';
1530 version = version_buf;
1531 }
1532 }
1533 }
1534 }
1535 }
1536
1537 printf(" %s%s%s@%s%s%s\n", C_BOLD, name, C_RESET, C_DIM, version, C_RESET);
1538 ctx->count++;
1539}
1540
1541typedef struct {
1542 int count;
1543 int total;
1544} pkg_ls_ctx_t;
1545
1546static void print_pkg_cb(const char *name, const char *version, void *user_data) {
1547 pkg_ls_ctx_t *ctx = (pkg_ls_ctx_t *)user_data;
1548 ctx->count++;
1549 const char *prefix = (ctx->count == ctx->total) ? "└──" : "├──";
1550 printf("%s%s%s %s%s%s@%s\n", C_DIM, prefix, C_RESET, C_BOLD, name, C_RESET, version);
1551}
1552
1553static int cmd_ls(bool is_global) {
1554 pkg_options_t opts = { .verbose = pkg_verbose };
1555 pkg_context_t *ctx = pkg_init(&opts);
1556 if (!ctx) {
1557 fprintf(stderr, "Error: Failed to initialize package manager\n");
1558 return EXIT_FAILURE;
1559 }
1560
1561 char path_buf[PATH_MAX];
1562 const char *base_path;
1563 const char *nm_path;
1564 char nm_full_path[PATH_MAX];
1565
1566 if (is_global) {
1567 base_path = get_global_dir();
1568 snprintf(nm_full_path, sizeof(nm_full_path), "%s/node_modules", base_path);
1569 nm_path = nm_full_path;
1570 } else {
1571 if (!getcwd(path_buf, sizeof(path_buf))) {
1572 fprintf(stderr, "Error: Could not get current directory\n");
1573 pkg_free(ctx);
1574 return EXIT_FAILURE;
1575 }
1576 base_path = path_buf;
1577 nm_path = "node_modules";
1578 }
1579
1580 uint32_t direct = is_global ? pkg_count_global(ctx) : pkg_count_local(ctx);
1581 uint32_t total = pkg_count_installed(nm_path);
1582
1583 printf("%s%s/node_modules%s", C_DIM, base_path, C_RESET);
1584
1585 if (direct == 0) {
1586 printf("\n (no package.json)\n");
1587 pkg_free(ctx);
1588 return EXIT_SUCCESS;
1589 }
1590
1591 if (total == 0) {
1592 printf("\n (empty)\n");
1593 pkg_free(ctx);
1594 return EXIT_SUCCESS;
1595 }
1596
1597 printf(" %s(%u)%s\n", C_DIM, total, C_RESET);
1598
1599 pkg_ls_ctx_t ls_ctx = { .count = 0, .total = (int)direct };
1600 if (is_global) pkg_list_global(ctx, print_pkg_cb, &ls_ctx);
1601 else pkg_list_local(ctx, print_pkg_cb, &ls_ctx);
1602
1603 pkg_free(ctx);
1604 return EXIT_SUCCESS;
1605}
1606
1607int pkg_cmd_ls(int argc, char **argv) {
1608 struct arg_lit *global = arg_lit0("g", "global", "list global packages");
1609 struct arg_lit *help = arg_lit0("h", "help", "display help");
1610 struct arg_end *end = arg_end(5);
1611
1612 void *argtable[] = { global, help, end };
1613 int nerrors = arg_parse(argc, argv, argtable);
1614
1615 int exitcode = EXIT_SUCCESS;
1616 if (help->count > 0) {
1617 printf("Usage: ant ls [-g]\n\n");
1618 printf("List installed packages.\n");
1619 printf("\nOptions:\n -g, --global List global packages\n");
1620 } else if (nerrors > 0) {
1621 arg_print_errors(stdout, end, "ant ls");
1622 exitcode = EXIT_FAILURE;
1623 } else exitcode = cmd_ls(global->count > 0);
1624
1625 arg_freetable(argtable, sizeof(argtable)/sizeof(argtable[0]));
1626 return exitcode;
1627}
1628
1629static int cmd_cache_info(void) {
1630 pkg_options_t opts = { .verbose = pkg_verbose };
1631 pkg_context_t *ctx = pkg_init(&opts);
1632 if (!ctx) {
1633 fprintf(stderr, "Error: Failed to initialize package manager\n");
1634 return EXIT_FAILURE;
1635 }
1636
1637 pkg_cache_stats_t stats;
1638 pkg_error_t err = pkg_cache_stats(ctx, &stats);
1639 if (err != PKG_OK) {
1640 fprintf(stderr, "Error: Failed to get cache stats\n");
1641 pkg_free(ctx);
1642 return EXIT_FAILURE;
1643 }
1644
1645 char size_buf[64], db_buf[64];
1646 printf("%sCache location:%s ~/.ant/pkg\n", C_BOLD, C_RESET);
1647 printf("%sPackages:%s %u\n", C_BOLD, C_RESET, stats.package_count);
1648 printf("%sSize:%s %s\n", C_BOLD, C_RESET, format_size(stats.total_size, size_buf, sizeof(size_buf)));
1649 printf("%sDB size:%s %s\n", C_BOLD, C_RESET, format_size(stats.db_size, db_buf, sizeof(db_buf)));
1650
1651 pkg_free(ctx);
1652 return EXIT_SUCCESS;
1653}
1654
1655static int cmd_cache_prune(uint32_t max_age_days) {
1656 pkg_options_t opts = { .verbose = pkg_verbose };
1657 pkg_context_t *ctx = pkg_init(&opts);
1658 if (!ctx) {
1659 fprintf(stderr, "Error: Failed to initialize package manager\n");
1660 return EXIT_FAILURE;
1661 }
1662
1663 int32_t pruned = pkg_cache_prune(ctx, max_age_days);
1664 if (pruned < 0) {
1665 fprintf(stderr, "Error: Failed to prune cache\n");
1666 pkg_free(ctx);
1667 return EXIT_FAILURE;
1668 }
1669
1670 if (pruned == 0) {
1671 printf("No packages to prune (all packages newer than %u days)\n", max_age_days);
1672 } else {
1673 printf("%sPruned%s %d package%s older than %u days\n",
1674 C_GREEN, C_RESET, pruned, pruned == 1 ? "" : "s", max_age_days);
1675 }
1676
1677 pkg_free(ctx);
1678 return EXIT_SUCCESS;
1679}
1680
1681static int cmd_cache_sync(void) {
1682 pkg_options_t opts = { .verbose = pkg_verbose };
1683 pkg_context_t *ctx = pkg_init(&opts);
1684 if (!ctx) {
1685 fprintf(stderr, "Error: Failed to initialize package manager\n");
1686 return EXIT_FAILURE;
1687 }
1688
1689 pkg_cache_sync(ctx);
1690 printf("%sCache synced%s\n", C_GREEN, C_RESET);
1691
1692 pkg_free(ctx);
1693 return EXIT_SUCCESS;
1694}
1695
1696int pkg_cmd_cache(int argc, char **argv) {
1697 if (argc < 2) {
1698 printf("Usage: ant cache <command>\n\n");
1699 printf("Manage the package cache.\n\n");
1700 printf("Commands:\n");
1701 printf(" info Show cache statistics\n");
1702 printf(" prune [days] Remove packages older than N days (default: 30)\n");
1703 printf(" sync Sync cache to disk\n");
1704 return EXIT_SUCCESS;
1705 }
1706
1707 const char *subcmd = argv[1];
1708
1709 if (strcmp(subcmd, "info") == 0) {
1710 return cmd_cache_info();
1711 } else if (strcmp(subcmd, "prune") == 0) {
1712 uint32_t days = 30;
1713 if (argc >= 3) {
1714 days = (uint32_t)atoi(argv[2]);
1715 if (days == 0) days = 30;
1716 }
1717 return cmd_cache_prune(days);
1718 } else if (strcmp(subcmd, "sync") == 0) {
1719 return cmd_cache_sync();
1720 } else {
1721 fprintf(stderr, "Unknown cache command: %s\n", subcmd);
1722 fprintf(stderr, "Run 'ant cache' for usage.\n");
1723 return EXIT_FAILURE;
1724 }
1725}
1726
1727int pkg_cmd_create(int argc, char **argv) {
1728 if (argc < 2) {
1729 printf("Usage: ant create <template> [dest] [...flags]\n");
1730 printf(" ant create <github-org/repo> [dest] [...flags]\n\n");
1731 printf("Scaffold a new project from a template.\n\n");
1732 printf("Templates:\n");
1733 printf(" NPM: Runs 'ant x create-<template>' with given arguments\n");
1734 printf(" GitHub: Clones repository contents as template\n\n");
1735 printf("Environment variables:\n");
1736 printf(" GITHUB_TOKEN Supply a token for private repos or higher rate limits\n");
1737 return EXIT_SUCCESS;
1738 }
1739
1740 const char *template = argv[1];
1741 bool is_github = (strchr(template, '/') != NULL);
1742
1743 if (is_github) {
1744 const char *dest = NULL;
1745
1746 for (int i = 2; i < argc; i++) {
1747 if (argv[i][0] != '-') { dest = argv[i]; break; }
1748 }
1749
1750 if (!dest) {
1751 const char *slash = strrchr(template, '/');
1752 dest = slash ? slash + 1 : template;
1753 }
1754
1755 struct stat st;
1756 if (stat(dest, &st) == 0) {
1757 fprintf(stderr, "Error: directory '%s' already exists\n", dest);
1758 return EXIT_FAILURE;
1759 }
1760
1761 char url[1024];
1762 if (strncmp(template, "https://", 8) == 0 || strncmp(template, "git@", 4) == 0) {
1763 snprintf(url, sizeof(url), "%s", template);
1764 } else snprintf(url, sizeof(url), "https://github.com/%s.git", template);
1765
1766 printf("%s+%s Creating project from %s%s%s...\n", C_GREEN, C_RESET, C_BOLD, template, C_RESET);
1767
1768 char cmd[2048];
1769 snprintf(cmd, sizeof(cmd), "git clone --depth 1 %s %s", url, dest);
1770 int ret = system(cmd);
1771 if (ret != 0) {
1772 fprintf(stderr, "Error: failed to clone %s\n", url);
1773 return EXIT_FAILURE;
1774 }
1775
1776 char git_dir[1024];
1777 snprintf(git_dir, sizeof(git_dir), "%s/.git", dest);
1778
1779 char rm_cmd[1024];
1780 snprintf(rm_cmd, sizeof(rm_cmd), "rm -rf %s", git_dir);
1781 system(rm_cmd);
1782
1783 if (stat(dest, &st) == 0) {
1784 char pkg_json[1024];
1785 snprintf(pkg_json, sizeof(pkg_json), "%s/package.json", dest);
1786 if (stat(pkg_json, &st) == 0) {
1787 printf("\n%sDone!%s Created %s%s%s\n", C_GREEN, C_RESET, C_BOLD, dest, C_RESET);
1788 printf("\n cd %s\n ant install\n\n", dest);
1789 } else printf("\n%sDone!%s Created %s%s%s\n", C_GREEN, C_RESET, C_BOLD, dest, C_RESET);
1790 }
1791
1792 return EXIT_SUCCESS;
1793 }
1794
1795 char create_pkg[512];
1796 snprintf(create_pkg, sizeof(create_pkg), "create-%s", template);
1797
1798 int new_argc = argc;
1799 char **new_argv = malloc(sizeof(char*) * (new_argc + 1));
1800 if (!new_argv) {
1801 fprintf(stderr, "Error: out of memory\n");
1802 return EXIT_FAILURE;
1803 }
1804
1805 new_argv[0] = argv[0];
1806 new_argv[1] = create_pkg;
1807
1808 for (int i = 2; i < argc; i++) new_argv[i] = argv[i];
1809 new_argv[new_argc] = NULL;
1810
1811 int ret = pkg_cmd_exec(new_argc, new_argv);
1812 free(new_argv);
1813
1814 return ret;
1815}