Messo nuovo esempio e figura corretta.
authorSimone Piccardi <piccardi@gnulinux.it>
Fri, 28 Jun 2002 23:07:38 +0000 (23:07 +0000)
committerSimone Piccardi <piccardi@gnulinux.it>
Fri, 28 Jun 2002 23:07:38 +0000 (23:07 +0000)
img/pipeuse.dia
ipc.tex

index dcd771a25cd73c0a4420bd31f272107f1cb1d95a..9f4d609ae2a7353bf3d21c232d0edf2d52cce9b2 100644 (file)
Binary files a/img/pipeuse.dia and b/img/pipeuse.dia differ
diff --git a/ipc.tex b/ipc.tex
index e5631536c274f154f1304afa51cb82241931b9e3..40c9fadd4c20f23f608497e443a65a2972107868 100644 (file)
--- a/ipc.tex
+++ b/ipc.tex
@@ -86,8 +86,8 @@ capo della pipe, l'altro pu
 \begin{figure}[htb]
   \centering
   \includegraphics[height=5cm]{img/pipefork}
 \begin{figure}[htb]
   \centering
   \includegraphics[height=5cm]{img/pipefork}
-  \caption{Schema dell'uso di una pipe come mezzo di comunicazione fra
-  processo attraverso una \func{fork}.}
+  \caption{Schema dei collegamenti ad una pipe, condivisi fra processo padre e
+    figlio dopo l'esecuzione \func{fork}.}
   \label{fig:ipc_pipe_fork}
 \end{figure}
 
   \label{fig:ipc_pipe_fork}
 \end{figure}
 
@@ -109,45 +109,58 @@ Per capire meglio il funzionamento di una pipe faremo un esempio di quello che
 è il loro uso più comune, analogo a quello effettuato della shell, e che
 consiste nell'inviare l'output di un processo (lo standard output) sull'input
 di un'altro. Realizzaremo il programma nella forma di un
 è il loro uso più comune, analogo a quello effettuato della shell, e che
 consiste nell'inviare l'output di un processo (lo standard output) sull'input
 di un'altro. Realizzaremo il programma nella forma di un
-\textit{CGI}\footnote{Un CGI (\textit{Common Gateway Interface} è un programma
+\textit{CGI}\footnote{Un CGI (\textit{Common Gateway Interface}) è un programma
   che permette la creazione dinamica di un oggetto da inserire all'interno di
   una pagina HTML.}  per apache, che genera una immagine JPEG di un codice a
 barre, specificato come parametro di input.
 
   che permette la creazione dinamica di un oggetto da inserire all'interno di
   una pagina HTML.}  per apache, che genera una immagine JPEG di un codice a
 barre, specificato come parametro di input.
 
-Un programma che deve essere eseguito come \textit{cgi} per apache deve
-rispondere a delle caratteristiche specifiche, esso infatti non viene lanciato
-da una shell, ma dallo stesso web server, alla richiesta di una specifica URL
-che di solito ha la forma:
+Un programma che deve essere eseguito come \textit{CGI} deve rispondere a
+delle caratteristiche specifiche, esso infatti non viene lanciato da una
+shell, ma dallo stesso web server, alla richiesta di una specifica URL, che di
+solito ha la forma:
 \begin{verbatim}
 \begin{verbatim}
-http://www.sito.it/cgi-bin/programma?parametro
+    http://www.sito.it/cgi-bin/programma?parametro
 \end{verbatim}
 ed il risultato dell'elaborazione deve essere presentato (con una intestazione
 \end{verbatim}
 ed il risultato dell'elaborazione deve essere presentato (con una intestazione
-che ne descrive il mime-type) sullo standard output, in modo che apache possa
-reinviarlo al browser che ha effettuato la richiesta.
+che ne descrive il mime-type) sullo standard output, in modo che il web-server
+possa reinviarlo al browser che ha effettuato la richiesta, che in questo modo
+è in grado di visualizzarlo opportunamente.
 
 Per fare questo useremo in sequenza i programmi \cmd{barcode} e \cmd{gs}, il
 primo infatti è in grado di generare immagini postscript di codici a barre
 corrispondenti ad una qualunque stringa, mentre il secondo serve per poter
 
 Per fare questo useremo in sequenza i programmi \cmd{barcode} e \cmd{gs}, il
 primo infatti è in grado di generare immagini postscript di codici a barre
 corrispondenti ad una qualunque stringa, mentre il secondo serve per poter
