Suppose you want to write some kind of game program, with input from the keyboard at unpredictable times. For example, hitting a certain key may make a Pacman figure change directions. Clearly we cannot use scanf for this purpose, because the program would just hang there, with the screen frozen, waiting for us to strike a key. This is not what we want; we want the animation on the screen to continue.
This kind of situation can be handled via the concept of signals.
The main library functions needed are signal, which specifies what function should be called when a ctrl-c or other signal is received by the program, and the pair of functions setjmp and longjmp, which are used to specify where the function is to resume execution if not at the point it was when the signal was received. In order to use these functions, you must have #include lines for <signal.h> and <setjmp.h>.
The program below is described in the comments in Lines 3-15. It acts like the Unix grep command (which finds all lines in a file which contain the user-specified string), except that it is more interactive: The user specifies the files one at a time, and (here is why the signals are needed) he/she can cancel a file search in progress without canceling the grep command as a whole.
For example, when I ran the program (Line 2), I first asked it to search for the string `type' (Line 4). It asked me what file to search in, and I gave it a file name (Line 6); the program then listed for me (Lines 7-20) all the lines from the file HowToUseEMail.tex which contain the string `type'. Then the program asked me if I wanted to search for that (same) string in another file (Line 21), so I asked it to look in the system dictionary (Line 22). The program had already printed out the first few response lines (Lines 23-25) when I changed my mind and decided that I didn't want to check that file after all; so, I typed control-c (Line 26), and program responded by confirming that I had abandoned its search in that file (Line 26). I then gave it another file to search (Line 28).
1 Script started on Wed May 27 16:20:18 1992 2 heather% a.out 3 string? 4 type 5 file name? 6 HowToUseEMail.tex 7 Simply type {\bf mail \it address}, where {\it address} is the 8 After you type this command line, you will be prompted to give 9 a subject title for your message, and then you type your message 10 typed so far. You can then edit the text as usual. When you are done, 11 type ZZ as usual to leave {\bf vi}, putting you back in send-mail mode, 12 and type ctrl-d to terminate and dispatch your message as explained 13 {\bf mail}, type tilde-v to get into {\bf vi} as above, and then use 14 Then simply type 15 Z. I would type the following: 16 ``begin'' line. Then she would type: 17 Simply type {\bf mail}. 18 When you are finished reading your mail, type either {\bf x} 19 truly discarding all of the messages that you had typed {\bf d} for, 20 and also discarding any that you typed {\bf s} for. By contrast, 21 file name? 22 /usr/dict/words 23 archetype 24 genotype 25 Linotype 26 ^COK, forget that file 27 file name? 28 Hwk4.tex 29 typedef struct PathNode *PNPtrType; 30 pointer type. The return value is a pointer to a list of PathNode 31 file name? 32 q 33 heather% cat $a40progs/Grep.c 34 35 36 /* program to illustrate use of signals 37 38 this is like the Unix `grep' command; it will report all lines 39 in a file that contain the given string; however, this one is 40 different: the user specifies the string first, and then the 41 program prompts the user for the names of the files to check 42 the string for, one file at a time 43 44 reading a large file will take some time, and while waiting 45 the user may change his/her mind, and withdraw the command 46 to check this particular file (the given string is still 47 valid, though, and will be used on whatever further files 48 the user specifies); this is where the signals are used */ 49 50 51 52 #define MaxLineLength 80 53 54 55 #include <stdio.h> 56 #include <signal.h> 57 #include <setjmp.h> 58 59 60 jmp_buf(GetFileName); 61 62 63 char Line[MaxLineLength], /* one line from an input file */ 64 String[MaxLineLength], /* current partial-word */ 65 FileName[50]; /* input file name */ 66 67 68 int StringLength; /* current length of the partial-word */ 69 70 71 FILE *FPtr; 72 73 74 /* ReadLine() will, as its name implies, read one line of the file; it 75 will return a value which will be the line length if it successfully 76 reads a line, or -1 if the end of the file was encountered */ 77 78 int ReadLine() 79 80 { char C; int LineLength; 81 82 LineLength = 0; 83 while (1) { 84 if (fscanf(FPtr,"%c",&C) == -1) return -1; 85 if (C != '\n') Line[LineLength++] = C; 86 else { 87 Line[LineLength] = 0; 88 break; 89 } 90 } 91 return LineLength; 92 } 93 94 95 FindAllMatches() 96 97 { int LL,J,MaxJ; 98 99 FPtr = fopen(FileName,"r"); 100 while (1) { 101 LL = ReadLine(); 102 if (LL == -1) break; 103 MaxJ = LL - StringLength + 1; 104 for (J = 0; J < MaxJ; J++) 105 if (!strncmp(String,Line+J,StringLength)) { 106 printf("%s\n",Line); 107 break; 108 } 109 } 110 } 111 112 113 CtrlC() 114 115 { printf("OK, forget that file\n"); 116 longjmp(GetFileName); 117 } 118 119 120 main() 121 122 { char NewLineChar; 123 124 signal(SIGINT,CtrlC); 125 printf("string?\n"); scanf("%s",String); scanf("%c",&NewLineChar); 126 StringLength = strlen(String); 127 while (1) { 128 setjmp(GetFileName); 129 printf("file name?\n"); scanf("%s",FileName); 130 if (!strcmp(FileName,"q")) exit(); 131 FindAllMatches(); 132 } 133 } 134 138 heather% e 139 heather% 140 script done on Wed May 27 16:22:48 1992
Analysis:
The non-signal part of the program is straightforward. The function main() has a while loop to go through each file (Lines 127-132), and for each file, there is a while loop to read in each line from the file and check for the given string (Lines 100-109).
On Line 124 we have the call to signal(). SIGINT is the signal number for control-c signals; it is defined in the #include file mentioned above.
When the user types control-c, we want the program to abandon the search in the present file, and start on the next file. In other words, when we finish executing CtrlC(), we do not want execution to resume at the line in the program where was at the instant the user typed control-c (typically somewhere in the range of Lines 100-109)--instead, what we want is for the program to jump to Line 129. This is accomplished by the longjmp() function, which in our case (Line 128) says to jump to the line named `GetFileName'. How do we assign a name to a line? Well, this is accomplished by the setjmp() function, which in our case (Line 128) says to name the next line (Line 129) GetFileName.
(This ``name'' will actually be an integer array which records the memory address of the first machine instruction the compiler generates from Line 129. One needs a declaration for this array, using the macro jmp_buf (Line 60), which again is defined in one of the #include files.)