MUTIL -- a library of C utilities

Copyright (C) 2006-2007 Laszlo Menczel (menczel at maibox dot hu)

This is free software distributed under the GNU Lesser General Public Licence (LGPL) version 2.1.

MUTIL is distributed in the hope that it will be useful, but with NO WARRANTY expressed or implied.

Special licence clause

The copyright holder (Laszlo Menczel) hereby grants you the additional right to statically link the MUTIL library to your application and distribute the resulting binary executable under a licence of your own choice.

The above clause does not free you from the obligation of providing (upon request) to the recipients of your binary executable the full source code of the MUTIL library used for building said executable. In the documentation accompanying your binary executable you must inform the end user that the source code of the library is available upon request.

If you distribute modified versions of the MUTIL library you must also grant end users the additional right of static linking as described above.

Summary

MUTIL is a collection of utility functions (written in C):

keyboard input functions (for console mode)
string to number conversion (better 'atoi' and 'atof')
functions for parsing command line arguments and INI files
easy to use (high-level) file name globbing
string manipulation (tokenization, parsing, etc.)
logging and tracing functions (useful for debugging)
file and filename handling utilities
date/time to string conversion functions
array manipulation utilities
comprehensive and uniform error handling

Content

1. Introduction

2. Error handling

3. General utilities
     mut_exec_prog

4. Keyboard functions
     mut_kbd_init
     mut_kbdclear
     mut_keypress
     mut_getkey
     mut_get_str

5. Array manipulation
     mut_iarray_sum
     mut_darray_sum
     mut_alloc_str_array
     mut_free_str_array
     mut_create_str_ptrs
     mut_delete_str_ptrs

6. Date and time functions
     mut_get_date
     mut_get_time
     mut_get_date_short
     mut_make_time_stamp

7. String manipulation
     mut_strip_ext
     mut_strip_eol
     mut_stripn_eol
     mut_str_end
     mut_strip_quote
     mut_str_last
     mut_str_first
     mut_str_find
     mut_str_split
     mut_str_repl
     mut_str_substr_ptr

8. String/number conversions
     mut_stoi
     mut_stof
     mut_itobs

9. Parsing strings
     mut_skip_delimiter
     mut_skip_space
     mut_get_token_ex
     mut_get_token
     mut_parse_int
     mut_parse_int_ex
     mut_parse_float
     mut_parse_float_ex

10. Simple file system utilities
     mut_fname_char
     mut_file_exist
     mut_file_delete
     mut_file_rename
     mut_fname_split
     mut_fname_get_path
     mut_fname_get_name
     mut_fget_line
     mut_fget_line
     mut_fname_check_len
     mut_fname_check_str

11. File and directory name globbing
     mut_glob_dir
     mut_glob_discard
     mut_glob_format
     mut_finfo_hidden
     mut_finfo_is_dir
     mut_new_glob
     mut_glob_append
     mut_glob_insert
     mut_glob_fill

12. Processing command line arguments
   12.1. Command line syntax
   12.2. Specifying the expected arguments in applications
   12.3. Reading arguments from the command line
     mut_parse_cmd_line

13. Loading configuration (INI) files
   13.1. Format of configuration files
   13.2. Specifying the parameter list in applications
   13.3. Reading the configuration file
     mut_load_config

14. Debugging functions
     mut_brkpt
     mut_brkpts
     mut_set_logname
     mut_set_loglimit
     mut_settrace
     mut_logtrace
     mut_logtrace2
     mut_logputs
     mut_logprintf

15. Error functions
     mut_last_error
     mut_errmsg

16. Building and using the library
   16.1. Building
   16.2. Usage

1. Introduction

This library is a collection of assorted utilities I have developed while working on various software projects. This is general purpose stuff (written in C) which can be quite useful (and is frequently needed) but is missing from standard C libraries. Among other goodies, you will find here improved versions of the 'fgets', 'atoi' and 'atof' functions of Glibc, functions for parsing command line arguments and INI files, and an easy to use (high-level) file globbing function.

The library can be used under Win32 and Linux. Both versions have been developed using the GCC compiler system (MinGW under Win32). I guess that other compilers could be used, but I have no motivation to do anything about this. Feel free to port the library to any compiler of your choice.

2. Error handling

Error handling in the library is performed according to the following general principles:

