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