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