GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/rdist/common.c Lines: 0 284 0.0 %
Date: 2017-11-07 Branches: 0 174 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: common.c,v 1.37 2015/12/22 08:48:39 mmcc Exp $	*/
2
3
/*
4
 * Copyright (c) 1983 Regents of the University of California.
5
 * All rights reserved.
6
 *
7
 * Redistribution and use in source and binary forms, with or without
8
 * modification, are permitted provided that the following conditions
9
 * are met:
10
 * 1. Redistributions of source code must retain the above copyright
11
 *    notice, this list of conditions and the following disclaimer.
12
 * 2. Redistributions in binary form must reproduce the above copyright
13
 *    notice, this list of conditions and the following disclaimer in the
14
 *    documentation and/or other materials provided with the distribution.
15
 * 3. Neither the name of the University nor the names of its contributors
16
 *    may be used to endorse or promote products derived from this software
17
 *    without specific prior written permission.
18
 *
19
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29
 * SUCH DAMAGE.
30
 */
31
32
#include <sys/stat.h>
33
#include <sys/time.h>
34
#include <sys/wait.h>
35
36
#include <errno.h>
37
#include <fcntl.h>
38
#include <grp.h>
39
#include <limits.h>
40
#include <paths.h>
41
#include <stdarg.h>
42
#include <stdio.h>
43
#include <stdlib.h>
44
#include <string.h>
45
#include <unistd.h>
46
47
#include "defs.h"
48
49
/*
50
 * Things common to both the client and server.
51
 */
52
53
/*
54
 * Variables common to both client and server
55
 */
56
char			host[HOST_NAME_MAX+1];	/* Name of this host */
57
uid_t			userid = (uid_t)-1;	/* User's UID */
58
gid_t			groupid = (gid_t)-1;	/* User's GID */
59
char		       *homedir = NULL;		/* User's $HOME */
60
char		       *locuser = NULL;		/* Local User's name */
61
int			isserver = FALSE;	/* We're the server */
62
int     		amchild = 0;		/* This PID is a child */
63
int			do_fork = 1;		/* Fork child process */
64
char		       *currenthost = NULL;	/* Current client hostname */
65
char		       *progname = NULL;	/* Name of this program */
66
int			rem_r = -1;		/* Client file descriptor */
67
int			rem_w = -1;		/* Client file descriptor */
68
struct passwd	       *pw = NULL;		/* Local user's pwd entry */
69
volatile sig_atomic_t 	contimedout = FALSE;	/* Connection timed out */
70
int			rtimeout = RTIMEOUT;	/* Response time out */
71
jmp_buf			finish_jmpbuf;		/* Finish() jmp buffer */
72
int			setjmp_ok = FALSE;	/* setjmp()/longjmp() status */
73
char		      **realargv;		/* Real main() argv */
74
int			realargc;		/* Real main() argc */
75
opt_t			options = 0;		/* Global install options */
76
char			defowner[64] = "bin";	/* Default owner */
77
char			defgroup[64] = "bin";	/* Default group */
78
79
static int sendcmdmsg(int, char *, size_t);
80
static ssize_t remread(int, u_char *, size_t);
81
static int remmore(void);
82
83
/*
84
 * Front end to write() that handles partial write() requests.
85
 */
86
ssize_t
87
xwrite(int fd, void *buf, size_t len)
88
{
89
    	size_t nleft = len;
90
	ssize_t nwritten;
91
	char *ptr = buf;
92
93
	while (nleft > 0) {
94
	    	if ((nwritten = write(fd, ptr, nleft)) <= 0) {
95
			return nwritten;
96
	    	}
97
	    	nleft -= nwritten;
98
	    	ptr += nwritten;
99
	}
100
101
	return len;
102
}
103
104
/*
105
 * Do run-time initialization
106
 */
