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