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