117daf93ecf4b005d585a8e9819f93ba5e81bf94
[reactos.git] / reactos / tools / spec2def / spec2def.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <ctype.h>
4 #include <string.h>
5
6 #ifdef _MSC_VER
7 #define strcasecmp _stricmp
8 #endif
9
10 typedef struct _STRING
11 {
12 const char *buf;
13 int len;
14 } STRING, *PSTRING;
15
16 typedef struct
17 {
18 STRING strName;
19 STRING strTarget;
20 int nCallingConvention;
21 int nOrdinal;
22 int nStackBytes;
23 int nArgCount;
24 int anArgs[30];
25 unsigned int uFlags;
26 int nNumber;
27 } EXPORT;
28
29 enum _ARCH
30 {
31 ARCH_X86,
32 ARCH_AMD64,
33 ARCH_IA64,
34 ARCH_ARM,
35 ARCH_PPC
36 };
37
38 typedef int (*PFNOUTLINE)(FILE *, EXPORT *);
39 int gbMSComp = 0;
40 int gbImportLib = 0;
41 int gbTracing = 0;
42 int giArch = ARCH_X86;
43 char *pszArchString = "i386";
44 char *pszArchString2;
45 char *pszDllName = 0;
46 char *gpszUnderscore = "";
47 int gbDebug;
48 #define DbgPrint(...) (!gbDebug || fprintf(stderr, __VA_ARGS__))
49
50 enum
51 {
52 FL_PRIVATE = 1,
53 FL_STUB = 2,
54 FL_NONAME = 4,
55 FL_ORDINAL = 8,
56 FL_NORELAY = 16,
57 FL_RET64 = 32,
58 FL_REGISTER = 64,
59 };
60
61 enum
62 {
63 CC_STDCALL,
64 CC_CDECL,
65 CC_FASTCALL,
66 CC_THISCALL,
67 CC_EXTERN,
68 CC_STUB,
69 };
70
71 enum
72 {
73 ARG_LONG,
74 ARG_PTR,
75 ARG_STR,
76 ARG_WSTR,
77 ARG_DBL,
78 ARG_INT64,
79 ARG_INT128,
80 ARG_FLOAT
81 };
82
83 char* astrCallingConventions[] =
84 {
85 "STDCALL",
86 "CDECL",
87 "FASTCALL",
88 "THISCALL",
89 "EXTERN"
90 };
91
92 static
93 int
94 IsSeparator(char chr)
95 {
96 return ((chr <= ',' && chr != '$') ||
97 (chr >= ':' && chr < '?') );
98 }
99
100 int
101 CompareToken(const char *token, const char *comparand)
102 {
103 while (*comparand)
104 {
105 if (*token != *comparand) return 0;
106 token++;
107 comparand++;
108 }
109 if (!IsSeparator(*token)) return 0;
110 return 1;
111 }
112
113 const char *
114 ScanToken(const char *token, char chr)
115 {
116 while (!IsSeparator(*token))
117 {
118 if (*token == chr) return token;
119 token++;
120 }
121 return 0;
122 }
123
124 char *
125 NextLine(char *pc)
126 {
127 while (*pc != 0)
128 {
129 if (pc[0] == '\n' && pc[1] == '\r') return pc + 2;
130 else if (pc[0] == '\n') return pc + 1;
131 pc++;
132 }
133 return pc;
134 }
135
136 int
137 TokenLength(char *pc)
138 {
139 int length = 0;
140
141 while (!IsSeparator(*pc++)) length++;
142
143 return length;
144 }
145
146 char *
147 NextToken(char *pc)
148 {
149 /* Skip token */
150 while (!IsSeparator(*pc)) pc++;
151
152 /* Skip white spaces */
153 while (*pc == ' ' || *pc == '\t') pc++;
154
155 /* Check for end of line */
156 if (*pc == '\n' || *pc == '\r' || *pc == 0) return 0;
157
158 /* Check for comment */
159 if (*pc == '#' || *pc == ';') return 0;
160
161 return pc;
162 }
163
164 void
165 OutputHeader_stub(FILE *file)
166 {
167 fprintf(file, "/* This file is autogenerated, do not edit. */\n\n"
168 "#include <stubs.h>\n");
169
170 if (gbTracing)
171 {
172 fprintf(file, "#include <wine/debug.h>\n");
173 fprintf(file, "#include <inttypes.h>\n");
174 fprintf(file, "WINE_DECLARE_DEBUG_CHANNEL(relay);\n");
175 }
176
177 fprintf(file, "\n");
178 }
179
180 int
181 OutputLine_stub(FILE *file, EXPORT *pexp)
182 {
183 int i;
184 int bRelay = 0;
185 int bInPrototype = 0;
186
187 if (pexp->nCallingConvention != CC_STUB &&
188 (pexp->uFlags & FL_STUB) == 0)
189 {
190 /* Only relay trace stdcall C functions */
191 if (!gbTracing || (pexp->nCallingConvention != CC_STDCALL)
192 || (pexp->uFlags & FL_NORELAY)
193 || (pexp->strName.buf[0] == '?'))
194 {
195 return 0;
196 }
197 bRelay = 1;
198 }
199
200 /* Declare the "real" function */
201 if (bRelay)
202 {
203 fprintf(file, "extern ");
204 bInPrototype = 1;
205 }
206
207 do
208 {
209 if (pexp->uFlags & FL_REGISTER)
210 {
211 /* FIXME: Not sure this is right */
212 fprintf(file, "void ");
213 }
214 else if (pexp->uFlags & FL_RET64)
215 {
216 fprintf(file, "__int64 ");
217 }
218 else
219 {
220 fprintf(file, "int ");
221 }
222
223 if ((giArch == ARCH_X86) &&
224 pexp->nCallingConvention == CC_STDCALL)
225 {
226 fprintf(file, "__stdcall ");
227 }
228
229 /* Check for C++ */
230 if (pexp->strName.buf[0] == '?')
231 {
232 fprintf(file, "stub_function%d(", pexp->nNumber);
233 }
234 else
235 {
236 if (!bRelay || bInPrototype)
237 fprintf(file, "%.*s(", pexp->strName.len, pexp->strName.buf);
238 else
239 fprintf(file, "$relaytrace$%.*s(", pexp->strName.len, pexp->strName.buf);
240 }
241
242 for (i = 0; i < pexp->nArgCount; i++)
243 {
244 if (i != 0) fprintf(file, ", ");
245 switch (pexp->anArgs[i])
246 {
247 case ARG_LONG: fprintf(file, "long"); break;
248 case ARG_PTR: fprintf(file, "void*"); break;
249 case ARG_STR: fprintf(file, "char*"); break;
250 case ARG_WSTR: fprintf(file, "wchar_t*"); break;
251 case ARG_DBL: fprintf(file, "double"); break;
252 case ARG_INT64 : fprintf(file, "__int64"); break;
253 case ARG_INT128 : fprintf(file, "__int128"); break;
254 case ARG_FLOAT: fprintf(file, "float"); break;
255 }
256 fprintf(file, " a%d", i);
257 }
258
259 if (bInPrototype)
260 {
261 fprintf(file, ");\n\n");
262 }
263 } while (bInPrototype--);
264
265 if (!bRelay)
266 {
267 fprintf(file, ")\n{\n\tDbgPrint(\"WARNING: calling stub %.*s(",
268 pexp->strName.len, pexp->strName.buf);
269 }
270 else
271 {
272 fprintf(file, ")\n{\n");
273 if (pexp->uFlags & FL_REGISTER)
274 {
275 /* No return value */
276 }
277 else if (pexp->uFlags & FL_RET64)
278 {
279 fprintf(file, "\t__int64 retval;\n");
280 }
281 else
282 {
283 fprintf(file, "\tint retval;\n");
284 }
285 fprintf(file, "\tif (TRACE_ON(relay))\n\t\tDPRINTF(\"%s: %.*s(",
286 pszDllName, pexp->strName.len, pexp->strName.buf);
287 }
288
289 for (i = 0; i < pexp->nArgCount; i++)
290 {
291 if (i != 0) fprintf(file, ",");
292 switch (pexp->anArgs[i])
293 {
294 case ARG_LONG: fprintf(file, "0x%%lx"); break;
295 case ARG_PTR: fprintf(file, "0x%%p"); break;
296 case ARG_STR: fprintf(file, "'%%s'"); break;
297 case ARG_WSTR: fprintf(file, "'%%ws'"); break;
298 case ARG_DBL: fprintf(file, "%%f"); break;
299 case ARG_INT64: fprintf(file, "%%\"PRix64\""); break;
300 case ARG_INT128: fprintf(file, "%%\"PRix128\""); break;
301 case ARG_FLOAT: fprintf(file, "%%f"); break;
302 }
303 }
304 fprintf(file, ")\\n\"");
305
306 for (i = 0; i < pexp->nArgCount; i++)
307 {
308 fprintf(file, ", ");
309 switch (pexp->anArgs[i])
310 {
311 case ARG_LONG: fprintf(file, "(long)a%d", i); break;
312 case ARG_PTR: fprintf(file, "(void*)a%d", i); break;
313 case ARG_STR: fprintf(file, "(char*)a%d", i); break;
314 case ARG_WSTR: fprintf(file, "(wchar_t*)a%d", i); break;
315 case ARG_DBL: fprintf(file, "(double)a%d", i); break;
316 case ARG_INT64: fprintf(file, "(__int64)a%d", i); break;
317 case ARG_INT128: fprintf(file, "(__int128)a%d", i); break;
318 case ARG_FLOAT: fprintf(file, "(float)a%d", i); break;
319 }
320 }
321 fprintf(file, ");\n");
322
323 if (pexp->nCallingConvention == CC_STUB)
324 {
325 fprintf(file, "\t__wine_spec_unimplemented_stub(\"%s\", __FUNCTION__);\n", pszDllName);
326 }
327 else if (bRelay)
328 {
329 if (pexp->uFlags & FL_REGISTER)
330 {
331 fprintf(file,"\t");
332 }
333 else
334 {
335 fprintf(file, "\tretval = ");
336 }
337 fprintf(file, "%.*s(", pexp->strName.len, pexp->strName.buf);
338
339 for (i = 0; i < pexp->nArgCount; i++)
340 {
341 if (i != 0) fprintf(file, ", ");
342 fprintf(file, "a%d", i);
343 }
344 fprintf(file, ");\n");
345 }
346
347 if (!bRelay)
348 fprintf(file, "\treturn 0;\n}\n\n");
349 else if ((pexp->uFlags & FL_REGISTER) == 0)
350 {
351 if (pexp->uFlags & FL_RET64)
352 {
353 fprintf(file, "\tif (TRACE_ON(relay))\n\t\tDPRINTF(\"%s: %.*s: retval = %%\"PRIx64\"\\n\", retval);\n",
354 pszDllName, pexp->strName.len, pexp->strName.buf);
355 }
356 else
357 {
358 fprintf(file, "\tif (TRACE_ON(relay))\n\t\tDPRINTF(\"%s: %.*s: retval = 0x%%lx\\n\", retval);\n",
359 pszDllName, pexp->strName.len, pexp->strName.buf);
360 }
361 fprintf(file, "\treturn retval;\n}\n\n");
362 }
363
364 return 1;
365 }
366
367 void
368 OutputHeader_asmstub(FILE *file, char *libname)
369 {
370 fprintf(file, "; File generated automatically, do not edit! \n\n");
371
372 if (giArch == ARCH_X86)
373 {
374 fprintf(file, ".586\n.model flat\n.code\n");
375 }
376 else if (giArch == ARCH_AMD64)
377 {
378 fprintf(file, ".code\n");
379 }
380 else if (giArch == ARCH_ARM)
381 {
382 fprintf(file,
383 " AREA |.text|,ALIGN=2,CODE,READONLY\n\n");
384 }
385 }
386
387 void
388 Output_stublabel(FILE *fileDest, char* pszSymbolName)
389 {
390 if (giArch == ARCH_ARM)
391 {
392 fprintf(fileDest,
393 " EXPORT %s [FUNC]\n%s\n",
394 pszSymbolName,
395 pszSymbolName);
396 }
397 else
398 {
399 fprintf(fileDest,
400 "PUBLIC %s\n%s: nop\n",
401 pszSymbolName,
402 pszSymbolName);
403 }
404 }
405
406 int
407 OutputLine_asmstub(FILE *fileDest, EXPORT *pexp)
408 {
409 char szNameBuffer[128];
410
411 /* Handle autoname */
412 if (pexp->strName.len == 1 && pexp->strName.buf[0] == '@')
413 {
414 sprintf(szNameBuffer, "%sordinal%d\n%sordinal%d: nop\n",
415 gpszUnderscore, pexp->nOrdinal, gpszUnderscore, pexp->nOrdinal);
416 }
417 else if (giArch != ARCH_X86)
418 {
419 sprintf(szNameBuffer, "_stub_%.*s",
420 pexp->strName.len, pexp->strName.buf);
421 }
422 else if (pexp->nCallingConvention == CC_STDCALL)
423 {
424 sprintf(szNameBuffer, "__stub_%.*s@%d",
425 pexp->strName.len, pexp->strName.buf, pexp->nStackBytes);
426 }
427 else if (pexp->nCallingConvention == CC_FASTCALL)
428 {
429 sprintf(szNameBuffer, "@_stub_%.*s@%d",
430 pexp->strName.len, pexp->strName.buf, pexp->nStackBytes);
431 }
432 else if ((pexp->nCallingConvention == CC_CDECL) ||
433 (pexp->nCallingConvention == CC_THISCALL) ||
434 (pexp->nCallingConvention == CC_EXTERN) ||
435 (pexp->nCallingConvention == CC_STUB))
436 {
437 sprintf(szNameBuffer, "__stub_%.*s",
438 pexp->strName.len, pexp->strName.buf);
439 }
440 else
441 {
442 fprintf(stderr, "Invalid calling convention");
443 return 0;
444 }
445
446 Output_stublabel(fileDest, szNameBuffer);
447
448 return 1;
449 }
450
451 void
452 OutputHeader_def(FILE *file, char *libname)
453 {
454 fprintf(file,
455 "; File generated automatically, do not edit!\n\n"
456 "NAME %s\n\n"
457 "EXPORTS\n",
458 libname);
459 }
460
461 void
462 PrintName(FILE *fileDest, EXPORT *pexp, PSTRING pstr, int fDeco)
463 {
464 const char *pcName = pstr->buf;
465 int nNameLength = pstr->len;
466 const char* pcDot, *pcAt;
467
468 /* Check for non-x86 first */
469 if (giArch != ARCH_X86)
470 {
471 /* Does the string already have stdcall decoration? */
472 pcAt = ScanToken(pcName, '@');
473 if (pcAt && (pcAt < (pcName + nNameLength)) && (pcName[0] == '_'))
474 {
475 /* Skip leading underscore and remove trailing decoration */
476 pcName++;
477 nNameLength = pcAt - pcName;
478 }
479
480 /* Print the undecorated function name */
481 fprintf(fileDest, "%.*s", nNameLength, pcName);
482 }
483 else if (fDeco &&
484 ((pexp->nCallingConvention == CC_STDCALL) ||
485 (pexp->nCallingConvention == CC_FASTCALL)))
486 {
487 /* Scan for a dll forwarding dot */
488 pcDot = ScanToken(pcName, '.');
489 if (pcDot)
490 {
491 /* First print the dll name, followed by a dot */
492 nNameLength = pcDot - pcName;
493 fprintf(fileDest, "%.*s.", nNameLength, pcName);
494
495 /* Now the actual function name */
496 pcName = pcDot + 1;
497 nNameLength = pexp->strTarget.len - nNameLength - 1;
498 }
499
500 /* Does the string already have decoration? */
501 pcAt = ScanToken(pcName, '@');
502 if (pcAt && (pcAt < (pcName + nNameLength)))
503 {
504 /* On GCC, we need to remove the leading stdcall underscore */
505 if (!gbMSComp && (pexp->nCallingConvention == CC_STDCALL))
506 {
507 pcName++;
508 nNameLength--;
509 }
510
511 /* Print the already decorated function name */
512 fprintf(fileDest, "%.*s", nNameLength, pcName);
513 }
514 else
515 {
516 /* Print the prefix, but skip it for (GCC && stdcall) */
517 if (gbMSComp || (pexp->nCallingConvention != CC_STDCALL))
518 {
519 fprintf(fileDest, "%c", pexp->nCallingConvention == CC_FASTCALL ? '@' : '_');
520 }
521
522 /* Print the name with trailing decoration */
523 fprintf(fileDest, "%.*s@%d", nNameLength, pcName, pexp->nStackBytes);
524 }
525 }
526 else
527 {
528 /* Print the undecorated function name */
529 fprintf(fileDest, "%.*s", nNameLength, pcName);
530 }
531 }
532
533 void
534 OutputLine_def_MS(FILE *fileDest, EXPORT *pexp)
535 {
536 PrintName(fileDest, pexp, &pexp->strName, 0);
537
538 if (gbImportLib)
539 {
540 /* Redirect to a stub function, to get the right decoration in the lib */
541 fprintf(fileDest, "=_stub_%.*s", pexp->strName.len, pexp->strName.buf);
542 }
543 else if (pexp->strTarget.buf)
544 {
545 if (pexp->strName.buf[0] == '?')
546 {
547 fprintf(stderr, "warning: ignoring C++ redirection %.*s -> %.*s\n",
548 pexp->strName.len, pexp->strName.buf, pexp->strTarget.len, pexp->strTarget.buf);
549 }
550 else
551 {
552 fprintf(fileDest, "=");
553
554 /* If the original name was decorated, use decoration in the forwarder as well */
555 if ((giArch == ARCH_X86) && ScanToken(pexp->strName.buf, '@') &&
556 !ScanToken(pexp->strTarget.buf, '@') &&
557 ((pexp->nCallingConvention == CC_STDCALL) ||
558 (pexp->nCallingConvention == CC_FASTCALL)) )
559 {
560 PrintName(fileDest, pexp, &pexp->strTarget, 1);
561 }
562 else
563 {
564 /* Write the undecorated redirection name */
565 fprintf(fileDest, "%.*s", pexp->strTarget.len, pexp->strTarget.buf);
566 }
567 }
568 }
569 else if (((pexp->uFlags & FL_STUB) || (pexp->nCallingConvention == CC_STUB)) &&
570 (pexp->strName.buf[0] == '?'))
571 {
572 /* C++ stubs are forwarded to C stubs */
573 fprintf(fileDest, "=stub_function%d", pexp->nNumber);
574 }
575 else if (gbTracing && ((pexp->uFlags & FL_NORELAY) == 0) && (pexp->nCallingConvention == CC_STDCALL) &&
576 (pexp->strName.buf[0] != '?'))
577 {
578 /* Redirect it to the relay-tracing trampoline */
579 fprintf(fileDest, "=$relaytrace$%.*s", pexp->strName.len, pexp->strName.buf);
580 }
581 }
582
583 void
584 OutputLine_def_GCC(FILE *fileDest, EXPORT *pexp)
585 {
586 int bTracing = 0;
587 /* Print the function name, with decoration for export libs */
588 PrintName(fileDest, pexp, &pexp->strName, gbImportLib);
589 DbgPrint("Generating def line for '%.*s'\n", pexp->strName.len, pexp->strName.buf);
590
591 /* Check if this is a forwarded export */
592 if (pexp->strTarget.buf)
593 {
594 int fIsExternal = !!ScanToken(pexp->strTarget.buf, '.');
595 DbgPrint("Got redirect '%.*s'\n", pexp->strTarget.len, pexp->strTarget.buf);
596
597 /* print the target name, don't decorate if it is external */
598 fprintf(fileDest, "=");
599 PrintName(fileDest, pexp, &pexp->strTarget, !fIsExternal);
600 }
601 else if (((pexp->uFlags & FL_STUB) || (pexp->nCallingConvention == CC_STUB)) &&
602 (pexp->strName.buf[0] == '?'))
603 {
604 /* C++ stubs are forwarded to C stubs */
605 fprintf(fileDest, "=stub_function%d", pexp->nNumber);
606 }
607 else if (gbTracing && ((pexp->uFlags & FL_NORELAY) == 0) &&
608 (pexp->nCallingConvention == CC_STDCALL) &&
609 (pexp->strName.buf[0] != '?'))
610 {
611 /* Redirect it to the relay-tracing trampoline */
612 char buf[256];
613 STRING strTarget;
614 fprintf(fileDest, "=");
615 sprintf(buf, "$relaytrace$%.*s", pexp->strName.len, pexp->strName.buf);
616 strTarget.buf = buf;
617 strTarget.len = pexp->strName.len + 12;
618 PrintName(fileDest, pexp, &strTarget, 1);
619 bTracing = 1;
620 }
621
622 /* Special handling for stdcall and fastcall */
623 if ((giArch == ARCH_X86) &&
624 ((pexp->nCallingConvention == CC_STDCALL) ||
625 (pexp->nCallingConvention == CC_FASTCALL)))
626 {
627 /* Is this the import lib? */
628 if (gbImportLib)
629 {
630 /* Is the name in the spec file decorated? */
631 const char* pcDeco = ScanToken(pexp->strName.buf, '@');
632 if (pcDeco && (pcDeco < pexp->strName.buf + pexp->strName.len))
633 {
634 /* Write the name including the leading @ */
635 fprintf(fileDest, "==%.*s", pexp->strName.len, pexp->strName.buf);
636 }
637 }
638 else if ((!pexp->strTarget.buf) && !(bTracing))
639 {
640 /* Write a forwarder to the actual decorated symbol */
641 fprintf(fileDest, "=");
642 PrintName(fileDest, pexp, &pexp->strName, 1);
643 }
644 }
645 }
646
647 int
648 OutputLine_def(FILE *fileDest, EXPORT *pexp)
649 {
650 DbgPrint("OutputLine_def: '%.*s'...\n", pexp->strName.len, pexp->strName.buf);
651 fprintf(fileDest, " ");
652
653 if (gbMSComp)
654 OutputLine_def_MS(fileDest, pexp);
655 else
656 OutputLine_def_GCC(fileDest, pexp);
657
658 if (pexp->uFlags & FL_ORDINAL)
659 {
660 fprintf(fileDest, " @%d", pexp->nOrdinal);
661 }
662
663 if (pexp->uFlags & FL_NONAME)
664 {
665 fprintf(fileDest, " NONAME");
666 }
667
668 if (pexp->uFlags & FL_PRIVATE)
669 {
670 fprintf(fileDest, " PRIVATE");
671 }
672
673 if (pexp->nCallingConvention == CC_EXTERN)
674 {
675 fprintf(fileDest, " DATA");
676 }
677
678 fprintf(fileDest, "\n");
679
680 return 1;
681 }
682
683 int
684 ParseFile(char* pcStart, FILE *fileDest, PFNOUTLINE OutputLine)
685 {
686 char *pc, *pcLine;
687 int nLine;
688 EXPORT exp;
689 int included;
690 char namebuffer[16];
691
692 //fprintf(stderr, "info: line %d, pcStart:'%.30s'\n", nLine, pcStart);
693
694 /* Loop all lines */
695 nLine = 1;
696 exp.nNumber = 0;
697 for (pcLine = pcStart; *pcLine; pcLine = NextLine(pcLine), nLine++)
698 {
699 pc = pcLine;
700
701 exp.nArgCount = 0;
702 exp.uFlags = 0;
703 exp.nNumber++;
704
705
706 //if (!strncmp(pcLine, "22 stdcall @(long) MPR_Alloc",28))
707 // gbDebug = 1;
708
709 //fprintf(stderr, "info: line %d, token:'%d, %.20s'\n",
710 // nLine, TokenLength(pcLine), pcLine);
711
712 /* Skip white spaces */
713 while (*pc == ' ' || *pc == '\t') pc++;
714
715 /* Skip empty lines, stop at EOF */
716 if (*pc == ';' || *pc <= '#') continue;
717 if (*pc == 0) return 0;
718
719 //fprintf(stderr, "info: line %d, token:'%.*s'\n",
720 // nLine, TokenLength(pc), pc);
721
722 /* Now we should get either an ordinal or @ */
723 if (*pc == '@')
724 exp.nOrdinal = -1;
725 else
726 {
727 exp.nOrdinal = atol(pc);
728 /* The import lib should contain the ordinal only if -ordinal was specified */
729 if (!gbImportLib)
730 exp.uFlags |= FL_ORDINAL;
731 }
732
733 /* Go to next token (type) */
734 if (!(pc = NextToken(pc)))
735 {
736 fprintf(stderr, "error: line %d, unexpected end of line\n", nLine);
737 return -10;
738 }
739
740 //fprintf(stderr, "info: Token:'%.*s'\n", TokenLength(pc), pc);
741
742 /* Now we should get the type */
743 if (CompareToken(pc, "stdcall"))
744 {
745 exp.nCallingConvention = CC_STDCALL;
746 }
747 else if (CompareToken(pc, "cdecl") ||
748 CompareToken(pc, "varargs"))
749 {
750 exp.nCallingConvention = CC_CDECL;
751 }
752 else if (CompareToken(pc, "fastcall"))
753 {
754 exp.nCallingConvention = CC_FASTCALL;
755 }
756 else if (CompareToken(pc, "thiscall"))
757 {
758 exp.nCallingConvention = CC_THISCALL;
759 }
760 else if (CompareToken(pc, "extern"))
761 {
762 exp.nCallingConvention = CC_EXTERN;
763 }
764 else if (CompareToken(pc, "stub"))
765 {
766 exp.nCallingConvention = CC_STUB;
767 }
768 else
769 {
770 fprintf(stderr, "error: line %d, expected callconv, got '%.*s' %d\n",
771 nLine, TokenLength(pc), pc, *pc);
772 return -11;
773 }
774
775 //fprintf(stderr, "info: nCallingConvention: %d\n", exp.nCallingConvention);
776
777 /* Go to next token (options or name) */
778 if (!(pc = NextToken(pc)))
779 {
780 fprintf(stderr, "fail2\n");
781 return -12;
782 }
783
784 /* Handle options */
785 included = 1;
786 while (*pc == '-')
787 {
788 if (CompareToken(pc, "-arch"))
789 {
790 /* Default to not included */
791 included = 0;
792 pc += 5;
793
794 /* Look if we are included */
795 while (*pc == '=' || *pc == ',')
796 {
797 pc++;
798 if (CompareToken(pc, pszArchString) ||
799 CompareToken(pc, pszArchString2))
800 {
801 included = 1;
802 }
803
804 /* Skip to next arch or end */
805 while (*pc > ',') pc++;
806 }
807 }
808 else if (CompareToken(pc, "-i386"))
809 {
810 if (giArch != ARCH_X86) included = 0;
811 }
812 else if (CompareToken(pc, "-private"))
813 {
814 exp.uFlags |= FL_PRIVATE;
815 }
816 else if (CompareToken(pc, "-noname"))
817 {
818 exp.uFlags |= FL_ORDINAL | FL_NONAME;
819 }
820 else if (CompareToken(pc, "-ordinal"))
821 {
822 exp.uFlags |= FL_ORDINAL;
823 /* GCC doesn't automatically import by ordinal if an ordinal
824 * is found in the def file. Force it. */
825 if (gbImportLib && !gbMSComp)
826 exp.uFlags |= FL_NONAME;
827 }
828 else if (CompareToken(pc, "-stub"))
829 {
830 exp.uFlags |= FL_STUB;
831 }
832 else if (CompareToken(pc, "-norelay"))
833 {
834 exp.uFlags |= FL_NORELAY;
835 }
836 else if (CompareToken(pc, "-ret64"))
837 {
838 exp.uFlags |= FL_RET64;
839 }
840 else if (CompareToken(pc, "-register"))
841 {
842 exp.uFlags |= FL_REGISTER;
843 }
844 else
845 {
846 fprintf(stderr, "info: ignored option: '%.*s'\n",
847 TokenLength(pc), pc);
848 }
849
850 /* Go to next token */
851 pc = NextToken(pc);
852 }
853
854 //fprintf(stderr, "info: Name:'%.10s'\n", pc);
855
856 /* If arch didn't match ours, skip this entry */
857 if (!included) continue;
858
859 /* Get name */
860 exp.strName.buf = pc;
861 exp.strName.len = TokenLength(pc);
862 DbgPrint("Got name: '%.*s'\n", exp.strName.len, exp.strName.buf);
863
864 /* Check for autoname */
865 if ((exp.strName.len == 1) && (exp.strName.buf[0] == '@'))
866 {
867 sprintf(namebuffer, "ordinal%d", exp.nOrdinal);
868 exp.strName.len = strlen(namebuffer);
869 exp.strName.buf = namebuffer;
870 exp.uFlags |= FL_ORDINAL | FL_NONAME;
871 }
872
873 /* Handle parameters */
874 exp.nStackBytes = 0;
875 if (exp.nCallingConvention != CC_EXTERN &&
876 exp.nCallingConvention != CC_STUB)
877 {
878 /* Go to next token */
879 if (!(pc = NextToken(pc)))
880 {
881 fprintf(stderr, "fail4\n");
882 return -13;
883 }
884
885 /* Verify syntax */
886 if (*pc++ != '(')
887 {
888 fprintf(stderr, "error: line %d, expected '('\n", nLine);
889 return -14;
890 }
891
892 /* Skip whitespaces */
893 while (*pc == ' ' || *pc == '\t') pc++;
894
895 exp.nStackBytes = 0;
896 while (*pc >= '0')
897 {
898 if (CompareToken(pc, "long"))
899 {
900 exp.nStackBytes += 4;
901 exp.anArgs[exp.nArgCount] = ARG_LONG;
902 }
903 else if (CompareToken(pc, "double"))
904 {
905 exp.nStackBytes += 8;
906 exp.anArgs[exp.nArgCount] = ARG_DBL;
907 }
908 else if (CompareToken(pc, "ptr"))
909 {
910 exp.nStackBytes += 4; // sizeof(void*) on x86
911 exp.anArgs[exp.nArgCount] = ARG_PTR;
912 }
913 else if (CompareToken(pc, "str"))
914 {
915 exp.nStackBytes += 4; // sizeof(void*) on x86
916 exp.anArgs[exp.nArgCount] = ARG_STR;
917 }
918 else if (CompareToken(pc, "wstr"))
919 {
920 exp.nStackBytes += 4; // sizeof(void*) on x86
921 exp.anArgs[exp.nArgCount] = ARG_WSTR;
922 }
923 else if (CompareToken(pc, "int64"))
924 {
925 exp.nStackBytes += 8;
926 exp.anArgs[exp.nArgCount] = ARG_INT64;
927 }
928 else if (CompareToken(pc, "int128"))
929 {
930 exp.nStackBytes += 16;
931 exp.anArgs[exp.nArgCount] = ARG_INT128;
932 }
933 else if (CompareToken(pc, "float"))
934 {
935 exp.nStackBytes += 4;
936 exp.anArgs[exp.nArgCount] = ARG_FLOAT;
937 }
938 else
939 fprintf(stderr, "error: line %d, expected type, got: %.10s\n", nLine, pc);
940
941 exp.nArgCount++;
942
943 /* Go to next parameter */
944 if (!(pc = NextToken(pc)))
945 {
946 fprintf(stderr, "fail5\n");
947 return -15;
948 }
949 }
950
951 /* Check syntax */
952 if (*pc++ != ')')
953 {
954 fprintf(stderr, "error: line %d, expected ')'\n", nLine);
955 return -16;
956 }
957 }
958
959 /* Handle special stub cases */
960 if (exp.nCallingConvention == CC_STUB)
961 {
962 /* Check for c++ mangled name */
963 if (pc[0] == '?')
964 {
965 //printf("Found c++ mangled name...\n");
966 //
967 }
968 else
969 {
970 /* Check for stdcall name */
971 const char *p = ScanToken(pc, '@');
972 if (p && (p - pc < exp.strName.len))
973 {
974 int i;
975
976 /* Truncate the name to before the @ */
977 exp.strName.len = (int)(p - pc);
978 if (exp.strName.len < 1)
979 {
980 fprintf(stderr, "error, @ in line %d\n", nLine);
981 return -1;
982 }
983 exp.nStackBytes = atoi(p + 1);
984 exp.nArgCount = exp.nStackBytes / 4;
985 exp.nCallingConvention = CC_STDCALL;
986 exp.uFlags |= FL_STUB;
987 for (i = 0; i < exp.nArgCount; i++)
988 exp.anArgs[i] = ARG_LONG;
989 }
990 }
991 }
992
993 /* Get optional redirection */
994 pc = NextToken(pc);
995 if (pc)
996 {
997 exp.strTarget.buf = pc;
998 exp.strTarget.len = TokenLength(pc);
999
1000 /* Check syntax (end of line) */
1001 if (NextToken(pc))
1002 {
1003 fprintf(stderr, "error: line %d, additional tokens after ')'\n", nLine);
1004 return -17;
1005 }
1006
1007 /* Don't relay-trace forwarded functions */
1008 exp.uFlags |= FL_NORELAY;
1009 }
1010 else
1011 {
1012 exp.strTarget.buf = 0;
1013 exp.strTarget.len = 0;
1014 }
1015
1016 /* Check for no-name without ordinal */
1017 if ((exp.uFlags & FL_ORDINAL) && (exp.nOrdinal == -1))
1018 {
1019 fprintf(stderr, "error: line %d, ordinal export without ordinal!\n", nLine);
1020 return -1;
1021 }
1022
1023 OutputLine(fileDest, &exp);
1024 gbDebug = 0;
1025 }
1026
1027 return 0;
1028 }
1029
1030
1031 void usage(void)
1032 {
1033 printf("syntax: spec2def [<options> ...] <spec file>\n"
1034 "Possible options:\n"
1035 " -h --help prints this screen\n"
1036 " -l=<file> generates an asm lib stub\n"
1037 " -d=<file> generates a def file\n"
1038 " -s=<file> generates a stub file\n"
1039 " --ms msvc compatibility\n"
1040 " -n=<name> name of the dll\n"
1041 " --implib generate a def file for an import library\n"
1042 " -a=<arch> Set architecture to <arch>. (i386, x86_64, arm)\n"
1043 " --with-tracing generates wine-like \"+relay\" trace trampolines. (necessitates -s)\n");
1044 }
1045
1046 int main(int argc, char *argv[])
1047 {
1048 size_t nFileSize;
1049 char *pszSource, *pszDefFileName = 0, *pszStubFileName = 0, *pszLibStubName = 0;
1050 char achDllName[40];
1051 FILE *file;
1052 int result = 0, i;
1053
1054 if (argc < 2)
1055 {
1056 usage();
1057 return -1;
1058 }
1059
1060 /* Read options */
1061 for (i = 1; i < argc && *argv[i] == '-'; i++)
1062 {
1063 if ((strcasecmp(argv[i], "--help") == 0) ||
1064 (strcasecmp(argv[i], "-h") == 0))
1065 {
1066 usage();
1067 return 0;
1068 }
1069 else if (argv[i][1] == 'd' && argv[i][2] == '=')
1070 {
1071 pszDefFileName = argv[i] + 3;
1072 }
1073 else if (argv[i][1] == 'l' && argv[i][2] == '=')
1074 {
1075 pszLibStubName = argv[i] + 3;
1076 }
1077 else if (argv[i][1] == 's' && argv[i][2] == '=')
1078 {
1079 pszStubFileName = argv[i] + 3;
1080 }
1081 else if (argv[i][1] == 'n' && argv[i][2] == '=')
1082 {
1083 pszDllName = argv[i] + 3;
1084 }
1085 else if ((strcasecmp(argv[i], "--implib") == 0))
1086 {
1087 gbImportLib = 1;
1088 }
1089 else if ((strcasecmp(argv[i], "--ms") == 0))
1090 {
1091 gbMSComp = 1;
1092 }
1093 else if ((strcasecmp(argv[i], "--with-tracing") == 0))
1094 {
1095 if (!pszStubFileName)
1096 {
1097 fprintf(stderr, "Error: cannot use --with-tracing without -s option.\n");
1098 return -1;
1099 }
1100 gbTracing = 1;
1101 }
1102 else if (argv[i][1] == 'a' && argv[i][2] == '=')
1103 {
1104 pszArchString = argv[i] + 3;
1105 }
1106 else
1107 {
1108 fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
1109 return -1;
1110 }
1111 }
1112
1113 if (strcasecmp(pszArchString, "i386") == 0)
1114 {
1115 giArch = ARCH_X86;
1116 gpszUnderscore = "_";
1117 }
1118 else if (strcasecmp(pszArchString, "x86_64") == 0) giArch = ARCH_AMD64;
1119 else if (strcasecmp(pszArchString, "ia64") == 0) giArch = ARCH_IA64;
1120 else if (strcasecmp(pszArchString, "arm") == 0) giArch = ARCH_ARM;
1121 else if (strcasecmp(pszArchString, "ppc") == 0) giArch = ARCH_PPC;
1122
1123 if ((giArch == ARCH_AMD64) || (giArch == ARCH_IA64))
1124 {
1125 pszArchString2 = "win64";
1126 }
1127 else
1128 pszArchString2 = "win32";
1129
1130 /* Set a default dll name */
1131 if (!pszDllName)
1132 {
1133 char *p1, *p2;
1134 size_t len;
1135
1136 p1 = strrchr(argv[i], '\\');
1137 if (!p1) p1 = strrchr(argv[i], '/');
1138 p2 = p1 = p1 ? p1 + 1 : argv[i];
1139
1140 /* walk up to '.' */
1141 while (*p2 != '.' && *p2 != 0) p2++;
1142 len = p2 - p1;
1143 if (len >= sizeof(achDllName) - 5)
1144 {
1145 fprintf(stderr, "name too long: %s\n", p1);
1146 return -2;
1147 }
1148
1149 strncpy(achDllName, p1, len);
1150 strncpy(achDllName + len, ".dll", sizeof(achDllName) - len);
1151 pszDllName = achDllName;
1152 }
1153
1154 /* Open input file argv[1] */
1155 file = fopen(argv[i], "r");
1156 if (!file)
1157 {
1158 fprintf(stderr, "error: could not open file %s ", argv[i]);
1159 return -3;
1160 }
1161
1162 /* Get file size */
1163 fseek(file, 0, SEEK_END);
1164 nFileSize = ftell(file);
1165 rewind(file);
1166
1167 /* Allocate memory buffer */
1168 pszSource = malloc(nFileSize + 1);
1169 if (!pszSource)
1170 {
1171 fclose(file);
1172 return -4;
1173 }
1174
1175 /* Load input file into memory */
1176 nFileSize = fread(pszSource, 1, nFileSize, file);
1177 fclose(file);
1178
1179 /* Zero terminate the source */
1180 pszSource[nFileSize] = '\0';
1181
1182 if (pszDefFileName)
1183 {
1184 /* Open output file */
1185 file = fopen(pszDefFileName, "w");
1186 if (!file)
1187 {
1188 fprintf(stderr, "error: could not open output file %s ", argv[i + 1]);
1189 return -5;
1190 }
1191
1192 OutputHeader_def(file, pszDllName);
1193 result = ParseFile(pszSource, file, OutputLine_def);
1194 fclose(file);
1195 }
1196
1197 if (pszStubFileName)
1198 {
1199 /* Open output file */
1200 file = fopen(pszStubFileName, "w");
1201 if (!file)
1202 {
1203 fprintf(stderr, "error: could not open output file %s ", argv[i + 1]);
1204 return -5;
1205 }
1206
1207 OutputHeader_stub(file);
1208 result = ParseFile(pszSource, file, OutputLine_stub);
1209 fclose(file);
1210 }
1211
1212 if (pszLibStubName)
1213 {
1214 /* Open output file */
1215 file = fopen(pszLibStubName, "w");
1216 if (!file)
1217 {
1218 fprintf(stderr, "error: could not open output file %s ", argv[i + 1]);
1219 return -5;
1220 }
1221
1222 OutputHeader_asmstub(file, pszDllName);
1223 result = ParseFile(pszSource, file, OutputLine_asmstub);
1224 fprintf(file, "\n END\n");
1225 fclose(file);
1226 }
1227
1228
1229 return result;
1230 }