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