The following error codes are defined in 'mutil.h':


  NO_ERROR                      operation was successful

  MUTERR_BAD_ARG                bad function argument
  MUTERR_ALLOC                  memory allocation failed
  MUTERR_FILE                   file open error
  MUTERR_FILE_TYPE              file type incorrect
  MUTERR_FILE_READ              error when reading file
  MUTERR_FILE_WRITE             error when writing file
  MUTERR_TOO_MANY               too many items
  MUTERR_TOO_FEW                not enough items
  MUTERR_BAD_TOKEN              bad token
  MUTERR_RANGE                  range error
  MUTERR_EMPTY                  no data (empty)
  MUTERR_TYPE                   type error
  MUTERR_SIZE                   size error
  MUTERR_END_OF_STR             unexpected end of string
  MUTERR_TIMEOUT                operation timeout
  MUTERR_DLL_LOAD               cannot load the DLL specified
  MUTERR_NOT_FOUND              object not found
  MUTERR_NOT_READY              the device is not ready or not initialized
  MUTERR_DIR                    directory not found
  MUTERR_SYSINFO                cannot obtain requested system information
  MUTERR_OVERFLOW               buffer overflow
  MUTERR_PARTIAL_READ           only part of the object was retrieved
  MUTERR_END_OF_FILE            end of file reached
  MUTERR_WIN32PROC              WinAPI error: process creation failed
  MUTERR_WIN32WAIT              WinAPI error: cannot wait on specified object
  MUTERR_WIN32EXITCODE          WinAPI error: cannot get process exit code
  MUTERR_ACCESS_FILE            cannot access file
  MUTERR_ACCESS_DIR             cannot access directory
  MUTERR_SYSCALL                system call failed
  MUTERR_BUSY                   resource is used by other process
  MUTERR_READ_ONLY              file or directory is read-only

  MUTERR_BAD_INT                bad integer (not a number?)
  MUTERR_BAD_FLOAT              bad float (not a number?)
  MUTERR_INT_BASE               invalid base for integer value
  MUTERR_TOO_MANY_DIG           too many digits
  MUTERR_NO_DIG                 no digits
  MUTERR_NOT_DIG                not a digit
  MUTERR_EXTRA_CHAR             extra characters in numeric string
  MUTERR_EXP_RANGE              exponent out of range
  MUTERR_NO_EXP                 no exponent value specified
  MUTERR_BAD_EXP                bad exponent value
  MUTERR_TOO_MANY_DOTS          too many decimal points

  MUTERR_PAR_FILE               cannot open/read config file
  MUTERR_PAR_NAME               parameter name NULL or empty
  MUTERR_PAR_TYPE               invalid parameter type
  MUTERR_PAR_VAR                pointer to parameter target variable is NULL
  MUTERR_PAR_SIZE               size of specified string buffer < 1
  MUTERR_PAR_INDEX              bad parameter index
  MUTERR_PAR_VALUE              no value specified for a parameter
  MUTERR_PAR_CONV               error when converting numeric parameter
  MUTERR_PAR_LOAD               at least one parameter is not loaded

  MUTERR_ARG_PROGNAME           program name is too long to store
  MUTERR_ARG_SIZE               argument string too long
  MUTERR_ARG_NAME               argument not recognized
  MUTERR_ARG_PARAM              paired argument has no value
  MUTERR_ARG_PARSIZE            value string too long
  MUTERR_ARG_BAD_PARAM          invalid characters in a value
  MUTERR_ARG_DUPLIC             duplicate argument
  MUTERR_ARG_MISSING            at least one required argument missing

There are a couple of functions for error handling, see Section 15.. The error messages retrieved by mut_errmsg are defined in 'error.c', they can be replaced by translated versions if necessary.

3. General utilities

int mut_exec_prog(char *prog, int wait, int *retval, char *outfile)
[Win32 specific]

The version of GLIBC supplied with the MinGW system does not fully implement the functions for the execution of a program from another one. The main problem is that there is no way to wait for the child program to finish (no 'wait_pid'). This function provides the missing functionality using the Win32 system calls 'CreateProcess', 'WaitForSingleObject' and 'GetExitCodeProcess'.

Runs the program 'prog' as a separate process. 'prog' must be a complete command line containing the name (path) of the program and all arguments.

The behaviour of the function after starting 'prog' is controlled by the value of 'wait' as follows:

  wait == -1     waits until 'prog' terminates and returns a non-zero value

  wait == 0      starts 'prog' and immediately returns a non-zero value

  wait > 0       waits for 'prog' to terminate, but after 'wait' milliseconds returns zero
                 and sets the error code to MUTERR_TIMEOUT

If 'wait' is set to -1, or in case if wait is non-zero and 'prog' terminates within the specified time period, the integer variable 'retval' will contain the return code of 'prog', otherwise it is set to zero. If '*retval' is NULL, the return code is ignored.

The string 'outfile' may contain a filename to which screen output from 'prog' should be redirected. If 'outfile' is NULL, 'stdout' is used. Please note that 'wait' must be -1 (wait for termination) if the output of 'prog' is redirected to a file.

