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