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