-effettuare la conversione della stessa immagine in formato JPEG.
+effettuare la conversione della stessa immagine in formato JPEG. Usando una
+pipe potremo inviare l'output del primo sull'input del secondo, secondo lo
+schema mostrato in \figref{fig:ipc_pipe_use}, in cui la direzione del flusso
+dei dati è data dalle frecce continue.
+
+\begin{figure}[htb]
+  \centering
+  \includegraphics[height=5cm]{img/pipeuse}
+  \caption{Schema dell'uso di una pipe come mezzo di comunicazione fra
+    due processi attraverso attraverso l'esecuzione una \func{fork} e la
+    chiusura dei capi non utilizzati.}
+  \label{fig:ipc_pipe_use}
+\end{figure}
 
 Si potrebbe obiettare che sarebbe molto più semplice salvare il risultato
 
 Si potrebbe obiettare che sarebbe molto più semplice salvare il risultato
-intermedio su un file temporaneo. Questo però non tiene conto del fatto che il
-\textit{cgi} deve poter gestire più richieste in concorrenza, e si avrebbe una
+intermedio su un file temporaneo. Questo però non tiene conto del fatto che un
+\textit{CGI} deve poter gestire più richieste in concorrenza, e si avrebbe una
 evidente race condition in caso di accesso simultaneo a detto
 evidente race condition in caso di accesso simultaneo a detto
