|
| 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 decimal positions */ |
| CONV_NO_ERRORS | 0 /* No errors */ |
| DATE_MD_COMPACT | 7 |
| DATE_MD_DELIM | 8 |
| DATE_MD_SPACE | 9 |
| DATE_ORDER_DMY | 2 |
| DATE_ORDER_MDY | 3 |
| DATE_ORDER_YMD | 1 |
| DATE_YMD_COMMA | 3 |
| DATE_YMD_COMPACT | 0 |
| DATE_YMD_DELIM | 1 |
| DATE_YMD_SPACE | 2 |
| DATE_YM_COMPACT | 4 |
| DATE_YM_DELIM | 5 |
| DATE_YM_SPACE | 6 |
| DECS_DROP_ZEROS | 2 |
| DECS_HIDE_ALL | 3 |
| DECS_SCIENTIFIC | 4 |
| DECS_SHOW_ALL | 1 |
| FLAG_D_CENTURY | 8 |
| FLAG_D_DD_AS_D | 1 /* Date field flags */ |
| FLAG_D_MM_AS_M | 2 |
| FLAG_D_MONTH_ABC | 4 |
| FLAG_D_ORDER_DMY | 64 |
| FLAG_D_ORDER_MDY | 128 |
| FLAG_D_ORDER_YMD | 32 |
| FLAG_D_UPPER | 16 |
| FLAG_N_DECIMALS | 2 |
| FLAG_N_LEFT | 4 |
| FLAG_N_SIGNED | 1 /* Number field flags */ |
| FLAG_N_THOUSANDS | 32 |
| FLAG_N_ZERO_BLANK | 16 |
| FLAG_N_ZERO_FILL | 8 |
| FLAG_T_12_HOUR | 32 |
| FLAG_T_CC_AS_C | 8 |
| FLAG_T_COMPACT | 16 |
| FLAG_T_HH_AS_H | 1 /* Time field flags */ |
| FLAG_T_MM_AS_M | 2 |
| FLAG_T_SS_AS_S | 4 |
| FORMAT_MAX | 80 /* Max. size of formatted field */ |
| SIGN_ALL_LEAD | 4 |
| SIGN_ALL_TRAIL | 2 |
| SIGN_FINANCIAL | 5 |
| SIGN_NEG_LEAD | 3 |
| SIGN_NEG_TRAIL | 1 /* Number field formatting */ |
| _DATE_FORMAT_FIRST | 0 /* Date field formatting */ |
| _DATE_FORMAT_LAST | 9 |
| _DATE_MD_LAST | 9 |
| _DATE_ORDER_FIRST | 1 /* Values for date_order */ |
| _DATE_ORDER_LAST | 3 |
| _DATE_YMD_LAST | 3 |
| _DATE_YM_LAST | 6 |
| _SFLCONV_INCLUDED | TRUE |
#include "sflconv.h"
char *
conv_bool_str (
Bool boolean,
int format)
Converts a Bool value to a string according to the specified format: 0 = Yes|No; 1 = Y|N, 2 = True|False, 3 = T|F, 4 = 1|0. Returns a pointer to a static string that is overwritten by each call.
{
static char *bool_name [] =
{
"Yes", "No",
"Y", "N",
"True", "False",
"T", "F",
"1", "0"
};
conv_reason = 0; /* No conversion errors so far */
return (bool_name [format * 2 + (boolean? 0: 1)]);
}
#include "sflconv.h"
char *
conv_date_pict (
long date,
const char *picture)
Converts a date to a string using a picture. The picture is composed of any combination of these formats:
| cc | century 2 digits, 01-99 |
| y | day of year, 1-366 |
| yy | year 2 digits, 00-99 |
| yyyy | year 4 digits, 100-9999 |
| m | month, 1-12 |
| mm | month, 01-12 |
| mmm | month, 3 letters |
| mmmm | month, full name |
| MMM | month, 3 letters, ucase |
| MMMM | month, full name, ucase |
| d | day, 1-31 |
| dd | day, 01-31 |
| ddd | day of week, Sun-Sat |
| dddd | day of week, Sunday- Saturday |
| DDD | day of week, SUN-SAT |
| DDDD | day of week, SUNDAY-SATURDAY |
| w | day of week, 1-7 (1=Sunday) |
| ww | week of year, 1-53 |
| q | year quarter, 1-4 |
| \x | literal character x |
| other | literal character |
puts (conv_date_pict (19951202, "mm d, yy"));
Dec 2, 95
puts (conv_date_pict (19951202, "d mmm, yy"));
2 Dec, 95
{
static char
*month_name [] =
{
"January", "February", "March", "April", "May", "June", "July",
"August", "September", "October", "November", "December"
},
*day_name [] =
{
"Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"
},
formatted [FORMAT_MAX + 1]; /* Formatted return string */
int
century, /* Century component of date */
year, /* Year component of date */
month, /* Month component of date */
day, /* Day component of date */
cursize; /* Size of current component */
char
*dest, /* Store formatted data here */
ch, /* Next character in picture */
lastch = '0'; /* Last character we output */
long
full_date = date;
conv_reason = 0; /* No conversion errors so far */
/* Zero date is returned as empty string */
if (date == 0)
{
strclr (formatted);
return (formatted);
}
default century (&full_date);
century = GET_CENTURY (full_date);
year = GET_YEAR (full_date);
month = GET_MONTH (full_date);
day = GET_DAY (full_date);
ASSERT (month > 0 && month <= 12);
ASSERT (day > 0 && day <= 31);
/* Scan through picture, converting each component */
dest = formatted;
*dest = 0; /* string is empty */
while (*picture)
{
/* Get character and count number of occurences */
ch = *picture++;
for (cursize = 1; *picture == ch; cursize++)
picture++;
switch (ch)
{
/* cc century 2 digits, 01-99 */
case 'c':
if (cursize == 2)
sprintf (dest, "%02d", century);
break;
/* y day of year, 1-366 */
/* yy year 2 digits, 00-99 */
/* yyyy year 4 digits, 0100-9999 */
case 'y': /* y = day of year */
if (cursize == 1)
sprintf (dest, "%d", julian date (full_date));
else
if (cursize == 2)
sprintf (dest, "%02d", year);
else
if (cursize == 4)
sprintf (dest, "%02d%02d", century, year);
break;
/* m month, 1-12 */
/* mm month, 01-12 */
/* mmm month, 3 letters */
/* mmmm month, full name */
case 'm':
if (cursize == 1)
sprintf (dest, (isdigit (lastch)? "%2d": "%d"), month);
else
if (cursize == 2)
sprintf (dest, "%02d", month);
else
if (cursize == 3)
{
memcpy (dest, month_name [month - 1], 3);
dest [3] = 0;
}
else
if (cursize == 4)
strcpy (dest, month_name [month - 1]);
break;
/* MMM month, 3-letters, ucase */
/* MMMM month, full name, ucase */
case 'M':
if (cursize == 3)
{
memcpy (dest, month_name [month - 1], 3);
dest [3] = 0;
strupc (dest);
}
else
if (cursize == 4)
{
strcpy (dest, month_name [month - 1]);
strupc (dest);
}
break;
/* d day, 1-31 */
/* dd day, 01-31 */
/* ddd day of week, Sun-Sat */
/* dddd day of week, Sunday-Saturday */
case 'd':
if (cursize == 1)
sprintf (dest, (isdigit (lastch)? "%2d": "%d"), day);
else
if (cursize == 2)
sprintf (dest, "%02d", day);
else
if (cursize == 3)
{
memcpy (dest, day_name [day of week (full_date)], 3);
dest [3] = 0;
}
else
if (cursize == 4)
strcpy (dest, day_name [day of week (full_date)]);
break;
/* DDD day of week, SUN-SAT */
/* DDDD day of week, SUNDAY-SATURDAY */
case 'D':
if (cursize == 3)
{
memcpy (dest, day_name [day of week (full_date)], 3);
dest [3] = 0;
strupc (dest);
}
else
if (cursize == 4)
{
strcpy (dest, day_name [day of week (full_date)]);
strupc (dest);
}
break;
/* w day of week, 1-7 (1=Sunday) */
/* ww week of year, 1-53 */
case 'w':
if (cursize == 1)
sprintf (dest, "%d", day of week (full_date) + 1);
else
if (cursize == 2)
sprintf (dest, "%d", week of year (full_date));
break;
/* q year quarter, 1-4 */
case 'q':
if (cursize == 1)
sprintf (dest, "%d", year quarter (full_date));
break;
/* \x literal character x */
case '\\':
ch = *picture++;
}
if (*dest) /* If something was output, */
while (*dest) /* skip to end of string */
dest++;
else
while (cursize--) /* Else output ch once or more */
*dest++ = ch; /* and bump dest pointer */
lastch = *(dest - 1); /* Get previous character */
*dest = 0; /* Terminate the string nicely */
}
return (formatted);
}
#include "sflconv.h"
char *
conv_date_str (
long date,
int flags,
int format,
int order,
char datesep,
int width)
Converts a date to a string. The format argument defines how the date is shown:
| DATE YMD COMPACT | ddmmyy |
| DATE YMD SLASH | dd/mm/yy |
| DATE YMD SPACE | dd mm yy |
| DATE YMD COMMA | dd mm, yy (DM,Y or MD,Y or Y,MD) |
| DATE YM COMPACT | mmyy |
| DATE YM SLASH | mm/yy |
| DATE YM SPACE | mm yy |
| DATE MD COMPACT | ddmm |
| DATE MD SLASH | dd/mm |
| DATE MD SPACE | dd mm |
| FLAG D DD AS D | Show day without leading zero |
| FLAG D MM AS M | Show month without leading zero |
| FLAG D MONTH ABC | Show month as letters (fullname if width > 16) |
| FLAG D CENTURY | Show year as four digits |
| FLAG D UPPERCASE | Month name in uppercase |
| FLAG D ORDER DMY | Order is DMY for this date |
| FLAG D ORDER MDY | Order is MDY for this date |
| FLAG D ORDER YMD | Order is YMD for this date |
{
static char *format_table [] = {
"ymd", "dmy", "mdy", /* DATE_YMD_COMPACT */
"y/m/d", "d/m/y", "m/d/y", /* DATE_YMD_DELIM */
"y m d", "d m y", "m d y", /* DATE_YMD_SPACE */
"y, m d", "d m, y", "m d, y", /* DATE_YMD_COMMA */
"ym", "my", "my", /* DATE_YM_COMPACT */
"y/m", "m/y", "m/y", /* DATE_YM_DELIM */
"y m", "m y", "m y", /* DATE_YM_SPACE */
"md", "dm", "md", /* DATE_MD_COMPACT */
"m/d", "d/m", "m/d", /* DATE_MD_DELIM */
"m d", "d m", "m d" /* DATE_MD_SPACE */
};
char
*format_ptr, /* Scan through format string */
delim [2], /* Delimiter character */
picture [14], /* Largest picture: dd mmmm, yyyy */
ch; /* Next char in format string */
int
index,
date_order = order; /* Order to use */
ASSERT (format >= _DATE_FORMAT_FIRST && format <= _DATE_FORMAT_LAST);
ASSERT (order >= _DATE_ORDER_FIRST && order <= _DATE_ORDER_LAST);
conv_reason = 0; /* No conversion errors so far */
if (flags & FLAG_D_ORDER_YMD)
date_order = DATE_ORDER_YMD;
else
if (flags & FLAG_D_ORDER_DMY)
date_order = DATE_ORDER_DMY;
else
if (flags & FLAG_D_ORDER_MDY)
date_order = DATE_ORDER_MDY;
/* Get index into table */
index = format * 3 + date_order - 1;
/* Now build-up picture according to format string */
strclr (picture);
for (format_ptr = format_table [index]; *format_ptr; format_ptr++)
{
ch = *format_ptr;
switch (ch)
{
case 'y':
strcat (picture, flags & FLAG_D_CENTURY? "yyyy": "yy");
break;
case 'm':
if (flags & FLAG_D_MONTH_ABC)
if (width > 16)
strcat (picture, flags & FLAG_D_UPPER? "MMMM": "mmmm");
else
strcat (picture, flags & FLAG_D_UPPER? "MMM": "mmm");
else
strcat (picture, flags & FLAG_D_MM_AS_M? "m": "mm");
break;
case 'd':
strcat (picture, flags & FLAG_D_DD_AS_D? "d": "dd");
break;
case '/':
ch = datesep; /* Use supplied date separator */
default:
delim [0] = ch;
delim [1] = 0;
strcat (picture, delim);
}
}
format_ptr = conv date pict (date, picture);
if (strlen (format_ptr) > (unsigned) width)
{
conv_reason = CONV_ERR_DATE_OVERFLOW;
return (NULL);
}
else
return (format_ptr);
}
#include "sflconv.h"
char *
conv_number_str (
const char *number, /* Number to convert */
int flags, /* Number formatting flags */
char dec_point, /* Decimal point: '.' or ',' */
int decimals, /* Number of decimals, or 0 */
int dec_format, /* How are decimals shown? */
int width, /* Output field width, or 0 */
int sign_format /* How are negatives shown? */
)
Converts a number to a string. The number format is defined largely by the flags argument, which can specify various values defined in sflconv.h:
| FLAG N SIGNED | Show signed number, using sign_format argument. |
| FLAG N DECIMALS | Show decimals, using dec_format argument. |
| FLAG N LEFT | Left-justify number; no effect if width is 0. |
| FLAG N ZERO FILL | Right-justfified, with leading zeroes. |
| FLAG N ZERO BLANK | Show zero as empty string or spaces (width > 0). |
| FLAG N THOUSANDS | Show number with thousands separators. |
| SIGN NEG TRAIL | Negative numbers only: 123- |
| SIGN ALL TRAIL | All non-zero numbers: 123- 123+ |
| SIGN NEG LEAD | Negative numbers only: -123 |
| SIGN ALL LEAD | All non-zero numbers: -123 +123 |
| SIGN FINANCIAL | Negative numbers only: (123) |
| DECS SHOW ALL | 123.10, 123.00, 0.95 |
| DECS DROP ZEROS | 123.1, 123, 0.95 |
| DECS HIDE ALL | 123, 123, 0 |
| DECS PERCENTAGE | 12300, 12300, 95 |
| DECS SCIENTIFIC | 1.231e2, 1.23e2, 9.5e-1 |
{
static char
formatted [FORMAT_MAX + 1], /* Formatted return string */
zero [CONV_MAX_DECS + 2]; /* Default value if needed */
int
sep_stop, /* Where we put next sep_char */
dec_stop, /* Where we put decimal point */
decs_wanted = decimals, /* Number of decimals wanted */
decs_seen, /* Number of decimals output */
sign_pos, /* Where we put sign, if any */
digits; /* Number of digits read so far */
char
*dest, /* Store formatted number here */
sign_char, /* Number's sign: ' ', '+', '-' */
sep_char, /* Thousands separator '.' or ',' */
drop_zero, /* We suppress this char */
ch; /* Next character in picture */
Bool
have_zero; /* TRUE if whole number is zero */
ASSERT (width <= FORMAT_MAX);
ASSERT (dec_point == '.' || dec_point == ',');
conv_reason = 0; /* No conversion errors so far */
/* --------------------------------- Prepare to copy digits ---------*/
if (decs_wanted > CONV_MAX_DECS)
{
conv_reason = CONV_ERR_DECS_OVERFLOW;
return (NULL); /* Error - too many decimals */
}
/* If value is empty, use "0" with enough decimals as default value */
/* We allow one whole digit and as many decimals as needed. */
if (strnull (number))
{
strpad (zero, '0', decs_wanted + 1);
number = zero;
}
/* Pick-up sign character if present */
if (*number == ' ' || *number == '+' || *number == '-')
sign_char = *number++;
else
sign_char = ' ';
/* While leading zero is '0' we blank-out zeros in the number */
drop_zero = (char) (flags & FLAG_N_ZERO_FILL? ' ': '0');
/* Prepare for decimals */
if ((flags & FLAG_N_DECIMALS) == 0)
decs_wanted = 0;
if (strchr (number, '.'))
dec_stop = (int) (strchr (number, '.') - (char *) number);
else
dec_stop = strlen (number) - decs_wanted;
if (dec_stop < 1)
{
conv_reason = CONV_ERR_DECS_MISSING;
return (NULL); /* Error - too few decimals */
}
/* Prepare for thousands-separators if FLAG_N_THOUSANDS */
if ((flags & FLAG_N_THOUSANDS) && !(flags & FLAG_N_ZERO_FILL))
{
/* Get number of whole digits, allowing for decimals & dec sign */
sep_char = (char) (dec_point == '.'? ',': '.');
sep_stop = (dec_stop - (decs_wanted? decs_wanted + 1: 0)) % 3;
if (sep_stop == 0)
sep_stop = 3; /* Get into range 1..3 */
}
else
{
sep_char = ' ';
sep_stop = 0; /* No thousands separators */
}
/* --------------------------------- Copy the digits ----------------*/
digits = 0; /* No digits loaded yet */
decs_seen = 0; /* No decimals output yet */
have_zero = TRUE; /* Assume number is zero */
dest = formatted; /* Format number */
while (*number) /* until we hit the terminator */
{
ch = *number++;
if (ch == '.')
continue; /* Ignore '.' in number */
digits++;
if (ch == drop_zero && digits < dec_stop)
ch = ' ';
else
if (isdigit (ch))
{
drop_zero = ' ';
if (ch > '0')
have_zero = FALSE;
}
if (ch != ' ' || (width > 0 && !(flags & FLAG_N_LEFT)))
{
*dest++ = ch; /* Output this digit */
if (digits > dec_stop)
decs_seen++; /* Count the decimal digit */
else
if (digits == dec_stop) /* Handle decimal stop */
{ /* with optional point */
if (flags & FLAG_N_DECIMALS)
*dest++ = dec_point;
sep_stop = 0; /* And kill further thousand seps */
}
}
/* Output thousands separator unless we are in blank area */
if (digits == sep_stop)
{
if (ch != ' ')
*dest++ = sep_char;
sep_stop += 3;
}
}
*dest = 0; /* Terminate the string nicely */
/* --------------------------------- Post-format the result ---------*/
if (decs_wanted > 0)
{
/* Output trailing decimal zeroes if not supplied */
if (decs_seen == 0)
*dest++ = dec_point;
while (decs_seen < decs_wanted)
{
*dest++ = '0';
decs_seen++;
}
/* Drop all decimals if format is DEC_HIDE_ALL */
if (dec_format == DECS_HIDE_ALL)
while (*dest != dec_point)
dest--; /* Drop-off trailing zero */
else
/* Drop trailing decimal zeroes if format is DEC_DROP_ZEROS */
if (dec_format == DECS_DROP_ZEROS)
while (*dest != dec_point)
if (*(dest - 1) > '0')
break;
else
dest--; /* Drop-off trailing zero */
*dest = 0; /* Terminate the string nicely */
}
/* Justify within width if width > 0 */
sign_pos = 0; /* Sign normally comes at start */
digits = strlen (formatted);
if (flags & FLAG_N_SIGNED)
{
digits++; /* Allow for eventual sign */
if (sign_format == SIGN_FINANCIAL)
digits++; /* Sign shown like (123) */
}
while (digits < width)
{
if (flags & FLAG_N_LEFT && !(flags & FLAG_N_ZERO_FILL))
strcat (formatted, " ");
else
{
stropen (formatted, FALSE); /* Insert blank at start of string */
if (flags & FLAG_N_ZERO_FILL)
formatted [0] = '0';
else
sign_pos++; /* Skip leading space */
}
digits++;
}
/* Format sign if FLAG_N_SIGNED */
if (flags & FLAG_N_SIGNED)
{
if (sign_format == SIGN_NEG_LEAD
|| sign_format == SIGN_ALL_LEAD
|| sign_format == SIGN_FINANCIAL)
stropen (formatted, FALSE);
if (sign_format == SIGN_NEG_TRAIL
|| sign_format == SIGN_ALL_TRAIL
|| sign_format == SIGN_FINANCIAL)
strcat (formatted, " ");
if (!have_zero) /* Zero has no sign */
switch (sign_format)
{
case SIGN_NEG_LEAD:
if (sign_char != '-')
break; /* Fall through if negative sign */
case SIGN_ALL_LEAD:
formatted [sign_pos] = sign_char;
break;
case SIGN_NEG_TRAIL:
if (sign_char != '-')
break; /* Fall through if negative sign */
case SIGN_ALL_TRAIL:
strlast (formatted) = sign_char;
break;
case SIGN_FINANCIAL:
if (sign_char == '-')
{
formatted [0] = '(';
strlast (formatted) = ')';
}
break;
}
}
/* If all zeroes, return a blank string if FLAG_N_ZERO_BLANK */
if ((flags & FLAG_N_ZERO_BLANK) && have_zero)
{
memset (formatted, ' ', width);
formatted [width] = 0;
}
if (width > 0 && (strlen (formatted) > (size_t) width))
{
conv_reason = CONV_ERR_NUM_OVERFLOW;
return (NULL); /* Overflow -- number too large */
}
else
return (formatted);
}
#include "sflconv.h"
int
conv_str_bool (
const char *string)
Converts a string to a Bool. Accepts T/Y/1 as TRUE, F/N/0 as FALSE, ignoring case. Looks only at the first letter of the string. Returns 1 for TRUE, 0 for FALSE, -1 if the string was not valid.
{
char
ch = tolower (string [0]);
conv_reason = 0; /* No conversion errors so far */
if (ch == 'y' || ch == 't' || ch == '1')
return (1);
else
if (ch == 'n' || ch == 'f' || ch == '0')
return (0);
else
{
conv_reason = CONV_ERR_NOT_BOOLEAN;
return (-1);
}
}
#include "sflconv.h"
long
conv_str_date (
const char *string,
int flags,
int format,
int order)
Converts a string to a date. The supposed format of the date is defined by the format argument, which can be one of:
| DATE YMD COMPACT | Year month day. |
| DATE YMD DELIM | Year month day. |
| DATE YMD SPACE | Year month day. |
| DATE YMD COMMA | Year month day. |
| DATE YM COMPACT | Year and month only; day is zero. |
| DATE YM DELIM | Year and month only; day is zero. |
| DATE YM SPACE | Year and month only; day is zero. |
| DATE MD COMPACT | Month and day only; year is zero. |
| DATE MD DELIM | Month and day only; year is zero. |
| DATE MD SPACE | Month and day only; year is zero. |
| DATE ORDER YMD | Year month day. |
| DATE ORDER DMY | Day month year. |
| DATE ORDER MDY | Month day year. |
| FLAG D ORDER YMD | Year month day. |
| FLAG D ORDER DMY | Day month year. |
| FLAG D ORDER MDY | Month day year. |
{
static
char *month_name [] =
{
"jan", "feb", "mar", "apr", "may", "jun",
"jul", "aug", "sep", "oct", "nov", "dec"
};
static byte
month_days [] =
{
31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
},
order_ptr [][3] = /* Year, Month, Day pointers */
{ /* for various orders & formats */
/* YY MM DD YYYY MM DD YY MM YYYY MM MM DD */
{ 0, 2, 4 }, { 0, 4, 6 }, { 0, 2, 99 }, { 0, 4, 99 }, { 99, 0, 2 },
{ 4, 2, 0 }, { 4, 2, 0 }, { 2, 0, 99 }, { 2, 0, 99 }, { 99, 2, 0 },
{ 4, 0, 2 }, { 4, 0, 2 }, { 2, 0, 99 }, { 2, 0, 99 }, { 99, 0, 2 },
};
char
date_digits [9], /* 8 digits of date */
month_letters [4] = "???", /* 3 characters of month */
ch; /* Next character in date */
long
feedback; /* Returned date; -1 = error */
int
digits, /* Number of digits in string */
delimiters, /* Number of delimiters in date */
digitseq, /* Number of digits in sequence */
count, /* Number of month letters */
year, /* Date year value */
month, /* Date month value */
day, /* Date day value */
order_index, /* Index into order table */
y_ptr, /* Where is year in date? */
m_ptr, /* Where is month in date? */
d_ptr, /* Where is day in date? */
date_order = order; /* Actual date order */
Bool
had_month; /* Did we already get a month? */
ASSERT (format >= _DATE_FORMAT_FIRST && format <= _DATE_FORMAT_LAST);
ASSERT (order >= _DATE_ORDER_FIRST && order <= _DATE_ORDER_LAST);
conv_reason = 0; /* No conversion errors so far */
if (flags & FLAG_D_ORDER_YMD)
date_order = DATE_ORDER_YMD;
else
if (flags & FLAG_D_ORDER_DMY)
date_order = DATE_ORDER_DMY;
else
if (flags & FLAG_D_ORDER_MDY)
date_order = DATE_ORDER_MDY;
/* Collect date digits */
digits = 0; /* Nothing collected so far */
digitseq = 0; /* No digits in sequence */
feedback = 0; /* No errors so far */
delimiters = 0; /* We allow up to 2 delimiters */
had_month = FALSE; /* True after 3-letter month seen */
do
{
ch = *string++;
if (isdigit (ch))
{
if (digits < 8)
{
digitseq++;
date_digits [digits++] = ch;
}
else
{
conv_reason = CONV_ERR_DATE_SIZE;
feedback = -1; /* Too many digits */
}
}
else
{
/* Fill-up to even number of digits */
if (digits > (digits / 2) * 2)
{
date_digits [digits] = date_digits [digits - 1];
date_digits [digits - 1] = '0';
digits++;
}
/* 3 or 5 in a row is not allowed */
if (digitseq == 3 || digitseq == 5)
{
conv_reason = CONV_ERR_REJECT_3_5;
feedback = -1;
}
digitseq = 0;
/* If a letter, try to match against a month */
if (isalpha (ch))
{
if (had_month)
{
conv_reason = CONV_ERR_MULTIPLE_MONTH;
feedback = -1;
}
else
{
for (count = 0; isalpha (ch); )
{
if (count < 3)
month_letters [count++] = (char) tolower (ch);
ch = *string++;
}
string--; /* Move back to char after month */
if (count < 3)
{
conv_reason = CONV_ERR_BAD_MONTH;
feedback = -1; /* Too few letters */
}
month_letters [3] = 0;
for (count = 0; count < 12; count++)
if (streq (month_letters, month_name [count]))
{
count++;
date_digits [digits++] = (char) (count / 10 + '0');
date_digits [digits++] = (char) (count % 10 + '0');
had_month = TRUE;
break;
}
if (!had_month)
{
conv_reason = CONV_ERR_BAD_MONTH;
feedback = -1; /* Month not found */
}
}
}
else
if (ispunct (ch)) /* Skip any delimiter */
if ((++delimiters > 2)
|| (format > _DATE_YMD_LAST && delimiters > 1))
{
conv_reason = CONV_ERR_MULTIPLE_DELIM;
feedback = -1; /* Multiple delimiters */
}
}
}
until (ch == 0);
/* Return zero date if empty */
if (digits == 0)
return (feedback);
/* Calculate offset in date_digits for various date order & formats */
order_index = (date_order - 1) * 5; /* Each row has 5 items */
if (format <= _DATE_YMD_LAST)
{
if (digits == 6)
; /* nothing */
else
if (digits == 8)
order_index += 1;
}
else
if (format <= _DATE_YM_LAST)
{
date_digits [digits++] = '0';
date_digits [digits++] = '0';
if (digits == 6)
order_index += 2;
else
if (digits == 8)
order_index += 3;
else
{
conv_reason = CONV_ERR_DATE_SIZE;
return (-1); /* Error - bad date size */
}
}
else
if (format <= _DATE_MD_LAST)
{
date_digits [digits++] = '0';
date_digits [digits++] = '0';
if (digits == 6)
order_index += 4;
else
{
conv_reason = CONV_ERR_DATE_SIZE;
return (-1); /* Error - bad date size */
}
}
/* Decode order to pick-up offset of day/month/year in date_digits */
y_ptr = order_ptr [order_index][0];
m_ptr = order_ptr [order_index][1];
d_ptr = order_ptr [order_index][2];
# define DIGIT(x) (date_digits [(x)] - '0')
if (y_ptr != 99)
{
year = DIGIT (y_ptr) * 10 + DIGIT (y_ptr + 1);
if (digits == 8)
year = DIGIT (y_ptr + 2) * 10 + DIGIT (y_ptr + 3) + year * 100;
if (year < 50)
year += 2000;
else
if (year < 100)
year += 1900;
}
else
year = 0;
if (m_ptr != 99)
{
month = DIGIT (m_ptr) * 10 + DIGIT (m_ptr + 1);
if (month == 0 || month > 12)
{
conv_reason = CONV_ERR_OUT_OF_RANGE;
feedback = -1;
}
}
else
month = 0;
if (d_ptr != 99)
{
day = DIGIT (d_ptr) * 10 + DIGIT (d_ptr + 1);
if ((day == 0 || day > (int) month_days [month - 1])
|| (month == 2 && day == 29 && !leap year (year)))
{
conv_reason = CONV_ERR_OUT_OF_RANGE;
feedback = -1;
}
}
else
day = 0;
if (feedback == 0)
feedback = year * 10000L + month * 100 + day;
return (feedback);
}
#include "sflconv.h"
int
conv_str_day (
const char *string)
Converts a string to a day. The string contains a day name in English. Only the first three letters of the name are significant. Returns a value 0..6 for Sunday to Saturday, or -1 if the name is not a valid day.
{
static
char *day_name [] =
{ "sun", "mon", "tue", "wed", "thu", "fri", "sat" };
int
day;
char
day_comp [4];
/* Get up to three letters of day name and convert to lower case */
strncpy (day_comp, string, 3);
day_comp [3] = '\0';
strlwc (day_comp);
/* I don't like magic constants, but "7" is pretty safe for now */
for (day = 0; day < 7; day++)
if (streq (day_name [day], day_comp))
break;
return (day >= 7? -1: day);
}
#include "sflconv.h"
char *
conv_str_number (
const char *string, /* String to convert */
int flags, /* Number field flags */
char dec_point, /* Decimal point: '.' or ',' */
int decimals, /* Number of decimals, or 0 */
int dec_format, /* How are decimals shown */
int width /* Output field width, > 0 */
)
Converts a string to a number. The number format is defined by one or more of these flags (you add them to get a flags argument):
| FLAG N SIGNED | Number is signed. |
| FLAG N DECIMALS | Number has decimals. |
| FLAG N ZERO FILL | Number has leading zeros. |
| FLAG N THOUSANDS | Number has thousands-separators. |
| DECS SHOW ALL | Accept decimals. |
| DECS DROP ZEROS | Accept decimals. |
| DECS HIDE ALL | Reject decimals. |
| DECS SCIENTIFIC | Accept decimals. |
{
static char
number [FORMAT_MAX + 1]; /* Cleaned-up return string */
int
digits, /* Number of digits read so far */
decs_wanted = decimals; /* Number of decimals wanted */
char
*dest, /* Store formatted number here */
sign_char, /* Number's sign: ' ', '+', '-' */
sep_char, /* Thousands separator '.' or ',' */
decs_seen, /* Number of decimals output */
ch; /* Next character in picture */
Bool
have_point, /* Have we seen a decimal point */
have_zero, /* TRUE if number is all zero */
end_loop; /* Flag to break out of scan loop */
ASSERT (width <= FORMAT_MAX);
ASSERT (width > 0);
ASSERT (dec_point == '.' || dec_point == ',');
conv_reason = 0; /* No conversion errors so far */
/* --------------------------------- Prepare to copy digits ---------*/
if ((flags & FLAG_N_THOUSANDS) && !(flags & FLAG_N_ZERO_FILL))
sep_char = dec_point == '.'? ',': '.';
else
sep_char = ' '; /* Reject any thousands separator */
/* --------------------------------- Copy the digits ----------------*/
digits = 0; /* No digits loaded yet */
decs_seen = 0; /* No decimals output yet */
sign_char = ' '; /* Final sign character '+' or '-' */
end_loop = FALSE; /* Flag to break out of scan loop */
have_point = FALSE; /* No decimal point seen */
have_zero = TRUE; /* So far, it's zero */
dest = number; /* Scan through number */
while (*string)
{
ch = *string++;
switch (ch)
{
case '9':
case '8':
case '7':
case '6':
case '5':
case '4':
case '3':
case '2':
case '1':
have_zero = FALSE;
case '0':
digits++;
*dest++ = ch;
if (have_point)
++decs_seen;
break;
case '-':
case '+':
case '(':
if (sign_char != ' ')
{
conv_reason = CONV_ERR_MULTIPLE_SIGN;
return (NULL); /* More than one sign char */
}
else
if (flags & FLAG_N_SIGNED)
sign_char = ch;
else
{
conv_reason = CONV_ERR_SIGN_REJECTED;
return (NULL); /* Number may not be signed */
}
break;
case ')':
if (sign_char == '(')
sign_char = '-';
else
{
conv_reason = CONV_ERR_SIGN_BAD_FIN;
return (NULL); /* Malformed financial negative */
}
break;
case ' ': /* Space ends number after digits */
end_loop = (digits > 0);
break;
default:
if (ch == dec_point)
{
if (have_point)
{
conv_reason = CONV_ERR_MULTIPLE_POINT;
return (NULL); /* More than one decimal point */
}
else
if (flags & FLAG_N_DECIMALS)
have_point = TRUE;
else
{
conv_reason = CONV_ERR_DECS_REJECTED;
return (NULL); /* No decimals are allowed */
}
}
else
if (ch != sep_char) /* We allow sep chars anywhere */
{
conv_reason = CONV_ERR_INVALID_INPUT;
return (NULL); /* else we have junk */
}
}
if (end_loop)
break;
}
/* --------------------------------- Post-format the result ---------*/
if (flags & FLAG_N_DECIMALS)
{
ASSERT (width > decs_wanted); /* At least decimals + 1 digit */
if (dec_format == DECS_HIDE_ALL)
{
if (have_point)
{
conv_reason = CONV_ERR_DECS_HIDDEN;
return (NULL); /* No decimals are allowed */
}
}
while (decs_seen < decs_wanted) /* Supply missing decimals */
{
digits++;
*dest++ = '0';
decs_seen++;
}
if (decs_seen > decs_wanted)
{
conv_reason = CONV_ERR_DECS_OVERFLOW;
return (NULL); /* More decimals than allowed */
}
}
else
decs_wanted = 0;
*dest = 0; /* Terminate the string nicely */
if (digits > width)
{
conv_reason = CONV_ERR_TOO_MANY_DIGITS;
return (NULL); /* Overflow -- number too large */
}
/* Supply leading zeroes */
if (digits < width)
{
/* Shift number and null to right of field */
memmove (number + (width - digits), number, digits + 1);
memset (number, '0', width - digits);
}
/* Store sign if necessary */
if (flags & FLAG_N_SIGNED)
{
ASSERT (width > 1); /* At least sign + 1 digit */
if (number [0] != '0')
{
conv_reason = CONV_ERR_TOO_MANY_DIGITS;
return (NULL); /* Overflow -- no room for sign */
}
if (sign_char == '(')
{
conv_reason = CONV_ERR_SIGN_BAD_FIN;
return (NULL); /* Malformed financial negative */
}
else
if (sign_char == ' ')
sign_char = '+';
if (have_zero)
number [0] = ' '; /* Store sign */
else
number [0] = sign_char;
}
return (number);
}
#include "sflconv.h" /* We supply the dialog file as the source long conv_str_time (const char *p_string)
Converts a string to a time. The string must have this format: hour[<delim>minute[<delim>second[<delim>centi ]]][a[m]|p[m]] Any non-digit is accepted as delimiter. Each component may be one or two digits. The input string must be null-terminated. Returns -1 in case of an invalid date or format. If the string was empty (contains no usable digits, returns 0. The am/pm indicator can occur one anywhere in the string.
{
After-Init:
(--) Ok -> Expect-Hour
+ Get-Next-Component
Expect-Hour:
(--) Number -> Expect-Minute
+ Have-Hour
+ Get-Next-Component
(--) Am-Pm -> Expect-Hour
+ Have-Am-Pm-Indicator
+ Get-Next-Component
Expect-Minute:
(--) Number -> Expect-Second
+ Have-Minute
+ Get-Next-Component
(--) Am-Pm -> Expect-Minute
+ Have-Am-Pm-Indicator
+ Get-Next-Component
Expect-Second:
(--) Number -> Expect-Centisecond
+ Have-Second
+ Get-Next-Component
(--) Am-Pm -> Expect-Second
+ Have-Am-Pm-Indicator
+ Get-Next-Component
Expect-Centisecond:
(--) Number -> Allow-Am-Pm
+ Have-Centisecond
+ Get-Next-Component
(--) Am-Pm -> Expect-Centisecond
+ Have-Am-Pm-Indicator
+ Get-Next-Component
Allow-Am-Pm:
(--) Am-Pm -> Expect-Finished
+ Have-Am-Pm-Indicator
+ Get-Next-Component
Expect-Finished:
(--) Finished ->
+ Have-Complete-Time
+ Terminate-The-Program
Defaults:
(--) Number ->
+ Have-Invalid-Time
+ Terminate-The-Program
(--) Am-Pm ->
+ Have-Invalid-Time
+ Terminate-The-Program
(--) Finished ->
+ Have-Complete-Time
+ Terminate-The-Program
(--) Delimiter ->
+ Have-Delimiter
+ Get-Next-Component
(--) Error ->
+ Have-Invalid-Time
+ Terminate-The-Program
}
#include "sflconv.h"
char *
conv_time_pict (
long time,
const char *picture)
Converts a time to a string using a picture. The picture is composed of any combination of these formats:
| h | hour, 0-23 |
| hh | hour, 00- 23 |
| m | minute, 0-59 |
| mm | minute, 00-59 |
| s | second, 0-59 |
| ss | second, 00-59 |
| c | centisecond, 0- 99 |
| cc | centisecond, 00-99 |
| a | a/p indicator - use 12-hour clock |
| aa | am/pm indicator - use 12-hour clock |
| A | A/P indicator - use 12-hour clock |
| AA | AM/PM indicator - use 12-hour clock |
| \x | literal character x |
| other | literal character |
{
static char
formatted [FORMAT_MAX + 1]; /* Formatted return string */
int
hour, /* Hour component of time */
minute, /* Minute component of time */
second, /* Second component of time */
centi, /* 1/100 sec component of time */
cursize; /* Size of current component */
char
*dest, /* Store formatted data here */
ch, /* Next character in picture */
lastch = '0'; /* Last character we output */
Bool
pm; /* TRUE when hour >= 12 */
conv_reason = 0; /* No conversion errors so far */
/* Zero time is returned as empty string */
if (time == 0)
{
strclr (formatted);
return (formatted);
}
hour = GET_HOUR (time);
minute = GET_MINUTE (time);
second = GET_SECOND (time);
centi = GET_CENTI (time);
/* If am/pm component specified, use 12-hour clock */
if (hour >= 12)
{
pm = TRUE;
if (strpbrk (picture, "aA") && hour > 12)
hour = 12;
}
else
pm = FALSE;
ASSERT (hour >= 0 && hour < 24);
ASSERT (minute >= 0 && minute < 60);
ASSERT (second >= 0 && second < 60);
/* Scan through picture, converting each component */
dest = formatted;
*dest = 0; /* string is empty */
while (*picture)
{
/* Get character and count number of occurences */
ch = *picture++;
for (cursize = 1; *picture == ch; cursize++)
picture++;
switch (ch)
{
/* h hour, 0-23 */
/* hh hour, 00-23 */
case 'h':
if (cursize == 1)
sprintf (dest, (isdigit (lastch)? "%2d": "%d"), hour);
else
if (cursize == 2)
sprintf (dest, "%02d", hour);
break;
/* m minute, 0-59 */
/* mm minute, 00-59 */
case 'm':
if (cursize == 1)
sprintf (dest, (isdigit (lastch)? "%2d": "%d"), minute);
else
if (cursize == 2)
sprintf (dest, "%02d", minute);
break;
/* s second, 0-59 */
/* ss second, 00-59 */
case 's':
if (cursize == 1)
sprintf (dest, (isdigit (lastch)? "%2d": "%d"), second);
else
if (cursize == 2)
sprintf (dest, "%02d", second);
break;
/* c centisecond, 0-99 */
/* cc centisecond, 00-99 */
case 'c':
if (cursize == 1)
sprintf (dest, (isdigit (lastch)? "%2d": "%d"), centi);
else
if (cursize == 2)
sprintf (dest, "%02d", centi);
break;
/* a a/p indicator */
/* aa am/pm indicator */
case 'a':
strncat (dest, (pm? "pm": "am"), cursize);
dest [cursize] = 0;
break;
/* A A/P indicator */
/* AA AM/PM indicator */
case 'A':
strncat (dest, (pm? "PM": "AM"), cursize);
dest [cursize] = 0;
break;
/* \x literal character x */
case '\\':
ch = *picture++;
}
if (*dest) /* If something was output, */
while (*dest) /* skip to end of string */
dest++;
else
while (cursize--) /* Else output ch once or more */
*dest++ = ch; /* and bump dest pointer */
lastch = *(dest - 1); /* Get previous character */
*dest = 0; /* Terminate the string nicely */
}
return (formatted);
}
#include "sflconv.h"
char *
conv_time_str (
long time,
int flags,
char timesep,
int width)
Converts a time to a string. The flags and width control the resulting string. You can use one or more of these flags added together:
| FLAG T HH AS H | Suppress leading zeroes on the hours. |
| FLAG T MM AS M | Suppress leading zeroes on the minutes. |
| FLAG T SS AS S | Suppress leading zeroes on the seconds. |
| FLAG T CC AS C | Suppress leading zeroes on the centiseconds. |
| FLAG T COMPACT | Show without delimiters. |
| FLAG T 12 HOUR | Append am/pm indicator. |
| 4 or less | Error. |
| 5 to 7 | "hh:mm" |
| 8 to 10 | "hh:mm:ss" |
| 11 or more | "hh:mm:ss:cc" |
| 3 or less | Error. |
| 4 to 5 | "hhmm" |
| 6 to 7 | "hhmmss" |
| 8 or more | "hhmmsscc" |
| 5 or less | Error. |
| 6 to 8 | "hh:mma" |
| 9 to 11 | "hh:mm:ssa" |
| 12 or more | "hh:mm:ss:cca" |
| 4 or less | Error. |
| 5 to 6 | "hhmma" |
| 7 to 8 | "hhmmssa" |
| 9 or more | "hhmmsscca" |
{
char
delim [2], /* Delimiter string, ":" or "" */
picture [13]; /* Largest picture: hh:mm:ss:cca */
int
delim_len;
int
space_left = width;
conv_reason = 0; /* No conversion errors so far */
if (flags & FLAG_T_COMPACT)
{
delim [0] = 0;
delim_len = 0;
}
else
{
delim [0] = timesep;
delim [1] = 0;
delim_len = 1;
}
if (flags & FLAG_T_12_HOUR)
space_left--; /* Subtract 1 if eventual "a" */
/* Build-up date picture components until we run out of space */
strcpy (picture, (flags & FLAG_T_HH_AS_H? "h": "hh"));
space_left -= 2;
if (space_left >= delim_len + 2)
{
strcat (picture, delim);
strcat (picture, (flags & FLAG_T_MM_AS_M? "m": "mm"));
space_left -= delim_len + 2;
}
else
return (NULL); /* Error - space_left is too small */
if (space_left >= delim_len + 2)
{
strcat (picture, delim);
strcat (picture, (flags & FLAG_T_SS_AS_S? "s": "ss"));
space_left -= delim_len + 2;
}
if (space_left >= delim_len + 2)
{
strcat (picture, delim);
strcat (picture, (flags & FLAG_T_CC_AS_C? "c": "cc"));
space_left -= delim_len + 2;
}
/* Append "a" (or "aa" if space) if 12-hour clock wanted */
if (flags & FLAG_T_12_HOUR)
strcat (picture, (space_left == 0? "a": "aa"));
return (conv time pict (time, picture));
}
Filename: sflcryp.h
Package: Standard Function Library (SFL)
Written: 96/01/23 iMatix SFL project team sfl@imatix.com
Revised: 97/09/08
Copyright: Copyright (c) 1991-98 iMatix
The encryption/decryption functions were based on the cryptosystem library by Andrew Brown asb@cs.nott.ac.uk, cleaned- up for portability. Thanks for a great package. IDEA is registered as the international patent WO 91/18459 "Device for Converting a Digital Block and the Use thereof". For commercial use of IDEA, you should contact: ASCOM TECH AG Freiburgstrasse 370 CH-3018 Bern, Switzerland
Description of IDEA cipher
--------------------------
The IDEA cipher operates on 64 bit (8 byte) blocks, using a 128 bit (16
byte) key. IDEA has found itself famous through its inclusion in the
well-known PGP package. The following is from the introduction to chapter
3 of the thesis that presented the cipher.
The block cipher IDEA (International Data Encryption Algorithm) is based
on the new design concept of "mixing operations from different algebraic
groups". The required "confusion" was achieved by successively using three
"incompatible" group operations on pairs of 16-bit subblocks and the cipher
structure was chosen to provide the necessary "diffusion". The cipher
structure was further chosen to facilitate both hardware and software
implementations. The IDEA cipher is an improved version of PES and was
developed to increase security against differential cryptanalysis.
Description of MDC cipher
-------------------------
This is a method for turning a hash function, here MD5, into a fast
secret-key encryption. Based on a suggestion by Phil Karn in sci.crypt, 13
Feb 1992. See also his comments from sci.crypt, 23 Mar 1992. The method is
a variant of that described in Zheng, Matsumoto and Imai, Crypto 89. See
also, "A New Class of Cryptosystems Based on Interconnection Networks" by
michaelp@terpsichore.informatic.rwth-aachen.de
Description of DES cipher
-------------------------
DES is the well known U.S. Data Encryption Standard cipher.
DES encrypts data in 64 bit blocks, using a 64 bit key -- of which
56 bits are used in the encipherment process.
sflcryp.h defines these symbols, possibly conditionally:
| Symbol: | Defined as: |
|---|---|
| CRYPT_DES | 2 /* DES algorithm */ |
| CRYPT_IDEA | 0 /* IDEA algorithm */ |
| CRYPT_MAX_BLOCK_SIZE | 32 /* Largest block size, in bytes */ |
| CRYPT_MDC | 1 /* MDC algorithm */ |
| CRYPT_TOP | 4 /* We support 4 algorithms */ |
| CRYPT_XOR | 3 /* A basic XOR algorithm */ |
| _SFLCRYP_INCLUDED | TRUE |
#include "sflcryp.h"
Bool
crypt_encode (
byte *buffer, /* Data to encrypt, in-place */
word buffer_size, /* Amount of data to encrypt */
int algorithm, /* What type of encryption */
const byte *key) /* Encryption key */
Encrypt a buffer with the specified algorithm and specified key. Returns TRUE if the buffer is encrypted sucessfully. The buffer is encrypted in-place. The algorithm can be one of:
| CRYPT IDEA | Use IDEA encryption. |
| CRYPT MDC | Use MDC encryption. |
| CRYPT DES | Use DES encryption. |
| CRYPT XOR | Use XOR encryption. |
| CRYPT IDEA | Buffer is at least 8 bytes long, key is 16 bytes. |
| CRYPT MDC | Buffer is at least 8 bytes long, key is 8 bytes. |
| CRYPT DES | Buffer is at least 32 bytes long, key is 16 bytes. |
| CRYPT XOR | Buffer is at least 16 bytes long, key is 16 bytes. |
{
return (crypt_data (buffer, buffer_size, algorithm, key, TRUE));
}
#include "sflcryp.h"
Bool
crypt_decode (
byte *buffer, /* Data to decrypt, in-place */
word buffer_size, /* Amount of data to decrypt */
int algorithm, /* What type of decryption */
const byte *key) /* Decryption key */
Decrypt a buffer that was produced by crypt encode(). You must (obviously, I reckon) use the same algorithm and key as was used to encrypt the data. Returns TRUE if the buffer is decrypted okay. The buffer is encrypted in-place. The algorithm can be one of:
| CRYPT IDEA | Use IDEA encryption. |
| CRYPT MDC | Use MDC encryption. |
| CRYPT DES | Use DES encryption. |
| CRYPT XOR | Use XOR encryption. |
| CRYPT IDEA | Buffer is at least 8 bytes long, key is 16 bytes. |
| CRYPT MDC | Buffer is at least 8 bytes long, key is 8 bytes. |
| CRYPT DES | Buffer is at least 32 bytes long, key is 16 bytes. |
| CRYPT XOR | Buffer is at least 16 bytes long, key is 16 bytes. |
{
return (crypt_data (buffer, buffer_size, algorithm, key, FALSE));
}
#include "sflcryp.h" qbyte calculate_crc (byte *block, size_t length)
Calculates the 32-bit CCITT CRC for a memory block. The CRC calculation is rapid, since the function uses a pre-calculated table. Returns the 32-bit CRC.
{
size_t
offset;
word
this_word;
qbyte
crc_value; /* Running CRC value */
crc_value = 0xFFFFFFFFL;
for (offset = 0; offset < length; offset++)
{
this_word = block [offset];
this_word = this_word ^ (dbyte) (crc_value & 255);
crc_value = (crc_value >> 8) ^ crc_table [this_word];
}
return (crc_value ^ 0xFFFFFFFFL);
}
Filename: sfldate.h
Package: Standard Function Library (SFL)
Written: 96/01/05 iMatix SFL project team sfl@imatix.com
Revised: 98/08/05
Copyright: Copyright (c) 1991-98 iMatix
Includes functions to get the current date/time, calculate the day or week, week of year and leap year. Dates and times are each stored in a 32-bit long value of 8 digits: dates are CCYYMMDD; times are HHMMSSCC. You can compare dates and times directly - e.g. if (date_wanted >= date_now).
sfldate.h defines these symbols, possibly conditionally:
| Symbol: | Defined as: |
|---|---|
| DAY_FRIDAY | 5 |
| DAY_MONDAY | 1 |
| DAY_SATURDAY | 6 |
| DAY_SUNDAY | 0 |
| DAY_THURSDAY | 4 |
| DAY_TUESDAY | 2 |
| DAY_WEDNESDAY | 3 |
| GET_CCYEAR(d) | (int) ( (d) / 10000L) |
| GET_CENTI(t) | (int) ( (t) % 100) |
| GET_CENTURY(d) | (int) ( (d) / 1000000L) |
| GET_DAY(d) | (int) ( (d) % 100) |
| GET_HOUR(t) | (int) ( (t) / 1000000L) |
| GET_MINUTE(t) | (int) (((t) % 1000000L) / 10000L) |
| GET_MONTH(d) | (int) (((d) % 10000L) / 100) |
| GET_SECOND(t) | (int) (((t) % 10000L) / 100) |
| GET_YEAR(d) | (int) (((d) % 1000000L) / 10000L) |
| INTERVAL_CENTI | 1 |
| INTERVAL_DAY | 8640000L |
| INTERVAL_HOUR | 360000L |
| INTERVAL_MIN | 6000 |
| INTERVAL_SEC | 100 |
| MAKE_DATE(c,y,m,d) | (long) (c) * 1000000L + |
| MAKE_TIME(h,m,s,c) | (long) (h) * 1000000L + |
| _SFLDATE_INCLUDED | TRUE |
| timeeq(d1,t1,d2,t2) | ((d1) == (d2) && (t1) == (t2)) |
| timege(d1,t1,d2,t2) | ((d1) > (d2) || ((d1) == (d2) && (t1) >= (t2))) |
| timegt(d1,t1,d2,t2) | ((d1) > (d2) || ((d1) == (d2) && (t1) > (t2))) |
| timele(d1,t1,d2,t2) | ((d1) < (d2) || ((d1) == (d2) && (t1) <= (t2))) |
| timelt(d1,t1,d2,t2) | ((d1) < (d2) || ((d1) == (d2) && (t1) < (t2))) |
| timeneq(d1,t1,d2,t2) | ((d1) != (d2) || (t1) != (t2)) |
#include "sfldate.h" long date_now (void)
Returns the current date as a long value (CCYYMMDD). Since most system clocks do not return a century, this function assumes that all years 80 and above are in the 20th century, and all years 00 to 79 are in the 21st century. For best results, consume before 1 Jan 2080.
{
return (timer to date (time (NULL)));
}
#include "sfldate.h" long time_now (void)
Returns the current time as a long value (HHMMSSCC). If the system clock does not return centiseconds, these are set to zero.
{
#if (defined (__TURBOC__))
/* The Turbo-C gettime() function returns just what we want */
struct time
time_struct;
gettime (&time_struct);
return (MAKE_TIME (time_struct.ti_hour,
time_struct.ti_min,
time_struct.ti_sec,
time_struct.ti_hund));
#elif (defined (__UTYPE_FREEBSD__))
return (timer to time (time (NULL)));
#elif (defined (__UNIX__) || defined (__VMS_XOPEN))
/* The BSD gettimeofday function returns seconds and microseconds */
struct timeval
time_struct;
gettimeofday (&time_struct, 0);
return (timer to time (time_struct.tv_sec)
+ time_struct.tv_usec / 10000);
#elif (defined (WIN32))
/* The Win32 GetLocalTime function returns just what we want */
SYSTEMTIME
time_struct;
GetLocalTime (&time_struct);
return (MAKE_TIME (time_struct.wHour,
time_struct.wMinute,
time_struct.wSecond,
time_struct.wMilliseconds / 10));
#else
/* Otherwise, just get the time without milliseconds */
return (timer to time (time (NULL)));
#endif
}
#include "sfldate.h" Bool leap_year (int year)
Returns TRUE if the year is a leap year. You must supply a 4- digit value for the year: 90 is taken to mean 90 ad. Handles leap centuries correctly.
{
return ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0);
}
#include "sfldate.h" int julian_date (long date)
Returns the number of days since 31 December last year. The Julian date of 1 January is 1.
{
static int
days [12] = {
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
};
int
julian;
julian = days [GET_MONTH (date) - 1] + GET_DAY (date);
if (GET_MONTH (date) > 2 && leap year (GET_YEAR (date)))
julian++;
return (julian);
}
#include "sfldate.h" int day_of_week (long date)
Returns the day of the week where 0 is Sunday, 1 is Monday, ... 6 is Saturday. Uses Zeller's Congurence algorithm.
{
int
year = GET_CCYEAR (date),
month = GET_MONTH (date),
day = GET_DAY (date);
if (month > 2)
month -= 2;
else
{
month += 10;
year--;
}
day = ((13 * month - 1) / 5) + day + (year % 100) +
((year % 100) / 4) + ((year / 100) / 4) - 2 *
(year / 100) + 77;
return (day - 7 * (day / 7));
}
#include "sfldate.h" long next_weekday (long date)
Returns the date of the next weekday, skipping from Friday to Monday.
{
long
days = date to days (date);
if (day of week (date) == 5) /* Friday */
days += 3;
else
if (day of week (date) == 6) /* Saturday */
days += 2;
else
days += 1; /* Sunday to Thursday */
return (days to date (days));
}
#include "sfldate.h" long prev_weekday (long date)
Returns the date of the previous weekday, skipping from Monday to Friday.
{
long
days = date to days (date);
if (day of week (date) == 1) /* Monday */
days -= 3;
else
if (day of week (date) == 0) /* Sunday */
days -= 2;
else
days -= 1; /* Tuesday to Saturday */
return (days to date (days));
}
#include "sfldate.h" int week_of_year (long date)
Returns the week of the year, where 1 is the first full week. Week 0 may or may not exist in any year. Uses a Lillian date algorithm to calculate the week of the year.
{
long
year = GET_CCYEAR (date) - 1501,
day = year * 365 + year / 4 - 29872L + 1
- year / 100 + (year - 300) / 400;
return ((julian date (date) + (int) ((day + 4) % 7)) / 7);
}
#include "sfldate.h" int year_quarter (long date)
Returns the year quarter, 1 to 4, depending on the month specified.
{
return ((GET_MONTH (date) - 1) / 3 + 1);
}
#include "sfldate.h" long default_century (long *date)
Supplies a default century for the year if necessary. If the year is 51 to 99, the century is set to 19. If the year is 0 to 50, the century is set to 20. Returns the adjusted date.
{
if (GET_CENTURY (*date) == 0)
*date += (GET_YEAR (*date) > 50? 19000000L: 20000000L);
return (*date);
}
#include "sfldate.h" word pack_date (long date)
Packs the date into a single unsigned short word. Use this function to store dates when memory space is at a premium. The packed date can be used correctly in comparisons. Returns the packed date. The date must be later than 31 December 1979.
{
return (word) (((GET_CCYEAR (date) - 1980) << 9) +
(GET_MONTH (date) << 5) +
GET_DAY (date));
}
#include "sfldate.h" word pack_time (long time)
Packs the time into a single unsigned short word. Use this function to store times when memory space is at a premium. The packed time can be used correctly in comparisons. Returns the packed time. Seconds are stored with 2-second accuracy and centiseconds are lost.
{
return (word) ((GET_HOUR (time) << 11) +
(GET_MINUTE (time) << 5) +
(GET_SECOND (time) >> 1));
}
#include "sfldate.h" long unpack_date (word packdate)
Converts a packed date back into a long value.
{
int year;
year = ((word) (packdate & 0xfe00) >> 9) + 80;
return (MAKE_DATE (year > 80? 19: 20,
year,
(word) (packdate & 0x01e0) >> 5,
(word) (packdate & 0x001f)));
}
#include "sfldate.h" long unpack_time (word packtime)
Converts a packed time back into a long value.
{
return (MAKE_TIME ((word) (packtime & 0xf800) >> 11,
(word) (packtime & 0x07e0) >> 5,
(word) (packtime & 0x001f) << 1, 0));
}
#include "sfldate.h" long date_to_days (long date)
Converts the date into a number of days since a distant but unspecified epoch. You can use this function to calculate differences between dates, and forward dates. Use days to date() to calculate the reverse function. Author: Robert G. Tantzen, translated from the Algol original in Collected Algorithms of the CACM (algorithm 199). Original translation into C by Nat Howard, posted to Usenet 5 Jul 1985.
{
long
year = GET_YEAR (date),
century = GET_CENTURY (date),
month = GET_MONTH (date),
day = GET_DAY (date);
if (month > 2)
month -= 3;
else
{
month += 9;
if (year)
year--;
else
{
year = 99;
century--;
}
}
return ((146097L * century) / 4L +
(1461L * year) / 4L +
(153L * month + 2L) / 5L +
day + 1721119L);
}
#include "sfldate.h" long days_to_date (long days)
Converts a number of days since some distant but unspecified epoch into a date. You can use this function to calculate differences between dates, and forward dates. Use date to days() to calculate the reverse function. Author: Robert G. Tantzen, translated from the Algol original in Collected Algorithms of the CACM (algorithm 199). Original translation into C by Nat Howard, posted to Usenet 5 Jul 1985.
{
long
century,
year,
month,
day;
days -= 1721119L;
century = (4L * days - 1L) / 146097L;
days = 4L * days - 1L - 146097L * century;
day = days / 4L;
year = (4L * day + 3L) / 1461L;
day = 4L * day + 3L - 1461L * year;
day = (day + 4L) / 4L;
month = (5L * day - 3L) / 153L;
day = 5L * day - 3 - 153L * month;
day = (day + 5L) / 5L;
if (month < 10)
month += 3;
else
{
month -= 9;
if (year++ == 99)
{
year = 0;
century++;
}
}
return (MAKE_DATE (century, year, month, day));
}
#include "sfldate.h" time_t date_to_timer (long date, long time)
Converts the supplied date and time into a time_t timer value. This is the number of non-leap seconds since 00:00:00 GMT Jan 1, 1970. Function was rewritten by Bruce Walter walter@fortean.com.
{
struct tm
time_struct;
time_struct.tm_sec = GET_SECOND (time);
time_struct.tm_min = GET_MINUTE (time);
time_struct.tm_hour = GET_HOUR (time);
time_struct.tm_mday = GET_DAY (date);
time_struct.tm_mon = GET_MONTH (date) - 1;
time_struct.tm_year = GET_CCYEAR (date) - 1900;
time_struct.tm_isdst = -1;
return (mktime (&time_struct));
}
#include "sfldate.h" long timer_to_date (time_t time_secs)
Converts the supplied timer value into a long date value. Dates are stored as long values: CCYYMMDD. If the supplied value is zero, returns zero. The timer value is assumed to be UTC (GMT).
{
struct tm
*time_struct;
if (time_secs == 0)
return (0);
else
{
/* Convert into a long value CCYYMMDD */
time_struct = localtime (&time_secs);
time_struct-> tm_year += 1900;
return (MAKE_DATE (time_struct-> tm_year / 100,
time_struct-> tm_year % 100,
time_struct-> tm_mon + 1,
time_struct-> tm_mday));
}
}
#include "sfldate.h" long timer_to_time (time_t time_secs)
Converts the supplied timer value into a long time value. Times are stored as long values: HHMMSS00. Since the timer value does not hold centiseconds, these are set to zero. If the supplied value was zero, returns zero. The timer value is assumed to be UTC (GMT).
{
struct tm
*time_struct;
if (time_secs == 0)
return (0);
else
{
/* Convert into a long value HHMMSS00 */
time_struct = localtime (&time_secs);
return (MAKE_TIME (time_struct-> tm_hour,
time_struct-> tm_min,
time_struct-> tm_sec,
0));
}
}
#include "sfldate.h" long timer_to_gmdate (time_t time_secs)
Converts the supplied timer value into a long date value in Greenwich Mean Time (GMT). Dates are stored as long values: CCYYMMDD. If the supplied value is zero, returns zero.
{
struct tm
*time_struct;
if (time_secs == 0)
return (0);
else
{
/* Convert into a long value CCYYMMDD */
time_struct = gmtime (&time_secs);
if (time_struct == NULL) /* If gmtime is not implemented */
time_struct = localtime (&time_secs);
ASSERT (time_struct);
time_struct-> tm_year += 1900;
return (MAKE_DATE (time_struct-> tm_year / 100,
time_struct-> tm_year % 100,
time_struct-> tm_mon + 1,
time_struct-> tm_mday));
}
}
#include "sfldate.h" long timer_to_gmtime (time_t time_secs)
Converts the supplied timer value into a long time value in Greenwich Mean Time (GMT). Times are stored as long values: HHMMSS00. On most systems the clock does not return centiseconds, so these are set to zero. If the supplied value is zero, returns zero.
{
struct tm
*time_struct;
if (time_secs == 0)
return (0);
else
{
/* Convert into a long value HHMMSS00 */
time_struct = gmtime (&time_secs);
if (time_struct == NULL) /* If gmtime is not implemented */
time_struct = localtime (&time_secs);
ASSERT (time_struct);
return (MAKE_TIME (time_struct-> tm_hour,
time_struct-> tm_min,
time_struct-> tm_sec,
0));
}
}
#include "sfldate.h" long time_to_csecs (long time)
Converts a time (HHMMSSCC) into a number of centiseconds.
{
return ((long) (GET_HOUR (time) * (long) INTERVAL_HOUR)
+ (long) (GET_MINUTE (time) * (long) INTERVAL_MIN)
+ (long) (GET_SECOND (time) * (long) INTERVAL_SEC)
+ (long) (GET_CENTI (time)));
}
#include "sfldate.h" long csecs_to_time (long csecs)
Converts a number of centiseconds (< INTERVAL_DAY) into a time value (HHMMSSCC).
{
long
hour,
min,
sec;
ASSERT (csecs < INTERVAL_DAY);
hour = csecs / INTERVAL_HOUR;
csecs = csecs % INTERVAL_HOUR;
min = csecs / INTERVAL_MIN;
csecs = csecs % INTERVAL_MIN;
sec = csecs / INTERVAL_SEC;
csecs = csecs % INTERVAL_SEC;
return (MAKE_TIME (hour, min, sec, csecs));
}
#include "sfldate.h" void future_date (long *date, long *time, long days, long csecs)
Calculates a future date and time from the date and time specified, plus an interval specified in days and 1/100th seconds. The date can be any date since some distant epoch (around 1600). If the date and time arguments are both zero, the current date and time are used. Either date and time arguments may be null.
{
long
dummy_date = 0,
dummy_time = 0;
if (date == NULL)
date = &dummy_date;
if (time == NULL)
time = &dummy_time;
/* Set date and time to NOW if necessary */
if (*date == 0 && *time == 0)
{
*date = date now ();
*time = time now ();
}
/* Get future date in days and centiseconds */
days = date to days (*date) + days;
csecs = time to csecs (*time) + csecs;
/* Normalise overflow in centiseconds */
while (csecs >= INTERVAL_DAY)
{
days++;
csecs -= INTERVAL_DAY;
}
/* Convert date and time back into organised values */
*date = days to date (days);
*time = csecs to time (csecs);
}
#include "sfldate.h" void past_date (long *date, long *time, long days, long csecs)
Calculates a past date and time from the date and time specified, minus an interval specified in days and 1/100th seconds. The date can be any date since some distant epoch (around 1600). If the date and time arguments are both zero, the current date and time are used. Either date and time arguments may be null.
{
long
dummy_date = 0,
dummy_time = 0;
if (date == NULL)
date = &dummy_date;
if (time == NULL)
time = &dummy_time;
/* Set date and time to NOW if necessary */
if (*date == 0 && *time == 0)
{
*date = date now ();
*time = time now ();
}
/* Get past date in days and centiseconds */
days = date to days (*date) - days;
csecs = time to csecs (*time) - csecs;
/* Normalise underflow in centiseconds */
while (csecs < 0)
{
days--;
csecs += INTERVAL_DAY;
}
/* Convert date and time back into organised values */
*date = days to date (days);
*time = csecs to time (csecs);
}
#include "sfldate.h"
void
date_diff (
long date1, long time1, /* Date and time */
long date2, long time2, /* minus this date and time */
long *days, long *csecs /* Gives these values */
)
Calculates the difference between two date/time values, and returns the difference as a number of days and a number of centiseconds. The date can be any date since some distant epoch (around 1600). The calculation is date1:time1 - date2:time2. The returned values may be negative.
{
*days = date to days (date1) - date to days (date2);
*csecs = time to csecs (time1) - time to csecs (time2);
}
#include "sfldate.h" Bool valid_date (long date)
Returns TRUE if the date is valid or zero; returns FALSE if the date is not valid.
{
int
month,
day;
Bool
feedback;
static byte
month_days [] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
month = GET_MONTH (date);
day = GET_DAY (date);
if (date == 0)
feedback = TRUE; /* Zero date is okay */
else
if (month < 1 || month > 12)
feedback = FALSE; /* Month out of range */
else
if ((day < 1 || day > month_days [month - 1])
|| (month == 2 && day == 29 && !leap year (GET_YEAR (date))))
feedback = FALSE; /* Day out of range */
else
feedback = TRUE; /* Zero date is okay */
return (feedback);
}
#include "sfldate.h" Bool valid_time (long time)
Returns TRUE if the time is valid or zero; returns FALSE if the time is not valid.
{
return (GET_SECOND (time) < 60
&& GET_MINUTE (time) < 60
&& GET_HOUR (time) < 24);
}
#include "sfldate.h" Bool date_is_future (long date, long time)
Returns TRUE if the specified date and time are in the future. Returns FALSE if the date and time are in the past, or the present (which will be the past by the time you've read this). Date is specified as a YYYYMMDD value; time as HHMMSSCC.
{
return (date > date now ()
|| (date == date now () && time > time now ()));
}
#include "sfldate.h" Bool date_is_past (long date, long time)
Returns TRUE if the specified date and time are in the past. Returns FALSE if the date and time are in the future or present (which despite any assertion to the contrary, is not the past. Although that may change soon). Date is specified as YYYYMMDD; time as HHMMSSCC.
{
return (date < date now ()
|| (date == date now () && time < time now ()));
}
#include "sfldate.h" char * timezone_string (void)
Returns a static string containing the time zone as a 4-digit number, with a leading '+' or '-' character. GMT is represented as "+0000"; Central European Time is "+1000". If the system does not support the timezone, returns "+0000".
{
#if (defined (TIMEZONE))
static char
formatted_string [6]; /* -nnnn plus null */
int
minutes; /* TIMEZONE is in seconds */
minutes = (int) (TIMEZONE / 60);
sprintf (formatted_string, "%03d%02d", 0 - minutes / 60, minutes % 60);
if (*formatted_string == '0')
*formatted_string = '+';
return (formatted_string);
#else
return ("+0000");
#endif
}
#include "sfldate.h" void local_to_gmt (long date, long time, long *gmt_date, long *gmt_time)
Converts the specified date and time to GMT. Returns the GMT date and time in two arguments.
{
time_t
time_value;
time_value = date to timer (date, time);
*gmt_date = timer to gmdate (time_value);
*gmt_time = timer to gmtime (time_value);
}
#include "sfldate.h" void gmt_to_local (long gmt_date, long gmt_time, long *date, long *time)
Converts the specified GMT date and time to the local time. Returns the local date and time in two arguments.
{
time_t
time_value;
struct tm
*time_struct;
/* Convert from GMT */
time_value = date to timer (gmt_date, gmt_time) - TIMEZONE;
time_struct = localtime (&time_value);
if (time_struct-> tm_isdst)
{
time_value += 3600; /* Adjust for daylight savings */
time_struct = localtime (&time_value);
}
time_struct-> tm_year += 1900;
*date = MAKE_DATE (time_struct-> tm_year / 100,
time_struct-> tm_year % 100,
time_struct-> tm_mon + 1,
time_struct-> tm_mday);
*time = MAKE_TIME (time_struct-> tm_hour,
time_struct-> tm_min,
time_struct-> tm_sec,
0);
}
Filename: sflexdr.h
Package: Standard Function Library (SFL)
Written: 96/06/25 iMatix SFL project team sfl@imatix.com
Revised: 97/09/08
Copyright: Copyright (c) 1991-98 iMatix
Provides functions to read and write data in a portable format that is suitable for transmission to other systems. The principle is similar to the ONC XDR standard used in RPC, but somewhat simpler. The streams produced by these functions are not compatible with ONC XDR.
sflexdr.h defines these symbols, possibly conditionally:
| Symbol: | Defined as: |
|---|---|
| _SFLEXDR_INCLUDED | TRUE |
#include "sflexdr.h" int exdr_write (byte *buffer, const char *format, ...)
Accepts a list of data items, prepares them according to the format string, and stores the result in the buffer. The buffer may be transmitted to another system, then decoded using exdr_read. Assumes nothing about system word sizes, etc. However, does assume that both systems use ASCII. If the buffer address is NULL, does not store the data items, but counts the effective size and returns that. The null-terminated format string can contain these sequences:
| c | Single character value (may be multibyte) |
| b | Single byte value |
| w,d | Double byte value (16-bit short integer) |
| q,l | Quad-byte value (32-bit long integer) |
| s | Null-terminated string (address of string), or NULL |
| B | Bool value (16-bit short integer) |
| m | Memory descriptor size (16-bit integer), >= 0 |
| M | Memory descriptor body (pointer to block), or NULL |
| h | Huge memory descriptor size (31-bit integer), >= 0 |
| H | Huge memory descriptor body (pointer), or NULL |
{
va_list
argptr; /* Argument list pointer */
byte
byte_value, /* Byte value from arguments */
*target, /* Pointer into target buffer */
*block; /* Source block for 'M' type */
char
*string; /* Source string for 's' type */
dbyte
dbyte_value; /* Network format dbyte value */
qbyte
memory_size = 0, /* Memory descriptor size value */
qbyte_value; /* Network format qbyte value */
ASSERT (format);
va_start (argptr, format); /* Start variable arguments list */
target = buffer;
while (*format)
{
switch (*format++)
{
case 'c': /* Character */
case 'b': /* Single byte */
byte_value = (byte) va_arg (argptr, int);
if (buffer)
*(byte *) target = byte_value;
target += 1;
break;
case 'd': /* Signed short integer */
case 'w': /* Unsigned short integer */
case 'B': /* Bool */
dbyte_value = htons ((short) va_arg (argptr, int));
if (buffer)
{
*(byte *) target++ = *((byte *) &dbyte_value);
*(byte *) target++ = *((byte *) &dbyte_value + 1);
}
else
target += 2;
break;
case 'l': /* Signed long (32-bit) */
case 'q': /* 4-byte unsigned value */
qbyte_value = htonl (va_arg (argptr, qbyte));
if (buffer)
{
*(byte *) target++ = *((byte *) &qbyte_value);
*(byte *) target++ = *((byte *) &qbyte_value + 1);
*(byte *) target++ = *((byte *) &qbyte_value + 2);
*(byte *) target++ = *((byte *) &qbyte_value + 3);
}
else
target += 4;
break;
case 's': /* Null-terminated string */
string = va_arg (argptr, char *);
if (string)
{
if (buffer)
strcpy ((char *) target, string);
target += strlen (string) + 1;
}
else /* Store NULL as single null byte */
{
if (buffer)
*(byte *) target++ = 0;
else
target += 1;
}
break;
case 'm': /* Memory descriptor size */
memory_size = va_arg (argptr, int);
dbyte_value = htons ((dbyte) memory_size);
if (buffer)
{
*(byte *) target++ = *((byte *) &dbyte_value);
*(byte *) target++ = *((byte *) &dbyte_value + 1);
}
else
target += 2;
break;
case 'M': /* Memory descriptor body */
block = va_arg (argptr, byte *);
if (block)
{
if (buffer)
memcpy (target, block, (size_t) memory_size);
target += (size_t) memory_size;
}
else
ASSERT (memory_size == 0);
break;
case 'h': /* Huge memory descriptor size */
memory_size = va_arg (argptr, qbyte);
qbyte_value = htonl (memory_size);
if (buffer)
{
*(byte *) target++ = *((byte *) &qbyte_value);
*(byte *) target++ = *((byte *) &qbyte_value + 1);
*(byte *) target++ = *((byte *) &qbyte_value + 2);
*(byte *) target++ = *((byte *) &qbyte_value + 3);
}
else
target += 4;
break;
case 'H': /* Huge memory descriptor body */
block = va_arg (argptr, byte *);
if (block)
{
if (buffer)
memcpy (target, block, (size_t) memory_size);
target += (size_t) memory_size;
}
else
ASSERT (memory_size == 0);
break;
}
}
va_end (argptr); /* End variable arguments list */
return ((int) (target - buffer));
}
#include "sflexdr.h" int exdr_writed (DESCR *buffer, const char *format, ...)
As exdr write(), but accepts a DESCR buffer. This is more secure. Aborts with an error if the formatted data would be too long for the buffer, if compiled with DEBUG. The buffer address cannot be NULL.
{
va_list
argptr; /* Argument list pointer */
byte
*target, /* Pointer into target buffer */
*block; /* Source block for 'M' type */
char
*string; /* Source string for 's' type */
dbyte
dbyte_value; /* Network format dbyte value */
qbyte
memory_size = 0, /* Memory descriptor size value */
qbyte_value; /* Network format qbyte value */
size_t
used_size; /* Current buffer data size */
ASSERT (buffer);
ASSERT (format);
va_start (argptr, format); /* Start variable arguments list */
target = buffer-> data;
while (*format)
{
used_size = (size_t) (target - buffer-> data);
switch (*format++)
{
case 'c': /* Character */
case 'b': /* Single byte */
*(byte *) target = (byte) va_arg (argptr, int);
ASSERT (used_size + 1 <= buffer-> size);
target += 1;
break;
case 'd': /* Signed short integer */
case 'w': /* Unsigned short integer */
case 'B': /* Bool */
dbyte_value = htons ((short) va_arg (argptr, int));
ASSERT (used_size + 2 <= buffer-> size);
*(byte *) target++ = *((byte *) &dbyte_value);
*(byte *) target++ = *((byte *) &dbyte_value + 1);
break;
case 'l': /* Signed long (32-bit) */
case 'q': /* 4-byte unsigned value */
qbyte_value = htonl (va_arg (argptr, qbyte));
ASSERT (used_size + 4 <= buffer-> size);
*(byte *) target++ = *((byte *) &qbyte_value);
*(byte *) target++ = *((byte *) &qbyte_value + 1);
*(byte *) target++ = *((byte *) &qbyte_value + 2);
*(byte *) target++ = *((byte *) &qbyte_value + 3);
break;
case 's': /* Null-terminated string */
string = va_arg (argptr, char *);
if (string)
{
ASSERT (used_size + strlen (string) + 1 <= buffer-> size);
strcpy ((char *) target, string);
target += strlen (string) + 1;
}
else /* Store NULL as single null byte */
{
ASSERT (used_size + 1 <= buffer-> size);
*(byte *) target++ = 0;
}
break;
case 'm': /* Memory descriptor size */
memory_size = va_arg (argptr, int);
ASSERT (used_size + 2 + memory_size <= buffer-> size);
dbyte_value = htons ((dbyte) memory_size);
*(byte *) target++ = *((byte *) &dbyte_value);
*(byte *) target++ = *((byte *) &dbyte_value + 1);
break;
case 'M': /* Memory descriptor body */
block = va_arg (argptr, byte *);
if (block)
{
memcpy (target, block, (size_t) memory_size);
target += (size_t) memory_size;
}
else
ASSERT (memory_size == 0);
break;
case 'h': /* Huge memory descriptor size */
memory_size = va_arg (argptr, qbyte);
ASSERT (used_size + 4 + memory_size <= buffer-> size);
qbyte_value = htonl (memory_size);
*(byte *) target++ = *((byte *) &qbyte_value);
*(byte *) target++ = *((byte *) &qbyte_value + 1);
*(byte *) target++ = *((byte *) &qbyte_value + 2);
*(byte *) target++ = *((byte *) &qbyte_value + 3);
break;
case 'H': /* Huge memory descriptor body */
block = va_arg (argptr, byte *);
if (block)
{
memcpy (target, block, (size_t) memory_size);
target += (size_t) memory_size;
}
else
ASSERT (memory_size == 0);
break;
}
}
va_end (argptr); /* End variable arguments list */
return ((int) (target - buffer-> data));
}
#include "sflexdr.h" int exdr_read (const byte *buffer, const char *format, ...)
Unpacks a buffer prepared by exdr write() into as set of data items as specified by a format string. See exdr write() for the syntax of the format string. Each format sequence corresponds to one item in in the list which must be specified as an address. Target strings and memory blocks must be large enough to hold the returned data: target strings and blocks can also be null, in which case the function calls mem_alloc to allocate heap memory. Note that you must supply a pointer to the string or memory block address, not the address itself. It is a common error to pass the address of a static block - see the example below for the *right* way to do it. Any of the argument addresses can be NULL, in which case that field is ignored. This is useful to get a few selected fields out of a message. Errors in the argument list can cause memory corruption and unpredictable program results. If a memory allocation fails, all previous memory allocations are "rolled back" and the function returns the value -1. Return codes: 0 - normal -1 - memory allocation failed
char *string = NULL;
byte buffer [1000];
byte *buffaddr = buffer;
int value, length;
exdr_read (buffer, "qdsmM", NULL, &value, &string, &length, &buffaddr);
{
MEMTRN
*memtrn; /* Memory transaction */
va_list
argptr; /* Argument list pointer */
void
*target; /* Source data item address */
dbyte
string_size, /* String size */
dbyte_value; /* Network format dbyte value */
qbyte
memory_size = 0, /* Memory descriptor size value */
qbyte_value; /* Network format qbyte value */
ASSERT (buffer);
ASSERT (format);
memtrn = mem new trans ();
va_start (argptr, format); /* Start variable arguments list */
while (*format)
{
target = va_arg (argptr, void *);
switch (*format++)
{
case 'c': /* Character */
case 'b': /* Single byte */
if (target)
*(byte *) target = *(byte *) buffer;
buffer += 1;
break;
case 'd': /* Signed short integer */
case 'w': /* Unsigned short integer */
case 'B': /* Bool */
*((byte *) &dbyte_value) = *(byte *) buffer++;
*((byte *) &dbyte_value + 1) = *(byte *) buffer++;
if (target)
*(dbyte *) target = ntohs (dbyte_value);
break;
case 'l': /* Signed long (32-bit) */
case 'q': /* 4-byte unsigned value */
*((byte *) &qbyte_value) = *(byte *) buffer++;
*((byte *) &qbyte_value + 1) = *(byte *) buffer++;
*((byte *) &qbyte_value + 2) = *(byte *) buffer++;
*((byte *) &qbyte_value + 3) = *(byte *) buffer++;
if (target)
*(qbyte *) target = ntohl (qbyte_value);
break;
case 's': /* Null-terminated string */
string_size = strlen ((char *) buffer) + 1;
if (target)
{
if (*(byte **) target == NULL)
*(byte **) target = mem_alloc (string_size);
if (*(byte **) target)
memcpy (*(byte **) target, buffer, string_size);
else
{
mem rollback (memtrn);
return (-1);
}
}
buffer += string_size;
break;
case 'm': /* Memory descriptor size */
*((byte *) &dbyte_value) = *(byte *) buffer++;
*((byte *) &dbyte_value + 1) = *(byte *) buffer++;
memory_size = ntohs (dbyte_value);
if (target)
*(dbyte *) target = (dbyte) memory_size;
break;
case 'M': /* Memory descriptor body */
if (target && memory_size > 0)
{
if (*(byte **) target == NULL)
*(byte **) target = mem_alloc ((size_t) memory_size);
if (*(byte **) target)
memcpy (*(byte **) target, buffer,
(size_t) memory_size);
else
{
mem rollback (memtrn);
return (-1);
}
}
buffer += (size_t) memory_size;
break;
case 'h': /* Huge memory descriptor size */
*((byte *) &qbyte_value) = *(byte *) buffer++;
*((byte *) &qbyte_value + 1) = *(byte *) buffer++;
*((byte *) &qbyte_value + 2) = *(byte *) buffer++;
*((byte *) &qbyte_value + 3) = *(byte *) buffer++;
memory_size = ntohl (qbyte_value);
if (target)
*(qbyte *) target = memory_size;
break;
case 'H': /* Huge memory descriptor body */
if (target && memory_size > 0)
{
if (*(byte **) target == NULL)
*(byte **) target = mem_alloc ((size_t) memory_size);
if (*(byte **) target)
memcpy (*(byte **) target, buffer,
(size_t) memory_size);
else
{
mem rollback (memtrn);
return (-1);
}
}
buffer += (size_t) memory_size;
break;
}
}
va_end (argptr); /* End variable arguments list */
mem commit (memtrn);
return (0);
}
Filename: sflfind.h
Package: Standard Function Library (SFL)
Written: 96/04/24 iMatix SFL project team sfl@imatix.com
Revised: 98/05/03
Copyright: Copyright (c) 1991-98 iMatix
Searches for a pattern within a string or block of memory using a variant of the Boyer-Moore algorithm (improved by Horspool and Sunday). As fast or faster than the normal Boyer-Moore algorithm for most search strings, and much simpler. Includes a basic function for searching blocks of memory with known sizes, plus an envelope that searches null-delimited strings. Provides the option of repeatedly searching for the same pattern without re-parsing the pattern each time. Original algorithm published by BOYER, R., and S. MOORE 1977, "A Fast String Searching Algorithm." CACM, 20, 762-72. Simplifications by HORSPOOL, R. N. 1980, "Practical Fast Searching in Strings." Software - Practice and Experience, 10, 501-06. More improvements by HUME, A., and D. M. SUNDAY 1991, "Fast String Searching." AT&T Bell Labs Computing Science Technical Report No. 156. Implemented in C by P. Hintjens. strfind r() and memfind r(), are reentrant versions of strfind() and memfind() for single searches, and strfind rb() and memfind rb() are reentrant versions of strfind() and memfind() supporting repeat searches against the same pattern.
sflfind.h defines these symbols, possibly conditionally:
| Symbol: | Defined as: |
|---|---|
| _SFLFIND_INCLUDED | TRUE |
#include "sflfind.h"
char *
strfind (const char *string, /* String containing data */
const char *pattern, /* Pattern to search for */
Bool repeat_find) /* Same pattern as last time */
Searches for a pattern in a string using the Boyer-Moore- Horspool-Sunday algorithm. The string and pattern are null- terminated strings. Returns a pointer to the pattern if found within the string, or NULL if the pattern was not found. If you repeatedly scan for the same pattern, use the repeat_find argument. If this is TRUE, the function does not re-parse the pattern. You must of course call the function with repeat_find equal to FALSE the first time. This function is meant to handle character data, and is most effective when you work with large strings. To search binary data use memfind(). Will not work on multibyte characters.
char *result;
result = strfind ("abracadabra", "cad", FALSE);
if (result)
puts (result);
{
static size_t
searchbuf [256]; /* Fixed search buffer */
ASSERT (string); /* Expect non-NULL pointers, but */
ASSERT (pattern); /* fall through if not debugging */
return (char *) memfind rb (string, strlen (string),
pattern, strlen (pattern),
searchbuf, &repeat_find);
}
#include "sflfind.h"
char *
strfind_r (const char *string, /* String containing data */
const char *pattern) /* Pattern to search for */
Searches for a pattern in a string using the Boyer-Moore- Horspool-Sunday algorithm. The string and pattern are null- terminated strings. Returns a pointer to the pattern if found within the string, or NULL if the pattern was not found. This function is meant to handle character data, and is most effective when you work with large strings. To search binary data use memfind(). Will not work on multibyte characters. Reentrant.
char *result;
result = strfind_r ("abracadabra", "cad");
if (result)
puts (result);
{
size_t
searchbuf [256]; /* One-time search buffer */
Bool
secondtime = FALSE; /* Search buffer init needed */
ASSERT (string); /* Expect non-NULL pointers, but */
ASSERT (pattern); /* fall through if not debugging */
return (char *) memfind rb (string, strlen (string),
pattern, strlen (pattern),
searchbuf, &secondtime);
}
#include "sflfind.h"
char *
strfind_rb (const char *string, /* String containing data */
const char *pattern, /* Pattern to search for */
size_t *shift, /* Working buffer between searches */
Bool *repeat_find) /* Flag for first/later search */
Searches for a pattern in a string using the Boyer-Moore- Horspool-Sunday algorithm. The string and pattern are null- terminated strings. Returns a pointer to the pattern if found within the string, or NULL if the pattern was not found. Supports more efficient repeat searches (for the same pattern), through a supplied search buffer. The search buffer must be long enough to contain 256 (2**8) size_t entries. On the first call repeat_find must be set to FALSE. After the search buffer has been initialised, repeat_find will be set to TRUE by the function, avoiding the search buffer initialisation on later calls. This function is most effective when repeated searches are made for the same pattern in one or more strings. This function is meant to handle character data, and is most effective when you work with large strings. To search binary data use memfind(). Will not work on multibyte characters. Reentrant.
char *result;
Bool repeat_search = FALSE;
size_t searchbuf[256];
result = strfind_rb ("abracadabra", "cad", searchbuf, &repeat_search);
if (result)
{
puts (result);
result = strfind_rb ("cad/cam", "cad", searchbuf, &repeat_search);
if (result)
puts (result);
}
{
ASSERT (string); /* Expect non-NULL pointers, but */
ASSERT (pattern); /* fall through if not debugging */
ASSERT (shift);
ASSERT (repeat_find);
return (char *) memfind rb (string, strlen (string),
pattern, strlen (pattern),
shift, repeat_find);
}
#include "sflfind.h"
void *
memfind (const void *block, /* Block containing data */
size_t block_size, /* Size of block in bytes */
const void *pattern, /* Pattern to search for */
size_t pattern_size, /* Size of pattern block */
Bool repeat_find) /* Same pattern as last time */
Searches for a pattern in a block of memory using the Boyer- Moore-Horspool-Sunday algorithm. The block and pattern may contain any values; you must explicitly provide their lengths. Returns a pointer to the pattern if found within the block, or NULL if the pattern was not found. If you repeatedly scan for the same pattern, use the repeat_find argument. If this is TRUE, the function does not re-parse the pattern. This function is meant to handle binary data. If you need to search strings, use the strfind_r or strfind rb() functions. Non-Reentrant.
{
static size_t
searchbuf [256]; /* Static shared search buffer */
ASSERT (block); /* Expect non-NULL pointers, but */
ASSERT (pattern); /* full through if not debugging */
return memfind rb (block, block_size, pattern, pattern_size,
searchbuf, &repeat_find);
}
#include "sflfind.h"
void *
memfind_r (const void *block, /* Block containing data */
size_t block_size, /* Size of block in bytes */
const void *pattern, /* Pattern to search for */
size_t pattern_size) /* Size of pattern block */
Searches for a pattern in a block of memory using the Boyer- Moore-Horspool-Sunday algorithm. The block and pattern may contain any values; you must explicitly provide their lengths. Returns a pointer to the pattern if found within the block, or NULL if the pattern was not found. This function is meant to handle binary data, for a single search for a given pattern. If you need to search strings, use the strfind r() or strfind rb() functions. If you want to do efficient repeated searches for one pattern, use memfind rb(). Reentrant.
{
size_t
searchbuf [256]; /* One-time search buffer */
Bool
secondtime = FALSE;
ASSERT (block); /* Expect non-NULL pointers, but */
ASSERT (pattern); /* full through if not debugging */
return memfind rb (block, block_size, pattern, pattern_size,
searchbuf, &secondtime);
}
#include "sflfind.h"
void *
memfind_rb (const void *in_block, /* Block containing data */
size_t block_size, /* Size of block in bytes */
const void *in_pattern, /* Pattern to search for */
size_t pattern_size, /* Size of pattern block */
size_t *shift, /* Shift table (search buffer) */
Bool *repeat_find) /* TRUE: search buffer already init */
Searches for a pattern in a block of memory using the Boyer- Moore-Horspool-Sunday algorithm. The block and pattern may contain any values; you must explicitly provide their lengths. Returns a pointer to the pattern if found within the block, or NULL if the pattern was not found. On the first search with a given pattern, *repeat_find should be FALSE. It will be set to TRUE after the shift table is initialised, allowing the initialisation phase to be skipped on subsequent searches. shift must point to an array big enough to hold 256 (8**2) size_t values. This function is meant to handle binary data, for repeated searches for the same pattern. If you need to search strings, use the strfind r() or strfind rb() functions. If you wish to search for a pattern only once consider using memfind r(). Reentrant.
{
size_t
byte_nbr, /* Distance through block */
match_size; /* Size of matched part */
const byte
*match_base = NULL, /* Base of match of pattern */
*match_ptr = NULL, /* Point within current match */
*limit = NULL; /* Last potiental match point */
const byte
*block = (byte *) in_block, /* Concrete pointer to block data */
*pattern = (byte *) in_pattern; /* Concrete pointer to search value */
ASSERT (block); /* Expect non-NULL pointers, but */
ASSERT (pattern); /* fail gracefully if not debugging */
ASSERT (shift); /* NULL repeat_find => is false */
if (block == NULL || pattern == NULL || shift == NULL)
return (NULL);
/* Pattern must be smaller or equal in size to string */
if (block_size < pattern_size)
return (NULL); /* Otherwise it's not found */
if (pattern_size == 0) /* Empty patterns match at start */
return ((void *)block);
/* Build the shift table unless we're continuing a previous search */
/* The shift table determines how far to shift before trying to match */
/* again, if a match at this point fails. If the byte after where the */
/* end of our pattern falls is not in our pattern, then we start to */
/* match again after that byte; otherwise we line up the last occurence */
/* of that byte in our pattern under that byte, and try match again. */
if (!repeat_find || !*repeat_find)
{
for (byte_nbr = 0; byte_nbr < 256; byte_nbr++)
shift [byte_nbr] = pattern_size + 1;
for (byte_nbr = 0; byte_nbr < pattern_size; byte_nbr++)
shift [(byte) pattern [byte_nbr]] = pattern_size - byte_nbr;
if (repeat_find)
*repeat_find = TRUE;
}
/* Search for the block, each time jumping up by the amount */
/* computed in the shift table */
limit = block + (block_size - pattern_size + 1);
ASSERT (limit > block);
for (match_base = block;
match_base < limit;
match_base += shift [*(match_base + pattern_size)])
{
match_ptr = match_base;
match_size = 0;
/* Compare pattern until it all matches, or we find a difference */
while (*match_ptr++ == pattern [match_size++])
{
ASSERT (match_size <= pattern_size &&
match_ptr == (match_base + match_size));
/* If we found a match, return the start address */
if (match_size >= pattern_size)
return ((void*)(match_base));
}
}
return (NULL); /* Found nothing */
}
#include "sflfind.h"
char *
txtfind (const char *string, /* String containing data */
const char *pattern) /* Pattern to search for */
Searches for a case-insensitive text pattern in a string using the Boyer-Moore-Horspool-Sunday algorithm. The string and pattern are null-terminated strings. Returns a pointer to the pattern if found within the string, or NULL if the pattern was not found. Will match strings irrespective of case. To match exact strings, use strfind(). Will not work on multibyte characters.
char *result;
result = txtfind ("AbracaDabra", "cad");
if (result)
puts (result);
{
size_t
shift [256]; /* Shift distance for each value */
size_t
string_size,
pattern_size,
byte_nbr, /* Index into byte array */
match_size; /* Size of matched part */
const char
*match_base = NULL, /* Base of match of pattern */
*match_ptr = NULL, /* Point within current match */
*limit = NULL; /* Last potiental match point */
ASSERT (string); /* Expect non-NULL pointers, but */
ASSERT (pattern); /* fail gracefully if not debugging */
if (string == NULL || pattern == NULL)
return (NULL);
string_size = strlen (string);
pattern_size = strlen (pattern);
/* Pattern must be smaller or equal in size to string */
if (string_size < pattern_size)
return (NULL); /* Otherwise it cannot be found */
if (pattern_size == 0) /* Empty string matches at start */
return (char *) string;
/* Build the shift table */
/* The shift table determines how far to shift before trying to match */
/* again, if a match at this point fails. If the byte after where the */
/* end of our pattern falls is not in our pattern, then we start to */
/* match again after that byte; otherwise we line up the last occurence */
/* of that byte in our pattern under that byte, and try match again. */
for (byte_nbr = 0; byte_nbr < 256; byte_nbr++)
shift [byte_nbr] = pattern_size + 1;
for (byte_nbr = 0; byte_nbr < pattern_size; byte_nbr++)
shift [(byte) tolower (pattern [byte_nbr])] = pattern_size - byte_nbr;
/* Search for the string. If we don't find a match, move up by the */
/* amount we computed in the shift table above, to find location of */
/* the next potiental match. */
limit = string + (string_size - pattern_size + 1);
ASSERT (limit > string);
for (match_base = string;
match_base < limit;
match_base += shift [(byte) tolower (*(match_base + pattern_size))])
{
match_ptr = match_base;
match_size = 0;
/* Compare pattern until it all matches, or we find a difference */
while (tolower (*match_ptr++) == tolower (pattern [match_size++]))
{
ASSERT (match_size <= pattern_size &&
match_ptr == (match_base + match_size));
/* If we found a match, return the start address */
if (match_size >= pattern_size)
return ((char *)(match_base));
}
}
return (NULL); /* Found nothing */
}
Filename: sflfile.h
Package: Standard Function Library (SFL)
Written: 92/10/25 iMatix SFL project team sfl@imatix.com
Revised: 98/08/30
Copyright: Copyright (c) 1991-98 iMatix
Provides functions to read and write files with explicit new- line/carriage-return control; to find files on a path; to copy files, check files' protection, etc.
sflfile.h defines these symbols, possibly conditionally:
| Symbol: | Defined as: |
|---|---|
| FILE_DIR_MAX | 64 /* Max size of directory name */ |
| FILE_NAME_MAX | 160 /* Max size of filename */ |
| FOPEN_APPEND_BINARY | (various) |
| FOPEN_APPEND_TEXT | (various) |
| FOPEN_READ_BINARY | (various) |
| FOPEN_READ_TEXT | (various) |
| FOPEN_WRITE_BINARY | (various) |
| FOPEN_WRITE_TEXT | (various) |
| _SFLFILE_INCLUDED | TRUE |
| file_lines(f) | get_file_lines(f) /* Changed 98/07/23 */ |
#include "sflfile.h"
FILE *
file_open (
const char *filename, /* Name of file to open */
char mode) /* 'r', 'w', or 'a' */
opens a text file for reading or writing. Use in combination with the file read() and file write() functions. These functions handle end-of-line sequences using a heuristic that works as follows. ... (at this point the author went for a pint of beer and has not been seen since. We're hoping that the old version - following - is ok.)Opens the specified file for input or output. If you use the file_read / file_write functions you must open the file using this function. This set of functions lets you read files without concern for the line format (CRLF or LF). Mode should be one of 'r' 'w' 'a'. Returns a FILE pointer if the file is opened correctly; else NULL. Sets the global variable file_crlf to FALSE on all systems except MS-DOS (and Windows by inheritence) where it is set to TRUE by default. When opening a file in append mode, automatically removes any Ctrl-Z character under MS-DOS or OS/2.
{
ASSERT (filename);
# if (defined (MSDOS_FILESYSTEM))
file_crlf = TRUE;
# else
file_crlf = FALSE;
# endif
if (mode == 'r')
return (fopen (filename, FOPEN_READ_BINARY));
else
if (mode == 'w')
return (fopen (filename, FOPEN_WRITE_BINARY));
else
if (mode == 'a'
&& safe to extend (filename))
return (fopen (filename, FOPEN_APPEND_BINARY));
else
return (NULL); /* Invalid mode */
}
#include "sflfile.h"
FILE *
file_locate (
const char *path,
const char *name,
const char *ext)
Combines the functions of file where() and file_open when you want to read a file. Searches for a file on a specified path, opens the file if found, and returns a FILE * for the open file. Returns NULL if the file was not found or could not be opened for reading.
{
char
*filename;
ASSERT (name);
filename = file where ('r', path, name, ext);
if (filename)
return (file open (filename, 'r'));
else
return (NULL);
}
#include "sflfile.h"
int
file_close (
FILE *stream)
Closes an open file stream. Returns 0 if okay, -1 if there was an error. For now, equivalent to fclose, and supplied because it looks nice when you use file open() and file close() together.
{
ASSERT (stream);
return (fclose (stream));
}
#include "sflfile.h"
Bool
file_read (
FILE *stream,
char *string)
Reads a line of text delimited by newline from the stream. The string must be LINE_MAX + 1 long. Places a null byte in place of the newline character. Expands tab characters to every 8th column. Returns TRUE when there is more input waiting; FALSE when the last line of the file has been read. Sets the global variable file_crlf to TRUE if CR was found in the file. This variable is by default FALSE. It is also used by file_write.
{
int
ch, /* Character read from file */
cnbr; /* Index into returned string */
ASSERT (stream);
ASSERT (string);
cnbr = 0; /* Start at the beginning... */
memset (string, ' ', LINE_MAX); /* and prepare entire line */
for (;;)
{
ch = fgetc (stream); /* Get next character from file */
if (ch == '\t') /* Jump if tab */
cnbr = ((cnbr >> 3) << 3) + 8;
else
if (ch == '\r') /* Found carriage-return */
file_crlf = TRUE; /* Set flag and ignore CR */
else
if ((ch == '\n') /* Have end of line */
|| (ch == EOF) /* or end of file */
|| (ch == 26)) /* or MS-DOS Ctrl-Z */
{
string [cnbr] = '\0'; /* Terminate string */
return (ch == '\n' || cnbr); /* and return TRUE/FALSE */
}
else
if (cnbr < LINE_MAX)
string [cnbr++] = (char) ch; /* Else add char to string */
if (cnbr >= LINE_MAX) /* Return in any case if line is */
{ /* too long - the line will be */
string [LINE_MAX] = '\0'; /* cut into pieces */
return (TRUE);
}
}
}
#include "sflfile.h"
Bool
file_readn (
FILE *stream,
char *string,
int line_max)
Works as file read() but with a maximum line-length specified by the caller. The supplied buffer must be at least as large as the specified line_max + 1.
{
int
ch, /* Character read from file */
cnbr; /* Index into returned string */
ASSERT (stream);
ASSERT (string);
cnbr = 0; /* Start at the beginning... */
memset (string, ' ', line_max); /* and prepare entire line */
for (;;)
{
ch = fgetc (stream); /* Get next character from file */
if (ch == '\t') /* Jump if tab */
cnbr = ((cnbr >> 3) << 3) + 8;
else
if (ch == '\r') /* Found carriage-return */
file_crlf = TRUE; /* Set flag and ignore CR */
else
if ((ch == '\n') /* Have end of line */
|| (ch == EOF) /* or end of file */
|| (ch == 26)) /* or MS-DOS Ctrl-Z */
{
string [cnbr] = '\0'; /* Terminate string */
return (ch == '\n' || cnbr); /* and return TRUE/FALSE */
}
else
if (cnbr < line_max)
string [cnbr++] = (char) ch; /* Else add char to string */
if (cnbr >= line_max) /* Return in any case if line is */
{ /* too long - the line will be */
string [line_max] = '\0'; /* cut into pieces */
return (TRUE);
}
}
}
#include "sflfile.h"
char *
file_write (
FILE *stream,
const char *string)
Writes a line of text to the specified output stream. If the variable file_crlf is TRUE, adds a carriage-return to the line being written to the output stream. This variable is supplied so that you can either ignore crlf issues (do nothing), or handle them explicitly (play with file_crlf). Returns the string written, or NULL if no data could be written to the file.
{
ASSERT (stream);
ASSERT (string);
fputs (string, stream);
if (file_crlf)
fputc ('\r', stream);
if (fputc ('\n', stream) == EOF)
return (NULL);
else
return ((char *) string);
}
#include "sflfile.h"
int
file_copy (
const char *dest,
const char *src,
char mode)
Copies a file called src to one called dest. The dest file may not already exist. If mode is 'b', copies a binary file; if mode is 't', copies a text file. This distinction only applies to MS-DOS file systems; on other platforms the two modes are equivalent. Returns 0 if no problems occurred, -1 if an error occurred, 1 if the destination file already exists.
{
FILE *inf, *outf;
char *buffer,
openmode [3] = "??";
size_t chars_read; /* Amount read from stream */
int feedback = 0;
ASSERT (dest);
ASSERT (src);
if (file exists (dest))
return (1); /* Cancel: dest already exists */
# if (defined (MSDOS_FILESYSTEM))
openmode [1] = mode;
# else
openmode [1] = 0;
# endif
openmode [0] = 'r';
if ((inf = fopen (src, openmode)) == NULL)
return (-1); /* Input file not found */
if ((buffer = mem_alloc (SHRT_MAX)) == NULL)
feedback = -1; /* Insufficient memory for buffer */
else
{
openmode [0] = 'w';
if ((outf = fopen (dest, openmode)) == NULL)
{
mem_free (buffer);
return (-1); /* Could not create output file */
}
while ((chars_read = fread (buffer, 1, SHRT_MAX, inf)) != 0)
if (fwrite (buffer, 1, chars_read, outf) != chars_read)
{
feedback = -1;
break;
}
fclose (outf);
mem_free (buffer);
}
fclose (inf);
return (feedback);
}
#include "sflfile.h"
int
file_concat (
const char *dest,
const char *src)
Copies the contents of dest onto src. If src does not exist, it is created. Returns 0 if the concatenation operation succeeded, or -1 if some error occurred.
{
FILE *inf, *outf;
char *buffer;
size_t chars_read; /* Amount read from stream */
int feedback = 0;
ASSERT (dest);
ASSERT (src);
if ((inf = fopen (src, FOPEN_READ_BINARY)) == NULL)
return (-1); /* Input file not found */
if ((buffer = mem_alloc (SHRT_MAX)) == NULL)
feedback = -1; /* Insufficient memory for buffer */
else
{
if ((outf = fopen (dest, FOPEN_APPEND_BINARY)) == NULL)
{
mem_free (buffer);
return (-1); /* Could not create output file */
}
while ((chars_read = fread (buffer, 1, SHRT_MAX, inf)) != 0)
if (fwrite (buffer, 1, chars_read, outf) != chars_read)
{
feedback = -1;
break;
}
fclose (outf);
mem_free (buffer);
}
fclose (inf);
return (feedback);
}
#include "sflfile.h"
int
file_rename (
const char *oldname,
const char *newname)
Renames a file from oldname to newname. Returns 0 if okay, or - 1 if there was an error. Does not overwrite existing files.
{
# if (defined (MSDOS_FILESYSTEM))
char *dos_newname;
int feedback;
ASSERT (oldname);
ASSERT (newname);
dos_newname = mem_strdup (newname);
strconvch (dos_newname, '/', '\\');
feedback = rename (oldname, dos_newname);
mem_free (dos_newname);
return (feedback);
# else
ASSERT (oldname);
ASSERT (newname);
return (rename (oldname, newname));
# endif
}
#include "sflfile.h"
int
file_delete (
const char *filename)
Deletes the specified file. Returns 0 if okay, -1 in case of an error.
{
#if (defined (__VMS__))
ASSERT (filename);
return (remove (filename));
#elif (defined (WIN32))
int
rc;
ASSERT (filename);
rc = !DeleteFile (filename);
if (rc && errno == EACCES)
{
/* Under WinNT and Win95, a delete of a freshly-created file can
* sometimes fail with a permission error which passes after a
* short delay. Ugly but it seems to work.
*/
Sleep (200);
rc = !DeleteFile (filename);
}
return (rc);
#else
ASSERT (filename);
return (unlink (filename));
#endif
}
#include "sflfile.h"
Bool
file_exists (
const char *filename)
Returns TRUE if the file exists, or FALSE if it does not.
{
ASSERT (filename);
return (file_mode (filename) > 0);
}
#include "sflfile.h"
char *
file_where (
char mode,
const char *path,
const char *name,
const char *ext)
Scans a user-specified path symbol for a specific file, and returns the fully-specified filename. Also adds an extension if this is required. The mode argument can be one of: r, w, a, or s for read, write, append, or static. The function tries to locate existing files somewhere on the path. New files are always created in the current directory. Static files are created in the first directory on the path. The path argument is only used when more is r, a, or s. If the path is NULL or empty, it is ignored. Otherwise, the path is translated as an environment variable, and cut into a list of directory names. The path is cut up as follows:
| MS-DOS | directory names separated by ';'. ;; means current. |
| OS/2 | directory names separated by ';'. ;; means current. |
| Unix | directory names separated by ':'. :: means current. |
| VMS | directory names separated by ','. " ", means current. |
| Other | single directory name. |
{
const char
*pathptr; /* End of directory in path */
char
*curdir;
Bool
search_curdir = TRUE; /* Look in current directory? */
ASSERT (name);
if (ext != NULL && *ext) /* Append extension if not null */
{ /* to get name + ext into */
if (ext [0] == '.') /* work_name. */
fixed extension (work_name, name, ext);
else
default extension (work_name, name, ext);
}
else
strcpy (work_name, name);
#if (NAMEFOLD == TRUE)
strupc (work_name); /* Fold to uppercase if needed */
#endif
if (path != NULL && *path) /* Get value of path, or NULL */
{
pathptr = getenv (path); /* Translate path symbol */
if (pathptr == NULL)
{
pathptr = path; /* If not found, use literally */
search_curdir = FALSE; /* Path now takes priority */
}
#if (PATHFOLD == TRUE) /* Fold to uppercase if necessary */
if (pathptr)
{
ASSERT (strlen (pathptr) < PATH_MAX);
strcpy (path_name, pathptr);
strupc (path_name);
pathptr = path_name; /* Redirect to uppercase version */
}
#endif
}
else
pathptr = NULL;
#if (defined (MSDOS_FILESYSTEM))
/* Normalise the path value by changing any slashes to backslashes */
if (pathptr)
{
if (pathptr != path_name)
{
strcpy (path_name, pathptr);
pathptr = path_name;
}
strconvch (path_name, '/', '\\');
}
#endif
/* Take care of 'w' and 's' options first */
if (mode == 'w') /* Create output file locally */
return (work_name);
if (mode == 's') /* Get specific directory name */
{
if (pathptr && file is directory (pathptr))
build_next_path (full_name, pathptr, work_name);
else
build_next_path (full_name, ".\\", work_name);
return (full_name);
}
/* If file exists as defined, prefix with current directory if not an */
/* absolute filename, then return the resulting filename */
if (search_curdir && file exists (work_name))
{
#if (defined (MSDOS_FILESYSTEM))
/* Under MSDOS we have a full path if we have any of:
* /directory/directory/filename
* D:/directory/directory/filename
* the variations of those with backslashes.
*/
if (work_name [0] == '\\' || work_name [0] == '/' ||
(isalpha (work_name [0]) && work_name [1] == ':' &&
(work_name [2] == '\\' || work_name [2] == '/')))
#else
/* Under UNIX, VMS, or OS/2, we have a full path if the path starts
* with the directory marker
*/
if (work_name [0] == PATHEND)
#endif
strcpy (full_name, work_name);
else
{
curdir = get curdir ();
sprintf (full_name, "%s%c%s", curdir, PATHEND, work_name);
mem_free (curdir);
}
#if (defined (MSDOS_FILESYSTEM))
strconvch (full_name, '/', '\\');
#endif
return (full_name); /* Then return path + name + ext */
}
if (!pathptr) /* Now we need a path */
return (NULL); /* - if none defined, give up */
for (;;) /* Try each path component */
{
pathptr = build_next_path (full_name, pathptr, work_name);
if (file exists (full_name))
return (full_name); /* Until we find one, */
if (*pathptr == '\0') /* or we come to the end of */
{ /* the path */
if (mode == 'r')
return (NULL); /* Input file was not found... */
else
return (full_name);
}
}
}
#include "sflfile.h"
Bool
file_cycle (
const char *filename,
int how)
Cycles the file: if the file already exists, renames the existing file. This function tries to rename the file using the date of creation of the file; if this fails because an existing file had the same name, generates a guaranteed unique file name. Returns TRUE if the cycle operation succeeded, or FALSE if it failed (e.g. due to a protection problem). The how argument must be one of:
| CYCLE ALWAYS | Cycle file unconditionally |
| CYCLE HOURLY | Cycle file if hour has changed |
| CYCLE DAILY | Cycle file if day has changed |
| CYCLE WEEKLY | Cycle file if week has changed |
| CYCLE MONTHLY | Cycle file if month has changed |
| CYCLE NEVER | Don't cycle the file |
{
long
file_date; /* Datestamp of file */
char
*point,
*insert_at; /* Where we start messing name */
int
unique_nbr; /* To generate a unique name */
ASSERT (filename);
/* If no cycling needed, do nothing */
if (!file cycle needed (filename, how))
return (TRUE); /* No errors, nothing in fact */
file_date = timer to date (get file time (filename));
strcpy (full_name, filename);
point = strrchr (full_name, '.');
if (point)
{
strcpy (work_name, point); /* Save extension, if any */
*point = '\0'; /* and truncate original name */
}
else
strclr (work_name);
/* We leave up to 2 original letters of the filename, then stick-in */
/* the 6-digit timestamp. */
insert_at = strrchr (full_name, '/');
#if (defined (MSDOS_FILESYSTEM))
if (!insert_at)
insert_at = strrchr (full_name, '\\');
#endif
if (insert_at)
insert_at++; /* Bump past slash, if found */
else
insert_at = full_name;
if (*insert_at) /* Bump insert_at twice, to leave */
insert_at++; /* up to 2 letters before we */
if (*insert_at) /* stick-in the date stamp */
insert_at++;
/* Format new name for file and make sure it does not already exist */
sprintf (insert_at, "%06d", (int) (file_date % 1000000L));
strcat (insert_at, work_name);
if (file exists (full_name))
{
point = strrchr (full_name, '.') + 1;
for (unique_nbr = 0; unique_nbr < 1000; unique_nbr++)
{
sprintf (point, "%03d", unique_nbr);
if (!file exists (full_name))
break;
}
}
if (file exists (full_name))
return (FALSE); /* We give up! */
if (file rename (filename, full_name))
return (FALSE); /* No permission */
else
return (TRUE); /* Okay, it worked */
}
#include "sflfile.h"
Bool
file_cycle_needed (
const char *filename,
int how)
Checks whether the file should be cycled or not. Returns TRUE if the file needs to be cycled, FALSE if not. The how argument must be one of:
| CYCLE ALWAYS | Cycle file unconditionally |
| CYCLE HOURLY | Cycle file if hour has changed |
| CYCLE DAILY | Cycle file if day has changed |
| CYCLE WEEKLY | Cycle file if week has changed |
| CYCLE MONTHLY | Cycle file if month has changed |
| CYCLE NEVER | Don't cycle the file |
{
long
curr_time, /* Current time */
curr_date, /* Current date */
file_date, /* Timestamp of file */
file_time; /* Datestamp of file */
Bool
cycle; /* Do we want to cycle the file? */
ASSERT (filename);
if (!file exists (filename)) /* Not found - nothing more to do */
return (FALSE);
file_time = timer to time (get file time (filename));
file_date = timer to date (get file time (filename));
curr_time = time now ();
curr_date = date now ();
switch (how)
{
case CYCLE_ALWAYS:
cycle = TRUE;
break;
case CYCLE_HOURLY:
cycle = GET_HOUR (file_time) != GET_HOUR (curr_time);
break;
case CYCLE_DAILY:
cycle = GET_DAY (file_date) != GET_DAY (curr_date);
break;
case CYCLE_WEEKLY:
cycle = week of year (file_date) != week of year (curr_date);
break;
case CYCLE_MONTHLY:
cycle = GET_MONTH (file_date) != GET_MONTH (curr_date);
break;
case CYCLE_NEVER:
cycle = FALSE;
break;
default:
cycle = FALSE;
}
return (cycle);
}
#include "sflfile.h"
Bool
file_has_changed (
const char *filename,
long old_date,
long old_time)
Returns TRUE if the file has changed since it was last read. The calling program must supply the date and time of the file as it was read. If the file is not present or accessible, returns FALSE.
{
long
file_date, /* Timestamp of file */
file_time; /* Datestamp of file */
ASSERT (filename);
if (!file exists (filename)) /* Not found - nothing more to do */
return (FALSE);
file_time = timer to time (get file time (filename));
file_date = timer to date (get file time (filename));
if (file_date > old_date
|| (file_date == old_date && file_time > old_time))
return (TRUE);
else
return (FALSE);
}
#include "sflfile.h"
Bool
safe_to_extend (
const char *filename)
Handles system-specific case of extending a file that may not be in a valid state for such an operation. Returns TRUE if the extend can go ahead; returns FALSE if the extend cannot be permitted. Under MS-DOS and Windows, if the last byte in the file is Ctrl-Z (26) the file is truncated by 1 position to remove this character.
{
#if (defined (MSDOS_FILESYSTEM))
int handle; /* Opened file handle */
char endoffile; /* Last character in file */
ASSERT (filename);
handle = open (filename, O_RDWR + O_BINARY, S_IREAD | S_IWRITE);
if (handle) /* If not found, ignore */
{
lseek (handle, -1, SEEK_END);
read (handle, &endoffile, 1);
if (endoffile == 26)
chsize (handle, filelength (handle) - 1);
close (handle);
}
#endif
return (TRUE);
}
#include "sflfile.h"
char *
default_extension (
char *dest,
const char *src,
const char *ext)
Copies src to dest and adds ext if necessary. Returns dest. Dest must be large enough for a fully-formatted filename; define it as char [FILE_NAME_MAX + 1]. The ext argument can start with or without If ext is null or empty, does nothing.
{
int len, i;
char *ptr;
ASSERT (dest);
ASSERT (src);
if (dest != src) /* Copy src to dest if not same */
strcpy (dest, src);
if (ext != NULL && *ext != 0)
{
len = strlen (dest);
for (i = len - 1, ptr = dest + i; i >= 0; i--, ptr--)
if (*ptr == '\\' || *ptr == '/' || *ptr == '.')
break;
if (i < 0 || *ptr != '.')
{
if (*ext != '.')
{
dest [len++] = '.';
dest [len] = '\0';
}
strcat (dest + len, ext);
}
}
return (dest);
}
#include "sflfile.h"
char *
fixed_extension (
char *dest,
const char *src,
const char *ext)
Copies src to dest and enforces ext extension. Returns dest. Dest must be large enough for a fully-formatted filename; define it as char [FILE_NAME_MAX + 1]. The ext argument can start with or without If ext is null or empty, does nothing.
{
ASSERT (dest);
ASSERT (src);
if (dest != src) /* Copy src to dest if not same */
strcpy (dest, src);
strip extension (dest);
return (default extension (dest, dest, ext));
}
#include "sflfile.h"
char *
strip_extension (
char *name)
Removes dot and extension from the name, if any was present. If the name contained multiple extensions, removes the last one only. Returns name.
{
char *dot, *slash;
ASSERT (name);
dot = strrchr (name, '.'); /* Find dot in name, if any */
slash = strrchr (name, '\\'); /* Find last slash (DOS or Unix) */
if (slash == NULL)
slash = strrchr (name, '/');
if (dot > slash)
*dot = 0; /* If we had a dot, truncate name */
return (name);
}
#include "sflfile.h"
char
*strip_file_path (
char *name)
Removes the leading path from the filename, if any path was present. Returns name. The path can be specified using the local operating system syntax; under MS-DOS, / and \ are interchangeable.
{
char *path_end;
ASSERT (name);
path_end = strrchr (name, PATHEND); /* Find end of path, if any */
#if (defined (MSDOS_FILESYSTEM))
if (path_end == NULL)
path_end = strrchr (name, '/');
#endif
if (path_end != NULL)
memmove (name, path_end + 1, strlen (path_end));
return (name);
}
#include "sflfile.h"
char
*strip_file_name (
char *name)
Returns the path for a fully-qualified filename. The path is cleaned-up and resolved. The returned string is held in a static area that should be copied directly after calling this function. The returned path does not end in '/' unless that is the entire path. If the supplied name contains no path, the returned path is ".".
{
char *path_end;
ASSERT (name);
ASSERT (strlen (name) <= LINE_MAX);
strcpy (work_name, name);
path_end = strrchr (work_name, PATHEND);
#if (defined (MSDOS_FILESYSTEM))
if (path_end == NULL)
path_end = strrchr (work_name, '/');
#endif
if (path_end == NULL)
return (".");
else
{
path_end [1] = '\0';
return (clean path (work_name));
}
}
#include "sflfile.h"
Bool
file_is_readable (
const char *filename)
Returns TRUE if the current process can read the specified file or directory. The filename may end in a slash (/ or \).
{
ASSERT (filename);
return ((file_mode (clean path (filename)) & S_IREAD) != 0);
}
#include "sflfile.h"
Bool
file_is_writeable (
const char *filename)
Returns TRUE if the current process can write the specified file or directory. The filename may end in a slash (/ or \).
{
ASSERT (filename);
return ((file_mode (clean path (filename)) & S_IWRITE) != 0);
}
#include "sflfile.h"
Bool
file_is_executable (
const char *filename)
Returns TRUE if the current process can execute the specified file. Directories are _not_ considered to be executable. Under DOS, Windows, appends ".com", ".exe", and ".bat" to the filename, in that order, to build a possible executable filename. If this fails, opens the file (if it exists) and examines the first few bytes of the file: if these are "#!", or '/'*! or "MZ" then the file is assumed to be executable. #! is a standard mechanism under Unix for indicating executable files. Note that process create() uses a compatible mechanism to launch the correct interpreter for such 'executable' scripts. NOTE: '/'*! is provided for REXX. [XXX] Under OS/2 appends ".exe" and ".cmd" to the filename, in that order, to be a possible executable filename. If this fails, it opens the file (if it exists) and examines the first few bytes of the file: if these are "#!" then the file is assumed to be executable. NOTE: REXX scripts MUST be in files named script.cmd in order to be found. BAT files are not considered, nor are COM files, since at present process_create does not support launching DOS processes. Under VMS, appends .exe and .com, in that order to build a possible executable filename. Does not search the PATH symbol; the filename must be specified with a path if necessary.
{
#if (defined (__UNIX__))
ASSERT (filename);
return ((file_mode (filename) & S_IEXEC) != 0
&& (file_mode (filename) & S_IFDIR) == 0);
#elif (defined (MSDOS_FILESYSTEM))
Bool
executable; /* Return code */
FILE
*stream; /* Opened file stream */
char
input_char = 0, /* First and second bytes of file */
*extension; /* File extension, if any */
ASSERT (filename);
/* Find file extension; if not found, set extension to empty string */
extension = strrchr (filename, '.');
if (extension == NULL
|| strchr (extension, '/') /* If last '.' is part of the path */
|| strchr (extension, '\\')) /* then the filename has no ext. */
extension = "";
/* Windows: If extension is .exe/.com/.bat, the file is an executable */
/* OS/2: If the extension is .exe/.cmd, the file is an executable */
#if (defined ( __OS2__))
if (lexcmp (extension, ".exe") == 0
|| lexcmp (extension, ".cmd") == 0)
#else /* DOS, WINDOWS */
if (lexcmp (extension, ".com") == 0
|| lexcmp (extension, ".exe") == 0
|| lexcmp (extension, ".bat") == 0)
#endif
executable = file exists (filename);
else
/* Windows: If the extension is empty, try .com, .exe, .bat */
/* OS/2: If the extension is empty, try .exe, .cmd */
if (strnull (extension)
#if (defined( __OS2__))
&& (file exists (default extension (work_name, filename, "exe"))
|| file exists (default extension (work_name, filename, "cmd"))))
#else /* DOS, WINDOWS */
&& (file exists (default extension (work_name, filename, "com"))
|| file exists (default extension (work_name, filename, "exe"))
|| file exists (default extension (work_name, filename, "bat"))))
#endif
executable = TRUE; /* Executable file found */
else
{
/* Look for magic header at start of file */
stream = file open (filename, 'r');
if (stream)
{
input_char = fgetc (stream);
executable = ((input_char == '#' && fgetc (stream) == '!')
# if (defined (__WINDOWS__))
|| (input_char == '/' && fgetc (stream) == '*'
&& fgetc (stream) == '!')
|| (input_char == 'M' && fgetc (stream) == 'Z')
# endif
);
file close (stream);
}
else
executable = FALSE;
}
return (executable);
#elif (defined (__VMS__))
Bool
executable; /* Return code */
char
*extension; /* File extension, if any */
ASSERT (filename);
/* Find file extension, if any */
extension = strrchr (filename, '.');
if ((file_mode (filename) & S_IEXEC) != 0)
executable = TRUE;
else
/* If the extension is empty, try .exe and .com */
if (!extension)
{
default extension (work_name, filename, "exe");
if ((file_mode (work_name) & S_IEXEC) != 0)
executable = TRUE;
else
{
default extension (work_name, filename, "com");
if ((file_mode (work_name) & S_IEXEC) != 0)
executable = TRUE;
else
executable = FALSE;
}
}
else
executable = FALSE;
return (executable);
#else
return (FALSE); /* Not supported on this system */
#endif
}
#include "sflfile.h"
Bool
file_is_program (
const char *filename)
Returns TRUE if the specified filename is an executable program on the PATH. Under DOS, and Windows, appends ".com", ".exe", and ".bat" to the file, in that order, to build an executable filename, then searches the PATH definition for the executable filename. Under OS/2, appends ".exe", and ".cmd" to the file, in that order, to build an executable filename, then searches the PATH definition for the executable filename. If the filename already has a path specifier, will not use the PATH definition. Under VMS, appends "exe" and "com" to the file, in that order, to build an executable filename. Searches the PATH if necessary.
{
Bool
executable = FALSE; /* Return code */
#if (defined (__UNIX__))
char
*found_file;
ASSERT (filename);
found_file = file where ('r', "PATH", filename, "");
if (found_file && (file_mode (found_file) & S_IEXEC))
executable = TRUE; /* Executable file found */
#elif (defined (__VMS__))
char
*found_file;
ASSERT (filename);
found_file = file where ('r', "PATH", filename, "");
if (!found_file)
found_file = file where ('r', "PATH", filename, ".exe");
if (!found_file)
found_file = file where ('r', "PATH", filename, ".com");
if (found_file && (file_mode (found_file) & S_IEXEC))
executable = TRUE; /* Executable file found */
#elif (defined (MSDOS_FILESYSTEM))
char
*path; /* What path do we search? */
ASSERT (filename);
/* If the filename already contains a path, don't look at PATH */
if (strchr (filename, '/') || strchr (filename, '\\'))
path = NULL;
else
path = "PATH";
# if (defined (__WINDOWS__))
if (file where ('r', path, filename, ".com")
|| file where ('r', path, filename, ".exe")
|| file where ('r', path, filename, ".bat"))
executable = TRUE; /* Executable file found */
# else /* OS/2 */
if (file where ('r', path, filename, ".exe")
|| file where ('r', path, filename, ".cmd"))
executable = TRUE;
# endif
#endif
return (executable);
}
#include "sflfile.h"
Bool
file_is_directory (
const char *filename)
Returns TRUE if the specified file is a directory. The filename may end in a slash (/ or \). Under MS-DOS/OS2/Windows, a directory name may consist solely of a disk-drive specifier. Under VMS the directory may optionally take the extension '.dir'.
{
static char
dir_name [LINE_MAX + 1];
ASSERT (filename);
strcpy (dir_name, clean path (filename));
#if (defined (__VMS__))
if (!file exists (dir_name))
default extension (dir_name, dir_name, "dir");
#endif
return ((file_mode (dir_name) & S_IFDIR) != 0);
}
#include "sflfile.h"
Bool
file_is_legal (
const char *arg_filename)
Checks whether the specified file is 'legal', which is a system-dependent definition. Under 32-bit Windows, a legal file is one who's name is not a shortened 8.3 version of a long name. This can be used to bypass filename-based security schemes. On other systems, the notion of 'illegal' is not defined. Returns TRUE if the file exists and is legal. Returns FALSE otherwise.
{
#if (defined (WIN32))
static WIN32_FIND_DATA
found;
HANDLE
handle;
char
*filename, /* Our copy of arg_filename */
*slash, /* Position of '\' in filename */
*component; /* Component to compare */
Bool
feedback; /* Function feedback */
/* For each path component of the filename, check that the long form
* of the name is the same as the short form. We scan backwards
* from the end of the filename, get the full pathname, and compare
* the last component each time:
*
* aaa\bbb\ccc\name.ext name.ext
* aaa\bbb\ccc ccc
* aaa\bbb bbb
* aaa aaa
*/
filename = mem_strdup (arg_filename);
feedback = TRUE; /* Assume we match everything */
strconvch (filename, '/', '\\');
if (strlast (filename) == '\\')
strlast (filename) = '\0'; /* Drop any trailing slash */
do
{
slash = strrchr (filename, '\\');
component = slash? slash + 1: filename;
handle = FindFirstFile (filename, &found);
if (handle == INVALID_HANDLE_VALUE
|| lexcmp (component, found.cFileName))
{
feedback = FALSE;
break;
}
FindClose (handle);
if (slash)
{
*slash = '\0'; /* Cut filename at slash */
if (filename [1] == ':'
&& filename [2] == '\0') /* We're at a disk specifier */
break; /* which is okay by now */
}
}
while (slash && *filename);
mem_free (filename);
return (feedback);
#else
return (file exists (arg_filename));
#endif
}
#include "sflfile.h"
char *
file_exec_name (
const char *filename)
If the specified filename is an executable program, formats a filename including any required extension and returns a static string with that value. If the specified filename is not an executable program, returns NULL. Under DOS, and Windows, appends ".com", ".exe", and ".bat" to the filename, in that order, to build a possible executable filename. Under OS/2, appends ".exe", and ".cmd" to the filename, in that order, to build a possible executable filename. If this fails, returns NULL. Does not search the PATH symbol; the filename must be specified with a path if necessary. The returned filename (if not NULL) points to a static string.
{
#if (defined (__UNIX__) || defined (__VMS__))
ASSERT (filename);
strcpy (exec_name, filename);
if (file_mode (exec_name) & S_IEXEC)
return (exec_name);
else
return (NULL);
#elif (defined (MSDOS_FILESYSTEM))
char
*extension; /* File extension, if any */
ASSERT (filename);
/* Find file extension; if not found, set extension to empty string */
extension = strrchr (filename, '.');
if (extension == NULL
|| strchr (extension, '/') /* If last '.' is part of the path */
|| strchr (extension, '\\')) /* then the filename has no ext. */
extension = "";
/* Windows: If extension is .exe/.com/.bat, the file is an executable */
/* OS/2: If extension is .exe/.cmd, the file is executable */
# if (defined (__OS2__))
if (lexcmp (extension, ".exe") == 0
|| lexcmp (extension, ".cmd") == 0
# else /* DOS, WINDOWS */
if (lexcmp (extension, ".com") == 0
|| lexcmp (extension, ".exe") == 0
|| lexcmp (extension, ".bat") == 0
# if (defined (__WINDOWS__))
|| is_exe_file (filename)
# endif
# endif
)
{
strcpy (exec_name, filename);
return (exec_name);
}
else
/* Windows: If the extension is empty, try .com, .exe, .bat */
/* OS/2: If the extension is empty, try .exe, .cmd */
if (strnull (extension)
# if (defined (__OS2__))
&& (file exists (default extension (exec_name, filename, "exe"))
|| file exists (default extension (exec_name, filename, "cmd"))))
# else /* DOS, WINDOWS */
&& (file exists (default extension (exec_name, filename, "com"))
|| file exists (default extension (exec_name, filename, "exe"))
|| file exists (default extension (exec_name, filename, "bat"))))
# endif
return (exec_name); /* Executable file found */
else
return (NULL);
#else
return (NULL); /* Not supported on this system */
#endif
}
#include "sflfile.h"
long
get_file_size (
const char *filename)
Returns the size, in bytes, of the specified file or directory. The size of a directory is not a portable concept. If there is an error, returns -1.
{
#if (defined (WIN32_NOT_IMPLEMENTED))
/* This code has been disactivated because it cannot work with a file
that is opened by another function.
*/
HANDLE
handle; /* We open file to get its size */
long
size;
ASSERT (filename);
handle = CreateFile (filename, 0, FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, 0);
if (handle == INVALID_HANDLE_VALUE)
return (-1);
size = GetFileSize (handle, NULL);
CloseHandle (handle);
return (size);
#else
struct stat
stat_buf;
ASSERT (filename);
if (stat ((char *) filename, &stat_buf) == 0)
return ((long) stat_buf.st_size);
else
return (-1);
#endif
}
#include "sflfile.h"
time_t
get_file_time (
const char *filename)
Returns the modification time of the specified file or directory. The returned time is suitable for feeding to localtime().
{
#if (defined (WIN32_NOT_IMPLEMENTED))
/* This code has been disactivated because it returns incorrect
values depending on the seasonal clock change.
*/
unsigned long thi,tlo;
double dthi,dtlo;
double secs_since_1601, secs_time_t;
double delta = 11644473600.;
double two_to_32 = 4294967296.;
HANDLE handle;
FILETIME creation, last_access, last_write;
handle = CreateFile (filename, GENERIC_READ, FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, 0);
if (handle == INVALID_HANDLE_VALUE)
return (0);
GetFileTime (handle, &creation, &last_access, &last_write);
CloseHandle (handle);
thi = last_write.dwHighDateTime;
tlo = last_write.dwLowDateTime;
dthi = (double) thi;
dtlo = (double) tlo;
secs_since_1601 = (dthi * two_to_32 + dtlo) / 1.0e7;
secs_time_t = secs_since_1601 - delta;
return ((time_t) secs_time_t);
#else
struct stat
stat_buf;
ASSERT (filename);
if (stat ((char *) filename, &stat_buf) == 0)
return (stat_buf.st_mtime > 0? stat_buf.st_mtime: 0);
else
return (0);
#endif
}
#include "sflfile.h"
long
get_file_lines (
const char *filename)
Reads an entire file, and returns the number of lines in the file. The file should be normal text. Returns 0 if the file cannot be opened for reading. May be a bit slow on large files.
{
long
file_size;
FILE
*file_stream;
int
ch;
ASSERT (filename);
file_stream = file open (filename, 'r');
if (file_stream == NULL)
return (0);
file_size = 0;
while ((ch = fgetc (file_stream)) != EOF)
if (ch == '\n')
file_size++;
fclose (file_stream);
return (file_size);
}
#include "sflfile.h"
DESCR *
file_slurp (
const char *filename)
Reads an entire file, and returns a DESCR containing the file data. The file is read as binary data. The returned DESCR should be freed using the mem_free() call. if the file is > 64K long, only the first 64K bytes are read into memory. This is to stop really silly things from happening. Returns NULL if the file cannot be found. Appends a null byte to the data in any case.
{
return (file_load_data (filename, 65535UL));
}
#include "sflfile.h"
DESCR *
file_slurpl (
const char *filename)
Reads an entire file, and returns a DESCR containing the file data. The file is read as binary data. The returned DESCR should be freed using the mem_free() call. Does not impose any limit on the size of the file (unlike file slurp() which stops at 64K bytes). Returns NULL if the file cannot be found. Appends a null byte to the data in any case.
{
return (file_load_data (filename, 0));
}
#include "sflfile.h" dbyte file_set_eoln (char *dst, const char *src, dbyte src_size, Bool add_cr)
Formats any end-of-line sequences in the buffer according to the value of the add_cr argument. If this is TRUE, all end-of- lines (LF or CRLF or LFCR) are represented by a CRLF sequence. If FALSE, all end-of-lines are represented by LF by itself. The target buffer must be large enough to accomodate the resulting line (twice the size of the source data). Returns the size of the resulting data in the target buffer not counting the final trailing null byte. The input data does not need to be null- terminated, but the output data is terminated with an extra null in any case.
{
char
*srcptr, /* Current character in src */
*dstptr, /* Current character in dst */
*last; /* Last character in src */
ASSERT (src);
ASSERT (dst);
srcptr = (char *) src;
dstptr = dst;
last = (char *) src + src_size;
while (*srcptr && srcptr < last)
{
if (*srcptr == '\n'
|| *srcptr == EOF)
{
if (add_cr)
*dstptr++ = '\r';
*dstptr++ = '\n';
}
else
if (*srcptr != '\r' && *srcptr != 26)
*dstptr++ = *srcptr;
srcptr++;
}
*dstptr = '\0';
return ((dbyte) (dstptr - dst));
}
#include "sflfile.h" char * get_tmp_file_name (const char *path, qbyte *index, const char *ext)
Get a temporary file name.
{
static char
file_name [LINE_MAX + 1];
do
{
if (path)
sprintf (file_name, "%s%c%08lX.%s", path, PATHEND, *index, ext);
else
sprintf (file_name, "%08lX.%s", *index++, ext);
(*index)++;
} while (file exists (file_name));
return (file_name);
}
Filename: sflini.h
Package: Standard Function Library (SFL)
Written: 94/01/08 iMatix SFL project team sfl@imatix.com
Revised: 97/09/08
Copyright: Copyright (c) 1991-98 iMatix
Provides functions to read an initialisation file that follows the MS-Windows style, i.e. consists of [Sections] followed by keyword = value lines.
sflini.h defines these symbols, possibly conditionally:
| Symbol: | Defined as: |
|---|---|
| _SLFINI_INCLUDED | TRUE |
#include "sflini.h"
Bool
ini_find_section (
FILE *inifile,
char *section,
Bool top)
Finds a specific section in the ini file. An ini file contains lines as shown below. The section name can be any mix of upper or lowercase. You should open the ini file using file_open before you call this function. If the 'top' argument is TRUE, repositions to the start of the file before reading, else reads from the current file offset. Returns TRUE if the section was found, and positions on the line that follows the section. Returns FALSE if the section was not found, and positions at the end of the file.
; comments like this, or
# comments like this if you prefer
! Text is echoed to console using trace()
[Section]
keyword = key_value; comments
keyword = "key_value"; comments
keyword = 'key_value'; comments
...
[Section]
keyword = key_value; comments
...
{
char
*first;
ASSERT (inifile != NULL);
ASSERT (section != NULL);
if (top) /* Reposition at top if wanted */
fseek (inifile, 0, SEEK_SET);
/* Read through file until we find what we are looking for */
while (file read (inifile, iniline))
{
first = strskp (iniline); /* Skip leading spaces */
if (*first == ';' || *first == '#' || *first == 0)
continue; /* Comment line */
else
if (*first == '!')
{
first = strskp (first + 1);
trace (first);
}
else
if (sscanf (first, "[%[^]]", ini_section) == 1
&& lexcmp (ini_section, section) == 0)
return (TRUE);
}
return (FALSE);
}
#include "sflini.h"
Bool
ini_scan_section (
FILE *inifile,
char **keyword,
char **value)
Scans the current section of the ini file, and returns a keyword and value if such was found. Returns the address of these values in the supplied arguments. The addresses point to static values that are overwritten with each call. Returns TRUE when a keyword/value pair is found. Returns FALSE if a new section name or end of file is found. In the first case, sets the keyword to the section name; in the second case sets the keyword to NULL. Ignores blank and comment lines, and lines that look like junk. Keyword and section names are returned as lower-case; values are returned exactly as specified in the ini file.
{
char
*first;
/* Read through file until we find what we are looking for */
while (file read (inifile, iniline))
{
first = strskp (iniline); /* Skip leading spaces */
if (*first == ';' || *first == '#' || *first == 0)
continue; /* Comment line */
else
if (*first == '!')
{
first = strskp (first + 1);
trace (first);
}
else
if (sscanf (first, "[%[^]]", ini_section) == 1)
{
*keyword = strlwc (ini_section);
*value = NULL;
return (FALSE); /* New section name */
}
else
if (streq (first, "[]")) /* Allow empty section names */
{
strcpy (ini_section, "");
*keyword = ini_section;
*value = NULL;
return (FALSE); /* New section name */
}
else
if (sscanf (first, "%[^=] = \"%[^\"]\"", ini_keyword, ini_value) == 2
|| sscanf (first, "%[^=] = '%[^\']'", ini_keyword, ini_value) == 2
|| sscanf (first, "%[^=] = %[^;#]", ini_keyword, ini_value) == 2)
{
strcrop (strlwc (ini_keyword));
strcrop (ini_value);
/* sscanf can't handle "" or '' as an empty value, so we do this
* ourselves. Note that this breaks '""' and "''". :-(
*/
if (streq (ini_value, "\"\"")
|| streq (ini_value, "''"))
strclr (ini_value);
*keyword = ini_keyword;
*value = ini_value;
return (TRUE); /* Found keyword = value */
}
}
*keyword = NULL;
return (FALSE); /* End of file */
}
#include "sflini.h"
SYMTAB *
ini_dyn_load (
SYMTAB *load_symtab,
const char *filename)
Loads the contents of an .ini file into a symbol table. If no symbol table is specified, creates a new symbol table. The ini file data is loaded as a set of symbols and values, where the symbol name is built from the section name and keyword like this: "section:keyword". The symbol name is always stored in lowercase, with no trailing spaces. If the same keyword occurs several times in a section, earlier symbols are overwritten. Ignores all comments and blank lines. Returns NULL if there is not enough memory. Stores these control variables in the symbol table if the table was freshly created or the file was loaded:
| filename | Name of input file |
| filetime | Time of input file, as 8-digit string "HHMMSSCC" |
| filedate | Date of input file, as 8-digit string "YYYYMMDD" |
{
FILE
*inifile;
SYMTAB
*symtab, /* Symbol table to populate */
*envtab; /* Environment, as symbol table */
char
*section = NULL, /* Filled as we scan through */
*keyword = NULL, /* the ini file */
*value = NULL,
*section_end; /* Null byte at end of section */
ASSERT (filename);
inifile = file locate ("PATH", filename, NULL);
if (load_symtab) /* Use specified symbol table */
symtab = load_symtab; /* or create a new one */
else
{
symtab = sym create table ();
if (symtab == NULL)
return (NULL); /* Quit if insufficient memory */
}
/* Store control variables in symbol table */
if (inifile || load_symtab == NULL)
{
sym assume symbol (symtab, "filename", filename);
sprintf (iniline, "%ld", timer to date (get file time (filename)));
sym assume symbol (symtab, "filedate", iniline);
sprintf (iniline, "%ld", timer to time (get file time (filename)));
sym assume symbol (symtab, "filetime", iniline);
}
if (!inifile)
return (symtab); /* File not found; empty table */
/* Now load the ini file, starting from the beginning */
envtab = env2symb ();
fseek (inifile, 0, SEEK_SET);
FOREVER
{
if (ini scan section (inifile, &keyword, &value))
{
if (section)
{
section_end = strchr (section, '\0');
ASSERT (section_end);
xstrcat (section, ":", keyword, NULL);
value = tok subst (value, envtab);
sym assume symbol (symtab, section, value);
mem_strfree (&value);
*section_end = '\0';
}
}
else
if (keyword) /* Found new section */
{
section = keyword;
sym assume symbol (symtab, section, "");
}
else
break;
}
file close (inifile);
sym delete table (envtab);
sym sort table (symtab, NULL); /* Sort table by symbol name */
return (symtab);
}
#include "sflini.h"
int
ini_dyn_save (
SYMTAB *symtab,
const char *filename)
Saves a symbol table to the specified file. The symbol table entries must be formatted as "section:name=value" - see ini dyn load(). Scans the ini file for a line containing only "#*END", then writes the symbol data to the file from that point. Returns the number of symbols saved, or -1 if there was an error. As a side-effect, sorts the table on the symbol name.
{
FILE
*inifile,
*wrkfile;
SYMBOL
*symbol; /* Next symbol in table */
Bool
header_found; /* Did we find a file header? */
int
count; /* How many symbols did we save? */
char
*colon; /* Points to ':' in symbol name */
ASSERT (filename);
ASSERT (symtab);
/* Copy ini file header to temporary file */
wrkfile = tmpfile ();
header_found = FALSE;
if ((inifile = file open (filename, 'r')) != NULL)
{
while (file read (inifile, iniline))
{
if (streq (iniline, "#*END"))
{
header_found = TRUE;
break;
}
file write (wrkfile, iniline);
}
file close (inifile);
}
/* Now rewrite ini file */
if ((inifile = file open (filename, 'w')) == NULL)
{
fclose (wrkfile);
return (-1); /* No permission to write file */
}
if (header_found)
{
fseek (wrkfile, 0, SEEK_SET);
while (file read (wrkfile, iniline))
file write (inifile, iniline);
}
file close (wrkfile); /* Finished with temporary file */
/* Output ini file values */
file write (inifile, "#*END");
strclr (ini_section); /* Current section */
count = 0;
sym sort table (symtab, NULL); /* Sort table by symbol name */
for (symbol = symtab-> symbols; symbol; symbol = symbol-> next)
{
/* Output symbols formatted as key:name */
colon = strrchr (symbol-> name, ':');
if (colon)
{
memcpy (ini_value, symbol-> name, colon - symbol-> name);
ini_value [colon - symbol-> name] = '\0';
strcpy (ini_keyword, colon + 1);
/* If we start a new section, output the section header */
*ini_value = toupper (*ini_value);
*ini_keyword = toupper (*ini_keyword);
if (strneq (ini_section, ini_value))
{
strcpy (ini_section, ini_value);
sprintf (iniline, "[%s]", ini_section);
file write (inifile, "");
file write (inifile, iniline);
}
if (strnull (symbol-> value))
sprintf (iniline, " %s=\"\"", ini_keyword);
else
if (strpbrk (symbol-> value, ";#="))
sprintf (iniline, " %s=\"%s\"", ini_keyword, symbol-> value);
else
sprintf (iniline, " %s=%s", ini_keyword, symbol-> value);
file write (inifile, iniline);
}
}
file close (inifile);
return (count);
}
#include "sflini.h"
Bool
ini_dyn_changed (
SYMTAB *symtab)
Returns TRUE if the ini file loaded into the specified table has in the meantime been changed. Returns FALSE if not.
{
char
*filename;
ASSERT (symtab);
/* Date, time, and name of original ini file are in the table */
filename = sym get value (symtab, "filename", NULL);
if (filename
&& file has changed (filename,
sym get number (symtab, "filedate", 0),
sym get number (symtab, "filetime", 0)))
return (TRUE);
else
return (FALSE);
}
#include "sflini.h"
Bool
ini_dyn_refresh (
SYMTAB *symtab)
Refreshes a symbol table created by ini dyn load(). If the original file (as specified by the 'filename' symbol) has been modified, reloads the whole ini file. You would typically call this function at regular intervals to permit automatic reloading of an ini file in an application. Returns TRUE if the ini file was actually reloaded, or FALSE if the file had not changed or could not be accessed, or if the symbol table was incorrectly created. If the symbol table is reloaded from the ini file, all previous symbols are deleted.
{
char
*filename;
ASSERT (symtab);
if (ini dyn changed (symtab))
{
filename = mem_strdup (sym get value (symtab, "filename", NULL));
sym empty table (symtab); /* Delete previous table contents */
ini dyn load (symtab, filename);
mem_free (filename);
return (TRUE);
}
return (FALSE);
}
#include "sflini.h"
char *
ini_dyn_value (
SYMTAB *symtab,
const char *section,
const char *keyword,
const char *default_value)
Finds a section:keyword in the symbol table and returns a pointer to its value. Returns the default value if the symbol is not defined in the table. The default value may be NULL. The specified section and keyword can be in any case; they are converted internally to lowercase to match the symbol table. If the keyword is empty or NULL, no ':keyword' is appended to the section name.
{
ASSERT (section);
if (keyword && *keyword)
sprintf (ini_keyword, "%s:%s", section, keyword);
else
strcpy (ini_keyword, section);
strlwc (ini_keyword);
return (sym get value (symtab, ini_keyword, default_value));
}
#include "sflini.h"
char **
ini_dyn_values (
SYMTAB *symtab,
const char *section,
const char *keyword,
const char *default_value)
Finds a section:keyword in the symbol table and returns a pointer to a string table containing the values, delimited by commas. When finished with the string table you should call tok free() to free the memory allocated for it. The default value may not be NULL. Returns a pointer to a table of string tokens (see tok split()), or NULL if there was insufficient memory. The specified section and keyword can be in any case; they are converted internally to lowercase to match the symbol table. If the keyword is empty or NULL, no ':keyword' is appended to the section name.
{
ASSERT (section);
ASSERT (default_value);
if (keyword && *keyword)
sprintf (ini_keyword, "%s:%s", section, keyword);
else
strcpy (ini_keyword, section);
strlwc (ini_keyword);
strcpy (iniline, sym get value (symtab, ini_keyword, default_value));
strconvch (iniline, ',', ' ');
return (tok split (iniline));
}
Filename: sfllang.h
Package: Standard Function Library (SFL)
Written: 97/06/04 iMatix SFL project team sfl@imatix.com
Revised: 98/05/31
Copyright: Copyright (c) 1991-98 iMatix
Provides hard-coded multilanguage dictionaries for dates and numbers, The hard-coded dictionaries work with most European languages.
sfllang.h defines these symbols, possibly conditionally:
| Symbol: | Defined as: |
|---|---|
| USERLANG_TOP | USERLANG_SV + 1 |
| _SFLLANG_INCLUDED | TRUE |
#include "sfllang.h" int set_userlang (int language)
Sets language used for date and numeric translation. The valid user languages are:
| USERLANG DEFAULT | Default language (use hard-coded values) |
| USERLANG DA | Danish |
| USERLANG DE | German |
| USERLANG EN | English |
| USERLANG ES | Castillian Spanish |
| USERLANG FB | Belgian or Swiss French |
| USERLANG FR | French |
| USERLANG IS | Icelandic |
| USERLANG IT | Italian |
| USERLANG NL | Dutch |
| USERLANG NO | Norwegian |
| USERLANG PO | Portuguese |
| USERLANG SV | Swedish |
{
/* Order of this table is not critical */
static struct {
int language;
char **units;
char **tens;
char **days;
char **months;
} languages [] =
{
{ USERLANG_DEFAULT, EN_units, EN_tens, EN_days, EN_months },
{ USERLANG_DA, DA_units, DA_tens, EN_days, DA_months },
{ USERLANG_DE, DE_units, DE_tens, EN_days, DE_months },
{ USERLANG_EN, EN_units, EN_tens, EN_days, EN_months },
{ USERLANG_ES, ES_units, ES_tens, EN_days, ES_months },
{ USERLANG_FB, FR_units, FB_tens, FR_days, FR_months },
{ USERLANG_FR, FR_units, FR_tens, FR_days, FR_months },
{ USERLANG_IS, IS_units, IS_tens, EN_days, IS_months },
{ USERLANG_IT, IT_units, IT_tens, EN_days, IT_months },
{ USERLANG_NL, NL_units, NL_tens, NL_days, NL_months },
{ USERLANG_NO, NO_units, NO_tens, EN_days, NO_months },
{ USERLANG_PO, PO_units, PO_tens, EN_days, PO_months },
{ USERLANG_SV, SV_units, SV_tens, EN_days, SV_months },
{ -1, NULL, NULL, NULL, NULL }
};
int
index;
for (index = 0; languages [index].language != -1; index++)
if (languages [index].language == language)
{
user_language = language;
units_table = languages [index].units;
tens_table = languages [index].tens;
day_table = languages [index].days;
month_table = languages [index].months;
return (0);
}
return (-1);
}
#include "sfllang.h" int set_userlang_str (const char *language)
Sets language used for date and numeric translation, using a string representation of the language. The valid user languages are:
| "" | Default language (use hard-coded values) |
| "--" | Alternative form for default language |
| "DA" | Danish |
| "DE" | German |
| "EN" | English |
| "ES" | Castillian Spanish |
| "FB" | Belgian or Swiss French |
| "FR" | French |
| "IS" | Icelandic |
| "IT" | Italian |
| "NL" | Dutch |
| "NO" | Norwegian |
| "PO" | Portuguese |
| "SV" | Swedish |
{
int
index;
if (strnull (language))
return (set userlang (USERLANG_DEFAULT));
for (index = 0; index < USERLANG_TOP; index++)
if (streq (language, language_str [index]))
return (set userlang (index));
return (-1);
}
#include "sfllang.h" int get_userlang (void)
Returns the current user language code.
{
return (user_language);
}
#include "sfllang.h" char * get_userlang_str (void)
Returns the current user language as a 2-character string.
{
return (language_str [user_language]);
}
#include "sfllang.h" int set_accents (Bool accents)
Enables or disables native-language accents. If enabled, accented characters in translated words are produced in the current system character set, if possible. Otherwise, suitable translations are made into the 26-letter English alphabet. By default, accents are enabled.
{
use_accents = accents;
return (0);
}
#include "sfllang.h" Bool get_accents (void)
Returns TRUE if accents are enabled, FALSE if not.
{
return (use_accents);
}
#include "sfllang.h" char * get_units_name (int units)
Returns the name for the specified units, which is a value from zero to 19. Accented characters are formatted according to the current accents setting.
{
ASSERT (units >= 0 && units <= 19);
return (handle_accents (units_table [units]));
}
#include "sfllang.h" char * get_tens_name (int tens)
Returns the name for the specified tens, which is a value from 10 to 90; it is rounded as required. Accented characters are formatted according to the current accents setting.
{
ASSERT (tens >= 10 && tens < 100);
return (handle_accents (tens_table [tens / 10 - 1]));
}
#include "sfllang.h" char * get_day_name (int day)
Returns the name for the specified day, which must be a value from 0 (Sunday) to 6 (Saturday). Accented characters are formatted according to the current accents setting.
{
ASSERT (day >= 0 && day <= 6);
return (handle_accents (day_table [day]));
}
#include "sfllang.h" char * get_day_abbrev (int day, Bool upper)
Returns the abbreviation for the specified day, which must be a value from 0 (Sunday) to 6 (Saturday). The abbreviation (3 letters) is converted into uppercase if the 'upper' argument is true. Accented characters are formatted according to the current accents setting.
{
char
abbrev [4];
ASSERT (day >= 0 && day <= 6);
strncpy (abbrev, day_table [day], 3);
abbrev [3] = '\0';
if (upper)
strupc (abbrev);
return (handle_accents (abbrev));
}
#include "sfllang.h" char * get_month_name (int month)
Returns the name for the specified month, which must be a value from 1 to 12. Accented characters are handled as per the current accents setting.
{
ASSERT (month >= 1 && month <= 12);
return (handle_accents (month_table [month - 1]));
}
#include "sfllang.h" char * get_month_abbrev (int month, Bool upper)
Returns the abbreviation for the specified month, which must be a value from 1 to 12. The abbreviation (3 letters) is converted into uppercase if the 'upper' argument is true. Accented characters are formatted according to the current accents setting.
{
char
abbrev [4];
ASSERT (month >= 1 && month <= 12);
strncpy (abbrev, month_table [month - 1], 3);
abbrev [3] = '\0';
if (upper)
strupc (abbrev);
return (handle_accents (abbrev));
}
#include "sfllang.h" char * timestamp_string (char *buffer, const char *pattern)
Formats a timestamp according to a user-supplied pattern. The result is returned in a buffer supplied by the caller; if this argument is NULL, allocates a buffer and returns that (the caller must then free the buffer using mem_free()). The pattern consists of arbitrary text mixed with insertion symbols indicated by '%':
| %y | day of year, 001-366 |
| %yy | year 2 digits, 00-99 |
| %yyyy | year 4 digits, 100-9999 |
| %mm | month, 01-12 |
| %mmm | month, Jan |
| %mmmm | month, January |
| %MMM | month, JAN |
| %MMMM | month, JANUARY |
| %dd | day, 01-31 |
| %ddd | day of week, Sun |
| %dddd | day of week, Sunday |
| %DDD | day of week, SUN |
| %DDDD | day of week, SUNDAY |
| %w | day of week, 1-7 (1=Sunday) |
| %ww | week of year, 01-53 |
| %q | year quarter, 1-4 |
| %h | hour, 00- 23 |
| %m | minute, 00- 59 |
| %s | second, 00- 59 |
| %c | centisecond, 00-99 |
| %% | literal character % |
{
long
date, /* Current date */
time; /* and time */
int
century, /* Century component of date */
year, /* Year component of date */
month, /* Month component of date */
day, /* Day component of date */
hour, /* Hour component of time */
minute, /* Minute component of time */
second, /* Second component of time */
centi, /* 1/100 sec component of time */
cursize; /* Size of current component */
char
*dest, /* Store formatted data here */
ch; /* Next character in picture */
date = date now ();
time = time now ();
century = GET_CENTURY (date);
year = GET_YEAR (date);
month = GET_MONTH (date);
day = GET_DAY (date);
hour = GET_HOUR (time);
minute = GET_MINUTE (time);
second = GET_SECOND (time);
centi = GET_CENTI (time);
if (buffer == NULL)
buffer = mem_alloc (strlen (pattern) * 2);
/* Scan through picture, converting each component */
dest = buffer;
*dest = 0; /* String is empty */
while (*pattern)
{
ch = *pattern++;
if (ch == '%' && *pattern)
{
ch = *pattern++; /* Count size of pattern after % */
for (cursize = 1; *pattern == ch; cursize++)
pattern++;
}
else
{
*dest++ = ch; /* Something else - store and next */
*dest = 0; /* Terminate the string nicely */
continue;
}
/* Now process pattern itself */
switch (ch)
{
case 'y':
if (cursize == 1) /* y day of year, 001-366 */
sprintf (dest, "%03d", julian date (date));
else
if (cursize == 2) /* yy year 2 digits, 00-99 */
sprintf (dest, "%02d", year);
else
if (cursize == 4) /* yyyy year 4 digits, 0100-9999 */
sprintf (dest, "%02d%02d", century, year);
break;
case 'm':
if (cursize == 1) /* m minute, 00-59 */
sprintf (dest, "%02d", minute);
else
if (cursize == 2) /* mm month, 01-12 */
sprintf (dest, "%02d", month);
else
if (cursize == 3) /* mmm month, 3 letters */
strcpy (dest, get month abbrev (month, FALSE));
else
if (cursize == 4) /* mmmm month, full name */
strcpy (dest, get month name (month));
break;
case 'M':
if (cursize == 3) /* MMM month, 3-letters, ucase */
strcpy (dest, get month abbrev (month, TRUE));
else
if (cursize == 4) /* MMMM month, full name, ucase */
{
strcpy (dest, get month name (month));
strupc (dest);
}
break;
case 'd':
if (cursize == 2) /* dd day, 01-31 */
sprintf (dest, "%02d", day);
else
if (cursize == 3) /* ddd day of week, Sun */
strcpy (dest, get day abbrev (day of week (date), FALSE));
else
if (cursize == 4) /* dddd day of week, Sunday */
strcpy (dest, get day name (day of week (date)));
break;
case 'D':
if (cursize == 3) /* DDD day of week, SUN */
strcpy (dest, get day abbrev (day of week (date), TRUE));
else
if (cursize == 4) /* DDDD day of week, SUNDAY */
{
strcpy (dest, get day name (day of week (date)));
strupc (dest);
}
break;
case 'w':
if (cursize == 1) /* w day of week, 1-7 (1=Sun) */
sprintf (dest, "%d", day of week (date) + 1);
else
if (cursize == 2) /* ww week of year, 01-53 */
sprintf (dest, "%d", week of year (date));
break;
case 'q':
if (cursize == 1) /* q year quarter, 1-4 */
sprintf (dest, "%d", year quarter (date));
break;
case 'h':
if (cursize == 1) /* h hour, 00-23 */
sprintf (dest, "%02d", hour);
break;
case 's':
if (cursize == 1) /* s second, 00-59 */
sprintf (dest, "%02d", second);
break;
case 'c':
if (cursize == 1) /* c centisecond, 00-99 */
sprintf (dest, "%02d", centi);
break;
case '%':
if (cursize == 1) /* % literal '%' */
strcpy (dest, "%");
break;
}
if (*dest) /* If something was output, */
while (*dest) /* skip to end of string */
dest++;
else
{
while (cursize--) /* Else output ch once or more */
*dest++ = ch; /* and bump dest pointer */
*dest = 0; /* Terminate the string nicely */
}
}
return (buffer);
}
Filename: sfllbuf.h
Package: Standard Function Library (SFL)
Written: 97/09/07 iMatix SFL project team sfl@imatix.com
Revised: 97/09/08
Copyright: Copyright (c) 1991-98 iMatix
Provides circular line buffering functions. A line buffer is a data structure that holds a fixed amount of data in a serial fashion; the oldest data gets discarded as new data is added.
sfllbuf.h defines these symbols, possibly conditionally:
| Symbol: | Defined as: |
|---|---|
| _SFLLBUF_INCLUDED | TRUE |
#include "sfllbuf.h" LINEBUF * linebuf_create (size_t maxsize)
Creates a new line buffer with the specified size. The size must be at least LINE_MAX + 1 characters long. Returns the address of the newly-created buffer, or NULL if there was insufficient memory. The fresh line buffer is set to empty (tail == head).
{
LINEBUF
*buffer;
ASSERT (maxsize > LINE_MAX);
buffer = mem_alloc (sizeof (LINEBUF));
if (!buffer)
return (NULL);
buffer-> data = mem_alloc (maxsize);
if (!buffer-> data)
{
free (buffer);
return (NULL);
}
buffer-> head = buffer-> data;
buffer-> tail = buffer-> data;
buffer-> top = buffer-> data + maxsize;
buffer-> size = maxsize;
return (buffer);
}
#include "sfllbuf.h" void linebuf_destroy (LINEBUF *buffer)
Destroys a line buffer and frees its memory.
{
ASSERT (buffer);
mem_free (buffer-> data);
mem_free (buffer);
}
#include "sfllbuf.h" void linebuf_reset (LINEBUF *buffer)
Resets a line buffer; i.e. empties it of all data. This is done simply by setting the tail and head to the start of the buffer.
{
ASSERT (buffer);
buffer-> head = buffer-> data;
buffer-> tail = buffer-> data;
}
#include "sfllbuf.h" void linebuf_append (LINEBUF *buffer, const char *line)
Appends a line to the line buffer. If the buffer was full, the oldest line is lost. Updates the buffer head and tail as needed.
{
int
length, /* Size of line to insert */
room_left, /* Space left between head and top */
tail_old, /* Offset of tail into buffer */
head_old, /* Offset of head before insert */
head_new; /* Offset of head after insert */
char
*linedata; /* Address of data to store */
ASSERT (buffer);
ASSERT (line);
linedata = (char *) line;
length = strlen (line) + 1; /* Include trailing null */
room_left = (int) (buffer-> top - buffer-> head);
/* We need to make space for the new line; we calculate the new head
* and if the tail falls between the old and new head, it must be moved
* up to the next line start. We compare 'ints' not 'char *' because
* they can be negative.
*/
tail_old = (int) (buffer-> tail - buffer-> data);
head_old = (int) (buffer-> head - buffer-> data);
if (head_old > tail_old) /* Shift head_old down to get it */
head_old -= buffer-> size; /* somewhere before tail_old */
head_new = head_old + length; /* And calculate head_new */
/* If the line is too large for the remaining space, copy what we can */
if (length > room_left)
{
memcpy (buffer-> head, linedata, room_left);
linedata += room_left;
length -= room_left;
buffer-> head = buffer-> data; /* Bump head to start of buffer */
}
/* Copy rest of line to buffer */
memcpy (buffer-> head, linedata, length);
buffer-> head += length; /* Bump head past string */
if (buffer-> head == buffer-> top) /* and maybe wrap-around */
buffer-> head = buffer-> data;
ASSERT (buffer-> head <= buffer-> top);
if (head_old < tail_old /* If tail falls between head_old */
&& tail_old <= head_new) /* and/on head_new, bump it up */
buffer-> tail = start_next_line (buffer, buffer-> head);
}
#include "sfllbuf.h" char * linebuf_first (LINEBUF *buffer, DESCR *descr)
Fetches the oldest line in the buffer. Returns a pointer that may be used in calls to linebuf next(). Returns NULL if the buffer is empty. The line is stored in the supplied descriptor, and is truncated if the descriptor is too small.
{
ASSERT (buffer);
ASSERT (descr);
return (linebuf next (buffer, descr, buffer-> tail));
}
#include "sfllbuf.h" char * linebuf_next (LINEBUF *buffer, DESCR *descr, const char *curline)
Fetches the next line in the buffer, using the pointer that was returned by linebuf first(). Returns NULL if there are no more lines in the buffer, or a pointer for further calls. The line is stored in the supplied descriptor, and is truncated if the descriptor is too small.
{
ASSERT (buffer);
ASSERT (descr);
ASSERT (curline);
if (curline == buffer-> head)
return (NULL); /* We're at the end */
else
return (get_line (buffer, descr, curline));
}
#include "sfllbuf.h" char * linebuf_last (LINEBUF *buffer, DESCR *descr)
Fetches the newest line in the buffer. Returns a pointer that may be used in calls to linebuf next(). Returns NULL if the buffer is empty. The line is stored in the supplied descriptor, and is truncated if the descriptor is too small.
{
ASSERT (buffer);
ASSERT (descr);
return (linebuf prev (buffer, descr, buffer-> head));
}
#include "sfllbuf.h" char * linebuf_prev (LINEBUF *buffer, DESCR *descr, const char *curline)
Fetches the previous line in the buffer, using the pointer that was returned by linebuf last(). Returns NULL if there are no more lines in the buffer, or a pointer for further calls. The line is stored in the supplied descriptor, and is truncated if the descriptor is too small.
{
ASSERT (buffer);
ASSERT (descr);
ASSERT (curline);
if (curline == buffer-> tail)
return (NULL); /* We're at the start */
else
{
/* We're pointing to the byte after the line's null byte */
buffer_dec (curline); /* Bump down to null */
ASSERT (*curline == '\0');
do
{
buffer_dec (curline); /* And now look for previous null */
if (*curline == '\0')
{
buffer_inc (curline); /* Bump up to start of string */
break;
}
}
until (curline == buffer-> tail);
get_line (buffer, descr, curline);
return ((char *) curline);
}
}
Filename: sfllist.h
Package: Standard Function Library (SFL)
Written: 97/07/28 iMatix SFL project team sfl@imatix.com
Revised: 98/07/26
Copyright: Copyright (c) 1991-98 iMatix
Provides functions to maintain doubly-linked lists. You can use these functions to work with lists of any structure. To make this work, all structures must start with two pointers, "void *next, *prev;". When you want to attach a linked-list to another structure, declare the list head as a list. You can then refer to this variable when you attach items to the list head. The code sets the global list_unsafe to TRUE whenever it is changing a list.
sfllist.h defines these symbols, possibly conditionally:
| Symbol: | Defined as: |
|---|---|
| FORLIST(node,root) | for ((node) = (root).next; |
| _SFLLIST_INCLUDED | TRUE |
| list_create(node,size) | if (((node) = mem_alloc (size)) != NULL) |
| list_empty(list) | ((list)-> prev == (list)) |
| list_pop(list,item) | list_remove (list, |
| list_push(list,item) | list_add (list, |
| list_queue(list,item) | list_add (((LIST *)list)-> prev, |
| list_relink_after(l,a) | (list_relink (a, l, ((LIST *) a)-> next)) |
| list_relink_before(l,b) | (list_relink (((LIST *) b)-> prev, l, b)) |
| list_reset(list) | (list)-> prev = (list)-> next = (list) |
| Type name: | Defined as: |
|---|---|
| NODE_COMPARE | Bool (*) (LIST *t1, LIST *t2) |
#include "sfllist.h"
void *
list_unlink (
void *list)
Unlinks the list from any list it may be in. Returns list.
{
list_unsafe = TRUE;
/* Join together next and previous nodes */
((LIST *) ((LIST *) list)-> prev)-> next = ((LIST *) list)-> next;
((LIST *) ((LIST *) list)-> next)-> prev = ((LIST *) list)-> prev;
/* The list is now empty */
list_reset ((LIST *) list);
list_unsafe = FALSE;
return (list);
}
#include "sfllist.h"
void *
list_relink (
void *left,
void *list,
void *right)
Links the list into a linked list. This is a general-purpose function that can be used to attach and remove lists anywhere in a list. Sets the global variable 'list_unsafe' while the list is being changed. Returns the address of list.
{
LIST *swap;
list_unsafe = TRUE;
swap = ((LIST *) left)-> next; /* Exchange left pointers */
((LIST *) left)-> next = list;
((LIST *) ((LIST *) list)-> prev)-> next = swap;
swap = ((LIST *) right)-> prev; /* Exchange right pointers */
((LIST *) right)-> prev = ((LIST *) list)-> prev;
((LIST *) list)-> prev = swap;
list_unsafe = FALSE;
return (list);
}
#include "sfllist.h" void * list_add (LIST *list, void *data, size_t size)
Creates a node at the head of a list of a specified size and copies the specified data into it. Use with the stack & queue macros list_push and list_queue.
{
LIST
*node;
node = mem_alloc (sizeof (LIST) + size);
if (node)
{
list_reset (node);
list_relink_after (node, list);
memcpy ((char *) node + sizeof (LIST), (char *) data, size);
}
return node;
}
#include "sfllist.h" void list_remove (LIST *list, void *data, size_t size)
Removes the node at the head of a list, copying the specified amount of data to the specified data block. This size must be exactly equal to the size of the data block. The list may not be empty. Use this with the macro list_pop.
{
LIST
*node;
ASSERT (!list_empty (list));
node = list-> next;
ASSERT (mem_size (node) - sizeof (LIST) == size);
memcpy ((char *) data, (char *) node + sizeof (LIST), size);
list unlink (node);
mem_free (node);
}
#include "sfllist.h" void list_sort (void *list, NODE_COMPARE comp)
Sorts a list using the "comb-sort" algorithm.
{
int
jump_size,
i;
LIST
*base,
*swap,
*temp;
Bool
swapped;
jump_size = 0;
FORLIST (base, * (LIST *) list)
jump_size++;
swapped = TRUE;
while ((jump_size > 1) || swapped)
{
jump_size = (10 * jump_size + 3) / 13;
base = ((LIST *) list)-> next;
swap = base;
for (i = 0; i < jump_size; i++)
swap = swap-> next;
swapped = FALSE;
while (swap != (LIST *) list)
{
if ((*comp) (base, swap))
{
temp = base-> prev;
list unlink (base);
list_relink_after (base, swap);
list unlink (swap);
list_relink_after (swap, temp);
temp = base;
base = swap;
swap = temp;
swapped = TRUE;
}
base = base-> next;
swap = swap-> next;
}
}
}
Filename: sflmail.h
Package: standard function library (sfl)
Written: 06/18/97 Scott Beasley (jscottb@infoave.com)
Revised: 98/05/11
Copyright: Copyright (C) 1991-97 Imatix
Functions to format and send SMTP messages. Messages can contain attachments, and be sent with "cc"'s "bcc"'s as well as the normal "to" receivers.
sflmail.h defines these symbols, possibly conditionally:
| Symbol: | Defined as: |
|---|---|
| _sflmail_included | TRUE |
#include "sflmail.h" int smtp_send_mail_ex ( SMTP *smtp)
Format and send a SMTP message. This function gives you the options of sneding to multi receivers, CC's, Bcc's and also send UUencoded attachments. Receivers and files are ";" or "," terminated. <PRE> NOTE: The sock_init function should be called before use of this function. </PRE>
{
FILE *fpin;
int iCnt;
sock_t iSocket;
char strOut[514], strFile[256], strRetBuff[513];
char strUUEFile[256], *strRcptUserIds;
int iOld_ip_nonblock = ip_nonblock;
/* Make sure we do not block. */
ip_nonblock = FALSE;
/* Open up the SMTP port (25 most of the time). */
iSocket = connect socket (smtp->strSmtpServer,
"smtp", "tcp", NULL,
smtp->connect_retry_cnt,
smtp->retry_wait_time);
if (getreply (iSocket, smtp) > 400 || iSocket < 1)
{
return -1;
}
/* Format a SMTP meassage header. */
/* Just say hello to the mail server. */
xstrcpy (strOut, "HELO ", get hostname (), "\n", NULL);
smtp_send_data (iSocket, strOut);
if (getreply (iSocket, smtp) > 400)
return -2;
/* Tell the mail server who the message is from. */
xstrcpy (strOut, "MAIL FROM:<", smtp->strSenderUserId, ">\n", NULL);
smtp_send_data (iSocket, strOut);
if (getreply (iSocket, smtp) > 400)
return -3;
strRcptUserIds = (char *) malloc (strlen (smtp->strDestUserIds) +
strlen (smtp->strCcUserIds) +
strlen (smtp->strBccUserIds) + 1);
sprintf (strRcptUserIds, "%s;%s;%s", smtp->strDestUserIds,
smtp->strCcUserIds, smtp->strBccUserIds);
/* The following tells the mail server who to send it to. */
iCnt = 0;
while (1)
{
getstrfld (strRcptUserIds, iCnt++, 0, ",;", strRetBuff);
if (*strRetBuff)
{
xstrcpy (strOut, "RCPT TO:<", strRetBuff, ">\r\n", NULL);
smtp_send_data (iSocket, strOut);
if (getreply (iSocket, smtp) > 400)
return -4;
}
else
break;
}
free (strRcptUserIds);
/* Now give it the Subject and the message to send. */
smtp_send_data (iSocket, "DATA\r\n");
if (getreply (iSocket, smtp) > 400)
return -5;
/* Set the date and time of the message. */
xstrcpy ( strOut, "Date: ", encode mime time (date now (), time now ()),
" \r\n", NULL );
/* The following shows all who it was sent to. */
replacechrswith (smtp->strDestUserIds, ";", ',');
xstrcpy (strOut, "To: ", smtp->strDestUserIds, "\r\n", NULL);
/* Set up the Reply-To path. */
if (!smtp->strRetPathUserId || !*smtp->strRetPathUserId)
smtp->strRetPathUserId = smtp->strSenderUserId;
xstrcat (strOut, "Reply-To:<", smtp->strRetPathUserId, ">\r\n", NULL);
xstrcat (strOut, "Sender:", smtp->strSenderUserId, "\r\n", NULL);
smtp_send_data (iSocket, strOut);
*strOut = '\0';
/* Post any CC's. */
if (smtp->strCcUserIds && *smtp->strCcUserIds)
{
replacechrswith (smtp->strCcUserIds, ";", ',');
xstrcat (strOut, "Cc:", smtp->strCcUserIds, "\r\n", NULL );
}
/* Post any BCC's. */
if (smtp->strBccUserIds && *smtp->strBccUserIds)
{
replacechrswith (smtp->strBccUserIds, ";", ',');
xstrcat (strOut, "Bcc:", smtp->strBccUserIds, "\r\n", NULL);
}
/* Post any Return-Receipt-To. */
if (smtp->strRrcpUserId && *smtp->strRrcpUserId)
xstrcat (strOut, "Return-Receipt-To:", smtp->strRrcpUserId, ">\r\n",
NULL);
if (smtp->strMailerName && *smtp->strMailerName)
xstrcat (strOut, "X-Mailer: ", smtp->strMailerName, "\r\n", NULL);
else
strcat (strOut, "X-Mailer: sflmail function\r\n");
/* Set the mime version. */
strcat (strOut, "MIME-Version: 1.0\r\n");
strcat (strOut,
"Content-Type: Multipart/Mixed; boundary=Message-Boundary-21132\r\n");
smtp_send_data (iSocket, strOut);
/* Write out any message comment included. */
xstrcpy (strOut, "Comments: ", smtp->strMsgComment, "\r\n", NULL);
/* Send the subject and message body. */
xstrcat (strOut, "Subject:", smtp->strSubject, "\n\r\n", NULL);
smtp_send_data (iSocket, strOut);
/* Keep rfc822 in mind with all the sections. */
if (smtp->strMessageBody && *smtp->strMessageBody)
{
strcat (strOut, "\r\n--Message-Boundary-21132\r\n");
strcat (strOut, "Content-Type: text/plain; charset=US-ASCII\r\n");
strcat (strOut, "Content-Transfer-Encoding: 7BIT\r\n");
strcat (strOut, "Content-description: Body of message\r\n\r\n");
smtp_send_data (iSocket, strOut);
smtp_send_data (iSocket, smtp->strMessageBody);
smtp_send_data (iSocket, "\r\n");
}
/* Include any Text type files and Attach them to the message. */
if (smtp->strTxtFiles && *smtp->strTxtFiles)
{
iCnt = 0;
while (1)
{
getstrfld (smtp->strTxtFiles, iCnt++, 0, ",;", strFile);
trim (strFile);
if (*strFile)
{
fpin = fopen (strFile, "rb");
if (!fpin)
{
strcpy (smtp->strlast_smtp_message, strFile);
return -6;
}
strcpy (strOut, "\r\n--Message-Boundary-21132\r\n");
strcat (strOut,
"Content-Type: text/plain; charset=US-ASCII\r\n");
strcat (strOut, "Content-Transfer-Encoding: 7BIT\r\n");
xstrcat (strOut, "Content-Disposition: attachment; filename=",
getfilename (strFile), "\r\n\n", NULL);
smtp_send_data (iSocket, strOut);
while (!feof (fpin))
{
memset (strRetBuff, 0, 513);
fread (strRetBuff, sizeof (char), 512, fpin);
smtp_send_data (iSocket, strRetBuff);
}
fclose (fpin);
}
else
break;
}
}
/* Attach any bin files to the message. */
if (smtp->strBinFiles && *smtp->strBinFiles)
{
iCnt = 0;
while (1)
{
getstrfld (smtp->strBinFiles, iCnt++, 0, ",;", strFile);
trim (strFile);
if (*strFile)
{
strcpy (strUUEFile, strFile);
if (strchr (strUUEFile, '.'))
*((strchr (strUUEFile, '.')))= (char)NULL;
strcat (strUUEFile, ".uue");
uuencode (strFile, strUUEFile, smtp->strlast_smtp_message);
fpin = fopen (strUUEFile, "rb");
if (!fpin)
{
return -6;
}
strcpy (strOut, "\r\n--Message-Boundary-21132\r\n");
xstrcat (strOut,
"Content-Type: application/octet-stream; name=",
getfilename (strFile), "\r\n", NULL);
strcat (strOut, "Content-Transfer-Encoding: x-uuencode\r\n");
xstrcat (strOut, "Content-Disposition: attachment; filename=",
getfilename (strFile), "\r\n\n", NULL);
smtp_send_data (iSocket, strOut);
while (!feof (fpin))
{
memset (strRetBuff, 0, 513);
fread (strRetBuff, sizeof (char), 512, fpin);
smtp_send_data (iSocket, strRetBuff);
}
fclose (fpin);
if ( !smtp->debug )
unlink (strUUEFile);
}
else
break;
}
}
/* This ends the message. */
smtp_send_data (iSocket, ".\r\n");
if (getreply (iSocket, smtp) > 400)
return -7;
/* Now log off the SMTP port. */
smtp_send_data (iSocket, "QUIT\n");
if (getreply (iSocket, smtp) > 400)
return -8;
/*
Clean-up.
*/
/* Close the port up. */
close socket (iSocket);
/* If a clean send, then reset and leave. */
ip_nonblock = iOld_ip_nonblock;
return 0;
}
#include "sflmail.h" int smtp_send_mail ( char *strSmtpServer, char *strMessageBody, char *strSubject, char *strSenderUserId, char *strDestUserIds, char *strCcUserIds, char *strBccUserIds, char *strRetPathUserId, char *strRrcpUserId, char *strMsgComment, char *strMailerName, char *strBinFiles, char *strTxtFiles)
Format and send a SMTP message, by calling the smtp_send_mail_ex function. This function is kept to be compatable with previous versions of smtp_send_mail, smtp_send_mail_ex should now be used, this will be deleted soon.
{
SMTP smtp;
smtp.strSmtpServer = strSmtpServer;
smtp.strMessageBody = strMessageBody;
smtp.strSubject = strSubject;
smtp.strSenderUserId = strSenderUserId;
smtp.strDestUserIds = strDestUserIds;
smtp.strCcUserIds = strCcUserIds;
smtp.strBccUserIds = strBccUserIds;
smtp.strRetPathUserId = strRetPathUserId;
smtp.strRrcpUserId = strRrcpUserId;
smtp.strMsgComment = strMsgComment;
smtp.strMailerName = strMailerName;
smtp.strBinFiles = strBinFiles;
smtp.strTxtFiles = strTxtFiles;
smtp.connect_retry_cnt = 3;
smtp.retry_wait_time = 0;
smtp.debug = 0;
return smtp send mail ex (&smtp);
}
Filename: sflmath.h
Package: Standard Function Library (SFL)
Written: 96/05/12 iMatix SFL project team sfl@imatix.com
Revised: 97/09/08
Copyright: Copyright (c) 1991-98 iMatix
Provides miscellaneous mathematical functions, including calculation of points within areas.
sflmath.h defines these symbols, possibly conditionally:
| Symbol: | Defined as: |
|---|---|
| _SFLMATH_INCLUDED | TRUE |
#include "sflmath.h" int point_in_rect (const FPOINT *point, const FPOINT *coords)
Checks if the requested FPOINT is within the specified rectangle. Returns TRUE or FALSE appropriately.
{
return ((point-> x >= coords [0].x && point-> x <= coords [1].x)
&& (point-> y >= coords [0].y && point-> y <= coords [1].y));
}
#include "sflmath.h" int point_in_circle (const FPOINT *point, const FPOINT *coords)
Checks if the requested FPOINT is within the specified circle. Returns TRUE or FALSE appropriately.
{
double
circle_radius,
distance_from_centre;
circle_radius = ((coords [0].y - coords [1].y) *
(coords [0].y - coords [1].y)) +
((coords [0].x - coords [1].x) *
(coords [0].x - coords [1].x));
distance_from_centre = ((coords [0].y - point-> y) *
(coords [0].y - point-> y)) +
((coords [0].x - point-> x) *
(coords [0].x - point-> x));
return (distance_from_centre <= circle_radius);
}
#include "sflmath.h" int point_in_poly (const FPOINT *point, const FPOINT *pgon, int nbpoints)
Checks if the requested FPOINT is within the specified polygon. Returns TRUE or FALSE.
{
int
inside_flag,
xflag0,
crossings;
const double
*stop;
double
*p,
tx,
ty,
y;
crossings = 0;
tx = point-> x;
ty = point-> y;
y = pgon [nbpoints - 1].y;
p = (double *) pgon + 1;
if ((y >= ty) != (*p >= ty))
{
if ((xflag0 = (pgon [nbpoints - 1].x >= tx)) ==
(*(double *) pgon >= tx))
{
if (xflag0)
crossings++;
}
else
crossings += (pgon [nbpoints - 1].x - (y - ty) *
(*(double *) pgon - pgon [nbpoints - 1].x) /
(*p - y)) >= tx;
}
stop = &pgon [nbpoints].y;
for (y = *p, p += 2; p <= stop; y = *p, p += 2)
{
if (y >= ty)
{
while ((p < stop) && (*p >= ty))
p += 2;
if (p >= stop)
break;
if ((xflag0 = (*(p - 3) >= tx)) == (*(p - 1) >= tx))
{
if (xflag0)
crossings++;
}
else
crossings += (*(p - 3) - (*(p - 2) - ty) *
(*(p - 1) - *(p - 3)) /
(*p - *(p - 2))) >= tx;
}
else
{
while ((p < stop) && (*p < ty))
p += 2;
if (p >= stop)
break;
if ((xflag0 = (*(p - 3) >= tx)) == (*(p - 1) >= tx))
{
if (xflag0)
crossings++;
}
else
crossings += (*(p - 3) - (*(p - 2) - ty) *
(*(p - 1) - *(p - 3)) /
(*p - *(p - 2))) >= tx;
}
}
inside_flag = crossings & 0x01;
return (inside_flag);
}
Filename: sflmesg.h
Package: Standard Function Library (SFL)
Written: 92/10/25 iMatix SFL project team sfl@imatix.com
Revised: 97/09/08
Copyright: Copyright (c) 1991-98 iMatix
Provides functions to read and format messages from a message file. The intention of such a file is to provide a single location for all error messages: you can easier translate these into foreign languages, and you can control the consistency of an application's error messages.
sflmesg.h defines these symbols, possibly conditionally:
| Symbol: | Defined as: |
|---|---|
| ERROR_ANY | 0000 /* Generic error message */ |
| _SFLMESG_INCLUDED | TRUE |
#include "sflmesg.h" int open_message_file (const char *filename)
Opens the specified error message file for reading. Returns 0 if the file exists and is readable, otherwise returns -1. Use this function before calling print message(). You can keep just one message file open at once; this function closes any previously-opened message file. This was done on purpose: it is common to open a message file for an entire application in the main function, then refer to it at other points in the code. It is a pain to pass file handles around the entire application, and global variables are generally a bad idea.
{
int feedback;
close message file ();
msgfile = file open (filename, 'r');
if (msgfile)
feedback = 0;
else
feedback = -1;
return (feedback);
}
#include "sflmesg.h" void close_message_file (void)
Closes the currently open message file, if any. Does not return anything.
{
if (msgfile)
{
file close (msgfile);
msgfile = NULL;
}
}
#include "sflmesg.h" void print_message (int msgid, ...)
Scans the message file for a message with the specified id. Each line in the message file should start with a four-digit id, then a space, then the message to print. The message can include format specifiers using '%'. Values for each format are passed after the msgid. Returns nothing. The message file must be sorted by ascending message id's. Make sure you call open message file () before this function. Prints the message on stderr.
{
va_list argptr; /* Argument list pointer */
read_msg (msgid); /* Retrieve message into msgline */
va_start (argptr, msgid); /* Start variable arguments list */
vfprintf (stderr, msgline, argptr);
va_end (argptr); /* End variable arguments list */
fprintf (stderr, "\n");
fflush (stderr);
}
#include "sflmesg.h" char * message_text (int msgid)
Works like print message(), but returns a pointer to the raw message rather than printing it. The message text is stored in a static area that is overwritten by each call. If msgid is -1, retrieves the next message sequentially, ignoring any numbering. This is only valid after previously reading a message. Places "." in the message if no more are found.
{
read_msg (msgid); /* Retrieve message into msgline */
return (msgline);
}
Filename: sflmem.h
Package: Standard Function Library (SFL)
Written: 96/06/08 iMatix SFL project team sfl@imatix.com
Revised: 98/08/31
Copyright: Copyright (c) 1991-98 iMatix
Encapsulated memory allocation functions. Based on an article by Jim Schimandle in DDJ August 1990. Provides 'safe' versions of malloc(), realloc(), free(), and strdup(). These functions protect the programmer from errors in calling memory allocation/free routines. When these calls are used, the allocation routines in this module add a data structure to the top of allocated memory blocks which tags them as legal memory blocks. When the free routine is called, the memory block to be freed is checked for legality. If the block is not legal, the memory list is dumped to stderr and the program is terminated. Some of these functions are called through macros that add the filename and line number of the call, for tracing. Do not call these functions directly.
sflmem.h defines these symbols, possibly conditionally:
| Symbol: | Defined as: |
|---|---|
| _SFLMEM_INCLUDED | TRUE |
| mem_alloc(n) | (various) |
| mem_assert() | (various) |
| mem_check(p) | (various) |
| mem_checkall() | (various) |
| mem_commit(t) | (various) |
| mem_descr(p,n) | (various) |
| mem_free(p) | (various) |
| mem_new_trans() | (various) |
| mem_realloc(p,n) | (various) |
| mem_rollback(t) | (various) |
| mem_size(p) | (various) |
| mem_strdup(s) | (various) |
| mem_strfree(ps) | (various) |
| memt_alloc(t,n) | (various) |
| memt_descr(t,p,n) | (various) |
| memt_strdup(t,s) | (various) |
| Type name: | Defined as: |
|---|---|
| MEMHDR | struct _MEMHDR |
| MEMTRN | struct _MEMTRN |
| scavenger | Bool (*) (void *) |
#include "sflmem.h"
void *
mem_alloc_ (
MEMTRN *trn, /* Associated transaction */
size_t size, /* Desired size of memory block */
const char *filename, /* Name of source file making call */
word lineno /* Line number in calling source */
)
Allocates a memory block. Use the mem_alloc() macro to call this function! Use mem free () to free blocks allocated with this function. Returns a pointer to the allocated memory block, or NULL if there was not enough memory available. The supplied source file name is assumed to be in a static area. The requested block size must be greater than zero bytes.
{
MEMHDR
*ptr; /* Allocated memory block */
/* Allocate block with extra space for the header */
ASSERT (size > 0); /* Cannot allocate zero bytes! */
ptr = malloc (RESERVE_SIZE + size);
if (ptr == NULL) /* If nothing free, do a hunt */
{ /* and try again... */
mem_scavenge ();
ptr = malloc (RESERVE_SIZE + size);
if (ptr == NULL)
return (NULL); /* Really in trouble now! */
}
# if (defined (MEM_TRACE))
if (filename)
trace ("%s (%d): alloc %d bytes->%p", filename, lineno, size, ptr);
# endif
ptr-> tag = MEMTAG; /* Initialise block header */
ptr-> size = size; /* Size of block */
ptr-> file = filename; /* Who allocated it */
ptr-> line = lineno; /* and where */
if (!trn) /* If no transaction then use the */
trn = &mem_list; /* main block list */
list_reset (ptr); /* Set up new block as list */
list_relink_before (ptr, /* Add to list of blocks */
&trn-> memhdr);
mem_total += size; /* Keep count of space used */
mem_alloc_count += 1; /* and number of allocations */
return (HDR_2_CLIENT (ptr)); /* and return client address */
}
#include "sflmem.h"
void *
mem_realloc_ (
void *client_ptr, /* Block of memory to reallocate */
size_t size, /* Desired size of memory block */
const char *filename, /* Name of source file making call */
word lineno /* Line number in calling source */
)
Reallocates a memory block, which remains part of the same transaction. Use the mem_realloc() macro to call this function! Accepts a pointer to a memory block and the desired size of the new memory block. Returns the address of the new memory block, or NULL if there was not enough memory available. If the specified block was not correctly allocated, dumps the memory allocation list and exits. The desired size must be greater than zero.
{
MEMHDR
*ptr,
*next;
ASSERT (client_ptr);
ASSERT (size > 0);
/* Check that block is valid */
ptr = CLIENT_2_HDR (client_ptr);
if (ptr-> tag != MEMTAG)
mem_tag_err (ptr, filename, lineno);
/* Invalidate header */
ptr-> tag = (word) ~MEMTAG;
mem_total -= ptr-> size;
mem_free_count += 1;
next = ptr-> next; /* Save where we were linked */
list unlink (ptr); /* and unlink */
/* Reallocate memory block */
ptr = (MEMHDR *) realloc (ptr, RESERVE_SIZE + size);
if (ptr == NULL) /* If nothing free, do a hunt */
{ /* and try again... */
mem_scavenge ();
ptr = (MEMHDR *) realloc (ptr, RESERVE_SIZE + size);
if (ptr == NULL)
return (NULL); /* Really in trouble now! */
}
# if (defined (MEM_TRACE))
if (filename)
trace ("%s (%d): realloc %d bytes ->%p", filename, lineno, size, ptr);
# endif
/* Update header */
ptr-> tag = MEMTAG;
ptr-> size = size;
ptr-> file = filename;
ptr-> line = lineno;
list_reset (ptr); /* Set up block as list */
list_relink_before (ptr, next); /* And link where old block was */
mem_total += size; /* Keep count of space used */
mem_alloc_count += 1; /* and number of allocations */
return (HDR_2_CLIENT (ptr));
}
#include "sflmem.h"
char *
mem_strdup_ (
MEMTRN *trn, /* Associated transaction */
const char *string, /* String to copy */
const char *filename, /* Name of source file making call */
word lineno /* Line number in calling source */
)
Saves a string in dynamic memory. Use the mem_strdup() macro to call this function! The caller is responsible for freeing the space allocated when it is no longer needed. Returns a pointer to the allocated string, which holds a copy of the parameter string. Returns NULL if there was insufficient heap storage available to allocate the string, or if the original string was itself NULL.
{
char *copy;
if (string) /* If string not null, copy it */
{
copy = mem alloc (trn, strlen (string) + 1, filename, lineno);
if (copy)
strcpy (copy, string);
}
else
copy = NULL; /* Just pass-through a NULL */
return (copy);
}
#include "sflmem.h"
void
mem_strfree_ (
char **string, /* Address of string to free */
const char *filename, /* Name of source file making call */
word lineno /* Line number in calling source */
)
Releases memory occupied by a string. Use the mem_strfree() macro to call this function! Call this function to free strings allocated using mem strdup (). Accepts the address of a char pointer as argument: if the pointer is not null, the string is freed, and the pointer is set to null. Returns the address of the modified pointer.
char
*string1 = NULL,
*string2 = NULL;
string1 = mem_strdup ("This is a string");
mem_strfree (&string1);
mem_strfree (&string2);
{
ASSERT (string);
if (*string)
{
mem free (*string, filename, lineno);
*string = NULL;
}
}
#include "sflmem.h"
void
mem_free_ (
void *client_ptr, /* Block of memory to free */
const char *filename, /* Name of source file making call */
word lineno /* Line number in calling source */
)
Releases memory previously allocated by mem alloc (), mem realloc (), or mem strdup (). Use the mem_free() macro to call this function! If the specified block was not correctly allocated, dumps the memory allocation list and exits. If you specify a null address, does nothing.
{
MEMHDR
*ptr;
if (client_ptr == NULL) /* Do nothing if address is null */
return;
/* Check for valid block */
ptr = CLIENT_2_HDR (client_ptr);
if (ptr-> tag != MEMTAG)
mem_tag_err (ptr, filename, lineno);
# if (defined (MEM_TRACE))
if (filename)
trace ("%s (%d): free=%p", filename, lineno, ptr);
# endif
/* Invalidate header */
ptr-> tag = (word) ~MEMTAG;
mem_total -= ptr-> size;
mem_free_count += 1;
list unlink (ptr); /* Remove block from list */
free (ptr);
}
#include "sflmem.h"
void
mem_assert_ (
const char *filename, /* Name of source file making call */
word lineno /* Line number in calling source */
)
Checks that all allocated memory was freed. Use the mem_assert macro to call this function! If any memory is still left allocated, displays the memory list on stderr and aborts. Generally we use this function at the end of a program, after deallocating all memory. If any memory has not been allocated, we get a nice list and an abort. Our principle is that any memory allocation must be matched by a free somewhere in the code.
{
FILE
*trace_file;
if (mem_total != 0
|| !list_empty (&tr_list))
{
fflush (stdout);
fprintf (stderr, "Clean-memory assertion failed - %s (%d)\n",
filename? filename: "<Unknown>", lineno);
fprintf (stderr, "Details are in memtrace.lst\n");
trace_file = fopen ("memtrace.lst", "w");
mem display (trace_file);
fclose (trace_file);
abort ();
}
}
#include "sflmem.h"
void
mem_checkall_ (
const char *filename, /* Name of source file making call */
word lineno /* Line number in calling source */
)
Checks all allocated memory blocks; if any block was corrupted, aborts with an error message, else does nothing.
{
MEMTRN
*trn;
# if (defined (MEM_TRACE))
if (filename)
trace ("%s (%d): check all memory", filename, lineno);
# endif
mem_check_list ((MEMHDR *) &mem_list.memhdr, filename, lineno);
trn = tr_list.next;
while (trn != (MEMTRN *) &tr_list)
{
mem_check_list ((MEMHDR *) &trn-> memhdr, filename, lineno);
trn = trn-> next;
}
}
#include "sflmem.h"
void
mem_check_ (
const void *client_ptr, /* Block of memory to free */
const char *filename, /* Name of source file making call */
word lineno /* Line number in calling source */
)
Checks that a block of memory has not been corrupted. If the block is corrupted, aborts with an error message, else does nothing.
{
MEMHDR
*ptr;
if (client_ptr == NULL) /* Do nothing if address is null */
return;
/* Check for valid block */
ptr = CLIENT_2_HDR (client_ptr);
if (ptr-> tag != MEMTAG)
mem_tag_err (ptr, filename, lineno);
}
#include "sflmem.h"
DESCR *
mem_descr_ (
MEMTRN *trn, /* Associated transaction */
const void *data_block, /* Block of memory to copy */
size_t data_size, /* Size of memory block */
const char *filename, /* Name of source file making call */
word lineno /* Line number in calling source */
)
Allocates a DESCR block for a specified block of data. Use the mem_descr macro to call this function! Returns a pointer to an allocated DESCR block, or NULL if there was not enough memory. The DESCR block is allocated as a single block, consisting of the DESCR block plus the data. To free the entire block you need one call to mem_free(). If the data_block argument is not null, its contents are copied into the newly allocated memory.
{
DESCR
*descr;
descr = mem alloc (trn, data_size + sizeof (DESCR), filename, lineno);
if (descr == NULL)
return (NULL);
# if (defined (MEM_TRACE))
if (filename)
trace ("%s (%d): allocate descr=%p", filename, lineno, descr);
# endif
/* Fill-in descriptor block unless it is NULL */
descr-> size = data_size;
descr-> data = (byte *) descr + sizeof (DESCR);
if (data_block)
memcpy (descr-> data, data_block, data_size);
return (descr);
}
#include "sflmem.h"
MEMTRN *
mem_new_trans_(
const char *filename, /* Name of source file making call */
word lineno /* Line number in calling source */
)
Allocates a transaction block. Use the mem new trans() macro to call this function. Use mem_commit or mem rollback() to delete the transaction. Returns a pointer to the allocated transaction block, or NULL if there was not enough memory available. The supplied source file name is assumed to be in a static area.
{
MEMTRN
*trn; /* Allocated transaction block */
/* Allocate block */
trn = malloc (MEMTRN_SIZE);
if (trn == NULL)
return (NULL);
# if (defined (MEM_TRACE))
if (filename)
trace ("%s (%d): new transaction", filename, lineno);
# endif
trn-> file = (char *) filename; /* Who allocated it */
trn-> line = lineno; /* and where */
list_reset (&trn-> memhdr); /* No memory blocks yet */
list_reset (trn); /* Only 1 item in list */
list_relink_before (trn, &tr_list); /* Add to list of transactions */
return (trn); /* and return address */
}
#include "sflmem.h"
void
mem_commit_ (
MEMTRN *trn,
const char *filename, /* Name of source file making call */
word lineno /* Line number in calling source */
)
Commits all blocks allocated to a transaction.
{
LIST
*ptr;
# if (defined (MEM_TRACE))
if (filename)
trace ("%s (%d): commit transaction", filename, lineno);
# endif
ptr = &trn-> memhdr;
if (!list_empty (ptr)) /* Are there any blocks to commit? */
{
list_relink_before (ptr, /* Relink list into main list */
&mem_list. memhdr);
list unlink (ptr);
}
mem_del_trans (trn);
}
#include "sflmem.h"
void
mem_rollback_ (
MEMTRN *trn,
const char *filename, /* Name of source file making call */
word lineno /* Line number in calling source */
)
Rolls back allocations for a particular transaction. This frees up all blocks allocated by calls to mem_alloc, mem_realloc and mem_strdup since the last call to mem_commit. Note that for blocks allocated with mem_realloc, this is not really a rollback but a free. The mem rollback() function must be used with some care... if you forget to do a mem commit(), a later mem rollback() will do damage to your memory space. The general rule is to start your processing with mem commit(), then do work, and call mem rollback() when there is an error. Finally, call mem commit() at the end, just to be sure.
{
MEMHDR
*ptr = NULL;
# if (defined (MEM_TRACE))
if (filename)
trace ("%s (%d): rollback transaction", filename, lineno);
# endif
while (!list_empty (&trn-> memhdr))
{
ptr = trn-> memhdr. next;
ptr-> tag = (word) ~MEMTAG;
mem_total -= ptr-> size;
mem_free_count += 1;
list unlink (ptr); /* Remove block from list */
free (ptr);
}
mem_del_trans (trn);
}
#include "sflmem.h"
size_t
mem_size_ (
const void *client_ptr, /* Block of memory to free */
const char *filename, /* Name of source file making call */
word lineno /* Line number in calling source */
)
Returns the size in bytes of a memory block.
{
MEMHDR
*ptr;
if (client_ptr == NULL) /* Do nothing if address is null */
return 0;
/* Check for valid block */
ptr = CLIENT_2_HDR (client_ptr);
if (ptr-> tag != MEMTAG)
mem_tag_err (ptr, filename, lineno);
return ptr-> size;
}
#include "sflmem.h" long mem_used (void)
Returns the number of bytes currently allocated using the memory management system. The value returned is simply the sum of the size requests to allocation routines. It does not reflect any overhead required by the memory management system.
{
return (mem_total);
}
#include "sflmem.h" long mem_allocs (void)
Returns the number of blocks allocated in total. Use this to get an idea of the activity of the memory management system. When program ends cleanly, mem allocs () should be equal to mem frees().
{
return (mem_alloc_count);
}
#include "sflmem.h" long mem_frees (void)
Returns the number of blocks freed in total. Use this to get an idea of the activity of the memory management system. When program ends cleanly, mem allocs () should be equal to mem frees().
{
return (mem_free_count);
}
#include "sflmem.h"
void
mem_display (
FILE *fp /* File to dump display to */
)
Displays the contents of the memory allocation list.
{
MEMTRN
*trn;
fprintf (fp, "Index Size File(Line) - total size %lu\n", mem_total);
mem_display_list ((MEMHDR *) &mem_list.memhdr, fp);
trn = tr_list.next;
while (trn != (MEMTRN *) &tr_list)
{
fprintf (fp, "* Transaction %s (%d)",
trn-> file? trn-> file: "<Unknown>", trn-> line);
fprintf (fp, "\n");
mem_display_list ((MEMHDR *) &trn-> memhdr, fp);
trn = trn-> next;
}
fflush (fp);
}
#include "sflmem.h"
int
mem_scavenger (
scavenger scav_fct, /* File to dump display to */
void * scav_arg
)
Registers a memory scavenger function. A memory scavenger function is an application function that is invoked by mem alloc () when memory is exhausted, so that unused application objects can be released. This allows you to allocate large amounts of memory -- for instance for caches -- and then release them when memory runs short. When you register a scavenger function you may provide a void * argument; this is passed back to the scavenger if it is ever invoked. The scavenger function returns TRUE if it could release some memory, otherwise it returns FALSE. Note that there is no way to unregister such a function. Furthermore, a scavenger function should not itself allocate any new memory, unless it can definitely free excess memory first. Scavenger functions are called in an unspecified order. Returns 0 if the scavenger function could be registered, -1 if not. There is no limit to the number of scavenger functions you can register, except available memory. The same scavenger function can be registered several times.
{
SCAVFCT
*scavfct; /* Allocated registry function */
/* Allocate an SCAVFCT block and attach it to the scavfcts list */
list_create (scavfct, sizeof (SCAVFCT));
if (scavfct == NULL)
return (-1);
list_relink_before (scavfct, &scavfcts);
scavfct-> scav_fct = scav_fct;
scavfct-> scav_arg = scav_arg;
return (0);
}
Filename: sflmime.h
Package: Standard Function Library (SFL)
Written: 96/03/28 iMatix SFL project team sfl@imatix.com
Revised: 97/09/08
Copyright: Copyright (c) 1991-98 iMatix
Provides various functions that support MIME encoding and decoding. See RFC 1521 for details.
Extract from RFC1521 for Base64 Content-Transfer-Encoding
---------------------------------------------------------
The Base64 Content-Transfer-Encoding is designed to represent
arbitrary sequences of octets in a form that need not be humanly
readable. The encoding and decoding algorithms are simple, but the
encoded data are consistently only about 33 percent larger than the
unencoded data. This encoding is virtually identical to the one used
in Privacy Enhanced Mail (PEM) applications, as defined in RFC 1421.
The base64 encoding is adapted from RFC 1421, with one change: base64
eliminates the "*" mechanism for embedded clear text.
A 65-character subset of US-ASCII is used, enabling 6 bits to be
represented per printable character. (The extra 65th character, "=",
is used to signify a special processing function.)
\NOTE: This subset has the important property that it is
represented identically in all versions of ISO 646, including US
ASCII, and all characters in the subset are also represented
identically in all versions of EBCDIC. Other popular encodings,
such as the encoding used by the uuencode utility and the base85
encoding specified as part of Level 2 PostScript, do not share
these properties, and thus do not fulfill the portability
requirements a binary transport encoding for mail must meet.
The encoding process represents 24-bit groups of input bits as output
strings of 4 encoded characters. Proceeding from left to right, a
24-bit input group is formed by concatenating 3 8-bit input groups.
These 24 bits are then treated as 4 concatenated 6-bit groups, each
of which is translated into a single digit in the base64 alphabet.
When encoding a bit stream via the base64 encoding, the bit stream
must be presumed to be ordered with the most-significant-bit first.
That is, the first bit in the stream will be the high-order bit in
the first byte, and the eighth bit will be the low-order bit in the
first byte, and so on.
Each 6-bit group is used as an index into an array of 64 printable
characters. The character referenced by the index is placed in the
output string. These characters, identified in Table 1, below, are
selected so as to be universally representable, and the set excludes
characters with particular significance to SMTP (e.g., ".", CR, LF)
and to the encapsulation boundaries defined in this document (e.g.,
"-").
Table 1: The Base64 Alphabet
Value Encoding Value Encoding Value Encoding Value Encoding
0 A 17 R 34 i 51 z
1 B 18 S 35 j 52 0
2 C 19 T 36 k 53 1
3 D 20 U 37 l 54 2
4 E 21 V 38 m 55 3
5 F 22 W 39 n 56 4
6 G 23 X 40 o 57 5
7 H 24 Y 41 p 58 6
8 I 25 Z 42 q 59 7
9 J 26 a 43 r 60 8
10 K 27 b 44 s 61 9
11 L 28 c 45 t 62 +
12 M 29 d 46 u 63 /
13 N 30 e 47 v
14 O 31 f 48 w (pad) =
15 P 32 g 49 x
16 Q 33 h 50 y
The output stream (encoded bytes) must be represented in lines of no
more than 76 characters each. All line breaks or other characters
not found in Table 1 must be ignored by decoding software. In base64
data, characters other than those in Table 1, line breaks, and other
white space probably indicate a transmission error, about which a
warning message or even a message rejection might be appropriate
under some circumstances.
Special processing is performed if fewer than 24 bits are available
at the end of the data being encoded. A full encoding quantum is
always completed at the end of a body. When fewer than 24 input bits
are available in an input group, zero bits are added (on the right)
to form an integral number of 6-bit groups. Padding at the end of
the data is performed using the '=' character. Since all base64
input is an integral number of octets, only the following cases can
\arise: (1) the final quantum of encoding input is an integral
multiple of 24 bits; here, the final unit of encoded output will be
an integral multiple of 4 characters with no "=" padding, (2) the
final quantum of encoding input is exactly 8 bits; here, the final
unit of encoded output will be two characters followed by two "="
padding characters, or (3) the final quantum of encoding input is
exactly 16 bits; here, the final unit of encoded output will be three
characters followed by one "=" padding character.
Because it is used only for padding at the end of the data, the
occurrence of any '=' characters may be taken as evidence that the
end of the data has been reached (without truncation in transit). No
such assurance is possible, however, when the number of octets
transmitted was a multiple of three.
Any characters outside of the base64 alphabet are to be ignored in
base64-encoded data. The same applies to any illegal sequence of
characters in the base64 encoding, such as "====="
Care must be taken to use the proper octets for line breaks if base64
encoding is applied directly to text material that has not been
converted to canonical form. In particular, text line breaks must be
converted into CRLF sequences prior to base64 encoding. The important
thing to note is that this may be done directly by the encoder rather
than in a prior canonicalization step in some implementations.
\NOTE: There is no need to worry about quoting apparent
encapsulation boundaries within base64-encoded parts of multipart
entities because no hyphen characters are used in the base64
encoding.
sflmime.h defines these symbols, possibly conditionally:
| Symbol: | Defined as: |
|---|---|
| _SFLMIME_INCLUDED | TRUE |
#include "sflmime.h" size_t encode_base64 (const char *source, byte *target, size_t source_size)
Encodes a source buffer in Base 64 and stores the result in the target buffer. The target buffer must be at least 1/3rd longer than the amount of data in the source buffer. The base64 data consists of portable printable characters as defined in RFC 1521. Returns the number of bytes output into the target buffer.
{
size_t
target_size = 0; /* Length of target buffer */
int
nb_block; /* Total number of blocks */
const char
*p_source; /* Pointer to source buffer */
byte
*p_target, /* Pointer to target buffer */
value; /* Value of Base64 byte */
ASSERT (source);
ASSERT (target);
if (source_size == 0)
return (0);
if (!tables_initialised)
init_conversion_tables ();
/* Bit positions
| byte 1 | byte 2 | byte 3 |
source block 87654321 87654321 87654321 -> 3 bytes of 8 bits
| byte 1 | byte 2 | byte 3 | byte 4 |
Encoded block 876543 218765 432187 654321 -> 4 bytes of 6 bits
*/
nb_block = (int) (source_size / 3);
/* Check if we have a partially-filled block */
if (nb_block * 3 != (int) source_size)
nb_block++;
target_size = (size_t) nb_block * 4;
target [target_size] = '\0';
p_source = source; /* Point to start of buffers */
p_target = target;
while (nb_block--)
{
/* Byte 1 */
value = *p_source >> 2;
*p_target++ = base64_to_char [value];
/* Byte 2 */
value = (*p_source++ & 0x03) << 4;
if ((size_t) (p_source - source) < source_size)
value |= (*p_source & 0xF0) >> 4;
*p_target++ = base64_to_char [value];
/* Byte 3 - pad the buffer with '=' if block not completed */
if ((size_t) (p_source - source) < source_size)
{
value = (*p_source++ & 0x0F) << 2;
if ((size_t) (p_source - source) < source_size)
value |= (*p_source & 0xC0) >> 6;
*p_target++ = base64_to_char [value];
}
else
*p_target++ = '=';
/* Byte 4 - pad the buffer with '=' if block not completed */
if ((size_t) (p_source - source) < source_size)
{
value = *p_source++ & 0x3F;
*p_target++ = base64_to_char [value];
}
else
*p_target++ = '=';
}
return (target_size);
}
#include "sflmime.h" size_t decode_base64 (const byte *source, char *target, size_t source_size)
Decodes a block of Base 64 data and stores the resulting binary data in a target buffer. The target buffer must be at least 3/4 the size of the base 64 data. Returns the number of characters output into the target buffer.
{
size_t
target_size = 0; /* Length of target buffer */
int
nb_block; /* Total number of block */
const byte
*p_source; /* Pointer in source buffer */
byte
value; /* Value of Base64 byte */
char
*p_target; /* Pointer in target buffer */
ASSERT (source);
ASSERT (target);
if (source_size == 0)
return (0);
if (!tables_initialised)
init_conversion_tables ();
/* Bit positions
| byte 1 | byte 2 | byte 3 | byte 4 |
Encoded block 654321 654321 654321 654321 -> 4 bytes of 6 bits
| byte 1 | byte 2 | byte 3 |
Decoded block 65432165 43216543 21654321 -> 3 bytes of 8 bits
*/
nb_block = source_size / 4;
target_size = (size_t) nb_block * 3;
target [target_size] = '\0';
p_source = source; /* Point to start of buffers */
p_target = target;
while (nb_block--)
{
/* Byte 1 */
*p_target = char_to_base64 [(byte) *p_source++] << 2;
value = char_to_base64 [(byte) *p_source++];
*p_target++ += ((value & 0x30) >> 4);
/* Byte 2 */
*p_target = ((value & 0x0F) << 4);
value = char_to_base64 [(byte) *p_source++];
*p_target++ += ((value & 0x3C) >> 2);
/* Byte 3 */
*p_target = (value & 0x03) << 6;
value = char_to_base64 [(byte) *p_source++];
*p_target++ += value;
}
return (target_size);
}
#include "sflmime.h" Bool decode_mime_time (const char *mime_string, long *date, long *time)
Takes a MIME date and time string in various formats and converts to a date and time (both long values). Returns TRUE if it could convert the date and time okay, else returns FALSE. Accepts these formats:
| Mon Jan 12 12:05:01 1995 | ctime format |
| Monday, 12- Jan-95 12:05:01 GMT | RFC 850 |
| Mon, 12 Jan 1995 12:05:01 GMT | RFC 1123 |
{
int
cent = 0,
year = 0,
month = 0,
day = 0,
hour = 0,
min = 0,
sec = 0;
static char
month_name [20],
buffer [50],
*p_char;
ASSERT (mime_string);
ASSERT (date);
ASSERT (time);
/* Whatever format we're looking at, it will start with weekday. */
/* Skip to first space. */
if (!(p_char = strchr (mime_string, ' ')))
return FALSE;
else
while (isspace (*p_char))
++p_char;
if (isalpha (*p_char))
{
/* ctime */
sscanf (p_char, "%s %d %d:%d:%d %d",
month_name, &day, &hour, &min, &sec, &year);
cent = (int) year / 100;
year -= cent * 100;
}
else
if (p_char [2] == '-')
{
/* RFC 850 */
sscanf (p_char, "%s %d:%d:%d",
buffer, &hour, &min, &sec);
buffer [2] = '\0';
day = atoi (buffer);
buffer [6] = '\0';
strcpy (month_name, &buffer [3]);
year = atoi (&buffer [7]);
/* Prevent wraparound from ambiguity */
if (year < 70)
cent = 20;
else
cent = 19;
}
else
{
/* RFC 1123 */
sscanf (p_char, "%d %s %d %d:%d:%d",
&day, month_name, &year, &hour, &min, &sec);
cent = (int) year / 100;
year -= cent * 100;
}
month = find_month (month_name);
*date = MAKE_DATE (cent, year, month, day);
*time = MAKE_TIME (hour, min, sec, 0 );
gmt to local (*date, *time, date, time);
return (TRUE);
}
#include "sflmime.h" char * encode_mime_time (long date, long time)
Encode date and time (in long format) in Mime RFC1123 date format, e.g. Mon, 12 Jan 1995 12:05:01 GMT. The supplied date and time are in local time. Returns the date/time string if the date was legal, else returns "?".
{
int
day_week, /* Day of week number (0 is sunday) */
month; /* Month number */
static char
buffer [LINE_MAX];
local to gmt (date, time, &date, &time);
day_week = day of week (date);
month = GET_MONTH (date);
if (day_week >= 0 && day_week < 7 && month > 0 && month < 13)
{
sprintf (buffer, "%s, %02d %s %04d %02d:%02d:%02d GMT",
days [day_week],
GET_DAY (date),
months [month - 1],
GET_CCYEAR (date),
GET_HOUR (time),
GET_MINUTE (time),
GET_SECOND (time)
);
return (buffer);
}
else
return ("?");
}
Filename: sflnode.h
Package: Standard Function Library (SFL)
Written: 96/06/03 iMatix SFL project team sfl@imatix.com
Revised: 97/09/08
Copyright: Copyright (c) 1991-98 iMatix
Provides functions to maintain doubly-linked lists. You can use these functions to work with lists of any structure. To make this work, all structures must start with two pointers, "void *next, *prev;". When you want to attach a linked-list to another structure, declare the list head as a NODE. You can then refer to this variable when you attach items to the list head. The code sets the global node_unsafe to TRUE whenever it is changing a list. NOTE: DEPRECATED IN FAVOUR OF SFLLIST.C.
sflnode.h defines these symbols, possibly conditionally:
| Symbol: | Defined as: |
|---|---|
| _SFLNODE_INCLUDED | TRUE |
| node_reset(node) | (node)-> prev = (node)-> next = (node) |
#include "sflnode.h"
void *
node_create (
void *after,
size_t size)
Creates a new node with the specified size, and attaches it to the linked list after the specified node. Initialises all fields in the node (except the main list pointers) to binary zeroes. If the 'after' argument is null, initialises but does not attach the node. Returns a pointer to the newly-created node, or NULL if there was not enough memory.
typedef struct {
void *prev, *next;
long data;
} BLOCK;
NODE head;
BLOCK *pointer;
// Initialise head of list
node_reset (&head);
// Attach new block to start of list
pointer = (BLOCK *) node_create (&head, sizeof (BLOCK));
pointer-> data = 1;
// Attach new block to end of list
pointer = (BLOCK *) node_create (head.prev, sizeof (BLOCK));
pointer-> data = 1000;
{
NODE
*node; /* Allocated node */
ASSERT (size > 0);
if ((node = mem_alloc (size)) != NULL)
{
memset (node, 0, size);
node_reset (node); /* Initialise node pointers */
if (after) /* Link into list if required */
node relink after (node, after);
}
return (node);
}
#include "sflnode.h"
void
node_destroy (
void *node)
Unlinks the specified node from any list it may be in, and frees its memory.
{
ASSERT (node);
node unlink (node);
mem_free (node);
}
#include "sflnode.h"
void *
node_relink_after (
void *node,
void *after)
Links a node into a doubly-linked list after a point in the list. Generally a linked list is attached to a 'head': an empty list consists of just the head node. To attach a node to the start of the list, link after the head. To attach a node to the end of the list, link before the head using node relink before(). In this way you can build doubly- ended queues, fifo queue, lists, etc. Returns the address of the node.
{
return (node relink (after, node, ((NODE *) after)-> next));
}
#include "sflnode.h"
void *
node_relink_before (
void *node,
void *before)
Links a node into a doubly-linked list before a point in the list. To link a node to the end of a doubly-linked list, link it before the list header node.
{
return (node relink (((NODE *) before)-> prev, node, before));
}
#include "sflnode.h"
void *
node_unlink (
void *node)
Unlinks the node from any list it may be in. Returns node.
{
return (node relink (((NODE *) node)-> prev, node,
((NODE *) node)-> next));
}
#include "sflnode.h"
void *
node_relink (
void *left,
void *node,
void *right)
Links the node into a linked list. This is a general-purpose function that can be used to attach and remove nodes anywhere in a list. Sets the global variable 'node_unsafe' while the list is being changed. Returns the address of node.
{
NODE *swap;
node_unsafe = TRUE;
swap = ((NODE *) left)-> next; /* Exchange left pointers */
((NODE *) left)-> next = ((NODE *) node)-> next;
((NODE *) node)-> next = swap;
swap = ((NODE *) right)-> prev; /* Exchange right pointers */
((NODE *) right)-> prev = ((NODE *) node)-> prev;
((NODE *) node)-> prev = swap;
node_unsafe = FALSE;
return (node);
}
Filename: sfldir.h
Package: Standard Function Library (SFL)
Written: 96/04/02 iMatix SFL project team sfl@imatix.com
Revised: 98/06/27
Copyright: Copyright (c) 1991-98 iMatix
The directory access functions provide a portable interface to the system's file directory structure. In general these functions are modelled around the UNIX opendir and readdir functions, but they are also similar to the DOS interface. These functions can fail on SVr4 if the <dirent.h> file does not match the C library. Recompile with the switch -D _USE_BSD_DIRENT and they should work a bit better. Tested on: MS-DOS (Turbo-C), Windows (MSVC 4.0), UNIX (Linux, IBM AIX, SunOS). OS/2 port was done by Ewen McNeill ewen@naos.co.nz. DJGPP and DRDOS LFN by Rob Judd judd@alphalink.com.au. Changes for Win32 by Will Menninger willus@netcom.com.
sfldir.h defines these symbols, possibly conditionally:
| Symbol: | Defined as: |
|---|---|
| ATTR_HIDDEN | 0x02 /* Hidden file */ |
| ATTR_MASK | 0x17 /* All bits together */ |
| ATTR_RDONLY | 0x01 /* Read only file */ |
| ATTR_SUBDIR | 0x10 /* Subdirectory */ |
| ATTR_SYSTEM | 0x04 /* System file */ |
| DEFAULT_DIR | (various) |
| Dirent | dirent /* We'll always refer to Dirent */ |
| GID_CACHE_MAX | 10 /* Max. different gid's we cache */ |
| MAXNAMLEN | (various) |
| NAME_MAX | MAXNAMLEN |
| UID_CACHE_MAX | 10 /* Max. different uid's we cache */ |
| _SFLDIR_INCLUDED | TRUE |
| stat | _stat |
| Type name: | Defined as: |
|---|---|
| mode_t | unsigned short |
| nlink_t | unsigned short |
| off_t | long |
#include "sfldir.h"
Bool
open_dir (
DIRST *dir,
const char *dir_name)
Creates a directory stream and returns the first entry in the directory. The order of entries is arbitrary, and it is undefined whether you will get entries like '.' and '..' or not. Returns TRUE if something was found, else FALSE. If TRUE, fills-in the values in the directory stream block. Use the same directory stream block for the read_dir and close dir() functions. You must supply a DIRST block when calling open dir(). If you do not supply a dir_name (i.e. it is NULL or ""), open dir() assumes you want to read from the current working directory. The strings in DIRST all point to static areas that may change after a further call to read_dir. If you need persistent data (i.e. because you want to collect a set of DIRSTs and then sort them, call fix dir() after each call to open_dir and read_dir. You should then call free dir() to release each DIRST when you are finished.
{
static char /* The name of the directory that */
dir_spec [LINE_MAX]; /* we are searching through */
char
*dir_spec_end; /* Points to NULL in dir_spec */
ASSERT (dir != NULL);
memset (dir, 0, sizeof (DIRST));
/* Copy and prepare the directory specification */
if (dir_name == NULL || *dir_name == 0)
strcpy (dir_spec, DEFAULT_DIR);
else
strcpy (dir_spec, dir_name);
#if (defined (MSDOS_FILESYSTEM))
strconvch (dir_spec, '/', '\\');
#endif
/* Remove a trailing slash from the directory name */
dir_spec_end = dir_spec + strlen (dir_spec);
if (dir_spec_end [-1] == PATHEND)
{
dir_spec_end [-1] = '\0';
dir_spec_end--;
}
/* Open directory stream or find first directory entry */
#if (defined (__UNIX__) || defined (__VMS_XOPEN) || defined (__OS2__))
if (strnull (dir_spec))
strcpy (dir_spec, "/");
if ((dir-> _dir_handle = opendir (dir_spec)) == NULL)
#elif (defined (WIN32))
strcat (dir_spec, "\\*");
if ((dir-> _dir_handle = FindFirstFile (dir_spec, &dir-> _dir_entry))
== INVALID_HANDLE_VALUE)
#elif (defined (_MSC_VER))
strcat (dir_spec, "\\*.*");
if ((dir-> _dir_handle = _dos_findfirst (dir_spec, _A_NORMAL | _A_SUBDIR,
&dir-> _dir_entry)) != 0)
#elif (defined (__TURBOC__) || defined (__DJGPP__))
strcat (dir_spec, "\\*.*");
if (findfirst (dir_spec, &dir-> _dir_entry, 255 - FA_LABEL) == -1)
#endif
return (FALSE); /* Could not open directory */
/* Save the directory name in directory stream structure */
#if (defined (__MSDOS__))
*dir_spec_end = '\0'; /* Kill the \*.* again */
#endif
dir-> dir_name = dir_spec;
#if (defined (__UNIX__) || defined (__VMS_XOPEN) || defined (__OS2__))
/* Under UNIX & VMS we still need to fetch the first file entry */
return (read dir (dir));
#elif (defined (WIN32))
/* Under Win32 we have read an entry, so return those values */
return (populate_entry (dir));
#elif (defined (_MSC_VER))
/* Under MSC we have read an entry, so return those values */
return (populate_entry (dir));
#elif (defined (__TURBOC__) || defined (__DJGPP__))
/* Under Borland C we have read an entry, so return those values */
return (populate_entry (dir));
#else
return (FALSE); /* Directory access not supported */
#endif
}
#include "sfldir.h"
Bool
read_dir (
DIRST *dir)
Reads the next entry from the directory stream. Returns TRUE if there was more data to read; returns FALSE if there was nothing more to read. Updates the fields in the directory structure when it returns TRUE. The strings in DIRST all point to static areas that may change after a further call to read_dir. If you need persistent data (i.e. because you want to collect a set of DIRSTs and then sort them, call fix dir() after each call to open_dir and read_dir. You should then call free dir() to release each DIRST when you are finished.
{
ASSERT (dir != NULL);
#if (defined (__UNIX__) || defined (__VMS_XOPEN) || defined (__OS2__))
if ((dir-> _dir_entry =
(struct Dirent *) readdir (dir-> _dir_handle)) != NULL)
return (populate_entry (dir));
else
#elif (defined (WIN32))
if (FindNextFile (dir->_dir_handle, &dir->_dir_entry))
return (populate_entry (dir));
else
#elif (defined (_MSC_VER))
if (_dos_findnext (&dir-> _dir_entry) == 0)
return (populate_entry (dir));
else
#elif (defined (__TURBOC__) || defined (__DJGPP__))
if (findnext (&dir-> _dir_entry) == 0)
return (populate_entry (dir));
else
#endif
return (FALSE);
}
#include "sfldir.h"
Bool
close_dir (
DIRST *dir)
Close the directory stream, and free any allocated memory. You should call this function when you are done reading a directory, or you will get memory leaks. Returns TRUE if okay, FALSE if there was an error.
{
Bool
rc;
ASSERT (dir != NULL);
#if (defined (__UNIX__) || defined (__VMS_XOPEN) || defined (__OS2__))
rc = (closedir (dir-> _dir_handle) == 0);
#elif (defined (WIN32))
rc = FindClose (dir->_dir_handle);
#elif (defined (_MSC_VER))
rc = TRUE; /* No function to close a dir */
#elif (defined (__TURBOC__) || defined (__DJGPP__))
rc = TRUE; /* No function to close a dir */
#else
rc = FALSE; /* Directory access not supported */
#endif
return (rc);
}
#include "sfldir.h"
char *
format_dir (
DIRST *dir,
Bool full)
Formats the directory entry information using the same conventions as the UNIX 'ls -l' command. Returns a static buffer that contains the the formatted string. If the full argument is TRUE, adds cosmetic hints to indicate the file type; for instance '/' if the file is a directory, '*' if it is executable.
{
static char
buffer [LINE_MAX]; /* Formatted directory entry */
ASSERT (dir != NULL);
sprintf (buffer, "%s %3d %-8.8s %-8.8s %8ld %s %s",
format_mode (dir),
dir-> file_nlink,
dir-> owner,
dir-> group,
(long) dir-> file_size,
format_time (dir),
format_name (dir, full)
);
return (buffer);
}
#include "sfldir.h" int fix_dir (DIRST *dir)
Converts all strings in the DIRST into permenant values, by allocating heap memory for each string. You must call this function if you intend to keep a set of DIRSTs, for searching or sorting. You do not need to call fix dir() if you handle each call to read dir() independently. If you use fix dir(), you must call free dir() for each DIRST when you terminate. Returns 0 if okay, -1 if there was insufficient memory or another fatal error.
{
char
*dir_name,
*owner,
*group,
*file_name;
/* Allocate each string */
dir_name = mem_strdup (dir-> dir_name);
owner = mem_strdup (dir-> owner);
group = mem_strdup (dir-> group);
file_name = mem_strdup (dir-> file_name);
/* If all okay, assign new strings and indicate everything okay */
if (dir_name && owner && group && file_name)
{
dir-> dir_name = dir_name;
dir-> owner = owner;
dir-> group = group;
dir-> file_name = file_name;
dir-> _fixed = TRUE;
return (0);
}
else
{
/* Otherwise patch things back the way they were */
if (dir_name)
mem_free (dir_name);
if (owner)
mem_free (owner);
if (group)
mem_free (group);
if (file_name)
mem_free (file_name);
return (-1);
}
}
#include "sfldir.h" int free_dir (DIRST *dir)
Frees all strings used in the DIRST. You should call this function to free space allocated by fix dir(). If you try to call free dir() for a DIRST that was not fixed, you will get an error feedback, and (if you compiled with DEBUG defined) an assertion fault. Returns 0 if okay, -1 if there was an error. After a call to free dir(), do not try to access the strings in DIRST; they are all set to point to an empty string.
{
static char
*empty = "";
ASSERT (dir-> _fixed);
if (dir-> _fixed)
{
/* Free allocated strings */
mem_free (dir-> dir_name);
mem_free (dir-> owner);
mem_free (dir-> group);
mem_free (dir-> file_name);
/* Now point the strings to an empty string */
dir-> dir_name = empty;
dir-> owner = empty;
dir-> group = empty;
dir-> file_name = empty;
/* And mark the DIRST as no longer 'fixed' */
dir-> _fixed = FALSE;
return (0);
}
else
return (-1);
}
#include "sfldir.h"
NODE *
load_dir_list (
const char *dir_name,
const char *sort)
Loads and sorts the contents of a directory. Returns a NODE pointer to a linked list containing the directory entries. Each node is a FILEINFO structure (mapped onto a NODE structure for purposes of manipulating the linked list). You can ask for the directory list to be sorted in various ways; in this case subdirectory entries are always sorted first. To specify the sort order you pass a string consisting of one or more of these characters, which are then used in order:
| n | Sort by ascending name. |
| N | Sort by descending name. |
| x | Sort by ascending extension. |
| X | Sort by descending extension. |
| t | Sort by ascending time and date. |
| T | Sort by descending time and date. |
| s | Sort by ascending size. |
| S | Sort by descending size. |
{
NODE
*file_list; /* File list head */
DIRST
dir;
Bool
rc;
int
nbr_files = 0;
file_list = mem_alloc (sizeof (NODE));
if (!file_list)
return (NULL);
node_reset (file_list); /* Initialise file list */
/* Load directory */
rc = open dir (&dir, dir_name);
while (rc)
{
add dir list (file_list, &dir);
nbr_files++;
rc = read dir (&dir);
}
close dir (&dir);
if (sort && nbr_files > 1)
sort dir list (file_list, sort);
return (file_list);
}
#include "sfldir.h" Bool free_dir_list (NODE *file_list)
Frees all FILELIST blocks in the specified linked list.
{
ASSERT (file_list);
while (file_list-> next != file_list)
{
free dir (&((FILEINFO *) file_list-> next)-> dir);
node destroy (file_list-> next);
}
mem_free (file_list);
return (TRUE);
}
#include "sfldir.h" void sort_dir_list (NODE *file_list, const char *sort)
Sorts the directory list as specified. Returns the number of items in the list. To specify the sort order you pass a string holding one or more of these characters, which are then used in order:
| n | Sort by ascending name. |
| N | Sort by descending name. |
| x | Sort by ascending extension. |
| X | Sort by descending extension. |
| t | Sort by ascending time and date. |
| T | Sort by descending time and date. |
| s | Sort by ascending size. |
| S | Sort by descending size. |
{
ASSERT (file_list);
sort_key = (char *) sort;
list sort (file_list, compare_dir);
}
#include "sfldir.h" FILEINFO * add_dir_list (NODE *file_list, const DIRST *dir)
Adds a node to the specified directory list. Returns the address of the new node, or NULL if there was insufficient memory.
{
FILEINFO
*file_info;
file_info = (FILEINFO *) node create (file_list-> prev, sizeof (FILEINFO));
if (file_info)
{
memcpy (&file_info-> dir, dir, sizeof (DIRST));
fix dir (&file_info-> dir);
file_info-> directory = (dir-> file_attrs & ATTR_SUBDIR) != 0;
}
return (file_info);
}
#include "sfldir.h"
char *
resolve_path (
const char *old_path)
Accepts a path consisting of zero or more directory names and optionally a filename plus extension. Removes '.' and '..' if they occur in the path. '..' is resolved by also removing the preceding directory name, if any. Returns the address of the resulting path, in a static area that is overwritten by each call. The returned path may be empty. Under OS/2 and MS-DOS, treats '\' and '/' both as directory separators. A '..' directory at the start of the path resolves into nothing. If the input path started with '/', the returned path also does, else it does not. For compatibility with DOS-based systems, '...' is treated as '../..', '....' as '../../..', and so on.
{
#if (defined (__UNIX__) || defined (MSDOS_FILESYSTEM) || defined (__VMS__))
static char
new_path [PATH_MAX]; /* Returned path value */
char
*new_ptr, /* Pointer into new_path */
last_char = '/'; /* Start of path counts as delim */
int
nbr_dots; /* Size of '..', '...' specifier */
ASSERT (old_path);
ASSERT (strlen (old_path) < PATH_MAX);
new_ptr = new_path;
while (*old_path)
{
if (path_delimiter (last_char) && *old_path == '.')
{
/* Handle one or more dots followed by a path delimiter */
nbr_dots = 0; /* Count number of dots */
while (old_path [nbr_dots] == '.')
nbr_dots++;
if (path_delimiter (old_path [nbr_dots]))
{
old_path += nbr_dots; /* Skip past dots */
if (*old_path)
old_path++; /* and past / if any */
/* Now backtrack in new path, dropping directories as */
/* many times as needed (0 or more times) */
while (nbr_dots > 1)
{
if (new_ptr > new_path + 1)
{
new_ptr--; /* Drop delimiter */
while (new_ptr > new_path)
{
if (path_delimiter (*(new_ptr - 1)))
break; /* and end after delimiter */
new_ptr--;
}
}
else
break; /* At start of name - finish */
nbr_dots--;
}
}
else
/* Handle '.something' */
last_char = *new_ptr++ = *old_path++;
}
else
last_char = *new_ptr++ = *old_path++;
}
*new_ptr = '\0'; /* Terminate string nicely */
return (new_path);
#else
return ((char *) old_path); /* Path resolution not supported */
#endif
}
#include "sfldir.h"
char *
locate_path (
const char *root,
const char *path)
Accepts a root directory and a path and locates the path with respect to the root. If the path looks like an absolute directory, returns the path after cleaning it up. Otherwise appends the path to the root, and returns the result. In any case, the resulting directory does not need to exist. Cleans-up the returned path by appending a '/' if necessary, and resolving any '..' subpaths. The returned value is held in a static string that is reused by each call to this function.
{
#if (defined (__UNIX__) || defined (MSDOS_FILESYSTEM) || defined (__VMS__))
static char
new_path [PATH_MAX]; /* Returned path value */
ASSERT (root);
ASSERT (path);
#if (defined (MSDOS_FILESYSTEM))
/* Under MSDOS, OS/2, or Windows we have a full path if we have any of:
* /directory
* D:/directory
* the variations of those with backslashes.
*/
if (path [0] == '\\' || path [0] == '/'
|| (isalpha (path [0]) && path [1] == ':'
&& (path [2] == '\\' || path [2] == '/')))
#else
/* Under UNIX or VMS we have a full path if the path starts
* with the directory marker
*/
if (path [0] == PATHEND)
#endif
strcpy (new_path, path); /* Use path as supplied */
else
{
strcpy (new_path, root); /* Build root/path */
if (!path_delimiter (strlast (new_path)))
strcat (new_path, "/");
strcat (new_path, path);
}
/* Append slash if necessary */
if (!path_delimiter (strlast (new_path)))
strcat (new_path, "/");
return (resolve path (new_path));
#else
return ((char *) path);
#endif
}
#include "sfldir.h" char * clean_p