[Introduction]

Unix Incompatibility Notes:
String and Memory Functions

Jan Wolter

This page describes portability issues related to various Unix string and memory block manipulation functions. It is incomplete.

I suggest using something like Gnu's autoconf package to test for the existance of various functions before compiling and define symbols like HAVE_STRCHR if the functions exist.

The string functions normally need the string.h header file. Some older version use strings.h instead, but the easiest thing is usually to just provide your own function prototypes if string.h isn't available.

I've supplied implementations of some of the functions that your system may be missing. They are mostly not very efficient and should only be used if nothing is available from the system libraries, since the system libraries should usually be better. All of the C code on this page is public domain and may be used without concern for licenses. Some was contributed by Dan Cross.

bcopy()
This is the BSD version of the System V memcpy(). This has been depreciated so memcpy() should be the prefered choice these days.

bzero()
Like bcopy() this is obsolete. memset() should be prefered.

index(), rindex()
Obsolete. Prefer strchr() and strrchr() if available.

memcpy(), memccpy(), memchr(), memset()
These System V functions, together with memcmp(), seem to travel together - if you have one, you have them all. They are pretty standard now, but are missing on some older BSD systems. In many cases, the bcopy() functions can be substituted. Note that on some systems memcpy() does not work if the source and destination overlap. Note also that it's arguments are backward compared to strcpy() and bcopy(). Many pre-ISO C compilers declared memchr() as returning a "char *" pointer instead of a "void *" pointer.

memcmp()
You probably have this if you have the other memory functions. Some versions of this (SunOS 4.1.3) are said not to work correctly on eight-bit data.

memmove()
The memmove() function is a bit rarer than the other memory functions - some systems that have the others don't have this. It is identical to memcpy() but is guaranteed to work even if the strings overlap. Most systems that don't have memmove() do have the BSD bcopy() though some really old systems have neither. Note that bcopy() has the order of the source and destination arguments reversed. Here's an implementation that could be used where memmove() is not available:
    #ifndef HAVE_MEMMOVE
    char *memmove(char *dst, char *src, int n)
    {
        if (src > dst)
             for ( ; n > 0; n--)
                 *(dst++)= *(src++);
        else
             for (dst+= n-1, src+= n-1; n > 0; n--)
                 *(dst--)= *(src--);
    }
    #endif

nstrcasecmp(), strncasecmp()
Many older systems don't have these. Here's an strcasecmp() implementation:
    #ifndef HAVE_STRCASECMP
    #define ccmp(a) ((a) == (b) ? 0 : ((a) > (b) ? 1 : -1))
    int strcasecmp(unsigned char *s1, unsigned char *s2)
    {
        unsigned char c1, c2;
        for ( ; ; )
        {
           if (*s1 == '\0' || *s2 == '\0')
                return ccmp(*s1,*s2);
            c1= (isascii(*s1) && isupper(*s1)) ? tolower(*s1) : *s1;
            c2= (isascii(*s2) && isupper(*s2)) ? tolower(*s2) : *s2;
            if (c1 != c2)
                return ccmp(c1,c2);
            s1++;
            s2++;
        }
    }
    #undef ccmp
    #endif

strcat(), strncat()
Available everywhere.

strchr(), strrchr()
On older BSD systems, you need to use index() and rindex() instead of these. These differ from the Sys V versions only in name, so you can be completely safe doing:
   #ifndef HAVE_STRCHR
   # define strchr(a,b) index(a,b)
   # define strrchr(a,b) rindex(a,b)
   #endif
Any system that doesn't have strchr(), will have index().

strcmp(), strncmp()
Available everywhere.

strlcpy(), strlcat()
Largely unique to OpenBSD and a few others. However, these are sufficiently a good idea, that it may be wise to include a copy of the OpenBSD source in your distribution for use on non-OpenBSD systems.

Dan Cross has provided the following simple public domain implementation of strlcpy():

    #ifndef HAVE_STRLCPY
    size_t strlcpy(char *dst, const char *src, size_t size)
    {
        size_t  len, srclen;
        srclen = strlen(src);
        if (--size <= 0) return(srclen);
        len = (size < srclen) ? size : srclen;
        memmove(dst, src, len);
        dst[len] = '\0';
        return(srclen);
    }
    #endif