107
int
108
init(int argc, char **argv, char **envp)
109
{
110
	int i;
111
112
	/*
113
	 * Save a copy of our argc and argv before setargs() overwrites them
114
	 */
115
	realargc = argc;
116
	realargv = xmalloc(sizeof(char *) * (argc+1));
117
	for (i = 0; i < argc; i++)
118
		realargv[i] = xstrdup(argv[i]);
119
120
	pw = getpwuid(userid = getuid());
121
	if (pw == NULL) {
122
		error("Your user id (%u) is not known to this system.",
123
		      getuid());
124
		return(-1);
125
	}
126
127
	debugmsg(DM_MISC, "UserID = %u pwname = '%s' home = '%s'\n",
128
		 userid, pw->pw_name, pw->pw_dir);
129
	homedir = xstrdup(pw->pw_dir);
130
	locuser = xstrdup(pw->pw_name);
131
	groupid = pw->pw_gid;
132
	gethostname(host, sizeof(host));
133
#if 0
134
	if ((cp = strchr(host, '.')) != NULL)
135
	    	*cp = CNULL;
136
#endif
137
138
	/*
139
	 * If we're not root, disable paranoid ownership checks
140
	 * since normal users cannot chown() files.
141
	 */
142
	if (!isserver && userid != 0) {
143
		FLAG_ON(options, DO_NOCHKOWNER);
144
		FLAG_ON(options, DO_NOCHKGROUP);
145
	}
146
147
	return(0);
148
}
149
150
/*
151
 * Finish things up before ending.
152
 */
153
void
154
finish(void)
155
{
156
	debugmsg(DM_CALL,
157
		 "finish() called: do_fork = %d amchild = %d isserver = %d",
158
		 do_fork, amchild, isserver);
159
	cleanup(0);
160
161
	/*
162
	 * There's no valid finish_jmpbuf for the rdist master parent.
163
	 */
164
	if (!do_fork || amchild || isserver) {
165
166
		if (!setjmp_ok) {
167
#ifdef DEBUG_SETJMP
168
			error("attemping longjmp() without target");
169
			abort();
170
#else
171
			exit(1);
172
#endif
173
		}
174
175
		longjmp(finish_jmpbuf, 1);
176
		/*NOTREACHED*/
177
		error("Unexpected failure of longjmp() in finish()");
178
		exit(2);
179
	} else
180
		exit(1);
181
}
182
183
/*
184
 * Handle lost connections
185
 */
186
void
187
lostconn(void)
188
{
189
	/* Prevent looping */
190
	(void) signal(SIGPIPE, SIG_IGN);
191
192
	rem_r = rem_w = -1;	/* Ensure we don't try to send to server */
193
	checkhostname();
194
	error("Lost connection to %s",
195
	      (currenthost) ? currenthost : "(unknown)");
196
197
	finish();
198
}
199
200
/*
201
 * General signal handler
202
 */
203
void
204
sighandler(int sig)
205
{
206
	int save_errno = errno;
207
208
	/* XXX signal race */
209
	debugmsg(DM_CALL, "sighandler() received signal %d\n", sig);
210
211
	switch (sig) {
212
	case SIGALRM:
213
		contimedout = TRUE;
214
		/* XXX signal race */
215
		checkhostname();
216
		error("Response time out");
217
		finish();
218
		break;
219
220
	case SIGPIPE:
221
		/* XXX signal race */
222
		lostconn();
223
		break;
224
225
	case SIGFPE:
226
		debug = !debug;
227
		break;
228
229
	case SIGHUP:
230
	case SIGINT:
231
	case SIGQUIT:
232
	case SIGTERM:
233
		/* XXX signal race */
234
		finish();
235
		break;
236
237
	default:
238
		/* XXX signal race */
239
		fatalerr("No signal handler defined for signal %d.", sig);
240
	}
241
	errno = save_errno;
242
}
243
244
/*
245
 * Function to actually send the command char and message to the
246
 * remote host.
247
 */
248
static int
249
sendcmdmsg(int cmd, char *msg, size_t msgsize)
250
{
251
	int len;
252
253
	if (rem_w < 0)
254
		return(-1);
255
256
	/*
257
	 * All commands except C_NONE should have a newline
258
	 */
259
	if (cmd != C_NONE && !strchr(msg + 1, '\n'))
260
		(void) strlcat(msg + 1, "\n", msgsize - 1);
261
262
	if (cmd == C_NONE)
263
		len = strlen(msg);
264
	else {
265
		len = strlen(msg + 1) + 1;
266
		msg[0] = cmd;
267
	}
268
269
	debugmsg(DM_PROTO, ">>> Cmd = %c (\\%3.3o) Msg = \"%.*s\"",
270
		 cmd, cmd,
271
		 (cmd == C_NONE) ? len-1 : len-2,
272
		 (cmd == C_NONE) ? msg : msg + 1);
273
274
	return(!(xwrite(rem_w, msg, len) == len));
275
}
276
277
/*
278
 * Send a command message to the remote host.
279
 * Called as sendcmd(char cmdchar, char *fmt, arg1, arg2, ...)
280
 * The fmt may be NULL, in which case there are no args.
281
 */
