镜像自地址
https://github.com/JGZYES/ParlzPackageManger.git
已同步 2026-09-11 08:52:45 +08:00
124 行
4.4 KiB
C
124 行
4.4 KiB
C
/* mirrors.c - apt-style mirror handling
|
|
*
|
|
* mirror.ini / mirror.conf format:
|
|
* [name]
|
|
* api = https://mirror.example.com # replaces git-host API base
|
|
* download = https://ghfast.example # gh-proxy style download prefix
|
|
* registry = https://mirror.example/pmm # registry mirror ({base}/{pkg}.json)
|
|
* priority = 1 # lower tried first (apt-like order)
|
|
*/
|
|
#include "mirrors.h"
|
|
#include "ini.h"
|
|
#include "pmm.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
static char *xstrdup_or(const char *s, const char *dflt) {
|
|
return strdup(s ? s : dflt);
|
|
}
|
|
|
|
static int cmp_priority(const void *a, const void *b) {
|
|
const Mirror *ma = a, *mb = b;
|
|
return ma->priority - mb->priority;
|
|
}
|
|
|
|
/* Write a default mirror.ini (built-in sz + main mirror) when no mirror
|
|
* config exists, so a fresh install works out-of-the-box from both mirrors. */
|
|
void pmm_ensure_default_mirror(const char *dir) {
|
|
if (pmm_find_config(dir, "mirror")) return; /* user already has a config */
|
|
char path[1200];
|
|
snprintf(path, sizeof(path), "%s/mirror.ini", dir);
|
|
FILE *f = fopen(path, "w");
|
|
if (!f) return;
|
|
fputs("# PMM default mirror.ini (auto-generated)\n"
|
|
"# priority 越小越优先;default=true 为未显式指定时的默认镜像。\n"
|
|
"#\n"
|
|
"[sz]\n"
|
|
"registry = https://sz.pmm.parlz.com/mirror/packages\n"
|
|
"priority = 1\n"
|
|
"default = true\n"
|
|
"\n"
|
|
"[main]\n"
|
|
"registry = https://pmm.parlz.com/mirror/packages\n"
|
|
"priority = 20\n", f);
|
|
fclose(f);
|
|
}
|
|
|
|
MirrorList *mirrors_load(void) {
|
|
char dir[1024];
|
|
pmm_config_dir(dir, sizeof(dir));
|
|
pmm_ensure_default_mirror(dir);
|
|
char *path = pmm_find_config(dir, "mirror");
|
|
if (!path) return calloc(1, sizeof(MirrorList));
|
|
Ini *ini = ini_load(path);
|
|
MirrorList *list = calloc(1, sizeof(MirrorList));
|
|
if (!ini) return list;
|
|
|
|
int cap = 0;
|
|
char cursec[512] = "";
|
|
for (IniEntry *e = ini->head; e; e = e->next) {
|
|
if (strcmp(e->section, cursec) != 0) {
|
|
strncpy(cursec, e->section, sizeof(cursec) - 1);
|
|
/* new mirror section begins */
|
|
if (list->count >= cap) {
|
|
cap = cap ? cap * 2 : 8;
|
|
list->items = realloc(list->items, cap * sizeof(Mirror));
|
|
}
|
|
Mirror *m = &list->items[list->count++];
|
|
memset(m, 0, sizeof(*m));
|
|
m->name = xstrdup_or(e->section, "");
|
|
m->priority = 100;
|
|
}
|
|
Mirror *m = list->count ? &list->items[list->count - 1] : NULL;
|
|
if (!m) continue;
|
|
if (strcmp(e->key, "api") == 0) m->api = xstrdup_or(e->value, "");
|
|
else if (strcmp(e->key, "download") == 0) m->download = xstrdup_or(e->value, "");
|
|
else if (strcmp(e->key, "registry") == 0) m->registry = xstrdup_or(e->value, "");
|
|
else if (strcmp(e->key, "priority") == 0) m->priority = atoi(e->value);
|
|
}
|
|
ini_free(ini);
|
|
qsort(list->items, list->count, sizeof(Mirror), cmp_priority);
|
|
return list;
|
|
}
|
|
|
|
void mirrors_free(MirrorList *list) {
|
|
if (!list) return;
|
|
for (int i = 0; i < list->count; i++) {
|
|
free(list->items[i].name);
|
|
free(list->items[i].api);
|
|
free(list->items[i].download);
|
|
free(list->items[i].registry);
|
|
}
|
|
free(list->items);
|
|
free(list);
|
|
}
|
|
|
|
Mirror *mirrors_active(const MirrorList *list, const char *active_name) {
|
|
if (!active_name || !*active_name) return NULL;
|
|
for (int i = 0; i < list->count; i++)
|
|
if (strcmp(list->items[i].name, active_name) == 0)
|
|
return &list->items[i];
|
|
return NULL;
|
|
}
|
|
|
|
char **mirrors_download_candidates(const MirrorList *list, const char *url, int *out_n) {
|
|
int cap = list->count + 1, n = 0;
|
|
char **cands = malloc(cap * sizeof(char *));
|
|
for (int i = 0; i < list->count && n < cap - 1; i++) {
|
|
const char *dl = list->items[i].download;
|
|
if (!dl || !*dl) continue;
|
|
size_t len = strlen(dl) + strlen(url) + 2;
|
|
cands[n] = malloc(len);
|
|
/* gh-proxy convention: prefix + full original URL */
|
|
if (dl[strlen(dl) - 1] == '=' || dl[strlen(dl) - 1] == '/')
|
|
snprintf(cands[n], len, "%s%s", dl, url);
|
|
else
|
|
snprintf(cands[n], len, "%s/%s", dl, url);
|
|
n++;
|
|
}
|
|
cands[n++] = strdup(url); /* direct last, like apt falling back to origin */
|
|
*out_n = n;
|
|
return cands;
|
|
}
|