From fa080de7afc95aa1c19a6e6fc0e0708ced2eadc4 Mon Sep 17 00:00:00 2001 From: Joseph Hunkeler Date: Wed, 8 Jul 2015 20:46:52 -0400 Subject: Initial commit --- unix/os/zfiond.c | 918 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 918 insertions(+) create mode 100644 unix/os/zfiond.c (limited to 'unix/os/zfiond.c') diff --git a/unix/os/zfiond.c b/unix/os/zfiond.c new file mode 100644 index 00000000..6f12413f --- /dev/null +++ b/unix/os/zfiond.c @@ -0,0 +1,918 @@ +/* Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef LINUX +#include +#endif +#ifdef MACOSX +#include +#endif + +#include +#include + +#define import_kernel +#define import_knames +#define import_zfstat +#define import_spp +#include + +/* + * ZFIOND -- This driver provides a FIO-compatible interface to network or + * IPC streaming devices such as Berkeley sockets, FIFOs, and the like. + * Any connection-oriented stream type network interface can be supported. + * + * The type of connection desired is determined at device open time by the + * "filename" and file access mode arguments. The syntax for the filename + * argument is as follows: + * + * :
[ : flag ] [ : flag...] + * + * where is one of "inet" (internet tcp/ip socket), "unix" (unix + * domain socket) or "fifo" (named pipe). The form of the address depends + * upon the domain, as illustrated in the examples below. + * + * inet:5187 Server connection to port 5187 on the local + * host. For a client, a connection to the + * given port on the local host. + * + * inet:5187:foo.bar.edu Client connection to port 5187 on internet + * host foo.bar.edu. The dotted form of address + * may also be used. + * + * unix:/tmp/.IMT212 Unix domain socket with the given pathname + * IPC method, local host only. + * + * fifo:/dev/imt1i:/dev/imt1o FIFO or named pipe with the given pathname. + * IPC method, local host only. Two pathnames + * are required, one for input and one for + * output, since FIFOs are not bidirectional. + * For a client the first fifo listed will be + * the client's input fifo; for a server the + * first fifo will be the server's output fifo. + * This allows the same address to be used for + * both the client and the server, as for the + * other domains. + * + * sock:5 Used by servers to accept a connection on + * a server socket opened in nonblocking mode + * on the given channel (5 in the example). + * The channel is returned in a previous call + * to open an INET or UNIX server port. + * + * The address field may contain up to two "%d" fields. If present, the + * user's UID will be substituted (e.g. "unix:/tmp/.IMT%d"). + * + * The protocol flags currently supported are "text", "binary", "nonblock", + * and "nodelay". If "text" is specified the datastream is assumed to + * consist only of byte packed ascii text and is automatically converted by + * the driver to and from SPP chars during i/o. The default is binary i/o + * (no conversions). The "nonblock" flag is used to specify nonblocking + * mode. The "nodelay" flag is used to return an error when opening a + * "sock" connection in "nonblock" mode and for read when there is no + * pending connection or data. + * + * + * Client connections normally use mode READ_WRITE, although READ_ONLY and + * WRITE_ONLY are permitted. APPEND is the same as WRITE_ONLY. A server + * connection is indicated by the mode NEW_FILE. The endpoints of the server + * connection will be created if necessary. A client connection will timeout + * if no server responds. + * + * By default a server connection will block until a client connects, and + * the channel returned will be the i/o channel for the client connection. + * If however a server connection is opened in nonblocking mode then a server + * socket will be opened which can be used for multiple client connections. + * When a client makes a connection attempt, opening the server socket as + * "sock:", where is the channel assigned to the socket, will + * accept the client connection and open a new channel to be used for + * bidirectional i/o to the client. The open will block until a client + * connects unless the socket is opened in nonblocking mode. If opening the + * channel under IRAF FIO and separate read and write streams are desired, + * this can be achieved by using REOPEN to open a second stream on the same + * channel). The server sees an EOF on the input stream when the client + * disconnects. + * + * The "nodelay" flag will poll for "sock" connections or for a pending + * read. This uses the SELECT call. If there is no pending connection + * then the open call will return an error through FIO. The application + * may trap the error and try again later. If there is no pending data in a + * read then the number of bytes read is set to XERR and FIO will return ERR. + * The exception to this is if asynchronous reads are used with AREADB + * and AWAITB. In this case the application will see the number of bytes + * read as zero for EOF (client disconnected) and ERR for no data. + * + * FIFO domain connection are slightly different. When the server opens a + * FIFO connection the open returns immediately. When the server reads from + * the input fifo the server will block until some data is written to the + * fifo by a client. The server connection will remain open over multiple + * client connections until it is closed by the server. This is done to + * avoid a race condition that could otherwise occur at open time, with both + * the client and the server blocked waiting for an open on the opposite stream. + */ + +#define SZ_NAME 256 +#define SZ_OBUF 4096 +#define MAXCONN 32 +#define MAXSEL 32 + +#define INET 1 +#define UNIX 2 +#define FIFO 3 +#define SOCK 4 + +#define F_SERVER 00001 +#define F_NONBLOCK 00002 +#define F_TEXT 00004 +#define F_DEL1 00010 +#define F_DEL2 00020 +#define F_NODELAY 00040 + +/* Network portal descriptor. */ +struct portal { + int channel; + int domain; + int flags; + int datain; + int dataout; + int keepalive; + char path1[SZ_NAME]; + char path2[SZ_NAME]; +}; + +#define get_desc(fd) ((struct portal *)zfd[fd].fp) +#define set_desc(fd,np) zfd[fd].fp = (FILE *)np +#define min(a,b) (((a)<(b))?(a):(b)) + +static jmp_buf jmpbuf; +static int jmpset = 0; +static int recursion = 0; +extern int errno; +static int getstr(); + +static void nd_onsig (int sig, int *arg1, int *arg2); + + + +/* ZOPNND -- Open a network device. + */ +int +ZOPNND ( + PKCHAR *pk_osfn, /* UNIX name of file */ + XINT *mode, /* file access mode */ + XINT *chan /* file number (output) */ +) +{ + register int fd; + register struct portal *np, *s_np = (struct portal *) NULL; + unsigned short host_port = 0; + unsigned long host_addr = 0; + char osfn[SZ_NAME*2]; + char flag[SZ_NAME]; + char *ip; + + /* Get network device descriptor. */ + if (!(np = (struct portal *) calloc (1, sizeof(struct portal)))) { + *chan = XERR; + return (XERR); + } + + /* Expand any %d fields in the network address to the UID. */ + sprintf (osfn, (char *)pk_osfn, getuid(), getuid()); + + /* Parse the network filename to determine the domain type and + * network address. + */ + if (strncmp (osfn, "inet:", 5) == 0) { + /* Internet connection. + */ + char port_str[SZ_NAME]; + char host_str[SZ_NAME]; + unsigned short port; + struct servent *sv; + struct hostent *hp; + + /* Get port number. This may be specified either as a service + * name or as a decimal port number. + */ + ip = osfn + 5; + if (getstr (&ip, port_str, SZ_NAME, ':') <= 0) + goto err; + if (isdigit (port_str[0])) { + port = atoi (port_str); + host_port = htons (port); + } else if ((sv = getservbyname(port_str,"tcp"))) { + host_port = sv->s_port; + } else + goto err; + + /* Get host address. This may be specified either has a host + * name or as an Internet address in dot notation. If no host + * name is specified default to the local host. + */ + if (getstr (&ip, host_str, SZ_NAME, ':') <= 0) + strcpy (host_str, "localhost"); + if (isdigit (host_str[0])) { + host_addr = inet_addr (host_str); + if ((int)host_addr == -1) + goto err; + } else if ((hp = gethostbyname(host_str))) { + bcopy (hp->h_addr, (char *)&host_addr, sizeof(host_addr)); + } else + goto err; + + np->domain = INET; + + } else if (strncmp (osfn, "unix:", 5) == 0) { + /* Unix domain socket connection. + */ + ip = osfn + 5; + if (!getstr (&ip, np->path1, SZ_NAME, ':')) + goto err; + np->domain = UNIX; + + } else if (strncmp (osfn, "sock:", 5) == 0) { + /* Open (accept) a client connection on an existing, open + * server socket. + */ + char chan_str[SZ_NAME]; + int channel; + + /* Get the channel of the server socket. */ + ip = osfn + 5; + if (getstr (&ip, chan_str, SZ_NAME, ':') <= 0) + goto err; + if (isdigit (chan_str[0])) + channel = atoi (chan_str); + else + goto err; + + /* Get the server portal descriptor. */ + s_np = get_desc(channel); + if (!(s_np->flags & F_SERVER)) + goto err; + + np->domain = SOCK; + + } else if (strncmp (osfn, "fifo:", 5) == 0) { + /* FIFO (named pipe) connection. + */ + ip = osfn + 5; + if (*mode == NEW_FILE) { + /* Server. */ + if (!getstr (&ip, np->path2, SZ_NAME, ':')) + goto err; + if (!getstr (&ip, np->path1, SZ_NAME, ':')) + goto err; + } else { + /* Client. */ + if (!getstr (&ip, np->path1, SZ_NAME, ':')) + goto err; + if (!getstr (&ip, np->path2, SZ_NAME, ':')) + goto err; + } + np->domain = FIFO; + + } else + goto err; + + /* Process any optional protocol flags. + */ + while (getstr (&ip, flag, SZ_NAME, ':') > 0) { + /* Get content type (text or binary). If the stream will be used + * only for byte-packed character data the content type can be + * specified as "text" and data will be automatically packed and + * unpacked during i/o. + */ + if (strcmp (flag, "text") == 0) + np->flags |= F_TEXT; + if (strcmp (flag, "binary") == 0) + np->flags &= ~F_TEXT; + + /* Check for nonblocking i/o or connections. */ + if (strcmp (flag, "nonblock") == 0) + np->flags |= F_NONBLOCK; + + /* Check for no delay flag. */ + if (strcmp (flag, "nodelay") == 0) + np->flags |= F_NODELAY; + } + + /* Open the network connection. + */ + switch (*mode) { + case READ_ONLY: + /* Client side read only FIFO connection. */ + if (np->domain == FIFO) { + if ((fd = open (np->path1, O_RDONLY|O_NDELAY)) != ERR) + fcntl (fd, F_SETFL, O_RDONLY); + np->datain = fd; + np->dataout = -1; + break; + } + /* fall through */ + + case WRITE_ONLY: + case APPEND: + /* Client side write only FIFO connection. */ + if (np->domain == FIFO) { + if ((fd = open (np->path2, O_WRONLY|O_NDELAY)) != ERR) + fcntl (fd, F_SETFL, O_WRONLY); + np->datain = -1; + np->dataout = fd; + break; + } + /* fall through */ + + case READ_WRITE: + if (np->domain == INET) { + /* Client side Internet domain connection. */ + struct sockaddr_in sockaddr; + + /* Get socket. */ + if ((fd = socket (AF_INET, SOCK_STREAM, 0)) < 0) + goto err; + + /* Compose network address. */ + bzero ((char *)&sockaddr, sizeof(sockaddr)); + sockaddr.sin_family = AF_INET; + sockaddr.sin_port = host_port; + bcopy ((char *)&host_addr, (char *)&sockaddr.sin_addr, + sizeof(host_addr)); + + /* Connect to server. */ + if (fd >= MAXOFILES || (connect (fd, + (struct sockaddr *)&sockaddr, sizeof(sockaddr)) < 0)) { + close (fd); + fd = ERR; + } else { + np->datain = fd; + np->dataout = fd; + } + + } else if (np->domain == UNIX) { + /* Client side Unix domain socket connection. */ + struct sockaddr_un sockaddr; + + /* Get socket. */ + if ((fd = socket (AF_UNIX, SOCK_STREAM, 0)) < 0) + goto err; + + /* Compose network address. */ + bzero ((char *)&sockaddr, sizeof(sockaddr)); + sockaddr.sun_family = AF_UNIX; + strncpy (sockaddr.sun_path, + np->path1, sizeof(sockaddr.sun_path)); + + /* Connect to server. */ + if (fd >= MAXOFILES || (connect (fd, + (struct sockaddr *)&sockaddr, sizeof(sockaddr)) < 0)) { + close (fd); + fd = ERR; + } else { + np->datain = fd; + np->dataout = fd; + } + + } else if (np->domain == FIFO) { + /* Client side FIFO connection. */ + int fd1, fd2; + + /* Open the fifos. */ + if ((fd1 = open (np->path1, O_RDONLY|O_NDELAY)) != ERR) + fcntl (fd1, F_SETFL, O_RDONLY); + if ((fd2 = open (np->path2, O_WRONLY|O_NDELAY)) != ERR) + fcntl (fd2, F_SETFL, O_WRONLY); + + /* Clean up if there is an error. */ + if (fd1 < 0 || fd1 > MAXOFILES || fd2 < 0 || fd2 > MAXOFILES) { + if (fd1 > 0) + close (fd1); + if (fd2 > 0) + close (fd2); + fd = ERR; + } else { + np->datain = fd1; + np->dataout = fd2; + fd = fd1; + } + } else + goto err; + break; + + case NEW_FILE: + /* Connect to a client. */ + np->flags |= F_SERVER; + + if (np->domain == INET) { + /* Server side Internet domain connection. */ + struct sockaddr_in sockaddr; + int s, reuse=1; + + /* Get socket. */ + if ((s = socket (AF_INET, SOCK_STREAM, 0)) < 0) + goto err; + + /* Bind server port to socket. */ + bzero ((char *)&sockaddr, sizeof(sockaddr)); + sockaddr.sin_family = AF_INET; + sockaddr.sin_port = host_port; + sockaddr.sin_addr.s_addr = htonl(INADDR_ANY); + + if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, + sizeof(reuse)) < 0) { + close (s); + goto err; + } + + if (bind (s, + (struct sockaddr *)&sockaddr, sizeof(sockaddr)) < 0) { + close (s); + goto err; + } + + /* Enable queuing of client connections. */ + if (listen (s, MAXCONN) < 0) { + close (s); + goto err; + } + + /* If in blocking mode wait for a client connection, otherwise + * return the server socket on the channel. + */ + if (!(np->flags & F_NONBLOCK)) { + if ((fd = accept (s, (struct sockaddr *)0, + (socklen_t *)0)) < 0) { + close (s); + goto err; + } else + close (s); + } else + fd = s; + + np->datain = fd; + np->dataout = fd; + + } else if (np->domain == UNIX) { + /* Server side Unix domain connection. */ + struct sockaddr_un sockaddr; + int addrlen, s; + + /* Get socket. */ + if ((s = socket (AF_UNIX, SOCK_STREAM, 0)) < 0) + goto err; + + /* Bind server port to socket. */ + bzero ((char *)&sockaddr, sizeof(sockaddr)); + sockaddr.sun_family = AF_UNIX; + strncpy (sockaddr.sun_path,np->path1,sizeof(sockaddr.sun_path)); + addrlen = sizeof(sockaddr) - sizeof(sockaddr.sun_path) + + strlen(np->path1); + + unlink (np->path1); + if (bind (s, (struct sockaddr *)&sockaddr, addrlen) < 0) { + close (s); + goto err; + } + + /* Enable queuing of client connections. */ + if (listen (s, MAXCONN) < 0) { + close (s); + goto err; + } + + /* If in blocking mode wait for a client connection, otherwise + * return the server socket on the channel. + */ + if (!(np->flags & F_NONBLOCK)) { + if ((fd = accept (s, (struct sockaddr *)0, + (socklen_t *)0)) < 0) { + close (s); + goto err; + } else + close (s); + } else + fd = s; + + np->datain = fd; + np->dataout = fd; + np->flags |= F_DEL1; + + } else if (np->domain == SOCK) { + /* Open (accept) a client connection on a server socket. */ + int s = s_np->channel; + + if (s_np->flags & F_NODELAY) { + struct timeval timeout; +#if defined(POSIX) || defined(LINUX) || defined(MACOSX) + fd_set readfds; + FD_ZERO (&readfds); + FD_SET (s, &readfds); +#else + int readfds = (1 << s); +#endif + timeout.tv_sec = 0; + timeout.tv_usec = 0; + if (select (MAXSEL, &readfds, NULL, NULL, &timeout)) { + if ((fd = accept (s, (struct sockaddr *)0, + (socklen_t *)0))<0) + goto err; + } else { + goto err; + } + } else { + if ((fd = accept (s, (struct sockaddr *)0, + (socklen_t *)0)) < 0) + goto err; + } + + np->datain = fd; + np->dataout = fd; + np->flags = s_np->flags; + + } else if (np->domain == FIFO) { + /* Server side FIFO connection. */ + int fd1=0, fd2=0, keepalive=0; + + /* Create fifos if necessary. */ + if (access (np->path1, 0) < 0) { + if (mknod (np->path1, 010660, 0) < 0) + goto err; + else + np->flags |= F_DEL1; + } + if (access (np->path2, 0) < 0) { + if (mknod (np->path2, 010660, 0) < 0) { + unlink (np->path1); + goto err; + } else + np->flags |= F_DEL2; + } + + /* Open the output fifo (which is the client's input fifo). + * We have to open it ourselves first as a client to get + * around the fifo open-no-client error. + */ + if ((fd1 = open (np->path2, O_RDONLY|O_NDELAY)) != -1) { + if ((fd2 = open (np->path2, O_WRONLY|O_NDELAY)) != -1) + fcntl (fd2, F_SETFL, O_WRONLY); + close (fd1); + } + + /* Open the input fifo. */ + if ((fd1 = open (np->path1, O_RDONLY|O_NDELAY)) == -1) + fprintf (stderr, "Warning: cannot open %s\n", np->path1); + else { + /* Clear O_NDELAY for reading. */ + fcntl (fd1, F_SETFL, O_RDONLY); + + /* Open the client's output fifo as a pseudo-client to + * make it appear that a client is connected. + */ + keepalive = open (np->path1, O_WRONLY); + } + + /* Clean up if there is an error. */ + if (fd1 < 0 || fd1 > MAXOFILES || fd2 < 0 || fd2 > MAXOFILES) { + if (fd1 > 0) { + close (fd1); + close (keepalive); + } + if (fd2 > 0) + close (fd2); + fd = ERR; + } else { + np->datain = fd1; + np->dataout = fd2; + np->keepalive = keepalive; + fd = fd1; + } + + } else + goto err; + break; + + default: + fd = ERR; + } + + /* Initialize the kernel file descriptor. Seeks are illegal for a + * network device; network devices are "streaming" files (blksize=1) + * which can only be accessed sequentially. + */ + if ((*chan = fd) == ERR) { +err: free (np); + *chan = XERR; + } else if (fd >= MAXOFILES) { + free (np); + close (fd); + *chan = XERR; + } else { + zfd[fd].fp = NULL; + zfd[fd].fpos = 0L; + zfd[fd].nbytes = 0; + zfd[fd].flags = 0; + zfd[fd].filesize = 0; + set_desc(fd,np); + np->channel = fd; + } + + return (*chan); +} + + +/* ZCLSND -- Close a network device. + */ +int +ZCLSND (XINT *fd, XINT *status) +{ + register struct portal *np = get_desc(*fd); + register int flags; + + if (np) { + flags = np->flags; + + if (np->datain > 0) + close (np->datain); + if (np->dataout > 0 && np->dataout != np->datain) + close (np->dataout); + if (np->keepalive > 0) + close (np->keepalive); + + if (flags & F_DEL1) + unlink (np->path1); + if (flags & F_DEL2) + unlink (np->path2); + + free (np); + set_desc(*fd,NULL); + *status = XOK; + + } else + *status = XERR; + + return (*status); +} + + +/* ZARDND -- "Asynchronous" binary block read. Initiate a read of at most + * maxbytes bytes from the file FD into the buffer BUF. Status is returned + * in a subsequent call to ZAWTND. + */ +int +ZARDND ( + XINT *chan, /* UNIX file number */ + XCHAR *buf, /* output buffer */ + XINT *maxbytes, /* max bytes to read */ + XLONG *offset /* 1-indexed file offset to read at */ +) +{ + register int n; + int fd = *chan; + struct fiodes *kfp = &zfd[fd]; + register struct portal *np = get_desc (fd); + register char *ip; + register XCHAR *op; + int nbytes, maxread; + struct timeval timeout; +#if defined(POSIX) || defined(LINUX) || defined(MACOSX) + fd_set readfds; + FD_ZERO (&readfds); + FD_SET (np->datain, &readfds); +#else + int readfds; +#endif + + /* Determine maximum amount of data to be read. */ + maxread = (np->flags & F_TEXT) ? *maxbytes/sizeof(XCHAR) : *maxbytes; + + /* The following call to select shouldn't be necessary, but it + * appears that, due to the way we open a FIFO with O_NDELAY, read + * can return zero if read is called before the process on the other + * end writes any data. This happens even though fcntl is called to + * restore blocking i/o after the open. + */ +#if defined(POSIX) || defined(LINUX) || defined(MACOSX) + FD_ZERO (&readfds); + FD_SET (np->datain, &readfds); +#else + readfds = (1 << np->datain); +#endif + if ((np->flags & F_NODELAY) && np->datain < MAXSEL) { + timeout.tv_sec = 0; + timeout.tv_usec = 0; + if (select (MAXSEL, &readfds, NULL, NULL, &timeout)) + nbytes = read (np->datain, (char *)buf, maxread); + else + nbytes = XERR; + } else { + if (np->domain == FIFO && np->datain < MAXSEL) { + select (MAXSEL, &readfds, NULL, NULL, NULL); + nbytes = read (np->datain, (char *)buf, maxread); + } else { + nbytes = read (np->datain, (char *)buf, maxread); + } + } + + if ((n = nbytes) > 0 && (np->flags & F_TEXT)) { + op = (XCHAR *) buf; + op[n] = XEOS; + for (ip = (char *)buf; --n >= 0; ) + op[n] = ip[n]; + nbytes *= sizeof(XCHAR); + } + + kfp->nbytes = nbytes; + + return (nbytes); +} + + +/* ZAWRND -- "Asynchronous" binary block write. Initiate a write of exactly + * nbytes bytes from the buffer BUF to the file FD. Status is returned in a + * subsequent call to ZAWTND. + */ +int +ZAWRND ( + XINT *chan, /* UNIX file number */ + XCHAR *buf, /* buffer containing data */ + XINT *nbytes, /* nbytes to be written */ + XLONG *offset /* 1-indexed file offset */ +) +{ + register int fd = *chan; + register struct fiodes *kfp = &zfd[fd]; + register struct portal *np = get_desc (fd); + int nwritten, maxbytes, n; + char *text, *ip = (char *)buf; + char obuf[SZ_OBUF]; + SIGFUNC sigpipe; + + + /* Enable a signal mask to catch SIGPIPE when the server has died. + */ + sigpipe = (SIGFUNC) signal (SIGPIPE, (SIGFUNC)nd_onsig); + recursion = 0; + + maxbytes = (np->domain == FIFO || (np->flags & F_TEXT)) ? SZ_OBUF : 0; + for (nwritten=0; nwritten < *nbytes; nwritten += n, ip+=n) { + n = *nbytes - nwritten; + if (maxbytes) + n = min (maxbytes, n); + + if (np->flags & F_TEXT) { + register XCHAR *ipp = (XCHAR *)ip; + register char *op = (char *)obuf; + register int nbytes = n / sizeof(XCHAR); + + while (--nbytes >= 0) + *op++ = *ipp++; + text = obuf; + + jmpset++; + if (setjmp (jmpbuf) == 0) { + if ((n = write(np->dataout, text, n / sizeof(XCHAR))) < 0) { + nwritten = ERR; + break; + } + } else { + nwritten = ERR; + break; + } + + n *= sizeof(XCHAR); + + } else { + text = ip; + if ((n = write (np->dataout, text, n)) < 0) { + nwritten = ERR; + break; + } + } + } + + /* Restore the signal mask. */ + jmpset = 0; + signal (SIGPIPE, sigpipe); + + kfp->nbytes = nwritten; + + return (nwritten); +} + + +/* ND_ONSIG -- Catch a signal. + * */ +static void +nd_onsig ( + int sig, /* signal which was trapped */ + int *arg1, /* not used */ + int *arg2 /* not used */ +) +{ + /* If we get a SIGPIPE writing to a server the server has probably + * died. Make it look like there was an i/o error on the channel. + */ + if (sig == SIGPIPE && recursion++ == 0) + ; + + if (jmpset) + longjmp (jmpbuf, sig); +} + + +/* ZAWTND -- "Wait" for an "asynchronous" read or write to complete, and + * return the number of bytes read or written, or ERR. + */ +int +ZAWTND (XINT *fd, XINT *status) +{ + if ((*status = zfd[*fd].nbytes) == ERR) + *status = XERR; + + return (*status); +} + + +/* ZSTTND -- Return file status information for a network device. + */ +int +ZSTTND (XINT *fd, XINT *param, XLONG *lvalue) +{ + switch (*param) { + case FSTT_BLKSIZE: + (*lvalue) = 0L; + break; + + case FSTT_FILSIZE: + (*lvalue) = 0L; + break; + + case FSTT_OPTBUFSIZE: + /* On some systems this parameter may be device dependent in which + * case device dependent code should be substituted here. + */ + (*lvalue) = ND_OPTBUFSIZE; + break; + + case FSTT_MAXBUFSIZE: + /* On some systems this parameter may be device dependent in which + * case device dependent code should be substituted here. + */ + (*lvalue) = ND_MAXBUFSIZE; + break; + + default: + (*lvalue) = XERR; + break; + } + + return (XOK); +} + + +/* + * Internal routines. + * ---------------------------- + */ + +/* GETSTR -- Internal routine to extract a metacharacter delimited substring + * from a formatted string. The metacharacter to be taken as the delimiter + * is passed as an argument. Any embedded whitespace between the tokens is + * stripped. The number of characters in the output token is returned as + * the function value, or zero if EOS or the delimiter is reached. + */ +static int +getstr (char **ipp, char *obuf, int maxch, int delim) +{ + register char *op, *ip = *ipp; + register char *otop = obuf + maxch; + + while (*ip && isspace(*ip)) + ip++; + for (op=obuf; *ip; ip++) { + if (*ip == delim) { + ip++; + break; + } else if (op < otop && !isspace(*ip)) + *op++ = *ip; + } + + *op = '\0'; + *ipp = ip; + + return (op - obuf); +} -- cgit