diff --git a/src/main.c b/src/main.c index 4b482c8..b38043f 100644 --- a/src/main.c +++ b/src/main.c @@ -16,6 +16,9 @@ * Mirrors: ~/.pmm/mirror.ini | mirror.conf */ #include +#ifdef _WIN32 +#include +#endif #include #include @@ -320,6 +323,10 @@ static int consume_drive_flag(int argc, char **argv) { r++; /* consume the drive arg too */ continue; } + /* -p : install under this full address */ + pmm_set_install_path(nxt); + r++; + continue; } argv[w++] = argv[r]; } @@ -327,6 +334,10 @@ static int consume_drive_flag(int argc, char **argv) { } int main(int argc, char **argv) { +#ifdef _WIN32 + SetConsoleOutputCP(CP_UTF8); /* UTF-8 output so Chinese help/version isn't mojibake */ + setvbuf(stdout, NULL, _IONBF, 0); +#endif if (argc < 2) { print_help(); return 0; } pmm_set_self_path(argv[0]); argc = consume_drive_flag(argc, argv); diff --git a/src/pdm.c b/src/pdm.c index a3af524..9ea0114 100644 --- a/src/pdm.c +++ b/src/pdm.c @@ -202,7 +202,8 @@ static void db_dirs(char *db, size_t dbs, char *root, size_t roots) { pmm_config_dir(home, sizeof(home) - 32); snprintf(db, dbs, "%s/installed", home); mkdir_p(db); - snprintf(root, roots, "%s/root", home); + if (pmm_flat_mode()) snprintf(root, roots, "%s", home); /* -p */ + else snprintf(root, roots, "%s/root", home); mkdir_p(root); } @@ -313,6 +314,44 @@ static void chdir_restore(void) { if (saved_cwd[0]) CHDIR(saved_cwd); } +/* Move everything under /bin up into , then remove bin/. (C-level, so + * it works on Windows cmd too where `mv` doesn't exist.) */ +static void flatten_bin(const char *dir) { + char bindir[1200]; + snprintf(bindir, sizeof(bindir), "%s/%s", dir, "bin"); +#ifdef _WIN32 + char patt[1300]; + snprintf(patt, sizeof(patt), "%s\\*", bindir); + struct _finddata_t fd; + intptr_t h = _findfirst(patt, &fd); + if (h != -1) { + do { + if (strcmp(fd.name, ".") == 0 || strcmp(fd.name, "..") == 0) continue; + char src[1400], dst[1400]; + snprintf(src, sizeof(src), "%s\\%s", bindir, fd.name); + snprintf(dst, sizeof(dst), "%s\\%s", dir, fd.name); + rename(src, dst); + } while (_findnext(h, &fd) == 0); + _findclose(h); + } + RMDIR(bindir); +#else + DIR *d = opendir(bindir); + if (d) { + struct dirent *e; + while ((e = readdir(d)) != NULL) { + if (e->d_name[0] == '.') continue; + char src[1400], dst[1400]; + snprintf(src, sizeof(src), "%s/%s", bindir, e->d_name); + snprintf(dst, sizeof(dst), "%s/%s", dir, e->d_name); + rename(src, dst); + } + closedir(d); + rmdir(bindir); + } +#endif +} + int pdm_info(const char *pdmfile) { printf("pdm: %s\n", base_name(pdmfile)); /* Copy into the pm dir + chdir, then use relative names (works with both @@ -338,9 +377,13 @@ int pdm_install_file(const char *pdmfile) { } char home[1024], db[1024], root[1024], cmd[2600]; pmm_config_dir(home, sizeof(home)); + int flat = pmm_flat_mode(); /* -p : deploy directly into that dir */ snprintf(db, sizeof(db), "%s/installed", home); mkdir_p(db); - snprintf(root, sizeof(root), "%s/root", home); + if (flat) + snprintf(root, sizeof(root), "%s", home); + else + snprintf(root, sizeof(root), "%s/root", home); mkdir_p(root); /* Copy the package into the PM dir, chdir there, and run tar with ONLY @@ -404,13 +447,19 @@ int pdm_install_file(const char *pdmfile) { chdir_restore(); remove(tmpname); return -1; } - /* extract data into root */ - snprintf(cmd, sizeof(cmd), "tar -xzf \"%s/data.tar.gz\" -C \"root\"", stage); + /* extract data into root (relative target: cwd is already the pm dir) */ + const char *tgt = flat ? "." : "root"; + snprintf(cmd, sizeof(cmd), "tar -xzf \"%s/data.tar.gz\" -C \"%s\"", stage, tgt); if (system(cmd) != 0) { fprintf(stderr, "pdm: data extraction failed\n"); chdir_restore(); remove(tmpname); return -1; } + /* flat mode (-p ): move bin/* up into the address itself, drop bin/ */ + if (flat) { + flatten_bin("."); /* cwd = the install address in flat mode */ + } + /* record file list + control in db */ snprintf(cmd, sizeof(cmd), "%s/%s.info", db, pkg); FILE *dbf = fopen(cmd, "wb"); /* binary: keep \n, avoid Windows CRLF translation */ @@ -423,8 +472,14 @@ int pdm_install_file(const char *pdmfile) { FILE *tf = PMM_POPEN_READ(ltcmd); if (tf) { char ln[2048]; - while (fgets(ln, sizeof(ln), tf)) + while (fgets(ln, sizeof(ln), tf)) { + if (flat) { /* reflection of the flatten: strip a leading bin/ */ + const char *q = ln; + while (*q==' '||*q=='\t') q++; + if (q[0]=='.'&&q[1]=='/'&&strncasecmp(q+2,"bin/",4)==0) { ln[0]='\0'; continue; } + } fputs(ln, dbf); + } PMM_PCLOSE_READ(tf); } else { fprintf(stderr, "pdm: warning: could not record file list\n"); diff --git a/src/pmm.c b/src/pmm.c index 226e668..b216fe8 100644 --- a/src/pmm.c +++ b/src/pmm.c @@ -98,10 +98,11 @@ static const char *home_dir(void) { return (h && *h) ? h : "."; } -/* ---- install-drive override (e.g. "pmm install x -pd" -> D:\.pmm) ---- - * The chosen drive is persisted in the always-reachable home dir so later - * commands remember it without needing the flag again. */ -static char g_drive[4] = ""; /* "D", or "" = use home */ +/* ---- install-location overrides ---- + * Either a drive letter ("D" -> D:\.pmm) via -p, or an exact path + * ("D:\apps\pmm" / "/opt/pmm") via -p . The path override wins. */ +static char g_drive[4] = ""; /* "D", or "" = use home */ +static char g_base_path[1024] = ""; /* exact install base, or "" */ static const char *drive_state_path(char *buf, size_t size) { snprintf(buf, size, "%s/.pmm/install-drive", home_dir()); @@ -114,7 +115,6 @@ void pmm_set_install_drive(const char *letter) { if (c < 'A' || c > 'Z') return; /* only a single A-Z drive letter */ g_drive[0] = c; g_drive[1] = '\0'; - /* persist so it becomes the default for later runs */ char state[1024]; drive_state_path(state, sizeof(state)); char dir[1024]; @@ -125,6 +125,17 @@ void pmm_set_install_drive(const char *letter) { printf("pmm: install drive set to %c:\n", c); } +void pmm_set_install_path(const char *path) { + if (!path || !*path) { g_base_path[0] = '\0'; return; } + snprintf(g_base_path, sizeof(g_base_path), "%s", path); + /* strip trailing slashes */ + size_t l = strlen(g_base_path); + while (l > 1 && (g_base_path[l-1] == '/' || g_base_path[l-1] == '\\')) g_base_path[--l] = '\0'; + mkdir_p(g_base_path); + printf("pmm: install path set to %s\n", g_base_path); +} +int pmm_flat_mode(void) { return g_base_path[0] ? 1 : 0; } + /* Resolve the .pmm base dir: explicit drive override > persisted drive > * home/.pmm (when that drive is picked) > DEFAULT = D:\.pmm. * Defaulting to D:\ spares the C: drive (user's primary diskspace concern); @@ -137,6 +148,10 @@ static char home_drive_letter(void) { } static const char *pmm_base_dir(char *buf, size_t size) { + if (g_base_path[0]) { /* -p */ + snprintf(buf, size, "%s", g_base_path); + return buf; + } char drive[4] = ""; if (g_drive[0]) { drive[0] = g_drive[0]; @@ -269,7 +284,8 @@ void pmm_add_to_path(void) { char home[1024]; pmm_config_dir(home, sizeof(home)); char base[1024]; - snprintf(base, sizeof(base), "%s/root", home); + if (pmm_flat_mode()) snprintf(base, sizeof(base), "%s", home); /* -p */ + else snprintf(base, sizeof(base), "%s/root", home); /* collect dirs: ~/.pmm/bin, ~/.pmm/root, and root subdirs holding executables */ char list[160][1200]; diff --git a/src/pmm.h b/src/pmm.h index 2659dd7..e322582 100644 --- a/src/pmm.h +++ b/src/pmm.h @@ -17,6 +17,10 @@ void pmm_set_install_drive(const char *letter); * "/opt/pmm"). Config/cache/bin/root all live under it. Empty clears back. */ void pmm_set_install_path(const char *path); +/* 1 if an exact install path was given via -p : packages deploy directly + * into that directory (files flattened), without a root/bin sub-folder. */ +int pmm_flat_mode(void); + /* File-association record written to the per-user registry on install. */ typedef struct { const char *progid; /* e.g. "Node.JSFile" */