-file.\footnote{la questione potrebbe essere evitata creando prima dei file
-  temporanei, da comunicare poi ai vari sotto-processi, da cancellare alla
-  fine dell'esecuzione; ma a questo punto avremmo perso tutta la semplicità.}
-L'uso di una pipe invece permette di risolvere il problema in maniera semplice
-ed elegante.
-
-Il programma ci servirà anche come esempio dell'uso di alcune delle funzioni
-di manipolazione dei file descriptor, come \func{dup} e \func{dup2}, viste in
-\secref{sec:file_dup}; è attraverso queste funzioni che è possibile dirottare
-gli stream standard dei processi (che abbiamo visto in
-\secref{sec:file_std_descr} e \secref{sec:file_std_stream}) sulla pipe. Le
-sezioni significative del programma è riportato in
-\figref{fig:ipc_barcode_code}, il codice è disponibile nel file
-\file{BarCode.c} nella directory dei sorgenti.
+file.\footnote{il problema potrebbe essere superato determinando in anticipo
+  un nome appropiato per il file temporaneo, che verrebbe utilizzato dai vari
+  sotto-processi, e cancellato alla fine della loro esecuzione; ma a questo le
+  cose non sarebbero più tanto semplici.}  L'uso di una pipe invece permette
+di risolvere il problema in maniera semplice ed elegante.
+
+Il programma ci servirà anche come esempio dell'uso delle funzioni di
+duplicazione dei file descriptor che abbiamo trattato in
+\secref{sec:file_dup}, in particolare di \func{dup2}. È attraverso queste
+funzioni infatti che è possibile dirottare gli stream standard dei processi
+(che abbiamo visto in \secref{sec:file_std_descr} e
+\secref{sec:file_std_stream}) sulla pipe. In \figref{fig:ipc_barcode_code}
+abbiamo riportato il corpo del programm, il cui codice completo è disponibile
+nel file \file{BarCodePage.c} che si trova nella directory dei sorgenti.
 
 
 \begin{figure}[!htb]
 
 
 \begin{figure}[!htb]
@@ -204,47 +217,46 @@ int main(int argc, char *argv[], char *envp[])
     \end{lstlisting}
   \end{minipage} 
   \normalsize 
     \end{lstlisting}
   \end{minipage} 
   \normalsize 
-  \caption{Codice del \textit{cgi-bin} \cmd{BarCode}.}
+  \caption{Codice del \textit{CGI} \cmd{BarCodePage}.}
   \label{fig:ipc_barcode_code}
 \end{figure}
 
   \label{fig:ipc_barcode_code}
 \end{figure}
 
-Il primo passo (\texttt{\small 4--12}) è quello di creare le due pipe che
-servono per la comunicazione fra i due programmi che verranno utilizzati per
-produrre il codice a barre; si ha cura di controllare la riuscita della
+La prima operazione del programma (\texttt{\small 4--12}) è quella di creare
+le due pipe che serviranno per la comunicazione fra i due comandi utilizzati
+per produrre il codice a barre; si ha cura di controllare la riuscita della
 chiamata, inviando in caso di errore un messaggio invece dell'immagine
 richiesta.\footnote{la funzione \func{WriteMess}, non è riportata in
 chiamata, inviando in caso di errore un messaggio invece dell'immagine
 richiesta.\footnote{la funzione \func{WriteMess}, non è riportata in
-  \ref{fig:ipc_barcode_code}, ma si incarica semplicemente di formattare
-  l'uscita, aggiungendo un \textit{mime type}, in modo che possa essere
-  interpretata direttamente da un browser.}
+  \secref{fig:ipc_barcode_code}; essa si incarica semplicemente di formattare
+  l'uscita alla maniera dei CGI, aggiungendo l'opportuno \textit{mime type}, e
+  formattando il messaggio in HTML, in modo che quest'ultimo possa essere
+  visualizzato correttamente da un browser.}
 
 
-Una volta create le pipe il programma può creare (\texttt{\small 13-17}) il
+Una volta create le pipe, il programma può creare (\texttt{\small 13-17}) il
 primo processo figlio, che si incaricherà (\texttt{\small 19--25}) di eseguire
 primo processo figlio, che si incaricherà (\texttt{\small 19--25}) di eseguire
-il programma \cmd{barcode}: quest'ultimo funziona ricevendo dallo standard
-input la stringa da convertire nell'immagine postscript del codice a barre,
-che sarà scritta sullo standard output.
-
-Per utilizzare queste caratteristiche il primo figlio chiude (\texttt{\small
-  20}) il capo aperto in scrittura della prima pipe, dato che userà il capo
-aperto in lettura per ricevere dal padre la stringa da codificare; per far
-questo collega (\texttt{\small 21}) il capo in lettura della pipe allo
-standard input usando \func{dup2}. Dato che \cmd{barcode} scrive l'immagine
-postscript del codice a barre sullo standard output per poter effettuare una
-ulteriore redirezione il capo in lettura della seconda pipe viene chiuso
-(\texttt{\small 22}) mentre il capo in scrittura viene collegato allo standard
-output (\texttt{\small 23}).  
+\cmd{barcode}. Quest'ultimo legge dallo standard input una stringa di
+caratteri, la converte nell'immagine postscript del codice a barre ad essa
+corrispondente, e poi scrive il risultato direttamente sullo standard output.
+
+Per poter utilizzare queste caratteristiche prima di eseguire \cmd{barcode} si
+chiude (\texttt{\small 20}) il capo aperto in scrittura della prima pipe, e se
+ne collega (\texttt{\small 21}) il capo in lettura allo standard input, usando
+\func{dup2}. Dato che \cmd{barcode} scrive l'immagine postscript del codice a
+barre sullo standard output, per poter effettuare una ulteriore redirezione il
+capo in lettura della seconda pipe viene chiuso (\texttt{\small 22}) mentre il
+capo in scrittura viene collegato allo standard output (\texttt{\small 23}).
 
 In questo modo all'esecuzione (\texttt{\small 25}) di \cmd{barcode} (cui si
 
 In questo modo all'esecuzione (\texttt{\small 25}) di \cmd{barcode} (cui si
-passa in \var{size} la dimensione per l'immagine) quest'ultimo leggerà la
-stringa da codificare che gli viene inviata dal padre dalla prima pipe e
-scriverà l'immagine postscript del codice a barre sulla seconda.
-
-Dall'altra parte il processo padre prima chiude (\texttt{\small 26}) il capo
-inutilizzato della prima pipe (quello in input), poi scrive (\texttt{\small
-  27}) la stringa da convertire sul capo in output, così che \cmd{barcode}
-possa riceverla dallo standard input; a questo punto l'uso della prima pipe è
-finito ed essa può essere definitivamente chiusa (\texttt{\small 28}), si
-attende poi (\texttt{\small 29}) che l'esecuzione di \cmd{barcode} sia
-completata.
+passa in \var{size} la dimensione della pagina per l'immagine) quest'ultimo
+leggerà dalla prima pipe la stringa da codificare che gli sarà inviata dal
+padre, e scriverà l'immagine postscript del codice a barre sulla seconda.
+
+Al contempo una volta lanciato il primo figlio, il processo padre prima chiude
+(\texttt{\small 26}) il capo inutilizzato della prima pipe (quello in input) e
+poi scrive (\texttt{\small 27}) la stringa da convertire sul capo in output,
+così che \cmd{barcode} possa riceverla dallo standard input. A questo punto
+l'uso della prima pipe è finito ed essa può essere definitivamente chiusa
+(\texttt{\small 28}), si attende poi (\texttt{\small 29}) che l'esecuzione di
+\cmd{barcode} sia completata.
 
 Alla conclusione della sua esecuzione \cmd{barcode} avrà inviato l'immagine
 postscript del codice a barre sul capo in scrittura della seconda pipe; a
 
 Alla conclusione della sua esecuzione \cmd{barcode} avrà inviato l'immagine
 postscript del codice a barre sul capo in scrittura della seconda pipe; a
@@ -254,24 +266,25 @@ processo figlio, che poi (\texttt{\small 35--42}) eseguir
 leggendo l'immagine postscript creata da \cmd{barcode} dallo standard input,
 per convertirla in JPEG.
 
 leggendo l'immagine postscript creata da \cmd{barcode} dallo standard input,
 per convertirla in JPEG.
 
-Per fare tutto ciò il secondo figlio anzitutto chiude (\texttt{\small 37}) il
-capo in scrittura della seconda pipe, e ne collega (\texttt{\small 38}) il
-capo in lettura allo standard input. Per poter formattare l'output del
-programma in maniera utilizzabile da un browser, si provvede anche
-\texttt{\small 40}) alla scrittura dell'apposita stringa di mime-type in testa
-allo standard output. A questo punto si può invocare \texttt{\small 41}) il
-programma \cmd{gs}, provvedendo gli appositi switch che consentono di leggere
-il file da convertire dallo standard input, ed inviare la conversione sullo
-standard output.
-
-Per concludere le operazioni il processo padre chiude \texttt{\small 44}) il
+Per fare tutto ciò anzitutto si chiude (\texttt{\small 37}) il capo in
+scrittura della seconda pipe, e se ne collega (\texttt{\small 38}) il capo in
+lettura allo standard input. Per poter formattare l'output del programma in
+maniera utilizzabile da un browser, si provvede anche \texttt{\small 40}) alla
+scrittura dell'apposita stringa di identificazione del mime-type in testa allo
+standard output. A questo punto si può invocare \texttt{\small 41}) \cmd{gs},
+provvedendo gli appositi switch che consentono di leggere il file da
+convertire dallo standard input e di inviare la conversione sullo standard
+output.
+
+Per completare le operazioni il processo padre chiude \texttt{\small 44}) il
 capo in scrittura della seconda pipe, e attende la conclusione del figlio
 capo in scrittura della seconda pipe, e attende la conclusione del figlio
