Pete Wilson : Consultant Software Engineer

Lowell MA   978.454.4547   pete at pwilson dot net    April 2008

 Home/Resume  
 Sample code 
 Experimental 
 SNMP 
 CGI notes 
 CSS notes 
 Standards 
 Books 
  Virus Alerts  
 wildcards  
 getopt() 
 safe strings 
 kbhit() 
 cli 
 snmp 

Safe string cat and cpy (16 jan 03)

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.

To use


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!

Everything starting here and to the bottom of the page is pure C code


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