Root/
| 1 | /* |
| 2 | * tree.c - Component hierarchy |
| 3 | * |
| 4 | * Copyright 2012 by Werner Almesberger |
| 5 | * |
| 6 | * This program is free software; you can redistribute it and/or modify |
| 7 | * it under the terms of the GNU General Public License as published by |
| 8 | * the Free Software Foundation; either version 2 of the License, or |
| 9 | * (at your option) any later version. |
| 10 | */ |
| 11 | |
| 12 | #define _GNU_SOURCE /* for strcasecmp */ |
| 13 | #include <stdlib.h> |
| 14 | #include <stdio.h> |
| 15 | #include <string.h> |
| 16 | #include <ctype.h> |
| 17 | |
| 18 | #include "util.h" |
| 19 | #include "libs.h" |
| 20 | #include "tree.h" |
| 21 | |
| 22 | |
| 23 | struct node *tree = NULL; |
| 24 | |
| 25 | |
| 26 | /* ----- Tree parsing ------------------------------------------------------ */ |
| 27 | |
| 28 | |
| 29 | void read_tree(FILE *file) |
| 30 | { |
| 31 | char buf[1100]; /* more than enough */ |
| 32 | struct node *last = NULL, *node; |
| 33 | int depth = -1; |
| 34 | const char *s, *p, *e; |
| 35 | char *name; |
| 36 | int n; |
| 37 | |
| 38 | next: |
| 39 | while (fgets(buf, sizeof(buf), file)) { |
| 40 | n = 0; |
| 41 | s = buf; |
| 42 | while (1) { |
| 43 | switch (*s++) { |
| 44 | case ' ': |
| 45 | n++; |
| 46 | continue; |
| 47 | case '\t': |
| 48 | n = (n+8) & ~7; |
| 49 | continue; |
| 50 | case 0: |
| 51 | case '#': |
| 52 | goto next; |
| 53 | default: |
| 54 | break; |
| 55 | } |
| 56 | break; |
| 57 | } |
| 58 | for (p = e = --s; *p && *p != '#' && *p != '\n'; p++) |
| 59 | if (*p != ' ' && *p != '\t') |
| 60 | e = p; |
| 61 | if (!last && n) { |
| 62 | fprintf(stderr, "first entry must not be indented\n"); |
| 63 | exit(1); |
| 64 | } |
| 65 | name = malloc(e-s+2); |
| 66 | if (!name) { |
| 67 | perror("malloc"); |
| 68 | exit(1); |
| 69 | } |
| 70 | memcpy(name, s, e-s+1); |
| 71 | name[e-s+1] = 0; |
| 72 | node = alloc_type(struct node); |
| 73 | node->name = name; |
| 74 | node->e = NULL; |
| 75 | node->comment = NULL; |
| 76 | node->indent = n; |
| 77 | node->child = node->next = NULL; |
| 78 | if (n > depth) { |
| 79 | if (last) |
| 80 | last->child = node; |
| 81 | else |
| 82 | tree = node; |
| 83 | node->parent = last; |
| 84 | } else { |
| 85 | while (last && last->indent != n) |
| 86 | last = last->parent; |
| 87 | if (!last && n) { |
| 88 | fprintf(stderr, "indentation error\n"); |
| 89 | exit(1); |
| 90 | } |
| 91 | last->next = node; |
| 92 | node->parent = last->parent; |
| 93 | } |
| 94 | last = node; |
| 95 | depth = n; |
| 96 | } |
| 97 | } |
| 98 | |
| 99 | |
| 100 | /* ----- Description parsing ----------------------------------------------- */ |
| 101 | |
| 102 | |
| 103 | static struct node *find_comp(struct node *node, const char *name) |
| 104 | { |
| 105 | struct node *found; |
| 106 | |
| 107 | while (node) { |
| 108 | if (!strcasecmp(node->name, name)) |
| 109 | return node; |
| 110 | found = find_comp(node->child, name); |
| 111 | if (found) |
| 112 | return found; |
| 113 | node = node->next; |
| 114 | } |
| 115 | return NULL; |
| 116 | } |
| 117 | |
| 118 | |
| 119 | static struct line *new_line(char *s) |
| 120 | { |
| 121 | const char *uris[] = { |
| 122 | "http://", |
| 123 | "https://", |
| 124 | "ftp://", |
| 125 | "file:/", /* one slash, for RFC3986 file:/localpath */ |
| 126 | NULL |
| 127 | }, **uri = uris; |
| 128 | struct line *line; |
| 129 | |
| 130 | line = alloc_type(struct line); |
| 131 | line->s = stralloc(s); |
| 132 | /* |
| 133 | * Require a slash at all to avoid misdetection. Require only one slash |
| 134 | * for compatibility with RFC3986. |
| 135 | */ |
| 136 | while (*uri && strncmp(s, *uri, strlen(*uri))) |
| 137 | uri++; |
| 138 | line->url = !!*uri; |
| 139 | line->next = NULL; |
| 140 | return line; |
| 141 | } |
| 142 | |
| 143 | |
| 144 | static void append_line(struct line *line, char *s) |
| 145 | { |
| 146 | int len1, len2; |
| 147 | int spc = !line->url; |
| 148 | |
| 149 | len1 = strlen(line->s); |
| 150 | len2 = strlen(s); |
| 151 | line->s = realloc(line->s, len1+len2+spc+1); |
| 152 | if (!line->s) { |
| 153 | perror("realloc"); |
| 154 | exit(1); |
| 155 | } |
| 156 | if (spc) |
| 157 | line->s[len1] = ' '; |
| 158 | memcpy(line->s+len1+spc, s, len2); |
| 159 | line->s[len1+len2+spc] = 0; |
| 160 | } |
| 161 | |
| 162 | |
| 163 | void read_desc(FILE *file) |
| 164 | { |
| 165 | struct line **anchor = NULL; |
| 166 | char buf[1100]; /* more than enough */ |
| 167 | struct node *node; |
| 168 | char *p, *end; |
| 169 | int skip = 0, lineno = 0; |
| 170 | |
| 171 | while (fgets(buf, sizeof(buf), file)) { |
| 172 | lineno++; |
| 173 | if (buf[0] == '#') |
| 174 | continue; |
| 175 | p = strchr(buf, '\n'); |
| 176 | if (p) |
| 177 | *p = 0; |
| 178 | p = buf; |
| 179 | if (*buf && !isspace(*buf)) { |
| 180 | /* tag is ^.*?:\s */ |
| 181 | while (1) { |
| 182 | p = strchr(p, ':'); |
| 183 | if (!p) { |
| 184 | fprintf(stderr, "no tag in line %d\n", |
| 185 | lineno); |
| 186 | exit(1); |
| 187 | } |
| 188 | if (!p[1] || isspace(p[1])) |
| 189 | break; |
| 190 | p++; |
| 191 | } |
| 192 | *p++ = 0; |
| 193 | node = find_comp(tree, buf); |
| 194 | if (!node) { |
| 195 | fprintf(stderr, |
| 196 | "component \"%s\" not found in line %d\n", |
| 197 | buf, lineno); |
| 198 | skip = 1; |
| 199 | continue; |
| 200 | } |
| 201 | for (anchor = &node->comment; *anchor; |
| 202 | anchor = &(*anchor)->next); |
| 203 | skip = 0; |
| 204 | } |
| 205 | if (skip) |
| 206 | continue; |
| 207 | |
| 208 | /* remove leading whitespace */ |
| 209 | while (*p && isspace(*p)) |
| 210 | p++; |
| 211 | if (!*p) { |
| 212 | if (*anchor) |
| 213 | anchor = &(*anchor)->next; |
| 214 | continue; |
| 215 | } |
| 216 | |
| 217 | /* remove training whitespace */ |
| 218 | end = strrchr(p, 0); |
| 219 | while (isspace(end[-1])) |
| 220 | end--; |
| 221 | *end = 0; |
| 222 | |
| 223 | if (*anchor) |
| 224 | append_line(*anchor, p); |
| 225 | else |
| 226 | *anchor = new_line(p); |
| 227 | } |
| 228 | } |
| 229 | |
| 230 | |
| 231 | /* ----- Associate library entries with tree nodes ------------------------- */ |
| 232 | |
| 233 | |
| 234 | void set_libs(const struct lib *lib, struct node *node) |
| 235 | { |
| 236 | while (node) { |
| 237 | if (!node->child) { |
| 238 | node->e = lookup_sym(lib, node->name); |
| 239 | if (!node->e) { |
| 240 | fprintf(stderr, "symbol %s not found\n", |
| 241 | node->name); |
| 242 | exit(1); |
| 243 | } |
| 244 | } |
| 245 | set_libs(lib, node->child); |
| 246 | node = node->next; |
| 247 | } |
| 248 | } |
| 249 | |
| 250 | |
| 251 | /* ----- Debug dumps ------------------------------------------------------- */ |
| 252 | |
| 253 | |
| 254 | static void dump_tree_level(const struct node *tree, int level) |
| 255 | { |
| 256 | const struct node *n; |
| 257 | const struct line *line; |
| 258 | const struct name *name; |
| 259 | |
| 260 | for (n = tree; n; n = n->next) { |
| 261 | printf("%*s%s", 4*level, "", n->name); |
| 262 | if (n->e) { |
| 263 | for (name = n->e->names; name; name = name->next) |
| 264 | printf("%s%s", |
| 265 | name == n->e->names ? " (" : ", ", name->s); |
| 266 | printf(")"); |
| 267 | } |
| 268 | printf("\n"); |
| 269 | for (line = n->comment; line; line = line->next) |
| 270 | printf("%*s\"%s\"\n", 4*level+2, "", line->s); |
| 271 | dump_tree_level(n->child, level+1); |
| 272 | } |
| 273 | } |
| 274 | |
| 275 | |
| 276 | void dump_tree(void) |
| 277 | { |
| 278 | dump_tree_level(tree, 0); |
| 279 | } |
| 280 | |
| 281 | |
| 282 | static void dump_comp_level(const struct node *tree) |
| 283 | { |
| 284 | const struct node *n; |
| 285 | |
| 286 | for (n = tree; n; n = n->next) { |
| 287 | if (n->e) |
| 288 | printf("%s\n", n->e->names->s); |
| 289 | dump_comp_level(n->child); |
| 290 | } |
| 291 | } |
| 292 | |
| 293 | |
| 294 | void dump_comp(void) |
| 295 | { |
| 296 | dump_comp_level(tree); |
| 297 | } |
| 298 |
Branches:
master