-\texttt{\small 45}), per poi uscire \texttt{\small 46}). Si tenga conto che,
-l'operazione di chiudere il capo in scrittura della seconda pipe è necessaria,
-infatti non chiudendola \cmd{gs}, che legge il suo stardard input da detta
-pipe, resterebbe bloccato in attesa di ulteriore input (l'unico modo che un
-programma ha per sapere che l'input è terminato è rilevare che lo standard
-input è stato chiuso), e la \func{wait} non ritornerebbe. 
+\texttt{\small 45}); a questo punto può \texttt{\small 46}). Si tenga conto
+che l'operazione di chiudere il capo in scrittura della seconda pipe è
+necessaria, infatti se non venisse chiusa \cmd{gs}, che legge il suo stardard
+input da detta pipe, resterebbe bloccato in attesa di ulteriori dati in
+ingresso (l'unico modo che un programma ha per sapere che l'input è terminato
+è rilevare che lo standard input è stato chiuso), e la \func{wait} non
+ritornerebbe.
 
 
 \subsection{Le funzioni \func{popen} e \func{pclose}}
 
 
 \subsection{Le funzioni \func{popen} e \func{pclose}}
@@ -294,13 +307,12 @@ stream restituito come valore di ritorno.
   potrà assumere i valori relativi alle sottostanti invocazioni di \func{pipe}
   e \func{fork} o \macro{EINVAL} se \param{type} non è valido.}
 \end{prototype}
   potrà assumere i valori relativi alle sottostanti invocazioni di \func{pipe}
   e \func{fork} o \macro{EINVAL} se \param{type} non è valido.}
 \end{prototype}
