

\documentstyle{article}

\setlength{\oddsidemargin}{0in}
\setlength{\evensidemargin}{0in}
\setlength{\topmargin}{0.0in}
\setlength{\headheight}{0in}
\setlength{\headsep}{0in}
\setlength{\textwidth}{6.5in}
\setlength{\textheight}{9.4in}
\setlength{\parindent}{0in}
\setlength{\parskip}{0.12in}

\begin{document}

\newcommand{\bfs}[1]{{\bf #1}}

\begin{center}
{\LARGE\bf Unix Signals

\end{center}

\bigskip

\section{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
\bfs{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 
{\it signals}.\footnote{This is similar to the concept of
{\bf 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 \bfs{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 \bfs{setjmp} and
\bfs{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$>$.

\section{Example Program}

The program below is described in the comments in Lines 3-15.  It
acts like the Unix \bfs{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 
\underline{without} canceling the \bfs{grep} command as a whole.\footnote{You
should make \underline{sure} that you understand why an ordinary \bfs{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).
  
\begin{verbatim}
  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
\end{verbatim}

\bigskip

\bfs{Analysis:}

The non-signal part of the program is straightforward.  The function
main() has a \bfs{while} loop to go through each file (Lines 127-132),
and for each file, there is a \bfs{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 \bfs{strncmp}
will begin there.}

On Line 124 we have the call to \bfs{signal()}.  SIGINT is the signal
number for control-c signals; it is defined in the \#include file 
mentioned above.\footnote{Or type \bfs{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 \bfs{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 {\it 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 \underline{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 \bfs{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 \bfs{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 {\bf return address} on that stack at that time.  By the way,
a {\bf 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.)

\end{document}



