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