1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
|
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define PKG_DB "../snaggies"
#define PKG_INFO_CMD "./get_pkg_attrs.sh"
typedef struct {
char* id;
char* name;
char* desc;
char* script_path;
} package_info_t;
void subcmd_install(char* pkgs[], size_t count);
bool load_package_info(package_info_t* info, char* pkgid);
int main(int argc, char* argv[]) {
// run install subcommand with each argument as a package
subcmd_install(argv+1, argc-1);
return 0;
}
void subcmd_install(char* pkgs[], size_t count) {
// install each package listed
for (int i = 0; i < count; ++i) {
char* pkgid = pkgs[i];
// load package description
package_info_t info;
if (load_package_info(&info, pkgid)) {
printf("Installing '%s'\n", info.name);
}
}
}
// loads and allocates package attributes for the info struct. does not do
// excessive validation, so this should be fine when loading package data in
// bulk.
bool load_package_info(package_info_t* info, char* pkgid) {
// construct package description directory path and ensure it exists
size_t pkg_dir_len = strlen(PKG_DB) + strlen(pkgid) + 1;
char* pkg_dir = malloc(pkg_dir_len + 1);
snprintf(pkg_dir, pkg_dir_len + 1, "%s/%s", PKG_DB, pkgid);
if (access(pkg_dir, F_OK) != 0) {
fprintf(stderr, "Package '%s' not found in database\n", pkgid);
free(pkg_dir);
return false;
}
// construct package description script path and ensure it exists
size_t script_path_len = pkg_dir_len + strlen(pkgid) + 4;
info->script_path = malloc(script_path_len + 1);
snprintf(info->script_path, script_path_len + 1, "%s/%s.sh", pkg_dir, pkgid);
free(pkg_dir);
if (access(info->script_path, F_OK) != 0) {
fprintf(stderr, "Package '%s' broken, doesn't have description script\n", pkgid);
free(info->script_path);
return false;
}
// run get_pkg_attrs script that outputs package attributes
size_t cmd_len = strlen(PKG_INFO_CMD) + script_path_len + 3;
char* cmd = malloc(cmd_len + 1);
snprintf(cmd, cmd_len + 1, "%s '%s'", PKG_INFO_CMD, info->script_path);
FILE* getinfo_fp = popen(cmd, "r");
free(cmd);
if (getinfo_fp == NULL) {
perror("failed to popen get_pkg_attrs");
exit(1); // fatal error
}
// go through each NULL delimited section of the output
size_t n = 0;
char* line = NULL;
ssize_t read;
while ((read = getdelim(&line, &n, '\0', getinfo_fp)) != -1) {
// read in property based on first attribute type character
char atype = line[0];
char* avalue = line+1;
switch (atype) {
case 'i':
// id
info->id = strdup(avalue);
break;
case 'n':
// name
info->name = strdup(avalue);
break;
case 'd':
// description
info->desc = strdup(avalue);
break;
}
}
free(line);
// close the get_pkg_attrs process
int close_err = pclose(getinfo_fp);
if (close_err == -1) {
perror("failed to pclose get_pkg_attrs");
exit(1); // fatal error
} else if (close_err != 0) {
fprintf(stderr, "failed to execute get_pkg_attrs: %d\n", close_err);
exit(1); // fatal error
}
return true;
}
|