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