[SPEC2DEF] Add support for ARM asm stub generation
[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 pexp->strName.len, pexp->strName.buf);
310 }
311 else if (pexp->nCallingConvention == CC_STDCALL)
312 {
313 sprintf(szNameBuffer, "__stub_%.*s@%d",
314 pexp->strName.len, pexp->strName.buf, pexp->nStackBytes,
315 pexp->strName.len, pexp->strName.buf, pexp->nStackBytes);
316 }
317 else if (pexp->nCallingConvention == CC_FASTCALL)
318 {
319 sprintf(szNameBuffer, "@_stub_%.*s@%d",
320 pexp->strName.len, pexp->strName.buf, pexp->nStackBytes,
321 pexp->strName.len, pexp->strName.buf, pexp->nStackBytes);
322 }
323 else if (pexp->nCallingConvention == CC_CDECL ||
324 pexp->nCallingConvention == CC_STUB)
325 {
326 sprintf(szNameBuffer, "__stub_%.*s",
327 pexp->strName.len, pexp->strName.buf,
328 pexp->strName.len, pexp->strName.buf);
329 }
330 else if (pexp->nCallingConvention == CC_EXTERN)
331 {
332 sprintf(szNameBuffer, "__stub_%.*s",
333 pexp->strName.len, pexp->strName.buf,
334 pexp->strName.len, pexp->strName.buf);
335 }
336
337 Output_symbol(fileDest, szNameBuffer);
338
339 return 1;
340 }
341
342 void
343 OutputHeader_def(FILE *file, char *libname)
344 {
345 fprintf(file,
346 "; File generated automatically, do not edit!\n\n"
347 "NAME %s\n\n"
348 "EXPORTS\n",
349 libname);
350 }
351
352 void
353 PrintName(FILE *fileDest, EXPORT *pexp, PSTRING pstr, int fDeco)
354 {
355 const char *pcName = pstr->buf;
356 int nNameLength = pstr->len;
357 const char* pcDot, *pcAt;
358
359 /* Check for non-x86 first */
360 if (giArch != ARCH_X86)
361 {
362 /* Does the string already have stdcall decoration? */
363 pcAt = ScanToken(pcName, '@');
364 if (pcAt && (pcAt < (pcName + nNameLength)) && (pcName[0] == '_'))
365 {
366 /* Skip leading underscore and remove trailing decoration */
367 pcName++;
368 nNameLength = pcAt - pcName;
369 }
370
371 /* Print the undecorated function name */
372 fprintf(fileDest, "%.*s", nNameLength, pcName);
373 }
374 else if (fDeco &&
375 ((pexp->nCallingConvention == CC_STDCALL) ||
376 (pexp->nCallingConvention == CC_FASTCALL)))
377 {
378 /* Scan for a dll forwarding dot */
379 pcDot = ScanToken(pcName, '.');
380 if (pcDot)
381 {
382 /* First print the dll name, followed by a dot */
383 nNameLength = pcDot - pcName;
384 fprintf(fileDest, "%.*s.", nNameLength, pcName);
385
386 /* Now the actual function name */
387 pcName = pcDot + 1;
388 nNameLength = pexp->strTarget.len - nNameLength - 1;
389 }
390
391 /* Does the string already have decoration? */
392 pcAt = ScanToken(pcName, '@');
393 if (pcAt && (pcAt < (pcName + nNameLength)))
394 {
395 /* On GCC, we need to remove the leading stdcall underscore */
396 if (!gbMSComp && (pexp->nCallingConvention == CC_STDCALL))
397 {
398 pcName++;
399 nNameLength--;
400 }
401
402 /* Print the already decorated function name */
403 fprintf(fileDest, "%.*s", nNameLength, pcName);
404 }
405 else
406 {
407 /* Print the prefix, but skip it for (GCC && stdcall) */
408 if (gbMSComp || (pexp->nCallingConvention != CC_STDCALL))
409 {
410 fprintf(fileDest, "%c", pexp->nCallingConvention == CC_FASTCALL ? '@' : '_');
411 }
412
413 /* Print the name with trailing decoration */
414 fprintf(fileDest, "%.*s@%d", nNameLength, pcName, pexp->nStackBytes);
415 }
416 }
417 else
418 {
419 /* Print the undecorated function name */
420 fprintf(fileDest, "%.*s", nNameLength, pcName);
421 }
422 }
423
424 void
425 OutputLine_def_MS(FILE *fileDest, EXPORT *pexp)
426 {
427 PrintName(fileDest, pexp, &pexp->strName, 0);
428
429 if (gbImportLib)
430 {
431 /* Redirect to a stub function, to get the right decoration in the lib */
432 fprintf(fileDest, "=_stub_%.*s", pexp->strName.len, pexp->strName.buf);
433 }
434 else if (pexp->strTarget.buf)
435 {
436 if (pexp->strName.buf[0] == '?')
437 {
438 fprintf(stderr, "warning: ignoring C++ redirection %.*s -> %.*s\n",
439 pexp->strName.len, pexp->strName.buf, pexp->strTarget.len, pexp->strTarget.buf);
440 }
441 else
442 {
443 fprintf(fileDest, "=");
444
445 /* If the original name was decorated, use decoration in the forwarder as well */
446 if ((giArch == ARCH_X86) && ScanToken(pexp->strName.buf, '@') &&
447 !ScanToken(pexp->strTarget.buf, '@') &&
448 ((pexp->nCallingConvention == CC_STDCALL) ||
449 (pexp->nCallingConvention == CC_FASTCALL)) )
450 {
451 PrintName(fileDest, pexp, &pexp->strTarget, 1);
452 }
453 else
454 {
455 /* Write the undecorated redirection name */
456 fprintf(fileDest, "%.*s", pexp->strTarget.len, pexp->strTarget.buf);
457 }
458 }
459 }
460 else if (((pexp->uFlags & FL_STUB) || (pexp->nCallingConvention == CC_STUB)) &&
461 (pexp->strName.buf[0] == '?'))
462 {
463 /* C++ stubs are forwarded to C stubs */
464 fprintf(fileDest, "=stub_function%d", pexp->nNumber);
465 }
466 }
467
468 void
469 OutputLine_def_GCC(FILE *fileDest, EXPORT *pexp)
470 {
471 /* Print the function name, with decoration for export libs */
472 PrintName(fileDest, pexp, &pexp->strName, gbImportLib);
473 DbgPrint("Generating def line for '%.*s'\n", pexp->strName.len, pexp->strName.buf);
474
475 /* Check if this is a forwarded export */
476 if (pexp->strTarget.buf)
477 {
478 int fIsExternal = !!ScanToken(pexp->strTarget.buf, '.');
479 DbgPrint("Got redirect '%.*s'\n", pexp->strTarget.len, pexp->strTarget.buf);
480
481 /* print the target name, don't decorate if it is external */
482 fprintf(fileDest, "=");
483 PrintName(fileDest, pexp, &pexp->strTarget, !fIsExternal);
484 }
485 else if (((pexp->uFlags & FL_STUB) || (pexp->nCallingConvention == CC_STUB)) &&
486 (pexp->strName.buf[0] == '?'))
487 {
488 /* C++ stubs are forwarded to C stubs */
489 fprintf(fileDest, "=stub_function%d", pexp->nNumber);
490 }
491
492 /* Special handling for stdcall and fastcall */
493 if ((giArch == ARCH_X86) &&
494 ((pexp->nCallingConvention == CC_STDCALL) ||
495 (pexp->nCallingConvention == CC_FASTCALL)))
496 {
497 /* Is this the import lib? */
498 if (gbImportLib)
499 {
500 /* Is the name in the spec file decorated? */
501 const char* pcDeco = ScanToken(pexp->strName.buf, '@');
502 if (pcDeco && (pcDeco < pexp->strName.buf + pexp->strName.len))
503 {
504 /* Write the name including the leading @ */
505 fprintf(fileDest, "==%.*s", pexp->strName.len, pexp->strName.buf);
506 }
507 }
508 else if (!pexp->strTarget.buf)
509 {
510 /* Write a forwarder to the actual decorated symbol */
511 fprintf(fileDest, "=");
512 PrintName(fileDest, pexp, &pexp->strName, 1);
513 }
514 }
515 }
516
517 int
518 OutputLine_def(FILE *fileDest, EXPORT *pexp)
519 {
520 DbgPrint("OutputLine_def: '%.*s'...\n", pexp->strName.len, pexp->strName.buf);
521 fprintf(fileDest, " ");
522
523 if (gbMSComp)
524 OutputLine_def_MS(fileDest, pexp);
525 else
526 OutputLine_def_GCC(fileDest, pexp);
527
528 if (pexp->uFlags & FL_ORDINAL)
529 {
530 fprintf(fileDest, " @%d", pexp->nOrdinal);
531 }
532
533 if (pexp->uFlags & FL_NONAME)
534 {
535 fprintf(fileDest, " NONAME");
536 }
537
538 if (pexp->uFlags & FL_PRIVATE)
539 {
540 fprintf(fileDest, " PRIVATE");
541 }
542
543 if (pexp->nCallingConvention == CC_EXTERN)
544 {
545 fprintf(fileDest, " DATA");
546 }
547
548 fprintf(fileDest, "\n");
549
550 return 1;
551 }
552
553 int
554 ParseFile(char* pcStart, FILE *fileDest, PFNOUTLINE OutputLine)
555 {
556 char *pc, *pcLine;
557 int nLine;
558 EXPORT exp;
559 int included;
560 char namebuffer[16];
561
562 //fprintf(stderr, "info: line %d, pcStart:'%.30s'\n", nLine, pcStart);
563
564 /* Loop all lines */
565 nLine = 1;
566 exp.nNumber = 0;
567 for (pcLine = pcStart; *pcLine; pcLine = NextLine(pcLine), nLine++)
568 {
569 pc = pcLine;
570
571 exp.nArgCount = 0;
572 exp.uFlags = 0;
573 exp.nNumber++;
574
575
576 //if (!strncmp(pcLine, "22 stdcall @(long) MPR_Alloc",28))
577 // gbDebug = 1;
578
579 //fprintf(stderr, "info: line %d, token:'%d, %.20s'\n",
580 // nLine, TokenLength(pcLine), pcLine);
581
582 /* Skip white spaces */
583 while (*pc == ' ' || *pc == '\t') pc++;
584
585 /* Skip empty lines, stop at EOF */
586 if (*pc == ';' || *pc <= '#') continue;
587 if (*pc == 0) return 0;
588
589 //fprintf(stderr, "info: line %d, token:'%.*s'\n",
590 // nLine, TokenLength(pc), pc);
591
592 /* Now we should get either an ordinal or @ */
593 if (*pc == '@')
594 exp.nOrdinal = -1;
595 else
596 {
597 exp.nOrdinal = atol(pc);
598 /* The import lib should contain the ordinal only if -ordinal was specified */
599 if (!gbImportLib)
600 exp.uFlags |= FL_ORDINAL;
601 }
602
603 /* Go to next token (type) */
604 if (!(pc = NextToken(pc)))
605 {
606 fprintf(stderr, "error: line %d, unexpected end of line\n", nLine);
607 return -10;
608 }
609
610 //fprintf(stderr, "info: Token:'%.*s'\n", TokenLength(pc), pc);
611
612 /* Now we should get the type */
613 if (CompareToken(pc, "stdcall"))
614 {
615 exp.nCallingConvention = CC_STDCALL;
616 }
617 else if (CompareToken(pc, "cdecl") ||
618 CompareToken(pc, "varargs"))
619 {
620 exp.nCallingConvention = CC_CDECL;
621 }
622 else if (CompareToken(pc, "fastcall"))
623 {
624 exp.nCallingConvention = CC_FASTCALL;
625 }
626 else if (CompareToken(pc, "thiscall"))
627 {
628 exp.nCallingConvention = CC_THISCALL;
629 }
630 else if (CompareToken(pc, "extern"))
631 {
632 exp.nCallingConvention = CC_EXTERN;
633 }
634 else if (CompareToken(pc, "stub"))
635 {
636 exp.nCallingConvention = CC_STUB;
637 }
638 else
639 {
640 fprintf(stderr, "error: line %d, expected callconv, got '%.*s' %d\n",
641 nLine, TokenLength(pc), pc, *pc);
642 return -11;
643 }
644
645 //fprintf(stderr, "info: nCallingConvention: %d\n", exp.nCallingConvention);
646
647 /* Go to next token (options or name) */
648 if (!(pc = NextToken(pc)))
649 {
650 fprintf(stderr, "fail2\n");
651 return -12;
652 }
653
654 /* Handle options */
655 included = 1;
656 while (*pc == '-')
657 {
658 if (CompareToken(pc, "-arch"))
659 {
660 /* Default to not included */
661 included = 0;
662 pc += 5;
663
664 /* Look if we are included */
665 while (*pc == '=' || *pc == ',')
666 {
667 pc++;
668 if (CompareToken(pc, pszArchString) ||
669 CompareToken(pc, pszArchString2))
670 {
671 included = 1;
672 }
673
674 /* Skip to next arch or end */
675 while (*pc > ',') pc++;
676 }
677 }
678 else if (CompareToken(pc, "-i386"))
679 {
680 if (giArch != ARCH_X86) included = 0;
681 }
682 else if (CompareToken(pc, "-private"))
683 {
684 exp.uFlags |= FL_PRIVATE;
685 }
686 else if (CompareToken(pc, "-noname"))
687 {
688 exp.uFlags |= FL_ORDINAL | FL_NONAME;
689 }
690 else if (CompareToken(pc, "-ordinal"))
691 {
692 exp.uFlags |= FL_ORDINAL;
693 /* GCC doesn't automatically import by ordinal if an ordinal
694 * is found in the def file. Force it. */
695 if (gbImportLib && !gbMSComp)
696 exp.uFlags |= FL_NONAME;
697 }
698 else if (CompareToken(pc, "-stub"))
699 {
700 exp.uFlags |= FL_STUB;
701 }
702 else if (CompareToken(pc, "-norelay") ||
703 CompareToken(pc, "-register") ||
704 CompareToken(pc, "-ret64"))
705 {
706 /* silently ignore these */
707 }
708 else
709 {
710 fprintf(stderr, "info: ignored option: '%.*s'\n",
711 TokenLength(pc), pc);
712 }
713
714 /* Go to next token */
715 pc = NextToken(pc);
716 }
717
718 //fprintf(stderr, "info: Name:'%.10s'\n", pc);
719
720 /* If arch didn't match ours, skip this entry */
721 if (!included) continue;
722
723 /* Get name */
724 exp.strName.buf = pc;
725 exp.strName.len = TokenLength(pc);
726 DbgPrint("Got name: '%.*s'\n", exp.strName.len, exp.strName.buf);
727
728 /* Check for autoname */
729 if ((exp.strName.len == 1) && (exp.strName.buf[0] == '@'))
730 {
731 sprintf(namebuffer, "ordinal%d", exp.nOrdinal);
732 exp.strName.len = strlen(namebuffer);
733 exp.strName.buf = namebuffer;
734 exp.uFlags |= FL_ORDINAL | FL_NONAME;
735 }
736
737 /* Handle parameters */
738 exp.nStackBytes = 0;
739 if (exp.nCallingConvention != CC_EXTERN &&
740 exp.nCallingConvention != CC_STUB)
741 {
742 /* Go to next token */
743 if (!(pc = NextToken(pc)))
744 {
745 fprintf(stderr, "fail4\n");
746 return -13;
747 }
748
749 /* Verify syntax */
750 if (*pc++ != '(')
751 {
752 fprintf(stderr, "error: line %d, expected '('\n", nLine);
753 return -14;
754 }
755
756 /* Skip whitespaces */
757 while (*pc == ' ' || *pc == '\t') pc++;
758
759 exp.nStackBytes = 0;
760 while (*pc >= '0')
761 {
762 if (CompareToken(pc, "long"))
763 {
764 exp.nStackBytes += 4;
765 exp.anArgs[exp.nArgCount] = ARG_LONG;
766 }
767 else if (CompareToken(pc, "double"))
768 {
769 exp.nStackBytes += 8;
770 exp.anArgs[exp.nArgCount] = ARG_DBL;
771 }
772 else if (CompareToken(pc, "ptr") ||
773 CompareToken(pc, "str") ||
774 CompareToken(pc, "wstr"))
775 {
776 exp.nStackBytes += 4; // sizeof(void*) on x86
777 exp.anArgs[exp.nArgCount] = ARG_PTR; // FIXME: handle strings
778 }
779 else if (CompareToken(pc, "int64"))
780 {
781 exp.nStackBytes += 8;
782 exp.anArgs[exp.nArgCount] = ARG_INT64;
783 }
784 else if (CompareToken(pc, "int128"))
785 {
786 exp.nStackBytes += 16;
787 exp.anArgs[exp.nArgCount] = ARG_INT128;
788 }
789 else if (CompareToken(pc, "float"))
790 {
791 exp.nStackBytes += 4;
792 exp.anArgs[exp.nArgCount] = ARG_FLOAT;
793 }
794 else
795 fprintf(stderr, "error: line %d, expected type, got: %.10s\n", nLine, pc);
796
797 exp.nArgCount++;
798
799 /* Go to next parameter */
800 if (!(pc = NextToken(pc)))
801 {
802 fprintf(stderr, "fail5\n");
803 return -15;
804 }
805 }
806
807 /* Check syntax */
808 if (*pc++ != ')')
809 {
810 fprintf(stderr, "error: line %d, expected ')'\n", nLine);
811 return -16;
812 }
813 }
814
815 /* Handle special stub cases */
816 if (exp.nCallingConvention == CC_STUB)
817 {
818 /* Check for c++ mangled name */
819 if (pc[0] == '?')
820 {
821 //printf("Found c++ mangled name...\n");
822 //
823 }
824 else
825 {
826 /* Check for stdcall name */
827 const char *p = ScanToken(pc, '@');
828 if (p && (p - pc < exp.strName.len))
829 {
830 int i;
831
832 /* Truncate the name to before the @ */
833 exp.strName.len = (int)(p - pc);
834 if (exp.strName.len < 1)
835 {
836 fprintf(stderr, "error, @ in line %d\n", nLine);
837 return -1;
838 }
839 exp.nStackBytes = atoi(p + 1);
840 exp.nArgCount = exp.nStackBytes / 4;
841 exp.nCallingConvention = CC_STDCALL;
842 exp.uFlags |= FL_STUB;
843 for (i = 0; i < exp.nArgCount; i++)
844 exp.anArgs[i] = ARG_LONG;
845 }
846 }
847 }
848
849 /* Get optional redirection */
850 pc = NextToken(pc);
851 if (pc)
852 {
853 exp.strTarget.buf = pc;
854 exp.strTarget.len = TokenLength(pc);
855
856 /* Check syntax (end of line) */
857 if (NextToken(pc))
858 {
859 fprintf(stderr, "error: line %d, additional tokens after ')'\n", nLine);
860 return -17;
861 }
862 }
863 else
864 {
865 exp.strTarget.buf = 0;
866 exp.strTarget.len = 0;
867 }
868
869 /* Check for no-name without ordinal */
870 if ((exp.uFlags & FL_ORDINAL) && (exp.nOrdinal == -1))
871 {
872 fprintf(stderr, "error: line %d, ordinal export without ordinal!\n", nLine);
873 return -1;
874 }
875
876 OutputLine(fileDest, &exp);
877 gbDebug = 0;
878 }
879
880 return 0;
881 }
882
883
884 void usage(void)
885 {
886 printf("syntax: spec2pdef [<options> ...] <spec file>\n"
887 "Possible options:\n"
888 " -h --help prints this screen\n"
889 " -l=<file> generates an asm lib stub\n"
890 " -d=<file> generates a def file\n"
891 " -s=<file> generates a stub file\n"
892 " --ms msvc compatibility\n"
893 " -n=<name> name of the dll\n"
894 " --implib generate a def file for an import library\n"
895 " -a=<arch> Set architecture to <arch>. (i386, x86_64, arm)\n");
896 }
897
898 int main(int argc, char *argv[])
899 {
900 size_t nFileSize;
901 char *pszSource, *pszDefFileName = 0, *pszStubFileName = 0, *pszLibStubName = 0;
902 char achDllName[40];
903 FILE *file;
904 int result = 0, i;
905
906 if (argc < 2)
907 {
908 usage();
909 return -1;
910 }
911
912 /* Read options */
913 for (i = 1; i < argc && *argv[i] == '-'; i++)
914 {
915 if ((strcasecmp(argv[i], "--help") == 0) ||
916 (strcasecmp(argv[i], "-h") == 0))
917 {
918 usage();
919 return 0;
920 }
921 else if (argv[i][1] == 'd' && argv[i][2] == '=')
922 {
923 pszDefFileName = argv[i] + 3;
924 }
925 else if (argv[i][1] == 'l' && argv[i][2] == '=')
926 {
927 pszLibStubName = argv[i] + 3;
928 }
929 else if (argv[i][1] == 's' && argv[i][2] == '=')
930 {
931 pszStubFileName = argv[i] + 3;
932 }
933 else if (argv[i][1] == 'n' && argv[i][2] == '=')
934 {
935 pszDllName = argv[i] + 3;
936 }
937 else if ((strcasecmp(argv[i], "--implib") == 0))
938 {
939 gbImportLib = 1;
940 }
941 else if ((strcasecmp(argv[i], "--ms") == 0))
942 {
943 gbMSComp = 1;
944 }
945 else if (argv[i][1] == 'a' && argv[i][2] == '=')
946 {
947 pszArchString = argv[i] + 3;
948 }
949 else
950 {
951 fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
952 return -1;
953 }
954 }
955
956 if (strcasecmp(pszArchString, "i386") == 0)
957 {
958 giArch = ARCH_X86;
959 gpszUnderscore = "_";
960 }
961 else if (strcasecmp(pszArchString, "x86_64") == 0) giArch = ARCH_AMD64;
962 else if (strcasecmp(pszArchString, "ia64") == 0) giArch = ARCH_IA64;
963 else if (strcasecmp(pszArchString, "arm") == 0) giArch = ARCH_ARM;
964 else if (strcasecmp(pszArchString, "ppc") == 0) giArch = ARCH_PPC;
965
966 if ((giArch == ARCH_AMD64) || (giArch == ARCH_IA64))
967 {
968 pszArchString2 = "win64";
969 }
970 else
971 pszArchString2 = "win32";
972
973 /* Set a default dll name */
974 if (!pszDllName)
975 {
976 char *p1, *p2;
977 size_t len;
978
979 p1 = strrchr(argv[i], '\\');
980 if (!p1) p1 = strrchr(argv[i], '/');
981 p2 = p1 = p1 ? p1 + 1 : argv[i];
982
983 /* walk up to '.' */
984 while (*p2 != '.' && *p2 != 0) p2++;
985 len = p2 - p1;
986 if (len >= sizeof(achDllName) - 5)
987 {
988 fprintf(stderr, "name too long: %s\n", p1);
989 return -2;
990 }
991
992 strncpy(achDllName, p1, len);
993 strncpy(achDllName + len, ".dll", sizeof(achDllName) - len);
994 pszDllName = achDllName;
995 }
996
997 /* Open input file argv[1] */
998 file = fopen(argv[i], "r");
999 if (!file)
1000 {
1001 fprintf(stderr, "error: could not open file %s ", argv[i]);
1002 return -3;
1003 }
1004
1005 /* Get file size */
1006 fseek(file, 0, SEEK_END);
1007 nFileSize = ftell(file);
1008 rewind(file);
1009
1010 /* Allocate memory buffer */
1011 pszSource = malloc(nFileSize + 1);
1012 if (!pszSource)
1013 {
1014 fclose(file);
1015 return -4;
1016 }
1017
1018 /* Load input file into memory */
1019 nFileSize = fread(pszSource, 1, nFileSize, file);
1020 fclose(file);
1021
1022 /* Zero terminate the source */
1023 pszSource[nFileSize] = '\0';
1024
1025 if (pszDefFileName)
1026 {
1027 /* Open output file */
1028 file = fopen(pszDefFileName, "w");
1029 if (!file)
1030 {
1031 fprintf(stderr, "error: could not open output file %s ", argv[i + 1]);
1032 return -5;
1033 }
1034
1035 OutputHeader_def(file, pszDllName);
1036 result = ParseFile(pszSource, file, OutputLine_def);
1037 fclose(file);
1038 }
1039
1040 if (pszStubFileName)
1041 {
1042 /* Open output file */
1043 file = fopen(pszStubFileName, "w");
1044 if (!file)
1045 {
1046 fprintf(stderr, "error: could not open output file %s ", argv[i + 1]);
1047 return -5;
1048 }
1049
1050 OutputHeader_stub(file);
1051 result = ParseFile(pszSource, file, OutputLine_stub);
1052 fclose(file);
1053 }
1054
1055 if (pszLibStubName)
1056 {
1057 /* Open output file */
1058 file = fopen(pszLibStubName, "w");
1059 if (!file)
1060 {
1061 fprintf(stderr, "error: could not open output file %s ", argv[i + 1]);
1062 return -5;
1063 }
1064
1065 OutputHeader_asmstub(file, pszDllName);
1066 result = ParseFile(pszSource, file, OutputLine_asmstub);
1067 fprintf(file, "\n END\n");
1068 fclose(file);
1069 }
1070
1071
1072 return result;
1073 }