282
int
283
sendcmd(char cmd, const char *fmt, ...)
284
{
285
	static char buf[BUFSIZ];
286
	va_list args;
287
288
	va_start(args, fmt);
289
	if (fmt)
290
		(void) vsnprintf(buf + (cmd != C_NONE),
291
				 sizeof(buf) - (cmd != C_NONE), fmt, args);
292
	else
293
		buf[1] = CNULL;
294
	va_end(args);
295
296
	return(sendcmdmsg(cmd, buf, sizeof(buf)));
297
}
298
299
/*
300
 * Internal variables and routines for reading lines from the remote.
301
 */
302
static u_char rembuf[BUFSIZ];
303
static u_char *remptr;
304
static ssize_t remleft;
305
306
#define remc() (--remleft < 0 ? remmore() : *remptr++)
307
308
/*
309
 * Back end to remote read()
310
 */
311
static ssize_t
312
remread(int fd, u_char *buf, size_t bufsiz)
313
{
314
	return(read(fd, (char *)buf, bufsiz));
315
}
316
317
static int
318
remmore(void)
319
{
320
	(void) signal(SIGALRM, sighandler);
321
	(void) alarm(rtimeout);
322
323
	remleft = remread(rem_r, rembuf, sizeof(rembuf));
324
325
	(void) alarm(0);
326
327
	if (remleft < 0)
328
		return (-2);	/* error */
329
	if (remleft == 0)
330
		return (-1);	/* EOF */
331
	remptr = rembuf;
332
	remleft--;
333
	return (*remptr++);
334
}
335
336
/*
337
 * Read an input line from the remote.  Return the number of bytes
338
 * stored (equivalent to strlen(p)).  If `cleanup' is set, EOF at
339
 * the beginning of a line is returned as EOF (-1); other EOFs, or
340
 * errors, call cleanup() or lostconn().  In other words, unless
341
 * the third argument is nonzero, this routine never returns failure.
342
 */
343
int
344
remline(u_char *buffer, int space, int doclean)
345
{
346
	int c, left = space;
347
	u_char *p = buffer;
348
349
	if (rem_r < 0) {
350
		error("Cannot read remote input: Remote descriptor not open.");
351
		return(-1);
352
	}
353
354
	while (left > 0) {
355
		if ((c = remc()) < -1) {	/* error */
356
			if (doclean) {
357
				finish();
358
				/*NOTREACHED*/
359
			}
360
			lostconn();
361
			/*NOTREACHED*/
362
		}
363
		if (c == -1) {			/* got EOF */
364
			if (doclean) {
365
				if (left == space)
366
					return (-1);/* signal proper EOF */
367
				finish();	/* improper EOF */
368
				/*NOTREACHED*/
369
			}
370
			lostconn();
371
			/*NOTREACHED*/
372
		}
373
		if (c == '\n') {
374
			*p = CNULL;
375
376
			if (debug) {
377
				static char mbuf[BUFSIZ];
378
379
				(void) snprintf(mbuf, sizeof(mbuf),
380
					"<<< Cmd = %c (\\%3.3o) Msg = \"%s\"",
381
					       buffer[0], buffer[0],
382
					       buffer + 1);
383
384
				debugmsg(DM_PROTO, "%s", mbuf);
385
			}
386
387
			return (space - left);
388
		}
389
		*p++ = c;
390
		left--;
391
	}
392
393
	/* this will probably blow the entire session */
394
	error("remote input line too long");
395
	p[-1] = CNULL;		/* truncate */
396
	return (space);
397
}
398
399
/*
400
 * Non-line-oriented remote read.
401
 */