-\noindent e serve per semplificare l'uso di \func{pipe}. 
 
 La funzione crea una pipe, esegue una \func{fork}, ed invoca il programma
 \param{command} attraverso la shell (in sostanza esegue \file{/bin/sh} con il
 
 La funzione crea una pipe, esegue una \func{fork}, ed invoca il programma
 \param{command} attraverso la shell (in sostanza esegue \file{/bin/sh} con il
-flag \code{-c}); l'argomento \param{type} deve essere una stringa \verb|"w"| o
-\verb|"r"|, per indicare se la pipe sarà collegata allo standard input o allo
-standard output del comando invocato.
+flag \code{-c}); l'argomento \param{type} deve essere una delle due stringhe
+\verb|"w"| o \verb|"r"|, per indicare se la pipe sarà collegata allo standard
+input o allo standard output del comando invocato.
 
 La funzione restituisce il puntatore allo stream associato alla pipe creata,
 che sarà aperto in sola lettura (e quindi associato allo standard output del
 
 La funzione restituisce il puntatore allo stream associato alla pipe creata,
 che sarà aperto in sola lettura (e quindi associato allo standard output del
@@ -308,10 +320,11 @@ programma indicato) in caso si sia indicato \code{"r"}, o in sola scrittura (e
 quindi associato allo standard input) in caso di \code{"w"}.
 
 Lo stream restituito da \func{popen} è identico a tutti gli effetti ai file
 quindi associato allo standard input) in caso di \code{"w"}.
 
 Lo stream restituito da \func{popen} è identico a tutti gli effetti ai file
-standard visti in \secref{cha:files_std_interface}, e viene sempre aperto in
-modalità \textit{fully-buffered} (vedi \secref{sec:file_buffering}); l'unica
-differenza è che deve essere chiuso dalla seconda delle due funzioni,
-\func{pclose}, il cui prototipo è:
+stream visti in \secref{cha:files_std_interface}, anche se è collegato ad una
+pipe e non ad un inode, e viene sempre aperto in modalità
+\textit{fully-buffered} (vedi \secref{sec:file_buffering}); l'unica differenza
+con gli usuali stream è che dovrà essere chiuso dalla seconda delle due nuove
+funzioni, \func{pclose}, il cui prototipo è:
 \begin{prototype}{stdio.h}
 {int pclose(FILE *stream)}
 
 \begin{prototype}{stdio.h}
 {int pclose(FILE *stream)}
 
@@ -322,23 +335,49 @@ attendendo la terminazione del processo ad essa associato.
   errore; nel quel caso il valore di \func{errno} deriva dalle sottostanti
   chiamate.}
 \end{prototype}
   errore; nel quel caso il valore di \func{errno} deriva dalle sottostanti
   chiamate.}
 \end{prototype}
-\noindent che si incarica anche di attendere la conclusione del processo
-creato dalla precedente \func{popen}.
-
-Per illustrare l'uso di queste due funzioni riprendiamo l'esempio in
-\figref{fig:ipc_barcode_code}: esso per quanto funzionante, è piuttosto
-complesso; inoltre nella pratica sconta un problema di \cmd{gs} che non è in
+\noindent che oltre alla chiusura dello stream si incarica anche di attendere
+(tramite \func{wait4}) la conclusione del processo creato dalla precedente
+\func{popen}.
+
+Per illustrare l'uso di queste due funzioni riprenderemo il problema
+precedente; il programma mostrato in \figref{fig:ipc_barcode_code} per quanto
+funzionante, è (volutamente) codificato in maniera piuttosto complesso;
+inoltre nella pratica sconta un problema di \cmd{gs} che non è in
 grado\footnote{nella versione GNU Ghostscript 6.53 (2002-02-13).} di
 riconoscere correttamente l'encapsulated postscript, per cui tutte le volte
 grado\footnote{nella versione GNU Ghostscript 6.53 (2002-02-13).} di
 riconoscere correttamente l'encapsulated postscript, per cui tutte le volte
