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