1/*
2 * misc.c - common miscellaneous functions for lsof
3 */
4
5
6/*
7 * Copyright 1994 Purdue Research Foundation, West Lafayette, Indiana
8 * 47907.  All rights reserved.
9 *
10 * Written by Victor A. Abell
11 *
12 * This software is not subject to any license of the American Telephone
13 * and Telegraph Company or the Regents of the University of California.
14 *
15 * Permission is granted to anyone to use this software for any purpose on
16 * any computer system, and to alter it and redistribute it freely, subject
17 * to the following restrictions:
18 *
19 * 1. Neither the authors nor Purdue University are responsible for any
20 *    consequences of the use of this software.
21 *
22 * 2. The origin of this software must not be misrepresented, either by
23 *    explicit claim or by omission.  Credit to the authors and Purdue
24 *    University must appear in documentation and sources.
25 *
26 * 3. Altered versions must be plainly marked as such, and must not be
27 *    misrepresented as being the original software.
28 *
29 * 4. This notice may not be removed or altered.
30 */
31
32#ifndef lint
33static char copyright[] =
34"@(#) Copyright 1994 Purdue Research Foundation.\nAll rights reserved.\n";
35static char *rcsid = "$Id: misc.c,v 1.26 2008/10/21 16:21:41 abe Exp $";
36#endif
37
38
39#include "lsof.h"
40
41#if	defined(HASWIDECHAR) && defined(WIDECHARINCL)
42#include WIDECHARINCL
43#endif	/* defined(HASWIDECHAR) && defined(WIDECHARINCL) */
44
45
46/*
47 * Local definitions
48 */
49
50#if	!defined(MAXSYMLINKS)
51#define	MAXSYMLINKS	32
52#endif	/* !defined(MAXSYMLINKS) */
53
54
55/*
56 * Local function prototypes
57 */
58
59_PROTOTYPE(static void closePipes,(void));
60_PROTOTYPE(static int dolstat,(char *path, char *buf, int len));
61_PROTOTYPE(static int dostat,(char *path, char *buf, int len));
62_PROTOTYPE(static int doreadlink,(char *path, char *buf, int len));
63_PROTOTYPE(static int doinchild,(int (*fn)(), char *fp, char *rbuf, int rbln));
64
65#if	defined(HASINTSIGNAL)
66_PROTOTYPE(static int handleint,(int sig));
67#else	/* !defined(HASINTSIGNAL) */
68_PROTOTYPE(static void handleint,(int sig));
69#endif	/* defined(HASINTSIGNAL) */
70
71_PROTOTYPE(static char *safepup,(unsigned int c, int *cl));
72
73
74/*
75 * Local variables
76 */
77
78static pid_t Cpid = 0;			/* child PID */
79static jmp_buf Jmp_buf;			/* jump buffer */
80static int Pipes[] =			/* pipes for child process */
81	{ -1, -1, -1, -1 };
82static int CtSigs[] = { 0, SIGINT, SIGKILL };
83					/* child termination signals (in order
84					 * of application) -- the first is a
85					 * dummy to allow pipe closure to
86					 * cause the child to exit */
87#define	NCTSIGS	(sizeof(CtSigs) / sizeof(int))
88
89
90#if	defined(HASNLIST)
91/*
92 * build-Nl() - build kernel name list table
93 */
94
95static struct drive_Nl *Build_Nl = (struct drive_Nl *)NULL;
96					/* the default Drive_Nl address */
97
98void
99build_Nl(d)
100	struct drive_Nl *d;		/* data to drive the construction */
101{
102	struct drive_Nl *dp;
103	int i, n;
104
105	for (dp = d, n = 0; dp->nn; dp++, n++)
106	    ;
107	if (n < 1) {
108	    (void) fprintf(stderr,
109		"%s: can't calculate kernel name list length\n", Pn);
110	    Exit(1);
111	}
112	if (!(Nl = (struct NLIST_TYPE *)calloc((n + 1),
113					       sizeof(struct NLIST_TYPE))))
114	{
115	    (void) fprintf(stderr,
116		"%s: can't allocate %d bytes to kernel name list structure\n",
117		Pn, (int)((n + 1) * sizeof(struct NLIST_TYPE)));
118	    Exit(1);
119	}
120	for (dp = d, i = 0; i < n; dp++, i++) {
121	    Nl[i].NL_NAME = dp->knm;
122	}
123	Nll = (int)((n + 1) * sizeof(struct NLIST_TYPE));
124	Build_Nl = d;
125}
126#endif	/* defined(HASNLIST) */
127
128
129/*
130 * childx() - make child process exit (if possible)
131 */
132
133void
134childx()
135{
136	static int at, sx;
137	pid_t wpid;
138
139	if (Cpid > 1) {
140
141	/*
142	 * First close the pipes to and from the child.  That should cause the
143	 * child to exit.  Compute alarm time shares.
144	 */
145	    (void) closePipes();
146	    if ((at = TmLimit / NCTSIGS) < TMLIMMIN)
147		at = TMLIMMIN;
148	/*
149	 * Loop, waiting for the child to exit.  After the first pass, help
150	 * the child exit by sending it signals.
151	 */
152	    for (sx = 0; sx < NCTSIGS; sx++) {
153		if (setjmp(Jmp_buf)) {
154
155		/*
156		 * An alarm has rung.  Disable further alarms.
157		 *
158		 * If there are more signals to send, continue the signal loop.
159		 *
160		 * If the last signal has been sent, issue a warning (unless
161		 * warninge have been suppressed) and exit the signal loop.
162		 */
163		    (void) alarm(0);
164		    (void) signal(SIGALRM, SIG_DFL);
165		    if (sx < (NCTSIGS - 1))
166			continue;
167		    if (!Fwarn)
168			(void) fprintf(stderr,
169			    "%s: WARNING -- child process %d may be hung.\n",
170			    Pn, (int)Cpid);
171		    break;
172	        }
173	    /*
174	     * Send the next signal to the child process, after the first pass
175	     * through the loop.
176	     *
177	     * Wrap the wait() with an alarm.
178	     */
179		if (sx)
180		    (void) kill(Cpid, CtSigs[sx]);
181		(void) signal(SIGALRM, handleint);
182		(void) alarm(at);
183		wpid = (pid_t) wait(NULL);
184		(void) alarm(0);
185		(void) signal(SIGALRM, SIG_DFL);
186		if (wpid == Cpid)
187		    break;
188	    }
189	    Cpid = 0;
190	}
191}
192
193
194/*
195 * closePipes() - close open pipe file descriptors
196 */
197
198static void
199closePipes()
200{
201	int i;
202
203	for (i = 0; i < 4; i++) {
204	    if (Pipes[i] >= 0) {
205		(void) close(Pipes[i]);
206		Pipes[i] = -1;
207	    }
208	}
209}
210
211
212/*
213 * compdev() - compare Devtp[] entries
214 */
215
216int
217compdev(a1, a2)
218	COMP_P *a1, *a2;
219{
220	struct l_dev **p1 = (struct l_dev **)a1;
221	struct l_dev **p2 = (struct l_dev **)a2;
222
223	if ((dev_t)((*p1)->rdev) < (dev_t)((*p2)->rdev))
224	    return(-1);
225	if ((dev_t)((*p1)->rdev) > (dev_t)((*p2)->rdev))
226	    return(1);
227	if ((INODETYPE)((*p1)->inode) < (INODETYPE)((*p2)->inode))
228	    return(-1);
229	if ((INODETYPE)((*p1)->inode) > (INODETYPE)((*p2)->inode))
230	    return(1);
231	return(strcmp((*p1)->name, (*p2)->name));
232}
233
234
235/*
236 * doinchild() -- do a function in a child process
237 */
238
239static int
240doinchild(fn, fp, rbuf, rbln)
241	int (*fn)();			/* function to perform */
242	char *fp;			/* function parameter */
243	char *rbuf;			/* response buffer */
244	int rbln;			/* response buffer length */
245{
246	int en, rv;
247/*
248 * Check reply buffer size.
249 */
250	if (!Fovhd && rbln > MAXPATHLEN) {
251	    (void) fprintf(stderr,
252		"%s: doinchild error; response buffer too large: %d\n",
253		Pn, rbln);
254	    Exit(1);
255	}
256/*
257 * Set up to handle an alarm signal; handle an alarm signal; build
258 * pipes for exchanging information with a child process; start the
259 * child process; and perform functions in the child process.
260 */
261	if (!Fovhd) {
262	    if (setjmp(Jmp_buf)) {
263
264	    /*
265	     * Process an alarm that has rung.
266	     */
267		(void) alarm(0);
268		(void) signal(SIGALRM, SIG_DFL);
269		(void) childx();
270		errno = ETIMEDOUT;
271		return(1);
272	    } else if (!Cpid) {
273
274	    /*
275	     * Create pipes to exchange function information with a child
276	     * process.
277	     */
278		if (pipe(Pipes) < 0 || pipe(&Pipes[2]) < 0) {
279		    (void) fprintf(stderr, "%s: can't open pipes: %s\n",
280			Pn, strerror(errno));
281		    Exit(1);
282		}
283	    /*
284	     * Fork a child to execute functions.
285	     */
286		if ((Cpid = fork()) == 0) {
287
288		/*
289		 * Begin the child process.
290		 */
291
292		    int fd, nd, r_al, r_rbln;
293		    char r_arg[MAXPATHLEN+1], r_rbuf[MAXPATHLEN+1];
294		    int (*r_fn)();
295		/*
296		 * Close all open file descriptors except Pipes[0] and
297		 * Pipes[3].
298		 */
299		    for (fd = 0, nd = GET_MAX_FD(); fd < nd; fd++) {
300			if (fd == Pipes[0] || fd == Pipes[3])
301			    continue;
302			(void) close(fd);
303			if (fd == Pipes[1])
304			    Pipes[1] = -1;
305			else if (fd == Pipes[2])
306			    Pipes[2] = -1;
307		    }
308		    if (Pipes[1] >= 0) {
309			(void) close(Pipes[1]);
310			Pipes[1] = -1;
311		    }
312		    if (Pipes[2] >= 0) {
313			(void) close(Pipes[2]);
314			Pipes[2] = -1;
315		    }
316		/*
317		 * Read function requests, process them, and return replies.
318		 */
319		    for (;;) {
320			if (read(Pipes[0], (char *)&r_fn, sizeof(r_fn))
321			    != (int)sizeof(r_fn)
322			||  read(Pipes[0], (char *)&r_al, sizeof(int))
323			    != (int)sizeof(int)
324			||  r_al < 1
325			||  r_al > (int)sizeof(r_arg)
326			||  read(Pipes[0], r_arg, r_al) != r_al
327			||  read(Pipes[0], (char *)&r_rbln, sizeof(r_rbln))
328			    != (int)sizeof(r_rbln)
329			||  r_rbln < 1 || r_rbln > (int)sizeof(r_rbuf))
330			    break;
331			rv = r_fn(r_arg, r_rbuf, r_rbln);
332			en = errno;
333			if (write(Pipes[3], (char *)&rv, sizeof(rv))
334			    != sizeof(rv)
335			||  write(Pipes[3], (char *)&en, sizeof(en))
336			    != sizeof(en)
337			||  write(Pipes[3], r_rbuf, r_rbln) != r_rbln)
338			    break;
339		    }
340		    (void) _exit(0);
341		}
342	    /*
343	     * Continue in the parent process to finish the setup.
344	     */
345		if (Cpid < 0) {
346		    (void) fprintf(stderr, "%s: can't fork: %s\n",
347			Pn, strerror(errno));
348		    Exit(1);
349		}
350		(void) close(Pipes[0]);
351		(void) close(Pipes[3]);
352		Pipes[0] = Pipes[3] = -1;
353	    }
354	}
355	if (!Fovhd) {
356	    int len;
357
358	/*
359	 * Send a function to the child and wait for the response.
360	 */
361	    len  = strlen(fp) + 1;
362	    (void) signal(SIGALRM, handleint);
363	    (void) alarm(TmLimit);
364	    if (write(Pipes[1], (char *)&fn, sizeof(fn)) != sizeof(fn)
365	    ||  write(Pipes[1], (char *)&len, sizeof(len)) != sizeof(len)
366	    ||  write(Pipes[1], fp, len) != len
367	    ||  write(Pipes[1], (char *)&rbln, sizeof(rbln)) != sizeof(rbln)
368	    ||  read(Pipes[2], (char *)&rv, sizeof(rv)) != sizeof(rv)
369	    ||  read(Pipes[2], (char *)&en, sizeof(en)) != sizeof(en)
370	    ||  read(Pipes[2], rbuf, rbln) != rbln) {
371		(void) alarm(0);
372		(void) signal(SIGALRM, SIG_DFL);
373		(void) childx();
374		errno = ECHILD;
375		return(-1);
376	    }
377	} else {
378
379	/*
380	 * Do the operation directly -- not in a child.
381	 */
382	    (void) signal(SIGALRM, handleint);
383	    (void) alarm(TmLimit);
384	    rv = fn(fp, rbuf, rbln);
385	    en = errno;
386	}
387/*
388 * Function completed, response collected -- complete the operation.
389 */
390	(void) alarm(0);
391	(void) signal(SIGALRM, SIG_DFL);
392	errno = en;
393	return(rv);
394}
395
396
397/*
398 * dolstat() - do an lstat() function
399 */
400
401static int
402dolstat(path, rbuf, rbln)
403	char *path;			/* path */
404	char *rbuf;			/* response buffer */
405	int rbln;			/* response buffer length */
406
407/* ARGSUSED */
408
409{
410	return(lstat(path, (struct stat *)rbuf));
411}
412
413
414/*
415 * doreadlink() -- do a readlink() function
416 */
417
418static int
419doreadlink(path, rbuf, rbln)
420	char *path;			/* path */
421	char *rbuf;			/* response buffer */
422	int rbln;			/* response buffer length */
423{
424	return(readlink(path, rbuf, rbln));
425}
426
427
428/*
429 * dostat() - do a stat() function
430 */
431
432static int
433dostat(path, rbuf, rbln)
434	char *path;			/* path */
435	char *rbuf;			/* response buffer */
436	int rbln;			/* response buffer length */
437
438/* ARGSUSED */
439
440{
441	return(stat(path, (struct stat *)rbuf));
442}
443
444
445#if	defined(WILLDROPGID)
446/*
447 * dropgid() - drop setgid permission
448 */
449
450void
451dropgid()
452{
453	if (!Setuidroot && Setgid) {
454	    if (setgid(Mygid) < 0) {
455		(void) fprintf(stderr, "%s: can't setgid(%d): %s\n",
456		    Pn, (int)Mygid, strerror(errno));
457		Exit(1);
458	    }
459	    Setgid = 0;
460	}
461}
462#endif	/* defined(WILLDROPGID) */
463
464
465/*
466 * enter_dev_ch() - enter device characters in file structure
467 */
468
469void
470enter_dev_ch(m)
471	char *m;
472{
473	char *mp;
474
475	if (!m || *m == '\0')
476	    return;
477	if (!(mp = mkstrcpy(m, (MALLOC_S *)NULL))) {
478	    (void) fprintf(stderr, "%s: no more dev_ch space at PID %d: \n",
479		Pn, Lp->pid);
480	    safestrprt(m, stderr, 1);
481	    Exit(1);
482	}
483	if (Lf->dev_ch)
484	   (void) free((FREE_P *)Lf->dev_ch);
485	Lf->dev_ch = mp;
486}
487
488
489/*
490 * enter_IPstate() -- enter a TCP or UDP state
491 */
492
493void
494enter_IPstate(ty, nm, nr)
495	char *ty;			/* type -- TCP or UDP */
496	char *nm;			/* state name (may be NULL) */
497	int nr;				/* state number */
498{
499
500#if	defined(USE_LIB_PRINT_TCPTPI)
501	TcpNstates = nr;
502#else	/* !defined(USE_LIB_PRINT_TCPTPI) */
503
504	int al, i, j, oc, nn, ns, off, tx;
505	char *cp;
506	MALLOC_S len;
507/*
508 * Check the type name and set the type index.
509 */
510	if (!ty) {
511	    (void) fprintf(stderr,
512		"%s: no type specified to enter_IPstate()\n", Pn);
513	    Exit(1);
514	}
515	if (!strcmp(ty, "TCP"))
516	    tx = 0;
517	else if (!strcmp(ty, "UDP"))
518	    tx = 1;
519	else {
520	    (void) fprintf(stderr, "%s: unknown type for enter_IPstate: %s\n",
521		Pn, ty);
522	    Exit(1);
523	}
524/*
525 * If the name argument is NULL, reduce the allocated table to its minimum
526 * size.
527 */
528	if (!nm) {
529	    if (tx) {
530		if (UdpSt) {
531		    if (!UdpNstates) {
532			(void) free((MALLOC_P *)UdpSt);
533			UdpSt = (char **)NULL;
534		    }
535		    if (UdpNstates < UdpStAlloc) {
536			len = (MALLOC_S)(UdpNstates * sizeof(char *));
537			if (!(UdpSt = (char **)realloc((MALLOC_P *)UdpSt, len)))
538			{
539			    (void) fprintf(stderr,
540				"%s: can't reduce UdpSt[]\n", Pn);
541			    Exit(1);
542			}
543		    }
544		    UdpStAlloc = UdpNstates;
545		}
546	    } else {
547		if (TcpSt) {
548		    if (!TcpNstates) {
549			(void) free((MALLOC_P *)TcpSt);
550			TcpSt = (char **)NULL;
551		    }
552		    if (TcpNstates < TcpStAlloc) {
553			len = (MALLOC_S)(TcpNstates * sizeof(char *));
554			if (!(TcpSt = (char **)realloc((MALLOC_P *)TcpSt, len)))
555			{
556			    (void) fprintf(stderr,
557				"%s: can't reduce TcpSt[]\n", Pn);
558			    Exit(1);
559			}
560		    }
561		    TcpStAlloc = TcpNstates;
562		}
563	    }
564	    return;
565	}
566/*
567 * Check the name and number.
568 */
569	if ((len = (size_t)strlen(nm)) < 1) {
570	    (void) fprintf(stderr,
571		"%s: bad %s name (\"%s\"), number=%d\n", Pn, ty, nm, nr);
572	    Exit(1);
573	}
574/*
575 * Make a copy of the name.
576 */
577	if (!(cp = mkstrcpy(nm, (MALLOC_S *)NULL))) {
578	    (void) fprintf(stderr,
579		"%s: enter_IPstate(): no %s space for %s\n",
580		Pn, ty, nm);
581	    Exit(1);
582	}
583/*
584 * Set the necessary offset for using nr as an index.  If it is
585 * a new offset, adjust previous entries.
586 */
587	if ((nr < 0) && ((off = -nr) > (tx ? UdpStOff : TcpStOff))) {
588	    if (tx ? UdpSt : TcpSt) {
589
590	    /*
591	     * A new, larger offset (smaller negative state number) could mean
592	     * a previously allocated state table must be enlarged and its
593	     * previous entries moved.
594	     */
595		oc = off - (tx ? UdpStOff : TcpStOff);
596		al = tx ? UdpStAlloc : TcpStAlloc;
597		ns = tx ? UdpNstates : TcpNstates;
598		if ((nn = ns + oc) >= al) {
599		    while ((nn + 5) > al) {
600			al += TCPUDPALLOC;
601		    }
602		    len = (MALLOC_S)(al * sizeof(char *));
603		    if (tx) {
604			if (!(UdpSt = (char **)realloc((MALLOC_P *)UdpSt, len)))
605			    goto no_IP_space;
606			UdpStAlloc = al;
607		    } else {
608			if (!(TcpSt = (char **)realloc((MALLOC_P *)TcpSt, len)))
609			    goto no_IP_space;
610			TcpStAlloc = al;
611		    }
612		    for (i = 0, j = oc; i < oc; i++, j++) {
613			if (tx) {
614			    if (i < UdpNstates)
615				UdpSt[j] = UdpSt[i];
616			    UdpSt[i] = (char *)NULL;
617			} else {
618			    if (i < TcpNstates)
619				TcpSt[j] = TcpSt[i];
620			    TcpSt[i] = (char *)NULL;
621			}
622		    }
623		    if (tx)
624			UdpNstates += oc;
625		    else
626			TcpNstates += oc;
627		}
628	    }
629	    if (tx)
630		UdpStOff = off;
631	    else
632		TcpStOff = off;
633	}
634/*
635 * Enter name as {Tc|Ud}pSt[nr + {Tc|Ud}pStOff].
636 *
637 * Allocate space, as required.
638 */
639	al = tx ? UdpStAlloc : TcpStAlloc;
640	off = tx ? UdpStOff : TcpStOff;
641	nn = nr + off + 1;
642	if (nn > al) {
643	    i = tx ? UdpNstates : TcpNstates;
644	    while ((nn + 5) > al) {
645		al += TCPUDPALLOC;
646	    }
647	    len = (MALLOC_S)(al * sizeof(char *));
648	    if (tx) {
649		if (UdpSt)
650		    UdpSt = (char **)realloc((MALLOC_P *)UdpSt, len);
651		else
652		    UdpSt = (char **)malloc(len);
653		if (!UdpSt) {
654
655no_IP_space:
656
657		    (void) fprintf(stderr, "%s: no %s state space\n", Pn, ty);
658		    Exit(1);
659		}
660		UdpNstates = nn;
661		UdpStAlloc = al;
662	    } else {
663		if (TcpSt)
664		    TcpSt = (char **)realloc((MALLOC_P *)TcpSt, len);
665		else
666		    TcpSt = (char **)malloc(len);
667		if (!TcpSt)
668		    goto no_IP_space;
669		TcpNstates = nn;
670		TcpStAlloc = al;
671	    }
672	    while (i < al) {
673		if (tx)
674		    UdpSt[i] = (char *)NULL;
675		else
676		    TcpSt[i] = (char *)NULL;
677		i++;
678	    }
679	} else {
680	    if (tx) {
681		if (nn > UdpNstates)
682		    UdpNstates = nn;
683	    } else {
684		if (nn > TcpNstates)
685		    TcpNstates = nn;
686	    }
687	}
688	if (tx) {
689	    if (UdpSt[nr + UdpStOff]) {
690
691dup_IP_state:
692
693		(void) fprintf(stderr,
694		    "%s: duplicate %s state %d (already %s): %s\n",
695		    Pn, ty, nr,
696		    tx ? UdpSt[nr + UdpStOff] : TcpSt[nr + TcpStOff],
697		    nm);
698	 	Exit(1);
699	    }
700	    UdpSt[nr + UdpStOff] = cp;
701	} else {
702	    if (TcpSt[nr + TcpStOff])
703		goto dup_IP_state;
704	    TcpSt[nr + TcpStOff] = cp;
705	}
706#endif	/* defined(USE_LIB_PRINT_TCPTPI) */
707
708}
709
710
711/*
712 * enter_nm() - enter name in local file structure
713 */
714
715void
716enter_nm(m)
717	char *m;
718{
719	char *mp;
720
721	if (!m || *m == '\0')
722	    return;
723	if (!(mp = mkstrcpy(m, (MALLOC_S *)NULL))) {
724	    (void) fprintf(stderr, "%s: no more nm space at PID %d for: ",
725		Pn, Lp->pid);
726	    safestrprt(m, stderr, 1);
727	    Exit(1);
728	}
729	if (Lf->nm)
730	    (void) free((FREE_P *)Lf->nm);
731	Lf->nm = mp;
732}
733
734
735/*
736 * Exit() - do a clean exit()
737 */
738
739void
740Exit(xv)
741	int xv;				/* exit() value */
742{
743	(void) childx();
744
745#if	defined(HASDCACHE)
746	if (DCrebuilt && !Fwarn)
747	    (void) fprintf(stderr, "%s: WARNING: %s was updated.\n",
748		Pn, DCpath[DCpathX]);
749#endif	/* defined(HASDCACHE) */
750
751	exit(xv);
752}
753
754
755#if	defined(HASNLIST)
756/*
757 * get_Nl_value() - get Nl value for nickname
758 */
759
760int
761get_Nl_value(nn, d, v)
762	char *nn;			/* nickname of requested entry */
763	struct drive_Nl *d;		/* drive_Nl table that built Nl
764					 * (if NULL, use Build_Nl) */
765	KA_T *v;			/* returned value (if NULL,
766					 * return nothing) */
767{
768	int i;
769
770	if (!Nl || !Nll)
771	    return(-1);
772	if (!d)
773	    d = Build_Nl;
774	for (i = 0; d->nn; d++, i++) {
775	    if (strcmp(d->nn, nn) == 0) {
776		if (v)
777		    *v = (KA_T)Nl[i].n_value;
778		return(i);
779	    }
780	}
781	return(-1);
782}
783#endif	/* defined(HASNLIST) */
784
785
786/*
787 * handleint() - handle an interrupt
788 */
789
790#if	defined(HASINTSIGNAL)
791static int
792#else
793static void
794#endif
795
796/* ARGSUSED */
797
798handleint(sig)
799	int sig;
800{
801	longjmp(Jmp_buf, 1);
802}
803
804
805/*
806 * hashbyname() - hash by name
807 */
808
809int
810hashbyname(nm, mod)
811	char *nm;			/* pointer to NUL-terminated name */
812	int mod;			/* hash modulus */
813{
814	int i, j;
815
816	for (i = j = 0; *nm; nm++) {
817	    i ^= (int)*nm << j;
818	    if (++j > 7)
819		j = 0;
820	}
821	return(((int)(i * 31415)) & (mod - 1));
822}
823
824
825/*
826 * is_nw_addr() - is this network address selected?
827 */
828
829int
830is_nw_addr(ia, p, af)
831	unsigned char *ia;		/* Internet address */
832	int p;				/* port */
833	int af;				/* address family -- e.g., AF_INET,
834					 * AF_INET6 */
835{
836	struct nwad *n;
837
838	if (!(n = Nwad))
839	    return(0);
840	for (; n; n = n->next) {
841	    if (n->proto) {
842		if (strcasecmp(n->proto, Lf->iproto) != 0)
843		    continue;
844	    }
845	    if (af && n->af && af != n->af)
846		continue;
847
848#if	defined(HASIPv6)
849	    if (af == AF_INET6) {
850		if (n->a[15] || n->a[14] || n->a[13] || n->a[12]
851		||  n->a[11] || n->a[10] || n->a[9]  || n->a[8]
852		||  n->a[7]  || n->a[6]  || n->a[5]  || n->a[4]
853		||  n->a[3]  || n->a[2]  || n->a[1]  || n->a[0]) {
854		    if (ia[15] != n->a[15] || ia[14] != n->a[14]
855		    ||  ia[13] != n->a[13] || ia[12] != n->a[12]
856		    ||  ia[11] != n->a[11] || ia[10] != n->a[10]
857		    ||  ia[9]  != n->a[9]  || ia[8]  != n->a[8]
858		    ||  ia[7]  != n->a[7]  || ia[6]  != n->a[6]
859		    ||  ia[5]  != n->a[5]  || ia[4]  != n->a[4]
860		    ||  ia[3]  != n->a[3]  || ia[2]  != n->a[2]
861		    ||  ia[1]  != n->a[1]  || ia[0]  != n->a[0])
862			continue;
863		}
864	    } else if (af == AF_INET)
865#endif	/* defined(HASIPv6) */
866
867	    {
868		if (n->a[3] || n->a[2] || n->a[1] || n->a[0]) {
869		    if (ia[3] != n->a[3] || ia[2] != n->a[2]
870		    ||  ia[1] != n->a[1] || ia[0] != n->a[0])
871			continue;
872		}
873	    }
874
875#if	defined(HASIPv6)
876	    else
877		continue;
878#endif	/* defined(HASIPv6) */
879
880	    if (n->sport == -1 || (p >= n->sport && p <= n->eport)) {
881		n->f = 1;
882		return(1);
883	    }
884	}
885	return(0);
886}
887
888
889/*
890 * mkstrcpy() - make a string copy in malloc()'d space
891 *
892 * return: copy pointer
893 *	   copy length (optional)
894 */
895
896char *
897mkstrcpy(src, rlp)
898	char *src;			/* source */
899	MALLOC_S *rlp;			/* returned length pointer (optional)
900					 * The returned length is an strlen()
901					 * equivalent */
902{
903	MALLOC_S len;
904	char *ns;
905
906	len = (MALLOC_S)(src ? strlen(src) : 0);
907	ns = (char *)malloc(len + 1);
908	if (ns) {
909	    if (src)
910		(void) snpf(ns, len + 1, "%s", src);
911	    else
912		*ns = '\0';
913	}
914	if (rlp)
915	    *rlp = len;
916	return(ns);
917}
918
919
920/*
921 * mkstrcat() - make a catenated copy of up to three strings under optional
922 *		string-by-string count control
923 *
924 * return: copy pointer
925 *	   copy string length (optional)
926 */
927
928char *
929mkstrcat(s1, l1, s2, l2, s3, l3, clp)
930	char *s1;			/* source string 1 */
931	int l1;				/* length of string 1 (-1 if none) */
932	char *s2;			/* source string 2 */
933	int l2;				/* length of string 2 (-1 if none) */
934	char *s3;			/* source string 3 (optional) */
935	int l3	;			/* length of string 3 (-1 if none) */
936	MALLOC_S *clp;			/* pointer to return of copy length
937					 * (optional) */
938{
939	MALLOC_S cl, len1, len2, len3;
940	char *cp;
941
942	if (s1)
943	    len1 = (MALLOC_S)((l1 >= 0) ? l1 : strlen(s1));
944	else
945	    len1 = (MALLOC_S)0;
946	if (s2)
947	    len2 = (MALLOC_S)((l2 >= 0) ? l2 : strlen(s2));
948	else
949	    len2 = (MALLOC_S)0;
950	if (s3)
951	    len3 = (MALLOC_S)((l3 >= 0) ? l3 : strlen(s3));
952	else
953	    len3 = (MALLOC_S)0;
954	cl = len1 + len2 + len3;
955	if ((cp = (char *)malloc(cl + 1))) {
956	    char *tp = cp;
957
958	    if (s1 && len1) {
959		(void) strncpy(tp, s1, len1);
960		tp += len1;
961	    }
962	    if (s2 && len2) {
963		(void) strncpy(tp, s2, len2);
964		tp += len2;
965	    }
966	    if (s3 && len3) {
967		(void) strncpy(tp, s3, len3);
968		tp += len3;
969	    }
970	    *tp = '\0';
971	}
972	if (clp)
973	    *clp = cl;
974	return(cp);
975}
976
977
978/*
979 * is_readable() -- is file readable
980 */
981
982int
983is_readable(path, msg)
984	char *path;			/* file path */
985	int msg;			/* issue warning message if 1 */
986{
987	if (access(path, R_OK) < 0) {
988	    if (!Fwarn && msg == 1)
989		(void) fprintf(stderr, ACCESSERRFMT, Pn, path, strerror(errno));
990	    return(0);
991	}
992	return(1);
993}
994
995
996/*
997 * lstatsafely() - lstat path safely (i. e., with timeout)
998 */
999
1000int
1001lstatsafely(path, buf)
1002	char *path;			/* file path */
1003	struct stat *buf;		/* stat buffer address */
1004{
1005	if (Fblock) {
1006	    if (!Fwarn)
1007		(void) fprintf(stderr,
1008		    "%s: avoiding stat(%s): -b was specified.\n",
1009		    Pn, path);
1010	    errno = EWOULDBLOCK;
1011	    return(1);
1012	}
1013	return(doinchild(dolstat, path, (char *)buf, sizeof(struct stat)));
1014}
1015
1016
1017/*
1018 * Readlink() - read and interpret file system symbolic links
1019 */
1020
1021char *
1022Readlink(arg)
1023	char *arg;			/* argument to be interpreted */
1024{
1025	char abuf[MAXPATHLEN+1];
1026	int alen;
1027	char *ap;
1028	char *argp1, *argp2;
1029	int i, len, llen, slen;
1030	char lbuf[MAXPATHLEN+1];
1031	static char *op = (char *)NULL;
1032	static int ss = 0;
1033	char *s1;
1034	static char **stk = (char **)NULL;
1035	static int sx = 0;
1036	char tbuf[MAXPATHLEN+1];
1037/*
1038 * See if avoiding kernel blocks.
1039 */
1040	if (Fblock) {
1041	    if (!Fwarn) {
1042		(void) fprintf(stderr, "%s: avoiding readlink(", Pn);
1043		safestrprt(arg, stderr, 0);
1044		(void) fprintf(stderr, "): -b was specified.\n");
1045	    }
1046	    op = (char *)NULL;
1047	    return(arg);
1048	}
1049/*
1050 * Save the original path.
1051 */
1052	if (!op)
1053	    op = arg;
1054/*
1055 * Evaluate each component of the argument for a symbolic link.
1056 */
1057	for (alen = 0, ap = abuf, argp1 = argp2 = arg; *argp2; argp1 = argp2 ) {
1058	    for (argp2 = argp1 + 1; *argp2 && *argp2 != '/'; argp2++)
1059		;
1060	    if ((len = argp2 - arg) >= (int)sizeof(tbuf)) {
1061
1062path_too_long:
1063		if (!Fwarn) {
1064		    (void) fprintf(stderr,
1065			"%s: readlink() path too long: ", Pn);
1066		    safestrprt(op ? op : arg, stderr, 1);
1067		}
1068		op = (char *)NULL;
1069		return((char *)NULL);
1070	    }
1071	    (void) strncpy(tbuf, arg, len);
1072	    tbuf[len] = '\0';
1073	/*
1074	 * Dereference a symbolic link.
1075	 */
1076	    if ((llen=doinchild(doreadlink,tbuf,lbuf,sizeof(lbuf) - 1)) >= 0) {
1077
1078	    /*
1079	     * If the link is a new absolute path, replace
1080	     * the previous assembly with it.
1081	     */
1082		if (lbuf[0] == '/') {
1083		    (void) strncpy(abuf, lbuf, llen);
1084		    ap = &abuf[llen];
1085		    *ap = '\0';
1086		    alen = llen;
1087		    continue;
1088		}
1089		lbuf[llen] = '\0';
1090		s1 = lbuf;
1091	    } else {
1092		llen = argp2 - argp1;
1093		s1 = argp1;
1094	    }
1095	/*
1096	 * Make sure two components are separated by a `/'.
1097	 *
1098	 * If the first component is not a link, don't force
1099	 * a leading '/'.
1100	 *
1101	 * If the first component is a link and the source of
1102	 * the link has a leading '/', force a leading '/'.
1103	 */
1104	    if (*s1 == '/')
1105		slen = 1;
1106	    else {
1107		if (alen > 0) {
1108
1109		/*
1110		 * This is not the first component.
1111		 */
1112		    if (abuf[alen - 1] == '/')
1113			slen = 1;
1114		    else
1115			slen = 2;
1116		} else {
1117
1118		/*
1119		 * This is the first component.
1120		 */
1121		    if (s1 == lbuf && tbuf[0] == '/')
1122			slen = 2;
1123		    else
1124			slen = 1;
1125		}
1126	    }
1127	/*
1128	 * Add to the path assembly.
1129	 */
1130	    if ((alen + llen + slen) >= (int)sizeof(abuf))
1131		goto path_too_long;
1132	    if (slen == 2)
1133		*ap++ = '/';
1134	    (void) strncpy(ap, s1, llen);
1135	    ap += llen;
1136	    *ap = '\0';
1137	    alen += (llen + slen - 1);
1138	}
1139/*
1140 * If the assembled path and argument are the same, free all but the
1141 * last string in the stack, and return the argument.
1142 */
1143	if (strcmp(arg, abuf) == 0) {
1144	    for (i = 0; i < sx; i++) {
1145		if (i < (sx - 1))
1146		    (void) free((FREE_P *)stk[i]);
1147		stk[i] = (char *)NULL;
1148	    }
1149	    sx = 0;
1150	    op = (char *)NULL;
1151	    return(arg);
1152	}
1153/*
1154 * If the assembled path and argument are different, add it to the
1155 * string stack, then Readlink() it.
1156 */
1157	if (!(s1 = mkstrcpy(abuf, (MALLOC_S *)NULL))) {
1158
1159no_readlink_space:
1160
1161	    (void) fprintf(stderr, "%s: no Readlink string space for ", Pn);
1162	    safestrprt(abuf, stderr, 1);
1163	    Exit(1);
1164	}
1165	if (sx >= MAXSYMLINKS) {
1166
1167	/*
1168	 * If there are too many symbolic links, report an error, clear
1169	 * the stack, and return no path.
1170	 */
1171	    if (!Fwarn) {
1172		(void) fprintf(stderr,
1173		    "%s: too many (> %d) symbolic links in readlink() path: ",
1174			Pn, MAXSYMLINKS);
1175		safestrprt(op ? op : arg, stderr, 1);
1176	    }
1177	    for (i = 0; i < sx; i++) {
1178		(void) free((FREE_P *)stk[i]);
1179		stk[i] = (char *)NULL;
1180	    }
1181	    (void) free((FREE_P *)stk);
1182	    stk = (char **)NULL;
1183	    ss = sx = 0;
1184	    op = (char *)NULL;
1185	    return((char *)NULL);
1186	}
1187	if (++sx > ss) {
1188	    if (!stk)
1189		stk = (char **)malloc((MALLOC_S)(sizeof(char *) * sx));
1190	    else
1191		stk = (char **)realloc((MALLOC_P *)stk,
1192					(MALLOC_S)(sizeof(char *) * sx));
1193	    if (!stk)
1194		goto no_readlink_space;
1195	    ss = sx;
1196	}
1197	stk[sx - 1] = s1;
1198	return(Readlink(s1));
1199}
1200
1201
1202#if	defined(HASSTREAMS)
1203/*
1204 * readstdata() - read stream's stdata structure
1205 */
1206
1207int
1208readstdata(addr, buf)
1209	KA_T addr;			/* stdata address in kernel*/
1210	struct stdata *buf;		/* buffer addess */
1211{
1212	if (!addr
1213	||  kread(addr, (char *)buf, sizeof(struct stdata))) {
1214	    (void) snpf(Namech, Namechl, "no stream data in %s",
1215		print_kptr(addr, (char *)NULL, 0));
1216	    return(1);
1217	}
1218	return(0);
1219}
1220
1221
1222/*
1223 * readsthead() - read stream head
1224 */
1225
1226int
1227readsthead(addr, buf)
1228	KA_T addr;			/* starting queue pointer in kernel */
1229	struct queue *buf;		/* buffer for queue head */
1230{
1231	KA_T qp;
1232
1233	if (!addr) {
1234	    (void) snpf(Namech, Namechl, "no stream queue head");
1235	    return(1);
1236	}
1237	for (qp = addr; qp; qp = (KA_T)buf->q_next) {
1238	    if (kread(qp, (char *)buf, sizeof(struct queue))) {
1239		(void) snpf(Namech, Namechl, "bad stream queue link at %s",
1240		    print_kptr(qp, (char *)NULL, 0));
1241		return(1);
1242	    }
1243	}
1244	return(0);
1245}
1246
1247
1248/*
1249 * readstidnm() - read stream module ID name
1250 */
1251
1252int
1253readstidnm(addr, buf, len)
1254	KA_T addr;			/* module ID name address in kernel */
1255	char *buf;			/* receiving buffer address */
1256	READLEN_T len;			/* buffer length */
1257{
1258	if (!addr || kread(addr, buf, len)) {
1259	    (void) snpf(Namech, Namechl, "can't read module ID name from %s",
1260		print_kptr(addr, (char *)NULL, 0));
1261	    return(1);
1262	}
1263	return(0);
1264}
1265
1266
1267/*
1268 * readstmin() - read stream's module info
1269 */
1270
1271int
1272readstmin(addr, buf)
1273	KA_T addr;			/* module info address in kernel */
1274	struct module_info *buf;	/* receiving buffer address */
1275{
1276	if (!addr || kread(addr, (char *)buf, sizeof(struct module_info))) {
1277	    (void) snpf(Namech, Namechl, "can't read module info from %s",
1278		print_kptr(addr, (char *)NULL, 0));
1279	    return(1);
1280	}
1281	return(0);
1282}
1283
1284
1285/*
1286 * readstqinit() - read stream's queue information structure
1287 */
1288
1289int
1290readstqinit(addr, buf)
1291	KA_T addr;			/* queue info address in kernel */
1292	struct qinit *buf;		/* receiving buffer address */
1293{
1294	if (!addr || kread(addr, (char *)buf, sizeof(struct qinit))) {
1295	    (void) snpf(Namech, Namechl, "can't read queue info from %s",
1296		print_kptr(addr, (char *)NULL, 0));
1297	    return(1);
1298	}
1299	return(0);
1300}
1301#endif	/* HASSTREAMS */
1302
1303
1304/*
1305 * safepup() - safely print an unprintable character -- i.e., print it in a
1306 *	       printable form
1307 *
1308 * return: char * to printable equivalent
1309 *	   cl = strlen(printable equivalent)
1310 */
1311
1312static char *
1313safepup(c, cl)
1314	unsigned int c;			/* unprintable (i.e., !isprint())
1315					 * character */
1316	int *cl;			/* returned printable strlen -- NULL if
1317					 * no return needed */
1318{
1319	int len;
1320	char *rp;
1321	static char up[8];
1322
1323	if (c < 0x20) {
1324	    switch (c) {
1325	    case '\b':
1326		rp = "\\b";
1327		break;
1328	    case '\f':
1329		rp = "\\f";
1330		break;
1331	    case '\n':
1332		rp = "\\n";
1333		break;
1334	    case '\r':
1335		rp = "\\r";
1336		break;
1337	    case '\t':
1338		rp = "\\t";
1339		break;
1340	    default:
1341		(void) snpf(up, sizeof(up), "^%c", c + 0x40);
1342		rp = up;
1343	    }
1344	    len = 2;
1345	} else if (c == 0xff) {
1346	    rp = "^?";
1347	    len = 2;
1348	} else {
1349	    (void) snpf(up, sizeof(up), "\\x%02x", (int)(c & 0xff));
1350	    rp = up;
1351	    len = 4;
1352	}
1353	if (cl)
1354	    *cl = len;
1355	return(rp);
1356}
1357
1358
1359/*
1360 * safestrlen() - calculate a "safe" string length -- i.e., compute space for
1361 *		  non-printable characters when printed in a printable form
1362 */
1363
1364int
1365safestrlen(sp, flags)
1366	char *sp;			/* string pointer */
1367	int flags;			/* flags:
1368					 *   bit 0: 0 (0) = no NL
1369					 *	    1 (1) = add trailing NL
1370					 *	 1: 0 (0) = ' ' printable
1371					 *	    1 (2) = ' ' not printable
1372					 */
1373{
1374	char c;
1375	int len = 0;
1376
1377	c = (flags & 2) ? ' ' : '\0';
1378	if (sp) {
1379	    for (; *sp; sp++) {
1380		if (!isprint((unsigned char)*sp) || *sp == c) {
1381		    if (*sp < 0x20 || (unsigned char)*sp == 0xff)
1382			len += 2;		/* length of \. or ^. form */
1383		    else
1384			len += 4;		/* length of "\x%02x" printf */
1385		} else
1386		    len++;
1387	    }
1388	}
1389	return(len);
1390}
1391
1392
1393/*
1394 * safestrprt() - print a string "safely" to the indicated stream -- i.e.,
1395 *		  print unprintable characters in a printable form
1396 */
1397
1398void
1399safestrprt(sp, fs, flags)
1400	char *sp;			/* string to print pointer pointer */
1401	FILE *fs;			/* destination stream -- e.g., stderr
1402					 * or stdout */
1403	int flags;			/* flags:
1404					 *   bit 0: 0 (0) = no NL
1405					 *	    1 (1) = add trailing NL
1406					 *	 1: 0 (0) = ' ' printable
1407					 *	    1 (2) = ' ' not printable
1408					 *	 2: 0 (0) = print string as is
1409					 *	    1 (4) = surround string
1410					 *		    with '"'
1411					 *	 4: 0 (0) = print ending '\n'
1412					 *	    1 (8) = don't print ending
1413					 *		    '\n'
1414					 */
1415{
1416	char c;
1417	int lnc, lnt, sl;
1418
1419#if	defined(HASWIDECHAR)
1420	wchar_t w;
1421	int wcmx = MB_CUR_MAX;
1422#else	/* !defined(HASWIDECHAR) */
1423	static int wcmx = 1;
1424#endif	/* defined(HASWIDECHAR) */
1425
1426	c = (flags & 2) ? ' ' : '\0';
1427	if (flags & 4)
1428	    putc('"', fs);
1429	if (sp) {
1430	    for (sl = strlen(sp); *sp; sl -= lnc, sp += lnc) {
1431
1432#if	defined(HASWIDECHAR)
1433		if (wcmx > 1) {
1434		    lnc = mblen(sp, sl);
1435		    if (lnc > 1) {
1436			if ((mbtowc(&w, sp, sl) == lnc) && iswprint(w)) {
1437			    for (lnt = 0; lnt < lnc; lnt++) {
1438				putc((int)*(sp + lnt), fs);
1439			    }
1440			} else {
1441			    for (lnt = 0; lnt < lnc; lnt++) {
1442			        fputs(safepup((unsigned int)*(sp + lnt),
1443					      (int *)NULL), fs);
1444			    }
1445			}
1446			continue;
1447		    } else
1448			lnc = 1;
1449		} else
1450		    lnc = 1;
1451#else	/* !defined(HASWIDECHAR) */
1452		lnc = 1;
1453#endif	/* defined(HASWIDECHAR) */
1454
1455		if (isprint((unsigned char)*sp) && *sp != c)
1456		    putc((int)(*sp & 0xff), fs);
1457		else {
1458		    if ((flags & 8) && (*sp == '\n') && !*(sp + 1))
1459			break;
1460		    fputs(safepup((unsigned int)*sp, (int *)NULL), fs);
1461		}
1462	    }
1463	}
1464	if (flags & 4)
1465	    putc('"', fs);
1466	if (flags & 1)
1467	    putc('\n', fs);
1468}
1469
1470
1471/*
1472 * safestrprtn() - print a specified number of characters from a string
1473 *		   "safely" to the indicated stream
1474 */
1475
1476void
1477safestrprtn(sp, len, fs, flags)
1478	char *sp;			/* string to print pointer pointer */
1479	int len;			/* safe number of characters to
1480					 * print */
1481	FILE *fs;			/* destination stream -- e.g., stderr
1482					 * or stdout */
1483	int flags;			/* flags:
1484					 *   bit 0: 0 (0) = no NL
1485					 *	    1 (1) = add trailing NL
1486					 *	 1: 0 (0) = ' ' printable
1487					 *	    1 (2) = ' ' not printable
1488					 *	 2: 0 (0) = print string as is
1489					 *	    1 (4) = surround string
1490					 *		    with '"'
1491					 *	 4: 0 (0) = print ending '\n'
1492					 *	    1 (8) = don't print ending
1493					 *		    '\n'
1494					 */
1495{
1496	char c, *up;
1497	int cl, i;
1498
1499	if (flags & 4)
1500	    putc('"', fs);
1501	if (sp) {
1502	    c = (flags & 2) ? ' ' : '\0';
1503	    for (i = 0; i < len && *sp; sp++) {
1504		if (isprint((unsigned char)*sp) && *sp != c) {
1505		    putc((int)(*sp & 0xff), fs);
1506		    i++;
1507		} else {
1508		    if ((flags & 8) && (*sp == '\n') && !*(sp + 1))
1509			break;
1510		    up = safepup((unsigned int)*sp, &cl);
1511		    if ((i + cl) > len)
1512			break;
1513		    fputs(up, fs);
1514		    i += cl;
1515		}
1516	    }
1517	} else
1518	    i = 0;
1519	for (; i < len; i++)
1520	    putc(' ', fs);
1521	if (flags & 4)
1522	    putc('"', fs);
1523	if (flags & 1)
1524	    putc('\n', fs);
1525}
1526
1527
1528/*
1529 * statsafely() - stat path safely (i. e., with timeout)
1530 */
1531
1532int
1533statsafely(path, buf)
1534	char *path;			/* file path */
1535	struct stat *buf;		/* stat buffer address */
1536{
1537	if (Fblock) {
1538	    if (!Fwarn)
1539		(void) fprintf(stderr,
1540		    "%s: avoiding stat(%s): -b was specified.\n",
1541		    Pn, path);
1542	    errno = EWOULDBLOCK;
1543	    return(1);
1544	}
1545	return(doinchild(dostat, path, (char *)buf, sizeof(struct stat)));
1546}
1547
1548
1549/*
1550 * stkdir() - stack directory name
1551 */
1552
1553void
1554stkdir(p)
1555	char *p;		/* directory path */
1556{
1557	MALLOC_S len;
1558/*
1559 * Provide adequate space for directory stack pointers.
1560 */
1561	if (Dstkx >= Dstkn) {
1562	    Dstkn += 128;
1563	    len = (MALLOC_S)(Dstkn * sizeof(char *));
1564	    if (!Dstk)
1565		Dstk = (char **)malloc(len);
1566	    else
1567		Dstk = (char **)realloc((MALLOC_P *)Dstk, len);
1568	    if (!Dstk) {
1569		(void) fprintf(stderr,
1570		    "%s: no space for directory stack at: ", Pn);
1571		safestrprt(p, stderr, 1);
1572		Exit(1);
1573	    }
1574	}
1575/*
1576 * Allocate space for the name, copy it there and put its pointer on the stack.
1577 */
1578	if (!(Dstk[Dstkx] = mkstrcpy(p, (MALLOC_S *)NULL))) {
1579	    (void) fprintf(stderr, "%s: no space for: ", Pn);
1580	    safestrprt(p, stderr, 1);
1581	    Exit(1);
1582	}
1583	Dstkx++;
1584}
1585
1586
1587/*
1588 * x2dev() - convert hexadecimal ASCII string to device number
1589 */
1590
1591char *
1592x2dev(s, d)
1593	char *s;			/* ASCII string */
1594	dev_t *d;			/* device receptacle */
1595{
1596	char *cp, *cp1;
1597	int n;
1598	dev_t r;
1599
1600/*
1601 * Skip an optional leading 0x.  Count the number of hex digits up to the end
1602 * of the string, or to a space, or to a comma.  Return an error if an unknown
1603 * character is encountered.  If the count is larger than (2 * sizeof(dev_t))
1604 * -- e.g., because of sign extension -- ignore excess leading hex 0xf digits,
1605 * but return an error if an excess leading digit isn't 0xf.
1606 */
1607	if  (strncasecmp(s, "0x", 2) == 0)
1608		s += 2;
1609	for (cp = s, n = 0; *cp; cp++, n++) {
1610	    if (isdigit((unsigned char)*cp))
1611		continue;
1612	    if ((unsigned char)*cp >= 'a' && (unsigned char)*cp <= 'f')
1613		continue;
1614	    if ((unsigned char)*cp >= 'A' && (unsigned char)*cp <= 'F')
1615		continue;
1616	    if (*cp == ' ' || *cp == ',')
1617		break;
1618	    return((char *)NULL);
1619	}
1620	if (!n)
1621	    return((char *)NULL);
1622	if (n > (2 * (int)sizeof(dev_t))) {
1623	    cp1 = s;
1624	    s += (n - (2 * sizeof(dev_t)));
1625	    while (cp1 < s) {
1626		if (*cp1 != 'f' && *cp1 != 'F')
1627		    return((char *)NULL);
1628		cp1++;
1629	    }
1630	}
1631/*
1632 * Assemble the validated hex digits of the device number, starting at a point
1633 * in the string relevant to sizeof(dev_t).
1634 */
1635	for (r = 0; s < cp; s++) {
1636	    r = r << 4;
1637	    if (isdigit((unsigned char)*s))
1638		r |= (unsigned char)(*s - '0') & 0xf;
1639	    else {
1640		if (isupper((unsigned char)*s))
1641		    r |= ((unsigned char)(*s - 'A') + 10) & 0xf;
1642		else
1643		    r |= ((unsigned char)(*s - 'a') + 10) & 0xf;
1644	    }
1645	}
1646	*d = r;
1647	return(s);
1648}
1649