Note: This function is provided for starting console mode applications. I have not tested it with GUI mode apps, it will probably fail. You have been warned :-)

4. Keyboard functions

Notes:
1. These functions should not be used in GUI-mode programs, only for console mode.
2. Under Linux the program using these functions should use the NCURSES library and at the start of the program the function 'mut_kbd_init' should be called.

void mut_kbd_init(void)
[Linux specific]

Initializes the NCURSES library for the keyboard input modules.

void mut_kbdclear(void)

Clears the keyboard input buffer (i.e. 'mut_keypress' will return zero after this function has been called).

int mut_keypress(void)

Returns non-zero if there is at least one character waiting in the keyboard input buffer, othwerwise returns zero.

int mut_getkey(void)

Extracts and returns the next character waiting in the keyboard buffer. If there is no character to extract, the function will wait for input. Special key codes returned by 'mut_getkey' are defined in 'mutil.h'.

int mut_get_str(char *buf, int max)

Reads characters from the keyboard input buffer and copies them to 'buf', until the RETURN (ENTER) key is pressed. Maximum 'max' characters are copied. The characters are echoed to the screen at the current cursor location (the cursor is advanced). Pressing ESC aborts input and clears 'buf'. The BACKSPACE key may be used for simple editing of the string input.

5. Array manipulation

int mut_iarray_sum(int *arr, int last_index)
double mut_darray_sum(double *arr, int last_index)

Return the sum of the elements of an array (integer or double) up to and including the element 'last_index'. In case of error the functions return zero. Since zero is a legitim sum, you should check the library error code to determine if an error indeed occurred when zero is returned.

char **mut_alloc_str_array(int count, int size)

Creates a string array, i.e. returns an array of pointers to newly allocated string buffers, each 'size' characters long.

int mut_free_str_array(char **array, int count)

Discards a string array previously created by 'mut_alloc_str_array'. 'count' must match the originally specified size of array. There is no check, beware!

int mut_create_str_ptrs(char **array, int count, int size)

'array' must be defined as an array of 'count' string pointers. The function allocates 'count' new strings buffers each 'size' characters long, and stores the pointers in 'array'.

int mut_delete_str_ptrs(char **array, int count)

Discards the string buffers located at the pointers stored in 'array'. 'count' must match the originally specified number of buffers. There is no check, beware!

6. Date and time functions

int mut_get_date(char *buf)

Obtains the local date and time of the system, converts it to a string and stores the result in 'buf' using the format 'month day year hour:min:sec'.

int mut_get_time(char *buf)

Obtains the local time of the system, converts it to a string and stores the result in 'buf' using the format 'hour:min:sec'.

int mut_get_date_short(char *buf)

Obtains the local date and time of the system, converts it to a string and stores the result in 'buf' using the format 'MM/DD/YYYY hour:min:sec'.

int mut_make_time_stamp(char *buf)

Obtains the local date and time of the system, converts it to a string and stores the result in 'buf' using the format 'YYYY-MM-DD-XXhYYm' where XX is the hour and YY is the minute. This is useful for creating a timestamp to be attached to e.g. filenames. 'buf' should be large enough to hold at least 18 characters.

7. String manipulation

int mut_strip_ext(char *s)