strcpy(), strncpy()
Available everywhere.

strdup(), strndup()
Many older systems are missing strdup() but it is ubiquitous today. The strndup() variant is a GNU extension and not very portable. Luckily, it is easy to write your own versions:
  #ifndef HAVE_STRDUP
  char *strdup(char *str)
  {
      char *dup= (char *)malloc( strlen(str)+1 );
      if (dup) strcpy(dup,str);
      return dup;
   }

  #ifndef HAVE_STRNDUP
  char *strndup(char *str, size_t len)
  {
      char *dup= (char *)malloc( len+1 );
      if (dup) {
          strncpy(dup,str,len);
          dup[len]= '\0';
      }
      return dup;
   }
   #endif

strerror()
Many older systems don't have this, but have the global sys_errlist array instead. I think a good substitute for strerror() is
   #ifndef HAVE_STRERROR
   char *strerror(int errno)
   {
   extern int sys_nerr;
   extern char *sys_errlist[];

      if (errno < 0 || errno > sys_nerr)
           return "Unknown Error";
      else
           return sys_errlist[errno];
   }
   #endif

strlen(), strnlen()
The strlen() function is available everywhere. The strnlen() function is a GNU extension. The version in AIX 4.3 is badly broken. The following is a substitute for strnlen():
   #ifndef HAVE_STRNLEN
   size_t strnlen(char *s, size_t maxlen)
   {
	size_t i;

	for (i= 0; i < maxlen && *s != '\0'; i++, s++)
	    ;
	return i;
   }
   #endif

strpbrk()
In POSIX and ISO standards, but does not exist on all systems that predate those standards.

strsep()
Available on most modern systems, but not in the standards. Dan Cross provides the following implementation, which, however, requires strcspn(), itself not all that portable:
    #ifndef HAVE_STRSEP
    char * strsep(char **sp, char *sep)
    {
        char *p, *s;
        if (sp == NULL || *sp == NULL || **sp == '\0') return(NULL);
        s = *sp;
        p = s + strcspn(s, sep);
        if (*p != '\0') *p++ = '\0';
        *sp = p;
        return(s);
    }
    #endif

strspn(),strcspn()
These defined by POSIX and ISO standards, but do not exist on all systems that predate those standards.

strstr(),strcasestr(),strnstr()
Strstr() is missing on many systems. Strcasestr() is rare and Strnstr() is probably unique to FreeBSD. The following substitute for strstr() is rather inefficient, but should work:
    #ifndef HAVE_STRSTR
    char *strstr(s,p)
    char *s, *p;
    {
        char *sp, *pp;
        for(sp= s, pp= p; *sp && *pp; )
        {
            if (*sp == *pp)
            {
                sp++;
                pp++;
            }
            else
            {
                sp= sp - (pp - p) + 1;
                pp= p;
            }
        }
        return (*pp ? NULL : sp-(pp-p));
    }
    #endif
Dan Cross provided the following reimplemention of strcasestr() which, unfortunately, relies on the availability of strcspn(), and strncasecmp:
    #ifndef HAVE_STRCASESTR
    char *strcasestr(char *a, char *b)
    {
        size_t l;
        char f[3];

        snprintf(f, sizeof(f), "%c%c", tolower(*b), toupper(*b));
        for (l = strcspn(a, f); l != strlen(a); l += strcspn(a + l + 1, f) + 1)
            if (strncasecmp(a + l, b, strlen(b)) == 0)
                return(a + l);
        return(NULL);
    }
    #endif

strtok()
This is defined by POSIX and ISO standards, but do not exist on all systems that predate those standards.


Jan Wolter (E-Mail)
Sat Apr 15 14:43:10 EDT 2000 - Original Release.
Fri Jul 30 10:46:51 EDT 2004 - Corrections and added functions from Dan Cross.
Sat Nov 24 19:19:54 EST 2012 - Corrections from Guillem Jover