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