paul@214 | 1 | /* |
paul@214 | 2 | * File parsing and importing. |
paul@214 | 3 | * |
paul@214 | 4 | * Copyright (C) 2022 Paul Boddie <paul@boddie.org.uk> |
paul@214 | 5 | * |
paul@214 | 6 | * This program is free software; you can redistribute it and/or |
paul@214 | 7 | * modify it under the terms of the GNU General Public License as |
paul@214 | 8 | * published by the Free Software Foundation; either version 2 of |
paul@214 | 9 | * the License, or (at your option) any later version. |
paul@214 | 10 | * |
paul@214 | 11 | * This program is distributed in the hope that it will be useful, |
paul@214 | 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
paul@214 | 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
paul@214 | 14 | * GNU General Public License for more details. |
paul@214 | 15 | * |
paul@214 | 16 | * You should have received a copy of the GNU General Public License |
paul@214 | 17 | * along with this program; if not, write to the Free Software |
paul@214 | 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, |
paul@214 | 19 | * Boston, MA 02110-1301, USA |
paul@214 | 20 | */ |
paul@214 | 21 | |
paul@214 | 22 | #include <stdio.h> |
paul@214 | 23 | #include <stdlib.h> |
paul@214 | 24 | #include <string.h> |
paul@214 | 25 | #include "common.h" |
paul@214 | 26 | #include "config.h" |
paul@214 | 27 | #include "imports.h" |
paul@214 | 28 | #include "program.h" |
paul@214 | 29 | |
paul@214 | 30 | |
paul@214 | 31 | |
paul@214 | 32 | /* Parser functions and settings. */ |
paul@214 | 33 | |
paul@214 | 34 | extern void yyparse(void); |
paul@214 | 35 | extern void yyrestart(FILE *); |
paul@214 | 36 | extern int yydebug; |
paul@214 | 37 | |
paul@214 | 38 | /* Specialised parser functions. */ |
paul@214 | 39 | |
paul@214 | 40 | extern void reset(const char *dirname, const char *basename); |
paul@214 | 41 | |
paul@214 | 42 | /* Parsing products. */ |
paul@214 | 43 | |
paul@214 | 44 | extern struct interface *last_interface; |
paul@214 | 45 | |
paul@214 | 46 | /* Input directory. */ |
paul@214 | 47 | |
paul@214 | 48 | static char *input_dirname = NULL; |
paul@214 | 49 | |
paul@214 | 50 | |
paul@214 | 51 | |
paul@214 | 52 | /* Obtain the basename and directory name of generated output. */ |
paul@214 | 53 | |
paul@214 | 54 | static int get_output_location(const char *filename, char **dirname, char **basename) |
paul@214 | 55 | { |
paul@214 | 56 | char *ext; |
paul@214 | 57 | |
paul@214 | 58 | /* Obtain the basename of the output prefix. */ |
paul@214 | 59 | |
paul@214 | 60 | *basename = make_basename(filename); |
paul@214 | 61 | |
paul@214 | 62 | if (*basename == NULL) |
paul@214 | 63 | return 0; |
paul@214 | 64 | |
paul@214 | 65 | /* Truncate the basename to remove the extension. */ |
paul@214 | 66 | |
paul@214 | 67 | ext = strrchr(*basename, '.'); |
paul@214 | 68 | |
paul@214 | 69 | if (ext != NULL) |
paul@214 | 70 | *ext = '\0'; |
paul@214 | 71 | |
paul@214 | 72 | /* Obtain the directory name from the output prefix. */ |
paul@214 | 73 | |
paul@214 | 74 | if (conf.output_dir == NULL) |
paul@214 | 75 | *dirname = make_dirname(filename); |
paul@214 | 76 | else |
paul@214 | 77 | *dirname = conf.output_dir; |
paul@214 | 78 | |
paul@214 | 79 | if (*dirname == NULL) |
paul@214 | 80 | { |
paul@214 | 81 | free(*basename); |
paul@214 | 82 | return 0; |
paul@214 | 83 | } |
paul@214 | 84 | |
paul@214 | 85 | return 1; |
paul@214 | 86 | } |
paul@214 | 87 | |
paul@214 | 88 | /* Obtain a suitable filename from the parsed import details. */ |
paul@214 | 89 | |
paul@214 | 90 | static struct interface *import_interface_file(const char *quoted_filename) |
paul@214 | 91 | { |
paul@214 | 92 | /* Allocate a string for the quoted filename minus quotes. */ |
paul@214 | 93 | |
paul@214 | 94 | size_t length = strlen(quoted_filename) - 1; |
paul@214 | 95 | char filename[length]; |
paul@214 | 96 | |
paul@214 | 97 | /* For full paths, allocate enough space for both the dirname and the |
paul@214 | 98 | filename. */ |
paul@214 | 99 | |
paul@214 | 100 | char pathname[strlen(input_dirname) + 1 + length]; |
paul@214 | 101 | |
paul@214 | 102 | /* Strip quotes from the filename token. */ |
paul@214 | 103 | |
paul@214 | 104 | strncpy(filename, quoted_filename + 1, length - 1); |
paul@214 | 105 | filename[length - 1] = '\0'; |
paul@214 | 106 | |
paul@214 | 107 | /* Introduce the same dirname as the original input file. |
paul@214 | 108 | NOTE: This should probably use a search path. */ |
paul@214 | 109 | |
paul@214 | 110 | if (is_basename(filename)) |
paul@214 | 111 | { |
paul@214 | 112 | sprintf(pathname, "%s/%s", input_dirname, filename); |
paul@214 | 113 | return import_file(pathname, 0); |
paul@214 | 114 | } |
paul@214 | 115 | else |
paul@214 | 116 | return import_file(filename, 0); |
paul@214 | 117 | } |
paul@214 | 118 | |
paul@214 | 119 | /* Resolve imports in the interface description. */ |
paul@214 | 120 | |
paul@214 | 121 | static int resolve_imports(struct interface *iface) |
paul@214 | 122 | { |
paul@214 | 123 | struct import *imp; |
paul@214 | 124 | |
paul@214 | 125 | for (; iface != NULL; iface = iface->tail) |
paul@214 | 126 | { |
paul@214 | 127 | /* Traverse the imports list to resolve imports. */ |
paul@214 | 128 | |
paul@214 | 129 | for (imp = iface->imports; imp != NULL; imp = imp->tail) |
paul@214 | 130 | { |
paul@214 | 131 | imp->iface = import_interface_file(imp->filename); |
paul@214 | 132 | |
paul@214 | 133 | if (imp->iface == NULL) |
paul@214 | 134 | return 0; |
paul@214 | 135 | } |
paul@214 | 136 | } |
paul@214 | 137 | |
paul@214 | 138 | return 1; |
paul@214 | 139 | } |
paul@214 | 140 | |
paul@214 | 141 | /* Populate an interface reference if the base is found. */ |
paul@214 | 142 | |
paul@214 | 143 | static int populate_base(struct interface_ref *base, struct interface *iface) |
paul@214 | 144 | { |
paul@214 | 145 | if (!strcmp(iface->name, base->name)) |
paul@214 | 146 | { |
paul@214 | 147 | base->iface = iface; |
paul@214 | 148 | return 1; |
paul@214 | 149 | } |
paul@214 | 150 | else |
paul@214 | 151 | return 0; |
paul@214 | 152 | } |
paul@214 | 153 | |
paul@214 | 154 | /* Find a base interface definition. */ |
paul@214 | 155 | |
paul@214 | 156 | static int find_base_in_imports(struct interface_ref *base, struct import *imp) |
paul@214 | 157 | { |
paul@214 | 158 | struct interface *iface; |
paul@214 | 159 | |
paul@214 | 160 | for (; imp != NULL; imp = imp->tail) |
paul@214 | 161 | { |
paul@214 | 162 | for (iface = imp->iface; iface != NULL; iface = iface->tail) |
paul@214 | 163 | { |
paul@214 | 164 | if (populate_base(base, iface)) |
paul@214 | 165 | return 1; |
paul@214 | 166 | |
paul@214 | 167 | /* Investigate imports related to an interface. */ |
paul@214 | 168 | |
paul@214 | 169 | if (find_base_in_imports(base, iface->imports)) |
paul@214 | 170 | return 1; |
paul@214 | 171 | } |
paul@214 | 172 | } |
paul@214 | 173 | |
paul@214 | 174 | return 0; |
paul@214 | 175 | } |
paul@214 | 176 | |
paul@214 | 177 | /* Find a base interface in the current file or in imports. */ |
paul@214 | 178 | |
paul@214 | 179 | static int find_base(struct interface_ref *base, struct interface *iface) |
paul@214 | 180 | { |
paul@214 | 181 | struct interface *preceding; |
paul@214 | 182 | |
paul@214 | 183 | /* Search preceding interfaces in the same file. */ |
paul@214 | 184 | |
paul@214 | 185 | for (preceding = iface->tail; preceding != NULL; preceding = preceding->tail) |
paul@214 | 186 | { |
paul@214 | 187 | if (populate_base(base, preceding)) |
paul@214 | 188 | return 1; |
paul@214 | 189 | } |
paul@214 | 190 | |
paul@214 | 191 | /* Search imported files. */ |
paul@214 | 192 | |
paul@214 | 193 | if (find_base_in_imports(base, iface->imports)) |
paul@214 | 194 | return 1; |
paul@214 | 195 | |
paul@214 | 196 | fprintf(stderr, "Could not locate base interface %s used by interface %s.\n", |
paul@214 | 197 | base->name, iface->name); |
paul@214 | 198 | return 0; |
paul@214 | 199 | } |
paul@214 | 200 | |
paul@214 | 201 | /* Populate any base interfaces by traversing all interfaces and searching for |
paul@214 | 202 | the base interface definitions. */ |
paul@214 | 203 | |
paul@214 | 204 | static int populate_bases(struct interface *iface) |
paul@214 | 205 | { |
paul@214 | 206 | struct interface_ref *base; |
paul@214 | 207 | |
paul@214 | 208 | /* Traverse all interfaces. */ |
paul@214 | 209 | |
paul@214 | 210 | for (; iface != NULL; iface = iface->tail) |
paul@214 | 211 | { |
paul@214 | 212 | /* Check all bases. */ |
paul@214 | 213 | |
paul@214 | 214 | for (base = iface->bases; base != NULL; base = base->tail) |
paul@214 | 215 | { |
paul@214 | 216 | if (!find_base(base, iface)) |
paul@214 | 217 | return 0; |
paul@214 | 218 | } |
paul@214 | 219 | } |
paul@214 | 220 | |
paul@214 | 221 | return 1; |
paul@214 | 222 | } |
paul@214 | 223 | |
paul@214 | 224 | |
paul@214 | 225 | |
paul@214 | 226 | /* Return the last interface from the parsing of the given file, or NULL if |
paul@214 | 227 | parsing or importing failed. */ |
paul@214 | 228 | |
paul@214 | 229 | struct interface *import_file(const char *filename, int top_level) |
paul@214 | 230 | { |
paul@214 | 231 | struct interface *result; |
paul@214 | 232 | FILE *fp; |
paul@214 | 233 | char *dirname, *basename; |
paul@214 | 234 | |
paul@214 | 235 | /* Obtain output location details for interfaces within a file. */ |
paul@214 | 236 | |
paul@214 | 237 | if (!get_output_location(filename, &dirname, &basename)) |
paul@214 | 238 | return NULL; |
paul@214 | 239 | |
paul@214 | 240 | /* Record the input dirname using the original input file. */ |
paul@214 | 241 | |
paul@214 | 242 | if (input_dirname == NULL) |
paul@214 | 243 | { |
paul@214 | 244 | input_dirname = make_dirname(filename); |
paul@214 | 245 | if (input_dirname == NULL) |
paul@214 | 246 | return NULL; |
paul@214 | 247 | } |
paul@214 | 248 | |
paul@214 | 249 | fp = fopen(filename, "r"); |
paul@214 | 250 | |
paul@214 | 251 | if (fp == NULL) |
paul@214 | 252 | { |
paul@214 | 253 | fprintf(stderr, "Could not open file: %s\n", filename); |
paul@214 | 254 | return NULL; |
paul@214 | 255 | } |
paul@214 | 256 | |
paul@214 | 257 | /* Reset any specialised parser state and set the prefix to annotate parsing |
paul@214 | 258 | products. */ |
paul@214 | 259 | |
paul@214 | 260 | reset(dirname, basename); |
paul@214 | 261 | |
paul@214 | 262 | yyrestart(fp); |
paul@214 | 263 | yyparse(); |
paul@214 | 264 | fclose(fp); |
paul@214 | 265 | |
paul@214 | 266 | /* Obtain the parsing products and process them. */ |
paul@214 | 267 | |
paul@214 | 268 | result = last_interface; |
paul@214 | 269 | |
paul@214 | 270 | if (!resolve_imports(result)) |
paul@214 | 271 | return NULL; |
paul@214 | 272 | |
paul@214 | 273 | if (!populate_bases(result)) |
paul@214 | 274 | return NULL; |
paul@214 | 275 | |
paul@214 | 276 | /* Either report basenames of processed files... */ |
paul@214 | 277 | |
paul@214 | 278 | if (conf.show_filenames) |
paul@214 | 279 | printf("%s\n", basename); |
paul@214 | 280 | |
paul@214 | 281 | /* Or write files where directed. */ |
paul@214 | 282 | |
paul@214 | 283 | else |
paul@214 | 284 | if (top_level || conf.generate_all) |
paul@214 | 285 | write_files(result); |
paul@214 | 286 | |
paul@214 | 287 | return result; |
paul@214 | 288 | } |