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