diff options
author | Kenneth G Raeburn <raeburn@mit.edu> | 1990-11-16 06:14:16 +0000 |
---|---|---|
committer | Kenneth G Raeburn <raeburn@mit.edu> | 1990-11-16 06:14:16 +0000 |
commit | d6c1a4a1043100a2534371d7572ec0ef89f117f4 (patch) | |
tree | b9de8a21615c4b46f49383ee91204836b69ca271 /clients/xzwrite | |
parent | 1849bb26ec874fdd26a5d129a1041fedd85850e2 (diff) |
Initial revision
Diffstat (limited to 'clients/xzwrite')
-rw-r--r-- | clients/xzwrite/GetString.c | 148 | ||||
-rw-r--r-- | clients/xzwrite/GetString.h | 9 | ||||
-rw-r--r-- | clients/xzwrite/Imakefile | 38 | ||||
-rw-r--r-- | clients/xzwrite/Popup.c | 100 | ||||
-rw-r--r-- | clients/xzwrite/XZwrite | 243 | ||||
-rw-r--r-- | clients/xzwrite/associate.c | 51 | ||||
-rw-r--r-- | clients/xzwrite/associate.h | 18 | ||||
-rw-r--r-- | clients/xzwrite/bfgets.c | 33 | ||||
-rw-r--r-- | clients/xzwrite/dest_window.c | 118 | ||||
-rw-r--r-- | clients/xzwrite/destlist.c | 354 | ||||
-rw-r--r-- | clients/xzwrite/edit_window.c | 123 | ||||
-rw-r--r-- | clients/xzwrite/gethomedir.c | 15 | ||||
-rw-r--r-- | clients/xzwrite/interface.c | 369 | ||||
-rw-r--r-- | clients/xzwrite/logins.c | 98 | ||||
-rw-r--r-- | clients/xzwrite/menu_window.c | 66 | ||||
-rw-r--r-- | clients/xzwrite/nmalloc.c | 996 | ||||
-rw-r--r-- | clients/xzwrite/nmalloc.h | 20 | ||||
-rw-r--r-- | clients/xzwrite/resource.c | 112 | ||||
-rw-r--r-- | clients/xzwrite/util.c | 76 | ||||
-rw-r--r-- | clients/xzwrite/xzwrite-proto.h | 98 | ||||
-rw-r--r-- | clients/xzwrite/xzwrite.1 | 400 | ||||
-rw-r--r-- | clients/xzwrite/xzwrite.c | 49 | ||||
-rw-r--r-- | clients/xzwrite/xzwrite.h | 50 | ||||
-rw-r--r-- | clients/xzwrite/yank.c | 58 | ||||
-rw-r--r-- | clients/xzwrite/zephyr.c | 262 |
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 *) ¤t_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 *) ¤t_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 *) ¤t_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(¬ice->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(¤t_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(¤t_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(¤t_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(¬ice, &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(¬ice); + else if (defs.auto_reply && + ! strcasecmp(notice.z_class, DEFAULT_CLASS) && + ! strcasecmp(notice.z_recipient, ZGetSender())) + dest_add_reply(¬ice); + 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(¬ice); + } +} + +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 *) ¬ice, 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(¬ice, (defs.auth) ? ZAUTH : ZNOAUTH); + free(sig_msg); + return ret; +} + +int zeph_ping(dest) + Dest dest; +{ + ZNotice_t notice; + + bzero((char *) ¬ice, 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(¬ice, ZNOAUTH)); +} + +int zeph_pong(dest) + Dest dest; +{ + ZNotice_t notice; + + bzero((char *) ¬ice, 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(¬ice, 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 *) ¬ice->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 : "**"); + } +} + |