xref: /reactos/sdk/tools/spec2def/spec2def.c (revision c2c66aff)
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 }
1271