[Pure C] Why memcpy/strcpy/strcat return a value?

Many noob C programmer wonders, why strcat/strcpy return a value, if it's the same, as the 'destination' operand? Isn't it a bit redundant?

char *strcpy(char *dest, const char *src);
char *strcat(char *dest, const char *src);
void *memcpy(void *dest, const void *src, size_t n);

Mainly for chaining.

At the end of the day, this is used for string construction, without sprintf() and libraries like strbuf (I wrote about it in the C/C++ programming language notes).

This is the best macro I've seen so far. It allocate a buffer for three strings, concatenate them all and return a pointer to it:

#define COMBINETHREESTRINGS( FirstString, SecondString, ThirdString) strcat( strcat( strcpy( _alloca( strlen( FirstString) + strlen( SecondString) + strlen( ThirdString) + 1), FirstString), SecondString), ThirdString)

( WinNT-related-src/leaked-src/XPSP1/sdktools/compdir/compdir.c )

Add file extension:

        szFileWSP = strcat( strcpy(szFileWSP , szBaseName ), ".WSP" );
...
        szFileTMI = strcat( strcpy( szFileTMI, szBaseName ), ".TMI" );

( WinNT-related-src/leaked-src/Win2K3/sdktools/wst/wstune/wsreduce.c )

Concatenate 3 strings:

                strcat(strcat(strcpy(rName.Ptr(), pNS), NAMESPACE_SEPARATOR_STR), pName);
...
                strcat(strcat(strcpy(rName.Ptr(), pNS), NAMESPACE_SEPARATOR_STR), pclsname);
...
            strcat(strcat(strcpy(rName.Ptr(), pNS), NAMESPACE_SEPARATOR_STR), pclsname);

( WinNT-related-src/leaked-src/Win2K3/com/netfx/src/clr/vm/tlbexport.cpp )

Construct a string and return the newly constructed buffer:

STATIC char *Cat1(char *s0, char *s1) {
  return strcat(strcat(Cat0(s0), "\t"), s1);
} /* Cat1 */

STATIC char *Cat2(char *s0, char *s1, char *s2) {
  return strcat(strcat(Cat1(s0, s1), ", "), s2);
} /* Cat2 */

STATIC char *Cat3(char *s0, char *s1, char *s2, char *s3) {
  return strcat(strcat(Cat2(s0, s1, s2), ", "), s3);
} /* Cat3 */

( WinNT-related-src/leaked-src/Win2K3/base/mvdm/wow16/sherlock/disasm.c )

// operator+= -- CoolString concatenation of another string: x += y;
// Input:        CoolString reference
// Output:       CoolString object concatenated with CoolString contents

inline CoolString& CoolString::operator+= (const CoolString& s) {
  return (strcat (*this, s));
}