402
ssize_t
403
readrem(char *p, ssize_t space)
404
{
405
	if (remleft <= 0) {
406
		/*
407
		 * Set remote time out alarm.
408
		 */
409
		(void) signal(SIGALRM, sighandler);
410
		(void) alarm(rtimeout);
411
412
		remleft = remread(rem_r, rembuf, sizeof(rembuf));
413
414
		(void) alarm(0);
415
		remptr = rembuf;
416
	}
417
418
	if (remleft <= 0)
419
		return (remleft);
420
	if (remleft < space)
421
		space = remleft;
422
423
	memcpy(p, remptr, space);
424
425
	remptr += space;
426
	remleft -= space;
427
428
	return (space);
429
}
430
431
/*
432
 * Get the user name for the uid.
433
 */
434
char *
435
getusername(uid_t uid, char *file, opt_t opts)
436
{
437
	static char buf[100];
438
	static uid_t lastuid = (uid_t)-1;
439
	struct passwd *pwd = NULL;
440
441
	/*
442
	 * The value of opts may have changed so we always
443
	 * do the opts check.
444
	 */
445
  	if (IS_ON(opts, DO_NUMCHKOWNER)) {
446
		(void) snprintf(buf, sizeof(buf), ":%u", uid);
447
		return(buf);
448
  	}
449
450
	/*
451
	 * Try to avoid getpwuid() call.
452
	 */
453
	if (lastuid == uid && buf[0] != '\0' && buf[0] != ':')
454
		return(buf);
455
456
	lastuid = uid;
457
458
	if ((pwd = getpwuid(uid)) == NULL) {
459
		if (IS_ON(opts, DO_DEFOWNER) && !isserver)
460
			(void) strlcpy(buf, defowner, sizeof(buf));
461
		else {
462
			message(MT_WARNING,
463
				"%s: No password entry for uid %u", file, uid);
464
			(void) snprintf(buf, sizeof(buf), ":%u", uid);
465
		}
466
	} else {
467
		(void) strlcpy(buf, pwd->pw_name, sizeof(buf));
468
	}
469
470
	return(buf);
471
}
472
473
/*
474
 * Get the group name for the gid.
475
 */
476
char *
477
getgroupname(gid_t gid, char *file, opt_t opts)
478
{
479
	static char buf[100];
480
	static gid_t lastgid = (gid_t)-1;
481
	struct group *grp = NULL;
482
483
	/*
484
	 * The value of opts may have changed so we always
485
	 * do the opts check.
486
	 */
487
  	if (IS_ON(opts, DO_NUMCHKGROUP)) {
488
		(void) snprintf(buf, sizeof(buf), ":%u", gid);
489
		return(buf);
490
  	}
491
492
	/*
493
	 * Try to avoid getgrgid() call.
494
	 */
495
	if (lastgid == gid && buf[0] != '\0' && buf[0] != ':')
496
		return(buf);
497
498
	lastgid = gid;
499
500
	if ((grp = (struct group *)getgrgid(gid)) == NULL) {
501
		if (IS_ON(opts, DO_DEFGROUP) && !isserver)
502
			(void) strlcpy(buf, defgroup, sizeof(buf));
503
		else {
504
			message(MT_WARNING, "%s: No name for group %u",
505
				file, gid);
506
			(void) snprintf(buf, sizeof(buf), ":%u", gid);
507
		}
508
	} else
509
		(void) strlcpy(buf, grp->gr_name, sizeof(buf));
510
511
	return(buf);
512
}
513
514
/*
515
 * Read a response from the remote host.
516
 */