Removes the extension (i.e. the section after the last '.' character from 's'. Assumes that 's' is a properly formed filename. If no '.' character is present, the string is not changed. The maximum length of string 's' defaults to 512 characters.

int mut_strip_eol(char *s)
int mut_stripn_eol(char *s, int max)

Replaces in 's' the first newline character with zero, then quits. 'mut_stripn_eol' scans maximum 'max' characters. Useful for stripping the newline from strings read by 'fgets'. Returns the position of newline replaced, or zero if failed.

char *mut_str_end(char *s)
char *mut_strn_end(char *s, int max)

Returns a pointer to the end of string 's' (i.e. to the terminating zero character) or NULL if error. 'mut_strn_end' scans maximum 'max' characters.

int mut_strip_quote(char *s, int len)

Removes the quotes from the beginning and end of string 's'. Assumes that the first character of 's' is a quote ("). Shifts to the left characters until another quote or the end of string is found. Replaces the closing quote with zero. Maximum 'len' characters are scanned ('len' must be at least 2).

char *mut_str_last(char *s, char c, int len)
char *mut_str_first(char *s, char c, int len)

Find the last/first occurence of character 'c' in string 's'. Return a pointer to its position, or NULL if the character is not present in 's'. Maximum 'len' characters are scanned.

char *mut_str_find(char *s, char c, int len, int dir)

Finds the first or last occurence of character 'c' in string 's' depending on the value of 'dir' (MUT_FIND_FIRST or MUT_FIND_LAST, defined in 'mutil.h'). Returns a pointer to its position, or NULL if the character is not present in 's'. Maximum 'len' characters are scanned.

Note: this is just a wrapper for 'mut_str_first' and 'mut_str_last'.

int mut_str_repl(char *s, char old, char new)

Replaces all occurences of the character 'old' in string 's' by 'new'. 'old' may be any character except zero. 'new' may be any character including zero.

char **mut_str_split(char *s, char *delim, int *res)

Splits 's' into substrings using the characters in 'delim' as delimiter. 'delim' may be NULL in which case ' ', '\t' and '\n' are used as delimiters. If two or more delimiter characters occur in a sequence, they act as a single delimiter.

Returns an array of pointers to newly allocated buffers containing the substrings. The integer 'res' is set to the number of substrings found. If an error occurs, 'res' is set to an error code and the function returns NULL.

Don't forget to call 'mut_free_string_array()' to discard the returned array if not needed any more.

Note: The number of substrings is limited to MUT_MAX_SUB_STRINGS (defined in 'mutil.h'). If the number of substrings exceeds this value, 'res' is set to MUTERR_TOO_MANY, but the return value is a valid pointer to an array which contains pointers to the first MUT_MAX_SUB_STRING substrings found.

char **mut_str_substr_ptr(char *buf, char delim, char end)

Scans the string 'buf' and returns an array of string pointers for each substring in 'buf' which is delimited by the character 'delim'. Returns NULL in case of error and sets the library error code as follows:


  MUTERR_BAD_ARG ---> 'buf' is NULL
  MUTERR_ALLOC -----> cannot allocate the pointer array
  MUTERR_EMPTY -----> no delimiter 'delim' found in 'buf'

8. String/number conversion

int mut_stoi(char *s, int base, int *dest)

Converts the string 's' to an integer value using number base 'base' (MUT_BASE_BIN, MUT_BASE_DEC or MUT_BASE_HEX, these are defined in 'mutil.h'). The result is stored at address 'dest'. This is a replacement for the practically useless 'atoi' function supplied in the standard C library. This function will properly report any error which may occur during conversion.

int mut_stof(char *s, double *dest)

Converts string 's' to a floating point number and stores the result at address 'dest'. Accepts different formats including scientific notation (exponent specified). This is a replacement for the rather primitive 'atof' function supplied in the standard C library. This function will properly report any error which may occur during conversion.

int mut_itobs(void *data, int len, char *buf)

This function converts the data value (8, 16 or 32 bit) located at address 'data' to a binary string. The length of data is specified in argument 'len'. The string is stored in 'buf'.

9. Parsing strings

char *mut_skip_delimiter(char *s, char *templ, int len)

Scans string 's' to find the first character which is not part of the string 'templ'. Returns a pointer to this character in the string. Returns NULL if the line is empty (no significant characters) or 'len' characters have been scanned w/o finding a significant one.

char *mut_skip_space(char *s, int len)

Scans string 's' to find the first character which is not a whitespace (i.e. 'space' or 'tab'). Returns a pointer to this character in the string. Returns NULL if the line is empty (no significant characters) or 'len' characters have been scanned w/o finding a significant one.

char *mut_get_token(char *dest, char *buf, char *delim, int maxlen)
char *mut_get_token_ex(char *dest, char *buf, char *end, char *delim, int maxlen)

Scans the string 'buf' and extracts a 'token', i.e. a substring which is separated from other parts of the string by characters in the string 'delim'. The token is copied to 'dest'. At most 'maxlen' characters are scanned. Returns a pointer to the first character beyond the end of token, or NULL if error. 'mut_get_token_ex' processes only that part of 'buf' which is between its start and 'end'.

'delim' may be NULL in which case it defaults to 'space' and 'tab'.

When a newline '\n' or a zero character is found, it is assumed that the end of string 'buf' has been reached. These characters are also interpreted as delimiters.

char *mut_parse_int(char *buf, char *delim, int base, int *dest)
char *mut_parse_int_ex(char *buf, char *end, char *delim, int base, int *dest)

Scan the string 'buf' and extracts a 'token', i.e. a substring which is separated from other parts of the string by characters in the string 'delim'. Try to convert the extracted token to an integer value using the number base 'base'. If successful, the value is copied to the integer variable 'dest'. Return a pointer to the first character beyond the end of token, or NULL if error. 'mut_parse_int_ex' processes only that part of 'buf' which is between its start and 'end'.

'delim' may be NULL in which case it defaults to 'space' and 'tab'.

When a newline '\n' or a zero character is found, it is assumed that the end of string 'buf' has been reached. These characters are also interpreted as delimiters.

char *mut_parse_float(char *buf, char *delim, double *dest)
char *mut_parse_float_ex(char *buf, char *end, char *delim, double *dest)

Scan the string 'buf' and extracts a 'token', i.e. a substring which is separated from other parts of the string by characters in the string 'delim'. Try to convert the extracted token to a double value. If successful, the value is copied to the double variable 'dest'. Return a pointer to the first character beyond the end of token, or NULL if error. 'mut_parse_float_ex' processes only that part of 'buf' which is between its start and 'end'.

'delim' may be NULL in which case it defaults to 'space' and 'tab'.

When a newline '\n' or a zero character is found, it is assumed that the end of string 'buf' has been reached. These characters are also interpreted as delimiters.

10. Simple file system uyilities

int mut_fname_char(char c)

Returns non-zero if character 'c' is legal in a filename, otherwise returns zero. This is a partial implementation of the Win32 filename rules, it allows the following special characters if compiled for Win32:


'a-z' 'A-Z' '0-9' . _ ! @ # $ % ^ & * ( ) [ ] { } = + - < > SPACE

Under Linux all characters except '/' are allowed.

Note: The tests assume ASCII character encoding.

int mut_file_exist(char *name)

Returns non-zero if the file 'name' exists, zero if not or in case of error (NULL pointer argument, empty file name, etc.).

Note: The function simply tries to open the file and returns non-zero if the operation succeded. Under Linux, file access permisssions may interfere (i.e. any file to which you have no access is reported as not found).

int mut_file_delete(char *name)
int mut_file_rename(char *old, char *new)

These are just wrappers for the 'unlink' and 'rename' functions in the C library. They provide error codes consistent with the error reporting system of MUTIL.


  MUTERR_ACCESS_FILE --> acces denied
  MUTERR_BUSY ---------> file is used by another process
  MUTERR_READ_ONLY ----> file is read-only
  MUTERR_UNDEF --------> cause of error is unknown

int mut_fname_split(char *s, char *path, char *name)

Splits the string 's' to a path specification and a filename. These parts are copied to 'path' and 'name', respectively. 'path' and 'name' may be NULL if the corresponding part is not required. These buffers must be capable of holding MUT_MAX_PATH_LEN and MUT_MAX_NAME_LEN characters, respectively. If 's' does not contain a path or a filename, the corresponding buffer will contain the empty string "". Returns non-zero if OK, zero if the path or name part exceeds the limit imposed by the currently used OS.

Note: The '\', '/' or ':' character terminating the path part is *not* removed.

int mut_fname_get_path(char *s, char *path)
int mut_fname_get_name(char *s, char *name)

These are wrappers for 'mut_fname_split' to obtain the filename or path part of 's' alone ('mut_fname_split(s, path, NULL)' and 'mut_fname_split(s, NULL, name)').

int mut_fget_line(char *buf, int len, FILE *f)

This is an improved version of 'fgets' providing proper error checking and reporting. In case of error plain 'fgets' just returns NULL and you have three guesses what the problem may be :-)

'mut_fget_line' reads the next line of file 'f' (using 'fgetc') into the buffer 'buf'. The return value may be one of the following:

positive integer
The line was sucessfully copied, the return value is the number of characters in the buffer w/o the terminating zero.

zero
If the library error code is MUTERR_BAD_ARG, one of the arguments was invalid (NULL pointer or out of range).

If the library error code is MUTERR_FILE_READ, an I/O error occured while reading and 'buf' is empty.

If the library error code is MUTERR_EMPTY, the line was empty (only a single newline character present) and 'buf' is empty as well.

If the library error code is MUTERR_END_OF_FILE, the end of file 'f' was reached. 'buf' may contain some characters (if the last line has not been properly terminated by a newline).

-1
The line was too long to fit into the buffer. The library error code is set to MUTERR_PARTIAL_READ, and 'buf' contains 'len' characters (including the terminating zero). The next call will continue reading at the current position in the line.

Notes:
1. 'f' should be opened in text mode.
2. 'buf' will always be terminated by a zero character.

int mut_fname_fix_delim(char *path)

Replaces '/' and '\' delimiters so that 'path' conforms to the pathname conventions of of the currently used operating system. Returns zero if no replacement was done or an error occured (check the error code pof the library), non-zero if delimiters were replaced. If 'path' is longer than MUT_MAX_PATH_LEN, it is truncated and the error code is set to MUTERR_TOO_MANY.

int mut_fname_check_len(char *s)

Checks the string 's' which is assumed to be a path to a file or directory, or a single filename. Returns non-zero if the path and name parts do not exceed the limit for the currently used operating system, otherwise returns zero.

int mut_fname_check_str(char *s)

Checks if the string 's' contains only path delimiters and legal filename characters (OS specific). Returns non-zero if yes, zero otherwise.

11. File and directory name globbing

The file globbing module of the GNU Libc is rather primitive (or should I be more respectful and say that only low-level functionality is provided?). Anyway, if you want to obtain a decent list of files and/or subdirectories (with recursive processing of subdirectories, and preferably with all the relevant info attached), you have a lot of coding to do. The functions in this module of MUTIL will save you the coding work.

You invoke the function 'mut_glob_dir' with the appropriate arguments (see below). Information obtained by globbing is returned in a linked list of the following structures:

#define MUT_MAX_PAH_LEN   256

typedef struct flist_s flist_t;

struct flist_s
{
  int     flag;                   // see below
  int     count;                  // total number of items in 'files'
  int     filenum;                // number of file items
  int     dirnum;                 // number of directory items
  int     dirlen;                 // length of directory name
  char    dir[MUT_MAX_PATH_LEN];  // name of base directory
  finfo_t *files;                 // 'finfo_t' array (see below)
  flist_t *next;                  // next structure in the list
};

The following bitmasks may be used to test 'flag':

  MUT_FLIST_HAS_DIR --> the list 'files' has directory entries
  MUT_FLIST_EMPTY ----> no items (files or directories) found
  MUT_FLIST_NO_MATCH -> no files matching the search pattern

Information on individual items (in the array 'files') is stored in the following structure:

#define MUT_MAX_NAME_LEN   256

typedef struct
{
  int  flag;                     // see below
  int  len;                      // length of item name
  char name[MUT_MAX_NAME_LEN];   // item (file or directory) name
} finfo_t;

The following bitmasks may be used to test 'flag':


  MUT_FINFO_IS_DIR --> item is a directory
  MUT_FINFO_HIDDEN --> item is hidden

In the info array files are listed first, then the subdirectories. Both sublists are sorted alphabetically. The two special dirctories '.' and '..' are not stored.

If simple (non-recursive) globbing is requested (see 'mut_glob_dir()' below), the list returned is actually a single structure. Otherwise the order of items in the linked list follows the order of expansion of subdirectories. Consider the following directory tree:


  current directory
  |
   -- dir_a
  |   |
  |   |-- file_a1
  |   |
  |    -- file_a2
  |
  |-- dir_b
  |   |
  |   |-- dir_ba
  |   |   |
  |   |   |-- file_ba1
  |   |   |
  |   |    -- file_ba2
  |   |    
  |   |-- dir_bb
  |   |   |
  |   |   |-- file_bb1
  |   |   |
  |   |    -- file_bb2
  |   |
  |   |-- file_b1
  |   |
  |    -- file_b2
  |
  |-- file_1
  |
   -- file_2

If you perform recursive globbing, then follow the resulting linked list and display the contents of the structures you get:


  file_1        structure 1
  file_2
  dir_a\
  dir_b\

  file_a1       structure 2
  file_a2

  file_b1       structure 3
  file_b2
  dir_ba\
  dir_bb\

  file_ba1      structure 4
  file_ba2

  file_bb1      structure 5
  file_bb2

flist_t *mut_glob_dir(char *dir, char *pattern, int mode)

This function performs file & directory name globbing in the specified directory. The following arguments are accepted:

Returns a pointer to a new linked list of flist_t structures if the operation was successful. In case of a simple (non-recursive) search the list contains a single member only.

A valid pointer return means that the globbing operation was completed w/o error. It *does not* automatically mean that there is useful data in any or all of the structures in the list. You should check the 'flag' fields of the structures for non-fatal errors like the directory being empty or containing no files matching the search pattern (see the description of the flist_t type for useful bitmasks).

In case of error the function returns NULL. The error code of the library is set to one of the following values:


  MUTERR_BAD_ARG -----> 'mode' is unknown
  MUTERR_ALLOC -------> memory allocation error
  MUTERR_SYSCALL -----> a system call (getcwd, chdir, etc.) failed
  MUTERR_ACCESS_DIR --> access denied when opening a (sub)directory
  MUTERR_OVERFLOW ----> a pathname is longer than MUT_MAX_PATH_LEN

The error code can be obtained by mut_last_error.

int mut_glob_discard(flist_t **list)

Discards the linked list of flist_t structures starting with the item to which '*list' points. If a NULL pointer is passed, error (zero) is returned. If '*list' is NULL, nothing is done. Sets '*list' to NULL after the structures have been destroyed.

char *mut_glob_format(flist_t *list, char delim, int mode)

Constructs a list of all items in the structures of the linked list 'list'. Returns a newly allocated buffer which contains a sequence of strings, one for each item (file or directory). The strings contain the names of the items.

The strings are separated by the character 'delim' (pass zero for normal string termination). The buffer is always terminated by a zero, regardless of the value of 'delim'.

'mode' controls the content of buffer as follows:


  MUT_FLIST_ALL  ---> both files and directories
  MUT_FLIST_FILES --> files only
  MUT_FLIST_DIRS ---> directories only

The flag 'MUT_FLIST_PATH' may be ORed to 'mode' to get a list in which filenames are prepended by the appropriate directory name.

Passing zero is equivalent to MUT_FLIST_ALL.

int mut_finfo_hidden(finfo_t *f)

This is a convenience function to test if an 'finfo_t' item is hidden or not. Returns 1 if yes, zero if not, a negative error code if 'f' is NULL.

int mut_finfo_is_dir(finfo_t *f)

This is a convenience function to test if an 'finfo_t' item is a directory or not. Returns 1 if yes, zero if not, a negative error code if 'f' is NULL.

Lower level functions

The functions described above are sufficient for most of the normal tasks involved in file globbing. For those of you who like to experiment, the library provides the following low-level functions.

flist_t *mut_new_glob(void)

Returns a pointer to a newly allocated flist_t structure.

int mut_glob_append(flist_t *f, flist_t *target)

Adds the structure 'f' to the end of the linked list starting with 'target'. Cannot add an element to an empty list.

int mut_glob_insert(flist_t *f, flist_t *targetm flist_t *pos)

Inserts 'f' into the linked list right after the structure 'pos'.

int mut_glob_fill(char *dir, char *patt, flist_t *f)

This function obtains information on the items in directory 'dir' and stores it in the structure 'f'. 'dir' may be NULL, in this case the current directory is used.

The wildcard pattern 'patt' is used for filtering file items in the usual way (however, directory items are *not* filtered). Passing NULL or "" means to use '*'.

In the 'finfo_t' array of 'f' files are stored first, followed by directories. Both sublists are sorted alphabetically (ascending order).

Possible error codes:


  MUTERR_BAD_ARG -----> unknown 'mode' value or 'dir' is longer than MUT_MAX_PATH_LEN
  MUTERR_OVERFLOW ----> name of an item is longer than MUT_MAX_NAME_LEN
  MUTERR_SYSCALL -----> a system call (getcwd, chdir, etc.) failed
  MUTERR_ACCESS_DIR --> could not access the directory specified

12. Processing command line arguments

12.1. Command line syntax

In order to use the command line parsing utility of this library, the arguments must be supplied according to the rules described below:

12.2. Specifying the expected arguments in applications

Arguments to search for are specified using the following structure:


  typedef struct arg_list
  {
    int  type;       // see below
    int  req;        // see below
    char *templ;     // argument string to search for
    void *dst;       // pointer to integer flag or string buffer
    int found;       // set to non-zero if this argument was found
  } arglist_t;

'type' may be 'MUT_ARG_SWITCH' or 'MUT_ARG_PAIRED'

'req' may be 'MUT_ARG_REQUIRED' or 'MUT_ARG_OPTIONAL'.

Note: When initializing the defining structure, omit the '-' minus character from the beginning of the string used for the initialization the field 'templ'! E.g. for the definition of the switch '-x' use 'x' only.

The list of allowed and/or required arguments is specified using an array of such structures. Please note that there must be an excess terminating element at the end of the array, and its type must be set to MUT_ARG_END (types are defined in 'mutil.h').

12.3. Reading arguments from the command line

int mut_parse_cmd_line(int argc, char *argv[], arglist_t *arglist, char *progname)

Parses the array of command line arguments 'argv' which contains 'argc' entries (including the name of program at index 0). 'argc' and 'argv' must be the arguments passed by the OS to the executing program.

First the name of program in argv[0] is extracted and stored in 'progname' (but only if 'progname' is not NULL).

The argument string vector is then searched for the template strings specified in the array 'arglist'. If a template is found, the following actions are carried out:

The function returns non-zero if the arguments were successfully parsed, zero in case of error. An appropriate error message can be retrieved by calling the function mut_errmsg.

13. Loading configuration (INI) files

This function can be used for the initialization of variables or arrays of variables (integer, floating point number or string) with values loaded from a configuration file.

13.1. Format of configuration files

Configuration files must be ASCII text files. Each line must contain either a comment starting with the character '#' in position zero, or a value specification in one of the following formats:


  name    value
  name[x] value

where 'name' is the description of the parameter, '[x]' is an optional index ('x' must be a non-negative integer) and 'value' is the parameter's desired value. These two fields must be separated by at least one space character. Additional space characters are allowed within the lines in any position (but see the note on string parameters below). For example:


  DataDirectory  c:/foobar/data
  ProgName       Widget Factory vers. 1.0
  ErrorLimit     1.0e-005
  MaxUser        16
  Day[0]         Monday
  Day[1]         Friday

Note: In the case of string parameters, all characters up to the end of line (including any spaces) are copied to the destination variable.

13.2. Specifying the parameter list in applications

Parameters to be loaded must be specified in an array of 'config_t' structures:


  typedef struct config_info
  {
    int type;       // see below
    char *name;     // name to search for in the INI file
    void *var;      // pointer to the variable to initialize
    int len;        // maximum length if string variable
    int error;      // set to non-zero if an error occurs
    int loaded;     // see below
  } config_t;

The array must be terminated with a 'config_t' structure in which 'type' is set to 'MUT_INI_END'.

'type' indicates what kind of variable is to be initialized as follows:


  MUT_INI_INT  MUT_INI_FLT  MUT_INI_STR ----> integer, double or string variable
  MUT_INI_AINT MUT_INI_AFLT MUT_INI_ASTR ---> integer, double or string array

13.3. Reading the configuration file

int mut_load_config(char *name, config_t *par)

'name' is the name (path) of the configuration file. 'par' is an array of 'config_t' structures (see above). The function returns non-zero if all parameters have been loaded successfully, zero in case of error. In each structure, the field 'loaded' will indicate how many times the corresponding variable has been updated (for arrays it will show how many array elements have been initialized). The field 'error' will be non-zero if any error occured when processing the value specified in the config file.

Please note that any line in the config file which does not have a corresponding 'config_t' structure in the list is silently ignored. If there is more than one line for a parameter, the value of the corresponding variable will be the one specified on the last line found. Cases of multiple initialization can be identified by checking the values of the 'loaded' fields of the structures.

14. Debugging functions

Together with 'printf', the functions in this module constitute the only debugger I ever need :-)

