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