Pete Wilson : Consultant Software Engineer
Lowell MA 978.454.4547 pete at pwilson dot net April 2008
A work in progress: string4.c and string4.h
I'm very, very interested in your comments. Any kind of response -- constructive, flameful, whatever, I'll take 'em all. I'd especially like to hear how you'd improve this package.
To get the package: download (three files; cut & paste from here), build, and try. Works under *nix and Win32.
See code prologue.
Let me know how you make out, OK? Send me some email: pete at pwilson dot net on comp.unix.programmer
Thanks!
/* string4.h */
/* ***************************************************************************
*
* Copyright 1992-2005 by Pete Wilson All Rights Reserved
* 50 Staples Street : Lowell Massachusetts 01851 : USA
* http://www.pwilson.net/ pete at pwilson dot net +1 978-454-4547
*
* This item is free software: you can redistribute it and/or modify it as
* long as you preserve this copyright notice. Pete Wilson prepared this item
* hoping it might be useful, but it has NO WARRANTY WHATEVER, not even any
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
*************************************************************************** */
/* ***************************************************************************
*
* string4.h
*
* Functions to move/copy strings around more safely and robustly than
* strcpy()/strcat(), strncpy/strncat(), and strlcpy()/strlcat().
*
* Revision History:
*
* DATE VER DESCRIPTION
* ----------- ------ ----------------------------------------------------
* 26-oct-2002 1.00 intial
* 28-oct-2002 1.00 fixes per A. Coopersmith, M. Rochkind, B. Margolin.
* 29-oct-2002 1.00 redo strncpy/cat, add strcpy/cat per Ming Xu.
* 30-oct-2002 1.00 add overlay functions strovl4() and st4novl()
* 6-jan-2003 1.00 C99-compliant
* 7-jan-2003 1.00 temp return val char* / int acc to def of TYP
* 10-jan-2003 1.00 return: success = &dst[strlen(dst)], fail = &dst[0]
* 13-jan-2003 1.00 bug fixes; forget TYP, always ret char *
* 14-jan-2003 1.00 add string-shift: st4shiftl(), st4shiftr()
* 15-jan-2003 1.00 new call seq: dest args all first, #define st4d(d)
*
*************************************************************************** */
#ifdef __cplusplus
extern "C" {
#endif
#ifndef STRING4_H
#define STRING4_H
extern int st4errno;
extern size_t st4trunc; /* global: count of chars not moved */
#define MIN(a,b) (a <= b) ? a : b
#define ST4D(d) (char *)d,(size_t)sizeof(d),(char *)&d[0]
#define st4d(d) (char *)d,(size_t)sizeof(d),(char *)&d[0]
char * st4cpy(char * dst, /* leftmost dst char to write: &dst[n] */
size_t sizeof_dst, /* of entire array: sizeof(dst0) */
char * dst0, /* origin of destination array: &dst[0] */
char * src); /* copy starting here */
char * st4ncpy(char * dst, /* leftmost dst char to write: &dst[n] */
size_t sizeof_dst, /* of entire array: sizeof(dst0) */
char * dst0, /* origin of destination array: &dst[0] */
char * src, /* copy starting here */
size_t s_max); /* copy at most n characters */
char * st4cat(char * dst, /* origin of destination array: &dst[0] */
size_t sizeof_dst, /* of entire array: sizeof(dst0) */
char * dst0, /* origin of destination array: &dst[0] */
char * src);
char * st4ncat(char * dst, /* origin of destination array: &dst[0] */
size_t sizeof_dst, /* of entire array: sizeof(dst0) */
char * dst0, /* origin of destination array: &dst[0] */
char * src,
size_t s_max); /* cat at most n characters */
char * st4err (void);
char * st4err_n (int errno);
void st4perr (char * s);
void st4perr_n(char * s, int errno);
#define ST4_SUCCESS 0 /* complete successful copy */
#define ST4_SIZED0 0x0001 /* no copy: sizeof(dest) was 0 */
#define ST4_NULLDST 0x0002 /* no copy: dest ptr was NULL */
#define ST4_NULLSRC 0x0004 /* no copy: src ptr was NULL */
#define ST4_NULLDST0 0x0008 /* no copy: dst0 ptr was NULL */
#define ST4_HIDEST 0x0010 /* no copy: dst pointer beyond array end */
#define ST4_LODEST 0x0020 /* no copy: dst pointer less than array start */
#define ST4_DSTNTERM 0x0040 /* no cat: no NUL terminator in dst */
#define ST4_TRUNC 0x0080 /* source was truncated */
#define ST4_NOMEM 0x0100 /* no pad: malloc() failed */
#endif /* STRING4_H */
#ifdef __cplusplus
}
#endif
/* string4_globals.c */ #include <string.h> #include <limits.h> int st4errno; /* err num: != 0 fail, == 0 success */ size_t st4trunc; /* nchars truncated, not moved; 0 if all were moved */ char * g_dst; size_t g_sizeof_dst; char * g_dst0; size_t g_src_max; char * g_src; char * d_end; /* last char in destination array */ size_t d_free; /* space available in dest */ size_t s_trunc; /* nchars not copied to target */ char * d_nulp; /* terminating NUL in dst */
/* string4.c */
/* ***************************************************************************
*
* Copyright 1992-2003 by Pete Wilson All Rights Reserved
* 50 Staples Street : Lowell Massachusetts 01851 : USA
* http://www.pwilson.net/ pete at pwilson dot net +1 978-454-4547
*
* This item is free software: you can redistribute it and/or modify it as
* long as you preserve this copyright notice. Pete Wilson prepared this item
* hoping it might be useful, but it has NO WARRANTY WHATEVER, not even any
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
*************************************************************************** */
/* ***************************************************************************
*
* string4.c
*
* Functions to move/copy strings around more safely, reliably and
* much more predictably than
* strcpy()/strcat(), strncpy/strncat(), and strlcpy()/strlcat().
*
* Revision History:
*
* DATE VER DESCRIPTION
* ----------- ------ ----------------------------------------------------
* 26-oct-2002 1.00 intial
* 28-oct-2002 1.00 fixes per A. Coopersmith, M. Rochkind, B. Margolin.
* 29-oct-2002 1.00 redo strncpy/cat, add strcpy/cat per Ming Xu.
* 30-oct-2002 1.00 add overlay functions strovl4() and st4novl()
* 6-jan-2003 1.00 C99-compliant
* 7-jan-2003 1.00 temp return val char* / int acc to def of TYP
* 10-jan-2003 1.00 return: success = &dst[strlen(dst)], fail = &dst[0]
* 13-jan-2003 1.00 bug fixes; forget TYP, always ret char *
* 14-jan-2003 1.00 add string-shift: st4shiftl(), st4shiftr()
* 15-jan-2003 1.00 new call seq: dest args all first, #define st4d(d)
*
*************************************************************************** */
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h>
#include <string.h>
#include <limits.h>
#include "string4.h"
extern int st4errno; /* err num: != 0 fail, == 0 success */
extern size_t st4trunc; /* nchars truncated, not moved; 0 if all were moved */
extern char * g_dst;
extern size_t g_sizeof_dst;
extern char * g_dst0;
extern char * g_src;
extern size_t g_src_max;
extern char * d_end; /* last char in destination array */
extern size_t d_free; /* space available in dest */
extern size_t s_trunc; /* nchars not copied to target */
extern char * d_nulp; /* terminating NUL in dst */
/* local: get_count() returns count of chars to move, not incl NUL */
/* local: setup() moves args to globals, calcs bounds, checks
pointer and bounds errors, and returns 0 if OK, or error code if not */
static size_t get_count(void);
static int setup(int flags, char * dst, size_t sizeof_dst, char * dst0,
char * src, size_t src_max);
/* ***************************************************************************
*
* Functions: char * st4cpy(), st4ncpy()
*
* st4cpy( st4ncpy(
* char * dst, char * dst, // leftmost target char: &dst[n]
* size_t sizeof_dst, size_t sizeof_dst, // of entire array: sizeof(dst0)
* char * dst0, char * dst0, // origin of dest array: &dst[0]
* char * src); char * src, // copy-from starting here
* size_t src_max); // copy at most n characters
*
*
* Args: dst : ptr to first char in dst to overwrite; can be anywhere
* in the array dest0.
* sizeof_dst : size of the whole char array; used to find array bounds.
* dst0 : ptr to dest[0]; used to find array bounds.
* src : ptr to first char to copy from.
* src_max : max chars to copy.
*
* Action: Copy at most siz-1 chars from src to dst.
* Guarantee correct behavior on overlapping strings.
* Guarantee NUL-terminated dst.
*
* Errcode: extern int st4errno: <0, fail; 0, success; >0, truncated
* Returns: success = &dst[strlen(dst)], fail = dst
*
*************************************************************************** */
char *
st4cpy(char * dst, /* copy-to starting here: &dst[n] */
size_t sizeof_dst, /* of entire array: sizeof(dst0) */
char * dst0, /* origin of destination array: &dst[0] */
char * src) /* leftmost copy-from pointer in src */
{
size_t count = 0;
st4errno = 0; /* init results for caller */
st4trunc = 0;
st4errno = setup(0x07, dst, sizeof_dst, dst0, src, sizeof_dst-1);
if (st4errno == 0) /* if setup() found no error */
{
g_src_max = d_free;
count = get_count(); /* find nchars to move */
memmove(g_dst, g_src, count);
g_dst[count] = '\0'; /* always terminate destination */
if (0 != (st4trunc = s_trunc))
{
st4errno |= ST4_TRUNC; /* truncation, set error */
}
}
return &g_dst[count]; /* return ptr to final NUL in dst */
}
char *
st4ncpy(char * dst, /* leftmost copy-to: &dst[0...sizeof_dst-1] */
size_t sizeof_dst, /* of entire dest array: sizeof(dst0) */
char * dst0, /* origin of destination array: &dst[0] */
char * src, /* leftmost copy-from */
size_t src_max) /* copy at most src_max chars */
{
size_t count = 0;
st4errno = 0; /* init globals for caller */
st4trunc = src_max; /* total of chars that we could not copy */
if (src_max == 0)
{
return dst; /* no copy possible, take the shortcut */
}
st4errno = setup(0x07, dst, sizeof_dst, dst0, src, src_max);
if (st4errno == 0) /* if setup() found no error */
{
count = get_count(); /* total chars to move, not counting NUL */
memmove(g_dst, g_src, count);
g_dst[count] = '\0'; /* always terminate destination */
st4trunc = s_trunc; /* save total chars truncated and, if there */
if (0 != st4trunc) /* any truncated, tell the caller about it */
{
st4errno |= ST4_TRUNC;
}
}
return &g_dst[count]; /* point final NUL in dst; or dst[0] if error */
}
/* ***************************************************************************
*
* Functions: char * st4cat(), st4ncat()
*
* st4cat( st4ncat(
* char * dst, char * dst, // leftmost target char: &dst[n]
* size_t sizeof_dst, size_t sizeof_dst, // of entire array: sizeof(dst0)
* char * dst0, char * dst0, // origin of dest array: &dst[0]
* char * src); char * src, // copy-from starting here
* size_t src_max); // copy at most n characters
*
*
* Args: dst : ptr to first char in dst to overwrite; can be
* anywhere in the array dst0.
* sizeof_dst : size of the whole char array; used to find
* dst char-array bounds.
* dst0 : ptr to dst[0]; used to find array bounds.
* src : ptr to first char to copy from.
* src_max : max chars to copy.
*
* Action: Cat at most src_max chars from src to dst.
* Guarantee correct behavior on overlapping strings.
* Guarantee NUL-terminated dst.
*
* Errcode: extern int st4errno: sucess = 0, fail > 0
* Returns: success = &dst[strlen(dst)], fail = &dst[0]
*
*************************************************************************** */
char *
st4cat(char * dst, /* someplace (any place) in dst */
size_t sizeof_dst, /* of entire array: sizeof(dest0) */
char * dst0, /* origin of destination array: &dst[0] */
char * src) /* leftmost copy-from in src */
{
size_t count = 0;
st4errno = 0;
st4trunc = 0;
st4errno = setup(0x07, dst, sizeof_dst, dst0, src, sizeof_dst-1);
if (st4errno == 0)
{
if (NULL == d_nulp)
{ /* no NUL in dst, can't cat */
st4errno = ST4_DSTNTERM; /* complain to caller */
}
else
{
g_dst = d_nulp;
d_free = d_end - g_dst;
g_src_max = d_free;
count = get_count();
memmove(g_dst, g_src, count); /* start at NUL in dst */
g_dst[count] = '\0';
if (0 != (st4trunc = s_trunc))
{
st4errno |= ST4_TRUNC; /* truncation, set error */
}
}
}
return &g_dst[count]; /* ptr to ending NUL or original dst */
}
char *
st4ncat(char * dst, /* target array: any non-NULL, actually */
size_t sizeof_dst, /* sizeof(dst) */
char * dst0, /* &dst[0] */
char * src, /* leftmost copy-from pointer in src */
size_t src_max) /* cat at most src_max characters */
{
size_t count = 0;
st4errno = 0;
st4trunc = src_max;
if (src_max == 0) /* no cat possible, short-circuit */
{
return dst;
}
st4errno = setup(0x07, dst, sizeof_dst, dst0, src, src_max);
if (st4errno == 0)
{
if (NULL == d_nulp)
{ /* no NUL in dst, can't cat */
st4errno = ST4_DSTNTERM; /* complain to caller */
}
else
{
g_dst = d_nulp;
d_free = d_end - g_dst;
count = get_count();
memmove(g_dst, g_src, count); /* start at NUL in dst */
g_dst[count] = '\0';
if (0 != (st4trunc = s_trunc))
{
st4errno |= ST4_TRUNC; /* truncation, set error */
}
}
}
return &g_dst[count]; /* ptr to ending NUL or original dst */
}
/* experimental: st4rshift() and st4lshift() */
char *
st4shiftr(char * dst, /* start the shift here */
size_t sizeof_dst, /* size of entire array: sizeof(dst0) */
char * dst0, /* origin of destination array: &dst[0] */
size_t ns) /* shift right this many char positions */
{
char * rp;
rp = st4ncpy(dst+ns, sizeof_dst, dst0, dst, sizeof_dst-1);
return rp;
}
char *
st4shiftl(char * dst, /* start shift with this char */
size_t sizeof_dst, /* size of entire array: sizeof(dst0) */
char * dst0, /* origin of destination array: &dst[0] */
size_t ns) /* shift str left this many char positions */
{
char * rp;
rp = st4ncpy(dst-ns, sizeof_dst, dst0, dst, sizeof_dst-1);
return rp;
}
/* experimental: overlay 0 or more dst chars with 0 or more src chars */
char *
st4novl(char * dst, /* start overlaying chars here */
size_t sizeof_dst, /* size of entire array: sizeof(dst0) */
char * dst0,
size_t dst_max, /* overlay this many starting at dest */
char * src, /* write characters from here */
size_t src_max) /* using this many characters from src */
{
size_t count;
st4errno = 0;
st4trunc = src_max;
st4errno = setup(0x07, dst, sizeof_dst, dst0, src, src_max);
if (st4errno == 0)
{
count = get_count();
if (dst_max < count)
{
memmove(dst+count, dst, d_end-dst-count);
}
else
{
memmove(dst, dst+count, d_end-dst+count);
}
memmove(dst, src, count);
}
return dst;
}
char *
st4err(void)
{
return st4err_n(st4errno);
}
char *
st4err_n(int err)
{
#define base_er "no copy"
#define sized0 ", sizeof_dst=0"
#define nulldst ", dst=NULL"
#define nullsrc ", src=NULL"
#define nulldst0 ", dst0=NULL"
#define hidest ", dst>end"
#define lodest ", dst<start"
#define dstnterm ", dst unterm"
static char errmsg[ sizeof(base_er) -1 + sizeof(sized0) -1
+ sizeof(nulldst) -1 + sizeof(nullsrc) -1
+ sizeof(nulldst0)-1 + sizeof(hidest) -1
+ sizeof(lodest) -1 + sizeof(dstnterm) ];
if (err == ST4_SUCCESS)
{
return "success";
}
if (err & ST4_TRUNC)
{
sprintf(errmsg,"truncated upto %lu chars", (unsigned long) st4trunc);
return errmsg;
}
else
{
memcpy(errmsg, base_er, sizeof(base_er));
if (err & ST4_DSTNTERM)
memcpy(memchr(errmsg,'\0',sizeof(errmsg)),dstnterm,sizeof(dstnterm));
if (err & ST4_LODEST)
memcpy(memchr(errmsg,'\0',sizeof(errmsg)),lodest,sizeof(lodest));
if (err & ST4_HIDEST)
memcpy(memchr(errmsg,'\0',sizeof(errmsg)),hidest,sizeof(hidest));
if (err & ST4_NULLDST0)
memcpy(memchr(errmsg,'\0',sizeof(errmsg)),nulldst0,sizeof(nulldst0));
if (err & ST4_NULLSRC)
memcpy(memchr(errmsg,'\0',sizeof(errmsg)),nullsrc,sizeof(nullsrc));
if (err & ST4_NULLDST)
memcpy(memchr(errmsg,'\0',sizeof(errmsg)),nulldst,sizeof(nulldst));
if (err & ST4_SIZED0)
memcpy(memchr(errmsg,'\0',sizeof(errmsg)),sized0,sizeof(sized0));
}
return errmsg;
}
void
st4perr(char * s)
{
printf("%s%s\n", (s==NULL)?"":s, st4err_n(st4errno));
}
void
st4perr_n(char * s, int err)
{
printf("%s%s\n",(s==NULL)?"":s, st4err_n(err));
}
/* save caller's params, check sanity, and calc bounds if possible */
static int
setup(int flags, /* 3 bits 111: check dst(4),src(2),dst0(1) */
char * dst,
size_t sizeof_dst,
char * dst0,
char * src,
size_t src_max)
{
int errno = 0; /* retval, non-zero if find error here */
g_dst = dst; /* first target char */
g_sizeof_dst = sizeof_dst;
g_dst0 = dst0;
g_src = src;
g_src_max = src_max,
d_end = NULL; /* point last possible target */
d_free = 0; /* total writeable */
d_nulp = dst;
if (sizeof_dst == 0) /* sizeof(dst) must be greater than zero */
errno |= ST4_SIZED0;
/* caller, in flags param, tells us which pointers to check */
if (dst == NULL && (flags & 4))
errno |= ST4_NULLDST;
if (src == NULL && (flags & 2))
errno |= ST4_NULLSRC;
if (dst0 == NULL && (flags & 1))
errno |= ST4_NULLDST0;
/* if caller's pointers not NULL && sizeof_dst != 0,
then we can check bounds */
if (errno == 0)
{
d_end = dst0 + sizeof_dst - 1; /* point last possible target */
d_free = sizeof_dst-(dst-dst0)-1; /* total writeable */
if (dst > d_end) /* dst must be <= rightmost position */
errno |= ST4_HIDEST;
else if (dst < dst0) /* and dst must be >= dst0 */
errno |= ST4_LODEST;
else
d_nulp = memchr(dst, '\0', d_free+1);
}
return errno;
}
/*
get_count(), return (size_t) chars to move and set truncation count
s_trunc: 0 if can copy complete src, else nchars we cannot copy
*/
static size_t
get_count(void)
{
char * s; /* local src ptr */
size_t n; /* min of src/dst char count */
n = MIN(d_free, g_src_max);
s = memchr(g_src, '\0', n+1);
if (s != NULL) /* non-NULL, found src terminator */
{
s_trunc = 0; /* can copy all src chars without truncation */
return s-g_src;
}
else /* no NUL seen in src: must we truncate src? */
{ /* give max (not necessarily actual) truncated */
s_trunc = (g_src_max > d_free) ? g_src_max - n : 0;
#if 0
printf("src_max=%lu, d_free=%lu, n=%lu, trunc=%lu\n",
g_src_max,d_free,n,s_trunc);
#endif
return n; /* n could be zero here */
}
}
/* string4 man page (to come) */