517
int
518
response(void)
519
{
520
	static u_char resp[BUFSIZ];
521
	u_char *s;
522
	int n;
523
524
	debugmsg(DM_CALL, "response() start\n");
525
526
	n = remline(s = resp, sizeof(resp), 0);
527
528
	n--;
529
	switch (*s++) {
530
        case C_ACK:
531
		debugmsg(DM_PROTO, "received ACK\n");
532
		return(0);
533
	case C_LOGMSG:
534
		if (n > 0) {
535
			message(MT_CHANGE, "%s", s);
536
			return(1);
537
		}
538
		debugmsg(DM_PROTO, "received EMPTY logmsg\n");
539
		return(0);
540
	case C_NOTEMSG:
541
		if (s)
542
			message(MT_NOTICE, "%s", s);
543
		return(response());
544
545
	default:
546
		s--;
547
		n++;
548
		/* fall into... */
549
550
	case C_ERRMSG:	/* Normal error message */
551
		if (s)
552
			message(MT_NERROR, "%s", s);
553
		return(-1);
554
555
	case C_FERRMSG:	/* Fatal error message */
556
		if (s)
557
			message(MT_FERROR, "%s", s);
558
		finish();
559
		return(-1);
560
	}
561
	/*NOTREACHED*/
562
}
563
564
/*
565
 * This should be in expand.c but the other routines call other modules
566
 * that we don't want to load in.
567
 *
568
 * Expand file names beginning with `~' into the
569
 * user's home directory path name. Return a pointer in buf to the
570
 * part corresponding to `file'.
571
 */
572
char *
573
exptilde(char *ebuf, char *file, size_t ebufsize)
574
{
575
	char *pw_dir, *rest;
576
	size_t len;
577
578
	if (*file != '~') {
579
notilde:
580
		(void) strlcpy(ebuf, file, ebufsize);
581
		return(ebuf);
582
	}
583
	if (*++file == CNULL) {
584
		pw_dir = homedir;
585
		rest = NULL;
586
	} else if (*file == '/') {
587
		pw_dir = homedir;
588
		rest = file;
589
	} else {
590
		rest = file;
591
		while (*rest && *rest != '/')
592
			rest++;
593
		if (*rest == '/')
594
			*rest = CNULL;
595
		else
596
			rest = NULL;
597
		if (pw == NULL || strcmp(pw->pw_name, file) != 0) {
598
			if ((pw = getpwnam(file)) == NULL) {
599
				error("%s: unknown user name", file);
600
				if (rest != NULL)
601
					*rest = '/';
602
				return(NULL);
603
			}
604
		}
605
		if (rest != NULL)
606
			*rest = '/';
607
		pw_dir = pw->pw_dir;
608
	}
609
	if ((len = strlcpy(ebuf, pw_dir, ebufsize)) >= ebufsize)
610
		goto notilde;
611
	pw_dir = ebuf + len;
612
	if (rest != NULL) {
613
		pw_dir++;
614
		if ((len = strlcat(ebuf, rest, ebufsize)) >= ebufsize)
615
			goto notilde;
616
	}
617
	return(pw_dir);
618
}
619
620
621
622
/*
623
 * Set access and modify times of a given file
624
 */
625
int
626
setfiletime(char *file, time_t atime, time_t mtime)
627
{
628
	struct timeval tv[2];
629
630
	if (atime != 0 && mtime != 0) {
631
		tv[0].tv_sec = atime;
632
		tv[1].tv_sec = mtime;
633
		tv[0].tv_usec = tv[1].tv_usec = 0;
634
		return (utimes(file, tv));
635
	} else	/* Set to current time */
636
		return (utimes(file, NULL));
637
}
638
639
/*
640
 * Get version info
641
 */
642
char *
643
getversion(void)
644
{
645
	static char buff[BUFSIZ];
646
647
	(void) snprintf(buff, sizeof(buff),
648
	"Version %s.%d (%s) - Protocol Version %d, Release %s, Patch level %d",
649
		       DISTVERSION, PATCHLEVEL, DISTSTATUS,
650
		       VERSION, DISTVERSION, PATCHLEVEL);
651
652
	return(buff);
653
}
654
655
/*
656
 * Execute a shell command to handle special cases.
657
 * This is now common to both server and client
658
 */
