| iMatix home page | << | < | > | >> |
SFL Version 1.91 |
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:
sflsrc18.tgz 304586 98/04/11 17:10:24 Gzip/tar archive sflsrc18.zip 372567 98/04/11 17:10:24 ZIP archive sflsrc19.tgz 308676 98/04/23 22:02:24 Gzip/tar archive sflsrc19.zip 378833 98/04/23 22:02:24 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:
sflbig18.tgz 155117 98/04/11 17:10:26 Gzip/tar archive sflbig18.zip 156189 98/04/11 17:10:26 ZIP archive sflbig19.tgz 184590 98/04/23 22:02:28 Gzip/tar archive sflbig19.zip 184251 98/04/23 22:02:28 ZIP archive sfldoc18.tgz 190286 98/04/11 17:10:26 Gzip/tar archive sfldoc18.zip 464103 98/04/11 17:10:24 ZIP archive sfldoc19.tgz 217199 98/04/23 22:02:26 Gzip/tar archive sfldoc19.zip 544018 98/04/23 22:02:26 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 sflsrc19.zip temp $ cd temp $ unzip -a sflsrc19
To decompress the source .tgz file you need GNU gzip/gunzip and tar:
$ mkdir temp $ mv sflsrc19.tgz temp $ cd temp $ gzip -d sflsrc19.tgz or $ gunzip sflsrc19.tgz $ tar -xvf sflsrc19.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 sflsrc19.zip [.temp] $ set def [.temp] $ unzip -a sflsrc19
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 sflsrc19.zip temp C:\DOWNLOAD> del sflsrc19.zip C:\DOWNLOAD> cd temp C:\DOWNLOAD> unzip sflsrc19
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/03/30
Copyright: Copyright (c) 1991-98 iMatix
Version: 1.90
1.90 PH | Released with SFL 1.90 |
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> |
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_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/04/20
Copyright: Copyright (c) 1991-98 iMatix
Defines the SFL_VERSION constant.
sflvers.h defines these symbols, possibly conditionally:
Symbol: | Defined as: |
---|---|
SFL_VERSION | "1.90" /* 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/04/13
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: |
---|---|
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); time_struct.tm_year = GET_YEAR (date); 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, optionally with a leading '-' character. GMT is represented as "0000"; Central European Time is "1000". If the system does not support the timezone, returns "0000".
{ static char formatted_string [6]; /* -nnnn plus null */ int minutes; /* TIMEZONE is in seconds */ minutes = (int) (TIMEZONE / 60); sprintf (formatted_string, "%02d%02d", 0 - minutes / 60, minutes % 60); return (formatted_string); }
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: 1996/04/24 iMatix SFL project team sfl@imatix.com
Revised: 1998/04/15 iMatix SFL project team sfl@imatix.com
Copyright: Copyright (c) 1991-1998 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 basic functions for searching blocks of memory with known sizes, plus envelopes that search null-delimited strings. Provides the option of repeatedly searching for the same pattern without re-parsing the pattern each time. 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. Use of strfind() and memfind() is discouraged (they are provided only for backwards compatibility).
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. Non-Reentrant. Static-Buffer. Depreciated (use strfind_r or strfind_rb).
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 */ const size_t block_size, /* Size of block in bytes */ const void *pattern, /* Pattern to search for */ const 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-Buffer. Depreciated (use memfind_r or memfind_rb).
{ 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 */ const size_t block_size, /* Size of block in bytes */ const void *pattern, /* Pattern to search for */ const 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 */ const size_t block_size, /* Size of block in bytes */ const void *in_pattern, /* Pattern to search for */ const 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. 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. Further improvements by HUME, A., and D. M. SUNDAY. 1991. "Fast String Searching." AT&T Bell Labs Computing Science Technical Report No. 156. Finally, implemented in C by P. Hintjens. 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 */ limit; const byte *match_ptr = NULL; 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_size - pattern_size + 1; ASSERT (limit > 0); for (byte_nbr = 0; byte_nbr < limit; byte_nbr += shift [block [byte_nbr + pattern_size]]) { ASSERT (byte_nbr >= 0 && byte_nbr < (block_size - pattern_size) + 1); /* If the first byte matches, compare rest of pattern */ if (block [byte_nbr] == *pattern) { match_ptr = block + byte_nbr + 1; match_size = 1; do { /* Loop invarients */ ASSERT (match_size > 0 && match_size <= pattern_size); ASSERT (match_ptr != NULL && match_ptr > block && match_ptr <= block+block_size); ASSERT (match_ptr == (block + byte_nbr + match_size)); /* If we found a match, return the start address */ if (match_size == pattern_size) return (void*)(block + byte_nbr); ASSERT (match_size < pattern_size && match_ptr < block+block_size); } while (*match_ptr++ == pattern [match_size++]); } ASSERT (byte_nbr + pattern_size <= block_size); } 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 first occurance of 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. Reentrant.
char *result; result = txtfind ("AbracaDabra", "cad"); if (result) puts (result);
{ int shift [256]; /* Shift distance for each value */ size_t string_size, pattern_size, byte_nbr, /* Distance through block */ match_size, /* Size of matched part */ limit; /* Last potiental match point */ const char *match_ptr = NULL; 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_size - pattern_size + 1; ASSERT (limit > 0); for (byte_nbr = 0; byte_nbr < limit; byte_nbr += shift [(byte) tolower (string [byte_nbr + pattern_size])]) { ASSERT (byte_nbr >= 0 && byte_nbr < (string_size - pattern_size) + 1); /* If the first byte matches, compare rest of pattern */ if (tolower (string [byte_nbr]) == tolower (*pattern)) { match_ptr = string + byte_nbr + 1; match_size = 1; do { /* Loop invarients */ ASSERT (match_size > 0 && match_size <= pattern_size); ASSERT (match_ptr != NULL && match_ptr > string && match_ptr <= string+string_size); ASSERT (match_ptr == (string + byte_nbr + match_size)); /* If all matched, return pointer to start of match */ if (match_size == pattern_size) return ((char *) string + byte_nbr); ASSERT (match_size < pattern_size && match_ptr < string+string_size); } while (tolower (*match_ptr++) == tolower (pattern [match_size++])); } ASSERT (byte_nbr + pattern_size <= string_size); } 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/03/21
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 |
#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" 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_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.
{ ASSERT (oldname); ASSERT (newname); return (rename (oldname, newname)); }
#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 = unlink (filename); if (rc && errno == EACCES) { /* Under WinNT and Win95, a file delete can sometimes fail with a * permission error which passes after a short delay. */ Sleep (200); rc = unlink (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 */ { build_next_path (full_name, pathptr, 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_time; /* 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_time = timer to time (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. */ if ((insert_at = strrchr (full_name, PATHEND)) == NULL) insert_at = full_name; else insert_at++; 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_time / 100)); 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 (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'.
{ ASSERT (filename); strcpy (work_name, clean path (filename)); #if (defined (__VMS__)) if (!file exists (work_name)) default extension (work_name, work_name, "dir"); #endif return ((file_mode (work_name) & S_IFDIR) != 0); }
#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.
{ struct stat stat_buf; ASSERT (filename); if (stat ((char *) filename, &stat_buf) == 0) return ((long) stat_buf.st_size); else return (-1); }
#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().
{ 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); }
#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 > 65000, only the first 65000 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.
{ DESCR *buffer; long file_size; int rc; FILE *file_stream; ASSERT (filename); file_size = get file size (filename); if (file_size == -1) return (NULL); else if (file_size > 65000L) file_size = 65000L; buffer = mem_descr (NULL, (word) file_size + 1); if (buffer == NULL) return (NULL); file_stream = fopen (filename, FOPEN_READ_BINARY); if (file_stream == NULL) { mem_free (buffer); return (NULL); } rc = fread (buffer-> data, (word) file_size, 1, file_stream); fclose (file_stream); if (rc != 1) { mem_free (buffer); return (NULL); } buffer-> data [(word) file_size] = '\0'; return (buffer); }
#include "sflfile.h" long 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" 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. Hyphens in keywords and section names are replaced by underlines.
{ 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; strconvch (*keyword, '-', '_'); 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) { strconvch (ini_keyword, '-', '_'); 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? */ 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 only symbols formatted as key:name */ if (sscanf (symbol-> name, "%[^:]:%s", ini_value, ini_keyword) == 2) { /* 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: 97/11/17
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)); }
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/02/27
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_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) |
#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); }
Filename: sflmail.h
Package: standard function library (sfl)
Written: 06/18/97 Scott Beasley (jscottb@infoave.com)
Revised: 98-03-17
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: |
---|---|
ENC(c) | ((c) ? ((c) & 077) + ' ': '`') |
_sflmail_included | TRUE |
smtp_send_data(sock,strout) | write_TCP((sock),(strout),strlen((strout))) |
#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. 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. NOTE: The sock_init function should be called before use of this function.
{ 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 TCP (strSmtpServer, "smtp"); if (getreply (iSocket) > 400 || iSocket < 1) { return -1; } /* Format a SMTP meassage header. */ /* Just say hello to the mail server. */ xstrcpy (strOut, "HELO ", strSmtpServer, "\n", NULL); smtp_send_data (iSocket, strOut); if (getreply (iSocket) > 400) return -2; /* Tell the mail server who the message is from. */ xstrcpy (strOut, "MAIL FROM:<", strSenderUserId, ">\n", NULL); smtp_send_data (iSocket, strOut); if (getreply (iSocket) > 400) return -3; strRcptUserIds = (char *) malloc (strlen (strDestUserIds) + strlen (strCcUserIds) + strlen (strBccUserIds) + 1); sprintf (strRcptUserIds, "%s;%s;%s", strDestUserIds, strCcUserIds, 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) > 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) > 400) return -5; /* The following shows all who it was sent to. */ replacechrswith (strDestUserIds, ";", ','); xstrcpy (strOut, "TO: ", strDestUserIds, "\r\n", NULL); /* Set up the Reply-To path. */ if (!strRetPathUserId || !*strRetPathUserId) { strRetPathUserId = strSenderUserId; } xstrcat (strOut, "Reply-To:<", strRetPathUserId, ">\r\n", NULL); smtp_send_data (iSocket, strOut); *strOut = '\0'; /* Post any CC's. */ if (strCcUserIds && *strCcUserIds) { replacechrswith (strCcUserIds, ";", ','); xstrcat (strOut, "Cc:", strCcUserIds, "\r\n", NULL ); } /* Post any BCC's. */ if (strBccUserIds && *strBccUserIds) { replacechrswith (strBccUserIds, ";", ','); xstrcat (strOut, "Bcc:", strBccUserIds, "\r\n", NULL); } /* Post any Return-Receipt-To. */ if (strRrcpUserId && *strRrcpUserId) { xstrcat (strOut, "Return-Receipt-To:", strRrcpUserId, ">\r\n", NULL); } if (strMailerName && *strMailerName) { xstrcat (strOut, "X-Mailer: ", 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: ", strMsgComment, "\r\n", NULL); /* Send the subject and message body. */ xstrcat (strOut, "Subject:", strSubject, "\n\r\n", NULL); /* Keep rfc822 in mind with all the sections. */ if (strMessageBody && *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"); xstrcat (strOut, "\r\n", strMessageBody, "\r\n", NULL); } smtp_send_data (iSocket, strOut); /* Include any Text type files and Attach them to the message. */ if (strTxtFiles && *strTxtFiles) { iCnt = 0; while (1) { getstrfld (strTxtFiles, iCnt++, 0, ",;", strFile); trim (strFile); if (*strFile) { fpin = fopen (strFile, "rb"); if (!fpin) { 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; } } /* UUencode any bin files and Attach them to the message. */ if (strBinFiles && *strBinFiles) { iCnt = 0; while (1) { getstrfld (strBinFiles, iCnt++, 0, ",;", strFile); trim (strFile); if (*strFile) { strcpy (strUUEFile, strFile); if (strchr (strUUEFile, '.')) *((strchr (strUUEFile, '.')))= (char)NULL; strcat (strUUEFile, ".uue"); uuencode (strFile, strUUEFile); 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=", 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); unlink (strUUEFile); } else break; } } /* This ends the message. */ smtp_send_data (iSocket, ".\r\n"); if (getreply (iSocket) > 400) return -7; /* Now log off the SMTP port. */ smtp_send_data (iSocket, "QUIT\n"); if (getreply (iSocket) > 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 "(unknown)" static int uuencode ( char *strIn, char *strOut)
Uuendcode a file, with the output going to a new file. This function is used by smtp_send_mail.
{ char strLine[46]; int iCnt, iLineLen; FILE *fpin, *fpout; if (!(fpin = fopen (strIn, "rb"))) { return 1; } if (!(fpout = fopen (strOut, "wb"))) { return 1; } fprintf (fpout, "begin 666 %s\n", getfilename (strIn)); while (1) { iLineLen = fread (strLine, sizeof (char), 45, fpin); if (iLineLen <= 0) break; fputc (ENC (iLineLen), fpout); for (iCnt = 0; iCnt < iLineLen; iCnt += 3) { putgroup (&strLine[iCnt], fpout); } fputc ('\n', fpout); } fprintf (fpout, "end\n"); fclose (fpin); fclose (fpout); return 0; }
#include "(unknown)" static void putgroup ( char *strgroup, FILE *fp)
Write out 3 char group to uuendcoded file making it printable This function is used by uuencode.
{ int ichr1, ichr2, ichr3, ichr4; ichr1 = *strgroup >> 2; ichr2 = (*strgroup << 4)& 0x030 | (strgroup[1] >> 4)& 0x00f; ichr3 = (strgroup[1] << 2)& 0x03c | (strgroup[2] >> 6)& 0x003; ichr4 = strgroup[2] & 0x03f; fputc (ENC (ichr1), fp); fputc (ENC (ichr2), fp); fputc (ENC (ichr3), fp); fputc (ENC (ichr4), fp); }
#include "(unknown)" static int getreply ( int iSocket)
Get a reply from the SMTP server and see thats it's not an error. This function is used by smtp_send_mail.
{ char strRetBuff[513]; *strRetBuff = 0; read TCP ((sock_t)iSocket, strRetBuff, 512); /* See if we have not gotten a responce back from the mail server. */ if (!*strRetBuff){ return 777; } /* Save off server reply. */ strcpy (strlast_smtp_message, strRetBuff); trim (strRetBuff); strRetBuff[3] = (char)0; return atoi (strRetBuff); }
#include "(unknown)" static char *getfilename ( char *strFullPath)
Get's the name from the full path of a file. This function is used by smtp_send_mail.
{ int iLen; char *strTmp; iLen = strlen (strFullPath); strTmp = (strFullPath + iLen); while (1) { if (*strTmp == '\\' || *strTmp == '/' || !iLen) break; strTmp--; iLen--; } if (*strTmp == '\\' || *strTmp == '/') strTmp++; return strTmp; }
#include "sflmail.h" char *get_last_smtp_message (void)
Get's the last message the smtp server submited.
{ return strlast_smtp_message; }
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: 97/09/08
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, which 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_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_size += 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_size -= 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_size += 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_size -= 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_size != 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_size -= ptr-> size; mem_free_count += 1; list unlink (ptr); /* Remove block from list */ free (ptr); } mem_del_trans (trn); }
#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_size); }
#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_size); 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 ); 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. Returns the string if it could encode the date and time okay, else returns "?".
{ int day_week, /* Day of week number (0 is sunday) */ month; /* Month number */ static char buffer [LINE_MAX]; 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/01/15
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.
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] == '/') { 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 (_MSC_VER) && defined (WIN32)) strcat (dir_spec, "\\*.*"); if ((dir-> _dir_handle = _findfirst (dir_spec, &dir-> _dir_entry)) == -1) #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__)) 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 (_MSC_VER)) /* Under MSC we have read an entry, so return those values */ return (populate_entry (dir)); #elif (defined (__TURBOC__)) /* 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 (_MSC_VER) && defined (WIN32)) if (_findnext (dir-> _dir_handle, &dir-> _dir_entry) == 0) return (populate_entry (dir)); else #elif (defined (_MSC_VER)) if (_dos_findnext (&dir-> _dir_entry) == 0) return (populate_entry (dir)); else #elif (defined (__TURBOC__)) 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 (_MSC_VER) && defined (WIN32)) rc = (_findclose (dir-> _dir_handle) == 0); #elif (defined (_MSC_VER)) rc = TRUE; /* No function to close a dir */ #elif (defined (__TURBOC__)) 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 */ FILEINFO *file_info; 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) { file_info = (FILEINFO *) node create (file_list-> prev, sizeof (FILEINFO)); if (file_info) /* If node allocated okay */ { memcpy (&file_info-> dir, &dir, sizeof (DIRST)); fix dir (&file_info-> dir); file_info-> directory = (dir.file_attrs & ATTR_SUBDIR) != 0; nbr_files++; } rc = read dir (&dir); } close dir (&dir); if (nbr_files > 1 && sort != NULL) sort_dir (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" 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_path ( const char *path)
Returns a clean directory name; i.e. resolves the path, removes a trailing slash unless the name consists just of '/'; on a MS- DOS file system, cleans-up a directory name consisting of a disk specifier. The cleaned-up directory path is in a static area that is overwritten by each call.
{ #if (defined (__UNIX__) || defined (MSDOS_FILESYSTEM) || defined (__VMS__)) static char new_path [PATH_MAX + 1]; /* Returned path value */ char *slash; strncpy (new_path, path, PATH_MAX); new_path [PATH_MAX] = '\0'; # if (defined (MSDOS_FILESYSTEM)) /* For DOS filesystems, use only back slashes */ strconvch (new_path, '/', '\\'); # endif slash = strrchr (new_path, PATHEND); /* Find last slash */ # if (defined (MSDOS_FILESYSTEM)) /* If slash is last character in string, maybe squash it */ if (slash && slash [1] == '\0') { if (slash > new_path && slash [-1] != ':') *slash = '\0'; } else /* Turn X: into X:\ */ if (new_path [1] == ':' && new_path [2] == '\0') { new_path [2] = '\\'; new_path [3] = '\0'; } # else /* If slash is last character in string, maybe squash it */ if (slash && slash [1] == '\0') if (slash > new_path) *slash = '\0'; # endif return (new_path); #else return ((char *) path); #endif }
#include "sfldir.h" char * get_curdir (void)
Returns a buffer containing the current working directory. This buffer is allocated using the mem_alloc() function and should be freed using mem_free() when no longer needed. Returns NULL if there was insufficient memory to allocate the buffer, or if the system does not provide the current working directory information. Under Windows, replaces backslash characters by the UNIX-like slash. Under OpenVMS, returns directory name in POSIX format.
{ static char curdir [PATH_MAX + 1]; /* String we get from the OS */ char *allocated; /* Re-allocated string */ #if (defined (__UNIX__) || defined (__OS2__)) getcwd (curdir, PATH_MAX); #elif (defined (__VMS__)) getcwd (curdir, PATH_MAX, 0); #elif (defined (MSDOS_FILESYSTEM)) getcwd (curdir, PATH_MAX); strconvch (curdir, '\\', '/'); #else strclr (curdir); #endif /* Reallocate buffer using mem_strdup, so that any memory leaks are */ /* correctly traced. */ allocated = mem_strdup (curdir); return (allocated); }
#include "sfldir.h" int set_curdir ( const char *path)
Sets the current working directory as specified. Returns 0 if the directory path was found; -1 if there was an error. Under Windos, replaces '/' by '\' before changing directory, and switches to the specified disk if the path starts with a letter and ':'. Does nothing if the path is NULL or empty.
{ int feedback = 0; #if (defined (__UNIX__) || defined (__VMS_XOPEN) || defined (__OS2__)) if (path && *path) feedback = chdir (path); #elif (defined (MSDOS_FILESYSTEM)) char *copy_path = mem_strdup (path); if (path == NULL || *path == '\0') return (0); /* Do nothing if path is empty */ /* MS-DOS compilers generally require a two-step process */ strconvch (copy_path, '/', '\\'); # if (defined (__TURBOC__)) feedback = chdir (copy_path); if (feedback == 0 && isalpha (path [0]) && path [1] == ':') setdisk (toupper (path [0]) - 'A'); # elif (defined (__LCC__)) feedback = chdir (copy_path); if (feedback == 0 && isalpha (path [0]) && path [1] == ':') chdrive (toupper (path [0]) - 'A' + 1); # elif (defined (__WINDOWS__)) feedback = _chdir (copy_path); if (feedback == 0 && isalpha (path [0]) && path [1] == ':') _chdrive (toupper (path [0]) - 'A' + 1); # endif mem_strfree (©_path); #else feedback = -1; #endif return (feedback); }
#include "sfldir.h" Bool file_matches ( const char *filename, const char *pattern)
Returns TRUE if the filename matches the pattern. The pattern is a character string that can contain these 'wildcard' characters:
{ char *pattern_ptr, /* Points to pattern */ *filename_ptr; /* Points to filename */ filename_ptr = (char *) filename; /* Start comparing file name */ pattern_ptr = (char *) pattern; /* Start comparing file name */ FOREVER { /* If we came to the end of the pattern and the filename, we have */ /* successful match. */ if (*pattern_ptr == '\0' && *filename_ptr == '\0') return (TRUE); /* Have a match */ /* Otherwise, end of either is a failed match */ if (*pattern_ptr == '\0' || *filename_ptr == '\0') return (FALSE); /* Match failed */ /* If the pattern character is '?', then we matched a char */ if (*pattern_ptr == '?' #if (defined (NAMEFOLD)) || toupper (*pattern_ptr) == toupper (*filename_ptr)) #else || *pattern_ptr == *filename_ptr) #endif { pattern_ptr++; filename_ptr++; } else /* If we have a '*', match as much of the filename as we can */ if (*pattern_ptr == '*') { pattern_ptr++; /* Try to match following char */ while (*filename_ptr && *filename_ptr != *pattern_ptr) filename_ptr++; } else return (FALSE); /* Match failed */ } }
#include "sfldir.h" int make_dir ( const char *path_to_create)
Create a new directory. Returns 0 if the directory was created; -1 if there was an error. Under Windows and OpenVMS, accepts directory names with '/'. Will create multiple levels of directory if required.
{ char *path, *slash; int rc = 0; path = mem_strdup (path_to_create); /* Working copy */ #if (defined (MSDOS_FILESYSTEM)) strconvch (path, '/', '\\'); #endif /* Create each component of directory as required */ slash = strchr (path + 1, PATHEND); /* Find first slash */ FOREVER /* Create any parent directories */ { if (slash) *slash = '\0'; /* Cut at slash */ if (!file is directory (path)) { #if (defined (__UNIX__) || defined (__VMS_XOPEN) || defined (__OS2__)) rc = mkdir (path, 0775); /* User RWE Group RWE World RE */ #elif (defined (MSDOS_FILESYSTEM)) rc = mkdir (path); /* Protection? What's that? */ #else rc = -1; /* Not a known system */ #endif if (rc) /* End if error */ break; } if (slash == NULL) /* End if last directory */ break; *slash = PATHEND; /* Restore path name */ slash = strchr (slash + 1, PATHEND); } mem_strfree (&path); return (rc); }
#include "sfldir.h" int remove_dir ( const char *path)
remove a directory. Returns 0 if the directory could be removed; -1 if there was an error. Under MS-DOS and OpenVMS accepts a directory name in UNIX format, i.e. containing '/' delimiters. The directory must be empty to be removed.
{ #if (defined (__UNIX__) || defined (__VMS_XOPEN) || defined (__OS2__)) /* Check that directory exists */ if (!file is directory (path)) return (-1); return (rmdir (path)); #elif (defined (MSDOS_FILESYSTEM)) int feedback; char *copy_path; /* Check that directory exists */ if (!file is directory (path)) return (-1); copy_path = mem_strdup (path); if (copy_path) { strconvch (copy_path, '/', '\\'); feedback = rmdir (copy_path); mem_strfree (©_path); } return (feedback); #else return (-1); #endif }
Filename: sflproc.h
Package: Standard Function Library (SFL)
Written: 96/09/09 iMatix SFL project team sfl@imatix.com
Revised: 98/01/30
Copyright: Copyright (c) 1991-98 iMatix
Provides functions to create and manage processes. The main set of functions lets you create, monitor, and end processes. A secondary function lets you run the current process as a background process.
sflproc.h defines these symbols, possibly conditionally:
Symbol: | Defined as: |
---|---|
FILEHANDLE_MAX | (various) |
NULL_PROCESS | (various) |
PRIORITY_HIGH | 2 |
PRIORITY_LOW | 0 |
PRIORITY_NORMAL | 1 |
PROCESS_ENDED_ERROR | 2 |
PROCESS_ENDED_OK | 1 |
PROCESS_INTERRUPTED | 3 |
PROCESS_RUNNING | 0 |
_SFLPROC_INCLUDED | TRUE |
Type name: | Defined as: |
---|---|
PROCESS | qbyte |
#include "sflproc.h" PROCESS process_create ( const char *filename, /* Name of file to execute */ char *argv [], /* Arguments for process, or NULL */ const char *workdir, /* Working directory, or NULL */ const char *std_in, /* Stdin device, or NULL */ const char *std_out, /* Stdout device, or NULL */ const char *std_err, /* Stderr device, or NULL */ char *envv [], /* Environment variables, or NULL */ Bool wait /* Wait for process to end */ )
Creates a subprocess and returns a PROCESS identifying the new process. Optionally directs standard input, output, and error streams to specified devices. The caller can also specify environment symbols that the subprocess can access. Accepts these arguments:
filename | File to execute; if not fully specified, searches PATH. |
argv [] | List of arguments; argv [0] is filename; ends in a NULL. |
workdir | Working directory; if NULL, remains in current directory. |
std in | Device to use for standard input; NULL = no redirection. |
std out | Device to use for standard output; NULL = no redirection. |
std err | Device to use for standard error; NULL = no redirection. |
envs [] | List of environment symbols to define, or NULL. |
{ #if (defined (__UNIX__)) /************************************************************************* ** UNIX *************************************************************** *************************************************************************/ pid_t fork_result; /* Result from fork() */ int pipe_handle [2], /* Parent-to-child pipe */ pipe_readsize, /* Amount of data read from pipe */ pipe_data; /* Data read from pipe */ struct itimerval timeout; /* Wait for response from child */ struct sigaction old_handler; /* Old handler for SIGALRM */ const char *filename_only; /* Filename, without arguments */ char *clean_filename, /* Unescaped filename */ *full_filename; /* File to execute, with path */ /* Create pipe for feedback from child to parent; quit if this fails */ if (pipe (pipe_handle)) return (0); /* Create subprocess - this returns 0 if we are the child, the pid if */ /* we are the parent, or -1 if there was an error (not enough memory). */ fork_result = fork (); if (fork_result < 0) /* < 0 is an error */ { close (pipe_handle [0]); /* Close the pipe */ close (pipe_handle [1]); return (0); /* Could not fork */ } else if (fork_result > 0) /* > 0 is the parent process */ { /* --- PARENT PROCESS HANDLING ------------------------------------ */ /* If the child process has a problem with the exec() call, it */ /* sends us an errno value across the pipe. If the exec() call */ /* works okay, we get no feedback across the pipe. We wait for a */ /* small time (number of msecs specified by process_delay). If */ /* nothing comes across the pipe, we assume everything went okay. */ /* The FD_CLOEXEC setting *should* cause the child pipe to close */ /* after exec() but this does not seem to work; the read() still */ /* blocks. Bummer. */ if (process_delay > 0) { timeout.it_interval.tv_sec = 0; timeout.it_interval.tv_usec = 0; timeout.it_value.tv_sec = process_delay / 1000; timeout.it_value.tv_usec = (process_delay % 1000) * 1000; /* Save old signal handler to be polite to the calling program */ /* then redirect the SIGALRM signal to our own (empty) handler */ sigaction (SIGALRM, NULL, &old_handler); signal (SIGALRM, ignore_signal); setitimer (ITIMER_REAL, &timeout, 0); /* Now read on the pipe until data arrives or the alarm goes */ pipe_readsize = read (pipe_handle [0], &pipe_data, sizeof (errno)); /* Restore old signal handler */ sigaction (SIGALRM, &old_handler, NULL); } else pipe_readsize = 0; close (pipe_handle [0]); /* Close the pipe */ close (pipe_handle [1]); if (pipe_readsize == -1) { if (errno == EBADF || errno == EINTR) { /* Normal - SIGALRM arrived or FD_CLOEXEC worked :) */ if (wait) waitpid (fork_result, 0, 0); return ((PROCESS) fork_result); } else return (0); /* Error on read() */ } else /* We come here if process_delay was zero, or FD_CLOEXEC did its */ /* job and the pipe was closed by the child process. */ if (pipe_readsize == 0) { if (wait) waitpid (fork_result, 0, 0); return ((PROCESS) fork_result); } else { /* We read data from the pipe - this is an error feedback from */ /* the child - i.e. file not found, or a permission problem. */ errno = pipe_data; /* Stuff the errno */ return (0); } } /* --- CHILD PROCESS HANDLING ----------------------------------------- */ /* Prepare the process environment and execute the file */ /* If argv[] array was not supplied, build it now from filename */ /* And pull out the name of the file that we want to run. */ if (!argv) { /* Split off the arguments, and pick out the filename to use */ argv = tok split (filename); /* The filename, and only the filename, is the 0th argument */ filename_only = argv[0]; } else { /* Already got our arguments, so the filename is just the filename */ filename_only = filename; } /* If requested, close stdin, stdout, stderr, and redirect them */ redirect_io (std_in, STDIN_FILENO, pipe_handle [1], TRUE); redirect_io (std_out, STDOUT_FILENO, pipe_handle [1], FALSE); redirect_io (std_err, STDERR_FILENO, pipe_handle [1], FALSE); /* Find file on path, make sure it is executable */ /* This is a good moment to unescape any spaces in the filename... */ clean_filename = process unesc (NULL, filename_only); if (strchr (clean_filename, '/') == NULL && strchr (clean_filename, PATHEND) == NULL) full_filename = file where ('r', "PATH", clean_filename, NULL); else full_filename = file where ('r', NULL, clean_filename, NULL); mem_free (clean_filename); if (full_filename == NULL) { errno = ENOENT; /* No such file */ write (pipe_handle [1], &errno, sizeof (errno)); exit (EXIT_FAILURE); /* Kill the child process */ } if (!file is executable (full_filename)) { errno = EACCES; /* No permission to access file */ write (pipe_handle [1], &errno, sizeof (errno)); exit (EXIT_FAILURE); /* Kill the child process */ } /* Tell the system to close the pipe when we've done the exec() */ fcntl (pipe_handle [0], F_SETFD, FD_CLOEXEC); fcntl (pipe_handle [1], F_SETFD, FD_CLOEXEC); /* If requested, change to working directory */ if (workdir) chdir (workdir); /* Execute the program - normally this call does not return, as it */ /* replaces the current process image by the new one. If we ever do */ /* return, it is because there was an error. */ if (envv) /* If caller provided envv, use it */ execve (full_filename, argv, envv); else /* Otherwise use current values */ execv (full_filename, argv); write (pipe_handle [1], &errno, sizeof (errno)); exit (EXIT_FAILURE); /* Kill the child process */ #elif (defined (__OS2__)) /************************************************************************* ** OS/2 *************************************************************** *************************************************************************/ int process = 0; /* Process number */ HANDLE old_stdin = -1, /* Dup'd handle for old stdin */ old_stdout = -1, /* Dup'd handle for old stdout */ old_stderr = -1; /* Dup'd handle for old stderr */ int parsedargs = 0, /* argv() points at token array */ free_argv = 0; /* argv() points at handbuilt array */ const char *filename_only = NULL, /* Filename, without arguments */ *actual_command = NULL; /* Actual command string to run */ char *clean_filename = NULL, /* Unescaped filename */ *full_filename = NULL, /* File to execute, with path */ *curdir = NULL, /* Current working directory */ *strfree_this = NULL; /* strfree() this, if not NULL */ /* NOTE: special care must be taken to ensure this code does not leak */ /* memory, as the memory will be leaked in the main process which */ /* potientally tries to run for a long period of time. Token arrays */ /* have a lot of potiental for leaks if care is not taken. To avoid */ /* these potiental problems strings are copied a little more than */ /* otherwise would have been done, and then the original token arrays */ /* are freed. */ /* If argv[] array was not supplied, build it now from filename */ /* And pull out the name of the file that we want to run. */ if (!argv) { /* Split off the arguments, and pick out the filename to use */ argv = tok split (filename); /* The filename, and only the filename, is the 0th argument */ filename_only = argv[0]; parsedargs = 1; /* Yes, we split off the arguments */ } else { /* Already got our arguments, so the filename is just the filename */ filename_only = filename; } /* Under OS/2, we accept the magic file headers "#!", and "'/'*!". */ /* We also special case running CMD scripts, so that we invoke the */ /* default command interpreter, with a "/c" parameter, and the script */ /* name. The magic file headers are checked first so can be used to */ /* override the default command interpreter. */ actual_command = redirect_exec (filename_only); if (actual_command != NULL) { /* At this point we have a string containing the name of the */ /* program to run, followed by the arguments and the scriptname, */ /* if it was a script that we were going to run. So we tokenise the*/ /* string we got back and arrange for those bits to end up in the */ /* arguments if required. */ char **newargs = NULL; int num_new = 0, num_existing = 0; int free_newargs = 0; newargs = tok split (actual_command); /* Split off the arguments */ actual_command = newargs[0]; /* Count the number of new arguments (should be at least 1) */ /* And while we are here, eliminate any double quotes around the */ /* arguments (especially the script name), since they'll only get */ /* in the way later. */ for (num_new = 0; newargs[num_new] != NULL; num_new++) if (*newargs[num_new] == '"') { char *pair = NULL; pair = strrchr(newargs[num_new], '"'); if (pair != NULL) { *pair = '\0'; /* Eliminate the last " */ newargs[num_new]++; /* Step over the first one */ } } ASSERT(num_new >= 1); /* Count the number of existing arguments (from above), should be */ /* at least 1. */ for (num_existing = 0; argv[num_existing] != NULL; num_existing++) ; /* EMPTY BODY */ ASSERT(num_existing >= 1); /* Handle .CMD script files where the redirection wasn't done above */ if (num_new == 1) { /* Okay, it didn't expand there. But possibly we have a CMD */ /* script and need to invoke the command processor. */ char *extension = NULL; /* Find file extension; if not found, set to NULL */ extension = strrchr (actual_command, '.'); if (extension == NULL || strchr (extension, '/') /* Last '.' is part of path */ || strchr (extension, '\\')) /* => filename has no ext */ extension = NULL; if (extension != NULL && (lexcmp(extension, ".CMD") == 0)) { /* This is a CMD script, and we need to invoke the command */ /* interpreter over it. */ char *command_processor = NULL; command_processor = strdupl (env get string ("COMSPEC", "")); if (*command_processor != '\0') /* Not an empty string */ { /* Determine command processor arguments */ char **cmdargs = NULL; char **tmpargs = NULL; cmdargs = tok split (command_processor); /* Count the number of new arguments (at least 1) */ for (num_new = 0; cmdargs[num_new] != NULL; num_new++) ; /* EMPTY BODY */ ASSERT(num_new >= 1); /* Now merge those arguments with script name */ /* Need: num_new + 1 for "/c", +1 for script name */ /* + 1 to terminate array */ tmpargs = mem_alloc((num_new+3) * sizeof(char *)); if (tmpargs != NULL) { /* Okay, copy all the arguments into place */ int i = 0; for (i = 0; i < num_new; i++) tmpargs[i] = strdupl (cmdargs[i]); tmpargs[num_new++] = strdupl ("/c"); tmpargs[num_new++] = strdupl (actual_command); tmpargs[num_new] = NULL; /* Free the old arguments, and the old parse */ tok free(newargs); tok free(cmdargs); /* Now use that for our new arguments */ newargs = tmpargs; actual_command = newargs[0]; free_newargs = 1; /* Must free newargs later */ } /* Free the command processor string */ strfree(&command_processor); } } /* extension is .cmd */ } /* only one new argument (filename to run) */ /* Now collect all the arguments together into one array */ if (num_new >= 2 && num_existing >= 2) { /* Okay, we've got arguments to merge together, so we put the */ /* new ones first followed by the old ones. */ char **tmpargs; ASSERT(newargs != NULL); /* Allocate space for the new arguments (at start), and the */ /* existing arumgnents (at end), and a terminator. */ tmpargs = mem_alloc((num_new+num_existing+1) * sizeof(char *)); if (tmpargs != NULL) { /* Okay, copy all the arguments into place */ int i = 0; for (i = 0; i < num_new; i++) tmpargs[i] = strdupl (newargs[i]); /* NOTE: We skip the first argument here, since it is the */ /* name of the script, and we've got one of those above. */ /* BUT we've got to put next arg in next position, hence -1*/ for (i = 1; i < num_existing; i++) tmpargs[num_new + i - 1] = strdupl (argv[i]); /* Terminate the array of arguments */ tmpargs[num_new + num_existing - 1] = NULL; /* Pick up a new pointer to the command to run */ actual_command = tmpargs[0]; /* Tidy up after ourselves */ if (free_newargs) { int j = 0; for (j = 0; newargs[j] != NULL; j++) strfree(&newargs[j]); mem_free(newargs); } else tok free (newargs); if (parsedargs) { tok free(argv); parsedargs = 0; } /* Change pointer to point at the new (combined) arguments */ argv = tmpargs; free_argv = 1; } else { /* We couldn't allocate the new memory required */ /* Return failure. */ tok free(newargs); if (parsedargs) tok free(argv); errno = ENOMEM; return ((PROCESS)0); } } else if (num_new >= 2 && num_existing <= 1) { /* There were no arguments before, there are now. Use new ones */ if (parsedargs) { /* We parsed the arguments, free up some of the memory */ tok free(argv); parsedargs = 0; } argv = newargs; if (free_newargs) /* Make sure we free arguments */ free_argv = 1; else parsedargs = 1; } else /* (num_new <= 1) */ /* num_existing is 1 or more */ { /* No expansion of the string, we just use the existing args */ /* But we do use the string as returned, because it may have */ /* an extension on it. */ ASSERT(num_new <= 1); ASSERT(num_existing >= 1); /* Copy the string as returned, so that we can use it below */ strfree_this = strdupl (actual_command); if (strfree_this != NULL) { actual_command = strfree_this; ASSERT(free_newargs == 0); tok free(newargs); } } } /* Redirection found a filename to run */ else { /* Redirection failed. This means that it isn't executable, because*/ /* we should either have got a full name back, or a command string */ /* to run. */ if (parsedargs) tok free(argv); errno = EACCES; /* No permission to access file */ return (PROCESS)0; } /* Find file on path, make sure it is executable */ /* This is a good moment to unescape any spaces in the filename... */ clean_filename = process unesc (NULL, actual_command); if (strchr (clean_filename, '/') == NULL && strchr (clean_filename, PATHEND) == NULL) full_filename = file where ('r', "PATH", clean_filename, NULL); else full_filename = file where ('r', NULL, clean_filename, NULL); mem_free (clean_filename); if (full_filename == NULL) { /* Clear out the memory that we don't need any longer */ if (parsedargs) tok free(argv); else if (free_argv) { int j = 0; for (j = 0; argv[j] != NULL; j++) strfree(&argv[j]); mem_free(argv); } if (strfree_this != NULL) strfree(&strfree_this); errno = ENOENT; /* No such file */ return (PROCESS)0; /* Failed to open */ } if (!file is executable (full_filename)) { /* Clear out the memory that we don't need any longer */ if (parsedargs) tok free(argv); else if (free_argv) { int j = 0; for (j = 0; argv[j] != NULL; j++) strfree(&argv[j]); mem_free(argv); } if (strfree_this != NULL) strfree(&strfree_this); errno = EACCES; /* No permission to access file */ return (PROCESS)0; } /* Redirect the IO streams, and save copies of the ones we redirect */ old_stdin = redirect_io(std_in, STDIN_FILENO, 0, TRUE); old_stdout = redirect_io(std_out, STDOUT_FILENO, 0, FALSE); old_stderr = redirect_io(std_err, STDERR_FILENO, 0, FALSE); if (old_stdin == -2 || old_stdout == -2 || old_stderr == -2) { /* An error redirecting one of the file handles; restore them all */ /* and exit having failed our job. */ restore_redirection(old_stdin, old_stdout, old_stderr); /* Clear out the memory that we don't need any longer */ if (parsedargs) tok free(argv); else if (free_argv) { int j = 0; for (j = 0; argv[j] != NULL; j++) strfree(&argv[j]); mem_free(argv); } if (strfree_this != NULL) strfree(&strfree_this); return (PROCESS)0; } /* If requested, change to working directory */ if (workdir) { curdir = getcwd(NULL, 256); chdir (workdir); } else curdir = NULL; /* Spawn the new program, and pick up its process ID. */ if (envv) /* If caller provided envv, use it */ process = spawnve (P_NOWAIT, full_filename, argv, envv); else /* Otherwise use the current values */ process = spawnv (P_NOWAIT, full_filename, argv); /* Put things back the way they were before */ restore_redirection(old_stdin, old_stdout, old_stderr); if (curdir != NULL) /* If directory changed, restore it */ { chdir(curdir); free(curdir); } /* Clear out the memory that we don't need any longer */ if (parsedargs) tok free(argv); else if (free_argv) { int j = 0; for (j = 0; argv[j] != NULL; j++) strfree(&argv[j]); mem_free(argv); } if (strfree_this != NULL) strfree(&strfree_this); if (process <= -1) return ((PROCESS)0); /* Error starting child process */ if (wait) waitpid (process, 0, 0); return ((PROCESS) process); #elif (defined (WIN32)) /************************************************************************* ** WINDOWS 32 ********************************************************* *************************************************************************/ PROCESS process; /* Our created process handle */ STARTUPINFO newinfo = {0}, /* Specification for new process */ curinfo; /* Specification of cur process */ PROCESS_INFORMATION procinfo; /* Information about created proc */ DWORD dwCreateFlags = CREATE_NEW_CONSOLE; char *olddir, /* Caller's working directory */ *fulldir, /* Process' working directory */ *args, /* Command arguments, if any */ *actual_command, /* Command, possibly qualified */ *buffer = NULL; /* Working buffer */ int argn; /* Argument number */ /* Format full working directory, if specified */ if (workdir) { olddir = get curdir (); /* Just a lazy way to let the OS */ set curdir (workdir); /* figure-out if the workdir is a */ fulldir = get curdir (); /* relative or absolute directory. */ set curdir (olddir); mem_free (olddir); } else fulldir = NULL; /* Under Windows we accept the magic file header "#!". If the */ /* caller supplied an argument list, we attach this to the command. */ actual_command = redirect_exec (filename); strconvch (actual_command, '/', '\\'); GetShortPathName (actual_command, actual_command, strlen (actual_command) + 1); args = strchr (filename, ' '); /* Find arguments, if any */ if (argv) { /* Build full command buffer */ buffer = mem_alloc (tok text size ((char **) argv) + strlen (actual_command) + 1); strcpy (buffer, actual_command); for (argn = 1; argv [argn]; argn++) xstrcat (buffer, " ", argv [argn], NULL); actual_command = buffer; } else if (args) { buffer = xstrcpy (NULL, actual_command, args, NULL); actual_command = buffer; } process = mem_alloc (sizeof (PROC_HANDLE)); process-> process = NULL; process-> in = redirect_io (std_in, 0, 0, TRUE); process-> out = redirect_io (std_out, 0, 0, FALSE); process-> err = redirect_io (std_err, 0, 0, FALSE); /* Convert environment to a Windows-type packed block of strings */ /* Use supplied environment, or parent environment if necessary. */ process-> envd = strt2descr (envv? (char **) envv: environ); GetStartupInfo (&curinfo); newinfo.cb = sizeof (newinfo); newinfo.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; newinfo.wShowWindow = SW_HIDE; newinfo.hStdInput = process-> in? process-> in: curinfo.hStdInput; newinfo.hStdOutput = process-> out? process-> out: curinfo.hStdOutput; newinfo.hStdError = process-> err? process-> err: curinfo.hStdError; newinfo.lpTitle = NULL; /* If necessary, run in separate VM, for 16-bit programs */ if (process_compatible) dwCreateFlags |= CREATE_SEPARATE_WOW_VDM; /* CreateProcess returns errors sometimes, even when the process was */ /* started correctly. The cause is not evident. For now: we detect */ /* an error by checking the value of procinfo.hProcess after the call. */ procinfo.hProcess = NULL; CreateProcess ( NULL, /* Name of executable module */ actual_command, /* Command line string */ NULL, /* Process security attributes */ NULL, /* Thread security attributes */ TRUE, /* Handle inheritance flag */ dwCreateFlags, /* Creation flags */ process-> envd-> data, /* New environment block */ fulldir, /* Current directory name */ &newinfo, /* STARTUPINFO */ &procinfo); /* PROCESS_INFORMATION */ mem_strfree (&fulldir); mem_strfree (&buffer); /* Deallocate buffer, if used */ if (procinfo.hProcess == NULL) /* Error, we presume */ { process close (process); return (NULL); } /* Release our hold on the thread */ CloseHandle (procinfo.hThread); process-> process = procinfo.hProcess; /* We do not need access to the files any longer in this process */ if (process-> in) { CloseHandle (process-> in); process-> in = NULL; } if (process-> out) { CloseHandle (process-> out); process-> out = NULL; } if (process-> err) { CloseHandle (process-> err); process-> err = NULL; } /* Wait for the process to finish or be cancelled */ if (wait) { WaitForSingleObject (procinfo.hProcess, INFINITE); process close (process); } return (process); #elif (defined (__VMS__)) /************************************************************************* ** OPENVMS ************************************************************ *************************************************************************/ PROCESS process; /* Our created process handle */ char *curdir, /* Current directory */ *clean_filename, /* Unescaped filename */ *full_filename = NULL, *full_std_in = NULL, *full_std_out = NULL; qbyte process_flags; /* Process creation flags */ int argn, /* Argument number */ rc; /* Return code from lib$spawn */ Bool rebuilt_argv = FALSE; /* Did we rebuild argv[]? */ VMS_STRING (command_dsc, ""); /* Define string descriptors */ VMS_STRING (std_in_dsc, ""); VMS_STRING (std_out_dsc, ""); /* If argv[] array was not supplied, build it now from filename */ if (!argv) { argv = tok split (filename); filename = argv [0]; rebuilt_argv = TRUE; } /* If filename contains a path or extension, disregard them */ clean_filename = strrchr (filename, '/'); if (clean_filename) clean_filename++; else clean_filename = (char *) filename; if (strchr (clean_filename, '.')) *strchr (clean_filename, '.') = '\0'; /* Rebuild full command from filename and arguments */ full_filename = mem_alloc (tok text size ((char **) argv) + strlen (clean_filename) + 1); strcpy (full_filename, clean_filename); for (argn = 1; argv [argn]; argn++) xstrcat (full_filename, " ", argv [argn], NULL); /* Free argument table if we allocated it dynamically here */ if (rebuilt_argv) tok free (argv); command_dsc.value = full_filename; command_dsc.length = strlen (full_filename); /* Prepare full names for stdin and stdout */ curdir = get curdir (); if (std_in) { if (strchr (std_in, '/')) /* If already with path, use as is */ full_std_in = mem_strdup (std_in); else full_std_in = xstrcpy (NULL, curdir, "/", std_in, NULL); translate_to_vms (full_std_in); std_in_dsc.value = full_std_in; } if (std_out) { if (strchr (std_out, '/')) /* If already with path, use as is */ full_std_out = mem_strdup (std_out); else full_std_out = xstrcpy (NULL, curdir, "/", std_out, NULL); translate_to_vms (full_std_out); std_out_dsc.value = full_std_out; } std_in_dsc.length = std_in? strlen (std_in_dsc.value): 0; std_out_dsc.length = std_out? strlen (std_out_dsc.value): 0; /* If requested, change to working directory */ if (workdir) chdir (workdir); /* Prepare process flags */ if (wait) process_flags = 0; else process_flags = 1; /* Bit 1 = don't wait for child */ process = mem_alloc (sizeof (PROC_HANDLE)); process-> id = 0; process-> status = 0; /* Completion status */ /* char *envv [], */ /* Environment variables, or NULL */ rc = lib$spawn ( &command_dsc, /* Command to run */ std_in? &std_in_dsc: NULL, /* Stdin descriptor */ std_out? &std_out_dsc: NULL, /* Stdout+stderr */ &process_flags, /* Options for new process */ &NULL, /* Process name -- generated */ &process-> id, /* Returned process ID */ &process-> status); if (workdir) /* Switch back to original dir */ chdir (curdir); mem_free (curdir); mem_strfree (&full_filename); /* Deallocate various buffers, */ mem_strfree (&full_std_in); /* if they were used */ mem_strfree (&full_std_out); /* */ /* Return process ID. If we waited for completion, the process id */ /* is always NULL. */ if (rc != 1) /* Process failed with error */ { process close (process); process = NULL; } else if (wait) /* Finished with process */ process close (process); return (process); #else return ((PROCESS) 0); /* Not supported on this system */ #endif }
#include "sflproc.h" int process_status ( PROCESS process)
Returns status of process specified by process ID. Returns one of these values, or -1 if there was an error:
PROCESS RUNNING | Process is still running. |
PROCESS ENDED OK | Process ended normally. |
PROCESS ENDED ERROR | Process ended with an error status. |
PROCESS INTERRUPTED | Process was interrupted (killed). |
{ #if (defined (__UNIX__) || defined (__OS2__)) int status; pid_t return_pid; /* waitpid() returns 0 if the child process is still running, or the */ /* process id if it stopped. It can also return -1 in case of error. */ /* No other return value is possible. */ return_pid = waitpid (process, &status, WNOHANG | WUNTRACED); if (return_pid == 0) return (PROCESS_RUNNING); else if (return_pid == process) { if (WIFEXITED (status)) /* Program called exit() */ { process_errno = WEXITSTATUS (status); if (process_errno) /* Treat exit (0) as normal end */ return (PROCESS_ENDED_ERROR); else return (PROCESS_ENDED_OK); } else if (WIFSIGNALED (status)) /* Process was interrupted */ return (PROCESS_INTERRUPTED); else return (PROCESS_ENDED_OK); } else return (-1); #elif (defined (WIN32)) DWORD status; ASSERT (process); status = WaitForSingleObject (process-> process, 0); if (status == WAIT_TIMEOUT) return (PROCESS_RUNNING); else if (status == WAIT_OBJECT_0) return (PROCESS_ENDED_OK); else if (status == WAIT_ABANDONED) return (PROCESS_ENDED_ERROR); else return (-1); #elif (defined (__VMS__)) ASSERT (process); if (process-> status == 0) return (PROCESS_RUNNING); else return (PROCESS_ENDED_OK); #else return (-1); /* Not supported on this system */ #endif }
#include "sflproc.h" int process_kill ( PROCESS process)
Ends a process specified by a process id. The current process must have the appropriate authority to stop the specified process. Returns zero if the process was killed, -1 if there was an error.
{ #if (defined (__UNIX__) || defined (__OS2__)) int count = 5; /* First give it a chance to gracefully exit... */ kill (process, SIGTERM); while (process status (process) == PROCESS_RUNNING && count--) sleep (1); /* Then get brutal if neccessary. */ if (process status (process) == PROCESS_RUNNING) { kill (process, SIGKILL); while (process status (process) == PROCESS_RUNNING) sleep (1); } return (0); #elif (defined (WIN32)) ASSERT (process); TerminateProcess (process-> process, 1); while (process status (process) == PROCESS_RUNNING) Sleep (100); process close (process); return (0); #elif (defined (__VMS__)) ASSERT (process); sys$delprc (process-> id); process close (process); return (0); #else return (-1); /* Not supported on this system */ #endif }
#include "sflproc.h" void process_close ( PROCESS process)
You should call this function when a process has ended normally, if you did not specify the wait option when calling the process create() function. On some systems, each created process uses some memory. process close() guarantees that this memory is correctly freed. Does nothing if the process handle is NULL.
{ #if (defined (WIN32)) if (process) { if (process-> process) CloseHandle (process-> process); if (process-> in) CloseHandle (process-> in); if (process-> out) CloseHandle (process-> out); if (process-> err) CloseHandle (process-> err); mem_free (process-> envd); mem_free (process); } #elif (defined (__VMS__)) mem_free (process); #endif }
#include "sflproc.h" int process_server ( const char *workdir, /* Where server runs, or NULL/"" */ const char *lockfile) /* For exclusive execution */
Converts the process from an interactive foreground process into a background process. The precise effect of this depends on the system. On UNIX, does this:
{ #if (defined (__UNIX__)) int fork_result, file_handle; char pid_buffer [10]; struct flock lock_file; /* flock() argument block */ /* We recreate our process as a child of init. The process continues */ /* to exit in the background. UNIX calls wait() for all children of */ /* the init process, so the server will exit cleanly. */ fork_result = fork (); if (fork_result < 0) /* < 0 is an error */ return (-1); /* Could not fork */ else if (fork_result > 0) /* > 0 is the parent process */ exit (EXIT_SUCCESS); /* End parent process */ /* We close all open file descriptors that may have been inherited */ /* from the parent process. This is to reduce the resources we use. */ for (file_handle = FILEHANDLE_MAX - 1; file_handle >= 0; file_handle--) close (file_handle); /* Ignore errors */ /* We move to a safe and known directory, which is supplied as an */ /* argument to this function (or not, if workdir is NULL or empty). */ if (workdir && strused (workdir)) chdir (workdir); /* We set the umask so that new files are given mode 750 octal */ umask (027); /* Complement of 0750 */ /* We set standard input and output to the null device so that any */ /* functions that assume that these files are open can still work. */ file_handle = open ("/dev/null", O_RDWR); /* stdin = handle 0 */ dup (file_handle); /* stdout = handle 1 */ dup (file_handle); /* stderr = handle 2 */ /* We enforce a lock on the lockfile, if specified, so that only one */ /* copy of the server can run at once. We return -1 if the lock fails. */ /* This locking code might be better isolated into a separate package, */ /* since it is not very portable between unices. */ if (lockfile && strused (lockfile)) { file_handle = open (lockfile, O_RDWR | O_CREAT, 0640); if (file_handle < 0) return (-1); /* We could not open lock file */ else { lock_file.l_type = F_WRLCK; if (fcntl (file_handle, F_SETLK, &lock_file)) return (-1); /* We could not obtain a lock */ } /* We record the server's process id in the lock file */ sprintf (pid_buffer, "%6d\n", getpid ()); write (file_handle, pid_buffer, strlen (pid_buffer)); } /* We ignore any hangup signal from the controlling TTY */ signal (SIGHUP, SIG_IGN); return (0); /* Initialisation completed ok */ #else return (0); /* Nothing to do on this system */ #endif }
#include "sflproc.h" Bool process_alarm (long delay)
Sets a system timer to raise a SIGALRM after a specified interval in milliseconds. Returns TRUE if the timer could be created and FALSE if there were insufficient resources, or if the system does not support timers. Permits a single alarm for the current process: any alarm that was still pending when you called this function is annulled. The implementation is system- dependent and highly non-portable. Under UNIX we use the setitimer() system function, which is clean and simple. Under 16-bit Windows we use the SetTimer() call. This does not work in 32-bit console applications. Under 32-bit Windows we use the 'multimedia' timer, which provides better resolution and does work in console applications. In both these cases we cache the id of the last-created alarm (and kill it before each new request), to avoid multiple active alarms. It is not a good idea to create too many concurrent timers; after 16 or so the alarms start to fail. This is not supposed to happen with MM timers, but does anyway. Under Windows, SIGALRM does not exist. Since signal() only accepts one of a small set of fixed signals, we hijack the SIGFPE signal... It's a compromise and requires that any code which expects a SIGALRM does not use SIGFPE. This can be tweaked in prelude.h. Under OS/2 we use the alarm() function which is accurate to one second only. The required accuracy of timing is not easily achieved, so process alarm() rounds down to whole seconds (except if rounding down would give 0, in which case it will delay 1 second). This will probably cause problems in code applications that depends on sub-second timing resolution. Under OpenVMS 7 and later we use the setitimer() function as for UNIX. Under OpenVMS 6 and earlier we use the alarm() function as for OS/2. This code may be tuned to use native VMS system calls.
{ #if (defined (__UNIX__) || defined (__VMS_XOPEN)) struct itimerval timeout; /* Timeout for setitimer */ /* If the system supports interval timers, ask for a signal */ timeout.it_interval.tv_sec = 0; timeout.it_interval.tv_usec = 0; timeout.it_value.tv_sec = delay / 1000; timeout.it_value.tv_usec = delay % 1000 * 1000L; setitimer (ITIMER_REAL, &timeout, 0); return (TRUE); #elif (defined (__OS2__) || defined (__VMS__)) /* Since we use alarm() for our timeout, we can only time to */ /* the nearest second, and alarm(0) turns off the alarm. */ /* NOTE: we also have only one timer -- if alarm() is called while */ /* the alarm is active, then it will be reset to the new value, and */ /* only a single SIGALRM will be generated. */ delay = (delay < 1000) ? 1 : (delay / 1000); alarm (delay); return (TRUE); #elif (defined (__WINDOWS__)) # if (defined (WIN32)) # pragma comment (lib, "winmm") /* Link-in multimedia library */ /* The multimedia timer gives the best accuracy, and works in console */ /* applications */ int rc; if (last_timer) rc = timeKillEvent (last_timer); last_timer = timeSetEvent (delay, 50, handle_timer, 0, TIME_ONESHOT); return (TRUE); # else /* But the normal Windows timer will do if we're in 16 bits */ if (last_timer) KillTimer ((HWND) NULL, last_timer); last_timer = SetTimer ((HWND) NULL, 0, (UINT) delay, handle_timer); return (TRUE); # endif #else return (FALSE); /* No timers - function failed */ #endif }
#include "sflproc.h" char * process_esc (char *dest, const char *src)
Escapes a directory string so that process create() can handle it correctly. If you pass a command to process_create with a directory name that contains spaces, it will assume that the spaces delimit the command from its arguments. For instance, under Windows 95, the filename "C:\Program Files\Myprog.exe" will be incorrectly treated as a program called "C:\Program" with arguments "Files\Myprog.exe". This function replaces spaces by the escape character (0x1B). You cannot use this value in a filename and expect process create() to work correctly. On an EBCDIC system, the escape character (0x27) is also used. If the dest argument is NULL, allocates a string using mem_alloc() and returns that. Otherwise copies into the dest string and returns that. If the src string is NULL, returns an empty string.
{ #if (defined (__EBCDIC__)) # define ESC_CHAR 0x27 #else # define ESC_CHAR 0x1B #endif /* Copy to dest, allocate if necessary */ if (dest != src) dest = xstrcpy (dest, src, NULL); strconvch (dest, ' ', ESC_CHAR); return (dest); }
#include "sflproc.h" char * process_unesc (char *dest, const char *src)
Does the reverse translaction to process esc().
{ /* Copy to dest, allocate if necessary */ if (dest != src) dest = xstrcpy (dest, src, NULL); strconvch (dest, ESC_CHAR, ' '); return (dest); }
#include "sflproc.h" int process_priority (int priority)
Sets process priority as specified, to one of PRIORITY_LOW, PRIORITY_NORMAL, or PRIORITY_HIGH. Currently has an effect only under Windows NT/95. Returns 0 if okay, -1 if there was an error.
{ #if (defined (WIN32)) int class; if (priority == PRIORITY_HIGH) class = HIGH_PRIORITY_CLASS; else if (priority == PRIORITY_LOW) class = IDLE_PRIORITY_CLASS; else class = NORMAL_PRIORITY_CLASS; return (SetPriorityClass (GetCurrentProcess (), HIGH_PRIORITY_CLASS)); #else return (0); #endif }
Filename: sflslot.h
Package: Standard Function Library (SFL)
Written: 96/01/01 iMatix SFL project team sfl@imatix.com
Revised: 97/09/08
Copyright: Copyright (c) 1991-98 iMatix
The time-slot functions provide long-running programs with a means to 'switch-on' and 'switch-off' depending on the time of day, and day of year. The intention is that the user can configure such programs to be active only between certain hours, on certain days, etc. The time-slot functions work with 'range' bitmaps for a day (in seconds) and a year (in days), and provide functions to set, clear, and test these ranges.
sflslot.h defines these symbols, possibly conditionally:
Symbol: | Defined as: |
---|---|
MAX_DAY | 366 /* Max. days in a normal year */ |
MAX_MIN | 1440 /* Max. minutes in a normal day */ |
_SFLSLOT_INCLUDED | TRUE |
#include "sflslot.h" void year_range_empty (byte *range)
Excludes all days in the year (sets all bits to zero).
{ memset (range, 0, sizeof (year_range)); }
#include "sflslot.h" void year_range_fill (byte *range)
Includes all days in the year (sets all bits to 1).
{ memset (range, 255, sizeof (year_range)); }
#include "sflslot.h" int year_slot_clear (byte *range, int day_from, int day_to)
Clears the slots for the specified day range. If day_to is zero, it is ignored; only the slot for day_from is cleared. Returns 0 if okay, -1 if the specified range is invalid.
{ if (day_to == 0) day_to = day_from; /* Range is just one day */ if (day_from > day_to) return (-1); /* Bad range */ while (day_from <= day_to) /* Find and clear each bit */ { ASSERT (day_from >= 0 && day_from < MAX_DAY); range [BYTE (day_from)] &= 255 - BIT (day_from); day_from++; } return (0); /* No errors */ }
#include "sflslot.h" int year_slot_set (byte *range, int day_from, int day_to)
Sets the slots for the specified day range. If day_to is zero, it is ignored; only the slot for day_from is set. Returns 0 if okay, -1 if the specified range is invalid.
{ if (day_to == 0) day_to = day_from; /* Range is just one day */ if (day_from > day_to) return (-1); /* Bad range */ while (day_from <= day_to) /* Find and set each bit */ { ASSERT (day_from >= 0 && day_from < MAX_DAY); range [BYTE (day_from)] |= BIT (day_from); day_from++; } return (0); /* No errors */ }
#include "sflslot.h" Bool year_slot_filled (const byte *range, int day)
Returns TRUE if the specified day slot is set; returns FALSE if the slot is not set.
{ ASSERT (day >= 0 && day < MAX_DAY); return ((range [BYTE (day)] & BIT (day)) > 0); }
#include "sflslot.h" void day_range_empty (byte *range)
Excludes all minutes in the day (sets all bits to zero).
{ memset (range, 0, sizeof (day_range)); }
#include "sflslot.h" void day_range_fill (byte *range)
Includes all minutes in the day (sets all bits to 1).
{ memset (range, 255, sizeof (day_range)); }
#include "sflslot.h" int day_slot_clear (byte *range, int min_from, int min_to)
Clears the slots for the specified minute range. If min_to is zero, it is ignored; only the slot for min_from is cleared. Returns 0 if okay, -1 if the specified range is invalid.
{ if (min_to == 0) min_to = min_from; /* Range is just one minute */ if (min_from > min_to) return (-1); /* Bad range */ while (min_from <= min_to) /* Find and clear each bit */ { ASSERT (min_from >= 0 && min_from < MAX_MIN); range [BYTE (min_from)] &= 255 - BIT (min_from); min_from++; } return (0); /* No errors */ }
#include "sflslot.h" int day_slot_set (byte *range, int min_from, int min_to)
Sets the slots for the specified minute range. If min_to is zero, it is ignored; only the slot for min_from is set. Returns 0 if okay, -1 if the specified range is invalid.
{ if (min_to == 0) min_to = min_from; /* Range is just one minute */ if (min_from > min_to) return (-1); /* Bad range */ while (min_from <= min_to) /* Find and set each bit */ { ASSERT (min_from >= 0 && min_from < MAX_MIN); range [BYTE (min_from)] |= BIT (min_from); min_from++; } return (0); /* No errors */ }
#include "sflslot.h" Bool day_slot_filled (const byte *range, int minute)
Returns TRUE if the specified minute slot is set; returns FALSE if the slot is not set.
{ ASSERT (minute >= 0 && minute < MAX_MIN); return ((range [BYTE (minute)] & BIT (minute)) > 0); }
#include "sflslot.h" int date_to_day (long date)
Extracts the day value (1..366) for the specified date. The date is an 8-digit number: YYYYMMDD.
{ return (julian date (date)); }
#include "sflslot.h" int time_to_min (long time)
Extracts the minute value (0..1439) for the specified time. The time is an 8-digit number: HHMMSSCC.
{ int hour, /* Hour component of time */ minute; /* Minute component of time */ hour = (int) (time / 1000000L); minute = (int) ((time % 1000000L) / 10000L); minute += hour * 60; return (minute); }
Filename: sflstr.h
Package: Standard Function Library (SFL)
Written: 92/10/25 iMatix SFL project team sfl@imatix.com
Revised: 97/10/02
Copyright: Copyright (c) 1991-98 iMatix
Provides various string-handling functions. Some of these functions are available on some but not all platforms; others are useful tools for string handling.
sflstr.h defines these symbols, possibly conditionally:
Symbol: | Defined as: |
---|---|
CPY | 0 |
IGNORECASE | 0 |
PTR | 1 |
SENCECASE | 1 |
_SFLSTR_INCLUDED | TRUE |
checkargcnt(reqnum) | ((argc-1)>=(reqnum)?1:0) |
cstrcpy(dest,src) | {*dest=src;*(dest+1)='\0';} |
deletechar(strbuf,pos) | strcpy((strbuf+pos),(strbuf+pos+1)) |
deletechars(strbuf,pos,cnt) | strcpy((strbuf+pos),(strbuf+pos+cnt)) |
getcommandlinearg(argnum) | ((argnum)<=(argc-1))?argv[argnum]:"" |
mstrncpy(dest,src,len) | {strncpy(dest,src,len);*(dest+len)='\0';} |
numofcmdargs() | ((argc-1)>0?argc-1:0) |
#include "sflstr.h" char * strdupl ( const char *string)
Makes a duplicate of string, obtaining space with a call to malloc(). The allocated space is strlen (string) + 1 bytes long. The caller is responsible for freeing the space allocated by strdup 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. Use this function in place of the non-portable strdup() function. You may also want to use the more robust mem strdup () function.
{ char *copy; if (string) { copy = malloc (strlen (string) + 1); if (copy) strcpy (copy, string); } else copy = NULL; return (copy); }
#include "sflstr.h" char ** strfree ( char **string)
Releases memory occupied by a string. Call this function only when you previously allocated the string using malloc or strdupl(). You pass the address of a char pointer; this function sets the pointer to NULL. This is a safety measure meant to make it safe to try to free non-allocated strings. In your code, initialise all such pointers to NULL. Returns the address of the modified pointer.
{ ASSERT (string); if (*string) { free (*string); *string = NULL; } return (string); }
#include "sflstr.h" char * strskp ( const char *string)
Skips leading spaces in string, and returns a pointer to the first non-blank character. If this is a null, the end of the string was reached.
{ char *skip = (char *) string; ASSERT (string); while (*skip == ' ') skip++; return (skip); }
#include "sflstr.h" char * strcset ( char *string, char ch)
Sets all characters in string up to but not including the final null character to ch. Returns string. Use this function instead of the equivalent but non-portable strset() function.
{ char *scan; ASSERT (string); scan = string; while (*scan) *scan++ = ch; return (string); }
#include "sflstr.h" char * strpad ( char *string, char ch, int length)
Returns string of length characters, padding with ch or truncating if necessary. String must be at least length + 1 long.
{ int cursize; ASSERT (string); cursize = strlen (string); /* Get current length of string */ while (cursize < length) /* Pad until at desired length */ string [cursize++] = ch; string [cursize++] = '\0'; /* Add terminating null */ return (string); /* and return to caller */ }
#include "sflstr.h" char * strlwc ( char *string)
Converts all alphabetic characters in string to lowercase, stopping at the final null character. Returns string. If string is null, returns null. We do not call this function strlwr because that is already provided by some systems (but not by ANSI C).
{ char *scan; if (string) { scan = string; while (*scan) { *scan = (char) tolower (*scan); scan++; } } return (string); }
#include "sflstr.h" char * strupc ( char *string)
Converts all alphabetic characters in string to uppercase, stopping at the final null character. Returns string. If string is null, returns null. We do not call this function strupr because that is already provided by some systems (but not by ANSI C).
{ char *scan; if (string) { scan = string; while (*scan) { *scan = (char) toupper (*scan); scan++; } } return (string); }
#include "sflstr.h" char * strcrop ( char *string)
Drops trailing whitespace from string by truncating string to the last non-whitespace character. Returns string. If string is null, returns null.
{ char *last; if (string) { last = string + strlen (string); while (last > string) { if (!isspace (*(last - 1))) break; last--; } *last = 0; } return (string); }
#include "sflstr.h" char * stropen ( char *string, Bool align)
Inserts a character at string, and places a blank in the gap. If align is TRUE, makes room by reducing the size of the next gap of 2 or more spaces. If align is FALSE, extends the size of the string. Returns string.
{ char *gap; int length; ASSERT (string); length = strlen (string) + 1; /* By default, move string + NULL */ if (align) /* If align is TRUE, find gap */ { gap = strstr (string, " "); if (gap) length = (int) (gap - string); } memmove (string + 1, string, length); string [0] = ' '; /* Stick a space into string */ return (string); }
#include "sflstr.h" char * strclose ( char *string, Bool align)
Removes the character at string, and shifts the remainder down by one. If align is TRUE, only shifts up to the next gap of 2 or more spaces. Returns string.
{ char *gap; int length; ASSERT (string); length = strlen (string); /* By default, move string + NULL */ if (align) { /* If align is TRUE, find gap */ gap = strstr (string, " "); if (gap && gap != string) length = (int) (gap - string); } memmove (string, string + 1, length); return (string); }
#include "sflstr.h" char * strunique ( char *string, char unique)
Reduces chains of some character to a single instances. For example: replace multiple spaces by one space. Returns string.
{ char *from, *to; ASSERT (string); if (strnull (string)) return (string); /* Get rid of special cases */ from = string + 1; to = string; while (*from) { if (*from == unique && *to == unique) from++; else *++to = *from++; } *++to = '\0'; return (string); }
#include "sflstr.h" int strmatch ( const char *string1, const char *string2)
Calculates a similarity index for the two strings. This is a value from 0 to 32767 with higher values indicating a closer match. The two strings are compared without regard for case. The algorithm was designed by Leif Svalgaard leif@ibm.net.
{ static int name_weight [30] = { 20, 15, 13, 11, 10, 9, 8, 8, 7, 7, 7, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4 }; int comp_index, name_index, start_of_string, longest_so_far, substring_contribution, substring_length, compare_length, longest_length, length_difference, name_length, char_index, similarity_index, similarity_weight; char cur_name_char; ASSERT (string1); ASSERT (string2); name_length = strlen (string1); compare_length = strlen (string2); if (name_length > compare_length) { length_difference = name_length - compare_length; longest_length = name_length; } else { length_difference = compare_length - name_length; longest_length = compare_length; } if (compare_length) { similarity_weight = 0; substring_contribution = 0; for (char_index = 0; char_index < name_length; char_index++) { start_of_string = char_index; cur_name_char = (char) tolower (string1 [char_index]); longest_so_far = 0; comp_index = 0; while (comp_index < compare_length) { while ((comp_index < compare_length) && (tolower (string2 [comp_index]) != cur_name_char)) comp_index++; substring_length = 0; name_index = start_of_string; while ((comp_index < compare_length) && (tolower (string2 [comp_index]) == tolower (string1 [name_index]))) { if (comp_index == name_index) substring_contribution++; comp_index++; if (name_index < name_length) { name_index++; substring_length++; } } substring_contribution += (substring_length + 1) * 3; if (longest_so_far < substring_length) longest_so_far = substring_length; } similarity_weight += (substring_contribution + longest_so_far + 1) * 2; similarity_weight /= name_length + 1; } similarity_index = (name_length < 30? name_weight [name_length]: 3) * longest_length; similarity_index /= 10; similarity_index += 2 * length_difference / longest_length; similarity_index = 100 * similarity_weight / similarity_index; } else similarity_index = 0; return (similarity_index); }
#include "sflstr.h" Bool strprefixed ( const char *string, const char *prefix)
If string starts with specified prefix, returns TRUE. If string does not start with specified prefix, returns FALSE.
{ ASSERT (string); ASSERT (prefix); if (*string == *prefix /* Check that first letters match */ && strlen (string) >= strlen (prefix) && memcmp (string, prefix, strlen (prefix)) == 0) return (TRUE); else return (FALSE); }
#include "sflstr.h" char * strprefix ( const char *string, const char *delims)
Looks for one of the delimiter characters in the string; if found, returns a string that contains the text up to that delimiter. If not found, returns NULL. The returned string can be zero or more characters long followed by a null byte. It is allocated using the mem_alloc() function; you should free it using mem_free() when finished.
{ const char *nextch; char *token; int token_size; ASSERT (string); ASSERT (delims); for (nextch = string; *nextch; nextch++) { if (strchr (delims, *string)) /* Is next character a delimiter */ { token_size = (int) (nextch - string); token = mem_alloc (token_size + 1); if (token == NULL) return (NULL); /* Not enough memory - fail */ memcpy (token, string, token_size); token [token_size] = 0; return (token); } } return (NULL); }
#include "sflstr.h" char * strdefix ( const char *string, const char *prefix)
If string starts with specified prefix, returns pointer to character after prefix. Null character is not considered part of the prefix. If string does not start with specified prefix, returns NULL.
{ ASSERT (string); ASSERT (prefix); if (strlen (string) >= strlen (prefix) && memcmp (string, prefix, strlen (prefix)) == 0) return ((char *) string + strlen (prefix)); else return (NULL); }
#include "sflstr.h" qbyte strhash ( const char *string)
Calculates a 32-bit hash value for the string. The string must end in a null. To use the result as a hash key, take the modulo over the hash table size. The algorithm was designed by Peter Weinberger. This version was adapted from Dr Dobb's Journal April 1996 page 26.
int index; index = (int) strhash (name) % TABLE_SIZE;
{ qbyte high_bits, hash_value = 0; ASSERT (string); while (*string) { hash_value = (hash_value << 4) + *string++; if ((high_bits = hash_value & 0xF0000000L) != 0) hash_value ^= high_bits >> 24; hash_value &= ~high_bits; } return (hash_value); }
#include "sflstr.h" char * strconvch ( char *string, char from, char to)
Converts all instances of one character in a string to some other character. Returns string. Does nothing if the string is NULL.
{ char *scan; if (string) { scan = string; while (*scan) { if (*scan == from) *scan = to; scan++; } } return (string); }
#include "sflstr.h" char * xstrcat ( char *dest, const char *src, ...)
Concatenates multiple strings into a single result. Eg. xstrcat (buffer, "A", "B", NULL) stores "AB" in buffer. Returns dest. Append the string to any existing contents of dest. From DDJ Nov 1992 p. 155, with adaptions.
{ char *feedback = dest; va_list va; ASSERT (dest); while (*dest) /* Find end of dest string */ dest++; va_start (va, src); while (src) { while (*src) *dest++ = *src++; src = va_arg (va, char *); } *dest = '\0'; /* Append a null character */ va_end (va); return (feedback); }
#include "sflstr.h" char * xstrcpy ( char *dest, const char *src, ...)
Concatenates multiple strings into a single result. Eg. xstrcpy (buffer, "A", "B", NULL) stores "AB" in buffer. Returns dest. Any existing contents of dest are cleared. If the dest buffer is NULL, allocates a new buffer with the required length and returns that. The buffer is allocated using mem_alloc(), and should eventually be freed using mem_free() or mem_strfree(). Returns NULL if the was insufficient memory to allocate the new string.
{ const char *src_ptr; va_list va; size_t dest_size; /* Size of concatenated strings */ /* Allocate new buffer if necessary */ if (dest == NULL) { va_start (va, src); /* Start variable args processing */ src_ptr = src; dest_size = 1; /* Allow for trailing null char */ while (src_ptr) { dest_size += strlen (src_ptr); src_ptr = va_arg (va, char *); } va_end (va); /* End variable args processing */ if ((dest = mem_alloc (dest_size)) == NULL) return (NULL); /* Not enough memory */ } /* Now copy strings into destination buffer */ va_start (va, src); /* Start variable args processing */ src_ptr = src; dest [0] = '\0'; while (src_ptr) { strcat (dest, src_ptr); src_ptr = va_arg (va, char *); } va_end (va); /* End variable args processing */ return (dest); }
#include "sflstr.h" int lexcmp ( const char *string1, const char *string2)
Performs an unsigned comparison of two strings without regard to the case of any letters in the strings. Returns a value that is
< 0 | if string1 is less than string2 |
== 0 | if string1 is equal to string2 |
> 0 | if string1 is greater than string2 |
{ int cmp; ASSERT (string1); ASSERT (string2); do { cmp = (byte) tolower (*string1) - (byte) tolower (*string2); } while (*string1++ && *string2++ && cmp == 0); return (cmp); }
#include "sflstr.h" int lexncmp ( const char *string1, const char *string2, const int count)
Performs an unsigned comparison of two strings without regard to the case of specified number of letters in the strings. Returns a value that is
< 0 | if string1 is less than string2 |
== 0 | if string1 is equal to string2 |
> 0 | if string1 is greater than string2 |
{ int cmp; char *end; ASSERT (string1); ASSERT (string2); end = (char *)string1 + count; do { cmp = (byte) tolower (*string1) - (byte) tolower (*string2); } while (*string1++ && *string2++ && cmp == 0 && string1 < end); return (cmp); }
#include "sflstr.h" int lexwcmp ( const char *string1, const char *pattern)
Compares two strings ignoring case, and allowing wildcards in the second string (the pattern). Two special characters are recognised in the pattern: '?' matches any character in the string, and '*' matches the remainder of the string. Returns a value that is:
< 0 | if string1 is less than pattern |
== 0 | if string1 is equal to pattern |
> 0 | if string1 is greater than pattern |
{ int cmp = 0; ASSERT (string1); ASSERT (pattern); do { if (*pattern != '?' && *pattern != '*') cmp = (byte) tolower (*string1) - (byte) tolower (*pattern); } while (*string1++ && *pattern++ && cmp == 0 && *pattern != '*'); return (cmp); }
#include "sflstr.h" char * soundex ( const char *string)
Calculates the SOUNDEX code for the string. Returns the address of a static area that holds the code. This area is overwritten by each call to the soundex function. The SOUNDEX encoding converts letters to uppercase, and translates each letter according to this table: A0 B1 C2 D3 E0 F1 G2 H0 I0 J2 K2 L4 M5 N5 O0 P1 Q2 R6 S2 T3 U0 V1 W0 X2 Y0 Z2. Non-letters are ignored, letters that translate to zero, and multiple occurences of the same value are also ignored. This function always returns a 4-letter encoding: the first letter of the string followed by the first three significant digits.
printf ("Soundex of %s = %s\n", argv [1], soundex (argv [1]));
{ ASSERT (string); return (soundexn (string, 4, FALSE)); }
#include "sflstr.h" char * soundexn ( const char *string, int size, Bool fold)
Calculates the SOUNDEX code for the string. Returns the address of a static area that holds the code. This area is overwritten by each call to the soundex function. The SOUNDEX encoding converts letters to uppercase, and translates each letter according to this table: A0 B1 C2 D3 E0 F1 G2 H0 I0 J2 K2 L4 M5 N5 O0 P1 Q2 R6 S2 T3 U0 V1 W0 X2 Y0 Z2. Non-letters are ignored, letters that translate to zero, and multiple occurences of the same value are also ignored. This function returns a N-letter encoding: the first letter of the string followed by the first N-1 significant digits. N may not be greater than SOUNDEX_MAX (100). If the fold argument is true, includes the first letter in the calculated digits, else starts with the second letter.
{ # define SOUNDEX_MAX 100 # define SOUNDEX_TABLE "00000000000000000000000000000000" "00000000000000000000000000000000" "00123012002245501262301020200000" "00123012002245501262301020200000" "00000000000000000000000000000000" "00000000000000000000000000000000" "00000000000000000000000000000000" "00000000000000000000000000000000" static char *soundex_table = SOUNDEX_TABLE, /* ASCII-SOUNDEX conversion */ soundex_code [SOUNDEX_MAX + 1]; /* Letter + 3 digits */ int index; char last_value = 0, this_value; ASSERT (string); ASSERT (size > 0 && size <= SOUNDEX_MAX); /* Initialise the soundex code to a string of zeroes */ memset (soundex_code, '0', size); soundex_code [size] = '\0'; soundex_code [0] = toupper (*string); last_value = fold? 0: soundex_table [(byte) *string]; index = 1; /* Store results at [index] */ while (*string) { this_value = soundex_table [(byte) *string++]; if (this_value == last_value /* Ignore doubles */ || this_value == '0') /* and 'quiet' letters */ { last_value = this_value; continue; } last_value = this_value; soundex_code [index++] = this_value; if (index == size) /* Up to size result characters */ break; } return (soundex_code); }
#include "sflstr.h" DESCR * strt2descr ( char **table)
Converts a table of strings into a single block of memory. The input table consists of an array of null-terminated strings, terminated in a null pointer. Returns the address of a DESCR block defined as: "typedef struct {size_t size; byte *data} DESCR;". Allocates the descriptor block using the mem_alloc() function; you must free it using mem_free() when you are finished with it. The strings are packed into the descriptor data field, each terminated by a null byte. The final string is terminated by two nulls. The total size of the descriptor is descr-> size + sizeof (DESCR). Note that if you omit the last null pointer in the input table, you will probably get an addressing error. Returns NULL if there was insufficient memory to allocate the descriptor block.
{ DESCR *descr; /* Allocated descriptor */ char *descr_ptr; /* Pointer into block */ size_t descr_size; /* Size of table */ int string_nbr; /* Index into string table */ ASSERT (table); /* Calculate the size of the descriptor */ descr_size = 1; /* Allow for final null byte */ for (string_nbr = 0; table [string_nbr]; string_nbr++) descr_size += strlen (table [string_nbr]) + 1; /* Allocate a descriptor and fill it with the strings */ descr = mem_alloc (descr_size + sizeof (DESCR)); if (descr) { descr-> size = descr_size; descr-> data = (byte *) descr + sizeof (DESCR); descr_ptr = (char *) descr-> data; for (string_nbr = 0; table [string_nbr]; string_nbr++) { strcpy (descr_ptr, table [string_nbr]); descr_ptr += strlen (descr_ptr) + 1; } *descr_ptr = '\0'; /* Add a null string */ } return (descr); }
#include "sflstr.h" char ** descr2strt ( const DESCR *descr)
Takes a descriptor prepared by strt2descr() and returns an array of strings pointers, terminated in a null pointer. The array is allocated using the mem_alloc() function. Each string is individually allocated. Thus, to free the string table you must call mem_free() for each entry in the table, except the last one, and then for the table. You can also call strtfree() to destroy the table in a single operation. Returns NULL if there was insufficient memory to allocate the table of strings.
{ char **table; int string_count, string_nbr; /* Index into string table */ char *descr_ptr; /* Pointer into block */ ASSERT (descr); /* Count the number of strings in the table */ descr_ptr = (char *) descr-> data; string_count = 0; while (*descr_ptr) /* Loop until we hit null string */ { string_count++; descr_ptr += strlen (descr_ptr) + 1; } /* Allocate a table and fill it with the strings */ table = mem_alloc ((string_count + 1) * sizeof (char *)); if (table) { descr_ptr = (char *) descr-> data; for (string_nbr = 0; string_nbr < string_count; string_nbr++) { table [string_nbr] = mem_strdup (descr_ptr); descr_ptr += strlen (descr_ptr) + 1; } table [string_count] = NULL; /* Store final null pointer */ } return (table); }
#include "sflstr.h" void strtfree ( char **table)
Releases a table of strings as created by descr2strt() or a similar function. If the argument is null, does nothing.
{ int string_nbr; /* Index into string array */ if (table) { for (string_nbr = 0; table [string_nbr]; string_nbr++) mem_free (table [string_nbr]); mem_free (table); } }
#include "sflstr.h" char * removechars ( char *strbuf, char *chrstorm)
Removes known chars from a string. Returns pointer to head of the buffer. Submitted by Scott Beasley jscottb@infoave.com
{ char *offset; offset = (char *)NULL; while (*strbuf) { offset = strpbrk (strbuf, chrstorm); if (offset) strcpy (offset, (offset + 1)); else break; } return strbuf; }
#include "sflstr.h" char * replacechrswith ( char *strbuf, char *chrstorm, char chartorlcwith)
Subsitutes known char(s)in a string with another. Returns pointer to head of the buffer. Submitted by Scott Beasley jscottb@infoave.com
{ char *offset; offset = (char *)NULL; while (*strbuf) { offset = strpbrk (strbuf, chrstorm); if (offset) { *(offset)= chartorlcwith; } else break; } return strbuf; }
#include "sflstr.h" char * insertstring ( char *strbuf, char *chrstoins, int pos)
Inserts a string into another string. Returns a pointer to head of the buffer. Submitted by Scott Beasley jscottb@infoave.com
{ memmove ((strbuf + pos)+ strlen (chrstoins), (strbuf + pos), strlen ((strbuf + pos))+ 1); memcpy ((strbuf + pos), chrstoins, strlen (chrstoins)); return strbuf; }
#include "sflstr.h" char * insertchar ( char *strbuf, char chrtoins, int pos)
Inserts a char into a string. Returns a pointer to head of the buffer. Submitted by Scott Beasley jscottb@infoave.com
{ memmove ((strbuf + pos)+ 1, (strbuf + pos), strlen ((strbuf + pos))+ 1); *(strbuf + pos)= chrtoins; return strbuf; }
#include "sflstr.h" char * leftfill ( char *strbuf, char chrtofill, unsigned len)
Pads a string to the left, to a know length, with the given char value. Returns a pointer to head of the buffer. Submitted by Scott Beasley jscottb@infoave.com
{ while (strlen (strbuf)< len) { insertchar (strbuf, chrtofill, 0); } return strbuf; }
#include "sflstr.h" char * rightfill ( char *strbuf, char chrtofill, unsigned len)
Pads a string to the right, to a known length, with the given char value. Returns a pointer to head of the buffer. Submitted by Scott Beasley jscottb@infoave.com
{ while (strlen (strbuf)< len) { insertchar (strbuf, chrtofill, strlen (strbuf)); } return strbuf; }
#include "sflstr.h" char * trim ( char *strin)
Eats the whitespace's from the left and right side of a string. This function maintains a proper pointer head. Returns a pointer to head of the buffer. Submitted by Scott Beasley jscottb@infoave.com
{ ltrim (strin); strcrop (strin); return strin; }
#include "sflstr.h" char * ltrim ( char *string)
Deletes leading white spaces in string, and returns a pointer to the first non-blank character. If this is a null, the end of the string was reached.
{ ASSERT (string); while (isspace(*string)) deletechar(string,0); return string; }
#include "sflstr.h" char * searchreplace ( char *strbuf, char *strtofnd, char *strtoins)
A case insensitive search and replace. Searches for all occurances of a string, and replaces it with another string. Returns a pointer to head of the buffer. Submitted by Scott Beasley jscottb@infoave.com
{ char *offset, *strbase; offset = strbase = (char *)NULL; while (*strbuf) { offset = stricstr (!offset ? strbuf : strbase, strtofnd); if (offset) { strbase = (offset + strlen (strtoins)); strcpy (offset, (offset + strlen (strtofnd))); memmove (offset + strlen (strtoins), offset, strlen (offset) + 1); memcpy (offset, strtoins, strlen (strtoins)); } else break; } return strbuf; }
#include "sflstr.h" char * deletestring ( char *strbuf, char *strtodel, int ignorecase)
Deletes all occurances of one string, in another string. Returns a pointer to head of the buffer. Submitted by Scott Beasley jscottb@infoave.com
{ char *offset; offset = (char *)NULL; while (*strbuf) { if (!ignorecase) offset = stricstr (strbuf, strtodel); else offset = strstr (strbuf, strtodel); if (offset) { strcpy (offset, (offset + strlen (strtodel))); } else break; } return strbuf; }
#include "sflstr.h" char * getstrfld ( char *strbuf, int fldno, int ofset, char *sep, char *retstr)
Gets a sub-string from a formated string. nice strtok replacement. usage: char strarray[] = { "123,456,789,abc" }; char strretbuff[4]; getstrfld (strarray, 2, 0, ",", strretbuff); This would return the string "789" and place it also in strretbuff. Returns a NULL if fldno is out of range, else returns a pointer to head of the buffer. Submitted by Scott Beasley jscottb@infoave.com
{ char *offset, *strptr; int curfld; offset = strptr = (char *)NULL; curfld = 0; strbuf += ofset; while (*strbuf) { strptr = !offset ? strbuf : offset; offset = strpbrk ((!offset ? strbuf : offset), sep); if (offset) offset++; else if (curfld != fldno) { *retstr = (char)NULL; break; } if (curfld == fldno) { strncpy (retstr, strptr, (int)(!offset ? strlen (strptr)+ 1 : (int)(offset - strptr))); if (offset) retstr[offset - strptr - 1] = 0; break; } curfld++; } return retstr; }
#include "sflstr.h" char * setstrfld ( char *strbuf, int fldno, int ofset, char *sep, char *strtoins)
Inserts a string into a fomated string. usage: char strsrray[26] = { "this is a test." }; setstrfld (strsrray, 2, 0, " ", "big "); result: this is a big test. Does nothing if fldno is out of range, else returns pointer to head of the buffer. Returns a pointer to head of the buffer. Submitted by Scott Beasley jscottb@infoave.com
{ char *offset, *strptr, *strhead; int curfld; offset = strptr = (char *)NULL; curfld = 0; strhead = strbuf; strbuf += ofset; while (*strbuf) { strptr = !offset ? strbuf : offset; offset = strpbrk ((!offset ? strbuf : offset), sep); if (offset) offset++; if (curfld == fldno) { insertstring (strptr, strtoins, (int)(!offset ? strlen (strptr): (int)(offset - strptr))); break; } curfld++; } return strhead; }
#include "sflstr.h" int getstrfldlen ( char *strbuf, int fldno, int ofset, char *sep)
Get the length of as a field in a string. Used mainly for getting the len to malloc mem to call getstrfld with. Returns the length of the field. Submitted by Scott Beasley jscottb@infoave.com
{ char *offset, *strptr; int curfld, retlen = 0; offset = strptr = (char *)NULL; curfld = 0; strbuf += ofset; while (*strbuf) { strptr = !offset ? strbuf : offset; offset = strpbrk ((!offset ? strbuf : offset), sep); if (offset) offset++; else if (curfld != fldno) { retlen = 0; break; } if (curfld == fldno) { retlen = (int)(!offset ? strlen (strptr) + 1 : (int)(offset - strptr)); break; } curfld++; } return retlen; }
#include "sflstr.h" char * findstrinfile ( FILE *fp, char *strtofind, char *strretstr, int *iLnNo)
Find's a string inside a text file and reads the line in and sets the file pointer to the beginning of that line. Assumes the line length to be <= 1024 bytes. Returns a pointer to head of the return buffer, and the file postion will be at the start of the found string. If the strretstr param is != NULL then strretstr will contain the line that the search string was found in. Submitted by Scott Beasley jscottb@infoave.com
{ char strline[1025]; int nfnd = 0; long lfpos; if (strretstr) *strretstr = 0; while (1) { lfpos = ftell (fp); fgets (strline, 1024, fp); trim (strline); if (!*strline) continue; if (iLnNo) (*iLnNo)++; if (stricstr (strline, strtofind)) { if (strretstr) { strcpy (strretstr, strline); } fseek (fp, lfpos, SEEK_SET); nfnd = 1; break; } if (feof (fp)) break; } if (strretstr) return strretstr; else return (char *)nfnd; }
#include "sflstr.h" char * getequval ( char *strline, char *strretstr)
get the everything on a line past a '='. char strtest[] = { "progpath=c:\sfl"); char strret[256]; getequval (strtest, strret); This would return: "c:\sfl". Returns a pointer to head of the return buffer. Submitted by Scott Beasley jscottb@infoave.com
{ char *stroffset; stroffset = strstr (strline, "="); if (stroffset) ltrim ((stroffset + 1)); else return (char *)NULL; return strcpy (strretstr, (stroffset && *(stroffset + 1))? (stroffset + 1): ""); }
#include "sflstr.h" int matchtable ( char *strbuf, char *strmatch, char *strsept, int ncase)
Function to compare a string with a set of strings. iret = matchtable (strname, "bill|william|billy", "|", IGNORECASE); If strname == "william", then matchtable would return 1. Returns >= 0 if match is found. a -1 if no match is found. Submitted by Scott Beasley jscottb@infoave.com
{ int nstate = -1, cnt = 0, icmpres; int ilen; char *strtemp; ASSERT (strbuf); ASSERT (strmatch); ASSERT (strsept); while (1) { ilen = getstrfldlen (strmatch, cnt, 0, strsept); strtemp = (char *) malloc (sizeof (char) * ilen + 1); ASSERT (strtemp); getstrfld (strmatch, cnt, 0, strsept, strtemp); if (*strtemp) { trim (strtemp); if (!ncase) { icmpres = lexcmp (strbuf, strtemp); } else { icmpres = strcmp (strbuf, strtemp); } if (!icmpres) { nstate = cnt; break; } else { if (!strcmp (strbuf, strtemp)) { nstate = cnt; break; } } } else { nstate = -1; break; } cnt++; free (strtemp); } return nstate; }
#include "sflstr.h" char * stringreplace ( char *strbuf, char *strpattern)
This function searches for known strings, and replaces them with another string. stringreplace (strfilename, "sqv|sqr,ruv|run,h_v|h"); This example would replace all occurences of sqv, with sqr, ruv with run and h_v with h. Returns pointer to head of the return buffer. Submitted by Scott Beasley jscottb@infoave.com
{ int ilen, ifld = 0; char *strsrch, *strrpl, *strpat; if (!strpattern) return strbuf; while (1) { ilen = getstrfldlen (strpattern, ifld, 0, ","); if (!ilen) break; strpat = (char *)malloc (ilen + 1); getstrfld (strpattern, ifld, 0, ",", strpat); ifld++; ilen = getstrfldlen (strpat, 0, 0, "|"); strsrch = (char *)malloc (ilen + 1); getstrfld (strpat, 0, 0, "|", strsrch); ilen = getstrfldlen (strpat, 1, 0, "|"); strrpl = (char *)malloc (ilen + 1); getstrfld (strpat, 1, 0, "|", strrpl); searchreplace (strbuf, strsrch, strrpl); free (strsrch); free (strrpl); free (strpat); } return strbuf; }
#include "sflstr.h" char * wordwrapstr ( char *strbuff, int iwid)
Function that does word wraping of a string at or less than iwid. Breaks up a string on word boundaries by placing '\n' in the string. Returns a pointer to head of the return buffer. Submitted by Scott Beasley jscottb@infoave.com
{ char *strtmp = strbuff; int icnt = 0; replacechrswith (strbuff, "\n", ' '); while (*strtmp) { if ((int)strlen (strtmp) > (int)iwid) { icnt = iwid; while (*(strtmp + icnt)) { if (strchr (" .?;!,", *(strtmp + icnt))) { ltrim ((strtmp + icnt)); insertchar (strtmp, '\n', icnt); strtmp += icnt + 1; break; } icnt--; if (!icnt) { if (strchr (" .?;!,", *(strtmp + icnt))) { ltrim ((strtmp + iwid)); insertchar (strtmp, '\n', iwid); strtmp += iwid + 1; break; } } } } else break; } return strbuff; }
#include "sflstr.h" char * stricstr ( const char *str1, const char *str2)
A case insensitive strstr. Returns a pointer to head of the str1. Submitted by Scott Beasley jscottb@infoave.com
{ char *strtmp = (char *)str1; int iret = 1; while (*strtmp) { if (strlen (strtmp)>= strlen (str2)) { iret = lexncmp (strtmp, str2, strlen (str2)); } else { break; } if (!iret) { break; } strtmp++; } return !iret ? strtmp : (char *)NULL; }
#include "sflstr.h" int strtempcmp ( const char *str1, const char *strPat)
Compares a string to a template. Template chars and there functions: # or 9 = Number. A or _ = Alpha. @ = Alphanumeric char = Literal. Char would be the literal to use. ie: "\%" - looks for a % in that postion Returns 0 if == to the template and 1 if != to the template. Submitted by Scott Beasley jscottb@infoave.com
{ int ires = 1; while (*str1 && *strPat) { switch ((int)*strPat) { case '#': case '9': ires = isdigit ((int)*str1); break; case 'A': case '_': ires = isalpha ((int)*str1); break; case '@': ires = isalnum ((int)*str1); break; case ' ': ires = isspace ((int)*str1); break; case '\\': strPat++; if (*str1 != *strPat) { ires = 1; } break; default: break; } if (!ires) { break; } str1++; strPat++; } return ires ? 0 : 1; }
#include "sflstr.h" int istoken ( char **strLine, const char *strtoken, int *iWasToken)
Eats strToEat from strBuff only if it begins with contents of strToEat, and returns a 0 or 1 to tell what it did. char strBuff[] = { "select * from mytbl;" }; int iWasToken; istoken (&strBuff, "SELECT", &iWasToken); On return here iWasToken would == 1, and strBuff would be: " * from mytbl;" If the token is not found, then strBuff will not be affected, and a 0 will be returned. Submitted by Scott Beasley jscottb@infoave.com
{ int iRet; char cChar; iRet = lexncmp (*strLine, strtoken, strlen (strtoken)); if (!iRet) { cChar = *(*strLine + strlen (strtoken)); if (!isalpha ((int)cChar)&& cChar != '_') { iRet = *iWasToken = 1; strcpy (*strLine, (*strLine + strlen (strtoken))); } else iRet = *iWasToken = 0; } else iRet = *iWasToken = 0; return iRet; }
#include "sflstr.h" char * eatstr ( char **strBuff, char *strToEat)
Eats strToEat from strBuff only if it begins with contents of strToEat. char strBuff[] = { "select * from mytbl;" }; eatstr (&strBuff, "SELECT"); On return here strBuff would be: " * from mytbl;" If the token is not found, then strBuff will not be affected and a NULL char * will be returned, but any white spaces on the left of strBuff would be trimed. Submitted by Scott Beasley jscottb@infoave.com
{ int iWasToken; ltrim (*strBuff); istoken (strBuff, strToEat, &iWasToken); return iWasToken ? *strBuff : (char *)NULL; }
#include "sflstr.h" char * eatstrpast ( char **strBuff, char *strCharsToEatPast)
Eats chars past first occurrence of one of the chars contained in strCharsToEatPast. char strBuff[] = { " , 456, 789" }; eatstrpast (&strBuff, ","); On return here strBuff would be: " 456, 789". Returns a pointer to head of the input buffer. Submitted by Scott Beasley jscottb@infoave.com
{ ltrim (*strBuff); while (**strBuff && strchr (strCharsToEatPast, **strBuff)) deletechar (*strBuff, 0); return *strBuff; }
#include "sflstr.h" char * movestrpast ( char **strBuff, char cCharToEatPast)
Eats chars past first occurrence of one of the chars contained in strCharsToEatPast. char strBuff[] = { "123, 456, 789" }; movestrpast (&strBuff, ","); On return here strBuff would be: " 456, 789". Returns a pointer to head of the input buffer. Submitted by Scott Beasley jscottb@infoave.com
{ ltrim (*strBuff); while (**strBuff && **strBuff != cCharToEatPast) deletechar (*strBuff, 0); if (**strBuff && **strBuff == cCharToEatPast) deletechar (*strBuff, 0); return *strBuff; }
#include "sflstr.h" char * eatchar ( char **strBuff, char cChar)
Trims white spaces and eats just past occurrence of cChar. If contents of cChar is not found then only white spaces are trimmed. char strBuff[] = { "('test', 5)" }; eatchar (&strBuff, '('); On return here strBuff would be: "'test', 5)". Returns a pointer to head of the input buffer. Submitted by Scott Beasley jscottb@infoave.com
{ ltrim (*strBuff); if (**strBuff && **strBuff == cChar) deletechar (*strBuff, 0); return *strBuff; }
#include "sflstr.h" int isoneoftokens ( char **strbuf, char *strmat, char *strsep, int *iWasToken)
Eats strToEat from strBuff only if it begins with contents of strToEat, and returns a 0 or 1 to tell what it did. Returns 0 if nothing found, and >= 1 which is an index of the one found. char strBuff[] = { "select * from mytbl;" }; int iWasToken; isoneoftokens (&strBuff, "INSERT|SELECT|DELETE", "|", &iWasToken); On return here iWasToken would == 1, and strBuff would be: " * from mytbl;" and the return value would be 2. If the token is not found, then strBuff will not be affected, and a 0 will be returned. Submitted by Scott Beasley jscottb@infoave.com
{ int nstate = 0, cnt = 0, icmpres; int iLen; char *strtemp, cChar; while (1) { iLen = getstrfldlen (strmat, cnt, 0, strsep); strtemp = (char *) malloc (iLen + 1); getstrfld (strmat, cnt, 0, strsep, strtemp); if (*strtemp) { trim (strtemp); icmpres = lexncmp (*strbuf, strtemp, strlen (strtemp)); if (!icmpres) { cChar = *(*strbuf + strlen (strtemp)); if (!isalpha ((int)cChar)&& cChar != '_') { *iWasToken = cnt + 1; strcpy (*strbuf, (*strbuf + strlen (strtemp))); nstate = cnt + 1; } break; } else { if (!lexncmp (*strbuf, strtemp, strlen (strtemp))) { cChar = *(*strbuf + strlen (strtemp)); if (!isalpha ((int)cChar)&& cChar != '_') { *iWasToken = cnt + 1; strcpy (*strbuf, (*strbuf + strlen (strtemp))); nstate = cnt + 1; } break; } } } else { *iWasToken = 0; nstate = 0; break; } cnt++; free (strtemp); } return nstate; }
Filename: sflsock.h
Package: Standard Function Library (SFL)
Written: 96/02/03 iMatix SFL project team sfl@imatix.com
Revised: 98/03/20
Copyright: Copyright (c) 1991-98 iMatix
Provides functions to create, read, and write TCP and UDP sockets. Encapsulates system dependencies. Tested under MS Winsock, UNIX (Linux, AIX, SunOs), OpenVMS. Some of the code in this module was based on the book "Internetworking With TCP/IP Volume III: Client-Server Programming And Applications BSD Socket Version" by Douglas E. Comer and David L. Stevens, published 1993 by Prentice-Hall Inc. ISBN 0-13-020272-X. Defines sock_t which you should use for all sockets. If you need to call a native socket function, use a (SOCKET) cast on the sock_t handle.
sflsock.h defines these symbols, possibly conditionally:
Symbol: | Defined as: |
---|---|
AF_INET | 0 |
DNS_PORT | 53 /* Domain Name server port */ |
EAGAIN | EWOULDBLOCK |
ECONNRESET | (various) |
EINPROGRESS | (various) |
EPIPE | -1 |
EWOULDBLOCK | (various) |
FAKE_SOCKETS | 1 |
FD_CLR(d,set) | ((set)->__bits[__FDELT(d)] &= ~__FDMASK(d)) |
FD_ISSET(d,set) | ((set)->__bits[__FDELT(d)] & __FDMASK(d)) |
FD_SET(d,set) | ((set)->__bits[__FDELT(d)] |= __FDMASK(d)) |
FD_SETSIZE | 256 |
FD_SETTYPE | (various) |
FD_ZERO(set) | ((void) memset((void *) (set), 0, sizeof(fd_set))) |
INADDR_ANY | 0 |
INADDR_NONE | -1 /* constant */ |
INVALID_SOCKET | (sock_t) -1 /* Invalid socket handle */ |
IP_BADHOST | 2 /* Host not known */ |
IP_BADPROTOCOL | 4 /* Invalid protocol specified */ |
IP_BADSERVICE | 3 /* Service or port not known */ |
IP_BINDERROR | 7 /* Error binding socket */ |
IP_CONNECTERROR | 6 /* Error making connection */ |
IP_LISTENERROR | 8 /* Error preparing to listen */ |
IP_NOERROR | 0 /* No errors */ |
IP_NOSOCKETS | 1 /* Sockets not supported */ |
IP_SOCKETERROR | 5 /* Error creating socket */ |
MAXHOSTNAMELEN | 256 /* constant */ |
NFDBITS | (sizeof (unsigned long int) * 8) |
SOCKET_ERROR | -1 /* Error on socket function */ |
SOCKET_LOOPBACK | 0x7f000001L /* Loopback address 127.0.0.1 */ |
_SFLSOCK_INCLUDED | TRUE |
__FDELT(d) | ((d) / NFDBITS) |
__FDMASK(d) | (1 << ((d) % NFDBITS)) |
htonl(x) | (various) |
htons(x) | (various) |
inet_addr(x) | 1 |
inet_ntoa(x) | "127.0.0.1" |
ntohl(x) | (various) |
ntohs(x) | (various) |
select(n,rf,wf,xf,t) | 1 |
sockerrno | (various) |
socket_hostaddr(handle) | socket_peeraddr (handle) |
Type name: | Defined as: |
---|---|
SOCKET | int |
sock_t | qbyte |
#include "sflsock.h" int sock_init (void)
Initialise the internet protocol. On most systems this is a null call. On some systems this loads dynamic libraries. Returns 0 0 if everything was okay, else returns SOCKET_ERROR. You should call sock term() when your program ends.
{ #if (defined (__WINDOWS__)) WORD wVersionRequested; /* We really want Winsock 1.1 */ WSADATA wsaData; wVersionRequested = 0x0101; /* ... but we'll take 1.1 */ if (WSAStartup (wVersionRequested, &wsaData) == 0) return (0); else return ((int) SOCKET_ERROR); #elif (defined (DOES_SOCKETS)) return (0); #elif (defined (FAKE_SOCKETS)) return (0); #else connect_error_value = IP_NOSOCKETS; return ((int) SOCKET_ERROR); /* Sockets not supported */ #endif }
#include "sflsock.h" int sock_term (void)
Shuts-down the internet protocol. On most systems this is a null call. On some systems this unloads dynamic libraries. Returns -1 if there was an error, or 0 if everything was okay. See sock init().
{ #if (defined (__WINDOWS__)) WSACleanup (); #endif return (0); }
#include "sflsock.h" sock_t passive_TCP ( const char *service, /* Service name or port as string */ int queue_length /* Queue length for listen() */ )
Creates a passive bound TCP socket for the specified service. Returns socket number or INVALID_SOCKET. If it returns INVALID_SOCKET, you can get the reason for the error by calling connect error (). This may be one of:
IP NOSOCKETS | Sockets not supported on this system |
IP BADSERVICE | Service cannot be converted to port number |
IP BADPROTOCOL | Cannot understand protocol name |
IP SOCKETERROR | Cannot create the passive socket |
IP BINDERROR | Cannot bind to the port |
IP LISTENERROR | Cannot listen to port |
{ ASSERT (service && *service); ASSERT (queue_length > 0); return (passive socket (service, "tcp", queue_length)); }
#include "sflsock.h" sock_t passive_UDP ( const char *service /* Service name or port as string */ )
Creates a passive UDP socket for the specified service. Returns socket number or INVALID_SOCKET. If it returns INVALID_SOCKET, you can get the reason for the error by calling connect error (). This may be one of:
IP NOSOCKETS | Sockets not supported on this system |
IP BADSERVICE | Service cannot be converted to port number |
IP BADPROTOCOL | Cannot understand protocol name |
IP SOCKETERROR | Cannot create the passive socket |
IP BINDERROR | Cannot bind to the port |
{ ASSERT (service && *service); return (passive socket (service, "udp", 0)); }
#include "sflsock.h" sock_t passive_socket ( const char *service, /* Service name or port as string */ const char *protocol, /* Protocol "tcp" or "udp" */ int queue_length /* Queue length for TCP sockets */ )
Creates a passive TCP or UDP socket. This function allows a server program to create a master socket, so that connections can be accepted. Used by the passive_TCP and passive_UDP functions. Returns a socket number or INVALID_SOCKET. If it returns INVALID_SOCKET, you can get the reason for the error by calling connect error (). This may be one of:
IP NOSOCKETS | Sockets not supported on this system |
IP BADSERVICE | Service cannot be converted to port number |
IP BADPROTOCOL | Cannot understand protocol name |
IP SOCKETERROR | Cannot create the passive socket |
IP BINDERROR | Cannot bind to the port |
IP LISTENERROR | Cannot listen to port |
{ #if (defined (DOES_SOCKETS)) struct servent *pse; /* Service information entry */ struct sockaddr_in sin; /* Internet end-point address */ sock_t handle; /* Socket from socket() call */ ASSERT (service && *service); ASSERT (protocol && *protocol); ASSERT (queue_length > 0); connect_error_value = IP_NOERROR; /* Assume no errors */ memset ((void *) &sin, 0, sizeof (sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = ip_passive; ip_passive = INADDR_ANY; /* Reset passive address */ /* Map service name to port number */ pse = getservbyname (service, protocol); if (pse) sin.sin_port = htons ((dbyte) (ntohs (pse-> s_port) + ip_portbase)); else { sin.sin_port = atoi (service); if (sin.sin_port + ip_portbase > 0) sin.sin_port = htons ((dbyte) (sin.sin_port + ip_portbase)); else { connect_error_value = IP_BADSERVICE; return (INVALID_SOCKET); } } handle = create socket (protocol); if (handle == INVALID_SOCKET) /* Cannot create the socket */ return (INVALID_SOCKET); /* Bind the socket */ if (bind ((SOCKET) handle, (struct sockaddr *) &sin, sizeof (sin)) == SOCKET_ERROR) { connect_error_value = IP_BINDERROR; return (INVALID_SOCKET); /* Cannot bind to port */ } /* Specify incoming queue length for stream socket */ if (streq (protocol, "tcp") && listen ((SOCKET) handle, queue_length) == SOCKET_ERROR) { connect_error_value = IP_LISTENERROR; return (INVALID_SOCKET); /* Cannot listen on port */ } return (handle); #elif (defined (FAKE_SOCKETS)) return (1); /* Return dummy handle */ #else connect_error_value = IP_NOSOCKETS; return (INVALID_SOCKET); /* Sockets not supported */ #endif }
#include "sflsock.h" sock_t create_socket ( const char *protocol /* Protocol "tcp" or "udp" */ )
Creates a TCP or UDP socket. The socket is not connected. To use with TCP services you must bind or connect the socket. You can use the socket with UDP services - e.g. read UDP () - immediately. Returns a socket number or INVALID_SOCKET, in which case you can get the reason for the error by calling connect error (). This may be one of:
IP NOSOCKETS | Sockets not supported on this system |
IP BADPROTOCOL | Cannot understand protocol name |
IP SOCKETERROR | Cannot create the socket |
{ #if (defined (DOES_SOCKETS)) struct protoent # if (defined (__VMS__)) ppe_struct = { NULL, NULL, 0 }, # endif *ppe; /* Protocol information entry */ int sock_type, /* Type of socket we want */ true_value = 1; /* Boolean value for setsockopt() */ sock_t handle; /* Socket from socket() call */ ASSERT (protocol && *protocol); connect_error_value = IP_NOERROR; /* Assume no errors */ /* On older VAXen, getprotobyname() is not available */ # if (defined (__VMS__)) ppe = &ppe_struct; ppe-> p_proto = 0; # else /* Map protocol name to protocol number */ ppe = getprotobyname (protocol); if (ppe == NULL) /* Cannot get protocol entry */ { connect_error_value = IP_BADPROTOCOL; return (INVALID_SOCKET); } # endif /* Use protocol string to choose a socket type */ if (streq (protocol, "udp")) sock_type = SOCK_DGRAM; else sock_type = SOCK_STREAM; /* Allocate a socket */ handle = (sock_t) socket (AF_INET, sock_type, ppe-> p_proto); if (handle == INVALID_SOCKET) /* Cannot create passive socket */ { connect_error_value = IP_SOCKETERROR; return (INVALID_SOCKET); } # if (!defined (__WINDOWS__)) /* On BSD-socket systems we need to do this to allow the server to * restart on a previously-used socket, without an annoying timeout * of several minutes. With winsock the reuseaddr option lets the * server work with an already-used socket (!), so we don't do it. */ setsockopt ((SOCKET) handle, SOL_SOCKET, SO_REUSEADDR, (char *) &true_value, sizeof (true_value)); # endif prepare_socket (handle); /* Ready socket for use */ return (handle); #elif (defined (FAKE_SOCKETS)) return (1); /* Return dummy handle */ #else connect_error_value = IP_NOSOCKETS; return (INVALID_SOCKET); /* Sockets not supported */ #endif }
#include "sflsock.h" sock_t connect_TCP ( const char *host, /* Host name */ const char *service /* Service name */ )
Creates a TCP socket and connects it to a specified host and service. Returns a socket number or INVALID_SOCKET. In that case you can get the reason for the error by calling connect error (). This may be:
IP NOSOCKETS | Sockets not supported on this system |
IP BADHOST | Host is not known |
IP BADPROTOCOL | Cannot understand protocol name |
IP SOCKETERROR | Cannot open a socket |
IP CONNECTERROR | Cannot connect socket |
sock_t handle; handle = connect_TCP ("", "8080"); handle = connect_TCP (NULL, "echo"); handle = connect_TCP ("www.imatix.com", "http");
{ ASSERT (service && *service); return (connect socket (host, /* We have a host name */ service, /* We have a service name */ "tcp", /* Protocol is TCP */ NULL, /* No prepared address */ 3, 0)); /* 3 retries, no waiting */ }
#include "sflsock.h" sock_t connect_UDP ( const char *host, /* Host name */ const char *service /* Service name */ )
Creates a UDP socket and connects it to a specified host and service. Returns a socket number or INVALID_SOCKET. In that case you can get the reason for the error by calling connect error (). This may be:
IP NOSOCKETS | Sockets not supported on this system |
IP BADHOST | Host is not known |
IP BADPROTOCOL | Cannot understand protocol name |
IP SOCKETERROR | Cannot open a socket |
IP CONNECTERROR | Cannot connect socket |
sock_t handle; handle = connect_UDP ("", "7"); handle = connect_UDP (NULL, "echo"); handle = connect_UDP ("imatix.com", "echo");
{ ASSERT (service && *service); return (connect socket (host, /* We have a host name */ service, /* We have a service name */ "udp", /* Protocol is UDP */ NULL, /* No prepared address */ 3, 0)); /* 3 retries, no waiting */ }
#include "sflsock.h" sock_t connect_TCP_fast ( const struct sockaddr_in *sin /* Socket address structure */ )
Creates a TCP socket and connects it to a specified host/port address. Returns a socket number or INVALID_SOCKET. In that case you can get the reason for the error by calling connect error (). This may be:
IP NOSOCKETS | Sockets not supported on this system |
IP BADHOST | Host is not known |
IP BADPROTOCOL | Cannot understand protocol name |
IP SOCKETERROR | Cannot open a socket |
IP CONNECTERROR | Cannot connect socket |
{ ASSERT (sin); return (connect socket (NULL, /* No host name */ NULL, /* No service name */ "tcp", /* Protocol is TCP */ sin, /* We have a prepared address */ 1, 0)); /* 1 retry, no waiting */ }
#include "sflsock.h" sock_t connect_UDP_fast ( const struct sockaddr_in *sin /* Socket address structure */ )
Creates a UDP socket and connects it to a specified host/port address. Returns a socket number or INVALID_SOCKET. In that case you can get the reason for the error by calling connect error (). This may be:
IP NOSOCKETS | Sockets not supported on this system |
IP BADHOST | Host is not known |
IP BADPROTOCOL | Cannot understand protocol name |
IP SOCKETERROR | Cannot open a socket |
IP CONNECTERROR | Cannot connect socket |
{ ASSERT (sin); return (connect socket (NULL, /* No host name */ NULL, /* No service name */ "udp", /* Protocol is UDP */ sin, /* We have a prepared address */ 1, 0)); /* 1 retry, no waiting */ }
#include "sflsock.h" sock_t connect_socket ( const char *host, /* Name of host, "" = localhost */ const char *service, /* Service name or port as string */ const char *protocol, /* Protocol "tcp" or "udp" */ const struct sockaddr_in *host_addr, /* Socket address structure */ int retries_left, /* Max. number of retries */ int retry_delay /* Delay between retries */ )
Makes a connection to a remote TCP or UDP port. This allows a client program to start sending information to a server. Used by the connect_TCP and connect_UDP functions. Returns a socket number or INVALID_SOCKET. If it returns INVALID_SOCKET, you can get the reason for the error by calling connect error (). This may be one of:
IP NOSOCKETS | Sockets not supported on this system |
IP BADHOST | Host is not known |
IP BADPROTOCOL | Cannot understand protocol name |
IP SOCKETERROR | Cannot open a socket |
IP CONNECTERROR | Cannot connect socket |
struct sockaddr_in host_addr; sock_t handle; build_sockaddr (&host_addr, 32_bit_host, 16_bit_port); handle = connect_socket (NULL, NULL, "tcp", &host_addr, 3, 0);
{ #if (defined (DOES_SOCKETS)) struct sockaddr_in sin; /* Internet end-point address */ sock_t handle = 0; /* Created socket */ int rc; /* Return code from call */ Bool old_nonblock; /* Create non-blocking sockets */ connect_error_value = IP_NOERROR; /* Assume no errors */ /* Format sockaddr_in port and hostname, and quit if that failed */ if (service && strused (service)) { ASSERT (protocol && *protocol); if (address end point (host, service, protocol, &sin)) return (INVALID_SOCKET); } else { ASSERT (host_addr); sin = *host_addr; /* Fast connect requested */ } /* Connect socket and maybe retry a few times... */ old_nonblock = ip_nonblock; # if (defined (BLOCKING_CONNECT)) ip_nonblock = FALSE; /* Block on this socket */ # endif while (retries_left) { handle = create socket (protocol); if (handle == INVALID_SOCKET) /* Unable to open a socket */ { ip_nonblock = old_nonblock; return (INVALID_SOCKET); } rc = connect ((SOCKET) handle, (struct sockaddr *) &sin, sizeof (sin)); if (rc == 0) break; /* Connected okay */ else { # if (defined (__WINDOWS__)) if (WSAGetLastError () == WSAEWOULDBLOCK) # else if (errno == EINPROGRESS) # endif break; /* Still connecting, but okay */ } /* Retry if we have any attempts left */ close socket (handle); if (--retries_left == 0) /* Connection failed */ { connect_error_value = IP_CONNECTERROR; ip_nonblock = old_nonblock; return (INVALID_SOCKET); } sleep (retry_delay); } ip_nonblock = old_nonblock; prepare_socket (handle); /* Set final blocking mode */ return (handle); #elif (defined (FAKE_SOCKETS)) return (1); /* Return dummy handle */ #else connect_error_value = IP_NOSOCKETS; return (INVALID_SOCKET); /* Sockets not supported */ #endif }
#include "sflsock.h" int connect_to_peer ( sock_t handle, /* Socket to connect */ const struct sockaddr_in *sin /* Socket address structure */ )
Connects an unconnected TCP or UDP socket to a peer specified by a sockaddr structure. Returns 0 if the connection succeeded, or SOCKET_ERROR if there was a problem. In the latter case you can get the reason for the error by calling sockmsg().
{ #if (defined (DOES_SOCKETS)) int rc; /* Return code from call */ Bool old_nonblock; /* Create non-blocking sockets */ ASSERT (sin); old_nonblock = ip_nonblock; # if (defined (BLOCKING_CONNECT)) ip_nonblock = FALSE; /* Block on this socket */ # endif rc = connect ((SOCKET) handle, (struct sockaddr *) sin, sizeof (*sin)); ip_nonblock = old_nonblock; prepare_socket (handle); /* Set final blocking mode */ # if (defined (__WINDOWS__)) return (win_error (rc)); # else return (rc); # endif #else connect_error_value = IP_NOSOCKETS; return ((int) SOCKET_ERROR); /* Sockets not supported */ #endif }
#include "sflsock.h" int address_end_point ( const char *host, /* Name of host, "" = localhost */ const char *service, /* Service name or port as string */ const char *protocol, /* Protocol "tcp" or "udp" */ struct sockaddr_in *sin /* Block for formatted address */ )
Formats an address block (struct sockaddr_in) for the specified host and service (port) information. Returns 0 if okay, SOCKET_ERROR if there was an error, in which case you can call connect error () to get the reason for the error. This may be one of:
IP NOSOCKETS | Sockets not supported on this system |
IP BADHOST | Host is not known |
{ #if (defined (DOES_SOCKETS)) struct hostent *phe; /* Host information entry */ struct servent *pse; /* Service information entry */ char hostname [MAXHOSTNAMELEN + 1]; /* Name of this system */ int feedback = 0; /* Assume everything works */ ASSERT (service && *service); ASSERT (protocol && *protocol); ASSERT (sin); connect_error_value = IP_NOERROR; /* Assume no errors */ memset ((void *) sin, 0, sizeof (*sin)); sin-> sin_family = AF_INET; /* Map service name to a port number */ pse = getservbyname (service, protocol); if (pse) sin-> sin_port = pse-> s_port + ip_portbase; else sin-> sin_port = htons ((short) (atoi (service) + ip_portbase)); /* Map host name to IP address, allowing for dotted decimal */ if (host && strused (host)) strcpy (hostname, host); else strcpy (hostname, "127.0.0.1"); /* Check if it's a valid IP address first */ sin-> sin_addr.s_addr = inet_addr (hostname); if (sin-> sin_addr.s_addr == INADDR_NONE) { /* Not a dotted address -- try to translate the name */ phe = gethostbyname (hostname); if (phe) memcpy ((void *) &sin-> sin_addr, phe-> h_addr, phe-> h_length); else { /* Cannot map to host */ connect_error_value = IP_BADHOST; feedback = (int) SOCKET_ERROR; } } return (feedback); #else connect_error_value = IP_NOSOCKETS; return ((int) SOCKET_ERROR); /* Sockets not supported */ #endif }
#include "sflsock.h" void build_sockaddr ( struct sockaddr_in *sin, /* Socket address structure */ qbyte host, /* 32-bit host address */ dbyte port /* 16-bit port number */ )
Builds a socket address structure from the specified host and port addresses. Does not return any value except the built structure.
{ ASSERT (sin); sin-> sin_family = AF_INET; sin-> sin_addr.s_addr = htonl (host); sin-> sin_port = htons (port); }
#include "sflsock.h" char * socket_localaddr ( sock_t handle)
Returns a string containing the local host address for the specified connected socket. The string is formatted as a string "n.n.n.n". Returns the address of a static string or a buffer that is overwritten by each call. If sockets are not supported, or there was an error, returns the loopback address "127.0.0.1".
{ #define NTOA_MAX 16 #if (defined (DOES_SOCKETS)) static char localaddr [NTOA_MAX + 1]; /* xxx.xxx.xxx.xxx */ struct sockaddr_in sin; /* Address of local system */ if (get sock addr (handle, &sin, NULL, 0)) return ("127.0.0.1"); else { strncpy (localaddr, inet_ntoa (sin.sin_addr), NTOA_MAX); return (localaddr); } #else return ("127.0.0.1"); #endif }
#include "sflsock.h" char * socket_peeraddr ( sock_t handle)
Returns a string containing the peer host address for the specified connected socket. The string is formatted as a string "n.n.n.n". Returns the address of a static string or a buffer that is overwritten by each call. If sockets are not supported, or there was an error, returns the loopback address "127.0.0.1".
{ #if (defined (DOES_SOCKETS)) static char peeraddr [NTOA_MAX + 1]; /* xxx.xxx.xxx.xxx */ struct sockaddr_in sin; /* Address of peer system */ if (get peer addr (handle, &sin, NULL, 0)) return ("127.0.0.1"); else { strncpy (peeraddr, inet_ntoa (sin.sin_addr), NTOA_MAX); return (peeraddr); } #else return ("127.0.0.1"); #endif }
#include "sflsock.h" int socket_nodelay ( sock_t handle)
Disables Nagle's algorithm for the specified socket; use this when you want to ensure that data is sent outwards as fast as possible, and when you are certain that Nagle's algorithm is causing a slowdown in performance. Recommended for HTTP, but not recommended for telnet. Returns 0 if okay, SOCKET_ERROR if there was a problem.
{ #if (defined (TCP_NODELAY)) int true_value = 1; /* Boolean value for setsockopt() */ return (setsockopt ((SOCKET) handle, SOL_SOCKET, TCP_NODELAY, (char *) &true_value, sizeof (true_value))); #else return (0); /* Not applicable to this system */ #endif }
#include "sflsock.h" Bool socket_is_alive ( sock_t handle)
Returns TRUE if the socket is open. Returns FALSE if the socket is no longer accessible. You can use this function to check that a socket has not been closed by the other party, before doing reading or writing.
{ #if (defined (DOES_SOCKETS)) int socket_type, socket_type_size = sizeof (SOCKET), rc; rc = getsockopt ((SOCKET) handle, SOL_SOCKET, SO_TYPE, (char *) &socket_type, &socket_type_size); return (rc == 0); #else return (FALSE); #endif }
#include "sflsock.h" int socket_error ( sock_t handle)
Returns an errno value for the socket, or 0 if no error was outstanding on the socket. This function is useful if you are handling sockets using the select() function: this may return error indicators on sockets, without precision on the type of error. This function will return the precise error number. Errors like EINPROGRESS, EAGAIN, and EWOULDBLOCK can usually be ignored or handled by retrying.
{ #if (defined (DOES_SOCKETS)) return (getsockopt ((SOCKET) handle, SOL_SOCKET, SO_ERROR, 0, 0)); #else return (0); #endif }
#include "sflsock.h" sock_t accept_socket ( sock_t master_socket)
Accepts a connection on a specified master socket. If you do not want to wait on this call, use select() to poll the socket until there is an incoming request, then call accept_socket. Returns the number of the new slave socket, or INVALID_SOCKET if there was an error on the accept call. You can handle errors as fatal except for EAGAIN which indicates that the operation would cause a non-blocking socket to block (treat EWOULDBLOCK in the same way).
{ #if (defined (DOES_SOCKETS)) sock_t slave_socket; /* Connected slave socket */ struct sockaddr_in sin; /* Address of connecting party */ int sin_length; /* Length of address */ connect_error_value = IP_NOERROR; /* Assume no errors */ sin_length = (int) sizeof (sin); slave_socket = accept ((SOCKET) master_socket, (struct sockaddr *) &sin, &sin_length); /* On non-Windows systems, accept returns -1 in case of error, which */ /* is the same as INVALID_SOCKET. */ # if (defined (__WINDOWS__)) if (slave_socket == INVALID_SOCKET) { int sock_errno = WSAGetLastError (); if (sock_errno == WSAEWOULDBLOCK || sock_errno == WSAEINPROGRESS) errno = EAGAIN; } # endif if (slave_socket != INVALID_SOCKET) prepare_socket (slave_socket); return (slave_socket); #else connect_error_value = IP_NOSOCKETS; return (INVALID_SOCKET); /* Sockets not supported */ #endif }
#include "sflsock.h" int connect_error (void)
Returns the last error code from one of the connection functions. For portability in a multithreaded environment, call immediately after the call to the connection function.
{ return (connect_error_value); }
#include "sflsock.h" int get_sock_addr ( sock_t handle, /* Socket to get address for */ struct sockaddr_in *sin, /* Block for formatted address */ char *name, /* Buffer for host name, or NULL */ int namesize /* Size of host name buffer */ )
Builds an address block (struct sockaddr_in) for the local end of the specified connected socket. Returns 0 if okay, SOCKET_ERROR if there was an error. If the name argument is not null, looks-up the host name and returns it. The name is truncated to namesize characters, including a trailing null character.
{ #if (defined (DOES_SOCKETS)) int rc; /* Return code from call */ struct hostent *phe; /* Host information entry */ int sin_length; /* Length of address */ /* Get address for local connected socket */ sin_length = (int) sizeof (*sin); /* Length of address */ rc = getsockname ((SOCKET) handle, (struct sockaddr *) sin, &sin_length); /* Translate into host name string, only if wanted */ if (name != NULL && rc == 0) { phe = gethostbyaddr ((char *) &sin-> sin_addr, sizeof (sin-> sin_addr), AF_INET); if (phe) { strncpy (name, phe-> h_name, namesize); name [namesize - 1] = '\0'; } } # if (defined (__WINDOWS__)) return (win_error (rc)); # else return (rc); # endif #else return ((int) SOCKET_ERROR); /* Sockets not supported */ #endif }
#include "sflsock.h" int get_peer_addr ( sock_t handle, /* Socket to get address for */ struct sockaddr_in *sin, /* Block for formatted address */ char *name, /* Buffer for host name, or NULL */ int namesize /* Size of host name buffer */ )
Builds an address block (struct sockaddr_in) for the remote end of the specified connected socket. Returns 0 if okay, SOCKET_ERROR if there was an error. If the name argument is not null, looks-up the host name and returns it. The name is truncated to namesize characters, including a trailing null character.
{ #if (defined (DOES_SOCKETS)) int rc; /* Return code from call */ struct hostent *phe; /* Host information entry */ int sin_length; /* Length of address */ ASSERT (sin); /* Get address for connected socket peer */ sin_length = (int) sizeof (*sin); /* Length of address */ rc = getpeername ((SOCKET) handle, (struct sockaddr *) sin, &sin_length); /* Translate into host name string, only if wanted */ if (name != NULL && rc == 0) { phe = gethostbyaddr ((char *) &sin-> sin_addr, sizeof (sin-> sin_addr), AF_INET); if (phe) { strncpy (name, phe-> h_name, namesize); name [namesize - 1] = '\0'; } } # if (defined (__WINDOWS__)) return (win_error (rc)); # else return (rc); # endif #else return ((int) SOCKET_ERROR); /* Sockets not supported */ #endif }
#include "sflsock.h" int read_TCP ( sock_t handle, /* Socket handle */ void *buffer, /* Buffer to receive data */ size_t length /* Maximum amount of data to read */ )
Reads data from the socket. On UNIX, VMS, OS/2, passes through to the standard read function; some other systems have particular ways of accessing sockets. If there is an error on the read this function returns SOCKET_ERROR. You can handle errors (in sockerrno) as fatal except for EAGAIN which indicates that the operation would cause a non-blocking socket to block, and EPIPE or ECONNRESET which indicate that the socket was closed at the other end. Treat EWOULDBLOCK as EAGAIN.
{ #if (defined (DOES_SOCKETS)) # if (defined (__UNIX__) || defined (__VMS__) || defined (__OS2__)) return (read ((SOCKET) handle, buffer, length)); # elif (defined (__WINDOWS__)) int rc; /* Return code from call */ ASSERT (buffer); rc = recv ((SOCKET) handle, buffer, length, 0); return (win_error (rc)); # else # error "No code for function body." # endif #else return ((int) SOCKET_ERROR); /* Sockets not supported */ #endif }
#include "sflsock.h" int write_TCP ( sock_t handle, /* Socket handle */ const void *buffer, /* Buffer containing data */ size_t length /* Amount of data to write */ )
Writes data from the socket. On UNIX, VMS, OS/2, calls the standard write function; some other systems have particular ways of accessing sockets. If there is an error on the write this function returns SOCKET_ERROR. You can handle errors (in sockerrno) as fatal except for EAGAIN which indicates that the operation would cause a non-blocking socket to block, and EPIPE or ECONNRESET which indicate that the socket was closed at the other end. Treat EWOULDBLOCK as EAGAIN.
{ #if (defined (DOES_SOCKETS)) # if (defined (__UNIX__) || defined (__VMS__) || defined (__OS2__)) return (write ((SOCKET) handle, buffer, length)); # elif (defined (__WINDOWS__)) int rc; /* Return code from call */ ASSERT (buffer); rc = send ((SOCKET) handle, buffer, length, 0); return (win_error (rc)); # else # error "No code for function body." # endif #else return ((int) SOCKET_ERROR); /* Sockets not supported */ #endif }
#include "sflsock.h" int read_UDP ( sock_t handle, /* Socket handle */ void *buffer, /* Buffer to receive data */ size_t length, /* Maximum amount of data to read */ const struct sockaddr_in *sin /* Block for address, or null */ )
Reads data from a connected or unconnected UDP socket. To prepare a connected UDP socket you call connect UDP (). This makes a connection to a specific port on a specific host, and returns a socket handle. When you call this function with a null value for the address argument, it assumes you are using a connected UDP socket. To prepare an unconnected UDP socket, call create socket () with the string "udp" as argument. This returns a sock_t handle that you can use in this function. If you use an unconnected UDP socket you must provide an address structure. The function places the remote host and port in this structure. This lets you reply using write UDP (). Generally a server can use unconnected sockets, and a client can use connected sockets. You can also format an address for a specific host and port using the address end point () function. If there is an error on the read this function returns SOCKET_ERROR. You can handle errors (in sockerrno) as fatal except for EAGAIN which indicates that the operation would cause a non-blocking socket to block. Treat EWOULDBLOCK as EAGAIN.
{ #if (defined (DOES_SOCKETS)) int sin_length, /* Length of address */ flags = 0, /* Flags for call */ rc; /* Return code from call */ ASSERT (buffer); ASSERT (sin); sin_length = (int) sizeof (*sin); if (sin) /* Read from unconnected UDP socket; we accept the address of the */ /* sending party in the sin argument. */ rc = recvfrom ((SOCKET) handle, buffer, length, flags, (struct sockaddr *) sin, &sin_length); else /* Read from a connected UDP socket; we don't need to get the */ /* address, since we already know it. */ rc = recv ((SOCKET) handle, buffer, length, flags); # if (defined (__WINDOWS__)) return (win_error (rc)); # else return (rc); # endif #else return ((int) SOCKET_ERROR); /* Sockets not supported */ #endif }
#include "sflsock.h" int write_UDP ( sock_t handle, /* Socket handle */ const void *buffer, /* Buffer containing data */ size_t length, /* Amount of data to write */ const struct sockaddr_in *sin /* Address to send to, or null */ )
Writes data to a connected or unconnected UDP socket. To prepare a connected UDP socket you call connect UDP (). This makes a connection to a specific port on a specific host, and returns a socket handle. When you call this function with a null value for the address argument, it assumes you are using a connected UDP socket. To prepare an unconnected UDP socket, call create socket () with the string "udp" as argument. This returns a sock_t handle that you can use in this function. If you use an unconnected UDP socket you must provide an address structure containing a valid host and port. You can get this information from a read UDP () or through address end point (). If there is an error on the write this function returns SOCKET_ERROR. You can handle errors as fatal except for EAGAIN which indicates that the operation would cause a non-blocking socket to block. Treat EWOULDBLOCK as EAGAIN.
{ #if (defined (DOES_SOCKETS)) int sin_length, /* Length of address */ flags = 0, /* Flags for call */ rc; /* Return code from call */ ASSERT (buffer); ASSERT (sin); sin_length = (int) sizeof (*sin); if (sin) /* Write to unconnected UDP socket; we provide the address of */ /* the receiving party in the sin argument. */ rc = sendto ((SOCKET) handle, buffer, length, flags, (struct sockaddr *) sin, sin_length); else /* Write to a connected UDP socket; we don't need to supply */ /* the address, since we already know it. */ rc = send ((SOCKET) handle, buffer, length, flags); # if (defined (__WINDOWS__)) return (win_error (rc)); # else return (rc); # endif #else return ((int) SOCKET_ERROR); /* Sockets not supported */ #endif }
#include "sflsock.h" int close_socket ( sock_t handle /* Socket handle */ )
Closes the socket. On UNIX, VMS, OS/2 calls the standard close function; some other systems have particular ways of accessing sockets. If there is an error on the close this function returns SOCKET_ERROR. You can handle errors (in sockerrno) as fatal except for EAGAIN which indicates that the operation would cause a non-blocking socket to block. Treat EWOULDBLOCK as EAGAIN.
{ #if (defined (DOES_SOCKETS)) if (!socket is alive (handle)) return (0); shutdown ((SOCKET) handle, 2); /* Shut socket gracefully */ # if (defined (__UNIX__) || defined (__VMS__) || defined (__OS2__)) return (close ((SOCKET) handle)); # elif (defined (__WINDOWS__)) { int rc; rc = closesocket ((SOCKET) handle); return (win_error (rc)); } # else # error "No code for function body." # endif #elif (defined (FAKE_SOCKETS)) return (0); /* Okay, closed */ #else return ((int) SOCKET_ERROR); /* Sockets not supported */ #endif }
#include "sflsock.h" int sock_select (int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout)
Performs the standard select() call. Use this in preference to select(), as some systems may not be 100% compatible with BSD sockets, Uses the same arguments as the select() call, and gives the same return codes. If sockets are not supported, always returns 0.
{ #if (defined (DOES_SOCKETS)) ASSERT (timeout); return (select (nfds, FD_SETTYPE readfds, FD_SETTYPE writefds, FD_SETTYPE errorfds, timeout)); #else return (0); #endif }
#include "sflsock.h" char * get_hostname (void)
Returns a string containing the local hostname. The returned string is in a static area. Only performs the local hostname lookup one time; the returned value is cached for later repeated calls to this function. If sockets are not supported, returns the value "localhost".
{ #if (defined (DOES_SOCKETS)) static char host_name [LINE_MAX + 1] = ""; if (strnull (host_name)) if (gethostname (host_name, LINE_MAX)) strcpy (host_name, "localhost"); return (host_name); #else return ("localhost"); #endif }
#include "sflsock.h" qbyte get_hostaddr (void)
Returns the current the host address as a 4-byte value in host format (not network format). Returns 0x7f000001 (loopback) if sockets are not supported or there was an error getting the current host IP address. If there are several IP addresses on the system, returns one arbitrary address.
{ #if (defined (DOES_SOCKETS)) struct hostent *phe; /* Host information entry */ phe = gethostbyname (get hostname ()); if (phe) return (*(qbyte *) (phe-> h_addr_list [0])); else return (htonl (SOCKET_LOOPBACK)); #else return (htonl (SOCKET_LOOPBACK)); #endif }
#include "sflsock.h" qbyte * get_hostaddrs (void)
Returns a table of all host IP addresses. The table ends in a zero address. Each address is a 4-byte value in host format. Returns NULL if there was an error. If sockets are not supported, returns a table with the loopback address (127.0.0.1) and a null address. The caller must free the table using mem_free() when finished using it.
{ #if (defined (DOES_SOCKETS)) int addr_count; /* How many addresses do we have */ qbyte *addr_table; /* Where we store the addresses */ struct hostent *phe; /* Host information entry */ if ((phe = gethostbyname (get hostname ())) == NULL) return (NULL); /* Count the addresses */ for (addr_count = 0; phe-> h_addr_list [addr_count]; addr_count++); /* Allocate a table; socket addresses are 4 bytes */ addr_table = mem_alloc (4 * (addr_count + 1)); /* Store the addresses */ for (addr_count = 0; phe-> h_addr_list [addr_count]; addr_count++) addr_table [addr_count] = *(qbyte *) (phe-> h_addr_list [addr_count]); addr_table [addr_count] = 0; return (addr_table); #else qbyte *addr_table; /* Where we store the addresses */ addr_table = mem_alloc (8); /* Addresses are 4 bytes */ addr_table [0] = htonl (SOCKET_LOOPBACK); addr_table [1] = 0; return (addr_table); #endif }
#include "sflsock.h" char * sock_ntoa (qbyte address)
Converts an IP address in network order to a string in dotted format. The string is stored in a statically-allocated buffer that is overwritten by each call.
{ static char string [16]; /* xxx.xxx.xxx.xxx */ byte *part; /* Network order is high-low so we can address the bytes in order */ part = (byte *) &address; sprintf (string, "%d.%d.%d.%d", part [0], part [1], part [2], part [3]); return (string); }
#include "sflsock.h" const char * sockmsg (void)
Returns a string describing the cause of the last fatal error to occur a socket. Should be called directly after a socket i/o operation; if you do other i/o operations or allow other threads to proceed in the meantime, the returned string may be incorrect.
{ #if (defined (__WINDOWS__)) char *message; switch (WSAGetLastError ()) { case WSAEINTR: message = "WSAEINTR"; break; case WSAEBADF: message = "WSAEBADF"; break; case WSAEACCES: message = "WSAEACCES"; break; case WSAEFAULT: message = "WSAEFAULT"; break; case WSAEINVAL: message = "WSAEINVAL"; break; case WSAEMFILE: message = "WSAEMFILE"; break; case WSAEWOULDBLOCK: message = "WSAEWOULDBLOCK"; break; case WSAEINPROGRESS: message = "WSAEINPROGRESS"; break; case WSAEALREADY: message = "WSAEALREADY"; break; case WSAENOTSOCK: message = "WSAENOTSOCK"; break; case WSAEDESTADDRREQ: message = "WSAEDESTADDRREQ"; break; case WSAEMSGSIZE: message = "WSAEMSGSIZE"; break; case WSAEPROTOTYPE: message = "WSAEPROTOTYPE"; break; case WSAENOPROTOOPT: message = "WSAENOPROTOOPT"; break; case WSAEPROTONOSUPPORT: message = "WSAEPROTONOSUPPORT"; break; case WSAESOCKTNOSUPPORT: message = "WSAESOCKTNOSUPPORT"; break; case WSAEOPNOTSUPP: message = "WSAEOPNOTSUPP"; break; case WSAEPFNOSUPPORT: message = "WSAEPFNOSUPPORT"; break; case WSAEAFNOSUPPORT: message = "WSAEAFNOSUPPORT"; break; case WSAEADDRINUSE: message = "WSAEADDRINUSE"; break; case WSAEADDRNOTAVAIL: message = "WSAEADDRNOTAVAIL"; break; case WSAENETDOWN: message = "WSAENETDOWN"; break; case WSAENETUNREACH: message = "WSAENETUNREACH"; break; case WSAENETRESET: message = "WSAENETRESET"; break; case WSAECONNABORTED: message = "WSAECONNABORTED"; break; case WSAECONNRESET: message = "WSAECONNRESET"; break; case WSAENOBUFS: message = "WSAENOBUFS"; break; case WSAEISCONN: message = "WSAEISCONN"; break; case WSAENOTCONN: message = "WSAENOTCONN"; break; case WSAESHUTDOWN: message = "WSAESHUTDOWN"; break; case WSAETOOMANYREFS: message = "WSAETOOMANYREFS"; break; case WSAETIMEDOUT: message = "WSAETIMEDOUT"; break; case WSAECONNREFUSED: message = "WSAECONNREFUSED"; break; case WSAELOOP: message = "WSAELOOP"; break; case WSAENAMETOOLONG: message = "WSAENAMETOOLONG"; break; case WSAEHOSTDOWN: message = "WSAEHOSTDOWN"; break; case WSAEHOSTUNREACH: message = "WSAEHOSTUNREACH"; break; case WSAENOTEMPTY: message = "WSAENOTEMPTY"; break; case WSAEPROCLIM: message = "WSAEPROCLIM"; break; case WSAEUSERS: message = "WSAEUSERS"; break; case WSAEDQUOT: message = "WSAEDQUOT"; break; case WSAESTALE: message = "WSAESTALE"; break; case WSAEREMOTE: message = "WSAEREMOTE"; break; case WSAEDISCON: message = "WSAEDISCON"; break; case WSASYSNOTREADY: message = "WSASYSNOTREADY"; break; case WSAVERNOTSUPPORTED: message = "WSAVERNOTSUPPORTED"; break; case WSANOTINITIALISED: message = "WSANOTINITIALISED"; break; default: message = "No error"; } return (message); #else return (strerror (errno)); #endif }
#include "sflsock.h" int winsock_last_error (void)
Convert a winsock error into a errno value.
{ int error = 0; switch (WSAGetLastError ()) { case WSAEINTR: error = EINTR; break; case WSAEBADF: error = EBADF; break; case WSAEWOULDBLOCK: error = EAGAIN; break; case WSAEINPROGRESS: error = EAGAIN; break; case WSAENETDOWN: error = EAGAIN; break; case WSAECONNRESET: error = EPIPE; break; case WSAECONNABORTED: error = EPIPE; break; case WSAEINVAL: error = EPIPE; break; # if defined (WIN32) default: error = GetLastError (); # else default: error = errno; # endif } return (error); }
#include "sflsock.h" Bool socket_is_permitted (const char *address, const char *mask)
Compares the specified address with a mask and returns TRUE if the address matches the mask, or FALSE if it does not. The address is formatted as a string "xxx.xxx.xxx.xxx". The mask is formatted as zero or more patterns, delimited by whitespace or commas. A pattern is an address string, with zero or more of the last components replaced by '*'. The pattern may also be prefixed by '!' to indicate exclusion. This is an example of a mask: "127.0.0.1, 253.34.*, !253.35.*". This mask allows all addresses: "*". To get the string address for a remote socket, use socket_peer_address().
{ char *addrptr, /* Pointer into address */ *maskptr; /* Pointer into mask */ Bool negate, /* If !pattern */ feedback = FALSE; /* False unless matched */ ASSERT (address); ASSERT (mask); maskptr = (char *) mask; while (*maskptr) { while (isspace (*maskptr) || *maskptr == ',') maskptr++; /* Get negation if necessary */ if (*maskptr == '!') { negate = TRUE; maskptr++; } else negate = FALSE; /* Compare pattern with address up to the end of the pattern */ for (addrptr = (char *) address; *addrptr; addrptr++) { if (*maskptr == '*') /* Matched address up to * */ return (!negate); /* So either accepted or failed */ else if (*maskptr == '\0') /* Did not match address */ return (negate); /* so fail unless negated */ else if (*addrptr != *maskptr) /* Some difference */ break; /* so stop comparing */ maskptr++; } if (*addrptr == '\0' /* Matched exact address? */ && (*maskptr == '\0' || isspace (*maskptr) || *maskptr == ',')) return (!negate); /* Either accepted or failed */ until (*maskptr == '\0' || isspace (*maskptr) || *maskptr == ',') maskptr++; /* Skip to end of this pattern */ } return (feedback); }
#include "sflsock.h" char * get_host_file (void)
returns the full path name of the host lookup file, if provided by the OS, and found. If not found, returns "hosts". The returned string is held in a static area of memory that may be overwritten by each call.
{ #if (defined (WIN32)) static OSVERSIONINFO version_info; static char name [LINE_MAX + 1]; strclr (name); GetWindowsDirectory (name, LINE_MAX); version_info.dwOSVersionInfoSize = sizeof (OSVERSIONINFO); if (GetVersionEx (&version_info)) /* On Windows NT the hosts file is well-hidden; on Win95 it's * more visible */ if (version_info.dwPlatformId == VER_PLATFORM_WIN32_NT) strcat (name, "\\system32\\drivers\\etc\\hosts"); else strcat (name, "\\hosts"); return (name); #elif (defined (__UNIX__)) return ("/etc/hosts"); #elif (defined (__VMS__)) return ("/etc/hosts"); /* Not correct -- needs more work */ #elif (defined (__OS2__)) /* Under OS/2 the hosts information is stored in the "hosts" file which * is in the directory pointed at by the %ETC% environment variable. * If that environment variable is not set, then TCP/IP support is not * properly installed. In that instance we return "/mptn/etc/hosts" * (the likely value on OS/2 Warp 3 Connect and OS/2 Warp 4) and hope * for the best. */ /* A static array is used only because the other versions use a static * array. If the resulting file name will not fit in the space allowed * then "/mtpn/etc/hosts" is used as before. */ static char name [LINE_MAX + 1]; char *etcenv = NULL; etcenv = getenv ("ETC"); if (etcenv != NULL && strlen (etcenv) < (LINE_MAX - 6)) { /* We've already checked it will all fit. */ strcpy (name, etcenv); strcat (name, "/hosts"); return (name); } else return ("/mptn/etc/hosts"); #else return ("hosts"); #endif }
#include "sflsock.h" int get_name_server (struct sockaddr_in *ns_address, int ns_max)
gets the addresses of the DNS servers defined in the TCP/IP configuration. The addresses are returned in a user-provided struct sockaddr_in array. The maximum number of addresses in this array is supplied as the ns_max argument. Return the number of address found.
{ int ns_count = 0; /* Number of servers that we found */ #if (defined (WIN32)) static OSVERSIONINFO version_info; HKEY hkey; /* Handle to returned reg. key */ static char registry_value [LINE_MAX + 1]; /* DNS server info from registry */ long size = LINE_MAX; /* Max. size of returned value */ DWORD type; char *key, **address_list = NULL; int address_nbr; /* Look in registry; this sometimes works, but not always */ version_info.dwOSVersionInfoSize = sizeof (OSVERSIONINFO); if (GetVersionEx (&version_info) && version_info.dwPlatformId == VER_PLATFORM_WIN32_NT) key = "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters"; else key = "SYSTEM\\CurrentControlSet\\Services\\Vxd\\Mstcp\\Parameters"; if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, key, 0, KEY_QUERY_VALUE, &hkey) == ERROR_SUCCESS && RegQueryValueEx (hkey, "NameServer", NULL, (LPDWORD) &type, (LPBYTE) registry_value, (LPDWORD) &size) == ERROR_SUCCESS) { address_list = tok split (registry_value); for (address_nbr = 0; address_list [address_nbr]; address_nbr++) { if (ns_count >= ns_max) break; ns_address [ns_count].sin_family = AF_INET; ns_address [ns_count].sin_port = htons (DNS_PORT); ns_address [ns_count].sin_addr.s_addr = inet_addr (address_list [address_nbr]); ns_count++; } tok free (address_list); RegCloseKey (hkey); } #elif (defined (__UNIX__)) static char buffer [LINE_MAX + 1], address [16]; FILE *resolver; int rc; resolver = file open ("/etc/resolv.conf", 'r'); if (resolver) while (file read (resolver, buffer)) { rc = sscanf (buffer, "nameserver %s", address); if (rc > 0 && rc != EOF) { if (ns_count >= ns_max) break; ns_address [ns_count].sin_family = AF_INET; ns_address [ns_count].sin_port = htons (DNS_PORT); ns_address [ns_count].sin_addr.s_addr = inet_addr (address); ns_count++; } } file close (resolver); #elif (defined (__OS2__)) static char buffer [LINE_MAX + 1], address [16]; char *etcenv = NULL, *filename = NULL; FILE *resolver = NULL; int rc; /* Under OS/2 the file controlling the resolver is stored in the */ /* directory pointed at by the ETC environment variable. It is called */ /* resolv2 or resolv (I *think* that is the order of preference), so we */ /* try those two file names in that order. */ /* If the ETC environment variable is not set we try the /mptn/etc */ /* directory since that is a likely default location for it. */ etcenv = getenv ("ETC"); if (etcenv) { filename = mem_alloc (strlen(etcenv) + 10); if (!filename) return 0; /* Cannot allocate memory for filename */ strcpy (filename, etcenv); strcat (filename, "/resolv2"); resolver = file open (filename, 'r'); if (! resolver) { /* Not available under that filename, let's try the other one */ strcpy (filename, etcenv); strcat (filename, "/resolv"); resolver = file open (filename, 'r'); } mem_free (filename); } else { /* No environment variable around, try using the defaults */ resolver = file open ("/mptn/etc/resolv2", 'r'); if (! resolver) resolver = file open ("/mptn/etc/resolv", 'r'); } if (resolver) while (file read (resolver, buffer)) { rc = sscanf (buffer, "nameserver %s", address); if (rc > 0 && rc != EOF) { if (ns_count >= ns_max) break; ns_address [ns_count].sin_family = AF_INET; ns_address [ns_count].sin_port = htons (DNS_PORT); ns_address [ns_count].sin_addr.s_addr = inet_addr (address); ns_count++; } } file close (resolver); #endif return (ns_count); }
Filename: sflsymb.h
Package: Standard Function Library (SFL)
Written: 93/12/27 iMatix SFL project team sfl@imatix.com
Revised: 97/09/08
Copyright: Copyright (c) 1991-98 iMatix
Symbol lookup is by name. Symbols contain a string value and a pointer to an caller-defined memory block. The symbol-table functions let you create and manage symbol tables. The functions are designed to be as general as possible (to support a wide variety of applications), but at the same time fast. The symbol table data structure is based on a combined linked list & hash table representation. The file sflsymb.h contains definitions for the various structures and external functions used in the sflsymb.c. Both the linked-list and hash-table representations have a guaranteed order. In the linked-list, new symbols are pushed on to the head of the list. In the hash table each bucket just contains a pointer to a linked-list of symbols. When a new symbol is created, it is pushed onto the front of this list. The reason that both data structures are used is to make the algorithm faster. Each representation has its stengths and weaknesses. For instance, if you wanted to lookup a symbol table entry for a given name using the hash table you could find it immediately, whereas with the linked- list, you would need to traverse most of the table to find the symbol.
sflsymb.h defines these symbols, possibly conditionally:
Symbol: | Defined as: |
---|---|
SYM_HASH_SIZE | 256 /* Assumed by sym_hash () */ |
_SFLSYMB_INCLUDED | TRUE |
Type name: | Defined as: |
---|---|
symfunc | Bool (*) (SYMBOL *, ...) |
symsort | int (*) (const void *symb1, const void *symb2) |
#include "sflsymb.h" SYMTAB * sym_create_table (void)
Creates a new symbol table. Returns a SYMTAB pointer which you must use in all future references to the symbol table. The symbol table is maintained in memory until the program ends or you use sym delete table() to delete it. Returns null if there was not enough memory to create the symbol table.
{ SYMTAB *table; /* Pointer to created table */ int hash_index; /* Index into hash bucket */ table = mem_alloc (sizeof (SYMTAB)); if (table) { table-> symbols = NULL; /* No symbols attached yet */ table-> size = 0; for (hash_index = 0; hash_index < SYM_HASH_SIZE; hash_index++) table-> hash [hash_index] = NULL; } return (table); }
#include "sflsymb.h" void sym_delete_table ( SYMTAB *table) /* Symbol table to delete */
Deletes the given symbol table. First frees any memory space used by the table and attached symbols, including the user data block if that is not null. If the table argument is NULL, does nothing.
{ SYMBOL *symbol, /* Pointer to symbol */ *next = NULL; /* and to next symbol in list */ if (!table) return; /* Do nothing if argument is null */ for (symbol = table-> symbols; symbol; symbol = next) { next = symbol-> next; /* Keep track of next in list */ mem_free (symbol-> value); /* Free value if used */ mem_free (symbol); /* Finally free symbol and name */ } mem_free (table); /* Now free the table */ }
#include "sflsymb.h" void sym_empty_table ( SYMTAB *table) /* Symbol table to empty */
Empties the given symbol table, by deleting all symbols. You can then add new symbols. If the table argument is NULL, does nothing.
{ SYMBOL *symbol, /* Pointer to symbol */ *next = NULL; /* and to next symbol in list */ int hash_index; /* Index into hash bucket */ if (!table) return; /* Do nothing if argument is null */ for (symbol = table-> symbols; symbol; symbol = next) { next = symbol-> next; /* Keep track of next in list */ mem_free (symbol-> value); /* Free value if used */ mem_free (symbol); /* Finally free symbol and name */ } table-> symbols = NULL; /* No symbols attached yet */ table-> size = 0; for (hash_index = 0; hash_index < SYM_HASH_SIZE; hash_index++) table-> hash [hash_index] = NULL; }
#include "sflsymb.h" int sym_merge_tables ( SYMTAB *table, /* Symbol table to import into */ const SYMTAB *import) /* Symbol table to import from */
Imports the contents of one symbol table into another. Will overwrite symbols with the same name. Returns the number of symbols imported. If there is a lack of available memory, will stop importing.
{ SYMBOL *symbol; /* Next symbol in table */ int count = 0; ASSERT (table); ASSERT (import); for (symbol = import-> symbols; symbol; symbol = symbol-> next) { if (sym assume symbol (table, symbol-> name, symbol-> value) == NULL) break; count++; } return (count); }
#include "sflsymb.h" SYMBOL * sym_lookup_symbol ( const SYMTAB *table, /* Symbol table to search */ const char *name) /* Symbol name to search for */
Searches for a symbol, by name, in the specified symbol table. Returns a pointer to the symbol if found, or NULL if not found. If more than one symbol with the same name exists, finds the latest entry.
{ SYMBOL *symbol; /* Search through hash bucket list */ ASSERT (table); for (symbol = table-> hash [sym hash (name)]; symbol; symbol = symbol-> h_next) { if (streq (symbol-> name, name)) return (symbol); } return (NULL); }
#include "sflsymb.h" SYMBOL * sym_create_symbol ( SYMTAB *table, /* Symbol table to insert into */ const char *name, /* Name of symbol to create */ const char *value) /* Value of symbol to create */
Creates a new symbol in the specified table. Returns a SYMBOL pointer to the created symbol, or NULL if there was not enough memory to create the symbol. Initialises the symbol name and value to the values supplied. Sets symbol data to NULL. You can set this yourself if you need to, after calling this function. Use mem_alloc() or mem_strdup() to assign values to the data block, otherwise you may cause problems when you delete the symbol or symbol table, since these functions free these fields. You can create several symbols with the same name; the last-defined is always placed before older instances and will be found first by sym lookup symbol().
SYMTAB *symbol_table; SYMBOL *new_symbol; symbol_table = sym_create_table (); ASSERT (symbol_table); new_symbol = sym_create_symbol (symbol_table, "This name", "This value"); if (new_symbol) { new_symbol-> data = mem_alloc (sizeof (my_block)); memcpy (new_symbol-> data, my_block); }
{ SYMBOL *symbol; /* Allocated symbol */ int hash; /* Hash bucket no. for symbol */ ASSERT (table); symbol = mem_alloc (sizeof (*symbol) + strlen (name) + 1); if (symbol) { /* Set the symbol pointers and fields */ hash = sym hash (name); symbol-> next = table-> symbols; symbol-> prev = NULL; symbol-> h_next = table-> hash [hash]; symbol-> h_prev = NULL; symbol-> name = (char *) symbol + sizeof (*symbol); symbol-> value = mem_strdup (value); symbol-> data = NULL; symbol-> hash = (byte) hash; strcpy (symbol-> name, name); if (table-> symbols) table-> symbols-> prev = symbol; table-> symbols = symbol; if (table-> hash [hash]) table-> hash [hash]-> h_prev = symbol; table-> hash [hash] = symbol; table-> size++; } return (symbol); }
#include "sflsymb.h" SYMBOL * sym_assume_symbol ( SYMTAB *table, /* Symbol table to search */ const char *name, /* Name of symbol to find/create */ const char *value) /* Value of symbol to create */
Searches for a symbol, by name, in the specified symbol table. If the symbol does not exist, creates the symbol as specified. Returns a SYMBOL pointer to the existing or new symbol, or NULL if a new symbol could not be created. The lookup and creation follow the same rules as sym lookup symbol() and sym create symbol(). The symbol's value is set to the supplied value in all cases.
{ SYMBOL *symbol; /* Allocated or found symbol */ ASSERT (table); symbol = sym lookup symbol (table, name); if (symbol) { /* Update the symbol value, if it has changed */ if (symbol-> value && strneq (symbol-> value, value)) sym set value (symbol, value); } else symbol = sym create symbol (table, name, value); return (symbol); }
#include "sflsymb.h" SYMBOL * sym_delete_symbol ( SYMTAB *table, /* Symbol table to search */ SYMBOL *symbol) /* Symbol to delete */
Removes the specified symbol from the symbol table and looks through the table for another with the same name. Returns a pointer to the next symbol, or NULL if no further symbols were found with the same name. Deallocates the symbol value, if not NULL.
{ SYMBOL *next; /* Next symbol with same name */ ASSERT (table); ASSERT (symbol); /* Find a symbol with the same name, or NULL if none found */ next = symbol; for (next = symbol-> h_next; next; next = next-> h_next) if (streq (next-> name, symbol-> name)) break; /* Fix up the pointers and remove the original symbol */ if (symbol-> prev) symbol-> prev-> next = symbol-> next; else table-> symbols = symbol-> next; if (symbol-> next) symbol-> next-> prev = symbol-> prev; if (symbol-> h_prev) symbol-> h_prev-> h_next = symbol-> h_next; else table-> hash [symbol-> hash] = symbol-> h_next; if (symbol-> h_next) symbol-> h_next-> h_prev = symbol-> h_prev; table-> size--; mem_free (symbol-> value); mem_free (symbol); return (next); }
#include "sflsymb.h" int sym_exec_all ( const SYMTAB *table, /* Symbol table to process */ symfunc the_function, ... /* Function to call */ )
Traverses the symbol table, executing the specified function for every symbol in the table. The function receives one or more arguments: the first argument is a SYMBOL pointer to the symbol, and following arguments as supplied by the caller. Continues so long as the function returns TRUE; halts when every symbol has been processed, or when the function returns FALSE. Returns the number of symbols processed without errors. The symbols are processed in reverse creation order; the newest symbol is processed first.
static Bool dump_symbol (SYMBOL *symbol, ...) { printf ("%s = %s\n", symbol-> name, symbol-> value); return (TRUE); } SYMTAB *table; table = sym_create_table (); sym_create_symbol (table, "Symbol 1", "value 1"); sym_create_symbol (table, "Symbol 2", "value 2"); sym_exec_all (table, dump_symbol); sym_delete_table (table);
{ SYMBOL *symbol; /* Next symbol in table */ va_list argptr; /* Argument list pointer */ int count = 0; /* Number of symbols processed ok */ ASSERT (table); va_start (argptr, the_function); /* Start variable args processing */ for (symbol = table-> symbols; symbol; symbol = symbol-> next) { if ((*the_function) (symbol, argptr)) count++; else break; } va_end (argptr); /* End variable args processing */ return (count); }
#include "sflsymb.h" int sym_hash ( const char *name)
Computes the hash value for a null-delimited string. The algorithm used is a simple 8-bit checksum of the characters in the string. The hash is within the range 0 .. SYM_HASH_SIZE - 1.
{ int hash; /* Computed hash value */ for (hash = 0; *name; name++) hash += *name; return (hash & (SYM_HASH_SIZE - 1)); }
#include "sflsymb.h" char * sym_get_value ( const SYMTAB *table, /* Symbol table to process */ const char *name, /* Name of symbol to look for */ const char *default_value) /* Value to return if not defined */
Returns value for specified symbol, if defined in table, or a default value otherwise. You can use this in situations where a symbol does not need to exist, and where frequent look-up by name is required. The symbol table must exist and be populated beforehand as appropriate. Returns a pointer to the value; you should never write to this string since it may exist as a string constant, not writable memory.
value = sym_get_value (env, "PATH", NULL); if (!value) puts ("PATH not defined");
{ SYMBOL *symbol; /* Search through symbol table */ ASSERT (table); symbol = sym lookup symbol (table, name); if (symbol) return (symbol-> value); else return ((char *) default_value); }
#include "sflsymb.h" long sym_get_number ( const SYMTAB *table, /* Symbol table to process */ const char *name, /* Name of symbol to look for */ const long default_value) /* Value to return if not defined */
Returns value for specified symbol, as a long value. If the symbol is not defined in the table, returns a default value.
value = sym_get_number (env, "MAX_USERS", 10);
{ char *value; value = sym get value (table, name, NULL); return (value? atol (value): default_value); }
#include "sflsymb.h" Bool sym_get_boolean ( const SYMTAB *table, /* Symbol table to process */ const char *name, /* Name of symbol to look for */ const Bool default_value) /* Value to return if not defined */
Returns value for specified symbol, as TRUE or FALSE. If the symbol is not defined in the table, returns a default value.
value = sym_get_boolean (env, "CONNECTS_ENABLED", TRUE);
{ char *value; value = sym get value (table, name, NULL); return (value? (conv str bool (value) != 0): default_value); }
#include "sflsymb.h" void sym_set_value ( SYMBOL *symbol, /* Symbol to change */ const char *value) /* New value to assign */
Assigns a new value for the symbol; this frees any previously assigned value and duplicates the supplied value, which must be a null terminated string. If you want to assign binary values, you can use the symbol's data block. If the value is NULL, any existing value is freed and the symbol value pointer is set to NULL.
{ ASSERT (symbol); mem_strfree (&symbol-> value); /* Free existing value if any */ symbol-> value = mem_strdup (value); }
#include "sflsymb.h" void sym_sort_table (SYMTAB *table, symsort sort_function)
Sorts a symbol table using the qsort library function. To access the symbol table, use the next and prev symbol pointers. If the sort_function is NULL, sorts on the symbol name.
int compare (const void *sym1, const void *sym2) { return (strcmp (*(SYMBOL **) sym1)-> value, *(SYMBOL **) sym2)-> value)); } SYMTAB *table; SYMBOL *symbol; table = sym_create_table (); sym_create_symbol (table, "Symbol 1", "A"); sym_create_symbol (table, "Symbol 2", "D"); sym_create_symbol (table, "Symbol 4", "B"); sym_create_symbol (table, "Symbol 3", "C"); sym_sort_table (table, compare); for (symbol = symtab-> symbols; symbol; symbol = symbol-> next) printf ("Symbol %s = %s\n", symbol-> name, symbol-> value); sym_delete_table (table);
{ SYMBOL *symbol, **array; int index; ASSERT (table); if (table-> size == 0) return; array = mem_alloc (table-> size * sizeof (SYMBOL *)); if (array == NULL) return; /* Not enough memory */ /* Build sorting array */ for (symbol = table-> symbols, index = 0; symbol && index < table-> size; symbol = symbol-> next, index++) array [index] = symbol; if (sort_function == NULL) sort_function = sym_sort_by_name; qsort ((void *) array, table-> size, sizeof (SYMBOL *), sort_function); /* Re-order symbol table links */ table-> symbols = array [0]; for (index = 0; index < table-> size; index++) { symbol = array [index]; symbol-> prev = (index > 0)? array [index -1]: NULL; symbol-> next = (index < table-> size - 1)? array [index + 1]: NULL; } mem_free (array); }
#include "sflsymb.h" char ** symb2strt ( const SYMTAB *symtab) /* Symbol table to export */
Exports the symbol table as an array of strings of the format "name=value". Returns a pointer to the array. The array is allocated dynamically. The array ends with a NULL string. To free the table, call strtfree(). If there was not enough memory to allocate the table, returns NULL. See also symb2env().
{ SYMBOL *symbol; /* Pointer to symbol */ char **strings, /* Returned string array */ *name_and_value; /* Name=value string */ int string_nbr; /* Index into symbol_array */ if (!symtab) return (NULL); /* Return NULL if argument is null */ /* Allocate the array of pointers with one slot for the final NULL */ strings = mem_alloc (sizeof (char *) * (symtab-> size + 1)); if (strings) { string_nbr = 0; for (symbol = symtab-> symbols; symbol; symbol = symbol-> next) { /* Allocate space for "name=value" plus final null char */ name_and_value = mem_alloc (strlen (symbol-> name) + strlen (symbol-> value) + 2); if (name_and_value) /* Fail-safe if no memory left */ sprintf (name_and_value, "%s=%s", symbol-> name, symbol-> value); strings [string_nbr++] = name_and_value; } strings [string_nbr] = NULL; /* Store final null pointer */ } return (strings); }
#include "sflsymb.h" SYMTAB * strt2symb ( char **table)
Converts a table of strings into a symbol table. The input table consists of an array of null-terminated strings, terminated in a null pointer. Ignores any strings that don't look like: "name=value". If the table contains multiple strings with the same name, the last instance is stored in the symbol table. Note that if you omit the last null pointer in the input table, you will probably get an addressing error. Returns NULL if there was insufficient memory to allocate the symbol table, or if the input argument was null.
{ SYMTAB *symtab; /* Allocated symbol table */ char *equals; /* Position of '=' in string */ int string_nbr; /* Index into string table */ if (!table) return (NULL); /* Return NULL if argument is null */ symtab = sym create table (); if (symtab) { for (string_nbr = 0; table [string_nbr]; string_nbr++) { equals = strchr (table [string_nbr], '='); if (equals) { *equals = '\0'; /* Cut into two strings */ sym assume symbol (symtab, table [string_nbr], equals + 1); *equals = '='; /* Restore previous state */ } } } return (symtab); }
#include "sflsymb.h" DESCR * symb2descr ( const SYMTAB *symtab) /* Symbol table to export */
Exports the symbol table as a table of strings in a DESCR block. Each string has the format "name=value". The block ends with a null string. Returns a pointer to the descriptor. The descriptor is allocated dynamically; to free it, use mem_free(). If there was not enough memory to allocate the descriptor, returns NULL.
{ char **strings; /* Formatted string array */ DESCR *descr; /* Formatted descriptor */ if (!symtab) return (NULL); /* Return NULL if argument is null */ strings = symb2strt (symtab); /* Convert symbol table to strings */ descr = strt2descr (strings); /* And build into descriptor */ strtfree (strings); return (descr); }
#include "sflsymb.h" SYMTAB * descr2symb ( const DESCR *descr)
Converts a DESCR block into a symbol table. The descriptor consists of a block of null-terminated strings, terminated in a double null byte. Ignores any strings that don't look like: "name=value". If the block contains multiple strings with the same name, the last instance is stored in the symbol table. Returns NULL if there was not enough memory to allocate the symbol table, or if the input argument was null.
{ SYMTAB *symtab; /* Allocated symbol table */ char **strings; /* Formatted string array */ if (!descr) return (NULL); /* Return NULL if argument is null */ strings = descr2strt (descr); /* Convert descriptor to strings */ symtab = strt2symb (strings); strtfree (strings); return (symtab); }
Filename: sflsyst.h
Package: Standard Function Library (SFL)
Written: 97/04/13 iMatix SFL project team sfl@imatix.com
Revised: 97/09/08
Copyright: Copyright (c) 1991-98 iMatix
Provides miscellaneous system-level functions.
sflsyst.h defines these symbols, possibly conditionally:
Symbol: | Defined as: |
---|---|
_SFLSYST_INCLUDED | TRUE |
#include "sflsyst.h" void sys_assert (const char *File, unsigned Line)
Displays an 'assertion failed' message and aborts the program. This function is required by prelude.h if you compile with the DEBUG symbol.
{ # if (defined (__WINDOWS__) && !defined (_CONSOLE)) static char buffer [LINE_MAX]; /* Formatted error message */ MSG msg; Bool quit; int rc; /* MessageBox return code */ sprintf (buffer, "Module %s, line %u", File, Line); /* If WM_QUIT is in the queue the message box won't show */ quit = PeekMessage(&msg, NULL, WM_QUIT, WM_QUIT, PM_REMOVE); rc = MessageBox (NULL, buffer, "Assertion failed!", MB_TASKMODAL | MB_ICONHAND | MB_ABORTRETRYIGNORE); if (quit) PostQuitMessage (msg.wParam); if (rc != IDABORT) return; # else fflush (stdout); fprintf (stderr, "\nAssertion failed: %s, line %u\n", File, Line); fflush (stderr); # endif abort (); }
#include "sflsyst.h" char * sys_name (void)
Returns a static buffer with the type or name of OS.
{ #if (defined (__WINDOWS__)) # if (defined (WIN32)) static char name [30]; OSVERSIONINFO version_info; version_info.dwOSVersionInfoSize = sizeof (OSVERSIONINFO); if (GetVersionEx (&version_info)) { if (version_info.dwPlatformId == VER_PLATFORM_WIN32_NT) sprintf (name, "Windows NT %ld.%ld", version_info.dwMajorVersion, version_info.dwMinorVersion); else if (version_info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) sprintf (name, "Windows 95 %ld.%ld", version_info.dwMajorVersion, version_info.dwMinorVersion); else sprintf (name, "Windows %ld.%ld with win32s", version_info.dwMajorVersion, version_info.dwMinorVersion); } return (name); # else return ("Windows 3.x"); # endif #elif (defined (__UNIX__)) # if (defined (__UTYPE_AUX)) return ("UNIX Type: Apple AUX"); # elif (defined (__UTYPE_DECALPHA)) return ("UNIX Type: Digital UNIX (Alpha)"); # elif (defined (__UTYPE_IBMAIX)) return ("UNIX Type: IBM RS/6000 AIX"); # elif (defined (__UTYPE_HPUX)) return ("UNIX Type: HP/UX"); # elif (defined (__UTYPE_LINUX)) return ("UNIX Type: Linux"); # elif (defined (__UTYPE_MIPS)) return ("UNIX Type: MIPS"); # elif (defined (__UTYPE_NETBSD)) return ("UNIX Type: NetBSD"); # elif (defined (__UTYPE_NEXT)) return ("UNIX Type: NeXT"); # elif (defined (__UTYPE_SCO)) return ("UNIX Type: SCO UNIX"); # elif (defined (__UTYPE_IRIX)) return ("UNIX Type: Silicon Graphics IRIX"); # elif (defined (__UTYPE_SUNOS)) return ("UNIX Type: SunOS"); # elif (defined (__UTYPE_SUNSOLARIS)) return ("UNIX Type: Sun Solaris"); # elif (defined (__UTYPE_UNIXWARE)) return ("UNIX Type: SCO UNIXWare"); # else return ("UNIX Type: Generic"); # endif #elif (defined (__VMS__)) return ("UNIX Type: Digital OpenVMS"); #elif (defined (__OS2__)) return ("UNIX Type: IBM OS/2"); #elif (defined (__MSDOS__)) return ("MS-DOS"); #else return ("Unknown"); #endif }
Filename: sflhttp.h
Package: Standard Function Library (SFL)
Written: 96/05/31 iMatix SFL project team sfl@imatix.com
Revised: 98/01/15
Copyright: Copyright (c) 1991-98 iMatix
Provides various functions that support HTTP and CGI programming, including escaping/unescaping, and CGI data manipulation.
sflhttp.h defines these symbols, possibly conditionally:
Symbol: | Defined as: |
---|---|
CGIETHER | 2 |
CGIGET | 0 |
CGIPOST | 1 |
_SFLHTTP_INCLUDED | TRUE |
cgi_free_input(strBuf) | free((strBuf)) |
#include "sflhttp.h" char * http_escape ( const char *string, char *result)
Performs HTTP escaping on a string. This works as follows: all characters except alphanumerics and spaces are converted into the 3-byte sequence "%xx" where xx is the character's hexadecimal value; spaces are replaced by '+'. Line breaks are stored as "%0D%0A", where a 'line break' is any one of: "\n", "\r", "\n\r", or "\r\n". If the result buffer is NULL, calculates the required size, allocates a block of memory, and returns that. Otherwise, returns result, which must be large enough for the escaping operation (see http escape size()). When you all http escape() with a null target block, you must free the returned block using mem_free(). Returns NULL if it could not allocate a target block as required.
{ static char hex_char [] = "0123456789ABCDEF"; char *target; /* Where we store the result */ ASSERT (string); if (result == NULL) if ((result = mem_alloc (http escape size (string))) == NULL) return (NULL); /* Could not allocate a block */ target = result; while (*string) { if (isalnum (*string)) /* Don't escape letters or digits */ *target++ = *string; else if (*string == ' ') /* Spaces are replaced by '+' */ *target++ = '+'; else if (*string == '\n' || *string == '\r') { if ((string [1] == '\n' || string [1] == '\r') && (string [1] != *string)) string++; *target++ = '%'; /* New line becomes %0A%0D */ *target++ = '0'; *target++ = 'A'; *target++ = '%'; *target++ = '0'; *target++ = 'D'; } else { *target++ = '%'; /* Some other escaped character */ *target++ = hex_char [(byte) *string >> 4]; *target++ = hex_char [(byte) *string & 15]; } string++; } *target = '\0'; /* Terminate target string */ return (result); }
#include "sflhttp.h" size_t http_escape_size ( const char *string)
Returns the size of a string after HTTP escaping. See the http escape() function for details of the escaping algorithm. Includes the null terminator in the returned size.
{ size_t result_size = 1; /* Allow for null terminator */ ASSERT (string); while (*string) { if (isalnum (*string)) /* Don't escape letters or digits */ result_size++; else if (*string == ' ') /* Spaces are replaced by '+' */ result_size++; else if (*string == '\n' || *string == '\r') { if ((string [1] == '\n' || string [1] == '\r') && (string [1] != *string)) string++; result_size += 6; /* Line ending becomes %0D%0A */ } else result_size += 3; /* Some other escaped character */ string++; } return (result_size); }
#include "sflhttp.h" char * http_unescape ( char *string, char *result)
Removes HTTP escaping from a string. See http escape() for details of the escaping algorithm. If the result string is NULL, modifies the source string in place, else fills-in the result string. Returns the resulting string. End-of-line sequences (%0A%0D) are stored as a single new-line character, i.e. carriage-returns (%0D) are not stored.
{ /* This lookup table gives us a quick way to convert a hex digit */ /* into a binary value. Note that the index must be [0..127]. */ static char hex_to_bin [128] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, /* 0..9 */ 0,10,11,12,13,14,15, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A..F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* */ 0,10,11,12,13,14,15, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* a..f */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; /* */ char *target; /* Where we store the result */ ASSERT (string); if (!result) /* If result string is null, */ result = string; /* modify in place */ target = result; while (*string) { if (*string == '%') /* Unescape %xx sequence */ { *target = hex_to_bin [string [1] & 127] * 16 + hex_to_bin [string [2] & 127]; string += 2; /* Bump past two hex digits */ if (*target != '\r') target++; /* We do not store CRs */ } else if (*string == '+') /* Spaces are escaped as '+' */ *target++ = ' '; else *target++ = *string; /* Otherwise just copy */ string++; } *target = '\0'; /* Terminate target string */ return (result); }
#include "sflhttp.h" char ** http_query2strt ( const char *original_query)
Parses a HTTP query string, building an array of strings of the format "name=value". The query string is assumed to be in escaped format, so http unescape() is always applied to the query string. Within the query string, field=value pairs are delimited by & or ;. Returns a pointer to the array. The array is allocated dynamically. The array ends with a NULL string. To free the table, call strtfree(). If there was not enough memory to allocate the table, returns NULL.
{ char *query, /* Local copy of query string */ *query_ptr, /* Pointer into query string */ *query_next, /* Pointer to next query chunk */ **strings; /* Returned string array */ int char_nbr, /* Index into query string */ string_count, /* Size of string table */ string_nbr; /* Index into string table */ ASSERT (original_query); if (*original_query == '&') /* Skip leading & if present */ original_query++; if ((query = mem_strdup (original_query)) == NULL) return (NULL); /* Could not allocate memory */ /* Break query string at & and ; delimiters and count strt size */ string_count = 1; /* Last string has no delimiter */ for (char_nbr = 0; original_query [char_nbr]; char_nbr++) if (query [char_nbr] == '&' || query [char_nbr] == ';') { query [char_nbr] = '\0'; string_count++; } /* Allocate the array of pointers with one slot for the final NULL */ if ((strings = mem_alloc (sizeof (char *) * (string_count + 1))) == NULL) { mem_free (query); return (NULL); /* Could not allocate memory */ } /* Query string now consists of a series of substrings, each ending in * a null character. We have to unescape each substring, which we do * in-place: the unescaped string is never larger than the original * string. */ query_ptr = query; for (string_nbr = 0; string_nbr < string_count; string_nbr++) { /* Unescape next query string component */ query_next = query_ptr + strlen (query_ptr) + 1; http unescape (query_ptr, NULL); /* Allocate space for "name=value" plus final null char */ strings [string_nbr] = mem_strdup (query_ptr); query_ptr = query_next; } strings [string_nbr] = NULL; /* Store final null pointer */ mem_free (query); /* Release temporary memory */ return (strings); }
#include "sflhttp.h" SYMTAB * http_query2symb ( const char *query)
Parses a HTTP query string, and populates a symbol table with the resulting field values. The query string is assumed to be escaped, so http unescape() is always applied to the query string. Within the query string, field=value pairs are delimited by & or ;. Returns a SYMTAB pointer to the new table. If there was not enough memory to allocate the table, returns NULL.
{ char **strings; /* Formatted string array */ SYMTAB *symtab; /* Returned symbol table */ strings = http query2strt (query); if (strings) { symtab = strt2symb (strings); strtfree (strings); return (symtab); } else return (NULL); /* Couldn't create string table */ }
#include "sflhttp.h" DESCR * http_query2descr ( const char *query)
Parses a HTTP query string, and returns the values as a DESCR block, composed of null-delimited strings with an empty string at the end. See strt2descr() and http query2symb() for more details. Returns the address of the allocated descriptor, or NULL if there was not enough memory.
{ char **strings; /* Formatted string array */ DESCR *descr; /* Returned descriptor */ strings = http query2strt (query); if (strings) { descr = strt2descr (strings); strtfree (strings); return (descr); } else return (NULL); /* Couldn't create string table */ }
#include "sflhttp.h" size_t http_encode_meta ( char *output, const char *input, size_t outmax)
Translates special characters into HTML/SGML metacharacters. The input buffer is not modified; you supply an output buffer and specify the maximum size of this buffer. The input buffer must end in a null. Returns the final size of the translated data excluding the final null byte. If the resulting data is too long, it is brutally truncated.
{ /* This lookup table provides a translation string for each byte * in the character set. We assume 8-bit characters. When the * string is NULL, the character is copied without translation. */ static char *meta [256]; /* Metacharacter translation table */ static Bool first_time = TRUE; /* First time flag */ int char_index; /* Index into translation table */ size_t space_left; /* Space left in destination */ const char *source; /* Pointer to source string */ char *dest, /* Pointer to result string */ *meta_char; /* Pointer through metachar string */ ASSERT (input); ASSERT (output); # define OUTPUT(c) if (!space_left) ; else { *dest++ = (c); space_left--; } /* Initialise translation table first time through */ if (first_time) { first_time = FALSE; for (char_index = 0; char_index < 256; char_index++) meta [char_index] = NULL; #if (defined (__UNIX__) || defined (__WINDOWS__)) /* UNIX and Windows generally use ISO-8859-1 (Latin-1) */ meta [0xC4] = "Auml"; meta [0xC5] = "Aring"; meta [0xC6] = "AElig"; meta [0xD6] = "Ouml"; meta [0xDC] = "Uuml"; meta [0xE0] = "agrave"; meta [0xE1] = "aacute"; meta [0xE2] = "acirc"; meta [0xE4] = "auml"; meta [0xE5] = "aring"; meta [0xE6] = "aelig"; meta [0xE7] = "ccedil"; meta [0xE8] = "egrave"; meta [0xE9] = "eacute"; meta [0xEA] = "ecirc"; meta [0xEB] = "euml"; meta [0xEC] = "igrave"; meta [0xED] = "iacute"; meta [0xEE] = "icirc"; meta [0xEF] = "iuml"; meta [0xF2] = "ograve"; meta [0xF3] = "oacute"; meta [0xF4] = "ocirc"; meta [0xF6] = "ouml"; meta [0xF9] = "ugrave"; meta [0xFA] = "uacute"; meta [0xFB] = "ucirc"; meta [0xFC] = "uuml"; meta [0xFD] = "yuml"; #elif (defined (__MSDOS__)) /* DOS generally uses the PC-1 alphabet */ meta [0x80] = "uuml"; meta [0x82] = "eacute"; meta [0x83] = "acirc"; meta [0x84] = "auml"; meta [0x85] = "agrave"; meta [0x86] = "aring"; meta [0x87] = "ccedil"; meta [0x88] = "ecirc"; meta [0x89] = "euml"; meta [0x8A] = "egrave"; meta [0x8B] = "iuml"; meta [0x8C] = "icirc"; meta [0x8D] = "igrave"; meta [0x8E] = "Auml"; meta [0x2F] = "Aring"; meta [0x91] = "aelig"; meta [0x92] = "AElig"; meta [0x93] = "ocirc"; meta [0x94] = "ouml"; meta [0x95] = "ograve"; meta [0x96] = "ucirc"; meta [0x97] = "ugrave"; meta [0x98] = "yuml"; meta [0x99] = "Ouml"; meta [0x9A] = "Uuml"; meta [0xA0] = "aacute"; meta [0xA1] = "iacute"; meta [0xA2] = "oacute"; meta [0xA3] = "uacute"; #endif } if (outmax == 0) /* Special case for zero space */ return (0); space_left = outmax - 1; /* Allow for final null byte */ dest = output; for (source = input; *source; source++) { meta_char = meta [(int) *source & 255]; if (meta_char) { OUTPUT ('&'); while (*meta_char) { OUTPUT (*meta_char); meta_char++; } OUTPUT (';'); } else OUTPUT (*source); } *dest = '\0'; return ((size_t) (dest - output)); }
#include "sflhttp.h" int cgi_parse_query_vars ( SYMTAB *symtab, const char *query, const char *prefix)
Parses a CGI query string and loads the resulting variables into an existing symbol table, optionally prefixing each name with a string. Returns the number of variables loaded. The prefix can be NULL or empty if not required.
{ static char query_var [LINE_MAX]; /* Query variable name */ char **query_vars, /* Query as string table */ *equals; /* Equal sign in variable */ int string_nbr, /* Index into string table */ variables = 0; /* Number of variables loaded */ ASSERT (symtab); if ((query_vars = http query2strt (query)) == NULL) return (0); /* Not enough memory */ for (string_nbr = 0; query_vars [string_nbr]; string_nbr++) { equals = strchr (query_vars [string_nbr], '='); if (equals) { *equals = '\0'; /* Cut into two strings */ if (prefix && *prefix) { ASSERT (strlen (query_vars [string_nbr]) + strlen (prefix) < LINE_MAX); xstrcpy (query_var, prefix, query_vars [string_nbr], NULL); } else strcpy (query_var, query_vars [string_nbr]); sym assume symbol (symtab, query_var, equals + 1); *equals = '='; /* Restore previous state */ variables++; /* Count this variable */ } } strtfree (query_vars); return (variables); }
#include "sflhttp.h" int cgi_parse_file_vars ( SYMTAB *symtab, FILE *file, const char *prefix, size_t size)
Parses a CGI query string stored in a file, and loads the resulting variables into an existing symbol table, optionally prefixing each name with a string. Returns the number of variables loaded. The prefix can be NULL or empty if not required. The file data is assumed to be escaped (see http escape()); the data should not contain line breaks, spaces, or other unescaped chars. The file should already have been opened: a typical use for this function is to parse the values supplied in stdin. The maximum size for the file is CGI_QUERY_FILE_MAX characters.
{ char *query; /* Data loaded from file */ size_t read_size; /* Amount of data read from file */ int variables = 0; /* Number of variables loaded */ ASSERT (file); ASSERT (symtab); ASSERT (size <= CGI_QUERY_FILE_MAX); if ((query = mem_alloc (size + 1)) == NULL) return (0); read_size = fread (query, 1, size, file); query [read_size] = '\0'; variables = cgi parse query vars (symtab, query, prefix); mem_free (query); return (variables); }
#include "sflhttp.h" DESCR * http_multipart_decode (const char *mime_file, const char *store_path)
Get form data from a multipart encoded data file. Each field have a entry in the symbol table. If the file content a file (INPUT field type FILE), the file is stored in a temporary file. Name of this file is added in symbol table, %variable_name%_tmp or if %variable_name% is numeric, %variable_name - 1%.
{ FILE *f_source, *f_tmp = NULL; char *tmp_name = NULL, *p_head, *p_data, *p_next, *buffer; int offset, read_size; static char separator [80 + 1]; static int sep_size; SYMTAB *table, *header_tab; qbyte tmp_index = 1; DESCR *descr = NULL; buffer = mem_alloc (MULTI_BUFFER_SIZE + 1); if (buffer == NULL) return (NULL); table = sym create table (); if (table == NULL) { mem_free (buffer); return (NULL); } header_tab = sym create table (); if (header_tab == NULL) { mem_free (buffer); sym delete table (table); return (NULL); } f_source = fopen (mime_file, "rb"); if (f_source == NULL) { mem_free (buffer); sym delete table (table); sym delete table (header_tab); return (NULL); } memset (separator, 0, sizeof (separator)); separator [0] = 0x0D; separator [1] = 0x0A; fgets (&separator [2], 78, f_source); strconvch (&separator [2] , '\r', '\0'); strconvch (&separator [2] , '\n', '\0'); sep_size = strlen (separator); read_size = fread (buffer, 1, MULTI_BUFFER_SIZE, f_source); p_next = buffer; while (read_size > 0) { sym empty table (header_tab); p_head = p_next; p_data = (char *) memfind ((byte *) p_head, MULTI_BUFFER_SIZE - (p_head - buffer), (byte *) "\r\n\r\n", 4, FALSE); if (p_data) { *p_data = '\0'; p_data += 4; } if (p_head) { multipart_decode_header (p_head, header_tab); if (sym lookup symbol (header_tab, "filename") != NULL) { if (f_tmp != NULL) { ASSERT (tmp_name != NULL); fclose (f_tmp); f_tmp = NULL; if (get file size (tmp_name) == 0) file delete (tmp_name); } tmp_name = get tmp file name (store_path, &tmp_index, "tmp"); f_tmp = fopen (tmp_name, "wb"); } } p_next = (char *) memfind ((byte *) p_data, read_size - (p_data - buffer), (byte *) separator, sep_size, FALSE); if (p_next != NULL) { *p_next = '\0'; save_multipart_header (table, header_tab, p_data, tmp_name); if (f_tmp) { fwrite (p_data, p_next - p_data, 1, f_tmp); fclose (f_tmp); f_tmp = NULL; if (get file size (tmp_name) == 0) file delete (tmp_name); } p_next += sep_size; /* Found end of file marker */ if (*p_next == '-' && *(p_next + 1) == '-') { if (f_tmp) { fclose (f_tmp); f_tmp = NULL; if (get file size (tmp_name) == 0) file delete (tmp_name); } break; } else while (*p_next == '\r' || *p_next == '\n') p_next++; } else { if (f_tmp) fwrite (p_data, &buffer [read_size - sep_size ] - p_data, 1, f_tmp); offset = 0; while (read_size > 0 && p_next == NULL) { memmove (buffer, &buffer [read_size - sep_size + offset ], sep_size); read_size = fread (&buffer [sep_size], 1, MULTI_BUFFER_SIZE - sep_size, f_source); p_next = (char *) memfind ((byte *) buffer, read_size + sep_size, (byte *) separator, sep_size, FALSE); if (p_next != NULL) { *p_next = '\0'; save_multipart_header (table, header_tab, p_data, tmp_name); if (f_tmp) { fwrite (buffer, p_next - buffer, 1, f_tmp); fclose (f_tmp); f_tmp = NULL; if (get file size (tmp_name) == 0) file delete (tmp_name); } p_next += sep_size; /* Found end of file marker */ if (*p_next == '-' && *(p_next + 1) == '-') { read_size = 0; break; } else while (*p_next == '\r' || *p_next == '\n') p_next++; read_size += sep_size; } else { if (f_tmp) fwrite (buffer, read_size, 1, f_tmp); offset = sep_size; } } } } if (f_tmp) { fclose (f_tmp); if (get file size (tmp_name) == 0) file delete (tmp_name); } sym delete table (header_tab); fclose (f_source); mem_free (buffer); descr = http_multipart2url (table); sym delete table (table); return (descr); }
#include "sflhttp.h" Bool is_full_url (const char *string)
If the specified string starts with a URL scheme, returns TRUE, else returns FALSE. A schema is one or more of [A-Za-z0-9+-.] followed by a ':'.
{ Bool scheme_size = 0; ASSERT (string); while (*string) { if (isalpha (*string) || isdigit (*string) || *string == '+' || *string == '-' || *string == '.') scheme_size++; /* So far, a valid scheme name */ else if (*string == ':') return (scheme_size > 0); /* Okay if ':' was not first char */ else return (FALSE); /* No scheme name found */ string++; } return (FALSE); /* End of string but no scheme */ }
#include "sflhttp.h" char * cgi_get_input ( int iMethod )
Gets CGI data input from stdin or the enviorment vairable QUERY_STRING, to form a stream to be used by cgi_fld_by_name, cgi_fld_by_index and cgi_fld_len_by_index functions. After you have finshed with the input stream from this function you should call, cgi_free_input to free it up. Submitted by Scott Beasley jscottb@infoave.com
{ int iStdinLen = 0, iMethodWas = 0; char *strHead, *strRetBuf; if ( iMethod == CGIPOST || iMethod == CGIETHER ) { if ( getenv ( "CONTENT_LENGTH" ) ) { iStdinLen = atoi ( getenv ( "CONTENT_LENGTH" ) ); iMethodWas = 1; } } if ( iMethod == CGIGET || iMethod == CGIETHER && !iStdinLen ) { if ( getenv ( "QUERY_STRING" ) ) { iStdinLen = strlen ( getenv ( "QUERY_STRING" ) ); iMethodWas = 0; } } if ( !iStdinLen ) return 0; strHead = strRetBuf = ( char * ) malloc ( sizeof ( char ) * iStdinLen + 1 ); ASSERT ( strHead ); memset ( strRetBuf, 0, iStdinLen + 1 ); if ( iMethodWas == CGIPOST ) fread ( strRetBuf, sizeof ( char ), iStdinLen, stdin ); else strcpy ( strRetBuf, getenv ( "QUERY_STRING" ) ); return *strHead ? strHead : ( char * ) NULL; }
#include "sflhttp.h" char * cgi_fld_by_name ( char *strFld, char *strIn, char *strRetBuf )
Gets field data from a CGI input stream based off a HTML form field name. This function returns the value of the form field. example: Form snippet: E-mail: <INPUT TYPE="text" NAME="Email" SIZE=80> CGI code to get form field: cgi fld by name ( "VERSION", strStdin, strVersion ); On return from the call: strVersion would = "VERSION" Submitted by Scott Beasley jscottb@infoave.com
{ int iCnt = 0, iLen; char *strTmp; ASSERT ( strFld ); ASSERT ( strIn ); trim ( strFld ); *strRetBuf = ( char ) NULL; while ( 1 ) { iLen = getstrfldlen ( strIn, iCnt, 0, "&" ); if ( !iLen ) break; strTmp = ( char * ) malloc ( sizeof ( char ) * iLen + 1 ); getstrfld ( strIn, iCnt++, 0, "&", strTmp ); if ( !lexncmp ( strFld, strTmp, strlen ( strFld ) ) ) { getequval ( strTmp, strRetBuf ); http unescape ( strRetBuf, NULL ); } if ( strTmp ) free ( strTmp ); strTmp = ( char * ) NULL; } return strRetBuf; }
#include "sflhttp.h" char * cgi_fld_by_index ( int iPos, char *strIn, char *strRetBuf, char *strFldName )
Gets field data from a CGI input stream based off a field index. The index is from 0 to n. Where 0 would be the first field in the stream, and n would be the last. This function returns the HTML form field name and the value of the field. example: Form snippet: E-mail: <INPUT TYPE="text" NAME="Email" SIZE=80> CGI code to get form field: cgi fld by index ( 1, strStdin, strFldValue, strFldName ); On return from the call: strFldValue would = "v1.4" and would strFldName be = "VERSION" Submitted by Scott Beasley jscottb@infoave.com
{ int iLen; char *strTmp; ASSERT ( strIn ); *strRetBuf = ( char ) NULL; iLen = getstrfldlen ( strIn, iPos, 0, "&" ); if ( iLen ) { strTmp = ( char * ) malloc ( sizeof ( char ) * iLen + 1 ); getstrfld ( strIn, iPos, 0, "&", strTmp ); getequval ( strTmp, strRetBuf ); http unescape ( strRetBuf, NULL ); if ( strFldName ) getstrfld ( strTmp, 0, 0, "=", strFldName ); free ( strTmp ); } return strRetBuf; }
#include "sflhttp.h" int cgi_fld_len_by_index ( int iPos, char *strIn, int *iDataLen, int *iNameLen )
Gets the length of the field data from a CGI input stream based off a field index. The index is from 0 to n. Where 0 would be the first field in the stream, and n would be the last. example: Form snippet: E-mail: <INPUT TYPE="text" NAME="Email" SIZE=80> CGI code to get form field: cgi fld len by index ( 1, strStdin, &iDataLen, &iNameLen ); On return from the call: iDataLen would = 4 and iNameLen would be = 7 Submitted by Scott Beasley jscottb@infoave.com
{ int iLen; char *strTmp; ASSERT ( strIn ); iLen = getstrfldlen ( strIn, iPos, 0, "&" ); if ( iLen ) { strTmp = ( char * ) malloc ( sizeof ( char ) * iLen + 1 ); getstrfld ( strIn, iPos, 0, "&", strTmp ); http unescape ( strTmp, NULL ); iLen = getstrfldlen ( strTmp, 1, 0, "=" ); if ( iDataLen ) *iDataLen = iLen; if ( iNameLen ) *iNameLen = getstrfldlen ( strTmp, 0, 0, "=" ); free ( strTmp ); } return iLen; }
#include "sflhttp.h" int displayform ( char *strformfile, char *strvalues )
Writes a HTML file out to the web browser or another file. Gives the option to have replaceable parameters in your HTML. Form snippet: E-mail: <INPUT TYPE="text" NAME="Email" SIZE=80> This would/could be a string you build at runtime: char *strformparms = "%PRONAME%|mailto,%PROVER%|v1.4"; char *strfilename = The full path to the HTML file to display. This will read the file and do any string replaces it needs to and then send it out to the browser, or file with the changes. displayform ( strfilename, strformparms ); Submitted by Scott Beasley jscottb@infoave.com
{ FILE *fp; char strline[1025]; ASSERT ( strformfile ); fp = fopen ( strformfile, "r" ); if ( !fp ) return 1; while ( !feof ( fp ) ) { fgets ( strline, 1024, fp ); strcrop ( strline ); if ( *strvalues ) stringreplace ( strline, strvalues ); puts ( strline ); } fclose ( fp ); return 0; }
Filename: sflenv.h
Package: Standard Function Library (SFL)
Written: 96/05/14 iMatix SFL project team sfl@imatix.com
Revised: 98/03/21
Copyright: Copyright (c) 1991-98 iMatix
Provides functions to read environment variables (also called shell variables or logical variables.) Provides translation into numeric and Boolean values. Provides functions to work with the environment block.
sflenv.h defines these symbols, possibly conditionally:
Symbol: | Defined as: |
---|---|
_SFLENV_INCLUDED | TRUE |
#include "sflenv.h" char * env_get_string ( const char *name, const char *default_value)
Translates the specified environment variable and returns a static string containing the value. If the variable is not defined in the environment, returns the specified default value. Note: if you want to use the value in a program you should use strdupl() to make a copy. The environment variable name is always translated into upper case. The default value may be NULL.
config_file = strdupl (env_get_string ("config", "default.cfg"));
{ static char variable_name [LINE_MAX]; char *variable_value; strupc (strcpy (variable_name, name)); variable_value = getenv (variable_name); return (variable_value? variable_value: (char *) default_value); }
#include "sflenv.h" long env_get_number ( const char *name, long default_value)
Translates the specified environment variable and returns the long numeric value of the string. If the variable is not defined in the environment, returns the specified default value. The environment variable name is always translated into upper case.
max_retries = env_get_number ("retries", 5);
{ char *variable_value; variable_value = env get string (name, NULL); return (variable_value? atol (variable_value): default_value); }
#include "sflenv.h" Bool env_get_boolean ( const char *name, Bool default_value)
Translates the specified environment variable and returns the Boolean value of the string. If the variable is not defined in the environment, returns the specified default value. The environment variable name is always translated into upper case. The environment variable value is interpreted irrespective to upper/lower case, and looking at the first letter only. T/Y/1 are TRUE, everything else is FALSE. See conv str bool() for the conversion rules.
enforce_security = env_get_number ("security", FALSE);
{ char *variable_value; variable_value = env get string (name, NULL); return (variable_value? (conv str bool (variable_value) != 0): default_value); }
#include "sflenv.h" DESCR * env2descr (void)
Returns a DESCR pointer containing the current process environment strings. The descriptor is allocated using mem_alloc(); you should use mem_free() to deallocate when you are finished. Returns NULL if there was not enough memory to allocate the descriptor.
{ return (strt2descr (environ)); }
#include "sflenv.h" char ** descr2env ( const DESCR *descr)
Returns an environment block from the supplied descriptor data. The returned block is an array of strings, terminated by a null pointer. Each string is allocated independently using mem_alloc(). Returns NULL if there was not enough memory to allocate the block.
{ return (descr2strt (descr)); }
#include "sflenv.h" SYMTAB * env2symb (void)
Creates a symbol table containing the environment variables. Each variable is stored as a name plus value. You can destroy the symbol table using sym delete table() when you are finished with it.
{ SYMTAB *symtab; /* Allocated symbol table */ char *next_entry, /* Environment variable + value */ *equals; /* Position of '=' in string */ int string_nbr; /* Index into string table */ /* We create the table here, instead of passing through strt2symb(), since we have to ensure that environment variable names are stored in uppercase. Some systems (NT) return mixed-case names. */ symtab = sym create table (); if (symtab) { for (string_nbr = 0; environ [string_nbr]; string_nbr++) { next_entry = mem_strdup (environ [string_nbr]); equals = strchr (next_entry, '='); if (equals) { *equals = '\0'; /* Cut into two strings */ strupc (next_entry); sym assume symbol (symtab, next_entry, equals + 1); } mem_free (next_entry); } } return (symtab); }
#include "sflenv.h" char ** symb2env ( const SYMTAB *symtab)
Returns an environment block from the supplied symbol table. The returned block is an array of strings, terminated by a null pointer. Each string is allocated independently using mem_alloc(). Returns NULL if there was not enough memory to allocate the block. Normalises the environment variable names as follows: converts all letters to uppercase, and non- alphanumeric characters to underlines. To free the array, use strtfree(). See also symb2strt().
{ MEMTRN *memtrn; /* Memory transation */ SYMBOL *symbol; /* Pointer to symbol */ char **strings, /* Returned string array */ *name_and_value, /* Name=value string */ *nameptr; /* Pointer into name */ int string_nbr; /* Index into symbol_array */ if (!symtab) return (NULL); /* Return NULL if argument is null */ /* Allocate the array of pointers with one slot for the final NULL */ memtrn = mem new trans (); strings = mem_alloc (sizeof (char *) * (symtab-> size + 1)); if (strings) { string_nbr = 0; for (symbol = symtab-> symbols; symbol; symbol = symbol-> next) { /* Allocate space for "name=value" plus final null char */ name_and_value = mem_alloc (strlen (symbol-> name) + strlen (symbol-> value) + 2); if (!name_and_value) /* Quit if no memory left */ { mem rollback (memtrn); return (NULL); } /* Get symbol name in uppercase, using underlines */ strcpy (name_and_value, symbol-> name); for (nameptr = name_and_value; *nameptr; nameptr++) if (isalnum (*nameptr)) *nameptr = toupper (*nameptr); else *nameptr = '_'; strcat (name_and_value, "="); strcat (name_and_value, symbol-> value); strings [string_nbr++] = name_and_value; } strings [string_nbr] = NULL; /* Store final null pointer */ } mem commit (memtrn); return (strings); }
Filename: sfltok.h
Package: Standard Function Library (SFL)
Written: 96/09/10 iMatix SFL project team sfl@imatix.com
Revised: 98/03/31
Copyright: Copyright (c) 1991-98 iMatix
Provides functions to break strings into tokens and handle symbol substitution in strings.
sfltok.h defines these symbols, possibly conditionally:
Symbol: | Defined as: |
---|---|
_SFLTOK_INCLUDED | TRUE |
#include "sfltok.h" char ** tok_split ( const char *string)
Accepts a string and breaks it into words, delimited by white space (spaces, tabs, newlines). Returns an array of strings which ends in a NULL string. If the string is empty or NULL, returns an array containing a single NULL value. The array is allocated dynamically by this function, and you must call tok free() to release it when you have finished. Returns NULL if there was insufficient memory to complete the split operation.
{ char *buffer, *bufptr, **token_list, /* Returned token list */ last_char = '\0'; /* Last character parsed */ int word_count = 0, /* Number of words in string */ word_nbr; /* Allocate space for work string, up to the size of the input string */ buffer = mem_alloc (strlen (string) + 1); if (buffer == NULL) return (NULL); /* Now copy string and eliminate whitespace */ bufptr = buffer; /* Move to start of target buffer */ if (string) /* Allow string to be NULL */ { while (*string) /* Copy entire string */ if (isspace (*string)) /* Collapse whitespace to */ { /* a single null byte */ while (isspace (*string)) string++; if (bufptr > buffer) { word_count++; /* We have a new word */ last_char = *bufptr++ = '\0'; } } else last_char = *bufptr++ = *string++; } /* Count last word if it was not terminated in a white space */ if (last_char > 0) word_count++; *bufptr = '\0'; /* End with final NULL */ /* The token list starts with a pointer to the buffer, then has one */ /* pointer to each string in the buffer. It ends with a null pointer. */ /* We return the address of the first string pointer, i.e. the caller */ /* does not see the pointer to the buffer. We can thus get away with */ /* just two allocs; one for the buffer and one for the token list. */ token_list = mem_alloc (sizeof (char *) * (word_count + 2)); if (token_list == NULL) return (NULL); token_list [0] = buffer; /* Store buffer address */ token_list++; /* and bump starting address */ bufptr = buffer; for (word_nbr = 0; word_nbr < word_count; word_nbr++) { token_list [word_nbr] = bufptr; bufptr += strlen (bufptr) + 1; } token_list [word_count] = NULL; /* Store final null pointer */ return (token_list); }
#include "sfltok.h" char ** tok_split_rich ( const char *string, const char *delims)
Works as the tok split() function, but handles tokens that are delimited by user-specified characters. A typical use of this function is to handle quoted strings. Each character in the delims argument is a potential token delimiter. For instance, to handle strings defined with single or double quotes, you could pass "\"'" as the delims argument. Note that this function always splits on spaces outside delimited tokens.
{ char *buffer, *bufptr, **token_list, /* Returned token list */ delim, /* Current delimiter character */ last_char = '\0'; /* Last character parsed */ int word_count = 0, /* Number of words in string */ word_nbr; /* Allocate space for work string, up to the size of the input string */ buffer = mem_alloc (strlen (string) + 1); if (buffer == NULL) return (NULL); /* Now copy string and eliminate spaces and cut on delimiters */ bufptr = buffer; /* Move to start of target buffer */ if (string) /* Allow string to be NULL */ { while (*string) /* Copy entire string */ if (strchr (delims, *string)) { /* User-specified delimiter */ word_count++; /* Count the word */ delim = *string++; /* and skip the delimiter */ while (*string != delim) { if (*string) *bufptr++ = *string++; else break; /* Unfinished token, but allright */ } last_char = *bufptr++ = '\0'; if (*string == delim) string++; /* Skip final delimiter */ while (isspace (*string)) string++; /* and any trailing spaces */ } else if (isspace (*string)) /* Collapse whitespace to */ { /* a single null byte */ word_count++; /* unless at the start */ while (isspace (*string)) string++; if (bufptr > buffer) last_char = *bufptr++ = '\0'; } else last_char = *bufptr++ = *string++; } /* Count last word if it was not terminated in a white space */ if (last_char > 0) word_count++; *bufptr = '\0'; /* End with final NULL */ /* The token list starts with a pointer to the buffer, then has one */ /* pointer to each string in the buffer. It ends with a null pointer. */ /* We return the address of the first string pointer, i.e. the caller */ /* does not see the pointer to the buffer. We can thus get away with */ /* just two allocs; one for the buffer and one for the token list. */ token_list = mem_alloc (sizeof (char *) * (word_count + 2)); if (token_list == NULL) return (NULL); token_list [0] = buffer; /* Store buffer address */ token_list++; /* and bump starting address */ bufptr = buffer; for (word_nbr = 0; word_nbr < word_count; word_nbr++) { token_list [word_nbr] = bufptr; bufptr += strlen (bufptr) + 1; } token_list [word_count] = NULL; /* Store final null pointer */ return (token_list); }
#include "sfltok.h" void tok_free ( char **token_list)
Frees the memory allocated by a tok split() call. You should call this function for each call to tok split(), to avoid memory leaks. Do not try to free the allocated memory yourself, as the structure of a token list is not documented and may change over time.
{ token_list--; /* Back-up to get right address */ mem_free (token_list [0]); /* Free buffer */ mem_free (token_list); /* and free token list */ }
#include "sfltok.h" char ** tok_push ( char **token_list, const char *string)
Prepends a string to the specified token table. Reallocates the table as required to make room for the new string. Returns the new token table -- the supplied token table is automatically freed by a call to tok free().
{ char *new_buffer, /* Newly-allocated buffer */ **new_list, /* Returned token list */ *new_bufptr; /* Pointer into new buffer */ int word_count, /* Number of words in string */ word_nbr; size_t buffer_size; buffer_size = tok text size (token_list); word_count = tok size (token_list); /* New list has one entry for each in old list, plus header, plus null * entry at end, plus one for the new string -- makes 3 more. */ new_list = mem_alloc (sizeof (char *) * (word_count + 3)); new_buffer = mem_alloc (buffer_size + strlen (string)); if (new_list == NULL || new_buffer == NULL) return (NULL); word_count++; /* We add one word */ strcpy (new_buffer, string); memcpy (new_buffer + strlen (string) + 1, token_list [-1], buffer_size); new_list [0] = new_buffer; /* Store buffer address */ new_list++; /* and bump starting address */ new_bufptr = new_buffer; for (word_nbr = 0; word_nbr < word_count; word_nbr++) { new_list [word_nbr] = new_bufptr; new_bufptr += strlen (new_bufptr) + 1; } new_list [word_count] = NULL; /* Store final null pointer */ tok free (token_list); /* Free old list */ return (new_list); }
#include "sfltok.h" int tok_size ( char **token_list)
Returns size of specified token list, in entries. Stops at the empty terminating token. Thus the table "This", "Is", "A", "String" will return a size of 4.
{ int word_nbr; for (word_nbr = 0; token_list [word_nbr]; word_nbr++); return (word_nbr); }
#include "sfltok.h" size_t tok_text_size ( char **token_list)
Returns size of specified token list, in characters. Counts the size of each token, plus one terminator for each token. Thus the table "This", "Is", "A", "String" will return a size of 17.
{ size_t text_size; int word_nbr; text_size = 0; for (word_nbr = 0; token_list [word_nbr]; word_nbr++) text_size += strlen (token_list [word_nbr]) + 1; return (text_size); }
#include "sfltok.h" char * tok_subst (const char *string, SYMTAB *symbols)
Performs symbol substitution into the specified string. Returns a newly-allocated string containing the result, or NULL if there was not enough memory. The symbol table holds the set of symbols that may be inserted. Undefined symbols are inserted as an empty value. Symbols are specified in the string using this syntax: $(name), where 'name' is case-sensitive. This version does not allow embedded symbols.
{ char *sym_start, *sym_end, *sym_name, /* Symbol name to look for */ *copied_to, /* Up to where we copied so far */ *cur_result, /* Current result buffer */ *new_result; /* After adding next symbol */ int sym_length; /* Length of symbol name */ SYMBOL *symbol; /* Symbol in symbol table */ ASSERT (string); ASSERT (symbols); /* Parse from left to right, looking for $(...) patterns */ cur_result = mem_strdup (""); /* Result buffer is empty */ sym_start = strchr (string, '$'); copied_to = (char *) string; while (sym_start) { sym_end = strchr (++sym_start, ')'); if (*sym_start == '(' && sym_end && *sym_end == ')') { /* First, copy string up to sym_start */ new_result = mem_alloc (strlen (cur_result) + sym_start - copied_to); strcpy (new_result, cur_result); strncat (new_result, copied_to, sym_start - 1 - copied_to); mem_free (cur_result); cur_result = new_result; copied_to = sym_end + 1; /* Now translate and insert symbol */ sym_length = sym_end - sym_start - 1; sym_name = mem_alloc (sym_length + 1); memcpy (sym_name, sym_start + 1, sym_length); sym_name [sym_length] = 0; symbol = sym lookup symbol (symbols, sym_name); mem_free (sym_name); if (symbol) { new_result = xstrcpy (NULL, cur_result, symbol-> value, NULL); mem_free (cur_result); cur_result = new_result; } } else sym_end = sym_start + 1; sym_start = strchr (sym_end, '$'); } /* Copy anything remaining in the string */ new_result = xstrcpy (NULL, cur_result, copied_to, NULL); mem_free (cur_result); return (new_result); }
Filename: sfltree.h
Package: Standard Function Library (SFL)
Written: 97/11/18 Jonathan Schultz jonathan@imatix.com
Revised: 98/01/03 Jonathan Schultz jonathan@imatix.com
Copyright: Copyright (c) 1991-98 iMatix
Provides functions to maintain 'Red-Black' balanced binary trees. You can use these functions to work with trees of any structure. To make this work, all structures must start with the following: "void *left, *right, *parent; TREE_COLOUR colour;". All trees need a pointer to the root of type TREE which should be initialised with tree_init - you can test whether a tree is empty by comparing its root with TREE_NULL. The order of nodes in the tree is determined by calling a node comparison function provided by the caller - this accepts two node pointers and returns zero if the two nodes are equal, -1 if the first is smaller and 1 if the first is larger.
sfltree.h defines these symbols, possibly conditionally:
Symbol: | Defined as: |
---|---|
TREE_DUPLICATE | -1 |
TREE_NULL | &TREE_EMPTY |
TREE_OK | 0 |
_SFLTREE_INCLUDED | TRUE |
Type name: | Defined as: |
---|---|
TREE_COLOUR | enum {BLACK, RED} |
TREE_COMPARE | int () (void *t1, void *t2) |
TREE_PROCESS | void () (void *t) |
#include "sfltree.h" void tree_init (TREE **root)
Initialises an empty tree.
{ *root = TREE_NULL; }
#include "sfltree.h" int tree_insert (TREE **root, void *tree, TREE_COMPARE *comp, Bool allow_duplicates)
Inserts a node into an existing tree. Initialises node pointers and colour to correct values. The data used by the compare functions must be filled in so that tree_insert can find the correct place in the tree to insert the node.
{ TREE *current, *parent; int last_comp = 0; /* find where node belongs */ current = *root; parent = NULL; while (current != TREE_NULL) { parent = current; last_comp = (comp) (tree, current); switch (last_comp) { case -1: current = current-> left; break; case 1: current = current-> right; break; default: if (allow_duplicates) current = current-> left; else return TREE_DUPLICATE; } } /* setup new node */ ((TREE *) tree)-> parent = parent; ((TREE *) tree)-> left = TREE_NULL; ((TREE *) tree)-> right = TREE_NULL; ((TREE *) tree)-> colour = RED; /* insert node in tree */ if (parent) switch (last_comp) { case 1: parent-> right = tree; break; default: parent-> left = tree; } else *root = tree; insert_fixup (root, tree); return (TREE_OK); }
#include "sfltree.h" void tree_delete (TREE **root, void *tree)
Deletes a node from a tree. Does not deallocate any memory.
{ TREE *youngest, *elder; TREE_COLOUR colour; if ((!tree) || (tree == TREE_NULL)) return; if ((((TREE *) tree)-> left == TREE_NULL) || (((TREE *) tree)-> right == TREE_NULL)) /* elder has a TREE_NULL node as a child */ elder = tree; else { /* find tree successor with a TREE_NULL node as a child */ elder = ((TREE *) tree)-> right; while (elder-> left != TREE_NULL) elder = elder-> left; } /* youngest is elder's only child */ if (elder-> left != TREE_NULL) youngest = elder-> left; else youngest = elder-> right; /* remove elder from the parent chain */ youngest-> parent = elder-> parent; if (elder-> parent) if (elder == elder-> parent-> left) elder-> parent-> left = youngest; else elder-> parent-> right = youngest; else *root = youngest; colour = elder-> colour; if (elder != tree) { /* JS 1997/11/18: This is from the original code. I have changed it because our implementation knows nothing about the data, and should handle varying size nodes within a single tree, provided the part of the data used for comparison remains the same. Plus just moving the data around will mess up any other pointers to it. tree-> Data = elder-> Data; */ elder-> left = ((TREE *) tree)-> left; elder-> right = ((TREE *) tree)-> right; elder-> parent = ((TREE *) tree)-> parent; elder-> colour = ((TREE *) tree)-> colour; if (((TREE *) tree)-> parent) if (tree == ((TREE *) tree)-> parent-> left) ((TREE *) tree)-> parent-> left = elder; else ((TREE *) tree)-> parent-> right = elder; else *root = elder; } if (colour == BLACK) delete_fixup (root, youngest); }
#include "sfltree.h" void *tree_find (TREE **root, void *tree, TREE_COMPARE *comp)
Finds a node with data matching that provided.
{ TREE *current = *root, *found; found = NULL; while (current != TREE_NULL) switch ((comp) (tree, current)) { case -1: current = current-> left; break; case 1: current = current-> right; break; default: found = current; /* In case of duplicates, */ current = current-> left; /* get the first one. */ } return found; }
#include "sfltree.h" void tree_traverse (void *tree, TREE_PROCESS *process, int method)
Traverse the tree, calling a processing function at each node.
{ if ((!tree) || (tree == TREE_NULL)) return; if (method == 1) { (process) (tree); tree traverse (((TREE *) tree)-> left, process, method); tree traverse (((TREE *) tree)-> right, process, method); } else if (method == 2) { tree traverse (((TREE *) tree)-> left, process, method); tree traverse (((TREE *) tree)-> right, process, method); (process) (tree); } else { tree traverse (((TREE *) tree)-> left, process, method); (process) (tree); tree traverse (((TREE *) tree)-> right, process, method); } }
#include "sfltree.h" void *tree_first (void *tree)
Finds and returns the first node in a (sub-)tree.
{ TREE *current; if ((!tree) || (tree == TREE_NULL)) return NULL; current = tree; while (current-> left != TREE_NULL) current = current-> left; return current; }
#include "sfltree.h" void *tree_last (void *tree)
Finds and returns the last node in a (sub-)tree.
{ TREE *current; if ((!tree) || (tree == TREE_NULL)) return NULL; current = tree; while (current-> right != TREE_NULL) current = current-> right; return current; }
#include "sfltree.h" void *tree_next (void *tree)
Finds and returns the next node in a tree.
{ TREE *current, *child; if ((!tree) || (tree == TREE_NULL)) return NULL; current = tree; if (current-> right != TREE_NULL) return tree first (current-> right); else { current = tree; child = TREE_NULL; while ((current-> parent) && (current-> right == child)) { child = current; current = current-> parent; } if (current-> right != child) return current; else return NULL; } }
#include "sfltree.h" void *tree_prev (void *tree)
Finds and returns the previous node in a tree.
{ TREE *current, *child; if ((!tree) || (tree == TREE_NULL)) return NULL; current = tree; if (current-> left != TREE_NULL) return tree last (current-> left); else { current = tree; child = TREE_NULL; while ((current-> parent) && (current-> left == child)) { child = current; current = current-> parent; } if (current-> left != child) return current; else return NULL; } }
Filename: sfltron.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 for a programmer who needs to insert long- term tracing code in software. The tracing code is activated and disactivated at run-time, for instance when problems are suspected.
sfltron.h defines these symbols, possibly conditionally:
Symbol: | Defined as: |
---|---|
_SFLTRON_INCLUDED | TRUE |
#include "sfltron.h" void enable_trace (void)
Enables tracing. All calls to trace() send a line of text to stdout or the trace file specified with set trace file().
{ trace_state = TRUE; }
#include "sfltron.h" void disable_trace (void)
Ends tracing. Following a call to this functions, calls to trace() have no effect.
{ trace_state = FALSE; }
#include "sfltron.h" void push_trace (Bool new_state)
Saves the current trace state. Restore with pop trace(). The current implementation only saves one level of tracing.
{ pushed_state = trace_state; trace_state = new_state; }
#include "sfltron.h" void pop_trace (void)
Restores a trace state saved by push trace().
{ trace_state = pushed_state; }
#include "sfltron.h" void set_trace_file (const char *filename, char mode)
Sends trace output to the specified file. If set_trace_file is not used, traces go to the console. If filename is null, any open trace file is close. Only one trace file can be open at any time. If mode is 'a', output is appended to the trace file; if 'w' the trace file is reset at open time. The caller can check for errors in this function by looking at the value of trace_file, which is left null if errors occur.
{ if (trace_file) { file close (trace_file); trace_file = NULL; } if (filename) { ASSERT (mode == 'w' || mode == 'a'); trace_file = file open (filename, mode); } }
#include "sfltron.h" void trace (const char *format, ...)
If the global variable trace_state is TRUE, this function formats the parameters (using printf() conventions) and sends these to stdout, or the trace_file if opened using set trace file(). The trace output is given a newline automatically.
{ static char trace_text [LINE_MAX]; va_list argptr; /* Argument list pointer */ int fmtsize; if (trace_state) { va_start (argptr, format); /* Start variable args processing */ #if (defined (DOES_SNPRINTF)) fmtsize = vsnprintf (trace_text, LINE_MAX, format, argptr); #else fmtsize = vsprintf (trace_text, format, argptr); #endif va_end (argptr); /* End variable args processing */ ASSERT (fmtsize < LINE_MAX); fprintf ((trace_file? trace_file: stdout), "%s: %s\n", time_str (), trace_text); fflush (trace_file? trace_file: stdout); } }
Filename: sfluid.h
Package: Standard Function Library (SFL)
Written: 96/05/03 iMatix SFL project team sfl@imatix.com
Revised: 97/09/08
Copyright: Copyright (c) 1991-98 iMatix
Provides functions to access user and group id names and manage the current real/effective uid's and gid's for a process. These functions are only meaningful on UNIX systems, and partially on VMS systems, but may be used by portable programs that must operate under UNIX as well as other environments. Some uid functions are non-portable between UNIX systems; this package provides a single API. Changes for OS/2 were done by Ewen McNeill ewen@naos.co.nz.
sfluid.h defines these symbols, possibly conditionally:
Symbol: | Defined as: |
---|---|
_SFLUID_INCLUDED | TRUE |
#include "sfluid.h" char * get_uid_name (uid_t uid)
Get user name from passwd file. We optimise by keeping a table of uids and names in memory. Note that this will cause problems if the program stays running when the passwd file has been changed. Returns a string containing the translated user name, or "<none>" if the uid could not be translated. Under MS- DOS the uid must be zero. The returned string is in a static area that is _not_ overwritten with each call, but which should be treated as read-only, and unstable: i.e. the value returned by one call to get_uid_name may change as a result of a later call. If you need persistent strings, use strdupl() after each call.
{ # if (defined (DOES_UID)) static struct uids { /* Table of cached uids */ uid_t id; char *name; } cache [UID_CACHE_MAX]; static int cache_size = 0, /* Number of uid's in cache */ cache_oldest = 0; /* Oldest entry in cache */ int cache_scan; /* Scan through cache */ struct passwd *passwd_entry; /* First, look for uid in cache */ for (cache_scan = 0; cache_scan < cache_size; cache_scan++) if (cache [cache_scan].id == uid) return (cache [cache_scan].name); /* Add new name to cache: if cache was full, kick-out oldest entry */ if (cache_size == UID_CACHE_MAX) { cache_scan = cache_oldest++; cache_oldest %= UID_CACHE_MAX; free (cache [cache_scan].name); } else cache_scan = cache_size++; cache [cache_scan].id = uid; # if (defined (__VMS__) && !defined (__VMS_XOPEN)) cache [cache_scan].name = "<none>"; # else if ((passwd_entry = getpwuid (uid)) == NULL) cache [cache_scan].name = "<none>"; else cache [cache_scan].name = strdupl (passwd_entry-> pw_name); # endif return (cache [cache_scan].name); # elif (defined (_MSC_VER)) return (uid == 0? "user": "<none>"); # elif (defined (__TURBOC__)) return (uid == 0? "user": "<none>"); # endif }
#include "sfluid.h" char * get_gid_name (gid_t gid)
Get group name from group file. We optimise by keeping a table of gids and names in memory. Note that this will cause problems if the program stays running when the group file has been changed. Returns a string containing the translated user name, or "<none>" if the gid could not be translated. Under MS- DOS the gid must be zero. The returned string is in a static area that is _not_ overwritten with each call, but which should be treated as read-only, and unstable: i.e. the value returned by one call to get_gid_name may change as a result of a later call. If you need persistent strings, use strdupl() after each call.
{ # if (defined (DOES_UID)) static struct gids { /* Table of cache'd gids */ gid_t id; char *name; } cache [GID_CACHE_MAX]; static int cache_size = 0, /* Number of gid's in cache */ cache_oldest = 0; /* Oldest entry in cache */ int cache_scan; /* Scan through cache */ struct group *group_entry; /* First, look for gid in cache */ for (cache_scan = 0; cache_scan < cache_size; cache_scan++) if (cache [cache_scan].id == gid) return (cache [cache_scan].name); /* Add new name to cache: if cache was full, kick-out oldest entry */ if (cache_size == GID_CACHE_MAX) { cache_scan = cache_oldest++; cache_oldest %= GID_CACHE_MAX; free (cache [cache_scan].name); } else cache_scan = cache_size++; cache [cache_scan].id = gid; # if (defined (__VMS__) && !defined (__VMS_XOPEN)) cache [cache_scan].name = "<none>"; # else if ((group_entry = getgrgid (gid)) == NULL) cache [cache_scan].name = "<none>"; else cache [cache_scan].name = strdupl (group_entry-> gr_name); # endif return (cache [cache_scan].name); # elif (defined (_MSC_VER)) return (gid == 0? "group": "<none>"); # elif (defined (__TURBOC__)) return (gid == 0? "group": "<none>"); # endif }
#include "sfluid.h" int set_uid_user (void)
This function can be used by 'setuid' programs; i.e. programs that run under a fixed uid such as 'root'. Typically such programs need to access root resources, but user data files. To do this they must switch between the 'root' uid and the 'user' uid. This function switches to the real user id. Use set uid root() to switch (back) to the 'root' uid. See also: set gid user() and set gid root().
{ #if (defined (DOES_UID)) # if (defined (__UTYPE_HPUX)) return (setuid (get_uid (REAL_ID))); # elif (defined (__OS2__)) /* OS/2 only supports one UID */ return (0); # elif (defined (__VMS__) && !defined (__VMS_XOPEN)) return (0); # else return (seteuid (get_uid (REAL_ID))); # endif #else return (0); #endif }
#include "sfluid.h" int set_uid_root (void)
This function can be used by 'setuid' programs; i.e. programs that run under a fixed uid such as 'root'. Typically such programs need to access root resources, but user data files. To do this they must switch between the 'root' uid and the 'user' uid. This function switches back to the root user id. Use set uid user() to switch to the 'user' uid. See also: set gid user() and set gid root().
{ #if (defined (DOES_UID)) # if (defined (__UTYPE_HPUX)) return (setuid (get_uid (EFFECTIVE_ID))); # elif (defined (__OS2__)) /* OS/2 only supports one UID */ return (0); # elif (defined (__VMS__) && !defined (__VMS_XOPEN)) return (0); # else return (seteuid (get_uid (EFFECTIVE_ID))); # endif #else return (0); #endif }
#include "sfluid.h" int set_gid_user (void)
This function can be used by 'setgid' programs; i.e. programs that run under a fixed gid such as 'root'. Typically such programs need to access root resources, but user data files. To do this they must switch between the 'root' gid and the 'user' gid. This function switches to the real user id. Use set gid root() to switch (back) to the 'root' gid. See also: set uid user() and set uid root().
{ #if (defined (DOES_UID)) # if (defined (__UTYPE_HPUX)) return (setgid (get_gid (REAL_ID))); # elif (defined (__OS2__)) /* OS/2 only supports one UID */ return (0); # elif (defined (__VMS__) && !defined (__VMS_XOPEN)) return (0); # else return (setegid (get_gid (REAL_ID))); # endif #else return (0); #endif }
#include "sfluid.h" int set_gid_root (void)
This function can be used by 'setgid' programs; i.e. programs that run under a fixed gid such as 'root'. Typically such programs need to access root resources, but user data files. To do this they must switch between the 'root' gid and the 'user' gid. This function switches back to the root user id. Use set gid user() to switch to the 'user' gid. See also: set gid user() and set gid root().
{ #if (defined (DOES_UID)) # if (defined (__UTYPE_HPUX)) return (setgid (get_gid (EFFECTIVE_ID))); # elif (defined (__OS2__)) /* OS/2 only supports one UID */ return (0); # elif (defined (__VMS__) && !defined (__VMS_XOPEN)) return (0); # else return (setegid (get_gid (EFFECTIVE_ID))); # endif #else return (0); #endif }
Filename: sflxml.h
Package: Standard Function Library (SFL)
Written: 98/02/25 iMatix SFL project team sfl@imatix.com
Revised: 98/03/30
Copyright: Copyright (c) 1998 iMatix
Provides functions to read and write XML files, and manipulate XML data in memory as list structures. XML is the Extensible Markup Language. Accepts this XML syntax: <item [attr=["]value["]]...>value [child]</item>
sflxml.h defines these symbols, possibly conditionally:
Symbol: | Defined as: |
---|---|
FORATTRIBUTES(attr,item) | for (attr = xml_first_attr (item); |
FORCHILDREN(child,item) | for (child = xml_first_child (item); |
_SLFXML_INCLUDED | TRUE |
Type name: | Defined as: |
---|---|
XML_ATTR | struct _XML_ATTR |
XML_ITEM | struct _XML_ITEM |
#include "sflxml.h" XML_ITEM * xml_new ( XML_ITEM *parent, const char *name, const char *value)
Creates and initialises a new XML_ITEM item. The item is set to link to itself, and its sublists are initialised to be empty. Returns the address of the created XML_ITEM item or NULL if there was not enough memory. Sets the new item's name and value as specified; either of these arguments may be null. If the parent argument is not null, attaches the new item to the end of the parent item list.
{ XML_ITEM *item; list_create (item, sizeof (XML_ITEM)); if (item) { list_reset (&item-> attrs); list_reset (&item-> children); item-> parent = parent; item-> name = mem_strdup (name); item-> value = mem_strdup (value); if (parent) list_relink_before (item, &parent-> children); return (item); } else return (NULL); }
#include "sflxml.h" void xml_modify_value (XML_ITEM *item, const char *value)
Modifies an existing XML item's value.
{ ASSERT (item); if (!item-> value) item-> value = mem_strdup (value); else if (!streq (value, item-> value)) { mem_strfree (&item-> value); item-> value = mem_strdup (value); } }
#include "sflxml.h" char * xml_item_name (XML_ITEM *item)
Extracts the name of a specified XML item. The returned string should NOT be modified. To manipulate it, first make a copy first.
{ ASSERT (item); return item-> name; }
#include "sflxml.h" char * xml_item_value (XML_ITEM *item)
Extracts the value of a specified XML item. The returned string should NOT be modified. To manipulate it, first make a copy first.
{ ASSERT (item); return item-> value; }
#include "sflxml.h" void xml_free ( XML_ITEM *item)
Frees all memory used by an XML_ITEM item and its children.
{ ASSERT (item); /* Free attribute nodes for the item */ while (!list_empty (&item-> attrs)) xml free attr (item-> attrs.next); /* Free child nodes for the item */ while (!list_empty (&item-> children)) xml free (item-> children.next); /* Now free this item itself */ list unlink (item); /* Unlink from its parent list */ mem_strfree (&item-> name); /* Free strings, if not null */ mem_strfree (&item-> value); mem_free (item); /* And free the item itself */ }
#include "sflxml.h" XML_ITEM * xml_first_child (XML_ITEM *item)
Returns the first child node of the specified item, or NULL if there are none.
{ ASSERT (item); if (!list_empty (&item-> children)) return item-> children. next; else return NULL; }
#include "sflxml.h" XML_ITEM * xml_next_sibling (XML_ITEM *item)
Returns the next sibling of the specified item, or NULL if there if are none.
{ ASSERT (item); if ((LIST *) item-> next != & item-> parent-> children) return item-> next; else return NULL; }
#include "sflxml.h" XML_ITEM * xml_parent (XML_ITEM *item)
Returns the parent of the specified item, or NULL if this is the root item.
{ ASSERT (item); return (item-> parent); }
#include "sflxml.h" int xml_put_attr ( XML_ITEM *item, const char *name, const char *value)
Sets, modifies, or deletes an attribute for the specified item. The attribute name must be supplied. If the value is NULL, any attribute with the specified name is deleted. Otherwise the specified attribute is either created or modified accordingly. Returns the number of attribute nodes created (-1, 0, or 1).
{ int feedback = 0; XML_ATTR *attr; ASSERT (item); ASSERT (name); attr = xml attr (item, name); if (attr) if (value) /* Value specified - update attr */ { mem_strfree (&attr-> value); attr-> value = mem_strdup (value); } else { xml free attr (attr); /* No value - delete attribute */ feedback = -1; } else if (value) /* Value specified - update attr */ { list_create (attr, sizeof (XML_ATTR)); if (attr) { attr-> name = mem_strdup (name); attr-> value = mem_strdup (value); attr-> parent = item; list_relink_before (attr, &item-> attrs); feedback = 1; } } return (feedback); }
#include "sflxml.h" XML_ATTR * xml_attr ( XML_ITEM *item, const char *name)
Searches for the attribute with the specified name; if found, returns the address of the attribute node, otherwise returns NULL.
{ XML_ATTR *attr; ASSERT (item); ASSERT (name); FORLIST (attr, item-> attrs) if (streq (attr-> name, name)) return (attr); return (NULL); }
#include "sflxml.h" char * xml_attr_name (XML_ATTR *attr)
Extracts the name of a specified XML attr. The returned string should NOT be modified. To manipulate it, first make a copy first.
{ ASSERT (attr); return attr-> name; }
#include "sflxml.h" char * xml_attr_value (XML_ATTR *attr)
Extracts the value of a specified XML attr. The returned string should NOT be modified. To manipulate it, first make a copy first.
{ ASSERT (attr); return attr-> value; }
#include "sflxml.h" char * xml_get_attr ( XML_ITEM *item, const char *name, const char *deflt)
Returns the value for the specified attribute, if it exists. Otherwise returns the default value.
{ XML_ATTR *attr; ASSERT (item); ASSERT (name); attr = xml attr (item, name); if (attr) return (attr-> value); else return ((char *) deflt); }
#include "sflxml.h" void xml_free_attr ( XML_ATTR *attr)
Frees all memory used by an XML_ATTR node.
{ ASSERT (attr); list unlink (attr); mem_strfree (&attr-> name); mem_strfree (&attr-> value); mem_free (attr); }
#include "sflxml.h" XML_ATTR * xml_first_attr (XML_ITEM *item)
Returns the first attribute of a specified XML item, or NULL if there are none.
{ ASSERT (item); if (!list_empty (&item-> attrs)) return item-> attrs. next; else return NULL; }
#include "sflxml.h" XML_ATTR * xml_next_attr (XML_ATTR *attr)
Returns the next attribute following the specified attribute, or NULL if there are none.
{ ASSERT (attr); if ((LIST *) attr-> next != & attr-> parent-> attrs) return attr-> next; else return NULL; }
#include "sflxml.h" Bool xml_changed ( XML_ITEM *item)
Returns TRUE if the XML file loaded into the specified list has in the meantime been changed. Returns FALSE if not.
{ char *filename; ASSERT (item); /* Date, time, and name of original XML file are in the list */ filename = xml get attr (item, "filename", NULL); if (filename && file has changed (filename, atol (xml get attr (item, "filedate", "0")), atol (xml get attr (item, "filetime", "0")))) return (TRUE); else return (FALSE); }
#include "sflxml.h" Bool xml_refresh ( XML_ITEM *item)
Refreshes an XML tree created by xml load (). If the original file (as specified by the 'filename' attribute of the root item) has been modified, reloads the whole XML file. Returns TRUE if the XML file was actually reloaded, or FALSE if the file had not changed or could not be accessed, or if the XML tree was incorrectly created.
{ char *filename, *pathsym; ASSERT (item); if (xml changed (item)) { pathsym = mem_strdup (xml get attr (item, "pathsym", NULL)); filename = mem_strdup (xml get attr (item, "filename", NULL)); xml free (item); /* Delete previous XML tree */ xml load (pathsym, filename); mem_free (pathsym); mem_free (filename); return (TRUE); } return (FALSE); }
sflxmll.h defines these symbols, possibly conditionally:
Symbol: | Defined as: |
---|---|
_SLFXMLL_INCLUDED | TRUE |
#include "sflxmll.h" int xml_save ( XML_ITEM *item, const char *filename)
Saves an XML tree to the specified file. Returns the number of items saved, or -1 if there was an error.
{ FILE *xmlfile; /* XML output stream */ XML_ITEM *child; int count; /* How many symbols did we save? */ ASSERT (item); ASSERT (filename); init_charmaps (); /* Initialise character maps */ if ((xmlfile = file open (filename, 'w')) == NULL) return (-1); /* No permission to write file */ /* Write XML file header */ /* fprintf (xmlfile, "<?XML VERSION=\"1.0\"?>\n"); */ /* Output children of XML root */ count = 0; FORCHILDREN (child, item) count += xml_save_item (xmlfile, child, 1); file close (xmlfile); return (count); }
#include "sflxmll.h" XML_ITEM *xml_load (const char *path, const char *filename)
Loads the contents of an XML file into a new XML tree. The XML data is not checked against a DTD. Returns NULL if there was insufficient memory, or the XML file could not be read. The XML tree always starts with a top-level item called 'XML' with these attributes:
filename | Name of the XML input file |
filetime | Modification time of the file, "HHMMSSCC" |
filedate | Modification date of input file, "YYYYMMDD" |
{ feedback = NULL; /* Reset return feedback */ ASSERT (filename); fname = file where ('r', path, filename, "xml"); # include "sflxmll.i" /* Include dialog interpreter */ }
| << | < | > | >> | Copyright © 1996-98 iMatix |