| 1 | /* $Id: emxomfstrip.c 3940 2014-11-10 20:16:06Z bird $ */
|
|---|
| 2 | /** @file
|
|---|
| 3 | * emxstrip - Simple LX debug info stripping tool.
|
|---|
| 4 | *
|
|---|
| 5 | * @copyright Copyright (C) 2014 knut st. osmundsen <bird-klibc-spam-xiv@anduin.net>
|
|---|
| 6 | * @licenses MIT, BSD2, BSD3, BSD4, LGPLv2.1, LGPLv3, LGPLvFuture.
|
|---|
| 7 | */
|
|---|
| 8 |
|
|---|
| 9 |
|
|---|
| 10 | /*******************************************************************************
|
|---|
| 11 | * Header Files *
|
|---|
| 12 | *******************************************************************************/
|
|---|
| 13 | #include "defs.h"
|
|---|
| 14 | #include <errno.h>
|
|---|
| 15 | #include <getopt.h>
|
|---|
| 16 | #include <stdarg.h>
|
|---|
| 17 | #include <stdio.h>
|
|---|
| 18 | #include <stdlib.h>
|
|---|
| 19 | #include <string.h>
|
|---|
| 20 | #include <sys/stat.h>
|
|---|
| 21 | #include <sys/omflib.h>
|
|---|
| 22 | #if defined(_MSC_VER) || defined(__WATCOMC__)
|
|---|
| 23 | # include <io.h>
|
|---|
| 24 | # define ftruncate _chsize
|
|---|
| 25 | #endif
|
|---|
| 26 |
|
|---|
| 27 |
|
|---|
| 28 |
|
|---|
| 29 | /*******************************************************************************
|
|---|
| 30 | * Structures and Typedefs *
|
|---|
| 31 | *******************************************************************************/
|
|---|
| 32 | typedef struct EMXOMFSTRIPOPTS
|
|---|
| 33 | {
|
|---|
| 34 | const char *pszInFile;
|
|---|
| 35 | const char *pszDbgFile;
|
|---|
| 36 | const char *pszOutFile;
|
|---|
| 37 | int cVerbose;
|
|---|
| 38 | } EMXOMFSTRIPOPTS;
|
|---|
| 39 |
|
|---|
| 40 |
|
|---|
| 41 | static int error(const char *pszMsg, ...)
|
|---|
| 42 | {
|
|---|
| 43 | va_list va;
|
|---|
| 44 | va_start(va, pszMsg);
|
|---|
| 45 | fprintf(stderr, "emxomfstrip: error: ");
|
|---|
| 46 | vfprintf(stderr, pszMsg, va);
|
|---|
| 47 | va_end(va);
|
|---|
| 48 | return 1;
|
|---|
| 49 | }
|
|---|
| 50 |
|
|---|
| 51 |
|
|---|
| 52 | static int copyFileBytes(FILE *pDst, const char *pszDstName, FILE *pSrc, const char *pszSrcName,
|
|---|
| 53 | unsigned long offSrc, unsigned long cb)
|
|---|
| 54 | {
|
|---|
| 55 | if (fseek(pDst, 0, SEEK_SET) != 0)
|
|---|
| 56 | return error("Seek error: %s\n", strerror(errno));
|
|---|
| 57 | if (fseek(pSrc, offSrc, SEEK_SET) != 0)
|
|---|
| 58 | return error("Seek error: %s\n", strerror(errno));
|
|---|
| 59 | while (cb > 0)
|
|---|
| 60 | {
|
|---|
| 61 | byte abBuf[0x8000];
|
|---|
| 62 | unsigned cbThis = cb < sizeof(abBuf) ? (unsigned)cb : sizeof(abBuf);
|
|---|
| 63 | if (fread(abBuf, cbThis, 1, pSrc) != 1)
|
|---|
| 64 | return error("fread (@%#lx) failed on '%s': %s\n", offSrc, pszSrcName, strerror(errno));
|
|---|
| 65 | if (fwrite(abBuf, cbThis, 1, pDst) != 1)
|
|---|
| 66 | return error("fwrite failed on '%s': %s\n", pszDstName, strerror(errno));
|
|---|
| 67 | cb -= cbThis;
|
|---|
| 68 | offSrc += cbThis;
|
|---|
| 69 | }
|
|---|
| 70 | return 0;
|
|---|
| 71 | }
|
|---|
| 72 |
|
|---|
| 73 |
|
|---|
| 74 | static int isNbSignature(byte const *pbMagic)
|
|---|
| 75 | {
|
|---|
| 76 | return pbMagic[0] == 'N'
|
|---|
| 77 | && pbMagic[1] == 'B'
|
|---|
| 78 | && pbMagic[2] >= '0' && pbMagic[2] <= '1'
|
|---|
| 79 | && pbMagic[3] >= '0' && pbMagic[3] <= '9';
|
|---|
| 80 | }
|
|---|
| 81 |
|
|---|
| 82 |
|
|---|
| 83 | static int isLxDebugInfoSignature(byte const *pbMagic)
|
|---|
| 84 | {
|
|---|
| 85 | return isNbSignature(pbMagic);
|
|---|
| 86 | }
|
|---|
| 87 |
|
|---|
| 88 |
|
|---|
| 89 | /**
|
|---|
| 90 | * Strips a file.
|
|---|
| 91 | *
|
|---|
| 92 | * @returns 0 on success, 1 on failure, -1 if no debug info.
|
|---|
| 93 | * @param pOpts Options.
|
|---|
| 94 | * @param pInFile The input file.
|
|---|
| 95 | * @param pOutFile The output file. Can be the same as @a pInFile, can
|
|---|
| 96 | * also be NULL (no stripping).
|
|---|
| 97 | * @param pDbgFile Where to write the raw debug.
|
|---|
| 98 | */
|
|---|
| 99 | static int stripFile(EMXOMFSTRIPOPTS *pOpts, FILE *pInFile, FILE *pOutFile, FILE *pDbgFile)
|
|---|
| 100 | {
|
|---|
| 101 | struct stat StIn;
|
|---|
| 102 | unsigned long offLX = 0;
|
|---|
| 103 | unsigned long offDbg = 0;
|
|---|
| 104 | unsigned long cbDbg = 0;
|
|---|
| 105 | int rc;
|
|---|
| 106 | union
|
|---|
| 107 | {
|
|---|
| 108 | struct exe1p2_header mz;
|
|---|
| 109 | struct os2_header lx;
|
|---|
| 110 | byte abBuf[32];
|
|---|
| 111 | } uHdr;
|
|---|
| 112 | union
|
|---|
| 113 | {
|
|---|
| 114 | byte abBuf[32];
|
|---|
| 115 | word awBuf[32/2];
|
|---|
| 116 | dword adwBuf[32/4];
|
|---|
| 117 | } uBuf;
|
|---|
| 118 |
|
|---|
| 119 | if (fstat(fileno(pInFile), &StIn) != 0)
|
|---|
| 120 | return error("fstat failed on '%s': %s\n", pOpts->pszInFile, strerror(errno));
|
|---|
| 121 |
|
|---|
| 122 | /*
|
|---|
| 123 | * Parse the executable header.
|
|---|
| 124 | */
|
|---|
| 125 | if (fread(&uHdr.mz, sizeof(uHdr.mz), 1, pInFile) != 1)
|
|---|
| 126 | return error("Failed to read the header of '%s': %s\n", pOpts->pszInFile, strerror(errno));
|
|---|
| 127 | if (uHdr.mz.e_magic == EXE_MAGIC_MZ)
|
|---|
| 128 | {
|
|---|
| 129 | offLX = uHdr.mz.e_lfanew;
|
|---|
| 130 | if (fseek(pInFile, offLX, SEEK_SET) != 0)
|
|---|
| 131 | return error("Failed to seek to 2nd header of '%s': %s\n", pOpts->pszInFile, strerror(errno));
|
|---|
| 132 | if (fread(&uHdr.lx, sizeof(uHdr.lx), 1, pInFile) != 1)
|
|---|
| 133 | return error("Failed to read the LX header of '%s': %s\n", pOpts->pszInFile, strerror(errno));
|
|---|
| 134 | if (uHdr.lx.magic != EXE_MAGIC_LX)
|
|---|
| 135 | return error("Header of '%s' is not recognized (%#x)\n", pOpts->pszInFile, uHdr.lx.magic);
|
|---|
| 136 | }
|
|---|
| 137 | else if (uHdr.lx.magic == EXE_MAGIC_LX)
|
|---|
| 138 | {
|
|---|
| 139 | if (fread(&uHdr.mz + 1, sizeof(uHdr.lx) - sizeof(uHdr.mz), 1, pInFile) != 1)
|
|---|
| 140 | return error("Failed to read the header of '%s': %s\n", pOpts->pszInFile, strerror(errno));
|
|---|
| 141 | }
|
|---|
| 142 | else if ( uHdr.abBuf[0] == THEADR
|
|---|
| 143 | && (uHdr.abBuf[1] > 3 || uHdr.abBuf[2]) )
|
|---|
| 144 | return error("%s: Stripping OMF object files is not currently supported!\n", pOpts->pszInFile);
|
|---|
| 145 | else if ( uHdr.abBuf[0] == LIBHDR
|
|---|
| 146 | && (uHdr.abBuf[1] > 3 || uHdr.abBuf[2]) )
|
|---|
| 147 | return error("%s: Stripping OMF library files is not currently supported!\n", pOpts->pszInFile);
|
|---|
| 148 | else
|
|---|
| 149 | return error("Header of '%s' is not recognized (%#x)\n", pOpts->pszInFile, uHdr.mz.e_magic);
|
|---|
| 150 |
|
|---|
| 151 | /*
|
|---|
| 152 | * Is there a pointer to the debug info in the header? If so, check that
|
|---|
| 153 | * it is at the end of the file.
|
|---|
| 154 | */
|
|---|
| 155 | if ( uHdr.lx.debug_offset != 0
|
|---|
| 156 | && uHdr.lx.debug_size != 0 && 0)
|
|---|
| 157 | {
|
|---|
| 158 | offDbg = uHdr.lx.debug_offset;
|
|---|
| 159 | cbDbg = uHdr.lx.debug_size;
|
|---|
| 160 | if ( offDbg > StIn.st_size
|
|---|
| 161 | || cbDbg > StIn.st_size
|
|---|
| 162 | || (off_t)offDbg + cbDbg > StIn.st_size)
|
|---|
| 163 | return error("Debug info indicated by the LX header of '%s' makes no sense! (%#lx + %#lx = %#lx (file: %#x)\n",
|
|---|
| 164 | pOpts->pszInFile, offDbg, cbDbg, offDbg + cbDbg, (unsigned long)StIn.st_size);
|
|---|
| 165 |
|
|---|
| 166 | if ( fseek(pInFile, offDbg, SEEK_SET) != 0
|
|---|
| 167 | || fread(&uBuf, 4, 1, pInFile) != 1)
|
|---|
| 168 | return error("Error seeking/reading debug info header (@%#lx) in '%s': %s\n",
|
|---|
| 169 | offDbg, pOpts->pszInFile, strerror(errno));
|
|---|
| 170 | if (!isLxDebugInfoSignature(uBuf.abBuf))
|
|---|
| 171 | {
|
|---|
| 172 | /* Try add the LX offset. */
|
|---|
| 173 | if ( fseek(pInFile, offDbg + offLX, SEEK_SET) == 0
|
|---|
| 174 | && fread(&uBuf.abBuf[4], 4, 1, pInFile) == 0
|
|---|
| 175 | && isLxDebugInfoSignature(&uBuf.abBuf[4]))
|
|---|
| 176 | offDbg += offLX;
|
|---|
| 177 | else
|
|---|
| 178 | return error("Debug info indicated by the LX header of '%s' is not recognized! (magic: %02x %02x %02x %02x)\n",
|
|---|
| 179 | pOpts->pszInFile, uBuf.abBuf[0], uBuf.abBuf[1], uBuf.abBuf[2], uBuf.abBuf[3]);
|
|---|
| 180 | }
|
|---|
| 181 |
|
|---|
| 182 | if (offDbg + cbDbg != StIn.st_size) /* (This can be relaxed if we wish.) */
|
|---|
| 183 | return error("Debug info indicated by the LX header is not at the end of '%s'! (%#lx + %#lx = %#lx (file: %#x)\n",
|
|---|
| 184 | pOpts->pszInFile, offDbg, cbDbg, offDbg + cbDbg, (unsigned long)StIn.st_size);
|
|---|
| 185 | }
|
|---|
| 186 | /*
|
|---|
| 187 | * Check for debug info at the end of the file.
|
|---|
| 188 | */
|
|---|
| 189 | else
|
|---|
| 190 | {
|
|---|
| 191 | if (fseek(pInFile, -8, SEEK_END) != 0)
|
|---|
| 192 | return error("Failed seeking in '%s': %s\n", pOpts->pszInFile, strerror(errno));
|
|---|
| 193 | if (fread(&uBuf, 8, 1, pInFile) != 1)
|
|---|
| 194 | return error("Error reading last 8 bytes of '%s': %s\n", pOpts->pszInFile, strerror(errno));
|
|---|
| 195 | if (isNbSignature(uBuf.abBuf))
|
|---|
| 196 | {
|
|---|
| 197 | int iVer = (uBuf.abBuf[2] - '0') * 10 + (uBuf.abBuf[3] - '0');
|
|---|
| 198 | if (iVer != 4 && iVer != 2)
|
|---|
| 199 | fprintf(stderr, "emxomfstrip: warning: Unknown debug info signature '%4.4s' in '%s'.\n",
|
|---|
| 200 | uBuf.abBuf, pOpts->pszInFile);
|
|---|
| 201 | cbDbg = uBuf.adwBuf[1];
|
|---|
| 202 | if ( cbDbg >= (unsigned long)StIn.st_size
|
|---|
| 203 | || cbDbg <= 16)
|
|---|
| 204 | return error("Bad size in NB trailer debug of '%s': %#x (file size %#lx)\n",
|
|---|
| 205 | pOpts->pszInFile, cbDbg, (unsigned long)StIn.st_size);
|
|---|
| 206 | offDbg = (unsigned long)StIn.st_size - cbDbg;
|
|---|
| 207 |
|
|---|
| 208 | if ( fseek(pInFile, offDbg, SEEK_SET) != 0
|
|---|
| 209 | || fread(&uBuf.abBuf[4], 4, 1, pInFile) != 1)
|
|---|
| 210 | return error("Error seeking/reading debug info header (@%#lx) in '%s': %s\n",
|
|---|
| 211 | offDbg, pOpts->pszInFile, strerror(errno));
|
|---|
| 212 | if (uBuf.adwBuf[0] != uBuf.adwBuf[1])
|
|---|
| 213 | return error("Debug header does not match trailer in '%s': %02x %02x %02x %02x != %02x %02x %02x %02x\n",
|
|---|
| 214 | pOpts->pszInFile,
|
|---|
| 215 | uBuf.abBuf[0], uBuf.abBuf[1], uBuf.abBuf[2], uBuf.abBuf[3],
|
|---|
| 216 | uBuf.abBuf[4+0], uBuf.abBuf[4+1], uBuf.abBuf[4+2], uBuf.abBuf[4+3]);
|
|---|
| 217 | }
|
|---|
| 218 | }
|
|---|
| 219 |
|
|---|
| 220 | /*
|
|---|
| 221 | * Did we find anything to strip? If not return immediately indicating this.
|
|---|
| 222 | */
|
|---|
| 223 | if (cbDbg == 0)
|
|---|
| 224 | return -1;
|
|---|
| 225 |
|
|---|
| 226 | /*
|
|---|
| 227 | * Do we copy the debug info into a separate file first?
|
|---|
| 228 | */
|
|---|
| 229 | rc = 0;
|
|---|
| 230 | if (pDbgFile)
|
|---|
| 231 | rc = copyFileBytes(pDbgFile, pOpts->pszDbgFile, pInFile, pOpts->pszInFile, offDbg, cbDbg);
|
|---|
| 232 |
|
|---|
| 233 | /*
|
|---|
| 234 | * If pOutFile is NULL, we're only supposed to extract the debug info.
|
|---|
| 235 | */
|
|---|
| 236 | if (rc == 0 && pOutFile != NULL)
|
|---|
| 237 | {
|
|---|
| 238 | /*
|
|---|
| 239 | * If there is a separate output file, copy over the bytes that we should keep.
|
|---|
| 240 | */
|
|---|
| 241 | if (pOutFile != pInFile)
|
|---|
| 242 | rc = copyFileBytes(pOutFile, pOpts->pszOutFile, pInFile, pOpts->pszInFile, 0, offDbg);
|
|---|
| 243 |
|
|---|
| 244 | /*
|
|---|
| 245 | * Update the LX header in the output(/input) file.
|
|---|
| 246 | */
|
|---|
| 247 | if ( rc == 0
|
|---|
| 248 | && ( uHdr.lx.debug_offset != 0
|
|---|
| 249 | || uHdr.lx.debug_size != 0))
|
|---|
| 250 | {
|
|---|
| 251 | uHdr.lx.debug_offset = 0;
|
|---|
| 252 | uHdr.lx.debug_size = 0;
|
|---|
| 253 | uHdr.lx.loader_checksum = 0;
|
|---|
| 254 | if (fseek(pOutFile, offLX, SEEK_SET) != 0)
|
|---|
| 255 | return error("Error seeking to LX header in output file: %s\n", strerror(errno));
|
|---|
| 256 | if (fwrite(&uHdr.lx, sizeof(uHdr.lx), 1, pOutFile) != 1)
|
|---|
| 257 | return error("Error writing LX header of output file: %s\n", strerror(errno));
|
|---|
| 258 | }
|
|---|
| 259 |
|
|---|
| 260 | /*
|
|---|
| 261 | * Truncate the output file if it's the same as the input.
|
|---|
| 262 | */
|
|---|
| 263 | if (rc == 0 && pOutFile == pInFile)
|
|---|
| 264 | {
|
|---|
| 265 | if (ftruncate(fileno(pOutFile), offDbg) != 0)
|
|---|
| 266 | return error("Error truncating '%s': %s\n", pOpts->pszInFile, strerror(errno));
|
|---|
| 267 | }
|
|---|
| 268 | }
|
|---|
| 269 |
|
|---|
| 270 | return rc;
|
|---|
| 271 | }
|
|---|
| 272 |
|
|---|
| 273 |
|
|---|
| 274 | static int usage(FILE *pOut, int rc)
|
|---|
| 275 | {
|
|---|
| 276 | fprintf(pOut, "emxomfstrip [-dgsSxXvV] [-D dbginfo.file] [-o output.file] <file1> [file2...]\n");
|
|---|
| 277 | return rc;
|
|---|
| 278 | }
|
|---|
| 279 |
|
|---|
| 280 |
|
|---|
| 281 | int main(int argc, char **argv)
|
|---|
| 282 | {
|
|---|
| 283 | int ch;
|
|---|
| 284 | int rcExit = 0;
|
|---|
| 285 | EMXOMFSTRIPOPTS Opts = { NULL, NULL, NULL, 0 } ;
|
|---|
| 286 |
|
|---|
| 287 | #ifdef __EMX__
|
|---|
| 288 | /* Preload the executable. */
|
|---|
| 289 | _emxload_env("GCCLOAD");
|
|---|
| 290 | #endif
|
|---|
| 291 |
|
|---|
| 292 | /* Get more arguments from EMXOMFSTRIPOPT and load reponse files and expand wildcards. */
|
|---|
| 293 | #ifdef __EMX__
|
|---|
| 294 | _envargs(&argc, &argv, "EMXOMFSTRIPOPT");
|
|---|
| 295 | _response(&argc, &argv);
|
|---|
| 296 | _wildcard(&argc, &argv);
|
|---|
| 297 | #endif
|
|---|
| 298 |
|
|---|
| 299 | /*
|
|---|
| 300 | * Parse the options.
|
|---|
| 301 | */
|
|---|
| 302 | while ((ch = getopt_long(argc, argv, "dD:go:sSxXvV", NULL, NULL)) != EOF)
|
|---|
| 303 | {
|
|---|
| 304 | switch (ch)
|
|---|
| 305 | {
|
|---|
| 306 | case 'd':
|
|---|
| 307 | case 'g':
|
|---|
| 308 | case 's':
|
|---|
| 309 | case 'S':
|
|---|
| 310 | case 'x':
|
|---|
| 311 | case 'X':
|
|---|
| 312 | break;
|
|---|
| 313 |
|
|---|
| 314 | case 'D':
|
|---|
| 315 | Opts.pszDbgFile = optarg;
|
|---|
| 316 | break;
|
|---|
| 317 |
|
|---|
| 318 | case 'o':
|
|---|
| 319 | Opts.pszOutFile = optarg;
|
|---|
| 320 | break;
|
|---|
| 321 |
|
|---|
| 322 | case 'v':
|
|---|
| 323 | Opts.cVerbose++;
|
|---|
| 324 | break;
|
|---|
| 325 |
|
|---|
| 326 | case 'V':
|
|---|
| 327 | printf(VERSION INNOTEK_VERSION "\n");
|
|---|
| 328 | return 0;
|
|---|
| 329 |
|
|---|
| 330 | default:
|
|---|
| 331 | return usage(stderr, 1);
|
|---|
| 332 | }
|
|---|
| 333 | }
|
|---|
| 334 |
|
|---|
| 335 | if (argc - optind == 0)
|
|---|
| 336 | return usage(stderr, 1);
|
|---|
| 337 | if ( argc - optind != 1
|
|---|
| 338 | && (Opts.pszDbgFile || Opts.pszOutFile))
|
|---|
| 339 | return error("exactly one file must be specified if -D or -o is used\n");
|
|---|
| 340 |
|
|---|
| 341 | /*
|
|---|
| 342 | * Process the files.
|
|---|
| 343 | */
|
|---|
| 344 | while ( optind < argc
|
|---|
| 345 | && rcExit == 0)
|
|---|
| 346 | {
|
|---|
| 347 | /* Open the input and maybe output file. */
|
|---|
| 348 | FILE *pInFile;
|
|---|
| 349 | Opts.pszInFile = argv[optind];
|
|---|
| 350 | if (Opts.pszOutFile)
|
|---|
| 351 | pInFile = fopen(Opts.pszInFile, "rb");
|
|---|
| 352 | else
|
|---|
| 353 | pInFile = fopen(Opts.pszInFile, "r+b");
|
|---|
| 354 | if (pInFile)
|
|---|
| 355 | {
|
|---|
| 356 | /* Open the output file if it differs. Special handling of /dev/null
|
|---|
| 357 | for facilitaing only extracting the debug info. */
|
|---|
| 358 | int fNull = 0;
|
|---|
| 359 | FILE *pOutFile;
|
|---|
| 360 | if (!Opts.pszOutFile)
|
|---|
| 361 | pOutFile = pInFile;
|
|---|
| 362 | else
|
|---|
| 363 | {
|
|---|
| 364 | if (strcmp(Opts.pszOutFile, "/dev/null"))
|
|---|
| 365 | pOutFile = fopen(Opts.pszOutFile, "wb");
|
|---|
| 366 | else
|
|---|
| 367 | {
|
|---|
| 368 | fNull = 1;
|
|---|
| 369 | pOutFile = NULL;
|
|---|
| 370 | }
|
|---|
| 371 | }
|
|---|
| 372 | if (pOutFile || fNull)
|
|---|
| 373 | {
|
|---|
| 374 | /* Open the debug file. */
|
|---|
| 375 | FILE *pDbgFile = NULL;
|
|---|
| 376 | if (Opts.pszDbgFile)
|
|---|
| 377 | pDbgFile = fopen(Opts.pszDbgFile, "wb");
|
|---|
| 378 | if (pDbgFile || !Opts.pszDbgFile)
|
|---|
| 379 | {
|
|---|
| 380 | /*
|
|---|
| 381 | * All the files have been opened, get down to business.
|
|---|
| 382 | */
|
|---|
| 383 | int rc = stripFile(&Opts, pInFile, pOutFile, pDbgFile);
|
|---|
| 384 | if (rc != 0 && rc != -1)
|
|---|
| 385 | rcExit = rc;
|
|---|
| 386 |
|
|---|
| 387 | /*
|
|---|
| 388 | * Close the files. The user can clean up files on
|
|---|
| 389 | * failure, at least for now.
|
|---|
| 390 | */
|
|---|
| 391 | if (pDbgFile && fclose(pDbgFile) != 0)
|
|---|
| 392 | rcExit = error("fclose failed on '%s': %s\n", Opts.pszDbgFile, strerror(errno));
|
|---|
| 393 | else if (rc == -1 && Opts.pszDbgFile != NULL && unlink(Opts.pszDbgFile) != 0)
|
|---|
| 394 | rcExit = error("Failed to unlink debug info file '%s': %s\n", Opts.pszDbgFile, strerror(errno));
|
|---|
| 395 | }
|
|---|
| 396 | else
|
|---|
| 397 | rcExit = error("Failed opening debug info file '%s': %s\n", Opts.pszDbgFile, strerror(errno));
|
|---|
| 398 | if (pOutFile && pOutFile != pInFile && fclose(pOutFile) != 0)
|
|---|
| 399 | rcExit = error("fclose failed on '%s': %s\n", Opts.pszOutFile, strerror(errno));
|
|---|
| 400 | }
|
|---|
| 401 | else
|
|---|
| 402 | rcExit = error("Failed opening output file '%s': %s\n", Opts.pszOutFile, strerror(errno));
|
|---|
| 403 | if (fclose(pInFile) != 0)
|
|---|
| 404 | rcExit = error("fclose failed on '%s': %s\n", Opts.pszInFile, strerror(errno));
|
|---|
| 405 | }
|
|---|
| 406 | else
|
|---|
| 407 | rcExit = error("Failed opening '%s': %s\n", Opts.pszInFile, strerror(errno));
|
|---|
| 408 | optind++;
|
|---|
| 409 | }
|
|---|
| 410 |
|
|---|
| 411 | return rcExit;
|
|---|
| 412 | }
|
|---|
| 413 |
|
|---|