|
| iMatix home page | << | < | > | >> |
SFLVersion 2.1 |
The SFL (Standard Function Library) from iMatix is a portable function library for C/C++ programs. The SFL is the result of many years' development, and is provided as free software for the benefit of the Internet community.
You may want to go straight to the Table of Contents.
The SFL is written in ANSI C and has been ported to MS-DOS, Windows, UNIX systems (Linux, IBM AIX, SunOS, HP/UX, Solaris, NetBSD, FreeBSD, SCO OpenServer, Digital UNIX) and Digital OpenVMS. It comes with complete sources and documentation in HTML.
The SFL provides about 300 functions that cover these areas:
The SFL is free software that you may use and distribute for private or commercial purposes according to the SFL License Agreement.
At iMatix we develop portable free and commercial software. We work in ANSI C to cover the widest range possible. A major part of our toolkit has always been our subroutine library. This was initially written for MS-DOS in 1991 but has developed into a more ambitious project since then.
From the outset, we ignored commercial libraries. Our software is usually free - as in 'liberated' rather than 'gratis' - and using a commercial library would have been intolerable. We looked for free libraries, but found only specialised and mostly non-portable collections of functions. So, we built our own. We hope you like it. We certainly use it all the time.
When we designed the SFL, we had certain things in mind:
The SFL is in use on these systems:
Some recent functions may not have been tested or implemented across all platforms. Some functions are empty on some platforms. Since the SFL is continually improving and enlarging, there are always newer functions that are less tested, and possibly less than 100% portable. Our intention is that the transparency of the SFL makes these functions easy to test and improve.
We supply the SFL as two archives: a source kit and a documentation kit (in HTML). These files are available for download by HTTP from our website on a permanent basis. You need to recompile the SFL for your specific system, using an ANSI C compiler. We don't provide binary kits (as yet) for several reasons:
The SFL source archive is supplied as a zip file and a GNU gzip+tar file. These are the files in the /pub/sfl/src directory:
sflsrc21.tgz 308722 98/04/25 10:17:16 Gzip/tar archive sflsrc21.zip 385323 98/07/25 23:28:32 ZIP archive
If you have trouble accessing the iMatix site, send us an e-mail and we'll send you the SFL archives by return e-mail, using the uuencode format.
The SFL documentation is supplied as HTML files, available on-line or off-line as a single .zip file that you can install on a hard disk for rapid access, and also as a GNU gzipped file. These are the files in the /pub/sfl/doc directory:
sflbig21.tgz 184689 98/04/25 10:17:22 Gzip/tar archive sflbig21.zip 190376 98/07/25 23:28:36 ZIP archive sfldoc21.tgz 217445 98/04/25 10:17:18 Gzip/tar archive sfldoc21.zip 571527 98/07/25 23:28:36 ZIP archive
We recommend that you unzip or gunzip/detar the archive into a subdirectory. Point your browser at the index.htm file. We use relative addressing in all HTML documents, so that links work just as well on a local hard-disk as on-line on our website. In a windowing environment is it easy and useful to create an icon that runs a Web browser on this file.
To install the SFL on a UNIX system you need to:
To unzip the source .zip file, you need the Infozip unzip tool:
$ mkdir temp $ mv sflsrc21.zip temp $ cd temp $ unzip -a sflsrc21
To decompress the source .tgz file you need GNU gzip/gunzip and tar:
$ mkdir temp $ mv sflsrc21.tgz temp $ cd temp $ gzip -d sflsrc21.tgz or $ gunzip sflsrc21.tgz $ tar -xvf sflsrc21.tar
You can also, in extreme cases, unzip the files on a PC and transfer the individual files to the UNIX system.
The SFL source archive includes a script, c, that you can (and should) use to compile the SFL sources. This script invokes the ANSI C compiler to produce an object code file. It detects the platform and invokes the compiler with the necessary switches for ANSI C compilation. On some systems this is the normal behaviour for the cc command. On other systems it is not normal. You should make the c script executable, (preferrably) install it in a shared directory like /usr/local/bin, and try it out:
$ chmod a+rx c $ mv c /usr/local/bin $ c
To compile the SFL sources, use this command:
$ chmod +x build $ build
If you get warnings or error messages, this is usually a bad sign. Some compilers issue warnings just because you ask for ANSI compilation. If you get any other error messages, please let us know.
You can use individual SFL files simply by specifying them on the command line when you compile and link a program. However, this is usually a pain. Therefore, the build script creates a library file called libsfl.a. The linker can automatically search this file for the SFL functions. To install libsfl.a in the /usr/lib directory, do this:
$ mv libsfl.a /usr/lib
To use an SFL function in your applications you must include a header file that defines the structures, prototypes, and types for the function API. The SFL provides three types of header files:
We recommend that you install the sfl.h file in /usr/include. A typical application program starts like this:
#include <sfl.h>
To link an application program, use the c -l command. This assumes that libsfl.a is installed /usr/lib.
To install the SFL on a Digital VMS system you need to:
To unzip the source .zip file, you need the Infozip unzip tool (note that you need the -a switch):
$ create/dir [.temp] $ ren sflsrc21.zip [.temp] $ set def [.temp] $ unzip -a sflsrc21
You can also, in extreme cases, unzip the files on a PC and transfer the individual files to the VMS system.
To compile the SFL sources, use this command:
$ @build.txt
If you get warnings or error messages, this is a bad sign - please let us know.
The build.txt command file creates a library file called libsfl.olb. You can install this in a central directory like SYS$LIBRARY if you wish. You'll need system privileges to do this.
To use an SFL function in your applications you must include a header file that defines the structures, prototypes, and types for the function API. The SFL provides three types of header files:
We recommend that you install the sfl.h file in SYS$LIBRARY. A typical application program starts like this:
#include <sfl.h>
Briefly, either create a static library, and include that in your project; create a .DLL and call that, or add the files you want to use to your project and compile them as part of the application.
With MSVC 4.0, we find it useful to create a main project for the application in hand, and a subproject for the SFL. We build the SFL as a static library. If you use MFC, you must compile everything (including MFC) in single-threaded mode, and use libd.
Under MSVC 1.5x, there is a bug in the project manager that generates invalid make files: the SFL prelude.h file refers to various non-Windows include files, within #if statements. The MSVC 1.5x project manager includes these in the make file; you must manually remove them. One solution is to edit prelude.h; another is to use a Perl or Awk script to edit the make file each time you change the project. You could also move to a different 16-bit compiler. Finally, you can create the make files as empty files in the C include directory.
To install the SFL on a MS-DOS system you need to:
To unzip the source .zip file, you need the Infozip unzip tool, or PKzip version 2.04g or later, or a compatible unzip program.:
C:\DOWNLOAD> md temp C:\DOWNLOAD> copy sflsrc21.zip temp C:\DOWNLOAD> del sflsrc21.zip C:\DOWNLOAD> cd temp C:\DOWNLOAD> unzip sflsrc21
These build scripts are provided for MS-DOS:
The build scripts create a library file called libsfl.lib. You can install this, and sfl.h, in a central directory if you wish.
To use an SFL function in your applications you must include a header file that defines the structures, prototypes, and types for the function API. The SFL provides three types of header files:
We recommend that you install the sfl.h file in the /include directory of your compiler. A typical application program starts like this:
#include <sfl.h>
The Universal Header File a technology that we have developed to make C applications more easily portable with less effort. One of the big difficulties in compiling C code on different platforms is that header files change their names, locations, and internal functions from system to system, even on one system over time.
Typically, you may see C programs that start with a rash of #ifdef's mixed with #include's depending on the system, compiler, and specific needs of the program.
Since we are basically really lazy, all this unnecessary work is intolerable. We would much rather make the compiler work harder. The systems we develop on (typically MS-DOS with Turbo-C) are so fast that we can afford to take a really lazy approach.
So, what we do is this: we include every 'useful' and 'standard' header file that we can think of. We then include every 'useful' non-portable file that we've ever needed, in a clean way, so that application code does not need to 'know' how it was done.
At the same time we define lots of things that make life easier. Generally we don't like macros, since these create 'pseudo languages' that are just more work to learn. However, some things (like #define FOREVER for (;;)) are so useful and pretty comonplace, so we stick them in too.
Lastly, we flatten-out the problem called 'what system am I running on', by providing a set of definitions like __UNIX__ and __UTYPE_SUNOS that code can use if it has to. Again, it can be quite messy to figure-out that we're compiling on a Brand X, so we need this-and-that header file. We hide this so that we can forget about it.
Okay, those are the benefits of this approach. What are the costs? We typically hear these criticisms:
We use the Universal Header File in all C projects (not just those based on the SFL). If it was not for the simple fact that it has helped us a lot, we'd probably not make it available.
You should probably read through the prelude.h file to best understand it. Our usual habit is to comment the code first, so that it's self-explanatory. The SFL documentation has a section on the Universal Header File. This section is generated from the code.
When you use the SFL Library Header File (sfl.h), you don't need to include prelude.h, since it's already embedded in sfl.h. This makes application programming easier (just one header file to include). If you need to change prelude.h, you can either change sfl.h as well, or rebuild sfl.h using the build script. Better still, tell us what you want to change, so that we can maintain a single version of the file.
Each module in the SFL consists of a header file and one or more C source files. You can choose to include the header files that you want (this is what the SFL source code does), but this can quickly become burdensome. To simplify matters, a single header file sfl.h contains all the individual header files. It also contains the Universal Header File.
Most of the SFL is portable to MS-DOS. Exceptions are: the socket i/o functions (sflsock), the user/group ID functions (sfluid) and the server process functions (sflserv). These are all null; you can call any of the functions, but they will return either an okay feedback (in most cases) or an error feedback (for the socket functions). The SFL compiles cleanly with Borland Turbo C/C++ 1.0 and Microsoft VC++ 4.0; it has not been tested with Borland C/C++.
Most of the SFL is portable to Windows 3.x, Windows 95, and Windows NT. Exceptions are: the user/group ID functions (sfluid) and the server process functions (sflserv). These are all null; you can call any of the functions, but they will return an okay feedback. The SFL compiles cleanly with Microsoft VC++ 4.0; it has not been tested with Borland C/C++.
The SFL is portable to Digital VMS except for the directory access functions (sfldir), user/group functions (sfluid) and the server process functions (sflserv). The sfldir functions will be implemented at a later date. The other functions are null; you can call any of the functions, but they will return an okay feedback. The SFL compiles cleanly with Vax C and Dec C and has been tested with various releases of these compilers.
The SFL is fully portable to Linux and has been tested with GNU C. It should give no compiler warning errors.
The SFL is fully portable to Sun OS. You may have trouble finding an ANSI C compiler, especially on Sparc systems. People sometimes install GNU C, using the Sun header files and libraries. This should work, although we have not tested it recently. Sometimes the Sun ANSI C compiler is called acc, not cc. You can use the CCNAME environment variable to point to the right compiler name. Some Sun C compilers give warnings when you use the ANSI compile mode. You can ignore these warnings.
See also the warning about 'Other UNIX Systems'; some SunOS installations show this symptom.
The SFL is fully portable to HP/UX. It should give no compiler warning errors.
The SFL is fully portable to IBM/AIX. It should give no compiler warning errors.
The SFL is fully portable to Digital UNIX. It should give no compiler warning errors. When compiling on an Alpha system, the word size is 64 bits.
The directory functions can fail on SVr4 if the <dirent.h> file does not match the C library. Recompile with CCDEFINES set to the value "-D _USE_BSD_DIRENT" and they should work a bit better. Under Solaris with GCC, you should not define this macro.
The SFL was ported to OS/2 by Ewen McNiell around New Year's Eve 1996. It runs under EMX. The SFL distribution kit includes an OS/2 build script. The 'c' script runs under OS/2 as well as UNIX.
This license agreement covers your use of the iMatix STANDARD FUNCTION LIBRARY (SFL), its source code, documentation, and executable files, hereinafter referred to as "the Product".
The Product is Copyright © 1991-98 iMatix. You may use it and distribute it according to this following License Agreement. If you do not agree with these terms, please remove the Product from your system. By incorporating the Product in your work or distributing the Product to others you implicitly agree to these license terms.
The Product is, and remains, Copyright © 1991-98 iMatix, with exception of specific copyrights as noted in the individual source files.
You do not need to provide the source code for the Product as part of your product. However, you must do one of these things to comply with the Product License Agreement:
You may freely and at no cost use the Product in any project, commercial, academic, military, or private, so long as you respect the License Agreement. The License Agreement does not affect any software except the Product. In particular, any application that uses the Product does not itself fall under the License Agreement.
You may modify any part of the Product, including sources and documentation, except this License Agreement, which you may not modify.
You must clearly indicate any modifications at the start of each source file. The user of any modified Product code must know that the source file is not original.
At your discretion, you may rewrite or reuse any part of the
Product so that your derived code is not obviously part of the
Product. This derived code does not fall under the Product
License Agreement directly, but you must include a credit at the
start of each source file indicating the original authorship and
source of the code, and a statement of copyright as follows:
"Parts copyright (c) 1991-98 iMatix."
You may freely distribute the Product, or any subset of the Product, by any means. The License, in the form of the file called "LICENSE.TXT" must accompany any such distribution.
You may charge a fee for distributing the Product, for providing a warranty on the Product, for making modifications to the Product, or for any other service provided in relation to the Product. You are not required to ask our permission for any of these activities.
At no time will iMatix associate itself with any distribution of the Product except that supplied from the Internet site http://www.imatix.com.
The Product is provided as free software, in the hope that it will be useful. It is provided "as-is", without warranty of any kind, either expressed or implied, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose. The entire risk as to the quality and performance of the Product is with you. Should the Product prove defective, the full cost of repair, servicing, or correction lies with you.
Filename: prelude.h
Package: Standard Function Library (SFL)
Written: 93/03/29 iMatix SFL project team sfl@imatix.com
Revised: 98/06/27
Copyright: Copyright (c) 1991-98 iMatix
Version: 1.92
| 1.90 PH | Released with SFL 1.90 |
| 1.91 PH | 98/04/27 Added DRDOS LFN and DJGPP support - rj |
| 1.92 PH | 98/05/18 Added QNX support (provided by Alessandro Sala) |
This header file encapsulates many generally-useful include files and defines lots of good stuff. The intention of this header file is to hide the messy #ifdef's that you typically need to make real programs compile & run. To use, specify as the first include file in your program. The main contributors to this file were:
| PH | Pieter Hintjens <ph@imatix.com> |
| EDM | Ewen McNeill <ewen@imatix.com> |
| PA | Pascal Antonnaux <pascal@imatix.com> |
| BW | Bruce Walter <walter@fortean.com> |
| RJ | Rob Judd <judd@alphalink.com.au> |
prelude.h defines these symbols, possibly conditionally:
| Symbol: | Defined as: |
|---|---|
| ASSERT(f) | (various) |
| DEBUG | TRUE |
| DOES_SNPRINTF | (various) |
| DOES_SOCKETS | /* System supports BSD sockets */ |
| DOES_UID | /* System supports uid functions */ |
| EXIT_FAILURE | 1 /* GCC, sometimes. */ |
| EXIT_SUCCESS | 0 /* but not defined on SunOs with */ |
| FALSE | 0 |
| FOREVER | for (;;) /* FOREVER { ... } */ |
| FORK_CHILD | 0 |
| FORK_ERROR | -1 /* Return codes from fork() */ |
| LINE_MAX | 255 /* if not previously #define'd */ |
| MSDOS_FILESYSTEM | (various) |
| NAMEFOLD | (various) |
| O_BINARY | 0 |
| O_NDELAY | 0 |
| O_NONBLOCK | (various) |
| PATHEND | (various) |
| PATHFOLD | (various) |
| PATHSEP | (various) |
| PATH_MAX | 2048 /* if not previously #define'd */ |
| SIGABRT | 22 /* Termination by abort() */ |
| SIGALRM | (various) |
| SIGILL | 4 /* Illegal instruction */ |
| SIGINT | 2 /* Ctrl-C sequence */ |
| SIGSEGV | 11 /* Segment violation */ |
| SIGTERM | 15 /* Kill signal */ |
| TIMEZONE | (various) |
| TRUE | 1 /* ANSI standard */ |
| _INCLUDE_HPUX_SOURCE | TRUE |
| _INCLUDE_POSIX_SOURCE | TRUE |
| _INCLUDE_XOPEN_SOURCE | TRUE |
| _PRELUDE_INCLUDED | TRUE |
| __IS_32BIT__ | /* Else assume 32-bit OS/compiler */ |
| __IS_64BIT__ | (various) |
| __MSDOS__ | (various) |
| __OS2__ | TRUE |
| __STRICT_ANSI__ | TRUE |
| __UNIX__ | (various) |
| __UTYPE_AUX | TRUE |
| __UTYPE_BSDOS | TRUE |
| __UTYPE_DECALPHA | TRUE |
| __UTYPE_FREEBSD | TRUE |
| __UTYPE_GENERIC | TRUE |
| __UTYPE_HPUX | TRUE |
| __UTYPE_IBMAIX | TRUE |
| __UTYPE_IRIX | TRUE |
| __UTYPE_LINUX | TRUE |
| __UTYPE_MIPS | TRUE |
| __UTYPE_NETBSD | TRUE |
| __UTYPE_NEXT | TRUE |
| __UTYPE_QNX | TRUE |
| __UTYPE_SCO | TRUE |
| __UTYPE_SINIX | TRUE |
| __UTYPE_SUNOS | TRUE |
| __UTYPE_SUNSOLARIS | TRUE |
| __UTYPE_UNIXWARE | TRUE |
| __VMS_XOPEN | TRUE |
| __VMS__ | TRUE |
| __WINDOWS__ | (various) |
| bit_clr(x,bit) | ((x) &= ~bit_msk (bit)) |
| bit_msk(bit) | (1 << (bit)) |
| bit_set(x,bit) | ((x) |= bit_msk (bit)) |
| bit_tst(x,bit) | ((x) & bit_msk (bit)) |
| environ | _environ |
| local | static void /* Shorthand for local functions */ |
| max(a,b) | (((a) > (b))? (a): (b)) |
| memmove(d,s,l) | bcopy (s,d,l) |
| min(a,b) | (((a) < (b))? (a): (b)) |
| random(num) | (various) |
| randomize() | srand ((unsigned) time (NULL)) |
| sleep(a) | (various) |
| snprintf | _snprintf |
| strclr(s) | (*(s) = 0) |
| streq(s1,s2) | (!strcmp ((s1), (s2))) |
| strerror(n) | sys_errlist [n] |
| strlast(s) | ((s) [strlen (s) - 1]) |
| strneq(s1,s2) | (strcmp ((s1), (s2))) |
| strnull(s) | (*(s) == 0) |
| strterm(s) | ((s) [strlen (s)]) |
| strused(s) | (*(s) != 0) |
| tbllast(x) | (x [tblsize (x) - 1]) |
| tblsize(x) | (sizeof (x) / sizeof ((x) [0])) |
| until(expr) | while (!(expr)) /* do { ... } until (expr) */ |
| vsnprintf | _vsnprintf |
| Type name: | Defined as: |
|---|---|
| Bool | unsigned short |
| byte | unsigned char |
| dbyte | unsigned short |
| dword | unsigned long |
| function | void (*) (void) |
| gid_t | int |
| qbyte | (various) |
| uid_t | int |
| word | unsigned short |
Filename: sflvers.h
Package: Standard Function Library (SFL)
Written: 96/11/21 iMatix SFL project team sfl@imatix.com
Revised: 98/10/02
Copyright: Copyright (c) 1991-98 iMatix
Defines the SFL_VERSION constant.
sflvers.h defines these symbols, possibly conditionally:
| Symbol: | Defined as: |
|---|---|
| SFL_VERSION | "2.01" /* Main SFL version */ |
| _SFLVERS_INCLUDED | TRUE |
Filename: sflbits.h
Package: Standard Function Library (SFL)
Written: 96/05/14 iMatix SFL project team sfl@imatix.com
Revised: 97/09/08
Copyright: Copyright (c) 1991-98 iMatix
Provides operations to manipulate large bitstrings. The bitstrings are compressed. Intended for bit-based index techniques, where bitstrings can be millions of bits long. These functions are still in development; this is an early version that provides basic functionality. Simple tests on large bitmaps with random filling show a cost of about 3 bytes per bit, after compression. This includes all the indexing information.
sflbits.h defines these symbols, possibly conditionally:
| Symbol: | Defined as: |
|---|---|
| BIT_DATASIZE | 500 /* Size of block data part */ |
| BIT_INDEXSIZE | BIT_DATASIZE/2 /* Size of block index part */ |
| BIT_MAXBITS | 16384000L /* Max. possible bit number */ |
| BIT_MAXBLOCKS | 1024 /* Max. size of bitstring */ |
| BIT_SECTSIZE | 8192 /* Size of one bitstring section */ |
| _SFLBITS_INCLUDED | TRUE |
#include "sflbits.h" int bits_init (void)
Initialises bitstring functions. You must call this before using any other bitstring functions. Returns 0 if okay, -1 if there was an error.
{
ASSERT (comp_zero == NULL);
comp_zero = mem_alloc (BIT_SECTSIZE + 1);
if (!comp_zero)
return (-1); /* Could not allocate new block */
memset (compressed, BIT_SECTSIZE, 0x00);
comp_zero_size = compress bits (compressed, comp_zero, BIT_SECTSIZE);
comp_zero = mem_realloc (comp_zero, comp_zero_size);
comp_ones = mem_alloc (BIT_SECTSIZE + 1);
if (!comp_ones)
{
mem_free (comp_ones);
return (-1); /* Could not allocate new block */
}
memset (compressed, BIT_SECTSIZE, 0xFF);
comp_ones_size = compress bits (compressed, comp_ones, BIT_SECTSIZE);
comp_ones = mem_realloc (comp_ones, comp_ones_size);
return (0);
}
#include "sflbits.h" int bits_term (void)
Terminates bitstring functions. You must call this when you are finished using the bitstring functions. Returns 0 if okay, -1 if there was an error.
{
mem_free (comp_zero);
mem_free (comp_ones);
return (0);
}
#include "sflbits.h" BITS * bits_create (void)
Creates a new bitstring and initialises all bits to zero. Returns a BITS handle which you should use in all further references to the bitstring.
{
BITS
*bits; /* Newly-created bitstring */
BITBLOCK
*index; /* Newly-created index block */
bits = mem_alloc (sizeof (BITS));
if (bits)
{
memset (bits, 0, sizeof (BITS));
index = mem_alloc (sizeof (BITBLOCK));
if (index)
{
/* Set all index fields to 0: bitstring is all zeroes */
memset (index, 0, sizeof (BITBLOCK));
index-> left = 0;
index-> right = 0;
index-> size = BIT_DATASIZE;
bits-> block [0] = index;
bits-> block_count = 1;
bits-> free_list = 0; /* No blocks in free list */
}
else
{
mem_free (bits);
bits = NULL;
}
}
return (bits);
}
#include "sflbits.h"
void
bits_destroy (
BITS *bits)
Releases all memory used by a bitstring and deletes the bitstring. Do not refer to the bitstring after calling this function.
{
int
block_nbr; /* Bitstring block number */
ASSERT (bits);
/* Free all blocks allocated to bitmap */
for (block_nbr = 0; block_nbr < bits-> block_count; block_nbr++)
mem_free (bits-> block [block_nbr]);
mem_free (bits);
}
#include "sflbits.h"
int
bits_set (
BITS *bits,
long bit)
Sets the specified bit in the bitmap. Returns ?
{
int
index, /* Number of index block */
section; /* Number of section in index */
dbyte
bit_nbr; /* Number of bit in section */
ASSERT (bits);
locate_bit (bits, bit, &index, §ion, &bit_nbr);
get_section (bits, index, section, section_data, TRUE);
section_data [bit_nbr / 8] |= 1 << (bit_nbr % 8);
put_section (bits, index, section, section_data);
return 0;
}
#include "sflbits.h"
int
bits_clear (
BITS *bits,
long bit)
Clears the specified bit in the bitmap. Returns ?
{
int
index, /* Number of index block */
section; /* Number of section in index */
dbyte
bit_nbr; /* Number of bit in section */
ASSERT (bits);
locate_bit (bits, bit, &index, §ion, &bit_nbr);
get_section (bits, index, section, section_data, TRUE);
section_data [bit_nbr / 8] &= 255 - (1 << (bit_nbr % 8));
put_section (bits, index, section, section_data);
return 0;
}
#include "sflbits.h"
int
bits_test (
const BITS *bits,
long bit)
Tests the specified bit in the bitmap. Returns 1 or 0.
{
int
index, /* Number of index block */
section; /* Number of section in index */
dbyte
bit_nbr; /* Number of bit in section */
ASSERT (bits);
locate_bit (bits, bit, &index, §ion, &bit_nbr);
get_section ((BITS *) bits, index, section, section_data, FALSE);
if ((section_data [bit_nbr / 8]) & (1 << (bit_nbr % 8)))
return (1);
else
return (0);
}
#include "sflbits.h"
int
bits_fput (FILE *file,
const BITS *bits)
Writes the bitstring to the specified file stream. To read the bitstring, use the bits fget() function. The structure of the bitstring is:
{
int
block_nbr; /* Bitstring block number */
word
comp_size; /* Size of compressed block */
BITBLOCK
*block_ptr; /* Points to bitstring block */
ASSERT (bits);
ASSERT (file);
/* Write bitstring header to file */
fwrite (&bits-> block_count, sizeof (bits-> block_count), 1, file);
fwrite (&bits-> free_list, sizeof (bits-> free_list), 1, file);
/* Write bitstring blocks to file */
for (block_nbr = 0; block_nbr < bits-> block_count; block_nbr++)
{
block_ptr = bits-> block [block_nbr];
comp_size = compress block ((byte *) block_ptr,
compressed, (word) block_ptr-> size);
fwrite (&comp_size, sizeof (comp_size), 1, file);
fwrite (compressed, comp_size, 1, file);
}
return 0;
}
#include "sflbits.h" BITS * bits_fget (FILE *file)
Reads a bitstring from the specified file stream. You must have previously written the bitstring using bit_fput (). Returns a newly-created bitmap, or NULL if there was insufficient memory.
{
int
block_nbr; /* Bitstring block number */
word
comp_size; /* Size of compressed block */
BITBLOCK
*block_ptr; /* Points to bitstring block */
BITS
*bits;
ASSERT (file);
bits = bits create (); /* Create a new, empty bitmap */
/* Read bitstring header from file */
fread (&bits-> block_count, sizeof (bits-> block_count), 1, file);
fread (&bits-> free_list, sizeof (bits-> free_list), 1, file);
/* Read bitstring blocks from file */
for (block_nbr = 0; block_nbr < bits-> block_count; block_nbr++)
{
block_nbr = alloc_block (bits);
if (block_nbr == 0)
{
bits destroy (bits);
return (NULL);
}
fread (&comp_size, sizeof (comp_size), 1, file);
fread (compressed, comp_size, 1, file);
block_ptr = bits-> block [block_nbr];
block_ptr-> size = expand block (compressed, (byte *) block_ptr,
comp_size);
}
return (bits);
}
Filename: sflcomp.h
Package: Standard Function Library (SFL)
Written: 91/05/20 iMatix SFL project team sfl@imatix.com
Revised: 97/09/08
Copyright: Copyright (c) 1991-98 iMatix
Various compression/decompression functions. The LZ-type algorith (LZRW1/KH) was originally written by Kurt Haenen <ghgaea8@blekul11> and made portable by P. Hintjens. This is a reasonable LZ/RLE algorithm, very fast, but about 30% less efficient than a ZIP-type algorithm in terms of space. The RLE algorithms are better suited to compressing sparse data. The nulls variant is specifically tuned to data that consists mostly of binary zeroes. The bits variant is tuned for compressing sparse bitmaps.
sflcomp.h defines these symbols, possibly conditionally:
| Symbol: | Defined as: |
|---|---|
| _SFLCOMP_INCLUDED | TRUE |
#include "sflcomp.h"
word
compress_block (
const byte *src,
byte *dst,
word src_size)
Takes up to 64Kb of uncompressed data in Source, compresses it using a fast LZ/RLE algorithm and places the result in Dest. The compression technique is comparable to that used by Zip and such tools, but less agressive. It is, however, fast enough to use in realtime. Returns the size of the compressed data. To decompress the data, use the expand block() function.
{
static short
Hash [4096];
short SymbolAddress;
word Key;
word Size;
byte Bit = 0;
word Command = 0;
word src_index = 0;
word dst_size = 3;
word HeaderIndex = 1;
dst [0] = FLAG_COMPRESS;
for (Key = 0; Key < 4096; Key++)
Hash [Key] = -1;
while ((src_index < src_size) && (dst_size <= src_size))
{
if (Bit > 15)
{
dst [HeaderIndex] = (byte) ((Command >> 8) & 0x00ff);
dst [HeaderIndex + 1] = (byte) ( Command & 0x00ff);
HeaderIndex = dst_size;
dst_size += 2;
Bit = 0;
}
for (Size = 1;; Size++)
if ((word) (src_index + Size) >= src_size
|| (src [src_index] != src [src_index + Size])
|| (Size >= 0x0fff))
break;
if (Size >= 16)
{
dst [dst_size++] = 0;
dst [dst_size++] = (byte) (((word) (Size - 16) >> 8) & 0x00ff);
dst [dst_size++] = (byte) ((Size - 16) & 0x00ff);
dst [dst_size++] = src [src_index];
src_index += Size;
Command = (Command << 1) + 1;
}
else
if (get_match (src, src_index, src_size,
Hash, &Size, &SymbolAddress) != 0)
{
Key = ((src_index - SymbolAddress) << 4) + (Size - 3);
dst [dst_size++] = (byte) ((Key >> 8) & 0x00ff);
dst [dst_size++] = (byte) (Key & 0x00ff);
src_index += Size;
Command = (Command << 1) + 1;
}
else
{
dst [dst_size++] = src [src_index++];
Command = (Command << 1);
}
Bit++;
}
Command <<= (16 - Bit);
dst [HeaderIndex] = (byte) ((Command >> 8) & 0x00ff);
dst [HeaderIndex + 1] = (byte) ( Command & 0x00ff);
if (dst_size > src_size)
{
for (dst_size = 0; dst_size < src_size; dst_size++)
dst [dst_size + 1] = src [dst_size];
dst [0] = FLAG_COPY;
return (src_size + 1);
}
return (dst_size);
}
#include "sflcomp.h"
word
expand_block (
const byte *src,
byte *dst,
word src_size)
Expands a block of data previously compressed using the compress block() function. The compressed block is passed in src; the expanded result in dst. dst must be large enough to accomodate the largest possible decompressed block. Returns the size of the uncompressed data.
{
word SymbolAddress;
word ChunkSize;
word Counter;
word Command = 0;
word src_index = 1;
word dst_size = 0;
byte Bit = 0;
if (src [0] == FLAG_COPY)
{
for (dst_size = 1; dst_size < src_size; dst_size++)
dst [dst_size - 1] = src [dst_size];
return (src_size - 1);
}
while (src_index < src_size)
{
if (Bit == 0)
{
Command = src [src_index++] << 8;
Command += src [src_index++];
Bit = 16;
}
if (Command & 0x8000)
{
SymbolAddress = (word) (src [src_index++] << 4);
SymbolAddress += (word) (src [src_index] >> 4);
if (SymbolAddress)
{
ChunkSize = (word) (src [src_index++] & 0x0f) + 3;
SymbolAddress = dst_size - SymbolAddress;
for (Counter = 0; Counter < ChunkSize; Counter++)
dst [dst_size++] = dst [SymbolAddress++];
}
else
{
ChunkSize = (word) (src [src_index++] << 8);
ChunkSize += (word) (src [src_index++] + 16);
for (Counter = 0; Counter < ChunkSize; Counter++)
dst [dst_size++] = src [src_index];
src_index++;
}
}
else
dst [dst_size++] = src [src_index++];
Command <<= 1;
Bit--;
}
return (dst_size);
}
#include "sflcomp.h"
word
compress_rle (
byte *src,
byte *dst,
word src_size)
Takes a block of uncompressed data in src, compresses it using a RLE algorithm and places the result in dst. To decompress the data, use the expand rle () function. Returns the size of the compressed data. The dst buffer should be 10% larger than the src buffer. The src buffer must be at least src_size + 1 bytes long. It may be modified. The compressed data contains these strings:
| [01-7F][data...] | String of uncompressed data, 1 to 127 bytes. |
| [83-FF][byte] | Run of 3 to 127 identical bytes. |
| [80][len][byte] | Run of 128 to 255 identical bytes. |
| [81][lo][hi][byte] | Run of 256 to 2^16 identical bytes. |
| [82][len] | Run of 3 to 255 spaces. |
| [00][len] | Run of 3 to 255 binary zeroes. |
{
word
dst_size, /* Size of compressed data */
src_scan, /* Scan through source data */
run_end, /* Points to end of run of bytes */
length = 0; /* Size of the run or string */
byte
cur_byte, /* Next byte to process */
*header; /* Header of unpacked string */
Bool
have_run; /* TRUE when we have a run */
src_scan = 0; /* Start at beginning of source */
dst_size = 0; /* No output yet */
header = NULL; /* No open unpacked string */
while (src_scan < src_size)
{
cur_byte = src [src_scan++];
have_run = FALSE; /* Unless we find a run */
/* Three identical bytes signals the start of a run */
if (cur_byte == src [src_scan]
&& cur_byte == src [src_scan + 1]
&& (src_scan + 1 < src_size))
{
/* Stick-in a sentinel character to ensure that the run ends */
src [src_size] = !cur_byte;
run_end = src_scan; /* src_scan <= src_size */
while (src [run_end] == cur_byte)
run_end++;
have_run = TRUE;
if (header) /* If we have a previous unpacked */
{ /* string, close it */
*header = (byte) length;
header = NULL;
}
length = run_end - src_scan + 1;
src_scan = run_end;
}
if (have_run)
{
/* We compress short runs of spaces and nulls separately */
if (length < 256 && cur_byte == 0)
{
dst [dst_size++] = 0x00;
dst [dst_size++] = (byte) length;
}
else
if (length < 256 && cur_byte == ' ')
{
dst [dst_size++] = 0x82;
dst [dst_size++] = (byte) length;
}
else
if (length < 128)
{
dst [dst_size++] = (byte) length | 0x80;
dst [dst_size++] = cur_byte;
}
else
if (length < 256) /* Short run 128-255 bytes */
{
dst [dst_size++] = 0x80;
dst [dst_size++] = (byte) length;
dst [dst_size++] = cur_byte;
}
else /* Long run 256-2^16 bytes */
{
dst [dst_size++] = 0x81;
dst [dst_size++] = (byte) (length & 0xff);
dst [dst_size++] = (byte) (length >> 8);
dst [dst_size++] = cur_byte;
}
}
else
{
if (!header) /* Start new unpacked string if */
{ /* necessary */
header = &dst [dst_size++];
length = 0;
}
dst [dst_size++] = cur_byte;
if (++length == 127) /* Each string can be up to 127 */
{ /* bytes long (high bit cleared) */
*header = (byte) length;
header = NULL;
}
}
}
if (header) /* If we have a previous unpacked */
{ /* string, close it */
*header = (byte) length;
header = NULL;
}
return (dst_size); /* Return compressed data size */
}
#include "sflcomp.h"
word
expand_rle (
const byte *src,
byte *dst,
word src_size)
Expands a block of data previously compressed using the compress rle() function. The compressed block is passed in src; the expanded result in dst. Dst must be large enough to accomodate the largest possible decompressed block. Returns the size of the expanded data.
{
word
dst_size, /* Size of expanded data */
src_scan, /* Scan through source data */
length; /* Size of the run or string */
byte
cur_byte; /* Next byte to process */
src_scan = 0;
dst_size = 0;
while (src_scan < src_size)
{
cur_byte = src [src_scan++];
/* 1 to 127 is uncompressed string of 1 to 127 bytes */
if (cur_byte > 0 && cur_byte < 128)
{
length = (word) cur_byte;
memcpy (dst + dst_size, src + src_scan, length);
src_scan += length;
dst_size += length;
}
else /* Run of 3 or more bytes */
{
switch (cur_byte)
{
case 0x00: /* Run of 3-255 zeroes */
length = src [src_scan++];
cur_byte = 0;
break;
case 0x82: /* Run of 3-255 spaces */
length = src [src_scan++];
cur_byte = ' ';
break;
case 0x80: /* Short run 128-255 bytes */
length = src [src_scan++];
cur_byte = src [src_scan++];
break;
case 0x81: /* Long run 256-2^16 bytes */
length = src [src_scan++];
length += src [src_scan++] << 8;
cur_byte = src [src_scan++];
break;
default: /* Run of 3 to 127 bytes */
length = cur_byte & 127;
cur_byte = src [src_scan++];
}
memset (dst + dst_size, cur_byte, length);
dst_size += length;
}
}
return (dst_size); /* Return expanded data size */
}
#include "sflcomp.h"
word
compress_nulls (
byte *src,
byte *dst,
word src_size)
Similar to compress rle(), but optimised towards compression of binary zeroes. Use this when you are certain that the sparse areas are set to binary zeroes. You must use expand nulls () to decompress a block compressed with this function. Returns the size of the compressed data. The dst buffer should be 10% larger than the src buffer. The src buffer must be at least src_size + 1 bytes long. It may be modified. The compressed data contains these strings:
| [01-7F][data...] | String of uncompressed data, 1 to 127 bytes. |
| [82-FF] | Run of 2 to 127 binary zeroes. |
| [81][80-FF] | Run of 128 to 255 binary zeroes. |
| [80][lo][hi] | Run of 256 to 2^16 binary zeroes. |
| [00][len][byte] | Run of 4 to 255 identical bytes. |
| [00][00][lo][hi][byte] | Run of 256 to 2^16 identical bytes. |
{
word
dst_size, /* Size of compressed data */
src_scan, /* Scan through source data */
run_end, /* Points to end of run of bytes */
length = 0; /* Size of the run or string */
byte
cur_byte, /* Next byte to process */
*header; /* Header of unpacked string */
Bool
have_run; /* TRUE when we have a run */
src_scan = 0; /* Start at beginning of source */
dst_size = 0; /* No output yet */
header = NULL; /* No open unpacked string */
while (src_scan < src_size)
{
cur_byte = src [src_scan++];
have_run = FALSE; /* Unless we find a run */
/* Two identical bytes may signal the start of a run */
if (cur_byte == src [src_scan]
&& src_scan < src_size)
{
/* Stick-in a sentinel character to ensure that the run ends */
src [src_size] = !cur_byte;
run_end = src_scan; /* src_scan <= src_size */
while (src [run_end] == cur_byte)
run_end++;
/* A run is 4+ identical bytes or 2+ nulls */
if ((run_end - src_scan > 2) || cur_byte == 0)
{
have_run = TRUE;
if (header) /* If we have a previous unpacked */
{ /* string, close it */
*header = (byte) length;
header = NULL;
}
length = run_end - src_scan + 1;
src_scan = run_end;
}
}
if (have_run)
{
if (cur_byte == 0)
{
if (length < 128) /* 2-127 binary zeroes */
dst [dst_size++] = (byte) (length | 0x80);
else
if (length < 256) /* 128-256 binary zeroes */
{
dst [dst_size++] = 0x81;
dst [dst_size++] = (byte) length;
}
else /* 256-2^15 binary zeroes */
{
dst [dst_size++] = 0x80;
dst [dst_size++] = (byte) (length & 0xff);
dst [dst_size++] = (byte) (length >> 8);
}
}
else
if (length < 256) /* Short run 4-255 bytes */
{
dst [dst_size++] = 0x00;
dst [dst_size++] = (byte) length;
dst [dst_size++] = cur_byte;
}
else /* Long run 256-2^16 bytes */
{
dst [dst_size++] = 0x00;
dst [dst_size++] = 0x00;
dst [dst_size++] = (byte) (length & 0xff);
dst [dst_size++] = (byte) (length >> 8);
dst [dst_size++] = cur_byte;
}
}
else
{
if (!header) /* Start new unpacked string if */
{ /* necessary */
header = &dst [dst_size++];
length = 0;
}
dst [dst_size++] = cur_byte;
if (++length == 127) /* Each string can be up to 127 */
{ /* bytes long (high bit cleared) */
*header = (byte) length;
header = NULL;
}
}
}
if (header) /* If we have a previous unpacked */
{ /* string, close it */
*header = (byte) length;
header = NULL;
}
return (dst_size); /* Return compressed data size */
}
#include "sflcomp.h"
word
expand_nulls (
const byte *src,
byte *dst,
word src_size)
Expands a block of data previously compressed using the compress nulls() function. The compressed block is passed in src; the expanded result in dst. Dst must be large enough to accomodate the largest possible decompressed block. Returns the size of the expanded data.
{
word
dst_size, /* Size of expanded data */
src_scan, /* Scan through source data */
length; /* Size of the run or string */
byte
cur_byte; /* Next byte to process */
src_scan = 0;
dst_size = 0;
while (src_scan < src_size)
{
cur_byte = src [src_scan++];
/* 1 to 127 is uncompressed string of 1 to 127 bytes */
if (cur_byte > 0 && cur_byte < 128)
{
length = (word) cur_byte;
memcpy (dst + dst_size, src + src_scan, length);
src_scan += length;
dst_size += length;
}
else /* Run of 2 or more bytes */
{
switch (cur_byte)
{
case 0x00: /* Run of non-zero bytes */
length = src [src_scan++];
if (length == 0) /* Stored as double-byte */
{
length = src [src_scan++];
length += src [src_scan++] << 8;
}
cur_byte = src [src_scan++];
break;
case 0x80: /* 256-2^16 zeroes */
length = src [src_scan++];
length += src [src_scan++] << 8;
cur_byte = 0;
break;
case 0x81: /* 128 to 255 zeroes */
length = src [src_scan++];
cur_byte = 0;
break;
default: /* 2 to 127 zeroes */
length = cur_byte & 127;
cur_byte = 0;
}
memset (dst + dst_size, cur_byte, length);
dst_size += length;
}
}
return (dst_size); /* Return expanded data size */
}
#include "sflcomp.h"
word
compress_bits (
byte *src,
byte *dst,
word src_size)
Similar to compress rle(), but optimised towards compression of sparse bitmaps. Use this when you are playing with large, sparse bitmaps. You must use expand bits () to decompress a block compressed with this function. Returns the size of the compressed data. The dst buffer should be 10% larger than the src buffer for worst cases. The src buffer must be at least src_size + 1 bytes long. It may be modified. The compressed data contains these strings:
| [00-07] | Single byte containing a bit in position 0 to 7. |
| [08-7F][data...] | String of uncompressed data, 1 to 120 bytes. |
| [82-FF] | Run of 1 to 126 binary zeroes. |
| [81][00-FD] | Run of 127 to 380 binary zeroes. |
| [81][FE][len][byte] | Run of 4 to 255 identical bytes. |
| [81][FF][lo][hi][byte] | Run of 256 to 2^16 identical bytes. |
| [80][lo][hi] | Run of 381 to 2^16 binary zeroes. |
{
word
dst_size, /* Size of compressed data */
src_scan, /* Scan through source data */
run_end, /* Points to end of run of bytes */
length = 0; /* Size of the run or string */
byte
cur_byte, /* Next byte to process */
*header; /* Header of unpacked string */
static byte
single_bits [256]; /* Bytes with one bit set */
static Bool
initialised = FALSE; /* First time flag */
/* The single_bits table provides a fast lookup for bytes with */
/* one bit set. The 'interesting' bytes are non-zero in the table */
/* where their value is the output code value (0-7) + 1. */
if (!initialised) /* First time? Initialise */
{
memset (single_bits, 0, 256);
single_bits [1] = 1;
single_bits [2] = 2;
single_bits [4] = 3;
single_bits [8] = 4;
single_bits [16] = 5;
single_bits [32] = 6;
single_bits [64] = 7;
single_bits [128] = 8;
}
src_scan = 0; /* Start at beginning of source */
dst_size = 0; /* No output yet */
header = NULL; /* No open unpacked string */
while (src_scan < src_size)
{
cur_byte = src [src_scan++];
/*- Look for 1 or more binary zeroes, and compress into a run -------*/
if (cur_byte == 0)
{
src [src_size] = 0xff; /* Stop with a sentinel */
run_end = src_scan; /* src_scan <= src_size */
while (src [run_end] == 0)
run_end++;
if (header) /* If we have a previous unpacked */
{ /* string, close it */
*header = (byte) length + 7;
header = NULL;
}
length = run_end - src_scan + 1;
src_scan = run_end;
if (length < 127) /* 1-126 binary zeroes */
dst [dst_size++] = (byte) (++length | 0x80);
else
if (length < 381) /* 127-380 binary zeroes */
{
dst [dst_size++] = 0x81;
dst [dst_size++] = (byte) length - 127;
}
else /* 381-2^16 binary zeroes */
{
dst [dst_size++] = 0x80;
dst [dst_size++] = (byte) (length & 0xff);
dst [dst_size++] = (byte) (length >> 8);
}
}
else
/*- Next, look for bytes with 1 bit set; we encode these as 1 byte --*/
if (single_bits [cur_byte]) /* Single bit value? */
{
dst [dst_size++] = single_bits [cur_byte] - 1;
if (header) /* If we have a previous unpacked */
{ /* string, close it */
*header = (byte) length + 7;
header = NULL;
}
}
else
/*- Next, look for a run of 4 or more identical (non-zero) bytes ----*/
if (cur_byte == src [src_scan]
&& cur_byte == src [src_scan + 1]
&& cur_byte == src [src_scan + 2]
&& (src_scan < src_size - 2))
{
src [src_size] = !cur_byte; /* Stick in a sentinel byte */
run_end = src_scan; /* src_scan <= src_size */
while (src [run_end] == cur_byte)
run_end++;
if (header) /* If we have a previous unpacked */
{ /* string, close it */
*header = (byte) length + 7;
header = NULL;
}
length = run_end - src_scan + 1;
src_scan = run_end;
if (length < 256) /* Short run 4-255 bytes */
{
dst [dst_size++] = 0x81;
dst [dst_size++] = 0xFE;
dst [dst_size++] = (byte) length;
dst [dst_size++] = cur_byte;
}
else /* Long run 256-2^16 bytes */
{
dst [dst_size++] = 0x81;
dst [dst_size++] = 0xFF;
dst [dst_size++] = (byte) (length & 0xff);
dst [dst_size++] = (byte) (length >> 8);
dst [dst_size++] = cur_byte;
}
}
else
/*- Lastly, compress unpackable strings into chunks of 120 bytes ----*/
{
if (!header) /* Start new unpacked string if */
{ /* necessary */
header = &dst [dst_size++];
length = 0;
}
dst [dst_size++] = cur_byte;
if (++length == 120) /* Each string can be up to 120 */
{ /* bytes long (high bit cleared) */
*header = (byte) length + 7;
header = NULL;
}
}
}
if (header) /* If we have a previous unpacked */
{ /* string, close it */
*header = (byte) length + 7;
header = NULL;
}
return (dst_size); /* Return compressed data size */
}
#include "sflcomp.h"
word
expand_bits (
const byte *src,
byte *dst,
word src_size)
Expands a block of data previously compressed using the compress bits() function. The compressed block is passed in src; the expanded result in dst. Dst must be large enough to accomodate the largest possible decompressed block. Returns the size of the expanded data.
{
word
dst_size, /* Size of expanded data */
src_scan, /* Scan through source data */
length; /* Size of the run or string */
byte
cur_byte; /* Next byte to process */
src_scan = 0;
dst_size = 0;
while (src_scan < src_size)
{
cur_byte = src [src_scan++];
if (cur_byte < 8) /* Single bit in position 0 to 7 */
dst [dst_size++] = 1 << cur_byte;
else
if (cur_byte < 128) /* String of 1 to 120 bytes */
{
length = (word) cur_byte - 7;
memcpy (dst + dst_size, src + src_scan, length);
src_scan += length;
dst_size += length;
}
else /* Run of 1 or more bytes */
{
switch (cur_byte)
{
case 0x80: /* 381-2^16 binary zeroes */
length = src [src_scan++];
length += src [src_scan++] << 8;
cur_byte = 0;
break;
case 0x81:
length = src [src_scan++];
if (length == 0xFE) /* 4-255 non-zero bytes */
{
length = src [src_scan++];
cur_byte = src [src_scan++];
}
else
if (length == 0xFF) /* Run of 256-2^15 non-zero bytes */
{
length = src [src_scan++];
length += src [src_scan++] << 8;
cur_byte = src [src_scan++];
}
else
{
length += 127;
cur_byte = 0; /* 127 to 380 zeroes */
}
break;
default: /* 1 to 126 zeroes */
length = (cur_byte - 1) & 127;
cur_byte = 0;
}
memset (dst + dst_size, cur_byte, length);
dst_size += length;
}
}
return (dst_size); /* Return expanded data size */
}
Filename: sflcons.h
Package: Standard Function Library (SFL)
Written: 97/05/22 iMatix SFL project team sfl@imatix.com
Revised: 98/02/08
Copyright: Copyright (c) 1991-98 iMatix
Provides redirectable console output: use the coprintf() and coputs() calls instead of printf() and puts() in a real-time application. Then, you can call console send() to send all console output to a specified function. This is a useful way to get output into -- for example -- a GUI window.
sflcons.h defines these symbols, possibly conditionally:
| Symbol: | Defined as: |
|---|---|
| _SFLCONS_INCLUDED | TRUE |
| Type name: | Defined as: |
|---|---|
| CONSOLE_FCT | void () (const char *) |
#include "sflcons.h" void console_send (CONSOLE_FCT *new_console_fct, Bool echo)
Redirects console output to a specified CONSOLE_FCT function. If the specified address is NULL, redirects back to the stdout stream. This is independent of any console capturing in progress. If the echo argument is TRUE, console output is also sent to stdout.
{
console_fct = new_console_fct;
console_echo = echo; /* Copy to stdout */
}
#include "sflcons.h" void console_enable (void)
Enables console output. Use together with console disable() to stop and start console output.
{
console_active = TRUE;
}
#include "sflcons.h" void console_disable (void)
Disables console output. Use together with console enable() to stop and start console output.
{
console_active = FALSE;
}
#include "sflcons.h" void console_set_mode (int mode)
Sets console display mode; the argument can be one of:
| CONSOLE PLAIN | Output text exactly as specified |
| CONSOLE DATETIME | Prefix text by "yy/mm/dd hh:mm:ss " |
| CONSOLE TIME | Prefix text by "hh:mm:ss " |
{
ASSERT (mode == CONSOLE_PLAIN
|| mode == CONSOLE_DATETIME
|| mode == CONSOLE_TIME);
console_mode = mode;
}
#include "sflcons.h" int console_capture (const char *filename, char mode)
Starts capturing console output to the specified file. If the mode is 'w', creates an empty capture file. If the mode is 'a', appends to any existing data. Returns 0 if okay, -1 if there was an error - in this case you can test the value of errno. If the filename is NULL or an empty string, closes any current capture file.
{
if (console_file)
{
file close (console_file);
console_file = NULL;
}
if (filename && *filename)
{
ASSERT (mode == 'w' || mode == 'a');
console_file = file open (filename, mode);
if (console_file == NULL)
return (-1);
}
return (0);
}
#include "sflcons.h" int coprintf (const char *format, ...)
As printf() but sends output to the current console. This is by default the stdout device, unless you used console send() to direct console output to some function. A newline is added automatically.
{
static char
formatted [LINE_MAX];
va_list
argptr; /* Argument list pointer */
int
fmtsize = 0; /* Size of formatted line */
char
*prefixed = NULL; /* Prefixed formatted line */
if (console_active)
{
va_start (argptr, format); /* Start variable args processing */
#if (defined (DOES_SNPRINTF))
fmtsize = vsnprintf (formatted, LINE_MAX, format, argptr);
#else
fmtsize = vsprintf (formatted, format, argptr);
#endif
va_end (argptr); /* End variable args processing */
ASSERT (fmtsize < LINE_MAX);
switch (console_mode)
{
case CONSOLE_DATETIME:
prefixed = xstrcpy (NULL, date_str (), " ", time_str (), ": ",
formatted, NULL);
break;
case CONSOLE_TIME:
prefixed = xstrcpy (NULL, time_str (), ": ", formatted, NULL);
break;
}
if (console_file)
{
file write (console_file, prefixed? prefixed: formatted);
fflush (console_file);
}
if (console_fct)
(console_fct) (prefixed? prefixed: formatted);
if (console_echo)
{
fprintf (stdout, prefixed? prefixed: formatted);
fprintf (stdout, "\n");
fflush (stdout);
}
if (prefixed)
{
fmtsize = strlen (prefixed);
mem_free (prefixed);
}
}
return (fmtsize);
}
#include "sflcons.h" int coputs (const char *string)
As puts() but sends output to the current console. This is by default the stdout device, unless you used console send() to direct console output to some function.
{
coprintf (string);
return (1);
}
#include "sflcons.h" int coputc (int character)
As putc() but sends output to the current console. This is by default the stdout device, unless you used console send() to direct console output to some function.
{
char
buffer [2];
if (console_active)
{
if (console_file)
{
putc (character, console_file);
fflush (console_file);
}
if (console_fct)
{
buffer [0] = (char) character;
buffer [1] = '\0';
(console_fct) (buffer);
}
if (console_echo)
{
putc (character, stdout);
fflush (stdout);
}
}
return (character);
}
Filename: sflconv.h
Package: Standard Function Library (SFL)
Written: 95/12/17 iMatix SFL project team sfl@imatix.com
Revised: 97/09/08
Copyright: Copyright (c) 1991-98 iMatix
These functions provide conversion between a set of datatypes (dates, times, numbers, Booleans) and external strings that represent the values. The objective is to format datatypes for display or printing, and to validate and convert strings supplied by the user. Conversion is controlled by a set of options specific to each datatype. Additionally, dates and times may be formatted using picture strings. The functions were written for use in an interactive 'forms' environment.
sflconv.h defines these symbols, possibly conditionally:
| Symbol: | Defined as: |
|---|---|
| BOOL_1_0 | 4 |
| BOOL_TRUE_FALSE | 2 |
| BOOL_T_F | 3 |
| BOOL_YES_NO | 0 /* Boolean field formatting */ |
| BOOL_Y_N | 1 |
| CONV_ERR_BAD_MONTH | 8 /* Unknown month name */ |
| CONV_ERR_DATE_OVERFLOW | 5 /* Result too large for output */ |
| CONV_ERR_DATE_SIZE | 6 /* Too few or too many digits */ |
| CONV_ERR_DECS_HIDDEN | 18 /* Decimals not allowed if hidden */ |
| CONV_ERR_DECS_MISSING | 11 /* Not enough decimals supplied */ |
| CONV_ERR_DECS_OVERFLOW | 19 /* Too many decimal positions */ |
| CONV_ERR_DECS_REJECTED | 17 /* Decimals not allowed if integer */ |
| CONV_ERR_INVALID_INPUT | 1 /* Unrecognised char in input */ |
| CONV_ERR_MULTIPLE_AM | 4 /* More than one 'am' or 'pm' */ |
| CONV_ERR_MULTIPLE_DELIM | 7 /* Too many delimiters */ |
| CONV_ERR_MULTIPLE_MONTH | 10 /* More than one month name */ |
| CONV_ERR_MULTIPLE_POINT | 16 /* More than one decimal point */ |
| CONV_ERR_MULTIPLE_SIGN | 13 /* More than one sign character */ |
| CONV_ERR_NOT_BOOLEAN | 3 /* Not a yes/no or true/false value */ |
| CONV_ERR_NUM_OVERFLOW | 12 /* Result too large for output */ |
| CONV_ERR_OUT_OF_RANGE | 2 /* Value out of valid range */ |
| CONV_ERR_REJECT_3_5 | 9 /* 3/5 digits in a row not allowed */ |
| CONV_ERR_SIGN_BAD_FIN | 15 /* Malformed financial negative */ |
| CONV_ERR_SIGN_REJECTED | 14 /* Sign not allowed if unsigned */ |
| CONV_ERR_TOO_MANY_DIGITS | 20 /* Too many digits for number */ |
| CONV_MAX_DECS | 100 /* Up to 100 dec |