From 31570910f0942393c5a3a63e29ab91dad3a93a63 Mon Sep 17 00:00:00 2001 From: d <> Date: Fri, 20 Nov 1998 11:18:51 +0000 Subject: [PATCH] Add thread-safety to libc, so that libc_r will build (on i386 at least). All POSIX libc api now there (to P1003.1c/D10) (more md stuff is needed for other libc/arch/*) (setlogin is no longer a special syscall) Add -pthread option to gcc (that makes it use -lc_r and -D_POSIX_THREADS). Doc some re-entrant routines Add libc_r to intro(3) dig() uses some libc srcs and an extra -I was needed there. Add more md stuff to libc_r. Update includes for the pthreads api Update libc_r TODO --- src/include/dirent.h | 3 ++- src/include/limits.h | 10 +++++++- src/include/signal.h | 3 ++- src/include/stdio.h | 39 +++++++++++++++++++++++++++++--- src/include/stdlib.h | 3 ++- src/include/string.h | 3 ++- src/include/time.h | 6 ++++- src/include/unistd.h | 4 +++- src/lib/libc/crypt/Makefile.inc | 4 ++-- src/lib/libc/hash/Makefile.inc | 4 ++-- src/lib/libc/stdlib/Makefile.inc | 2 +- src/lib/libc/stdlib/abort.c | 11 ++++++++- src/lib/libc/stdlib/exit.c | 11 ++++++++- src/lib/libc/stdlib/malloc.c | 30 ++++++++++++++++-------- src/lib/libc/stdlib/rand.3 | 19 +++++++++++++++- src/lib/libc/stdlib/rand.c | 12 ++++++++-- src/lib/libc/string/Makefile.inc | 14 ++++++------ 17 files changed, 142 insertions(+), 36 deletions(-) diff --git a/src/include/dirent.h b/src/include/dirent.h index 3962c412..6023a5db 100644 --- a/src/include/dirent.h +++ b/src/include/dirent.h @@ -1,4 +1,4 @@ -/* $OpenBSD: dirent.h,v 1.3 1997/09/21 10:45:30 niklas Exp $ */ +/* $OpenBSD: dirent.h,v 1.4 1998/11/20 11:18:25 d Exp $ */ /* $NetBSD: dirent.h,v 1.9 1995/03/26 20:13:37 jtc Exp $ */ /*- @@ -105,6 +105,7 @@ int scandir __P((const char *, struct dirent ***, int alphasort __P((const void *, const void *)); int getdirentries __P((int, char *, int, long *)); #endif /* not POSIX */ +int readdir_r __P((DIR *, struct dirent *, struct dirent **)); __END_DECLS #endif /* !_KERNEL */ diff --git a/src/include/limits.h b/src/include/limits.h index f9998eb7..772688e8 100644 --- a/src/include/limits.h +++ b/src/include/limits.h @@ -1,4 +1,4 @@ -/* $OpenBSD: limits.h,v 1.5 1998/07/11 06:02:44 deraadt Exp $ */ +/* $OpenBSD: limits.h,v 1.6 1998/11/20 11:18:25 d Exp $ */ /* $NetBSD: limits.h,v 1.7 1994/10/26 00:56:00 cgd Exp $ */ /* @@ -63,6 +63,10 @@ #define _POSIX2_LINE_MAX 2048 #define _POSIX2_RE_DUP_MAX 255 +/* P1003.1c */ +#define _POSIX_TTY_NAME_MAX 260 +#define _POSIX_LOGIN_NAME_MAX MAXLOGNAME + #if !defined(_POSIX_C_SOURCE) || defined(_XOPEN_SOURCE) #define PASS_MAX 128 @@ -78,6 +82,10 @@ #endif /* !_ANSI_SOURCE */ +/* where does this belong? it is defined by P1003.1c */ +#define TTY_NAME_MAX _POSIX_TTY_NAME_MAX +#define LOGIN_NAME_MAX MAXLOGNAME + #include #include diff --git a/src/include/signal.h b/src/include/signal.h index c2ab6cd8..82f39b14 100644 --- a/src/include/signal.h +++ b/src/include/signal.h @@ -1,4 +1,4 @@ -/* $OpenBSD: signal.h,v 1.3 1996/09/20 07:27:50 deraadt Exp $ */ +/* $OpenBSD: signal.h,v 1.4 1998/11/20 11:18:26 d Exp $ */ /* $NetBSD: signal.h,v 1.8 1996/02/29 00:04:57 jtc Exp $ */ /*- @@ -113,6 +113,7 @@ int sigstack __P((const struct sigstack *, struct sigstack *)); int sigaltstack __P((const struct sigaltstack *, struct sigaltstack *)); int sigvec __P((int, struct sigvec *, struct sigvec *)); void psignal __P((unsigned int, const char *)); +int sigwait __P((const sigset_t *, int *)); #endif /* !_POSIX_SOURCE */ #endif /* !_ANSI_SOURCE */ __END_DECLS diff --git a/src/include/stdio.h b/src/include/stdio.h index 3458c678..ed1fb32c 100644 --- a/src/include/stdio.h +++ b/src/include/stdio.h @@ -1,4 +1,4 @@ -/* $OpenBSD: stdio.h,v 1.9 1997/11/29 20:01:03 millert Exp $ */ +/* $OpenBSD: stdio.h,v 1.10 1998/11/20 11:18:26 d Exp $ */ /* $NetBSD: stdio.h,v 1.18 1996/04/25 18:29:21 jtc Exp $ */ /*- @@ -271,10 +271,31 @@ __END_DECLS __BEGIN_DECLS char *ctermid __P((char *)); +char *ctermid_r __P((char *)); char *cuserid __P((char *)); FILE *fdopen __P((int, const char *)); int fileno __P((FILE *)); +void flockfile __P((FILE *)); +int ftrylockfile __P((FILE *)); +void funlockfile __P((FILE *)); +void _flockfile_debug __P((FILE *, const char *, int)); +int getc_unlocked __P((FILE *)); +int putc_unlocked __P((int, FILE *)); +int getchar_unlocked __P((void)); +int putchar_unlocked __P((int)); __END_DECLS + +#ifndef _POSIX_THREADS +# define flockfile(fp) /* nothing */ +# define ftrylockfile(fp) (0) +# define funlockfile(fp) /* nothing */ +# define _flockfile_debug(fp,f,l) /* nothing */ +#endif + +#if 0 /* defined(DEBUG_FLOCKFILE) && defined(_POSIX_THREADS) */ +# define flockfile(fp) _flockfile_debug(fp, __FILE__, __LINE__) +#endif + #endif /* not ANSI */ /* @@ -368,23 +389,35 @@ static __inline int __sputc(int _c, FILE *_p) { #define feof(p) __sfeof(p) #define ferror(p) __sferror(p) + +#ifndef _POSIX_THREADS #define clearerr(p) __sclearerr(p) +#endif #ifndef _ANSI_SOURCE #define fileno(p) __sfileno(p) #endif #ifndef lint +#ifndef _POSIX_THREADS #define getc(fp) __sgetc(fp) +#endif /* _POSIX_THREADS */ +#define getc_unlocked(fp) __sgetc(fp) /* - * The macro implementation of putc is not fully POSIX - * compliant; it does not set errno on failure + * The macro implementations of putc and putc_unlocked are not + * fully POSIX compliant; they do not set errno on failure */ #ifndef _POSIX_SOURCE +#ifndef _POSIX_THREADS #define putc(x, fp) __sputc(x, fp) +#endif /* _POSIX_THREADS */ +#define putc_unlocked(x, fp) __sputc(x, fp) #endif /* _POSIX_SOURCE */ #endif /* lint */ #define getchar() getc(stdin) #define putchar(x) putc(x, stdout) +#define getchar_unlocked() getc_unlocked(stdin) +#define putchar_unlocked(c) putc_unlocked(c, stdout) + #endif /* _STDIO_H_ */ diff --git a/src/include/stdlib.h b/src/include/stdlib.h index 39aae35f..85e0da15 100644 --- a/src/include/stdlib.h +++ b/src/include/stdlib.h @@ -1,4 +1,4 @@ -/* $OpenBSD: stdlib.h,v 1.7 1998/02/07 02:16:26 millert Exp $ */ +/* $OpenBSD: stdlib.h,v 1.8 1998/11/20 11:18:26 d Exp $ */ /* $NetBSD: stdlib.h,v 1.25 1995/12/27 21:19:08 jtc Exp $ */ /*- @@ -105,6 +105,7 @@ void *malloc __P((size_t)); void qsort __P((void *, size_t, size_t, int (*)(const void *, const void *))); int rand __P((void)); +int rand_r __P((unsigned int *)); void *realloc __P((void *, size_t)); void srand __P((unsigned)); double strtod __P((const char *, char **)); diff --git a/src/include/string.h b/src/include/string.h index 42a2e1a4..d170174f 100644 --- a/src/include/string.h +++ b/src/include/string.h @@ -1,4 +1,4 @@ -/* $OpenBSD: string.h,v 1.4 1998/11/04 19:35:48 millert Exp $ */ +/* $OpenBSD: string.h,v 1.5 1998/11/20 11:18:26 d Exp $ */ /* $NetBSD: string.h,v 1.6 1994/10/26 00:56:30 cgd Exp $ */ /*- @@ -73,6 +73,7 @@ char *strrchr __P((const char *, int)); size_t strspn __P((const char *, const char *)); char *strstr __P((const char *, const char *)); char *strtok __P((char *, const char *)); +char *strtok_r __P((char *, const char *, char **)); size_t strxfrm __P((char *, const char *, size_t)); /* Nonstandard routines */ diff --git a/src/include/time.h b/src/include/time.h index 75cb1c52..88a1658a 100644 --- a/src/include/time.h +++ b/src/include/time.h @@ -1,4 +1,4 @@ -/* $OpenBSD: time.h,v 1.3 1998/02/08 18:50:05 deraadt Exp $ */ +/* $OpenBSD: time.h,v 1.4 1998/11/20 11:18:26 d Exp $ */ /* $NetBSD: time.h,v 1.9 1994/10/26 00:56:35 cgd Exp $ */ /* @@ -95,6 +95,10 @@ time_t mktime __P((struct tm *)); size_t strftime __P((char *, size_t, const char *, const struct tm *)); char *strptime __P((const char *, const char *, struct tm *)); time_t time __P((time_t *)); +char *asctime_r __P((const struct tm *, char *)); +char *ctime_r __P((const time_t *, char *)); +struct tm *gmtime_r __P((const time_t *, struct tm *)); +struct tm *localtime_r __P((const time_t *, struct tm *)); #if !defined(_ANSI_SOURCE) #define CLK_TCK 100 diff --git a/src/include/unistd.h b/src/include/unistd.h index 7a43d447..57f9bc53 100644 --- a/src/include/unistd.h +++ b/src/include/unistd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: unistd.h,v 1.23 1998/11/19 06:44:07 deraadt Exp $ */ +/* $OpenBSD: unistd.h,v 1.24 1998/11/20 11:18:26 d Exp $ */ /* $NetBSD: unistd.h,v 1.26.4.1 1996/05/28 02:31:51 mrg Exp $ */ /*- @@ -76,6 +76,7 @@ uid_t geteuid __P((void)); gid_t getgid __P((void)); int getgroups __P((int, gid_t *)); char *getlogin __P((void)); +int getlogin_r __P((char *, size_t)); pid_t getpgrp __P((void)); pid_t getpid __P((void)); pid_t getpgid __P((pid_t)); @@ -98,6 +99,7 @@ long sysconf __P((int)); pid_t tcgetpgrp __P((int)); int tcsetpgrp __P((int, pid_t)); char *ttyname __P((int)); +int ttyname_r __P((int, char *, size_t)); int unlink __P((const char *)); ssize_t write __P((int, const void *, size_t)); diff --git a/src/lib/libc/crypt/Makefile.inc b/src/lib/libc/crypt/Makefile.inc index 9d96d657..8364da1e 100644 --- a/src/lib/libc/crypt/Makefile.inc +++ b/src/lib/libc/crypt/Makefile.inc @@ -1,6 +1,6 @@ -# $OpenBSD: Makefile.inc,v 1.10 1998/07/21 22:23:20 provos Exp $ +# $OpenBSD: Makefile.inc,v 1.11 1998/11/20 11:18:33 d Exp $ -.PATH: ${.CURDIR}/arch/${MACHINE_ARCH}/crypt ${.CURDIR}/crypt +.PATH: ${LIBCSRCDIR}/arch/${MACHINE_ARCH}/crypt ${LIBCSRCDIR}/crypt SRCS+= cast.c crypt.c morecrypt.c md5crypt.c arc4random.c blowfish.c SRCS+= bcrypt.c diff --git a/src/lib/libc/hash/Makefile.inc b/src/lib/libc/hash/Makefile.inc index d198805f..e40edc01 100644 --- a/src/lib/libc/hash/Makefile.inc +++ b/src/lib/libc/hash/Makefile.inc @@ -1,7 +1,7 @@ -# $OpenBSD: Makefile.inc,v 1.5 1997/07/17 06:02:42 millert Exp $ +# $OpenBSD: Makefile.inc,v 1.6 1998/11/20 11:18:41 d Exp $ # hash functions -.PATH: ${.CURDIR}/hash +.PATH: ${LIBCSRCDIR}/hash SRCS+= sha1.c sha1hl.c rmd160.c rmd160hl.c MAN+= sha1.3 rmd160.3 diff --git a/src/lib/libc/stdlib/Makefile.inc b/src/lib/libc/stdlib/Makefile.inc index bdd5280d..3191f083 100644 --- a/src/lib/libc/stdlib/Makefile.inc +++ b/src/lib/libc/stdlib/Makefile.inc @@ -1,7 +1,7 @@ # $OpenBDS: Makefile.inc,v 1.6 1996/08/21 03:47:21 tholo Exp $ # stdlib sources -.PATH: ${.CURDIR}/arch/${MACHINE_ARCH}/stdlib ${.CURDIR}/stdlib +.PATH: ${LIBCSRCDIR}/arch/${MACHINE_ARCH}/stdlib ${LIBCSRCDIR}/stdlib SRCS+= a64l.c abort.c atexit.c atoi.c atof.c atol.c bsearch.c calloc.c \ cfree.c exit.c getenv.c getopt.c getsubopt.c heapsort.c l64a.c \ diff --git a/src/lib/libc/stdlib/abort.c b/src/lib/libc/stdlib/abort.c index 4ea8a2ca..4cc6257a 100644 --- a/src/lib/libc/stdlib/abort.c +++ b/src/lib/libc/stdlib/abort.c @@ -32,12 +32,13 @@ */ #if defined(LIBC_SCCS) && !defined(lint) -static char *rcsid = "$OpenBSD: abort.c,v 1.5 1997/06/22 20:21:25 tholo Exp $"; +static char *rcsid = "$OpenBSD: abort.c,v 1.6 1998/11/20 11:18:49 d Exp $"; #endif /* LIBC_SCCS and not lint */ #include #include #include +#include "thread_private.h" void (*__cleanup)(); @@ -54,7 +55,11 @@ abort() * any errors -- X311J doesn't allow abort to return anyway. */ sigdelset(&mask, SIGABRT); +#ifdef _THREAD_SAFE + (void)_thread_sys_sigprocmask(SIG_SETMASK, &mask, (sigset_t *)NULL); +#else _THREAD_SAFE (void)sigprocmask(SIG_SETMASK, &mask, (sigset_t *)NULL); +#endif _THREAD_SAFE /* * POSIX requires we flush stdio buffers on abort @@ -71,7 +76,11 @@ abort() * it again, only harder. */ (void)signal(SIGABRT, SIG_DFL); +#ifdef _THREAD_SAFE + (void)_thread_sys_sigprocmask(SIG_SETMASK, &mask, (sigset_t *)NULL); +#else _THREAD_SAFE (void)sigprocmask(SIG_SETMASK, &mask, (sigset_t *)NULL); +#endif _THREAD_SAFE (void)kill(getpid(), SIGABRT); exit(1); } diff --git a/src/lib/libc/stdlib/exit.c b/src/lib/libc/stdlib/exit.c index e358c94f..bc7fd395 100644 --- a/src/lib/libc/stdlib/exit.c +++ b/src/lib/libc/stdlib/exit.c @@ -32,7 +32,7 @@ */ #if defined(LIBC_SCCS) && !defined(lint) -static char *rcsid = "$OpenBSD: exit.c,v 1.2 1996/08/19 08:33:30 tholo Exp $"; +static char *rcsid = "$OpenBSD: exit.c,v 1.3 1998/11/20 11:18:50 d Exp $"; #endif /* LIBC_SCCS and not lint */ #include @@ -41,6 +41,15 @@ static char *rcsid = "$OpenBSD: exit.c,v 1.2 1996/08/19 08:33:30 tholo Exp $"; void (*__cleanup)(); +/* + * This variable is zero until a process has created a thread. + * It is used to avoid calling locking functions in libc when they + * are not required. By default, libc is intended to be(come) + * thread-safe, but without a (significant) penalty to non-threaded + * processes. + */ +int __isthreaded = 0; + /* * Exit, flushing stdio buffers if necessary. */ diff --git a/src/lib/libc/stdlib/malloc.c b/src/lib/libc/stdlib/malloc.c index d1d87597..ecbf93dc 100644 --- a/src/lib/libc/stdlib/malloc.c +++ b/src/lib/libc/stdlib/malloc.c @@ -8,7 +8,7 @@ */ #if defined(LIBC_SCCS) && !defined(lint) -static char rcsid[] = "$OpenBSD: malloc.c,v 1.32 1998/08/06 16:26:32 millert Exp $"; +static char rcsid[] = "$OpenBSD: malloc.c,v 1.33 1998/11/20 11:18:50 d Exp $"; #endif /* LIBC_SCCS and not lint */ /* @@ -87,15 +87,27 @@ static char rcsid[] = "$OpenBSD: malloc.c,v 1.32 1998/08/06 16:26:32 millert Exp #endif /* __OpenBSD__ */ #ifdef _THREAD_SAFE -#include -static pthread_mutex_t malloc_lock; -#define THREAD_LOCK() pthread_mutex_lock(&malloc_lock) -#define THREAD_UNLOCK() pthread_mutex_unlock(&malloc_lock) -#define THREAD_LOCK_INIT() pthread_mutex_init(&malloc_lock, 0); +# include "thread_private.h" +# if 0 + /* kernel threads */ +# include + static pthread_mutex_t malloc_lock; +# define THREAD_LOCK() pthread_mutex_lock(&malloc_lock) +# define THREAD_UNLOCK() pthread_mutex_unlock(&malloc_lock) +# define THREAD_LOCK_INIT() pthread_mutex_init(&malloc_lock, 0); +# else + /* user threads */ +# include "spinlock.h" + static spinlock_t malloc_lock = _SPINLOCK_INITIALIZER; +# define THREAD_LOCK() if (__isthreaded) _SPINLOCK(&malloc_lock) +# define THREAD_UNLOCK() if (__isthreaded) _SPINUNLOCK(&malloc_lock) +# define THREAD_LOCK_INIT() +# endif #else -#define THREAD_LOCK() -#define THREAD_UNLOCK() -#define THREAD_LOCK_INIT() + /* no threads */ +# define THREAD_LOCK() +# define THREAD_UNLOCK() +# define THREAD_LOCK_INIT() #endif /* diff --git a/src/lib/libc/stdlib/rand.3 b/src/lib/libc/stdlib/rand.3 index 32d32761..28496ec1 100644 --- a/src/lib/libc/stdlib/rand.3 +++ b/src/lib/libc/stdlib/rand.3 @@ -33,7 +33,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $OpenBSD: rand.3,v 1.4 1998/07/05 19:54:22 millert Exp $ +.\" $OpenBSD: rand.3,v 1.5 1998/11/20 11:18:50 d Exp $ .\" .Dd June 29, 1991 .Dt RAND 3 @@ -48,6 +48,8 @@ .Fn srand "unsigned seed" .Ft int .Fn rand void +.Ft int +.Fn rand_r "unsigned int *seed" .Sh DESCRIPTION .Bf -symbolic These interfaces are obsoleted by @@ -73,6 +75,14 @@ with the same seed value. .Pp If no seed value is provided, the functions are automatically seeded with a value of 1. +.Pp +The +.Fn rand_r +is a thread-safe version of +.Fn rand . +Storage for the seed must be provided through the +.Ar seed +argument, and needs to have been initialized by the caller. .Sh SEE ALSO .Xr arc4random 3 , .Xr rand48 3 , @@ -85,3 +95,10 @@ and functions conform to .St -ansiC . +.Pp +The +.Fn rand_r +function +conforms to ISO/IEC 9945-1 ANSI/IEEE +.Pq Dq Tn POSIX +Std 1003.1c Draft 10. diff --git a/src/lib/libc/stdlib/rand.c b/src/lib/libc/stdlib/rand.c index f270ffd9..61fb66e5 100644 --- a/src/lib/libc/stdlib/rand.c +++ b/src/lib/libc/stdlib/rand.c @@ -32,7 +32,7 @@ */ #if defined(LIBC_SCCS) && !defined(lint) -static char *rcsid = "$OpenBSD: rand.c,v 1.2 1996/08/19 08:33:44 tholo Exp $"; +static char *rcsid = "$OpenBSD: rand.c,v 1.3 1998/11/20 11:18:50 d Exp $"; #endif /* LIBC_SCCS and not lint */ #include @@ -40,10 +40,18 @@ static char *rcsid = "$OpenBSD: rand.c,v 1.2 1996/08/19 08:33:44 tholo Exp $"; static u_long next = 1; +int +rand_r(seed) +u_int *seed; +{ + *seed = *seed * 1103515245 + 12345; + return (u_int)(*seed / 65536) % ((u_int)RAND_MAX + 1); +} + int rand() { - return ((next = next * 1103515245 + 12345) % ((u_int)RAND_MAX + 1)); + return rand_r(&next); } void diff --git a/src/lib/libc/string/Makefile.inc b/src/lib/libc/string/Makefile.inc index 076db789..78eee7ef 100644 --- a/src/lib/libc/string/Makefile.inc +++ b/src/lib/libc/string/Makefile.inc @@ -1,7 +1,7 @@ -# $OpenBSD: Makefile.inc,v 1.5 1998/07/01 01:29:44 millert Exp $ +# $OpenBSD: Makefile.inc,v 1.6 1998/11/20 11:18:51 d Exp $ # string sources -.PATH: ${.CURDIR}/arch/${MACHINE_ARCH}/string ${.CURDIR}/string +.PATH: ${LIBCSRCDIR}/arch/${MACHINE_ARCH}/string ${LIBCSRCDIR}/string SRCS+= bm.c memccpy.c strcasecmp.c strcoll.c strdup.c strerror.c \ strlcat.c strlcpy.c strmode.c strsignal.c strtok.c strxfrm.c \ @@ -16,7 +16,7 @@ SRCS+= bm.c memccpy.c strcasecmp.c strcoll.c strdup.c strerror.c \ # m-d Makefile.inc may include sources for: # memcpy() memmove() strchr() strrchr() -.include "${.CURDIR}/arch/${MACHINE_ARCH}/string/Makefile.inc" +.include "${LIBCSRCDIR}/arch/${MACHINE_ARCH}/string/Makefile.inc" # if no machine specific memmove(3), build one out of bcopy(3). .if empty(SRCS:Mmemmove.S) @@ -100,19 +100,19 @@ LOBJS+= memmove.ln memcpy.ln strchr.ln strrchr.ln memmove.ln: bcopy.c lint ${LINTFLAGS} -DMEMMOVE ${CFLAGS:M-[IDU]*} -i -o ${.TARGET} \ - ${.CURDIR}/string/bcopy.c + ${LIBCSRCDIR}/string/bcopy.c memcpy.ln: bcopy.c lint ${LINTFLAGS} -DMEMCOPY ${CFLAGS:M-[IDU]*} -i -o ${.TARGET} \ - ${.CURDIR}/string/bcopy.c + ${LIBCSRCDIR}/string/bcopy.c strchr.ln: index.c lint ${LINTFLAGS} -DSTRCHR ${CFLAGS:M-[IDU]*} -i -o ${.TARGET} \ - ${.CURDIR}/string/index.c + ${LIBCSRCDIR}/string/index.c strrchr.ln: rindex.c lint ${LINTFLAGS} -DSTRRCHR ${CFLAGS:M-[IDU]*} -i -o ${.TARGET} \ - ${.CURDIR}/string/rindex.c + ${LIBCSRCDIR}/string/rindex.c MAN+= bm.3 bcmp.3 bcopy.3 bstring.3 bzero.3 ffs.3 index.3 memccpy.3 memchr.3 \ memcmp.3 memcpy.3 memmove.3 memset.3 rindex.3 strcasecmp.3 strcat.3 \