-generata una pagina intera, invece che una semplice figura.  Se si vuole
-generare una immagine di dimensioni corrette si deve allora ricorrere ad
-ulteriore programma, \cmd{epstopsf}, per convertire in PDF il file EPS
-generato da \cmd{barcode}. Utilizzando un file in PDF invece, \cmd{gs} esegue
-la conversione rispettando le dimensioni originarie del codice a barre.
-
-Ci si trova dunque davanti al classico caso dell'uso delle pipe in cui si
-devono eseguire più processi in fila, inviando l'output di ciascuno all'input
-del successivo, per poi ottenere il risultato finale sullo standard output.
+generata una pagina intera, invece che semplice figura delle dimensioni
+corrispondenti al codice a barre.
+
+Se si vuole generare una immagine di dimensioni corrette si deve usare un
+approccio diverso; una possibilità sarebbe quella di ricorrere ad ulteriore
+programma, \cmd{epstopsf}, per convertire in PDF il file EPS generato da
+\cmd{barcode} (utilizzando lo switch \cmd{-E} di quest'ultimo). Utilizzando un
+PDF al posto di un EPS \cmd{gs} esegue la conversione rispettando le
+dimensioni originarie del codice a barre e produce un JPEG delle dimensioni
+adeguate. 
+
+Questo però ci porta a scontrarci con una caratteristica peculiare delle pipe,
+che a prima vista non è evidente. Per poter effettuare la conversione di un
+PDF infatti è necessario, per la struttura del formato, dover eseguire delle
+\func{lseek} sul file da convertire; una pipe però è rigidamente sequenziale,
+ed il tentativo di eseguire detta funzioni su un file descriptor associato ad
+una pipe comporta l'immediato fallimento con un errore di \macro{ESPIPE}.
+
+Per questo motivo si è utilizzata una strada diversa, che prevede la
+conversione attraverso \cmd{gs} del PS in un altro formato intermedio, il
+PPM,\footnote{il \textit{Portable PixMap file format} è un formato usato
+  spesso come formato intermedio per effettuare conversioni, è estremamente
+  inefficiente, ma molto facile da manipolare dato che usa caratteri ASCII per
+  memorizzare le immagini.} dal quale poi si può ottenere un'immagine di
+dimensioni corrette attraverso vari programmi di manipolazione (\cmd{pnmcrop},
+\cmd{pnmmargin}) che può essere infine trasformata in PNG.
+
+In questo caso però occorre eseguire sequenza ben quattro comandi diversi,
+inviando l'output di ciascuno all'input del successivo, per poi ottenere il
+risultato finale sullo standard output, il caso più classico dell'uso delle
+pipe. 
+
+
 Dato che questo caso ciascun processo deve scrivere il suo output sullo
 standard input del successivo, occorrerà usare \func{popen} aprendo la pipe in
 scrittura.
 Dato che questo caso ciascun processo deve scrivere il suo output sullo
 standard input del successivo, occorrerà usare \func{popen} aprendo la pipe in
 scrittura.
@@ -357,6 +396,44 @@ quest'ultimo venga invocato dopo.
     \begin{lstlisting}{}
 int main(int argc, char *argv[], char *envp[])
 {
     \begin{lstlisting}{}
 int main(int argc, char *argv[], char *envp[])
 {
+int main(int argc, char *argv[], char *envp[])
+{
+    FILE *pipe[4];
+    FILE *pipein;
+    char *cmd_string[4]={
+        "pnmtopng",
+        "pnmmargin -white 10",
+        "pnmcrop",
+        "gs -sDEVICE=ppmraw -sOutputFile=- -sNOPAUSE -q - -c showpage -c quit"
+    };  
+    char content[]="Content-type: image/png\n\n";
+    int i;
+    /* write mime-type to stout */ 
+    write(STDOUT_FILENO, content, strlen(content));
+    /* execute chain of command */
+    for (i=0; i<4; i++) {
+        pipe[i] = popen(cmd_string[i], "w");
+        dup2(fileno(pipe[i]), STDOUT_FILENO); 
+    }
+    /* create barcode (in PS) */
+    pipein = popen("barcode", "w");
+    /* send barcode string to barcode program */
+    write(fileno(pipein), argv[1], strlen(argv[1]));
+    /* close all pipes (in reverse order) */
+    for (i=4; i==0; i--) {
+        pclose((pipe[i]));
+    }
+    exit(0);
+}
+/*
+ * Routine to produce an HTML error message on output 
+ */
+void WriteMess(char *mess)
+{
+    printf("Content-type: text/html\n\n");
+    perror(mess);
+    printf("<br>\n");
+}
 }
 
     \end{lstlisting}
 }
 
     \end{lstlisting}