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