/* OLEdecode - Decode Microsoft OLE files into its components. Copyright (C) 1998, 1999 Andrew Scriven This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Released under GPL, written by Andrew Scriven Copyright (C) 1998, 1999 Andrew Scriven */ /* ----------------------------------------------------------------------- Andrew Scriven Research and Engineering Electron Building, Windmill Hill, Whitehill Way, Swindon, SN5 6PB, UK Phone (44) 1793 896206, Fax (44) 1793 896251 ----------------------------------------------------------------------- */ /* *Extremely* modified by Arturo Tena */ #include #include #include #include #include #include #include #if !(defined( __BORLANDC__ ) || defined( __WIN32__ )) #include "cole.h" #include "config.h" #include /* for unlink() */ #else #include "cole.h.in" #endif /* FIXME: replace all VERBOSE with COLE_VERBOSE */ #ifdef COLE_VERBOSE #define VERBOSE #else #undef VERBOSE #endif #include "support.h" #include "internal.h" #define ENTRYCHUNK 20 /* number of entries in root_list and sbd_list will be added each time. must be at least 1 */ #define MIN(a,b) ((a)<(b) ? (a) : (b)) /* reorder pps tree, from tree structure to a linear one, and write the level numbers, returns zero if OLE format fails, returns no zero if success */ static int reorder_pps_tree (pps_entry * root_pps, U16 level); /* free memory used (except the pps tree) */ static void ends (void); /* close and remove files in the tree */ /* closeOLEtreefiles --- outdated because not to generate the real files by now --- cole 2.0.0 */ /* static void closeOLEtreefiles (pps_entry * tree, U32 root); */ /* Verbose pps tree. Input: pps_list: stream list. root_pps: root pps. level: how much levels will be extracted. Output: none. */ static void verbosePPSTree (pps_entry * pps_list, U32 root_pps, int level); static FILE *input; static U8 *Block; static U8 *Blockx; static U8 *BDepot, *SDepot, *Root; static pps_entry *pps_list; static U32 num_of_pps; static FILE *sbfile; /* sbfilename is stored in *_sbfilename instead -- cole 2.0.0 */ /* static char sbfilename[L_tmpnam]; */ static U32 *sbd_list; static U32 *root_list; int __OLEdecode (char *OLEfilename, pps_entry ** stream_list, U32 * root, U8 **_BDepot, U8 **_SDepot, FILE **_sbfile, char **_sbfilename, FILE **_input, U16 max_level) { int c; U32 num_bbd_blocks; U32 num_xbbd_blocks; U32 bl; U32 i, j, len; U8 *s, *p, *t; long FilePos; /* FilePos is long, not U32, because second argument of fseek is long */ /* initialize static variables */ input = sbfile = NULL; Block = Blockx = BDepot = SDepot = Root = NULL; pps_list = NULL; num_of_pps = 0; /* sbfilename is stored in *_sbfilename instead -- cole 2.0.0 */ /* sbfilename[0] = 0; */ root_list = sbd_list = NULL; /* initalize return parameters */ *stream_list = NULL; /* open input file */ verbose ("open input file"); input = fopen (OLEfilename, "rb"); test_exitf (input != NULL, 4, ends ()); *_input = input; /* fast check type of file */ verbose ("fast testing type of file"); test_exitf ((c = getc (input)) != EOF, 5, ends ()); test_exitf (ungetc (c, input) != EOF, 5, ends ()); /* test_exitf (!isprint (c), 8, ends ()); OpenBSD suggestion to comment this out */ test_exitf (c == 0xd0, 9, ends ()); /* read header block */ verbose ("read header block"); Block = (U8 *) malloc (0x0200); test_exitf (Block != NULL, 10, ends ()); fread (Block, 0x0200, 1, input); test_exitf (!ferror (input), 5, ends ()); /* really check type of file */ rewind (input); verbose ("testing type of file"); test_exitf (fil_sreadU32 (Block) != 0xd0cf11e0UL, 9, ends ()); test_exitf (fil_sreadU32 (Block + 0x04) != 0xa1b11ae1UL, 9, ends ()); /* read big block depot */ verbose ("read big block depot (bbd)"); num_bbd_blocks = fil_sreadU32 (Block + 0x2c); num_xbbd_blocks = fil_sreadU32 (Block + 0x48); verboseU32 (num_bbd_blocks); verboseU32 (num_xbbd_blocks); BDepot = (U8 *) malloc (0x0200 * (num_bbd_blocks + num_xbbd_blocks)); test_exitf (BDepot != NULL, 10, ends ()); *_BDepot = BDepot; s = BDepot; assert (num_bbd_blocks <= (0x0200 / 4 - 1) * num_xbbd_blocks + (0x0200 / 4) - 19); /* the first 19 U32 in header does not belong to bbd_list */ for (i = 0; i < MIN (num_bbd_blocks, 0x0200 / 4 - 19); i++) { /* note: next line may be needed to be cast to long in right side */ FilePos = 0x0200 * (1 + fil_sreadU32 (Block + 0x4c + (i * 4))); assert (FilePos >= 0); test_exitf (!fseek (input, FilePos, SEEK_SET), 5, ends ()); fread (s, 0x0200, 1, input); test_exitf (!ferror (input), 5, ends ()); s += 0x0200; } Blockx = (U8 *) malloc (0x0200); test_exitf (Blockx != NULL, 10, ends ()); bl = fil_sreadU32 (Block + 0x44); for (i = 0; i < num_xbbd_blocks; i++) { FilePos = 0x0200 * (1 + bl); assert (FilePos >= 0); test_exitf (!fseek (input, FilePos, SEEK_SET), 5, ends ()); fread (Blockx, 0x0200, 1, input); test_exitf (!ferror (input), 5, ends ()); for (j=0; j < 0x0200 / 4 - 1;j++) /* last U32 is for the next bl */ { if (fil_sreadU32 (Blockx + (j * 4)) == 0xfffffffeUL || fil_sreadU32 (Blockx + (j * 4)) == 0xfffffffdUL || fil_sreadU32 (Blockx + (j * 4)) == 0xffffffffUL) break; /* note: next line may be needed to be cast to long in right side */ FilePos = 0x0200 * (1 + fil_sreadU32 (Blockx + (j * 4))); assert (FilePos >= 0); test_exitf (!fseek (input, FilePos, SEEK_SET), 5, ends ()); fread (s, 0x0200, 1, input); test_exitf (!ferror (input), 5, ends ()); s += 0x0200; } bl = fil_sreadU32 (Blockx + 0x0200 - 4); } verboseU8Array (BDepot, (num_bbd_blocks+num_xbbd_blocks), 0x0200); /* extract the sbd block list */ verbose ("extract small block depot (sbd) block list"); sbd_list = (U32 *) malloc (ENTRYCHUNK * 4); test_exitf (sbd_list != NULL, 10, ends ()); sbd_list[0] = fil_sreadU32 (Block + 0x3c); /* -2 signed long int == 0xfffffffe unsinged long int */ for (len = 1; sbd_list[len - 1] != 0xfffffffeUL; len++) { test_exitf (len != 0, 5, ends ()); /* means file is too big */ /* if memory allocated in sbd_list is all used, allocate more memory */ if (!(len % ENTRYCHUNK)) { U32 *newspace; newspace = realloc (sbd_list, (1 + len / ENTRYCHUNK) * ENTRYCHUNK * 4); test_exitf (newspace != NULL, 10, ends ()); sbd_list = newspace; } sbd_list[len] = fil_sreadU32 (BDepot + (sbd_list[len - 1] * 4)); /*verboseU32 (len);*/ /*verboseU32 (sbd_list[0]);*/ /*verboseU32 (sbd_list[1]);*/ if (sbd_list[len] != 0xfffffffeUL) test_exitf (sbd_list[len] <= num_bbd_blocks * 0x0200 - 4, 5, ends ()); test_exitf (sbd_list[len] != 0xfffffffdUL && sbd_list[len] != 0xffffffffUL, 5, ends ()); } len--; verboseU32Array (sbd_list, len+1); /* read in small block depot, if there's any small block */ if (len == 0) { SDepot = NULL; verbose ("not read small block depot (sbd): there's no small blocks"); } else { verbose ("read small block depot (sbd)"); SDepot = (U8 *) malloc (0x0200 * len); test_exitf (SDepot != NULL, 10, ends ()); s = SDepot; for (i = 0; i < len; i++) { FilePos = 0x0200 * (1 + sbd_list[i]); assert (FilePos >= 0); test_exitf (!fseek (input, FilePos, SEEK_SET), 5, ends ()); fread (s, 0x0200, 1, input); test_exitf (!ferror (input), 5, ends ()); s += 0x200; } verboseU8Array (SDepot, len, 0x0200); } *_SDepot = SDepot; /* extract the root block list */ verbose ("extract root block depot (root) block list"); root_list = (U32 *) malloc (ENTRYCHUNK * 4); test_exitf (root_list != NULL, 10, ends ()); root_list[0] = fil_sreadU32 (Block + 0x30); for (len = 1; root_list[len - 1] != 0xfffffffeUL; len++) { test_exitf (len != 0, 5, ends ()); /* means file is too long */ /* if memory allocated in root_list is all used, allocate more memory */ if (!(len % ENTRYCHUNK)) { U32 *newspace; newspace = realloc (root_list, (1 + len / ENTRYCHUNK) * ENTRYCHUNK * 4); test_exitf (newspace != NULL, 10, ends ()); root_list = newspace; } root_list[len] = fil_sreadU32 (BDepot + (root_list[len - 1] * 4)); test_exitf (root_list[len] != 0xfffffffdUL && root_list[len] != 0xffffffffUL, 5, ends ()); } len--; verboseU32Array (root_list, len+1); /* read in root block depot */ verbose ("read in root block depot (Root)"); Root = (U8 *) malloc (0x0200 * len); test_exitf (Root != NULL, 10, ends ()); s = Root; for (i = 0; i < len; i++) { FilePos = 0x0200 * (root_list[i] + 1); assert (FilePos >= 0); test_exitf (!fseek (input, FilePos, SEEK_SET), 5, ends ()); fread (s, 0x0200, 1, input); test_exitf (!ferror (input), 5, ends ()); s += 0x200; } verboseU8Array (Root, len, 0x0200); /* assign space for pps list */ verbose ("read pps list"); num_of_pps = len * 4; /* each sbd block have 4 pps */ *stream_list = pps_list = (pps_entry *)malloc(num_of_pps*sizeof(pps_entry)); test_exitf (pps_list != NULL, 10, ends ()); /* read pss entry details and look out for "Root Entry" */ verbose ("read pps entry details"); for (i = 0; i < num_of_pps; i++) { U16 size_of_name; s = Root + (i * 0x80); /* read the number */ pps_list[i].ppsnumber = i; /* read the name */ size_of_name = (U16)MIN (0x40, fil_sreadU16 (s + 0x40)); pps_list[i].name[0] = 0; if (size_of_name == 0) continue; for (p = (U8 *) pps_list[i].name, t = s; t < s + size_of_name; t++) *p++ = *t++; /* makes visible the non printable first character */ /* if (!isprint (pps_list[i].name[0]) && pps_list[i].name[0]) pps_list[i].name[0] += 'a'; */ /* read the pps type */ pps_list[i].type = *(s + 0x42); if (pps_list[i].type == 5) { assert (i == 0); *root = i; /* this pps is the root */ } /* read the others fields */ pps_list[i].previous = fil_sreadU32 (s + 0x44); pps_list[i].next = fil_sreadU32 (s + 0x48); pps_list[i].dir = fil_sreadU32 (s + 0x4c); pps_list[i].start = fil_sreadU32 (s + 0x74); pps_list[i].size = fil_sreadU32 (s + 0x78); pps_list[i].seconds1 = fil_sreadU32 (s + 0x64); pps_list[i].seconds2 = fil_sreadU32 (s + 0x6c); pps_list[i].days1 = fil_sreadU32 (s + 0x68); pps_list[i].days2 = fil_sreadU32 (s + 0x70); } /* NEXT IS VERBOSE verbose */ #ifdef VERBOSE { U32 i; printf ("before reorder pps tree\n"); printf ("pps type prev next dir start level size name\n"); for (i = 0; i < num_of_pps; i++) { if (!pps_list[i].name[0]) { printf (" -\n"); continue; } printf ("%08lx ", pps_list[i].ppsnumber); printf ("%d ", pps_list[i].type); printf ("%08lx ", pps_list[i].previous); printf ("%08lx ", pps_list[i].next); printf ("%08lx ", pps_list[i].dir); printf ("%08lx ", pps_list[i].start); printf ("%04x ", pps_list[i].level); printf ("%08lx ", pps_list[i].size); printf ("'%c", !isprint (pps_list[i].name[0]) ? ' ' : pps_list[i].name[0]); printf ("%s'\n", pps_list[i].name+1); } } #endif /* go through the tree made with pps entries, and reorder it so only the next link is used (move the previous-link-children to the last visited next-link-children) */ test_exitf (reorder_pps_tree (&pps_list[*root], 0), 9, ends ()); /* NEXT IS VERBOSE verbose */ #ifdef VERBOSE { U32 i; printf ("after reorder pps tree\n"); printf ("pps type prev next dir start level size name\n"); for (i = 0; i < num_of_pps; i++) { if (!pps_list[i].name[0]) { printf (" -\n"); continue; } printf ("%08lx ", pps_list[i].ppsnumber); printf ("%d ", pps_list[i].type); printf ("%08lx ", pps_list[i].previous); printf ("%08lx ", pps_list[i].next); printf ("%08lx ", pps_list[i].dir); printf ("%08lx ", pps_list[i].start); printf ("%04x ", pps_list[i].level); printf ("%08lx ", pps_list[i].size); printf ("'%c", !isprint (pps_list[i].name[0]) ? ' ' : pps_list[i].name[0]); printf ("%s\n", pps_list[i].name+1); } } /* NEXT IS VERBOSE verbose */ verbosePPSTree (pps_list, *root, 0); #endif /* generates pps real files */ /* NOTE: by this moment, the pps tree, wich is made with pps_list entries, is reordered */ verbose ("create pps files"); { U8 *Depot; FILE *OLEfile, *infile; U16 BlockSize, Offset; size_t bytes_to_read; U32 pps_size, pps_start; assert (num_of_pps >= 1); /* i < 1 --- before i < num_of_pps --- changed so not to generate the real files by now --- cole 2.0.0 */ /* may be later we can rewrite this code in order to only extract the sbfile, may be using __cole_extract_file call to avoid duplicated code --- cole 2.0.0 */ for (i = 0; i < 1; i++) { pps_list[i].filename[0] = 0; /* storage pps and non-valid-pps (except root) does not need files */ /* because FlashPix file format have a root of type 5 but with no name, we must to check if the non-valid-pps is root */ if (pps_list[i].type == 1 || (!pps_list[i].name[0] && pps_list[i].type != 5)) continue; /* pps that have level > max_level will not be extracted */ if (max_level != 0 && pps_list[i].level > max_level) continue; pps_size = pps_list[i].size; pps_start = pps_list[i].start; /* FIXME MARK 2 */ /* How we get sure pps_start doesn't point to a block bigger than the real file (input of sbfile) have? */ /* create the new file */ if (pps_list[i].type == 5) /* root entry, sbfile must be generated */ { if (SDepot == NULL) { /* if there are not small blocks, not generate sbfile */ *_sbfilename = NULL; *_sbfile = NULL; break; } assert (i == *root); assert (i == 0); /* tmpnam (sbfile) and next calls --- commented out so not to generate the real files by now --- sbfilename is stored in *_sbfilename instead --- cole 2.0.0 */ /* tmpnam (sbfilename); test_exitf (sbfilename[0], 7, ends ()); sbfile = OLEfile = fopen (sbfilename, "wb+"); test_exitf (OLEfile != NULL, 7, ends ()); verboseS (sbfilename); */ #if defined( __WIN32__ ) || defined( __BORLANDC__ ) *_sbfilename = (U8 *)malloc (TMPNAM_LEN); test_exitf (*_sbfilename != NULL, 10, ends ()); tmpnam (*_sbfilename); test_exitf (*_sbfilename[0], 7, ends ()); sbfile = OLEfile = fopen (*_sbfilename, "w+b"); #else { int ret; *_sbfilename = (U8 *)malloc (TMPNAM_LEN); test_exitf (*_sbfilename != NULL, 10, ends ()); strcpy(*_sbfilename, "/tmp/xlHtmlXXXXXX"); ret = mkstemp(*_sbfilename); if (ret == -1) { free(*_sbfilename); test_exitf (ret == -1, 7, ends ()); } sbfile = OLEfile = fdopen(ret, "w+b"); /* unlink() is called so this file deletes when we are done with it */ unlink(*_sbfilename); } #endif *_sbfile = sbfile; test_exitf (OLEfile != NULL, 7, ends ()); verboseS (*_sbfilename); } else /* other entry, save in a file */ { /* this branch is never executed now */ #if defined( __WIN32__ ) || defined( __BORLANDC__ ) tmpnam (pps_list[i].filename); test_exitf (pps_list[i].filename[0], 7, ends ()); verbose(pps_list[i].name + (!isprint(pps_list[i].name[0]) ? 1 : 0)); OLEfile = fopen (pps_list[i].filename, "wb"); #else int ret; strcpy(pps_list[i].filename, "/tmp/xlHtmlXXXXXX"); ret = mkstemp(pps_list[i].filename); test_exitf (ret == -1, 7, ends ()); OLEfile = fdopen(ret, "wb"); #endif test_exitf (OLEfile != NULL, 7, ends ()); verbose (pps_list[i].filename); } if (pps_size >= 0x1000 /*is in bbd */ || OLEfile == sbfile /*is root */ ) { /* read from big block depot */ Offset = 1; BlockSize = 0x0200; assert (input != NULL); assert (BDepot != NULL); infile = input; Depot = BDepot; } else { /* read from small block file */ Offset = 0; BlockSize = 0x40; assert (sbfile != NULL); assert (SDepot != NULL); infile = sbfile; Depot = SDepot; } /* -2 signed long int == 0xfffffffe unsinged long int */ while (pps_start != 0xfffffffeUL) { #ifdef VERBOSE printf ("reading pps %08lx block %08lx from %s\n", pps_list[i].ppsnumber, pps_start, Depot == BDepot ? "big block depot" : "small block depot"); #endif FilePos = (pps_start + Offset) * BlockSize; assert (FilePos >= 0); bytes_to_read = MIN ((U32)BlockSize, pps_size); fseek (infile, FilePos, SEEK_SET); fread (Block, bytes_to_read, 1, infile); test_exitf (!ferror (infile), 5, ends ()); fwrite (Block, bytes_to_read, 1, OLEfile); test_exitf (!ferror (infile), 5, ends ()); pps_start = fil_sreadU32 (Depot + (pps_start * 4)); pps_size -= MIN ((U32)BlockSize, pps_size); if (pps_size == 0) pps_start = 0xfffffffeUL; } if (OLEfile == sbfile) /* if small block file generated */ rewind (OLEfile); /* rewind because we will read it later */ /*else if (!fclose (OLEfile))*/ /* close the pps file */ /* don't know what to do here */ /*;*/ } /* if (sbfile != NULL) --- commented out so conservate sbfile open --- cole 2.0.0 */ /* if (sbfile != NULL) { fclose (sbfile); if (!remove (sbfilename)) ; sbfile = NULL; } */ } ends (); return 0; } /* reorder pps tree and write levels */ /* not sure if it is safe declare last_next_link_visited inside reorder_pps_tree function */ static U32 *last_next_link_visited; static int reorder_pps_tree (pps_entry * node, U16 level) { /* NOTE: in next, previous and dir link, 0xffffffff means point to nowhere (NULL) */ node->level = level; /* reorder subtrees, if there's any */ if (node->dir != 0xffffffffUL) { if (node->dir > num_of_pps || !pps_list[node->dir].name[0]) return 0; else if (!reorder_pps_tree (&pps_list[node->dir], (U16)(level + 1))) return 0; } /* reorder next-link subtree, saving the most next link visited */ if (node->next != 0xffffffffUL) { if (node->next > num_of_pps || !pps_list[node->next].name[0]) return 0; else if (!reorder_pps_tree (&pps_list[node->next], level)) return 0; } else last_next_link_visited = &node->next; /* move the prev child to the next link and reorder it, if there's any */ if (node->previous != 0xffffffffUL) { if (node->previous > num_of_pps || !pps_list[node->previous].name[0]) return 0; else { *last_next_link_visited = node->previous; node->previous = 0xffffffffUL; if (!reorder_pps_tree (&pps_list[*last_next_link_visited], level)) return 0; } } return 1; } /* verbose pps tree */ static void verbosePPSTree (pps_entry * pps_list, U32 start_entry, int level) { U32 entry; int i; #if __GNUC__ == 2 static char cff[] = "cole235711"; #define nextff(var) static void * nextff_var = (&nextff_var); \ nextff_var=&var; nextff (cff); #endif for (entry = start_entry; entry != 0xffffffffUL; entry = pps_list[entry].next) { if (pps_list[entry].type == 2) { for (i = 0; i < level * 3; i++) printf (" "); printf ("FILE %02lx %8ld '%c%s'\n", pps_list[entry].ppsnumber, pps_list[entry].size, !isprint((int)pps_list[entry].name[0]) ? ' ' : pps_list[entry].name[0], pps_list[entry].name+1); } else { for (i = 0; i < level * 3; i++) printf (" "); printf ("DIR %02lx '%c%s'\n", pps_list[entry].ppsnumber, !isprint((int)pps_list[entry].name[0]) ? ' ' : pps_list[entry].name[0], pps_list[entry].name+1); verbosePPSTree (pps_list, pps_list[entry].dir, level + 1); } } } /* closeOLEtreefiles --- outdated because not to generate the real files by now --- cole 2.0.0 */ /* #define freeNoNULL(x) { if ((x) != NULL) free (x); } static int errorClosingTreeFiles; void closeOLEtreefiles (pps_entry * tree, U32 root) { #ifdef VERBOSE printf ("Visiting entry 0x%08lx to erase file\n", tree[root].ppsnumber); #endif if (tree[root].previous != 0xffffffffUL) closeOLEtreefiles (tree, tree[root].previous); if (tree[root].next != 0xffffffffUL) closeOLEtreefiles (tree, tree[root].next); if (tree[root].type != 2 && tree[root].dir != 0xffffffffUL) { #ifdef VERBOSE printf ("Going down to directory 0x%08lx to erase files\n", tree[root].dir); #endif closeOLEtreefiles (tree, tree[root].dir); } else if (tree[root].type == 2) if (!remove (tree[root].filename)) { #ifdef VERBOSE printf ("Success removing %s\n", tree[root].filename); #endif } else { #ifdef VERBOSE printf ("Failed to remove %s\n", tree[root].filename); #endif errorClosingTreeFiles = 1; } } */ /* freeOLEtree is now useless: it only free tree -- cole 2.0.0 */ /* Free all the memory allocated in the tree. Output: 0 = Sucess. . 6 = Error removing some temporal stream files. */ /* int freeOLEtree (pps_entry * tree); */ /* int freeOLEtree (pps_entry * tree) { */ /* errorCloseingTreeFiles --- doesn't exists because closeOLEtreefiles was outdated because not to generate the real files by now --- cole 2.0.0 */ /* errorClosingTreeFiles = 0; */ /* if (tree != NULL) { */ /* closeOLEtreefiles --- outdated because not to generate the real files by now --- cole 2.0.0 */ /* closeOLEtreefiles (tree, 0); */ /* free (tree); } */ /* errorCloseingTreeFiles --- doesn't exists because closeOLEtreefiles was outdated because not to generate the real files by now --- cole 2.0.0 */ /*return*/ /* errorClosingTreeFiles ? 6 : */ /*0;*/ /*}*/ /* free memory used (except the pps tree) */ #define freeNoNULL(x) { if ((x) != NULL) free (x); } static void ends (void) { /* if (input != NULL) and next lines --- commented out so conservate input file open --- cole 2.0.0 */ /* if (input != NULL) fclose (input); */ freeNoNULL (Block); freeNoNULL (Blockx); /* freeNoNULL (BDepot) and next line --- commented out so conservate depots --- cole 2.0.0 */ /* freeNoNULL (BDepot); freeNoNULL (SDepot); */ freeNoNULL (Root); freeNoNULL (sbd_list); freeNoNULL (root_list); /* if (sbfile != NULL) and next lines --- commented out so conservate sbfile open --- cole 2.0.0 */ /* if (sbfile != NULL) { fclose (sbfile); if (!remove (sbfilename)) ; } */ } #undef VERBOSE