DECUS C LANGUAGE SYSTEM Utility Library Reference Manual by Jerry Leichter This document describes the RSX/VMS/RSTS/RT11 DECUS C language system Utility Library. DECUS Structured Languages SIG Document compiled Tue May 07 18:52:25 1985 Copyright (C) 1982, DECUS General permission to copy or modify, but not for profit, is hereby granted, provided that the above copyright notice is included and reference made to the fact that reproduction privileges were granted by DECUS. The information in this document is subject to change without notice and should not be construed as a commitment by Digital Equipment Corporation or by DECUS. Neither Digital Equipment Corporation, DECUS, nor the authors assume any responsibility for the use or reliability of this document or the described software. This software is made available without any support whatsoever. The person responsible for an implementation of this system should expect to have to understand and modify the source code if any problems are encountered in implementing or maintaining the compiler or its run-time library. The DECUS 'Structured Languages Special Interest Group' is the primary focus for communication among users of this software. UNIX is a trademark of Bell Telephone Laboratories. RSX, RSTS/E, RT11 and VMS are trademarks of Digital Equipment Corporation. CHAPTER 1 INTRODUCTION The C Utility Library consists of routines to perform many common functions. For example, there is a set of routines that handle VSTRINGs, which are dynamically allocated, variable length strings. Another set of routines handle TABLEs, which are hash tables that provide a powerful associative memory capability. Some routines replace or extend routines in the standard library. The Utility Library also provides an emulation of some obsolete routines, such as iovtoa(), thus providing compatibility with older versions of the standard library. All the routines in the Utility Library are written in C, and every attempt has been made to make them reasonably clear and easy to modify. Thus, you may often be able to use a Utility routine that is close to what you need as a basis for the exact code you need. To use the Utility Library, you must do two things: "include" any required header files, as documented in the routine descriptions; and arrange for the Utility Library to be searched when you link or task-build your program. Generally, the Utility Library header files will be stored in the standard C account - where stdio.h is. All the documentation assumes this. If this is not the case, you will have to modify your "include" statements appropriately. The Utility Library's object modules are stored in CU.OBJ on RT11-based systems, and in CU.OLB on RSX-based systems. One of these - or, sometimes, on RSTS, both - will be stored in the standard C library account - where C.OLB or CLIB.OBJ is stored. Your RT11 linker or Task Builder commands must arrange to search the appropriate account and file. Note that you will usually want to search CU BEFORE C or CLIB, so that you get the C Utility Library versions of routines that exist in both. Searching C or CLIB first is possible, but proper functioning cannot be guaranteed, since some of the Utility Library routines may rely on their ability to call their cohorts in the Library, [[ C Utility Library]] Page 1-2 Reference Manual rather than the standard library routines of the same name. Caveat emptor! [[ C Utility Library]] Page 1-3 Reference Manual 1.1 Point After a Matching String _____ _____ _ ________ ______ ********* * after * ********* NAME: after -- Point After a Matching String SYNOPSIS: char * after(s,p) char s[]; /* Subject string */ char p[]; /* Pattern string */ DESCRIPTION: after(s,p) searches s for the first occurence of p. If p occurs as a substring of s, after() returns a pointer to the next memory location beyond the matching instance. If p is not a substring of p, after() returns NULL. Thus, after("abcde","bcd") returns a pointer to the "e" in the first argument; after("abcde","acd") returns NULL. If strlen(p) == 0, after() returns s. See also instr(). BUGS: AUTHOR: Jerry Leichter [[ C Utility Library]] Page 1-4 any Pattern Match -- Match Member of a Character Set 1.2 Pattern Match -- Match Member of a Character Set _______ _____ __ _____ ______ __ _ _________ ___ ******* * any * ******* NAME: any -- Pattern Match -- Match Member of a Character Set SYNOPSIS: #ifdef vms #include "c:cset.h" #else #include #endif char * any(s,cs) char s[]; CSET *cs; DESCRIPTION: Any() matches, returning a pointer to the second character of s, if the first character of s is a member of cs. Otherwise, it returns NULL. The null that terminates s is never considered to be a member of the cset, and is never matched; NULL is returned. Those familiar with SNOBOL should note that there is no notany() - the same effect can be obtained by using any() with cscomp(cs). BUGS: AUTHOR: Jerry Leichter [[ C Utility Library]] Page 1-5 blkcmp Compare Two Blocks of Memory 1.3 Compare Two Blocks of Memory _______ ___ ______ __ ______ ********** * blkcmp * ********** NAME: blkcmp -- Compare Two Blocks of Memory SYNOPSIS: blkcmp(a,b,nbytes) char *a; char *b; unsigned nbytes; DESCRIPTION: blkcmp compares two blocks of memory, each nbytes long. Like strcmp, it returns: -1 a < b 0 a == b +1 a > b where the comparison is lexicographical (as for strcmp). Zero bytes have no special meaning to blkcmp() and are compared along with everything else. BUGS: AUTHOR: Jerry Leichter [[ C Utility Library]] Page 1-6 blkeq Test Two Blocks of Memory For Equality 1.4 Test Two Blocks of Memory For Equality ____ ___ ______ __ ______ ___ ________ ********* * blkeq * ********* NAME: blkeq -- Test Two Blocks of Memory For Equality SYNOPSIS: blkeq(a,b,nbytes) char *a; char *b; unsigned nbytes; DESCRIPTION: blkeq returns TRUE if a and b point to nbytes bytes of identical memory. Zero bytes have no special meaning to blkeq() and are compared along with everything else. BUGS: AUTHOR: Jerry Leichter [[ C Utility Library]] Page 1-7 cscomp Complement a Cset 1.5 Complement a Cset __________ _ ____ ********** * cscomp * ********** NAME: cscomp -- Complement a Cset SYNOPSIS: #ifdef vms #include "c:cset.h" #else #include #endif CSET * cscomp(cs) CSET *cs; CSET * _cscomp(cs) CSET *cs; DESCRIPTION: cscomp() returns a cset whose members are exactly those characters not in cs. It is a macro, is extremely fast, and requires no memory beyond cs itself. _cscomp() does the same thing, but is a real function. Further, unlike cscomp(), it will return a unique cset if csunique is TRUE. BUGS: AUTHOR: Jerry Leichter [[ C Utility Library]] Page 1-8 cscopy Copy a Cset 1.6 Copy a Cset ____ _ ____ ********** * cscopy * ********** NAME: cscopy -- Copy a Cset SYNOPSIS: #ifdef vms #include "c:cset.h" #else #include #endif int cscopy(cs); CSET *cs; DESCRIPTION: cscopy() returns a cset whose members are exactly the same as the members of cs; however, the copy is independent of cs, and changes to cs will not affect it. cscopy() returns NULL if it can't obtain space for the new cset. BUGS: AUTHOR: Jerry Leichter [[ C Utility Library]] Page 1-9 csdiff Compute Difference of Two Csets 1.7 Compute Difference of Two Csets _______ __________ __ ___ _____ ********** * csdiff * ********** NAME: csdiff -- Compute Difference of Two Csets SYNOPSIS: #ifdef vms #include "c:cset.h" #else #include #endif CSET * csdiff(cs1,cs2); CSET *cs1; CSET *cs2; DESCRIPTION: The cset returned by csdiff() has as members all elements of cs1 that are not members of cs2. Whenever possible, csdiff() shares existing data by returning a cset that shares the memory used by cs1 and cs2. If csunique is true, a new cset is always formed. csdiff() returns NULL if it couldn't allocate a cset to return. BUGS: AUTHOR: Jerry Leichter [[ C Utility Library]] Page 1-10 cset Header file for character set functions 1.8 Header file for character set functions ______ ____ ___ _________ ___ _________ ******** * cset * ******** NAME: cset -- Header file for character set functions SYNOPSIS: #ifdef vms #include "c:cset.h" #else #include #endif DESCRIPTION: The character set functions provide a set of routines for describing and manipulating sets of characters. The character sets, called "csets", created in this way can be manipulated quickly, and require relatively little storage. They are meant to be used as arguments to pattern-matching functions like span() (which see). For these purposes, a set of functions to create csets, and produce the complement (with respect to the set of all 8-bit characters) of a set, and the join (union), meet (intersection) and difference of two csets is provided; see cset(), cscomp(), csjoin(), csmeet(), and csdiff(). csets can also be used more generally as representations of sets - i.e., the name can be read as "C sets". In this case, the universe is the set of numbers 0...(cssize-1), where cssize is a global parameter defined in cset.c; it is normally 256 for character work. The functions provided for this kind of application include csmember(), which checks membership, and csless() and cswith(), which add and remove elements from sets. When csets are used in this way, it is important to understand that a cset is a data object with an internal structure, and that different csets may share internal data - i.e., csets are not normally "atomic" objects and care must be taken in manipulating them. A look at the representation of csets should help clarify this point. The only object you normally manipulate directly in your [[ C Utility Library]] Page 1-11 cset Header file for character set functions code is a cset pointer, type (CSET *). This pointer points to a cset header, which contains a mask and a pointer to a table of cssize bytes. A character is in the cset if any of the bits in its mask is on in the corresponding table entry. Csets created by cset() always have a one-bit mask; however, csjoin() and friends, avoid, if possible, using up a bit position, by creating a header with a mask containing more than one bit. Hence, the join of two csets often can be represented very cheaply. Complements of csets are represented still more efficiently; even the header of a cset and its complement are shared. Only the pointer is changed - its bit pattern is complemented. A consequence of this representation is that a great deal of data is often shared between csets. When manipulating csets as arbitrary sets, it is important to understand that applying csless() or cswith() to a cset may cause any related csets to be changed. Thus, after the sequence of calls: uvowels = cset("AEIOU"); lvowels = cset("aeiou"); vowels = csjoin(uvowels,lvowels); lvowels = cswith(lvowels,'y'); 'y' is probably a member of vowels. (Only "probably" because it is impossible to predict whether uvowels and lvowels happen to get the same table; csjoin() cannot use the "cheap" representation if they don't.) Two methods are available to avoid this problem. First, cscopy() returns a guaranteed-"unique" copy of a cset. Second, the global csunique (in cset.c) can be set, forcing functions such as csjoin() to avoid space-saving shortcuts. BUGS: AUTHOR: Jerry Leichter [[ C Utility Library]] Page 1-12 cset Make a character set from a string 1.9 Make a character set from a string ____ _ _________ ___ ____ _ ______ ******** * cset * ******** NAME: cset -- Make a character set from a string SYNOPSIS: #ifdef vms #include "c:cset.h" #else #include #endif CSET *cset(s) char s[]; CSET *cset_t(s) char s[]; extern int csmask; extern int cssize; extern int csunique; DESCRIPTION: Both cset() and cset_t() make a character set containing exactly the characters in the string passed, and return a pointer to it, or NULL if they couldn't allocate space. The difference between the two is that cset_t() does not reserve the space it has used - the next cset created will re-use the space used for the set created at this call. Hence, cset_t() should be used to create temporary cset's that can be discarded automatically. WARNING: Do NOT use cset_t() to construct an argument to a function, such as csjoin(), that can allocate a cset. Should that function have to make an allocation, it will clobber the temporary cset that you passed as an argument, with unpredictabel results. cset_t() is meant for use in functions like span() which only read csets. Note, however, that the cscomp() never creates a new cset, so calls like span(cscomp(cset_t("aeiou")) are safe. The trailing null on s will not be a member of the cset [[ C Utility Library]] Page 1-13 cset Make a character set from a string created. Defined within this module are three global parameters that can be set to modify the actions of the cset functions. cssize defines the size of a cset, i.e., the maximum number of distinct characters that can be members. (The members have numbers from 0 to cssize-1.) By default, cssize is 256 to support full 8-bit ASCII. Do not change cssize once you have created a cset, or unpredictable results will occur. csmask is a mask applied to characters before they are used as indices in the data arrays. It is defined because without such masking the sign extension of 8-bit ASCII characters when they are made into int's - when passed to functions, for example - would cause problems. By default, csmask is 0377, which keeps only the low byte. You might want to change it, probably to ~0, if you set cssize greater than 256; in this case, the string-oriented routines - like cset() - may not work properly if strings with characters whose ASCII codes are greater than 127 are passed. Note: csmask is only used where necessary to avoid sign-extension problems; it should not be viewed as a general-purpose "hook". Values of csmask other than 0377 or ~0 will yield unpredictable results. csunique, when TRUE, forces all cset operators, EXCEPT cscomp(), to return unique csets, rather than attempting to optimize (on both space and speed) by sharing existing data. This is mainly useful if you intend to use csets as general sets (with cswith() and csless()). By default, csunique is FALSE. WARNING: cscomp() is NOT sensitive to the value of csunique. You must use the real function, _cscomp(), which does test csunique. You might want to consider re-defining the macro form to call the function in this case. Note: csunique has no effect on cset_t(), which always returns a "temporary" cset. BUGS: AUTHOR: Jerry Leichter [[ C Utility Library]] Page 1-14 csis Definitions For Use With csis.c 1.10 Definitions For Use With csis.c ___________ ___ ___ ____ ______ ******** * csis * ******** NAME: csis -- Definitions For Use With csis.c SYNOPSIS: #ifdef vms #include "c:cset.h" #include "c:csis.h" #else #include #include #endif DESCRIPTION: This header file just provides the external definitions for all the csets defined in cset.c. Note that the cset header file, cset.h, must be #include'd before this file, since this file makes use of definitions made there. BUGS: AUTHOR: Jerry Leichter [[ C Utility Library]] Page 1-15 csis Define Csets For Standard "is" Functions 1.11 Define Csets For Standard "is" Functions ______ _____ ___ ________ ____ _________ ******** * csis * ******** NAME: csis -- Define Csets For Standard "is" Functions SYNOPSIS: #ifdef vms #include "c:cset.h" #include "c:csis.h" #else #include #include #endif extern CSET *csupper; extern CSET *cslower; extern CSET *csalpha; extern CSET *csdigit; extern CSET *csalnum; extern CSET *csxdigit; extern CSET *csspace; extern CSET *cspunct; extern CSET *csgraph; extern CSET *csprint; extern CSET *cscntrl; DESCRIPTION: Csis defines a group of csets that are based on the standard "is" functions, and the character table _ctype_. In fact, the csets defined use _ctype_ as their table. In general, isxxx(c) is equivalent to csmember(csxxx,c). The only exception is that there is no equivalent of isascii(), since that test is a range check to see if a reference to the table is meaningful, so there is no meaningful analogue. Further, meaningless results may be returned if c is EOF. Note that new csets can be built using the csets defined here as a base; however, if you apply csless() or cswith() to one of these csets, you will change the data base that all the "is" functions refer to, with unfortunate results. [[ C Utility Library]] Page 1-16 csis Define Csets For Standard "is" Functions BUGS: AUTHOR: Jerry Leichter [[ C Utility Library]] Page 1-17 csjoin Compute Join (Union) of Two Csets 1.12 Compute Join (Union) of Two Csets _______ ____ _______ __ ___ _____ ********** * csjoin * ********** NAME: csjoin -- Compute Join (Union) of Two Csets SYNOPSIS: #ifdef vms #include "c:cset.h" #else #include #endif CSET * csjoin(cs1,cs2); CSET *cs1; CSET *cs2; DESCRIPTION: The cset returned by csjoin() has as members all elements of either cs1 or cs2. Whenever possible, csjoin() shares existing data by returning a cset that shares the memory used by cs1 and cs2. This is most likely to occur if very few calls to cset() (or other cset functions that allocate new csets) occured between the calls that created cs1 and cs2. If csunique is true, a new cset is always formed. csjoin() returns NULL if it couldn't allocate a cset to return. BUGS: AUTHOR: Jerry Leichter [[ C Utility Library]] Page 1-18 csjoin Compute Join (Union) of Two Csets 1.13 Compute Join (Union) of Two Csets _______ ____ _______ __ ___ _____ ********** * csjoin * ********** NAME: csjoin -- Compute Join (Union) of Two Csets SYNOPSIS: #ifdef vms #include "c:cset.h" #else #include #endif CSET * csjoin(cs1,cs2); CSET *cs1; CSET *cs2; DESCRIPTION: The cset returned by csjoin() has as members all elements of either cs1 or cs2. Whenever possible, csjoin() shares existing data by returning a cset that shares the memory used by cs1 and cs2. This is most likely to occur if very few calls to cset() (or other cset functions that allocate new csets) occured between the calls that created cs1 and cs2. If csunique is true, a new cset is always formed. csjoin() returns NULL if it couldn't allocate a cset to return. BUGS: AUTHOR: Jerry Leichter [[ C Utility Library]] Page 1-19 csless Remove an Element From a Cset 1.14 Remove an Element From a Cset ______ __ _______ ____ _ ____ ********** * csless * ********** NAME: csless -- Remove an Element From a Cset SYNOPSIS: #ifdef vms #include "c:cset.h" #else #include #endif CSET * csless(cs,c) CSET *cs; int c; DESCRIPTION: The element c, which is generally a character, is removed from the cset cs if it is a member. If c is not a member, cs is unchanged. This call modifies the actual cset cs, whatever the value of csunique. cs itself is returned. Note: c is not validated; c < 0 or c >= cs may cause random bits in memory to be changed. However, csmask is used to control unwanted sign extension - see cset(). BUGS: AUTHOR: Jerry Leichter [[ C Utility Library]] Page 1-20 csless Remove an Element From a Cset 1.15 Remove an Element From a Cset ______ __ _______ ____ _ ____ ********** * csless * ********** NAME: csless -- Remove an Element From a Cset SYNOPSIS: #ifdef vms #include "c:cset.h" #else #include #endif CSET * csless(cs,c) CSET *cs; int c; DESCRIPTION: The element c, which is generally a character, is removed from the cset cs if it is a member. If c is not a member, cs is unchanged. This call modifies the actual cset cs, whatever the value of csunique. cs itself is returned. Note: c is not validated; c < 0 or c >= cs may cause random bits in memory to be changed. However, csmask is used to control unwanted sign extension - see cset(). BUGS: AUTHOR: Jerry Leichter [[ C Utility Library]] Page 1-21 csmeet Compute Meet (Intersection) of Two Csets 1.16 Compute Meet (Intersection) of Two Csets _______ ____ ______________ __ ___ _____ ********** * csmeet * ********** NAME: csmeet -- Compute Meet (Intersection) of Two Csets SYNOPSIS: #ifdef vms #include "c:cset.h" #else #include #endif CSET * csmeet(cs1,cs2); CSET *cs1; CSET *cs2; DESCRIPTION: The cset returned by csmeet() has as members all elements of both cs1 and cs2. Whenever possible, csmeet() shares existing data by returning a cset that shares the memory used by cs1 and cs2. If csunique is true, a new cset is always formed. csmeet() returns NULL if it couldn't allocate a cset to return. BUGS: AUTHOR: Jerry Leichter [[ C Utility Library]] Page 1-22 csmember Test For Membership in a Cset 1.17 Test For Membership in a Cset ____ ___ __________ __ _ ____ ************ * csmember * ************ NAME: csmember -- Test For Membership in a Cset SYNOPSIS: #ifdef vms #include "c:cset.h" #else #include #endif int csmember(cs,c); CSET *cs; int c; DESCRIPTION: csmember() returns TRUE if c is a member of the cset cs, FALSE otherwise. Note that c is typically a char. Note: c is not validated; c<0 or c>=cssize will return nonsense. However, csmask is used to control unwanted sign extension - see cset(). BUGS: AUTHOR: Jerry Leichter [[ C Utility Library]] Page 1-23 cswith Add an Element To a Cset 1.18 Add an Element To a Cset ___ __ _______ __ _ ____ ********** * cswith * ********** NAME: cswith -- Add an Element To a Cset SYNOPSIS: #ifdef vms #include "c:cset.h" #else #include #endif CSET * cswith(cs,c) CSET *cs; int c; DESCRIPTION: The element c, which is generally a character, is added to the cset cs. This call modifies the actual cset cs, whatever the value of csunique. cs itself is returned. Note: c is not validated; c < 0 or c >= cssize may cause cswith() to set an arbitrary bit in memory. However, csmask is used to control unwanted sign extension; see cset(). BUGS: AUTHOR: Jerry Leichter [[ C Utility Library]] Page 1-24 esc Process Escaped Characters 1.19 Process Escaped Characters _______ _______ __________ ******* * esc * ******* NAME: esc -- Process Escaped Characters SYNOPSIS: char esc(ppc) char **ppc; int esc_msk; DESCRIPTION: esc(ppc) recognizes and processes the escaped characters recognized by the C compiler. If *ppc points to any character other than '\', esc() just returns that character. Otherwise, it examines the next character. If it finds a member of the set {b,f,n,r,t}, the appropriate character is returned, e.g., a for '\t'. Also, \ddd, ddd being up to three octal digits, returns the character with value ddd. Only the bottom 8 bits are retained, so the result is always a "proper" character. This can be changed by changing the global esc_msk to contain whatever mask you prefer. (The default is 0377.) The special characters b, f, and so on are recognized in lower case only. If '\' is the last character in s, '\' itself is returned. If '\' is followed by any other character x, x itself is returned. In all cases, *ppc is left pointing at the last character that esc() has "eaten", e.g., at the 't' if it is returning a for '\t'. BUGS: AUTHOR: Jerry Leichter [[ C Utility Library]] Page 1-25 eval Expression Evaluation 1.20 Expression Evaluation __________ __________ ******** * eval * ******** NAME: eval -- Expression Evaluation SYNOPSIS: eval expression (as a main program) int eval(string, result) char *string; /* String to evaluate */ union { /* Pointer to result */ long value; /* Result if no error */ char *error; /* Message if error */ } *result; DESCRIPTION: By setting a compile-time parameter, eval may be configured as a main program that reads an expression, printing the result in decimal, octal, and hexadecimal. When compiled as a subroutine, eval() accepts a character string argument, returning a long result (and an error indicator): If eval() returns zero, the evaluation is stored in result.value. Else, there is an error and and the value returned is the index in string of the first byte not parsed, while result.error is a pointer to an error message. (Note that, if the error was discovered after the entire string was parsed, the index may be greater than the length of the string.) AUTHOR: Martin Minow, using an algorithm developed by Mike Lutz and Bob Harper of RPI. [[ C Utility Library]] Page 1-26 flex Header File For Flexes 1.21 Header File For Flexes ______ ____ ___ ______ ******** * flex * ******** NAME: flex -- Header File For Flexes SYNOPSIS: #ifdef vms #include "c:flex.h" #else #include #endif DESCRIPTION: This header file is used to allow programs to access flexes. See flex.c for additional details. This file defines the structure of a flex. The fields are as follows: char *fxdata Pointer to the actual data. This will always be the first element, so that a flex can be considered to be of type char ** (or whatever) if you only want to read it. fxdata will be NULL if the flex has been damaged. unsigned fxused Number of items used in the flex. unsigned fxdim Number of items the flex can hold; 0 if the flex has been damaged. unsigned fxisz Size of one item, in bytes. unsigned fxext Number of items to grow by; if 0, the flex won't grow. [[ C Utility Library]] Page 1-27 flex Header File For Flexes The only fields generally directly accessed by programs are fxdata and fxlen. Note that fxdata may change over time as items are added to and removed from the flex. The only field you should normally change is fxext, the extension quantity. The value current the next time the flex must grow will determine how much it grows by. flex.h also contains extern declarations describing the datatypes of all the flex-handling routines. BUGS: AUTHOR: Jerry Leichter [[ C Utility Library]] Page 1-28 flex Create a New Flex 1.22 Create a New Flex ______ _ ___ ____ ******** * flex * ******** NAME: flex -- Create a New Flex SYNOPSIS: #ifdef vms #include "c:flex.h" #else #include #endif FLEX * flex(dim,isz,ext) unsigned dim; /* Initial size, items */ unsigned isz; /* Item size, bytes */ unsigned ext; /* Items to extend by */ DESCRIPTION: Flexes are dynamically expandable arrays. If an attempt is made to add an item to a flex that is already full, the flex's core area is re-allocated to make more room. All the items in a flex must be the same size; all references are by item numbers, which, like array indices, run from 0 to (# items in the flex minus 1). When a flex must expand, it expands some whole number of items; the particular number to expand by is stored in the flex's header. If this number is zero, the flex cannot expand. If a flex cannot expand, either because its header sets a growth size of 0 or because there is insufficient core, but a request is made to add an item while it is full, the flex is marked damaged. The core it used for its data is lost (realloc() free's it) and any further attempts to use the flex are ignored. In general, any time a flex becomes damaged, or a damaged flex is passed to one of the flex handling routines, the routine returns NULL. (Usually, they just return the flex passed.) A flex actually consists of a header, which contains information describing it, and a separate block of data. The first item in the header is a pointer to the data. [[ C Utility Library]] Page 1-29 flex Create a New Flex While the header never moves, the data may move any time items are added to or removed from the flex. It is occassionally necessary to recover the information in a damaged flex (in most cases, there is no reasonable way to continue once the program runs out of memory). This can be done by making use of realloc()'s ability to reallocate the most recently freed block of core. Before making the potentially damaging call, save the current values of the fxdata and fxused elements of the flex. Should the flex become damaged, the fxdata value can be passed to realloc() safely. Note that this recovers the data, but not the flex's header; you still cannot apply any of the flex-handling functions to it. The various flex handling functions in the Utility Library all have names beginning "fx". They form a fairly complete set of primitives. You may wish to restrict yourself to doing all accesses through these routines since this would, in principle, allow you to replace them with a set of routines that, for example, page items to disk if a flex gets too large. The file flex.h contains some definitions that are useful when dealing with flexes. The function flex(dim,isz,ext) creates a new flex. The data area of the flex will initially contain sufficient space for dim items, each isz bytes in length. If the flex fills, it will be extended in units of ext items. BUGS: AUTHOR: Jerry Leichter [[ C Utility Library]] Page 1-30 fmatch Pattern Match Using DEC Filename Conventions 1.23 Pattern Match Using DEC Filename Conventions _______ _____ _____ ___ ________ ___________ ********** * fmatch * ********** NAME: fmatch -- Pattern Match Using DEC Filename Conventions SYNOPSIS: int fmatch(name, pattern) char *name; /* What to look for */ char *pattern; /* Pattern to match */ DESCRIPTION: fmatch returns TRUE if the name argument is matched by the pattern argument. The pattern follows DEC filename wildcard conventions: '*' matches any string (even null) and '?' or '%' matches any single (non-null) byte. Also, upper/lower case differences are ignored. When matching against file name strings, the calling program must already have parsed the name string to remove terminators, such as '.' or ';'. BUGS: AUTHOR: Martin Minow, borrowed from RT11 PIP. [[ C Utility Library]] Page 1-31 fxadd Add an Item To a Flex 1.24 Add an Item To a Flex ___ __ ____ __ _ ____ ********* * fxadd * ********* NAME: fxadd -- Add an Item To a Flex SYNOPSIS: #ifdef vms #include "c:flex.h" #else #include #endif FLEX * fxadd(fx,itm) register FLEX *fx; char *itm; DESCRIPTION: fxadd() adds an item to a flex. It returns NULL if the flex would have had to grow but couldn't because of insufficient memory or a zero extension quantity. In this case, the data in the flex is lost and the flex itself cannot be used again; further attempts always return NULL. If all went well, the flex pointer passed is returned. Note that an item is defined entirely by its length; fxadd() will try to copy exactly fx->fxisz bytes, regardless of null bytes, etc. BUGS: AUTHOR: Jerry Leichter [[ C Utility Library]] Page 1-32 fxaddo Add Ordered To a Flex 1.25 Add Ordered To a Flex ___ _______ __ _ ____ ********** * fxaddo * ********** NAME: fxaddo -- Add Ordered To a Flex SYNOPSIS: #ifdef vms #include "c:flex.h" #else #include #endif FLEX *fxaddo(fx,itm,ord) FLEX *fx; char *itm; int (*ord)(); extern unsigned fxladd; DESCRIPTION: fxaddo is similar to fxadd except that it ensures that the items in the flex are in increasing order, where "in order" is defined by a passed function, ord. ord must return values akin to strcmp: ord(a,b) is -1 if ab. Elements that compare equal will be stored in the order they are passed. The global fxladd gets the item number at which itm was added. WARNING: fxaddo() uses straight insertion to maintain the ordering. This is an O(n**2) algorithm and will become very slow when the flex gets large. If you need to build a large, sorted flex, it is probably best to fill it first and then use something like qqsort() to sort it when you are done. (Another approach, more in keeping with the philosophy of fxaddo(), would be to define, say, fxaddh(), which keeps the flex ordered as a heap, and then scan through it as a heap. I leave this to someone else.) Note: If for some reason you don't want to include equal elements, you can safely issue an error message in ord() and use, for example, setjmp() and longjmp() to abort the insertion. This process will leave the flex [[ C Utility Library]] Page 1-33 fxaddo Add Ordered To a Flex unchanged. Alternatively, set a flag and use fxladd to eject the item later. BUGS: AUTHOR: Jerry Leichter [[ C Utility Library]] Page 1-34 fxeject Eject (Delete) an Item From a Flex 1.26 Eject (Delete) an Item From a Flex _____ ________ __ ____ ____ _ ____ *********** * fxeject * *********** NAME: fxeject -- Eject (Delete) an Item From a Flex SYNOPSIS: #ifdef vms #include "c:flex.h" #else #include #endif FLEX * fxeject(fx,n) FLEX *fx; unsigned n; DESCRIPTION: fxeject(fx,n) removes item n from the flex fx, moving all later items in to fill in the gap. The space allocated for the flex is not changed, however. fxeject() returns fx if all went well, NULL if n was not a legal item number or fx was damaged. BUGS: AUTHOR: Jerry Leichter [[ C Utility Library]] Page 1-35 fxfree Free a Flex 1.27 Free a Flex ____ _ ____ ********** * fxfree * ********** NAME: fxfree -- Free a Flex SYNOPSIS: #ifdef vms #include "c:flex.h" #else #include #endif fxfree(fx) FLEX *fx; DESCRIPTION: Free a flex. Attempting to free something that was not created with flex() will cause trouble. However, NULL or a flex that was damaged may be passed safely. BUGS: AUTHOR: Jerry Leichter [[ C Utility Library]] Page 1-36 fxinject Inject an Item Into a Flex 1.28 Inject an Item Into a Flex ______ __ ____ ____ _ ____ ************ * fxinject * ************ NAME: fxinject -- Inject an Item Into a Flex SYNOPSIS: #ifdef vms #include "c:flex.h" #else #include #endif FLEX * fxinject(fx,itm,n) FLEX *fx; char *itm; unsigned n; DESCRIPTION: fxinject(fx,itm,n) injects itm into flex fx so that it becomes item number n; items already in the flex are moved to make room. Another way to look at this is that fxinject injects itm before item n in fx, and allows the special case of n=1+(greatest item number) for injection at the end (which is functionally identical to fxadd()). fxinject() returns fx if all went well, otherwise NULL. fx is never expanded or marked damaged if n is out of range. BUGS: AUTHOR: Jerry Leichter [[ C Utility Library]] Page 1-37 fxitem Obtain (the Address of) an Item In a Flex 1.29 Obtain (the Address of) an Item In a Flex ______ ____ _______ ___ __ ____ __ _ ____ ********** * fxitem * ********** NAME: fxitem -- Obtain (the Address of) an Item In a Flex SYNOPSIS: #ifdef vms #include "c:flex.h" #else #include #endif char * fxitem(fx,n) register FLEX *fx; unsigned n; DESCRIPTION: fxitem(fx,n) returns a pointer to the n'th item in the flex, if there is one, or NULL if there isn't or the flex has been damaged. The first item in the flex has item number 0. BUGS: AUTHOR: Jerry Leichter [[ C Utility Library]] Page 1-38 fxneed Explicitly Grow or Shrink a Flex 1.30 Explicitly Grow or Shrink a Flex __________ ____ __ ______ _ ____ ********** * fxneed * ********** NAME: fxneed -- Explicitly Grow or Shrink a Flex SYNOPSIS: #ifdef vms #include "c:flex.h" #else #include #endif FLEX *fxneed(fx,cnt) FLEX *fx; unsigned cnt; DESCRIPTION: Ensure that a flex contains room for some entries. fxneed(fx,cnt) expands the flex, if possible, so that at least cnt items will fit with no further expansion. (If fx is expanded, it is expanded so that it will have room for EXACTLY cnt more items.) If cnt=0, the flex is reduced in size, if necessary, so that it has no empty space in it. (The extension quantity is not altered; you can add to the flex again later.) fxneed() ignores the extension quantity (fxext) specified for the flex; it will extend exactly as much as it needs to, and will extend even when fxext is 0. If all goes well, fx is returned; otherwise, NULL is returned and the flex is marked damaged. BUGS: AUTHOR: Jerry Leichter [[ C Utility Library]] Page 1-39 fxset Set the Value of an Item In a Flex 1.31 Set the Value of an Item In a Flex ___ ___ _____ __ __ ____ __ _ ____ ********* * fxset * ********* NAME: fxset -- Set the Value of an Item In a Flex SYNOPSIS: #ifdef vms #include "c:flex.h" #else #include #endif FLEX * fxset(fx,itm,n) FLEX *fx; char *itm; unsigned n; DESCRIPTION: fxset(fx,itm,n) changes the value of item n in fx to itm. It is mainly useful for "information-hiding"; if you always use fxset() to set elements, rather than using the address returned by fxitem() directly, you'll find it much easier to use a different set of flex handlers - e.g. flex handlers that page data to disk and need to know when in-core data has been changed. Also, it could easily produce some sort of immediate "index out of range" error, instead of just returning NULL (for easier debugging). fxset() returns fx itself if all went well, and NULL if fx was damaged or had no item n. BUGS: AUTHOR: Jerry Leichter [[ C Utility Library]] Page 1-40 initial Specify Initializers and Finalizers 1.32 Specify Initializers and Finalizers _______ ____________ ___ __________ *********** * initial * *********** NAME: initial -- Specify Initializers and Finalizers SYNOPSIS: #include INITIAL { initial-code-block }; FINAL { final-code-block }; DESCRIPTION: The macros defined in this module provide a facility for module-specific initialization and finalization code. The code in the initial-code-block is called as a normal C function before main() is called; the final-code-block is called on exit, just after wrapup(). Neither call passes any arguments. Any number of modules in an image may independently declare initializers or finalizers; all of them will be called at startup or exit. However, it is impossible to predict what order the calls will be made in, and programs should not rely on any particular ordering. A typical use of initializers and finalizers is the following: Suppose you have a package that supports access to some on-disk data base; a user of the package will call a lookup and an update function. The file containing the data base must be opened before any operations on it can take place, and it should be closed when the program is finished. (Assume that the package maintains some sort of resident buffer which it must flush.) There are two conventional approaches to solving this problem: Have the lookup and update functions check the file on each call, and open it if necessary - which could be quite expensive if they are very small functions, and in any case does not solve the problem of properly closing the file - or have the main program call an initialization and finalization function at the proper time. The problem with this latter approach is [[ C Utility Library]] Page 1-41 initial Specify Initializers and Finalizers lack of modularity - the main program ought not to have to know that the module needs initialization or finalization. The solution using these macros is straightforward. The defining module includes the calls: INITIAL { open-the-data-base }; FINAL { flush-buffers-and-close-the-data-base }; The wrapup() function is treated like a built-in finalizer; however, it is guaranteed to execute before any other finalizers. (The module defining wrapup() may declare an initializer or finalizer if it wishes; it will be treated just like all other initializers and finalizers.) If any finalizer (or wrapup()) calls exit(), the program exits immediately. Notes: Using INITIAL will declare a static function named $init$(), and a (char *) named $init_; similarly, FINAL will declare $finl$() and $finl_. Also, both INITIAL and FINAL generate dsect commands. Since C provides no way to "remember" the current dsect, both macros issue a dsect "" command when they are done, restoring the C default dsect. While it is a poor idea to write code that depends on the order in which initializers and finalizers are executed, this can be useful information to have for debugging. Execution is always in the order that the modules involved were examined by the task builder or linker. When the modules are named explicitly, this will just be the order in which they were named. When the modules come from a library, it will be extremely difficult to predict the order in which they will be extracted and linked in. Warning: While initializers and finalizers provide some modularity to package initialization and finalization, they are not a panacea. For example, if package A calls routines in package B from within its initializers, and package B also declares initializers, the correct functioning of a program that includes both - hence, of any program using package A - will be problematical. BUGS: Requires the DECUS C dsect commands; hence, very non-portable. It may be possible to provide the same [[ C Utility Library]] Page 1-42 initial Specify Initializers and Finalizers functionality using different techniques; if not, what's wrong with your implementation? AUTHOR: Jerry Leichter [[ C Utility Library]] Page 1-43 instr Get Index of a String In a String 1.33 Get Index of a String In a String ___ _____ __ _ ______ __ _ ______ ********* * instr * ********* NAME: instr -- Get Index of a String In a String SYNOPSIS: int instr(s,p) char s[]; /* Subject string */ char p[]; /* Pattern string */ DESCRIPTION: instr(s,p) returns the index of the first occurence of *p in *s, or -1 if p is not a substring of s. (The index is the offset to the beginning of the occurence; for example, instr("abcd","bc")==1.) If strlen(p) == 0, instr() returns 0. See also after(). BUGS: AUTHOR: Jerry Leichter [[ C Utility Library]] Page 1-44 iovtoa Convert File Name to Ascii - obsolete 1.34 Convert File Name to Ascii - obsolete _______ ____ ____ __ _____ _ ________ ********** * iovtoa * ********** NAME: iovtoa -- Convert File Name to Ascii - obsolete SYNOPSIS: char * iovtoa(fd,buffer); FILE *fd; char *buffer; DESCRIPTION: iovtoa() is an obsolete name and form of fgetname(). It exists solely for compatibility purposes; new programs should use fgetname() and old ones SHOULD (eventually) be converted. Note that iovtoa() differs from fgetname() in only one way: While fgetname() returns buffer, iovtoa() returns a pointer to the null trailing the extracted name. BUGS: Obsolete. AUTHOR: Jerry Leichter [[ C Utility Library]] Page 1-45 match Determine if a String Is a Leading Substring of Another 1.35 Determine if a String Is a Leading Substring of Another _________ __ _ ______ __ _ _______ _________ __ _______ ********* * match * ********* NAME: match -- Determine if a String Is a Leading Substring of Another SYNOPSIS: char * match(s,p) char *s; char *p; DESCRIPTION: match(s,p) returns a pointer to the first character of s beyond those matching p, if p is an initial substring of s. Otherwise, it returns NULL. Thus, match("abcde","abc") returns a pointer to the 'd' in the first string; match("abcde","bc") returns NULL. BUGS: AUTHOR: Jerry Leichter [[ C Utility Library]] Page 1-46 ospan Pattern Match -- Span Optional Matching Characters 1.36 Pattern Match -- Span Optional Matching Characters _______ _____ __ ____ ________ ________ __________ ********* * ospan * ********* NAME: ospan -- Pattern Match -- Span Optional Matching Characters SYNOPSIS: #ifdef vms #include "c:cset.h" #else #include #endif char * ospan(s,cs) char s[]; CSET *cs; DESCRIPTION: Ospan() matches the longest leading substring of s that consists entirely of characters that are members of the cset cs. It returns a pointer to the first character after the matched characters. Ospan(), unlike span(), will match the null string, returing s if the first character in s is a member of cs. The null that terminates s is never considered to be a member of the cset, and is never included in the spanned characters; if all the other characters in s are members of cs, ospan() returns a pointer to the trailing null. BUGS: AUTHOR: Jerry Leichter [[ C Utility Library]] Page 1-47 pmatch Pattern Match -- Unix File Conventions 1.37 Pattern Match -- Unix File Conventions _______ _____ __ ____ ____ ___________ ********** * pmatch * ********** NAME: pmatch -- Pattern Match -- Unix File Conventions SYNOPSIS: int pmatch(string, pattern) char *string; /* What to look for */ char *pattern; /* Pattern to match */ DESCRIPTION: pmatch() returns TRUE if the string argument is matched by the pattern argument. The pattern follows Unix filename conventions: * matches any (even null) string ? matches any single (non-null) byte [class] defines a class of characters. A class consists of either a list of valid characters or pairs of characters separated by '-' defining a range. \ uses the next character literally; same as '\\' at pattern end. Note that upper- and lower-case are distinct. BUGS: AUTHOR: Martin Minow, taken from various Unix utilities. [[ C Utility Library]] Page 1-48 qksort Sort an array in memory 1.38 Sort an array in memory ____ __ _____ __ ______ ********** * qksort * ********** NAME: qksort -- Sort an array in memory SYNOPSIS: qksort(array, num_elements, elt_size, qkcmp); BYTE array[]; unsigned int num_elements; int element_size; int (*qkcmp)(); DESCRIPTION: Sort the array. The user must supply the routine which qksort calls to compare two array elements. The parameters are as follows: array The data to be sorted. Within the program, it will be treated as a character vector. Its true organization must be understood by the comparison routine. num_elements The number of elements in the array. element_size The size of each element in the array. qkcmp() A user-supplied routine that compares two elements. qkcmp() will be called as if it were declared: qkcmp(a, b) BYTE *a; BYTE *b; It returns -1, 0, +1 according to whether element a is respectively less than, equal to, or greater than element b. For example: int int_array[10]; [[ C Utility Library]] Page 1-49 qksort Sort an array in memory char *str_array[10]; ... qksort(int_array, 10, sizeof(int), &int_cmp); qksort(str_array, 10, sizeof(char *), &str_cmp); ... int_cmp(a, b) int *a, *b; { return (*a - *b); } ... str_cmp(a, b) char *a[], *b[]; { return (strcmp(*a, *b)); } NOTE: The performance is best if array elements are aligned on word boundaries. This will be the case if the array is of int's, etc, or pointers to strings. The obvious choice for strings is for it to be an array of pointers to strings, anyway. This routine is NOT recursive, requires minimal stack space, and is efficient for any existing order in the original array, even if it is already sorted, in reverse order, or has all equal elements. qksort() is essentially 'program 2' from R. Sedgewick, "Quicksort Programs", Comm ACM, Oct 1978 p 847-856. This is the slickest quicksort I have ever come across, and I have been collecting them for several years. The source module contains extensive documentation on the algorithm's performance, including a test package that is useful when the algorithm is changed. AUTHOR: Ray Van Tassle 13 Apr 1982 [[ C Utility Library]] Page 1-50 replace Simple Character Translation 1.39 Simple Character Translation ______ _________ ___________ *********** * replace * *********** NAME: replace -- Simple Character Translation SYNOPSIS: char * replace(s,from,to) char s[]; char from[]; char to[]; extern char rep_tab[256]; DESCRIPTION: replace() provides a simple interface to translate() and the functions associated with it; see transl.c for details. Briefly, each character character in s that occurs in from is replaced by the corresponding character in to. If there are more characters in from than in to, the "excess" characters are deleted from the output string. replace() returns the output string in space allocated from malloc(); the input string is unchanged. If replace() can't get the space it needs, it returns NULL. rep_tab is a global character table that is used in the internal calls to the translation routines. It is large enough to translate any 8-bit byte. rep_tab is cleared and the set up on each call to replace(). If you intend to call replace() many times with the same second and third arguments, you can call it once to set up rep_tab and then call translate() directly - change subsequent s1 = replace(s,t,f) calls to translate(rep_tab,s1 = savestr(s)). Note: The power of a function like replace() is often not appreciated. If s is any 5-character string, then replace("abcde","edcba",s) is s reversed - i.e., this call maps "words" to "sdrow". In general, if P is any permutation of n-character strings, n<256, then the call replace(b,P(b),s) returns P(s), where b is any (constant) string of length n containing n different characters. (s, of course, is also of length n). Thus, replace() implements character substitutions, deletions, [[ C Utility Library]] Page 1-51 replace Simple Character Translation and permutations, all in one small, simple to use function. BUGS: AUTHOR: Jerry Leichter [[ C Utility Library]] Page 1-52 savestr Make a Safe Copy of a String 1.40 Make a Safe Copy of a String ____ _ ____ ____ __ _ ______ *********** * savestr * *********** NAME: savestr -- Make a Safe Copy of a String SYNOPSIS: char * savestr(s) char s[]; DESCRIPTION: savestr(s) returns a pointer to a newly-allocated block of memory containing a copy of s. The memory is obtained from malloc() and should be freed with free() when you are done with it. savestr() returns NULL if it cannot obtain the memory it needs. BUGS: AUTHOR: Jerry Leichter [[ C Utility Library]] Page 1-53 span Pattern Match -- Span Required Matching Characters 1.41 Pattern Match -- Span Required Matching Characters _______ _____ __ ____ ________ ________ __________ ******** * span * ******** NAME: span -- Pattern Match -- Span Required Matching Characters SYNOPSIS: #ifdef vms #include "c:cset.h" #else #include #endif char * span(s,cs) char s[]; CSET *cs; DESCRIPTION: Span() matches the longest leading substring of s that consists entirely of characters that are members of the cset cs. It returns a pointer to the first character after the matched characters. The null that terminates s is never considered to be a member of the cset, and is never included in the spanned characters; if all the other characters in s are members of cs, span() returns a pointer to the trailing null. Span() differs from ospan() in that it must find at least one matching character; otherwise, it returns NULL. BUGS: AUTHOR: Jerry Leichter [[ C Utility Library]] Page 1-54 strfns Simple String Functions 1.42 Simple String Functions ______ ______ _________ ********** * strfns * ********** NAME: strfns -- Simple String Functions SYNOPSIS: char * ctos(c) char c; char * scopy(s) char s[]; char * scat(s1,s2) char s1[],s2[]; char * mid(s,n,l) char s[]; int n,l; char * seg(s,n,l) char s[]; int n,l; char * right(s,l) char s[]; int l; char * left(s,n) char s[]; int n; DESCRIPTION: The functions defined here provide simple manipulation of character strings a la BASIC. The strings returned are all new - i.e. not substrings of the strings passed. All returned strings are in space acquired from talloc() (q.v.); you should set a mark before calling these functions and then used tfree() to clean up. [[ C Utility Library]] Page 1-55 strfns Simple String Functions A return of NULL indicates a fatal error - usually it means that the function couldn't allocate space for the string it wanted to make. ctos(c) returns a new string containing single character c. scopy(s) returns a fresh copy of s. scat(s,t) concatenates s and t. mid(s,n,l) extracts l characters from s starting at character n (first character is n=0, etc.). If the substring specified does not exist, the part that does is extracted. For example, n<0 is equivalent to n=0. seg(s,n,m) is the substring of characters in positions n through m inclusive. (The first character is position 0). If n>m, they are exchanged. As with mid(), if the "substring" specified overflows s, only the "proper" part is extracted. left(s,l) extracts the leftmost l characters of s, with "overflow" handled as for mid(). right(s,n) is the substring of characters starting at character n, where the first character is character 0. Overflow handling is as for mid(). Note: These functions have been written to be simple to use, not to be particularly efficient. If you are going to be doing large amounts of string processing, you will almost certainly want to use different techniques. Don't forget that every string created in space allocated by talloc() requires 4 words of overhead - 2 for malloc() and 1 for talloc(). BUGS: AUTHOR: Jerry Leichter [[ C Utility Library]] Page 1-56 tables Hash Table Handler Functions 1.43 Hash Table Handler Functions ____ _____ _______ _________ ********** * tables * ********** NAME: tables -- Hash Table Handler Functions SYNOPSIS: typedef char TABLE; TABLE * table(size) int size; tdelete(t,key) TABLE *t; char *key; tinsert(t,key,value) TABLE *t; char *key; int value; tlookup(t,key) TABLE *t; char *key; tnext(t,n,key,value) TABLE *t; int n; char **key; int *value; tupdate(t,key,value) TABLE *t; char *key; int value; DESCRIPTION: Tables are associative memory. They consist of keys and associated values. You can add, delete, and change pairs of keys and values and look up the value associated with a key. Keys are arbitrary strings; values are arbitrary integers, except that NULL should not be used as a value since tlookup() returns NULL to indicate that a key was [[ C Utility Library]] Page 1-57 tables Hash Table Handler Functions not found in the table. In many cases, value is actually a pointer to a string. Note the difference in the handling of key and value. The functions automatically copy the string that key points to (with savestr()) when they create a new table entry; hence, you can pass a pointer to a string in a buffer or anywhere. value, however, is treated as a simple int; if it points to a string somewhere, it is your responsibility to make sure that that string remains unchanged as long as you need it. Tables can be freed with free(); however, note that this will not free the space used for the keys in the table. You must free this space, and space used for value strings, if any; use tnext() to find all the elements. table(size) creates a new table and returns a pointer to it. The table has room for size elements, and cannot grow; however, the space used for deleted elements is reclaimed automatically. table() returns NULL if it cannot obtain (from malloc()) the space it needs - 2*sizeof(char *)*size bytes, plus a small fixed overhead. Tables are implemented using a hashing technique. Accesses will be fast as long as the table does not become too full; however, performance will degrade rapidly if the table fills. A good rule of thumb is to allow space for 10% more elements than you will actually need to store. tdelete(t,key) deletes key and its associated value from t. If all goes well, 0 is returned; if key was not in t, 1 is returned and t is unaltered. Note that only the space that tinsert() allocated - for the key - is freed automatically; you must free any space used by the value associated with key. tinsert(t,key,value) inserts value as the value associated with key in table t. If all went well, tinsert() returns 0. If the table is full, or no space could be allocated to copy the key, tinsert() returns -1. If key is already in t, tinsert() returns 1; the value currently associated with key is not changed. tlookup(t,key) returns the value associated with key in t or NULL if key isn't found. [[ C Utility Library]] Page 1-58 tables Hash Table Handler Functions tnext() is used to scan through all elements in a table in sequence. It is used thus: char *key; int value; for (n = -1; (n = tnext(t, n, &key, &value)) >= 0;) { /* process pair (key, value) */ } Unpredictable results will occur if you insert new elements into a table (with tinsert()) while scanning it. The other table functions may be called with no problems. CAUTION: The string pointed to by key as returned is the only copy available in the table. Treat it as read-only! tupdate(t,key,value) changes the value associated with key to value. tupdate() returns 0 if all went well, 1 if key was not found in t. In the latter case, t is unaltered. BUGS: AUTHOR: Jerry Leichter [[ C Utility Library]] Page 1-59 talloc Threaded Storage Allocator Functions 1.44 Threaded Storage Allocator Functions ________ _______ _________ _________ ********** * talloc * ********** NAME: talloc -- Threaded Storage Allocator Functions SYNOPSIS: char * talloc(n) unsigned n; tfree(p) char *p; extern char *tmark; DESCRIPTION: talloc() is like malloc(); it allocates space. However, all this space is linked (threaded) together, and can be freed easily. Thus: here = tmark; a = talloc(5); b = talloc(5); ... tfree(here); The call to tfree() deletes a, b, and anything else allocated by talloc() between the point at which tmark was sampled and the call to tfree(). Like malloc(), talloc() returns NULL if it can't get you space. It is safe to save tmark more than once; for example, if a function knows its caller has saved tmark, it needn't ensure that it always frees its space after an error; the caller's tfree() will get rid of everything. talloc() calls malloc(), and malloc() can be used without interfering with talloc(); tfree() has no effect on space allocated directly with malloc(). Space allocated with talloc() must NEVER be passed to realloc() or free(); the pointers differ. [[ C Utility Library]] Page 1-60 talloc Threaded Storage Allocator Functions Needless to say, passing a value to tfree() that was not obtained from tmark or which is obsolete because a value obtained earlier was already passed will cause trouble. The overhead involved in using talloc() instead of malloc() is one (char *) per call. BUGS: AUTHOR: Jerry Leichter [[ C Utility Library]] Page 1-61 translate Character Translation Functions 1.45 Character Translation Functions _________ ___________ _________ ************* * translate * ************* NAME: translate -- Character Translation Functions SYNOPSIS: clrttab(t,max) char t[]; int max; setttab(t,from,to) char t[]; char *from; char *to; translate(t,s) char t[]; char *s; DESCRIPTION: These functions provide a character translation facility. translate() does the actual translation; clrttab() and setttab() set up a table for use with translate(). A translation table t[] is simple a byte array; translate() will map character c into t[c]. However, if t[c] is 0, it does not appear in the output - i.e., translating a character to null deletes it. t can be from 1 to 256 bytes in length. Only clrttab(), however, accepts the length of t as an argument; the other functions assume that any character they try to translate can be safely looked up in t. Usually, t is 128 bytes long and only legal ASCII characters, without parity, are passed, or 256 to support extended ASCII. clrttab(t,max) clears a table to the null translation - i.e., t will translate every character into itself. t is assumed to be max+1 bytes long (so that it can translate characters with values from 0 to max). setttab(t,from,to) modifies t so that characters in from are mapped into corresponding characters in to. If from is longer than to, any "excess" characters will map to 0, i.e., be deleted. Thus, the calls [[ C Utility Library]] Page 1-62 translate Character Translation Functions clrttab(t,127); setttab(t,"abcdewxyz","ABCDE"); create a table that maps a,b,c,d and e into their upper-case equivalents and deletes w,x,y and z. Note: It is meaningless to set up a translation for the 0 byte; t[0] is never used. translate(t,s) translates string s using table t. s itself is modified. s will be properly null-trailed, but may be shorter than it was as passed (if any of its characters are deleted). See also replace() for a simple way to use these functions. BUGS: AUTHOR: Jerry Leichter [[ C Utility Library]] Page 1-63 trim Trim Trailing Whitespace From a String 1.46 Trim Trailing Whitespace From a String ____ ________ __________ ____ _ ______ ******** * trim * ******** NAME: trim -- Trim Trailing Whitespace From a String SYNOPSIS: trim(s) char s[]; DESCRIPTION: trim() trims all trailing whitespace from the string passed. Note that the actual string passed is modified. "Whitespace-ness" is determined by the function isspace(). BUGS: AUTHOR: Jerry Leichter [[ C Utility Library]] Page 1-64 upto Pattern Match -- Match Up to Characters 1.47 Pattern Match -- Match Up to Characters _______ _____ __ _____ __ __ __________ ******** * upto * ******** NAME: upto -- Pattern Match -- Match Up to Characters SYNOPSIS: #ifdef vms #include "c:cset.h" #else #include #endif char * upto(s,cs) char s[]; CSET *cs; DESCRIPTION: upto() skips over characters in s until it reaches a member of cs, and returns a pointer to the matched character. If no matching character can be found, upto() returns NULL. upto() will accept the null string (returning s) if the first character of s is a member of cs. The null that terminates s is never considered to be a member of the cset, and thus upto() can never match through the end of s. Note: The effect of a upto() that will match all the way to the end of the string, if necessary, can be achieved by using: ospan(s,cscomp(cs)); Those familiar with SNOBOL will recognize upto() as the SNOBOL BREAK() function. Unfortunately, "break" is a reserved word in C. BUGS: AUTHOR: Jerry Leichter [[ C Utility Library]] Page 1-65 upto Pattern Match -- Match Up to Characters [[ C Utility Library]] Page 1-66 vstring Header file for vstrings 1.48 Header file for vstrings ______ ____ ___ ________ *********** * vstring * *********** NAME: vstring -- Header file for vstrings SYNOPSIS: #ifdef vms #include "c:vstrin.h" #else #include #endif DESCRIPTION: This header file is used to allow programs to access vstrings. See vstring.c for additional details. This file defines the structure of a vstring. The fields are: char *vsdata Pointer to the actual data. This will always be the first field, so that a vstring can be considered to be of type char ** if you only want to read it. vsdata will be NULL if the vstring has been damaged. unsigned vslen Number of bytes stored in the vstring. unsigned vsdim Total number of bytes there is room for. unsigned vsext Allocation quantum - the amount the vstring is to grow by if it fills. If vsext==0, the vstring cannot grow. The only fields generally directly accessed by programs are vsdata and vslen. Note that vsdata may change over time as items are added to and removed from the vstring. [[ C Utility Library]] Page 1-67 vstring Header file for vstrings The only field you should normally change is vsext, the extension quantum. The value current the next time the vstring must grow will determine how much it grows by. vstring.h also includes external declarations of the appropriate type for the functions in vstring.c. BUGS: A vstring is really just a special case of a flex with item size 1. It has been retained as a separate datatype because the type of operations done on strings are not always generalizable. For example, there is no obvious use for an fxadds(). AUTHOR: Jerry Leichter [[ C Utility Library]] Page 1-68 vstring Functions to Manipulate Vstrings 1.49 Functions to Manipulate Vstrings _________ __ __________ ________ *********** * vstring * *********** NAME: vstring -- Functions to Manipulate Vstrings SYNOPSIS: #ifdef vms #include "c:vstrin.h" #else #include #endif VSTRING * vstring(dim,ext) unsigned dim; unsigned ext; VSTRING * vsaddc(pv,c) VSTRING *pv; char c; VSTRING * vsadds(pv,s) VSTRING *pv; char *s; vsfree(pv) VSTRING *pv; DESCRIPTION: Vstrings are dynamically expandable strings. They have room for some fixed number of characters, but can grow, obtaining memory through malloc(), if necessary. vstring(dim,ext) returns a pointer to a new vstring, or NULL if there is no space for one. The vstring initially has room for dim bytes, and will grow by ext bytes if it fills. vsaddc() adds a character to a vstring. vsadds() adds a string to a vstring. It merely calls vsaddc() in a loop. The trailing EOS is NOT added. [[ C Utility Library]] Page 1-69 vstring Functions to Manipulate Vstrings Both vsaddc() and vsadds() return NULL if the vstring would have had to grow but couldn't due to lack of memory or a zero growth quantum. In this case, the data in the vstring is lost and the vstring itself is marked damaged and cannot be used again; further attempts always return NULL. If all went well, the vstring's address as passed is returned. It is occassionally necessary to recover the information in a damaged vstring (in most cases, there is no reasonable way to continue once the program runs out of memory). This can be done by making use of realloc()'s ability to reallocate the most recently freed block of core. Before making the potentially damaging call, save the current values of the vsdata and vsused elements of the vstring. Should the vstring become damaged, the vsdata value can be passed to realloc() safely. Note that this recovers the data, but not the vstring's header; you still cannot apply any of the vstring-handling functions to it. vsfree() frees a vstring. Attempting to free something that was not created with vstring() will cause trouble. However, NULL or a vstring that was filled and marked damaged may be safely passed. BUGS: A vstring is a special case of a flex which always has an item size of 1 byte. It is convenient to maintain it as a separate data type because the kinds of operations done on the two differ. For example, fxadd() takes the address of the item to add; vsaddc() takes the character itself, which is often more convenient. The basic set of functions provided for vstrings is more limited; perhaps more purely string-oriented functions that work on vstrings will be defined later. AUTHOR: Jerry Leichter APPENDIX A UTILITY LIBRARY INDEX The following is a keyword in context index to the Utility Library. The entry in the left-hand column is the title of the routine in the main Library documentation.