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