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