1 /* 2 * Main program. 3 * 4 * Copyright (C) 2019 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 <getopt.h> 23 #include <stdio.h> 24 #include <stdlib.h> 25 #include <string.h> 26 #include <strings.h> 27 #include "common.h" 28 #include "config.h" 29 #include "program.h" 30 #include "templates.h" 31 #include "version.h" 32 33 /* Parser functions and settings. */ 34 35 extern void yyparse(void); 36 extern void yyrestart(FILE *); 37 extern int yydebug; 38 39 /* Specialised parser functions. */ 40 41 extern void reset(void); 42 43 44 45 /* Return a copy of the given string without any extension. */ 46 47 static char *make_prefix(const char *s) 48 { 49 char *copy = strdup(s); 50 char *ext; 51 52 if (copy == NULL) 53 return NULL; 54 55 ext = strrchr(copy, '.'); 56 57 if (ext != NULL) 58 *ext = '\0'; 59 60 return copy; 61 } 62 63 static int get_compound_prefix_conflict(int argc, char *argv[]) 64 { 65 char *arg, *prefix, *name; 66 int index, result; 67 68 for (index = optind; index < argc; index++) 69 { 70 arg = argv[index]; 71 prefix = make_prefix(arg); 72 result = 0; 73 74 if (prefix != NULL) 75 { 76 name = make_basename(prefix); 77 78 if (name != NULL) 79 { 80 result = strcasecmp(name, conf.compound); 81 free(name); 82 } 83 84 free(prefix); 85 } 86 87 if (!result) 88 return index; 89 } 90 91 return 0; 92 } 93 94 95 /* Option definitions. */ 96 97 static struct option long_options[] = { 98 /* long opt following var pointer short opt */ 99 {"all", no_argument, NULL, 'a' }, 100 {"client", no_argument, NULL, 'c' }, 101 {"comp", required_argument, NULL, 'C' }, 102 {"dir", required_argument, NULL, 'd' }, 103 {"headers", no_argument, NULL, 'h' }, 104 {"help", no_argument, NULL, '?' }, 105 {"interfaces", no_argument, NULL, 'i' }, 106 {"language", required_argument, NULL, 'l' }, 107 {"comp-name", required_argument, NULL, 'N' }, 108 {"routines", no_argument, NULL, 'r' }, 109 {"server", no_argument, NULL, 's' }, 110 {"verbose", no_argument, NULL, 'v' }, 111 {"version", no_argument, NULL, 'V' }, 112 {0, 0, 0, 0 }, 113 }; 114 115 116 /* Main program, parsing indicated files or standard input. */ 117 118 int main(int argc, char *argv[]) 119 { 120 FILE *fp; 121 char *arg, *progname; 122 int option, index, selected_content = 0, selected_role = 0; 123 124 #if YYDEBUG == 1 125 yydebug = 0; 126 #endif 127 128 /* Accept various options. */ 129 130 while (1) 131 { 132 option = getopt_long(argc, argv, "acC:d:hil:N:rsvV?", long_options, NULL); 133 if (option == -1) 134 break; 135 136 switch (option) 137 { 138 /* Generate everything. */ 139 case 'a': 140 conf.client = 1; conf.server = 1; 141 conf.headers = 1; conf.interfaces = 1; conf.routines = 1; 142 selected_content = 1; 143 selected_role = 1; 144 break; 145 146 /* Select client output. */ 147 case 'c': 148 conf.client = 1; 149 selected_role = 1; 150 break; 151 152 /* Select compound interface filename prefix and output. */ 153 case 'C': 154 conf.compound = optarg; 155 if (conf.compound_name == NULL) 156 conf.compound_name = conf.compound; 157 selected_role = 1; 158 break; 159 160 /* Set output directory. */ 161 case 'd': 162 conf.output_dir = optarg; 163 break; 164 165 /* Generate headers. */ 166 case 'h': 167 conf.headers = 1; 168 selected_content = 1; 169 break; 170 171 /* Generate interface headers. */ 172 case 'i': 173 conf.interfaces = 1; 174 selected_content = 1; 175 break; 176 177 /* Select output language. */ 178 case 'l': 179 if (!strcasecmp(optarg, "c")) 180 conf.language = C_LANGUAGE; 181 else if (!strcasecmp(optarg, "c++")) 182 conf.language = CPP_LANGUAGE; 183 else 184 { 185 fprintf(stderr, "Output language not recognised: %s\n", optarg); 186 return 1; 187 } 188 break; 189 190 /* Select compound interface name and output. */ 191 case 'N': 192 conf.compound_name = optarg; 193 if (conf.compound == NULL) 194 conf.compound = conf.compound_name; 195 selected_role = 1; 196 break; 197 198 /* Generate routines (definitions). */ 199 case 'r': 200 conf.routines = 1; 201 selected_content = 1; 202 break; 203 204 /* Select server output. */ 205 case 's': 206 conf.server = 1; 207 selected_role = 1; 208 break; 209 210 /* Verbose reporting. */ 211 case 'v': 212 conf.verbose = 1; 213 break; 214 215 /* Show version (defined in the Makefile) and exit. */ 216 case 'V': 217 puts(VERSION_INFORMATION); 218 return 0; 219 220 /* Show help and exit. */ 221 case '?': 222 progname = make_basename(argv[0]); 223 printf(help_text, progname); 224 free(progname); 225 return 0; 226 227 default: 228 return 1; 229 } 230 } 231 232 /* Without any output selection, enable all output of the given kind. */ 233 234 if (!selected_content) 235 { 236 conf.headers = 1; conf.interfaces = 1; conf.routines = 1; 237 } 238 239 if (!selected_role) 240 { 241 conf.client = 1; conf.server = 1; 242 } 243 244 /* Check any compound interface prefix against named files. */ 245 246 if (conf.compound != NULL) 247 { 248 if ((index = get_compound_prefix_conflict(argc, argv))) 249 { 250 fprintf(stderr, "Compound interface prefix %s conflicts with filename %s.\n", 251 conf.compound, argv[index]); 252 return 1; 253 } 254 } 255 256 /* Produce an error without any input files. */ 257 258 if (optind >= argc) 259 { 260 fputs("No input files.\n", stderr); 261 return 1; 262 } 263 264 /* Begin generating any compound interface code. */ 265 266 if (!begin_compound_output()) 267 { 268 end_compound_output(); 269 return 1; 270 } 271 272 /* Process named files. */ 273 274 for (index = optind; index < argc; index++) 275 { 276 arg = argv[index]; 277 278 /* Handle actual files. */ 279 280 conf.output_prefix = make_prefix(arg); 281 282 if (conf.output_prefix != NULL) 283 { 284 fp = fopen(arg, "r"); 285 286 if (fp != NULL) 287 { 288 yyrestart(fp); 289 yyparse(); 290 fclose(fp); 291 292 /* Reset any specialised parser state. */ 293 294 reset(); 295 } 296 297 free(conf.output_prefix); 298 299 if (fp == NULL) 300 { 301 fprintf(stderr, "Could not open file: %s\n", arg); 302 return 1; 303 } 304 } 305 } 306 307 /* End generating any compound interface code. */ 308 309 end_compound_output(); 310 311 return 0; 312 }