This is compressHelp.m in view mode; [Download] [Up]
/* COMPRESSHELP, convert Help directory to a `Help.store' file. All rights reserved. (C) 1992,1993 Frank Thomas <frank@glocke.robin.de> Copyright (C) 1993 by Michael L.H. Brouwer <michael@urc.tue.nl> 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. Outline: 2 BTrees, a dirTree and a fileTree dirTree: Key=Name of directory; Value=f1 n1;f2 n2;... where f1.. are the filenames and n1.. are numbers (FileNumber) fileTree: Key=FileNumber Value=the whole file Rtf and .index.store files are in compressed form in the fileTree The fileTree is reduced by the fact that identical files are mapped to the same FileNumber in the dirTree. $Modified: Mon Apr 5 00:56:44 1993 by michael $ $Id: compressHelp.m,v 1.14 1993/04/04 22:57:22 michael Exp $ */ #import <appkit/appkit.h> #import <objc/objc.h> #import <mach/mach.h> #import <mach/mach_error.h> #import <assert.h> #import <stdio.h> #import <libc.h> #import <errno.h> #import <string.h> #import <stdlib.h> #import <sys/wait.h> #import <store/IXStoreFile.h> #import <btree/IXBTree.h> #import <btree/IXBTreeCursor.h> #import <sys/param.h> #import <sys/types.h> #import <sys/dir.h> #import <sys/stat.h> #import "global.h" #import "getopt.h" #import "dirtree.h" #import "mapfile.h" char *version_string = "compressHelp 1.1 (5 apr 93)"; /* No include file contains this one! */ extern char *realpath (const char *file_name, char *resolved_name); /* Command line options. */ bool delete_dir = FALSE; static struct option longopts[] = { {"delete", 0, NULL, 'd'}, {"help", 0, NULL, 'h'}, {"nowarn", 0, NULL, 'n'}, {"verbose", 0, NULL, 'v'}, {"version", 0, NULL, 'V'}, {NULL, 0, NULL, 0} }; static char *getBufferPointer; static char * getBuffer (size_t size) { static bufferSize; if (getBufferPointer) { if (size < bufferSize) return getBufferPointer; free (getBufferPointer); } bufferSize = size; return getBufferPointer = xmalloc (size); } /* getBuffer */ static void freeBuffer (void) { if (getBufferPointer) free (getBufferPointer); } /* freeBuffer */ /* Compress file src to dst. Return 0 on success and -1 on failure. */ static bool compressTo (const char *src, const char *dst) { union wait statusp; int pid; pid = fork (); if (pid < 0) return errorf ("fork"); if (pid == 0) { int src_fd, dst_fd; src_fd = open (src, O_RDONLY); if (src_fd < 0) fatalf ("open (RDONLY): %s", src); dst_fd = open (dst, O_RDWR | O_TRUNC | O_CREAT, 0666); if (dst_fd < 0) fatalf ("open (RDWR): %s", dst); dup2 (src_fd, fileno (stdin)); dup2 (dst_fd, fileno (stdout)); execl ("/usr/ucb/compress", "/usr/ucb/compress", NULL); fatalf ("exec /usr/ucb/compress"); } if (wait (&statusp) < 0 || statusp.w_retcode) return -1; return 0; } /* compressTo */ /* Store each directory in T into the IXBTree associsated with DIRCURSOR. Return 0 on success and -1 on failure. */ static bool insertDirTree (dt_tree *t, IXBTreeCursor *dirCursor) { int i; dt_file *f; if (verbose) fprintf (stderr, "Writing DirTree\n"); /* Sort for dirtree. */ sort_by_name (t); for (i = t->elems, f = t->files; i > 0; i--, f++) { char *ext; char key[MAXPATHLEN]; ext = strrchr (f->dir, '.'); if (ext && strcmp (ext, ".rtfd/") == 0) { /* Stuff the whole dir in the store. */ int cnt; dt_file *f2; size_t size = 0; char *data; char *curr; int keyLen = ext - f->dir + 6; bool found; strcpy (key, f->dir); key[keyLen - 1] = 0; /* How large is the data? */ for (cnt = i, f2 = f; cnt > 0 && f2->dir == f->dir; f2++, cnt--) size += strlen (f2->name); /* For number, semicolon and space. */ size += (i - cnt) * 14; curr = data = getBuffer (size); /* Build the data. */ for (; cnt != i; f++, i--) { sprintf (curr, "%s %ld; ", f->name, f->id); curr += strlen (curr); } /* Compensate the last step-part of the for-loop. */ f--; i++; /* And stuff the data in the store. */ found = [dirCursor setKey: key andLength: keyLen]; assert (found == NO); [dirCursor writeValue: data andLength: curr - data + 1]; } else { /* Single file. */ char data[MAXPATHLEN + 12]; sprintf (key, "%s%s", f->dir, f->name); sprintf (data, "%s %ld;", f->name, f->id); ext = strrchr (f->name, '.'); /* Check if we know its type. */ if (strcmp (f->name, ".index.store") == 0 || (ext && strcmp(ext,".rtf") == 0)) { /* Insert single file. */ [dirCursor setKey: key andLength: strlen (key) + 1]; [dirCursor writeValue: data andLength: strlen (data) + 1]; } else { all_ok = FALSE; if (warnings) fprintf (stderr, "Unknown filetype %s%s\n", f->dir, f->name); } } } return 0; } /* insertDirTree */ /* Store each file with a unique ID in T once into the IXBTree associsated with FILECURSOR. Return 0 on success and -1 on failure. */ static bool insertFileTree (dt_tree *t, IXBTreeCursor *fileCursor) { /* Long enough for /usr/tmp... */ char compressedName[30]; long lastId = -1; size_t dataSize; char *dataFile; dt_file *f; int i; if (verbose) fprintf (stderr, "Writing FileTree\n"); sort_by_id (t); sprintf (compressedName, "/usr/tmp/ch..%d", getpid ()); for (i = t->elems, f = t->files; i > 0; i--, f++) { char name[MAXPATHLEN]; bool compressed; map_handle mh; char *ext; /* We store every id only once. */ if (f->id == lastId) continue; /* Save for next time around. */ lastId = f->id; [fileCursor setKey: &f->id andLength: sizeof (f->id)]; /* Full name. */ sprintf (name, "%s%s", f->dir, f->name); /* Extension of basename. */ ext = strrchr (f->name, '.'); /* Check if we have to compress this one. */ if (strcmp (f->name, ".index.store") == 0 || (ext && strcmp(ext,".rtf") == 0)) { struct stat st; /* Compress it. */ if (compressTo (name, compressedName)) return -1; stat (compressedName, &st); dataFile = compressedName; dataSize = st.st_size; compressed = TRUE; } else { dataFile = name; dataSize = f->size; compressed = FALSE; } /* Map file to mem. */ mh.name = dataFile; if (map_file (&mh)) return -1; [fileCursor writeValue: mh.mem andLength: dataSize]; if (unmap_file (&mh)) return -1; } unlink (compressedName); return 0; } /* insertFileTree */ /* Store all *directories* in T into the IXBTree associsated with DIRCURSOR, and store all *files* in T into the IXBTree associsated with DIRCURSOR. Return 0 on success and -1 on failure. */ static bool insertAll (dt_tree *t, IXBTreeCursor *dirCursor, IXBTreeCursor *fileCursor) { #ifdef DEBUG dt_print (t); #endif /* Write the dirtree followed by the filetree. */ if (insertDirTree (t, dirCursor) || insertFileTree (t, fileCursor)) return -1; return 0; } /* insertAll */ /* Create a DIR.store file for DIR. Return 0 on success and return -1 on failure. */ static int compressHelp_dir (char *dir) { char resolved_name[MAXPATHLEN], oldPath[MAXPATHLEN]; IXBTreeCursor *fileCursor; IXBTreeCursor *dirCursor; IXStoreFile *helpStore; IXBTree *fileTree; IXBTree *dirTree; char *helpName; dt_tree *t; int files; int res; /* Save old path. */ getwd (oldPath); /* Build name of store-file. */ if (!realpath (dir, resolved_name)) return errorf ("%s", resolved_name); helpName = alloca (strlen (resolved_name) + sizeof (".store")); strcat (strcpy (helpName, resolved_name), ".store"); /* Destroy old one store file. */ unlink (helpName); /* Create help.store before changing directory. */ helpStore = [[IXStoreFile alloc] initWithFile: helpName]; if (!helpStore) return errorf ("[IXStoreFile initWithFile: %s]", helpName); chdir (resolved_name); t = dt_read (); if (t == (dt_tree *) -1) { chdir (oldPath); return -1; } files = dt_calc_ids (t); if (files == -1) { chdir (oldPath); return -1; } /* I do only inserts but want a small Help.store and -compact doesn't work (see GeneralRef/07.../Classes/IXBTree.rtf) using optimizeForSpace doesn't seem to help as well (the only difference seems to be some timestamps in the database), but NeXT's Help.store files are smaller. I think they used a test version of the indexing kit which can compact IXBTrees. So I added 'compact' and hope that this will help some time in the future. */ /* Create Dir-Tree as Block 1. */ dirTree = [[IXBTree alloc] initInStore: helpStore]; if (!dirTree) { fprintf (stderr, "Can't create dirTree\n"); [helpStore free]; chdir (oldPath); return -1; } /* Create File-Tree as Block 2. */ fileTree = [[IXBTree alloc] initInStore: helpStore]; if (!fileTree) { fprintf (stderr, "Can't create fileTree\n"); [dirTree free]; [helpStore free]; chdir (oldPath); return -1; } /* How to compare. */ [fileTree setComparator: IXCompareUnsignedLongs andContext: NULL]; [dirTree setComparator: IXCompareStrings andContext: NULL]; /* One cursor for each tree. */ dirCursor = [[IXBTreeCursor alloc] initWithBTree: dirTree]; fileCursor = [[IXBTreeCursor alloc] initWithBTree: fileTree]; /* Insert all files we gathered (and compress them if necessary). */ res = insertAll (t, dirCursor, fileCursor); if (!res) { if (verbose) fprintf (stderr, "Compacting DirTree\n"); [dirTree compact]; if (verbose) fprintf (stderr, "Compacting FileTree\n"); [fileTree compact]; } [fileCursor free]; [dirCursor free]; [fileTree free]; [dirTree free]; /* Save all outstanding blocks to disk. */ [helpStore commitTransaction]; [helpStore free]; chdir (oldPath); if (res) unlink (helpName); else if (delete_dir && all_ok) { char *buf = malloc (8 + strlen (dir)); sprintf (buf, "rm -rf %s", dir); if (verbose) fprintf (stderr, "Removing directory: %s\n", dir); res = system (buf); if (res) errorf ("system (%s) returned %d", buf, res); free (buf); } return res; } /* compressHelp_dir */ /* If PRINTHELP is TRUE, print verbose help otherwise just show a short usage line. Exit return code 4. */ static void usage (bool printhelp) { if (!printhelp) fprintf (stderr, "\ usage: %s [-dhnvV] dir...\n\ for more help, type: %s -h\n", prog, prog); else fprintf (stderr, "\ %s\n\ usage: %s [-dhnvV] dir...\n\ -d --delete delete directory after creating .store file\n\ -h --help give this help\n\ -n --nowarn don't print warnings for unrecognized files\n\ -v --verbose verbose mode\n\ -V --version display version number\n\ dir... directories to create .store files for.\n", version_string, prog); exit (4); } /* usage */ /* Yes, the main program, which parses arguments, and does the right thing with them. */ int main (int argc, char **argv) { int opt; init_prog (argv[0]); while ((opt = getopt_long (argc, argv, "dhnvV", longopts, (int *) 0)) != EOF) { switch (opt) { case 'd': delete_dir = TRUE; break; case 'h': usage (TRUE); case 'n': warnings = FALSE; break; case 'v': verbose = TRUE; break; case 'V': fprintf (stderr, "%s\n", version_string); break; default: usage (FALSE); } } if (optind == argc) usage (FALSE); /* Loop though all the directory arguments. Exit when the first one goes wrong. */ while (optind < argc) if (compressHelp_dir (argv[optind++])) return 1; return 0; } /* main */
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.