| /* |
| * misc.c - common miscellaneous functions for lsof |
| */ |
| |
| |
| /* |
| * Copyright 1994 Purdue Research Foundation, West Lafayette, Indiana |
| * 47907. All rights reserved. |
| * |
| * Written by Victor A. Abell |
| * |
| * This software is not subject to any license of the American Telephone |
| * and Telegraph Company or the Regents of the University of California. |
| * |
| * Permission is granted to anyone to use this software for any purpose on |
| * any computer system, and to alter it and redistribute it freely, subject |
| * to the following restrictions: |
| * |
| * 1. Neither the authors nor Purdue University are responsible for any |
| * consequences of the use of this software. |
| * |
| * 2. The origin of this software must not be misrepresented, either by |
| * explicit claim or by omission. Credit to the authors and Purdue |
| * University must appear in documentation and sources. |
| * |
| * 3. Altered versions must be plainly marked as such, and must not be |
| * misrepresented as being the original software. |
| * |
| * 4. This notice may not be removed or altered. |
| */ |
| |
| #ifndef lint |
| static char copyright[] = |
| "@(#) Copyright 1994 Purdue Research Foundation.\nAll rights reserved.\n"; |
| static char *rcsid = "$Id: misc.c,v 1.26 2008/10/21 16:21:41 abe Exp $"; |
| #endif |
| |
| |
| #include "lsof.h" |
| |
| #if defined(HASWIDECHAR) && defined(WIDECHARINCL) |
| #include WIDECHARINCL |
| #endif /* defined(HASWIDECHAR) && defined(WIDECHARINCL) */ |
| |
| |
| /* |
| * Local definitions |
| */ |
| |
| #if !defined(MAXSYMLINKS) |
| #define MAXSYMLINKS 32 |
| #endif /* !defined(MAXSYMLINKS) */ |
| |
| |
| /* |
| * Local function prototypes |
| */ |
| |
| _PROTOTYPE(static void closePipes,(void)); |
| _PROTOTYPE(static int dolstat,(char *path, char *buf, int len)); |
| _PROTOTYPE(static int dostat,(char *path, char *buf, int len)); |
| _PROTOTYPE(static int doreadlink,(char *path, char *buf, int len)); |
| _PROTOTYPE(static int doinchild,(int (*fn)(), char *fp, char *rbuf, int rbln)); |
| |
| #if defined(HASINTSIGNAL) |
| _PROTOTYPE(static int handleint,(int sig)); |
| #else /* !defined(HASINTSIGNAL) */ |
| _PROTOTYPE(static void handleint,(int sig)); |
| #endif /* defined(HASINTSIGNAL) */ |
| |
| _PROTOTYPE(static char *safepup,(unsigned int c, int *cl)); |
| |
| |
| /* |
| * Local variables |
| */ |
| |
| static pid_t Cpid = 0; /* child PID */ |
| static jmp_buf Jmp_buf; /* jump buffer */ |
| static int Pipes[] = /* pipes for child process */ |
| { -1, -1, -1, -1 }; |
| static int CtSigs[] = { 0, SIGINT, SIGKILL }; |
| /* child termination signals (in order |
| * of application) -- the first is a |
| * dummy to allow pipe closure to |
| * cause the child to exit */ |
| #define NCTSIGS (sizeof(CtSigs) / sizeof(int)) |
| |
| |
| #if defined(HASNLIST) |
| /* |
| * build-Nl() - build kernel name list table |
| */ |
| |
| static struct drive_Nl *Build_Nl = (struct drive_Nl *)NULL; |
| /* the default Drive_Nl address */ |
| |
| void |
| build_Nl(d) |
| struct drive_Nl *d; /* data to drive the construction */ |
| { |
| struct drive_Nl *dp; |
| int i, n; |
| |
| for (dp = d, n = 0; dp->nn; dp++, n++) |
| ; |
| if (n < 1) { |
| (void) fprintf(stderr, |
| "%s: can't calculate kernel name list length\n", Pn); |
| Exit(1); |
| } |
| if (!(Nl = (struct NLIST_TYPE *)calloc((n + 1), |
| sizeof(struct NLIST_TYPE)))) |
| { |
| (void) fprintf(stderr, |
| "%s: can't allocate %d bytes to kernel name list structure\n", |
| Pn, (int)((n + 1) * sizeof(struct NLIST_TYPE))); |
| Exit(1); |
| } |
| for (dp = d, i = 0; i < n; dp++, i++) { |
| Nl[i].NL_NAME = dp->knm; |
| } |
| Nll = (int)((n + 1) * sizeof(struct NLIST_TYPE)); |
| Build_Nl = d; |
| } |
| #endif /* defined(HASNLIST) */ |
| |
| |
| /* |
| * childx() - make child process exit (if possible) |
| */ |
| |
| void |
| childx() |
| { |
| static int at, sx; |
| pid_t wpid; |
| |
| if (Cpid > 1) { |
| |
| /* |
| * First close the pipes to and from the child. That should cause the |
| * child to exit. Compute alarm time shares. |
| */ |
| (void) closePipes(); |
| if ((at = TmLimit / NCTSIGS) < TMLIMMIN) |
| at = TMLIMMIN; |
| /* |
| * Loop, waiting for the child to exit. After the first pass, help |
| * the child exit by sending it signals. |
| */ |
| for (sx = 0; sx < NCTSIGS; sx++) { |
| if (setjmp(Jmp_buf)) { |
| |
| /* |
| * An alarm has rung. Disable further alarms. |
| * |
| * If there are more signals to send, continue the signal loop. |
| * |
| * If the last signal has been sent, issue a warning (unless |
| * warninge have been suppressed) and exit the signal loop. |
| */ |
| (void) alarm(0); |
| (void) signal(SIGALRM, SIG_DFL); |
| if (sx < (NCTSIGS - 1)) |
| continue; |
| if (!Fwarn) |
| (void) fprintf(stderr, |
| "%s: WARNING -- child process %d may be hung.\n", |
| Pn, (int)Cpid); |
| break; |
| } |
| /* |
| * Send the next signal to the child process, after the first pass |
| * through the loop. |
| * |
| * Wrap the wait() with an alarm. |
| */ |
| if (sx) |
| (void) kill(Cpid, CtSigs[sx]); |
| (void) signal(SIGALRM, handleint); |
| (void) alarm(at); |
| wpid = (pid_t) wait(NULL); |
| (void) alarm(0); |
| (void) signal(SIGALRM, SIG_DFL); |
| if (wpid == Cpid) |
| break; |
| } |
| Cpid = 0; |
| } |
| } |
| |
| |
| /* |
| * closePipes() - close open pipe file descriptors |
| */ |
| |
| static void |
| closePipes() |
| { |
| int i; |
| |
| for (i = 0; i < 4; i++) { |
| if (Pipes[i] >= 0) { |
| (void) close(Pipes[i]); |
| Pipes[i] = -1; |
| } |
| } |
| } |
| |
| |
| /* |
| * compdev() - compare Devtp[] entries |
| */ |
| |
| int |
| compdev(a1, a2) |
| COMP_P *a1, *a2; |
| { |
| struct l_dev **p1 = (struct l_dev **)a1; |
| struct l_dev **p2 = (struct l_dev **)a2; |
| |
| if ((dev_t)((*p1)->rdev) < (dev_t)((*p2)->rdev)) |
| return(-1); |
| if ((dev_t)((*p1)->rdev) > (dev_t)((*p2)->rdev)) |
| return(1); |
| if ((INODETYPE)((*p1)->inode) < (INODETYPE)((*p2)->inode)) |
| return(-1); |
| if ((INODETYPE)((*p1)->inode) > (INODETYPE)((*p2)->inode)) |
| return(1); |
| return(strcmp((*p1)->name, (*p2)->name)); |
| } |
| |
| |
| /* |
| * doinchild() -- do a function in a child process |
| */ |
| |
| static int |
| doinchild(fn, fp, rbuf, rbln) |
| int (*fn)(); /* function to perform */ |
| char *fp; /* function parameter */ |
| char *rbuf; /* response buffer */ |
| int rbln; /* response buffer length */ |
| { |
| int en, rv; |
| /* |
| * Check reply buffer size. |
| */ |
| if (!Fovhd && rbln > MAXPATHLEN) { |
| (void) fprintf(stderr, |
| "%s: doinchild error; response buffer too large: %d\n", |
| Pn, rbln); |
| Exit(1); |
| } |
| /* |
| * Set up to handle an alarm signal; handle an alarm signal; build |
| * pipes for exchanging information with a child process; start the |
| * child process; and perform functions in the child process. |
| */ |
| if (!Fovhd) { |
| if (setjmp(Jmp_buf)) { |
| |
| /* |
| * Process an alarm that has rung. |
| */ |
| (void) alarm(0); |
| (void) signal(SIGALRM, SIG_DFL); |
| (void) childx(); |
| errno = ETIMEDOUT; |
| return(1); |
| } else if (!Cpid) { |
| |
| /* |
| * Create pipes to exchange function information with a child |
| * process. |
| */ |
| if (pipe(Pipes) < 0 || pipe(&Pipes[2]) < 0) { |
| (void) fprintf(stderr, "%s: can't open pipes: %s\n", |
| Pn, strerror(errno)); |
| Exit(1); |
| } |
| /* |
| * Fork a child to execute functions. |
| */ |
| if ((Cpid = fork()) == 0) { |
| |
| /* |
| * Begin the child process. |
| */ |
| |
| int fd, nd, r_al, r_rbln; |
| char r_arg[MAXPATHLEN+1], r_rbuf[MAXPATHLEN+1]; |
| int (*r_fn)(); |
| /* |
| * Close all open file descriptors except Pipes[0] and |
| * Pipes[3]. |
| */ |
| for (fd = 0, nd = GET_MAX_FD(); fd < nd; fd++) { |
| if (fd == Pipes[0] || fd == Pipes[3]) |
| continue; |
| (void) close(fd); |
| if (fd == Pipes[1]) |
| Pipes[1] = -1; |
| else if (fd == Pipes[2]) |
| Pipes[2] = -1; |
| } |
| if (Pipes[1] >= 0) { |
| (void) close(Pipes[1]); |
| Pipes[1] = -1; |
| } |
| if (Pipes[2] >= 0) { |
| (void) close(Pipes[2]); |
| Pipes[2] = -1; |
| } |
| /* |
| * Read function requests, process them, and return replies. |
| */ |
| for (;;) { |
| if (read(Pipes[0], (char *)&r_fn, sizeof(r_fn)) |
| != (int)sizeof(r_fn) |
| || read(Pipes[0], (char *)&r_al, sizeof(int)) |
| != (int)sizeof(int) |
| || r_al < 1 |
| || r_al > (int)sizeof(r_arg) |
| || read(Pipes[0], r_arg, r_al) != r_al |
| || read(Pipes[0], (char *)&r_rbln, sizeof(r_rbln)) |
| != (int)sizeof(r_rbln) |
| || r_rbln < 1 || r_rbln > (int)sizeof(r_rbuf)) |
| break; |
| rv = r_fn(r_arg, r_rbuf, r_rbln); |
| en = errno; |
| if (write(Pipes[3], (char *)&rv, sizeof(rv)) |
| != sizeof(rv) |
| || write(Pipes[3], (char *)&en, sizeof(en)) |
| != sizeof(en) |
| || write(Pipes[3], r_rbuf, r_rbln) != r_rbln) |
| break; |
| } |
| (void) _exit(0); |
| } |
| /* |
| * Continue in the parent process to finish the setup. |
| */ |
| if (Cpid < 0) { |
| (void) fprintf(stderr, "%s: can't fork: %s\n", |
| Pn, strerror(errno)); |
| Exit(1); |
| } |
| (void) close(Pipes[0]); |
| (void) close(Pipes[3]); |
| Pipes[0] = Pipes[3] = -1; |
| } |
| } |
| if (!Fovhd) { |
| int len; |
| |
| /* |
| * Send a function to the child and wait for the response. |
| */ |
| len = strlen(fp) + 1; |
| (void) signal(SIGALRM, handleint); |
| (void) alarm(TmLimit); |
| if (write(Pipes[1], (char *)&fn, sizeof(fn)) != sizeof(fn) |
| || write(Pipes[1], (char *)&len, sizeof(len)) != sizeof(len) |
| || write(Pipes[1], fp, len) != len |
| || write(Pipes[1], (char *)&rbln, sizeof(rbln)) != sizeof(rbln) |
| || read(Pipes[2], (char *)&rv, sizeof(rv)) != sizeof(rv) |
| || read(Pipes[2], (char *)&en, sizeof(en)) != sizeof(en) |
| || read(Pipes[2], rbuf, rbln) != rbln) { |
| (void) alarm(0); |
| (void) signal(SIGALRM, SIG_DFL); |
| (void) childx(); |
| errno = ECHILD; |
| return(-1); |
| } |
| } else { |
| |
| /* |
| * Do the operation directly -- not in a child. |
| */ |
| (void) signal(SIGALRM, handleint); |
| (void) alarm(TmLimit); |
| rv = fn(fp, rbuf, rbln); |
| en = errno; |
| } |
| /* |
| * Function completed, response collected -- complete the operation. |
| */ |
| (void) alarm(0); |
| (void) signal(SIGALRM, SIG_DFL); |
| errno = en; |
| return(rv); |
| } |
| |
| |
| /* |
| * dolstat() - do an lstat() function |
| */ |
| |
| static int |
| dolstat(path, rbuf, rbln) |
| char *path; /* path */ |
| char *rbuf; /* response buffer */ |
| int rbln; /* response buffer length */ |
| |
| /* ARGSUSED */ |
| |
| { |
| return(lstat(path, (struct stat *)rbuf)); |
| } |
| |
| |
| /* |
| * doreadlink() -- do a readlink() function |
| */ |
| |
| static int |
| doreadlink(path, rbuf, rbln) |
| char *path; /* path */ |
| char *rbuf; /* response buffer */ |
| int rbln; /* response buffer length */ |
| { |
| return(readlink(path, rbuf, rbln)); |
| } |
| |
| |
| /* |
| * dostat() - do a stat() function |
| */ |
| |
| static int |
| dostat(path, rbuf, rbln) |
| char *path; /* path */ |
| char *rbuf; /* response buffer */ |
| int rbln; /* response buffer length */ |
| |
| /* ARGSUSED */ |
| |
| { |
| return(stat(path, (struct stat *)rbuf)); |
| } |
| |
| |
| #if defined(WILLDROPGID) |
| /* |
| * dropgid() - drop setgid permission |
| */ |
| |
| void |
| dropgid() |
| { |
| if (!Setuidroot && Setgid) { |
| if (setgid(Mygid) < 0) { |
| (void) fprintf(stderr, "%s: can't setgid(%d): %s\n", |
| Pn, (int)Mygid, strerror(errno)); |
| Exit(1); |
| } |
| Setgid = 0; |
| } |
| } |
| #endif /* defined(WILLDROPGID) */ |
| |
| |
| /* |
| * enter_dev_ch() - enter device characters in file structure |
| */ |
| |
| void |
| enter_dev_ch(m) |
| char *m; |
| { |
| char *mp; |
| |
| if (!m || *m == '\0') |
| return; |
| if (!(mp = mkstrcpy(m, (MALLOC_S *)NULL))) { |
| (void) fprintf(stderr, "%s: no more dev_ch space at PID %d: \n", |
| Pn, Lp->pid); |
| safestrprt(m, stderr, 1); |
| Exit(1); |
| } |
| if (Lf->dev_ch) |
| (void) free((FREE_P *)Lf->dev_ch); |
| Lf->dev_ch = mp; |
| } |
| |
| |
| /* |
| * enter_IPstate() -- enter a TCP or UDP state |
| */ |
| |
| void |
| enter_IPstate(ty, nm, nr) |
| char *ty; /* type -- TCP or UDP */ |
| char *nm; /* state name (may be NULL) */ |
| int nr; /* state number */ |
| { |
| |
| #if defined(USE_LIB_PRINT_TCPTPI) |
| TcpNstates = nr; |
| #else /* !defined(USE_LIB_PRINT_TCPTPI) */ |
| |
| int al, i, j, oc, nn, ns, off, tx; |
| char *cp; |
| MALLOC_S len; |
| /* |
| * Check the type name and set the type index. |
| */ |
| if (!ty) { |
| (void) fprintf(stderr, |
| "%s: no type specified to enter_IPstate()\n", Pn); |
| Exit(1); |
| } |
| if (!strcmp(ty, "TCP")) |
| tx = 0; |
| else if (!strcmp(ty, "UDP")) |
| tx = 1; |
| else { |
| (void) fprintf(stderr, "%s: unknown type for enter_IPstate: %s\n", |
| Pn, ty); |
| Exit(1); |
| } |
| /* |
| * If the name argument is NULL, reduce the allocated table to its minimum |
| * size. |
| */ |
| if (!nm) { |
| if (tx) { |
| if (UdpSt) { |
| if (!UdpNstates) { |
| (void) free((MALLOC_P *)UdpSt); |
| UdpSt = (char **)NULL; |
| } |
| if (UdpNstates < UdpStAlloc) { |
| len = (MALLOC_S)(UdpNstates * sizeof(char *)); |
| if (!(UdpSt = (char **)realloc((MALLOC_P *)UdpSt, len))) |
| { |
| (void) fprintf(stderr, |
| "%s: can't reduce UdpSt[]\n", Pn); |
| Exit(1); |
| } |
| } |
| UdpStAlloc = UdpNstates; |
| } |
| } else { |
| if (TcpSt) { |
| if (!TcpNstates) { |
| (void) free((MALLOC_P *)TcpSt); |
| TcpSt = (char **)NULL; |
| } |
| if (TcpNstates < TcpStAlloc) { |
| len = (MALLOC_S)(TcpNstates * sizeof(char *)); |
| if (!(TcpSt = (char **)realloc((MALLOC_P *)TcpSt, len))) |
| { |
| (void) fprintf(stderr, |
| "%s: can't reduce TcpSt[]\n", Pn); |
| Exit(1); |
| } |
| } |
| TcpStAlloc = TcpNstates; |
| } |
| } |
| return; |
| } |
| /* |
| * Check the name and number. |
| */ |
| if ((len = (size_t)strlen(nm)) < 1) { |
| (void) fprintf(stderr, |
| "%s: bad %s name (\"%s\"), number=%d\n", Pn, ty, nm, nr); |
| Exit(1); |
| } |
| /* |
| * Make a copy of the name. |
| */ |
| if (!(cp = mkstrcpy(nm, (MALLOC_S *)NULL))) { |
| (void) fprintf(stderr, |
| "%s: enter_IPstate(): no %s space for %s\n", |
| Pn, ty, nm); |
| Exit(1); |
| } |
| /* |
| * Set the necessary offset for using nr as an index. If it is |
| * a new offset, adjust previous entries. |
| */ |
| if ((nr < 0) && ((off = -nr) > (tx ? UdpStOff : TcpStOff))) { |
| if (tx ? UdpSt : TcpSt) { |
| |
| /* |
| * A new, larger offset (smaller negative state number) could mean |
| * a previously allocated state table must be enlarged and its |
| * previous entries moved. |
| */ |
| oc = off - (tx ? UdpStOff : TcpStOff); |
| al = tx ? UdpStAlloc : TcpStAlloc; |
| ns = tx ? UdpNstates : TcpNstates; |
| if ((nn = ns + oc) >= al) { |
| while ((nn + 5) > al) { |
| al += TCPUDPALLOC; |
| } |
| len = (MALLOC_S)(al * sizeof(char *)); |
| if (tx) { |
| if (!(UdpSt = (char **)realloc((MALLOC_P *)UdpSt, len))) |
| goto no_IP_space; |
| UdpStAlloc = al; |
| } else { |
| if (!(TcpSt = (char **)realloc((MALLOC_P *)TcpSt, len))) |
| goto no_IP_space; |
| TcpStAlloc = al; |
| } |
| for (i = 0, j = oc; i < oc; i++, j++) { |
| if (tx) { |
| if (i < UdpNstates) |
| UdpSt[j] = UdpSt[i]; |
| UdpSt[i] = (char *)NULL; |
| } else { |
| if (i < TcpNstates) |
| TcpSt[j] = TcpSt[i]; |
| TcpSt[i] = (char *)NULL; |
| } |
| } |
| if (tx) |
| UdpNstates += oc; |
| else |
| TcpNstates += oc; |
| } |
| } |
| if (tx) |
| UdpStOff = off; |
| else |
| TcpStOff = off; |
| } |
| /* |
| * Enter name as {Tc|Ud}pSt[nr + {Tc|Ud}pStOff]. |
| * |
| * Allocate space, as required. |
| */ |
| al = tx ? UdpStAlloc : TcpStAlloc; |
| off = tx ? UdpStOff : TcpStOff; |
| nn = nr + off + 1; |
| if (nn > al) { |
| i = tx ? UdpNstates : TcpNstates; |
| while ((nn + 5) > al) { |
| al += TCPUDPALLOC; |
| } |
| len = (MALLOC_S)(al * sizeof(char *)); |
| if (tx) { |
| if (UdpSt) |
| UdpSt = (char **)realloc((MALLOC_P *)UdpSt, len); |
| else |
| UdpSt = (char **)malloc(len); |
| if (!UdpSt) { |
| |
| no_IP_space: |
| |
| (void) fprintf(stderr, "%s: no %s state space\n", Pn, ty); |
| Exit(1); |
| } |
| UdpNstates = nn; |
| UdpStAlloc = al; |
| } else { |
| if (TcpSt) |
| TcpSt = (char **)realloc((MALLOC_P *)TcpSt, len); |
| else |
| TcpSt = (char **)malloc(len); |
| if (!TcpSt) |
| goto no_IP_space; |
| TcpNstates = nn; |
| TcpStAlloc = al; |
| } |
| while (i < al) { |
| if (tx) |
| UdpSt[i] = (char *)NULL; |
| else |
| TcpSt[i] = (char *)NULL; |
| i++; |
| } |
| } else { |
| if (tx) { |
| if (nn > UdpNstates) |
| UdpNstates = nn; |
| } else { |
| if (nn > TcpNstates) |
| TcpNstates = nn; |
| } |
| } |
| if (tx) { |
| if (UdpSt[nr + UdpStOff]) { |
| |
| dup_IP_state: |
| |
| (void) fprintf(stderr, |
| "%s: duplicate %s state %d (already %s): %s\n", |
| Pn, ty, nr, |
| tx ? UdpSt[nr + UdpStOff] : TcpSt[nr + TcpStOff], |
| nm); |
| Exit(1); |
| } |
| UdpSt[nr + UdpStOff] = cp; |
| } else { |
| if (TcpSt[nr + TcpStOff]) |
| goto dup_IP_state; |
| TcpSt[nr + TcpStOff] = cp; |
| } |
| #endif /* defined(USE_LIB_PRINT_TCPTPI) */ |
| |
| } |
| |
| |
| /* |
| * enter_nm() - enter name in local file structure |
| */ |
| |
| void |
| enter_nm(m) |
| char *m; |
| { |
| char *mp; |
| |
| if (!m || *m == '\0') |
| return; |
| if (!(mp = mkstrcpy(m, (MALLOC_S *)NULL))) { |
| (void) fprintf(stderr, "%s: no more nm space at PID %d for: ", |
| Pn, Lp->pid); |
| safestrprt(m, stderr, 1); |
| Exit(1); |
| } |
| if (Lf->nm) |
| (void) free((FREE_P *)Lf->nm); |
| Lf->nm = mp; |
| } |
| |
| |
| /* |
| * Exit() - do a clean exit() |
| */ |
| |
| void |
| Exit(xv) |
| int xv; /* exit() value */ |
| { |
| (void) childx(); |
| |
| #if defined(HASDCACHE) |
| if (DCrebuilt && !Fwarn) |
| (void) fprintf(stderr, "%s: WARNING: %s was updated.\n", |
| Pn, DCpath[DCpathX]); |
| #endif /* defined(HASDCACHE) */ |
| |
| exit(xv); |
| } |
| |
| |
| #if defined(HASNLIST) |
| /* |
| * get_Nl_value() - get Nl value for nickname |
| */ |
| |
| int |
| get_Nl_value(nn, d, v) |
| char *nn; /* nickname of requested entry */ |
| struct drive_Nl *d; /* drive_Nl table that built Nl |
| * (if NULL, use Build_Nl) */ |
| KA_T *v; /* returned value (if NULL, |
| * return nothing) */ |
| { |
| int i; |
| |
| if (!Nl || !Nll) |
| return(-1); |
| if (!d) |
| d = Build_Nl; |
| for (i = 0; d->nn; d++, i++) { |
| if (strcmp(d->nn, nn) == 0) { |
| if (v) |
| *v = (KA_T)Nl[i].n_value; |
| return(i); |
| } |
| } |
| return(-1); |
| } |
| #endif /* defined(HASNLIST) */ |
| |
| |
| /* |
| * handleint() - handle an interrupt |
| */ |
| |
| #if defined(HASINTSIGNAL) |
| static int |
| #else |
| static void |
| #endif |
| |
| /* ARGSUSED */ |
| |
| handleint(sig) |
| int sig; |
| { |
| longjmp(Jmp_buf, 1); |
| } |
| |
| |
| /* |
| * hashbyname() - hash by name |
| */ |
| |
| int |
| hashbyname(nm, mod) |
| char *nm; /* pointer to NUL-terminated name */ |
| int mod; /* hash modulus */ |
| { |
| int i, j; |
| |
| for (i = j = 0; *nm; nm++) { |
| i ^= (int)*nm << j; |
| if (++j > 7) |
| j = 0; |
| } |
| return(((int)(i * 31415)) & (mod - 1)); |
| } |
| |
| |
| /* |
| * is_nw_addr() - is this network address selected? |
| */ |
| |
| int |
| is_nw_addr(ia, p, af) |
| unsigned char *ia; /* Internet address */ |
| int p; /* port */ |
| int af; /* address family -- e.g., AF_INET, |
| * AF_INET6 */ |
| { |
| struct nwad *n; |
| |
| if (!(n = Nwad)) |
| return(0); |
| for (; n; n = n->next) { |
| if (n->proto) { |
| if (strcasecmp(n->proto, Lf->iproto) != 0) |
| continue; |
| } |
| if (af && n->af && af != n->af) |
| continue; |
| |
| #if defined(HASIPv6) |
| if (af == AF_INET6) { |
| if (n->a[15] || n->a[14] || n->a[13] || n->a[12] |
| || n->a[11] || n->a[10] || n->a[9] || n->a[8] |
| || n->a[7] || n->a[6] || n->a[5] || n->a[4] |
| || n->a[3] || n->a[2] || n->a[1] || n->a[0]) { |
| if (ia[15] != n->a[15] || ia[14] != n->a[14] |
| || ia[13] != n->a[13] || ia[12] != n->a[12] |
| || ia[11] != n->a[11] || ia[10] != n->a[10] |
| || ia[9] != n->a[9] || ia[8] != n->a[8] |
| || ia[7] != n->a[7] || ia[6] != n->a[6] |
| || ia[5] != n->a[5] || ia[4] != n->a[4] |
| || ia[3] != n->a[3] || ia[2] != n->a[2] |
| || ia[1] != n->a[1] || ia[0] != n->a[0]) |
| continue; |
| } |
| } else if (af == AF_INET) |
| #endif /* defined(HASIPv6) */ |
| |
| { |
| if (n->a[3] || n->a[2] || n->a[1] || n->a[0]) { |
| if (ia[3] != n->a[3] || ia[2] != n->a[2] |
| || ia[1] != n->a[1] || ia[0] != n->a[0]) |
| continue; |
| } |
| } |
| |
| #if defined(HASIPv6) |
| else |
| continue; |
| #endif /* defined(HASIPv6) */ |
| |
| if (n->sport == -1 || (p >= n->sport && p <= n->eport)) { |
| n->f = 1; |
| return(1); |
| } |
| } |
| return(0); |
| } |
| |
| |
| /* |
| * mkstrcpy() - make a string copy in malloc()'d space |
| * |
| * return: copy pointer |
| * copy length (optional) |
| */ |
| |
| char * |
| mkstrcpy(src, rlp) |
| char *src; /* source */ |
| MALLOC_S *rlp; /* returned length pointer (optional) |
| * The returned length is an strlen() |
| * equivalent */ |
| { |
| MALLOC_S len; |
| char *ns; |
| |
| len = (MALLOC_S)(src ? strlen(src) : 0); |
| ns = (char *)malloc(len + 1); |
| if (ns) { |
| if (src) |
| (void) snpf(ns, len + 1, "%s", src); |
| else |
| *ns = '\0'; |
| } |
| if (rlp) |
| *rlp = len; |
| return(ns); |
| } |
| |
| |
| /* |
| * mkstrcat() - make a catenated copy of up to three strings under optional |
| * string-by-string count control |
| * |
| * return: copy pointer |
| * copy string length (optional) |
| */ |
| |
| char * |
| mkstrcat(s1, l1, s2, l2, s3, l3, clp) |
| char *s1; /* source string 1 */ |
| int l1; /* length of string 1 (-1 if none) */ |
| char *s2; /* source string 2 */ |
| int l2; /* length of string 2 (-1 if none) */ |
| char *s3; /* source string 3 (optional) */ |
| int l3 ; /* length of string 3 (-1 if none) */ |
| MALLOC_S *clp; /* pointer to return of copy length |
| * (optional) */ |
| { |
| MALLOC_S cl, len1, len2, len3; |
| char *cp; |
| |
| if (s1) |
| len1 = (MALLOC_S)((l1 >= 0) ? l1 : strlen(s1)); |
| else |
| len1 = (MALLOC_S)0; |
| if (s2) |
| len2 = (MALLOC_S)((l2 >= 0) ? l2 : strlen(s2)); |
| else |
| len2 = (MALLOC_S)0; |
| if (s3) |
| len3 = (MALLOC_S)((l3 >= 0) ? l3 : strlen(s3)); |
| else |
| len3 = (MALLOC_S)0; |
| cl = len1 + len2 + len3; |
| if ((cp = (char *)malloc(cl + 1))) { |
| char *tp = cp; |
| |
| if (s1 && len1) { |
| (void) strncpy(tp, s1, len1); |
| tp += len1; |
| } |
| if (s2 && len2) { |
| (void) strncpy(tp, s2, len2); |
| tp += len2; |
| } |
| if (s3 && len3) { |
| (void) strncpy(tp, s3, len3); |
| tp += len3; |
| } |
| *tp = '\0'; |
| } |
| if (clp) |
| *clp = cl; |
| return(cp); |
| } |
| |
| |
| /* |
| * is_readable() -- is file readable |
| */ |
| |
| int |
| is_readable(path, msg) |
| char *path; /* file path */ |
| int msg; /* issue warning message if 1 */ |
| { |
| if (access(path, R_OK) < 0) { |
| if (!Fwarn && msg == 1) |
| (void) fprintf(stderr, ACCESSERRFMT, Pn, path, strerror(errno)); |
| return(0); |
| } |
| return(1); |
| } |
| |
| |
| /* |
| * lstatsafely() - lstat path safely (i. e., with timeout) |
| */ |
| |
| int |
| lstatsafely(path, buf) |
| char *path; /* file path */ |
| struct stat *buf; /* stat buffer address */ |
| { |
| if (Fblock) { |
| if (!Fwarn) |
| (void) fprintf(stderr, |
| "%s: avoiding stat(%s): -b was specified.\n", |
| Pn, path); |
| errno = EWOULDBLOCK; |
| return(1); |
| } |
| return(doinchild(dolstat, path, (char *)buf, sizeof(struct stat))); |
| } |
| |
| |
| /* |
| * Readlink() - read and interpret file system symbolic links |
| */ |
| |
| char * |
| Readlink(arg) |
| char *arg; /* argument to be interpreted */ |
| { |
| char abuf[MAXPATHLEN+1]; |
| int alen; |
| char *ap; |
| char *argp1, *argp2; |
| int i, len, llen, slen; |
| char lbuf[MAXPATHLEN+1]; |
| static char *op = (char *)NULL; |
| static int ss = 0; |
| char *s1; |
| static char **stk = (char **)NULL; |
| static int sx = 0; |
| char tbuf[MAXPATHLEN+1]; |
| /* |
| * See if avoiding kernel blocks. |
| */ |
| if (Fblock) { |
| if (!Fwarn) { |
| (void) fprintf(stderr, "%s: avoiding readlink(", Pn); |
| safestrprt(arg, stderr, 0); |
| (void) fprintf(stderr, "): -b was specified.\n"); |
| } |
| op = (char *)NULL; |
| return(arg); |
| } |
| /* |
| * Save the original path. |
| */ |
| if (!op) |
| op = arg; |
| /* |
| * Evaluate each component of the argument for a symbolic link. |
| */ |
| for (alen = 0, ap = abuf, argp1 = argp2 = arg; *argp2; argp1 = argp2 ) { |
| for (argp2 = argp1 + 1; *argp2 && *argp2 != '/'; argp2++) |
| ; |
| if ((len = argp2 - arg) >= (int)sizeof(tbuf)) { |
| |
| path_too_long: |
| if (!Fwarn) { |
| (void) fprintf(stderr, |
| "%s: readlink() path too long: ", Pn); |
| safestrprt(op ? op : arg, stderr, 1); |
| } |
| op = (char *)NULL; |
| return((char *)NULL); |
| } |
| (void) strncpy(tbuf, arg, len); |
| tbuf[len] = '\0'; |
| /* |
| * Dereference a symbolic link. |
| */ |
| if ((llen=doinchild(doreadlink,tbuf,lbuf,sizeof(lbuf) - 1)) >= 0) { |
| |
| /* |
| * If the link is a new absolute path, replace |
| * the previous assembly with it. |
| */ |
| if (lbuf[0] == '/') { |
| (void) strncpy(abuf, lbuf, llen); |
| ap = &abuf[llen]; |
| *ap = '\0'; |
| alen = llen; |
| continue; |
| } |
| lbuf[llen] = '\0'; |
| s1 = lbuf; |
| } else { |
| llen = argp2 - argp1; |
| s1 = argp1; |
| } |
| /* |
| * Make sure two components are separated by a `/'. |
| * |
| * If the first component is not a link, don't force |
| * a leading '/'. |
| * |
| * If the first component is a link and the source of |
| * the link has a leading '/', force a leading '/'. |
| */ |
| if (*s1 == '/') |
| slen = 1; |
| else { |
| if (alen > 0) { |
| |
| /* |
| * This is not the first component. |
| */ |
| if (abuf[alen - 1] == '/') |
| slen = 1; |
| else |
| slen = 2; |
| } else { |
| |
| /* |
| * This is the first component. |
| */ |
| if (s1 == lbuf && tbuf[0] == '/') |
| slen = 2; |
| else |
| slen = 1; |
| } |
| } |
| /* |
| * Add to the path assembly. |
| */ |
| if ((alen + llen + slen) >= (int)sizeof(abuf)) |
| goto path_too_long; |
| if (slen == 2) |
| *ap++ = '/'; |
| (void) strncpy(ap, s1, llen); |
| ap += llen; |
| *ap = '\0'; |
| alen += (llen + slen - 1); |
| } |
| /* |
| * If the assembled path and argument are the same, free all but the |
| * last string in the stack, and return the argument. |
| */ |
| if (strcmp(arg, abuf) == 0) { |
| for (i = 0; i < sx; i++) { |
| if (i < (sx - 1)) |
| (void) free((FREE_P *)stk[i]); |
| stk[i] = (char *)NULL; |
| } |
| sx = 0; |
| op = (char *)NULL; |
| return(arg); |
| } |
| /* |
| * If the assembled path and argument are different, add it to the |
| * string stack, then Readlink() it. |
| */ |
| if (!(s1 = mkstrcpy(abuf, (MALLOC_S *)NULL))) { |
| |
| no_readlink_space: |
| |
| (void) fprintf(stderr, "%s: no Readlink string space for ", Pn); |
| safestrprt(abuf, stderr, 1); |
| Exit(1); |
| } |
| if (sx >= MAXSYMLINKS) { |
| |
| /* |
| * If there are too many symbolic links, report an error, clear |
| * the stack, and return no path. |
| */ |
| if (!Fwarn) { |
| (void) fprintf(stderr, |
| "%s: too many (> %d) symbolic links in readlink() path: ", |
| Pn, MAXSYMLINKS); |
| safestrprt(op ? op : arg, stderr, 1); |
| } |
| for (i = 0; i < sx; i++) { |
| (void) free((FREE_P *)stk[i]); |
| stk[i] = (char *)NULL; |
| } |
| (void) free((FREE_P *)stk); |
| stk = (char **)NULL; |
| ss = sx = 0; |
| op = (char *)NULL; |
| return((char *)NULL); |
| } |
| if (++sx > ss) { |
| if (!stk) |
| stk = (char **)malloc((MALLOC_S)(sizeof(char *) * sx)); |
| else |
| stk = (char **)realloc((MALLOC_P *)stk, |
| (MALLOC_S)(sizeof(char *) * sx)); |
| if (!stk) |
| goto no_readlink_space; |
| ss = sx; |
| } |
| stk[sx - 1] = s1; |
| return(Readlink(s1)); |
| } |
| |
| |
| #if defined(HASSTREAMS) |
| /* |
| * readstdata() - read stream's stdata structure |
| */ |
| |
| int |
| readstdata(addr, buf) |
| KA_T addr; /* stdata address in kernel*/ |
| struct stdata *buf; /* buffer addess */ |
| { |
| if (!addr |
| || kread(addr, (char *)buf, sizeof(struct stdata))) { |
| (void) snpf(Namech, Namechl, "no stream data in %s", |
| print_kptr(addr, (char *)NULL, 0)); |
| return(1); |
| } |
| return(0); |
| } |
| |
| |
| /* |
| * readsthead() - read stream head |
| */ |
| |
| int |
| readsthead(addr, buf) |
| KA_T addr; /* starting queue pointer in kernel */ |
| struct queue *buf; /* buffer for queue head */ |
| { |
| KA_T qp; |
| |
| if (!addr) { |
| (void) snpf(Namech, Namechl, "no stream queue head"); |
| return(1); |
| } |
| for (qp = addr; qp; qp = (KA_T)buf->q_next) { |
| if (kread(qp, (char *)buf, sizeof(struct queue))) { |
| (void) snpf(Namech, Namechl, "bad stream queue link at %s", |
| print_kptr(qp, (char *)NULL, 0)); |
| return(1); |
| } |
| } |
| return(0); |
| } |
| |
| |
| /* |
| * readstidnm() - read stream module ID name |
| */ |
| |
| int |
| readstidnm(addr, buf, len) |
| KA_T addr; /* module ID name address in kernel */ |
| char *buf; /* receiving buffer address */ |
| READLEN_T len; /* buffer length */ |
| { |
| if (!addr || kread(addr, buf, len)) { |
| (void) snpf(Namech, Namechl, "can't read module ID name from %s", |
| print_kptr(addr, (char *)NULL, 0)); |
| return(1); |
| } |
| return(0); |
| } |
| |
| |
| /* |
| * readstmin() - read stream's module info |
| */ |
| |
| int |
| readstmin(addr, buf) |
| KA_T addr; /* module info address in kernel */ |
| struct module_info *buf; /* receiving buffer address */ |
| { |
| if (!addr || kread(addr, (char *)buf, sizeof(struct module_info))) { |
| (void) snpf(Namech, Namechl, "can't read module info from %s", |
| print_kptr(addr, (char *)NULL, 0)); |
| return(1); |
| } |
| return(0); |
| } |
| |
| |
| /* |
| * readstqinit() - read stream's queue information structure |
| */ |
| |
| int |
| readstqinit(addr, buf) |
| KA_T addr; /* queue info address in kernel */ |
| struct qinit *buf; /* receiving buffer address */ |
| { |
| if (!addr || kread(addr, (char *)buf, sizeof(struct qinit))) { |
| (void) snpf(Namech, Namechl, "can't read queue info from %s", |
| print_kptr(addr, (char *)NULL, 0)); |
| return(1); |
| } |
| return(0); |
| } |
| #endif /* HASSTREAMS */ |
| |
| |
| /* |
| * safepup() - safely print an unprintable character -- i.e., print it in a |
| * printable form |
| * |
| * return: char * to printable equivalent |
| * cl = strlen(printable equivalent) |
| */ |
| |
| static char * |
| safepup(c, cl) |
| unsigned int c; /* unprintable (i.e., !isprint()) |
| * character */ |
| int *cl; /* returned printable strlen -- NULL if |
| * no return needed */ |
| { |
| int len; |
| char *rp; |
| static char up[8]; |
| |
| if (c < 0x20) { |
| switch (c) { |
| case '\b': |
| rp = "\\b"; |
| break; |
| case '\f': |
| rp = "\\f"; |
| break; |
| case '\n': |
| rp = "\\n"; |
| break; |
| case '\r': |
| rp = "\\r"; |
| break; |
| case '\t': |
| rp = "\\t"; |
| break; |
| default: |
| (void) snpf(up, sizeof(up), "^%c", c + 0x40); |
| rp = up; |
| } |
| len = 2; |
| } else if (c == 0xff) { |
| rp = "^?"; |
| len = 2; |
| } else { |
| (void) snpf(up, sizeof(up), "\\x%02x", (int)(c & 0xff)); |
| rp = up; |
| len = 4; |
| } |
| if (cl) |
| *cl = len; |
| return(rp); |
| } |
| |
| |
| /* |
| * safestrlen() - calculate a "safe" string length -- i.e., compute space for |
| * non-printable characters when printed in a printable form |
| */ |
| |
| int |
| safestrlen(sp, flags) |
| char *sp; /* string pointer */ |
| int flags; /* flags: |
| * bit 0: 0 (0) = no NL |
| * 1 (1) = add trailing NL |
| * 1: 0 (0) = ' ' printable |
| * 1 (2) = ' ' not printable |
| */ |
| { |
| char c; |
| int len = 0; |
| |
| c = (flags & 2) ? ' ' : '\0'; |
| if (sp) { |
| for (; *sp; sp++) { |
| if (!isprint((unsigned char)*sp) || *sp == c) { |
| if (*sp < 0x20 || (unsigned char)*sp == 0xff) |
| len += 2; /* length of \. or ^. form */ |
| else |
| len += 4; /* length of "\x%02x" printf */ |
| } else |
| len++; |
| } |
| } |
| return(len); |
| } |
| |
| |
| /* |
| * safestrprt() - print a string "safely" to the indicated stream -- i.e., |
| * print unprintable characters in a printable form |
| */ |
| |
| void |
| safestrprt(sp, fs, flags) |
| char *sp; /* string to print pointer pointer */ |
| FILE *fs; /* destination stream -- e.g., stderr |
| * or stdout */ |
| int flags; /* flags: |
| * bit 0: 0 (0) = no NL |
| * 1 (1) = add trailing NL |
| * 1: 0 (0) = ' ' printable |
| * 1 (2) = ' ' not printable |
| * 2: 0 (0) = print string as is |
| * 1 (4) = surround string |
| * with '"' |
| * 4: 0 (0) = print ending '\n' |
| * 1 (8) = don't print ending |
| * '\n' |
| */ |
| { |
| char c; |
| int lnc, lnt, sl; |
| |
| #if defined(HASWIDECHAR) |
| wchar_t w; |
| int wcmx = MB_CUR_MAX; |
| #else /* !defined(HASWIDECHAR) */ |
| static int wcmx = 1; |
| #endif /* defined(HASWIDECHAR) */ |
| |
| c = (flags & 2) ? ' ' : '\0'; |
| if (flags & 4) |
| putc('"', fs); |
| if (sp) { |
| for (sl = strlen(sp); *sp; sl -= lnc, sp += lnc) { |
| |
| #if defined(HASWIDECHAR) |
| if (wcmx > 1) { |
| lnc = mblen(sp, sl); |
| if (lnc > 1) { |
| if ((mbtowc(&w, sp, sl) == lnc) && iswprint(w)) { |
| for (lnt = 0; lnt < lnc; lnt++) { |
| putc((int)*(sp + lnt), fs); |
| } |
| } else { |
| for (lnt = 0; lnt < lnc; lnt++) { |
| fputs(safepup((unsigned int)*(sp + lnt), |
| (int *)NULL), fs); |
| } |
| } |
| continue; |
| } else |
| lnc = 1; |
| } else |
| lnc = 1; |
| #else /* !defined(HASWIDECHAR) */ |
| lnc = 1; |
| #endif /* defined(HASWIDECHAR) */ |
| |
| if (isprint((unsigned char)*sp) && *sp != c) |
| putc((int)(*sp & 0xff), fs); |
| else { |
| if ((flags & 8) && (*sp == '\n') && !*(sp + 1)) |
| break; |
| fputs(safepup((unsigned int)*sp, (int *)NULL), fs); |
| } |
| } |
| } |
| if (flags & 4) |
| putc('"', fs); |
| if (flags & 1) |
| putc('\n', fs); |
| } |
| |
| |
| /* |
| * safestrprtn() - print a specified number of characters from a string |
| * "safely" to the indicated stream |
| */ |
| |
| void |
| safestrprtn(sp, len, fs, flags) |
| char *sp; /* string to print pointer pointer */ |
| int len; /* safe number of characters to |
| * print */ |
| FILE *fs; /* destination stream -- e.g., stderr |
| * or stdout */ |
| int flags; /* flags: |
| * bit 0: 0 (0) = no NL |
| * 1 (1) = add trailing NL |
| * 1: 0 (0) = ' ' printable |
| * 1 (2) = ' ' not printable |
| * 2: 0 (0) = print string as is |
| * 1 (4) = surround string |
| * with '"' |
| * 4: 0 (0) = print ending '\n' |
| * 1 (8) = don't print ending |
| * '\n' |
| */ |
| { |
| char c, *up; |
| int cl, i; |
| |
| if (flags & 4) |
| putc('"', fs); |
| if (sp) { |
| c = (flags & 2) ? ' ' : '\0'; |
| for (i = 0; i < len && *sp; sp++) { |
| if (isprint((unsigned char)*sp) && *sp != c) { |
| putc((int)(*sp & 0xff), fs); |
| i++; |
| } else { |
| if ((flags & 8) && (*sp == '\n') && !*(sp + 1)) |
| break; |
| up = safepup((unsigned int)*sp, &cl); |
| if ((i + cl) > len) |
| break; |
| fputs(up, fs); |
| i += cl; |
| } |
| } |
| } else |
| i = 0; |
| for (; i < len; i++) |
| putc(' ', fs); |
| if (flags & 4) |
| putc('"', fs); |
| if (flags & 1) |
| putc('\n', fs); |
| } |
| |
| |
| /* |
| * statsafely() - stat path safely (i. e., with timeout) |
| */ |
| |
| int |
| statsafely(path, buf) |
| char *path; /* file path */ |
| struct stat *buf; /* stat buffer address */ |
| { |
| if (Fblock) { |
| if (!Fwarn) |
| (void) fprintf(stderr, |
| "%s: avoiding stat(%s): -b was specified.\n", |
| Pn, path); |
| errno = EWOULDBLOCK; |
| return(1); |
| } |
| return(doinchild(dostat, path, (char *)buf, sizeof(struct stat))); |
| } |
| |
| |
| /* |
| * stkdir() - stack directory name |
| */ |
| |
| void |
| stkdir(p) |
| char *p; /* directory path */ |
| { |
| MALLOC_S len; |
| /* |
| * Provide adequate space for directory stack pointers. |
| */ |
| if (Dstkx >= Dstkn) { |
| Dstkn += 128; |
| len = (MALLOC_S)(Dstkn * sizeof(char *)); |
| if (!Dstk) |
| Dstk = (char **)malloc(len); |
| else |
| Dstk = (char **)realloc((MALLOC_P *)Dstk, len); |
| if (!Dstk) { |
| (void) fprintf(stderr, |
| "%s: no space for directory stack at: ", Pn); |
| safestrprt(p, stderr, 1); |
| Exit(1); |
| } |
| } |
| /* |
| * Allocate space for the name, copy it there and put its pointer on the stack. |
| */ |
| if (!(Dstk[Dstkx] = mkstrcpy(p, (MALLOC_S *)NULL))) { |
| (void) fprintf(stderr, "%s: no space for: ", Pn); |
| safestrprt(p, stderr, 1); |
| Exit(1); |
| } |
| Dstkx++; |
| } |
| |
| |
| /* |
| * x2dev() - convert hexadecimal ASCII string to device number |
| */ |
| |
| char * |
| x2dev(s, d) |
| char *s; /* ASCII string */ |
| dev_t *d; /* device receptacle */ |
| { |
| char *cp, *cp1; |
| int n; |
| dev_t r; |
| |
| /* |
| * Skip an optional leading 0x. Count the number of hex digits up to the end |
| * of the string, or to a space, or to a comma. Return an error if an unknown |
| * character is encountered. If the count is larger than (2 * sizeof(dev_t)) |
| * -- e.g., because of sign extension -- ignore excess leading hex 0xf digits, |
| * but return an error if an excess leading digit isn't 0xf. |
| */ |
| if (strncasecmp(s, "0x", 2) == 0) |
| s += 2; |
| for (cp = s, n = 0; *cp; cp++, n++) { |
| if (isdigit((unsigned char)*cp)) |
| continue; |
| if ((unsigned char)*cp >= 'a' && (unsigned char)*cp <= 'f') |
| continue; |
| if ((unsigned char)*cp >= 'A' && (unsigned char)*cp <= 'F') |
| continue; |
| if (*cp == ' ' || *cp == ',') |
| break; |
| return((char *)NULL); |
| } |
| if (!n) |
| return((char *)NULL); |
| if (n > (2 * (int)sizeof(dev_t))) { |
| cp1 = s; |
| s += (n - (2 * sizeof(dev_t))); |
| while (cp1 < s) { |
| if (*cp1 != 'f' && *cp1 != 'F') |
| return((char *)NULL); |
| cp1++; |
| } |
| } |
| /* |
| * Assemble the validated hex digits of the device number, starting at a point |
| * in the string relevant to sizeof(dev_t). |
| */ |
| for (r = 0; s < cp; s++) { |
| r = r << 4; |
| if (isdigit((unsigned char)*s)) |
| r |= (unsigned char)(*s - '0') & 0xf; |
| else { |
| if (isupper((unsigned char)*s)) |
| r |= ((unsigned char)(*s - 'A') + 10) & 0xf; |
| else |
| r |= ((unsigned char)(*s - 'a') + 10) & 0xf; |
| } |
| } |
| *d = r; |
| return(s); |
| } |