]> asedeno.scripts.mit.edu Git - 1ts-debian.git/blob - zephyr/zwgc/port.c
r4275@bucket (orig r265): kcr | 2008-01-21 02:57:32 -0500
[1ts-debian.git] / zephyr / zwgc / port.c
1 /* This file is part of the Project Athena Zephyr Notification System.
2  * It is one of the source files comprising zwgc, the Zephyr WindowGram
3  * client.
4  *
5  *      Created by:     Marc Horowitz <marc@athena.mit.edu>
6  *
7  *      $Id$
8  *
9  *      Copyright (c) 1989 by the Massachusetts Institute of Technology.
10  *      For copying and distribution information, see the file
11  *      "mit-copyright.h".
12  */
13
14 #include <sysdep.h>
15
16 #if (!defined(lint) && !defined(SABER))
17 static const char rcsid_port_c[] = "$Id$";
18 #endif
19
20 #include <zephyr/mit-copyright.h>
21
22 /****************************************************************************/
23 /*                                                                          */
24 /*                    The Implementation of the port type:                  */
25 /*                                                                          */
26 /****************************************************************************/
27
28 #include "new_string.h"
29 #include "port_dictionary.h"
30 #include "port.h"
31 #include "notice.h"
32 #include "variables.h"
33
34 /****************************************************************************/
35 /*                                                                          */
36 /*                        Port methods (internal):                          */
37 /*                                                                          */
38 /****************************************************************************/
39
40 static string
41 port_get(port *p)
42 {
43     char *(*get_proc)(port *, char **);
44     char *error = NULL;
45     char *result;
46
47     if (p->status & INPUT_CLOSED) {
48         var_set_variable("error",
49                     "Attempt to read from a port whose input has been closed");
50         return(string_Copy(""));
51     }
52
53     get_proc = p->get;
54     if (!get_proc) {
55         var_set_variable("error",
56                  "Attempt to read from a port which does not support reading");
57         return(string_Copy(""));
58     }
59
60     result = get_proc(p, &error);
61     if (!result) {
62         var_set_variable("error", error);
63         return(string_Copy(""));
64     } else
65       return(result);
66 }
67
68 static void
69 port_put(port *p,
70          char *data,
71          int length)
72 {
73     char *(*put_proc)(port *, char *, int);
74     char *error;
75
76     if (p->status & OUTPUT_CLOSED) {
77         var_set_variable("error",
78                  "Attempt to write to a port whose output has been closed");
79         return;
80     }
81
82     put_proc = p->put;
83     if (!put_proc) {
84         var_set_variable("error",
85                  "Attempt to write to a port which does not support writing");
86         return;
87     }
88
89     error = put_proc(p, data, length);
90     if (error)
91       var_set_variable("error", error);
92 }
93
94 static void
95 port_close_input(port *p)
96 {
97     char *(*close_input_proc)(port *);
98     char *error;
99
100     if (p->status & INPUT_CLOSED)
101       return;
102     p->status |= INPUT_CLOSED;
103
104     close_input_proc = p->close_input;
105     if (!close_input_proc)
106       return;
107
108     error = close_input_proc(p);
109     if (error)
110       var_set_variable("error", error);
111 }
112
113 static void
114 port_close_output(port *p)
115 {
116     char *(*close_output_proc)(port *);
117     char *error;
118
119     if (p->status & OUTPUT_CLOSED)
120       return;
121     p->status |= OUTPUT_CLOSED;
122
123     close_output_proc = p->close_output;
124     if (!close_output_proc)
125       return;
126
127     error = close_output_proc(p);
128     if (error)
129       var_set_variable("error", error);
130 }
131
132 /****************************************************************************/
133 /*                                                                          */
134 /*                 Code to implement a namespace of ports:                  */
135 /*                                                                          */
136 /****************************************************************************/
137
138 /*
139  * port_dict - the dictionary mapping portnames to ports
140  */
141
142 static port_dictionary port_dict = NULL;
143
144 /*
145  *    void init_ports()
146  *        Modifies: all ports
147  *        Effects: Closes all existing ports.  Must be called before
148  *                 any other port call is made.
149  */
150
151 static void
152 close_port_from_binding(port_dictionary_binding *b)
153 {
154     port_close_input(&(b->value));
155     port_close_output(&(b->value));
156 }
157
158 void
159 init_ports(void)
160 {
161     if (port_dict) {
162         port_dictionary_Enumerate(port_dict, close_port_from_binding);
163         port_dictionary_Destroy(port_dict);
164     }
165
166     port_dict = port_dictionary_Create(31);
167 }
168
169 /*
170  * Internal Routine:
171  *
172  *    port *create_named_port(string name)
173  *        Modifies: the port named name
174  *        Requires: init_ports has been called
175  *        Effects: If a port with name name already exists, it is first
176  *                 closed (& destroyed).  A new unfilled in port is then
177  *                 created and assigned the name name.  Its address is
178  *                 then returned.  It is up to the caller to fill in its
179  *                 various fields correctly.
180  */
181
182 static port *
183 create_named_port(string name)
184 {
185     int already_exists;
186     port_dictionary_binding *binding;
187
188     binding = port_dictionary_Define(port_dict, name, &already_exists);
189     if (already_exists) {
190         port_close_input(&(binding->value));
191         port_close_output(&(binding->value));
192     }
193
194     return(&(binding->value));
195 }
196
197 /*
198  * Internal Routine:
199  *
200  *    port *get_named_port(string name)
201  *        Requires: init_ports has been called
202  *        Effects: If there is a port by name name, returns a pointer to
203  *                 it.  Otherwise returns NULL.
204  */
205
206 static port *
207 get_named_port(string name)
208 {
209     port_dictionary_binding *binding;
210
211     binding = port_dictionary_Lookup(port_dict, name);
212     if (!binding)
213       return(NULL);
214
215     return(&(binding->value));
216 }
217     
218 /****************************************************************************/
219 /*                                                                          */
220 /*                    External interface to named ports:                    */
221 /*                                                                          */
222 /****************************************************************************/
223
224 /*
225  *    string read_from_port(string name)
226  *        Requires: init_ports has been called
227  *        Modifies: the port named name if any, $error
228  *        Effects: If a port by name name does not exist, sets $error to
229  *                 "No such port" & returns "".  Otherwise, attempts to
230  *                 read from that port.  If an error occurs, $error is
231  *                 set to the error message and "" returned.  Otherwise
232  *                 the read string is returned.  The returned string is
233  *                 on the heap & must be eventually freed.
234  */
235
236 string
237 read_from_port(string name)
238 {
239     port *p;
240
241     if (!(p = get_named_port(name))) {
242         var_set_variable("error", "No such port");
243         return(string_Copy(""));
244     }
245
246     return(port_get(p));
247 }
248
249 /*
250  *    void write_on_port(string name, char *text, int length)
251  *        Requires: init_ports has been called, length>=0
252  *        Modifies: the port named name if any, $error
253  *        Effects: If a port by name name does not exist, sets $error to
254  *                 "No such port" & returns.  Otherwise, attempts to
255  *                 write text[0..length-1] on that port.  If an error
256  *                 occurs, $error is set to the error message.
257  */
258
259 void
260 write_on_port(string name,
261               char *text,
262               int length)
263 {
264     port *p;
265
266     if (!(p = get_named_port(name))) {
267         var_set_variable("error", "No such port");
268         return;
269     }
270
271     port_put(p, text, length);
272 }
273
274 /*
275  *    void close_port_input(string name)
276  *        Requires: init_ports has been called
277  *        Modifies: the port named name if any, $error
278  *        Effects: If a port by name name does not exist, sets $error to
279  *                 "No such port" & returns.  Otherwise, closes the
280  *                 input part of the port by name name.  When both a
281  *                 port's input & output parts have been closed, the
282  *                 port is deleted to save space.  If an error
283  *                 occurs, $error is set to the error message.
284  */
285
286 void
287 close_port_input(string name)
288 {
289     port_dictionary_binding *binding;
290
291     binding = port_dictionary_Lookup(port_dict, name);
292     if (!binding)
293       return;
294
295     port_close_input(&(binding->value));
296     if (binding->value.status == PORT_CLOSED)
297       port_dictionary_Delete(port_dict, binding);
298 }
299
300 /*
301  *    void close_port_output(string name)
302  *        Requires: init_ports has been called
303  *        Modifies: the port named name if any, $error
304  *        Effects: If a port by name name does not exist, sets $error to
305  *                 "No such port" & returns.  Otherwise, closes the
306  *                 output part of the port by name name.  When both a
307  *                 port's input & output parts have been closed, the
308  *                 port is deleted to save space.  If an error
309  *                 occurs, $error is set to the error message.
310  */
311
312 void
313 close_port_output(string name)
314 {
315     port_dictionary_binding *binding;
316
317     binding = port_dictionary_Lookup(port_dict, name);
318     if (!binding)
319       return;
320
321     port_close_output(&(binding->value));
322     if (binding->value.status == PORT_CLOSED)
323       port_dictionary_Delete(port_dict, binding);
324 }
325
326 /****************************************************************************/
327 /*                                                                          */
328 /*               Code to implement a port given some FILE *'s:              */
329 /*                                                                          */
330 /****************************************************************************/
331
332 static string
333 get_file(port *p,
334          char **error_p)
335 {
336     char buffer[10000]; /* <<<>>> */
337
338     if (!p->data.file.input_connector) {
339         *error_p = "Attempt to read past end of file";
340         return(NULL);
341     }
342
343     buffer[0] = 0;
344     errno = 0;
345     if (!fgets(buffer, 9999, p->data.file.input_connector)) {
346         if (errno)
347           *error_p = strerror(errno);
348         else
349           *error_p = "Attempt to read past end of file";
350
351         return(NULL);
352     }
353
354     buffer[9999] = 0;
355     return(string_Copy(buffer));
356 }
357
358 static char *
359 put_file(port *p,
360          string text,
361          int length)
362 {
363     if (!p->data.file.output_connector)
364       return(NULL);
365
366     errno = 0;
367     fwrite(text, 1, length, p->data.file.output_connector);
368     fflush(p->data.file.output_connector);
369
370     if (errno)
371       return(strerror(errno));
372
373     return(NULL);
374 }
375
376 static char *
377 close_file_input(port *p)
378 {
379     errno = 0;
380     if (p->data.file.input_connector) {
381         fclose(p->data.file.input_connector);
382         p->data.file.input_connector = 0;
383     }
384
385     if (errno)
386       return(strerror(errno));
387
388     return(NULL);
389 }
390
391 static char *
392 close_file_output(port *p)
393 {
394     errno = 0;
395     if (p->data.file.output_connector) {
396         fclose(p->data.file.output_connector);
397         p->data.file.output_connector = 0;
398     }
399
400     if (errno)
401       return(strerror(errno));
402
403     return(NULL);
404 }
405
406 void create_port_from_files(string name,
407                             FILE *input,
408                             FILE *output)
409 {
410     port *p = create_named_port(name);
411
412 #if !defined(__HIGHC__)
413     p->get = input ? get_file : NULL;
414     p->put = output ? put_file : NULL;
415 #else
416     /* RT compiler (hc2.1y) bug workaround */
417     if (input)
418         p->get = get_file;
419     else
420         p->get = NULL;
421     if (output)
422         p->put = put_file;
423     else
424         p->put = NULL;
425 #endif
426     p->close_input = close_file_input;
427     p->close_output = close_file_output;
428     p->status = 0;
429     p->data.file.input_connector = input;
430     p->data.file.output_connector = output;
431 }
432
433 /****************************************************************************/
434 /*                                                                          */
435 /*            Code for creating various types of FILE * ports:              */
436 /*                                                                          */
437 /****************************************************************************/
438
439 void
440 create_subprocess_port(string name,
441                        char **argv)
442 {
443     int pid;
444     int to_child_descriptors[2];
445     int to_parent_descriptors[2];
446     FILE *in = 0;
447     FILE *out = 0;
448
449     /* <<<>>> (file leak) */
450     if (pipe(to_child_descriptors)!=0 || pipe(to_parent_descriptors)!=0)
451       return;
452
453     pid = fork();
454     if (pid == -1) {
455         fprintf(stderr, "zwgc: error while attempting to fork: ");
456         perror("");
457         return; /* <<<>>> */
458     } else if (pid == 0) { /* in child */
459         close(0);
460         close(1);
461         dup2(to_child_descriptors[0], 0);
462         dup2(to_parent_descriptors[1], 1);
463         close(to_child_descriptors[1]);
464         close(to_parent_descriptors[0]);
465
466         execvp(argv[0], argv);
467         fprintf(stderr,"zwgc: unable to exec %s: ", argv[0]);
468         perror("");
469         _exit(errno);
470     }
471
472     fcntl(to_parent_descriptors[0], F_SETFD, 1);
473     fcntl(to_child_descriptors[1], F_SETFD, 1);
474     in = fdopen(to_parent_descriptors[0],"r");
475     out = fdopen(to_child_descriptors[1],"w");
476     close(to_child_descriptors[0]);
477     close(to_parent_descriptors[1]);
478
479     create_port_from_files(name, in, out);
480 }
481
482 void
483 create_file_append_port(string name,
484                         string filename)
485 {
486     FILE *out;
487     int oumask;
488
489     errno = 0;
490
491     oumask = umask(077);                /* allow read/write for us only */
492     out = fopen(filename, "a");
493     (void) umask(oumask);
494     if (out == NULL) {
495         var_set_variable("error", strerror(errno));
496         return;
497     }
498
499     create_port_from_files(name, 0, out);
500 }
501
502 void
503 create_file_input_port(string name,
504                        string filename)
505 {
506     FILE *in;
507
508     errno = 0;
509     in = fopen(filename, "r");
510     if (in == NULL) {
511         var_set_variable("error", strerror(errno));
512         return;
513     }
514
515     create_port_from_files(name, in, 0);
516 }
517
518 void
519 create_file_output_port(string name,
520                         string filename)
521 {
522     FILE *out;
523     int oumask;
524
525     errno = 0;
526
527     oumask = umask(077);                /* allow read/write for us only */
528     out = fopen(filename, "w");
529     (void) umask(oumask);
530     if (out == NULL) {
531         var_set_variable("error", strerror(errno));
532         return;
533     }
534
535     create_port_from_files(name, 0, out);
536 }
537
538 /****************************************************************************/
539 /*                                                                          */
540 /*             Code to implement a port given a filter function:            */
541 /*                                                                          */
542 /****************************************************************************/
543
544 static string
545 get_filter(port *p,
546            char **error_p)
547 {
548     string result;
549
550     if (string_stack_empty(p->data.filter.waiting_packets)) {
551         *error_p = "Attempt to read from port when no data available";
552         return(NULL);
553     }
554
555     result = string_stack_top(p->data.filter.waiting_packets);
556     string_stack_pop(p->data.filter.waiting_packets);
557     return(result);
558 }
559
560 static char *
561 put_filter(port *p,
562            string text,
563            int length)
564 {
565     string input;
566     string output;
567
568     if (p->status & INPUT_CLOSED)
569       return(NULL);
570
571     input = convert_nulls_to_newlines(text, length);
572     output = (*(p->data.filter.filter))(input);
573     free(input);
574     string_stack_push(p->data.filter.waiting_packets, output);
575     return(NULL);
576 }
577
578 static char *
579 close_filter_input(port *p)
580 {
581     while (!string_stack_empty(p->data.filter.waiting_packets))
582       string_stack_pop(p->data.filter.waiting_packets);
583
584     return(NULL);
585 }
586
587 /*ARGSUSED*/
588 static char *
589 close_filter_output(port *p)
590 {
591     return(NULL);
592 }
593
594 void
595 create_port_from_filter(string name,
596                         string (*filter)(string))
597 {
598     port *p = create_named_port(name);
599
600     p->get = get_filter;
601     p->put = put_filter;
602     p->close_input = close_filter_input;
603     p->close_output = close_filter_output;
604     p->status = 0;
605     p->data.filter.waiting_packets = string_stack_create();
606     p->data.filter.filter = filter;
607 }
608
609 /****************************************************************************/
610 /*                                                                          */
611 /*             Code to implement a port given an output function:           */
612 /*                                                                          */
613 /****************************************************************************/
614
615 static char *
616 put_output(port *p,
617            string text,
618            int length)
619 {
620     string input;
621     char *error;
622
623     input = convert_nulls_to_newlines(text, length);
624     error = p->data.output.output(input);
625     free(input);
626     return(error);
627 }
628
629 /*ARGSUSED*/
630 static char *
631 close_output(port *p)
632 {
633     return(NULL);
634 }
635
636 void
637 create_port_from_output_proc(string name,
638                              char *(*output)(string))
639 {
640 #ifdef SABER /* Yes, it's another ANSI incompatiblity */
641     port *p;
642 #else
643     port *p = create_named_port(name);
644 #endif
645
646 #ifdef SABER
647     p = create_named_port(name);
648 #endif
649
650     p->get = NULL;
651     p->put = put_output;
652     p->close_input = close_output;
653     p->close_output = close_output;
654     p->status = 0;
655     p->data.output.output = output;
656 }