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