void mut_brkpt(int num)
void mut_brkpts(char *msg)

These functions print their argument and wait for the user to press ENTER.

void mut_set_logname(char *name)

Sets the name of logfile to use to 'name'. If 'name' is NULL, the name of logfile is reset to 'deafult.log'.

void mut_set_loglimit(int count)

Sets the maximum number of logged messages to 'count'. If count is < 0, the limit is set to 1. If it is > MAX_LOG_COUNT (#defined in 'mutil.h'), the limit is set to MAX_LOG_COUNT.

void mut_settrace(int level)

Set tracing level to 'level. This value should be between zero and 2 (inclusive). This value controls whether the functions 'mut_logtrace' and 'mut_logtrace2' write to the logfile or not (0 = no tracing, 1 = mut_logtrace active, 2 = both active).

void mut_logtrace(char *msg)
void mut_logtrace2(char *msg)

These functions write their arguments to the logfile depending on the value set by 'mut_settrace' (see above).

void mut_startlog(char *name)

Starts the logging system, subsequent messages are written to the file 'name'.

void mut_stoplog(void)

Stops logging, log functions (see below) are inactivated.

void mut_logputs(char *msg)

If logging is curently active, appends 'msg' to the previously designated logfile. The file is opened and closed for each transaction, to avoid the corruption of file content in case of a program crash.

void mut_logprintf(char *format, ...)

If logging is curently active, writes the values of variables as indicated in the 'format' string and the following arguments ('printf' style) to the previously designated logfile. The file is opened and closed for each transaction, to avoid the corruption of file content in case of a program crash.

15. Error functions

The functions described below can be used for obtaining the error code and human readable info on the cause of errors.

int mut_last_error(void)

Returns the value of the variable '__mut_errcode', which has the value set by the last called library function.

char *mut_errmsg(int code)

Returns a pointer to a string which describes the error corresponding to error code 'code'. This is a static string, do not attempt to free!

16. Building and using the library

16.1. Building

Appropriate Makefiles and build scripts are present in the directories 'build-mingw' and 'build-linux'. They should work 'out of the box'. In case of the Win32 version you must set an environmental variable to reflect your configuration (see the batch file 'mk.bat').

Precompiled versions of the library are included in the directories 'bin\linux' and 'bin\win32'. After compiling the library you can save a copy of the new version by running the script 'save(.bat)' from the build directory. The library can be installed by running the 'inst(.bat)' script from 'bin\linux' or 'bin\win32'.

16.2. Usage

Most of the functions in the library depend only on Glibc and (under Linux) on 'libm' (math libary). The keyboard module under Linux needs the 'ncurses' library. Programs using the library should include the header file 'mutil.h' and link to the libraries listed above. That's all.

I have tested only the static version of the library. Since it is rather small (the Linux lib is just over 40K), linking the static version does not increase the size of applications by a significant amount. In fact, shared versions (DLL or .so) may not work correctly, since I paid no attention to this aspect when I wrote the code. The library is most probably not thread-safe. I have plans to add code to protect the relevant variables by mutexes. There may be a couple of functions which are not re-entrant.