xref: /reactos/sdk/tools/spec2def/spec2def.c (revision 94a413ae)
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     DbgPrint("OutputLine_def: '%.*s'...\n", pexp->strName.len, pexp->strName.buf);
734     fprintf(fileDest, " ");
735 
736     if (gbMSComp)
737         OutputLine_def_MS(fileDest, pexp);
738     else
739         OutputLine_def_GCC(fileDest, pexp);
740 
741     /* On GCC builds we force ordinals */
742     if ((pexp->uFlags & FL_ORDINAL) || (!gbMSComp && !gbImportLib))
743     {
744         fprintf(fileDest, " @%d", pexp->nOrdinal);
745     }
746 
747     if (pexp->uFlags & FL_NONAME)
748     {
749         fprintf(fileDest, " NONAME");
750     }
751 
752     /* Either PRIVATE or DATA */
753     if (pexp->uFlags & FL_PRIVATE)
754     {
755         fprintf(fileDest, " PRIVATE");
756     }
757     else if (pexp->nCallingConvention == CC_EXTERN)
758     {
759         fprintf(fileDest, " DATA");
760     }
761 
762     fprintf(fileDest, "\n");
763 
764     return 1;
765 }
766 
767 void
768 Fatalv(
769     const char* filename,
770     unsigned nLine,
771     const char *pcLine,
772     const char *pc,
773     size_t errorlen,
774     const char *format,
775     va_list argptr)
776 {
777     unsigned i, errorpos, len;
778     const char* pcLineEnd;
779 
780     /* Get the length of the line */
781     pcLineEnd = strpbrk(pcLine, "\r\n");
782     len = (unsigned)(pcLineEnd - pcLine);
783 
784     if (pc == NULL)
785     {
786         pc = pcLine + len - 1;
787         errorlen = 1;
788     }
789 
790     errorpos = (unsigned)(pc - pcLine);
791 
792     /* Output the error message */
793     fprintf(stderr, "ERROR: (%s:%u:%u): ", filename, nLine, errorpos);
794     vfprintf(stderr, format, argptr);
795     fprintf(stderr, "\n");
796 
797     /* Output the line with the error */
798     fprintf(stderr, "> %.*s\n", len, pcLine);
799 
800     if (errorlen == 0)
801     {
802         errorlen = TokenLength(pc);
803     }
804 
805     for (i = 0; i < errorpos + 2; i++)
806     {
807         fprintf(stderr, " ");
808     }
809     for (i = 0; i < errorlen; i++)
810     {
811         fprintf(stderr, "~");
812     }
813     fprintf(stderr, "\n");
814     exit(-1);
815 }
816 
817 void
818 Fatal(
819     const char* filename,
820     unsigned nLine,
821     const char *pcLine,
822     const char *pc,
823     size_t errorlen,
824     const char *format,
825     ...)
826 {
827     va_list argptr;
828 
829     va_start(argptr, format);
830     Fatalv(filename, nLine, pcLine, pc, errorlen, format, argptr);
831     va_end(argptr);
832 }
833 
834 EXPORT *
835 ParseFile(char* pcStart, FILE *fileDest, unsigned *cExports)
836 {
837     EXPORT *pexports;
838     const char *pc, *pcLine;
839     int cLines, nLine;
840     EXPORT exp;
841     int included;
842     unsigned int i;
843 
844     *cExports = 0;
845 
846     //fprintf(stderr, "info: line %d, pcStart:'%.30s'\n", nLine, pcStart);
847 
848     /* Count the lines */
849     for (cLines = 1, pcLine = pcStart; *pcLine; pcLine = NextLine(pcLine), cLines++)
850     {
851         /* Nothing */
852     }
853 
854     /* Allocate an array of EXPORT structures */
855     pexports = malloc(cLines * sizeof(EXPORT));
856     if (pexports == NULL)
857     {
858         fprintf(stderr, "ERROR: %s: failed to allocate EXPORT array of %u elements\n", pszSourceFileName, cLines);
859         return NULL;
860     }
861 
862     /* Loop all lines */
863     nLine = 1;
864     exp.nNumber = 0;
865     for (pcLine = pcStart; *pcLine; pcLine = NextLine(pcLine), nLine++)
866     {
867         pc = pcLine;
868 
869         exp.strName.buf = NULL;
870         exp.strName.len = 0;
871         exp.strTarget.buf = NULL;
872         exp.strTarget.len = 0;
873         exp.nArgCount = 0;
874         exp.uFlags = 0;
875         exp.nNumber++;
876         exp.nStartVersion = 0;
877         exp.nEndVersion = 0xFFFFFFFF;
878         exp.bVersionIncluded = 1;
879 
880         /* Skip white spaces */
881         while (*pc == ' ' || *pc == '\t') pc++;
882 
883         /* Check for line break or comment */
884         if ((*pc == '\r') || (*pc == '\n') ||
885             (*pc == ';') || (*pc == '#'))
886         {
887             continue;
888         }
889 
890         /* On EOF we are done */
891         if (*pc == 0)
892         {
893             return pexports;
894         }
895 
896         /* Now we should get either an ordinal or @ */
897         if (*pc == '@')
898         {
899             exp.nOrdinal = -1;
900         }
901         else if ((*pc >= '0') && (*pc <= '9'))
902         {
903             char* end;
904             long int number = strtol(pc, &end, 10);
905             if ((*end != ' ') && (*end != '\t'))
906             {
907                 Fatal(pszSourceFileName, nLine, pcLine, end, 0, "Unexpected character(s) after ordinal");
908             }
909 
910             if ((number < 0) || (number > 0xFFFE))
911             {
912                 Fatal(pszSourceFileName, nLine, pcLine, pc, 0, "Invalid value for ordinal");
913             }
914 
915             exp.nOrdinal = number;
916 
917             /* The import lib should contain the ordinal only if -ordinal was specified */
918             if (!gbImportLib)
919                 exp.uFlags |= FL_ORDINAL;
920         }
921         else
922         {
923             Fatal(pszSourceFileName, nLine, pcLine, pc, 0, "Expected '@' or ordinal");
924         }
925 
926         /* Go to next token (type) */
927         if (!(pc = NextToken(pc)))
928         {
929             Fatal(pszSourceFileName, nLine, pcLine, pc, 1, "Unexpected end of line");
930         }
931 
932         //fprintf(stderr, "info: Token:'%.*s'\n", TokenLength(pc), pc);
933 
934         /* Now we should get the type */
935         if (CompareToken(pc, "stdcall"))
936         {
937             exp.nCallingConvention = CC_STDCALL;
938         }
939         else if (CompareToken(pc, "cdecl") ||
940                  CompareToken(pc, "varargs"))
941         {
942             exp.nCallingConvention = CC_CDECL;
943         }
944         else if (CompareToken(pc, "fastcall"))
945         {
946             exp.nCallingConvention = CC_FASTCALL;
947         }
948         else if (CompareToken(pc, "thiscall"))
949         {
950             exp.nCallingConvention = CC_THISCALL;
951         }
952         else if (CompareToken(pc, "extern"))
953         {
954             exp.nCallingConvention = CC_EXTERN;
955         }
956         else if (CompareToken(pc, "stub"))
957         {
958             exp.nCallingConvention = CC_STUB;
959         }
960         else
961         {
962             Fatal(pszSourceFileName, nLine, pcLine, pc, 0, "Invalid calling convention");
963         }
964 
965         /* Go to next token (options or name) */
966         if (!(pc = NextToken(pc)))
967         {
968             Fatal(pszSourceFileName, nLine, pcLine, pc, 1, "Unexpected end of line");
969         }
970 
971         /* Handle options */
972         included = 1;
973         while (*pc == '-')
974         {
975             if (CompareToken(pc, "-arch="))
976             {
977                 /* Default to not included */
978                 included = 0;
979                 pc += 5;
980 
981                 /* Look if we are included */
982                 do
983                 {
984                     pc++;
985                     if (CompareToken(pc, pszArchString) ||
986                         CompareToken(pc, pszArchString2))
987                     {
988                         included = 1;
989                     }
990 
991                     /* Skip to next arch or end */
992                     while (*pc > ',') pc++;
993                 } while (*pc == ',');
994             }
995             else if (CompareToken(pc, "-i386"))
996             {
997                 if (giArch != ARCH_X86) included = 0;
998             }
999             else if (CompareToken(pc, "-version="))
1000             {
1001                 const char *pcVersionStart = pc + 9;
1002 
1003                 /* Default to not included */
1004                 exp.bVersionIncluded = 0;
1005                 pc += 8;
1006 
1007                 /* Look if we are included */
1008                 do
1009                 {
1010                     unsigned version, endversion;
1011 
1012                     /* Optionally skip leading '0x' */
1013                     pc++;
1014                     if ((pc[0] == '0') && (pc[1] == 'x')) pc += 2;
1015 
1016                     /* Now get the version number */
1017                     endversion = version = strtoul(pc, (char**)&pc, 16);
1018 
1019                     /* Check if it's a range */
1020                     if (pc[0] == '+')
1021                     {
1022                         endversion = 0xFFF;
1023                         pc++;
1024                     }
1025                     else if (pc[0] == '-')
1026                     {
1027                         /* Optionally skip leading '0x' */
1028                         pc++;
1029                         if ((pc[0] == '0') && (pc[1] == 'x')) pc += 2;
1030                         endversion = strtoul(pc, (char**)&pc, 16);
1031                     }
1032 
1033                     /* Check for degenerate range */
1034                     if (version > endversion)
1035                     {
1036                         Fatal(pszSourceFileName,
1037                               nLine,
1038                               pcLine,
1039                               pcVersionStart,
1040                               pc - pcVersionStart,
1041                               "Invalid version range");
1042                     }
1043 
1044                     exp.nStartVersion = version;
1045                     exp.nEndVersion = endversion;
1046 
1047                     /* Now compare the range with our version */
1048                     if ((guOsVersion >= version) &&
1049                         (guOsVersion <= endversion))
1050                     {
1051                         exp.bVersionIncluded = 1;
1052                     }
1053 
1054                     /* Skip to next arch or end */
1055                     while (*pc > ',') pc++;
1056 
1057                 } while (*pc == ',');
1058             }
1059             else if (CompareToken(pc, "-private"))
1060             {
1061                 exp.uFlags |= FL_PRIVATE;
1062             }
1063             else if (CompareToken(pc, "-noname"))
1064             {
1065                 exp.uFlags |= FL_ORDINAL | FL_NONAME;
1066             }
1067             else if (CompareToken(pc, "-ordinal"))
1068             {
1069                 exp.uFlags |= FL_ORDINAL;
1070                 /* GCC doesn't automatically import by ordinal if an ordinal
1071                  * is found in the def file. Force it. */
1072                 if (gbImportLib && !gbMSComp)
1073                     exp.uFlags |= FL_NONAME;
1074             }
1075             else if (CompareToken(pc, "-stub"))
1076             {
1077                 exp.uFlags |= FL_STUB;
1078             }
1079             else if (CompareToken(pc, "-norelay"))
1080             {
1081                 exp.uFlags |= FL_NORELAY;
1082             }
1083             else if (CompareToken(pc, "-ret64"))
1084             {
1085                 exp.uFlags |= FL_RET64;
1086             }
1087             else if (CompareToken(pc, "-register"))
1088             {
1089                 exp.uFlags |= FL_REGISTER;
1090             }
1091             else
1092             {
1093                 fprintf(stdout,
1094                         "INFO: %s line %d: Ignored option: '%.*s'\n",
1095                         pszSourceFileName,
1096                         nLine,
1097                         TokenLength(pc),
1098                         pc);
1099             }
1100 
1101             /* Go to next token */
1102             pc = NextToken(pc);
1103         }
1104 
1105         //fprintf(stderr, "info: Name:'%.10s'\n", pc);
1106 
1107         /* If arch didn't match ours, skip this entry */
1108         if (!included) continue;
1109 
1110         /* Get name */
1111         exp.strName.buf = pc;
1112         exp.strName.len = TokenLength(pc);
1113         //DbgPrint("Got name: '%.*s'\n", exp.strName.len, exp.strName.buf);
1114 
1115         /* Check for autoname */
1116         if ((exp.strName.len == 1) && (exp.strName.buf[0] == '@'))
1117         {
1118             exp.uFlags |= FL_ORDINAL | FL_NONAME;
1119         }
1120 
1121         /* Handle parameters */
1122         exp.nStackBytes = 0;
1123         if (exp.nCallingConvention != CC_EXTERN &&
1124             exp.nCallingConvention != CC_STUB)
1125         {
1126             /* Go to next token */
1127             if (!(pc = NextToken(pc)))
1128             {
1129                 Fatal(pszSourceFileName, nLine, pcLine, pc, 1, "Unexpected end of line");
1130             }
1131 
1132             /* Verify syntax */
1133             if (*pc++ != '(')
1134             {
1135                 Fatal(pszSourceFileName, nLine, pcLine, pc - 1, 0, "Expected '('");
1136             }
1137 
1138             /* Skip whitespaces */
1139             while (*pc == ' ' || *pc == '\t') pc++;
1140 
1141             exp.nStackBytes = 0;
1142             while (*pc >= '0')
1143             {
1144                 if (CompareToken(pc, "long"))
1145                 {
1146                     exp.nStackBytes += 4;
1147                     exp.anArgs[exp.nArgCount] = ARG_LONG;
1148                 }
1149                 else if (CompareToken(pc, "double"))
1150                 {
1151                     exp.nStackBytes += 8;
1152                     exp.anArgs[exp.nArgCount] = ARG_DBL;
1153                 }
1154                 else if (CompareToken(pc, "ptr"))
1155                 {
1156                     exp.nStackBytes += 4; // sizeof(void*) on x86
1157                     exp.anArgs[exp.nArgCount] = ARG_PTR;
1158                 }
1159                 else if (CompareToken(pc, "str"))
1160                 {
1161                     exp.nStackBytes += 4; // sizeof(void*) on x86
1162                     exp.anArgs[exp.nArgCount] = ARG_STR;
1163                 }
1164                 else if (CompareToken(pc, "wstr"))
1165                 {
1166                     exp.nStackBytes += 4; // sizeof(void*) on x86
1167                     exp.anArgs[exp.nArgCount] = ARG_WSTR;
1168                 }
1169                 else if (CompareToken(pc, "int64"))
1170                 {
1171                     exp.nStackBytes += 8;
1172                     exp.anArgs[exp.nArgCount] = ARG_INT64;
1173                 }
1174                 else if (CompareToken(pc, "int128"))
1175                 {
1176                     exp.nStackBytes += 16;
1177                     exp.anArgs[exp.nArgCount] = ARG_INT128;
1178                 }
1179                 else if (CompareToken(pc, "float"))
1180                 {
1181                     exp.nStackBytes += 4;
1182                     exp.anArgs[exp.nArgCount] = ARG_FLOAT;
1183                 }
1184                 else
1185                 {
1186                     Fatal(pszSourceFileName, nLine, pcLine, pc, 0, "Unrecognized type");
1187                 }
1188 
1189                 exp.nArgCount++;
1190 
1191                 /* Go to next parameter */
1192                 if (!(pc = NextToken(pc)))
1193                 {
1194                     Fatal(pszSourceFileName, nLine, pcLine, pc, 1, "Unexpected end of line");
1195                 }
1196             }
1197 
1198             /* Check syntax */
1199             if (*pc++ != ')')
1200             {
1201                 Fatal(pszSourceFileName, nLine, pcLine, pc - 1, 0, "Expected ')'");
1202             }
1203         }
1204 
1205         /* Handle special stub cases */
1206         if (exp.nCallingConvention == CC_STUB)
1207         {
1208             /* Check for c++ mangled name */
1209             if (pc[0] == '?')
1210             {
1211                 //printf("Found c++ mangled name...\n");
1212                 //
1213             }
1214             else
1215             {
1216                 /* Check for stdcall name */
1217                 const char *p = ScanToken(pc, '@');
1218                 if (p && (p - pc < exp.strName.len))
1219                 {
1220                     int i;
1221 
1222                     /* Truncate the name to before the @ */
1223                     exp.strName.len = (int)(p - pc);
1224                     if (exp.strName.len < 1)
1225                     {
1226                         Fatal(pszSourceFileName, nLine, pcLine, p, 1, "Unexpected @");
1227                     }
1228                     exp.nStackBytes = atoi(p + 1);
1229                     exp.nArgCount =  exp.nStackBytes / 4;
1230                     exp.nCallingConvention = CC_STDCALL;
1231                     exp.uFlags |= FL_STUB;
1232                     for (i = 0; i < exp.nArgCount; i++)
1233                         exp.anArgs[i] = ARG_LONG;
1234                 }
1235             }
1236         }
1237 
1238         /* Get optional redirection */
1239         pc = NextToken(pc);
1240         if (pc)
1241         {
1242             exp.strTarget.buf = pc;
1243             exp.strTarget.len = TokenLength(pc);
1244 
1245             /* Check syntax (end of line) */
1246             if (NextToken(pc))
1247             {
1248                 Fatal(pszSourceFileName, nLine, pcLine, NextToken(pc), 0, "Excess token(s) at end of definition");
1249             }
1250 
1251             /* Don't relay-trace forwarded functions */
1252             exp.uFlags |= FL_NORELAY;
1253         }
1254         else
1255         {
1256             exp.strTarget.buf = NULL;
1257             exp.strTarget.len = 0;
1258         }
1259 
1260         /* Check for no-name without ordinal */
1261         if ((exp.uFlags & FL_ORDINAL) && (exp.nOrdinal == -1))
1262         {
1263             Fatal(pszSourceFileName, nLine, pcLine, pc, 0, "Ordinal export without ordinal");
1264         }
1265 
1266         /*
1267          * Check for special handling of OLE exports, only when MSVC
1268          * is not used, since otherwise this is handled by MS LINK.EXE.
1269          */
1270         if (!gbMSComp)
1271         {
1272             /* Check whether the current export is not PRIVATE, or has an ordinal */
1273             int bIsNotPrivate = (!gbNotPrivateNoWarn && /*gbImportLib &&*/ !(exp.uFlags & FL_PRIVATE));
1274             int bHasOrdinal = (exp.uFlags & FL_ORDINAL);
1275 
1276             /* Check whether the current export is an OLE export, in case any of these tests pass */
1277             if (bIsNotPrivate || bHasOrdinal)
1278             {
1279                 for (i = 0; i < ARRAYSIZE(astrOlePrivateExports); ++i)
1280                 {
1281                     if (strlen(astrOlePrivateExports[i]) == exp.strName.len &&
1282                         strncmp(exp.strName.buf, astrOlePrivateExports[i], exp.strName.len) == 0)
1283                     {
1284                         /* The current export is an OLE export: display the corresponding warning */
1285                         if (bIsNotPrivate)
1286                         {
1287                             fprintf(stderr, "WARNING: %s line %d: Exported symbol '%.*s' should be PRIVATE\n",
1288                                     pszSourceFileName, nLine, exp.strName.len, exp.strName.buf);
1289                         }
1290                         if (bHasOrdinal)
1291                         {
1292                             fprintf(stderr, "WARNING: %s line %d: exported symbol '%.*s' should not be assigned an ordinal\n",
1293                                     pszSourceFileName, nLine, exp.strName.len, exp.strName.buf);
1294                         }
1295                         break;
1296                     }
1297                 }
1298             }
1299         }
1300 
1301         pexports[*cExports] = exp;
1302         (*cExports)++;
1303         gbDebug = 0;
1304     }
1305 
1306     return pexports;
1307 }
1308 
1309 int
1310 ApplyOrdinals(EXPORT* pexports, unsigned cExports)
1311 {
1312     unsigned short i, j;
1313     char* used;
1314 
1315     /* Allocate a table to mark used ordinals */
1316     used = malloc(65536);
1317     if (used == NULL)
1318     {
1319         fprintf(stderr, "Failed to allocate memory for ordinal use table\n");
1320         return -1;
1321     }
1322     memset(used, 0, 65536);
1323 
1324     /* Pass 1: mark the ordinals that are already used */
1325     for (i = 0; i < cExports; i++)
1326     {
1327         if (pexports[i].uFlags & FL_ORDINAL)
1328         {
1329             if (used[pexports[i].nOrdinal] != 0)
1330             {
1331                 fprintf(stderr, "Found duplicate ordinal: %u\n", pexports[i].nOrdinal);
1332                 return -1;
1333             }
1334             used[pexports[i].nOrdinal] = 1;
1335         }
1336     }
1337 
1338     /* Pass 2: apply available ordinals */
1339     for (i = 0, j = 1; i < cExports; i++)
1340     {
1341         if ((pexports[i].uFlags & FL_ORDINAL) == 0 && pexports[i].bVersionIncluded)
1342         {
1343             while (used[j] != 0)
1344                 j++;
1345 
1346             pexports[i].nOrdinal = j;
1347             used[j] = 1;
1348         }
1349     }
1350 
1351     free(used);
1352     return 0;
1353 }
1354 
1355 void usage(void)
1356 {
1357     printf("syntax: spec2def [<options> ...] <spec file>\n"
1358            "Possible options:\n"
1359            "  -h --help               print this help screen\n"
1360            "  -l=<file>               generate an asm lib stub\n"
1361            "  -d=<file>               generate a def file\n"
1362            "  -s=<file>               generate a stub file\n"
1363            "  --ms                    MSVC compatibility\n"
1364            "  -n=<name>               name of the dll\n"
1365            "  --implib                generate a def file for an import library\n"
1366            "  --no-private-warnings   suppress warnings about symbols that should be -private\n"
1367            "  -a=<arch>               set architecture to <arch> (i386, x86_64, arm)\n"
1368            "  --with-tracing          generate wine-like \"+relay\" trace trampolines (needs -s)\n");
1369 }
1370 
1371 int main(int argc, char *argv[])
1372 {
1373     size_t nFileSize;
1374     char *pszSource, *pszDefFileName = NULL, *pszStubFileName = NULL, *pszLibStubName = NULL;
1375     const char* pszVersionOption = "--version=0x";
1376     char achDllName[40];
1377     FILE *file;
1378     unsigned cExports = 0, i;
1379     EXPORT *pexports;
1380 
1381     if (argc < 2)
1382     {
1383         usage();
1384         return -1;
1385     }
1386 
1387     /* Read options */
1388     for (i = 1; i < (unsigned)argc && *argv[i] == '-'; i++)
1389     {
1390         if ((strcasecmp(argv[i], "--help") == 0) ||
1391             (strcasecmp(argv[i], "-h") == 0))
1392         {
1393             usage();
1394             return 0;
1395         }
1396         else if (argv[i][1] == 'd' && argv[i][2] == '=')
1397         {
1398             pszDefFileName = argv[i] + 3;
1399         }
1400         else if (argv[i][1] == 'l' && argv[i][2] == '=')
1401         {
1402             pszLibStubName = argv[i] + 3;
1403         }
1404         else if (argv[i][1] == 's' && argv[i][2] == '=')
1405         {
1406             pszStubFileName = argv[i] + 3;
1407         }
1408         else if (argv[i][1] == 'n' && argv[i][2] == '=')
1409         {
1410             pszDllName = argv[i] + 3;
1411         }
1412         else if (strncasecmp(argv[i], pszVersionOption, strlen(pszVersionOption)) == 0)
1413         {
1414             guOsVersion = strtoul(argv[i] + strlen(pszVersionOption), NULL, 16);
1415         }
1416         else if (strcasecmp(argv[i], "--implib") == 0)
1417         {
1418             gbImportLib = 1;
1419         }
1420         else if (strcasecmp(argv[i], "--ms") == 0)
1421         {
1422             gbMSComp = 1;
1423         }
1424         else if (strcasecmp(argv[i], "--no-private-warnings") == 0)
1425         {
1426             gbNotPrivateNoWarn = 1;
1427         }
1428         else if (strcasecmp(argv[i], "--with-tracing") == 0)
1429         {
1430             if (!pszStubFileName)
1431             {
1432                 fprintf(stderr, "Error: cannot use --with-tracing without -s option.\n");
1433                 return -1;
1434             }
1435             gbTracing = 1;
1436         }
1437         else if (argv[i][1] == 'a' && argv[i][2] == '=')
1438         {
1439             pszArchString = argv[i] + 3;
1440         }
1441         else
1442         {
1443             fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
1444             return -1;
1445         }
1446     }
1447 
1448     if (strcasecmp(pszArchString, "i386") == 0)
1449     {
1450         giArch = ARCH_X86;
1451         gpszUnderscore = "_";
1452     }
1453     else if (strcasecmp(pszArchString, "x86_64") == 0) giArch = ARCH_AMD64;
1454     else if (strcasecmp(pszArchString, "ia64") == 0) giArch = ARCH_IA64;
1455     else if (strcasecmp(pszArchString, "arm") == 0) giArch = ARCH_ARM;
1456     else if (strcasecmp(pszArchString, "ppc") == 0) giArch = ARCH_PPC;
1457 
1458     if ((giArch == ARCH_AMD64) || (giArch == ARCH_IA64))
1459     {
1460         pszArchString2 = "win64";
1461     }
1462     else
1463         pszArchString2 = "win32";
1464 
1465     /* Set a default dll name */
1466     if (!pszDllName)
1467     {
1468         char *p1, *p2;
1469         size_t len;
1470 
1471         p1 = strrchr(argv[i], '\\');
1472         if (!p1) p1 = strrchr(argv[i], '/');
1473         p2 = p1 = p1 ? p1 + 1 : argv[i];
1474 
1475         /* walk up to '.' */
1476         while (*p2 != '.' && *p2 != 0) p2++;
1477         len = p2 - p1;
1478         if (len >= sizeof(achDllName) - 5)
1479         {
1480             fprintf(stderr, "name too long: %s\n", p1);
1481             return -2;
1482         }
1483 
1484         strncpy(achDllName, p1, len);
1485         strncpy(achDllName + len, ".dll", sizeof(achDllName) - len);
1486         pszDllName = achDllName;
1487     }
1488 
1489     /* Open input file */
1490     pszSourceFileName = argv[i];
1491     file = fopen(pszSourceFileName, "r");
1492     if (!file)
1493     {
1494         fprintf(stderr, "error: could not open file %s\n", pszSourceFileName);
1495         return -3;
1496     }
1497 
1498     /* Get file size */
1499     fseek(file, 0, SEEK_END);
1500     nFileSize = ftell(file);
1501     rewind(file);
1502 
1503     /* Allocate memory buffer */
1504     pszSource = malloc(nFileSize + 1);
1505     if (!pszSource)
1506     {
1507         fclose(file);
1508         return -4;
1509     }
1510 
1511     /* Load input file into memory */
1512     nFileSize = fread(pszSource, 1, nFileSize, file);
1513     fclose(file);
1514 
1515     /* Zero terminate the source */
1516     pszSource[nFileSize] = '\0';
1517 
1518     pexports = ParseFile(pszSource, file, &cExports);
1519     if (pexports == NULL)
1520     {
1521         fprintf(stderr, "error: could not parse file!\n");
1522         return -1;
1523     }
1524 
1525     if (!gbMSComp)
1526     {
1527         if (ApplyOrdinals(pexports, cExports) < 0)
1528         {
1529             fprintf(stderr, "error: could not apply ordinals!\n");
1530             return -1;
1531         }
1532     }
1533 
1534     if (pszDefFileName)
1535     {
1536         /* Open output file */
1537         file = fopen(pszDefFileName, "w");
1538         if (!file)
1539         {
1540             fprintf(stderr, "error: could not open output file %s\n", argv[i + 1]);
1541             return -5;
1542         }
1543 
1544         OutputHeader_def(file, pszDllName);
1545 
1546         for (i = 0; i < cExports; i++)
1547         {
1548             if (pexports[i].bVersionIncluded)
1549                  OutputLine_def(file, &pexports[i]);
1550         }
1551 
1552         fclose(file);
1553     }
1554 
1555     if (pszStubFileName)
1556     {
1557         /* Open output file */
1558         file = fopen(pszStubFileName, "w");
1559         if (!file)
1560         {
1561             fprintf(stderr, "error: could not open output file %s\n", argv[i + 1]);
1562             return -5;
1563         }
1564 
1565         OutputHeader_stub(file);
1566 
1567         for (i = 0; i < cExports; i++)
1568         {
1569             if (pexports[i].bVersionIncluded)
1570                 OutputLine_stub(file, &pexports[i]);
1571         }
1572 
1573         fclose(file);
1574     }
1575 
1576     if (pszLibStubName)
1577     {
1578         /* Open output file */
1579         file = fopen(pszLibStubName, "w");
1580         if (!file)
1581         {
1582             fprintf(stderr, "error: could not open output file %s\n", argv[i + 1]);
1583             return -5;
1584         }
1585 
1586         OutputHeader_asmstub(file, pszDllName);
1587 
1588         for (i = 0; i < cExports; i++)
1589         {
1590             if (pexports[i].bVersionIncluded)
1591                 OutputLine_asmstub(file, &pexports[i]);
1592         }
1593 
1594         fprintf(file, "\n    END\n");
1595         fclose(file);
1596     }
1597 
1598     free(pexports);
1599 
1600     return 0;
1601 }
1602