tools/llvm: Do not build with symbols
[minix3.git] / minix / usr.bin / toproto / toproto.c
blob349da52b8f67e39bd8fb4b7f1d78ad9c91e8d179
1 #if HAVE_NBTOOL_CONFIG_H
2 #include "nbtool_config.h"
3 #else
4 #include <sys/cdefs.h>
5 #endif
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <errno.h>
11 #include <assert.h>
13 #include <getopt.h>
14 #include <unistd.h>
16 #include <sys/types.h>
17 #include <sys/stat.h>
18 #include <fcntl.h>
20 #include "pack_dev.h"
22 #define MAX_ENTRIES 100000
23 #define MAX_LINE_SIZE 0xfff
27 * Tool to convert the netbsd METALOG into a proto file usable by mkfs.mfs
29 * todo:
30 * Possibly use netbsd usr.sbin/makefs to create mfs file systems.
33 enum entry_type
34 { ENTRY_DIR, ENTRY_FILE, ENTRY_LINK, ENTRY_BLOCK, ENTRY_CHAR };
37 struct entry
39 char *path;
40 char *filename; /* point to last component in the path */
41 enum entry_type type; /* entry type */
42 int mode; /* unix mode e.g. 0755 */
43 char *uid;
44 char *gid;
45 char *time; /* time 1365836670.000000000 */
46 char *size;
47 char *link;
48 /* Can't use devmajor_t/devminor_t on linux systems :( */
49 int32_t dev_major;
50 int32_t dev_minor;
52 /* just internal variables used to create a tree */
53 int depth;
54 struct entry *parent;
58 static struct entry entries[MAX_ENTRIES];
59 static int entry_total_count;
61 static int
62 convert_to_entry(char *line, struct entry *entry)
64 /* convert a input line from sanitized input into an entry */
65 char *saveptr;
66 char *key, *value;
68 saveptr = NULL;
70 /* we need to have a terminated string */
71 assert(strnlen(line, MAX_LINE_SIZE - 1) != MAX_LINE_SIZE - 1);
73 /* skip comment lines */
74 if (*line == '#')
75 return 1;
77 line = strtok_r(line, " ", &saveptr);
79 /* skip empty lines */
80 if (!line) {
81 return 1;
84 memset(entry, 0, sizeof(struct entry));
86 /* the first entry is the path name */
87 entry->path = strndup(line, MAX_LINE_SIZE);
89 /* the next entries are key,value pairs */
90 while ((line = strtok_r(NULL, " ", &saveptr)) != NULL) {
91 key = value = NULL;
92 char *p;
93 if (strstr(line, "=") == NULL) {
94 fprintf(stderr, "expected key/value pair in %s\n",
95 line);
96 free(entry->path);
97 return 1;
99 p = NULL;
100 key = strtok_r(line, "=", &p);
101 value = strtok_r(NULL, "=", &p);
102 if (value) {
103 if (strncmp(key, "type", 5) == 0) {
104 if (strncmp(value, "dir", 4) == 0) {
105 entry->type = ENTRY_DIR;
106 } else if (strncmp(value, "file", 5) == 0) {
107 entry->type = ENTRY_FILE;
108 } else if (strncmp(value, "link", 5) == 0) {
109 entry->type = ENTRY_LINK;
110 } else if (strncmp(value, "block", 6) == 0) {
111 entry->type = ENTRY_BLOCK;
112 } else if (strncmp(value, "char", 5) == 0) {
113 entry->type = ENTRY_CHAR;
114 } else {
115 fprintf(stderr,
116 "\tunknown type %s -> '%s'\n", key,
117 value);
119 } else if (strncmp(key, "mode", 5) == 0) {
120 sscanf(value,"%o",&entry->mode);
121 } else if (strncmp(key, "uid", 4) == 0) {
122 entry->uid = strndup(value, MAX_LINE_SIZE);
123 } else if (strncmp(key, "gid", 4) == 0) {
124 entry->gid = strndup(value, MAX_LINE_SIZE);
125 } else if (strncmp(key, "time", 5) == 0) {
126 entry->time = strndup(value, MAX_LINE_SIZE);
127 } else if (strncmp(key, "size", 5) == 0) {
128 entry->size = strndup(value, MAX_LINE_SIZE);
129 } else if (strncmp(key, "link", 5) == 0) {
130 entry->link = strndup(value, MAX_LINE_SIZE);
131 } else if (strncmp(key, "device", 7) == 0) {
132 long int dev_id;
133 dev_id = strtoul(value, NULL, 16);
134 entry->dev_major = major_netbsd(dev_id);
135 entry->dev_minor = minor_netbsd(dev_id);
136 } else {
137 fprintf(stderr,
138 "\tunknown attribute %s -> %s\n", key,
139 value);
143 return 0;
146 static int
147 iterate_over_input(int fh_in, void (*callback) (char *line))
149 char buf[MAX_LINE_SIZE];
150 int r_size, err;
151 int line_size;
152 int buf_end;
153 int line_nr;
154 char *p;
155 memset(buf, 0, MAX_LINE_SIZE);
157 r_size = 0;
158 buf_end = 0;
159 line_nr = 0;
161 while (1 == 1) {
162 /* fill buffer taking into account there can already be
163 * content at the start */
164 r_size = read(fh_in, &buf[buf_end], MAX_LINE_SIZE - buf_end);
165 if (r_size == -1) {
166 err = errno;
167 fprintf(stderr, "failed reading input:%s\n",
168 strerror(err));
169 return 1;
171 /* checking for read size of 0 is not enough as the buffer
172 * still can contain content */
173 buf_end = buf_end + r_size;
175 /* is there data we need to process ? */
176 if (buf_end == 0) {
177 return 0; /* normal exit is here */
180 /* find end of line or eof. start a the start of the buffer */
181 p = buf;
182 while (p < buf + buf_end) {
183 if (*p == '\n' || *p == '\0') {
184 /* replace either by a null terminator */
185 line_nr++;
186 *p = '\0';
187 break;
189 p++;
192 /* If we are at the end of the buffer we did not find a
193 * terminator */
194 if (p == buf + buf_end) {
195 fprintf(stderr,
196 "Line(%d) does not fit the buffer %d\n", line_nr,
197 MAX_LINE_SIZE);
198 return 1;
201 line_size = p - buf; /* size of the line we currently are
202 * reading */
204 /* here we have a valid line */
205 callback(buf);
207 /* copy the remaining data over to the start */
208 memmove(buf, p + 1, MAX_LINE_SIZE - line_size);
209 buf_end -= (line_size + 1);
211 return 0;
214 static void
215 parse_line_cb(char *line)
217 if (convert_to_entry(line, &entries[entry_total_count]) == 0) {
218 entry_total_count++;
219 assert(entry_total_count < MAX_ENTRIES);
220 } else {
221 memset(&entries[entry_total_count], 0, sizeof(struct entry));
225 static int
226 create_entries(int handle)
228 int c;
229 char *p;
230 struct entry *entry;
232 char tmppath[MAX_LINE_SIZE];
233 int i;
235 if (iterate_over_input(handle, parse_line_cb)) {
236 return 1;
239 /* calculate depth for each entry */
240 for (c = 0; c < entry_total_count; c++) {
241 p = entries[c].path;
242 while (*p != 0) {
243 if (*p == '/') {
244 entries[c].depth++;
246 p++;
250 /* find parent entry and set the filename */
251 for (c = 0; c < entry_total_count; c++) {
252 entry = &entries[c];
253 if (entry->depth > 0) {
254 /* calculate path */
255 /* find last "/" element and "null" it */
256 strncpy(tmppath, entry->path, MAX_LINE_SIZE - 1);
257 i = strlen(tmppath);
258 while (i > 0) {
259 if (tmppath[i] == '/') {
260 entry->filename = &entry->path[i + 1];
261 tmppath[i] = '\0';
262 break;
264 i--;
266 if (i == 0) {
267 fprintf
268 (stderr,
269 "error while searching for parent path of %s\n",
270 entry->path);
271 return 1;
274 /* now compare with the other entries */
275 for (i = 0; i < entry_total_count; i++) {
276 if (strncmp(entries[i].path, tmppath,
277 MAX_LINE_SIZE) == 0) {
278 /* found entry */
279 entry->parent = &entries[i];
280 break;
283 if (entry->parent == NULL) {
284 fprintf(stderr,
285 "Failed to find parent directory of %s\n",
286 entry->path);
287 return 1;
289 assert(entry->parent->type == ENTRY_DIR);
290 } else {
291 /* same in this case */
292 entry->filename = entry->path;
296 return 0;
299 static char * parse_mode(int mode){
300 /* Convert a 4 digit octal number int a proto entry as described in
301 the mkfs.mfs man page e.g. [suid-char][guid-char]0777 mode */
303 static char m[6];
304 memset(m,0,6);
305 char suid,guid;
306 suid = (mode & 04000)?'u':'-';
307 guid = (mode & 02000)?'g':'-';
308 snprintf(m,6,"%c%c%3o",suid,guid,mode & 0777);
309 return m;
312 static char *parse_filename(char *path)
314 /* Skipping the first . in the path */
315 return &path[1];
318 static int
319 dump_entry(FILE * out, int mindex, const char *base_dir)
322 int space;
323 int i;
324 struct entry *entry = &entries[mindex];
326 /* Ensure uid & gid are set to something meaningful. */
327 if (entry->uid == NULL) {
328 entry->uid = __UNCONST("0");
330 if (entry->gid == NULL) {
331 entry->gid = __UNCONST("0");
334 /* Indent the line */
335 for (space = 0; space < entries[mindex].depth; space++) {
336 fprintf(out, " ");
338 if (entry->type == ENTRY_DIR) {
339 if (entries[mindex].depth > 0) {
340 fprintf(out, "%s ", entry->filename);
342 fprintf(out, "d%s", parse_mode(entry->mode));
343 fprintf(out, " %s", entry->uid);
344 fprintf(out, " %s", entry->gid);
345 fprintf(out, "\n");
346 for (i = 0; i < entry_total_count; i++) {
347 if (entries[i].parent == entry) {
348 dump_entry(out, i, base_dir);
351 for (space = 0; space < entries[mindex].depth; space++) {
352 fprintf(out, " ");
354 fprintf(out, "$\n");
355 } else if (entry->type == ENTRY_FILE) {
356 fprintf(out, "%s -%s %s %s %s%s\n", entry->filename,
357 parse_mode(entry->mode), entry->uid, entry->gid,
358 base_dir, parse_filename(entry->path));
359 } else if (entry->type == ENTRY_LINK) {
360 fprintf(out, "%s s%s %s %s %s\n", entry->filename,
361 parse_mode(entry->mode), entry->uid, entry->gid,
362 entry->link);
363 } else if (entry->type == ENTRY_CHAR) {
364 fprintf(out, "%s c%s %s %s %d %d\n", entry->filename,
365 parse_mode(entry->mode), entry->uid, entry->gid,
366 entry->dev_major, entry->dev_minor);
367 } else if (entry->type == ENTRY_BLOCK) {
368 fprintf(out, "%s b%s %s %s %d %d\n", entry->filename,
369 parse_mode(entry->mode), entry->uid, entry->gid,
370 entry->dev_major, entry->dev_minor);
371 } else {
372 /* Unknown line type. */
373 fprintf(out, "#");
374 fprintf(out, "%i %s\n", entry->type, entry->path);
375 exit(1);
376 return 1;
378 return 0;
381 static int
382 dump_proto(FILE * out, const char *base_dir)
384 int i;
385 fprintf(out, "boot\n0 0");
386 for (i = 0; i < entry_total_count; i++) {
387 if (entries[i].depth == 0) {
388 fprintf(out, "\n");
389 dump_entry(out, i, base_dir);
392 return 0;
395 static void
396 print_usage(void)
398 printf("Usage: toproto [OPTION]...\n");
399 printf
400 ("Convert a netbsd METALOG file into a proto file for mkfs.mfs.\n");
401 printf("\n");
402 printf(" -i input METALOG\n");
403 printf(" -b base_path\n");
404 printf(" -o output proto\n");
405 printf(" -h show this this help and exit\n");
409 main(int argc, char **argv)
411 int ch, fh_in;
412 FILE *out;
413 const char *base_path;
414 char *input_file, *output_file;
416 input_file = NULL;
417 output_file = NULL;
418 base_path = ".";
419 fh_in = STDIN_FILENO;
420 out = stdout;
422 while ((ch = getopt(argc, argv, "i:b:o:h")) != -1) {
423 switch (ch) {
424 case 'i':
425 input_file = optarg;
426 break;
427 case 'b':
428 base_path = optarg;
429 break;
430 case 'o':
431 output_file = optarg;
432 break;
433 case 'h':
434 print_usage();
435 exit(0);
436 break;
437 default:
438 print_usage();
439 exit(1);
442 argc -= optind;
443 argv += optind;
445 if (input_file) {
446 fh_in = open(input_file, O_RDONLY);
447 if (fh_in == -1) {
448 fprintf(stderr, "Failed to open input file (%s):%s\n",
449 input_file, strerror(errno));
450 exit(1);
453 if (output_file) {
454 out = fopen(output_file, "w+");
455 if (!out) {
456 fprintf(stderr, "Failed to open input file (%s):%s\n",
457 input_file, strerror(errno));
458 exit(1);
462 if (create_entries(fh_in)) {
463 fprintf(stderr, "Failed to create entries\n");
464 exit(1);
466 if (input_file)
467 close(fh_in);
469 if (dump_proto(out, base_path)) {
470 fprintf(stderr, "Failed to create entries\n");
471 exit(1);
474 if (output_file)
475 fclose(out);
476 return 0;