xref: /reactos/sdk/tools/spec2def/spec2def.c (revision 0c2cdcae)
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <ctype.h>
4 #include <string.h>
5 #include <stdarg.h>
6 
7 #ifdef _MSC_VER
8 #define strcasecmp(_String1, _String2) _stricmp(_String1, _String2)
9 #define strncasecmp(_String1, _String2, _MaxCount) _strnicmp(_String1, _String2, _MaxCount)
10 #endif
11 
12 #define ARRAYSIZE(a) (sizeof(a) / sizeof((a)[0]))
13 
14 typedef struct _STRING
15 {
16     const char *buf;
17     int len;
18 } STRING, *PSTRING;
19 
20 typedef struct
21 {
22     STRING strName;
23     STRING strTarget;
24     int nCallingConvention;
25     int nOrdinal;
26     int nStackBytes;
27     int nArgCount;
28     int anArgs[30];
29     unsigned int uFlags;
30     int nNumber;
31     unsigned nStartVersion;
32     unsigned nEndVersion;
33     int bVersionIncluded;
34 } EXPORT;
35 
36 #if 0 // Debug helper function
37 void
38 PrintExport(EXPORT *pexp)
39 {
40     fprintf(stderr, "strName='%.*s'\n", pexp->strName.len, pexp->strName.buf);
41     fprintf(stderr, "strName='%.*s'\n", pexp->strTarget.len, pexp->strTarget.buf);
42     fprintf(stderr, "nCallingConvention=%u\n", pexp->nCallingConvention);
43     fprintf(stderr, "nOrdinal=%u\n", pexp->nOrdinal);
44     fprintf(stderr, "nStackBytes=%u\n", pexp->nStackBytes);
45     fprintf(stderr, "nArgCount=%u\n", pexp->nArgCount);
46     fprintf(stderr, "uFlags=0x%x\n", pexp->uFlags);
47     fprintf(stderr, "nNumber=%u\n", pexp->nNumber);
48     fprintf(stderr, "nStartVersion=%u\n", pexp->nStartVersion);
49     fprintf(stderr, "nEndVersion=%u\n", pexp->nEndVersion);
50     fprintf(stderr, "bVersionIncluded=%u\n", pexp->bVersionIncluded);
51 }
52 #endif
53 
54 enum _ARCH
55 {
56     ARCH_X86,
57     ARCH_AMD64,
58     ARCH_IA64,
59     ARCH_ARM,
60     ARCH_ARM64,
61     ARCH_PPC
62 };
63 
64 typedef int (*PFNOUTLINE)(FILE *, EXPORT *);
65 int gbMSComp = 0;
66 int gbImportLib = 0;
67 int gbNotPrivateNoWarn = 0;
68 int gbTracing = 0;
69 int giArch = ARCH_X86;
70 char *pszArchString = "i386";
71 char *pszArchString2;
72 char *pszSourceFileName = NULL;
73 char *pszDllName = NULL;
74 char *gpszUnderscore = "";
75 int gbDebug;
76 unsigned guOsVersion = 0x502;
77 #define DbgPrint(...) (!gbDebug || fprintf(stderr, __VA_ARGS__))
78 
79 enum
80 {
81     FL_PRIVATE = 1,
82     FL_STUB = 2,
83     FL_NONAME = 4,
84     FL_ORDINAL = 8,
85     FL_NORELAY = 16,
86     FL_RET64 = 32,
87     FL_REGISTER = 64,
88     FL_IMPSYM = 128,
89 };
90 
91 enum
92 {
93     CC_STDCALL,
94     CC_CDECL,
95     CC_FASTCALL,
96     CC_THISCALL,
97     CC_EXTERN,
98     CC_STUB,
99 };
100 
101 enum
102 {
103     ARG_LONG,
104     ARG_PTR,
105     ARG_STR,
106     ARG_WSTR,
107     ARG_DBL,
108     ARG_INT64,
109     ARG_INT128,
110     ARG_FLOAT
111 };
112 
113 const char* astrCallingConventions[] =
114 {
115     "STDCALL",
116     "CDECL",
117     "FASTCALL",
118     "THISCALL",
119     "EXTERN"
120 };
121 
122 /*
123  * List of OLE exports that should be PRIVATE and not be assigned an ordinal.
124  * In case these conditions are not met when linking with MS LINK.EXE, warnings
125  * LNK4104 and LNK4222 respectively are emitted.
126  */
127 static const char* astrOlePrivateExports[] =
128 {
129     "DllCanUnloadNow",
130     "DllGetClassObject",
131     "DllGetClassFactoryFromClassString",
132     "DllGetDocumentation",
133     "DllInitialize",
134     "DllInstall",
135     "DllRegisterServer",
136     "DllRegisterServerEx",
137     "DllRegisterServerExW",
138     "DllUnload",
139     "DllUnregisterServer",
140     "RasCustomDeleteEntryNotify",
141     "RasCustomDial",
142     "RasCustomDialDlg",
143     "RasCustomEntryDlg",
144 };
145 
146 static
147 int
148 IsSeparator(char chr)
149 {
150     return ((chr <= ',' && chr != '$' && chr != '#') ||
151             (chr >= ':' && chr < '?') );
152 }
153 
154 int
155 CompareToken(const char *token, const char *comparand)
156 {
157     while (*comparand)
158     {
159         if (*token != *comparand) return 0;
160         token++;
161         comparand++;
162     }
163     if (IsSeparator(comparand[-1])) return 1;
164     if (!IsSeparator(*token)) return 0;
165     return 1;
166 }
167 
168 const char *
169 ScanToken(const char *token, char chr)
170 {
171     while (!IsSeparator(*token))
172     {
173         if (*token == chr) return token;
174         token++;
175     }
176     return 0;
177 }
178 
179 const char *
180 NextLine(const char *pc)
181 {
182     while (*pc != 0)
183     {
184         if (pc[0] == '\n' && pc[1] == '\r') return pc + 2;
185         else if (pc[0] == '\n') return pc + 1;
186         pc++;
187     }
188     return pc;
189 }
190 
191 int
192 TokenLength(const char *pc)
193 {
194     int length = 0;
195 
196     while (!IsSeparator(*pc++)) length++;
197 
198     return length;
199 }
200 
201 const char *
202 NextToken(const char *pc)
203 {
204     /* Skip token */
205     while (!IsSeparator(*pc)) pc++;
206 
207     /* Skip white spaces */
208     while (*pc == ' ' || *pc == '\t') pc++;
209 
210     /* Check for end of line */
211     if (*pc == '\n' || *pc == '\r' || *pc == 0) return 0;
212 
213     /* Check for comment */
214     if (*pc == '#' || *pc == ';') return 0;
215 
216     return pc;
217 }
218 
219 void
220 OutputHeader_stub(FILE *file)
221 {
222     fprintf(file, "/* This file is autogenerated, do not edit. */\n\n"
223             "#include <stubs.h>\n");
224 
225     if (gbTracing)
226     {
227         fprintf(file, "#include <wine/debug.h>\n");
228         fprintf(file, "#include <inttypes.h>\n");
229         fprintf(file, "WINE_DECLARE_DEBUG_CHANNEL(relay);\n");
230     }
231 
232     fprintf(file, "\n");
233 }
234 
235 int
236 OutputLine_stub(FILE *file, EXPORT *pexp)
237 {
238     int i;
239     int bRelay = 0;
240     int bInPrototype = 0;
241 
242     /* Workaround for forwarded externs. See here for an explanation:
243      * https://stackoverflow.com/questions/4060143/forwarding-data-in-a-dll */
244     if (gbMSComp &&
245         (pexp->nCallingConvention == CC_EXTERN) &&
246         (pexp->strTarget.buf != NULL) &&
247         (!!ScanToken(pexp->strTarget.buf, '.')))
248     {
249         fprintf(file, "#pragma comment(linker,\"/export:%s%.*s=%.*s,DATA\")\n\n",
250             gpszUnderscore, pexp->strName.len, pexp->strName.buf, pexp->strTarget.len, pexp->strTarget.buf);
251         return 0;
252     }
253 
254     if (pexp->nCallingConvention != CC_STUB &&
255         (pexp->uFlags & FL_STUB) == 0)
256     {
257         /* Only relay trace stdcall C functions */
258         if (!gbTracing || (pexp->nCallingConvention != CC_STDCALL)
259                 || (pexp->uFlags & FL_NORELAY)
260                 || (pexp->strName.buf[0] == '?'))
261         {
262             return 0;
263         }
264         bRelay = 1;
265     }
266 
267     /* Declare the "real" function */
268     if (bRelay)
269     {
270         fprintf(file, "extern ");
271         bInPrototype = 1;
272     }
273 
274     do
275     {
276         if (pexp->uFlags & FL_REGISTER)
277         {
278             /* FIXME: Not sure this is right */
279             fprintf(file, "void ");
280         }
281         else if (pexp->uFlags & FL_RET64)
282         {
283             fprintf(file, "__int64 ");
284         }
285         else
286         {
287             fprintf(file, "int ");
288         }
289 
290         if ((giArch == ARCH_X86) &&
291             pexp->nCallingConvention == CC_STDCALL)
292         {
293             fprintf(file, "__stdcall ");
294         }
295 
296         /* Check for C++ */
297         if (pexp->strName.buf[0] == '?')
298         {
299             fprintf(file, "stub_function%d(", pexp->nNumber);
300         }
301         else
302         {
303             if (!bRelay || bInPrototype)
304                 fprintf(file, "%.*s(", pexp->strName.len, pexp->strName.buf);
305             else
306                 fprintf(file, "$relaytrace$%.*s(", pexp->strName.len, pexp->strName.buf);
307         }
308 
309         for (i = 0; i < pexp->nArgCount; i++)
310         {
311             if (i != 0) fprintf(file, ", ");
312             switch (pexp->anArgs[i])
313             {
314                 case ARG_LONG:   fprintf(file, "long");     break;
315                 case ARG_PTR:    fprintf(file, "void*");    break;
316                 case ARG_STR:    fprintf(file, "char*");    break;
317                 case ARG_WSTR:   fprintf(file, "wchar_t*"); break;
318                 case ARG_DBL:    fprintf(file, "double");   break;
319                 case ARG_INT64:  fprintf(file, "__int64");  break;
320                 /* __int128 is not supported on x86, so use a custom type */
321                 case ARG_INT128: fprintf(file, "MyInt128"); break;
322                 case ARG_FLOAT:  fprintf(file, "float");    break;
323             }
324             fprintf(file, " a%d", i);
325         }
326 
327         if (bInPrototype)
328         {
329             fprintf(file, ");\n\n");
330         }
331     } while (bInPrototype--);
332 
333     if (!bRelay)
334     {
335         fprintf(file, ")\n{\n\tDbgPrint(\"WARNING: calling stub %.*s(",
336                 pexp->strName.len, pexp->strName.buf);
337     }
338     else
339     {
340         fprintf(file, ")\n{\n");
341         if (pexp->uFlags & FL_REGISTER)
342         {
343             /* No return value */
344         }
345         else if (pexp->uFlags & FL_RET64)
346         {
347             fprintf(file, "\t__int64 retval;\n");
348         }
349         else
350         {
351             fprintf(file, "\tint retval;\n");
352         }
353         fprintf(file, "\tif (TRACE_ON(relay))\n\t\tDPRINTF(\"%s: %.*s(",
354                 pszDllName, pexp->strName.len, pexp->strName.buf);
355     }
356 
357     for (i = 0; i < pexp->nArgCount; i++)
358     {
359         if (i != 0) fprintf(file, ",");
360         switch (pexp->anArgs[i])
361         {
362             case ARG_LONG:   fprintf(file, "0x%%lx"); break;
363             case ARG_PTR:    fprintf(file, "0x%%p");  break;
364             case ARG_STR:    fprintf(file, "'%%s'");  break;
365             case ARG_WSTR:   fprintf(file, "'%%ws'"); break;
366             case ARG_DBL:    fprintf(file, "%%f");    break;
367             case ARG_INT64:  fprintf(file, "0x%%\"PRIx64\""); break;
368             case ARG_INT128: fprintf(file, "0x%%\"PRIx64\"-0x%%\"PRIx64\""); break;
369             case ARG_FLOAT:  fprintf(file, "%%f");    break;
370         }
371     }
372     fprintf(file, ")\\n\"");
373 
374     for (i = 0; i < pexp->nArgCount; i++)
375     {
376         fprintf(file, ", ");
377         switch (pexp->anArgs[i])
378         {
379             case ARG_LONG: case ARG_PTR: case ARG_STR:
380             case ARG_WSTR: case ARG_DBL: case ARG_INT64:
381                 fprintf(file, "a%d", i); break;
382             case ARG_INT128:
383                 fprintf(file, "a%d.lower, a%d.upper", i, i); break;
384             case ARG_FLOAT:
385                 fprintf(file, "a%d", i); break;
386         }
387     }
388     fprintf(file, ");\n");
389 
390     if (pexp->nCallingConvention == CC_STUB)
391     {
392         fprintf(file, "\t__wine_spec_unimplemented_stub(\"%s\", __FUNCTION__);\n", pszDllName);
393     }
394     else if (bRelay)
395     {
396         if (pexp->uFlags & FL_REGISTER)
397         {
398             fprintf(file,"\t");
399         }
400         else
401         {
402             fprintf(file, "\tretval = ");
403         }
404         fprintf(file, "%.*s(", pexp->strName.len, pexp->strName.buf);
405 
406         for (i = 0; i < pexp->nArgCount; i++)
407         {
408             if (i != 0) fprintf(file, ", ");
409             fprintf(file, "a%d", i);
410         }
411         fprintf(file, ");\n");
412     }
413 
414     if (!bRelay)
415         fprintf(file, "\treturn 0;\n}\n\n");
416     else if ((pexp->uFlags & FL_REGISTER) == 0)
417     {
418         if (pexp->uFlags & FL_RET64)
419         {
420             fprintf(file, "\tif (TRACE_ON(relay))\n\t\tDPRINTF(\"%s: %.*s: retval = 0x%%\"PRIx64\"\\n\", retval);\n",
421                     pszDllName, pexp->strName.len, pexp->strName.buf);
422         }
423         else
424         {
425             fprintf(file, "\tif (TRACE_ON(relay))\n\t\tDPRINTF(\"%s: %.*s: retval = 0x%%lx\\n\", retval);\n",
426                     pszDllName, pexp->strName.len, pexp->strName.buf);
427         }
428         fprintf(file, "\treturn retval;\n}\n\n");
429     }
430 
431     return 1;
432 }
433 
434 void
435 OutputHeader_asmstub(FILE *file, char *libname)
436 {
437     fprintf(file, "; File generated automatically, do not edit!\n\n");
438 
439     if (giArch == ARCH_X86)
440     {
441         fprintf(file, ".586\n.model flat\n.code\n");
442     }
443     else if (giArch == ARCH_AMD64)
444     {
445         fprintf(file, ".code\n");
446     }
447     else if (giArch == ARCH_ARM || giArch == ARCH_ARM64)
448     {
449         fprintf(file, "    AREA |.text|,ALIGN=2,CODE,READONLY\n\n");
450     }
451 }
452 
453 void
454 Output_stublabel(FILE *fileDest, char* pszSymbolName)
455 {
456     if (giArch == ARCH_ARM || giArch == ARCH_ARM64)
457     {
458         fprintf(fileDest,
459                 "\tEXPORT |%s| [FUNC]\n|%s|\n",
460                 pszSymbolName,
461                 pszSymbolName);
462     }
463     else
464     {
465         fprintf(fileDest,
466                 "PUBLIC %s\n%s: nop\n",
467                 pszSymbolName,
468                 pszSymbolName);
469     }
470 }
471 
472 int
473 OutputLine_asmstub(FILE *fileDest, EXPORT *pexp)
474 {
475     char szNameBuffer[128];
476 
477     /* Handle autoname */
478     if (pexp->strName.len == 1 && pexp->strName.buf[0] == '@')
479     {
480         sprintf(szNameBuffer, "%s_stub_ordinal%d",
481                 gpszUnderscore, pexp->nOrdinal);
482     }
483     else if (giArch != ARCH_X86)
484     {
485         /* Does the string already have stdcall decoration? */
486         const char *pcAt = ScanToken(pexp->strName.buf, '@');
487         if (pcAt && (pcAt < (pexp->strName.buf + pexp->strName.len)) &&
488             (pexp->strName.buf[0] == '_'))
489         {
490             /* Skip leading underscore and remove trailing decoration */
491             sprintf(szNameBuffer, "_stub_%.*s",
492                     (int)(pcAt - pexp->strName.buf - 1),
493                     pexp->strName.buf + 1);
494         }
495         else
496         {
497             sprintf(szNameBuffer, "_stub_%.*s",
498                     pexp->strName.len, pexp->strName.buf);
499         }
500     }
501     else if (pexp->nCallingConvention == CC_STDCALL)
502     {
503         sprintf(szNameBuffer, "__stub_%.*s@%d",
504                 pexp->strName.len, pexp->strName.buf, pexp->nStackBytes);
505     }
506     else if (pexp->nCallingConvention == CC_FASTCALL)
507     {
508         sprintf(szNameBuffer, "@_stub_%.*s@%d",
509                 pexp->strName.len, pexp->strName.buf, pexp->nStackBytes);
510     }
511     else if ((pexp->nCallingConvention == CC_CDECL) ||
512              (pexp->nCallingConvention == CC_THISCALL) ||
513              (pexp->nCallingConvention == CC_EXTERN) ||
514              (pexp->nCallingConvention == CC_STUB))
515     {
516         sprintf(szNameBuffer, "__stub_%.*s",
517                 pexp->strName.len, pexp->strName.buf);
518     }
519     else
520     {
521         fprintf(stderr, "Invalid calling convention");
522         return 0;
523     }
524 
525     Output_stublabel(fileDest, szNameBuffer);
526 
527     return 1;
528 }
529 
530 void
531 OutputHeader_def(FILE *file, char *libname)
532 {
533     fprintf(file,
534             "; File generated automatically, do not edit!\n\n"
535             "NAME %s\n\n"
536             "EXPORTS\n",
537             libname);
538 }
539 
540 void
541 PrintName(FILE *fileDest, EXPORT *pexp, PSTRING pstr, int fDeco)
542 {
543     const char *pcName = pstr->buf;
544     int nNameLength = pstr->len;
545     const char* pcDot, *pcAt;
546     char namebuffer[19];
547 
548     if ((nNameLength == 1) && (pcName[0] == '@'))
549     {
550         sprintf(namebuffer, "ordinal%d", pexp->nOrdinal);
551         pcName = namebuffer;
552         nNameLength = strlen(namebuffer);
553     }
554 
555     /* Check for non-x86 first */
556     if (giArch != ARCH_X86)
557     {
558         /* Does the string already have stdcall decoration? */
559         pcAt = ScanToken(pcName, '@');
560         if (pcAt && (pcAt < (pcName + nNameLength)) && (pcName[0] == '_'))
561         {
562             /* Skip leading underscore and remove trailing decoration */
563             pcName++;
564             nNameLength = (int)(pcAt - pcName);
565         }
566 
567         /* Print the undecorated function name */
568         fprintf(fileDest, "%.*s", nNameLength, pcName);
569     }
570     else if (fDeco &&
571         ((pexp->nCallingConvention == CC_STDCALL) ||
572          (pexp->nCallingConvention == CC_FASTCALL)))
573     {
574         /* Beware with C++ exports */
575         int is_cpp = pcName[0] == '?';
576 
577         /* Scan for a dll forwarding dot */
578         pcDot = ScanToken(pcName, '.');
579         if (pcDot)
580         {
581             /* First print the dll name, followed by a dot */
582             nNameLength = (int)(pcDot - pcName);
583             fprintf(fileDest, "%.*s.", nNameLength, pcName);
584 
585             /* Now the actual function name */
586             pcName = pcDot + 1;
587             nNameLength = pexp->strTarget.len - nNameLength - 1;
588         }
589 
590         /* Does the string already have decoration? */
591         pcAt = ScanToken(pcName, '@');
592         if (pcAt && (pcAt < (pcName + nNameLength)))
593         {
594             /* On GCC, we need to remove the leading stdcall underscore, but not for C++ exports */
595             if (!gbMSComp && !is_cpp && (pexp->nCallingConvention == CC_STDCALL))
596             {
597                 pcName++;
598                 nNameLength--;
599             }
600 
601             /* Print the already decorated function name */
602             fprintf(fileDest, "%.*s", nNameLength, pcName);
603         }
604         else
605         {
606             /* Print the prefix, but skip it for (GCC && stdcall) */
607             if (gbMSComp || (pexp->nCallingConvention != CC_STDCALL))
608             {
609                 fprintf(fileDest, "%c", pexp->nCallingConvention == CC_FASTCALL ? '@' : '_');
610             }
611 
612             /* Print the name with trailing decoration */
613             fprintf(fileDest, "%.*s@%d", nNameLength, pcName, pexp->nStackBytes);
614         }
615     }
616     else if (fDeco && (pexp->nCallingConvention == CC_CDECL) && gbMSComp)
617     {
618         /* Print with cdecl decoration */
619         fprintf(fileDest, "_%.*s", nNameLength, pcName);
620     }
621     else
622     {
623         /* Print the undecorated function name */
624         fprintf(fileDest, "%.*s", nNameLength, pcName);
625     }
626 }
627 
628 void
629 OutputLine_def_MS(FILE *fileDest, EXPORT *pexp)
630 {
631     PrintName(fileDest, pexp, &pexp->strName, 0);
632 
633     if (gbImportLib)
634     {
635         /* Redirect to a stub function, to get the right decoration in the lib */
636         fprintf(fileDest, "=_stub_");
637         PrintName(fileDest, pexp, &pexp->strName, 0);
638     }
639     else if (pexp->strTarget.buf)
640     {
641         if (pexp->strName.buf[0] == '?')
642         {
643             //fprintf(stderr, "warning: ignoring C++ redirection %.*s -> %.*s\n",
644             //        pexp->strName.len, pexp->strName.buf, pexp->strTarget.len, pexp->strTarget.buf);
645         }
646         else
647         {
648             fprintf(fileDest, "=");
649 
650             /* If the original name was decorated, use decoration in the forwarder as well */
651             if ((giArch == ARCH_X86) && ScanToken(pexp->strName.buf, '@') &&
652                 !ScanToken(pexp->strTarget.buf, '@') &&
653                 ((pexp->nCallingConvention == CC_STDCALL) ||
654                 (pexp->nCallingConvention == CC_FASTCALL)) )
655             {
656                 PrintName(fileDest, pexp, &pexp->strTarget, 1);
657             }
658             else
659             {
660                 /* Write the undecorated redirection name */
661                 fprintf(fileDest, "%.*s", pexp->strTarget.len, pexp->strTarget.buf);
662             }
663         }
664     }
665     else if (((pexp->uFlags & FL_STUB) || (pexp->nCallingConvention == CC_STUB)) &&
666              (pexp->strName.buf[0] == '?'))
667     {
668         /* C++ stubs are forwarded to C stubs */
669         fprintf(fileDest, "=stub_function%d", pexp->nNumber);
670     }
671     else if (gbTracing && ((pexp->uFlags & FL_NORELAY) == 0) && (pexp->nCallingConvention == CC_STDCALL) &&
672             (pexp->strName.buf[0] != '?'))
673     {
674         /* Redirect it to the relay-tracing trampoline */
675         fprintf(fileDest, "=$relaytrace$%.*s", pexp->strName.len, pexp->strName.buf);
676     }
677 }
678 
679 void
680 OutputLine_def_GCC(FILE *fileDest, EXPORT *pexp)
681 {
682     int bTracing = 0;
683     /* Print the function name, with decoration for export libs */
684     PrintName(fileDest, pexp, &pexp->strName, gbImportLib);
685     DbgPrint("Generating def line for '%.*s'\n", pexp->strName.len, pexp->strName.buf);
686 
687     /* Check if this is a forwarded export */
688     if (pexp->strTarget.buf)
689     {
690         int fIsExternal = !!ScanToken(pexp->strTarget.buf, '.');
691         DbgPrint("Got redirect '%.*s'\n", pexp->strTarget.len, pexp->strTarget.buf);
692 
693         /* print the target name, don't decorate if it is external */
694         fprintf(fileDest, pexp->uFlags & FL_IMPSYM ? "==" : "=");
695         PrintName(fileDest, pexp, &pexp->strTarget, !fIsExternal);
696     }
697     else if (((pexp->uFlags & FL_STUB) || (pexp->nCallingConvention == CC_STUB)) &&
698              (pexp->strName.buf[0] == '?'))
699     {
700         /* C++ stubs are forwarded to C stubs */
701         fprintf(fileDest, "=stub_function%d", pexp->nNumber);
702     }
703     else if (gbTracing && ((pexp->uFlags & FL_NORELAY) == 0) &&
704              (pexp->nCallingConvention == CC_STDCALL) &&
705             (pexp->strName.buf[0] != '?'))
706     {
707         /* Redirect it to the relay-tracing trampoline */
708         char buf[256];
709         STRING strTarget;
710         fprintf(fileDest, "=");
711         sprintf(buf, "$relaytrace$%.*s", pexp->strName.len, pexp->strName.buf);
712         strTarget.buf = buf;
713         strTarget.len = pexp->strName.len + 12;
714         PrintName(fileDest, pexp, &strTarget, 1);
715         bTracing = 1;
716     }
717 
718     /* Special handling for stdcall and fastcall, but not C++ exports*/
719     if ((giArch == ARCH_X86) &&
720         (pexp->strName.buf[0] != '?') &&
721         ((pexp->nCallingConvention == CC_STDCALL) ||
722          (pexp->nCallingConvention == CC_FASTCALL)))
723     {
724         /* Is this the import lib? */
725         if (gbImportLib)
726         {
727             /* Is the name in the spec file decorated? */
728             const char* pcDeco = ScanToken(pexp->strName.buf, '@');
729             if (pcDeco &&
730                 (pexp->strName.len > 1) &&
731                 (pcDeco < pexp->strName.buf + pexp->strName.len))
732             {
733                 /* Write the name including the leading @  */
734                 fprintf(fileDest, "==%.*s", pexp->strName.len, pexp->strName.buf);
735             }
736         }
737         else if ((!pexp->strTarget.buf) && !(bTracing))
738         {
739             /* Write a forwarder to the actual decorated symbol */
740             fprintf(fileDest, "=");
741             PrintName(fileDest, pexp, &pexp->strName, 1);
742         }
743     }
744 }
745 
746 int
747 OutputLine_def(FILE *fileDest, EXPORT *pexp)
748 {
749     /* Don't add private exports to the import lib */
750     if (gbImportLib && (pexp->uFlags & FL_PRIVATE))
751     {
752         DbgPrint("OutputLine_def: skipping private export '%.*s'...\n", pexp->strName.len, pexp->strName.buf);
753         return 1;
754     }
755 
756     /* Handle import symbols */
757     if (pexp->uFlags & FL_IMPSYM)
758     {
759         /* Skip these, if we are not creating an import lib, or if this is MS */
760         if (!gbImportLib || gbMSComp)
761         {
762             DbgPrint("OutputLine_def: skipping import symbol '%.*s'...\n", pexp->strName.len, pexp->strName.buf);
763             return 1;
764         }
765     }
766 
767     /* For MS linker, forwarded externs are managed via #pragma comment(linker,"/export:_data=org.data,DATA") */
768     if (gbMSComp && !gbImportLib && (pexp->nCallingConvention == CC_EXTERN) &&
769         (pexp->strTarget.buf != NULL) && !!ScanToken(pexp->strTarget.buf, '.'))
770     {
771         DbgPrint("OutputLine_def: skipping forwarded extern export '%.*s' ->'%.*s'...\n",
772             pexp->strName.len, pexp->strName.buf, pexp->strTarget.len, pexp->strTarget.buf);
773         return 1;
774     }
775 
776     DbgPrint("OutputLine_def: '%.*s'...\n", pexp->strName.len, pexp->strName.buf);
777     fprintf(fileDest, " ");
778 
779     if (gbMSComp)
780         OutputLine_def_MS(fileDest, pexp);
781     else
782         OutputLine_def_GCC(fileDest, pexp);
783 
784     /* On GCC builds we force ordinals */
785     if ((pexp->uFlags & FL_ORDINAL) || (!gbMSComp && !gbImportLib))
786     {
787         fprintf(fileDest, " @%d", pexp->nOrdinal);
788     }
789 
790     if (pexp->uFlags & FL_NONAME)
791     {
792         fprintf(fileDest, " NONAME");
793     }
794 
795     /* Either PRIVATE or DATA */
796     if (pexp->uFlags & FL_PRIVATE)
797     {
798         fprintf(fileDest, " PRIVATE");
799     }
800     else if (pexp->nCallingConvention == CC_EXTERN)
801     {
802         fprintf(fileDest, " DATA");
803     }
804 
805     fprintf(fileDest, "\n");
806 
807     return 1;
808 }
809 
810 void
811 PrintNameOrImpName(FILE *fileDest, EXPORT *pexp, PSTRING pstr, int fDeco, int fImp)
812 {
813     if (fImp)
814     {
815         fprintf(fileDest, "__imp_");
816     }
817 
818     PrintName(fileDest, pexp, pstr, fDeco);
819 }
820 
821 void
822 OutputAlias(FILE *fileDest, EXPORT *pexp, int fImp)
823 {
824     if ((giArch == ARCH_ARM) || (giArch == ARCH_ARM64))
825     {
826         fprintf(fileDest, "    IMPORT ");
827         PrintNameOrImpName(fileDest, pexp, &pexp->strName, 1, fImp);
828         fprintf(fileDest, ", WEAK ");
829         PrintNameOrImpName(fileDest, pexp, &pexp->strTarget, 1, fImp);
830         fprintf(fileDest, "\n");
831     }
832     else
833     {
834         fprintf(fileDest, "    EXTERN ");
835         PrintNameOrImpName(fileDest, pexp, &pexp->strTarget, 1, fImp);
836         fprintf(fileDest, ":PROC\n    ALIAS <");
837         PrintNameOrImpName(fileDest, pexp, &pexp->strName, 1, fImp);
838         fprintf(fileDest, "> = <");
839         PrintNameOrImpName(fileDest, pexp, &pexp->strTarget, 1, fImp);
840         fprintf(fileDest, ">\n");
841     }
842 }
843 
844 int
845 OutputLine_implib_asm(FILE *fileDest, EXPORT *pexp)
846 {
847     if ((pexp->uFlags & FL_IMPSYM) == 0)
848     {
849         return 1;
850     }
851 
852     OutputAlias(fileDest, pexp, 0);
853     OutputAlias(fileDest, pexp, 1);
854 
855     return 1;
856 }
857 
858 void
859 Fatalv(
860     const char* filename,
861     unsigned nLine,
862     const char *pcLine,
863     const char *pc,
864     size_t errorlen,
865     const char *format,
866     va_list argptr)
867 {
868     unsigned i, errorpos, len;
869     const char* pcLineEnd;
870 
871     /* Get the length of the line */
872     pcLineEnd = strpbrk(pcLine, "\r\n");
873     len = (unsigned)(pcLineEnd - pcLine);
874 
875     if (pc == NULL)
876     {
877         pc = pcLine + len - 1;
878         errorlen = 1;
879     }
880 
881     errorpos = (unsigned)(pc - pcLine);
882 
883     /* Output the error message */
884     fprintf(stderr, "ERROR: (%s:%u:%u): ", filename, nLine, errorpos);
885     vfprintf(stderr, format, argptr);
886     fprintf(stderr, "\n");
887 
888     /* Output the line with the error */
889     fprintf(stderr, "> %.*s\n", len, pcLine);
890 
891     if (errorlen == 0)
892     {
893         errorlen = TokenLength(pc);
894     }
895 
896     for (i = 0; i < errorpos + 2; i++)
897     {
898         fprintf(stderr, " ");
899     }
900     for (i = 0; i < errorlen; i++)
901     {
902         fprintf(stderr, "~");
903     }
904     fprintf(stderr, "\n");
905     exit(-1);
906 }
907 
908 void
909 Fatal(
910     const char* filename,
911     unsigned nLine,
912     const char *pcLine,
913     const char *pc,
914     size_t errorlen,
915     const char *format,
916     ...)
917 {
918     va_list argptr;
919 
920     va_start(argptr, format);
921     Fatalv(filename, nLine, pcLine, pc, errorlen, format, argptr);
922     va_end(argptr);
923 }
924 
925 EXPORT *
926 ParseFile(char* pcStart, FILE *fileDest, unsigned *cExports)
927 {
928     EXPORT *pexports;
929     const char *pc, *pcLine;
930     int cLines, nLine;
931     EXPORT exp;
932     int included;
933     unsigned int i;
934 
935     *cExports = 0;
936 
937     //fprintf(stderr, "info: line %d, pcStart:'%.30s'\n", nLine, pcStart);
938 
939     /* Count the lines */
940     for (cLines = 1, pcLine = pcStart; *pcLine; pcLine = NextLine(pcLine), cLines++)
941     {
942         /* Nothing */
943     }
944 
945     /* Allocate an array of EXPORT structures */
946     pexports = malloc(cLines * sizeof(EXPORT));
947     if (pexports == NULL)
948     {
949         fprintf(stderr, "ERROR: %s: failed to allocate EXPORT array of %u elements\n", pszSourceFileName, cLines);
950         return NULL;
951     }
952 
953     /* Loop all lines */
954     nLine = 1;
955     exp.nNumber = 0;
956     for (pcLine = pcStart; *pcLine; pcLine = NextLine(pcLine), nLine++)
957     {
958         pc = pcLine;
959 
960         exp.strName.buf = NULL;
961         exp.strName.len = 0;
962         exp.strTarget.buf = NULL;
963         exp.strTarget.len = 0;
964         exp.nArgCount = 0;
965         exp.uFlags = 0;
966         exp.nNumber++;
967         exp.nStartVersion = 0;
968         exp.nEndVersion = 0xFFFFFFFF;
969         exp.bVersionIncluded = 1;
970 
971         /* Skip white spaces */
972         while (*pc == ' ' || *pc == '\t') pc++;
973 
974         /* Check for line break or comment */
975         if ((*pc == '\r') || (*pc == '\n') ||
976             (*pc == ';') || (*pc == '#'))
977         {
978             continue;
979         }
980 
981         /* On EOF we are done */
982         if (*pc == 0)
983         {
984             return pexports;
985         }
986 
987         /* Now we should get either an ordinal or @ */
988         if (*pc == '@')
989         {
990             exp.nOrdinal = -1;
991         }
992         else if ((*pc >= '0') && (*pc <= '9'))
993         {
994             char* end;
995             long int number = strtol(pc, &end, 10);
996             if ((*end != ' ') && (*end != '\t'))
997             {
998                 Fatal(pszSourceFileName, nLine, pcLine, end, 0, "Unexpected character(s) after ordinal");
999             }
1000 
1001             if ((number < 0) || (number > 0xFFFE))
1002             {
1003                 Fatal(pszSourceFileName, nLine, pcLine, pc, 0, "Invalid value for ordinal");
1004             }
1005 
1006             exp.nOrdinal = number;
1007 
1008             /* The import lib should contain the ordinal only if -ordinal was specified */
1009             if (!gbImportLib)
1010                 exp.uFlags |= FL_ORDINAL;
1011         }
1012         else
1013         {
1014             Fatal(pszSourceFileName, nLine, pcLine, pc, 0, "Expected '@' or ordinal");
1015         }
1016 
1017         /* Go to next token (type) */
1018         if (!(pc = NextToken(pc)))
1019         {
1020             Fatal(pszSourceFileName, nLine, pcLine, pc, 1, "Unexpected end of line");
1021         }
1022 
1023         //fprintf(stderr, "info: Token:'%.*s'\n", TokenLength(pc), pc);
1024 
1025         /* Now we should get the type */
1026         if (CompareToken(pc, "stdcall"))
1027         {
1028             exp.nCallingConvention = CC_STDCALL;
1029         }
1030         else if (CompareToken(pc, "cdecl") ||
1031                  CompareToken(pc, "varargs"))
1032         {
1033             exp.nCallingConvention = CC_CDECL;
1034         }
1035         else if (CompareToken(pc, "fastcall"))
1036         {
1037             exp.nCallingConvention = CC_FASTCALL;
1038         }
1039         else if (CompareToken(pc, "thiscall"))
1040         {
1041             exp.nCallingConvention = CC_THISCALL;
1042         }
1043         else if (CompareToken(pc, "extern"))
1044         {
1045             exp.nCallingConvention = CC_EXTERN;
1046         }
1047         else if (CompareToken(pc, "stub"))
1048         {
1049             exp.nCallingConvention = CC_STUB;
1050         }
1051         else
1052         {
1053             Fatal(pszSourceFileName, nLine, pcLine, pc, 0, "Invalid calling convention");
1054         }
1055 
1056         /* Go to next token (options or name) */
1057         if (!(pc = NextToken(pc)))
1058         {
1059             Fatal(pszSourceFileName, nLine, pcLine, pc, 1, "Unexpected end of line");
1060         }
1061 
1062         /* Handle options */
1063         included = 1;
1064         while (*pc == '-')
1065         {
1066             if (CompareToken(pc, "-arch="))
1067             {
1068                 /* Default to not included */
1069                 included = 0;
1070                 pc += 5;
1071 
1072                 /* Look if we are included */
1073                 do
1074                 {
1075                     pc++;
1076                     if (CompareToken(pc, pszArchString) ||
1077                         CompareToken(pc, pszArchString2))
1078                     {
1079                         included = 1;
1080                     }
1081 
1082                     /* Skip to next arch or end */
1083                     while (*pc > ',') pc++;
1084                 } while (*pc == ',');
1085             }
1086             else if (CompareToken(pc, "-i386"))
1087             {
1088                 if (giArch != ARCH_X86) included = 0;
1089             }
1090             else if (CompareToken(pc, "-version="))
1091             {
1092                 const char *pcVersionStart = pc + 9;
1093 
1094                 /* Default to not included */
1095                 exp.bVersionIncluded = 0;
1096                 pc += 8;
1097 
1098                 /* Look if we are included */
1099                 do
1100                 {
1101                     unsigned version, endversion;
1102 
1103                     /* Optionally skip leading '0x' */
1104                     pc++;
1105                     if ((pc[0] == '0') && (pc[1] == 'x')) pc += 2;
1106 
1107                     /* Now get the version number */
1108                     endversion = version = strtoul(pc, (char**)&pc, 16);
1109 
1110                     /* Check if it's a range */
1111                     if (pc[0] == '+')
1112                     {
1113                         endversion = 0xFFF;
1114                         pc++;
1115                     }
1116                     else if (pc[0] == '-')
1117                     {
1118                         /* Optionally skip leading '0x' */
1119                         pc++;
1120                         if ((pc[0] == '0') && (pc[1] == 'x')) pc += 2;
1121                         endversion = strtoul(pc, (char**)&pc, 16);
1122                     }
1123 
1124                     /* Check for degenerate range */
1125                     if (version > endversion)
1126                     {
1127                         Fatal(pszSourceFileName,
1128                               nLine,
1129                               pcLine,
1130                               pcVersionStart,
1131                               pc - pcVersionStart,
1132                               "Invalid version range");
1133                     }
1134 
1135                     exp.nStartVersion = version;
1136                     exp.nEndVersion = endversion;
1137 
1138                     /* Now compare the range with our version */
1139                     if ((guOsVersion >= version) &&
1140                         (guOsVersion <= endversion))
1141                     {
1142                         exp.bVersionIncluded = 1;
1143                     }
1144 
1145                     /* Skip to next arch or end */
1146                     while (*pc > ',') pc++;
1147 
1148                 } while (*pc == ',');
1149             }
1150             else if (CompareToken(pc, "-private"))
1151             {
1152                 exp.uFlags |= FL_PRIVATE;
1153             }
1154             else if (CompareToken(pc, "-noname"))
1155             {
1156                 exp.uFlags |= FL_ORDINAL | FL_NONAME;
1157             }
1158             else if (CompareToken(pc, "-impsym"))
1159             {
1160                 exp.uFlags |= FL_IMPSYM;
1161             }
1162             else if (CompareToken(pc, "-ordinal"))
1163             {
1164                 exp.uFlags |= FL_ORDINAL;
1165                 /* GCC doesn't automatically import by ordinal if an ordinal
1166                  * is found in the def file. Force it. */
1167                 if (gbImportLib && !gbMSComp)
1168                     exp.uFlags |= FL_NONAME;
1169             }
1170             else if (CompareToken(pc, "-stub"))
1171             {
1172                 exp.uFlags |= FL_STUB;
1173             }
1174             else if (CompareToken(pc, "-norelay"))
1175             {
1176                 exp.uFlags |= FL_NORELAY;
1177             }
1178             else if (CompareToken(pc, "-ret64"))
1179             {
1180                 exp.uFlags |= FL_RET64;
1181             }
1182             else if (CompareToken(pc, "-register"))
1183             {
1184                 exp.uFlags |= FL_REGISTER;
1185             }
1186             else
1187             {
1188                 fprintf(stdout,
1189                         "INFO: %s line %d: Ignored option: '%.*s'\n",
1190                         pszSourceFileName,
1191                         nLine,
1192                         TokenLength(pc),
1193                         pc);
1194             }
1195 
1196             /* Go to next token */
1197             pc = NextToken(pc);
1198         }
1199 
1200         //fprintf(stderr, "info: Name:'%.10s'\n", pc);
1201 
1202         /* If arch didn't match ours, skip this entry */
1203         if (!included) continue;
1204 
1205         /* Get name */
1206         exp.strName.buf = pc;
1207         exp.strName.len = TokenLength(pc);
1208         //DbgPrint("Got name: '%.*s'\n", exp.strName.len, exp.strName.buf);
1209 
1210         /* Check for autoname */
1211         if ((exp.strName.len == 1) && (exp.strName.buf[0] == '@'))
1212         {
1213             exp.uFlags |= FL_ORDINAL | FL_NONAME;
1214         }
1215 
1216         /* Handle parameters */
1217         exp.nStackBytes = 0;
1218         pc = NextToken(pc);
1219         /* Extern can't have parameters, and it's optional to provide ones for stubs. All other exports must have them */
1220         if (!pc && (exp.nCallingConvention != CC_EXTERN && exp.nCallingConvention != CC_STUB))
1221         {
1222             Fatal(pszSourceFileName, nLine, pcLine, pc, 1, "Unexpected end of line");
1223         }
1224 
1225         if (pc && (exp.nCallingConvention != CC_EXTERN))
1226         {
1227             /* Verify syntax */
1228             if (*pc++ != '(')
1229             {
1230                 Fatal(pszSourceFileName, nLine, pcLine, pc - 1, 0, "Expected '('");
1231             }
1232 
1233             /* Skip whitespaces */
1234             while (*pc == ' ' || *pc == '\t') pc++;
1235 
1236             exp.nStackBytes = 0;
1237             while (*pc >= '0')
1238             {
1239                 if (CompareToken(pc, "long"))
1240                 {
1241                     exp.nStackBytes += 4;
1242                     exp.anArgs[exp.nArgCount] = ARG_LONG;
1243                 }
1244                 else if (CompareToken(pc, "double"))
1245                 {
1246                     exp.nStackBytes += 8;
1247                     exp.anArgs[exp.nArgCount] = ARG_DBL;
1248                 }
1249                 else if (CompareToken(pc, "ptr"))
1250                 {
1251                     exp.nStackBytes += 4; // sizeof(void*) on x86
1252                     exp.anArgs[exp.nArgCount] = ARG_PTR;
1253                 }
1254                 else if (CompareToken(pc, "str"))
1255                 {
1256                     exp.nStackBytes += 4; // sizeof(void*) on x86
1257                     exp.anArgs[exp.nArgCount] = ARG_STR;
1258                 }
1259                 else if (CompareToken(pc, "wstr"))
1260                 {
1261                     exp.nStackBytes += 4; // sizeof(void*) on x86
1262                     exp.anArgs[exp.nArgCount] = ARG_WSTR;
1263                 }
1264                 else if (CompareToken(pc, "int64"))
1265                 {
1266                     exp.nStackBytes += 8;
1267                     exp.anArgs[exp.nArgCount] = ARG_INT64;
1268                 }
1269                 else if (CompareToken(pc, "int128"))
1270                 {
1271                     exp.nStackBytes += 16;
1272                     exp.anArgs[exp.nArgCount] = ARG_INT128;
1273                 }
1274                 else if (CompareToken(pc, "float"))
1275                 {
1276                     exp.nStackBytes += 4;
1277                     exp.anArgs[exp.nArgCount] = ARG_FLOAT;
1278                 }
1279                 else
1280                 {
1281                     Fatal(pszSourceFileName, nLine, pcLine, pc, 0, "Unrecognized type");
1282                 }
1283 
1284                 exp.nArgCount++;
1285 
1286                 /* Go to next parameter */
1287                 if (!(pc = NextToken(pc)))
1288                 {
1289                     Fatal(pszSourceFileName, nLine, pcLine, pc, 1, "Unexpected end of line");
1290                 }
1291             }
1292 
1293             /* Check syntax */
1294             if (*pc++ != ')')
1295             {
1296                 Fatal(pszSourceFileName, nLine, pcLine, pc - 1, 0, "Expected ')'");
1297             }
1298 
1299             /* Go to next token */
1300             pc = NextToken(pc);
1301         }
1302 
1303         /* Handle special stub cases */
1304         if (exp.nCallingConvention == CC_STUB)
1305         {
1306             /* If we got parameters, assume STDCALL */
1307             if (exp.nArgCount != 0)
1308             {
1309                 exp.nCallingConvention = CC_STDCALL;
1310                 exp.uFlags |= FL_STUB;
1311             }
1312 
1313             /* Check for c++ mangled name */
1314             if (exp.strName.buf[0] == '?')
1315             {
1316                 //printf("Found c++ mangled name...\n");
1317                 //
1318             }
1319             else
1320             {
1321                 /* Check for stdcall name */
1322                 const char *p = ScanToken(exp.strName.buf, '@');
1323                 if (p && (p - exp.strName.buf < exp.strName.len))
1324                 {
1325                     int i;
1326 
1327                     /* Truncate the name to before the @ */
1328                     exp.strName.len = (int)(p - exp.strName.buf);
1329                     if (exp.strName.len < 1)
1330                     {
1331                         Fatal(pszSourceFileName, nLine, pcLine, p, 1, "Unexpected @");
1332                     }
1333                     exp.nStackBytes = atoi(p + 1);
1334                     exp.nArgCount =  exp.nStackBytes / 4;
1335                     exp.nCallingConvention = CC_STDCALL;
1336                     exp.uFlags |= FL_STUB;
1337                     for (i = 0; i < exp.nArgCount; i++)
1338                         exp.anArgs[i] = ARG_LONG;
1339                 }
1340             }
1341         }
1342 
1343         /* Check optional redirection */
1344         if (pc)
1345         {
1346             exp.strTarget.buf = pc;
1347             exp.strTarget.len = TokenLength(pc);
1348 
1349             /* Check syntax (end of line) */
1350             if (NextToken(pc))
1351             {
1352                 Fatal(pszSourceFileName, nLine, pcLine, NextToken(pc), 0, "Excess token(s) at end of definition");
1353             }
1354 
1355             /* Don't relay-trace forwarded functions */
1356             exp.uFlags |= FL_NORELAY;
1357         }
1358         else
1359         {
1360             exp.strTarget.buf = NULL;
1361             exp.strTarget.len = 0;
1362         }
1363 
1364         /* Check for no-name without ordinal */
1365         if ((exp.uFlags & FL_ORDINAL) && (exp.nOrdinal == -1))
1366         {
1367             Fatal(pszSourceFileName, nLine, pcLine, pc, 0, "Ordinal export without ordinal");
1368         }
1369 
1370         /* Check for import symbol without target */
1371         if ((exp.uFlags & FL_IMPSYM) && (exp.strTarget.buf == NULL))
1372         {
1373             Fatal(pszSourceFileName, nLine, pcLine, pc, 1, "Import symbol without target");
1374         }
1375 
1376         /*
1377          * Check for special handling of OLE exports, only when MSVC
1378          * is not used, since otherwise this is handled by MS LINK.EXE.
1379          */
1380         if (!gbMSComp)
1381         {
1382             /* Check whether the current export is not PRIVATE, or has an ordinal */
1383             int bIsNotPrivate = (!gbNotPrivateNoWarn && /*gbImportLib &&*/ !(exp.uFlags & FL_PRIVATE));
1384             int bHasOrdinal = (exp.uFlags & FL_ORDINAL);
1385 
1386             /* Check whether the current export is an OLE export, in case any of these tests pass */
1387             if (bIsNotPrivate || bHasOrdinal)
1388             {
1389                 for (i = 0; i < ARRAYSIZE(astrOlePrivateExports); ++i)
1390                 {
1391                     if (strlen(astrOlePrivateExports[i]) == exp.strName.len &&
1392                         strncmp(exp.strName.buf, astrOlePrivateExports[i], exp.strName.len) == 0)
1393                     {
1394                         /* The current export is an OLE export: display the corresponding warning */
1395                         if (bIsNotPrivate)
1396                         {
1397                             fprintf(stderr, "WARNING: %s line %d: Exported symbol '%.*s' should be PRIVATE\n",
1398                                     pszSourceFileName, nLine, exp.strName.len, exp.strName.buf);
1399                         }
1400                         if (bHasOrdinal)
1401                         {
1402                             fprintf(stderr, "WARNING: %s line %d: exported symbol '%.*s' should not be assigned an ordinal\n",
1403                                     pszSourceFileName, nLine, exp.strName.len, exp.strName.buf);
1404                         }
1405                         break;
1406                     }
1407                 }
1408             }
1409         }
1410 
1411         pexports[*cExports] = exp;
1412         (*cExports)++;
1413         gbDebug = 0;
1414     }
1415 
1416     return pexports;
1417 }
1418 
1419 int
1420 ApplyOrdinals(EXPORT* pexports, unsigned cExports)
1421 {
1422     unsigned short i, j;
1423     char* used;
1424 
1425     /* Allocate a table to mark used ordinals */
1426     used = malloc(65536);
1427     if (used == NULL)
1428     {
1429         fprintf(stderr, "Failed to allocate memory for ordinal use table\n");
1430         return -1;
1431     }
1432     memset(used, 0, 65536);
1433 
1434     /* Pass 1: mark the ordinals that are already used */
1435     for (i = 0; i < cExports; i++)
1436     {
1437         if (pexports[i].uFlags & FL_ORDINAL)
1438         {
1439             if (used[pexports[i].nOrdinal] != 0)
1440             {
1441                 fprintf(stderr, "Found duplicate ordinal: %u\n", pexports[i].nOrdinal);
1442                 return -1;
1443             }
1444             used[pexports[i].nOrdinal] = 1;
1445         }
1446     }
1447 
1448     /* Pass 2: apply available ordinals */
1449     for (i = 0, j = 1; i < cExports; i++)
1450     {
1451         if ((pexports[i].uFlags & FL_ORDINAL) == 0 && pexports[i].bVersionIncluded)
1452         {
1453             while (used[j] != 0)
1454                 j++;
1455 
1456             pexports[i].nOrdinal = j;
1457             used[j] = 1;
1458         }
1459     }
1460 
1461     free(used);
1462     return 0;
1463 }
1464 
1465 void usage(void)
1466 {
1467     printf("syntax: spec2def [<options> ...] <spec file>\n"
1468            "Possible options:\n"
1469            "  -h --help               print this help screen\n"
1470            "  -l=<file>               generate an asm lib stub\n"
1471            "  -d=<file>               generate a def file\n"
1472            "  -s=<file>               generate a stub file\n"
1473            "  -i=<file>               generate an import alias file\n"
1474            "  --ms                    MSVC compatibility\n"
1475            "  -n=<name>               name of the dll\n"
1476            "  --version=<version>     Sets the version to create exports for\n"
1477            "  --implib                generate a def file for an import library\n"
1478            "  --no-private-warnings   suppress warnings about symbols that should be -private\n"
1479            "  -a=<arch>               set architecture to <arch> (i386, x86_64, arm, arm64)\n"
1480            "  --with-tracing          generate wine-like \"+relay\" trace trampolines (needs -s)\n");
1481 }
1482 
1483 int main(int argc, char *argv[])
1484 {
1485     size_t nFileSize;
1486     char *pszSource, *pszDefFileName = NULL, *pszStubFileName = NULL, *pszLibStubName = NULL;
1487     char *pszImpLibAliasFileName = NULL;
1488     const char* pszVersionOption = "--version=0x";
1489     char achDllName[40];
1490     FILE *file;
1491     unsigned cExports = 0, i;
1492     EXPORT *pexports;
1493 
1494     if (argc < 2)
1495     {
1496         usage();
1497         return -1;
1498     }
1499 
1500     /* Read options */
1501     for (i = 1; i < (unsigned)argc && *argv[i] == '-'; i++)
1502     {
1503         if ((strcasecmp(argv[i], "--help") == 0) ||
1504             (strcasecmp(argv[i], "-h") == 0))
1505         {
1506             usage();
1507             return 0;
1508         }
1509         else if (argv[i][1] == 'd' && argv[i][2] == '=')
1510         {
1511             pszDefFileName = argv[i] + 3;
1512         }
1513         else if (argv[i][1] == 'l' && argv[i][2] == '=')
1514         {
1515             pszLibStubName = argv[i] + 3;
1516         }
1517         else if (argv[i][1] == 's' && argv[i][2] == '=')
1518         {
1519             pszStubFileName = argv[i] + 3;
1520         }
1521         else if (argv[i][1] == 'i' && argv[i][2] == '=')
1522         {
1523             pszImpLibAliasFileName = argv[i] + 3;
1524         }
1525         else if (argv[i][1] == 'n' && argv[i][2] == '=')
1526         {
1527             pszDllName = argv[i] + 3;
1528         }
1529         else if (strncasecmp(argv[i], pszVersionOption, strlen(pszVersionOption)) == 0)
1530         {
1531             guOsVersion = strtoul(argv[i] + strlen(pszVersionOption), NULL, 16);
1532         }
1533         else if (strcasecmp(argv[i], "--implib") == 0)
1534         {
1535             gbImportLib = 1;
1536         }
1537         else if (strcasecmp(argv[i], "--ms") == 0)
1538         {
1539             gbMSComp = 1;
1540         }
1541         else if (strcasecmp(argv[i], "--no-private-warnings") == 0)
1542         {
1543             gbNotPrivateNoWarn = 1;
1544         }
1545         else if (strcasecmp(argv[i], "--with-tracing") == 0)
1546         {
1547             if (!pszStubFileName)
1548             {
1549                 fprintf(stderr, "Error: cannot use --with-tracing without -s option.\n");
1550                 return -1;
1551             }
1552             gbTracing = 1;
1553         }
1554         else if (argv[i][1] == 'a' && argv[i][2] == '=')
1555         {
1556             pszArchString = argv[i] + 3;
1557         }
1558         else
1559         {
1560             fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
1561             return -1;
1562         }
1563     }
1564 
1565     if (strcasecmp(pszArchString, "i386") == 0)
1566     {
1567         giArch = ARCH_X86;
1568         gpszUnderscore = "_";
1569     }
1570     else if (strcasecmp(pszArchString, "x86_64") == 0) giArch = ARCH_AMD64;
1571     else if (strcasecmp(pszArchString, "ia64") == 0) giArch = ARCH_IA64;
1572     else if (strcasecmp(pszArchString, "arm") == 0) giArch = ARCH_ARM;
1573     else if (strcasecmp(pszArchString, "arm64") == 0) giArch = ARCH_ARM64;
1574     else if (strcasecmp(pszArchString, "ppc") == 0) giArch = ARCH_PPC;
1575 
1576     if ((giArch == ARCH_AMD64) || (giArch == ARCH_IA64))
1577     {
1578         pszArchString2 = "win64";
1579     }
1580     else
1581         pszArchString2 = "win32";
1582 
1583     /* Set a default dll name */
1584     if (!pszDllName)
1585     {
1586         char *p1, *p2;
1587         size_t len;
1588 
1589         p1 = strrchr(argv[i], '\\');
1590         if (!p1) p1 = strrchr(argv[i], '/');
1591         p2 = p1 = p1 ? p1 + 1 : argv[i];
1592 
1593         /* walk up to '.' */
1594         while (*p2 != '.' && *p2 != 0) p2++;
1595         len = p2 - p1;
1596         if (len >= sizeof(achDllName) - 5)
1597         {
1598             fprintf(stderr, "name too long: %s\n", p1);
1599             return -2;
1600         }
1601 
1602         strncpy(achDllName, p1, len);
1603         strncpy(achDllName + len, ".dll", sizeof(achDllName) - len);
1604         pszDllName = achDllName;
1605     }
1606 
1607     /* Open input file */
1608     pszSourceFileName = argv[i];
1609     file = fopen(pszSourceFileName, "r");
1610     if (!file)
1611     {
1612         fprintf(stderr, "error: could not open file %s\n", pszSourceFileName);
1613         return -3;
1614     }
1615 
1616     /* Get file size */
1617     fseek(file, 0, SEEK_END);
1618     nFileSize = ftell(file);
1619     rewind(file);
1620 
1621     /* Allocate memory buffer */
1622     pszSource = malloc(nFileSize + 1);
1623     if (!pszSource)
1624     {
1625         fclose(file);
1626         return -4;
1627     }
1628 
1629     /* Load input file into memory */
1630     nFileSize = fread(pszSource, 1, nFileSize, file);
1631     fclose(file);
1632 
1633     /* Zero terminate the source */
1634     pszSource[nFileSize] = '\0';
1635 
1636     pexports = ParseFile(pszSource, file, &cExports);
1637     if (pexports == NULL)
1638     {
1639         fprintf(stderr, "error: could not parse file!\n");
1640         return -1;
1641     }
1642 
1643     if (!gbMSComp)
1644     {
1645         if (ApplyOrdinals(pexports, cExports) < 0)
1646         {
1647             fprintf(stderr, "error: could not apply ordinals!\n");
1648             return -1;
1649         }
1650     }
1651 
1652     if (pszDefFileName)
1653     {
1654         /* Open output file */
1655         file = fopen(pszDefFileName, "w");
1656         if (!file)
1657         {
1658             fprintf(stderr, "error: could not open output file %s\n", pszDefFileName);
1659             return -5;
1660         }
1661 
1662         OutputHeader_def(file, pszDllName);
1663 
1664         for (i = 0; i < cExports; i++)
1665         {
1666             if (pexports[i].bVersionIncluded)
1667                  OutputLine_def(file, &pexports[i]);
1668         }
1669 
1670         fclose(file);
1671     }
1672 
1673     if (pszStubFileName)
1674     {
1675         /* Open output file */
1676         file = fopen(pszStubFileName, "w");
1677         if (!file)
1678         {
1679             fprintf(stderr, "error: could not open output file %s\n", pszStubFileName);
1680             return -5;
1681         }
1682 
1683         OutputHeader_stub(file);
1684 
1685         for (i = 0; i < cExports; i++)
1686         {
1687             if (pexports[i].bVersionIncluded)
1688                 OutputLine_stub(file, &pexports[i]);
1689         }
1690 
1691         fclose(file);
1692     }
1693 
1694     if (pszLibStubName)
1695     {
1696         /* Open output file */
1697         file = fopen(pszLibStubName, "w");
1698         if (!file)
1699         {
1700             fprintf(stderr, "error: could not open output file %s\n", pszLibStubName);
1701             return -5;
1702         }
1703 
1704         OutputHeader_asmstub(file, pszDllName);
1705 
1706         for (i = 0; i < cExports; i++)
1707         {
1708             if (pexports[i].bVersionIncluded)
1709                 OutputLine_asmstub(file, &pexports[i]);
1710         }
1711 
1712         fprintf(file, "\n    END\n");
1713         fclose(file);
1714     }
1715 
1716     if (pszImpLibAliasFileName)
1717     {
1718         /* Open output file */
1719         file = fopen(pszImpLibAliasFileName, "w");
1720         if (!file)
1721         {
1722             fprintf(stderr, "error: could not open output file %s\n", pszImpLibAliasFileName);
1723             return -5;
1724         }
1725 
1726         OutputHeader_asmstub(file, pszDllName);
1727 
1728         for (i = 0; i < cExports; i++)
1729         {
1730             if (pexports[i].bVersionIncluded)
1731                 OutputLine_implib_asm(file, &pexports[i]);
1732         }
1733 
1734         fprintf(file, "\n    END\n");
1735         fclose(file);
1736     }
1737 
1738     free(pexports);
1739 
1740     return 0;
1741 }
1742