cramer@kontron.UUCP (Clayton Cramer) (01/22/86)
Does anyone know of a way to do read from a terminal under Berkeley 4.2 UNIX so that after a specified number of seconds control is returned to the program if there is no response? Right now I'm forking off of the main program, and having the child process do the read, with the parent setting an alarm to interrupt the child after a certain number of seconds. This works, but it's devilishly difficult to debug using dbx. Alternate techniques would be appreciated. Please don't suggest using alarm within the same process to interrupt a read -- to quote SIGNAL(3C): If a caught signal occurs during certain system calls, causing the call to terminate prematurely, the call is automatically restarted. In particular, this can occur during a _read_ or _write_(2) on a slow device (such as a terminal; but not a file) and during a _wait_(2). In short, the one time it would be most useful!
mo@wgivax.UUCP (01/24/86)
If you are willing to write a simple input manager, you could use getch() with alarm() and longjmp/setjmp() to manage your problem. For instance: #include <stdio.h> #include <setjmp.h> #include <signal.h> jmp_buf xyz; main() { int trapalarm(); char string[80]; do { fprintf(stderr,"Please enter string: "); signal(SIGALRM, trapalarm); alarm(5); if(setjmp(xyz) == 0) { get_input(string); fprintf(stderr,"%s",string); } else { fprintf(stderr,"no input\n"); continue; } } while(strcmp(string,"end")); } get_input(str) char *str; { register char *c; c = str; do { *c = getchar(); alarm(0); } while(*c++ != '\n'); *c = 0; } trapalarm() { alarm(0); longjmp(xyz,1); } =============================================================================== Mike O'Shea (decvax!mcnc!unccvax!wgivax!mo)
steve@umcp-cs.UUCP (Steve D. Miller) (01/26/86)
In article <482@kontron.UUCP> cramer@kontron.UUCP writes: >Does anyone know of a way to do read from a terminal under Berkeley 4.2 UNIX >so that after a specified number of seconds control is returned to the >program if there is no response? ... Methinks that what you want to use is select(2). Something similar to what I think you want to do would probably look like: #include <sys/time.h> /* for struct timeval */ ... struct timeval timeout; int s, readfds, nfound; /* s = fd we want to look at */ ... timeout.tv_sec = <number of secs to wait>; timeout.tv_usec = <number of microseconds to wait in addition to above> readfds = 1 << s; /* form bit mask */ /* * This will time out after the period specified in timeout * (and return zero in &readfds), or return before that with * nfound == 1 and (1 << s) set in readfds). See the man entry * for more details; select() falls into the category of * "really massively useful system calls that you want to * know about". */ nfound = select(32, &readfds, (int *) 0, (int *) 0, &timeout); if (nfound < 0) { /* error */ } if (nfound > 0) { /* got something */ read(s, buf, <whatever you want>); ... } else { /* whatever happens when you don't get input */ } In general, select() will let you multiplex input over a number of file descriptors in which you are interested in reading from, writing to, or knowing about "exceptional conditions" (means nothing in 4.2, though I could be wrong, but means at least "out-of-band data" in 4.3) on. If you're select()ing on more than one descriptor, then you want to check the returned bit masks (i.e., "if (readfds & (1 << s)) { .. }) for each descriptor you're interested in; the above example is simplified since it deals with only one descriptor. Note (for those of you who are interested) that all this changes in 4.3BSD. The above code will still work, but is no longer correct. Since there are more than 32 fds available, a more general scheme has been devised, using struct fd_set's (which are really just arrays of integer bit masks, though you don't need to know that; they're defined in <sys/types.h>, and can also be referred to as fd_sets, since they're typedef'ed). Four macros are defined for use in manipulating fd_sets: FD_SET(n, p), which adds fd n to the fd_set pointed to by p; FD_CLR(n, p), which removes fd n from the fd_set pointed to by p; FD_ISSET(n, p), which returns an indication of whether or not fd n is a member of the fd_set pointed to by p; and FD_ZERO(p), which clears the fd_set pointed to by p. Therefore, the code above would, under 4.3BSD, look like: #include <sys/types.h> #include <sys/time.h> /* for struct timeval */ ... struct timeval timeout; int s, nfound; /* s = fd we want to look at */ fd_set readfds; ... timeout.tv_sec = <number of secs to wait>; timeout.tv_usec = <number of microseconds to wait in addition to above> FD_ZERO(&readfds); FD_SET(s, &readfds); /* * This will time out after the period specified in timeout * (and return readfds with no bits set), or return before that with * nfound == 1 and FD_ISSET(s, &readfds) == true. See the man entry * for more details; select() falls into the category of * "really massively useful system calls that you want to * know about". */ nfound = select(32, &readfds, (fd_set *) 0, (fd_set *) 0, &timeout); if (nfound < 0) { /* error */ } if (nfound > 0) { /* got something */ read(s, buf, <whatever you want>); ... } else { /* whatever happens when you don't get input */ } Again, if we were select()ing on more than one descriptor, we'd want to check the status of the returned fd_set with FD_ISSET, since the number of fds available for reading (as indicated by nfound) would in that case not indicate that we could read off of any given fd. I hope this is of some use (to you or to someone else)... -Steve