# Technical Overview

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.

footnote: This is similar to the concept of interrupts studied in ECS 50 and EEC 70.
Actually, a signal-handler function, which we will see an example of here, is a high-level version of an interrupt-handler; the latter still exists, but is hidden from the ordinary programmer, who works only through the former. This is good both for convenience, and because on timesharing systems, ordinary users are not allowed to access the portions of memory in which interrupt vectors are placed. For example, you can arrange things so that if the user hits ctrl-c, the program calls a programmer-specified function instead of just being killed.

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>.

# Example Program

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.

footnote: You should make sure that you understand why an ordinary scanf call will not work here.

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
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)  {
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).

footnote: Note the expression Line+J in Line 105. Recall that an array name without a subscript, in this case Line', is taken to be a pointer to the beginning of that array. Thus Line+J is taken to be pointer arithmetic, with the result being a pointer to Line[J]; the string comparison of strncmp will begin there.

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.

footnote: Or type man signal. There are lots of other signal types, e.g. SIGHUP, which is generated if the user has a phone-in connection to the machine and suddenly hangs up the phone while the program is running.
In this call to signal() we are saying that whenever the user types control-c, we want the program to call the function CtrlC() (Lines 113-117).
footnote: Such a function is called a signal-handler. We say, for example, that on Line 124 we are telling the system that we want our function CtrlC() to be the signal-handler for SIGINT-type signals.

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.

footnote: Again, the concepts in ECS 50 and EEC 70 provide more detail here. What is actually happening is that GetFileName will contain the memory address of the first machine instruction in the compiled form of Line 129. How can the function setjmp() know'' this address? The answer is that this address will be the return address on that stack at that time. By the way, a goto statement won't work here, because one can't jump to a goto which is in a different function.

(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.)

Norm Matloff
Wed Nov 8 17:36:58 PST 1995