int main(int argc, char **argv) { program_name = argv[0]; static char stderr_buf[BUFSIZ]; setbuf(stderr, stderr_buf); const char *base_name = 0; typedef int (*parser_t)(const char *); parser_t parser = do_file; const char *directory = 0; const char *foption = 0; int opt; static const struct option long_options[] = { { "help", no_argument, 0, CHAR_MAX + 1 }, { "version", no_argument, 0, 'v' }, { NULL, 0, 0, 0 } }; while ((opt = getopt_long(argc, argv, "c:o:h:i:k:l:t:n:c:d:f:vw", long_options, NULL)) != EOF) switch (opt) { case 'c': common_words_file = optarg; break; case 'd': directory = optarg; break; case 'f': foption = optarg; break; case 'h': check_integer_arg('h', optarg, 1, &hash_table_size); if (!is_prime(hash_table_size)) { while (!is_prime(++hash_table_size)) ; warning("%1 not prime: using %2 instead", optarg, hash_table_size); } break; case 'i': ignore_fields = optarg; break; case 'k': check_integer_arg('k', optarg, 1, &max_keys_per_item); break; case 'l': check_integer_arg('l', optarg, 0, &shortest_len); break; case 'n': check_integer_arg('n', optarg, 0, &n_ignore_words); break; case 'o': base_name = optarg; break; case 't': check_integer_arg('t', optarg, 1, &truncate_len); break; case 'w': parser = do_whole_file; break; case 'v': printf("GNU indxbib (groff) version %s\n", Version_string); exit(0); break; case CHAR_MAX + 1: // --help usage(stdout); exit(0); break; case '?': usage(stderr); exit(1); break; default: assert(0); break; } if (optind >= argc && foption == 0) fatal("no files and no -f option"); if (!directory) { char *path = get_cwd(); store_filename(path); a_delete path; } else store_filename(directory); init_hash_table(); store_filename(common_words_file); store_filename(ignore_fields); key_buffer = new char[truncate_len]; read_common_words_file(); if (!base_name) base_name = optind < argc ? argv[optind] : DEFAULT_INDEX_NAME; const char *p = strrchr(base_name, DIR_SEPS[0]), *p1; const char *sep = &DIR_SEPS[1]; while (*sep) { p1 = strrchr(base_name, *sep); if (p1 && (!p || p1 > p)) p = p1; sep++; } size_t name_max; if (p) { char *dir = strsave(base_name); dir[p - base_name] = '\0'; name_max = file_name_max(dir); a_delete dir; } else name_max = file_name_max("."); const char *filename = p ? p + 1 : base_name; if (strlen(filename) + sizeof(INDEX_SUFFIX) - 1 > name_max) fatal("`%1.%2' is too long for a filename", filename, INDEX_SUFFIX); if (p) { p++; temp_index_file = new char[p - base_name + sizeof(TEMP_INDEX_TEMPLATE)]; memcpy(temp_index_file, base_name, p - base_name); strcpy(temp_index_file + (p - base_name), TEMP_INDEX_TEMPLATE); } else { temp_index_file = strsave(TEMP_INDEX_TEMPLATE); } catch_fatal_signals(); int fd = mkstemp(temp_index_file); if (fd < 0) fatal("can't create temporary index file: %1", strerror(errno)); indxfp = fdopen(fd, FOPEN_WB); if (indxfp == 0) fatal("fdopen failed"); if (fseek(indxfp, sizeof(index_header), 0) < 0) fatal("can't seek past index header: %1", strerror(errno)); int failed = 0; if (foption) { FILE *fp = stdin; if (strcmp(foption, "-") != 0) { errno = 0; fp = fopen(foption, "r"); if (!fp) fatal("can't open `%1': %2", foption, strerror(errno)); } string path; int lineno = 1; for (;;) { int c; for (c = getc(fp); c != '\n' && c != EOF; c = getc(fp)) { if (c == '\0') error_with_file_and_line(foption, lineno, "nul character in pathname ignored"); else path += c; } if (path.length() > 0) { path += '\0'; if (!(*parser)(path.contents())) failed = 1; path.clear(); } if (c == EOF) break; lineno++; } if (fp != stdin) fclose(fp); } for (int i = optind; i < argc; i++) if (!(*parser)(argv[i])) failed = 1; write_hash_table(); if (fclose(indxfp) < 0) fatal("error closing temporary index file: %1", strerror(errno)); char *index_file = new char[strlen(base_name) + sizeof(INDEX_SUFFIX)]; strcpy(index_file, base_name); strcat(index_file, INDEX_SUFFIX); #ifdef HAVE_RENAME #ifdef __EMX__ if (access(index_file, R_OK) == 0) unlink(index_file); #endif /* __EMX__ */ if (rename(temp_index_file, index_file) < 0) { #ifdef __MSDOS__ // RENAME could fail on plain MSDOS filesystems because // INDEX_FILE is an invalid filename, e.g. it has multiple dots. char *fname = p ? index_file + (p - base_name) : 0; char *dot = 0; // Replace the dot with an underscore and try again. if (fname && (dot = strchr(fname, '.')) != 0 && strcmp(dot, INDEX_SUFFIX) != 0) *dot = '_'; if (rename(temp_index_file, index_file) < 0) #endif fatal("can't rename temporary index file: %1", strerror(errno)); } #else /* not HAVE_RENAME */ ignore_fatal_signals(); if (unlink(index_file) < 0) { if (errno != ENOENT) fatal("can't unlink `%1': %2", index_file, strerror(errno)); } if (link(temp_index_file, index_file) < 0) fatal("can't link temporary index file: %1", strerror(errno)); if (unlink(temp_index_file) < 0) fatal("can't unlink temporary index file: %1", strerror(errno)); #endif /* not HAVE_RENAME */ temp_index_file = 0; return failed; }
// packing: use RCO_DATA_COMPRESSION_* constants uint8_t write_rco (rRCOFile * rco, char *fn, writerco_options opts) { uint32_t i; rRCOFile_writehelper rcoH; // delete file if exists if (file_exists (fn)) { if (remove (fn)) { error ("Unable to write to file %s", fn); return FALSE; } } rcoH.rco = rco; rcoH.fp = fopen (fn, "wb"); if (!rcoH.fp) { error ("Unable to open file %s", fn); return FALSE; } PRFHeader header; header.signature = RCO_SIGNATURE; header.version = (opts.packHeader == RCO_DATA_COMPRESSION_RLZ ? 0x95 : opts.packHeader == RCO_DATA_COMPRESSION_ZLIB ? 0x90 : 0x71); if (rco->verId) { // we won't actually use specified value, // rather, we'll require using the minimum // version from above if (rco->verId > header.version) header.version = rco->verId; } header.null = 0; header.compression = (opts.packHeader << 4) | (rco->umdFlag & 0xF); header.pMainTable = 0xA4; // pretty much always the case // set other sections to nothing for now header.pVSMXTable = header.pTextTable = header.pSoundTable = header.pModelTable = header.pImgTable = header.pObjTable = header.pAnimTable = RCO_NULL_PTR; header.pUnknown = header.pFontTable = RCO_NULL_PTR; // don't know positions of text/label/event data too, but we do know the // lengths for label/events // header.pTextData = header.pLabelData = header.pEventData = 0; header.lLabelData = rco->labelsLen; header.lEventData = rco->eventsLen; header.lTextData = 0; // set pointer sections to blank too header.pTextPtrs = header.pImgPtrs = header.pModelPtrs = header.pSoundPtrs = header.pObjPtrs = header.pAnimPtrs = RCO_NULL_PTR; header.lTextPtrs = header.lImgPtrs = header.lModelPtrs = header.lSoundPtrs = header.lObjPtrs = header.lAnimPtrs = 0; // also blank... header.pImgData = header.pSoundData = header.pModelData = RCO_NULL_PTR; header.lImgData = header.lSoundData = header.lModelData = 0; header.unknown[0] = header.unknown[1] = header.unknown[2] = 0xFFFFFFFF; // write resources to a separate file to get around the issue of unknown // packed size when writing the header (and you can't change it backed after // the header is packed...) FILE *fTmp = NULL; if ((rco->tblImage && rco->tblImage->numSubentries) || (rco->tblSound && rco->tblSound->numSubentries) || (rco->tblModel && rco->tblModel->numSubentries)) { uint32_t totalPackedLen = 0; rRCOEntry *rcoNode; fTmp = tmpfile (); if (rco->tblImage && rco->tblImage->numSubentries) { for (rcoNode = rco->tblImage->firstChild; rcoNode; rcoNode = rcoNode->next) { // our compression decision thing uint32_t c = ((rRCOImgModelEntry *) (rcoNode->extra))->compression; if (((rRCOImgModelEntry *) (rcoNode->extra))->format < RCO_IMG_BMP) { if (opts.packImgCompr != -1) c = opts.packImgCompr; } else { if (opts.packImg != -1) c = opts.packImg; } if (rcoNode->srcLenUnpacked) { rcoNode->srcLen = rco_write_resource (fTmp, rcoNode, c, &opts, rco); if (!rcoNode->srcLen && rcoNode->labelOffset != RCO_NULL_PTR) warning ("[resource] Can't write image resource '%s'!", rco->labels + rcoNode->labelOffset); } rcoNode->srcCompression = c; rcoNode->srcAddr = totalPackedLen; totalPackedLen += (rcoNode->srcLen % 4 ? (rcoNode->srcLen / 4) * 4 + 4 : rcoNode->srcLen); } header.lImgData = totalPackedLen; } totalPackedLen = 0; if (rco->tblSound && rco->tblSound->numSubentries) { for (rcoNode = rco->tblSound->firstChild; rcoNode; rcoNode = rcoNode->next) { if (rcoNode->srcLenUnpacked) { uint32_t packedLen = rco_write_resource (fTmp, rcoNode, RCO_DATA_COMPRESSION_NONE, &opts, rco); if (!packedLen && rcoNode->labelOffset != RCO_NULL_PTR) warning ("[resource] Can't write sound resource '%s'!", rco->labels + rcoNode->labelOffset); totalPackedLen += ALIGN_TO_4 (packedLen); // if(totalPackedLen %4) totalPackedLen += 4-(totalPackedLen%4); } } header.lSoundData = totalPackedLen; } totalPackedLen = 0; if (rco->tblModel && rco->tblModel->numSubentries) { for (rcoNode = rco->tblModel->firstChild; rcoNode; rcoNode = rcoNode->next) { uint32_t c = ((rRCOImgModelEntry *) (rcoNode->extra))->compression; if (opts.packModel != -1) c = opts.packModel; if (rcoNode->srcLenUnpacked) { rcoNode->srcLen = rco_write_resource (fTmp, rcoNode, c, &opts, rco); if (!rcoNode->srcLen && rcoNode->labelOffset != RCO_NULL_PTR) warning ("[resource] Can't write model resource '%s'!", rco->labels + rcoNode->labelOffset); } rcoNode->srcCompression = c; rcoNode->srcAddr = totalPackedLen; totalPackedLen += (rcoNode->srcLen % 4 ? (rcoNode->srcLen / 4) * 4 + 4 : rcoNode->srcLen); } header.lModelData = totalPackedLen; } rewind (fTmp); } filewrite (rcoH.fp, &header, sizeof (header)); rcoH.tables = 0; // if compressing, write to memory if (opts.packHeader) { rcoH.tables = malloc (RCO_WRITE_MEM_BUFFER); rcoH.memPos = rcoH.tablesSize = 0; rcoH.tablesBuffered = RCO_WRITE_MEM_BUFFER; rcoH.memOffset = ftell (rcoH.fp); } rcoH.sizeImg = rcoH.sizeModel = rcoH.sizeSound = rcoH.sizeText = 0; rcoH.longestLangData = 0; write_entry (&rcoH, &(rco->tblMain), 0xA4 /* typically where the main * table is written to */ , 0, TRUE); // fix up object/anim extra data { if (rco->tblObj) rco_write_fix_refs (rco->tblObj, &rcoH, rco, RCO_OBJ_EXTRA_LEN, RCO_OBJ_EXTRA_LEN_NUM, TRUE); if (rco->tblAnim) rco_write_fix_refs (rco->tblAnim, &rcoH, rco, RCO_ANIM_EXTRA_LEN, RCO_ANIM_EXTRA_LEN_NUM, FALSE); } { // write hashtable data /* { // special case for text hashes if(rco->numPtrText) { header.pTextPtrs * = rcowrite_ftell(&rcoH); for(i=0; i<rco->numPtrText; i++) { uint32_t * writePtr = 0; if(rco->ptrText[i].textEntry && rco->ptrText[i].index) * writePtr = rco->ptrText[i].textEntry->offset + sizeof(RCOEntry) + * sizeof(RCOTextEntry) + (rco->ptrText[i].index - * ((rRCOTextEntry*)(rco->ptrText[i].textEntry->extra))->indexes)*sizeof(RCOTextIndex); * rco_fwrite(&rcoH, &writePtr, sizeof(uint32_t)); } } } */ if (rco->tblText) { header.pTextPtrs = rcowrite_ftell (&rcoH); header.lTextPtrs = 0; // generate sorted list of text entries, sorted by languageID rRCOEntry **sList = make_sorted_list_of_subentries (rco->tblText, text_hash_table_qsort); for (i = 0; i < rco->tblText->numSubentries; i++) header.lTextPtrs += write_text_hash_table (&rcoH, sList[i], rco); free (sList); header.lTextPtrs *= sizeof (uint32_t); } if (rco->tblImage) { header.pImgPtrs = rcowrite_ftell (&rcoH); header.lImgPtrs = write_hash_table (&rcoH, rco->tblImage, rco) * sizeof (uint32_t); } if (rco->tblModel) { header.pModelPtrs = rcowrite_ftell (&rcoH); header.lModelPtrs = write_hash_table (&rcoH, rco->tblModel, rco) * sizeof (uint32_t); } if (rco->tblSound) { header.pSoundPtrs = rcowrite_ftell (&rcoH); header.lSoundPtrs = write_hash_table (&rcoH, rco->tblSound, rco) * sizeof (uint32_t); } if (rco->tblObj) { header.pObjPtrs = rcowrite_ftell (&rcoH); header.lObjPtrs = write_hash_table (&rcoH, rco->tblObj, rco) * sizeof (uint32_t); } if (rco->tblAnim) { header.pAnimPtrs = rcowrite_ftell (&rcoH); header.lAnimPtrs = write_hash_table (&rcoH, rco->tblAnim, rco) * sizeof (uint32_t); } /* * #define RCO_WRITERCO_WRITE_PTR_SECT(pd, pl, hp) { \ if(pl) { \ hp = * rcowrite_ftell(&rcoH); \ for(i=0; i<pl; i++) { \ if(pd[i]) \ * rco_fwrite(&rcoH, &(((rRCOEntry*)(pd[i]))->offset), sizeof(uint32_t)); \ * else { \ uint32_t zero = 0; \ rco_fwrite(&rcoH, &zero, sizeof(uint32_t)); \ * } \ } \ } \ } //RCO_WRITERCO_WRITE_PTR_SECT(rco->ptrText, * rco->numPtrText, header.pTextPtrs); * RCO_WRITERCO_WRITE_PTR_SECT(rco->ptrImg, rco->numPtrImg, * header.pImgPtrs); RCO_WRITERCO_WRITE_PTR_SECT(rco->ptrModel, * rco->numPtrModel, header.pModelPtrs); * RCO_WRITERCO_WRITE_PTR_SECT(rco->ptrSound, rco->numPtrSound, * header.pSoundPtrs); RCO_WRITERCO_WRITE_PTR_SECT(rco->ptrObj, * rco->numPtrObj, header.pObjPtrs); * RCO_WRITERCO_WRITE_PTR_SECT(rco->ptrAnim, rco->numPtrAnim, * header.pAnimPtrs); */ } { // write label/event data (and text if // applicable) // write text (note, old behaviour - newer RCOs have text written in a // different location) if (!opts.packText && rco->tblText && rco->tblText->numSubentries) { rRCOEntry *rcoNode; header.pTextData = rcowrite_ftell (&rcoH); header.lTextData = rcoH.sizeText; for (rcoNode = rco->tblText->firstChild; rcoNode; rcoNode = rcoNode->next) { rco_write_text_resource (&rcoH, rcoNode, RCO_DATA_COMPRESSION_NONE, &opts, ((rRCOTextEntry *) (rcoNode->extra))->lang, (rco->tblText->lastChild == rcoNode)); } } // write label+event data header.pLabelData = rcowrite_ftell (&rcoH); if (rco->labelsLen) rco_fwrite (&rcoH, rco->labels, rco->labelsLen); header.pEventData = rcowrite_ftell (&rcoH); if (rco->eventsLen) rco_fwrite (&rcoH, rco->events, rco->eventsLen); else if (rco->tblObj || rco->tblAnim) { // weird case: if there's // object entries, there will // be 4 bytes for events; I'll // assume this covers anim as // well (although there isn't // an RCO with anim that // doesn't have objects) uint32_t zero = 0; rco_fwrite (&rcoH, &zero, sizeof (zero)); header.lEventData = sizeof (zero); } // the text pointer is weird in that if there's no text, it's set equal to // the label pointer; even weirder, some RCOs have a null pointer (for // FW5.00 all except lftv_* RCOs have null pointers for pTextData if // there's no text) // my theory: if compressing, it will be RCO_NULL_PTR, otherwise it'll = // header.pLabelData // if(!header.lTextData) header.pTextData = RCO_NULL_PTR; // if(!header.lTextData) header.pTextData = header.pLabelData; if (!header.lTextData) header.pTextData = (opts.packHeader ? RCO_NULL_PTR : header.pLabelData); } // flush compression stuff here HeaderComprInfo ci; if (opts.packHeader) { uint8_t *bufferOut = NULL; ci.lenLongestText = rcoH.longestLangData; ci.lenUnpacked = rcoH.tablesSize; ci.lenPacked = 0; if (opts.packHeader == RCO_DATA_COMPRESSION_ZLIB) { uint32_t bound = compressBound (rcoH.tablesSize); bufferOut = (uint8_t *) malloc (bound); ci.lenPacked = zlib_compress (rcoH.tables, rcoH.tablesSize, bufferOut, bound, opts.zlibLevel, opts.zlibMethod); } else if (opts.packHeader == RCO_DATA_COMPRESSION_RLZ) { bufferOut = (uint8_t *) malloc (rcoH.tablesSize); ci.lenPacked = rlz_compress (rcoH.tables, rcoH.tablesSize, bufferOut, rcoH.tablesSize, opts.rlzMode); } else { error ("lulwut?"); exit (1); } int comprMisalign = ci.lenPacked % 4; uint32_t packedLen = ci.lenPacked; if (rco->eSwap) es_headerComprInfo (&ci); filewrite (rcoH.fp, &ci, sizeof (ci)); filewrite (rcoH.fp, bufferOut, packedLen); free (bufferOut); if (comprMisalign) { // 4 byte align uint32_t zero = 0; filewrite (rcoH.fp, &zero, 4 - comprMisalign); } } // write text if packing header if (opts.packText && rco->tblText && rco->tblText->numSubentries) { rRCOEntry *rcoNode; // header.pTextData = rcowrite_ftell(&rcoH); header.pTextData = ftell (rcoH.fp); header.lTextData = 0; // rcoH.sizeText; for (rcoNode = rco->tblText->firstChild; rcoNode; rcoNode = rcoNode->next) { header.lTextData += rco_write_text_resource (&rcoH, rcoNode, opts.packHeader, &opts, ((rRCOTextEntry *) (rcoNode->extra))->lang, (rco->tblText->lastChild == rcoNode)); } } // write resources /* { uint32_t totalPackedLen = 0; if(rco->tblImage) { header.pImgData = * rcowrite_ftell(&rcoH); header.lImgData = rcoH.sizeImg; // TOxDO: this * model actually won't work - we have to update the offsets of ALL the * entries after packing... for(i=0; i<rco->tblImage->numSubentries; i++) { * uint32_t packedSize = rco_write_resource(&rcoH, * &(rco->tblImage->subentries[i]), RCO_DATA_COMPRESSION_NONE); // TOxDO: * change this // TOxDO: update packed size value uint32_t curFpos = * rcowrite_ftell(rcoH.fp); totalPackedLen += (packedSize % 4 ? * (packedSize/4)*4+4 : packedSize); } header.lImgData = totalPackedLen; } * totalPackedLen = 0; if(rco->tblSound) { header.pSoundData = * rcowrite_ftell(&rcoH); header.lSoundData = rcoH.sizeSound; for(i=0; * i<rco->tblSound->numSubentries; i++) { totalPackedLen += * rco_write_resource(&rcoH, &(rco->tblSound->subentries[i]), * RCO_DATA_COMPRESSION_NONE); if(totalPackedLen %4) totalPackedLen += * 4-(totalPackedLen%4); } header.lSoundData = totalPackedLen; } // TOxDO: * write model resources } */ if ((rco->tblImage && rco->tblImage->numSubentries) || (rco->tblSound && rco->tblSound->numSubentries) || (rco->tblModel && rco->tblModel->numSubentries)) { // update data pointers uint32_t pos = ftell (rcoH.fp); if (rco->tblImage && rco->tblImage->numSubentries) { header.pImgData = pos; pos += header.lImgData; } if (rco->tblSound && rco->tblSound->numSubentries) { header.pSoundData = pos; pos += header.lSoundData; } if (rco->tblModel && rco->tblModel->numSubentries) { header.pModelData = pos; pos += header.lModelData; } // copy contents of fTmp across (uses a simple buffered copy) uint32_t len = header.lImgData + header.lSoundData + header.lModelData; uint8_t buffer[65536]; while (len) { uint32_t readAmt = (len > 65536 ? 65536 : len); fileread (fTmp, buffer, readAmt); filewrite (rcoH.fp, buffer, readAmt); len -= readAmt; } fclose (fTmp); // this deletes our temp file } // fix header if (rco->tblVSMX) header.pVSMXTable = rco->tblVSMX->offset; if (rco->tblText) header.pTextTable = rco->tblText->offset; if (rco->tblSound) header.pSoundTable = rco->tblSound->offset; if (rco->tblModel) header.pModelTable = rco->tblModel->offset; if (rco->tblImage) header.pImgTable = rco->tblImage->offset; if (rco->tblFont) header.pFontTable = rco->tblFont->offset; if (rco->tblObj) header.pObjTable = rco->tblObj->offset; if (rco->tblAnim) header.pAnimTable = rco->tblAnim->offset; rewind (rcoH.fp); if (rco->eSwap) es_rcoHeader (&header); filewrite (rcoH.fp, &header, sizeof (header)); // TODO: fix resource pointers? // TODO: tie things up etc?? fclose (rcoH.fp); return TRUE; }