7 #define strcasecmp _stricmp
15 int nRedirectionLength
;
16 int nCallingConvention
;
33 typedef int (*PFNOUTLINE
)(FILE *, EXPORT
*);
37 int no_redirections
= 0;
38 int giArch
= ARCH_X86
;
39 char *pszArchString
= "i386";
42 char *gpszUnderscore
= "";
70 char* astrCallingConventions
[] =
82 return ((chr
<= ',' && chr
!= '$') ||
83 (chr
>= ':' && chr
< '?') );
87 CompareToken(const char *token
, const char *comparand
)
91 if (*token
!= *comparand
) return 0;
95 if (!IsSeparator(*token
)) return 0;
100 ScanToken(const char *token
, char chr
)
102 while (!IsSeparator(*token
))
104 if (*token
++ == chr
) return 1;
114 if (pc
[0] == '\n' && pc
[1] == '\r') return pc
+ 2;
115 else if (pc
[0] == '\n') return pc
+ 1;
122 TokenLength(char *pc
)
126 while (!IsSeparator(*pc
++)) length
++;
135 while (!IsSeparator(*pc
)) pc
++;
137 /* Skip white spaces */
138 while (*pc
== ' ' || *pc
== '\t') pc
++;
140 /* Check for end of line */
141 if (*pc
== '\n' || *pc
== '\r' || *pc
== 0) return 0;
143 /* Check for comment */
144 if (*pc
== '#' || *pc
== ';') return 0;
150 OutputHeader_stub(FILE *file
)
152 fprintf(file
, "/* This file is autogenerated, do not edit. */\n\n"
153 "#include <stubs.h>\n\n");
157 OutputLine_stub(FILE *file
, EXPORT
*pexp
)
161 if (pexp
->nCallingConvention
!= CC_STUB
&&
162 (pexp
->uFlags
& FL_STUB
) == 0) return 0;
164 fprintf(file
, "int ");
165 if ((giArch
== ARCH_X86
) &&
166 pexp
->nCallingConvention
== CC_STDCALL
)
168 fprintf(file
, "__stdcall ");
171 fprintf(file
, "%.*s(", pexp
->nNameLength
, pexp
->pcName
);
173 for (i
= 0; i
< pexp
->nArgCount
; i
++)
175 if (i
!= 0) fprintf(file
, ", ");
176 switch (pexp
->anArgs
[i
])
178 case ARG_LONG
: fprintf(file
, "long"); break;
179 case ARG_PTR
: fprintf(file
, "void*"); break;
180 case ARG_STR
: fprintf(file
, "char*"); break;
181 case ARG_WSTR
: fprintf(file
, "wchar_t*"); break;
182 case ARG_DBL
: case ARG_INT64
: fprintf(file
, "__int64"); break;
184 fprintf(file
, " a%d", i
);
186 fprintf(file
, ")\n{\n\tDPRINT1(\"WARNING: calling stub %.*s(",
187 pexp
->nNameLength
, pexp
->pcName
);
189 for (i
= 0; i
< pexp
->nArgCount
; i
++)
191 if (i
!= 0) fprintf(file
, ",");
192 switch (pexp
->anArgs
[i
])
194 case ARG_LONG
: fprintf(file
, "0x%%lx"); break;
195 case ARG_PTR
: fprintf(file
, "0x%%p"); break;
196 case ARG_STR
: fprintf(file
, "'%%s'"); break;
197 case ARG_WSTR
: fprintf(file
, "'%%ws'"); break;
198 case ARG_DBL
: fprintf(file
, "%%f"); break;
199 case ARG_INT64
: fprintf(file
, "%%\"PRix64\""); break;
202 fprintf(file
, ")\\n\"");
204 for (i
= 0; i
< pexp
->nArgCount
; i
++)
207 switch (pexp
->anArgs
[i
])
209 case ARG_LONG
: fprintf(file
, "(long)a%d", i
); break;
210 case ARG_PTR
: fprintf(file
, "(void*)a%d", i
); break;
211 case ARG_STR
: fprintf(file
, "(char*)a%d", i
); break;
212 case ARG_WSTR
: fprintf(file
, "(wchar_t*)a%d", i
); break;
213 case ARG_DBL
: fprintf(file
, "(double)a%d", i
); break;
214 case ARG_INT64
: fprintf(file
, "(__int64)a%d", i
); break;
217 fprintf(file
, ");\n");
219 if (pexp
->nCallingConvention
== CC_STUB
)
221 fprintf(file
, "\t__wine_spec_unimplemented_stub(\"%s\", __FUNCTION__);\n", pszDllName
);
224 fprintf(file
, "\treturn 0;\n}\n\n");
230 OutputHeader_asmstub(FILE *file
, char *libname
)
232 fprintf(file
, "; File generated automatically, do not edit! \n\n");
234 if (giArch
== ARCH_X86
)
235 fprintf(file
, ".586\n.model flat\n");
237 fprintf(file
, ".code\n");
241 OutputLine_asmstub(FILE *fileDest
, EXPORT
*pexp
)
243 /* Handle autoname */
244 if (pexp
->nNameLength
== 1 && pexp
->pcName
[0] == '@')
246 fprintf(fileDest
, "PUBLIC %sordinal%d\n%sordinal%d: nop\n",
247 gpszUnderscore
, pexp
->nOrdinal
, gpszUnderscore
, pexp
->nOrdinal
);
249 else if (giArch
!= ARCH_X86
)
251 fprintf(fileDest
, "PUBLIC _stub_%.*s\n_stub_%.*s: nop\n",
252 pexp
->nNameLength
, pexp
->pcName
,
253 pexp
->nNameLength
, pexp
->pcName
);
255 else if (pexp
->nCallingConvention
== CC_STDCALL
)
257 fprintf(fileDest
, "PUBLIC __stub_%.*s@%d\n__stub_%.*s@%d: nop\n",
258 pexp
->nNameLength
, pexp
->pcName
, pexp
->nStackBytes
,
259 pexp
->nNameLength
, pexp
->pcName
, pexp
->nStackBytes
);
261 else if (pexp
->nCallingConvention
== CC_FASTCALL
)
263 fprintf(fileDest
, "PUBLIC @_stub_%.*s@%d\n@_stub_%.*s@%d: nop\n",
264 pexp
->nNameLength
, pexp
->pcName
, pexp
->nStackBytes
,
265 pexp
->nNameLength
, pexp
->pcName
, pexp
->nStackBytes
);
267 else if (pexp
->nCallingConvention
== CC_CDECL
||
268 pexp
->nCallingConvention
== CC_STUB
)
270 fprintf(fileDest
, "PUBLIC __stub_%.*s\n__stub_%.*s: nop\n",
271 pexp
->nNameLength
, pexp
->pcName
,
272 pexp
->nNameLength
, pexp
->pcName
);
274 else if (pexp
->nCallingConvention
== CC_EXTERN
)
276 fprintf(fileDest
, "PUBLIC __stub_%.*s\n__stub_%.*s:\n",
277 pexp
->nNameLength
, pexp
->pcName
,
278 pexp
->nNameLength
, pexp
->pcName
);
285 OutputHeader_def(FILE *file
, char *libname
)
288 "; File generated automatically, do not edit!\n\n"
295 PrintName(FILE *fileDest
, EXPORT
*pexp
, char *pszPrefix
, int fRedir
, int fDeco
)
297 char *pcName
= fRedir
? pexp
->pcRedirection
: pexp
->pcName
;
298 size_t nNameLength
= fRedir
? pexp
->nRedirectionLength
: pexp
->nNameLength
;
300 /* Handle autoname */
301 if (nNameLength
== 1 && pcName
[0] == '@')
303 fprintf(fileDest
, "ordinal%d", pexp
->nOrdinal
);
307 if (fDeco
&& pexp
->nCallingConvention
== CC_FASTCALL
)
308 fprintf(fileDest
, "@");
309 fprintf(fileDest
, "%s%.*s", pszPrefix
, nNameLength
, pcName
);
310 if ((pexp
->nCallingConvention
== CC_STDCALL
||
311 pexp
->nCallingConvention
== CC_FASTCALL
) && fDeco
)
313 fprintf(fileDest
, "@%d", pexp
->nStackBytes
);
319 OutputLine_def(FILE *fileDest
, EXPORT
*pexp
)
321 fprintf(fileDest
, " ");
323 PrintName(fileDest
, pexp
, "", 0, (giArch
== ARCH_X86
) && !gbKillAt
);
327 fprintf(fileDest
, "=");
328 PrintName(fileDest
, pexp
, "_stub_", 0, 0);
330 else if (pexp
->pcRedirection
)
332 int fDeco
= ((giArch
== ARCH_X86
) && !ScanToken(pexp
->pcRedirection
, '.'));
334 fprintf(fileDest
, "=");
335 PrintName(fileDest
, pexp
, "", 1, fDeco
&& !gbMSComp
);
337 else if ((giArch
== ARCH_X86
) && gbKillAt
&& !gbMSComp
&&
338 (pexp
->nCallingConvention
== CC_STDCALL
||
339 pexp
->nCallingConvention
== CC_FASTCALL
))
341 fprintf(fileDest
, "=");
342 PrintName(fileDest
, pexp
, "", 0, 1);
345 if (pexp
->nOrdinal
!= -1)
347 fprintf(fileDest
, " @%d", pexp
->nOrdinal
);
350 if (pexp
->nCallingConvention
== CC_EXTERN
)
352 fprintf(fileDest
, " DATA");
355 if (pexp
->uFlags
& FL_PRIVATE
)
357 fprintf(fileDest
, " PRIVATE");
360 if (pexp
->uFlags
& FL_NONAME
)
362 fprintf(fileDest
, " NONAME");
365 fprintf(fileDest
, "\n");
371 ParseFile(char* pcStart
, FILE *fileDest
, PFNOUTLINE OutputLine
)
378 //fprintf(stderr, "info: line %d, pcStart:'%.30s'\n", nLine, pcStart);
382 for (pcLine
= pcStart
; *pcLine
; pcLine
= NextLine(pcLine
), nLine
++)
389 //fprintf(stderr, "info: line %d, token:'%d, %.20s'\n",
390 // nLine, TokenLength(pcLine), pcLine);
392 /* Skip white spaces */
393 while (*pc
== ' ' || *pc
== '\t') pc
++;
395 /* Skip empty lines, stop at EOF */
396 if (*pc
== ';' || *pc
<= '#') continue;
397 if (*pc
== 0) return 0;
399 //fprintf(stderr, "info: line %d, token:'%.*s'\n",
400 // nLine, TokenLength(pc), pc);
402 /* Now we should get either an ordinal or @ */
403 if (*pc
== '@') exp
.nOrdinal
= -1;
404 else exp
.nOrdinal
= atol(pc
);
406 /* Go to next token (type) */
407 if (!(pc
= NextToken(pc
)))
409 fprintf(stderr
, "error: line %d, unexpected end of line\n", nLine
);
413 //fprintf(stderr, "info: Token:'%.10s'\n", pc);
415 /* Now we should get the type */
416 if (CompareToken(pc
, "stdcall"))
418 exp
.nCallingConvention
= CC_STDCALL
;
420 else if (CompareToken(pc
, "cdecl") ||
421 CompareToken(pc
, "varargs"))
423 exp
.nCallingConvention
= CC_CDECL
;
425 else if (CompareToken(pc
, "fastcall"))
427 exp
.nCallingConvention
= CC_FASTCALL
;
429 else if (CompareToken(pc
, "extern"))
431 exp
.nCallingConvention
= CC_EXTERN
;
433 else if (CompareToken(pc
, "stub"))
435 exp
.nCallingConvention
= CC_STUB
;
439 fprintf(stderr
, "error: line %d, expected type, got '%.*s' %d\n",
440 nLine
, TokenLength(pc
), pc
, *pc
);
444 //fprintf(stderr, "info: nCallingConvention: %d\n", exp.nCallingConvention);
446 /* Go to next token (options or name) */
447 if (!(pc
= NextToken(pc
)))
449 fprintf(stderr
, "fail2\n");
457 if (CompareToken(pc
, "-arch"))
459 /* Default to not included */
463 /* Look if we are included */
464 while (*pc
== '=' || *pc
== ',')
467 if (CompareToken(pc
, pszArchString
) ||
468 CompareToken(pc
, pszArchString2
))
473 /* Skip to next arch or end */
474 while (*pc
> ',') pc
++;
477 else if (CompareToken(pc
, "-i386"))
479 if (giArch
!= ARCH_X86
) included
= 0;
481 else if (CompareToken(pc
, "-private"))
483 exp
.uFlags
|= FL_PRIVATE
;
485 else if (CompareToken(pc
, "-noname") ||
486 CompareToken(pc
, "-ordinal"))
488 exp
.uFlags
|= FL_NONAME
;
490 else if (CompareToken(pc
, "-stub"))
492 exp
.uFlags
|= FL_STUB
;
494 else if (CompareToken(pc
, "-norelay") ||
495 CompareToken(pc
, "-register") ||
496 CompareToken(pc
, "-ret64"))
498 /* silently ignore these */
502 fprintf(stderr
, "info: ignored option: '%.*s'\n",
503 TokenLength(pc
), pc
);
506 /* Go to next token */
510 //fprintf(stderr, "info: Name:'%.10s'\n", pc);
512 /* If arch didn't match ours, skip this entry */
513 if (!included
) continue;
517 exp
.nNameLength
= TokenLength(pc
);
519 /* Handle parameters */
521 if (exp
.nCallingConvention
!= CC_EXTERN
&&
522 exp
.nCallingConvention
!= CC_STUB
)
524 //fprintf(stderr, "info: options:'%.10s'\n", pc);
525 /* Go to next token */
526 if (!(pc
= NextToken(pc
)))
528 fprintf(stderr
, "fail4\n");
535 fprintf(stderr
, "error: line %d, expected '('\n", nLine
);
539 /* Skip whitespaces */
540 while (*pc
== ' ' || *pc
== '\t') pc
++;
545 if (CompareToken(pc
, "long"))
547 exp
.nStackBytes
+= 4;
548 exp
.anArgs
[exp
.nArgCount
] = ARG_LONG
;
550 else if (CompareToken(pc
, "double"))
552 exp
.nStackBytes
+= 8;
553 exp
.anArgs
[exp
.nArgCount
] = ARG_DBL
;
555 else if (CompareToken(pc
, "ptr") ||
556 CompareToken(pc
, "str") ||
557 CompareToken(pc
, "wstr"))
559 exp
.nStackBytes
+= 4; // sizeof(void*) on x86
560 exp
.anArgs
[exp
.nArgCount
] = ARG_PTR
; // FIXME: handle strings
562 else if (CompareToken(pc
, "int64"))
564 exp
.nStackBytes
+= 8;
565 exp
.anArgs
[exp
.nArgCount
] = ARG_INT64
;
568 fprintf(stderr
, "error: line %d, expected type, got: %.10s\n", nLine
, pc
);
572 /* Go to next parameter */
573 if (!(pc
= NextToken(pc
)))
575 fprintf(stderr
, "fail5\n");
583 fprintf(stderr
, "error: line %d, expected ')'\n", nLine
);
588 /* Handle special stub cases */
589 if (exp
.nCallingConvention
== CC_STUB
)
591 /* Check for c++ mangled name */
594 printf("Found c++ mangled name...\n");
599 /* Check for stdcall name */
600 char *p
= strchr(pc
, '@');
601 if (p
&& ((size_t)(p
- pc
) < exp
.nNameLength
))
604 exp
.nNameLength
= p
- pc
;
605 if (exp
.nNameLength
< 1)
607 fprintf(stderr
, "error, @ in line %d\n", nLine
);
610 exp
.nStackBytes
= atoi(p
+ 1);
611 exp
.nArgCount
= exp
.nStackBytes
/ 4;
612 exp
.nCallingConvention
= CC_STDCALL
;
613 exp
.uFlags
|= FL_STUB
;
614 for (i
= 0; i
< exp
.nArgCount
; i
++)
615 exp
.anArgs
[i
] = ARG_LONG
;
620 /* Get optional redirection */
621 if ((pc
= NextToken(pc
)))
623 exp
.pcRedirection
= pc
;
624 exp
.nRedirectionLength
= TokenLength(pc
);
626 /* Check syntax (end of line) */
629 fprintf(stderr
, "error: line %d, additional tokens after ')'\n", nLine
);
635 exp
.pcRedirection
= 0;
636 exp
.nRedirectionLength
= 0;
639 OutputLine(fileDest
, &exp
);
648 printf("syntax: spec2pdef [<options> ...] <spec file>\n"
649 "Possible options:\n"
650 " -h --help prints this screen\n"
651 " -l=<file> generates an asm lib stub\n"
652 " -d=<file> generates a def file\n"
653 " -s=<file> generates a stub file\n"
654 " --ms msvc compatibility\n"
655 " -n=<name> name of the dll\n"
656 " --kill-at removes @xx decorations from exports\n"
657 " -r removes redirections from def file\n"
658 " -a=<arch> Set architecture to <arch>. (i386, x86_64, arm)\n");
661 int main(int argc
, char *argv
[])
664 char *pszSource
, *pszDefFileName
= 0, *pszStubFileName
= 0, *pszLibStubName
= 0;
676 for (i
= 1; i
< argc
&& *argv
[i
] == '-'; i
++)
678 if ((strcasecmp(argv
[i
], "--help") == 0) ||
679 (strcasecmp(argv
[i
], "-h") == 0))
684 else if (argv
[i
][1] == 'd' && argv
[i
][2] == '=')
686 pszDefFileName
= argv
[i
] + 3;
688 else if (argv
[i
][1] == 'l' && argv
[i
][2] == '=')
690 pszLibStubName
= argv
[i
] + 3;
692 else if (argv
[i
][1] == 's' && argv
[i
][2] == '=')
694 pszStubFileName
= argv
[i
] + 3;
696 else if (argv
[i
][1] == 'n' && argv
[i
][2] == '=')
698 pszDllName
= argv
[i
] + 3;
700 else if ((strcasecmp(argv
[i
], "--implib") == 0))
705 else if ((strcasecmp(argv
[i
], "--kill-at") == 0))
709 else if ((strcasecmp(argv
[i
], "--ms") == 0))
713 else if ((strcasecmp(argv
[i
], "-r") == 0))
717 else if (argv
[i
][1] == 'a' && argv
[i
][2] == '=')
719 pszArchString
= argv
[i
] + 3;
723 fprintf(stderr
, "Unrecognized option: %s\n", argv
[i
]);
728 if (strcasecmp(pszArchString
, "i386") == 0)
731 gpszUnderscore
= "_";
733 else if (strcasecmp(pszArchString
, "x86_64") == 0) giArch
= ARCH_AMD64
;
734 else if (strcasecmp(pszArchString
, "ia64") == 0) giArch
= ARCH_IA64
;
735 else if (strcasecmp(pszArchString
, "arm") == 0) giArch
= ARCH_ARM
;
736 else if (strcasecmp(pszArchString
, "ppc") == 0) giArch
= ARCH_PPC
;
738 if ((giArch
== ARCH_AMD64
) || (giArch
== ARCH_IA64
))
740 pszArchString2
= "win64";
743 pszArchString2
= "win32";
745 /* Set a default dll name */
751 p1
= strrchr(argv
[i
], '\\');
752 if (!p1
) p1
= strrchr(argv
[i
], '/');
753 p2
= p1
= p1
? p1
+ 1 : argv
[i
];
756 while (*p2
!= '.' && *p2
!= 0) p2
++;
758 if (len
>= sizeof(achDllName
) - 5)
760 fprintf(stderr
, "name too long: %s\n", p1
);
764 strncpy(achDllName
, p1
, len
);
765 strncpy(achDllName
+ len
, ".dll", sizeof(achDllName
) - len
);
766 pszDllName
= achDllName
;
769 /* Open input file argv[1] */
770 file
= fopen(argv
[i
], "r");
773 fprintf(stderr
, "error: could not open file %s ", argv
[i
]);
778 fseek(file
, 0, SEEK_END
);
779 nFileSize
= ftell(file
);
782 /* Allocate memory buffer */
783 pszSource
= malloc(nFileSize
+ 1);
784 if (!pszSource
) return -4;
786 /* Load input file into memory */
787 nFileSize
= fread(pszSource
, 1, nFileSize
, file
);
790 /* Zero terminate the source */
791 pszSource
[nFileSize
] = '\0';
795 /* Open output file */
796 file
= fopen(pszDefFileName
, "w");
799 fprintf(stderr
, "error: could not open output file %s ", argv
[i
+ 1]);
803 OutputHeader_def(file
, pszDllName
);
804 result
= ParseFile(pszSource
, file
, OutputLine_def
);
810 /* Open output file */
811 file
= fopen(pszStubFileName
, "w");
814 fprintf(stderr
, "error: could not open output file %s ", argv
[i
+ 1]);
818 OutputHeader_stub(file
);
819 result
= ParseFile(pszSource
, file
, OutputLine_stub
);
825 /* Open output file */
826 file
= fopen(pszLibStubName
, "w");
829 fprintf(stderr
, "error: could not open output file %s ", argv
[i
+ 1]);
833 OutputHeader_asmstub(file
, pszDllName
);
834 result
= ParseFile(pszSource
, file
, OutputLine_asmstub
);
835 fprintf(file
, "\nEND\n");