adding zephyr-im master branch
This commit is contained in:
656
zwgc/port.c
Normal file
656
zwgc/port.c
Normal file
@ -0,0 +1,656 @@
|
||||
/* This file is part of the Project Athena Zephyr Notification System.
|
||||
* It is one of the source files comprising zwgc, the Zephyr WindowGram
|
||||
* client.
|
||||
*
|
||||
* Created by: Marc Horowitz <marc@athena.mit.edu>
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (c) 1989 by the Massachusetts Institute of Technology.
|
||||
* For copying and distribution information, see the file
|
||||
* "mit-copyright.h".
|
||||
*/
|
||||
|
||||
#include <sysdep.h>
|
||||
|
||||
#if (!defined(lint) && !defined(SABER))
|
||||
static const char rcsid_port_c[] = "$Id$";
|
||||
#endif
|
||||
|
||||
#include <zephyr/mit-copyright.h>
|
||||
|
||||
/****************************************************************************/
|
||||
/* */
|
||||
/* The Implementation of the port type: */
|
||||
/* */
|
||||
/****************************************************************************/
|
||||
|
||||
#include "new_string.h"
|
||||
#include "port_dictionary.h"
|
||||
#include "port.h"
|
||||
#include "notice.h"
|
||||
#include "variables.h"
|
||||
|
||||
/****************************************************************************/
|
||||
/* */
|
||||
/* Port methods (internal): */
|
||||
/* */
|
||||
/****************************************************************************/
|
||||
|
||||
static string
|
||||
port_get(port *p)
|
||||
{
|
||||
char *(*get_proc)(port *, char **);
|
||||
char *error = NULL;
|
||||
char *result;
|
||||
|
||||
if (p->status & INPUT_CLOSED) {
|
||||
var_set_variable("error",
|
||||
"Attempt to read from a port whose input has been closed");
|
||||
return(string_Copy(""));
|
||||
}
|
||||
|
||||
get_proc = p->get;
|
||||
if (!get_proc) {
|
||||
var_set_variable("error",
|
||||
"Attempt to read from a port which does not support reading");
|
||||
return(string_Copy(""));
|
||||
}
|
||||
|
||||
result = get_proc(p, &error);
|
||||
if (!result) {
|
||||
var_set_variable("error", error);
|
||||
return(string_Copy(""));
|
||||
} else
|
||||
return(result);
|
||||
}
|
||||
|
||||
static void
|
||||
port_put(port *p,
|
||||
char *data,
|
||||
int length)
|
||||
{
|
||||
char *(*put_proc)(port *, char *, int);
|
||||
char *error;
|
||||
|
||||
if (p->status & OUTPUT_CLOSED) {
|
||||
var_set_variable("error",
|
||||
"Attempt to write to a port whose output has been closed");
|
||||
return;
|
||||
}
|
||||
|
||||
put_proc = p->put;
|
||||
if (!put_proc) {
|
||||
var_set_variable("error",
|
||||
"Attempt to write to a port which does not support writing");
|
||||
return;
|
||||
}
|
||||
|
||||
error = put_proc(p, data, length);
|
||||
if (error)
|
||||
var_set_variable("error", error);
|
||||
}
|
||||
|
||||
static void
|
||||
port_close_input(port *p)
|
||||
{
|
||||
char *(*close_input_proc)(port *);
|
||||
char *error;
|
||||
|
||||
if (p->status & INPUT_CLOSED)
|
||||
return;
|
||||
p->status |= INPUT_CLOSED;
|
||||
|
||||
close_input_proc = p->close_input;
|
||||
if (!close_input_proc)
|
||||
return;
|
||||
|
||||
error = close_input_proc(p);
|
||||
if (error)
|
||||
var_set_variable("error", error);
|
||||
}
|
||||
|
||||
static void
|
||||
port_close_output(port *p)
|
||||
{
|
||||
char *(*close_output_proc)(port *);
|
||||
char *error;
|
||||
|
||||
if (p->status & OUTPUT_CLOSED)
|
||||
return;
|
||||
p->status |= OUTPUT_CLOSED;
|
||||
|
||||
close_output_proc = p->close_output;
|
||||
if (!close_output_proc)
|
||||
return;
|
||||
|
||||
error = close_output_proc(p);
|
||||
if (error)
|
||||
var_set_variable("error", error);
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
/* */
|
||||
/* Code to implement a namespace of ports: */
|
||||
/* */
|
||||
/****************************************************************************/
|
||||
|
||||
/*
|
||||
* port_dict - the dictionary mapping portnames to ports
|
||||
*/
|
||||
|
||||
static port_dictionary port_dict = NULL;
|
||||
|
||||
/*
|
||||
* void init_ports()
|
||||
* Modifies: all ports
|
||||
* Effects: Closes all existing ports. Must be called before
|
||||
* any other port call is made.
|
||||
*/
|
||||
|
||||
static void
|
||||
close_port_from_binding(port_dictionary_binding *b)
|
||||
{
|
||||
port_close_input(&(b->value));
|
||||
port_close_output(&(b->value));
|
||||
}
|
||||
|
||||
void
|
||||
init_ports(void)
|
||||
{
|
||||
if (port_dict) {
|
||||
port_dictionary_Enumerate(port_dict, close_port_from_binding);
|
||||
port_dictionary_Destroy(port_dict);
|
||||
}
|
||||
|
||||
port_dict = port_dictionary_Create(31);
|
||||
}
|
||||
|
||||
/*
|
||||
* Internal Routine:
|
||||
*
|
||||
* port *create_named_port(string name)
|
||||
* Modifies: the port named name
|
||||
* Requires: init_ports has been called
|
||||
* Effects: If a port with name name already exists, it is first
|
||||
* closed (& destroyed). A new unfilled in port is then
|
||||
* created and assigned the name name. Its address is
|
||||
* then returned. It is up to the caller to fill in its
|
||||
* various fields correctly.
|
||||
*/
|
||||
|
||||
static port *
|
||||
create_named_port(string name)
|
||||
{
|
||||
int already_exists;
|
||||
port_dictionary_binding *binding;
|
||||
|
||||
binding = port_dictionary_Define(port_dict, name, &already_exists);
|
||||
if (already_exists) {
|
||||
port_close_input(&(binding->value));
|
||||
port_close_output(&(binding->value));
|
||||
}
|
||||
|
||||
return(&(binding->value));
|
||||
}
|
||||
|
||||
/*
|
||||
* Internal Routine:
|
||||
*
|
||||
* port *get_named_port(string name)
|
||||
* Requires: init_ports has been called
|
||||
* Effects: If there is a port by name name, returns a pointer to
|
||||
* it. Otherwise returns NULL.
|
||||
*/
|
||||
|
||||
static port *
|
||||
get_named_port(string name)
|
||||
{
|
||||
port_dictionary_binding *binding;
|
||||
|
||||
binding = port_dictionary_Lookup(port_dict, name);
|
||||
if (!binding)
|
||||
return(NULL);
|
||||
|
||||
return(&(binding->value));
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
/* */
|
||||
/* External interface to named ports: */
|
||||
/* */
|
||||
/****************************************************************************/
|
||||
|
||||
/*
|
||||
* string read_from_port(string name)
|
||||
* Requires: init_ports has been called
|
||||
* Modifies: the port named name if any, $error
|
||||
* Effects: If a port by name name does not exist, sets $error to
|
||||
* "No such port" & returns "". Otherwise, attempts to
|
||||
* read from that port. If an error occurs, $error is
|
||||
* set to the error message and "" returned. Otherwise
|
||||
* the read string is returned. The returned string is
|
||||
* on the heap & must be eventually freed.
|
||||
*/
|
||||
|
||||
string
|
||||
read_from_port(string name)
|
||||
{
|
||||
port *p;
|
||||
|
||||
if (!(p = get_named_port(name))) {
|
||||
var_set_variable("error", "No such port");
|
||||
return(string_Copy(""));
|
||||
}
|
||||
|
||||
return(port_get(p));
|
||||
}
|
||||
|
||||
/*
|
||||
* void write_on_port(string name, char *text, int length)
|
||||
* Requires: init_ports has been called, length>=0
|
||||
* Modifies: the port named name if any, $error
|
||||
* Effects: If a port by name name does not exist, sets $error to
|
||||
* "No such port" & returns. Otherwise, attempts to
|
||||
* write text[0..length-1] on that port. If an error
|
||||
* occurs, $error is set to the error message.
|
||||
*/
|
||||
|
||||
void
|
||||
write_on_port(string name,
|
||||
char *text,
|
||||
int length)
|
||||
{
|
||||
port *p;
|
||||
|
||||
if (!(p = get_named_port(name))) {
|
||||
var_set_variable("error", "No such port");
|
||||
return;
|
||||
}
|
||||
|
||||
port_put(p, text, length);
|
||||
}
|
||||
|
||||
/*
|
||||
* void close_port_input(string name)
|
||||
* Requires: init_ports has been called
|
||||
* Modifies: the port named name if any, $error
|
||||
* Effects: If a port by name name does not exist, sets $error to
|
||||
* "No such port" & returns. Otherwise, closes the
|
||||
* input part of the port by name name. When both a
|
||||
* port's input & output parts have been closed, the
|
||||
* port is deleted to save space. If an error
|
||||
* occurs, $error is set to the error message.
|
||||
*/
|
||||
|
||||
void
|
||||
close_port_input(string name)
|
||||
{
|
||||
port_dictionary_binding *binding;
|
||||
|
||||
binding = port_dictionary_Lookup(port_dict, name);
|
||||
if (!binding)
|
||||
return;
|
||||
|
||||
port_close_input(&(binding->value));
|
||||
if (binding->value.status == PORT_CLOSED)
|
||||
port_dictionary_Delete(port_dict, binding);
|
||||
}
|
||||
|
||||
/*
|
||||
* void close_port_output(string name)
|
||||
* Requires: init_ports has been called
|
||||
* Modifies: the port named name if any, $error
|
||||
* Effects: If a port by name name does not exist, sets $error to
|
||||
* "No such port" & returns. Otherwise, closes the
|
||||
* output part of the port by name name. When both a
|
||||
* port's input & output parts have been closed, the
|
||||
* port is deleted to save space. If an error
|
||||
* occurs, $error is set to the error message.
|
||||
*/
|
||||
|
||||
void
|
||||
close_port_output(string name)
|
||||
{
|
||||
port_dictionary_binding *binding;
|
||||
|
||||
binding = port_dictionary_Lookup(port_dict, name);
|
||||
if (!binding)
|
||||
return;
|
||||
|
||||
port_close_output(&(binding->value));
|
||||
if (binding->value.status == PORT_CLOSED)
|
||||
port_dictionary_Delete(port_dict, binding);
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
/* */
|
||||
/* Code to implement a port given some FILE *'s: */
|
||||
/* */
|
||||
/****************************************************************************/
|
||||
|
||||
static string
|
||||
get_file(port *p,
|
||||
char **error_p)
|
||||
{
|
||||
char buffer[10000]; /* <<<>>> */
|
||||
|
||||
if (!p->data.file.input_connector) {
|
||||
*error_p = "Attempt to read past end of file";
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
buffer[0] = 0;
|
||||
errno = 0;
|
||||
if (!fgets(buffer, 9999, p->data.file.input_connector)) {
|
||||
if (errno)
|
||||
*error_p = strerror(errno);
|
||||
else
|
||||
*error_p = "Attempt to read past end of file";
|
||||
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
buffer[9999] = 0;
|
||||
return(string_Copy(buffer));
|
||||
}
|
||||
|
||||
static char *
|
||||
put_file(port *p,
|
||||
string text,
|
||||
int length)
|
||||
{
|
||||
if (!p->data.file.output_connector)
|
||||
return(NULL);
|
||||
|
||||
errno = 0;
|
||||
fwrite(text, 1, length, p->data.file.output_connector);
|
||||
fflush(p->data.file.output_connector);
|
||||
|
||||
if (errno)
|
||||
return(strerror(errno));
|
||||
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
static char *
|
||||
close_file_input(port *p)
|
||||
{
|
||||
errno = 0;
|
||||
if (p->data.file.input_connector) {
|
||||
fclose(p->data.file.input_connector);
|
||||
p->data.file.input_connector = 0;
|
||||
}
|
||||
|
||||
if (errno)
|
||||
return(strerror(errno));
|
||||
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
static char *
|
||||
close_file_output(port *p)
|
||||
{
|
||||
errno = 0;
|
||||
if (p->data.file.output_connector) {
|
||||
fclose(p->data.file.output_connector);
|
||||
p->data.file.output_connector = 0;
|
||||
}
|
||||
|
||||
if (errno)
|
||||
return(strerror(errno));
|
||||
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
void create_port_from_files(string name,
|
||||
FILE *input,
|
||||
FILE *output)
|
||||
{
|
||||
port *p = create_named_port(name);
|
||||
|
||||
#if !defined(__HIGHC__)
|
||||
p->get = input ? get_file : NULL;
|
||||
p->put = output ? put_file : NULL;
|
||||
#else
|
||||
/* RT compiler (hc2.1y) bug workaround */
|
||||
if (input)
|
||||
p->get = get_file;
|
||||
else
|
||||
p->get = NULL;
|
||||
if (output)
|
||||
p->put = put_file;
|
||||
else
|
||||
p->put = NULL;
|
||||
#endif
|
||||
p->close_input = close_file_input;
|
||||
p->close_output = close_file_output;
|
||||
p->status = 0;
|
||||
p->data.file.input_connector = input;
|
||||
p->data.file.output_connector = output;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
/* */
|
||||
/* Code for creating various types of FILE * ports: */
|
||||
/* */
|
||||
/****************************************************************************/
|
||||
|
||||
void
|
||||
create_subprocess_port(string name,
|
||||
char **argv)
|
||||
{
|
||||
int pid;
|
||||
int to_child_descriptors[2];
|
||||
int to_parent_descriptors[2];
|
||||
FILE *in = 0;
|
||||
FILE *out = 0;
|
||||
|
||||
/* <<<>>> (file leak) */
|
||||
if (pipe(to_child_descriptors)!=0 || pipe(to_parent_descriptors)!=0)
|
||||
return;
|
||||
|
||||
pid = fork();
|
||||
if (pid == -1) {
|
||||
fprintf(stderr, "zwgc: error while attempting to fork: ");
|
||||
perror("");
|
||||
return; /* <<<>>> */
|
||||
} else if (pid == 0) { /* in child */
|
||||
close(0);
|
||||
close(1);
|
||||
dup2(to_child_descriptors[0], 0);
|
||||
dup2(to_parent_descriptors[1], 1);
|
||||
close(to_child_descriptors[1]);
|
||||
close(to_parent_descriptors[0]);
|
||||
|
||||
execvp(argv[0], argv);
|
||||
fprintf(stderr,"zwgc: unable to exec %s: ", argv[0]);
|
||||
perror("");
|
||||
_exit(errno);
|
||||
}
|
||||
|
||||
fcntl(to_parent_descriptors[0], F_SETFD, 1);
|
||||
fcntl(to_child_descriptors[1], F_SETFD, 1);
|
||||
in = fdopen(to_parent_descriptors[0],"r");
|
||||
out = fdopen(to_child_descriptors[1],"w");
|
||||
close(to_child_descriptors[0]);
|
||||
close(to_parent_descriptors[1]);
|
||||
|
||||
create_port_from_files(name, in, out);
|
||||
}
|
||||
|
||||
void
|
||||
create_file_append_port(string name,
|
||||
string filename)
|
||||
{
|
||||
FILE *out;
|
||||
int oumask;
|
||||
|
||||
errno = 0;
|
||||
|
||||
oumask = umask(077); /* allow read/write for us only */
|
||||
out = fopen(filename, "a");
|
||||
(void) umask(oumask);
|
||||
if (out == NULL) {
|
||||
var_set_variable("error", strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
create_port_from_files(name, 0, out);
|
||||
}
|
||||
|
||||
void
|
||||
create_file_input_port(string name,
|
||||
string filename)
|
||||
{
|
||||
FILE *in;
|
||||
|
||||
errno = 0;
|
||||
in = fopen(filename, "r");
|
||||
if (in == NULL) {
|
||||
var_set_variable("error", strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
create_port_from_files(name, in, 0);
|
||||
}
|
||||
|
||||
void
|
||||
create_file_output_port(string name,
|
||||
string filename)
|
||||
{
|
||||
FILE *out;
|
||||
int oumask;
|
||||
|
||||
errno = 0;
|
||||
|
||||
oumask = umask(077); /* allow read/write for us only */
|
||||
out = fopen(filename, "w");
|
||||
(void) umask(oumask);
|
||||
if (out == NULL) {
|
||||
var_set_variable("error", strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
create_port_from_files(name, 0, out);
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
/* */
|
||||
/* Code to implement a port given a filter function: */
|
||||
/* */
|
||||
/****************************************************************************/
|
||||
|
||||
static string
|
||||
get_filter(port *p,
|
||||
char **error_p)
|
||||
{
|
||||
string result;
|
||||
|
||||
if (string_stack_empty(p->data.filter.waiting_packets)) {
|
||||
*error_p = "Attempt to read from port when no data available";
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
result = string_stack_top(p->data.filter.waiting_packets);
|
||||
string_stack_pop(p->data.filter.waiting_packets);
|
||||
return(result);
|
||||
}
|
||||
|
||||
static char *
|
||||
put_filter(port *p,
|
||||
string text,
|
||||
int length)
|
||||
{
|
||||
string input;
|
||||
string output;
|
||||
|
||||
if (p->status & INPUT_CLOSED)
|
||||
return(NULL);
|
||||
|
||||
input = convert_nulls_to_newlines(text, length);
|
||||
output = (*(p->data.filter.filter))(input);
|
||||
free(input);
|
||||
string_stack_push(p->data.filter.waiting_packets, output);
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
static char *
|
||||
close_filter_input(port *p)
|
||||
{
|
||||
while (!string_stack_empty(p->data.filter.waiting_packets))
|
||||
string_stack_pop(p->data.filter.waiting_packets);
|
||||
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
/*ARGSUSED*/
|
||||
static char *
|
||||
close_filter_output(port *p)
|
||||
{
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
void
|
||||
create_port_from_filter(string name,
|
||||
string (*filter)(string))
|
||||
{
|
||||
port *p = create_named_port(name);
|
||||
|
||||
p->get = get_filter;
|
||||
p->put = put_filter;
|
||||
p->close_input = close_filter_input;
|
||||
p->close_output = close_filter_output;
|
||||
p->status = 0;
|
||||
p->data.filter.waiting_packets = string_stack_create();
|
||||
p->data.filter.filter = filter;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
/* */
|
||||
/* Code to implement a port given an output function: */
|
||||
/* */
|
||||
/****************************************************************************/
|
||||
|
||||
static char *
|
||||
put_output(port *p,
|
||||
string text,
|
||||
int length)
|
||||
{
|
||||
string input;
|
||||
char *error;
|
||||
|
||||
input = convert_nulls_to_newlines(text, length);
|
||||
error = p->data.output.output(input);
|
||||
free(input);
|
||||
return(error);
|
||||
}
|
||||
|
||||
/*ARGSUSED*/
|
||||
static char *
|
||||
close_output(port *p)
|
||||
{
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
void
|
||||
create_port_from_output_proc(string name,
|
||||
char *(*output)(string))
|
||||
{
|
||||
#ifdef SABER /* Yes, it's another ANSI incompatiblity */
|
||||
port *p;
|
||||
#else
|
||||
port *p = create_named_port(name);
|
||||
#endif
|
||||
|
||||
#ifdef SABER
|
||||
p = create_named_port(name);
|
||||
#endif
|
||||
|
||||
p->get = NULL;
|
||||
p->put = put_output;
|
||||
p->close_input = close_output;
|
||||
p->close_output = close_output;
|
||||
p->status = 0;
|
||||
p->data.output.output = output;
|
||||
}
|
Reference in New Issue
Block a user