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

Configure Feed

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

at mir/inline-method 1815 lines 56 kB view raw
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}