#include "spk.h"
#include "filesystem.h"
#include "string.h"
#include "stdlib.h"
#include "user.h"
extern void print(const char *s);
extern void print_num(int n);
extern void putchar(char c);
struct SpkHeader {
char magic[4];
unsigned int version;
char name[SPK_NAME_LEN];
char pkg_version[SPK_VER_LEN];
char desc[SPK_DESC_LEN];
unsigned int file_count;
} __attribute__((packed));
struct SpkFileHeader {
char path[SPK_PATH_LEN];
unsigned int size;
} __attribute__((packed));
static int ensure_dir(int parent, const char *name)
{
int idx = fs_find_in(parent, name);
if (idx >= 0 && fs_is_dir(idx)) return idx;
return fs_mkdir(parent, name);
}
static int spk_root_dir(void)
{
int root = fs_cd(0, "/");
int var, lib, spk;
if (root < 0) return -1;
var = ensure_dir(root, "var");
if (var < 0) return -1;
lib = ensure_dir(var, "lib");
if (lib < 0) return -1;
spk = ensure_dir(lib, "spk");
return spk;
}
int spk_init(void)
{
int d = spk_root_dir();
if (d < 0) { print("spk: init failed\n"); return -1; }
return 0;
}
static int resolve_path_create(const char *path, int *out_parent,
char *out_name, int name_max)
{
int cur = 0;
const char *p = path;
char part[MAX_NAME];
char last_part[MAX_NAME];
int i;
last_part[0] = '\0';
if (*p == '/') p++;
while (*p) {
i = 0;
while (*p && *p != '/' && i < MAX_NAME - 1) part[i++] = *p++;
part[i] = '\0';
if (*p == '/') p++;
if (i == 0) continue;
if (*p == '\0') {
strncpy(out_name, part, name_max);
out_name[name_max - 1] = '\0';
*out_parent = cur;
return 0;
}
int idx = fs_find_in(cur, part);
if (idx < 0) {
idx = fs_mkdir(cur, part);
if (idx < 0) return -1;
} else if (!fs_is_dir(idx)) {
return -1;
}
cur = idx;
}
return -1;
}
static int resolve_path(const char *path, int *out_parent,
char *out_name, int name_max)
{
char tmp[256];
char *p;
int parent;
if (strlen(path) >= sizeof(tmp)) return -1;
strcpy(tmp, path);
p = tmp;
while (*p) p++;
while (p > tmp && p[-1] != '/') p--;
if (p == tmp) {
parent = 0;
strncpy(out_name, tmp, name_max);
out_name[name_max - 1] = '\0';
} else {
p[-1] = '\0';
parent = fs_cd(0, tmp);
strncpy(out_name, p, name_max);
out_name[name_max - 1] = '\0';
}
if (parent < 0) return -1;
*out_parent = parent;
return 0;
}
int spk_build(const char *outfile, const char *name, const char *version,
const char *desc, const char **files, int nfiles)
{
unsigned char *buf;
struct SpkHeader *hdr;
int total_size = sizeof(struct SpkHeader);
int pos, i, j;
if (nfiles > SPK_MAX_FILES) {
print("spk: too many files (max "); print_num(SPK_MAX_FILES); print(")\n");
return -1;
}
for (i = 0; i < nfiles; i++) {
int parent;
char fname[MAX_NAME];
int idx, sz;
if (resolve_path(files[i], &parent, fname, MAX_NAME) < 0) {
print("spk: cannot resolve "); print(files[i]); putchar('\n');
return -1;
}
idx = fs_find_in(parent, fname);
if (idx < 0) {
print("spk: file not found: "); print(files[i]); putchar('\n');
return -1;
}
sz = fs_size_of(idx);
total_size += sizeof(struct SpkFileHeader) + sz;
}
if (total_size > 64000) {
print("spk: package too large\n");
return -1;
}
buf = (unsigned char *)malloc(total_size);
if (!buf) { print("spk: out of memory\n"); return -1; }
memset(buf, 0, total_size);
hdr = (struct SpkHeader *)buf;
hdr->magic[0] = 'S'; hdr->magic[1] = 'P';
hdr->magic[2] = 'K'; hdr->magic[3] = '1';
hdr->version = 1;
strncpy(hdr->name, name, SPK_NAME_LEN - 1);
strncpy(hdr->pkg_version, version, SPK_VER_LEN - 1);
strncpy(hdr->desc, desc, SPK_DESC_LEN - 1);
hdr->file_count = (unsigned int)nfiles;
pos = sizeof(struct SpkHeader);
for (i = 0; i < nfiles; i++) {
struct SpkFileHeader *fh;
int parent, sz;
char fname[MAX_NAME];
if (resolve_path(files[i], &parent, fname, MAX_NAME) < 0) {
free(buf);
return -1;
}
sz = fs_read_at(parent, fname, buf + pos + sizeof(struct SpkFileHeader), 32768);
if (sz < 0) sz = 0;
fh = (struct SpkFileHeader *)(buf + pos);
strncpy(fh->path, files[i], SPK_PATH_LEN - 1);
fh->size = (unsigned int)sz;
pos += sizeof(struct SpkFileHeader) + sz;
}
{
int oparent;
char oname[MAX_NAME];
if (resolve_path_create(outfile, &oparent, oname, MAX_NAME) < 0) {
free(buf);
print("spk: bad output path\n");
return -1;
}
if (fs_find_in(oparent, oname) < 0)
fs_create_ex(oparent, oname, FS_FILE, current_uid, 0644);
fs_write_at(oparent, oname, buf, pos);
}
free(buf);
print("Built "); print(outfile);
print(" ("); print_num(pos); print(" bytes, ");
print_num(nfiles); print(" files)\n");
return 0;
}
int spk_install(const char *pkgfile)
{
unsigned char *buf;
struct SpkHeader *hdr;
unsigned int pos, i;
int sz, err = 0;
int spkdir;
buf = (unsigned char *)malloc(65536);
if (!buf) { print("spk: out of memory\n"); return -1; }
sz = fs_read(pkgfile, buf, 65536);
if (sz <= 0) {
free(buf);
print("spk: cannot read "); print(pkgfile); putchar('\n');
return -1;
}
if (sz < (int)sizeof(struct SpkHeader)) {
free(buf);
print("spk: file too small\n");
return -1;
}
hdr = (struct SpkHeader *)buf;
if (hdr->magic[0] != 'S' || hdr->magic[1] != 'P' ||
hdr->magic[2] != 'K' || hdr->magic[3] != '1') {
free(buf);
print("spk: not a valid spk package\n");
return -1;
}
if (hdr->file_count > SPK_MAX_FILES) {
free(buf);
print("spk: too many files in package\n");
return -1;
}
spkdir = spk_root_dir();
if (spkdir < 0) { free(buf); return -1; }
{
char *db = (char *)malloc(16384);
int dbsize = fs_find_in(spkdir, "installed") >= 0
? fs_read_at(spkdir, "installed", db, 16384) : 0;
int p = 0;
if (dbsize > 0) {
db[dbsize] = '\0';
while (p < dbsize) {
char line[128];
int len = 0;
while (p < dbsize && db[p] != '\n' && len < 127) line[len++] = db[p++];
line[len] = '\0';
if (p < dbsize) p++;
if (len == 0) continue;
{
char nm[64]; int k = 0;
char *lp = line;
while (*lp && *lp != ' ' && k < 63) nm[k++] = *lp++;
nm[k] = '\0';
if (strcmp(nm, hdr->name) == 0) {
print("spk: "); print(hdr->name);
print(" is already installed\n");
free(db); free(buf);
return -1;
}
}
{
char *lp = line;
int nf = 0;
while (*lp && *lp != ' ') lp++;
if (*lp == ' ') lp++;
while (*lp && *lp != ' ') lp++;
if (*lp == ' ') lp++;
while (*lp >= '0' && *lp <= '9') { nf = nf * 10 + (*lp - '0'); lp++; }
while (nf-- > 0) {
while (p < dbsize && db[p] != '\n') p++;
if (p < dbsize) p++;
}
}
}
}
free(db);
}
print("Installing "); print(hdr->name);
print(" "); print(hdr->pkg_version); putchar('\n');
pos = sizeof(struct SpkHeader);
for (i = 0; i < hdr->file_count; i++) {
struct SpkFileHeader *fh;
int parent, idx;
char fname[MAX_NAME];
if (pos + sizeof(struct SpkFileHeader) > (unsigned int)sz) { err = 1; break; }
fh = (struct SpkFileHeader *)(buf + pos);
pos += sizeof(struct SpkFileHeader);
if (pos + fh->size > (unsigned int)sz) { err = 1; break; }
if (resolve_path_create(fh->path, &parent, fname, MAX_NAME) < 0) {
print("spk: bad path "); print(fh->path); putchar('\n');
err = 1; break;
}
idx = fs_find_in(parent, fname);
if (idx < 0) {
fs_create_ex(parent, fname, FS_FILE, 0, 0644);
}
fs_write_at(parent, fname, buf + pos, fh->size);
print(" "); print(fh->path);
print(" ("); print_num(fh->size); print(" bytes)\n");
pos += fh->size;
}
if (!err) {
char *db = (char *)malloc(16384);
int dbsize = 0, outpos = 0;
int dbi;
if (db) {
dbi = fs_find_in(spkdir, "installed");
if (dbi >= 0)
dbsize = fs_read_at(spkdir, "installed", db, 16000);
if (dbsize < 0) dbsize = 0;
outpos = dbsize;
{
const char *sp = hdr->name;
while (*sp && outpos < 16380) db[outpos++] = *sp++;
if (outpos < 16380) db[outpos++] = ' ';
sp = hdr->pkg_version;
while (*sp && outpos < 16380) db[outpos++] = *sp++;
if (outpos < 16380) db[outpos++] = ' ';
{
char t[12]; int ti = 0, n = (int)hdr->file_count;
if (n == 0) t[ti++] = '0';
while (n > 0) { t[ti++] = '0' + n % 10; n /= 10; }
while (ti > 0 && outpos < 16380) db[outpos++] = t[--ti];
}
if (outpos < 16380) db[outpos++] = '\n';
}
{
unsigned int pp = sizeof(struct SpkHeader);
unsigned int k;
for (k = 0; k < hdr->file_count; k++) {
struct SpkFileHeader *fh = (struct SpkFileHeader *)(buf + pp);
const char *sp = fh->path;
while (*sp && outpos < 16380) db[outpos++] = *sp++;
if (outpos < 16380) db[outpos++] = '\n';
pp += sizeof(struct SpkFileHeader) + fh->size;
}
}
if (dbi < 0)
fs_create_ex(spkdir, "installed", FS_FILE, 0, 0644);
fs_write_at(spkdir, "installed", db, outpos);
free(db);
}
}
free(buf);
if (err) { print("spk: install failed\n"); return -1; }
print("Done.\n");
return 0;
}
int spk_remove(const char *pkgname)
{
char *db, *out;
int dbsize, pos, end, outpos = 0;
int spkdir, found = 0;
char line[256];
char name[64];
int nfiles = 0, i;
spkdir = spk_root_dir();
if (spkdir < 0) return -1;
db = (char *)malloc(16384);
out = (char *)malloc(16384);
if (!db || !out) { free(db); free(out); return -1; }
dbsize = fs_read_at(spkdir, "installed", db, 16384);
if (dbsize <= 0) {
free(db); free(out);
print("spk: no packages installed\n");
return -1;
}
end = dbsize;
pos = 0;
while (pos < end) {
int hdr_start = pos;
int len = 0;
while (pos < end && db[pos] != '\n' && len < 255) line[len++] = db[pos++];
line[len] = '\0';
if (pos < end) pos++;
{
char *lp = line;
int k = 0;
while (*lp && *lp != ' ' && k < 63) name[k++] = *lp++;
name[k] = '\0';
nfiles = 0;
while (*lp && *lp != ' ') lp++;
if (*lp == ' ') lp++;
while (*lp && *lp != ' ') lp++;
if (*lp == ' ') lp++;
while (*lp >= '0' && *lp <= '9') { nfiles = nfiles * 10 + (*lp - '0'); lp++; }
}
if (strcmp(name, pkgname) == 0) {
found = 1;
for (i = 0; i < nfiles; i++) {
int flen = 0;
while (pos < end && db[pos] != '\n' && flen < 255)
line[flen++] = db[pos++];
line[flen] = '\0';
if (pos < end) pos++;
{
int parent;
char fname[MAX_NAME];
if (resolve_path(line, &parent, fname, MAX_NAME) >= 0) {
fs_delete(parent, fname);
print(" removed "); print(line); putchar('\n');
}
}
}
} else {
for (i = hdr_start; i < pos; i++) {
if (outpos < 16383) out[outpos++] = db[i];
}
for (i = 0; i < nfiles; i++) {
while (pos < end && db[pos] != '\n') {
if (outpos < 16383) out[outpos++] = db[pos];
pos++;
}
if (pos < end) {
if (outpos < 16383) out[outpos++] = db[pos];
pos++;
}
}
}
}
if (!found) {
free(db); free(out);
print("spk: package not installed: "); print(pkgname); putchar('\n');
return -1;
}
fs_write_at(spkdir, "installed", out, outpos);
free(db);
free(out);
print("Removed "); print(pkgname); putchar('\n');
return 0;
}
void spk_list(void)
{
char *db;
int dbsize, pos, end;
int spkdir, count = 0;
spkdir = spk_root_dir();
if (spkdir < 0) return;
db = (char *)malloc(16384);
if (!db) return;
dbsize = fs_read_at(spkdir, "installed", db, 16384);
if (dbsize <= 0) {
free(db);
print("spk: no packages installed\n");
return;
}
end = dbsize;
print("Installed packages:\n");
pos = 0;
while (pos < end) {
char line[256];
int len = 0;
int nfiles = 0;
char *lp;
while (pos < end && db[pos] != '\n' && len < 255) line[len++] = db[pos++];
line[len] = '\0';
if (pos < end) pos++;
if (len == 0) continue;
lp = line;
while (*lp && *lp != ' ') lp++;
if (*lp == ' ') *lp++ = '\0';
{
char ver[16]; int k = 0;
while (*lp && *lp != ' ' && k < 15) ver[k++] = *lp++;
ver[k] = '\0';
if (*lp == ' ') lp++;
while (*lp >= '0' && *lp <= '9') { nfiles = nfiles * 10 + (*lp - '0'); lp++; }
print(" "); print(line); print(" "); print(ver); putchar('\n');
count++;
}
while (nfiles-- > 0) {
while (pos < end && db[pos] != '\n') pos++;
if (pos < end) pos++;
}
}
if (count == 0) print(" (empty)\n");
free(db);
}
void spk_info(const char *pkgname)
{
char *db;
int dbsize, pos, end;
int spkdir, found = 0;
spkdir = spk_root_dir();
if (spkdir < 0) return;
db = (char *)malloc(16384);
if (!db) return;
dbsize = fs_read_at(spkdir, "installed", db, 16384);
if (dbsize <= 0) {
free(db);
print("spk: no packages installed\n");
return;
}
end = dbsize;
pos = 0;
while (pos < end) {
char line[256];
int len = 0, nfiles = 0, i;
char name[64];
char *lp;
while (pos < end && db[pos] != '\n' && len < 255) line[len++] = db[pos++];
line[len] = '\0';
if (pos < end) pos++;
if (len == 0) continue;
lp = line;
{
int k = 0;
while (*lp && *lp != ' ' && k < 63) name[k++] = *lp++;
name[k] = '\0';
}
if (*lp == ' ') lp++;
{
char ver[16]; int k = 0;
while (*lp && *lp != ' ' && k < 15) ver[k++] = *lp++;
ver[k] = '\0';
if (*lp == ' ') lp++;
while (*lp >= '0' && *lp <= '9') { nfiles = nfiles * 10 + (*lp - '0'); lp++; }
if (strcmp(name, pkgname)) {
found = 1;
print("Package: "); print(name); putchar('\n');
print("Version: "); print(ver); putchar('\n');
print("Files: "); print_num(nfiles); putchar('\n');
for (i = 0; i < nfiles; i++) {
int flen = 0;
while (pos < end && db[pos] != '\n' && flen < 255)
line[flen++] = db[pos++];
line[flen] = '\0';
if (pos < end) pos++;
print(" "); print(line); putchar('\n');
}
free(db);
return;
}
}
while (nfiles-- > 0) {
while (pos < end && db[pos] != '\n') pos++;
if (pos < end) pos++;
}
}
if (!found) {
print("spk: package not installed: "); print(pkgname); putchar('\n');
}
free(db);
}