summaryrefslogtreecommitdiff
path: root/clients/xzwrite
diff options
context:
space:
mode:
authorGravatar Kenneth G Raeburn <raeburn@mit.edu>1990-11-16 06:14:16 +0000
committerGravatar Kenneth G Raeburn <raeburn@mit.edu>1990-11-16 06:14:16 +0000
commitd6c1a4a1043100a2534371d7572ec0ef89f117f4 (patch)
treeb9de8a21615c4b46f49383ee91204836b69ca271 /clients/xzwrite
parent1849bb26ec874fdd26a5d129a1041fedd85850e2 (diff)
Initial revision
Diffstat (limited to 'clients/xzwrite')
-rw-r--r--clients/xzwrite/GetString.c148
-rw-r--r--clients/xzwrite/GetString.h9
-rw-r--r--clients/xzwrite/Imakefile38
-rw-r--r--clients/xzwrite/Popup.c100
-rw-r--r--clients/xzwrite/XZwrite243
-rw-r--r--clients/xzwrite/associate.c51
-rw-r--r--clients/xzwrite/associate.h18
-rw-r--r--clients/xzwrite/bfgets.c33
-rw-r--r--clients/xzwrite/dest_window.c118
-rw-r--r--clients/xzwrite/destlist.c354
-rw-r--r--clients/xzwrite/edit_window.c123
-rw-r--r--clients/xzwrite/gethomedir.c15
-rw-r--r--clients/xzwrite/interface.c369
-rw-r--r--clients/xzwrite/logins.c98
-rw-r--r--clients/xzwrite/menu_window.c66
-rw-r--r--clients/xzwrite/nmalloc.c996
-rw-r--r--clients/xzwrite/nmalloc.h20
-rw-r--r--clients/xzwrite/resource.c112
-rw-r--r--clients/xzwrite/util.c76
-rw-r--r--clients/xzwrite/xzwrite-proto.h98
-rw-r--r--clients/xzwrite/xzwrite.1400
-rw-r--r--clients/xzwrite/xzwrite.c49
-rw-r--r--clients/xzwrite/xzwrite.h50
-rw-r--r--clients/xzwrite/yank.c58
-rw-r--r--clients/xzwrite/zephyr.c262
25 files changed, 3904 insertions, 0 deletions
diff --git a/clients/xzwrite/GetString.c b/clients/xzwrite/GetString.c
new file mode 100644
index 0000000..2a5faaf
--- /dev/null
+++ b/clients/xzwrite/GetString.c
@@ -0,0 +1,148 @@
+#include <X11/Intrinsic.h>
+#include <X11/StringDefs.h>
+#include <X11/Shell.h>
+#include <X11/Xaw/Form.h>
+#include <X11/Xaw/Label.h>
+#include <X11/Xaw/AsciiText.h>
+#include <X11/Xaw/Command.h>
+
+#include "GetString.h"
+
+#define XVCMW XtVaCreateManagedWidget
+
+static int accepted, cancelled;
+static void Accept(), Cancel(), Focus();
+static int HierEvent();
+
+extern void Popup();
+
+static XtActionsRec actionTable[] = {
+ {"Accept", (XtActionProc) Accept},
+ {"Cancel", (XtActionProc) Cancel},
+ {"Focus", (XtActionProc) Focus},
+};
+
+Widget InitGetString(parent, name)
+ Widget parent;
+ char *name;
+{
+ static int first_time = 1;
+ Widget getStringWindow, form, title, edit, accept, cancel;
+
+ if (first_time) {
+ XtAppAddActions(XtWidgetToApplicationContext(parent), actionTable,
+ XtNumber(actionTable));
+ first_time = 0;
+ };
+
+ getStringWindow = XtVaCreatePopupShell(name, transientShellWidgetClass,
+ parent,
+ XtNinput, True,
+ NULL);
+ form = XVCMW("getStringForm", formWidgetClass, getStringWindow, NULL);
+ title = XVCMW("getStringTitle", labelWidgetClass, form, NULL);
+ edit = XVCMW("getStringEdit", asciiTextWidgetClass, form, NULL);
+ accept = XVCMW("getStringAccept", commandWidgetClass, form, NULL);
+ cancel = XVCMW("getStringCancel", commandWidgetClass, form, NULL);
+ XtSetKeyboardFocus(form, edit);
+
+ return getStringWindow;
+}
+
+int GetString(getStringWindow, label, value, pop_type, buf, len)
+ Widget getStringWindow;
+ String label, value;
+ int pop_type;
+ char *buf;
+ int len;
+{
+ XtAppContext app_con;
+ Widget title, edit;
+ XEvent event;
+
+ app_con = XtWidgetToApplicationContext(getStringWindow);
+ title = XtNameToWidget(getStringWindow, "getStringForm.getStringTitle");
+ edit = XtNameToWidget(getStringWindow, "getStringForm.getStringEdit");
+
+ XtVaSetValues(title, XtNlabel, label, NULL);
+ XtVaSetValues(edit, XtNstring, value, NULL);
+
+ XtRealizeWidget(getStringWindow);
+ Popup(getStringWindow, XtGrabExclusive, pop_type);
+
+ accepted = cancelled = 0;
+ while (! accepted && ! cancelled) {
+ XtAppNextEvent(app_con, &event);
+ XtDispatchEvent(&event);
+ }
+
+ XtPopdown(getStringWindow);
+
+ if (accepted) {
+ char *s;
+
+ XtVaGetValues(edit, XtNstring, (XtArgVal) &s, NULL);
+ strncpy(buf, s, len-2);
+ buf[len-1] = '\0';
+ XawAsciiSourceFreeString(edit);
+
+ return GETSTRING_ACCEPT;
+ }
+ else
+ return GETSTRING_CANCEL;
+}
+
+/*
+ * I thought I needed this routine becaues XtAppNextEvent was
+ * returning events for widgets that should have been blocked by the
+ * XtGrabExclusive.. but it turns out that XtDispatch deals with that
+ * (correctly) so this code is useless.
+ */
+static int HierEvent(w, event)
+ Widget w;
+ XAnyEvent *event;
+{
+ Widget event_w;
+
+ event_w = XtWindowToWidget(XtDisplay(w), event->window);
+
+ while (event_w = XtParent(event_w)) {
+ if (w == event_w)
+ return 1;
+ else if (XtIsShell(event_w))
+ break;
+ }
+
+ return 0;
+}
+
+/* ARGSUSED */
+static void Accept(w, e, p, n)
+ Widget w;
+ XEvent *e;
+ String *p;
+ Cardinal *n;
+{
+ accepted = 1;
+}
+
+/* ARGSUSED */
+static void Cancel(w, e, p, n)
+ Widget w;
+ XEvent *e;
+ String *p;
+ Cardinal *n;
+{
+ cancelled = 1;
+}
+
+/* ARGSUSED */
+static void Focus(w, e, p, n)
+ Widget w;
+ XEvent *e;
+ String *p;
+ Cardinal *n;
+{
+ XSetInputFocus(XtDisplay(w), XtWindow(w), RevertToPointerRoot,
+ CurrentTime);
+}
diff --git a/clients/xzwrite/GetString.h b/clients/xzwrite/GetString.h
new file mode 100644
index 0000000..562485d
--- /dev/null
+++ b/clients/xzwrite/GetString.h
@@ -0,0 +1,9 @@
+#include <X11/Intrinsic.h>
+
+#define GETSTRING_ACCEPT -1000
+#define GETSTRING_CANCEL -1001
+
+Widget InitGetString();
+int GetString();
+
+
diff --git a/clients/xzwrite/Imakefile b/clients/xzwrite/Imakefile
new file mode 100644
index 0000000..3d00c37
--- /dev/null
+++ b/clients/xzwrite/Imakefile
@@ -0,0 +1,38 @@
+/**/# Copyright 1990 Massachusetts Institute of Technology.
+/**/#
+/**/# For copying and distribution information, see the file
+/**/# "mit-copyright.h".
+/**/#
+/**/# $Source$
+/**/# $Author$
+/**/# $Header$
+/**/#
+
+LIBS= ${ZEPHYR_LIB} ${COMERR_LIB} ${KRB_LIB} ${DES_LIB} $(DYN_LIB) \
+ -lXaw -lXmu -lXt -lXext -lX11
+LINTLIBS= ${ZEPHYR_LINTLIB} ${COMERR_LINTLIB} ${KRB_LINTLIB} \
+ ${DES_LINTLIB} $(DYN_LINTLIB)
+
+SRCS = interface.c resource.c destlist.c util.c bfgets.c \
+ gethomedir.c dest_window.c xzwrite.c edit_window.c zephyr.c\
+ GetString.c Popup.c yank.c menu_window.c logins.c
+
+OBJS = interface.o resource.o destlist.o util.o bfgets.o \
+ gethomedir.o dest_window.o xzwrite.o edit_window.o zephyr.o\
+ GetString.o Popup.o yank.o menu_window.o logins.o
+
+HDRS = GetString.h associate.h xzwrite.h xzwrite-proto.h
+
+SRCDIR= ${SRCTOP}/clients/xzwrite
+CODE= ${SRCS} ${HDRS} Imakefile xzwrite.1 XZwrite
+DEFINES=-DXZWRITE_SEARCH_PATHS=\"$(ACLDIR)\"
+
+normal_obj_rule()
+
+program(xzwrite,${OBJS},,${LIBS},${CLIENTDIR})
+
+manpage(1,xzwrite.1)
+
+install::
+ $(RM) $(ACLDIR)/XZwrite
+ $(CP) XZwrite $(ACLDIR)/XZwrite
diff --git a/clients/xzwrite/Popup.c b/clients/xzwrite/Popup.c
new file mode 100644
index 0000000..616f594
--- /dev/null
+++ b/clients/xzwrite/Popup.c
@@ -0,0 +1,100 @@
+/*
+ * This code has gone back and forth between myself and Jon Kamens
+ * so many times that neither really knows who wrote it..
+ */
+
+#include <X11/Intrinsic.h>
+#include <X11/StringDefs.h>
+
+static void _initPopup();
+void Popup(), PopupSafe(), PopupAtPointer();
+
+static int display_height, display_width;
+
+static void _initPopup(w)
+ Widget w;
+{
+ Display *dpy;
+ int screen;
+
+ dpy = XtDisplay(w);
+ screen = DefaultScreen(dpy);
+ display_height = DisplayHeight(dpy, screen);
+ display_width = DisplayWidth(dpy, screen);
+}
+
+/* ARGSUSED */
+void Popup(shell, GrabType, pop_type)
+ Widget shell;
+ XtGrabKind GrabType;
+ int pop_type;
+{
+ PopupAtPointer(shell, GrabType);
+}
+
+void PopupSafe(w, x, y, GrabType)
+ Widget w;
+ Dimension x, y;
+ XtGrabKind GrabType;
+{
+ static int first_time = 1;
+ Dimension width, height, border;
+
+ if (first_time) {
+ _initPopup(w);
+ first_time = 0;
+ }
+
+ XtVaGetValues(w,
+ XtNwidth, &width,
+ XtNheight, &height,
+ XtNborderWidth, &border,
+ NULL);
+
+ if (x + width + 2 * border > display_width)
+ x = display_width - width - 2 * border;
+ if (y + height + 2 * border > display_height)
+ y = display_height - height - 2 * border;
+
+ XtVaSetValues(w,
+ XtNx, x,
+ XtNy, y,
+ NULL);
+
+ XtPopup(w, GrabType);
+}
+
+void PopupAtPointer(w, GrabType)
+ Widget w;
+ XtGrabKind GrabType;
+{
+ Window garbage1, garbage2, window;
+ int root_x, root_y, x2, y2;
+ unsigned int mask;
+ Dimension width, height, border;
+ Display *dpy;
+
+ dpy = XtDisplay(w);
+ window = XtWindow(XtParent(w));
+
+ if (XQueryPointer(dpy, window, &garbage1, &garbage2,
+ &root_x, &root_y, &x2, &y2, &mask)) {
+
+ XtVaGetValues(w,
+ XtNwidth, &width,
+ XtNheight, &height,
+ XtNborderWidth, &border,
+ NULL);
+
+ if (root_x >= width / 2 + border)
+ root_x -= width / 2 + border;
+ else
+ root_x = 0;
+ if (root_y >= height / 2 + border)
+ root_y -= height / 2 + border;
+ else
+ root_y = 0;
+
+ PopupSafe(w, (Dimension) root_x, (Dimension) root_y, GrabType);
+ }
+}
diff --git a/clients/xzwrite/XZwrite b/clients/xzwrite/XZwrite
new file mode 100644
index 0000000..7568c85
--- /dev/null
+++ b/clients/xzwrite/XZwrite
@@ -0,0 +1,243 @@
+*resize: on
+*allowShellResize: on
+
+*reverseVideo: on
+*maxYanks: 25
+*ping: on
+*verbose: on
+*auth: on
+*yankDest: off
+*addGlobals: on
+*classInst: on
+*closeOnSend: off
+*trackLogins: on
+*pongScan: off
+*readAnyone: on
+*readXzwrite: on
+
+*icon.bitmap: /usr/sipb/bitmaps.x11/z
+*icon.translations: #override\
+ <BtnDown>: set() \n\
+ <Btn1Up>: OpenSend() unset() \n\
+ Ctrl<Btn2Up>: Quit() \n\
+ <Btn3Up>: OpenMenu() unset()
+
+*sendForm.defaultDistance: -1
+*sendForm.borderWidth: 0
+
+*sendClose.label: Close Window
+*sendClose.top: ChainTop
+*sendClose.bottom: ChainTop
+*sendClose.left: ChainLeft
+*sendClose.right: ChainRight
+*sendClose.width: 671
+*sendClose.translations:#override\
+ <BtnDown>: set() \n\
+ <BtnUp>: CloseSend() unset() \n\
+
+*editPane.fromVert: sendClose
+*editPane.top: ChainTop
+*editPane.bottom: ChainBottom
+*editPane.left: ChainLeft
+
+*editTitle.showGrip: false
+*editTitle.width: 480
+*editTitle.borderWidth: 0
+
+*editForm.showGrip: false
+*editForm.borderWidth: 2
+*editForm.borderColor: black
+
+*editSend.label: Send Message
+*editSend.left: ChainLeft
+*editSend.translations:#override\
+ <BtnDown>: set() \n\
+ <BtnUp>: YankStore() SendMessage() unset() \n\
+
+*editClear.label: Clear Editor
+*editClear.fromHoriz: editSend
+*editClear.translations:#override\
+ <BtnDown>: set() \n\
+ <BtnUp>: ClearEditor() unset() \n\
+
+*editPrev.label: Yank-Prev
+*editPrev.fromHoriz: editClear
+*editPrev.translations:#override\
+ <BtnDown>: set() \n\
+ <BtnUp>: YankPrev() unset() \n\
+
+*editNext.label: Yank-Next
+*editNext.fromHoriz: editPrev
+*editNext.right: ChainRight
+*editNext.translations:#override\
+ <BtnDown>: set() \n\
+ <BtnUp>: YankNext() unset() \n\
+
+*editor.width: 480
+*editor.height: 130
+*editor*editType: edit
+*editor*wrap: never
+*editor*autoFill: true
+*editor*useStringInPlace: false
+*editor.translations: #override\
+ Ctrl<Key>Return: YankStore() SendMessage() ClearEditor() \n\
+ Ctrl<Key>Y: YankStore() YankPrev() \n\
+ Meta<Key>O: YankStore() YankPrev() \n\
+ Meta<Key>P: YankPrev() \n\
+ Meta<Key>N: YankNext()
+
+*destForm.borderWidth: 0
+*destForm.defaultDistance: 0
+*destForm.fromVert: sendClose
+*destForm.top: ChainTop
+*destForm.bottom: ChainBottom
+*destForm.right: ChainRight
+*destForm.fromHoriz: editPane
+
+*destScroll.top: ChainTop
+*destScroll.bottom: ChainBottom
+*destScroll.left: ChainLeft
+*destScroll.right: ChainRight
+*destScroll.width: 190
+*destScroll.height: 178
+*destScroll.resizable: false
+*destScroll.allowVert: true
+*destScroll.allowHoriz: false
+*destScroll.forceBars: true
+
+*destList.forceColumns: on
+*destList.defaultColumns: 1
+*destList.translations: #override\
+ <Motion>: Set() \n\
+ <Btn1Up>: Set() SelectDest() Unset() \n\
+ <Btn2Up>: CreateDest() \n\
+ <Btn3Up>: Set() DeleteDest() Unset() \n\
+ <LeaveWindow>: Unset()
+
+*menuClose.label: Close Window
+*menuClose.top: ChainTop
+*menuClose.left: ChainLeft
+*menuClose.right: ChainRight
+*menuClose.width: 200
+*menuClose.translations:#override\
+ <BtnDown>: set() \n\
+ <BtnUp>: CloseMenu() unset() \n\
+
+*signature.label: Change Signature
+*signature.fromVert: menuClose
+*signature.left: ChainLeft
+*signature.right: ChainRight
+*signature.width: 200
+*signature.translations: #override\
+ <BtnDown>: set() \n\
+ <BtnUp>: Signature()
+
+*closeOnSend.label: Close On Send
+*closeOnSend.fromVert: signature
+*closeOnSend.left: ChainLeft
+*closeOnSend.right: ChainRight
+*closeOnSend.width: 200
+*closeOnSend.translations: #override\
+ <BtnDown>,<BtnUp>: toggle() ToggleOption()
+
+*pings.label: Pings
+*pings.fromVert: closeOnSend
+*pings.left: ChainLeft
+*pings.right: ChainRight
+*pings.width: 200
+*pings.translations: #override\
+ <BtnDown>,<BtnUp>: toggle() ToggleOption()
+
+*verbose.label: Verbose
+*verbose.fromVert: pings
+*verbose.left: ChainLeft
+*verbose.right: ChainRight
+*verbose.width: 200
+*verbose.translations: #override\
+ <BtnDown>,<BtnUp>: toggle() ToggleOption()
+
+*authentic.label: Authenticate
+*authentic.fromVert: verbose
+*authentic.left: ChainLeft
+*authentic.right: ChainRight
+
+*authentic.width: 200
+*authentic.translations: #override\
+ <BtnDown>,<BtnUp>: toggle() ToggleOption()
+
+*yankDest.label: Yank Destinations
+*yankDest.fromVert: authentic
+*yankDest.left: ChainLeft
+*yankDest.right: ChainRight
+*yankDest.width: 200
+*yankDest.translations: #override\
+ <BtnDown>,<BtnUp>: toggle() ToggleOption()
+
+*addGlobals.label: Add Globals
+*addGlobals.fromVert: yankDest
+*addGlobals.left: ChainLeft
+*addGlobals.right: ChainRight
+*addGlobals.width: 200
+*addGlobals.translations: #override\
+ <BtnDown>,<BtnUp>: toggle() ToggleOption()
+
+*classInst.label: Class/Inst
+*classInst.fromVert: addGlobals
+*classInst.left: ChainLeft
+*classInst.right: ChainRight
+*classInst.width: 200
+*classInst.translations: #override\
+ <BtnDown>,<BtnUp>: toggle() ToggleOption()
+
+*exitProgram.label: Quit XZWRITE
+*exitProgram.fromVert: classInst
+*exitProgram.left: ChainLeft
+*exitProgram.right: ChainRight
+*exitProgram.width: 200
+*exitProgram.translations:#override\
+ <BtnDown>: set() \n\
+ <BtnUp>: Quit()
+
+*getStringWindow.resize: true
+
+*getStringTitle.borderWidth: 0
+*getStringTitle.top: ChainTop
+*getStringTitle.bottom: ChainTop
+*getStringTitle.left: ChainLeft
+*getStringTitle.right: ChainRight
+
+*getStringForm.width: 210
+
+*getStringEdit*editType: edit
+*getStringEdit.resize: width
+*getStringEdit.resizable: true
+*getStringEdit.top: ChainTop
+*getStringEdit.bottom: ChainTop
+*getStringEdit.left: ChainLeft
+*getStringEdit.right: ChainRight
+*getStringEdit.fromVert: getStringTitle
+*getStringEdit.translations: #override\
+ <Key>Return: Accept() \n\
+
+*getStringAccept.width: 105
+*getStringAccept.label: Accept
+*getStringAccept.fromVert: getStringEdit
+*getStringAccept.top: ChainTop
+*getStringAccept.bottom: ChainTop
+*getStringAccept.left: ChainRight
+*getStringAccept.right: ChainRight
+*getStringAccept.translations: #override\
+ <BtnDown>: set() \n\
+ <BtnUp>: Accept() unset()
+
+*getStringCancel.width: 105
+*getStringCancel.label: Cancel
+*getStringCancel.fromVert: getStringEdit
+*getStringCancel.fromHoriz: getStringAccept
+*getStringCancel.top: ChainTop
+*getStringCancel.bottom: ChainTop
+*getStringCancel.left: ChainRight
+*getStringCancel.right: ChainRight
+*getStringCancel.translations: #override\
+ <BtnDown>: set() \n\
+ <BtnUp>: Cancel() unset()
diff --git a/clients/xzwrite/associate.c b/clients/xzwrite/associate.c
new file mode 100644
index 0000000..5864a24
--- /dev/null
+++ b/clients/xzwrite/associate.c
@@ -0,0 +1,51 @@
+/*
+ * This is a string-associative array abstraction with really lousy
+ * semantics. But it does what I need at the moment.
+ */
+
+#include "associate.h"
+
+AArray AACreate()
+{
+ return (DynCreate(sizeof(AElementRec), 0));
+}
+
+void AADestroy(array)
+ AArray array;
+{
+ DynDestroy(array);
+}
+
+int AAInsert(array, index, value)
+ AArray array;
+ char *index, *value;
+{
+ AElementRec temp;
+ int ret;
+
+ temp.index = index;
+ temp.value = value;
+
+ ret = DynAdd(array, &temp);
+ if (ret != DYN_OK)
+ return AA_FAILED;
+ else
+ return AA_OK;
+}
+
+char *AALookup(array, index)
+ AArray array;
+ char *index;
+{
+ AElementRec *a;
+ int i;
+
+ a = DynGet((char *) array, 0);
+ for (i=0; i < DynSize(array); i++)
+ if (strcmp(a[i].index, index) == 0)
+ return (a[i].value);
+
+ return NULL;
+}
+
+
diff --git a/clients/xzwrite/associate.h b/clients/xzwrite/associate.h
new file mode 100644
index 0000000..a6ff2dc
--- /dev/null
+++ b/clients/xzwrite/associate.h
@@ -0,0 +1,18 @@
+#ifndef _Associate_h
+#define _Associate_h
+
+#include <stdio.h>
+#include <dyn.h>
+
+#define AA_OK -1000
+#define AA_FAILED -1001
+#define AA_NOTFOUND -1002
+
+typedef struct _array_elements {
+ char *index;
+ char *value;
+} AElementRec, *AElement;
+
+typedef DynObject AArray;
+
+#endif /* _Associate_h */
diff --git a/clients/xzwrite/bfgets.c b/clients/xzwrite/bfgets.c
new file mode 100644
index 0000000..19df253
--- /dev/null
+++ b/clients/xzwrite/bfgets.c
@@ -0,0 +1,33 @@
+/* bfgets.c
+ *
+ * declaration:
+ * char *bfgets(s, n, iop)
+ * char *s;
+ * int n;
+ * FILE *iop;
+ *
+ * Reads n-1 characters or until a newline from iop. The terminating newline
+ * is NOT RETURNED.
+ *
+ * Written by Barr3y Jaspan (bjaspan@athena.mit.edu)
+ */
+
+#include <stdio.h>
+
+char *bfgets();
+
+char *bfgets(s, n, iop)
+ char *s;
+ int n;
+ FILE *iop;
+{
+ register int c;
+ register char *cs;
+
+ cs = s;
+ while ((--n > 0) && ((c = getc(iop)) !=EOF) && (c != '\n'))
+ *cs++ = c;
+
+ *cs = '\0';
+ return (c == EOF && cs == s) ? NULL : s;
+}
diff --git a/clients/xzwrite/dest_window.c b/clients/xzwrite/dest_window.c
new file mode 100644
index 0000000..4e32329
--- /dev/null
+++ b/clients/xzwrite/dest_window.c
@@ -0,0 +1,118 @@
+#include <X11/Intrinsic.h>
+#include <X11/StringDefs.h>
+#include <X11/Shell.h>
+#include <X11/Xaw/Command.h>
+#include <X11/Xaw/Form.h>
+#include <X11/Xaw/Toggle.h>
+#include <X11/Xaw/List.h>
+
+#include "xzwrite.h"
+#include "GetString.h"
+
+extern Widget toplevel, getString, destList;
+extern DestRec current_dest;
+extern Defaults defs;
+
+void display_dest()
+{
+ XawListChange(destList, (String *) dest_text(), dest_num(), 0, True);
+}
+
+void delete_dest()
+{
+ XawListReturnStruct *item;
+
+ item = XawListShowCurrent(destList);
+ if (item->list_index == XAW_LIST_NONE)
+ return;
+
+ dest_delete_string(item->string);
+ display_dest();
+}
+
+void create_dest()
+{
+ char buf[ZLEN*3+2], *s;
+ int ret;
+
+ ret = GetString(getString, "Enter new <class,instance,recipient> triple:",
+ "", 0, buf, ZLEN*3+2);
+ if (ret == GETSTRING_ACCEPT) {
+ s = (char *) malloc(strlen(buf)+1);
+ strcpy(s, buf);
+ if (dest_add_string(s) == NULL) {
+ XBell(XtDisplay(toplevel), 0);
+ free(s);
+ }
+ else
+ display_dest();
+ }
+}
+
+void select_dest()
+{
+ DestRec dest;
+ XawListReturnStruct *item;
+ int ret, used_global = 0;
+
+ item = XawListShowCurrent(destList);
+ if (item->list_index == XAW_LIST_NONE)
+ return;
+
+ parse_into_dest(&dest, item->string);
+
+ if (! strcmp(dest.zclass, "...")) {
+ ret = GetString(getString, "Enter CLASS to send to:", "", 0,
+ dest.zclass, ZLEN);
+ if (ret != GETSTRING_ACCEPT) return;
+ used_global = 1;
+ }
+
+ if (! strcmp(dest.zinst, "...")) {
+ ret = GetString(getString, "Enter INSTANCE to send to:", "", 0,
+ dest.zinst, ZLEN);
+ if (ret != GETSTRING_ACCEPT) return;
+ used_global = 1;
+ }
+
+ if (! strcmp(dest.zrecip, "...")) {
+ ret = GetString(getString, "Enter RECIPIENT to send to:", "", 0,
+ dest.zrecip, ZLEN);
+ if (ret != GETSTRING_ACCEPT) return;
+ used_global = 1;
+ }
+
+ if (defs.add_globals && used_global) {
+ /* A hack so using "..." looks pretty */
+ if (! strcmp(dest.zclass, DEFAULT_CLASS) &&
+ ! strcmp(dest.zinst, DEFAULT_INST)) {
+ char *temp;
+
+ temp = (char *) malloc(strlen(dest.zrecip) + 1);
+ strcpy(temp, dest.zrecip);
+ dest_add_string(temp);
+ }
+ else
+ dest_add(&dest);
+ display_dest();
+ }
+
+ if (defs.ping && *dest.zrecip) {
+ ret = zeph_ping(&dest);
+ switch (ret) {
+ case SEND_OK:
+ edit_set_title(&dest);
+ bcopy((char *) &dest, (char *) &current_dest, sizeof(DestRec));
+ break;
+ case SENDFAIL_SEND:
+ case SENDFAIL_RECV:
+ case SENDFAIL_ACK:
+ XBell(XtDisplay(toplevel), 0);
+ return;
+ }
+ }
+ else {
+ edit_set_title(&dest);
+ bcopy((char *) &dest, (char *) &current_dest, sizeof(DestRec));
+ }
+}
diff --git a/clients/xzwrite/destlist.c b/clients/xzwrite/destlist.c
new file mode 100644
index 0000000..71771b7
--- /dev/null
+++ b/clients/xzwrite/destlist.c
@@ -0,0 +1,354 @@
+#include <stdio.h>
+#include <dyn.h>
+#include <strings.h>
+
+#include "xzwrite.h"
+
+/*
+ * The following code extracts keypressed from an X event:
+ *
+ * keyevent = event->xkey;
+ * XLookupString(&keyevent, buffer, 1, NULL, NULL);
+ */
+
+/*
+ * This entire file could easily be changes so that multiple destination
+ * lists could be used. But I don't know that that's necessary for this
+ * program.
+ */
+
+/* Globals */
+DestRec current_dest;
+
+static DynObject dests;
+extern Defaults defs;
+
+static void get_dest_from_file(), _get_default_dest();
+static int sort_dest_func();
+
+/* A function for debugging */
+void dest_print()
+{
+ char **d;
+ int i;
+
+ d = (char **) DynGet(dests, 0);
+ for (i=0; i<DynSize(dests); i++)
+ printf("%d %s\n", i, d[i]);
+}
+
+char **dest_text()
+{
+ return ((char **) DynGet(dests, 0));
+}
+
+int dest_num()
+{
+ return (DynSize(dests));
+}
+
+void dest_set_current_dest(dest)
+ Dest dest;
+{
+ bcopy((char *) dest, (char *) &current_dest, sizeof(DestRec));
+}
+
+void dest_init()
+{
+ dests = DynCreate(sizeof(char *), 0);
+ if (! dests)
+ Error("Out of memory reading destinations", NULL);
+
+ strcpy(current_dest.zclass, DEFAULT_CLASS);
+ strcpy(current_dest.zinst, DEFAULT_INST);
+ strcpy(current_dest.zrecip, get_username());
+}
+
+char **load_default_dest()
+{
+ char *get_home_dir();
+
+ if (! *get_home_dir())
+ Error("Cannot find your home directory.", NULL);
+
+ if (defs.read_xzwrite)
+ _get_default_dest(XZWRITE_DEST_FILE);
+ if (defs.read_zephyr)
+ _get_default_dest(ZEPHYR_FILE);
+ if (defs.read_anyone)
+ _get_default_dest(ANYONE_FILE);
+
+ if (DynSize(dests) == 0)
+ Error("No destinations specified", NULL);
+
+ sort_destinations();
+ return ((char **) DynGet(dests, 0));
+}
+
+static void _get_default_dest(s)
+ char *s;
+{
+ char *filename;
+
+ filename = (char *) Malloc(strlen(get_home_dir()) + strlen(s) + 1,
+ "While reading file ", s, NULL);
+ sprintf(filename, "%s%s", get_home_dir(), s);
+ get_dest_from_file(dests, filename);
+ free(filename);
+}
+
+static void get_dest_from_file(dests, f)
+ DynObject dests;
+ char *f;
+{
+ FILE *file;
+ char *line, buf[BUFSIZ];
+ DestRec dest;
+
+ if ((file = fopen(f, "r")) == NULL) {
+ Warning("Cannot find destinations file", f, NULL);
+ return;
+ }
+
+ while (bfgets(buf, 80, file)) {
+ if (buf[0] == '#' || buf[0] == '\0') {
+ if (defs.debug)
+ printf("xzwrite: skipping comment or blank line\n");
+ continue;
+ }
+
+ if (! parse_into_dest(&dest, buf)) {
+ Warning("Ignoring incorrect destination: ", buf, NULL);
+ continue;
+ }
+
+ line = (char *) Malloc(strlen(buf) + 1, "parsing file ", f, NULL);
+ strcpy(line, buf);
+ if (DynAdd(dests, (char *) &line) == DYN_NOMEM)
+ Error("Out of memory parsing file ", f, NULL);
+ }
+
+ fclose(file);
+}
+
+char **dest_add(dest)
+ Dest dest;
+{
+ char *buf;
+
+ /* Two extra bytes if instance or recipient are "" */
+ buf = (char *) Malloc(strlen(dest->zclass) + strlen(dest->zinst) +
+ strlen(dest->zrecip) + 5,
+ "while adding destination ", NULL);
+ sprintf(buf, "%s,%s,%s", dest->zclass,
+ *dest->zinst ? dest->zinst : "*",
+ *dest->zrecip ? dest->zrecip : "*");
+
+ if (DynAdd(dests, &buf) == DYN_NOMEM) {
+ Warning("Out of memory adding destination ", buf, ". Skipping.",
+ NULL);
+ free(buf);
+ }
+
+ sort_destinations();
+ return ((char **) DynGet(dests, 0));
+}
+
+/* XXX The return/output semantics of this function are not good */
+char **dest_add_string(s)
+ char *s;
+{
+ DestRec dest;
+
+ if (! parse_into_dest(&dest, s))
+ return NULL;
+
+ if (DynAdd(dests, &s) == DYN_NOMEM)
+ Warning("Out of memory adding destination ", s, ". Skipping.",
+ NULL);
+
+ sort_destinations();
+ return ((char **) DynGet(dests, 0));
+}
+
+char **dest_delete_string(s)
+ char *s;
+{
+ int i;
+ char **d;
+
+ d = (char **) DynGet(dests, 0);
+ for (i=0; i<DynSize(dests); i++) {
+ if (! strcmp(s, d[i])) {
+ DynDelete(dests, i);
+ break;
+ }
+ }
+
+ return ((char **) DynGet(dests, 0));
+}
+
+char **delete_dest_index(i)
+ int i;
+{
+ int ret;
+
+ ret = DynDelete(dests, i);
+ if (ret != DYN_OK)
+ return NULL;
+
+ return ((char **) DynGet(dests, 0));
+}
+
+
+static int sort_dest_func(c1, c2)
+ char **c1, **c2;
+{
+ char *s1, *s2, *i1, *i2;
+
+ /* A string with a , in it is always less than one without */
+ s1 = *c1; s2 = *c2;
+ i1 = index(s1, ',');
+ i2 = index(s2, ',');
+ if (i1 == NULL && i2 != NULL)
+ return 1;
+ else if (i1 != NULL && i2 == NULL)
+ return -1;
+ else
+ return strcmp(s1, s2);
+}
+
+char **sort_destinations()
+{
+ char **d;
+
+ d = (char **) DynGet(dests, 0);
+ qsort(d, DynSize(dests), sizeof(char *), sort_dest_func);
+ return d;
+}
+
+/* Fills in dest from s */
+#define distance(a,b) ((int) b - (int) a)
+int parse_into_dest(dest, s)
+ Dest dest;
+ char *s;
+{
+ char *a, *b;
+ int x, y;
+
+ /* Check for just recipient */
+ if ((a=index(s, ','))==0) {
+ if (strlen(s) > ZLEN)
+ return 0;
+ strcpy(dest->zclass, DEFAULT_CLASS);
+ strcpy(dest->zinst, DEFAULT_INST);
+ strcpy(dest->zrecip, s);
+ }
+
+ /* Check for just class,instance or instace,recipient */
+ else if ((b=index((++a), ','))==0) {
+ if (defs.class_inst) {
+ x = distance(s, a-1);
+ if (x >= ZLEN)
+ return 0;
+
+ strncpy(dest->zclass, s, x);
+ dest->zclass[x] = '\0';
+ strcpy(dest->zinst, a);
+ strcpy(dest->zrecip, "*"); }
+ else {
+ x = distance(s, a-1);
+ if (x >= ZLEN)
+ return 0;
+
+ strcpy(dest->zclass, DEFAULT_CLASS);
+ strncpy(dest->zinst, s, x);
+ dest->zinst[x] = '\0';
+ strcpy(dest->zrecip, a); }
+ }
+
+ /* Otherwise, deal with class,instance,recipent */
+ else {
+ ++b;
+ x = distance(s, a-1);
+ y = distance(a, b-1);
+ if (x >= ZLEN || y >= ZLEN)
+ return 0;
+
+ strncpy(dest->zclass, s, x);
+ dest->zclass[x] = '\0';
+ strncpy(dest->zinst, a, y);
+ dest->zinst[y] = '\0';
+ strcpy(dest->zrecip, b);
+ }
+ if (!strcmp(dest->zrecip,"*")) *(dest->zrecip) = '\0';
+ if (!strcmp(dest->zinst,"*")) *(dest->zinst) = '\0';
+
+ return 1;
+}
+#undef distance
+
+/*
+ * notice is from <MESSAGE,inst,sender>. If inst is "PERSONAL", add
+ * destination string "<sender>" if
+ * 1) MESSAGE,PERSONAL,<sender> is not in list, and
+ * 2) <sender> is not in list.
+ * If inst is not "PERSONAL", add destination string
+ * "<MESSAGE,<inst>,<sender>>" if it is not in the list.
+ */
+void dest_add_reply(notice)
+ ZNotice_t *notice;
+{
+ Dest dest;
+ char **list, *newdest, buf[ZLEN*3+2];
+ int i, num;
+
+ list = dest_text();
+ num = dest_num();
+
+
+ /* A hack so local-realm is less annoying */
+ {
+ char *r;
+
+ r = index(notice->z_sender, '@');
+ if (r && ! strcmp(r+1, ZGetRealm()))
+ *r = '\0';
+ }
+
+ if (! strcasecmp(notice->z_class_inst, DEFAULT_INST)) {
+ sprintf(buf, "message,personal,%s", notice->z_sender);
+ for (i=0; i < num; i++) {
+ if (! strcasecmp(list[i], buf) ||
+ ! strcasecmp(list[i], notice->z_sender))
+ return;
+ }
+
+ newdest = (char *) Malloc(strlen(notice->z_sender) + 1,
+ "while adding reply destination", NULL);
+ sprintf(newdest, "%s", notice->z_sender);
+ }
+ else {
+ sprintf(buf, "message,%s,%s", notice->z_class_inst,
+ notice->z_sender);
+ for (i=0; i < num; i++) {
+ if (! strcasecmp(list[i], buf))
+ return;
+ }
+
+ newdest = (char *) Malloc(strlen(notice->z_class) +
+ strlen(notice->z_class_inst) +
+ strlen(notice->z_sender) + 3,
+ "while adding reply destintion",
+ NULL);
+ sprintf(newdest, "%s,%s,%s", notice->z_class,
+ notice->z_class_inst, notice->z_sender);
+ }
+
+ dest_add_string(newdest);
+ display_dest();
+
+ if (defs.track_logins)
+ zeph_subto_logins(&notice->z_sender, 1);
+}
+
diff --git a/clients/xzwrite/edit_window.c b/clients/xzwrite/edit_window.c
new file mode 100644
index 0000000..6d23058
--- /dev/null
+++ b/clients/xzwrite/edit_window.c
@@ -0,0 +1,123 @@
+#include <X11/Intrinsic.h>
+#include <X11/StringDefs.h>
+#include <X11/Xaw/Paned.h>
+#include <X11/Xaw/Label.h>
+#include <X11/Xaw/Form.h>
+#include <X11/Xaw/Command.h>
+#include <X11/Xaw/AsciiText.h>
+
+#include "xzwrite.h"
+
+extern Widget toplevel, editor, editTitle;
+extern Defaults defs;
+extern DestRec current_dest;
+
+void edit_win_init()
+{
+ edit_set_title(&current_dest);
+}
+
+void send_message()
+{
+ char *buf;
+ int ret;
+
+ /* I should do more interesting things with these error conditions */
+
+ XtVaGetValues(editor,
+ XtNstring, (XtArgVal) &buf,
+ NULL);
+
+ ret = zeph_send_message(&current_dest, buf);
+ XawAsciiSourceFreeString(editor);
+
+ switch (ret) {
+ case SEND_OK:
+ break;
+ case SENDFAIL_SEND:
+ case SENDFAIL_RECV:
+ case SENDFAIL_ACK:
+ if (defs.verbose)
+ XBell(XtDisplay(toplevel), 0);
+ break;
+ }
+
+ /* Only the second argument matters */
+ if (defs.close_on_send)
+ XtCallActionProc(toplevel, "CloseSend", NULL, NULL, 0);
+}
+
+void edit_set_title(dest)
+ Dest dest;
+{
+ char *title;
+
+ /* alloc two extra bytes for * in case zinst or zrecip are "" */
+ title = (char *) Malloc( strlen(dest->zclass) + strlen(dest->zinst) +
+ strlen(dest->zrecip) + 20, "while setting title",
+ NULL);
+ sprintf(title, "Sending to <%s, %s, %s>", dest->zclass,
+ *dest->zinst ? dest->zinst : "*",
+ *dest->zrecip ? dest->zrecip : "*");
+
+ XtVaSetValues(editTitle,
+ XtNlabel, title,
+ NULL);
+
+ free(title);
+}
+
+void edit_clear()
+{
+ XtVaSetValues(editor,
+ XtNstring, "",
+ NULL);
+}
+
+void edit_yank_prev()
+{
+ Yank yank;
+
+ yank = yank_prev();
+ if (! yank)
+ return;
+
+ XtVaSetValues(editor,
+ XtNstring, (XtArgVal) yank->msg,
+ NULL);
+ if (defs.yank_dest) {
+ dest_set_current_dest(&yank->dest);
+ edit_set_title(&yank->dest);
+ }
+}
+
+void edit_yank_next()
+{
+ Yank yank;
+
+ yank = yank_next();
+ if (! yank)
+ return;
+
+ XtVaSetValues(editor,
+ XtNstring, (XtArgVal) yank->msg,
+ NULL);
+ if (defs.yank_dest) {
+ dest_set_current_dest(&yank->dest);
+ edit_set_title(&yank->dest);
+ }
+}
+
+void edit_yank_store()
+{
+ char *buf;
+
+ XtVaGetValues(editor,
+ XtNstring, (XtArgVal) &buf,
+ NULL);
+
+ if (buf != NULL && *buf != NULL)
+ yank_store(&current_dest, buf);
+
+ XawAsciiSourceFreeString(editor);
+}
diff --git a/clients/xzwrite/gethomedir.c b/clients/xzwrite/gethomedir.c
new file mode 100644
index 0000000..5c3d372
--- /dev/null
+++ b/clients/xzwrite/gethomedir.c
@@ -0,0 +1,15 @@
+#include <stdio.h>
+#include <pwd.h>
+
+char *get_home_dir()
+{
+ struct passwd *pwuid;
+ static char *h = NULL;
+
+ if (h) return h;
+
+ if (h = (char *) getenv("HOME")) return h;
+
+ pwuid = getpwuid(getuid());
+ return (pwuid->pw_dir);
+}
diff --git a/clients/xzwrite/interface.c b/clients/xzwrite/interface.c
new file mode 100644
index 0000000..dff18d1
--- /dev/null
+++ b/clients/xzwrite/interface.c
@@ -0,0 +1,369 @@
+/*
+ * The xzwrite interface structure. All top level widgets except toplevel
+ * are popup shells.
+ *
+ * toplevel - the top level shell
+ * icon - the top level "Z" icon
+ *
+ * sendWindow - the popup shell for the editor/destlist
+ * sendForm - the form holding the edit tree and dest tree
+ * sendClose - button to close sendWindow
+ *
+ * editPane - the pane holding editor widgets
+ * editTitle - the label holding the zephyr triple
+ * editForm - the box holding editor command buttons
+ * editSend - button to send message
+ * editClear - button to clear editor
+ * editPrev - button to yank previous
+ * editNext - button to yank next
+ * editor - the text editor
+ *
+ * destForm - the form holding the destinations list/button
+ * destScroll - the scrollbar holding the list
+ * destList - the destination list
+ *
+ * menuWindow - the popup shell for the menu
+ * menuForm - the form holding the menu list/button
+ * menuClose - the Close Window button for the dest list
+ * signature - button to change signature
+ * closeOnSend
+ * pings
+ * verbose
+ * authentic
+ * yankDest
+ * addGlobals
+ * classInst
+ * exitProgram
+ *
+ * getStringWindow - the popup shell for dialog boxes (GetString.c)
+ * getStringForm - the form containing the dialog widgets
+ * getStringTitle - the title label width
+ * getStringEdit - the text editor
+ * getStringAccept - the accept button
+ * getStringCancel - the cancel button
+ */
+
+#include <X11/Intrinsic.h>
+#include <X11/StringDefs.h>
+#include <X11/Shell.h>
+#include <X11/Xaw/Label.h>
+#include <X11/Xaw/Command.h>
+#include <X11/Xaw/Paned.h>
+#include <X11/Xaw/List.h>
+#include <X11/Xaw/Box.h>
+#include <X11/Xaw/Form.h>
+#include <X11/Xaw/AsciiText.h>
+#include <X11/Xaw/Toggle.h>
+#include <X11/Xaw/Viewport.h>
+
+#include <zephyr/zephyr.h> /* for ZGetFD() */
+
+#include "xzwrite.h"
+#include "GetString.h"
+
+#define XVCMW XtVaCreateManagedWidget
+
+/* Action Procedure declarations */
+static void Quit(), SendMessage(), OpenSend(), CloseSend(),
+ ClearEditor(), YankPrev(), YankNext(), YankStore(), AlignParagraph(),
+ DeleteDest(), HighlightDest(), SelectDest(), OpenMenu(),
+ ToggleOption(), Signature(), CloseMenu(), CreateDest();
+
+static XtActionsRec actionTable[] = {
+ /* sendWindow actions */
+ {"OpenSend", (XtActionProc) OpenSend},
+ {"CloseSend", (XtActionProc) CloseSend},
+
+ /* Editor actions */
+ {"Quit", (XtActionProc) Quit},
+ {"SendMessage", (XtActionProc) SendMessage},
+ {"ClearEditor", (XtActionProc) ClearEditor},
+ {"YankStore", (XtActionProc) YankStore},
+ {"YankPrev", (XtActionProc) YankPrev},
+ {"YankNext", (XtActionProc) YankNext},
+ {"AlignParagraph", (XtActionProc) AlignParagraph},
+
+ /* Destination list actions */
+ {"SelectDest", (XtActionProc) SelectDest},
+ {"DeleteDest", (XtActionProc) DeleteDest},
+ {"CreateDest", (XtActionProc) CreateDest},
+ {"HighlightDest", (XtActionProc) HighlightDest},
+
+ /* Menu actions */
+ {"OpenMenu", (XtActionProc) OpenMenu},
+ {"ToggleOption", (XtActionProc) ToggleOption},
+ {"Signature", (XtActionProc) Signature},
+ {"CloseMenu", (XtActionProc) CloseMenu},
+};
+
+extern unsigned int num_options, num_resources;
+extern String fallback_resources[];
+extern XrmOptionDescRec app_options[];
+extern XtResource app_resources[];
+
+XtAppContext app_con;
+Defaults defs;
+
+/* Widgets */
+Widget toplevel, icon, getString;
+Widget sendWindow, sendForm, sendClose;
+Widget destForm, destScroll, destList;
+Widget editPane, editTitle, editForm, editSend, editClear,
+ editPrev, editNext, editor;
+Widget menuWindow, menuForm, menuClose, signature,
+ closeOnSend, pings, verbose, authentic, yankDest, addGlobals,
+ classInst, commandMask, exitProgram;
+
+void go()
+{
+ XtAppMainLoop(app_con);
+}
+
+void build_interface(argc, argv)
+ int *argc;
+ char **argv;
+{
+ /* Set XFILESEARCHPATH to find xzwrite's resource file */
+ /* XXX This is gross XXX */
+ {
+ char *path1, *path2;
+
+ path1 = (char *) getenv("XFILESEARCHPATH");
+ if (! path1) path1 = "";
+ path2 = (char *) malloc(strlen(path1) +
+ strlen(XZWRITE_SEARCH_PATHS) + 2);
+ sprintf(path2, "%s:%s", path1, XZWRITE_SEARCH_PATHS);
+ setenv("XFILESEARCHPATH", path2, 1);
+ free(path2);
+ }
+
+ toplevel = XtVaAppInitialize(&app_con, "XZwrite", app_options,
+ num_options, (Cardinal *) argc, argv,
+ fallback_resources, NULL);
+
+ XtVaGetApplicationResources(toplevel, (XtPointer) &defs, app_resources,
+ num_resources, NULL);
+
+ XtAppAddActions(app_con, actionTable, XtNumber(actionTable));
+
+ /* Create the icon */
+ icon = XtVaCreateManagedWidget("icon", commandWidgetClass, toplevel,
+ NULL);
+
+ /* Create the menu */
+ menuWindow = XtVaCreatePopupShell("menuWindow", transientShellWidgetClass,
+ toplevel, NULL);
+ menuForm = XVCMW("menuForm", formWidgetClass, menuWindow, NULL);
+ menuClose = XVCMW("menuClose", commandWidgetClass, menuForm, NULL);
+ signature = XVCMW("signature", commandWidgetClass, menuForm, NULL);
+ closeOnSend = XVCMW("closeOnSend", toggleWidgetClass, menuForm, NULL);
+ pings = XVCMW("pings", toggleWidgetClass, menuForm, NULL);
+ verbose = XVCMW("verbose", toggleWidgetClass, menuForm, NULL);
+ authentic = XVCMW("authentic", toggleWidgetClass, menuForm, NULL);
+ yankDest = XVCMW("yankDest", toggleWidgetClass, menuForm, NULL);
+ addGlobals = XVCMW("addGlobals", toggleWidgetClass, menuForm, NULL);
+ classInst = XVCMW("classInst", toggleWidgetClass, menuForm, NULL);
+ exitProgram = XVCMW("exitProgram", commandWidgetClass, menuForm, NULL);
+
+ /* Create the editor/destination list */
+ sendWindow = XtVaCreatePopupShell("sendWindow", transientShellWidgetClass,
+ toplevel, NULL);
+ sendForm = XVCMW("sendForm", formWidgetClass, sendWindow, NULL);
+ sendClose = XVCMW("sendClose", commandWidgetClass, sendForm, NULL);
+
+ editPane = XVCMW("editPane", panedWidgetClass, sendForm, NULL);
+ editTitle = XVCMW("editTitle", labelWidgetClass, editPane, NULL);
+ editForm = XVCMW("editForm", formWidgetClass, editPane, NULL);
+ editSend = XVCMW("editSend", commandWidgetClass, editForm, NULL);
+ editClear = XVCMW("editClear", commandWidgetClass, editForm, NULL);
+ editPrev = XVCMW("editPrev", commandWidgetClass, editForm, NULL);
+ editNext = XVCMW("editNext", commandWidgetClass, editForm, NULL);
+ editor = XVCMW("editor", asciiTextWidgetClass, editPane, NULL);
+
+ destForm = XVCMW("destForm", formWidgetClass, sendForm, NULL);
+ destScroll = XVCMW("destScroll", viewportWidgetClass, destForm, NULL);
+ destList = XVCMW("destList", listWidgetClass, destScroll, NULL);
+
+ XtSetKeyboardFocus(sendForm, editor);
+ getString = InitGetString(toplevel, "getStringWindow");
+
+ XtAppAddInput(app_con, ZGetFD(), XtInputReadMask, zeph_dispatch, NULL);
+
+ if (defs.track_logins) {
+ XtAppAddWorkProc(app_con, login_scan_work, NULL);
+ }
+
+ XtRealizeWidget(toplevel);
+}
+
+/* Action Procedures */
+
+/* ARGSUSED */
+static void Quit(w, e, p, n)
+ Widget w;
+ XEvent *e;
+ String *p;
+ Cardinal *n;
+{
+ XtDestroyApplicationContext(app_con);
+ ZCancelSubscriptions();
+ exit(0);
+}
+
+/* ARGSUSED */
+static void OpenSend(w, e, p, n)
+ Widget w;
+ XEvent *e;
+ String *p;
+ Cardinal *n;
+{
+ XtPopup(sendWindow, XtGrabNone);
+}
+
+/* ARGSUSED */
+static void CloseSend(w, e, p, n)
+ Widget w;
+ XEvent *e;
+ String *p;
+ Cardinal *n;
+{
+ XtPopdown(sendWindow);
+}
+
+/* ARGSUSED */
+static void SendMessage(w, e, p, n)
+ Widget w;
+ XEvent *e;
+ String *p;
+ Cardinal *n;
+{
+ send_message();
+}
+
+/* ARGSUSED */
+static void ClearEditor(w, e, p, n)
+ Widget w;
+ XEvent *e;
+ String *p;
+ Cardinal *n;
+{
+ edit_clear();
+}
+
+/* ARGSUSED */
+static void YankStore(w, e, p, n)
+ Widget w;
+ XEvent *e;
+ String *p;
+ Cardinal *n;
+{
+ edit_yank_store();
+}
+
+/* ARGSUSED */
+static void YankPrev(w, e, p, n)
+ Widget w;
+ XEvent *e;
+ String *p;
+ Cardinal *n;
+{
+ edit_yank_prev();
+}
+
+/* ARGSUSED */
+static void YankNext(w, e, p, n)
+ Widget w;
+ XEvent *e;
+ String *p;
+ Cardinal *n;
+{
+ edit_yank_next();
+}
+
+/* ARGSUSED */
+static void AlignParagraph(w, e, p, n)
+ Widget w;
+ XEvent *e;
+ String *p;
+ Cardinal *n;
+{
+}
+
+/* ARGSUSED */
+static void SelectDest(w, e, p, n)
+ Widget w;
+ XEvent *e;
+ String *p;
+ Cardinal *n;
+{
+ select_dest();
+}
+
+/* ARGSUSED */
+static void DeleteDest(w, e, p, n)
+ Widget w;
+ XEvent *e;
+ String *p;
+ Cardinal *n;
+{
+ delete_dest();
+}
+
+/* ARGSUSED */
+static void HighlightDest(w, e, p, n)
+ Widget w;
+ XEvent *e;
+ String *p;
+ Cardinal *n;
+{
+}
+
+/* ARGSUSED */
+static void CreateDest(w, e, p, n)
+ Widget w;
+ XEvent *e;
+ String *p;
+ Cardinal *n;
+{
+ create_dest();
+}
+
+/* ARGSUSED */
+static void OpenMenu(w, e, p, n)
+ Widget w;
+ XEvent *e;
+ String *p;
+ Cardinal *n;
+{
+ XtPopup(menuWindow, XtGrabNone);
+}
+
+/* ARGSUSED */
+static void ToggleOption(w, e, p, n)
+ Widget w;
+ XEvent *e;
+ String *p;
+ Cardinal *n;
+{
+ menu_toggle(w);
+}
+
+/* ARGSUSED */
+static void Signature(w, e, p, n)
+ Widget w;
+ XEvent *e;
+ String *p;
+ Cardinal *n;
+{
+ menu_signature();
+}
+
+/* ARGSUSED */
+static void CloseMenu(w, e, p, n)
+ Widget w;
+ XEvent *e;
+ String *p;
+ Cardinal *n;
+{
+ XtPopdown(menuWindow);
+}
diff --git a/clients/xzwrite/logins.c b/clients/xzwrite/logins.c
new file mode 100644
index 0000000..362d4a6
--- /dev/null
+++ b/clients/xzwrite/logins.c
@@ -0,0 +1,98 @@
+#include <X11/Intrinsic.h> /* for Boolean */
+#include <zephyr/zephyr.h>
+#include <dyn.h>
+
+#include "xzwrite.h"
+
+extern Defaults defs;
+
+#define distance(a,b) ((int) b - (int) a)
+void logins_deal(notice)
+ ZNotice_t *notice;
+{
+ char *newdest;
+ int d;
+
+ d = distance(notice->z_class_inst, index(notice->z_class_inst, '@'));
+ newdest = (char *) Malloc(d+1, "while dealing with login/logout notice",
+ NULL);
+ strncpy(newdest, notice->z_class_inst, d);
+ newdest[d] = '\0';
+
+ if (! strcmp(notice->z_opcode, "USER_LOGIN")) {
+ dest_add_string(newdest);
+ display_dest();
+ }
+ else if (! strcmp(notice->z_opcode, "USER_LOGOUT")) {
+ dest_delete_string(newdest);
+ display_dest();
+ }
+ else {
+ Warning("Invalid login/logout notice. Opcode: ",
+ notice->z_opcode, "\n", NULL);
+ free(newdest);
+ }
+}
+#undef distance
+
+/* Considers a destination with a , and without a . in to be a username */
+void logins_subscribe()
+{
+ DestRec dest;
+ DynObject users;
+ char **list;
+ int num;
+
+ users = DynCreate(sizeof(char *), 0);
+ if (! users)
+ Error("Out of memory subscribing to logins", NULL);
+
+ list = dest_text();
+ num = dest_num();
+ while (--num) {
+ parse_into_dest(&dest, list[num]);
+ if (*dest.zrecip)
+ if (DynAdd(users, list + num) != DYN_OK)
+ Error("Out of memory subscribing to logins", NULL);
+ }
+
+ zeph_subto_logins((char **) DynGet(users, 0), DynSize(users));
+
+ DynDestroy(users);
+}
+
+/* ARGSUSED */
+Boolean login_scan_work(client_data)
+ caddr_t client_data;
+{
+ static int i, num, first = 1;
+ static DestRec dest = {"MESSAGE", "PERSONAL", ""};
+ static char **text;
+
+ if (first) {
+ text = dest_text();
+ num = dest_num();
+ i = first = 0;
+ }
+
+ if (i >= num)
+ return True;
+
+ if (index(text[i], ',') || index(text[i], '.')) {
+ i += 1;
+ return False; }
+
+ strcpy(dest.zrecip, text[i]);
+ if ((defs.pong_scan && zeph_pong(&dest) != SEND_OK) ||
+ (! defs.pong_scan && ! zeph_locateable(text[i]))) {
+ dest_delete_string(text[i]);
+ i -= 1;
+ num -= 1;
+ display_dest();
+ }
+
+ i += 1;
+
+ return False;
+}
+
diff --git a/clients/xzwrite/menu_window.c b/clients/xzwrite/menu_window.c
new file mode 100644
index 0000000..02f41c4
--- /dev/null
+++ b/clients/xzwrite/menu_window.c
@@ -0,0 +1,66 @@
+#include <stdio.h>
+#include <X11/Intrinsic.h>
+#include <X11/StringDefs.h>
+
+#include <X11/Xaw/Toggle.h>
+
+#include "xzwrite.h"
+#include "GetString.h"
+
+extern Widget getString, closeOnSend, pings, verbose, authentic, yankDest,
+ addGlobals, classInst;
+extern Defaults defs;
+
+#define toggle(v) (v = !v)
+void menu_toggle(w)
+ Widget w;
+{
+ if (w == closeOnSend)
+ toggle(defs.close_on_send);
+ else if (w == pings)
+ toggle(defs.ping);
+ else if (w == verbose)
+ toggle(defs.verbose);
+ else if (w == authentic)
+ toggle(defs.auth);
+ else if (w == yankDest)
+ toggle(defs.yank_dest);
+ else if (w == addGlobals)
+ toggle(defs.add_globals);
+ else if (w == classInst)
+ toggle(defs.class_inst);
+ else
+ Warning("Unknown toggle widget, ignoring.", NULL);
+}
+#undef toggle
+
+#define set(w, i) XtVaSetValues(w, XtNstate, i ? True : False, NULL)
+void menu_match_defs()
+{
+ set(closeOnSend, defs.close_on_send);
+ set(pings, defs.ping);
+ set(verbose, defs.verbose);
+ set(authentic, defs.auth);
+ set(yankDest, defs.yank_dest);
+ set(addGlobals, defs.add_globals);
+ set(classInst, defs.class_inst);
+}
+#undef set
+
+void menu_signature()
+{
+ char buf[BUFSIZ];
+ int ret;
+
+ ret = GetString(getString, "Enter new signature:", defs.signature,
+ 0, buf, BUFSIZ);
+
+ if (ret != GETSTRING_ACCEPT)
+ return;
+
+ /* XXX Is this safe? */
+ free(defs.signature);
+ defs.signature = (char *) Malloc(strlen(buf) + 1,
+ "while setting signature", NULL);
+ strcpy(defs.signature, buf);
+}
diff --git a/clients/xzwrite/nmalloc.c b/clients/xzwrite/nmalloc.c
new file mode 100644
index 0000000..42994cd
--- /dev/null
+++ b/clients/xzwrite/nmalloc.c
@@ -0,0 +1,996 @@
+#define scribblecheck
+#define public
+#define rcheck
+#define NBUCKETS 30
+
+#include <stdio.h>
+#ifdef MALLOC_DEBUG
+#define register
+#endif MALLOC_DEBUG
+malloc_warning(s)
+ char *s;
+{
+ printf("Malloc warning: %s\n",s);
+}
+
+botch(s)
+ char *s;
+{
+ printf("Botch: <%s> dumping core...\n",s);
+ fflush(stdout);
+ fflush(stderr);
+ abort();
+}
+
+/* Copyright (C) 1985 Richard M. Stallman,
+ based mostly on the public domain work of others.
+
+This program is distributed in the hope that it will be useful,
+but without any warranty. No author or distributor
+accepts responsibility to anyone for the consequences of using it
+or for whether it serves any particular purpose or works at all,
+unless he says so in writing.
+
+ Permission is granted to anyone to distribute verbatim copies
+ of this program's source code as received, in any medium, provided that
+ the copyright notice, the nonwarraty notice above
+ and this permission notice are preserved,
+ and that the distributor grants the recipient all rights
+ for further redistribution as permitted by this notice,
+ and informs him of these rights.
+
+ Permission is granted to distribute modified versions of this
+ program's source code, or of portions of it, under the above
+ conditions, plus the conditions that all changed files carry
+ prominent notices stating who last changed them and that the
+ derived material, including anything packaged together with it and
+ conceptually functioning as a modification of it rather than an
+ application of it, is in its entirety subject to a permission
+ notice identical to this one.
+
+ Permission is granted to distribute this program (verbatim or
+ as modified) in compiled or executable form, provided verbatim
+ redistribution is permitted as stated above for source code, and
+ A. it is accompanied by the corresponding machine-readable
+ source code, under the above conditions, or
+ B. it is accompanied by a written offer, with no time limit,
+ to distribute the corresponding machine-readable source code,
+ under the above conditions, to any one, in return for reimbursement
+ of the cost of distribution. Verbatim redistribution of the
+ written offer must be permitted. Or,
+ C. it is distributed by someone who received only the
+ compiled or executable form, and is accompanied by a copy of the
+ written offer of source code which he received along with it.
+
+ Permission is granted to distribute this program (verbatim or as modified)
+ in executable form as part of a larger system provided that the source
+ code for this program, including any modifications used,
+ is also distributed or offered as stated in the preceding paragraph.
+
+In other words, you are welcome to use, share and improve this program.
+You are forbidden to forbid anyone else to use, share and improve
+what you give them. Help stamp out software-hoarding! */
+
+/****************************************************************
+ * *
+ * Helpful historical comments *
+ * *
+ ****************************************************************/
+
+/*
+ * @(#)nmalloc.c 1 (Caltech) 2/21/82
+ *
+ * U of M Modified: 20 Jun 1983 ACT: strange hacks for Emacs
+ *
+ * Nov 1983, Mike@BRL, Added support for 4.1C/4.2 BSD.
+ *
+ * This is a very fast storage allocator. It allocates blocks of a small
+ * number of different sizes, and keeps free lists of each size. Blocks
+ * that don't exactly fit are passed up to the next larger size. In this
+ * implementation, the available sizes are (2^n)-4 (or -16) bytes long.
+ * This is designed for use in a program that uses vast quantities of
+ * memory, but bombs when it runs out. To make it a little better, it
+ * warns the user when he starts to get near the end.
+ *
+ * June 84, ACT: modified rcheck code to check the range given to malloc,
+ * rather than the range determined by the 2-power used.
+ *
+ * Jan 85, RMS: calls malloc_warning to issue warning on nearly full.
+ * No longer Emacs-specific; can serve as all-purpose malloc for GNU.
+ * You should call malloc_init to reinitialize after loading dumped Emacs.
+ * Call malloc_stats to get info on memory stats if MSTATS turned on.
+ * realloc knows how to return same block given, just changing its size,
+ * if the power of 2 is correct.
+ *
+ * Jan 86, WDC: Removed Emacs specific stuff, and neatened a few comments.
+ *
+ * March 86 WDC: Added in code by Eichin for Scribble checking of blocks
+ * Scribble check writes a known pattern into the free blocks, checks it
+ * to see if it is still undamaged before allocating it. It writes a
+ * different pattern into the space beyond the end of an allocated block,
+ * and tests it for damage when expanding the block's bounds in realloc.
+ * Note, this check takes *TIME* and should not be compiled in by default.
+ *
+ * Aug 18 1986, MWE: added 'public' stuff to allow makedecl to generate
+ * a malloc.h file.
+ *
+ * Berkeley UNIX 4.3 has a storage allocator that shares a common
+ * ancestor with this one. It handles realloc compatibly with the
+ * archaic use of realloc on an already freed block to "compact"
+ * storage. It uses a pagesize system call rather than assuming the
+ * page size is 1024 bytes. Finally it guarantees that a freed block
+ * is not munged by the allocator itself, incase someone wants to fiddle
+ * with freed space after freeing it but before allocating more.
+ *
+ * This particular storage allocator would benefit from having a
+ * non-hardwired pagesize. But because of the scribble check it would
+ * not be useful to keep the free pointer in the header. SO: When you
+ * free something allocated with this allocator, DONT TRY TO USE IT.
+ * It is GUARANTEED to be damaged by the freeing process.
+ *
+ * For interfacing to systems that want to be able to ask the size of
+ * the allocated block, rather than remembering it, the m_blocksize
+ * function, rips open the block and tells you how big it is. The size
+ * returned is nbytes, the number of bytes asked for, NOT the actual
+ * amount of space in the block.
+ *
+ * This malloc also has a horrible but very useful hack which can be
+ * enabled by defined MALLOC_DEBUG. This causes malloc to keep track
+ * of who requested a particular block of memory to be malloc'ed or
+ * free'ed. A dump function (which is usually called from a signal
+ * handler) will then print out what all the blocks of memory and who
+ * last malloc'ed or free'ed it. It accomplishes this feat by opening
+ * the binary and groveling over the symbol table. Thus, this is not
+ * guaranteed to work across all systems. It will work on BSD 4.[23]
+ * systems, however.
+ */
+
+/****************************************************************
+ * *
+ * Includes, declarations, and definitions *
+ * *
+ ****************************************************************/
+#include "nmalloc.h"
+#ifndef public
+#define New(TYPE) ((TYPE *)malloc(sizeof(TYPE)))
+#endif public
+/* Determine which kind of system this is. */
+#include <signal.h>
+#ifndef SIGTSTP
+#define USG
+#else /* SIGTSTP */
+#ifdef SIGIO
+#define BSD42
+#endif /* SIGIO */
+#endif /* SIGTSTP */
+
+#ifndef BSD42
+#ifndef USG
+#include <sys/vlimit.h> /* warn the user when near the end */
+#endif
+#else /* if BSD42 */
+#include <sys/time.h>
+#include <sys/resource.h>
+#endif /* BSD42 */
+
+#ifdef scribblecheck
+#define rcheck
+#endif /* we need to have range data to use block boundary checking */
+
+#ifdef rcheck
+/*
+ * To implement range checking, we write magic values in at the
+ * beginning and end of each allocated block, and make sure they
+ * are undisturbed whenever a free or a realloc occurs.
+ */
+
+/* Written in each of the 4 bytes following the block's real space */
+#define MAGIC1 0x55
+#define MAGICFREE 0x69 /* 0110 1001 Magic value for Free blocks */
+
+/* Written in the 4 bytes before the block's real space */
+#define MAGIC4 0x55555555
+#define MAGICFREE4 0x69696969
+
+#define ASSERT(p) if (!(p)) botch("p"); else
+#define EXTRA 4 /* 4 bytes extra for MAGIC1s */
+#else
+#define ASSERT(p)
+#define EXTRA 0
+#endif /* rcheck */
+
+#define ISALLOC ((char) 0xf7) /* magic byte that implies allocation */
+#define ISFREE ((char) 0x54) /* magic byte that implies free block */
+ /* this is for error checking only */
+
+/* If range checking is not turned on, all we have is a flag
+ * indicating whether memory is allocated, an index in nextf[],
+ * and a field that tells how many bytes.
+ * To realloc() memory we copy nbytes.
+ * 16 bits of header space is unused.
+ */
+struct mhead {
+ char mh_alloc; /* ISALLOC or ISFREE */
+ char mh_index; /* index in nextf[] */
+ unsigned short mh_extra;/* Currently wasted 16 bits */
+/* Remainder are valid only when block is allocated */
+ unsigned mh_nbytes; /* number of bytes allocated */
+#ifdef MALLOC_DEBUG
+ char *mh_parent; /* set to caller of malloc/realloc */
+ struct mhead *mh_link; /* set to next block or NULL */
+#endif MALLOC_DEBUG
+#ifdef rcheck
+ int mh_magic4; /* should be == MAGIC4 */
+#endif /* rcheck */
+};
+
+#ifdef MALLOC_DEBUG
+struct mhead *baselink=NULL;
+struct mhead *endlink=NULL;
+#endif MALLOC_DEBUG
+
+/*
+ * Access free-list pointer of a block.
+ * It is stored at block + 4.
+ * This is not a field in the mhead structure because we want
+ * sizeof (struct mhead) to describe the overhead for when the
+ * block is in use, and we do not want the free-list pointer
+ * to count in that.
+ */
+#define CHAIN(a) \
+ (*(struct mhead **) (sizeof (char *) + (char *) (a)))
+
+
+/****************************************************************
+ * *
+ * Variable Creations *
+ * *
+ ****************************************************************/
+
+extern char etext;
+extern char *start_of_data (); /* This seems necessary for USG */
+
+/* These two are for user programs to look at, when they are interested. */
+
+int malloc_sbrk_used; /* amount of data space used now */
+int malloc_sbrk_unused; /* amount more we can have */
+
+/* start of data space; can be changed by calling init_malloc */
+static char *data_space_start = 0;
+
+#ifdef MSTATS
+/*
+ * nmalloc[i] is the difference between the number of mallocs and frees
+ * for a given block size.
+ */
+static int nmalloc[NBUCKETS];
+static int nmal, nfre;
+#endif /* MSTATS */
+
+/*
+ * nextf[i] is the pointer to the next free block of size 2^(i+3). The
+ * smallest allocatable block is 8 bytes. The overhead information will
+ * go in the first int of the block, and the returned pointer will point
+ * to the second.
+ */
+static struct mhead *nextf[NBUCKETS];
+
+/* Number of bytes of writable memory we can expect to be able to get */
+static int lim_data = 0;
+/* Level number of warnings already issued.
+ * 0 -- no warnings issued.
+ * 1 -- 75% warning already issued.
+ * 2 -- 85% warning already issued.
+ */
+static int warnlevel = 0;
+
+/* nonzero once initial bunch of free blocks made */
+static int gotpool;
+
+/****************************************************************
+ * *
+ * Start of procedures *
+ * *
+ * malloc_init, m_blocksize *
+ * *
+ ****************************************************************/
+
+/*
+ * Cause reinitialization based on job parameters;
+ * also declare where the end of pure storage is.
+ */
+public malloc_init (start)
+ char *start;
+{
+ data_space_start = start;
+ lim_data = 0;
+ warnlevel = 0;
+#ifdef MALLOC_DEBUG
+ baselink = NULL;
+ endlink = NULL;
+#endif MALLOC_DEBUG
+}
+
+public int m_blocksize(a_block)
+ register char *a_block;
+{
+ if (a_block == (char *)0) return 0;
+ return(((struct mhead *)a_block-1)->mh_nbytes);
+}
+
+/****************************************************************
+ * *
+ * morecore - Ask the system for more memory *
+ * *
+ ****************************************************************/
+
+static
+morecore (nu) /* ask system for more memory */
+ register int nu; /* size index to get more of */
+{
+ char *sbrk ();
+ register char *cp;
+ register int nblks;
+ register int siz;
+
+ if (!data_space_start)
+ {
+#if defined(USG)
+ data_space_start = start_of_data ();
+#else /* not USG */
+ data_space_start = &etext;
+#endif /* not USG */
+ }
+
+ if (lim_data == 0)
+ get_lim_data ();
+
+ /* On initial startup, get two blocks of each size up to 1k bytes */
+ if (!gotpool)
+ getpool (), getpool (), gotpool = 1;
+
+ /* Find current end of memory and issue warning if getting near max */
+
+ cp = sbrk (0);
+ siz = cp - data_space_start;
+ malloc_sbrk_used = siz;
+ malloc_sbrk_unused = lim_data - siz;
+
+ switch (warnlevel)
+ {
+ case 0:
+ if (siz > (lim_data / 4) * 3)
+ {
+ warnlevel++;
+ malloc_warning ("Warning: past 75% of memory limit");
+ }
+ break;
+ case 1:
+ if (siz > (lim_data / 20) * 17)
+ {
+ warnlevel++;
+ malloc_warning ("Warning: past 85% of memory limit");
+ }
+ break;
+ case 2:
+ if (siz > (lim_data / 20) * 19)
+ {
+ warnlevel++;
+ malloc_warning ("Warning: past 95% of memory limit");
+ }
+ break;
+ }
+
+ if ((int) cp & 0x3ff) /* land on 1K boundaries */
+ sbrk (1024 - ((int) cp & 0x3ff));
+
+ /* Take at least 2k, and figure out how many blocks of the desired size
+ we're about to get */
+ nblks = 1;
+ if ((siz = nu) < 8)
+ nblks = 1 << ((siz = 8) - nu);
+
+ if ((cp = sbrk (1 << (siz + 3))) == (char *) -1)
+ return; /* no more room! */
+ if ((int) cp & 7)
+ { /* shouldn't happen, but just in case */
+ cp = (char *) (((int) cp + 8) & ~7);
+ nblks--;
+ }
+
+ /* save new header and link the nblks blocks together */
+ nextf[nu] = (struct mhead *) cp;
+ siz = 1 << (nu + 3);
+ while (1)
+ {
+ ((struct mhead *) cp) -> mh_alloc = ISFREE;
+ ((struct mhead *) cp) -> mh_index = nu;
+#ifdef MALLOC_DEBUG
+ ((struct mhead *) cp) -> mh_parent = NULL;
+ ((struct mhead *) cp) -> mh_link = NULL;
+#endif MALLOC_DEBUG
+#ifdef rcheck
+ ((struct mhead *) cp) -> mh_magic4 = MAGICFREE4;
+#endif /* rcheck */
+#ifdef scribblecheck
+ {
+ /* Check that upper stuff was still MAGIC1 */
+ register char *m = (char *)((struct mhead *)cp+1);
+ register char *en = (8<<nu) + cp;
+ /* Fill whole block with MAGICFREE */
+ while (m<en) *m++ = MAGICFREE;
+ }
+#endif /* scribblecheck */
+
+ /* Clear newly allocated blocks, to match free ones */
+ if (--nblks <= 0) break;
+ CHAIN ((struct mhead *) cp) = (struct mhead *) (cp + siz);
+ cp += siz;
+ }
+ CHAIN ((struct mhead *) cp) = 0;
+}
+
+/****************************************************************
+ * *
+ * getpool - Get initial pools of small blocks *
+ * *
+ ****************************************************************/
+
+static
+getpool ()
+{
+ register int nu;
+ register char *cp = sbrk (0);
+
+ if ((int) cp & 0x3ff) /* land on 1K boundaries */
+ sbrk (1024 - ((int) cp & 0x3ff));
+
+ /* Get 2k of storage */
+
+ cp = sbrk (04000);
+ if (cp == (char *) -1)
+ return;
+
+ /* Divide it into an initial 8-word block
+ plus one block of size 2**nu for nu = 3 ... 10. */
+
+ CHAIN (cp) = nextf[0];
+ nextf[0] = (struct mhead *) cp;
+ ((struct mhead *) cp) -> mh_alloc = ISFREE;
+ ((struct mhead *) cp) -> mh_index = 0;
+#ifdef MALLOC_DEBUG
+ ((struct mhead *) cp) -> mh_parent = NULL;
+ ((struct mhead *) cp) -> mh_link = NULL;
+#endif MALLOC_DEBUG
+#ifdef rcheck
+ ((struct mhead *) cp) -> mh_magic4 = MAGICFREE4;
+#endif /* rcheck */
+ cp += 8;
+
+ for (nu = 0; nu < 7; nu++)
+ {
+ CHAIN (cp) = nextf[nu];
+ nextf[nu] = (struct mhead *) cp;
+ ((struct mhead *) cp) -> mh_alloc = ISFREE;
+ ((struct mhead *) cp) -> mh_index = nu;
+#ifdef rcheck
+ ((struct mhead *) cp) -> mh_magic4 = MAGICFREE4;
+#endif /* rcheck */
+#ifdef scribblecheck
+ {
+ register char *m = (char *)((struct mhead *)cp+1);
+ register char *en = (8<<nu) + cp;
+ /* Fill whole block with MAGICFREE */
+ while (m<en) *m++ = MAGICFREE;
+ }
+#endif /* scribblecheck */
+ cp += 8 << nu;
+ }
+}
+
+/****************************************************************
+ * *
+ * malloc - get a block of space from a pool *
+ * *
+ ****************************************************************/
+
+public char *
+malloc (n) /* get a block */
+ unsigned n;
+{
+ register struct mhead *p;
+ register unsigned int nbytes;
+ register int nunits = 0;
+
+#ifdef MALLOC_DEBUG
+ char *PAPA = *(char **)(&n-2);
+#endif MALLOC_DEBUG
+
+ /* Figure out how many bytes are required, rounding up to the nearest
+ multiple of 4, then figure out which nextf[] area to use */
+ nbytes = (n + sizeof *p + EXTRA + 3) & ~3;
+ {
+ register unsigned int shiftr = (nbytes - 1) >> 2;
+
+ while (shiftr >>= 1)
+ nunits++;
+ }
+
+ /* If there are no blocks of the appropriate size, go get some */
+ /* COULD SPLIT UP A LARGER BLOCK HERE ... ACT */
+ if (nextf[nunits] == 0)
+ morecore (nunits);
+
+ /* Get one block off the list, and set the new list head */
+ if ((p = nextf[nunits]) == 0)
+ return 0;
+ nextf[nunits] = CHAIN (p);
+
+ /* Check for free block clobbered */
+ /* If not for this check, we would gobble a clobbered free chain ptr */
+ /* and bomb out on the NEXT allocate of this size block */
+ if (p -> mh_alloc != ISFREE || p -> mh_index != nunits)
+ botch ("block on free list clobbered");
+#ifdef rcheck
+ if (p -> mh_magic4 != MAGICFREE4)
+ botch ("Magic in block on free list clobbered");
+#endif /* rcheck */
+#ifdef scribblecheck
+ /* Check for block filled with magic numbers, then change to zeros */
+ {
+ register char *m = (char *) (p + 1);
+ register char *en = (8<<p->mh_index) + (char *) p;
+ register int block_valid = 0;
+ while(m<en && (block_valid=(*m==MAGICFREE)))
+ *m++=(char)0;
+ /* so, status comes out as 1 if ok, 0 if terminated */
+ if (!block_valid) botch ("data on free list damaged");
+ }
+#endif /* scribblecheck */
+ /* Fill in the info, and if range checking, set up the magic numbers */
+ p -> mh_alloc = ISALLOC;
+ p -> mh_nbytes = n;
+#ifdef MALLOC_DEBUG
+ p -> mh_parent = PAPA;
+ if(!baselink)
+ {
+ baselink = p;
+ endlink = p;
+ }
+ else if (p -> mh_link == NULL && endlink != p)
+ {
+ endlink -> mh_link = p;
+ endlink = p;
+ }
+#endif MALLOC_DEBUG
+#ifdef rcheck
+ p -> mh_magic4 = MAGIC4;
+ {
+ register char *m = (char *) (p + 1) + n;
+#ifdef scribblecheck
+ register char *en = (8<<p->mh_index)+(char *)p;
+ /* point to end of block */
+ while (m<en) *m++ = MAGIC1;
+#else /* scribblecheck */
+ *m++ = MAGIC1, *m++ = MAGIC1, *m++ = MAGIC1, *m = MAGIC1;
+#endif /* scribblecheck */
+ }
+#endif /* not rcheck */
+#ifdef MSTATS
+ nmalloc[nunits]++;
+ nmal++;
+#endif /* MSTATS */
+ return (char *) (p + 1);
+}
+
+/****************************************************************
+ * *
+ * free - Free a block of space *
+ * *
+ ****************************************************************/
+
+public free (mem)
+ char *mem;
+{
+ register struct mhead *p;
+#ifdef MALLOC_DEBUG
+ char *PAPA = *(char **)((&mem)-2);
+#endif MALLOC_DEBUG
+ {
+ register char *ap = mem;
+
+ ASSERT (ap != 0);
+ p = (struct mhead *) ap - 1;
+ ASSERT (p -> mh_alloc == ISALLOC);
+#ifdef rcheck
+ ASSERT (p -> mh_magic4 == MAGIC4);
+ ap += p -> mh_nbytes;
+ p->mh_magic4 = MAGICFREE4;
+ ASSERT (*ap++ == MAGIC1); ASSERT (*ap++ == MAGIC1);
+ ASSERT (*ap++ == MAGIC1); ASSERT (*ap == MAGIC1);
+#endif /* rcheck */
+ }
+ {
+ register int nunits = p -> mh_index;
+
+ ASSERT (nunits < NBUCKETS);
+#ifdef scribblecheck
+ {
+ /* Check that upper stuff was still MAGIC1 */
+ register char *m = (char *) (p + 1) + p->mh_nbytes;
+ register char *en = (8<<p->mh_index) + (char *) p;
+ register int block_valid = 0;
+ while(m<en && (block_valid=(*m++==MAGIC1)));
+ if (!block_valid) botch ("block freed with data out of bounds");
+ /* Fill whole block with MAGICFREE */
+ m = (char *) (p + 1);
+ while (m<en) *m++ = MAGICFREE;
+ }
+#endif /* scribblecheck */
+ p -> mh_alloc = ISFREE;
+ CHAIN (p) = nextf[nunits];
+ nextf[nunits] = p;
+#ifdef MSTATS
+ nmalloc[nunits]--;
+ nfre++;
+#endif /* MSTATS */
+ }
+#ifdef MALLOC_DEBUG
+ p -> mh_parent = PAPA; /* since it IS freed... */
+#endif MALLOC_DEBUG
+}
+
+/****************************************************************
+ * *
+ * realloc - resize a block, copy if necessary *
+ * *
+ ****************************************************************/
+
+public char *
+realloc (mem, n)
+ char *mem;
+ register unsigned n;
+{
+ register struct mhead *p;
+ register unsigned int tocopy;
+ register int nbytes;
+ register int nunits;
+#ifdef MALLOC_DEBUG
+ char *PAPA = *(char **)((&mem)-2);
+#endif MALLOC_DEBUG
+
+ if ((p = (struct mhead *) mem) == 0)
+ return malloc (n); /* well, shouldn't realloc(NULL) anyway... */
+ p--;
+#ifdef MALLOC_DEBUG
+ p -> mh_parent = PAPA;
+#endif MALLOC_DEBUG
+ nunits = p -> mh_index;
+ ASSERT (p -> mh_alloc == ISALLOC);
+ tocopy = p -> mh_nbytes;
+#ifdef rcheck
+ ASSERT (p -> mh_magic4 == MAGIC4);
+ {
+ register char *m = mem + tocopy;
+#ifdef scribblecheck
+ register char *en = (8<<p->mh_index) + (char *)p;
+ register int block_valid = 0;
+ while(m<en && (block_valid=(*m++==MAGIC1)));
+ if (!block_valid) botch ("out of bounds data on realloc");
+#else /* scribblecheck */
+ ASSERT (*m++ == MAGIC1); ASSERT (*m++ == MAGIC1);
+ ASSERT (*m++ == MAGIC1); ASSERT (*m == MAGIC1);
+#endif /* scribblecheck */
+ }
+#endif /* not rcheck */
+
+ /* See if desired size rounds to same power of 2 as actual size. */
+ nbytes = (n + sizeof *p + EXTRA + 7) & ~7;
+
+ /* If ok, use the same block, just marking its size as changed. */
+ if (nbytes > (4 << nunits) && nbytes <= (8 << nunits))
+ {
+ /* Here we check on realloc if we are grabbing unused space */
+#ifdef rcheck
+ register char *m = mem + tocopy;
+#ifdef scribblecheck
+ register char *en = (8<<p->mh_index) + (char *) p;
+ while (m<en) *m++=(char)0;
+#else /* scribblecheck */
+ *m++ = 0; *m++ = 0; *m++ = 0; *m++ = 0;
+#endif /* scribblecheck */
+ m = mem + n;
+#ifdef scribblecheck
+ while(m<en) *m++ = MAGIC1;
+#else /* scribblecheck */
+ *m++ = MAGIC1; *m++ = MAGIC1; *m++ = MAGIC1; *m++ = MAGIC1;
+#endif /* scribblecheck */
+#endif /* not rcheck */
+ p-> mh_nbytes = n;
+ return mem;
+ }
+
+ if (n < tocopy)
+ tocopy = n;
+ {
+ register char *new;
+
+ if ((new = malloc (n)) == 0)
+ return 0;
+ bcopy (mem, new, tocopy);
+ free (mem);
+ return new;
+ }
+}
+
+/****************************************************************
+ * *
+ * Memory Statistics stuff *
+ * *
+ ****************************************************************/
+
+#ifdef MSTATS
+/* Return statistics describing allocation of blocks of size 2**n. */
+
+#ifndef public
+struct mstats_value
+ {
+ int blocksize;
+ int nfree;
+ int nused;
+ };
+#endif public
+
+public struct mstats_value
+malloc_stats (size)
+ int size;
+{
+ struct mstats_value v;
+ register int i;
+ register struct mhead *p;
+
+ v.nfree = 0;
+
+ if (size < 0 || size >= NBUCKETS) {
+ v.blocksize = 0;
+ v.nused = 0;
+ return v;
+ }
+
+ v.blocksize = 1 << (size + 3);
+ v.nused = nmalloc[size];
+
+ for (p = nextf[size]; p; p = CHAIN (p))
+ v.nfree++;
+
+ return v;
+}
+
+/*
+ * showall - print out statistics about malloc
+ *
+ * Prints two lines of numbers, one showing the length of the free list
+ * for each size category, the second showing the number of mallocs -
+ * frees for each size category.
+ */
+showall(v)
+ char **v;
+{
+ register int i, j;
+ register struct mhead *p;
+ int totfree = 0,
+ totused = 0;
+
+ printf("tcsh memory allocation statistics\nfree:\t");
+ for (i = 0; i < NBUCKETS; i++) {
+ j=0;
+ for (p = nextf[i]; p; p = CHAIN(p))
+ j++;
+ printf(" %4d", j);
+ totfree += j * (1 << (i + 3));
+ }
+ printf("\nused:\t");
+ for (i = 0; i < NBUCKETS; i++) {
+ printf(" %4d", nmalloc[i]);
+ totused += nmalloc[i] * (1 << (i + 3));
+ }
+ printf("\n\tTotal in use: %d, total free: %d\n",
+ totused, totfree);
+}
+#endif
+
+/****************************************************************
+ * *
+ * Stuff having to do with determining memory limits *
+ * *
+ ****************************************************************/
+
+/*
+ * This function returns the total number of bytes that the process
+ * will be allowed to allocate via the sbrk(2) system call. On
+ * BSD systems this is the total space allocatable to stack and
+ * data. On USG systems this is the data space only.
+ */
+
+#ifdef USG
+
+public get_lim_data ()
+{
+ extern long ulimit ();
+
+ lim_data = ulimit (3, 0);
+ lim_data -= (long) data_space_start;
+}
+
+#else /* not USG */
+#ifndef BSD42
+
+public get_lim_data ()
+{
+ lim_data = vlimit (LIM_DATA, -1);
+}
+
+#else /* BSD42 */
+
+public get_lim_data ()
+{
+ struct rlimit XXrlimit;
+
+ getrlimit (RLIMIT_DATA, &XXrlimit);
+ lim_data = XXrlimit.rlim_cur; /* soft limit */
+}
+
+#endif /* BSD42 */
+#endif /* not USG */
+#ifdef MALLOC_DEBUG
+/*
+ Here we have the interesting dump routines to describe the inner
+ state of malloc.
+ */
+
+#include <a.out.h>
+#include <stab.h>
+#include <sys/file.h>
+
+/*
+ * malloc_dump_all --- displays what routines malloced what and when.
+ *
+ * This is a gross kludge. However, it's wonderful for finding memory
+ * leaks and other malloc problems.
+ */
+malloc_dump_all(file)
+ char *file;
+{
+ int fd;
+ struct mhead *p;
+ struct exec base;
+ struct nlist *nls;
+ int numsyms;
+ char *strtab;
+ int strtablen;
+ int stat;
+
+ printf("Dumping state:\n");
+
+ fd = open(file, O_RDONLY, 0777); /* open the binary */
+ if(fd<0)
+ {
+ perror("malloc_dump_all:open");
+ return;
+ }
+
+ stat = lseek(fd, 0L, L_SET); /* seek (redundant... for later) */
+ if(stat < 0)
+ {
+ perror("malloc_dump_all:initseek");
+ return;
+ }
+
+ stat = read(fd, &base, sizeof(base)); /* read in base block */
+ if(stat <= 0)
+ {
+ perror("malloc_dump_all:read");
+ return;
+ }
+
+ stat = lseek(fd, N_SYMOFF(base), L_SET); /* seek to symtab */
+ if(stat < 0)
+ {
+ perror("malloc_dump_all:symseek");
+ return;
+ }
+ nls = (struct nlist *)malloc(base.a_syms);
+ numsyms = base.a_syms / sizeof(struct nlist);
+
+ stat = read(fd, nls, base.a_syms); /* read in symtab */
+ if(stat <= 0)
+ {
+ perror("malloc_dump_all:symread");
+ return;
+ }
+
+ stat = lseek(fd, N_STROFF(base), L_SET); /* seek to string table */
+ if(stat < 0)
+ {
+ perror("malloc_dump_all:seekstrtab");
+ return;
+ }
+
+ stat = read(fd, &strtablen, sizeof(strtablen)); /* read stringtable len */
+ if(stat <= 0)
+ {
+ perror("malloc_dump_all:readstrtablen");
+ return;
+ }
+ strtab = malloc(strtablen);
+
+ stat = read(fd, strtab, strtablen); /* read in stringtable */
+ if(stat <= 0)
+ {
+ perror("malloc_dump_all:readstrtab");
+ return;
+ }
+
+ for(p = baselink; p != endlink; p = p -> mh_link)
+ {
+ find_sym(nls, numsyms, strtab, p -> mh_parent);
+ switch (p -> mh_alloc) {
+ case ISALLOC:
+ printf(" allocated %d bytes\n",p->mh_nbytes);
+ break;
+ case ISFREE:
+ printf(" freed block\n");
+ break;
+ default:
+ printf(" invalid block (%d)\n", p-> mh_nbytes);
+ break;
+ }
+ }
+ flush(stdout);
+ free(strtab);
+ free(nls);
+ printf("Done dumping state.\n");
+}
+
+find_sym(nls, numsyms, strs, par)
+ struct nlist *nls;
+ int numsyms;
+ char *strs;
+ char *par;
+{
+ int sym, stat;
+ int last;
+ int tfile=0, tf0=0;
+ char *x;
+
+ x = par;
+
+ sym = -1;
+ while(++sym < numsyms)
+ {
+ switch(nls[sym].n_type)
+ {
+ case N_TEXT:
+ case N_SO:
+ tf0 = tfile;
+ tfile = nls[sym].n_un.n_strx;
+ case N_SLINE:
+ if(((char *)nls[sym].n_value) > x)
+ {
+ sym = numsyms;
+ }
+ else
+ {
+ last = nls[sym].n_desc;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+
+ printf("\"%s\", line %d: Address[0x%x]", strs+tf0-sizeof(int), last, x);
+
+}
+#endif MALLOC_DEBUG
diff --git a/clients/xzwrite/nmalloc.h b/clients/xzwrite/nmalloc.h
new file mode 100644
index 0000000..57144c7
--- /dev/null
+++ b/clients/xzwrite/nmalloc.h
@@ -0,0 +1,20 @@
+#ifndef _malloc_
+#define _malloc_
+
+#define New(TYPE) ((TYPE *)malloc(sizeof(TYPE)))
+malloc_init ( /* start */ );
+int m_blocksize( /* a_block */ );
+char * malloc ( /* n */ ); /* get a block */
+free ( /* mem */ );
+char * realloc ( /* mem, n */ );
+struct mstats_value
+ {
+ int blocksize;
+ int nfree;
+ int nused;
+ };
+struct mstats_value malloc_stats ( /* size */ );
+get_lim_data ( /* */ );
+get_lim_data ( /* */ );
+get_lim_data ( /* */ );
+#endif _malloc_
diff --git a/clients/xzwrite/resource.c b/clients/xzwrite/resource.c
new file mode 100644
index 0000000..29103e9
--- /dev/null
+++ b/clients/xzwrite/resource.c
@@ -0,0 +1,112 @@
+#include <X11/Intrinsic.h>
+#include <X11/StringDefs.h>
+
+#include "xzwrite.h"
+
+String fallback_resources[] = {
+ "*icon.label: Cannot find xzwrite resource file. Click to exit.",
+ "*icon.translations: #override \n <BtnDown>: Set() \n <BtnUp>: Quit()",
+ NULL,
+};
+
+XrmOptionDescRec app_options[] = {
+ {"+d","*auth", XrmoptionNoArg, (caddr_t) "true"},
+ {"-d","*auth", XrmoptionNoArg, (caddr_t) "false"},
+ {"-s","*signature", XrmoptionSepArg, (caddr_t) NULL},
+ {"+v","*verbose", XrmoptionNoArg, (caddr_t) "true"},
+ {"-v","*verbose", XrmoptionNoArg, (caddr_t) "false"},
+ {"-close","*closeOnSend", XrmoptionNoArg, (caddr_t) "false"},
+ {"+close","*closeOnSend", XrmoptionNoArg, (caddr_t) "true"},
+ {"+n","*ping", XrmoptionNoArg, (caddr_t) "true"},
+ {"-n","*ping", XrmoptionNoArg, (caddr_t) "false"},
+ {"+yd","*yankDest", XrmoptionNoArg, (caddr_t) "true"},
+ {"-yd","*yankDest", XrmoptionNoArg, (caddr_t) "false"},
+ {"+av","*addVars", XrmoptionNoArg, (caddr_t) "true"},
+ {"-av","*addVars", XrmoptionNoArg, (caddr_t) "false"},
+ {"+ci","*classInst", XrmoptionNoArg, (caddr_t) "true"},
+ {"-ci","*classInst", XrmoptionNoArg, (caddr_t) "false"},
+ {"-my","*maxYanks", XrmoptionSepArg, 0},
+ {"+l","*trackLogins", XrmoptionNoArg, (caddr_t) "true"},
+ {"-l","*trackLogins", XrmoptionNoArg, (caddr_t) "false"},
+ {"+x","*readXzwrite", XrmoptionNoArg, (caddr_t) "true"},
+ {"+z","*readZephyr", XrmoptionNoArg, (caddr_t) "true"},
+ {"+a","*readAnyone", XrmoptionNoArg, (caddr_t) "true"},
+ {"-x","*readXzwrite", XrmoptionNoArg, (caddr_t) "false"},
+ {"-z","*readZephyr", XrmoptionNoArg, (caddr_t) "false"},
+ {"-a","*readAnyone", XrmoptionNoArg, (caddr_t) "false"},
+ {"+pac", "*popupAtCursor", XrmoptionNoArg, (caddr_t) "true"},
+ {"-pac", "*popupAtCursor", XrmoptionNoArg, (caddr_t) "false"},
+ {"-mask", "*commandMask", XrmoptionSepArg, (caddr_t) 0},
+ {"-debug", "*debug", XrmoptionNoArg, (caddr_t) "true"},
+ {"-opcode", "*opcode", XrmoptionSepArg, (caddr_t) ""},
+ {"+pong", "*pongScan", XrmoptionNoArg, (caddr_t) "true"},
+ {"-pong", "*pongScan", XrmoptionNoArg, (caddr_t) "false"},
+ {"+reply", "*autoReply", XrmoptionNoArg, (caddr_t) "true"},
+ {"-reply", "*autoReply", XrmoptionNoArg, (caddr_t) "false"},
+};
+
+#define offset(field) XtOffset(Defaults *, field)
+XtResource app_resources[] = {
+ {"auth", "Auth", XtRBoolean, sizeof(Boolean),
+ offset(auth), XtRString, "true"},
+
+ {"yankDest", "YankDest", XtRBoolean, sizeof(Boolean),
+ offset(yank_dest), XtRString, "false"},
+
+ {"addGlobals", "AddGlobals", XtRBoolean, sizeof(Boolean),
+ offset(add_globals), XtRString, "false"},
+
+ {"signature", "Signature", XtRString, sizeof(String),
+ offset(signature), XtRString, ""},
+
+ {"verbose", "Verbose", XtRBoolean, sizeof(Boolean),
+ offset(verbose), XtRString, "false"},
+
+ {"closeOnSend", "Close", XtRBoolean, sizeof(Boolean),
+ offset(close_on_send), XtRString, "false"},
+
+ {"ping", "Ping", XtRBoolean, sizeof(Boolean),
+ offset(ping), XtRString, "true"},
+
+ {"classInst", "ClassInst", XtRBoolean, sizeof(Boolean),
+ offset(class_inst), XtRString, "true"},
+
+ {"maxYanks", "MaxYanks", XtRInt, sizeof(int),
+ offset(max_yanks), XtRString, "25"},
+
+ {"trackLogins", "TrackLogins", XtRBoolean, sizeof(Boolean),
+ offset(track_logins), XtRString, "false"},
+
+ {"readZephyr", "ReadFile", XtRBoolean, sizeof(Boolean),
+ offset(read_zephyr), XtRString, "false"},
+
+ {"readAnyone", "ReadFile", XtRBoolean, sizeof(Boolean),
+ offset(read_anyone), XtRString, "false"},
+
+ {"readXzwrite", "ReadFile", XtRBoolean, sizeof(Boolean),
+ offset(read_xzwrite), XtRString, "false"},
+
+ {"popupAtCursor", "PopupAtCursor", XtRBoolean, sizeof(Boolean),
+ offset(popup_cursor), XtRString, "false"},
+
+ {"commandMask", "CommandMask", XtRInt, sizeof(int),
+ offset(command_mask), XtRString, "0"},
+
+ {"debug", "Debug", XtRBoolean, sizeof(Boolean),
+ offset(debug), XtRString, "false"},
+
+ {"opcode", "Opcode", XtRString, sizeof(String),
+ offset(opcode), XtRString, ""},
+
+ {"pongScan", "PongScan", XtRBoolean, sizeof(Boolean),
+ offset(pong_scan), XtRString, "true"},
+
+ {"autoReply", "AutoReply", XtRBoolean, sizeof(Boolean),
+ offset(auto_reply), XtRString, "false"},
+};
+#undef offset
+
+/* These are necessary because XtNumber uses sizeof, and these arrays
+ * are declared as extern in interface.c */
+unsigned int num_options = XtNumber(app_options);
+unsigned int num_resources = XtNumber(app_resources);
diff --git a/clients/xzwrite/util.c b/clients/xzwrite/util.c
new file mode 100644
index 0000000..7648ef4
--- /dev/null
+++ b/clients/xzwrite/util.c
@@ -0,0 +1,76 @@
+#include <stdio.h>
+#include <varargs.h>
+#include <pwd.h>
+
+#include "xzwrite.h"
+
+/*VARARGS*/
+void Warning(first, va_alist)
+ char *first;
+ va_dcl
+{
+ va_list vp;
+ char *s;
+
+ fputs(first, stderr);
+
+ va_start(vp);
+ while ((s = va_arg(vp, char *)) != NULL)
+ fputs(s, stderr);
+ va_end(vp);
+}
+
+/*VARARGS*/
+void Error(first, va_alist)
+ char *first;
+ va_dcl
+{
+ va_list vp;
+ char *s;
+
+ fputs(first, stderr);
+
+ va_start(vp);
+ while ((s = va_arg(vp, char *)) != NULL)
+ fputs(s, stderr);
+ va_end(vp);
+
+ exit(1);
+}
+
+/*VARARGS*/
+char *Malloc(n, va_alist)
+ int n;
+ va_dcl
+{
+ va_list vp;
+ char *ptr, *s;
+
+ ptr = (char *) malloc((unsigned) n);
+ if (ptr)
+ return ptr;
+
+ fputs("Out of memory: ", stderr);
+
+ va_start(vp);
+ while ((s = va_arg(vp, char *)) != NULL)
+ fputs(s, stderr);
+ va_end(vp);
+
+ exit(1);
+}
+
+char *get_username()
+{
+ struct passwd *pwuid;
+ static char *u = NULL;
+
+ if (u) return u;
+
+ if (u = (char *) getenv("USER")) return u;
+
+ pwuid = getpwuid(getuid());
+ u = pwuid->pw_name;
+
+ return (u);
+}
diff --git a/clients/xzwrite/xzwrite-proto.h b/clients/xzwrite/xzwrite-proto.h
new file mode 100644
index 0000000..feed86b
--- /dev/null
+++ b/clients/xzwrite/xzwrite-proto.h
@@ -0,0 +1,98 @@
+#ifdef __STDC__
+# define P(s) s
+#else
+# define P(s) ()
+#endif
+
+
+/* interface.c */
+void go P((void ));
+void build_interface P((int *argc , char **argv ));
+
+/* resource.c */
+
+/* destlist.c */
+void dest_print P((void ));
+char **dest_text P((void ));
+int dest_num P((void ));
+void dest_set_current_dest P((Dest dest ));
+void dest_init P((void ));
+char **load_default_dest P((void ));
+char **dest_add P((Dest dest ));
+char **dest_add_string P((char *s ));
+char **dest_delete_string P((char *s ));
+char **delete_dest_index P((int i ));
+char **sort_destinations P((void ));
+int parse_into_dest P((Dest dest , char *s ));
+void dest_add_reply P((ZNotice_t *notice ));
+
+/* util.c */
+char *get_username P((void ));
+
+/* bfgets.c */
+char *bfgets P((char *s , int n , FILE *iop ));
+
+/* gethomedir.c */
+char *get_home_dir P((void ));
+
+/* dest_window.c */
+void dest_add_reply P((ZNotice_t *notice ));
+void display_dest P((void ));
+void delete_dest P((void ));
+void create_dest P((void ));
+void select_dest P((void ));
+
+/* xzwrite.c */
+int main P((int argc , char **argv ));
+int usage P((void ));
+
+/* edit_window.c */
+void edit_win_init P((void ));
+void send_message P((void ));
+void edit_set_title P((Dest dest ));
+void edit_clear P((void ));
+void edit_yank_prev P((void ));
+void edit_yank_next P((void ));
+void edit_yank_store P((void ));
+
+/* zephyr.c */
+void zeph_dispatch P((caddr_t client_data , int source , XtInputId *input_id ));
+void zeph_init P((void ));
+int zeph_locateable P((char *user ));
+void zeph_subto_logins P((char **users , int num ));
+void zeph_subto_replys P((void ));
+int zeph_send_message P((Dest dest , char *msg ));
+int zeph_ping P((Dest dest ));
+int zeph_pong P((Dest dest ));
+char *zeph_get_signature P((void ));
+
+/* GetString.c */
+Widget InitGetString P((Widget parent , char *name ));
+int GetString P((Widget getStringWindow , String label , String value , int pop_type , char *buf , int len ));
+
+/* Popup.c */
+void Popup P((Widget shell , XtGrabKind GrabType , int pop_type ));
+void PopupSafe P((Widget w , Dimension x , Dimension y , XtGrabKind GrabType ));
+void PopupAtPointer P((Widget w , XtGrabKind GrabType ));
+
+/* yank.c */
+void yank_init P((void ));
+Yank yank_prev P((void ));
+Yank yank_next P((void ));
+void yank_store P((Dest dest , char *msg ));
+
+/* menu_window.c */
+void menu_toggle P((Widget w ));
+void menu_match_defs P((void ));
+void menu_signature P((void ));
+
+/* logins.c */
+void logins_deal P((ZNotice_t *notice ));
+void logins_subscribe P((void ));
+Boolean login_scan_work P((caddr_t client_data ));
+
+/* xzwrite.h */
+
+/* GetString.h */
+
+#undef P
diff --git a/clients/xzwrite/xzwrite.1 b/clients/xzwrite/xzwrite.1
new file mode 100644
index 0000000..328b25d
--- /dev/null
+++ b/clients/xzwrite/xzwrite.1
@@ -0,0 +1,400 @@
+.TH XZWRITE 1 "7 February 1989"
+.SH NAME
+xzwrite \- X application to write to another user via Zephyr
+.SH SYNOPSIS
+.B xzwrite
+[ -toolkitoption ... ] [-s signature] [+d | -d] [+n | -n] [+v | -v]
+[+yd | -yd] [+av | -av] [+ci | -ci] [-my yanks] [+l | -l] [+a | -a]
+[+x | -x] [+z | -z] [+pong | -pong] [+reply | -reply]
+
+.SH DESCRIPTION
+.I Xzwrite
+is an X application that sends messages to other users
+through the
+.IR Zephyr (1)
+notification service. It puts an icon on the
+screen that allows the user to send a message, select the destination
+of a message, or exit. The program remains active until explicity
+told to exit, thus eliminating the need to run a program every time
+the user wants to send a zephyr message.
+.SH USING THE DESTINATION LIST
+.PP
+.I Xzwrite
+maintains a list of 'destinations'; that is, a list of
+<class, instance, recipient> triples that a user can send messages to.
+When a user selects a destination, all subsequent messages will be
+sent to that triple, until a new destination is selected.
+.I Xzwrite
+can get its list of destinations from the files .xzwrite.dest,
+.anyone, or .zephyr.vars in the
+user's home directory. These files must consist of a list of lines, and
+each is interpreted in the following way: a line containing no commas
+is taken to mean <MESSAGE,PERSONAL,string>; a line with one comma is
+taken to be either <class,instance,*> or <MESSAGE,instance,recipient>
+depending on the value of the classInst resource (see below); a line
+with two commas is taken to be <class,instance,recipient>. The lines
+must appear
+.B WITHOUT WHITESPACE
+between the fields and with a linefeed between each line. Blank lines
+and lines beginning with an octothorpe (#) are ignored.
+.PP
+
+Clicking the left button in the
+.I xzwrite
+icon pops up the editor and
+destination list. Clicking with the left button in the destination
+list selects the highlighted destination; clicking with the right
+button deletes the highlighed destination. Clicking the middle button
+invokes the action CreateDest (see ACTIONS below) that prompts the
+user for a new <class,instance,recipient> triple to be added to the list.
+
+.PP
+
+If the user specifies a destination in the .xzwrite.dest file
+with an instance or recipient of "...",
+.I xzwrite
+will prompt the user to enter an instance or recipient when the
+message editor is popped up. Setting both instance and recipient to
+"..." (ie: <MESSAGE,...,...>) works. The new
+<class,instance,recipient> triple formed each time a destination with
+"..." is used may or may not be added to the destination list, depending
+on the addVars resource (see below.)
+
+.PP
+
+While the mouse pointer is inside the
+.I xzwrite
+icon, the mouse buttons have the following effect:
+.TP
+.B Left button:
+Pops up the editor and destination list so the user can create and
+send messages.
+.TP
+.B Right button:
+Pops up a list of things that can be changed while xzwrite is running.
+Clicking the "Change Signature" box causes
+.I xzwrite
+to prompt for a new signature, to be used in future messages. All the
+others toggle options which are initially set by resources or
+command-line arguments. The "Quit XZWRITE" causes
+.I xzwrite
+to exit.
+.TP
+.B Ctrl-Right button:
+Exits
+.IR xzwrite .
+
+.SH USING THE MESSAGE EDITOR
+There are four buttons in the message editor. The Send button
+sends the text currently in the message editor to the current
+destination, optionally clearing the message editor at the same time.
+The Clear Editor button clears the message editor. The Yank-Prev button yanks
+the previous message, along with its destination, into the message
+editor. The Yank-Next button yanks the next message (or the first
+message in the yank buffer, if Yank-Prev has not been called) into the
+message editor. The yank buffer is circular, so old messages are
+periodically overwritten by new ones, and stores the previous (by
+default) 25 messages.
+.PP
+The following key sequences have been defined for convenience:
+.PP
+.nf
+ Ctrl-Return Send message
+ Meta-O Store current message, yank previous
+ Meta-P Yank Previous
+ Meta-N Yank Next
+
+.SH OPTIONS
+
+.I Xzwrite
+will accept all X Toolkit command-line options and
+resource database specifications, under the name 'XZwrite'; for more
+information, see
+.IR X (1).
+The instance names of the different parts of
+.I xzwrite
+are as follows (each should be preceded by XZwrite* in the
+user's .Xresources file. For examples of how to use these resource
+names, look in /mit/sipb/usr/lib/app-defaults/XZwrite.)
+
+.nf
+ toplevel - the top level shell
+ icon - the top level "Z" icon
+
+ sendWindow - the popup shell for the editor/destlist
+ sendForm - the form holding the edit tree and dest tree
+ sendClose - button to close sendWindow
+
+ editPane - the pane holding editor widgets
+ editTitle - the label holding the zephyr triple
+ editForm - the box holding editor command buttons
+ editSend - button to send message
+ editClear - button to clear editor
+ editPrev - button to yank previous
+ editNext - button to yank next
+ editor - the text editor
+
+ destForm - the form holding the destinations list/button
+ destScroll - the scrollbar holding the list
+ destList - the destination list
+
+ menuWindow - the popup shell for the menu
+ menuForm - the form holding the menu list/button
+ menuClose - the Close Window button for the dest list
+ signature - button to change signature
+ closeOnSend
+ pings
+ verbose
+ authentic
+ yankDest
+ addGlobals
+ classInst
+ exitProgram
+
+ getStringWindow - the popup shell for dialog boxes (GetString.c)
+ getStringForm - the form containing the dialog widgets
+ getStringTitle - the title label width
+ getStringEdit - the text editor
+ getStringAccept - the accept button
+ getStringCancel - the cancel button
+
+.fi
+
+.PP
+In addition,
+.I xzwrite
+will accept the following command-line options
+(or resource database specifications). Each should be preceded by
+XZwrite* in the user's .Xresources file. When a command-lie
+.TP
+.B +d (auth = true)
+.br
+.ns
+.HP 5
+.B -d (auth = false)
+.br
+When true, Zephyr messages to be sent authentic. When false, Zephyr
+messages are sent unauthentic.
+.TP
+.B +v (verbose = true)
+.br
+.ns
+.HP 5
+.B -v (verbose = false)
+.br
+When true, causes
+.I xzwrite
+to inform the user no one received a sent message by beeping. This
+is useful if the user wants to know if someone logged out between
+the time when the editor is popped up (when a PING is sent) and when
+the message is actually sent.
+.TP
+.B +z (readZephyr = true)
+.br
+.ns
+.HP 5
+.B -z (readZephyr = false)
+.br
+When true, causes
+.I xzwrite
+to include the .zephyr.subs file for its initial list of destinations.
+.TP
+.B +a (readAnyone = true)
+.br
+.ns
+.HP 5
+.B -a (readAnyone = false)
+.br
+When true, causes
+.I xzwrite
+to include the user's .anyone file for its initial list of destinations.
+.TP
+.B +x (readXzwrite = true)
+.br
+.ns
+.HP 5
+.B -x (readXzwrite = false)
+.br
+When true, causes
+.I xzwrite
+to include the user's .xzwrite.dest file for its initial list of destinations.
+.TP
+.B +l (trackLogins = true)
+.br
+.ns
+.HP 5
+.B -l (trackLogins = false)
+.br
+When true,
+.I xzwrite
+determines (at startup) if each username on the destination
+list is logged on and removes those usernames that are not. It then
+subscribes to login and logout messages for each
+username on the list, and keeps the destination list up to date with
+respect to which users are zwrite-able.
+.TP
+.B +pong (pongScan = true)
+.br
+.ns
+.HP 5
+.B -pong (pongScan = false)
+.br
+Controls the method
+.I xzwrite
+uses determine whether a certain user is logged in. If true,
+.I xzwrite
+sends a notice with an opcode of PING (and a message body of PONG) and
+awaits a response; if false,
+.I xzwrite
+performs a "zlocate". Note that this resource is only used when
+trackLogins is true.
+.TP
+.B -s (signature)
+Specifies the 'signature' for all messages sent. The signature will
+appear as the first field in every message sent.
+.I Xzwrite
+will also look in the user's .zephyr.vars file to a signature, first
+for the variable xzwrite-signature and then for the variable
+zwrite-signature.
+.TP
+.B +n (ping = true)
+.br
+.ns
+.HP 5
+.B -n (ping = false)
+.br
+When ping is set to true,
+.I xzwrite
+sends a PING to the destination when it is initially selected.
+.I Xzwrite
+uses the PING to determine if anyone will actually receive a message
+sent to that destination, and will not allow it to be selected if not.
+.TP
+.B +ci (classInst = true)
+.br
+.ns
+.HP 5
+.B -ci (classInst = false)
+.br
+When ci is set to true, a destination that contains two strings
+separated by a comma is interpreted as a class and instance, with
+a recipient of "*". When it is false, the same string is interpreted
+as an instance and recipient, with a class of MESSAGE.
+.TP
+.B +yd (yankDest = true)
+.br
+.ns
+.HP 5
+.B -yd (yankDest = false)
+.br
+When yd is set to true, yanking a previous message in the message editor
+also restores the original destination of the message. When set to false,
+only the message text is yanked, and the current destination remains
+unchanged.
+.TP
+.B +av (addVars = true)
+.br
+.ns
+.HP 5
+.B -av (addVars = false)
+.br
+When av is set to true, destinations that are specified as the result
+of a recipient or instance of "..." are added to the destinations list
+so they can be selected again.
+.TP
+.B +reply (autoReply = true)
+.br
+.ns
+.HP 5
+.B -reply (autoReply = false)
+.br
+When autoReply is set to true, xzwrite subscribes to <MESSAGE,*,%me%>
+(in other words, all messages sent directly to the user). Each time
+such a message is received, a destination that will reply to the
+sender on the same instance is added to the destination list, if it is
+not already there.
+
+.SH ACTIONS
+
+Every useful action that
+.I xzwrite
+can perform can be bound to any sequence of X events through the
+mechanism of translation tables. The following action procedures
+available to the user.
+.PP
+.nf
+ OpenSend
+ CloseSend
+ Pops up/Pops down the message editor/destination list.
+
+ SendMessage
+ Sends the message in the editor to the current destination.
+
+ ClearEditor
+ Clears the editor.
+
+ YankStore
+ Stores the contents in the message editor in the Yank buffer.
+
+ YankPrev
+ YankNext
+ Puts the previous/next item in the yank buffer into the editor,
+ optionally restoring the destination as well.
+
+ SelectDest
+ DeleteDest
+ Selects/deletes the hightlighed destination.
+
+ CreateDest
+ Prompts the user for a <class,instance,recipient> triple to
+ be added to the destinations list.
+
+ OpenMenu
+ CloseMenu
+ Pops up/Pops down the options menu.
+
+ ToggleOption
+ Toggles the option corresponding to the hightlighed item on the
+ options menu.
+
+ Signature
+ Pops up a dialog box and changes the Zephyr signature to whatever
+ is typed into it.
+
+For examples on how to use these action procedures, look in
+/usr/sipb/lib/app-defaults/XZwrite.
+
+.SH FILES
+.TP
+/usr/sipb/bitmaps.x11/z
+Default icon bitmap
+.TP
+/usr/sipb/lib/app-defaults/XZwrite
+Xzwrite program defaults
+.TP
+~/.Xresources
+user X resources database file
+.TP
+~/.xzwrite.dest
+The user's xzwrite destinations list.
+~/.anyone
+The user's .anyone file.
+~/.zephyr.subs
+The user's zephyr subscription file.
+.SH SEE ALSO
+X(1), zephyr(1)
+
+.SH BUGS
+
+.I xzwrite
+occasionally decided to ignore the state of the "Pings" and
+"Authentic" menu options, unless you happen to be running the program
+under a debugger.
+
+This man page contains many errors and omissions.
+
+.SH AUTHOR
+
+Written by Barr3y Jaspan (bjaspan@athena.mit.edu), MIT Project Athena
+and MIT Student Information Processing Board, for X11R4, the X
+Toolkit, and the Athena Widgets.
diff --git a/clients/xzwrite/xzwrite.c b/clients/xzwrite/xzwrite.c
new file mode 100644
index 0000000..51bf21d
--- /dev/null
+++ b/clients/xzwrite/xzwrite.c
@@ -0,0 +1,49 @@
+#include <stdio.h>
+
+#include "xzwrite.h"
+
+extern Defaults defs;
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ zeph_init();
+
+ build_interface(&argc, argv);
+
+ if (argc > 1) usage();
+
+ /* Do magic with signature */
+ if (! *defs.signature) {
+ char *sig;
+
+ sig = (char *) zeph_get_signature();
+ if (sig) {
+ defs.signature = (char *) Malloc(strlen(sig) + 1,
+ "getting signature",
+ NULL);
+ strcpy(defs.signature, sig);
+ }
+ }
+
+ dest_init();
+ yank_init();
+ edit_win_init();
+ menu_match_defs();
+ (void) load_default_dest();
+ display_dest();
+
+ if (defs.track_logins)
+ logins_subscribe();
+ if (defs.auto_reply)
+ zeph_subto_replies();
+
+ go();
+}
+
+usage()
+{
+ fprintf(stderr, "Usage: xzwrite [ -toolkitoption ... ] [-s signature] [+d | -d] [+n | -n]\n\t[+v | -v] [+yd | -yd] [+av | -av] [+ci | -ci] [-my yanks]\n\t[+l | -l] [+a | -a] [+x | -x] [+z | -z] [+pong | -pong] [+reply | -reply]\n");
+ exit(1);
+}
diff --git a/clients/xzwrite/xzwrite.h b/clients/xzwrite/xzwrite.h
new file mode 100644
index 0000000..0a4e295
--- /dev/null
+++ b/clients/xzwrite/xzwrite.h
@@ -0,0 +1,50 @@
+#include <stdio.h>
+#include <X11/Intrinsic.h> /* for String and Boolean */
+
+#define ZLEN 60
+#define DEFAULT_CLASS "MESSAGE"
+#define DEFAULT_INST "PERSONAL"
+#define XZWRITE_DEST_FILE "/.xzwrite.dest"
+#define ZEPHYR_FILE "/.zephyr.subs"
+#define ANYONE_FILE "/.anyone"
+
+#define SEND_OK -1000
+#define SENDFAIL_RECV -1001
+#define SENDFAIL_SEND -1002
+#define SENDFAIL_ACK -1003
+
+/* Structure to contains values from the resource database */
+typedef struct _defaults {
+ String signature, opcode;
+ Boolean auth;
+ Boolean close_on_send;
+ Boolean clear_on_send;
+ Boolean ping;
+ Boolean verbose;
+ Boolean yank_dest;
+ Boolean add_globals;
+ Boolean read_xzwrite;
+ Boolean read_zephyr;
+ Boolean read_anyone;
+ Boolean class_inst;
+ Boolean track_logins;
+ Boolean popup_cursor;
+ Boolean debug;
+ Boolean pong_scan;
+ Boolean auto_reply;
+ int max_yanks, command_mask;
+} Defaults;
+
+/* Structure to contain a legal zephyr triple */
+typedef struct _destination {
+ char zclass[ZLEN], zinst[ZLEN], zrecip[ZLEN];
+} DestRec, *Dest;
+
+/* Structure to contain a yank */
+typedef struct _yank {
+ DestRec dest;
+ char *msg;
+} YankRec, *Yank;
+
+#include <zephyr/zephyr.h>
+#include "xzwrite-proto.h"
diff --git a/clients/xzwrite/yank.c b/clients/xzwrite/yank.c
new file mode 100644
index 0000000..2aba0e8
--- /dev/null
+++ b/clients/xzwrite/yank.c
@@ -0,0 +1,58 @@
+#include "xzwrite.h"
+
+static Yank yank_buffer;
+extern Defaults defs;
+
+static int read_index, write_index, highest;
+
+void yank_init()
+{
+ yank_buffer = (Yank) Malloc(defs.max_yanks*sizeof(YankRec),
+ "while allocating yank buffer", NULL);
+ bzero((char *) yank_buffer, defs.max_yanks*sizeof(YankRec));
+
+ read_index = write_index = 0;
+ highest = -1;
+}
+
+Yank yank_prev()
+{
+ if (highest == -1)
+ return NULL;
+
+ if (--read_index < 0) read_index = highest;
+ return &yank_buffer[read_index];
+}
+
+Yank yank_next()
+{
+ if (highest == -1)
+ return NULL;
+
+ if (++read_index > highest) read_index = 0;
+ return &yank_buffer[read_index];
+}
+
+void yank_store(dest, msg)
+ Dest dest;
+ char *msg;
+{
+ yank_buffer[write_index].dest = *dest;
+ if (yank_buffer[write_index].msg)
+ free(yank_buffer[write_index].msg);
+ yank_buffer[write_index].msg = (char *) Malloc(strlen(msg) + 1,
+ "while yanking message",
+ NULL);
+ strcpy(yank_buffer[write_index].msg, msg);
+
+ /*
+ * read_index = write_index + 1 so that if I follow the store by
+ * a yank_prev I will get the message just stored (since
+ * read_index is decremented before being used). If I do a
+ * yank_next, then read_index will be > highest and reset to zero.
+ */
+ read_index = write_index + 1;
+ if (write_index > highest)
+ highest = write_index;
+ write_index = (write_index + 1) % defs.max_yanks;
+}
diff --git a/clients/xzwrite/zephyr.c b/clients/xzwrite/zephyr.c
new file mode 100644
index 0000000..88e1295
--- /dev/null
+++ b/clients/xzwrite/zephyr.c
@@ -0,0 +1,262 @@
+#include <zephyr/zephyr.h>
+
+#include "xzwrite.h"
+#include <strings.h>
+
+static int zeph_send_notice();
+extern Defaults defs;
+
+/* ARGSUSED */
+void zeph_dispatch(client_data, source, input_id)
+ caddr_t client_data;
+ int source;
+ XtInputId *input_id;
+{
+ ZNotice_t notice;
+ struct sockaddr_in from;
+ int ret;
+
+ while (ZPending() > 0) {
+ ret = ZReceiveNotice(&notice, &from);
+ if (ret != ZERR_NONE) {
+ Warning(error_message(ret), " while receiving Zephyr notice.",
+ NULL);
+ continue;
+ }
+
+ if (defs.track_logins &&
+ (! strcmp(notice.z_opcode, "USER_LOGIN") ||
+ ! strcmp(notice.z_opcode, "USER_LOGOUT")))
+ logins_deal(&notice);
+ else if (defs.auto_reply &&
+ ! strcasecmp(notice.z_class, DEFAULT_CLASS) &&
+ ! strcasecmp(notice.z_recipient, ZGetSender()))
+ dest_add_reply(&notice);
+ else {
+ Warning("Invalid notice.\n", "To: <", notice.z_class, ", ",
+ notice.z_class_inst, ", ", (*notice.z_recipient) ?
+ notice.z_recipient : "*", ">\n",
+ "From: ", notice.z_sender, "\nOpcode: ",
+ notice.z_opcode, "\nMessage: ", notice.z_message,
+ "\n", NULL);
+ }
+
+ ZFreeNotice(&notice);
+ }
+}
+
+void zeph_init()
+{
+ int retval;
+
+ retval = ZInitialize();
+ if (retval != ZERR_NONE)
+ Error("Cannot initialize the Zephyr library.", NULL);
+
+ retval = ZOpenPort((int *) 0);
+ if (retval != ZERR_NONE)
+ Error("Cannot open Zephyr port.", NULL);
+}
+
+int zeph_locateable(user)
+ char *user;
+{
+ char buf[BUFSIZ];
+ int n;
+
+ if (index(user, '@') == NULL)
+ sprintf(buf, "%s@%s", user, ZGetRealm());
+ ZLocateUser(buf, &n);
+ return (!! n);
+}
+
+/* XXX This will break on interrealm zephyr */
+void zeph_subto_logins(users, num)
+ char **users;
+ int num;
+{
+ ZSubscription_t *sublist;
+ char *name, *realm;
+ int rlen, c = 0;
+
+ realm = ZGetRealm();
+ rlen = strlen(realm);
+ sublist = (ZSubscription_t *) Malloc(num*sizeof(ZSubscription_t),
+ "while subscribing to logins", NULL);
+
+ while (c < num) {
+ sublist[c].zsub_class = "login";
+ sublist[c].zsub_recipient = "";
+ name = (char *) Malloc(strlen(users[c])+rlen+2,
+ "while subscribing to login, ", users[c],
+ NULL);
+ if (index(users[c], '@'))
+ sprintf(name, "%s", users[c]);
+ else
+ sprintf(name, "%s@%s", users[c], realm);
+ sublist[c].zsub_classinst = name;
+ c += 1;
+ }
+
+ ZSubscribeToSansDefaults(sublist, c, (unsigned short) 0);
+ for(; c; --c)
+ free(sublist[c-1].zsub_classinst);
+ free(sublist);
+}
+
+void zeph_subto_replies()
+{
+ ZSubscription_t sub;
+
+ sub.zsub_class = "message";
+ sub.zsub_classinst = "*";
+ sub.zsub_recipient = ZGetSender();
+
+ ZSubscribeToSansDefaults(&sub, 1, (unsigned short) 0);
+}
+
+int zeph_send_message(dest, msg)
+ Dest dest;
+ char *msg;
+{
+ ZNotice_t notice;
+ int msglen, siglen, ret;
+ char *sig_msg;
+
+ msglen = strlen(msg);
+ siglen = strlen(defs.signature);
+ sig_msg = (char *) Malloc(msglen + siglen + 2, "while sending message",
+ NULL);
+ sprintf(sig_msg, "%s%c%s", defs.signature, '\0', msg);
+
+ bzero((char *) &notice, sizeof(ZNotice_t));
+ notice.z_kind = ACKED;
+ notice.z_class = dest->zclass;
+ notice.z_class_inst = dest->zinst;
+ notice.z_recipient = dest->zrecip;
+ notice.z_sender = 0;
+ notice.z_opcode = defs.opcode;
+ notice.z_port = 0;
+ notice.z_message = sig_msg;
+ notice.z_message_len = msglen + siglen + 1;
+
+ /* This really gross looking mess is brought to you by zwrite.c */
+ if (defs.auth) {
+ if (*defs.signature)
+ notice.z_default_format = "Class $class, Instance $instance:\nTo: @bold($recipient)\n@bold($1) <$sender>\n\n$2";
+ else
+ notice.z_default_format = "Class $class, Instance $instance:\nTo: @bold($recipient)\n$message";
+ }
+ else {
+ if (*defs.signature)
+ notice.z_default_format = "@bold(UNAUTHENTIC) Class $class, Instance $instance:\n@bold($1) <$sender>\n\n$2";
+ else
+ notice.z_default_format = "@bold(UNAUTHENTIC) Class $class, Instance $instance:\n$message";
+ }
+
+ ret = zeph_send_notice(&notice, (defs.auth) ? ZAUTH : ZNOAUTH);
+ free(sig_msg);
+ return ret;
+}
+
+int zeph_ping(dest)
+ Dest dest;
+{
+ ZNotice_t notice;
+
+ bzero((char *) &notice, sizeof(ZNotice_t));
+ notice.z_kind = ACKED;
+ notice.z_class = dest->zclass;
+ notice.z_class_inst = dest->zinst;
+ notice.z_recipient = dest->zrecip;
+ notice.z_opcode = "PING";
+
+ /* Should a PING ever be authenticated? */
+ return (zeph_send_notice(&notice, ZNOAUTH));
+}
+
+int zeph_pong(dest)
+ Dest dest;
+{
+ ZNotice_t notice;
+
+ bzero((char *) &notice, sizeof(ZNotice_t));
+ notice.z_kind = ACKED;
+ notice.z_class = dest->zclass;
+ notice.z_class_inst = dest->zinst;
+ notice.z_recipient = dest->zrecip;
+ notice.z_opcode = "PING";
+ notice.z_message = "PONG";
+ notice.z_message_len = 4;
+
+ /* Should a PING ever be authenticated? */
+ return (zeph_send_notice(&notice, ZNOAUTH));
+}
+
+char *zeph_get_signature()
+{
+ char *sig;
+
+ sig = ZGetVariable("xzwrite-signature");
+ if (! sig) sig = ZGetVariable("zwrite-signature");
+ return sig;
+}
+
+static int zeph_send_notice(notice, auth)
+ ZNotice_t *notice;
+ int (*auth)();
+{
+ int retval;
+ ZNotice_t retnotice;
+
+ /* Send message with appropriate authentication */
+ retval = ZSendNotice(notice, auth);
+ if (retval != ZERR_NONE) {
+ if (defs.debug)
+ Warning(error_message(retval), " while sending message.", NULL);
+ return SENDFAIL_SEND;
+ }
+
+ /* Wait for server acknowledgement */
+ retval = ZIfNotice(&retnotice, (struct sockaddr_in *) 0,
+ ZCompareUIDPred, (char *) &notice->z_uid);
+
+ if (retval != ZERR_NONE) {
+ if (defs.debug)
+ Warning(error_message(retval),
+ " while waiting for acknowledgement.", NULL);
+ return SENDFAIL_ACK;
+ }
+
+ /* Make sure someone receives it */
+ if (strcmp(retnotice.z_message, ZSRVACK_NOTSENT)==0)
+ return SENDFAIL_RECV;
+
+ return SEND_OK;
+}
+
+/* debugging function */
+int zeph_display_subscriptions()
+{
+ ZSubscription_t sub;
+ int n, retval, i = 1;
+
+ retval = ZRetrieveSubscriptions((unsigned short) 0, &n);
+ if (retval != ZERR_NONE) {
+ Warning(error_message(retval), " while retrieving subscriptions.",
+ NULL);
+ return;
+ }
+
+ printf("Retrieving %d subscriptions.\n", n);
+
+ while (ZGetSubscriptions(&sub, &i) == ZERR_NONE) {
+ if (i != 1)
+ Warning("Subscriptions skipped while printing.", NULL);
+
+ printf("<%s,%s,%s>\n", sub.class, (*sub.classinst) ?
+ sub.classinst : "**", (*sub.recipient) ?
+ sub.recipient : "**");
+ }
+}
+