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