( https://github.com/open-watcom/open-watcom-v2/blob/master/bld/plustest/cool/source/string.h )

Add file extensions:

    size = strlen( fileprefix);
    codefilename   = MALLOC( size + 6, char );
    strcat( strcpy( codefilename, fileprefix), "tab.c" );
    headerfilename = MALLOC( size + 6, char );
    strcat( strcpy( headerfilename, fileprefix), "tab.h" );
    descfilename   = MALLOC( size + 5, char );
    strcat( strcpy( descfilename, fileprefix), ".out" );
    actout = openw( codefilename );
    defs( actout );
    tokout = openw( headerfilename );
    dump_header( tokout );

( https://github.com/open-watcom/open-watcom-v2/blob/master/bld/yacc/c/yacc.c )

Construct a string for environment:

  env[0] = strcat(strcpy(env0, "TZ="), val);

( glibc/glibc-2.26/timezone/zdump.c )

Construct a string and pass it as an argument:

        if( FileNameWild( argv[i], rxflag ) ) {
            dirp = NULL;
        } else if( IsSpecialRoot( argv[i] ) ) {
            dirp = opendir( strcat( strcpy( filebuff, argv[i] ), "\\" ) );
        } else {
            dirp = opendir( argv[i] );
        }

( https://github.com/open-watcom/open-watcom-v2/blob/master/bld/posix/src/ls/ls.c )

        GetLine(rgb, strcat(strcpy(sbPrompt,GetMsg(P_defprompt)), " [nul.def]: "));

( WinNT-related-src/leaked-src/Win2K3/sdktools/link16/newcmd.c )

Construct a long buffer:

    // FEXTRA
    *output_curpos++ = 3; // LSB
    *output_curpos++ = 0; // MSB
    output_curpos += 3; // 3 bytes of data

    // FNAME, null terminated filename
    output_curpos += strlen(strcpy(output_curpos, "my filename"))+1;

    // FCOMMENT, null terminated comment
    output_curpos += strlen(strcpy(output_curpos, "my comment"))+1;

( WinNT-related-src/leaked-src/XPSP1/inetsrv/iis/svcs/w3/filters/compress/gzip/gzip.c )

Construct a long string:

            for (n = 0; n < 7; ++n) {
                pn->wday_abbr[n] = s;
                s += strlen(strcpy(s, pt->wday_abbr[n])) + 1;
                pn->wday[n] = s;
                s += strlen(strcpy(s, pt->wday[n])) + 1;
            }
            for (n = 0; n < 12; ++n) {
                pn->month_abbr[n] = s;
                s += strlen(strcpy(s, pt->month_abbr[n])) + 1;
                pn->month[n] = s;
                s += strlen(strcpy(s, pt->month[n])) + 1;
            }

( Win2K3/base/crts/crtw32/time/strftime.c )

Copy string and get its length at the same line:

            rc = strlen(strncpy(outputString, "Unable to load msvcrt!__unDName", maxStringLength));

( WinNT-related-src/leaked-src/2000/private/sdktools/imagehlp/imagehlp.c )

Nested strcat/strcpy, pass the result to another function:

    if (nameopt(strcat(strcat(strcpy(to, dir), "/"), from)) < 0) {

( QNX/trunk/utils/p/pax_qnx/pass.c )

Make copy of string and 'upper' it:

        strupr( strcpy( node[i].fname, fname ) );

( https://github.com/open-watcom/open-watcom-v2/blob/master/bld/sdk/imgedit/c/ieopen.c )

Make copy of a string and do something with it:

    slashify(strcpy(cmd, s), p);

( QNX/trunk/utils/g/gawk/pc/popen.c )

Allocate and copy buffer. This is the same as memdup() does:

   PWSTR attrName = (PWSTR)memcpy(_alloca(len), newVal, len);

( WinNT-related-src/leaked-src/XPSP1/net/ias/providers/nap/cond/match.cpp )

/*
 * We mark any token, that that equals to a known enumerator, as
 * SYM_ENUM_CONST. The parser will change this for struct and union tags later,
 * the only problem is struct and union members:
 *    enum e { a, b }; struct s { int a, b; }
 * but in this case, the only effect will be, that the ABI checksums become
 * more volatile, which is acceptable. Also, such collisions are quite rare,
 * so far it was only observed in include/linux/telephony.h.
 */
#define _APP(T,L)	do {						   \
			  cur_node = next_node;				   \
			  next_node = xmalloc(sizeof(*next_node));	   \
			  next_node->next = cur_node;			   \
			  cur_node->string = memcpy(xmalloc(L+1), T, L+1); \
			  cur_node->tag =				   \
			    find_symbol(cur_node->string, SYM_ENUM_CONST, 1)?\
			    SYM_ENUM_CONST : SYM_NORMAL ;		   \
			  cur_node->in_source_file = in_source_file;       \
			} while (0)

( https://github.com/torvalds/linux/blob/master/scripts/genksyms/lex.l )

Allocate and copy string. This is the same as strdup() does:

	return strcpy(alloc(strlen(str)+1), str);

( id/quake3-1.32b/lcc/etc/lcc.c )

Allocate string, construct it and return it, in the same line:

        return(strcpy(alloc((unsigned)(strlen(string)+1)),string));

( WinNT-related-src/leaked-src/Win2K3/sdktools/vi/alloc.c )

How memdup() and strdup() can be implemented?

#define smbestrdup(p) strcpy(malloc(strlen(p) + 1), p)

( plan9/extr/sys/src/cmd/aquarela/smbfns.h )

#   define zstrdup( x ) strcpy( zmalloc( strlen(x)+1 ), x )

( WinNT-related-src/leaked-src/XPSP1/shell/osshell/accesory/ratpak/ratpak.h )

/* Clone an object P of size S, with error checking.  There's no need
   for xnmemdup (P, N, S), since xmemdup (P, N * S) works without any
   need for an arithmetic overflow check.  */

void *
xmemdup (void const *p, size_t s)
{
  return memcpy (xmalloc (s), p, s);
}

/* Clone STRING.  */

char *
xstrdup (char const *string)
{
  return xmemdup (string, strlen (string) + 1);
}

( QNX/trunk/ports/tar/lib/xmalloc.c )


Comments at Reddit, at lobste.rs


List of my other blog posts.