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