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