Select Git revision
Forked from
Nettle / nettle
Source project has a limited visibility.
s-string.c 22.80 KiB
/*
* $Id: s-string.c,v 1.20 1998/07/08 13:57:47 ceder Exp $
* Copyright (C) 1990, 1991, 1992, 1993, 1994, 1995, 1996 Lysator Academic Computer Association.
*
* This file is part of the LysKOM server.
*
* LysKOM is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* LysKOM is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License
* along with LysKOM; see the file COPYING. If not, write to
* Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
* or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
* MA 02139, USA.
*
* Please mail bug reports to bug-lyskom@lysator.liu.se.
*/
/*
* s-string.c -- Routines for manipulating objects of type String.
*
*
* Copyright (C) 1990, 1991, 1992, 1993, 1994, 1995, 1996 Lysator Computer Club,
* Linkoping University, Sweden
*
* Everyone is granted permission to copy, modify and redistribute
* this code, provided the people they give it to can.
*
*
* Author: Thomas Bellman
* Lysator Computer Club
* Linkoping University
* Sweden
*
* email: Bellman@Lysator.LiU.SE
*
*
* Any opinions expressed in this code are the author's PERSONAL opinions,
* and does NOT, repeat NOT, represent any official standpoint of Lysator,
* even if so stated.
*/
static const char *
rcsid = "$Id: s-string.c,v 1.20 1998/07/08 13:57:47 ceder Exp $";
#include <sys/types.h>
#include <stdio.h>
#include <ctype.h>
#include <assert.h>
#include <limits.h>
#ifdef HAVE_STDDEF_H
# include <stddef.h>
#endif
#if STDC_HEADERS || HAVE_STRING_H
# include <string.h>
# if !STDC_HEADERS && HAVE_MEMORY_H
# include <memory.h>
# endif
#else /* not STDC_HEADERS and not HAVE_STRING_H */
# include <strings.h>
/* memory.h and strings.h conflict on some systems. */
#endif /* not STDC_HEADERS and not HAVE_STRING_H */
#include "misc-types.h"
#include "s-collat-tabs.h"
#include "s-string.h"
#include "rcs.h"
USE(rcsid);
#define EXPORT /* To emphasize objects that are exported */
/* The empty String */
EXPORT const String EMPTY_STRING = EMPTY_STRING_i;
static free_function x_free = NULL;
static malloc_function x_malloc = NULL;
static realloc_function x_realloc = NULL;
/*
* Functions to manipulate strings of type 'String'.
* All these functions return a value of type Success, which is
* an enum of OK an FAILURE (do NOT trust the order of these!).
*
* All functions automatically allocates (and deallocates) any
* necessary storage, using the storage management functions
* defined by the s_set_storage_management() call.
*/
/* Macro to reallocate new storage. Works even if PTR is a
* nil pointer. */
#define REALLOC_0(ptr, size) (((ptr) == NULL) \
? MALLOC_0 (size) \
: (*x_realloc) ((ptr), size))
/* Free storage. Works even if PTR is a nil pointer. */
#define FREE_0(ptr) (((ptr) == NULL) \
? 0 \
: ((*x_free) (ptr), ptr))
#define MALLOC_0(size) ((*x_malloc) (size))
/*
* Set the functions to use for storage management. These must
* be call compatible with the normal functions malloc(),
* realloc() and free().
*/
EXPORT void
s_set_storage_management (malloc_function new_malloc,
realloc_function new_realloc,
free_function new_free)
{
x_malloc = new_malloc;
x_realloc = new_realloc;
x_free = new_free;
}
/*
* Create an object of type String from an ordinary C string.
*/
EXPORT Success
s_crea_str (String * dest_string,
const char * c_string )
{
String_size length;
void * temp_ptr; /* To hold result from malloc/realloc
* before actually using it. */
length = strlen(c_string);
temp_ptr = MALLOC_0 (length);
if (temp_ptr == NULL)
{
return FAILURE;
}
FREE_0 (dest_string->string);
dest_string->string = temp_ptr;
strncpy((char *)dest_string->string, c_string, length);
dest_string->len = length;
return OK;
}
/*
* Create a string from a buffer. The contents of the buffer
* are copied into the new string.
*/
EXPORT Success
s_mem_crea_str (String * dest_string,
const unsigned char * buffer,
String_size length)
{
void * temp_ptr; /* To hold result from malloc/realloc
* before actually using it. */
temp_ptr = MALLOC_0 (length);
if (temp_ptr == NULL)
{
return FAILURE;
}
FREE_0 (dest_string->string);
dest_string->string = temp_ptr;
memcpy(dest_string->string, buffer, length);
dest_string->len = length;
return OK;
}
/*
* Create a string of a given size. The contents of the string
* are unspecified. The LysKOM-server uses this to get a string
* of a fixed size into which it can fread() data. This is probably
* not a good idea since it relies heavily on the implementation
* of strings. However, by using this function, those places are
* easy to identify if the implementation should be done differently.
*/
EXPORT Success
s_size_crea_str(String *result,
String_size length)
{
void * temp_ptr; /* To hold result from malloc/realloc
* before actually using it. */
temp_ptr = MALLOC_0 (length);
if (temp_ptr == NULL)
{
return FAILURE;
}
FREE_0 (result->string);
result->string = temp_ptr;
result->len = length;
return OK;
}
/*
* Return a temporary String from a C string, i e return a struct
* pointing to the actual C string. Suitable for sending a 'String'
* object as parameter to a function (IF that function does not modify
* its parameter). Note that normally String variables should be set
* by s_crea_str(), to deallocate the memory used by it.
*/
EXPORT String
s_fcrea_str (const char * c_string)
{
String temp;
/* Cast needed to make compiler not warn about assignment from
* const pointer to non-const pointer. Sigh... */
temp.string = (unsigned char *)c_string;
temp.len = strlen(c_string);
return temp;
}
/*
* Free's the space used by a String object. The object is
* set to EMPTY_STRING.
*/
EXPORT void
s_clear (String * str)
{
FREE_0 (str->string);
*str = EMPTY_STRING;
}
/*
* Copy SOURCE_STRING to DEST_STRING. Old value of DEST_STRING is
* retained if an error was encountered. If 'foo' is a separate
* String, then it is legal to do 's_strcpy(&foo, foo)'.
*/
EXPORT Success
s_strcpy (String * dest_string,
const String source_string)
{
void * temp_ptr; /* To hold result from malloc/realloc
* before actually using it. */
if (s_strlen(source_string) == 0)
{
s_clear (dest_string);
return OK;
}
/* (Re-)allocate memory */
temp_ptr = REALLOC_0 (dest_string->string, s_strlen(source_string));
if (temp_ptr == NULL)
return FAILURE;
dest_string->string = temp_ptr;
/* Copy the string */
memcpy (dest_string->string,
source_string.string,
s_strlen (source_string));
dest_string->len = s_strlen (source_string);
return OK;
}
/*
* In String STR remove the characters starting with number FIRST
* and ending with number LAST, inclusive. If FIRST > LAST, then
* no characters are removed.
*/
extern Success
s_strdel (String * str,
String_size first,
String_size last )
{
String_size i; /* Just an index in a loop */
assert ( first >= 0 && first <= s_strlen (*str)
&& last >= -1 && last < s_strlen (*str));
for ( i = last + 1 ; i < s_strlen(*str) ; i++ )
str->string [i-(last-first+1)] = str->string [i];
str->len -= last-first+1;
return OK;
}
/*
* Append SOURCE_STRING to the end of DEST_STRING. DEST_STRING is not
* changed if an error is encountered.
*/
EXPORT Success
s_strcat (String * dest_string,
const String source_string)
{
void * temp_ptr; /* To hold result from malloc/realloc
* before actually using it. */
/* (Re-)alloc space for new string */
temp_ptr = REALLOC_0 (dest_string->string,
dest_string->len + source_string.len);
if (temp_ptr == NULL)
{
return FAILURE;
}
dest_string->string = temp_ptr;
/* Append SOURCE_STRING to *DEST_STRING */
memcpy (dest_string->string + s_strlen (*dest_string),
source_string.string,
s_strlen (source_string));
dest_string->len += source_string.len;
return OK;
}
/*
* Extract a substring from SOURCE_STRING, starting with char no
* START_CHAR and ending with char no END_CHAR. First character
* is character no 0. If END_CHAR < START_CHAR, then DEST_STRING
* is set to 'EMPTY_STRING'. If END_CHAR is equal to the macro
* 'END_OF_STRING', then the substring reaches to the last character
* in SOURCE_STRING.
*/
EXPORT Success
s_substr (String * dest_string,
const String source_string,
String_size start_char,
String_size end_char)
{
String_size sub_len; /* Length of substring */
void * temp_ptr; /* To hold result from malloc/realloc
* before really using it. */
if (end_char == END_OF_STRING)
end_char = s_strlen(source_string)-1;
assert ( start_char >= 0 && end_char >= -1
&& start_char <= s_strlen (source_string)
&& end_char < s_strlen (source_string));
sub_len = end_char - start_char + 1;
/* Will the substring be empty? */
if (sub_len <= 0)
{
s_clear (dest_string); /* Free and set to EMPTY_STRING */
return OK;
}
/* (Re-)allocate space for substring */
temp_ptr = REALLOC_0(dest_string->string, sub_len);
if (temp_ptr == NULL)
{
return FAILURE;
}
dest_string->string = temp_ptr;
/* Copy substring to DEST_STRING */
memcpy (dest_string->string,
source_string.string + start_char,
sub_len);
dest_string->len = sub_len;
return OK;
}
/*
* Fast extraction of a substring. Returns an object of type
* String wich points into SOURCE_STRING. Thus you should NEVER
* modify the result of this function. (Raw character modifying
* is OK if you know what you are doing, but never use anything
* that might call free or realloc on it.) If END_CHAR is equal
* to the macro 'END_OF_STRING', then the substring reaches to
* the last character in SOURCE_STRING.
*/
EXPORT String
s_fsubstr (const String source_string,
String_size start_char,
String_size end_char )
{
String sub_string; /* Substring struct */
if (end_char == END_OF_STRING)
end_char = s_strlen(source_string)-1;
assert ( start_char >= 0 && end_char >= -1
&& start_char <= s_strlen (source_string)
&& end_char < s_strlen (source_string));
sub_string.len = end_char - start_char + 1;
/* Is the substring empty? */
if (sub_string.len < 1)
{
return EMPTY_STRING;
}
sub_string.string = source_string.string + start_char;
return sub_string;
}
/*
* Returns -1 if ARG is negative, 0 if 0 and +1 if positive.
* Used in the string comparison routines.
*/
static int
sign (int arg)
{
if (arg < 0)
return -1;
else if (arg == 0)
return 0;
else
return 1;
}
/*
* Compares two strings. Returns -1 if STR1 is lexically less
* than STR2, +1 if STR1 is greater than STR2, and 0 if they are
* equal.
*/
EXPORT int
s_strcmp (String str1,
String str2)
{
String_size index;
String_size shortest; /* Length of the shortest string */
int retval_based_on_lengths;
retval_based_on_lengths = sign(str1.len - str2.len);
if (str1.len < str2.len)
shortest = str1.len;
else
shortest = str2.len;
/* If they point to the same string, then we only have to
* compare the lengths. */
if (str1.string == str2.string)
return retval_based_on_lengths;
/* Find first diff:ing character (in [index]) */
index = 0;
while ( (index < shortest)
&& (str1.string[index] == str2.string[index]) )
index++;
/* If no diff:ing char, then the shortest is the "smallest" */
if (index >= shortest)
return retval_based_on_lengths;
else /* ...else check which character was the "smallest". */
return sign(str1.string[index] - str2.string[index]);
}
/*
* Makes INDEX (type char) positive. Those that are negative,
* result in a positive number above the other numbers. This
* works as if bitcopying a 2's complement nuber to an unsigned
* number.
*
* NOTE: This code might need to be modified for some architectures.
*/
#define POS_INDEX(index) ((unsigned char)(index))
/*
* Compares two strings with user specified collation order.
* Returns the same values as s_srcmp().
* COLLAT_TAB is a table of collating values for every char.
*/
EXPORT int
s_usr_strcmp (String str1,
String str2,
char * collat_tab)
{
String_size index;
String_size shortest; /* Length of the shortest string */
int retval_based_on_lengths;
retval_based_on_lengths = sign(str1.len - str2.len);
if (str1.len < str2.len)
shortest = str1.len;
else
shortest = str2.len;
/* If they point to the same string, then we only have to
* compare the lengths. */
if (str1.string == str2.string)
return retval_based_on_lengths;
/* Find first diff:ing character (in [index-1]) */
index = 0;
while ( (index < shortest)
&& ( collat_tab [ POS_INDEX( str1.string [index] )]
== collat_tab [ POS_INDEX( str2.string [index] )]) )
index++;
/* If no diff:ing char, then the shortest is the "smallest" */
if (index >= shortest)
return retval_based_on_lengths;
/* ...else check which character was the "smallest". */
else
return sign( collat_tab [ POS_INDEX( str1.string[index] )]
- collat_tab [ POS_INDEX( str2.string[index] )] );
}
/*
* Checks if STR1 is the exact beginning of STR2, e g if STR1
* is "foobar" and STR2 is "foobarf" then the result is TRUE.
*/
EXPORT Bool
s_strhead (String str1,
String str2)
{
String_size i; /* Index in comparison loop */
/* If STR1 is longer than STR2, then it can't be a head. */
if (s_strlen(str1) > s_strlen(str2))
return FALSE;
/* If they point to the same string, then STR1 is a head
* of STR2. (We have already compared the lengths.) */
if (str1.string == str2.string)
return TRUE;
/* Check character by character */
for ( i = 0 ; i < s_strlen(str1) ; i++)
if (str1.string[i] != str2.string[i])
return FALSE; /* Diff. found, so not head. */
/* No differences found */
return TRUE;
}
/*
* Checks if STR1 is the exact beginning of STR2, but uses a
* user specified collating sequence for comparison (as in
* s_usr_strcmp()).
*/
EXPORT Bool
s_usr_strhead (String str1,
String str2,
char collat_tab [ COLLAT_TAB_SIZE ])
{
String_size i; /* Index in comparison loop */
/* If STR1 is longer than STR2, then it can't be a head. */
if (s_strlen(str1) > s_strlen(str2))
return FALSE;
/* If they point to the same string, then STR1 is a head
* of STR2. (We have already compared the lengths.) */
if (str1.string == str2.string)
return TRUE;
/* Check character by character */
for ( i = 0 ; i < s_strlen(str1) ; i++)
{
if ( collat_tab [ POS_INDEX( str1.string[i] )]
!= collat_tab [ POS_INDEX( str2.string[i] )] )
{
return FALSE; /* Diff. found, so not head. */
}
}
/* No differences found */
return TRUE;
}
/*
* From STR strip all trailing characters that can be found
* in STRIP_STRING.
*/
extern String
s_strip_trailing (String str,
const String strip_string)
{
while ( s_strlen (str) > 0
&& s_strchr (strip_string,
str.string[s_strlen(str) - 1], 0) != -1)
str.len--;
return str;
}
/*
* Returns the index of the first occurrence in the String STR
* of the character CH, starting at position STAR_POS. Returns
* -1 if no occurrence.
*/
EXPORT String_size
s_strchr (const String str,
unsigned char ch,
String_size start_pos)
{
unsigned char * ptr;
assert (start_pos >= 0 && start_pos <= s_strlen (str));
ptr = memchr (str.string + start_pos, ch, s_strlen (str) - start_pos);
if (ptr == NULL)
return -1;
else
return ptr - str.string;
}
/*
* Returns the index of the last occurrence in the String STR
* of the character CH, starting at position START_POS. Returns
* -1 if no occurrence.
*/
EXPORT String_size
s_strrchr (const String str,
unsigned char ch,
String_size start_pos)
{
String_size index;
if (start_pos == END_OF_STRING)
start_pos = s_strlen (str) - 1;
assert (start_pos >= -1 && start_pos < s_strlen (str));
index = start_pos;
while (index >= 0 && str.string[index] != ch)
index--;
return index; /* This will be -1 if not found. */
}
/*
* Return the index of the first occurrence in the String LOOK_IN
* of any of the characters in the String SEARCH_FOR, or -1 if none.
*/
extern String_size
s_strpbrk (const String look_in,
const String search_for)
{
String_size i;
for ( i = 0 ; i < s_strlen (look_in) ; i++ )
if (s_strchr (search_for, look_in.string[i], 0) != -1)
return i;
return -1;
}
/*
* Find the first character in LOOK_IN that *is* present in
* SEARCH_FOR, and return its index, or the length of LOOK_IN if
* it contains only characters that are not present in SEARCH_FOR.
*/
extern String_size
s_strcspn (const String look_in,
const String search_for)
{
String_size i;
i = 0;
while ( i < s_strlen (look_in)
&& s_strchr (search_for, look_in.string[i], 0) == -1)
i++;
return i;
}
/*
* Find the first character in LOOK_IN that is *not* present in
* SKIP_CHARS, and return its index, or the length of LOOK_IN if
* it contains only characters that are present in SKIP_CHARS.
*/
extern String_size
s_strspn (const String look_in,
const String skip_chars)
{
String_size i;
i = 0;
while ( i < s_strlen (look_in)
&& s_strchr (skip_chars, look_in.string[i], 0) != -1)
i++;
return i;
}
/*
* Pick out the first token from SOURCE separated by one or more
* of the characters in SEPARATORS, starting in position START_POS.
*
* More specifically: start in position START_POS and skip over
* separator characters (any of those present in SEPARATORS).
* Extract the substring starting with the first non-separator,
* and ending the character immediately before the first separator
* following. *start_pos will be the index of the first separator
* character after the token.
*
* Note that the return value actually points into SOURCE. It
* is not separately allocated.
*/
EXPORT String
s_strtok (const String source,
String_size * start_pos,
const String separators)
{
String_size first_char; /* First character in token */
String_size end_char; /* First character after token */
/* Check of parameters - we might save some time on this */
if ( (*start_pos >= s_strlen(source))
|| ((s_empty(separators) == TRUE)
|| (s_empty(source) == TRUE)) )
{
return EMPTY_STRING;
}
/* Skip leading separators */
first_char = *start_pos;
while ( (first_char < s_strlen(source))
&& (s_strchr (separators, source.string[first_char], 0) != -1) )
first_char++;
/* End of source string? Then we can stop here. */
if (first_char >= s_strlen(source))
{
*start_pos = first_char;
return EMPTY_STRING;
}
/* Find next separator */
end_char = first_char;
while ( (end_char < s_strlen(source))
&& (s_strchr (separators, source.string[end_char], 0) == -1) )
end_char++;
/* OK, we're practically done. */
*start_pos = end_char;
return s_fsubstr(source, first_char, end_char-1);
}
/****************************************************
* Misc. routines using our String type. *
****************************************************/
/*
* Convert a char to a number in base BASE.
*/
static int
char2digit (const char ch,
const int base)
{
int index;
static const char * translate_table =
"0123456789abcdefghijklmnopqrstuvwxyz";
int c = (unsigned char)ch;
index = 0;
while ( (index < base)
&& ( translate_table[index]
!= (isalpha(c) ? tolower(c) : ch)))
index++;
if (index >= base)
return -1;
else
return index;
}
/*
* Convert the String STR to a long, using the base BASE.
* Leading blanks are skipped according to isblank() in <ctype.h>.
* The index of the first character that couldn't be used to form
* the number is returned in *FIRST_ILL_CHAR. Returns -1 in
* *first_ill_char if there was an error in the parameters.
* BASE may be in the range 2..36
*/
#define MAXBASE 36
EXPORT long
s_strtol (const String str,
String_size * first_ill_char,
const int base )
{
long number = 0; /* The result */
int sign = 0; /* Sign o' the times :-) */
String_size char_no;
int digit;
assert (base >= 2 && base <= MAXBASE);
if (s_empty(str))
{
*first_ill_char = -1;
return 0;
}
/* Skip all blanks */
char_no = 0;
while ( (char_no < s_strlen(str)) && isspace(str.string[char_no]) )
char_no++;
/* Find any sign character (+ or -) */
if (str.string[char_no] == '+')
{
sign = 1;
char_no++;
}
else if (str.string[char_no] == '-')
{
sign = -1;
char_no++;
}
while ( (char_no < s_strlen(str))
&& ((digit = char2digit(str.string[char_no], base)) != -1) )
{
number = number * base + digit;
char_no++;
}
*first_ill_char = char_no++;
if (sign != -1)
return number;
else
return -number;
}
/************************************************
* Input/Output routines for String *
************************************************/
/*
* Outputs the string STR on the stream STREAM. No information
* about the length of the string is output.
*/
EXPORT Success
s_fputs (FILE * stream,
const String str)
{
String_size i;
for ( i = 0 ; i < str.len ; i++ )
putc (str.string[i], stream);
return OK;
}
/*
* Create an ordinary C string from a String.
* The pointer returned will point to a '\0'-terminated character
* array which is obtained with the malloc-function supplied to
* s_set_storage_management(). It should be freed by the caller.
* NULL is returned if there is an error. The String may contain
* '\0's, but the resulting string will be truncated at the first
* '\0'. Thou shalt not depend on this behaviour. Later versions
* might substitute "foobar" for '\0's.
*/
EXPORT char *
s_crea_c_str(const String source)
{
char *dest;
dest = MALLOC_0( 1 + s_strlen(source) );
if ( dest == NULL )
return NULL;
memcpy(dest, source.string, s_strlen(source));
dest[ s_strlen(source) ] = '\0';
return dest;
}