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