659
void
660
runcommand(char *cmd)
661
{
662
	ssize_t nread;
663
	pid_t pid, wpid;
664
	char *cp, *s;
665
	char sbuf[BUFSIZ], buf[BUFSIZ];
666
	int fd[2], status;
667
668
	if (pipe(fd) < 0) {
669
		error("pipe of %s failed: %s", cmd, SYSERR);
670
		return;
671
	}
672
673
	if ((pid = fork()) == 0) {
674
		/*
675
		 * Return everything the shell commands print.
676
		 */
677
		(void) close(0);
678
		(void) close(1);
679
		(void) close(2);
680
		(void) open(_PATH_DEVNULL, O_RDONLY);
681
		(void) dup(fd[PIPE_WRITE]);
682
		(void) dup(fd[PIPE_WRITE]);
683
		(void) close(fd[PIPE_READ]);
684
		(void) close(fd[PIPE_WRITE]);
685
		(void) execl(_PATH_BSHELL, "sh", "-c", cmd, (char *)NULL);
686
		_exit(127);
687
	}
688
	(void) close(fd[PIPE_WRITE]);
689
	s = sbuf;
690
	*s++ = C_LOGMSG;
691
	while ((nread = read(fd[PIPE_READ], buf, sizeof(buf))) > 0) {
692
		cp = buf;
693
		do {
694
			*s++ = *cp++;
695
			if (cp[-1] != '\n') {
696
				if (s < (char *) &sbuf[sizeof(sbuf)-1])
697
					continue;
698
				*s++ = '\n';
699
			}
700
			/*
701
			 * Throw away blank lines.
702
			 */
703
			if (s == &sbuf[2]) {
704
				s--;
705
				continue;
706
			}
707
			if (isserver)
708
				(void) xwrite(rem_w, sbuf, s - sbuf);
709
			else {
710
				*s = CNULL;
711
				message(MT_INFO, "%s", sbuf+1);
712
			}
713
			s = &sbuf[1];
714
		} while (--nread);
715
	}
716
	if (s > (char *) &sbuf[1]) {
717
		*s++ = '\n';
718
		if (isserver)
719
			(void) xwrite(rem_w, sbuf, s - sbuf);
720
		else {
721
			*s = CNULL;
722
			message(MT_INFO, "%s", sbuf+1);
723
		}
724
	}
725
	while ((wpid = wait(&status)) != pid && wpid != -1)
726
		;
727
	if (wpid == -1)
728
		status = -1;
729
	(void) close(fd[PIPE_READ]);
730
	if (status)
731
		error("shell returned %d", status);
732
	else if (isserver)
733
		ack();
734
}
735
736
/*
737
 * Malloc with error checking
738
 */
739
void *
740
xmalloc(size_t amt)
741
{
742
	void *ptr;
743
744
	if ((ptr = malloc(amt)) == NULL)
745
		fatalerr("Cannot malloc %zu bytes of memory.", amt);
746
747
	return (ptr);
748
}
749
750
/*
751
 * realloc with error checking
752
 */
753
void *
754
xrealloc(void *baseptr, size_t amt)
755
{
756
	void *new;
757
758
	if ((new = realloc(baseptr, amt)) == NULL)
759
		fatalerr("Cannot realloc %zu bytes of memory.", amt);
760
761
	return (new);
762
}
763
764
/*
765
 * calloc with error checking
766
 */
767
void *
768
xcalloc(size_t num, size_t esize)
769
{
770
	void *ptr;
771
772
	if ((ptr = calloc(num, esize)) == NULL)
773
		fatalerr("Cannot calloc %zu * %zu = %zu bytes of memory.",
774
		      num, esize, num * esize);
775
776
	return (ptr);
777
}
778
779
/*
780
 * Strdup with error checking
781
 */
782
char *
783
xstrdup(const char *str)
784
{
785
	size_t len = strlen(str) + 1;
786
	char *nstr = xmalloc(len);
787
788
	return (memcpy(nstr, str, len));
789
}
790
791
/*
792
 * Private version of basename()
793
 */
794
char *
795
xbasename(char *path)
796
{
797
	char *cp;
798
799
	if ((cp = strrchr(path, '/')) != NULL)
800
		return(cp+1);
801
	else
802
		return(path);
803
}
804
805
/*
806
 * Take a colon (':') separated path to a file and
807
 * search until a component of that path is found and
808
 * return the found file name.
809
 */
810
char *
811
searchpath(char *path)
812
{
813
	char *file;
814
	char *space;
815
	int found;
816
	struct stat statbuf;
817
818
	for (found = 0; !found && (file = strsep(&path, ":")) != NULL; ) {
819
		if ((space = strchr(file, ' ')) != NULL)
820
			*space = CNULL;
821
		found = stat(file, &statbuf) == 0;
822
		if (space)
823
			*space = ' ';		/* Put back what we zapped */
824
	}
825
	return (file);
826
}