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