/*===========================================================================* * * * sflfort.c - * * * * Copyright (c) 1991-2003 iMatix Corporation * * * * ------------------ GPL Licensed Source Code ------------------ * * iMatix makes this software available under the GNU General * * Public License (GPL) license for open source projects. For * * details of the GPL license please see www.gnu.org or read the * * file license.gpl provided in this package. * * * * 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 in the file 'license.gpl'; if * * not, write to the Free Software Foundation, Inc., 59 Temple * * Place - Suite 330, Boston, MA 02111-1307, USA. * * * * You can also license this software under iMatix's General Terms * * of Business (GTB) for commercial projects. If you have not * * explicitly licensed this software under the iMatix GTB you may * * only use it under the terms of the GNU General Public License. * * * * For more information, send an email to info@imatix.com. * * -------------------------------------------------------------- * *===========================================================================*/ #include "prelude.h" /* Universal header file */ #include "sflfile.h" /* File-handling functions */ #include "sflstr.h" /* String-handling functions */ #include "sflmem.h" /* Memory-handling functions */ #include "sflcomp.h" /* Compression functions */ #include "sflfort.h" /* Prototypes for functions */ /* These constants define the maximum sizes of blocks in the fortune file * and the maxium number of blocks in such a file. You should not change * these unless you're prepared to rebuild all fortune files. */ #define BLOCK_SIZE 32000U /* Max. size of output block */ #define MAX_PARAS 250 /* Max. paragraphs per block */ #define MAX_BLOCKS 65000U /* Max. blocks in file */ static void have_end_of_paragraph (Bool compress); static void have_end_of_block (Bool compress); static FILE *input, /* Input file stream */ *output, /* Output file stream */ *scratch; /* Temporary copy of input */ static int *sizes; /* Size of each block */ static dbyte insize, outsize, nbr_blocks, nbr_paras, output_dbyte; static qbyte output_qbyte; static byte *inbuf, *outbuf; /* ---------------------------------------------------------------------[<]- Function: fortune_build Synopsis: Builds an indexed fortune file from a formatted text file. The text file contains paragraphs separated by lines containing '%%'. If okay, returns 0, else returns -1, see errno for the cause. ---------------------------------------------------------------------[>]-*/ int fortune_build ( const char *infile, /* Name of file to compress */ const char *outfile, /* Output file, created */ Bool compress) /* Compress yes/no */ { char line [LINE_MAX + 1]; /* Line from input file */ int block_nbr; long offset; inbuf = mem_alloc (BLOCK_SIZE); outbuf = mem_alloc (BLOCK_SIZE); sizes = mem_alloc (MAX_BLOCKS * sizeof (int)); input = file_open (infile, 'r'); scratch = ftmp_open (NULL); if (!inbuf || !outbuf || !sizes || !input || !scratch) { mem_free (inbuf); mem_free (outbuf); mem_free (sizes); return (-1); } nbr_blocks = 0; nbr_paras = 0; /* Number of paragraphs in block */ insize = 2; /* Leave room for nbr_paras */ while (file_read (input, line)) { if (streq (line, "%%")) have_end_of_paragraph (compress); else { memcpy (inbuf + insize, line, strlen (line)); insize += strlen (line); inbuf [insize++] = '\n'; } } have_end_of_paragraph (compress); if (nbr_paras) have_end_of_block (compress); fclose (input); /* Finished with input file */ output = fopen (outfile, "wb"); fprintf (output, "IFF%s -- http://www.imatix.com/ --\n%c", compress? "CMP": "TXT", 26); output_dbyte = htons (nbr_blocks); fwrite (&output_dbyte, 2, 1, output); offset = ftell (output) + nbr_blocks * 4; for (block_nbr = 0; block_nbr < nbr_blocks; block_nbr++) { output_qbyte = htonl (offset); fwrite (&output_qbyte, 4, 1, output); offset += sizes [block_nbr]; } fseek (scratch, 0, SEEK_SET); /* Back to start of scratch file */ while ((outsize = fread (outbuf, 1, BLOCK_SIZE, scratch)) != 0) fwrite (outbuf, 1, outsize, output); file_close (output); ftmp_close (scratch); mem_free (sizes); mem_free (inbuf); mem_free (outbuf); return (0); /* No errors */ } static void have_end_of_paragraph (Bool compress) { if (insize) /* If there is a paragraph waiting */ { inbuf [insize++] = '\0'; /* Terminate paragraph with null */ nbr_paras++; if ((nbr_paras == MAX_PARAS) || (insize > BLOCK_SIZE - 2000)) { have_end_of_block (compress); nbr_paras = 0; insize = 2; } } } static void have_end_of_block (Bool compress) { output_dbyte = htons (nbr_paras); *(dbyte *) inbuf = output_dbyte; if (compress) { outsize = compress_block (inbuf, outbuf, insize); output_dbyte = htons (outsize); fwrite (&output_dbyte, 2, 1, scratch); fwrite (outbuf, 1, outsize, scratch); sizes [nbr_blocks] = outsize + 2; } else { output_dbyte = htons (insize); fwrite (&output_dbyte, 2, 1, scratch); fwrite (inbuf, 1, insize, scratch); sizes [nbr_blocks] = insize + 2; } insize = 2; /* Clear input buffer */ nbr_paras = 0; nbr_blocks++; } /* ---------------------------------------------------------------------[<]- Function: fortune_read Synopsis: Reads a random paragraph from the specified fortune file. The fortune file is located in the current directory or on the current path. The paragraph of text is returned in a freshly-allocated block of memory that you should free afterwards using mem_free(). Returns NULL if the fortune file could not be opened. ---------------------------------------------------------------------[>]-*/ char * fortune_read (const char *fortune_file) { static Bool first_time = TRUE; FILE *fortunes; /* FFF input stream */ byte *inbuf, /* Block read from file */ *outbuf; /* And after decompression */ int nbr_blocks, /* Number of blocks in data file */ nbr_paras, /* Number of paragraphs in block */ paragraph; dbyte input_dbyte, block_size; /* Size of current block */ qbyte input_qbyte, block_offset; /* Offset of block in file */ char *para_start; /* Start of paragraph */ Bool compressed; /* Compressed fortune data? */ /* Initialise random-number generator if necessary */ if (first_time) { randomize (); first_time = FALSE; } /* Look for fortunes file */ fortunes = file_locate ("PATH", fortune_file, NULL); if (fortunes == NULL) return (NULL); /* Allocate working buffers */ inbuf = mem_alloc (BLOCK_SIZE); outbuf = mem_alloc (BLOCK_SIZE); if (inbuf == NULL || outbuf == NULL) { mem_free (inbuf); mem_free (outbuf); return (NULL); } /* Indexed Fortune File format starts with IFFCMP or IFFTXT */ file_read (fortunes, (char *) inbuf); if (memcpy (inbuf, "IFFTXT", 6) == 0) compressed = FALSE; else compressed = TRUE; while (fgetc (fortunes) != 26); /* Skip past file header */ /* Get total number of blocks in file */ fread (&input_dbyte, 2, 1, fortunes); nbr_blocks = ntohs (input_dbyte); /* Look at random block address in Toc */ fseek (fortunes, random (nbr_blocks) * 4, SEEK_CUR); fread (&input_qbyte, 4, 1, fortunes); block_offset = ntohl (input_qbyte); /* Go read, then decompress the block */ fseek (fortunes, block_offset, SEEK_SET); fread (&input_dbyte, 2, 1, fortunes); block_size = ntohs (input_dbyte); if (compressed) { fread (inbuf, 1, block_size, fortunes); expand_block (inbuf, outbuf, block_size); } else fread (outbuf, 1, block_size, fortunes); /* Chose random paragraph from block */ input_dbyte = *(dbyte *) outbuf; nbr_paras = ntohs (input_dbyte); paragraph = random (nbr_paras); para_start = (char *) outbuf + 2; while (paragraph--) para_start = strchr (para_start, '\0') + 1; para_start = mem_strdup (para_start); /* Release allocated memory */ fclose (fortunes); mem_free (inbuf); mem_free (outbuf); return (para_start); }