1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <ctype.h> 4 #include <string.h> 5 #include <stdarg.h> 6 7 #ifdef _MSC_VER 8 #define strcasecmp(_String1, _String2) _stricmp(_String1, _String2) 9 #define strncasecmp(_String1, _String2, _MaxCount) _strnicmp(_String1, _String2, _MaxCount) 10 #endif 11 12 #define ARRAYSIZE(a) (sizeof(a) / sizeof((a)[0])) 13 14 typedef struct _STRING 15 { 16 const char *buf; 17 int len; 18 } STRING, *PSTRING; 19 20 typedef struct 21 { 22 STRING strName; 23 STRING strTarget; 24 int nCallingConvention; 25 int nOrdinal; 26 int nStackBytes; 27 int nArgCount; 28 int anArgs[30]; 29 unsigned int uFlags; 30 int nNumber; 31 unsigned nStartVersion; 32 unsigned nEndVersion; 33 int bVersionIncluded; 34 } EXPORT; 35 36 #if 0 // Debug helper function 37 void 38 PrintExport(EXPORT *pexp) 39 { 40 fprintf(stderr, "strName='%.*s'\n", pexp->strName.len, pexp->strName.buf); 41 fprintf(stderr, "strName='%.*s'\n", pexp->strTarget.len, pexp->strTarget.buf); 42 fprintf(stderr, "nCallingConvention=%u\n", pexp->nCallingConvention); 43 fprintf(stderr, "nOrdinal=%u\n", pexp->nOrdinal); 44 fprintf(stderr, "nStackBytes=%u\n", pexp->nStackBytes); 45 fprintf(stderr, "nArgCount=%u\n", pexp->nArgCount); 46 fprintf(stderr, "uFlags=0x%x\n", pexp->uFlags); 47 fprintf(stderr, "nNumber=%u\n", pexp->nNumber); 48 fprintf(stderr, "nStartVersion=%u\n", pexp->nStartVersion); 49 fprintf(stderr, "nEndVersion=%u\n", pexp->nEndVersion); 50 fprintf(stderr, "bVersionIncluded=%u\n", pexp->bVersionIncluded); 51 } 52 #endif 53 54 enum _ARCH 55 { 56 ARCH_X86, 57 ARCH_AMD64, 58 ARCH_IA64, 59 ARCH_ARM, 60 ARCH_PPC 61 }; 62 63 typedef int (*PFNOUTLINE)(FILE *, EXPORT *); 64 int gbMSComp = 0; 65 int gbImportLib = 0; 66 int gbNotPrivateNoWarn = 0; 67 int gbTracing = 0; 68 int giArch = ARCH_X86; 69 char *pszArchString = "i386"; 70 char *pszArchString2; 71 char *pszSourceFileName = NULL; 72 char *pszDllName = NULL; 73 char *gpszUnderscore = ""; 74 int gbDebug; 75 unsigned guOsVersion = 0x502; 76 #define DbgPrint(...) (!gbDebug || fprintf(stderr, __VA_ARGS__)) 77 78 enum 79 { 80 FL_PRIVATE = 1, 81 FL_STUB = 2, 82 FL_NONAME = 4, 83 FL_ORDINAL = 8, 84 FL_NORELAY = 16, 85 FL_RET64 = 32, 86 FL_REGISTER = 64, 87 }; 88 89 enum 90 { 91 CC_STDCALL, 92 CC_CDECL, 93 CC_FASTCALL, 94 CC_THISCALL, 95 CC_EXTERN, 96 CC_STUB, 97 }; 98 99 enum 100 { 101 ARG_LONG, 102 ARG_PTR, 103 ARG_STR, 104 ARG_WSTR, 105 ARG_DBL, 106 ARG_INT64, 107 ARG_INT128, 108 ARG_FLOAT 109 }; 110 111 const char* astrCallingConventions[] = 112 { 113 "STDCALL", 114 "CDECL", 115 "FASTCALL", 116 "THISCALL", 117 "EXTERN" 118 }; 119 120 /* 121 * List of OLE exports that should be PRIVATE and not be assigned an ordinal. 122 * In case these conditions are not met when linking with MS LINK.EXE, warnings 123 * LNK4104 and LNK4222 respectively are emitted. 124 */ 125 static const char* astrOlePrivateExports[] = 126 { 127 "DllCanUnloadNow", 128 "DllGetClassObject", 129 "DllGetClassFactoryFromClassString", 130 "DllGetDocumentation", 131 "DllInitialize", 132 "DllInstall", 133 "DllRegisterServer", 134 "DllRegisterServerEx", 135 "DllRegisterServerExW", 136 "DllUnload", 137 "DllUnregisterServer", 138 "RasCustomDeleteEntryNotify", 139 "RasCustomDial", 140 "RasCustomDialDlg", 141 "RasCustomEntryDlg", 142 }; 143 144 static 145 int 146 IsSeparator(char chr) 147 { 148 return ((chr <= ',' && chr != '$' && chr != '#') || 149 (chr >= ':' && chr < '?') ); 150 } 151 152 int 153 CompareToken(const char *token, const char *comparand) 154 { 155 while (*comparand) 156 { 157 if (*token != *comparand) return 0; 158 token++; 159 comparand++; 160 } 161 if (IsSeparator(comparand[-1])) return 1; 162 if (!IsSeparator(*token)) return 0; 163 return 1; 164 } 165 166 const char * 167 ScanToken(const char *token, char chr) 168 { 169 while (!IsSeparator(*token)) 170 { 171 if (*token == chr) return token; 172 token++; 173 } 174 return 0; 175 } 176 177 const char * 178 NextLine(const char *pc) 179 { 180 while (*pc != 0) 181 { 182 if (pc[0] == '\n' && pc[1] == '\r') return pc + 2; 183 else if (pc[0] == '\n') return pc + 1; 184 pc++; 185 } 186 return pc; 187 } 188 189 int 190 TokenLength(const char *pc) 191 { 192 int length = 0; 193 194 while (!IsSeparator(*pc++)) length++; 195 196 return length; 197 } 198 199 const char * 200 NextToken(const char *pc) 201 { 202 /* Skip token */ 203 while (!IsSeparator(*pc)) pc++; 204 205 /* Skip white spaces */ 206 while (*pc == ' ' || *pc == '\t') pc++; 207 208 /* Check for end of line */ 209 if (*pc == '\n' || *pc == '\r' || *pc == 0) return 0; 210 211 /* Check for comment */ 212 if (*pc == '#' || *pc == ';') return 0; 213 214 return pc; 215 } 216 217 void 218 OutputHeader_stub(FILE *file) 219 { 220 fprintf(file, "/* This file is autogenerated, do not edit. */\n\n" 221 "#include <stubs.h>\n"); 222 223 if (gbTracing) 224 { 225 fprintf(file, "#include <wine/debug.h>\n"); 226 fprintf(file, "#include <inttypes.h>\n"); 227 fprintf(file, "WINE_DECLARE_DEBUG_CHANNEL(relay);\n"); 228 } 229 230 fprintf(file, "\n"); 231 } 232 233 int 234 OutputLine_stub(FILE *file, EXPORT *pexp) 235 { 236 int i; 237 int bRelay = 0; 238 int bInPrototype = 0; 239 240 if (pexp->nCallingConvention != CC_STUB && 241 (pexp->uFlags & FL_STUB) == 0) 242 { 243 /* Only relay trace stdcall C functions */ 244 if (!gbTracing || (pexp->nCallingConvention != CC_STDCALL) 245 || (pexp->uFlags & FL_NORELAY) 246 || (pexp->strName.buf[0] == '?')) 247 { 248 return 0; 249 } 250 bRelay = 1; 251 } 252 253 /* Declare the "real" function */ 254 if (bRelay) 255 { 256 fprintf(file, "extern "); 257 bInPrototype = 1; 258 } 259 260 do 261 { 262 if (pexp->uFlags & FL_REGISTER) 263 { 264 /* FIXME: Not sure this is right */ 265 fprintf(file, "void "); 266 } 267 else if (pexp->uFlags & FL_RET64) 268 { 269 fprintf(file, "__int64 "); 270 } 271 else 272 { 273 fprintf(file, "int "); 274 } 275 276 if ((giArch == ARCH_X86) && 277 pexp->nCallingConvention == CC_STDCALL) 278 { 279 fprintf(file, "__stdcall "); 280 } 281 282 /* Check for C++ */ 283 if (pexp->strName.buf[0] == '?') 284 { 285 fprintf(file, "stub_function%d(", pexp->nNumber); 286 } 287 else 288 { 289 if (!bRelay || bInPrototype) 290 fprintf(file, "%.*s(", pexp->strName.len, pexp->strName.buf); 291 else 292 fprintf(file, "$relaytrace$%.*s(", pexp->strName.len, pexp->strName.buf); 293 } 294 295 for (i = 0; i < pexp->nArgCount; i++) 296 { 297 if (i != 0) fprintf(file, ", "); 298 switch (pexp->anArgs[i]) 299 { 300 case ARG_LONG: fprintf(file, "long"); break; 301 case ARG_PTR: fprintf(file, "void*"); break; 302 case ARG_STR: fprintf(file, "char*"); break; 303 case ARG_WSTR: fprintf(file, "wchar_t*"); break; 304 case ARG_DBL: fprintf(file, "double"); break; 305 case ARG_INT64 : fprintf(file, "__int64"); break; 306 /* __int128 is not supported on x86, and int128 in spec files most often represents a GUID */ 307 case ARG_INT128 : fprintf(file, "GUID"); break; 308 case ARG_FLOAT: fprintf(file, "float"); break; 309 } 310 fprintf(file, " a%d", i); 311 } 312 313 if (bInPrototype) 314 { 315 fprintf(file, ");\n\n"); 316 } 317 } while (bInPrototype--); 318 319 if (!bRelay) 320 { 321 fprintf(file, ")\n{\n\tDbgPrint(\"WARNING: calling stub %.*s(", 322 pexp->strName.len, pexp->strName.buf); 323 } 324 else 325 { 326 fprintf(file, ")\n{\n"); 327 if (pexp->uFlags & FL_REGISTER) 328 { 329 /* No return value */ 330 } 331 else if (pexp->uFlags & FL_RET64) 332 { 333 fprintf(file, "\t__int64 retval;\n"); 334 } 335 else 336 { 337 fprintf(file, "\tint retval;\n"); 338 } 339 fprintf(file, "\tif (TRACE_ON(relay))\n\t\tDPRINTF(\"%s: %.*s(", 340 pszDllName, pexp->strName.len, pexp->strName.buf); 341 } 342 343 for (i = 0; i < pexp->nArgCount; i++) 344 { 345 if (i != 0) fprintf(file, ","); 346 switch (pexp->anArgs[i]) 347 { 348 case ARG_LONG: fprintf(file, "0x%%lx"); break; 349 case ARG_PTR: fprintf(file, "0x%%p"); break; 350 case ARG_STR: fprintf(file, "'%%s'"); break; 351 case ARG_WSTR: fprintf(file, "'%%ws'"); break; 352 case ARG_DBL: fprintf(file, "%%f"); break; 353 case ARG_INT64: fprintf(file, "%%\"PRIx64\""); break; 354 case ARG_INT128: fprintf(file, "'%%s'"); break; 355 case ARG_FLOAT: fprintf(file, "%%f"); break; 356 } 357 } 358 fprintf(file, ")\\n\""); 359 360 for (i = 0; i < pexp->nArgCount; i++) 361 { 362 fprintf(file, ", "); 363 switch (pexp->anArgs[i]) 364 { 365 case ARG_LONG: fprintf(file, "(long)a%d", i); break; 366 case ARG_PTR: fprintf(file, "(void*)a%d", i); break; 367 case ARG_STR: fprintf(file, "(char*)a%d", i); break; 368 case ARG_WSTR: fprintf(file, "(wchar_t*)a%d", i); break; 369 case ARG_DBL: fprintf(file, "(double)a%d", i); break; 370 case ARG_INT64: fprintf(file, "(__int64)a%d", i); break; 371 case ARG_INT128: fprintf(file, "wine_dbgstr_guid(&a%d)", i); break; 372 case ARG_FLOAT: fprintf(file, "(float)a%d", i); break; 373 } 374 } 375 fprintf(file, ");\n"); 376 377 if (pexp->nCallingConvention == CC_STUB) 378 { 379 fprintf(file, "\t__wine_spec_unimplemented_stub(\"%s\", __FUNCTION__);\n", pszDllName); 380 } 381 else if (bRelay) 382 { 383 if (pexp->uFlags & FL_REGISTER) 384 { 385 fprintf(file,"\t"); 386 } 387 else 388 { 389 fprintf(file, "\tretval = "); 390 } 391 fprintf(file, "%.*s(", pexp->strName.len, pexp->strName.buf); 392 393 for (i = 0; i < pexp->nArgCount; i++) 394 { 395 if (i != 0) fprintf(file, ", "); 396 fprintf(file, "a%d", i); 397 } 398 fprintf(file, ");\n"); 399 } 400 401 if (!bRelay) 402 fprintf(file, "\treturn 0;\n}\n\n"); 403 else if ((pexp->uFlags & FL_REGISTER) == 0) 404 { 405 if (pexp->uFlags & FL_RET64) 406 { 407 fprintf(file, "\tif (TRACE_ON(relay))\n\t\tDPRINTF(\"%s: %.*s: retval = %%\"PRIx64\"\\n\", retval);\n", 408 pszDllName, pexp->strName.len, pexp->strName.buf); 409 } 410 else 411 { 412 fprintf(file, "\tif (TRACE_ON(relay))\n\t\tDPRINTF(\"%s: %.*s: retval = 0x%%lx\\n\", retval);\n", 413 pszDllName, pexp->strName.len, pexp->strName.buf); 414 } 415 fprintf(file, "\treturn retval;\n}\n\n"); 416 } 417 418 return 1; 419 } 420 421 void 422 OutputHeader_asmstub(FILE *file, char *libname) 423 { 424 fprintf(file, "; File generated automatically, do not edit! \n\n"); 425 426 if (giArch == ARCH_X86) 427 { 428 fprintf(file, ".586\n.model flat\n.code\n"); 429 } 430 else if (giArch == ARCH_AMD64) 431 { 432 fprintf(file, ".code\n"); 433 } 434 else if (giArch == ARCH_ARM) 435 { 436 fprintf(file, " AREA |.text|,ALIGN=2,CODE,READONLY\n\n"); 437 } 438 } 439 440 void 441 Output_stublabel(FILE *fileDest, char* pszSymbolName) 442 { 443 if (giArch == ARCH_ARM) 444 { 445 fprintf(fileDest, 446 "\tEXPORT |%s| [FUNC]\n|%s|\n", 447 pszSymbolName, 448 pszSymbolName); 449 } 450 else 451 { 452 fprintf(fileDest, 453 "PUBLIC %s\n%s: nop\n", 454 pszSymbolName, 455 pszSymbolName); 456 } 457 } 458 459 int 460 OutputLine_asmstub(FILE *fileDest, EXPORT *pexp) 461 { 462 char szNameBuffer[128]; 463 464 /* Handle autoname */ 465 if (pexp->strName.len == 1 && pexp->strName.buf[0] == '@') 466 { 467 sprintf(szNameBuffer, "%s_stub_ordinal%d", 468 gpszUnderscore, pexp->nOrdinal); 469 } 470 else if (giArch != ARCH_X86) 471 { 472 /* Does the string already have stdcall decoration? */ 473 const char *pcAt = ScanToken(pexp->strName.buf, '@'); 474 if (pcAt && (pcAt < (pexp->strName.buf + pexp->strName.len)) && 475 (pexp->strName.buf[0] == '_')) 476 { 477 /* Skip leading underscore and remove trailing decoration */ 478 sprintf(szNameBuffer, "_stub_%.*s", 479 (int)(pcAt - pexp->strName.buf - 1), 480 pexp->strName.buf + 1); 481 } 482 else 483 { 484 sprintf(szNameBuffer, "_stub_%.*s", 485 pexp->strName.len, pexp->strName.buf); 486 } 487 } 488 else if (pexp->nCallingConvention == CC_STDCALL) 489 { 490 sprintf(szNameBuffer, "__stub_%.*s@%d", 491 pexp->strName.len, pexp->strName.buf, pexp->nStackBytes); 492 } 493 else if (pexp->nCallingConvention == CC_FASTCALL) 494 { 495 sprintf(szNameBuffer, "@_stub_%.*s@%d", 496 pexp->strName.len, pexp->strName.buf, pexp->nStackBytes); 497 } 498 else if ((pexp->nCallingConvention == CC_CDECL) || 499 (pexp->nCallingConvention == CC_THISCALL) || 500 (pexp->nCallingConvention == CC_EXTERN) || 501 (pexp->nCallingConvention == CC_STUB)) 502 { 503 sprintf(szNameBuffer, "__stub_%.*s", 504 pexp->strName.len, pexp->strName.buf); 505 } 506 else 507 { 508 fprintf(stderr, "Invalid calling convention"); 509 return 0; 510 } 511 512 Output_stublabel(fileDest, szNameBuffer); 513 514 return 1; 515 } 516 517 void 518 OutputHeader_def(FILE *file, char *libname) 519 { 520 fprintf(file, 521 "; File generated automatically, do not edit!\n\n" 522 "NAME %s\n\n" 523 "EXPORTS\n", 524 libname); 525 } 526 527 void 528 PrintName(FILE *fileDest, EXPORT *pexp, PSTRING pstr, int fDeco) 529 { 530 const char *pcName = pstr->buf; 531 int nNameLength = pstr->len; 532 const char* pcDot, *pcAt; 533 char namebuffer[19]; 534 535 if ((nNameLength == 1) && (pcName[0] == '@')) 536 { 537 sprintf(namebuffer, "ordinal%d", pexp->nOrdinal); 538 pcName = namebuffer; 539 nNameLength = strlen(namebuffer); 540 } 541 542 /* Check for non-x86 first */ 543 if (giArch != ARCH_X86) 544 { 545 /* Does the string already have stdcall decoration? */ 546 pcAt = ScanToken(pcName, '@'); 547 if (pcAt && (pcAt < (pcName + nNameLength)) && (pcName[0] == '_')) 548 { 549 /* Skip leading underscore and remove trailing decoration */ 550 pcName++; 551 nNameLength = (int)(pcAt - pcName); 552 } 553 554 /* Print the undecorated function name */ 555 fprintf(fileDest, "%.*s", nNameLength, pcName); 556 } 557 else if (fDeco && 558 ((pexp->nCallingConvention == CC_STDCALL) || 559 (pexp->nCallingConvention == CC_FASTCALL))) 560 { 561 /* Scan for a dll forwarding dot */ 562 pcDot = ScanToken(pcName, '.'); 563 if (pcDot) 564 { 565 /* First print the dll name, followed by a dot */ 566 nNameLength = (int)(pcDot - pcName); 567 fprintf(fileDest, "%.*s.", nNameLength, pcName); 568 569 /* Now the actual function name */ 570 pcName = pcDot + 1; 571 nNameLength = pexp->strTarget.len - nNameLength - 1; 572 } 573 574 /* Does the string already have decoration? */ 575 pcAt = ScanToken(pcName, '@'); 576 if (pcAt && (pcAt < (pcName + nNameLength))) 577 { 578 /* On GCC, we need to remove the leading stdcall underscore */ 579 if (!gbMSComp && (pexp->nCallingConvention == CC_STDCALL)) 580 { 581 pcName++; 582 nNameLength--; 583 } 584 585 /* Print the already decorated function name */ 586 fprintf(fileDest, "%.*s", nNameLength, pcName); 587 } 588 else 589 { 590 /* Print the prefix, but skip it for (GCC && stdcall) */ 591 if (gbMSComp || (pexp->nCallingConvention != CC_STDCALL)) 592 { 593 fprintf(fileDest, "%c", pexp->nCallingConvention == CC_FASTCALL ? '@' : '_'); 594 } 595 596 /* Print the name with trailing decoration */ 597 fprintf(fileDest, "%.*s@%d", nNameLength, pcName, pexp->nStackBytes); 598 } 599 } 600 else 601 { 602 /* Print the undecorated function name */ 603 fprintf(fileDest, "%.*s", nNameLength, pcName); 604 } 605 } 606 607 void 608 OutputLine_def_MS(FILE *fileDest, EXPORT *pexp) 609 { 610 PrintName(fileDest, pexp, &pexp->strName, 0); 611 612 if (gbImportLib) 613 { 614 /* Redirect to a stub function, to get the right decoration in the lib */ 615 fprintf(fileDest, "=_stub_"); 616 PrintName(fileDest, pexp, &pexp->strName, 0); 617 } 618 else if (pexp->strTarget.buf) 619 { 620 if (pexp->strName.buf[0] == '?') 621 { 622 //fprintf(stderr, "warning: ignoring C++ redirection %.*s -> %.*s\n", 623 // pexp->strName.len, pexp->strName.buf, pexp->strTarget.len, pexp->strTarget.buf); 624 } 625 else 626 { 627 fprintf(fileDest, "="); 628 629 /* If the original name was decorated, use decoration in the forwarder as well */ 630 if ((giArch == ARCH_X86) && ScanToken(pexp->strName.buf, '@') && 631 !ScanToken(pexp->strTarget.buf, '@') && 632 ((pexp->nCallingConvention == CC_STDCALL) || 633 (pexp->nCallingConvention == CC_FASTCALL)) ) 634 { 635 PrintName(fileDest, pexp, &pexp->strTarget, 1); 636 } 637 else 638 { 639 /* Write the undecorated redirection name */ 640 fprintf(fileDest, "%.*s", pexp->strTarget.len, pexp->strTarget.buf); 641 } 642 } 643 } 644 else if (((pexp->uFlags & FL_STUB) || (pexp->nCallingConvention == CC_STUB)) && 645 (pexp->strName.buf[0] == '?')) 646 { 647 /* C++ stubs are forwarded to C stubs */ 648 fprintf(fileDest, "=stub_function%d", pexp->nNumber); 649 } 650 else if (gbTracing && ((pexp->uFlags & FL_NORELAY) == 0) && (pexp->nCallingConvention == CC_STDCALL) && 651 (pexp->strName.buf[0] != '?')) 652 { 653 /* Redirect it to the relay-tracing trampoline */ 654 fprintf(fileDest, "=$relaytrace$%.*s", pexp->strName.len, pexp->strName.buf); 655 } 656 } 657 658 void 659 OutputLine_def_GCC(FILE *fileDest, EXPORT *pexp) 660 { 661 int bTracing = 0; 662 /* Print the function name, with decoration for export libs */ 663 PrintName(fileDest, pexp, &pexp->strName, gbImportLib); 664 DbgPrint("Generating def line for '%.*s'\n", pexp->strName.len, pexp->strName.buf); 665 666 /* Check if this is a forwarded export */ 667 if (pexp->strTarget.buf) 668 { 669 int fIsExternal = !!ScanToken(pexp->strTarget.buf, '.'); 670 DbgPrint("Got redirect '%.*s'\n", pexp->strTarget.len, pexp->strTarget.buf); 671 672 /* print the target name, don't decorate if it is external */ 673 fprintf(fileDest, "="); 674 PrintName(fileDest, pexp, &pexp->strTarget, !fIsExternal); 675 } 676 else if (((pexp->uFlags & FL_STUB) || (pexp->nCallingConvention == CC_STUB)) && 677 (pexp->strName.buf[0] == '?')) 678 { 679 /* C++ stubs are forwarded to C stubs */ 680 fprintf(fileDest, "=stub_function%d", pexp->nNumber); 681 } 682 else if (gbTracing && ((pexp->uFlags & FL_NORELAY) == 0) && 683 (pexp->nCallingConvention == CC_STDCALL) && 684 (pexp->strName.buf[0] != '?')) 685 { 686 /* Redirect it to the relay-tracing trampoline */ 687 char buf[256]; 688 STRING strTarget; 689 fprintf(fileDest, "="); 690 sprintf(buf, "$relaytrace$%.*s", pexp->strName.len, pexp->strName.buf); 691 strTarget.buf = buf; 692 strTarget.len = pexp->strName.len + 12; 693 PrintName(fileDest, pexp, &strTarget, 1); 694 bTracing = 1; 695 } 696 697 /* Special handling for stdcall and fastcall */ 698 if ((giArch == ARCH_X86) && 699 ((pexp->nCallingConvention == CC_STDCALL) || 700 (pexp->nCallingConvention == CC_FASTCALL))) 701 { 702 /* Is this the import lib? */ 703 if (gbImportLib) 704 { 705 /* Is the name in the spec file decorated? */ 706 const char* pcDeco = ScanToken(pexp->strName.buf, '@'); 707 if (pcDeco && 708 (pexp->strName.len > 1) && 709 (pcDeco < pexp->strName.buf + pexp->strName.len)) 710 { 711 /* Write the name including the leading @ */ 712 fprintf(fileDest, "==%.*s", pexp->strName.len, pexp->strName.buf); 713 } 714 } 715 else if ((!pexp->strTarget.buf) && !(bTracing)) 716 { 717 /* Write a forwarder to the actual decorated symbol */ 718 fprintf(fileDest, "="); 719 PrintName(fileDest, pexp, &pexp->strName, 1); 720 } 721 } 722 } 723 724 int 725 OutputLine_def(FILE *fileDest, EXPORT *pexp) 726 { 727 DbgPrint("OutputLine_def: '%.*s'...\n", pexp->strName.len, pexp->strName.buf); 728 fprintf(fileDest, " "); 729 730 if (gbMSComp) 731 OutputLine_def_MS(fileDest, pexp); 732 else 733 OutputLine_def_GCC(fileDest, pexp); 734 735 if (pexp->uFlags & FL_ORDINAL) 736 { 737 fprintf(fileDest, " @%d", pexp->nOrdinal); 738 } 739 740 if (pexp->uFlags & FL_NONAME) 741 { 742 fprintf(fileDest, " NONAME"); 743 } 744 745 /* Either PRIVATE or DATA */ 746 if (pexp->uFlags & FL_PRIVATE) 747 { 748 fprintf(fileDest, " PRIVATE"); 749 } 750 else if (pexp->nCallingConvention == CC_EXTERN) 751 { 752 fprintf(fileDest, " DATA"); 753 } 754 755 fprintf(fileDest, "\n"); 756 757 return 1; 758 } 759 760 void 761 Fatalv( 762 const char* filename, 763 unsigned nLine, 764 const char *pcLine, 765 const char *pc, 766 size_t errorlen, 767 const char *format, 768 va_list argptr) 769 { 770 unsigned i, errorpos, len; 771 const char* pcLineEnd; 772 773 /* Get the length of the line */ 774 pcLineEnd = strpbrk(pcLine, "\r\n"); 775 len = pcLineEnd - pcLine; 776 777 if (pc == NULL) 778 { 779 pc = pcLine + len - 1; 780 errorlen = 1; 781 } 782 783 errorpos = (unsigned)(pc - pcLine); 784 785 /* Output the error message */ 786 fprintf(stderr, "ERROR: (%s:%u:%u): ", filename, nLine, errorpos); 787 vfprintf(stderr, format, argptr); 788 fprintf(stderr, "\n"); 789 790 /* Output the line with the error */ 791 fprintf(stderr, "> %.*s\n", len, pcLine); 792 793 if (errorlen == 0) 794 { 795 errorlen = TokenLength(pc); 796 } 797 798 for (i = 0; i < errorpos + 2; i++) 799 { 800 fprintf(stderr, " "); 801 } 802 for (i = 0; i < errorlen; i++) 803 { 804 fprintf(stderr, "~"); 805 } 806 fprintf(stderr, "\n"); 807 exit(-1); 808 } 809 810 void 811 Fatal( 812 const char* filename, 813 unsigned nLine, 814 const char *pcLine, 815 const char *pc, 816 size_t errorlen, 817 const char *format, 818 ...) 819 { 820 va_list argptr; 821 822 va_start(argptr, format); 823 Fatalv(filename, nLine, pcLine, pc, errorlen, format, argptr); 824 va_end(argptr); 825 } 826 827 EXPORT * 828 ParseFile(char* pcStart, FILE *fileDest, unsigned *cExports) 829 { 830 EXPORT *pexports; 831 const char *pc, *pcLine; 832 int cLines, nLine; 833 EXPORT exp; 834 int included; 835 unsigned int i; 836 837 *cExports = 0; 838 839 //fprintf(stderr, "info: line %d, pcStart:'%.30s'\n", nLine, pcStart); 840 841 /* Count the lines */ 842 for (cLines = 1, pcLine = pcStart; *pcLine; pcLine = NextLine(pcLine), cLines++) 843 { 844 /* Nothing */ 845 } 846 847 /* Allocate an array of EXPORT structures */ 848 pexports = malloc(cLines * sizeof(EXPORT)); 849 if (pexports == NULL) 850 { 851 fprintf(stderr, "ERROR: %s: failed to allocate EXPORT array of %u elements\n", pszSourceFileName, cLines); 852 return NULL; 853 } 854 855 /* Loop all lines */ 856 nLine = 1; 857 exp.nNumber = 0; 858 for (pcLine = pcStart; *pcLine; pcLine = NextLine(pcLine), nLine++) 859 { 860 pc = pcLine; 861 862 exp.strName.buf = NULL; 863 exp.strName.len = 0; 864 exp.strTarget.buf = NULL; 865 exp.strTarget.len = 0; 866 exp.nArgCount = 0; 867 exp.uFlags = 0; 868 exp.nNumber++; 869 exp.nStartVersion = 0; 870 exp.nEndVersion = 0xFFFFFFFF; 871 exp.bVersionIncluded = 1; 872 873 /* Skip white spaces */ 874 while (*pc == ' ' || *pc == '\t') pc++; 875 876 /* Check for line break or comment */ 877 if ((*pc == '\r') || (*pc == '\n') || 878 (*pc == ';') || (*pc == '#')) 879 { 880 continue; 881 } 882 883 /* On EOF we are done */ 884 if (*pc == 0) 885 { 886 return pexports; 887 } 888 889 /* Now we should get either an ordinal or @ */ 890 if (*pc == '@') 891 { 892 exp.nOrdinal = -1; 893 } 894 else if ((*pc >= '0') && (*pc <= '9')) 895 { 896 char* end; 897 long int number = strtol(pc, &end, 10); 898 if ((*end != ' ') && (*end != '\t')) 899 { 900 Fatal(pszSourceFileName, nLine, pcLine, end, 0, "Unexpected character(s) after ordinal"); 901 } 902 903 if ((number < 0) || (number > 0xFFFE)) 904 { 905 Fatal(pszSourceFileName, nLine, pcLine, pc, 0, "Invalid value for ordinal"); 906 } 907 908 exp.nOrdinal = number; 909 910 /* The import lib should contain the ordinal only if -ordinal was specified */ 911 if (!gbImportLib) 912 exp.uFlags |= FL_ORDINAL; 913 } 914 else 915 { 916 Fatal(pszSourceFileName, nLine, pcLine, pc, 0, "Expected '@' or ordinal"); 917 } 918 919 /* Go to next token (type) */ 920 if (!(pc = NextToken(pc))) 921 { 922 Fatal(pszSourceFileName, nLine, pcLine, pc, 1, "Unexpected end of line"); 923 } 924 925 //fprintf(stderr, "info: Token:'%.*s'\n", TokenLength(pc), pc); 926 927 /* Now we should get the type */ 928 if (CompareToken(pc, "stdcall")) 929 { 930 exp.nCallingConvention = CC_STDCALL; 931 } 932 else if (CompareToken(pc, "cdecl") || 933 CompareToken(pc, "varargs")) 934 { 935 exp.nCallingConvention = CC_CDECL; 936 } 937 else if (CompareToken(pc, "fastcall")) 938 { 939 exp.nCallingConvention = CC_FASTCALL; 940 } 941 else if (CompareToken(pc, "thiscall")) 942 { 943 exp.nCallingConvention = CC_THISCALL; 944 } 945 else if (CompareToken(pc, "extern")) 946 { 947 exp.nCallingConvention = CC_EXTERN; 948 } 949 else if (CompareToken(pc, "stub")) 950 { 951 exp.nCallingConvention = CC_STUB; 952 } 953 else 954 { 955 Fatal(pszSourceFileName, nLine, pcLine, pc, 0, "Invalid calling convention"); 956 } 957 958 /* Go to next token (options or name) */ 959 if (!(pc = NextToken(pc))) 960 { 961 Fatal(pszSourceFileName, nLine, pcLine, pc, 1, "Unexpected end of line"); 962 } 963 964 /* Handle options */ 965 included = 1; 966 while (*pc == '-') 967 { 968 if (CompareToken(pc, "-arch=")) 969 { 970 /* Default to not included */ 971 included = 0; 972 pc += 5; 973 974 /* Look if we are included */ 975 do 976 { 977 pc++; 978 if (CompareToken(pc, pszArchString) || 979 CompareToken(pc, pszArchString2)) 980 { 981 included = 1; 982 } 983 984 /* Skip to next arch or end */ 985 while (*pc > ',') pc++; 986 } while (*pc == ','); 987 } 988 else if (CompareToken(pc, "-i386")) 989 { 990 if (giArch != ARCH_X86) included = 0; 991 } 992 else if (CompareToken(pc, "-version=")) 993 { 994 const char *pcVersionStart = pc + 9; 995 996 /* Default to not included */ 997 exp.bVersionIncluded = 0; 998 pc += 8; 999 1000 /* Look if we are included */ 1001 do 1002 { 1003 unsigned version, endversion; 1004 1005 /* Optionally skip leading '0x' */ 1006 pc++; 1007 if ((pc[0] == '0') && (pc[1] == 'x')) pc += 2; 1008 1009 /* Now get the version number */ 1010 endversion = version = strtoul(pc, (char**)&pc, 16); 1011 1012 /* Check if it's a range */ 1013 if (pc[0] == '+') 1014 { 1015 endversion = 0xFFF; 1016 pc++; 1017 } 1018 else if (pc[0] == '-') 1019 { 1020 /* Optionally skip leading '0x' */ 1021 pc++; 1022 if ((pc[0] == '0') && (pc[1] == 'x')) pc += 2; 1023 endversion = strtoul(pc, (char**)&pc, 16); 1024 } 1025 1026 /* Check for degenerate range */ 1027 if (version > endversion) 1028 { 1029 Fatal(pszSourceFileName, 1030 nLine, 1031 pcLine, 1032 pcVersionStart, 1033 pc - pcVersionStart, 1034 "Invalid version range"); 1035 } 1036 1037 exp.nStartVersion = version; 1038 exp.nEndVersion = endversion; 1039 1040 /* Now compare the range with our version */ 1041 if ((guOsVersion >= version) && 1042 (guOsVersion <= endversion)) 1043 { 1044 exp.bVersionIncluded = 1; 1045 } 1046 1047 /* Skip to next arch or end */ 1048 while (*pc > ',') pc++; 1049 1050 } while (*pc == ','); 1051 } 1052 else if (CompareToken(pc, "-private")) 1053 { 1054 exp.uFlags |= FL_PRIVATE; 1055 } 1056 else if (CompareToken(pc, "-noname")) 1057 { 1058 exp.uFlags |= FL_ORDINAL | FL_NONAME; 1059 } 1060 else if (CompareToken(pc, "-ordinal")) 1061 { 1062 exp.uFlags |= FL_ORDINAL; 1063 /* GCC doesn't automatically import by ordinal if an ordinal 1064 * is found in the def file. Force it. */ 1065 if (gbImportLib && !gbMSComp) 1066 exp.uFlags |= FL_NONAME; 1067 } 1068 else if (CompareToken(pc, "-stub")) 1069 { 1070 exp.uFlags |= FL_STUB; 1071 } 1072 else if (CompareToken(pc, "-norelay")) 1073 { 1074 exp.uFlags |= FL_NORELAY; 1075 } 1076 else if (CompareToken(pc, "-ret64")) 1077 { 1078 exp.uFlags |= FL_RET64; 1079 } 1080 else if (CompareToken(pc, "-register")) 1081 { 1082 exp.uFlags |= FL_REGISTER; 1083 } 1084 else 1085 { 1086 fprintf(stdout, 1087 "INFO: %s line %d: Ignored option: '%.*s'\n", 1088 pszSourceFileName, 1089 nLine, 1090 TokenLength(pc), 1091 pc); 1092 } 1093 1094 /* Go to next token */ 1095 pc = NextToken(pc); 1096 } 1097 1098 //fprintf(stderr, "info: Name:'%.10s'\n", pc); 1099 1100 /* If arch didn't match ours, skip this entry */ 1101 if (!included) continue; 1102 1103 /* Get name */ 1104 exp.strName.buf = pc; 1105 exp.strName.len = TokenLength(pc); 1106 //DbgPrint("Got name: '%.*s'\n", exp.strName.len, exp.strName.buf); 1107 1108 /* Check for autoname */ 1109 if ((exp.strName.len == 1) && (exp.strName.buf[0] == '@')) 1110 { 1111 exp.uFlags |= FL_ORDINAL | FL_NONAME; 1112 } 1113 1114 /* Handle parameters */ 1115 exp.nStackBytes = 0; 1116 if (exp.nCallingConvention != CC_EXTERN && 1117 exp.nCallingConvention != CC_STUB) 1118 { 1119 /* Go to next token */ 1120 if (!(pc = NextToken(pc))) 1121 { 1122 Fatal(pszSourceFileName, nLine, pcLine, pc, 1, "Unexpected end of line"); 1123 } 1124 1125 /* Verify syntax */ 1126 if (*pc++ != '(') 1127 { 1128 Fatal(pszSourceFileName, nLine, pcLine, pc - 1, 0, "Expected '('"); 1129 } 1130 1131 /* Skip whitespaces */ 1132 while (*pc == ' ' || *pc == '\t') pc++; 1133 1134 exp.nStackBytes = 0; 1135 while (*pc >= '0') 1136 { 1137 if (CompareToken(pc, "long")) 1138 { 1139 exp.nStackBytes += 4; 1140 exp.anArgs[exp.nArgCount] = ARG_LONG; 1141 } 1142 else if (CompareToken(pc, "double")) 1143 { 1144 exp.nStackBytes += 8; 1145 exp.anArgs[exp.nArgCount] = ARG_DBL; 1146 } 1147 else if (CompareToken(pc, "ptr")) 1148 { 1149 exp.nStackBytes += 4; // sizeof(void*) on x86 1150 exp.anArgs[exp.nArgCount] = ARG_PTR; 1151 } 1152 else if (CompareToken(pc, "str")) 1153 { 1154 exp.nStackBytes += 4; // sizeof(void*) on x86 1155 exp.anArgs[exp.nArgCount] = ARG_STR; 1156 } 1157 else if (CompareToken(pc, "wstr")) 1158 { 1159 exp.nStackBytes += 4; // sizeof(void*) on x86 1160 exp.anArgs[exp.nArgCount] = ARG_WSTR; 1161 } 1162 else if (CompareToken(pc, "int64")) 1163 { 1164 exp.nStackBytes += 8; 1165 exp.anArgs[exp.nArgCount] = ARG_INT64; 1166 } 1167 else if (CompareToken(pc, "int128")) 1168 { 1169 exp.nStackBytes += 16; 1170 exp.anArgs[exp.nArgCount] = ARG_INT128; 1171 } 1172 else if (CompareToken(pc, "float")) 1173 { 1174 exp.nStackBytes += 4; 1175 exp.anArgs[exp.nArgCount] = ARG_FLOAT; 1176 } 1177 else 1178 { 1179 Fatal(pszSourceFileName, nLine, pcLine, pc, 0, "Unrecognized type"); 1180 } 1181 1182 exp.nArgCount++; 1183 1184 /* Go to next parameter */ 1185 if (!(pc = NextToken(pc))) 1186 { 1187 Fatal(pszSourceFileName, nLine, pcLine, pc, 1, "Unexpected end of line"); 1188 } 1189 } 1190 1191 /* Check syntax */ 1192 if (*pc++ != ')') 1193 { 1194 Fatal(pszSourceFileName, nLine, pcLine, pc - 1, 0, "Expected ')'"); 1195 } 1196 } 1197 1198 /* Handle special stub cases */ 1199 if (exp.nCallingConvention == CC_STUB) 1200 { 1201 /* Check for c++ mangled name */ 1202 if (pc[0] == '?') 1203 { 1204 //printf("Found c++ mangled name...\n"); 1205 // 1206 } 1207 else 1208 { 1209 /* Check for stdcall name */ 1210 const char *p = ScanToken(pc, '@'); 1211 if (p && (p - pc < exp.strName.len)) 1212 { 1213 int i; 1214 1215 /* Truncate the name to before the @ */ 1216 exp.strName.len = (int)(p - pc); 1217 if (exp.strName.len < 1) 1218 { 1219 Fatal(pszSourceFileName, nLine, pcLine, p, 1, "Unexpected @"); 1220 } 1221 exp.nStackBytes = atoi(p + 1); 1222 exp.nArgCount = exp.nStackBytes / 4; 1223 exp.nCallingConvention = CC_STDCALL; 1224 exp.uFlags |= FL_STUB; 1225 for (i = 0; i < exp.nArgCount; i++) 1226 exp.anArgs[i] = ARG_LONG; 1227 } 1228 } 1229 } 1230 1231 /* Get optional redirection */ 1232 pc = NextToken(pc); 1233 if (pc) 1234 { 1235 exp.strTarget.buf = pc; 1236 exp.strTarget.len = TokenLength(pc); 1237 1238 /* Check syntax (end of line) */ 1239 if (NextToken(pc)) 1240 { 1241 Fatal(pszSourceFileName, nLine, pcLine, NextToken(pc), 0, "Excess token(s) at end of definition"); 1242 } 1243 1244 /* Don't relay-trace forwarded functions */ 1245 exp.uFlags |= FL_NORELAY; 1246 } 1247 else 1248 { 1249 exp.strTarget.buf = NULL; 1250 exp.strTarget.len = 0; 1251 } 1252 1253 /* Check for no-name without ordinal */ 1254 if ((exp.uFlags & FL_ORDINAL) && (exp.nOrdinal == -1)) 1255 { 1256 Fatal(pszSourceFileName, nLine, pcLine, pc, 0, "Ordinal export without ordinal"); 1257 } 1258 1259 /* 1260 * Check for special handling of OLE exports, only when MSVC 1261 * is not used, since otherwise this is handled by MS LINK.EXE. 1262 */ 1263 if (!gbMSComp) 1264 { 1265 /* Check whether the current export is not PRIVATE, or has an ordinal */ 1266 int bIsNotPrivate = (!gbNotPrivateNoWarn && /*gbImportLib &&*/ !(exp.uFlags & FL_PRIVATE)); 1267 int bHasOrdinal = (exp.uFlags & FL_ORDINAL); 1268 1269 /* Check whether the current export is an OLE export, in case any of these tests pass */ 1270 if (bIsNotPrivate || bHasOrdinal) 1271 { 1272 for (i = 0; i < ARRAYSIZE(astrOlePrivateExports); ++i) 1273 { 1274 if (strlen(astrOlePrivateExports[i]) == exp.strName.len && 1275 strncmp(exp.strName.buf, astrOlePrivateExports[i], exp.strName.len) == 0) 1276 { 1277 /* The current export is an OLE export: display the corresponding warning */ 1278 if (bIsNotPrivate) 1279 { 1280 fprintf(stderr, "WARNING: %s line %d: Exported symbol '%.*s' should be PRIVATE\n", 1281 pszSourceFileName, nLine, exp.strName.len, exp.strName.buf); 1282 } 1283 if (bHasOrdinal) 1284 { 1285 fprintf(stderr, "WARNING: %s line %d: exported symbol '%.*s' should not be assigned an ordinal\n", 1286 pszSourceFileName, nLine, exp.strName.len, exp.strName.buf); 1287 } 1288 break; 1289 } 1290 } 1291 } 1292 } 1293 1294 pexports[*cExports] = exp; 1295 (*cExports)++; 1296 gbDebug = 0; 1297 } 1298 1299 return pexports; 1300 } 1301 1302 void usage(void) 1303 { 1304 printf("syntax: spec2def [<options> ...] <spec file>\n" 1305 "Possible options:\n" 1306 " -h --help print this help screen\n" 1307 " -l=<file> generate an asm lib stub\n" 1308 " -d=<file> generate a def file\n" 1309 " -s=<file> generate a stub file\n" 1310 " --ms MSVC compatibility\n" 1311 " -n=<name> name of the dll\n" 1312 " --implib generate a def file for an import library\n" 1313 " --no-private-warnings suppress warnings about symbols that should be -private\n" 1314 " -a=<arch> set architecture to <arch> (i386, x86_64, arm)\n" 1315 " --with-tracing generate wine-like \"+relay\" trace trampolines (needs -s)\n"); 1316 } 1317 1318 int main(int argc, char *argv[]) 1319 { 1320 size_t nFileSize; 1321 char *pszSource, *pszDefFileName = NULL, *pszStubFileName = NULL, *pszLibStubName = NULL; 1322 const char* pszVersionOption = "--version=0x"; 1323 char achDllName[40]; 1324 FILE *file; 1325 unsigned cExports = 0, i; 1326 EXPORT *pexports; 1327 1328 if (argc < 2) 1329 { 1330 usage(); 1331 return -1; 1332 } 1333 1334 /* Read options */ 1335 for (i = 1; i < (unsigned)argc && *argv[i] == '-'; i++) 1336 { 1337 if ((strcasecmp(argv[i], "--help") == 0) || 1338 (strcasecmp(argv[i], "-h") == 0)) 1339 { 1340 usage(); 1341 return 0; 1342 } 1343 else if (argv[i][1] == 'd' && argv[i][2] == '=') 1344 { 1345 pszDefFileName = argv[i] + 3; 1346 } 1347 else if (argv[i][1] == 'l' && argv[i][2] == '=') 1348 { 1349 pszLibStubName = argv[i] + 3; 1350 } 1351 else if (argv[i][1] == 's' && argv[i][2] == '=') 1352 { 1353 pszStubFileName = argv[i] + 3; 1354 } 1355 else if (argv[i][1] == 'n' && argv[i][2] == '=') 1356 { 1357 pszDllName = argv[i] + 3; 1358 } 1359 else if (strncasecmp(argv[i], pszVersionOption, strlen(pszVersionOption)) == 0) 1360 { 1361 guOsVersion = strtoul(argv[i] + strlen(pszVersionOption), NULL, 16); 1362 } 1363 else if (strcasecmp(argv[i], "--implib") == 0) 1364 { 1365 gbImportLib = 1; 1366 } 1367 else if (strcasecmp(argv[i], "--ms") == 0) 1368 { 1369 gbMSComp = 1; 1370 } 1371 else if (strcasecmp(argv[i], "--no-private-warnings") == 0) 1372 { 1373 gbNotPrivateNoWarn = 1; 1374 } 1375 else if (strcasecmp(argv[i], "--with-tracing") == 0) 1376 { 1377 if (!pszStubFileName) 1378 { 1379 fprintf(stderr, "Error: cannot use --with-tracing without -s option.\n"); 1380 return -1; 1381 } 1382 gbTracing = 1; 1383 } 1384 else if (argv[i][1] == 'a' && argv[i][2] == '=') 1385 { 1386 pszArchString = argv[i] + 3; 1387 } 1388 else 1389 { 1390 fprintf(stderr, "Unrecognized option: %s\n", argv[i]); 1391 return -1; 1392 } 1393 } 1394 1395 if (strcasecmp(pszArchString, "i386") == 0) 1396 { 1397 giArch = ARCH_X86; 1398 gpszUnderscore = "_"; 1399 } 1400 else if (strcasecmp(pszArchString, "x86_64") == 0) giArch = ARCH_AMD64; 1401 else if (strcasecmp(pszArchString, "ia64") == 0) giArch = ARCH_IA64; 1402 else if (strcasecmp(pszArchString, "arm") == 0) giArch = ARCH_ARM; 1403 else if (strcasecmp(pszArchString, "ppc") == 0) giArch = ARCH_PPC; 1404 1405 if ((giArch == ARCH_AMD64) || (giArch == ARCH_IA64)) 1406 { 1407 pszArchString2 = "win64"; 1408 } 1409 else 1410 pszArchString2 = "win32"; 1411 1412 /* Set a default dll name */ 1413 if (!pszDllName) 1414 { 1415 char *p1, *p2; 1416 size_t len; 1417 1418 p1 = strrchr(argv[i], '\\'); 1419 if (!p1) p1 = strrchr(argv[i], '/'); 1420 p2 = p1 = p1 ? p1 + 1 : argv[i]; 1421 1422 /* walk up to '.' */ 1423 while (*p2 != '.' && *p2 != 0) p2++; 1424 len = p2 - p1; 1425 if (len >= sizeof(achDllName) - 5) 1426 { 1427 fprintf(stderr, "name too long: %s\n", p1); 1428 return -2; 1429 } 1430 1431 strncpy(achDllName, p1, len); 1432 strncpy(achDllName + len, ".dll", sizeof(achDllName) - len); 1433 pszDllName = achDllName; 1434 } 1435 1436 /* Open input file */ 1437 pszSourceFileName = argv[i]; 1438 file = fopen(pszSourceFileName, "r"); 1439 if (!file) 1440 { 1441 fprintf(stderr, "error: could not open file %s\n", pszSourceFileName); 1442 return -3; 1443 } 1444 1445 /* Get file size */ 1446 fseek(file, 0, SEEK_END); 1447 nFileSize = ftell(file); 1448 rewind(file); 1449 1450 /* Allocate memory buffer */ 1451 pszSource = malloc(nFileSize + 1); 1452 if (!pszSource) 1453 { 1454 fclose(file); 1455 return -4; 1456 } 1457 1458 /* Load input file into memory */ 1459 nFileSize = fread(pszSource, 1, nFileSize, file); 1460 fclose(file); 1461 1462 /* Zero terminate the source */ 1463 pszSource[nFileSize] = '\0'; 1464 1465 pexports = ParseFile(pszSource, file, &cExports); 1466 if (pexports == NULL) 1467 { 1468 fprintf(stderr, "Failed to allocate memory for export data!\n"); 1469 return -1; 1470 } 1471 1472 if (pszDefFileName) 1473 { 1474 /* Open output file */ 1475 file = fopen(pszDefFileName, "w"); 1476 if (!file) 1477 { 1478 fprintf(stderr, "error: could not open output file %s\n", argv[i + 1]); 1479 return -5; 1480 } 1481 1482 OutputHeader_def(file, pszDllName); 1483 1484 for (i = 0; i < cExports; i++) 1485 { 1486 if (pexports[i].bVersionIncluded) 1487 OutputLine_def(file, &pexports[i]); 1488 } 1489 1490 fclose(file); 1491 } 1492 1493 if (pszStubFileName) 1494 { 1495 /* Open output file */ 1496 file = fopen(pszStubFileName, "w"); 1497 if (!file) 1498 { 1499 fprintf(stderr, "error: could not open output file %s\n", argv[i + 1]); 1500 return -5; 1501 } 1502 1503 OutputHeader_stub(file); 1504 1505 for (i = 0; i < cExports; i++) 1506 { 1507 if (pexports[i].bVersionIncluded) 1508 OutputLine_stub(file, &pexports[i]); 1509 } 1510 1511 fclose(file); 1512 } 1513 1514 if (pszLibStubName) 1515 { 1516 /* Open output file */ 1517 file = fopen(pszLibStubName, "w"); 1518 if (!file) 1519 { 1520 fprintf(stderr, "error: could not open output file %s\n", argv[i + 1]); 1521 return -5; 1522 } 1523 1524 OutputHeader_asmstub(file, pszDllName); 1525 1526 for (i = 0; i < cExports; i++) 1527 { 1528 if (pexports[i].bVersionIncluded) 1529 OutputLine_asmstub(file, &pexports[i]); 1530 } 1531 1532 fprintf(file, "\n END\n"); 1533 fclose(file); 1534 } 1535 1536 free(pexports); 1537 1538 return 0; 1539 } 1540