adding zephyr-im master branch
This commit is contained in:
4
zwgc/.gitattributes
vendored
Normal file
4
zwgc/.gitattributes
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
instantiate ident
|
||||
zwgc_resources ident
|
||||
zwgc.desc ident
|
||||
zwgc.el ident
|
167
zwgc/Makefile.in
Normal file
167
zwgc/Makefile.in
Normal file
@ -0,0 +1,167 @@
|
||||
SHELL=@SHELL@
|
||||
|
||||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
datadir=@datadir@
|
||||
sysconfdir=@sysconfdir@
|
||||
sbindir=@sbindir@
|
||||
lsbindir=@lsbindir@
|
||||
datarootdir=@datarootdir@
|
||||
|
||||
includedir=@includedir@
|
||||
mandir=@mandir@
|
||||
libdir=@libdir@
|
||||
bindir=@bindir@
|
||||
top_builddir=..
|
||||
|
||||
srcdir=@srcdir@
|
||||
top_srcdir=@top_srcdir@
|
||||
BUILDTOP=..
|
||||
VPATH=@srcdir@
|
||||
LIBTOOL=@LIBTOOL@
|
||||
CC=@CC@
|
||||
YACC=@YACC@
|
||||
INSTALL=@INSTALL@
|
||||
INSTANTIATE=${srcdir}/instantiate
|
||||
|
||||
editman = sed \
|
||||
-e 's|@datadir[@]|${datadir}|g' \
|
||||
-e 's|@sysconfdir[@]|${sysconfdir}|g' \
|
||||
-e 's|@sbindir[@]|${sbindir}|g' \
|
||||
-e 's|@lsbindir[@]|${lsbindir}|g'
|
||||
|
||||
LIBZEPHYR=${BUILDTOP}/lib/libzephyr.la
|
||||
CPPFLAGS=@CPPFLAGS@
|
||||
CFLAGS=@CFLAGS@
|
||||
ALL_CFLAGS=${CFLAGS} -DDATADIR=\"${datadir}\" -I${top_srcdir}/h \
|
||||
-I${BUILDTOP}/h -I${srcdir} -I. @X_CFLAGS@ ${CPPFLAGS}
|
||||
YFLAGS=-d
|
||||
LDFLAGS=@X_LIBS@ @LDFLAGS@
|
||||
LIBS=${LIBZEPHYR} @LIBS@ -lcom_err @ZWGC_LIBX11@ @X_EXTRA_LIBS@ \
|
||||
@TLIB@ @REGEX_LIBS@ @ARES_LIBS@
|
||||
|
||||
OBJS= port_dictionary.o pointer_dictionary.o unsigned_long_dictionary.o \
|
||||
string_dictionary.o int_dictionary.o string_dictionary_aux.o \
|
||||
parser.o lexer.o node.o exec.o buffer.o main.o zephyr.o X_driver.o \
|
||||
substitute.o port.o xshow.o mux.o eval.o subscriptions.o notice.o \
|
||||
xcut.o regexp.o character_class.o text_operations.o file.o error.o \
|
||||
variables.o formatter.o X_fonts.o X_gram.o tty_filter.o \
|
||||
standard_ports.o xselect.o xmark.o xrevstack.o xerror.o \
|
||||
new_string.o new_memory.o plus.o
|
||||
|
||||
all: zwgc zwgc.1
|
||||
|
||||
zwgc: ${OBJS} ${LIBZEPHYR}
|
||||
${LIBTOOL} --mode=link ${CC} ${LDFLAGS} -o $@ ${OBJS} ${LIBS}
|
||||
|
||||
port_dictionary.c port_dictionary.h: dictionary.c dictionary.h
|
||||
${INSTANTIATE} ${srcdir} dictionary port port.h
|
||||
|
||||
pointer_dictionary.c pointer_dictionary.h: dictionary.c dictionary.h
|
||||
${INSTANTIATE} ${srcdir} dictionary pointer pointer.h
|
||||
|
||||
unsigned_long_dictionary.c unsigned_long_dictionary.h: dictionary.c \
|
||||
dictionary.h
|
||||
${INSTANTIATE} ${srcdir} dictionary unsigned_long unsigned_long.h
|
||||
|
||||
string_dictionary.c string_dictionary.h: dictionary.c dictionary.h
|
||||
${INSTANTIATE} ${srcdir} dictionary string new_string.h
|
||||
|
||||
int_dictionary.c int_dictionary.h: dictionary.c dictionary.h
|
||||
${INSTANTIATE} ${srcdir} dictionary int
|
||||
|
||||
char_stack.h: stack.h
|
||||
${INSTANTIATE} ${srcdir} stack char
|
||||
|
||||
string_stack.h: stack.h
|
||||
${INSTANTIATE} ${srcdir} stack string
|
||||
|
||||
xmode_stack.h: stack.h
|
||||
${INSTANTIATE} ${srcdir} stack xmode
|
||||
|
||||
lexer.o: y.tab.h
|
||||
|
||||
parser.o: y.tab.c y.tab.h
|
||||
${CC} -c ${ALL_CFLAGS} -o $@ y.tab.c
|
||||
|
||||
y.tab.c y.tab.h: ${srcdir}/parser.y
|
||||
${YACC} ${YFLAGS} ${srcdir}/parser.y
|
||||
|
||||
.c.o:
|
||||
${CC} -c ${ALL_CFLAGS} $<
|
||||
|
||||
zwgc.1: ${srcdir}/zwgc.1.in Makefile
|
||||
${editman} ${srcdir}/$@.in > $@.tmp
|
||||
mv $@.tmp $@
|
||||
|
||||
check:
|
||||
|
||||
install: zwgc zwgc.1
|
||||
${LIBTOOL} --mode=install ${INSTALL} -m 755 zwgc ${DESTDIR}${bindir}
|
||||
${INSTALL} -m 644 zwgc.1 ${DESTDIR}${mandir}/man1
|
||||
${INSTALL} -m 644 ${srcdir}/zwgc.desc ${DESTDIR}${datadir}/zephyr
|
||||
${INSTALL} -m 644 ${srcdir}/zwgc_resources ${DESTDIR}${datadir}/zephyr
|
||||
|
||||
clean:
|
||||
${LIBTOOL} --mode=clean rm -f zwgc
|
||||
rm -f ${OBJS} port_dictionary.[ch] pointer_dictionary.[ch]
|
||||
rm -f unsigned_long_dictionary.[ch] string_dictionary.[ch]
|
||||
rm -f int_dictionary.[ch] char_stack.h string_stack.h xmode_stack.h
|
||||
rm -f y.tab.[ch]
|
||||
rm -f zwgc.1
|
||||
|
||||
${OBJS}: ${top_srcdir}/h/sysdep.h ${BUILDTOP}/h/config.h
|
||||
zephyr.o: ${BUILDTOP}/h/zephyr/zephyr.h ${BUILDTOP}/h/zephyr/zephyr_err.h
|
||||
|
||||
port_dictionary.o: port.h string_stack.h new_string.h new_memory.h
|
||||
pointer_dictionary.o: pointer.h new_string.h new_memory.h
|
||||
unsigned_long_dictionary.o: new_string.h new_memory.h
|
||||
string_dictionary.o: new_string.h new_memory.h
|
||||
int_dictionary.o: new_string.h new_memory.h
|
||||
X_driver.o: X_driver.h new_memory.h formatter.h mux.h variables.h error.h
|
||||
X_driver.o: X_gram.h xselect.h unsigned_long_dictionary.h
|
||||
X_fonts.o: X_fonts.h new_memory.h new_string.h error.h pointer_dictionary.h
|
||||
X_fonts.o: zwgc.h
|
||||
X_gram.o: X_gram.h xmark.h zwgc.h X_driver.h X_fonts.h error.h new_string.h
|
||||
X_gram.o: xrevstack.h xerror.h xselect.h
|
||||
browser.o: zwgc.h
|
||||
buffer.o: new_memory.h buffer.h
|
||||
character_class.o: character_class.h
|
||||
eval.o: new_memory.h node.h eval.h substitute.h port.h buffer.h regexp.h
|
||||
eval.o: text_operations.h zwgc.h variables.h
|
||||
exec.o: new_memory.h exec.h eval.h node.h buffer.h port.h variables.h notice.h
|
||||
file.o: new_memory.h new_string.h error.h
|
||||
formatter.o: new_memory.h char_stack.h string_dictionary.h formatter.h
|
||||
formatter.o: text_operations.h
|
||||
lexer.o: new_memory.h new_string.h int_dictionary.h lexer.h parser.h
|
||||
main.o: new_memory.h zwgc.h parser.h node.h exec.h zephyr.h notice.h
|
||||
main.o: subscriptions.h file.h mux.h port.h variables.h main.h
|
||||
mux.o: mux.h error.h zwgc.h pointer.h
|
||||
new_memory.o: new_memory.h int_dictionary.h
|
||||
new_string.o: new_memory.h
|
||||
node.o: new_memory.h node.h
|
||||
notice.o: new_memory.h error.h variables.h notice.h
|
||||
port.o: new_string.h port_dictionary.h port.h notice.h variables.h
|
||||
regexp.o: regexp.h
|
||||
standard_ports.o: new_memory.h port.h variables.h error.h main.h
|
||||
string_dictionary_aux.o: new_memory.h string_dictionary.h
|
||||
subscriptions.o: new_memory.h new_string.h int_dictionary.h zwgc.h
|
||||
subscriptions.o: subscriptions.h error.h file.h main.h
|
||||
substitute.o: new_memory.h lexer.h substitute.h
|
||||
text_operations.o: new_memory.h text_operations.h char_stack.h
|
||||
tty_filter.o: new_memory.h new_string.h string_dictionary_aux.h formatter.h
|
||||
tty_filter.o: zwgc.h error.h
|
||||
variables.o: new_memory.h notice.h string_dictionary_aux.h variables.h
|
||||
xcut.o: new_memory.h new_string.h X_gram.h zwgc.h xselect.h xmark.h error.h
|
||||
xcut.o: xrevstack.h
|
||||
xerror.o: mux.h
|
||||
xmark.o: X_gram.h X_fonts.h xmark.h new_string.h
|
||||
xrevstack.o: X_gram.h zwgc.h
|
||||
xselect.o: new_string.h xselect.h
|
||||
xshow.o: pointer_dictionary.h new_memory.h formatter.h variables.h zwgc.h
|
||||
xshow.o: X_fonts.h X_gram.h xmode_stack.h
|
||||
zephyr.o: new_string.h zephyr.h error.h mux.h subscriptions.h variables.h
|
||||
zephyr.o: pointer.h X_driver.h
|
||||
|
||||
.PHONY: all check install clean
|
||||
|
412
zwgc/X_driver.c
Normal file
412
zwgc/X_driver.c
Normal file
@ -0,0 +1,412 @@
|
||||
/* 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_X_driver_c[] = "$Id$";
|
||||
#endif
|
||||
|
||||
#include <zephyr/mit-copyright.h>
|
||||
|
||||
/****************************************************************************/
|
||||
/* */
|
||||
/* The X driver: */
|
||||
/* */
|
||||
/****************************************************************************/
|
||||
|
||||
#ifndef X_DISPLAY_MISSING
|
||||
|
||||
#include <zephyr/zephyr.h>
|
||||
#include "new_string.h"
|
||||
#include "X_driver.h"
|
||||
#include <X11/Xresource.h>
|
||||
#include "new_memory.h"
|
||||
#include "formatter.h"
|
||||
#include "mux.h"
|
||||
#include "variables.h"
|
||||
#include "error.h"
|
||||
#include "X_gram.h"
|
||||
#include "xselect.h"
|
||||
#include "unsigned_long_dictionary.h"
|
||||
#include "zephyr.h"
|
||||
|
||||
char *app_instance;
|
||||
|
||||
/*
|
||||
* x_dpy - the display we are outputting to
|
||||
*/
|
||||
|
||||
Display *x_dpy = NULL;
|
||||
|
||||
/****************************************************************************/
|
||||
/* */
|
||||
/* Code to deal with getting X resources: */
|
||||
/* */
|
||||
/****************************************************************************/
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef APPNAME
|
||||
#define APPNAME "zwgc"
|
||||
#endif
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef APPCLASS
|
||||
#define APPCLASS "Zwgc"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* x_resources - our X resources from application resources, command line,
|
||||
* and user's X resources.
|
||||
*/
|
||||
|
||||
static XrmDatabase x_resources = NULL;
|
||||
|
||||
/*
|
||||
* Internal Routine:
|
||||
*
|
||||
* int convert_string_to_bool(string text)
|
||||
* Effects: If text represents yes/true/on, return 1. If text
|
||||
* representes no/false/off, return 0. Otherwise,
|
||||
* returns -1.
|
||||
*/
|
||||
|
||||
static int
|
||||
convert_string_to_bool(string text)
|
||||
{
|
||||
if (!strcasecmp("yes", text) || !strcasecmp("y", text) ||
|
||||
!strcasecmp("true", text) || !strcasecmp("t", text) ||
|
||||
!strcasecmp("on", text))
|
||||
return(1);
|
||||
else if (!strcasecmp("no", text) || !strcasecmp("n", text) ||
|
||||
!strcasecmp("false", text) || !strcasecmp("f", text) ||
|
||||
!strcasecmp("off", text))
|
||||
return(0);
|
||||
else
|
||||
return(-1);
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
char *
|
||||
get_string_resource(string name,
|
||||
string class)
|
||||
{
|
||||
string full_name, full_class;
|
||||
int status;
|
||||
char *type;
|
||||
XrmValue value;
|
||||
|
||||
full_name = string_Concat(APPNAME, ".");
|
||||
full_name = string_Concat2(full_name, name);
|
||||
full_class = string_Concat(APPCLASS, ".");
|
||||
full_class = string_Concat2(full_class, class);
|
||||
|
||||
status = XrmGetResource(x_resources, full_name, full_class, &type, &value);
|
||||
free(full_name);
|
||||
free(full_class);
|
||||
|
||||
if (status != True)
|
||||
return(NULL);
|
||||
|
||||
if (string_Neq(type, "String"))
|
||||
return(NULL);
|
||||
|
||||
return(value.addr);
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
int
|
||||
get_bool_resource(string name,
|
||||
string class,
|
||||
int default_value)
|
||||
{
|
||||
int result;
|
||||
char *temp;
|
||||
|
||||
if (!(temp = get_string_resource(name, class)))
|
||||
return(default_value);
|
||||
|
||||
result = convert_string_to_bool(temp);
|
||||
if (result == -1)
|
||||
result = default_value;
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
static unsigned_long_dictionary color_dict = NULL;
|
||||
|
||||
/* Requires: name points to color name or hex string. name must be free'd
|
||||
* eventually by the caller.
|
||||
* Effects: returns unsigned long pixel value, or default if the
|
||||
* color is not known by the server. If name is NULL, returns
|
||||
* default;
|
||||
*
|
||||
* comment: caches return values from X server round trips. If name does
|
||||
* not resolve, this fact is NOT cached, and will result in a round
|
||||
* trip each time.
|
||||
*/
|
||||
|
||||
unsigned long
|
||||
x_string_to_color(char *name,
|
||||
unsigned long def)
|
||||
{
|
||||
unsigned_long_dictionary_binding *binding;
|
||||
int exists;
|
||||
XColor xc;
|
||||
|
||||
if (name == NULL)
|
||||
return(def);
|
||||
|
||||
binding = unsigned_long_dictionary_Define(color_dict,name,&exists);
|
||||
|
||||
if (exists) {
|
||||
return((unsigned long) binding->value);
|
||||
} else {
|
||||
if (XParseColor(x_dpy,
|
||||
DefaultColormapOfScreen(DefaultScreenOfDisplay(x_dpy)),
|
||||
name,&xc)) {
|
||||
if (XAllocColor(x_dpy,
|
||||
DefaultColormapOfScreen(DefaultScreenOfDisplay(x_dpy)),
|
||||
&xc)) {
|
||||
binding->value = (unsigned long) xc.pixel;
|
||||
return(xc.pixel);
|
||||
} else {
|
||||
ERROR2("Error in XAllocColor on \"%s\": using default color\n",
|
||||
name);
|
||||
}
|
||||
} else {
|
||||
ERROR2("Error in XParseColor on \"%s\": using default color\n",
|
||||
name);
|
||||
}
|
||||
unsigned_long_dictionary_Delete(color_dict,binding);
|
||||
return(def);
|
||||
}
|
||||
/*NOTREACHED*/
|
||||
}
|
||||
|
||||
/*
|
||||
* Standard X Toolkit command line options:
|
||||
*/
|
||||
|
||||
static XrmOptionDescRec cmd_options[] = {
|
||||
{"+rv", "*reverseVideo", XrmoptionNoArg, (XPointer) "off"},
|
||||
{"+synchronous", "*synchronous", XrmoptionNoArg, (XPointer) "off"},
|
||||
{"-background", "*background", XrmoptionSepArg, (XPointer) NULL},
|
||||
{"-bd", "*borderColor", XrmoptionSepArg, (XPointer) NULL},
|
||||
{"-bg", "*background", XrmoptionSepArg, (XPointer) NULL},
|
||||
{"-bordercolor", "*borderColor", XrmoptionSepArg, (XPointer) NULL},
|
||||
{"-borderwidth", ".borderWidth", XrmoptionSepArg, (XPointer) NULL},
|
||||
{"-bw", ".borderWidth", XrmoptionSepArg, (XPointer) NULL},
|
||||
{"-display", ".display", XrmoptionSepArg, (XPointer) NULL},
|
||||
{"-fg", "*foreground", XrmoptionSepArg, (XPointer) NULL},
|
||||
{"-fn", "*font", XrmoptionSepArg, (XPointer) NULL},
|
||||
{"-font", "*font", XrmoptionSepArg, (XPointer) NULL},
|
||||
{"-foreground", "*foreground", XrmoptionSepArg, (XPointer) NULL},
|
||||
{"-geometry", ".geometry", XrmoptionSepArg, (XPointer) NULL},
|
||||
{"-iconname", ".iconName", XrmoptionSepArg, (XPointer) NULL},
|
||||
#ifdef CMU_ZWGCPLUS
|
||||
{"-lifespan", "*lifespan", XrmoptionSepArg, (XPointer) NULL},
|
||||
#endif
|
||||
{"-name", ".name", XrmoptionSepArg, (XPointer) NULL},
|
||||
{"-reverse", "*reverseVideo", XrmoptionNoArg, (XPointer) "on"},
|
||||
{"-rv", "*reverseVideo", XrmoptionNoArg, (XPointer) "on"},
|
||||
{"-transient", "*transient", XrmoptionNoArg, (XPointer) "on"},
|
||||
{"-synchronous", "*synchronous", XrmoptionNoArg, (XPointer) "on"},
|
||||
{"-title", ".title", XrmoptionSepArg, (XPointer) NULL},
|
||||
{"-xrm", NULL, XrmoptionResArg, (XPointer) NULL} };
|
||||
|
||||
#define NUMBER_OF_OPTIONS ((sizeof (cmd_options))/ sizeof(cmd_options[0]))
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
static int
|
||||
open_display_and_load_resources(int *pargc,
|
||||
char **argv)
|
||||
{
|
||||
XrmDatabase temp_db1, temp_db2, temp_db3;
|
||||
char *filename, *res, *xdef;
|
||||
char dbasename[128];
|
||||
|
||||
/* Initialize X resource manager: */
|
||||
XrmInitialize();
|
||||
|
||||
/*
|
||||
* Parse X toolkit command line arguments (including -display)
|
||||
* into resources:
|
||||
*/
|
||||
XrmParseCommand(&x_resources, cmd_options, NUMBER_OF_OPTIONS, APPNAME,
|
||||
pargc, argv);
|
||||
|
||||
/*
|
||||
* Try and open the display using the display specified if given.
|
||||
* If can't open the display, return an error code.
|
||||
*/
|
||||
x_dpy = XOpenDisplay(get_string_resource("display", "display"));
|
||||
if (!x_dpy)
|
||||
return(1);
|
||||
|
||||
/* Read in our application-specific resources: */
|
||||
sprintf(dbasename, "%s/zephyr/zwgc_resources", DATADIR);
|
||||
temp_db1 = XrmGetFileDatabase(dbasename);
|
||||
|
||||
/*
|
||||
* Get resources from the just opened display:
|
||||
*/
|
||||
xdef = XResourceManagerString(x_dpy);
|
||||
if (xdef)
|
||||
temp_db2 = XrmGetStringDatabase(xdef);
|
||||
else
|
||||
temp_db2 = NULL;
|
||||
|
||||
/*
|
||||
* Merge the 4 sets of resources together such that when searching
|
||||
* for resources, they are checking in the following order:
|
||||
* command arguments, XENVIRONMENT resources, server resources,
|
||||
* application resources
|
||||
*/
|
||||
XrmMergeDatabases(temp_db2, &temp_db1);
|
||||
|
||||
#if XlibSpecificationRelease > 4
|
||||
/* X11 R5 per-screen resources */
|
||||
res = XScreenResourceString (DefaultScreenOfDisplay (x_dpy));
|
||||
if (res != NULL)
|
||||
XrmMergeDatabases(XrmGetStringDatabase(res), &temp_db1);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Get XENVIRONMENT resources, if they exist, and merge
|
||||
*/
|
||||
filename = getenv("XENVIRONMENT");
|
||||
if (filename)
|
||||
{
|
||||
temp_db3 = XrmGetFileDatabase(filename);
|
||||
XrmMergeDatabases(temp_db3, &temp_db1);
|
||||
}
|
||||
XrmMergeDatabases(x_resources, &temp_db1);
|
||||
x_resources = temp_db1;
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* X_driver_ioerror: called by Xlib in case of an X IO error.
|
||||
* Shouldn't return (according to man page).
|
||||
*
|
||||
* on IO error, we clean up and exit.
|
||||
*
|
||||
* XXX it would be better to set mux_end_loop_p, but we can't return to
|
||||
* get there (Xlib will exit if this routine returns).
|
||||
*
|
||||
*/
|
||||
|
||||
static int
|
||||
X_driver_ioerror(Display *display)
|
||||
{
|
||||
ERROR2("X IO error on display '%s'--exiting\n", DisplayString(display));
|
||||
finalize_zephyr();
|
||||
exit(1);
|
||||
return 1;
|
||||
}
|
||||
/****************************************************************************/
|
||||
/* */
|
||||
/* Code to deal with initializing the driver: */
|
||||
/* */
|
||||
/****************************************************************************/
|
||||
|
||||
/*ARGSUSED*/
|
||||
int
|
||||
X_driver_init(char *drivername,
|
||||
char notfirst,
|
||||
int *pargc,
|
||||
char **argv)
|
||||
{
|
||||
string temp;
|
||||
int is_sync;
|
||||
|
||||
/*
|
||||
* Attempt to open display and read resources, including from the
|
||||
* command line. If fail, exit with error code, disabling this
|
||||
* driver:
|
||||
*/
|
||||
if (open_display_and_load_resources(pargc, argv)) {
|
||||
ERROR("Unable to open X display -- disabling X driver.\n");
|
||||
return(1);
|
||||
}
|
||||
|
||||
XSetIOErrorHandler(X_driver_ioerror);
|
||||
|
||||
/*
|
||||
* For now, set some useful variables using resources:
|
||||
*/
|
||||
is_sync = get_bool_resource("synchronous", "Synchronous", 0);
|
||||
if (is_sync)
|
||||
XSynchronize(x_dpy, is_sync);
|
||||
temp = get_string_resource("geometry", "Geometry");
|
||||
if (temp)
|
||||
var_set_variable("default_X_geometry", temp);
|
||||
|
||||
temp=strrchr(argv[0],'/');
|
||||
|
||||
app_instance=string_Copy(temp?temp+1:argv[0]);
|
||||
|
||||
color_dict = unsigned_long_dictionary_Create(37);
|
||||
|
||||
xshowinit();
|
||||
x_gram_init(x_dpy);
|
||||
xicccmInitAtoms(x_dpy);
|
||||
|
||||
mux_add_input_source(ConnectionNumber(x_dpy),
|
||||
(void(*)(void *))x_get_input, x_dpy);
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
/* */
|
||||
/* The display routine itself: */
|
||||
/* */
|
||||
/****************************************************************************/
|
||||
|
||||
char *
|
||||
X_driver(string text)
|
||||
{
|
||||
string text_copy;
|
||||
desctype *desc;
|
||||
int numstr, numnl;
|
||||
|
||||
text_copy = string_Copy(text);
|
||||
desc = disp_get_cmds(text_copy, &numstr, &numnl);
|
||||
|
||||
xshow(x_dpy, desc, numstr, numnl);
|
||||
|
||||
free(text_copy);
|
||||
free_desc(desc);
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
#endif /* X_DISPLAY_MISSING */
|
||||
|
32
zwgc/X_driver.h
Normal file
32
zwgc/X_driver.h
Normal file
@ -0,0 +1,32 @@
|
||||
#ifndef x_driver_MODULE
|
||||
#define x_driver_MODULE
|
||||
|
||||
/* 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 <zephyr/mit-copyright.h>
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
|
||||
#include "new_string.h"
|
||||
|
||||
extern Display *x_dpy;
|
||||
|
||||
extern char *X_driver(string);
|
||||
extern int X_driver_init(char *, char, int *, char **);
|
||||
extern char *get_string_resource(string, string);
|
||||
extern int get_bool_resource(string, string, int);
|
||||
extern unsigned long x_string_to_color(char *, unsigned long);
|
||||
|
||||
#endif
|
255
zwgc/X_fonts.c
Normal file
255
zwgc/X_fonts.c
Normal file
@ -0,0 +1,255 @@
|
||||
/* 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_X_fonts_c[] = "$Id$";
|
||||
#endif
|
||||
|
||||
#include <zephyr/mit-copyright.h>
|
||||
|
||||
/****************************************************************************/
|
||||
/* */
|
||||
/* Code dealing with X fonts: */
|
||||
/* */
|
||||
/****************************************************************************/
|
||||
|
||||
#ifndef X_DISPLAY_MISSING
|
||||
|
||||
#include "X_fonts.h"
|
||||
#include "new_memory.h"
|
||||
#include "new_string.h"
|
||||
#include "error.h"
|
||||
#include "pointer_dictionary.h"
|
||||
#include "zwgc.h"
|
||||
|
||||
/*
|
||||
* font_dict - Lookup cache for fonts (the value pointers are XFontSet's)
|
||||
*/
|
||||
|
||||
static pointer_dictionary family_dict = NULL;
|
||||
static pointer_dictionary fontname_dict = NULL;
|
||||
static pointer_dictionary fontst_dict = NULL;
|
||||
|
||||
/*
|
||||
* {face,size}_to_string - lookup tables for converting {face,size} int
|
||||
* constants to ascii strings:
|
||||
*/
|
||||
|
||||
static string face_to_string[] = { "roman", "bold", "italic", "bolditalic" };
|
||||
static string size_to_string[] = { "small", "medium", "large" };
|
||||
|
||||
static char *
|
||||
get_family(char *style, char *substyle)
|
||||
{
|
||||
char *desc;
|
||||
pointer_dictionary_binding *binding;
|
||||
int exists;
|
||||
char *family;
|
||||
|
||||
desc=string_Concat("style.", style);
|
||||
desc=string_Concat2(desc, ".substyle.");
|
||||
desc=string_Concat2(desc, substyle);
|
||||
desc=string_Concat2(desc, ".fontfamily");
|
||||
|
||||
if (!family_dict)
|
||||
family_dict = pointer_dictionary_Create(37);
|
||||
binding = pointer_dictionary_Define(family_dict, desc, &exists);
|
||||
|
||||
if (exists) {
|
||||
free(desc);
|
||||
return((string) binding->value);
|
||||
} else {
|
||||
family = get_string_resource(desc,
|
||||
"StyleKey.Style1.Style2.Style3.SubstyleKey.Substyle.FontfamilyKey");
|
||||
free(desc);
|
||||
if (family == NULL)
|
||||
pointer_dictionary_Delete(family_dict, binding);
|
||||
else
|
||||
binding->value = (pointer)family;
|
||||
return(family); /* If resource returns NULL, return NULL also */
|
||||
}
|
||||
}
|
||||
|
||||
static char *
|
||||
get_specific_fontname(char *family,
|
||||
int size,
|
||||
int face)
|
||||
{
|
||||
char *desc;
|
||||
pointer_dictionary_binding *binding;
|
||||
int exists;
|
||||
char *fontname;
|
||||
|
||||
desc = string_Concat("fontfamily.", family);
|
||||
desc = string_Concat2(desc, ".");
|
||||
desc = string_Concat2(desc, size_to_string[size]);
|
||||
desc = string_Concat2(desc, ".");
|
||||
desc = string_Concat2(desc, face_to_string[face]);
|
||||
|
||||
if (!fontname_dict)
|
||||
fontname_dict = pointer_dictionary_Create(37);
|
||||
binding = pointer_dictionary_Define(fontname_dict, desc, &exists);
|
||||
|
||||
if (exists) {
|
||||
free(desc);
|
||||
return (string)binding->value;
|
||||
} else {
|
||||
fontname = get_string_resource(desc, "FontfamilyKey.Fontfamily.Size.Face");
|
||||
free(desc);
|
||||
if (fontname == NULL)
|
||||
pointer_dictionary_Delete(fontname_dict, binding);
|
||||
else
|
||||
binding->value = (pointer)fontname;
|
||||
return fontname; /* If resource returns NULL, return NULL also */
|
||||
}
|
||||
}
|
||||
|
||||
static XFontSet
|
||||
get_fontst(Display *dpy, char *fontname)
|
||||
{
|
||||
pointer_dictionary_binding *binding;
|
||||
int exists;
|
||||
XFontSet fontst;
|
||||
char **missing_list;
|
||||
int missing_count;
|
||||
char *def_string;
|
||||
|
||||
if (!fontst_dict)
|
||||
fontst_dict = pointer_dictionary_Create(37);
|
||||
binding = pointer_dictionary_Define(fontst_dict, fontname, &exists);
|
||||
|
||||
if (exists)
|
||||
return((XFontSet)binding->value);
|
||||
|
||||
fontst = XCreateFontSet(dpy, fontname, &missing_list, &missing_count,
|
||||
&def_string);
|
||||
XFreeStringList(missing_list);
|
||||
|
||||
if (fontst == NULL)
|
||||
pointer_dictionary_Delete(fontst_dict,binding);
|
||||
else
|
||||
binding->value = (pointer)fontst;
|
||||
|
||||
return(fontst); /* If resource returns NULL, return NULL also */
|
||||
}
|
||||
|
||||
static char *
|
||||
get_fontname(char *family, int size, int face)
|
||||
{
|
||||
char *fontname;
|
||||
|
||||
fontname = get_specific_fontname(family, size, face);
|
||||
if (!fontname)
|
||||
fontname = get_specific_fontname(family, size, ROMAN_FACE);
|
||||
if (!fontname)
|
||||
fontname = get_specific_fontname(family, MEDIUM_SIZE, face);
|
||||
if (!fontname)
|
||||
fontname = get_specific_fontname(family, MEDIUM_SIZE, ROMAN_FACE);
|
||||
return(fontname);
|
||||
}
|
||||
|
||||
static XFontSet
|
||||
complete_get_fontst(Display *dpy,
|
||||
string style,
|
||||
string substyle,
|
||||
int size,
|
||||
int face)
|
||||
{
|
||||
char *family, *fontname;
|
||||
XFontSet fontst;
|
||||
|
||||
family = get_family(style, substyle);
|
||||
if (!family)
|
||||
return NULL;
|
||||
fontname = get_fontname(family, size, face);
|
||||
if (!fontname)
|
||||
return NULL;
|
||||
fontst = get_fontst(dpy, fontname);
|
||||
if (!fontst)
|
||||
return NULL;
|
||||
|
||||
return fontst;
|
||||
}
|
||||
|
||||
/*
|
||||
* XFontSet get_font(string style, substyle; int size, face)
|
||||
* Requires: size is one of SMALL_SIZE, MEDIUM_SIZE, LARGE_SIZE and
|
||||
* face is one of ROMAN_FACE, BOLD_FACE, ITALIC_FACE,
|
||||
* BOLDITALIC_FACE.
|
||||
* Effects: unknown
|
||||
*/
|
||||
|
||||
XFontSet
|
||||
get_font(Display *dpy,
|
||||
string style,
|
||||
string substyle,
|
||||
int size,
|
||||
int face)
|
||||
{
|
||||
char *family,*fontname;
|
||||
XFontSet fontst = NULL;
|
||||
|
||||
if (size == SPECIAL_SIZE) {
|
||||
/* attempt to process @font explicitly */
|
||||
fontst = get_fontst(dpy, substyle);
|
||||
} else {
|
||||
family = get_family(style, substyle);
|
||||
|
||||
if (family)
|
||||
fontname = get_fontname(family, size, face);
|
||||
else
|
||||
fontname = get_fontname(substyle, size, face);
|
||||
|
||||
if (fontname) {
|
||||
fontst = get_fontst(dpy, fontname);
|
||||
if (fontst)
|
||||
return fontst;
|
||||
}
|
||||
|
||||
/* At this point, the no-failure case didn't happen, and the case
|
||||
of substyle being the fontfamily didn't happen, either. */
|
||||
|
||||
fontst = complete_get_fontst(dpy, style, "text", size, face);
|
||||
if (!fontst)
|
||||
fontst = complete_get_fontst(dpy, "default", substyle, size, face);
|
||||
if (!fontst)
|
||||
fontst = complete_get_fontst(dpy, "default", "text", size, face);
|
||||
if (!fontst) {
|
||||
fontname = get_fontname("default", size, face);
|
||||
if (fontname)
|
||||
fontst = get_fontst(dpy, fontname);
|
||||
}
|
||||
}
|
||||
if (fontst)
|
||||
return fontst;
|
||||
|
||||
/* If all else fails, try fixed */
|
||||
|
||||
fontst = get_fontst(dpy, "fixed");
|
||||
|
||||
if (fontst)
|
||||
return fontst;
|
||||
|
||||
/* No fonts available. Die. */
|
||||
|
||||
ERROR("Unable to open font \"fixed\". Aborting...");
|
||||
#ifdef DEBUG
|
||||
abort();
|
||||
#else
|
||||
exit(1);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* X_DISPLAY_MISSING */
|
46
zwgc/X_fonts.h
Normal file
46
zwgc/X_fonts.h
Normal file
@ -0,0 +1,46 @@
|
||||
#ifndef x_fonts_MODULE
|
||||
#define x_fonts_MODULE
|
||||
|
||||
/* 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 <zephyr/mit-copyright.h>
|
||||
|
||||
#include "X_driver.h"
|
||||
|
||||
#define SPECIAL_FACE -1
|
||||
#define ROMAN_FACE 0
|
||||
#define BOLD_FACE 1
|
||||
#define ITALIC_FACE 2
|
||||
#define BOLD_ITALIC_FACE 3
|
||||
|
||||
#define SPECIAL_SIZE -1
|
||||
#define SMALL_SIZE 0
|
||||
#define MEDIUM_SIZE 1
|
||||
#define LARGE_SIZE 2
|
||||
|
||||
/*
|
||||
* XFontSet get_font(string family; int size, face)
|
||||
* Requires: size is one of SMALL_SIZE, MEDIUM_SIZE, LARGE_SIZE and
|
||||
* face is one of ROMAN_FACE, BOLD_FACE, ITALIC_FACE,
|
||||
* BOLDITALIC_FACE.
|
||||
* Effects: Looks up the font specified by the above in the
|
||||
* X resources. If that font is not specified by in
|
||||
* the X resources or it can't be loaded, the font
|
||||
* specified by default.medium.roman is used. <<<>>>
|
||||
*/
|
||||
|
||||
extern XFontSet get_font(Display *, string, string, int, int);
|
||||
|
||||
#endif
|
562
zwgc/X_gram.c
Normal file
562
zwgc/X_gram.c
Normal file
@ -0,0 +1,562 @@
|
||||
/* 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_X_gram_c[] = "$Id$";
|
||||
#endif
|
||||
|
||||
#include <zephyr/mit-copyright.h>
|
||||
|
||||
#ifndef X_DISPLAY_MISSING
|
||||
|
||||
#include <zephyr/zephyr.h>
|
||||
#include "X_gram.h"
|
||||
#include "xmark.h"
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/cursorfont.h>
|
||||
#include <X11/Xatom.h>
|
||||
#include "zwgc.h"
|
||||
#include "X_driver.h"
|
||||
#include "X_fonts.h"
|
||||
#include "error.h"
|
||||
#include "new_string.h"
|
||||
#include "xrevstack.h"
|
||||
#include "xerror.h"
|
||||
#include "xselect.h"
|
||||
#ifdef CMU_ZWGCPLUS
|
||||
#include "plus.h"
|
||||
#endif
|
||||
|
||||
extern XContext desc_context;
|
||||
extern char *app_instance;
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
int internal_border_width = 2;
|
||||
|
||||
unsigned long default_fgcolor;
|
||||
unsigned long default_bgcolor;
|
||||
unsigned long default_bordercolor;
|
||||
long ttl = 0;
|
||||
static int reset_saver;
|
||||
static int border_width = 1;
|
||||
static int cursor_code = XC_sailboat;
|
||||
static int set_transient;
|
||||
static int enable_delete;
|
||||
static char *title_name,*icon_name;
|
||||
static Cursor cursor;
|
||||
static Window group_leader; /* In order to have transient windows,
|
||||
* I need a top-level window to always exist
|
||||
*/
|
||||
static XClassHint classhint;
|
||||
static XSetWindowAttributes xattributes;
|
||||
static unsigned long xattributes_mask;
|
||||
static int set_all_desktops = True;
|
||||
static Atom net_wm_desktop = None;
|
||||
static Atom net_wm_window_type = None;
|
||||
static Atom net_wm_window_type_utility = None;
|
||||
|
||||
/* ICCCM note:
|
||||
*
|
||||
* the following properties must be set on all top-level windows:
|
||||
*
|
||||
* WM_NAME XStoreName(dpy,w,name);
|
||||
* WM_ICON_NAME XSetIconName(dpy,w,name);
|
||||
* WM_NORMAL_HINTS XSetNormalHints(dpy,w,sizehints);
|
||||
* WM_HINTS XSetWMHints(dpy,w,wmhints);
|
||||
* WM_CLASS XSetClassHint(dpy,w,classhint);
|
||||
*
|
||||
* and for individual zgrams:
|
||||
*
|
||||
* WM_TRANSIENT_FOR XSetTransientForHint(dpy,w,main_window);
|
||||
* WM_PROTOCOLS XSetWMProtocols(dpy,w,protocols,cnt);
|
||||
*/
|
||||
|
||||
/* set all properties defined in ICCCM. If main_window == 0,
|
||||
* per-zgram initialization is not done.
|
||||
*/
|
||||
|
||||
/*ARGSUSED*/
|
||||
static void
|
||||
x_set_icccm_hints(Display *dpy,
|
||||
Window w,
|
||||
char *name,
|
||||
char *wm_icon_name,
|
||||
XSizeHints *psizehints,
|
||||
XWMHints *pwmhints,
|
||||
Window main_window)
|
||||
{
|
||||
XStoreName(dpy,w,name);
|
||||
XSetIconName(dpy,w,wm_icon_name);
|
||||
XSetWMNormalHints(dpy,w,psizehints);
|
||||
XSetWMHints(dpy,w,pwmhints);
|
||||
XSetClassHint(dpy,w,&classhint);
|
||||
/* in order for some wm's to iconify, the window shouldn't be transient.
|
||||
e.g. Motif wm */
|
||||
if (main_window != None) {
|
||||
if (set_transient)
|
||||
XSetTransientForHint(dpy,w,main_window);
|
||||
}
|
||||
if (enable_delete)
|
||||
XSetWMProtocols(dpy,w,&XA_WM_DELETE_WINDOW,1);
|
||||
}
|
||||
|
||||
void
|
||||
x_gram_init(Display *dpy)
|
||||
{
|
||||
char *temp;
|
||||
XSizeHints sizehints;
|
||||
XWMHints wmhints;
|
||||
unsigned long rv,tc;
|
||||
|
||||
default_fgcolor = BlackPixelOfScreen(DefaultScreenOfDisplay(dpy));
|
||||
default_bgcolor = WhitePixelOfScreen(DefaultScreenOfDisplay(dpy));
|
||||
rv = get_bool_resource("reverseVideo", "ReverseVideo", 0);
|
||||
if (rv) {
|
||||
tc = default_fgcolor;
|
||||
default_fgcolor = default_bgcolor;
|
||||
default_bgcolor = tc;
|
||||
}
|
||||
temp = get_string_resource("foreground", "Foreground");
|
||||
if (temp)
|
||||
default_fgcolor = x_string_to_color(temp, default_fgcolor);
|
||||
temp = get_string_resource("background", "Background");
|
||||
if (temp)
|
||||
default_bgcolor = x_string_to_color(temp, default_bgcolor);
|
||||
default_bordercolor = default_fgcolor;
|
||||
temp = get_string_resource("borderColor", "BorderColor");
|
||||
if (temp)
|
||||
default_bordercolor = x_string_to_color(temp, default_bordercolor);
|
||||
|
||||
temp = get_string_resource("minTimeToLive", "MinTimeToLive");
|
||||
if (temp && atoi(temp)>=0)
|
||||
ttl = atoi(temp);
|
||||
|
||||
#ifdef CMU_ZWGCPLUS
|
||||
if (ttl == 0) {
|
||||
temp = get_string_resource("lifespan", "LifeSpan");
|
||||
if (temp && atoi(temp)>=0)
|
||||
ttl = atoi(temp);
|
||||
}
|
||||
|
||||
get_full_names = get_bool_resource("getFullNames", "GetFullNames", 0);
|
||||
#endif
|
||||
|
||||
reverse_stack = get_bool_resource("reverseStack", "ReverseStack", 0);
|
||||
reset_saver = get_bool_resource("resetSaver", "ResetSaver", 1);
|
||||
/* The default here should be 1, but mwm sucks */
|
||||
set_transient = get_bool_resource("transient", "Transient", 0);
|
||||
enable_delete = get_bool_resource("enableDelete", "EnableDelete", 1);
|
||||
|
||||
temp = get_string_resource("borderWidth", "BorderWidth");
|
||||
/* <<<>>> */
|
||||
if (temp && atoi(temp)>=0)
|
||||
border_width = atoi(temp);
|
||||
|
||||
temp = get_string_resource("internalBorder", "InternalBorder");
|
||||
/* <<<>>> */
|
||||
if (temp && atoi(temp)>=0)
|
||||
internal_border_width = atoi(temp);
|
||||
|
||||
temp = get_string_resource("cursorCode", "CursorCode");
|
||||
/* <<<>>> */
|
||||
if (temp && atoi(temp))
|
||||
cursor_code = atoi(temp);
|
||||
|
||||
cursor = XCreateFontCursor(dpy, cursor_code);
|
||||
if (!cursor)
|
||||
cursor = XCreateFontCursor(dpy, XC_sailboat);
|
||||
|
||||
temp = get_string_resource("pointerColor", "Foreground");
|
||||
if (temp) {
|
||||
char *temp2;
|
||||
XColor cursor_fore, cursor_back;
|
||||
/* XXX need to do our own parsing here, since the RecolorCursor
|
||||
routine requires an XColor, not an unsigned long (pixel) */
|
||||
if (!(temp2 = get_string_resource("background","Background"))) {
|
||||
if (default_bgcolor == WhitePixelOfScreen(DefaultScreenOfDisplay(dpy)))
|
||||
temp2 = "white";
|
||||
else
|
||||
temp2 = "black";
|
||||
}
|
||||
if (XParseColor(dpy,
|
||||
DefaultColormapOfScreen(DefaultScreenOfDisplay(dpy)),
|
||||
temp, &cursor_fore) &&
|
||||
XParseColor(dpy,
|
||||
DefaultColormapOfScreen(DefaultScreenOfDisplay(dpy)),
|
||||
temp2, &cursor_back)) {
|
||||
XRecolorCursor(dpy, cursor, &cursor_fore, &cursor_back);
|
||||
}
|
||||
}
|
||||
if (!(title_name=get_string_resource("title","Title")))
|
||||
if (!(title_name=get_string_resource("name","Name")))
|
||||
title_name=app_instance;
|
||||
|
||||
if (!(icon_name=get_string_resource("iconName","IconName")))
|
||||
if (!(icon_name=get_string_resource("name","Name")))
|
||||
icon_name=app_instance;
|
||||
|
||||
if (!(temp=get_string_resource("name","Name")))
|
||||
if (!(temp=(char *) getenv("RESOURCE_NAME")))
|
||||
temp=app_instance;
|
||||
classhint.res_name=string_Copy(temp);
|
||||
classhint.res_class="Zwgc";
|
||||
|
||||
if (set_transient) {
|
||||
group_leader=XCreateSimpleWindow(dpy,DefaultRootWindow(dpy),0,0,100,100,
|
||||
0,default_bordercolor,default_bgcolor);
|
||||
sizehints.x = 0;
|
||||
sizehints.y = 0;
|
||||
sizehints.width = 100;
|
||||
sizehints.height = 100;
|
||||
sizehints.flags = PPosition | PSize;
|
||||
|
||||
wmhints.input = False;
|
||||
wmhints.initial_state = DontCareState;
|
||||
wmhints.flags = InputHint | StateHint;
|
||||
|
||||
x_set_icccm_hints(dpy,group_leader,"ZwgcGroup","ZwgcGroup",&sizehints,
|
||||
&wmhints,0);
|
||||
}
|
||||
xattributes.border_pixel = default_bordercolor;
|
||||
xattributes.cursor = cursor;
|
||||
xattributes.event_mask = (ExposureMask|ButtonReleaseMask|ButtonPressMask
|
||||
|LeaveWindowMask|Button1MotionMask
|
||||
#ifdef CMU_ZWGCPLUS
|
||||
|KeyPressMask
|
||||
#endif
|
||||
|Button3MotionMask|StructureNotifyMask);
|
||||
xattributes_mask = (CWBackPixel|CWBorderPixel|CWEventMask|CWCursor);
|
||||
|
||||
set_all_desktops = get_bool_resource("allDesktops", "AllDesktops", True);
|
||||
net_wm_desktop = XInternAtom(dpy, "_NET_WM_DESKTOP", False);
|
||||
net_wm_window_type = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
|
||||
net_wm_window_type_utility = XInternAtom(dpy,
|
||||
"_NET_WM_WINDOW_TYPE_UTILITY",
|
||||
False);
|
||||
|
||||
temp = get_string_resource ("backingStore", "BackingStore");
|
||||
if (!temp)
|
||||
return;
|
||||
xattributes_mask |= CWBackingStore;
|
||||
if (!strcasecmp (temp, "notuseful"))
|
||||
xattributes.backing_store = NotUseful;
|
||||
else if (!strcasecmp (temp, "whenmapped"))
|
||||
xattributes.backing_store = WhenMapped;
|
||||
else if (!strcasecmp (temp, "always"))
|
||||
xattributes.backing_store = Always;
|
||||
else if (!strcasecmp (temp, "default"))
|
||||
xattributes_mask &= ~CWBackingStore;
|
||||
else {
|
||||
switch (get_bool_resource ("backingStore", "BackingStore", -1)) {
|
||||
case 0:
|
||||
xattributes.backing_store = NotUseful;
|
||||
break;
|
||||
case 1:
|
||||
xattributes.backing_store = WhenMapped;
|
||||
break;
|
||||
case -1:
|
||||
fprintf (stderr,
|
||||
"zwgc: Cannot interpret backing-store resource value `%s'.\n",
|
||||
temp);
|
||||
xattributes_mask &= ~CWBackingStore;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
x_calc_gravity(int xalign,
|
||||
int yalign)
|
||||
{
|
||||
if (yalign > 0) { /* North */
|
||||
return (xalign > 0) ? NorthWestGravity
|
||||
: (xalign == 0) ? NorthGravity
|
||||
: NorthEastGravity;
|
||||
} else if (yalign == 0) { /* Center */
|
||||
return (xalign > 0) ? WestGravity
|
||||
: (xalign == 0) ? CenterGravity
|
||||
: EastGravity;
|
||||
} else { /* South */
|
||||
return (xalign > 0) ? SouthWestGravity
|
||||
: (xalign == 0) ? SouthGravity
|
||||
: SouthEastGravity;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
x_gram_create(Display *dpy,
|
||||
x_gram *gram,
|
||||
int xalign,
|
||||
int yalign,
|
||||
int xpos,
|
||||
int ypos,
|
||||
int xsize,
|
||||
int ysize,
|
||||
int beepcount)
|
||||
{
|
||||
Window w;
|
||||
XSizeHints sizehints;
|
||||
XWMHints wmhints;
|
||||
XSetWindowAttributes attributes;
|
||||
unsigned long all_desktops = 0xFFFFFFFF;
|
||||
|
||||
/*
|
||||
* Adjust xpos, ypos based on the alignments xalign, yalign and the sizes:
|
||||
*/
|
||||
if (xalign < 0)
|
||||
xpos = WidthOfScreen(DefaultScreenOfDisplay(dpy))
|
||||
- xpos - xsize - 2 * border_width;
|
||||
else if (xalign == 0)
|
||||
xpos = ((WidthOfScreen(DefaultScreenOfDisplay(dpy))
|
||||
- xsize - 2 * border_width) >> 1) + xpos;
|
||||
|
||||
if (yalign<0)
|
||||
ypos = HeightOfScreen(DefaultScreenOfDisplay(dpy))
|
||||
- ypos - ysize - 2 * border_width;
|
||||
else if (yalign == 0)
|
||||
ypos = ((HeightOfScreen(DefaultScreenOfDisplay(dpy))
|
||||
- ysize - 2 * border_width) >> 1) + ypos;
|
||||
|
||||
/*
|
||||
* Create the window:
|
||||
*/
|
||||
attributes = xattributes;
|
||||
attributes.background_pixel = gram->bgcolor;
|
||||
|
||||
gram->w = w = XCreateWindow (dpy, DefaultRootWindow (dpy), xpos, ypos,
|
||||
xsize, ysize, border_width, 0,
|
||||
CopyFromParent, CopyFromParent,
|
||||
xattributes_mask, &attributes);
|
||||
|
||||
sizehints.x = xpos;
|
||||
sizehints.y = ypos;
|
||||
sizehints.width = xsize;
|
||||
sizehints.height = ysize;
|
||||
sizehints.win_gravity = x_calc_gravity(xalign, yalign);
|
||||
sizehints.flags = USPosition | USSize | PWinGravity;
|
||||
|
||||
wmhints.input = False;
|
||||
wmhints.initial_state = NormalState;
|
||||
if (set_transient) {
|
||||
wmhints.window_group = group_leader;
|
||||
wmhints.flags = InputHint | StateHint | WindowGroupHint;
|
||||
|
||||
x_set_icccm_hints(dpy, w, title_name, icon_name, &sizehints, &wmhints,
|
||||
group_leader);
|
||||
} else {
|
||||
wmhints.flags = InputHint | StateHint;
|
||||
|
||||
x_set_icccm_hints(dpy, w, title_name, icon_name, &sizehints, &wmhints, 0);
|
||||
}
|
||||
|
||||
if (net_wm_window_type != None && net_wm_window_type_utility != None)
|
||||
XChangeProperty(dpy, w, net_wm_window_type, XA_ATOM, 32, PropModeReplace,
|
||||
(unsigned char *) &net_wm_window_type_utility, 1);
|
||||
if (set_all_desktops && net_wm_desktop != None)
|
||||
XChangeProperty(dpy, w, net_wm_desktop, XA_CARDINAL, 32, PropModeReplace,
|
||||
(unsigned char *) &all_desktops, 1);
|
||||
|
||||
XSaveContext(dpy, w, desc_context, (XPointer)gram);
|
||||
|
||||
gram->can_die.tv_sec = 0;
|
||||
|
||||
XMapWindow(dpy, w);
|
||||
|
||||
if (beepcount)
|
||||
XBell(dpy, 0);
|
||||
|
||||
xerror_happened = 0;
|
||||
if (reverse_stack && bottom_gram) {
|
||||
XWindowChanges winchanges;
|
||||
|
||||
winchanges.sibling = bottom_gram->w;
|
||||
winchanges.stack_mode = Below;
|
||||
/* Metacity may use border_width even if it's not specified in
|
||||
* the value mask, so we must initialize it. See:
|
||||
* http://bugzilla.gnome.org/show_bug.cgi?id=305257 */
|
||||
winchanges.border_width = border_width;
|
||||
|
||||
begin_xerror_trap (dpy);
|
||||
XReconfigureWMWindow (dpy, w, DefaultScreen (dpy),
|
||||
CWSibling | CWStackMode, &winchanges);
|
||||
end_xerror_trap (dpy);
|
||||
if (xerror_happened) {
|
||||
/* The event didn't go. Print an error message, and continue. */
|
||||
ERROR ("Error configuring window to the bottom of the stack.\n");
|
||||
}
|
||||
}
|
||||
/* we always need to keep a linked list of windows */
|
||||
add_to_bottom(gram);
|
||||
if (xerror_happened)
|
||||
pull_to_top(gram);
|
||||
|
||||
if (reset_saver)
|
||||
XResetScreenSaver(dpy);
|
||||
|
||||
XFlush(dpy);
|
||||
/* Because the flushing/syncing/etc with the error trapping can cause
|
||||
events to be read into the Xlib queue, we need to go through the queue
|
||||
here before exiting so that any pending events get processed.
|
||||
*/
|
||||
x_get_input(dpy);
|
||||
}
|
||||
|
||||
inline static void
|
||||
SetFG(Display *dpy, GC gc, unsigned long foreground) {
|
||||
XGCValues gcvals;
|
||||
|
||||
gcvals.foreground = foreground;
|
||||
XChangeGC(dpy, gc, GCForeground, &gcvals);
|
||||
}
|
||||
|
||||
static void
|
||||
x_gram_draw(Display *dpy, Window w, x_gram *gram, Region region)
|
||||
{
|
||||
int i;
|
||||
GC gc;
|
||||
XGCValues gcvals;
|
||||
xblock *xb;
|
||||
#ifdef X_HAVE_UTF8_STRING
|
||||
XmbTextItem text;
|
||||
#else
|
||||
XwcTextItem text;
|
||||
#endif
|
||||
int startblock, endblock, startpixel = 0, endpixel = 0;
|
||||
|
||||
gc = XCreateGC(dpy, w, 0, &gcvals);
|
||||
XSetRegion(dpy, gc, region);
|
||||
|
||||
if ((markgram == gram) && (STARTBLOCK != -1) && (ENDBLOCK != -1)) {
|
||||
if (xmarkSecond() == XMARK_END_BOUND) {
|
||||
startblock = STARTBLOCK;
|
||||
endblock = ENDBLOCK;
|
||||
startpixel = STARTPIXEL;
|
||||
endpixel = ENDPIXEL;
|
||||
} else {
|
||||
startblock = ENDBLOCK;
|
||||
endblock = STARTBLOCK;
|
||||
startpixel = ENDPIXEL;
|
||||
endpixel = STARTPIXEL;
|
||||
}
|
||||
} else {
|
||||
startblock = -1;
|
||||
endblock = -1;
|
||||
}
|
||||
|
||||
for (i=0, xb = gram->blocks; i < gram->numblocks; i++, xb++) {
|
||||
if (XRectInRegion(region, xb->x1, xb->y1, xb->x2 - xb->x1,
|
||||
xb->y2 - xb->y1) != RectangleOut) {
|
||||
if (i == startblock) {
|
||||
if (i == endblock) {
|
||||
SetFG(dpy, gc, gram->bgcolor);
|
||||
XFillRectangle(dpy, w, gc, xb->x1, xb->y1, startpixel,
|
||||
xb->y2 - xb->y1);
|
||||
SetFG(dpy, gc, xb->fgcolor);
|
||||
XFillRectangle(dpy, w, gc, xb->x1 + startpixel, xb->y1,
|
||||
endpixel - startpixel, xb->y2 - xb->y1);
|
||||
SetFG(dpy, gc, gram->bgcolor);
|
||||
XFillRectangle(dpy, w, gc, xb->x1 + endpixel, xb->y1,
|
||||
xb->x2 - xb->x1 - endpixel, xb->y2 - xb->y1);
|
||||
} else {
|
||||
SetFG(dpy, gc, gram->bgcolor);
|
||||
XFillRectangle(dpy, w, gc, xb->x1, xb->y1, startpixel,
|
||||
xb->y2 - xb->y1);
|
||||
SetFG(dpy, gc, xb->fgcolor);
|
||||
XFillRectangle(dpy, w, gc, xb->x1 + startpixel, xb->y1,
|
||||
xb->x2 - xb->x1 - startpixel,xb->y2 - xb->y1);
|
||||
}
|
||||
} else if (i == endblock) {
|
||||
SetFG(dpy, gc, xb->fgcolor);
|
||||
XFillRectangle(dpy, w, gc, xb->x1, xb->y1, endpixel,
|
||||
xb->y2 - xb->y1);
|
||||
SetFG(dpy, gc, gram->bgcolor);
|
||||
XFillRectangle(dpy, w, gc, xb->x1 + endpixel, xb->y1,
|
||||
xb->x2 - xb->x1 - endpixel, xb->y2 - xb->y1);
|
||||
} else {
|
||||
if (startblock < i && i < endblock) {
|
||||
SetFG(dpy, gc, xb->fgcolor);
|
||||
} else {
|
||||
SetFG(dpy, gc, gram->bgcolor);
|
||||
}
|
||||
XFillRectangle(dpy, w, gc, xb->x1, xb->y1, xb->x2 - xb->x1,
|
||||
xb->y2 - xb->y1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gcvals.function = GXxor;
|
||||
XChangeGC(dpy, gc, GCFunction, &gcvals);
|
||||
|
||||
for (i=0, xb = gram->blocks; i < gram->numblocks; i++, xb++) {
|
||||
if (XRectInRegion(region, xb->x1, xb->y1, xb->x2 - xb->x1,
|
||||
xb->y2 - xb->y1) != RectangleOut) {
|
||||
SetFG(dpy, gc, gram->bgcolor ^ xb->fgcolor);
|
||||
#ifdef X_HAVE_UTF8_STRING
|
||||
text.chars = xb->wstr;
|
||||
#else
|
||||
text.chars = (wchar_t *)xb->wstr;
|
||||
#endif
|
||||
text.nchars = xb->wlen;
|
||||
text.delta = 0;
|
||||
text.font_set = xb->font;
|
||||
#ifdef X_HAVE_UTF8_STRING
|
||||
Xutf8DrawText(dpy, w, gc, xb->x, xb->y, &text, 1);
|
||||
#else
|
||||
XwcDrawText(dpy, w, gc, xb->x, xb->y, &text, 1);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
XFreeGC(dpy, gc);
|
||||
}
|
||||
|
||||
void
|
||||
x_gram_expose(Display *dpy,
|
||||
Window w,
|
||||
x_gram *gram,
|
||||
XExposeEvent *event)
|
||||
{
|
||||
static Region region;
|
||||
static int partregion;
|
||||
XRectangle rect;
|
||||
|
||||
rect.x = (short) event->x;
|
||||
rect.y = (short) event->y;
|
||||
rect.width = (unsigned short) event->width;
|
||||
rect.height = (unsigned short) event->height;
|
||||
|
||||
#ifdef MARK_DEBUG
|
||||
printf("----- xeventExpose:\nx=%d y=%d w=%d h=%d\n-----",
|
||||
event->x,event->y,event->width,event->height);
|
||||
#endif
|
||||
|
||||
if (! partregion) {
|
||||
region=XCreateRegion();
|
||||
partregion = 1;
|
||||
}
|
||||
|
||||
if (rect.width && rect.height) XUnionRectWithRegion(&rect,region,region);
|
||||
|
||||
if (event->count == 0) {
|
||||
x_gram_draw(dpy,w,gram,region);
|
||||
partregion = 0;
|
||||
XDestroyRegion(region);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* X_DISPLAY_MISSING */
|
92
zwgc/X_gram.h
Normal file
92
zwgc/X_gram.h
Normal file
@ -0,0 +1,92 @@
|
||||
/* 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 <zephyr/mit-copyright.h>
|
||||
|
||||
#ifndef x_gram_TYPE
|
||||
#define x_gram_TYPE
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "formatter.h"
|
||||
|
||||
typedef struct _xblock {
|
||||
unsigned long fgcolor;
|
||||
XFontSet font;
|
||||
int x,y;
|
||||
int x1,y1,x2,y2; /* bounds of block. used for cut and paste. */
|
||||
int strindex;
|
||||
int strlen;
|
||||
char *wstr;
|
||||
int wlen;
|
||||
} xblock;
|
||||
|
||||
typedef struct _x_gram {
|
||||
unsigned long bgcolor;
|
||||
int numblocks;
|
||||
xblock *blocks;
|
||||
char *text;
|
||||
struct _x_gram *below,*above;
|
||||
Window w;
|
||||
#ifdef CMU_ZWGCPLUS
|
||||
ZNotice_t *notice;
|
||||
#endif
|
||||
struct timeval can_die;
|
||||
} x_gram;
|
||||
|
||||
typedef struct _xauxblock {
|
||||
int align;
|
||||
XFontSet font;
|
||||
char *str;
|
||||
int len;
|
||||
int width;
|
||||
} xauxblock;
|
||||
|
||||
typedef struct _xmode {
|
||||
int bold;
|
||||
int italic;
|
||||
int size;
|
||||
int align;
|
||||
int expcolor;
|
||||
unsigned long color;
|
||||
char *substyle;
|
||||
char *font;
|
||||
} xmode;
|
||||
|
||||
typedef struct _xlinedesc {
|
||||
int startblock;
|
||||
int numblock;
|
||||
int lsize;
|
||||
int csize;
|
||||
int rsize;
|
||||
int ascent;
|
||||
int descent;
|
||||
} xlinedesc;
|
||||
|
||||
/* alignment values: */
|
||||
#define LEFTALIGN 0
|
||||
#define CENTERALIGN 1
|
||||
#define RIGHTALIGN 2
|
||||
|
||||
extern void x_gram_init(Display *);
|
||||
extern void x_gram_create(Display *, x_gram *, int, int, int, int, int, int, int);
|
||||
extern void x_gram_expose(Display *, Window, x_gram *, XExposeEvent *);
|
||||
extern void xshow(Display *, desctype *, int, int);
|
||||
extern void xcut(Display *, XEvent *, XContext);
|
||||
extern void x_get_input(Display *);
|
||||
extern void xshowinit(void);
|
||||
|
||||
#endif
|
55
zwgc/browser.c
Normal file
55
zwgc/browser.c
Normal file
@ -0,0 +1,55 @@
|
||||
/* 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".
|
||||
*/
|
||||
|
||||
#if (!defined(lint) && !defined(SABER))
|
||||
static char rcsid_browser_c[] = "$Id$";
|
||||
#endif
|
||||
|
||||
#include <zephyr/mit-copyright.h>
|
||||
|
||||
#include <sysdep.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include "zwgc.h"
|
||||
|
||||
static int browser_fd;
|
||||
struct sockaddr_un sun;
|
||||
|
||||
int BOpenSocket()
|
||||
{
|
||||
int fd,len;
|
||||
char *temp;
|
||||
|
||||
if ((fd=socket(PF_UNIX,SOCK_STREAM,0)) == -1)
|
||||
return(-1);
|
||||
|
||||
sun.sun_family=AF_UNIX;
|
||||
if (temp=getenv("WGSOCK"))
|
||||
strncpy(sun.sunpath,temp,sizeof(sun.sunpath));
|
||||
else
|
||||
sprintf(sun.sun_path,"/tmp/.zwgc.%d",getuid());
|
||||
if (bind(fd,(struct sockaddr *) &sun,
|
||||
(len=strlen(sun.sunpath)) > sizeof(sun.sunpath)?
|
||||
sizeof(sun.sunpath):len) == -1) {
|
||||
close(fd);
|
||||
return(-1);
|
||||
}
|
||||
|
||||
if (listen(fd,5) == -1) {
|
||||
unlink(sun.sunpath);
|
||||
close(fd);
|
||||
return(-1);
|
||||
}
|
||||
|
||||
return(fd);
|
||||
}
|
40
zwgc/browser.h
Normal file
40
zwgc/browser.h
Normal file
@ -0,0 +1,40 @@
|
||||
/* 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 <zephyr/mit-copyright.h>
|
||||
|
||||
#define BROWSER_NEW_REQ 1
|
||||
#define BROWSER_NEW_REQ_RESP 2
|
||||
#define BROWSER_ZPACKET 3
|
||||
#define BROWSER_ZPACKET_RESP 4
|
||||
#define BROWSER_TEXT 5
|
||||
#define BROWSER_WINDOW_ID 6
|
||||
#define BROWSER_VAR_REQ 7
|
||||
#define BROWSER_VAR_REQ_RESP 8
|
||||
|
||||
|
||||
#define BROWSER_TYPE_OVERRIDE 11
|
||||
#define BROWSER_TYPE_DRIVER 12
|
||||
#define BROWSER_TYPE_WM 13
|
||||
#define BROWSER_TYPE_SIMPLE 14
|
||||
|
||||
#define BROWSER_ACK 21
|
||||
#define BROWSER_NAK 22
|
||||
|
||||
#define BROWSER_KEEP 31
|
||||
#define BROWSER_LOSE 32
|
||||
|
||||
extern int ZBOpenConnection();
|
||||
extern void ZBCloseConnection( /* int fd */ );
|
||||
extern char *var_get_variable( /* char *varname */ );
|
46
zwgc/buffer.c
Normal file
46
zwgc/buffer.c
Normal file
@ -0,0 +1,46 @@
|
||||
/* 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_buffer_c[] = "$Id$";
|
||||
#endif
|
||||
|
||||
#include <zephyr/mit-copyright.h>
|
||||
|
||||
#include "new_memory.h"
|
||||
#include "buffer.h"
|
||||
|
||||
static char *buffer = 0;
|
||||
|
||||
string
|
||||
buffer_to_string(void)
|
||||
{
|
||||
return(buffer);
|
||||
}
|
||||
|
||||
void
|
||||
clear_buffer(void)
|
||||
{
|
||||
if (buffer)
|
||||
free(buffer);
|
||||
|
||||
buffer = string_Copy("");
|
||||
}
|
||||
|
||||
void
|
||||
append_buffer(char *str)
|
||||
{
|
||||
buffer = string_Concat2(buffer, str);
|
||||
}
|
26
zwgc/buffer.h
Normal file
26
zwgc/buffer.h
Normal file
@ -0,0 +1,26 @@
|
||||
/* 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 <zephyr/mit-copyright.h>
|
||||
|
||||
#ifndef buffer_MODULE
|
||||
#define buffer_MODULE
|
||||
|
||||
#include "new_string.h"
|
||||
|
||||
extern string buffer_to_string(void);
|
||||
extern void clear_buffer(void);
|
||||
extern void append_buffer(char *);
|
||||
|
||||
#endif
|
45
zwgc/character_class.c
Normal file
45
zwgc/character_class.c
Normal file
@ -0,0 +1,45 @@
|
||||
/* 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_character_class_c[] = "$Id$";
|
||||
#endif
|
||||
|
||||
#include <zephyr/mit-copyright.h>
|
||||
#include <zephyr/zephyr.h>
|
||||
#include "character_class.h"
|
||||
|
||||
/*
|
||||
* It may look like we are passing the cache by value, but since it's
|
||||
* really an array we are passing by reference. C strikes again....
|
||||
*/
|
||||
|
||||
static character_class cache;
|
||||
|
||||
/* character_class */
|
||||
char *
|
||||
string_to_character_class(string str)
|
||||
{
|
||||
int i, l;
|
||||
|
||||
(void) memset(cache, 0, sizeof(cache));
|
||||
|
||||
l = strlen(str);
|
||||
|
||||
for (i = 0; i < l; i++)
|
||||
cache[(unsigned char)str[i]] = 1;
|
||||
|
||||
return(cache);
|
||||
}
|
28
zwgc/character_class.h
Normal file
28
zwgc/character_class.h
Normal file
@ -0,0 +1,28 @@
|
||||
/* 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 <zephyr/mit-copyright.h>
|
||||
|
||||
#ifndef character_class_TYPE
|
||||
#define character_class_TYPE
|
||||
|
||||
#include "new_string.h"
|
||||
|
||||
#define NUMBER_OF_CHARACTERS 256
|
||||
|
||||
typedef char character_class[NUMBER_OF_CHARACTERS];
|
||||
|
||||
extern /* character_class */ char * string_to_character_class(string);
|
||||
|
||||
#endif
|
260
zwgc/dictionary.c
Normal file
260
zwgc/dictionary.c
Normal file
@ -0,0 +1,260 @@
|
||||
/* 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_dictionary_c[] = "$Id$";
|
||||
#endif
|
||||
|
||||
/*
|
||||
* dictionary - a module implementing a generic dictionary. That is,
|
||||
* any type can be used for the values that keys are bound to.
|
||||
* Keys are always strings.
|
||||
*
|
||||
* Overview:
|
||||
*
|
||||
* A dictionary is a set of bindings which bind values of some
|
||||
* type (this type is the generic parameter of the dictionary) to
|
||||
* strings. At most one value can be bound to any one string.
|
||||
* The value that a string is bound to can be changed later.
|
||||
* Bindings can also be deleted later. It is also possible to
|
||||
* enumerate all of the bindings in a dictionary. Dictionarys
|
||||
* are heap based and must be created & destroyed accordingly.
|
||||
*
|
||||
* Note: This module assumes that malloc NEVER returns 0 for reasonable
|
||||
* requests. It is the users responsibility to either ensure that
|
||||
* this happens or supply a version of malloc with error
|
||||
* handling.
|
||||
*
|
||||
* Dictionarys are mutable.
|
||||
*
|
||||
* Implementation:
|
||||
*
|
||||
* A standard chaining hash table is used to implement dictionarys.
|
||||
* Each dictionary has an associated size (# of slots), allowing
|
||||
* different size dictionaries as needed.
|
||||
*/
|
||||
|
||||
#include "TYPE_T_dictionary.h"
|
||||
#include "new_string.h"
|
||||
#include "new_memory.h"
|
||||
|
||||
#ifndef NULL
|
||||
#define NULL 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
* TYPE_T_dictionary TYPE_T_dictionary_Create(int size):
|
||||
* Requires: size > 0
|
||||
* Effects: Returns a new empty dictionary containing no bindings.
|
||||
* The returned dictionary must be destroyed using
|
||||
* TYPE_T_dictionary_Destroy. Size is a time vs space
|
||||
* parameter. For this implementation, space used is
|
||||
* proportional to size and time used is proportional
|
||||
* to number of bindings divided by size. It is preferable
|
||||
* that size is a prime number.
|
||||
*/
|
||||
|
||||
TYPE_T_dictionary
|
||||
TYPE_T_dictionary_Create(int size)
|
||||
{
|
||||
int i;
|
||||
TYPE_T_dictionary result;
|
||||
|
||||
result = (TYPE_T_dictionary)malloc(sizeof(struct _TYPE_T_dictionary));
|
||||
result->size = size;
|
||||
result->slots = (TYPE_T_dictionary_binding **)malloc(
|
||||
size*sizeof(TYPE_T_dictionary_binding *));
|
||||
|
||||
for (i=0; i<size; i++)
|
||||
result->slots[i] = NULL;
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
/*
|
||||
* void TYPE_T_dictionary_Destroy(TYPE_T_dictionary d):
|
||||
* Requires: d is a non-destroyed TYPE_T_dictionary
|
||||
* Modifies: d
|
||||
* Effects: Destroys dictionary d freeing up the space it consumes.
|
||||
* Dictionary d should never be referenced again. Note that
|
||||
* free is NOT called on the values of the bindings. If
|
||||
* this is needed, the client must do this first using
|
||||
* TYPE_T_dictionary_Enumerate.
|
||||
*/
|
||||
|
||||
void
|
||||
TYPE_T_dictionary_Destroy(TYPE_T_dictionary d)
|
||||
{
|
||||
int i;
|
||||
TYPE_T_dictionary_binding *binding_ptr, *new_binding_ptr;
|
||||
|
||||
for (i=0; i<d->size; i++) {
|
||||
binding_ptr = d->slots[i];
|
||||
while (binding_ptr) {
|
||||
new_binding_ptr = binding_ptr->next;
|
||||
free(binding_ptr->key);
|
||||
free(binding_ptr);
|
||||
binding_ptr = new_binding_ptr;
|
||||
}
|
||||
}
|
||||
free(d->slots);
|
||||
free(d);
|
||||
}
|
||||
|
||||
/*
|
||||
* void TYPE_T_dictionary_Enumerate(TYPE_T_dictionary d; void (*proc)()):
|
||||
* Requires: proc is a void procedure taking 1 argument, a
|
||||
* TYPE_T_dictionary_binding pointer, which does not
|
||||
* make any calls using dictionary d.
|
||||
* Effects: Calls proc once with each binding in dictionary d.
|
||||
* Order of bindings passed is undefined. Note that
|
||||
* only the value field of the binding should be considered
|
||||
* writable by proc.
|
||||
*/
|
||||
|
||||
void TYPE_T_dictionary_Enumerate(TYPE_T_dictionary d,
|
||||
void (*proc)(TYPE_T_dictionary_binding *))
|
||||
{
|
||||
int i;
|
||||
TYPE_T_dictionary_binding *binding_ptr;
|
||||
|
||||
for (i=0; i<d->size; i++) {
|
||||
binding_ptr = d->slots[i];
|
||||
while (binding_ptr) {
|
||||
proc(binding_ptr);
|
||||
binding_ptr = binding_ptr->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Private routine:
|
||||
*
|
||||
* unsigned int dictionary__hash(char *s):
|
||||
* Effects: Hashs s to an unsigned integer. This number mod the
|
||||
* hash table size is supposed to roughly evenly distribute
|
||||
* keys over the table's slots.
|
||||
*/
|
||||
|
||||
static unsigned int
|
||||
dictionary__hash(char *s)
|
||||
{
|
||||
unsigned int result = 0;
|
||||
|
||||
if (!s)
|
||||
return(result);
|
||||
|
||||
while (s[0]) {
|
||||
result <<= 1;
|
||||
result += s[0];
|
||||
s++;
|
||||
}
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
/*
|
||||
* TYPE_T_dictionary_binding *TYPE_T_dictionary_Lookup(TYPE_T_dictionary d,
|
||||
* char *key):
|
||||
* Effects: If key is not bound in d, returns 0. Othersize,
|
||||
* returns a pointer to the binding that binds key.
|
||||
* Note the access restrictions on bindings...
|
||||
*/
|
||||
|
||||
TYPE_T_dictionary_binding *
|
||||
TYPE_T_dictionary_Lookup(TYPE_T_dictionary d,
|
||||
char *key)
|
||||
{
|
||||
TYPE_T_dictionary_binding *binding_ptr;
|
||||
|
||||
binding_ptr = d->slots[dictionary__hash(key)%(d->size)];
|
||||
while (binding_ptr) {
|
||||
if (string_Eq(key, binding_ptr->key))
|
||||
return(binding_ptr);
|
||||
binding_ptr = binding_ptr->next;
|
||||
}
|
||||
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* TYPE_T_dictionary_binding *TYPE_T_dictionary_Define(TYPE_T_dictionary d,
|
||||
* char *key,
|
||||
* int *already_existed):
|
||||
* Modifies: d
|
||||
* Effects: If key is bound in d, returns a pointer to the binding
|
||||
* that binds key. Otherwise, adds a binding of key to
|
||||
* d and returns its address. If already_existed is non-zero
|
||||
* then *already_existed is set to 0 if key was not
|
||||
* previously bound in d and 1 otherwise.
|
||||
* Note the access restrictions on bindings... Note also
|
||||
* that the value that key is bounded to if a binding is
|
||||
* created is undefined. The caller should set the value
|
||||
* in this case.
|
||||
*/
|
||||
|
||||
TYPE_T_dictionary_binding *
|
||||
TYPE_T_dictionary_Define(TYPE_T_dictionary d,
|
||||
char *key,
|
||||
int *already_existed)
|
||||
{
|
||||
TYPE_T_dictionary_binding **ptr_to_the_slot, *binding_ptr;
|
||||
|
||||
ptr_to_the_slot = &(d->slots[dictionary__hash(key)%(d->size)]);
|
||||
|
||||
binding_ptr = *ptr_to_the_slot;
|
||||
while (binding_ptr) {
|
||||
if (string_Eq(binding_ptr->key, key)) {
|
||||
if (already_existed)
|
||||
*already_existed = 1;
|
||||
return(binding_ptr);
|
||||
}
|
||||
binding_ptr = binding_ptr->next;
|
||||
}
|
||||
|
||||
if (already_existed)
|
||||
*already_existed = 0;
|
||||
binding_ptr = (TYPE_T_dictionary_binding *)malloc(
|
||||
sizeof(TYPE_T_dictionary_binding));
|
||||
binding_ptr->next = *ptr_to_the_slot;
|
||||
binding_ptr->key = string_Copy(key);
|
||||
*ptr_to_the_slot = binding_ptr;
|
||||
return(binding_ptr);
|
||||
}
|
||||
|
||||
/*
|
||||
* void TYPE_T_dictionary_Delete(TYPE_T_dictionary d,
|
||||
* TYPE_T_dictionary_binding *b):
|
||||
* Requires: *b is a binding in d.
|
||||
* Modifies: d
|
||||
* Effects: Removes the binding *b from d. Note that if
|
||||
* b->value needs to be freed, it should be freed
|
||||
* before making this call.
|
||||
*/
|
||||
|
||||
void TYPE_T_dictionary_Delete(TYPE_T_dictionary d,
|
||||
TYPE_T_dictionary_binding *b)
|
||||
{
|
||||
TYPE_T_dictionary_binding **ptr_to_binding_ptr;
|
||||
|
||||
ptr_to_binding_ptr = &(d->slots[dictionary__hash(b->key)%(d->size)]);
|
||||
|
||||
while (*ptr_to_binding_ptr != b)
|
||||
ptr_to_binding_ptr = &((*ptr_to_binding_ptr)->next);
|
||||
|
||||
*ptr_to_binding_ptr = b->next;
|
||||
free(b->key);
|
||||
free(b);
|
||||
}
|
112
zwgc/dictionary.h
Normal file
112
zwgc/dictionary.h
Normal file
@ -0,0 +1,112 @@
|
||||
/* 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".
|
||||
*/
|
||||
|
||||
#ifndef TYPE_T_dictionary_TYPE
|
||||
#define TYPE_T_dictionary_TYPE
|
||||
|
||||
typedef struct _TYPE_T_dictionary_binding {
|
||||
struct _TYPE_T_dictionary_binding *next; /* PRIVATE */
|
||||
char *key; /* READ-ONLY */
|
||||
TYPE_T value;
|
||||
} TYPE_T_dictionary_binding;
|
||||
|
||||
typedef struct _TYPE_T_dictionary { /* PRIVATE */
|
||||
int size;
|
||||
TYPE_T_dictionary_binding **slots;
|
||||
} *TYPE_T_dictionary;
|
||||
|
||||
/*
|
||||
* TYPE_T_dictionary TYPE_T_dictionary_Create(int size):
|
||||
* Requires: size > 0
|
||||
* Effects: Returns a new empty dictionary containing no bindings.
|
||||
* The returned dictionary must be destroyed using
|
||||
* TYPE_T_dictionary_Destroy. Size is a time vs space
|
||||
* parameter. For this implementation, space used is
|
||||
* proportional to size and time used is proportional
|
||||
* to number of bindings divided by size. It is preferable
|
||||
* that size is a prime number.
|
||||
*/
|
||||
|
||||
extern TYPE_T_dictionary TYPE_T_dictionary_Create(int);
|
||||
|
||||
/*
|
||||
* void TYPE_T_dictionary_Destroy(TYPE_T_dictionary d):
|
||||
* Requires: d is a non-destroyed TYPE_T_dictionary
|
||||
* Modifies: d
|
||||
* Effects: Destroys dictionary d freeing up the space it consumes.
|
||||
* Dictionary d should never be referenced again. Note that
|
||||
* free is NOT called on the values of the bindings. If
|
||||
* this is needed, the client must do this first using
|
||||
* TYPE_T_dictionary_Enumerate.
|
||||
*/
|
||||
|
||||
extern void TYPE_T_dictionary_Destroy(TYPE_T_dictionary);
|
||||
|
||||
/*
|
||||
* void TYPE_T_dictionary_Enumerate(TYPE_T_dictionary d;
|
||||
* void (*proc)(TYPE_T_dictionary_binding *b)):
|
||||
* Requires: proc is a void procedure taking 1 argument, a
|
||||
* TYPE_T_dictionary_binding pointer, which does not
|
||||
* make any calls using dictionary d.
|
||||
* Effects: Calls proc once with each binding in dictionary d.
|
||||
* Order of bindings passed is undefined. Note that
|
||||
* only the value field of the binding should be considered
|
||||
* writable by proc.
|
||||
*/
|
||||
|
||||
extern void TYPE_T_dictionary_Enumerate(TYPE_T_dictionary, void (*)(TYPE_T_dictionary_binding *));
|
||||
|
||||
/*
|
||||
* TYPE_T_dictionary_binding *TYPE_T_dictionary_Lookup(TYPE_T_dictionary d,
|
||||
* char *key):
|
||||
* Effects: If key is not bound in d, returns 0. Othersize,
|
||||
* returns a pointer to the binding that binds key.
|
||||
* Note the access restrictions on bindings...
|
||||
*/
|
||||
|
||||
extern TYPE_T_dictionary_binding *TYPE_T_dictionary_Lookup(TYPE_T_dictionary,
|
||||
char *);
|
||||
|
||||
/*
|
||||
* TYPE_T_dictionary_binding *TYPE_T_dictionary_Define(TYPE_T_dictionary d,
|
||||
* char *key,
|
||||
* int *already_existed):
|
||||
* Modifies: d
|
||||
* Effects: If key is bound in d, returns a pointer to the binding
|
||||
* that binds key. Otherwise, adds a binding of key to
|
||||
* d and returns its address. If already_existed is non-zero
|
||||
* then *already_existed is set to 0 if key was not
|
||||
* previously bound in d and 1 otherwise.
|
||||
* Note the access restrictions on bindings... Note also
|
||||
* that the value that key is bounded to if a binding is
|
||||
* created is undefined. The caller should set the value
|
||||
* in this case.
|
||||
*/
|
||||
|
||||
extern TYPE_T_dictionary_binding *TYPE_T_dictionary_Define(TYPE_T_dictionary,
|
||||
char *, int *);
|
||||
|
||||
/*
|
||||
* void TYPE_T_dictionary_Delete(TYPE_T_dictionary d,
|
||||
* TYPE_T_dictionary_binding *b):
|
||||
* Requires: *b is a binding in d.
|
||||
* Modifies: d
|
||||
* Effects: Removes the binding *b from d. Note that if
|
||||
* b->value needs to be freed, it should be freed
|
||||
* before making this call.
|
||||
*/
|
||||
|
||||
extern void TYPE_T_dictionary_Delete(TYPE_T_dictionary,
|
||||
TYPE_T_dictionary_binding *);
|
||||
|
||||
#endif
|
22
zwgc/error.c
Normal file
22
zwgc/error.c
Normal file
@ -0,0 +1,22 @@
|
||||
/* 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_error_c[] = "$Id$";
|
||||
#endif
|
||||
|
||||
#include <zephyr/mit-copyright.h>
|
||||
|
||||
int error_code;
|
59
zwgc/error.h
Normal file
59
zwgc/error.h
Normal file
@ -0,0 +1,59 @@
|
||||
/* 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 <zephyr/mit-copyright.h>
|
||||
|
||||
#ifndef error_MODULE
|
||||
#define error_MODULE
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <zephyr/zephyr.h>
|
||||
|
||||
extern int error_code;
|
||||
|
||||
#define FATAL_TRAP(function_call, message) \
|
||||
{ error_code = (function_call);\
|
||||
if (error_code) {\
|
||||
com_err("zwgc", error_code,\
|
||||
message);\
|
||||
exit(3);\
|
||||
}\
|
||||
}
|
||||
|
||||
#define TRAP(function_call, message) \
|
||||
{ error_code = (function_call);\
|
||||
if (error_code) {\
|
||||
com_err("zwgc", error_code,\
|
||||
message);\
|
||||
}\
|
||||
}
|
||||
|
||||
#define ERROR(a) { fprintf(stderr, "zwgc: "); \
|
||||
fprintf(stderr, a);\
|
||||
fflush(stderr); }
|
||||
|
||||
#define ERROR2(a,b) { fprintf(stderr, "zwgc: "); \
|
||||
fprintf(stderr, a, b);\
|
||||
fflush(stderr); }
|
||||
|
||||
#define ERROR3(a,b,c) { fprintf(stderr, "zwgc: "); \
|
||||
fprintf(stderr, a, b, c);\
|
||||
fflush(stderr); }
|
||||
|
||||
#define ERROR4(a,b,c,d) { fprintf(stderr, "zwgc: "); \
|
||||
fprintf(stderr, a, b, c, d);\
|
||||
fflush(stderr); }
|
||||
|
||||
#endif
|
300
zwgc/eval.c
Normal file
300
zwgc/eval.c
Normal file
@ -0,0 +1,300 @@
|
||||
/* 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_eval_c[] = "$Id$";
|
||||
#endif
|
||||
|
||||
#include <zephyr/mit-copyright.h>
|
||||
|
||||
/****************************************************************************/
|
||||
/* */
|
||||
/* Code to evaluate an expression: */
|
||||
/* */
|
||||
/****************************************************************************/
|
||||
|
||||
#include <zephyr/zephyr.h>
|
||||
#include "new_memory.h"
|
||||
#include "node.h"
|
||||
#include "eval.h"
|
||||
#include "substitute.h"
|
||||
#include "port.h"
|
||||
#include "buffer.h"
|
||||
#include "regexp.h"
|
||||
#include "formatter.h"
|
||||
#include "text_operations.h"
|
||||
#include "zwgc.h"
|
||||
#include "variables.h"
|
||||
|
||||
/****************************************************************************/
|
||||
/* */
|
||||
/* Code to deal with string/boolean conversion: */
|
||||
/* */
|
||||
/****************************************************************************/
|
||||
|
||||
/*
|
||||
* Internal Routine:
|
||||
*
|
||||
* int string_to_bool(string str)
|
||||
* Effects: Returns true iff the string str represents true.
|
||||
* True is represented by any string which is equal to
|
||||
* "true" when case is disregraded.
|
||||
*/
|
||||
|
||||
#define string_to_bool(str) (!strcasecmp(str,"true"))
|
||||
|
||||
/*
|
||||
* Internal Routine:
|
||||
*
|
||||
* string bool_to_string(int bool)
|
||||
* Effects: Returns a string representive for the C boolean bool.
|
||||
* (In C, true == non-zero) I.e.,
|
||||
* string_to_bool(bool_to_string(x)) == !!x.
|
||||
* The string returned is on the heap & must be freed
|
||||
* eventually.
|
||||
*/
|
||||
|
||||
static string
|
||||
bool_to_string(int bool)
|
||||
{
|
||||
return(bool ? string_Copy("TRUE") : string_Copy("FALSE"));
|
||||
}
|
||||
|
||||
/*
|
||||
* int eval_bool_expr(Node *expr)
|
||||
* Modifies: dict
|
||||
* Requires: expr is a proper expression or NULL. (see node.c)
|
||||
* Effects: Evaluates expr to its boolean value which is returned.
|
||||
* NULL is defined to have the boolean value true.
|
||||
*/
|
||||
|
||||
int
|
||||
eval_bool_expr(Node *expr)
|
||||
{
|
||||
string temp;
|
||||
int result;
|
||||
|
||||
if (!expr)
|
||||
return(1);
|
||||
|
||||
temp = eval_expr(expr);
|
||||
result = string_to_bool(temp);
|
||||
free(temp);
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
/* */
|
||||
/* Code to evaluate an expression: */
|
||||
/* */
|
||||
/****************************************************************************/
|
||||
|
||||
/*
|
||||
* string eval_expr(Node *expr)
|
||||
* Modifies: dict
|
||||
* Requires: expr is a proper expression (NOT NULL). (see node.c)
|
||||
* Effects: Evaluates expr to its string value which is returned.
|
||||
* The returned string is on the heap and must be freed
|
||||
* eventually.
|
||||
*/
|
||||
|
||||
string
|
||||
eval_expr(Node *expr)
|
||||
{
|
||||
int opcode = expr->opcode;
|
||||
int bool_result = 0;
|
||||
string first, second;
|
||||
char *result = NULL;
|
||||
string *text_ptr;
|
||||
|
||||
/*
|
||||
* Dispatch based on the opcode of the top node in the expression:
|
||||
*/
|
||||
switch (opcode) {
|
||||
case STRING_CONSTANT_OPCODE:
|
||||
return(string_Copy(expr->d.string_constant));
|
||||
|
||||
case VARREF_OPCODE:
|
||||
return(string_Copy(var_get_variable(expr->d.string_constant)));
|
||||
|
||||
case BUFFER_OPCODE:
|
||||
return(string_Copy(buffer_to_string()));
|
||||
|
||||
/*
|
||||
* Handle unary expressions:
|
||||
*/
|
||||
case NOT_OPCODE:
|
||||
case SUBSTITUTE_OPCODE:
|
||||
case PROTECT_OPCODE:
|
||||
case VERBATIM_OPCODE:
|
||||
case STYLESTRIP_OPCODE:
|
||||
case GETENV_OPCODE:
|
||||
case UPCASE_OPCODE:
|
||||
case DOWNCASE_OPCODE:
|
||||
case ZVAR_OPCODE:
|
||||
case GET_OPCODE:
|
||||
first = eval_expr(expr->d.nodes.first);
|
||||
|
||||
switch (opcode) {
|
||||
case NOT_OPCODE:
|
||||
result = bool_to_string(! string_to_bool(first));
|
||||
break;
|
||||
|
||||
case SUBSTITUTE_OPCODE:
|
||||
result = substitute(var_get_variable, first);
|
||||
break;
|
||||
|
||||
case PROTECT_OPCODE:
|
||||
result=protect(first);
|
||||
break;
|
||||
|
||||
case VERBATIM_OPCODE:
|
||||
return(verbatim(first,0));
|
||||
|
||||
case STYLESTRIP_OPCODE:
|
||||
return(stylestrip(first));
|
||||
|
||||
case GETENV_OPCODE:
|
||||
result = getenv(first);
|
||||
if (!result)
|
||||
result = string_Copy("");
|
||||
else
|
||||
result = string_Copy(result);
|
||||
break;
|
||||
|
||||
case UPCASE_OPCODE:
|
||||
return(string_Upcase(first));
|
||||
|
||||
case DOWNCASE_OPCODE:
|
||||
return(string_Downcase(first));
|
||||
|
||||
case ZVAR_OPCODE:
|
||||
result = ZGetVariable(first);
|
||||
if (!result)
|
||||
result = string_Copy("");
|
||||
else
|
||||
result = string_Copy(result);
|
||||
break;
|
||||
|
||||
case GET_OPCODE:
|
||||
result = read_from_port(first);
|
||||
break;
|
||||
}
|
||||
free(first);
|
||||
break;
|
||||
|
||||
/*
|
||||
* Handle binary operators:
|
||||
*/
|
||||
case PLUS_OPCODE:
|
||||
case AND_OPCODE:
|
||||
case OR_OPCODE:
|
||||
case EQ_OPCODE:
|
||||
case NEQ_OPCODE:
|
||||
case REGEQ_OPCODE:
|
||||
case REGNEQ_OPCODE:
|
||||
first = eval_expr(expr->d.nodes.first);
|
||||
second = eval_expr(expr->d.nodes.second);
|
||||
|
||||
switch (opcode) {
|
||||
case PLUS_OPCODE:
|
||||
result = string_Concat(first, second);
|
||||
free(first);
|
||||
free(second);
|
||||
return(result);
|
||||
|
||||
case AND_OPCODE:
|
||||
bool_result = string_to_bool(first) && string_to_bool(second);
|
||||
break;
|
||||
|
||||
case OR_OPCODE:
|
||||
bool_result = string_to_bool(first) || string_to_bool(second);
|
||||
break;
|
||||
|
||||
case EQ_OPCODE:
|
||||
bool_result = string_Eq(first, second);
|
||||
break;
|
||||
|
||||
case NEQ_OPCODE:
|
||||
bool_result = string_Neq(first, second);
|
||||
break;
|
||||
|
||||
case REGEQ_OPCODE:
|
||||
bool_result = ed_regexp_match_p(first, second);
|
||||
break;
|
||||
|
||||
case REGNEQ_OPCODE:
|
||||
bool_result = !ed_regexp_match_p(first, second);
|
||||
break;
|
||||
}
|
||||
free(first);
|
||||
free(second);
|
||||
result = bool_to_string(bool_result);
|
||||
break;
|
||||
|
||||
/*
|
||||
* Handle text-manipulation operators:
|
||||
*/
|
||||
case LANY_OPCODE: case RANY_OPCODE:
|
||||
case LBREAK_OPCODE: case RBREAK_OPCODE:
|
||||
case LSPAN_OPCODE: case RSPAN_OPCODE:
|
||||
first = eval_expr(expr->d.nodes.first);
|
||||
second = eval_expr(expr->d.nodes.second);
|
||||
text_ptr = &first;
|
||||
|
||||
switch (opcode) {
|
||||
case LANY_OPCODE:
|
||||
result = lany(text_ptr, second);
|
||||
break;
|
||||
|
||||
case RANY_OPCODE:
|
||||
result = rany(text_ptr, second);
|
||||
break;
|
||||
|
||||
case LBREAK_OPCODE:
|
||||
result = lbreak(text_ptr, string_to_character_class(second));
|
||||
break;
|
||||
|
||||
case RBREAK_OPCODE:
|
||||
result = rbreak(text_ptr, string_to_character_class(second));
|
||||
break;
|
||||
|
||||
case LSPAN_OPCODE:
|
||||
result = lspan(text_ptr, string_to_character_class(second));
|
||||
break;
|
||||
|
||||
case RSPAN_OPCODE:
|
||||
result = rspan(text_ptr, string_to_character_class(second));
|
||||
break;
|
||||
}
|
||||
|
||||
if (expr->d.nodes.first->opcode == VARREF_OPCODE)
|
||||
var_set_variable(expr->d.nodes.first->d.string_constant, first);
|
||||
free(first);
|
||||
free(second);
|
||||
break;
|
||||
|
||||
#ifdef DEBUG
|
||||
default:
|
||||
printf("zwgc: internal error: attempt to evaluate the following non-expression:\n"); fflush(stdout);
|
||||
node_display(expr);
|
||||
printf("\n\n");
|
||||
exit(2);
|
||||
#endif
|
||||
}
|
||||
|
||||
return(result);
|
||||
}
|
43
zwgc/eval.h
Normal file
43
zwgc/eval.h
Normal file
@ -0,0 +1,43 @@
|
||||
/* 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 <zephyr/mit-copyright.h>
|
||||
|
||||
#ifndef eval_MODULE
|
||||
#define eval_MODULE
|
||||
|
||||
#include "new_string.h"
|
||||
|
||||
/*
|
||||
* string eval_expr(Node *expr)
|
||||
* Modifies: dict
|
||||
* Requires: expr is a proper expression (NOT NULL). (see node.c)
|
||||
* Effects: Evaluates expr to its string value which is returned.
|
||||
* The returned string is on the heap and must be freed
|
||||
* eventually.
|
||||
*/
|
||||
|
||||
extern string eval_expr(Node *);
|
||||
|
||||
/*
|
||||
* int eval_bool_expr(Node *expr)
|
||||
* Modifies: dict
|
||||
* Requires: expr is a proper expression or NULL. (see node.c)
|
||||
* Effects: Evaluates expr to its boolean value which is returned.
|
||||
* NULL is defined to have the boolean value true.
|
||||
*/
|
||||
|
||||
extern int eval_bool_expr(Node *);
|
||||
|
||||
#endif
|
482
zwgc/exec.c
Normal file
482
zwgc/exec.c
Normal file
@ -0,0 +1,482 @@
|
||||
/* 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_exec_c[] = "$Id$";
|
||||
#endif
|
||||
|
||||
#include <zephyr/mit-copyright.h>
|
||||
|
||||
/****************************************************************************/
|
||||
/* */
|
||||
/* Module containing code to execute a program: */
|
||||
/* */
|
||||
/****************************************************************************/
|
||||
|
||||
#include <zephyr/zephyr.h>
|
||||
#include "new_memory.h"
|
||||
#include "node.h"
|
||||
#include "exec.h"
|
||||
#include "eval.h"
|
||||
#include "buffer.h"
|
||||
#include "port.h"
|
||||
#include "variables.h"
|
||||
#include "notice.h"
|
||||
#ifdef CMU_ZWGCPLUS
|
||||
#include "plus.h"
|
||||
#endif
|
||||
|
||||
static int exec_subtree(Node *);
|
||||
static int exec_fields(Node *);
|
||||
|
||||
/****************************************************************************/
|
||||
/* */
|
||||
/* Utility subroutines: */
|
||||
/* */
|
||||
/****************************************************************************/
|
||||
|
||||
static string
|
||||
eval_exprlist_to_string(Node *exprlist)
|
||||
{
|
||||
string result = string_Copy("");
|
||||
string temp;
|
||||
int first_time = 1;
|
||||
|
||||
for (; exprlist; exprlist=exprlist->next) {
|
||||
if (!first_time)
|
||||
result = string_Concat2(result, " ");
|
||||
else
|
||||
first_time = 0;
|
||||
|
||||
temp = eval_expr(exprlist);
|
||||
result = string_Concat2(result, temp);
|
||||
free(temp);
|
||||
}
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
static char **
|
||||
eval_exprlist_to_args(Node *exprlist)
|
||||
{
|
||||
char **result = (char **)malloc(sizeof(char *));
|
||||
int argc = 0;
|
||||
|
||||
for (; exprlist; exprlist=exprlist->next) {
|
||||
result[argc] = eval_expr(exprlist);
|
||||
argc++;
|
||||
result = (char **)realloc(result, (argc+1)*sizeof(char *));
|
||||
}
|
||||
|
||||
result[argc] = NULL;
|
||||
return(result);
|
||||
}
|
||||
|
||||
static void
|
||||
free_args(char **args)
|
||||
{
|
||||
char **p;
|
||||
|
||||
for (p=args; *p; p++) {
|
||||
free(*p);
|
||||
}
|
||||
|
||||
free(args);
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
/* */
|
||||
/* Subroutines to handle each particular statement type: */
|
||||
/* */
|
||||
/****************************************************************************/
|
||||
|
||||
#define NOBREAK 0
|
||||
#define BREAK 1
|
||||
#define EXIT 2
|
||||
|
||||
/*ARGSUSED*/
|
||||
static int
|
||||
exec_noop(Node *node)
|
||||
{
|
||||
return(NOBREAK);
|
||||
}
|
||||
|
||||
/*ARGSUSED*/
|
||||
static int
|
||||
exec_break(Node *node)
|
||||
{
|
||||
return(BREAK);
|
||||
}
|
||||
|
||||
/*ARGSUSED*/
|
||||
static int
|
||||
exec_exit(Node *node)
|
||||
{
|
||||
return(EXIT);
|
||||
}
|
||||
|
||||
static int
|
||||
exec_set(Node *node)
|
||||
{
|
||||
var_set_variable_then_free_value(node->d.nodes.first->d.string_constant,
|
||||
eval_expr(node->d.nodes.second));
|
||||
|
||||
return(NOBREAK);
|
||||
}
|
||||
|
||||
static int
|
||||
exec_execport(Node *node)
|
||||
{
|
||||
string name = eval_expr(node->d.nodes.first);
|
||||
char **argv = eval_exprlist_to_args(node->d.nodes.second);
|
||||
|
||||
create_subprocess_port(name, argv);
|
||||
|
||||
free(name);
|
||||
free_args(argv);
|
||||
return(NOBREAK);
|
||||
}
|
||||
|
||||
static int
|
||||
exec_appendport(Node *node)
|
||||
{
|
||||
string name, filename;
|
||||
|
||||
name = eval_expr(node->d.nodes.first);
|
||||
filename = eval_expr(node->d.nodes.second);
|
||||
|
||||
create_file_append_port(name, filename);
|
||||
|
||||
free(name);
|
||||
free(filename);
|
||||
return(NOBREAK);
|
||||
}
|
||||
|
||||
static int
|
||||
exec_inputport(Node *node)
|
||||
{
|
||||
string name, filename;
|
||||
|
||||
name = eval_expr(node->d.nodes.first);
|
||||
filename = eval_expr(node->d.nodes.second);
|
||||
|
||||
create_file_input_port(name, filename);
|
||||
|
||||
free(name);
|
||||
free(filename);
|
||||
return(NOBREAK);
|
||||
}
|
||||
|
||||
static int
|
||||
exec_outputport(Node *node)
|
||||
{
|
||||
string name, filename;
|
||||
|
||||
name = eval_expr(node->d.nodes.first);
|
||||
filename = eval_expr(node->d.nodes.second);
|
||||
|
||||
create_file_output_port(name, filename);
|
||||
|
||||
free(name);
|
||||
free(filename);
|
||||
return(NOBREAK);
|
||||
}
|
||||
|
||||
static int
|
||||
exec_closeinput(Node *node)
|
||||
{
|
||||
string name;
|
||||
|
||||
name = eval_expr(node->d.nodes.first);
|
||||
close_port_input(name);
|
||||
|
||||
free(name);
|
||||
return(NOBREAK);
|
||||
}
|
||||
|
||||
static int
|
||||
exec_closeoutput(Node *node)
|
||||
{
|
||||
string name;
|
||||
|
||||
name = eval_expr(node->d.nodes.first);
|
||||
close_port_output(name);
|
||||
|
||||
free(name);
|
||||
return(NOBREAK);
|
||||
}
|
||||
|
||||
static int
|
||||
exec_closeport(Node *node)
|
||||
{
|
||||
string name;
|
||||
|
||||
name = eval_expr(node->d.nodes.first);
|
||||
close_port_input(name);
|
||||
close_port_output(name);
|
||||
|
||||
free(name);
|
||||
return(NOBREAK);
|
||||
}
|
||||
|
||||
static int
|
||||
exec_put(Node *node)
|
||||
{
|
||||
string name, temp;
|
||||
|
||||
if (node->d.nodes.second)
|
||||
temp = eval_exprlist_to_string(node->d.nodes.second);
|
||||
else
|
||||
temp = string_Copy(buffer_to_string());
|
||||
|
||||
if (node->d.nodes.first) {
|
||||
name = eval_expr(node->d.nodes.first);
|
||||
|
||||
write_on_port(name, temp, strlen(temp));
|
||||
free(name);
|
||||
} else
|
||||
write_on_port(var_get_variable("output_driver"), temp, strlen(temp));
|
||||
|
||||
free(temp);
|
||||
return(NOBREAK);
|
||||
}
|
||||
|
||||
static int
|
||||
exec_print(Node *node)
|
||||
{
|
||||
string temp;
|
||||
|
||||
temp = eval_exprlist_to_string(node->d.nodes.first);
|
||||
append_buffer(temp);
|
||||
free(temp);
|
||||
|
||||
return(NOBREAK);
|
||||
}
|
||||
|
||||
/*ARGSUSED*/
|
||||
static int
|
||||
exec_clearbuf(Node *node)
|
||||
{
|
||||
clear_buffer();
|
||||
|
||||
return(NOBREAK);
|
||||
}
|
||||
|
||||
static int
|
||||
exec_case(Node *node)
|
||||
{
|
||||
string constant,temp;
|
||||
Node *match, *cond;
|
||||
int equal_p;
|
||||
|
||||
constant = string_Downcase(eval_expr(node->d.nodes.first));
|
||||
|
||||
for (match=node->d.nodes.second; match; match=match->next) {
|
||||
cond = match->d.nodes.first;
|
||||
if (!cond) { /* default case */
|
||||
free(constant);
|
||||
return(exec_subtree(match->d.nodes.second));
|
||||
}
|
||||
for (; cond; cond=cond->next) {
|
||||
temp = string_Downcase(eval_expr(cond));
|
||||
equal_p = string_Eq(constant, temp);
|
||||
free(temp);
|
||||
if (equal_p) {
|
||||
free(constant);
|
||||
return(exec_subtree(match->d.nodes.second));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(constant);
|
||||
return(NOBREAK);
|
||||
}
|
||||
|
||||
static int
|
||||
exec_while(Node *node)
|
||||
{
|
||||
int continue_code = NOBREAK;
|
||||
|
||||
while (eval_bool_expr(node->d.nodes.first)) {
|
||||
continue_code = exec_subtree(node->d.nodes.second);
|
||||
if (continue_code != NOBREAK)
|
||||
break;
|
||||
}
|
||||
|
||||
if (continue_code == BREAK)
|
||||
continue_code = NOBREAK;
|
||||
|
||||
return(continue_code);
|
||||
}
|
||||
|
||||
static int
|
||||
exec_if(Node *node)
|
||||
{
|
||||
Node *conds;
|
||||
|
||||
for (conds=node->d.nodes.first; conds; conds=conds->next)
|
||||
if (eval_bool_expr(conds->d.nodes.first))
|
||||
return(exec_subtree(conds->d.nodes.second));
|
||||
|
||||
return(NOBREAK);
|
||||
}
|
||||
|
||||
static int
|
||||
exec_exec(Node *node)
|
||||
{
|
||||
int pid;
|
||||
char **argv = eval_exprlist_to_args(node->d.nodes.first);
|
||||
|
||||
pid = fork();
|
||||
if (pid == -1) {
|
||||
fprintf(stderr, "zwgc: error while attempting to fork: ");
|
||||
perror("");
|
||||
} else if (pid == 0) { /* in child */
|
||||
execvp(argv[0], argv);
|
||||
fprintf(stderr,"zwgc: unable to exec %s: ", argv[0]);
|
||||
perror("");
|
||||
_exit(errno);
|
||||
}
|
||||
|
||||
free_args(argv);
|
||||
return(NOBREAK);
|
||||
}
|
||||
|
||||
static struct _Opstuff {
|
||||
int (*exec)(Node *);
|
||||
} const opstuff[] = {
|
||||
{ exec_noop }, /* string_constant */
|
||||
{ exec_noop }, /* varref */
|
||||
{ exec_noop }, /* varname */
|
||||
{ exec_noop }, /* not */
|
||||
{ exec_noop }, /* plus */
|
||||
{ exec_noop }, /* and */
|
||||
{ exec_noop }, /* or */
|
||||
{ exec_noop }, /* eq */
|
||||
{ exec_noop }, /* neq */
|
||||
{ exec_noop }, /* regeq */
|
||||
{ exec_noop }, /* regneq */
|
||||
{ exec_noop }, /* buffer */
|
||||
{ exec_noop }, /* substitute */
|
||||
{ exec_noop }, /* protect */
|
||||
{ exec_noop }, /* verbatim */
|
||||
{ exec_noop }, /* stylestrip */
|
||||
{ exec_noop }, /* getenv */
|
||||
{ exec_noop }, /* upcase */
|
||||
{ exec_noop }, /* downcase */
|
||||
{ exec_noop }, /* zvar */
|
||||
{ exec_noop }, /* get */
|
||||
{ exec_noop }, /* lany */
|
||||
{ exec_noop }, /* rany */
|
||||
{ exec_noop }, /* lbreak */
|
||||
{ exec_noop }, /* rbreak */
|
||||
{ exec_noop }, /* lspan */
|
||||
{ exec_noop }, /* rspan */
|
||||
|
||||
{ exec_noop }, /* noop statement */
|
||||
{ exec_set },
|
||||
{ exec_fields },
|
||||
|
||||
{ exec_print },
|
||||
{ exec_clearbuf },
|
||||
|
||||
{ exec_appendport },
|
||||
{ exec_execport },
|
||||
{ exec_inputport },
|
||||
{ exec_outputport },
|
||||
{ exec_put },
|
||||
{ exec_closeinput },
|
||||
{ exec_closeoutput },
|
||||
{ exec_closeport },
|
||||
|
||||
{ exec_exec },
|
||||
|
||||
{ exec_if },
|
||||
{ exec_case },
|
||||
{ exec_while },
|
||||
{ exec_break },
|
||||
{ exec_exit },
|
||||
|
||||
{ exec_noop }, /* if */
|
||||
{ exec_noop }, /* elseif */
|
||||
{ exec_noop }, /* else */
|
||||
{ exec_noop }, /* matchlist */
|
||||
{ exec_noop }, /* default */
|
||||
};
|
||||
|
||||
static int
|
||||
exec_subtree(Node *node)
|
||||
{
|
||||
int retval = NOBREAK;
|
||||
|
||||
for (; node; node=node->next) {
|
||||
retval = (opstuff[node->opcode].exec)(node);
|
||||
if (retval != NOBREAK)
|
||||
return(retval);
|
||||
}
|
||||
|
||||
return(NOBREAK);
|
||||
}
|
||||
|
||||
/***************************************************************************/
|
||||
|
||||
static char *notice_fields;
|
||||
static int notice_fields_length = 0;
|
||||
static int number_of_fields = 0;
|
||||
|
||||
static int
|
||||
exec_fields(Node *node)
|
||||
{
|
||||
for (node=node->d.nodes.first; node; node=node->next) {
|
||||
var_set_variable_then_free_value(node->d.string_constant,
|
||||
get_next_field(¬ice_fields,
|
||||
¬ice_fields_length));
|
||||
if (number_of_fields)
|
||||
number_of_fields--;
|
||||
}
|
||||
|
||||
var_set_variable_to_number("number_of_fields", number_of_fields);
|
||||
|
||||
return(NOBREAK);
|
||||
}
|
||||
|
||||
void
|
||||
exec_process_packet(Node *program,
|
||||
ZNotice_t *notice)
|
||||
{
|
||||
#ifdef CMU_ZWGCPLUS
|
||||
set_stored_notice(notice);
|
||||
#endif
|
||||
|
||||
notice_fields = notice->z_message;
|
||||
notice_fields_length = notice->z_message_len;
|
||||
|
||||
var_set_number_variables_to_fields(notice_fields, notice_fields_length);
|
||||
|
||||
number_of_fields = count_nulls(notice_fields, notice_fields_length)+1;
|
||||
/* workaround for bug in old zwrite */
|
||||
if (notice_fields[notice_fields_length-1] == '\0')
|
||||
number_of_fields--;
|
||||
var_set_variable_to_number("number_of_fields", number_of_fields);
|
||||
|
||||
clear_buffer();
|
||||
(void)exec_subtree(program);
|
||||
|
||||
#ifdef CMU_ZWGCPLUS
|
||||
plus_queue_notice(notice);
|
||||
plus_window_deletions(notice); /* OOPS */
|
||||
set_stored_notice(NULL);
|
||||
#endif
|
||||
}
|
22
zwgc/exec.h
Normal file
22
zwgc/exec.h
Normal file
@ -0,0 +1,22 @@
|
||||
/* 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 <zephyr/mit-copyright.h>
|
||||
|
||||
#ifndef exec_MODULE
|
||||
#define exec_MODULE
|
||||
|
||||
extern void exec_process_packet(Node *, ZNotice_t *);
|
||||
|
||||
#endif
|
119
zwgc/file.c
Normal file
119
zwgc/file.c
Normal file
@ -0,0 +1,119 @@
|
||||
/* 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_file_c[] = "$Id$";
|
||||
#endif
|
||||
|
||||
#include <zephyr/mit-copyright.h>
|
||||
|
||||
#include <pwd.h>
|
||||
#include "new_memory.h"
|
||||
#include "new_string.h"
|
||||
#include "error.h"
|
||||
#include "file.h"
|
||||
|
||||
/*
|
||||
* char *get_home_directory()
|
||||
*
|
||||
* Effects: Attempts to locate the user's (by user, the owner of this
|
||||
* process is meant) home directory & return its pathname.
|
||||
* Returns NULL if unable to do so. Does so by first checking
|
||||
* the environment variable HOME. If it is unset, falls back
|
||||
* on the user's password entry.
|
||||
* Note: The returned pointer may point to a static buffer & hence
|
||||
* go away on further calls to getenv, get_home_directory,
|
||||
* getpwuid, or related calls. The caller should copy it
|
||||
* if necessary.
|
||||
*/
|
||||
|
||||
static char
|
||||
*get_home_directory(void)
|
||||
{
|
||||
char *result;
|
||||
struct passwd *passwd_entry;
|
||||
|
||||
result = getenv("HOME");
|
||||
if (result)
|
||||
return(result);
|
||||
|
||||
if (!(passwd_entry = getpwuid(getuid())))
|
||||
return(NULL);
|
||||
|
||||
return(passwd_entry->pw_dir);
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
FILE *locate_file(char *override_filename,
|
||||
char *home_dir_filename,
|
||||
char *fallback_filename)
|
||||
{
|
||||
char *filename;
|
||||
FILE *result;
|
||||
|
||||
errno = 0;
|
||||
|
||||
if (override_filename) {
|
||||
if (string_Eq(override_filename, "-"))
|
||||
return(stdin);
|
||||
|
||||
result = fopen(override_filename, "r");
|
||||
if (!(result = fopen(override_filename, "r"))) {
|
||||
/* <<<>>> */
|
||||
fprintf(stderr, "zwgc: error while opening %s for reading: ",
|
||||
override_filename);
|
||||
perror("");
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
if (home_dir_filename) {
|
||||
filename = get_home_directory();
|
||||
if (filename) {
|
||||
filename = string_Concat(filename, "/");
|
||||
filename = string_Concat2(filename, home_dir_filename);
|
||||
result = fopen(filename, "r");
|
||||
if (result) {
|
||||
free(filename);
|
||||
return(result);
|
||||
}
|
||||
if (errno != ENOENT) {
|
||||
/* <<<>>> */
|
||||
fprintf(stderr, "zwgc: error while opening %s for reading: ",
|
||||
filename);
|
||||
perror("");
|
||||
free(filename);
|
||||
return(result);
|
||||
}
|
||||
free(filename);
|
||||
} else
|
||||
ERROR("unable to find your home directory.\n");
|
||||
}
|
||||
|
||||
if (fallback_filename) {
|
||||
if (!(result = fopen(fallback_filename, "r"))) {
|
||||
/* <<<>>> */
|
||||
fprintf(stderr, "zwgc: error while opening %s for reading: ",
|
||||
fallback_filename);
|
||||
perror("");
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
return(NULL);
|
||||
}
|
24
zwgc/file.h
Normal file
24
zwgc/file.h
Normal file
@ -0,0 +1,24 @@
|
||||
/* 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 <zephyr/mit-copyright.h>
|
||||
|
||||
#ifndef file_MODULE
|
||||
#define file_MODULE
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
extern FILE *locate_file(char *, char *, char *);
|
||||
|
||||
#endif
|
562
zwgc/formatter.c
Normal file
562
zwgc/formatter.c
Normal file
@ -0,0 +1,562 @@
|
||||
/* 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_formatter_c[] = "$Id$";
|
||||
#endif
|
||||
|
||||
#include <zephyr/mit-copyright.h>
|
||||
#include <zephyr/zephyr.h>
|
||||
|
||||
#include "new_memory.h"
|
||||
#include "char_stack.h"
|
||||
#include "string_dictionary.h"
|
||||
#include "formatter.h"
|
||||
#include "text_operations.h"
|
||||
|
||||
#if !defined(__STDC__) && !defined(const)
|
||||
#define const
|
||||
#endif
|
||||
|
||||
static int pure_text_length(char *, char);
|
||||
static int env_length(char *);
|
||||
|
||||
#ifdef notdef
|
||||
static character_class atsign_set = { /* '@' = 0x40 */
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||
};
|
||||
#endif
|
||||
|
||||
static const character_class paren_set = { /* '(' = 0x28, ')' = 0x29 */
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||
};
|
||||
|
||||
static const character_class sbracket_set = { /* '[' = 0x5b, ']' = 0x5d */
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||
};
|
||||
|
||||
static const character_class abracket_set = { /* '<' = 0x3c, '>' = 0x3e */
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||
};
|
||||
|
||||
static const character_class cbracket_set = { /* '{' = 0x7b, '}' = 0x7d */
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||
};
|
||||
|
||||
static const character_class allbracket_set = {
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||
};
|
||||
|
||||
static const character_class allmaskable_set = {
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,
|
||||
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||
};
|
||||
|
||||
static char brackets[]="()<>[]{}@";
|
||||
static char *openbracket[]={"@<","@<","@[","@[","@{","@{","@(","@(","@("};
|
||||
static char *closebracket[]={">",">","]","]","}","}",")",")",")"};
|
||||
|
||||
static int
|
||||
not_contains(string str,
|
||||
const character_class set)
|
||||
{
|
||||
while (*str && ! set[(int)*str]) str++;
|
||||
return (! *str);
|
||||
}
|
||||
|
||||
static int
|
||||
pure_text_length(char *text,
|
||||
char terminator)
|
||||
{
|
||||
int len=0;
|
||||
|
||||
while (1) {
|
||||
while (*text!='@' && *text!=terminator && *text) {
|
||||
text++;
|
||||
len++;
|
||||
}
|
||||
|
||||
if (*text!='@')
|
||||
return(len);
|
||||
|
||||
if (*(text+1)=='@') {
|
||||
text++;
|
||||
len++;
|
||||
} else if (env_length(text+1) != -1)
|
||||
return(len);
|
||||
|
||||
text++;
|
||||
len++;
|
||||
}
|
||||
}
|
||||
|
||||
static char
|
||||
otherside(char opener)
|
||||
{
|
||||
switch (opener) {
|
||||
case '(':
|
||||
return(')');
|
||||
case '{':
|
||||
return('}');
|
||||
case '[':
|
||||
return(']');
|
||||
case '<':
|
||||
return('>');
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
abort();
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* the char * that str points to is free'd by this function.
|
||||
* if you want to keep it, save it yourself
|
||||
*/
|
||||
string
|
||||
verbatim(string str, int bracketsonly)
|
||||
{
|
||||
char *temp,*temp2;
|
||||
int bracketnum,len;
|
||||
|
||||
len = strlen(str);
|
||||
if (len == pure_text_length(str,0)) {
|
||||
/* No environments, so consider the fast-and-easy methods */
|
||||
|
||||
if (not_contains(str,allbracket_set)) {
|
||||
temp = string_Copy(str);
|
||||
free(str);
|
||||
return(temp);
|
||||
}
|
||||
|
||||
if (not_contains(str,abracket_set)) {
|
||||
temp=(char *) malloc((len=strlen(str))+4);
|
||||
temp[0]='@';
|
||||
temp[1]='<';
|
||||
(void) memcpy(temp+2,str,len);
|
||||
temp[len+2]='>';
|
||||
temp[len+3]='\0';
|
||||
free(str);
|
||||
return(temp);
|
||||
}
|
||||
if (not_contains(str,sbracket_set)) {
|
||||
temp=(char *) malloc((len=strlen(str))+4);
|
||||
temp[0]='@';
|
||||
temp[1]='[';
|
||||
(void) memcpy(temp+2,str,len);
|
||||
temp[len+2]=']';
|
||||
temp[len+3]='\0';
|
||||
free(str);
|
||||
return(temp);
|
||||
}
|
||||
if (not_contains(str,cbracket_set)) {
|
||||
temp=(char *) malloc((len=strlen(str))+4);
|
||||
temp[0]='@';
|
||||
temp[1]='{';
|
||||
(void) memcpy(temp+2,str,len);
|
||||
temp[len+2]='}';
|
||||
temp[len+3]='\0';
|
||||
free(str);
|
||||
return(temp);
|
||||
}
|
||||
if (not_contains(str,paren_set)) {
|
||||
temp=(char *) malloc((len=strlen(str))+4);
|
||||
temp[0]='@';
|
||||
temp[1]='(';
|
||||
(void) memcpy(temp+2,str,len);
|
||||
temp[len+2]=')';
|
||||
temp[len+3]='\0';
|
||||
free(str);
|
||||
return(temp);
|
||||
}
|
||||
}
|
||||
|
||||
temp=lbreak(&str,bracketsonly?allbracket_set:allmaskable_set);
|
||||
while(*str) {
|
||||
bracketnum=(int) (strchr(brackets,str[0])-brackets);
|
||||
temp=string_Concat2(temp,openbracket[bracketnum]);
|
||||
temp=string_Concat2(temp,temp2=lany(&str," "));
|
||||
free(temp2);
|
||||
temp=string_Concat2(temp,closebracket[bracketnum]);
|
||||
temp=string_Concat2(temp,temp2=lbreak(&str,bracketsonly?
|
||||
allbracket_set:allmaskable_set));
|
||||
free(temp2);
|
||||
}
|
||||
free(str); /* str is "" at this point, anyway */
|
||||
|
||||
return(temp);
|
||||
}
|
||||
|
||||
/* text points to beginning of text string. return value is
|
||||
length of string, up to but not including the passed terminator
|
||||
or the default terminator \0. The text will not be modified,
|
||||
and @@ will be counted twice */
|
||||
|
||||
string
|
||||
protect(string str)
|
||||
{
|
||||
string temp,temp2,temp3;
|
||||
int len,templen;
|
||||
char_stack chs;
|
||||
char tos;
|
||||
|
||||
temp = string_Copy("");
|
||||
templen = 1;
|
||||
chs = char_stack_create();
|
||||
|
||||
while(*str) {
|
||||
tos = (char_stack_empty(chs)?0:char_stack_top(chs));
|
||||
|
||||
if (*str == tos) {
|
||||
/* if the character is the next terminator */
|
||||
|
||||
temp = (char *) realloc(temp,++templen);
|
||||
temp[templen-2] = *str++;
|
||||
char_stack_pop(chs);
|
||||
temp[templen-1] = '\0';
|
||||
} else if ((len = pure_text_length(str,tos))) {
|
||||
if (tos) {
|
||||
/* if the block is text in an environment, just copy it */
|
||||
|
||||
temp2 = string_CreateFromData(str,len);
|
||||
str += len;
|
||||
temp = string_Concat2(temp,temp2);
|
||||
templen += len;
|
||||
free(temp2);
|
||||
} else {
|
||||
/* if the block is top level text, verbatim brackets only
|
||||
(not @'s) and add text to temp */
|
||||
|
||||
temp2 = string_CreateFromData(str,len);
|
||||
str += len;
|
||||
temp3 = verbatim(temp2,1);
|
||||
temp = string_Concat2(temp,temp3);
|
||||
templen += strlen(temp3);
|
||||
free(temp3);
|
||||
}
|
||||
} else {
|
||||
/* if the block is an environment, copy it, push delimiter */
|
||||
|
||||
len = env_length(str+1);
|
||||
char_stack_push(chs,otherside(str[len+1]));
|
||||
len += 2;
|
||||
temp2 = string_CreateFromData(str,len);
|
||||
str += len;
|
||||
temp = string_Concat2(temp,temp2);
|
||||
templen += len;
|
||||
free(temp2);
|
||||
}
|
||||
}
|
||||
/* all blocks have been copied. */
|
||||
|
||||
while (!char_stack_empty(chs)) {
|
||||
temp = (char *) realloc(temp,++templen);
|
||||
temp[templen-2] = char_stack_top(chs);
|
||||
char_stack_pop(chs);
|
||||
}
|
||||
temp[templen-1] = '\0';
|
||||
|
||||
return(temp);
|
||||
}
|
||||
|
||||
/* str points to a string. return value is another string
|
||||
which is the original with all styles removed. */
|
||||
string
|
||||
stylestrip(string str)
|
||||
{
|
||||
int templen = 0, otherchar;
|
||||
char *temp = (char *) malloc(string_Length(str) + 1);
|
||||
char_stack chs;
|
||||
string ostr = str;
|
||||
|
||||
chs = char_stack_create();
|
||||
|
||||
while (*str) {
|
||||
if (*str == '@') {
|
||||
int len = env_length(str + 1);
|
||||
if (len != -1) {
|
||||
otherchar = 0;
|
||||
if ((len == 4 && !strncasecmp(str + 1, "font", 4))
|
||||
|| (len == 5 && !strncasecmp(str + 1, "color", 5)))
|
||||
otherchar = 0x80;
|
||||
otherchar |= otherside(str[len + 1]);
|
||||
char_stack_push(chs, otherchar);
|
||||
str += len + 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!char_stack_empty(chs) && *str == (char_stack_top(chs) & 0x7f)) {
|
||||
char_stack_pop(chs);
|
||||
str++;
|
||||
continue;
|
||||
}
|
||||
if (!char_stack_empty(chs) && (char_stack_top(chs) & 0x80))
|
||||
str++;
|
||||
else
|
||||
temp[templen++] = *str++;
|
||||
}
|
||||
temp[templen] = 0;
|
||||
|
||||
while (!char_stack_empty(chs))
|
||||
char_stack_pop(chs);
|
||||
free(ostr);
|
||||
|
||||
return(temp);
|
||||
}
|
||||
|
||||
void
|
||||
free_desc(desctype *desc)
|
||||
{
|
||||
desctype *next_desc;
|
||||
|
||||
while (desc->code != DT_EOF) {
|
||||
next_desc = desc->next;
|
||||
free(desc);
|
||||
desc = next_desc;
|
||||
}
|
||||
free(desc);
|
||||
}
|
||||
|
||||
/* text points to beginning of possible env name. return value is
|
||||
length of env name, not including @ or opener, or -1 if not a
|
||||
possible env name. */
|
||||
static int
|
||||
env_length(char *text)
|
||||
{
|
||||
int len=0;
|
||||
|
||||
while (*text && (isalnum(*text) || *text=='_')) {
|
||||
text++;
|
||||
len++;
|
||||
}
|
||||
|
||||
if ((*text=='(') || (*text=='{') || (*text=='[') || (*text=='<'))
|
||||
return(len);
|
||||
else
|
||||
return(-1);
|
||||
}
|
||||
|
||||
/* text points to beginning of text string. return value is
|
||||
length of string, up to but not including the passed terminator
|
||||
or the default terminators \0 \n @. This can modify text, and 0
|
||||
is a valid return value. */
|
||||
static int
|
||||
text_length(char *text,
|
||||
char terminator)
|
||||
{
|
||||
int len=0;
|
||||
|
||||
while (1) {
|
||||
while (*text!='@' && *text!='\n' && *text!=terminator && *text) {
|
||||
text++;
|
||||
len++;
|
||||
}
|
||||
|
||||
if (*text!='@')
|
||||
return(len);
|
||||
|
||||
if (*(text+1)=='@')
|
||||
(void) memmove(text+1,text+2,strlen(text+1));
|
||||
else if (env_length(text+1) != -1)
|
||||
return(len);
|
||||
|
||||
text++;
|
||||
len++;
|
||||
}
|
||||
}
|
||||
|
||||
/* parses str into a desc linked list. Returns number of strings and
|
||||
newlines in *pstr and *pnl */
|
||||
|
||||
desctype *
|
||||
disp_get_cmds(char *str,
|
||||
int *pstr,
|
||||
int *pnl)
|
||||
{
|
||||
desctype *desc,*here;
|
||||
int len;
|
||||
char_stack terminators = char_stack_create();
|
||||
char terminator;
|
||||
int nstr=0, nnl=0;
|
||||
char *curstr;
|
||||
|
||||
desc=(desctype *) malloc(sizeof(desctype));
|
||||
here=desc;
|
||||
curstr=str;
|
||||
terminator = '\0';
|
||||
|
||||
while (*curstr) {
|
||||
if (*curstr=='\n') {
|
||||
here->code=DT_NL;
|
||||
curstr++;
|
||||
nnl++;
|
||||
} else if (*curstr==terminator) { /* if this is the end of an env */
|
||||
here->code=DT_END;
|
||||
terminator = char_stack_top(terminators);
|
||||
char_stack_pop(terminators);
|
||||
curstr++;
|
||||
} else if ((len=text_length(curstr, terminator))) { /* if there is a text
|
||||
block here */
|
||||
here->code=DT_STR;
|
||||
here->str=curstr;
|
||||
here->len=len;
|
||||
curstr+=len;
|
||||
nstr++;
|
||||
} else if (*curstr=='@') { /* if this is the beginning of an env */
|
||||
len=env_length(curstr+1);
|
||||
here->code=DT_ENV;
|
||||
here->str=curstr+1;
|
||||
here->len=len;
|
||||
char_stack_push(terminators, terminator);
|
||||
terminator=otherside(*(curstr+1+len));
|
||||
curstr+=(len+2); /* jump over @, env name, and opener */
|
||||
}
|
||||
|
||||
here->next=(desctype *) malloc(sizeof(desctype));
|
||||
here=here->next;
|
||||
}
|
||||
|
||||
while (!char_stack_empty(terminators)) {
|
||||
here->code=DT_END;
|
||||
terminator = char_stack_top(terminators);
|
||||
char_stack_pop(terminators);
|
||||
here->next=(desctype *) malloc(sizeof(desctype));
|
||||
here=here->next;
|
||||
}
|
||||
here->code=DT_EOF;
|
||||
*pstr=nstr;
|
||||
*pnl=nnl;
|
||||
|
||||
#ifdef DEBUG_PRINTOUT
|
||||
{ string temp;
|
||||
here = desc;
|
||||
while (here->code != DT_EOF) {
|
||||
if (here->code == DT_STR || here->code == DT_ENV) {
|
||||
temp = string_CreateFromData(here->str, here->len);
|
||||
printf("[%d <%s>]\n", here->code, temp);
|
||||
free(temp);
|
||||
} else
|
||||
printf("[%d]\n", here->code);
|
||||
here=here->next;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return(desc);
|
||||
}
|
43
zwgc/formatter.h
Normal file
43
zwgc/formatter.h
Normal file
@ -0,0 +1,43 @@
|
||||
/* 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 <zephyr/mit-copyright.h>
|
||||
|
||||
#include "new_string.h"
|
||||
|
||||
#ifndef formatter_MODULE
|
||||
#define formatter_MODULE
|
||||
|
||||
typedef struct _desctype {
|
||||
struct _desctype *next;
|
||||
|
||||
short int code;
|
||||
#define DT_EOF 0 /* End of message. */
|
||||
#define DT_ENV 1 /* Open environment. */
|
||||
#define DT_STR 2 /* Display string. */
|
||||
#define DT_END 3 /* Close environment. */
|
||||
#define DT_NL 4 /* Newline. */
|
||||
|
||||
char *str; /* Name of environment, string to be displayed. */
|
||||
int len; /* Length of string/environment name for
|
||||
ENV, STR, END. Undefined for EOF */
|
||||
} desctype;
|
||||
|
||||
extern desctype *disp_get_cmds(char *, int *, int *);
|
||||
extern void free_desc(desctype *);
|
||||
|
||||
extern string protect(string);
|
||||
extern string verbatim(string, int);
|
||||
extern string stylestrip(string);
|
||||
#endif
|
39
zwgc/instantiate
Executable file
39
zwgc/instantiate
Executable file
@ -0,0 +1,39 @@
|
||||
#!/bin/sh -
|
||||
|
||||
# This file is part of the Project Athena Zephyr Notification System.
|
||||
# It is one of the source files comprising zwgc, the Zephyr WindowGram
|
||||
# client.
|
||||
#
|
||||
# $Id$
|
||||
#
|
||||
# Copyright (c) 1989,1993 by the Massachusetts Institute of Technology.
|
||||
# For copying and distribution information, see the file
|
||||
# "mit-copyright.h".
|
||||
#
|
||||
|
||||
if [ "$1" = "" ]; then
|
||||
echo "Usage: generate_instance <srcdir> <type> <name> [<include file>]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
source=$1
|
||||
type=$2
|
||||
name=$3
|
||||
incfile=$4
|
||||
|
||||
if [ "$type" != "stack" ]; then
|
||||
if [ ! -f ${source}/${type}.c ]; then
|
||||
echo "$0: unable to open ${source}/${type}.c"
|
||||
exit 2
|
||||
fi
|
||||
sed "s/TYPE_T/$name/g" ${source}/${type}.c > ${name}_${type}.c
|
||||
fi
|
||||
|
||||
if [ "$incfile" != "" ]; then
|
||||
echo "#include \"$incfile\"" > ${name}_${type}.h
|
||||
fi
|
||||
if [ ! -f ${source}/${type}.h ]; then
|
||||
echo "$0: unable to open ${source}/${type}.h"
|
||||
exit 2
|
||||
fi
|
||||
sed "s/TYPE_T/$name/g" ${source}/${type}.h >> ${name}_${type}.h
|
673
zwgc/lexer.c
Normal file
673
zwgc/lexer.c
Normal file
@ -0,0 +1,673 @@
|
||||
/* 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_lexer_c[] = "$Id$";
|
||||
#endif
|
||||
|
||||
#include <zephyr/mit-copyright.h>
|
||||
|
||||
/****************************************************************************/
|
||||
/* */
|
||||
/* The lexer for the zwgc description language: */
|
||||
/* */
|
||||
/****************************************************************************/
|
||||
|
||||
#include "new_memory.h"
|
||||
#include "new_string.h"
|
||||
#include "int_dictionary.h"
|
||||
#include "lexer.h"
|
||||
#include "parser.h"
|
||||
#include "y.tab.h"
|
||||
|
||||
/*
|
||||
* yylineno - this holds the current line # we are on. Updated automatically
|
||||
* by input() and unput().
|
||||
*/
|
||||
|
||||
int yylineno;
|
||||
|
||||
/*
|
||||
* keyword_dict - this dictionary maps keyword names to their token numbers.
|
||||
*/
|
||||
|
||||
static int_dictionary keyword_dict = NULL;
|
||||
|
||||
/****************************************************************************/
|
||||
/* */
|
||||
/* I/O functions: */
|
||||
/* */
|
||||
/****************************************************************************/
|
||||
|
||||
/*
|
||||
* input_file - this holds the FILE pointer to the file currently being lexed.
|
||||
*/
|
||||
|
||||
static FILE *input_file;
|
||||
|
||||
/*
|
||||
* pushback - if not -1, holds a character that was pushed back by unput but
|
||||
* not yet read by input.
|
||||
*/
|
||||
|
||||
static int pushback = -1;
|
||||
|
||||
static char
|
||||
input(void)
|
||||
{
|
||||
int c;
|
||||
|
||||
if (pushback != -1) {
|
||||
c = pushback;
|
||||
pushback = -1;
|
||||
if (c=='\n')
|
||||
yylineno++;
|
||||
return(c);
|
||||
}
|
||||
|
||||
c = getc(input_file);
|
||||
if (c=='\n')
|
||||
yylineno++;
|
||||
if (c==EOF)
|
||||
c = 0;
|
||||
|
||||
return(c);
|
||||
}
|
||||
|
||||
static void
|
||||
unput(int c)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
if (pushback != -1) {
|
||||
printf("Attempt to push back 2 characters at one time!\n");
|
||||
exit(1);
|
||||
}
|
||||
#endif
|
||||
|
||||
pushback = c;
|
||||
if (c == '\n')
|
||||
yylineno--;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
/* */
|
||||
/* Initialization routines: */
|
||||
/* */
|
||||
/****************************************************************************/
|
||||
|
||||
struct keyword_info {
|
||||
string keyword;
|
||||
int keyword_number;
|
||||
};
|
||||
|
||||
/*
|
||||
* keywords - This table holds a copy of the mapping from keyword name to
|
||||
* token number and is used to initialize keyword_dict:
|
||||
*/
|
||||
|
||||
static struct keyword_info keywords[] = {
|
||||
{ "and", '&' },
|
||||
{ "appendport", APPENDPORT },
|
||||
{ "buffer", BUFFER },
|
||||
{ "break", BREAK },
|
||||
{ "closeinput", CLOSEINPUT },
|
||||
{ "closeoutput", CLOSEOUTPUT },
|
||||
{ "closeport", CLOSEPORT },
|
||||
{ "case", CASE },
|
||||
{ "clearbuf", CLEARBUF },
|
||||
{ "default", DEFAULT },
|
||||
{ "do", DO },
|
||||
{ "downcase", DOWNCASE },
|
||||
{ "else", ELSE },
|
||||
{ "elseif", ELSEIF },
|
||||
{ "endcase", ENDCASE },
|
||||
{ "endif", ENDIF },
|
||||
{ "endwhile", ENDWHILE },
|
||||
{ "exec", EXEC },
|
||||
{ "execport", EXECPORT },
|
||||
{ "exit", EXIT },
|
||||
{ "fields", FIELDS },
|
||||
{ "get", GET },
|
||||
{ "getenv", GETENV },
|
||||
{ "if", IF },
|
||||
{ "inputport", INPUTPORT },
|
||||
{ "lany", LANY },
|
||||
{ "lbreak", LBREAK },
|
||||
{ "lspan", LSPAN },
|
||||
{ "match", MATCH },
|
||||
{ "noop", NOOP },
|
||||
{ "not", '!' },
|
||||
{ "or", '|' },
|
||||
{ "outputport", OUTPUTPORT },
|
||||
{ "print", PRINT },
|
||||
{ "protect", PROTECT },
|
||||
{ "put", PUT },
|
||||
{ "rany", RANY },
|
||||
{ "rbreak", RBREAK },
|
||||
{ "rspan", RSPAN },
|
||||
{ "set", SET },
|
||||
{ "show", SHOW },
|
||||
{ "stylestrip", STYLESTRIP },
|
||||
{ "substitute", SUBSTITUTE },
|
||||
{ "then", THEN },
|
||||
{ "upcase", UPCASE },
|
||||
{ "while", WHILE },
|
||||
{ "verbatim", VERBATIM },
|
||||
{ "zvar", ZVAR } };
|
||||
|
||||
/*
|
||||
* lex_open - this routine [re]initializes the lexer & prepares it to lex
|
||||
* a file. Resets current line # to 1.
|
||||
*/
|
||||
|
||||
void
|
||||
lex_open(FILE *file)
|
||||
{
|
||||
/*
|
||||
* Initialize I/O:
|
||||
*/
|
||||
input_file = file;
|
||||
yylineno = 1;
|
||||
pushback = -1;
|
||||
|
||||
/*
|
||||
* Initialize keyword_dict from keywords if needed:
|
||||
*/
|
||||
if (!keyword_dict) {
|
||||
unsigned int i;
|
||||
|
||||
keyword_dict = int_dictionary_Create(101);
|
||||
|
||||
for (i=0; i<sizeof(keywords)/sizeof(struct keyword_info); i++)
|
||||
int_dictionary_Define(keyword_dict, keywords[i].keyword,
|
||||
0)->value = keywords[i].keyword_number;
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
/* */
|
||||
/* lex subroutines: */
|
||||
/* */
|
||||
/****************************************************************************/
|
||||
|
||||
/*
|
||||
* eat_escape_code - this rountine eats an escape code & returns the character
|
||||
* it codes for or 0 if it codes for "".
|
||||
* (an escape code is what follows a '\\' in a quoted
|
||||
* string) Current escape codes are:
|
||||
*
|
||||
* "n" == '\n'
|
||||
* "t" == '\t'
|
||||
* "b" == '\b'
|
||||
* "\n" == "" (i.e., returns 0)
|
||||
* <EOF> == ""
|
||||
* [0-7]{1,3} == the character represented by the code
|
||||
* interpreted as an octal number.
|
||||
* [^ntb0-7\n] == the same character. I.e., "*" == '*'
|
||||
*/
|
||||
|
||||
#define is_octal_digit(c) (((c)>='0') && ((c)<='7'))
|
||||
|
||||
static char
|
||||
eat_escape_code(void)
|
||||
{
|
||||
int c, coded_char;
|
||||
|
||||
c = input();
|
||||
|
||||
switch (c) {
|
||||
case 0: /* i.e., EOF */
|
||||
unput(c);
|
||||
return(c);
|
||||
case '\n':
|
||||
return(0);
|
||||
case 'n':
|
||||
return('\n');
|
||||
case 't':
|
||||
return('\t');
|
||||
case 'b':
|
||||
return('\b');
|
||||
case '0': case '1': case '2': case '3':
|
||||
case '4': case '5': case '6': case '7':
|
||||
coded_char = c - '0';
|
||||
c = input();
|
||||
if (!is_octal_digit(c)) {
|
||||
unput(c);
|
||||
return(coded_char);
|
||||
}
|
||||
coded_char = coded_char*8 + c-'0';
|
||||
c = input();
|
||||
if (!is_octal_digit(c)) {
|
||||
unput(c);
|
||||
return(coded_char);
|
||||
}
|
||||
return(coded_char*8 + c-'0');
|
||||
default:
|
||||
return(c);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* eat_string - this routine eats characters allowing escape codes via '\\'
|
||||
* until a '"' is eaten. If no '"' is seen before a '\n' or
|
||||
* the <EOF>, a parse_error is set & 0 is returned. Otherwise,
|
||||
* the string represented by what has been eaten is returned.
|
||||
* I.e., 'hello \n there"' would cause "hello \n there" to be
|
||||
* returned. (thats not a <cr> in the first case, a <cr> in the
|
||||
* second) The returned string is on the heap & must be freed
|
||||
* eventually. This routine should be passed the line # that the
|
||||
* string we are eating started on.
|
||||
*/
|
||||
|
||||
static char *
|
||||
eat_string(int starting_line)
|
||||
{
|
||||
int c;
|
||||
char buffer[500];
|
||||
char *ptr = buffer;
|
||||
|
||||
for (;;) {
|
||||
/*
|
||||
* Get the next input character, handling EOF:
|
||||
*/
|
||||
c = input();
|
||||
if (!c) {
|
||||
unput(c);
|
||||
report_parse_error("unterminated string found beginning",
|
||||
starting_line);
|
||||
return(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Deal with special characters ('\\', '"', and '\n'):
|
||||
*/
|
||||
if (c=='\\') {
|
||||
c = eat_escape_code();
|
||||
if (!c)
|
||||
continue;
|
||||
} else if (c == '"') {
|
||||
*ptr = 0;
|
||||
return(string_Copy(buffer));
|
||||
} else if (c == '\n') {
|
||||
unput(c); /* fix line # reference to right line # */
|
||||
report_parse_error("carriage return found in string", yylineno);
|
||||
return(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add the character c to the current string:
|
||||
*/
|
||||
*ptr = c;
|
||||
ptr++;
|
||||
|
||||
/*
|
||||
* If out of buffer space, do a recursive call then
|
||||
* concatanate the result to the string read in so far to get the
|
||||
* entire string and return that:
|
||||
*/
|
||||
if (ptr>buffer+sizeof(buffer)-20) {
|
||||
string rest_of_string, result;
|
||||
|
||||
rest_of_string = eat_string(starting_line);
|
||||
if (!rest_of_string)
|
||||
return(0);
|
||||
|
||||
*ptr = 0;
|
||||
result = string_Concat(buffer, rest_of_string);
|
||||
free(rest_of_string);
|
||||
return(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* eat_show_line - internal routine for eat_show:
|
||||
*
|
||||
* This routine reads in a physical line of text allowing escape
|
||||
* codes via '\\'. If the line ends with a newline, the newline is eaten.
|
||||
* If the line ends with a EOF, the EOF is not eaten. The string
|
||||
* represented by what has been eaten is returned. The returned string
|
||||
* is on the heap & must be freed eventually. If test_for_endshow is
|
||||
* true and the line read in starts off with "endshow" exactly
|
||||
* (i.e., no escape codes) followed by any non-identifier-char, then
|
||||
* instead of doing the above, we just eat the "endshow" & return 0.
|
||||
*/
|
||||
|
||||
static char *
|
||||
eat_show_line(int test_for_endshow)
|
||||
{
|
||||
int c;
|
||||
int saw_escape_code = 0;
|
||||
int starting_line = yylineno;
|
||||
char buffer[200]; /* This must be large enough to hold "endshow" */
|
||||
char *ptr = buffer;
|
||||
|
||||
while (yylineno == starting_line) {
|
||||
c = input();
|
||||
if (!c) {
|
||||
unput(c);
|
||||
*ptr = '\0';
|
||||
return(string_Copy(buffer));
|
||||
} else if (c == '\\') {
|
||||
saw_escape_code = 1;
|
||||
c = eat_escape_code();
|
||||
if (!c)
|
||||
continue;
|
||||
}
|
||||
|
||||
*ptr = c;
|
||||
ptr++;
|
||||
|
||||
if ((ptr==buffer+strlen("endshow")) && test_for_endshow)
|
||||
if (!strncmp(buffer, "endshow", strlen("endshow"))
|
||||
&& !saw_escape_code) {
|
||||
c = input();
|
||||
unput(c);
|
||||
if (!is_identifier_char(c))
|
||||
return(0);
|
||||
}
|
||||
|
||||
if (ptr>buffer+sizeof(buffer)-2) {
|
||||
string the_line;
|
||||
string rest_of_line = eat_show_line(0);
|
||||
|
||||
*ptr = '\0';
|
||||
the_line = string_Concat(buffer, rest_of_line);
|
||||
free(rest_of_line);
|
||||
return(the_line);
|
||||
}
|
||||
}
|
||||
|
||||
*ptr = '\0';
|
||||
return(string_Copy(buffer));
|
||||
}
|
||||
|
||||
/*
|
||||
* eat_til_endshow - this routine eats characters allowing escape codes via
|
||||
* '\\' up to a endshow\{nonalpha} found at the
|
||||
* start of a line not counting leading whitespace.
|
||||
* If <EOF> is seen before the terminator, a parse_error
|
||||
* is set & 0 returned. Otherwise, the string represented
|
||||
* by what has been eaten (escape codes replaced by what
|
||||
* they stand for and leading spaces and tabs removed from
|
||||
* each physical line) is returned. The returned string
|
||||
* is on the heap & must be freed eventually. Note that
|
||||
* to embed endshow in a message, endsho\w can be used.
|
||||
* This routine should be passed the line # of the show
|
||||
* command it is being used to process for use in error
|
||||
* messages.
|
||||
*/
|
||||
|
||||
static char *
|
||||
eat_til_endshow(int start_line_no)
|
||||
{
|
||||
register int c;
|
||||
string text_so_far = string_Copy("");
|
||||
string next_line;
|
||||
|
||||
for (;;) {
|
||||
/*
|
||||
* Skip the spaces & tabs at the start of the current line:
|
||||
*/
|
||||
while ((c=input()), c==' ' || c=='\t') ;
|
||||
unput(c);
|
||||
|
||||
/*
|
||||
* Handle unterminated shows:
|
||||
*/
|
||||
if (!c) {
|
||||
report_parse_error("unterminated show beginning", start_line_no);
|
||||
free(text_so_far);
|
||||
return(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read in rest of the line (including the <cr> at end), allowing
|
||||
* for escape codes and checking for "endshow{nonalpha}" at the
|
||||
* start of the line. (Note: \<newline> is considered the
|
||||
* end of a line here!)
|
||||
*/
|
||||
next_line = eat_show_line(1);
|
||||
|
||||
if (!next_line) /* i.e., is this the endshow line? */
|
||||
return(text_so_far);
|
||||
|
||||
text_so_far = string_Concat2(text_so_far, next_line);
|
||||
free(next_line);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* handle_show - this routine is called after "show"\{nonalpha} is
|
||||
* found to handle up to the endshow. The token # is
|
||||
* returned.
|
||||
*/
|
||||
|
||||
static int
|
||||
handle_show(void)
|
||||
{
|
||||
int c;
|
||||
int start_line_no = yylineno;
|
||||
|
||||
/*
|
||||
* Eat up ' ' and '\t's after show. If the next character is a newline,
|
||||
* eat it. This is so we don't get an extra newline when we call
|
||||
* eat_til_endshow:
|
||||
*/
|
||||
while (c=input(), c==' ' || c=='\t') ;
|
||||
if (c!='\n')
|
||||
unput(c);
|
||||
|
||||
yylval.text = eat_til_endshow(start_line_no);
|
||||
if (yylval.text)
|
||||
return(SHOW);
|
||||
else
|
||||
return(ERROR);
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
/* */
|
||||
/* The main lexer itself: */
|
||||
/* */
|
||||
/****************************************************************************/
|
||||
|
||||
/*
|
||||
* yylex - performs as per. the yacc manual's requirements
|
||||
*/
|
||||
|
||||
int yylex(void)
|
||||
{
|
||||
register int c, last_char;
|
||||
register char *ptr;
|
||||
int start_line_no;
|
||||
int_dictionary_binding *binding;
|
||||
char varname[MAX_IDENTIFIER_LENGTH+1];
|
||||
|
||||
for (;;) {
|
||||
switch (c = input()) {
|
||||
|
||||
/*
|
||||
* Skip whitespace:
|
||||
*/
|
||||
case ' ': case '\t': case '\n':
|
||||
continue;
|
||||
|
||||
/*
|
||||
* '#' comments out everything up to the and including
|
||||
* the next <cr>:
|
||||
*/
|
||||
case '#':
|
||||
while ( (c=input()) && (c!='\n') ) ;
|
||||
if (!c)
|
||||
unput(c);
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Handle c-style comments. Note that "/[^*]" is not the start
|
||||
* of any valid token.
|
||||
*/
|
||||
case '/':
|
||||
start_line_no = yylineno;
|
||||
|
||||
/* verify that next character is a '*': */
|
||||
if ((c=input()) != '*')
|
||||
return(ERROR);
|
||||
|
||||
/* Scan until "*\/" or <EOF>: */
|
||||
for (last_char=0; ; last_char=c) {
|
||||
c = input();
|
||||
if (c == '/' && (last_char=='*'))
|
||||
break;
|
||||
if (!c) {
|
||||
unput(c);
|
||||
report_parse_error("unterminated c style comment found beginning", start_line_no);
|
||||
return(ERROR);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
|
||||
/*
|
||||
* The following characters lex as themselves:
|
||||
* '+', '|', '&', '(', ')', '.', ',' and <EOF>:
|
||||
*/
|
||||
case 0: case '+': case '|': case '&': case '(':
|
||||
case ')': case '.': case ',':
|
||||
return(c);
|
||||
|
||||
/*
|
||||
* Handle "=[^~=]", "=~", and "==":
|
||||
*/
|
||||
case '=':
|
||||
switch (c = input()) {
|
||||
case '~':
|
||||
return(REGEQ);
|
||||
case '=':
|
||||
return(EQ);
|
||||
default:
|
||||
unput(c);
|
||||
return('=');
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle "![^~=]", "!~", and "!=":
|
||||
*/
|
||||
case '!':
|
||||
switch (c = input()) {
|
||||
case '~':
|
||||
return(REGNEQ);
|
||||
case '=':
|
||||
return(NEQ);
|
||||
default:
|
||||
unput(c);
|
||||
return('!');
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle identifiers and keywords:
|
||||
*
|
||||
* Note that the below set of characters is hard coded from
|
||||
* is_identifier_char from parser.h.
|
||||
*/
|
||||
case 'a': case 'b': case 'c': case 'd': case 'e':
|
||||
case 'f': case 'g': case 'h': case 'i': case 'j':
|
||||
case 'k': case 'l': case 'm': case 'n': case 'o':
|
||||
case 'p': case 'q': case 'r': case 's': case 't':
|
||||
case 'u': case 'v': case 'w': case 'x': case 'y':
|
||||
case 'z':
|
||||
case 'A': case 'B': case 'C': case 'D': case 'E':
|
||||
case 'F': case 'G': case 'H': case 'I': case 'J':
|
||||
case 'K': case 'L': case 'M': case 'N': case 'O':
|
||||
case 'P': case 'Q': case 'R': case 'S': case 'T':
|
||||
case 'U': case 'V': case 'W': case 'X': case 'Y':
|
||||
case 'Z':
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
case '_':
|
||||
/*
|
||||
* Read in the first MAX_IDENTIFIER_LENGTH characters of the
|
||||
* identifier into varname null terminated. Eat
|
||||
* the rest of the characters of the identifier:
|
||||
*/
|
||||
for (ptr = varname;;) {
|
||||
if (ptr<varname+MAX_IDENTIFIER_LENGTH)
|
||||
*(ptr++) = c;
|
||||
c = input();
|
||||
if (!is_identifier_char(c))
|
||||
break;
|
||||
}
|
||||
unput(c);
|
||||
*ptr = '\0';
|
||||
|
||||
/*
|
||||
* Look up the identifier in the keyword dictionary.
|
||||
* If its a match, return the keyword's #. In the case
|
||||
* of show, call handle_show to do more processing.
|
||||
* If not a match, treat as a variable name.
|
||||
*/
|
||||
binding = int_dictionary_Lookup(keyword_dict, varname);
|
||||
if (!binding) {
|
||||
yylval.text = string_Copy(varname);
|
||||
return(VARNAME);
|
||||
}
|
||||
if (binding->value == SHOW)
|
||||
return(handle_show());
|
||||
else
|
||||
return(binding->value);
|
||||
|
||||
/*
|
||||
* Handle "${identifier}". Note that $ followed by a
|
||||
* non-identifier character is not the start of any valid token.
|
||||
*/
|
||||
case '$':
|
||||
c = input();
|
||||
if (!is_identifier_char(c))
|
||||
return(ERROR);
|
||||
|
||||
/*
|
||||
* Read in the first MAX_IDENTIFIER_LENGTH characters of the
|
||||
* identifier into varname null terminated. Eat
|
||||
* the rest of the characters of the identifier:
|
||||
*/
|
||||
for (ptr = varname;;) {
|
||||
if (ptr<varname+MAX_IDENTIFIER_LENGTH)
|
||||
*(ptr++) = c;
|
||||
c = input();
|
||||
if (!is_identifier_char(c))
|
||||
break;
|
||||
}
|
||||
unput(c);
|
||||
*ptr = '\0';
|
||||
|
||||
yylval.text = string_Copy(varname);
|
||||
return(VARREF);
|
||||
|
||||
/*
|
||||
* Handle constant strings:
|
||||
*/
|
||||
case '"':
|
||||
yylval.text = eat_string(yylineno);
|
||||
if (yylval.text)
|
||||
return(STRING);
|
||||
else
|
||||
return(ERROR);
|
||||
|
||||
/*
|
||||
* All other characters do not start valid tokens:
|
||||
*/
|
||||
default:
|
||||
return(ERROR);
|
||||
}
|
||||
}
|
||||
}
|
59
zwgc/lexer.h
Normal file
59
zwgc/lexer.h
Normal file
@ -0,0 +1,59 @@
|
||||
/* 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 <zephyr/mit-copyright.h>
|
||||
|
||||
#ifndef lexer_MODULE
|
||||
#define lexer_MODULE
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
/*
|
||||
* is_identifier_char(c) - is c a character that could be part of
|
||||
* an identifier?
|
||||
*
|
||||
* NOTE: this information is hardwired into yylex() in lexer.c!
|
||||
*/
|
||||
|
||||
#define is_identifier_char(c) (isalnum(c) || (c)=='_')
|
||||
|
||||
/*
|
||||
* The maximum # of significant letters in an identifier:
|
||||
*
|
||||
* Note: in order for all keywords to be recognized, this must be at least 20.
|
||||
*/
|
||||
|
||||
#define MAX_IDENTIFIER_LENGTH 128
|
||||
|
||||
/*
|
||||
* yylineno - this holds the current line # we are on. Updated automatically
|
||||
* by yylex.
|
||||
*/
|
||||
|
||||
extern int yylineno;
|
||||
|
||||
/*
|
||||
* lex_open - this routine [re]initializes the lexer & prepares it to lex
|
||||
* a file. Resets current line # to 1.
|
||||
*/
|
||||
|
||||
extern void lex_open(FILE *);
|
||||
|
||||
/*
|
||||
* yylex - performs as per. the yacc manual's requirements
|
||||
*/
|
||||
|
||||
extern int yylex(void);
|
||||
|
||||
#endif
|
730
zwgc/main.c
Normal file
730
zwgc/main.c
Normal file
@ -0,0 +1,730 @@
|
||||
/* 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>
|
||||
#ifdef HAVE_ARES
|
||||
#include <ares.h>
|
||||
#endif
|
||||
|
||||
#if (!defined(lint) && !defined(SABER))
|
||||
static const char rcsid_main_c[] = "$Id$";
|
||||
#endif
|
||||
|
||||
#include <netdb.h>
|
||||
#include <arpa/nameser.h>
|
||||
#ifdef HAVE_ARPA_NAMESER_COMPAT_H
|
||||
#include <arpa/nameser_compat.h>
|
||||
#endif
|
||||
#include <sys/socket.h>
|
||||
#include <sys/resource.h>
|
||||
#include <locale.h>
|
||||
#include <zephyr/mit-copyright.h>
|
||||
#include <zephyr/zephyr.h>
|
||||
|
||||
#include "new_memory.h"
|
||||
#include "zwgc.h"
|
||||
#include "parser.h"
|
||||
#include "node.h"
|
||||
#include "exec.h"
|
||||
#include "zephyr.h"
|
||||
#include "notice.h"
|
||||
#include "subscriptions.h"
|
||||
#include "file.h"
|
||||
#include "mux.h"
|
||||
#include "port.h"
|
||||
#include "variables.h"
|
||||
#include "main.h"
|
||||
#ifdef CMU_ZCTL_PUNT
|
||||
#include "int_dictionary.h"
|
||||
#endif
|
||||
#ifdef CMU_ZWGCPLUS
|
||||
#include "plus.h"
|
||||
int zwgcplus = 1;
|
||||
#endif
|
||||
|
||||
void notice_handler(ZNotice_t *);
|
||||
static void process_notice(ZNotice_t *, char *);
|
||||
static void setup_signals(int);
|
||||
static void detach(void);
|
||||
static void signal_exit(int);
|
||||
#ifdef HAVE_ARES
|
||||
static void notice_callback(void *, int, int, char *, char *);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Global zwgc-wide variables:
|
||||
*/
|
||||
|
||||
#ifdef DEBUG
|
||||
int zwgc_debug = 0;
|
||||
#endif
|
||||
|
||||
static char *zwgc_version_string = "1.0";
|
||||
|
||||
/*
|
||||
* description_filename_override - <<<>>>
|
||||
*/
|
||||
|
||||
static char *description_filename_override = NULL;
|
||||
|
||||
/*
|
||||
* progname - <<<>>> export!
|
||||
*/
|
||||
|
||||
char *progname = NULL;
|
||||
|
||||
/*
|
||||
* subscriptions_filename_override - <<<>>> export!
|
||||
*/
|
||||
|
||||
char *subscriptions_filename_override = NULL;
|
||||
|
||||
/*
|
||||
* location_override - <<<>>> export!
|
||||
*/
|
||||
|
||||
char *location_override = NULL;
|
||||
|
||||
#ifdef HAVE_ARES
|
||||
/*
|
||||
* achannel - <<<>>> export!
|
||||
*/
|
||||
|
||||
ares_channel achannel;
|
||||
#endif
|
||||
|
||||
/****************************************************************************/
|
||||
/* */
|
||||
/* Code to deal with reading in the description file: */
|
||||
/* */
|
||||
/****************************************************************************/
|
||||
|
||||
/*
|
||||
* program - this holds a pointer to the node representation of the
|
||||
* description file once it has been read in.
|
||||
*/
|
||||
|
||||
static struct _Node *program = NULL;
|
||||
|
||||
/*
|
||||
* <<<>>>
|
||||
*/
|
||||
|
||||
static void
|
||||
fake_startup_packet(void)
|
||||
{
|
||||
ZNotice_t *notice = (ZNotice_t *)malloc(sizeof(ZNotice_t));
|
||||
struct timezone tz;
|
||||
char msgbuf[BUFSIZ];
|
||||
extern void Z_gettimeofday(struct _ZTimeval *, struct timezone *);
|
||||
|
||||
var_set_variable("version", zwgc_version_string);
|
||||
|
||||
(void) memset(notice, 0, sizeof(ZNotice_t));
|
||||
|
||||
notice->z_version = "";
|
||||
notice->z_class = "WG_CTL_CLASS";
|
||||
notice->z_class_inst = "WG_CTL_USER<<<>>>";
|
||||
notice->z_opcode = "WG_STARTUP";
|
||||
notice->z_default_format = "Zwgc mark II version $version now running...\n";
|
||||
notice->z_recipient = "";
|
||||
notice->z_sender = "ZWGC";
|
||||
Z_gettimeofday(¬ice->z_time, &tz);
|
||||
notice->z_port = 0;
|
||||
notice->z_kind = ACKED;
|
||||
notice->z_auth = ZAUTH_YES;
|
||||
notice->z_charset = ZCHARSET_UNKNOWN;
|
||||
sprintf(msgbuf,"Zwgc mark II version %s now running...",
|
||||
zwgc_version_string);
|
||||
notice->z_message = msgbuf;
|
||||
notice->z_message_len = strlen(notice->z_message)+1;
|
||||
|
||||
#ifdef CMU_ZWGCPLUS
|
||||
list_add_notice(notice);
|
||||
set_notice_fake(notice, 1);
|
||||
#endif
|
||||
process_notice(notice, NULL);
|
||||
#ifdef CMU_ZWGCPLUS
|
||||
list_del_notice(notice);
|
||||
#else
|
||||
free(notice);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
read_in_description_file(void)
|
||||
{
|
||||
FILE *input_file;
|
||||
char defdesc[128];
|
||||
|
||||
/* var_clear_all_variables(); <<<>>> */
|
||||
|
||||
sprintf(defdesc, "%s/zephyr/%s", DATADIR, DEFDESC);
|
||||
input_file = locate_file(description_filename_override, USRDESC, defdesc);
|
||||
if (input_file)
|
||||
program = parse_file(input_file);
|
||||
else
|
||||
program = NULL;
|
||||
|
||||
fake_startup_packet();
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
/* */
|
||||
/* Code to deal with argument parsing & overall control: */
|
||||
/* */
|
||||
/****************************************************************************/
|
||||
|
||||
/*
|
||||
* void usage()
|
||||
* Effects: Prints out an usage message on stderr then exits the
|
||||
* program with error code 1.
|
||||
*/
|
||||
|
||||
void
|
||||
usage(void)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "\
|
||||
zwgc: usage: zwgc [-debug] [-f <filename>] [-subfile <filename>]\n\
|
||||
[-ttymode] [-nofork] [-reenter] [-loc text]\n\
|
||||
[-default <driver>] {-disable <driver>}*\n\
|
||||
[output driver options]\n");
|
||||
#else
|
||||
fprintf(stderr, "\
|
||||
zwgc: usage: zwgc [-f <filename>] [-subfile <filename>]\n\
|
||||
[-ttymode] [-nofork] [-reenter] [-loc text]\n\
|
||||
[-default <driver>] {-disable <driver>}*\n\
|
||||
[output driver options]\n");
|
||||
#endif
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* <<<>>>
|
||||
*/
|
||||
|
||||
static void
|
||||
run_initprogs(void)
|
||||
{
|
||||
/*
|
||||
* This code stolen from old zwgc: yuck. Clean up & fix. <<<>>>
|
||||
* Should this fork instead of just systeming?
|
||||
*/
|
||||
|
||||
int status;
|
||||
char *iprogname = ZGetVariable("initprogs");
|
||||
|
||||
if (!iprogname)
|
||||
return;
|
||||
|
||||
status = system(iprogname);
|
||||
if (status == 127) {
|
||||
perror("zwgc initprog exec");
|
||||
fprintf(stderr,"zwgc initprog of <%s> failed: no shell.\n",
|
||||
iprogname);
|
||||
} else if (status!=-1 && status>>8) {
|
||||
perror("zwgc initprog exec");
|
||||
fprintf(stderr,"zwgc initprog of <%s> failed with status [%d].\n",
|
||||
iprogname, status>>8);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* main -- the program entry point. Does parsing & top level control.
|
||||
*/
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
char **new;
|
||||
register char **current;
|
||||
int dofork = 1;
|
||||
#ifdef HAVE_ARES
|
||||
int status;
|
||||
#endif
|
||||
|
||||
setlocale(LC_ALL, "");
|
||||
progname = argv[0];
|
||||
|
||||
/*
|
||||
* Process "-f <filename>", "-subfile <filename>", "-nofork",
|
||||
* "-reenter" (which is ignored) and (if DEBUG) "-debug"
|
||||
* arguments, removing then from argc, argv:
|
||||
*/
|
||||
for (new=current=argv+1; *current; current++) {
|
||||
if (string_Eq(*current, "-debug")) {
|
||||
argc--;
|
||||
#ifdef DEBUG
|
||||
zwgc_debug = 1;
|
||||
#endif
|
||||
} else if (string_Eq(*current, "-f")) {
|
||||
argc -= 2; current++;
|
||||
if (!*current)
|
||||
usage();
|
||||
description_filename_override = *current;
|
||||
} else if (string_Eq(*current, "-subfile")) {
|
||||
argc -= 2; current++;
|
||||
if (!*current)
|
||||
usage();
|
||||
subscriptions_filename_override = *current;
|
||||
} else if (string_Eq(*current, "-nofork")) {
|
||||
argc--;
|
||||
dofork = 0;
|
||||
} else if (string_Eq(*current, "-reenter")) {
|
||||
argc--; /* just throw it away */
|
||||
} else if (string_Eq(*current, "-loc")) {
|
||||
argc -= 2; current++;
|
||||
if (!*current)
|
||||
usage();
|
||||
location_override = *current;
|
||||
} else
|
||||
*(new)++ = *current;
|
||||
}
|
||||
*new = *current;
|
||||
|
||||
#ifdef HAVE_ARES
|
||||
/*
|
||||
* Initialize resolver library
|
||||
*/
|
||||
status = ares_init(&achannel);
|
||||
if (status != ARES_SUCCESS) {
|
||||
fprintf(stderr, "Couldn't initialize resolver: %s\n",
|
||||
ares_strerror(status));
|
||||
return(1);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Initialize various subsystems in proper order:
|
||||
*/
|
||||
dprintf("Initializing subsystems...\n"); /*<<<>>>*/
|
||||
#ifdef CMU_ZWGCPLUS
|
||||
init_noticelist();
|
||||
#endif
|
||||
mux_init();
|
||||
var_clear_all_variables(); /* <<<>>> */
|
||||
init_ports(); /* <<<>>> */
|
||||
dprintf("Initializing standard ports...\n");
|
||||
init_standard_ports(&argc, argv);
|
||||
if (argc>1)
|
||||
usage();
|
||||
dprintf("Initializing zephyr...\n");
|
||||
setup_signals(dofork);
|
||||
zephyr_init(notice_handler);
|
||||
|
||||
if (dofork)
|
||||
detach();
|
||||
/*
|
||||
* Run the initprogs program(s) now that we are all set to deal:
|
||||
*/
|
||||
dprintf("Running initprogs program...\n");
|
||||
run_initprogs();
|
||||
|
||||
dprintf("Test Zwgc parser.\n\n");
|
||||
read_in_description_file();
|
||||
|
||||
dprintf("Entering main loop\n");
|
||||
mux_loop();
|
||||
|
||||
dprintf("Returning from main loop\n");
|
||||
finalize_zephyr();
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
/* */
|
||||
/* : */
|
||||
/* */
|
||||
/****************************************************************************/
|
||||
|
||||
#define USER_SUPPRESS "SUPPRESS"
|
||||
#define USER_UNSUPPRESS "UNSUPPRESS"
|
||||
|
||||
#ifdef CMU_ZCTL_PUNT
|
||||
#define USER_LIST_SUPPRESSED "LIST-SUPPRESSED"
|
||||
|
||||
#define PUNT_INC 1024
|
||||
extern int_dictionary puntable_addresses_dict;
|
||||
ZNotice_t punt_reply;
|
||||
|
||||
static void
|
||||
create_punt_reply(int_dictionary_binding *punt_ent)
|
||||
{
|
||||
int key_len = strlen(punt_ent->key);
|
||||
char *tmp;
|
||||
|
||||
if (!punt_reply.z_message) {
|
||||
punt_reply.z_message = (char *)malloc(PUNT_INC);
|
||||
punt_reply.z_message[0] = 0;
|
||||
}
|
||||
|
||||
if ((punt_reply.z_message_len + key_len + 1) / PUNT_INC >
|
||||
(punt_reply.z_message_len + PUNT_INC - 1) / PUNT_INC) {
|
||||
char *new_message = (char *)malloc((punt_reply.z_message_len
|
||||
/ PUNT_INC + 1) * PUNT_INC);
|
||||
|
||||
strcpy(new_message, punt_reply.z_message);
|
||||
|
||||
free(punt_reply.z_message);
|
||||
punt_reply.z_message = new_message;
|
||||
}
|
||||
tmp = punt_reply.z_message + strlen(punt_reply.z_message);
|
||||
strcat (punt_reply.z_message, punt_ent->key);
|
||||
strcat (punt_reply.z_message, "\n");
|
||||
punt_reply.z_message_len += key_len + 1;
|
||||
|
||||
while (*tmp != '\001') tmp++;
|
||||
*tmp = ',';
|
||||
while (*tmp != '\001') tmp++;
|
||||
*tmp = ',';
|
||||
}
|
||||
#endif /* CMU_ZCTL_PUNT */
|
||||
|
||||
void
|
||||
notice_handler(ZNotice_t *notice)
|
||||
{
|
||||
#ifndef HAVE_ARES
|
||||
int ret;
|
||||
char node[NS_MAXDNAME];
|
||||
#endif
|
||||
|
||||
#if defined(CMU_ZWGCPLUS)
|
||||
list_add_notice(notice);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_ARES
|
||||
ares_getnameinfo(achannel,
|
||||
(const struct sockaddr *)&(notice->z_sender_sockaddr),
|
||||
notice->z_sender_sockaddr.sa.sa_family == AF_INET ?
|
||||
sizeof(struct sockaddr_in) :
|
||||
notice->z_sender_sockaddr.sa.sa_family == AF_INET6 ?
|
||||
sizeof(struct sockaddr_in6) :
|
||||
sizeof(notice->z_sender_sockaddr), ARES_NI_LOOKUPHOST,
|
||||
notice_callback, notice);
|
||||
#else
|
||||
ret = getnameinfo((const struct sockaddr *)&(notice->z_sender_sockaddr),
|
||||
notice->z_sender_sockaddr.sa.sa_family == AF_INET ?
|
||||
sizeof(struct sockaddr_in) :
|
||||
notice->z_sender_sockaddr.sa.sa_family == AF_INET6 ?
|
||||
sizeof(struct sockaddr_in6) :
|
||||
sizeof(notice->z_sender_sockaddr),
|
||||
node, sizeof(node), NULL, 0, 0);
|
||||
if (ret != 0)
|
||||
strcpy(node, "?");
|
||||
|
||||
process_notice(notice, node);
|
||||
#ifdef CMU_ZWGCPLUS
|
||||
/* Let list_del_notice clean up for us. */
|
||||
#else
|
||||
ZFreeNotice(notice);
|
||||
free(notice);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef HAVE_ARES
|
||||
/*
|
||||
static void
|
||||
notice_callback(void *arg,
|
||||
int status,
|
||||
int timeouts,
|
||||
struct hostent *fromhost)
|
||||
*/
|
||||
static void
|
||||
notice_callback(void *arg,
|
||||
int status,
|
||||
int timeouts,
|
||||
char *node,
|
||||
char *service)
|
||||
{
|
||||
ZNotice_t *notice = (ZNotice_t *) arg;
|
||||
|
||||
#ifdef CMU_ZWGCPLUS
|
||||
plus_set_hname(notice, node);
|
||||
#endif
|
||||
|
||||
process_notice(notice, node);
|
||||
#ifdef CMU_ZWGCPLUS
|
||||
list_del_notice(notice);
|
||||
#else
|
||||
ZFreeNotice(notice);
|
||||
free(notice);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
process_notice(ZNotice_t *notice,
|
||||
char *hostname)
|
||||
{
|
||||
char *control_opcode;
|
||||
|
||||
dprintf("Got a message\n");
|
||||
|
||||
control_opcode = decode_notice(notice, hostname);
|
||||
if (control_opcode) {
|
||||
#ifdef DEBUG
|
||||
printf("got control opcode <%s>.\n", control_opcode);
|
||||
#endif
|
||||
if (!strcasecmp(control_opcode, USER_REREAD)) {
|
||||
read_in_description_file();
|
||||
} else if (!strcasecmp(control_opcode, USER_SHUTDOWN))
|
||||
zwgc_shutdown();
|
||||
else if (!strcasecmp(control_opcode, USER_STARTUP)) {
|
||||
#ifdef DEBUG_MEMORY
|
||||
report_memory_usage(); /* <<<>>> */
|
||||
#endif
|
||||
zwgc_startup();
|
||||
} else if (!strcasecmp(control_opcode, USER_SUPPRESS)) {
|
||||
string class = get_field(notice->z_message,
|
||||
notice->z_message_len, 1);
|
||||
string instance = get_field(notice->z_message,
|
||||
notice->z_message_len, 2);
|
||||
string recipient = get_field(notice->z_message,
|
||||
notice->z_message_len, 3);
|
||||
punt(class, instance, recipient);
|
||||
free(class);
|
||||
free(instance);
|
||||
free(recipient);
|
||||
} else if (!strcasecmp(control_opcode, USER_UNSUPPRESS)) {
|
||||
string class = get_field(notice->z_message,
|
||||
notice->z_message_len, 1);
|
||||
string instance = get_field(notice->z_message,
|
||||
notice->z_message_len, 2);
|
||||
string recipient = get_field(notice->z_message,
|
||||
notice->z_message_len, 3);
|
||||
unpunt(class, instance, recipient);
|
||||
free(class);
|
||||
free(instance);
|
||||
free(recipient);
|
||||
#ifdef CMU_ZCTL_PUNT
|
||||
} else if (!strcasecmp(control_opcode, USER_LIST_SUPPRESSED)) {
|
||||
struct sockaddr_in old, to;
|
||||
int retval;
|
||||
|
||||
if (!notice->z_port) {
|
||||
printf("zwgc: can't reply to LIST-SUPPRESSED request\n");
|
||||
return;
|
||||
}
|
||||
memset((char *) &punt_reply, 0, sizeof(ZNotice_t));
|
||||
punt_reply.z_kind = CLIENTACK;
|
||||
punt_reply.z_class = WG_CTL_CLASS;
|
||||
punt_reply.z_class_inst = "WG_REPLY";
|
||||
punt_reply.z_recipient = "zctl?";
|
||||
punt_reply.z_sender = "Zwgc";
|
||||
punt_reply.z_default_format = "";
|
||||
punt_reply.z_opcode = USER_LIST_SUPPRESSED;
|
||||
punt_reply.z_port = notice->z_port;
|
||||
punt_reply.z_message = NULL;
|
||||
punt_reply.z_message_len = 0;
|
||||
|
||||
if (puntable_addresses_dict) {
|
||||
int_dictionary_Enumerate(puntable_addresses_dict,
|
||||
create_punt_reply);
|
||||
}
|
||||
|
||||
old = ZGetDestAddr();
|
||||
to = old;
|
||||
|
||||
to.sin_port = notice->z_port;
|
||||
if ((retval = ZSetDestAddr(&to)) != ZERR_NONE) {
|
||||
com_err("zwgc",retval,"while setting destination address");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ZSendNotice(&punt_reply, ZNOAUTH);
|
||||
|
||||
if ((retval = ZSetDestAddr(&old)) != ZERR_NONE) {
|
||||
com_err("zwgc",retval,"while resetting destination address");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (punt_reply.z_message) {
|
||||
free(punt_reply.z_message);
|
||||
punt_reply.z_message = NULL;
|
||||
}
|
||||
#endif
|
||||
} else if (!strcasecmp(control_opcode, USER_EXIT)) {
|
||||
signal_exit(0);
|
||||
} else
|
||||
printf("zwgc: unknown control opcode %s.\n", control_opcode);
|
||||
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (!zwgc_active) {
|
||||
#ifdef DEBUG
|
||||
if (zwgc_debug)
|
||||
printf("NON-ACTIVE: PUNTED <%s>!!!!\n", notice->z_class_inst);
|
||||
#endif
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (puntable_address_p(notice->z_class,
|
||||
notice->z_class_inst,
|
||||
notice->z_recipient)) {
|
||||
#ifdef DEBUG
|
||||
if (zwgc_debug)
|
||||
printf("PUNTED <%s>!!!!\n", notice->z_class_inst);
|
||||
#endif
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
exec_process_packet(program, notice);
|
||||
cleanup:
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef CMU_ZWGCPLUS
|
||||
void
|
||||
reprocess_notice(ZNotice_t *notice, char *hostname)
|
||||
{
|
||||
list_add_notice(notice);
|
||||
process_notice(notice, hostname);
|
||||
list_del_notice(notice);
|
||||
}
|
||||
#endif
|
||||
|
||||
/***************************************************************************/
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
static void
|
||||
signal_exit(int ignored)
|
||||
{
|
||||
mux_end_loop_p = 1;
|
||||
}
|
||||
|
||||
/* clean up ALL the waiting children, in case we get hit with
|
||||
multiple SIGCHLD's at once, and don't process in time. */
|
||||
static RETSIGTYPE
|
||||
signal_child(int ignored)
|
||||
{
|
||||
#ifdef HAVE_WAITPID
|
||||
int status;
|
||||
#else
|
||||
union wait status;
|
||||
#endif
|
||||
int pid, old_errno = errno;
|
||||
|
||||
do {
|
||||
#ifdef HAVE_WAITPID
|
||||
pid = waitpid(-1, &status, WNOHANG);
|
||||
#else
|
||||
pid = wait3(&status, WNOHANG, (struct rusage *)0);
|
||||
#endif
|
||||
} while (pid != 0 && pid != -1);
|
||||
errno = old_errno;
|
||||
}
|
||||
|
||||
/* rewrite the wgfile in case it has gone away */
|
||||
static RETSIGTYPE
|
||||
signal_usr1(int ignored)
|
||||
{
|
||||
write_wgfile();
|
||||
}
|
||||
|
||||
static void
|
||||
setup_signals(int dofork)
|
||||
{
|
||||
#ifdef _POSIX_VERSION
|
||||
struct sigaction sa;
|
||||
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = 0;
|
||||
|
||||
if (dofork) {
|
||||
sa.sa_handler = SIG_IGN;
|
||||
sigaction(SIGINT, &sa, (struct sigaction *)0);
|
||||
sigaction(SIGTSTP, &sa, (struct sigaction *)0);
|
||||
sigaction(SIGQUIT, &sa, (struct sigaction *)0);
|
||||
sigaction(SIGTTOU, &sa, (struct sigaction *)0);
|
||||
} else {
|
||||
/* clean up on SIGINT; exiting on logout is the user's problem, now. */
|
||||
sa.sa_handler = signal_exit;
|
||||
sigaction(SIGINT, &sa, (struct sigaction *)0);
|
||||
}
|
||||
|
||||
/* behavior never changes */
|
||||
sa.sa_handler = signal_exit;
|
||||
sigaction(SIGTERM, &sa, (struct sigaction *)0);
|
||||
sigaction(SIGHUP, &sa, (struct sigaction *)0);
|
||||
|
||||
sa.sa_handler = SIG_IGN;
|
||||
sigaction(SIGPIPE, &sa, (struct sigaction *)0);
|
||||
|
||||
sa.sa_handler = signal_child;
|
||||
sigaction(SIGCHLD, &sa, (struct sigaction *)0);
|
||||
|
||||
sa.sa_handler = signal_usr1;
|
||||
sigaction(SIGUSR1, &sa, (struct sigaction *)0);
|
||||
|
||||
#else /* !POSIX */
|
||||
if (dofork) {
|
||||
/* Ignore keyboard signals if forking. Bad things will happen. */
|
||||
signal(SIGINT, SIG_IGN);
|
||||
signal(SIGTSTP, SIG_IGN);
|
||||
signal(SIGTTOU, SIG_IGN);
|
||||
signal(SIGQUIT, SIG_IGN);
|
||||
} else {
|
||||
/* clean up on SIGINT; exiting on logout is the user's problem, now. */
|
||||
signal(SIGINT, signal_exit);
|
||||
}
|
||||
|
||||
/* behavior never changes */
|
||||
signal(SIGTERM, signal_exit);
|
||||
signal(SIGHUP, signal_exit);
|
||||
signal(SIGCHLD, signal_child);
|
||||
signal(SIGPIPE, SIG_IGN); /* so that Xlib gets an error */
|
||||
signal(SIGUSR1, signal_usr1);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* detach() taken from old zwgc, with lots of stuff ripped out */
|
||||
|
||||
static void
|
||||
detach(void)
|
||||
{
|
||||
/* detach from terminal and fork. */
|
||||
register int i;
|
||||
|
||||
/* Attempt to join the process group of the session leader. This
|
||||
* will get us a HUP if the session leader is in the foreground at
|
||||
* logout time (which is often the case) or if the shell is running
|
||||
* under telnetd or xterm (both of which HUP the process group of
|
||||
* their child process). If we have getsid(), that's the best way
|
||||
* of finding the session leader; otherwise use the process group of
|
||||
* the parent process, which is a good guess. */
|
||||
#if defined(HAVE_GETSID)
|
||||
|
||||
setpgid(0, getsid(0));
|
||||
#elif defined(HAVE_GETPGID)
|
||||
setpgid(0, getpgid(getppid()));
|
||||
#elif !defined(GETPGRP_VOID)
|
||||
setpgid(0, getpgrp(getppid()));
|
||||
#endif
|
||||
|
||||
/* fork off and let parent exit... */
|
||||
i = fork();
|
||||
if (i) {
|
||||
if (i < 0) {
|
||||
perror("zwgc: cannot fork, aborting:");
|
||||
exit(1);
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
}
|
54
zwgc/main.h
Normal file
54
zwgc/main.h
Normal file
@ -0,0 +1,54 @@
|
||||
/* 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 <zephyr/mit-copyright.h>
|
||||
|
||||
#ifndef main_MODULE
|
||||
#define main_MODULE
|
||||
|
||||
#ifdef HAVE_ARES
|
||||
#include <ares.h>
|
||||
|
||||
extern ares_channel achannel;
|
||||
#endif
|
||||
|
||||
extern char *progname;
|
||||
extern char *subscriptions_filename_override;
|
||||
extern char *location_override;
|
||||
|
||||
/*
|
||||
* void usage()
|
||||
* Effects: Prints out a usage message on stderr then exits the
|
||||
* program with error code 1.
|
||||
*/
|
||||
|
||||
extern void usage(void);
|
||||
|
||||
#ifdef CMU_ZWGCPLUS
|
||||
extern void reprocess_notice(ZNotice_t *notice, char *hostname);
|
||||
#endif
|
||||
|
||||
/* USRDESC points to a file (relative to user's homedir) which has a user's
|
||||
description file */
|
||||
|
||||
#define USRDESC ".zwgc.desc"
|
||||
|
||||
/* DEFDESC points to a file (relative to the data directory) which has the
|
||||
* system default description file */
|
||||
|
||||
#ifndef DEFDESC
|
||||
#define DEFDESC "zwgc.desc"
|
||||
#endif
|
||||
|
||||
#endif
|
239
zwgc/mux.c
Normal file
239
zwgc/mux.c
Normal file
@ -0,0 +1,239 @@
|
||||
/* 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_mux_c[] = "$Id$";
|
||||
#endif
|
||||
|
||||
/****************************************************************************/
|
||||
/* */
|
||||
/* Module containing code to wait on multiple file descriptors: */
|
||||
/* */
|
||||
/****************************************************************************/
|
||||
|
||||
#include <zephyr/zephyr.h>
|
||||
#include "main.h"
|
||||
#include "mux.h"
|
||||
#include "error.h"
|
||||
#include "zwgc.h"
|
||||
#include "pointer.h"
|
||||
#ifdef CMU_ZWGCPLUS
|
||||
#include "plus.h"
|
||||
#endif
|
||||
|
||||
#ifdef _AIX
|
||||
#include <sys/select.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* mux_end_loop_p - Setting this to true during a mux_loop causes the mux_loop
|
||||
* to be exited.
|
||||
*/
|
||||
|
||||
int mux_end_loop_p;
|
||||
|
||||
/*
|
||||
* have_tty - is defined to be true if there is a controlling tty for this
|
||||
* process. When we can no longer access the controlling tty,
|
||||
* the process will die.
|
||||
*/
|
||||
|
||||
static int have_tty = 0;
|
||||
|
||||
/*
|
||||
* max_source - the maximum file descriptor that a handler was ever
|
||||
* registered for:
|
||||
*/
|
||||
|
||||
static int max_source = -1;
|
||||
|
||||
/*
|
||||
* Which file descriptors we're waiting on for input & the accompanying
|
||||
* input handlers & their arguments:
|
||||
*/
|
||||
|
||||
static fd_set input_sources;
|
||||
static void (*input_handler[MAX_SOURCES])(void *);
|
||||
static pointer input_handler_arg[MAX_SOURCES];
|
||||
|
||||
static int check_tty(void);
|
||||
|
||||
/*
|
||||
* void mux_init()
|
||||
* Requires: mux_init has never been called before
|
||||
* Effects: Initializes the mux module. Must be called before
|
||||
* any other mux call.
|
||||
*/
|
||||
|
||||
void
|
||||
mux_init(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
FD_ZERO(&input_sources);
|
||||
|
||||
for (i=0; i<MAX_SOURCES; i++)
|
||||
input_handler[i] = NULL;
|
||||
|
||||
have_tty = check_tty();
|
||||
}
|
||||
|
||||
/*
|
||||
* void mux_add_input_source(int descriptor; void (*handler)(); pointer arg)
|
||||
* Requires: 0<=descriptor<MAX_SOURCES, mux_init has been called
|
||||
* Modifies: Removes the previous input handler if any for descriptor
|
||||
* Effects: Registers handler as the input handler for file descriptor
|
||||
* descriptor. When mux_loop() is running and input is
|
||||
* available on descriptor, handler will be called with
|
||||
* argument arg.
|
||||
*/
|
||||
|
||||
void
|
||||
mux_add_input_source(int descriptor,
|
||||
void (*handler)(void *),
|
||||
pointer arg)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
if(descriptor < 0 || descriptor >= MAX_SOURCES)
|
||||
abort(); /* <<<>>> */
|
||||
#endif
|
||||
|
||||
input_handler[descriptor] = handler;
|
||||
input_handler_arg[descriptor] = arg;
|
||||
FD_SET(descriptor, &input_sources);
|
||||
if(descriptor > max_source)
|
||||
max_source = descriptor;
|
||||
}
|
||||
|
||||
/*
|
||||
* void mux_loop()
|
||||
* Requires: mux_init has been called.
|
||||
* Effects: Loops until mux_end_loop_p becomes true. (Sets
|
||||
* mux_end_loop_p false to start). Whenever input is
|
||||
* available on an input source which has a registered
|
||||
* handler (see mux_add_input_source), that handler is
|
||||
* called with its argument. It is guaranteed that if
|
||||
* input is available on a source, its respective input
|
||||
* handler, if any, will eventually be called. No other
|
||||
* ordering guarantees are made. When some signal handler
|
||||
* or input handler eventually sets mux_end_loop_p to
|
||||
* true, we return.
|
||||
*/
|
||||
|
||||
void
|
||||
mux_loop(void)
|
||||
{
|
||||
int i, nfds;
|
||||
fd_set inputs, outputs;
|
||||
struct timeval tv, *tvp;
|
||||
|
||||
mux_end_loop_p = 0;
|
||||
|
||||
for (;;) {
|
||||
/*
|
||||
* Exit if mux_end_loop_p has been set to true by a handler:
|
||||
*/
|
||||
if (mux_end_loop_p)
|
||||
break;
|
||||
tvp = NULL;
|
||||
tv.tv_sec = 0;
|
||||
if (have_tty) {
|
||||
#ifdef CMU_ZWGCPLUS
|
||||
tv.tv_sec = plus_timequeue_events();
|
||||
if (tv.tv_sec > 10) tv.tv_sec = 10;
|
||||
#else
|
||||
tv.tv_sec = 10;
|
||||
#endif
|
||||
tv.tv_usec = 0;
|
||||
#ifdef CMU_ZWGCPLUS
|
||||
} else {
|
||||
tv.tv_sec = plus_timequeue_events();
|
||||
tv.tv_usec = 0;
|
||||
#endif
|
||||
}
|
||||
if (tv.tv_sec)
|
||||
tvp = &tv;
|
||||
|
||||
/*
|
||||
* Do a select on all the file descriptors we care about to
|
||||
* wait until at least one of them has input available:
|
||||
*/
|
||||
inputs = input_sources;
|
||||
FD_ZERO(&outputs);
|
||||
|
||||
#ifdef HAVE_ARES
|
||||
nfds = ares_fds(achannel, &inputs, &outputs);
|
||||
if (nfds < max_source + 1)
|
||||
nfds = max_source + 1;
|
||||
tvp = ares_timeout(achannel, tvp, &tv);
|
||||
#else
|
||||
nfds = max_source + 1;
|
||||
#endif
|
||||
|
||||
i = select(nfds, &inputs, &outputs, NULL, tvp);
|
||||
|
||||
if (i == -1) {
|
||||
if (errno == EINTR)
|
||||
continue; /* on a signal restart checking mux_loop_end_p */
|
||||
else
|
||||
FATAL_TRAP( errno, "while selecting" );
|
||||
}
|
||||
else if (i == 0) {
|
||||
if (have_tty && !check_tty()) {
|
||||
mux_end_loop_p = 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_ARES
|
||||
ares_process(achannel, &inputs, &outputs);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Call all input handlers whose corresponding file descriptors have
|
||||
* input:
|
||||
*/
|
||||
for(i=0; i<=max_source; i++)
|
||||
if (FD_ISSET(i, &inputs) && input_handler[i]) {
|
||||
#ifdef DEBUG
|
||||
if (zwgc_debug)
|
||||
fprintf(stderr,
|
||||
"mux_loop...activity on fd %d, calling %lx(%lx)\n",
|
||||
i, (unsigned long)input_handler[i],
|
||||
(unsigned long)input_handler_arg[i]);
|
||||
#endif
|
||||
input_handler[i](input_handler_arg[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
check_tty(void)
|
||||
{
|
||||
register int result;
|
||||
int pgrp;
|
||||
int tty = open("/dev/tty", O_RDONLY|O_NDELAY);
|
||||
|
||||
if (tty < 0) return 0;
|
||||
|
||||
#if defined(_POSIX_VERSION)
|
||||
result = ( ((pgrp = tcgetpgrp(tty)) < 0) ? 0 : 1 );
|
||||
#else
|
||||
result = ( (ioctl(tty, TIOCGPGRP, &pgrp) < 0) ? 0 : 1 );
|
||||
#endif
|
||||
|
||||
close(tty);
|
||||
return(result);
|
||||
}
|
72
zwgc/mux.h
Normal file
72
zwgc/mux.h
Normal file
@ -0,0 +1,72 @@
|
||||
/* 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 <zephyr/mit-copyright.h>
|
||||
|
||||
#ifndef mux_MODULE
|
||||
#define mux_MODULE
|
||||
|
||||
/*
|
||||
* MAX_SOURCES - the greatest file descriptor # that can be waited on minus one
|
||||
* This can not exceed FD_SETSIZE from <sys/types.h>.
|
||||
*/
|
||||
|
||||
#define MAX_SOURCES 32
|
||||
|
||||
/*
|
||||
* mux_end_loop_p - Setting this to true during a mux_loop causes the mux_loop
|
||||
* to be exited.
|
||||
*/
|
||||
|
||||
extern int mux_end_loop_p;
|
||||
|
||||
/*
|
||||
* void mux_init()
|
||||
* Requires: mux_init has never been called before
|
||||
* Effects: Initializes the mux module. Must be called before
|
||||
* any other mux call.
|
||||
*/
|
||||
|
||||
extern void mux_init(void);
|
||||
|
||||
/*
|
||||
* void mux_add_input_source(int descriptior; void (*handler)(); void *arg)
|
||||
* Requires: 0<=descriptor<MAX_SOURCES, mux_init has been called
|
||||
* Modifies: Removes the previous input handler if any for descriptor
|
||||
* Effects: Registers handler as the input handler for file descriptor
|
||||
* descriptor. When mux_loop() is running and input is
|
||||
* available on descriptor, handler will be called with
|
||||
* argument arg.
|
||||
*/
|
||||
|
||||
extern void mux_add_input_source(int, void (*)(void *), void *);
|
||||
|
||||
/*
|
||||
* void mux_loop()
|
||||
* Requires: mux_init has been called.
|
||||
* Effects: Loops until mux_end_loop_p becomes true. (Sets
|
||||
* mux_end_loop_p false to start). Whenever input is
|
||||
* available on an input source which has a registered
|
||||
* handler (see mux_add_input_source), that handler is
|
||||
* called with its argument. It is guarenteed that if
|
||||
* input is available on a source, its respective input
|
||||
* handler, if any, will eventually be called. No other
|
||||
* ordering guarentees are made. When some signal handler
|
||||
* or input handler eventually sets mux_end_loop_p to
|
||||
* true, we return.
|
||||
*/
|
||||
|
||||
extern void mux_loop(void);
|
||||
|
||||
#endif
|
244
zwgc/new_memory.c
Normal file
244
zwgc/new_memory.c
Normal file
@ -0,0 +1,244 @@
|
||||
/* 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_new_memory_c[] = "$Id$";
|
||||
#endif
|
||||
|
||||
#if !defined(SABER) && (defined(DEBUG) || defined(MEMORY_DEBUG))
|
||||
|
||||
/*
|
||||
* memory - module wrapping debugging code around normal malloc/free/etc.
|
||||
* routines.
|
||||
*
|
||||
* Overview:
|
||||
*
|
||||
* ...
|
||||
*/
|
||||
|
||||
#define memory__PROVIDER
|
||||
#include "new_memory.h"
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
#ifdef DEBUG
|
||||
#define assert(x) if (!(x)) abort()
|
||||
#else
|
||||
#define assert(x)
|
||||
#endif
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
#ifdef DEBUG_MEMORY
|
||||
|
||||
extern void record_request();
|
||||
char *current_module = 0;
|
||||
int current_line = -1;
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* string string_CreateFromData(char *data, int length):
|
||||
* Requires: data[0], data[1], ..., data[length-1] != 0
|
||||
* Effects: Takes the first length characters at data and
|
||||
* creates a string containing them. The returned string
|
||||
* is on the heap & must be freed eventually.
|
||||
* I.e., if passed "foobar" and 3, it would return
|
||||
* string_Copy("foo").
|
||||
*/
|
||||
|
||||
void *memory__malloc(size)
|
||||
unsigned size;
|
||||
{
|
||||
char *result;
|
||||
|
||||
result = malloc(size + memory__size_of_header);
|
||||
if (!result)
|
||||
abort(); /* <<<>>> */
|
||||
|
||||
#ifdef DEBUG_MEMORY
|
||||
((memory_block_header *)result)->size = size;
|
||||
((memory_block_header *)result)->creating_module = current_module;
|
||||
((memory_block_header *)result)->line_number_in_creating_module =
|
||||
current_line;
|
||||
((memory_block_header *)result)->check_field = CHECK_FIELD_VALUE;
|
||||
result += memory__size_of_header;
|
||||
|
||||
record_request(current_module, current_line, 1, size);
|
||||
#endif
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
void *memory__realloc(aptr, size)
|
||||
void *aptr;
|
||||
unsigned size;
|
||||
{
|
||||
char *result, *ptr = aptr;
|
||||
|
||||
assert(ptr);
|
||||
|
||||
#ifdef DEBUG_MEMORY
|
||||
if (!memory__on_heap_p(ptr)) {
|
||||
printf("realloced non-memory block in %s on line %d!\n",
|
||||
current_module, current_line);
|
||||
fflush(stdout);
|
||||
return(realloc(ptr, size));
|
||||
}
|
||||
#endif
|
||||
|
||||
result = realloc(ptr-memory__size_of_header, size+memory__size_of_header);
|
||||
if (!result)
|
||||
abort(); /* <<<>>> */
|
||||
|
||||
return(result+memory__size_of_header);
|
||||
}
|
||||
|
||||
void *memory__calloc(nelem, elsize)
|
||||
unsigned nelem;
|
||||
unsigned elsize;
|
||||
{
|
||||
char *result;
|
||||
|
||||
#ifdef DEBUG_MEMORY
|
||||
printf("in calloc\n"); fflush(stdout);
|
||||
#endif
|
||||
|
||||
abort();
|
||||
|
||||
#ifdef FRED
|
||||
result = calloc(nelem, elsize);
|
||||
if (!result)
|
||||
abort();
|
||||
|
||||
record_request(1);
|
||||
#endif
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
void memory__free(aptr)
|
||||
void *aptr;
|
||||
{
|
||||
char *ptr = aptr;
|
||||
assert(ptr);
|
||||
|
||||
#ifdef DEBUG_MEMORY
|
||||
if (!memory__on_heap_p(ptr)) {
|
||||
printf("freed non-memory block in %s on line %d!\n", current_module,
|
||||
current_line);
|
||||
fflush(stdout);
|
||||
(void)free(ptr);
|
||||
return;
|
||||
}
|
||||
|
||||
record_request(memory__get_header(ptr)->creating_module,
|
||||
memory__get_header(ptr)->line_number_in_creating_module,
|
||||
-1,
|
||||
memory__get_header(ptr)->size);
|
||||
#endif
|
||||
|
||||
(void)free(ptr-memory__size_of_header);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_MEMORY
|
||||
|
||||
#include "int_dictionary.h"
|
||||
|
||||
static int request_off = 0;
|
||||
static int_dictionary requests = 0;
|
||||
static int outstanding_requests = 0;
|
||||
static int outstanding_memory = 0;
|
||||
|
||||
void record_request(module, line_number, dir, size)
|
||||
char *module;
|
||||
int line_number;
|
||||
int dir;
|
||||
unsigned int size;
|
||||
{
|
||||
int_dictionary_binding *binding;
|
||||
int already_exists;
|
||||
#ifdef LINE
|
||||
char buffer[20];
|
||||
#endif
|
||||
|
||||
if (request_off)
|
||||
return;
|
||||
request_off = 1;
|
||||
|
||||
if (!requests)
|
||||
requests = int_dictionary_Create(101);
|
||||
|
||||
#ifdef LINE
|
||||
module = string_Concat(module, ":");
|
||||
sprintf(buffer, "%d", line_number);
|
||||
module = string_Concat2(module, buffer);
|
||||
#endif
|
||||
|
||||
binding = int_dictionary_Define(requests, module, &already_exists);
|
||||
if (!already_exists)
|
||||
binding->value = 0;
|
||||
|
||||
#ifdef LINE
|
||||
free(module);
|
||||
#endif
|
||||
|
||||
binding->value += dir;
|
||||
outstanding_requests += dir;
|
||||
outstanding_memory += size*dir;
|
||||
|
||||
request_off = 0;
|
||||
}
|
||||
|
||||
void proc(binding)
|
||||
int_dictionary_binding *binding;
|
||||
{
|
||||
if (binding->value)
|
||||
printf(" %-30s %6d blocks allocated\n", binding->key, binding->value);
|
||||
}
|
||||
|
||||
void report_memory_usage()
|
||||
{
|
||||
printf("\n# of blocks on the heap = %d\n", outstanding_requests);
|
||||
printf("Total heap space in use: %d bytes\n", outstanding_memory);
|
||||
|
||||
printf("\nHeap Allocations by module:\n");
|
||||
int_dictionary_Enumerate(requests, proc);
|
||||
printf("\n");
|
||||
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
void set_module(file, line)
|
||||
char *file;
|
||||
int line;
|
||||
{
|
||||
if (request_off)
|
||||
return;
|
||||
|
||||
if (!strcmp(file, "new_string.c"))
|
||||
return;
|
||||
if (!strcmp(file, "string_dictionary_aux.c"))
|
||||
return;
|
||||
|
||||
current_line = line;
|
||||
current_module = file;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* SABER */
|
84
zwgc/new_memory.h
Normal file
84
zwgc/new_memory.h
Normal file
@ -0,0 +1,84 @@
|
||||
/* 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".
|
||||
*/
|
||||
|
||||
/* This entire module goes out the window in saber */
|
||||
#if !defined(SABER) && (defined(DEBUG) || defined(DEBUG_MEMORY))
|
||||
|
||||
#ifndef memory_MODULE
|
||||
#define memory_MODULE
|
||||
|
||||
extern void *memory__malloc(unsigned); /* PRIVATE */
|
||||
extern void *memory__realloc(void *, unsigned); /* PRIVATE */
|
||||
extern void *memory__calloc(unsigned, unsigned); /* PRIVATE */
|
||||
extern void memory__free(void *); /* PRIVATE */
|
||||
|
||||
#ifdef DEBUG_MEMORY
|
||||
|
||||
#define CHECK_FIELD_VALUE 0xe5e7e3e9
|
||||
|
||||
typedef struct _memory_block_header {
|
||||
unsigned size;
|
||||
char *creating_module;
|
||||
int line_number_in_creating_module;
|
||||
unsigned int check_field;
|
||||
} memory_block_header;
|
||||
|
||||
#define memory__size_of_header (sizeof(struct _memory_block_header))
|
||||
|
||||
#define memory__get_header(block) \
|
||||
((struct _memory_block_header *)((block)-memory__size_of_header))
|
||||
|
||||
#define memory__on_heap_p(block) \
|
||||
(memory__get_header(block)->check_field==CHECK_FIELD_VALUE)
|
||||
|
||||
#else
|
||||
|
||||
#define memory__size_of_header 0
|
||||
|
||||
#define memory__on_heap_p(block) 1
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* int string_Length(string s):
|
||||
* Effects: Returns the number of non-null characters in s.
|
||||
*/
|
||||
|
||||
#ifndef memory__PROVIDER
|
||||
#ifdef DEBUG_MEMORY
|
||||
|
||||
extern char *current_module;
|
||||
extern void set_module();
|
||||
|
||||
#define malloc(size) (set_module(__FILE__,__LINE__),\
|
||||
memory__malloc(size))
|
||||
#define realloc(ptr, size) (set_module(__FILE__,__LINE__),\
|
||||
memory__realloc((char *) ptr, size))
|
||||
#define calloc(nelem, elsize) (set_module(__FILE__,__LINE__),\
|
||||
memory__calloc(nelem, elsize))
|
||||
#define free(ptr) (set_module(__FILE__,__LINE__),\
|
||||
memory__free((char *) ptr))
|
||||
#else
|
||||
|
||||
#define malloc(size) memory__malloc(size)
|
||||
#define realloc(ptr, size) memory__realloc((char *) ptr, size)
|
||||
#define calloc(nelem, elsize) memory__calloc(nelem, elsize)
|
||||
#define free(ptr) memory__free((char *) ptr)
|
||||
|
||||
#endif /* DEBUG_MEMORY */
|
||||
|
||||
#endif /* memory__PROVIDER */
|
||||
|
||||
#endif /* memory_MODULE */
|
||||
|
||||
#endif /* SABER */
|
193
zwgc/new_string.c
Normal file
193
zwgc/new_string.c
Normal file
@ -0,0 +1,193 @@
|
||||
/* 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>
|
||||
#include "new_string.h"
|
||||
|
||||
#if (!defined(lint) && !defined(SABER))
|
||||
static const char rcsid_new_string_c[] = "$Id$";
|
||||
#endif
|
||||
|
||||
/*
|
||||
* string - a module providing operations on C strings. (i.e., char *'s)
|
||||
*
|
||||
* Overview:
|
||||
*
|
||||
* A string is a standard C string. I.e., a char pointer to a
|
||||
* null-terminated sequence of characters. 0 is NOT considered a valid
|
||||
* string! Various operations are available. See the string_spec file
|
||||
* for details.
|
||||
*
|
||||
* Note: This module assumes that malloc NEVER returns 0 for reasonable
|
||||
* requests. It is the users responsibility to either ensure that
|
||||
* this happens or supply a version of malloc with error
|
||||
* handling.
|
||||
*
|
||||
* Some strings are mutable.
|
||||
*/
|
||||
|
||||
#ifdef DEBUG
|
||||
#define assert(x) if (!(x)) abort()
|
||||
#else
|
||||
#define assert(x)
|
||||
#endif
|
||||
|
||||
#include "new_memory.h"
|
||||
|
||||
/*
|
||||
* string string_CreateFromData(char *data, int length):
|
||||
* Requires: data[0], data[1], ..., data[length-1] != 0
|
||||
* Effects: Takes the first length characters at data and
|
||||
* creates a string containing them. The returned string
|
||||
* is on the heap & must be freed eventually.
|
||||
* I.e., if passed "foobar" and 3, it would return
|
||||
* string_Copy("foo").
|
||||
*/
|
||||
|
||||
string string__CreateFromData(char *data, int length)
|
||||
{
|
||||
string result;
|
||||
|
||||
assert(length>=0);
|
||||
|
||||
result = (string)malloc(length+1);
|
||||
assert(result);
|
||||
|
||||
(void) memcpy(result, data, length);
|
||||
result[length] = 0;
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
/*
|
||||
* string string_Copy(string s):
|
||||
* Effects: Returns a copy of s on the heap. The copy must be
|
||||
* freed eventually.
|
||||
*/
|
||||
|
||||
string
|
||||
string__Copy(string s)
|
||||
{
|
||||
int length;
|
||||
string result;
|
||||
|
||||
assert(s);
|
||||
|
||||
length = string_Length(s)+1;
|
||||
result = (string)malloc(length);
|
||||
assert(result);
|
||||
|
||||
(void) memcpy(result, s, length);
|
||||
return(result);
|
||||
}
|
||||
|
||||
/*
|
||||
* string string_Concat(string a, b):
|
||||
* Effects: Returns a string equal to a concatenated to b.
|
||||
* The returned string is on the heap and must be
|
||||
* freed eventually. I.e., given "abc" and "def",
|
||||
* returns string_Copy("abcdef").
|
||||
*/
|
||||
|
||||
string
|
||||
string__Concat(string a,
|
||||
string b)
|
||||
{
|
||||
string result;
|
||||
int a_length, b_size, result_size;
|
||||
|
||||
a_length = string_Length(a);
|
||||
b_size = string_Length(b)+1;
|
||||
result_size = a_length+b_size;
|
||||
result = (string)malloc(result_size);
|
||||
assert(result);
|
||||
|
||||
(void) memcpy(result, a, a_length);
|
||||
(void) memcpy(result+a_length, b, b_size);
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
/*
|
||||
* string string_Concat2(string a, b):
|
||||
* Modifies: a
|
||||
* Requires: a is on the heap, b does not point into a.
|
||||
* Effects: Equivalent to:
|
||||
* string temp;
|
||||
* temp = string_Concat(a,b);
|
||||
* free(a);
|
||||
* return(temp);
|
||||
* only faster. I.e., uses realloc instead of malloc+memcpy.
|
||||
*/
|
||||
|
||||
string
|
||||
string__Concat2(string a,
|
||||
string b)
|
||||
{
|
||||
int a_length = string_Length(a);
|
||||
int b_size = string_Length(b)+1;
|
||||
|
||||
#ifdef DEBUG_MEMORY
|
||||
assert(memory__on_heap_p(a));
|
||||
#endif
|
||||
|
||||
a = (string)realloc(a, a_length+b_size);
|
||||
assert(a);
|
||||
(void) memcpy(a+a_length, b, b_size);
|
||||
|
||||
return(a);
|
||||
}
|
||||
|
||||
/*
|
||||
* string string_Downcase(string s):
|
||||
* Modifies: s
|
||||
* Effects: Modifies s by changing every uppercase character in s
|
||||
* to the corresponding lowercase character. Nothing else
|
||||
* is changed. I.e., "FoObAr19." is changed to "foobar19.".
|
||||
* S is returned as a convenience.
|
||||
*/
|
||||
|
||||
string
|
||||
string_Downcase(string s)
|
||||
{
|
||||
char *ptr;
|
||||
|
||||
for (ptr=s; *ptr; ptr++) {
|
||||
if (isupper(*ptr))
|
||||
*ptr = tolower(*ptr);
|
||||
}
|
||||
|
||||
return(s);
|
||||
}
|
||||
|
||||
/*
|
||||
* string string_Upcase(string s):
|
||||
* Modifies: s
|
||||
* Effects: Modifies s by changing every lowercase character in s
|
||||
* to the corresponding uppercase character. Nothing else
|
||||
* is changed. I.e., "FoObAr19." is changed to "FOOBAR19.".
|
||||
* S is returned as a convenience.
|
||||
*/
|
||||
|
||||
string
|
||||
string_Upcase(string s)
|
||||
{
|
||||
char *ptr;
|
||||
|
||||
for (ptr=s; *ptr; ptr++) {
|
||||
if (islower(*ptr))
|
||||
*ptr = toupper(*ptr);
|
||||
}
|
||||
|
||||
return(s);
|
||||
}
|
134
zwgc/new_string.h
Normal file
134
zwgc/new_string.h
Normal file
@ -0,0 +1,134 @@
|
||||
/* 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".
|
||||
*/
|
||||
|
||||
#ifndef string_TYPE
|
||||
#define string_TYPE
|
||||
|
||||
#include <string.h>
|
||||
#include "new_memory.h"
|
||||
|
||||
typedef char *string;
|
||||
|
||||
/*
|
||||
* int string_Length(string s):
|
||||
* Effects: Returns the number of non-null characters in s.
|
||||
*/
|
||||
|
||||
#define string_Length(s) strlen(s)
|
||||
|
||||
/*
|
||||
* int string_Eq(string a, b):
|
||||
* Effects: Returns true iff strings a & b are equal. I.e., have the
|
||||
* same character contents.
|
||||
*/
|
||||
|
||||
#define string_Eq(a,b) (!strcmp(a,b))
|
||||
|
||||
/*
|
||||
* int string_Neq(string a, b):
|
||||
* Effects: Returns true iff strings a & b are not equal.
|
||||
*/
|
||||
|
||||
#define string_Neq(a,b) (strcmp(a,b))
|
||||
|
||||
/*
|
||||
* string string_CreateFromData(char *data, int length):
|
||||
* Requires: data[0], data[1], ..., data[length-1] != 0
|
||||
* Effects: Takes the first length characters at data and
|
||||
* creates a string containing them. The returned string
|
||||
* is on the heap & must be freed eventually.
|
||||
* I.e., if passed "foobar" and 3, it would return
|
||||
* string_Copy("foo").
|
||||
*/
|
||||
|
||||
extern string string__CreateFromData(char *, int);
|
||||
#ifdef DEBUG_MEMORY
|
||||
#define string_CreateFromData(data,length) (set_module(__FILE__,__LINE__),\
|
||||
string__CreateFromData(data,length))
|
||||
#else
|
||||
#define string_CreateFromData(data,length) string__CreateFromData(data,length)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* string string_Copy(string s):
|
||||
* Effects: Returns a copy of s on the heap. The copy must be
|
||||
* freed eventually.
|
||||
*/
|
||||
|
||||
extern string string__Copy(string);
|
||||
#ifdef DEBUG_MEMORY
|
||||
#define string_Copy(data) (set_module(__FILE__,__LINE__),\
|
||||
string__Copy(data))
|
||||
#else
|
||||
#define string_Copy(data) string__Copy(data)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* string string_Concat(string a, b):
|
||||
* Effects: Returns a string equal to a concatenated to b.
|
||||
* The returned string is on the heap and must be
|
||||
* freed eventually. I.e., given "abc" and "def",
|
||||
* returns string_Copy("abcdef").
|
||||
*/
|
||||
|
||||
extern string string__Concat(string, string);
|
||||
#ifdef DEBUG_MEMORY
|
||||
#define string_Concat(a,b) (set_module(__FILE__,__LINE__),\
|
||||
string__Concat(a,b))
|
||||
#else
|
||||
#define string_Concat(a,b) string__Concat(a,b)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* string string_Concat2(string a, b):
|
||||
* Modifies: a
|
||||
* Requires: a is on the heap, b does not point into a.
|
||||
* Effects: Equivalent to:
|
||||
* string temp;
|
||||
* temp = string_Concat(a,b);
|
||||
* free(a);
|
||||
* return(temp);
|
||||
* only faster. I.e., uses realloc instead of malloc+bcopy.
|
||||
*/
|
||||
|
||||
extern string string__Concat2(string, string);
|
||||
#ifdef DEBUG_MEMORY
|
||||
#define string_Concat2(a,b) (set_module(__FILE__,__LINE__),\
|
||||
string__Concat2(a,b))
|
||||
#else
|
||||
#define string_Concat2(a,b) string__Concat2(a,b)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* string string_Downcase(string s):
|
||||
* Modifies: s
|
||||
* Effects: Modifies s by changing every uppercase character in s
|
||||
* to the corresponding lowercase character. Nothing else
|
||||
* is changed. I.e., "FoObAr19." is changed to "foobar19.".
|
||||
* S is returned as a convenience.
|
||||
*/
|
||||
|
||||
extern string string_Downcase(string);
|
||||
|
||||
/*
|
||||
* string string_Upcase(string s):
|
||||
* Modifies: s
|
||||
* Effects: Modifies s by changing every lowercase character in s
|
||||
* to the corresponding uppercase character. Nothing else
|
||||
* is changed. I.e., "FoObAr19." is changed to "FOOBAR19.".
|
||||
* S is returned as a convenience.
|
||||
*/
|
||||
|
||||
extern string string_Upcase(string);
|
||||
|
||||
#endif
|
331
zwgc/node.c
Normal file
331
zwgc/node.c
Normal file
@ -0,0 +1,331 @@
|
||||
/* 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_node_c[] = "$Id$";
|
||||
#endif
|
||||
|
||||
#include <zephyr/mit-copyright.h>
|
||||
|
||||
#include "new_memory.h"
|
||||
#include "node.h"
|
||||
|
||||
/****************************************************************************/
|
||||
/* */
|
||||
/* Internal node construction & destruction functions: */
|
||||
/* */
|
||||
/****************************************************************************/
|
||||
|
||||
/*
|
||||
* NODE_BATCH_SIZE - the number of nodes to malloc at once to save overhead:
|
||||
*/
|
||||
|
||||
#define NODE_BATCH_SIZE 100
|
||||
|
||||
/*
|
||||
* The nodes we have malloced are kept in a linked list of bunches of
|
||||
* NODE_BATCH_SIZE nodes. Nodes points to the first bunch on the list
|
||||
* and current_bunch to the last. All nodes from the first one in the first
|
||||
* bunch to the last_node_in_current_bunch_used'th one in the last bunch
|
||||
* are in use. The others have not been used yet.
|
||||
*/
|
||||
|
||||
static struct _bunch_of_nodes {
|
||||
struct _bunch_of_nodes *next_bunch;
|
||||
Node nodes[NODE_BATCH_SIZE];
|
||||
} *nodes = NULL;
|
||||
static struct _bunch_of_nodes *current_bunch = NULL;
|
||||
static int last_node_in_current_bunch_used = -1;
|
||||
|
||||
/*
|
||||
* Internal Routine:
|
||||
*
|
||||
* Node *node_create(int opcode)
|
||||
* Effects: Creates a node with opcode opcode and returns a pointer
|
||||
* to it. The next pointer of the returned node is NULL.
|
||||
* If the opcode is STRING_CONSTANT_OPCODE the caller must
|
||||
* ensure that the string_constant field points to a valid
|
||||
* string on the heap when node_DestroyAllNodes is called.
|
||||
*/
|
||||
|
||||
static Node *
|
||||
node_create(int opcode)
|
||||
{
|
||||
Node *result;
|
||||
|
||||
if (!nodes) {
|
||||
/*
|
||||
* Handle special case where no nodes allocated yet:
|
||||
*/
|
||||
current_bunch = nodes = (struct _bunch_of_nodes *)
|
||||
malloc(sizeof(struct _bunch_of_nodes));
|
||||
nodes->next_bunch = NULL;
|
||||
last_node_in_current_bunch_used = -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* If all nodes allocated so far in use, allocate another
|
||||
* bunch of NODE_BATCH_SIZE nodes:
|
||||
*/
|
||||
if (last_node_in_current_bunch_used == NODE_BATCH_SIZE-1) {
|
||||
current_bunch->next_bunch = (struct _bunch_of_nodes *)
|
||||
malloc(sizeof(struct _bunch_of_nodes));
|
||||
current_bunch = current_bunch->next_bunch;
|
||||
current_bunch->next_bunch = NULL;
|
||||
last_node_in_current_bunch_used = -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get next not already used node & ready it for use:
|
||||
*/
|
||||
last_node_in_current_bunch_used++;
|
||||
result = &(current_bunch->nodes[last_node_in_current_bunch_used]);
|
||||
result->opcode = opcode;
|
||||
result->next = NULL;
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
void
|
||||
node_DestroyAllNodes(void)
|
||||
{
|
||||
struct _bunch_of_nodes *next_bunch;
|
||||
int i, last_node_used_in_this_bunch;
|
||||
|
||||
while (nodes) {
|
||||
next_bunch = nodes->next_bunch;
|
||||
last_node_used_in_this_bunch = next_bunch ?
|
||||
NODE_BATCH_SIZE-1 : last_node_in_current_bunch_used;
|
||||
for (i=0; i<=last_node_used_in_this_bunch; i++) {
|
||||
if (nodes->nodes[i].opcode==STRING_CONSTANT_OPCODE)
|
||||
free(nodes->nodes[i].d.string_constant);
|
||||
else if (nodes->nodes[i].opcode==VARREF_OPCODE)
|
||||
free(nodes->nodes[i].d.string_constant);
|
||||
else if (nodes->nodes[i].opcode==VARNAME_OPCODE)
|
||||
free(nodes->nodes[i].d.string_constant);
|
||||
}
|
||||
free(nodes);
|
||||
nodes = next_bunch;
|
||||
}
|
||||
|
||||
current_bunch = nodes;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
/* */
|
||||
/* Node construction functions: */
|
||||
/* */
|
||||
/****************************************************************************/
|
||||
|
||||
Node *
|
||||
node_create_string_constant(int opcode,
|
||||
string text)
|
||||
{
|
||||
Node *n;
|
||||
|
||||
n = node_create(opcode);
|
||||
n->d.string_constant = text;
|
||||
return(n);
|
||||
}
|
||||
|
||||
Node *
|
||||
node_create_noary(int opcode)
|
||||
{
|
||||
Node *n;
|
||||
|
||||
n = node_create(opcode);
|
||||
return(n);
|
||||
}
|
||||
|
||||
Node *
|
||||
node_create_unary(int opcode,
|
||||
Node *arg)
|
||||
{
|
||||
Node *n;
|
||||
|
||||
n = node_create(opcode);
|
||||
n->d.nodes.first = arg;
|
||||
return(n);
|
||||
}
|
||||
|
||||
Node *
|
||||
node_create_binary(int opcode,
|
||||
Node *first_arg,
|
||||
Node *second_arg)
|
||||
{
|
||||
Node *n;
|
||||
|
||||
n = node_create(opcode);
|
||||
n->d.nodes.first = first_arg;
|
||||
n->d.nodes.second = second_arg;
|
||||
return(n);
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
/* */
|
||||
/* Node utility functions: */
|
||||
/* */
|
||||
/****************************************************************************/
|
||||
|
||||
/*
|
||||
* Node *reverse_list_of_nodes(Node *list)
|
||||
* Modifies: the nodes on the linked list list
|
||||
* Effects: Reverses the linked list list and returns it.
|
||||
* This is done by modifing the next pointers of the
|
||||
* list elements to point to the previous node & returning
|
||||
* the address of the (previously) last node.
|
||||
*/
|
||||
|
||||
Node *
|
||||
reverse_list_of_nodes(Node *list)
|
||||
{
|
||||
Node *next_node;
|
||||
Node *head = NULL;
|
||||
|
||||
while (list) {
|
||||
next_node = list->next;
|
||||
|
||||
/*
|
||||
* Add the node list to the beginning of linked list head:
|
||||
*/
|
||||
list->next = head;
|
||||
head = list;
|
||||
|
||||
list = next_node;
|
||||
}
|
||||
|
||||
return(head);
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
/* */
|
||||
/* Node display functions: */
|
||||
/* */
|
||||
/****************************************************************************/
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
static void
|
||||
print_stuff(Node *node,
|
||||
string format_string)
|
||||
{
|
||||
char c;
|
||||
|
||||
for (c=(*(format_string++)); c; c=(*(format_string++))) {
|
||||
if (c!='%') {
|
||||
putchar(c);
|
||||
continue;
|
||||
}
|
||||
c=(*(format_string++));
|
||||
if (!c) {
|
||||
format_string--;
|
||||
continue;
|
||||
}
|
||||
if (c=='s')
|
||||
printf("%s", node->d.string_constant);
|
||||
else if (c=='1')
|
||||
node_display(node->d.nodes.first);
|
||||
else if (c=='2')
|
||||
node_display(node->d.nodes.second);
|
||||
else
|
||||
putchar(c);
|
||||
}
|
||||
}
|
||||
|
||||
static string how_to_print[] = {
|
||||
"\"%s\"", /* constant string */
|
||||
"$%s", /* varref */
|
||||
"%s", /* varname */
|
||||
|
||||
"!%1",
|
||||
|
||||
"( %1 + %2 )",
|
||||
"( %1 and %2 )",
|
||||
"( %1 or %2 )",
|
||||
"( %1 == %2 )",
|
||||
"( %1 != %2 )",
|
||||
"( %1 =~ %2 )",
|
||||
"( %1 !~ %2 )",
|
||||
|
||||
"buffer()",
|
||||
|
||||
"substitute(%1)",
|
||||
"protect(%1)",
|
||||
"verbatim(%1)",
|
||||
"stylestrip(%1)",
|
||||
"getenv(%1)",
|
||||
"upcase(%1)",
|
||||
"downcase(%1)",
|
||||
"zvar(%1)",
|
||||
"get(%1)",
|
||||
|
||||
"lany(%1, %2)",
|
||||
"rany(%1, %2)",
|
||||
"lbreak(%1, %2)",
|
||||
"rbreak(%1, %2)",
|
||||
"lspan(%1, %2)",
|
||||
"rspan(%1, %2)",
|
||||
|
||||
"noop\n",
|
||||
"set %1 = %2\n",
|
||||
"fields %1\n",
|
||||
|
||||
"print %1\n",
|
||||
"clearbuf\n",
|
||||
|
||||
"appendport %1 %2\n",
|
||||
"execport %1 %2\n",
|
||||
"inputport %1 %2\n",
|
||||
"outputport %1 %2\n",
|
||||
"put %1 %2\n",
|
||||
"closeinput %1\n",
|
||||
"closeoutput %1\n",
|
||||
"closeport %1\n",
|
||||
|
||||
"exec %1 %2\n",
|
||||
|
||||
"%1endif\n",
|
||||
"case %1\n%2endcase\n",
|
||||
"while %1 do\n%2endwhile\n",
|
||||
"break\n",
|
||||
"exit\n",
|
||||
|
||||
"if %1 then\n%2",
|
||||
"elseif %1 then\n%2",
|
||||
"else\n%2",
|
||||
"match %1\n%2",
|
||||
"default\n%2" };
|
||||
|
||||
void node_display(Node *node)
|
||||
{
|
||||
int opcode = LAST_EXPR_OPCODE + 1;
|
||||
|
||||
for (; node; node=node->next) {
|
||||
if (opcode<=LAST_EXPR_OPCODE)
|
||||
printf(" ");
|
||||
|
||||
opcode = node->opcode;
|
||||
if (opcode>=0 && opcode<NUMBER_OF_OPCODES)
|
||||
print_stuff(node, how_to_print[opcode]);
|
||||
else
|
||||
printf("[opcode %d]", opcode);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
126
zwgc/node.h
Normal file
126
zwgc/node.h
Normal file
@ -0,0 +1,126 @@
|
||||
/* 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 <zephyr/mit-copyright.h>
|
||||
|
||||
#ifndef node_MODULE
|
||||
#define node_MODULE
|
||||
|
||||
#include "new_string.h"
|
||||
|
||||
#define STRING_CONSTANT_OPCODE 0
|
||||
#define VARREF_OPCODE 1
|
||||
#define VARNAME_OPCODE 2
|
||||
|
||||
#define NOT_OPCODE 3
|
||||
|
||||
#define PLUS_OPCODE 4
|
||||
#define AND_OPCODE 5
|
||||
#define OR_OPCODE 6
|
||||
#define EQ_OPCODE 7
|
||||
#define NEQ_OPCODE 8
|
||||
#define REGEQ_OPCODE 9
|
||||
#define REGNEQ_OPCODE 10
|
||||
|
||||
#define BUFFER_OPCODE 11
|
||||
|
||||
#define SUBSTITUTE_OPCODE 12
|
||||
#define PROTECT_OPCODE 13
|
||||
#define VERBATIM_OPCODE 14
|
||||
#define STYLESTRIP_OPCODE 15
|
||||
#define GETENV_OPCODE 16
|
||||
#define UPCASE_OPCODE 17
|
||||
#define DOWNCASE_OPCODE 18
|
||||
#define ZVAR_OPCODE 19
|
||||
#define GET_OPCODE 20
|
||||
|
||||
#define LANY_OPCODE 21
|
||||
#define RANY_OPCODE 22
|
||||
#define LBREAK_OPCODE 23
|
||||
#define RBREAK_OPCODE 24
|
||||
#define LSPAN_OPCODE 25
|
||||
#define RSPAN_OPCODE 26
|
||||
|
||||
#define LAST_EXPR_OPCODE 26
|
||||
|
||||
#define NOOP_OPCODE 27
|
||||
#define SET_OPCODE 28
|
||||
#define FIELDS_OPCODE 29
|
||||
|
||||
#define PRINT_OPCODE 30
|
||||
#define CLEARBUF_OPCODE 31
|
||||
|
||||
#define APPENDPORT_OPCODE 32
|
||||
#define EXECPORT_OPCODE 33
|
||||
#define INPUTPORT_OPCODE 34
|
||||
#define OUTPUTPORT_OPCODE 35
|
||||
#define PUT_OPCODE 36
|
||||
#define CLOSEINPUT_OPCODE 37
|
||||
#define CLOSEOUTPUT_OPCODE 38
|
||||
#define CLOSEPORT_OPCODE 39
|
||||
|
||||
#define EXEC_OPCODE 40
|
||||
|
||||
#define IF_STMT_OPCODE 41
|
||||
#define CASE_OPCODE 42
|
||||
#define WHILE_OPCODE 43
|
||||
#define BREAK_OPCODE 44
|
||||
#define EXIT_OPCODE 45
|
||||
|
||||
#define IF_OPCODE 46
|
||||
#define ELSEIF_OPCODE 47
|
||||
#define ELSE_OPCODE 48
|
||||
#define MATCHLIST_OPCODE 49
|
||||
#define DEFAULT_OPCODE 50
|
||||
|
||||
#define NUMBER_OF_OPCODES 51
|
||||
|
||||
typedef struct _Node {
|
||||
int opcode; /* Read-only */
|
||||
struct _Node *next;
|
||||
union {
|
||||
string string_constant;
|
||||
struct {
|
||||
struct _Node *first;
|
||||
struct _Node *second;
|
||||
} nodes;
|
||||
} d;
|
||||
} Node;
|
||||
|
||||
/* Function externs */
|
||||
|
||||
extern void node_DestroyAllNodes(void);
|
||||
|
||||
extern Node *node_create_string_constant(int, string);
|
||||
|
||||
extern Node *node_create_noary(int);
|
||||
extern Node *node_create_unary(int, Node *);
|
||||
extern Node *node_create_binary(int, Node *, Node *);
|
||||
|
||||
/*
|
||||
* Node *reverse_list_of_nodes(Node *list)
|
||||
* Modifies: the nodes on the linked list list
|
||||
* Effects: Reverses the linked list list and returns it.
|
||||
* This is done by modifing the next pointers of the
|
||||
* list elements to point to the previous node & returning
|
||||
* the address of the (previously) last node.
|
||||
*/
|
||||
|
||||
extern Node *reverse_list_of_nodes(Node *);
|
||||
|
||||
#ifdef DEBUG
|
||||
extern void node_display(Node *);
|
||||
#endif
|
||||
|
||||
#endif
|
351
zwgc/notice.c
Normal file
351
zwgc/notice.c
Normal file
@ -0,0 +1,351 @@
|
||||
/* 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_notice_c[] = "$Id$";
|
||||
#endif
|
||||
|
||||
#include <zephyr/mit-copyright.h>
|
||||
|
||||
/****************************************************************************/
|
||||
/* */
|
||||
/* Module containing code to extract a notice's fields: */
|
||||
/* */
|
||||
/****************************************************************************/
|
||||
|
||||
#include <zephyr/zephyr.h>
|
||||
#include <arpa/inet.h>
|
||||
#include "new_memory.h"
|
||||
#include "error.h"
|
||||
#include "variables.h"
|
||||
#include "notice.h"
|
||||
#ifdef CMU_ZWGCPLUS
|
||||
#include <pwd.h>
|
||||
#include "plus.h"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* int count_nulls(char *data, int length)
|
||||
* Requires: length>=0
|
||||
* Effects: Returns the # of nulls in data[0]..data[length-1]
|
||||
*/
|
||||
|
||||
int
|
||||
count_nulls(char *data,
|
||||
int length)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
for (; length; data++, length--)
|
||||
if (!*data)
|
||||
count++;
|
||||
|
||||
return(count);
|
||||
}
|
||||
|
||||
/*
|
||||
* string get_next_field(char **data_p, int *length_p)
|
||||
* Requires: *length_p >= 0
|
||||
* Modifies: *data_p, *length_p
|
||||
* Effects: Treats (*data_p)[0], (*data_p)[1], ... (*data_p)[length-1]
|
||||
* as a series of null-seperated fields. This function
|
||||
* returns a copy of the first field on the heap. This
|
||||
* string must eventually be freed. Also, *data_p is
|
||||
* advanced and *length_p decreased so that another
|
||||
* call to this procedure with the same arguments will
|
||||
* return the second field. The next call will return
|
||||
* the third field, etc. "" is returned if 0 fields
|
||||
* remain. (this is the case when *length_p == 0)
|
||||
*/
|
||||
|
||||
string
|
||||
get_next_field(char **data_p,
|
||||
int *length_p)
|
||||
{
|
||||
char *data = *data_p;
|
||||
int length = *length_p;
|
||||
char *ptr;
|
||||
|
||||
for (ptr=data; length; ptr++, length--)
|
||||
if (!*ptr) {
|
||||
*data_p = ptr+1;
|
||||
*length_p = length-1;
|
||||
return(string_Copy(data));
|
||||
}
|
||||
|
||||
length = *length_p;
|
||||
*data_p = ptr;
|
||||
*length_p = 0;
|
||||
return(string_CreateFromData(data, length));
|
||||
}
|
||||
|
||||
/*
|
||||
* string get_field(char *data, int length, int num)
|
||||
* Requires: length>=0, num>0
|
||||
* Effects: Treats data[0]..data[length-1] as a series of
|
||||
* null-seperated fields. This function returns a copy of
|
||||
* the num'th field (numbered from 1 in this case) on the
|
||||
* heap. This string must eventually be freed. If there
|
||||
* is no num'th field (because num<1 or num># of fields),
|
||||
* "" is returned.
|
||||
*/
|
||||
|
||||
string get_field(char *data,
|
||||
int length,
|
||||
int num)
|
||||
{
|
||||
/*
|
||||
* While num>1 and there are fields left, skip a field & decrement num:
|
||||
*/
|
||||
while (length && num>1) {
|
||||
if (!*data)
|
||||
num--;
|
||||
length--;
|
||||
data++;
|
||||
}
|
||||
|
||||
/*
|
||||
* If any more fields left, the first field is the one we want.
|
||||
* Otherwise, there is no such field as num -- return "".
|
||||
*/
|
||||
if (length)
|
||||
return(get_next_field(&data, &length));
|
||||
else
|
||||
return(string_Copy(""));
|
||||
}
|
||||
|
||||
/*
|
||||
* string convert_nulls_to_newlines(data, length)
|
||||
* Requires: length>=0, malloc never returns NULL
|
||||
* Effects: Takes data[0]..data[length-1], converts all nulls to
|
||||
* newlines ('\n') and returns the result as a null-terminated
|
||||
* string on the heap. The returned string must eventually
|
||||
* be freed.
|
||||
*/
|
||||
|
||||
string
|
||||
convert_nulls_to_newlines(char *data,
|
||||
int length)
|
||||
{
|
||||
char *result, *ptr;
|
||||
char c;
|
||||
|
||||
result = (char *) malloc(length+1);
|
||||
result[length] = '\0';
|
||||
|
||||
for (ptr=result; length; data++, ptr++, length--)
|
||||
*ptr = (c = *data) ? c : '\n';
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Internal Routine:
|
||||
*
|
||||
* string z_kind_to_ascii(ZNotice_Kind_t z_kind)
|
||||
* Effects: Returns an ascii representation for z_kind.
|
||||
* The string returned is on the heap and must be freed
|
||||
* eventually.
|
||||
*/
|
||||
|
||||
static string
|
||||
z_kind_to_ascii(ZNotice_Kind_t z_kind)
|
||||
{
|
||||
string result;
|
||||
|
||||
switch (z_kind) {
|
||||
case UNSAFE:
|
||||
result = "unsafe";
|
||||
break;
|
||||
|
||||
case UNACKED:
|
||||
result = "unacked";
|
||||
break;
|
||||
|
||||
case ACKED:
|
||||
result = "acked";
|
||||
break;
|
||||
|
||||
case HMACK:
|
||||
result = "hmack";
|
||||
break;
|
||||
|
||||
case HMCTL:
|
||||
result = "hmctl";
|
||||
break;
|
||||
|
||||
case SERVACK:
|
||||
result = "servack";
|
||||
break;
|
||||
|
||||
case SERVNAK:
|
||||
result = "servnak";
|
||||
break;
|
||||
|
||||
case CLIENTACK:
|
||||
result = "clientack";
|
||||
break;
|
||||
|
||||
case STAT:
|
||||
result = "stat";
|
||||
break;
|
||||
|
||||
default:
|
||||
result = "<unknown kind>";
|
||||
break;
|
||||
}
|
||||
|
||||
return(string_Copy(result));
|
||||
}
|
||||
|
||||
/*
|
||||
* Internal Routine:
|
||||
*
|
||||
* string z_auth_to_ascii(int z_auth)
|
||||
* Effects: Returns an ascii representation for z_auth.
|
||||
* The string returned is on the heap and must be freed
|
||||
* eventually.
|
||||
*/
|
||||
|
||||
static string
|
||||
z_auth_to_ascii(int z_auth)
|
||||
{
|
||||
string result;
|
||||
|
||||
switch (z_auth) {
|
||||
case ZAUTH_FAILED:
|
||||
result = "forged";
|
||||
break;
|
||||
|
||||
case ZAUTH_NO:
|
||||
result = "no";
|
||||
break;
|
||||
|
||||
case ZAUTH_YES:
|
||||
result = "yes";
|
||||
break;
|
||||
|
||||
default:
|
||||
result = "unknown";
|
||||
break;
|
||||
}
|
||||
|
||||
return(string_Copy(result));
|
||||
}
|
||||
|
||||
/*
|
||||
* char *decode_notice(ZNotice_t *notice)
|
||||
* Modifies: various description language variables
|
||||
* Effects:
|
||||
*/
|
||||
|
||||
char *
|
||||
decode_notice(ZNotice_t *notice,
|
||||
char *hostname)
|
||||
{
|
||||
char *temp;
|
||||
string when, notyear, year, date_string, time_string;
|
||||
|
||||
/*
|
||||
* Convert useful notice fields to ascii and store away in
|
||||
* description language variables for later use by the
|
||||
* the user's program:
|
||||
*/
|
||||
var_set_variable("zephyr_version", notice->z_version);
|
||||
var_set_variable("class", notice->z_class);
|
||||
var_set_variable("instance", notice->z_class_inst);
|
||||
var_set_variable("opcode", notice->z_opcode);
|
||||
var_set_variable("default", notice->z_default_format);
|
||||
var_set_variable("notice_charset", (char *)ZCharsetToString(notice->z_charset)); /*XXX const*/
|
||||
var_set_variable("recipient",
|
||||
(notice->z_recipient[0] ? notice->z_recipient : "*"));
|
||||
var_set_variable("fullsender", notice->z_sender);
|
||||
var_set_variable_to_number("port", (int)ntohs(notice->z_port));
|
||||
var_set_variable_then_free_value("kind", z_kind_to_ascii(notice->z_kind));
|
||||
var_set_variable_then_free_value("auth", z_auth_to_ascii(notice->z_auth));
|
||||
|
||||
#ifdef CMU_ZWGCPLUS
|
||||
if ((temp=getSelectedText()) != 0)
|
||||
var_set_variable("selection", temp);
|
||||
|
||||
var_set_variable("delete_window", "none");
|
||||
var_set_variable("event_time", "none");
|
||||
var_set_variable("event_name", "event");
|
||||
#endif
|
||||
/*
|
||||
* Set $sender to the name of the notice sender except first strip off the
|
||||
* realm name if it is the local realm:
|
||||
*/
|
||||
if ( (temp=strchr(notice->z_sender,'@')) && string_Eq(temp+1, ZGetRealm()) )
|
||||
var_set_variable_then_free_value("sender",
|
||||
string_CreateFromData(notice->z_sender,
|
||||
temp-notice->z_sender));
|
||||
else
|
||||
var_set_variable("sender", notice->z_sender);
|
||||
#ifdef CMU_ZWGCPLUS
|
||||
if (get_full_names) {
|
||||
struct passwd *pwnam = getpwnam(var_get_variable("sender"));
|
||||
if (pwnam) {
|
||||
temp = string_Copy(pwnam->pw_gecos);
|
||||
var_set_variable_then_free_value("sendername", temp);
|
||||
} else {
|
||||
var_set_variable("sendername", "unknown");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Convert time & date notice was sent to ascii. The $time
|
||||
* has the format "01:03:52" while $date has the format
|
||||
* "Sun Sep 16 1973".
|
||||
*/
|
||||
{
|
||||
/* the fields of struct timeval might not be the right type to pass
|
||||
to ctime, so use a temporary */
|
||||
time_t sec = notice->z_time.tv_sec;
|
||||
when = ctime(&sec);
|
||||
}
|
||||
time_string = string_CreateFromData(when+11,8);
|
||||
var_set_variable_then_free_value("time", time_string);
|
||||
date_string = string_Concat(notyear=string_CreateFromData(when,11),
|
||||
year=string_CreateFromData(when+20,4));
|
||||
var_set_variable_then_free_value("date", date_string);
|
||||
free(notyear);
|
||||
free(year);
|
||||
|
||||
/*
|
||||
* Convert host notice sent from to ascii:
|
||||
*/
|
||||
var_set_variable("fromhost", hostname ? hostname :
|
||||
inet_ntoa(notice->z_sender_addr));
|
||||
|
||||
/*
|
||||
* Set $message to the message field of the notice with nulls changed
|
||||
* to newlines:
|
||||
*/
|
||||
var_set_variable_then_free_value("message",
|
||||
convert_nulls_to_newlines(notice->z_message,
|
||||
notice->z_message_len));
|
||||
|
||||
/*
|
||||
* Decide if its a control notice. If so, return the notice's
|
||||
* opcode. Otherwise, return NULL:
|
||||
*/
|
||||
if ((strcasecmp(notice->z_class, WG_CTL_CLASS)==0) && /* <<<>>> */
|
||||
(strcasecmp(notice->z_class_inst, WG_CTL_USER)==0))
|
||||
return(notice->z_opcode);
|
||||
return(0);
|
||||
}
|
75
zwgc/notice.h
Normal file
75
zwgc/notice.h
Normal file
@ -0,0 +1,75 @@
|
||||
/* 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 <zephyr/mit-copyright.h>
|
||||
|
||||
#ifndef notice_MODULE
|
||||
#define notice_MODULE
|
||||
|
||||
#include <zephyr/zephyr.h>
|
||||
#include "new_string.h"
|
||||
|
||||
/*
|
||||
* int count_nulls(char *data, int length)
|
||||
* Requires: length>=0
|
||||
* Effects: Returns the # of nulls in data[0]..data[length-1]
|
||||
*/
|
||||
|
||||
extern int count_nulls(char *, int);
|
||||
|
||||
/*
|
||||
* string get_next_field(char **data_p, int *length_p)
|
||||
* Requires: *length_p >= 0
|
||||
* Modifies: *data_p, *length_p
|
||||
* Effects: Treats (*data_p)[0], (*data_p)[1], ... (*data_p)[length-1]
|
||||
* as a series of null-seperated fields. This function
|
||||
* returns a copy of the first field on the heap. This
|
||||
* string must eventually be freed. Also, *data_p is
|
||||
* advanced and *length_p decreased so that another
|
||||
* call to this procedure with the same arguments will
|
||||
* return the second field. The next call will return
|
||||
* the third field, etc. "" is returned if 0 fields
|
||||
* remain. (this is the case when *length_p == 0)
|
||||
*/
|
||||
|
||||
extern string get_next_field(char **, int *);
|
||||
|
||||
/*
|
||||
* string get_field(char *data, int length, int num)
|
||||
* Requires: length>=0, num>0
|
||||
* Effects: Treats data[0]..data[length-1] as a series of
|
||||
* null-seperated fields. This function returns a copy of
|
||||
* the num'th field (numbered from 1 in this case) on the
|
||||
* heap. This string must eventually be freed. If there
|
||||
* is no num'th field (because num<1 or num># of fields),
|
||||
* "" is returned.
|
||||
*/
|
||||
|
||||
extern string get_field(char *, int, int);
|
||||
|
||||
/*
|
||||
* string convert_nulls_to_newlines(data, length)
|
||||
* Requires: length>=0, malloc never returns NULL
|
||||
* Effects: Takes data[0]..data[length-1], converts all nulls to
|
||||
* newlines ('\n') and returns the result as a null-terminated
|
||||
* string on the heap. The returned string must eventually
|
||||
* be freed.
|
||||
*/
|
||||
|
||||
extern string convert_nulls_to_newlines(char *, int);
|
||||
|
||||
|
||||
extern char *decode_notice(ZNotice_t *, char *);
|
||||
|
||||
#endif
|
54
zwgc/parser.h
Normal file
54
zwgc/parser.h
Normal file
@ -0,0 +1,54 @@
|
||||
/* 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 <zephyr/mit-copyright.h>
|
||||
|
||||
#ifndef parser_MODULE
|
||||
#define parser_MODULE
|
||||
|
||||
/*
|
||||
* Parser-Lexer Internal Routine:
|
||||
*
|
||||
* void report_parse_error(char *error_message, int line_number)
|
||||
* Modifies: error_occured, stderr
|
||||
* Effects: This routine is called to report a parser or lexer
|
||||
* error. Error_message is the error message and line_number
|
||||
* the line number it occured on. The reported error message
|
||||
* is of the form "....<error_message> on line <line #>.\n".
|
||||
* This routine sets error_occured (local to parser.y) to
|
||||
* true. If it was previously false, the error message
|
||||
* is reported to the user via stderr.
|
||||
*/
|
||||
|
||||
extern void report_parse_error(char *, int);
|
||||
|
||||
/*
|
||||
* struct _Node *parse_file(FILE *input_file)
|
||||
* Requires: input_file is opened for reading, no pointers to
|
||||
* existing nodes will ever be dereferened.
|
||||
* Modifies: *input_file, stderr, all existing nodes
|
||||
* Effects: First this routine destroys all nodes. Then it parses
|
||||
* input_file as a zwgc description langauge file. If
|
||||
* an error is encountered, an error message is printed
|
||||
* on stderr and NULL is returned. If no error is
|
||||
* encountered, a pointer to the node representation of
|
||||
* the parsed program is returned, suitable for passing to
|
||||
* exec.c. Note that NULL will also be returned for a
|
||||
* empty file & is a valid program. Either way, input_file
|
||||
* is closed before this routine returns.
|
||||
*/
|
||||
|
||||
extern struct _Node *parse_file(FILE *);
|
||||
|
||||
#endif
|
387
zwgc/parser.y
Normal file
387
zwgc/parser.y
Normal file
@ -0,0 +1,387 @@
|
||||
%{
|
||||
/* 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_parser_y[] = "$Id$";
|
||||
#endif
|
||||
|
||||
#include <zephyr/mit-copyright.h>
|
||||
|
||||
/* Saber-C suppressions because yacc loses */
|
||||
|
||||
/*SUPPRESS 288*/
|
||||
/*SUPPRESS 287*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "lexer.h"
|
||||
#include "parser.h"
|
||||
#include "node.h"
|
||||
#include "zwgc.h"
|
||||
|
||||
static void yyerror(char *);
|
||||
|
||||
/*
|
||||
* the_program - local variable used to communicate the program's node
|
||||
* representation from the program action to the parse_file
|
||||
* function.
|
||||
*/
|
||||
|
||||
static Node *the_program;
|
||||
%}
|
||||
|
||||
%union{
|
||||
char *text;
|
||||
struct _Node *node;
|
||||
}
|
||||
|
||||
%start program
|
||||
|
||||
%token ERROR
|
||||
%token <text> VARNAME VARREF STRING SHOW
|
||||
|
||||
%token APPENDPORT BUFFER BREAK CLOSEINPUT CLOSEOUTPUT
|
||||
%token CLOSEPORT CASE CLEARBUF DEFAULT DISPLAY DO DOWNCASE
|
||||
%token ELSE ELSEIF ENDCASE ENDIF ENDWHILE EXEC EXECPORT EXIT
|
||||
%token FIELDS GET GETENV IF INPUTPORT LANY LBREAK LSPAN
|
||||
%token MATCH NOOP NOT OUTPUTPORT PRINT PROTECT VERBATIM PUT RANY RBREAK
|
||||
%token RSPAN SET SUBSTITUTE THEN UPCASE WHILE ZVAR STYLESTRIP
|
||||
|
||||
%type <node> expr varname string
|
||||
%type <node> exprlist comma_exprlist varnamelist
|
||||
%type <node> statement statements program elseparts elseifparts
|
||||
%type <node> match matchlist
|
||||
|
||||
%left '|'
|
||||
%left '&'
|
||||
%left EQ NEQ REGEQ REGNEQ
|
||||
%left '+'
|
||||
%left '!'
|
||||
|
||||
%%
|
||||
|
||||
/*
|
||||
* A program is simply a list of statements: (may be NULL if no statements...)
|
||||
*/
|
||||
program : statements
|
||||
{ the_program = reverse_list_of_nodes($1);
|
||||
$$ = the_program; }
|
||||
;
|
||||
|
||||
varname : VARNAME
|
||||
{ $$ = node_create_string_constant(VARNAME_OPCODE, $1); }
|
||||
;
|
||||
|
||||
string : STRING
|
||||
{ $$ = node_create_string_constant(STRING_CONSTANT_OPCODE, $1); }
|
||||
;
|
||||
|
||||
expr : '(' expr ')'
|
||||
{ $$ = $2; }
|
||||
|
||||
| string
|
||||
{ $$ = $1; }
|
||||
| VARREF
|
||||
{ $$ = node_create_string_constant(VARREF_OPCODE, $1); }
|
||||
|
||||
| '!' expr
|
||||
{ $$ = node_create_unary(NOT_OPCODE, $2); }
|
||||
|
||||
| expr '+' expr
|
||||
{ $$ = node_create_binary(PLUS_OPCODE, $1, $3); }
|
||||
| expr '|' expr /* note "or" == '|' */
|
||||
{ $$ = node_create_binary(OR_OPCODE, $1, $3); }
|
||||
| expr '&' expr /* note "and" == '&' */
|
||||
{ $$ = node_create_binary(AND_OPCODE, $1, $3); }
|
||||
| expr EQ expr
|
||||
{ $$ = node_create_binary(EQ_OPCODE, $1, $3); }
|
||||
| expr NEQ expr
|
||||
{ $$ = node_create_binary(NEQ_OPCODE, $1, $3); }
|
||||
| expr REGEQ expr
|
||||
{ $$ = node_create_binary(REGEQ_OPCODE, $1, $3); }
|
||||
| expr REGNEQ expr
|
||||
{ $$ = node_create_binary(REGNEQ_OPCODE, $1, $3); }
|
||||
|
||||
| BUFFER '(' ')'
|
||||
{ $$ = node_create_noary(BUFFER_OPCODE); }
|
||||
|
||||
| SUBSTITUTE '(' expr ')'
|
||||
{ $$ = node_create_unary(SUBSTITUTE_OPCODE, $3); }
|
||||
| PROTECT '(' expr ')'
|
||||
{ $$ = node_create_unary(PROTECT_OPCODE, $3); }
|
||||
| VERBATIM '(' expr ')'
|
||||
{ $$ = node_create_unary(VERBATIM_OPCODE, $3); }
|
||||
| GETENV '(' expr ')'
|
||||
{ $$ = node_create_unary(GETENV_OPCODE, $3); }
|
||||
| UPCASE '(' expr ')'
|
||||
{ $$ = node_create_unary(UPCASE_OPCODE, $3); }
|
||||
| DOWNCASE '(' expr ')'
|
||||
{ $$ = node_create_unary(DOWNCASE_OPCODE, $3); }
|
||||
| ZVAR '(' expr ')'
|
||||
{ $$ = node_create_unary(ZVAR_OPCODE, $3); }
|
||||
| GET '(' expr ')'
|
||||
{ $$ = node_create_unary(GET_OPCODE, $3); }
|
||||
| STYLESTRIP '(' expr ')'
|
||||
{ $$ = node_create_unary(STYLESTRIP_OPCODE, $3); }
|
||||
|
||||
| LANY '(' expr ',' expr ')'
|
||||
{ $$ = node_create_binary(LANY_OPCODE, $3, $5 ); }
|
||||
| RANY '(' expr ',' expr ')'
|
||||
{ $$ = node_create_binary(RANY_OPCODE, $3, $5 ); }
|
||||
| LBREAK '(' expr ',' expr ')'
|
||||
{ $$ = node_create_binary(LBREAK_OPCODE, $3, $5 ); }
|
||||
| RBREAK '(' expr ',' expr ')'
|
||||
{ $$ = node_create_binary(RBREAK_OPCODE, $3, $5 ); }
|
||||
| LSPAN '(' expr ',' expr ')'
|
||||
{ $$ = node_create_binary(LSPAN_OPCODE, $3, $5 ); }
|
||||
| RSPAN '(' expr ',' expr ')'
|
||||
{ $$ = node_create_binary(RSPAN_OPCODE, $3, $5 ); }
|
||||
;
|
||||
|
||||
statement : NOOP
|
||||
{ $$ = node_create_noary(NOOP_OPCODE); }
|
||||
| SET varname '=' expr
|
||||
{ $$ = node_create_binary(SET_OPCODE, $2, $4); }
|
||||
| FIELDS varnamelist
|
||||
{ $$ = node_create_unary(FIELDS_OPCODE,
|
||||
reverse_list_of_nodes($2)); }
|
||||
|
||||
/*
|
||||
* Output to & control of output buffer statements:
|
||||
*/
|
||||
| PRINT exprlist
|
||||
{ $$ = node_create_unary(PRINT_OPCODE,
|
||||
reverse_list_of_nodes($2)); }
|
||||
| SHOW
|
||||
{ $$ = node_create_unary(PRINT_OPCODE,
|
||||
node_create_unary(SUBSTITUTE_OPCODE,
|
||||
node_create_string_constant(STRING_CONSTANT_OPCODE,
|
||||
$1))); }
|
||||
| CLEARBUF
|
||||
{ $$ = node_create_noary(CLEARBUF_OPCODE); }
|
||||
|
||||
/*
|
||||
* Statements to manage ports:
|
||||
*/
|
||||
| APPENDPORT expr expr
|
||||
{ $$ = node_create_binary(APPENDPORT_OPCODE, $2, $3); }
|
||||
| EXECPORT expr expr exprlist
|
||||
{ $3->next = reverse_list_of_nodes($4);
|
||||
$$ = node_create_binary(EXECPORT_OPCODE, $2, $3); }
|
||||
| INPUTPORT expr expr
|
||||
{ $$ = node_create_binary(INPUTPORT_OPCODE, $2, $3); }
|
||||
| OUTPUTPORT expr expr
|
||||
{ $$ = node_create_binary(OUTPUTPORT_OPCODE, $2, $3); }
|
||||
| PUT expr exprlist
|
||||
{ $$ = node_create_binary(PUT_OPCODE, $2,
|
||||
reverse_list_of_nodes($3)); }
|
||||
| PUT
|
||||
{ $$ = node_create_binary(PUT_OPCODE, 0, 0); }
|
||||
| CLOSEINPUT expr
|
||||
{ $$ = node_create_unary(CLOSEINPUT_OPCODE, $2); }
|
||||
| CLOSEOUTPUT expr
|
||||
{ $$ = node_create_unary(CLOSEOUTPUT_OPCODE, $2); }
|
||||
| CLOSEPORT expr
|
||||
{ $$ = node_create_unary(CLOSEPORT_OPCODE, $2); }
|
||||
|
||||
/*
|
||||
* Statements to run subprocesses without I/O to them:
|
||||
*/
|
||||
| EXEC expr exprlist
|
||||
{ $2->next = reverse_list_of_nodes($3);
|
||||
$$ = node_create_unary(EXEC_OPCODE, $2); }
|
||||
|
||||
/*
|
||||
* Control statements:
|
||||
*/
|
||||
| IF expr THEN statements elseparts ENDIF
|
||||
{ Node *n = node_create_binary(IF_OPCODE, $2,
|
||||
reverse_list_of_nodes($4));
|
||||
n->next = $5;
|
||||
$$ = node_create_unary(IF_STMT_OPCODE, n); }
|
||||
| CASE expr matchlist ENDCASE
|
||||
{ $$ = node_create_binary(CASE_OPCODE, $2,
|
||||
reverse_list_of_nodes($3)); }
|
||||
| WHILE expr DO statements ENDWHILE
|
||||
{ $$ = node_create_binary(WHILE_OPCODE, $2,
|
||||
reverse_list_of_nodes($4)); }
|
||||
| BREAK
|
||||
{ $$ = node_create_noary(BREAK_OPCODE); }
|
||||
| EXIT
|
||||
{ $$ = node_create_noary(EXIT_OPCODE); }
|
||||
;
|
||||
|
||||
elseparts : elseifparts
|
||||
{ $$ = reverse_list_of_nodes($1); }
|
||||
| elseifparts ELSE statements
|
||||
{ $$ = node_create_binary(ELSE_OPCODE, 0,
|
||||
reverse_list_of_nodes($3));
|
||||
$$->next = $1;
|
||||
$$ = reverse_list_of_nodes($$); }
|
||||
;
|
||||
|
||||
/* elseifparts needs to be reversed before using... */
|
||||
elseifparts : /* empty */
|
||||
{ $$ = 0; }
|
||||
| elseifparts ELSEIF expr THEN statements
|
||||
{ $$ = node_create_binary(ELSEIF_OPCODE, $3,
|
||||
reverse_list_of_nodes($5));
|
||||
$$->next = $1; }
|
||||
;
|
||||
|
||||
match : MATCH comma_exprlist statements
|
||||
{ $$ = node_create_binary(MATCHLIST_OPCODE,
|
||||
reverse_list_of_nodes($2),
|
||||
reverse_list_of_nodes($3)); }
|
||||
| DEFAULT statements
|
||||
{ $$ = node_create_binary(DEFAULT_OPCODE, 0,
|
||||
reverse_list_of_nodes($2)); }
|
||||
;
|
||||
|
||||
/*
|
||||
* Various lists of non-terminals like expr's and varname's. Each is
|
||||
* built up as a linked list using the nodes' next fields. To prevent
|
||||
* Yacc stack overflow on long lists, these are put on the linked list
|
||||
* BACKWARDS. The user of these must first call reverse_list_of_nodes
|
||||
* on one of these before using it. All except comma_exprlist
|
||||
* allow 0 elements on the list in which case their value is NULL.
|
||||
* (comma_exprlist requires at least one element)
|
||||
*/
|
||||
|
||||
exprlist : /* empty */
|
||||
{ $$ = 0; }
|
||||
| exprlist expr
|
||||
{ $$ = $2;
|
||||
$$->next = $1; }
|
||||
;
|
||||
|
||||
comma_exprlist : expr
|
||||
{ $$ = $1; }
|
||||
| comma_exprlist ',' expr
|
||||
{ $$ = $3;
|
||||
$$->next = $1; }
|
||||
;
|
||||
|
||||
varnamelist : /* empty */
|
||||
{ $$ = 0; }
|
||||
| varnamelist varname
|
||||
{ $$ = $2;
|
||||
$$->next = $1; }
|
||||
;
|
||||
|
||||
matchlist : /* empty */
|
||||
{ $$ = 0; }
|
||||
| matchlist match
|
||||
{ $$ = $2;
|
||||
$$->next = $1; }
|
||||
;
|
||||
|
||||
statements : /* empty */
|
||||
{ $$ = 0; }
|
||||
| statements statement
|
||||
{ $$ = $2;
|
||||
$$->next = $1; }
|
||||
;
|
||||
|
||||
%%
|
||||
|
||||
/*
|
||||
* error_occured - Set to true when a parse error is reported. If it is false
|
||||
* at the time a parse error is reported, a message is
|
||||
* printed on stderr. See report_parse_error for more
|
||||
* details.
|
||||
*/
|
||||
|
||||
static int error_occured = 0;
|
||||
|
||||
/*
|
||||
* Parser-Lexer Internal Routine:
|
||||
*
|
||||
* void report_parse_error(char *error_message, int line_number)
|
||||
* Modifies: error_occured, stderr
|
||||
* Effects: This routine is called to report a parser or lexer
|
||||
* error. Error_message is the error message and line_number
|
||||
* the line number it occured on. The reported error message
|
||||
* is of the form "....<error_message> on line <line #>.\n".
|
||||
* This routine sets error_occured (local to parser.y) to
|
||||
* true. If it was previously false, the error message
|
||||
* is reported to the user via stderr.
|
||||
*/
|
||||
|
||||
void
|
||||
report_parse_error(char *error_message,
|
||||
int line_number)
|
||||
{
|
||||
if (error_occured)
|
||||
return;
|
||||
error_occured = 1;
|
||||
|
||||
fprintf(stderr, "zwgc: error in description file: %s on line %d.\n",
|
||||
error_message, line_number);
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
/*
|
||||
* yyerror - internal routine - used by yacc to report syntax errors and
|
||||
* stack overflow errors.
|
||||
*/
|
||||
|
||||
static void yyerror(char *message)
|
||||
{
|
||||
report_parse_error(message, yylineno);
|
||||
}
|
||||
|
||||
/*
|
||||
* struct _Node *parse_file(FILE *input_file)
|
||||
* Requires: input_file is opened for reading, no pointers to
|
||||
* existing nodes will ever be dereferened.
|
||||
* Modifies: *input_file, stderr, all existing nodes
|
||||
* Effects: First this routine destroys all nodes. Then it parses
|
||||
* input_file as a zwgc description langauge file. If
|
||||
* an error is encountered, an error message is printed
|
||||
* on stderr and NULL is returned. If no error is
|
||||
* encountered, a pointer to the node representation of
|
||||
* the parsed program is returned, suitable for passing to
|
||||
* exec.c. Note that NULL will also be returned for a
|
||||
* empty file & is a valid program. Either way, input_file
|
||||
* is closed before this routine returns.
|
||||
*/
|
||||
|
||||
struct _Node *
|
||||
parse_file(FILE *input_file)
|
||||
{
|
||||
the_program = NULL;
|
||||
error_occured = 0;
|
||||
node_DestroyAllNodes();
|
||||
|
||||
lex_open(input_file);
|
||||
yyparse();
|
||||
fclose(input_file);
|
||||
|
||||
if (error_occured) {
|
||||
node_DestroyAllNodes();
|
||||
the_program = NULL;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
if (zwgc_debug) {
|
||||
printf("****************************************************************************\n");
|
||||
node_display(the_program);
|
||||
printf("****************************************************************************\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
return(the_program);
|
||||
}
|
499
zwgc/plus.c
Normal file
499
zwgc/plus.c
Normal file
@ -0,0 +1,499 @@
|
||||
/*
|
||||
This file contains code related to the zwgcplus extension to zwgc.
|
||||
zwgc is copyrighted by the Massachusetts Institute of Technology.
|
||||
This file is public domain.
|
||||
Written by Andrew Plotkin, ap1i+@andrew.cmu.edu
|
||||
Timequeue code added by Ryan Ingram, ryani+@andrew.cmu.edu
|
||||
Rewritten for incorporation into MIT zwgc from 2.0.2 by Derrick Brashear
|
||||
*/
|
||||
|
||||
#include <sysdep.h>
|
||||
#ifdef CMU_ZWGCPLUS
|
||||
#if (!defined(lint) && !defined(SABER))
|
||||
static const char rcsid_plus_c[] = "$Id$";
|
||||
#endif
|
||||
|
||||
#include <zephyr/mit-copyright.h>
|
||||
#include <zephyr/zephyr.h>
|
||||
|
||||
#include "new_memory.h"
|
||||
#include "node.h"
|
||||
#include "exec.h"
|
||||
#include "eval.h"
|
||||
#include "node.h"
|
||||
#include "buffer.h"
|
||||
#include "port.h"
|
||||
#include "variables.h"
|
||||
#include "notice.h"
|
||||
#include "X_gram.h"
|
||||
#include "xrevstack.h"
|
||||
#include "main.h"
|
||||
#include "plus.h"
|
||||
|
||||
int get_full_names = 0;
|
||||
|
||||
#define HASHSIZE (251)
|
||||
|
||||
typedef struct timenode_s {
|
||||
ZNotice_t *notice;
|
||||
struct timenode_s *next;
|
||||
time_t when;
|
||||
char *event_name;
|
||||
} TimeNode;
|
||||
|
||||
typedef struct _notnode {
|
||||
ZNotice_t *notice;
|
||||
int fake_notice; /* if TRUE, do not call ZFreeNotice() */
|
||||
int refcount;
|
||||
struct _notnode *next;
|
||||
char *opcode;
|
||||
char *hname;
|
||||
} notnode;
|
||||
|
||||
static ZNotice_t *stored_notice;
|
||||
static notnode *notlist[HASHSIZE];
|
||||
TimeNode *timeq_head = NULL;
|
||||
|
||||
int list_hash_fun(ZNotice_t *notice);
|
||||
|
||||
static TimeNode *
|
||||
addtimenode(TimeNode *head, TimeNode *node)
|
||||
{
|
||||
if(head == NULL) {
|
||||
#ifdef DEBUG_TIMEQUEUE
|
||||
fprintf(stderr, "adding new timenode; creating queue\n");
|
||||
#endif
|
||||
node->next = NULL;
|
||||
return node;
|
||||
}
|
||||
|
||||
if(head->when > node->when) {
|
||||
#ifdef DEBUG_TIMEQUEUE
|
||||
fprintf(stderr, "adding new timenode at start of queue\n");
|
||||
#endif
|
||||
node->next = head;
|
||||
return node;
|
||||
}
|
||||
|
||||
head->next = addtimenode(head->next, node);
|
||||
return head;
|
||||
}
|
||||
|
||||
static void
|
||||
handle_timeq_event(TimeNode *event)
|
||||
{
|
||||
char buf[128];
|
||||
notnode *pt;
|
||||
int bx = list_hash_fun(event->notice);
|
||||
|
||||
for (pt=notlist[bx]; pt && pt->notice!=event->notice; pt=pt->next);
|
||||
|
||||
/* "time-" + event_name + '\0' */
|
||||
#ifdef DEBUG_TIMEQUEUE
|
||||
fprintf(stderr, "handle_timeq_event()\n");
|
||||
#endif
|
||||
|
||||
if (strlen(event->event_name)<123)
|
||||
sprintf(buf, "time-%s", event->event_name);
|
||||
else
|
||||
sprintf(buf, "time-bogus");
|
||||
|
||||
#ifdef DEBUG_TIMEQUEUE
|
||||
fprintf(stderr, "opcode: %s\n", buf);
|
||||
#endif
|
||||
|
||||
event->notice->z_version = "zwgcplus-repeat";
|
||||
event->notice->z_opcode = buf;
|
||||
|
||||
reprocess_notice(event->notice, pt->hname);
|
||||
|
||||
#ifdef DEBUG_TIMEQUEUE
|
||||
fprintf(stderr, "end handle_timeq_event()\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
schedule_event(long secs, char *name, ZNotice_t *notice)
|
||||
{
|
||||
time_t eventtime = (time(NULL)) + secs;
|
||||
TimeNode *newnode;
|
||||
char *buf;
|
||||
|
||||
#ifdef DEBUG_TIMEQUEUE
|
||||
fprintf(stderr, "schedule_event(%ld, %ld, %s)\n", eventtime, secs, name);
|
||||
#endif
|
||||
|
||||
if(!notice || !name) return;
|
||||
|
||||
list_add_notice(notice);
|
||||
|
||||
newnode = (TimeNode *)malloc(sizeof(TimeNode));
|
||||
buf = (char *)malloc(strlen(name) + 1);
|
||||
|
||||
strcpy(buf, name);
|
||||
|
||||
#ifdef DEBUG_TIMEQUEUE
|
||||
fprintf(stderr, "name: %s\n", buf);
|
||||
#endif
|
||||
|
||||
newnode->when = eventtime;
|
||||
newnode->event_name = buf;
|
||||
newnode->notice = notice;
|
||||
|
||||
timeq_head = addtimenode(timeq_head, newnode);
|
||||
#ifdef DEBUG_TIMEQUEUE
|
||||
fprintf(stderr, "end schedule_event()\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
free_timenode(TimeNode *node)
|
||||
{
|
||||
#ifdef DEBUG_TIMEQUEUE
|
||||
fprintf(stderr, "free_timenode(%s)\n", node->event_name);
|
||||
#endif
|
||||
|
||||
free(node->event_name);
|
||||
free(node);
|
||||
}
|
||||
|
||||
/* returns the number of notices destroyed */
|
||||
static int
|
||||
destroy_timeq_notice(ZNotice_t *notice, char *name)
|
||||
{
|
||||
TimeNode *curr = timeq_head;
|
||||
TimeNode *prev = NULL;
|
||||
TimeNode *tmp;
|
||||
|
||||
int ct = 0;
|
||||
|
||||
#ifdef DEBUG_TIMEQUEUE
|
||||
fprintf(stderr, "destroy_timeq_notice(%s)\n", name);
|
||||
#endif
|
||||
|
||||
while(curr != NULL) {
|
||||
if(curr->notice == notice &&
|
||||
(!name || !strcmp(curr->event_name, name)))
|
||||
{
|
||||
ct++;
|
||||
if(!prev) {
|
||||
timeq_head = curr->next;
|
||||
} else {
|
||||
prev->next = curr->next;
|
||||
}
|
||||
tmp = curr;
|
||||
curr = curr->next;
|
||||
free_timenode(tmp);
|
||||
} else {
|
||||
prev = curr;
|
||||
curr = curr->next;
|
||||
}
|
||||
}
|
||||
|
||||
return ct;
|
||||
}
|
||||
|
||||
long
|
||||
plus_timequeue_events(void)
|
||||
{
|
||||
/* returns number of seconds to the next event or 0L */
|
||||
/* if there are no events remaining to be processed */
|
||||
|
||||
time_t timenow = time(NULL);
|
||||
TimeNode *curr;
|
||||
|
||||
while(timeq_head != NULL && timeq_head->when <= timenow) {
|
||||
#ifdef DEBUG_TIMEQUEUE
|
||||
fprintf(stderr, "handling event\n");
|
||||
#endif
|
||||
handle_timeq_event(timeq_head);
|
||||
curr = timeq_head;
|
||||
timeq_head = timeq_head->next;
|
||||
free_timenode(curr);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_TIMEQUEUE
|
||||
if(timeq_head != NULL)
|
||||
fprintf(stderr, "next event in %ld seconds.\n",
|
||||
(timeq_head->when) - timenow);
|
||||
#endif
|
||||
|
||||
return ((timeq_head == NULL) ? 0L : ((timeq_head->when) - timenow));
|
||||
}
|
||||
|
||||
void
|
||||
plus_set_hname(ZNotice_t *notice, char *hname)
|
||||
{
|
||||
notnode *pt;
|
||||
int bx;
|
||||
|
||||
if (hname) {
|
||||
bx = list_hash_fun(notice);
|
||||
for (pt=notlist[bx]; pt && pt->notice!=notice; pt=pt->next);
|
||||
pt->hname=(char *)malloc(strlen(hname)+1);
|
||||
strcpy(pt->hname, hname);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
plus_queue_notice(ZNotice_t *notice)
|
||||
{
|
||||
char *val;
|
||||
int howlong = 0;
|
||||
|
||||
#ifdef DEBUG_TIMEQUEUE
|
||||
fprintf(stderr, "plus_queue_notice()\n");
|
||||
#endif
|
||||
|
||||
val = var_get_variable("event_time");
|
||||
if(val) {
|
||||
if(strcmp(val, "kill")) {
|
||||
howlong = atoi(val);
|
||||
#ifdef DEBUG_TIMEQUEUE
|
||||
fprintf(stderr, "$event_time %d\n", howlong);
|
||||
#endif
|
||||
} else {
|
||||
val = var_get_variable("event_name");
|
||||
if(!val || strcmp(val, "all"))
|
||||
destroy_timeq_notice(notice, (val && val[0]) ? val : "event");
|
||||
else
|
||||
destroy_timeq_notice(notice, (char *)NULL);
|
||||
}
|
||||
}
|
||||
|
||||
if(howlong > 0) {
|
||||
val = var_get_variable("event_name");
|
||||
#ifdef DEBUG_TIMEQUEUE
|
||||
fprintf(stderr, "$event_name = %s\n", val);
|
||||
#endif
|
||||
schedule_event(howlong, (val && val[0]) ? val : "event", notice);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
list_hash_fun(ZNotice_t *notice)
|
||||
{
|
||||
unsigned int ix;
|
||||
int res = 0, val = 1, ptval;
|
||||
char *pt = (char *)(notice);
|
||||
|
||||
for (ix=0; ix<sizeof(ZNotice_t *); ix++) {
|
||||
ptval = (int)pt[ix];
|
||||
if (ptval<0) ptval = (-ptval);
|
||||
res += val * ptval;
|
||||
res %= HASHSIZE;
|
||||
val *= 7;
|
||||
};
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/* initialize hash table */
|
||||
void
|
||||
init_noticelist(void)
|
||||
{
|
||||
int ix;
|
||||
|
||||
stored_notice = NULL;
|
||||
|
||||
for (ix=0; ix<HASHSIZE; ix++) {
|
||||
notlist[ix] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
dump_noticelist(void)
|
||||
{
|
||||
notnode *pt;
|
||||
int bx;
|
||||
|
||||
for (bx=0; bx<HASHSIZE; bx++) {
|
||||
for (pt=notlist[bx]; pt; pt=pt->next) {
|
||||
fprintf(stderr, "Not %p: %d [%d]\n", (void *)pt->notice,
|
||||
pt->refcount, bx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* add notice to table. Either generate a new entry, or increment ref count. */
|
||||
void
|
||||
list_add_notice(ZNotice_t *notice)
|
||||
{
|
||||
notnode *pt;
|
||||
int bx = list_hash_fun(notice);
|
||||
|
||||
for (pt=notlist[bx]; pt && pt->notice!=notice; pt=pt->next);
|
||||
|
||||
if (pt) {
|
||||
/* found entry */
|
||||
pt->refcount++;
|
||||
}
|
||||
else {
|
||||
/* no entry */
|
||||
pt = (notnode *)malloc(sizeof(notnode));
|
||||
pt->notice = notice;
|
||||
pt->refcount = 1;
|
||||
pt->fake_notice = 0;
|
||||
pt->next = notlist[bx];
|
||||
pt->opcode = notice->z_opcode;
|
||||
pt->hname = NULL;
|
||||
notlist[bx] = pt;
|
||||
}
|
||||
|
||||
/*fprintf(stderr, "list_add_notice(%p)\n", notice);
|
||||
dump_noticelist();*/
|
||||
}
|
||||
|
||||
/* remove notice from table. If refcount reaches 0, return 1; if refcount is
|
||||
still positive, return 0; if notice not there, return -1. */
|
||||
int
|
||||
list_del_notice(ZNotice_t *notice)
|
||||
{
|
||||
notnode *pt, **ppt;
|
||||
int bx = list_hash_fun(notice);
|
||||
|
||||
for (pt=notlist[bx]; pt && pt->notice!=notice; pt=pt->next);
|
||||
|
||||
if (!pt) {
|
||||
/* no entry */
|
||||
/*fprintf(stderr, "list_del_notice(%p): ERROR\n", notice);
|
||||
dump_noticelist();*/
|
||||
return (-1);
|
||||
}
|
||||
|
||||
pt->refcount--;
|
||||
if (pt->refcount > 0) {
|
||||
/*fprintf(stderr, "list_del_notice(%p): count %d\n", notice, pt->refcount);
|
||||
dump_noticelist();*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (ppt = &(notlist[bx]); (*ppt)!=pt; ppt = &((*ppt)->next));
|
||||
|
||||
*ppt = (*ppt)->next;
|
||||
|
||||
if (!pt->fake_notice)
|
||||
ZFreeNotice(pt->notice);
|
||||
if (pt->hname)
|
||||
free(pt->hname);
|
||||
free(pt->notice);
|
||||
free(pt);
|
||||
|
||||
/*fprintf(stderr, "list_del_notice(%p): count 0, gone\n", notice);*/
|
||||
/*dump_noticelist();*/
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
set_notice_fake(ZNotice_t *notice, int val)
|
||||
{
|
||||
notnode *pt;
|
||||
int bx = list_hash_fun(notice);
|
||||
|
||||
for (pt=notlist[bx]; pt && pt->notice!=notice; pt=pt->next);
|
||||
|
||||
if (pt) {
|
||||
pt->fake_notice = val;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
get_notice_fake(ZNotice_t *notice)
|
||||
{
|
||||
notnode *pt;
|
||||
int bx = list_hash_fun(notice);
|
||||
|
||||
for (pt=notlist[bx]; pt && pt->notice!=notice; pt=pt->next);
|
||||
|
||||
if (pt) {
|
||||
return pt->fake_notice;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
get_list_refcount(ZNotice_t *notice)
|
||||
{
|
||||
notnode *pt;
|
||||
int bx = list_hash_fun(notice);
|
||||
|
||||
for (pt=notlist[bx]; pt && pt->notice!=notice; pt=pt->next);
|
||||
|
||||
if (pt) {
|
||||
/*fprintf(stderr, "get_list_refcount(%p): count %d\n", notice, pt->refcount);*/
|
||||
return pt->refcount;
|
||||
}
|
||||
else {
|
||||
/*fprintf(stderr, "get_list_refcount(%p): count 0\n", notice);*/
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* export a reference to the current notice. */
|
||||
ZNotice_t *
|
||||
get_stored_notice(void)
|
||||
{
|
||||
if (!stored_notice)
|
||||
return NULL;
|
||||
|
||||
list_add_notice(stored_notice);
|
||||
|
||||
return stored_notice;
|
||||
}
|
||||
|
||||
void
|
||||
set_stored_notice(ZNotice_t *notice)
|
||||
{
|
||||
stored_notice = notice;
|
||||
}
|
||||
|
||||
void
|
||||
plus_retry_notice(ZNotice_t *notice, char ch, int metaflag)
|
||||
{
|
||||
char buf[128];
|
||||
char *tmp;
|
||||
notnode *pt;
|
||||
int bx;
|
||||
|
||||
if (!notice)
|
||||
return;
|
||||
|
||||
bx = list_hash_fun(notice);
|
||||
for (pt=notlist[bx]; pt && pt->notice!=notice; pt=pt->next);
|
||||
|
||||
if (metaflag) tmp = "-meta";
|
||||
else tmp = "";
|
||||
|
||||
if (ch==' ')
|
||||
sprintf(buf, "key%s-space", tmp);
|
||||
else if (ch==127)
|
||||
sprintf(buf, "key%s-delete", tmp);
|
||||
else if (ch==0)
|
||||
sprintf(buf, "key%s-ctrl-@", tmp);
|
||||
else if (ch==27)
|
||||
sprintf(buf, "key%s-esc", tmp);
|
||||
else if (isprint(ch))
|
||||
sprintf(buf, "key%s-%c", tmp, ch);
|
||||
else if (ch>=1 && ch<=26)
|
||||
sprintf(buf, "key%s-ctrl-%c", tmp, ch+'a'-1);
|
||||
else if (iscntrl(ch))
|
||||
sprintf(buf, "key%s-ctrl-%c", tmp, ch+'A'-1);
|
||||
else
|
||||
sprintf(buf, "key%s-unknown", tmp);
|
||||
|
||||
/* concat the old opcode if they're running in "new" mode */
|
||||
if (zwgcplus == 2 && pt && pt->opcode[0] &&
|
||||
strcmp(pt->opcode, "") != 0)
|
||||
{
|
||||
strcat(buf, " ");
|
||||
strncat(buf, pt->opcode, sizeof(buf)-strlen(buf));
|
||||
}
|
||||
|
||||
notice->z_version = "zwgcplus-repeat";
|
||||
notice->z_opcode = buf;
|
||||
|
||||
reprocess_notice(notice, NULL);
|
||||
}
|
||||
#endif /* CMU_ZWGCPLUS */
|
29
zwgc/plus.h
Normal file
29
zwgc/plus.h
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
This file contains code related to the zwgcplus extension to zwgc.
|
||||
zwgc is copyrighted by the Massachusetts Institute of Technology.
|
||||
This file is public domain.
|
||||
Written by Andrew Plotkin, ap1i+@andrew.cmu.edu
|
||||
*/
|
||||
|
||||
#define NAMESIZE (256)
|
||||
|
||||
extern int get_full_names;
|
||||
extern int zwgcplus;
|
||||
|
||||
extern void init_noticelist(void);
|
||||
extern void dump_noticelist(void);
|
||||
extern void list_add_notice(ZNotice_t *notice);
|
||||
extern int list_del_notice(ZNotice_t *notice);
|
||||
extern int get_list_refcount(ZNotice_t *notice);
|
||||
extern void set_notice_fake(ZNotice_t *notice, int val);
|
||||
extern int get_notice_fake(ZNotice_t *notice);
|
||||
extern ZNotice_t *get_stored_notice(void);
|
||||
extern void plus_retry_notice(ZNotice_t *notice, char ch, int metaflag);
|
||||
extern void set_stored_notice(ZNotice_t *notice);
|
||||
extern void plus_window_deletions(ZNotice_t *notice); /* actually in xshow.c */
|
||||
|
||||
extern void plus_queue_notice(ZNotice_t *notice);
|
||||
extern long plus_timequeue_events(void);
|
||||
void plus_set_hname(ZNotice_t *notice, char *hname);
|
||||
|
||||
extern char *getSelectedText(void); /* actually in xcut.c */
|
26
zwgc/pointer.h
Normal file
26
zwgc/pointer.h
Normal file
@ -0,0 +1,26 @@
|
||||
/* 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 <zephyr/mit-copyright.h>
|
||||
|
||||
#ifndef pointer_MODULE
|
||||
#define pointer_MODULE
|
||||
|
||||
#ifdef __STDC__
|
||||
typedef void *pointer;
|
||||
#else
|
||||
typedef char *pointer;
|
||||
#endif
|
||||
|
||||
#endif
|
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;
|
||||
}
|
124
zwgc/port.h
Normal file
124
zwgc/port.h
Normal file
@ -0,0 +1,124 @@
|
||||
#ifndef port_TYPE
|
||||
#define port_TYPE
|
||||
|
||||
/* 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 <zephyr/mit-copyright.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include "new_string.h"
|
||||
#include "string_stack.h"
|
||||
|
||||
union port__data {
|
||||
struct {
|
||||
FILE *input_connector;
|
||||
FILE *output_connector;
|
||||
} file;
|
||||
struct {
|
||||
string_stack waiting_packets;
|
||||
string (*filter)(string);
|
||||
} filter;
|
||||
struct {
|
||||
char *(*output)(string);
|
||||
} output;
|
||||
};
|
||||
|
||||
typedef struct port__struct { /* PRIVATE */
|
||||
char *(*get)(struct port__struct *, char **);
|
||||
char *(*put)(struct port__struct *, char *, int);
|
||||
char *(*close_input)(struct port__struct *);
|
||||
char *(*close_output)(struct port__struct *);
|
||||
#define INPUT_CLOSED 0x1
|
||||
#define OUTPUT_CLOSED 0x2
|
||||
#define PORT_CLOSED 0x3
|
||||
int status;
|
||||
union port__data data;
|
||||
} port;
|
||||
|
||||
/*
|
||||
* void init_ports()
|
||||
* Modifies: all ports
|
||||
* Effects: Closes all existing ports. Must be called before
|
||||
* any other port call is made.
|
||||
*/
|
||||
|
||||
extern void init_ports(void);
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
extern string read_from_port(string);
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
extern void write_on_port(string, char *, int);
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
extern void close_port_input(string);
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
extern void close_port_output(string);
|
||||
|
||||
|
||||
extern void create_subprocess_port(string, char **);
|
||||
extern void create_file_append_port(string, string);
|
||||
extern void create_file_input_port(string, string);
|
||||
extern void create_file_output_port(string, string);
|
||||
extern void create_port_from_filter(string, string (*)(string));
|
||||
extern void create_port_from_output_proc(string, char *(*)(string));
|
||||
|
||||
extern void init_standard_ports(int *, char **);
|
||||
extern void create_port_from_files(string, FILE *, FILE *);
|
||||
|
||||
#endif
|
46
zwgc/regexp.c
Normal file
46
zwgc/regexp.c
Normal file
@ -0,0 +1,46 @@
|
||||
/* 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>
|
||||
#include <regex.h>
|
||||
|
||||
#if (!defined(lint) && !defined(SABER))
|
||||
static const char rcsid_regexp_c[] = "$Id$";
|
||||
#endif
|
||||
|
||||
#include "regexp.h"
|
||||
|
||||
int
|
||||
ed_regexp_match_p(string test_string,
|
||||
string pattern)
|
||||
{
|
||||
regex_t RE;
|
||||
int retval;
|
||||
char errbuf[512];
|
||||
|
||||
retval = regcomp(&RE, pattern, REG_NOSUB);
|
||||
if (retval != 0) {
|
||||
regerror(retval, &RE, errbuf, sizeof(errbuf));
|
||||
fprintf(stderr,"%s in regcomp %s\n",errbuf,pattern);
|
||||
return(0);
|
||||
}
|
||||
retval = regexec(&RE, test_string, 0, NULL, 0);
|
||||
if (retval != 0 && retval != REG_NOMATCH) {
|
||||
regerror(retval, &RE, errbuf, sizeof(errbuf));
|
||||
fprintf(stderr,"%s in regexec %s\n",errbuf,pattern);
|
||||
regfree(&RE);
|
||||
return(0);
|
||||
}
|
||||
regfree(&RE);
|
||||
return(retval == 0 ? 1 : 0);
|
||||
}
|
24
zwgc/regexp.h
Normal file
24
zwgc/regexp.h
Normal file
@ -0,0 +1,24 @@
|
||||
/* 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 <zephyr/mit-copyright.h>
|
||||
|
||||
#ifndef regexp_MODULE
|
||||
#define regexp_MODULE
|
||||
|
||||
#include "new_string.h"
|
||||
|
||||
extern int ed_regexp_match_p(string, string);
|
||||
|
||||
#endif
|
65
zwgc/stack.h
Normal file
65
zwgc/stack.h
Normal file
@ -0,0 +1,65 @@
|
||||
/* 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 <zephyr/mit-copyright.h>
|
||||
|
||||
/****************************************************************************/
|
||||
/* */
|
||||
/* A generic stack type based on linked lists: */
|
||||
/* */
|
||||
/****************************************************************************/
|
||||
|
||||
#ifndef TYPE_T_stack_TYPE
|
||||
#define TYPE_T_stack_TYPE
|
||||
|
||||
#ifndef NULL
|
||||
#define NULL 0
|
||||
#endif
|
||||
|
||||
typedef struct _TYPE_T_stack {
|
||||
struct _TYPE_T_stack *next;
|
||||
TYPE_T data;
|
||||
} *TYPE_T_stack;
|
||||
|
||||
#define TYPE_T_stack_create() ((struct _TYPE_T_stack *) NULL)
|
||||
|
||||
#define TYPE_T_stack_empty(stack) (!(stack))
|
||||
|
||||
#ifdef DEBUG
|
||||
#define TYPE_T_stack_top(stack) ((stack) ? (stack)->data :\
|
||||
(abort(),(stack)->data))
|
||||
#else
|
||||
#define TYPE_T_stack_top(stack) ((stack)->data)
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
#define TYPE_T_stack_pop(stack) { TYPE_T_stack old = (stack);\
|
||||
if (!old)\
|
||||
abort(); /*<<<>>>*/\
|
||||
(stack) = old->next;\
|
||||
free(old); }
|
||||
#else
|
||||
#define TYPE_T_stack_pop(stack) { TYPE_T_stack old = (stack);\
|
||||
(stack) = old->next;\
|
||||
free(old); }
|
||||
#endif
|
||||
|
||||
#define TYPE_T_stack_push(stack,object) \
|
||||
{ TYPE_T_stack new = (struct _TYPE_T_stack *)\
|
||||
malloc(sizeof (struct _TYPE_T_stack));\
|
||||
new->next = (stack);\
|
||||
new->data = object;\
|
||||
(stack) = new; }
|
||||
|
||||
#endif
|
305
zwgc/standard_ports.c
Normal file
305
zwgc/standard_ports.c
Normal file
@ -0,0 +1,305 @@
|
||||
/* 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_standard_ports_c[] = "$Id$";
|
||||
#endif
|
||||
|
||||
#include <zephyr/mit-copyright.h>
|
||||
|
||||
/****************************************************************************/
|
||||
/* */
|
||||
/* Code to setup standard ports: */
|
||||
/* */
|
||||
/****************************************************************************/
|
||||
|
||||
#include <zephyr/zephyr.h>
|
||||
#include "new_memory.h"
|
||||
#include "port.h"
|
||||
#include "variables.h"
|
||||
#include "error.h"
|
||||
#include "main.h"
|
||||
#include "tty_filter.h"
|
||||
#ifndef X_DISPLAY_MISSING
|
||||
#include "X_driver.h"
|
||||
#endif
|
||||
|
||||
|
||||
extern char *tty_filter(string, int);
|
||||
extern int tty_filter_init(char *, char, int *, char **);
|
||||
|
||||
extern void usage(void);
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
static char *
|
||||
plain_driver(string input)
|
||||
{
|
||||
string processed_input = tty_filter(input, 0);
|
||||
|
||||
fputs(processed_input, stdout);
|
||||
fflush(stdout);
|
||||
free(processed_input);
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
static char *
|
||||
tty_driver(string input)
|
||||
{
|
||||
string processed_input = tty_filter(input, 1);
|
||||
|
||||
fputs(processed_input, stdout);
|
||||
fflush(stdout);
|
||||
free(processed_input);
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
static string
|
||||
noop_filter(string input)
|
||||
{
|
||||
return(input);
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
static string
|
||||
plain_filter(string input)
|
||||
{
|
||||
return(tty_filter(input, 0));
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
static string
|
||||
fancy_filter(string input)
|
||||
{
|
||||
return(tty_filter(input, 1));
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
static struct standard_port_info {
|
||||
char *port_name;
|
||||
/*
|
||||
* 0 = ok to use as the default output port
|
||||
* 1 = not ok to use as the default output port
|
||||
* 2 = disabled
|
||||
*/
|
||||
#define DEFAULT_OK 0
|
||||
#define DEFAULT_NOTOK 1
|
||||
#define DISABLED 2
|
||||
|
||||
int port_setup_status;
|
||||
int (*port_init)(char *, char, int *, char **);
|
||||
#define INPUT_DESC 0
|
||||
#define OUTPUT_DESC 1
|
||||
#define FILTER 2
|
||||
#define OUTPUT_PROC 3
|
||||
int type;
|
||||
char *(*function)(string);
|
||||
int setup_arg;
|
||||
} standard_port_info_table[] = {
|
||||
#ifndef X_DISPLAY_MISSING
|
||||
{ "X", DEFAULT_OK, X_driver_init, OUTPUT_PROC, X_driver, 0},
|
||||
{ "tty", DEFAULT_NOTOK, tty_filter_init, OUTPUT_PROC, tty_driver, 0},
|
||||
#else
|
||||
{ "tty", DEFAULT_OK, tty_filter_init, OUTPUT_PROC, tty_driver, 0},
|
||||
#endif
|
||||
{ "plain", DEFAULT_NOTOK, tty_filter_init, OUTPUT_PROC, plain_driver, 0},
|
||||
{ "stdout", DEFAULT_NOTOK, NULL, OUTPUT_DESC, NULL, 1},
|
||||
{ "stderr", DEFAULT_NOTOK, NULL, OUTPUT_DESC, NULL, 2},
|
||||
|
||||
{ "stdin", DEFAULT_NOTOK, NULL, INPUT_DESC, NULL, 0},
|
||||
{ "loopback", DEFAULT_NOTOK, NULL, FILTER, noop_filter, 0},
|
||||
{ "plain_filter", DEFAULT_NOTOK, tty_filter_init, FILTER, plain_filter, 0},
|
||||
{ "tty_filter", DEFAULT_NOTOK, tty_filter_init, FILTER, fancy_filter, 0},
|
||||
|
||||
{ NULL, DISABLED, NULL, FILTER, NULL, 0} };
|
||||
|
||||
/*
|
||||
* <<<>>>
|
||||
*/
|
||||
|
||||
static struct standard_port_info *
|
||||
get_standard_port_info(string port_name)
|
||||
{
|
||||
struct standard_port_info *p;
|
||||
|
||||
for (p=standard_port_info_table; p->port_name; p++)
|
||||
if (string_Eq(p->port_name, port_name) && p->port_setup_status!=DISABLED)
|
||||
return(p);
|
||||
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Internal Routine:
|
||||
*
|
||||
* int boolean_value_of(string text)
|
||||
* Effects: If text represents yes/true/on, return 1. If text
|
||||
* representes no/false/off, return 0. Otherwise,
|
||||
* returns -1.
|
||||
*/
|
||||
|
||||
static int
|
||||
boolean_value_of(string text)
|
||||
{
|
||||
if (!text)
|
||||
return(-1); /* not set */
|
||||
if (!strcasecmp("yes", text) || !strcasecmp("y", text) ||
|
||||
!strcasecmp("true", text) || !strcasecmp("t", text) ||
|
||||
!strcasecmp("on", text))
|
||||
return(1);
|
||||
else if (!strcasecmp("no", text) || !strcasecmp("n", text) ||
|
||||
!strcasecmp("false", text) || !strcasecmp("f", text) ||
|
||||
!strcasecmp("off", text))
|
||||
return(0);
|
||||
else
|
||||
return(-1);
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
void init_standard_ports(int *pargc,
|
||||
char **argv)
|
||||
{
|
||||
struct standard_port_info *p;
|
||||
string first_working_port = "";
|
||||
string default_port = "";
|
||||
char **new, **current;
|
||||
int fallback;
|
||||
char *charset = NULL;
|
||||
|
||||
/*
|
||||
* Process argument list handling "-disable <port>" and
|
||||
* "-default <output port>" arguments, as well as "-ttymode"
|
||||
* and -charset, because tty_filter_init gets run twice
|
||||
*/
|
||||
for (new=current=argv+1; *current; current++) {
|
||||
if (string_Eq((string) *current, "-disable")) {
|
||||
current++; *pargc -= 2;
|
||||
if (!*current)
|
||||
usage();
|
||||
p = get_standard_port_info((string) *current);
|
||||
if (p)
|
||||
p->port_setup_status = DISABLED;
|
||||
} else if (string_Eq((string) *current, "-default")) {
|
||||
current++; *pargc -= 2;
|
||||
if (!*current)
|
||||
usage();
|
||||
default_port = (string) *current;
|
||||
p = get_standard_port_info((string) *current);
|
||||
if (p)
|
||||
p->port_setup_status = DEFAULT_OK;
|
||||
} else if (string_Eq((string) *current, "-charset")) {
|
||||
current++; *pargc -= 2;
|
||||
if (!*current)
|
||||
usage();
|
||||
charset = *current;
|
||||
} else if (string_Eq((string) *current, "-ttymode")) {
|
||||
default_port = (string) "tty";
|
||||
(*pargc)--;
|
||||
p = get_standard_port_info(default_port);
|
||||
if (p) {
|
||||
p->port_setup_status = DEFAULT_OK;
|
||||
p = get_standard_port_info ((string) "X");
|
||||
if (p)
|
||||
p->port_setup_status = DISABLED;
|
||||
}
|
||||
} else
|
||||
*(new++) = *current;
|
||||
}
|
||||
*new = *current;
|
||||
|
||||
var_set_variable_then_free_value("tty_charset", (string)ZGetCharsetString(charset));
|
||||
|
||||
fallback = boolean_value_of(ZGetVariable("fallback"));
|
||||
/*
|
||||
* Initialize all non-disabled ports. If a port reports an error,
|
||||
* disable that port. Set default_port if not already set
|
||||
* by the -default argument to the first non-disabled port.
|
||||
*/
|
||||
for (p = standard_port_info_table; p->port_name; p++) {
|
||||
if (p->port_setup_status == DISABLED)
|
||||
continue;
|
||||
|
||||
if (p->port_init && (*(p->port_init))(p->port_name,
|
||||
*first_working_port,
|
||||
pargc, argv)) {
|
||||
p->port_setup_status = DISABLED;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fallback == 1) {
|
||||
/* we are doing fallback, make DEFAULT_NOTOK ports OK */
|
||||
p->port_setup_status = DEFAULT_OK;
|
||||
}
|
||||
if (!*first_working_port)
|
||||
first_working_port = p->port_name;
|
||||
switch (p->type) {
|
||||
case INPUT_DESC:
|
||||
create_port_from_files(p->port_name, fdopen(p->setup_arg, "r"),0);
|
||||
break;
|
||||
|
||||
case OUTPUT_DESC:
|
||||
create_port_from_files(p->port_name, 0, fdopen(p->setup_arg, "w"));
|
||||
break;
|
||||
|
||||
case FILTER:
|
||||
create_port_from_filter(p->port_name, p->function);
|
||||
break;
|
||||
|
||||
case OUTPUT_PROC:
|
||||
create_port_from_output_proc(p->port_name, p->function);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!default_port[0]) {
|
||||
/* no default port has been set */
|
||||
for (p = get_standard_port_info(first_working_port); p->port_name; p++)
|
||||
if ((p->port_setup_status == DEFAULT_OK))
|
||||
break;
|
||||
if (p->port_name)
|
||||
var_set_variable("output_driver", p->port_name);
|
||||
else { /* no suitable default has been found */
|
||||
if (fallback == -1) /* complain, since indeterminate */
|
||||
ERROR2(
|
||||
"To receive Zephyrgrams, (type `%s -ttymode').\n",
|
||||
progname);
|
||||
exit(1);
|
||||
}
|
||||
} else
|
||||
var_set_variable("output_driver", default_port);
|
||||
|
||||
}
|
102
zwgc/string_dictionary_aux.c
Normal file
102
zwgc/string_dictionary_aux.c
Normal file
@ -0,0 +1,102 @@
|
||||
/* 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".
|
||||
*/
|
||||
|
||||
#if (!defined(lint) && !defined(SABER))
|
||||
static const char rcsid_string_dictionary_aux_c[] = "$Id$";
|
||||
#endif
|
||||
|
||||
/*
|
||||
* string_dictionary_aux - a module implementing convenience routines for use
|
||||
* with string_dictionarys
|
||||
*
|
||||
* Overview:
|
||||
*
|
||||
* This module implements Fetch and Set operations on
|
||||
* string_dictionaries which take the place of Define and Lookup for
|
||||
* most uses. The importance difference between them and Define and
|
||||
* Lookup is that they maintain the invariant that all the value strings
|
||||
* in a string_dictionary are on the heap. In particular, they do
|
||||
* free's and string_Copy's whenever needed. Also implemented is
|
||||
* SafeDestroy which does a Destroy after freeing all the value strings
|
||||
* in a string_dictionary.
|
||||
*/
|
||||
|
||||
#include <sysdep.h>
|
||||
#include "new_memory.h"
|
||||
#include "string_dictionary.h"
|
||||
#include "string_dictionary_aux.h"
|
||||
|
||||
/*
|
||||
* void string_dictionary_Set(string_dictionary d, string key,string value):
|
||||
* Modifies: d
|
||||
* Effects: Binds key to value in d. Automatically free's the
|
||||
* previous value of key, if any. Value is copied on the
|
||||
* heap.
|
||||
*/
|
||||
|
||||
void
|
||||
string__dictionary_Set(string_dictionary d,
|
||||
string key,
|
||||
string value)
|
||||
{
|
||||
string_dictionary_binding *binding;
|
||||
int already_exists;
|
||||
|
||||
binding = string_dictionary_Define(d, key, &already_exists);
|
||||
if (already_exists)
|
||||
free(binding->value);
|
||||
|
||||
binding->value = string_Copy(value);
|
||||
}
|
||||
|
||||
/*
|
||||
* char *string_dictionary_Fetch(string_dictionary d, string key)
|
||||
* Effects: If key is not bound in d, returns 0. Otherwise,
|
||||
* returns the value that key is bound to.
|
||||
* Note that the returned string if any should not be
|
||||
* freed or modified in any way. Note also that it may
|
||||
* disappear later if key is rebound.
|
||||
*/
|
||||
|
||||
char *
|
||||
string_dictionary_Fetch(string_dictionary d,
|
||||
string key)
|
||||
{
|
||||
string_dictionary_binding *binding;
|
||||
|
||||
binding = string_dictionary_Lookup(d, key);
|
||||
if (!binding)
|
||||
return(0);
|
||||
|
||||
return(binding->value);
|
||||
}
|
||||
|
||||
/*
|
||||
* void string_dictionary_SafeDestroy(string_dictionary d)
|
||||
* Modifies: d
|
||||
* Effects: Like string_dictionary_Destroy except first frees
|
||||
* all value's in the dictionary.
|
||||
*/
|
||||
|
||||
static void
|
||||
free_value_of_binding(string_dictionary_binding *b)
|
||||
{
|
||||
free(b->value);
|
||||
}
|
||||
|
||||
void
|
||||
string_dictionary_SafeDestroy(string_dictionary d)
|
||||
{
|
||||
string_dictionary_Enumerate(d, free_value_of_binding);
|
||||
string_dictionary_Destroy(d);
|
||||
}
|
57
zwgc/string_dictionary_aux.h
Normal file
57
zwgc/string_dictionary_aux.h
Normal file
@ -0,0 +1,57 @@
|
||||
/* 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".
|
||||
*/
|
||||
|
||||
#ifndef string_dictionary_aux_MODULE
|
||||
#define string_dictionary_aux_MODULE
|
||||
|
||||
#include "new_memory.h"
|
||||
#include "string_dictionary.h"
|
||||
|
||||
/*
|
||||
* void string_dictionary_Set(string_dictionary d, string key,string value):
|
||||
* Modifies: d
|
||||
* Effects: Binds key to value in d. Automatically free's the
|
||||
* previous value of key, if any. Value is copied on the
|
||||
* heap.
|
||||
*/
|
||||
|
||||
extern void string__dictionary_Set(string_dictionary, string, string);
|
||||
#ifdef DEBUG_MEMORY
|
||||
#define string_dictionary_Set(a,b,c) (set_module(__FILE__,__LINE__),\
|
||||
string__dictionary_Set(a,b,c))
|
||||
#else
|
||||
#define string_dictionary_Set(a,b,c) string__dictionary_Set(a,b,c)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* char *string_dictionary_Fetch(string_dictionary d, string key)
|
||||
* Effects: If key is not bound in d, returns 0. Otherwise,
|
||||
* returns the value that key is bound to.
|
||||
* Note that the returned string if any should not be
|
||||
* freed or modified in any way. Note also that it may
|
||||
* disappear later if key is rebound.
|
||||
*/
|
||||
|
||||
extern char *string_dictionary_Fetch(string_dictionary,
|
||||
string);
|
||||
|
||||
/*
|
||||
* void string_dictionary_SafeDestroy(string_dictionary d)
|
||||
* Modifies: d
|
||||
* Effects: Like string_dictionary_Destroy except first frees
|
||||
* all value's in the dictionary.
|
||||
*/
|
||||
|
||||
extern void string_dictionary_SafeDestroy(string_dictionary);
|
||||
|
||||
#endif
|
402
zwgc/subscriptions.c
Normal file
402
zwgc/subscriptions.c
Normal file
@ -0,0 +1,402 @@
|
||||
/* 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".
|
||||
*/
|
||||
|
||||
#if (!defined(lint) && !defined(SABER))
|
||||
static const char rcsid_subscriptions_c[] = "$Id$";
|
||||
#endif
|
||||
|
||||
/****************************************************************************/
|
||||
/* */
|
||||
/* Subscriptions.c: code to deal with subscriptions & punting: */
|
||||
/* */
|
||||
/****************************************************************************/
|
||||
|
||||
#include <sysdep.h>
|
||||
#include <zephyr/zephyr.h>
|
||||
#include <netdb.h>
|
||||
#include <arpa/nameser.h>
|
||||
#include "new_memory.h"
|
||||
#include "new_string.h"
|
||||
#include "int_dictionary.h"
|
||||
#include "zwgc.h"
|
||||
#include "subscriptions.h"
|
||||
#include "error.h"
|
||||
#include "file.h"
|
||||
#include "main.h"
|
||||
|
||||
/****************************************************************************/
|
||||
/* */
|
||||
/* Code to implement punting of notices: */
|
||||
/* */
|
||||
/****************************************************************************/
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
#ifndef CMU_ZCTL_PUNT
|
||||
static
|
||||
#endif
|
||||
int_dictionary puntable_addresses_dict = 0;
|
||||
|
||||
static void
|
||||
init_puntable_dict(void)
|
||||
{
|
||||
puntable_addresses_dict = int_dictionary_Create(33);
|
||||
}
|
||||
|
||||
static string
|
||||
address_to_string(string class,
|
||||
string instance,
|
||||
string recipient)
|
||||
{
|
||||
string result;
|
||||
|
||||
/*
|
||||
* Treat a recipient of "" as "*":
|
||||
*/
|
||||
if (string_Eq(recipient,""))
|
||||
recipient = "*";
|
||||
else if (recipient[0] == '@') {
|
||||
recipient = string_Concat("*", recipient);
|
||||
}
|
||||
|
||||
/*
|
||||
* The following is a hack for now only. It should be replaced with
|
||||
* several calls to escape_code... <<<>>>
|
||||
*/
|
||||
result = string_Concat(class, "\001");
|
||||
result = string_Concat2(result, instance);
|
||||
result = string_Concat2(result, "\001");
|
||||
result = string_Concat2(result, recipient);
|
||||
string_Downcase(result);
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
int puntable_address_p(string class,
|
||||
string instance,
|
||||
string recipient)
|
||||
{
|
||||
string temp;
|
||||
|
||||
if (!puntable_addresses_dict)
|
||||
init_puntable_dict();
|
||||
|
||||
temp = address_to_string(class, instance, recipient);
|
||||
if (int_dictionary_Lookup(puntable_addresses_dict, temp)) {
|
||||
free(temp);
|
||||
return 1;
|
||||
}
|
||||
free(temp);
|
||||
|
||||
/* This kludge is to allow punts of wildcard instance to work */
|
||||
temp = address_to_string(class, "*", recipient);
|
||||
if (int_dictionary_Lookup(puntable_addresses_dict, temp)) {
|
||||
free(temp);
|
||||
return(1);
|
||||
}
|
||||
|
||||
free(temp);
|
||||
return(0);
|
||||
}
|
||||
|
||||
void punt(string class,
|
||||
string instance,
|
||||
string recipient)
|
||||
{
|
||||
string temp;
|
||||
|
||||
if (!puntable_addresses_dict)
|
||||
init_puntable_dict();
|
||||
|
||||
temp = address_to_string(class, instance, recipient);
|
||||
(void)int_dictionary_Define(puntable_addresses_dict, temp, 0);
|
||||
free(temp);
|
||||
}
|
||||
|
||||
void unpunt(string class,
|
||||
string instance,
|
||||
string recipient)
|
||||
{
|
||||
string temp;
|
||||
int_dictionary_binding *binding;
|
||||
|
||||
if (!puntable_addresses_dict)
|
||||
init_puntable_dict();
|
||||
|
||||
temp = address_to_string(class, instance, recipient);
|
||||
binding = int_dictionary_Define(puntable_addresses_dict, temp, 0);
|
||||
free(temp);
|
||||
if (binding)
|
||||
int_dictionary_Delete(puntable_addresses_dict, binding);
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
/* */
|
||||
/* Code to implement batching [un]subscription requests: */
|
||||
/* */
|
||||
/****************************************************************************/
|
||||
|
||||
/*
|
||||
* <<<>>> these routines require zwgc_active to be false (0)
|
||||
*/
|
||||
|
||||
#define BATCH_SIZE 20
|
||||
|
||||
static int subscription_list_size = 0;
|
||||
static ZSubscription_t subscription_list[BATCH_SIZE];
|
||||
|
||||
static int unsubscription_list_size = 0;
|
||||
static ZSubscription_t unsubscription_list[BATCH_SIZE];
|
||||
|
||||
static void
|
||||
free_subscription_list(ZSubscription_t *list,
|
||||
int number_of_elements)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i<number_of_elements; i++) {
|
||||
free(list[i].zsub_class);
|
||||
free(list[i].zsub_classinst);
|
||||
free(list[i].zsub_recipient);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
flush_subscriptions(void)
|
||||
{
|
||||
TRAP(ZSubscribeTo(subscription_list,subscription_list_size, 0),
|
||||
"while subscribing");
|
||||
|
||||
free_subscription_list(subscription_list, subscription_list_size);
|
||||
subscription_list_size = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
flush_unsubscriptions(void)
|
||||
{
|
||||
if (unsubscription_list_size)
|
||||
TRAP(ZUnsubscribeTo(unsubscription_list, unsubscription_list_size, 0),
|
||||
"while unsubscribing");
|
||||
|
||||
free_subscription_list(unsubscription_list, unsubscription_list_size);
|
||||
unsubscription_list_size = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
subscribe(string class,
|
||||
string instance,
|
||||
string recipient)
|
||||
{
|
||||
subscription_list[subscription_list_size].zsub_class = string_Copy(class);
|
||||
subscription_list[subscription_list_size].zsub_classinst= string_Copy(instance);
|
||||
subscription_list[subscription_list_size].zsub_recipient=string_Copy(recipient);
|
||||
|
||||
if (++subscription_list_size == BATCH_SIZE)
|
||||
flush_subscriptions();
|
||||
}
|
||||
|
||||
static void
|
||||
unsubscribe(string class,
|
||||
string instance,
|
||||
string recipient)
|
||||
{
|
||||
unsubscription_list[unsubscription_list_size].zsub_class = string_Copy(class);
|
||||
unsubscription_list[unsubscription_list_size].zsub_classinst
|
||||
= string_Copy(instance);
|
||||
unsubscription_list[unsubscription_list_size].zsub_recipient
|
||||
= string_Copy(recipient);
|
||||
|
||||
if (++unsubscription_list_size == BATCH_SIZE)
|
||||
flush_unsubscriptions();
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
/* */
|
||||
/* Code to implement reading [un]subscriptions from a file: */
|
||||
/* */
|
||||
/****************************************************************************/
|
||||
|
||||
#define TOKEN_HOSTNAME "%host%"
|
||||
#define TOKEN_CANONNAME "%canon%"
|
||||
#define TOKEN_ME "%me%"
|
||||
#define TOKEN_WILD "*"
|
||||
|
||||
char ourhost[NS_MAXDNAME], ourhostcanon[NS_MAXDNAME];
|
||||
|
||||
static void
|
||||
inithosts(void)
|
||||
{
|
||||
struct hostent *hent;
|
||||
if (gethostname(ourhost, sizeof(ourhost)-1) == -1) {
|
||||
ERROR3("unable to retrieve hostname, %s and %s will be wrong in subscriptions.\n", TOKEN_HOSTNAME, TOKEN_CANONNAME);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(hent = gethostbyname(ourhost))) {
|
||||
ERROR2("unable to resolve hostname, %s will be wrong in subscriptions.\n", TOKEN_CANONNAME);
|
||||
strncpy(ourhostcanon, ourhost, sizeof(ourhostcanon)-1);
|
||||
return;
|
||||
}
|
||||
strncpy(ourhostcanon, hent->h_name, sizeof(ourhostcanon)-1);
|
||||
return;
|
||||
}
|
||||
|
||||
static void
|
||||
macro_sub(char *str)
|
||||
{
|
||||
static int initedhosts = 0;
|
||||
|
||||
if (!initedhosts) {
|
||||
inithosts();
|
||||
initedhosts = 1;
|
||||
}
|
||||
if (string_Eq(str, TOKEN_ME))
|
||||
strcpy(str, ZGetSender());
|
||||
else if (string_Eq(str, TOKEN_HOSTNAME))
|
||||
strcpy(str, ourhost);
|
||||
else if (string_Eq(str, TOKEN_CANONNAME))
|
||||
strcpy(str, ourhostcanon);
|
||||
}
|
||||
|
||||
#define UNSUBSCRIBE_CHARACTER '!'
|
||||
#define PUNT_CHARACTER '-'
|
||||
|
||||
static void
|
||||
load_subscriptions_from_file(FILE *file)
|
||||
{
|
||||
char line[BUFSIZ];
|
||||
char class_buffer[BUFSIZ], instance[BUFSIZ], recipient[BUFSIZ];
|
||||
char *class, *temp;
|
||||
char c;
|
||||
|
||||
while ((!feof(file)) && (!ferror(file))) {
|
||||
if (fgets(line, BUFSIZ, file)) {
|
||||
class = class_buffer;
|
||||
/* Parse line */
|
||||
/* <<<>>>
|
||||
* The below does NOT work is the recipient field is "":
|
||||
*/
|
||||
temp = strchr(line, '#');
|
||||
if (temp)
|
||||
*temp = '\0';
|
||||
for (temp=line; *temp && *temp==' '; temp++) ;
|
||||
if (!*temp || *temp=='\n')
|
||||
continue;
|
||||
|
||||
sscanf(temp,"%[^,],%[^,],%s", class, instance, recipient);
|
||||
|
||||
/* skip type indicator if any: */
|
||||
c = class[0];
|
||||
if (c==UNSUBSCRIBE_CHARACTER || c==PUNT_CHARACTER)
|
||||
class++;
|
||||
|
||||
/* perform macro substitutions */
|
||||
macro_sub(class);
|
||||
macro_sub(instance);
|
||||
macro_sub(recipient);
|
||||
|
||||
/* do the right thing with it */
|
||||
switch (c) {
|
||||
case UNSUBSCRIBE_CHARACTER:
|
||||
unsubscribe(class, instance, recipient);
|
||||
break;
|
||||
case PUNT_CHARACTER:
|
||||
punt(class, instance, recipient);
|
||||
break;
|
||||
default:
|
||||
subscribe(class, instance, recipient);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ferror(file)) {
|
||||
com_err("zwgc", errno, "while reading from subscription file");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
flush_subscriptions();
|
||||
flush_unsubscriptions();
|
||||
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
#define DEFSUBS "/dev/null"
|
||||
|
||||
static void
|
||||
load_subscriptions(void)
|
||||
{
|
||||
FILE *subscriptions_file;
|
||||
|
||||
/* no system default sub file on client--they live on the server */
|
||||
/* BUT...we need to use something to call load_subscriptions_from_file,
|
||||
so we use /dev/null */
|
||||
subscriptions_file = locate_file(subscriptions_filename_override,
|
||||
USRSUBS, DEFSUBS);
|
||||
if (subscriptions_file)
|
||||
load_subscriptions_from_file(subscriptions_file);
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
/* */
|
||||
/* Code to implement shutdown and startup: */
|
||||
/* */
|
||||
/****************************************************************************/
|
||||
|
||||
int zwgc_active = 0;
|
||||
|
||||
static ZSubscription_t *saved_subscriptions = NULL;
|
||||
static int number_of_saved_subscriptions;
|
||||
|
||||
void zwgc_shutdown(void)
|
||||
{
|
||||
if (!zwgc_active)
|
||||
return;
|
||||
|
||||
TRAP(ZRetrieveSubscriptions(0, &number_of_saved_subscriptions),
|
||||
"while retrieving zephyr subscription list");
|
||||
if (error_code)
|
||||
return;
|
||||
saved_subscriptions = (ZSubscription_t *)
|
||||
malloc(number_of_saved_subscriptions*sizeof(ZSubscription_t));
|
||||
if (number_of_saved_subscriptions)
|
||||
TRAP(ZGetSubscriptions(saved_subscriptions,
|
||||
&number_of_saved_subscriptions),
|
||||
"while getting subscriptions");
|
||||
if (error_code) {
|
||||
free(saved_subscriptions);
|
||||
saved_subscriptions = NULL;
|
||||
}
|
||||
TRAP(ZCancelSubscriptions(0), "while canceling subscriptions") ;
|
||||
|
||||
zwgc_active = 0;
|
||||
}
|
||||
|
||||
void zwgc_startup(void)
|
||||
{
|
||||
if (zwgc_active)
|
||||
return;
|
||||
|
||||
if (saved_subscriptions) {
|
||||
TRAP(ZSubscribeToSansDefaults(saved_subscriptions,number_of_saved_subscriptions,0),
|
||||
"while resubscribing to zephyr messages");
|
||||
free(saved_subscriptions);
|
||||
saved_subscriptions = NULL;
|
||||
} else
|
||||
load_subscriptions();
|
||||
|
||||
zwgc_active = 1;
|
||||
}
|
30
zwgc/subscriptions.h
Normal file
30
zwgc/subscriptions.h
Normal file
@ -0,0 +1,30 @@
|
||||
/* 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 <zephyr/mit-copyright.h>
|
||||
|
||||
#ifndef subscriptions_MODULE
|
||||
#define subscriptions_MODULE
|
||||
|
||||
extern int zwgc_active;
|
||||
|
||||
extern int puntable_address_p(string, string, string);
|
||||
extern void punt(string, string, string);
|
||||
extern void unpunt(string, string, string);
|
||||
extern void zwgc_shutdown(void);
|
||||
extern void zwgc_startup(void);
|
||||
|
||||
#define USRSUBS ".zephyr.subs"
|
||||
|
||||
#endif
|
169
zwgc/substitute.c
Normal file
169
zwgc/substitute.c
Normal file
@ -0,0 +1,169 @@
|
||||
/* 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".
|
||||
*/
|
||||
|
||||
#if (!defined(lint) && !defined(SABER))
|
||||
static const char rcsid_substitute_c[] = "$Id$";
|
||||
#endif
|
||||
|
||||
#include <zephyr/mit-copyright.h>
|
||||
|
||||
#include <sysdep.h>
|
||||
#include "new_memory.h"
|
||||
#include "lexer.h"
|
||||
#include "substitute.h"
|
||||
|
||||
/*
|
||||
* Internal Routine:
|
||||
*
|
||||
* string eat_dollar_sign_stuff(string (*lookup)(string); string *text_ptr)
|
||||
* Modifies: *text_ptr
|
||||
* Effects: This routine deals with handling the stuff after a '$'
|
||||
* for substitute. If *text_ptr starts with a valid
|
||||
* variable reference (minus the leading '$'), we look up
|
||||
* the variable using lookup and return its value.
|
||||
* *text_ptr is also advanced past the variable reference.
|
||||
* If a '$' starts *text_ptr, *text_ptr is advanced past it &
|
||||
* "$" returned. (This handles "$$" -> "$") Otherwise,
|
||||
* "$" is returned and *text_ptr is not advanced.
|
||||
* The returned string must not be freed.
|
||||
*/
|
||||
|
||||
static string
|
||||
eat_dollar_sign_stuff(string (*lookup)(string),
|
||||
string *text_ptr) /* Input/Output parameter */
|
||||
{
|
||||
char c;
|
||||
char closing_brace = 0;
|
||||
char *p = *text_ptr;
|
||||
char *variable_name_start;
|
||||
int variable_name_length;
|
||||
|
||||
/*
|
||||
* Handle "$$" -> "$" translation:
|
||||
*/
|
||||
c = *p;
|
||||
if (c=='$') {
|
||||
*text_ptr = p+1;
|
||||
return("$");
|
||||
}
|
||||
|
||||
/*
|
||||
* If opening brace present (i.e., '(' or '{'), skip it and save away
|
||||
* what closing brace we must see at the end of the variable reference:
|
||||
*/
|
||||
if (c=='{') {
|
||||
closing_brace = '}';
|
||||
c = *++p;
|
||||
} else if (c=='(') {
|
||||
closing_brace = ')';
|
||||
c = *++p;
|
||||
}
|
||||
|
||||
/*
|
||||
* Eat {identifier_char}* keeping track of what we ate:
|
||||
*/
|
||||
variable_name_start = p;
|
||||
variable_name_length = 0;
|
||||
while (c = *p, is_identifier_char(c)) {
|
||||
p++;
|
||||
variable_name_length++;
|
||||
}
|
||||
|
||||
/*
|
||||
* If there was an opening brace, there had better be a comparable
|
||||
* closing brace. If so, skip it. If not, we have an invalid variable
|
||||
* reference so return '$' without advancing *text_ptr.
|
||||
*/
|
||||
if (closing_brace) {
|
||||
if (c==closing_brace)
|
||||
c = *++p;
|
||||
else
|
||||
return("$");
|
||||
}
|
||||
|
||||
/*
|
||||
* Zero length variable names are not valid:
|
||||
*/
|
||||
if (!variable_name_length)
|
||||
return("$");
|
||||
|
||||
/*
|
||||
* We have a valid variable reference. Advance past it then lookup
|
||||
* its value and return it:
|
||||
*/
|
||||
*text_ptr = p;
|
||||
if (variable_name_length > MAX_IDENTIFIER_LENGTH)
|
||||
variable_name_length = MAX_IDENTIFIER_LENGTH;
|
||||
variable_name_start = string_CreateFromData(variable_name_start,
|
||||
variable_name_length);
|
||||
p = lookup(variable_name_start);
|
||||
free(variable_name_start);
|
||||
return(p);
|
||||
}
|
||||
|
||||
/*
|
||||
* string substitute(string (*lookup)(string); string text)
|
||||
* Effects: returns the result of expanding all variable
|
||||
* references in text using lookup. Example:
|
||||
* "test $foo.$bar baz" would be translated to
|
||||
* "text <foo>.<bar> baz" where "<foo>" is the value of
|
||||
* lookup("foo") and "<bar>" is the value of lookup("bar").
|
||||
* Variables are case sensitive and have the form
|
||||
* {identifier_char}+ where identifier_char is defined
|
||||
* in lexer.h by is_identifier_char. $(foo) and
|
||||
* ${foo} are alternate forms for $foo. In particular,
|
||||
* ${foo}bar is a reference to foo followed by "bar" while
|
||||
* $foobar is a reference to foobar. Incomplete variable
|
||||
* references like $(foo bar are displayed as if they
|
||||
* were not variable references. To allow quoting, "$$"
|
||||
* is translated to "$". Only the first
|
||||
* MAX_IDENTIFIER_LENGTH characters of an identifier are
|
||||
* significant. The strings returned by lookup are not
|
||||
* modified in any way or freed.
|
||||
*/
|
||||
|
||||
string
|
||||
substitute(string (*lookup)(string),
|
||||
string text)
|
||||
{
|
||||
string result_so_far = string_Copy("");
|
||||
char *p, *temp;
|
||||
|
||||
for (;;) {
|
||||
/*
|
||||
* Move [^$]* from start of text to end of result_so_far:
|
||||
*/
|
||||
for (p=text; *p && (*p)!='$'; p++) ;
|
||||
if (text != p) {
|
||||
temp = string_CreateFromData(text, p-text);
|
||||
text = p;
|
||||
result_so_far = string_Concat2(result_so_far, temp);
|
||||
free(temp);
|
||||
}
|
||||
|
||||
/*
|
||||
* If text now empty, exit -- the result is in result_so_far:
|
||||
*/
|
||||
if (!*text)
|
||||
return(result_so_far);
|
||||
|
||||
/*
|
||||
* Otherwise, text begins with a '$'. Eat it then call
|
||||
* eat_dollar_sign_stuff to process stuff after it.
|
||||
* Append result to result_so_far, update text, & continue.
|
||||
*/
|
||||
text++;
|
||||
p = eat_dollar_sign_stuff(lookup, &text);
|
||||
result_so_far = string_Concat2(result_so_far, p);
|
||||
}
|
||||
}
|
45
zwgc/substitute.h
Normal file
45
zwgc/substitute.h
Normal file
@ -0,0 +1,45 @@
|
||||
/* 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 <zephyr/mit-copyright.h>
|
||||
|
||||
#ifndef substitute_MODULE
|
||||
#define substitute_MODULE
|
||||
|
||||
#include "new_string.h"
|
||||
|
||||
/*
|
||||
* string substitute(string (*lookup)(string); string text)
|
||||
* Effects: returns the result of expanding all variable
|
||||
* references in text using lookup. Example:
|
||||
* "test $foo.$bar baz" would be translated to
|
||||
* "text <foo>.<bar> baz" where "<foo>" is the value of
|
||||
* lookup("foo") and "<bar>" is the value of lookup("bar").
|
||||
* Variables are case sensitive and have the form
|
||||
* {identifier_char}+ where identifier_char is defined
|
||||
* in lexer.h by is_identifier_char. $(foo) and
|
||||
* ${foo} are alternate forms for $foo. In particular,
|
||||
* ${foo}bar is a reference to foo followed by "bar" while
|
||||
* $foobar is a reference to foobar. Incomplete variable
|
||||
* references like $(foo bar are displayed as if they
|
||||
* were not variable references. To allow quoting, "$$"
|
||||
* is translated to "$". Only the first
|
||||
* MAX_IDENTIFIER_LENGTH characters of an identifier are
|
||||
* significant. The strings returned by lookup are not
|
||||
* modified in any way or freed.
|
||||
*/
|
||||
|
||||
extern string substitute(string (*)(string), string);
|
||||
|
||||
#endif
|
129
zwgc/text_operations.c
Normal file
129
zwgc/text_operations.c
Normal file
@ -0,0 +1,129 @@
|
||||
/* 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_text_operations_c[] = "$Id$";
|
||||
#endif
|
||||
|
||||
#include <zephyr/mit-copyright.h>
|
||||
|
||||
#include "new_memory.h"
|
||||
#include "text_operations.h"
|
||||
#include "char_stack.h"
|
||||
|
||||
string
|
||||
lany(string *text_ptr,
|
||||
string str)
|
||||
{
|
||||
string result, whats_left;
|
||||
char *p = *text_ptr;
|
||||
|
||||
while (*p && *str) p++, str++;
|
||||
|
||||
result = string_CreateFromData(*text_ptr, p - *text_ptr);
|
||||
whats_left = string_Copy(p);
|
||||
free(*text_ptr);
|
||||
*text_ptr = whats_left;
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
string
|
||||
lbreak(string *text_ptr,
|
||||
const character_class set)
|
||||
{
|
||||
string result, whats_left;
|
||||
char *p = *text_ptr;
|
||||
|
||||
while (*p && !set[(int)*p]) p++;
|
||||
|
||||
result = string_CreateFromData(*text_ptr, p - *text_ptr);
|
||||
whats_left = string_Copy(p);
|
||||
free(*text_ptr);
|
||||
*text_ptr = whats_left;
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
string
|
||||
lspan(string *text_ptr,
|
||||
character_class set)
|
||||
{
|
||||
string result, whats_left;
|
||||
char *p = *text_ptr;
|
||||
|
||||
while (*p && set[(int)*p]) p++;
|
||||
|
||||
result = string_CreateFromData(*text_ptr, p - *text_ptr);
|
||||
whats_left = string_Copy(p);
|
||||
free(*text_ptr);
|
||||
*text_ptr = whats_left;
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
string
|
||||
rany(string *text_ptr,
|
||||
string str)
|
||||
{
|
||||
string result, whats_left;
|
||||
string text = *text_ptr;
|
||||
char *p = text + strlen(text);
|
||||
|
||||
while (text<p && *str) p--, str++;
|
||||
|
||||
result = string_Copy(p);
|
||||
whats_left = string_CreateFromData(text, p - text);
|
||||
free(text);
|
||||
*text_ptr = whats_left;
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
string
|
||||
rbreak(string *text_ptr,
|
||||
character_class set)
|
||||
{
|
||||
string result, whats_left;
|
||||
string text = *text_ptr;
|
||||
char *p = text + strlen(text);
|
||||
|
||||
while (text<p && !set[(int)p[-1]]) p--;
|
||||
|
||||
result = string_Copy(p);
|
||||
whats_left = string_CreateFromData(text, p - text);
|
||||
free(text);
|
||||
*text_ptr = whats_left;
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
string
|
||||
rspan(string *text_ptr,
|
||||
character_class set)
|
||||
{
|
||||
string result, whats_left;
|
||||
string text = *text_ptr;
|
||||
char *p = text + strlen(text);
|
||||
|
||||
while (text<p && set[(int)p[-1]]) p--;
|
||||
|
||||
result = string_Copy(p);
|
||||
whats_left = string_CreateFromData(text, p - text);
|
||||
free(text);
|
||||
*text_ptr = whats_left;
|
||||
|
||||
return(result);
|
||||
}
|
29
zwgc/text_operations.h
Normal file
29
zwgc/text_operations.h
Normal file
@ -0,0 +1,29 @@
|
||||
/* 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 <zephyr/mit-copyright.h>
|
||||
|
||||
#ifndef text_operations_MODULE
|
||||
#define text_operations_MODULE
|
||||
|
||||
#include "character_class.h"
|
||||
|
||||
extern string lany(string *, string);
|
||||
extern string lbreak(string *, const character_class);
|
||||
extern string lspan(string *, character_class);
|
||||
extern string rany(string *, string);
|
||||
extern string rbreak(string *, character_class);
|
||||
extern string rspan(string *, character_class);
|
||||
|
||||
#endif
|
597
zwgc/tty_filter.c
Normal file
597
zwgc/tty_filter.c
Normal file
@ -0,0 +1,597 @@
|
||||
/* 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_tty_filter_c[] = "$Id$";
|
||||
#endif
|
||||
|
||||
#include <zephyr/mit-copyright.h>
|
||||
|
||||
/****************************************************************************/
|
||||
/* */
|
||||
/* The tty & plain filters: */
|
||||
/* */
|
||||
/****************************************************************************/
|
||||
#ifdef HAVE_TERMCAP_H
|
||||
#include <termcap.h>
|
||||
#else
|
||||
#ifdef HAVE_TERM_H
|
||||
#ifdef HAVE_TERMIO_H
|
||||
/* I blame Solaris. Solaris to blame. */
|
||||
#include <termio.h>
|
||||
#endif
|
||||
#ifdef HAVE_CURSES_H
|
||||
#include <curses.h>
|
||||
#endif
|
||||
#include <term.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <zephyr/zephyr.h>
|
||||
|
||||
#include "new_memory.h"
|
||||
#include "new_string.h"
|
||||
#include "variables.h"
|
||||
#include "string_dictionary_aux.h"
|
||||
#include "formatter.h"
|
||||
#include "zwgc.h"
|
||||
#include "error.h"
|
||||
#include "tty_filter.h"
|
||||
|
||||
/***************************************************************************/
|
||||
#ifndef HAVE_TERMCAP_H
|
||||
extern int tgetent();
|
||||
extern char *tgetstr(),*getenv();
|
||||
#ifdef linux
|
||||
extern speed_t ospeed;
|
||||
#else
|
||||
extern short ospeed;
|
||||
#endif
|
||||
char PC;
|
||||
#endif
|
||||
|
||||
/* Dictionary naming convention:
|
||||
|
||||
B.xxx is the termcap sequence to begin environment xxx.
|
||||
E.xxx is the termcap sequence to end environment xxx.
|
||||
|
||||
*/
|
||||
|
||||
static string_dictionary termcap_dict;
|
||||
static char code_buf[10240], *code_buf_pos = code_buf, *code;
|
||||
|
||||
/* Define the following commands:
|
||||
|
||||
(Hopefully) shared with all devices:
|
||||
|
||||
@center Guess.
|
||||
|
||||
@em Emphasis. User underline if available, else reverse video.
|
||||
@bold Bold letters. If not available, reverse video, else underline.
|
||||
@beep "bl" termcap entry, else "^G"
|
||||
|
||||
Other:
|
||||
|
||||
@blink "mb"/"me" termcap entry, else nothing.
|
||||
@rv "so"/"se" termcap entry.
|
||||
@u "us"/"ue" termcap entry.
|
||||
*/
|
||||
|
||||
#define TD_SET(k,v) (string_dictionary_Define(termcap_dict, (k), &ex)->value \
|
||||
= (v))
|
||||
#define EXPAND(k) (code = code_buf_pos, tputs(tmp, 1, tty_outc), \
|
||||
*code_buf_pos++ = 0, TD_SET(k, code))
|
||||
|
||||
static int
|
||||
tty_outc(int c)
|
||||
{
|
||||
*code_buf_pos++ = c;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
int
|
||||
tty_filter_init(char *drivername,
|
||||
char notfirst,
|
||||
int *pargc,
|
||||
char **argv)
|
||||
{
|
||||
static char st_buf[128];
|
||||
char tc_buf[1024], *p = st_buf, *tmp, *term;
|
||||
int ex;
|
||||
string_dictionary_binding *b;
|
||||
int isrealtty = string_Eq(drivername, "tty");
|
||||
struct termios tbuf;
|
||||
|
||||
ospeed = (tcgetattr(STDIN_FILENO, &tbuf) == 0) ? cfgetospeed(&tbuf) : 2400;
|
||||
|
||||
if (termcap_dict == (string_dictionary) NULL)
|
||||
termcap_dict = string_dictionary_Create(7);
|
||||
|
||||
if (!(term = getenv("TERM"))) { /* Only use termcap if $TERM. */
|
||||
if (isrealtty && !notfirst)
|
||||
/* only complain if initializing tty mode, and would be first
|
||||
available port */
|
||||
ERROR("$TERM not set. tty mode will be plain.\n");
|
||||
}
|
||||
#ifdef _AIX
|
||||
/*
|
||||
* This is a temporary KLUDGE to get around the problem where some people
|
||||
* might start zwgc in their ~/.startup.X and it hangs on the RISC/6000.
|
||||
* Apparently, the call to tgetent() with the Athena console window causes
|
||||
* the process to get stopped on tty access. Since the terminal type is
|
||||
* "dumb" (set by tcsh), we can pretty much assume there isn't anything
|
||||
* to setup from the termcap information.
|
||||
*/
|
||||
else if (!strcmp(term, "dumb")) { }
|
||||
#endif
|
||||
else {
|
||||
tgetent(tc_buf, term);
|
||||
|
||||
/* Step 1: get all of {rv,bold,u,bell,blink} that are available. */
|
||||
|
||||
/* We cheat here, and ignore the padding (if any) specified for
|
||||
the mode-change strings (it's a real pain to do "right") */
|
||||
|
||||
tmp = tgetstr("pc", &p);
|
||||
PC = (tmp) ? *tmp : 0;
|
||||
tmp = tgetstr("md", &p);
|
||||
if (tmp) { /* bold ? */
|
||||
EXPAND("B.bold");
|
||||
tmp = tgetstr("me",&p);
|
||||
EXPAND("E.bold");
|
||||
}
|
||||
tmp = tgetstr("mr", &p);
|
||||
if (tmp) { /* reverse video? */
|
||||
EXPAND("B.rw");
|
||||
tmp = tgetstr("me", &p);
|
||||
EXPAND("E.rw");
|
||||
}
|
||||
tmp = tgetstr("bl", &p);
|
||||
if (tmp) { /* Bell ? */
|
||||
EXPAND("B.bell");
|
||||
TD_SET("E.bell", NULL);
|
||||
}
|
||||
tmp = tgetstr("mb", &p);
|
||||
if (tmp) { /* Blink ? */
|
||||
EXPAND("B.blink");
|
||||
tmp = tgetstr("me", &p);
|
||||
EXPAND("E.blink");
|
||||
}
|
||||
tmp = tgetstr("us", &p);
|
||||
if (tmp) { /* Underline ? */
|
||||
EXPAND("B.u");
|
||||
tmp = tgetstr("ue", &p);
|
||||
EXPAND("E.u");
|
||||
}
|
||||
tmp = tgetstr("so", &p);
|
||||
if (tmp) { /* Standout ? */
|
||||
EXPAND("B.so");
|
||||
tmp = tgetstr("se", &p);
|
||||
EXPAND("E.so");
|
||||
}
|
||||
}
|
||||
/* Step 2: alias others to the nearest substitute */
|
||||
|
||||
/* Bold = so, else rv, else ul */
|
||||
if (NULL == string_dictionary_Lookup(termcap_dict, "B.bold")) {
|
||||
if((b = string_dictionary_Lookup(termcap_dict, "B.so"))) {
|
||||
TD_SET("B.bold", b->value);
|
||||
TD_SET("E.bold",
|
||||
string_dictionary_Lookup(termcap_dict, "E.so")->value);
|
||||
} else if ((b = string_dictionary_Lookup(termcap_dict, "B.rv"))) {
|
||||
TD_SET("B.bold", b->value);
|
||||
TD_SET("E.bold",
|
||||
string_dictionary_Lookup(termcap_dict, "E.rv")->value);
|
||||
} else if ((b = string_dictionary_Lookup(termcap_dict,"B.u"))) {
|
||||
TD_SET("B.bold", b->value);
|
||||
TD_SET("E.bold",
|
||||
string_dictionary_Lookup(termcap_dict, "E.u")->value);
|
||||
}
|
||||
}
|
||||
|
||||
/* Bell = ^G */
|
||||
if (NULL == string_dictionary_Lookup(termcap_dict, "B.bell")) {
|
||||
TD_SET("B.bell", "\007");
|
||||
TD_SET("E.bell", NULL);
|
||||
}
|
||||
|
||||
/* Underline -> nothing */
|
||||
/* Blink -> nothing */
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
/***************************************************************************/
|
||||
|
||||
|
||||
|
||||
|
||||
static int
|
||||
fixed_string_eq(string pattern,
|
||||
char *text,
|
||||
int text_length)
|
||||
{
|
||||
while (*pattern && text_length>0 && *pattern == *text) {
|
||||
pattern++;
|
||||
text++;
|
||||
text_length--;
|
||||
}
|
||||
|
||||
return(!*pattern && !text_length);
|
||||
}
|
||||
|
||||
typedef struct _tty_str_info {
|
||||
struct _tty_str_info *next;
|
||||
|
||||
char *str;
|
||||
int len;
|
||||
|
||||
char alignment; /* 'l', 'c', 'r', or ' ' to indicate newline */
|
||||
unsigned int bold_p : 1;
|
||||
unsigned int italic_p : 1;
|
||||
unsigned int bell_p : 1;
|
||||
unsigned int ignore: 1;
|
||||
} tty_str_info;
|
||||
|
||||
const char *info_default_string = "";
|
||||
|
||||
static void
|
||||
free_info(tty_str_info *info)
|
||||
{
|
||||
tty_str_info *next_info;
|
||||
|
||||
while (info) {
|
||||
next_info = info->next;
|
||||
if (info->str != info_default_string)
|
||||
free(info->str);
|
||||
free(info);
|
||||
info = next_info;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
do_mode_change(tty_str_info *current_mode_p,
|
||||
char *text,
|
||||
int text_length)
|
||||
{
|
||||
/* alignment commands: */
|
||||
if (fixed_string_eq("left", text, text_length) ||
|
||||
fixed_string_eq("l", text, text_length))
|
||||
current_mode_p->alignment = 'l';
|
||||
else if (fixed_string_eq("center", text, text_length) ||
|
||||
fixed_string_eq("c", text, text_length))
|
||||
current_mode_p->alignment = 'c';
|
||||
else if (fixed_string_eq("right", text, text_length) ||
|
||||
fixed_string_eq("r", text, text_length))
|
||||
current_mode_p->alignment = 'r';
|
||||
|
||||
/* font commands: */
|
||||
else if (fixed_string_eq("bold", text, text_length) ||
|
||||
fixed_string_eq("b", text, text_length))
|
||||
current_mode_p->bold_p = 1;
|
||||
else if (fixed_string_eq("italic", text, text_length) ||
|
||||
fixed_string_eq("i", text, text_length))
|
||||
current_mode_p->italic_p = 1;
|
||||
else if (fixed_string_eq("roman", text, text_length)) {
|
||||
current_mode_p->bold_p = 0;
|
||||
current_mode_p->italic_p = 0;
|
||||
} else if (fixed_string_eq("beep", text, text_length)) {
|
||||
current_mode_p->bell_p = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* commands ignored in tty mode: */
|
||||
else if (fixed_string_eq("color", text, text_length) ||
|
||||
fixed_string_eq("font", text, text_length)) {
|
||||
current_mode_p->ignore = 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
zwgc_transliterate(char *in, int inlen, char **out, int *outlen){
|
||||
int retval = 0;
|
||||
string notice_charset = var_get_variable("notice_charset");
|
||||
string tty_charset = var_get_variable("tty_charset");
|
||||
|
||||
if (string_Eq(notice_charset, "UNKNOWN") ||
|
||||
string_Eq(notice_charset, tty_charset) ||
|
||||
(retval = ZTransliterate(in, inlen,
|
||||
notice_charset, tty_charset,
|
||||
out, outlen)) != 0) {
|
||||
*out = string_CreateFromData(in, inlen);
|
||||
*outlen = inlen;
|
||||
}
|
||||
if (retval != 0)
|
||||
var_set_variable("error", strerror(retval));
|
||||
}
|
||||
|
||||
static tty_str_info *
|
||||
convert_desc_to_tty_str_info(desctype *desc)
|
||||
{
|
||||
tty_str_info *temp;
|
||||
tty_str_info *result = NULL;
|
||||
tty_str_info *last_result_block = NULL;
|
||||
int isbeep, did_beep = 0;
|
||||
|
||||
#if !defined(SABER) && defined(__STDC__)
|
||||
tty_str_info current_mode = { NULL, "", 0, 'l', 0 , 0, 0, 0};
|
||||
#else
|
||||
/* This is needed due to a bug in saber, and lack of pre-ANSI support. */
|
||||
tty_str_info current_mode;
|
||||
|
||||
current_mode.next = NULL;
|
||||
current_mode.str = info_default_string; /* "" */
|
||||
current_mode.len = 0;
|
||||
current_mode.alignment = 'l';
|
||||
current_mode.bold_p = 0;
|
||||
current_mode.italic_p = 0;
|
||||
current_mode.bell_p = 0;
|
||||
current_mode.ignore = 0;
|
||||
#endif
|
||||
|
||||
for (; desc->code!=DT_EOF; desc=desc->next) {
|
||||
isbeep = 0;
|
||||
/* Handle environments: */
|
||||
if (desc->code == DT_ENV) {
|
||||
/* PUSH! */
|
||||
temp = (tty_str_info *)malloc(sizeof(struct _tty_str_info));
|
||||
*temp = current_mode;
|
||||
current_mode.next = temp;
|
||||
|
||||
isbeep = do_mode_change(¤t_mode, desc->str, desc->len);
|
||||
if (!isbeep || did_beep)
|
||||
continue; /* process one beep, ignore other envs */
|
||||
} else if (desc->code == DT_END) {
|
||||
/* POP! */
|
||||
temp = current_mode.next;
|
||||
current_mode = *temp;
|
||||
free(temp);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Add new block (call it temp) to result: */
|
||||
temp = (tty_str_info *)malloc(sizeof(struct _tty_str_info));
|
||||
*temp = current_mode;
|
||||
if (last_result_block) {
|
||||
last_result_block->next = temp;
|
||||
last_result_block = temp;
|
||||
} else {
|
||||
result = temp;
|
||||
last_result_block = temp;
|
||||
}
|
||||
|
||||
if (isbeep) {
|
||||
/* special processing: need to insert a bell */
|
||||
string_dictionary_binding *b;
|
||||
b = string_dictionary_Lookup(termcap_dict,"B.bell");
|
||||
if (b) {
|
||||
temp->str = strdup(b->value);
|
||||
temp->len = string_Length(temp->str);
|
||||
} else
|
||||
/* shouldn't get here! */
|
||||
abort();
|
||||
did_beep++;
|
||||
continue;
|
||||
}
|
||||
if (desc->code == DT_STR) {
|
||||
/* just combine string info with current mode: */
|
||||
zwgc_transliterate(desc->str, desc->len, &temp->str, &temp->len);
|
||||
} else if (desc->code == DT_NL) {
|
||||
/* make the new block a ' ' alignment block with an empty string */
|
||||
temp->alignment = ' ';
|
||||
temp->len = 0;
|
||||
temp->ignore = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (last_result_block)
|
||||
last_result_block->next = NULL;
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
#define max(a,b) ((a)>(b)?(a):(b))
|
||||
|
||||
static int
|
||||
line_width(int left_width,
|
||||
int center_width,
|
||||
int right_width)
|
||||
{
|
||||
if (center_width>0) {
|
||||
if (left_width==0 && right_width==0)
|
||||
return(center_width);
|
||||
return(center_width+2+max(left_width,right_width)*2);
|
||||
} else {
|
||||
if (left_width && right_width)
|
||||
return(1+left_width+right_width);
|
||||
else
|
||||
return(left_width+right_width);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
calc_max_line_width(tty_str_info *info)
|
||||
{
|
||||
int max_line_width = 0;
|
||||
int left = 0;
|
||||
int center = 0;
|
||||
int right = 0;
|
||||
|
||||
for (; info; info=info->next) {
|
||||
if (info->ignore)
|
||||
continue;
|
||||
switch (info->alignment) {
|
||||
case 'l':
|
||||
left += info->len;
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
center += info->len;
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
right += info->len;
|
||||
break;
|
||||
|
||||
case ' ':
|
||||
#ifdef DEBUG
|
||||
if (zwgc_debug)
|
||||
printf("width: %d %d %d = %d\n", left, center, right,
|
||||
line_width(left, center, right));
|
||||
#endif
|
||||
max_line_width = max(max_line_width,
|
||||
line_width(left, center, right));
|
||||
left = center = right = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
if (zwgc_debug)
|
||||
printf("width: %d %d %d = %d\n", left, center, right,
|
||||
line_width(left, center, right));
|
||||
#endif
|
||||
max_line_width = max(max_line_width,
|
||||
line_width(left, center, right));
|
||||
|
||||
return(max_line_width);
|
||||
}
|
||||
|
||||
string
|
||||
tty_filter(string text,
|
||||
int use_fonts)
|
||||
{
|
||||
string text_copy = string_Copy(text);
|
||||
string result_so_far = string_Copy("");
|
||||
desctype *desc;
|
||||
int number_of_strs;
|
||||
int number_of_lines;
|
||||
tty_str_info *info, *info_head;
|
||||
int max_line_width;
|
||||
|
||||
desc = disp_get_cmds(text_copy, &number_of_strs, &number_of_lines);
|
||||
info_head = info = convert_desc_to_tty_str_info(desc);
|
||||
free_desc(desc);
|
||||
|
||||
#ifdef DEBUG
|
||||
if (zwgc_debug)
|
||||
{ tty_str_info *ptr;
|
||||
for (ptr=info; ptr; ptr=ptr->next) {
|
||||
printf("%c: %s %s %s <%s>\n", ptr->alignment,
|
||||
ptr->bold_p ? "(bold)" : "",
|
||||
ptr->italic_p ? "(italic)" : "",
|
||||
ptr->bell_p ? "(bell)" : "",
|
||||
string_CreateFromData(ptr->str, ptr->len));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
max_line_width = calc_max_line_width(info);
|
||||
dprintf1("max width = %d\n", max_line_width);
|
||||
|
||||
while (info) {
|
||||
string left, center, right;
|
||||
int left_width, center_width, right_width;
|
||||
char *temp;
|
||||
|
||||
left_width = center_width = right_width = 0;
|
||||
left = string_Copy("");
|
||||
center = string_Copy("");
|
||||
right = string_Copy("");
|
||||
|
||||
for (; info && info->alignment!=' '; info=info->next) {
|
||||
string item;
|
||||
|
||||
if (info->ignore)
|
||||
continue;
|
||||
|
||||
item = string_Copy("");
|
||||
|
||||
if (info->bold_p && use_fonts) {
|
||||
temp = string_dictionary_Fetch(termcap_dict, "B.bold");
|
||||
if (temp)
|
||||
item = string_Concat2(item, temp);
|
||||
} else if (info->italic_p && use_fonts) {
|
||||
temp = string_dictionary_Fetch(termcap_dict, "B.u");
|
||||
if (temp)
|
||||
item = string_Concat2(item, temp);
|
||||
}
|
||||
temp = string_CreateFromData(info->str, info->len);
|
||||
item = string_Concat2(item, temp);
|
||||
free(temp);
|
||||
|
||||
if (info->bold_p && use_fonts) {
|
||||
temp = string_dictionary_Fetch(termcap_dict, "E.bold");
|
||||
if (temp)
|
||||
item = string_Concat2(item, temp);
|
||||
} else if (info->italic_p && use_fonts) {
|
||||
temp = string_dictionary_Fetch(termcap_dict, "E.u");
|
||||
if (temp)
|
||||
item = string_Concat2(item, temp);
|
||||
}
|
||||
|
||||
switch (info->alignment) {
|
||||
default:
|
||||
case 'l':
|
||||
left = string_Concat2(left, item);
|
||||
left_width += info->len;
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
center = string_Concat2(center, item);
|
||||
center_width += info->len;
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
right = string_Concat2(right, item);
|
||||
right_width += info->len;
|
||||
break;
|
||||
}
|
||||
free(item);
|
||||
}
|
||||
|
||||
result_so_far = string_Concat2(result_so_far, left);
|
||||
if (center_width)
|
||||
while (left_width < (max_line_width-center_width)/2 ) {
|
||||
result_so_far = string_Concat2(result_so_far, " ");
|
||||
left_width++;
|
||||
}
|
||||
result_so_far = string_Concat2(result_so_far, center);
|
||||
left_width += center_width;
|
||||
|
||||
if (right_width)
|
||||
while (left_width<max_line_width-right_width) {
|
||||
result_so_far = string_Concat2(result_so_far, " ");
|
||||
left_width++;
|
||||
}
|
||||
result_so_far = string_Concat2(result_so_far, right);
|
||||
free(left); free(center); free(right);
|
||||
|
||||
if (info && info->alignment == ' ') {
|
||||
info = info->next;
|
||||
result_so_far = string_Concat2(result_so_far, "\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
free_info(info_head);
|
||||
free(text_copy);
|
||||
if (number_of_lines &&
|
||||
(result_so_far[string_Length(result_so_far)-1] != '\n'))
|
||||
/* CRLF-terminate all results */
|
||||
result_so_far = string_Concat2(result_so_far, "\r\n");
|
||||
return(result_so_far);
|
||||
}
|
2
zwgc/tty_filter.h
Normal file
2
zwgc/tty_filter.h
Normal file
@ -0,0 +1,2 @@
|
||||
extern int tty_filter_init(char *, char, int *, char **);
|
||||
extern string tty_filter(string, int);
|
4
zwgc/unsigned_long.h
Normal file
4
zwgc/unsigned_long.h
Normal file
@ -0,0 +1,4 @@
|
||||
/* $Id$
|
||||
*/
|
||||
|
||||
#define unsigned_long unsigned long
|
243
zwgc/variables.c
Normal file
243
zwgc/variables.c
Normal file
@ -0,0 +1,243 @@
|
||||
/* 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_variables_c[] = "$Id$";
|
||||
#endif
|
||||
|
||||
#include <zephyr/mit-copyright.h>
|
||||
|
||||
/****************************************************************************/
|
||||
/* */
|
||||
/* Module containing code to deal with description langauge variables: */
|
||||
/* */
|
||||
/****************************************************************************/
|
||||
|
||||
#include "new_memory.h"
|
||||
#include "notice.h"
|
||||
#include "string_dictionary_aux.h"
|
||||
#include "variables.h"
|
||||
|
||||
/*
|
||||
* fields_data[_length] - these point to the field data that the number
|
||||
* variables were last set to using
|
||||
* var_set_number_variables_to_fields.
|
||||
*/
|
||||
|
||||
static char *fields_data;
|
||||
static int fields_data_length = 0;
|
||||
|
||||
/*
|
||||
* [non_]number_variable_dict - contains the values of all the [non-]number
|
||||
* variables that have been set since the last
|
||||
* var_clear_all_variables call or (for numbers
|
||||
* only) var_set_number_variables_to_fields call.
|
||||
*/
|
||||
|
||||
static string_dictionary non_number_variable_dict = NULL;
|
||||
static string_dictionary number_variable_dict = NULL;
|
||||
|
||||
/*
|
||||
* Internal Routine:
|
||||
*
|
||||
* int is_digits(string text)
|
||||
* Effects: Returns true iff text matches [0-9]*. ("" matches...)
|
||||
*/
|
||||
|
||||
static int
|
||||
is_digits(string text)
|
||||
{
|
||||
for (; *text; text++)
|
||||
if (!isdigit(*text))
|
||||
return(0);
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Internal Routine:
|
||||
*
|
||||
* int is_number_variable(string text)
|
||||
* Effects: Returns true iff text matches [0-9]+.
|
||||
*/
|
||||
|
||||
#define is_number_variable(x) (isdigit(*(x)) && is_digits((x)))
|
||||
|
||||
/*
|
||||
* void var_clear_all_variables()
|
||||
* Requires: This routine must be called before any other
|
||||
* var module routine is called.
|
||||
* Modifies: All description language variables
|
||||
* Effects: Sets all description langauge variables to "".
|
||||
*/
|
||||
|
||||
void
|
||||
var_clear_all_variables(void)
|
||||
{
|
||||
if (non_number_variable_dict) {
|
||||
string_dictionary_SafeDestroy(non_number_variable_dict);
|
||||
string_dictionary_SafeDestroy(number_variable_dict);
|
||||
}
|
||||
|
||||
non_number_variable_dict = string_dictionary_Create(101);
|
||||
number_variable_dict = string_dictionary_Create(11);
|
||||
fields_data_length = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* string var_get_variable(string name)
|
||||
* Requires: var_clear_all_variables has been called
|
||||
* Effects: Returns the value of the description langauge variable
|
||||
* named name. The returned string is read-only and is
|
||||
* guarenteed to last only until the next var module
|
||||
* call. DO NOT FREE THIS STRING.
|
||||
*/
|
||||
|
||||
string
|
||||
var_get_variable(string name)
|
||||
{
|
||||
char *result;
|
||||
int field_to_get;
|
||||
static string last_get_field_call_result = NULL;
|
||||
|
||||
if (is_number_variable(name)) {
|
||||
result = string_dictionary_Fetch(number_variable_dict, name);
|
||||
if (result)
|
||||
return(result);
|
||||
|
||||
/*
|
||||
* Convert name to an integer avoiding overflow:
|
||||
*/
|
||||
while (*name=='0')
|
||||
name++;
|
||||
if (strlen(name)>12)
|
||||
field_to_get = 0; /* no way we'll have > 1 trillian fields... */
|
||||
else
|
||||
field_to_get = atoi(name);
|
||||
|
||||
if (!field_to_get)
|
||||
return("");
|
||||
if (last_get_field_call_result)
|
||||
free(last_get_field_call_result);
|
||||
last_get_field_call_result = get_field(fields_data,
|
||||
fields_data_length,
|
||||
field_to_get);
|
||||
return(last_get_field_call_result);
|
||||
}
|
||||
|
||||
if (!(result = string_dictionary_Fetch(non_number_variable_dict, name)))
|
||||
result = "";
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
/*
|
||||
* void var_set_variable(string name, value)
|
||||
* Requires: var_clear_all_variables has been called
|
||||
* Modifies: The value of description langauge variable
|
||||
* named name.
|
||||
* Effects: Sets the description langauge variable named name
|
||||
* to have the value value.
|
||||
*/
|
||||
|
||||
void
|
||||
var_set_variable(string name,
|
||||
string value)
|
||||
{
|
||||
string_dictionary_Set(is_number_variable(name) ? number_variable_dict
|
||||
: non_number_variable_dict, name, value);
|
||||
}
|
||||
|
||||
/*
|
||||
* void var_set_variable_to_number(string name; int number)
|
||||
* Requires: var_clear_all_variables has been called
|
||||
* Modifies: The value of description langauge variable
|
||||
* named name.
|
||||
* Effects: Sets the description langauge variable named name
|
||||
* to have as its value number's ascii representation.
|
||||
*/
|
||||
|
||||
void
|
||||
var_set_variable_to_number(string name,
|
||||
int number)
|
||||
{
|
||||
char buffer[20];
|
||||
|
||||
sprintf(buffer, "%d", number);
|
||||
var_set_variable(name, buffer);
|
||||
}
|
||||
|
||||
/*
|
||||
* void var_set_variable_then_free_value(string name, value)
|
||||
* Requires: var_clear_all_variables has been called, value is
|
||||
* on the heap.
|
||||
* Modifies: The value of description langauge variable
|
||||
* named name, value
|
||||
* Effects: Sets the description langauge variable named name
|
||||
* to have the value value then frees value. This
|
||||
* routine is slightly faster than calling var_set_variable
|
||||
* then freeing value. It is provided mainly for
|
||||
* convenience reasons.
|
||||
*/
|
||||
|
||||
void
|
||||
var_set_variable_then_free_value(string name,
|
||||
string value)
|
||||
{
|
||||
string_dictionary_binding *binding;
|
||||
int exists;
|
||||
|
||||
#ifdef DEBUG_MEMORY
|
||||
if (!memory__on_heap_p(value))
|
||||
abort(); /* <<<>>> */
|
||||
#endif
|
||||
|
||||
if (is_number_variable(name)) {
|
||||
var_set_variable(name, value);
|
||||
free(value);
|
||||
return;
|
||||
}
|
||||
|
||||
binding = string_dictionary_Define(non_number_variable_dict, name,
|
||||
&exists);
|
||||
if (exists)
|
||||
free(binding->value);
|
||||
binding->value = value;
|
||||
}
|
||||
|
||||
/*
|
||||
* void var_set_number_variables_to_fields(char *data, int length)
|
||||
* Requires: var_clear_all_variables has been called
|
||||
* Modifies: All numeric description language variables
|
||||
* Effects: Treats data[0]..data[length-1] as a series of
|
||||
* null-seperated fields. Sets $<number> (<number>
|
||||
* here means [0-9]+ to field # <number> in data.
|
||||
* Field 0 is defined to be "" as are all field #'s
|
||||
* greater than the number of fields in data.
|
||||
* Data[0]..data[length-1] must not be changed (or freed)
|
||||
* until either this call is made again with different
|
||||
* data or var_clear_all_variables is called.
|
||||
*/
|
||||
|
||||
void
|
||||
var_set_number_variables_to_fields(char *data,
|
||||
int length)
|
||||
{
|
||||
fields_data = data;
|
||||
fields_data_length = length;
|
||||
|
||||
string_dictionary_SafeDestroy(number_variable_dict);
|
||||
number_variable_dict = string_dictionary_Create(11);
|
||||
}
|
96
zwgc/variables.h
Normal file
96
zwgc/variables.h
Normal file
@ -0,0 +1,96 @@
|
||||
/* 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 <zephyr/mit-copyright.h>
|
||||
|
||||
#ifndef var_MODULE
|
||||
#define var_MODULE
|
||||
|
||||
#include "new_string.h"
|
||||
|
||||
/*
|
||||
* void var_clear_all_variables()
|
||||
* Requires: This routine must be called before any other
|
||||
* var module routine is called.
|
||||
* Modifies: All description language variables
|
||||
* Effects: Sets all description langauge variables to "".
|
||||
*/
|
||||
|
||||
extern void var_clear_all_variables(void);
|
||||
|
||||
/*
|
||||
* string var_get_variable(string name)
|
||||
* Requires: var_clear_all_variables has been called
|
||||
* Effects: Returns the value of the description langauge variable
|
||||
* named name. The returned string is read-only and is
|
||||
* guarenteed to last only until the next var module
|
||||
* call. DO NOT FREE THIS STRING.
|
||||
*/
|
||||
|
||||
extern string var_get_variable(string);
|
||||
|
||||
/*
|
||||
* void var_set_variable(string name, value)
|
||||
* Requires: var_clear_all_variables has been called
|
||||
* Modifies: The value of description langauge variable
|
||||
* named name.
|
||||
* Effects: Sets the description langauge variable named name
|
||||
* to have the value value.
|
||||
*/
|
||||
|
||||
extern void var_set_variable(string, string);
|
||||
|
||||
/*
|
||||
* void var_set_variable_to_number(string name; int number)
|
||||
* Requires: var_clear_all_variables has been called
|
||||
* Modifies: The value of description langauge variable
|
||||
* named name.
|
||||
* Effects: Sets the description langauge variable named name
|
||||
* to have as its value number's ascii representation.
|
||||
*/
|
||||
|
||||
extern void var_set_variable_to_number(string, int);
|
||||
|
||||
/*
|
||||
* void var_set_variable_then_free_value(string name, value)
|
||||
* Requires: var_clear_all_variables has been called, value is
|
||||
* on the heap.
|
||||
* Modifies: The value of description langauge variable
|
||||
* named name, value
|
||||
* Effects: Sets the description langauge variable named name
|
||||
* to have the value value then frees value. This
|
||||
* routine is slightly faster than calling var_set_variable
|
||||
* then freeing value. It is provided mainly for
|
||||
* convenience reasons.
|
||||
*/
|
||||
|
||||
extern void var_set_variable_then_free_value(string, string);
|
||||
|
||||
/*
|
||||
* void var_set_number_variables_to_fields(char *data, int length)
|
||||
* Requires: var_clear_all_variables has been called
|
||||
* Modifies: All numeric description language variables
|
||||
* Effects: Treats data[0]..data[length-1] as a series of
|
||||
* null-seperated fields. Sets $<number> (<number>
|
||||
* here means [0-9]+ to field # <number> in data.
|
||||
* Field 0 is defined to be "" as are all field #'s
|
||||
* greater than the number of fields in data.
|
||||
* Data[0]..data[length-1] must not be changed (or freed)
|
||||
* until either this call is made again with different
|
||||
* data or var_clear_all_variables is called.
|
||||
*/
|
||||
|
||||
extern void var_set_number_variables_to_fields(char *, int);
|
||||
|
||||
#endif
|
437
zwgc/xcut.c
Normal file
437
zwgc/xcut.c
Normal file
@ -0,0 +1,437 @@
|
||||
/* 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_xcut_c[] = "$Id$";
|
||||
#endif
|
||||
|
||||
#include <zephyr/mit-copyright.h>
|
||||
|
||||
/****************************************************************************/
|
||||
/* */
|
||||
/* Code to deal with handling X events: */
|
||||
/* */
|
||||
/****************************************************************************/
|
||||
|
||||
#ifndef X_DISPLAY_MISSING
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <zephyr/zephyr.h>
|
||||
#include "new_memory.h"
|
||||
#include "new_string.h"
|
||||
#include "X_gram.h"
|
||||
#include "zwgc.h"
|
||||
#include "xselect.h"
|
||||
#include "xmark.h"
|
||||
#include "error.h"
|
||||
#include "xrevstack.h"
|
||||
#include "X_driver.h"
|
||||
#include "xcut.h"
|
||||
#ifdef CMU_ZWGCPLUS
|
||||
#include "plus.h"
|
||||
#include "variables.h"
|
||||
#endif
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
extern long ttl;
|
||||
|
||||
static char *selected_text=NULL;
|
||||
static Window selecting_in = 0;
|
||||
|
||||
char *
|
||||
getSelectedText(void)
|
||||
{
|
||||
return(selected_text);
|
||||
}
|
||||
|
||||
#ifdef notdef
|
||||
static string
|
||||
x_gram_to_string(x_gram *gram)
|
||||
{
|
||||
int i, index, len;
|
||||
int last_y = -1;
|
||||
string temp;
|
||||
string text_so_far = string_Copy("");
|
||||
char *text;
|
||||
|
||||
text = gram->text;
|
||||
for (i=0; i<gram->numblocks; i++) {
|
||||
if (last_y != -1 && last_y != gram->blocks[i].y)
|
||||
text_so_far = string_Concat2(text_so_far, "\n");
|
||||
index = gram->blocks[i].strindex;
|
||||
len = gram->blocks[i].strlen;
|
||||
temp = string_CreateFromData(text+index, len);
|
||||
text_so_far = string_Concat2(text_so_far, temp);
|
||||
free(temp);
|
||||
last_y = gram->blocks[i].y;
|
||||
}
|
||||
|
||||
text_so_far = string_Concat2(text_so_far, "\n");
|
||||
return(text_so_far);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
#if 0
|
||||
/*ARGSUSED*/
|
||||
static Bool
|
||||
isShiftButton1(Display *dpy,
|
||||
XEvent *event,
|
||||
char *arg)
|
||||
{
|
||||
return(event->xbutton.state & (ShiftMask|Button1Mask));
|
||||
}
|
||||
|
||||
/*ARGSUSED*/
|
||||
static Bool
|
||||
isShiftButton3(Display *dpy,
|
||||
XEvent *event,
|
||||
char *arg)
|
||||
{
|
||||
return(event->xbutton.state & (ShiftMask|Button3Mask));
|
||||
}
|
||||
|
||||
static void
|
||||
getLastEvent(Display *dpy,
|
||||
unsigned int state,
|
||||
XEvent *event)
|
||||
{
|
||||
XEvent xev;
|
||||
|
||||
if (state & Button1Mask) {
|
||||
while(XCheckIfEvent(dpy,&xev,isShiftButton1,NULL))
|
||||
*event=xev;
|
||||
} else if (state & Button3Mask) {
|
||||
while(XCheckIfEvent(dpy,&xev,isShiftButton3,NULL))
|
||||
*event=xev;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
xunmark(Display *dpy,
|
||||
Window w,
|
||||
x_gram *gram,
|
||||
XContext desc_context)
|
||||
{
|
||||
XPointer gramp; /* Avoid strict aliasing violation */
|
||||
|
||||
if (gram == NULL) {
|
||||
if (XFindContext(dpy, w, desc_context, &gramp))
|
||||
return;
|
||||
gram = (x_gram *)gramp;
|
||||
}
|
||||
|
||||
xmarkClear();
|
||||
xmarkRedraw(dpy,w,gram,XMARK_REDRAW_OLD);
|
||||
}
|
||||
|
||||
/* This is out here so xdestroygram can get at it */
|
||||
|
||||
#define PRESSOP_NONE 0 /* nothing */
|
||||
#define PRESSOP_KILL 1 /* normal click */
|
||||
#define PRESSOP_SEL 2 /* shift left */
|
||||
#define PRESSOP_EXT 3 /* shift right */
|
||||
#define PRESSOP_NUKE 4 /* ctrl */
|
||||
#define PRESSOP_STOP 5 /* pressop cancelled by moving out of window */
|
||||
|
||||
static int current_pressop = PRESSOP_NONE;
|
||||
|
||||
void
|
||||
xdestroygram(Display *dpy,
|
||||
Window w,
|
||||
XContext desc_context,
|
||||
x_gram *gram)
|
||||
{
|
||||
struct timeval now;
|
||||
int i;
|
||||
|
||||
gettimeofday(&now,NULL);
|
||||
if ((gram->can_die.tv_sec == 0) ||
|
||||
(gram->can_die.tv_sec > now.tv_sec) ||
|
||||
((gram->can_die.tv_sec == now.tv_sec) &&
|
||||
(gram->can_die.tv_usec > now.tv_usec)))
|
||||
return;
|
||||
|
||||
if (w == selecting_in) {
|
||||
selecting_in = 0;
|
||||
xmarkClear();
|
||||
}
|
||||
current_pressop = PRESSOP_NONE;
|
||||
XDeleteContext(dpy, w, desc_context);
|
||||
XDestroyWindow(dpy, w);
|
||||
delete_gram(gram);
|
||||
free(gram->text);
|
||||
for (i=0; i < gram->numblocks; i++)
|
||||
free(gram->blocks[i].wstr);
|
||||
free(gram->blocks);
|
||||
#ifdef CMU_ZWGCPLUS
|
||||
if (gram->notice)
|
||||
list_del_notice(gram->notice);
|
||||
#endif
|
||||
free(gram);
|
||||
|
||||
#ifdef CMU_ZWGCPLUS
|
||||
XFlush(dpy);
|
||||
#endif
|
||||
|
||||
if (bottom_gram == NULL && unlinked == NULL) {
|
||||
/* flush colormap here */
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
xcut(Display *dpy,
|
||||
XEvent *event,
|
||||
XContext desc_context)
|
||||
{
|
||||
XPointer gramp; /* Avoid strict aliasing violation */
|
||||
x_gram *gram;
|
||||
Window w = event->xany.window;
|
||||
int changedbound;
|
||||
|
||||
/*
|
||||
* If event is for a window that's not ours anymore (say we're
|
||||
* in the process of deleting it...), ignore it:
|
||||
*/
|
||||
if (XFindContext(dpy, w, desc_context, &gramp))
|
||||
return;
|
||||
gram = (x_gram *)gramp;
|
||||
|
||||
/*
|
||||
* Dispatch on the event type:
|
||||
*/
|
||||
switch(event->type) {
|
||||
#ifdef CMU_ZWGCPLUS
|
||||
case KeyPress:
|
||||
{
|
||||
char c;
|
||||
char *plusvar;
|
||||
int res, metaflag;
|
||||
res = XLookupString(&(event->xkey), &c, 1, NULL, NULL);
|
||||
metaflag = event->xkey.state & Mod1Mask;
|
||||
|
||||
/* Recheck if zwgcplus is turned on;
|
||||
* Zephyr variables override zwgc variables
|
||||
*/
|
||||
|
||||
zwgcplus = 1;
|
||||
plusvar = ZGetVariable("zwgcplus")
|
||||
? ZGetVariable("zwgcplus") : var_get_variable("zwgcplus");
|
||||
|
||||
if ((plusvar[0]=='\0') || (strcmp(plusvar,"no") == 0))
|
||||
zwgcplus = 0;
|
||||
else {
|
||||
if (strcmp(plusvar,"no") == 0)
|
||||
zwgcplus = 0;
|
||||
if (strcmp(plusvar,"new") == 0)
|
||||
zwgcplus = 2;
|
||||
}
|
||||
|
||||
if (res != 0 && zwgcplus != 0)
|
||||
plus_retry_notice(gram->notice, c, metaflag);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case ClientMessage:
|
||||
if ((event->xclient.message_type == XA_WM_PROTOCOLS) &&
|
||||
(event->xclient.format == 32) &&
|
||||
(event->xclient.data.l[0] == (long)XA_WM_DELETE_WINDOW))
|
||||
xdestroygram(dpy,w,desc_context,gram);
|
||||
break;
|
||||
|
||||
case MapNotify:
|
||||
/* I don't like using the local time, but MapNotify events don't
|
||||
* come with a timestamp, and there's no way to query the server
|
||||
*/
|
||||
|
||||
if (gram->can_die.tv_sec == 0) {
|
||||
gettimeofday(&(gram->can_die),NULL);
|
||||
gram->can_die.tv_sec += (int) (ttl/1000);
|
||||
gram->can_die.tv_usec += (ttl%1000)*1000;
|
||||
}
|
||||
break;
|
||||
|
||||
case UnmapNotify:
|
||||
unlink_gram(gram);
|
||||
break;
|
||||
|
||||
case LeaveNotify:
|
||||
if (current_pressop == PRESSOP_KILL ||
|
||||
current_pressop == PRESSOP_NUKE)
|
||||
current_pressop = PRESSOP_STOP;
|
||||
break;
|
||||
|
||||
case MotionNotify:
|
||||
if (current_pressop == PRESSOP_SEL) {
|
||||
/* getLastEvent(dpy,Button1Mask,event); */
|
||||
changedbound=xmarkExtendFromFirst(gram,event->xmotion.x,
|
||||
event->xmotion.y);
|
||||
xmarkRedraw(dpy,w,gram,changedbound);
|
||||
} else if (current_pressop == PRESSOP_EXT) {
|
||||
/* getLastEvent(dpy,Button3Mask,event); */
|
||||
changedbound=xmarkExtendFromNearest(gram,event->xmotion.x,
|
||||
event->xmotion.y);
|
||||
xmarkRedraw(dpy,w,gram,changedbound);
|
||||
}
|
||||
break;
|
||||
|
||||
case ButtonPress:
|
||||
if (current_pressop != PRESSOP_NONE) {
|
||||
current_pressop = PRESSOP_STOP;
|
||||
} else if ((event->xbutton.button==Button4 ||
|
||||
event->xbutton.button==Button5) &&
|
||||
!get_bool_resource("scrollDelete","ScrollDelete",0)) {
|
||||
/* Ignore scroll wheel movement. */
|
||||
break;
|
||||
} else if ( (event->xbutton.state)&ShiftMask ) {
|
||||
if (event->xbutton.button==Button1) {
|
||||
if (selecting_in)
|
||||
xunmark(dpy,selecting_in,NULL,desc_context);
|
||||
if (selected_text) free(selected_text);
|
||||
selected_text = NULL;
|
||||
if (! xselGetOwnership(dpy,w,event->xbutton.time)) {
|
||||
XBell(dpy,0);
|
||||
ERROR("Unable to get ownership of PRIMARY selection.\n");
|
||||
selecting_in = 0;
|
||||
current_pressop = PRESSOP_STOP;
|
||||
} else {
|
||||
selecting_in = w;
|
||||
xmarkStart(gram,event->xbutton.x,event->xbutton.y);
|
||||
current_pressop = PRESSOP_SEL;
|
||||
}
|
||||
} else if ((event->xbutton.button==Button3) &&
|
||||
(w == selecting_in)) {
|
||||
if (selected_text) free(selected_text);
|
||||
selected_text = NULL;
|
||||
changedbound=xmarkExtendFromNearest(gram,event->xbutton.x,
|
||||
event->xbutton.y);
|
||||
xmarkRedraw(dpy,w,gram,changedbound);
|
||||
selected_text = xmarkGetText();
|
||||
/* this is ok, since to get here, the selection must be owned */
|
||||
current_pressop = PRESSOP_EXT;
|
||||
#ifdef CMU_ZWGCPLUS
|
||||
if (selected_text)
|
||||
XStoreBytes(dpy, selected_text, strlen(selected_text)+1);
|
||||
#endif
|
||||
}
|
||||
} else if ( (event->xbutton.state)&ControlMask ) {
|
||||
current_pressop = PRESSOP_NUKE;
|
||||
} else {
|
||||
current_pressop = PRESSOP_KILL;
|
||||
}
|
||||
break;
|
||||
|
||||
case ButtonRelease:
|
||||
if (current_pressop == PRESSOP_KILL) {
|
||||
xdestroygram(dpy,w,desc_context,gram);
|
||||
} else if (current_pressop == PRESSOP_SEL ||
|
||||
current_pressop == PRESSOP_EXT) {
|
||||
if (selected_text) free(selected_text);
|
||||
selected_text = xmarkGetText();
|
||||
#ifdef CMU_ZWGCPLUS
|
||||
if (selected_text)
|
||||
XStoreBytes(dpy, selected_text, strlen(selected_text)+1);
|
||||
#endif
|
||||
} else if (current_pressop == PRESSOP_NUKE) {
|
||||
XWindowAttributes wa;
|
||||
int gx,gy;
|
||||
Window temp;
|
||||
x_gram *next;
|
||||
|
||||
for (gram = bottom_gram ; gram ; gram = next) {
|
||||
XGetWindowAttributes(dpy,gram->w,&wa);
|
||||
XTranslateCoordinates(dpy,gram->w,wa.root,0,0,&gx,&gy,
|
||||
&temp);
|
||||
|
||||
next = gram->above;
|
||||
|
||||
if ((wa.map_state == IsViewable) &&
|
||||
(gx <= event->xbutton.x_root) &&
|
||||
(event->xbutton.x_root < gx+wa.width) &&
|
||||
(gy <= event->xbutton.y_root) &&
|
||||
(event->xbutton.y_root < gy+wa.height)) {
|
||||
xdestroygram(dpy,gram->w,desc_context,gram);
|
||||
}
|
||||
}
|
||||
for (gram = unlinked ; gram ; gram = next) {
|
||||
XGetWindowAttributes(dpy,gram->w,&wa);
|
||||
XTranslateCoordinates(dpy,gram->w,wa.root,0,0,&gx,&gy,
|
||||
&temp);
|
||||
|
||||
next = gram->above;
|
||||
|
||||
if ((wa.map_state == IsViewable) &&
|
||||
(gx <= event->xbutton.x_root) &&
|
||||
(event->xbutton.x_root < gx+wa.width) &&
|
||||
(gy <= event->xbutton.y_root) &&
|
||||
(event->xbutton.y_root < gy+wa.height)) {
|
||||
xdestroygram(dpy,gram->w,desc_context,gram);
|
||||
}
|
||||
}
|
||||
}
|
||||
current_pressop = PRESSOP_NONE;
|
||||
break;
|
||||
|
||||
case SelectionRequest:
|
||||
xselProcessSelection(dpy,w,event);
|
||||
break;
|
||||
|
||||
case SelectionClear:
|
||||
xselOwnershipLost(event->xselectionclear.time);
|
||||
if (w == selecting_in) {
|
||||
selecting_in = 0;
|
||||
xunmark(dpy,w,gram,desc_context);
|
||||
if (selected_text) free(selected_text);
|
||||
selected_text = NULL;
|
||||
}
|
||||
break;
|
||||
|
||||
#ifdef notdef
|
||||
case ConfigureNotify:
|
||||
#ifdef DEBUG
|
||||
if (zwgc_debug)
|
||||
printf("ConfigureNotify received for wid %lx above wid %lx\n",
|
||||
(long) w,(long) event->xconfigure.above);
|
||||
#endif
|
||||
if (gram->above==gram) {
|
||||
/* a new zgram. Straight to the bottom! */
|
||||
add_to_bottom(gram);
|
||||
} else if (event->xconfigure.above) {
|
||||
/* some zgram was pulled to the top */
|
||||
pull_to_top(gram);
|
||||
} else {
|
||||
/* Some zgram was pushed to the bottom */
|
||||
push_to_bottom(gram);
|
||||
}
|
||||
/* Note that there is no option to configure a zgram to the middle */
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
XFlush(dpy);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
5
zwgc/xcut.h
Normal file
5
zwgc/xcut.h
Normal file
@ -0,0 +1,5 @@
|
||||
/* $Id$
|
||||
*
|
||||
*/
|
||||
|
||||
void xdestroygram(Display *dpy, Window w, XContext desc_context, x_gram *gram);
|
55
zwgc/xerror.c
Normal file
55
zwgc/xerror.c
Normal file
@ -0,0 +1,55 @@
|
||||
/* 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_xerror_c[] = "$Id$";
|
||||
#endif
|
||||
|
||||
#include <zephyr/mit-copyright.h>
|
||||
|
||||
#ifndef X_DISPLAY_MISSING
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
#include "mux.h"
|
||||
#include "xerror.h"
|
||||
|
||||
int xerror_happened;
|
||||
|
||||
/*ARGSUSED*/
|
||||
static int
|
||||
xerrortrap(Display *dpy,
|
||||
XErrorEvent *xerrev)
|
||||
{
|
||||
xerror_happened = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*ARGSUSED*/
|
||||
void
|
||||
begin_xerror_trap(Display *dpy)
|
||||
{
|
||||
xerror_happened = 0;
|
||||
XSetErrorHandler(xerrortrap);
|
||||
}
|
||||
|
||||
void
|
||||
end_xerror_trap(Display *dpy)
|
||||
{
|
||||
XSync(dpy,False);
|
||||
XSetErrorHandler(NULL);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
28
zwgc/xerror.h
Normal file
28
zwgc/xerror.h
Normal file
@ -0,0 +1,28 @@
|
||||
/* 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".
|
||||
*/
|
||||
|
||||
#ifndef _XERROR_H_
|
||||
#define _XERROR_H_
|
||||
|
||||
#if (!defined(lint) && !defined(SABER))
|
||||
static const char rcsid_xerror_h[] = "$Id$";
|
||||
#endif
|
||||
|
||||
#include <zephyr/mit-copyright.h>
|
||||
|
||||
extern int xerror_happened;
|
||||
|
||||
void begin_xerror_trap(Display *);
|
||||
void end_xerror_trap(Display *);
|
||||
|
||||
#endif
|
418
zwgc/xmark.c
Normal file
418
zwgc/xmark.c
Normal file
@ -0,0 +1,418 @@
|
||||
/* 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".
|
||||
*/
|
||||
|
||||
#if (!defined(lint) && !defined(SABER))
|
||||
static const char rcsid_xmark_c[] = "$Id$";
|
||||
#endif
|
||||
|
||||
#include <zephyr/mit-copyright.h>
|
||||
|
||||
#include <sysdep.h>
|
||||
|
||||
#ifndef X_DISPLAY_MISSING
|
||||
|
||||
#include <X11/X.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <zephyr/zephyr.h>
|
||||
#include "X_gram.h"
|
||||
#include "X_fonts.h"
|
||||
#include "xmark.h"
|
||||
#include "new_string.h"
|
||||
|
||||
int markblock[3] = { -1 , -1 , -1 };
|
||||
int markchar[3] = { -1 , -1 , -1 };
|
||||
int markpixel[3] = { -1 , -1 , -1 };
|
||||
x_gram *markgram = NULL;
|
||||
|
||||
int oldblock[2] = { -1 , -1 };
|
||||
int oldpixel[2] = { -1 , -1 };
|
||||
x_gram *oldgram = NULL;
|
||||
|
||||
#define xmarkValid() \
|
||||
((markgram) && \
|
||||
(STARTBLOCK != -1) && (ENDBLOCK != -1) && \
|
||||
(STARTCHAR != -1) && (ENDCHAR != -1) && \
|
||||
(STARTPIXEL != -1) && (ENDPIXEL != -1))
|
||||
|
||||
void
|
||||
xmarkSetBound(x_gram *gram,
|
||||
int x,
|
||||
int y,
|
||||
int which)
|
||||
{
|
||||
int i, xofs, yofs;
|
||||
XFontSet font;
|
||||
xblock *xb;
|
||||
int num_chars;
|
||||
XRectangle *ink, *logical;
|
||||
|
||||
#ifdef MARK_DEBUG
|
||||
#define RETURN \
|
||||
if ((oldblock[which] != markblock[which]) || \
|
||||
(oldpixel[which] != markpixel[which])) { \
|
||||
printf("----- SetBound:\noldblock[%d]=%d, oldpixel[%d]=%d\nmarkblock[%d]=%d, markpixel[%d]=%d\n-----", \
|
||||
which,oldblock[which],which,oldpixel[which], \
|
||||
which,markblock[which],which,markpixel[which]); \
|
||||
} \
|
||||
return
|
||||
#else
|
||||
#define RETURN return
|
||||
#endif
|
||||
|
||||
if (markgram != gram) {
|
||||
xmarkClear();
|
||||
markgram = gram;
|
||||
} else if (which < XMARK_TEMP_BOUND) {
|
||||
oldblock[which]=markblock[which];
|
||||
oldpixel[which]=markpixel[which];
|
||||
}
|
||||
|
||||
/* Start at the top, fastforward to first span not too high. */
|
||||
for (i=0,xb=gram->blocks ;
|
||||
(i<gram->numblocks) && (xb->y2 < y) ;
|
||||
i++,xb++) ;
|
||||
|
||||
/* the point is after the end */
|
||||
if (i==gram->numblocks) {
|
||||
markblock[which]=i;
|
||||
markchar[which]=0;
|
||||
markpixel[which]=0;
|
||||
RETURN;
|
||||
}
|
||||
|
||||
/* is the point before the beginning of the line? */
|
||||
if (x <= xb->x1) {
|
||||
markblock[which]=i;
|
||||
markchar[which]=0;
|
||||
markpixel[which]=0;
|
||||
RETURN;
|
||||
}
|
||||
|
||||
/* is the point in the nether space between this line and the last? */
|
||||
if (y < xb->y1) {
|
||||
markblock[which]=i;
|
||||
markchar[which]=0;
|
||||
markpixel[which]=0;
|
||||
RETURN;
|
||||
}
|
||||
|
||||
for (yofs=xb->y1;(i<gram->numblocks) && (xb->y1 == yofs);i++,xb++) {
|
||||
|
||||
if (x <= xb->x2) {
|
||||
markblock[which]=i;
|
||||
|
||||
xofs=xb->x1;
|
||||
if ((x < xofs) || (y < xb->y1)) {
|
||||
markchar[which]=0;
|
||||
RETURN;
|
||||
}
|
||||
font = xb->font;
|
||||
#ifdef X_HAVE_UTF8_STRING
|
||||
Xutf8TextPerCharExtents(font, xb->wstr, xb->wlen,
|
||||
NULL, NULL, -1, &num_chars, NULL, NULL);
|
||||
#else
|
||||
XwcTextPerCharExtents(font, (wchar_t *)xb->wstr, xb->wlen,
|
||||
NULL, NULL, -1, &num_chars, NULL, NULL);
|
||||
#endif
|
||||
ink = malloc(num_chars * sizeof(XRectangle));
|
||||
logical = malloc(num_chars * sizeof(XRectangle));
|
||||
#ifdef X_HAVE_UTF8_STRING
|
||||
Xutf8TextPerCharExtents(font, xb->wstr, xb->wlen,
|
||||
ink, logical, num_chars, &num_chars, NULL, NULL);
|
||||
#else
|
||||
XwcTextPerCharExtents(font, (wchar_t *)xb->wstr, xb->wlen,
|
||||
ink, logical, num_chars, &num_chars, NULL, NULL);
|
||||
#endif
|
||||
for (i=0;i<num_chars;i++) {
|
||||
if (x <= xofs + logical[i].x + logical[i].width) {
|
||||
markchar[which]=i;
|
||||
markpixel[which]=xofs + logical[i].x - xb->x1;
|
||||
free(ink);
|
||||
free(logical);
|
||||
RETURN;
|
||||
}
|
||||
}
|
||||
free(ink);
|
||||
free(logical);
|
||||
}
|
||||
}
|
||||
|
||||
/* The endpoint is after the end of the block if the loop ends */
|
||||
markblock[which]=i;
|
||||
markchar[which]=0;
|
||||
markpixel[which]=0;
|
||||
RETURN;
|
||||
}
|
||||
|
||||
/* needs both bounds to be valid (!= -1) */
|
||||
static int
|
||||
xmarkNearest(int x,
|
||||
int y)
|
||||
{
|
||||
int middle;
|
||||
|
||||
xmarkSetBound(markgram,x,y,XMARK_TEMP_BOUND);
|
||||
middle=(ENDBLOCK+STARTBLOCK)/2;
|
||||
|
||||
if (markblock[XMARK_TEMP_BOUND] < middle)
|
||||
return(XMARK_START_BOUND);
|
||||
else if (markblock[XMARK_TEMP_BOUND] > middle)
|
||||
return(XMARK_END_BOUND);
|
||||
else {
|
||||
middle=(ENDCHAR+STARTCHAR)/2;
|
||||
if (markchar[XMARK_TEMP_BOUND] < middle)
|
||||
return(XMARK_START_BOUND);
|
||||
else
|
||||
return(XMARK_END_BOUND);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
xmarkExpose(Display *dpy,
|
||||
Window w,
|
||||
x_gram *gram,
|
||||
unsigned int b1,
|
||||
unsigned int p1,
|
||||
unsigned int b2,
|
||||
unsigned int p2)
|
||||
{
|
||||
#define swap(x,y) temp=(x); (x)=(y); (y)=temp
|
||||
unsigned int i,temp;
|
||||
XEvent event;
|
||||
#define expose (event.xexpose)
|
||||
|
||||
if (((int)b1==-1) || ((int)p1==-1) || ((int)b2==-1) || ((int)p2==-1))
|
||||
return;
|
||||
|
||||
if ((b1 > b2) || ((b1 == b2) && (p1 > p2))) {
|
||||
swap(b1,b2);
|
||||
swap(p1,p2);
|
||||
}
|
||||
|
||||
#if defined(_IBMR2) && !defined(__GNUC__) && defined(RS6000_OPT_BUG)
|
||||
/* A version of the AIX 3.1 RS/6000 C compiler needs this to prevent
|
||||
a core dump in the loop below. */
|
||||
&b1;
|
||||
#endif
|
||||
|
||||
expose.type=Expose;
|
||||
expose.display=dpy;
|
||||
expose.window=w;
|
||||
|
||||
for (i=b1;i<=b2;i++) {
|
||||
if (b1==b2) {
|
||||
expose.x=gram->blocks[i].x1+p1;
|
||||
expose.y=gram->blocks[i].y1;
|
||||
expose.width=p2-p1;
|
||||
expose.height=gram->blocks[i].y2-gram->blocks[i].y1;
|
||||
expose.count=0;
|
||||
} else if (i==b1) {
|
||||
expose.x=gram->blocks[i].x1+p1;
|
||||
expose.y=gram->blocks[i].y1;
|
||||
expose.width=gram->blocks[i].x2-p1;
|
||||
expose.height=gram->blocks[i].y2-gram->blocks[i].y1;
|
||||
expose.count=b2-i;
|
||||
} else if (i==b2) {
|
||||
expose.x=gram->blocks[i].x1;
|
||||
expose.y=gram->blocks[i].y1;
|
||||
expose.width=p2;
|
||||
expose.height=gram->blocks[i].y2-gram->blocks[i].y1;
|
||||
expose.count=b2-i;
|
||||
} else {
|
||||
expose.x=gram->blocks[i].x1;
|
||||
expose.y=gram->blocks[i].y1;
|
||||
expose.width=gram->blocks[i].x2-gram->blocks[i].x1;
|
||||
expose.height=gram->blocks[i].y2-gram->blocks[i].y1;
|
||||
expose.count=b2-i;
|
||||
}
|
||||
|
||||
#ifdef MARK_DEBUG
|
||||
if (expose.width && expose.height) {
|
||||
printf("---- markExpose:\nb1=%d p1=%d b2=%d p2=%d\n",b1,p1,b2,p2);
|
||||
printf("x=%d y=%d w=%d h=%d\n-----",
|
||||
expose.x,expose.y,expose.width,expose.height);
|
||||
}
|
||||
#endif
|
||||
if ((expose.width && expose.height) || (expose.count == 0))
|
||||
XSendEvent(dpy,w,True,ExposureMask,&event);
|
||||
}
|
||||
}
|
||||
|
||||
/* Public functions: */
|
||||
|
||||
void
|
||||
xmarkRedraw(Display *dpy,
|
||||
Window w,
|
||||
x_gram *gram,
|
||||
int range)
|
||||
{
|
||||
#define ob1 ((unsigned int) oldblock[XMARK_START_BOUND])
|
||||
#define ob2 ((unsigned int) oldblock[XMARK_END_BOUND])
|
||||
#define nb1 ((unsigned int) markblock[XMARK_START_BOUND])
|
||||
#define nb2 ((unsigned int) markblock[XMARK_END_BOUND])
|
||||
#define op1 ((unsigned int) oldpixel[XMARK_START_BOUND])
|
||||
#define op2 ((unsigned int) oldpixel[XMARK_END_BOUND])
|
||||
#define np1 ((unsigned int) markpixel[XMARK_START_BOUND])
|
||||
#define np2 ((unsigned int) markpixel[XMARK_END_BOUND])
|
||||
|
||||
if (range==XMARK_REDRAW_CURRENT) {
|
||||
if (!markgram) return;
|
||||
xmarkExpose(dpy,w,gram,nb1,np1,nb2,np2);
|
||||
} else if (range==XMARK_REDRAW_OLD) {
|
||||
if (!oldgram) return;
|
||||
xmarkExpose(dpy,w,gram,ob1,op1,ob2,op2);
|
||||
} else if (range==XMARK_REDRAW_START) {
|
||||
if (!markgram) return;
|
||||
xmarkExpose(dpy,w,gram,ob1,op1,nb1,np1);
|
||||
} else if (range==XMARK_REDRAW_END) {
|
||||
if (!markgram) return;
|
||||
xmarkExpose(dpy,w,gram,ob2,op2,nb2,np2);
|
||||
}
|
||||
#ifdef DEBUG
|
||||
else {
|
||||
printf("xmarkRedraw: This shouldn't happen!\n");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* needs both bounds to be valid (!= -1) */
|
||||
int
|
||||
xmarkSecond(void)
|
||||
{
|
||||
if (STARTBLOCK > ENDBLOCK)
|
||||
return(XMARK_START_BOUND);
|
||||
else if (STARTBLOCK < ENDBLOCK)
|
||||
return(XMARK_END_BOUND);
|
||||
else {
|
||||
if (STARTCHAR > ENDCHAR)
|
||||
return(XMARK_START_BOUND);
|
||||
else if (STARTCHAR < ENDCHAR)
|
||||
return(XMARK_END_BOUND);
|
||||
else
|
||||
return(XMARK_END_BOUND);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
xmarkClear(void)
|
||||
{
|
||||
oldblock[0]=markblock[0];
|
||||
oldblock[1]=markblock[1];
|
||||
oldpixel[0]=markpixel[0];
|
||||
oldpixel[1]=markpixel[1];
|
||||
oldgram=markgram;
|
||||
|
||||
markblock[0] = -1;
|
||||
markblock[1] = -1;
|
||||
markchar[0] = -1;
|
||||
markchar[1] = -1;
|
||||
markpixel[0] = -1;
|
||||
markpixel[1] = -1;
|
||||
markgram=NULL;
|
||||
}
|
||||
|
||||
int
|
||||
xmarkExtendFromFirst(x_gram *gram,
|
||||
int x,
|
||||
int y)
|
||||
{
|
||||
if (markgram != gram) {
|
||||
xmarkClear();
|
||||
markgram = gram;
|
||||
}
|
||||
|
||||
if (STARTBLOCK == -1) {
|
||||
xmarkStart(gram,x,y);
|
||||
xmarkEnd(gram,x,y);
|
||||
return(XMARK_REDRAW_CURRENT);
|
||||
} else if (ENDBLOCK == -1) {
|
||||
xmarkEnd(gram,x,y);
|
||||
return(XMARK_REDRAW_CURRENT);
|
||||
} else {
|
||||
xmarkSetBound(gram,x,y,XMARK_END_BOUND);
|
||||
return(XMARK_REDRAW_END);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
xmarkExtendFromNearest(x_gram *gram,
|
||||
int x,
|
||||
int y)
|
||||
{
|
||||
int bound;
|
||||
|
||||
if (markgram != gram) {
|
||||
xmarkClear();
|
||||
markgram = gram;
|
||||
}
|
||||
|
||||
if (STARTBLOCK == -1) {
|
||||
xmarkStart(gram,x,y);
|
||||
xmarkEnd(gram,x,y);
|
||||
return(XMARK_REDRAW_CURRENT);
|
||||
} else if (ENDBLOCK == -1) {
|
||||
xmarkEnd(gram,x,y);
|
||||
return(XMARK_REDRAW_CURRENT);
|
||||
} else {
|
||||
xmarkSetBound(gram,x,y,bound=xmarkNearest(x,y));
|
||||
return(bound==XMARK_START_BOUND?XMARK_REDRAW_START:XMARK_REDRAW_END);
|
||||
}
|
||||
}
|
||||
|
||||
char *
|
||||
xmarkGetText(void)
|
||||
{
|
||||
int i, idx, len;
|
||||
int last_y = -1;
|
||||
string temp;
|
||||
string text_so_far = string_Copy("");
|
||||
char *text = markgram->text;
|
||||
int startblock,endblock,startchar,endchar;
|
||||
|
||||
if (xmarkValid()) {
|
||||
if (xmarkSecond() == XMARK_END_BOUND) {
|
||||
startblock=STARTBLOCK;
|
||||
endblock=ENDBLOCK;
|
||||
startchar=STARTCHAR;
|
||||
endchar=ENDCHAR;
|
||||
} else {
|
||||
startblock=ENDBLOCK;
|
||||
endblock=STARTBLOCK;
|
||||
startchar=ENDCHAR;
|
||||
endchar=STARTCHAR;
|
||||
}
|
||||
|
||||
for (i=startblock; i<=endblock; i++) {
|
||||
if (last_y != -1 && last_y != markgram->blocks[i].y)
|
||||
text_so_far = string_Concat2(text_so_far, "\n");
|
||||
idx = markgram->blocks[i].strindex;
|
||||
len = markgram->blocks[i].strlen;
|
||||
if (startblock == endblock)
|
||||
temp = string_CreateFromData(text+idx+startchar,
|
||||
endchar-startchar);
|
||||
else if (i==startblock)
|
||||
temp = string_CreateFromData(text+idx+startchar,len-startchar);
|
||||
else if (i==endblock)
|
||||
temp = string_CreateFromData(text+idx,endchar);
|
||||
else
|
||||
temp = string_CreateFromData(text+idx,len);
|
||||
text_so_far = string_Concat2(text_so_far, temp);
|
||||
free(temp);
|
||||
last_y = markgram->blocks[i].y;
|
||||
}
|
||||
}
|
||||
|
||||
return(text_so_far);
|
||||
}
|
||||
|
||||
#endif /* X_DISPLAY_MISSING */
|
52
zwgc/xmark.h
Normal file
52
zwgc/xmark.h
Normal file
@ -0,0 +1,52 @@
|
||||
/* 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 <zephyr/mit-copyright.h>
|
||||
|
||||
#ifndef _XMARK_H_
|
||||
#define _XMARK_H_
|
||||
|
||||
#define XMARK_START_BOUND 0
|
||||
#define XMARK_END_BOUND 1
|
||||
#define XMARK_TEMP_BOUND 2
|
||||
|
||||
#define XMARK_REDRAW_CURRENT 1
|
||||
#define XMARK_REDRAW_OLD 2
|
||||
#define XMARK_REDRAW_START 3
|
||||
#define XMARK_REDRAW_END 4
|
||||
|
||||
#define xmarkStart(gram,x,y) xmarkSetBound(gram,x,y,XMARK_START_BOUND)
|
||||
#define xmarkEnd(gram,x,y) xmarkSetBound(gram,x,y,XMARK_END_BOUND)
|
||||
|
||||
extern int markblock[];
|
||||
extern int markchar[];
|
||||
extern int markpixel[];
|
||||
extern x_gram *markgram;
|
||||
|
||||
#define STARTBLOCK (markblock[XMARK_START_BOUND])
|
||||
#define ENDBLOCK (markblock[XMARK_END_BOUND])
|
||||
#define STARTCHAR (markchar[XMARK_START_BOUND])
|
||||
#define ENDCHAR (markchar[XMARK_END_BOUND])
|
||||
#define STARTPIXEL (markpixel[XMARK_START_BOUND])
|
||||
#define ENDPIXEL (markpixel[XMARK_END_BOUND])
|
||||
|
||||
extern void xmarkSetBound(x_gram *, int, int, int);
|
||||
extern int xmarkSecond(void);
|
||||
extern void xmarkRedraw(Display *, Window, x_gram *, int);
|
||||
extern void xmarkClear(void);
|
||||
extern int xmarkExtendFromFirst(x_gram *, int, int);
|
||||
extern int xmarkExtendFromNearest(x_gram *, int, int);
|
||||
extern char *xmarkGetText(void);
|
||||
|
||||
#endif
|
271
zwgc/xrevstack.c
Normal file
271
zwgc/xrevstack.c
Normal file
@ -0,0 +1,271 @@
|
||||
/* 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_xrevstack_c[] = "$Id$";
|
||||
#endif
|
||||
|
||||
#include <zephyr/mit-copyright.h>
|
||||
|
||||
#ifndef X_DISPLAY_MISSING
|
||||
|
||||
#ifndef TRUEREVSTACK
|
||||
#include <zephyr/zephyr.h>
|
||||
#include "X_gram.h"
|
||||
#include "xrevstack.h"
|
||||
|
||||
x_gram *bottom_gram = NULL;
|
||||
x_gram *unlinked = NULL;
|
||||
int reverse_stack = 0;
|
||||
|
||||
void
|
||||
add_to_bottom(x_gram *gram)
|
||||
{
|
||||
if (bottom_gram) {
|
||||
bottom_gram->below = gram;
|
||||
gram->below = NULL;
|
||||
gram->above = bottom_gram;
|
||||
bottom_gram = gram;
|
||||
} else {
|
||||
gram->above = NULL;
|
||||
gram->below = NULL;
|
||||
bottom_gram = gram;
|
||||
}
|
||||
}
|
||||
|
||||
/*ARGSUSED*/
|
||||
void
|
||||
pull_to_top(x_gram *gram)
|
||||
{
|
||||
}
|
||||
|
||||
/*ARGSUSED*/
|
||||
void
|
||||
push_to_bottom(x_gram *gram)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
delete_gram(x_gram *gram)
|
||||
{
|
||||
if (gram == bottom_gram) {
|
||||
if (gram->above) {
|
||||
bottom_gram = gram->above;
|
||||
bottom_gram->below = NULL;
|
||||
} else {
|
||||
bottom_gram = NULL;
|
||||
}
|
||||
} else if (gram == unlinked) {
|
||||
if (gram->above) {
|
||||
unlinked = gram->above;
|
||||
unlinked->below = NULL;
|
||||
} else {
|
||||
unlinked = NULL;
|
||||
}
|
||||
} else {
|
||||
if (gram->above)
|
||||
gram->above->below = gram->below;
|
||||
gram->below->above = gram->above;
|
||||
}
|
||||
|
||||
/* fix up above & below pointers so that calling delete_gram
|
||||
again is safe */
|
||||
gram->below = gram;
|
||||
gram->above = gram;
|
||||
}
|
||||
|
||||
void
|
||||
unlink_gram(x_gram *gram)
|
||||
{
|
||||
delete_gram(gram);
|
||||
|
||||
if (unlinked) {
|
||||
unlinked->below = gram;
|
||||
gram->below = NULL;
|
||||
gram->above = unlinked;
|
||||
unlinked = gram;
|
||||
} else {
|
||||
gram->above = NULL;
|
||||
gram->below = NULL;
|
||||
unlinked = gram;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef TRUEREVSTACK
|
||||
|
||||
#include "X_gram.h"
|
||||
#include "zwgc.h"
|
||||
#include <stdio.h>
|
||||
|
||||
x_gram *bottom_gram=NULL;
|
||||
static x_gram *top_gram=NULL;
|
||||
|
||||
#ifdef DEBUG
|
||||
void
|
||||
print_gram_list(char *str)
|
||||
{
|
||||
x_gram *gram;
|
||||
char buf[80];
|
||||
|
||||
if (zwgc_debug) {
|
||||
printf("----- From function %s: Top of tree\n",str);
|
||||
if (top_gram)
|
||||
if (top_gram->above)
|
||||
printf("Tree munged: something above top_gram\n");
|
||||
for (gram=top_gram;gram;gram=gram->below) {
|
||||
strncpy(buf,gram->text,63);
|
||||
buf[63]='\0';
|
||||
printf("wid %lx txt: %s\n",(long) gram->w,buf);
|
||||
}
|
||||
if (bottom_gram)
|
||||
if (bottom_gram->below)
|
||||
printf("Tree munged: something below bottom_gram\n");
|
||||
printf("----- Bottom of tree\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
pull_to_top(x_gram *gram)
|
||||
{
|
||||
if (gram==top_gram) {
|
||||
/* already here */
|
||||
return;
|
||||
} else if (top_gram==NULL) {
|
||||
/* no grams at all. Make gram both top and bottom */
|
||||
top_gram=gram;
|
||||
bottom_gram=gram;
|
||||
} else if (gram==bottom_gram) {
|
||||
/* bottom gram is special case */
|
||||
bottom_gram=bottom_gram->above;
|
||||
bottom_gram->below=NULL;
|
||||
top_gram->above=gram;
|
||||
gram->below=top_gram;
|
||||
top_gram=gram;
|
||||
} else {
|
||||
/* normal case of a gram in the middle */
|
||||
gram->above->below=gram->below;
|
||||
gram->below->above=gram->above;
|
||||
top_gram->above=gram;
|
||||
gram->below=top_gram;
|
||||
gram->above=NULL;
|
||||
top_gram=gram;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
print_gram_list("pull_to_top");
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
push_to_bottom(x_gram *gram)
|
||||
{
|
||||
if (gram==bottom_gram) {
|
||||
/* already here */
|
||||
return;
|
||||
} else if (bottom_gram==NULL) {
|
||||
/* no grams at all. Make gram both top and bottom */
|
||||
gram->above=NULL;
|
||||
gram->below=NULL;
|
||||
top_gram=gram;
|
||||
bottom_gram=gram;
|
||||
} else if (gram==top_gram) {
|
||||
/* top gram is special case */
|
||||
top_gram=top_gram->below;
|
||||
top_gram->above=NULL;
|
||||
bottom_gram->below=gram;
|
||||
gram->above=bottom_gram;
|
||||
bottom_gram=gram;
|
||||
} else {
|
||||
/* normal case of a gram in the middle */
|
||||
gram->above->below=gram->below;
|
||||
gram->below->above=gram->above;
|
||||
bottom_gram->below=gram;
|
||||
gram->above=bottom_gram;
|
||||
gram->below=NULL;
|
||||
bottom_gram=gram;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
print_gram_list("push_to_bottom");
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
unlink_gram(x_gram *gram)
|
||||
{
|
||||
if (top_gram==bottom_gram) {
|
||||
/* the only gram in the stack */
|
||||
top_gram=NULL;
|
||||
bottom_gram=NULL;
|
||||
} else if (gram==top_gram) {
|
||||
top_gram=gram->below;
|
||||
top_gram->above=NULL;
|
||||
} else if (gram==bottom_gram) {
|
||||
bottom_gram=gram->above;
|
||||
bottom_gram->below=NULL;
|
||||
} else {
|
||||
gram->above->below=gram->below;
|
||||
gram->below->above=gram->above;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
print_gram_list("unlink_gram");
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef notdef
|
||||
void
|
||||
add_to_top(x_gram *gram)
|
||||
{
|
||||
if (top_gram==NULL) {
|
||||
gram->above=NULL;
|
||||
gram->below=NULL;
|
||||
top_gram=gram;
|
||||
bottom_gram=gram;
|
||||
} else {
|
||||
top_gram->above=gram;
|
||||
gram->above=NULL;
|
||||
gram->below=top_gram;
|
||||
top_gram=gram;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
print_gram_list("add_to_top");
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
add_to_bottom(x_gram *gram)
|
||||
{
|
||||
if (bottom_gram==NULL) {
|
||||
gram->above=NULL;
|
||||
gram->below=NULL;
|
||||
top_gram=gram;
|
||||
bottom_gram=gram;
|
||||
} else {
|
||||
bottom_gram->below=gram;
|
||||
gram->above=bottom_gram;
|
||||
gram->below=NULL;
|
||||
bottom_gram=gram;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
print_gram_list("add_to_bottom");
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* TRUEREVSTACK */
|
||||
|
||||
#endif /* X_DISPLAY_MISSING */
|
||||
|
33
zwgc/xrevstack.h
Normal file
33
zwgc/xrevstack.h
Normal file
@ -0,0 +1,33 @@
|
||||
/* 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".
|
||||
*/
|
||||
|
||||
#ifndef _XREVSTACK_H_
|
||||
#define _XREVSTACK_H_
|
||||
|
||||
#if (!defined(lint) && !defined(SABER))
|
||||
static const char rcsid_xrevstack_h[] = "$Id$";
|
||||
#endif
|
||||
|
||||
#include <zephyr/mit-copyright.h>
|
||||
|
||||
extern x_gram *bottom_gram; /* for testing against NULL */
|
||||
extern x_gram *unlinked;
|
||||
extern int reverse_stack; /* is reverse stack on? */
|
||||
|
||||
extern void add_to_bottom(x_gram *);
|
||||
extern void delete_gram(x_gram *);
|
||||
extern void unlink_gram(x_gram *);
|
||||
extern void pull_to_top(x_gram *);
|
||||
extern void push_to_bottom(x_gram *);
|
||||
|
||||
#endif /* _XREVSTACK_H_ */
|
214
zwgc/xselect.c
Normal file
214
zwgc/xselect.c
Normal file
@ -0,0 +1,214 @@
|
||||
/* 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_xselect_c[] = "$Id$";
|
||||
#endif
|
||||
|
||||
#include <zephyr/mit-copyright.h>
|
||||
|
||||
/* xselect.c - ICCCM compliant cut-and-paste */
|
||||
/* also includes some other ICCCMisms, such as the WM_PROTOCOL handling */
|
||||
|
||||
#ifndef X_DISPLAY_MISSING
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xproto.h>
|
||||
#include <X11/Xatom.h>
|
||||
#include "new_string.h"
|
||||
#include "xselect.h"
|
||||
|
||||
extern char *getSelectedText(void);
|
||||
|
||||
static Time ownership_start = CurrentTime;
|
||||
static Time ownership_end = CurrentTime;
|
||||
Atom XA_WM_PROTOCOLS,XA_WM_DELETE_WINDOW;
|
||||
static Atom ZA_TARGETS,ZA_MULTIPLE,ZA_TIMESTAMP,ZA_ATOM_PAIR;
|
||||
|
||||
static struct _ZAtom {
|
||||
Atom *patom;
|
||||
char *name;
|
||||
} ZAtom[] = {
|
||||
{&XA_WM_PROTOCOLS,"WM_PROTOCOLS"},
|
||||
{&XA_WM_DELETE_WINDOW,"WM_DELETE_WINDOW"},
|
||||
{&ZA_TARGETS,"TARGETS"},
|
||||
{&ZA_MULTIPLE,"MULTIPLE"},
|
||||
{&ZA_TIMESTAMP,"TIMESTAMP"},
|
||||
{&ZA_ATOM_PAIR,"ATOM_PAIR"}
|
||||
};
|
||||
#define NumZAtoms (sizeof(ZAtom)/sizeof(struct _ZAtom))
|
||||
|
||||
/* internal static functions */
|
||||
|
||||
static void
|
||||
xselNotify(Display *dpy,
|
||||
XSelectionRequestEvent *selreq,
|
||||
Atom property)
|
||||
{
|
||||
XSelectionEvent ev;
|
||||
|
||||
ev.type=SelectionNotify;
|
||||
ev.requestor=selreq->requestor;
|
||||
ev.selection=selreq->selection;
|
||||
ev.target=selreq->target;
|
||||
ev.property=property;
|
||||
ev.time=selreq->time;
|
||||
|
||||
XSendEvent(dpy,ev.requestor,False,0,(XEvent *) &ev);
|
||||
}
|
||||
|
||||
/* pRequestAtoms and RequestAtoms should have the same size. */
|
||||
static Atom *pRequestAtoms[] = {
|
||||
&ZA_TARGETS,&ZA_MULTIPLE,&ZA_TIMESTAMP,NULL
|
||||
};
|
||||
static Atom RequestAtoms[] = {
|
||||
None,None,None,XA_STRING
|
||||
};
|
||||
#define NumRequestAtoms (sizeof(RequestAtoms)/sizeof(Atom))
|
||||
#define PROP(prop,targ) ((prop)!=None?(prop):(targ))
|
||||
#define ChangeProp(type,format,data,size) \
|
||||
XChangeProperty(dpy,w,PROP(property,target),(type),(format), \
|
||||
PropModeReplace, (unsigned char *) (data),(size))
|
||||
|
||||
static void
|
||||
xselSetProperties(Display *dpy,
|
||||
Window w,
|
||||
Atom property,
|
||||
Atom target,
|
||||
XSelectionRequestEvent *selreq)
|
||||
{
|
||||
if (target==ZA_TARGETS) {
|
||||
|
||||
ChangeProp(XA_ATOM,32,RequestAtoms,NumRequestAtoms);
|
||||
XSync(dpy,0);
|
||||
} else if (target==ZA_MULTIPLE) {
|
||||
Atom atype;
|
||||
int aformat;
|
||||
unsigned char *alistp; /* Avoid strict aliasing violation, we hope */
|
||||
Atom *alist;
|
||||
unsigned long alistsize,i;
|
||||
|
||||
XGetWindowProperty(dpy,w,property,0L,0L,False,ZA_ATOM_PAIR,&atype,
|
||||
&aformat,&i,&alistsize,&alistp);
|
||||
|
||||
if (alistsize)
|
||||
XGetWindowProperty(dpy,w,property,0L,alistsize/sizeof(Atom),False,
|
||||
ZA_ATOM_PAIR,&atype,&aformat,&alistsize,&i, &alistp);
|
||||
|
||||
alist = (Atom *)alistp;
|
||||
alistsize/=(sizeof(Atom)/4);
|
||||
for (i=0;i<alistsize;i+=2)
|
||||
xselSetProperties(dpy,w,alist[i+1],alist[i],selreq);
|
||||
|
||||
XFree((char *) alist);
|
||||
} else if (target==ZA_TIMESTAMP) {
|
||||
ChangeProp(XA_INTEGER,32,&ownership_start,1);
|
||||
XSync(dpy,0);
|
||||
} else if (target==XA_STRING) {
|
||||
char *selected;
|
||||
|
||||
selected = getSelectedText();
|
||||
if (selected) {
|
||||
ChangeProp(XA_STRING,8,selected,string_Length(selected));
|
||||
} else {
|
||||
/* This should only happen if the pasting client is out of
|
||||
spec (or if this program is buggy), but it could happen */
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr,
|
||||
"SelectionRequest event received for unowned selection: requestor wid=0x%lx", (unsigned long)w);
|
||||
#endif
|
||||
ChangeProp(XA_STRING,8,"",0);
|
||||
}
|
||||
XSync(dpy,0);
|
||||
}
|
||||
|
||||
xselNotify(dpy,selreq,property);
|
||||
}
|
||||
|
||||
/* global functions */
|
||||
|
||||
void
|
||||
xicccmInitAtoms(Display *dpy)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i=0;i<NumZAtoms;i++)
|
||||
*(ZAtom[i].patom)=XInternAtom(dpy,ZAtom[i].name,False);
|
||||
for (i=0;i<NumRequestAtoms;i++)
|
||||
if (pRequestAtoms[i])
|
||||
RequestAtoms[i] = *(pRequestAtoms[i]);
|
||||
}
|
||||
|
||||
int
|
||||
xselGetOwnership(Display *dpy,
|
||||
Window w,
|
||||
Time when)
|
||||
{
|
||||
int temp;
|
||||
|
||||
XSetSelectionOwner(dpy,XA_PRIMARY,w,when);
|
||||
temp=(w == XGetSelectionOwner(dpy,XA_PRIMARY));
|
||||
|
||||
if (temp)
|
||||
ownership_start = when;
|
||||
|
||||
return(temp);
|
||||
}
|
||||
|
||||
/* Get the selection. Return !0 if success, 0 if fail */
|
||||
int
|
||||
xselProcessSelection(Display *dpy,
|
||||
Window w,
|
||||
XEvent *event)
|
||||
{
|
||||
XSelectionRequestEvent *selreq = &(event->xselectionrequest);
|
||||
|
||||
#ifdef DEBUG
|
||||
if ((selreq->owner != w) || (selreq->selection != XA_PRIMARY))
|
||||
fprintf(stderr,"SelectionRequest event has bogus field values\n");
|
||||
#endif
|
||||
|
||||
if ((ownership_start == CurrentTime) ||
|
||||
(((selreq->time != CurrentTime) &&
|
||||
(selreq->time < ownership_start)) ||
|
||||
((ownership_end != CurrentTime) &&
|
||||
(ownership_end > ownership_start) &&
|
||||
(selreq->time > ownership_end))))
|
||||
xselNotify(dpy,selreq,None);
|
||||
else
|
||||
xselSetProperties(dpy,selreq->requestor,selreq->property,selreq->target,
|
||||
selreq);
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
void
|
||||
xselOwnershipLost(Time when)
|
||||
{
|
||||
ownership_end = when;
|
||||
}
|
||||
|
||||
/*ARGSUSED*/
|
||||
void
|
||||
xselGiveUpOwnership(Display *dpy,
|
||||
Window w)
|
||||
{
|
||||
XSetSelectionOwner(dpy,XA_PRIMARY,None,ownership_start);
|
||||
|
||||
ownership_end=ownership_start; /* Is this right? what should I use? */
|
||||
}
|
||||
|
||||
#endif /* X_DISPLAY_MISSING */
|
||||
|
28
zwgc/xselect.h
Normal file
28
zwgc/xselect.h
Normal file
@ -0,0 +1,28 @@
|
||||
/* 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 <zephyr/mit-copyright.h>
|
||||
|
||||
#ifndef _XSELECT_H_
|
||||
#define _XSELECT_H_
|
||||
|
||||
extern void xicccmInitAtoms(Display *);
|
||||
extern int xselGetOwnership(Display *, Window, Time);
|
||||
extern int xselProcessSelection(Display *, Window, XEvent *);
|
||||
extern void xselOwnershipLost(Time);
|
||||
extern void xselGiveUpOwnership(Display *, Window);
|
||||
|
||||
extern Atom XA_WM_PROTOCOLS, XA_WM_DELETE_WINDOW;
|
||||
|
||||
#endif
|
833
zwgc/xshow.c
Normal file
833
zwgc/xshow.c
Normal file
@ -0,0 +1,833 @@
|
||||
/* 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_xshow_c[] = "$Id$";
|
||||
#endif
|
||||
|
||||
#include <zephyr/mit-copyright.h>
|
||||
|
||||
#ifndef X_DISPLAY_MISSING
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/Xresource.h>
|
||||
#include <zephyr/zephyr.h>
|
||||
#include "pointer_dictionary.h"
|
||||
#include "new_memory.h"
|
||||
#include "new_string.h"
|
||||
#include "formatter.h"
|
||||
#include "variables.h"
|
||||
#include "zwgc.h"
|
||||
#include "X_driver.h"
|
||||
#include "X_fonts.h"
|
||||
#include "X_gram.h"
|
||||
#include "xmode_stack.h"
|
||||
#ifdef CMU_ZWGCPLUS
|
||||
#include <zephyr/zephyr.h>
|
||||
#include "xrevstack.h"
|
||||
#include "plus.h"
|
||||
#include "xcut.h"
|
||||
#endif
|
||||
|
||||
#define max(a,b) ((a)>(b)?(a):(b))
|
||||
|
||||
XContext desc_context;
|
||||
|
||||
extern int internal_border_width;
|
||||
extern unsigned long default_bgcolor;
|
||||
extern unsigned long default_fgcolor;
|
||||
|
||||
void
|
||||
xshowinit(void)
|
||||
{
|
||||
desc_context = XUniqueContext();
|
||||
}
|
||||
|
||||
struct res_dict_type {
|
||||
pointer_dictionary dict;
|
||||
char * resname_suffix;
|
||||
char * resclass;
|
||||
};
|
||||
|
||||
static char *
|
||||
xres_get_resource(struct res_dict_type *restype,
|
||||
char *style)
|
||||
{
|
||||
char *desc;
|
||||
pointer_dictionary_binding *binding;
|
||||
int exists;
|
||||
char *value;
|
||||
|
||||
desc=string_Concat("style.", style);
|
||||
desc=string_Concat2(desc, restype->resname_suffix);
|
||||
|
||||
if (!restype->dict)
|
||||
restype->dict = pointer_dictionary_Create(37);
|
||||
binding = pointer_dictionary_Define(restype->dict, desc, &exists);
|
||||
|
||||
if (exists) {
|
||||
free(desc);
|
||||
return((string) binding->value);
|
||||
} else {
|
||||
value=get_string_resource(desc, restype->resclass);
|
||||
free(desc);
|
||||
if (value==NULL)
|
||||
pointer_dictionary_Delete(restype->dict, binding);
|
||||
else
|
||||
binding->value=(pointer) value;
|
||||
return value; /* If resource returns NULL, return NULL also */
|
||||
}
|
||||
}
|
||||
|
||||
static struct res_dict_type geometry_resources = {
|
||||
NULL, ".geometry", "StyleKey.Style1.Style2.Style3.GeometryKey",
|
||||
};
|
||||
|
||||
static struct res_dict_type bgcolor_resources = {
|
||||
NULL, ".background", "StyleKey.Style1.Style2.Style3.BackgroundKey",
|
||||
};
|
||||
|
||||
#define xres_get_geometry(style) xres_get_resource(&geometry_resources,style)
|
||||
#define xres_get_bgcolor(style) xres_get_resource(&bgcolor_resources,style)
|
||||
|
||||
static struct res_dict_type fgcolor_resources = {
|
||||
NULL, ".foreground",
|
||||
"StyleKey.Style1.Style2.Style3.SubstyleKey.Substyle.ForegroundKey",
|
||||
};
|
||||
|
||||
/*ARGSUSED*/
|
||||
static char *
|
||||
mode_to_colorname (Display *dpy,
|
||||
char *style,
|
||||
xmode *mode)
|
||||
{
|
||||
char *desc, *result;
|
||||
|
||||
desc = string_Concat (style, ".substyle.");
|
||||
desc = string_Concat2 (desc, mode->substyle);
|
||||
result = xres_get_resource (&fgcolor_resources, desc);
|
||||
free (desc);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
fixup_and_draw(Display *dpy,
|
||||
char *style,
|
||||
xauxblock *auxblocks,
|
||||
xblock *blocks,
|
||||
int num,
|
||||
xlinedesc *lines,
|
||||
int numlines,
|
||||
int beepcount)
|
||||
{
|
||||
int gram_xalign = 1;
|
||||
int gram_yalign = 1;
|
||||
int gram_xpos, gram_ypos, gram_xsize, gram_ysize;
|
||||
|
||||
x_gram *gram;
|
||||
int strindex = 0;
|
||||
|
||||
int line, block=0;
|
||||
int maxwidth=0, chars=0, maxascent, maxdescent;
|
||||
int ssize, lsize,csize, rsize, width = 0;
|
||||
int i, ascent, descent;
|
||||
|
||||
int yofs = internal_border_width;
|
||||
int lofs, cofs, rofs;
|
||||
int ystart,yend;
|
||||
|
||||
char *bgstr, *geometry, xpos[10], ypos[10], xfrom, yfrom;
|
||||
XFontSetExtents *fse;
|
||||
|
||||
gram = (x_gram *)malloc(sizeof(x_gram));
|
||||
|
||||
/* Find total lengths of left, center, and right parts. Also find the
|
||||
length of the longest line and the total number of characters. */
|
||||
|
||||
for (line = 0; line < numlines; line++) {
|
||||
lsize = csize = rsize = 0;
|
||||
maxascent = maxdescent = 0;
|
||||
|
||||
/* add up sizes for each block, get max ascent and descent */
|
||||
|
||||
for (i = 0; i < lines[line].numblock; i++, block++) {
|
||||
chars += auxblocks[block].len;
|
||||
#ifdef X_HAVE_UTF8_STRING
|
||||
ssize = Xutf8TextEscapement(auxblocks[block].font,
|
||||
blocks[block].wstr,
|
||||
blocks[block].wlen);
|
||||
#else
|
||||
ssize = XwcTextEscapement(auxblocks[block].font,
|
||||
(wchar_t *)blocks[block].wstr,
|
||||
blocks[block].wlen);
|
||||
#endif
|
||||
auxblocks[block].width = ssize;
|
||||
fse = XExtentsOfFontSet(auxblocks[block].font);
|
||||
ascent = -fse->max_logical_extent.y;
|
||||
descent = fse->max_logical_extent.y + fse->max_logical_extent.height;
|
||||
if (ascent > maxascent)
|
||||
maxascent = ascent;
|
||||
if (descent > maxdescent)
|
||||
maxdescent = descent;
|
||||
switch (auxblocks[block].align) {
|
||||
case LEFTALIGN:
|
||||
lsize += ssize;
|
||||
break;
|
||||
|
||||
case CENTERALIGN:
|
||||
csize += ssize;
|
||||
break;
|
||||
|
||||
case RIGHTALIGN:
|
||||
rsize += ssize;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* save what we need to do size fixups */
|
||||
|
||||
if (maxascent > lines[line].ascent)
|
||||
lines[line].ascent = maxascent;
|
||||
if (maxdescent > lines[line].descent)
|
||||
lines[line].descent = maxdescent;
|
||||
lines[line].lsize = lsize;
|
||||
lines[line].csize = csize;
|
||||
lines[line].rsize = rsize;
|
||||
|
||||
/* get width of line and see if it is bigger than the max width */
|
||||
|
||||
switch ((lsize ? 1 : 0) + (csize ?2 : 0) + (rsize ? 4 : 0)) {
|
||||
#ifdef DEBUG
|
||||
default:
|
||||
abort();
|
||||
#endif
|
||||
|
||||
case 0:
|
||||
width = 0;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
width = lsize;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
width = csize;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
/* in all these cases, we just want to add the width of *any*
|
||||
space, so the first font will do just fine. */
|
||||
/* XXX implicit assumption that a line must have at least one
|
||||
block, so that there is indeed a reasonable font in
|
||||
auxblocks[0].font */
|
||||
width = lsize * 2 + csize + XmbTextEscapement(auxblocks[0].font, " ", 1);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
width = rsize;
|
||||
break;
|
||||
|
||||
case 5:
|
||||
width = lsize + rsize + XmbTextEscapement(auxblocks[0].font, " ", 1);
|
||||
break;
|
||||
|
||||
case 6:
|
||||
width = csize + rsize * 2 + XmbTextEscapement(auxblocks[0].font, " ", 1);
|
||||
break;
|
||||
|
||||
case 7:
|
||||
width = max(lsize, rsize) * 2 + csize +
|
||||
XmbTextEscapement(auxblocks[0].font, " ", 1) * 2;
|
||||
break;
|
||||
}
|
||||
if (width > maxwidth)
|
||||
maxwidth = width;
|
||||
}
|
||||
|
||||
/* fixup x,y for each block, create big string and indices into it */
|
||||
/* set x1,y1,x2,y2 of each block also. */
|
||||
|
||||
gram->text = (char *)malloc(chars);
|
||||
block = 0;
|
||||
|
||||
for (line = 0; line < numlines; line++) {
|
||||
lofs = internal_border_width;
|
||||
cofs = ((maxwidth - lines[line].csize) >> 1) + internal_border_width;
|
||||
rofs = maxwidth - lines[line].rsize + internal_border_width;
|
||||
ystart = yofs;
|
||||
yofs += lines[line].ascent;
|
||||
yend = yofs + lines[line].descent + 1; /* +1 because lines look scrunched
|
||||
without it. */
|
||||
|
||||
for (i = 0; i < lines[line].numblock; i++, block++) {
|
||||
blocks[block].font = auxblocks[block].font;
|
||||
switch (auxblocks[block].align) {
|
||||
case LEFTALIGN:
|
||||
blocks[block].x = lofs;
|
||||
blocks[block].x1 = lofs;
|
||||
lofs += auxblocks[block].width;
|
||||
blocks[block].x2 = lofs;
|
||||
break;
|
||||
|
||||
case CENTERALIGN:
|
||||
blocks[block].x = cofs;
|
||||
blocks[block].x1 = cofs;
|
||||
cofs += auxblocks[block].width;
|
||||
blocks[block].x2 = cofs;
|
||||
break;
|
||||
|
||||
case RIGHTALIGN:
|
||||
blocks[block].x = rofs;
|
||||
blocks[block].x1 = rofs;
|
||||
rofs += auxblocks[block].width;
|
||||
blocks[block].x2 = rofs;
|
||||
break;
|
||||
}
|
||||
blocks[block].y = yofs;
|
||||
blocks[block].y1 = ystart;
|
||||
blocks[block].y2 = yend;
|
||||
blocks[block].strindex = strindex;
|
||||
blocks[block].strlen = auxblocks[block].len;
|
||||
strncpy(gram->text + strindex, auxblocks[block].str,
|
||||
auxblocks[block].len);
|
||||
strindex += blocks[block].strlen;
|
||||
}
|
||||
|
||||
yofs = yend;
|
||||
|
||||
}
|
||||
|
||||
geometry = var_get_variable("X_geometry");
|
||||
if (geometry[0] == '\0')
|
||||
geometry = xres_get_geometry(style);
|
||||
if (geometry == NULL)
|
||||
geometry = var_get_variable("default_X_geometry");
|
||||
if (geometry[0] == '\0')
|
||||
geometry = "+0+0";
|
||||
|
||||
sscanf(geometry, "%c%[0123456789c]%c%[0123456789c]", &xfrom, xpos,
|
||||
&yfrom, ypos);
|
||||
|
||||
if (xpos[0] == 'c') {
|
||||
gram_xalign = 0;
|
||||
gram_xpos = 0;
|
||||
} else
|
||||
gram_xpos = atoi(xpos);
|
||||
if (xfrom == '-')
|
||||
gram_xalign *= -1;
|
||||
|
||||
if (ypos[0] == 'c') {
|
||||
gram_yalign = 0;
|
||||
gram_ypos = 0;
|
||||
} else
|
||||
gram_ypos = atoi(ypos);
|
||||
if (yfrom == '-')
|
||||
gram_yalign *= -1;
|
||||
|
||||
bgstr = var_get_variable("X_background");
|
||||
if (bgstr[0] == '\0')
|
||||
bgstr = xres_get_bgcolor(style);
|
||||
if (bgstr == NULL)
|
||||
bgstr = var_get_variable("default_X_background");
|
||||
if (bgstr[0]=='\0')
|
||||
gram->bgcolor = default_bgcolor;
|
||||
|
||||
if (bgstr && bgstr[0])
|
||||
gram->bgcolor = x_string_to_color(bgstr, default_bgcolor);
|
||||
|
||||
gram_xsize = maxwidth + (internal_border_width << 1);
|
||||
gram_ysize = yofs + internal_border_width;
|
||||
gram->numblocks = num;
|
||||
gram->blocks = blocks;
|
||||
#ifdef CMU_ZWGCPLUS
|
||||
gram->notice = get_stored_notice();
|
||||
#endif
|
||||
|
||||
x_gram_create(dpy, gram, gram_xalign, gram_yalign, gram_xpos,
|
||||
gram_ypos, gram_xsize, gram_ysize, beepcount);
|
||||
}
|
||||
|
||||
/* Silly almost-but-not-quite-useless helper function */
|
||||
static char *
|
||||
no_dots_downcase_var(char *str)
|
||||
{
|
||||
register char *var, *var2;
|
||||
|
||||
var = string_Downcase(var_get_variable(str));
|
||||
var2 = var;
|
||||
while (*var++)
|
||||
if (*var == '.')
|
||||
*var = '_';
|
||||
return(var2);
|
||||
}
|
||||
|
||||
inline static XFontSet
|
||||
mode_to_font(Display *dpy, char *style, xmode *mode) {
|
||||
return get_font(dpy,
|
||||
style,
|
||||
mode->font ? mode->font : mode->substyle,
|
||||
mode->size,
|
||||
mode->bold + mode->italic * 2);
|
||||
}
|
||||
|
||||
inline static int
|
||||
envmatch(desctype *desc, char *str) {
|
||||
int len = strlen(str);
|
||||
return desc->len == len && strncasecmp(desc->str, str, len) == 0;
|
||||
}
|
||||
|
||||
void
|
||||
xshow(Display *dpy, desctype *desc, int numstr, int numnl)
|
||||
{
|
||||
XFontSet font;
|
||||
XFontSetExtents *fse;
|
||||
xmode_stack modes = xmode_stack_create();
|
||||
xmode curmode;
|
||||
xlinedesc *lines;
|
||||
xblock *blocks;
|
||||
xauxblock *auxblocks;
|
||||
int nextblock=0;
|
||||
int line=0,linestart=0;
|
||||
char *style;
|
||||
int free_style = 0;
|
||||
int beepcount = 0;
|
||||
char *notice_charset = var_get_variable("notice_charset");
|
||||
int i;
|
||||
|
||||
lines = (xlinedesc *)malloc(sizeof(xlinedesc) * (numnl + 1));
|
||||
|
||||
blocks = (xblock *)malloc(sizeof(xblock) * numstr);
|
||||
auxblocks = (xauxblock *)malloc(sizeof(xauxblock) * numstr);
|
||||
|
||||
memset(&curmode, 0, sizeof(curmode));
|
||||
curmode.bold = 0;
|
||||
curmode.italic = 0;
|
||||
curmode.size = MEDIUM_SIZE;
|
||||
curmode.align = LEFTALIGN;
|
||||
curmode.expcolor = 0;
|
||||
curmode.substyle = string_Copy("default");
|
||||
curmode.font = NULL;
|
||||
|
||||
style = var_get_variable("style");
|
||||
if (style[0] == '\0') {
|
||||
style = string_Concat(no_dots_downcase_var("class"), ".");
|
||||
style = string_Concat2(style, no_dots_downcase_var("instance"));
|
||||
style = string_Concat2(style, ".");
|
||||
style = string_Concat2(style, no_dots_downcase_var("sender"));
|
||||
string_Downcase(style);
|
||||
free_style = 1;
|
||||
}
|
||||
|
||||
for (; desc->code != DT_EOF; desc = desc->next) {
|
||||
switch (desc->code) {
|
||||
case DT_ENV:
|
||||
xmode_stack_push(modes, curmode);
|
||||
curmode.substyle = string_Copy(curmode.substyle);
|
||||
if (curmode.font)
|
||||
curmode.font = string_Copy(curmode.font);
|
||||
if (envmatch(desc, "roman")) {
|
||||
curmode.bold = 0;
|
||||
curmode.italic = 0;
|
||||
} else if (envmatch(desc, "bold") || envmatch(desc, "b"))
|
||||
curmode.bold = 1;
|
||||
else if (envmatch(desc, "italic") || envmatch(desc, "i"))
|
||||
curmode.italic = 1;
|
||||
else if (envmatch(desc, "large"))
|
||||
curmode.size = LARGE_SIZE;
|
||||
else if (envmatch(desc, "medium"))
|
||||
curmode.size = MEDIUM_SIZE;
|
||||
else if (envmatch(desc, "small"))
|
||||
curmode.size = SMALL_SIZE;
|
||||
else if (envmatch(desc, "left") || envmatch(desc, "l"))
|
||||
curmode.align = LEFTALIGN;
|
||||
else if (envmatch(desc, "center") || envmatch(desc, "c"))
|
||||
curmode.align = CENTERALIGN;
|
||||
else if (envmatch(desc, "right") || envmatch(desc, "r"))
|
||||
curmode.align = RIGHTALIGN;
|
||||
else if (envmatch(desc, "beep"))
|
||||
beepcount++;
|
||||
else if (envmatch(desc, "font")) {
|
||||
/* lookahead needed. desc->next->str should be the
|
||||
font name, and desc->next->next->code should be
|
||||
a DT_END*/
|
||||
if ((desc->next) &&
|
||||
(desc->next->next) &&
|
||||
(desc->next->code == DT_STR) &&
|
||||
(desc->next->next->code == DT_END)) {
|
||||
|
||||
/* Since @font mutates the current environment, we have
|
||||
to pop the environment that this case usually pushes */
|
||||
free(curmode.substyle);
|
||||
curmode = xmode_stack_top(modes);
|
||||
xmode_stack_pop(modes);
|
||||
|
||||
/* mutating... */
|
||||
curmode.size = SPECIAL_SIZE; /* This is an @font() */
|
||||
curmode.font = string_CreateFromData(desc->next->str,
|
||||
desc->next->len);
|
||||
/* skip over the rest of the @font */
|
||||
desc = desc->next->next;
|
||||
}
|
||||
} else if (envmatch(desc, "color")) {
|
||||
/* lookahead needed. desc->next->str should be the
|
||||
font name, and desc->next->next->code should be
|
||||
a DT_END*/
|
||||
if ((desc->next) &&
|
||||
(desc->next->next) &&
|
||||
(desc->next->code == DT_STR) &&
|
||||
(desc->next->next->code == DT_END)) {
|
||||
char *colorname;
|
||||
|
||||
/* Since @font mutates the current environment, we have
|
||||
to pop the environment that this case usually pushes */
|
||||
free(curmode.substyle);
|
||||
curmode = xmode_stack_top(modes);
|
||||
xmode_stack_pop(modes);
|
||||
|
||||
/* mutating... */
|
||||
colorname = string_CreateFromData(desc->next->str,
|
||||
desc->next->len);
|
||||
curmode.color = x_string_to_color(colorname, default_fgcolor);
|
||||
free(colorname);
|
||||
curmode.expcolor = 1;
|
||||
/* skip over the rest of the @font */
|
||||
desc = desc->next->next;
|
||||
}
|
||||
} else if (desc->len > 0) { /* avoid @{...} */
|
||||
free(curmode.substyle);
|
||||
if (curmode.font) {
|
||||
free(curmode.font);
|
||||
curmode.font = NULL;
|
||||
}
|
||||
curmode.substyle = string_CreateFromData(desc->str, desc->len);
|
||||
}
|
||||
break;
|
||||
|
||||
case DT_STR:
|
||||
auxblocks[nextblock].align = curmode.align;
|
||||
auxblocks[nextblock].font = mode_to_font(dpy, style, &curmode);
|
||||
auxblocks[nextblock].str = desc->str;
|
||||
auxblocks[nextblock].len = desc->len;
|
||||
i = ZTransliterate(desc->str, desc->len,
|
||||
strcmp(notice_charset, "UNKNOWN") ?
|
||||
notice_charset : "ISO-8859-1",
|
||||
#ifdef X_HAVE_UTF8_STRING
|
||||
"UTF-8",
|
||||
#else
|
||||
"UTF-16BE",
|
||||
#endif
|
||||
&blocks[nextblock].wstr,
|
||||
&blocks[nextblock].wlen);
|
||||
if (i) {
|
||||
var_set_variable("error", strerror(i));
|
||||
#ifdef X_HAVE_UTF8_STRING
|
||||
blocks[nextblock].wlen = desc->len;
|
||||
blocks[nextblock].wstr = strdup(desc->str);
|
||||
#else
|
||||
blocks[nextblock].wlen = desc->len * 2;
|
||||
blocks[nextblock].wstr = malloc(blocks[nextblock].wlen);
|
||||
for (i = 0; i < desc->len; i++)
|
||||
*(short *)&(blocks[nextblock].wstr[i * 2]) = htons((short)(unsigned char)desc->str[i]);
|
||||
/* XXX */
|
||||
#endif
|
||||
}
|
||||
#ifndef X_HAVE_UTF8_STRING
|
||||
blocks[nextblock].wlen /= 2;
|
||||
#endif
|
||||
if (curmode.expcolor)
|
||||
blocks[nextblock].fgcolor = curmode.color;
|
||||
else
|
||||
blocks[nextblock].fgcolor =
|
||||
x_string_to_color(mode_to_colorname(dpy, style, &curmode),
|
||||
default_fgcolor);
|
||||
nextblock++;
|
||||
break;
|
||||
|
||||
case DT_END:
|
||||
free(curmode.substyle);
|
||||
curmode = xmode_stack_top(modes);
|
||||
xmode_stack_pop(modes);
|
||||
break;
|
||||
|
||||
case DT_NL:
|
||||
lines[line].startblock = linestart;
|
||||
lines[line].numblock = nextblock-linestart;
|
||||
font = mode_to_font(dpy, style, &curmode);
|
||||
fse = XExtentsOfFontSet(font);
|
||||
lines[line].ascent = -fse->max_logical_extent.y;
|
||||
lines[line].descent = fse->max_logical_extent.y +
|
||||
fse->max_logical_extent.height;
|
||||
line++;
|
||||
linestart = nextblock;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* case DT_EOF: will drop through to here. */
|
||||
|
||||
if (linestart != nextblock) {
|
||||
lines[line].startblock = linestart;
|
||||
lines[line].numblock = nextblock-linestart;
|
||||
font = mode_to_font(dpy, style, &curmode);
|
||||
lines[line].ascent = 0;
|
||||
lines[line].descent = 0;
|
||||
line++;
|
||||
}
|
||||
|
||||
free(curmode.substyle);
|
||||
fixup_and_draw(dpy, style, auxblocks, blocks, nextblock, lines, line,
|
||||
beepcount);
|
||||
free(lines);
|
||||
free(auxblocks);
|
||||
if (free_style)
|
||||
free(style);
|
||||
}
|
||||
|
||||
static void
|
||||
xhandleevent(Display *dpy,
|
||||
Window w,
|
||||
XEvent *event)
|
||||
{
|
||||
XPointer gramp; /* Avoid strict aliasing violation */
|
||||
x_gram *gram;
|
||||
|
||||
if (XFindContext(dpy, w, desc_context, &gramp))
|
||||
return;
|
||||
gram = (x_gram *)gramp;
|
||||
|
||||
if (event->type == Expose)
|
||||
x_gram_expose(dpy, w, gram,&(event->xexpose));
|
||||
else
|
||||
xcut(dpy, event, desc_context);
|
||||
|
||||
XFlush(dpy);
|
||||
}
|
||||
|
||||
void
|
||||
x_get_input(Display *dpy)
|
||||
{
|
||||
XEvent event;
|
||||
|
||||
dprintf1("Entering x_get_input(%lx).\n",(unsigned long)dpy);
|
||||
|
||||
/*
|
||||
* Kludge to get around lossage in XPending:
|
||||
*
|
||||
* (the problem: XPending on a partial packet returns 0 without
|
||||
* reading in the packet. This causes a problem when the X server
|
||||
* dies in the middle of sending a packet.)
|
||||
*/
|
||||
if (XPending(dpy)==0)
|
||||
XNoOp(dpy); /* Ensure server is still with us... */
|
||||
|
||||
while (XPending(dpy)) {
|
||||
XNextEvent(dpy,&event);
|
||||
xhandleevent(dpy, event.xany.window, &event);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CMU_ZWGCPLUS
|
||||
void
|
||||
plus_window_deletions(ZNotice_t *notice)
|
||||
{
|
||||
x_gram *tmp, *fry;
|
||||
char *val;
|
||||
int done;
|
||||
static char class_nm[NAMESIZE], instance_nm[NAMESIZE], recip_nm[NAMESIZE];
|
||||
|
||||
if (!x_dpy)
|
||||
return;
|
||||
|
||||
val = var_get_variable("delete_window");
|
||||
|
||||
#ifdef DEBUG_DELETION
|
||||
fprintf(stderr, "delete_window(%s)\n", val);
|
||||
#endif
|
||||
if (val) {
|
||||
if (!strcmp(val, "this")) {
|
||||
do {
|
||||
done = 1;
|
||||
tmp = bottom_gram;
|
||||
while (tmp) {
|
||||
if (tmp->notice == notice) {
|
||||
fry = tmp;
|
||||
tmp = tmp->above;
|
||||
xdestroygram(x_dpy, fry->w, desc_context, fry);
|
||||
done = 0;
|
||||
} else {
|
||||
tmp = tmp->above;
|
||||
}
|
||||
}
|
||||
} while (!done);
|
||||
}
|
||||
else if (!strcmp(val, "s")) {
|
||||
/* I cheated. This is really sender, not class */
|
||||
strcpy(class_nm, notice->z_sender);
|
||||
do {
|
||||
done = 1;
|
||||
tmp = bottom_gram;
|
||||
while (tmp) {
|
||||
if (!strcasecmp(((ZNotice_t *)(tmp->notice))->z_sender, class_nm)) {
|
||||
fry = tmp;
|
||||
tmp = tmp->above;
|
||||
xdestroygram(x_dpy, fry->w, desc_context, fry);
|
||||
done = 0;
|
||||
} else {
|
||||
tmp = tmp->above;
|
||||
}
|
||||
}
|
||||
} while (!done);
|
||||
}
|
||||
else if (!strcmp(val, "ns")) {
|
||||
/* I cheated. This is really sender, not class */
|
||||
strcpy(class_nm, notice->z_sender);
|
||||
do {
|
||||
done = 1;
|
||||
tmp = bottom_gram;
|
||||
while (tmp) {
|
||||
if (!!strcasecmp(((ZNotice_t *)(tmp->notice))->z_sender, class_nm)) {
|
||||
fry = tmp;
|
||||
tmp = tmp->above;
|
||||
xdestroygram(x_dpy, fry->w, desc_context, fry);
|
||||
done = 0;
|
||||
} else {
|
||||
tmp = tmp->above;
|
||||
}
|
||||
}
|
||||
} while (!done);
|
||||
}
|
||||
else if (!strcmp(val, "r")) {
|
||||
strcpy(recip_nm, notice->z_recipient);
|
||||
do {
|
||||
done = 1;
|
||||
tmp = bottom_gram;
|
||||
while (tmp) {
|
||||
if (!strcasecmp(((ZNotice_t *)(tmp->notice))->z_recipient, recip_nm)) {
|
||||
fry = tmp;
|
||||
tmp = tmp->above;
|
||||
xdestroygram(x_dpy, fry->w, desc_context, fry);
|
||||
done = 0;
|
||||
} else {
|
||||
tmp = tmp->above;
|
||||
}
|
||||
}
|
||||
} while (!done);
|
||||
}
|
||||
else if (!strcmp(val, "nr")) {
|
||||
strcpy(recip_nm, notice->z_recipient);
|
||||
do {
|
||||
done = 1;
|
||||
tmp = bottom_gram;
|
||||
while (tmp) {
|
||||
if (!!strcasecmp(((ZNotice_t *)(tmp->notice))->z_recipient, recip_nm)) {
|
||||
fry = tmp;
|
||||
tmp = tmp->above;
|
||||
xdestroygram(x_dpy, fry->w, desc_context, fry);
|
||||
done = 0;
|
||||
} else {
|
||||
tmp = tmp->above;
|
||||
}
|
||||
}
|
||||
} while (!done);
|
||||
}
|
||||
else if (!strcmp(val, "cir")) {
|
||||
strcpy(class_nm, notice->z_class);
|
||||
strcpy(instance_nm, notice->z_class_inst);
|
||||
strcpy(recip_nm, notice->z_recipient);
|
||||
do {
|
||||
done = 1;
|
||||
tmp = bottom_gram;
|
||||
while (tmp) {
|
||||
if (!strcasecmp(((ZNotice_t *)(tmp->notice))->z_class_inst, instance_nm)
|
||||
&& !strcasecmp(((ZNotice_t *)(tmp->notice))->z_class, class_nm)
|
||||
&& !strcasecmp(((ZNotice_t *)(tmp->notice))->z_recipient, recip_nm))
|
||||
{
|
||||
fry = tmp;
|
||||
tmp = tmp->above;
|
||||
xdestroygram(x_dpy, fry->w, desc_context, fry);
|
||||
done = 0;
|
||||
} else {
|
||||
tmp = tmp->above;
|
||||
}
|
||||
}
|
||||
} while (!done);
|
||||
}
|
||||
else if (!strcmp(val, "ci")) {
|
||||
strcpy(class_nm, notice->z_class);
|
||||
strcpy(instance_nm, notice->z_class_inst);
|
||||
do {
|
||||
done = 1;
|
||||
tmp = bottom_gram;
|
||||
while (tmp) {
|
||||
if (!strcasecmp(((ZNotice_t *)(tmp->notice))->z_class_inst, instance_nm)
|
||||
&& !strcasecmp(((ZNotice_t *)(tmp->notice))->z_class, class_nm))
|
||||
{
|
||||
fry = tmp;
|
||||
tmp = tmp->above;
|
||||
xdestroygram(x_dpy, fry->w, desc_context, fry);
|
||||
done = 0;
|
||||
} else {
|
||||
tmp = tmp->above;
|
||||
}
|
||||
}
|
||||
} while (!done);
|
||||
}
|
||||
else if (!strcmp(val, "cr")) {
|
||||
strcpy(class_nm, notice->z_class);
|
||||
strcpy(recip_nm, notice->z_recipient);
|
||||
do {
|
||||
done = 1;
|
||||
tmp = bottom_gram;
|
||||
while (tmp) {
|
||||
if (!strcasecmp(((ZNotice_t *)(tmp->notice))->z_class, class_nm) &&
|
||||
!strcasecmp(((ZNotice_t *)(tmp->notice))->z_recipient, recip_nm))
|
||||
{
|
||||
fry = tmp;
|
||||
tmp = tmp->above;
|
||||
xdestroygram(x_dpy, fry->w, desc_context, fry);
|
||||
done = 0;
|
||||
} else {
|
||||
tmp = tmp->above;
|
||||
}
|
||||
}
|
||||
} while (!done);
|
||||
}
|
||||
else if (!strcmp(val, "c")) {
|
||||
strcpy(class_nm, notice->z_class);
|
||||
do {
|
||||
done = 1;
|
||||
tmp = bottom_gram;
|
||||
while (tmp) {
|
||||
if (!strcasecmp(((ZNotice_t *)(tmp->notice))->z_class, class_nm)) {
|
||||
fry = tmp;
|
||||
tmp = tmp->above;
|
||||
xdestroygram(x_dpy, fry->w, desc_context, fry);
|
||||
done = 0;
|
||||
} else {
|
||||
tmp = tmp->above;
|
||||
}
|
||||
}
|
||||
} while (!done);
|
||||
}
|
||||
else if (!strcmp(val, "all")) {
|
||||
while (bottom_gram) {
|
||||
xdestroygram(x_dpy, bottom_gram->w, desc_context, bottom_gram);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif /* X_DISPLAY_MISSING */
|
267
zwgc/zephyr.c
Normal file
267
zwgc/zephyr.c
Normal file
@ -0,0 +1,267 @@
|
||||
/* 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_zephyr_c[] = "$Id$";
|
||||
#endif
|
||||
|
||||
#include <zephyr/mit-copyright.h>
|
||||
|
||||
/****************************************************************************/
|
||||
/* */
|
||||
/* Module containing code dealing with zephyr: */
|
||||
/* */
|
||||
/****************************************************************************/
|
||||
|
||||
#include <zephyr/zephyr.h>
|
||||
#include <sys/socket.h>
|
||||
#include "new_string.h"
|
||||
#include "zephyr.h"
|
||||
#include "error.h"
|
||||
#include "mux.h"
|
||||
#include "subscriptions.h"
|
||||
#include "variables.h"
|
||||
#include "pointer.h"
|
||||
#include "main.h"
|
||||
#ifndef X_DISPLAY_MISSING
|
||||
#include "X_driver.h"
|
||||
#endif
|
||||
#ifdef CMU_ZWGCPLUS
|
||||
#include "plus.h"
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
extern int zwgc_debug;
|
||||
#endif /* DEBUG */
|
||||
|
||||
static int zephyr_inited = 0;
|
||||
static unsigned short zephyr_port = 0;
|
||||
|
||||
/*
|
||||
* Internal Routine:
|
||||
*
|
||||
* string get_zwgc_port_number_filename()
|
||||
* Effects: Returns the filename that the zwgc port # is/should be
|
||||
* stored in, based on the user's uid & the environment
|
||||
* variable WGFILE. The returned string points into a
|
||||
* static buffer that may change on further calls to this
|
||||
* routine or getenv. The returned string should not be
|
||||
* modified in any way.
|
||||
*/
|
||||
|
||||
static string
|
||||
get_zwgc_port_number_filename(void)
|
||||
{
|
||||
static char buffer[40];
|
||||
char *temp;
|
||||
|
||||
temp = getenv("WGFILE");
|
||||
if (temp)
|
||||
return(temp);
|
||||
else {
|
||||
sprintf(buffer, "/tmp/wg.%d", getuid());
|
||||
return(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Write out the port number to the wg file.
|
||||
*/
|
||||
|
||||
void
|
||||
write_wgfile(void)
|
||||
{
|
||||
char *name = get_zwgc_port_number_filename();
|
||||
FILE *port_file;
|
||||
|
||||
port_file = fopen(name, "w");
|
||||
if (port_file) {
|
||||
fprintf(port_file, "%d\n", zephyr_port);
|
||||
fclose(port_file);
|
||||
} else {
|
||||
fprintf(stderr, "zwgc: error while opening %s for writing: ", name);
|
||||
perror("");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
struct notice_handler_ptr {
|
||||
void (*notice_handler)(ZNotice_t *);
|
||||
};
|
||||
|
||||
static void
|
||||
handle_zephyr_input(struct notice_handler_ptr *nhp)
|
||||
{
|
||||
ZNotice_t *notice;
|
||||
struct sockaddr_in from;
|
||||
int complete_packets_ready;
|
||||
|
||||
for (;;) {
|
||||
errno = 0;
|
||||
if ( (complete_packets_ready=ZPending()) < 0 )
|
||||
FATAL_TRAP( errno, "while calling ZPending()" );
|
||||
|
||||
if (complete_packets_ready==0)
|
||||
return;
|
||||
|
||||
notice = malloc(sizeof(ZNotice_t));
|
||||
|
||||
TRAP( ZReceiveNotice(notice, &from), "while getting zephyr notice" );
|
||||
if (!error_code) {
|
||||
notice->z_auth = ZCheckAuthentication(notice, &from);
|
||||
nhp->notice_handler(notice);
|
||||
}
|
||||
#ifdef CMU_ZWGCPLUS
|
||||
if (get_list_refcount(notice) <= 0) {
|
||||
/* no windows created */
|
||||
if (!get_notice_fake(notice))
|
||||
list_del_notice(notice);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
void zephyr_init(void (*notice_handler)(ZNotice_t *))
|
||||
{
|
||||
struct notice_handler_ptr *nhp;
|
||||
char *temp;
|
||||
char *exposure;
|
||||
char *tty = NULL;
|
||||
FILE *port_file;
|
||||
|
||||
/*
|
||||
* Initialize zephyr. If error, print error message & exit.
|
||||
*/
|
||||
nhp = malloc(sizeof(struct notice_handler_ptr));
|
||||
if (!nhp) {
|
||||
fprintf(stderr, "Out of memory setting up zephyr notice handler.\n");
|
||||
exit(3);
|
||||
}
|
||||
FATAL_TRAP( ZInitialize(), "while initializing Zephyr" );
|
||||
FATAL_TRAP( ZOpenPort(&zephyr_port), "while opening Zephyr port" );
|
||||
|
||||
/*
|
||||
* Save away our port number in a special place so that zctl and
|
||||
* other clients can send us control messages: <<<>>>
|
||||
*/
|
||||
temp = get_zwgc_port_number_filename();
|
||||
port_file = fopen(temp, "r");
|
||||
if (port_file) {
|
||||
fprintf(stderr, "zwgc: windowgram file already exists. If you are\n");
|
||||
fprintf(stderr, "zwgc: not already running zwgc, delete %s\n", temp);
|
||||
fprintf(stderr, "zwgc: and try again.\n");
|
||||
exit(1);
|
||||
}
|
||||
write_wgfile();
|
||||
|
||||
/* Set hostname and tty for locations. If we support X, use the
|
||||
* display string for the default tty name. */
|
||||
if (location_override)
|
||||
tty = location_override;
|
||||
#ifndef X_DISPLAY_MISSING
|
||||
else if (x_dpy)
|
||||
tty = DisplayString(x_dpy);
|
||||
#endif
|
||||
error_code = ZInitLocationInfo(NULL, tty);
|
||||
TRAP( error_code, "while initializing location information" );
|
||||
|
||||
/*
|
||||
* Retrieve the user's desired exposure level (from the zephyr variable
|
||||
* "exposure"), convert it to the proper internal form then
|
||||
* set the user's location using it. If the exposure level is
|
||||
* not one of the allowed ones, print an error and treat it as
|
||||
* EXPOSE_NONE.
|
||||
*/
|
||||
temp = ZGetVariable("exposure");
|
||||
if (temp) {
|
||||
if (!(exposure = ZParseExposureLevel(temp))) {
|
||||
ERROR2("invalid exposure level %s, using exposure level none instead.\n", temp);
|
||||
exposure = EXPOSE_NONE;
|
||||
}
|
||||
} else
|
||||
exposure = EXPOSE_OPSTAFF;
|
||||
error_code = ZSetLocation(exposure); /* <<<>>> */
|
||||
if (error_code != ZERR_LOGINFAIL)
|
||||
TRAP( error_code, "while setting location" );
|
||||
|
||||
/*
|
||||
* If the exposure level isn't EXPOSE_NONE, turn on recieving notices.
|
||||
* (this involves reading in the subscription file, etc.)
|
||||
*/
|
||||
if (string_Neq(exposure, EXPOSE_NONE))
|
||||
zwgc_startup();
|
||||
|
||||
/*
|
||||
* Set $realm to our realm and $user to our zephyr username:
|
||||
*/
|
||||
var_set_variable("realm", (char *)ZGetRealm()); /* XXX should propagate the
|
||||
* const */
|
||||
var_set_variable("user", ZGetSender());
|
||||
|
||||
/*
|
||||
* <<<>>>
|
||||
*/
|
||||
nhp->notice_handler = notice_handler;
|
||||
mux_add_input_source(ZGetFD(), (void (*)(void *))handle_zephyr_input, nhp);
|
||||
zephyr_inited = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
void finalize_zephyr(void) /* <<<>>> */
|
||||
{
|
||||
string temp;
|
||||
|
||||
if (zephyr_inited) {
|
||||
/*
|
||||
* Remove the file containing our port # since it is no longer needed:
|
||||
*/
|
||||
errno = 0;
|
||||
temp = get_zwgc_port_number_filename();
|
||||
unlink(temp);
|
||||
if (errno) {
|
||||
fprintf(stderr, "zwgc: error while trying to delete %s: ", temp);
|
||||
perror("");
|
||||
}
|
||||
|
||||
/*
|
||||
* Cancel our subscriptions, unset our location, and close our zephyr
|
||||
* connection:
|
||||
*/
|
||||
#ifdef DEBUG
|
||||
if (zwgc_debug) {
|
||||
TRAP( ZUnsetLocation(), "while unsetting location" );
|
||||
TRAP( ZCancelSubscriptions(0), "while canceling subscriptions" );
|
||||
} else {
|
||||
#endif /* DEBUG */
|
||||
(void) ZUnsetLocation();
|
||||
(void) ZCancelSubscriptions(0);
|
||||
#ifdef DEBUG
|
||||
}
|
||||
#endif /* DEBUG */
|
||||
ZClosePort();
|
||||
}
|
||||
return;
|
||||
}
|
26
zwgc/zephyr.h
Normal file
26
zwgc/zephyr.h
Normal file
@ -0,0 +1,26 @@
|
||||
/* 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 <zephyr/mit-copyright.h>
|
||||
|
||||
#ifndef zephyr_MODULE
|
||||
#define zephyr_MODULE
|
||||
|
||||
#include <zephyr/zephyr.h>
|
||||
|
||||
extern void zephyr_init(void(*)(ZNotice_t *));
|
||||
extern void finalize_zephyr(void);
|
||||
extern void write_wgfile(void);
|
||||
|
||||
#endif
|
1067
zwgc/zwgc.1.in
Normal file
1067
zwgc/zwgc.1.in
Normal file
File diff suppressed because it is too large
Load Diff
136
zwgc/zwgc.desc
Normal file
136
zwgc/zwgc.desc
Normal file
@ -0,0 +1,136 @@
|
||||
# Copyright 1989, 1990 Massachusetts Institute of Technology
|
||||
#
|
||||
# For copying and distribution information, see the file
|
||||
# "mit-copyright.h".
|
||||
#
|
||||
# $Id$
|
||||
#
|
||||
#
|
||||
# Default WindowGram description file
|
||||
#
|
||||
|
||||
# Opcode "ping" is used by sender programs to see if the message would
|
||||
# really get sent, or if the recipient has logged out. No useful
|
||||
# information is normally contained in these messages, so we discard them.
|
||||
if (upcase($opcode) == "PING") then exit endif
|
||||
|
||||
#
|
||||
# AUTHENTICATION information
|
||||
#
|
||||
# $auth can be either Yes, No, or Forged
|
||||
#
|
||||
# "Yes" means that the sender field present in the notice was verified by
|
||||
# Kerberos authentication
|
||||
#
|
||||
# "No" means that either the sender did not include any authentication
|
||||
# information, or the authentication information was not verified by the
|
||||
# Zephyr Server before the notice was sent to you.
|
||||
#
|
||||
# "Forged" means that the Server claims that the sender of the notice
|
||||
# was verified by Kerberos authentication, but your WindowGram client
|
||||
# could not verify this. This stage of verification is done by a cryptographic
|
||||
# checksum. The most probable cause of the failure of the checksum
|
||||
# provided by the Server to match the checksum generated by your
|
||||
# WindowGram client is that you changed Kerberos tickets, and the Server
|
||||
# was using an old value to compute the cryptographic checksum. You can
|
||||
# update the Server's value by typing 'zctl load' to your prompt.
|
||||
#
|
||||
# By default, notices which appear forged are labeled as 'UNAUTHENTIC'
|
||||
# to avoid confusion as to what 'Forged' really means.
|
||||
# To change this display, change the last word in the line following
|
||||
# 'match "forged" to something other than "UNAUTHENTIC".
|
||||
case $auth
|
||||
match "yes"
|
||||
set aval = "Authentic"
|
||||
match "no","forged"
|
||||
set aval = "@b(@large(UNAUTHENTIC))"
|
||||
endcase
|
||||
|
||||
case $class
|
||||
match "WG_CTL_CLASS"
|
||||
exit
|
||||
match "message"
|
||||
if (downcase($recipient) == downcase($user)) then
|
||||
case $instance
|
||||
match "PERSONAL"
|
||||
set type = "Personal"
|
||||
match "URGENT"
|
||||
set type = "Urgent"
|
||||
default
|
||||
set type = $instance
|
||||
endcase
|
||||
else
|
||||
set type = "Instance "+$instance
|
||||
endif
|
||||
|
||||
if ($number_of_fields == "1") then
|
||||
fields body
|
||||
set signature = ""
|
||||
else
|
||||
fields signature body
|
||||
endif
|
||||
if ($signature =~ "^[Ff]rom: .*") then
|
||||
set dummy = lany($signature,"From: ")
|
||||
endif
|
||||
if ($signature =~ "\n$") then
|
||||
set dummy = rany($signature,"\n")
|
||||
endif
|
||||
if ($signature == "") then
|
||||
set ftext = "From: @bold("+protect($sender)+")"
|
||||
else
|
||||
set ftext = "From: @bold(@{"+protect($signature)+"} <"+
|
||||
protect($sender)+">)"
|
||||
endif
|
||||
|
||||
print "@center(@bold("+$aval+") "+$type+" message at "+$time+
|
||||
" on "+$date+"\n"+$ftext+" on "+$fromhost+"\nTo: "+
|
||||
$recipient+")\n\n"
|
||||
print $body
|
||||
put
|
||||
exit
|
||||
|
||||
match "login"
|
||||
case $opcode
|
||||
match "USER_LOGIN"
|
||||
set log = "logged in"
|
||||
match "USER_LOGOUT"
|
||||
set log = "logged out"
|
||||
default
|
||||
set log = "unknown opcode"
|
||||
endcase
|
||||
|
||||
fields host when tty
|
||||
print "@center(@bold("+$sender+") "+$log+")\n"
|
||||
print "@center(on @bold("+$host+") on "+$tty+")\n"
|
||||
print "@center(at "+$when+")"
|
||||
put
|
||||
exit
|
||||
|
||||
default
|
||||
if (downcase($class) == "filsys" and downcase($opcode) == "shutdown") then
|
||||
set format = "From $sender:\n@bold(Shutdown message from $1 at $time)\n"+
|
||||
"@center(System going down, message is:)\n\n$2\n\n@center(@bold($3))"
|
||||
elseif (downcase($class) == "filsys") then
|
||||
set format = "@bold(Filesystem Operation Message for $instance:)\n"+
|
||||
"From: @bold($sender) at $time $date\n$message"
|
||||
elseif (downcase($class) == "mail" and downcase($instance) == "popret") then
|
||||
set format = "You have new mail:\n\nFrom: $1\nTo: $2\nSubject: $3"
|
||||
elseif (downcase($class) == "mail") then
|
||||
set format = "From Post Office $1:\n$2"
|
||||
elseif (downcase($class) == "syslog") then
|
||||
set format = "From $sender:\nSyslog message from $instance, level "+
|
||||
"$opcode:\n$message"
|
||||
elseif ($number_of_fields == "1") then
|
||||
set format = "Class $class, Instance $instance:\nTo: @bold($recipient) "+
|
||||
"at $time $date\nFrom: @bold($sender)\n\n$message"
|
||||
else
|
||||
set format = "Class $class, Instance $instance:\nTo: @bold($recipient) "+
|
||||
"at $time $date\nFrom: @bold($1) <$sender>\n\n$2"
|
||||
endif
|
||||
|
||||
print "(Authentication: @bold("+$aval+") from host: "+$fromhost+")\n"
|
||||
print substitute($format)
|
||||
put
|
||||
exit
|
||||
|
||||
endcase
|
184
zwgc/zwgc.el
Normal file
184
zwgc/zwgc.el
Normal file
@ -0,0 +1,184 @@
|
||||
; zwgc.el
|
||||
;
|
||||
; This file is part of the Project Athena Zephyr Notification System.
|
||||
; Created by: Mark W. Eichin <eichin@athena.mit.edu>
|
||||
; $Id$
|
||||
; Copyright (c) 1988 by the Massachusetts Institute of Technology.
|
||||
; For copying and distribution information, see the file
|
||||
; "mit-copyright.h".
|
||||
;
|
||||
; Emacs mode for running zwgc in a sub process of emacs. It pops up a
|
||||
; window for every new message; if you make bells appear between each
|
||||
; message, it will be able to seperate them. If you move the mouse
|
||||
; into the message window and hit `delete' it will delete the current
|
||||
; message; if there are other messages, it will show them, if not, it
|
||||
; will make the window go away.
|
||||
;
|
||||
; Invoke with M-x zwgc.
|
||||
;
|
||||
; Also included is M-x zsend, which prompts for a user name and a
|
||||
; message to send to them. If the message is blank, a buffer is popped
|
||||
; up to edit the message in. If a prefix argument is given, zsend
|
||||
; prompts for an instance instead. If the user name is blank, the last
|
||||
; one is reused.
|
||||
;
|
||||
; The following should be added to your .zephyr.desc file if you want
|
||||
; to take advantage of the zwgc message seperation features:
|
||||
; does $mode
|
||||
; match tty
|
||||
; beep
|
||||
; endmatch
|
||||
; enddoes
|
||||
;
|
||||
(defvar zwgc_el-RCS-id)
|
||||
(setq zwgc_el-RCS-id "$Id$")
|
||||
;
|
||||
;
|
||||
|
||||
(defun narrow-to-string (str)
|
||||
"narrow and put up a string..."
|
||||
(interactive "sString: ")
|
||||
(narrow-to-region (point) (point))
|
||||
(insert str))
|
||||
|
||||
(defvar zwgc-prog "/usr/etc/zwgc"
|
||||
"*Program to run as the zwgc process. Should set it for the machine type.")
|
||||
|
||||
(defun zwgc-wakeup (proc string)
|
||||
"Procedure called when zwgc spits something out"
|
||||
(let (start-limit)
|
||||
(save-excursion (set-buffer (get-buffer "*zwgc*"))
|
||||
(setq start-limit (point))
|
||||
(goto-char (point-max))
|
||||
(if (= 7 (string-to-char string))
|
||||
(progn
|
||||
(ding 1)
|
||||
(message "got one!")
|
||||
(narrow-to-string string))
|
||||
(insert string))
|
||||
(search-backward "\007" start-limit t)
|
||||
(while (search-forward "\015" (point-max) t) ;flush ^M's
|
||||
(delete-backward-char 1)))
|
||||
(Special-pop-up-window (get-buffer "*zwgc*"))
|
||||
))
|
||||
|
||||
(defun zwgc ()
|
||||
"emacs mode for running zwgc in a sub process of emacs. It pops up a
|
||||
window for every new message; if you make bells appear between each
|
||||
message, it will be able to seperate them. If you move the mouse into
|
||||
the message window and hit `delete' it will delete the current
|
||||
message; if there are other messages, it will show them, if not, it
|
||||
will make the window go away."
|
||||
(interactive)
|
||||
(require 'shell)
|
||||
(let ((buffer (get-buffer-create "*zwgc*")) proc status)
|
||||
(setq proc (get-buffer-process buffer))
|
||||
(if proc
|
||||
(setq status (process-status proc)))
|
||||
(save-excursion
|
||||
(set-buffer buffer)
|
||||
(if (memq status '(run stop))
|
||||
nil
|
||||
(if proc (delete-process proc))
|
||||
(setq proc (start-process "Zwgc" buffer
|
||||
zwgc-prog "-disable" "X"
|
||||
"-default" "plain" "-nofork"))
|
||||
(set-process-filter proc 'zwgc-wakeup))
|
||||
(shell-mode)
|
||||
(local-set-key "\177" 'zwgc-punt)
|
||||
)
|
||||
))
|
||||
|
||||
|
||||
(defun Special-pop-up-window (buffer &optional max-height)
|
||||
"Pop up a window that is just big enough to hold the current buffer."
|
||||
(interactive "bBuffer to pop: ")
|
||||
(let* ((retwin (selected-window))
|
||||
(pop-up-windows t)
|
||||
(window-min-height 1))
|
||||
(pop-to-buffer buffer)
|
||||
(setq lines (1+ (count-lines (point-min) (point-max))))
|
||||
(enlarge-window (- lines (window-height (selected-window))))
|
||||
(goto-char (point-min))
|
||||
(other-window 1)
|
||||
))
|
||||
|
||||
(defun zwgc-punt ()
|
||||
"Delete the current ZephyrGram from the *zwgc* buffer."
|
||||
(interactive)
|
||||
(let ((window-min-height 1))
|
||||
(display-buffer (get-buffer "*zwgc*"))
|
||||
(delete-region (point-min) (point-max))
|
||||
(widen)
|
||||
(if (not (search-backward "\007" nil t))
|
||||
(delete-windows-on "*zwgc*")
|
||||
(narrow-to-region (point) (point-max))
|
||||
(enlarge-window (- (1+ (count-lines (point-min) (point-max)))
|
||||
(window-height (selected-window))))
|
||||
(goto-char (point-min))
|
||||
)))
|
||||
;;
|
||||
;; [eichin:19880309.2005EST]
|
||||
;; zsend.el
|
||||
;; Send zephyrgrams from emacs...
|
||||
;;
|
||||
|
||||
(defvar *who* "" "last user sent to with zsend")
|
||||
|
||||
(defun zsend (&optional who message)
|
||||
"zsend prompts for a user name and a message to send to them as a
|
||||
ZephyrGram. If the message is blank, a buffer is popped up to edit the
|
||||
message in. If a prefix argument is given, zsend prompts for an
|
||||
instance instead. If the user name is blank, the last one is reused."
|
||||
(interactive
|
||||
(list (if current-prefix-arg ; is this portable???
|
||||
(cons 'instance (read-input "Instance:"))
|
||||
(cons 'who (read-input "Who:")))
|
||||
; (select-window (minibuffer-window))
|
||||
; (enlarge-window 4)
|
||||
(read-input "Message:")))
|
||||
(save-excursion
|
||||
(let ((tempbuf (get-buffer-create " *zephyr*send*")))
|
||||
(switch-to-buffer tempbuf)
|
||||
(local-set-key "\C-c\C-c" 'exit-recursive-edit)
|
||||
(erase-buffer)
|
||||
(if (and (equal (cdr who) "")
|
||||
(equal *who* ""))
|
||||
(message "Please specify user at least once.")
|
||||
(if (not (equal (cdr who) ""))
|
||||
(setq *who* who) ; save *who* for next time
|
||||
(setq who *who*)) ; or, use the old value
|
||||
(if (not (equal message ""))
|
||||
(progn
|
||||
(insert message)
|
||||
(zwrite who))
|
||||
(progn
|
||||
(recursive-edit)
|
||||
(zwrite who)))))))
|
||||
|
||||
|
||||
(defun zwrite (who)
|
||||
"Send a ZephyrGram to user WHO, zsend is the user interface to this."
|
||||
(if (eq 'who (car who))
|
||||
(call-process-region (point-min) (point-max) ;range
|
||||
"/usr/athena/zwrite" ;process
|
||||
t ;delete-p
|
||||
t ;output-p
|
||||
nil ;redisplay-p
|
||||
"-q" ;args -- ignore server responses.
|
||||
(cdr who))
|
||||
(call-process-region (point-min) (point-max) ;range
|
||||
"/usr/athena/zwrite" ;process
|
||||
t ;delete-p
|
||||
t ;output-p
|
||||
nil ;redisplay-p
|
||||
"-q" ;args -- ignore server responses.
|
||||
"-i" ;[eichin:19880312.0015EST]
|
||||
(cdr who)))
|
||||
(if (not (equal (point-max) 1))
|
||||
(message (buffer-substring 1 (1- (point-max))))))
|
||||
|
||||
; suggested binding (control-meta-z)
|
||||
;(global-set-key "\M-\C-z" 'zsend)
|
||||
|
||||
|
28
zwgc/zwgc.h
Normal file
28
zwgc/zwgc.h
Normal file
@ -0,0 +1,28 @@
|
||||
/* 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 <zephyr/mit-copyright.h>
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
extern int zwgc_debug;
|
||||
#define dprintf(x) if (zwgc_debug) printf(x)
|
||||
#define dprintf1(x,y) if (zwgc_debug) printf(x,y)
|
||||
|
||||
#else
|
||||
|
||||
#define dprintf(x)
|
||||
#define dprintf1(x,y)
|
||||
|
||||
#endif
|
102
zwgc/zwgc_resources
Normal file
102
zwgc/zwgc_resources
Normal file
@ -0,0 +1,102 @@
|
||||
! Copyright 1989 Massachusetts Institute of Technology
|
||||
!
|
||||
! For copying and distribution information, see the file
|
||||
! "mit-copyright.h".
|
||||
!
|
||||
! $Id$
|
||||
!
|
||||
!
|
||||
! Zwgc application specific global resources:
|
||||
!
|
||||
|
||||
*style*substyle.default.fontfamily: default
|
||||
*style.message.personal*substyle.title.fontfamily: huge
|
||||
|
||||
*style*geometry: +0+0
|
||||
|
||||
!
|
||||
! The following is the adobe-courier font family. Availiable sizes are
|
||||
! 80, 100, 120, 140, 180, and 240. This family used to be courier.
|
||||
!
|
||||
|
||||
*fontfamily.default.small.roman: *-courier-medium-r-*-80-*-m-*,*
|
||||
*fontfamily.default.small.bold: *-courier-bold-r-*-80-*-m-*,*
|
||||
*fontfamily.default.small.italic: *-courier-medium-o-*-80-*-m-*,*
|
||||
*fontfamily.default.small.bolditalic: *-courier-bold-o-*-80-*-m-*,*
|
||||
|
||||
*fontfamily.default.medium.roman: *-courier-medium-r-*-120-*-m-*,*
|
||||
*fontfamily.default.medium.bold: *-courier-bold-r-*-120-*-m-*,*
|
||||
*fontfamily.default.medium.italic: *-courier-medium-o-*-120-*-m-*,*
|
||||
*fontfamily.default.medium.bolditalic: *-courier-bold-o-*-120-*-m-*,*
|
||||
|
||||
*fontfamily.default.large.roman: *-courier-medium-r-*-240-*-m-*,*
|
||||
*fontfamily.default.large.bold: *-courier-bold-r-*-240-*-m-*,*
|
||||
*fontfamily.default.large.italic: *-courier-medium-o-*-240-*-m-*,*
|
||||
*fontfamily.default.large.bolditalic: *-courier-bold-o-*-240-*-m-*,*
|
||||
|
||||
!
|
||||
! The following is the adobe-courier font family. Availiable sizes are
|
||||
! 80, 100, 120, 140, 180, and 240. This family used to be courier.
|
||||
!
|
||||
|
||||
*fontfamily.courier.small.roman: *-courier-medium-r-*-80-*-m-*,*
|
||||
*fontfamily.courier.small.bold: *-courier-bold-r-*-80-*-m-*,*
|
||||
*fontfamily.courier.small.italic: *-courier-medium-o-*-80-*-m-*,*
|
||||
*fontfamily.courier.small.bolditalic: *-courier-bold-o-*-80-*-m-*,*
|
||||
|
||||
*fontfamily.courier.medium.roman: *-courier-medium-r-*-120-*-m-*,*
|
||||
*fontfamily.courier.medium.bold: *-courier-bold-r-*-120-*-m-*,*
|
||||
*fontfamily.courier.medium.italic: *-courier-medium-o-*-120-*-m-*,*
|
||||
*fontfamily.courier.medium.bolditalic: *-courier-bold-o-*-120-*-m-*,*
|
||||
|
||||
*fontfamily.courier.large.roman: *-courier-medium-r-*-240-*-m-*,*
|
||||
*fontfamily.courier.large.bold: *-courier-bold-r-*-240-*-m-*,*
|
||||
*fontfamily.courier.large.italic: *-courier-medium-o-*-240-*-m-*,*
|
||||
*fontfamily.courier.large.bolditalic: *-courier-bold-o-*-240-*-m-*,*
|
||||
|
||||
!
|
||||
! The following is the adobe-times font family. Availiable sizes are
|
||||
! 80, 100, 120, 140, 180, and 240. This family used to be times-roman.
|
||||
!
|
||||
*fontfamily.times.small.roman: *-times-medium-r-*-80-*-p-*,*
|
||||
*fontfamily.times.small.bold: *-times-bold-r-*-80-*-p-*,*
|
||||
*fontfamily.times.small.italic: *-times-medium-i-*-80-*-p-*,*
|
||||
*fontfamily.times.small.bolditalic: *-times-bold-i-*-80-*-p-*,*
|
||||
|
||||
*fontfamily.times.medium.roman: *-times-medium-r-*-120-*-p-*,*
|
||||
*fontfamily.times.medium.bold: *-times-bold-r-*-120-*-p-*,*
|
||||
*fontfamily.times.medium.italic: *-times-medium-i-*-120-*-p-*,*
|
||||
*fontfamily.times.medium.bolditalic: *-times-bold-i-*-120-*-p-*,*
|
||||
|
||||
*fontfamily.times.large.roman: *-times-medium-r-*-240-*-p-*,*
|
||||
*fontfamily.times.large.bold: *-times-bold-r-*-240-*-p-*,*
|
||||
*fontfamily.times.large.italic: *-times-medium-i-*-240-*-p-*,*
|
||||
*fontfamily.times.large.bolditalic: *-times-bold-i-*-240-*-p-*,*
|
||||
|
||||
!
|
||||
! The following is the adobe-helvetica font family. Availiable sizes are
|
||||
! 80, 100, 120, 140, 180, and 240. This family used to be helvetica.
|
||||
!
|
||||
*fontfamily.helvetica.small.roman: *-helvetica-medium-r-*-80-*-p-*,*
|
||||
*fontfamily.helvetica.small.bold: *-helvetica-bold-r-*-80-*-p-*,*
|
||||
*fontfamily.helvetica.small.italic: *-helvetica-medium-o-*-80-*-p-*,*
|
||||
*fontfamily.helvetica.small.bolditalic: *-helvetica-bold-o-*-80-*-p-*,*
|
||||
|
||||
*fontfamily.helvetica.medium.roman: *-helvetica-medium-r-*-120-*-p-*,*
|
||||
*fontfamily.helvetica.medium.bold: *-helvetica-bold-r-*-120-*-p-*,*
|
||||
*fontfamily.helvetica.medium.italic: *-helvetica-medium-o-*-120-*-p-*,*
|
||||
*fontfamily.helvetica.medium.bolditalic:*-helvetica-bold-o-*-120-*-p-*,*
|
||||
|
||||
*fontfamily.helvetica.large.roman: *-helvetica-medium-r-*-240-*-p-*,*
|
||||
*fontfamily.helvetica.large.bold: *-helvetica-bold-r-*-240-*-p-*,*
|
||||
*fontfamily.helvetica.large.italic: *-helvetica-medium-o-*-240-*-p-*,*
|
||||
*fontfamily.helvetica.large.bolditalic: *-helvetica-bold-o-*-240-*-p-*,*
|
||||
|
||||
!
|
||||
! Quick hack...
|
||||
!
|
||||
|
||||
*fontfamily.huge*roman: *-charter-medium-r-*-33-*-p-*,*
|
||||
*fontfamily.huge*bold: *-charter-bold-r-*-33-*-p-*,*
|
||||
*fontfamily.huge*italic: *-charter-medium-i-*-33-*-p-*,*
|
||||
*fontfamily.huge*bolditalic: *-charter-bold-i-*-33-*-p-*,*
|
Reference in New Issue
Block a user