7 #define strcasecmp _stricmp
15 int nRedirectionLength
;
16 int nCallingConvention
;
34 typedef int (*PFNOUTLINE
)(FILE *, EXPORT
*);
38 int no_redirections
= 0;
39 int giArch
= ARCH_X86
;
40 char *pszArchString
= "i386";
43 char *gpszUnderscore
= "";
74 char* astrCallingConventions
[] =
87 return ((chr
<= ',' && chr
!= '$') ||
88 (chr
>= ':' && chr
< '?') );
92 CompareToken(const char *token
, const char *comparand
)
96 if (*token
!= *comparand
) return 0;
100 if (!IsSeparator(*token
)) return 0;
105 ScanToken(const char *token
, char chr
)
107 while (!IsSeparator(*token
))
109 if (*token
++ == chr
) return 1;
119 if (pc
[0] == '\n' && pc
[1] == '\r') return pc
+ 2;
120 else if (pc
[0] == '\n') return pc
+ 1;
127 TokenLength(char *pc
)
131 while (!IsSeparator(*pc
++)) length
++;
140 while (!IsSeparator(*pc
)) pc
++;
142 /* Skip white spaces */
143 while (*pc
== ' ' || *pc
== '\t') pc
++;
145 /* Check for end of line */
146 if (*pc
== '\n' || *pc
== '\r' || *pc
== 0) return 0;
148 /* Check for comment */
149 if (*pc
== '#' || *pc
== ';') return 0;
155 OutputHeader_stub(FILE *file
)
157 fprintf(file
, "/* This file is autogenerated, do not edit. */\n\n"
158 "#include <stubs.h>\n\n");
162 OutputLine_stub(FILE *file
, EXPORT
*pexp
)
166 if (pexp
->nCallingConvention
!= CC_STUB
&&
167 (pexp
->uFlags
& FL_STUB
) == 0) return 0;
169 fprintf(file
, "int ");
170 if ((giArch
== ARCH_X86
) &&
171 pexp
->nCallingConvention
== CC_STDCALL
)
173 fprintf(file
, "__stdcall ");
177 if (pexp
->pcName
[0] == '?')
179 fprintf(file
, "stub_function%d(", pexp
->nNumber
);
183 fprintf(file
, "%.*s(", pexp
->nNameLength
, pexp
->pcName
);
186 for (i
= 0; i
< pexp
->nArgCount
; i
++)
188 if (i
!= 0) fprintf(file
, ", ");
189 switch (pexp
->anArgs
[i
])
191 case ARG_LONG
: fprintf(file
, "long"); break;
192 case ARG_PTR
: fprintf(file
, "void*"); break;
193 case ARG_STR
: fprintf(file
, "char*"); break;
194 case ARG_WSTR
: fprintf(file
, "wchar_t*"); break;
196 case ARG_INT64
: fprintf(file
, "__int64"); break;
197 case ARG_INT128
: fprintf(file
, "__int128"); break;
198 case ARG_FLOAT
: fprintf(file
, "float"); break;
200 fprintf(file
, " a%d", i
);
202 fprintf(file
, ")\n{\n\tDPRINT1(\"WARNING: calling stub %.*s(",
203 pexp
->nNameLength
, pexp
->pcName
);
205 for (i
= 0; i
< pexp
->nArgCount
; i
++)
207 if (i
!= 0) fprintf(file
, ",");
208 switch (pexp
->anArgs
[i
])
210 case ARG_LONG
: fprintf(file
, "0x%%lx"); break;
211 case ARG_PTR
: fprintf(file
, "0x%%p"); break;
212 case ARG_STR
: fprintf(file
, "'%%s'"); break;
213 case ARG_WSTR
: fprintf(file
, "'%%ws'"); break;
214 case ARG_DBL
: fprintf(file
, "%%f"); break;
215 case ARG_INT64
: fprintf(file
, "%%\"PRix64\""); break;
216 case ARG_INT128
: fprintf(file
, "%%\"PRix128\""); break;
217 case ARG_FLOAT
: fprintf(file
, "%%f"); break;
220 fprintf(file
, ")\\n\"");
222 for (i
= 0; i
< pexp
->nArgCount
; i
++)
225 switch (pexp
->anArgs
[i
])
227 case ARG_LONG
: fprintf(file
, "(long)a%d", i
); break;
228 case ARG_PTR
: fprintf(file
, "(void*)a%d", i
); break;
229 case ARG_STR
: fprintf(file
, "(char*)a%d", i
); break;
230 case ARG_WSTR
: fprintf(file
, "(wchar_t*)a%d", i
); break;
231 case ARG_DBL
: fprintf(file
, "(double)a%d", i
); break;
232 case ARG_INT64
: fprintf(file
, "(__int64)a%d", i
); break;
233 case ARG_INT128
: fprintf(file
, "(__int128)a%d", i
); break;
234 case ARG_FLOAT
: fprintf(file
, "(float)a%d", i
); break;
237 fprintf(file
, ");\n");
239 if (pexp
->nCallingConvention
== CC_STUB
)
241 fprintf(file
, "\t__wine_spec_unimplemented_stub(\"%s\", __FUNCTION__);\n", pszDllName
);
244 fprintf(file
, "\treturn 0;\n}\n\n");
250 OutputHeader_asmstub(FILE *file
, char *libname
)
252 fprintf(file
, "; File generated automatically, do not edit! \n\n");
254 if (giArch
== ARCH_X86
)
255 fprintf(file
, ".586\n.model flat\n");
257 fprintf(file
, ".code\n");
261 OutputLine_asmstub(FILE *fileDest
, EXPORT
*pexp
)
263 /* Handle autoname */
264 if (pexp
->nNameLength
== 1 && pexp
->pcName
[0] == '@')
266 fprintf(fileDest
, "PUBLIC %sordinal%d\n%sordinal%d: nop\n",
267 gpszUnderscore
, pexp
->nOrdinal
, gpszUnderscore
, pexp
->nOrdinal
);
269 else if (giArch
!= ARCH_X86
)
271 fprintf(fileDest
, "PUBLIC _stub_%.*s\n_stub_%.*s: nop\n",
272 pexp
->nNameLength
, pexp
->pcName
,
273 pexp
->nNameLength
, pexp
->pcName
);
275 else if (pexp
->nCallingConvention
== CC_STDCALL
)
277 fprintf(fileDest
, "PUBLIC __stub_%.*s@%d\n__stub_%.*s@%d: nop\n",
278 pexp
->nNameLength
, pexp
->pcName
, pexp
->nStackBytes
,
279 pexp
->nNameLength
, pexp
->pcName
, pexp
->nStackBytes
);
281 else if (pexp
->nCallingConvention
== CC_FASTCALL
)
283 fprintf(fileDest
, "PUBLIC @_stub_%.*s@%d\n@_stub_%.*s@%d: nop\n",
284 pexp
->nNameLength
, pexp
->pcName
, pexp
->nStackBytes
,
285 pexp
->nNameLength
, pexp
->pcName
, pexp
->nStackBytes
);
287 else if (pexp
->nCallingConvention
== CC_CDECL
||
288 pexp
->nCallingConvention
== CC_STUB
)
290 fprintf(fileDest
, "PUBLIC __stub_%.*s\n__stub_%.*s: nop\n",
291 pexp
->nNameLength
, pexp
->pcName
,
292 pexp
->nNameLength
, pexp
->pcName
);
294 else if (pexp
->nCallingConvention
== CC_EXTERN
)
296 fprintf(fileDest
, "PUBLIC __stub_%.*s\n__stub_%.*s:\n",
297 pexp
->nNameLength
, pexp
->pcName
,
298 pexp
->nNameLength
, pexp
->pcName
);
305 OutputHeader_def(FILE *file
, char *libname
)
308 "; File generated automatically, do not edit!\n\n"
315 PrintName(FILE *fileDest
, EXPORT
*pexp
, char *pszPrefix
, int fRedir
, int fDeco
)
317 char *pcName
= fRedir
? pexp
->pcRedirection
: pexp
->pcName
;
318 int nNameLength
= fRedir
? pexp
->nRedirectionLength
: pexp
->nNameLength
;
320 /* Handle autoname */
321 if (nNameLength
== 1 && pcName
[0] == '@')
323 fprintf(fileDest
, "ordinal%d", pexp
->nOrdinal
);
327 if (fDeco
&& pexp
->nCallingConvention
== CC_FASTCALL
)
328 fprintf(fileDest
, "@");
329 fprintf(fileDest
, "%s%.*s", pszPrefix
, nNameLength
, pcName
);
330 if ((pexp
->nCallingConvention
== CC_STDCALL
||
331 pexp
->nCallingConvention
== CC_FASTCALL
) && fDeco
)
333 fprintf(fileDest
, "@%d", pexp
->nStackBytes
);
339 OutputLine_def(FILE *fileDest
, EXPORT
*pexp
)
341 fprintf(fileDest
, " ");
343 PrintName(fileDest
, pexp
, "", 0, (giArch
== ARCH_X86
) && !gbKillAt
);
347 fprintf(fileDest
, "=");
348 PrintName(fileDest
, pexp
, "_stub_", 0, 0);
350 else if (pexp
->pcRedirection
)
352 if (gbMSComp
&& (pexp
->pcName
[0] == '?'))
354 /* ignore c++ redirection, since link doesn't like that! */
360 fDeco
= ((giArch
== ARCH_X86
) && !ScanToken(pexp
->pcRedirection
, '.'));
361 fprintf(fileDest
, "=");
362 PrintName(fileDest
, pexp
, "", 1, fDeco
&& !gbMSComp
);
365 else if (((pexp
->uFlags
& FL_STUB
) || (pexp
->nCallingConvention
== CC_STUB
)) &&
366 (pexp
->pcName
[0] == '?'))
368 /* C++ stubs are forwarded to C stubs */
369 fprintf(fileDest
, "=");
370 fprintf(fileDest
, "stub_function%d", pexp
->nNumber
);
372 else if ((giArch
== ARCH_X86
) && gbKillAt
&& !gbMSComp
&&
373 (pexp
->nCallingConvention
== CC_STDCALL
||
374 pexp
->nCallingConvention
== CC_FASTCALL
))
376 fprintf(fileDest
, "=");
377 PrintName(fileDest
, pexp
, "", 0, 1);
380 if (pexp
->nOrdinal
!= -1)
382 fprintf(fileDest
, " @%d", pexp
->nOrdinal
);
385 if (pexp
->nCallingConvention
== CC_EXTERN
)
387 fprintf(fileDest
, " DATA");
390 if (pexp
->uFlags
& FL_PRIVATE
)
392 fprintf(fileDest
, " PRIVATE");
395 if (pexp
->uFlags
& FL_NONAME
)
397 fprintf(fileDest
, " NONAME");
400 fprintf(fileDest
, "\n");
406 ParseFile(char* pcStart
, FILE *fileDest
, PFNOUTLINE OutputLine
)
413 //fprintf(stderr, "info: line %d, pcStart:'%.30s'\n", nLine, pcStart);
418 for (pcLine
= pcStart
; *pcLine
; pcLine
= NextLine(pcLine
), nLine
++)
426 //fprintf(stderr, "info: line %d, token:'%d, %.20s'\n",
427 // nLine, TokenLength(pcLine), pcLine);
429 /* Skip white spaces */
430 while (*pc
== ' ' || *pc
== '\t') pc
++;
432 /* Skip empty lines, stop at EOF */
433 if (*pc
== ';' || *pc
<= '#') continue;
434 if (*pc
== 0) return 0;
436 //fprintf(stderr, "info: line %d, token:'%.*s'\n",
437 // nLine, TokenLength(pc), pc);
439 /* Now we should get either an ordinal or @ */
440 if (*pc
== '@') exp
.nOrdinal
= -1;
441 else exp
.nOrdinal
= atol(pc
);
443 /* Go to next token (type) */
444 if (!(pc
= NextToken(pc
)))
446 fprintf(stderr
, "error: line %d, unexpected end of line\n", nLine
);
450 //fprintf(stderr, "info: Token:'%.10s'\n", pc);
452 /* Now we should get the type */
453 if (CompareToken(pc
, "stdcall"))
455 exp
.nCallingConvention
= CC_STDCALL
;
457 else if (CompareToken(pc
, "cdecl") ||
458 CompareToken(pc
, "varargs"))
460 exp
.nCallingConvention
= CC_CDECL
;
462 else if (CompareToken(pc
, "fastcall"))
464 exp
.nCallingConvention
= CC_FASTCALL
;
466 else if (CompareToken(pc
, "thiscall"))
468 exp
.nCallingConvention
= CC_THISCALL
;
470 else if (CompareToken(pc
, "extern"))
472 exp
.nCallingConvention
= CC_EXTERN
;
474 else if (CompareToken(pc
, "stub"))
476 exp
.nCallingConvention
= CC_STUB
;
480 fprintf(stderr
, "error: line %d, expected type, got '%.*s' %d\n",
481 nLine
, TokenLength(pc
), pc
, *pc
);
485 //fprintf(stderr, "info: nCallingConvention: %d\n", exp.nCallingConvention);
487 /* Go to next token (options or name) */
488 if (!(pc
= NextToken(pc
)))
490 fprintf(stderr
, "fail2\n");
498 if (CompareToken(pc
, "-arch"))
500 /* Default to not included */
504 /* Look if we are included */
505 while (*pc
== '=' || *pc
== ',')
508 if (CompareToken(pc
, pszArchString
) ||
509 CompareToken(pc
, pszArchString2
))
514 /* Skip to next arch or end */
515 while (*pc
> ',') pc
++;
518 else if (CompareToken(pc
, "-i386"))
520 if (giArch
!= ARCH_X86
) included
= 0;
522 else if (CompareToken(pc
, "-private"))
524 exp
.uFlags
|= FL_PRIVATE
;
526 else if (CompareToken(pc
, "-noname") ||
527 CompareToken(pc
, "-ordinal"))
529 exp
.uFlags
|= FL_NONAME
;
531 else if (CompareToken(pc
, "-stub"))
533 exp
.uFlags
|= FL_STUB
;
535 else if (CompareToken(pc
, "-norelay") ||
536 CompareToken(pc
, "-register") ||
537 CompareToken(pc
, "-ret64"))
539 /* silently ignore these */
543 fprintf(stderr
, "info: ignored option: '%.*s'\n",
544 TokenLength(pc
), pc
);
547 /* Go to next token */
551 //fprintf(stderr, "info: Name:'%.10s'\n", pc);
553 /* If arch didn't match ours, skip this entry */
554 if (!included
) continue;
558 exp
.nNameLength
= TokenLength(pc
);
560 /* Handle parameters */
562 if (exp
.nCallingConvention
!= CC_EXTERN
&&
563 exp
.nCallingConvention
!= CC_STUB
)
565 //fprintf(stderr, "info: options:'%.10s'\n", pc);
566 /* Go to next token */
567 if (!(pc
= NextToken(pc
)))
569 fprintf(stderr
, "fail4\n");
576 fprintf(stderr
, "error: line %d, expected '('\n", nLine
);
580 /* Skip whitespaces */
581 while (*pc
== ' ' || *pc
== '\t') pc
++;
586 if (CompareToken(pc
, "long"))
588 exp
.nStackBytes
+= 4;
589 exp
.anArgs
[exp
.nArgCount
] = ARG_LONG
;
591 else if (CompareToken(pc
, "double"))
593 exp
.nStackBytes
+= 8;
594 exp
.anArgs
[exp
.nArgCount
] = ARG_DBL
;
596 else if (CompareToken(pc
, "ptr") ||
597 CompareToken(pc
, "str") ||
598 CompareToken(pc
, "wstr"))
600 exp
.nStackBytes
+= 4; // sizeof(void*) on x86
601 exp
.anArgs
[exp
.nArgCount
] = ARG_PTR
; // FIXME: handle strings
603 else if (CompareToken(pc
, "int64"))
605 exp
.nStackBytes
+= 8;
606 exp
.anArgs
[exp
.nArgCount
] = ARG_INT64
;
608 else if (CompareToken(pc
, "int128"))
610 exp
.nStackBytes
+= 16;
611 exp
.anArgs
[exp
.nArgCount
] = ARG_INT128
;
613 else if (CompareToken(pc
, "float"))
615 exp
.nStackBytes
+= 4;
616 exp
.anArgs
[exp
.nArgCount
] = ARG_FLOAT
;
619 fprintf(stderr
, "error: line %d, expected type, got: %.10s\n", nLine
, pc
);
623 /* Go to next parameter */
624 if (!(pc
= NextToken(pc
)))
626 fprintf(stderr
, "fail5\n");
634 fprintf(stderr
, "error: line %d, expected ')'\n", nLine
);
639 /* Handle special stub cases */
640 if (exp
.nCallingConvention
== CC_STUB
)
642 /* Check for c++ mangled name */
645 //printf("Found c++ mangled name...\n");
650 /* Check for stdcall name */
651 char *p
= strchr(pc
, '@');
652 if (p
&& (p
- pc
< exp
.nNameLength
))
655 exp
.nNameLength
= (int)(p
- pc
);
656 if (exp
.nNameLength
< 1)
658 fprintf(stderr
, "error, @ in line %d\n", nLine
);
661 exp
.nStackBytes
= atoi(p
+ 1);
662 exp
.nArgCount
= exp
.nStackBytes
/ 4;
663 exp
.nCallingConvention
= CC_STDCALL
;
664 exp
.uFlags
|= FL_STUB
;
665 for (i
= 0; i
< exp
.nArgCount
; i
++)
666 exp
.anArgs
[i
] = ARG_LONG
;
671 /* Get optional redirection */
675 exp
.pcRedirection
= pc
;
676 exp
.nRedirectionLength
= TokenLength(pc
);
678 /* Check syntax (end of line) */
681 fprintf(stderr
, "error: line %d, additional tokens after ')'\n", nLine
);
687 exp
.pcRedirection
= 0;
688 exp
.nRedirectionLength
= 0;
691 OutputLine(fileDest
, &exp
);
700 printf("syntax: spec2pdef [<options> ...] <spec file>\n"
701 "Possible options:\n"
702 " -h --help prints this screen\n"
703 " -l=<file> generates an asm lib stub\n"
704 " -d=<file> generates a def file\n"
705 " -s=<file> generates a stub file\n"
706 " --ms msvc compatibility\n"
707 " -n=<name> name of the dll\n"
708 " --kill-at removes @xx decorations from exports\n"
709 " -r removes redirections from def file\n"
710 " -a=<arch> Set architecture to <arch>. (i386, x86_64, arm)\n");
713 int main(int argc
, char *argv
[])
716 char *pszSource
, *pszDefFileName
= 0, *pszStubFileName
= 0, *pszLibStubName
= 0;
728 for (i
= 1; i
< argc
&& *argv
[i
] == '-'; i
++)
730 if ((strcasecmp(argv
[i
], "--help") == 0) ||
731 (strcasecmp(argv
[i
], "-h") == 0))
736 else if (argv
[i
][1] == 'd' && argv
[i
][2] == '=')
738 pszDefFileName
= argv
[i
] + 3;
740 else if (argv
[i
][1] == 'l' && argv
[i
][2] == '=')
742 pszLibStubName
= argv
[i
] + 3;
744 else if (argv
[i
][1] == 's' && argv
[i
][2] == '=')
746 pszStubFileName
= argv
[i
] + 3;
748 else if (argv
[i
][1] == 'n' && argv
[i
][2] == '=')
750 pszDllName
= argv
[i
] + 3;
752 else if ((strcasecmp(argv
[i
], "--implib") == 0))
757 else if ((strcasecmp(argv
[i
], "--kill-at") == 0))
761 else if ((strcasecmp(argv
[i
], "--ms") == 0))
765 else if ((strcasecmp(argv
[i
], "-r") == 0))
769 else if (argv
[i
][1] == 'a' && argv
[i
][2] == '=')
771 pszArchString
= argv
[i
] + 3;
775 fprintf(stderr
, "Unrecognized option: %s\n", argv
[i
]);
780 if (strcasecmp(pszArchString
, "i386") == 0)
783 gpszUnderscore
= "_";
785 else if (strcasecmp(pszArchString
, "x86_64") == 0) giArch
= ARCH_AMD64
;
786 else if (strcasecmp(pszArchString
, "ia64") == 0) giArch
= ARCH_IA64
;
787 else if (strcasecmp(pszArchString
, "arm") == 0) giArch
= ARCH_ARM
;
788 else if (strcasecmp(pszArchString
, "ppc") == 0) giArch
= ARCH_PPC
;
790 if ((giArch
== ARCH_AMD64
) || (giArch
== ARCH_IA64
))
792 pszArchString2
= "win64";
795 pszArchString2
= "win32";
797 /* Set a default dll name */
803 p1
= strrchr(argv
[i
], '\\');
804 if (!p1
) p1
= strrchr(argv
[i
], '/');
805 p2
= p1
= p1
? p1
+ 1 : argv
[i
];
808 while (*p2
!= '.' && *p2
!= 0) p2
++;
810 if (len
>= sizeof(achDllName
) - 5)
812 fprintf(stderr
, "name too long: %s\n", p1
);
816 strncpy(achDllName
, p1
, len
);
817 strncpy(achDllName
+ len
, ".dll", sizeof(achDllName
) - len
);
818 pszDllName
= achDllName
;
821 /* Open input file argv[1] */
822 file
= fopen(argv
[i
], "r");
825 fprintf(stderr
, "error: could not open file %s ", argv
[i
]);
830 fseek(file
, 0, SEEK_END
);
831 nFileSize
= ftell(file
);
834 /* Allocate memory buffer */
835 pszSource
= malloc(nFileSize
+ 1);
842 /* Load input file into memory */
843 nFileSize
= fread(pszSource
, 1, nFileSize
, file
);
846 /* Zero terminate the source */
847 pszSource
[nFileSize
] = '\0';
851 /* Open output file */
852 file
= fopen(pszDefFileName
, "w");
855 fprintf(stderr
, "error: could not open output file %s ", argv
[i
+ 1]);
859 OutputHeader_def(file
, pszDllName
);
860 result
= ParseFile(pszSource
, file
, OutputLine_def
);
866 /* Open output file */
867 file
= fopen(pszStubFileName
, "w");
870 fprintf(stderr
, "error: could not open output file %s ", argv
[i
+ 1]);
874 OutputHeader_stub(file
);
875 result
= ParseFile(pszSource
, file
, OutputLine_stub
);
881 /* Open output file */
882 file
= fopen(pszLibStubName
, "w");
885 fprintf(stderr
, "error: could not open output file %s ", argv
[i
+ 1]);
889 OutputHeader_asmstub(file
, pszDllName
);
890 result
= ParseFile(pszSource
, file
, OutputLine_asmstub
);
891 fprintf(file
, "\nEND\n");