Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/tcl8.5.2/unix/tclUnixCompat.c @ 52

Last change on this file since 52 was 25, checked in by landauf, 17 years ago

added tcl to libs

File size: 17.9 KB
Line 
1/*
2 * tclUnixCompat.c
3 *
4 * Written by: Zoran Vasiljevic (vasiljevic@users.sourceforge.net).
5 *
6 * See the file "license.terms" for information on usage and redistribution of
7 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
8 *
9 * RCS: @(#) $Id: tclUnixCompat.c,v 1.15 2008/02/28 20:14:12 jenglish Exp $
10 *
11 */
12
13#include "tclInt.h"
14#include <pwd.h>
15#include <grp.h>
16#include <errno.h>
17#include <string.h>
18
19/* See also: SC_BLOCKING_STYLE in unix/tcl.m4
20 */
21#ifdef  USE_FIONBIO
22#   ifdef HAVE_SYS_FILIO_H
23#       include <sys/filio.h>   /* For FIONBIO. */
24#   endif
25#   ifdef HAVE_SYS_IOCTL_H
26#       include <sys/ioctl.h>
27#   endif
28#endif  /* USE_FIONBIO */
29
30/*
31 *---------------------------------------------------------------------------
32 *
33 * TclUnixSetBlockingMode --
34 *
35 *      Set the blocking mode of a file descriptor.
36 *
37 * Results:
38 *
39 *      0 on success, -1 (with errno set) on error.
40 *
41 *---------------------------------------------------------------------------
42 */
43int
44TclUnixSetBlockingMode(
45    int fd,             /* File descriptor */
46    int mode)           /* TCL_MODE_BLOCKING or TCL_MODE_NONBLOCKING */
47{
48#ifndef USE_FIONBIO
49    int flags = fcntl(fd, F_GETFL);
50
51    if (mode == TCL_MODE_BLOCKING) {
52        flags &= ~O_NONBLOCK;
53    } else {
54        flags |= O_NONBLOCK;
55    }
56    return fcntl(fd, F_SETFL, flags);
57#else /* USE_FIONBIO */
58    int state = (mode == TCL_MODE_NONBLOCKING);
59    return ioctl(fd, FIONBIO, &state);
60#endif /* !USE_FIONBIO */
61}
62
63/*
64 * Used to pad structures at size'd boundaries
65 *
66 * This macro assumes that the pointer 'buffer' was created from an aligned
67 * pointer by adding the 'length'. If this 'length' was not a multiple of the
68 * 'size' the result is unaligned and PadBuffer corrects both the pointer,
69 * _and_ the 'length'. The latter means that future increments of 'buffer' by
70 * 'length' stay aligned.
71 */
72
73#define PadBuffer(buffer, length, size)             \
74    if (((length) % (size))) {                      \
75        (buffer) += ((size) - ((length) % (size))); \
76        (length) += ((size) - ((length) % (size))); \
77    }
78
79/*
80 * Per-thread private storage used to store values returned from MT-unsafe
81 * library calls.
82 */
83
84#ifdef TCL_THREADS
85
86typedef struct ThreadSpecificData {
87    struct passwd pwd;
88    char pbuf[2048];
89
90    struct group grp;
91    char gbuf[2048];
92
93#if !defined(HAVE_MTSAFE_GETHOSTBYNAME) || !defined(HAVE_MTSAFE_GETHOSTBYADDR)
94    struct hostent hent;
95    char hbuf[2048];
96#endif
97}  ThreadSpecificData;
98static Tcl_ThreadDataKey dataKey;
99
100#if ((!defined(HAVE_GETHOSTBYNAME_R) || !defined(HAVE_GETHOSTBYADDR_R)) && \
101     (!defined(HAVE_MTSAFE_GETHOSTBYNAME) || \
102      !defined(HAVE_MTSAFE_GETHOSTBYADDR))) || \
103      !defined(HAVE_GETPWNAM_R) || !defined(HAVE_GETPWUID_R) || \
104      !defined(HAVE_GETGRNAM_R) || !defined(HAVE_GETGRGID_R)
105/*
106 * Mutex to lock access to MT-unsafe calls. This is just to protect our own
107 * usage. It does not protect us from others calling the same functions
108 * without (or using some different) lock.
109 */
110
111static Tcl_Mutex compatLock;
112
113/*
114 * Helper function declarations. Note that these are only used if needed and
115 * only defined if used (via the NEED_* macros).
116 */
117
118#undef NEED_COPYARRAY
119#undef NEED_COPYGRP
120#undef NEED_COPYHOSTENT
121#undef NEED_COPYPWD
122#undef NEED_COPYSTRING
123
124static int              CopyArray(char **src, int elsize, char *buf,
125                            int buflen);
126static int              CopyGrp(struct group *tgtPtr, char *buf, int buflen);
127static int              CopyHostent(struct hostent *tgtPtr, char *buf,
128                            int buflen);
129static int              CopyPwd(struct passwd *tgtPtr, char *buf, int buflen);
130static int              CopyString(char *src, char *buf, int buflen);
131
132#endif
133#endif /* TCL_THREADS */
134
135/*
136 *---------------------------------------------------------------------------
137 *
138 * TclpGetPwNam --
139 *
140 *      Thread-safe wrappers for getpwnam(). See "man getpwnam" for more
141 *      details.
142 *
143 * Results:
144 *      Pointer to struct passwd on success or NULL on error.
145 *
146 * Side effects:
147 *      None.
148 *
149 *---------------------------------------------------------------------------
150 */
151
152struct passwd *
153TclpGetPwNam(
154    const char *name)
155{
156#if !defined(TCL_THREADS)
157    return getpwnam(name);
158#else
159    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
160
161#if defined(HAVE_GETPWNAM_R_5)
162    struct passwd *pwPtr = NULL;
163
164    return (getpwnam_r(name, &tsdPtr->pwd, tsdPtr->pbuf, sizeof(tsdPtr->pbuf),
165                       &pwPtr) == 0 && pwPtr != NULL) ? &tsdPtr->pwd : NULL;
166
167#elif defined(HAVE_GETPWNAM_R_4)
168    return getpwnam_r(name, &tsdPtr->pwd, tsdPtr->pbuf, sizeof(tsdPtr->pbuf));
169
170#else
171#define NEED_COPYPWD 1
172    struct passwd *pwPtr;
173
174    Tcl_MutexLock(&compatLock);
175    pwPtr = getpwnam(name);
176    if (pwPtr != NULL) {
177        tsdPtr->pwd = *pwPtr;
178        pwPtr = &tsdPtr->pwd;
179        if (CopyPwd(&tsdPtr->pwd, tsdPtr->pbuf, sizeof(tsdPtr->pbuf)) == -1) {
180            pwPtr = NULL;
181        }
182    }
183    Tcl_MutexUnlock(&compatLock);
184    return pwPtr;
185#endif
186
187    return NULL;                /* Not reached. */
188#endif /* TCL_THREADS */
189}
190
191/*
192 *---------------------------------------------------------------------------
193 *
194 * TclpGetPwUid --
195 *
196 *      Thread-safe wrappers for getpwuid(). See "man getpwuid" for more
197 *      details.
198 *
199 * Results:
200 *      Pointer to struct passwd on success or NULL on error.
201 *
202 * Side effects:
203 *      None.
204 *
205 *---------------------------------------------------------------------------
206 */
207
208struct passwd *
209TclpGetPwUid(
210    uid_t uid)
211{
212#if !defined(TCL_THREADS)
213    return getpwuid(uid);
214#else
215    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
216
217#if defined(HAVE_GETPWUID_R_5)
218    struct passwd *pwPtr = NULL;
219
220    return (getpwuid_r(uid, &tsdPtr->pwd, tsdPtr->pbuf, sizeof(tsdPtr->pbuf),
221                       &pwPtr) == 0 && pwPtr != NULL) ? &tsdPtr->pwd : NULL;
222
223#elif defined(HAVE_GETPWUID_R_4)
224    return getpwuid_r(uid, &tsdPtr->pwd, tsdPtr->pbuf, sizeof(tsdPtr->pbuf));
225
226#else
227#define NEED_COPYPWD 1
228    struct passwd *pwPtr;
229
230    Tcl_MutexLock(&compatLock);
231    pwPtr = getpwuid(uid);
232    if (pwPtr != NULL) {
233        tsdPtr->pwd = *pwPtr;
234        pwPtr = &tsdPtr->pwd;
235        if (CopyPwd(&tsdPtr->pwd, tsdPtr->pbuf, sizeof(tsdPtr->pbuf)) == -1) {
236            pwPtr = NULL;
237        }
238    }
239    Tcl_MutexUnlock(&compatLock);
240    return pwPtr;
241#endif
242
243    return NULL;                /* Not reached. */
244#endif /* TCL_THREADS */
245}
246
247/*
248 *---------------------------------------------------------------------------
249 *
250 * TclpGetGrNam --
251 *
252 *      Thread-safe wrappers for getgrnam(). See "man getgrnam" for more
253 *      details.
254 *
255 * Results:
256 *      Pointer to struct group on success or NULL on error.
257 *
258 * Side effects:
259 *      None.
260 *
261 *---------------------------------------------------------------------------
262 */
263
264struct group *
265TclpGetGrNam(
266    const char *name)
267{
268#if !defined(TCL_THREADS)
269    return getgrnam(name);
270#else
271    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
272
273#if defined(HAVE_GETGRNAM_R_5)
274    struct group *grPtr = NULL;
275
276    return (getgrnam_r(name, &tsdPtr->grp, tsdPtr->gbuf, sizeof(tsdPtr->gbuf),
277                       &grPtr) == 0 && grPtr != NULL) ? &tsdPtr->grp : NULL;
278
279#elif defined(HAVE_GETGRNAM_R_4)
280    return getgrnam_r(name, &tsdPtr->grp, tsdPtr->gbuf, sizeof(tsdPtr->gbuf));
281
282#else
283#define NEED_COPYGRP 1
284    struct group *grPtr;
285
286    Tcl_MutexLock(&compatLock);
287    grPtr = getgrnam(name);
288    if (grPtr != NULL) {
289        tsdPtr->grp = *grPtr;
290        grPtr = &tsdPtr->grp;
291        if (CopyGrp(&tsdPtr->grp, tsdPtr->gbuf, sizeof(tsdPtr->gbuf)) == -1) {
292            grPtr = NULL;
293        }
294    }
295    Tcl_MutexUnlock(&compatLock);
296    return grPtr;
297#endif
298
299    return NULL;                /* Not reached. */
300#endif /* TCL_THREADS */
301}
302
303/*
304 *---------------------------------------------------------------------------
305 *
306 * TclpGetGrGid --
307 *
308 *      Thread-safe wrappers for getgrgid(). See "man getgrgid" for more
309 *      details.
310 *
311 * Results:
312 *      Pointer to struct group on success or NULL on error.
313 *
314 * Side effects:
315 *      None.
316 *
317 *---------------------------------------------------------------------------
318 */
319
320struct group *
321TclpGetGrGid(
322    gid_t gid)
323{
324#if !defined(TCL_THREADS)
325    return getgrgid(gid);
326#else
327    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
328
329#if defined(HAVE_GETGRGID_R_5)
330    struct group *grPtr = NULL;
331
332    return (getgrgid_r(gid, &tsdPtr->grp, tsdPtr->gbuf, sizeof(tsdPtr->gbuf),
333                       &grPtr) == 0 && grPtr != NULL) ? &tsdPtr->grp : NULL;
334
335#elif defined(HAVE_GETGRGID_R_4)
336    return getgrgid_r(gid, &tsdPtr->grp, tsdPtr->gbuf, sizeof(tsdPtr->gbuf));
337
338#else
339#define NEED_COPYGRP 1
340    struct group *grPtr;
341
342    Tcl_MutexLock(&compatLock);
343    grPtr = getgrgid(gid);
344    if (grPtr != NULL) {
345        tsdPtr->grp = *grPtr;
346        grPtr = &tsdPtr->grp;
347        if (CopyGrp(&tsdPtr->grp, tsdPtr->gbuf, sizeof(tsdPtr->gbuf)) == -1) {
348            grPtr = NULL;
349        }
350    }
351    Tcl_MutexUnlock(&compatLock);
352    return grPtr;
353#endif
354
355    return NULL;                /* Not reached. */
356#endif /* TCL_THREADS */
357}
358
359/*
360 *---------------------------------------------------------------------------
361 *
362 * TclpGetHostByName --
363 *
364 *      Thread-safe wrappers for gethostbyname(). See "man gethostbyname" for
365 *      more details.
366 *
367 * Results:
368 *      Pointer to struct hostent on success or NULL on error.
369 *
370 * Side effects:
371 *      None.
372 *
373 *---------------------------------------------------------------------------
374 */
375
376struct hostent *
377TclpGetHostByName(
378    const char *name)
379{
380#if !defined(TCL_THREADS) || defined(HAVE_MTSAFE_GETHOSTBYNAME)
381    return gethostbyname(name);
382#else
383    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
384
385#if defined(HAVE_GETHOSTBYNAME_R_5)
386    int h_errno;
387
388    return gethostbyname_r(name, &tsdPtr->hent, tsdPtr->hbuf,
389                           sizeof(tsdPtr->hbuf), &h_errno);
390
391#elif defined(HAVE_GETHOSTBYNAME_R_6)
392    struct hostent *hePtr = NULL;
393    int h_errno, result;
394
395    result = gethostbyname_r(name, &tsdPtr->hent, tsdPtr->hbuf,
396            sizeof(tsdPtr->hbuf), &hePtr, &h_errno);
397    return (result == 0) ? hePtr : NULL;
398
399#elif defined(HAVE_GETHOSTBYNAME_R_3)
400    struct hostent_data data;
401
402    return (gethostbyname_r(name, &tsdPtr->hent, &data) == 0)
403            ? &tsdPtr->hent : NULL;
404
405#else
406#define NEED_COPYHOSTENT 1
407    struct hostent *hePtr;
408
409    Tcl_MutexLock(&compatLock);
410    hePtr = gethostbyname(name);
411    if (hePtr != NULL) {
412        tsdPtr->hent = *hePtr;
413        hePtr = &tsdPtr->hent;
414        if (CopyHostent(&tsdPtr->hent, tsdPtr->hbuf,
415                sizeof(tsdPtr->hbuf)) == -1) {
416            hePtr = NULL;
417        }
418    }
419    Tcl_MutexUnlock(&compatLock);
420    return hePtr;
421#endif
422
423    return NULL;                /* Not reached. */
424#endif /* TCL_THREADS */
425}
426
427/*
428 *---------------------------------------------------------------------------
429 *
430 * TclpGetHostByAddr --
431 *
432 *      Thread-safe wrappers for gethostbyaddr(). See "man gethostbyaddr" for
433 *      more details.
434 *
435 * Results:
436 *      Pointer to struct hostent on success or NULL on error.
437 *
438 * Side effects:
439 *      None.
440 *
441 *---------------------------------------------------------------------------
442 */
443
444struct hostent *
445TclpGetHostByAddr(
446    const char *addr,
447    int length,
448    int type)
449{
450#if !defined(TCL_THREADS) || defined(HAVE_MTSAFE_GETHOSTBYADDR)
451    return gethostbyaddr(addr, length, type);
452#else
453    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
454
455#if defined(HAVE_GETHOSTBYADDR_R_7)
456    int h_errno;
457
458    return gethostbyaddr_r(addr, length, type, &tsdPtr->hent, tsdPtr->hbuf,
459            sizeof(tsdPtr->hbuf), &h_errno);
460
461#elif defined(HAVE_GETHOSTBYADDR_R_8)
462    struct hostent *hePtr;
463    int h_errno;
464
465    return (gethostbyaddr_r(addr, length, type, &tsdPtr->hent, tsdPtr->hbuf,
466                sizeof(tsdPtr->hbuf), &hePtr, &h_errno) == 0)
467            ? &tsdPtr->hent : NULL;
468#else
469#define NEED_COPYHOSTENT 1
470    struct hostent *hePtr;
471
472    Tcl_MutexLock(&compatLock);
473    hePtr = gethostbyaddr(addr, length, type);
474    if (hePtr != NULL) {
475        tsdPtr->hent = *hePtr;
476        hePtr = &tsdPtr->hent;
477        if (CopyHostent(&tsdPtr->hent, tsdPtr->hbuf,
478                sizeof(tsdPtr->hbuf)) == -1) {
479            hePtr = NULL;
480        }
481    }
482    Tcl_MutexUnlock(&compatLock);
483    return hePtr;
484#endif
485
486    return NULL;                /* Not reached. */
487#endif /* TCL_THREADS */
488}
489
490/*
491 *---------------------------------------------------------------------------
492 *
493 * CopyGrp --
494 *
495 *      Copies string fields of the group structure to the private buffer,
496 *      honouring the size of the buffer.
497 *
498 * Results:
499 *      0 on success or -1 on error (errno = ERANGE).
500 *
501 * Side effects:
502 *      None.
503 *
504 *---------------------------------------------------------------------------
505 */
506
507#ifdef NEED_COPYGRP
508#define NEED_COPYARRAY 1
509#define NEED_COPYSTRING 1
510
511static int
512CopyGrp(
513    struct group *tgtPtr,
514    char *buf,
515    int buflen)
516{
517    register char *p = buf;
518    register int copied, len = 0;
519
520    /*
521     * Copy username.
522     */
523
524    copied = CopyString(tgtPtr->gr_name, p, buflen - len);
525    if (copied == -1) {
526        goto range;
527    }
528    tgtPtr->gr_name = (copied > 0) ? p : NULL;
529    len += copied;
530    p = buf + len;
531
532    /*
533     * Copy password.
534     */
535
536    copied = CopyString(tgtPtr->gr_passwd, p, buflen - len);
537    if (copied == -1) {
538        goto range;
539    }
540    tgtPtr->gr_passwd = (copied > 0) ? p : NULL;
541    len += copied;
542    p = buf + len;
543
544    /*
545     * Copy group members.
546     */
547
548    PadBuffer(p, len, sizeof(char *));
549    copied = CopyArray((char **)tgtPtr->gr_mem, -1, p, buflen - len);
550    if (copied == -1) {
551        goto range;
552    }
553    tgtPtr->gr_mem = (copied > 0) ? (char **)p : NULL;
554
555    return 0;
556
557  range:
558    errno = ERANGE;
559    return -1;
560}
561#endif /* NEED_COPYGRP */
562
563/*
564 *---------------------------------------------------------------------------
565 *
566 * CopyHostent --
567 *
568 *      Copies string fields of the hostnent structure to the private buffer,
569 *      honouring the size of the buffer.
570 *
571 * Results:
572 *      Number of bytes copied on success or -1 on error (errno = ERANGE)
573 *
574 * Side effects:
575 *      None
576 *
577 *---------------------------------------------------------------------------
578 */
579
580#ifdef NEED_COPYHOSTENT
581#define NEED_COPYSTRING 1
582#define NEED_COPYARRAY 1
583
584static int
585CopyHostent(
586    struct hostent *tgtPtr,
587    char *buf,
588    int buflen)
589{
590    char *p = buf;
591    int copied, len = 0;
592
593    copied = CopyString(tgtPtr->h_name, p, buflen - len);
594    if (copied == -1) {
595        goto range;
596    }
597    tgtPtr->h_name = (copied > 0) ? p : NULL;
598    len += copied;
599    p = buf + len;
600
601    PadBuffer(p, len, sizeof(char *));
602    copied = CopyArray(tgtPtr->h_aliases, -1, p, buflen - len);
603    if (copied == -1) {
604        goto range;
605    }
606    tgtPtr->h_aliases = (copied > 0) ? (char **)p : NULL;
607    len += copied;
608    p += len;
609
610    PadBuffer(p, len, sizeof(char *));
611    copied = CopyArray(tgtPtr->h_addr_list, tgtPtr->h_length, p, buflen-len);
612    if (copied == -1) {
613        goto range;
614    }
615    tgtPtr->h_addr_list = (copied > 0) ? (char **)p : NULL;
616
617    return 0;
618
619  range:
620    errno = ERANGE;
621    return -1;
622}
623#endif /* NEED_COPYHOSTENT */
624
625/*
626 *---------------------------------------------------------------------------
627 *
628 * CopyPwd --
629 *
630 *      Copies string fields of the passwd structure to the private buffer,
631 *      honouring the size of the buffer.
632 *
633 * Results:
634 *      0 on success or -1 on error (errno = ERANGE).
635 *
636 * Side effects:
637 *      We are not copying the gecos field as it may not be supported on all
638 *      platforms.
639 *
640 *---------------------------------------------------------------------------
641 */
642
643#ifdef NEED_COPYPWD
644#define NEED_COPYSTRING 1
645
646static int
647CopyPwd(
648    struct passwd *tgtPtr,
649    char *buf,
650    int buflen)
651{
652    char *p = buf;
653    int copied, len = 0;
654
655    copied = CopyString(tgtPtr->pw_name, p, buflen - len);
656    if (copied == -1) {
657    range:
658        errno = ERANGE;
659        return -1;
660    }
661    tgtPtr->pw_name = (copied > 0) ? p : NULL;
662    len += copied;
663    p = buf + len;
664
665    copied = CopyString(tgtPtr->pw_passwd, p, buflen - len);
666    if (copied == -1) {
667        goto range;
668    }
669    tgtPtr->pw_passwd = (copied > 0) ? p : NULL;
670    len += copied;
671    p = buf + len;
672
673    copied = CopyString(tgtPtr->pw_dir, p, buflen - len);
674    if (copied == -1) {
675        goto range;
676    }
677    tgtPtr->pw_dir = (copied > 0) ? p : NULL;
678    len += copied;
679    p = buf + len;
680
681    copied = CopyString(tgtPtr->pw_shell, p, buflen - len);
682    if (copied == -1) {
683        goto range;
684    }
685    tgtPtr->pw_shell = (copied > 0) ? p : NULL;
686
687    return 0;
688}
689#endif /* NEED_COPYPWD */
690
691/*
692 *---------------------------------------------------------------------------
693 *
694 * CopyArray --
695 *
696 *      Copies array of NULL-terminated or fixed-length strings to the private
697 *      buffer, honouring the size of the buffer.
698 *
699 * Results:
700 *      Number of bytes copied on success or -1 on error (errno = ERANGE)
701 *
702 * Side effects:
703 *      None.
704 *
705 *---------------------------------------------------------------------------
706 */
707
708#ifdef NEED_COPYARRAY
709static int
710CopyArray(
711    char **src,                 /* Array of elements to copy. */
712    int elsize,                 /* Size of each element, or -1 to indicate
713                                 * that they are C strings of dynamic
714                                 * length. */
715    char *buf,                  /* Buffer to copy into. */
716    int buflen)                 /* Size of buffer. */
717{
718    int i, j, len = 0;
719    char *p, **new;
720
721    if (src == NULL) {
722        return 0;
723    }
724
725    for (i = 0; src[i] != NULL; i++) {
726        /*
727         * Empty loop to count how many.
728         */
729    }
730    len = sizeof(char *) * (i + 1);     /* Leave place for the array. */
731    if (len >  buflen) {
732        return -1;
733    }
734
735    new = (char **) buf;
736    p = buf + len;
737
738    for (j = 0; j < i; j++) {
739        int sz = (elsize<0 ? strlen(src[j])+1 : elsize);
740
741        len += sz;
742        if (len > buflen) {
743            return -1;
744        }
745        memcpy(p, src[j], sz);
746        new[j] = p;
747        p = buf + len;
748    }
749    new[j] = NULL;
750
751    return len;
752}
753#endif /* NEED_COPYARRAY */
754
755/*
756 *---------------------------------------------------------------------------
757 *
758 * CopyString --
759 *
760 *      Copies a NULL-terminated string to the private buffer, honouring the
761 *      size of the buffer
762 *
763 * Results:
764 *      0 success or -1 on error (errno = ERANGE)
765 *
766 * Side effects:
767 *      None
768 *
769 *---------------------------------------------------------------------------
770 */
771
772#ifdef NEED_COPYSTRING
773static int
774CopyString(
775    char *src,                  /* String to copy. */
776    char *buf,                  /* Buffer to copy into. */
777    int buflen)                 /* Size of buffer. */
778{
779    int len = 0;
780
781    if (src != NULL) {
782        len = strlen(src) + 1;
783        if (len > buflen) {
784            return -1;
785        }
786        memcpy(buf, src, len);
787    }
788
789    return len;
790}
791#endif /* NEED_COPYSTRING */
792
793/*
794 * Local Variables:
795 * mode: c
796 * c-basic-offset: 4
797 * fill-column: 78
798 * End:
799 */
Note: See TracBrowser for help on using the repository browser.