brucee@runx.ips.oz (Bruce Evans) (05/15/88)
The 'strn' functions in the Minix 1.2 library *all* diverge from the V7 standard. I think that means they are broken. strncpy( char *t, char *s, int n ) ------- Supposed to copy *exactly* n characters, truncating or *null padding* as necessary. The target need *not* be null-terminated. Actually copies at most (n + 1) characters and doesn't null pad more than once. Always null terminates the string, thereby copying 1 too many in some cases. Treats zero counts as 64K and negative counts as unsigned. (I've always thought counts should be unsigned, but that's not the standard). strncat( char *t, char *s, int n ) ------- Supposed to copy *at most* n characters. The target need *not* be null-terminated. Always null terminates the string, thereby copying 1 too many in some cases. Treats zero count correctly but negative counts as unsigned. strncmp( char *s1, char *s2, int n ) ------- Supposed to compare at most n characters. Treats zero count as 64K and negative counts as unsigned. --- I can easily post correct versions (in C and 2+ different non-Minix assembly languages yet), but I'm more worried about the correctness of the library generally, since there is nothing much easier or better documented than strncpy. (Perhaps the precise documentation is the problem?) (:-). Bruce Evans Internet: brucee@runx.ips.oz.au UUCP: uunet!runx.ips.oz.au!brucee
brucee@runx.ips.oz (Bruce Evans) (05/22/88)
Here are my versions of the 'strn' library functions, to fix the problems mentioned in my previous posting. I said I could 'easily' post corrected versions, but decided to test them carefully first and spent a long time on it when a bug showed up. To avoid unnecessary complications, I decided not to post the assembler versions. I said that the old 'strncat' sometimes does an incorrect null termination. This is wrong, since the null is effectively the one moved from the end of the initial target string. There is some confusion about what the type of the count arguments are. The functions here treat them as ints and not unsigneds, according to Unix Programmer's Manual 1983,1979 (V7) C A Reference Manual (Harbison and Steele, 1984) but for practical DOS compilers Turbo C V1.0: arguments are declared as int but act as unsigned (wrong) Microsoft C V4.0: arguments are declared and act as unsigned and Unofficial ANSI C standard: arguments are size_t (unsigned for Minix). So it looks like the new standard is that the count arguments are unsigned. This brings in a new set of problems, as lint will quite rightly complain about millions of old programs which pass int parameters. # This is a shell archive. Remove anything before this line, # then unpack it by saving it in a file and typing "sh file". # # Wrapped by brucee on Sun May 22 23:44:57 GMT+10:00 1988 # Contents: strncat.c strncmp.c strncpy.c echo x - strncat.c sed 's/^@//' > "strncat.c" <<'@//E*O*F strncat.c//' /* strncat.c - char *strncat( char *s1, char *s2, int n ) Strncat appends up to n characters from s2 to the end of s1. It returns s1. */ char *strncat( s1, s2, n ) char *s1; register char *s2; int n; { register char *rs1; rs1 = s1; if ( n > 0 ) { while ( *rs1++ != 0 ) ; --rs1; while ( (*rs1++ = *s2++) != 0 ) if ( --n == 0 ) { *rs1 = 0; break; } } return s1; } @//E*O*F strncat.c// chmod u=rw,g=,o= strncat.c echo x - strncmp.c sed 's/^@//' > "strncmp.c" <<'@//E*O*F strncmp.c//' /* strncmp.c - int strncmp( char *s1, char *s2, int n ) Strcmp compares s1 to s2, up to at most n characters (lexicographically with native character comparison). It returns positive if s1 > s2 zero if s1 = s2 negative if s1 < s2. */ int strncmp( s1, s2, n ) register char *s1; register char *s2; int n; { if ( n <= 0 ) return 0; while ( *s1++ == *s2++ ) if ( s1[-1] == 0 || --n == 0 ) return 0; return s1[-1] - s2[-1]; } @//E*O*F strncmp.c// chmod u=rw,g=,o= strncmp.c echo x - strncpy.c sed 's/^@//' > "strncpy.c" <<'@//E*O*F strncpy.c//' /* strncpy.c - char *strncpy( char *s1, char *s2, int n ) Strncpy writes exactly n (or 0 if n < 0) characters to s1. It copies up to n characters from s2, and null-pads the rest. The result is null terminated iff strlen( s2 ) < n. It returns the target string. */ char *strncpy( s1, s2, n ) char *s1; register char *s2; int n; { register char *rs1; rs1 = s1; if ( n > 0 ) { while ( (*rs1++ = *s2++) != 0 && --n != 0 ) ; if ( n != 0 ) while ( --n != 0 ) *rs1++ = 0; } return s1; } @//E*O*F strncpy.c// chmod u=rw,g=,o= strncpy.c exit 0 Bruce Evans Internet: brucee@runx.ips.oz.au UUCP: uunet!runx.ips.oz.au!brucee
henry@utzoo.uucp (Henry Spencer) (05/26/88)
People tinkering with the string functions might want to note the version I contributed to comp.sources.unix quite a while ago. It's highly portable, although generally not optimal for a specific machine. More to the point, though, is that it includes a 650-line test program that turns all the functions upside-down and shakes vigorously, in hopes that bugs will fall out. You may want the test program even if you don't want the portable string functions (which are a complete SysV/BSD/X3J11 set, although they are not up-to-date with recent changes to X3J11). -- "For perfect safety... sit on a fence| Henry Spencer @ U of Toronto Zoology and watch the birds." --Wilbur Wright| {ihnp4,decvax,uunet!mnetor}!utzoo!henry