Added spec generation tool specmaker.
[wine/hacks.git] / tools / specmaker / output.c
blobb966a24dfb6747e4d1bfb5dba2a2716e9ae30ba7
1 /*
2 * Code generation functions
4 * Copyright 2000 Jon Griffiths
5 */
6 #include "specmaker.h"
8 /* Output files */
9 static FILE *specfile = NULL;
10 static FILE *hfile = NULL;
11 static FILE *cfile = NULL;
13 static void output_spec_postamble (void);
14 static void output_header_postamble (void);
15 static void output_c_postamble (void);
16 static void output_prototype (FILE *file, const parsed_symbol *sym);
17 static void output_c_banner (const parsed_symbol *sym);
18 static const char *get_format_str (int type);
19 static const char *get_in_or_out (const parsed_symbol *sym, size_t arg);
22 /*******************************************************************
23 * output_spec_preamble
25 * Write the first part of the .spec file
27 void output_spec_preamble (void)
29 specfile = open_file (OUTPUT_DLL_NAME, ".spec", "w");
31 atexit (output_spec_postamble);
33 if (VERBOSE)
34 puts ("Creating .spec preamble");
36 fprintf (specfile,
37 "# Generated from %s.dll by specmaker\nname %s\n"
38 "type win32\ninit %s_Init\n\nimport kernel32.dll\n"
39 "import ntdll.dll\n", globals.input_name, OUTPUT_DLL_NAME,
40 OUTPUT_UC_DLL_NAME);
42 if (globals.forward_dll)
43 fprintf (specfile,"#import %s.dll\n", globals.forward_dll);
45 fprintf (specfile, "\n\ndebug_channels (%s)\n\n", OUTPUT_DLL_NAME);
49 /*******************************************************************
50 * output_spec_symbol
52 * Write a symbol to the .spec file
54 void output_spec_symbol (const parsed_symbol *sym)
56 assert (specfile);
57 assert (sym && sym->symbol);
59 if (!globals.do_code || !sym->function_name)
61 if (globals.forward_dll)
62 fprintf (specfile, "@ forward %s %s.%s\n", sym->symbol,
63 globals.forward_dll, sym->symbol);
64 else
66 if (!symbol_is_valid_c (sym))
67 fputc ('#', specfile);
68 fprintf (specfile, "@ stub %s\n", sym->symbol);
71 else
73 unsigned int i;
75 fprintf (specfile, "@ %s %s(", sym->varargs ? "varargs" :
76 symbol_is_cdecl (sym) ? "cdecl" : "stdcall", sym->symbol);
78 for (i = 0; i < sym->argc; i++)
79 fprintf (specfile, " %s", symbol_get_spec_type(sym, i));
81 if (sym->argc)
82 fputc (' ', specfile);
83 fprintf (specfile, ") %s_%s\n", OUTPUT_UC_DLL_NAME, sym->function_name);
88 /*******************************************************************
89 * output_spec_postamble
91 * Write the last part of the .spec file
93 static void output_spec_postamble (void)
95 if (specfile)
96 fclose (specfile);
97 specfile = NULL;
101 /*******************************************************************
102 * output_header_preamble
104 * Write the first part of the .h file
106 void output_header_preamble (void)
108 hfile = open_file (OUTPUT_DLL_NAME, "_dll.h", "w");
110 atexit (output_header_postamble);
112 fprintf (hfile,
113 "/*\n * %s.dll\n *\n * Generated from %s.dll by specmaker.\n *\n"
114 " * DO NOT SEND GENERATED DLLS FOR INCLUSION INTO WINE !\n * \n */"
115 "\n#ifndef __WINE_%s_DLL_H\n#define __WINE_%s_DLL_H\n\n#include "
116 "\"config.h\"\n#include \"windef.h\"\n#include \"debugtools.h\"\n"
117 "#include \"winbase.h\"\n#include \"winnt.h\"\n\n\n",
118 OUTPUT_DLL_NAME, OUTPUT_DLL_NAME, OUTPUT_UC_DLL_NAME,
119 OUTPUT_UC_DLL_NAME);
123 /*******************************************************************
124 * output_header_symbol
126 * Write a symbol to the .h file
128 void output_header_symbol (const parsed_symbol *sym)
130 assert (hfile);
131 assert (sym && sym->symbol);
133 if (!globals.do_code)
134 return;
136 if (!sym->function_name)
137 fprintf (hfile, "/* %s %s_%s(); */\n", CALLING_CONVENTION,
138 OUTPUT_UC_DLL_NAME, sym->symbol);
139 else
141 output_prototype (hfile, sym);
142 fputs (";\n", hfile);
147 /*******************************************************************
148 * output_header_postamble
150 * Write the last part of the .h file
152 static void output_header_postamble (void)
154 if (hfile)
156 fprintf (hfile, "\n\n\n#endif\t/* __WINE_%s_DLL_H */\n",
157 OUTPUT_UC_DLL_NAME);
158 fclose (hfile);
159 hfile = NULL;
164 /*******************************************************************
165 * output_c_preamble
167 * Write the first part of the .c file
169 void output_c_preamble (void)
171 cfile = open_file (OUTPUT_DLL_NAME, "_main.c", "w");
173 atexit (output_c_postamble);
175 fprintf (cfile,
176 "/*\n * %s.dll\n *\n * Generated from %s.dll by specmaker.\n *\n"
177 " * DO NOT SUBMIT GENERATED DLLS FOR INCLUSION INTO WINE!\n * \n */"
178 "\n\n#include \"%s_dll.h\"\n\nDEFAULT_DEBUG_CHANNEL(%s);\n\n",
179 OUTPUT_DLL_NAME, globals.input_name, OUTPUT_DLL_NAME,
180 OUTPUT_DLL_NAME);
182 if (globals.forward_dll)
184 if (VERBOSE)
185 puts ("Creating a forwarding DLL");
187 fputs ("\nHMODULE hDLL=0;\t/* DLL to call */\n\n\n", cfile);
190 fprintf (cfile,
191 "BOOL WINAPI %s_Init(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID "
192 "lpvReserved)\n{\n\tTRACE(\"(0x%%08x, %%ld, %%p)\\n\",hinstDLL,"
193 "fdwReason,lpvReserved);\n\n\t"
194 "if (fdwReason == DLL_PROCESS_ATTACH)\n\t{\n\t\t",
195 OUTPUT_UC_DLL_NAME);
197 if (globals.forward_dll)
199 fprintf (cfile,
200 "hDLL = LoadLibraryA( \"%s\" );\n\t\t"
201 "TRACE(\":Forwarding DLL (%s) loaded (%%ld)\\n\",(LONG)hDLL);",
202 globals.forward_dll, globals.forward_dll);
204 else
205 fputs ("/* FIXME: Initialisation */", cfile);
207 fputs ("\n\t}\n\telse if (fdwReason == DLL_PROCESS_DETACH)\n\t{\n\t\t",
208 cfile);
210 if (globals.forward_dll)
212 fprintf (cfile,
213 "FreeLibrary( hDLL );\n\t\tTRACE(\":Forwarding DLL (%s)"
214 " freed\\n\");", globals.forward_dll);
216 else
217 fputs ("/* FIXME: Cleanup */", cfile);
219 fputs ("\n\t}\n\n\treturn TRUE;\n}\n\n\n", cfile);
223 /*******************************************************************
224 * output_c_symbol
226 * Write a symbol to the .c file
228 void output_c_symbol (const parsed_symbol *sym)
230 unsigned int i;
231 int is_void;
233 assert (cfile);
234 assert (sym && sym->symbol);
236 if (!globals.do_code)
237 return;
239 output_c_banner(sym);
241 if (!sym->function_name)
243 /* #ifdef'd dummy */
244 fprintf (cfile, "#if 0\n%s %s_%s()\n{\n\t%s in .spec */\n}\n#endif\n\n\n",
245 CALLING_CONVENTION, OUTPUT_UC_DLL_NAME, sym->symbol,
246 globals.forward_dll ? "/* @forward" : "/* @stub");
247 return;
250 is_void = !strcmp (sym->return_text, "void");
252 output_prototype (cfile, sym);
253 fputs ("\n{\n", cfile);
255 if (!globals.do_trace)
257 fputs ("\tFIXME(\":stub\\n\");\n", cfile);
258 if (!is_void)
259 fprintf (cfile, "\treturn (%s) 0;\n", sym->return_text);
260 fputs ("}\n\n\n", cfile);
261 return;
264 /* Tracing, maybe forwarding as well */
265 if (globals.forward_dll)
267 /* Write variables for calling */
268 fprintf (cfile, "\t%s (%s *pFunc)(", sym->return_text,
269 sym->calling_convention);
271 for (i = 0; i < sym->argc; i++)
272 fprintf (cfile, "%s%s", i ? ", " : "", sym->arg_text [i]);
274 fprintf (cfile, "%s)=(void*)GetProcAddress(hDLL,\"%s\");\n%s",
275 sym->varargs ? ",..." : sym->argc ? "" : "void", sym->symbol,
276 sym->varargs ? "\tva_list valist;\n" : "");
278 if (!is_void)
279 fprintf (cfile, "\t%s retVal;\n", sym->return_text);
282 /* TRACE input arguments */
283 fprintf (cfile, "\tTRACE(\"(%s", !sym->argc ? "void" : "");
285 for (i = 0; i < sym->argc; i++)
286 fprintf (cfile, "%s(%s)%s", i ? "," : "", sym->arg_text [i],
287 get_format_str (sym->arg_type [i]));
289 fprintf (cfile, "%s): %s\\n\"", sym->varargs ? ",..." : "",
290 globals.forward_dll ? "forward" : "stub");
292 for (i = 0; i < sym->argc; i++)
293 if (sym->arg_type[i] != ARG_STRUCT)
294 fprintf(cfile, ",%s%s%s%s", sym->arg_type[i] == ARG_LONG ? "(LONG)" : "",
295 sym->arg_type[i] == ARG_WIDE_STRING ? "debugstr_w(" : "",
296 sym->arg_name[i],
297 sym->arg_type[i] == ARG_WIDE_STRING ? ")" : "");
299 fputs (");\n", cfile);
301 if (!globals.forward_dll)
303 if (!is_void)
304 fprintf (cfile, "\treturn (%s) 0;\n", sym->return_text);
305 fputs ("}\n\n\n", cfile);
306 return;
309 /* Call the DLL */
310 if (sym->varargs)
311 fprintf (cfile, "\tva_start(valist,%s);\n", sym->arg_name[sym->argc-1]);
313 fprintf (cfile, "\t%spFunc(", !is_void ? "retVal = " : "");
315 for (i = 0; i < sym->argc; i++)
316 fprintf (cfile, "%s%s", i ? "," : "", sym->arg_name [i]);
318 fputs (sym->varargs ? ",valist);\n\tva_end(valist);" : ");", cfile);
320 /* TRACE return value */
321 fprintf (cfile, "\n\tTRACE(\"Returned (%s)\\n\"",
322 get_format_str (sym->return_type));
324 if (!is_void)
326 if (sym->return_type == ARG_WIDE_STRING)
327 fputs (",debugstr_w(retVal)", cfile);
328 else
329 fprintf (cfile, ",%s%s", sym->return_type == ARG_LONG ? "(LONG)" : "",
330 sym->return_type == ARG_STRUCT ? "" : "retVal");
331 fputs (");\n\treturn retVal;\n", cfile);
333 else
334 fputs (");\n", cfile);
336 fputs ("}\n\n\n", cfile);
340 /*******************************************************************
341 * output_c_postamble
343 * Write the last part of the .c file
345 static void output_c_postamble (void)
347 if (cfile)
348 fclose (cfile);
349 cfile = NULL;
353 /*******************************************************************
354 * output_makefile
356 * Write a Wine compatable makefile.in
358 void output_makefile (void)
360 FILE *makefile = open_file ("Makefile", ".in", "w");
362 if (VERBOSE)
363 puts ("Creating makefile");
365 fprintf (makefile,
366 "# Generated from %s.dll by specmaker.\nTOPSRCDIR = @top_srcdir@\n"
367 "TOPOBJDIR = ../..\nSRCDIR = @srcdir@\nVPATH = @srcdir@\n"
368 "MODULE = %s\nEXTRALIBS = $(LIBUNICODE)\n\n"
369 "LDDLLFLAGS = @LDDLLFLAGS@\nSYMBOLFILE = $(MODULE).tmp.o\n\n"
370 "C_SRCS = \\\n\t%s_main.c\n\n@MAKE_DLL_RULES@\n\n### Dependencies:",
371 globals.input_name, OUTPUT_DLL_NAME, OUTPUT_DLL_NAME);
373 fclose (makefile);
377 /*******************************************************************
378 * output_install_script
380 * Write a script to insert the DLL into Wine
382 * Rather than using diff/patch, several sed calls are generated
383 * so the script can be re-run at any time without breaking.
385 void output_install_script (void)
387 char cmd[128];
388 FILE *install_file = open_file (OUTPUT_DLL_NAME, "_install", "w");
390 if (VERBOSE)
391 puts ("Creating install script");
393 fprintf (install_file,
394 "#!/bin/bash\n# Generated from %s.dll by specmaker.\n\n"
395 "if [ $# -ne 1 ] || [ ! -d $1 ] || [ ! -f"
396 " $1/AUTHORS ]; then\n\t[ $# -eq 1 ] && echo \"Invalid path\"\n"
397 "\techo \"Usage: $0 wine-base-dir\"\n\texit 1\nfi\n\n"
398 "if [ -d $1/dlls/%s ]; then\n\techo \"DLL is already present\"\n"
399 "\texit 1\nfi\n\necho Adding DLL %s to Wine build tree...\n"
400 "echo\n\nmkdir $1/dlls/%s\ncp %s.spec $1/dlls/%s\n"
401 "cp %s_main.c $1/dlls/%s\ncp %s_dll.h $1/dlls/%s\n"
402 "cp Makefile.in $1/dlls/%s\necho Copied DLL files\n\n"
403 "cd $1\n\nsed '/dlls\\/"
404 "x11drv\\/Makefile/{G;s/$/dlls\\/%s\\/Makefile/;}' configure.in"
405 " >t.tmp\nmv -f t.tmp configure.in\necho Patched configure.in\n\n"
406 "sed '/ws2_32/{G;s/$/\\^%s \\\\/;}' Make.rules.in | tr ^ \\\\t"
407 " >t.tmp\nmv -f t.tmp Make.rules.in\necho Patched Make.rules.in"
408 "\n\nsed '/DLLFILES =/{G;s/$/\\^%s\\/lib%s.so \\\\/;}'"
409 " dlls/Makefile.in| tr ^ \\\\t >t.tmp\n"
410 "sed '/SUBDIRS =/{G;s/$/\\^%s \\\\/;}' t.tmp | tr ^ \\\\t >t.tmp2"
411 "\nsed '/Map library name /{G;s/$/^\\$(RM) \\$\\@ \\&\\& \\$\\"
412 "(LN_S\\) %s\\/lib%s.\\@LIBEXT\\@ \\$\\@/;}' t.tmp2 | tr ^ \\\\t"
413 " > t.tmp\nsed '/Map library name /{G;s/$/lib%s.\\@LIBEXT\\@: "
414 "%s\\/lib%s.\\@LIBEXT\\@/;}' t.tmp > t.tmp2\nsed '/dll "
415 "dependencies /{G;s/$/%s\\/lib%s.\\@LIBEXT\\@\\: libkernel32."
416 "\\@LIBEXT\\@ libntdll.\\@LIBEXT\\@/;}' t.tmp2 > t.tmp\n"
417 "mv -f t.tmp dlls/Makefile.in\nrm -f t.tmp2\necho Patched dlls/"
418 "Makefile.in\n\necho\necho ...done.\necho Run \\'autoconf\\', "
419 "\\'./configure\\' then \\'make\\' to rebuild Wine\n\n",
420 OUTPUT_DLL_NAME, OUTPUT_DLL_NAME, OUTPUT_DLL_NAME, OUTPUT_DLL_NAME,
421 OUTPUT_DLL_NAME, OUTPUT_DLL_NAME, OUTPUT_DLL_NAME, OUTPUT_DLL_NAME,
422 OUTPUT_DLL_NAME, OUTPUT_DLL_NAME, OUTPUT_DLL_NAME, OUTPUT_DLL_NAME,
423 OUTPUT_DLL_NAME, OUTPUT_DLL_NAME, OUTPUT_DLL_NAME, OUTPUT_DLL_NAME,
424 OUTPUT_DLL_NAME, OUTPUT_DLL_NAME, OUTPUT_DLL_NAME, OUTPUT_DLL_NAME,
425 OUTPUT_DLL_NAME, OUTPUT_DLL_NAME, OUTPUT_DLL_NAME);
427 fclose (install_file);
428 snprintf (cmd, sizeof (cmd), "chmod a+x %s_install", OUTPUT_DLL_NAME);
429 system (cmd);
433 /*******************************************************************
434 * output_prototype
436 * Write a C prototype for a parsed symbol
438 static void output_prototype (FILE *file, const parsed_symbol *sym)
440 unsigned int i;
442 fprintf (file, "%s %s %s_%s(", sym->return_text, sym->calling_convention,
443 OUTPUT_UC_DLL_NAME, sym->function_name);
445 if (!sym->argc)
446 fputs ("void", file);
447 else
448 for (i = 0; i < sym->argc; i++)
449 fprintf (file, "%s%s %s", i ? ", " : "", sym->arg_text [i],
450 sym->arg_name [i]);
451 if (sym->varargs)
452 fputs (", ...", file);
453 fputc (')', file);
457 /*******************************************************************
458 * output_c_banner
460 * Write a function banner to the .c file
462 void output_c_banner (const parsed_symbol *sym)
464 size_t i;
466 fprintf (cfile, "/*********************************************************"
467 "*********\n *\t\t%s (%s.@)\n *\n", sym->symbol,
468 OUTPUT_UC_DLL_NAME);
470 if (globals.do_documentation && sym->function_name)
472 fputs (" *\n * PARAMS\n *\n", cfile);
474 if (!sym->argc)
475 fputs (" * None.\n *\n", cfile);
476 else
478 for (i = 0; i < sym->argc; i++)
479 fprintf (cfile, " * %s [%s]%s\n", sym->arg_name [i],
480 get_in_or_out(sym, i),
481 strcmp (sym->arg_name [i], "_this") ? "" :
482 " Pointer to the class object");
484 if (sym->varargs)
485 fputs (" * ...[I]\n", cfile);
486 fputs (" *\n", cfile);
489 fputs (" * RETURNS\n *\n", cfile);
491 if (sym->return_text && !strcmp (sym->return_text, "void"))
492 fputs (" * Nothing.\n", cfile);
493 else
494 fprintf (cfile, " * %s\n", sym->return_text);
496 fputs (" *\n */\n", cfile);
500 /*******************************************************************
501 * get_format_str
503 * Get a string containing the correct format string for a type
505 static const char *get_format_str (int type)
507 switch (type)
509 case ARG_VOID: return "void";
510 case ARG_FLOAT: return "%f";
511 case ARG_DOUBLE: return "%g";
512 case ARG_POINTER: return "%p";
513 case ARG_WIDE_STRING:
514 case ARG_STRING: return "%s";
515 case ARG_LONG: return "%ld";
516 case ARG_STRUCT: return "struct";
518 assert (0);
519 return "";
523 /*******************************************************************
524 * get_in_or_out
526 * Determin if a parameter is In or In/Out
528 static const char *get_in_or_out (const parsed_symbol *sym, size_t arg)
530 assert (sym && arg < sym->argc);
531 assert (globals.do_documentation);
533 if (sym->arg_flag [arg] & CT_CONST)
534 return "In";
536 switch (sym->arg_type [arg])
538 case ARG_FLOAT:
539 case ARG_DOUBLE:
540 case ARG_LONG:
541 case ARG_STRUCT: return "In";
542 case ARG_POINTER:
543 case ARG_WIDE_STRING:
544 case ARG_STRING: return "In/Out";
546 assert (0);
547 return "";