| iMatix home page
| << | < | > | >>
SFL Logo SFL
Version 1.91

The Standard Function Library

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.  

Table of Contents

  • What Is The SFL
  • Origins
  • Objectives
  • Portability
  • Installing The SFL
  • Availability and Distribution
  • Installation for UNIX Systems
  • Installation for Digital VMS Systems
  • Installation for Windows
  • Installation for MS-DOS
  • Using The SFL
  • The Universal Header File - prelude.h
  • The Library Header File - sfl.h
  • System Notes
  • To Do List
  • Contributors and References
  • The SFL License Agreement
  • Universal Header File for C programming
  • Define SFL version
  • Large bitstring manipulation functions
  • bits_init
  • bits_term
  • bits_create
  • bits_destroy
  • bits_set
  • bits_clear
  • bits_test
  • bits_fput
  • bits_fget
  • Compression functions
  • compress_block
  • expand_block
  • compress_rle
  • expand_rle
  • compress_nulls
  • expand_nulls
  • compress_bits
  • expand_bits
  • Console output functions
  • console_send
  • console_enable
  • console_disable
  • console_set_mode
  • console_capture
  • coprintf
  • coputs
  • coputc
  • Conversion functions
  • conv_bool_str
  • conv_date_pict
  • conv_date_str
  • conv_number_str
  • conv_str_bool
  • conv_str_date
  • conv_str_day
  • conv_str_number
  • conv_str_time
  • conv_time_pict
  • conv_time_str
  • Encryption and decryption functions
  • crypt_encode
  • crypt_decode
  • calculate_crc
  • Date and time functions
  • date_now
  • time_now
  • leap_year
  • julian_date
  • day_of_week
  • next_weekday
  • prev_weekday
  • week_of_year
  • year_quarter
  • default_century
  • pack_date
  • pack_time
  • unpack_date
  • unpack_time
  • date_to_days
  • days_to_date
  • date_to_timer
  • timer_to_date
  • timer_to_time
  • timer_to_gmdate
  • timer_to_gmtime
  • time_to_csecs
  • csecs_to_time
  • future_date
  • past_date
  • date_diff
  • valid_date
  • valid_time
  • date_is_future
  • date_is_past
  • timezone_string
  • External data representation functions
  • exdr_write
  • exdr_writed
  • exdr_read
  • Fast string searching functions
  • strfind
  • strfind_r
  • strfind_rb
  • memfind
  • memfind_r
  • memfind_rb
  • txtfind
  • File-access functions
  • file_open
  • file_locate
  • file_close
  • file_read
  • file_write
  • file_copy
  • file_rename
  • file_delete
  • file_exists
  • file_where
  • file_cycle
  • file_cycle_needed
  • file_has_changed
  • safe_to_extend
  • default_extension
  • fixed_extension
  • strip_extension
  • strip_file_path
  • strip_file_name
  • file_is_readable
  • file_is_writeable
  • file_is_executable
  • file_is_program
  • file_is_directory
  • file_exec_name
  • get_file_size
  • get_file_time
  • file_slurp
  • file_lines
  • file_set_eoln
  • get_tmp_file_name
  • Initialisation file access functions
  • ini_find_section
  • ini_scan_section
  • ini_dyn_load
  • ini_dyn_save
  • ini_dyn_changed
  • ini_dyn_refresh
  • ini_dyn_value
  • ini_dyn_values
  • Multilanguage support
  • set_userlang
  • set_userlang_str
  • get_userlang
  • get_userlang_str
  • set_accents
  • get_accents
  • get_units_name
  • get_tens_name
  • get_day_name
  • get_day_abbrev
  • get_month_name
  • get_month_abbrev
  • Line buffering functions
  • linebuf_create
  • linebuf_destroy
  • linebuf_reset
  • linebuf_append
  • linebuf_first
  • linebuf_next
  • linebuf_last
  • linebuf_prev
  • Linked-list functions
  • list_unlink
  • list_relink
  • SMTP mailer function
  • smtp_send_mail
  • srcdoc W (sflmail.c 336): function uuencode not known
  • srcdoc W (sflmail.c 361): function putgroup not known
  • srcdoc W (sflmail.c 391): function getreply not known
  • srcdoc W (sflmail.c 421): function getfilename not known
  • get_last_smtp_message
  • Mathematic functions
  • point_in_rect
  • point_in_circle
  • point_in_poly
  • Message-file access functions
  • open_message_file
  • close_message_file
  • print_message
  • message_text
  • Memory allocation functions
  • mem_alloc_
  • mem_realloc_
  • mem_strdup_
  • mem_strfree_
  • mem_free_
  • mem_assert_
  • mem_checkall_
  • mem_check_
  • mem_descr_
  • mem_new_trans_
  • mem_commit_
  • mem_rollback_
  • mem_used
  • mem_allocs
  • mem_frees
  • mem_display
  • mem_scavenger
  • MIME support functions
  • encode_base64
  • decode_base64
  • decode_mime_time
  • encode_mime_time
  • Linked-list functions
  • node_create
  • node_destroy
  • node_relink_after
  • node_relink_before
  • node_unlink
  • node_relink
  • Directory access functions
  • open_dir
  • read_dir
  • close_dir
  • format_dir
  • fix_dir
  • free_dir
  • load_dir_list
  • free_dir_list
  • resolve_path
  • locate_path
  • clean_path
  • get_curdir
  • set_curdir
  • file_matches
  • make_dir
  • remove_dir
  • Process control functions
  • process_create
  • process_status
  • process_kill
  • process_close
  • process_server
  • process_alarm
  • process_esc
  • process_unesc
  • process_priority
  • Time-slot functions
  • year_range_empty
  • year_range_fill
  • year_slot_clear
  • year_slot_set
  • year_slot_filled
  • day_range_empty
  • day_range_fill
  • day_slot_clear
  • day_slot_set
  • day_slot_filled
  • date_to_day
  • time_to_min
  • String-handling functions
  • strdupl
  • strfree
  • strskp
  • strcset
  • strpad
  • strlwc
  • strupc
  • strcrop
  • stropen
  • strclose
  • strunique
  • strmatch
  • strprefixed
  • strprefix
  • strdefix
  • strhash
  • strconvch
  • xstrcat
  • xstrcpy
  • lexcmp
  • lexncmp
  • lexwcmp
  • soundex
  • soundexn
  • strt2descr
  • descr2strt
  • strtfree
  • removechars
  • replacechrswith
  • insertstring
  • insertchar
  • leftfill
  • rightfill
  • trim
  • ltrim
  • searchreplace
  • deletestring
  • getstrfld
  • setstrfld
  • getstrfldlen
  • findstrinfile
  • getequval
  • matchtable
  • stringreplace
  • wordwrapstr
  • stricstr
  • strtempcmp
  • istoken
  • eatstr
  • eatstrpast
  • movestrpast
  • eatchar
  • isoneoftokens
  • TCP/IP, UDP/IP socket functions
  • sock_init
  • sock_term
  • passive_TCP
  • passive_UDP
  • passive_socket
  • create_socket
  • connect_TCP
  • connect_UDP
  • connect_TCP_fast
  • connect_UDP_fast
  • connect_socket
  • connect_to_peer
  • address_end_point
  • build_sockaddr
  • socket_localaddr
  • socket_peeraddr
  • socket_nodelay
  • socket_is_alive
  • socket_error
  • accept_socket
  • connect_error
  • get_sock_addr
  • get_peer_addr
  • read_TCP
  • write_TCP
  • read_UDP
  • write_UDP
  • close_socket
  • sock_select
  • get_hostname
  • get_hostaddr
  • get_hostaddrs
  • sock_ntoa
  • sockmsg
  • winsock_last_error
  • socket_is_permitted
  • get_host_file
  • get_name_server
  • Symbol-table functions
  • sym_create_table
  • sym_delete_table
  • sym_empty_table
  • sym_merge_tables
  • sym_lookup_symbol
  • sym_create_symbol
  • sym_assume_symbol
  • sym_delete_symbol
  • sym_exec_all
  • sym_hash
  • sym_get_value
  • sym_get_number
  • sym_get_boolean
  • sym_set_value
  • sym_sort_table
  • symb2strt
  • strt2symb
  • symb2descr
  • descr2symb
  • System-level functions (assertions,...)
  • sys_assert
  • sys_name
  • HTTP and CGI Support functions
  • http_escape
  • http_escape_size
  • http_unescape
  • http_query2strt
  • http_query2symb
  • http_query2descr
  • http_encode_meta
  • cgi_parse_query_vars
  • cgi_parse_file_vars
  • http_multipart_decode
  • is_full_url
  • cgi_get_input
  • cgi_fld_by_name
  • cgi_fld_by_index
  • cgi_fld_len_by_index
  • displayform
  • Environment variable functions
  • env_get_string
  • env_get_number
  • env_get_boolean
  • env2descr
  • descr2env
  • env2symb
  • symb2env
  • String token manipulation functions.
  • tok_split
  • tok_split_rich
  • tok_free
  • tok_push
  • tok_size
  • tok_text_size
  • tok_subst
  • Linked-list functions
  • tree_init
  • tree_insert
  • tree_delete
  • tree_find
  • tree_traverse
  • tree_first
  • tree_last
  • tree_next
  • tree_prev
  • Tracing functions
  • enable_trace
  • disable_trace
  • push_trace
  • pop_trace
  • set_trace_file
  • trace
  • Process user id (uid) and group id (gid) functions
  • get_uid_name
  • get_gid_name
  • set_uid_user
  • set_uid_root
  • set_gid_user
  • set_gid_root
  • XML (Extensible Markup Language) access functions
  • xml_new
  • xml_modify_value
  • xml_item_name
  • xml_item_value
  • xml_free
  • xml_first_child
  • xml_next_sibling
  • xml_parent
  • xml_put_attr
  • xml_attr
  • xml_attr_name
  • xml_attr_value
  • xml_get_attr
  • xml_free_attr
  • xml_first_attr
  • xml_next_attr
  • xml_changed
  • xml_refresh
  • xml_save
  • xml_load
  • What Is The SFL

    Origins

    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.

    Objectives

    When we designed the SFL, we had certain things in mind:

    Portability

    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.

    Installing The SFL

    Availability and Distribution

    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:

    Getting The Source Archive

    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.

    Getting The The Documentation Kit

    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.


    Installation for UNIX Systems

    To install the SFL on a UNIX system you need to:

    1. Download the source archive and decompress it.
    2. Run the 'build' script to compile the SFL sources and build the libsfl.a archive file.
    3. Optionally, install the libsfl.a file in the /usr/lib directory.
    4. Optionally, install the SFL header file(s) in the /usr/include directory.

    Decompressing The Source Archive

    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.

    Compiling The SFL Sources

    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
    
    

    Using The SFL In Your Applications

    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:

    1. One header file per function group. These are provided for reference; you will normally not use these directly.
    2. A Universal Header File which encapsulates and replaces all local header files. Again, this is provided for reference; you do not normally use this directly.
    3. A Library Header File that contains the Universal Header File and all the SFL header files in one go. This is meant to simplify application programming and installation.

    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.


    Installation for Digital VMS Systems

    To install the SFL on a Digital VMS system you need to:

    1. Download the source archive and decompress it.
    2. Run the 'build.txt' command file to build the libsfl.olb library file.
    3. Optionally, install the libsfl.olb file in the SYS$LIBRARY directory.
    4. Optionally, install the SFL header file(s) in the SYS$LIBRARY directory.

    Decompressing The Source Archive

    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.

    Compiling The SFL Sources

    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.

    Using The SFL In Your Applications

    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:

    1. One header file per function group. These are provided for reference; you will normally not use these directly.
    2. A Universal Header File which encapsulates and replaces all local header files. Again, this is provided for reference; you do not normally use this directly.
    3. A Library Header File that contains the Universal Header File and all the SFL header files in one go. This is meant to simplify application programming and installation.

    We recommend that you install the sfl.h file in SYS$LIBRARY. A typical application program starts like this:

    #include <sfl.h>
    
    

    Installation for Windows

    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.


    Installation for MS-DOS

    To install the SFL on a MS-DOS system you need to:

    1. Download the source archive and decompress it.
    2. Run build.bat to build the libsfl.lib library file.
    3. Optionally, install the libsfl.lib file in central directory.
    4. Optionally, install the SFL header file(s) in a central directory.

    Decompressing The Source Archive

    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
    
    

    Compiling The SFL Sources

    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.

    Using The SFL In Your Applications

    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:

    1. One header file per function group. These are provided for reference; you will normally not use these directly.
    2. A Universal Header File which encapsulates and replaces all local header files. Again, this is provided for reference; you do not normally use this directly.
    3. A Library Header File that contains the Universal Header File and all the SFL header files in one go. This is meant to simplify application programming and installation.

    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>
    
    

    Using The SFL

     

    The Universal Header File - prelude.h

    What and Why?

    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.

    Using the Universal Header File

    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.  

    The Library Header File - sfl.h

    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.

    System Notes

    MS-DOS

    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++.

    MS-Windows

    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++.

    Digital VAX

    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.

    Linux

    The SFL is fully portable to Linux and has been tested with GNU C. It should give no compiler warning errors.

    Sun OS and Sun Sparc

    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.

    HP/UX

    The SFL is fully portable to HP/UX. It should give no compiler warning errors.

    IBM AIX

    The SFL is fully portable to IBM/AIX. It should give no compiler warning errors.

    Digital UNIX

    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.

    Other UNIX Systems

    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.

    OS/2

    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.

    To Do List

    Contributors and References

     

    The SFL License Agreement

    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.

    Statement Of Copyright

    The Product is, and remains, Copyright © 1991-98 iMatix, with exception of specific copyrights as noted in the individual source files.

    Conditions Of Use

    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:

    1. Provide the source code for Product modules that you use, or
    2. Make your product freely available according to a license similar to the GNU General Public License, or the Perl Artistic License, or
    3. Add this phrase to the documentation for your product: "This product uses parts of the iMatix SFL, Copyright © 1991-98 iMatix <http://www.imatix.com>".

    Rights Of Usage

    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."

    Rights Of Distribution

    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.

    Disclaimer Of Warranty

    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.

    Universal Header File for C programming

    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

    Synopsis

    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>

    List of Functions

    List of Symbol Definitions

    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

    List of Type Definitions

    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

    Define SFL version

    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

    Synopsis

    Defines the SFL_VERSION constant.

    List of Symbol Definitions

    sflvers.h defines these symbols, possibly conditionally:
    Symbol: Defined as:
    SFL_VERSION "1.90" /* Main SFL version */
    _SFLVERS_INCLUDED TRUE

    Large bitstring manipulation functions

    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

    Synopsis

    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.

    List of Functions

    List of Symbol Definitions

    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
     

    bits_init

    
    #include "sflbits.h"
    
    int
    
    bits_init (void)
    
    

    Synopsis

    Initialises bitstring functions. You must call this before using any other bitstring functions. Returns 0 if okay, -1 if there was an error.

    Source Code - (sflbits.c)

    
    {
    
        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);
    
    }
    
    
     

    bits_term

    
    #include "sflbits.h"
    
    int
    
    bits_term (void)
    
    

    Synopsis

    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.

    Source Code - (sflbits.c)

    
    {
    
        mem_free (comp_zero);
    
        mem_free (comp_ones);
    
        return (0);
    
    }
    
    
     

    bits_create

    
    #include "sflbits.h"
    
    BITS *
    
    bits_create (void)
    
    

    Synopsis

    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.

    Source Code - (sflbits.c)

    
    {
    
        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);
    
    }
    
    
     

    bits_destroy

    
    #include "sflbits.h"
    
    void
    
    bits_destroy (
    
        BITS *bits)
    
    

    Synopsis

    Releases all memory used by a bitstring and deletes the bitstring. Do not refer to the bitstring after calling this function.

    Source Code - (sflbits.c)

    
    {
    
        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);
    
    }
    
    
     

    bits_set

    
    #include "sflbits.h"
    
    int
    
    bits_set (
    
        BITS *bits,
    
        long bit)
    
    

    Synopsis

    Sets the specified bit in the bitmap. Returns ?

    Source Code - (sflbits.c)

    
    {
    
        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, &section, &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;
    
    }
    
    
     

    bits_clear

    
    #include "sflbits.h"
    
    int
    
    bits_clear (
    
        BITS *bits,
    
        long bit)
    
    

    Synopsis

    Clears the specified bit in the bitmap. Returns ?

    Source Code - (sflbits.c)

    
    {
    
        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, &section, &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;
    
    }
    
    
     

    bits_test

    
    #include "sflbits.h"
    
    int
    
    bits_test (
    
        const BITS *bits,
    
        long bit)
    
    

    Synopsis

    Tests the specified bit in the bitmap. Returns 1 or 0.

    Source Code - (sflbits.c)

    
    {
    
        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, &section, &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);
    
    }
    
    
     

    bits_fput

    
    #include "sflbits.h"
    
    int
    
    bits_fput (FILE *file,
    
        const BITS *bits)
    
    

    Synopsis

    Writes the bitstring to the specified file stream. To read the bitstring, use the bits fget() function. The structure of the bitstring is:

    Source Code - (sflbits.c)

    
    {
    
        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;
    
    }
    
    
     

    bits_fget

    
    #include "sflbits.h"
    
    BITS *
    
    bits_fget (FILE *file)
    
    

    Synopsis

    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.

    Source Code - (sflbits.c)

    
    {
    
        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);
    
    }
    
    

    Compression functions

    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

    Synopsis

    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.

    List of Functions

    List of Symbol Definitions

    sflcomp.h defines these symbols, possibly conditionally:
    Symbol: Defined as:
    _SFLCOMP_INCLUDED TRUE
     

    compress_block

    
    #include "sflcomp.h"
    
    word
    
    compress_block (
    
        const byte *src,
    
        byte *dst,
    
        word src_size)
    
    

    Synopsis

    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.

    Source Code - (sflcomp.c)

    
    {
    
        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);
    
    }
    
    
     

    expand_block

    
    #include "sflcomp.h"
    
    word
    
    expand_block (
    
        const byte *src,
    
        byte *dst,
    
        word src_size)
    
    

    Synopsis

    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.

    Source Code - (sflcomp.c)

    
    {
    
        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);
    
    }
    
    
     

    compress_rle

    
    #include "sflcomp.h"
    
    word
    
    compress_rle (
    
        byte *src,
    
        byte *dst,
    
        word src_size)
    
    

    Synopsis

    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.

    Source Code - (sflcomp.c)

    
    {
    
        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      */
    
    }
    
    
     

    expand_rle

    
    #include "sflcomp.h"
    
    word
    
    expand_rle (
    
        const byte *src,
    
        byte *dst,
    
        word src_size)
    
    

    Synopsis

    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.

    Source Code - (sflcomp.c)

    
    {
    
        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        */
    
    }
    
    
     

    compress_nulls

    
    #include "sflcomp.h"
    
    word
    
    compress_nulls (
    
        byte *src,
    
        byte *dst,
    
        word src_size)
    
    

    Synopsis

    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.

    Source Code - (sflcomp.c)

    
    {
    
        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      */
    
    }
    
    
     

    expand_nulls

    
    #include "sflcomp.h"
    
    word
    
    expand_nulls (
    
        const byte *src,
    
        byte *dst,
    
        word src_size)
    
    

    Synopsis

    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.

    Source Code - (sflcomp.c)

    
    {
    
        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        */
    
    }
    
    
     

    compress_bits

    
    #include "sflcomp.h"
    
    word
    
    compress_bits (
    
        byte *src,
    
        byte *dst,
    
        word src_size)
    
    

    Synopsis

    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.

    Source Code - (sflcomp.c)

    
    {
    
        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      */
    
    }
    
    
     

    expand_bits

    
    #include "sflcomp.h"
    
    word
    
    expand_bits (
    
        const byte *src,
    
        byte *dst,
    
        word src_size)
    
    

    Synopsis

    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.

    Source Code - (sflcomp.c)

    
    {
    
        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        */
    
    }
    
    

    Console output functions

    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

    Synopsis

    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.

    List of Functions

    List of Symbol Definitions

    sflcons.h defines these symbols, possibly conditionally:
    Symbol: Defined as:
    _SFLCONS_INCLUDED TRUE

    List of Type Definitions

    Type name: Defined as:
    CONSOLE_FCT void () (const char *)
     

    console_send

    
    #include "sflcons.h"
    
    void
    
    console_send (CONSOLE_FCT *new_console_fct, Bool echo)
    
    

    Synopsis

    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.

    Source Code - (sflcons.c)

    
    {
    
        console_fct  = new_console_fct;
    
        console_echo = echo;                /*  Copy to stdout                   */
    
    }
    
    
     

    console_enable

    
    #include "sflcons.h"
    
    void
    
    console_enable (void)
    
    

    Synopsis

    Enables console output. Use together with console disable() to stop and start console output.

    Source Code - (sflcons.c)

    
    {
    
        console_active = TRUE;
    
    }
    
    
     

    console_disable

    
    #include "sflcons.h"
    
    void
    
    console_disable (void)
    
    

    Synopsis

    Disables console output. Use together with console enable() to stop and start console output.

    Source Code - (sflcons.c)

    
    {
    
        console_active = FALSE;
    
    }
    
    
     

    console_set_mode

    
    #include "sflcons.h"
    
    void
    
    console_set_mode (int mode)
    
    

    Synopsis

    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 "
    The default is plain output.

    Source Code - (sflcons.c)

    
    {
    
        ASSERT (mode == CONSOLE_PLAIN
    
             || mode == CONSOLE_DATETIME
    
             || mode == CONSOLE_TIME);
    
    
    
        console_mode = mode;
    
    }
    
    
     

    console_capture

    
    #include "sflcons.h"
    
    int
    
    console_capture (const char *filename, char mode)
    
    

    Synopsis

    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.

    Source Code - (sflcons.c)

    
    {
    
        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);
    
    }
    
    
     

    coprintf

    
    #include "sflcons.h"
    
    int
    
    coprintf (const char *format, ...)
    
    

    Synopsis

    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.

    Source Code - (sflcons.c)

    
    {
    
        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);
    
    }
    
    
     

    coputs

    
    #include "sflcons.h"
    
    int
    
    coputs (const char *string)
    
    

    Synopsis

    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.

    Source Code - (sflcons.c)

    
    {
    
        coprintf (string);
    
        return (1);
    
    }
    
    
     

    coputc

    
    #include "sflcons.h"
    
    int
    
    coputc (int character)
    
    

    Synopsis

    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.

    Source Code - (sflcons.c)

    
    {
    
        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);
    
    }
    
    

    Conversion functions

    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

    Synopsis

    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.

    List of Functions

    List of Symbol Definitions

    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
     

    conv_bool_str

    
    #include "sflconv.h"
    
    char *
    
    conv_bool_str (
    
        Bool boolean,
    
        int  format)
    
    

    Synopsis

    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.

    Source Code - (sflcvbs.c)

    
    {
    
        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)]);
    
    }
    
    
     

    conv_date_pict

    
    #include "sflconv.h"
    
    char *
    
    conv_date_pict (
    
        long date,
    
        const char *picture)
    
    

    Synopsis

    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
    Returns the formatted result. This is a static string, of at most 80 characters, that is overwritten by each call. If date is zero, returns an empty string. The 'm' and 'd' formats output a leading space when used at the start of the picture. This is to improve alignment of columns of dates. The 'm' and 'd' formats also output a space when the previous character was a digit; otherwise the date components stick together and are illegible.

    Examples

    
        puts (conv_date_pict (19951202, "mm d, yy"));
    
            Dec 2, 95
    
        puts (conv_date_pict (19951202, "d mmm, yy"));
    
            2 Dec, 95
    
    

    Source Code - (sflcvdp.c)

    
    {
    
        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);
    
    }
    
    
     

    conv_date_str

    
    #include "sflconv.h"
    
    char *
    
    conv_date_str (
    
        long date,
    
        int  flags,
    
        int  format,
    
        int  order,
    
        char datesep,
    
        int  width)
    
    

    Synopsis

    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
    The date order (year/month/day) is normally supplied in the order argument. However, the date flags can override this. The flags are:
    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
    Returns a pointer to a static area holding the string, or NULL if there was an error (for instance, formatted date greater than width).

    Source Code - (sflcvds.c)

    
    {
    
        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);
    
    }
    
    
     

    conv_number_str

    
    #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?         */
    
    )
    
    

    Synopsis

    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 formats:
    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)
    Decimal formats:
    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
    The input number string may contain leading zeros and a leading sign character (space, '+', '-') if signed. These are examples of valid 8-digit numbers: "1234" "00001234" "12345678" "+12345678". If the flag FLAG_N_DECIMALS is set, the last X digits are taken to be decimals, where X is the value of the decimals argument. If the number contains a decimal point (always '.'), this is taken to indicate the start of the decimal part. The formatted number is placed within a field of specified width. If the number is right-justfied, this means it may have leading spaces. If the field width is 0, the number will never have leading spaces. Returns a pointer to the formatted string, or NULL if the specified width is too small for formatted number or the supplied number does not contain enough digits.

    Source Code - (sflcvns.c)

    
    {
    
        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);
    
    }
    
    
     

    conv_str_bool

    
    #include "sflconv.h"
    
    int
    
    conv_str_bool (
    
        const char *string)
    
    

    Synopsis

    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.

    Source Code - (sflcvsb.c)

    
    {
    
        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);
    
          }
    
    }
    
    
     

    conv_str_date

    
    #include "sflconv.h"
    
    long
    
    conv_str_date (
    
        const char *string,
    
        int  flags,
    
        int  format,
    
        int  order)
    
    

    Synopsis

    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.
    The date order must be one of:
    DATE ORDER YMD Year month day.
    DATE ORDER DMY Day month year.
    DATE ORDER MDY Month day year.
    You can override the order using these flags:
    FLAG D ORDER YMD Year month day.
    FLAG D ORDER DMY Day month year.
    FLAG D ORDER MDY Month day year.
    Returns the date as a long integer, YYYYMMDD. The conversion is pretty relaxed and allows strings like: 010195, 1-1-95, 1-Jan- 95, 1jan95, 01jan95, 1 1 95, etc. The input string must be null-terminated. Returns -1 in case of an invalid date or format. If the short formats (DATE_DM_..., DATE_YM_...) are used, the missing field is always set to 0 and cannot be supplied in the string. If the date was empty, i.e. contains no usable digits, returns 0.

    Source Code - (sflcvsd.c)

    
    {
    
        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);
    
    }
    
    
     

    conv_str_day

    
    #include "sflconv.h"
    
    int
    
    conv_str_day (
    
        const char *string)
    
    

    Synopsis

    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.

    Source Code - (sflcvsd.c)

    
    {
    
        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);
    
    }
    
    
     

    conv_str_number

    
    #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          */
    
    )
    
    

    Synopsis

    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.
    The input string may contain digits, decimal point, thousand separators and a sign character or indicator. Formatting characters are only accepted if they correspond to the number format. A blank string is accepted as zero. A space following digits ends the number; anything further is ignored. Returns a string of width digits, including leading sign if that is required. Zeroes are signed with a space. Width must be at least 1. If the flag FLAG_N_DECIMALS is set, the last X digits are decimals, where X is the value of the decimals argument. Decimals are then accepted or rejected depending on the dec_format:
    DECS SHOW ALL Accept decimals.
    DECS DROP ZEROS Accept decimals.
    DECS HIDE ALL Reject decimals.
    DECS SCIENTIFIC Accept decimals.
    If the flag FLAG_N_SIGNED is set, accepts a leading or trailing sign, or a financial negative like this: (123). Returns a pointer to the formatted string, or null if the string was rejected. These are the possible reasons for rejection:

    Source Code - (sflcvsn.c)

    
    {
    
        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);
    
    }
    
    
     

    conv_str_time

    
    #include "sflconv.h"
    
    /*  We supply the dialog file as the source
    
    long
    
    conv_str_time (const char *p_string)
    
    

    Synopsis

    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.

    Source Code - (sflcvst.c)

    
    {
    
    
    
    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
    
    }
    
    
     

    conv_time_pict

    
    #include "sflconv.h"
    
    char *
    
    conv_time_pict (
    
        long time,
    
        const char *picture)
    
    

    Synopsis

    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
    Returns the formatted result. This is a static string, of at most 80 characters, that is overwritten by each call. If time is zero, returns an empty string. The 'h', 'm', 's', and 'c' formats output a leading space when used at the start of the picture. This is to improve the alignment of a column of times. If the previous character was a digit, these formats also output a space in place of the leading zero.

    Source Code - (sflcvtp.c)

    
    {
    
        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);
    
    }
    
    
     

    conv_time_str

    
    #include "sflconv.h"
    
    char *
    
    conv_time_str (
    
        long time,
    
        int  flags,
    
        char timesep,
    
        int  width)
    
    

    Synopsis

    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.
    Returns a pointer to a static area holding the string, or NULL if there was an error. If no flags are used, the width argument gives these results (shown as a picture, which is how conv_time_str works - see conv_time_pict):
    4 or less Error.
    5 to 7 "hh:mm"
    8 to 10 "hh:mm:ss"
    11 or more "hh:mm:ss:cc"
    If FLAG_T_COMPACT is used, width gives these results:
    3 or less Error.
    4 to 5 "hhmm"
    6 to 7 "hhmmss"
    8 or more "hhmmsscc"
    If FLAG_T_12_HOUR is used, width gives these results:
    5 or less Error.
    6 to 8 "hh:mma"
    9 to 11 "hh:mm:ssa"
    12 or more "hh:mm:ss:cca"
    If FLAG_T_COMPACT and FLAG_T_12_HOUR are used, width gives these results:
    4 or less Error.
    5 to 6 "hhmma"
    7 to 8 "hhmmssa"
    9 or more "hhmmsscca"

    Source Code - (sflcvts.c)

    
    {
    
        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));
    
    }
    
    

    Encryption and decryption functions

    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

    Synopsis

    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

    Notes

    
        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.
    
    

    List of Functions

    List of Symbol Definitions

    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
     

    crypt_encode

    
    #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                   */
    
    

    Synopsis

    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.
    The minimum buffer size, and key size depends on the algorithm:
    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.
    Use crypt decode() with the same algorithm and key to decrypt the buffer. The buffer size must be a multiple of the minimum size shown above. If you specify a buffer size of zero the function does nothing but returns TRUE. For portability, the buffer size is limited to 64k.

    Source Code - (sflcryp.c)

    
    {
    
        return (crypt_data (buffer, buffer_size, algorithm, key, TRUE));
    
    }
    
    
     

    crypt_decode

    
    #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                   */
    
    

    Synopsis

    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.
    The minimum buffer size, and key size depends on the algorithm:
    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.
    The buffer size must be a multiple of the minimum size shown above. If you specify a buffer size of zero the function does nothing but returns TRUE.

    Source Code - (sflcryp.c)

    
    {
    
        return (crypt_data (buffer, buffer_size, algorithm, key, FALSE));
    
    }
    
    
     

    calculate_crc

    
    #include "sflcryp.h"
    
    qbyte
    
    calculate_crc (byte *block, size_t length)
    
    

    Synopsis

    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.

    Source Code - (sflcryp.c)

    
    {
    
        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);
    
    }
    
    

    Date and time functions

    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

    Synopsis

    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).

    List of Functions

    List of Symbol Definitions

    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))
     

    date_now

    
    #include "sfldate.h"
    
    long
    
    date_now (void)
    
    

    Synopsis

    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.

    Source Code - (sfldate.c)

    
    {
    
        return (timer to date (time (NULL)));
    
    }
    
    
     

    time_now

    
    #include "sfldate.h"
    
    long
    
    time_now (void)
    
    

    Synopsis

    Returns the current time as a long value (HHMMSSCC). If the system clock does not return centiseconds, these are set to zero.

    Source Code - (sfldate.c)

    
    {
    
    #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
    
    }
    
    
     

    leap_year

    
    #include "sfldate.h"
    
    Bool
    
    leap_year (int year)
    
    

    Synopsis

    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.

    Source Code - (sfldate.c)

    
    {
    
        return ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0);
    
    }
    
    
     

    julian_date

    
    #include "sfldate.h"
    
    int
    
    julian_date (long date)
    
    

    Synopsis

    Returns the number of days since 31 December last year. The Julian date of 1 January is 1.

    Source Code - (sfldate.c)

    
    {
    
        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);
    
    }
    
    
     

    day_of_week

    
    #include "sfldate.h"
    
    int
    
    day_of_week (long date)
    
    

    Synopsis

    Returns the day of the week where 0 is Sunday, 1 is Monday, ... 6 is Saturday. Uses Zeller's Congurence algorithm.

    Source Code - (sfldate.c)

    
    {
    
        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));
    
    }
    
    
     

    next_weekday

    
    #include "sfldate.h"
    
    long
    
    next_weekday (long date)
    
    

    Synopsis

    Returns the date of the next weekday, skipping from Friday to Monday.

    Source Code - (sfldate.c)

    
    {
    
        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));
    
    }
    
    
     

    prev_weekday

    
    #include "sfldate.h"
    
    long
    
    prev_weekday (long date)
    
    

    Synopsis

    Returns the date of the previous weekday, skipping from Monday to Friday.

    Source Code - (sfldate.c)

    
    {
    
        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));
    
    }
    
    
     

    week_of_year

    
    #include "sfldate.h"
    
    int
    
    week_of_year (long date)
    
    

    Synopsis

    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.

    Source Code - (sfldate.c)

    
    {
    
        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);
    
    }
    
    
     

    year_quarter

    
    #include "sfldate.h"
    
    int
    
    year_quarter (long date)
    
    

    Synopsis

    Returns the year quarter, 1 to 4, depending on the month specified.

    Source Code - (sfldate.c)

    
    {
    
        return ((GET_MONTH (date) - 1) / 3 + 1);
    
    }
    
    
     

    default_century

    
    #include "sfldate.h"
    
    long
    
    default_century (long *date)
    
    

    Synopsis

    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.

    Source Code - (sfldate.c)

    
    {
    
        if (GET_CENTURY (*date) == 0)
    
            *date += (GET_YEAR (*date) > 50? 19000000L: 20000000L);
    
        return (*date);
    
    }
    
    
     

    pack_date

    
    #include "sfldate.h"
    
    word
    
    pack_date (long date)
    
    

    Synopsis

    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.

    Source Code - (sfldate.c)

    
    {
    
        return (word) (((GET_CCYEAR (date) - 1980) << 9) +
    
                        (GET_MONTH  (date) << 5) +
    
                         GET_DAY    (date));
    
    }
    
    
     

    pack_time

    
    #include "sfldate.h"
    
    word
    
    pack_time (long time)
    
    

    Synopsis

    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.

    Source Code - (sfldate.c)

    
    {
    
        return (word) ((GET_HOUR   (time) << 11) +
    
                       (GET_MINUTE (time) << 5)  +
    
                       (GET_SECOND (time) >> 1));
    
    }
    
    
     

    unpack_date

    
    #include "sfldate.h"
    
    long
    
    unpack_date (word packdate)
    
    

    Synopsis

    Converts a packed date back into a long value.

    Source Code - (sfldate.c)

    
    {
    
        int year;
    
    
    
        year = ((word) (packdate & 0xfe00) >> 9) + 80;
    
        return (MAKE_DATE (year > 80? 19: 20,
    
                           year,
    
                           (word) (packdate & 0x01e0) >> 5,
    
                           (word) (packdate & 0x001f)));
    
    }
    
    
     

    unpack_time

    
    #include "sfldate.h"
    
    long
    
    unpack_time (word packtime)
    
    

    Synopsis

    Converts a packed time back into a long value.

    Source Code - (sfldate.c)

    
    {
    
        return (MAKE_TIME ((word) (packtime & 0xf800) >> 11,
    
                           (word) (packtime & 0x07e0) >> 5,
    
                           (word) (packtime & 0x001f) << 1, 0));
    
    }
    
    
     

    date_to_days

    
    #include "sfldate.h"
    
    long
    
    date_to_days (long date)
    
    

    Synopsis

    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.

    Source Code - (sfldate.c)

    
    {
    
        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);
    
    }
    
    
     

    days_to_date

    
    #include "sfldate.h"
    
    long
    
    days_to_date (long days)
    
    

    Synopsis

    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.

    Source Code - (sfldate.c)

    
    {
    
        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));
    
    }
    
    
     

    date_to_timer

    
    #include "sfldate.h"
    
    time_t
    
    date_to_timer (long date, long time)
    
    

    Synopsis

    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.

    Source Code - (sfldate.c)

    
    {
    
        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));
    
    }
    
    
     

    timer_to_date

    
    #include "sfldate.h"
    
    long
    
    timer_to_date (time_t time_secs)
    
    

    Synopsis

    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).

    Source Code - (sfldate.c)

    
    {
    
        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));
    
          }
    
    }
    
    
     

    timer_to_time

    
    #include "sfldate.h"
    
    long
    
    timer_to_time (time_t time_secs)
    
    

    Synopsis

    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).

    Source Code - (sfldate.c)

    
    {
    
        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));
    
          }
    
    }
    
    
     

    timer_to_gmdate

    
    #include "sfldate.h"
    
    long
    
    timer_to_gmdate (time_t time_secs)
    
    

    Synopsis

    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.

    Source Code - (sfldate.c)

    
    {
    
        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));
    
          }
    
    }
    
    
     

    timer_to_gmtime

    
    #include "sfldate.h"
    
    long
    
    timer_to_gmtime (time_t time_secs)
    
    

    Synopsis

    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.

    Source Code - (sfldate.c)

    
    {
    
        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));
    
          }
    
    }
    
    
     

    time_to_csecs

    
    #include "sfldate.h"
    
    long
    
    time_to_csecs (long time)
    
    

    Synopsis

    Converts a time (HHMMSSCC) into a number of centiseconds.

    Source Code - (sfldate.c)

    
    {
    
        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)));
    
    }
    
    
     

    csecs_to_time

    
    #include "sfldate.h"
    
    long
    
    csecs_to_time (long csecs)
    
    

    Synopsis

    Converts a number of centiseconds (< INTERVAL_DAY) into a time value (HHMMSSCC).

    Source Code - (sfldate.c)

    
    {
    
        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));
    
    }
    
    
     

    future_date

    
    #include "sfldate.h"
    
    void
    
    future_date (long *date, long *time, long days, long csecs)
    
    

    Synopsis

    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.

    Source Code - (sfldate.c)

    
    {
    
        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);
    
    }
    
    
     

    past_date

    
    #include "sfldate.h"
    
    void
    
    past_date (long *date, long *time, long days, long csecs)
    
    

    Synopsis

    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.

    Source Code - (sfldate.c)

    
    {
    
        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);
    
    }
    
    
     

    date_diff

    
    #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               */
    
    )
    
    

    Synopsis

    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.

    Source Code - (sfldate.c)

    
    {
    
        *days  = date to days  (date1) - date to days  (date2);
    
        *csecs = time to csecs (time1) - time to csecs (time2);
    
    }
    
    
     

    valid_date

    
    #include "sfldate.h"
    
    Bool
    
    valid_date (long date)
    
    

    Synopsis

    Returns TRUE if the date is valid or zero; returns FALSE if the date is not valid.

    Source Code - (sfldate.c)

    
    {
    
        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);
    
    }
    
    
     

    valid_time

    
    #include "sfldate.h"
    
    Bool
    
    valid_time (long time)
    
    

    Synopsis

    Returns TRUE if the time is valid or zero; returns FALSE if the time is not valid.

    Source Code - (sfldate.c)

    
    {
    
        return (GET_SECOND (time) < 60
    
            &&  GET_MINUTE (time) < 60
    
            &&  GET_HOUR   (time) < 24);
    
    }
    
    
     

    date_is_future

    
    #include "sfldate.h"
    
    Bool
    
    date_is_future (long date, long time)
    
    

    Synopsis

    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.

    Source Code - (sfldate.c)

    
    {
    
        return (date  > date now ()
    
            || (date == date now () && time > time now ()));
    
    }
    
    
     

    date_is_past

    
    #include "sfldate.h"
    
    Bool
    
    date_is_past (long date, long time)
    
    

    Synopsis

    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.

    Source Code - (sfldate.c)

    
    {
    
        return (date  < date now ()
    
            || (date == date now () && time < time now ()));
    
    }
    
    
     

    timezone_string

    
    #include "sfldate.h"
    
    char *
    
    timezone_string (void)
    
    

    Synopsis

    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".

    Source Code - (sfldate.c)

    
    {
    
        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);
    
    }
    
    

    External data representation functions

    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

    Synopsis

    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.

    List of Functions

    List of Symbol Definitions

    sflexdr.h defines these symbols, possibly conditionally:
    Symbol: Defined as:
    _SFLEXDR_INCLUDED TRUE
     

    exdr_write

    
    #include "sflexdr.h"
    
    int
    
    exdr_write (byte *buffer, const char *format, ...)
    
    

    Synopsis

    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
    Each format sequence corresponds to one item in the list. The buffer must be large enough to hold the formatted result. Returns the size of the formatted data. Ignores invalid format characters; you can insert hyphens or spaces freely. Strings may be specified as (void *) NULL - they are stored as empty strings. Memory blocks may be specified as 0 and (void *) NULL together. Note that if you do not use the (void *) typecast when calling exdr write(), your code will fail on systems where an int is not the same size as a void *. Huge memory blocks cannot be more than 2^31 bytes large (2Gb) or 2^16 bytes if size_t is 16 bits large.

    Source Code - (sflexdr.c)

    
    {
    
        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));
    
    }
    
    
     

    exdr_writed

    
    #include "sflexdr.h"
    
    int
    
    exdr_writed (DESCR *buffer, const char *format, ...)
    
    

    Synopsis

    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.

    Source Code - (sflexdr.c)

    
    {
    
        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));
    
    }
    
    
     

    exdr_read

    
    #include "sflexdr.h"
    
    int
    
    exdr_read (const byte *buffer, const char *format, ...)
    
    

    Synopsis

    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

    Examples

    
        char *string = NULL;
    
        byte buffer [1000];
    
        byte *buffaddr = buffer;
    
        int value, length;
    
        exdr_read (buffer, "qdsmM", NULL, &value, &string, &length, &buffaddr);
    
    

    Source Code - (sflexdr.c)

    
    {
    
        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);
    
    }
    
    

    Fast string searching functions

    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

    Synopsis

    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).

    List of Functions

    List of Symbol Definitions

    sflfind.h defines these symbols, possibly conditionally:
    Symbol: Defined as:
    _SFLFIND_INCLUDED TRUE
     

    strfind

    
    #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        */
    
    

    Synopsis

    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).

    Examples

    
        char *result;
    
    
    
        result = strfind ("abracadabra", "cad", FALSE);
    
        if (result)
    
            puts (result);
    
    

    Source Code - (sflfind.c)

    
    {
    
        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);
    
    }
    
    
     

    strfind_r

    
    #include "sflfind.h"
    
    char *
    
    strfind_r (const char *string,          /*  String containing data           */
    
               const char *pattern)         /*  Pattern to search for            */
    
    

    Synopsis

    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.

    Examples

    
        char *result;
    
    
    
        result = strfind_r ("abracadabra", "cad");
    
        if (result)
    
            puts (result);
    
    

    Source Code - (sflfind.c)

    
    {
    
        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);
    
    }
    
    
     

    strfind_rb

    
    #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      */
    
    

    Synopsis

    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.

    Examples

    
        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);
    
          }
    
    
    
    

    Source Code - (sflfind.c)

    
    {
    
        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);
    
    }
    
    
     

    memfind

    
    #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        */
    
    

    Synopsis

    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).

    Source Code - (sflfind.c)

    
    {
    
        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);
    
    }
    
    
     

    memfind_r

    
    #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            */
    
    

    Synopsis

    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.

    Source Code - (sflfind.c)

    
    {
    
        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);
    
    }
    
    
     

    memfind_rb

    
    #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 */
    
    

    Synopsis

    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.

    Source Code - (sflfind.c)

    
    {
    
        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                    */
    
    }
    
    
     

    txtfind

    
    #include "sflfind.h"
    
    char *
    
    txtfind (const char *string,            /*  String containing data           */
    
             const char *pattern)           /*  Pattern to search for            */
    
    

    Synopsis

    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.

    Examples

    
        char *result;
    
    
    
        result = txtfind ("AbracaDabra", "cad");
    
        if (result)
    
            puts (result);
    
    

    Source Code - (sflfind.c)

    
    {
    
        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                    */
    
    }
    
    

    File-access functions

    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

    Synopsis

    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.

    List of Functions

    List of Symbol Definitions

    sflfile.h defines these symbols, possibly conditionally:
    Symbol: Defined as:
    FILE_DIR_MAX 64 /* Max size of directory name */
    FILE_NAME_MAX 160 /* Max size of filename */
    FOPEN_APPEND_BINARY (various)
    FOPEN_APPEND_TEXT (various)
    FOPEN_READ_BINARY (various)
    FOPEN_READ_TEXT (various)
    FOPEN_WRITE_BINARY (various)
    FOPEN_WRITE_TEXT (various)
    _SFLFILE_INCLUDED TRUE
     

    file_open

    
    #include "sflfile.h"
    
    FILE *
    
    file_open (
    
        const char *filename,               /*  Name of file to open             */
    
        char mode)                          /*  'r', 'w', or 'a'                 */
    
    

    Synopsis

    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.

    Source Code - (sflfile.c)

    
    {
    
        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                     */
    
    }
    
    
     

    file_locate

    
    #include "sflfile.h"
    
    FILE *
    
    file_locate (
    
        const char *path,
    
        const char *name,
    
        const char *ext)
    
    

    Synopsis

    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.

    Source Code - (sflfile.c)

    
    {
    
        char
    
            *filename;
    
    
    
        ASSERT (name);
    
        filename = file where ('r', path, name, ext);
    
        if (filename)
    
            return (file open (filename, 'r'));
    
        else
    
            return (NULL);
    
    }
    
    
     

    file_close

    
    #include "sflfile.h"
    
    int
    
    file_close (
    
        FILE *stream)
    
    

    Synopsis

    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.

    Source Code - (sflfile.c)

    
    {
    
        ASSERT (stream);
    
        return (fclose (stream));
    
    }
    
    
     

    file_read

    
    #include "sflfile.h"
    
    Bool
    
    file_read (
    
        FILE *stream,
    
        char *string)
    
    

    Synopsis

    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.

    Source Code - (sflfile.c)

    
    {
    
        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);
    
              }
    
          }
    
    }
    
    
     

    file_write

    
    #include "sflfile.h"
    
    char *
    
    file_write (
    
        FILE *stream,
    
        const char *string)
    
    

    Synopsis

    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.

    Source Code - (sflfile.c)

    
    {
    
        ASSERT (stream);
    
        ASSERT (string);
    
    
    
        fputs (string, stream);
    
        if (file_crlf)
    
            fputc ('\r', stream);
    
    
    
        if (fputc ('\n', stream) == EOF)
    
            return (NULL);
    
        else
    
            return ((char *) string);
    
    }
    
    
     

    file_copy

    
    #include "sflfile.h"
    
    int
    
    file_copy (
    
        const char *dest,
    
        const char *src,
    
        char mode)
    
    

    Synopsis

    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.

    Source Code - (sflfile.c)

    
    {
    
        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);
    
    }
    
    
     

    file_rename

    
    #include "sflfile.h"
    
    int
    
    file_rename (
    
        const char *oldname,
    
        const char *newname)
    
    

    Synopsis

    Renames a file from oldname to newname. Returns 0 if okay, or - 1 if there was an error. Does not overwrite existing files.

    Source Code - (sflfile.c)

    
    {
    
        ASSERT (oldname);
    
        ASSERT (newname);
    
    
    
        return (rename (oldname, newname));
    
    }
    
    
     

    file_delete

    
    #include "sflfile.h"
    
    int
    
    file_delete (
    
        const char *filename)
    
    

    Synopsis

    Deletes the specified file. Returns 0 if okay, -1 in case of an error.

    Source Code - (sflfile.c)

    
    {
    
    #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
    
    }
    
    
     

    file_exists

    
    #include "sflfile.h"
    
    Bool
    
    file_exists (
    
        const char *filename)
    
    

    Synopsis

    Returns TRUE if the file exists, or FALSE if it does not.

    Source Code - (sflfile.c)

    
    {
    
        ASSERT (filename);
    
        return (file_mode (filename) > 0);
    
    }
    
    
     

    file_where

    
    #include "sflfile.h"
    
    char *
    
    file_where (
    
        char mode,
    
        const char *path,
    
        const char *name,
    
        const char *ext)
    
    

    Synopsis

    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.
    When the mode is 'r' or 'a', searches the current directory before considering the path value. When the path cannot be translated, and is not null or empty, it is used as a literal value. The name argument is the filename with or without extension. It will be prefixed by the path and suffixed by the extension, if required. The ext argument is a default or mandatory extension. If ext starts with a dot, it is mandatory and always used. Otherwise it is used only if the name does not already have an extension. If ext is NULL or empty, it is ignored. The total length of a name including path, name, extension, and any delimiters is FILE_NAME_MAX. Names are truncated if too long. The maximum size of one directory component is FILE_DIR_MAX chars. All parameters are case- sensitive; the precise effect of this depends on the system. On MS-DOS, filenames are always folded to uppercase, but the path must be supplied in uppercase correctly. On UNIX, all parameters are case sensitive. On VMS, path and filenames are folded into uppercase. Returns a pointer to a static character array containing the filename; if mode is 'r' and the file does not exist, returns NULL. If the mode is 'w', 'a', or 's', always returns a valid filename. Under VMS, all filenames are handled in POSIX mode, i.e. /disk/path/file instead of $disk:[path]file.

    Source Code - (sflfile.c)

    
    {
    
        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);
    
              }
    
          }
    
    }
    
    
     

    file_cycle

    
    #include "sflfile.h"
    
    Bool
    
    file_cycle (
    
        const char *filename,
    
        int how)
    
    

    Synopsis

    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

    Source Code - (sflfile.c)

    
    {
    
        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                  */
    
    }
    
    
     

    file_cycle_needed

    
    #include "sflfile.h"
    
    Bool
    
    file_cycle_needed (
    
        const char *filename,
    
        int how)
    
    

    Synopsis

    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
    If the specified file does not exist or is not accessible, returns FALSE.

    Source Code - (sflfile.c)

    
    {
    
        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);
    
    }
    
    
     

    file_has_changed

    
    #include "sflfile.h"
    
    Bool
    
    file_has_changed (
    
        const char *filename,
    
        long old_date,
    
        long old_time)
    
    

    Synopsis

    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.

    Source Code - (sflfile.c)

    
    {
    
        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);
    
    }
    
    
     

    safe_to_extend

    
    #include "sflfile.h"
    
    Bool
    
    safe_to_extend (
    
        const char *filename)
    
    

    Synopsis

    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.

    Source Code - (sflfile.c)

    
    {
    
    #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);
    
    }
    
    
     

    default_extension

    
    #include "sflfile.h"
    
    char *
    
    default_extension (
    
        char *dest,
    
        const char *src,
    
        const char *ext)
    
    

    Synopsis

    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.

    Source Code - (sflfile.c)

    
    {
    
        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);
    
    }
    
    
     

    fixed_extension

    
    #include "sflfile.h"
    
    char *
    
    fixed_extension (
    
        char *dest,
    
        const char *src,
    
        const char *ext)
    
    

    Synopsis

    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.

    Source Code - (sflfile.c)

    
    {
    
        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));
    
    }
    
    
     

    strip_extension

    
    #include "sflfile.h"
    
    char *
    
    strip_extension (
    
        char *name)
    
    

    Synopsis

    Removes dot and extension from the name, if any was present. If the name contained multiple extensions, removes the last one only. Returns name.

    Source Code - (sflfile.c)

    
    {
    
        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);
    
    }
    
    
     

    strip_file_path

    
    #include "sflfile.h"
    
    char
    
    *strip_file_path (
    
        char *name)
    
    

    Synopsis

    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.

    Source Code - (sflfile.c)

    
    {
    
        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);
    
    }
    
    
     

    strip_file_name

    
    #include "sflfile.h"
    
    char
    
    *strip_file_name (
    
        char *name)
    
    

    Synopsis

    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 ".".

    Source Code - (sflfile.c)

    
    {
    
        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));
    
          }
    
    }
    
    
     

    file_is_readable

    
    #include "sflfile.h"
    
    Bool
    
    file_is_readable (
    
        const char *filename)
    
    

    Synopsis

    Returns TRUE if the current process can read the specified file or directory. The filename may end in a slash (/ or \).

    Source Code - (sflfile.c)

    
    {
    
        ASSERT (filename);
    
        return ((file_mode (clean path (filename)) & S_IREAD) != 0);
    
    }
    
    
     

    file_is_writeable

    
    #include "sflfile.h"
    
    Bool
    
    file_is_writeable (
    
        const char *filename)
    
    

    Synopsis

    Returns TRUE if the current process can write the specified file or directory. The filename may end in a slash (/ or \).

    Source Code - (sflfile.c)

    
    {
    
        ASSERT (filename);
    
    
    
        return ((file_mode (clean path (filename)) & S_IWRITE) != 0);
    
    }
    
    
     

    file_is_executable

    
    #include "sflfile.h"
    
    Bool
    
    file_is_executable (
    
        const char *filename)
    
    

    Synopsis

    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.

    Source Code - (sflfile.c)

    
    {
    
    #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
    
    }
    
    
     

    file_is_program

    
    #include "sflfile.h"
    
    Bool
    
    file_is_program (
    
        const char *filename)
    
    

    Synopsis

    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.

    Source Code - (sflfile.c)

    
    {
    
        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);
    
    }
    
    
     

    file_is_directory

    
    #include "sflfile.h"
    
    Bool
    
    file_is_directory (
    
        const char *filename)
    
    

    Synopsis

    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'.

    Source Code - (sflfile.c)

    
    {
    
        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);
    
    }
    
    
     

    file_exec_name

    
    #include "sflfile.h"
    
    char *
    
    file_exec_name (
    
        const char *filename)
    
    

    Synopsis

    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.

    Source Code - (sflfile.c)

    
    {
    
    #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
    
    }
    
    
     

    get_file_size

    
    #include "sflfile.h"
    
    long
    
    get_file_size (
    
        const char *filename)
    
    

    Synopsis

    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.

    Source Code - (sflfile.c)

    
    {
    
        struct stat
    
            stat_buf;
    
    
    
        ASSERT (filename);
    
    
    
        if (stat ((char *) filename, &stat_buf) == 0)
    
            return ((long) stat_buf.st_size);
    
        else
    
            return (-1);
    
    }
    
    
     

    get_file_time

    
    #include "sflfile.h"
    
    time_t
    
    get_file_time (
    
        const char *filename)
    
    

    Synopsis

    Returns the modification time of the specified file or directory. The returned time is suitable for feeding to localtime().

    Source Code - (sflfile.c)

    
    {
    
        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);
    
    }
    
    
     

    file_slurp

    
    #include "sflfile.h"
    
    DESCR *
    
    file_slurp (
    
        const char *filename)
    
    

    Synopsis

    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.

    Source Code - (sflfile.c)

    
    {
    
        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);
    
    }
    
    
     

    file_lines

    
    #include "sflfile.h"
    
    long
    
    file_lines (
    
        const char *filename)
    
    

    Synopsis

    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.

    Source Code - (sflfile.c)

    
    {
    
        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);
    
    }
    
    
     

    file_set_eoln

    
    #include "sflfile.h"
    
    dbyte
    
    file_set_eoln (char *dst, const char *src, dbyte src_size, Bool add_cr)
    
    

    Synopsis

    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.

    Source Code - (sflfile.c)

    
    {
    
        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));
    
    }
    
    
     

    get_tmp_file_name

    
    #include "sflfile.h"
    
    char *
    
    get_tmp_file_name (const char *path, qbyte *index, const char *ext)
    
    

    Synopsis

    Get a temporary file name.

    Source Code - (sflfile.c)

    
    {
    
        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);
    
    }
    
    

    Initialisation file access functions

    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

    Synopsis

    Provides functions to read an initialisation file that follows the MS-Windows style, i.e. consists of [Sections] followed by keyword = value lines.

    List of Functions

    List of Symbol Definitions

    sflini.h defines these symbols, possibly conditionally:
    Symbol: Defined as:
    _SLFINI_INCLUDED TRUE
     

    ini_find_section

    
    #include "sflini.h"
    
    Bool
    
    ini_find_section (
    
        FILE *inifile,
    
        char *section,
    
        Bool top)
    
    

    Synopsis

    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.

    Examples

    
        ;   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
    
            ...
    
    

    Source Code - (sflini.c)

    
    {
    
        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);
    
    }
    
    
     

    ini_scan_section

    
    #include "sflini.h"
    
    Bool
    
    ini_scan_section (
    
        FILE *inifile,
    
        char **keyword,
    
        char **value)
    
    

    Synopsis

    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.

    Source Code - (sflini.c)

    
    {
    
        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                      */
    
    }
    
    
     

    ini_dyn_load

    
    #include "sflini.h"
    
    SYMTAB *
    
    ini_dyn_load (
    
        SYMTAB *load_symtab,
    
        const char *filename)
    
    

    Synopsis

    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"
    Also creates a symbol for each section, with name equal to the section name, and value equal to a null string. Looks for the .ini file on the current PATH. The table is sorted after loading.

    Source Code - (sflini.c)

    
    {
    
        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);
    
    }
    
    
     

    ini_dyn_save

    
    #include "sflini.h"
    
    int
    
    ini_dyn_save (
    
        SYMTAB *symtab,
    
        const char *filename)
    
    

    Synopsis

    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.

    Source Code - (sflini.c)

    
    {
    
        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);
    
    }
    
    
     

    ini_dyn_changed

    
    #include "sflini.h"
    
    Bool
    
    ini_dyn_changed (
    
        SYMTAB *symtab)
    
    

    Synopsis

    Returns TRUE if the ini file loaded into the specified table has in the meantime been changed. Returns FALSE if not.

    Source Code - (sflini.c)

    
    {
    
        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);
    
    }
    
    
     

    ini_dyn_refresh

    
    #include "sflini.h"
    
    Bool
    
    ini_dyn_refresh (
    
        SYMTAB *symtab)
    
    

    Synopsis

    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.

    Source Code - (sflini.c)

    
    {
    
        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);
    
    }
    
    
     

    ini_dyn_value

    
    #include "sflini.h"
    
    char *
    
    ini_dyn_value (
    
        SYMTAB *symtab,
    
        const char *section,
    
        const char *keyword,
    
        const char *default_value)
    
    

    Synopsis

    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.

    Source Code - (sflini.c)

    
    {
    
        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));
    
    }
    
    
     

    ini_dyn_values

    
    #include "sflini.h"
    
    char **
    
    ini_dyn_values (
    
        SYMTAB *symtab,
    
        const char *section,
    
        const char *keyword,
    
        const char *default_value)
    
    

    Synopsis

    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.

    Source Code - (sflini.c)

    
    {
    
        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));
    
    }
    
    

    Multilanguage support

    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

    Synopsis

    Provides hard-coded multilanguage dictionaries for dates and numbers, The hard-coded dictionaries work with most European languages.

    List of Functions

    List of Symbol Definitions

    sfllang.h defines these symbols, possibly conditionally:
    Symbol: Defined as:
    USERLANG_TOP USERLANG_SV + 1
    _SFLLANG_INCLUDED TRUE
     

    set_userlang

    
    #include "sfllang.h"
    
    int
    
    set_userlang (int language)
    
    

    Synopsis

    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
    Returns 0 if okay, -1 if an unsupported language was specified.

    Source Code - (sfllang.c)

    
    {
    
        /*  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);
    
    }
    
    
     

    set_userlang_str

    
    #include "sfllang.h"
    
    int
    
    set_userlang_str (const char *language)
    
    

    Synopsis

    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
    Returns 0 if okay, -1 if an unsupported language was specified.

    Source Code - (sfllang.c)

    
    {
    
        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);
    
    }
    
    
     

    get_userlang

    
    #include "sfllang.h"
    
    int
    
    get_userlang (void)
    
    

    Synopsis

    Returns the current user language code.

    Source Code - (sfllang.c)

    
    {
    
        return (user_language);
    
    }
    
    
     

    get_userlang_str

    
    #include "sfllang.h"
    
    char *
    
    get_userlang_str (void)
    
    

    Synopsis

    Returns the current user language as a 2-character string.

    Source Code - (sfllang.c)

    
    {
    
        return (language_str [user_language]);
    
    }
    
    
     

    set_accents

    
    #include "sfllang.h"
    
    int
    
    set_accents (Bool accents)
    
    

    Synopsis

    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.

    Source Code - (sfllang.c)

    
    {
    
        use_accents = accents;
    
        return (0);
    
    }
    
    
     

    get_accents

    
    #include "sfllang.h"
    
    Bool
    
    get_accents (void)
    
    

    Synopsis

    Returns TRUE if accents are enabled, FALSE if not.

    Source Code - (sfllang.c)

    
    {
    
        return (use_accents);
    
    }
    
    
     

    get_units_name

    
    #include "sfllang.h"
    
    char *
    
    get_units_name (int units)
    
    

    Synopsis

    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.

    Source Code - (sfllang.c)

    
    {
    
        ASSERT (units >= 0 && units <= 19);
    
        return (handle_accents (units_table [units]));
    
    }
    
    
     

    get_tens_name

    
    #include "sfllang.h"
    
    char *
    
    get_tens_name (int tens)
    
    

    Synopsis

    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.

    Source Code - (sfllang.c)

    
    {
    
        ASSERT (tens >= 10 && tens < 100);
    
        return (handle_accents (tens_table [tens / 10 - 1]));
    
    }
    
    
     

    get_day_name

    
    #include "sfllang.h"
    
    char *
    
    get_day_name (int day)
    
    

    Synopsis

    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.

    Source Code - (sfllang.c)

    
    {
    
        ASSERT (day >= 0 && day <= 6);
    
        return (handle_accents (day_table [day]));
    
    }
    
    
     

    get_day_abbrev

    
    #include "sfllang.h"
    
    char *
    
    get_day_abbrev (int day, Bool upper)
    
    

    Synopsis

    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.

    Source Code - (sfllang.c)

    
    {
    
        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));
    
    }
    
    
     

    get_month_name

    
    #include "sfllang.h"
    
    char *
    
    get_month_name (int month)
    
    

    Synopsis

    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.

    Source Code - (sfllang.c)

    
    {
    
        ASSERT (month >= 1 && month <= 12);
    
        return (handle_accents (month_table [month - 1]));
    
    }
    
    
     

    get_month_abbrev

    
    #include "sfllang.h"
    
    char *
    
    get_month_abbrev (int month, Bool upper)
    
    

    Synopsis

    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.

    Source Code - (sfllang.c)

    
    {
    
        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));
    
    }
    
    

    Line buffering functions

    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

    Synopsis

    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.

    List of Functions

    List of Symbol Definitions

    sfllbuf.h defines these symbols, possibly conditionally:
    Symbol: Defined as:
    _SFLLBUF_INCLUDED TRUE
     

    linebuf_create

    
    #include "sfllbuf.h"
    
    LINEBUF *
    
    linebuf_create (size_t maxsize)
    
    

    Synopsis

    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).

    Source Code - (sfllbuf.c)

    
    {
    
        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);
    
    }
    
    
     

    linebuf_destroy

    
    #include "sfllbuf.h"
    
    void
    
    linebuf_destroy (LINEBUF *buffer)
    
    

    Synopsis

    Destroys a line buffer and frees its memory.

    Source Code - (sfllbuf.c)

    
    {
    
        ASSERT (buffer);
    
    
    
        mem_free (buffer-> data);
    
        mem_free (buffer);
    
    }
    
    
     

    linebuf_reset

    
    #include "sfllbuf.h"
    
    void
    
    linebuf_reset (LINEBUF *buffer)
    
    

    Synopsis

    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.

    Source Code - (sfllbuf.c)

    
    {
    
        ASSERT (buffer);
    
        buffer-> head = buffer-> data;
    
        buffer-> tail = buffer-> data;
    
    }
    
    
     

    linebuf_append

    
    #include "sfllbuf.h"
    
    void
    
    linebuf_append (LINEBUF *buffer, const char *line)
    
    

    Synopsis

    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.

    Source Code - (sfllbuf.c)

    
    {
    
        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);
    
    }
    
    
     

    linebuf_first

    
    #include "sfllbuf.h"
    
    char *
    
    linebuf_first (LINEBUF *buffer, DESCR *descr)
    
    

    Synopsis

    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.

    Source Code - (sfllbuf.c)

    
    {
    
        ASSERT (buffer);
    
        ASSERT (descr);
    
    
    
        return (linebuf next (buffer, descr, buffer-> tail));
    
    }
    
    
     

    linebuf_next

    
    #include "sfllbuf.h"
    
    char *
    
    linebuf_next (LINEBUF *buffer, DESCR *descr, const char *curline)
    
    

    Synopsis

    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.

    Source Code - (sfllbuf.c)

    
    {
    
        ASSERT (buffer);
    
        ASSERT (descr);
    
        ASSERT (curline);
    
    
    
        if (curline == buffer-> head)
    
            return (NULL);                  /*  We're at the end                 */
    
        else
    
            return (get_line (buffer, descr, curline));
    
    }
    
    
     

    linebuf_last

    
    #include "sfllbuf.h"
    
    char *
    
    linebuf_last (LINEBUF *buffer, DESCR *descr)
    
    

    Synopsis

    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.

    Source Code - (sfllbuf.c)

    
    {
    
        ASSERT (buffer);
    
        ASSERT (descr);
    
    
    
        return (linebuf prev (buffer, descr, buffer-> head));
    
    }
    
    
     

    linebuf_prev

    
    #include "sfllbuf.h"
    
    char *
    
    linebuf_prev (LINEBUF *buffer, DESCR *descr, const char *curline)
    
    

    Synopsis

    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.

    Source Code - (sfllbuf.c)

    
    {
    
        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);
    
          }
    
    }
    
    

    Linked-list functions

    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

    Synopsis

    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.

    List of Functions

    List of Symbol Definitions

    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)
     

    list_unlink

    
    #include "sfllist.h"
    
    void *
    
    list_unlink (
    
        void *list)
    
    

    Synopsis

    Unlinks the list from any list it may be in. Returns list.

    Source Code - (sfllist.c)

    
    {
    
        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);
    
    }
    
    
     

    list_relink

    
    #include "sfllist.h"
    
    void *
    
    list_relink (
    
        void *left,
    
        void *list,
    
        void *right)
    
    

    Synopsis

    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.

    Source Code - (sfllist.c)

    
    {
    
        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);
    
    }
    
    

    SMTP mailer function

    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

    Synopsis

    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.

    List of Functions

    List of Symbol Definitions

    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)))
     

    smtp_send_mail

    
    #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)
    
    

    Synopsis

    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.

    Source Code - (sflmail.c)

    
    {
    
       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;
    
    }
    
    
     

    srcdoc W (sflmail.c 336): function uuencode not known

    
    #include "(unknown)"
    
    static int uuencode (
    
       char *strIn,
    
       char *strOut)
    
    

    Synopsis

    Uuendcode a file, with the output going to a new file. This function is used by smtp_send_mail.

    Source Code - (sflmail.c)

    
    {
    
       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;
    
    }
    
    
     

    srcdoc W (sflmail.c 361): function putgroup not known

    
    #include "(unknown)"
    
    static void putgroup (
    
       char *strgroup,
    
       FILE *fp)
    
    

    Synopsis

    Write out 3 char group to uuendcoded file making it printable This function is used by uuencode.

    Source Code - (sflmail.c)

    
    {
    
       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);
    
    }
    
    
     

    srcdoc W (sflmail.c 391): function getreply not known

    
    #include "(unknown)"
    
    static int getreply (
    
       int iSocket)
    
    

    Synopsis

    Get a reply from the SMTP server and see thats it's not an error. This function is used by smtp_send_mail.

    Source Code - (sflmail.c)

    
    {
    
    
    
       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);
    
    }
    
    
     

    srcdoc W (sflmail.c 421): function getfilename not known

    
    #include "(unknown)"
    
    static char *getfilename (
    
       char *strFullPath)
    
    

    Synopsis

    Get's the name from the full path of a file. This function is used by smtp_send_mail.

    Source Code - (sflmail.c)

    
    {
    
       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;
    
    }
    
    
     

    get_last_smtp_message

    
    #include "sflmail.h"
    
    char *get_last_smtp_message (void)
    
    

    Synopsis

    Get's the last message the smtp server submited.

    Source Code - (sflmail.c)

    
    {
    
       return strlast_smtp_message;
    
    }
    
    

    Mathematic functions

    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

    Synopsis

    Provides miscellaneous mathematical functions, including calculation of points within areas.

    List of Functions

    List of Symbol Definitions

    sflmath.h defines these symbols, possibly conditionally:
    Symbol: Defined as:
    _SFLMATH_INCLUDED TRUE
     

    point_in_rect

    
    #include "sflmath.h"
    
    int
    
    point_in_rect (const FPOINT *point, const FPOINT *coords)
    
    

    Synopsis

    Checks if the requested FPOINT is within the specified rectangle. Returns TRUE or FALSE appropriately.

    Source Code - (sflmath.c)

    
    {
    
        return ((point-> x >= coords [0].x && point-> x <= coords [1].x)
    
             && (point-> y >= coords [0].y && point-> y <= coords [1].y));
    
    }
    
    
     

    point_in_circle

    
    #include "sflmath.h"
    
    int
    
    point_in_circle (const FPOINT *point, const FPOINT *coords)
    
    

    Synopsis

    Checks if the requested FPOINT is within the specified circle. Returns TRUE or FALSE appropriately.

    Source Code - (sflmath.c)

    
    {
    
        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);
    
    }
    
    
     

    point_in_poly

    
    #include "sflmath.h"
    
    int
    
    point_in_poly (const FPOINT *point, const FPOINT *pgon, int nbpoints)
    
    

    Synopsis

    Checks if the requested FPOINT is within the specified polygon. Returns TRUE or FALSE.

    Source Code - (sflmath.c)

    
    {
    
        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);
    
    }
    
    

    Message-file access functions

    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

    Synopsis

    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.

    List of Functions

    List of Symbol Definitions

    sflmesg.h defines these symbols, possibly conditionally:
    Symbol: Defined as:
    ERROR_ANY 0000 /* Generic error message */
    _SFLMESG_INCLUDED TRUE
     

    open_message_file

    
    #include "sflmesg.h"
    
    int
    
    open_message_file (const char *filename)
    
    

    Synopsis

    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.

    Source Code - (sflmesg.c)

    
    {
    
        int feedback;
    
    
    
        close message file ();
    
        msgfile = file open (filename, 'r');
    
    
    
        if (msgfile)
    
            feedback = 0;
    
        else
    
            feedback = -1;
    
    
    
        return (feedback);
    
    }
    
    
     

    close_message_file

    
    #include "sflmesg.h"
    
    void
    
    close_message_file (void)
    
    

    Synopsis

    Closes the currently open message file, if any. Does not return anything.

    Source Code - (sflmesg.c)

    
    {
    
        if (msgfile)
    
          {
    
            file close (msgfile);
    
            msgfile = NULL;
    
          }
    
    }
    
    
     

    print_message

    
    #include "sflmesg.h"
    
    void
    
    print_message (int msgid, ...)
    
    

    Synopsis

    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.

    Source Code - (sflmesg.c)

    
    {
    
        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);
    
    }
    
    
     

    message_text

    
    #include "sflmesg.h"
    
    char *
    
    message_text (int msgid)
    
    

    Synopsis

    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.

    Source Code - (sflmesg.c)

    
    {
    
        read_msg (msgid);                   /*  Retrieve message into msgline    */
    
        return (msgline);
    
    }
    
    

    Memory allocation functions

    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

    Synopsis

    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.

    List of Functions

    List of Symbol Definitions

    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)

    List of Type Definitions

    Type name: Defined as:
    MEMHDR struct _MEMHDR
    MEMTRN struct _MEMTRN
    scavenger Bool (*) (void *)
     

    mem_alloc_

    
    #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    */
    
    )
    
    

    Synopsis

    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.

    Source Code - (sflmem.c)

    
    {
    
        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       */
    
    }
    
    
     

    mem_realloc_

    
    #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    */
    
    )
    
    

    Synopsis

    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.

    Source Code - (sflmem.c)

    
    {
    
        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));
    
    }
    
    
     

    mem_strdup_

    
    #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    */
    
    )
    
    

    Synopsis

    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.

    Source Code - (sflmem.c)

    
    {
    
        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);
    
    }
    
    
     

    mem_strfree_

    
    #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    */
    
    )
    
    

    Synopsis

    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.

    Examples

    
        char
    
            *string1 = NULL,
    
            *string2 = NULL;
    
        string1 = mem_strdup ("This is a string");
    
        mem_strfree (&string1);
    
        mem_strfree (&string2);
    
    

    Source Code - (sflmem.c)

    
    {
    
        ASSERT (string);
    
        if (*string)
    
          {
    
            mem free  (*string, filename, lineno);
    
            *string = NULL;
    
          }
    
    }
    
    
     

    mem_free_

    
    #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    */
    
    )
    
    

    Synopsis

    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.

    Source Code - (sflmem.c)

    
    {
    
        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);
    
    }
    
    
     

    mem_assert_

    
    #include "sflmem.h"
    
    void
    
    mem_assert_ (
    
        const char *filename,               /*  Name of source file making call  */
    
        word lineno                         /*  Line number in calling source    */
    
    )
    
    

    Synopsis

    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.

    Source Code - (sflmem.c)

    
    {
    
        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 ();
    
          }
    
    }
    
    
     

    mem_checkall_

    
    #include "sflmem.h"
    
    void
    
    mem_checkall_ (
    
        const char *filename,               /*  Name of source file making call  */
    
        word lineno                         /*  Line number in calling source    */
    
    )
    
    

    Synopsis

    Checks all allocated memory blocks; if any block was corrupted, aborts with an error message, else does nothing.

    Source Code - (sflmem.c)

    
    {
    
        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;
    
          }
    
    }
    
    
     

    mem_check_

    
    #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    */
    
    )
    
    

    Synopsis

    Checks that a block of memory has not been corrupted. If the block is corrupted, aborts with an error message, else does nothing.

    Source Code - (sflmem.c)

    
    {
    
        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);
    
    }
    
    
     

    mem_descr_

    
    #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    */
    
    )
    
    

    Synopsis

    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.

    Source Code - (sflmem.c)

    
    {
    
        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);
    
    }
    
    
     

    mem_new_trans_

    
    #include "sflmem.h"
    
    MEMTRN *
    
    mem_new_trans_(
    
        const char *filename,               /*  Name of source file making call  */
    
        word lineno                         /*  Line number in calling source    */
    
    )
    
    

    Synopsis

    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.

    Source Code - (sflmem.c)

    
    {
    
        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              */
    
    }
    
    
     

    mem_commit_

    
    #include "sflmem.h"
    
    void
    
    mem_commit_ (
    
        MEMTRN *trn,
    
        const char *filename,               /*  Name of source file making call  */
    
        word lineno                         /*  Line number in calling source    */
    
    )
    
    

    Synopsis

    Commits all blocks allocated to a transaction.

    Source Code - (sflmem.c)

    
    {
    
        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);
    
    }
    
    
     

    mem_rollback_

    
    #include "sflmem.h"
    
    void
    
    mem_rollback_ (
    
        MEMTRN *trn,
    
        const char *filename,               /*  Name of source file making call  */
    
        word lineno                         /*  Line number in calling source    */
    
    )
    
    

    Synopsis

    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.

    Source Code - (sflmem.c)

    
    {
    
        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);
    
    }
    
    
     

    mem_used

    
    #include "sflmem.h"
    
    long
    
    mem_used (void)
    
    

    Synopsis

    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.

    Source Code - (sflmem.c)

    
    {
    
        return (mem_size);
    
    }
    
    
     

    mem_allocs

    
    #include "sflmem.h"
    
    long
    
    mem_allocs (void)
    
    

    Synopsis

    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().

    Source Code - (sflmem.c)

    
    {
    
        return (mem_alloc_count);
    
    }
    
    
     

    mem_frees

    
    #include "sflmem.h"
    
    long
    
    mem_frees (void)
    
    

    Synopsis

    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().

    Source Code - (sflmem.c)

    
    {
    
        return (mem_free_count);
    
    }
    
    
     

    mem_display

    
    #include "sflmem.h"
    
    void
    
    mem_display (
    
        FILE *fp                            /*  File to dump display to          */
    
    )
    
    

    Synopsis

    Displays the contents of the memory allocation list.

    Source Code - (sflmem.c)

    
    {
    
        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);
    
    }
    
    
     

    mem_scavenger

    
    #include "sflmem.h"
    
    int
    
    mem_scavenger (
    
        scavenger scav_fct,                 /*  File to dump display to          */
    
        void    * scav_arg
    
    )
    
    

    Synopsis

    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.

    Source Code - (sflmem.c)

    
    {
    
        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);
    
    }
    
    

    MIME support functions

    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

    Synopsis

    Provides various functions that support MIME encoding and decoding. See RFC 1521 for details.

    Notes

    
        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.
    
    

    List of Functions

    List of Symbol Definitions

    sflmime.h defines these symbols, possibly conditionally:
    Symbol: Defined as:
    _SFLMIME_INCLUDED TRUE
     

    encode_base64

    
    #include "sflmime.h"
    
    size_t
    
    encode_base64 (const char *source, byte *target, size_t source_size)
    
    

    Synopsis

    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.

    Source Code - (sflmime.c)

    
    {
    
        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);
    
    }
    
    
     

    decode_base64

    
    #include "sflmime.h"
    
    size_t
    
    decode_base64 (const byte *source, char *target, size_t source_size)
    
    

    Synopsis

    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.

    Source Code - (sflmime.c)

    
    {
    
        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);
    
    }
    
    
     

    decode_mime_time

    
    #include "sflmime.h"
    
    Bool
    
    decode_mime_time (const char *mime_string, long *date, long *time)
    
    

    Synopsis

    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

    Source Code - (sflmime.c)

    
    {
    
        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);
    
    }
    
    
     

    encode_mime_time

    
    #include "sflmime.h"
    
    char *
    
    encode_mime_time (long date, long time)
    
    

    Synopsis

    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 "?".

    Source Code - (sflmime.c)

    
    {
    
        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 ("?");
    
    }
    
    

    Linked-list functions

    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

    Synopsis

    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.

    List of Functions

    List of Symbol Definitions

    sflnode.h defines these symbols, possibly conditionally:
    Symbol: Defined as:
    _SFLNODE_INCLUDED TRUE
    node_reset(node) (node)-> prev = (node)-> next = (node)
     

    node_create

    
    #include "sflnode.h"
    
    void *
    
    node_create (
    
        void *after,
    
        size_t size)
    
    

    Synopsis

    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.

    Examples

    
        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;
    
    

    Source Code - (sflnode.c)

    
    {
    
        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);
    
    }
    
    
     

    node_destroy

    
    #include "sflnode.h"
    
    void
    
    node_destroy (
    
        void *node)
    
    

    Synopsis

    Unlinks the specified node from any list it may be in, and frees its memory.

    Source Code - (sflnode.c)

    
    {
    
        ASSERT (node);
    
    
    
        node unlink (node);
    
        mem_free (node);
    
    }
    
    
     

    node_relink_after

    
    #include "sflnode.h"
    
    void *
    
    node_relink_after (
    
        void *node,
    
        void *after)
    
    

    Synopsis

    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.

    Source Code - (sflnode.c)

    
    {
    
        return (node relink (after, node, ((NODE *) after)-> next));
    
    }
    
    
     

    node_relink_before

    
    #include "sflnode.h"
    
    void *
    
    node_relink_before (
    
        void *node,
    
        void *before)
    
    

    Synopsis

    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.

    Source Code - (sflnode.c)

    
    {
    
        return (node relink (((NODE *) before)-> prev, node, before));
    
    }
    
    
     

    node_unlink

    
    #include "sflnode.h"
    
    void *
    
    node_unlink (
    
        void *node)
    
    

    Synopsis

    Unlinks the node from any list it may be in. Returns node.

    Source Code - (sflnode.c)

    
    {
    
        return (node relink (((NODE *) node)-> prev, node,
    
                             ((NODE *) node)-> next));
    
    }
    
    
     

    node_relink

    
    #include "sflnode.h"
    
    void *
    
    node_relink (
    
        void *left,
    
        void *node,
    
        void *right)
    
    

    Synopsis

    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.

    Source Code - (sflnode.c)

    
    {
    
        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);
    
    }
    
    

    Directory access functions

    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

    Synopsis

    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.

    List of Functions

    List of Symbol Definitions

    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

    List of Type Definitions

    Type name: Defined as:
    mode_t unsigned short
    nlink_t unsigned short
    off_t long
     

    open_dir

    
    #include "sfldir.h"
    
    Bool
    
    open_dir (
    
        DIRST *dir,
    
        const char *dir_name)
    
    

    Synopsis

    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.

    Source Code - (sfldir.c)

    
    {
    
        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
    
    }
    
    
     

    read_dir

    
    #include "sfldir.h"
    
    Bool
    
    read_dir (
    
        DIRST *dir)
    
    

    Synopsis

    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.

    Source Code - (sfldir.c)

    
    {
    
        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);
    
    }
    
    
     

    close_dir

    
    #include "sfldir.h"
    
    Bool
    
    close_dir (
    
        DIRST *dir)
    
    

    Synopsis

    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.

    Source Code - (sfldir.c)

    
    {
    
        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);
    
    }
    
    
     

    format_dir

    
    #include "sfldir.h"
    
    char *
    
    format_dir (
    
        DIRST *dir,
    
        Bool full)
    
    

    Synopsis

    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.

    Source Code - (sfldir.c)

    
    {
    
        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);
    
    }
    
    
     

    fix_dir

    
    #include "sfldir.h"
    
    int
    
    fix_dir (DIRST *dir)
    
    

    Synopsis

    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.

    Source Code - (sfldir.c)

    
    {
    
        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);
    
          }
    
    }
    
    
     

    free_dir

    
    #include "sfldir.h"
    
    int
    
    free_dir (DIRST *dir)
    
    

    Synopsis

    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.

    Source Code - (sfldir.c)

    
    {
    
        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);
    
    }
    
    
     

    load_dir_list

    
    #include "sfldir.h"
    
    NODE *
    
    load_dir_list (
    
        const char *dir_name,
    
        const char *sort)
    
    

    Synopsis

    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.
    If the sort string is NULL, no sort is carried out.

    Source Code - (sfldir.c)

    
    {
    
        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);
    
    }
    
    
     

    free_dir_list

    
    #include "sfldir.h"
    
    Bool
    
    free_dir_list (NODE *file_list)
    
    

    Synopsis

    Frees all FILELIST blocks in the specified linked list.

    Source Code - (sfldir.c)

    
    {
    
        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);
    
    }
    
    
     

    resolve_path

    
    #include "sfldir.h"
    
    char *
    
    resolve_path (
    
        const char *old_path)
    
    

    Synopsis

    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.

    Source Code - (sfldir.c)

    
    {
    
    #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
    
    }
    
    
     

    locate_path

    
    #include "sfldir.h"
    
    char *
    
    locate_path (
    
        const char *root,
    
        const char *path)
    
    

    Synopsis

    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.

    Source Code - (sfldir.c)

    
    {
    
    #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
    
    }
    
    
     

    clean_path

    
    #include "sfldir.h"
    
    char *
    
    clean_path (
    
        const char *path)
    
    

    Synopsis

    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.

    Source Code - (sfldir.c)

    
    {
    
    #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
    
    }
    
    
     

    get_curdir

    
    #include "sfldir.h"
    
    char *
    
    get_curdir (void)
    
    

    Synopsis

    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.

    Source Code - (sfldir.c)

    
    {
    
        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);
    
    }
    
    
     

    set_curdir

    
    #include "sfldir.h"
    
    int
    
    set_curdir (
    
        const char *path)
    
    

    Synopsis

    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.

    Source Code - (sfldir.c)

    
    {
    
        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 (&copy_path);
    
    #else
    
        feedback = -1;
    
    #endif
    
    
    
        return (feedback);
    
    }
    
    
     

    file_matches

    
    #include "sfldir.h"
    
    Bool
    
    file_matches (
    
        const char *filename,
    
        const char *pattern)
    
    

    Synopsis

    Returns TRUE if the filename matches the pattern. The pattern is a character string that can contain these 'wildcard' characters:

    Source Code - (sfldir.c)

    
    {
    
        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                     */
    
          }
    
    }
    
    
     

    make_dir

    
    #include "sfldir.h"
    
    int
    
    make_dir (
    
        const char *path_to_create)
    
    

    Synopsis

    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.

    Source Code - (sfldir.c)

    
    {
    
        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);
    
    }
    
    
     

    remove_dir

    
    #include "sfldir.h"
    
    int
    
    remove_dir (
    
        const char *path)
    
    

    Synopsis

    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.

    Source Code - (sfldir.c)

    
    {
    
    #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 (&copy_path);
    
          }
    
        return (feedback);
    
    #else
    
        return (-1);
    
    #endif
    
    }
    
    

    Process control functions

    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

    Synopsis

    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.

    List of Functions

    List of Symbol Definitions

    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

    List of Type Definitions

    Type name: Defined as:
    PROCESS qbyte
     

    process_create

    
    #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          */
    
    )
    
    

    Synopsis

    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 argv is NULL, parses the filename argument into words delimited by whitespace and builds the necessary argv table automatically. Use this feature to execute a command with arguments, specified as one string. The envv list consists of strings in the form "name=value", ending in a NULL pointer. If the envv argument is null, the environment of the current process is passed. Otherwise the envv environment is used. If the process is started correctly, this function will sleep for 'process_delay' milliseconds. If the child command detects an error at startup, it may exit with an error status. The sleep allows this error to be collected by calling process status() after this call. If process_delay is 0, any child error is ignored. Returns child process id, or 0 if there was an error. Under VMS, the filename must have been defined as a command before the calling process was started; the path is disregarded. Under OS/2 the filename can be the name of a CMD script, and this will be run with the interpreter specified in the first line (EXTPROC line, or "'/'*!" line; or failing that with the default command interpreter. Known bugs: when parsing filename argument into words, does not handle quotes in any special way; "this text" is 2 words, '"this' and 'text"'. You should have passed the filename through process esc() before adding any optional arguments.

    Source Code - (sflproc.c)

    
    {
    
    #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
    
    }
    
    
     

    process_status

    
    #include "sflproc.h"
    
    int
    
    process_status (
    
        PROCESS process)
    
    

    Synopsis

    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).
    In the case of PROCESS_ENDED_ERROR, the global variable process_errno is set to the exit code returned by the process.

    Source Code - (sflproc.c)

    
    {
    
    #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
    
    }
    
    
     

    process_kill

    
    #include "sflproc.h"
    
    int
    
    process_kill (
    
        PROCESS process)
    
    

    Synopsis

    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.

    Source Code - (sflproc.c)

    
    {
    
    #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
    
    }
    
    
     

    process_close

    
    #include "sflproc.h"
    
    void
    
    process_close (
    
        PROCESS process)
    
    

    Synopsis

    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.

    Source Code - (sflproc.c)

    
    {
    
    #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
    
    }
    
    
     

    process_server

    
    #include "sflproc.h"
    
    int
    
    process_server (
    
        const char *workdir,                /*  Where server runs, or NULL/""    */
    
        const char *lockfile)               /*  For exclusive execution          */
    
    

    Synopsis

    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:

    On other systems, may do nothing.

    Source Code - (sflproc.c)

    
    {
    
    #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
    
    }
    
    
     

    process_alarm

    
    #include "sflproc.h"
    
    Bool
    
    process_alarm (long delay)
    
    

    Synopsis

    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.

    Source Code - (sflproc.c)

    
    {
    
    #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
    
    }
    
    
     

    process_esc

    
    #include "sflproc.h"
    
    char *
    
    process_esc (char *dest, const char *src)
    
    

    Synopsis

    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.

    Source Code - (sflproc.c)

    
    {
    
    #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);
    
    }
    
    
     

    process_unesc

    
    #include "sflproc.h"
    
    char *
    
    process_unesc (char *dest, const char *src)
    
    

    Synopsis

    Does the reverse translaction to process esc().

    Source Code - (sflproc.c)

    
    {
    
        /*  Copy to dest, allocate if necessary                                  */
    
        if (dest != src)
    
            dest = xstrcpy (dest, src, NULL);
    
        strconvch (dest, ESC_CHAR, ' ');
    
        return (dest);
    
    }
    
    
     

    process_priority

    
    #include "sflproc.h"
    
    int
    
    process_priority (int priority)
    
    

    Synopsis

    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.

    Source Code - (sflproc.c)

    
    {
    
    #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
    
    }
    
    

    Time-slot functions

    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

    Synopsis

    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.

    List of Functions

    List of Symbol Definitions

    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
     

    year_range_empty

    
    #include "sflslot.h"
    
    void
    
    year_range_empty (byte *range)
    
    

    Synopsis

    Excludes all days in the year (sets all bits to zero).

    Source Code - (sflslot.c)

    
    {
    
        memset (range, 0, sizeof (year_range));
    
    }
    
    
     

    year_range_fill

    
    #include "sflslot.h"
    
    void
    
    year_range_fill (byte *range)
    
    

    Synopsis

    Includes all days in the year (sets all bits to 1).

    Source Code - (sflslot.c)

    
    {
    
        memset (range, 255, sizeof (year_range));
    
    }
    
    
     

    year_slot_clear

    
    #include "sflslot.h"
    
    int
    
    year_slot_clear (byte *range, int day_from, int day_to)
    
    

    Synopsis

    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.

    Source Code - (sflslot.c)

    
    {
    
        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                        */
    
    }
    
    
     

    year_slot_set

    
    #include "sflslot.h"
    
    int
    
    year_slot_set (byte *range, int day_from, int day_to)
    
    

    Synopsis

    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.

    Source Code - (sflslot.c)

    
    {
    
        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                        */
    
    }
    
    
     

    year_slot_filled

    
    #include "sflslot.h"
    
    Bool
    
    year_slot_filled (const byte *range, int day)
    
    

    Synopsis

    Returns TRUE if the specified day slot is set; returns FALSE if the slot is not set.

    Source Code - (sflslot.c)

    
    {
    
        ASSERT (day >= 0 && day < MAX_DAY);
    
        return ((range [BYTE (day)] & BIT (day)) > 0);
    
    }
    
    
     

    day_range_empty

    
    #include "sflslot.h"
    
    void
    
    day_range_empty (byte *range)
    
    

    Synopsis

    Excludes all minutes in the day (sets all bits to zero).

    Source Code - (sflslot.c)

    
    {
    
        memset (range, 0, sizeof (day_range));
    
    }
    
    
     

    day_range_fill

    
    #include "sflslot.h"
    
    void
    
    day_range_fill (byte *range)
    
    

    Synopsis

    Includes all minutes in the day (sets all bits to 1).

    Source Code - (sflslot.c)

    
    {
    
        memset (range, 255, sizeof (day_range));
    
    }
    
    
     

    day_slot_clear

    
    #include "sflslot.h"
    
    int
    
    day_slot_clear (byte *range, int min_from, int min_to)
    
    

    Synopsis

    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.

    Source Code - (sflslot.c)

    
    {
    
        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                        */
    
    }
    
    
     

    day_slot_set

    
    #include "sflslot.h"
    
    int
    
    day_slot_set (byte *range, int min_from, int min_to)
    
    

    Synopsis

    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.

    Source Code - (sflslot.c)

    
    {
    
        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                        */
    
    }
    
    
     

    day_slot_filled

    
    #include "sflslot.h"
    
    Bool
    
    day_slot_filled (const byte *range, int minute)
    
    

    Synopsis

    Returns TRUE if the specified minute slot is set; returns FALSE if the slot is not set.

    Source Code - (sflslot.c)

    
    {
    
        ASSERT (minute >= 0 && minute < MAX_MIN);
    
        return ((range [BYTE (minute)] & BIT (minute)) > 0);
    
    }
    
    
     

    date_to_day

    
    #include "sflslot.h"
    
    int
    
    date_to_day (long date)
    
    

    Synopsis

    Extracts the day value (1..366) for the specified date. The date is an 8-digit number: YYYYMMDD.

    Source Code - (sflslot.c)

    
    {
    
        return (julian date (date));
    
    }
    
    
     

    time_to_min

    
    #include "sflslot.h"
    
    int
    
    time_to_min (long time)
    
    

    Synopsis

    Extracts the minute value (0..1439) for the specified time. The time is an 8-digit number: HHMMSSCC.

    Source Code - (sflslot.c)

    
    {
    
        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);
    
    }
    
    

    String-handling functions

    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

    Synopsis

    Provides various string-handling functions. Some of these functions are available on some but not all platforms; others are useful tools for string handling.

    List of Functions

    List of Symbol Definitions

    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)
     

    strdupl

    
    #include "sflstr.h"
    
    char *
    
    strdupl (
    
        const char *string)
    
    

    Synopsis

    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.

    Source Code - (sflstr.c)

    
    {
    
        char *copy;
    
    
    
        if (string)
    
          {
    
            copy = malloc (strlen (string) + 1);
    
            if (copy)
    
                strcpy (copy, string);
    
          }
    
        else
    
            copy = NULL;
    
    
    
        return (copy);
    
    }
    
    
     

    strfree

    
    #include "sflstr.h"
    
    char **
    
    strfree (
    
        char **string)
    
    

    Synopsis

    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.

    Source Code - (sflstr.c)

    
    {
    
        ASSERT (string);
    
        if (*string)
    
          {
    
            free (*string);
    
            *string = NULL;
    
          }
    
        return (string);
    
    }
    
    
     

    strskp

    
    #include "sflstr.h"
    
    char *
    
    strskp (
    
        const char *string)
    
    

    Synopsis

    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.

    Source Code - (sflstr.c)

    
    {
    
        char
    
            *skip = (char *) string;
    
    
    
        ASSERT (string);
    
        while (*skip == ' ')
    
            skip++;
    
        return (skip);
    
    }
    
    
     

    strcset

    
    #include "sflstr.h"
    
    char *
    
    strcset (
    
        char *string,
    
        char ch)
    
    

    Synopsis

    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.

    Source Code - (sflstr.c)

    
    {
    
        char *scan;
    
    
    
        ASSERT (string);
    
        scan = string;
    
        while (*scan)
    
            *scan++ = ch;
    
        return (string);
    
    }
    
    
     

    strpad

    
    #include "sflstr.h"
    
    char *
    
    strpad (
    
        char *string,
    
        char ch,
    
        int  length)
    
    

    Synopsis

    Returns string of length characters, padding with ch or truncating if necessary. String must be at least length + 1 long.

    Source Code - (sflstr.c)

    
    {
    
        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           */
    
    }
    
    
     

    strlwc

    
    #include "sflstr.h"
    
    char *
    
    strlwc (
    
        char *string)
    
    

    Synopsis

    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).

    Source Code - (sflstr.c)

    
    {
    
        char *scan;
    
    
    
        if (string)
    
          {
    
            scan = string;
    
            while (*scan)
    
              {
    
                *scan = (char) tolower (*scan);
    
                scan++;
    
              }
    
          }
    
        return (string);
    
    }
    
    
     

    strupc

    
    #include "sflstr.h"
    
    char *
    
    strupc (
    
        char *string)
    
    

    Synopsis

    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).

    Source Code - (sflstr.c)

    
    {
    
        char *scan;
    
    
    
        if (string)
    
          {
    
            scan = string;
    
            while (*scan)
    
              {
    
                *scan = (char) toupper (*scan);
    
                scan++;
    
              }
    
          }
    
        return (string);
    
    }
    
    
     

    strcrop

    
    #include "sflstr.h"
    
    char *
    
    strcrop (
    
        char *string)
    
    

    Synopsis

    Drops trailing whitespace from string by truncating string to the last non-whitespace character. Returns string. If string is null, returns null.

    Source Code - (sflstr.c)

    
    {
    
        char *last;
    
    
    
        if (string)
    
          {
    
            last = string + strlen (string);
    
            while (last > string)
    
              {
    
                if (!isspace (*(last - 1)))
    
                    break;
    
                last--;
    
              }
    
            *last = 0;
    
          }
    
        return (string);
    
    }
    
    
     

    stropen

    
    #include "sflstr.h"
    
    char *
    
    stropen (
    
        char *string,
    
        Bool  align)
    
    

    Synopsis

    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.

    Source Code - (sflstr.c)

    
    {
    
        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);
    
    }
    
    
     

    strclose

    
    #include "sflstr.h"
    
    char *
    
    strclose (
    
        char *string,
    
        Bool align)
    
    

    Synopsis

    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.

    Source Code - (sflstr.c)

    
    {
    
        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);
    
    }
    
    
     

    strunique

    
    #include "sflstr.h"
    
    char *
    
    strunique (
    
        char *string,
    
        char  unique)
    
    

    Synopsis

    Reduces chains of some character to a single instances. For example: replace multiple spaces by one space. Returns string.

    Source Code - (sflstr.c)

    
    {
    
        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);
    
    }
    
    
     

    strmatch

    
    #include "sflstr.h"
    
    int
    
    strmatch (
    
        const char *string1,
    
        const char *string2)
    
    

    Synopsis

    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.

    Source Code - (sflstr.c)

    
    {
    
        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);
    
    }
    
    
     

    strprefixed

    
    #include "sflstr.h"
    
    Bool
    
    strprefixed (
    
        const char *string,
    
        const char *prefix)
    
    

    Synopsis

    If string starts with specified prefix, returns TRUE. If string does not start with specified prefix, returns FALSE.

    Source Code - (sflstr.c)

    
    {
    
        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);
    
    }
    
    
     

    strprefix

    
    #include "sflstr.h"
    
    char *
    
    strprefix (
    
        const char *string,
    
        const char *delims)
    
    

    Synopsis

    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.

    Source Code - (sflstr.c)

    
    {
    
        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);
    
    }
    
    
     

    strdefix

    
    #include "sflstr.h"
    
    char *
    
    strdefix (
    
        const char *string,
    
        const char *prefix)
    
    

    Synopsis

    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.

    Source Code - (sflstr.c)

    
    {
    
        ASSERT (string);
    
        ASSERT (prefix);
    
    
    
        if (strlen (string) >= strlen (prefix)
    
        &&  memcmp (string, prefix, strlen (prefix)) == 0)
    
            return ((char *) string + strlen (prefix));
    
        else
    
            return (NULL);
    
    }
    
    
     

    strhash

    
    #include "sflstr.h"
    
    qbyte
    
    strhash (
    
        const char *string)
    
    

    Synopsis

    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.

    Examples

    
        int index;
    
        index = (int) strhash (name) % TABLE_SIZE;
    
    

    Source Code - (sflstr.c)

    
    {
    
        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);
    
    }
    
    
     

    strconvch

    
    #include "sflstr.h"
    
    char *
    
    strconvch (
    
        char *string,
    
        char from,
    
        char to)
    
    

    Synopsis

    Converts all instances of one character in a string to some other character. Returns string. Does nothing if the string is NULL.

    Source Code - (sflstr.c)

    
    {
    
        char *scan;
    
    
    
        if (string)
    
          {
    
            scan = string;
    
            while (*scan)
    
              {
    
                if (*scan == from)
    
                   *scan = to;
    
                scan++;
    
              }
    
          }
    
        return (string);
    
    }
    
    
     

    xstrcat

    
    #include "sflstr.h"
    
    char *
    
    xstrcat (
    
        char *dest,
    
        const char *src, ...)
    
    

    Synopsis

    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.

    Source Code - (sflstr.c)

    
    {
    
        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);
    
    }
    
    
     

    xstrcpy

    
    #include "sflstr.h"
    
    char *
    
    xstrcpy (
    
        char *dest,
    
        const char *src, ...)
    
    

    Synopsis

    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.

    Source Code - (sflstr.c)

    
    {
    
        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);
    
    }
    
    
     

    lexcmp

    
    #include "sflstr.h"
    
    int
    
    lexcmp (
    
        const char *string1,
    
        const char *string2)
    
    

    Synopsis

    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

    Source Code - (sflstr.c)

    
    {
    
        int cmp;
    
    
    
        ASSERT (string1);
    
        ASSERT (string2);
    
    
    
        do
    
          {
    
            cmp = (byte) tolower (*string1) - (byte) tolower (*string2);
    
          }
    
        while (*string1++ && *string2++ && cmp == 0);
    
        return (cmp);
    
    }
    
    
     

    lexncmp

    
    #include "sflstr.h"
    
    int
    
    lexncmp (
    
        const char *string1,
    
        const char *string2,
    
        const int   count)
    
    

    Synopsis

    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

    Source Code - (sflstr.c)

    
    {
    
        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);
    
    }
    
    
     

    lexwcmp

    
    #include "sflstr.h"
    
    int
    
    lexwcmp (
    
        const char *string1,
    
        const char *pattern)
    
    

    Synopsis

    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

    Source Code - (sflstr.c)

    
    {
    
        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);
    
    }
    
    
     

    soundex

    
    #include "sflstr.h"
    
    char *
    
    soundex (
    
        const char *string)
    
    

    Synopsis

    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.

    Examples

    
        printf ("Soundex of %s = %s\n", argv [1], soundex (argv [1]));
    
    

    Source Code - (sflstr.c)

    
    {
    
        ASSERT (string);
    
        return (soundexn (string, 4, FALSE));
    
    }
    
    
     

    soundexn

    
    #include "sflstr.h"
    
    char *
    
    soundexn (
    
        const char *string, int size, Bool fold)
    
    

    Synopsis

    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.

    Source Code - (sflstr.c)

    
    {
    
    #   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);
    
    }
    
    
     

    strt2descr

    
    #include "sflstr.h"
    
    DESCR *
    
    strt2descr (
    
        char **table)
    
    

    Synopsis

    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.

    Source Code - (sflstr.c)

    
    {
    
        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);
    
    }
    
    
     

    descr2strt

    
    #include "sflstr.h"
    
    char **
    
    descr2strt (
    
        const DESCR *descr)
    
    

    Synopsis

    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.

    Source Code - (sflstr.c)

    
    {
    
        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);
    
    }
    
    
     

    strtfree

    
    #include "sflstr.h"
    
    void
    
    strtfree (
    
        char **table)
    
    

    Synopsis

    Releases a table of strings as created by descr2strt() or a similar function. If the argument is null, does nothing.

    Source Code - (sflstr.c)

    
    {
    
        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);
    
          }
    
    }
    
    
     

    removechars

    
    #include "sflstr.h"
    
    char *
    
    removechars (
    
        char *strbuf,
    
        char *chrstorm)
    
    

    Synopsis

    Removes known chars from a string. Returns pointer to head of the buffer. Submitted by Scott Beasley jscottb@infoave.com

    Source Code - (sflstr.c)

    
    {
    
       char *offset;
    
    
    
       offset = (char *)NULL;
    
    
    
       while (*strbuf)
    
          {
    
             offset = strpbrk (strbuf, chrstorm);
    
             if (offset)
    
                 strcpy (offset, (offset + 1));
    
             else
    
                 break;
    
          }
    
    
    
       return strbuf;
    
    }
    
    
     

    replacechrswith

    
    #include "sflstr.h"
    
    char *
    
    replacechrswith (
    
        char *strbuf,
    
        char *chrstorm,
    
        char chartorlcwith)
    
    

    Synopsis

    Subsitutes known char(s)in a string with another. Returns pointer to head of the buffer. Submitted by Scott Beasley jscottb@infoave.com

    Source Code - (sflstr.c)

    
    {
    
       char *offset;
    
    
    
       offset = (char *)NULL;
    
    
    
       while (*strbuf)
    
          {
    
             offset = strpbrk (strbuf, chrstorm);
    
             if (offset)
    
               {
    
                 *(offset)= chartorlcwith;
    
               }
    
    
    
             else
    
                 break;
    
          }
    
    
    
       return strbuf;
    
    }
    
    
     

    insertstring

    
    #include "sflstr.h"
    
    char *
    
    insertstring (
    
        char *strbuf,
    
        char *chrstoins,
    
        int pos)
    
    

    Synopsis

    Inserts a string into another string. Returns a pointer to head of the buffer. Submitted by Scott Beasley jscottb@infoave.com

    Source Code - (sflstr.c)

    
    {
    
       memmove ((strbuf + pos)+ strlen (chrstoins),
    
       (strbuf + pos), strlen ((strbuf + pos))+ 1);
    
       memcpy ((strbuf + pos), chrstoins, strlen (chrstoins));
    
    
    
       return strbuf;
    
    }
    
    
     

    insertchar

    
    #include "sflstr.h"
    
    char *
    
    insertchar (
    
        char *strbuf,
    
        char chrtoins,
    
        int pos)
    
    

    Synopsis

    Inserts a char into a string. Returns a pointer to head of the buffer. Submitted by Scott Beasley jscottb@infoave.com

    Source Code - (sflstr.c)

    
    {
    
       memmove ((strbuf + pos)+ 1,
    
       (strbuf + pos), strlen ((strbuf + pos))+ 1);
    
       *(strbuf + pos)= chrtoins;
    
    
    
       return strbuf;
    
    }
    
    
     

    leftfill

    
    #include "sflstr.h"
    
    char *
    
    leftfill (
    
        char *strbuf,
    
        char chrtofill,
    
        unsigned len)
    
    

    Synopsis

    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

    Source Code - (sflstr.c)

    
    {
    
       while (strlen (strbuf)< len)
    
         {
    
           insertchar (strbuf, chrtofill, 0);
    
         }
    
    
    
       return strbuf;
    
    }
    
    
     

    rightfill

    
    #include "sflstr.h"
    
    char *
    
    rightfill (
    
        char *strbuf,
    
        char chrtofill,
    
        unsigned len)
    
    

    Synopsis

    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

    Source Code - (sflstr.c)

    
    {
    
       while (strlen (strbuf)< len)
    
         {
    
           insertchar (strbuf, chrtofill, strlen (strbuf));
    
         }
    
    
    
       return strbuf;
    
    }
    
    
     

    trim

    
    #include "sflstr.h"
    
    char *
    
    trim (
    
        char *strin)
    
    

    Synopsis

    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

    Source Code - (sflstr.c)

    
    {
    
        ltrim (strin);
    
        strcrop (strin);
    
    
    
        return strin;
    
    }
    
    
     

    ltrim

    
    #include "sflstr.h"
    
    char *
    
    ltrim (
    
        char *string)
    
    

    Synopsis

    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.

    Source Code - (sflstr.c)

    
    {
    
       ASSERT (string);
    
    
    
       while (isspace(*string))
    
           deletechar(string,0);
    
    
    
       return string;
    
    }
    
    
     

    searchreplace

    
    #include "sflstr.h"
    
    char *
    
    searchreplace (
    
        char *strbuf,
    
        char *strtofnd,
    
        char *strtoins)
    
    

    Synopsis

    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

    Source Code - (sflstr.c)

    
    {
    
       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;
    
    }
    
    
     

    deletestring

    
    #include "sflstr.h"
    
    char *
    
    deletestring (
    
        char *strbuf,
    
        char *strtodel,
    
        int ignorecase)
    
    

    Synopsis

    Deletes all occurances of one string, in another string. Returns a pointer to head of the buffer. Submitted by Scott Beasley jscottb@infoave.com

    Source Code - (sflstr.c)

    
    {
    
       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;
    
    }
    
    
     

    getstrfld

    
    #include "sflstr.h"
    
    char *
    
    getstrfld (
    
        char *strbuf,
    
        int fldno,
    
        int ofset,
    
        char *sep,
    
        char *retstr)
    
    

    Synopsis

    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

    Source Code - (sflstr.c)

    
    {
    
       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;
    
    }
    
    
     

    setstrfld

    
    #include "sflstr.h"
    
    char *
    
    setstrfld (
    
        char *strbuf,
    
        int fldno,
    
        int ofset,
    
        char *sep,
    
        char *strtoins)
    
    

    Synopsis

    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

    Source Code - (sflstr.c)

    
    {
    
       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;
    
    }
    
    
     

    getstrfldlen

    
    #include "sflstr.h"
    
    int
    
    getstrfldlen (
    
        char *strbuf,
    
        int fldno,
    
        int ofset,
    
        char *sep)
    
    

    Synopsis

    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

    Source Code - (sflstr.c)

    
    {
    
       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;
    
    }
    
    
     

    findstrinfile

    
    #include "sflstr.h"
    
    char *
    
    findstrinfile (
    
        FILE *fp,
    
        char *strtofind,
    
        char *strretstr,
    
        int *iLnNo)
    
    

    Synopsis

    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

    Source Code - (sflstr.c)

    
    {
    
       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;
    
    }
    
    
     

    getequval

    
    #include "sflstr.h"
    
    char *
    
    getequval (
    
        char *strline,
    
        char *strretstr)
    
    

    Synopsis

    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

    Source Code - (sflstr.c)

    
    {
    
       char *stroffset;
    
    
    
       stroffset = strstr (strline, "=");
    
    
    
       if (stroffset)
    
           ltrim ((stroffset + 1));
    
       else
    
           return (char *)NULL;
    
    
    
       return strcpy (strretstr,
    
       (stroffset && *(stroffset + 1))? (stroffset + 1): "");
    
    }
    
    
     

    matchtable

    
    #include "sflstr.h"
    
    int
    
    matchtable (
    
        char *strbuf,
    
        char *strmatch,
    
        char *strsept,
    
        int ncase)
    
    

    Synopsis

    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

    Source Code - (sflstr.c)

    
    {
    
       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;
    
    }
    
    
     

    stringreplace

    
    #include "sflstr.h"
    
    char *
    
    stringreplace (
    
        char *strbuf,
    
        char *strpattern)
    
    

    Synopsis

    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

    Source Code - (sflstr.c)

    
    {
    
       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;
    
    }
    
    
     

    wordwrapstr

    
    #include "sflstr.h"
    
    char *
    
    wordwrapstr (
    
        char *strbuff,
    
        int iwid)
    
    

    Synopsis

    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

    Source Code - (sflstr.c)

    
    {
    
       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;
    
    }
    
    
     

    stricstr

    
    #include "sflstr.h"
    
    char *
    
    stricstr (
    
        const char *str1,
    
        const char *str2)
    
    

    Synopsis

    A case insensitive strstr. Returns a pointer to head of the str1. Submitted by Scott Beasley jscottb@infoave.com

    Source Code - (sflstr.c)

    
    {
    
       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;
    
    }
    
    
     

    strtempcmp

    
    #include "sflstr.h"
    
    int
    
    strtempcmp (
    
        const char *str1,
    
        const char *strPat)
    
    

    Synopsis

    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

    Source Code - (sflstr.c)

    
    {
    
       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;
    
    }
    
    
     

    istoken

    
    #include "sflstr.h"
    
    int
    
    istoken (
    
        char **strLine,
    
        const char *strtoken,
    
        int *iWasToken)
    
    

    Synopsis

    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

    Source Code - (sflstr.c)

    
    {
    
       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;
    
    }
    
    
     

    eatstr

    
    #include "sflstr.h"
    
    char *
    
    eatstr (
    
        char **strBuff,
    
        char *strToEat)
    
    

    Synopsis

    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

    Source Code - (sflstr.c)

    
    {
    
       int iWasToken;
    
    
    
       ltrim (*strBuff);
    
       istoken (strBuff, strToEat, &iWasToken);
    
    
    
       return iWasToken ? *strBuff : (char *)NULL;
    
    }
    
    
     

    eatstrpast

    
    #include "sflstr.h"
    
    char *
    
    eatstrpast (
    
        char **strBuff,
    
        char *strCharsToEatPast)
    
    

    Synopsis

    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

    Source Code - (sflstr.c)

    
    {
    
       ltrim (*strBuff);
    
       while (**strBuff && strchr (strCharsToEatPast, **strBuff))
    
           deletechar (*strBuff, 0);
    
    
    
       return *strBuff;
    
    }
    
    
     

    movestrpast

    
    #include "sflstr.h"
    
    char *
    
    movestrpast (
    
        char **strBuff,
    
        char cCharToEatPast)
    
    

    Synopsis

    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

    Source Code - (sflstr.c)

    
    {
    
       ltrim (*strBuff);
    
       while (**strBuff && **strBuff != cCharToEatPast)
    
           deletechar (*strBuff, 0);
    
    
    
       if (**strBuff && **strBuff == cCharToEatPast)
    
           deletechar (*strBuff, 0);
    
    
    
       return *strBuff;
    
    }
    
    
     

    eatchar

    
    #include "sflstr.h"
    
    char *
    
    eatchar (
    
        char **strBuff,
    
        char cChar)
    
    

    Synopsis

    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

    Source Code - (sflstr.c)

    
    {
    
       ltrim (*strBuff);
    
       if (**strBuff && **strBuff == cChar)
    
           deletechar (*strBuff, 0);
    
    
    
       return *strBuff;
    
    }
    
    
     

    isoneoftokens

    
    #include "sflstr.h"
    
    int
    
    isoneoftokens (
    
        char **strbuf,
    
        char *strmat,
    
        char *strsep,
    
        int *iWasToken)
    
    

    Synopsis

    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

    Source Code - (sflstr.c)

    
    {
    
       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;
    
    }
    
    

    TCP/IP, UDP/IP socket functions

    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

    Synopsis

    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.

    List of Functions

    List of Symbol Definitions

    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)

    List of Type Definitions

    Type name: Defined as:
    SOCKET int
    sock_t qbyte
     

    sock_init

    
    #include "sflsock.h"
    
    int
    
    sock_init (void)
    
    

    Synopsis

    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.

    Source Code - (sflsock.c)

    
    {
    
    #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
    
    }
    
    
     

    sock_term

    
    #include "sflsock.h"
    
    int
    
    sock_term (void)
    
    

    Synopsis

    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().

    Source Code - (sflsock.c)

    
    {
    
    #if (defined (__WINDOWS__))
    
        WSACleanup ();
    
    #endif
    
        return (0);
    
    }
    
    
     

    passive_TCP

    
    #include "sflsock.h"
    
    sock_t
    
    passive_TCP (
    
        const char *service,                /*  Service name or port as string   */
    
        int queue_length                    /*  Queue length for listen()        */
    
    )
    
    

    Synopsis

    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

    Source Code - (sflsock.c)

    
    {
    
        ASSERT (service && *service);
    
        ASSERT (queue_length > 0);
    
        return (passive socket (service, "tcp", queue_length));
    
    }
    
    
     

    passive_UDP

    
    #include "sflsock.h"
    
    sock_t
    
    passive_UDP (
    
        const char *service                 /*  Service name or port as string   */
    
    )
    
    

    Synopsis

    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

    Source Code - (sflsock.c)

    
    {
    
        ASSERT (service && *service);
    
        return (passive socket (service, "udp", 0));
    
    }
    
    
     

    passive_socket

    
    #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     */
    
    )
    
    

    Synopsis

    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
    By default, opens a socket on all available IP addresses. You can open the socket on a specific address, by setting the global variable ip_passive to the address (in network order). This variable is reset to INADDR_ANY after each call to passive_socket or one of the functions that calls it.

    Source Code - (sflsock.c)

    
    {
    
    #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
    
    }
    
    
     

    create_socket

    
    #include "sflsock.h"
    
    sock_t
    
    create_socket (
    
        const char *protocol                /*  Protocol "tcp" or "udp"          */
    
    )
    
    

    Synopsis

    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

    Source Code - (sflsock.c)

    
    {
    
    #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
    
    }
    
    
     

    connect_TCP

    
    #include "sflsock.h"
    
    sock_t
    
    connect_TCP (
    
        const char *host,                   /*  Host name                        */
    
        const char *service                 /*  Service name                     */
    
    )
    
    

    Synopsis

    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
    The host name may be a full name, NULL or "" meaning the current host, or a dotted-decimal number. The service may be a defined service, e.g. "echo", or a port number, specified as an ASCII string. See connect socket() for details. Adds the current value ip_portbase to the port number.

    Examples

    
        sock_t handle;
    
        handle = connect_TCP ("", "8080");
    
        handle = connect_TCP (NULL, "echo");
    
        handle = connect_TCP ("www.imatix.com", "http");
    
    

    Source Code - (sflsock.c)

    
    {
    
        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            */
    
    }
    
    
     

    connect_UDP

    
    #include "sflsock.h"
    
    sock_t
    
    connect_UDP (
    
        const char *host,                   /*  Host name                        */
    
        const char *service                 /*  Service name                     */
    
    )
    
    

    Synopsis

    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
    The host name may be a full name, NULL or "" meaning the current host, or a dotted-decimal number. The service may be a defined service, e.g. "echo", or a port number, specified as an ASCII string. See connect socket() for details. Adds the current value ip_portbase to the port number.

    Examples

    
        sock_t handle;
    
        handle = connect_UDP ("", "7");
    
        handle = connect_UDP (NULL, "echo");
    
        handle = connect_UDP ("imatix.com", "echo");
    
    

    Source Code - (sflsock.c)

    
    {
    
        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            */
    
    }
    
    
     

    connect_TCP_fast

    
    #include "sflsock.h"
    
    sock_t
    
    connect_TCP_fast (
    
        const struct sockaddr_in *sin       /*  Socket address structure         */
    
    )
    
    

    Synopsis

    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
    This function is faster, if you know the host system address and port, than connect TCP() because no translation is needed. You can get the host/address structure by calling address end point() or get peer addr(). See connect socket() for details.

    Source Code - (sflsock.c)

    
    {
    
        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              */
    
    }
    
    
     

    connect_UDP_fast

    
    #include "sflsock.h"
    
    sock_t
    
    connect_UDP_fast (
    
        const struct sockaddr_in *sin       /*  Socket address structure         */
    
    )
    
    

    Synopsis

    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
    This function is faster, if you know the host system address and port, than connect UDP() because no translation is needed. You can get the host/address structure by calling address end point() or get peer addr(). See connect socket() for details.

    Source Code - (sflsock.c)

    
    {
    
        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              */
    
    }
    
    
     

    connect_socket

    
    #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            */
    
    )
    
    

    Synopsis

    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
    Always blocks until the connection has been made; i.e. when this function returns you can start to read and write on the socket. Adds the current value ip_portbase to the port number. The host name may be a full name, NULL or "" meaning the current host, or a dotted-decimal number. The service may be a defined service, e.g. "echo", or a port number, specified as an ASCII string. Alternatively, both these values may be NULL or "", in which case the function uses the host_addr argument to supply an address. If you want to build the host_addr structure yourself, use build sockaddr().

    Examples

    
        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);
    
    

    Source Code - (sflsock.c)

    
    {
    
    #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
    
    }
    
    
     

    connect_to_peer

    
    #include "sflsock.h"
    
    int
    
    connect_to_peer (
    
        sock_t handle,                      /*  Socket to connect                */
    
        const struct sockaddr_in *sin       /*  Socket address structure         */
    
    )
    
    

    Synopsis

    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().

    Source Code - (sflsock.c)

    
    {
    
    #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
    
    }
    
    
     

    address_end_point

    
    #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      */
    
    )
    
    

    Synopsis

    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
    Adds the current value ip_portbase to the port number.

    Source Code - (sflsock.c)

    
    {
    
    #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
    
    }
    
    
     

    build_sockaddr

    
    #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               */
    
    )
    
    

    Synopsis

    Builds a socket address structure from the specified host and port addresses. Does not return any value except the built structure.

    Source Code - (sflsock.c)

    
    {
    
        ASSERT (sin);
    
    
    
        sin-> sin_family      = AF_INET;
    
        sin-> sin_addr.s_addr = htonl (host);
    
        sin-> sin_port        = htons (port);
    
    }
    
    
     

    socket_localaddr

    
    #include "sflsock.h"
    
    char *
    
    socket_localaddr (
    
        sock_t handle)
    
    

    Synopsis

    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".

    Source Code - (sflsock.c)

    
    {
    
    #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
    
    }
    
    
     

    socket_peeraddr

    
    #include "sflsock.h"
    
    char *
    
    socket_peeraddr (
    
        sock_t handle)
    
    

    Synopsis

    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".

    Source Code - (sflsock.c)

    
    {
    
    #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
    
    }
    
    
     

    socket_nodelay

    
    #include "sflsock.h"
    
    int
    
    socket_nodelay (
    
        sock_t handle)
    
    

    Synopsis

    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.

    Source Code - (sflsock.c)

    
    {
    
    #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
    
    }
    
    
     

    socket_is_alive

    
    #include "sflsock.h"
    
    Bool
    
    socket_is_alive (
    
        sock_t handle)
    
    

    Synopsis

    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.

    Source Code - (sflsock.c)

    
    {
    
    #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
    
    }
    
    
     

    socket_error

    
    #include "sflsock.h"
    
    int
    
    socket_error (
    
        sock_t handle)
    
    

    Synopsis

    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.

    Source Code - (sflsock.c)

    
    {
    
    #if (defined (DOES_SOCKETS))
    
        return (getsockopt ((SOCKET) handle, SOL_SOCKET, SO_ERROR, 0, 0));
    
    #else
    
        return (0);
    
    #endif
    
    }
    
    
     

    accept_socket

    
    #include "sflsock.h"
    
    sock_t
    
    accept_socket (
    
        sock_t master_socket)
    
    

    Synopsis

    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).

    Source Code - (sflsock.c)

    
    {
    
    #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
    
    }
    
    
     

    connect_error

    
    #include "sflsock.h"
    
    int
    
    connect_error (void)
    
    

    Synopsis

    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.

    Source Code - (sflsock.c)

    
    {
    
        return (connect_error_value);
    
    }
    
    
     

    get_sock_addr

    
    #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         */
    
    )
    
    

    Synopsis

    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.

    Source Code - (sflsock.c)

    
    {
    
    #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
    
    }
    
    
     

    get_peer_addr

    
    #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         */
    
    )
    
    

    Synopsis

    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.

    Source Code - (sflsock.c)

    
    {
    
    #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
    
    }
    
    
     

    read_TCP

    
    #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   */
    
    )
    
    

    Synopsis

    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.

    Source Code - (sflsock.c)

    
    {
    
    #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
    
    }
    
    
     

    write_TCP

    
    #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          */
    
    )
    
    

    Synopsis

    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.

    Source Code - (sflsock.c)

    
    {
    
    #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
    
    }
    
    
     

    read_UDP

    
    #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       */
    
    )
    
    

    Synopsis

    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.

    Source Code - (sflsock.c)

    
    {
    
    #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
    
    }
    
    
     

    write_UDP

    
    #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      */
    
    )
    
    

    Synopsis

    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.

    Source Code - (sflsock.c)

    
    {
    
    #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
    
    }
    
    
     

    close_socket

    
    #include "sflsock.h"
    
    int
    
    close_socket (
    
        sock_t handle                       /*  Socket handle                    */
    
    )
    
    

    Synopsis

    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.

    Source Code - (sflsock.c)

    
    {
    
    #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
    
    }
    
    
     

    sock_select

    
    #include "sflsock.h"
    
    int
    
    sock_select (int nfds, fd_set *readfds, fd_set *writefds,
    
                 fd_set *errorfds, struct timeval *timeout)
    
    

    Synopsis

    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.

    Source Code - (sflsock.c)

    
    {
    
    #if (defined (DOES_SOCKETS))
    
        ASSERT (timeout);
    
        return (select (nfds,
    
                        FD_SETTYPE readfds,
    
                        FD_SETTYPE writefds,
    
                        FD_SETTYPE errorfds,
    
                        timeout));
    
    #else
    
        return (0);
    
    #endif
    
    }
    
    
     

    get_hostname

    
    #include "sflsock.h"
    
    char *
    
    get_hostname (void)
    
    

    Synopsis

    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".

    Source Code - (sflsock.c)

    
    {
    
    #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
    
    }
    
    
     

    get_hostaddr

    
    #include "sflsock.h"
    
    qbyte
    
    get_hostaddr (void)
    
    

    Synopsis

    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.

    Source Code - (sflsock.c)

    
    {
    
    #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
    
    }
    
    
     

    get_hostaddrs

    
    #include "sflsock.h"
    
    qbyte *
    
    get_hostaddrs (void)
    
    

    Synopsis

    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.

    Source Code - (sflsock.c)

    
    {
    
    #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
    
    }
    
    
     

    sock_ntoa

    
    #include "sflsock.h"
    
    char *
    
    sock_ntoa (qbyte address)
    
    

    Synopsis

    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.

    Source Code - (sflsock.c)

    
    {
    
        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);
    
    }
    
    
     

    sockmsg

    
    #include "sflsock.h"
    
    const char *
    
    sockmsg (void)
    
    

    Synopsis

    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.

    Source Code - (sflsock.c)

    
    {
    
    #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
    
    }
    
    
     

    winsock_last_error

    
    #include "sflsock.h"
    
    int
    
    winsock_last_error (void)
    
    

    Synopsis

    Convert a winsock error into a errno value.

    Source Code - (sflsock.c)

    
    {
    
        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);
    
    }
    
    
     

    socket_is_permitted

    
    #include "sflsock.h"
    
    Bool
    
    socket_is_permitted (const char *address, const char *mask)
    
    

    Synopsis

    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().

    Source Code - (sflsock.c)

    
    {
    
        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);
    
    }
    
    
     

    get_host_file

    
    #include "sflsock.h"
    
    char *
    
    get_host_file (void)
    
    

    Synopsis

    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.

    Source Code - (sflsock.c)

    
    {
    
    #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
    
    }
    
    
     

    get_name_server

    
    #include "sflsock.h"
    
    int
    
    get_name_server (struct sockaddr_in *ns_address, int ns_max)
    
    

    Synopsis

    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.

    Source Code - (sflsock.c)

    
    {
    
        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);
    
    }
    
    

    Symbol-table functions

    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

    Synopsis

    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.

    List of Functions

    List of Symbol Definitions

    sflsymb.h defines these symbols, possibly conditionally:
    Symbol: Defined as:
    SYM_HASH_SIZE 256 /* Assumed by sym_hash () */
    _SFLSYMB_INCLUDED TRUE

    List of Type Definitions

    Type name: Defined as:
    symfunc Bool (*) (SYMBOL *, ...)
    symsort int (*) (const void *symb1, const void *symb2)
     

    sym_create_table

    
    #include "sflsymb.h"
    
    SYMTAB *
    
    sym_create_table (void)
    
    

    Synopsis

    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.

    Source Code - (sflsymb.c)

    
    {
    
        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);
    
    }
    
    
     

    sym_delete_table

    
    #include "sflsymb.h"
    
    void
    
    sym_delete_table (
    
        SYMTAB *table)                      /*  Symbol table to delete           */
    
    

    Synopsis

    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.

    Source Code - (sflsymb.c)

    
    {
    
        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               */
    
    }
    
    
     

    sym_empty_table

    
    #include "sflsymb.h"
    
    void
    
    sym_empty_table (
    
        SYMTAB *table)                      /*  Symbol table to empty            */
    
    

    Synopsis

    Empties the given symbol table, by deleting all symbols. You can then add new symbols. If the table argument is NULL, does nothing.

    Source Code - (sflsymb.c)

    
    {
    
        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;
    
    }
    
    
     

    sym_merge_tables

    
    #include "sflsymb.h"
    
    int
    
    sym_merge_tables (
    
        SYMTAB *table,                      /*  Symbol table to import into      */
    
        const SYMTAB *import)               /*  Symbol table to import from      */
    
    

    Synopsis

    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.

    Source Code - (sflsymb.c)

    
    {
    
        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);
    
    }
    
    
     

    sym_lookup_symbol

    
    #include "sflsymb.h"
    
    SYMBOL *
    
    sym_lookup_symbol (
    
        const SYMTAB *table,                /*  Symbol table to search           */
    
        const char   *name)                 /*  Symbol name to search for        */
    
    

    Synopsis

    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.

    Source Code - (sflsymb.c)

    
    {
    
        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);
    
    }
    
    
     

    sym_create_symbol

    
    #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        */
    
    

    Synopsis

    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().

    Examples

    
        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);
    
          }
    
    

    Source Code - (sflsymb.c)

    
    {
    
        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);
    
    }
    
    
     

    sym_assume_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        */
    
    

    Synopsis

    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.

    Source Code - (sflsymb.c)

    
    {
    
        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);
    
    }
    
    
     

    sym_delete_symbol

    
    #include "sflsymb.h"
    
    SYMBOL *
    
    sym_delete_symbol (
    
        SYMTAB *table,                      /*  Symbol table to search           */
    
        SYMBOL *symbol)                     /*  Symbol to delete                 */
    
    

    Synopsis

    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.

    Source Code - (sflsymb.c)

    
    {
    
        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);
    
    }
    
    
     

    sym_exec_all

    
    #include "sflsymb.h"
    
    int
    
    sym_exec_all (
    
        const SYMTAB *table,                /*  Symbol table to process          */
    
        symfunc the_function, ...           /*  Function to call                 */
    
    )
    
    

    Synopsis

    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.

    Examples

    
        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);
    
    

    Source Code - (sflsymb.c)

    
    {
    
        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);
    
    }
    
    
     

    sym_hash

    
    #include "sflsymb.h"
    
    int
    
    sym_hash (
    
        const char *name)
    
    

    Synopsis

    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.

    Source Code - (sflsymb.c)

    
    {
    
        int
    
            hash;                           /*  Computed hash value              */
    
    
    
        for (hash = 0; *name; name++)
    
            hash += *name;
    
    
    
        return (hash & (SYM_HASH_SIZE - 1));
    
    }
    
    
     

    sym_get_value

    
    #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   */
    
    

    Synopsis

    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.

    Examples

    
        value = sym_get_value (env, "PATH", NULL);
    
        if (!value)
    
            puts ("PATH not defined");
    
    

    Source Code - (sflsymb.c)

    
    {
    
        SYMBOL
    
            *symbol;                        /*  Search through symbol table      */
    
    
    
        ASSERT (table);
    
        symbol = sym lookup symbol (table, name);
    
        if (symbol)
    
            return (symbol-> value);
    
        else
    
            return ((char *) default_value);
    
    }
    
    
     

    sym_get_number

    
    #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   */
    
    

    Synopsis

    Returns value for specified symbol, as a long value. If the symbol is not defined in the table, returns a default value.

    Examples

    
        value = sym_get_number (env, "MAX_USERS", 10);
    
    

    Source Code - (sflsymb.c)

    
    {
    
        char
    
            *value;
    
    
    
        value = sym get value (table, name, NULL);
    
        return (value? atol (value): default_value);
    
    }
    
    
     

    sym_get_boolean

    
    #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   */
    
    

    Synopsis

    Returns value for specified symbol, as TRUE or FALSE. If the symbol is not defined in the table, returns a default value.

    Examples

    
        value = sym_get_boolean (env, "CONNECTS_ENABLED", TRUE);
    
    

    Source Code - (sflsymb.c)

    
    {
    
        char
    
            *value;
    
    
    
        value = sym get value (table, name, NULL);
    
        return (value? (conv str bool (value) != 0): default_value);
    
    }
    
    
     

    sym_set_value

    
    #include "sflsymb.h"
    
    void
    
    sym_set_value (
    
        SYMBOL *symbol,                     /*  Symbol to change                 */
    
        const char *value)                  /*  New value to assign              */
    
    

    Synopsis

    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.

    Source Code - (sflsymb.c)

    
    {
    
        ASSERT (symbol);
    
    
    
        mem_strfree (&symbol-> value);      /*  Free existing value if any       */
    
        symbol-> value = mem_strdup (value);
    
    }
    
    
     

    sym_sort_table

    
    #include "sflsymb.h"
    
    void
    
    sym_sort_table (SYMTAB *table, symsort sort_function)
    
    

    Synopsis

    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.

    Examples

    
        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);
    
    

    Source Code - (sflsymb.c)

    
    {
    
        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);
    
    }
    
    
     

    symb2strt

    
    #include "sflsymb.h"
    
    char **
    
    symb2strt (
    
        const SYMTAB *symtab)               /*  Symbol table to export           */
    
    

    Synopsis

    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().

    Source Code - (sflsymb.c)

    
    {
    
        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);
    
    }
    
    
     

    strt2symb

    
    #include "sflsymb.h"
    
    SYMTAB *
    
    strt2symb (
    
        char **table)
    
    

    Synopsis

    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.

    Source Code - (sflsymb.c)

    
    {
    
        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);
    
    }
    
    
     

    symb2descr

    
    #include "sflsymb.h"
    
    DESCR *
    
    symb2descr (
    
        const SYMTAB *symtab)               /*  Symbol table to export           */
    
    

    Synopsis

    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.

    Source Code - (sflsymb.c)

    
    {
    
        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);
    
    }
    
    
     

    descr2symb

    
    #include "sflsymb.h"
    
    SYMTAB *
    
    descr2symb (
    
        const DESCR *descr)
    
    

    Synopsis

    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.

    Source Code - (sflsymb.c)

    
    {
    
        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);
    
    }
    
    

    System-level functions (assertions,...)

    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

    Synopsis

    Provides miscellaneous system-level functions.

    List of Functions

    List of Symbol Definitions

    sflsyst.h defines these symbols, possibly conditionally:
    Symbol: Defined as:
    _SFLSYST_INCLUDED TRUE
     

    sys_assert

    
    #include "sflsyst.h"
    
    void
    
    sys_assert (const char *File, unsigned Line)
    
    

    Synopsis

    Displays an 'assertion failed' message and aborts the program. This function is required by prelude.h if you compile with the DEBUG symbol.

    Source Code - (sflsyst.c)

    
    {
    
    #   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   ();
    
    }
    
    
     

    sys_name

    
    #include "sflsyst.h"
    
    char *
    
    sys_name (void)
    
    

    Synopsis

    Returns a static buffer with the type or name of OS.

    Source Code - (sflsyst.c)

    
    {
    
    #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
    
    }
    
    

    HTTP and CGI Support functions

    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

    Synopsis

    Provides various functions that support HTTP and CGI programming, including escaping/unescaping, and CGI data manipulation.

    List of Functions

    List of Symbol Definitions

    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))
     

    http_escape

    
    #include "sflhttp.h"
    
    char *
    
    http_escape (
    
        const char *string,
    
        char *result)
    
    

    Synopsis

    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.

    Source Code - (sflhttp.c)

    
    {
    
        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);
    
    }
    
    
     

    http_escape_size

    
    #include "sflhttp.h"
    
    size_t
    
    http_escape_size (
    
        const char *string)
    
    

    Synopsis

    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.

    Source Code - (sflhttp.c)

    
    {
    
        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);
    
    }
    
    
     

    http_unescape

    
    #include "sflhttp.h"
    
    char *
    
    http_unescape (
    
        char *string,
    
        char *result)
    
    

    Synopsis

    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.

    Source Code - (sflhttp.c)

    
    {
    
        /*  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);
    
    }
    
    
     

    http_query2strt

    
    #include "sflhttp.h"
    
    char **
    
    http_query2strt (
    
        const char *original_query)
    
    

    Synopsis

    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.

    Source Code - (sflhttp.c)

    
    {
    
        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);
    
    }
    
    
     

    http_query2symb

    
    #include "sflhttp.h"
    
    SYMTAB *
    
    http_query2symb (
    
        const char *query)
    
    

    Synopsis

    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.

    Source Code - (sflhttp.c)

    
    {
    
        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     */
    
    }
    
    
     

    http_query2descr

    
    #include "sflhttp.h"
    
    DESCR *
    
    http_query2descr (
    
        const char *query)
    
    

    Synopsis

    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.

    Source Code - (sflhttp.c)

    
    {
    
        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     */
    
    }
    
    
     

    http_encode_meta

    
    #include "sflhttp.h"
    
    size_t
    
    http_encode_meta (
    
        char       *output,
    
        const char *input,
    
        size_t      outmax)
    
    

    Synopsis

    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.

    Source Code - (sflhttp.c)

    
    {
    
        /*  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));
    
    }
    
    
     

    cgi_parse_query_vars

    
    #include "sflhttp.h"
    
    int
    
    cgi_parse_query_vars (
    
        SYMTAB *symtab,
    
        const char *query,
    
        const char *prefix)
    
    

    Synopsis

    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.

    Source Code - (sflhttp.c)

    
    {
    
        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);
    
    }
    
    
     

    cgi_parse_file_vars

    
    #include "sflhttp.h"
    
    int
    
    cgi_parse_file_vars (
    
        SYMTAB *symtab,
    
        FILE   *file,
    
        const char *prefix,
    
        size_t size)
    
    

    Synopsis

    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.

    Source Code - (sflhttp.c)

    
    {
    
        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);
    
    }
    
    
     

    http_multipart_decode

    
    #include "sflhttp.h"
    
    DESCR *
    
    http_multipart_decode (const char *mime_file, const char *store_path)
    
    

    Synopsis

    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%.

    Source Code - (sflhttp.c)

    
    {
    
        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);
    
    }
    
    
     

    is_full_url

    
    #include "sflhttp.h"
    
    Bool
    
    is_full_url (const char *string)
    
    

    Synopsis

    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 ':'.

    Source Code - (sflhttp.c)

    
    {
    
        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      */
    
    }
    
    
     

    cgi_get_input

    
    #include "sflhttp.h"
    
    char *
    
    cgi_get_input (
    
        int iMethod )
    
    

    Synopsis

    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

    Source Code - (sflhttp.c)

    
    {
    
        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;
    
    }
    
    
     

    cgi_fld_by_name

    
    #include "sflhttp.h"
    
    char *
    
    cgi_fld_by_name (
    
        char *strFld,
    
        char *strIn,
    
        char *strRetBuf )
    
    

    Synopsis

    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

    Source Code - (sflhttp.c)

    
    {
    
        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;
    
    }
    
    
     

    cgi_fld_by_index

    
    #include "sflhttp.h"
    
    char *
    
    cgi_fld_by_index (
    
        int iPos,
    
        char *strIn,
    
        char *strRetBuf,
    
        char *strFldName )
    
    

    Synopsis

    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

    Source Code - (sflhttp.c)

    
    {
    
        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;
    
    }
    
    
     

    cgi_fld_len_by_index

    
    #include "sflhttp.h"
    
    int
    
    cgi_fld_len_by_index (
    
        int iPos,
    
        char *strIn,
    
        int *iDataLen,
    
        int *iNameLen )
    
    

    Synopsis

    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

    Source Code - (sflhttp.c)

    
    {
    
        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;
    
    }
    
    
     

    displayform

    
    #include "sflhttp.h"
    
    int
    
    displayform (
    
        char *strformfile,
    
        char *strvalues )
    
    

    Synopsis

    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

    Source Code - (sflhttp.c)

    
    {
    
        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;
    
    }
    
    

    Environment variable functions

    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

    Synopsis

    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.

    List of Functions

    List of Symbol Definitions

    sflenv.h defines these symbols, possibly conditionally:
    Symbol: Defined as:
    _SFLENV_INCLUDED TRUE
     

    env_get_string

    
    #include "sflenv.h"
    
    char *
    
    env_get_string (
    
        const char *name,
    
        const char *default_value)
    
    

    Synopsis

    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.

    Examples

    
        config_file = strdupl (env_get_string ("config", "default.cfg"));
    
    

    Source Code - (sflenv.c)

    
    {
    
        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);
    
    }
    
    
     

    env_get_number

    
    #include "sflenv.h"
    
    long
    
    env_get_number (
    
        const char *name,
    
        long default_value)
    
    

    Synopsis

    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.

    Examples

    
        max_retries = env_get_number ("retries", 5);
    
    

    Source Code - (sflenv.c)

    
    {
    
        char
    
            *variable_value;
    
    
    
        variable_value = env get string (name, NULL);
    
        return (variable_value? atol (variable_value): default_value);
    
    }
    
    
     

    env_get_boolean

    
    #include "sflenv.h"
    
    Bool
    
    env_get_boolean (
    
        const char *name,
    
        Bool default_value)
    
    

    Synopsis

    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.

    Examples

    
        enforce_security = env_get_number ("security", FALSE);
    
    

    Source Code - (sflenv.c)

    
    {
    
        char
    
            *variable_value;
    
    
    
        variable_value = env get string (name, NULL);
    
        return (variable_value?
    
               (conv str bool (variable_value) != 0): default_value);
    
    }
    
    
     

    env2descr

    
    #include "sflenv.h"
    
    DESCR *
    
    env2descr (void)
    
    

    Synopsis

    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.

    Source Code - (sflenv.c)

    
    {
    
        return (strt2descr (environ));
    
    }
    
    
     

    descr2env

    
    #include "sflenv.h"
    
    char **
    
    descr2env (
    
        const DESCR *descr)
    
    

    Synopsis

    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.

    Source Code - (sflenv.c)

    
    {
    
        return (descr2strt (descr));
    
    }
    
    
     

    env2symb

    
    #include "sflenv.h"
    
    SYMTAB *
    
    env2symb (void)
    
    

    Synopsis

    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.

    Source Code - (sflenv.c)

    
    {
    
        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);
    
    }
    
    
     

    symb2env

    
    #include "sflenv.h"
    
    char **
    
    symb2env (
    
        const SYMTAB *symtab)
    
    

    Synopsis

    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().

    Source Code - (sflenv.c)

    
    {
    
        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);
    
    }
    
    

    String token manipulation functions.

    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

    Synopsis

    Provides functions to break strings into tokens and handle symbol substitution in strings.

    List of Functions

    List of Symbol Definitions

    sfltok.h defines these symbols, possibly conditionally:
    Symbol: Defined as:
    _SFLTOK_INCLUDED TRUE
     

    tok_split

    
    #include "sfltok.h"
    
    char **
    
    tok_split (
    
        const char *string)
    
    

    Synopsis

    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.

    Source Code - (sfltok.c)

    
    {
    
        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);
    
    }
    
    
     

    tok_split_rich

    
    #include "sfltok.h"
    
    char **
    
    tok_split_rich (
    
        const char *string,
    
        const char *delims)
    
    

    Synopsis

    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.

    Source Code - (sfltok.c)

    
    {
    
        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);
    
    
    
    }
    
    
     

    tok_free

    
    #include "sfltok.h"
    
    void
    
    tok_free (
    
        char **token_list)
    
    

    Synopsis

    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.

    Source Code - (sfltok.c)

    
    {
    
        token_list--;                       /*  Back-up to get right address     */
    
        mem_free (token_list [0]);          /*  Free buffer                      */
    
        mem_free (token_list);              /*    and free token list            */
    
    }
    
    
     

    tok_push

    
    #include "sfltok.h"
    
    char **
    
    tok_push (
    
        char **token_list,
    
        const char *string)
    
    

    Synopsis

    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().

    Source Code - (sfltok.c)

    
    {
    
        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);
    
    }
    
    
     

    tok_size

    
    #include "sfltok.h"
    
    int
    
    tok_size (
    
        char **token_list)
    
    

    Synopsis

    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.

    Source Code - (sfltok.c)

    
    {
    
        int
    
            word_nbr;
    
    
    
        for (word_nbr = 0; token_list [word_nbr]; word_nbr++);
    
        return (word_nbr);
    
    }
    
    
     

    tok_text_size

    
    #include "sfltok.h"
    
    size_t
    
    tok_text_size (
    
        char **token_list)
    
    

    Synopsis

    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.

    Source Code - (sfltok.c)

    
    {
    
        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);
    
    }
    
    
     

    tok_subst

    
    #include "sfltok.h"
    
    char *
    
    tok_subst (const char *string, SYMTAB *symbols)
    
    

    Synopsis

    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.

    Source Code - (sfltok.c)

    
    {
    
        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);
    
    }
    
    

    Linked-list functions

    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

    Synopsis

    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.

    List of Functions

    List of Symbol Definitions

    sfltree.h defines these symbols, possibly conditionally:
    Symbol: Defined as:
    TREE_DUPLICATE -1
    TREE_NULL &TREE_EMPTY
    TREE_OK 0
    _SFLTREE_INCLUDED TRUE

    List of Type Definitions

    Type name: Defined as:
    TREE_COLOUR enum {BLACK, RED}
    TREE_COMPARE int () (void *t1, void *t2)
    TREE_PROCESS void () (void *t)
     

    tree_init

    
    #include "sfltree.h"
    
    void tree_init (TREE **root)
    
    

    Synopsis

    Initialises an empty tree.

    Source Code - (sfltree.c)

    
    {
    
        *root = TREE_NULL;
    
    }
    
    
     

    tree_insert

    
    #include "sfltree.h"
    
    int tree_insert (TREE **root, void *tree, TREE_COMPARE *comp,
    
                     Bool allow_duplicates)
    
    

    Synopsis

    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.

    Source Code - (sfltree.c)

    
    {
    
        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);
    
    }
    
    
     

    tree_delete

    
    #include "sfltree.h"
    
    void tree_delete (TREE **root, void *tree)
    
    

    Synopsis

    Deletes a node from a tree. Does not deallocate any memory.

    Source Code - (sfltree.c)

    
    {
    
        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);
    
    }
    
    
     

    tree_find

    
    #include "sfltree.h"
    
    void *tree_find (TREE **root, void *tree, TREE_COMPARE *comp)
    
    

    Synopsis

    Finds a node with data matching that provided.

    Source Code - (sfltree.c)

    
    {
    
        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;
    
    }
    
    
     

    tree_traverse

    
    #include "sfltree.h"
    
    void tree_traverse (void *tree, TREE_PROCESS *process, int method)
    
    

    Synopsis

    Traverse the tree, calling a processing function at each node.

    Source Code - (sfltree.c)

    
    {
    
        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);
    
          }
    
    }
    
    
     

    tree_first

    
    #include "sfltree.h"
    
    void *tree_first (void *tree)
    
    

    Synopsis

    Finds and returns the first node in a (sub-)tree.

    Source Code - (sfltree.c)

    
    {
    
        TREE
    
           *current;
    
    
    
        if ((!tree)
    
        ||  (tree == TREE_NULL))
    
            return NULL;
    
    
    
        current = tree;
    
        while (current-> left != TREE_NULL)
    
            current = current-> left;
    
    
    
        return current;
    
    }
    
    
     

    tree_last

    
    #include "sfltree.h"
    
    void *tree_last (void *tree)
    
    

    Synopsis

    Finds and returns the last node in a (sub-)tree.

    Source Code - (sfltree.c)

    
    {
    
        TREE
    
           *current;
    
    
    
        if ((!tree)
    
        ||  (tree == TREE_NULL))
    
            return NULL;
    
    
    
        current = tree;
    
        while (current-> right != TREE_NULL)
    
            current = current-> right;
    
    
    
        return current;
    
    }
    
    
     

    tree_next

    
    #include "sfltree.h"
    
    void *tree_next (void *tree)
    
    

    Synopsis

    Finds and returns the next node in a tree.

    Source Code - (sfltree.c)

    
    {
    
        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;
    
          }
    
    }
    
    
     

    tree_prev

    
    #include "sfltree.h"
    
    void *tree_prev (void *tree)
    
    

    Synopsis

    Finds and returns the previous node in a tree.

    Source Code - (sfltree.c)

    
    {
    
        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;
    
          }
    
    }
    
    

    Tracing functions

    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

    Synopsis

    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.

    List of Functions

    List of Symbol Definitions

    sfltron.h defines these symbols, possibly conditionally:
    Symbol: Defined as:
    _SFLTRON_INCLUDED TRUE
     

    enable_trace

    
    #include "sfltron.h"
    
    void
    
    enable_trace (void)
    
    

    Synopsis

    Enables tracing. All calls to trace() send a line of text to stdout or the trace file specified with set trace file().

    Source Code - (sfltron.c)

    
    {
    
        trace_state = TRUE;
    
    }
    
    
     

    disable_trace

    
    #include "sfltron.h"
    
    void
    
    disable_trace (void)
    
    

    Synopsis

    Ends tracing. Following a call to this functions, calls to trace() have no effect.

    Source Code - (sfltron.c)

    
    {
    
        trace_state = FALSE;
    
    }
    
    
     

    push_trace

    
    #include "sfltron.h"
    
    void
    
    push_trace (Bool new_state)
    
    

    Synopsis

    Saves the current trace state. Restore with pop trace(). The current implementation only saves one level of tracing.

    Source Code - (sfltron.c)

    
    {
    
        pushed_state = trace_state;
    
        trace_state = new_state;
    
    }
    
    
     

    pop_trace

    
    #include "sfltron.h"
    
    void
    
    pop_trace (void)
    
    

    Synopsis

    Restores a trace state saved by push trace().

    Source Code - (sfltron.c)

    
    {
    
        trace_state = pushed_state;
    
    }
    
    
     

    set_trace_file

    
    #include "sfltron.h"
    
    void
    
    set_trace_file (const char *filename, char mode)
    
    

    Synopsis

    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.

    Source Code - (sfltron.c)

    
    {
    
        if (trace_file)
    
          {
    
            file close (trace_file);
    
            trace_file = NULL;
    
          }
    
        if (filename)
    
          {
    
            ASSERT (mode == 'w' || mode == 'a');
    
            trace_file = file open (filename, mode);
    
          }
    
    }
    
    
     

    trace

    
    #include "sfltron.h"
    
    void
    
    trace (const char *format, ...)
    
    

    Synopsis

    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.

    Source Code - (sfltron.c)

    
    {
    
        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);
    
          }
    
    }
    
    

    Process user id (uid) and group id (gid) functions

    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

    Synopsis

    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.

    List of Functions

    List of Symbol Definitions

    sfluid.h defines these symbols, possibly conditionally:
    Symbol: Defined as:
    _SFLUID_INCLUDED TRUE
     

    get_uid_name

    
    #include "sfluid.h"
    
    char *
    
    get_uid_name (uid_t uid)
    
    

    Synopsis

    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.

    Source Code - (sfluid.c)

    
    {
    
    #   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
    
    }
    
    
     

    get_gid_name

    
    #include "sfluid.h"
    
    char *
    
    get_gid_name (gid_t gid)
    
    

    Synopsis

    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.

    Source Code - (sfluid.c)

    
    {
    
    #   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
    
    }
    
    
     

    set_uid_user

    
    #include "sfluid.h"
    
    int
    
    set_uid_user (void)
    
    

    Synopsis

    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().

    Source Code - (sfluid.c)

    
    {
    
    #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
    
    }
    
    
     

    set_uid_root

    
    #include "sfluid.h"
    
    int
    
    set_uid_root (void)
    
    

    Synopsis

    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().

    Source Code - (sfluid.c)

    
    {
    
    #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
    
    }
    
    
     

    set_gid_user

    
    #include "sfluid.h"
    
    int
    
    set_gid_user (void)
    
    

    Synopsis

    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().

    Source Code - (sfluid.c)

    
    {
    
    #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
    
    }
    
    
     

    set_gid_root

    
    #include "sfluid.h"
    
    int
    
    set_gid_root (void)
    
    

    Synopsis

    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().

    Source Code - (sfluid.c)

    
    {
    
    #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
    
    }
    
    

    XML (Extensible Markup Language) access functions

    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

    Synopsis

    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>

    List of Functions

    List of Symbol Definitions

    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

    List of Type Definitions

    Type name: Defined as:
    XML_ATTR struct _XML_ATTR
    XML_ITEM struct _XML_ITEM
     

    xml_new

    
    #include "sflxml.h"
    
    XML_ITEM *
    
    xml_new (
    
        XML_ITEM   *parent,
    
        const char *name,
    
        const char *value)
    
    

    Synopsis

    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.

    Source Code - (sflxml.c)

    
    {
    
        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);
    
    }
    
    
     

    xml_modify_value

    
    #include "sflxml.h"
    
    void
    
    xml_modify_value  (XML_ITEM *item, const char *value)
    
    

    Synopsis

    Modifies an existing XML item's value.

    Source Code - (sflxml.c)

    
    {
    
        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);
    
          }
    
    }
    
    
     

    xml_item_name

    
    #include "sflxml.h"
    
    char *
    
    xml_item_name (XML_ITEM *item)
    
    

    Synopsis

    Extracts the name of a specified XML item. The returned string should NOT be modified. To manipulate it, first make a copy first.

    Source Code - (sflxml.c)

    
    {
    
        ASSERT (item);
    
    
    
        return item-> name;
    
    }
    
    
     

    xml_item_value

    
    #include "sflxml.h"
    
    char *
    
    xml_item_value (XML_ITEM *item)
    
    

    Synopsis

    Extracts the value of a specified XML item. The returned string should NOT be modified. To manipulate it, first make a copy first.

    Source Code - (sflxml.c)

    
    {
    
        ASSERT (item);
    
    
    
        return item-> value;
    
    }
    
    
     

    xml_free

    
    #include "sflxml.h"
    
    void
    
    xml_free (
    
        XML_ITEM *item)
    
    

    Synopsis

    Frees all memory used by an XML_ITEM item and its children.

    Source Code - (sflxml.c)

    
    {
    
        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         */
    
    }
    
    
     

    xml_first_child

    
    #include "sflxml.h"
    
    XML_ITEM *
    
    xml_first_child (XML_ITEM *item)
    
    

    Synopsis

    Returns the first child node of the specified item, or NULL if there are none.

    Source Code - (sflxml.c)

    
    {
    
        ASSERT (item);
    
    
    
        if (!list_empty (&item-> children))
    
            return item-> children. next;
    
        else
    
            return NULL;
    
    }
    
    
     

    xml_next_sibling

    
    #include "sflxml.h"
    
    XML_ITEM *
    
    xml_next_sibling (XML_ITEM *item)
    
    

    Synopsis

    Returns the next sibling of the specified item, or NULL if there if are none.

    Source Code - (sflxml.c)

    
    {
    
        ASSERT (item);
    
    
    
        if ((LIST *) item-> next != & item-> parent-> children)
    
            return item-> next;
    
        else
    
            return NULL;
    
    }
    
    
     

    xml_parent

    
    #include "sflxml.h"
    
    XML_ITEM *
    
    xml_parent (XML_ITEM *item)
    
    

    Synopsis

    Returns the parent of the specified item, or NULL if this is the root item.

    Source Code - (sflxml.c)

    
    {
    
        ASSERT (item);
    
    
    
        return (item-> parent);
    
    }
    
    
     

    xml_put_attr

    
    #include "sflxml.h"
    
    int
    
    xml_put_attr (
    
        XML_ITEM   *item,
    
        const char *name,
    
        const char *value)
    
    

    Synopsis

    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).

    Source Code - (sflxml.c)

    
    {
    
        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);
    
    }
    
    
     

    xml_attr

    
    #include "sflxml.h"
    
    XML_ATTR *
    
    xml_attr (
    
        XML_ITEM   *item,
    
        const char *name)
    
    

    Synopsis

    Searches for the attribute with the specified name; if found, returns the address of the attribute node, otherwise returns NULL.

    Source Code - (sflxml.c)

    
    {
    
        XML_ATTR
    
            *attr;
    
    
    
        ASSERT (item);
    
        ASSERT (name);
    
    
    
        FORLIST (attr, item-> attrs)
    
            if (streq (attr-> name, name))
    
                return (attr);
    
    
    
        return (NULL);
    
    }
    
    
     

    xml_attr_name

    
    #include "sflxml.h"
    
    char *
    
    xml_attr_name (XML_ATTR *attr)
    
    

    Synopsis

    Extracts the name of a specified XML attr. The returned string should NOT be modified. To manipulate it, first make a copy first.

    Source Code - (sflxml.c)

    
    {
    
        ASSERT (attr);
    
    
    
        return attr-> name;
    
    }
    
    
     

    xml_attr_value

    
    #include "sflxml.h"
    
    char *
    
    xml_attr_value (XML_ATTR *attr)
    
    

    Synopsis

    Extracts the value of a specified XML attr. The returned string should NOT be modified. To manipulate it, first make a copy first.

    Source Code - (sflxml.c)

    
    {
    
        ASSERT (attr);
    
    
    
        return attr-> value;
    
    }
    
    
     

    xml_get_attr

    
    #include "sflxml.h"
    
    char *
    
    xml_get_attr (
    
        XML_ITEM   *item,
    
        const char *name,
    
        const char *deflt)
    
    

    Synopsis

    Returns the value for the specified attribute, if it exists. Otherwise returns the default value.

    Source Code - (sflxml.c)

    
    {
    
        XML_ATTR
    
            *attr;
    
    
    
        ASSERT (item);
    
        ASSERT (name);
    
    
    
        attr = xml attr (item, name);
    
        if (attr)
    
            return (attr-> value);
    
        else
    
            return ((char *) deflt);
    
    }
    
    
     

    xml_free_attr

    
    #include "sflxml.h"
    
    void
    
    xml_free_attr (
    
        XML_ATTR *attr)
    
    

    Synopsis

    Frees all memory used by an XML_ATTR node.

    Source Code - (sflxml.c)

    
    {
    
        ASSERT (attr);
    
    
    
        list unlink (attr);
    
        mem_strfree (&attr-> name);
    
        mem_strfree (&attr-> value);
    
        mem_free (attr);
    
    }
    
    
     

    xml_first_attr

    
    #include "sflxml.h"
    
    XML_ATTR *
    
    xml_first_attr (XML_ITEM *item)
    
    

    Synopsis

    Returns the first attribute of a specified XML item, or NULL if there are none.

    Source Code - (sflxml.c)

    
    {
    
        ASSERT (item);
    
    
    
        if (!list_empty (&item-> attrs))
    
            return item-> attrs. next;
    
        else
    
            return NULL;
    
    }
    
    
     

    xml_next_attr

    
    #include "sflxml.h"
    
    XML_ATTR *
    
    xml_next_attr (XML_ATTR *attr)
    
    

    Synopsis

    Returns the next attribute following the specified attribute, or NULL if there are none.

    Source Code - (sflxml.c)

    
    {
    
        ASSERT (attr);
    
    
    
        if ((LIST *) attr-> next != & attr-> parent-> attrs)
    
            return attr-> next;
    
        else
    
            return NULL;
    
    }
    
    
     

    xml_changed

    
    #include "sflxml.h"
    
    Bool
    
    xml_changed (
    
        XML_ITEM *item)
    
    

    Synopsis

    Returns TRUE if the XML file loaded into the specified list has in the meantime been changed. Returns FALSE if not.

    Source Code - (sflxml.c)

    
    {
    
        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);
    
    }
    
    
     

    xml_refresh

    
    #include "sflxml.h"
    
    Bool
    
    xml_refresh (
    
        XML_ITEM *item)
    
    

    Synopsis

    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.

    Source Code - (sflxml.c)

    
    {
    
        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);
    
    }
    
    

    List of Functions

    List of Symbol Definitions

    sflxmll.h defines these symbols, possibly conditionally:
    Symbol: Defined as:
    _SLFXMLL_INCLUDED TRUE
     

    xml_save

    
    #include "sflxmll.h"
    
    int
    
    xml_save (
    
        XML_ITEM   *item,
    
        const char *filename)
    
    

    Synopsis

    Saves an XML tree to the specified file. Returns the number of items saved, or -1 if there was an error.

    Source Code - (sflxmll.c)

    
    {
    
        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);
    
    }
    
    
     

    xml_load

    
    #include "sflxmll.h"
    
    XML_ITEM
    
    *xml_load (const char *path,
    
               const char *filename)
    
    

    Synopsis

    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"
    Looks for the XML file on the specified path symbol, or in the current directory if the path argument is null. Adds the specified extension to the file name if there is none already included.

    Source Code - (sflxmll.c)

    
    {
    
        feedback = NULL;                    /*  Reset return feedback            */
    
    
    
        ASSERT (filename);
    
        fname = file where ('r', path, filename, "xml");
    
    
    
    #   include "sflxmll.i"                 /*  Include dialog interpreter       */
    
    }
    
    

    | << | < | > | >> iMatix Copyright © 1996-98 iMatix