paul@14 | 1 | /* |
paul@100 | 2 | * Code generation from interface descriptions. |
paul@28 | 3 | * |
paul@210 | 4 | * Copyright (C) 2019, 2020, 2022 Paul Boddie <paul@boddie.org.uk> |
paul@28 | 5 | * |
paul@28 | 6 | * This program is free software; you can redistribute it and/or |
paul@28 | 7 | * modify it under the terms of the GNU General Public License as |
paul@28 | 8 | * published by the Free Software Foundation; either version 2 of |
paul@28 | 9 | * the License, or (at your option) any later version. |
paul@28 | 10 | * |
paul@28 | 11 | * This program is distributed in the hope that it will be useful, |
paul@28 | 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
paul@28 | 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
paul@28 | 14 | * GNU General Public License for more details. |
paul@28 | 15 | * |
paul@28 | 16 | * You should have received a copy of the GNU General Public License |
paul@28 | 17 | * along with this program; if not, write to the Free Software |
paul@28 | 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, |
paul@28 | 19 | * Boston, MA 02110-1301, USA |
paul@14 | 20 | */ |
paul@14 | 21 | |
paul@14 | 22 | #include <stdlib.h> |
paul@100 | 23 | #include "client.h" |
paul@44 | 24 | #include "common.h" |
paul@126 | 25 | #include "config.h" |
paul@100 | 26 | #include "declaration.h" |
paul@99 | 27 | #include "dispatch.h" |
paul@112 | 28 | #include "includes.h" |
paul@113 | 29 | #include "interface.h" |
paul@98 | 30 | #include "message.h" |
paul@48 | 31 | #include "program.h" |
paul@100 | 32 | #include "server.h" |
paul@67 | 33 | #include "templates.h" |
paul@14 | 34 | |
paul@39 | 35 | |
paul@72 | 36 | |
paul@87 | 37 | /* Client, server and common files. */ |
paul@87 | 38 | |
paul@87 | 39 | FILE *client_fp = NULL, *client_header_fp = NULL, |
paul@87 | 40 | *server_fp = NULL, *server_header_fp = NULL, |
paul@120 | 41 | *interface_fp = NULL; |
paul@87 | 42 | |
paul@56 | 43 | |
paul@56 | 44 | |
paul@214 | 45 | /* Generate functions corresponding to the signatures. */ |
paul@56 | 46 | |
paul@214 | 47 | static void write_functions(struct signature *sig, FILE *fp, |
paul@214 | 48 | struct interface *iface, |
paul@214 | 49 | void (*write_function)(struct signature *, FILE *, struct interface *)) |
paul@214 | 50 | { |
paul@214 | 51 | if (sig == NULL) |
paul@214 | 52 | return; |
paul@90 | 53 | |
paul@214 | 54 | write_function(sig, fp, iface); |
paul@214 | 55 | write_functions(sig->tail, fp, iface, write_function); |
paul@56 | 56 | } |
paul@56 | 57 | |
paul@214 | 58 | /* Write includes for base interfaces. */ |
paul@162 | 59 | |
paul@214 | 60 | static void write_server_includes(struct interface *iface, FILE *fp) |
paul@56 | 61 | { |
paul@214 | 62 | struct interface_ref *base; |
paul@210 | 63 | |
paul@214 | 64 | for (base = iface->bases; base != NULL; base = base->tail) |
paul@214 | 65 | write_include(base->iface->output_basename, "_server.h", fp); |
paul@56 | 66 | } |
paul@56 | 67 | |
paul@50 | 68 | |
paul@50 | 69 | |
paul@33 | 70 | /* Generate files containing interface details. */ |
paul@33 | 71 | |
paul@33 | 72 | void write_files(struct interface *iface) |
paul@33 | 73 | { |
paul@110 | 74 | char *filename; |
paul@41 | 75 | |
paul@126 | 76 | if (conf.headers) |
paul@39 | 77 | { |
paul@126 | 78 | if (conf.client) |
paul@45 | 79 | { |
paul@214 | 80 | client_header_fp = get_output_file(client_header_filename, |
paul@214 | 81 | iface->output_dirname, |
paul@214 | 82 | iface->output_basename); |
paul@45 | 83 | if (client_header_fp == NULL) |
paul@45 | 84 | goto finalisation; |
paul@45 | 85 | } |
paul@45 | 86 | |
paul@126 | 87 | if (conf.server) |
paul@45 | 88 | { |
paul@214 | 89 | server_header_fp = get_output_file(server_header_filename, |
paul@214 | 90 | iface->output_dirname, |
paul@214 | 91 | iface->output_basename); |
paul@45 | 92 | if (server_header_fp == NULL) |
paul@45 | 93 | goto finalisation; |
paul@45 | 94 | } |
paul@45 | 95 | } |
paul@45 | 96 | |
paul@126 | 97 | if (conf.interfaces) |
paul@86 | 98 | { |
paul@214 | 99 | interface_fp = get_output_file(interface_filename, iface->output_dirname, |
paul@214 | 100 | iface->output_basename); |
paul@119 | 101 | if (interface_fp == NULL) |
paul@119 | 102 | goto finalisation; |
paul@86 | 103 | } |
paul@86 | 104 | |
paul@126 | 105 | if (conf.routines) |
paul@45 | 106 | { |
paul@126 | 107 | if (conf.client) |
paul@45 | 108 | { |
paul@214 | 109 | filename = (conf.language == CPP_LANGUAGE) ? client_filename_cpp |
paul@214 | 110 | : client_filename_c; |
paul@214 | 111 | client_fp = get_output_file(filename, iface->output_dirname, |
paul@214 | 112 | iface->output_basename); |
paul@45 | 113 | if (client_fp == NULL) |
paul@45 | 114 | goto finalisation; |
paul@45 | 115 | } |
paul@45 | 116 | |
paul@126 | 117 | if (conf.server) |
paul@45 | 118 | { |
paul@214 | 119 | filename = (conf.language == CPP_LANGUAGE) ? server_filename_cpp |
paul@214 | 120 | : server_filename_c; |
paul@214 | 121 | server_fp = get_output_file(filename, iface->output_dirname, |
paul@214 | 122 | iface->output_basename); |
paul@45 | 123 | if (server_fp == NULL) |
paul@56 | 124 | goto finalisation; |
paul@45 | 125 | } |
paul@39 | 126 | } |
paul@35 | 127 | |
paul@35 | 128 | /* Emit prologues. */ |
paul@35 | 129 | |
paul@45 | 130 | if (client_fp != NULL) |
paul@214 | 131 | fprintf(client_fp, client_prologue, iface->output_basename, |
paul@214 | 132 | iface->output_basename); |
paul@45 | 133 | |
paul@45 | 134 | if (client_header_fp != NULL) |
paul@45 | 135 | fputs(header_prologue, client_header_fp); |
paul@35 | 136 | |
paul@45 | 137 | if (server_fp != NULL) |
paul@214 | 138 | fprintf(server_fp, server_prologue, iface->output_basename, |
paul@214 | 139 | iface->output_basename); |
paul@45 | 140 | |
paul@45 | 141 | if (server_header_fp != NULL) |
paul@71 | 142 | fputs(server_header_prologue, server_header_fp); |
paul@71 | 143 | |
paul@119 | 144 | if (interface_fp != NULL) |
paul@119 | 145 | fputs(header_prologue, interface_fp); |
paul@35 | 146 | |
paul@35 | 147 | /* Write the interface details. */ |
paul@33 | 148 | |
paul@87 | 149 | write_interfaces(iface); |
paul@45 | 150 | |
paul@45 | 151 | finalisation: |
paul@35 | 152 | |
paul@35 | 153 | /* Close the files. */ |
paul@35 | 154 | |
paul@45 | 155 | if (client_fp != NULL) |
paul@45 | 156 | fclose(client_fp); |
paul@45 | 157 | |
paul@45 | 158 | if (client_header_fp != NULL) |
paul@45 | 159 | fclose(client_header_fp); |
paul@35 | 160 | |
paul@45 | 161 | if (server_fp != NULL) |
paul@45 | 162 | fclose(server_fp); |
paul@45 | 163 | |
paul@45 | 164 | if (server_header_fp != NULL) |
paul@45 | 165 | fclose(server_header_fp); |
paul@35 | 166 | |
paul@119 | 167 | if (interface_fp != NULL) |
paul@119 | 168 | fclose(interface_fp); |
paul@33 | 169 | } |
paul@33 | 170 | |
paul@214 | 171 | /* Generate source file content corresponding to each interface in turn. */ |
paul@14 | 172 | |
paul@87 | 173 | void write_interfaces(struct interface *iface) |
paul@14 | 174 | { |
paul@51 | 175 | if (iface == NULL) |
paul@51 | 176 | return; |
paul@51 | 177 | |
paul@214 | 178 | /* The list is reversed by recursing to subsequent interfaces and thus |
paul@214 | 179 | processing them first. */ |
paul@33 | 180 | |
paul@87 | 181 | write_interfaces(iface->tail); |
paul@14 | 182 | |
paul@46 | 183 | /* Emit function definitions. */ |
paul@14 | 184 | |
paul@45 | 185 | if (client_fp != NULL) |
paul@117 | 186 | { |
paul@214 | 187 | /* Do not write client definitions for compound interfaces. */ |
paul@14 | 188 | |
paul@214 | 189 | if (!is_compound_interface(iface)) |
paul@214 | 190 | { |
paul@214 | 191 | write_functions(iface->signatures, client_fp, iface, |
paul@214 | 192 | write_client_function); |
paul@214 | 193 | |
paul@214 | 194 | if (conf.language == C_LANGUAGE) |
paul@214 | 195 | write_client_interface(iface, client_fp); |
paul@214 | 196 | } |
paul@117 | 197 | } |
paul@117 | 198 | |
paul@46 | 199 | if (server_fp != NULL) |
paul@52 | 200 | { |
paul@214 | 201 | /* Write the dispatcher and handler functions. */ |
paul@214 | 202 | |
paul@46 | 203 | write_functions(iface->signatures, server_fp, iface, |
paul@46 | 204 | write_server_function); |
paul@14 | 205 | |
paul@106 | 206 | /* Emit handle and dispatch functions. */ |
paul@106 | 207 | |
paul@117 | 208 | write_handler_signature(iface->name, DEFINITION_ROLE, server_fp); |
paul@106 | 209 | fprintf(server_fp, handle_function, iface->name); |
paul@117 | 210 | fputs(END_FUNCTION, server_fp); |
paul@106 | 211 | |
paul@214 | 212 | write_dispatcher(server_fp, iface); |
paul@210 | 213 | |
paul@210 | 214 | /* Emit default configuration instance definition. */ |
paul@210 | 215 | |
paul@210 | 216 | fprintf(server_fp, server_config_instance, |
paul@210 | 217 | iface->name, iface->name, iface->name); |
paul@52 | 218 | } |
paul@52 | 219 | |
paul@14 | 220 | /* Emit the headers. */ |
paul@14 | 221 | |
paul@45 | 222 | if (client_header_fp != NULL) |
paul@115 | 223 | { |
paul@116 | 224 | /* Emit interface include to obtain the type employed by the client |
paul@214 | 225 | interface, doing this once per file. */ |
paul@214 | 226 | |
paul@214 | 227 | if (iface->tail == NULL) |
paul@214 | 228 | write_include(iface->output_basename, "_interface.h", client_header_fp); |
paul@115 | 229 | |
paul@214 | 230 | /* Do not write client definitions for compound interfaces. */ |
paul@115 | 231 | |
paul@214 | 232 | if (!is_compound_interface(iface)) |
paul@214 | 233 | write_interface_definition(iface, CLIENT_ROLE, client_header_fp); |
paul@115 | 234 | } |
paul@46 | 235 | |
paul@46 | 236 | if (server_header_fp != NULL) |
paul@46 | 237 | { |
paul@214 | 238 | /* Emit interface include to obtain the interface type, doing this once per |
paul@214 | 239 | file. */ |
paul@90 | 240 | |
paul@214 | 241 | if (iface->tail == NULL) |
paul@214 | 242 | write_include(iface->output_basename, "_interface.h", server_header_fp); |
paul@90 | 243 | |
paul@217 | 244 | /* Write any includes for base interfaces. */ |
paul@217 | 245 | |
paul@217 | 246 | write_server_includes(iface, server_header_fp); |
paul@217 | 247 | |
paul@46 | 248 | /* Emit signatures. */ |
paul@46 | 249 | |
paul@117 | 250 | write_signatures(iface->signatures, DECLARATION_ROLE, server_header_fp, |
paul@117 | 251 | iface, write_server_signature); |
paul@52 | 252 | |
paul@106 | 253 | /* Emit dispatch and handle function signatures. */ |
paul@106 | 254 | |
paul@110 | 255 | write_dispatcher_signature(iface->name, DECLARATION_ROLE, server_header_fp); |
paul@110 | 256 | write_handler_signature(iface->name, DECLARATION_ROLE, server_header_fp); |
paul@210 | 257 | |
paul@210 | 258 | /* Emit default configuration instance declaration. */ |
paul@210 | 259 | |
paul@210 | 260 | fprintf(server_header_fp, server_config_instance_declaration, iface->name); |
paul@32 | 261 | } |
paul@32 | 262 | |
paul@120 | 263 | /* The server interface is used by client and server code. */ |
paul@120 | 264 | |
paul@119 | 265 | if (interface_fp != NULL) |
paul@119 | 266 | write_interface_definition(iface, SERVER_ROLE, interface_fp); |
paul@14 | 267 | } |