feat(pkg): version_code 安卓式版本规则落地 (agent-190, 用户定案 APPS §3.8) — app.json 增 version_code(纯数字唯一比较依据), version 仅显示; nyai install/upgrade/list-upgradable/index 全走 code 比较(缺 code 拒装); launcher meow_pkg_install 同步门禁; 旧点分比较函数移除
nyai f2938d28→4cf5f690; launcher 24c9ee5d→ab8e610f; 宿主 9 场景全过(升/同版/降级拒/缺code拒/index/upgrade一致) 出入: 全新安装缺 code 放行(门禁仅 replacing 分支); export 保留签名的版本比对仍用显示版字符串(nyai+launcher 一致,待后续统一)
This commit is contained in:
1 parent
555c4a458e
commit
60072a08f1
5 files changed
+75
-76
No files matched your search
@@ -620,6 +620,26 @@ int meow_pkg_install(const char *mpk_path)
|
|||||||
logmsg("pkg_install: meow_verify unavailable rc=%d, %s unverified (3rd party)", vr, pkg);
|
logmsg("pkg_install: meow_verify unavailable rc=%d, %s unverified (3rd party)", vr, pkg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 防降级门禁(与 nyai 同规范,APPS.md §3.8):仅按 version_code 比较。
|
||||||
|
* 新版 code < 已装 → 拒;相等 → 允许重装;任一侧缺 version_code → 拒。 */
|
||||||
|
if (replacing) {
|
||||||
|
char nc[32], oc[32];
|
||||||
|
long long ncode = -1, ocode = -1;
|
||||||
|
int nok = 0, ok2 = 0;
|
||||||
|
if (pkg_json_field_mpk(mpk_path, "version_code", nc, sizeof nc) == 0) {
|
||||||
|
char *ep; long long v = strtoll(nc, &ep, 10);
|
||||||
|
if (ep != nc && *ep == '\0') { ncode = v; nok = 1; }
|
||||||
|
}
|
||||||
|
char instj[280];
|
||||||
|
snprintf(instj, sizeof instj, "/usr/data/mnt/sd_0/MeowAPPs/%s/app.json", pkg);
|
||||||
|
if (pkg_json_field(instj, "version_code", oc, sizeof oc) == 0) {
|
||||||
|
char *ep; long long v = strtoll(oc, &ep, 10);
|
||||||
|
if (ep != oc && *ep == '\0') { ocode = v; ok2 = 1; }
|
||||||
|
}
|
||||||
|
if (!nok || !ok2) { logmsg("pkg_install: %s version_code missing, reject", pkg); return -4; }
|
||||||
|
if (ncode < ocode) { logmsg("pkg_install: downgrade %s new %lld < old %lld, reject", pkg, ncode, ocode); return -4; }
|
||||||
|
}
|
||||||
|
|
||||||
/* BusyBox tar 无 -z:用 gzip -dc | tar -x 处理 tar.gz。 */
|
/* BusyBox tar 无 -z:用 gzip -dc | tar -x 处理 tar.gz。 */
|
||||||
snprintf(cmd, sizeof cmd,
|
snprintf(cmd, sizeof cmd,
|
||||||
"rm -rf '%s' 2>/dev/null; "
|
"rm -rf '%s' 2>/dev/null; "
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
+55
-76
@@ -5,7 +5,7 @@
|
|||||||
* APPS.md 附录 v2)。支持镜像源:
|
* APPS.md 附录 v2)。支持镜像源:
|
||||||
* - 源配置 MeowOS/opt/nyai/sources.list(每行一个源;local <路径> / http <url> /
|
* - 源配置 MeowOS/opt/nyai/sources.list(每行一个源;local <路径> / http <url> /
|
||||||
* deb [trusted=yes] <uri> / file:// ; # 注释)。默认 local MeowOS/opt/repo。
|
* deb [trusted=yes] <uri> / file:// ; # 注释)。默认 local MeowOS/opt/repo。
|
||||||
* - 每源一个 Packages 索引(行式 Package/Version/Filename/Size/SHA256/Depends/Description),
|
* - 每源一个 Packages 索引(行式 Package/Version/Version-Code/Filename/Size/SHA256/Depends/Description),
|
||||||
* 由 `nyai index <目录>` 生成。
|
* 由 `nyai index <目录>` 生成。
|
||||||
* - update 缓存索引到 MeowOS/opt/nyai/cache;install 按名称从索引取包(本地文件或 http 拉取),
|
* - update 缓存索引到 MeowOS/opt/nyai/cache;install 按名称从索引取包(本地文件或 http 拉取),
|
||||||
* 处理 Depends(递归)、校验 SHA256 + app.json;同 name 先备份;解压 MeowAPPs/<pkg>/;
|
* 处理 Depends(递归)、校验 SHA256 + app.json;同 name 先备份;解压 MeowAPPs/<pkg>/;
|
||||||
@@ -58,6 +58,8 @@ typedef struct {
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
char pkg[128];
|
char pkg[128];
|
||||||
char version[64];
|
char version[64];
|
||||||
|
long long version_code; /* 索引 Version-Code(正整数;has_=0 表示索引缺该项) */
|
||||||
|
int has_version_code;
|
||||||
char filename[256];
|
char filename[256];
|
||||||
char sha256[128];
|
char sha256[128];
|
||||||
char depends[256];
|
char depends[256];
|
||||||
@@ -154,67 +156,6 @@ static void human_size(long long b, char *out, int n)
|
|||||||
else snprintf(out,n,"%.2f MB", b/(1024.0*1024));
|
else snprintf(out,n,"%.2f MB", b/(1024.0*1024));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 简版版本比较(点/横分段,数字段按数值):a>b 返回 1,=0,< -1 */
|
|
||||||
static int version_cmp(const char *a, const char *b)
|
|
||||||
{
|
|
||||||
const char *x=a,*y=b;
|
|
||||||
while (*x || *y) {
|
|
||||||
while (*x=='.'||*x=='-') x++;
|
|
||||||
while (*y=='.'||*y=='-') y++;
|
|
||||||
if (!*x || !*y) break;
|
|
||||||
if (isdigit((unsigned char)*x) && isdigit((unsigned char)*y)) {
|
|
||||||
long nx=0, ny=0;
|
|
||||||
while (isdigit((unsigned char)*x)) nx=nx*10+(*x++ - '0');
|
|
||||||
while (isdigit((unsigned char)*y)) ny=ny*10+(*y++ - '0');
|
|
||||||
if (nx!=ny) return nx<ny?-1:1;
|
|
||||||
} else {
|
|
||||||
if (*x!=*y) return *x<*y?-1:1;
|
|
||||||
x++; y++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!*x && !*y) return 0;
|
|
||||||
return *x ? 1 : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 点分数字版本:APPS.md 附录 §3「更新规范」升级门禁专用(install_mpk_file)。
|
|
||||||
* 规则:以 '.' 或 '-' 分段,每段必须为纯数字;空串/含非数字段 = 不可解析(返回负数)。
|
|
||||||
* 段数不齐时短者补 0(如 1.2 == 1.2.0)。 */
|
|
||||||
#define MAXVSEG 16
|
|
||||||
static int version_parse(const char *v, unsigned long *segs, int max)
|
|
||||||
{
|
|
||||||
if (!v || !*v) return -1;
|
|
||||||
int n = 0;
|
|
||||||
const char *p = v;
|
|
||||||
while (*p) {
|
|
||||||
while (*p=='.'||*p=='-') p++;
|
|
||||||
if (!*p) break;
|
|
||||||
const char *start = p;
|
|
||||||
while (*p && isdigit((unsigned char)*p)) p++;
|
|
||||||
if (p == start) return -1; /* 空段或非数字段 */
|
|
||||||
unsigned long x = 0;
|
|
||||||
for (const char *q=start; q<p; q++) { x = x*10 + (*q-'0'); if (x > 0x7fffffffUL) return -1; }
|
|
||||||
if (n < max) segs[n++] = x;
|
|
||||||
}
|
|
||||||
return n ? n : -1;
|
|
||||||
}
|
|
||||||
static int version_ok(const char *v)
|
|
||||||
{ unsigned long s[MAXVSEG]; return version_parse(v, s, MAXVSEG) >= 0; }
|
|
||||||
/* 返回:a>b=1, a==b=0, a<b=-1;任一不可解析返回 INT_MIN(不可比) */
|
|
||||||
static int version_num_cmp(const char *a, const char *b)
|
|
||||||
{
|
|
||||||
unsigned long na[MAXVSEG], nb[MAXVSEG];
|
|
||||||
int ca = version_parse(a, na, MAXVSEG);
|
|
||||||
int cb = version_parse(b, nb, MAXVSEG);
|
|
||||||
if (ca < 0 || cb < 0) return INT_MIN;
|
|
||||||
int n = ca > cb ? ca : cb;
|
|
||||||
for (int i = 0; i < n; i++) {
|
|
||||||
unsigned long xa = i < ca ? na[i] : 0UL; /* 段数不齐,短者补 0 */
|
|
||||||
unsigned long xb = i < cb ? nb[i] : 0UL;
|
|
||||||
if (xa != xb) return xa < xb ? -1 : 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---------------- JSON 读取 ---------------- */
|
/* ---------------- JSON 读取 ---------------- */
|
||||||
static int json_str(const char *j, const char *key, char *out, int n)
|
static int json_str(const char *j, const char *key, char *out, int n)
|
||||||
{
|
{
|
||||||
@@ -278,10 +219,38 @@ static int json_raw(const char *j, const char *key, char *out, int n)
|
|||||||
out[o]=0;
|
out[o]=0;
|
||||||
return out[0] ? 0 : -1;
|
return out[0] ? 0 : -1;
|
||||||
}
|
}
|
||||||
|
/* 读 JSON 整型字段(键带引号、值必须为纯数字,如 "version_code": 12)。
|
||||||
|
* 缺键 / 值空 / 非数字 / 溢出 → 返回 -1;成功返回 0 并写入 *out。 */
|
||||||
|
static int json_int(const char *j, const char *key, long long *out)
|
||||||
|
{
|
||||||
|
if (!j || !key || !out) return -1;
|
||||||
|
char pat[160];
|
||||||
|
snprintf(pat, sizeof pat, "\"%s\"", key);
|
||||||
|
const char *p = j;
|
||||||
|
while ((p = strstr(p, pat)) != NULL) {
|
||||||
|
p += strlen(pat);
|
||||||
|
while (*p==' '||*p=='\t'||*p=='\n'||*p=='\r') p++;
|
||||||
|
if (*p != ':') { p++; continue; }
|
||||||
|
p++;
|
||||||
|
while (*p==' '||*p=='\t'||*p=='\n'||*p=='\r') p++;
|
||||||
|
if (!isdigit((unsigned char)*p)) return -1; /* 缺值 / 字符串 / 非数字 */
|
||||||
|
long long v = 0;
|
||||||
|
while (isdigit((unsigned char)*p)) {
|
||||||
|
int d = *p++ - '0';
|
||||||
|
if (v > (LLONG_MAX - d) / 10) return -1; /* 溢出 */
|
||||||
|
v = v * 10 + d;
|
||||||
|
}
|
||||||
|
*out = v;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char name[256];
|
char name[256];
|
||||||
char version[64];
|
char version[64]; /* 仅作显示字符串(versionName 角色,不作比较) */
|
||||||
|
long long version_code; /* app.json "version_code"(正整数,安装/升级比较唯一依据;缺时 has_=0) */
|
||||||
|
int has_version_code; /* app.json 是否显式声明 version_code */
|
||||||
char exec[512];
|
char exec[512];
|
||||||
char package[64]; /* app.json "package"(权威包名,== 文件名茎;II 期绑定校验用) */
|
char package[64]; /* app.json "package"(权威包名,== 文件名茎;II 期绑定校验用) */
|
||||||
char author[128];
|
char author[128];
|
||||||
@@ -309,6 +278,7 @@ static int load_app_meta(const char *path, appmeta *m)
|
|||||||
if (json_str(j,"exec",m->exec,sizeof m->exec)!=0 || !m->exec[0]) return -1;
|
if (json_str(j,"exec",m->exec,sizeof m->exec)!=0 || !m->exec[0]) return -1;
|
||||||
if (json_str(j,"package",m->package,sizeof m->package)!=0) m->package[0]=0;
|
if (json_str(j,"package",m->package,sizeof m->package)!=0) m->package[0]=0;
|
||||||
if (json_str(j,"version",m->version,sizeof m->version)!=0) m->version[0]=0;
|
if (json_str(j,"version",m->version,sizeof m->version)!=0) m->version[0]=0;
|
||||||
|
m->has_version_code = (json_int(j,"version_code",&m->version_code)==0);
|
||||||
if (json_str(j,"author",m->author,sizeof m->author)!=0) m->author[0]=0;
|
if (json_str(j,"author",m->author,sizeof m->author)!=0) m->author[0]=0;
|
||||||
if (json_str(j,"depends",m->depends,sizeof m->depends)!=0) m->depends[0]=0;
|
if (json_str(j,"depends",m->depends,sizeof m->depends)!=0) m->depends[0]=0;
|
||||||
if (json_str(j,"description",m->description,sizeof m->description)!=0) m->description[0]=0;
|
if (json_str(j,"description",m->description,sizeof m->description)!=0) m->description[0]=0;
|
||||||
@@ -344,6 +314,7 @@ static int mpk_appinfo(const char *file, appmeta *m)
|
|||||||
j[r]=0;
|
j[r]=0;
|
||||||
if (json_str(j,"name",m->name,sizeof m->name)!=0 || !m->name[0]) return -1;
|
if (json_str(j,"name",m->name,sizeof m->name)!=0 || !m->name[0]) return -1;
|
||||||
if (json_str(j,"version",m->version,sizeof m->version)!=0) m->version[0]=0;
|
if (json_str(j,"version",m->version,sizeof m->version)!=0) m->version[0]=0;
|
||||||
|
m->has_version_code = (json_int(j,"version_code",&m->version_code)==0);
|
||||||
if (json_str(j,"depends",m->depends,sizeof m->depends)!=0) m->depends[0]=0;
|
if (json_str(j,"depends",m->depends,sizeof m->depends)!=0) m->depends[0]=0;
|
||||||
if (json_str(j,"description",m->description,sizeof m->description)!=0) m->description[0]=0;
|
if (json_str(j,"description",m->description,sizeof m->description)!=0) m->description[0]=0;
|
||||||
return 0;
|
return 0;
|
||||||
@@ -490,6 +461,7 @@ static int parse_index(const char *path, pkgrec *recs, int max)
|
|||||||
char *key = trim(t), *val = trim(colon+1);
|
char *key = trim(t), *val = trim(colon+1);
|
||||||
if (!strcmp(key,"Package")) { snprintf(cur.pkg,sizeof cur.pkg,"%s",val); have = (cur.pkg[0]!=0); }
|
if (!strcmp(key,"Package")) { snprintf(cur.pkg,sizeof cur.pkg,"%s",val); have = (cur.pkg[0]!=0); }
|
||||||
else if (!strcmp(key,"Version")) snprintf(cur.version,sizeof cur.version,"%s",val);
|
else if (!strcmp(key,"Version")) snprintf(cur.version,sizeof cur.version,"%s",val);
|
||||||
|
else if (!strcmp(key,"Version-Code")) { cur.version_code = atoll(val); cur.has_version_code = (val[0]!=0); }
|
||||||
else if (!strcmp(key,"Filename")) snprintf(cur.filename,sizeof cur.filename,"%s",val);
|
else if (!strcmp(key,"Filename")) snprintf(cur.filename,sizeof cur.filename,"%s",val);
|
||||||
else if (!strcmp(key,"SHA256")) snprintf(cur.sha256,sizeof cur.sha256,"%s",val);
|
else if (!strcmp(key,"SHA256")) snprintf(cur.sha256,sizeof cur.sha256,"%s",val);
|
||||||
else if (!strcmp(key,"Size")) { cur.size = atoll(val); }
|
else if (!strcmp(key,"Size")) { cur.size = atoll(val); }
|
||||||
@@ -684,6 +656,7 @@ static int cmd_index(const char *dir)
|
|||||||
char sha[128]=""; sha256_file(fpath, sha, sizeof sha);
|
char sha[128]=""; sha256_file(fpath, sha, sizeof sha);
|
||||||
fprintf(f,"Package: %s\n", stem);
|
fprintf(f,"Package: %s\n", stem);
|
||||||
fprintf(f,"Version: %s\n", ver[0]?ver:"0.0.0");
|
fprintf(f,"Version: %s\n", ver[0]?ver:"0.0.0");
|
||||||
|
if (m.has_version_code) fprintf(f,"Version-Code: %lld\n", m.version_code);
|
||||||
fprintf(f,"Filename: %s\n", de->d_name);
|
fprintf(f,"Filename: %s\n", de->d_name);
|
||||||
fprintf(f,"Size: %lld\n", (long long)st.st_size);
|
fprintf(f,"Size: %lld\n", (long long)st.st_size);
|
||||||
if (sha[0]) fprintf(f,"SHA256: %s\n", sha);
|
if (sha[0]) fprintf(f,"SHA256: %s\n", sha);
|
||||||
@@ -738,7 +711,9 @@ static int cmd_list_upgradable(pkgrec *recs, int nrec)
|
|||||||
char ap[BUF]; snprintf(ap,sizeof ap,"%s/%s/app.json", r, de->d_name);
|
char ap[BUF]; snprintf(ap,sizeof ap,"%s/%s/app.json", r, de->d_name);
|
||||||
appmeta m;
|
appmeta m;
|
||||||
if (load_app_meta(ap,&m)!=0) break;
|
if (load_app_meta(ap,&m)!=0) break;
|
||||||
if (version_cmp(recs[i].version, m.version) > 0) {
|
/* 仅按 version_code 比较(APPS.md §3.8);任一侧缺 code → 不判定为可升级 */
|
||||||
|
if (recs[i].has_version_code && m.has_version_code &&
|
||||||
|
recs[i].version_code > m.version_code) {
|
||||||
printf("%-24s %-12s %-12s\n", de->d_name, m.version[0]?m.version:"?", recs[i].version);
|
printf("%-24s %-12s %-12s\n", de->d_name, m.version[0]?m.version:"?", recs[i].version);
|
||||||
n++;
|
n++;
|
||||||
}
|
}
|
||||||
@@ -906,17 +881,18 @@ static int install_mpk_file(const char *mpk_path)
|
|||||||
rm_rf(stg); return 1;
|
rm_rf(stg); return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ② 同包名替换前的防降级门禁:新版必须 >= 已装(相等=重装,允许;<即降级拒装) */
|
/* ② 同包名替换前的防降级门禁(APPS.md §3.8,仅按 version_code 比较):
|
||||||
|
* 新版 code >= 已装 code(相等=重装,允许;<即降级拒装);任一侧缺 version_code → 拒装。 */
|
||||||
if (replacing) {
|
if (replacing) {
|
||||||
char instj[BUF]; snprintf(instj,sizeof instj,"%s/app.json", dst);
|
char instj[BUF]; snprintf(instj,sizeof instj,"%s/app.json", dst);
|
||||||
appmeta im;
|
appmeta im;
|
||||||
if (load_app_meta(instj,&im)!=0 || !version_ok(im.version) || !version_ok(m.version)) {
|
if (load_app_meta(instj,&im)!=0 || !m.has_version_code || !im.has_version_code) {
|
||||||
fprintf(stderr,"nyai: install: 版本无法判定(新=「%s」 已装=「%s」),拒绝安装\n",
|
fprintf(stderr,"nyai: install: 版本号无法判定(新缺 version_code 或已装缺 version_code),拒绝安装\n");
|
||||||
m.version[0]?m.version:"(缺失)", im.version[0]?im.version:"(缺失)");
|
|
||||||
rm_rf(stg); return 1;
|
rm_rf(stg); return 1;
|
||||||
}
|
}
|
||||||
if (version_num_cmp(m.version, im.version) < 0) {
|
if (m.version_code < im.version_code) {
|
||||||
fprintf(stderr,"nyai: install: 不允许降级: 新版本 %s < 已装 %s\n", m.version, im.version);
|
fprintf(stderr,"nyai: install: 不允许降级: 新版本 code %lld < 已装 code %lld\n",
|
||||||
|
m.version_code, im.version_code);
|
||||||
rm_rf(stg); return 1;
|
rm_rf(stg); return 1;
|
||||||
}
|
}
|
||||||
rm_rf(bak);
|
rm_rf(bak);
|
||||||
@@ -1034,6 +1010,8 @@ static int cmd_info(const char *pkg)
|
|||||||
printf("Package: %s\n", pkg);
|
printf("Package: %s\n", pkg);
|
||||||
printf("Name: %s\n", m.name);
|
printf("Name: %s\n", m.name);
|
||||||
printf("Version: %s\n", m.version[0]?m.version:"(none)");
|
printf("Version: %s\n", m.version[0]?m.version:"(none)");
|
||||||
|
if (m.has_version_code) printf("Version-Code: %lld\n", m.version_code);
|
||||||
|
else printf("Version-Code: (none)\n");
|
||||||
printf("Author: %s\n", m.author[0]?m.author:"(none)");
|
printf("Author: %s\n", m.author[0]?m.author:"(none)");
|
||||||
printf("Exec: %s\n", m.exec);
|
printf("Exec: %s\n", m.exec);
|
||||||
printf("Size: %s\n", hs);
|
printf("Size: %s\n", hs);
|
||||||
@@ -1247,18 +1225,19 @@ static int cmd_upgrade(const char *pkg, const char *file)
|
|||||||
else if (vr == 2) { if (sysp) { fprintf(stderr,"nyai: upgrade: 系统包 %s 必须签名,拒绝升级\n", pkg); return 1; } printf("nyai: 提示: %s 未签名(第三方放行)\n", pkg); }
|
else if (vr == 2) { if (sysp) { fprintf(stderr,"nyai: upgrade: 系统包 %s 必须签名,拒绝升级\n", pkg); return 1; } printf("nyai: 提示: %s 未签名(第三方放行)\n", pkg); }
|
||||||
else if (vr != 0) { if (sysp) { fprintf(stderr,"nyai: upgrade: meow_verify 不可用(rc=%d),os.meow.* 拒升\n", vr); return 1; } printf("nyai: 警告: meow_verify 不可用(rc=%d),%s 未验签放行\n", vr, pkg); }
|
else if (vr != 0) { if (sysp) { fprintf(stderr,"nyai: upgrade: meow_verify 不可用(rc=%d),os.meow.* 拒升\n", vr); return 1; } printf("nyai: 警告: meow_verify 不可用(rc=%d),%s 未验签放行\n", vr, pkg); }
|
||||||
}
|
}
|
||||||
/* 防降级门禁(与 install 同规范):新版必须 >= 已装(相等=重装允许) */
|
/* 防降级门禁(与 install 同规范,APPS.md §3.8,仅按 version_code 比较):
|
||||||
|
* 新版 code >= 已装 code(相等=重装允许);任一侧缺 version_code → 拒绝。 */
|
||||||
{
|
{
|
||||||
char aj[BUF]; snprintf(aj,sizeof aj,"%s/app.json", dst);
|
char aj[BUF]; snprintf(aj,sizeof aj,"%s/app.json", dst);
|
||||||
appmeta im, nm;
|
appmeta im, nm;
|
||||||
if (load_app_meta(aj,&im)!=0 || mpk_appinfo(mpk,&nm)!=0 ||
|
if (load_app_meta(aj,&im)!=0 || mpk_appinfo(mpk,&nm)!=0 ||
|
||||||
!version_ok(im.version) || !version_ok(nm.version)) {
|
!im.has_version_code || !nm.has_version_code) {
|
||||||
fprintf(stderr,"nyai: upgrade: 版本无法判定(新=「%s」 已装=「%s」),拒绝\n",
|
fprintf(stderr,"nyai: upgrade: 版本号无法判定(新缺 version_code 或已装缺 version_code),拒绝\n");
|
||||||
nm.version[0]?nm.version:"(缺失)", im.version[0]?im.version:"(缺失)");
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (version_num_cmp(nm.version, im.version) < 0) {
|
if (nm.version_code < im.version_code) {
|
||||||
fprintf(stderr,"nyai: upgrade: 不允许降级: 新版本 %s < 已装 %s\n", nm.version, im.version);
|
fprintf(stderr,"nyai: upgrade: 不允许降级: 新版本 code %lld < 已装 code %lld\n",
|
||||||
|
nm.version_code, im.version_code);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
Reference in new issue
Block a user