--- /dev/null
+This file explains how to build and install Zephyr on a machine. To
+learn how to use Zephyr once you've installed it, read the file USING.
+To learn how to set up Zephyr service at a site, read the file
+OPERATING.
+
+To build and install Zephyr, run:
+
+ ./configure
+ make
+ make install
+
+This will build Zephyr without Hesiod or Kerberos support, and install
+in /usr/local. To install in a location other than /usr/local, add
+"--prefix=INSTPREFIX" to the configure line, where INSTPREFIX is the
+directory you want to install Zephyr in.
+
+If your site has a Hesiod service with a valid zephyr.sloc entry (or
+you can add one), you can enable Hesiod support by adding the option
+"--with-hesiod=HESPREFIX" to the configure line, where
+HESPREFIX/include and HESPREFIX/lib are the directories you have the
+Hesiod libraries installed in.
+
+If your site has a Kerberos 4 service, you can enable Kerberos support
+by adding the option "--with-krb4=KRBPREFIX" to the configure line,
+where KRBPREFIX/include and KRBPREFIX/lib are the direcetories you
+have the Kerberos libraries installed in.
+
+If you have a make which supports VPATH in a manner compatible with
+GNU make, you can build in a separate directory. Simply invoke the
+configure script from within the build directory and configure will
+locate the source directory for you. (If that doesn't work for some
+reason, you can also specify "--srcdir=SOURCEDIR" on the configuration
+line.)
+
+If configure can't properly find your X11 include or library
+directories, add "--x-includes=INCDIR" and "--x-libraries=LIBDIR" to
+the configure line. To build without X11 support, add "--without-x"
+to the configure line.
+
+If you have Hesiod and/or Kerberos installed such that you can't
+specify a single prefix for both include files and libraries, set the
+environment variables CPPFLAGS and LDFLAGS to include the relevant
+directories, and just configure with "--with-krb4" and
+"--with-hesiod". For instance (for a csh-like shell):
+
+ setenv CPPFLAGS "-I/opt/athena/include"
+ setenv LDFLAGS "-I/opt/athena/arch/sparc/lib"
+ ./configure --with-hesiod --with-krb4
+ make
+ make install
+
+Although it's not necessary for Zephyr to function correctly, you
+should add the following services to /etc/services if possible:
+
+zephyr-clt 2103/udp # Zephyr serv-hm connection
+zephyr-hm 2104/udp # Zephyr hostmanager
+zephyr-hm-srv 2105/udp # Zephyr hm-serv connection
+
+To learn how to use Zephyr, read the file USING and the man pages for
+the various Zephyr programs. To learn how to operate a Zephyr
+service, read the file OPERATING.
+
+We have tried to make Zephyr as portable as is reasonably possible,
+but have not taken into account every possible kind of system. If you
+have any problems building or installing Zephyr according to these
+instructions, please send mail to zephyr-bugs@mit.edu.
+
--- /dev/null
+SHELL = /bin/sh
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+datadir=@datadir@
+sysconfdir=@sysconfdir@
+sbindir=@sbindir@
+
+includedir=${prefix}/include
+mandir=${prefix}/man
+bindir=${exec_prefix}/bin
+libdir=${exec_prefix}/lib
+
+srcdir=@srcdir@
+top_srcdir=@top_srcdir@
+ENSUREDIR=${srcdir}/ensure-dir.sh
+INSTALL=@INSTALL@
+
+SUBDIRS=lib libdyn clients server zhm zwgc
+
+all:
+ for i in ${SUBDIRS}; do (cd $$i; ${MAKE} $@) || exit 1; done
+
+check clean:
+ for i in ${SUBDIRS}; do (cd $$i; ${MAKE} $@) || exit 1; done
+
+install:
+ ${ENSUREDIR} ${DESTDIR}${prefix} 755
+ ${ENSUREDIR} ${DESTDIR}${exec_prefix} 755
+ ${ENSUREDIR} ${DESTDIR}${bindir} 755
+ ${ENSUREDIR} ${DESTDIR}${libdir} 755
+ ${ENSUREDIR} ${DESTDIR}${datadir} 755
+ ${ENSUREDIR} ${DESTDIR}${datadir}/zephyr 755
+ ${ENSUREDIR} ${DESTDIR}${sysconfdir} 755
+ ${ENSUREDIR} ${DESTDIR}${sysconfdir}/zephyr/acl 755
+ ${ENSUREDIR} ${DESTDIR}${sbindir} 755
+ ${ENSUREDIR} ${DESTDIR}${includedir} 755
+ ${ENSUREDIR} ${DESTDIR}${includedir}/zephyr 755
+ ${ENSUREDIR} ${DESTDIR}${mandir} 755
+ ${ENSUREDIR} ${DESTDIR}${mandir}/man1 755
+ ${ENSUREDIR} ${DESTDIR}${mandir}/man3 755
+ ${ENSUREDIR} ${DESTDIR}${mandir}/man8 755
+ ${INSTALL} -m 644 ${srcdir}/h/zephyr/mit-copyright.h \
+ ${DESTDIR}${includedir}/zephyr
+ ${INSTALL} -m 644 ${srcdir}/h/zephyr/zephyr.h \
+ ${DESTDIR}${includedir}/zephyr
+ ${INSTALL} -m 644 h/zephyr/zephyr_err.h ${DESTDIR}${includedir}/zephyr
+ for i in ${SUBDIRS}; do (cd $$i; ${MAKE} $@) || exit 1; done
+
+.PHONY: all check install clean
+
--- /dev/null
+Please observe the following notes when making changes to the source
+tree.
+
+The directory h contains header files used internally in the zephyr
+source tree. The directory h/zephyr contains header files containing
+declarations for the Zephyr API. h/zephyr/zephyr_err.h is generated
+from lib/zephyr/zephyr_err.et when lib/zephyr is built.
+h/zephyr/zephyr.h is generated by configure, as is h/config.h. Those
+header files live in the build tree. All other header files in h live
+in the source tree.
+
+h/config.h is generated by configure and contains the results of
+configure tests as well as definitions for the installation
+configuration directory and data directory. h/sysdep.h uses
+h/config.h to include various header files or make various external
+declarations which many programs might be interested in. h/internal.h
+contains declarations internal to the "Zephyr system proper," which
+includes the Zephyr library, the server, the hostmanager, and (for
+now) zstat.
+
+h/zephyr/zephyr.h is a public header file; that is, it is installed
+with the Zephyr system. As such, this header file may need to work
+with compilers other than the compiler used to build the Zephyr system
+itself. Thus, we use __STDC__ (both in h/sysdep.h and in zephyr.h) to
+guess whether the compiler will handle const, prototypes, and stdarg.
+This is unfortunately not as reliable as using autoconf features like
+AC_C_CONST.
+
+The Zephyr system version number is kept in server/version.c, and
+should be updated when releases are made.
+
--- /dev/null
+This file explains how to operate a Zephyr service once you have
+installed Zephyr on all the relevant machines and file servers in your
+environment. To learn how to configure, build, and install Zephyr,
+read the file INSTALL.
+
+To set up Zephyr service at a site, follow these steps:
+
+1. Choose the machines you wish to have act as Zephyr servers at your
+site. Expect the server to be CPU-efficient but to consume a bit more
+memory than you might expect (at MIT, with around a thousand
+simultaneous users, the Zephyr server's data size is 40MB). If you
+have a lot of users, the server should have enough memory so that the
+process doesn't swap.
+
+2a. If you configured Zephyr with Hesiod support, make sure your
+Hesiod realm has a "zephyr.sloc" entry containing a record for each
+server. (Each entry should contain the name of the server, nothing
+else.) The Zephyr servers will use the zephyr.sloc entry to find the
+other servers. Host managers will use the zephyr.sloc entry to find
+the Zephyr servers by default; however, you can control the set of
+servers for each host manager by giving each host a
+"<hostname>.cluster" entry containing a record "zcluster <serverset>".
+If such a record is found, the host manager will resolve
+"<serverset>.sloc" instead of "zephyr.sloc".
+
+2b. If you configured Zephyr without Hesiod support, and you have
+multiple Zephyr servers, each server should have a file "server.list"
+in the configuration directory (which is /etc/athena/zephyr if you
+configured with --enable-athena, or /usr/local/etc/zephyr if you
+installed Zephyr in /usr/local and didn't use --enable-athena). This
+file should contain a list of the servers, one per line.
+
+3. If you configured Zephyr with Kerberos 4 support, make a service
+key "zephyr.zephyr@<your realm>" and install a srvtab for that service
+as "srvtab" in the configuration directory of each of your zephyr
+servers.
+
+4. Start zephyrd from the system binary directory (/usr/athena/etc if
+you configured with --enable-athena, /usr/local/sbin if you installed
+in /usr/local and didn't use --enable-athena). zephyrd logs as
+service "local6"; watch the syslogs for error messages. Arrange for
+zephyrd to be run at boot time on your server machines.
+
+5. Each client machine should run zhm (the Zephyr Host Manager) from
+the local system binary directory (/etc/athena for --enable-athena,
+/usr/local/sbin if you installed in /usr/local and didn't use
+--enable-athena). If you built Zephyr without Hesiod support, you
+should start zhm as "zhm server1 server2 server3 ..." so that zhm
+knows where the Zephyr servers are. Do not use "localhost" or
+"127.0.0.1" as a server name, or zhm will become confused.
+
+You can send a SIGFPE signal to the server process to make it dump its
+subscription database to /var/tmp/zephyr.db. (If /var/tmp didn't
+exist when Zephyr was built, the subscription database will be dumped
+in /usr/tmp or /tmp instead.)
--- /dev/null
+This is a snapshot of the Project Athena Zephyr notification system.
+Zephyr allows users to send messages to other users or to groups of
+users. Users can view incoming Zephyr messages as windowgrams
+(transient X windows) or as text on a terminal. Zephyr can optionally
+make use of the Kerberos version 4 security library or the Hesiod
+service name resolution library.
+
+Bug reports or patches should go to bug-zephyr@mit.edu. Please do not
+send questions about snapshots; they are not supported.
+
+To find out how to configure, build, and install Zephyr, read the file
+INSTALL.
+
+To find out how to operate a Zephyr service, read the file OPERATING.
+
+To find out how to use an existing Zephyr service, read the file
+USING.
+
+Please read the file NOTES before making any modifications to the
+source tree.
+
+--Greg Hudson
+ghudson@mit.edu
+
--- /dev/null
+This file explains how to use an existing Zephyr service once you've
+built and installed the Zephyr 2.0 distribution. To learn how to
+build and install Zephyr, read the file INSTALL. To learn how to set
+up Zephyr service at a site, read the file OPERATING.
+
+First, before you can do anything else, your client machine must be
+running zhm from the local system binary directory (/etc/athena/zephyr
+if you built Zephyr with --enable-athena, /usr/local/sbin/zephyr if
+you installed Zephyr in /usr/local and didn't use --enable-athena).
+Only one copy of zhm can be running on a given machine, and it can be
+started by any user. If you're using a machine you don't administer,
+you may want to check if the machine is configured to start up zhm
+automatically at boot time.
+
+Once you have zhm running, you can start receiving zephyrgrams by
+running the command:
+
+ zwgc
+
+"zwgc" stands for "Zephyr WindowGram Client". If you built Zephyr
+with X support and are using an X display, you will receive messages
+as windows on your screen (click on them to get rid of them);
+otherwise, you will receive messages in your terminal as text. Read
+the man page on zwgc to find out how to configure it using the
+.zwgc.desc file in your home directory.
+
+You can send messages to another user with:
+
+ zwrite username
+
+To write to groups of users, you must agree on a "class" and/or
+"instance" to write to (this will be explained in greater detail
+below). At MIT, most users communicate in private groups via classes.
+Suppose a bunch of people wanted to communicate on a class "newclass".
+They would all subscribe to the class with the command:
+
+ zctl add newclass \* \*
+
+and send messages with:
+
+ zwrite -c newclass
+
+The "zctl add" command adds the subscription to the .zephyr.subs file
+in your home directory, so that you will automatically be subscribed
+to the class the next time you run zwgc. If you just want to
+subscribe without adding the subscription to your .zephyr.subs file,
+use "zctl sub" instead of "zctl add".
+
+Now for a bit more explanation about what classes and instances are:
+every Zephyr message is send to a class, an instance, and a recipient,
+commonly written as <class,instance,recipient>. The default class is
+"MESSAGE"; the default instance is "PERSONAL". When you use "zwrite
+username", you are sending a message to <MESSAGE,PERSONAL,username>.
+If you don't specify a username on the zwrite command line, you will
+be sending to the recipient "*", so when you use "zwrite -c newclass",
+you are sending a message to <newclass,PERSONAL,username>.
+
+Similarly, every time you request a subscription, you are subscribing
+to a class, an instance, and a recipient. The recipient must be
+either "*" or your username. The instance can be any string; however,
+if you subscribe to instance "*", you will receive messages to any
+instance as long as the class and recipient also match. The class can
+be any string; "*" has no special meaning for class names. When you
+start zwgc, you are automatically subscribed to
+<MESSAGE,PERSONAL,yourusername> and <MESSAGE,URGENT,yourusername> even
+if you don't explicitly request those subscriptions.
+
+As an example of how you might use these features, at MIT we have
+several frequently-used instances of class MESSAGE, called
+"white-magic", "help", "weather", "tmbg" and so forth. These are
+commonly known as "public" instances because they are not intended to
+exclude anyone. Users can subscribe to individual instances using
+"zctl add message help \*", or they can subscribe to all of them at
+once using "zctl add message \* \*". (If users do this, they can tell
+zwgc to filter out messages from certain instances; see the man page
+for zwgc.) If users want to have semi-private group conversations,
+they use separate classes, as described earlier.
--- /dev/null
+/* Define to compile with Hesiod support. */
+#undef HAVE_HESIOD
+
+/* Define to compile with Kerberos support. */
+#undef HAVE_KRB4
+
+/* Define to compile with ares support. */
+#undef HAVE_ARES
+
+/* Define to a signed 32-bit integral type. */
+#define ZEPHYR_INT32 long
+
+/* Define if you have the System Resource Controller library. */
+#undef HAVE_SRC
+
+/* Define to "unsigned long" if your system headers don't. */
+#undef ulong
+
+/* Define to a temporary directory on your system. */
+#define FOUND_TMP "/var/tmp"
+
+/* Define to the type of the host system. */
+#define MACHINE_TYPE "unknown"
+
+/* Define if `regcomp' exists and works. */
+#undef HAVE_REGCOMP
+
--- /dev/null
+dnl $Id$
+
+dnl Copyright 1996 by the Massachusetts Institute of Technology.
+dnl
+dnl Permission to use, copy, modify, and distribute this
+dnl software and its documentation for any purpose and without
+dnl fee is hereby granted, provided that the above copyright
+dnl notice appear in all copies and that both that copyright
+dnl notice and this permission notice appear in supporting
+dnl documentation, and that the name of M.I.T. not be used in
+dnl advertising or publicity pertaining to distribution of the
+dnl software without specific, written prior permission.
+dnl M.I.T. makes no representations about the suitability of
+dnl this software for any purpose. It is provided "as is"
+dnl without express or implied warranty.
+
+dnl This file provides local macros for packages which use specific
+dnl external libraries. The public macros are:
+dnl
+dnl ATHENA_UTIL_COM_ERR
+dnl Generates error if com_err not found.
+dnl ATHENA_UTIL_SS
+dnl Generates error if ss not found.
+dnl ATHENA_REGEXP
+dnl Sets REGEX_LIBS if rx library used; ensures POSIX
+dnl regexp support.
+dnl ATHENA_MOTIF
+dnl Sets MOTIF_LIBS and defines HAVE_MOTIF if Motif used.
+dnl ATHENA_MOTIF_REQUIRED
+dnl Generates error if Motif not found.
+dnl ATHENA_AFS
+dnl Sets AFS_LIBS and defines HAVE_AFS if AFS used. Pass
+dnl in an argument giving the desired AFS libraries;
+dnl AFS_LIBS will be set to that value if AFS is found.
+dnl AFS_DIR will be set to the prefix given.
+dnl ATHENA_AFS_REQUIRED
+dnl Generates error if AFS libraries not found. AFS_DIR
+dnl will be set to the prefix given.
+dnl ATHENA_KRB4
+dnl Sets KRB4_LIBS and defines HAVE_KRB4 if krb4 used.
+dnl ATHENA_KRB4_REQUIRED
+dnl Generates error if krb4 not found. Sets KRB4_LIBS
+dnl otherwise. (Special behavior because krb4 libraries
+dnl may be different if using krb4 compatibility libraries
+dnl from krb5.)
+dnl ATHENA_KRB5
+dnl Sets KRB5_LIBS and defines HAVE_KRB5 if krb5 used.
+dnl ATHENA_KRB5_REQUIRED
+dnl Generates error if krb5 not found.
+dnl ATHENA_HESIOD
+dnl Sets HESIOD_LIBS and defines HAVE_HESIOD if Hesiod
+dnl used.
+dnl ATHENA_HESIOD_REQUIRED
+dnl Generates error if Hesiod not found.
+dnl ATHENA_ARES
+dnl Sets ARES_LIBS and defines HAVE_ARES if libares
+dnl used.
+dnl ATHENA_ARES_REQUIRED
+dnl Generates error if libares not found.
+dnl ATHENA_ZEPHYR
+dnl Sets ZEPHYR_LIBS and defines HAVE_ZEPHYR if zephyr
+dnl used.
+dnl ATHENA_ZEPHYR_REQUIRED
+dnl Generates error if zephyr not found.
+dnl
+dnl All of the macros may extend CPPFLAGS and LDFLAGS to let the
+dnl compiler find the requested libraries. Put ATHENA_UTIL_COM_ERR
+dnl and ATHENA_UTIL_SS before ATHENA_AFS or ATHENA_AFS_REQUIRED; there
+dnl is a com_err library in the AFS libraries which requires -lutil.
+
+dnl ----- com_err -----
+
+AC_DEFUN(ATHENA_UTIL_COM_ERR,
+[AC_ARG_WITH(com_err,
+ [ --with-com_err=PREFIX Specify location of com_err],
+ [com_err="$withval"], [com_err=yes])
+if test "$com_err" != no; then
+ if test "$com_err" != yes; then
+ CPPFLAGS="$CPPFLAGS -I$com_err/include"
+ LDFLAGS="$LDFLAGS -L$com_err/lib"
+ fi
+ AC_CHECK_LIB(com_err, com_err, :,
+ [AC_MSG_ERROR(com_err library not found)])
+else
+ AC_MSG_ERROR(This package requires com_err.)
+fi])
+
+dnl ----- ss -----
+
+AC_DEFUN(ATHENA_UTIL_SS,
+[AC_ARG_WITH(ss,
+ [ --with-ss=PREFIX Specify location of ss (requires com_err)],
+ [ss="$withval"], [ss=yes])
+if test "$ss" != no; then
+ if test "$ss" != yes; then
+ CPPFLAGS="$CPPFLAGS -I$ss/include"
+ LDFLAGS="$LDFLAGS -L$ss/lib"
+ fi
+ AC_CHECK_LIB(ss, ss_perror, :,
+ [AC_MSG_ERROR(ss library not found)], -lcom_err)
+else
+ AC_MSG_ERROR(This package requires ss.)
+fi])
+
+dnl ----- Regular expressions -----
+
+AC_DEFUN(ATHENA_REGEXP,
+[AC_ARG_WITH(regex,
+ [ --with-regex=PREFIX Use installed regex library],
+ [regex="$withval"], [regex=no])
+if test "$regex" != no; then
+ if test "$regex" != yes; then
+ CPPFLAGS="$CPPFLAGS -I$regex/include"
+ LDFLAGS="$LDFLAGS -L$regex/lib"
+ fi
+ AC_CHECK_LIB(regex, regcomp, REGEX_LIBS=-lregex,
+ [AC_MSG_ERROR(regex library not found)])
+else
+ AC_CHECK_FUNC(regcomp, :,
+ [AC_MSG_ERROR(can't find POSIX regexp support)])
+fi
+AC_SUBST(REGEX_LIBS)])
+
+dnl ----- Motif -----
+
+AC_DEFUN(ATHENA_MOTIF_CHECK,
+[if test "$motif" != yes; then
+ CPPFLAGS="$CPPFLAGS -I$motif/include"
+ LDFLAGS="$LDFLAGS -L$motif/lib"
+fi
+AC_CHECK_LIB(Xm, XmStringFree, :, [AC_MSG_ERROR(Motif library not found)])])
+
+AC_DEFUN(ATHENA_MOTIF,
+[AC_ARG_WITH(motif,
+ [ --with-motif=PREFIX Use Motif],
+ [motif="$withval"], [motif=no])
+if test "$motif" != no; then
+ ATHENA_MOTIF_CHECK
+ MOTIF_LIBS=-lXm
+ AC_DEFINE(HAVE_MOTIF)
+fi
+AC_SUBST(MOTIF_LIBS)])
+
+AC_DEFUN(ATHENA_MOTIF_REQUIRED,
+[AC_ARG_WITH(motif,
+ [ --with-motif=PREFIX Specify location of Motif],
+ [motif="$withval"], [motif=yes])
+if test "$motif" != no; then
+ ATHENA_MOTIF_CHECK
+else
+ AC_MSG_ERROR(This package requires Motif.)
+fi])
+
+dnl ----- AFS -----
+
+AC_DEFUN(ATHENA_AFS_CHECK,
+[AC_CHECK_FUNC(insque, :, AC_CHECK_LIB(compat, insque))
+AC_CHECK_FUNC(gethostbyname, :, AC_CHECK_LIB(nsl, gethostbyname))
+AC_CHECK_FUNC(socket, :, AC_CHECK_LIB(socket, socket))
+if test "$afs" != yes; then
+ CPPFLAGS="$CPPFLAGS -I$afs/include"
+ LDFLAGS="$LDFLAGS -L$afs/lib -L$afs/lib/afs"
+fi
+AC_CHECK_LIB(sys, pioctl, :, [AC_MSG_ERROR(AFS libraries not found)],
+ -lrx -llwp -lsys)
+AFS_DIR=$afs
+AC_SUBST(AFS_DIR)])
+
+dnl Specify desired AFS libraries as a parameter.
+AC_DEFUN(ATHENA_AFS,
+[AC_ARG_WITH(afs,
+ [ --with-afs=PREFIX Use AFS libraries],
+ [afs="$withval"], [afs=no])
+if test "$afs" != no; then
+ ATHENA_AFS_CHECK
+ AFS_LIBS=$1
+ AC_DEFINE(HAVE_AFS)
+fi
+AC_SUBST(AFS_LIBS)])
+
+AC_DEFUN(ATHENA_AFS_REQUIRED,
+[AC_ARG_WITH(afs,
+ [ --with-afs=PREFIX Specify location of AFS libraries],
+ [afs="$withval"], [afs=/usr/afsws])
+if test "$afs" != no; then
+ ATHENA_AFS_CHECK
+else
+ AC_MSG_ERROR(This package requires AFS libraries.)
+fi])
+
+dnl ----- Kerberos 4 -----
+
+AC_DEFUN(ATHENA_KRB4_CHECK,
+[AC_CHECK_FUNC(gethostbyname, :, AC_CHECK_LIB(nsl, gethostbyname))
+AC_CHECK_FUNC(socket, :, AC_CHECK_LIB(socket, socket))
+AC_CHECK_LIB(gen, compile)
+if test "$krb4" != yes; then
+ CPPFLAGS="$CPPFLAGS -I$krb4/include"
+ if test -d "$krb4/include/kerberosIV"; then
+ CPPFLAGS="$CPPFLAGS -I$krb4/include/kerberosIV"
+ fi
+ LDFLAGS="$LDFLAGS -L$krb4/lib"
+fi
+AC_CHECK_LIB(krb4, krb_rd_req,
+ [KRB4_LIBS="-lkrb4 -ldes425 -lkrb5 -lk5crypto -lcom_err"],
+ [AC_CHECK_LIB(krb, krb_rd_req,
+ [KRB4_LIBS="-lkrb -ldes"],
+ [AC_MSG_ERROR(Kerberos 4 libraries not found)],
+ -ldes)],
+ -ldes425 -lkrb5 -lk5crypto -lcom_err)])
+
+AC_DEFUN(ATHENA_KRB4,
+[AC_ARG_WITH(krb4,
+ [ --with-krb4=PREFIX Use Kerberos 4],
+ [krb4="$withval"], [krb4=no])
+if test "$krb4" != no; then
+ ATHENA_KRB4_CHECK
+ AC_DEFINE(HAVE_KRB4)
+fi
+AC_SUBST(KRB4_LIBS)])
+
+AC_DEFUN(ATHENA_KRB4_REQUIRED,
+[AC_ARG_WITH(krb4,
+ [ --with-krb4=PREFIX Specify location of Kerberos 4],
+ [krb4="$withval"], [krb4=yes])
+if test "$krb4" != no; then
+ ATHENA_KRB4_CHECK
+ AC_SUBST(KRB4_LIBS)
+else
+ AC_MSG_ERROR(This package requires Kerberos 4.)
+fi])
+
+dnl ----- Kerberos 5 -----
+
+AC_DEFUN(ATHENA_KRB5_CHECK,
+[AC_SEARCH_LIBS(gethostbyname, nsl)
+AC_SEARCH_LIBS(socket, socket)
+AC_CHECK_LIB(gen, compile)
+if test "$krb5" != yes; then
+ CPPFLAGS="$CPPFLAGS -I$krb5/include"
+ LDFLAGS="$LDFLAGS -L$krb5/lib"
+fi
+AC_CHECK_LIB(krb5, krb5_init_context, :,
+ [AC_MSG_ERROR(Kerberos 5 libraries not found)],
+ -lk5crypto -lcom_err)])
+
+AC_DEFUN(ATHENA_KRB5,
+[AC_ARG_WITH(krb5,
+ [ --with-krb5=PREFIX Use Kerberos 5],
+ [krb5="$withval"], [krb5=no])
+if test "$krb5" != no; then
+ ATHENA_KRB5_CHECK
+ KRB5_LIBS="-lkrb5 -lk5crypto -lcom_err"
+ AC_DEFINE(HAVE_KRB5)
+fi
+AC_SUBST(KRB5_LIBS)])
+
+AC_DEFUN(ATHENA_KRB5_REQUIRED,
+[AC_ARG_WITH(krb5,
+ [ --with-krb5=PREFIX Specify location of Kerberos 5],
+ [krb5="$withval"], [krb5=yes])
+if test "$krb5" != no; then
+ ATHENA_KRB5_CHECK
+else
+ AC_MSG_ERROR(This package requires Kerberos 5.)
+fi])
+
+dnl ----- Hesiod -----
+
+AC_DEFUN(ATHENA_HESIOD_CHECK,
+[AC_CHECK_FUNC(res_send, :, AC_CHECK_LIB(resolv, res_send))
+if test "$hesiod" != yes; then
+ CPPFLAGS="$CPPFLAGS -I$hesiod/include"
+ LDFLAGS="$LDFLAGS -L$hesiod/lib"
+fi
+AC_CHECK_LIB(hesiod, hes_resolve, :,
+ [AC_MSG_ERROR(Hesiod library not found)])])
+
+AC_DEFUN(ATHENA_HESIOD,
+[AC_ARG_WITH(hesiod,
+ [ --with-hesiod=PREFIX Use Hesiod],
+ [hesiod="$withval"], [hesiod=no])
+if test "$hesiod" != no; then
+ ATHENA_HESIOD_CHECK
+ HESIOD_LIBS="-lhesiod"
+ AC_DEFINE(HAVE_HESIOD)
+fi
+AC_SUBST(HESIOD_LIBS)])
+
+AC_DEFUN(ATHENA_HESIOD_REQUIRED,
+[AC_ARG_WITH(hesiod,
+ [ --with-hesiod=PREFIX Specify location of Hesiod],
+ [hesiod="$withval"], [hesiod=yes])
+if test "$hesiod" != no; then
+ ATHENA_HESIOD_CHECK
+else
+ AC_MSG_ERROR(This package requires Hesiod.)
+fi])
+
+dnl ----- libares -----
+
+AC_DEFUN(ATHENA_ARES_CHECK,
+[AC_CHECK_FUNC(res_send, :, AC_CHECK_LIB(resolv, res_send))
+if test "$ares" != yes; then
+ CPPFLAGS="$CPPFLAGS -I$ares/include"
+ LDFLAGS="$LDFLAGS -L$ares/lib"
+fi
+AC_CHECK_LIB(ares, ares_init, :, [AC_MSG_ERROR(libares not found)])])
+
+AC_DEFUN(ATHENA_ARES,
+[AC_ARG_WITH(ares,
+ [ --with-ares=PREFIX Use libares],
+ [ares="$withval"], [ares=no])
+if test "$ares" != no; then
+ ATHENA_ARES_CHECK
+ ARES_LIBS="-lares"
+ AC_DEFINE(HAVE_ARES)
+fi
+AC_SUBST(ARES_LIBS)])
+
+AC_DEFUN(ATHENA_ARES_REQUIRED,
+[AC_ARG_WITH(ares,
+ [ --with-ares=PREFIX Specify location of libares],
+ [ares="$withval"], [ares=yes])
+if test "$ares" != no; then
+ ATHENA_ARES_CHECK
+else
+ AC_MSG_ERROR(This package requires libares.)
+fi])
+dnl ----- zephyr -----
+
+AC_DEFUN(ATHENA_ZEPHYR_CHECK,
+[if test "$zephyr" != yes; then
+ CPPFLAGS="$CPPFLAGS -I$zephyr/include"
+ LDFLAGS="$LDFLAGS -L$zephyr/lib"
+fi
+AC_CHECK_LIB(zephyr, ZFreeNotice, :, [AC_MSG_ERROR(zephyr not found)])])
+
+AC_DEFUN(ATHENA_ZEPHYR,
+[AC_ARG_WITH(zephyr,
+ [ --with-zephyr=PREFIX Use zephyr],
+ [zephyr="$withval"], [zephyr=no])
+if test "$zephyr" != no; then
+ ATHENA_ZEPHYR_CHECK
+ ZEPHYR_LIBS="-lzephyr"
+ AC_DEFINE(HAVE_ZEPHYR)
+fi
+AC_SUBST(ZEPHYR_LIBS)])
+
+AC_DEFUN(ATHENA_ZEPHYR_REQUIRED,
+[AC_ARG_WITH(zephyr,
+ [ --with-zephyr=PREFIX Specify location of zephyr],
+ [zephyr="$withval"], [zephyr=yes])
+if test "$zephyr" != no; then
+ ATHENA_ZEPHYR_CHECK
+else
+ AC_MSG_ERROR(This package requires zephyr.)
+fi])
--- /dev/null
+SHELL = /bin/sh
+
+SUBDIRS=@XCLIENTS@ zaway zctl zleave zlocate zmailnotify znol \
+ zpopnotify zshutdown_notify zstat zwrite
+
+all:
+ for i in ${SUBDIRS}; do (cd $$i; ${MAKE} $@) || exit 1; done
+
+check install clean:
+ for i in ${SUBDIRS}; do (cd $$i; ${MAKE} $@) || exit 1; done
+
+.PHONY: all check install clean
+
--- /dev/null
+#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();
+
+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;
+ Widget text_source;
+
+ XtVaGetValues(edit, XtNstring, (XtArgVal) &s, XtNtextSource,
+ (XtArgVal) &text_source, NULL);
+ strncpy(buf, s, len-2);
+ buf[len-1] = '\0';
+ XawAsciiSourceFreeString(text_source);
+
+ return GETSTRING_ACCEPT;
+ }
+ else
+ return GETSTRING_CANCEL;
+}
+
+/* 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);
+}
--- /dev/null
+#include <X11/Intrinsic.h>
+
+#define GETSTRING_ACCEPT -1000
+#define GETSTRING_CANCEL -1001
+
+Widget InitGetString();
+int GetString();
+
+
--- /dev/null
+SHELL = /bin/sh
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+datadir=@datadir@
+sysconfdir=@sysconfdir@
+sbindir=@sbindir@
+lsbindir=@lsbindir@
+
+includedir=${prefix}/include
+mandir=${prefix}/man
+libdir=${exec_prefix}/lib
+bindir=${exec_prefix}/bin
+
+srcdir=@srcdir@
+top_srcdir=@top_srcdir@
+BUILDTOP=../..
+VPATH=@srcdir@
+CC=@CC@
+INSTALL=@INSTALL@
+at=@
+
+CPPFLAGS=@CPPFLAGS@
+CFLAGS=@CFLAGS@
+ALL_CFLAGS=${CFLAGS} -DDATADIR=\"${datadir}\" -I${top_srcdir}/h \
+ -I${BUILDTOP}/h @X_CFLAGS@ ${CPPFLAGS}
+LDFLAGS=-L${BUILDTOP}/lib -L${BUILDTOP}/libdyn @X_LIBS@ @LDFLAGS@
+LIBS=-lzephyr -ldyn -lXaw -lXmu -lXt @X_PRE_LIBS@ -lX11 -lXext @X_EXTRA_LIBS@ \
+ @LIBS@ -lcom_err
+
+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
+
+all: xzwrite XZwrite
+
+xzwrite: ${OBJS} ${BUILDTOP}/lib/libzephyr.a ${BUILDTOP}/libdyn/libdyn.a
+ ${CC} ${LDFLAGS} -o $@ ${OBJS} ${LIBS}
+
+.c.o:
+ ${CC} -c ${ALL_CFLAGS} $<
+
+XZwrite: XZwrite.in
+ rm -f XZwrite
+ sed -e 's,$(at)datadir$(at),$(datadir),' < ${srcdir}/XZwrite.in \
+ > XZwrite
+
+check:
+
+install: xzwrite XZwrite
+ ${INSTALL} -m 755 -s xzwrite ${DESTDIR}${bindir}
+ ${INSTALL} -m 644 ${srcdir}/xzwrite.1 ${DESTDIR}${mandir}/man1
+ ${INSTALL} -m 644 XZwrite ${DESTDIR}${datadir}/zephyr
+ ${INSTALL} -m 644 ${srcdir}/xzwrite.bitmap ${DESTDIR}${datadir}/zephyr
+
+clean:
+ rm -f ${OBJS} xzwrite
+
+${OBJS}: xzwrite.h xzwrite-proto.h ${top_srcdir}/h/sysdep.h
+${OBJS}: ${BUILDTOP}/h/config.h ${BUILDTOP}/h/zephyr/zephyr.h
+${OBJS}: ${BUILDTOP}/h/zephyr/zephyr_err.h
+destlist.o logins.o xzwrite.o zephyr.o: ${top_srcdir}/h/dyn.h
+
+.PHONY: all check install clean
+
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+! @configure_input@
+
+*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: @datadir@/zephyr/xzwrite.bitmap
+*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.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.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.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.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()
+
+*clearOnSend.label: Clear On Send
+*clearOnSend.fromVert: signature
+*clearOnSend.left: ChainLeft
+*clearOnSend.right: ChainRight
+*clearOnSend.width: 200
+*clearOnSend.translations: #override\
+ <BtnDown>,<BtnUp>: toggle() ToggleOption()
+
+*closeOnSend.label: Close On Send
+*closeOnSend.fromVert: clearOnSend
+*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()
--- /dev/null
+/*
+ * 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 = (AElementRec *) DynGet((char *) array, 0);
+ for (i=0; i < DynSize(array); i++)
+ if (strcmp(a[i].index, index) == 0)
+ return (a[i].value);
+
+ return NULL;
+}
+
+
--- /dev/null
+#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 */
--- /dev/null
+/* 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 = 0;
+ 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;
+}
--- /dev/null
+#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);
+ (void) memcpy((char *) ¤t_dest, (char *) &dest,
+ sizeof(DestRec));
+ break;
+ case SENDFAIL_SEND:
+ case SENDFAIL_RECV:
+ case SENDFAIL_ACK:
+ XBell(XtDisplay(toplevel), 0);
+ return;
+ }
+ }
+ else {
+ edit_set_title(&dest);
+ (void) memcpy((char *) ¤t_dest, (char *) &dest,
+ sizeof(DestRec));
+ }
+}
--- /dev/null
+#include <sysdep.h>
+#include <dyn.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(const void *, const void *);
+
+/* 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;
+{
+ (void) memcpy((char *) ¤t_dest, (char *) 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) {
+ char *def;
+
+ Warning("XZwrite: No destinations specified, using default.",
+ NULL);
+
+ def = (char *) Malloc(strlen("...") + 1, "adding default dests",
+ NULL);
+ strcpy(def, "...");
+ if (DynAdd(dests, (char *) &def) == DYN_NOMEM)
+ Error("Out of memory adding default destinations.", 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, (DynPtr) &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, (DynPtr) &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(a1, a2)
+ const void *a1, *a2;
+{
+ char **c1 = (char **) a1, **c2 = (char **) a2;
+ char *s1, *s2, *i1, *i2;
+
+ /* A string with a , in it is always less than one without */
+ s1 = *c1; s2 = *c2;
+ i1 = strchr(s1, ',');
+ i2 = strchr(s2, ',');
+ if (i1 == NULL && i2 != NULL)
+ return 1;
+ else if (i1 != NULL && i2 == NULL)
+ return -1;
+ else
+ return strcmp(s1, s2);
+}
+
+static int
+binary_find_dest(key)
+char *key;
+{
+ register int low = 0, high = DynHigh(dests), mid;
+ register int val;
+ register char **d;
+
+ d = (char **) DynGet(dests, 0);
+
+ /* do binary search */
+ while (low <= high) {
+ mid = (low + high) / 2;
+ val = sort_dest_func(&key, &d[mid]);
+ if (val < 0) {
+ high = mid - 1;
+ } else if (val > 0) {
+ low = mid + 1;
+ } else {
+ return (mid);
+ }
+ }
+
+ return -1;
+}
+
+char **sort_destinations()
+{
+ register char **d;
+ register int idx, idx2;
+ int dsiz = DynSize(dests);
+
+ d = (char **) DynGet(dests, 0);
+ qsort(d, dsiz, sizeof(char *), sort_dest_func);
+
+ for (idx = 0; idx < DynSize(dests);) {
+ if (d[idx][0] == '!') {
+ /* unsubscription */
+ char *next = d[idx];
+ next++;
+ while ((idx2 = binary_find_dest(next)) >= 0) {
+ /* found one to nuke */
+ DynDelete(dests, idx2);
+ if (idx2 <= idx) {
+ /* indexes shifted, so restart this pass. */
+ idx--;
+ if (idx <= 0)
+ idx = 0;
+ continue;
+ }
+ }
+ /* ok, no more to nuke from this one, so delete it and
+ move on. */
+ DynDelete(dests, idx);
+ continue;
+ }
+ /* nope, continue on to next unsub */
+ idx++;
+ }
+ return d;
+}
+
+/* Fills in dest from s */
+int parse_into_dest(dest, s)
+ Dest dest;
+ char *s;
+{
+ char *a, *b;
+ int x, y;
+
+ /* Check for just recipient */
+ if ((a=strchr(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=strchr((++a), ','))==0) {
+ if (defs.class_inst) {
+ x = a - 1 - s;
+ if (x >= ZLEN)
+ return 0;
+
+ strncpy(dest->zclass, s, x);
+ dest->zclass[x] = '\0';
+ strcpy(dest->zinst, a);
+ strcpy(dest->zrecip, "*"); }
+ else {
+ x = a - 1 - s;
+ 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 = a - 1 - s;
+ y = b - 1 - a;
+ 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;
+}
+
+/*
+ * 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;
+{
+ 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 = strchr(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);
+}
+
--- /dev/null
+#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;
+ Widget text_source;
+
+ /* I should do more interesting things with these error conditions */
+
+ XtVaGetValues(editor, XtNstring, (XtArgVal) &buf,
+ XtNtextSource, (XtArgVal) &text_source, NULL);
+
+ ret = zeph_send_message(¤t_dest, buf);
+ XawAsciiSourceFreeString(text_source);
+
+ 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);
+
+ if (defs.clear_on_send)
+ XtCallActionProc(toplevel, "ClearEditor", 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;
+ Widget text_source;
+
+ XtVaGetValues(editor, XtNstring, (XtArgVal) &buf,
+ XtNtextSource, (XtArgVal) &text_source, NULL);
+
+ if (buf != NULL && *buf != '\0')
+ yank_store(¤t_dest, buf);
+
+ XawAsciiSourceFreeString(text_source);
+}
--- /dev/null
+#include <stdio.h>
+#include <pwd.h>
+#include "xzwrite.h"
+
+char *get_home_dir()
+{
+ struct passwd *pwuid;
+ static char *h = NULL;
+
+ if (h) return h;
+
+ if ((h = getenv("HOME")) != NULL) return h;
+
+ pwuid = getpwuid(getuid());
+ return (pwuid->pw_dir);
+}
--- /dev/null
+/*
+ * 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
+ * clearOnSend
+ * 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 "xzwrite.h"
+#include "GetString.h"
+
+#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() */
+
+#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 void set_editor_width(), set_sendclose_width();
+
+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,
+ clearOnSend, 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) +
+#ifdef HAVE_PUTENV
+ strlen("XFILESEARCHPATH=") +
+#endif
+ strlen(DATADIR) + 12);
+ if (path2 != NULL) {
+#ifdef HAVE_PUTENV
+ sprintf(path2, "XFILESEARCHPATH=%s:%s/zephyr/%%N", path1,
+ DATADIR);
+ putenv(path2);
+#else
+ sprintf(path2, "%s:%s/zephyr/%%N", path1, DATADIR);
+ setenv("XFILESEARCHPATH", path2, 1);
+ free(path2);
+#endif
+ }
+ }
+
+ toplevel = XtVaAppInitialize(&app_con, "XZwrite", app_options,
+#if XtSpecificationRelease > 4
+ num_options, argc, argv,
+#else
+ num_options, (Cardinal *) argc, argv,
+#endif
+ fallback_resources, NULL);
+
+ XtVaGetApplicationResources(toplevel, (XtPointer) &defs, app_resources,
+ num_resources, NULL);
+
+ XtAppAddActions(app_con, actionTable, XtNumber(actionTable));
+
+ /* Create the icon */
+ icon = XVCMW("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);
+ clearOnSend = XVCMW("clearOnSend", toggleWidgetClass, 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(), (XtPointer)XtInputReadMask, zeph_dispatch, NULL);
+
+ if (defs.track_logins) {
+ XtAppAddWorkProc(app_con, (XtWorkProc)login_scan_work, NULL);
+ }
+
+ set_editor_width();
+ set_sendclose_width();
+ XtRealizeWidget(toplevel);
+}
+
+/* Action Procedures */
+
+/* ARGSUSED */
+static void Quit(w, e, p, n)
+ Widget w;
+ XEvent *e;
+ String *p;
+ Cardinal *n;
+{
+ XtDestroyApplicationContext(app_con);
+ ZCancelSubscriptions(0);
+ 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);
+}
+
+static void set_editor_width ()
+{
+ int w, c; char m = 'm';
+ XFontStruct *fs = (XFontStruct *) NULL;
+
+ c = defs.columns;
+ /* get the font structure. */
+ XtVaGetValues(editor, XtNfont, &fs, NULL);
+ if (c < 1 || fs == (XFontStruct *) NULL) return;
+
+ /* set the editor width */
+ w = c * XTextWidth(fs, &m, 1);
+ XtVaSetValues(editor, XtNwidth, (Dimension)w, NULL);
+
+ /* set the destList to have 3/8ths the width of the editor */
+ /* an other idea would be to make it have 3/8ths as many characters,
+ which makes a difference when the editor and destList are in
+ different fonts.
+ however, I prefer this way. */
+ XtVaSetValues(destForm, XtNwidth, (Dimension)(w*3/8), NULL);
+}
+
+static void set_sendclose_width ()
+{
+ /* make the Close Window button the width of the form */
+ Dimension wi = 0;
+ XtRealizeWidget (sendWindow);
+ XtVaGetValues(sendForm, XtNwidth, &wi, NULL);
+ XtUnrealizeWidget (sendWindow);
+ XtVaSetValues(sendClose, XtNwidth, wi, NULL);
+}
--- /dev/null
+#include "xzwrite.h"
+
+#include <X11/Intrinsic.h> /* for Boolean */
+#include <dyn.h>
+#include <zephyr/zephyr.h>
+
+extern Defaults defs;
+
+void logins_deal(notice)
+ ZNotice_t *notice;
+{
+ char *newdest, *p;
+ int d;
+
+ p = strchr(notice->z_class_inst, '@');
+ d = (p) ? p - notice->z_class_inst : strlen(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);
+ }
+}
+
+/* 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, (DynPtr)(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 (strchr(text[i], ',') || strchr(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;
+}
+
--- /dev/null
+#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, clearOnSend, closeOnSend, pings, verbose,
+ authentic, yankDest, addGlobals, classInst;
+extern Defaults defs;
+
+#define toggle(v) (v = !v)
+void menu_toggle(w)
+ Widget w;
+{
+ if (w == clearOnSend)
+ toggle(defs.clear_on_send);
+ else 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(clearOnSend, defs.clear_on_send);
+ 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);
+
+ /* Set the zephyr variable. */
+ ZSetVariable("zwrite-signature", buf);
+}
--- /dev/null
+#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"},
+ {"-clear","*clearOnSend", XrmoptionNoArg, (caddr_t) "false"},
+ {"+clear","*clearOnSend", 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"},
+ {"-columns", "*columns", XrmoptionSepArg, (caddr_t) 80},
+ {"-zsigs", "*randomZsigFile", XrmoptionSepArg, (caddr_t) "*"},
+ {"-logfile", "*logFile", XrmoptionSepArg, (caddr_t) "*"},
+};
+
+#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"},
+
+ {"clearOnSend", "Close", XtRBoolean, sizeof(Boolean),
+ offset(clear_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"},
+
+ {"columns", "Columns", XtRInt, sizeof(int),
+ offset(columns), XtRString, "80"},
+
+ {"randomZsigFile", "RandomZsigFile", XtRString, sizeof(String),
+ offset(zsigfile), XtRString, "*"},
+
+ {"logFile", "LogFile", XtRString, sizeof(String),
+ offset(logfile), XtRString, "*"},
+};
+#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);
--- /dev/null
+#include <stdio.h>
+#include <pwd.h>
+
+#include "xzwrite.h"
+
+#ifdef __STDC__
+void Warning(const char *first, ...)
+#else
+/*VARARGS*/
+void Warning(first, va_alist)
+ const char *first;
+ va_dcl
+#endif
+{
+ va_list vp;
+ char *s;
+
+ fputs(first, stderr);
+
+ VA_START(vp, first);
+ while ((s = va_arg(vp, char *)) != NULL)
+ fputs(s, stderr);
+ va_end(vp);
+ putc('\n', stderr);
+}
+
+#ifdef __STDC__
+void Error(const char *first, ...)
+#else
+/*VARARGS*/
+void Error(first, va_alist)
+ const char *first;
+ va_dcl
+#endif
+{
+ va_list vp;
+ char *s;
+
+ fputs(first, stderr);
+
+ VA_START(vp, first);
+ while ((s = va_arg(vp, char *)) != NULL)
+ fputs(s, stderr);
+ va_end(vp);
+ putc('\n', stderr);
+
+ exit(1);
+}
+
+#ifdef __STDC__
+char *Malloc(int n, ...)
+#else
+/*VARARGS*/
+char *Malloc(n, va_alist)
+ int n;
+ va_dcl
+#endif
+{
+ va_list vp;
+ char *ptr, *s;
+
+ ptr = (char *) malloc((unsigned) n);
+ if (ptr)
+ return ptr;
+
+ fputs("Out of memory: ", stderr);
+
+ VA_START(vp, n);
+ while ((s = va_arg(vp, char *)) != NULL)
+ fputs(s, stderr);
+ va_end(vp);
+ putc('\n', stderr);
+
+ exit(1);
+}
+
+char *get_username()
+{
+ struct passwd *pwuid;
+ static char *u = NULL;
+
+ if (u) return u;
+
+ if ((u = getenv("USER")) != NULL) return u;
+
+ pwuid = getpwuid(getuid());
+ if (pwuid)
+ return u = pwuid->pw_name;
+ else
+ return NULL;
+}
--- /dev/null
+#ifndef __P
+#ifdef __STDC__
+# define __P(s) s
+#else
+# define __P(s) ()
+#endif
+#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 */
+void Warning __P((const char *first, ...));
+void Error __P((const char *first, ...));
+char *Malloc __P((int n, ...));
+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 ));
+void 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((XtPointer 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_replies __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 ));
+void log_message __P((Dest dest , char *msg ));
+
+/* 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
--- /dev/null
+.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>. A line
+that begins with an exclamation point (!) is treated as an
+"unsubscription" and has the effect of removing destinations read
+from any other file that match the destination on that line. 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 /usr/athena/lib/zephyr/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. If neither is found,
+.I Xzwrite
+will look in the /etc/passwd file for the user's name.
+.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/athena/lib/zephyr/XZwrite.
+
+.SH FILES
+.TP
+/usr/athena/lib/zephyr/xzwrite.bitmap
+Default icon bitmap
+.TP
+/usr/athena/lib/zephyr/XZwrite
+Xzwrite program defaults
+.TP
+/etc/passwd
+Signature field (from gecos information)
+.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 Barry Jaspan (bjaspan@mit.edu), MIT Project Athena
+and MIT Student Information Processing Board.
--- /dev/null
+#define z_width 16
+#define z_height 16
+static char z_bits[] = {
+ 0xff, 0xff, 0xff, 0x7f, 0x00, 0x38, 0x00, 0x1c, 0x00, 0x0e, 0x00, 0x07,
+ 0x80, 0x03, 0xc0, 0x01, 0xe0, 0x00, 0x70, 0x00, 0x38, 0x00, 0x1c, 0x00,
+ 0x0e, 0x00, 0x07, 0x00, 0xff, 0xff, 0xff, 0xff};
--- /dev/null
+#include <stdio.h>
+#include <string.h>
+#include <pwd.h>
+#include <dyn.h>
+
+#include "xzwrite.h"
+
+extern Defaults defs;
+DynObject zsigs = NULL;
+
+static void set_signature __P((void));
+static Boolean set_random_zsigs __P((void));
+
+int main(argc, argv)
+ int argc;
+ char **argv;
+{
+ zeph_init();
+
+ build_interface(&argc, argv);
+
+ if (argc > 1) usage();
+
+ set_signature();
+ 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();
+ return 0;
+}
+
+static void set_signature()
+{
+ char *sig, sigbfr[BUFSIZ];
+
+ /* Do magic with signature */
+ if (defs.zsigfile)
+ if (strcmp(defs.zsigfile, "*"))
+ if (set_random_zsigs()) return;
+
+ if (*defs.signature)
+ return;
+
+ sig = (char *) zeph_get_signature();
+ if (!sig) {
+ /* try to find name in the password file */
+ register struct passwd *pwd;
+ register char *cp = sigbfr;
+ register char *cp2, *pp;
+
+ pwd = getpwuid(getuid());
+ if (pwd) {
+ cp2 = pwd->pw_gecos;
+ for (; *cp2 && *cp2 != ',' ; cp2++) {
+ if (*cp2 == '&') {
+ pp = pwd->pw_name;
+ *cp++ = islower(*pp) ? toupper(*pp) : *pp;
+ pp++;
+ while (*pp)
+ *cp++ = *pp++;
+ } else
+ *cp++ = *cp2;
+ }
+ *cp = '\0';
+ sig = sigbfr;
+ }
+ }
+
+ if (sig) {
+ defs.signature = (char *) Malloc(strlen(sig) + 1,
+ "getting signature",
+ NULL);
+ strcpy(defs.signature, sig);
+ }
+}
+
+
+
+void 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);
+}
+
+#define BUF_SIZE 1024
+
+static Boolean set_random_zsigs()
+{ int x, n;
+ char z[BUF_SIZE], *z2;
+ FILE *fp;
+
+ fp = fopen(defs.zsigfile, "r");
+ if (!fp) {
+ fprintf(stderr, "xzwrite: cant open file \"%s\".\n", defs.zsigfile);
+ return False; }
+
+ zsigs = DynCreate(sizeof(char*), 5);
+
+ while ( fgets(z, BUF_SIZE, fp) != NULL) {
+ if (z[0] == '#' || z[0] == 0) continue;
+ n = strlen(z);
+ z2 = (char *) calloc (sizeof(char), n);
+ if (!z2) {
+ fprintf(stderr, "xzwrite: out of memory.\n"); exit(1); }
+ if (z[n-1] == '\n') { n--; z[n] = 0; }
+ for (x = 0; x <= n; x++) {
+ if (z[x] != '\\') z2[x] = z[x];
+ else z2[x] = '\n'; }
+ DynAdd(zsigs, (DynPtr) &z2); }
+
+ fclose(fp);
+ return True;
+}
--- /dev/null
+#include <sysdep.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, columns;
+ String zsigfile;
+ String logfile;
+} 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"
--- /dev/null
+#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);
+ (void) memset((char *) yank_buffer, 0, 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;
+}
--- /dev/null
+#include "xzwrite.h"
+#include <string.h>
+#include <dyn.h>
+#include <com_err.h>
+
+#include <zephyr/zephyr.h>
+
+static int zeph_send_notice();
+extern Defaults defs;
+extern DynObject zsigs;
+
+/* ARGSUSED */
+void zeph_dispatch(client_data, source, input_id)
+ XtPointer 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);
+
+ /* Handle the zlocating bug the Zephyr library explicitly. */
+ /* Only display bogon zlocate packets in debug mode */
+ else if (strcmp(notice.z_class, LOCATE_CLASS) || defs.debug) {
+ Warning("XZwrite: Unexpected notice received. ",
+ "You can probably ignore this.\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(NULL);
+ if (retval != ZERR_NONE)
+ Error("Cannot open Zephyr port.", NULL);
+}
+
+int zeph_locateable(user)
+ char *user;
+{
+ char buf[BUFSIZ];
+ int n;
+
+ if (strchr(user, '@') == NULL)
+ sprintf(buf, "%s@%s", user, ZGetRealm());
+ ZLocateUser(buf, &n, ZAUTH);
+ 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 (strchr(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, *sig;
+
+ if (!zsigs) sig = defs.signature;
+ else {
+ char **tmp;
+ tmp = (char **) DynGet (zsigs, rand() % DynSize(zsigs));
+ sig = *tmp; }
+
+ msglen = strlen(msg);
+ siglen = strlen(sig);
+ sig_msg = (char *) Malloc(msglen + siglen + 2, "while sending message",
+ NULL);
+ sprintf(sig_msg, "%s%c%s", sig, '\0', msg);
+
+ memset((char *) ¬ice, 0, 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 (*sig)
+ 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 (*sig)
+ 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);
+
+ /* log to file */
+ if (defs.logfile)
+ if (strcmp(defs.logfile, "*"))
+ log_message (dest, msg);
+
+ return ret;
+}
+
+int zeph_ping(dest)
+ Dest dest;
+{
+ ZNotice_t notice;
+
+ (void) memset((char *) ¬ice, 0, 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;
+
+ (void) memset((char *) ¬ice, 0, 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;
+}
+
+void log_message(dest, msg)
+ Dest dest;
+ char *msg;
+{
+ FILE *fp;
+ int i;
+ time_t now;
+
+ fp = fopen(defs.logfile, "a");
+ if (!fp) {
+ fp = fopen(defs.logfile, "w");
+ if (!fp) {
+ fprintf(stderr, "xzwrite: could not open log file \"%s\".\n",
+ defs.logfile);
+ return; }
+ }
+
+ now = time (NULL);
+ fprintf(fp, "To: %s, %s, %s\n", dest->zclass, dest->zinst, dest->zrecip);
+ fprintf(fp, "Date: %s\n", ctime (&now));
+
+ i = strlen(msg)-1;
+ while (msg[i] == '\n' && i > 0) i--;
+ if (msg[i] != '\n') i++; msg[i] = 0;
+ fputs(msg, fp); fprintf(fp, "\n\n");
+ fclose(fp);
+}
--- /dev/null
+SHELL = /bin/sh
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+datadir=@datadir@
+sysconfdir=@sysconfdir@
+sbindir=@sbindir@
+lsbindir=@lsbindir@
+
+includedir=${prefix}/include
+mandir=${prefix}/man
+libdir=${exec_prefix}/lib
+bindir=${exec_prefix}/bin
+
+srcdir=@srcdir@
+top_srcdir=@top_srcdir@
+BUILDTOP=../..
+VPATH=@srcdir@
+CC=@CC@
+INSTALL=@INSTALL@
+
+CPPFLAGS=@CPPFLAGS@
+CFLAGS=@CFLAGS@
+ALL_CFLAGS=${CFLAGS} -I${top_srcdir}/h -I${BUILDTOP}/h ${CPPFLAGS}
+LDFLAGS=-L${BUILDTOP}/lib @LDFLAGS@
+LIBS=-lzephyr @LIBS@ -lcom_err
+
+OBJS= zaway.o
+
+all: zaway
+
+zaway: ${OBJS} ${BUILDTOP}/lib/libzephyr.a
+ ${CC} ${LDFLAGS} -o $@ ${OBJS} ${LIBS}
+
+.c.o:
+ ${CC} -c ${ALL_CFLAGS} $<
+
+check:
+
+install: zaway
+ ${INSTALL} -m 755 -s zaway ${DESTDIR}${bindir}
+ ${INSTALL} -m 644 ${srcdir}/zaway.1 ${DESTDIR}${mandir}/man1
+
+clean:
+ rm -f ${OBJS} zaway
+
+${OBJS}: ${top_srcdir}/h/sysdep.h ${BUILDTOP}/h/config.h
+${OBJS}: ${BUILDTOP}/h/zephyr/zephyr.h ${BUILDTOP}/h/zephyr/zephyr_err.h
+
+.PHONY: all check install clean
+
--- /dev/null
+.\" $Id: zaway.1,v 1.9 1999/07/21 12:34:18 ghudson Exp $
+.\"
+.\" Copyright 1987,1988 by the Massachusetts Institute of Technology
+.\" All rights reserved. The file /usr/include/zephyr/mit-copyright.h
+.\" specifies the terms and conditions for redistribution.
+.\"
+.\" @(#)zaway.1 6.1 (MIT) 7/9/87
+.\"
+.TH ZAWAY 1 "July 1, 1988" "MIT Project Athena"
+.ds ]W MIT Project Athena
+.SH NAME
+zaway \- tell other people via Zephyr that you aren't around
+.SH SYNOPSIS
+.B zaway
+[
+.I OPTIONS
+]
+[
+.I FILE
+]
+.SH DESCRIPTION
+.I zaway
+provides a way for you to automatically send replies when other people
+contact you using
+.I zwrite(1). zaway
+subscribes itself to class "MESSAGE", instance "*", so that it can
+monitor your incoming messages. It does not affect the operation of any
+other client receiving messages.
+.I zaway
+is typically run when you are leaving your terminal or display
+temporarily.
+.I zaway
+usually never exits; when you return to your terminal you should type
+the interrupt character (usually ^C) in order to make
+.I zaway
+exit.
+.SS OPTIONS
+.TP
+.I "\-m STRING"
+Use STRING as the body of the auto-reply message. Any message file
+(specified on the command line or the default) is ignored.
+.TP
+.I "\-w"
+Watch the invoking user's location status. If the user is locatable
+anywhere, no auto-replies will be sent.
+.TP
+.I "\-h"
+Displays a short usage message and exits.
+.PP
+.I zaway
+uses a message file (which defaults
+to $HOME/.away) to describe what reponses should be sent to which
+senders. The general format of this file
+is:
+.PP
+.nf
+ >name
+ >name
+ message
+ >name
+ message
+.fi
+.PP
+Any number of user names may be specified preceding the message to send
+to those senders. If a user name appears more than once, the message will
+be a concatenation of each of the appropriate messages. There are
+two special names: "*" indicates that the following message should be
+sent to all senders and "%" indicates that the following message should
+only be sent if the user name has not matched yet.
+.PP
+If no file is specified,
+and no default file can be found, the following message is returned:
+.sp
+.in +5
+I'm sorry, but I am currently away from the terminal and am
+not able to receive your message.
+.in -5
+.sp
+If a user name does not match any of those listed in the file, and no
+"*" or "%" field is specified, no return message is sent.
+All messages are preceded by the line "Automated reply:".
+To avoid loops, messages are not sent in response to messages beginning
+with "Automated reply:" or sent by the same Kerberos principal as the
+user running
+.I zaway.
+
+.SH SAMPLE FILE
+.nf
+>eichin
+>tony
+Hi there guys! I'm in the other room right now.
+I'll be back in 5 minutes or so.
+>jruser
+Sorry, but I'm gone for the day...
+>%
+Hello...I'm not sure who you are. I'll be back soon,
+though.
+>*
+This message comes to you compliments of zaway!
+.fi
+
+The final "compliments" message will be included in all messages,
+whereas the "I'm not sure" message will only be included in messages that
+are not from "eichin", "tony", or "jruser".
+.SH FILES
+$HOME/.away
+.SH SEE ALSO
+zephyr(1), zwgc(1), zwrite(1), zhm(8), zephyrd(8)
+.br
+Project Athena Technical Plan Section E.4.1, `Zephyr Notification
+Service'
+.SH AUTHOR
+.PP
+Robert S. French (MIT-Project Athena)
+.SH RESTRICTIONS
+Copyright (c) 1987,1988 by the Massachusetts Institute of Technology.
+All Rights Reserved.
+.br
+.I zephyr(1)
+specifies the terms and conditions for redistribution.
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains code for the "zaway" command.
+ *
+ * Created by: Robert French
+ *
+ * $Id: zaway.c,v 1.14 1999/07/21 12:34:22 ghudson Exp $
+ *
+ * Copyright (c) 1987, 1993 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <sysdep.h>
+#include <zephyr/mit-copyright.h>
+#include <zephyr/zephyr.h>
+#include <pwd.h>
+#include <com_err.h>
+
+#ifndef lint
+static const char rcsid_zaway_c[] = "$Id: zaway.c,v 1.14 1999/07/21 12:34:22 ghudson Exp $";
+#endif
+
+#define MESSAGE_CLASS "MESSAGE"
+#define DEFAULT_MSG "I'm sorry, but I am currently away from the terminal and am\nnot able to receive your message.\n"
+#define RESPONSE_OPCODE ""
+
+RETSIGTYPE cleanup();
+u_short port;
+
+void usage(name)
+ char *name;
+{
+ printf("Usage: %s [OPTIONS] [FILE]\n"
+ "\n"
+ " -m STRING use STRING as the body of the reply message\n"
+ " -w watch your location and only reply if you aren't locatable\n"
+ " -h display this help and exit\n",
+ name);
+}
+
+int main(argc,argv)
+ int argc;
+ char *argv[];
+{
+ FILE *fp;
+ ZNotice_t notice;
+ ZSubscription_t sub;
+ register int retval;
+ struct passwd *pw;
+ register char *ptr;
+ char awayfile[BUFSIZ],*msg[2],*envptr;
+ int optchar, watch_location;
+ char *cmdline_msg;
+ int nlocs;
+ char *find_message();
+#ifdef _POSIX_VERSION
+ struct sigaction sa;
+#endif
+
+ if ((retval = ZInitialize()) != ZERR_NONE) {
+ com_err(argv[0],retval,"while initializing");
+ exit(1);
+ }
+
+ port = 0;
+ if ((retval = ZOpenPort(&port)) != ZERR_NONE) {
+ com_err(argv[0],retval,"while opening port");
+ exit(1);
+ }
+
+ sub.zsub_class = MESSAGE_CLASS;
+ sub.zsub_classinst = "*";
+ sub.zsub_recipient = ZGetSender();
+
+ cmdline_msg = 0;
+ watch_location = 0;
+ while ((optchar = getopt(argc, argv, "m:wh")) != EOF) {
+ switch (optchar) {
+ case 'm':
+ cmdline_msg = optarg;
+ break;
+
+ case 'w':
+ watch_location = 1;
+ break;
+
+ case 'h':
+ usage(argv[0]);
+ return 0;
+
+ case '?':
+ fprintf(stderr,
+ "Unrecognized option '-%c'.\n"
+ "Try '%s -h' for more information.\n",
+ optopt, argv[0]);
+ return 1;
+ }
+ }
+
+ if (argc > optind)
+ (void) strcpy(awayfile,argv[optind]);
+ else {
+ envptr = getenv("HOME");
+ if (envptr)
+ (void) sprintf(awayfile,"%s/.away",envptr);
+ else {
+ if (!(pw = getpwuid((int) getuid()))) {
+ fprintf(stderr,"Who are you?\n");
+ exit(1);
+ }
+ (void) sprintf(awayfile,"%s/.away",pw->pw_dir);
+ }
+ }
+
+ fp = fopen(awayfile,"r");
+ if (!fp && argc > 1) {
+ fprintf(stderr,"File %s not found!\n",awayfile);
+ exit(1);
+ }
+#ifdef _POSIX_VERSION
+ (void) sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sa.sa_handler = cleanup;
+ (void) sigaction(SIGINT, &sa, (struct sigaction *)0);
+ (void) sigaction(SIGTERM, &sa, (struct sigaction *)0);
+ (void) sigaction(SIGHUP, &sa, (struct sigaction *)0);
+#else
+ (void) signal(SIGINT, cleanup);
+ (void) signal(SIGTERM, cleanup);
+ (void) signal(SIGHUP, cleanup);
+#endif
+ if ((retval = ZSubscribeToSansDefaults(&sub,1,port)) != ZERR_NONE) {
+ com_err(argv[0],retval,"while subscribing");
+ exit(1);
+ }
+
+ for (;;) {
+ if ((retval = ZReceiveNotice(¬ice, (struct sockaddr_in *)0)) != ZERR_NONE) {
+ com_err(argv[0],retval,"while receiving notice");
+ continue;
+ }
+
+ if (strcmp(notice.z_sender,ZGetSender()) == 0 ||
+ strcmp(notice.z_opcode,"PING") == 0 ||
+ strcmp(notice.z_message,"Automated reply:") == 0) {
+ ZFreeNotice(¬ice);
+ continue;
+ }
+
+ if (watch_location) {
+ if ((retval = ZLocateUser(ZGetSender(), &nlocs, ZNOAUTH))
+ != ZERR_NONE) {
+ com_err(argv[0],retval,"while locating self");
+ continue;
+ }
+
+ if (nlocs != 0) {
+ /* User is logged in. Don't send an autoreply. */
+ continue;
+ }
+
+ ZFlushLocations();
+ }
+
+ if (cmdline_msg) {
+ ptr = malloc(strlen(cmdline_msg));
+ if (!ptr) {
+ com_err(argv[0],ENOMEM,"while getting cmdline message");
+ exit(1);
+ }
+ (void) strcpy(ptr,cmdline_msg);
+ }
+ else if (fp) {
+ if (!(ptr = find_message(¬ice,fp))) {
+ ZFreeNotice(¬ice);
+ continue;
+ }
+ }
+ else {
+ ptr = malloc(sizeof(DEFAULT_MSG)+1);
+ if (!ptr) {
+ com_err(argv[0],ENOMEM,"while getting default message");
+ exit(1);
+ }
+ (void) strcpy(ptr,DEFAULT_MSG);
+ }
+ notice.z_recipient = notice.z_sender;
+ notice.z_sender = 0;
+ notice.z_default_format = "";
+ notice.z_opcode = RESPONSE_OPCODE;
+
+ msg[0] = "Automated reply:";
+ msg[1] = ptr;
+
+ notice.z_message_len = strlen(notice.z_message)+1;
+ if ((retval = ZSendList(¬ice,msg,2,ZNOAUTH)) != ZERR_NONE) {
+ com_err(argv[0],retval,"while sending notice");
+ }
+ free(ptr);
+ ZFreeNotice(¬ice);
+ }
+}
+
+char *find_message(notice,fp)
+ ZNotice_t *notice;
+ register FILE *fp;
+{
+ register char *ptr,*ptr2;
+ char bfr[BUFSIZ],sender[BUFSIZ];
+ int gotone,lastwasnt;
+
+ rewind(fp);
+
+ (void) strcpy(sender,notice->z_sender);
+ ptr2 = strchr(sender,'@');
+ if (ptr2)
+ *ptr2 = '\0';
+
+ ptr = 0;
+ gotone = 0;
+ lastwasnt = 0;
+
+ while (fgets(bfr,sizeof bfr,fp) != (char *)0) {
+ if (*bfr == '>') {
+ if (lastwasnt)
+ gotone = 0;
+ bfr[strlen(bfr)-1] = '\0';
+ ptr2 = strchr(bfr,'@');
+ if (ptr2)
+ *ptr2 = '\0';
+ if (!strcmp(bfr+1,sender) ||
+ !strcmp(bfr+1,"*") ||
+ (!strcmp(bfr+1,"%") && !ptr))
+ gotone = 1;
+ lastwasnt = 0;
+ }
+ else {
+ if (gotone) {
+ if (!ptr) {
+ ptr = malloc((unsigned)(strlen(bfr)+1));
+ *ptr = '\0';
+ }
+ else
+ ptr = realloc(ptr,(unsigned)(strlen(bfr)+strlen(ptr)+1));
+ (void) strcat(ptr,bfr);
+ }
+ lastwasnt = 1;
+ }
+ }
+
+ return (ptr);
+}
+
+RETSIGTYPE cleanup()
+{
+ ZCancelSubscriptions(port);
+ exit(1);
+}
--- /dev/null
+SHELL = /bin/sh
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+datadir=@datadir@
+sysconfdir=@sysconfdir@
+sbindir=@sbindir@
+lsbindir=@lsbindir@
+
+includedir=${prefix}/include
+mandir=${prefix}/man
+libdir=${exec_prefix}/lib
+bindir=${exec_prefix}/bin
+
+srcdir=@srcdir@
+top_srcdir=@top_srcdir@
+BUILDTOP=../..
+VPATH=@srcdir@
+CC=@CC@
+INSTALL=@INSTALL@
+
+CPPFLAGS=@CPPFLAGS@
+CFLAGS=@CFLAGS@
+ALL_CFLAGS=${CFLAGS} -I${top_srcdir}/h -I${BUILDTOP}/h @X_CFLAGS@ ${CPPFLAGS}
+LDFLAGS=-L${BUILDTOP}/lib @LDFLAGS@
+LIBS=-lzephyr -lss -lreadline -lhistory -lcurses @LIBS@ -lcom_err
+
+OBJS= zctl.o zctl_cmds.o
+
+all: zctl
+
+zctl: ${OBJS} ${BUILDTOP}/lib/libzephyr.a
+ ${CC} ${LDFLAGS} -o $@ ${OBJS} ${LIBS}
+
+zctl_cmds.c: zctl_cmds.ct
+ mk_cmds ${srcdir}/zctl_cmds.ct
+
+.c.o:
+ ${CC} -c ${ALL_CFLAGS} $<
+
+check:
+
+install: zctl
+ ${INSTALL} -m 755 -s zctl ${DESTDIR}${bindir}
+ ${INSTALL} -m 644 ${srcdir}/zctl.1 ${DESTDIR}${mandir}/man1
+
+clean:
+ rm -f ${OBJS} zctl_cmds.c zctl
+
+${OBJS}: ${top_srcdir}/h/sysdep.h ${BUILDTOP}/h/config.h
+${OBJS}: ${BUILDTOP}/h/zephyr/zephyr.h ${BUILDTOP}/h/zephyr/zephyr_err.h
+
+.PHONY: all check install clean
+
--- /dev/null
+.\" $Id: zctl.1,v 1.12 1999/01/22 23:18:29 ghudson Exp $
+.\"
+.\" Copyright 1987,1988 by the Massachusetts Institute of Technology
+.\" All rights reserved. The file /usr/include/zephyr/mit-copyright.h
+.\" specifies the terms and conditions for redistribution.
+.\"
+.\"
+.TH ZCTL 1 "July 1, 1988" "MIT Project Athena"
+.ds ]W MIT Project Athena
+.SH NAME
+zctl \- zephyr control program
+.SH SYNOPSIS
+.B zctl
+[
+.I options
+]
+.SH DESCRIPTION
+.I Zctl
+is a general purpose control program for the
+.I Zephyr(1)
+Notification Service. It allows the user to subscribe to specific
+notice types, to save the subscriptions in a file (default
+$HOME/.zephyr.subs), to change his location information, and to send
+control messages to the HostManager,
+.I zhm(8),
+and the WindowGram client,
+.I zwgc(1).
+.PP
+The commands may be typed on the command line, or may be entered
+interactively by just typing
+.I zctl
+and then typing commands to the prompt.
+.br
+.B NOTE:
+For all commands accepting an optional \fIrecipient\fR argument, the
+\fIrecipient\fR defaults to your Kerberos principal. You may also
+subscribe to recipient ``\fI*\fR''. If you specify a recipient, it is
+silently converted to ``\fI*\fR''.
+.br
+The commands are as follows:
+.TP 15
+.B add \fIclass instance\fR [ \fIrecipient\fR ]
+Subscribe to \fIclass, instance, recipient\fR, and add this triplet to
+the subscriptions file.
+.TP
+.B add_unsubscription \fIclass instance\fR [ \fIrecipient\fR ]
+Unsubscribe to \fIclass, instance, recipient\fR, and add this triplet
+to the subscriptions file as an un-subscription.
+For an explanation of un-subscriptions, see below.
+.TP
+.B cancel
+Cancel all subscriptions.
+.TP
+.B defaults
+Retrieve the default subscription list from the Zephyr server.
+.TP
+.B delete \fIclass instance\fR [ \fIrecipient\fR ]
+Unsubscribe to \fIclass, instance, recipient\fR, and remove this triplet
+from the subscriptions file.
+.TP
+.B delete_unsubscription \fIclass instance\fR [ \fIrecipient\fR ]
+Unsubscribe to \fIclass, instance, recipient\fR, and remove this triplet
+from the subscriptions file as an un-subscription.
+.TP
+.B file \fR[ \fIfile\fR ]
+Set default subscriptions file to \fIfile\fR. If \fIfile\fR isn't specified,
+show what the current subscriptions file is.
+.TP
+.B flush_locs
+Tell the Zephyr servers to flush all location information associated with
+the user. This should only be used to remove any incorrect data that may have
+been left after a system crash.
+.TP
+.B hide
+Hide your location as maintained by the Zephyr server. This does not
+affect the value of the exposure variable (see below, under
+.B set).
+.TP
+.B hm_flush
+Tell the HostManager,
+.I zhm(8),
+to ask the server to flush all state associated with the current host.
+.TP
+.B list \fR[ \fIfile\fR ]
+List contents of current subscriptions file or
+.I file.
+Any macros in the file (see below) are displayed verbatim and not expanded.
+.TP
+.B list_requests
+List all available commands. May be abbreviated by '?'.
+.TP
+.B load \fR[ \fIfile\fR ]
+Subscribe to all subscription triplets and unsubscribe to all
+un-subscription triplets in current subscriptions file or \fIfile\fR.
+.TP
+.B new_server
+Tell the HostManager,
+.I zhm(8),
+to find a new Zephyr server.
+.TP
+.B quit
+Exit from \fIzctl.
+.TP
+.B retrieve
+Retrieve all current subscriptions from the Zephyr server. These include
+subscriptions that might have been made by other programs, such as
+.I znol(1).
+.TP
+.B save \fR[ \fIfile\fR ]
+Save all current subscriptions (as returned by the Zephyr server)
+into current subscriptions file or \fIfile\fR. The
+file will be replaced.
+.TP
+.B set \fIvar\fR [ \fIvalue\fR ]
+Set the value of Zephyr variable \fIvar\fR to \fIvalue\fR, or null if
+no \fIvalue\fR is specified. The variable \fBexposure\fR has special
+significance, and can only be set to the values none, opstaff, realm-visible,
+realm-announced, net-visible, and net-announced. Setting this variable
+immediately updates the information in the Zephyr servers (see below for
+an explanation of the exposure levels). In addition,
+setting this variable to none automatically performs the equivalent of a
+.B wg_shutdown
+command, and setting it to one of the other values automatically
+performs the equivalent of a
+.B wg_startup
+command.
+.br
+Any variable settings you make will be stored in \fI$HOME/.zephyr.vars\fR
+.TP
+.B show \fIvar\fR [ \fIvar\fR \ ... ]
+Show the value of the specified Zephyr variables. If a variable is not
+defined in the user's own variables file, the system variables file
+(\fI/etc/athena/zephyr.vars\fR) is searched for a default value.
+.TP
+.B subscribe \fIclass instance\fR [ \fIrecipient\fR ]
+Subscribe to \fIclass, instance, recipient\fR, but don't add this triplet to
+the subscriptions file.
+.TP
+.B unhide
+Make your location as maintained by the Zephyr server visible. This does not
+affect the value of the exposure variable.
+.TP
+.B unload \fR[ \fIfile\fR ]
+Unsubscribe to all subscription triplets in current subscriptions file
+or \fIfile\fR. Un-subscriptions in the file are ignored.
+.TP
+.B unset \fIvar\fR [ \fIvar\fR \ ... ]
+Delete the definitions of the specified Zephyr variables.
+.TP
+.B unsubscribe \fIclass instance\fR [ \fIrecipient\fR ]
+Unsubscribe to \fIclass, instance, recipient\fR, but don't remove this triplet
+from the subscriptions file.
+.TP
+.B wg_exit
+Tell the WindowGram client,
+.I zwgc(1),
+to exit.
+.TP
+.B wg_read
+Tell the WindowGram client,
+.I zwgc(1),
+to reread its description file.
+.TP
+.B wg_shutdown
+Tell the WindowGram client to shutdown; this causes it to ignore all
+notices until a wg_startup command is issued.
+.TP
+.B wg_startup
+Tell the WindowGram client to start accepting notices again; useful
+after a wg_shutdown command has been issued.
+.SH MACROS and SUBSCRIPTION FILES
+There are three macros,
+.I %host%, %canon%, \fRand\fI %me%. %host%
+is converted to the current hostname, \fI%canon%\fR is converted to the
+official hostname as returned by
+.I gethostbyname(3),
+and \fI%me%\fR is converted to your Kerberos principal. These macros can be
+used in your \fI$HOME/.zephyr.subs\fR file or as arguments to commands
+to specify the
+.I class
+or
+.I instance
+fields. A sample \fI$HOME/.zephyr.subs\fR file might contain the following:
+.PP
+.nf
+ message,urgent,%me%
+ syslog,%host%,*
+ mail,pop,%me%
+.fi
+.PP
+.I Zctl
+reads the environment variable \fBWGFILE\fR, to find the name of the
+file where the windowgram port resides. If \fBWGFILE\fR is not set,
+the file name defaults to /tmp/wg.\fIuid\fR, where \fIuid\fR is the
+user's UNIX uid.
+.SH UN-SUBSCRIPTIONS
+The zephyr server,
+.I zephyrd(8),
+maintains default subscriptions which are automatically added to all
+users' subscriptions at the time of their first subscription during a
+login session. If you wish to automatically remove some of these
+default subscriptions, you use
+.B un-subscriptions.
+When you
+.B load
+a subscription file containing
+un-subscriptions, the un-subscriptions are automatically sent to the
+server as if you had used the
+.B unsubscribe
+command.
+.SH EXPOSURE LEVELS
+The different exposure levels affect the operation of zephyr and its
+interaction with the user, as follows:
+.TP 10
+.I none
+This completely disables Zephyr for the user. The user is not
+registered with Zephyr. No user location information is
+retained by Zephyr. No login or logout announcements will be
+sent. No subscriptions will be entered for the user, and no notices
+will be displayed by
+.I zwgc(1).
+.TP
+.I opstaff
+The user is registered with Zephyr. No login or logout
+announcements will be sent, and location information will only be
+visible to Operations staff. Default subscriptions and any additional
+personal subscriptions will be entered for the user.
+.TP
+.I realm-visible
+The user is registered with Zephyr. User location information is retained by
+Zephyr and made available only to users within the user's
+Kerberos realm. No login or logout announcements will be sent. This
+is the system default. Default subscriptions and any additional
+personal subscriptions will be entered for the user.
+.TP
+.I realm-announced
+The user is registered with Zephyr. User location information is retained by
+Zephyr and made available only to users authenticated within the user's
+Kerberos realm. Login and logout announcements will be sent, but only to
+users within the user's Kerberos realm who have explicitly requested
+such via subscriptions. Default subscriptions and any additional
+personal subscriptions will be entered for the user.
+.TP
+.I net-visible
+The user is registered with Zephyr. User location information is
+retained by Zephyr and made available to any authenticated user who
+requests such. Login and logout announcements will be sent only to users
+within the user's Kerberos realm who have explicitly requested such via
+subscriptions. Default subscriptions and any additional personal
+subscriptions will be entered for the user.
+.TP
+.I net-announced
+The user is registered with Zephyr. User location information is retained by
+Zephyr and made available to any authenticated user who requests such. Login
+and logout announcements will be sent to any user has requested such.
+Default subscriptions and any additional personal
+subscriptions will be entered for the user.
+.SH EXAMPLES
+.TP 25
+.B zctl
+Runs \fIzctl\fR in interactive mode.
+.TP
+.B zctl load
+Load subscriptions and un-subscriptions from \fI$HOME/.zephyr.subs\fR file.
+.TP
+.B zctl sub message personal
+Subscribe to personal messages, but don't add this to the
+subscriptions file.
+.TP
+.B zctl save
+Save all current subscriptions to the default subscriptions file.
+.TP
+.B zctl set exposure none
+Set your exposure level to `none', effectively turning off Zephyr.
+.SH SEE ALSO
+zephyr(1), zwgc(1), zhm(8), zephyrd(8)
+gethostbyname(3)
+.br
+Project Athena Technical Plan Section E.4.1, `Zephyr Notification
+Service'
+.SH FILES
+/tmp/wg.*
+.br
+$HOME/.zephyr.subs
+.br
+$HOME/.zephyr.vars
+.br
+/etc/athena/zephyr.vars
+.SH AUTHOR
+.PP
+Robert S. French (MIT-Project Athena)
+.sp
+.SH RESTRICTIONS
+Copyright (c) 1987,1988 by the Massachusetts Institute of Technology.
+All Rights Reserved.
+.br
+.I zephyr(1)
+specifies the terms and conditions for redistribution.
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains code for the "zctl" command.
+ *
+ * Created by: Robert French
+ *
+ * $Id: zctl.c,v 1.31 1999/08/13 00:19:38 danw Exp $
+ *
+ * Copyright (c) 1987,1988 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <sysdep.h>
+#include <zephyr/zephyr.h>
+#include <ss/ss.h>
+#include <com_err.h>
+#include <pwd.h>
+#include <netdb.h>
+#ifndef lint
+static const char *rcsid_zctl_c = "$Id: zctl.c,v 1.31 1999/08/13 00:19:38 danw Exp $";
+#endif
+
+#define SUBSATONCE 7
+#define SUB 0
+#define UNSUB 1
+#define LIST 2
+
+#define USERS_SUBS "/.zephyr.subs"
+#define OLD_SUBS "/.subscriptions"
+
+#define TOKEN_HOSTNAME "%host%"
+#define TOKEN_CANONNAME "%canon%"
+#define TOKEN_ME "%me%"
+#define TOKEN_WILD "*"
+
+#define ALL 0
+#define UNSUBONLY 1
+#define SUBONLY 2
+
+#define ERR (-1)
+#define NOT_REMOVED 0
+#define REMOVED 1
+int purge_subs();
+
+int sci_idx;
+char subsname[BUFSIZ];
+char ourhost[MAXHOSTNAMELEN],ourhostcanon[MAXHOSTNAMELEN];
+
+extern ss_request_table zctl_cmds;
+
+void add_file(), del_file(), fix_macros(), fix_macros2();
+
+main(argc,argv)
+ int argc;
+ char *argv[];
+{
+ struct passwd *pwd;
+ struct hostent *hent;
+ char ssline[BUFSIZ],oldsubsname[BUFSIZ],*envptr,*tty = NULL;
+ int retval,code,i;
+#ifdef HAVE_SYS_UTSNAME
+ struct utsname name;
+#endif
+
+ if ((retval = ZInitialize()) != ZERR_NONE) {
+ com_err(argv[0],retval,"while initializing");
+ exit (1);
+ }
+
+ /* Set hostname and tty for locations. If we support X, use the
+ * DISPLAY environment variable for the tty name. */
+#ifndef X_DISPLAY_MISSING
+ tty = getenv("DISPLAY");
+#endif
+ if ((retval = ZInitLocationInfo(NULL, tty)) != ZERR_NONE)
+ com_err(argv[0], retval, "initializing location information");
+
+ envptr = getenv("ZEPHYR_SUBS");
+ if (envptr)
+ strcpy(subsname,envptr);
+ else {
+ envptr = getenv("HOME");
+ if (envptr)
+ strcpy(subsname,envptr);
+ else {
+ if (!(pwd = getpwuid((int) getuid()))) {
+ fprintf(stderr,"Who are you?\n");
+ exit (1);
+ }
+
+ strcpy(subsname,pwd->pw_dir);
+ }
+ strcpy(oldsubsname,subsname);
+ strcat(oldsubsname,OLD_SUBS);
+ strcat(subsname,USERS_SUBS);
+ if (!access(oldsubsname,F_OK) && access(subsname, F_OK)) {
+ /* only if old one exists and new one does not exist */
+ printf("The .subscriptions file in your home directory is now being used as\n.zephyr.subs . I will rename it to .zephyr.subs for you.\n");
+ if (rename(oldsubsname,subsname))
+ com_err(argv[0], errno, "renaming .subscriptions");
+ }
+ }
+
+#ifdef HAVE_SYS_UTSNAME
+ uname(&name);
+ strcpy(ourhost, name.nodename);
+#else
+ if (gethostname(ourhost,MAXHOSTNAMELEN) == -1) {
+ com_err(argv[0],errno,"while getting host name");
+ exit (1);
+ }
+#endif
+
+ if (!(hent = gethostbyname(ourhost))) {
+ fprintf(stderr,"%s: Can't resolve hostname %s; %s may be "
+ "wrong in subscriptions",argv[0],ourhost,
+ TOKEN_CANONNAME);
+ strncpy(ourhostcanon,ourhost,sizeof(ourhostcanon)-1);
+ } else
+ strncpy(ourhostcanon,hent->h_name,sizeof(ourhostcanon)-1);
+
+ sci_idx = ss_create_invocation("zctl","",0,&zctl_cmds,&code);
+ if (code) {
+ ss_perror(sci_idx,code,"while creating invocation");
+ exit(1);
+ }
+
+ if (argc > 1) {
+ *ssline = '\0';
+ for (i=1;i<argc;i++)
+ (void) sprintf(ssline+strlen(ssline),"%s ",argv[i]);
+ ssline[strlen(ssline)-1] = '\0';
+ code = ss_execute_line(sci_idx,ssline);
+ if (code)
+ fprintf (stderr, "%s: %s: %s\n",
+ argv[0], error_message (code), ssline);
+ exit((code != 0));
+ }
+
+ printf("ZCTL $Revision: 1.31 $ (Protocol %s%d.%d) - Type '?' for a list of commands.\n\n",
+ ZVERSIONHDR,
+ ZVERSIONMAJOR,ZVERSIONMINOR);
+
+ ss_listen(sci_idx);
+ exit(0);
+}
+
+void
+set_file(argc,argv)
+ int argc;
+ char *argv[];
+{
+ if (argc > 2) {
+ fprintf(stderr,"Usage: %s filename\n",argv[0]);
+ return;
+ }
+
+ if (argc == 1)
+ printf("Current file: %s\n",subsname);
+ else
+ (void) strcpy(subsname,argv[1]);
+}
+
+void
+flush_locations(argc,argv)
+ int argc;
+ char *argv[];
+{
+ int retval;
+
+ if (argc > 1) {
+ fprintf(stderr,"Usage: %s\n",argv[0]);
+ return;
+ }
+
+ if ((retval = ZFlushMyLocations()) != ZERR_NONE)
+ ss_perror(sci_idx,retval,"while flushing locations");
+}
+
+void
+wgc_control(argc,argv)
+ int argc;
+ register char **argv;
+{
+ int retval;
+ short newport;
+ struct sockaddr_in newsin;
+ ZNotice_t notice;
+
+ newsin = ZGetDestAddr();
+
+ if (argc > 1) {
+ fprintf(stderr,"Usage: %s\n",argv[0]);
+ return;
+ }
+
+ if ((newport = ZGetWGPort()) == -1) {
+ ss_perror(sci_idx,errno,"while getting WindowGram port");
+ return;
+ }
+
+ newsin.sin_port = (u_short) newport;
+ if ((retval = ZSetDestAddr(&newsin)) != ZERR_NONE) {
+ ss_perror(sci_idx,retval,"while setting destination address");
+ return;
+ }
+
+ (void) memset((char *)¬ice, 0, sizeof(notice));
+ notice.z_kind = UNSAFE;
+ notice.z_port = 0;
+ notice.z_class = WG_CTL_CLASS;
+ notice.z_class_inst = WG_CTL_USER;
+
+ if (!strcmp(argv[0],"wg_read"))
+ notice.z_opcode = USER_REREAD;
+ if (!strcmp(argv[0],"wg_shutdown"))
+ notice.z_opcode = USER_SHUTDOWN;
+ if (!strcmp(argv[0],"wg_startup"))
+ notice.z_opcode = USER_STARTUP;
+ if (!strcmp(argv[0],"wg_exit"))
+ notice.z_opcode = USER_EXIT;
+ if (!notice.z_opcode) {
+ fprintf(stderr,
+ "unknown WindowGram client control command %s\n",
+ argv[0]);
+ return;
+ }
+ notice.z_sender = 0;
+ notice.z_recipient = "";
+ notice.z_default_format = "";
+ notice.z_message_len = 0;
+
+ if ((retval = ZSendNotice(¬ice,ZNOAUTH)) != ZERR_NONE)
+ ss_perror(sci_idx,retval,"while sending notice");
+
+ if ((retval = ZInitialize()) != ZERR_NONE)
+ ss_perror(sci_idx,retval,
+ "while reinitializing");
+}
+
+void
+hm_control(argc,argv)
+ int argc;
+ char *argv[];
+{
+ int retval;
+ ZNotice_t notice;
+
+ if (argc > 1) {
+ fprintf(stderr,"Usage: %s\n",argv[0]);
+ return;
+ }
+
+ (void) memset((char *)¬ice, 0, sizeof(notice));
+ notice.z_kind = HMCTL;
+ notice.z_port = 0;
+ notice.z_class = HM_CTL_CLASS;
+ notice.z_class_inst = HM_CTL_CLIENT;
+
+ if (!strcmp(argv[0],"hm_flush"))
+ notice.z_opcode = CLIENT_FLUSH;
+ if (!strcmp(argv[0],"new_server"))
+ notice.z_opcode = CLIENT_NEW_SERVER;
+ if (!notice.z_opcode) {
+ fprintf(stderr, "unknown HostManager control command %s\n",
+ argv[0]);
+ return;
+ }
+ notice.z_sender = 0;
+ notice.z_recipient = "";
+ notice.z_default_format = "";
+ notice.z_message_len = 0;
+
+ if ((retval = ZSendNotice(¬ice,ZNOAUTH)) != ZERR_NONE)
+ ss_perror(sci_idx,retval,"while sending notice");
+}
+
+void
+show_var(argc,argv)
+ int argc;
+ char *argv[];
+{
+ int i;
+ char *value;
+
+ if (argc < 2) {
+ fprintf(stderr,"Usage: %s <varname> <varname> ...\n",argv[0]);
+ return;
+ }
+
+ for (i=1;i<argc;i++) {
+ value = ZGetVariable(argv[i]);
+ if (value)
+ printf("%s: %s\n",argv[i],value);
+ else
+ printf("%s: not defined\n",argv[i]);
+ }
+}
+
+void
+set_var(argc,argv)
+ int argc;
+ register char **argv;
+{
+ int retval,setting_exp,i;
+ char *exp_level,*newargv[1];
+ char varcat[BUFSIZ];
+
+ if (argc < 2) {
+ fprintf(stderr,"Usage: %s <varname> [value]\n",
+ argv[0]);
+ return;
+ }
+
+ setting_exp = 0;
+
+ if (!strcasecmp(argv[1],"exposure")) {
+ setting_exp = 1;
+ if (argc != 3) {
+ fprintf(stderr,"An exposure setting must be specified.\n");
+ return;
+ }
+ exp_level = (char *)0;
+ if (!strcasecmp(argv[2],EXPOSE_NONE))
+ exp_level = EXPOSE_NONE;
+ if (!strcasecmp(argv[2],EXPOSE_OPSTAFF))
+ exp_level = EXPOSE_OPSTAFF;
+ if (!strcasecmp(argv[2],EXPOSE_REALMVIS))
+ exp_level = EXPOSE_REALMVIS;
+ if (!strcasecmp(argv[2],EXPOSE_REALMANN))
+ exp_level = EXPOSE_REALMANN;
+ if (!strcasecmp(argv[2],EXPOSE_NETVIS))
+ exp_level = EXPOSE_NETVIS;
+ if (!strcasecmp(argv[2],EXPOSE_NETANN))
+ exp_level = EXPOSE_NETANN;
+ if (!exp_level) {
+ fprintf(stderr,"The exposure setting must be one of:\n");
+ fprintf(stderr,"%s, %s, %s, %s, %s, %s.\n",
+ EXPOSE_NONE,
+ EXPOSE_OPSTAFF,
+ EXPOSE_REALMVIS,
+ EXPOSE_REALMANN,
+ EXPOSE_NETVIS,
+ EXPOSE_NETANN);
+ return;
+ }
+ }
+ if (argc == 2)
+ retval = ZSetVariable(argv[1],"");
+ else {
+ (void) strcpy(varcat,argv[2]);
+ for (i=3;i<argc;i++) {
+ (void) strcat(varcat," ");
+ (void) strcat(varcat,argv[i]);
+ }
+ retval = ZSetVariable(argv[1],varcat);
+ }
+
+ if (retval != ZERR_NONE) {
+ ss_perror(sci_idx,retval,"while setting variable value");
+ return;
+ }
+
+ /* Side-effects? Naw, us? */
+
+ if (setting_exp) {
+ if ((retval = ZSetLocation(exp_level)) != ZERR_NONE)
+ ss_perror(sci_idx,retval,"while changing exposure status");
+ if (!strcmp(exp_level,EXPOSE_NONE)) {
+ newargv[0] = "wg_shutdown";
+ wgc_control(1,newargv);
+ } else {
+ newargv[0] = "wg_startup";
+ wgc_control(1,newargv);
+ }
+ return;
+ }
+}
+
+void
+do_hide(argc,argv)
+ int argc;
+ char *argv[];
+{
+ char *exp_level = NULL;
+ Code_t retval;
+
+ if (argc != 1) {
+ fprintf(stderr, "Usage: %s\n",argv[0]);
+ return;
+ }
+ if (!strcmp(argv[0],"unhide"))
+ exp_level = EXPOSE_REALMVIS;
+ else
+ exp_level = EXPOSE_OPSTAFF;
+ if ((retval = ZSetLocation(exp_level)) != ZERR_NONE)
+ ss_perror(sci_idx,retval,"while changing exposure status");
+ return;
+}
+
+void
+unset_var(argc,argv)
+ int argc;
+ char *argv[];
+{
+ int retval,i;
+
+ if (argc < 2) {
+ fprintf(stderr,"Usage: %s <varname> [<varname> ... ]\n",
+ argv[0]);
+ return;
+ }
+
+ for (i=1;i<argc;i++)
+ if ((retval = ZUnsetVariable(argv[i])) != ZERR_NONE)
+ ss_perror(sci_idx,retval,
+ "while unsetting variable value");
+}
+
+void
+cancel_subs(argc,argv)
+ int argc;
+ char *argv[];
+{
+ int retval;
+ short wgport;
+
+ if (argc != 1) {
+ fprintf(stderr,"Usage: %s\n",argv[0]);
+ return;
+ }
+
+ if ((wgport = ZGetWGPort()) == -1) {
+ ss_perror(sci_idx,errno,"while finding WindowGram port");
+ return;
+ }
+ if ((retval = ZCancelSubscriptions((u_short)wgport)) != ZERR_NONE)
+ ss_perror(sci_idx,retval,"while cancelling subscriptions");
+}
+
+void
+subscribe(argc,argv)
+ int argc;
+ char *argv[];
+{
+ int retval;
+ short wgport;
+ ZSubscription_t sub,sub2;
+
+ if (argc > 4 || argc < 3) {
+ fprintf(stderr,"Usage: %s class instance [*]\n",argv[0]);
+ return;
+ }
+
+ sub.zsub_class = argv[1];
+ sub.zsub_classinst = argv[2];
+ sub.zsub_recipient = (argc == 3)?ZGetSender():argv[3];
+
+ fix_macros(&sub,&sub2,1);
+
+ if ((wgport = ZGetWGPort()) == -1) {
+ ss_perror(sci_idx,errno,"while finding WindowGram port");
+ return;
+ }
+
+ retval = (*argv[0] == 's') ? ZSubscribeTo(&sub2,1,(u_short)wgport) :
+ ZUnsubscribeTo(&sub2,1,(u_short)wgport);
+
+ if (retval != ZERR_NONE)
+ ss_perror(sci_idx,retval,"while subscribing");
+}
+
+void
+sub_file(argc,argv)
+ int argc;
+ char *argv[];
+{
+ ZSubscription_t sub;
+ short wgport;
+
+ if (argc > 4 || argc < 3) {
+ fprintf(stderr,"Usage: %s class instance [*]\n",argv[0]);
+ return;
+ }
+
+ if (argv[1][0] == '!') {
+ ss_perror(sci_idx,0,
+ (!strcmp(argv[0],"add_unsubscription") ||
+ !strcmp(argv[0],"add_un") ||
+ !strcmp(argv[0],"delete_unsubscription") ||
+ !strcmp(argv[0],"del_un")) ?
+ "Do not use `!' as the first character of a class.\n\tIt is automatically added before modifying the subscription file." :
+ "Do not use `!' as the first character of a class.\n\tIt is reserved for internal use with un-subscriptions.");
+ return;
+ }
+ sub.zsub_class = argv[1];
+ sub.zsub_classinst = argv[2];
+ sub.zsub_recipient = (argc == 3)?TOKEN_ME:argv[3];
+
+ if (make_exist(subsname))
+ return;
+ if ((wgport = ZGetWGPort()) == -1) {
+ ss_perror(sci_idx,errno,"while finding WindowGram port");
+ return;
+ }
+
+ if (!strcmp(argv[0],"add"))
+ add_file(wgport,&sub,0);
+ else if (!strcmp(argv[0],"add_unsubscription") ||
+ !strcmp(argv[0],"add_un"))
+ add_file(wgport,&sub,1);
+ else if (!strcmp(argv[0],"delete") ||
+ !strcmp(argv[0],"del") ||
+ !strcmp(argv[0],"dl"))
+ del_file(wgport,&sub,0);
+ else if (!strcmp(argv[0],"delete_unsubscription") ||
+ !strcmp(argv[0],"del_un")) {
+ del_file(wgport,&sub,1);
+ } else
+ ss_perror(sci_idx,0,"unknown command name");
+ return;
+}
+
+void
+add_file(wgport,subs,unsub)
+short wgport;
+ZSubscription_t *subs;
+int unsub;
+{
+ FILE *fp;
+ char errbuf[BUFSIZ];
+ ZSubscription_t sub2;
+ Code_t retval;
+
+ (void) purge_subs(subs,ALL); /* remove copies in the subs file */
+ if (!(fp = fopen(subsname,"a"))) {
+ (void) sprintf(errbuf,"while opening %s for append",subsname);
+ ss_perror(sci_idx,errno,errbuf);
+ return;
+ }
+ fprintf(fp,"%s%s,%s,%s\n",
+ unsub ? "!" : "",
+ subs->zsub_class, subs->zsub_classinst, subs->zsub_recipient);
+ if (fclose(fp) == EOF) {
+ (void) sprintf(errbuf, "while closing %s", subsname);
+ ss_perror(sci_idx, errno, errbuf);
+ return;
+ }
+ fix_macros(subs,&sub2,1);
+ if (retval = (unsub ? ZUnsubscribeTo(&sub2,1,(u_short)wgport) :
+ ZSubscribeTo(&sub2,1,(u_short)wgport)))
+ ss_perror(sci_idx,retval,
+ unsub ? "while unsubscribing" :
+ "while subscribing");
+ return;
+}
+
+void
+del_file(wgport,subs,unsub)
+short wgport;
+register ZSubscription_t *subs;
+int unsub;
+{
+ ZSubscription_t sub2;
+ int retval;
+
+ retval = purge_subs(subs, unsub ? UNSUBONLY : SUBONLY);
+ if (retval == ERR)
+ return;
+ if (retval == NOT_REMOVED)
+ fprintf(stderr,
+ "Couldn't find %sclass %s instance %s recipient %s in\n\tfile %s\n",
+ unsub ? "un-subscription " : "",
+ subs->zsub_class, subs->zsub_classinst,
+ subs->zsub_recipient, subsname);
+ fix_macros(subs,&sub2,1);
+ if ((retval = ZUnsubscribeTo(&sub2,1,(u_short)wgport)) !=
+ ZERR_NONE)
+ ss_perror(sci_idx,retval,"while unsubscribing");
+ return;
+}
+
+int
+purge_subs(subs,which)
+register ZSubscription_t *subs;
+int which;
+{
+ FILE *fp,*fpout;
+ char errbuf[BUFSIZ],subline[BUFSIZ];
+ char backup[BUFSIZ],ourline[BUFSIZ];
+ int delflag = NOT_REMOVED;
+ int keep;
+
+ switch (which) {
+ case SUBONLY:
+ case UNSUBONLY:
+ case ALL:
+ break;
+ default:
+ ss_perror(sci_idx,0,"internal error in purge_subs");
+ return(ERR);
+ }
+
+ (void) sprintf(ourline,"%s,%s,%s",
+ subs->zsub_class,
+ subs->zsub_classinst,
+ subs->zsub_recipient);
+
+ if (!(fp = fopen(subsname,"r"))) {
+ (void) sprintf(errbuf,"while opening %s for read",subsname);
+ ss_perror(sci_idx,errno,errbuf);
+ return(ERR);
+ }
+ (void) strcpy(backup, subsname);
+ (void) strcat(backup, ".temp");
+ (void) unlink(backup);
+ if (!(fpout = fopen(backup,"w"))) {
+ (void) sprintf(errbuf,"while opening %s for writing",backup);
+ ss_perror(sci_idx,errno,errbuf);
+ (void) fclose(fp);
+ return(ERR);
+ }
+ for (;;) {
+ if (!fgets(subline,sizeof subline,fp))
+ break;
+ if (*subline)
+ subline[strlen(subline)-1] = '\0'; /* nuke newline */
+ switch (which) {
+ case SUBONLY:
+ keep = strcmp(subline,ourline);
+ break;
+ case UNSUBONLY:
+ keep = (*subline != '!' || strcmp(subline+1,ourline));
+ break;
+ case ALL:
+ keep = (strcmp(subline,ourline) &&
+ (*subline != '!' || strcmp(subline+1,
+ ourline)));
+ break;
+ }
+ if (keep) {
+ fputs(subline, fpout);
+ if (ferror(fpout) || (fputc('\n', fpout) == EOF)) {
+ (void) sprintf(errbuf, "while writing to %s",
+ backup);
+ ss_perror(sci_idx, errno, errbuf);
+ }
+ } else
+ delflag = REMOVED;
+ }
+ (void) fclose(fp); /* open read-only, ignore errs */
+ if (fclose(fpout) == EOF) {
+ (void) sprintf(errbuf, "while closing %s",backup);
+ ss_perror(sci_idx, errno, errbuf);
+ return(ERR);
+ }
+ if (rename(backup,subsname) == -1) {
+ (void) sprintf(errbuf,"while renaming %s to %s\n",
+ backup,subsname);
+ ss_perror(sci_idx,errno,errbuf);
+ return(ERR);
+ }
+ return(delflag);
+}
+
+void
+load_subs(argc,argv)
+ int argc;
+ char *argv[];
+{
+ ZSubscription_t subs[SUBSATONCE],subs2[SUBSATONCE],unsubs[SUBSATONCE];
+ FILE *fp;
+ int ind,unind,lineno,i,retval,type;
+ short wgport;
+ char *comma,*comma2,*file,subline[BUFSIZ];
+
+ if (argc > 2) {
+ fprintf(stderr,"Usage: %s [file]\n",argv[0]);
+ return;
+ }
+
+ if (*argv[0] == 'u')
+ type = UNSUB;
+ else
+ if (!strcmp(argv[0],"list") || !strcmp(argv[0],"ls"))
+ type = LIST;
+ else
+ type = SUB;
+
+ if (type != LIST)
+ if ((wgport = ZGetWGPort()) == -1) {
+ ss_perror(sci_idx,errno,
+ "while finding WindowGram port");
+ return;
+ }
+
+ file = (argc == 1) ? subsname : argv[1];
+
+ fp = fopen(file,"r");
+
+ if (fp == NULL) {
+ ss_perror(sci_idx,errno,
+ "while loading subscription file");
+ return;
+ }
+
+ ind = unind = 0;
+ lineno = 1;
+
+ for (;;lineno++) {
+ if (!fgets(subline,sizeof subline,fp))
+ break;
+ if (*subline == '#' || !*subline)
+ continue;
+ subline[strlen(subline)-1] = '\0'; /* nuke newline */
+ comma = strchr(subline,',');
+ if (comma)
+ comma2 = strchr(comma+1,',');
+ else
+ comma2 = 0;
+ if (!comma || !comma2) {
+ fprintf(stderr,
+ "Malformed subscription at line %d of %s:\n%s\n",
+ lineno,file,subline);
+ continue;
+ }
+ *comma = '\0';
+ *comma2 = '\0';
+ if (type == LIST) {
+ if (*subline == '!')
+ printf("(Un-subscription) Class %s instance %s recipient %s\n",
+ subline+1, comma+1, comma2+1);
+ else
+ printf("Class %s instance %s recipient %s\n",
+ subline, comma+1, comma2+1);
+ continue;
+ }
+ if (*subline == '!') { /* an un-subscription */
+ /* if we are explicitly un-subscribing to
+ the contents of a subscription file, ignore
+ any un-subscriptions in that file */
+ if (type == UNSUB)
+ continue;
+ unsubs[unind].zsub_class =
+ (char *)malloc((unsigned)(strlen(subline)));
+ /* XXX check malloc return */
+ /* skip the leading '!' */
+ (void) strcpy(unsubs[unind].zsub_class,subline+1);
+ unsubs[unind].zsub_classinst =
+ (char *)malloc((unsigned)(strlen(comma+1)+1));
+ /* XXX check malloc return */
+ (void) strcpy(unsubs[unind].zsub_classinst,comma+1);
+ unsubs[unind].zsub_recipient =
+ (char *)malloc((unsigned)(strlen(comma2+1)+1));
+ /* XXX check malloc return */
+ (void) strcpy(unsubs[unind].zsub_recipient,comma2+1);
+ unind++;
+ } else {
+ subs[ind].zsub_class =
+ (char *)malloc((unsigned)(strlen(subline)+1));
+ /* XXX check malloc return */
+ (void) strcpy(subs[ind].zsub_class,subline);
+ subs[ind].zsub_classinst =
+ (char *)malloc((unsigned)(strlen(comma+1)+1));
+ /* XXX check malloc return */
+ (void) strcpy(subs[ind].zsub_classinst,comma+1);
+ subs[ind].zsub_recipient =
+ (char *)malloc((unsigned)(strlen(comma2+1)+1));
+ /* XXX check malloc return */
+ (void) strcpy(subs[ind].zsub_recipient,comma2+1);
+ ind++;
+ }
+ if (ind == SUBSATONCE) {
+ fix_macros(subs,subs2,ind);
+ if ((retval = (type == SUB)?
+ ZSubscribeTo(subs2,ind,(u_short)wgport):
+ ZUnsubscribeTo(subs2,ind,(u_short)wgport)) !=
+ ZERR_NONE) {
+ ss_perror(sci_idx,retval,(type == SUB)?
+ "while subscribing":
+ "while unsubscribing");
+ goto cleanup;
+ }
+ for (i=0;i<ind;i++) {
+ free(subs[i].zsub_class);
+ free(subs[i].zsub_classinst);
+ free(subs[i].zsub_recipient);
+ }
+ ind = 0;
+ }
+ if (unind == SUBSATONCE) {
+ fix_macros(unsubs,subs2,unind);
+ if ((retval = ZUnsubscribeTo(subs2,unind,(u_short)wgport)) != ZERR_NONE) {
+ ss_perror(sci_idx,retval,
+ "while unsubscribing to un-subscriptions");
+ goto cleanup;
+ }
+ for (i=0;i<unind;i++) {
+ free(unsubs[i].zsub_class);
+ free(unsubs[i].zsub_classinst);
+ free(unsubs[i].zsub_recipient);
+ }
+ unind = 0;
+ }
+ }
+
+ if (type != LIST) {
+ /* even if we have no subscriptions, be sure to send
+ an empty packet to trigger the default subscriptions */
+ fix_macros(subs,subs2,ind);
+ if ((retval = (type == SUB)?ZSubscribeTo(subs2,ind,(u_short)wgport):
+ ZUnsubscribeTo(subs2,ind,(u_short)wgport)) != ZERR_NONE) {
+ ss_perror(sci_idx,retval,(type == SUB)?
+ "while subscribing":
+ "while unsubscribing");
+ goto cleanup;
+ }
+ if (unind) {
+ fix_macros(unsubs,subs2,unind);
+ if ((retval =
+ ZUnsubscribeTo(subs2,unind,(u_short)wgport)) != ZERR_NONE) {
+ ss_perror(sci_idx,retval,
+ "while unsubscribing to un-subscriptions");
+ goto cleanup;
+ }
+ }
+ }
+cleanup:
+ for (i=0;i<ind;i++) {
+ free(subs[i].zsub_class);
+ free(subs[i].zsub_classinst);
+ free(subs[i].zsub_recipient);
+ }
+ for (i=0;i<unind;i++) {
+ free(unsubs[i].zsub_class);
+ free(unsubs[i].zsub_classinst);
+ free(unsubs[i].zsub_recipient);
+ }
+
+ (void) fclose(fp); /* ignore errs--file is read-only */
+ return;
+}
+
+void
+current(argc,argv)
+ int argc;
+ char *argv[];
+{
+ FILE *fp;
+ char errbuf[BUFSIZ];
+ ZSubscription_t subs;
+ int i,nsubs,retval,save,one,defs;
+ short wgport;
+ char *file,backup[BUFSIZ];
+
+ save = 0;
+ defs = 0;
+
+ if (!strcmp(argv[0],"save"))
+ save = 1;
+ else if (!strcmp(argv[0], "defaults") || !strcmp(argv[0], "defs"))
+ defs = 1;
+
+ if (argc != 1 && !(save && argc == 2)) {
+ fprintf(stderr,"Usage: %s%s\n",argv[0],save?" [filename]":"");
+ return;
+ }
+
+ if (!defs)
+ if ((wgport = ZGetWGPort()) == -1) {
+ ss_perror(sci_idx,errno,
+ "while finding WindowGram port");
+ return;
+ }
+
+ if (defs)
+ retval = ZRetrieveDefaultSubscriptions(&nsubs);
+ else
+ retval = ZRetrieveSubscriptions((u_short)wgport,&nsubs);
+
+ if (retval == ZERR_TOOMANYSUBS) {
+ fprintf(stderr,"Too many subscriptions -- some have not been returned.\n");
+ if (save) {
+ fprintf(stderr,"Save aborted.\n");
+ return;
+ }
+ }
+ else
+ if (retval != ZERR_NONE) {
+ ss_perror(sci_idx,retval,"retrieving subscriptions");
+ return;
+ }
+
+ if (save) {
+ file = (argc == 1)?subsname:argv[1];
+ (void) strcpy(backup,file);
+ (void) strcat(backup,".temp");
+ if (!(fp = fopen(backup,"w"))) {
+ (void) sprintf(errbuf,"while opening %s for write",
+ backup);
+ ss_perror(sci_idx,errno,errbuf);
+ return;
+ }
+ }
+
+ for (i=0;i<nsubs;i++) {
+ one = 1;
+ if ((retval = ZGetSubscriptions(&subs,&one)) != ZERR_NONE) {
+ ss_perror(sci_idx,retval,"while getting subscription");
+ if (save) {
+ fprintf(stderr,"Subscriptions file not modified\n");
+ (void) fclose(fp);
+ (void) unlink(backup);
+ }
+ return;
+ }
+ if (save)
+ fprintf(fp,"%s,%s,%s\n",subs.zsub_class,
+ subs.zsub_classinst, subs.zsub_recipient);
+ else
+ printf("Class %s Instance %s Recipient %s\n",
+ subs.zsub_class, subs.zsub_classinst,
+ subs.zsub_recipient);
+ }
+
+ if (save) {
+ if (fclose(fp) == EOF) {
+ (void) sprintf(errbuf, "while closing %s", backup);
+ ss_perror(sci_idx, errno, errbuf);
+ return;
+ }
+ if (rename(backup,file) == -1) {
+ (void) sprintf(errbuf,"while renaming %s to %s",
+ backup,file);
+ ss_perror(sci_idx,retval,errbuf);
+ (void) unlink(backup);
+ }
+ }
+}
+
+int
+make_exist(filename)
+ char *filename;
+{
+ char errbuf[BUFSIZ];
+ FILE *fpout;
+
+ if (!access(filename,F_OK))
+ return (0);
+
+ if (!(fpout = fopen(filename,"w"))) {
+ (void) sprintf(errbuf,"while opening %s for write",filename);
+ ss_perror(sci_idx,errno,errbuf);
+ return (1);
+ }
+
+ if (fclose(fpout) == EOF) {
+ (void) sprintf(errbuf, "while closing %s", filename);
+ ss_perror(sci_idx, errno, errbuf);
+ return(1);
+ }
+ return (0);
+}
+
+void
+fix_macros(subs,subs2,num)
+ ZSubscription_t *subs,*subs2;
+ int num;
+{
+ int i;
+
+ for (i=0;i<num;i++) {
+ subs2[i] = subs[i];
+ fix_macros2(subs[i].zsub_class,&subs2[i].zsub_class);
+ fix_macros2(subs[i].zsub_classinst,&subs2[i].zsub_classinst);
+ fix_macros2(subs[i].zsub_recipient,&subs2[i].zsub_recipient);
+ }
+}
+
+void
+fix_macros2(src,dest)
+ register char *src;
+ char **dest;
+{
+ if (!strcmp(src,TOKEN_HOSTNAME)) {
+ *dest = ourhost;
+ return;
+ }
+ if (!strcmp(src,TOKEN_CANONNAME)) {
+ *dest = ourhostcanon;
+ return;
+ }
+ if (!strcmp(src,TOKEN_ME))
+ *dest = ZGetSender();
+}
--- /dev/null
+# $Id: zctl_cmds.ct,v 1.7 1999/01/22 23:18:30 ghudson Exp $
+#
+ command_table zctl_cmds;
+
+ request set_file, "Set default subscriptions file.",
+ file;
+
+ request cancel_subs, "Cancel all subscriptions.",
+ cancel;
+
+ request load_subs, "Subscribe to a subscriptions file.",
+ load, ld;
+
+ request load_subs, "Unsubscribe to a subscriptions file.",
+ unload, unld;
+
+ request load_subs, "List a subscriptions file.",
+ list, ls;
+
+ request subscribe, "Subscribe to a class/class instance.",
+ subscribe, sub;
+
+ request subscribe, "Unsubscribe to a class/class instance.",
+ unsubscribe, unsub;
+
+ request sub_file, "Subscribe and add to subscriptions file.",
+ add;
+
+ request sub_file, "Unsubscribe and add to subscriptions file\n as un-subscription.",
+ add_unsubscription, add_un;
+
+ request sub_file, "Unsubscribe and delete subscription from\n subscriptions file.",
+ delete, del, dl;
+ request sub_file, "Delete un-subscription from subscriptions file.",
+ delete_unsubscription, del_un;
+
+ request current, "Retrieve current subscriptions.",
+ retrieve, ret;
+
+ request current, "Retrieve system-wide default subscriptions.",
+ defaults, defs;
+
+ request current, "Save current subscriptions (replacing existing file).",
+ save;
+
+ request show_var, "Show a variable's value.",
+ show;
+
+ request set_var, "Set a variable's value.",
+ set;
+
+ request unset_var, "Delete a variable's value.",
+ unset;
+
+ request wgc_control, "Get the WindowGram to reread it's description file.",
+ wg_read;
+
+ request wgc_control, "Tell the WindowGram not to react to incoming notices.",
+ wg_shutdown;
+
+ request wgc_control, "Tell the WindowGram to react to incoming notices.",
+ wg_startup;
+
+ request wgc_control, "Tell the WindowGram to exit completely.",
+ wg_exit;
+
+ request hm_control, "Tell the server to flush information about this host.",
+ hm_flush;
+
+ request hm_control, "Tell the HostManager to find a new server.",
+ new_server;
+
+ request flush_locations, "Flush all location information.",
+ flush_locs;
+
+ request do_hide, "Hide your location.",
+ hide;
+
+ request do_hide, "Show (un-hide) your location.",
+ unhide;
+
+ request ss_list_requests, "List available commands.",
+ list_requests, lr, "?";
+
+ request ss_quit, "Quit.",
+ quit, exit, q;
+
+ end;
--- /dev/null
+SHELL = /bin/sh
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+datadir=@datadir@
+sysconfdir=@sysconfdir@
+sbindir=@sbindir@
+lsbindir=@lsbindir@
+
+includedir=${prefix}/include
+mandir=${prefix}/man
+libdir=${exec_prefix}/lib
+bindir=${exec_prefix}/bin
+
+srcdir=@srcdir@
+top_srcdir=@top_srcdir@
+BUILDTOP=../..
+VPATH=@srcdir@
+CC=@CC@
+INSTALL=@INSTALL@
+
+CPPFLAGS=@CPPFLAGS@
+CFLAGS=@CFLAGS@
+ALL_CFLAGS=${CFLAGS} -I${top_srcdir}/h -I${BUILDTOP}/h ${CPPFLAGS}
+LDFLAGS=-L${BUILDTOP}/lib @LDFLAGS@
+LIBS=-lzephyr @LIBS@ -lcom_err
+
+OBJS= zleave.o
+
+all: zleave
+
+zleave: ${OBJS} ${BUILDTOP}/lib/libzephyr.a
+ ${CC} ${LDFLAGS} -o $@ ${OBJS} ${LIBS}
+
+.c.o:
+ ${CC} -c ${ALL_CFLAGS} $<
+
+check:
+
+install: zleave
+ ${INSTALL} -m 755 -s zleave ${DESTDIR}${bindir}
+ ${INSTALL} -m 644 ${srcdir}/zleave.1 ${DESTDIR}${mandir}/man1
+
+clean:
+ rm -f ${OBJS} zleave
+
+${OBJS}: ${top_srcdir}/h/sysdep.h ${BUILDTOP}/h/config.h
+${OBJS}: ${BUILDTOP}/h/zephyr/zephyr.h ${BUILDTOP}/h/zephyr/zephyr_err.h
+
+.PHONY: all check install clean
+
--- /dev/null
+.\" $Id: zleave.1,v 1.10 1999/01/22 23:18:32 ghudson Exp $
+.\"
+.\" Copyright (c) 1980 Regents of the University of California.
+.\" All rights reserved. The Berkeley software License Agreement
+.\" specifies the terms and conditions for redistribution.
+.\"
+.\"
+.TH ZLEAVE 1 "July 1, 1988" "MIT Project Athena"
+.ds ]W MIT Project Athena
+.SH NAME
+zleave \- notify you via Zephyr when you have to leave
+.SH SYNOPSIS
+.B zleave
+[
+.RI [+] hhmm
+[
+.I -m "Reminder Message"
+] ]
+.br
+.B zleave
+.I can\fR[\fIcel\fR]
+.SH DESCRIPTION
+.I Zleave
+waits until the specified time, then reminds you that you
+have to leave, using the \fIZephyr(1)\fR Notification Service.
+You are reminded 5 minutes and 1 minute before the actual
+time, at the time, and every minute thereafter.
+When you log off,
+.I zleave
+exits just before it would have sent the next message.
+.PP
+The time of day is in the form hhmm where hh is a time in
+hours (on a 12 or 24 hour clock).
+All times are converted to a 12 hour clock, and assumed to
+be in the next 12 hours.
+.PP
+If the time is preceded by `+', the alarm will go off in hours and minutes
+from the current time.
+.PP
+If no argument is given,
+.I zleave
+prompts with "When do you
+have to leave?". A reply of newline causes
+.I zleave
+to exit,
+otherwise the reply is assumed to be a time.
+This form is suitable for inclusion in a
+.I .login
+or
+.I .profile.
+.PP
+The
+.I cancel
+option cancels the currently running \fIzleave\fR. If another
+.I zleave
+is running, it is automatically killed when a new time to leave is
+set. The process id is stored in the file /tmp/zleave.\fIuid\fR, where
+\fIuid\fR is the user's UNIX uid.
+.PP
+If the
+.I -m
+argument is specified, the next argument
+is appended to the standard message
+(a sentence describing how much time remains until the appointed hour)
+sent at appropriate times.
+If you want to append a multiple-word message, you normally must quote it with
+double quotes (") (This is necessary for users of
+.IR csh (1)
+and
+.IR sh (1).)
+.PP
+.I Zleave
+automatically subscribes you to Zephyr class "MESSAGE",
+instance "LEAVE". You do not have to add anything to your
+default subscriptions file (see
+.IR zctl (1)).
+The reminder message is displayed by the WindowGram client (usually
+.IR zwgc (1)).
+.PP
+If Zephyr is unavailable,
+.I zleave
+acts essentially like
+.IR leave (1).
+.PP
+Zleave ignores SIGINT, SIGQUIT, and SIGTERM.
+To get rid of it you should either log off or use the
+.I cancel
+option.
+.SH FILES
+/tmp/zleave.\fIuid\fR
+/tmp/wg.*
+.SH SEE ALSO
+calendar(1), zephyr(1), leave(1), zwgc(1), zctl(1), csh(1), sh(1)
+.br
+Project Athena Technical Plan Section E.4.1, `Zephyr Notification
+Service'
+.SH RESTRICTIONS
+Copyright (c) 1980, Regents of the University of California.
+All rights reserved.
+Redistribution and use in source and binary forms are permitted
+provided that this notice is preserved and that due credit is given
+to the University of California at Berkeley. The name of the University
+may not be used to endorse or promote products derived from this
+software without specific written prior permission. This software
+is provided ``as is'' without express or implied warranty.
+.sp
+Copyright (c) 1987,1988 by the Massachusetts Institute of Technology.
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains code for the "zleave" command.
+ *
+ * Created by: David Jedlinsky
+ *
+ * $Id: zleave.c,v 1.25 1999/08/13 00:19:39 danw Exp $
+ *
+ * Copyright (c) 1987,1988 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <sysdep.h>
+#include <zephyr/mit-copyright.h>
+#include <zephyr/zephyr.h>
+
+#include <com_err.h>
+
+#ifndef lint
+static char rcsid_zlocate_c[] = "$Id: zleave.c,v 1.25 1999/08/13 00:19:39 danw Exp $";
+#endif /* lint */
+
+/*
+ * Copyright (c) 1980 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of California at Berkeley. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific written prior permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1980 Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#define MESSAGE_CLASS "MESSAGE"
+#define INSTANCE "LEAVE"
+/*
+ * zleave [[+]hhmm [ -m "Reminder Message" ]]
+ * or
+ * zleave can[cel]
+ *
+ * Reminds you when you have to leave.
+ * Leave prompts for input and goes away if you hit return.
+ * Messages are sent through Zephyr. Subscriptions are handled automagically.
+ * It nags you like a mother hen.
+ */
+char origlogin[20];
+char tempfile[40];
+char *getlogin();
+char *whenleave;
+char *reminder_message = NULL;
+char buff[100];
+int use_zephyr=1, oldpid;
+
+void usage(), doalarm(), bother(), delay();
+
+main(argc, argv)
+char **argv;
+{
+ time_t now;
+ long when, diff, hours, minutes;
+ char *cp;
+ FILE *fp;
+ struct tm *nv;
+ int gethm();
+ int port, c;
+ ZSubscription_t sub;
+
+ if (ZInitialize() != ZERR_NONE) {
+ fprintf(stderr,"No Zephyr! Will write directly to terminal.\n");
+ use_zephyr = 0;
+ }
+ (void) sprintf(tempfile, "/tmp/zleave.%d", (int) getuid());
+
+ if (use_zephyr) {
+ if ((port = ZGetWGPort()) == -1) {
+ fprintf(stderr,
+ "Can't find WindowGram subscription port.\n");
+ fprintf(stderr,"Will write directly to terminal.\n");
+ use_zephyr = 0;
+ } else {
+ sub.zsub_class = MESSAGE_CLASS;
+ sub.zsub_classinst = INSTANCE;
+ sub.zsub_recipient = ZGetSender();
+ if (ZSubscribeToSansDefaults(&sub,1,(u_short)port)
+ != ZERR_NONE) {
+ fprintf(stderr,
+ "Subscription error! Writing to your terminal...\n");
+ use_zephyr = 0;
+ }
+ }
+ }
+ if (!use_zephyr) {
+ if ((cp = getlogin()) == NULL) {
+ fputs("leave: You are not logged in.\n", stderr);
+ exit(1);
+ }
+ (void) strcpy(origlogin, cp);
+ }
+
+ c = 1;
+ while ((c<argc) && (! reminder_message))
+ if (!strcmp(argv[c++],"-m")) {
+ if (argv[c])
+ reminder_message = argv[c];
+ else
+ usage();
+ }
+
+ if (!reminder_message)
+ reminder_message = "";
+
+ if (argc < 2) {
+ printf("When do you have to leave? ");
+ (void) fflush(stdout);
+ buff[read(0, buff, sizeof buff)] = 0;
+ cp = buff;
+ } else
+ cp = argv[1];
+ if (*cp == '\n')
+ exit(0);
+ if (*cp == '+') {
+ cp++;
+ if (!gethm(cp, &hours, &minutes))
+ usage();
+ if (minutes < 0 || minutes > 59)
+ usage();
+ diff = 60*hours+minutes;
+ doalarm(diff);
+ exit(0);
+ }
+ if (!strcmp(cp, "cancel") || !strcmp(cp, "can")) {
+ if (!(fp = fopen(tempfile,"r"))) {
+ printf("No zleave is currently running.\n");
+ exit(0);
+ }
+ if (fscanf(fp, "%d", &oldpid) != 1) {
+ printf("The zleave pid file is corrupted.\n");
+ (void) fclose(fp);
+ exit(0);
+ }
+ (void) fclose(fp);
+ if (kill(oldpid,9))
+ printf("No zleave is currently running.\n");
+ (void) unlink(tempfile);
+ exit(0);
+ }
+ if (!gethm(cp, &hours, &minutes))
+ usage();
+ if (hours > 12)
+ hours -= 12;
+ if (hours == 12)
+ hours = 0;
+
+ if (hours < 0 || hours > 12 || minutes < 0 || minutes > 59)
+ usage();
+
+ (void) time(&now);
+ nv = localtime(&now);
+ when = 60*hours+minutes;
+ if (nv->tm_hour > 12)
+ nv->tm_hour -= 12; /* do am/pm bit */
+ now = 60 * nv->tm_hour + nv->tm_min;
+ diff = when - now;
+ while (diff < 0)
+ diff += 12*60;
+ if (diff > 11*60) {
+ fprintf(stderr, "That time has already passed!\n");
+ exit(1);
+ }
+
+ doalarm(diff);
+ exit(0);
+}
+
+void
+usage()
+{
+ fprintf(stderr, "usage: zleave [[+]hhmm [-m \"Reminder Message\"]]\n\
+\tor: zleave can[cel]\n");
+ exit(1);
+}
+
+int
+gethm(cp, hp, mp)
+register char *cp;
+int *hp, *mp;
+{
+ register char c;
+ register int tod;
+
+ tod = 0;
+ while ((c = *cp++) != '\0') {
+ if (!isdigit(c))
+ return(0);
+ tod = tod * 10 + (c - '0');
+ }
+ *hp = tod / 100;
+ *mp = tod % 100;
+ return(1);
+}
+
+void
+doalarm(nmins)
+long nmins;
+{
+ time_t daytime;
+ char *msg1, *msg2, *msg3, *msg4;
+ register int i;
+ long slp1, slp2, slp3, slp4;
+ long seconds, gseconds;
+ FILE *fp;
+#ifdef _POSIX_VERSION
+ struct sigaction sa;
+#endif
+
+ seconds = 60 * nmins;
+ if (seconds <= 0)
+ seconds = 1;
+ gseconds = seconds;
+
+ msg1 = "You have to leave in 5 minutes";
+ if (seconds <= 60*5) {
+ slp1 = 0;
+ } else {
+ slp1 = seconds - 60*5;
+ seconds = 60*5;
+ }
+
+ msg2 = "Just one more minute!";
+ if (seconds <= 60) {
+ slp2 = 0;
+ } else {
+ slp2 = seconds - 60;
+ seconds = 60;
+ }
+
+ msg3 = "Time to leave!";
+ slp3 = seconds;
+
+ msg4 = "You're going to be late!";
+ slp4 = 60L;
+
+ (void) time(&daytime);
+ daytime += gseconds;
+ whenleave = ctime(&daytime);
+
+ if (fp = fopen(tempfile,"r")) {
+ if (fscanf(fp, "%d", &oldpid) == 1)
+ if (!kill(oldpid,9))
+ printf("Old zleave process killed.\n");
+ (void) fclose(fp);
+ }
+ printf("Alarm set for %s", whenleave);
+
+/* Subscribe to MESSAGE.LEAVE here */
+
+ switch(fork()) {
+ case -1:
+ perror("fork");
+ exit(-1);
+ break;
+ case 0:
+ break;
+ default:
+ exit(0);
+ break;
+ }
+ if (!(fp = fopen(tempfile, "w")))
+ fprintf(stderr, "Cannot open pid file.\n");
+ else {
+ fprintf(fp, "%d\n", getpid());
+ if (fclose(fp) == EOF)
+ (void) perror("fclose on pid file");
+ }
+
+#ifdef _POSIX_VERSION
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sa.sa_handler = SIG_IGN;
+ sigaction(SIGINT, &sa, (struct sigaction *)0);
+ sigaction(SIGQUIT, &sa, (struct sigaction *)0);
+ sigaction(SIGTERM, &sa, (struct sigaction *)0);
+ sigaction(SIGTTOU, &sa, (struct sigaction *)0);
+#else
+ (void) signal(SIGINT, SIG_IGN);
+ (void) signal(SIGQUIT, SIG_IGN);
+ (void) signal(SIGTERM, SIG_IGN);
+ (void) signal(SIGTTOU, SIG_IGN);
+#endif
+
+ if (slp1)
+ bother(slp1, msg1);
+ if (slp2)
+ bother(slp2, msg2);
+ bother(slp3, msg3);
+ for (i = 0; i < 10; i++)
+ bother(slp4, msg4);
+
+ bother(0L, "That was the last time I'll tell you. Bye.");
+ (void) unlink(tempfile);
+ exit(0);
+}
+
+void
+bother(slp, msg)
+long slp;
+char *msg;
+{
+ ZNotice_t notice;
+ ZNotice_t retnotice;
+ int retval;
+ char *real_message;
+
+ delay(slp);
+
+ if (use_zephyr) {
+ real_message = (char *) malloc(strlen(msg) +
+ strlen(reminder_message) + 3);
+ if (real_message == NULL) {
+ fprintf (stderr, "zleave: out of memory\n");
+ exit (1);
+ }
+ sprintf(real_message,"%c%s\n%s",'\0',msg,reminder_message);
+
+ (void) memset((char *)¬ice, 0, sizeof(notice));
+ notice.z_kind = ACKED;
+ notice.z_port = 0;
+ notice.z_class = MESSAGE_CLASS;
+ notice.z_class_inst = INSTANCE;
+ notice.z_recipient = ZGetSender();
+ notice.z_opcode = "";
+ notice.z_sender = (char *) 0;
+ notice.z_default_format = "\n$2";
+ notice.z_message = real_message;
+ /* +3: initial null, newline, final null */
+ notice.z_message_len = strlen(msg)+strlen(reminder_message)+3;
+
+ if (ZSendNotice(¬ice, ZAUTH) != ZERR_NONE) {
+ printf("\7\7\7%s\n%s", msg, reminder_message);
+ use_zephyr = 0;
+ } else
+ if ((retval = ZIfNotice(&retnotice, (struct sockaddr_in *) 0,
+ ZCompareUIDPred,
+ (char *)¬ice.z_uid)) != ZERR_NONE) {
+ fprintf(stderr,
+ "zleave: %s while waiting for acknowledgement\n",
+ error_message(retval));
+ use_zephyr = 0;
+ } else
+ if (retnotice.z_kind == SERVNAK) {
+ fprintf(stderr,
+ "zleave: authorization failure while sending\n");
+ use_zephyr = 0;
+ } else
+ if (retnotice.z_kind != SERVACK || !retnotice.z_message_len) {
+ fprintf(stderr, "zleave: Detected server failure while receiving acknowledgement\n");
+ use_zephyr = 0;
+ } else
+ if (strcmp(retnotice.z_message, ZSRVACK_SENT)) {
+ /* it wasn't sent */
+ exit(0);
+ }
+ if (!use_zephyr)
+ exit(1);
+ ZFreeNotice(&retnotice);
+ free(real_message);
+ } else
+#ifdef __STDC__
+ printf("\a\a\a%s\n%s", msg, reminder_message);
+#else
+ printf("\7\7\7%s\n%s", msg, reminder_message);
+#endif
+}
+
+/*
+ * delay is like sleep but does it in 100 sec pieces and
+ * knows what zero means.
+ */
+void
+delay(secs)
+long secs;
+{
+ long n;
+ register char *l;
+
+ while (secs > 0) {
+ n = 100;
+ if (secs < n)
+ n = secs;
+ secs -= n;
+ if (n > 0)
+ sleep((unsigned) n);
+ if (!use_zephyr) {
+ l = getlogin();
+ if (l == NULL)
+ exit(0);
+ if (strcmp(origlogin, l) != 0)
+ exit(0);
+ }
+ }
+}
+
+#ifndef HAVE_GETLOGIN
+char *getlogin() {
+#include <utmp.h>
+
+ static struct utmp ubuf;
+ int ufd;
+
+ ufd = open("/etc/utmp",0);
+ seek(ufd, ttyn(0)*sizeof(ubuf), 0);
+ read(ufd, &ubuf, sizeof(ubuf));
+ ubuf.ut_name[sizeof(ubuf.ut_name)] = 0;
+ return(&ubuf.ut_name);
+}
+#endif
--- /dev/null
+SHELL = /bin/sh
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+datadir=@datadir@
+sysconfdir=@sysconfdir@
+sbindir=@sbindir@
+lsbindir=@lsbindir@
+
+includedir=${prefix}/include
+mandir=${prefix}/man
+libdir=${exec_prefix}/lib
+bindir=${exec_prefix}/bin
+
+srcdir=@srcdir@
+top_srcdir=@top_srcdir@
+BUILDTOP=../..
+VPATH=@srcdir@
+CC=@CC@
+INSTALL=@INSTALL@
+
+CPPFLAGS=@CPPFLAGS@
+CFLAGS=@CFLAGS@
+ALL_CFLAGS=${CFLAGS} -I${top_srcdir}/h -I${BUILDTOP}/h ${CPPFLAGS}
+LDFLAGS=-L${BUILDTOP}/lib @LDFLAGS@
+LIBS=-lzephyr @LIBS@ -lcom_err
+
+OBJS= zlocate.o
+
+all: zlocate
+
+zlocate: ${OBJS} ${BUILDTOP}/lib/libzephyr.a
+ ${CC} ${LDFLAGS} -o $@ ${OBJS} ${LIBS}
+
+.c.o:
+ ${CC} -c ${ALL_CFLAGS} $<
+
+check:
+
+install: zlocate
+ ${INSTALL} -m 755 -s zlocate ${DESTDIR}${bindir}
+ ${INSTALL} -m 644 ${srcdir}/zlocate.1 ${DESTDIR}${mandir}/man1
+
+clean:
+ rm -f ${OBJS} zlocate
+
+${OBJS}: ${top_srcdir}/h/sysdep.h ${BUILDTOP}/h/config.h
+${OBJS}: ${BUILDTOP}/h/zephyr/zephyr.h ${BUILDTOP}/h/zephyr/zephyr_err.h
+
+.PHONY: all check install clean
+
--- /dev/null
+.\" $Id: zlocate.1,v 1.10 1999/01/22 23:18:36 ghudson Exp $
+.\"
+.\" Copyright 1987,1988 by the Massachusetts Institute of Technology
+.\" All rights reserved. The file /usr/include/zephyr/mit-copyright.h
+.\" specifies the terms and conditions for redistribution.
+.\"
+.\"
+.TH ZLOCATE 1 "April 17, 1990" "MIT Project Athena"
+.ds ]W MIT Project Athena
+.SH NAME
+zlocate \- find a user using Zephyr
+.SH SYNOPSIS
+.B zlocate
+[
+.B -a
+|
+.B -d
+] user ...
+.SH DESCRIPTION
+.I Zlocate
+uses the
+.I Zephyr(1)
+Notification Service to find where a user is currently logged in. If
+the user is not logged in, or has set his location information such that
+you do not have access to see it,
+.I zlocate
+prints "Hidden or not logged-in". Otherwise, each machine that the
+user is currently logged into is printed, along with the time of
+login and location. The location is usually the X window system display
+name of the user's display, but may be the terminal name if he is not
+using X or for some other reason is only using the terminal interface to
+.I zwgc(1).
+
+By default, all zlocate requests are authenticated using
+.IR Kerberos .
+If you do not have kerberos tickets, or for some other reason do not
+want to authenticate, the
+.B -d
+option will turn off authentication. The
+.B -a
+option is the default, authentication on.
+
+.SH DIAGNOSTICS
+.I zlocate
+exits with status zero (0) if at least one user was found, and one (1)
+if no users were found.
+.SH SEE ALSO
+zctl(1), zephyr(1), znol(1), zwgc(1), zhm(8), zephyrd(8), X(1)
+.br
+Project Athena Technical Plan Section E.4.1, `Zephyr Notification
+Service'
+.SH AUTHORS
+.PP
+Robert S. French (MIT-Project Athena)
+.br
+Marc Horowitz (MIT-Project Athena)
+.SH RESTRICTIONS
+Copyright (c) 1987,1988 by the Massachusetts Institute of Technology.
+All Rights Reserved.
+.br
+.I zephyr(1)
+specifies the terms and conditions for redistribution.
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains code for the "zlocate" command.
+ *
+ * Created by: Robert French
+ *
+ * $Id: zlocate.c,v 1.15 1999/01/22 23:18:36 ghudson Exp $
+ *
+ * Copyright (c) 1987 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <sysdep.h>
+#include <zephyr/zephyr.h>
+#include <sys/socket.h>
+
+#if !defined(lint) && !defined(SABER)
+static const char rcsid_zlocate_c[] = "$Id: zlocate.c,v 1.15 1999/01/22 23:18:36 ghudson Exp $";
+#endif
+
+int numusers=0, numleft=0, parallel=0, oneline=0;
+char *whoami;
+
+RETSIGTYPE timeout(sig)
+{
+ fprintf (stderr, "%s: no response from server\n", whoami);
+ exit(1);
+}
+
+void usage()
+{
+ printf("Usage: %s [ -a | -d ] [ -p ] [ -1 ] user ... \n",whoami);
+ exit(1);
+}
+
+void print_locs(user,nlocs)
+ char *user;
+ int nlocs;
+{
+ int one = 1, retval;
+ ZLocations_t locations;
+
+ if ((!oneline) && (numusers>1))
+ printf("\t%s:\n",user);
+
+ if ((!oneline) && (nlocs == 0))
+ printf("Hidden or not logged-in\n");
+
+ for (;nlocs;nlocs--) {
+ if ((retval = ZGetLocations(&locations,&one)) != ZERR_NONE) {
+ com_err(whoami,retval,"while getting location");
+ exit(1);
+ }
+
+ if (oneline) {
+ printf("%s:\t%s\t%s\t%s\n",user,locations.host,locations.tty,
+ locations.time);
+ } else {
+ printf("%-42s %-7s %s\n",locations.host, locations.tty, locations.time);
+ }
+ }
+
+ if ((!oneline) && (numusers > 1) && (numleft > 0))
+ printf("\n");
+}
+
+/*ARGSUSED*/
+main(argc,argv)
+ int argc;
+ char *argv[];
+{
+ char user[BUFSIZ],*whichuser;
+ ZAsyncLocateData_t ald;
+ int retval,i,numlocs,numfound,loc,auth,rlen;
+ ZNotice_t notice;
+#ifdef _POSIX_VERSION
+ struct sigaction sa;
+#endif
+
+ whoami = argv[0];
+ auth = -1;
+
+ argv++;
+ argc--;
+
+ for (i=0; i < argc; i++)
+ if (argv[i][0] == '-')
+ switch (argv[i][1]) {
+ case 'a':
+ if (auth != -1) usage();
+ auth = 1;
+ break;
+ case 'd':
+ if (auth != -1) usage();
+ auth = 0;
+ break;
+ case 'p':
+ parallel = 1;
+ break;
+ case '1':
+ oneline = 1;
+ break;
+ default:
+ usage();
+ break;
+ }
+ else
+ numusers++;
+
+ if (numusers == 0)
+ usage();
+
+ if (auth == -1) auth = 1;
+
+ if ((retval = ZInitialize()) != ZERR_NONE) {
+ com_err(whoami,retval,"while initializing");
+ exit(1);
+ }
+
+ numleft = numusers;
+ numfound = 0;
+ rlen = strlen(ZGetRealm());
+
+ i = 0;
+ for (loc = 0; loc < argc; loc++) {
+ if (argv[loc][0] == '-') continue;
+
+ (void) strncpy(user,argv[loc],sizeof(user) - rlen - 2);
+ user[sizeof(user) - rlen - 2] = '\0';
+ if (!strchr(user,'@')) {
+ (void) strcat(user,"@");
+ (void) strcat(user,ZGetRealm());
+ }
+ if (parallel) {
+ if ((retval = ZRequestLocations(user, &ald, i ? UNSAFE : UNACKED,
+ auth?ZAUTH:ZNOAUTH)) != ZERR_NONE) {
+ com_err(whoami,retval,"requesting location of %s",user);
+ exit(1);
+ }
+ i = 1;
+ } else {
+ if ((retval = ZLocateUser(user,&numlocs,auth?ZAUTH:ZNOAUTH)) != ZERR_NONE) {
+ com_err(whoami,retval,"while locating user %s",user);
+ exit(1);
+ }
+ print_locs(user,numlocs);
+ numfound += numlocs;
+ }
+ }
+
+ if (parallel) {
+#ifdef _POSIX_VERSION
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sa.sa_handler = timeout;
+ sigaction(SIGALRM, &sa, (struct sigaction *)0);
+#else
+ signal (SIGALRM, timeout);
+#endif
+ while (numleft-- > 0) {
+ alarm(SRV_TIMEOUT);
+ if ((retval = ZReceiveNotice(¬ice, NULL)) != ZERR_NONE) {
+ com_err(whoami,retval,"while searching notice queue");
+ continue;
+ }
+ if ((retval = ZParseLocations(¬ice, (ZAsyncLocateData_t *)NULL,
+ &numlocs, &whichuser)) != ZERR_NONE) {
+ com_err(whoami,retval,"while parsing locations");
+ continue;
+ }
+ if (numlocs >= 0) {
+ print_locs(whichuser,numlocs);
+ free(whichuser);
+ numfound += numlocs;
+ }
+ ZFreeNotice(¬ice);
+ }
+ }
+ return((numfound > 0) ? 0 : 1);
+}
--- /dev/null
+SHELL = /bin/sh
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+datadir=@datadir@
+sysconfdir=@sysconfdir@
+sbindir=@sbindir@
+lsbindir=@lsbindir@
+
+includedir=${prefix}/include
+mandir=${prefix}/man
+libdir=${exec_prefix}/lib
+bindir=${exec_prefix}/bin
+
+srcdir=@srcdir@
+top_srcdir=@top_srcdir@
+BUILDTOP=../..
+VPATH=@srcdir@
+CC=@CC@
+INSTALL=@INSTALL@
+
+CPPFLAGS=@CPPFLAGS@
+CFLAGS=@CFLAGS@
+ALL_CFLAGS=${CFLAGS} -I${top_srcdir}/h -I${BUILDTOP}/h -DKPOP ${CPPFLAGS}
+LDFLAGS=-L${BUILDTOP}/lib @LDFLAGS@
+LIBS=-lzephyr @LIBS@ -lcom_err
+
+OBJS= zmailnotify.o
+
+all: zmailnotify
+
+zmailnotify: ${OBJS} ${BUILDTOP}/lib/libzephyr.a
+ ${CC} ${LDFLAGS} -o $@ ${OBJS} ${LIBS}
+
+.c.o:
+ ${CC} -c ${ALL_CFLAGS} $<
+
+check:
+
+install: zmailnotify
+ ${INSTALL} -m 755 -s zmailnotify ${DESTDIR}${bindir}
+ ${INSTALL} -m 644 ${srcdir}/zmailnotify.1 ${DESTDIR}${mandir}/man1
+
+clean:
+ rm -f ${OBJS} zmailnotify
+
+${OBJS}: ${top_srcdir}/h/sysdep.h ${BUILDTOP}/h/config.h
+${OBJS}: ${BUILDTOP}/h/zephyr/zephyr.h ${BUILDTOP}/h/zephyr/zephyr_err.h
+
+.PHONY: all check install clean
+
--- /dev/null
+.\" $Id: zmailnotify.1,v 1.8 1999/01/22 23:18:38 ghudson Exp $
+.\"
+.\" Copyright 1988 by the Massachusetts Institute of Technology
+.\" All rights reserved. The file /usr/include/zephyr/mit-copyright.h
+.\" specifies the terms and conditions for redistribution.
+.\"
+.TH ZMAILNOTIFY 1 "July 8, 1988" "MIT Project Athena"
+.ds ]W MIT Project Athena
+.SH NAME
+zmailnotify \- retrieve mail headers from post office and transmit via Zephyr
+.SH SYNOPSIS
+.B zmailnotify
+.SH DESCRIPTION
+.I zmailnotify
+connects to the invoking user's POP post office and retrieves the From,
+To, and Subject fields of waiting messages, and then sends one Zephyr
+notice per message to class MAIL, instance POPRET, recipient
+.I <user@realm>,
+where <user@realm> is the invoking user's username concatenated with the
+local Zephyr realm (e.g. jruser@ATHENA.MIT.EDU).
+.PP
+.I zmailnotify
+is intended to be executed by
+.I zwgc(1)
+when notifications of spooled mail (such
+as those generated by \fIzpopnotify(8)\fR) are delivered.
+By default, these notifications are not delivered, nor are they acted
+upon. To receive the notices, you must subscribe to class MAIL,
+instance POP, recipient username@realm (see \fIzctl\fR(1)).
+Once you subscribe to these notices, by default they will be simply
+displayed on your display or terminal by
+.I zwgc(1).
+To have
+.I zmailnotify
+executed when they are delivered, you must copy the system default
+windowgram description file (\fI/usr/athena/lib/zephyr/zwgc.desc\fR) to your home
+directory as filename
+.I ~/.zwgc.desc
+and follow the directions contained in that file pertaining to
+.B MAIL NOTIFICATION.
+.PP
+.I zmailnotify
+only retrieves four headers at one time in order to reduce the
+competition for the mailbox lock on the post office. If more than four mail
+messages are waiting,
+.I zmailnotify
+will only retrieve the four most recent mail headers.
+.PP
+To check for mail already waiting when you log in, use the standard MH
+program
+.I msgchk(1).
+.SH BUGS
+.I zmailnotify
+only sends notification of mail which appears to be unique.
+Uniqueness is determined by comparing the To:, From:, and Subject:
+fields of the message with the last checked mail message; if the
+three fields match exactly, the new message is
+not considered unique. The To:, From:, and Subject: fields of the last
+message are stored in the file
+.IR $HOME/.maillock .
+.SH FILES
+$HOME/.maillock
+.SH SEE ALSO
+msgchk(1), zctl(1), zephyr(1), zwgc(1), zhm(8), zephyrd(8),
+zpopnotify(8), popd(8)
+.br
+Project Athena Technical Plan Section E.4.1, `Zephyr Notification
+Service'
+.SH AUTHOR
+.PP
+Robert S. French (MIT-Project Athena)
+.SH RESTRICTIONS
+Copyright (c) 1987,1988 by the Massachusetts Institute of Technology.
+All Rights Reserved.
+.br
+.I zephyr(1)
+specifies the terms and conditions for redistribution.
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains code for the "zmailnotify" command.
+ *
+ * Created by: Robert French
+ *
+ * $Id: zmailnotify.c,v 1.25 1997/10/25 21:47:11 ghudson Exp $
+ *
+ * Copyright (c) 1987,1993 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <sysdep.h>
+#include <zephyr/mit-copyright.h>
+#include <zephyr/zephyr.h>
+
+#ifndef lint
+static const char rcsid_zmailnotify_c[] =
+ "$Id: zmailnotify.c,v 1.25 1997/10/25 21:47:11 ghudson Exp $";
+#endif
+
+#include <sys/socket.h>
+#include <pwd.h>
+#include <netdb.h>
+#ifdef HAVE_HESIOD
+#include <hesiod.h>
+#endif
+
+#ifndef HAVE_KRB4
+#undef KPOP
+#endif
+
+#ifdef KPOP
+#include <krb.h>
+#endif
+
+#define NOTOK (-1)
+#define OK 0
+#define DONE 1
+
+FILE *sfi;
+FILE *sfo;
+char Errmsg[80];
+
+#ifdef KPOP
+char *PrincipalHostname();
+#endif
+
+void get_message(), pop_close(), mail_notify(), fatal_pop_err ();
+int pop_command __P((char *, ...));
+#define MAXMAIL 4
+
+struct _mail {
+ char *from;
+ char *to;
+ char *subj;
+} maillist[MAXMAIL];
+
+char *mailptr = NULL;
+char *prog = "zmailnotify";
+
+/* This entire program is a kludge - beware! */
+
+main(argc, argv)
+ char *argv[];
+{
+ FILE *lock;
+ int nmsgs;
+ char *user,response[512],lockfile[100];
+ char *host,*dir;
+ char *auth_cmd;
+ int i,nbytes,retval,uselock;
+ struct passwd *pwd;
+ struct _mail mymail;
+#ifdef HAVE_HESIOD
+ struct hes_postoffice *p;
+#endif
+
+ if (argv[0] && *argv[0])
+ prog = argv[0];
+
+ if ((retval = ZInitialize()) != ZERR_NONE) {
+ com_err(prog,retval,"while initializing");
+ exit(1);
+ }
+
+ dir = (char *)getenv("HOME");
+ user = (char *)getenv("USER");
+ if (!user || !dir) {
+ pwd = (struct passwd *)getpwuid((int) getuid());
+ if (!pwd) {
+ fprintf(stderr,"%s: Can't figure out who you are!\n",
+ prog);
+ exit(1);
+ }
+ if (!user)
+ user = pwd->pw_name;
+ if (!dir)
+ dir = pwd->pw_dir;
+ }
+ if (argc > 1)
+ user = argv[1];
+
+ (void) sprintf(lockfile,"%s/.maillock",dir);
+
+ host = (char *)getenv("MAILHOST");
+#ifdef HAVE_HESIOD
+ if (host == NULL) {
+ p = hes_getmailhost(user);
+ if (p != NULL && strcmp(p->po_type, "POP") == 0)
+ host = p->po_host;
+ else {
+ fprintf(stderr,
+ "%s: no POP server listed in Hesiod for %s\n",
+ prog, user);
+ exit(1);
+ }
+ }
+#endif
+ if (host == NULL) {
+ fprintf(stderr,"%s: no MAILHOST defined\n", prog);
+ exit(1);
+ }
+
+ lock = fopen(lockfile,"r+");
+#ifdef _POSIX_VERSION
+ if (lock) {
+ struct flock fl;
+
+ /* lock the whole file exclusively */
+ fl.l_type = F_WRLCK;
+ fl.l_whence = SEEK_SET;
+ fl.l_start = 0;
+ fl.l_len = 0;
+ (void) fcntl(fileno(lock),F_SETLKW,&fl);
+ }
+#else
+ if (lock)
+ (void) flock(fileno(lock),LOCK_EX);
+#endif
+
+ if (pop_init(host) == NOTOK) {
+ fprintf(stderr,"%s: %s\n",prog, Errmsg);
+ exit(1);
+ }
+
+ if ((getline(response, sizeof response, sfi) != OK) ||
+ (*response != '+')) {
+ fprintf(stderr,"%s: %s\n",prog,response);
+ exit(1);
+ }
+
+#ifdef KPOP
+ auth_cmd = "PASS %s";
+#else
+ auth_cmd = "RPOP %s";
+#endif
+ if (pop_command("USER %s", user) == NOTOK
+ || pop_command(auth_cmd, user) == NOTOK)
+ fatal_pop_err ();
+
+ if (pop_stat(&nmsgs, &nbytes) == NOTOK)
+ fatal_pop_err ();
+
+ if (!nmsgs) {
+ if (lock) {
+#ifdef _POSIX_VERSION
+ struct flock fl;
+
+ /* unlock the whole file */
+ fl.l_type = F_UNLCK;
+ fl.l_whence = SEEK_SET;
+ fl.l_start = 0;
+ fl.l_len = 0;
+ (void) fcntl(fileno(lock),F_SETLKW,&fl);
+#else
+ (void) flock(fileno(lock),LOCK_UN);
+#endif
+ (void) fclose(lock);
+ }
+ (void) unlink(lockfile);
+ (void) pop_command("QUIT");
+ pop_close();
+ exit (0);
+ }
+
+ uselock = 0;
+ if (lock) {
+ uselock = 1;
+ mymail.to = (char *)malloc(BUFSIZ);
+ mymail.from = (char *)malloc(BUFSIZ);
+ mymail.subj = (char *)malloc(BUFSIZ);
+ if (fgets(mymail.from,BUFSIZ,lock) != NULL)
+ mymail.from[strlen(mymail.from)-1] = 0;
+ else
+ mymail.from[0]=0;
+ if (fgets(mymail.to,BUFSIZ,lock) != NULL)
+ mymail.to[strlen(mymail.to)-1] = 0;
+ else
+ mymail.to[0] = 0;
+ if (fgets(mymail.subj,BUFSIZ,lock) != NULL)
+ mymail.subj[strlen(mymail.subj)-1] = 0;
+ else
+ mymail.subj[0] = 0;
+ }
+ else {
+ lock = fopen(lockfile,"w");
+#ifdef _POSIX_VERSION
+ if (lock) {
+ struct flock fl;
+
+ /* lock the whole file exclusively */
+ fl.l_type = F_WRLCK;
+ fl.l_whence = SEEK_SET;
+ fl.l_start = 0;
+ fl.l_len = 0;
+ (void) fcntl(fileno(lock),F_SETLKW,&fl);
+ }
+#else
+ if (lock)
+ (void) flock(fileno(lock),LOCK_EX);
+#endif
+ uselock = 0;
+ }
+
+ for (i=nmsgs;i>0;i--) {
+ if (nmsgs-i == MAXMAIL)
+ break;
+ if (get_mail(i,&maillist[nmsgs-i]))
+ exit (1);
+ if (uselock && (!strcmp(maillist[nmsgs-i].to,mymail.to) &&
+ !strcmp(maillist[nmsgs-i].from,mymail.from) &&
+ !strcmp(maillist[nmsgs-i].subj,mymail.subj)))
+ break;
+ }
+
+ (void) pop_command("QUIT");
+ pop_close();
+
+ i++;
+ for (;i<=nmsgs;i++)
+ mail_notify(&maillist[nmsgs-i]);
+ i--;
+ if (lock) {
+#ifdef _POSIX_VERSION
+ struct flock fl;
+
+ /* unlock the whole file */
+ fl.l_type = F_UNLCK;
+ fl.l_whence = SEEK_SET;
+ fl.l_start = 0;
+ fl.l_len = 0;
+ (void) fcntl(fileno(lock),F_SETLKW,&fl);
+#else
+ (void) flock(fileno(lock),LOCK_UN);
+#endif
+ (void) fclose(lock);
+ }
+ lock = fopen(lockfile,"w");
+ if (!lock)
+ exit (1);
+ fprintf(lock,"%s\n%s\n%s\n",
+ maillist[nmsgs-i].from,
+ maillist[nmsgs-i].to,
+ maillist[nmsgs-i].subj);
+ (void) fclose(lock);
+
+ exit(0);
+}
+
+void fatal_pop_err ()
+{
+ fprintf (stderr, "%s: %s\n", prog, Errmsg);
+ (void) pop_command ("QUIT");
+ pop_close ();
+ exit (1);
+}
+
+void get_message(i)
+ int i;
+{
+ int mbx_write();
+ if (pop_scan(i, mbx_write, 0) != OK)
+ fatal_pop_err ();
+}
+
+/* Pop stuff */
+
+void pop_close()
+{
+ if (sfi)
+ (void) fclose(sfi);
+ if (sfo)
+ (void) fclose(sfo);
+}
+
+get_mail(i,mail)
+ int i;
+ struct _mail *mail;
+{
+ char from[512],to[512],subj[512];
+ char *c,*ptr,*ptr2;
+
+ *from = 0;
+ *to = 0;
+ *subj = 0;
+
+ if (mailptr)
+ free(mailptr);
+
+ mailptr = 0;
+
+ get_message(i);
+
+ ptr = mailptr;
+ while (ptr) {
+ ptr2 = strchr(ptr,'\n');
+ if (ptr2)
+ *ptr2++ = 0;
+ if (*ptr == '\0')
+ break;
+ if (!strncmp(ptr, "From: ", 6))
+ (void) strcpy(from, ptr+6);
+ else if (!strncmp(ptr, "To: ", 4))
+ (void) strcpy(to, ptr+4);
+ else if (!strncmp(ptr, "Subject: ", 9))
+ (void) strcpy(subj, ptr+9);
+ ptr = ptr2;
+ }
+
+ /* add elipsis at end of "To:" field if it continues onto */
+ /* more than one line */
+ i = strlen(to) - 2;
+ c = to+i;
+ if (*c++ == ',') {
+ *c++ = ' ';
+ *c++ = '.';
+ *c++ = '.';
+ *c++ = '.';
+ *c++ = '\n';
+ *c = 0;
+ }
+
+ mail->from = (char *)malloc((unsigned)(strlen(from)+1));
+ (void) strcpy(mail->from,from);
+ mail->to = (char *)malloc((unsigned)(strlen(to)+1));
+ (void) strcpy(mail->to,to);
+ mail->subj = (char *)malloc((unsigned)(strlen(subj)+1));
+ (void) strcpy(mail->subj,subj);
+
+ return (0);
+}
+
+void
+mail_notify(mail)
+ struct _mail *mail;
+{
+ int retval;
+ char *fields[3];
+ ZNotice_t notice;
+
+ (void) memset((char *)¬ice, 0, sizeof(notice));
+ notice.z_kind = UNACKED;
+ notice.z_port = 0;
+ notice.z_class = "MAIL";
+ notice.z_class_inst = "POPRET";
+ notice.z_opcode = "NEW_MAIL";
+ notice.z_sender = 0;
+ notice.z_recipient = ZGetSender();
+ notice.z_default_format = "You have new mail:\n\nFrom: $1\nTo: $2\nSubject: $3";
+
+ fields[0] = mail->from;
+ fields[1] = mail->to;
+ fields[2] = mail->subj;
+
+ if ((retval = ZSendList(¬ice,fields,3,ZNOAUTH)) != ZERR_NONE)
+ com_err(prog,retval,"while sending notice");
+}
+
+/*
+ * These are the necessary KPOP routines snarfed from
+ * the GNU movemail program.
+ */
+
+pop_init(host)
+char *host;
+{
+ register struct hostent *hp;
+ register struct servent *sp;
+ int lport = IPPORT_RESERVED - 1;
+ struct sockaddr_in sin;
+ register int s;
+#ifdef KPOP
+ KTEXT ticket = (KTEXT)NULL;
+ int rem;
+ long authopts;
+ char *host_save;
+#endif
+ char *svc_name;
+
+ hp = gethostbyname(host);
+ if (hp == NULL) {
+ (void) sprintf(Errmsg, "MAILHOST unknown: %s", host);
+ return(NOTOK);
+ }
+
+
+#ifdef KPOP
+#ifdef ATHENA_COMPAT
+ svc_name = "knetd";
+#else
+ svc_name = "kpop";
+#endif
+#else
+ svc_name = "pop";
+#endif
+
+ sp = getservbyname (svc_name, "tcp");
+ if (sp == 0) {
+ (void) sprintf (Errmsg, "%s/tcp: unknown service", svc_name);
+ return NOTOK;
+ }
+ sin.sin_family = hp->h_addrtype;
+ (void) memcpy((char *)&sin.sin_addr, hp->h_addr, hp->h_length);
+ sin.sin_port = sp->s_port;
+#ifdef KPOP
+ s = socket(AF_INET, SOCK_STREAM, 0);
+#else
+ s = rresvport(&lport);
+#endif
+ if (s < 0) {
+ (void) sprintf(Errmsg, "error creating socket: %s", strerror(errno));
+ return(NOTOK);
+ }
+
+ if (connect(s, (struct sockaddr *)&sin, sizeof sin) < 0) {
+ (void) sprintf(Errmsg, "error during connect: %s", strerror(errno));
+ (void) close(s);
+ return(NOTOK);
+ }
+#ifdef KPOP
+ ticket = (KTEXT)malloc( sizeof(KTEXT_ST) );
+ rem=KSUCCESS;
+#ifdef ATHENA_COMPAT
+ authopts = KOPT_DO_OLDSTYLE;
+ rem = krb_sendsvc(s,"pop");
+ if (rem != KSUCCESS) {
+ (void) sprintf(Errmsg, "kerberos error: %s", krb_get_err_text(rem));
+ (void) close(s);
+ return(NOTOK);
+ }
+#else
+ authopts = 0L;
+#endif
+ host_save = malloc(strlen(hp->h_name) + 1);
+ if (!host_save) {
+ sprintf(Errmsg, "Out of memory.");
+ return(NOTOK);
+ }
+ strcpy(host_save, hp->h_name);
+ rem = krb_sendauth(authopts, s, ticket, "pop", host_save, (char *)0,
+ 0, (MSG_DAT *) 0, (CREDENTIALS *) 0,
+ (bit_64 *) 0, (struct sockaddr_in *)0,
+ (struct sockaddr_in *)0,"ZMAIL0.0");
+ free(host_save);
+ free(ticket);
+ if (rem != KSUCCESS) {
+ (void) sprintf(Errmsg, "kerberos error: %s",krb_get_err_text(rem));
+ (void) close(s);
+ return(NOTOK);
+ }
+#endif
+
+ sfi = fdopen(s, "r");
+ sfo = fdopen(s, "w");
+ if (sfi == NULL || sfo == NULL) {
+ (void) sprintf(Errmsg, "error in fdopen: %s", strerror(errno));
+ (void) close(s);
+ return(NOTOK);
+ }
+
+ return(OK);
+}
+
+#ifdef __STDC__
+pop_command(char *fmt, ...)
+#else
+pop_command(fmt, va_alist)
+ va_dcl
+#endif
+{
+ va_list args;
+ char buf[4096];
+
+ VA_START(args, fmt);
+ (void) vsprintf(buf, fmt, args);
+ va_end(args);
+
+ if (putline(buf, Errmsg, sfo) == NOTOK) return(NOTOK);
+
+ if (getline(buf, sizeof buf, sfi) != OK) {
+ (void) strcpy(Errmsg, buf);
+ return(NOTOK);
+ }
+
+ if (*buf != '+') {
+ (void) strcpy(Errmsg, buf);
+ return(NOTOK);
+ } else {
+ return(OK);
+ }
+}
+
+
+pop_stat(nmsgs, nbytes)
+int *nmsgs, *nbytes;
+{
+ char buf[4096];
+
+ if (putline("STAT", Errmsg, sfo) == NOTOK) return(NOTOK);
+
+ if (getline(buf, sizeof buf, sfi) != OK) {
+ (void) strcpy(Errmsg, buf);
+ return(NOTOK);
+ }
+
+ if (*buf != '+') {
+ (void) strcpy(Errmsg, buf);
+ return(NOTOK);
+ } else {
+ if (sscanf(buf, "+OK %d %d", nmsgs, nbytes) != 2)
+ return(NOTOK);
+ return(OK);
+ }
+}
+
+pop_scan(msgno, action, arg)
+int (*action)();
+{
+ char buf[4096];
+
+#ifdef HAVE_POP3_TOP
+ (void) sprintf(buf, "TOP %d 0", msgno);
+#else
+ (void) sprintf(buf, "RETR %d", msgno);
+#endif
+ if (putline(buf, Errmsg, sfo) == NOTOK) return(NOTOK);
+
+ if (getline(buf, sizeof buf, sfi) != OK) {
+ (void) strcpy(Errmsg, buf);
+ return(NOTOK);
+ }
+
+ while (1) {
+ switch (multiline(buf, sizeof buf, sfi)) {
+ case OK:
+ (*action)(buf, arg);
+ break;
+ case DONE:
+ return (OK);
+ case NOTOK:
+ (void) strcpy(Errmsg, buf);
+ return (NOTOK);
+ }
+ }
+}
+
+getline(buf, n, f)
+char *buf;
+register int n;
+FILE *f;
+{
+ register char *p;
+
+ p = fgets(buf, n, f);
+
+ if (ferror(f)) {
+ (void) strcpy(buf, "error on connection");
+ return (NOTOK);
+ }
+
+ if (p == NULL) {
+ (void) strcpy(buf, "connection closed by foreign host\n");
+ return (DONE);
+ }
+
+ p = buf + strlen(buf);
+ if (*--p == '\n') *p = '\0';
+ if (*--p == '\r') *p = '\0';
+ return(OK);
+}
+
+multiline(buf, n, f)
+char *buf;
+register int n;
+FILE *f;
+{
+ if (getline(buf, n, f) != OK) return (NOTOK);
+ if (*buf == '.') {
+ if (*(buf+1) == '\0') {
+ return (DONE);
+ } else {
+ (void) strcpy(buf, buf+1);
+ }
+ } else if (*buf == '\0') {
+ /* suck up all future lines, since this is after all only for headers */
+ while(! ((buf[0]=='.') && (buf[1] == '\0')) ) {
+ if (getline(buf, n, f) != OK) return (NOTOK);
+ }
+ return DONE;
+ }
+ return(OK);
+}
+
+putline(buf, err, f)
+char *buf;
+char *err;
+FILE *f;
+{
+ fprintf(f, "%s\r\n", buf);
+ (void) fflush(f);
+ if (ferror(f)) {
+ (void) strcpy(err, "lost connection");
+ return(NOTOK);
+ }
+ return(OK);
+}
+
+/*ARGSUSED*/
+mbx_write(line, dummy)
+char *line;
+int dummy; /* for consistency with pop_scan */
+{
+ if (mailptr) {
+ mailptr = (char *)realloc(mailptr,(unsigned)(strlen(mailptr)+strlen(line)+2));
+ (void) strcat(mailptr,line);
+ }
+ else {
+ mailptr = (char *)malloc((unsigned)(strlen(line)+2));
+ (void) strcpy(mailptr,line);
+ }
+ (void) strcat(mailptr,"\n");
+ return(0);
+}
--- /dev/null
+SHELL = /bin/sh
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+datadir=@datadir@
+sysconfdir=@sysconfdir@
+sbindir=@sbindir@
+lsbindir=@lsbindir@
+
+includedir=${prefix}/include
+mandir=${prefix}/man
+libdir=${exec_prefix}/lib
+bindir=${exec_prefix}/bin
+
+srcdir=@srcdir@
+top_srcdir=@top_srcdir@
+BUILDTOP=../..
+VPATH=@srcdir@
+CC=@CC@
+INSTALL=@INSTALL@
+
+CPPFLAGS=@CPPFLAGS@
+CFLAGS=@CFLAGS@
+ALL_CFLAGS=${CFLAGS} -I${top_srcdir}/h -I${BUILDTOP}/h ${CPPFLAGS}
+LDFLAGS=-L${BUILDTOP}/lib @LDFLAGS@
+LIBS=-lzephyr @LIBS@ -lcom_err
+
+OBJS= znol.o
+
+all: znol
+
+znol: ${OBJS} ${BUILDTOP}/lib/libzephyr.a
+ ${CC} ${LDFLAGS} -o $@ ${OBJS} ${LIBS}
+
+.c.o:
+ ${CC} -c ${ALL_CFLAGS} $<
+
+check:
+
+install: znol
+ ${INSTALL} -m 755 -s znol ${DESTDIR}${bindir}
+ ${INSTALL} -m 644 ${srcdir}/znol.1 ${DESTDIR}${mandir}/man1
+
+clean:
+ rm -f ${OBJS} znol
+
+${OBJS}: ${top_srcdir}/h/sysdep.h ${BUILDTOP}/h/config.h
+${OBJS}: ${BUILDTOP}/h/zephyr/zephyr.h ${BUILDTOP}/h/zephyr/zephyr_err.h
+
+.PHONY: all check install clean
+
--- /dev/null
+.\" $Id: znol.1,v 1.9 1999/01/22 23:18:41 ghudson Exp $
+.\"
+.\" Copyright 1987,1988 by the Massachusetts Institute of Technology
+.\" All rights reserved. The file /usr/include/zephyr/mit-copyright.h
+.\" specifies the terms and conditions for redistribution.
+.\"
+.\" @(#)znol.1 6.1 (MIT) 7/9/87
+.\"
+.TH ZNOL 1 "July 1, 1988" "MIT Project Athena"
+.ds ]W MIT Project Athena
+.SH NAME
+znol \- notify via Zephyr upon login or logout of interesting people
+.SH SYNOPSIS
+.B znol
+[
+.BI on|off
+] [
+.BI \-f \ file
+] [
+.BI \-u \ username
+] [
+.BI \-l
+] [
+.BI \-q
+]
+.SH DESCRIPTION
+.I Znol
+provides a way for you to be notified when "interesting" people log in
+or out. It uses the
+.I Zephyr(1)
+Notification Service, which causes a message to appear on your screen
+for every person specified in a namelist (which defaults to
+$HOME/.anyone). The namelist should have one login name per line. Any
+line starting with `#' is considered a comment and ignored.
+Anyone in the namelist who is logged in when
+.I znol
+is executed is printed to stdout. The control arguments are as
+follows:
+.TP 12
+.B on|off
+Turns notification on or off.
+.TP
+.B \-f
+The namelist file is taken to be
+.I file.
+If
+.I file
+is "-", then the standard input is used instead of a file. If
+.I file
+does not exist, an error message is printed, and
+.I znol
+exits.
+This option may not be used in conjunction with the
+.B \-u
+option.
+.TP
+.B \-l
+Causes
+.I znol
+to just list the people in the namelist who are currently logged in,
+without subscribing to the login messages. This option may not be used
+in conjunction with the
+.BI \-q
+option.
+.TP
+.B \-q
+Disables printing who is currently logged in when subscribing. This
+option may not be used in conjunction with the
+.BI \-l
+option.
+.TP
+.B \-u
+Instead of reading a file to specify the "interesting" users, the next
+argument is used as the only "interesting" user. This option may not be
+used in conjunction with the
+.B \-f
+option.
+.SH EXAMPLES
+.nf
+.in +.5in
+znol
+.in -.5in
+.fi
+reads the standard namelist file, prints the locations of any users
+named therein which can be found on the system, and enters subscriptions
+for notices about those users.
+.nf
+.in +.5in
+znol -l -u foo
+.in -.5in
+.fi
+prints the location (if visible) of the user 'foo'.
+.SH FILES
+$HOME/.anyone
+.SH SEE ALSO
+anyone(SIPB), nol(SIPB), zctl(1), zephyr(1), zwgc(1), zhm(8), zephyrd(8)
+.br
+Project Athena Technical Plan Section E.4.1, `Zephyr Notification
+Service'
+.SH AUTHOR
+.PP
+Robert S. French (MIT-Project Athena)
+.sp
+.SH RESTRICTIONS
+Copyright (c) 1987,1988 by the Massachusetts Institute of Technology.
+All Rights Reserved.
+.br
+.I zephyr(1)
+specifies the terms and conditions for redistribution.
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains code for the "znol" command.
+ *
+ * Created by: Robert French
+ *
+ * $Id: znol.c,v 1.16 1999/10/15 04:59:55 ghudson Exp $
+ *
+ * Copyright (c) 1987 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <sysdep.h>
+#include <zephyr/zephyr.h>
+
+#include <pwd.h>
+
+#ifndef lint
+static const char rcsid_znol_c[] = "$Id: znol.c,v 1.16 1999/10/15 04:59:55 ghudson Exp $";
+#endif
+
+#define SUBSATONCE 7
+#define ON 1
+#define OFF 0
+
+main(argc,argv)
+ int argc;
+ register char *argv[];
+{
+ register char *cp;
+ ZSubscription_t subs[SUBSATONCE];
+ ZLocations_t locations;
+ FILE *fp;
+ struct passwd *pwd;
+ char anyonename[BUFSIZ],name[BUFSIZ],cleanname[BUFSIZ],*envptr;
+ char *comment_ptr;
+ int onoff = ON,quiet = 0,justlist = 0,useronly = 0, filenamed = 0;
+ int retval,arg,ind,one,numlocs,i;
+ int wgport;
+
+ if ((retval = ZInitialize()) != ZERR_NONE) {
+ com_err(argv[0],retval,"initializing");
+ exit (1);
+ }
+
+ for (arg=1;arg<argc;arg++) {
+ if (!strcmp(argv[arg],"on")) {
+ onoff = ON;
+ continue;
+ }
+ if (!strcmp(argv[arg],"off")) {
+ onoff = OFF;
+ continue;
+ }
+ if (argv[arg][0] == '-') {
+ char opt = argv[arg][1];
+ if (opt == 0 || argv[arg][2] != 0)
+ goto usage;
+ switch (argv[arg][1]) {
+ case 'q':
+ quiet = 1;
+ break;
+ case 'l':
+ justlist = 1;
+ break;
+ case 'f':
+ if (arg == argc-1) {
+ fprintf(stderr,"No file name specified\n");
+ goto usage;
+ }
+ (void) strcpy(anyonename,argv[++arg]);
+ filenamed = 1;
+ break;
+ case 'u':
+ if (arg == argc-1) {
+ fprintf(stderr,"No username specified\n");
+ goto usage;
+ }
+ (void) strcpy(cleanname,argv[++arg]);
+ useronly = 1;
+ break;
+ default:
+ goto usage;
+ }
+ continue;
+ }
+ usage:
+ fprintf(stderr,"Usage: %s [on|off] [-q | -l] [-f file | -u username]\n", argv[0]);
+ exit (1);
+ }
+
+ if (quiet && justlist) {
+ fprintf(stderr,"-q and -l cannot both be used\n");
+ goto usage;
+ }
+ if (useronly && filenamed) {
+ fprintf(stderr,"-u and -f cannot both be used\n");
+ goto usage;
+ }
+ if (!justlist)
+ if ((wgport = ZGetWGPort()) == -1) {
+ com_err(argv[0],errno,"while getting WindowGram port");
+ exit(1);
+ }
+
+ if (!useronly) {
+ /* If no filename specified, get the default */
+ if (!filenamed) {
+ envptr = getenv("HOME");
+ if (envptr)
+ (void) strcpy(anyonename,envptr);
+ else {
+ if (!(pwd = getpwuid((int) getuid()))) {
+ fprintf(stderr,"You are not listed in the password file.\n");
+ exit (1);
+ }
+ (void) strcpy(anyonename,pwd->pw_dir);
+ }
+ (void) strcat(anyonename,"/.anyone");
+ }
+
+ /* if the filename is "-", read stdin */
+ if (strcmp(anyonename,"-") == 0) {
+ fp = stdin;
+ } else if (!(fp = fopen(anyonename,"r"))) {
+ fprintf(stderr,"Can't open %s for input\n",anyonename);
+ exit (1);
+ }
+ }
+
+ ind = 0;
+
+ for (;;) {
+ if (!useronly) {
+ if (!fgets(cleanname,sizeof cleanname,fp))
+ break;
+ if (cleanname[0] == '#' || cleanname[0] == '\0' ||
+ cleanname[0] == '\n')
+ continue; /* ignore comment and empty lines */
+ if (comment_ptr = strchr(cleanname, '#'))
+ *comment_ptr = '\0'; /* Ignore from # onwards */
+ /* Get rid of old-style nol entries, just in case */
+ cp = cleanname + strlen(cleanname) - 1;
+ *cp = '\0';
+ while(*--cp == ' ')
+ *cp = '\0';
+ if (*cleanname == '@' || !*cleanname)
+ continue;
+ } else if (ind)
+ break; /* only do the one name */
+
+ subs[ind].zsub_class = LOGIN_CLASS;
+ (void) strcpy(name,cleanname);
+ if (!strchr(name,'@')) {
+ cp = name + strlen(name);
+ *cp++ = '@';
+ (void) strcpy(cp,ZGetRealm());
+ }
+ if ((subs[ind].zsub_classinst = malloc((unsigned)(strlen(name)+1))) == NULL) {
+ fprintf (stderr, "znol: out of memory");
+ exit (1);
+ }
+ (void) strcpy(subs[ind].zsub_classinst, name);
+ subs[ind++].zsub_recipient = "";
+
+ if (!quiet && onoff == ON) {
+ if ((retval = ZLocateUser(name,&numlocs,ZAUTH))
+ != ZERR_NONE) {
+ com_err(argv[0],retval,"locating user");
+ exit(1);
+ }
+ one = 1;
+ if (numlocs) {
+ for (i=0;i<numlocs;i++) {
+ if ((retval =
+ ZGetLocations(&locations,&one))
+ != ZERR_NONE) {
+ com_err(argv[0],retval,
+ "getting location");
+ continue;
+ }
+ if (one != 1) {
+ printf("%s: internal failure while getting location\n",argv[0]);
+ exit(1);
+ }
+ printf("%s: %s\t%s\t%s\n",cleanname,
+ locations.host,
+ locations.tty,
+ locations.time);
+ }
+ }
+ }
+
+ if (ind == SUBSATONCE) {
+ if (!justlist)
+ if ((retval = (onoff==ON)?
+ ZSubscribeTo(subs,ind,(u_short)wgport):
+ ZUnsubscribeTo(subs,ind,(u_short)wgport)) !=
+ ZERR_NONE) {
+ com_err(argv[0],retval,(onoff==ON)?
+ "subscribing":
+ "unsubscribing");
+ exit(1);
+ }
+ for (ind=0;ind<SUBSATONCE;ind++)
+ free(subs[ind].zsub_classinst);
+ ind = 0;
+ }
+ }
+
+ if (ind && !justlist)
+ if ((retval = (onoff==ON)?
+ ZSubscribeTo(subs,ind,(u_short)wgport):
+ ZUnsubscribeTo(subs,ind,(u_short)wgport)) !=
+ ZERR_NONE) {
+ com_err(argv[0],retval,(onoff==ON)?
+ "subscribing":
+ "unsubscribing");
+ exit(1);
+ }
+
+ if (!useronly)
+ (void) fclose(fp); /* file is open read-only,
+ ignore errs */
+ exit(0);
+}
--- /dev/null
+SHELL = /bin/sh
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+datadir=@datadir@
+sysconfdir=@sysconfdir@
+sbindir=@sbindir@
+lsbindir=@lsbindir@
+
+includedir=${prefix}/include
+mandir=${prefix}/man
+libdir=${exec_prefix}/lib
+bindir=${exec_prefix}/bin
+
+srcdir=@srcdir@
+top_srcdir=@top_srcdir@
+BUILDTOP=../..
+VPATH=@srcdir@
+CC=@CC@
+INSTALL=@INSTALL@
+
+CPPFLAGS=@CPPFLAGS@
+CFLAGS=@CFLAGS@
+ALL_CFLAGS=${CFLAGS} -I${top_srcdir}/h -I${BUILDTOP}/h ${CPPFLAGS}
+LDFLAGS=-L${BUILDTOP}/lib @LDFLAGS@
+LIBS=-lzephyr @LIBS@ -lcom_err
+
+OBJS= zpopnotify.o
+
+all: zpopnotify
+
+zpopnotify: ${OBJS} ${BUILDTOP}/lib/libzephyr.a
+ ${CC} ${LDFLAGS} -o $@ ${OBJS} ${LIBS}
+
+.c.o:
+ ${CC} -c ${ALL_CFLAGS} $<
+
+check:
+
+install: zpopnotify
+ ${INSTALL} -m 755 -s zpopnotify ${DESTDIR}${sbindir}
+ ${INSTALL} -m 644 ${srcdir}/zpopnotify.8 ${DESTDIR}${mandir}/man8
+
+clean:
+ rm -f ${OBJS} zpopnotify
+
+${OBJS}: ${top_srcdir}/h/sysdep.h ${BUILDTOP}/h/config.h
+${OBJS}: ${BUILDTOP}/h/zephyr/zephyr.h ${BUILDTOP}/h/zephyr/zephyr_err.h
+
+.PHONY: all check install clean
+
--- /dev/null
+.\" $Id: zpopnotify.8,v 1.6 1999/01/22 23:18:43 ghudson Exp $
+.\"
+.\" Copyright 1988 by the Massachusetts Institute of Technology
+.\" All rights reserved. The file /usr/include/zephyr/mit-copyright.h
+.\" specifies the terms and conditions for redistribution.
+.\"
+.TH ZPOPNOTIFY 8 "July 8, 1988" "MIT Project Athena"
+.ds ]W MIT Project Athena
+.SH NAME
+zpopnotify \- notify users of newly spooled mail via Zephyr
+.SH SYNOPSIS
+.B zpopnotify
+.BI username
+.SH DESCRIPTION
+.I zpopnotify
+sends a Zephyr notice announcing the delivery of new mail to class MAIL,
+instance POP, recipient
+.I <user@realm>,
+where <user@realm> is the command-line specified
+.BI username
+concatenated with the local Zephyr realm (e.g. "zpopnotify jruser" would
+send to recipient jruser@ATHENA.MIT.EDU at Project Athena).
+The body of the message contains the official hostname of the sending
+host.
+.SH SEE ALSO
+zephyr(1), zwgc(1), zmailnotify(1), zhm(8), zephyrd(8), popd(8), spop(8)
+.br
+Project Athena Technical Plan Section E.4.1, `Zephyr Notification
+Service'
+.SH AUTHOR
+.PP
+Robert S. French (MIT-Project Athena)
+.SH RESTRICTIONS
+Copyright (c) 1987,1988 by the Massachusetts Institute of Technology.
+All Rights Reserved.
+.br
+.I zephyr(1)
+specifies the terms and conditions for redistribution.
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains code for the "zpopnotify" command.
+ *
+ * Created by: Robert French
+ *
+ * $Id: zpopnotify.c,v 1.13 1999/08/13 00:19:41 danw Exp $
+ *
+ * Copyright (c) 1987,1988 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+
+#include <sysdep.h>
+#include <zephyr/mit-copyright.h>
+#include <zephyr/zephyr.h>
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <sys/param.h> /* for MAXHOSTNAMELEN */
+#include <com_err.h>
+#include <errno.h>
+
+#ifndef lint
+static char rcsid_zpopnotify_c[] = "$Id: zpopnotify.c,v 1.13 1999/08/13 00:19:41 danw Exp $";
+#endif /* lint */
+
+#define MAIL_CLASS "MAIL"
+#define MAIL_INSTANCE "POP"
+
+void usage();
+
+main(argc,argv)
+ int argc;
+ char *argv[];
+{
+ ZNotice_t notice;
+ struct hostent *hent;
+ int retval;
+ register int i;
+ char *whoami,myhost[MAXHOSTNAMELEN],mysender[BUFSIZ];
+ char *lines[2];
+
+ whoami = argv[0];
+
+ if ((retval = ZInitialize()) != ZERR_NONE) {
+ com_err(whoami,retval,"while initializing");
+ exit(1);
+ }
+
+ if (argc < 2) {
+ usage(whoami);
+ exit(1);
+ }
+
+ if (gethostname(myhost,MAXHOSTNAMELEN) == -1) {
+ com_err(whoami,errno,"Can't get hostname");
+ exit(1);
+ }
+ myhost[MAXHOSTNAMELEN-1] = '\0';
+
+ if (!(hent = gethostbyname(myhost))) {
+ com_err(whoami,errno,"Can't get canonical hostname");
+ exit(1);
+ }
+
+ (void) strncpy(myhost,hent->h_name,MAXHOSTNAMELEN);
+ myhost[MAXHOSTNAMELEN-1] = '\0';
+
+ lines[0] = myhost;
+ lines[1] = "You have new mail.";
+
+ (void) strcpy(mysender,"pop@");
+ (void) strcat(mysender,ZGetRealm());
+
+ for (i = 1; i < argc; i++) {
+ (void) memset((char *)¬ice, 0, sizeof(notice));
+ notice.z_kind = UNSAFE;
+ notice.z_class = MAIL_CLASS;
+ notice.z_class_inst = MAIL_INSTANCE;
+ notice.z_opcode = "";
+ notice.z_sender = mysender;
+ notice.z_default_format = "From Post Office $1:\n$2";
+
+ /* in case it's a mailbox name (pathname), strip to username */
+ notice.z_recipient = (char *)strrchr(argv[i],'/');
+ if (notice.z_recipient)
+ notice.z_recipient++;
+ else
+ notice.z_recipient = argv[i];
+
+ if ((retval = ZSendList(¬ice,lines,2,ZNOAUTH)) != ZERR_NONE) {
+ com_err(whoami,retval,"while sending notice");
+ exit(1);
+ }
+ }
+}
+
+void
+usage(whoami)
+ char *whoami;
+{
+ printf("Usage: %s username [ username ... ]\n",whoami);
+}
--- /dev/null
+SHELL = /bin/sh
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+datadir=@datadir@
+sysconfdir=@sysconfdir@
+sbindir=@sbindir@
+lsbindir=@lsbindir@
+
+includedir=${prefix}/include
+mandir=${prefix}/man
+libdir=${exec_prefix}/lib
+bindir=${exec_prefix}/bin
+
+srcdir=@srcdir@
+top_srcdir=@top_srcdir@
+BUILDTOP=../..
+VPATH=@srcdir@
+CC=@CC@
+INSTALL=@INSTALL@
+
+CPPFLAGS=@CPPFLAGS@
+CFLAGS=@CFLAGS@
+ALL_CFLAGS=${CFLAGS} -I${top_srcdir}/h -I${BUILDTOP}/h ${CPPFLAGS}
+LDFLAGS=-L${BUILDTOP}/lib @LDFLAGS@
+LIBS=-lzephyr @LIBS@ -lcom_err
+
+OBJS= zshutdown_notify.o
+
+all: zshutdown_notify
+
+zshutdown_notify: ${OBJS} ${BUILDTOP}/lib/libzephyr.a
+ ${CC} ${LDFLAGS} -o $@ ${OBJS} ${LIBS}
+
+.c.o:
+ ${CC} -c ${ALL_CFLAGS} $<
+
+check:
+
+install: zshutdown_notify
+ ${INSTALL} -m 755 -s zshutdown_notify ${DESTDIR}${lsbindir}
+ ${INSTALL} -m 644 ${srcdir}/zshutdown_notify.8 ${DESTDIR}${mandir}/man8
+
+clean:
+ rm -f ${OBJS} zshutdown_notify
+
+${OBJS}: ${top_srcdir}/h/sysdep.h ${BUILDTOP}/h/config.h
+${OBJS}: ${BUILDTOP}/h/zephyr/zephyr.h ${BUILDTOP}/h/zephyr/zephyr_err.h
+
+.PHONY: all check install clean
+
--- /dev/null
+.\" $Id: zshutdown_notify.8,v 1.5 1999/01/22 23:18:46 ghudson Exp $
+.\"
+.\" Copyright 1988 by the Massachusetts Institute of Technology
+.\" All rights reserved. The file /usr/include/zephyr/mit-copyright.h
+.\" specifies the terms and conditions for redistribution.
+.\"
+.TH ZSHUTDOWN_NOTIFY 8 "July 8, 1988" "MIT Project Athena"
+.ds ]W MIT Project Athena
+.SH NAME
+zshutdown_notify \- notify users of impending shutdown via Zephyr
+.SH SYNOPSIS
+.B zshutdown_notify
+.SH DESCRIPTION
+.I zshutdown_notify
+reads the standard input until EOF is reached, and then sends a Zephyr
+notice with the official hostname, the collected input, and a warning
+message as the message body of the notice.
+The notice is sent to class FILSRV, instance
+.I <hostname>,
+recipient "*", where <hostname> is the official hostname of the sending host.
+.PP
+Any users who are using resources on the sending host are expected to
+subscribe to these messages so they will be warned of any impending
+shutdowns.
+.PP
+.I zshutdown_notify
+is usually invoked from within
+.I /etc/shutdown.
+.SH SEE ALSO
+attach(1), zephyr(1), zwgc(1), shutdown(8), zhm(8), zephyrd(8)
+.br
+Project Athena Technical Plan Section E.4.1, `Zephyr Notification
+Service'
+.SH AUTHOR
+.PP
+C. Anthony Della Fera (Digital Equipment Corporation-Project Athena)
+.sp
+.SH RESTRICTIONS
+Copyright (c) 1987,1988 by the Massachusetts Institute of Technology.
+All Rights Reserved.
+.br
+.I zephyr(1)
+specifies the terms and conditions for redistribution.
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains code for "zshutdown_notify", a utility called by
+ * shutdown(8) to do Zephyr notification on shutdown.
+ *
+ * Created by: C. Anthony Della Fera
+ *
+ * $Id: zshutdown_notify.c,v 1.14 1997/10/25 21:47:15 ghudson Exp $
+ *
+ * Copyright (c) 1987, 1993 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <sysdep.h>
+#include <zephyr/mit-copyright.h>
+#include <zephyr/zephyr.h>
+
+#include <sys/socket.h>
+#include <netdb.h>
+
+#ifndef lint
+static const char *rcsid_zshutdown_notify_c =
+ "$Id: zshutdown_notify.c,v 1.14 1997/10/25 21:47:15 ghudson Exp $";
+#endif
+
+#define N_KIND UNSAFE
+#define N_CLASS "FILSRV"
+#define N_OPCODE "SHUTDOWN"
+#define N_DEF_FORMAT "From $sender:\n@bold(Shutdown message from $1 at $time)\n@center(System going down, message is:)\n\n$2\n\n@center(@bold($3))"
+#define N_FIELD_CNT 3
+
+#ifdef HAVE_KRB4
+#define SVC_NAME "rcmd"
+#endif
+
+/*
+ * Standard warning strings appended as extra fields to
+ * the message body.
+ */
+
+static char warning[] = "Please detach any filesystems you may have\nattached from this host by typing detach -host %s";
+
+/*ARGSUSED*/
+main(argc,argv)
+ int argc;
+ char *argv[];
+{
+ ZNotice_t notice;
+ struct hostent *hp;
+ int retval;
+ char hostname[MAXHOSTNAMELEN];
+ char msgbuff[BUFSIZ], message[Z_MAXPKTLEN], *ptr;
+ char scratch[BUFSIZ];
+ char *msg[N_FIELD_CNT];
+#ifdef HAVE_KRB4
+ char tkt_filename[MAXPATHLEN];
+ char rlm[REALM_SZ];
+ char hn2[MAXHOSTNAMELEN];
+ char *cp;
+ extern char *krb_get_phost();
+#endif
+
+ if (gethostname(hostname, MAXHOSTNAMELEN) < 0) {
+ com_err(argv[0], errno, "while finding hostname");
+ exit(1);
+ }
+
+ if ((hp = gethostbyname(hostname)) != NULL)
+ (void) strcpy(hostname, hp->h_name);
+
+ msg[0] = hostname;
+ msg[1] = message;
+ sprintf(scratch, warning, hostname);
+ msg[2] = scratch;
+
+#ifdef HAVE_KRB4
+ (void) sprintf(tkt_filename, "/tmp/tkt_zshut_%d", getpid());
+ krb_set_tkt_string(tkt_filename);
+
+ cp = krb_get_phost(hostname);
+ if (cp)
+ (void) strcpy(hn2, cp);
+ else {
+ fprintf(stderr, "%s: can't figure out canonical hostname\n",argv[0]);
+ exit(1);
+ }
+ if (retval = krb_get_lrealm(rlm, 1)) {
+ fprintf(stderr, "%s: can't get local realm: %s\n",
+ argv[0], krb_get_err_text(retval));
+ exit(1);
+ }
+ if (retval = krb_get_svc_in_tkt(SVC_NAME, hn2, rlm,
+ SERVER_SERVICE, SERVER_INSTANCE, 1,
+ KEYFILE)) {
+ fprintf(stderr, "%s: can't get tickets: %s\n",
+ argv[0], krb_get_err_text(retval));
+ exit(1);
+ }
+#endif
+
+ if ((retval = ZInitialize()) != ZERR_NONE) {
+ com_err(argv[0], retval, "while initializing");
+ exit(1);
+ }
+
+ ptr = message;
+
+ for (;;) {
+ if (!fgets(msgbuff, sizeof(msgbuff), stdin))
+ break;
+ if ((strlen(msgbuff) + (ptr - message)) > Z_MAXPKTLEN){
+ break;
+ }
+ (void) strcpy(ptr, msgbuff);
+ ptr += strlen(ptr);
+ }
+
+ (void) memset((char *)¬ice, 0, sizeof(notice));
+
+ notice.z_kind = N_KIND;
+ notice.z_port = 0;
+ notice.z_class = N_CLASS;
+ notice.z_class_inst = hostname;
+ notice.z_opcode = N_OPCODE;
+ notice.z_sender = 0;
+ notice.z_message_len = 0;
+ notice.z_recipient = "";
+ notice.z_default_format = N_DEF_FORMAT;
+
+ retval = ZSendList(¬ice, msg, N_FIELD_CNT, ZAUTH);
+#ifdef HAVE_KRB4
+ (void) dest_tkt();
+#endif
+
+ if (retval != ZERR_NONE) {
+ com_err(argv[0], retval, "while sending notice");
+ exit(1);
+ }
+ return 0;
+}
--- /dev/null
+SHELL = /bin/sh
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+datadir=@datadir@
+sysconfdir=@sysconfdir@
+sbindir=@sbindir@
+lsbindir=@lsbindir@
+
+includedir=${prefix}/include
+mandir=${prefix}/man
+libdir=${exec_prefix}/lib
+bindir=${exec_prefix}/bin
+
+srcdir=@srcdir@
+top_srcdir=@top_srcdir@
+BUILDTOP=../..
+VPATH=@srcdir@
+CC=@CC@
+INSTALL=@INSTALL@
+
+CPPFLAGS=@CPPFLAGS@
+CFLAGS=@CFLAGS@
+ALL_CFLAGS=${CFLAGS} -I${top_srcdir}/h -I${BUILDTOP}/h ${CPPFLAGS}
+LDFLAGS=-L${BUILDTOP}/lib @LDFLAGS@
+LIBS=-lzephyr @LIBS@ -lcom_err
+
+OBJS= zstat.o
+
+all: zstat
+
+zstat: ${OBJS} ${BUILDTOP}/lib/libzephyr.a
+ ${CC} ${LDFLAGS} -o $@ ${OBJS} ${LIBS}
+
+.c.o:
+ ${CC} -c ${ALL_CFLAGS} $<
+
+check:
+
+install: zstat
+ ${INSTALL} -m 755 -s zstat ${DESTDIR}${bindir}
+ ${INSTALL} -m 644 ${srcdir}/zstat.8 ${DESTDIR}${mandir}/man8
+
+clean:
+ rm -f ${OBJS} zstat
+
+${OBJS}: zserver.h ${top_srcdir}/h/sysdep.h ${BUILDTOP}/h/config.h
+${OBJS}: ${BUILDTOP}/h/zephyr/zephyr.h ${BUILDTOP}/h/zephyr/zephyr_err.h
+
+.PHONY: all check install clean
+
--- /dev/null
+#ifndef __ZSERVER_H__
+#define __ZSERVER_H__
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains declarations for use in the server.
+ *
+ * Created by: John T. Kohl
+ *
+ * $Id: zserver.h,v 1.33 1999/01/22 23:18:48 ghudson Exp $
+ *
+ * Copyright (c) 1987,1988 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#define ADMIN_HELLO "HELLO" /* Opcode: hello, are you there */
+#define ADMIN_IMHERE "IHEARDYOU" /* Opcode: yes, I am here */
+#define ADMIN_SHUTDOWN "GOODBYE" /* Opcode: I am shutting down */
+#define ADMIN_BDUMP "DUMP_AVAIL" /* Opcode: I will give you a dump */
+#define ADMIN_DONE "DUMP_DONE" /* Opcode: brain dump for this server
+ is complete */
+#define ADMIN_NEWCLT "NEXT_CLIENT" /* Opcode: this is a new client */
+#define ADMIN_LOST_CLT "LOST_CLIENT" /* Opcode: client not ack'ing */
+#define ADMIN_KILL_CLT "KILL_CLIENT" /* Opcode: client is dead, remove */
+#define ADMIN_STATUS "STATUS" /* Opcode: please send status */
+
+#endif /* !__ZSERVER_H__ */
--- /dev/null
+.\" $Id: zstat.8,v 1.6 1999/01/22 23:18:49 ghudson Exp $
+.\"
+.\" Copyright 1987,1988 by the Massachusetts Institute of Technology
+.\" All rights reserved. The file /usr/include/zephyr/mit-copyright.h
+.\" specifies the terms and conditions for redistribution.
+.\"
+.\"
+.TH ZSTAT 8 "July 1, 1988" "MIT Project Athena"
+.ds ]W MIT Project Athena
+.SH NAME
+zstat \- display Zephyr statistics
+.SH SYNOPSIS
+.B zstat
+[
+.BI -h
+] [
+.BI -s
+] [
+.BI host \ ...
+]
+.SH DESCRIPTION
+.I Zstat
+is used to display statistics reported by a HostManager,
+.I zhm(8),
+or a Zephyr Server
+.I zephyrd(8),
+or both.
+.TP 12
+.B \-h
+is used to indicate that only HostManager statistics should be displayed.
+.TP
+.B \-s
+is used to indicate that only server statistics should be displayed.
+.TP
+If no hosts are specified, the current host is assumed.
+When both HostManager and server statistics are displayed,
+statistics from the current server for each host are displayed.
+.SH SEE ALSO
+zephyr(1), zhm(8), zephyrd(8)
+.br
+Project Athena Technical Plan Section E.4.1, `Zephyr Notification
+Service'
+.SH AUTHOR
+.PP
+David C. Jedlinsky (MIT-Project Athena)
+.SH RESTRICTIONS
+Copyright (c) 1987,1988 by the Massachusetts Institute of Technology.
+All Rights Reserved.
+.br
+.I zephyr(1)
+specifies the terms and conditions for redistribution.
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains the zstat program.
+ *
+ * Created by: David C. Jedlinsky
+ *
+ * $Id: zstat.c,v 1.21 1999/08/13 00:18:50 danw Exp $
+ *
+ * Copyright (c) 1987,1988 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+/* There should be library interfaces for the operations in zstat; for now,
+ * however, zstat is more or less internal to the Zephyr system. */
+#include <internal.h>
+
+#include <sys/socket.h>
+#include "zserver.h"
+
+#if !defined(lint) && !defined(SABER)
+static const char rcsid_zstat_c[] = "$Id: zstat.c,v 1.21 1999/08/13 00:18:50 danw Exp $";
+#endif
+
+const char *hm_head[] = {
+ "Current server =",
+ "Items in queue:",
+ "Client packets received:",
+ "Server packets received:",
+ "Server changes:",
+ "Version:",
+ "Looking for a new server:",
+ "Time running:",
+ "Size:",
+ "Machine type:"
+};
+#define HM_SIZE (sizeof(hm_head) / sizeof (char *))
+const char *srv_head[] = {
+ "Current server version =",
+ "Packets handled:",
+ "Uptime:",
+ "Server states:",
+};
+#define SRV_SIZE (sizeof(srv_head) / sizeof (char *))
+
+int outoftime = 0;
+
+int serveronly = 0,hmonly = 0;
+u_short srv_port;
+
+void usage(), do_stat();
+
+RETSIGTYPE timeout()
+{
+ outoftime = 1;
+}
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ Code_t ret;
+ char hostname[MAXHOSTNAMELEN];
+ int optchar;
+ struct servent *sp;
+ extern char *optarg;
+ extern int optind;
+
+ if ((ret = ZInitialize()) != ZERR_NONE) {
+ com_err("zstat", ret, "initializing");
+ exit(-1);
+ }
+
+ if ((ret = ZOpenPort((u_short *)0)) != ZERR_NONE) {
+ com_err("zstat", ret, "opening port");
+ exit(-1);
+ }
+
+ while ((optchar = getopt(argc, argv, "sh")) != EOF) {
+ switch(optchar) {
+ case 's':
+ serveronly++;
+ break;
+ case 'h':
+ hmonly++;
+ break;
+ case '?':
+ default:
+ usage(argv[0]);
+ exit(1);
+ }
+ }
+
+ if (serveronly && hmonly) {
+ fprintf(stderr,"Only one of -s and -h may be specified\n");
+ exit(1);
+ }
+
+ sp = getservbyname(SERVER_SVCNAME,"udp");
+ srv_port = (sp) ? sp->s_port : SERVER_SVC_FALLBACK;
+
+ if (optind == argc) {
+ if (gethostname(hostname, MAXHOSTNAMELEN) < 0) {
+ com_err("zstat",errno,"while finding hostname");
+ exit(-1);
+ }
+ do_stat(hostname);
+ exit(0);
+ }
+
+ for (;optind<argc;optind++)
+ do_stat(argv[optind]);
+
+ exit(0);
+}
+
+void
+do_stat(host)
+ char *host;
+{
+ char srv_host[MAXHOSTNAMELEN];
+
+ if (serveronly) {
+ (void) srv_stat(host);
+ return;
+ }
+
+ if (hm_stat(host,srv_host))
+ return;
+
+ if (!hmonly)
+ (void) srv_stat(srv_host);
+}
+
+int
+hm_stat(host,server)
+ char *host,*server;
+{
+ struct in_addr inaddr;
+ Code_t code;
+
+ char *line[20],*mp;
+ int i,nf;
+ struct hostent *hp;
+ time_t runtime;
+ struct tm *tim;
+ ZNotice_t notice;
+#ifdef _POSIX_VERSION
+ struct sigaction sa;
+#endif
+
+ if ((inaddr.s_addr = inet_addr(host)) == (unsigned)(-1)) {
+ if ((hp = gethostbyname(host)) == NULL) {
+ fprintf(stderr,"Unknown host: %s\n",host);
+ exit(-1);
+ }
+ (void) memcpy((char *) &inaddr, hp->h_addr, hp->h_length);
+
+ printf("Hostmanager stats: %s\n", hp->h_name);
+ } else {
+ printf("Hostmanager stats: %s\n", host);
+ }
+
+ if ((code = ZhmStat(&inaddr, ¬ice)) != ZERR_NONE) {
+ com_err("zstat", code, "getting hostmanager status");
+ exit(-1);
+ }
+
+ mp = notice.z_message;
+ for (nf=0;mp<notice.z_message+notice.z_message_len;nf++) {
+ line[nf] = mp;
+ mp += strlen(mp)+1;
+ }
+
+ (void) strcpy(server,line[0]);
+
+ printf("HostManager protocol version = %s\n",notice.z_version);
+
+ for (i=0; (i < nf) && (i < HM_SIZE); i++) {
+ if (!strncmp("Time",hm_head[i],4)) {
+ runtime = atol(line[i]);
+ tim = gmtime(&runtime);
+ printf("%s %d days, %02d:%02d:%02d\n", hm_head[i],
+ tim->tm_yday,
+ tim->tm_hour,
+ tim->tm_min,
+ tim->tm_sec);
+ }
+ else
+ printf("%s %s\n",hm_head[i],line[i]);
+ }
+
+ printf("\n");
+
+ ZFreeNotice(¬ice);
+ return(0);
+}
+
+int
+srv_stat(host)
+ char *host;
+{
+ char *line[20],*mp;
+ int sock,i,nf,ret;
+ struct hostent *hp;
+ struct sockaddr_in sin;
+ ZNotice_t notice;
+ time_t runtime;
+ struct tm *tim;
+#ifdef _POSIX_VERSION
+ struct sigaction sa;
+#endif
+
+ (void) memset((char *) &sin, 0, sizeof(struct sockaddr_in));
+
+ sin.sin_port = srv_port;
+
+ if ((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
+ perror("socket:");
+ exit(-1);
+ }
+
+ sin.sin_family = AF_INET;
+
+ if ((sin.sin_addr.s_addr = inet_addr(host)) == (unsigned)(-1)) {
+ if ((hp = gethostbyname(host)) == NULL) {
+ fprintf(stderr,"Unknown host: %s\n",host);
+ exit(-1);
+ }
+ (void) memcpy((char *) &sin.sin_addr, hp->h_addr, hp->h_length);
+
+ printf("Server stats: %s\n", hp->h_name);
+ } else {
+ printf("Server stats: %s\n", host);
+ }
+
+ (void) memset((char *)¬ice, 0, sizeof(notice));
+ notice.z_kind = UNSAFE;
+ notice.z_port = 0;
+ notice.z_class = ZEPHYR_ADMIN_CLASS;
+ notice.z_class_inst = "";
+ notice.z_opcode = ADMIN_STATUS;
+ notice.z_sender = "";
+ notice.z_recipient = "";
+ notice.z_default_format = "";
+ notice.z_message_len = 0;
+
+ if ((ret = ZSetDestAddr(&sin)) != ZERR_NONE) {
+ com_err("zstat", ret, "setting destination");
+ exit(-1);
+ }
+ if ((ret = ZSendNotice(¬ice, ZNOAUTH)) != ZERR_NONE) {
+ com_err("zstat", ret, "sending notice");
+ exit(-1);
+ }
+
+#ifdef _POSIX_VERSION
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sa.sa_handler = timeout;
+ (void) sigaction(SIGALRM, &sa, (struct sigaction *)0);
+#else
+ (void) signal(SIGALRM,timeout);
+#endif
+ outoftime = 0;
+ (void) alarm(10);
+ if (((ret = ZReceiveNotice(¬ice, (struct sockaddr_in *) 0))
+ != ZERR_NONE) &&
+ ret != EINTR) {
+ com_err("zstat", ret, "receiving notice");
+ return (1);
+ }
+ (void) alarm(0);
+ if (outoftime) {
+ fprintf(stderr,"No response after 10 seconds.\n");
+ return (1);
+ }
+
+ mp = notice.z_message;
+ for (nf=0;mp<notice.z_message+notice.z_message_len;nf++) {
+ line[nf] = mp;
+ mp += strlen(mp)+1;
+ }
+
+ printf("Server protocol version = %s\n",notice.z_version);
+
+ for (i=0; i < nf; i++) {
+ if (i < 2)
+ printf("%s %s\n",srv_head[i],line[i]);
+ else if (i == 2) { /* uptime field */
+ runtime = atol(line[i]);
+ tim = gmtime(&runtime);
+ printf("%s %d days, %02d:%02d:%02d\n",
+ srv_head[i],
+ tim->tm_yday,
+ tim->tm_hour,
+ tim->tm_min,
+ tim->tm_sec);
+ } else if (i == 3) {
+ printf("%s\n",srv_head[i]);
+ printf("%s\n",line[i]);
+ } else printf("%s\n",line[i]);
+ }
+ printf("\n");
+
+ (void) close(sock);
+ ZFreeNotice(¬ice);
+ return(0);
+}
+
+void
+usage(s)
+ char *s;
+{
+ fprintf(stderr,"usage: %s [-s] [-h] [host ...]\n",s);
+ exit(1);
+}
--- /dev/null
+SHELL = /bin/sh
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+datadir=@datadir@
+sysconfdir=@sysconfdir@
+sbindir=@sbindir@
+lsbindir=@lsbindir@
+
+includedir=${prefix}/include
+mandir=${prefix}/man
+libdir=${exec_prefix}/lib
+bindir=${exec_prefix}/bin
+
+srcdir=@srcdir@
+top_srcdir=@top_srcdir@
+BUILDTOP=../..
+VPATH=@srcdir@
+CC=@CC@
+INSTALL=@INSTALL@
+
+CPPFLAGS=@CPPFLAGS@
+CFLAGS=@CFLAGS@
+ALL_CFLAGS=${CFLAGS} -I${top_srcdir}/h -I${BUILDTOP}/h ${CPPFLAGS}
+LDFLAGS=-L${BUILDTOP}/lib @LDFLAGS@
+LIBS=-lzephyr @LIBS@ -lcom_err
+
+OBJS= zwrite.o
+
+all: zwrite
+
+zwrite: ${OBJS} ${BUILDTOP}/lib/libzephyr.a
+ ${CC} ${LDFLAGS} -o $@ ${OBJS} ${LIBS}
+
+.c.o:
+ ${CC} -c ${ALL_CFLAGS} $<
+
+check:
+
+install: zwrite
+ ${INSTALL} -m 755 -s zwrite ${DESTDIR}${bindir}
+ ${INSTALL} -m 644 ${srcdir}/zwrite.1 ${DESTDIR}${mandir}/man1
+
+clean:
+ rm -f ${OBJS} zwrite
+
+${OBJS}: ${top_srcdir}/h/sysdep.h ${BUILDTOP}/h/config.h
+${OBJS}: ${BUILDTOP}/h/zephyr/zephyr.h ${BUILDTOP}/h/zephyr/zephyr_err.h
+
+.PHONY: all check install clean
+
--- /dev/null
+.\" $Id: zwrite.1,v 1.22 1999/03/11 04:21:53 danw Exp $
+.\"
+.\" Copyright 1987,1988 by the Massachusetts Institute of Technology
+.\" All rights reserved. The file /usr/include/zephyr/mit-copyright.h
+.\" specifies the terms and conditions for redistribution.
+.\"
+.\"
+.TH ZWRITE 1 "October 26, 1989" "MIT Project Athena"
+.ds ]W MIT Project Athena
+.SH NAME
+zwrite \- write to another user via Zephyr
+.SH SYNOPSIS
+.B zwrite
+[
+.BI -a
+] [
+.BI -d
+] [
+.BI -v
+] [
+.BI -q
+] [
+.BI -t
+] [
+.BI -u
+] [
+.BI -o
+] [
+.BI -n
+] [
+.BI -l
+] [
+.BI -C
+] [
+.BI -O
+opcode ] [
+.BI -s
+signature ] [
+.BI -c
+class ] [
+.BI -i
+instance ] [
+.BI -r
+realm ] [
+.BI -f
+arg ] [
+.BI user \ ...
+] [
+.BI -m
+.BI message
+]
+.SH DESCRIPTION
+.I Zwrite
+sends a message from you to another user through the
+.I zephyr(1)
+notification service. The user must have subscribed to messages of
+the appropriate class and instance using the
+.I zctl(1)
+program.
+.PP
+.I Zwrite
+understands the following options.
+.TP 12
+.B \-a
+Causes
+.I zwrite
+to send the message authenticated, using
+.I Kerberos
+to perform the authentication. This is the default.
+.TP
+.B \-d
+Causes
+.I zwrite
+to send the message unauthenticated.
+.TP
+.B \-v
+Causes
+.I zwrite
+to print what type of message it is sending, and whether or not it was
+successful.
+.TP
+.B \-q
+Forces
+.I zwrite
+to suppress information about whether or not the message was sent.
+.TP
+.B \-t
+Prevents
+.I zwrite
+from expanding tabs in the message into appropriate spaces. Normally
+.I zwrite
+will expand any tabs it finds into the appropriate number of spaces to
+maintain separation based on 8-character tab stops.
+.TP
+.B \-l
+Causes
+.I zwrite
+to ignore `.' on a line by itself in the input and only end a typed
+message when the user types the end-of-file character (usually
+control-D). When the input is not a terminal, this is the default action.
+.TP
+.B \-u
+Sends an urgent message. This changes the
+.I zephyr
+instance of the message to ``URGENT''.
+.TP
+.B \-o
+Causes
+.I zwrite
+to ignore the Zephyr variables
+.IR zwrite-class ,
+.IR zwrite-inst ,
+and
+.I zwrite-opcode
+when picking a default class, instance, and opcode.
+.TP
+.B \-n
+prevents
+.I zwrite
+from sending a PING message to verify the recipient is subscribing to
+messages. By default,
+.I zwrite
+will send a notice to the destination class, instance, and recipient,
+with opcode ``PING'' before sending the message. If the PING is sent,
+and the server response indicates there are no recipients subscribed to
+your message,
+.I zwrite
+exits without sending a message.
+When the
+.B \-m
+option is used, no PING is sent.
+.TP
+.B \-C
+prepends a "CC:" line to the body of the message indicating the
+recipients of the message. This is strictly a convenience: the
+presence of a "CC:" line at the top of a zephyr body does not
+necessarily indicate that this option was used, or that the message
+really was sent to the listed users, and its lack doesn't indicate
+that the message was not sent to multiple users.
+.TP
+.B \-s \fIsignature\fR
+sets the signature to be included in the message. This overrides both
+the user's name (as found in the password file) and any
+setting of the Zephyr variable
+.IR zwrite-signature .
+.I signature
+must
+be a single argument, hence when using a shell it should be quoted with
+double quotes. A
+.I signature
+argument of "" leaves the signature in
+the message empty.
+.TP
+.B \-c \fIclass\fR
+Allows a user to specify a different class for the message. This allows
+a message to be sent to a large group of people with some degree of
+security. See
+.I zephyr(1)
+and
+.I zephyrd(8)
+for a description of how to restrict access to classes. When this option
+is specified, the message is sent to recipient "*" unless an additional
+list of recipients is specified.
+.br
+This argument may not be used in conjunction with the -f option.
+.TP
+.B \-i \fIinstance\fR
+Allows a user to specify a different instance than the default.
+When this option is used, the message is sent to recipient "*" unless an
+additional list of recipients is specified. This allows a message to be
+sent to a large group of people (e.g. a development group) just by having
+the members subscribe to messages of class "MESSAGE", the specified instance,
+and recipient "*".
+.br
+This argument may not be used in conjunction with the -f option.
+.TP
+.B \-r \fIrealm\fR
+Allows a user to specify a different realm for the message, if the
+server supports interrealm Zephyr.
+.TP
+.B \-F \fIformat\fR
+Allows a user to specify a different default format for the message.
+.TP
+.B \-O \fIopcode\fR
+Allows a user to specify a different opcode for the message.
+Some Zephyr notice display programs may use the opcode to decide how
+to display a notice.
+.TP
+.B \-f \fIarg\fR
+Allows a user to specify an argument to be interpreted as a filesystem
+specification. The class is set to
+.BR FILSRV .
+he instance is set
+to
+.I arg
+as modified:
+If
+.I arg
+contains no colons (`:'), it is assumed to
+be a host name, and it is converted into an official host name via
+.I gethostbyname(3).
+If
+.I arg
+contains a colon, the portion preceding the colon is
+treated as a host name, and the colon and any trailing characters are
+appended to the offical host name returned by
+.I gethostbyname.
+If the name fails to resolve into an official host name, the instance is
+set to
+.I arg
+unmodified.
+.br
+This option may not be used in conjunction with the -c or -i option.
+.TP
+.B \-m
+.I Zwrite
+sends the remaining arguments on the command line as the message.
+.PP
+If the
+.I \-m
+option is not specified, the user is prompted for the message to be
+sent. The message may be terminated by typing ^D or ``.'' on a line
+by itself.
+.PP
+The default class for messages is ``MESSAGE'', the default instance
+is ``PERSONAL'', andthe default opcode is ``'' (an empty string).
+These defaults can be overridden by setting the Zephyr
+variables
+.IR zwrite-class ,
+.IR zwrite-inst ,
+and
+.IR zwrite-opcode ,
+respectively.
+Command-line options can override the defaults.
+.PP
+If the class is ``MESSAGE'' and the instance is either ``PERSONAL'' or
+``URGENT'', a recipient must be specified. These comparisons are
+case-sensitive.
+.PP
+Unless the
+.B \-s
+option is used, the contents of the Zephyr variable
+.I zwrite-signature
+are used to augment the user's username in the
+message. If
+.I zwrite-signature
+is not set and the
+.B \-s
+option is not specified, the user's full name (as specified in the
+password file) is used instead.
+.SH BUGS
+Tab expansion should really be done by the receiver of the message.
+.br
+The \-u option is provided for compatibility with old versions of
+.I zwrite
+and is not necessarily useful for sending messages to users who do not
+have old subscription files.
+.SH SEE ALSO
+kerberosintro(1), zctl(1), zephyr(1), zwgc(1), zhm(8), zephyrd(8),
+gethostbyname(3)
+.br
+Project Athena Technical Plan Section E.4.1, `Zephyr Notification
+Service'
+.SH FILES
+/etc/passwd
+.br
+$HOME/.zephyr.vars
+.SH AUTHOR
+.PP
+.br
+Robert S. French (MIT-Project Athena)
+.SH RESTRICTIONS
+Copyright (c) 1987,1988 by the Massachusetts Institute of Technology.
+All Rights Reserved.
+.br
+.I zephyr(1)
+specifies the terms and conditions for redistribution.
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains code for the "zwrite" command.
+ *
+ * Created by: Robert French
+ *
+ * $Id: zwrite.c,v 1.49 1999/08/13 00:19:42 danw Exp $
+ *
+ * Copyright (c) 1987,1988 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <sysdep.h>
+#include <zephyr/mit-copyright.h>
+#include <zephyr/zephyr.h>
+#include <netdb.h>
+#include <pwd.h>
+
+#ifndef lint
+static const char rcsid_zwrite_c[] = "$Id: zwrite.c,v 1.49 1999/08/13 00:19:42 danw Exp $";
+#endif /* lint */
+
+#define DEFAULT_CLASS "MESSAGE"
+#define DEFAULT_INSTANCE "PERSONAL"
+#define URGENT_INSTANCE "URGENT"
+#define DEFAULT_OPCODE ""
+#define FILSRV_CLASS "FILSRV"
+
+#define MAXRECIPS 100
+
+int nrecips, msgarg, verbose, quiet, nodot, cc;
+char *whoami, *inst, *class, *opcode, *realm, *recips[MAXRECIPS];
+Z_AuthProc auth;
+void un_tabify();
+
+char *fix_filsrv_inst();
+void usage(), send_off();
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int retval, arg, nocheck, nchars, msgsize, filsys, tabexpand;
+ char *message, *signature = NULL, *format = NULL;
+ static char bfr[BUFSIZ], classbfr[BUFSIZ], instbfr[BUFSIZ], sigbfr[BUFSIZ];
+ static char opbfr[BUFSIZ];
+ static ZNotice_t notice;
+
+ whoami = argv[0];
+
+ if ((retval = ZInitialize()) != ZERR_NONE) {
+ com_err(whoami, retval, "while initializing");
+ exit(1);
+ }
+
+ if (argc < 2)
+ usage(whoami);
+
+ auth = ZAUTH;
+ verbose = quiet = msgarg = nrecips = nocheck = filsys = nodot = 0;
+ tabexpand = 1;
+
+ if (class = ZGetVariable("zwrite-class")) {
+ (void) strcpy(classbfr, class);
+ class = classbfr;
+ }
+ else
+ class = DEFAULT_CLASS;
+ if (inst = ZGetVariable("zwrite-inst")) {
+ (void) strcpy(instbfr, inst);
+ inst = instbfr;
+ }
+ else
+ inst = DEFAULT_INSTANCE;
+
+ if (opcode = ZGetVariable("zwrite-opcode"))
+ opcode = strcpy(opbfr, opcode);
+ else
+ opcode = DEFAULT_OPCODE;
+
+ signature = ZGetVariable("zwrite-signature");
+ if (signature) {
+ (void) strcpy(sigbfr, signature);
+ signature = sigbfr;
+ }
+
+ arg = 1;
+
+ for (;arg<argc&&!msgarg;arg++) {
+ if (*argv[arg] != '-') {
+ recips[nrecips++] = argv[arg];
+ continue;
+ }
+ if (strlen(argv[arg]) > 2)
+ usage(whoami);
+ switch (argv[arg][1]) {
+ case 'a': /* Backwards compatibility */
+ auth = ZAUTH;
+ break;
+ case 'o':
+ class = DEFAULT_CLASS;
+ inst = DEFAULT_INSTANCE;
+ opcode = DEFAULT_OPCODE;
+ break;
+ case 'd':
+ auth = ZNOAUTH;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 'q':
+ quiet = 1;
+ break;
+ case 'n':
+ nocheck = 1;
+ break;
+ case 't':
+ tabexpand = 0;
+ break;
+ case 'u':
+ inst = URGENT_INSTANCE;
+ break;
+ case 'O':
+ if (arg == argc-1)
+ usage(whoami);
+ arg++;
+ opcode = argv[arg];
+ break;
+ case 'i':
+ if (arg == argc-1 || filsys == 1)
+ usage(whoami);
+ arg++;
+ inst = argv[arg];
+ filsys = -1;
+ break;
+ case 'c':
+ if (arg == argc-1 || filsys == 1)
+ usage(whoami);
+ arg++;
+ class = argv[arg];
+ filsys = -1;
+ break;
+ case 'f':
+ if (arg == argc-1 || filsys == -1)
+ usage(whoami);
+ arg++;
+ class = FILSRV_CLASS;
+ inst = fix_filsrv_inst(argv[arg]);
+ filsys = 1;
+ nocheck = 1; /* implied -n (no ping) */
+ break;
+ case 's':
+ if (arg == argc-1)
+ usage(whoami);
+ arg++;
+ signature = argv[arg];
+ break;
+ case 'm':
+ if (arg == argc-1)
+ usage(whoami);
+ nocheck = 1; /* implied -n (no ping) */
+ msgarg = arg+1;
+ break;
+ case 'l': /* literal */
+ nodot = 1;
+ break;
+ case 'F':
+ if (arg == argc-1)
+ usage(whoami);
+ arg++;
+ format = argv[arg];
+ break;
+ case 'r':
+ if (arg == argc-1)
+ usage(whoami);
+ arg++;
+ realm = argv[arg];
+ break;
+ case 'C':
+ cc = 1;
+ break;
+ default:
+ usage(whoami);
+ }
+ }
+
+ if (!nrecips && !(strcmp(class, DEFAULT_CLASS) ||
+ (strcmp(inst, DEFAULT_INSTANCE) &&
+ strcmp(inst, URGENT_INSTANCE)))) {
+ /* must specify recipient if using default class and
+ (default instance or urgent instance) */
+ fprintf(stderr, "No recipients specified.\n");
+ usage(whoami);
+ }
+
+ if (!signature) {
+ /* try to find name in the password file */
+ register struct passwd *pwd;
+ register char *cp = sigbfr;
+ register char *cp2, *pp;
+
+ pwd = getpwuid(getuid());
+ if (pwd) {
+ cp2 = pwd->pw_gecos;
+ for (; *cp2 && *cp2 != ',' ; cp2++) {
+ if (*cp2 == '&') {
+ pp = pwd->pw_name;
+ *cp++ = islower(*pp) ? toupper(*pp) : *pp;
+ pp++;
+ while (*pp)
+ *cp++ = *pp++;
+ } else
+ *cp++ = *cp2;
+ }
+ signature = sigbfr;
+ }
+ }
+
+ notice.z_kind = ACKED;
+ notice.z_port = 0;
+ notice.z_class = class;
+ notice.z_class_inst = inst;
+ notice.z_opcode = "PING";
+ notice.z_sender = 0;
+ notice.z_message_len = 0;
+ notice.z_recipient = "";
+ if (format)
+ notice.z_default_format = format;
+ else if (filsys == 1)
+ notice.z_default_format = "@bold(Filesystem Operation Message for $instance:)\nFrom: @bold($sender) at $time $date\n$message";
+ else if (auth == ZAUTH) {
+ if (signature)
+ notice.z_default_format = "Class $class, Instance $instance:\nTo: @bold($recipient) at $time $date\nFrom: @bold($1) <$sender>\n\n$2";
+ else
+ notice.z_default_format = "Class $class, Instance $instance:\nTo: @bold($recipient) at $time $date\n$message";
+ } else {
+ if (signature)
+ notice.z_default_format = "@bold(UNAUTHENTIC) Class $class, Instance $instance at $time $date:\nFrom: @bold($1) <$sender>\n\n$2";
+ else
+ notice.z_default_format = "@bold(UNAUTHENTIC) Class $class, Instance $instance at $time $date:\n$message";
+ }
+ if (!nocheck && nrecips)
+ send_off(¬ice, 0);
+
+ if (!msgarg && isatty(0))
+ if (nodot)
+ printf("Type your message now. End with the end-of-file character.\n");
+ else
+ printf("Type your message now. End with control-D or a dot on a line by itself.\n");
+
+ message = NULL;
+ msgsize = 0;
+ if (signature) {
+ message = malloc((unsigned)(strlen(signature)+2));
+ (void) strcpy(message, signature);
+ msgsize = strlen(message);
+ message[msgsize++] = '\0';
+ } else {
+ message = malloc(1);
+ message[msgsize++] = '\0';
+ }
+
+ if (cc && nrecips > 1) {
+ int size = msgsize;
+ for (arg=0;arg<nrecips;arg++)
+ size += (strlen(recips[arg]) + 2);
+ size += 6; /* for the newlines and "cc: " */
+ message = realloc(message, (unsigned) size);
+ (void) strcpy(message+msgsize, "CC: ");
+ msgsize += 4;
+ for (arg=0;arg<nrecips;arg++) {
+ (void) strcpy(message+msgsize, recips[arg]);
+ msgsize += strlen(recips[arg]);
+ if (arg != nrecips-1) {
+ message[msgsize] = ' ';
+ msgsize++;
+ }
+ }
+ message[msgsize] = '\n';
+ msgsize += 1;
+ }
+
+ if (msgarg) {
+ int size = msgsize;
+ for (arg=msgarg;arg<argc;arg++)
+ size += (strlen(argv[arg]) + 1);
+ size++; /* for the newline */
+ message = realloc(message, (unsigned) size);
+ for (arg=msgarg;arg<argc;arg++) {
+ (void) strcpy(message+msgsize, argv[arg]);
+ msgsize += strlen(argv[arg]);
+ if (arg != argc-1) {
+ message[msgsize] = ' ';
+ msgsize++;
+ }
+ }
+ message[msgsize] = '\n';
+ msgsize += 1;
+ } else {
+ if (isatty(0)) {
+ for (;;) {
+ unsigned int l;
+ if (!fgets(bfr, sizeof bfr, stdin))
+ break;
+ if (!nodot && bfr[0] == '.' &&
+ (bfr[1] == '\n' || bfr[1] == '\0'))
+ break;
+ l = strlen(bfr);
+ message = realloc(message, msgsize+l+1);
+ (void) strcpy(message+msgsize, bfr);
+ msgsize += l;
+ }
+ message = realloc(message, (unsigned)(msgsize+1));
+ }
+ else { /* Use read so you can send binary messages... */
+ while (nchars = read(fileno(stdin), bfr, sizeof bfr)) {
+ if (nchars == -1) {
+ fprintf(stderr, "Read error from stdin! Can't continue!\n");
+ exit(1);
+ }
+ message = realloc(message, (unsigned)(msgsize+nchars));
+ (void) memcpy(message+msgsize, bfr, nchars);
+ msgsize += nchars;
+ }
+ /* end of msg */
+ message = realloc(message, (unsigned)(msgsize+1));
+ }
+ }
+
+ notice.z_opcode = opcode;
+ if (tabexpand)
+ un_tabify(&message, &msgsize);
+ notice.z_message = message;
+ notice.z_message_len = msgsize;
+
+ send_off(¬ice, 1);
+ exit(0);
+}
+
+void
+send_off(notice, real)
+ ZNotice_t *notice;
+ int real;
+{
+ int i, success, retval;
+ char bfr[BUFSIZ], realm_recip[BUFSIZ], dest[3 * BUFSIZ], *cp;
+ ZNotice_t retnotice;
+
+ success = 0;
+
+ for (i=0;i<nrecips || !nrecips;i++) {
+ if (realm) {
+ sprintf(realm_recip, "%s@%s", (nrecips) ? recips[i] : "", realm);
+ notice->z_recipient = realm_recip;
+ } else {
+ notice->z_recipient = (nrecips) ? recips[i] : "";
+ }
+ if (nrecips)
+ strcpy(dest, recips[i]);
+ else if (!strcmp(class, DEFAULT_CLASS))
+ sprintf(dest, "instance \"%s\"", inst);
+ else if (!strcmp(inst, DEFAULT_INSTANCE))
+ sprintf(dest, "class \"%s\"", class);
+ else
+ sprintf(dest, "class \"%s\", instance \"%s\"", class, inst);
+ if (verbose && real)
+ printf("Sending %smessage, class %s, instance %s, to %s\n",
+ auth?"authenticated ":"",
+ class, inst,
+ nrecips?notice->z_recipient:"everyone");
+ if ((retval = ZSendNotice(notice, auth)) != ZERR_NONE) {
+ (void) sprintf(bfr, "while sending notice to %s", dest);
+ com_err(whoami, retval, bfr);
+ break;
+ }
+ if (real && !quiet) {
+ if (verbose)
+ printf("Queued... ");
+ else
+ printf("Message queued for %s... ", dest);
+ fflush(stdout);
+ }
+ if ((retval = ZIfNotice(&retnotice, (struct sockaddr_in *) 0,
+ ZCompareUIDPred,
+ (char *)¬ice->z_uid)) !=
+ ZERR_NONE) {
+ ZFreeNotice(&retnotice);
+ (void) sprintf(bfr, "while waiting for acknowledgement for %s",
+ dest);
+ com_err(whoami, retval, bfr);
+ continue;
+ }
+ if (retnotice.z_kind == SERVNAK) {
+ if (!quiet) {
+ printf("Received authorization failure while sending to %s\n",
+ dest);
+ }
+ ZFreeNotice(&retnotice);
+ break; /* if auth fails, punt */
+ }
+ if (retnotice.z_kind != SERVACK || !retnotice.z_message_len) {
+ if (!quiet) {
+ printf("Detected server failure while receiving acknowledgement for %s\n",
+ dest);
+ }
+ ZFreeNotice(&retnotice);
+ continue;
+ }
+ if (!strcmp(retnotice.z_message, ZSRVACK_SENT)) {
+ success = 1;
+ if (real && !quiet)
+ printf("sent\n");
+ } else if (!strcmp(retnotice.z_message, ZSRVACK_NOTSENT)) {
+ if (verbose && real && !quiet) {
+ if (strcmp(class, DEFAULT_CLASS)) {
+ fprintf(stderr, "Not logged in or not subscribing to class %s, instance %s\n",
+ class, inst);
+ } else {
+ fprintf(stderr,
+ "Not logged in or not subscribing to messages\n");
+ }
+ }
+ else if (!quiet) {
+ if (!nrecips) {
+ fprintf(stderr,
+ "No one subscribing to class %s, instance %s\n",
+ class, inst);
+ } else {
+ if (strcmp(class, DEFAULT_CLASS)) {
+ fprintf(stderr, "%s: Not logged in or not subscribing to class %s, instance %s\n",
+ notice->z_recipient, class, inst);
+ } else {
+ fprintf(stderr, "%s: Not logged in or not subscribing to messages\n",
+ notice->z_recipient);
+ }
+ }
+ }
+ }
+ else
+ printf("Internal failure - illegal message field in server response\n");
+ ZFreeNotice(&retnotice);
+ if (!nrecips)
+ break;
+ }
+ if (!success)
+ exit(1);
+}
+
+void
+usage(s)
+ char *s;
+{
+ fprintf(stderr,
+ "Usage: %s [-a] [-o] [-d] [-v] [-q] [-n] [-t] [-u] [-l]\n\
+\t[-c class] [-i inst] [-O opcode] [-f fsname] [-s signature] [-C]\n\
+\t[user ...] [-F format] [-r realm] [-m message]\n", s);
+ fprintf(stderr,"\t-f and -c are mutually exclusive\n\
+\t-f and -i are mutually exclusive\n\
+\trecipients must be specified unless -c or -f specifies a class\n\
+\tother than the default class or -i or -f specifies an instance\n\
+\tother than the default or urgent instance\n");
+ exit(1);
+}
+
+/*
+ if the -f option is specified, this routine is called to canonicalize
+ an instance of the form hostname[:pack]. It turns the hostname into the
+ name returned by gethostbyname(hostname)
+ */
+
+char *fix_filsrv_inst(str)
+char *str;
+{
+ static char fsinst[BUFSIZ];
+ char *ptr;
+ struct hostent *hp;
+
+ ptr = strchr(str,':');
+ if (ptr)
+ *ptr = '\0';
+
+ hp = gethostbyname(str);
+ if (!hp) {
+ if (ptr)
+ *ptr = ':';
+ return(str);
+ }
+ (void) strcpy(fsinst, hp->h_name);
+ if (ptr) {
+ (void) strcat(fsinst, ":");
+ ptr++;
+ (void) strcat(fsinst, ptr);
+ }
+ return(fsinst);
+}
+
+/* convert tabs in the buffer into appropriate # of spaces.
+ slightly tricky since the buffer can have NUL's in it. */
+
+#ifndef TABSTOP
+#define TABSTOP 8 /* #chars between tabstops */
+#endif /* ! TABSTOP */
+
+void
+un_tabify(bufp, sizep)
+char **bufp;
+register int *sizep;
+{
+ register char *cp, *cp2;
+ char *cp3;
+ register int i;
+ register int column; /* column of next character */
+ register int size = *sizep;
+
+ for (cp = *bufp, i = 0; size; size--, cp++)
+ if (*cp == '\t')
+ i++; /* count tabs in buffer */
+
+ if (!i)
+ return; /* no tabs == no work */
+
+ /* To avoid allocation churning, allocate enough extra space to convert
+ every tab into TABSTOP spaces */
+ /* only add (TABSTOP-1)x because we re-use the cell holding the
+ tab itself */
+ cp = malloc((unsigned)(*sizep + (i * (TABSTOP-1))));
+ if (!cp) /* XXX */
+ return; /* punt expanding if memory fails */
+ cp3 = cp;
+ /* Copy buffer, converting tabs to spaces as we go */
+ for (cp2 = *bufp, column = 1, size = *sizep; size; cp2++, size--) {
+ switch (*cp2) {
+ case '\n':
+ case '\0':
+ /* newline or null: reset column */
+ column = 1;
+ *cp++ = *cp2; /* copy the newline */
+ break;
+ default:
+ /* copy the character */
+ *cp = *cp2;
+ cp++;
+ column++;
+ break;
+ case '\t':
+ /* it's a tab, compute how many spaces to expand into. */
+ i = TABSTOP - ((column - 1) % TABSTOP);
+ for (; i > 0; i--) {
+ *cp++ = ' '; /* fill in the spaces */
+ column++;
+ (*sizep)++; /* increment the size */
+ }
+ (*sizep)--; /* remove one (we replaced the tab) */
+ break;
+ }
+ }
+ free(*bufp); /* free the old buf */
+ *bufp = cp3;
+ return;
+}
--- /dev/null
+#! /bin/sh
+# Attempt to guess a canonical system name.
+# Copyright (C) 1992, 93, 94, 95, 96, 97, 1998 Free Software Foundation, Inc.
+#
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Written by Per Bothner <bothner@cygnus.com>.
+# The master version of this file is at the FSF in /home/gd/gnu/lib.
+#
+# This script attempts to guess a canonical system name similar to
+# config.sub. If it succeeds, it prints the system name on stdout, and
+# exits with 0. Otherwise, it exits with 1.
+#
+# The plan is that this can be called by configure scripts if you
+# don't specify an explicit system type (host/target name).
+#
+# Only a few systems have been added to this list; please add others
+# (but try to keep the structure clean).
+#
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 8/24/94.)
+if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
+ PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+trap 'rm -f dummy.c dummy.o dummy; exit 1' 1 2 15
+
+# Note: order is significant - the case branches are not exclusive.
+
+case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
+ alpha:OSF1:*:*)
+ if test $UNAME_RELEASE = "V4.0"; then
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+ fi
+ # A Vn.n version is a released version.
+ # A Tn.n version is a released field test version.
+ # A Xn.n version is an unreleased experimental baselevel.
+ # 1.2 uses "1.2" for uname -r.
+ cat <<EOF >dummy.s
+ .globl main
+ .ent main
+main:
+ .frame \$30,0,\$26,0
+ .prologue 0
+ .long 0x47e03d80 # implver $0
+ lda \$2,259
+ .long 0x47e20c21 # amask $2,$1
+ srl \$1,8,\$2
+ sll \$2,2,\$2
+ sll \$0,3,\$0
+ addl \$1,\$0,\$0
+ addl \$2,\$0,\$0
+ ret \$31,(\$26),1
+ .end main
+EOF
+ ${CC-cc} dummy.s -o dummy 2>/dev/null
+ if test "$?" = 0 ; then
+ ./dummy
+ case "$?" in
+ 7)
+ UNAME_MACHINE="alpha"
+ ;;
+ 15)
+ UNAME_MACHINE="alphaev5"
+ ;;
+ 14)
+ UNAME_MACHINE="alphaev56"
+ ;;
+ 10)
+ UNAME_MACHINE="alphapca56"
+ ;;
+ 16)
+ UNAME_MACHINE="alphaev6"
+ ;;
+ esac
+ fi
+ rm -f dummy.s dummy
+ echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[VTX]//' | tr [[A-Z]] [[a-z]]`
+ exit 0 ;;
+ 21064:Windows_NT:50:3)
+ echo alpha-dec-winnt3.5
+ exit 0 ;;
+ Amiga*:UNIX_System_V:4.0:*)
+ echo m68k-cbm-sysv4
+ exit 0;;
+ amiga:NetBSD:*:*)
+ echo m68k-cbm-netbsd${UNAME_RELEASE}
+ exit 0 ;;
+ amiga:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ *:[Aa]miga[Oo][Ss]:*:*)
+ echo ${UNAME_MACHINE}-unknown-amigaos
+ exit 0 ;;
+ arc64:OpenBSD:*:*)
+ echo mips64el-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ arc:OpenBSD:*:*)
+ echo mipsel-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ hkmips:OpenBSD:*:*)
+ echo mips-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ pmax:OpenBSD:*:*)
+ echo mipsel-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ sgi:OpenBSD:*:*)
+ echo mips-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ wgrisc:OpenBSD:*:*)
+ echo mipsel-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+ echo arm-acorn-riscix${UNAME_RELEASE}
+ exit 0;;
+ arm32:NetBSD:*:*)
+ echo arm-unknown-netbsd`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+ exit 0 ;;
+ SR2?01:HI-UX/MPP:*:*)
+ echo hppa1.1-hitachi-hiuxmpp
+ exit 0;;
+ Pyramid*:OSx*:*:*|MIS*:OSx*:*:*|MIS*:SMP_DC-OSx*:*:*)
+ # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+ if test "`(/bin/universe) 2>/dev/null`" = att ; then
+ echo pyramid-pyramid-sysv3
+ else
+ echo pyramid-pyramid-bsd
+ fi
+ exit 0 ;;
+ NILE:*:*:dcosx)
+ echo pyramid-pyramid-svr4
+ exit 0 ;;
+ sun4H:SunOS:5.*:*)
+ echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+ echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ i86pc:SunOS:5.*:*)
+ echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ sun4*:SunOS:6*:*)
+ # According to config.sub, this is the proper way to canonicalize
+ # SunOS6. Hard to guess exactly what SunOS6 will be like, but
+ # it's likely to be more like Solaris than SunOS4.
+ echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ sun4*:SunOS:*:*)
+ case "`/usr/bin/arch -k`" in
+ Series*|S4*)
+ UNAME_RELEASE=`uname -v`
+ ;;
+ esac
+ # Japanese Language versions have a version number like `4.1.3-JL'.
+ echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
+ exit 0 ;;
+ sun3*:SunOS:*:*)
+ echo m68k-sun-sunos${UNAME_RELEASE}
+ exit 0 ;;
+ sun*:*:4.2BSD:*)
+ UNAME_RELEASE=`(head -1 /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+ test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
+ case "`/bin/arch`" in
+ sun3)
+ echo m68k-sun-sunos${UNAME_RELEASE}
+ ;;
+ sun4)
+ echo sparc-sun-sunos${UNAME_RELEASE}
+ ;;
+ esac
+ exit 0 ;;
+ aushp:SunOS:*:*)
+ echo sparc-auspex-sunos${UNAME_RELEASE}
+ exit 0 ;;
+ atari*:NetBSD:*:*)
+ echo m68k-atari-netbsd${UNAME_RELEASE}
+ exit 0 ;;
+ atari*:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ sun3*:NetBSD:*:*)
+ echo m68k-sun-netbsd${UNAME_RELEASE}
+ exit 0 ;;
+ sun3*:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mac68k:NetBSD:*:*)
+ echo m68k-apple-netbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mac68k:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mvme68k:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mvme88k:OpenBSD:*:*)
+ echo m88k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ powerpc:machten:*:*)
+ echo powerpc-apple-machten${UNAME_RELEASE}
+ exit 0 ;;
+ macppc:NetBSD:*:*)
+ echo powerpc-apple-netbsd${UNAME_RELEASE}
+ exit 0 ;;
+ RISC*:Mach:*:*)
+ echo mips-dec-mach_bsd4.3
+ exit 0 ;;
+ RISC*:ULTRIX:*:*)
+ echo mips-dec-ultrix${UNAME_RELEASE}
+ exit 0 ;;
+ VAX*:ULTRIX*:*:*)
+ echo vax-dec-ultrix${UNAME_RELEASE}
+ exit 0 ;;
+ 2020:CLIX:*:*)
+ echo clipper-intergraph-clix${UNAME_RELEASE}
+ exit 0 ;;
+ mips:*:*:UMIPS | mips:*:*:RISCos)
+ sed 's/^ //' << EOF >dummy.c
+ int main (argc, argv) int argc; char **argv; {
+ #if defined (host_mips) && defined (MIPSEB)
+ #if defined (SYSTYPE_SYSV)
+ printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_SVR4)
+ printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+ printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
+ #endif
+ #endif
+ exit (-1);
+ }
+EOF
+ ${CC-cc} dummy.c -o dummy \
+ && ./dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \
+ && rm dummy.c dummy && exit 0
+ rm -f dummy.c dummy
+ echo mips-mips-riscos${UNAME_RELEASE}
+ exit 0 ;;
+ Night_Hawk:Power_UNIX:*:*)
+ echo powerpc-harris-powerunix
+ exit 0 ;;
+ m88k:CX/UX:7*:*)
+ echo m88k-harris-cxux7
+ exit 0 ;;
+ m88k:*:4*:R4*)
+ echo m88k-motorola-sysv4
+ exit 0 ;;
+ m88k:*:3*:R3*)
+ echo m88k-motorola-sysv3
+ exit 0 ;;
+ AViiON:dgux:*:*)
+ # DG/UX returns AViiON for all architectures
+ UNAME_PROCESSOR=`/usr/bin/uname -p`
+ if [ $UNAME_PROCESSOR = mc88100 -o $UNAME_PROCESSOR = mc88110 ] ; then
+ if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx \
+ -o ${TARGET_BINARY_INTERFACE}x = x ] ; then
+ echo m88k-dg-dgux${UNAME_RELEASE}
+ else
+ echo m88k-dg-dguxbcs${UNAME_RELEASE}
+ fi
+ else echo i586-dg-dgux${UNAME_RELEASE}
+ fi
+ exit 0 ;;
+ M88*:DolphinOS:*:*) # DolphinOS (SVR3)
+ echo m88k-dolphin-sysv3
+ exit 0 ;;
+ M88*:*:R3*:*)
+ # Delta 88k system running SVR3
+ echo m88k-motorola-sysv3
+ exit 0 ;;
+ XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+ echo m88k-tektronix-sysv3
+ exit 0 ;;
+ Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+ echo m68k-tektronix-bsd
+ exit 0 ;;
+ *:IRIX*:*:*)
+ echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
+ exit 0 ;;
+ ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+ echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id
+ exit 0 ;; # Note that: echo "'`uname -s`'" gives 'AIX '
+ i?86:AIX:*:*)
+ echo i386-ibm-aix
+ exit 0 ;;
+ *:AIX:2:3)
+ if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+ sed 's/^ //' << EOF >dummy.c
+ #include <sys/systemcfg.h>
+
+ main()
+ {
+ if (!__power_pc())
+ exit(1);
+ puts("powerpc-ibm-aix3.2.5");
+ exit(0);
+ }
+EOF
+ ${CC-cc} dummy.c -o dummy && ./dummy && rm dummy.c dummy && exit 0
+ rm -f dummy.c dummy
+ echo rs6000-ibm-aix3.2.5
+ elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+ echo rs6000-ibm-aix3.2.4
+ else
+ echo rs6000-ibm-aix3.2
+ fi
+ exit 0 ;;
+ *:AIX:*:4)
+ IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | head -1 | awk '{ print $1 }'`
+ if /usr/sbin/lsattr -EHl ${IBM_CPU_ID} | grep POWER >/dev/null 2>&1; then
+ IBM_ARCH=rs6000
+ else
+ IBM_ARCH=powerpc
+ fi
+ if [ -x /usr/bin/oslevel ] ; then
+ IBM_REV=`/usr/bin/oslevel`
+ else
+ IBM_REV=4.${UNAME_RELEASE}
+ fi
+ echo ${IBM_ARCH}-ibm-aix${IBM_REV}
+ exit 0 ;;
+ *:AIX:*:*)
+ echo rs6000-ibm-aix
+ exit 0 ;;
+ ibmrt:4.4BSD:*|romp-ibm:BSD:*)
+ echo romp-ibm-bsd4.4
+ exit 0 ;;
+ ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC NetBSD and
+ echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to
+ exit 0 ;; # report: romp-ibm BSD 4.3
+ *:BOSX:*:*)
+ echo rs6000-bull-bosx
+ exit 0 ;;
+ DPX/2?00:B.O.S.:*:*)
+ echo m68k-bull-sysv3
+ exit 0 ;;
+ 9000/[34]??:4.3bsd:1.*:*)
+ echo m68k-hp-bsd
+ exit 0 ;;
+ hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+ echo m68k-hp-bsd4.4
+ exit 0 ;;
+ 9000/[34678]??:HP-UX:*:*)
+ case "${UNAME_MACHINE}" in
+ 9000/31? ) HP_ARCH=m68000 ;;
+ 9000/[34]?? ) HP_ARCH=m68k ;;
+ 9000/6?? | 9000/7?? | 9000/80[24] | 9000/8?[13679] | 9000/892 )
+ sed 's/^ //' << EOF >dummy.c
+ #include <stdlib.h>
+ #include <unistd.h>
+
+ int main ()
+ {
+ #if defined(_SC_KERNEL_BITS)
+ long bits = sysconf(_SC_KERNEL_BITS);
+ #endif
+ long cpu = sysconf (_SC_CPU_VERSION);
+
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+ case CPU_PA_RISC2_0:
+ #if defined(_SC_KERNEL_BITS)
+ switch (bits)
+ {
+ case 64: puts ("hppa2.0w"); break;
+ case 32: puts ("hppa2.0n"); break;
+ default: puts ("hppa2.0"); break;
+ } break;
+ #else /* !defined(_SC_KERNEL_BITS) */
+ puts ("hppa2.0"); break;
+ #endif
+ default: puts ("hppa1.0"); break;
+ }
+ exit (0);
+ }
+EOF
+ (${CC-cc} dummy.c -o dummy 2>/dev/null ) && HP_ARCH=`./dummy`
+ rm -f dummy.c dummy
+ esac
+ HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+ echo ${HP_ARCH}-hp-hpux${HPUX_REV}
+ exit 0 ;;
+ 3050*:HI-UX:*:*)
+ sed 's/^ //' << EOF >dummy.c
+ #include <unistd.h>
+ int
+ main ()
+ {
+ long cpu = sysconf (_SC_CPU_VERSION);
+ /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+ true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct
+ results, however. */
+ if (CPU_IS_PA_RISC (cpu))
+ {
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+ default: puts ("hppa-hitachi-hiuxwe2"); break;
+ }
+ }
+ else if (CPU_IS_HP_MC68K (cpu))
+ puts ("m68k-hitachi-hiuxwe2");
+ else puts ("unknown-hitachi-hiuxwe2");
+ exit (0);
+ }
+EOF
+ ${CC-cc} dummy.c -o dummy && ./dummy && rm dummy.c dummy && exit 0
+ rm -f dummy.c dummy
+ echo unknown-hitachi-hiuxwe2
+ exit 0 ;;
+ 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
+ echo hppa1.1-hp-bsd
+ exit 0 ;;
+ 9000/8??:4.3bsd:*:*)
+ echo hppa1.0-hp-bsd
+ exit 0 ;;
+ hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
+ echo hppa1.1-hp-osf
+ exit 0 ;;
+ hp8??:OSF1:*:*)
+ echo hppa1.0-hp-osf
+ exit 0 ;;
+ i?86:OSF1:*:*)
+ if [ -x /usr/sbin/sysversion ] ; then
+ echo ${UNAME_MACHINE}-unknown-osf1mk
+ else
+ echo ${UNAME_MACHINE}-unknown-osf1
+ fi
+ exit 0 ;;
+ parisc*:Lites*:*:*)
+ echo hppa1.1-hp-lites
+ exit 0 ;;
+ C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+ echo c1-convex-bsd
+ exit 0 ;;
+ C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit 0 ;;
+ C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+ echo c34-convex-bsd
+ exit 0 ;;
+ C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+ echo c38-convex-bsd
+ exit 0 ;;
+ C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+ echo c4-convex-bsd
+ exit 0 ;;
+ CRAY*X-MP:*:*:*)
+ echo xmp-cray-unicos
+ exit 0 ;;
+ CRAY*Y-MP:*:*:*)
+ echo ymp-cray-unicos${UNAME_RELEASE}
+ exit 0 ;;
+ CRAY*[A-Z]90:*:*:*)
+ echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
+ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/
+ exit 0 ;;
+ CRAY*TS:*:*:*)
+ echo t90-cray-unicos${UNAME_RELEASE}
+ exit 0 ;;
+ CRAY-2:*:*:*)
+ echo cray2-cray-unicos
+ exit 0 ;;
+ F300:UNIX_System_V:*:*)
+ FUJITSU_SYS=`uname -p | tr [A-Z] [a-z] | sed -e 's/\///'`
+ FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
+ echo "f300-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit 0 ;;
+ F301:UNIX_System_V:*:*)
+ echo f301-fujitsu-uxpv`echo $UNAME_RELEASE | sed 's/ .*//'`
+ exit 0 ;;
+ hp3[0-9][05]:NetBSD:*:*)
+ echo m68k-hp-netbsd${UNAME_RELEASE}
+ exit 0 ;;
+ hp300:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ sparc*:BSD/OS:*:*)
+ echo sparc-unknown-bsdi${UNAME_RELEASE}
+ exit 0 ;;
+ i?86:BSD/386:*:* | *:BSD/OS:*:*)
+ echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
+ exit 0 ;;
+ *:FreeBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+ exit 0 ;;
+ *:NetBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-netbsd`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+ exit 0 ;;
+ *:OpenBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-openbsd`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+ exit 0 ;;
+ i*:CYGWIN*:*)
+ echo ${UNAME_MACHINE}-pc-cygwin
+ exit 0 ;;
+ i*:MINGW*:*)
+ echo ${UNAME_MACHINE}-pc-mingw32
+ exit 0 ;;
+ p*:CYGWIN*:*)
+ echo powerpcle-unknown-cygwin
+ exit 0 ;;
+ prep*:SunOS:5.*:*)
+ echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ *:GNU:*:*)
+ echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
+ exit 0 ;;
+ *:Linux:*:*)
+ # uname on the ARM produces all sorts of strangeness, and we need to
+ # filter it out.
+ case "$UNAME_MACHINE" in
+ arm* | sa110*) UNAME_MACHINE="arm" ;;
+ esac
+
+ # The BFD linker knows what the default object file format is, so
+ # first see if it will tell us.
+ ld_help_string=`ld --help 2>&1`
+ ld_supported_emulations=`echo $ld_help_string \
+ | sed -ne '/supported emulations:/!d
+ s/[ ][ ]*/ /g
+ s/.*supported emulations: *//
+ s/ .*//
+ p'`
+ case "$ld_supported_emulations" in
+ i?86linux) echo "${UNAME_MACHINE}-pc-linux-gnuaout" ; exit 0 ;;
+ i?86coff) echo "${UNAME_MACHINE}-pc-linux-gnucoff" ; exit 0 ;;
+ sparclinux) echo "${UNAME_MACHINE}-unknown-linux-gnuaout" ; exit 0 ;;
+ armlinux) echo "${UNAME_MACHINE}-unknown-linux-gnuaout" ; exit 0 ;;
+ m68klinux) echo "${UNAME_MACHINE}-unknown-linux-gnuaout" ; exit 0 ;;
+ elf32ppc) echo "powerpc-unknown-linux-gnu" ; exit 0 ;;
+ esac
+
+ if test "${UNAME_MACHINE}" = "alpha" ; then
+ sed 's/^ //' <<EOF >dummy.s
+ .globl main
+ .ent main
+ main:
+ .frame \$30,0,\$26,0
+ .prologue 0
+ .long 0x47e03d80 # implver $0
+ lda \$2,259
+ .long 0x47e20c21 # amask $2,$1
+ srl \$1,8,\$2
+ sll \$2,2,\$2
+ sll \$0,3,\$0
+ addl \$1,\$0,\$0
+ addl \$2,\$0,\$0
+ ret \$31,(\$26),1
+ .end main
+EOF
+ LIBC=""
+ ${CC-cc} dummy.s -o dummy 2>/dev/null
+ if test "$?" = 0 ; then
+ ./dummy
+ case "$?" in
+ 7)
+ UNAME_MACHINE="alpha"
+ ;;
+ 15)
+ UNAME_MACHINE="alphaev5"
+ ;;
+ 14)
+ UNAME_MACHINE="alphaev56"
+ ;;
+ 10)
+ UNAME_MACHINE="alphapca56"
+ ;;
+ 16)
+ UNAME_MACHINE="alphaev6"
+ ;;
+ esac
+
+ objdump --private-headers dummy | \
+ grep ld.so.1 > /dev/null
+ if test "$?" = 0 ; then
+ LIBC="libc1"
+ fi
+ fi
+ rm -f dummy.s dummy
+ echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} ; exit 0
+ elif test "${UNAME_MACHINE}" = "mips" ; then
+ cat >dummy.c <<EOF
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+#ifdef __MIPSEB__
+ printf ("%s-unknown-linux-gnu\n", argv[1]);
+#endif
+#ifdef __MIPSEL__
+ printf ("%sel-unknown-linux-gnu\n", argv[1]);
+#endif
+ return 0;
+}
+EOF
+ ${CC-cc} dummy.c -o dummy 2>/dev/null && ./dummy "${UNAME_MACHINE}" && rm dummy.c dummy && exit 0
+ rm -f dummy.c dummy
+ else
+ # Either a pre-BFD a.out linker (linux-gnuoldld)
+ # or one that does not give us useful --help.
+ # GCC wants to distinguish between linux-gnuoldld and linux-gnuaout.
+ # If ld does not provide *any* "supported emulations:"
+ # that means it is gnuoldld.
+ echo "$ld_help_string" | grep >/dev/null 2>&1 "supported emulations:"
+ test $? != 0 && echo "${UNAME_MACHINE}-pc-linux-gnuoldld" && exit 0
+
+ case "${UNAME_MACHINE}" in
+ i?86)
+ VENDOR=pc;
+ ;;
+ *)
+ VENDOR=unknown;
+ ;;
+ esac
+ # Determine whether the default compiler is a.out or elf
+ cat >dummy.c <<EOF
+#include <features.h>
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+#ifdef __ELF__
+# ifdef __GLIBC__
+# if __GLIBC__ >= 2
+ printf ("%s-${VENDOR}-linux-gnu\n", argv[1]);
+# else
+ printf ("%s-${VENDOR}-linux-gnulibc1\n", argv[1]);
+# endif
+# else
+ printf ("%s-${VENDOR}-linux-gnulibc1\n", argv[1]);
+# endif
+#else
+ printf ("%s-${VENDOR}-linux-gnuaout\n", argv[1]);
+#endif
+ return 0;
+}
+EOF
+ ${CC-cc} dummy.c -o dummy 2>/dev/null && ./dummy "${UNAME_MACHINE}" && rm dummy.c dummy && exit 0
+ rm -f dummy.c dummy
+ fi ;;
+# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. earlier versions
+# are messed up and put the nodename in both sysname and nodename.
+ i?86:DYNIX/ptx:4*:*)
+ echo i386-sequent-sysv4
+ exit 0 ;;
+ i?86:UNIX_SV:4.2MP:2.*)
+ # Unixware is an offshoot of SVR4, but it has its own version
+ # number series starting with 2...
+ # I am not positive that other SVR4 systems won't match this,
+ # I just have to hope. -- rms.
+ # Use sysv4.2uw... so that sysv4* matches it.
+ echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
+ exit 0 ;;
+ i?86:*:4.*:* | i?86:SYSTEM_V:4.*:*)
+ if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+ echo ${UNAME_MACHINE}-univel-sysv${UNAME_RELEASE}
+ else
+ echo ${UNAME_MACHINE}-pc-sysv${UNAME_RELEASE}
+ fi
+ exit 0 ;;
+ i?86:*:3.2:*)
+ if test -f /usr/options/cb.name; then
+ UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+ echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
+ elif /bin/uname -X 2>/dev/null >/dev/null ; then
+ UNAME_REL=`(/bin/uname -X|egrep Release|sed -e 's/.*= //')`
+ (/bin/uname -X|egrep i80486 >/dev/null) && UNAME_MACHINE=i486
+ (/bin/uname -X|egrep '^Machine.*Pentium' >/dev/null) \
+ && UNAME_MACHINE=i586
+ echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
+ else
+ echo ${UNAME_MACHINE}-pc-sysv32
+ fi
+ exit 0 ;;
+ i?86:UnixWare:*:*)
+ if /bin/uname -X 2>/dev/null >/dev/null ; then
+ (/bin/uname -X|egrep '^Machine.*Pentium' >/dev/null) \
+ && UNAME_MACHINE=i586
+ fi
+ echo ${UNAME_MACHINE}-unixware-${UNAME_RELEASE}-${UNAME_VERSION}
+ exit 0 ;;
+ pc:*:*:*)
+ # uname -m prints for DJGPP always 'pc', but it prints nothing about
+ # the processor, so we play safe by assuming i386.
+ echo i386-pc-msdosdjgpp
+ exit 0 ;;
+ Intel:Mach:3*:*)
+ echo i386-pc-mach3
+ exit 0 ;;
+ paragon:*:*:*)
+ echo i860-intel-osf1
+ exit 0 ;;
+ i860:*:4.*:*) # i860-SVR4
+ if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+ echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
+ else # Add other i860-SVR4 vendors below as they are discovered.
+ echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4
+ fi
+ exit 0 ;;
+ mini*:CTIX:SYS*5:*)
+ # "miniframe"
+ echo m68010-convergent-sysv
+ exit 0 ;;
+ M68*:*:R3V[567]*:*)
+ test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;;
+ 3[34]??:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 4850:*:4.0:3.0)
+ OS_REL=''
+ test -r /etc/.relid \
+ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && echo i486-ncr-sysv4.3${OS_REL} && exit 0
+ /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+ && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;;
+ 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && echo i486-ncr-sysv4 && exit 0 ;;
+ m68*:LynxOS:2.*:*)
+ echo m68k-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ mc68030:UNIX_System_V:4.*:*)
+ echo m68k-atari-sysv4
+ exit 0 ;;
+ i?86:LynxOS:2.*:*)
+ echo i386-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ TSUNAMI:LynxOS:2.*:*)
+ echo sparc-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ rs6000:LynxOS:2.*:* | PowerPC:LynxOS:2.*:*)
+ echo rs6000-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ SM[BE]S:UNIX_SV:*:*)
+ echo mips-dde-sysv${UNAME_RELEASE}
+ exit 0 ;;
+ RM*:SINIX-*:*:*)
+ echo mips-sni-sysv4
+ exit 0 ;;
+ *:SINIX-*:*:*)
+ if uname -p 2>/dev/null >/dev/null ; then
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
+ echo ${UNAME_MACHINE}-sni-sysv4
+ else
+ echo ns32k-sni-sysv
+ fi
+ exit 0 ;;
+ PENTIUM:CPunix:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+ # says <Richard.M.Bartel@ccMail.Census.GOV>
+ echo i586-unisys-sysv4
+ exit 0 ;;
+ *:UNIX_System_V:4*:FTX*)
+ # From Gerald Hewes <hewes@openmarket.com>.
+ # How about differentiating between stratus architectures? -djm
+ echo hppa1.1-stratus-sysv4
+ exit 0 ;;
+ *:*:*:FTX*)
+ # From seanf@swdc.stratus.com.
+ echo i860-stratus-sysv4
+ exit 0 ;;
+ mc68*:A/UX:*:*)
+ echo m68k-apple-aux${UNAME_RELEASE}
+ exit 0 ;;
+ news*:NEWS-OS:*:6*)
+ echo mips-sony-newsos6
+ exit 0 ;;
+ R3000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R4000:UNIX_SV:*:*)
+ if [ -d /usr/nec ]; then
+ echo mips-nec-sysv${UNAME_RELEASE}
+ else
+ echo mips-unknown-sysv${UNAME_RELEASE}
+ fi
+ exit 0 ;;
+ BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only.
+ echo powerpc-be-beos
+ exit 0 ;;
+ BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only.
+ echo powerpc-apple-beos
+ exit 0 ;;
+ BePC:BeOS:*:*) # BeOS running on Intel PC compatible.
+ echo i586-pc-beos
+ exit 0 ;;
+esac
+
+#echo '(No uname command or uname output not recognized.)' 1>&2
+#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
+
+cat >dummy.c <<EOF
+#ifdef _SEQUENT_
+# include <sys/types.h>
+# include <sys/utsname.h>
+#endif
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+ /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed,
+ I don't know.... */
+ printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+ printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+ "4"
+#else
+ ""
+#endif
+ ); exit (0);
+#endif
+#endif
+
+#if defined (__arm) && defined (__acorn) && defined (__unix)
+ printf ("arm-acorn-riscix"); exit (0);
+#endif
+
+#if defined (hp300) && !defined (hpux)
+ printf ("m68k-hp-bsd\n"); exit (0);
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+ int version;
+ version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+ printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+ exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+ printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+ printf ("ns32k-encore-mach\n"); exit (0);
+#else
+ printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+ printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+ printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+ printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+ struct utsname un;
+
+ uname(&un);
+
+ if (strncmp(un.version, "V2", 2) == 0) {
+ printf ("i386-sequent-ptx2\n"); exit (0);
+ }
+ if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+ printf ("i386-sequent-ptx1\n"); exit (0);
+ }
+ printf ("i386-sequent-ptx\n"); exit (0);
+
+#endif
+
+#if defined (vax)
+#if !defined (ultrix)
+ printf ("vax-dec-bsd\n"); exit (0);
+#else
+ printf ("vax-dec-ultrix\n"); exit (0);
+#endif
+#endif
+
+#if defined (alliant) && defined (i860)
+ printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+ exit (1);
+}
+EOF
+
+${CC-cc} dummy.c -o dummy 2>/dev/null && ./dummy && rm dummy.c dummy && exit 0
+rm -f dummy.c dummy
+
+# Apollos put the system type in the environment.
+
+test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; }
+
+# Convex versions that predate uname can use getsysinfo(1)
+
+if [ -x /usr/convex/getsysinfo ]
+then
+ case `getsysinfo -f cpu_type` in
+ c1*)
+ echo c1-convex-bsd
+ exit 0 ;;
+ c2*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit 0 ;;
+ c34*)
+ echo c34-convex-bsd
+ exit 0 ;;
+ c38*)
+ echo c38-convex-bsd
+ exit 0 ;;
+ c4*)
+ echo c4-convex-bsd
+ exit 0 ;;
+ esac
+fi
+
+#echo '(Unable to guess system type)' 1>&2
+
+exit 1
--- /dev/null
+#! /bin/sh
+# Configuration validation subroutine script, version 1.1.
+# Copyright (C) 1991, 92-97, 1998 Free Software Foundation, Inc.
+# This file is (in principle) common to ALL GNU software.
+# The presence of a machine in this file suggests that SOME GNU software
+# can handle that machine. It does not imply ALL GNU software can.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support. The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+if [ x$1 = x ]
+then
+ echo Configuration name missing. 1>&2
+ echo "Usage: $0 CPU-MFR-OPSYS" 1>&2
+ echo "or $0 ALIAS" 1>&2
+ echo where ALIAS is a recognized configuration type. 1>&2
+ exit 1
+fi
+
+# First pass through any local machine types.
+case $1 in
+ *local*)
+ echo $1
+ exit 0
+ ;;
+ *)
+ ;;
+esac
+
+# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
+# Here we must recognize all the valid KERNEL-OS combinations.
+maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
+case $maybe_os in
+ linux-gnu*)
+ os=-$maybe_os
+ basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
+ ;;
+ *)
+ basic_machine=`echo $1 | sed 's/-[^-]*$//'`
+ if [ $basic_machine != $1 ]
+ then os=`echo $1 | sed 's/.*-/-/'`
+ else os=; fi
+ ;;
+esac
+
+### Let's recognize common machines as not being operating systems so
+### that things like config.sub decstation-3100 work. We also
+### recognize some manufacturers as not being operating systems, so we
+### can provide default operating systems below.
+case $os in
+ -sun*os*)
+ # Prevent following clause from handling this invalid input.
+ ;;
+ -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
+ -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
+ -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
+ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
+ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
+ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
+ -apple)
+ os=
+ basic_machine=$1
+ ;;
+ -hiux*)
+ os=-hiuxwe2
+ ;;
+ -sco5)
+ os=sco3.2v5
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco4)
+ os=-sco3.2v4
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco3.2.[4-9]*)
+ os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco3.2v[4-9]*)
+ # Don't forget version if it is 3.2v4 or newer.
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco*)
+ os=-sco3.2v2
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -isc)
+ os=-isc2.2
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -clix*)
+ basic_machine=clipper-intergraph
+ ;;
+ -isc*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -lynx*)
+ os=-lynxos
+ ;;
+ -ptx*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
+ ;;
+ -windowsnt*)
+ os=`echo $os | sed -e 's/windowsnt/winnt/'`
+ ;;
+ -psos*)
+ os=-psos
+ ;;
+esac
+
+# Decode aliases for certain CPU-COMPANY combinations.
+case $basic_machine in
+ # Recognize the basic CPU types without company name.
+ # Some are omitted here because they have special meanings below.
+ tahoe | i860 | m32r | m68k | m68000 | m88k | ns32k | arc | arm \
+ | arme[lb] | pyramid | mn10200 | mn10300 | tron | a29k \
+ | 580 | i960 | h8300 | hppa | hppa1.0 | hppa1.1 | hppa2.0 \
+ | alpha | alphaev5 | alphaev56 | we32k | ns16k | clipper \
+ | i370 | sh | powerpc | powerpcle | 1750a | dsp16xx | pdp11 \
+ | mips64 | mipsel | mips64el | mips64orion | mips64orionel \
+ | mipstx39 | mipstx39el \
+ | sparc | sparclet | sparclite | sparc64 | v850)
+ basic_machine=$basic_machine-unknown
+ ;;
+ # We use `pc' rather than `unknown'
+ # because (1) that's what they normally are, and
+ # (2) the word "unknown" tends to confuse beginning users.
+ i[34567]86)
+ basic_machine=$basic_machine-pc
+ ;;
+ # Object if more than one company name word.
+ *-*-*)
+ echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+ exit 1
+ ;;
+ # Recognize the basic CPU types with company name.
+ vax-* | tahoe-* | i[34567]86-* | i860-* | m32r-* | m68k-* | m68000-* \
+ | m88k-* | sparc-* | ns32k-* | fx80-* | arc-* | arm-* | c[123]* \
+ | mips-* | pyramid-* | tron-* | a29k-* | romp-* | rs6000-* \
+ | power-* | none-* | 580-* | cray2-* | h8300-* | i960-* \
+ | xmp-* | ymp-* | hppa-* | hppa1.0-* | hppa1.1-* | hppa2.0-* \
+ | alpha-* | alphaev5-* | alphaev56-* | we32k-* | cydra-* \
+ | ns16k-* | pn-* | np1-* | xps100-* | clipper-* | orion-* \
+ | sparclite-* | pdp11-* | sh-* | powerpc-* | powerpcle-* \
+ | sparc64-* | mips64-* | mipsel-* \
+ | mips64el-* | mips64orion-* | mips64orionel-* \
+ | mipstx39-* | mipstx39el-* \
+ | f301-*)
+ ;;
+ # Recognize the various machine names and aliases which stand
+ # for a CPU type and a company and sometimes even an OS.
+ 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+ basic_machine=m68000-att
+ ;;
+ 3b*)
+ basic_machine=we32k-att
+ ;;
+ alliant | fx80)
+ basic_machine=fx80-alliant
+ ;;
+ altos | altos3068)
+ basic_machine=m68k-altos
+ ;;
+ am29k)
+ basic_machine=a29k-none
+ os=-bsd
+ ;;
+ amdahl)
+ basic_machine=580-amdahl
+ os=-sysv
+ ;;
+ amiga | amiga-*)
+ basic_machine=m68k-cbm
+ ;;
+ amigaos | amigados)
+ basic_machine=m68k-cbm
+ os=-amigaos
+ ;;
+ amigaunix | amix)
+ basic_machine=m68k-cbm
+ os=-sysv4
+ ;;
+ apollo68)
+ basic_machine=m68k-apollo
+ os=-sysv
+ ;;
+ aux)
+ basic_machine=m68k-apple
+ os=-aux
+ ;;
+ balance)
+ basic_machine=ns32k-sequent
+ os=-dynix
+ ;;
+ convex-c1)
+ basic_machine=c1-convex
+ os=-bsd
+ ;;
+ convex-c2)
+ basic_machine=c2-convex
+ os=-bsd
+ ;;
+ convex-c32)
+ basic_machine=c32-convex
+ os=-bsd
+ ;;
+ convex-c34)
+ basic_machine=c34-convex
+ os=-bsd
+ ;;
+ convex-c38)
+ basic_machine=c38-convex
+ os=-bsd
+ ;;
+ cray | ymp)
+ basic_machine=ymp-cray
+ os=-unicos
+ ;;
+ cray2)
+ basic_machine=cray2-cray
+ os=-unicos
+ ;;
+ [ctj]90-cray)
+ basic_machine=c90-cray
+ os=-unicos
+ ;;
+ crds | unos)
+ basic_machine=m68k-crds
+ ;;
+ da30 | da30-*)
+ basic_machine=m68k-da30
+ ;;
+ decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
+ basic_machine=mips-dec
+ ;;
+ delta | 3300 | motorola-3300 | motorola-delta \
+ | 3300-motorola | delta-motorola)
+ basic_machine=m68k-motorola
+ ;;
+ delta88)
+ basic_machine=m88k-motorola
+ os=-sysv3
+ ;;
+ dpx20 | dpx20-*)
+ basic_machine=rs6000-bull
+ os=-bosx
+ ;;
+ dpx2* | dpx2*-bull)
+ basic_machine=m68k-bull
+ os=-sysv3
+ ;;
+ ebmon29k)
+ basic_machine=a29k-amd
+ os=-ebmon
+ ;;
+ elxsi)
+ basic_machine=elxsi-elxsi
+ os=-bsd
+ ;;
+ encore | umax | mmax)
+ basic_machine=ns32k-encore
+ ;;
+ fx2800)
+ basic_machine=i860-alliant
+ ;;
+ genix)
+ basic_machine=ns32k-ns
+ ;;
+ gmicro)
+ basic_machine=tron-gmicro
+ os=-sysv
+ ;;
+ h3050r* | hiux*)
+ basic_machine=hppa1.1-hitachi
+ os=-hiuxwe2
+ ;;
+ h8300hms)
+ basic_machine=h8300-hitachi
+ os=-hms
+ ;;
+ harris)
+ basic_machine=m88k-harris
+ os=-sysv3
+ ;;
+ hp300-*)
+ basic_machine=m68k-hp
+ ;;
+ hp300bsd)
+ basic_machine=m68k-hp
+ os=-bsd
+ ;;
+ hp300hpux)
+ basic_machine=m68k-hp
+ os=-hpux
+ ;;
+ hp9k2[0-9][0-9] | hp9k31[0-9])
+ basic_machine=m68000-hp
+ ;;
+ hp9k3[2-9][0-9])
+ basic_machine=m68k-hp
+ ;;
+ hp9k7[0-9][0-9] | hp7[0-9][0-9] | hp9k8[0-9]7 | hp8[0-9]7)
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[0-9][0-9] | hp8[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hppa-next)
+ os=-nextstep3
+ ;;
+ i370-ibm* | ibm*)
+ basic_machine=i370-ibm
+ os=-mvs
+ ;;
+# I'm not sure what "Sysv32" means. Should this be sysv3.2?
+ i[34567]86v32)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv32
+ ;;
+ i[34567]86v4*)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv4
+ ;;
+ i[34567]86v)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv
+ ;;
+ i[34567]86sol2)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-solaris2
+ ;;
+ iris | iris4d)
+ basic_machine=mips-sgi
+ case $os in
+ -irix*)
+ ;;
+ *)
+ os=-irix4
+ ;;
+ esac
+ ;;
+ isi68 | isi)
+ basic_machine=m68k-isi
+ os=-sysv
+ ;;
+ m88k-omron*)
+ basic_machine=m88k-omron
+ ;;
+ magnum | m3230)
+ basic_machine=mips-mips
+ os=-sysv
+ ;;
+ merlin)
+ basic_machine=ns32k-utek
+ os=-sysv
+ ;;
+ miniframe)
+ basic_machine=m68000-convergent
+ ;;
+ mipsel*-linux*)
+ basic_machine=mipsel-unknown
+ os=-linux-gnu
+ ;;
+ mips*-linux*)
+ basic_machine=mips-unknown
+ os=-linux-gnu
+ ;;
+ mips3*-*)
+ basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
+ ;;
+ mips3*)
+ basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
+ ;;
+ ncr3000)
+ basic_machine=i486-ncr
+ os=-sysv4
+ ;;
+ news | news700 | news800 | news900)
+ basic_machine=m68k-sony
+ os=-newsos
+ ;;
+ news1000)
+ basic_machine=m68030-sony
+ os=-newsos
+ ;;
+ news-3600 | risc-news)
+ basic_machine=mips-sony
+ os=-newsos
+ ;;
+ next | m*-next )
+ basic_machine=m68k-next
+ case $os in
+ -nextstep* )
+ ;;
+ -ns2*)
+ os=-nextstep2
+ ;;
+ *)
+ os=-nextstep3
+ ;;
+ esac
+ ;;
+ nh3000)
+ basic_machine=m68k-harris
+ os=-cxux
+ ;;
+ nh[45]000)
+ basic_machine=m88k-harris
+ os=-cxux
+ ;;
+ nindy960)
+ basic_machine=i960-intel
+ os=-nindy
+ ;;
+ np1)
+ basic_machine=np1-gould
+ ;;
+ pa-hitachi)
+ basic_machine=hppa1.1-hitachi
+ os=-hiuxwe2
+ ;;
+ paragon)
+ basic_machine=i860-intel
+ os=-osf
+ ;;
+ pbd)
+ basic_machine=sparc-tti
+ ;;
+ pbb)
+ basic_machine=m68k-tti
+ ;;
+ pc532 | pc532-*)
+ basic_machine=ns32k-pc532
+ ;;
+ pentium | p5 | k5 | nexen)
+ basic_machine=i586-pc
+ ;;
+ pentiumpro | p6 | k6 | 6x86)
+ basic_machine=i686-pc
+ ;;
+ pentiumii | pentium2)
+ basic_machine=i786-pc
+ ;;
+ pentium-* | p5-* | k5-* | nexen-*)
+ basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentiumpro-* | p6-* | k6-* | 6x86-*)
+ basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentiumii-* | pentium2-*)
+ basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pn)
+ basic_machine=pn-gould
+ ;;
+ power) basic_machine=rs6000-ibm
+ ;;
+ ppc) basic_machine=powerpc-unknown
+ ;;
+ ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppcle | powerpclittle | ppc-le | powerpc-little)
+ basic_machine=powerpcle-unknown
+ ;;
+ ppcle-* | powerpclittle-*)
+ basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ps2)
+ basic_machine=i386-ibm
+ ;;
+ rm[46]00)
+ basic_machine=mips-siemens
+ ;;
+ rtpc | rtpc-*)
+ basic_machine=romp-ibm
+ ;;
+ sequent)
+ basic_machine=i386-sequent
+ ;;
+ sh)
+ basic_machine=sh-hitachi
+ os=-hms
+ ;;
+ sps7)
+ basic_machine=m68k-bull
+ os=-sysv2
+ ;;
+ spur)
+ basic_machine=spur-unknown
+ ;;
+ sun2)
+ basic_machine=m68000-sun
+ ;;
+ sun2os3)
+ basic_machine=m68000-sun
+ os=-sunos3
+ ;;
+ sun2os4)
+ basic_machine=m68000-sun
+ os=-sunos4
+ ;;
+ sun3os3)
+ basic_machine=m68k-sun
+ os=-sunos3
+ ;;
+ sun3os4)
+ basic_machine=m68k-sun
+ os=-sunos4
+ ;;
+ sun4os3)
+ basic_machine=sparc-sun
+ os=-sunos3
+ ;;
+ sun4os4)
+ basic_machine=sparc-sun
+ os=-sunos4
+ ;;
+ sun4sol2)
+ basic_machine=sparc-sun
+ os=-solaris2
+ ;;
+ sun3 | sun3-*)
+ basic_machine=m68k-sun
+ ;;
+ sun4)
+ basic_machine=sparc-sun
+ ;;
+ sun386 | sun386i | roadrunner)
+ basic_machine=i386-sun
+ ;;
+ symmetry)
+ basic_machine=i386-sequent
+ os=-dynix
+ ;;
+ tx39)
+ basic_machine=mipstx39-unknown
+ ;;
+ tx39el)
+ basic_machine=mipstx39el-unknown
+ ;;
+ tower | tower-32)
+ basic_machine=m68k-ncr
+ ;;
+ udi29k)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ ultra3)
+ basic_machine=a29k-nyu
+ os=-sym1
+ ;;
+ vaxv)
+ basic_machine=vax-dec
+ os=-sysv
+ ;;
+ vms)
+ basic_machine=vax-dec
+ os=-vms
+ ;;
+ vpp*|vx|vx-*)
+ basic_machine=f301-fujitsu
+ ;;
+ vxworks960)
+ basic_machine=i960-wrs
+ os=-vxworks
+ ;;
+ vxworks68)
+ basic_machine=m68k-wrs
+ os=-vxworks
+ ;;
+ vxworks29k)
+ basic_machine=a29k-wrs
+ os=-vxworks
+ ;;
+ xmp)
+ basic_machine=xmp-cray
+ os=-unicos
+ ;;
+ xps | xps100)
+ basic_machine=xps100-honeywell
+ ;;
+ none)
+ basic_machine=none-none
+ os=-none
+ ;;
+
+# Here we handle the default manufacturer of certain CPU types. It is in
+# some cases the only manufacturer, in others, it is the most popular.
+ mips)
+ if [ x$os = x-linux-gnu ]; then
+ basic_machine=mips-unknown
+ else
+ basic_machine=mips-mips
+ fi
+ ;;
+ romp)
+ basic_machine=romp-ibm
+ ;;
+ rs6000)
+ basic_machine=rs6000-ibm
+ ;;
+ vax)
+ basic_machine=vax-dec
+ ;;
+ pdp11)
+ basic_machine=pdp11-dec
+ ;;
+ we32k)
+ basic_machine=we32k-att
+ ;;
+ sparc)
+ basic_machine=sparc-sun
+ ;;
+ cydra)
+ basic_machine=cydra-cydrome
+ ;;
+ orion)
+ basic_machine=orion-highlevel
+ ;;
+ orion105)
+ basic_machine=clipper-highlevel
+ ;;
+ *)
+ echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $basic_machine in
+ *-digital*)
+ basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
+ ;;
+ *-commodore*)
+ basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
+ ;;
+ *)
+ ;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if [ x"$os" != x"" ]
+then
+case $os in
+ # First match some system type aliases
+ # that might get confused with valid system types.
+ # -solaris* is a basic system type, with this one exception.
+ -solaris1 | -solaris1.*)
+ os=`echo $os | sed -e 's|solaris1|sunos4|'`
+ ;;
+ -solaris)
+ os=-solaris2
+ ;;
+ -svr4*)
+ os=-sysv4
+ ;;
+ -unixware*)
+ os=-sysv4.2uw
+ ;;
+ -gnu/linux*)
+ os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
+ ;;
+ # First accept the basic system types.
+ # The portable systems comes first.
+ # Each alternative MUST END IN A *, to match a version number.
+ # -sysv* is not here because it comes later, after sysvr4.
+ -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
+ | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\
+ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \
+ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
+ | -aos* \
+ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
+ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
+ | -hiux* | -386bsd* | -netbsd* | -openbsd* | -freebsd* | -riscix* \
+ | -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* | -elf* \
+ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
+ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
+ | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+ | -mingw32* | -linux-gnu* | -uxpv* | -beos*)
+ # Remember, each alternative MUST END IN *, to match a version number.
+ ;;
+ -linux*)
+ os=`echo $os | sed -e 's|linux|linux-gnu|'`
+ ;;
+ -sunos5*)
+ os=`echo $os | sed -e 's|sunos5|solaris2|'`
+ ;;
+ -sunos6*)
+ os=`echo $os | sed -e 's|sunos6|solaris3|'`
+ ;;
+ -osfrose*)
+ os=-osfrose
+ ;;
+ -osf*)
+ os=-osf
+ ;;
+ -utek*)
+ os=-bsd
+ ;;
+ -dynix*)
+ os=-bsd
+ ;;
+ -acis*)
+ os=-aos
+ ;;
+ -ctix* | -uts*)
+ os=-sysv
+ ;;
+ -ns2 )
+ os=-nextstep2
+ ;;
+ # Preserve the version number of sinix5.
+ -sinix5.*)
+ os=`echo $os | sed -e 's|sinix|sysv|'`
+ ;;
+ -sinix*)
+ os=-sysv4
+ ;;
+ -triton*)
+ os=-sysv3
+ ;;
+ -oss*)
+ os=-sysv3
+ ;;
+ -svr4)
+ os=-sysv4
+ ;;
+ -svr3)
+ os=-sysv3
+ ;;
+ -sysvr4)
+ os=-sysv4
+ ;;
+ # This must come after -sysvr4.
+ -sysv*)
+ ;;
+ -xenix)
+ os=-xenix
+ ;;
+ -none)
+ ;;
+ *)
+ # Get rid of the `-' at the beginning of $os.
+ os=`echo $os | sed 's/[^-]*-//'`
+ echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system. Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+case $basic_machine in
+ *-acorn)
+ os=-riscix1.2
+ ;;
+ arm*-semi)
+ os=-aout
+ ;;
+ pdp11-*)
+ os=-none
+ ;;
+ *-dec | vax-*)
+ os=-ultrix4.2
+ ;;
+ m68*-apollo)
+ os=-domain
+ ;;
+ i386-sun)
+ os=-sunos4.0.2
+ ;;
+ m68000-sun)
+ os=-sunos3
+ # This also exists in the configure program, but was not the
+ # default.
+ # os=-sunos4
+ ;;
+ *-tti) # must be before sparc entry or we get the wrong os.
+ os=-sysv3
+ ;;
+ sparc-* | *-sun)
+ os=-sunos4.1.1
+ ;;
+ *-be)
+ os=-beos
+ ;;
+ *-ibm)
+ os=-aix
+ ;;
+ *-hp)
+ os=-hpux
+ ;;
+ *-hitachi)
+ os=-hiux
+ ;;
+ i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+ os=-sysv
+ ;;
+ *-cbm)
+ os=-amigaos
+ ;;
+ *-dg)
+ os=-dgux
+ ;;
+ *-dolphin)
+ os=-sysv3
+ ;;
+ m68k-ccur)
+ os=-rtu
+ ;;
+ m88k-omron*)
+ os=-luna
+ ;;
+ *-next )
+ os=-nextstep
+ ;;
+ *-sequent)
+ os=-ptx
+ ;;
+ *-crds)
+ os=-unos
+ ;;
+ *-ns)
+ os=-genix
+ ;;
+ i370-*)
+ os=-mvs
+ ;;
+ *-next)
+ os=-nextstep3
+ ;;
+ *-gould)
+ os=-sysv
+ ;;
+ *-highlevel)
+ os=-bsd
+ ;;
+ *-encore)
+ os=-bsd
+ ;;
+ *-sgi)
+ os=-irix
+ ;;
+ *-siemens)
+ os=-sysv4
+ ;;
+ *-masscomp)
+ os=-rtu
+ ;;
+ f301-fujitsu)
+ os=-uxpv
+ ;;
+ *)
+ os=-none
+ ;;
+esac
+fi
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer. We pick the logical manufacturer.
+vendor=unknown
+case $basic_machine in
+ *-unknown)
+ case $os in
+ -riscix*)
+ vendor=acorn
+ ;;
+ -sunos*)
+ vendor=sun
+ ;;
+ -aix*)
+ vendor=ibm
+ ;;
+ -hpux*)
+ vendor=hp
+ ;;
+ -hiux*)
+ vendor=hitachi
+ ;;
+ -unos*)
+ vendor=crds
+ ;;
+ -dgux*)
+ vendor=dg
+ ;;
+ -luna*)
+ vendor=omron
+ ;;
+ -genix*)
+ vendor=ns
+ ;;
+ -mvs*)
+ vendor=ibm
+ ;;
+ -ptx*)
+ vendor=sequent
+ ;;
+ -vxsim* | -vxworks*)
+ vendor=wrs
+ ;;
+ -aux*)
+ vendor=apple
+ ;;
+ esac
+ basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
+ ;;
+esac
+
+echo $basic_machine$os
--- /dev/null
+#! /bin/sh
+
+# Guess values for system-dependent variables and create Makefiles.
+# Generated automatically using autoconf version 2.13
+# Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc.
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+
+# Defaults:
+ac_help=
+ac_default_prefix=/usr/local
+# Any additions from configure.in:
+ac_help="$ac_help
+ --with-x use the X Window System"
+ac_help="$ac_help
+ --with-krb4=PREFIX Use Kerberos 4"
+ac_help="$ac_help
+ --with-hesiod=PREFIX Use Hesiod"
+ac_help="$ac_help
+ --with-regex=PREFIX Use installed regex library"
+ac_help="$ac_help
+ --with-ares=PREFIX Use libares"
+ac_help="$ac_help
+ --with-com_err=PREFIX Specify location of com_err"
+ac_help="$ac_help
+ --with-ss=PREFIX Specify location of ss (requires com_err)"
+
+# Initialize some variables set by options.
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+build=NONE
+cache_file=./config.cache
+exec_prefix=NONE
+host=NONE
+no_create=
+nonopt=NONE
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+target=NONE
+verbose=
+x_includes=NONE
+x_libraries=NONE
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datadir='${prefix}/share'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+libdir='${exec_prefix}/lib'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+infodir='${prefix}/info'
+mandir='${prefix}/man'
+
+# Initialize some other variables.
+subdirs=
+MFLAGS= MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+# Maximum number of lines to put in a shell here document.
+ac_max_here_lines=12
+
+ac_prev=
+for ac_option
+do
+
+ # If the previous option needs an argument, assign it.
+ if test -n "$ac_prev"; then
+ eval "$ac_prev=\$ac_option"
+ ac_prev=
+ continue
+ fi
+
+ case "$ac_option" in
+ -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
+ *) ac_optarg= ;;
+ esac
+
+ # Accept the important Cygnus configure options, so we can diagnose typos.
+
+ case "$ac_option" in
+
+ -bindir | --bindir | --bindi | --bind | --bin | --bi)
+ ac_prev=bindir ;;
+ -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+ bindir="$ac_optarg" ;;
+
+ -build | --build | --buil | --bui | --bu)
+ ac_prev=build ;;
+ -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+ build="$ac_optarg" ;;
+
+ -cache-file | --cache-file | --cache-fil | --cache-fi \
+ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+ ac_prev=cache_file ;;
+ -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+ cache_file="$ac_optarg" ;;
+
+ -datadir | --datadir | --datadi | --datad | --data | --dat | --da)
+ ac_prev=datadir ;;
+ -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \
+ | --da=*)
+ datadir="$ac_optarg" ;;
+
+ -disable-* | --disable-*)
+ ac_feature=`echo $ac_option|sed -e 's/-*disable-//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then
+ { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+ fi
+ ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+ eval "enable_${ac_feature}=no" ;;
+
+ -enable-* | --enable-*)
+ ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then
+ { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+ fi
+ ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+ case "$ac_option" in
+ *=*) ;;
+ *) ac_optarg=yes ;;
+ esac
+ eval "enable_${ac_feature}='$ac_optarg'" ;;
+
+ -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+ | --exec | --exe | --ex)
+ ac_prev=exec_prefix ;;
+ -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+ | --exec=* | --exe=* | --ex=*)
+ exec_prefix="$ac_optarg" ;;
+
+ -gas | --gas | --ga | --g)
+ # Obsolete; use --with-gas.
+ with_gas=yes ;;
+
+ -help | --help | --hel | --he)
+ # Omit some internal or obsolete options to make the list less imposing.
+ # This message is too long to be a string in the A/UX 3.1 sh.
+ cat << EOF
+Usage: configure [options] [host]
+Options: [defaults in brackets after descriptions]
+Configuration:
+ --cache-file=FILE cache test results in FILE
+ --help print this message
+ --no-create do not create output files
+ --quiet, --silent do not print \`checking...' messages
+ --version print the version of autoconf that created configure
+Directory and file names:
+ --prefix=PREFIX install architecture-independent files in PREFIX
+ [$ac_default_prefix]
+ --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX
+ [same as prefix]
+ --bindir=DIR user executables in DIR [EPREFIX/bin]
+ --sbindir=DIR system admin executables in DIR [EPREFIX/sbin]
+ --libexecdir=DIR program executables in DIR [EPREFIX/libexec]
+ --datadir=DIR read-only architecture-independent data in DIR
+ [PREFIX/share]
+ --sysconfdir=DIR read-only single-machine data in DIR [PREFIX/etc]
+ --sharedstatedir=DIR modifiable architecture-independent data in DIR
+ [PREFIX/com]
+ --localstatedir=DIR modifiable single-machine data in DIR [PREFIX/var]
+ --libdir=DIR object code libraries in DIR [EPREFIX/lib]
+ --includedir=DIR C header files in DIR [PREFIX/include]
+ --oldincludedir=DIR C header files for non-gcc in DIR [/usr/include]
+ --infodir=DIR info documentation in DIR [PREFIX/info]
+ --mandir=DIR man documentation in DIR [PREFIX/man]
+ --srcdir=DIR find the sources in DIR [configure dir or ..]
+ --program-prefix=PREFIX prepend PREFIX to installed program names
+ --program-suffix=SUFFIX append SUFFIX to installed program names
+ --program-transform-name=PROGRAM
+ run sed PROGRAM on installed program names
+EOF
+ cat << EOF
+Host type:
+ --build=BUILD configure for building on BUILD [BUILD=HOST]
+ --host=HOST configure for HOST [guessed]
+ --target=TARGET configure for TARGET [TARGET=HOST]
+Features and packages:
+ --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
+ --enable-FEATURE[=ARG] include FEATURE [ARG=yes]
+ --with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
+ --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
+ --x-includes=DIR X include files are in DIR
+ --x-libraries=DIR X library files are in DIR
+EOF
+ if test -n "$ac_help"; then
+ echo "--enable and --with options recognized:$ac_help"
+ fi
+ exit 0 ;;
+
+ -host | --host | --hos | --ho)
+ ac_prev=host ;;
+ -host=* | --host=* | --hos=* | --ho=*)
+ host="$ac_optarg" ;;
+
+ -includedir | --includedir | --includedi | --included | --include \
+ | --includ | --inclu | --incl | --inc)
+ ac_prev=includedir ;;
+ -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+ | --includ=* | --inclu=* | --incl=* | --inc=*)
+ includedir="$ac_optarg" ;;
+
+ -infodir | --infodir | --infodi | --infod | --info | --inf)
+ ac_prev=infodir ;;
+ -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+ infodir="$ac_optarg" ;;
+
+ -libdir | --libdir | --libdi | --libd)
+ ac_prev=libdir ;;
+ -libdir=* | --libdir=* | --libdi=* | --libd=*)
+ libdir="$ac_optarg" ;;
+
+ -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+ | --libexe | --libex | --libe)
+ ac_prev=libexecdir ;;
+ -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+ | --libexe=* | --libex=* | --libe=*)
+ libexecdir="$ac_optarg" ;;
+
+ -localstatedir | --localstatedir | --localstatedi | --localstated \
+ | --localstate | --localstat | --localsta | --localst \
+ | --locals | --local | --loca | --loc | --lo)
+ ac_prev=localstatedir ;;
+ -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+ | --localstate=* | --localstat=* | --localsta=* | --localst=* \
+ | --locals=* | --local=* | --loca=* | --loc=* | --lo=*)
+ localstatedir="$ac_optarg" ;;
+
+ -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+ ac_prev=mandir ;;
+ -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+ mandir="$ac_optarg" ;;
+
+ -nfp | --nfp | --nf)
+ # Obsolete; use --without-fp.
+ with_fp=no ;;
+
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c)
+ no_create=yes ;;
+
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+ no_recursion=yes ;;
+
+ -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+ | --oldin | --oldi | --old | --ol | --o)
+ ac_prev=oldincludedir ;;
+ -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+ oldincludedir="$ac_optarg" ;;
+
+ -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+ ac_prev=prefix ;;
+ -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+ prefix="$ac_optarg" ;;
+
+ -program-prefix | --program-prefix | --program-prefi | --program-pref \
+ | --program-pre | --program-pr | --program-p)
+ ac_prev=program_prefix ;;
+ -program-prefix=* | --program-prefix=* | --program-prefi=* \
+ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+ program_prefix="$ac_optarg" ;;
+
+ -program-suffix | --program-suffix | --program-suffi | --program-suff \
+ | --program-suf | --program-su | --program-s)
+ ac_prev=program_suffix ;;
+ -program-suffix=* | --program-suffix=* | --program-suffi=* \
+ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+ program_suffix="$ac_optarg" ;;
+
+ -program-transform-name | --program-transform-name \
+ | --program-transform-nam | --program-transform-na \
+ | --program-transform-n | --program-transform- \
+ | --program-transform | --program-transfor \
+ | --program-transfo | --program-transf \
+ | --program-trans | --program-tran \
+ | --progr-tra | --program-tr | --program-t)
+ ac_prev=program_transform_name ;;
+ -program-transform-name=* | --program-transform-name=* \
+ | --program-transform-nam=* | --program-transform-na=* \
+ | --program-transform-n=* | --program-transform-=* \
+ | --program-transform=* | --program-transfor=* \
+ | --program-transfo=* | --program-transf=* \
+ | --program-trans=* | --program-tran=* \
+ | --progr-tra=* | --program-tr=* | --program-t=*)
+ program_transform_name="$ac_optarg" ;;
+
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ silent=yes ;;
+
+ -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+ ac_prev=sbindir ;;
+ -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+ | --sbi=* | --sb=*)
+ sbindir="$ac_optarg" ;;
+
+ -sharedstatedir | --sharedstatedir | --sharedstatedi \
+ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+ | --sharedst | --shareds | --shared | --share | --shar \
+ | --sha | --sh)
+ ac_prev=sharedstatedir ;;
+ -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+ | --sha=* | --sh=*)
+ sharedstatedir="$ac_optarg" ;;
+
+ -site | --site | --sit)
+ ac_prev=site ;;
+ -site=* | --site=* | --sit=*)
+ site="$ac_optarg" ;;
+
+ -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+ ac_prev=srcdir ;;
+ -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+ srcdir="$ac_optarg" ;;
+
+ -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+ | --syscon | --sysco | --sysc | --sys | --sy)
+ ac_prev=sysconfdir ;;
+ -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+ sysconfdir="$ac_optarg" ;;
+
+ -target | --target | --targe | --targ | --tar | --ta | --t)
+ ac_prev=target ;;
+ -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+ target="$ac_optarg" ;;
+
+ -v | -verbose | --verbose | --verbos | --verbo | --verb)
+ verbose=yes ;;
+
+ -version | --version | --versio | --versi | --vers)
+ echo "configure generated by autoconf version 2.13"
+ exit 0 ;;
+
+ -with-* | --with-*)
+ ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then
+ { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+ fi
+ ac_package=`echo $ac_package| sed 's/-/_/g'`
+ case "$ac_option" in
+ *=*) ;;
+ *) ac_optarg=yes ;;
+ esac
+ eval "with_${ac_package}='$ac_optarg'" ;;
+
+ -without-* | --without-*)
+ ac_package=`echo $ac_option|sed -e 's/-*without-//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then
+ { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+ fi
+ ac_package=`echo $ac_package| sed 's/-/_/g'`
+ eval "with_${ac_package}=no" ;;
+
+ --x)
+ # Obsolete; use --with-x.
+ with_x=yes ;;
+
+ -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+ | --x-incl | --x-inc | --x-in | --x-i)
+ ac_prev=x_includes ;;
+ -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+ x_includes="$ac_optarg" ;;
+
+ -x-libraries | --x-libraries | --x-librarie | --x-librari \
+ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+ ac_prev=x_libraries ;;
+ -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+ x_libraries="$ac_optarg" ;;
+
+ -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; }
+ ;;
+
+ *)
+ if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then
+ echo "configure: warning: $ac_option: invalid host type" 1>&2
+ fi
+ if test "x$nonopt" != xNONE; then
+ { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; }
+ fi
+ nonopt="$ac_option"
+ ;;
+
+ esac
+done
+
+if test -n "$ac_prev"; then
+ { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; }
+fi
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+# File descriptor usage:
+# 0 standard input
+# 1 file creation
+# 2 errors and warnings
+# 3 some systems may open it to /dev/tty
+# 4 used on the Kubota Titan
+# 6 checking for... messages and results
+# 5 compiler messages saved in config.log
+if test "$silent" = yes; then
+ exec 6>/dev/null
+else
+ exec 6>&1
+fi
+exec 5>./config.log
+
+echo "\
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+" 1>&5
+
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Also quote any args containing shell metacharacters.
+ac_configure_args=
+for ac_arg
+do
+ case "$ac_arg" in
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c) ;;
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;;
+ *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*)
+ ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+ *) ac_configure_args="$ac_configure_args $ac_arg" ;;
+ esac
+done
+
+# NLS nuisances.
+# Only set these to C if already set. These must not be set unconditionally
+# because not all systems understand e.g. LANG=C (notably SCO).
+# Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'!
+# Non-C LC_CTYPE values break the ctype check.
+if test "${LANG+set}" = set; then LANG=C; export LANG; fi
+if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi
+if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi
+if test "${LC_CTYPE+set}" = set; then LC_CTYPE=C; export LC_CTYPE; fi
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -rf conftest* confdefs.h
+# AIX cpp loses on an empty file, so make sure it contains at least a newline.
+echo > confdefs.h
+
+# A filename unique to this package, relative to the directory that
+# configure is in, which we can look for to find out if srcdir is correct.
+ac_unique_file=server/server.c
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+ ac_srcdir_defaulted=yes
+ # Try the directory containing this script, then its parent.
+ ac_prog=$0
+ ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'`
+ test "x$ac_confdir" = "x$ac_prog" && ac_confdir=.
+ srcdir=$ac_confdir
+ if test ! -r $srcdir/$ac_unique_file; then
+ srcdir=..
+ fi
+else
+ ac_srcdir_defaulted=no
+fi
+if test ! -r $srcdir/$ac_unique_file; then
+ if test "$ac_srcdir_defaulted" = yes; then
+ { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; }
+ else
+ { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; }
+ fi
+fi
+srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'`
+
+# Prefer explicitly selected file to automatically selected ones.
+if test -z "$CONFIG_SITE"; then
+ if test "x$prefix" != xNONE; then
+ CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site"
+ else
+ CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+ fi
+fi
+for ac_site_file in $CONFIG_SITE; do
+ if test -r "$ac_site_file"; then
+ echo "loading site script $ac_site_file"
+ . "$ac_site_file"
+ fi
+done
+
+if test -r "$cache_file"; then
+ echo "loading cache $cache_file"
+ . $cache_file
+else
+ echo "creating cache $cache_file"
+ > $cache_file
+fi
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+ac_exeext=
+ac_objext=o
+if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then
+ # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu.
+ if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then
+ ac_n= ac_c='
+' ac_t=' '
+ else
+ ac_n=-n ac_c= ac_t=
+ fi
+else
+ ac_n= ac_c='\c' ac_t=
+fi
+
+
+
+test -d h || mkdir h
+test -d h/zephyr || mkdir h/zephyr
+
+test -z "$lbindir" && lbindir='${bindir}'
+test -z "$lsbindir" && lsbindir='${sbindir}'
+
+
+
+ac_aux_dir=
+for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do
+ if test -f $ac_dir/install-sh; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install-sh -c"
+ break
+ elif test -f $ac_dir/install.sh; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install.sh -c"
+ break
+ fi
+done
+if test -z "$ac_aux_dir"; then
+ { echo "configure: error: can not find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." 1>&2; exit 1; }
+fi
+ac_config_guess=$ac_aux_dir/config.guess
+ac_config_sub=$ac_aux_dir/config.sub
+ac_configure=$ac_aux_dir/configure # This should be Cygnus configure.
+
+
+# Do some error checking and defaulting for the host and target type.
+# The inputs are:
+# configure --host=HOST --target=TARGET --build=BUILD NONOPT
+#
+# The rules are:
+# 1. You are not allowed to specify --host, --target, and nonopt at the
+# same time.
+# 2. Host defaults to nonopt.
+# 3. If nonopt is not specified, then host defaults to the current host,
+# as determined by config.guess.
+# 4. Target and build default to nonopt.
+# 5. If nonopt is not specified, then target and build default to host.
+
+# The aliases save the names the user supplied, while $host etc.
+# will get canonicalized.
+case $host---$target---$nonopt in
+NONE---*---* | *---NONE---* | *---*---NONE) ;;
+*) { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; } ;;
+esac
+
+
+# Make sure we can run config.sub.
+if ${CONFIG_SHELL-/bin/sh} $ac_config_sub sun4 >/dev/null 2>&1; then :
+else { echo "configure: error: can not run $ac_config_sub" 1>&2; exit 1; }
+fi
+
+echo $ac_n "checking host system type""... $ac_c" 1>&6
+echo "configure:595: checking host system type" >&5
+
+host_alias=$host
+case "$host_alias" in
+NONE)
+ case $nonopt in
+ NONE)
+ if host_alias=`${CONFIG_SHELL-/bin/sh} $ac_config_guess`; then :
+ else { echo "configure: error: can not guess host type; you must specify one" 1>&2; exit 1; }
+ fi ;;
+ *) host_alias=$nonopt ;;
+ esac ;;
+esac
+
+host=`${CONFIG_SHELL-/bin/sh} $ac_config_sub $host_alias`
+host_cpu=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
+host_vendor=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
+host_os=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
+echo "$ac_t""$host" 1>&6
+
+echo $ac_n "checking target system type""... $ac_c" 1>&6
+echo "configure:616: checking target system type" >&5
+
+target_alias=$target
+case "$target_alias" in
+NONE)
+ case $nonopt in
+ NONE) target_alias=$host_alias ;;
+ *) target_alias=$nonopt ;;
+ esac ;;
+esac
+
+target=`${CONFIG_SHELL-/bin/sh} $ac_config_sub $target_alias`
+target_cpu=`echo $target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
+target_vendor=`echo $target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
+target_os=`echo $target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
+echo "$ac_t""$target" 1>&6
+
+echo $ac_n "checking build system type""... $ac_c" 1>&6
+echo "configure:634: checking build system type" >&5
+
+build_alias=$build
+case "$build_alias" in
+NONE)
+ case $nonopt in
+ NONE) build_alias=$host_alias ;;
+ *) build_alias=$nonopt ;;
+ esac ;;
+esac
+
+build=`${CONFIG_SHELL-/bin/sh} $ac_config_sub $build_alias`
+build_cpu=`echo $build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
+build_vendor=`echo $build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
+build_os=`echo $build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
+echo "$ac_t""$build" 1>&6
+
+test "$host_alias" != "$target_alias" &&
+ test "$program_prefix$program_suffix$program_transform_name" = \
+ NONENONEs,x,x, &&
+ program_prefix=${target_alias}-
+
+cat >> confdefs.h <<EOF
+#define MACHINE_TYPE "$host"
+EOF
+
+
+# Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:664: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_CC="gcc"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+ echo "$ac_t""$CC" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+if test -z "$CC"; then
+ # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:694: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_prog_rejected=no
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then
+ ac_prog_rejected=yes
+ continue
+ fi
+ ac_cv_prog_CC="cc"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+if test $ac_prog_rejected = yes; then
+ # We found a bogon in the path, so make sure we never use it.
+ set dummy $ac_cv_prog_CC
+ shift
+ if test $# -gt 0; then
+ # We chose a different compiler from the bogus one.
+ # However, it has the same basename, so the bogon will be chosen
+ # first if we set CC to just the basename; use the full file name.
+ shift
+ set dummy "$ac_dir/$ac_word" "$@"
+ shift
+ ac_cv_prog_CC="$@"
+ fi
+fi
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+ echo "$ac_t""$CC" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+ if test -z "$CC"; then
+ case "`uname -s`" in
+ *win32* | *WIN32*)
+ # Extract the first word of "cl", so it can be a program name with args.
+set dummy cl; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:745: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_CC="cl"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+ echo "$ac_t""$CC" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+ ;;
+ esac
+ fi
+ test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; }
+fi
+
+echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6
+echo "configure:777: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+cat > conftest.$ac_ext << EOF
+
+#line 788 "configure"
+#include "confdefs.h"
+
+main(){return(0);}
+EOF
+if { (eval echo configure:793: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ ac_cv_prog_cc_works=yes
+ # If we can't run a trivial program, we are probably using a cross compiler.
+ if (./conftest; exit) 2>/dev/null; then
+ ac_cv_prog_cc_cross=no
+ else
+ ac_cv_prog_cc_cross=yes
+ fi
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ ac_cv_prog_cc_works=no
+fi
+rm -fr conftest*
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+echo "$ac_t""$ac_cv_prog_cc_works" 1>&6
+if test $ac_cv_prog_cc_works = no; then
+ { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; }
+fi
+echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6
+echo "configure:819: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5
+echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6
+cross_compiling=$ac_cv_prog_cc_cross
+
+echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6
+echo "configure:824: checking whether we are using GNU C" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.c <<EOF
+#ifdef __GNUC__
+ yes;
+#endif
+EOF
+if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:833: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then
+ ac_cv_prog_gcc=yes
+else
+ ac_cv_prog_gcc=no
+fi
+fi
+
+echo "$ac_t""$ac_cv_prog_gcc" 1>&6
+
+if test $ac_cv_prog_gcc = yes; then
+ GCC=yes
+else
+ GCC=
+fi
+
+ac_test_CFLAGS="${CFLAGS+set}"
+ac_save_CFLAGS="$CFLAGS"
+CFLAGS=
+echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6
+echo "configure:852: checking whether ${CC-cc} accepts -g" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ echo 'void f(){}' > conftest.c
+if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then
+ ac_cv_prog_cc_g=yes
+else
+ ac_cv_prog_cc_g=no
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$ac_cv_prog_cc_g" 1>&6
+if test "$ac_test_CFLAGS" = set; then
+ CFLAGS="$ac_save_CFLAGS"
+elif test $ac_cv_prog_cc_g = yes; then
+ if test "$GCC" = yes; then
+ CFLAGS="-g -O2"
+ else
+ CFLAGS="-g"
+ fi
+else
+ if test "$GCC" = yes; then
+ CFLAGS="-O2"
+ else
+ CFLAGS=
+ fi
+fi
+
+for ac_prog in 'bison -y' byacc
+do
+# Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:888: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_YACC'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$YACC"; then
+ ac_cv_prog_YACC="$YACC" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_YACC="$ac_prog"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+fi
+fi
+YACC="$ac_cv_prog_YACC"
+if test -n "$YACC"; then
+ echo "$ac_t""$YACC" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+test -n "$YACC" && break
+done
+test -n "$YACC" || YACC="yacc"
+
+# Extract the first word of "flex", so it can be a program name with args.
+set dummy flex; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:921: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_LEX'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$LEX"; then
+ ac_cv_prog_LEX="$LEX" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_LEX="flex"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+ test -z "$ac_cv_prog_LEX" && ac_cv_prog_LEX="lex"
+fi
+fi
+LEX="$ac_cv_prog_LEX"
+if test -n "$LEX"; then
+ echo "$ac_t""$LEX" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+if test -z "$LEXLIB"
+then
+ case "$LEX" in
+ flex*) ac_lib=fl ;;
+ *) ac_lib=l ;;
+ esac
+ echo $ac_n "checking for yywrap in -l$ac_lib""... $ac_c" 1>&6
+echo "configure:955: checking for yywrap in -l$ac_lib" >&5
+ac_lib_var=`echo $ac_lib'_'yywrap | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-l$ac_lib $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 963 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char yywrap();
+
+int main() {
+yywrap()
+; return 0; }
+EOF
+if { (eval echo configure:974: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ LEXLIB="-l$ac_lib"
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+fi
+
+# Find a good install program. We prefer a C program (faster),
+# so one script is as good as another. But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# ./install, which can be erroneously created by make from ./install.sh.
+echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6
+echo "configure:1008: checking for a BSD compatible install" >&5
+if test -z "$INSTALL"; then
+if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ IFS="${IFS= }"; ac_save_IFS="$IFS"; IFS=":"
+ for ac_dir in $PATH; do
+ # Account for people who put trailing slashes in PATH elements.
+ case "$ac_dir/" in
+ /|./|.//|/etc/*|/usr/sbin/*|/usr/etc/*|/sbin/*|/usr/afsws/bin/*|/usr/ucb/*) ;;
+ *)
+ # OSF1 and SCO ODT 3.0 have their own names for install.
+ # Don't use installbsd from OSF since it installs stuff as root
+ # by default.
+ for ac_prog in ginstall scoinst install; do
+ if test -f $ac_dir/$ac_prog; then
+ if test $ac_prog = install &&
+ grep dspmsg $ac_dir/$ac_prog >/dev/null 2>&1; then
+ # AIX install. It has an incompatible calling convention.
+ :
+ else
+ ac_cv_path_install="$ac_dir/$ac_prog -c"
+ break 2
+ fi
+ fi
+ done
+ ;;
+ esac
+ done
+ IFS="$ac_save_IFS"
+
+fi
+ if test "${ac_cv_path_install+set}" = set; then
+ INSTALL="$ac_cv_path_install"
+ else
+ # As a last resort, use the slow shell script. We don't cache a
+ # path for INSTALL within a source directory, because that will
+ # break other packages using the cache if that directory is
+ # removed, or if the path is relative.
+ INSTALL="$ac_install_sh"
+ fi
+fi
+echo "$ac_t""$INSTALL" 1>&6
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL_PROGRAM}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+# Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:1063: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$RANLIB"; then
+ ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_RANLIB="ranlib"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+ test -z "$ac_cv_prog_RANLIB" && ac_cv_prog_RANLIB=":"
+fi
+fi
+RANLIB="$ac_cv_prog_RANLIB"
+if test -n "$RANLIB"; then
+ echo "$ac_t""$RANLIB" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+
+echo $ac_n "checking location of temporary directory""... $ac_c" 1>&6
+echo "configure:1092: checking location of temporary directory" >&5
+if test -d /var/tmp; then
+ found_tmp=/var/tmp/
+elif test -d /usr/tmp; then
+ found_tmp=/usr/tmp/
+else
+ found_tmp=/tmp/
+fi
+cat >> confdefs.h <<EOF
+#define FOUND_TMP "${found_tmp}"
+EOF
+
+echo "$ac_t""${found_tmp}" 1>&6
+
+echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6
+echo "configure:1107: checking how to run the C preprocessor" >&5
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+ CPP=
+fi
+if test -z "$CPP"; then
+if eval "test \"`echo '$''{'ac_cv_prog_CPP'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ # This must be in double quotes, not single quotes, because CPP may get
+ # substituted into the Makefile and "${CC-cc}" will confuse make.
+ CPP="${CC-cc} -E"
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp.
+ cat > conftest.$ac_ext <<EOF
+#line 1122 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1128: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ :
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ CPP="${CC-cc} -E -traditional-cpp"
+ cat > conftest.$ac_ext <<EOF
+#line 1139 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1145: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ :
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ CPP="${CC-cc} -nologo -E"
+ cat > conftest.$ac_ext <<EOF
+#line 1156 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1162: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ :
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ CPP=/lib/cpp
+fi
+rm -f conftest*
+fi
+rm -f conftest*
+fi
+rm -f conftest*
+ ac_cv_prog_CPP="$CPP"
+fi
+ CPP="$ac_cv_prog_CPP"
+else
+ ac_cv_prog_CPP="$CPP"
+fi
+echo "$ac_t""$CPP" 1>&6
+
+# If we find X, set shell vars x_includes and x_libraries to the
+# paths, otherwise set no_x=yes.
+# Uses ac_ vars as temps to allow command line to override cache and checks.
+# --without-x overrides everything else, but does not touch the cache.
+echo $ac_n "checking for X""... $ac_c" 1>&6
+echo "configure:1191: checking for X" >&5
+
+# Check whether --with-x or --without-x was given.
+if test "${with_x+set}" = set; then
+ withval="$with_x"
+ :
+fi
+
+# $have_x is `yes', `no', `disabled', or empty when we do not yet know.
+if test "x$with_x" = xno; then
+ # The user explicitly disabled X.
+ have_x=disabled
+else
+ if test "x$x_includes" != xNONE && test "x$x_libraries" != xNONE; then
+ # Both variables are already set.
+ have_x=yes
+ else
+if eval "test \"`echo '$''{'ac_cv_have_x'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ # One or both of the vars are not set, and there is no cached value.
+ac_x_includes=NO ac_x_libraries=NO
+rm -fr conftestdir
+if mkdir conftestdir; then
+ cd conftestdir
+ # Make sure to not put "make" in the Imakefile rules, since we grep it out.
+ cat > Imakefile <<'EOF'
+acfindx:
+ @echo 'ac_im_incroot="${INCROOT}"; ac_im_usrlibdir="${USRLIBDIR}"; ac_im_libdir="${LIBDIR}"'
+EOF
+ if (xmkmf) >/dev/null 2>/dev/null && test -f Makefile; then
+ # GNU make sometimes prints "make[1]: Entering...", which would confuse us.
+ eval `${MAKE-make} acfindx 2>/dev/null | grep -v make`
+ # Open Windows xmkmf reportedly sets LIBDIR instead of USRLIBDIR.
+ for ac_extension in a so sl; do
+ if test ! -f $ac_im_usrlibdir/libX11.$ac_extension &&
+ test -f $ac_im_libdir/libX11.$ac_extension; then
+ ac_im_usrlibdir=$ac_im_libdir; break
+ fi
+ done
+ # Screen out bogus values from the imake configuration. They are
+ # bogus both because they are the default anyway, and because
+ # using them would break gcc on systems where it needs fixed includes.
+ case "$ac_im_incroot" in
+ /usr/include) ;;
+ *) test -f "$ac_im_incroot/X11/Xos.h" && ac_x_includes="$ac_im_incroot" ;;
+ esac
+ case "$ac_im_usrlibdir" in
+ /usr/lib | /lib) ;;
+ *) test -d "$ac_im_usrlibdir" && ac_x_libraries="$ac_im_usrlibdir" ;;
+ esac
+ fi
+ cd ..
+ rm -fr conftestdir
+fi
+
+if test "$ac_x_includes" = NO; then
+ # Guess where to find include files, by looking for this one X11 .h file.
+ test -z "$x_direct_test_include" && x_direct_test_include=X11/Intrinsic.h
+
+ # First, try using that file with no special directory specified.
+cat > conftest.$ac_ext <<EOF
+#line 1253 "configure"
+#include "confdefs.h"
+#include <$x_direct_test_include>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1258: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ # We can compile using X headers with no special include directory.
+ac_x_includes=
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ # Look for the header file in a standard set of common directories.
+# Check X11 before X11Rn because it is often a symlink to the current release.
+ for ac_dir in \
+ /usr/X11/include \
+ /usr/X11R6/include \
+ /usr/X11R5/include \
+ /usr/X11R4/include \
+ \
+ /usr/include/X11 \
+ /usr/include/X11R6 \
+ /usr/include/X11R5 \
+ /usr/include/X11R4 \
+ \
+ /usr/local/X11/include \
+ /usr/local/X11R6/include \
+ /usr/local/X11R5/include \
+ /usr/local/X11R4/include \
+ \
+ /usr/local/include/X11 \
+ /usr/local/include/X11R6 \
+ /usr/local/include/X11R5 \
+ /usr/local/include/X11R4 \
+ \
+ /usr/X386/include \
+ /usr/x386/include \
+ /usr/XFree86/include/X11 \
+ \
+ /usr/include \
+ /usr/local/include \
+ /usr/unsupported/include \
+ /usr/athena/include \
+ /usr/local/x11r5/include \
+ /usr/lpp/Xamples/include \
+ \
+ /usr/openwin/include \
+ /usr/openwin/share/include \
+ ; \
+ do
+ if test -r "$ac_dir/$x_direct_test_include"; then
+ ac_x_includes=$ac_dir
+ break
+ fi
+ done
+fi
+rm -f conftest*
+fi # $ac_x_includes = NO
+
+if test "$ac_x_libraries" = NO; then
+ # Check for the libraries.
+
+ test -z "$x_direct_test_library" && x_direct_test_library=Xt
+ test -z "$x_direct_test_function" && x_direct_test_function=XtMalloc
+
+ # See if we find them without any special options.
+ # Don't add to $LIBS permanently.
+ ac_save_LIBS="$LIBS"
+ LIBS="-l$x_direct_test_library $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1327 "configure"
+#include "confdefs.h"
+
+int main() {
+${x_direct_test_function}()
+; return 0; }
+EOF
+if { (eval echo configure:1334: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ LIBS="$ac_save_LIBS"
+# We can link X programs with no special library path.
+ac_x_libraries=
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ LIBS="$ac_save_LIBS"
+# First see if replacing the include by lib works.
+# Check X11 before X11Rn because it is often a symlink to the current release.
+for ac_dir in `echo "$ac_x_includes" | sed s/include/lib/` \
+ /usr/X11/lib \
+ /usr/X11R6/lib \
+ /usr/X11R5/lib \
+ /usr/X11R4/lib \
+ \
+ /usr/lib/X11 \
+ /usr/lib/X11R6 \
+ /usr/lib/X11R5 \
+ /usr/lib/X11R4 \
+ \
+ /usr/local/X11/lib \
+ /usr/local/X11R6/lib \
+ /usr/local/X11R5/lib \
+ /usr/local/X11R4/lib \
+ \
+ /usr/local/lib/X11 \
+ /usr/local/lib/X11R6 \
+ /usr/local/lib/X11R5 \
+ /usr/local/lib/X11R4 \
+ \
+ /usr/X386/lib \
+ /usr/x386/lib \
+ /usr/XFree86/lib/X11 \
+ \
+ /usr/lib \
+ /usr/local/lib \
+ /usr/unsupported/lib \
+ /usr/athena/lib \
+ /usr/local/x11r5/lib \
+ /usr/lpp/Xamples/lib \
+ /lib/usr/lib/X11 \
+ \
+ /usr/openwin/lib \
+ /usr/openwin/share/lib \
+ ; \
+do
+ for ac_extension in a so sl; do
+ if test -r $ac_dir/lib${x_direct_test_library}.$ac_extension; then
+ ac_x_libraries=$ac_dir
+ break 2
+ fi
+ done
+done
+fi
+rm -f conftest*
+fi # $ac_x_libraries = NO
+
+if test "$ac_x_includes" = NO || test "$ac_x_libraries" = NO; then
+ # Didn't find X anywhere. Cache the known absence of X.
+ ac_cv_have_x="have_x=no"
+else
+ # Record where we found X for the cache.
+ ac_cv_have_x="have_x=yes \
+ ac_x_includes=$ac_x_includes ac_x_libraries=$ac_x_libraries"
+fi
+fi
+ fi
+ eval "$ac_cv_have_x"
+fi # $with_x != no
+
+if test "$have_x" != yes; then
+ echo "$ac_t""$have_x" 1>&6
+ no_x=yes
+else
+ # If each of the values was on the command line, it overrides each guess.
+ test "x$x_includes" = xNONE && x_includes=$ac_x_includes
+ test "x$x_libraries" = xNONE && x_libraries=$ac_x_libraries
+ # Update the cache value to reflect the command line values.
+ ac_cv_have_x="have_x=yes \
+ ac_x_includes=$x_includes ac_x_libraries=$x_libraries"
+ echo "$ac_t""libraries $x_libraries, headers $x_includes" 1>&6
+fi
+
+if test "$no_x" = yes; then
+ # Not all programs may use this symbol, but it does not hurt to define it.
+ cat >> confdefs.h <<\EOF
+#define X_DISPLAY_MISSING 1
+EOF
+
+ X_CFLAGS= X_PRE_LIBS= X_LIBS= X_EXTRA_LIBS=
+else
+ if test -n "$x_includes"; then
+ X_CFLAGS="$X_CFLAGS -I$x_includes"
+ fi
+
+ # It would also be nice to do this for all -L options, not just this one.
+ if test -n "$x_libraries"; then
+ X_LIBS="$X_LIBS -L$x_libraries"
+ # For Solaris; some versions of Sun CC require a space after -R and
+ # others require no space. Words are not sufficient . . . .
+ case "`(uname -sr) 2>/dev/null`" in
+ "SunOS 5"*)
+ echo $ac_n "checking whether -R must be followed by a space""... $ac_c" 1>&6
+echo "configure:1440: checking whether -R must be followed by a space" >&5
+ ac_xsave_LIBS="$LIBS"; LIBS="$LIBS -R$x_libraries"
+ cat > conftest.$ac_ext <<EOF
+#line 1443 "configure"
+#include "confdefs.h"
+
+int main() {
+
+; return 0; }
+EOF
+if { (eval echo configure:1450: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ ac_R_nospace=yes
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ ac_R_nospace=no
+fi
+rm -f conftest*
+ if test $ac_R_nospace = yes; then
+ echo "$ac_t""no" 1>&6
+ X_LIBS="$X_LIBS -R$x_libraries"
+ else
+ LIBS="$ac_xsave_LIBS -R $x_libraries"
+ cat > conftest.$ac_ext <<EOF
+#line 1466 "configure"
+#include "confdefs.h"
+
+int main() {
+
+; return 0; }
+EOF
+if { (eval echo configure:1473: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ ac_R_space=yes
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ ac_R_space=no
+fi
+rm -f conftest*
+ if test $ac_R_space = yes; then
+ echo "$ac_t""yes" 1>&6
+ X_LIBS="$X_LIBS -R $x_libraries"
+ else
+ echo "$ac_t""neither works" 1>&6
+ fi
+ fi
+ LIBS="$ac_xsave_LIBS"
+ esac
+ fi
+
+ # Check for system-dependent libraries X programs must link with.
+ # Do this before checking for the system-independent R6 libraries
+ # (-lICE), since we may need -lsocket or whatever for X linking.
+
+ if test "$ISC" = yes; then
+ X_EXTRA_LIBS="$X_EXTRA_LIBS -lnsl_s -linet"
+ else
+ # Martyn.Johnson@cl.cam.ac.uk says this is needed for Ultrix, if the X
+ # libraries were built with DECnet support. And karl@cs.umb.edu says
+ # the Alpha needs dnet_stub (dnet does not exist).
+ echo $ac_n "checking for dnet_ntoa in -ldnet""... $ac_c" 1>&6
+echo "configure:1505: checking for dnet_ntoa in -ldnet" >&5
+ac_lib_var=`echo dnet'_'dnet_ntoa | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-ldnet $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1513 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char dnet_ntoa();
+
+int main() {
+dnet_ntoa()
+; return 0; }
+EOF
+if { (eval echo configure:1524: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ X_EXTRA_LIBS="$X_EXTRA_LIBS -ldnet"
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+ if test $ac_cv_lib_dnet_dnet_ntoa = no; then
+ echo $ac_n "checking for dnet_ntoa in -ldnet_stub""... $ac_c" 1>&6
+echo "configure:1546: checking for dnet_ntoa in -ldnet_stub" >&5
+ac_lib_var=`echo dnet_stub'_'dnet_ntoa | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-ldnet_stub $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1554 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char dnet_ntoa();
+
+int main() {
+dnet_ntoa()
+; return 0; }
+EOF
+if { (eval echo configure:1565: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ X_EXTRA_LIBS="$X_EXTRA_LIBS -ldnet_stub"
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+ fi
+
+ # msh@cis.ufl.edu says -lnsl (and -lsocket) are needed for his 386/AT,
+ # to get the SysV transport functions.
+ # chad@anasazi.com says the Pyramis MIS-ES running DC/OSx (SVR4)
+ # needs -lnsl.
+ # The nsl library prevents programs from opening the X display
+ # on Irix 5.2, according to dickey@clark.net.
+ echo $ac_n "checking for gethostbyname""... $ac_c" 1>&6
+echo "configure:1594: checking for gethostbyname" >&5
+if eval "test \"`echo '$''{'ac_cv_func_gethostbyname'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1599 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char gethostbyname(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char gethostbyname();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_gethostbyname) || defined (__stub___gethostbyname)
+choke me
+#else
+gethostbyname();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:1622: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func_gethostbyname=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_gethostbyname=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'gethostbyname`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ :
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+ if test $ac_cv_func_gethostbyname = no; then
+ echo $ac_n "checking for gethostbyname in -lnsl""... $ac_c" 1>&6
+echo "configure:1643: checking for gethostbyname in -lnsl" >&5
+ac_lib_var=`echo nsl'_'gethostbyname | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lnsl $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1651 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char gethostbyname();
+
+int main() {
+gethostbyname()
+; return 0; }
+EOF
+if { (eval echo configure:1662: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ X_EXTRA_LIBS="$X_EXTRA_LIBS -lnsl"
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+ fi
+
+ # lieder@skyler.mavd.honeywell.com says without -lsocket,
+ # socket/setsockopt and other routines are undefined under SCO ODT
+ # 2.0. But -lsocket is broken on IRIX 5.2 (and is not necessary
+ # on later versions), says simon@lia.di.epfl.ch: it contains
+ # gethostby* variants that don't use the nameserver (or something).
+ # -lsocket must be given before -lnsl if both are needed.
+ # We assume that if connect needs -lnsl, so does gethostbyname.
+ echo $ac_n "checking for connect""... $ac_c" 1>&6
+echo "configure:1692: checking for connect" >&5
+if eval "test \"`echo '$''{'ac_cv_func_connect'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1697 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char connect(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char connect();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_connect) || defined (__stub___connect)
+choke me
+#else
+connect();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:1720: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func_connect=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_connect=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'connect`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ :
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+ if test $ac_cv_func_connect = no; then
+ echo $ac_n "checking for connect in -lsocket""... $ac_c" 1>&6
+echo "configure:1741: checking for connect in -lsocket" >&5
+ac_lib_var=`echo socket'_'connect | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lsocket $X_EXTRA_LIBS $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1749 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char connect();
+
+int main() {
+connect()
+; return 0; }
+EOF
+if { (eval echo configure:1760: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ X_EXTRA_LIBS="-lsocket $X_EXTRA_LIBS"
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+ fi
+
+ # gomez@mi.uni-erlangen.de says -lposix is necessary on A/UX.
+ echo $ac_n "checking for remove""... $ac_c" 1>&6
+echo "configure:1784: checking for remove" >&5
+if eval "test \"`echo '$''{'ac_cv_func_remove'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1789 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char remove(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char remove();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_remove) || defined (__stub___remove)
+choke me
+#else
+remove();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:1812: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func_remove=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_remove=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'remove`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ :
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+ if test $ac_cv_func_remove = no; then
+ echo $ac_n "checking for remove in -lposix""... $ac_c" 1>&6
+echo "configure:1833: checking for remove in -lposix" >&5
+ac_lib_var=`echo posix'_'remove | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lposix $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1841 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char remove();
+
+int main() {
+remove()
+; return 0; }
+EOF
+if { (eval echo configure:1852: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ X_EXTRA_LIBS="$X_EXTRA_LIBS -lposix"
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+ fi
+
+ # BSDI BSD/OS 2.1 needs -lipc for XOpenDisplay.
+ echo $ac_n "checking for shmat""... $ac_c" 1>&6
+echo "configure:1876: checking for shmat" >&5
+if eval "test \"`echo '$''{'ac_cv_func_shmat'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1881 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char shmat(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char shmat();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_shmat) || defined (__stub___shmat)
+choke me
+#else
+shmat();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:1904: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func_shmat=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_shmat=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'shmat`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ :
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+ if test $ac_cv_func_shmat = no; then
+ echo $ac_n "checking for shmat in -lipc""... $ac_c" 1>&6
+echo "configure:1925: checking for shmat in -lipc" >&5
+ac_lib_var=`echo ipc'_'shmat | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lipc $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1933 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char shmat();
+
+int main() {
+shmat()
+; return 0; }
+EOF
+if { (eval echo configure:1944: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ X_EXTRA_LIBS="$X_EXTRA_LIBS -lipc"
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+ fi
+ fi
+
+ # Check for libraries that X11R6 Xt/Xaw programs need.
+ ac_save_LDFLAGS="$LDFLAGS"
+ test -n "$x_libraries" && LDFLAGS="$LDFLAGS -L$x_libraries"
+ # SM needs ICE to (dynamically) link under SunOS 4.x (so we have to
+ # check for ICE first), but we must link in the order -lSM -lICE or
+ # we get undefined symbols. So assume we have SM if we have ICE.
+ # These have to be linked with before -lX11, unlike the other
+ # libraries we check for below, so use a different variable.
+ # --interran@uluru.Stanford.EDU, kb@cs.umb.edu.
+ echo $ac_n "checking for IceConnectionNumber in -lICE""... $ac_c" 1>&6
+echo "configure:1977: checking for IceConnectionNumber in -lICE" >&5
+ac_lib_var=`echo ICE'_'IceConnectionNumber | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lICE $X_EXTRA_LIBS $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1985 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char IceConnectionNumber();
+
+int main() {
+IceConnectionNumber()
+; return 0; }
+EOF
+if { (eval echo configure:1996: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ X_PRE_LIBS="$X_PRE_LIBS -lSM -lICE"
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+ LDFLAGS="$ac_save_LDFLAGS"
+
+fi
+
+echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&6
+echo "configure:2021: checking for ANSI C header files" >&5
+if eval "test \"`echo '$''{'ac_cv_header_stdc'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2026 "configure"
+#include "confdefs.h"
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:2034: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ ac_cv_header_stdc=yes
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+if test $ac_cv_header_stdc = yes; then
+ # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+cat > conftest.$ac_ext <<EOF
+#line 2051 "configure"
+#include "confdefs.h"
+#include <string.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "memchr" >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+cat > conftest.$ac_ext <<EOF
+#line 2069 "configure"
+#include "confdefs.h"
+#include <stdlib.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "free" >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+if test "$cross_compiling" = yes; then
+ :
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2090 "configure"
+#include "confdefs.h"
+#include <ctype.h>
+#define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+#define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int main () { int i; for (i = 0; i < 256; i++)
+if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2);
+exit (0); }
+
+EOF
+if { (eval echo configure:2101: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+ :
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -fr conftest*
+ ac_cv_header_stdc=no
+fi
+rm -fr conftest*
+fi
+
+fi
+fi
+
+echo "$ac_t""$ac_cv_header_stdc" 1>&6
+if test $ac_cv_header_stdc = yes; then
+ cat >> confdefs.h <<\EOF
+#define STDC_HEADERS 1
+EOF
+
+fi
+
+echo $ac_n "checking for sys/wait.h that is POSIX.1 compatible""... $ac_c" 1>&6
+echo "configure:2125: checking for sys/wait.h that is POSIX.1 compatible" >&5
+if eval "test \"`echo '$''{'ac_cv_header_sys_wait_h'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2130 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <sys/wait.h>
+#ifndef WEXITSTATUS
+#define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
+#endif
+#ifndef WIFEXITED
+#define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
+#endif
+int main() {
+int s;
+wait (&s);
+s = WIFEXITED (s) ? WEXITSTATUS (s) : 1;
+; return 0; }
+EOF
+if { (eval echo configure:2146: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+ rm -rf conftest*
+ ac_cv_header_sys_wait_h=yes
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ ac_cv_header_sys_wait_h=no
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_header_sys_wait_h" 1>&6
+if test $ac_cv_header_sys_wait_h = yes; then
+ cat >> confdefs.h <<\EOF
+#define HAVE_SYS_WAIT_H 1
+EOF
+
+fi
+
+for ac_hdr in fcntl.h paths.h termios.h sgtty.h unistd.h malloc.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "configure:2170: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2175 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:2180: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=yes"
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+done
+
+for ac_hdr in sys/filio.h sys/ioctl.h sys/time.h sys/file.h sys/utsname.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "configure:2210: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2215 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:2220: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=yes"
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+done
+
+for ac_hdr in sys/select.h sys/msgbuf.h sys/cdefs.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "configure:2250: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2255 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:2260: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=yes"
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+done
+
+
+if test "$no_x" != "yes"; then
+ XCLIENTS=xzwrite
+ ZWGC_LIBX11=-lX11
+fi
+
+
+
+echo $ac_n "checking return type of signal handlers""... $ac_c" 1>&6
+echo "configure:2295: checking return type of signal handlers" >&5
+if eval "test \"`echo '$''{'ac_cv_type_signal'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2300 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <signal.h>
+#ifdef signal
+#undef signal
+#endif
+#ifdef __cplusplus
+extern "C" void (*signal (int, void (*)(int)))(int);
+#else
+void (*signal ()) ();
+#endif
+
+int main() {
+int i;
+; return 0; }
+EOF
+if { (eval echo configure:2317: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+ rm -rf conftest*
+ ac_cv_type_signal=void
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ ac_cv_type_signal=int
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_type_signal" 1>&6
+cat >> confdefs.h <<EOF
+#define RETSIGTYPE $ac_cv_type_signal
+EOF
+
+
+echo $ac_n "checking for uid_t in sys/types.h""... $ac_c" 1>&6
+echo "configure:2336: checking for uid_t in sys/types.h" >&5
+if eval "test \"`echo '$''{'ac_cv_type_uid_t'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2341 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "uid_t" >/dev/null 2>&1; then
+ rm -rf conftest*
+ ac_cv_type_uid_t=yes
+else
+ rm -rf conftest*
+ ac_cv_type_uid_t=no
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$ac_cv_type_uid_t" 1>&6
+if test $ac_cv_type_uid_t = no; then
+ cat >> confdefs.h <<\EOF
+#define uid_t int
+EOF
+
+ cat >> confdefs.h <<\EOF
+#define gid_t int
+EOF
+
+fi
+
+echo $ac_n "checking size of short""... $ac_c" 1>&6
+echo "configure:2370: checking size of short" >&5
+if eval "test \"`echo '$''{'ac_cv_sizeof_short'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test "$cross_compiling" = yes; then
+ { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2378 "configure"
+#include "confdefs.h"
+#include <stdio.h>
+main()
+{
+ FILE *f=fopen("conftestval", "w");
+ if (!f) exit(1);
+ fprintf(f, "%d\n", sizeof(short));
+ exit(0);
+}
+EOF
+if { (eval echo configure:2389: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+ ac_cv_sizeof_short=`cat conftestval`
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -fr conftest*
+ ac_cv_sizeof_short=0
+fi
+rm -fr conftest*
+fi
+
+fi
+echo "$ac_t""$ac_cv_sizeof_short" 1>&6
+cat >> confdefs.h <<EOF
+#define SIZEOF_SHORT $ac_cv_sizeof_short
+EOF
+
+
+echo $ac_n "checking size of int""... $ac_c" 1>&6
+echo "configure:2409: checking size of int" >&5
+if eval "test \"`echo '$''{'ac_cv_sizeof_int'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test "$cross_compiling" = yes; then
+ { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2417 "configure"
+#include "confdefs.h"
+#include <stdio.h>
+main()
+{
+ FILE *f=fopen("conftestval", "w");
+ if (!f) exit(1);
+ fprintf(f, "%d\n", sizeof(int));
+ exit(0);
+}
+EOF
+if { (eval echo configure:2428: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+ ac_cv_sizeof_int=`cat conftestval`
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -fr conftest*
+ ac_cv_sizeof_int=0
+fi
+rm -fr conftest*
+fi
+
+fi
+echo "$ac_t""$ac_cv_sizeof_int" 1>&6
+cat >> confdefs.h <<EOF
+#define SIZEOF_INT $ac_cv_sizeof_int
+EOF
+
+
+echo $ac_n "checking size of long""... $ac_c" 1>&6
+echo "configure:2448: checking size of long" >&5
+if eval "test \"`echo '$''{'ac_cv_sizeof_long'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test "$cross_compiling" = yes; then
+ { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2456 "configure"
+#include "confdefs.h"
+#include <stdio.h>
+main()
+{
+ FILE *f=fopen("conftestval", "w");
+ if (!f) exit(1);
+ fprintf(f, "%d\n", sizeof(long));
+ exit(0);
+}
+EOF
+if { (eval echo configure:2467: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+ ac_cv_sizeof_long=`cat conftestval`
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -fr conftest*
+ ac_cv_sizeof_long=0
+fi
+rm -fr conftest*
+fi
+
+fi
+echo "$ac_t""$ac_cv_sizeof_long" 1>&6
+cat >> confdefs.h <<EOF
+#define SIZEOF_LONG $ac_cv_sizeof_long
+EOF
+
+
+echo $ac_n "checking for 32-bit integer type""... $ac_c" 1>&6
+echo "configure:2487: checking for 32-bit integer type" >&5
+if test "$ac_cv_sizeof_long" = 4; then
+ int32=long
+elif test "$ac_cv_sizeof_int" = 4; then
+ int32=int
+elif test "$ac_cv_sizeof_short" = 4; then
+ int32=short
+else
+ echo "configure: warning: Can't find 32-bit type, using long" 1>&2
+ int32=long
+fi
+cat >> confdefs.h <<EOF
+#define ZEPHYR_INT32 ${int32}
+EOF
+
+echo "$ac_t""${int32}" 1>&6
+
+echo $ac_n "checking for wslen in -lw""... $ac_c" 1>&6
+echo "configure:2505: checking for wslen in -lw" >&5
+ac_lib_var=`echo w'_'wslen | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lw $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 2513 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char wslen();
+
+int main() {
+wslen()
+; return 0; }
+EOF
+if { (eval echo configure:2524: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_lib=HAVE_LIB`echo w | sed -e 's/[^a-zA-Z0-9_]/_/g' \
+ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+ LIBS="-lw $LIBS"
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+echo $ac_n "checking for dlopen in -ldl""... $ac_c" 1>&6
+echo "configure:2552: checking for dlopen in -ldl" >&5
+ac_lib_var=`echo dl'_'dlopen | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-ldl $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 2560 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char dlopen();
+
+int main() {
+dlopen()
+; return 0; }
+EOF
+if { (eval echo configure:2571: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_lib=HAVE_LIB`echo dl | sed -e 's/[^a-zA-Z0-9_]/_/g' \
+ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+ LIBS="-ldl $LIBS"
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+
+
+echo $ac_n "checking for library containing gethostbyname""... $ac_c" 1>&6
+echo "configure:2601: checking for library containing gethostbyname" >&5
+if eval "test \"`echo '$''{'ac_cv_search_gethostbyname'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_func_search_save_LIBS="$LIBS"
+ac_cv_search_gethostbyname="no"
+cat > conftest.$ac_ext <<EOF
+#line 2608 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char gethostbyname();
+
+int main() {
+gethostbyname()
+; return 0; }
+EOF
+if { (eval echo configure:2619: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ ac_cv_search_gethostbyname="none required"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+fi
+rm -f conftest*
+test "$ac_cv_search_gethostbyname" = "no" && for i in nsl; do
+LIBS="-l$i $ac_func_search_save_LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 2630 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char gethostbyname();
+
+int main() {
+gethostbyname()
+; return 0; }
+EOF
+if { (eval echo configure:2641: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ ac_cv_search_gethostbyname="-l$i"
+break
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+fi
+rm -f conftest*
+done
+LIBS="$ac_func_search_save_LIBS"
+fi
+
+echo "$ac_t""$ac_cv_search_gethostbyname" 1>&6
+if test "$ac_cv_search_gethostbyname" != "no"; then
+ test "$ac_cv_search_gethostbyname" = "none required" || LIBS="$ac_cv_search_gethostbyname $LIBS"
+
+else :
+
+fi
+
+echo $ac_n "checking for library containing socket""... $ac_c" 1>&6
+echo "configure:2663: checking for library containing socket" >&5
+if eval "test \"`echo '$''{'ac_cv_search_socket'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_func_search_save_LIBS="$LIBS"
+ac_cv_search_socket="no"
+cat > conftest.$ac_ext <<EOF
+#line 2670 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char socket();
+
+int main() {
+socket()
+; return 0; }
+EOF
+if { (eval echo configure:2681: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ ac_cv_search_socket="none required"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+fi
+rm -f conftest*
+test "$ac_cv_search_socket" = "no" && for i in socket; do
+LIBS="-l$i $ac_func_search_save_LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 2692 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char socket();
+
+int main() {
+socket()
+; return 0; }
+EOF
+if { (eval echo configure:2703: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ ac_cv_search_socket="-l$i"
+break
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+fi
+rm -f conftest*
+done
+LIBS="$ac_func_search_save_LIBS"
+fi
+
+echo "$ac_t""$ac_cv_search_socket" 1>&6
+if test "$ac_cv_search_socket" != "no"; then
+ test "$ac_cv_search_socket" = "none required" || LIBS="$ac_cv_search_socket $LIBS"
+
+else :
+
+fi
+
+# Hesiod needs -lresolv on Sun systems for res_send.
+if test "$hesiod" != "no"; then
+ echo $ac_n "checking for strerror in -l44bsd""... $ac_c" 1>&6
+echo "configure:2727: checking for strerror in -l44bsd" >&5
+ac_lib_var=`echo 44bsd'_'strerror | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-l44bsd $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 2735 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char strerror();
+
+int main() {
+strerror()
+; return 0; }
+EOF
+if { (eval echo configure:2746: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_lib=HAVE_LIB`echo 44bsd | sed -e 's/[^a-zA-Z0-9_]/_/g' \
+ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+ LIBS="-l44bsd $LIBS"
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+ echo $ac_n "checking for res_send in -lresolv""... $ac_c" 1>&6
+echo "configure:2774: checking for res_send in -lresolv" >&5
+ac_lib_var=`echo resolv'_'res_send | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lresolv $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 2782 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char res_send();
+
+int main() {
+res_send()
+; return 0; }
+EOF
+if { (eval echo configure:2793: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_lib=HAVE_LIB`echo resolv | sed -e 's/[^a-zA-Z0-9_]/_/g' \
+ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+ LIBS="-lresolv $LIBS"
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+fi
+
+echo $ac_n "checking for tgetstr in -lcurses""... $ac_c" 1>&6
+echo "configure:2823: checking for tgetstr in -lcurses" >&5
+ac_lib_var=`echo curses'_'tgetstr | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lcurses $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 2831 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char tgetstr();
+
+int main() {
+tgetstr()
+; return 0; }
+EOF
+if { (eval echo configure:2842: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ TLIB=-lcurses
+else
+ echo "$ac_t""no" 1>&6
+TLIB=-ltermcap
+fi
+
+echo $ac_n "checking for srcsrpy in -lsrc""... $ac_c" 1>&6
+echo "configure:2864: checking for srcsrpy in -lsrc" >&5
+ac_lib_var=`echo src'_'srcsrpy | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lsrc $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 2872 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char srcsrpy();
+
+int main() {
+srcsrpy()
+; return 0; }
+EOF
+if { (eval echo configure:2883: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ SLIB=-lsrc; cat >> confdefs.h <<\EOF
+#define HAVE_SRC 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+
+
+
+
+# Check whether --with-krb4 or --without-krb4 was given.
+if test "${with_krb4+set}" = set; then
+ withval="$with_krb4"
+ krb4="$withval"
+else
+ krb4=no
+fi
+
+if test "$krb4" != no; then
+ echo $ac_n "checking for gethostbyname""... $ac_c" 1>&6
+echo "configure:2920: checking for gethostbyname" >&5
+if eval "test \"`echo '$''{'ac_cv_func_gethostbyname'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2925 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char gethostbyname(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char gethostbyname();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_gethostbyname) || defined (__stub___gethostbyname)
+choke me
+#else
+gethostbyname();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:2948: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func_gethostbyname=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_gethostbyname=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'gethostbyname`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ :
+else
+ echo "$ac_t""no" 1>&6
+echo $ac_n "checking for gethostbyname in -lnsl""... $ac_c" 1>&6
+echo "configure:2966: checking for gethostbyname in -lnsl" >&5
+ac_lib_var=`echo nsl'_'gethostbyname | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lnsl $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 2974 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char gethostbyname();
+
+int main() {
+gethostbyname()
+; return 0; }
+EOF
+if { (eval echo configure:2985: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_lib=HAVE_LIB`echo nsl | sed -e 's/^a-zA-Z0-9_/_/g' \
+ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+ LIBS="-lnsl $LIBS"
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+fi
+
+echo $ac_n "checking for socket""... $ac_c" 1>&6
+echo "configure:3015: checking for socket" >&5
+if eval "test \"`echo '$''{'ac_cv_func_socket'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 3020 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char socket(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char socket();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_socket) || defined (__stub___socket)
+choke me
+#else
+socket();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:3043: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func_socket=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_socket=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'socket`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ :
+else
+ echo "$ac_t""no" 1>&6
+echo $ac_n "checking for socket in -lsocket""... $ac_c" 1>&6
+echo "configure:3061: checking for socket in -lsocket" >&5
+ac_lib_var=`echo socket'_'socket | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lsocket $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 3069 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char socket();
+
+int main() {
+socket()
+; return 0; }
+EOF
+if { (eval echo configure:3080: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_lib=HAVE_LIB`echo socket | sed -e 's/^a-zA-Z0-9_/_/g' \
+ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+ LIBS="-lsocket $LIBS"
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+fi
+
+echo $ac_n "checking for compile in -lgen""... $ac_c" 1>&6
+echo "configure:3110: checking for compile in -lgen" >&5
+ac_lib_var=`echo gen'_'compile | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lgen $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 3118 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char compile();
+
+int main() {
+compile()
+; return 0; }
+EOF
+if { (eval echo configure:3129: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_lib=HAVE_LIB`echo gen | sed -e 's/[^a-zA-Z0-9_]/_/g' \
+ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+ LIBS="-lgen $LIBS"
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+if test "$krb4" != yes; then
+ CPPFLAGS="$CPPFLAGS -I$krb4/include"
+ if test -d "$krb4/include/kerberosIV"; then
+ CPPFLAGS="$CPPFLAGS -I$krb4/include/kerberosIV"
+ fi
+ LDFLAGS="$LDFLAGS -L$krb4/lib"
+fi
+echo $ac_n "checking for krb_rd_req in -lkrb4""... $ac_c" 1>&6
+echo "configure:3164: checking for krb_rd_req in -lkrb4" >&5
+ac_lib_var=`echo krb4'_'krb_rd_req | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lkrb4 -ldes425 -lkrb5 -lk5crypto -lcom_err $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 3172 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char krb_rd_req();
+
+int main() {
+krb_rd_req()
+; return 0; }
+EOF
+if { (eval echo configure:3183: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ KRB4_LIBS="-lkrb4 -ldes425 -lkrb5 -lk5crypto -lcom_err"
+else
+ echo "$ac_t""no" 1>&6
+echo $ac_n "checking for krb_rd_req in -lkrb""... $ac_c" 1>&6
+echo "configure:3202: checking for krb_rd_req in -lkrb" >&5
+ac_lib_var=`echo krb'_'krb_rd_req | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lkrb -ldes $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 3210 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char krb_rd_req();
+
+int main() {
+krb_rd_req()
+; return 0; }
+EOF
+if { (eval echo configure:3221: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ KRB4_LIBS="-lkrb -ldes"
+else
+ echo "$ac_t""no" 1>&6
+{ echo "configure: error: Kerberos 4 libraries not found" 1>&2; exit 1; }
+fi
+
+fi
+
+ cat >> confdefs.h <<\EOF
+#define HAVE_KRB4 1
+EOF
+
+fi
+
+# Check whether --with-hesiod or --without-hesiod was given.
+if test "${with_hesiod+set}" = set; then
+ withval="$with_hesiod"
+ hesiod="$withval"
+else
+ hesiod=no
+fi
+
+if test "$hesiod" != no; then
+ echo $ac_n "checking for res_send""... $ac_c" 1>&6
+echo "configure:3260: checking for res_send" >&5
+if eval "test \"`echo '$''{'ac_cv_func_res_send'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 3265 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char res_send(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char res_send();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_res_send) || defined (__stub___res_send)
+choke me
+#else
+res_send();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:3288: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func_res_send=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_res_send=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'res_send`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ :
+else
+ echo "$ac_t""no" 1>&6
+echo $ac_n "checking for res_send in -lresolv""... $ac_c" 1>&6
+echo "configure:3306: checking for res_send in -lresolv" >&5
+ac_lib_var=`echo resolv'_'res_send | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lresolv $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 3314 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char res_send();
+
+int main() {
+res_send()
+; return 0; }
+EOF
+if { (eval echo configure:3325: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_lib=HAVE_LIB`echo resolv | sed -e 's/^a-zA-Z0-9_/_/g' \
+ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+ LIBS="-lresolv $LIBS"
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+fi
+
+if test "$hesiod" != yes; then
+ CPPFLAGS="$CPPFLAGS -I$hesiod/include"
+ LDFLAGS="$LDFLAGS -L$hesiod/lib"
+fi
+echo $ac_n "checking for hes_resolve in -lhesiod""... $ac_c" 1>&6
+echo "configure:3359: checking for hes_resolve in -lhesiod" >&5
+ac_lib_var=`echo hesiod'_'hes_resolve | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lhesiod $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 3367 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char hes_resolve();
+
+int main() {
+hes_resolve()
+; return 0; }
+EOF
+if { (eval echo configure:3378: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ :
+else
+ echo "$ac_t""no" 1>&6
+{ echo "configure: error: Hesiod library not found" 1>&2; exit 1; }
+fi
+
+ HESIOD_LIBS="-lhesiod"
+ cat >> confdefs.h <<\EOF
+#define HAVE_HESIOD 1
+EOF
+
+fi
+
+# Check whether --with-regex or --without-regex was given.
+if test "${with_regex+set}" = set; then
+ withval="$with_regex"
+ regex="$withval"
+else
+ regex=no
+fi
+
+if test "$regex" != no; then
+ if test "$regex" != yes; then
+ CPPFLAGS="$CPPFLAGS -I$regex/include"
+ LDFLAGS="$LDFLAGS -L$regex/lib"
+ fi
+ echo $ac_n "checking for regcomp in -lregex""... $ac_c" 1>&6
+echo "configure:3420: checking for regcomp in -lregex" >&5
+ac_lib_var=`echo regex'_'regcomp | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lregex $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 3428 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char regcomp();
+
+int main() {
+regcomp()
+; return 0; }
+EOF
+if { (eval echo configure:3439: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ REGEX_LIBS=-lregex
+else
+ echo "$ac_t""no" 1>&6
+{ echo "configure: error: regex library not found" 1>&2; exit 1; }
+fi
+
+else
+ echo $ac_n "checking for regcomp""... $ac_c" 1>&6
+echo "configure:3462: checking for regcomp" >&5
+if eval "test \"`echo '$''{'ac_cv_func_regcomp'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 3467 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char regcomp(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char regcomp();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_regcomp) || defined (__stub___regcomp)
+choke me
+#else
+regcomp();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:3490: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func_regcomp=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_regcomp=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'regcomp`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ :
+else
+ echo "$ac_t""no" 1>&6
+{ echo "configure: error: can't find POSIX regexp support" 1>&2; exit 1; }
+fi
+
+fi
+
+# Check whether --with-ares or --without-ares was given.
+if test "${with_ares+set}" = set; then
+ withval="$with_ares"
+ ares="$withval"
+else
+ ares=no
+fi
+
+if test "$ares" != no; then
+ echo $ac_n "checking for res_send""... $ac_c" 1>&6
+echo "configure:3522: checking for res_send" >&5
+if eval "test \"`echo '$''{'ac_cv_func_res_send'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 3527 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char res_send(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char res_send();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_res_send) || defined (__stub___res_send)
+choke me
+#else
+res_send();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:3550: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func_res_send=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_res_send=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'res_send`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ :
+else
+ echo "$ac_t""no" 1>&6
+echo $ac_n "checking for res_send in -lresolv""... $ac_c" 1>&6
+echo "configure:3568: checking for res_send in -lresolv" >&5
+ac_lib_var=`echo resolv'_'res_send | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lresolv $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 3576 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char res_send();
+
+int main() {
+res_send()
+; return 0; }
+EOF
+if { (eval echo configure:3587: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_lib=HAVE_LIB`echo resolv | sed -e 's/^a-zA-Z0-9_/_/g' \
+ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+ LIBS="-lresolv $LIBS"
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+fi
+
+if test "$ares" != yes; then
+ CPPFLAGS="$CPPFLAGS -I$ares/include"
+ LDFLAGS="$LDFLAGS -L$ares/lib"
+fi
+echo $ac_n "checking for ares_init in -lares""... $ac_c" 1>&6
+echo "configure:3621: checking for ares_init in -lares" >&5
+ac_lib_var=`echo ares'_'ares_init | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lares $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 3629 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char ares_init();
+
+int main() {
+ares_init()
+; return 0; }
+EOF
+if { (eval echo configure:3640: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ :
+else
+ echo "$ac_t""no" 1>&6
+{ echo "configure: error: libares not found" 1>&2; exit 1; }
+fi
+
+ ARES_LIBS="-lares"
+ cat >> confdefs.h <<\EOF
+#define HAVE_ARES 1
+EOF
+
+fi
+
+# Check whether --with-com_err or --without-com_err was given.
+if test "${with_com_err+set}" = set; then
+ withval="$with_com_err"
+ com_err="$withval"
+else
+ com_err=yes
+fi
+
+if test "$com_err" != no; then
+ if test "$com_err" != yes; then
+ CPPFLAGS="$CPPFLAGS -I$com_err/include"
+ LDFLAGS="$LDFLAGS -L$com_err/lib"
+ fi
+ echo $ac_n "checking for com_err in -lcom_err""... $ac_c" 1>&6
+echo "configure:3682: checking for com_err in -lcom_err" >&5
+ac_lib_var=`echo com_err'_'com_err | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lcom_err $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 3690 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char com_err();
+
+int main() {
+com_err()
+; return 0; }
+EOF
+if { (eval echo configure:3701: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ :
+else
+ echo "$ac_t""no" 1>&6
+{ echo "configure: error: com_err library not found" 1>&2; exit 1; }
+fi
+
+else
+ { echo "configure: error: This package requires com_err." 1>&2; exit 1; }
+fi
+# Check whether --with-ss or --without-ss was given.
+if test "${with_ss+set}" = set; then
+ withval="$with_ss"
+ ss="$withval"
+else
+ ss=yes
+fi
+
+if test "$ss" != no; then
+ if test "$ss" != yes; then
+ CPPFLAGS="$CPPFLAGS -I$ss/include"
+ LDFLAGS="$LDFLAGS -L$ss/lib"
+ fi
+ echo $ac_n "checking for ss_perror in -lss""... $ac_c" 1>&6
+echo "configure:3739: checking for ss_perror in -lss" >&5
+ac_lib_var=`echo ss'_'ss_perror | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lss -lcom_err $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 3747 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char ss_perror();
+
+int main() {
+ss_perror()
+; return 0; }
+EOF
+if { (eval echo configure:3758: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ :
+else
+ echo "$ac_t""no" 1>&6
+{ echo "configure: error: ss library not found" 1>&2; exit 1; }
+fi
+
+else
+ { echo "configure: error: This package requires ss." 1>&2; exit 1; }
+fi
+LIBS="$KRB4_LIBS $HESIOD_LIBS $LIBS"
+
+if test $ac_cv_prog_gcc = yes; then
+ echo $ac_n "checking whether ${CC-cc} needs -traditional""... $ac_c" 1>&6
+echo "configure:3786: checking whether ${CC-cc} needs -traditional" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_gcc_traditional'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_pattern="Autoconf.*'x'"
+ cat > conftest.$ac_ext <<EOF
+#line 3792 "configure"
+#include "confdefs.h"
+#include <sgtty.h>
+Autoconf TIOCGETP
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "$ac_pattern" >/dev/null 2>&1; then
+ rm -rf conftest*
+ ac_cv_prog_gcc_traditional=yes
+else
+ rm -rf conftest*
+ ac_cv_prog_gcc_traditional=no
+fi
+rm -f conftest*
+
+
+ if test $ac_cv_prog_gcc_traditional = no; then
+ cat > conftest.$ac_ext <<EOF
+#line 3810 "configure"
+#include "confdefs.h"
+#include <termio.h>
+Autoconf TCGETA
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "$ac_pattern" >/dev/null 2>&1; then
+ rm -rf conftest*
+ ac_cv_prog_gcc_traditional=yes
+fi
+rm -f conftest*
+
+ fi
+fi
+
+echo "$ac_t""$ac_cv_prog_gcc_traditional" 1>&6
+ if test $ac_cv_prog_gcc_traditional = yes; then
+ CC="$CC -traditional"
+ fi
+fi
+
+echo $ac_n "checking for vprintf""... $ac_c" 1>&6
+echo "configure:3832: checking for vprintf" >&5
+if eval "test \"`echo '$''{'ac_cv_func_vprintf'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 3837 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char vprintf(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char vprintf();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_vprintf) || defined (__stub___vprintf)
+choke me
+#else
+vprintf();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:3860: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func_vprintf=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_vprintf=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'vprintf`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ cat >> confdefs.h <<\EOF
+#define HAVE_VPRINTF 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+if test "$ac_cv_func_vprintf" != yes; then
+echo $ac_n "checking for _doprnt""... $ac_c" 1>&6
+echo "configure:3884: checking for _doprnt" >&5
+if eval "test \"`echo '$''{'ac_cv_func__doprnt'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 3889 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char _doprnt(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char _doprnt();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub__doprnt) || defined (__stub____doprnt)
+choke me
+#else
+_doprnt();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:3912: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func__doprnt=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func__doprnt=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'_doprnt`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ cat >> confdefs.h <<\EOF
+#define HAVE_DOPRNT 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+fi
+
+echo $ac_n "checking whether getpgrp takes no argument""... $ac_c" 1>&6
+echo "configure:3937: checking whether getpgrp takes no argument" >&5
+if eval "test \"`echo '$''{'ac_cv_func_getpgrp_void'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test "$cross_compiling" = yes; then
+ { echo "configure: error: cannot check getpgrp if cross compiling" 1>&2; exit 1; }
+else
+ cat > conftest.$ac_ext <<EOF
+#line 3945 "configure"
+#include "confdefs.h"
+
+/*
+ * If this system has a BSD-style getpgrp(),
+ * which takes a pid argument, exit unsuccessfully.
+ *
+ * Snarfed from Chet Ramey's bash pgrp.c test program
+ */
+#include <stdio.h>
+#include <sys/types.h>
+
+int pid;
+int pg1, pg2, pg3, pg4;
+int ng, np, s, child;
+
+main()
+{
+ pid = getpid();
+ pg1 = getpgrp(0);
+ pg2 = getpgrp();
+ pg3 = getpgrp(pid);
+ pg4 = getpgrp(1);
+
+ /*
+ * If all of these values are the same, it's pretty sure that
+ * we're on a system that ignores getpgrp's first argument.
+ */
+ if (pg2 == pg4 && pg1 == pg3 && pg2 == pg3)
+ exit(0);
+
+ child = fork();
+ if (child < 0)
+ exit(1);
+ else if (child == 0) {
+ np = getpid();
+ /*
+ * If this is Sys V, this will not work; pgrp will be
+ * set to np because setpgrp just changes a pgrp to be
+ * the same as the pid.
+ */
+ setpgrp(np, pg1);
+ ng = getpgrp(0); /* Same result for Sys V and BSD */
+ if (ng == pg1) {
+ exit(1);
+ } else {
+ exit(0);
+ }
+ } else {
+ wait(&s);
+ exit(s>>8);
+ }
+}
+
+EOF
+if { (eval echo configure:4000: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+ ac_cv_func_getpgrp_void=yes
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -fr conftest*
+ ac_cv_func_getpgrp_void=no
+fi
+rm -fr conftest*
+fi
+
+
+fi
+
+echo "$ac_t""$ac_cv_func_getpgrp_void" 1>&6
+if test $ac_cv_func_getpgrp_void = yes; then
+ cat >> confdefs.h <<\EOF
+#define GETPGRP_VOID 1
+EOF
+
+fi
+
+echo $ac_n "checking whether setpgrp takes no argument""... $ac_c" 1>&6
+echo "configure:4024: checking whether setpgrp takes no argument" >&5
+if eval "test \"`echo '$''{'ac_cv_func_setpgrp_void'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test "$cross_compiling" = yes; then
+ { echo "configure: error: cannot check setpgrp if cross compiling" 1>&2; exit 1; }
+else
+ cat > conftest.$ac_ext <<EOF
+#line 4032 "configure"
+#include "confdefs.h"
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+/*
+ * If this system has a BSD-style setpgrp, which takes arguments, exit
+ * successfully.
+ */
+main()
+{
+ if (setpgrp(1,1) == -1)
+ exit(0);
+ else
+ exit(1);
+}
+
+EOF
+if { (eval echo configure:4052: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+ ac_cv_func_setpgrp_void=no
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -fr conftest*
+ ac_cv_func_setpgrp_void=yes
+fi
+rm -fr conftest*
+fi
+
+
+fi
+
+echo "$ac_t""$ac_cv_func_setpgrp_void" 1>&6
+if test $ac_cv_func_setpgrp_void = yes; then
+ cat >> confdefs.h <<\EOF
+#define SETPGRP_VOID 1
+EOF
+
+fi
+
+for ac_func in putenv strchr memcpy memmove waitpid getlogin strerror random
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+echo "configure:4078: checking for $ac_func" >&5
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 4083 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char $ac_func();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:4106: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+done
+
+for ac_func in lrand48 gethostid getsid getpgid krb_get_err_text krb_log
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+echo "configure:4133: checking for $ac_func" >&5
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 4138 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char $ac_func();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:4161: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+done
+
+
+
+trap '' 1 2 15
+cat > confcache <<\EOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs. It is not useful on other systems.
+# If it contains results you don't want to keep, you may remove or edit it.
+#
+# By default, configure uses ./config.cache as the cache file,
+# creating it if it does not exist already. You can give configure
+# the --cache-file=FILE option to use a different cache file; that is
+# what configure does when it calls configure scripts in
+# subdirectories, so they share the cache.
+# Giving --cache-file=/dev/null disables caching, for debugging configure.
+# config.status only pays attention to the cache file if you give it the
+# --recheck option to rerun configure.
+#
+EOF
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, don't put newlines in cache variables' values.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(set) 2>&1 |
+ case `(ac_space=' '; set | grep ac_space) 2>&1` in
+ *ac_space=\ *)
+ # `set' does not quote correctly, so add quotes (double-quote substitution
+ # turns \\\\ into \\, and sed turns \\ into \).
+ sed -n \
+ -e "s/'/'\\\\''/g" \
+ -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p"
+ ;;
+ *)
+ # `set' quotes correctly as required by POSIX, so do not add quotes.
+ sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p'
+ ;;
+ esac >> confcache
+if cmp -s $cache_file confcache; then
+ :
+else
+ if test -w $cache_file; then
+ echo "updating cache $cache_file"
+ cat confcache > $cache_file
+ else
+ echo "not updating unwritable cache $cache_file"
+ fi
+fi
+rm -f confcache
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# Any assignment to VPATH causes Sun make to only execute
+# the first set of double-colon rules, so remove it if not needed.
+# If there is a colon in the path, we need to keep it.
+if test "x$srcdir" = x.; then
+ ac_vpsub='/^[ ]*VPATH[ ]*=[^:]*$/d'
+fi
+
+trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15
+
+DEFS=-DHAVE_CONFIG_H
+
+# Without the "./", some shells look in PATH for config.status.
+: ${CONFIG_STATUS=./config.status}
+
+echo creating $CONFIG_STATUS
+rm -f $CONFIG_STATUS
+cat > $CONFIG_STATUS <<EOF
+#! /bin/sh
+# Generated automatically by configure.
+# Run this file to recreate the current configuration.
+# This directory was configured as follows,
+# on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+#
+# $0 $ac_configure_args
+#
+# Compiler output produced by configure, useful for debugging
+# configure, is in ./config.log if it exists.
+
+ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]"
+for ac_option
+do
+ case "\$ac_option" in
+ -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+ echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion"
+ exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;;
+ -version | --version | --versio | --versi | --vers | --ver | --ve | --v)
+ echo "$CONFIG_STATUS generated by autoconf version 2.13"
+ exit 0 ;;
+ -help | --help | --hel | --he | --h)
+ echo "\$ac_cs_usage"; exit 0 ;;
+ *) echo "\$ac_cs_usage"; exit 1 ;;
+ esac
+done
+
+ac_given_srcdir=$srcdir
+ac_given_INSTALL="$INSTALL"
+
+trap 'rm -fr `echo "Makefile clients/Makefile clients/xzwrite/Makefile
+ clients/zaway/Makefile clients/zctl/Makefile
+ clients/zleave/Makefile clients/zlocate/Makefile
+ clients/zmailnotify/Makefile clients/znol/Makefile
+ clients/zpopnotify/Makefile clients/zshutdown_notify/Makefile
+ clients/zstat/Makefile clients/zwrite/Makefile lib/Makefile
+ libdyn/Makefile server/Makefile zhm/Makefile zwgc/Makefile h/config.h" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+# Protect against being on the right side of a sed subst in config.status.
+sed 's/%@/@@/; s/@%/@@/; s/%g\$/@g/; /@g\$/s/[\\\\&%]/\\\\&/g;
+ s/@@/%@/; s/@@/@%/; s/@g\$/%g/' > conftest.subs <<\\CEOF
+$ac_vpsub
+$extrasub
+s%@SHELL@%$SHELL%g
+s%@CFLAGS@%$CFLAGS%g
+s%@CPPFLAGS@%$CPPFLAGS%g
+s%@CXXFLAGS@%$CXXFLAGS%g
+s%@FFLAGS@%$FFLAGS%g
+s%@DEFS@%$DEFS%g
+s%@LDFLAGS@%$LDFLAGS%g
+s%@LIBS@%$LIBS%g
+s%@exec_prefix@%$exec_prefix%g
+s%@prefix@%$prefix%g
+s%@program_transform_name@%$program_transform_name%g
+s%@bindir@%$bindir%g
+s%@sbindir@%$sbindir%g
+s%@libexecdir@%$libexecdir%g
+s%@datadir@%$datadir%g
+s%@sysconfdir@%$sysconfdir%g
+s%@sharedstatedir@%$sharedstatedir%g
+s%@localstatedir@%$localstatedir%g
+s%@libdir@%$libdir%g
+s%@includedir@%$includedir%g
+s%@oldincludedir@%$oldincludedir%g
+s%@infodir@%$infodir%g
+s%@mandir@%$mandir%g
+s%@lbindir@%$lbindir%g
+s%@lsbindir@%$lsbindir%g
+s%@host@%$host%g
+s%@host_alias@%$host_alias%g
+s%@host_cpu@%$host_cpu%g
+s%@host_vendor@%$host_vendor%g
+s%@host_os@%$host_os%g
+s%@target@%$target%g
+s%@target_alias@%$target_alias%g
+s%@target_cpu@%$target_cpu%g
+s%@target_vendor@%$target_vendor%g
+s%@target_os@%$target_os%g
+s%@build@%$build%g
+s%@build_alias@%$build_alias%g
+s%@build_cpu@%$build_cpu%g
+s%@build_vendor@%$build_vendor%g
+s%@build_os@%$build_os%g
+s%@CC@%$CC%g
+s%@YACC@%$YACC%g
+s%@LEX@%$LEX%g
+s%@LEXLIB@%$LEXLIB%g
+s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g
+s%@INSTALL_SCRIPT@%$INSTALL_SCRIPT%g
+s%@INSTALL_DATA@%$INSTALL_DATA%g
+s%@RANLIB@%$RANLIB%g
+s%@CPP@%$CPP%g
+s%@X_CFLAGS@%$X_CFLAGS%g
+s%@X_PRE_LIBS@%$X_PRE_LIBS%g
+s%@X_LIBS@%$X_LIBS%g
+s%@X_EXTRA_LIBS@%$X_EXTRA_LIBS%g
+s%@XCLIENTS@%$XCLIENTS%g
+s%@ZWGC_LIBX11@%$ZWGC_LIBX11%g
+s%@TLIB@%$TLIB%g
+s%@RLIB@%$RLIB%g
+s%@SLIB@%$SLIB%g
+s%@KRB4_LIBS@%$KRB4_LIBS%g
+s%@HESIOD_LIBS@%$HESIOD_LIBS%g
+s%@REGEX_LIBS@%$REGEX_LIBS%g
+s%@ARES_LIBS@%$ARES_LIBS%g
+
+CEOF
+EOF
+
+cat >> $CONFIG_STATUS <<\EOF
+
+# Split the substitutions into bite-sized pieces for seds with
+# small command number limits, like on Digital OSF/1 and HP-UX.
+ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script.
+ac_file=1 # Number of current file.
+ac_beg=1 # First line for current file.
+ac_end=$ac_max_sed_cmds # Line after last line for current file.
+ac_more_lines=:
+ac_sed_cmds=""
+while $ac_more_lines; do
+ if test $ac_beg -gt 1; then
+ sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file
+ else
+ sed "${ac_end}q" conftest.subs > conftest.s$ac_file
+ fi
+ if test ! -s conftest.s$ac_file; then
+ ac_more_lines=false
+ rm -f conftest.s$ac_file
+ else
+ if test -z "$ac_sed_cmds"; then
+ ac_sed_cmds="sed -f conftest.s$ac_file"
+ else
+ ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file"
+ fi
+ ac_file=`expr $ac_file + 1`
+ ac_beg=$ac_end
+ ac_end=`expr $ac_end + $ac_max_sed_cmds`
+ fi
+done
+if test -z "$ac_sed_cmds"; then
+ ac_sed_cmds=cat
+fi
+EOF
+
+cat >> $CONFIG_STATUS <<EOF
+
+CONFIG_FILES=\${CONFIG_FILES-"Makefile clients/Makefile clients/xzwrite/Makefile
+ clients/zaway/Makefile clients/zctl/Makefile
+ clients/zleave/Makefile clients/zlocate/Makefile
+ clients/zmailnotify/Makefile clients/znol/Makefile
+ clients/zpopnotify/Makefile clients/zshutdown_notify/Makefile
+ clients/zstat/Makefile clients/zwrite/Makefile lib/Makefile
+ libdyn/Makefile server/Makefile zhm/Makefile zwgc/Makefile"}
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then
+ # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+ case "$ac_file" in
+ *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'`
+ ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+ *) ac_file_in="${ac_file}.in" ;;
+ esac
+
+ # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories.
+
+ # Remove last slash and all that follows it. Not all systems have dirname.
+ ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
+ if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
+ # The file is in a subdirectory.
+ test ! -d "$ac_dir" && mkdir "$ac_dir"
+ ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`"
+ # A "../" for each directory in $ac_dir_suffix.
+ ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'`
+ else
+ ac_dir_suffix= ac_dots=
+ fi
+
+ case "$ac_given_srcdir" in
+ .) srcdir=.
+ if test -z "$ac_dots"; then top_srcdir=.
+ else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;;
+ /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;;
+ *) # Relative path.
+ srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix"
+ top_srcdir="$ac_dots$ac_given_srcdir" ;;
+ esac
+
+ case "$ac_given_INSTALL" in
+ [/$]*) INSTALL="$ac_given_INSTALL" ;;
+ *) INSTALL="$ac_dots$ac_given_INSTALL" ;;
+ esac
+
+ echo creating "$ac_file"
+ rm -f "$ac_file"
+ configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure."
+ case "$ac_file" in
+ *Makefile*) ac_comsub="1i\\
+# $configure_input" ;;
+ *) ac_comsub= ;;
+ esac
+
+ ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"`
+ sed -e "$ac_comsub
+s%@configure_input@%$configure_input%g
+s%@srcdir@%$srcdir%g
+s%@top_srcdir@%$top_srcdir%g
+s%@INSTALL@%$INSTALL%g
+" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file
+fi; done
+rm -f conftest.s*
+
+# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where
+# NAME is the cpp macro being defined and VALUE is the value it is being given.
+#
+# ac_d sets the value in "#define NAME VALUE" lines.
+ac_dA='s%^\([ ]*\)#\([ ]*define[ ][ ]*\)'
+ac_dB='\([ ][ ]*\)[^ ]*%\1#\2'
+ac_dC='\3'
+ac_dD='%g'
+# ac_u turns "#undef NAME" with trailing blanks into "#define NAME VALUE".
+ac_uA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)'
+ac_uB='\([ ]\)%\1#\2define\3'
+ac_uC=' '
+ac_uD='\4%g'
+# ac_e turns "#undef NAME" without trailing blanks into "#define NAME VALUE".
+ac_eA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)'
+ac_eB='$%\1#\2define\3'
+ac_eC=' '
+ac_eD='%g'
+
+if test "${CONFIG_HEADERS+set}" != set; then
+EOF
+cat >> $CONFIG_STATUS <<EOF
+ CONFIG_HEADERS="h/config.h"
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+fi
+for ac_file in .. $CONFIG_HEADERS; do if test "x$ac_file" != x..; then
+ # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+ case "$ac_file" in
+ *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'`
+ ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+ *) ac_file_in="${ac_file}.in" ;;
+ esac
+
+ echo creating $ac_file
+
+ rm -f conftest.frag conftest.in conftest.out
+ ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"`
+ cat $ac_file_inputs > conftest.in
+
+EOF
+
+# Transform confdefs.h into a sed script conftest.vals that substitutes
+# the proper values into config.h.in to produce config.h. And first:
+# Protect against being on the right side of a sed subst in config.status.
+# Protect against being in an unquoted here document in config.status.
+rm -f conftest.vals
+cat > conftest.hdr <<\EOF
+s/[\\&%]/\\&/g
+s%[\\$`]%\\&%g
+s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD}%gp
+s%ac_d%ac_u%gp
+s%ac_u%ac_e%gp
+EOF
+sed -n -f conftest.hdr confdefs.h > conftest.vals
+rm -f conftest.hdr
+
+# This sed command replaces #undef with comments. This is necessary, for
+# example, in the case of _POSIX_SOURCE, which is predefined and required
+# on some systems where configure will not decide to define it.
+cat >> conftest.vals <<\EOF
+s%^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*%/* & */%
+EOF
+
+# Break up conftest.vals because some shells have a limit on
+# the size of here documents, and old seds have small limits too.
+
+rm -f conftest.tail
+while :
+do
+ ac_lines=`grep -c . conftest.vals`
+ # grep -c gives empty output for an empty file on some AIX systems.
+ if test -z "$ac_lines" || test "$ac_lines" -eq 0; then break; fi
+ # Write a limited-size here document to conftest.frag.
+ echo ' cat > conftest.frag <<CEOF' >> $CONFIG_STATUS
+ sed ${ac_max_here_lines}q conftest.vals >> $CONFIG_STATUS
+ echo 'CEOF
+ sed -f conftest.frag conftest.in > conftest.out
+ rm -f conftest.in
+ mv conftest.out conftest.in
+' >> $CONFIG_STATUS
+ sed 1,${ac_max_here_lines}d conftest.vals > conftest.tail
+ rm -f conftest.vals
+ mv conftest.tail conftest.vals
+done
+rm -f conftest.vals
+
+cat >> $CONFIG_STATUS <<\EOF
+ rm -f conftest.frag conftest.h
+ echo "/* $ac_file. Generated automatically by configure. */" > conftest.h
+ cat conftest.in >> conftest.h
+ rm -f conftest.in
+ if cmp -s $ac_file conftest.h 2>/dev/null; then
+ echo "$ac_file is unchanged"
+ rm -f conftest.h
+ else
+ # Remove last slash and all that follows it. Not all systems have dirname.
+ ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
+ if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
+ # The file is in a subdirectory.
+ test ! -d "$ac_dir" && mkdir "$ac_dir"
+ fi
+ rm -f $ac_file
+ mv conftest.h $ac_file
+ fi
+fi; done
+
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+
+exit 0
+EOF
+chmod +x $CONFIG_STATUS
+rm -fr confdefs* $ac_clean_files
+test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1
+
--- /dev/null
+dnl Process this file with autoconf to produce a configure script.
+AC_INIT(server/server.c)
+
+test -d h || mkdir h
+test -d h/zephyr || mkdir h/zephyr
+
+test -z "$lbindir" && lbindir='${bindir}'
+test -z "$lsbindir" && lsbindir='${sbindir}'
+AC_SUBST(lbindir)
+AC_SUBST(lsbindir)
+
+AC_CANONICAL_SYSTEM
+AC_DEFINE_UNQUOTED(MACHINE_TYPE, "$host")
+
+dnl Checks for programs.
+AC_PROG_CC
+AC_PROG_YACC
+AC_PROG_LEX
+AC_PROG_INSTALL
+AC_PROG_RANLIB
+
+AC_MSG_CHECKING(location of temporary directory)
+if test -d /var/tmp; then
+ found_tmp=/var/tmp/
+elif test -d /usr/tmp; then
+ found_tmp=/usr/tmp/
+else
+ found_tmp=/tmp/
+fi
+AC_DEFINE_UNQUOTED(FOUND_TMP, "${found_tmp}")
+AC_MSG_RESULT(${found_tmp})
+
+dnl Checks for header files.
+AC_PATH_XTRA
+AC_HEADER_STDC
+AC_HEADER_SYS_WAIT
+AC_CHECK_HEADERS(fcntl.h paths.h termios.h sgtty.h unistd.h malloc.h)
+AC_CHECK_HEADERS(sys/filio.h sys/ioctl.h sys/time.h sys/file.h sys/utsname.h)
+AC_CHECK_HEADERS(sys/select.h sys/msgbuf.h sys/cdefs.h)
+
+if test "$no_x" != "yes"; then
+ XCLIENTS=xzwrite
+ ZWGC_LIBX11=-lX11
+fi
+AC_SUBST(XCLIENTS)
+AC_SUBST(ZWGC_LIBX11)
+
+dnl Checks for typedefs, structures, and compiler characteristics.
+AC_TYPE_SIGNAL
+AC_TYPE_UID_T
+AC_CHECK_SIZEOF(short)
+AC_CHECK_SIZEOF(int)
+AC_CHECK_SIZEOF(long)
+AC_MSG_CHECKING(for 32-bit integer type)
+if test "$ac_cv_sizeof_long" = 4; then
+ int32=long
+elif test "$ac_cv_sizeof_int" = 4; then
+ int32=int
+elif test "$ac_cv_sizeof_short" = 4; then
+ int32=short
+else
+ AC_WARN([Can't find 32-bit type, using long])
+ int32=long
+fi
+AC_DEFINE_UNQUOTED(ZEPHYR_INT32, ${int32})
+AC_MSG_RESULT(${int32})
+
+AC_CHECK_LIB(w, wslen)
+AC_CHECK_LIB(dl, dlopen)
+
+AC_SEARCH_LIBS(gethostbyname, nsl)
+AC_SEARCH_LIBS(socket, socket)
+
+# Hesiod needs -lresolv on Sun systems for res_send.
+if test "$hesiod" != "no"; then
+ AC_CHECK_LIB(44bsd, strerror)
+ AC_CHECK_LIB(resolv, res_send)
+fi
+
+AC_CHECK_LIB(curses, tgetstr, [TLIB=-lcurses], [TLIB=-ltermcap])
+AC_CHECK_LIB(src, srcsrpy, [SLIB=-lsrc; AC_DEFINE(HAVE_SRC)])
+AC_SUBST(TLIB)
+AC_SUBST(RLIB)
+AC_SUBST(SLIB)
+
+ATHENA_KRB4
+ATHENA_HESIOD
+ATHENA_REGEXP
+ATHENA_ARES
+ATHENA_UTIL_COM_ERR
+ATHENA_UTIL_SS
+LIBS="$KRB4_LIBS $HESIOD_LIBS $LIBS"
+
+dnl Checks for library functions.
+AC_PROG_GCC_TRADITIONAL
+AC_FUNC_VPRINTF
+AC_FUNC_GETPGRP
+AC_FUNC_SETPGRP
+AC_CHECK_FUNCS(putenv strchr memcpy memmove waitpid getlogin strerror random)
+AC_CHECK_FUNCS(lrand48 gethostid getsid getpgid krb_get_err_text krb_log)
+
+AC_CONFIG_HEADER(h/config.h)
+AC_OUTPUT(Makefile clients/Makefile clients/xzwrite/Makefile
+ clients/zaway/Makefile clients/zctl/Makefile
+ clients/zleave/Makefile clients/zlocate/Makefile
+ clients/zmailnotify/Makefile clients/znol/Makefile
+ clients/zpopnotify/Makefile clients/zshutdown_notify/Makefile
+ clients/zstat/Makefile clients/zwrite/Makefile lib/Makefile
+ libdyn/Makefile server/Makefile zhm/Makefile zwgc/Makefile)
--- /dev/null
+#!/bin/sh
+
+pathname=$1
+mode=$2
+OLDIFS="${IFS}"; IFS=/; set $pathname; IFS="${OLDIFS}"
+
+case $pathname in
+ /*) partial=/; ;;
+ *) partial=""; ;;
+esac
+
+for i do
+ case i in "") continue; ;; esac
+ partial="${partial}${i}"
+ if [ ! -d ${partial} ]; then
+ mkdir ${partial} || exit 1;
+ chmod ${mode} ${partial}
+ fi
+ partial="${partial}/"
+done
+
--- /dev/null
+/* h/config.h.in. Generated automatically from configure.in by autoheader. */
+
+/* Define if the `getpgrp' function takes no argument. */
+#undef GETPGRP_VOID
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+#undef gid_t
+
+/* Define if you don't have vprintf but do have _doprnt. */
+#undef HAVE_DOPRNT
+
+/* Define if you have <sys/wait.h> that is POSIX.1 compatible. */
+#undef HAVE_SYS_WAIT_H
+
+/* Define if you have the vprintf function. */
+#undef HAVE_VPRINTF
+
+/* Define as the return type of signal handlers (int or void). */
+#undef RETSIGTYPE
+
+/* Define if the `setpgrp' function takes no argument. */
+#undef SETPGRP_VOID
+
+/* Define if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+#undef uid_t
+
+/* Define if the X Window System is missing or not being used. */
+#undef X_DISPLAY_MISSING
+
+/* Define to compile with Hesiod support. */
+#undef HAVE_HESIOD
+
+/* Define to compile with Kerberos support. */
+#undef HAVE_KRB4
+
+/* Define to compile with ares support. */
+#undef HAVE_ARES
+
+/* Define to a signed 32-bit integral type. */
+#define ZEPHYR_INT32 long
+
+/* Define if you have the System Resource Controller library. */
+#undef HAVE_SRC
+
+/* Define to a temporary directory on your system. */
+#define FOUND_TMP "/var/tmp"
+
+/* Define to the type of the host system. */
+#define MACHINE_TYPE "unknown"
+
+/* The number of bytes in a int. */
+#undef SIZEOF_INT
+
+/* The number of bytes in a long. */
+#undef SIZEOF_LONG
+
+/* The number of bytes in a short. */
+#undef SIZEOF_SHORT
+
+/* Define if you have the gethostid function. */
+#undef HAVE_GETHOSTID
+
+/* Define if you have the getlogin function. */
+#undef HAVE_GETLOGIN
+
+/* Define if you have the getpgid function. */
+#undef HAVE_GETPGID
+
+/* Define if you have the getsid function. */
+#undef HAVE_GETSID
+
+/* Define if you have the krb_get_err_text function. */
+#undef HAVE_KRB_GET_ERR_TEXT
+
+/* Define if you have the krb_log function. */
+#undef HAVE_KRB_LOG
+
+/* Define if you have the lrand48 function. */
+#undef HAVE_LRAND48
+
+/* Define if you have the memcpy function. */
+#undef HAVE_MEMCPY
+
+/* Define if you have the memmove function. */
+#undef HAVE_MEMMOVE
+
+/* Define if you have the putenv function. */
+#undef HAVE_PUTENV
+
+/* Define if you have the random function. */
+#undef HAVE_RANDOM
+
+/* Define if you have the strchr function. */
+#undef HAVE_STRCHR
+
+/* Define if you have the strerror function. */
+#undef HAVE_STRERROR
+
+/* Define if you have the waitpid function. */
+#undef HAVE_WAITPID
+
+/* Define if you have the <fcntl.h> header file. */
+#undef HAVE_FCNTL_H
+
+/* Define if you have the <malloc.h> header file. */
+#undef HAVE_MALLOC_H
+
+/* Define if you have the <paths.h> header file. */
+#undef HAVE_PATHS_H
+
+/* Define if you have the <sgtty.h> header file. */
+#undef HAVE_SGTTY_H
+
+/* Define if you have the <sys/cdefs.h> header file. */
+#undef HAVE_SYS_CDEFS_H
+
+/* Define if you have the <sys/file.h> header file. */
+#undef HAVE_SYS_FILE_H
+
+/* Define if you have the <sys/filio.h> header file. */
+#undef HAVE_SYS_FILIO_H
+
+/* Define if you have the <sys/ioctl.h> header file. */
+#undef HAVE_SYS_IOCTL_H
+
+/* Define if you have the <sys/msgbuf.h> header file. */
+#undef HAVE_SYS_MSGBUF_H
+
+/* Define if you have the <sys/select.h> header file. */
+#undef HAVE_SYS_SELECT_H
+
+/* Define if you have the <sys/time.h> header file. */
+#undef HAVE_SYS_TIME_H
+
+/* Define if you have the <sys/utsname.h> header file. */
+#undef HAVE_SYS_UTSNAME_H
+
+/* Define if you have the <termios.h> header file. */
+#undef HAVE_TERMIOS_H
+
+/* Define if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define if you have the 44bsd library (-l44bsd). */
+#undef HAVE_LIB44BSD
+
+/* Define if you have the dl library (-ldl). */
+#undef HAVE_LIBDL
+
+/* Define if you have the gen library (-lgen). */
+#undef HAVE_LIBGEN
+
+/* Define if you have the nsl library (-lnsl). */
+#undef HAVE_LIBNSL
+
+/* Define if you have the resolv library (-lresolv). */
+#undef HAVE_LIBRESOLV
+
+/* Define if you have the socket library (-lsocket). */
+#undef HAVE_LIBSOCKET
+
+/* Define if you have the w library (-lw). */
+#undef HAVE_LIBW
--- /dev/null
+/*
+ * This file is part of libdyn.a, the C Dynamic Object library. It
+ * contains the public header file.
+ *
+ * There are no restrictions on this code; however, if you make any
+ * changes, I request that you document them so that I do not get
+ * credit or blame for your modifications.
+ *
+ * Written by Barr3y Jaspan, Student Information Processing Board (SIPB)
+ * and MIT-Project Athena, 1989.
+ */
+
+
+/*
+ * dyn.h -- header file to be included by programs linking against
+ * libdyn.a.
+ */
+
+#ifndef _Dyn_h
+#define _Dyn_h
+
+/* Reliance on <sysdep.h> for __P() below makes this unsuitable for use
+ * outside of the Zephyr source tree. */
+#include <sysdep.h>
+
+typedef char *DynPtr;
+typedef struct _DynObject *DynObject;
+
+/* Function macros */
+#define DynHigh(obj) (DynSize(obj) - 1)
+#define DynLow(obj) (0)
+
+#ifdef SUNOS
+#define memmove(a, b, c) bcopy(b, a, c)
+#endif
+
+/* Return status codes */
+#define DYN_OK -1000
+#define DYN_NOMEM -1001
+#define DYN_BADINDEX -1002
+#define DYN_BADVALUE -1003
+
+/* Function declarations */
+int DynAppend __P((DynObject obj, DynPtr els, int num));
+int DynAdd __P((DynObject obj, DynPtr el));
+DynObject DynCreate __P((int el_size, int inc));
+int DynDebug __P((DynObject obj, int state));
+int DynDelete __P((DynObject obj, int idx));
+int DynDestroy __P((DynObject obj));
+DynPtr DynGet __P((DynObject obj, int num));
+int DynInsert __P((DynObject obj, int idx, DynPtr els, int num));
+int DynParanoid __P((DynObject obj, int state));
+int DynSize __P((DynObject obj));
+
+#endif /* _Dyn_h */
+/* DO NOT ADD ANYTHING AFTER THIS #endif */
--- /dev/null
+
+#ifndef __INTERNAL_H__
+#define __INTERNAL_H__
+
+#include <sysdep.h>
+#include <zephyr/zephyr.h>
+#include <netdb.h>
+
+#ifdef HAVE_KRB4
+#include <krb.h>
+#include <krb_err.h>
+#endif
+
+#ifdef HAVE_HESIOD
+#include <hesiod.h>
+#endif
+
+#ifndef HAVE_KRB4
+#define REALM_SZ MAXHOSTNAMELEN
+#define INST_SZ 0 /* no instances w/o Kerberos */
+#define ANAME_SZ 9 /* size of a username + null */
+#define CLOCK_SKEW 300 /* max time to cache packet ids */
+#endif
+
+#define SERVER_SVC_FALLBACK htons((unsigned short) 2103)
+#define HM_SVC_FALLBACK htons((unsigned short) 2104)
+#define HM_SRV_SVC_FALLBACK htons((unsigned short) 2105)
+
+#define ZAUTH_UNSET (-3) /* Internal to client library. */
+#define Z_MAXFRAGS 500 /* Max number of packet fragments */
+#define Z_MAXNOTICESIZE 400000 /* Max size of incoming notice */
+#define Z_MAXQUEUESIZE 1500000 /* Max size of input queue notices */
+#define Z_FRAGFUDGE 13 /* Room to for multinotice field */
+#define Z_NOTICETIMELIMIT 30 /* Time to wait for fragments */
+#define Z_INITFILTERSIZE 30 /* Starting size of uid filter */
+
+struct _Z_Hole {
+ struct _Z_Hole *next;
+ int first;
+ int last;
+};
+
+struct _Z_InputQ {
+ struct _Z_InputQ *next;
+ struct _Z_InputQ *prev;
+ ZNotice_Kind_t kind;
+ unsigned ZEPHYR_INT32 timep;
+ int packet_len;
+ char *packet;
+ int complete;
+ struct sockaddr_in from;
+ struct _Z_Hole *holelist;
+ ZUnique_Id_t uid;
+ int auth;
+ int header_len;
+ char *header;
+ int msg_len;
+ char *msg;
+};
+
+extern struct _Z_InputQ *__Q_Head, *__Q_Tail;
+
+extern int __Zephyr_open; /* 0 if FD opened, 1 otherwise */
+extern int __HM_set; /* 0 if dest addr set, 1 otherwise */
+extern int __Zephyr_server; /* 0 if normal client, 1 if server or zhm */
+
+extern ZLocations_t *__locate_list;
+extern int __locate_num;
+extern int __locate_next;
+
+extern ZSubscription_t *__subscriptions_list;
+extern int __subscriptions_num;
+extern int __subscriptions_next;
+
+extern int __Zephyr_port; /* Port number */
+extern struct in_addr __My_addr;
+
+typedef Code_t (*Z_SendProc) __P((ZNotice_t *, char *, int, int));
+
+struct _Z_InputQ *Z_GetFirstComplete __P((void));
+struct _Z_InputQ *Z_GetNextComplete __P((struct _Z_InputQ *));
+Code_t Z_XmitFragment __P((ZNotice_t*, char *,int,int));
+void Z_RemQueue __P((struct _Z_InputQ *));
+Code_t Z_AddNoticeToEntry __P((struct _Z_InputQ*, ZNotice_t*, int));
+Code_t Z_FormatAuthHeader __P((ZNotice_t *, char *, int, int *, Z_AuthProc));
+Code_t Z_FormatHeader __P((ZNotice_t *, char *, int, int *, Z_AuthProc));
+Code_t Z_FormatRawHeader __P((ZNotice_t *, char*, int,
+ int*, char **, char **));
+Code_t Z_ReadEnqueue __P((void));
+Code_t Z_ReadWait __P((void));
+Code_t Z_SendLocation __P((char*, char*, Z_AuthProc, char*));
+Code_t Z_SendFragmentedNotice __P((ZNotice_t *notice, int len,
+ Z_AuthProc cert_func,
+ Z_SendProc send_func));
+Code_t Z_WaitForComplete __P((void));
+Code_t Z_WaitForNotice __P((ZNotice_t *notice,
+ int (*pred) __P((ZNotice_t *, void *)), void *arg,
+ int timeout));
+
+#endif /* __INTERNAL_H__ */
+
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains system-dependent header code.
+ *
+ * Created by: Greg Hudson
+ *
+ * $Id: sysdep.h,v 1.3 1999/01/22 23:18:57 ghudson Exp $
+ * $Zephyr: /mit/zephyr/src/include/zephyr/RCS/zephyr_conf.h,v 1.8 90/12/21 17:40:40 raeburn Exp $
+ *
+ * Copyright (c) 1988,1991 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#ifndef __SYSDEP_H__
+#define __SYSDEP_H__
+
+#include <config.h>
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <time.h>
+#include <signal.h>
+#include <syslog.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/time.h>
+
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+#else
+# ifdef HAVE_MALLOC_H
+# include <malloc.h>
+# else
+char *malloc(), *realloc();
+# endif
+char *getenv(), *strerror(), *ctime(), *strcpy();
+time_t time();
+ZEPHYR_INT32 random();
+#endif
+
+#ifndef HAVE_RANDOM
+#ifdef HAVE_LRAND48
+#define random lrand48
+#define srandom srand48
+#else
+#define random rand
+#define srandom srand
+#endif
+#endif
+
+#ifndef HAVE_STRERROR
+extern char *sys_errlist[];
+# define strerror(x) (sys_errlist[(x)])
+#endif
+
+/* Strings. */
+#ifdef STDC_HEADERS
+# include <string.h>
+#else
+# ifndef HAVE_STRCHR
+# define strchr index
+# define strrchr rindex
+# endif
+char *strchr(), *strrchr();
+# ifndef HAVE_MEMCPY
+# define memcpy(d, s, n) bcopy ((s), (d), (n))
+# define memcmp bcmp
+# endif
+# ifndef HAVE_MEMMOVE
+# define memmove(d, s, n) bcopy ((s), (d), (n))
+# endif
+#endif
+
+/* Exit status handling and wait(). */
+#ifdef HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+#ifndef WEXITSTATUS
+# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
+#endif
+#ifndef WIFEXITED
+# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
+#endif
+
+#ifdef HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+
+/* Because we have public header files (and our prototypes need to agree with
+ * those header files), use __STDC__ to guess whether the compiler can handle
+ * stdarg, const, and prototypes. */
+#ifdef __STDC__
+# include <stdarg.h>
+# define VA_START(ap, last) va_start(ap, last)
+# ifndef __P
+# define __P(x) x
+# endif
+#else
+# include <varargs.h>
+# define VA_START(ap, last) va_start(ap)
+# define const
+# ifndef __P
+# define __P(x) ()
+# endif
+#endif
+
+/* openlog(). */
+#ifdef LOG_AUTH
+/* A decent syslog */
+#define OPENLOG(str, opts, facility) openlog(str, opts, facility)
+#else
+/* Probably a 4.2-type syslog */
+#define OPENLOG(str, opts, facility) openlog(str, opts)
+#endif
+
+#ifdef HAVE_FCNTL_H
+# include <fcntl.h>
+#endif
+
+#ifdef HAVE_PATHS_H
+# include <paths.h>
+# define TEMP_DIRECTORY _PATH_VARTMP
+#else
+# define TEMP_DIRECTORY FOUND_TMP
+#endif
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#else
+# ifdef HAVE_SYS_FILE_H
+# include <sys/file.h>
+# endif
+uid_t getuid();
+char *ttyname();
+#ifdef HAVE_GETHOSTID
+ZEPHYR_INT32 gethostid();
+#endif
+#endif
+
+#ifndef STDIN_FILENO
+#define STDIN_FILENO 0
+#define STDOUT_FILENO 1
+#define STDERR_FILENO 2
+#endif
+
+#ifdef HAVE_TERMIOS_H
+# include <termios.h>
+#else
+# ifdef HAVE_SYS_FILIO_H
+# include <sys/filio.h>
+# else
+# ifdef HAVE_SGTTY_H
+# include <sgtty.h>
+# endif
+# ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+# endif
+# endif
+#endif
+
+/* Kerberos compatibility. */
+#ifdef HAVE_KRB4
+# include <krb.h>
+# include <krb_err.h>
+# include <des.h>
+# ifndef HAVE_KRB_GET_ERR_TEXT
+# define krb_get_err_text(n) krb_err_txt[n]
+# endif
+# ifndef HAVE_KRB_LOG
+# define krb_log log
+# endif
+#endif
+
+#ifdef HAVE_SYS_UTSNAME_H
+# include <sys/utsname.h>
+#endif
+
+#ifdef HAVE_SYS_SELECT_H
+# include <sys/select.h>
+#endif
+
+#ifdef HAVE_SYS_MSGBUF_H
+#include <sys/msgbuf.h>
+#endif
+
+#ifndef MSG_BSIZE
+#define MSG_BSIZE BUFSIZ
+#endif
+
+#endif /* __SYSDEP_H__ */
+
--- /dev/null
+/*
+
+Copyright 1987,1988,1995 by the Massachusetts Institute of Technology
+
+All rights reserved.
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of the Massachusetts
+Institute of Technology (M.I.T.) not be used in advertising or publicity
+pertaining to distribution of the software without specific, written
+prior permission.
+
+M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+*/
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains global definitions
+ *
+ * Created by: Robert French
+ *
+ * $Id: zephyr.h,v 1.54 1999/01/22 23:18:59 ghudson Exp $
+ *
+ * Copyright (c) 1987,1988,1991 by the Massachusetts Institute of
+ * Technology. For copying and distribution information, see the
+ * file "mit-copyright.h".
+ */
+
+#ifndef __ZEPHYR_H__
+#define __ZEPHYR_H__
+
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <zephyr/zephyr_err.h>
+
+#ifndef IPPROTO_MAX /* Make sure not already included */
+#include <netinet/in.h>
+#endif
+
+/* Use __STDC__ to guess whether we can use stdarg, prototypes, and const.
+ * This is a public header file, so autoconf can't help us here. */
+#ifdef __STDC__
+# include <stdarg.h>
+# define ZP(x) x
+# define ZCONST const
+#else
+# define ZP(x) ()
+# define ZCONST
+#endif
+
+/* Service names */
+#define HM_SVCNAME "zephyr-hm"
+#define HM_SRV_SVCNAME "zephyr-hm-srv"
+#define SERVER_SVCNAME "zephyr-clt"
+#define SERVER_SERVICE "zephyr"
+#define SERVER_INSTANCE "zephyr"
+
+#define ZVERSIONHDR "ZEPH"
+#define ZVERSIONMAJOR 0
+#define ZVERSIONMINOR 2
+
+#define Z_MAXPKTLEN 1024
+#define Z_MAXHEADERLEN 800
+#define Z_MAXOTHERFIELDS 10 /* Max unknown fields in ZNotice_t */
+#define Z_NUMFIELDS 17
+
+/* Authentication levels returned by ZCheckAuthentication */
+#define ZAUTH_FAILED (-1)
+#define ZAUTH_YES 1
+#define ZAUTH_NO 0
+
+typedef char ZPacket_t[Z_MAXPKTLEN];
+
+/* Packet type */
+typedef enum {
+ UNSAFE, UNACKED, ACKED, HMACK, HMCTL, SERVACK, SERVNAK, CLIENTACK, STAT
+} ZNotice_Kind_t;
+extern ZCONST char *ZNoticeKinds[9];
+
+/* Unique ID format */
+typedef struct _ZUnique_Id_t {
+ struct in_addr zuid_addr;
+ struct timeval tv;
+} ZUnique_Id_t;
+
+/* Checksum */
+typedef unsigned long ZChecksum_t;
+
+/* Notice definition */
+typedef struct _ZNotice_t {
+ char *z_packet;
+ char *z_version;
+ ZNotice_Kind_t z_kind;
+ ZUnique_Id_t z_uid;
+#define z_sender_addr z_uid.zuid_addr
+ struct timeval z_time;
+ unsigned short z_port;
+ int z_auth;
+ int z_checked_auth;
+ int z_authent_len;
+ char *z_ascii_authent;
+ char *z_class;
+ char *z_class_inst;
+ char *z_opcode;
+ char *z_sender;
+ char *z_recipient;
+ char *z_default_format;
+ char *z_multinotice;
+ ZUnique_Id_t z_multiuid;
+ ZChecksum_t z_checksum;
+ int z_num_other_fields;
+ char *z_other_fields[Z_MAXOTHERFIELDS];
+ caddr_t z_message;
+ int z_message_len;
+} ZNotice_t;
+
+/* Subscription structure */
+typedef struct _ZSubscriptions_t {
+ char *zsub_recipient;
+ char *zsub_class;
+ char *zsub_classinst;
+} ZSubscription_t;
+
+/* Function return code */
+typedef int Code_t;
+
+/* Locations structure */
+typedef struct _ZLocations_t {
+ char *host;
+ char *time;
+ char *tty;
+} ZLocations_t;
+
+typedef struct _ZAsyncLocateData_t {
+ char *user;
+ ZUnique_Id_t uid;
+ char *version;
+} ZAsyncLocateData_t;
+
+/* for ZSetDebug */
+#ifdef Z_DEBUG
+void (*__Z_debug_print) ZP((ZCONST char *fmt, va_list args, void *closure));
+void *__Z_debug_print_closure;
+#endif
+
+int ZCompareUIDPred ZP((ZNotice_t *, void *));
+int ZCompareMultiUIDPred ZP((ZNotice_t *, void *));
+
+/* Defines for ZFormatNotice, et al. */
+typedef Code_t (*Z_AuthProc) ZP((ZNotice_t*, char *, int, int *));
+Code_t ZMakeAuthentication ZP((ZNotice_t*, char *,int, int*));
+
+char *ZGetSender ZP((void));
+char *ZGetVariable ZP((char *));
+Code_t ZSetVariable ZP((char *var, char *value));
+Code_t ZUnsetVariable ZP((char *var));
+int ZGetWGPort ZP((void));
+Code_t ZSetDestAddr ZP((struct sockaddr_in *));
+Code_t ZFormatNoticeList ZP((ZNotice_t*, char**, int,
+ char **, int*, Z_AuthProc));
+Code_t ZParseNotice ZP((char*, int, ZNotice_t *));
+Code_t ZReadAscii ZP((char*, int, unsigned char*, int));
+Code_t ZReadAscii32 ZP((char *, int, unsigned long *));
+Code_t ZReadAscii16 ZP((char *, int, unsigned short *));
+Code_t ZSendPacket ZP((char*, int, int));
+Code_t ZSendList ZP((ZNotice_t*, char *[], int, Z_AuthProc));
+Code_t ZSrvSendList ZP((ZNotice_t*, char*[], int, Z_AuthProc, Code_t (*)()));
+Code_t ZSendNotice ZP((ZNotice_t *, Z_AuthProc));
+Code_t ZSrvSendNotice ZP((ZNotice_t*, Z_AuthProc, Code_t (*)()));
+Code_t ZFormatNotice ZP((ZNotice_t*, char**, int*, Z_AuthProc));
+Code_t ZFormatSmallNotice ZP((ZNotice_t*, ZPacket_t, int*, Z_AuthProc));
+Code_t ZFormatRawNoticeList ZP((ZNotice_t *notice, char *list[], int nitems,
+ char **buffer, int *ret_len));
+Code_t ZLocateUser ZP((char *, int *, Z_AuthProc));
+Code_t ZRequestLocations ZP((char *, ZAsyncLocateData_t *,
+ ZNotice_Kind_t, Z_AuthProc));
+Code_t ZhmStat ZP((struct in_addr *, ZNotice_t *));
+Code_t ZInitialize ZP((void));
+Code_t ZSetServerState ZP((int));
+Code_t ZSetFD ZP((int));
+Code_t ZFormatSmallRawNotice ZP((ZNotice_t*, ZPacket_t, int*));
+int ZCompareUID ZP((ZUnique_Id_t*, ZUnique_Id_t*));
+Code_t ZSrvSendRawList ZP((ZNotice_t*, char*[], int,
+ Code_t (*)(ZNotice_t *, char *, int, int)));
+Code_t ZMakeAscii ZP((char*, int, unsigned char*, int));
+Code_t ZMakeAscii32 ZP((char *, int, unsigned long));
+Code_t ZMakeAscii16 ZP((char *, int, unsigned int));
+Code_t ZReceivePacket ZP((ZPacket_t, int*, struct sockaddr_in*));
+Code_t ZCheckAuthentication ZP((ZNotice_t*, struct sockaddr_in*));
+Code_t ZInitLocationInfo ZP((char *hostname, char *tty));
+Code_t ZSetLocation ZP((char *exposure));
+Code_t ZUnsetLocation ZP((void));
+Code_t ZFlushMyLocations ZP((void));
+char *ZParseExposureLevel ZP((char *text));
+Code_t ZFormatRawNotice ZP((ZNotice_t *, char**, int *));
+Code_t ZRetrieveSubscriptions ZP((unsigned short, int*));
+Code_t ZOpenPort ZP((unsigned short *port));
+Code_t ZClosePort ZP((void));
+Code_t ZFlushLocations ZP((void));
+Code_t ZFlushSubscriptions ZP((void));
+Code_t ZFreeNotice ZP((ZNotice_t *notice));
+Code_t ZParseLocations ZP((register ZNotice_t *notice,
+ register ZAsyncLocateData_t *zald, int *nlocs,
+ char **user));
+int ZCompareALDPred ZP((ZNotice_t *notice, void *zald));
+void ZFreeALD ZP((register ZAsyncLocateData_t *zald));
+Code_t ZCheckIfNotice ZP((ZNotice_t *notice, struct sockaddr_in *from,
+ register int (*predicate) ZP((ZNotice_t *,void *)),
+ void *args));
+Code_t ZPeekPacket ZP((char **buffer, int *ret_len,
+ struct sockaddr_in *from));
+Code_t ZPeekNotice ZP((ZNotice_t *notice, struct sockaddr_in *from));
+Code_t ZIfNotice ZP((ZNotice_t *notice, struct sockaddr_in *from,
+ int (*predicate) ZP((ZNotice_t *, void *)), void *args));
+Code_t ZSubscribeTo ZP((ZSubscription_t *sublist, int nitems,
+ unsigned int port));
+Code_t ZSubscribeToSansDefaults ZP((ZSubscription_t *sublist, int nitems,
+ unsigned int port));
+Code_t ZUnsubscribeTo ZP((ZSubscription_t *sublist, int nitems,
+ unsigned int port));
+Code_t ZCancelSubscriptions ZP((unsigned int port));
+int ZPending ZP((void));
+Code_t ZReceiveNotice ZP((ZNotice_t *notice, struct sockaddr_in *from));
+#ifdef Z_DEBUG
+void Z_debug ZP((ZCONST char *, ...));
+#endif
+
+#undef ZP
+
+/* Compatibility */
+#define ZNewLocateUser ZLocateUser
+
+/* Macros to retrieve Zephyr library values. */
+extern int __Zephyr_fd;
+extern int __Q_CompleteLength;
+extern struct sockaddr_in __HM_addr;
+extern char __Zephyr_realm[];
+#define ZGetFD() __Zephyr_fd
+#define ZQLength() __Q_CompleteLength
+#define ZGetDestAddr() __HM_addr
+#define ZGetRealm() __Zephyr_realm
+
+
+#ifdef Z_DEBUG
+void ZSetDebug ZP((void (*)(ZCONST char *, va_list, void *), void *));
+#define ZSetDebug(proc,closure) (__Z_debug_print=(proc), \
+ __Z_debug_print_closure=(closure), \
+ (void) 0)
+#else
+#define ZSetDebug(proc,closure)
+#endif
+
+/* Maximum queue length */
+#define Z_MAXQLEN 30
+
+/* Successful function return */
+#define ZERR_NONE 0
+
+/* Hostmanager wait time (in secs) */
+#define HM_TIMEOUT 10
+
+/* Server wait time (in secs) */
+#define SRV_TIMEOUT 30
+
+#define ZAUTH (ZMakeAuthentication)
+#define ZNOAUTH ((Z_AuthProc)0)
+
+/* Packet strings */
+#define ZSRVACK_SENT "SENT" /* SERVACK codes */
+#define ZSRVACK_NOTSENT "LOST"
+#define ZSRVACK_FAIL "FAIL"
+
+/* Server internal class */
+#define ZEPHYR_ADMIN_CLASS "ZEPHYR_ADMIN" /* Class */
+
+/* Control codes sent to a server */
+#define ZEPHYR_CTL_CLASS "ZEPHYR_CTL" /* Class */
+
+#define ZEPHYR_CTL_REALM "REALM" /* Inst: From realm */
+#define REALM_ADD_SUBSCRIBE "ADD_SUBSCRIBE" /* Opcode: Add subs */
+#define REALM_REQ_SUBSCRIBE "REQ_SUBSCRIBE" /* Opcode: Request subs */
+#define REALM_SUBSCRIBE "RLM_SUBSCRIBE" /* Opcode: Subscribe realm */
+#define REALM_UNSUBSCRIBE "RLM_UNSUBSCRIBE" /* Opcode: Unsub realm */
+
+#define ZEPHYR_CTL_CLIENT "CLIENT" /* Inst: From client */
+#define CLIENT_SUBSCRIBE "SUBSCRIBE" /* Opcode: Subscribe */
+#define CLIENT_SUBSCRIBE_NODEFS "SUBSCRIBE_NODEFS" /* Opcode: Subscribe */
+#define CLIENT_UNSUBSCRIBE "UNSUBSCRIBE" /* Opcode: Unsubsubscribe */
+#define CLIENT_CANCELSUB "CLEARSUB" /* Opcode: Clear all subs */
+#define CLIENT_GIMMESUBS "GIMME" /* Opcode: Give me subs */
+#define CLIENT_GIMMEDEFS "GIMMEDEFS" /* Opcode: Give me default
+ * subscriptions */
+
+#define ZEPHYR_CTL_HM "HM" /* Inst: From HM */
+#define HM_BOOT "BOOT" /* Opcode: Boot msg */
+#define HM_FLUSH "FLUSH" /* Opcode: Flush me */
+#define HM_DETACH "DETACH" /* Opcode: Detach me */
+#define HM_ATTACH "ATTACH" /* Opcode: Attach me */
+
+/* Control codes send to a HostManager */
+#define HM_CTL_CLASS "HM_CTL" /* Class */
+
+#define HM_CTL_SERVER "SERVER" /* Inst: From server */
+#define SERVER_SHUTDOWN "SHUTDOWN" /* Opcode: Server shutdown */
+#define SERVER_PING "PING" /* Opcode: PING */
+
+#define HM_CTL_CLIENT "CLIENT" /* Inst: From client */
+#define CLIENT_FLUSH "FLUSH" /* Opcode: Send flush to srv */
+#define CLIENT_NEW_SERVER "NEWSERV" /* Opcode: Find new server */
+
+/* HM Statistics */
+#define HM_STAT_CLASS "HM_STAT" /* Class */
+
+#define HM_STAT_CLIENT "HMST_CLIENT" /* Inst: From client */
+#define HM_GIMMESTATS "GIMMESTATS" /* Opcode: get stats */
+
+/* Login class messages */
+#define LOGIN_CLASS "LOGIN" /* Class */
+
+/* Class Instance is principal of user who is logging in or logging out */
+
+#define EXPOSE_NONE "NONE" /* Opcode: Not visible */
+#define EXPOSE_OPSTAFF "OPSTAFF" /* Opcode: Opstaff visible */
+#define EXPOSE_REALMVIS "REALM-VISIBLE" /* Opcode: Realm visible */
+#define EXPOSE_REALMANN "REALM-ANNOUNCED"/* Opcode: Realm announced */
+#define EXPOSE_NETVIS "NET-VISIBLE" /* Opcode: Net visible */
+#define EXPOSE_NETANN "NET-ANNOUNCED" /* Opcode: Net announced */
+#define LOGIN_USER_LOGIN "USER_LOGIN" /* Opcode: user login
+ (from server) */
+#define LOGIN_USER_LOGOUT "USER_LOGOUT" /* Opcode: User logout */
+#define LOGIN_USER_FLUSH "USER_FLUSH" /* Opcode: flush all locs */
+
+/* Locate class messages */
+#define LOCATE_CLASS "USER_LOCATE" /* Class */
+
+#define LOCATE_HIDE "USER_HIDE" /* Opcode: Hide me */
+#define LOCATE_UNHIDE "USER_UNHIDE" /* Opcode: Unhide me */
+
+/* Class Instance is principal of user to locate */
+#define LOCATE_LOCATE "LOCATE" /* Opcode: Locate user */
+
+/* WG_CTL class messages */
+#define WG_CTL_CLASS "WG_CTL" /* Class */
+
+#define WG_CTL_USER "USER" /* Inst: User request */
+#define USER_REREAD "REREAD" /* Opcode: Reread desc file */
+#define USER_SHUTDOWN "SHUTDOWN" /* Opcode: Go catatonic */
+#define USER_STARTUP "STARTUP" /* Opcode: Come out of it */
+#define USER_EXIT "EXIT" /* Opcode: Exit the client */
+
+#endif /* __ZEPHYR_H__ */
--- /dev/null
+#!/bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5 (mit/util/scripts/install.sh).
+#
+# Copyright 1991 by the Massachusetts Institute of Technology
+#
+# Permission to use, copy, modify, distribute, and sell this software and its
+# documentation for any purpose is hereby granted without fee, provided that
+# the above copyright notice appear in all copies and that both that
+# copyright notice and this permission notice appear in supporting
+# documentation, and that the name of M.I.T. not be used in advertising or
+# publicity pertaining to distribution of the software without specific,
+# written prior permission. M.I.T. makes no representations about the
+# suitability of this software for any purpose. It is provided "as is"
+# without express or implied warranty.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch. It can only install one file at a time, a restriction
+# shared with many OS's install programs.
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+transformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+ case $1 in
+ -c) instcmd="$cpprog"
+ shift
+ continue;;
+
+ -d) dir_arg=true
+ shift
+ continue;;
+
+ -m) chmodcmd="$chmodprog $2"
+ shift
+ shift
+ continue;;
+
+ -o) chowncmd="$chownprog $2"
+ shift
+ shift
+ continue;;
+
+ -g) chgrpcmd="$chgrpprog $2"
+ shift
+ shift
+ continue;;
+
+ -s) stripcmd="$stripprog"
+ shift
+ continue;;
+
+ -t=*) transformarg=`echo $1 | sed 's/-t=//'`
+ shift
+ continue;;
+
+ -b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+ shift
+ continue;;
+
+ *) if [ x"$src" = x ]
+ then
+ src=$1
+ else
+ # this colon is to work around a 386BSD /bin/sh bug
+ :
+ dst=$1
+ fi
+ shift
+ continue;;
+ esac
+done
+
+if [ x"$src" = x ]
+then
+ echo "install: no input file specified"
+ exit 1
+else
+ true
+fi
+
+if [ x"$dir_arg" != x ]; then
+ dst=$src
+ src=""
+
+ if [ -d $dst ]; then
+ instcmd=:
+ chmodcmd=""
+ else
+ instcmd=mkdir
+ fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad
+# if $src (and thus $dsttmp) contains '*'.
+
+ if [ -f $src -o -d $src ]
+ then
+ true
+ else
+ echo "install: $src does not exist"
+ exit 1
+ fi
+
+ if [ x"$dst" = x ]
+ then
+ echo "install: no destination specified"
+ exit 1
+ else
+ true
+ fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+ if [ -d $dst ]
+ then
+ dst="$dst"/`basename $src`
+ else
+ true
+ fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+# this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+ pathcomp="${pathcomp}${1}"
+ shift
+
+ if [ ! -d "${pathcomp}" ] ;
+ then
+ $mkdirprog "${pathcomp}"
+ else
+ true
+ fi
+
+ pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+ $doit $instcmd $dst &&
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+ if [ x"$transformarg" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ dstfile=`basename $dst $transformbasename |
+ sed $transformarg`$transformbasename
+ fi
+
+# don't allow the sed command to completely eliminate the filename
+
+ if [ x"$dstfile" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ true
+ fi
+
+# Make a temp file name in the proper directory.
+
+ dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+ $doit $instcmd $src $dsttmp &&
+
+ trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing. If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+ $doit $rmcmd -f $dstdir/$dstfile &&
+ $doit $mvcmd $dsttmp $dstdir/$dstfile
+
+fi &&
+
+
+exit 0
--- /dev/null
+#!/bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.
+#
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+tranformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+ case $1 in
+ -c) instcmd="$cpprog"
+ shift
+ continue;;
+
+ -d) dir_arg=true
+ shift
+ continue;;
+
+ -m) chmodcmd="$chmodprog $2"
+ shift
+ shift
+ continue;;
+
+ -o) chowncmd="$chownprog $2"
+ shift
+ shift
+ continue;;
+
+ -g) chgrpcmd="$chgrpprog $2"
+ shift
+ shift
+ continue;;
+
+ -s) stripcmd="$stripprog"
+ shift
+ continue;;
+
+ -t=*) transformarg=`echo $1 | sed 's/-t=//'`
+ shift
+ continue;;
+
+ -b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+ shift
+ continue;;
+
+ *) if [ x"$src" = x ]
+ then
+ src=$1
+ else
+ # this colon is to work around a 386BSD /bin/sh bug
+ :
+ dst=$1
+ fi
+ shift
+ continue;;
+ esac
+done
+
+if [ x"$src" = x ]
+then
+ echo "install: no input file specified"
+ exit 1
+else
+ true
+fi
+
+if [ x"$dir_arg" != x ]; then
+ dst=$src
+ src=""
+
+ if [ -d $dst ]; then
+ instcmd=:
+ else
+ instcmd=mkdir
+ fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad
+# if $src (and thus $dsttmp) contains '*'.
+
+ if [ -f $src -o -d $src ]
+ then
+ true
+ else
+ echo "install: $src does not exist"
+ exit 1
+ fi
+
+ if [ x"$dst" = x ]
+ then
+ echo "install: no destination specified"
+ exit 1
+ else
+ true
+ fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+ if [ -d $dst ]
+ then
+ dst="$dst"/`basename $src`
+ else
+ true
+ fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+# this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+ pathcomp="${pathcomp}${1}"
+ shift
+
+ if [ ! -d "${pathcomp}" ] ;
+ then
+ $mkdirprog "${pathcomp}"
+ else
+ true
+ fi
+
+ pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+ $doit $instcmd $dst &&
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+ if [ x"$transformarg" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ dstfile=`basename $dst $transformbasename |
+ sed $transformarg`$transformbasename
+ fi
+
+# don't allow the sed command to completely eliminate the filename
+
+ if [ x"$dstfile" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ true
+ fi
+
+# Make a temp file name in the proper directory.
+
+ dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+ $doit $instcmd $src $dsttmp &&
+
+ trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing. If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+ $doit $rmcmd -f $dstdir/$dstfile &&
+ $doit $mvcmd $dsttmp $dstdir/$dstfile
+
+fi &&
+
+
+exit 0
--- /dev/null
+SHELL = /bin/sh
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+datadir=@datadir@
+sysconfdir=@sysconfdir@
+sbindir=@sbindir@
+lsbindir=@lsbindir@
+
+includedir=${prefix}/include
+mandir=${prefix}/man
+libdir=${exec_prefix}/lib
+
+srcdir=@srcdir@
+top_srcdir=@top_srcdir@
+BUILDTOP=..
+VPATH=@srcdir@
+CC=@CC@
+INSTALL=@INSTALL@
+RANLIB=@RANLIB@
+
+CPPFLAGS=@CPPFLAGS@
+CFLAGS=@CFLAGS@
+ALL_CFLAGS=${CFLAGS} -DSYSCONFDIR=\"${sysconfdir}\" -I${top_srcdir}/h \
+ -I${BUILDTOP}/h ${CPPFLAGS}
+
+OBJS = zephyr_err.o ZAsyncLocate.o ZCkAuth.o ZCkIfNot.o ZClosePort.o \
+ ZCmpUID.o ZCmpUIDP.o ZFlsLocs.o ZFlsSubs.o ZFmtAuth.o ZFmtList.o \
+ ZFmtNotice.o ZFmtRaw.o ZFmtRawLst.o ZFmtSmRLst.o ZFmtSmRaw.o \
+ ZFreeNot.o ZGetLocs.o ZGetSender.o ZGetSubs.o ZGetWGPort.o ZhmStat.o \
+ ZIfNotice.o ZInit.o ZLocations.o ZMakeAscii.o ZMkAuth.o ZNewLocU.o \
+ ZOpenPort.o ZParseNot.o ZPeekIfNot.o ZPeekNot.o ZPeekPkt.o ZPending.o \
+ ZReadAscii.o ZRecvNot.o ZRecvPkt.o ZRetSubs.o ZSendList.o ZSendNot.o \
+ ZSendPkt.o ZSendRaw.o ZSendRLst.o ZSetDest.o ZSetFD.o ZSetSrv.o \
+ ZSubs.o ZVariables.o ZWait4Not.o Zinternal.o
+
+all: libzephyr.a
+
+libzephyr.a: ${OBJS}
+ ar cru $@ ${OBJS}
+ ${RANLIB} $@
+
+zephyr_err.c ${BUILDTOP}/h/zephyr/zephyr_err.h: zephyr_err.et
+ compile_et ${srcdir}/zephyr_err.et
+ mv zephyr_err.h ${BUILDTOP}/h/zephyr
+
+.c.o:
+ ${CC} -c ${ALL_CFLAGS} $<
+
+check:
+
+install: libzephyr.a
+ ${INSTALL} -m 644 libzephyr.a ${DESTDIR}${libdir}
+ ${INSTALL} -m 644 ${srcdir}/zephyr.1 ${DESTDIR}${mandir}/man1
+
+clean:
+ rm -f ${OBJS} libzephyr.a zephyr_err.c zephyr_err.h
+
+${OBJS}: ${top_srcdir}/h/internal.h ${top_srcdir}/h/sysdep.h
+${OBJS}: ${BUILDTOP}/h/config.h ${BUILDTOP}/h/zephyr/zephyr.h
+${OBJS}: ${BUILDTOP}/h/zephyr/zephyr_err.h
+
+.PHONY: all check install clean
+
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains source for asynchronous location functions.
+ *
+ * Created by: Marc Horowitz
+ *
+ * $Id: ZAsyncLocate.c,v 1.6 1999/01/22 23:19:02 ghudson Exp $
+ *
+ * Copyright (c) 1990,1991 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <internal.h>
+
+#ifndef lint
+static const char rcsid_ZAsyncLocate_c[] = "$Id: ZAsyncLocate.c,v 1.6 1999/01/22 23:19:02 ghudson Exp $";
+#endif
+
+Code_t ZRequestLocations(user, zald, kind, auth)
+ char *user;
+ register ZAsyncLocateData_t *zald;
+ ZNotice_Kind_t kind; /* UNSAFE, UNACKED, or ACKED */
+ Z_AuthProc auth;
+{
+ int retval;
+ ZNotice_t notice;
+
+ if (ZGetFD() < 0)
+ if ((retval = ZOpenPort((u_short *)0)) != ZERR_NONE)
+ return (retval);
+
+ (void) memset((char *)¬ice, 0, sizeof(notice));
+ notice.z_kind = kind;
+ notice.z_port = __Zephyr_port;
+ notice.z_class = LOCATE_CLASS;
+ notice.z_class_inst = user;
+ notice.z_opcode = LOCATE_LOCATE;
+ notice.z_sender = 0;
+ notice.z_recipient = "";
+ notice.z_default_format = "";
+ notice.z_message_len = 0;
+
+ if ((retval = ZSendNotice(¬ice, auth)) != ZERR_NONE)
+ return(retval);
+
+ if ((zald->user = (char *) malloc(strlen(user)+1)) == NULL) {
+ return(ENOMEM);
+ }
+ if ((zald->version = (char *) malloc(strlen(notice.z_version)+1)) == NULL) {
+ free(zald->user);
+ return(ENOMEM);
+ }
+ zald->uid = notice.z_multiuid;
+ strcpy(zald->user,user);
+ strcpy(zald->version,notice.z_version);
+
+ return(ZERR_NONE);
+}
+
+Code_t ZParseLocations(notice,zald,nlocs,user)
+ register ZNotice_t *notice;
+ register ZAsyncLocateData_t *zald;
+ int *nlocs;
+ char **user;
+{
+ char *ptr, *end;
+ int i;
+
+ ZFlushLocations(); /* This never fails (this function is part of the
+ library, so it is allowed to know this). */
+
+ /* non-matching protocol version numbers means the
+ server is probably an older version--must punt */
+
+ if (zald && strcmp(notice->z_version, zald->version))
+ return(ZERR_VERS);
+
+ if (notice->z_kind == SERVNAK)
+ return (ZERR_SERVNAK);
+
+ /* flag ACKs as special */
+ if (notice->z_kind == SERVACK &&
+ !strcmp(notice->z_opcode, LOCATE_LOCATE)) {
+ *nlocs = -1;
+ return(ZERR_NONE);
+ }
+
+ if (notice->z_kind != ACKED)
+ return (ZERR_INTERNAL);
+
+ end = notice->z_message+notice->z_message_len;
+
+ __locate_num = 0;
+
+ for (ptr=notice->z_message;ptr<end;ptr++)
+ if (!*ptr)
+ __locate_num++;
+
+ __locate_num /= 3;
+
+ if (__locate_num)
+ {
+ __locate_list = (ZLocations_t *)malloc((unsigned)__locate_num*
+ sizeof(ZLocations_t));
+ if (!__locate_list)
+ return (ENOMEM);
+ } else {
+ __locate_list = 0;
+ }
+
+ for (ptr=notice->z_message, i=0; i<__locate_num; i++) {
+ unsigned int len;
+
+ len = strlen (ptr) + 1;
+ __locate_list[i].host = (char *) malloc(len);
+ if (!__locate_list[i].host)
+ return (ENOMEM);
+ (void) strcpy(__locate_list[i].host, ptr);
+ ptr += len;
+
+ len = strlen (ptr) + 1;
+ __locate_list[i].time = (char *) malloc(len);
+ if (!__locate_list[i].time)
+ return (ENOMEM);
+ (void) strcpy(__locate_list[i].time, ptr);
+ ptr += len;
+
+ len = strlen (ptr) + 1;
+ __locate_list[i].tty = (char *) malloc(len);
+ if (!__locate_list[i].tty)
+ return (ENOMEM);
+ (void) strcpy(__locate_list[i].tty, ptr);
+ ptr += len;
+ }
+
+ __locate_next = 0;
+ *nlocs = __locate_num;
+ if (user) {
+ if (zald) {
+ if ((*user = (char *) malloc(strlen(zald->user)+1)) == NULL)
+ return(ENOMEM);
+ strcpy(*user,zald->user);
+ } else {
+ if ((*user = (char *) malloc(strlen(notice->z_class_inst)+1)) == NULL)
+ return(ENOMEM);
+ strcpy(*user,notice->z_class_inst);
+ }
+ }
+ return (ZERR_NONE);
+}
+
+int ZCompareALDPred(notice, zald)
+ ZNotice_t *notice;
+ void *zald;
+{
+ return(ZCompareUID(&(notice->z_multiuid),
+ &(((ZAsyncLocateData_t *) zald)->uid)));
+}
+
+void ZFreeALD(zald)
+ register ZAsyncLocateData_t *zald;
+{
+ if (!zald) return;
+
+ if (zald->user) free(zald->user);
+ if (zald->version) free(zald->version);
+ (void) memset(zald, 0, sizeof(*zald));
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains source for the ZCheckAuthentication function.
+ *
+ * Created by: Robert French
+ *
+ * $Id: ZCkAuth.c,v 1.23 1999/01/22 23:19:02 ghudson Exp $
+ *
+ * Copyright (c) 1987,1991 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#ifndef lint
+static char rcsid_ZCheckAuthentication_c[] =
+ "$Zephyr: /mit/zephyr/src/lib/RCS/ZCheckAuthentication.c,v 1.14 89/03/24 14:17:38 jtkohl Exp Locker: raeburn $";
+#endif
+
+#include <internal.h>
+
+/* Check authentication of the notice.
+ If it looks authentic but fails the Kerberos check, return -1.
+ If it looks authentic and passes the Kerberos check, return 1.
+ If it doesn't look authentic, return 0
+
+ When not using Kerberos, return true if the notice claims to be authentic.
+ Only used by clients; the server uses its own routine.
+ */
+Code_t ZCheckAuthentication(notice, from)
+ ZNotice_t *notice;
+ struct sockaddr_in *from;
+{
+#ifdef HAVE_KRB4
+ int result;
+ ZChecksum_t our_checksum;
+ CREDENTIALS cred;
+
+ /* If the value is already known, return it. */
+ if (notice->z_checked_auth != ZAUTH_UNSET)
+ return (notice->z_checked_auth);
+
+ if (!notice->z_auth)
+ return (ZAUTH_NO);
+
+ if ((result = krb_get_cred(SERVER_SERVICE, SERVER_INSTANCE,
+ __Zephyr_realm, &cred)) != 0)
+ return (ZAUTH_NO);
+
+#ifdef NOENCRYPTION
+ our_checksum = 0;
+#else
+ our_checksum = des_quad_cksum(notice->z_packet, NULL,
+ notice->z_default_format+
+ strlen(notice->z_default_format)+1-
+ notice->z_packet, 0, cred.session);
+#endif
+ /* if mismatched checksum, then the packet was corrupted */
+ return ((our_checksum == notice->z_checksum) ? ZAUTH_YES : ZAUTH_FAILED);
+
+#else
+ return (notice->z_auth ? ZAUTH_YES : ZAUTH_NO);
+#endif
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains source for the ZCheckIfNotice function.
+ *
+ * Created by: Robert French
+ *
+ * $Id: ZCkIfNot.c,v 1.15 1999/01/22 23:19:03 ghudson Exp $
+ *
+ * Copyright (c) 1987,1988 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <internal.h>
+
+#ifndef lint
+static const char rcsid_ZCheckIfNotice_c[] = "$Id: ZCkIfNot.c,v 1.15 1999/01/22 23:19:03 ghudson Exp $";
+#endif
+
+Code_t ZCheckIfNotice(notice, from, predicate, args)
+ ZNotice_t *notice;
+ struct sockaddr_in *from;
+ register int (*predicate) __P((ZNotice_t *, void *));
+ void *args;
+{
+ ZNotice_t tmpnotice;
+ Code_t retval;
+ register char *buffer;
+ register struct _Z_InputQ *qptr;
+
+ if ((retval = Z_ReadEnqueue()) != ZERR_NONE)
+ return (retval);
+
+ qptr = Z_GetFirstComplete();
+
+ while (qptr) {
+ if ((retval = ZParseNotice(qptr->packet, qptr->packet_len,
+ &tmpnotice)) != ZERR_NONE)
+ return (retval);
+ if ((*predicate)(&tmpnotice, args)) {
+ if (!(buffer = (char *) malloc((unsigned) qptr->packet_len)))
+ return (ENOMEM);
+ (void) memcpy(buffer, qptr->packet, qptr->packet_len);
+ if (from)
+ *from = qptr->from;
+ if ((retval = ZParseNotice(buffer, qptr->packet_len,
+ notice)) != ZERR_NONE) {
+ free(buffer);
+ return (retval);
+ }
+ Z_RemQueue(qptr);
+ return (ZERR_NONE);
+ }
+ qptr = Z_GetNextComplete(qptr);
+ }
+
+ return (ZERR_NONOTICE);
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains source for the ZClosePort function.
+ *
+ * Created by: Robert French
+ *
+ * $Id: ZClosePort.c,v 1.8 1999/01/22 23:19:03 ghudson Exp $
+ *
+ * Copyright (c) 1987 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <internal.h>
+
+#ifndef lint
+static const char rcsid_ZClosePort_c[] = "$Id: ZClosePort.c,v 1.8 1999/01/22 23:19:03 ghudson Exp $";
+#endif
+
+Code_t ZClosePort()
+{
+ if (__Zephyr_fd >= 0 && __Zephyr_open)
+ (void) close(__Zephyr_fd);
+
+ __Zephyr_fd = -1;
+ __Zephyr_open = 0;
+
+ return (ZERR_NONE);
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains source for the ZCompareUID function.
+ *
+ * Created by: Robert French
+ *
+ * $Id: ZCmpUID.c,v 1.10 1999/01/22 23:19:04 ghudson Exp $
+ *
+ * Copyright (c) 1987 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#ifndef lint
+static char rcsid_ZCompareUID_c[] = "$Id: ZCmpUID.c,v 1.10 1999/01/22 23:19:04 ghudson Exp $";
+#endif
+
+#include <internal.h>
+
+int ZCompareUID(uid1, uid2)
+ ZUnique_Id_t *uid1, *uid2;
+{
+ return (!memcmp((char *)uid1, (char *)uid2, sizeof (*uid1)));
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains source for the ZCompareUIDPred function.
+ *
+ * Created by: Robert French
+ *
+ * $Id: ZCmpUIDP.c,v 1.7 1999/01/22 23:19:04 ghudson Exp $
+ *
+ * Copyright (c) 1987 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#ifndef lint
+static char rcsid_ZCompareUIDPred_c[] = "$Id: ZCmpUIDP.c,v 1.7 1999/01/22 23:19:04 ghudson Exp $";
+#endif
+
+#include <internal.h>
+
+int ZCompareUIDPred(notice, uid)
+ ZNotice_t *notice;
+ void *uid;
+{
+ return (ZCompareUID(¬ice->z_uid, (ZUnique_Id_t *) uid));
+}
+
+int ZCompareMultiUIDPred(notice, uid)
+ ZNotice_t *notice;
+ void *uid;
+{
+ return (ZCompareUID(¬ice->z_multiuid, (ZUnique_Id_t *) uid));
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains source for the ZFlushLocations function.
+ *
+ * Created by: Robert French
+ *
+ * $Id: ZFlsLocs.c,v 1.8 1999/01/22 23:19:05 ghudson Exp $
+ *
+ * Copyright (c) 1987 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#ifndef lint
+static char rcsid_ZFlushLocations_c[] = "$Id: ZFlsLocs.c,v 1.8 1999/01/22 23:19:05 ghudson Exp $";
+#endif
+
+#include <internal.h>
+
+Code_t ZFlushLocations()
+{
+ int i;
+
+ if (!__locate_list)
+ return (ZERR_NONE);
+
+ for (i=0;i<__locate_num;i++) {
+ free(__locate_list[i].host);
+ free(__locate_list[i].time);
+ free(__locate_list[i].tty);
+ }
+
+ free((char *)__locate_list);
+
+ __locate_list = 0;
+ __locate_num = 0;
+
+ return (ZERR_NONE);
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains source for the ZFlushSubscriptions function.
+ *
+ * Created by: Robert French
+ *
+ * $Id: ZFlsSubs.c,v 1.5 1999/01/22 23:19:06 ghudson Exp $
+ *
+ * Copyright (c) 1987 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <internal.h>
+
+#ifndef lint
+static const char rcsid_ZFlushSubscriptions_c[] = "$Id: ZFlsSubs.c,v 1.5 1999/01/22 23:19:06 ghudson Exp $";
+#endif
+
+Code_t ZFlushSubscriptions()
+{
+ register int i;
+
+ if (!__subscriptions_list)
+ return (ZERR_NONE);
+
+ for (i=0;i<__subscriptions_num;i++) {
+ free(__subscriptions_list[i].zsub_class);
+ free(__subscriptions_list[i].zsub_classinst);
+ free(__subscriptions_list[i].zsub_recipient);
+ }
+
+ free((char *)__subscriptions_list);
+
+ __subscriptions_list = 0;
+ __subscriptions_num = 0;
+
+ return (ZERR_NONE);
+}
+
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains source for the ZFormatAuthenticNotice function.
+ *
+ * Created by: Robert French
+ *
+ * $Id: ZFmtAuth.c,v 1.20 1999/01/22 23:19:06 ghudson Exp $
+ *
+ * Copyright (c) 1987,1988 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#ifndef lint
+static char rcsid_ZFormatAuthenticNotice_c[] = "$Id: ZFmtAuth.c,v 1.20 1999/01/22 23:19:06 ghudson Exp $";
+#endif
+
+#include <internal.h>
+
+#ifdef HAVE_KRB4
+Code_t ZFormatAuthenticNotice(notice, buffer, buffer_len, len, session)
+ ZNotice_t *notice;
+ register char *buffer;
+ register int buffer_len;
+ int *len;
+ C_Block session;
+{
+ ZNotice_t newnotice;
+ char *ptr;
+ int retval, hdrlen;
+
+ newnotice = *notice;
+ newnotice.z_auth = 1;
+ newnotice.z_authent_len = 0;
+ newnotice.z_ascii_authent = "";
+
+ if ((retval = Z_FormatRawHeader(&newnotice, buffer, buffer_len,
+ &hdrlen, &ptr, NULL)) != ZERR_NONE)
+ return (retval);
+
+#ifdef NOENCRYPTION
+ newnotice.z_checksum = 0;
+#else
+ newnotice.z_checksum =
+ (ZChecksum_t)des_quad_cksum(buffer, NULL, ptr - buffer, 0, session);
+#endif
+ if ((retval = Z_FormatRawHeader(&newnotice, buffer, buffer_len,
+ &hdrlen, NULL, NULL)) != ZERR_NONE)
+ return (retval);
+
+ ptr = buffer+hdrlen;
+
+ if (newnotice.z_message_len+hdrlen > buffer_len)
+ return (ZERR_PKTLEN);
+
+ (void) memcpy(ptr, newnotice.z_message, newnotice.z_message_len);
+
+ *len = hdrlen+newnotice.z_message_len;
+
+ if (*len > Z_MAXPKTLEN)
+ return (ZERR_PKTLEN);
+
+ return (ZERR_NONE);
+}
+#endif
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains source for the ZFormatNoticeList function.
+ *
+ * Created by: Robert French
+ *
+ * $Id: ZFmtList.c,v 1.16 1999/01/22 23:19:07 ghudson Exp $
+ *
+ * Copyright (c) 1987,1991 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <internal.h>
+
+#ifndef lint
+static const char rcsid_ZFormatNoticeList_c[] =
+ "$Id: ZFmtList.c,v 1.16 1999/01/22 23:19:07 ghudson Exp $";
+#endif
+
+Code_t ZFormatNoticeList(notice, list, nitems, buffer, ret_len,
+ cert_routine)
+ ZNotice_t *notice;
+ register char **list;
+ int nitems;
+ char **buffer;
+ int *ret_len;
+ Z_AuthProc cert_routine;
+{
+ char header[Z_MAXHEADERLEN];
+ register int i;
+ int hdrlen, size;
+ char *ptr;
+ Code_t retval;
+
+ if ((retval = Z_FormatHeader(notice, header, sizeof(header), &hdrlen,
+ cert_routine)) != ZERR_NONE)
+ return (retval);
+
+ size = 0;
+ for (i=0;i<nitems;i++)
+ size += strlen(list[i])+1;
+
+ *ret_len = hdrlen+size;
+
+ /* *ret_len can never be zero here, no need to worry about malloc(0). */
+ if (!(*buffer = (char *) malloc((unsigned)*ret_len)))
+ return (ENOMEM);
+
+ (void) memcpy(*buffer, header, hdrlen);
+
+ ptr = *buffer+hdrlen;
+
+ for (;nitems;nitems--, list++) {
+ i = strlen(*list)+1;
+ (void) memcpy(ptr, *list, i);
+ ptr += i;
+ }
+
+ return (ZERR_NONE);
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains source for the ZFormatNotice function.
+ *
+ * Created by: Robert French
+ *
+ * $Id: ZFmtNotice.c,v 1.17 1999/01/22 23:19:07 ghudson Exp $
+ *
+ * Copyright (c) 1987,1988 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#ifndef lint
+static char rcsid_ZFormatNotice_c[] = "$Id: ZFmtNotice.c,v 1.17 1999/01/22 23:19:07 ghudson Exp $";
+#endif
+
+#include <internal.h>
+
+Code_t ZFormatNotice(notice, buffer, ret_len, cert_routine)
+ register ZNotice_t *notice;
+ char **buffer;
+ int *ret_len;
+ Z_AuthProc cert_routine;
+{
+ char header[Z_MAXHEADERLEN];
+ int hdrlen;
+ Code_t retval;
+
+ if ((retval = Z_FormatHeader(notice, header, sizeof(header), &hdrlen,
+ cert_routine)) != ZERR_NONE)
+ return (retval);
+
+ *ret_len = hdrlen+notice->z_message_len;
+
+ /* Length can never be zero, don't have to worry about malloc(0). */
+ if (!(*buffer = (char *) malloc((unsigned)*ret_len)))
+ return (ENOMEM);
+
+ (void) memcpy(*buffer, header, hdrlen);
+ (void) memcpy(*buffer+hdrlen, notice->z_message, notice->z_message_len);
+
+ return (ZERR_NONE);
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains source for the ZFormatRawNotice function.
+ *
+ * Created by: Robert French
+ *
+ * $Id: ZFmtRaw.c,v 1.13 1999/01/22 23:19:08 ghudson Exp $
+ *
+ * Copyright (c) 1987 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#ifndef lint
+static char rcsid_ZFormatRawNotice_c[] = "$Id: ZFmtRaw.c,v 1.13 1999/01/22 23:19:08 ghudson Exp $";
+#endif
+
+#include <internal.h>
+
+Code_t ZFormatRawNotice(notice, buffer, ret_len)
+ register ZNotice_t *notice;
+ char **buffer;
+ int *ret_len;
+{
+ char header[Z_MAXHEADERLEN];
+ int hdrlen;
+ Code_t retval;
+
+ if ((retval = Z_FormatRawHeader(notice, header, sizeof(header),
+ &hdrlen, NULL, NULL)) != ZERR_NONE)
+ return (retval);
+
+ *ret_len = hdrlen+notice->z_message_len;
+
+ /* *ret_len is never 0, don't have to worry about malloc(0) */
+ if (!(*buffer = (char *) malloc((unsigned) *ret_len)))
+ return (ENOMEM);
+
+ (void) memcpy(*buffer, header, hdrlen);
+ (void) memcpy(*buffer+hdrlen, notice->z_message, notice->z_message_len);
+
+ return (ZERR_NONE);
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains source for the ZFormatRawNoticeList function.
+ *
+ * Created by: Robert French
+ *
+ * $Id: ZFmtRawLst.c,v 1.12 1999/01/22 23:19:09 ghudson Exp $
+ *
+ * Copyright (c) 1987 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#ifndef lint
+static char rcsid_ZFormatRawNoticeList_c[] = "$Id: ZFmtRawLst.c,v 1.12 1999/01/22 23:19:09 ghudson Exp $";
+#endif
+
+#include <internal.h>
+
+Code_t ZFormatRawNoticeList(notice, list, nitems, buffer, ret_len)
+ ZNotice_t *notice;
+ char *list[];
+ int nitems;
+ char **buffer;
+ int *ret_len;
+{
+ char header[Z_MAXHEADERLEN];
+ int hdrlen, i, size;
+ char *ptr;
+ Code_t retval;
+
+ if ((retval = Z_FormatRawHeader(notice, header, sizeof(header),
+ &hdrlen, NULL, NULL)) != ZERR_NONE)
+ return (retval);
+
+ size = 0;
+ for (i=0;i<nitems;i++)
+ size += strlen(list[i])+1;
+
+ *ret_len = hdrlen+size;
+
+ if (!(*buffer = (char *) malloc((unsigned) *ret_len)))
+ return (ENOMEM);
+
+ (void) memcpy(*buffer, header, hdrlen);
+
+ ptr = *buffer+hdrlen;
+
+ for (;nitems;nitems--, list++) {
+ i = strlen(*list)+1;
+ (void) memcpy(ptr, *list, i);
+ ptr += i;
+ }
+
+ return (ZERR_NONE);
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains source for the ZFormatSmallRawNoticeList function.
+ *
+ * Created by: John T. Kohl
+ *
+ * $Id: ZFmtSmRLst.c,v 1.9 1999/01/22 23:19:09 ghudson Exp $
+ *
+ * Copyright (c) 1988 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#ifndef lint
+static char rcsid_ZFormatRawNoticeList_c[] = "$Id: ZFmtSmRLst.c,v 1.9 1999/01/22 23:19:09 ghudson Exp $";
+#endif
+
+#include <internal.h>
+
+Code_t ZFormatSmallRawNoticeList(notice, list, nitems, buffer, ret_len)
+ ZNotice_t *notice;
+ char *list[];
+ int nitems;
+ ZPacket_t buffer;
+ int *ret_len;
+{
+ Code_t retval;
+ int hdrlen, i, size;
+ char *ptr;
+
+ if ((retval = Z_FormatRawHeader(notice, buffer, Z_MAXHEADERLEN,
+ &hdrlen, NULL, NULL)) != ZERR_NONE)
+ return (retval);
+
+ size = 0;
+ for (i=0;i<nitems;i++)
+ size += strlen(list[i])+1;
+
+ *ret_len = hdrlen+size;
+
+ if (*ret_len > Z_MAXPKTLEN)
+ return (ZERR_PKTLEN);
+
+ ptr = buffer+hdrlen;
+
+ for (;nitems;nitems--, list++) {
+ i = strlen(*list)+1;
+ (void) memcpy(ptr, *list, i);
+ ptr += i;
+ }
+
+ return (ZERR_NONE);
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains source for the ZFormatSmallRawNotice function.
+ *
+ * Created by: Robert French
+ *
+ * $Id: ZFmtSmRaw.c,v 1.8 1999/01/22 23:19:10 ghudson Exp $
+ *
+ * Copyright (c) 1987 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#ifndef lint
+static char rcsid_ZFormatRawNotice_c[] = "$Id: ZFmtSmRaw.c,v 1.8 1999/01/22 23:19:10 ghudson Exp $";
+#endif
+
+#include <internal.h>
+
+Code_t ZFormatSmallRawNotice(notice, buffer, ret_len)
+ ZNotice_t *notice;
+ ZPacket_t buffer;
+ int *ret_len;
+{
+ Code_t retval;
+ int hdrlen;
+
+ if ((retval = Z_FormatRawHeader(notice, buffer, Z_MAXHEADERLEN,
+ &hdrlen, NULL, NULL)) != ZERR_NONE)
+ return (retval);
+
+ *ret_len = hdrlen+notice->z_message_len;
+
+ if (*ret_len > Z_MAXPKTLEN)
+ return (ZERR_PKTLEN);
+
+ (void) memcpy(buffer+hdrlen, notice->z_message, notice->z_message_len);
+
+ return (ZERR_NONE);
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains source for the ZFreeNotice function.
+ *
+ * Created by: Robert French
+ *
+ * $Id: ZFreeNot.c,v 1.6 1999/01/22 23:19:10 ghudson Exp $
+ *
+ * Copyright (c) 1987 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#ifndef lint
+static char rcsid_ZFreeNotice_c[] = "$Id: ZFreeNot.c,v 1.6 1999/01/22 23:19:10 ghudson Exp $";
+#endif
+
+#include <internal.h>
+
+Code_t ZFreeNotice(notice)
+ ZNotice_t *notice;
+{
+ free(notice->z_packet);
+ return 0;
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains source for the ZGetLocations function.
+ *
+ * Created by: Robert French
+ *
+ * $Id: ZGetLocs.c,v 1.7 1999/01/22 23:19:11 ghudson Exp $
+ *
+ * Copyright (c) 1987 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#ifndef lint
+static char rcsid_ZGetLocations_c[] = "$Id: ZGetLocs.c,v 1.7 1999/01/22 23:19:11 ghudson Exp $";
+#endif
+
+#include <internal.h>
+
+#define min(a,b) ((a)<(b)?(a):(b))
+
+Code_t ZGetLocations(location, numlocs)
+ ZLocations_t *location;
+ int *numlocs;
+{
+ int i;
+
+ if (!__locate_list)
+ return (ZERR_NOLOCATIONS);
+
+ if (__locate_next == __locate_num)
+ return (ZERR_NOMORELOCS);
+
+ for (i=0;i<min(*numlocs, __locate_num-__locate_next);i++)
+ location[i] = __locate_list[i+__locate_next];
+
+ if (__locate_num-__locate_next < *numlocs)
+ *numlocs = __locate_num-__locate_next;
+
+ __locate_next += *numlocs;
+
+ return (ZERR_NONE);
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains source for the ZGetSender.c function.
+ *
+ * Created by: Robert French
+ *
+ * $Id: ZGetSender.c,v 1.14 1999/01/22 23:19:11 ghudson Exp $
+ *
+ * Copyright (c) 1987, 1991 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <internal.h>
+
+#ifndef lint
+static const char rcsid_ZGetSender_c[] =
+ "$Id: ZGetSender.c,v 1.14 1999/01/22 23:19:11 ghudson Exp $";
+#endif
+
+#include <pwd.h>
+
+char *ZGetSender()
+{
+ struct passwd *pw;
+#ifdef HAVE_KRB4
+ char pname[ANAME_SZ], pinst[INST_SZ], prealm[REALM_SZ];
+ static char sender[ANAME_SZ+INST_SZ+REALM_SZ+3] = "";
+#else
+ static char sender[128] = "";
+#endif
+
+ /* Return it if already cached */
+ if (*sender)
+ return (sender);
+
+#ifdef HAVE_KRB4
+ if (krb_get_tf_fullname((char *)TKT_FILE, pname, pinst, prealm) == KSUCCESS)
+ {
+ (void) sprintf(sender, "%s%s%s@%s", pname, (pinst[0]?".":""),
+ pinst, prealm);
+ return (sender);
+ }
+#endif
+
+ /* XXX a uid_t is a u_short (now), but getpwuid
+ * wants an int. AARGH! */
+ pw = getpwuid((int) getuid());
+ if (!pw)
+ return ("unknown");
+ (void) sprintf(sender, "%s@%s", pw->pw_name, __Zephyr_realm);
+ return (sender);
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains source for the ZGetSubscriptions function.
+ *
+ * Created by: Robert French
+ *
+ * $Id: ZGetSubs.c,v 1.6 1999/01/22 23:19:12 ghudson Exp $
+ *
+ * Copyright (c) 1987 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#ifndef lint
+static char rcsid_ZGetSubscriptions_c[] = "$Id: ZGetSubs.c,v 1.6 1999/01/22 23:19:12 ghudson Exp $";
+#endif
+
+#include <internal.h>
+
+#define min(a,b) ((a)<(b)?(a):(b))
+
+Code_t ZGetSubscriptions(subscription, numsubs)
+ ZSubscription_t *subscription;
+ int *numsubs;
+{
+ int i;
+
+ if (!__subscriptions_list)
+ return (ZERR_NOSUBSCRIPTIONS);
+
+ if (__subscriptions_next == __subscriptions_num)
+ return (ZERR_NOMORESUBSCRIPTIONS);
+
+ for (i=0;i<min(*numsubs, __subscriptions_num-__subscriptions_next);i++)
+ subscription[i] = __subscriptions_list[i+__subscriptions_next];
+
+ if (__subscriptions_num-__subscriptions_next < *numsubs)
+ *numsubs = __subscriptions_num-__subscriptions_next;
+
+ __subscriptions_next += *numsubs;
+
+ return (ZERR_NONE);
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains source for the ZGetWGPort function.
+ *
+ * Created by: Robert French
+ *
+ * $Id: ZGetWGPort.c,v 1.11 1999/01/22 23:19:12 ghudson Exp $
+ *
+ * Copyright (c) 1987 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#ifndef lint
+static char rcsid_ZGetWGPort_c[] = "$Id: ZGetWGPort.c,v 1.11 1999/01/22 23:19:12 ghudson Exp $";
+#endif
+
+#include <internal.h>
+
+int ZGetWGPort()
+{
+ char *envptr, name[128];
+ FILE *fp;
+ int wgport;
+
+ envptr = getenv("WGFILE");
+ if (!envptr) {
+ (void) sprintf(name, "/tmp/wg.%d", getuid());
+ envptr = name;
+ }
+ if (!(fp = fopen(envptr, "r")))
+ return (-1);
+
+ /* if fscanf fails, return -1 via wgport */
+ if (fscanf(fp, "%d", &wgport) != 1)
+ wgport = -1;
+
+ (void) fclose(fp);
+
+ return (wgport);
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains source for the ZIfNotice function.
+ *
+ * Created by: Robert French
+ *
+ * $Id: ZIfNotice.c,v 1.15 1999/01/22 23:19:13 ghudson Exp $
+ *
+ * Copyright (c) 1987,1988 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <internal.h>
+
+#ifndef lint
+static const char rcsid_ZIfNotice_c[] = "$Id: ZIfNotice.c,v 1.15 1999/01/22 23:19:13 ghudson Exp $";
+#endif
+
+Code_t ZIfNotice(notice, from, predicate, args)
+ ZNotice_t *notice;
+ struct sockaddr_in *from;
+ int (*predicate) __P((ZNotice_t *, void *));
+ void *args;
+{
+ ZNotice_t tmpnotice;
+ Code_t retval;
+ char *buffer;
+ struct _Z_InputQ *qptr;
+
+ if ((retval = Z_WaitForComplete()) != ZERR_NONE)
+ return (retval);
+
+ qptr = Z_GetFirstComplete();
+
+ for (;;) {
+ while (qptr) {
+ if ((retval = ZParseNotice(qptr->packet, qptr->packet_len,
+ &tmpnotice)) != ZERR_NONE)
+ return (retval);
+ if ((*predicate)(&tmpnotice, args)) {
+ if (!(buffer = (char *) malloc((unsigned) qptr->packet_len)))
+ return (ENOMEM);
+ (void) memcpy(buffer, qptr->packet, qptr->packet_len);
+ if (from)
+ *from = qptr->from;
+ if ((retval = ZParseNotice(buffer, qptr->packet_len,
+ notice)) != ZERR_NONE) {
+ free(buffer);
+ return (retval);
+ }
+ Z_RemQueue(qptr);
+ return (ZERR_NONE);
+ }
+ qptr = Z_GetNextComplete(qptr);
+ }
+ if ((retval = Z_ReadWait()) != ZERR_NONE)
+ return (retval);
+ qptr = Z_GetFirstComplete(); /* need to look over all of
+ the queued messages, in case
+ a fragment has been reassembled */
+ }
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains source for the ZInitialize function.
+ *
+ * Created by: Robert French
+ *
+ * $Id: ZInit.c,v 1.27 1999/01/22 23:19:14 ghudson Exp $
+ *
+ * Copyright (c) 1987, 1991 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#ifndef lint
+static char rcsid_ZInitialize_c[] =
+ "$Zephyr: /afs/athena.mit.edu/astaff/project/zephyr/src/lib/RCS/ZInitialize.c,v 1.17 89/05/30 18:11:25 jtkohl Exp $";
+#endif
+
+#include <internal.h>
+
+#include <sys/socket.h>
+#ifdef HAVE_KRB4
+#include <krb_err.h>
+#endif
+
+#ifndef INADDR_NONE
+#define INADDR_NONE 0xffffffff
+#endif
+
+Code_t ZInitialize()
+{
+ struct servent *hmserv;
+ struct hostent *hostent;
+ char addr[4], hostname[MAXHOSTNAMELEN];
+ struct in_addr servaddr;
+ struct sockaddr_in sin;
+ int s, sinsize = sizeof(sin);
+ Code_t code;
+ ZNotice_t notice;
+#ifdef HAVE_KRB4
+ char *krealm = NULL;
+ int krbval;
+ char d1[ANAME_SZ], d2[INST_SZ];
+
+ initialize_krb_error_table();
+#endif
+
+ initialize_zeph_error_table();
+
+ (void) memset((char *)&__HM_addr, 0, sizeof(__HM_addr));
+
+ __HM_addr.sin_family = AF_INET;
+
+ /* Set up local loopback address for HostManager */
+ addr[0] = 127;
+ addr[1] = 0;
+ addr[2] = 0;
+ addr[3] = 1;
+
+ hmserv = (struct servent *)getservbyname(HM_SVCNAME, "udp");
+ __HM_addr.sin_port = (hmserv) ? hmserv->s_port : HM_SVC_FALLBACK;
+
+ (void) memcpy((char *)&__HM_addr.sin_addr, addr, 4);
+
+ __HM_set = 0;
+
+ /* Initialize the input queue */
+ __Q_Tail = NULL;
+ __Q_Head = NULL;
+
+ /* if the application is a server, there might not be a zhm. The
+ code will fall back to something which might not be "right",
+ but this is is ok, since none of the servers call krb_rd_req. */
+
+ servaddr.s_addr = INADDR_NONE;
+ if (! __Zephyr_server) {
+ if ((code = ZOpenPort(NULL)) != ZERR_NONE)
+ return(code);
+
+ if ((code = ZhmStat(NULL, ¬ice)) != ZERR_NONE)
+ return(code);
+
+ ZClosePort();
+
+ /* the first field, which is NUL-terminated, is the server name.
+ If this code ever support a multiplexing zhm, this will have to
+ be made smarter, and probably per-message */
+
+#ifdef HAVE_KRB4
+ krealm = krb_realmofhost(notice.z_message);
+#endif
+ hostent = gethostbyname(notice.z_message);
+ if (hostent && hostent->h_addrtype == AF_INET)
+ memcpy(&servaddr, hostent->h_addr, sizeof(servaddr));
+
+ ZFreeNotice(¬ice);
+ }
+
+#ifdef HAVE_KRB4
+ if (krealm) {
+ strcpy(__Zephyr_realm, krealm);
+ } else if ((krb_get_tf_fullname(TKT_FILE, d1, d2, __Zephyr_realm)
+ != KSUCCESS) &&
+ ((krbval = krb_get_lrealm(__Zephyr_realm, 1)) != KSUCCESS)) {
+ return (krbval);
+ }
+#else
+ strcpy(__Zephyr_realm, "local-realm");
+#endif
+
+ __My_addr.s_addr = INADDR_NONE;
+ if (servaddr.s_addr != INADDR_NONE) {
+ /* Try to get the local interface address by connecting a UDP
+ * socket to the server address and getting the local address.
+ * Some broken operating systems (e.g. Solaris 2.0-2.5) yield
+ * INADDR_ANY (zero), so we have to check for that. */
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s != -1) {
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ memcpy(&sin.sin_addr, &servaddr, sizeof(servaddr));
+ sin.sin_port = HM_SRV_SVC_FALLBACK;
+ if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) == 0
+ && getsockname(s, (struct sockaddr *) &sin, &sinsize) == 0
+ && sin.sin_addr.s_addr != 0)
+ memcpy(&__My_addr, &sin.sin_addr, sizeof(__My_addr));
+ close(s);
+ }
+ }
+ if (__My_addr.s_addr == INADDR_NONE) {
+ /* We couldn't figure out the local interface address by the
+ * above method. Try by resolving the local hostname. (This
+ * is a pretty broken thing to do, and unfortunately what we
+ * always do on server machines.) */
+ if (gethostname(hostname, sizeof(hostname)) == 0) {
+ hostent = gethostbyname(hostname);
+ if (hostent && hostent->h_addrtype == AF_INET)
+ memcpy(&__My_addr, hostent->h_addr, sizeof(__My_addr));
+ }
+ }
+ /* If the above methods failed, zero out __My_addr so things will
+ * sort of kind of work. */
+ if (__My_addr.s_addr == INADDR_NONE)
+ __My_addr.s_addr = 0;
+
+ /* Get the sender so we can cache it */
+ (void) ZGetSender();
+
+ return (ZERR_NONE);
+}
+
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains source for the ZLocateUser function.
+ *
+ * Created by: Robert French
+ *
+ * $Id: ZLocateU.c,v 1.24 1999/01/22 23:19:14 ghudson Exp $
+ *
+ * Copyright (c) 1987,1988 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#ifndef lint
+static char rcsid_ZLocateUser_c[] = "$Id: ZLocateU.c,v 1.24 1999/01/22 23:19:14 ghudson Exp $";
+#endif
+
+#include <internal.h>
+
+Code_t ZLocateUser(user, nlocs)
+ char *user;
+ int *nlocs;
+{
+ return(ZNewLocateUser(user,nlocs,ZAUTH));
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains source for the ZSetLocation, ZUnsetLocation, and
+ * ZFlushMyLocations functions.
+ *
+ * Created by: Robert French
+ *
+ * $Id: ZLocations.c,v 1.37 1999/01/22 23:19:15 ghudson Exp $
+ *
+ * Copyright (c) 1987,1988,1991 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#ifndef lint
+static char rcsid_ZLocations_c[] =
+ "$Zephyr: /afs/athena.mit.edu/astaff/project/zephyr/src/lib/RCS/ZLocations.c,v 1.30 90/12/20 03:04:39 raeburn Exp $";
+#endif
+
+#include <internal.h>
+
+#include <pwd.h>
+
+static char host[MAXHOSTNAMELEN], mytty[MAXPATHLEN];
+static int location_info_set = 0;
+
+Code_t ZInitLocationInfo(hostname, tty)
+ char *hostname;
+ char *tty;
+{
+ char *ttyp, *p;
+ struct hostent *hent;
+
+ if (hostname) {
+ strcpy(host, hostname);
+ } else {
+ if (gethostname(host, MAXHOSTNAMELEN) < 0)
+ return (errno);
+ hent = gethostbyname(host);
+ if (hent) {
+ (void) strncpy(host, hent->h_name, sizeof(host));
+ host[sizeof(host) - 1] = '\0';
+ }
+ }
+ if (tty) {
+ strcpy(mytty, tty);
+ } else {
+ ttyp = ttyname(0);
+ if (ttyp && *ttyp) {
+ p = strchr(ttyp + 1, '/');
+ strcpy(mytty, (p) ? p + 1 : ttyp);
+ } else {
+ strcpy(mytty, "unknown");
+ }
+ }
+ location_info_set = 1;
+ return (ZERR_NONE);
+}
+
+Code_t ZSetLocation(exposure)
+ char *exposure;
+{
+ return (Z_SendLocation(LOGIN_CLASS, exposure, ZAUTH,
+ "$sender logged in to $1 on $3 at $2"));
+}
+
+Code_t ZUnsetLocation()
+{
+ return (Z_SendLocation(LOGIN_CLASS, LOGIN_USER_LOGOUT, ZNOAUTH,
+ "$sender logged out of $1 on $3 at $2"));
+}
+
+Code_t ZFlushMyLocations()
+{
+ return (Z_SendLocation(LOGIN_CLASS, LOGIN_USER_FLUSH, ZAUTH, ""));
+}
+
+char *ZParseExposureLevel(text)
+ char *text;
+{
+ if (!strcasecmp(text, EXPOSE_NONE))
+ return (EXPOSE_NONE);
+ else if (!strcasecmp(text, EXPOSE_OPSTAFF))
+ return (EXPOSE_OPSTAFF);
+ else if (!strcasecmp(text, EXPOSE_REALMVIS))
+ return (EXPOSE_REALMVIS);
+ else if (!strcasecmp(text, EXPOSE_REALMANN))
+ return (EXPOSE_REALMANN);
+ else if (!strcasecmp(text, EXPOSE_NETVIS))
+ return (EXPOSE_NETVIS);
+ else if (!strcasecmp(text, EXPOSE_NETANN))
+ return (EXPOSE_NETANN);
+ else
+ return(NULL);
+}
+
+Code_t Z_SendLocation(class, opcode, auth, format)
+ char *class;
+ char *opcode;
+ Z_AuthProc auth;
+ char *format;
+{
+ int retval;
+ time_t ourtime;
+ ZNotice_t notice, retnotice;
+ char *bptr[3];
+ struct hostent *hent;
+ short wg_port = ZGetWGPort();
+
+ if (!location_info_set)
+ ZInitLocationInfo(NULL, NULL);
+
+ memset((char *)¬ice, 0, sizeof(notice));
+ notice.z_kind = ACKED;
+ notice.z_port = (u_short) ((wg_port == -1) ? 0 : wg_port);
+ notice.z_class = class;
+ notice.z_class_inst = ZGetSender();
+ notice.z_opcode = opcode;
+ notice.z_sender = 0;
+ notice.z_recipient = "";
+ notice.z_num_other_fields = 0;
+ notice.z_default_format = format;
+
+ bptr[0] = host;
+ ourtime = time((time_t *)0);
+ bptr[1] = ctime(&ourtime);
+ bptr[1][strlen(bptr[1])-1] = '\0';
+ bptr[2] = mytty;
+
+ if ((retval = ZSendList(¬ice, bptr, 3, auth)) != ZERR_NONE)
+ return (retval);
+
+ retval = Z_WaitForNotice (&retnotice, ZCompareUIDPred, ¬ice.z_uid,
+ SRV_TIMEOUT);
+ if (retval != ZERR_NONE)
+ return retval;
+
+ if (retnotice.z_kind == SERVNAK) {
+ if (!retnotice.z_message_len) {
+ ZFreeNotice(&retnotice);
+ return (ZERR_SERVNAK);
+ }
+ if (!strcmp(retnotice.z_message, ZSRVACK_NOTSENT)) {
+ ZFreeNotice(&retnotice);
+ return (ZERR_AUTHFAIL);
+ }
+ if (!strcmp(retnotice.z_message, ZSRVACK_FAIL)) {
+ ZFreeNotice(&retnotice);
+ return (ZERR_LOGINFAIL);
+ }
+ ZFreeNotice(&retnotice);
+ return (ZERR_SERVNAK);
+ }
+
+ if (retnotice.z_kind != SERVACK) {
+ ZFreeNotice(&retnotice);
+ return (ZERR_INTERNAL);
+ }
+
+ if (!retnotice.z_message_len) {
+ ZFreeNotice(&retnotice);
+ return (ZERR_INTERNAL);
+ }
+
+ if (strcmp(retnotice.z_message, ZSRVACK_SENT) &&
+ strcmp(retnotice.z_message, ZSRVACK_NOTSENT)) {
+ ZFreeNotice(&retnotice);
+ return (ZERR_INTERNAL);
+ }
+
+ ZFreeNotice(&retnotice);
+
+ return (ZERR_NONE);
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains source for the ZMakeAscii function.
+ *
+ * Created by: Robert French
+ *
+ * $Id: ZMakeAscii.c,v 1.13 1999/01/22 23:19:16 ghudson Exp $
+ *
+ * Copyright (c) 1987 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <internal.h>
+#include <assert.h>
+
+#ifndef lint
+static const char rcsid_ZMakeAscii_c[] = "$Id: ZMakeAscii.c,v 1.13 1999/01/22 23:19:16 ghudson Exp $";
+#endif
+
+static char *itox_chars = "0123456789ABCDEF";
+
+Code_t ZMakeAscii(ptr, len, field, num)
+ register char *ptr;
+ int len;
+ unsigned char *field;
+ int num;
+{
+ int i;
+
+ for (i=0;i<num;i++) {
+ /* we need to add "0x" if we are between 4 byte pieces */
+ if ((i & 3) == 0) {
+ if (len < (i?4:3))
+ return ZERR_FIELDLEN;
+ /* except at the beginning, put a space in before the "0x" */
+ if (i) {
+ *ptr++ = ' ';
+ len--;
+ }
+ *ptr++ = '0';
+ *ptr++ = 'x';
+ len -= 2;
+ }
+ if (len < 3)
+ return ZERR_FIELDLEN;
+ *ptr++ = itox_chars[(int) (field[i] >> 4)];
+ *ptr++ = itox_chars[(int) (field[i] & 0xf)];
+ len -= 2;
+ }
+
+ *ptr = '\0';
+ return ZERR_NONE;
+}
+
+Code_t ZMakeAscii32(ptr, len, value)
+ register char *ptr;
+ int len;
+ unsigned long value;
+{
+ if (len < 11)
+ return ZERR_FIELDLEN;
+ *ptr++ = '0';
+ *ptr++ = 'x';
+ *ptr++ = itox_chars[(value >> 28) & 0xf];
+ *ptr++ = itox_chars[(value >> 24) & 0xf];
+ *ptr++ = itox_chars[(value >> 20) & 0xf];
+ *ptr++ = itox_chars[(value >> 16) & 0xf];
+ *ptr++ = itox_chars[(value >> 12) & 0xf];
+ *ptr++ = itox_chars[(value >> 8) & 0xf];
+ *ptr++ = itox_chars[(value >> 4) & 0xf];
+ *ptr++ = itox_chars[(value >> 0) & 0xf];
+ *ptr = 0;
+ return ZERR_NONE;
+}
+
+Code_t ZMakeAscii16(ptr, len, value)
+ register char *ptr;
+ int len;
+ unsigned int value;
+{
+ if (len < 7)
+ return ZERR_FIELDLEN;
+ *ptr++ = '0';
+ *ptr++ = 'x';
+ *ptr++ = itox_chars[(value >> 12) & 0xf];
+ *ptr++ = itox_chars[(value >> 8) & 0xf];
+ *ptr++ = itox_chars[(value >> 4) & 0xf];
+ *ptr++ = itox_chars[(value >> 0) & 0xf];
+ *ptr = 0;
+ return ZERR_NONE;
+}
+
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains source for the ZMakeAuthentication function.
+ *
+ * Created by: Robert French
+ *
+ * $Id: ZMkAuth.c,v 1.19 1999/01/22 23:19:16 ghudson Exp $
+ *
+ * Copyright (c) 1987 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <internal.h>
+
+#ifndef lint
+static const char rcsid_ZMakeAuthentication_c[] = "$Id: ZMkAuth.c,v 1.19 1999/01/22 23:19:16 ghudson Exp $";
+#endif
+
+#ifdef HAVE_KRB4
+#include <krb_err.h>
+static long last_authent_time = 0L;
+static KTEXT_ST last_authent;
+#endif
+
+Code_t ZResetAuthentication () {
+#ifdef HAVE_KRB4
+ last_authent_time = 0L;
+#endif
+ return ZERR_NONE;
+}
+
+Code_t ZMakeAuthentication(notice, buffer, buffer_len, len)
+ register ZNotice_t *notice;
+ char *buffer;
+ int buffer_len;
+ int *len;
+{
+#ifdef HAVE_KRB4
+ int result;
+ time_t now;
+ KTEXT_ST authent;
+ char *cstart, *cend;
+ ZChecksum_t checksum;
+ CREDENTIALS cred;
+ extern unsigned long des_quad_cksum();
+
+ now = time(0);
+ if (last_authent_time == 0 || (now - last_authent_time > 120)) {
+ result = krb_mk_req(&authent, SERVER_SERVICE,
+ SERVER_INSTANCE, __Zephyr_realm, 0);
+ if (result != MK_AP_OK) {
+ last_authent_time = 0;
+ return (result+krb_err_base);
+ }
+ last_authent_time = now;
+ last_authent = authent;
+ }
+ else {
+ authent = last_authent;
+ }
+ notice->z_auth = 1;
+ notice->z_authent_len = authent.length;
+ notice->z_ascii_authent = (char *)malloc((unsigned)authent.length*3);
+ /* zero length authent is an error, so malloc(0) is not a problem */
+ if (!notice->z_ascii_authent)
+ return (ENOMEM);
+ if ((result = ZMakeAscii(notice->z_ascii_authent,
+ authent.length*3,
+ authent.dat,
+ authent.length)) != ZERR_NONE) {
+ free(notice->z_ascii_authent);
+ return (result);
+ }
+ result = Z_FormatRawHeader(notice, buffer, buffer_len, len, &cstart,
+ &cend);
+ free(notice->z_ascii_authent);
+ notice->z_authent_len = 0;
+ if (result)
+ return(result);
+
+ /* Compute a checksum over the header and message. */
+ if ((result = krb_get_cred(SERVER_SERVICE, SERVER_INSTANCE,
+ __Zephyr_realm, &cred)) != 0)
+ return result;
+ checksum = des_quad_cksum(buffer, NULL, cstart - buffer, 0, cred.session);
+ checksum ^= des_quad_cksum(cend, NULL, buffer + *len - cend, 0,
+ cred.session);
+ checksum ^= des_quad_cksum(notice->z_message, NULL, notice->z_message_len,
+ 0, cred.session);
+ notice->z_checksum = checksum;
+ ZMakeAscii32(cstart, buffer + buffer_len - cstart, checksum);
+
+ return (ZERR_NONE);
+#else
+ notice->z_checksum = 0;
+ notice->z_auth = 1;
+ notice->z_authent_len = 0;
+ notice->z_ascii_authent = "";
+ return (Z_FormatRawHeader(notice, buffer, buffer_len, len, NULL, NULL));
+#endif
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains source for the ZNewLocateUser function.
+ *
+ * Created by: Robert French
+ *
+ * $Id: ZNewLocU.c,v 1.10 1999/01/22 23:19:17 ghudson Exp $
+ *
+ * Copyright (c) 1987,1988,1991 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <internal.h>
+
+#ifndef lint
+static const char rcsid_ZNewLocateUser_c[] =
+ "$Id: ZNewLocU.c,v 1.10 1999/01/22 23:19:17 ghudson Exp $";
+#endif
+
+Code_t ZLocateUser(user, nlocs, auth)
+ char *user;
+ int *nlocs;
+ Z_AuthProc auth;
+{
+ Code_t retval;
+ ZNotice_t notice;
+ ZAsyncLocateData_t zald;
+
+ (void) ZFlushLocations(); /* ZFlushLocations never fails (the library
+ is allowed to know this). */
+
+ if ((retval = ZRequestLocations(user, &zald, UNACKED, auth)) != ZERR_NONE)
+ return(retval);
+
+ retval = Z_WaitForNotice (¬ice, ZCompareALDPred, &zald, SRV_TIMEOUT);
+ if (retval == ZERR_NONOTICE)
+ return ETIMEDOUT;
+ if (retval != ZERR_NONE)
+ return retval;
+
+ if ((retval = ZParseLocations(¬ice, &zald, nlocs, NULL)) != ZERR_NONE) {
+ ZFreeNotice(¬ice);
+ return(retval);
+ }
+
+ ZFreeNotice(¬ice);
+ ZFreeALD(&zald);
+ return(ZERR_NONE);
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains source for the ZOpenPort function.
+ *
+ * Created by: Robert French
+ *
+ * $Id: ZOpenPort.c,v 1.15 1999/01/22 23:19:17 ghudson Exp $
+ *
+ * Copyright (c) 1987 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#ifndef lint
+static char rcsid_ZOpenPort_c[] = "$Id: ZOpenPort.c,v 1.15 1999/01/22 23:19:17 ghudson Exp $";
+#endif
+
+#include <internal.h>
+#include <sys/socket.h>
+
+Code_t ZOpenPort(port)
+ u_short *port;
+{
+ struct sockaddr_in bindin;
+ int len;
+#ifdef SO_BSDCOMPAT
+ int on = 1;
+#endif
+
+ (void) ZClosePort();
+
+ if ((__Zephyr_fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ __Zephyr_fd = -1;
+ return (errno);
+ }
+
+#ifdef SO_BSDCOMPAT
+ /* Prevent Linux from giving us socket errors we don't care about. */
+ setsockopt(__Zephyr_fd, SOL_SOCKET, SO_BSDCOMPAT, &on, sizeof(on));
+#endif
+
+ bindin.sin_family = AF_INET;
+
+ if (port && *port)
+ bindin.sin_port = *port;
+ else
+ bindin.sin_port = 0;
+
+ bindin.sin_addr.s_addr = INADDR_ANY;
+
+ if (bind(__Zephyr_fd, (struct sockaddr *)&bindin, sizeof(bindin)) < 0) {
+ if (errno == EADDRINUSE && port && *port)
+ return (ZERR_PORTINUSE);
+ else
+ return (errno);
+ }
+
+ if (!bindin.sin_port) {
+ len = sizeof(bindin);
+ if (getsockname(__Zephyr_fd, (struct sockaddr *)&bindin, &len))
+ return (errno);
+ }
+
+ __Zephyr_port = bindin.sin_port;
+ __Zephyr_open = 1;
+
+ if (port)
+ *port = bindin.sin_port;
+
+ return (ZERR_NONE);
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains source for the ZParseNotice function.
+ *
+ * Created by: Robert French
+ *
+ * $Id: ZParseNot.c,v 1.29 1999/08/13 00:19:44 danw Exp $
+ *
+ * Copyright (c) 1987,1991 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#ifndef lint
+static char rcsid_ZParseNotice_c[] =
+ "$Zephyr: /mit/zephyr/src/lib/RCS/ZParseNotice.c,v 1.22 91/03/29 03:34:46 raeburn Exp $";
+#endif
+
+#include <internal.h>
+
+/* Assume that strlen is efficient on this machine... */
+#define next_field(ptr) ptr += strlen (ptr) + 1
+
+#if defined (__GNUC__) && defined (__vax__)
+#undef next_field
+static __inline__ char * Istrend (char *str) {
+ /*
+ * This should be faster on VAX models outside the 2 series. Don't
+ * use it if you are using MicroVAX 2 servers. If you are using a
+ * VS2 server, use something like
+ * #define next_field(ptr) while(*ptr++)
+ * instead of this code.
+ *
+ * This requires use of GCC to get the optimized code, but
+ * everybody uses GCC, don't they? :-)
+ */
+ register char *str2 asm ("r1");
+ /* Assumes that no field is longer than 64K.... */
+ asm ("locc $0,$65535,(%1)" : "=r" (str2) : "r" (str) : "r0");
+ return str2;
+}
+#define next_field(ptr) ptr = Istrend (ptr) + 1
+#endif
+
+#ifdef mips
+#undef next_field
+/*
+ * The compiler doesn't optimize this macro as well as it does the
+ * following function.
+ */
+#define next_fieldXXX(ptr) do{register unsigned c1,c2;c1= *ptr; \
+ while((ptr++,c2= *ptr,c1)&&(ptr++,c1= *ptr,c2));}while(0)
+static char *next_field_1 (s) char *s; {
+ /*
+ * Calling overhead is still present, but this routine is faster
+ * than strlen, and doesn't bother with some of the other math
+ * that we'd just have to undo later anyways.
+ */
+ register unsigned c1 = *s, c2;
+ while (1) {
+ s++; c2 = *s; if (c1 == 0) break;
+ s++; c1 = *s; if (c2 == 0) break;
+ s++; c2 = *s; if (c1 == 0) break;
+ s++; c1 = *s; if (c2 == 0) break;
+ }
+ return s;
+}
+#define next_field(ptr) ptr=next_field_1(ptr)
+#endif
+
+Code_t ZParseNotice(buffer, len, notice)
+ char *buffer;
+ int len;
+ ZNotice_t *notice;
+{
+ char *ptr, *end;
+ unsigned long temp;
+ int maj, numfields, i;
+
+#ifdef __LINE__
+ int lineno;
+ /* Note: This definition of BAD eliminates lint and compiler
+ * complains about the "while (0)", but require that the macro not
+ * be used as the "then" part of an "if" statement that also has
+ * an "else" clause.
+ */
+#define BAD_PACKET {lineno=__LINE__;goto badpkt;}
+ /* This one gets lint/compiler complaints. */
+/*#define BAD do{lineno=__LINE__;goto badpkt;}while(0)*/
+#else
+#define BAD_PACKET goto badpkt
+#endif
+
+ (void) memset((char *)notice, 0, sizeof(ZNotice_t));
+
+ ptr = buffer;
+ end = buffer+len;
+
+ notice->z_packet = buffer;
+
+ notice->z_version = ptr;
+ if (strncmp(ptr, ZVERSIONHDR, sizeof(ZVERSIONHDR) - 1))
+ return (ZERR_VERS);
+ ptr += sizeof(ZVERSIONHDR) - 1;
+ if (!*ptr) {
+#ifdef Z_DEBUG
+ Z_debug ("ZParseNotice: null version string");
+#endif
+ return ZERR_BADPKT;
+ }
+ maj = atoi(ptr);
+ if (maj != ZVERSIONMAJOR)
+ return (ZERR_VERS);
+ next_field (ptr);
+
+ if (ZReadAscii32(ptr, end-ptr, &temp) == ZERR_BADFIELD)
+ BAD_PACKET;
+ numfields = temp;
+ next_field (ptr);
+
+ /*XXX 3 */
+ numfields -= 2; /* numfields, version, and checksum */
+ if (numfields < 0) {
+#ifdef __LINE__
+ lineno = __LINE__;
+ badpkt:
+#ifdef Z_DEBUG
+ Z_debug ("ZParseNotice: bad packet from %s/%d (line %d)",
+ inet_ntoa (notice->z_uid.zuid_addr.s_addr),
+ notice->z_port, lineno);
+#endif
+#else
+ badpkt:
+#ifdef Z_DEBUG
+ Z_debug ("ZParseNotice: bad packet from %s/%d",
+ inet_ntoa (notice->z_uid.zuid_addr.s_addr),
+ notice->z_port);
+#endif
+#endif
+ return ZERR_BADPKT;
+ }
+
+ if (numfields) {
+ if (ZReadAscii32(ptr, end-ptr, &temp) == ZERR_BADFIELD)
+ BAD_PACKET;
+ notice->z_kind = (ZNotice_Kind_t)temp;
+ numfields--;
+ next_field (ptr);
+ }
+ else
+ BAD_PACKET;
+
+ if (numfields) {
+ if (ZReadAscii(ptr, end-ptr, (unsigned char *)¬ice->z_uid,
+ sizeof(ZUnique_Id_t)) == ZERR_BADFIELD)
+ BAD_PACKET;
+ notice->z_time.tv_sec = ntohl((u_long) notice->z_uid.tv.tv_sec);
+ notice->z_time.tv_usec = ntohl((u_long) notice->z_uid.tv.tv_usec);
+ numfields--;
+ next_field (ptr);
+ }
+ else
+ BAD_PACKET;
+
+ if (numfields) {
+ if (ZReadAscii16(ptr, end-ptr, ¬ice->z_port) == ZERR_BADFIELD)
+ BAD_PACKET;
+ notice->z_port = htons(notice->z_port);
+ numfields--;
+ next_field (ptr);
+ }
+ else
+ BAD_PACKET;
+
+ if (numfields) {
+ if (ZReadAscii32(ptr, end-ptr, &temp) == ZERR_BADFIELD)
+ BAD_PACKET;
+ notice->z_auth = temp;
+ numfields--;
+ next_field (ptr);
+ }
+ else
+ BAD_PACKET;
+ notice->z_checked_auth = ZAUTH_UNSET;
+
+ if (numfields) {
+ if (ZReadAscii32(ptr, end-ptr, &temp) == ZERR_BADFIELD)
+ BAD_PACKET;
+ notice->z_authent_len = temp;
+ numfields--;
+ next_field (ptr);
+ }
+ else
+ BAD_PACKET;
+
+ if (numfields) {
+ notice->z_ascii_authent = ptr;
+ numfields--;
+ next_field (ptr);
+ }
+ else
+ BAD_PACKET;
+
+ if (numfields) {
+ notice->z_class = ptr;
+ numfields--;
+ next_field (ptr);
+ }
+ else
+ notice->z_class = "";
+
+ if (numfields) {
+ notice->z_class_inst = ptr;
+ numfields--;
+ next_field (ptr);
+ }
+ else
+ notice->z_class_inst = "";
+
+ if (numfields) {
+ notice->z_opcode = ptr;
+ numfields--;
+ next_field (ptr);
+ }
+ else
+ notice->z_opcode = "";
+
+ if (numfields) {
+ notice->z_sender = ptr;
+ numfields--;
+ next_field (ptr);
+ }
+ else
+ notice->z_sender = "";
+
+ if (numfields) {
+ notice->z_recipient = ptr;
+ numfields--;
+ next_field (ptr);
+ }
+ else
+ notice->z_recipient = "";
+
+ if (numfields) {
+ notice->z_default_format = ptr;
+ numfields--;
+ next_field (ptr);
+ }
+ else
+ notice->z_default_format = "";
+
+/*XXX*/
+ if (ZReadAscii32(ptr, end-ptr, &temp) == ZERR_BADFIELD)
+ BAD_PACKET;
+ notice->z_checksum = temp;
+ numfields--;
+ next_field (ptr);
+
+ if (numfields) {
+ notice->z_multinotice = ptr;
+ numfields--;
+ next_field (ptr);
+ }
+ else
+ notice->z_multinotice = "";
+
+ if (numfields) {
+ if (ZReadAscii(ptr, end-ptr, (unsigned char *)¬ice->z_multiuid,
+ sizeof(ZUnique_Id_t)) == ZERR_BADFIELD)
+ BAD_PACKET;
+ notice->z_time.tv_sec = ntohl((u_long) notice->z_multiuid.tv.tv_sec);
+ notice->z_time.tv_usec = ntohl((u_long) notice->z_multiuid.tv.tv_usec);
+ numfields--;
+ next_field (ptr);
+ }
+ else
+ notice->z_multiuid = notice->z_uid;
+
+ for (i=0;i<Z_MAXOTHERFIELDS && numfields;i++,numfields--) {
+ notice->z_other_fields[i] = ptr;
+ next_field (ptr);
+ }
+ notice->z_num_other_fields = i;
+
+ for (i=0;i<numfields;i++)
+ next_field (ptr);
+
+ notice->z_message = (caddr_t) ptr;
+ notice->z_message_len = len-(ptr-buffer);
+
+ return (ZERR_NONE);
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains source for the ZPeekIfNotice function.
+ *
+ * Created by: Robert French
+ *
+ * $Id: ZPeekIfNot.c,v 1.14 1999/01/22 23:19:19 ghudson Exp $
+ *
+ * Copyright (c) 1987,1988 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#ifndef lint
+static char rcsid_ZPeekIfNotice_c[] = "$Id: ZPeekIfNot.c,v 1.14 1999/01/22 23:19:19 ghudson Exp $";
+#endif
+
+#include <internal.h>
+
+Code_t ZPeekIfNotice(notice, from, predicate, args)
+ ZNotice_t *notice;
+ struct sockaddr_in *from;
+ int (*predicate)();
+ char *args;
+{
+ ZNotice_t tmpnotice;
+ Code_t retval;
+ char *buffer;
+ struct _Z_InputQ *qptr;
+
+ if ((retval = Z_WaitForComplete()) != ZERR_NONE)
+ return (retval);
+
+ for (;;) {
+ qptr = Z_GetFirstComplete();
+ while (qptr) {
+ if ((retval = ZParseNotice(qptr->packet, qptr->packet_len,
+ &tmpnotice)) != ZERR_NONE)
+ return (retval);
+ if ((*predicate)(&tmpnotice, args)) {
+ if (!(buffer = (char *) malloc((unsigned) qptr->packet_len)))
+ return (ENOMEM);
+ (void) memcpy(buffer, qptr->packet, qptr->packet_len);
+ if (from)
+ *from = qptr->from;
+ if ((retval = ZParseNotice(buffer, qptr->packet_len,
+ notice)) != ZERR_NONE) {
+ free(buffer);
+ return (retval);
+ }
+ return (ZERR_NONE);
+ }
+ qptr = Z_GetNextComplete(qptr);
+ }
+ if ((retval = Z_ReadWait()) != ZERR_NONE)
+ return (retval);
+ }
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains source for ZPeekNotice function.
+ *
+ * Created by: Robert French
+ *
+ * $Id: ZPeekNot.c,v 1.9 1999/01/22 23:19:19 ghudson Exp $
+ *
+ * Copyright (c) 1987 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#ifndef lint
+static char rcsid_ZPeekNotice_c[] = "$Id: ZPeekNot.c,v 1.9 1999/01/22 23:19:19 ghudson Exp $";
+#endif
+
+#include <internal.h>
+
+Code_t ZPeekNotice(notice, from)
+ ZNotice_t *notice;
+ struct sockaddr_in *from;
+{
+ char *buffer;
+ int len;
+ Code_t retval;
+
+ if ((retval = ZPeekPacket(&buffer, &len, from)) != ZERR_NONE)
+ return (retval);
+
+ return (ZParseNotice(buffer, len, notice));
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains source for ZPeekPacket function.
+ *
+ * Created by: Robert French
+ *
+ * $Id: ZPeekPkt.c,v 1.13 1999/01/22 23:19:20 ghudson Exp $
+ *
+ * Copyright (c) 1987 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#ifndef lint
+static char rcsid_ZPeekPacket_c[] = "$Id: ZPeekPkt.c,v 1.13 1999/01/22 23:19:20 ghudson Exp $";
+#endif
+
+#include <internal.h>
+
+Code_t ZPeekPacket(buffer, ret_len, from)
+ char **buffer;
+ int *ret_len;
+ struct sockaddr_in *from;
+{
+ Code_t retval;
+ struct _Z_InputQ *nextq;
+
+ if ((retval = Z_WaitForComplete()) != ZERR_NONE)
+ return (retval);
+
+ nextq =Z_GetFirstComplete();
+
+ *ret_len = nextq->packet_len;
+
+ if (!(*buffer = (char *) malloc((unsigned) *ret_len)))
+ return (ENOMEM);
+
+ (void) memcpy(*buffer, nextq->packet, *ret_len);
+
+ if (from)
+ *from = nextq->from;
+
+ return (ZERR_NONE);
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains source for the ZPending function.
+ *
+ * Created by: Robert French
+ *
+ * $Id: ZPending.c,v 1.8 1999/01/22 23:19:20 ghudson Exp $
+ *
+ * Copyright (c) 1987 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#ifndef lint
+static char rcsid_ZPending_c[] = "$Id: ZPending.c,v 1.8 1999/01/22 23:19:20 ghudson Exp $";
+#endif
+
+#include <internal.h>
+
+int ZPending()
+{
+ int retval;
+
+ if (ZGetFD() < 0) {
+ errno = ZERR_NOPORT;
+ return (-1);
+ }
+
+ if ((retval = Z_ReadEnqueue()) != ZERR_NONE) {
+ errno = retval;
+ return (-1);
+ }
+
+ return(ZQLength());
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains source for the ZReadAscii function.
+ *
+ * Created by: Robert French
+ *
+ * $Id: ZReadAscii.c,v 1.18 1999/01/22 23:19:21 ghudson Exp $
+ *
+ * Copyright (c) 1987, 1990 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#ifndef lint
+static char rcsid_ZReadAscii_c[] = "$Id: ZReadAscii.c,v 1.18 1999/01/22 23:19:21 ghudson Exp $";
+#endif /* lint */
+
+#include <internal.h>
+#include <assert.h>
+
+#if 0
+static __inline__
+int
+Z_cnvt_xtoi (char c)
+{
+ c -= '0';
+ if (c < 10)
+ return c;
+ c -= 'A'-'9'-1;
+ if (c < 16)
+ return c;
+ return -1;
+}
+#endif
+
+#define Z_cnvt_xtoi(c) ((temp=(c)-'0'),(temp<10)?temp:((temp-='A'-'9'-1),(temp<16)?temp:-1))
+
+Code_t ZReadAscii(ptr, len, field, num)
+ char *ptr;
+ int len;
+ unsigned char *field;
+ int num;
+{
+ int i;
+ unsigned int hexbyte;
+ register int c1, c2;
+ register unsigned int temp;
+
+ for (i=0;i<num;i++) {
+ if (*ptr == ' ') {
+ ptr++;
+ if (--len < 0)
+ return ZERR_BADFIELD;
+ }
+ if (ptr[0] == '0' && ptr[1] == 'x') {
+ ptr += 2;
+ len -= 2;
+ if (len < 0)
+ return ZERR_BADFIELD;
+ }
+ c1 = Z_cnvt_xtoi(ptr[0]);
+ if (c1 < 0)
+ return ZERR_BADFIELD;
+ c2 = Z_cnvt_xtoi(ptr[1]);
+ if (c2 < 0)
+ return ZERR_BADFIELD;
+ hexbyte = (c1 << 4) | c2;
+ field[i] = hexbyte;
+ ptr += 2;
+ len -= 2;
+ if (len < 0)
+ return ZERR_BADFIELD;
+ }
+
+ return *ptr ? ZERR_BADFIELD : ZERR_NONE;
+}
+
+Code_t ZReadAscii32(ptr, len, value_ptr)
+ char *ptr;
+ int len;
+ unsigned long *value_ptr;
+{
+ unsigned char buf[4];
+ Code_t retval;
+
+ retval = ZReadAscii(ptr, len, buf, 4);
+ if (retval != ZERR_NONE)
+ return retval;
+ *value_ptr = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
+ return ZERR_NONE;
+}
+
+Code_t ZReadAscii16(ptr, len, value_ptr)
+ char *ptr;
+ int len;
+ unsigned short *value_ptr;
+{
+ unsigned char buf[2];
+ Code_t retval;
+
+ retval = ZReadAscii(ptr, len, buf, 2);
+ if (retval != ZERR_NONE)
+ return retval;
+ *value_ptr = (buf[0] << 8) | buf[1];
+ return ZERR_NONE;
+}
+
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains source for ZReceiveNotice function.
+ *
+ * Created by: Robert French
+ *
+ * $Id: ZRecvNot.c,v 1.15 1999/01/22 23:19:21 ghudson Exp $
+ *
+ * Copyright (c) 1987,1988 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#ifndef lint
+static char rcsid_ZReceiveNotice_c[] = "$Id: ZRecvNot.c,v 1.15 1999/01/22 23:19:21 ghudson Exp $";
+#endif
+
+#include <internal.h>
+
+Code_t ZReceiveNotice(notice, from)
+ ZNotice_t *notice;
+ struct sockaddr_in *from;
+{
+ char *buffer;
+ struct _Z_InputQ *nextq;
+ int len, auth;
+ Code_t retval;
+
+ if ((retval = Z_WaitForComplete()) != ZERR_NONE)
+ return (retval);
+
+ nextq = Z_GetFirstComplete();
+
+ len = nextq->packet_len;
+
+ if (!(buffer = (char *) malloc((unsigned) len)))
+ return (ENOMEM);
+
+ if (from)
+ *from = nextq->from;
+
+ (void) memcpy(buffer, nextq->packet, len);
+
+ auth = nextq->auth;
+ Z_RemQueue(nextq);
+
+ if ((retval = ZParseNotice(buffer, len, notice)) != ZERR_NONE)
+ return (retval);
+ notice->z_checked_auth = auth;
+ return ZERR_NONE;
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains source for ZReceivePacket function.
+ *
+ * Created by: Robert French
+ *
+ * $Id: ZRecvPkt.c,v 1.17 1999/01/22 23:19:22 ghudson Exp $
+ *
+ * Copyright (c) 1987,1988 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#ifndef lint
+static char rcsid_ZReceivePacket_c[] = "$Id: ZRecvPkt.c,v 1.17 1999/01/22 23:19:22 ghudson Exp $";
+#endif
+
+#include <internal.h>
+
+Code_t ZReceivePacket(buffer, ret_len, from)
+ ZPacket_t buffer;
+ int *ret_len;
+ struct sockaddr_in *from;
+{
+ Code_t retval;
+ struct _Z_InputQ *nextq;
+
+ if ((retval = Z_WaitForComplete()) != ZERR_NONE)
+ return (retval);
+
+ nextq = Z_GetFirstComplete();
+
+ *ret_len = nextq->packet_len;
+ if (*ret_len > Z_MAXPKTLEN)
+ return (ZERR_PKTLEN);
+
+ (void) memcpy(buffer, nextq->packet, *ret_len);
+
+ if (from)
+ *from = nextq->from;
+
+ Z_RemQueue(nextq);
+
+ return (ZERR_NONE);
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains source for the ZRetrieveSubscriptions and
+ * ZRetrieveDefaultSubscriptions functions.
+ *
+ * Created by: Robert French
+ *
+ * $Id: ZRetSubs.c,v 1.26 1999/01/22 23:19:23 ghudson Exp $
+ *
+ * Copyright (c) 1987,1988,1991 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <internal.h>
+
+#ifndef lint
+static const char rcsid_ZRetrieveSubscriptions_c[] =
+ "$Id: ZRetSubs.c,v 1.26 1999/01/22 23:19:23 ghudson Exp $";
+#endif
+
+static Code_t Z_RetSubs ();
+
+/* Need STDC definition when possible for unsigned short argument. */
+#ifdef __STDC__
+Code_t ZRetrieveSubscriptions(unsigned short port, int *nsubs)
+#else
+Code_t ZRetrieveSubscriptions(port,nsubs)
+ unsigned short port;
+ int *nsubs;
+#endif
+{
+ int retval;
+ ZNotice_t notice;
+ char asciiport[50];
+
+ if (!port) /* use default port */
+ port = __Zephyr_port;
+
+ retval = ZMakeAscii16(asciiport, sizeof(asciiport), ntohs(port));
+ if (retval != ZERR_NONE)
+ return (retval);
+
+ (void) memset((char *)¬ice, 0, sizeof(notice));
+ notice.z_message = asciiport;
+ notice.z_message_len = strlen(asciiport)+1;
+ notice.z_opcode = CLIENT_GIMMESUBS;
+
+ return(Z_RetSubs(¬ice, nsubs, ZAUTH));
+}
+
+Code_t ZRetrieveDefaultSubscriptions(nsubs)
+ int *nsubs;
+{
+ ZNotice_t notice;
+
+ (void) memset((char *)¬ice, 0, sizeof(notice));
+ notice.z_message = (char *) 0;
+ notice.z_message_len = 0;
+ notice.z_opcode = CLIENT_GIMMEDEFS;
+
+ return(Z_RetSubs(¬ice, nsubs, ZNOAUTH));
+
+}
+
+static Code_t Z_RetSubs(notice, nsubs, auth_routine)
+ register ZNotice_t *notice;
+ int *nsubs;
+ Z_AuthProc auth_routine;
+{
+ register int i;
+ int retval,nrecv,gimmeack;
+ ZNotice_t retnotice;
+ char *ptr,*end,*ptr2;
+ ZSubscription_t *list = __subscriptions_list;
+
+ retval = ZFlushSubscriptions();
+
+ if (retval != ZERR_NONE && retval != ZERR_NOSUBSCRIPTIONS)
+ return (retval);
+
+ if (ZGetFD() < 0)
+ if ((retval = ZOpenPort((u_short *)0)) != ZERR_NONE)
+ return (retval);
+
+ notice->z_kind = ACKED;
+ notice->z_port = __Zephyr_port;
+ notice->z_class = ZEPHYR_CTL_CLASS;
+ notice->z_class_inst = ZEPHYR_CTL_CLIENT;
+ notice->z_sender = 0;
+ notice->z_recipient = "";
+ notice->z_default_format = "";
+
+ if ((retval = ZSendNotice(notice,auth_routine)) != ZERR_NONE)
+ return (retval);
+
+ nrecv = 0;
+ gimmeack = 0;
+ list = (ZSubscription_t *) 0;
+
+ while (!nrecv || !gimmeack) {
+ retval = Z_WaitForNotice (&retnotice, ZCompareMultiUIDPred,
+ ¬ice->z_multiuid, SRV_TIMEOUT);
+ if (retval == ZERR_NONOTICE)
+ return ETIMEDOUT;
+ else if (retval != ZERR_NONE)
+ return retval;
+
+ if (retnotice.z_kind == SERVNAK) {
+ ZFreeNotice(&retnotice);
+ return (ZERR_SERVNAK);
+ }
+ /* non-matching protocol version numbers means the
+ server is probably an older version--must punt */
+ if (strcmp(notice->z_version,retnotice.z_version)) {
+ ZFreeNotice(&retnotice);
+ return(ZERR_VERS);
+ }
+ if (retnotice.z_kind == SERVACK &&
+ !strcmp(retnotice.z_opcode,notice->z_opcode)) {
+ ZFreeNotice(&retnotice);
+ gimmeack = 1;
+ continue;
+ }
+
+ if (retnotice.z_kind != ACKED) {
+ ZFreeNotice(&retnotice);
+ return (ZERR_INTERNAL);
+ }
+
+ nrecv++;
+
+ end = retnotice.z_message+retnotice.z_message_len;
+
+ __subscriptions_num = 0;
+ for (ptr=retnotice.z_message;ptr<end;ptr++)
+ if (!*ptr)
+ __subscriptions_num++;
+
+ __subscriptions_num = __subscriptions_num / 3;
+
+ list = (ZSubscription_t *)
+ malloc(__subscriptions_num * sizeof(ZSubscription_t));
+ if (__subscriptions_num && !list) {
+ ZFreeNotice(&retnotice);
+ return (ENOMEM);
+ }
+
+ ptr = retnotice.z_message;
+ for (i = 0; i < __subscriptions_num; i++) {
+ list[i].zsub_class = (char *)
+ malloc(strlen(ptr) + 1);
+ if (!list[i].zsub_class) {
+ ZFreeNotice(&retnotice);
+ return (ENOMEM);
+ }
+ strcpy(list[i].zsub_class, ptr);
+ ptr += strlen(ptr)+1;
+ list[i].zsub_classinst = (char *)
+ malloc(strlen(ptr) + 1);
+ if (!list[i].zsub_classinst) {
+ ZFreeNotice(&retnotice);
+ return (ENOMEM);
+ }
+ strcpy(list[i].zsub_classinst, ptr);
+ ptr += strlen(ptr)+1;
+ ptr2 = ptr;
+ list[i].zsub_recipient = (char *)
+ malloc(strlen(ptr2) + 2);
+ if (!list[i].zsub_recipient) {
+ ZFreeNotice(&retnotice);
+ return (ENOMEM);
+ }
+ if (*ptr2 == '@' || *ptr2 == 0) {
+ *list[i].zsub_recipient = '*';
+ strcpy(list[i].zsub_recipient + 1, ptr2);
+ } else {
+ strcpy(list[i].zsub_recipient, ptr2);
+ }
+ ptr += strlen(ptr)+1;
+ }
+ ZFreeNotice(&retnotice);
+ }
+
+ __subscriptions_list = list;
+ __subscriptions_next = 0;
+ *nsubs = __subscriptions_num;
+
+ return (ZERR_NONE);
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains source for the ZSendList function.
+ *
+ * Created by: Robert French
+ *
+ * $Id: ZSendList.c,v 1.13 1999/01/22 23:19:23 ghudson Exp $
+ *
+ * Copyright (c) 1987 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#ifndef lint
+static char rcsid_ZSendList_c[] = "$Id: ZSendList.c,v 1.13 1999/01/22 23:19:23 ghudson Exp $";
+#endif
+
+#include <internal.h>
+
+Code_t ZSendList(notice, list, nitems, cert_routine)
+ ZNotice_t *notice;
+ char *list[];
+ int nitems;
+ Z_AuthProc cert_routine;
+{
+ return(ZSrvSendList(notice, list, nitems, cert_routine, Z_XmitFragment));
+}
+
+Code_t ZSrvSendList(notice, list, nitems, cert_routine, send_routine)
+ ZNotice_t *notice;
+ char *list[];
+ int nitems;
+ Z_AuthProc cert_routine;
+ Code_t (*send_routine)();
+{
+ Code_t retval;
+ ZNotice_t newnotice;
+ char *buffer;
+ int len;
+
+ if ((retval = ZFormatNoticeList(notice, list, nitems, &buffer,
+ &len, cert_routine)) != ZERR_NONE)
+ return (retval);
+
+ if ((retval = ZParseNotice(buffer, len, &newnotice)) != ZERR_NONE)
+ return (retval);
+
+ retval = Z_SendFragmentedNotice(&newnotice, len, cert_routine,
+ send_routine);
+
+ free(buffer);
+
+ return (retval);
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains source for the ZSendNotice function.
+ *
+ * Created by: Robert French
+ *
+ * $Id: ZSendNot.c,v 1.13 1999/01/22 23:19:24 ghudson Exp $
+ *
+ * Copyright (c) 1987 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#ifndef lint
+static char rcsid_ZSendNotice_c[] = "$Id: ZSendNot.c,v 1.13 1999/01/22 23:19:24 ghudson Exp $";
+#endif
+
+#include <internal.h>
+
+Code_t ZSendNotice(notice, cert_routine)
+ ZNotice_t *notice;
+ Z_AuthProc cert_routine;
+{
+ return(ZSrvSendNotice(notice, cert_routine, Z_XmitFragment));
+}
+
+Code_t ZSrvSendNotice(notice, cert_routine, send_routine)
+ ZNotice_t *notice;
+ Z_AuthProc cert_routine;
+ Code_t (*send_routine)();
+{
+ Code_t retval;
+ ZNotice_t newnotice;
+ char *buffer;
+ int len;
+
+ if ((retval = ZFormatNotice(notice, &buffer, &len,
+ cert_routine)) != ZERR_NONE)
+ return (retval);
+
+ if ((retval = ZParseNotice(buffer, len, &newnotice)) != ZERR_NONE)
+ return (retval);
+
+ retval = Z_SendFragmentedNotice(&newnotice, len, cert_routine,
+ send_routine);
+
+ free(buffer);
+
+ return (retval);
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains source for the ZSendPacket function.
+ *
+ * Created by: Robert French
+ *
+ * $Id: ZSendPkt.c,v 1.32 1999/01/22 23:19:24 ghudson Exp $
+ *
+ * Copyright (c) 1987,1991 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#ifndef lint
+static char rcsid_ZSendPacket_c[] =
+ "$Zephyr: /mit/zephyr/src/lib/RCS/ZSendPacket.c,v 1.29 91/03/21 11:57:08 raeburn Exp $";
+#endif
+
+#include <internal.h>
+#include <sys/socket.h>
+
+static int wait_for_hmack();
+
+Code_t ZSendPacket(packet, len, waitforack)
+ char *packet;
+ int len;
+ int waitforack;
+{
+ Code_t retval;
+ struct sockaddr_in dest;
+ ZNotice_t notice, acknotice;
+
+ if (!packet || len < 0)
+ return (ZERR_ILLVAL);
+
+ if (len > Z_MAXPKTLEN)
+ return (ZERR_PKTLEN);
+
+ if (ZGetFD() < 0)
+ if ((retval = ZOpenPort((u_short *)0)) != ZERR_NONE)
+ return (retval);
+
+ dest = ZGetDestAddr();
+
+ if (sendto(ZGetFD(), packet, len, 0, (struct sockaddr *)&dest,
+ sizeof(dest)) < 0)
+ return (errno);
+
+ if (!waitforack)
+ return (ZERR_NONE);
+
+ if ((retval = ZParseNotice(packet, len, ¬ice)) != ZERR_NONE)
+ return (retval);
+
+ retval = Z_WaitForNotice (&acknotice, wait_for_hmack, ¬ice.z_uid,
+ HM_TIMEOUT);
+ if (retval == ETIMEDOUT)
+ return ZERR_HMDEAD;
+ if (retval == ZERR_NONE)
+ ZFreeNotice (&acknotice);
+ return retval;
+}
+
+static int wait_for_hmack(notice, uid)
+ ZNotice_t *notice;
+ ZUnique_Id_t *uid;
+{
+ return (notice->z_kind == HMACK && ZCompareUID(¬ice->z_uid, uid));
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains source for the ZSendRawNotice function.
+ *
+ * Created by: John T. Kohl
+ *
+ * $Id: ZSendRLst.c,v 1.5 1999/01/22 23:19:27 ghudson Exp $
+ *
+ * Copyright (c) 1988 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#ifndef lint
+static char rcsid_ZSendRawList_c[] = "$Id: ZSendRLst.c,v 1.5 1999/01/22 23:19:27 ghudson Exp $";
+#endif
+
+#include <internal.h>
+
+Code_t ZSendRawList(notice, list, nitems)
+ ZNotice_t *notice;
+ char *list[];
+ int nitems;
+{
+ return(ZSrvSendRawList(notice, list, nitems, Z_XmitFragment));
+}
+
+Code_t ZSrvSendRawList(notice, list, nitems, send_routine)
+ ZNotice_t *notice;
+ char *list[];
+ int nitems;
+ Code_t (*send_routine)();
+{
+ Code_t retval;
+ ZNotice_t newnotice;
+ char *buffer;
+ int len;
+
+ if ((retval = ZFormatRawNoticeList(notice, list, nitems, &buffer,
+ &len)) != ZERR_NONE)
+ return (retval);
+
+ if ((retval = ZParseNotice(buffer, len, &newnotice)) != ZERR_NONE)
+ return (retval);
+
+ retval = Z_SendFragmentedNotice(&newnotice, len, NULL, send_routine);
+
+ free(buffer);
+
+ return (retval);
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains source for the ZSendRawNotice function.
+ *
+ * Created by: Robert French
+ *
+ * $Id: ZSendRaw.c,v 1.8 1999/01/22 23:19:27 ghudson Exp $
+ *
+ * Copyright (c) 1987 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#ifndef lint
+static char rcsid_ZSendRawNotice_c[] = "$Id: ZSendRaw.c,v 1.8 1999/01/22 23:19:27 ghudson Exp $";
+#endif
+
+#include <internal.h>
+
+Code_t ZSendRawNotice(notice)
+ ZNotice_t *notice;
+{
+ Code_t retval;
+ ZNotice_t newnotice;
+ char *buffer;
+ int len;
+
+ if ((retval = ZFormatRawNotice(notice, &buffer, &len)) !=
+ ZERR_NONE)
+ return (retval);
+
+ if ((retval = ZParseNotice(buffer, len, &newnotice)) != ZERR_NONE)
+ return (retval);
+
+ retval = Z_SendFragmentedNotice(&newnotice, len, NULL, Z_XmitFragment);
+
+ free(buffer);
+
+ return (retval);
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains source for the ZSetDestAddr function.
+ *
+ * Created by: Robert French
+ *
+ * $Id: ZSetDest.c,v 1.5 1999/01/22 23:19:28 ghudson Exp $
+ *
+ * Copyright (c) 1987 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#ifndef lint
+static char rcsid_ZSetDestAddr_c[] = "$Id: ZSetDest.c,v 1.5 1999/01/22 23:19:28 ghudson Exp $";
+#endif
+
+#include <internal.h>
+
+Code_t ZSetDestAddr(addr)
+ struct sockaddr_in *addr;
+{
+ __HM_addr = *addr;
+
+ __HM_set = 1;
+
+ return (ZERR_NONE);
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains source for the ZSetFD function.
+ *
+ * Created by: Robert French
+ *
+ * $Id: ZSetFD.c,v 1.8 1999/01/22 23:19:28 ghudson Exp $
+ *
+ * Copyright (c) 1987 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#ifndef lint
+static char rcsid_ZSetFD_c[] = "$Id: ZSetFD.c,v 1.8 1999/01/22 23:19:28 ghudson Exp $";
+#endif
+
+#include <internal.h>
+
+Code_t ZSetFD(fd)
+ int fd;
+{
+ (void) ZClosePort();
+
+ __Zephyr_fd = fd;
+ __Zephyr_open = 0;
+
+ return (ZERR_NONE);
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains source for the ZSetServerState function.
+ *
+ * Created by: Robert French
+ *
+ * $Id: ZSetSrv.c,v 1.5 1999/01/22 23:19:29 ghudson Exp $
+ *
+ * Copyright (c) 1987 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#ifndef lint
+static char rcsid_ZSetServerState_c[] = "$Id: ZSetSrv.c,v 1.5 1999/01/22 23:19:29 ghudson Exp $";
+#endif
+
+#include <internal.h>
+
+Code_t ZSetServerState(state)
+ int state;
+{
+ __Zephyr_server = state;
+
+ return (ZERR_NONE);
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains source for the ZSubscribeTo, ZUnsubscribeTo, and
+ * ZCancelSubscriptions functions.
+ *
+ * Created by: Robert French
+ *
+ * $Id: ZSubs.c,v 1.21 1999/01/22 23:19:30 ghudson Exp $
+ *
+ * Copyright (c) 1987,1988 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <internal.h>
+
+#ifndef lint
+static const char rcsid_ZSubscriptions_c[] = "$Id: ZSubs.c,v 1.21 1999/01/22 23:19:30 ghudson Exp $";
+#endif
+
+static Code_t Z_Subscriptions __P((register ZSubscription_t *sublist,
+ int nitems, unsigned int port,
+ char *opcode, int authit));
+static Code_t subscr_sendoff __P((ZNotice_t *notice, char **lyst, int num,
+ int authit));
+
+Code_t ZSubscribeTo(sublist, nitems, port)
+ ZSubscription_t *sublist;
+ int nitems;
+ unsigned int port;
+{
+ return (Z_Subscriptions(sublist, nitems, port, CLIENT_SUBSCRIBE, 1));
+}
+
+Code_t ZSubscribeToSansDefaults(sublist, nitems, port)
+ ZSubscription_t *sublist;
+ int nitems;
+ unsigned int port;
+{
+ return (Z_Subscriptions(sublist, nitems, port, CLIENT_SUBSCRIBE_NODEFS,
+ 1));
+}
+
+Code_t ZUnsubscribeTo(sublist, nitems, port)
+ ZSubscription_t *sublist;
+ int nitems;
+ unsigned int port;
+{
+ return (Z_Subscriptions(sublist, nitems, port, CLIENT_UNSUBSCRIBE, 1));
+}
+
+Code_t ZCancelSubscriptions(port)
+ unsigned int port;
+{
+ return (Z_Subscriptions((ZSubscription_t *)0, 0, port,
+ CLIENT_CANCELSUB, 0));
+}
+
+/*
+ * This routine must do its own fragmentation. Subscriptions must
+ * not be broken across packet boundaries, or else the server will
+ * mis-interpret them.
+ */
+
+static Code_t
+Z_Subscriptions(sublist, nitems, port, opcode, authit)
+ register ZSubscription_t *sublist;
+ int nitems;
+ unsigned int port;
+ char *opcode;
+ int authit;
+{
+ register int i, j;
+ int retval;
+ ZNotice_t notice;
+ char header[Z_MAXHEADERLEN];
+ char **list;
+ char *recip;
+ int hdrlen;
+ int size_avail = Z_MAXPKTLEN-Z_FRAGFUDGE; /* space avail for data,
+ adjusted below */
+ int size, start, numok;
+
+ /* nitems = 0 means cancel all subscriptions; still need to allocate a */
+ /* array for one item so we can cancel, however. */
+
+ list = (char **)malloc((unsigned)((nitems==0)?1:nitems)*3*sizeof(char *));
+ if (!list)
+ return (ENOMEM);
+
+ (void) memset((char *)¬ice, 0, sizeof(notice));
+ notice.z_kind = ACKED;
+ notice.z_port = port;
+ notice.z_class = ZEPHYR_CTL_CLASS;
+ notice.z_class_inst = ZEPHYR_CTL_CLIENT;
+ notice.z_opcode = opcode;
+ notice.z_sender = 0;
+ notice.z_recipient = "";
+ notice.z_default_format = "";
+ notice.z_message_len = 0;
+
+ /* format the header to figure out how long it is */
+ retval = Z_FormatHeader(¬ice, header, sizeof(header), &hdrlen, ZAUTH);
+ if (retval != ZERR_NONE && !authit)
+ retval = Z_FormatHeader(¬ice, header, sizeof(header),
+ &hdrlen, ZNOAUTH);
+ if (retval != ZERR_NONE) {
+ free((char *)list);
+ return(retval);
+ }
+
+ /* compute amount of room left */
+ size_avail -= hdrlen;
+ size = size_avail;
+
+ /* assemble subs into an array of pointers */
+ for (i=0;i<nitems;i++) {
+ list[i*3] = sublist[i].zsub_class;
+ list[i*3+1] = sublist[i].zsub_classinst;
+ recip = sublist[i].zsub_recipient;
+ if (recip && *recip == '*')
+ recip++;
+ if (!recip || (*recip != 0 && *recip != '@'))
+ recip = ZGetSender();
+ list[i*3+2] = recip;
+ }
+
+ start = -1;
+ i = 0;
+ numok = 0;
+ if (!nitems) {
+ /* there aren't really any, but we need to xmit anyway */
+ retval = subscr_sendoff(¬ice, list, 0, authit);
+ free((char *)list);
+ return(retval);
+ }
+ while(i < nitems) {
+ if (start == -1) {
+ size = size_avail;
+ start = i;
+ numok = 0;
+ }
+ if ((j = strlen(list[i*3])
+ + strlen(list[i*3+1])
+ + strlen(list[i*3+2]) + 3) <= size) {
+ /* it will fit in this packet */
+ size -= j;
+ numok++;
+ i++;
+ continue;
+ }
+ if (!numok) { /* a single subscription won't
+ fit into one packet */
+ free((char *)list);
+ return(ZERR_FIELDLEN);
+ }
+ retval = subscr_sendoff(¬ice, &list[start*3], numok, authit);
+ if (retval) {
+ free((char *)list);
+ return(retval);
+ }
+ start = -1;
+ }
+ if (numok)
+ retval = subscr_sendoff(¬ice, &list[start*3], numok, authit);
+ free((char *)list);
+ return(retval);
+}
+
+static Code_t
+subscr_sendoff(notice, lyst, num, authit)
+ZNotice_t *notice;
+char **lyst;
+int num;
+int authit;
+{
+ register Code_t retval;
+ ZNotice_t retnotice;
+
+ retval = ZSendList(notice, lyst, num*3, ZAUTH);
+ if (retval != ZERR_NONE && !authit)
+ retval = ZSendList(notice, lyst, num*3, ZNOAUTH);
+
+ if (retval != ZERR_NONE)
+ return (retval);
+ if ((retval = ZIfNotice(&retnotice, (struct sockaddr_in *)0,
+ ZCompareUIDPred, (char *)¬ice->z_uid)) !=
+ ZERR_NONE)
+ return (retval);
+ if (retnotice.z_kind == SERVNAK) {
+ ZFreeNotice(&retnotice);
+ return (ZERR_SERVNAK);
+ }
+ if (retnotice.z_kind != SERVACK) {
+ ZFreeNotice(&retnotice);
+ return (ZERR_INTERNAL);
+ }
+ ZFreeNotice(&retnotice);
+ return (ZERR_NONE);
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains source for the ZGetVariable, ZSetVariable, and ZUnsetVariable
+ * functions.
+ *
+ * Created by: Robert French
+ *
+ * $Id: ZVariables.c,v 1.17 1999/06/03 14:51:42 danw Exp $
+ *
+ * Copyright (c) 1987 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#ifndef lint
+static char rcsid_ZVariables_c[] = "$Id: ZVariables.c,v 1.17 1999/06/03 14:51:42 danw Exp $";
+#endif
+
+#include <internal.h>
+
+#include <ctype.h>
+#include <pwd.h>
+
+static int get_localvarfile __P((char *bfr));
+static char *get_varval __P((char *fn, char *val));
+static int varline __P((char *bfr, char *var));
+
+char *ZGetVariable(var)
+ char *var;
+{
+ char varfile[128], *ret;
+ char *get_varval();
+
+ if (get_localvarfile(varfile))
+ return ((char *)0);
+
+ if ((ret = get_varval(varfile, var)) != ZERR_NONE)
+ return (ret);
+
+ sprintf(varfile, "%s/zephyr.vars", SYSCONFDIR);
+ return (get_varval(varfile, var));
+}
+
+Code_t ZSetVariable(var, value)
+ char *var;
+ char *value;
+{
+ int written;
+ FILE *fpin, *fpout;
+ char varfile[128], varfilebackup[128], varbfr[512];
+
+ written = 0;
+
+ if (get_localvarfile(varfile))
+ return (ZERR_INTERNAL);
+
+ (void) strcpy(varfilebackup, varfile);
+ (void) strcat(varfilebackup, ".backup");
+
+ if (!(fpout = fopen(varfilebackup, "w")))
+ return (errno);
+ if ((fpin = fopen(varfile, "r")) != NULL) {
+ while (fgets(varbfr, sizeof varbfr, fpin) != (char *) 0) {
+ if (varbfr[strlen(varbfr)-1] < ' ')
+ varbfr[strlen(varbfr)-1] = '\0';
+ if (varline(varbfr, var)) {
+ fprintf(fpout, "%s = %s\n", var, value);
+ written = 1;
+ }
+ else
+ fprintf(fpout, "%s\n", varbfr);
+ }
+ (void) fclose(fpin); /* don't care about errs on input */
+ }
+ if (!written)
+ fprintf(fpout, "%s = %s\n", var, value);
+ if (fclose(fpout) == EOF)
+ return(EIO); /* can't rely on errno */
+ if (rename(varfilebackup, varfile))
+ return (errno);
+ return (ZERR_NONE);
+}
+
+Code_t ZUnsetVariable(var)
+ char *var;
+{
+ FILE *fpin, *fpout;
+ char varfile[128], varfilebackup[128], varbfr[512];
+
+ if (get_localvarfile(varfile))
+ return (ZERR_INTERNAL);
+
+ (void) strcpy(varfilebackup, varfile);
+ (void) strcat(varfilebackup, ".backup");
+
+ if (!(fpout = fopen(varfilebackup, "w")))
+ return (errno);
+ if ((fpin = fopen(varfile, "r")) != NULL) {
+ while (fgets(varbfr, sizeof varbfr, fpin) != (char *) 0) {
+ if (varbfr[strlen(varbfr)-1] < ' ')
+ varbfr[strlen(varbfr)-1] = '\0';
+ if (!varline(varbfr, var))
+ fprintf(fpout, "%s\n", varbfr);
+ }
+ (void) fclose(fpin); /* don't care about read close errs */
+ }
+ if (fclose(fpout) == EOF)
+ return(EIO); /* errno isn't reliable */
+ if (rename(varfilebackup, varfile))
+ return (errno);
+ return (ZERR_NONE);
+}
+
+static int get_localvarfile(bfr)
+ char *bfr;
+{
+ char *envptr;
+ struct passwd *pwd;
+
+ envptr = getenv("ZEPHYR_VARS");
+ if (envptr)
+ (void) strcpy(bfr, envptr);
+ else {
+ envptr = getenv("HOME");
+ if (envptr)
+ (void) strcpy(bfr, envptr);
+ else {
+ if (!(pwd = getpwuid((int) getuid()))) {
+ fprintf(stderr, "Zephyr internal failure: Can't determine your home directory.\n");
+ return (1);
+ }
+ (void) strcpy(bfr, pwd->pw_dir);
+ }
+
+ (void) strcat(bfr, "/");
+ (void) strcat(bfr, ".zephyr.vars");
+ }
+ return (0);
+}
+
+static char *get_varval(fn, var)
+ char *fn;
+ char *var;
+{
+ FILE *fp;
+ static char varbfr[512];
+ int i;
+
+ fp = fopen(fn, "r");
+ if (!fp)
+ return ((char *)0);
+
+ while (fgets(varbfr, sizeof varbfr, fp) != (char *) 0) {
+ if (varbfr[strlen(varbfr)-1] < ' ')
+ varbfr[strlen(varbfr)-1] = '\0';
+ if (!(i = varline(varbfr, var)))
+ continue;
+ (void) fclose(fp); /* open read-only, don't care */
+ return (varbfr+i);
+ }
+ (void) fclose(fp); /* open read-only, don't care */
+ return ((char *)0);
+}
+
+/* If the variable in the line bfr[] is the same as var, return index to
+ the variable value, else return 0. */
+static int varline(bfr, var)
+ char *bfr;
+ char *var;
+{
+ register char *cp;
+
+
+ if (!bfr[0] || bfr[0] == '#') /* comment or null line */
+ return (0);
+
+ cp = bfr;
+ while (*cp && !isspace(*cp) && (*cp != '='))
+ cp++;
+
+#define max(a,b) ((a > b) ? (a) : (b))
+
+ if (strncasecmp(bfr, var, max(strlen(var),cp - bfr)))
+ return(0); /* var is not the var in
+ bfr ==> no match */
+
+ cp = strchr(bfr, '=');
+ if (!cp)
+ return(0);
+ cp++;
+ while (*cp && isspace(*cp)) /* space up to variable value */
+ cp++;
+
+ return (cp - bfr); /* return index */
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains the ZCheckIfNotice/select loop used for waiting for
+ * a notice, with a timeout.
+ *
+ * Created by: <Joe Random Hacker>
+ *
+ * $Id: ZWait4Not.c,v 1.3 1999/01/22 23:19:31 ghudson Exp $
+ *
+ * Copyright (c) 1991 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include "mit-copyright.h"
+
+#ifndef lint
+static char rcsid_ZWaitForNotice_c[] = "$Id: ZWait4Not.c,v 1.3 1999/01/22 23:19:31 ghudson Exp $";
+#endif
+
+#include <internal.h>
+#include <sys/socket.h>
+
+Code_t Z_WaitForNotice (notice, pred, arg, timeout)
+ ZNotice_t *notice;
+ int (*pred) __P((ZNotice_t *, void *));
+ void *arg;
+ int timeout;
+{
+ Code_t retval;
+ struct timeval tv, t0;
+ fd_set fdmask;
+ int i, fd;
+
+ retval = ZCheckIfNotice (notice, (struct sockaddr_in *) 0, pred,
+ (char *) arg);
+ if (retval == ZERR_NONE)
+ return ZERR_NONE;
+ if (retval != ZERR_NONOTICE)
+ return retval;
+
+ fd = ZGetFD ();
+ FD_ZERO (&fdmask);
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+ gettimeofday (&t0, (struct timezone *) 0);
+ t0.tv_sec += timeout;
+ while (1) {
+ FD_SET (fd, &fdmask);
+ i = select (fd + 1, &fdmask, (fd_set *) 0, (fd_set *) 0, &tv);
+ if (i == 0)
+ return ETIMEDOUT;
+ if (i < 0 && errno != EINTR)
+ return errno;
+ if (i > 0) {
+ retval = ZCheckIfNotice (notice, (struct sockaddr_in *) 0, pred,
+ (char *) arg);
+ if (retval != ZERR_NONOTICE) /* includes ZERR_NONE */
+ return retval;
+ }
+ gettimeofday (&tv, (struct timezone *) 0);
+ tv.tv_usec = t0.tv_usec - tv.tv_usec;
+ if (tv.tv_usec < 0) {
+ tv.tv_usec += 1000000;
+ tv.tv_sec = t0.tv_sec - tv.tv_sec - 1;
+ }
+ else
+ tv.tv_sec = t0.tv_sec - tv.tv_sec;
+ }
+ /*NOTREACHED*/
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains the ZhmStat() function.
+ *
+ * Created by: Marc Horowitz
+ *
+ * $Id: ZhmStat.c,v 1.5 1999/01/22 23:19:32 ghudson Exp $
+ *
+ * Copyright (c) 1996 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <internal.h>
+#include <sys/socket.h>
+
+#ifndef INADDR_LOOPBACK
+#define INADDR_LOOPBACK 0x7f000001
+#endif
+
+Code_t ZhmStat(hostaddr, notice)
+ struct in_addr *hostaddr;
+ ZNotice_t *notice;
+{
+ struct servent *sp;
+ struct sockaddr_in sin;
+ ZNotice_t req;
+ Code_t code;
+ struct timeval tv;
+ fd_set readers;
+
+ (void) memset((char *)&sin, 0, sizeof(struct sockaddr_in));
+
+ sp = getservbyname(HM_SVCNAME, "udp");
+
+ sin.sin_port = (sp) ? sp->s_port : HM_SVC_FALLBACK;
+ sin.sin_family = AF_INET;
+
+ if (hostaddr)
+ sin.sin_addr = *hostaddr;
+ else
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ (void) memset((char *)&req, 0, sizeof(req));
+ req.z_kind = STAT;
+ req.z_port = 0;
+ req.z_class = HM_STAT_CLASS;
+ req.z_class_inst = HM_STAT_CLIENT;
+ req.z_opcode = HM_GIMMESTATS;
+ req.z_sender = "";
+ req.z_recipient = "";
+ req.z_default_format = "";
+ req.z_message_len = 0;
+
+ if ((code = ZSetDestAddr(&sin)) != ZERR_NONE)
+ return(code);
+
+ if ((code = ZSendNotice(&req, ZNOAUTH)) != ZERR_NONE)
+ return(code);
+
+ /* Wait up to ten seconds for a response. */
+ FD_ZERO(&readers);
+ FD_SET(ZGetFD(), &readers);
+ tv.tv_sec = 10;
+ tv.tv_usec = 0;
+ code = select(ZGetFD() + 1, &readers, NULL, NULL, &tv);
+ if (code < 0 && errno != EINTR)
+ return(errno);
+ if (code == 0 || (code < 0 && errno == EINTR) || ZPending() == 0)
+ return(ZERR_HMDEAD);
+
+ return(ZReceiveNotice(notice, (struct sockaddr_in *) 0));
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains source for the internal Zephyr routines.
+ *
+ * Created by: Robert French
+ *
+ * $Id: Zinternal.c,v 1.41 2000/01/27 03:48:53 ghudson Exp $
+ *
+ * Copyright (c) 1987,1988,1991 by the Massachusetts Institute of
+ * Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <internal.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <utmp.h>
+
+#ifndef lint
+static const char rcsid_Zinternal_c[] =
+ "$Id: Zinternal.c,v 1.41 2000/01/27 03:48:53 ghudson Exp $";
+static const char copyright[] =
+ "Copyright (c) 1987,1988,1991 by the Massachusetts Institute of Technology.";
+#endif
+
+extern char *inet_ntoa ();
+
+int __Zephyr_fd = -1;
+int __Zephyr_open;
+int __Zephyr_port = -1;
+struct in_addr __My_addr;
+int __Q_CompleteLength;
+int __Q_Size;
+struct _Z_InputQ *__Q_Head, *__Q_Tail;
+struct sockaddr_in __HM_addr;
+struct sockaddr_in __HM_addr_real;
+int __HM_set;
+int __Zephyr_server;
+ZLocations_t *__locate_list;
+int __locate_num;
+int __locate_next;
+ZSubscription_t *__subscriptions_list;
+int __subscriptions_num;
+int __subscriptions_next;
+int Z_discarded_packets = 0;
+
+#ifdef HAVE_KRB4
+C_Block __Zephyr_session;
+#endif
+char __Zephyr_realm[REALM_SZ];
+
+#ifdef Z_DEBUG
+void (*__Z_debug_print) __P((const char *fmt, va_list args, void *closure));
+void *__Z_debug_print_closure;
+#endif
+
+#define min(a,b) ((a)<(b)?(a):(b))
+
+static int Z_AddField __P((char **ptr, char *field, char *end));
+static int find_or_insert_uid __P((ZUnique_Id_t *uid, ZNotice_Kind_t kind));
+
+/* Find or insert uid in the old uids buffer. The buffer is a sorted
+ * circular queue. We make the assumption that most packets arrive in
+ * order, so we can usually search for a uid or insert it into the buffer
+ * by looking back just a few entries from the end. Since this code is
+ * only executed by the client, the implementation isn't microoptimized. */
+static int find_or_insert_uid(uid, kind)
+ ZUnique_Id_t *uid;
+ ZNotice_Kind_t kind;
+{
+ static struct _filter {
+ ZUnique_Id_t uid;
+ ZNotice_Kind_t kind;
+ time_t t;
+ } *buffer;
+ static long size;
+ static long start;
+ static long num;
+
+ time_t now;
+ struct _filter *new;
+ long i, j, new_size;
+ int result;
+
+ /* Initialize the uid buffer if it hasn't been done already. */
+ if (!buffer) {
+ size = Z_INITFILTERSIZE;
+ buffer = (struct _filter *) malloc(size * sizeof(*buffer));
+ if (!buffer)
+ return 0;
+ }
+
+ /* Age the uid buffer, discarding any uids older than the clock skew. */
+ time(&now);
+ while (num && (now - buffer[start % size].t) > CLOCK_SKEW)
+ start++, num--;
+ start %= size;
+
+ /* Make room for a new uid, since we'll probably have to insert one. */
+ if (num == size) {
+ new_size = size * 2 + 2;
+ new = (struct _filter *) malloc(new_size * sizeof(*new));
+ if (!new)
+ return 0;
+ for (i = 0; i < num; i++)
+ new[i] = buffer[(start + i) % size];
+ free(buffer);
+ buffer = new;
+ size = new_size;
+ start = 0;
+ }
+
+ /* Search for this uid in the buffer, starting from the end. */
+ for (i = start + num - 1; i >= start; i--) {
+ result = memcmp(uid, &buffer[i % size].uid, sizeof(*uid));
+ if (result == 0 && buffer[i % size].kind == kind)
+ return 1;
+ if (result > 0)
+ break;
+ }
+
+ /* We didn't find it; insert the uid into the buffer after i. */
+ i++;
+ for (j = start + num; j > i; j--)
+ buffer[j % size] = buffer[(j - 1) % size];
+ buffer[i % size].uid = *uid;
+ buffer[i % size].kind = kind;
+ buffer[i % size].t = now;
+ num++;
+
+ return 0;
+}
+
+
+/* Return 1 if there is a packet waiting, 0 otherwise */
+
+int Z_PacketWaiting()
+{
+ struct timeval tv;
+ fd_set read;
+
+ tv.tv_sec = tv.tv_usec = 0;
+ FD_ZERO(&read);
+ FD_SET(ZGetFD(), &read);
+ return (select(ZGetFD() + 1, &read, NULL, NULL, &tv));
+}
+
+
+/* Wait for a complete notice to become available */
+
+Code_t Z_WaitForComplete()
+{
+ Code_t retval;
+
+ if (__Q_CompleteLength)
+ return (Z_ReadEnqueue());
+
+ while (!__Q_CompleteLength)
+ if ((retval = Z_ReadWait()) != ZERR_NONE)
+ return (retval);
+
+ return (ZERR_NONE);
+}
+
+
+/* Read any available packets and enqueue them */
+
+Code_t Z_ReadEnqueue()
+{
+ Code_t retval;
+
+ if (ZGetFD() < 0)
+ return (ZERR_NOPORT);
+
+ while (Z_PacketWaiting())
+ if ((retval = Z_ReadWait()) != ZERR_NONE)
+ return (retval);
+
+ return (ZERR_NONE);
+}
+
+
+/*
+ * Search the queue for a notice with the proper multiuid - remove any
+ * notices that haven't been touched in a while
+ */
+
+struct _Z_InputQ *Z_SearchQueue(uid, kind)
+ ZUnique_Id_t *uid;
+ ZNotice_Kind_t kind;
+{
+ register struct _Z_InputQ *qptr;
+ struct _Z_InputQ *next;
+ struct timeval tv;
+
+ (void) gettimeofday(&tv, (struct timezone *)0);
+
+ qptr = __Q_Head;
+
+ while (qptr) {
+ if (ZCompareUID(uid, &qptr->uid) && qptr->kind == kind)
+ return (qptr);
+ next = qptr->next;
+ if (qptr->timep && (qptr->timep+Z_NOTICETIMELIMIT < tv.tv_sec))
+ Z_RemQueue(qptr);
+ qptr = next;
+ }
+ return (NULL);
+}
+
+/*
+ * Now we delve into really convoluted queue handling and
+ * fragmentation reassembly algorithms and other stuff you probably
+ * don't want to look at...
+ *
+ * This routine does NOT guarantee a complete packet will be ready when it
+ * returns.
+ */
+
+Code_t Z_ReadWait()
+{
+ register struct _Z_InputQ *qptr;
+ ZNotice_t notice;
+ ZPacket_t packet;
+ struct sockaddr_in olddest, from;
+ int from_len, packet_len, zvlen, part, partof;
+ char *slash;
+ Code_t retval;
+ fd_set fds;
+ struct timeval tv;
+
+ if (ZGetFD() < 0)
+ return (ZERR_NOPORT);
+
+ FD_ZERO(&fds);
+ FD_SET(ZGetFD(), &fds);
+ tv.tv_sec = 60;
+ tv.tv_usec = 0;
+
+ if (select(ZGetFD() + 1, &fds, NULL, NULL, &tv) < 0)
+ return (errno);
+ if (!FD_ISSET(ZGetFD(), &fds))
+ return ETIMEDOUT;
+
+ from_len = sizeof(struct sockaddr_in);
+
+ packet_len = recvfrom(ZGetFD(), packet, sizeof(packet), 0,
+ (struct sockaddr *)&from, &from_len);
+
+ if (packet_len < 0)
+ return (errno);
+
+ if (!packet_len)
+ return (ZERR_EOF);
+
+ /* Ignore obviously non-Zephyr packets. */
+ zvlen = sizeof(ZVERSIONHDR) - 1;
+ if (packet_len < zvlen || memcmp(packet, ZVERSIONHDR, zvlen) != 0) {
+ Z_discarded_packets++;
+ return (ZERR_NONE);
+ }
+
+ /* Parse the notice */
+ if ((retval = ZParseNotice(packet, packet_len, ¬ice)) != ZERR_NONE)
+ return (retval);
+
+ /*
+ * If we're not a server and the notice is of an appropriate kind,
+ * send back a CLIENTACK to whoever sent it to say we got it.
+ */
+ if (!__Zephyr_server) {
+ if (notice.z_kind != HMACK && notice.z_kind != SERVACK &&
+ notice.z_kind != SERVNAK && notice.z_kind != CLIENTACK) {
+ ZNotice_t tmpnotice;
+ ZPacket_t pkt;
+ int len;
+
+ tmpnotice = notice;
+ tmpnotice.z_kind = CLIENTACK;
+ tmpnotice.z_message_len = 0;
+ olddest = __HM_addr;
+ __HM_addr = from;
+ if ((retval = ZFormatSmallRawNotice(&tmpnotice, pkt, &len))
+ != ZERR_NONE)
+ return(retval);
+ if ((retval = ZSendPacket(pkt, len, 0)) != ZERR_NONE)
+ return (retval);
+ __HM_addr = olddest;
+ }
+ if (find_or_insert_uid(¬ice.z_uid, notice.z_kind))
+ return(ZERR_NONE);
+
+ /* Check authentication on the notice. */
+ notice.z_checked_auth = ZCheckAuthentication(¬ice, &from);
+ }
+
+
+ /*
+ * Parse apart the z_multinotice field - if the field is blank for
+ * some reason, assume this packet stands by itself.
+ */
+ slash = strchr(notice.z_multinotice, '/');
+ if (slash) {
+ part = atoi(notice.z_multinotice);
+ partof = atoi(slash+1);
+ if (part > partof || partof == 0) {
+ part = 0;
+ partof = notice.z_message_len;
+ }
+ }
+ else {
+ part = 0;
+ partof = notice.z_message_len;
+ }
+
+ /* Too big a packet...just ignore it! */
+ if (partof > Z_MAXNOTICESIZE)
+ return (ZERR_NONE);
+
+ /*
+ * If we aren't a server and we can find a notice in the queue
+ * with the same multiuid field, insert the current fragment as
+ * appropriate.
+ */
+ switch (notice.z_kind) {
+ case SERVACK:
+ case SERVNAK:
+ /* The SERVACK and SERVNAK replies shouldn't be reassembled
+ (they have no parts). Instead, we should hold on to the reply
+ ONLY if it's the first part of a fragmented message, i.e.
+ multi_uid == uid. This allows programs to wait for the uid
+ of the first packet, and get a response when that notice
+ arrives. Acknowledgements of the other fragments are discarded
+ (XXX we assume here that they all carry the same information
+ regarding failure/success)
+ */
+ if (!__Zephyr_server &&
+ !ZCompareUID(¬ice.z_multiuid, ¬ice.z_uid))
+ /* they're not the same... throw away this packet. */
+ return(ZERR_NONE);
+ /* fall thru & process it */
+ default:
+ /* for HMACK types, we assume no packet loss (local loopback
+ connections). The other types can be fragmented and MUST
+ run through this code. */
+ if (!__Zephyr_server && (qptr = Z_SearchQueue(¬ice.z_multiuid,
+ notice.z_kind))) {
+ /*
+ * If this is the first fragment, and we haven't already
+ * gotten a first fragment, grab the header from it.
+ */
+ if (part == 0 && !qptr->header) {
+ qptr->header_len = packet_len-notice.z_message_len;
+ qptr->header = (char *) malloc((unsigned) qptr->header_len);
+ if (!qptr->header)
+ return (ENOMEM);
+ (void) memcpy(qptr->header, packet, qptr->header_len);
+ }
+ return (Z_AddNoticeToEntry(qptr, ¬ice, part));
+ }
+ }
+
+ /*
+ * We'll have to create a new entry...make sure the queue isn't
+ * going to get too big.
+ */
+ if (__Q_Size+(__Zephyr_server ? notice.z_message_len : partof) > Z_MAXQUEUESIZE)
+ return (ZERR_NONE);
+
+ /*
+ * This is a notice we haven't heard of, so create a new queue
+ * entry for it and zero it out.
+ */
+ qptr = (struct _Z_InputQ *)malloc(sizeof(struct _Z_InputQ));
+ if (!qptr)
+ return (ENOMEM);
+ (void) memset((char *)qptr, 0, sizeof(struct _Z_InputQ));
+
+ /* Insert the entry at the end of the queue */
+ qptr->next = NULL;
+ qptr->prev = __Q_Tail;
+ if (__Q_Tail)
+ __Q_Tail->next = qptr;
+ __Q_Tail = qptr;
+
+ if (!__Q_Head)
+ __Q_Head = qptr;
+
+
+ /* Copy the from field, multiuid, kind, and checked authentication. */
+ qptr->from = from;
+ qptr->uid = notice.z_multiuid;
+ qptr->kind = notice.z_kind;
+ qptr->auth = notice.z_checked_auth;
+
+ /*
+ * If this is the first part of the notice, we take the header
+ * from it. We only take it if this is the first fragment so that
+ * the Unique ID's will be predictable.
+ *
+ * If a Zephyr Server, we always take the header.
+ */
+ if (__Zephyr_server || part == 0) {
+ qptr->header_len = packet_len-notice.z_message_len;
+ qptr->header = (char *) malloc((unsigned) qptr->header_len);
+ if (!qptr->header)
+ return ENOMEM;
+ (void) memcpy(qptr->header, packet, qptr->header_len);
+ }
+
+ /*
+ * If this is not a fragmented notice, then don't bother with a
+ * hole list.
+ * If we are a Zephyr server, all notices are treated as complete.
+ */
+ if (__Zephyr_server || (part == 0 && notice.z_message_len == partof)) {
+ __Q_CompleteLength++;
+ qptr->holelist = (struct _Z_Hole *) 0;
+ qptr->complete = 1;
+ /* allocate a msg buf for this piece */
+ if (notice.z_message_len == 0)
+ qptr->msg = 0;
+ else if (!(qptr->msg = (char *) malloc((unsigned) notice.z_message_len)))
+ return(ENOMEM);
+ else
+ (void) memcpy(qptr->msg, notice.z_message, notice.z_message_len);
+ qptr->msg_len = notice.z_message_len;
+ __Q_Size += notice.z_message_len;
+ qptr->packet_len = qptr->header_len+qptr->msg_len;
+ if (!(qptr->packet = (char *) malloc((unsigned) qptr->packet_len)))
+ return (ENOMEM);
+ (void) memcpy(qptr->packet, qptr->header, qptr->header_len);
+ if(qptr->msg)
+ (void) memcpy(qptr->packet+qptr->header_len, qptr->msg,
+ qptr->msg_len);
+ return (ZERR_NONE);
+ }
+
+ /*
+ * We know how long the message is going to be (this is better
+ * than IP fragmentation...), so go ahead and allocate it all.
+ */
+ if (!(qptr->msg = (char *) malloc((unsigned) partof)) && partof)
+ return (ENOMEM);
+ qptr->msg_len = partof;
+ __Q_Size += partof;
+
+ /*
+ * Well, it's a fragmented notice...allocate a hole list and
+ * initialize it to the full packet size. Then insert the
+ * current fragment.
+ */
+ if (!(qptr->holelist = (struct _Z_Hole *)
+ malloc(sizeof(struct _Z_Hole))))
+ return (ENOMEM);
+ qptr->holelist->next = (struct _Z_Hole *) 0;
+ qptr->holelist->first = 0;
+ qptr->holelist->last = partof-1;
+ return (Z_AddNoticeToEntry(qptr, ¬ice, part));
+}
+
+
+/* Fragment management routines - compliments, more or less, of RFC815 */
+
+Code_t Z_AddNoticeToEntry(qptr, notice, part)
+ struct _Z_InputQ *qptr;
+ ZNotice_t *notice;
+ int part;
+{
+ int last, oldfirst, oldlast;
+ struct _Z_Hole *hole, *lasthole;
+ struct timeval tv;
+
+ /* Incorporate this notice's checked authentication. */
+ if (notice->z_checked_auth == ZAUTH_FAILED)
+ qptr->auth = ZAUTH_FAILED;
+ else if (notice->z_checked_auth == ZAUTH_NO && qptr->auth != ZAUTH_FAILED)
+ qptr->auth = ZAUTH_NO;
+
+ (void) gettimeofday(&tv, (struct timezone *)0);
+ qptr->timep = tv.tv_sec;
+
+ last = part+notice->z_message_len-1;
+
+ hole = qptr->holelist;
+ lasthole = (struct _Z_Hole *) 0;
+
+ /* copy in the message body */
+ (void) memcpy(qptr->msg+part, notice->z_message, notice->z_message_len);
+
+ /* Search for a hole that overlaps with the current fragment */
+ while (hole) {
+ if (part <= hole->last && last >= hole->first)
+ break;
+ lasthole = hole;
+ hole = hole->next;
+ }
+
+ /* If we found one, delete it and reconstruct a new hole */
+ if (hole) {
+ oldfirst = hole->first;
+ oldlast = hole->last;
+ if (lasthole)
+ lasthole->next = hole->next;
+ else
+ qptr->holelist = hole->next;
+ free((char *)hole);
+ /*
+ * Now create a new hole that is the original hole without the
+ * current fragment.
+ */
+ if (part > oldfirst) {
+ /* Search for the end of the hole list */
+ hole = qptr->holelist;
+ lasthole = (struct _Z_Hole *) 0;
+ while (hole) {
+ lasthole = hole;
+ hole = hole->next;
+ }
+ if (lasthole) {
+ if (!(lasthole->next = (struct _Z_Hole *)
+ malloc(sizeof(struct _Z_InputQ))))
+ return (ENOMEM);
+ hole = lasthole->next;
+ }
+ else {
+ if (!(qptr->holelist = (struct _Z_Hole *)
+ malloc(sizeof(struct _Z_InputQ))))
+ return (ENOMEM);
+ hole = qptr->holelist;
+ }
+ hole->next = NULL;
+ hole->first = oldfirst;
+ hole->last = part-1;
+ }
+ if (last < oldlast) {
+ /* Search for the end of the hole list */
+ hole = qptr->holelist;
+ lasthole = (struct _Z_Hole *) 0;
+ while (hole) {
+ lasthole = hole;
+ hole = hole->next;
+ }
+ if (lasthole) {
+ if (!(lasthole->next = (struct _Z_Hole *)
+ malloc(sizeof(struct _Z_InputQ))))
+ return (ENOMEM);
+ hole = lasthole->next;
+ }
+ else {
+ if (!(qptr->holelist = (struct _Z_Hole *)
+ malloc(sizeof(struct _Z_InputQ))))
+ return (ENOMEM);
+ hole = qptr->holelist;
+ }
+ hole->next = (struct _Z_Hole *) 0;
+ hole->first = last+1;
+ hole->last = oldlast;
+ }
+ }
+
+ if (!qptr->holelist) {
+ if (!qptr->complete)
+ __Q_CompleteLength++;
+ qptr->complete = 1;
+ qptr->timep = 0; /* don't time out anymore */
+ qptr->packet_len = qptr->header_len+qptr->msg_len;
+ if (!(qptr->packet = (char *) malloc((unsigned) qptr->packet_len)))
+ return (ENOMEM);
+ (void) memcpy(qptr->packet, qptr->header, qptr->header_len);
+ (void) memcpy(qptr->packet+qptr->header_len, qptr->msg,
+ qptr->msg_len);
+ }
+
+ return (ZERR_NONE);
+}
+
+Code_t Z_FormatHeader(notice, buffer, buffer_len, len, cert_routine)
+ ZNotice_t *notice;
+ char *buffer;
+ int buffer_len;
+ int *len;
+ Z_AuthProc cert_routine;
+{
+ Code_t retval;
+ static char version[BUFSIZ]; /* default init should be all \0 */
+ struct sockaddr_in name;
+ int namelen = sizeof(name);
+
+ if (!notice->z_sender)
+ notice->z_sender = ZGetSender();
+
+ if (notice->z_port == 0) {
+ if (ZGetFD() < 0) {
+ retval = ZOpenPort((u_short *)0);
+ if (retval != ZERR_NONE)
+ return (retval);
+ }
+ retval = getsockname(ZGetFD(), (struct sockaddr *) &name, &namelen);
+ if (retval != 0)
+ return (retval);
+ notice->z_port = name.sin_port;
+ }
+
+ notice->z_multinotice = "";
+
+ (void) gettimeofday(¬ice->z_uid.tv, (struct timezone *)0);
+ notice->z_uid.tv.tv_sec = htonl((u_long) notice->z_uid.tv.tv_sec);
+ notice->z_uid.tv.tv_usec = htonl((u_long) notice->z_uid.tv.tv_usec);
+
+ (void) memcpy(¬ice->z_uid.zuid_addr, &__My_addr, sizeof(__My_addr));
+
+ notice->z_multiuid = notice->z_uid;
+
+ if (!version[0])
+ (void) sprintf(version, "%s%d.%d", ZVERSIONHDR, ZVERSIONMAJOR,
+ ZVERSIONMINOR);
+ notice->z_version = version;
+
+ return Z_FormatAuthHeader(notice, buffer, buffer_len, len, cert_routine);
+}
+
+Code_t Z_FormatAuthHeader(notice, buffer, buffer_len, len, cert_routine)
+ ZNotice_t *notice;
+ char *buffer;
+ int buffer_len;
+ int *len;
+ Z_AuthProc cert_routine;
+{
+ if (!cert_routine) {
+ notice->z_auth = 0;
+ notice->z_authent_len = 0;
+ notice->z_ascii_authent = "";
+ notice->z_checksum = 0;
+ return (Z_FormatRawHeader(notice, buffer, buffer_len,
+ len, NULL, NULL));
+ }
+
+ return ((*cert_routine)(notice, buffer, buffer_len, len));
+}
+
+Code_t Z_FormatRawHeader(notice, buffer, buffer_len, len, cstart, cend)
+ ZNotice_t *notice;
+ char *buffer;
+ int buffer_len;
+ int *len;
+ char **cstart, **cend;
+{
+ char newrecip[BUFSIZ];
+ char *ptr, *end;
+ int i;
+
+ if (!notice->z_class)
+ notice->z_class = "";
+
+ if (!notice->z_class_inst)
+ notice->z_class_inst = "";
+
+ if (!notice->z_opcode)
+ notice->z_opcode = "";
+
+ if (!notice->z_recipient)
+ notice->z_recipient = "";
+
+ if (!notice->z_default_format)
+ notice->z_default_format = "";
+
+ ptr = buffer;
+ end = buffer+buffer_len;
+
+ if (buffer_len < strlen(notice->z_version)+1)
+ return (ZERR_HEADERLEN);
+
+ (void) strcpy(ptr, notice->z_version);
+ ptr += strlen(ptr)+1;
+
+ if (ZMakeAscii32(ptr, end-ptr, Z_NUMFIELDS + notice->z_num_other_fields)
+ == ZERR_FIELDLEN)
+ return (ZERR_HEADERLEN);
+ ptr += strlen(ptr)+1;
+
+ if (ZMakeAscii32(ptr, end-ptr, notice->z_kind) == ZERR_FIELDLEN)
+ return (ZERR_HEADERLEN);
+ ptr += strlen(ptr)+1;
+
+ if (ZMakeAscii(ptr, end-ptr, (unsigned char *)¬ice->z_uid,
+ sizeof(ZUnique_Id_t)) == ZERR_FIELDLEN)
+ return (ZERR_HEADERLEN);
+ ptr += strlen(ptr)+1;
+
+ if (ZMakeAscii16(ptr, end-ptr, ntohs(notice->z_port)) == ZERR_FIELDLEN)
+ return (ZERR_HEADERLEN);
+ ptr += strlen(ptr)+1;
+
+ if (ZMakeAscii32(ptr, end-ptr, notice->z_auth) == ZERR_FIELDLEN)
+ return (ZERR_HEADERLEN);
+ ptr += strlen(ptr)+1;
+
+ if (ZMakeAscii32(ptr, end-ptr, notice->z_authent_len) == ZERR_FIELDLEN)
+ return (ZERR_HEADERLEN);
+ ptr += strlen(ptr)+1;
+
+ if (Z_AddField(&ptr, notice->z_ascii_authent, end))
+ return (ZERR_HEADERLEN);
+ if (Z_AddField(&ptr, notice->z_class, end))
+ return (ZERR_HEADERLEN);
+ if (Z_AddField(&ptr, notice->z_class_inst, end))
+ return (ZERR_HEADERLEN);
+ if (Z_AddField(&ptr, notice->z_opcode, end))
+ return (ZERR_HEADERLEN);
+ if (Z_AddField(&ptr, notice->z_sender, end))
+ return (ZERR_HEADERLEN);
+ if (strchr(notice->z_recipient, '@') || !*notice->z_recipient) {
+ if (Z_AddField(&ptr, notice->z_recipient, end))
+ return (ZERR_HEADERLEN);
+ }
+ else {
+ if (strlen(notice->z_recipient) + strlen(__Zephyr_realm) + 2 >
+ sizeof(newrecip))
+ return (ZERR_HEADERLEN);
+ (void) sprintf(newrecip, "%s@%s", notice->z_recipient, __Zephyr_realm);
+ if (Z_AddField(&ptr, newrecip, end))
+ return (ZERR_HEADERLEN);
+ }
+ if (Z_AddField(&ptr, notice->z_default_format, end))
+ return (ZERR_HEADERLEN);
+
+ /* copy back the end pointer location for crypto checksum */
+ if (cstart)
+ *cstart = ptr;
+ if (ZMakeAscii32(ptr, end-ptr, notice->z_checksum) == ZERR_FIELDLEN)
+ return (ZERR_HEADERLEN);
+ ptr += strlen(ptr)+1;
+ if (cend)
+ *cend = ptr;
+
+ if (Z_AddField(&ptr, notice->z_multinotice, end))
+ return (ZERR_HEADERLEN);
+
+ if (ZMakeAscii(ptr, end-ptr, (unsigned char *)¬ice->z_multiuid,
+ sizeof(ZUnique_Id_t)) == ZERR_FIELDLEN)
+ return (ZERR_HEADERLEN);
+ ptr += strlen(ptr)+1;
+
+ for (i=0;i<notice->z_num_other_fields;i++)
+ if (Z_AddField(&ptr, notice->z_other_fields[i], end))
+ return (ZERR_HEADERLEN);
+
+ *len = ptr-buffer;
+
+ return (ZERR_NONE);
+}
+
+static int
+Z_AddField(ptr, field, end)
+ char **ptr, *field, *end;
+{
+ register int len;
+
+ len = field ? strlen (field) + 1 : 1;
+
+ if (*ptr+len > end)
+ return 1;
+ if (field)
+ (void) strcpy(*ptr, field);
+ else
+ **ptr = '\0';
+ *ptr += len;
+
+ return 0;
+}
+
+struct _Z_InputQ *Z_GetFirstComplete()
+{
+ struct _Z_InputQ *qptr;
+
+ qptr = __Q_Head;
+
+ while (qptr) {
+ if (qptr->complete)
+ return (qptr);
+ qptr = qptr->next;
+ }
+
+ return ((struct _Z_InputQ *)0);
+}
+
+struct _Z_InputQ *Z_GetNextComplete(qptr)
+ struct _Z_InputQ *qptr;
+{
+ qptr = qptr->next;
+ while (qptr) {
+ if (qptr->complete)
+ return (qptr);
+ qptr = qptr->next;
+ }
+
+ return ((struct _Z_InputQ *)0);
+}
+
+void Z_RemQueue(qptr)
+ struct _Z_InputQ *qptr;
+{
+ struct _Z_Hole *hole, *nexthole;
+
+ if (qptr->complete)
+ __Q_CompleteLength--;
+
+ __Q_Size -= qptr->msg_len;
+
+ if (qptr->header)
+ free(qptr->header);
+ if (qptr->msg)
+ free(qptr->msg);
+ if (qptr->packet)
+ free(qptr->packet);
+
+ hole = qptr->holelist;
+ while (hole) {
+ nexthole = hole->next;
+ free((char *)hole);
+ hole = nexthole;
+ }
+
+ if (qptr == __Q_Head && __Q_Head == __Q_Tail) {
+ free ((char *)qptr);
+ __Q_Head = (struct _Z_InputQ *)0;
+ __Q_Tail = (struct _Z_InputQ *)0;
+ return;
+ }
+
+ if (qptr == __Q_Head) {
+ __Q_Head = qptr->next;
+ __Q_Head->prev = (struct _Z_InputQ *)0;
+ free ((char *)qptr);
+ return;
+ }
+ if (qptr == __Q_Tail) {
+ __Q_Tail = qptr->prev;
+ __Q_Tail->next = (struct _Z_InputQ *)0;
+ free ((char *)qptr);
+ return;
+ }
+ qptr->prev->next = qptr->next;
+ qptr->next->prev = qptr->prev;
+ free ((char *)qptr);
+ return;
+}
+
+Code_t Z_SendFragmentedNotice(notice, len, cert_func, send_func)
+ ZNotice_t *notice;
+ int len;
+ Z_AuthProc cert_func;
+ Z_SendProc send_func;
+{
+ ZNotice_t partnotice;
+ ZPacket_t buffer;
+ char multi[64];
+ int offset, hdrsize, fragsize, ret_len, message_len, waitforack;
+ Code_t retval;
+
+ hdrsize = len-notice->z_message_len;
+ fragsize = Z_MAXPKTLEN-hdrsize-Z_FRAGFUDGE;
+
+ offset = 0;
+
+ waitforack = ((notice->z_kind == UNACKED || notice->z_kind == ACKED)
+ && !__Zephyr_server);
+
+ partnotice = *notice;
+
+ while (offset < notice->z_message_len || !notice->z_message_len) {
+ (void) sprintf(multi, "%d/%d", offset, notice->z_message_len);
+ partnotice.z_multinotice = multi;
+ if (offset > 0) {
+ (void) gettimeofday(&partnotice.z_uid.tv,
+ (struct timezone *)0);
+ partnotice.z_uid.tv.tv_sec =
+ htonl((u_long) partnotice.z_uid.tv.tv_sec);
+ partnotice.z_uid.tv.tv_usec =
+ htonl((u_long) partnotice.z_uid.tv.tv_usec);
+ (void) memcpy((char *)&partnotice.z_uid.zuid_addr, &__My_addr,
+ sizeof(__My_addr));
+ }
+ message_len = min(notice->z_message_len-offset, fragsize);
+ partnotice.z_message = notice->z_message+offset;
+ partnotice.z_message_len = message_len;
+ if ((retval = Z_FormatAuthHeader(&partnotice, buffer, Z_MAXHEADERLEN,
+ &ret_len, cert_func)) != ZERR_NONE) {
+ return (retval);
+ }
+ memcpy(buffer + ret_len, partnotice.z_message, message_len);
+ if ((retval = (*send_func)(&partnotice, buffer, ret_len+message_len,
+ waitforack)) != ZERR_NONE) {
+ return (retval);
+ }
+ offset += fragsize;
+ if (!notice->z_message_len)
+ break;
+ }
+
+ return (ZERR_NONE);
+}
+
+/*ARGSUSED*/
+Code_t Z_XmitFragment(notice, buf, len, wait)
+ZNotice_t *notice;
+char *buf;
+int len;
+int wait;
+{
+ return(ZSendPacket(buf, len, wait));
+}
+
+#ifdef Z_DEBUG
+/* For debugging printing */
+const char *const ZNoticeKinds[] = {
+ "UNSAFE", "UNACKED", "ACKED", "HMACK", "HMCTL", "SERVACK", "SERVNAK",
+ "CLIENTACK", "STAT"
+};
+#endif
+
+#ifdef Z_DEBUG
+
+#undef Z_debug
+#ifdef HAVE_STDARG_H
+void Z_debug (const char *format, ...)
+{
+ va_list pvar;
+ if (!__Z_debug_print)
+ return;
+ va_start (pvar, format);
+ (*__Z_debug_print) (format, pvar, __Z_debug_print_closure);
+ va_end (pvar);
+}
+#else /* stdarg */
+void Z_debug (va_alist) va_dcl
+{
+ va_list pvar;
+ char *format;
+ if (!__Z_debug_print)
+ return;
+ va_start (pvar);
+ format = va_arg (pvar, char *);
+ (*__Z_debug_print) (format, pvar, __Z_debug_print_closure);
+ va_end (pvar);
+}
+#endif
+
+void Z_debug_stderr (format, args, closure)
+ const char *format;
+ va_list args;
+ void *closure;
+{
+#ifdef HAVE_VPRINTF
+ vfprintf (stderr, format, args);
+#else
+ _doprnt (format, args, stderr);
+#endif
+ putc ('\n', stderr);
+}
+
+#undef ZGetFD
+int ZGetFD () { return __Zephyr_fd; }
+
+#undef ZQLength
+int ZQLength () { return __Q_CompleteLength; }
+
+#undef ZGetDestAddr
+struct sockaddr_in ZGetDestAddr () { return __HM_addr; }
+
+#undef ZGetRealm
+Zconst char * ZGetRealm () { return __Zephyr_realm; }
+
+#undef ZSetDebug
+void ZSetDebug(proc, arg)
+ void (*proc) __P((const char *, va_list, void *));
+ char *arg;
+{
+ __Z_debug_print = proc;
+ __Z_debug_print_closure = arg;
+}
+#endif /* Z_DEBUG */
+
--- /dev/null
+/*
+
+Copyright 1987,1988 by the Massachusetts Institute of Technology
+
+All rights reserved.
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of the Massachusetts
+Institute of Technology (M.I.T.) not be used in advertising or publicity
+pertaining to distribution of the software without specific, written
+prior permission.
+
+M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+*/
--- /dev/null
+/* Copyright (c) 1988 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ */
+#include <zephyr/zephyr.h>
+
+main()
+{
+ FILE *fp;
+ char buf[512],*ptr;
+ int auth,retval;
+ u_short port;
+ ZNotice_t notice;
+ ZSubscription_t sub;
+ struct sockaddr_in from;
+
+ if ((retval = ZInitialize()) != ZERR_NONE) {
+ com_err("foo",retval,"initing");
+ exit(1);
+ }
+
+ port = 0;
+ if ((retval = ZOpenPort(&port)) != ZERR_NONE) {
+ com_err("foo",retval,"opening port");
+ exit(1);
+ }
+ printf("Using port %d\n",(int)port);
+ sprintf(buf,"/tmp/wg.%d",getuid());
+ fp = fopen(buf,"w");
+ if (!fp) {
+ com_err("foo",errno,"opening file");
+ exit(1);
+ }
+ fprintf(fp,"%d\n",(int)port);
+ fclose(fp);
+
+ printf("All ready...\n");
+
+ sub.class = "MESSAGE";
+ sub.classinst = "PERSONAL";
+ sub.recipient = ZGetSender();
+
+ if ((retval = ZSubscribeTo(&sub,1,port)) != ZERR_NONE) {
+ com_err("foo",retval,"subscribing");
+ exit(1);
+ }
+ for (;;) {
+ if ((retval = ZReceiveNotice(¬ice,&from)) != ZERR_NONE) {
+ com_err("foo",retval,"receiving packet");
+ continue;
+ }
+ auth = ZCheckAuthentication(¬ice,&from);
+ printf("Class = %s Instance = %s Sender = %s\nTime = %s Auth = %d\n",
+ notice.z_class,notice.z_class_inst,notice.z_sender,
+ ctime(¬ice.z_time.tv_sec),auth);
+ printf("Len = %d\n",notice.z_message_len);
+/* ptr = notice.z_message;
+ for (;ptr<notice.z_message+notice.z_message_len;) {
+ printf("%s\n",ptr);
+ ptr += strlen(ptr)+1;
+ }
+ printf("\n");*/
+ ZFreeNotice(¬ice);
+ }
+}
--- /dev/null
+.\" $Id: zephyr.1,v 1.12 1999/01/22 23:19:33 ghudson Exp $
+.\"
+.\" Copyright 1987,1988 by the Massachusetts Institute of Technology
+.\" All rights reserved. The file /usr/include/zephyr/mit-copyright.h
+.\" specifies the terms and conditions for redistribution.
+.\"
+.TH ZEPHYR 1 "July 1, 1988" "MIT Project Athena"
+.ds ]W MIT Project Athena
+.SH NAME
+zephyr \- Zephyr Notification Service
+.SH DESCRIPTION
+.PP
+Zephyr is a notice transport and delivery system developed at MIT
+which runs under 4.3BSD Unix.
+.PP
+A Notice Transport and Delivery system is a method of getting small
+quantities of time sensitive information efficiently from one client
+(or server) on a network to another. The object is to accomplish this
+with the highest possible fan-out (i.e., client to server ratio) while
+maintaining both network and server performance. Zephyr is a
+multi-cast notice transport and delivery system based upon an
+authenticated datagram protocol. Localized Zephyr servers provide
+routing, queueing and dispatching services to clients which
+communicate with them via the Zephyr Client Library. Two special
+purpose Zephyr clients, the WindowGram client and the HostManager
+client provide user and client host communication support.
+.PP
+.I Zephyrd(8)
+servers run on designated server machines. These servers maintain a
+database of subscriptions and locations for every user using Zephyr.
+The servers stay in contact with one another, and provide a reliable
+backup system (via duplication) in the event of network failures.
+.PP
+Each client machine on the network runs a
+.I zhm(8)
+HostManager client program which is the link between the Zephyr
+servers and the users. User programs send notices to the HostManager,
+and the HostManager forwards these notices to the nearest server for
+action. The HostManager is responsible for ensuring that the notices
+reach a server, and for finding a new one if its server fails to respond.
+.PP
+Each user on the network usually runs a WindowGram client program
+automatically upon login.
+.I Zwgc(1)
+displays notices to the user, and handles user
+responses. Only notices to which the user has subscribed will be sent
+to the WindowGram client.
+.PP
+Subscriptions are handled through the
+.I zctl(1)
+program. This program allows the user to add or delete subscriptions
+from Zephyr, to add the subscriptions to a file, and to perform other
+miscellaneous functions.
+.SH SEE ALSO
+kerberosintro(1)
+.br
+zaway(1), zctl(1), zleave(1), zlocate(1), znol(1), zwgc(1),
+zwrite(1), zmailnotify(1)
+.br
+zhm(8), zephyrd(8), zinit(8), zstat(8), zpopnotify(8),
+zshutdown_notify(8), syslogd(8)
+.br
+Project Athena Technical Plan Section E.4.1, `Zephyr Notification
+Service'
+.SH AUTHORS
+.PP
+.br
+Tony Della Fera (MIT-Project Athena, DEC),
+Mark W. Eichin (MIT-Project Athena),
+Robert S. French (MIT-Project Athena),
+David C. Jedlinsky (MIT-Project Athena),
+John T. Kohl (MIT-Project Athena, DEC),
+William E. Sommerfeld (MIT-Project Athena).
+.SH RESTRICTIONS
+Copyright (c) 1987,1988,1989 by the Massachusetts Institute of Technology.
+All Rights Reserved.
+.br
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of the Massachusetts
+Institute of Technology not be used in advertising or publicity
+pertaining to distribution of the software without specific, written
+prior permission.
+.br
+M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
--- /dev/null
+# Copyright 1987,1988 Massachusetts Institute of Technology
+#
+# For copying and distribution information, see the file
+# "mit-copyright.h".
+#
+# $Id: zephyr_err.et,v 1.10 1999/01/22 23:19:34 ghudson Exp $
+#
+ et zeph
+
+ec ZERR_PKTLEN,
+ "Packet too long or buffer too small"
+ec ZERR_HEADERLEN,
+ "Notice header too large"
+ec ZERR_ILLVAL,
+ "Illegal value in notice"
+ec ZERR_HMPORT,
+ "Can't get host manager port"
+ec ZERR_PORTINUSE,
+ "Can't assign port"
+ec ZERR_BADPKT,
+ "Bad packet format"
+ec ZERR_VERS,
+ "Incompatible version numbers"
+ec ZERR_NOPORT,
+ "No port opened"
+ec ZERR_NONOTICE,
+ "No notices match criteria"
+ec ZERR_QLEN,
+ "Input queue too long"
+ec ZERR_HMDEAD,
+ "Hostmanager not responding"
+ec ZERR_INTERNAL,
+ "Internal error"
+ec ZERR_NOLOCATIONS,
+ "No previous call to ZLocateUser"
+ec ZERR_NOMORELOCS,
+ "No more locations available"
+ec ZERR_FIELDLEN,
+ "Field too long for buffer"
+ec ZERR_BADFIELD,
+ "Improperly formatted field"
+ec ZERR_SERVNAK,
+ "SERVNAK received"
+ec ZERR_AUTHFAIL,
+ "Server could not verify authentication"
+ec ZERR_LOGINFAIL,
+ "Not logged-in"
+ec ZERR_NOSUBSCRIPTIONS,
+ "No previous call to ZRetrieveSubscriptions"
+ec ZERR_NOMORESUBSCRIPTIONS,
+ "No more subscriptions available"
+ec ZERR_TOOMANYSUBS,
+ "Too many subscriptions to transmit"
+ec ZERR_EOF,
+ "End of file detected during read"
+
+ end
--- /dev/null
+SHELL = /bin/sh
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+datadir=@datadir@
+sysconfdir=@sysconfdir@
+sbindir=@sbindir@
+lsbindir=@lsbindir@
+
+includedir=${prefix}/include
+mandir=${prefix}/man
+libdir=${exec_prefix}/lib
+
+srcdir=@srcdir@
+top_srcdir=@top_srcdir@
+BUILDTOP=..
+VPATH=@srcdir@
+CC=@CC@
+RANLIB=@RANLIB@
+
+CPPFLAGS=@CPPFLAGS@
+CFLAGS=@CFLAGS@
+ALL_CFLAGS=${CFLAGS} -I${top_srcdir}/h -I${BUILDTOP}/h ${CPPFLAGS}
+
+OBJS = dyn_append.o dyn_create.o dyn_debug.o dyn_delete.o dyn_insert.o \
+ dyn_paranoid.o dyn_put.o dyn_realloc.o dyn_size.o
+
+all: libdyn.a dyntest
+
+libdyn.a: ${OBJS}
+ ar cru $@ ${OBJS}
+ ${RANLIB} $@
+
+dyntest: libdyn.a dyntest.o
+ ${CC} ${LDFLAGS} -o $@ dyntest.o libdyn.a
+
+.c.o:
+ ${CC} -c ${ALL_CFLAGS} $<
+
+check:
+
+install:
+
+clean:
+ rm -f ${OBJS} libdyn.a dyntest.o dyntest
+
+${OBJS} dyntest.o: dynP.h ${top_srcdir}/h/dyn.h ${top_srcdir}/h/sysdep.h
+${OBJS} dyntest.o: ${BUILDTOP}/h/config.h
+
+.PHONY: all check install clean
+
--- /dev/null
+libdyn.a -- Release 1.0
+
+A C Dynamic Object is an array that takes care of resizing itself as
+elements are added and deleted from it. It can be of any type for
+which sizeof is defined and for which an address of a variable of that
+type can be passed to a function.
+
+To build libdyn.a, simply type "make depend all" (if you don't have
+the program makedepend, of course, leave out the "depend" part). If
+your system's bcopy() cannot handle overlapping regions, you'll need
+to write one that can. (Left as an excercise for the reader..)
+
+The library should compile and work without modification on a vast
+number of systems. It only uses 5 external functions: malloc,
+realloc, free, bcopy, and fprintf (to stderr). Of these, only bcopy
+should need to be changed for other systems (such as MS-DOS) and it
+could probably be done with a -D flag to the compiler.
+
+The test/demo program is built by "make all". This program produces
+the library's debugging output (to stderr) as well as some of its own
+output (to stdout).
+
+The library has been tested (with test.c) on a VAX VSII, VAXstation
+3100, DECstation 3100, and IBM RT all running BSD4.3 (except for the
+DECstation, which was running Ultrix V2.1).
+
+An earlier version of this library was posted to alt.sources. This
+version contains one new function (DynInsert) and slightly cleaner
+code, but no bugfixes (no bugs were found).
+
+Author: Barr3y Jaspan, Student Information Processing Board (SIPB) and
+MIT-Project Athena, bjaspan@athena.mit.edu, 1990
--- /dev/null
+.TH DYN 3M "15 March 1990"
+
+.SH NAME
+dyn \- the C Dynamic Object library
+
+.SH DESCRIPTION
+
+A C Dynamic Object is an array that takes care of resizing
+itself as you add and delete elements from it. It can be of any type
+for which sizeof is defined and for which an address of a variable of
+that type can be passed to a function. The library containing the
+functions described below is called
+.IR libdyn.a ,
+and the necessary declarations to use them are in
+.RI < dyn.h >.
+.PP
+A DynObject is actually a structure that contains an array and a
+couple of integers to maintain necessary state information. When a
+Dyn function is said to operate on "the object" or "the array", it is
+operating on the array stored in the structure while at the same time
+updating internal state information.
+
+.SH LIST OF FUNCTIONS
+.nf
+DynObject DynCreate(size, increment)
+ int size, increment;
+.fi
+.PP
+.IR Requires :
+.I size
+and
+.I increment
+are greater than zero.
+.PP
+.IR Effects :
+Creates a new DynObject that will store elements of size
+.I size
+and will allocate memory in blocks large enough to hold exactly
+.I increment
+elements. For example, if you are storing 8-byte double
+precision numbers and
+.I increment
+is 5, each 5th element you add to the object will cause it to request
+40 more bytes (8 * 5) from the operating system. If
+.I increment
+is zero, a default value is used (currently 100). This is the only
+time the programmer deals with a dynamic object's memory allocation.
+.PP
+.IR Returns :
+.B DynCreate
+returns the new DynObject, or NULL if there is insufficient memory.
+.PP
+.nf
+int DynDestroy(obj)
+ DynObject obj;
+.fi
+.PP
+.IR Modifies :
+obj
+.PP
+.IR Effects :
+Frees all memory associated with
+.IR obj .
+The results of calling any Dyn function on a destroyed object are
+undefined (except for DynCreate, which resets the object).
+.PP
+.IR Returns :
+.B DynDestroy
+returns DYN_OK.
+.PP
+.nf
+int DynAdd(obj, el)
+ DynObject obj;
+ DynPtr el;
+.fi
+.PP
+.IR Modifies :
+obj
+.PP
+.IR Effects :
+Adds the element pointed to by
+.I el
+to the object
+.IR obj ,
+resizing the object if necessary.
+The new element becomes the last element in obj's array.
+.PP
+.IR Returns :
+.B DynAdd
+returns DYN_OK on success or DYN_NOMEM if there is insufficient
+memory.
+.PP
+.nf
+int DynInsert(obj, index, els, num)
+ DynObject obj;
+ DynPtr els;
+ int index, num;
+.fi
+.PP
+.IR Modifies :
+obj
+.PP
+.IR Effects :
+Inserts the array of
+.I num
+elements, pointed to by
+.IR els,
+into the object
+.I obj
+starting at the array location
+.IR index ,
+resizing the object if necessary. Order is preserved; if you have the
+array "1 2 3 4 5" and insert "10 11 12" at the third position, you
+will have the array "1 2 10 11 12 3 4 5".
+.PP
+.IR Returns :
+.B DynInsert
+returns DYN_BADINDEX if
+.I index
+is not between 0 and
+.BR DynSize ( obj ) ;
+DYN_BADVALUE if
+.I num
+is less than 1; DYN_NOMEM if there is insufficient memory.
+.PP
+.nf
+int DynGet(obj, index)
+ DynObject obj;
+ int index;
+.fi
+.PP
+.IR Effects :
+Returns the address of the element
+.I index
+in the array of
+.IR obj .
+This pointer can be treated as a normal array of the type specified to
+.BR DynCreate .
+The order of elements in this array is the order in which they were
+added to the object. The returned pointer is guaranteed to be valid
+only until obj is modified.
+.PP
+.IR Returns :
+.B DynGet
+returns NULL if
+.I index
+is larger than the number of elements in the array of less than zero.
+.PP
+.nf
+int DynDelete(obj, index)
+ DynObject obj;
+ int index;
+.fi
+.PP
+.IR Modifies :
+obj
+.PP
+.IR Effects :
+The element
+.I index
+is deleted from the object
+.IR obj .
+Note that the element is actually removed permanently from the array.
+If you have the array "1 2 3 4 5" and delete the third element, you
+will have the array "1 2 4 5". The order of elements in not affected.
+.PP
+.IR Returns :
+.B DynDelete
+will return DYN_OK on success or DYN_BADINDEX if the element
+.I index
+does not exist in the array or is less than zero.
+.PP
+.nf
+int DynSize(obj)
+ DynObject obj;
+.fi
+.PP
+.IR Effects :
+Returns the number of elements in the object
+.IR obj .
+.PP
+.nf
+int DynHigh(obj)
+ DynObject obj;
+.fi
+.PP
+.IR Effects :
+Returns the index of the highest element in the object
+.IR obj .
+In this version,
+.B DynHigh
+is macro that expands to
+.B DynSize
+- 1.
+.PP
+.nf
+int DynLow(obj)
+ DynObject obj;
+.fi
+.PP
+.IR Effects :
+Returns the index of the lowest element in the object
+.IR obj .
+In this version,
+.B DynLow
+is macro that expands to 0.
+.PP
+.nf
+int DynDebug(obj, state)
+ DynObject obj;
+ int state;
+.fi
+.PP
+.IR Modifies :
+obj
+.PP
+.IR Effects :
+Sets the debugging state of
+.I obj
+to
+.I state
+and prints a message on stderr saying what state debugging was set to.
+Any non-zero value for
+.I state
+turns debugging ``on''. When debugging is on, all Dyn functions will
+produce (hopefully useful) output to stderr describing what is going on.
+.PP
+.IR Returns :
+.B DynDebug
+returns DYN_OK.
+.SH AUTHOR
+Barr3y Jaspan, Student Information Processing Board (SIPB) and
+MIT-Project Athena, bjaspan@athena.mit.edu
--- /dev/null
+/*
+ * This file is part of libdyn.a, the C Dynamic Object library. It
+ * contains the private header file.
+ *
+ * There are no restrictions on this code; however, if you make any
+ * changes, I request that you document them so that I do not get
+ * credit or blame for your modifications.
+ *
+ * Written by Barr3y Jaspan, Student Information Processing Board (SIPB)
+ * and MIT-Project Athena, 1989.
+ */
+
+
+/*
+ * dynP.h -- private header file included by source files for libdyn.a.
+ */
+
+#ifndef _DynP_h
+#define _DynP_h
+
+#include <sysdep.h>
+#include <dyn.h>
+
+/*
+ * Rep invariant:
+ * 1) el_size is the number of bytes per element in the object
+ * 2) num_el is the number of elements currently in the object. It is
+ * one higher than the highest index at which an element lives.
+ * 3) size is the number of elements the object can hold without
+ * resizing. num_el <= index.
+ * 4) inc is a multiple of the number of elements the object grows by
+ * each time it is reallocated.
+ */
+
+typedef struct _DynObject {
+ DynPtr array;
+ int el_size, num_el, size, inc;
+ char debug, paranoid;
+} DynObjectRec;
+
+/* Internal functions */
+int _DynRealloc __P((DynObject obj, int num_incs));
+
+#define _DynResize(obj, req) \
+ ((obj)->size > (req) ? DYN_OK : \
+ (_DynRealloc((obj), (((req) - (obj)->size) / (obj)->inc) + 1)))
+
+#endif /* _DynP_h */
+/* DON'T ADD STUFF AFTER THIS #endif */
--- /dev/null
+/*
+ * This file is part of libdyn.a, the C Dynamic Object library. It
+ * contains the source code for the function DynAppend().
+ *
+ * There are no restrictions on this code; however, if you make any
+ * changes, I request that you document them so that I do not get
+ * credit or blame for your modifications.
+ *
+ * Written by Barr3y Jaspan, Student Information Processing Board (SIPB)
+ * and MIT-Project Athena, 1989.
+ */
+
+#include <stdio.h>
+
+#include "dynP.h"
+
+int DynAppend(obj, els, num)
+ DynObject obj;
+ DynPtr els;
+ int num;
+{
+ if (obj->debug)
+ fprintf(stderr, "dyn: append: Writing %d bytes from %p to %p + %d\n",
+ obj->el_size*num, els, obj->array, obj->num_el*obj->el_size);
+
+ if (obj->size < obj->num_el + num) {
+ int num_incs, ret;
+
+ num_incs = ((obj->num_el + num - obj->size) / obj->inc) + 1;
+ if ((ret = _DynRealloc(obj, num_incs)) != DYN_OK)
+ return ret;
+ }
+
+ (void) memmove(obj->array + obj->num_el*obj->el_size, els,
+ obj->el_size*num);
+
+ obj->num_el += num;
+
+ if (obj->debug)
+ fprintf(stderr, "dyn: append: done.\n");
+
+ return DYN_OK;
+}
+
--- /dev/null
+/*
+ * This file is part of libdyn.a, the C Dynamic Object library. It
+ * contains the source code for the functions DynCreate() and
+ * DynDestroy().
+ *
+ * There are no restrictions on this code; however, if you make any
+ * changes, I request that you document them so that I do not get
+ * credit or blame for your modifications.
+ *
+ * Written by Barr3y Jaspan, Student Information Processing Board (SIPB)
+ * and MIT-Project Athena, 1989.
+ */
+
+#include <stdio.h>
+
+#include "dynP.h"
+
+#ifndef DEFAULT_INC
+#define DEFAULT_INC 100
+#endif
+
+static int default_increment = DEFAULT_INC;
+
+DynObject DynCreate(el_size, inc)
+ int el_size, inc;
+{
+ DynObject obj;
+
+ obj = (DynObject) malloc(sizeof(DynObjectRec));
+ if (obj == NULL)
+ return NULL;
+
+ obj->array = (DynPtr) malloc(0);
+ obj->el_size = el_size;
+ obj->num_el = obj->size = 0;
+ obj->debug = obj->paranoid = 0;
+ obj->inc = (!! inc) ? inc : default_increment;
+
+ return obj;
+}
+
+int DynDestroy(obj)
+ DynObject obj;
+{
+ free(obj->array);
+ free(obj);
+ return DYN_OK;
+}
--- /dev/null
+/*
+ * This file is part of libdyn.a, the C Dynamic Object library. It
+ * contains the source code for the function DynDebug().
+ *
+ * There are no restrictions on this code; however, if you make any
+ * changes, I request that you document them so that I do not get
+ * credit or blame for your modifications.
+ *
+ * Written by Barr3y Jaspan, Student Information Processing Board (SIPB)
+ * and MIT-Project Athena, 1989.
+ */
+
+#include <stdio.h>
+
+#include "dynP.h"
+
+int DynDebug(obj, state)
+ DynObject obj;
+ int state;
+{
+ obj->debug = state;
+
+ fprintf(stderr, "dyn: debug: Debug state set to %d.\n", state);
+ return DYN_OK;
+}
--- /dev/null
+/*
+ * This file is part of libdyn.a, the C Dynamic Object library. It
+ * contains the source code for the function DynDelete().
+ *
+ * There are no restrictions on this code; however, if you make any
+ * changes, I request that you document them so that I do not get
+ * credit or blame for your modifications.
+ *
+ * Written by Barr3y Jaspan, Student Information Processing Board (SIPB)
+ * and MIT-Project Athena, 1989.
+ */
+
+#include <stdio.h>
+
+#include "dynP.h"
+
+/*
+ * Checkers! Get away from that "hard disk erase" button!
+ * (Stupid dog. He almost did it to me again ...)
+ */
+int DynDelete(obj, idx)
+ DynObject obj;
+ int idx;
+{
+ if (idx < 0) {
+ if (obj->debug)
+ fprintf(stderr, "dyn: delete: bad index %d\n", idx);
+ return DYN_BADINDEX;
+ }
+
+ if (idx >= obj->num_el) {
+ if (obj->debug)
+ fprintf(stderr, "dyn: delete: Highest index is %d.\n",
+ obj->num_el);
+ return DYN_BADINDEX;
+ }
+
+ if (idx == obj->num_el-1) {
+ if (obj->paranoid) {
+ if (obj->debug)
+ fprintf(stderr, "dyn: delete: last element, zeroing.\n");
+ (void) memset(obj->array + idx*obj->el_size, 0, obj->el_size);
+ }
+ else {
+ if (obj->debug)
+ fprintf(stderr, "dyn: delete: last element, punting.\n");
+ }
+ }
+ else {
+ if (obj->debug)
+ fprintf(stderr,
+ "dyn: delete: copying %d bytes from %p + %d to + %d.\n",
+ obj->el_size*(obj->num_el - idx), obj->array,
+ (idx+1)*obj->el_size, idx*obj->el_size);
+
+ (void) memmove(obj->array + idx*obj->el_size,
+ obj->array + (idx+1)*obj->el_size,
+ obj->el_size*(obj->num_el - idx));
+
+ if (obj->paranoid) {
+ if (obj->debug)
+ fprintf(stderr,
+ "dyn: delete: zeroing %d bytes from %p + %d\n",
+ obj->el_size, obj->array,
+ obj->el_size*(obj->num_el - 1));
+ (void) memset(obj->array + obj->el_size*(obj->num_el - 1),
+ 0, obj->el_size);
+ }
+ }
+
+ --obj->num_el;
+
+ if (obj->debug)
+ fprintf(stderr, "dyn: delete: done.\n");
+
+ return DYN_OK;
+}
--- /dev/null
+/*
+ * This file is part of libdyn.a, the C Dynamic Object library. It
+ * contains the source code for the function xxx.
+ *
+ * There are no restrictions on this code; however, if you make any
+ * changes, I request that you document them so that I do not get
+ * credit or blame for your modifications.
+ *
+ * Written by Barr3y Jaspan, Student Information Processing Board (SIPB)
+ * and MIT-Project Athena, 1989.
+ */
--- /dev/null
+/*
+ * This file is part of libdyn.a, the C Dynamic Object library. It
+ * contains the source code for the function DynInsert().
+ *
+ * There are no restrictions on this code; however, if you make any
+ * changes, I request that you document them so that I do not get
+ * credit or blame for your modifications.
+ *
+ * Written by Barr3y Jaspan, Student Information Processing Board (SIPB)
+ * and MIT-Project Athena, 1989.
+ */
+
+#include "dynP.h"
+
+int DynInsert(obj, idx, els, num)
+ DynObject obj;
+ DynPtr els;
+ int idx, num;
+{
+ int ret;
+
+ if (idx < 0 || idx > obj->num_el) {
+ if (obj->debug)
+ fprintf(stderr, "dyn: insert: index %d is not in [0,%d]\n",
+ idx, obj->num_el);
+ return DYN_BADINDEX;
+ }
+
+ if (num < 1) {
+ if (obj->debug)
+ fprintf(stderr, "dyn: insert: cannot insert %d elements\n",
+ num);
+ return DYN_BADVALUE;
+ }
+
+ if (obj->debug)
+ fprintf(stderr,"dyn: insert: Moving %d bytes from %p + %d to + %d\n",
+ (obj->num_el-idx)*obj->el_size, obj->array,
+ obj->el_size*idx, obj->el_size*(idx+num));
+
+ if ((ret = _DynResize(obj, obj->num_el + num)) != DYN_OK)
+ return ret;
+
+ (void) memmove(obj->array + (idx + num), obj->array + idx,
+ (obj->num_el-idx)*obj->el_size);
+
+ if (obj->debug)
+ fprintf(stderr, "dyn: insert: Copying %d bytes from %p to %p + %d\n",
+ obj->el_size*num, els, obj->array, obj->el_size*idx);
+
+ (void) memmove(obj->array + obj->el_size*idx, els, obj->el_size*num);
+
+ obj->num_el += num;
+
+ if (obj->debug)
+ fprintf(stderr, "dyn: insert: done.\n");
+
+ return DYN_OK;
+}
--- /dev/null
+/*
+ * This file is part of libdyn.a, the C Dynamic Object library. It
+ * contains the source code for the function DynDebug().
+ *
+ * There are no restrictions on this code; however, if you make any
+ * changes, I request that you document them so that I do not get
+ * credit or blame for your modifications.
+ *
+ * Written by Barr3y Jaspan, Student Information Processing Board (SIPB)
+ * and MIT-Project Athena, 1989.
+ */
+
+#include <stdio.h>
+
+#include "dynP.h"
+
+int DynParanoid(obj, state)
+ DynObject obj;
+ int state;
+{
+ obj->paranoid = state;
+
+ if (obj->debug)
+ fprintf(stderr, "dyn: paranoid: Paranoia set to %d.\n", state);
+ return DYN_OK;
+}
--- /dev/null
+/*
+ * This file is part of libdyn.a, the C Dynamic Object library. It
+ * contains the source code for the functions DynGet() and DynAdd().
+ *
+ * There are no restrictions on this code; however, if you make any
+ * changes, I request that you document them so that I do not get
+ * credit or blame for your modifications.
+ *
+ * Written by Barr3y Jaspan, Student Information Processing Board (SIPB)
+ * and MIT-Project Athena, 1989.
+ */
+
+#include "dynP.h"
+
+static int DynPut __P((DynObject obj, DynPtr el, int index));
+
+DynPtr DynGet(obj, num)
+ DynObject obj;
+ int num;
+{
+ if (num < 0) {
+ if (obj->debug)
+ fprintf(stderr, "dyn: get: bad index %d\n", num);
+ return NULL;
+ }
+
+ if (num >= obj->num_el) {
+ if (obj->debug)
+ fprintf(stderr, "dyn: get: highest element is %d.\n",
+ obj->num_el);
+ return NULL;
+ }
+
+ if (obj->debug)
+ fprintf(stderr, "dyn: get: Returning address %p + %d.\n",
+ obj->array, obj->el_size*num);
+
+ return (DynPtr) obj->array + obj->el_size*num;
+}
+
+int DynAdd(obj, el)
+ DynObject obj;
+ DynPtr el;
+{
+ int ret;
+
+ ret = DynPut(obj, el, obj->num_el);
+ if (ret != DYN_OK)
+ return ret;
+
+ ++obj->num_el;
+ return ret;
+}
+
+/*
+ * WARNING! There is a reason this function is not documented in the
+ * man page. If DynPut used to mutate already existing elements,
+ * everything will go fine. If it is used to add new elements
+ * directly, however, the state within the object (such as
+ * obj->num_el) will not be updated properly and many other functions
+ * in the library will lose. Have a nice day.
+ */
+static int DynPut(obj, el, index)
+ DynObject obj;
+ DynPtr el;
+ int index;
+{
+ int ret;
+
+ if (obj->debug)
+ fprintf(stderr, "dyn: put: Writing %d bytes from %p to %p + %d\n",
+ obj->el_size, el, obj->array, index*obj->el_size);
+
+ if ((ret = _DynResize(obj, index)) != DYN_OK)
+ return ret;
+
+ (void) memmove(obj->array + index*obj->el_size, el, obj->el_size);
+
+ if (obj->debug)
+ fprintf(stderr, "dyn: put: done.\n");
+
+ return DYN_OK;
+}
--- /dev/null
+/*
+ * This file is part of libdyn.a, the C Dynamic Object library. It
+ * contains the source code for the internal function _DynRealloc().
+ *
+ * There are no restrictions on this code; however, if you make any
+ * changes, I request that you document them so that I do not get
+ * credit or blame for your modifications.
+ *
+ * Written by Barr3y Jaspan, Student Information Processing Board (SIPB)
+ * and MIT-Project Athena, 1989.
+ */
+
+#include <stdio.h>
+
+#include "dynP.h"
+
+/*
+ * Ideally, this function should not be called from outside the
+ * library. However, nothing will break if it is.
+ */
+int _DynRealloc(obj, num_incs)
+ DynObject obj;
+ int num_incs;
+{
+ DynPtr temp;
+ int new_size_in_bytes;
+
+ new_size_in_bytes = obj->el_size*(obj->size + obj->inc*num_incs);
+
+ if (obj->debug)
+ fprintf(stderr,
+ "dyn: alloc: Increasing object by %d bytes (%d incs).\n",
+ obj->el_size*obj->inc*num_incs, num_incs);
+
+ temp = (DynPtr) realloc(obj->array, new_size_in_bytes);
+ if (temp == NULL) {
+ if (obj->debug)
+ fprintf(stderr, "dyn: alloc: Out of memory.\n");
+ return DYN_NOMEM;
+ }
+ else {
+ obj->array = temp;
+ obj->size += obj->inc*num_incs;
+ }
+
+ if (obj->debug)
+ fprintf(stderr, "dyn: alloc: done.\n");
+
+ return DYN_OK;
+}
--- /dev/null
+/*
+ * This file is part of libdyn.a, the C Dynamic Object library. It
+ * contains the source code for the function DynSize().
+ *
+ * There are no restrictions on this code; however, if you make any
+ * changes, I request that you document them so that I do not get
+ * credit or blame for your modifications.
+ *
+ * Written by Barr3y Jaspan, Student Information Processing Board (SIPB)
+ * and MIT-Project Athena, 1989.
+ */
+
+#include <stdio.h>
+
+#include "dynP.h"
+
+int DynSize(obj)
+ DynObject obj;
+{
+ if (obj->debug)
+ fprintf(stderr, "dyn: size: returning size %d.\n", obj->num_el);
+
+ return obj->num_el;
+}
--- /dev/null
+/*
+ * This file is a (rather silly) demonstration of the use of the
+ * C Dynamic Object library. It is a also reasonably thorough test
+ * of the library (except that it only tests it with one data size).
+ *
+ * There are no restrictions on this code; however, if you make any
+ * changes, I request that you document them so that I do not get
+ * credit or blame for your modifications.
+ *
+ * Written by Barr3y Jaspan, Student Information Processing Board (SIPB)
+ * and MIT-Project Athena, 1989.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "dyn.h"
+
+static char random_string[] = "This is a random string.";
+static char insert1[] = "This will be put at the beginning.";
+static char insert2[] = "(parenthetical remark!) ";
+static char insert3[] = " This follows the random string.";
+
+int main(argc, argv)
+ int argc;
+ char **argv;
+{
+ DynObject obj;
+ int i, s;
+ char d, *data;
+
+ obj = DynCreate(sizeof(char), 8);
+ if (! obj) {
+ fprintf(stderr, "test: create failed.\n");
+ exit(1);
+ }
+
+ DynDebug(obj, 1);
+ DynParanoid(obj, 1);
+
+ if (DynGet(obj, -5) || DynGet(obj, 0) || DynGet(obj, 1000)) {
+ fprintf(stderr, "test: Get did not fail when it should have.\n");
+ exit(1);
+ }
+
+ if (DynDelete(obj, -1) != DYN_BADINDEX ||
+ DynDelete(obj, 0) != DYN_BADINDEX ||
+ DynDelete(obj, 100) != DYN_BADINDEX) {
+ fprintf(stderr, "test: Delete did not fail when it should have.\n");
+ exit(1);
+ }
+
+ printf("Size of empty object: %d\n", DynSize(obj));
+
+ for (i=0; i<14; i++) {
+ d = (char) i;
+ if (DynAdd(obj, &d) != DYN_OK) {
+ fprintf(stderr, "test: Adding %d failed.\n", i);
+ exit(1);
+ }
+ }
+
+ if (DynAppend(obj, random_string, strlen(random_string)+1) != DYN_OK) {
+ fprintf(stderr, "test: appending array failed.\n");
+ exit(1);
+ }
+
+ if (DynDelete(obj, DynHigh(obj) / 2) != DYN_OK) {
+ fprintf(stderr, "test: deleting element failed.\n");
+ exit(1);
+ }
+
+ if (DynDelete(obj, DynHigh(obj) * 2) == DYN_OK) {
+ fprintf(stderr, "test: delete should have failed here.\n");
+ exit(1);
+ }
+
+ d = 200;
+ if (DynAdd(obj, &d) != DYN_OK) {
+ fprintf(stderr, "test: Adding %d failed.\n", i);
+ exit(1);
+ }
+
+ data = (char *) DynGet(obj, 0);
+ s = DynSize(obj);
+ for (i=0; i < s; i++)
+ printf("Element %d is %d.\n", i, (unsigned char) data[i]);
+
+ data = (char *) DynGet(obj, 13);
+ printf("Element 13 is %d.\n", (unsigned char) *data);
+
+ data = (char *) DynGet(obj, DynSize(obj));
+ if (data) {
+ fprintf(stderr, "DynGet did not return NULL when it should have.\n");
+ exit(1);
+ }
+
+ printf("This should be the random string: \"%s\"\n",
+ (char *) DynGet(obj, 14));
+
+ if (DynInsert(obj, -1, "foo", 4) != DYN_BADINDEX ||
+ DynInsert(obj, DynSize(obj) + 1, "foo", 4) != DYN_BADINDEX ||
+ DynInsert(obj, 0, "foo", -1) != DYN_BADVALUE) {
+ fprintf(stderr, "DynInsert did not fail when it should have.\n");
+ exit(1);
+ }
+
+ if (DynInsert(obj, DynSize(obj) - 2, insert3, strlen(insert3) +
+ 1) != DYN_OK) {
+ fprintf(stderr, "DynInsert to end failed.\n");
+ exit(1);
+ }
+
+ if (DynInsert(obj, 19, insert2, strlen(insert2)) != DYN_OK) {
+ fprintf(stderr, "DynInsert to middle failed.\n");
+ exit(1);
+ }
+
+ if (DynInsert(obj, 0, insert1, strlen(insert1)+1) != DYN_OK) {
+ fprintf(stderr, "DynInsert to start failed.\n");
+ exit(1);
+ }
+
+ printf("A new random string: \"%s\"\n",
+ (char *) DynGet(obj, 14 + strlen(insert1) + 1));
+ printf("This was put at the beginning: \"%s\"\n",
+ (char *) DynGet(obj, 0));
+
+ DynDestroy(obj);
+
+ return 0;
+}
--- /dev/null
+#! /bin/sh
+# mkinstalldirs --- make directory hierarchy
+# Author: Noah Friedman <friedman@prep.ai.mit.edu>
+# Created: 1993-05-16
+# Public domain
+
+# $Id: mkinstalldirs,v 1.1 1999/11/01 19:33:54 danw Exp $
+
+errstatus=0
+
+for file
+do
+ set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'`
+ shift
+
+ pathcomp=
+ for d
+ do
+ pathcomp="$pathcomp$d"
+ case "$pathcomp" in
+ -* ) pathcomp=./$pathcomp ;;
+ esac
+
+ if test ! -d "$pathcomp"; then
+ echo "mkdir $pathcomp"
+
+ mkdir "$pathcomp" || lasterr=$?
+
+ if test ! -d "$pathcomp"; then
+ errstatus=$lasterr
+ fi
+ fi
+
+ pathcomp="$pathcomp/"
+ done
+done
+
+exit $errstatus
+
+# mkinstalldirs ends here
--- /dev/null
+SHELL = /bin/sh
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+datadir=@datadir@
+sysconfdir=@sysconfdir@
+sbindir=@sbindir@
+lsbindir=@lsbindir@
+
+includedir=${prefix}/include
+mandir=${prefix}/man
+libdir=${exec_prefix}/lib
+
+srcdir=@srcdir@
+top_srcdir=@top_srcdir@
+BUILDTOP=..
+VPATH=@srcdir@
+CC=@CC@
+INSTALL=@INSTALL@
+
+CPPFLAGS=@CPPFLAGS@
+CFLAGS=@CFLAGS@
+ALL_CFLAGS=${CFLAGS} -DSYSCONFDIR=\"${sysconfdir}\" -I${top_srcdir}/h \
+ -I${BUILDTOP}/h -I. ${CPPFLAGS}
+LDFLAGS=-L${BUILDTOP}/lib @LDFLAGS@
+LIBS=-lzephyr @LIBS@ -lcom_err
+
+OBJS= zsrv_err.o access.o acl_files.o bdump.o class.o client.o common.o \
+ dispatch.o kopt.o kstuff.o main.o server.o subscr.o timer.o uloc.o \
+ zstring.o realm.o version.o
+
+all: zephyrd
+
+zephyrd: ${OBJS} ${BUILDTOP}/lib/libzephyr.a
+ ${CC} ${LDFLAGS} -o $@ ${OBJS} ${LIBS}
+
+zsrv_err.c zsrv_err.h: zsrv_err.et
+ compile_et ${srcdir}/zsrv_err.et
+
+version.o: version.h
+
+version.h: always
+ sh ${srcdir}/new_vers.sh
+
+.c.o:
+ ${CC} -c ${ALL_CFLAGS} $<
+
+check:
+
+# No dependency on zephyrd, to avoid rebuilding version.o.
+install:
+ ${INSTALL} -m 755 -s zephyrd ${DESTDIR}${sbindir}
+ ${INSTALL} -m 644 ${srcdir}/zephyrd.8 ${DESTDIR}${mandir}/man8
+ ${INSTALL} -m 644 ${srcdir}/default.subscriptions \
+ ${DESTDIR}${sysconfdir}/zephyr
+
+clean:
+ rm -f ${OBJS} zephyrd zsrv_err.[ch]
+
+always:
+
+${OBJS}: zserver.h zsrv_err.h timer.h zsrv_conf.h zstring.h access.h acl.h
+${OBJS}: ${top_srcdir}/h/internal.h ${top_srcdir}/h/sysdep.h
+${OBJS}: ${BUILDTOP}/h/config.h ${BUILDTOP}/h/zephyr/zephyr.h
+${OBJS}: ${BUILDTOP}/h/zephyr/zephyr_err.h
+
+.PHONY: all check install clean always
+
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains functions for dealing with acl's.
+ *
+ * Created by: John T. Kohl
+ *
+ * $Id: access.c,v 1.18 1999/01/22 23:19:36 ghudson Exp $
+ *
+ * Copyright (c) 1987 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <zephyr/mit-copyright.h>
+#include "zserver.h"
+#include <com_err.h>
+
+#if !defined (lint) && !defined (SABER)
+static const char rcsid_access_c[] =
+ "$Id: access.c,v 1.18 1999/01/22 23:19:36 ghudson Exp $";
+#endif
+
+/*
+ *
+ * External routines:
+ *
+ * int access_check(notice, acl, accesstype)
+ * ZNotice_t *notice;
+ * Acl *acl;
+ * Access accesstype;
+ *
+ * void access_init();
+ *
+ * void access_reinit();
+ */
+
+/*
+ * Each restricted class has four ACL's associated with it,
+ * governing subscriptions, transmission, and instance restrictions.
+ * This module provides the 'glue' between the standard Athena ACL
+ * routines and the support needed by the Zephyr server.
+ */
+
+/*
+ * Our private types for the acl_types field in the Acl structure.
+ * -TYT 8/14/90
+ */
+#define ACL_XMT 1
+#define ACL_SUB 2
+#define ACL_IWS 4
+#define ACL_IUI 8
+
+static void check_acl __P((Acl *acl));
+static void check_acl_type __P((Acl *acl, Access accesstype, int typeflag));
+static void access_setup __P((int first));
+
+/*
+ * check access. return 1 if ok, 0 if not ok.
+ */
+
+int
+access_check(sender, acl, accesstype)
+ char *sender;
+ Acl *acl;
+ Access accesstype;
+{
+ char buf[MAXPATHLEN]; /* holds the real acl name */
+ char *prefix;
+ int flag;
+ int retval;
+
+ switch (accesstype) {
+ case TRANSMIT:
+ prefix = "xmt";
+ flag = ACL_XMT;
+ break;
+ case SUBSCRIBE:
+ prefix = "sub";
+ flag = ACL_SUB;
+ break;
+ case INSTWILD:
+ prefix = "iws";
+ flag = ACL_IWS;
+ break;
+ case INSTUID:
+ prefix = "iui";
+ flag = ACL_IUI;
+ break;
+ default:
+ syslog(LOG_ERR, "unknown access type %d", (int) accesstype);
+ return 0;
+ }
+ if (!(acl->acl_types & flag)) /* no acl ==> no restriction */
+ return 1;
+ sprintf(buf, "%s/%s-%s.acl", acl_dir, prefix, acl->acl_filename);
+ /*
+ * If we can't load it (because it probably doesn't exist),
+ * we deny access.
+ */
+#if 0
+ zdbug ((LOG_DEBUG, "checking %s for %s", buf, sender));
+#endif
+
+ retval = acl_load(buf);
+ if (retval < 0) {
+ syslog(LOG_DEBUG, "Error in acl_load of %s for %s", buf, sender);
+ return 0;
+ }
+ return acl_check(buf, sender);
+}
+
+static void
+check_acl(acl)
+ Acl *acl;
+{
+ acl->acl_types = 0;
+ check_acl_type(acl, TRANSMIT, ACL_XMT);
+ check_acl_type(acl, SUBSCRIBE, ACL_SUB);
+ check_acl_type(acl, INSTWILD, ACL_IWS);
+ check_acl_type(acl, INSTUID, ACL_IUI);
+}
+
+static void
+check_acl_type(acl, accesstype, typeflag)
+ Acl *acl;
+ Access accesstype;
+ int typeflag;
+{
+ char buf[MAXPATHLEN]; /* holds the real acl name */
+ char *prefix;
+
+ switch (accesstype) {
+ case TRANSMIT:
+ prefix = "xmt";
+ break;
+ case SUBSCRIBE:
+ prefix = "sub";
+ break;
+ case INSTWILD:
+ prefix = "iws";
+ break;
+ case INSTUID:
+ prefix = "iui";
+ break;
+ default:
+ syslog(LOG_ERR, "unknown access type %d", (int) accesstype);
+ return;
+ }
+ sprintf(buf, "%s/%s-%s.acl", acl_dir, prefix, acl->acl_filename);
+ if (!access(buf, F_OK))
+ acl->acl_types |= typeflag;
+}
+
+
+/*
+ * Re-init code written by TYT, 8/14/90.
+ *
+ * General plan of action; we reread the registry list, and add any
+ * new restricted classes. If any restricted classes disappear (this
+ * should be rarely) the Acl structure is not deallocated; rather,
+ * the acl_types field will be left at zero, since there will be no
+ * acl files for the (non-)restricted class.
+ */
+static void
+access_setup(first)
+ int first;
+{
+ char buf[MAXPATHLEN];
+ char class_name[512]; /* assume class names <= 511 bytes */
+ FILE *registry;
+ Acl *acl;
+ int len;
+ char *colon_idx;
+ Code_t retval = 0;
+
+ sprintf(buf, "%s/%s", acl_dir, ZEPHYR_CLASS_REGISTRY);
+ registry = fopen(buf, "r");
+ if (!registry) {
+ syslog(LOG_ERR, "no registry available, all classes are free");
+ return;
+ }
+ while (fgets(class_name, 512, registry)) {
+ colon_idx = strchr(class_name, ':');
+ if (colon_idx != NULL)
+ *colon_idx = '\0';
+ else if ((len = strlen(class_name)) != 0)
+ class_name[len - 1] = '\0';
+ acl = 0;
+ if (!first) {
+ String *z;
+
+ z = make_string(class_name,1);
+ acl = class_get_acl(z);
+ free_string(z);
+ }
+ if (!acl) {
+ acl = (Acl *) malloc(sizeof(Acl));
+ if (!acl) {
+ syslog(LOG_ERR, "no mem acl alloc");
+ abort();
+ }
+ acl->acl_filename = strsave(class_name);
+ check_acl(acl);
+
+ if (!first) {
+ /* Try to restrict already existing class */
+ retval = class_restrict(class_name, acl);
+ if (retval == ZSRV_NOCLASS)
+ retval = class_setup_restricted(class_name, acl);
+ } else {
+ retval = class_setup_restricted(class_name, acl);
+ }
+ }
+ if (retval) {
+ syslog(LOG_ERR, "can't restrict %s: %s",
+ class_name, error_message(retval));
+ continue;
+ }
+ zdbug((LOG_DEBUG, "restricted %s", class_name));
+ }
+ fclose(registry);
+}
+
+void
+access_init()
+{
+ access_setup(1);
+}
+
+void
+access_reinit()
+{
+ acl_cache_reset();
+ access_setup(0);
+}
--- /dev/null
+/*
+ * This file is part of the Project Athena Zephyr Notification System.
+ *
+ * It contains declarations for use in the server, relating to access
+ * control.
+ *
+ * Created by Ken Raeburn.
+ *
+ * $Id: access.h,v 1.5 1999/01/22 23:19:37 ghudson Exp $
+ *
+ * Copyright (c) 1990 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <zephyr/mit-copyright.h>
+
+#include "acl.h"
+#include "zstring.h"
+
+typedef enum _Access {
+ TRANSMIT, /* use transmission acl */
+ SUBSCRIBE, /* use subscription acl */
+ INSTWILD, /* use instance wildcard acl */
+ INSTUID /* use instance UID identity acl */
+} Access;
+
+typedef struct _Acl {
+ char *acl_filename;
+ int acl_types; /* Internal; access fields present. */
+} Acl;
+
+/* found in access.c */
+void access_init __P((void));
+void access_reinit __P((void));
+
+/* found in acl_files.c */
+int acl_load __P((char *));
+
+/* external data relevant */
+extern int zdebug;
+
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains definitions for the ACL library
+ *
+ * Created by: John T. Kohl
+ *
+ * $Id: acl.h,v 3.2 1999/01/22 23:19:38 ghudson Exp $
+ *
+ * Copyright (c) 1987 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <zephyr/mit-copyright.h>
+
+#ifndef __ACL__
+#define __ACL__
+
+int acl_add __P((char *, char *));
+int acl_check __P((char *, char *));
+int acl_delete __P((char *, char *));
+int acl_initialize __P((char *, int));
+void acl_cache_reset __P((void));
+
+#endif /* __ACL__ */
+
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains functions for maintaining Access Control Lists.
+ *
+ * Created by: John T. Kohl
+ *
+ * $Id: acl_files.c,v 3.3 1999/01/22 23:19:38 ghudson Exp $
+ *
+ * Copyright (c) 1987,1988 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+/* Define this if you really want the ACL-writing code included. */
+/* #define WRITE_ACL */
+
+/*
+ * Stolen from lib/acl_files.c because acl_load needs to be externally
+ * declared and not statically declared.
+ */
+
+#include <zephyr/mit-copyright.h>
+#include "zserver.h"
+
+
+#ifndef SABER
+#ifndef lint
+static const char rcsid_acl_files_c[] = "$Id: acl_files.c,v 3.3 1999/01/22 23:19:38 ghudson Exp $";
+#endif /* lint */
+#endif /* SABER */
+
+/*** Routines for manipulating access control list files ***/
+
+/* "aname.inst@realm" */
+#define MAX_PRINCIPAL_SIZE (ANAME_SZ + INST_SZ + REALM_SZ + 3)
+#define INST_SEP '.'
+#define REALM_SEP '@'
+
+#define LINESIZE 2048 /* Maximum line length in an acl file */
+
+#define NEW_FILE "%s.~NEWACL~" /* Format for name of altered acl file */
+#define WAIT_TIME 300 /* Maximum time allowed write acl file */
+
+#define CACHED_ACLS 64 /* How many acls to cache */
+#define ACL_LEN 256 /* Twice a reasonable acl length */
+
+#define MAX(a,b) (((a)>(b))?(a):(b))
+#define MIN(a,b) (((a)<(b))?(a):(b))
+
+#define COR(a,b) ((a!=NULL)?(a):(b))
+
+extern int errno;
+
+extern time_t time();
+
+/* Canonicalize a principal name */
+/* If instance is missing, it becomes "" */
+/* If realm is missing, it becomes the local realm */
+/* Canonicalized form is put in canon, which must be big enough to hold
+ MAX_PRINCIPAL_SIZE characters */
+void acl_canonicalize_principal(principal, canon)
+ char *principal;
+ char *canon;
+{
+ char *end;
+ char *dot, *atsign;
+ int len;
+
+ dot = strchr(principal, INST_SEP);
+ atsign = strchr(principal, REALM_SEP);
+
+ /* Maybe we're done already */
+ if (dot != NULL && atsign != NULL) {
+ if (dot < atsign) {
+ /* It's for real */
+ /* Copy into canon */
+ strncpy(canon, principal, MAX_PRINCIPAL_SIZE);
+ canon[MAX_PRINCIPAL_SIZE-1] = '\0';
+ return;
+ } else {
+ /* Nope, it's part of the realm */
+ dot = NULL;
+ }
+ }
+
+ /* No such luck */
+ end = principal + strlen(principal);
+
+ /* Get the principal name */
+ len = MIN(ANAME_SZ, COR(dot, COR(atsign, end)) - principal);
+ strncpy(canon, principal, len);
+ canon += len;
+
+ /* Add INST_SEP */
+ *canon++ = INST_SEP;
+
+ /* Get the instance, if it exists */
+ if (dot != NULL) {
+ ++dot;
+ len = MIN(INST_SZ, COR(atsign, end) - dot);
+ strncpy(canon, dot, len);
+ canon += len;
+ }
+
+ /* Add REALM_SEP */
+ *canon++ = REALM_SEP;
+
+ /* Get the realm, if it exists */
+ /* Otherwise, default to local realm */
+ if (atsign != NULL) {
+ ++atsign;
+ len = MIN(REALM_SZ, end - atsign);
+ strncpy(canon, atsign, len);
+ canon += len;
+ *canon++ = '\0';
+ }
+#ifdef HAVE_KRB4
+ else if (krb_get_lrealm(canon, 1) != KSUCCESS) {
+ strcpy(canon, KRB_REALM);
+ }
+#endif
+}
+
+#ifdef notdef
+/* Get a lock to modify acl_file */
+/* Return new FILE pointer */
+/* or NULL if file cannot be modified */
+/* REQUIRES WRITE PERMISSION TO CONTAINING DIRECTORY */
+static FILE *acl_lock_file(acl_file)
+ char *acl_file;
+{
+ struct stat s;
+ char new[LINESIZE];
+ int nfd;
+ FILE *nf;
+ int mode;
+
+ if (stat(acl_file, &s) < 0) return(NULL);
+ mode = s.st_mode;
+ sprintf(new, NEW_FILE, acl_file);
+ for (;;) {
+ /* Open the new file */
+ if ((nfd = open(new, O_WRONLY|O_CREAT|O_EXCL, mode)) < 0) {
+ if (errno == EEXIST) {
+ /* Maybe somebody got here already, maybe it's just old */
+ if (stat(new, &s) < 0) return(NULL);
+ if (time(0) - s.st_ctime > WAIT_TIME) {
+ /* File is stale, kill it */
+ unlink(new);
+ continue;
+ } else {
+ /* Wait and try again */
+ sleep(1);
+ continue;
+ }
+ } else {
+ /* Some other error, we lose */
+ return(NULL);
+ }
+ }
+
+ /* If we got to here, the lock file is ours and ok */
+ /* Reopen it under stdio */
+ if ((nf = fdopen(nfd, "w")) == NULL) {
+ /* Oops, clean up */
+ unlink(new);
+ }
+ return(nf);
+ }
+}
+
+/* Commit changes to acl_file written onto FILE *f */
+/* Returns zero if successful */
+/* Returns > 0 if lock was broken */
+/* Returns < 0 if some other error occurs */
+/* Closes f */
+static int acl_commit(acl_file, f)
+ char *acl_file;
+ FILE *f;
+{
+#ifdef WRITE_ACL
+ char new[LINESIZE];
+ int ret;
+ struct stat s;
+
+ sprintf(new, NEW_FILE, acl_file);
+ if (fflush(f) < 0
+ || fstat(fileno(f), &s) < 0
+ || s.st_nlink == 0) {
+ acl_abort(acl_file, f);
+ return(-1);
+ }
+
+ ret = rename(new, acl_file);
+ fclose(f);
+ return(ret);
+#else
+ abort ();
+#endif
+}
+
+/* Abort changes to acl_file written onto FILE *f */
+/* Returns 0 if successful, < 0 otherwise */
+/* Closes f */
+static int acl_abort(acl_file, f)
+ char *acl_file;
+ FILE *f;
+{
+#ifdef WRITE_ACL
+ char new[LINESIZE];
+ int ret;
+ struct stat s;
+
+ /* make sure we aren't nuking someone else's file */
+ if (fstat(fileno(f), &s) < 0 || s.st_nlink == 0) {
+ fclose(f);
+ return(-1);
+ } else {
+ sprintf(new, NEW_FILE, acl_file);
+ ret = unlink(new);
+ fclose(f);
+ return(ret);
+ }
+#else
+ abort ();
+#endif
+}
+
+/* Initialize an acl_file */
+/* Creates the file with permissions perm if it does not exist */
+/* Erases it if it does */
+/* Returns return value of acl_commit */
+int
+acl_initialize(acl_file, perm)
+ char *acl_file;
+ int perm;
+{
+ FILE *new;
+ int fd;
+
+ /* Check if the file exists already */
+ if ((new = acl_lock_file(acl_file)) != NULL) {
+ return(acl_commit(acl_file, new));
+ } else {
+ /* File must be readable and writable by owner */
+ if ((fd = open(acl_file, O_CREAT|O_EXCL, perm|0600)) < 0) {
+ return(-1);
+ } else {
+ close(fd);
+ return(0);
+ }
+ }
+}
+
+#endif /* notdef */
+
+/* Eliminate all whitespace character in buf */
+/* Modifies its argument */
+static void nuke_whitespace(buf)
+ char *buf;
+{
+ char *pin, *pout;
+
+ for (pin = pout = buf; *pin != '\0'; pin++)
+ if (!isspace(*pin)) *pout++ = *pin;
+ *pout = '\0'; /* Terminate the string */
+}
+
+/* Hash table stuff */
+
+struct hashtbl {
+ int size; /* Max number of entries */
+ int entries; /* Actual number of entries */
+ char **tbl; /* Pointer to start of table */
+};
+
+/* Make an empty hash table of size s */
+static struct hashtbl *make_hash(size)
+ int size;
+{
+ struct hashtbl *h;
+
+ if (size < 1) size = 1;
+ h = (struct hashtbl *) malloc(sizeof(struct hashtbl));
+ h->size = size;
+ h->entries = 0;
+ h->tbl = (char **) calloc(size, sizeof(char *));
+ return(h);
+}
+
+/* Destroy a hash table */
+static void
+destroy_hash(h)
+ struct hashtbl *h;
+{
+ int i;
+
+ for (i = 0; i < h->size; i++) {
+ if (h->tbl[i] != NULL) free(h->tbl[i]);
+ }
+ free(h->tbl);
+ free(h);
+}
+
+/* Compute hash value for a string */
+static unsigned int
+hashval(s)
+ char *s;
+{
+ unsigned hv;
+
+ for (hv = 0; *s != '\0'; s++) {
+ hv ^= ((hv << 3) ^ *s);
+ }
+ return(hv);
+}
+
+/* Add an element to a hash table */
+static void add_hash(h, el)
+ struct hashtbl *h;
+ char *el;
+{
+ unsigned hv;
+ char *s;
+ char **old;
+ int i;
+
+#if 0
+ fprintf (stderr, "adding %s to acl hash %08X\n", el, h);
+#endif
+ /* Make space if it isn't there already */
+ if (h->entries + 1 > (h->size >> 1)) {
+ old = h->tbl;
+ h->tbl = (char **) calloc(h->size << 1, sizeof(char *));
+ for (i = 0; i < h->size; i++) {
+ if (old[i] != NULL) {
+ hv = hashval(old[i]) % (h->size << 1);
+ while(h->tbl[hv] != NULL) hv = (hv+1) % (h->size << 1);
+ h->tbl[hv] = old[i];
+ }
+ }
+ h->size = h->size << 1;
+ free(old);
+ }
+
+ hv = hashval(el) % h->size;
+ while(h->tbl[hv] != NULL && strcmp(h->tbl[hv], el)) hv = (hv+1) % h->size;
+ s = (char *) malloc(strlen(el)+1);
+ strcpy(s, el);
+ h->tbl[hv] = s;
+ h->entries++;
+}
+
+/* Returns nonzero if el is in h */
+static int
+check_hash(h, el)
+ struct hashtbl *h;
+ char *el;
+{
+ unsigned hv;
+
+#if 0
+ fprintf (stderr, "looking for %s in acl %08X\n", el, h);
+#endif
+ for (hv = hashval(el) % h->size; h->tbl[hv]; hv = (hv + 1) % h->size) {
+#if 0
+ fprintf (stderr, "\tstrcmp (%s,...)\n", h->tbl[hv]);
+#endif
+ if (!strcmp(h->tbl[hv], el)) {
+#if 0
+ fprintf (stderr, "success!\n");
+#endif
+ return 1;
+ }
+ }
+#if 0
+ fprintf (stderr, "failure\n");
+#endif
+ return 0;
+}
+
+struct acl {
+ char filename[LINESIZE]; /* Name of acl file */
+ struct hashtbl *acl; /* Acl entries */
+};
+
+static struct acl acl_cache[CACHED_ACLS];
+
+static int acl_cache_count = 0;
+static int acl_cache_next = 0;
+
+/* Returns < 0 if unsuccessful in loading acl */
+/* Returns index into acl_cache otherwise */
+/* Note that if acl is already loaded, this is just a lookup */
+int acl_load(name)
+ char *name;
+{
+ int i;
+ FILE *f;
+ char buf[MAX_PRINCIPAL_SIZE];
+ char canon[MAX_PRINCIPAL_SIZE];
+
+ /* See if it's there already */
+ for (i = 0; i < acl_cache_count; i++) {
+ if (!strcmp(acl_cache[i].filename, name))
+ goto got_it;
+ }
+
+ /* It isn't, load it in */
+ /* maybe there's still room */
+ if (acl_cache_count < CACHED_ACLS) {
+ i = acl_cache_count++;
+ } else {
+ /* No room, clean one out */
+ i = acl_cache_next;
+ acl_cache_next = (acl_cache_next + 1) % CACHED_ACLS;
+ if (acl_cache[i].acl) {
+ destroy_hash(acl_cache[i].acl);
+ acl_cache[i].acl = (struct hashtbl *) 0;
+ }
+ }
+
+ /* Set up the acl */
+ strcpy(acl_cache[i].filename, name);
+ /* Force reload */
+ acl_cache[i].acl = (struct hashtbl *) 0;
+
+ got_it:
+ /*
+ * See if we need to reload the ACL
+ */
+ if (acl_cache[i].acl == (struct hashtbl *) 0) {
+ /* Gotta reload */
+#if 0
+ fprintf (stderr, "attempting to load %s\n", name);
+#endif
+ if ((f = fopen(name, "r")) == NULL) {
+#if 0
+ perror (name);
+#endif
+ return -1;
+ }
+ if (acl_cache[i].acl) destroy_hash(acl_cache[i].acl);
+ acl_cache[i].acl = make_hash(ACL_LEN);
+ while(fgets(buf, sizeof(buf), f) != NULL) {
+ nuke_whitespace(buf);
+ acl_canonicalize_principal(buf, canon);
+ add_hash(acl_cache[i].acl, canon);
+ }
+ fclose(f);
+ }
+ return(i);
+}
+
+/*
+ * This destroys all cached ACL's so that new ones will be loaded in
+ * the next time they are requested.
+ */
+void
+acl_cache_reset()
+{
+ int i;
+
+ /* See if it's there already */
+ for (i = 0; i < acl_cache_count; i++)
+ if (acl_cache[i].acl) {
+ destroy_hash(acl_cache[i].acl);
+ acl_cache[i].acl = (struct hashtbl *) 0;
+ }
+ acl_cache_count = 0;
+ acl_cache_next = 0;
+ }
+
+
+/* Returns nonzero if it can be determined that acl contains principal */
+/* Principal is not canonicalized, and no wildcarding is done */
+acl_exact_match(acl, principal)
+ char *acl;
+ char *principal;
+{
+ int idx;
+
+#if 0
+ fprintf (stderr, "checking for %s in %s\n", principal, acl);
+#endif
+ return((idx = acl_load(acl)) >= 0
+ && check_hash(acl_cache[idx].acl, principal));
+}
+
+/* Returns nonzero if it can be determined that acl contains principal */
+/* Recognizes wildcards in acl. */
+int
+acl_check(acl, principal)
+ char *acl;
+ char *principal;
+{
+ char buf[MAX_PRINCIPAL_SIZE];
+ char canon[MAX_PRINCIPAL_SIZE];
+ char *instance, *realm;
+ int p, i, r;
+
+ /* Parse into principal, instance, and realm. */
+ acl_canonicalize_principal(principal, canon);
+ instance = (char *) strchr(canon, INST_SEP);
+ *instance++ = 0;
+ realm = (char *) strchr(instance, REALM_SEP);
+ *realm++ = 0;
+
+ for (p = 0; p <= 1; p++) {
+ for (i = 0; i <= 1; i++) {
+ for (r = 0; r <= 1; r++) {
+ sprintf(buf, "%s%c%s%c%s", (p) ? canon : "*", INST_SEP,
+ (i) ? instance : "*", REALM_SEP, (r) ? realm : "*");
+ if (acl_exact_match(acl, buf))
+ return 1;
+ }
+ }
+ }
+
+ return(0);
+}
+
+#ifdef notdef
+/* Adds principal to acl */
+/* Wildcards are interpreted literally */
+int
+acl_add(acl, principal)
+ char *acl;
+ char *principal;
+{
+ int idx;
+ int i;
+ FILE *new;
+ char canon[MAX_PRINCIPAL_SIZE];
+
+ acl_canonicalize_principal(principal, canon);
+
+ if ((new = acl_lock_file(acl)) == NULL) return(-1);
+ if ((acl_exact_match(acl, canon))
+ || (idx = acl_load(acl)) < 0) {
+ acl_abort(acl, new);
+ return(-1);
+ }
+ /* It isn't there yet, copy the file and put it in */
+ for (i = 0; i < acl_cache[idx].acl->size; i++) {
+ if (acl_cache[idx].acl->tbl[i] != NULL) {
+ if (fputs(acl_cache[idx].acl->tbl[i], new) == NULL
+ || putc('\n', new) != '\n') {
+ acl_abort(acl, new);
+ return(-1);
+ }
+ }
+ }
+ fputs(canon, new);
+ putc('\n', new);
+ return(acl_commit(acl, new));
+}
+
+/* Removes principal from acl */
+/* Wildcards are interpreted literally */
+int
+acl_delete(acl, principal)
+ char *acl;
+ char *principal;
+{
+ int idx;
+ int i;
+ FILE *new;
+ char canon[MAX_PRINCIPAL_SIZE];
+
+ acl_canonicalize_principal(principal, canon);
+
+ if ((new = acl_lock_file(acl)) == NULL) return(-1);
+ if ((!acl_exact_match(acl, canon))
+ || (idx = acl_load(acl)) < 0) {
+ acl_abort(acl, new);
+ return(-1);
+ }
+ /* It isn't there yet, copy the file and put it in */
+ for (i = 0; i < acl_cache[idx].acl->size; i++) {
+ if (acl_cache[idx].acl->tbl[i] != NULL
+ && strcmp(acl_cache[idx].acl->tbl[i], canon)) {
+ fputs(acl_cache[idx].acl->tbl[i], new);
+ putc('\n', new);
+ }
+ }
+ return(acl_commit(acl, new));
+}
+#endif /* notdef */
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains functions for dumping server state between servers.
+ *
+ * Created by: John T. Kohl
+ *
+ * $Id: bdump.c,v 1.52 1999/01/22 23:19:39 ghudson Exp $
+ *
+ * Copyright (c) 1987,1988,1991 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <zephyr/mit-copyright.h>
+#include "zserver.h"
+#include <sys/socket.h>
+#include <com_err.h>
+
+#ifndef lint
+static const char rcsid_bdump_c[] = "$Id: bdump.c,v 1.52 1999/01/22 23:19:39 ghudson Exp $";
+#endif /* lint */
+
+/*
+ * External functions are:
+ *
+ * void bdump_offer(who)
+ * strut sockaddr_in *who;
+ *
+ * void bdump_send()
+ *
+ * void bdump_get(notice, auth, who, server)
+ * ZNotice_t *notice;
+ * int auth;
+ * struct sockaddr_in *who;
+ * Server *server;
+ *
+ * Code_t bdump_send_list_tcp(kind, port, class, inst, opcode,
+ * sender, recip, lyst, num)
+ * ZNotice_Kind_t kind;
+ * u_short port;
+ * char *class, *inst, *opcode, *sender, *recip;
+ * char *lyst[];
+ * int num;
+ */
+
+static void close_bdump __P((void* arg));
+static Code_t bdump_send_loop __P((Server *server)),
+bdump_ask_for __P((char *inst)),
+bdump_recv_loop __P((Server *server));
+static void bdump_get_v12 __P((ZNotice_t *, int, struct sockaddr_in *,
+ Server *));
+static Code_t get_packet __P((void *packet, int len, int *retlen));
+static Code_t extract_sin __P((ZNotice_t *notice, struct sockaddr_in *target));
+static Code_t send_done __P((void));
+static Code_t send_list __P((ZNotice_Kind_t kind, int port, char *class_name,
+ char *inst, char *opcode, char *sender,
+ char *recip, char **lyst, int num));
+static Code_t send_normal_tcp __P((ZNotice_Kind_t kind, int port,
+ char *class_name,
+ char *inst, char *opcode, char *sender,
+ char *recip, char *message, int len));
+static int net_read __P((FILE *f, char *buf, int len));
+static int net_write __P((FILE *f, char *buf, int len));
+static int setup_file_pointers __P((void));
+static void shutdown_file_pointers __P((void));
+static void cleanup __P((Server *server));
+
+#ifdef HAVE_KRB4
+static long ticket_time;
+static char my_realm[REALM_SZ];
+
+#define TKTLIFETIME 120
+#define tkt_lifetime(val) ((long) val * 5L * 60L)
+
+#ifndef NOENCRYPTION
+extern C_Block serv_key;
+extern Sched serv_ksched;
+#endif
+#endif /* HAVE_KRB4 */
+
+static Timer *bdump_timer;
+static int live_socket = -1;
+static FILE *input, *output;
+static struct sockaddr_in bdump_sin;
+#ifdef notdef
+static int cancel_outgoing_dump;
+#endif
+
+int bdumping;
+int bdump_concurrent;
+extern char *bdump_version;
+
+/*
+ * Functions for performing a brain dump between servers.
+ */
+
+/*
+ * offer the brain dump to another server
+ */
+
+void
+bdump_offer(who)
+ struct sockaddr_in *who;
+{
+ Code_t retval;
+ char buf[512], *addr, *lyst[2];
+#ifndef HAVE_KRB4
+ int bdump_port = IPPORT_RESERVED - 1;
+#endif /* !HAVE_KRB4 */
+#if 1
+ zdbug((LOG_DEBUG, "bdump_offer"));
+#endif
+#ifdef HAVE_KRB4
+ /*
+ * when using HAVE_KRB4 server-server authentication, we can
+ * use any random local address
+ */
+ bdump_socket = socket(AF_INET, SOCK_STREAM, 0);
+ if (bdump_socket < 0) {
+ syslog(LOG_ERR,"bdump_offer: socket: %m");
+ bdump_socket = -1;
+ return;
+ }
+ memset(&bdump_sin, 0, sizeof(bdump_sin));
+ /* a port field of 0 makes the UNIX
+ * kernel choose an appropriate port/address pair */
+
+ bdump_sin.sin_port = 0;
+ bdump_sin.sin_addr = my_addr;
+ bdump_sin.sin_family = AF_INET;
+ retval = bind(bdump_socket, (struct sockaddr *) &bdump_sin,
+ sizeof(bdump_sin));
+ if (retval < 0) {
+ syslog(LOG_ERR, "bdump_offer: bind: %m");
+ close(bdump_socket);
+ bdump_socket = -1;
+ return;
+ }
+ if (!bdump_sin.sin_port) {
+ int len = sizeof(bdump_sin);
+
+ if (getsockname(bdump_socket,
+ (struct sockaddr *) &bdump_sin, &len) < 0) {
+ syslog(LOG_ERR, "bdump_offer: getsockname: %m");
+ close(bdump_socket);
+ bdump_socket = -1;
+ return;
+ }
+ }
+#else /* !HAVE_KRB4 */
+ /*
+ * when not using HAVE_KRB4, we can't use any old port, we use
+ * Internet reserved ports instead (rresvport)
+ */
+ bdump_socket = rresvport(&bdump_port);
+ if (bdump_socket < 0) {
+ syslog(LOG_ERR,"bdump_offer: socket: %m");
+ bdump_socket = -1;
+ return;
+ }
+ memset(&bdump_sin, 0, sizeof(bdump_sin));
+ bdump_sin.sin_port = htons((unsigned short) bdump_port);
+ bdump_sin.sin_addr = my_addr;
+ bdump_sin.sin_family = AF_INET;
+#endif /* HAVE_KRB4 */
+
+ listen(bdump_socket, 1);
+
+ bdump_timer = timer_set_rel(20L, close_bdump, NULL);
+ FD_SET(bdump_socket, &interesting);
+ nfds = max(bdump_socket, srv_socket) + 1;
+
+ addr = inet_ntoa(bdump_sin.sin_addr);
+ sprintf(buf, "%d", ntohs(bdump_sin.sin_port));
+ lyst[0] = addr;
+ lyst[1] = buf;
+
+ retval = ZSetDestAddr(who);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_WARNING, "bdump_offer: ZSetDestAddr: %s",
+ error_message(retval));
+ return;
+ }
+
+ /* myname is the hostname */
+ /* the class instance is the version number, here it is */
+ /* bdump_version, which is set in main */
+ send_list(ACKED, srv_addr.sin_port, ZEPHYR_ADMIN_CLASS, bdump_version,
+ ADMIN_BDUMP, myname, "", lyst, 2);
+
+#if 1
+ zdbug((LOG_DEBUG,"bdump_offer: address is %s/%d\n",
+ inet_ntoa(bdump_sin.sin_addr),
+ ntohs(bdump_sin.sin_port)));
+#endif
+ return;
+}
+
+/*
+ * Accept a connection, and send the brain dump to the other server
+ */
+
+void
+bdump_send()
+{
+ struct sockaddr_in from;
+ Server *server;
+ Code_t retval;
+ int fromlen = sizeof(from);
+ int on = 1;
+#ifdef _POSIX_VERSION
+ struct sigaction action;
+#endif
+
+#ifdef HAVE_KRB4
+ KTEXT_ST ticket;
+ AUTH_DAT kdata;
+#else
+ unsigned short fromport;
+#endif /* HAVE_KRB4 */
+
+#if 1
+ zdbug((LOG_DEBUG, "bdump_send"));
+#endif
+ /* accept the connection, and send the brain dump */
+ live_socket = accept(bdump_socket, (struct sockaddr *) &from, &fromlen);
+ if (live_socket < 0) {
+ syslog(LOG_ERR,"bdump_send: accept: %m");
+ return;
+ }
+ if (setsockopt(live_socket, SOL_SOCKET, SO_KEEPALIVE, (char *) &on,
+ sizeof(on)) < 0)
+ syslog(LOG_WARNING, "bdump_send: setsockopt (SO_KEEPALIVE): %m");
+
+#ifndef HAVE_KRB4
+ fromport = ntohs(from.sin_port);
+#endif
+
+#ifdef _POSIX_VERSION
+ sigemptyset(&action.sa_mask);
+ action.sa_flags = 0;
+ action.sa_handler = SIG_IGN;
+ sigaction(SIGPIPE, &action, NULL);
+
+#else
+ signal(SIGPIPE, SIG_IGN); /* so we can detect failures */
+#endif
+
+ from.sin_port = srv_addr.sin_port; /* we don't care what port
+ * it came from, and we need to
+ * fake out server_which_server() */
+ server = server_which_server(&from);
+ if (!server) {
+ syslog(LOG_ERR, "bdump_send: unknown server?");
+ server = limbo_server;
+ }
+#if 1
+ zdbug((LOG_DEBUG, "bdump_send: connection from %s/%d",
+ inet_ntoa(from.sin_addr), ntohs(from.sin_port)));
+#endif
+
+ bdumping = 1;
+ server->dumping = 1;
+
+ if (bdump_socket >= 0) {
+ /* shut down the listening socket and the timer. */
+ FD_CLR(bdump_socket, &interesting);
+ close(bdump_socket);
+ nfds = srv_socket + 1;
+ bdump_socket = -1;
+ timer_reset(bdump_timer);
+ }
+
+ /* Now begin the brain dump. */
+
+#ifdef HAVE_KRB4
+ /* receive the authenticator */
+ retval = GetKerberosData(live_socket, from.sin_addr, &kdata,
+ SERVER_SERVICE, srvtab_file);
+ if (retval != KSUCCESS) {
+ syslog(LOG_ERR, "bdump_send: getkdata: %s",
+ krb_get_err_text(retval));
+ cleanup(server);
+ return;
+ }
+ if (get_tgt()) {
+ cleanup(server);
+ return;
+ }
+ if (strcmp(kdata.pname, SERVER_SERVICE) ||
+ strcmp(kdata.pinst, SERVER_INSTANCE) ||
+ strcmp(kdata.prealm, ZGetRealm())) {
+ syslog(LOG_ERR, "bdump_send: peer not zephyr: %s.%s@%s",
+ kdata.pname, kdata.pinst, kdata.prealm);
+ cleanup(server);
+ return;
+ }
+ /* authenticate back */
+ retval = SendKerberosData(live_socket, &ticket, SERVER_SERVICE,
+ SERVER_INSTANCE);
+ if (retval != 0) {
+ syslog(LOG_ERR,"bdump_send: SendKerberosData: %s",
+ error_message (retval));
+ cleanup(server);
+ return;
+ }
+#else /* !HAVE_KRB4 */
+ if (fromport > IPPORT_RESERVED || fromport < IPPORT_RESERVED / 2) {
+ syslog(LOG_ERR, "bdump_send: bad port from peer: %d", fromport);
+ cleanup(server);
+ return;
+ }
+#endif /* HAVE_KRB4 */
+
+ retval = setup_file_pointers();
+ if (retval != 0) {
+ syslog (LOG_WARNING, "bdump_send: can't set up file pointers: %s",
+ error_message(retval));
+ cleanup(server);
+ return;
+ }
+ retval = bdump_send_loop(server);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_WARNING, "bdump_send: bdump_send_loop failed: %s",
+ error_message(retval));
+ cleanup(server);
+ return;
+ }
+ retval = bdump_recv_loop(server);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_WARNING, "bdump_send: bdump_recv_loop failed: %s",
+ error_message(retval));
+ cleanup(server);
+ return;
+ }
+#if 1
+ zdbug((LOG_DEBUG, "bdump_send: finished"));
+#endif
+ if (server != limbo_server) {
+ /* set this guy to be up, and schedule a hello */
+ server->state = SERV_UP;
+ timer_reset(server->timer);
+ server->timer = timer_set_rel(0L, server_timo, server);
+ }
+#if 0
+ zdbug((LOG_DEBUG,"cleanup sbd"));
+#endif
+ shutdown_file_pointers();
+
+#ifdef _POSIX_VERSION
+ action.sa_handler = SIG_DFL;
+ sigaction(SIGPIPE, &action, NULL);
+#else
+ signal(SIGPIPE, SIG_DFL);
+#endif
+ bdumping = 0;
+ server->dumping = 0;
+ /* Now that we are finished dumping, send all the queued packets */
+ server_send_queue(server);
+ return;
+}
+
+/*ARGSUSED*/
+static void
+bdump_get_v12 (notice, auth, who, server)
+ ZNotice_t *notice;
+ int auth;
+ struct sockaddr_in *who;
+ Server *server;
+{
+ struct sockaddr_in from;
+ Code_t retval;
+ int on = 1;
+#ifdef _POSIX_VERSION
+ struct sigaction action;
+#endif
+#ifdef HAVE_KRB4
+ KTEXT_ST ticket;
+ AUTH_DAT kdata;
+#else /* !HAVE_KRB4 */
+ int reserved_port = IPPORT_RESERVED - 1;
+#endif /* HAVE_KRB4 */
+
+ bdumping = 1;
+ server->dumping = 1;
+
+#ifdef _POSIX_VERSION
+ action.sa_flags = 0;
+ sigemptyset(&action.sa_mask);
+ action.sa_handler = SIG_IGN;
+ sigaction(SIGPIPE, &action, NULL);
+#else
+ signal(SIGPIPE, SIG_IGN); /* so we can detect problems */
+#endif /* _POSIX_VRESION */
+
+ if (bdump_socket >= 0) {
+ /* We cannot go get a brain dump when someone may
+ potentially be connecting to us (if that other
+ server is the server to whom we are connecting,
+ we will deadlock. so we shut down the listening
+ socket and the timer. */
+ FD_CLR(bdump_socket, &interesting);
+ close(bdump_socket);
+ nfds = srv_socket+1;
+ bdump_socket = -1;
+ timer_reset(bdump_timer);
+ }
+
+ retval = extract_sin(notice, &from);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_ERR, "bdump_get: sin: %s", error_message(retval));
+#ifdef _POSIX_VERSION
+ action.sa_handler = SIG_DFL;
+ sigaction(SIGPIPE, &action, NULL);
+#else
+ signal(SIGPIPE, SIG_DFL);
+#endif
+ bdumping = 0;
+ server->dumping = 0;
+ return;
+ }
+#ifndef HAVE_KRB4
+ if (ntohs(from.sin_port) > IPPORT_RESERVED ||
+ ntohs(from.sin_port) < IPPORT_RESERVED / 2) {
+ syslog(LOG_ERR, "bdump_get: port not reserved: %d",
+ ntohs(from.sin_port));
+ cleanup(server);
+ return;
+ }
+ live_socket = rresvport(&reserved_port);
+#else /* !HAVE_KRB4 */
+ live_socket = socket(AF_INET, SOCK_STREAM, 0);
+#endif /* HAVE_KRB4 */
+ if (live_socket < 0) {
+ syslog(LOG_ERR, "bdump_get: socket: %m");
+ cleanup(server);
+ return;
+ }
+ if (connect(live_socket, (struct sockaddr *) &from, sizeof(from))) {
+ syslog(LOG_ERR, "bdump_get: connect: %m");
+ cleanup(server);
+ return;
+ }
+ if (setsockopt(live_socket, SOL_SOCKET, SO_KEEPALIVE, (char *)&on,
+ sizeof(on)) < 0)
+ syslog(LOG_WARNING, "bdump_get: setsockopt (SO_KEEPALIVE): %m");
+#if 1
+ zdbug((LOG_DEBUG, "bdump_get: connected"));
+#endif
+
+ /* Now begin the brain dump. */
+
+#ifdef HAVE_KRB4
+ /* send an authenticator */
+ if (get_tgt()) {
+ cleanup(server);
+ return;
+ }
+ retval = SendKerberosData(live_socket, &ticket, SERVER_SERVICE,
+ SERVER_INSTANCE);
+ if (retval != 0) {
+ syslog(LOG_ERR,"bdump_get: %s", error_message(retval));
+ cleanup(server);
+ return;
+ }
+#if 1
+ zdbug((LOG_DEBUG, "bdump_get: SendKerberosData ok"));
+#endif
+
+ /* get his authenticator */
+ retval = GetKerberosData(live_socket, from.sin_addr, &kdata,
+ SERVER_SERVICE, srvtab_file);
+ if (retval != KSUCCESS) {
+ syslog(LOG_ERR, "bdump_get getkdata: %s",krb_get_err_text(retval));
+ cleanup(server);
+ return;
+ }
+ /* my_realm is filled in inside get_tgt() */
+ if (strcmp(kdata.pname, SERVER_SERVICE) ||
+ strcmp(kdata.pinst, SERVER_INSTANCE) ||
+ strcmp(kdata.prealm, my_realm)) {
+ syslog(LOG_ERR, "bdump_get: peer not zephyr in lrealm: %s.%s@%s",
+ kdata.pname, kdata.pinst,kdata.prealm);
+ cleanup(server);
+ return;
+ }
+#endif /* HAVE_KRB4 */
+ retval = setup_file_pointers();
+ if (retval != 0) {
+ syslog(LOG_WARNING, "bdump_get: can't set up file pointers: %s",
+ error_message (retval));
+ cleanup(server);
+ return;
+ }
+ retval = bdump_recv_loop(server);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_WARNING, "bdump_get: bdump_recv_loop failed: %s",
+ error_message(retval));
+ cleanup(server);
+ return;
+ }
+#if 1
+ zdbug((LOG_DEBUG,"bdump_get: gbdl ok"));
+#endif
+ retval = bdump_send_loop(server);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_WARNING, "bdump_send_loop failed: %s",
+ error_message(retval));
+ cleanup(server);
+ return;
+ }
+#if 1
+ zdbug((LOG_DEBUG, "bdump_get: gbd finished"));
+#endif
+ /* set this guy to be up, and schedule a hello */
+ server->state = SERV_UP;
+ timer_reset(server->timer);
+ server->timer = timer_set_rel(0L, server_timo, server);
+
+#if 1
+ zdbug((LOG_DEBUG,"cleanup gbd"));
+#endif
+ shutdown_file_pointers();
+#ifdef _POSIX_VERSION
+ action.sa_handler = SIG_DFL;
+ sigaction(SIGPIPE, &action, NULL);
+#else
+ signal(SIGPIPE, SIG_DFL);
+#endif
+ bdumping = 0;
+ server->dumping = 0;
+ /* Now that we are finished dumping, send all the queued packets */
+ server_send_queue(server);
+
+ return;
+}
+
+void
+bdump_get(notice, auth, who, server)
+ ZNotice_t *notice;
+ int auth;
+ struct sockaddr_in *who;
+ Server *server;
+{
+ void (*proc) __P((ZNotice_t *, int, struct sockaddr_in *, Server *));
+
+ proc = NULL;
+
+#if 1
+ if (zdebug) {
+ syslog(LOG_DEBUG, "bdump_get: bdump v%s avail %s",
+ notice->z_class_inst, inet_ntoa(who->sin_addr));
+ }
+#endif
+ if (strcmp (notice->z_class_inst, "1.2") == 0)
+ proc = bdump_get_v12;
+
+ if (proc) {
+ (*proc)(notice, auth, who, server);
+ } else {
+ syslog(LOG_WARNING,
+ "bdump_get: Incompatible bdump version '%s' from %s",
+ notice->z_class_inst,
+ inet_ntoa(who->sin_addr));
+ }
+}
+
+/*
+ * Send a list off as the specified notice
+ */
+
+Code_t
+bdump_send_list_tcp(kind, addr, class_name, inst, opcode, sender, recip, lyst,
+ num)
+ ZNotice_Kind_t kind;
+ struct sockaddr_in *addr;
+ int num;
+ char *class_name, *inst, *opcode, *sender, *recip, **lyst;
+{
+ ZNotice_t notice;
+ char *pack, addrbuf[100];
+ int packlen, count;
+ Code_t retval;
+ u_short length;
+
+ retval = ZMakeAscii(addrbuf, sizeof(addrbuf),
+ (unsigned char *) &addr->sin_addr,
+ sizeof(struct in_addr));
+ if (retval != ZERR_NONE)
+ return retval;
+ notice.z_kind = kind;
+
+ notice.z_port = addr->sin_port;
+ notice.z_class = class_name;
+ notice.z_class_inst = inst;
+ notice.z_opcode = opcode;
+ notice.z_sender = sender;
+ notice.z_recipient = recip;
+ notice.z_default_format = "";
+ notice.z_num_other_fields = 1;
+ notice.z_other_fields[0] = addrbuf;
+
+ retval = ZFormatNoticeList(¬ice, lyst, num, &pack, &packlen, ZNOAUTH);
+ if (retval != ZERR_NONE)
+ return retval;
+
+ length = htons((u_short) packlen);
+
+ count = net_write(output, (char *) &length, sizeof(length));
+ if (count != sizeof(length)) {
+ if (count < 0) {
+ free(pack);
+ return(errno);
+ } else {
+ syslog(LOG_WARNING, "slt (length) xmit: %d vs %d",
+ sizeof(length), count);
+ free(pack);
+ return(ZSRV_PKSHORT);
+ }
+ }
+
+ count = net_write(output, pack, packlen);
+ if (count != packlen) {
+ if (count < 0) {
+ free(pack);
+ return(errno);
+ } else {
+ syslog(LOG_WARNING, "slt (packet) xmit: %d vs %d",
+ packlen, count);
+ free(pack);
+ return(ZSRV_PKSHORT);
+ }
+ }
+ free(pack);
+ return(ZERR_NONE);
+}
+
+static void
+shutdown_file_pointers() {
+ if (input) {
+ fclose(input);
+ input = 0;
+ }
+ if (output) {
+ fclose(output);
+ output = 0;
+ }
+ if (live_socket >= 0) {
+ close(live_socket);
+ live_socket = -1;
+ }
+}
+
+static void
+cleanup(server)
+ Server *server;
+{
+#ifdef _POSIX_VERSION
+ struct sigaction action;
+#endif
+
+#if 1
+ zdbug((LOG_DEBUG, "bdump cleanup"));
+#endif
+ if (server != limbo_server) {
+ server->state = SERV_DEAD;
+ timer_reset(server->timer);
+ server->timer = timer_set_rel(0L, server_timo, server);
+ }
+ shutdown_file_pointers ();
+#ifdef _POSIX_VERSION
+ action.sa_flags = 0;
+ sigemptyset(&action.sa_mask);
+ action.sa_handler = SIG_DFL;
+ sigaction(SIGPIPE,&action, NULL);
+#else
+ signal(SIGPIPE, SIG_DFL);
+#endif /* _POSIX_VERSION */
+ bdumping = 0;
+ server->dumping = 0;
+}
+
+#ifdef HAVE_KRB4
+int
+get_tgt()
+{
+ /* MIT Kerberos 4 get_svc_in_tkt() requires instance to be writable and
+ * at least INST_SZ bytes long. */
+ static char buf[INST_SZ + 1] = SERVER_INSTANCE;
+ int retval = 0;
+ CREDENTIALS cred;
+#ifndef NOENCRYPTION
+ Sched *s;
+#endif
+
+ if (!*my_realm) {
+ retval = krb_get_lrealm(my_realm, 1);
+ if (retval != KSUCCESS) {
+ syslog(LOG_ERR,"krb_get_lrealm: %s", krb_get_err_text(retval));
+ *my_realm = '\0';
+ return(1);
+ }
+ }
+ /* have they expired ? */
+ if (ticket_time < NOW - tkt_lifetime(TKTLIFETIME) + 15L) {
+ /* +15 for leeway */
+#if 0
+ zdbug((LOG_DEBUG,"get new tickets: %d %d %d", ticket_time, NOW,
+ NOW - tkt_lifetime(TKTLIFETIME) + 15L));
+#endif
+ dest_tkt();
+
+ retval = krb_get_svc_in_tkt(SERVER_SERVICE, buf, ZGetRealm(),
+ "krbtgt", ZGetRealm(),
+ TKTLIFETIME, srvtab_file);
+ if (retval != KSUCCESS) {
+ syslog(LOG_ERR,"get_tgt: krb_get_svc_in_tkt: %s",
+ krb_get_err_text(retval));
+ ticket_time = 0;
+ return(1);
+ } else {
+ ticket_time = NOW;
+ }
+
+#ifndef NOENCRYPTION
+ retval = read_service_key(SERVER_SERVICE, SERVER_INSTANCE,
+ ZGetRealm(), 0 /*kvno*/,
+ srvtab_file, serv_key);
+ if (retval != KSUCCESS) {
+ syslog(LOG_ERR, "get_tgt: read_service_key: %s",
+ krb_get_err_text(retval));
+ return 1;
+ }
+ s = (Sched *) check_key_sched_cache(serv_key);
+ if (s) {
+ serv_ksched = *s;
+ } else {
+ des_key_sched(serv_key, serv_ksched.s);
+ add_to_key_sched_cache(serv_key, &serv_ksched);
+ }
+#endif /* !NOENCRYPTION */
+ }
+ return(0);
+}
+#endif /* HAVE_KRB4 */
+
+/*
+ * The braindump offer wasn't taken, so we retract it.
+ */
+
+/*ARGSUSED*/
+static void
+close_bdump(arg)
+ void * arg;
+{
+ if (bdump_socket >= 0) {
+ FD_CLR(bdump_socket, &interesting);
+ close(bdump_socket);
+ nfds = srv_socket + 1;
+ bdump_socket = -1;
+#if 1
+ zdbug((LOG_DEBUG, "bdump not used"));
+#endif
+ } else {
+#if 1
+ zdbug((LOG_DEBUG, "bdump not open"));
+#endif
+ }
+ return;
+}
+
+/*
+ * Start receiving instruction notices from the brain dump socket
+ */
+
+static Code_t
+bdump_recv_loop(server)
+ Server *server;
+{
+ ZNotice_t notice;
+ ZPacket_t packet;
+ int len;
+ Code_t retval;
+ Client *client = NULL;
+ struct sockaddr_in who;
+#ifdef HAVE_KRB4
+ char *cp;
+ C_Block cblock;
+#endif /* HAVE_KRB4 */
+ Realm *realm = NULL;
+
+#if 1
+ zdbug((LOG_DEBUG, "bdump recv loop"));
+#endif
+
+ /* do the inverse of bdump_send_loop, registering stuff on the fly */
+ while (1) {
+ if (packets_waiting()) {
+ /* A non-braindump packet is waiting; handle it. */
+ bdumping = 0;
+ bdump_concurrent = 1;
+ handle_packet();
+ bdump_concurrent = 0;
+ bdumping = 1;
+ }
+ len = sizeof(packet);
+ retval = get_packet(packet, len, &len);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_ERR, "brl get pkt: %s", error_message(retval));
+ return retval;
+ }
+
+ retval = ZParseNotice(packet, len, ¬ice);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_ERR, "brl notice parse: %s", error_message(retval));
+ return retval;
+ }
+#if defined (DEBUG)
+ if (zdebug) {
+ char buf[4096];
+
+ sprintf(buf, "bdump:%s '%s' '%s' '%s' '%s' '%s'",
+ ZNoticeKinds[(int) notice.z_kind], notice.z_class,
+ notice.z_class_inst, notice.z_opcode, notice.z_sender,
+ notice.z_recipient);
+ syslog(LOG_DEBUG, buf);
+ }
+#endif /* DEBUG */
+ if (notice.z_num_other_fields >= 1) {
+ retval = ZReadAscii(notice.z_other_fields[0],
+ strlen(notice.z_other_fields[0]),
+ (unsigned char *) &who.sin_addr,
+ sizeof(struct in_addr));
+ if (retval != ZERR_NONE) {
+ syslog(LOG_ERR, "brl zreadascii failed: %s",
+ error_message(retval));
+ return retval;
+ }
+ } else {
+ who.sin_addr.s_addr = notice.z_sender_addr.s_addr;
+ }
+ who.sin_family = AF_INET;
+ who.sin_port = notice.z_port;
+
+ if (strcmp(notice.z_opcode, ADMIN_DONE) == 0) {
+ /* end of brain dump */
+ return ZERR_NONE;
+ } else if (strcmp(notice.z_opcode, ADMIN_NEWREALM) == 0) {
+ /* get a realm from the message */
+ realm = realm_get_realm_by_name(notice.z_message);
+ if (!realm)
+ return(ZERR_NONE);
+ } else if (strcmp(notice.z_class, LOGIN_CLASS) == 0) {
+ /* 1 = tell it we are authentic */
+ retval = ulogin_dispatch(¬ice, 1, &who, server);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_ERR, "brl ul_disp failed: %s",
+ error_message(retval));
+ return retval;
+ }
+ } else if (strcmp(notice.z_opcode, ADMIN_NEWCLT) == 0) {
+ /* a new client */
+ notice.z_port = htons((u_short) atoi(notice.z_message));
+ retval = client_register(¬ice, &who.sin_addr, &client, 0);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_ERR,"brl failed: %s", error_message(retval));
+ return retval;
+ }
+#ifdef HAVE_KRB4
+ memset(client->session_key, 0, sizeof(C_Block));
+ if (*notice.z_class_inst) {
+ /* a C_Block is there */
+ cp = notice.z_message + strlen(notice.z_message) + 1;
+ retval = ZReadAscii(cp, strlen(cp), cblock, sizeof(C_Block));
+ if (retval != ZERR_NONE) {
+ syslog(LOG_ERR,"brl bad cblk read: %s (%s)",
+ error_message(retval), cp);
+ } else {
+#ifdef NOENCRYPTION
+ memcpy(cblock, client->session_key, sizeof(C_Block));
+#else
+ des_ecb_encrypt(cblock, client->session_key, serv_ksched.s,
+ DES_DECRYPT);
+#endif
+ }
+ }
+#endif /* HAVE_KRB4 */
+ } else if (strcmp(notice.z_opcode, CLIENT_SUBSCRIBE) == 0) {
+ /* a subscription packet */
+ if (!client) {
+ syslog(LOG_ERR, "brl no client");
+ return ZSRV_NOCLT;
+ }
+ retval = subscr_subscribe(client, ¬ice);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_WARNING, "brl subscr failed: %s",
+ error_message(retval));
+ return retval;
+ }
+ } else if (strcmp(notice.z_opcode, REALM_SUBSCRIBE) == 0) {
+ /* add a subscription for a realm */
+ if (!realm) {
+ syslog(LOG_ERR, "brl no realm");
+ return(ZSRV_NORLM);
+ }
+ retval = subscr_realm(realm, ¬ice);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_WARNING, "brl subscr failed: %s",
+ error_message(retval));
+ return retval;
+ }
+ } else {
+ syslog(LOG_ERR, "brl bad opcode %s",notice.z_opcode);
+ return ZSRV_UNKNOWNOPCODE;
+ }
+ }
+}
+
+/*
+ * Send all the state to the peer.
+ */
+
+static Code_t
+bdump_send_loop(server)
+ Server *server;
+{
+ Code_t retval;
+
+#if 1
+ zdbug((LOG_DEBUG, "bdump send loop"));
+#endif
+
+ retval = uloc_send_locations();
+ if (retval != ZERR_NONE)
+ return retval;
+ retval = client_send_clients();
+ if (retval != ZERR_NONE)
+ return retval;
+ retval = realm_send_realms();
+ if (retval != ZERR_NONE)
+ return retval;
+ return send_done();
+}
+
+/*
+ * Send a sync indicating end of this host
+ */
+
+static Code_t
+send_done()
+{
+ Code_t retval;
+
+#if 1
+ zdbug((LOG_DEBUG, "send_done"));
+#endif
+ retval = send_normal_tcp(SERVACK, bdump_sin.sin_port, ZEPHYR_ADMIN_CLASS,
+ "", ADMIN_DONE, myname, "", NULL, 0);
+ return retval;
+}
+
+
+/*
+ * Send a list off as the specified notice
+ */
+
+static Code_t
+send_list(kind, port, class_name, inst, opcode, sender, recip, lyst, num)
+ ZNotice_Kind_t kind;
+ int port, num;
+ char *class_name, *inst, *opcode, *sender, *recip, **lyst;
+{
+ ZNotice_t notice;
+ char *pack;
+ int packlen;
+ Code_t retval;
+
+ notice.z_kind = kind;
+ notice.z_port = port;
+ notice.z_class = class_name;
+ notice.z_class_inst = inst;
+ notice.z_opcode = opcode;
+ notice.z_sender = sender;
+ notice.z_recipient = recip;
+ notice.z_default_format = "";
+ notice.z_num_other_fields = 0;
+
+ retval = ZFormatNoticeList(¬ice, lyst, num, &pack, &packlen, ZNOAUTH);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_WARNING, "sl format: %s", error_message(retval));
+ return retval;
+ }
+
+ retval = ZSendPacket(pack, packlen, 0);
+ if (retval != ZERR_NONE)
+ syslog(LOG_WARNING, "sl xmit: %s", error_message(retval));
+ free(pack);
+ return retval;
+}
+
+/*
+ * Send a message off as the specified notice, via TCP
+ */
+
+static Code_t
+send_normal_tcp(kind, port, class_name, inst, opcode, sender, recip,
+ message, len)
+ ZNotice_Kind_t kind;
+ int port, len;
+ char *class_name, *inst, *opcode, *sender, *recip, *message;
+{
+ ZNotice_t notice;
+ char *pack;
+ int packlen, count;
+ Code_t retval;
+ u_short length;
+
+ notice.z_kind = kind;
+ notice.z_port = port;
+ notice.z_class = class_name;
+ notice.z_class_inst = inst;
+ notice.z_opcode = opcode;
+ notice.z_sender = sender;
+ notice.z_recipient = recip;
+ notice.z_default_format = "";
+ notice.z_message = message;
+ notice.z_message_len = len;
+ notice.z_num_other_fields = 0;
+
+ retval = ZFormatNotice(¬ice, &pack, &packlen, ZNOAUTH);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_WARNING, "sn format: %s", error_message(retval));
+ return retval;
+ }
+
+ length = htons((u_short) packlen);
+
+ count = net_write(output, (char *) &length, sizeof(length));
+ if (count != sizeof(length)) {
+ if (count < 0) {
+ syslog(LOG_WARNING, "snt xmit/len: %m");
+ free(pack);
+ return errno;
+ } else {
+ syslog(LOG_WARNING, "snt xmit: %d vs %d",sizeof(length),count);
+ free(pack);
+ return ZSRV_LEN;
+ }
+ }
+ count = net_write(output, pack, packlen);
+ if (count != packlen) {
+ if (count < 0) {
+ syslog(LOG_WARNING, "snt xmit: %m");
+ free(pack);
+ return errno;
+ } else {
+ syslog(LOG_WARNING, "snt xmit: %d vs %d",packlen, count);
+ free(pack);
+ return ZSRV_LEN;
+ }
+ }
+ free(pack);
+ return ZERR_NONE;
+}
+
+/*
+ * get a packet from the TCP socket
+ * return 0 if successful, error code else
+ */
+
+static Code_t
+get_packet(packet, len, retlen)
+ void *packet;
+ int len;
+ int *retlen;
+{
+ u_short length;
+ int result;
+
+ result = net_read(input, (char *) &length, sizeof(u_short));
+ if (result < sizeof(short)) {
+ if (result < 0) {
+ return errno;
+ } else {
+ syslog(LOG_ERR, "get_pkt len: %d vs %d (%m)", result,
+ sizeof(short));
+ return ZSRV_LEN;
+ }
+ }
+
+ length = ntohs(length);
+ if (len < length)
+ return ZSRV_BUFSHORT;
+ result = net_read(input, packet, (int) length);
+ if (result < length) {
+ if (result < 0) {
+ return errno;
+ } else {
+ syslog(LOG_ERR, "get_pkt: %d vs %d (%m)", result, length);
+ return ZSRV_LEN;
+ }
+ }
+ *retlen = length;
+ return ZERR_NONE;
+}
+
+static Code_t
+extract_sin(notice, target)
+ ZNotice_t *notice;
+ struct sockaddr_in *target;
+{
+ char *cp = notice->z_message;
+ char *buf;
+
+ buf = cp;
+ if (!notice->z_message_len || *buf == '\0') {
+#if 0
+ zdbug((LOG_DEBUG,"no addr"));
+#endif
+ return ZSRV_PKSHORT;
+ }
+ target->sin_addr.s_addr = inet_addr(cp);
+
+ cp += (strlen(cp) + 1); /* past the null */
+ if ((cp >= notice->z_message + notice->z_message_len) || (*cp == '\0')) {
+#if 0
+ zdbug((LOG_DEBUG, "no port"));
+#endif
+ return(ZSRV_PKSHORT);
+ }
+ target->sin_port = htons((u_short) atoi(cp));
+ target->sin_family = AF_INET;
+ return ZERR_NONE;
+}
+
+static int
+net_read(f, buf, len)
+ FILE *f;
+ char *buf;
+ int len;
+{
+ int cc, len2 = 0;
+
+ fflush (output);
+ do {
+ errno = 0;
+ cc = fread(buf, 1, len, f);
+ if (cc == 0)
+ return -1;
+ buf += cc;
+ len2 += cc;
+ len -= cc;
+ } while (len > 0);
+ return len2;
+}
+
+static int
+net_write(f, buf, len)
+ FILE *f;
+ char *buf;
+ int len;
+{
+ int cc;
+ int wrlen = len;
+ do {
+ cc = fwrite (buf, 1, wrlen, f);
+ if (cc == 0)
+ return -1;
+ buf += cc;
+ wrlen -= cc;
+ } while (wrlen > 0);
+ return len;
+}
+
+static int
+setup_file_pointers ()
+{
+ int fd;
+
+ input = fdopen (live_socket, "r");
+ if (!input)
+ return errno;
+
+ fd = dup (live_socket);
+ if (fd < 0)
+ return errno;
+ output = fdopen (fd, "w");
+ if (!output)
+ return errno;
+
+ return 0;
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains functions for the Zephyr server class manager subsystem.
+ *
+ * Created by: John T. Kohl
+ *
+ * $Id: class.c,v 1.26 1999/01/22 23:19:40 ghudson Exp $
+ *
+ * Copyright (c) 1987 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <zephyr/mit-copyright.h>
+#include "zserver.h" /* includes zephyr/zephyr.h */
+#include <assert.h>
+
+#if !defined (lint) && !defined (SABER)
+static const char rcsid_class_c[] =
+"$Id: class.c,v 1.26 1999/01/22 23:19:40 ghudson Exp $";
+#endif
+
+/*
+ * Class manager subsystem.
+ *
+ *
+ * External functions are:
+ *
+ * Code_t triplet_register(client, subs, realm)
+ *
+ * Code_t triplet_deregister(client, subs, realm)
+ *
+ * Client *triplet_lookup(subs)
+ * Client *client;
+ * Destlist *subs;
+ *
+ * Acl *class_get_acl(class_name)
+ * String *class_name;
+ *
+ * Code_t class_restrict(class_name, acl)
+ * char *class_name;
+ * Acl *acl;
+ *
+ * Code_t class_setup_restricted(class_name, acl)
+ * char *class_name;
+ * Acl *acl;
+ *
+ * and several Destination methods.
+ */
+
+/*
+ * The data structure used for the class manager is an array of hash buckets
+ * each containing a pointer to a doubly linked circular list (in the style
+ * of insque/remque). Each element of this list contains a class.instance
+ * name (which hashes into the bucket associated with this list) and a
+ * doubly linked list of clients which are interested in this class.
+ * The data pointed to by these clients is owned by other modules. Care
+ * must be taken by the caller not to a free()'d client
+ * structure.
+ *
+ * If any hash bucket is empty, the pointer is null.
+ *
+ * The first element in the hash bucket is a special header unused for
+ * storing classes, and is used for finding the end of the list.
+ *
+ * If any list of interested clients is empty, the class name is garbage
+ * collected, unless the class has been registered as restricted.
+ */
+
+/* Private variables */
+#define EMPTY_CLASS 2000
+
+#define ALLOC_OFFSET 8 /* Allocate 32 bytes less than a power of 2. */
+#define ALLOC_INIT 8 /* Initial number of subscriptions. */
+
+#define HASHSIZE 1023
+#define HASHVAL(c, i, r) (((c)->hash_val ^ (i)->hash_val ^ (r)->hash_val) \
+ % HASHSIZE)
+#define DEST_HASHVAL(dest) HASHVAL((dest).classname, (dest).inst, (dest).recip)
+
+static Triplet *triplet_bucket[HASHSIZE]; /* the hash table of pointers */
+
+static Code_t remove_client __P((Triplet *triplet, Client *client,
+ Realm *realm));
+static Code_t insert_client __P((Triplet *triplet, Client *client,
+ Realm *realm));
+static Triplet *triplet_alloc __P((String *classname, String *inst,
+ String *recipient));
+static void free_triplet __P((Triplet *));
+
+/* public routines */
+
+/*
+ * Determine if two destination triplets are equal. Note the backup
+ * case-insensitive recipient check in the third term. Recipients are
+ * not downcased at subscription time (in order to preserve case for,
+ * say, "zctl ret"), but traditional zephyr server behavior has not
+ * been case-sensitive in the recipient string. In most cases, a
+ * failed match will fail on the classname or instance, and a successful
+ * match will succeed on the (d1->recip == d2->recip) check, so this
+ * shouldn't affect performance.
+ */
+
+int ZDest_eq(d1, d2)
+ Destination *d1, *d2;
+{
+ return((d1->classname == d2->classname) &&
+ (d1->inst == d2->inst) &&
+ (d1->recip == d2->recip ||
+ strcasecmp(d1->recip->string, d2->recip->string) == 0));
+}
+
+
+/* the client as interested in a triplet */
+
+Code_t
+triplet_register(client, dest, realm)
+ Client *client;
+ Destination *dest;
+ Realm *realm;
+{
+ Triplet *triplet;
+ unsigned long hashval;
+
+ hashval = DEST_HASHVAL(*dest);
+ for (triplet = triplet_bucket[hashval]; triplet; triplet = triplet->next) {
+ if (ZDest_eq(&triplet->dest, dest))
+ return insert_client(triplet, client, realm);
+ }
+
+ /* Triplet not present in hash table, insert it. */
+ triplet = triplet_alloc(dest->classname, dest->inst, dest->recip);
+ LIST_INSERT(&triplet_bucket[hashval], triplet);
+ return insert_client(triplet, client, realm);
+}
+
+/* dissociate client from the class, garbage collecting if appropriate */
+
+Code_t
+triplet_deregister(client, dest, realm)
+ Client *client;
+ Destination *dest;
+ Realm *realm;
+{
+ Triplet *triplet;
+ int retval;
+ unsigned long hashval;
+
+#if 0
+ zdbug((LOG_DEBUG, "class_dereg: %s %s", dest->classname->string,
+ dest->inst->string));
+#endif
+ hashval = DEST_HASHVAL(*dest);
+ for (triplet = triplet_bucket[hashval]; triplet; triplet = triplet->next) {
+ if (ZDest_eq(&triplet->dest, dest)) {
+ retval = remove_client(triplet, client, realm);
+ if (retval != ZERR_NONE)
+ return retval;
+ if (*triplet->clients == NULL && !triplet->acl) {
+ LIST_DELETE(triplet);
+ free_triplet(triplet);
+ }
+ return ZERR_NONE;
+ }
+ }
+ return(ZSRV_BADASSOC);
+}
+
+/* return a linked list of what clients are interested in this triplet */
+
+Client **
+triplet_lookup(dest)
+ Destination *dest;
+{
+ Triplet *triplet;
+ unsigned long hashval;
+
+ hashval = DEST_HASHVAL(*dest);
+ for (triplet = triplet_bucket[hashval]; triplet; triplet = triplet->next) {
+ if (ZDest_eq(&triplet->dest, dest))
+ return triplet->clients;
+ }
+ return NULL;
+}
+
+/*
+ * return the acl structure associated with class, or NULL if there is
+ * no such acl struct
+ */
+
+Acl *
+class_get_acl(class_name)
+ String *class_name;
+{
+ Triplet *triplet;
+ unsigned long hashval;
+
+ hashval = HASHVAL(class_name, empty, empty);
+ for (triplet = triplet_bucket[hashval]; triplet; triplet = triplet->next) {
+ if (triplet->dest.classname == class_name &&
+ triplet->dest.inst == empty && triplet->dest.recip == empty)
+ return triplet->acl;
+ }
+
+ /* No acl found, not restricted. */
+ return NULL;
+}
+
+/*
+ * restrict class by associating it with the acl structure acl.
+ * return ZERR_NONE if no error, or ZSRV_NOCLASS if there is no such
+ * class, or ZSRV_CLASSRESTRICTED if it is already restricted.
+ */
+
+Code_t
+class_restrict(class_name, acl)
+ char *class_name;
+ Acl *acl;
+{
+ Triplet *triplet;
+ String *d;
+ unsigned long hashval;
+
+ d = make_string(class_name,1);
+ hashval = HASHVAL(d, empty, empty);
+ for (triplet = triplet_bucket[hashval]; triplet; triplet = triplet->next) {
+ if (triplet->dest.classname == d && triplet->dest.inst == empty &&
+ triplet->dest.recip == empty) {
+ if (triplet->acl)
+ return ZSRV_CLASSRESTRICTED;
+ triplet->acl = acl;
+ free_string(d);
+ return ZERR_NONE;
+ }
+ }
+
+ free_string(d);
+ return ZSRV_NOCLASS;
+}
+
+/*
+ * restrict class by registering it and associating it with the acl
+ * structure acl. return ZERR_NONE if no error, or ZSRV_CLASSXISTS
+ * if the class is already registered, or ENOMEM in case of malloc failure.
+ */
+
+Code_t
+class_setup_restricted(class_name, acl)
+ char *class_name;
+ Acl *acl;
+{
+ Triplet *triplet;
+ String *d;
+ unsigned long hashval;
+
+ d = make_string(class_name,1);
+ hashval = HASHVAL(d, empty, empty);
+ for (triplet = triplet_bucket[hashval]; triplet; triplet = triplet->next) {
+ if (triplet->dest.classname == d && triplet->dest.inst == empty &&
+ triplet->dest.recip == d) {
+ free_string(d);
+ return ZSRV_CLASSXISTS;
+ }
+ }
+
+ /* Triplet not present in hash table, insert it. */
+ triplet = triplet_alloc(d, empty, empty);
+ free_string(d);
+ if (!triplet)
+ return ENOMEM;
+ triplet->acl = acl;
+ LIST_INSERT(&triplet_bucket[hashval], triplet);
+ return ZERR_NONE;
+}
+
+/* private routines */
+
+/* allocate space for a class structure */
+
+static Triplet *
+triplet_alloc(classname,inst,recipient)
+ String *classname, *inst, *recipient;
+{
+ Triplet *triplet;
+ Client *clist;
+
+ triplet = (Triplet *) malloc(sizeof(Triplet));
+ if (!triplet)
+ return NULL;
+
+ triplet->dest.classname = dup_string(classname);
+ triplet->dest.inst = dup_string(inst);
+ triplet->dest.recip = dup_string(recipient);
+ triplet->clients = NULL;
+ triplet->acl = NULL;
+
+ return triplet;
+}
+
+/* insert a client into the list associated with the class *ptr */
+
+static Code_t
+insert_client(triplet, client, realm)
+ Triplet *triplet;
+ Client *client;
+ Realm *realm;
+{
+ Client **clientp, **newclients;
+ int new_size;
+
+ if (triplet->clients) {
+ /* Avoid duplication. */
+ for (clientp = triplet->clients; *clientp; clientp++) {
+ if (*clientp == client || (realm && (*clientp)->realm == realm))
+ return ZSRV_CLASSXISTS;
+ }
+
+ if (clientp + 1 - triplet->clients >= triplet->clients_size) {
+ new_size = triplet->clients_size * 2 + ALLOC_OFFSET;
+ newclients = (Client **) realloc(triplet->clients,
+ new_size * sizeof(Client *));
+ if (newclients == NULL)
+ return ENOMEM;
+ clientp = newclients + (clientp - triplet->clients);
+ triplet->clients = newclients;
+ triplet->clients_size = new_size;
+ }
+ } else {
+ /* Allocate an initial list of client pointers. */
+ triplet->clients = (Client **) malloc(ALLOC_INIT * sizeof(Client *));
+ if (triplet->clients == NULL)
+ return ENOMEM;
+ triplet->clients_size = ALLOC_INIT;
+ clientp = triplet->clients;
+ }
+
+ *clientp = client;
+ clientp[1] = NULL;
+ return ZERR_NONE;
+}
+
+/*
+ * remove the client from the list associated with class *ptr, garbage
+ * collecting if appropriate
+ */
+
+static Code_t remove_client(triplet, client, realm)
+ Triplet *triplet;
+ Client *client;
+ Realm *realm;
+{
+ Client **clientp;
+
+ for (clientp = triplet->clients; *clientp; clientp++) {
+ if (*clientp == client || (realm && (*clientp)->realm == realm)) {
+ for (; *clientp; clientp++)
+ *clientp = clientp[1];
+ return ZERR_NONE;
+ }
+ }
+
+ return ZSRV_BADASSOC;
+}
+
+static void free_triplet(triplet)
+ Triplet *triplet;
+{
+ if (triplet->clients)
+ free(triplet->clients);
+ free_string(triplet->dest.classname);
+ free_string(triplet->dest.inst);
+ free_string(triplet->dest.recip);
+ free(triplet);
+}
+
+void triplet_dump_subs(fp)
+ FILE *fp;
+{
+ int i;
+ Triplet *triplet;
+ Client **clientp;
+
+ for (i = 0; i < HASHSIZE; i++) {
+ for (triplet = triplet_bucket[i]; triplet; triplet = triplet->next) {
+ fputs("Triplet '", fp);
+ dump_quote(triplet->dest.classname->string, fp);
+ fputs("' '", fp);
+ dump_quote(triplet->dest.inst->string, fp);
+ fputs("' '", fp);
+ dump_quote(triplet->dest.recip->string, fp);
+ fputs("':\n", fp);
+ if (triplet->clients) {
+ for (clientp = triplet->clients; *clientp; clientp++) {
+ fprintf(fp, "\t%s %d (%s)\n",
+ inet_ntoa((*clientp)->addr.sin_addr),
+ ntohs((*clientp)->addr.sin_port),
+ (*clientp)->principal->string);
+ }
+ }
+ }
+ }
+}
+
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains functions for the Client Manager subsystem of the Zephyr server.
+ *
+ * Created by: John T. Kohl
+ *
+ * $Id: client.c,v 1.34 1999/01/22 23:19:41 ghudson Exp $
+ *
+ * Copyright (c) 1987,1988,1991 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <zephyr/mit-copyright.h>
+#include "zserver.h"
+#include <sys/socket.h>
+
+#if !defined (lint) && !defined (SABER)
+static const char rcsid_client_c[] =
+"$Id: client.c,v 1.34 1999/01/22 23:19:41 ghudson Exp $";
+#endif
+
+/*
+ * External functions:
+ *
+ * Code_t client_register(notice, who, client, server, wantdefaults)
+ * ZNotice_t *notice;
+ * struct sockaddr_in *who;
+ * Client **client; (RETURN)
+ * Server *server;
+ * int wantdefaults;
+ *
+ * Code_t client_deregister(client, host, flush)
+ * Client *client;
+ * Host *host;
+ * int flush;
+ *
+ * Client *client_find(who, unsigned int port)
+ * struct in_addr *host;
+ * unsigned int port;
+ *
+ * void client_dump_clients(fp, clist)
+ * FILE *fp;
+ * Client *clist;
+ */
+
+/*
+ * a client: allocate space, find or insert the address in the
+ * server's list of hosts, initialize and insert the client into
+ * the host's list of clients.
+ *
+ * This routine assumes that the client has not been registered yet.
+ * The caller should check by calling client_find.
+ */
+
+#define HASHSIZE 1024
+static Client *client_bucket[HASHSIZE];
+
+#define INET_HASH(host, port) ((htonl((host)->s_addr) + \
+ htons((unsigned short) (port))) % HASHSIZE)
+
+Code_t
+client_register(notice, host, client_p, wantdefaults)
+ ZNotice_t *notice;
+ struct in_addr *host;
+ Client **client_p;
+ int wantdefaults;
+{
+ Client *client;
+ Code_t retval;
+
+ /* chain the client's host onto this server's host list */
+
+#if 1
+ zdbug((LOG_DEBUG, "client_register: adding %s at %s/%d",
+ notice->z_sender, inet_ntoa(*host), ntohs(notice->z_port)));
+#endif
+
+ if (!notice->z_port)
+ return ZSRV_BADSUBPORT;
+
+ *client_p = client = client_find(host, notice->z_port);
+ if (!client) {
+ *client_p = client = (Client *) malloc(sizeof(Client));
+ if (!client)
+ return ENOMEM;
+ memset(&client->addr, 0, sizeof(struct sockaddr_in));
+#ifdef KERBEROS
+ memset(&client->session_key, 0, sizeof(client->session_key));
+#endif
+ client->last_send = 0;
+ client->last_ack = NOW;
+ client->addr.sin_family = AF_INET;
+ client->addr.sin_addr.s_addr = host->s_addr;
+ client->addr.sin_port = notice->z_port;
+ client->subs = NULL;
+ client->realm = NULL;
+ client->principal = make_string(notice->z_sender, 0);
+ LIST_INSERT(&client_bucket[INET_HASH(&client->addr.sin_addr,
+ notice->z_port)], client);
+ }
+
+ /* Add default subscriptions only if this is not resulting from a brain
+ * dump, AND this request wants defaults. */
+ if (!bdumping && wantdefaults)
+ return subscr_def_subs(client);
+ else
+ return ZERR_NONE;
+}
+
+/*
+ * Deregister the client, freeing resources.
+ * Remove any packets in the nack queue, release subscriptions, release
+ * locations, and dequeue him from the host.
+ */
+
+void
+client_deregister(client, flush)
+ Client *client;
+ int flush;
+{
+ LIST_DELETE(client);
+ nack_release(client);
+ subscr_cancel_client(client);
+ free_string(client->principal);
+ if (flush)
+ uloc_flush_client(&client->addr);
+ free(client);
+}
+
+void
+client_flush_host(host)
+ struct in_addr *host;
+{
+ int i;
+ Client *client, *next;
+
+ for (i = 0; i < HASHSIZE; i++) {
+ for (client = client_bucket[i]; client; client = next) {
+ next = client->next;
+ if (client->addr.sin_addr.s_addr == host->s_addr)
+ client_deregister(client, 1);
+ }
+ }
+ uloc_hflush(host);
+}
+
+Code_t
+client_send_clients()
+{
+ int i;
+ Client *client;
+ Code_t retval;
+
+ for (i = 0; i < HASHSIZE; i++) {
+ /* Allow packets to be processed between rows of the hash table. */
+ if (packets_waiting()) {
+ bdumping = 0;
+ bdump_concurrent = 1;
+ handle_packet();
+ bdump_concurrent = 0;
+ bdumping = 1;
+ }
+ for (client = client_bucket[i]; client; client = client->next) {
+ if (client->subs) {
+ retval = subscr_send_subs(client);
+ if (retval != ZERR_NONE)
+ return retval;
+ }
+ }
+ }
+ return ZERR_NONE;
+}
+
+/*
+ * dump info about clients in this clist onto the fp.
+ * assumed to be called with SIGFPE blocked
+ * (true if called from signal handler)
+ */
+
+void
+client_dump_clients(fp)
+ FILE *fp;
+{
+ Client *client;
+ int i;
+
+ for (i = 0; i < HASHSIZE; i++) {
+ for (client = client_bucket[i]; client; client = client->next) {
+ fprintf(fp, "%s/%d (%s):\n", inet_ntoa(client->addr.sin_addr),
+ ntohs(client->addr.sin_port), client->principal->string);
+ subscr_dump_subs(fp, client->subs);
+ }
+ }
+}
+
+/*
+ * find a client by host and port
+ */
+
+Client *
+client_find(host, port)
+ struct in_addr *host;
+ unsigned int port;
+{
+ Client *client;
+ long hashval;
+
+ hashval = INET_HASH(host, port);
+ for (client = client_bucket[hashval]; client; client = client->next) {
+ if (client->addr.sin_addr.s_addr == host->s_addr
+ && client->addr.sin_port == port)
+ return client;
+ }
+ return NULL;
+}
+
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains functions for general use within the Zephyr server.
+ *
+ * Created by: John T. Kohl
+ *
+ * $Id: common.c,v 1.13 1999/01/22 23:19:42 ghudson Exp $
+ *
+ * Copyright (c) 1987 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <zephyr/mit-copyright.h>
+#include "zserver.h"
+
+#ifndef lint
+#ifndef SABER
+static const char rcsid_common_c[] =
+ "$Id: common.c,v 1.13 1999/01/22 23:19:42 ghudson Exp $";
+#endif /* SABER */
+#endif /* lint */
+
+/* common routines for the server */
+
+/* copy a string into a newly allocated area */
+
+char *
+strsave (sp)
+ const char *sp;
+{
+ char *ret;
+
+ ret = (char *) malloc(strlen(sp) + 1);
+ if (!ret) {
+ syslog(LOG_CRIT, "no mem strdup'ing");
+ abort();
+ }
+ strcpy(ret, sp);
+ return ret;
+}
+
+/* The "& 0x5f" provides case-insensitivity for ASCII. */
+
+unsigned long
+hash(string)
+ const char *string;
+{
+ unsigned long hval = 0;
+ char cp;
+
+ while (1) {
+ cp = *string++;
+ if (!cp)
+ break;
+ hval += cp & 0x5f;
+
+ cp = *string++;
+ if (!cp)
+ break;
+ hval += (cp & 0x5f) * (3 + (1 << 16));
+
+ cp = *string++;
+ if (!cp)
+ break;
+ hval += (cp & 0x5f) * (1 + (1 << 8));
+
+ cp = *string++;
+ if (!cp)
+ break;
+ hval += (cp & 0x5f) * (1 + (1 << 12));
+
+ cp = *string++;
+ if (!cp)
+ break;
+ hval += (cp & 0x5f) * (1 + (1 << 4));
+
+ hval += ((long) hval) >> 18;
+ }
+
+ hval &= 0x7fffffff;
+ return hval;
+}
+
+/* Output a name, replacing newlines with \n and single quotes with \q. */
+void dump_quote(p, fp)
+ char *p;
+ FILE *fp;
+{
+ for (; *p; p++) {
+ if (*p == '\'') {
+ putc('\\', fp);
+ putc('q', fp);
+ } else if (*p == '\n') {
+ putc('\\', fp);
+ putc('n', fp);
+ } else {
+ putc(*p, fp);
+ }
+ }
+}
+
--- /dev/null
+operations,message,*
+message,personal,%me%
+message,urgent,%me%
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains functions for dispatching a notice.
+ *
+ * Created by: John T. Kohl
+ *
+ * $Id: dispatch.c,v 1.60 2000/02/11 20:59:36 ghudson Exp $
+ *
+ * Copyright (c) 1987, 1991 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <zephyr/mit-copyright.h>
+#include "zserver.h"
+#include <sys/socket.h>
+
+#ifndef lint
+#ifndef SABER
+static const char rcsid_dispatch_c[] =
+"$Id: dispatch.c,v 1.60 2000/02/11 20:59:36 ghudson Exp $";
+#endif
+#endif
+
+#define NACKTAB_HASHSIZE 1023
+#define NACKTAB_HASHVAL(sockaddr, uid) (((sockaddr).sin_addr.s_addr ^ \
+ (sockaddr).sin_port ^ \
+ (uid).zuid_addr.s_addr ^ \
+ (uid).tv.tv_sec ^ \
+ (uid).tv.tv_usec) % NACKTAB_HASHSIZE)
+#define HOSTS_SIZE_INIT 256
+
+#ifdef DEBUG
+ZCONST char *ZNoticeKinds[9] = {"UNSAFE", "UNACKED", "ACKED", "HMACK",
+ "HMCTL", "SERVACK", "SERVNAK", "CLIENTACK",
+ "STAT"};
+#endif
+/*
+ *
+ * External Routines:
+ *
+ * void dispatch(notice, auth, who)
+ * ZNotice_t *notice;
+ * int auth;
+ * struct sockaddr_in *who;
+ *
+ * void clt_ack(notice, who, sent)
+ * ZNotice_t *notice;
+ * struct sockaddr_in *who;
+ * Sent_type sent;
+ *
+ * void nack_release(client)
+ * Client *client;
+ *
+ * void sendit(notice, auth, who)
+ * ZNotice_t *notice;
+ * int auth;
+ * struct sockaddr_in *who;
+ *
+ * void xmit(notice, dest, auth, client)
+ * ZNotice_t *notice;
+ * struct sockaddr_in *dest;
+ * int auth;
+ * Client *client;
+ */
+
+
+String *class_control, *class_admin, *class_hm, *class_ulogin, *class_ulocate;
+
+int rexmit_times[] = REXMIT_TIMES;
+
+static void nack_cancel __P((ZNotice_t *, struct sockaddr_in *));
+static void dispatch __P((ZNotice_t *, int, struct sockaddr_in *, int));
+static int send_to_dest __P((ZNotice_t *, int, Destination *dest, int, int));
+static void hostm_deathgram __P((struct sockaddr_in *, Server *));
+static char *hm_recipient __P((void));
+
+Statistic realm_notices = {0, "inter-realm notices"};
+Statistic interserver_notices = {0, "inter-server notices"};
+Statistic hm_packets = {0, "hostmanager packets"};
+Statistic control_notices = {0, "client control notices"};
+Statistic message_notices = {0, "message notices"};
+Statistic login_notices = {0, "login notices"};
+Statistic i_s_ctls = {0, "inter-server control notices"};
+Statistic i_s_logins = {0, "inter-server login notices"};
+Statistic i_s_admins = {0, "inter-server admin notices"};
+Statistic i_s_locates = {0, "inter-server locate notices"};
+Statistic locate_notices = {0, "locate notices"};
+Statistic admin_notices = {0, "admin notices"};
+
+static Unacked *nacktab[NACKTAB_HASHSIZE];
+static struct in_addr *hosts;
+static int hosts_size = 0, num_hosts = 0;
+
+static void
+dump_stats (arg)
+ void *arg;
+{
+ syslog(LOG_INFO, "stats: %s: %d", hm_packets.str, hm_packets.val);
+ syslog(LOG_INFO, "stats: %s: %d", control_notices.str,
+ control_notices.val);
+ syslog(LOG_INFO, "stats: %s: %d", message_notices.str,
+ message_notices.val);
+ syslog(LOG_INFO, "stats: %s: %d", login_notices.str, login_notices.val);
+ syslog(LOG_INFO, "stats: %s: %d", locate_notices.str, locate_notices.val);
+ syslog(LOG_INFO, "stats: %s: %d", admin_notices.str, admin_notices.val);
+ syslog(LOG_INFO, "stats: %s: %d", realm_notices.str, realm_notices.val);
+ syslog(LOG_INFO, "stats: %s: %d", interserver_notices.str,
+ interserver_notices.val);
+ syslog(LOG_INFO, "stats: %s: %d", i_s_ctls.str, i_s_ctls.val);
+ syslog(LOG_INFO, "stats: %s: %d", i_s_logins.str, i_s_logins.val);
+ syslog(LOG_INFO, "stats: %s: %d", i_s_admins.str, i_s_admins.val);
+ syslog(LOG_INFO, "stats: %s: %d", i_s_locates.str, i_s_locates.val);
+
+ /* log stuff once an hour */
+ timer_set_rel ((long) 6*60*60, dump_stats, arg);
+}
+
+/*
+ * Handle an input packet.
+ * Warning: this function may be called from within a brain dump.
+ */
+
+void
+handle_packet()
+{
+ Code_t status;
+ ZPacket_t input_packet; /* from the network */
+ ZNotice_t new_notice; /* parsed from input_packet */
+ int input_len; /* len of packet */
+ struct sockaddr_in input_sin; /* Zconstructed for authent */
+ struct sockaddr_in whoisit; /* for holding peer's address */
+ int authentic; /* authentic flag */
+ Pending *pending; /* pending packet */
+ int from_server; /* packet is from another server */
+ Realm *realm; /* foreign realm ptr */
+#ifdef DEBUG
+ static int first_time = 1;
+#endif
+
+#ifdef DEBUG
+ /* Dump statistics five minutes after startup */
+ if (first_time) {
+ first_time = 0;
+ timer_set_rel(5*60, dump_stats, NULL);
+ }
+#endif
+ /* handle traffic */
+
+ if (otherservers[me_server_idx].queue) {
+ /* something here for me; take care of it */
+#if 1
+ zdbug((LOG_DEBUG, "internal queue process"));
+#endif
+
+ pending = server_dequeue(me_server);
+
+ status = ZParseNotice(pending->packet, pending->len, &new_notice);
+ if (status != ZERR_NONE) {
+ syslog(LOG_ERR, "bad notice parse (%s): %s",
+ inet_ntoa(pending->who.sin_addr), error_message(status));
+ } else {
+ dispatch(&new_notice, pending->auth, &pending->who, 1);
+ }
+ server_pending_free(pending);
+ return;
+ }
+
+ /*
+ * nothing in internal queue, go to the external library
+ * queue/socket
+ */
+ status = ZReceivePacket(input_packet, &input_len, &whoisit);
+ if (status != ZERR_NONE) {
+ syslog(LOG_ERR, "bad packet receive: %s from %s",
+ error_message(status), inet_ntoa(whoisit.sin_addr));
+ return;
+ }
+ npackets++;
+ status = ZParseNotice(input_packet, input_len, &new_notice);
+ if (status != ZERR_NONE) {
+ syslog(LOG_ERR, "bad notice parse (%s): %s",
+ inet_ntoa(whoisit.sin_addr), error_message(status));
+ return;
+ }
+ if (server_which_server(&whoisit)) {
+ /* we need to parse twice--once to get
+ the source addr, second to check
+ authentication */
+ memset(&input_sin, 0, sizeof(input_sin));
+ input_sin.sin_addr.s_addr = new_notice.z_sender_addr.s_addr;
+ input_sin.sin_port = new_notice.z_port;
+ input_sin.sin_family = AF_INET;
+ realm = realm_which_realm(&input_sin);
+ if (realm) {
+ authentic = ZCheckRealmAuthentication(&new_notice, &input_sin,
+ realm->name);
+ } else {
+ authentic = ZCheckAuthentication(&new_notice, &input_sin);
+ }
+ from_server = 1;
+ } else {
+ from_server = 0;
+ realm = realm_which_realm(&whoisit);
+ if (realm) {
+ authentic = ZCheckRealmAuthentication(&new_notice, &whoisit,
+ realm->name);
+ } else {
+ authentic = ZCheckAuthentication(&new_notice, &whoisit);
+ }
+ }
+
+ if (whoisit.sin_port != hm_port && whoisit.sin_port != hm_srv_port &&
+ strcasecmp(new_notice.z_class, ZEPHYR_ADMIN_CLASS) != 0 &&
+ whoisit.sin_port != srv_addr.sin_port &&
+ new_notice.z_kind != CLIENTACK) {
+ syslog(LOG_ERR, "bad port %s/%d", inet_ntoa(whoisit.sin_addr),
+ ntohs(whoisit.sin_port));
+ return;
+ }
+
+ message_notices.val++;
+ dispatch(&new_notice, authentic, &whoisit, from_server);
+ return;
+}
+/*
+ * Dispatch a notice.
+ */
+
+static void
+dispatch(notice, auth, who, from_server)
+ ZNotice_t *notice;
+ int auth;
+ struct sockaddr_in *who;
+ int from_server;
+{
+ Code_t status;
+ String *notice_class;
+ struct sockaddr_in who2;
+ int authflag;
+ Realm *realm;
+ char *cp;
+#ifdef DEBUG
+ char dbg_buf[BUFSIZ];
+#endif
+
+ authflag = (auth == ZAUTH_YES);
+
+ if ((int) notice->z_kind < (int) UNSAFE ||
+ (int) notice->z_kind > (int) CLIENTACK) {
+ syslog(LOG_NOTICE, "bad notice kind 0x%x from %s", notice->z_kind,
+ inet_ntoa(who->sin_addr));
+ return;
+ }
+#if 0
+ if (zdebug) {
+ sprintf(dbg_buf,
+ "disp:%s '%s' '%s' '%s' notice to '%s' from '%s' %s/%d/%d",
+ ZNoticeKinds[(int) notice->z_kind], notice->z_class,
+ notice->z_class_inst, notice->z_opcode, notice->z_recipient,
+ notice->z_sender, inet_ntoa(who->sin_addr),
+ ntohs(who->sin_port), ntohs(notice->z_port));
+ syslog(LOG_DEBUG, "%s", dbg_buf);
+ }
+#endif
+
+ if (notice->z_kind == CLIENTACK) {
+ nack_cancel(notice, who);
+ return;
+ }
+
+ who2 = *who;
+#if 0
+ if (0 && from_server) {
+ /* incorporate server_dispatch here */
+ }
+#endif
+ notice_class = make_string(notice->z_class,1);
+
+ if (from_server) {
+ interserver_notices.val++;
+ status = server_dispatch(notice, authflag, who);
+ } else if (class_is_hm(notice_class)) {
+ hm_packets.val++;
+ status = hostm_dispatch(notice, authflag, who, me_server);
+ } else if (realm_which_realm(who) && !(class_is_admin(notice_class))) {
+ realm_notices.val++;
+ status = realm_dispatch(notice, authflag, who, me_server);
+ } else if (class_is_control(notice_class)) {
+ control_notices.val++;
+ status = control_dispatch(notice, authflag, who, me_server);
+ } else if (class_is_ulogin(notice_class)) {
+ login_notices.val++;
+ status = ulogin_dispatch(notice, authflag, who, me_server);
+ } else if (class_is_ulocate(notice_class)) {
+ locate_notices.val++;
+ status = ulocate_dispatch(notice, authflag, who, me_server);
+ } else if (class_is_admin(notice_class)) {
+ admin_notices.val++;
+ status = server_adispatch(notice, authflag, who, me_server);
+ } else {
+ if (!bound_for_local_realm(notice)) {
+ cp = strchr(notice->z_recipient, '@');
+ if (!cp ||
+ !(realm = realm_get_realm_by_name(realm_expand_realm(cp + 1))))
+ sendit(notice, authflag, who, 0);
+ else
+ realm_handoff(notice, authflag, who, realm, 1);
+ } else {
+ if (notice->z_recipient[0] == '@')
+ notice->z_recipient = "";
+ sendit(notice, authflag, who, 1);
+ }
+ free_string(notice_class);
+ return;
+ }
+
+ if (status == ZSRV_REQUEUE)
+ server_self_queue(notice, authflag, who);
+ free_string(notice_class);
+}
+
+/*
+ * Send a notice off to those clients who have subscribed to it.
+ */
+
+void
+sendit(notice, auth, who, external)
+ ZNotice_t *notice;
+ int auth;
+ struct sockaddr_in *who;
+ int external;
+{
+ static int send_counter = 0;
+ char recipbuf[ANAME_SZ + INST_SZ + REALM_SZ + 3], *recipp;
+ int any = 0;
+ Acl *acl;
+ Destination dest;
+ String *class;
+
+ class = make_string(notice->z_class, 1);
+ acl = class_get_acl(class);
+ if (acl != NULL) {
+ /* if controlled and not auth, fail */
+ if (!auth) {
+ syslog(LOG_WARNING, "sendit unauthentic %s from %s",
+ notice->z_class, notice->z_sender);
+ clt_ack(notice, who, AUTH_FAILED);
+ free_string(class);
+ return;
+ }
+ /* if not auth to transmit, fail */
+ if (!access_check(notice->z_sender, acl, TRANSMIT)) {
+ syslog(LOG_WARNING, "sendit unauthorized %s from %s",
+ notice->z_class, notice->z_sender);
+ clt_ack(notice, who, AUTH_FAILED);
+ free_string(class);
+ return;
+ }
+ /* sender != inst and not auth to send to others --> fail */
+ if (strcmp(notice->z_sender, notice->z_class_inst) != 0 &&
+ !access_check(notice->z_sender, acl, INSTUID)) {
+ syslog(LOG_WARNING, "sendit unauth uid %s %s.%s", notice->z_sender,
+ notice->z_class, notice->z_class_inst);
+ clt_ack(notice, who, AUTH_FAILED);
+ free_string(class);
+ return;
+ }
+ }
+ if (!realm_which_realm(who)) {
+ if (memcmp(¬ice->z_sender_addr.s_addr, &who->sin_addr.s_addr,
+ sizeof(notice->z_sender_addr.s_addr))) {
+ /* someone is playing games... */
+ /* inet_ntoa returns pointer to static area */
+ /* max size is 255.255.255.255 */
+ char buffer[16];
+ strcpy(buffer, inet_ntoa(who->sin_addr));
+ if (!auth) {
+ syslog(LOG_WARNING,
+ "sendit unauthentic fake packet: claimed %s, real %s",
+ inet_ntoa(notice->z_sender_addr), buffer);
+ clt_ack(notice, who, AUTH_FAILED);
+ free_string(class);
+ return;
+ }
+ if (ntohl(notice->z_sender_addr.s_addr) != 0) {
+ syslog(LOG_WARNING,
+ "sendit invalid address: claimed %s, real %s",
+ inet_ntoa(notice->z_sender_addr), buffer);
+ clt_ack(notice, who, AUTH_FAILED);
+ free_string(class);
+ return;
+ }
+ syslog(LOG_WARNING, "sendit addr mismatch: claimed %s, real %s",
+ inet_ntoa(notice->z_sender_addr), buffer);
+ }
+ }
+
+ /* Increment the send counter, used to prevent duplicate sends to
+ * clients. On the off-chance that we wrap around to 0, skip over
+ * it to prevent missing clients which have never had a packet
+ * sent to them. */
+ send_counter++;
+ if (send_counter == 0)
+ send_counter = 1;
+
+ /* Send to clients subscribed to the triplet itself. */
+ dest.classname = class;
+ dest.inst = make_string(notice->z_class_inst, 1);
+ if (bound_for_local_realm(notice) && *notice->z_recipient == '@') {
+ dest.recip = make_string("", 0);
+ } else {
+ strncpy(recipbuf, notice->z_recipient, sizeof(recipbuf));
+ recipp = strrchr(recipbuf, '@');
+ if (recipp)
+ sprintf(recipp + 1, "%s", realm_expand_realm(recipp + 1));
+ dest.recip = make_string(recipbuf, 0);
+ }
+ if (send_to_dest(notice, auth, &dest, send_counter, external))
+ any = 1;
+
+ /* Send to clients subscribed to the triplet with the instance
+ * substituted with the wildcard instance. */
+ free_string(dest.inst);
+ dest.inst = wildcard_instance;
+ if (send_to_dest(notice, auth, &dest, send_counter, external))
+ any = 1;
+
+ free_string(class);
+ free_string(dest.recip);
+ if (any)
+ ack(notice, who);
+ else
+ nack(notice, who);
+}
+
+/*
+ * Send to each client in the list. Avoid duplicates by setting
+ * last_send on each client to send_counter, a nonce which is updated
+ * by sendit() above.
+ */
+
+static int
+send_to_dest(notice, auth, dest, send_counter, external)
+ ZNotice_t *notice;
+ int auth;
+ Destination *dest;
+ int send_counter;
+ int external;
+{
+ Client **clientp;
+ int any = 0;
+
+ clientp = triplet_lookup(dest);
+ if (!clientp)
+ return 0;
+
+ for (; *clientp; clientp++) {
+ if ((*clientp)->last_send == send_counter)
+ continue;
+ (*clientp)->last_send = send_counter;
+ if ((*clientp)->realm && external)
+ realm_handoff(notice, auth, &clientp[0]->addr, clientp[0]->realm,
+ 1);
+ else
+ xmit(notice, &((*clientp)->addr), auth, *clientp);
+ any = 1;
+ }
+
+ return any;
+}
+
+/*
+ * Release anything destined for the client in the not-yet-acked table.
+ */
+
+void
+nack_release(client)
+ Client *client;
+{
+ int i;
+ Unacked *nacked, *next;
+
+ for (i = 0; i < NACKTAB_HASHSIZE; i++) {
+ for (nacked = nacktab[i]; nacked; nacked = next) {
+ next = nacked->next;
+ if (nacked->client == client) {
+ timer_reset(nacked->timer);
+ LIST_DELETE(nacked);
+ free(nacked->packet);
+ free(nacked);
+ }
+ }
+ }
+}
+
+/*
+ * Send one packet of a fragmented message to a client. After transmitting,
+ * put it onto the not ack'ed list.
+ */
+
+/* the arguments must be the same as the arguments to Z_XmitFragment */
+/*ARGSUSED*/
+Code_t
+xmit_frag(notice, buf, len, waitforack)
+ ZNotice_t *notice;
+ char *buf;
+ int len;
+ int waitforack;
+{
+ struct sockaddr_in sin;
+ char *savebuf;
+ Unacked *nacked;
+ Code_t retval;
+ int hashval, sendfail = 0;
+
+ retval = ZSendPacket(buf, len, 0);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_WARNING, "xmit_frag send: %s", error_message(retval));
+ if (retval != EAGAIN && retval != ENOBUFS)
+ return retval;
+ sendfail = 1;
+ }
+
+ /* now we've sent it, mark it as not ack'ed */
+ nacked = (Unacked *) malloc(sizeof(Unacked));
+ if (!nacked) {
+ /* no space: just punt */
+ syslog(LOG_WARNING, "xmit_frag nack malloc");
+ return ENOMEM;
+ }
+
+ savebuf = (char *) malloc(len);
+ if (!savebuf) {
+ /* no space: just punt */
+ syslog(LOG_WARNING, "xmit_frag pack malloc");
+ free(nacked);
+ return ENOMEM;
+ }
+
+ memcpy(savebuf, buf, len);
+
+ sin = ZGetDestAddr();
+ nacked->client = NULL;
+ nacked->rexmits = (sendfail) ? -1 : 0;
+ nacked->packet = savebuf;
+ nacked->dest.addr = sin;
+ nacked->packsz = len;
+ nacked->uid = notice->z_uid;
+ nacked->timer = timer_set_rel(rexmit_times[0], rexmit, nacked);
+ LIST_INSERT(&nacktab[NACKTAB_HASHVAL(sin, nacked->uid)], nacked);
+ return(ZERR_NONE);
+}
+
+/*
+ * Send the notice to the client. After transmitting, put it onto the
+ * not ack'ed list.
+ */
+
+void
+xmit(notice, dest, auth, client)
+ ZNotice_t *notice;
+ struct sockaddr_in *dest;
+ int auth;
+ Client *client;
+{
+ char *noticepack;
+ Unacked *nacked;
+ int packlen, sendfail = 0;
+ Code_t retval;
+
+#if 0
+ zdbug((LOG_DEBUG,"xmit"));
+#endif
+
+ noticepack = (char *) malloc(sizeof(ZPacket_t));
+ if (!noticepack) {
+ syslog(LOG_ERR, "xmit malloc");
+ return; /* DON'T put on nack list */
+ }
+
+ packlen = sizeof(ZPacket_t);
+
+ if (auth && client) { /*
+ we are distributing authentic and
+ we have a pointer to auth info
+ */
+#ifdef HAVE_KRB4
+ retval = ZFormatAuthenticNotice(notice, noticepack, packlen, &packlen,
+ client->session_key);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_ERR, "xmit auth format: %s", error_message(retval));
+ free(noticepack);
+ return;
+ }
+#else /* !HAVE_KRB4 */
+ notice->z_auth = 1;
+ retval = ZFormatSmallRawNotice(notice, noticepack, &packlen);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_ERR, "xmit auth/raw format: %s", error_message(retval));
+ free(noticepack);
+ return;
+ }
+#endif /* HAVE_KRB4 */
+ } else {
+ notice->z_auth = 0;
+ notice->z_authent_len = 0;
+ notice->z_ascii_authent = (char *)"";
+ retval = ZFormatSmallRawNotice(notice, noticepack, &packlen);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_ERR, "xmit format: %s", error_message(retval));
+ free(noticepack);
+ return; /* DON'T put on nack list */
+ }
+ }
+#if 0
+ zdbug((LOG_DEBUG," to %s/%d", inet_ntoa(dest->sin_addr),
+ ntohs(dest->sin_port)));
+#endif
+ retval = ZSetDestAddr(dest);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_WARNING, "xmit set addr: %s", error_message(retval));
+ free(noticepack);
+ return;
+ }
+ retval = ZSendPacket(noticepack, packlen, 0);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_WARNING, "xmit xmit: (%s/%d) %s", inet_ntoa(dest->sin_addr),
+ ntohs(dest->sin_port), error_message(retval));
+ if (retval != EAGAIN && retval != ENOBUFS) {
+ free(noticepack);
+ return;
+ }
+ sendfail = 1;
+ }
+
+ /* now we've sent it, mark it as not ack'ed */
+
+ nacked = (Unacked *) malloc(sizeof(Unacked));
+ if (!nacked) {
+ /* no space: just punt */
+ syslog(LOG_WARNING, "xmit nack malloc");
+ free(noticepack);
+ return;
+ }
+
+ nacked->client = client;
+ nacked->rexmits = (sendfail) ? -1 : 0;
+ nacked->packet = noticepack;
+ nacked->dest.addr = *dest;
+ nacked->packsz = packlen;
+ nacked->uid = notice->z_uid;
+ nacked->timer = timer_set_rel(rexmit_times[0], rexmit, nacked);
+ LIST_INSERT(&nacktab[NACKTAB_HASHVAL(*dest, nacked->uid)], nacked);
+}
+
+/*
+ * Retransmit the packet specified. If we have timed out or retransmitted
+ * too many times, punt the packet and initiate the host recovery algorithm
+ * Else, increment the count and re-send the notice packet.
+ */
+
+void
+rexmit(arg)
+ void *arg;
+{
+ Unacked *nacked = (Unacked *) arg;
+ int retval;
+
+#if 1
+ syslog(LOG_DEBUG, "rexmit %s/%d #%d time %d",
+ inet_ntoa(nacked->dest.addr.sin_addr),
+ ntohs(nacked->dest.addr.sin_port), nacked->rexmits + 1, NOW);
+#endif
+
+ nacked->rexmits++;
+ if (rexmit_times[nacked->rexmits] == -1) {
+ if (!nacked->client
+ || NOW - nacked->client->last_ack >= CLIENT_GIVEUP_MIN) {
+ /* The client (if there was one) has been unresponsive.
+ * Give up sending this packet, and kill the client if
+ * there was one. (Make sure to remove nacked from the
+ * nack list before calling client_deregister(), which
+ * scans the nack list.)
+ */
+ LIST_DELETE(nacked);
+ if (nacked->client) {
+ server_kill_clt(nacked->client);
+ client_deregister(nacked->client, 1);
+ }
+ free(nacked->packet);
+ free(nacked);
+ return;
+ } else {
+ /* The client has sent us an ack recently. Retry with the maximum
+ * retransmit time. */
+ nacked->rexmits--;
+ }
+ }
+
+ /* retransmit the packet */
+#if 0
+ zdbug((LOG_DEBUG," to %s/%d", inet_ntoa(nacked->dest.addr.sin_addr),
+ ntohs(nacked->dest.addr.sin_port)));
+#endif
+ retval = ZSetDestAddr(&nacked->dest.addr);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_WARNING, "rexmit set addr: %s", error_message(retval));
+ } else {
+ retval = ZSendPacket(nacked->packet, nacked->packsz, 0);
+ if (retval != ZERR_NONE)
+ syslog(LOG_WARNING, "rexmit xmit: %s", error_message(retval));
+ if (retval == EAGAIN || retval == ENOBUFS)
+ nacked->rexmits--;
+ }
+
+ /* reset the timer */
+ nacked->timer = timer_set_rel(rexmit_times[nacked->rexmits], rexmit,
+ nacked);
+ return;
+}
+
+/*
+ * Send an acknowledgement to the sending client, by sending back the
+ * header from the original notice with the z_kind field changed to either
+ * SERVACK or SERVNAK, and the contents of the message either SENT or
+ * NOT_SENT, depending on the value of the sent argument.
+ */
+
+void
+clt_ack(notice, who, sent)
+ ZNotice_t *notice;
+ struct sockaddr_in *who;
+ Sent_type sent;
+{
+ ZNotice_t acknotice;
+ ZPacket_t ackpack;
+ int packlen;
+ int notme = 0;
+ char *sent_name;
+ Code_t retval;
+
+ if (bdumping) { /* don't ack while dumping */
+#if 1
+ zdbug((LOG_DEBUG,"bdumping, no ack"));
+#endif
+ return;
+ }
+
+ acknotice = *notice;
+
+ acknotice.z_kind = SERVACK;
+ switch (sent) {
+ case SENT:
+ acknotice.z_message = ZSRVACK_SENT;
+ sent_name = "sent";
+ break;
+ case NOT_FOUND:
+ acknotice.z_message = ZSRVACK_FAIL;
+ acknotice.z_kind = SERVNAK;
+ sent_name = "fail";
+ break;
+ case AUTH_FAILED:
+ acknotice.z_kind = SERVNAK;
+ acknotice.z_message = ZSRVACK_NOTSENT;
+ sent_name = "nak/not_sent";
+ break;
+ case NOT_SENT:
+ acknotice.z_message = ZSRVACK_NOTSENT;
+ sent_name = "not_sent";
+ break;
+ default:
+ abort ();
+ }
+
+#if 0
+ zdbug((LOG_DEBUG,"clt_ack type %s for %d to %s/%d", sent_name,
+ ntohs(notice->z_port), inet_ntoa(who->sin_addr),
+ ntohs(who->sin_port)));
+#endif
+
+ acknotice.z_multinotice = "";
+
+ /* leave room for the trailing null */
+ acknotice.z_message_len = strlen(acknotice.z_message) + 1;
+
+ packlen = sizeof(ackpack);
+
+ retval = ZFormatSmallRawNotice(&acknotice, ackpack, &packlen);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_ERR, "clt_ack format: %s", error_message(retval));
+ return;
+ }
+ retval = ZSetDestAddr(who);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_WARNING, "clt_ack set addr: %s", error_message(retval));
+ return;
+ }
+ retval = ZSendPacket(ackpack, packlen, 0);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_WARNING, "clt_ack xmit: %s", error_message(retval));
+ return;
+ } else {
+ zdbug((LOG_DEBUG, "packet sent"));
+ }
+ return;
+}
+
+/*
+ * An ack has arrived.
+ * remove the packet matching this notice from the not-yet-acked queue
+ */
+
+static void
+nack_cancel(notice, who)
+ ZNotice_t *notice;
+ struct sockaddr_in *who;
+{
+ Unacked *nacked;
+ int hashval;
+
+ /* search the not-yet-acked table for this packet, and flush it. */
+#if 0
+ zdbug((LOG_DEBUG, "nack_cancel: %s:%08X,%08X",
+ inet_ntoa(notice->z_uid.zuid_addr),
+ notice->z_uid.tv.tv_sec, notice->z_uid.tv.tv_usec));
+#endif
+ hashval = NACKTAB_HASHVAL(*who, notice->z_uid);
+ for (nacked = nacktab[hashval]; nacked; nacked = nacked->next) {
+ if (nacked->dest.addr.sin_addr.s_addr == who->sin_addr.s_addr
+ && nacked->dest.addr.sin_port == who->sin_port
+ && ZCompareUID(&nacked->uid, ¬ice->z_uid)) {
+ if (nacked->client)
+ nacked->client->last_ack = NOW;
+ timer_reset(nacked->timer);
+ free(nacked->packet);
+ LIST_DELETE(nacked);
+ free(nacked);
+ return;
+ }
+ }
+
+#if 1
+ zdbug((LOG_DEBUG,"nack_cancel: nack not found %s:%08X,%08X",
+ inet_ntoa (notice->z_uid.zuid_addr),
+ notice->z_uid.tv.tv_sec, notice->z_uid.tv.tv_usec));
+#endif
+}
+
+/* for compatibility when sending subscription information to old clients */
+#ifdef OLD_COMPAT
+#define OLD_ZEPHYR_VERSION "ZEPH0.0"
+#endif /* OLD_COMPAT */
+
+/* Dispatch an HM_CTL notice. */
+
+Code_t
+hostm_dispatch(notice, auth, who, server)
+ ZNotice_t *notice;
+ int auth;
+ struct sockaddr_in *who;
+ Server *server;
+{
+ Server *owner;
+ char *opcode = notice->z_opcode;
+ Code_t retval;
+ int i, add = 0, remove = 0;
+
+#if 0
+ zdbug((LOG_DEBUG,"hm_disp"));
+#endif
+
+ if (notice->z_kind == HMACK) {
+ /* Ignore. */
+ ;
+ } else if (notice->z_kind != HMCTL) {
+#if 0
+ zdbug((LOG_DEBUG, "bogus HM packet"));
+#endif
+ clt_ack(notice, who, AUTH_FAILED);
+ } else if (strcmp(opcode, HM_FLUSH) == 0) {
+ client_flush_host(&who->sin_addr);
+ if (server == me_server)
+ server_forward(notice, auth, who);
+ } else if (strcmp(opcode, HM_BOOT) == 0) {
+ client_flush_host(&who->sin_addr);
+ if (server == me_server) {
+ server_forward(notice, auth, who);
+ ack(notice, who);
+ add = 1;
+ }
+ } else if (strcmp(opcode, HM_ATTACH) == 0) {
+ if (server == me_server) {
+ server_forward(notice, auth, who);
+ ack(notice, who);
+ add = 1;
+ } else {
+ remove = 1;
+ }
+ } else if (strcmp(opcode, HM_DETACH) == 0) {
+ remove = 1;
+ } else {
+ syslog(LOG_WARNING, "hm_dispatch: unknown opcode %s", opcode);
+ }
+
+ if (add) {
+ for (i = 0; i < num_hosts; i++) {
+ if (hosts[i].s_addr == who->sin_addr.s_addr)
+ break;
+ }
+ if (i == num_hosts) {
+ if (hosts_size == 0) {
+ hosts = (struct in_addr *) malloc(HOSTS_SIZE_INIT *
+ sizeof(struct in_addr));
+ if (!hosts)
+ return ENOMEM;
+ hosts_size = HOSTS_SIZE_INIT;
+ } else if (num_hosts == hosts_size) {
+ hosts = (struct in_addr *) realloc(hosts, hosts_size * 2 *
+ sizeof(struct in_addr));
+ if (!hosts)
+ return ENOMEM;
+ hosts_size *= 2;
+ }
+ hosts[num_hosts++] = who->sin_addr;
+ }
+ } else if (remove) {
+ for (i = 0; i < num_hosts; i++) {
+ if (hosts[i].s_addr == who->sin_addr.s_addr) {
+ memmove(&hosts[i], &hosts[i + 1], num_hosts - (i + 1));
+ num_hosts--;
+ break;
+ }
+ }
+ }
+ return ZERR_NONE;
+}
+
+/*
+ * Dispatch a ZEPHYR_CTL notice.
+ */
+
+Code_t
+control_dispatch(notice, auth, who, server)
+ ZNotice_t *notice;
+ int auth;
+ struct sockaddr_in *who;
+ Server *server;
+{
+ char *opcode = notice->z_opcode;
+ Client *client;
+ Code_t retval;
+ int wantdefs;
+ Realm *realm;
+ struct sockaddr_in newwho;
+
+ /*
+ * ZEPHYR_CTL Opcodes expected are:
+ * BOOT (inst HM): host has booted; flush data.
+ * CLIENT_SUBSCRIBE: process with the subscription mananger.
+ * CLIENT_UNSUBSCRIBE: ""
+ * CLIENT_CANCELSUB: ""
+ */
+
+ zdbug((LOG_DEBUG, "ctl_disp: opc=%s", opcode));
+
+ newwho.sin_addr.s_addr = notice->z_sender_addr.s_addr;
+ newwho.sin_port = notice->z_port;
+ realm = realm_which_realm(&newwho);
+ if (realm)
+ return(realm_control_dispatch(notice, auth, who, server, realm));
+
+ if (strcasecmp(notice->z_class_inst, ZEPHYR_CTL_HM) == 0) {
+ return hostm_dispatch(notice, auth, who, server);
+ } else if (strcmp(opcode, CLIENT_GIMMESUBS) == 0 ||
+ strcmp(opcode, CLIENT_GIMMEDEFS) == 0) {
+ /* this special case is before the auth check so that
+ someone who has no subscriptions does NOT get a SERVNAK
+ but rather an empty list. Note we must therefore
+ check authentication inside subscr_sendlist */
+#ifdef OLD_COMPAT
+ /* only acknowledge if *not* old version; the old version
+ acknowledges the packet with the reply */
+ if (strcmp(notice->z_version, OLD_ZEPHYR_VERSION) != 0)
+ ack(notice, who);
+#else /* !OLD_COMPAT */
+ ack(notice, who);
+#endif /* OLD_COMPAT */
+ subscr_sendlist(notice, auth, who);
+ return ZERR_NONE;
+ } else if (!auth) {
+#if 0
+ zdbug((LOG_DEBUG,"unauth ctrl_disp"));
+#endif
+ if (server == me_server)
+ clt_ack(notice, who, AUTH_FAILED);
+ return ZERR_NONE;
+ }
+
+ wantdefs = strcmp(opcode, CLIENT_SUBSCRIBE_NODEFS);
+ if (!wantdefs || strcmp(opcode, CLIENT_SUBSCRIBE) == 0) {
+ /* subscription notice */
+ retval = client_register(notice, &who->sin_addr, &client, wantdefs);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_NOTICE, "subscr %s/%s/%d failed: %s",
+ notice->z_sender, inet_ntoa(who->sin_addr),
+ ntohs(notice->z_port), error_message(retval));
+ if (server == me_server) {
+ if (retval == ZSRV_BADSUBPORT)
+ clt_ack(notice, who, AUTH_FAILED);
+ else
+ nack(notice, who);
+ }
+ return(ZERR_NONE);
+ }
+ if (strcmp(client->principal->string, notice->z_sender) != 0) {
+ /* you may only subscribe for your own clients */
+ if (server == me_server)
+ clt_ack(notice, who, AUTH_FAILED);
+ return ZERR_NONE;
+ }
+#ifdef HAVE_KRB4
+ /* in case it's changed */
+ memcpy(client->session_key, ZGetSession(), sizeof(C_Block));
+#endif
+ retval = subscr_subscribe(client, notice);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_WARNING, "subscr failed: %s", error_message(retval));
+ if (server == me_server)
+ nack(notice, who);
+ return ZERR_NONE;
+ }
+ } else if (strcmp(opcode, CLIENT_UNSUBSCRIBE) == 0) {
+ client = client_find(&who->sin_addr, notice->z_port);
+ if (client != NULL) {
+ if (strcmp(client->principal->string, notice->z_sender) != 0) {
+ /* you may only cancel for your own clients */
+ if (server == me_server)
+ clt_ack(notice, who, AUTH_FAILED);
+ return ZERR_NONE;
+ }
+#if 0
+ if (zdebug) {
+ if (server == me_server) {
+ syslog(LOG_DEBUG, "subscription cancel for %s/%d\n",
+ inet_ntoa(who->sin_addr), ntohs(who->sin_port));
+ } else {
+ syslog(LOG_DEBUG,
+ "subscription cancel for %s/%d from %s\n",
+ inet_ntoa(who->sin_addr), ntohs(who->sin_port),
+ server->addr_str);
+ }
+ }
+#endif
+ subscr_cancel(who, notice);
+ } else {
+ nack(notice, who);
+ return ZERR_NONE;
+ }
+ } else if (strcmp(opcode, CLIENT_CANCELSUB) == 0) {
+ /* canceling subscriptions implies I can punt info about this client */
+ client = client_find(&who->sin_addr, notice->z_port);
+ if (client == NULL) {
+#if 0
+ zdbug((LOG_DEBUG,"can_sub not found client"));
+#endif
+ if (server == me_server)
+ nack(notice, who);
+ return ZERR_NONE;
+ }
+ if (strcmp(client->principal->string, notice->z_sender) != 0) {
+ /* you may only cancel for your own clients */
+ if (server == me_server)
+ clt_ack(notice, who, AUTH_FAILED);
+ return ZERR_NONE;
+ }
+ /* don't flush locations here, let him do it explicitly */
+#if 0
+ zdbug((LOG_DEBUG, "cancelsub clt_dereg %s/%d",
+ inet_ntoa(who->sin_addr), ntohs(who->sin_port)));
+#endif
+ client_deregister(client, 0);
+ } else {
+ syslog(LOG_WARNING, "unknown ctl opcode %s", opcode);
+ if (server == me_server)
+ nack(notice, who);
+ return ZERR_NONE;
+ }
+
+ if (server == me_server) {
+ ack(notice, who);
+ server_forward(notice, auth, who);
+ }
+ return ZERR_NONE;
+}
+
+void
+hostm_shutdown()
+{
+ int i, s, newserver;
+ struct sockaddr_in sin;
+
+ for (i = 0; i < nservers; i++) {
+ if (i != me_server_idx && otherservers[i].state == SERV_UP)
+ break;
+ }
+ newserver = (i < nservers);
+ for (i = 0; i < num_hosts; i++) {
+ sin.sin_addr = hosts[i];
+ sin.sin_port = hm_port;
+ if (newserver) {
+ while (1) {
+ s = (random() % (nservers - 1)) + 1;
+ if (otherservers[s].state == SERV_UP)
+ break;
+ }
+ hostm_deathgram(&sin, &otherservers[s]);
+ } else {
+ hostm_deathgram(&sin, NULL);
+ }
+ }
+}
+
+static void
+hostm_deathgram(sin, server)
+ struct sockaddr_in *sin;
+ Server *server;
+{
+ Code_t retval;
+ int shutlen;
+ ZNotice_t shutnotice;
+ char *shutpack;
+
+ shutnotice.z_kind = HMCTL;
+ shutnotice.z_port = sin->sin_port; /* we are sending it */
+ shutnotice.z_class = HM_CTL_CLASS;
+ shutnotice.z_class_inst = HM_CTL_SERVER;
+ shutnotice.z_opcode = SERVER_SHUTDOWN;
+ shutnotice.z_sender = HM_CTL_SERVER;
+ shutnotice.z_recipient = hm_recipient();
+ shutnotice.z_default_format = "";
+ shutnotice.z_num_other_fields = 0;
+ shutnotice.z_message = (server) ? server->addr_str : NULL;
+ shutnotice.z_message_len = (server) ? strlen(server->addr_str) + 1 : 0;
+
+ retval = ZFormatNotice(&shutnotice, &shutpack, &shutlen, ZNOAUTH);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_ERR, "hm_shut format: %s",error_message(retval));
+ return;
+ }
+ retval = ZSetDestAddr(sin);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_WARNING, "hm_shut set addr: %s", error_message(retval));
+ free(shutpack);
+ return;
+ }
+ retval = ZSendPacket(shutpack, shutlen, 0);
+ if (retval != ZERR_NONE)
+ syslog(LOG_WARNING, "hm_shut xmit: %s", error_message(retval));
+ free(shutpack);
+}
+
+static char *
+hm_recipient()
+{
+ static char *recipient;
+ char *realm;
+
+ if (recipient)
+ return recipient;
+
+ realm = ZGetRealm();
+ if (!realm)
+ realm = "???";
+ recipient = (char *) malloc(strlen(realm) + 4);
+ strcpy (recipient, "hm@");
+ strcat (recipient, realm);
+ return recipient;
+}
+
--- /dev/null
+/*
+ * $Id: kopt.c,v 1.14 1999/01/22 23:19:44 ghudson Exp $
+ *
+ * Copyright 1985, 1986, 1987, 1988, 1990, 1991 by the Massachusetts
+ * Institute of Technology.
+ *
+ * For copying and distribution information, please see the file
+ * <mit-copyright.h>.
+ */
+
+/*
+ * This includes code taken from:
+ * Kerberos: rd_req.c,v 4.16 89/03/22 14:52:06 jtkohl Exp
+ * Kerberos: prot.h,v 4.13 89/01/24 14:27:22 jtkohl Exp
+ * Kerberos: krb_conf.h,v 4.0 89/01/23 09:59:27 jtkohl Exp
+ */
+
+#include <zephyr/mit-copyright.h>
+#include "zserver.h"
+
+#ifndef lint
+#ifndef SABER
+static const char *rcsid_rd_req_c =
+ "$Id: kopt.c,v 1.14 1999/01/22 23:19:44 ghudson Exp $";
+#endif /* lint */
+#endif /* SABER */
+
+#ifdef HAVE_KRB4
+#ifndef NOENCRYPTION
+
+/* Byte ordering */
+#undef HOST_BYTE_ORDER
+static int krbONE = 1;
+#define HOST_BYTE_ORDER (* (char *) &krbONE)
+
+#define KRB_PROT_VERSION 4
+
+/* Message types , always leave lsb for byte order */
+
+#define AUTH_MSG_KDC_REQUEST 1<<1
+#define AUTH_MSG_KDC_REPLY 2<<1
+#define AUTH_MSG_APPL_REQUEST 3<<1
+#define AUTH_MSG_APPL_REQUEST_MUTUAL 4<<1
+#define AUTH_MSG_ERR_REPLY 5<<1
+#define AUTH_MSG_PRIVATE 6<<1
+#define AUTH_MSG_SAFE 7<<1
+#define AUTH_MSG_APPL_ERR 8<<1
+#define AUTH_MSG_DIE 63<<1
+
+/* values for kerb error codes */
+
+#define KERB_ERR_OK 0
+#define KERB_ERR_NAME_EXP 1
+#define KERB_ERR_SERVICE_EXP 2
+#define KERB_ERR_AUTH_EXP 3
+#define KERB_ERR_PKT_VER 4
+#define KERB_ERR_NAME_MAST_KEY_VER 5
+#define KERB_ERR_SERV_MAST_KEY_VER 6
+#define KERB_ERR_BYTE_ORDER 7
+#define KERB_ERR_PRINCIPAL_UNKNOWN 8
+#define KERB_ERR_PRINCIPAL_NOT_UNIQUE 9
+#define KERB_ERR_NULL_KEY 10
+
+extern int krb_ap_req_debug;
+
+extern struct timeval t_local;
+
+/*
+ * Keep the following information around for subsequent calls
+ * to this routine by the same server using the same key.
+ */
+
+static Sched serv_ksched; /* Key sched to decrypt ticket */
+static des_cblock serv_key; /* Initialization vector */
+
+static int st_kvno; /* version number for this key */
+static char st_rlm[REALM_SZ]; /* server's realm */
+static char st_nam[ANAME_SZ]; /* service name */
+static char st_inst[INST_SZ]; /* server's instance */
+
+/*
+ * Cache of key schedules
+ */
+#define HASH_SIZE_1 255 /* not a power of 2 */
+#define HASH_SIZE_2 3
+static unsigned long last_use;
+typedef struct {
+ unsigned long last_time_used;
+ des_cblock key;
+ Sched schedule;
+} KeySchedRec;
+static KeySchedRec scheds[HASH_SIZE_1][HASH_SIZE_2];
+
+Sched *check_key_sched_cache(key)
+ des_cblock key;
+{
+ unsigned int hash_value = key[0] + key[1] * 256;
+ KeySchedRec *rec = scheds[hash_value % HASH_SIZE_1];
+ int i;
+
+ for (i = HASH_SIZE_2 - 1; i >= 0; i--) {
+ if (rec[i].last_time_used && key[0] == rec[i].key[0]
+ && !memcmp(key, rec[i].key, sizeof(des_cblock))) {
+ rec[i].last_time_used = last_use++;
+ return &rec[i].schedule;
+ }
+ }
+ return 0;
+}
+
+void add_to_key_sched_cache(key, sched)
+ des_cblock key;
+ Sched *sched;
+{
+ unsigned int hash_value = key[0] + key[1] * 256;
+ KeySchedRec *rec = scheds[hash_value % HASH_SIZE_1];
+ int i, oldest = HASH_SIZE_2 - 1;
+
+ for (i = HASH_SIZE_2 - 1; i >= 0; i--) {
+ if (rec[i].last_time_used == 0) {
+ oldest = i;
+ break;
+ }
+ if (rec[i].last_time_used < rec[oldest].last_time_used)
+ oldest = i;
+ }
+ memcpy (rec[oldest].key, key, sizeof(des_cblock));
+ rec[oldest].schedule = *sched;
+ rec[oldest].last_time_used = last_use++;
+}
+
+/*
+ * This file contains two functions. krb_set_key() takes a DES
+ * key or password string and returns a DES key (either the original
+ * key, or the password converted into a DES key) and a key schedule
+ * for it.
+ *
+ * krb_rd_req() reads an authentication request and returns information
+ * about the identity of the requestor, or an indication that the
+ * identity information was not authentic.
+ */
+
+/*
+ * krb_set_key() takes as its first argument either a DES key or a
+ * password string. The "cvt" argument indicates how the first
+ * argument "key" is to be interpreted: if "cvt" is null, "key" is
+ * taken to be a DES key; if "cvt" is non-null, "key" is taken to
+ * be a password string, and is converted into a DES key using
+ * string_to_key(). In either case, the resulting key is returned
+ * in the external variable "serv_key". A key schedule is
+ * generated for "serv_key" and returned in the external variable
+ * "serv_ksched".
+ *
+ * This routine returns the return value of des_key_sched.
+ *
+ * krb_set_key() needs to be in the same .o file as krb_rd_req() so that
+ * the key set by krb_set_key() is available in private storage for
+ * krb_rd_req().
+ */
+
+int
+krb_set_key(key,cvt)
+ char *key;
+ int cvt;
+{
+#ifdef NOENCRYPTION
+ memset(serv_key, 0, sizeof(serv_key));
+ return KSUCCESS;
+#else /* Encrypt */
+ Sched *s;
+ int ret;
+
+ if (cvt)
+ string_to_key(key,serv_key);
+ else
+ memcpy((char *)serv_key,key,8);
+
+ s = check_key_sched_cache (serv_key);
+ if (s) {
+ serv_ksched = *s;
+ return 0;
+ }
+ ret = des_key_sched(serv_key, serv_ksched.s);
+ add_to_key_sched_cache(serv_key, &serv_ksched);
+ return ret;
+#endif /* NOENCRYPTION */
+}
+
+
+/*
+ * krb_rd_req() takes an AUTH_MSG_APPL_REQUEST or
+ * AUTH_MSG_APPL_REQUEST_MUTUAL message created by krb_mk_req(),
+ * checks its integrity and returns a judgement as to the requestor's
+ * identity.
+ *
+ * The "authent" argument is a pointer to the received message.
+ * The "service" and "instance" arguments name the receiving server,
+ * and are used to get the service's ticket to decrypt the ticket
+ * in the message, and to compare against the server name inside the
+ * ticket. "from_addr" is the network address of the host from which
+ * the message was received; this is checked against the network
+ * address in the ticket. If "from_addr" is zero, the check is not
+ * performed. "ad" is an AUTH_DAT structure which is
+ * filled in with information about the sender's identity according
+ * to the authenticator and ticket sent in the message. Finally,
+ * "fn" contains the name of the file containing the server's key.
+ * (If "fn" is NULL, the server's key is assumed to have been set
+ * by krb_set_key(). If "fn" is the null string ("") the default
+ * file KEYFILE, defined in "krb.h", is used.)
+ *
+ * krb_rd_req() returns RD_AP_OK if the authentication information
+ * was genuine, or one of the following error codes (defined in
+ * "krb.h"):
+ *
+ * RD_AP_VERSION - wrong protocol version number
+ * RD_AP_MSG_TYPE - wrong message type
+ * RD_AP_UNDEC - couldn't decipher the message
+ * RD_AP_INCON - inconsistencies found
+ * RD_AP_BADD - wrong network address
+ * RD_AP_TIME - client time (in authenticator)
+ * too far off server time
+ * RD_AP_NYV - Kerberos time (in ticket) too
+ * far off server time
+ * RD_AP_EXP - ticket expired
+ *
+ * For the message format, see krb_mk_req().
+ *
+ * Mutual authentication is not implemented.
+ */
+
+int
+krb_rd_req(authent,service,instance,from_addr,ad,fn)
+ KTEXT authent; /* The received message */
+ char *service; /* Service name */
+ char *instance; /* Service instance */
+ unsigned KRB_INT32 from_addr; /* Net address of originating host */
+ AUTH_DAT *ad; /* Structure to be filled in */
+ char *fn; /* Filename to get keys from */
+{
+ KTEXT_ST ticket; /* Temp storage for ticket */
+ KTEXT tkt = &ticket;
+ KTEXT_ST req_id_st; /* Temp storage for authenticator */
+ KTEXT req_id = &req_id_st;
+
+ char realm[REALM_SZ]; /* Realm of issuing kerberos */
+ Sched seskey_sched, *sched; /* Key sched for session key */
+ unsigned char skey[KKEY_SZ]; /* Session key from ticket */
+ char sname[SNAME_SZ]; /* Service name from ticket */
+ char iname[INST_SZ]; /* Instance name from ticket */
+ char r_aname[ANAME_SZ]; /* Client name from authenticator */
+ char r_inst[INST_SZ]; /* Client instance from authenticator */
+ char r_realm[REALM_SZ]; /* Client realm from authenticator */
+ unsigned int r_time_ms; /* Fine time from authenticator */
+ unsigned long r_time_sec; /* Coarse time from authenticator */
+ char *ptr; /* For stepping through */
+ unsigned long delta_t; /* Time in authenticator - local time */
+ long tkt_age; /* Age of ticket */
+ int swap_bytes; /* Need to swap bytes? */
+ int mutual; /* Mutual authentication requested? */
+ unsigned char s_kvno; /* Version number of the server's key
+ * Kerberos used to encrypt ticket */
+ int status;
+
+ if (authent->length <= 0)
+ return(RD_AP_MODIFIED);
+
+ ptr = (char *) authent->dat;
+
+ /* get msg version, type and byte order, and server key version */
+
+ /* check version */
+ if (KRB_PROT_VERSION != (unsigned int) *ptr++)
+ return RD_AP_VERSION;
+
+ /* byte order */
+ swap_bytes = 0;
+ if ((*ptr & 1) != HOST_BYTE_ORDER)
+ swap_bytes++;
+
+ /* check msg type */
+ mutual = 0;
+ switch (*ptr++ & ~1) {
+ case AUTH_MSG_APPL_REQUEST:
+ break;
+ case AUTH_MSG_APPL_REQUEST_MUTUAL:
+ mutual++;
+ break;
+ default:
+ return(RD_AP_MSG_TYPE);
+ }
+
+#ifdef lint
+ /* XXX mutual is set but not used; why??? */
+ /* this is a crock to get lint to shut up */
+ if (mutual)
+ mutual = 0;
+#endif /* lint */
+ s_kvno = *ptr++; /* get server key version */
+ strcpy(realm,ptr); /* And the realm of the issuing KDC */
+ ptr += strlen(ptr) + 1; /* skip the realm "hint" */
+
+ /*
+ * If "fn" is NULL, key info should already be set; don't
+ * bother with ticket file. Otherwise, check to see if we
+ * already have key info for the given server and key version
+ * (saved in the static st_* variables). If not, go get it
+ * from the ticket file. If "fn" is the null string, use the
+ * default ticket file.
+ */
+ if (fn && (strcmp(st_nam,service) != 0 || strcmp(st_inst,instance) != 0 ||
+ strcmp(st_rlm,realm) != 0 || (st_kvno != s_kvno))) {
+ if (*fn == 0)
+ fn = KEYFILE;
+ st_kvno = s_kvno;
+#ifndef NOENCRYPTION
+ if (read_service_key(service,instance,realm, (int) s_kvno,
+ fn, (char *) skey))
+ return(RD_AP_UNDEC);
+ status = krb_set_key((char *) skey, 0);
+ if (status != 0)
+ return(status);
+#endif /* !NOENCRYPTION */
+ strcpy(st_rlm,realm);
+ strcpy(st_nam,service);
+ strcpy(st_inst,instance);
+ }
+
+ /* Get ticket from authenticator */
+ tkt->length = (int) *ptr++;
+ if ((tkt->length + (ptr+1 - (char *) authent->dat)) > authent->length)
+ return RD_AP_MODIFIED;
+ memcpy(tkt->dat, ptr + 1, tkt->length);
+
+ if (krb_ap_req_debug)
+ krb_log("ticket->length: %d", tkt->length);
+
+#ifndef NOENCRYPTION
+ /* Decrypt and take apart ticket */
+#endif
+
+ if (decomp_ticket(tkt, &ad->k_flags, ad->pname, ad->pinst, ad->prealm,
+ &(ad->address), ad->session, &(ad->life),
+ &(ad->time_sec), sname, iname, serv_key, serv_ksched.s))
+ return RD_AP_UNDEC;
+
+ if (krb_ap_req_debug) {
+ krb_log("Ticket Contents.");
+ krb_log(" Aname: %s.%s",ad->pname,
+ ((int)*(ad->prealm) ? ad->prealm : "Athena"));
+ krb_log(" Service: %s%s%s", sname, ((int)*iname ? "." : ""), iname);
+ }
+
+ /* Extract the authenticator */
+ req_id->length = (int) *(ptr++);
+ if ((req_id->length + (ptr + tkt->length - (char *) authent->dat)) >
+ authent->length)
+ return RD_AP_MODIFIED;
+ memcpy(req_id->dat, ptr + tkt->length, req_id->length);
+
+#ifndef NOENCRYPTION
+ /* And decrypt it with the session key from the ticket */
+ if (krb_ap_req_debug)
+ krb_log("About to decrypt authenticator");
+ sched = check_key_sched_cache(ad->session);
+ if (!sched) {
+ sched = &seskey_sched;
+ key_sched(ad->session, seskey_sched.s);
+ add_to_key_sched_cache(ad->session, &seskey_sched);
+ }
+ /* can't do much to optimize this... */
+ pcbc_encrypt((C_Block *) req_id->dat, (C_Block *) req_id->dat,
+ (long) req_id->length, sched->s, ad->session, DES_DECRYPT);
+ if (krb_ap_req_debug)
+ krb_log("Done.");
+#endif /* NOENCRYPTION */
+
+#define check_ptr() if ((ptr - (char *) req_id->dat) > req_id->length) return(RD_AP_MODIFIED);
+
+ ptr = (char *) req_id->dat;
+ strcpy(r_aname,ptr); /* Authentication name */
+ ptr += strlen(r_aname) + 1;
+ check_ptr();
+ strcpy(r_inst,ptr); /* Authentication instance */
+ ptr += strlen(r_inst) + 1;
+ check_ptr();
+ strcpy(r_realm,ptr); /* Authentication name */
+ ptr += strlen(r_realm) + 1;
+ check_ptr();
+ memcpy(&ad->checksum, ptr, 4); /* Checksum */
+ ptr += 4;
+ check_ptr();
+ if (swap_bytes)
+ swap_u_long(ad->checksum);
+ r_time_ms = *(ptr++); /* Time (fine) */
+#ifdef lint
+ /* XXX r_time_ms is set but not used. why??? */
+ /* this is a crock to get lint to shut up */
+ if (r_time_ms)
+ r_time_ms = 0;
+#endif /* lint */
+ check_ptr();
+ /* assume sizeof(r_time_sec) == 4 ?? */
+ memcpy(&r_time_sec,ptr,4); /* Time (coarse) */
+ if (swap_bytes)
+ swap_u_long(r_time_sec);
+
+ /* Check for authenticity of the request */
+ if (krb_ap_req_debug)
+ krb_log("Pname: %s %s",ad->pname,r_aname);
+ if (strcmp(ad->pname,r_aname) != 0)
+ return RD_AP_INCON;
+ if (strcmp(ad->pinst,r_inst) != 0)
+ return RD_AP_INCON;
+ if (krb_ap_req_debug)
+ krb_log("Realm: %s %s", ad->prealm, r_realm);
+ if (strcmp(ad->prealm,r_realm) != 0)
+ return RD_AP_INCON;
+
+ if (krb_ap_req_debug)
+ krb_log("Address: %d %d", ad->address, from_addr);
+ if (from_addr && (ad->address != from_addr))
+ return RD_AP_BADD;
+
+ delta_t = abs((int)(t_local.tv_sec - r_time_sec));
+ if (delta_t > CLOCK_SKEW) {
+ gettimeofday(&t_local, NULL);
+ delta_t = abs((int)(t_local.tv_sec - r_time_sec));
+ if (delta_t > CLOCK_SKEW) {
+ if (krb_ap_req_debug) {
+ krb_log("Time out of range: %d - %d = %d",
+ t_local.tv_sec, r_time_sec, delta_t);
+ }
+ return RD_AP_TIME;
+ }
+ }
+
+ /* Now check for expiration of ticket */
+
+ tkt_age = t_local.tv_sec - ad->time_sec;
+ if (krb_ap_req_debug) {
+ krb_log("Time: %d Issue Date: %d Diff: %d Life %x",
+ t_local.tv_sec, ad->time_sec, tkt_age, ad->life);
+ }
+
+ if (t_local.tv_sec < ad->time_sec) {
+ if (ad->time_sec - t_local.tv_sec > CLOCK_SKEW)
+ return RD_AP_NYV;
+ } else if (t_local.tv_sec - ad->time_sec > 5 * 60 * ad->life) {
+ return RD_AP_EXP;
+ }
+
+ /* All seems OK */
+ ad->reply.length = 0;
+
+ return RD_AP_OK;
+}
+#endif /* NOENCRYPTION */
+
+int
+krb_find_ticket(authent, ticket)
+ KTEXT authent, ticket;
+{
+ char *ptr; /* For stepping through */
+
+ /* Check for bogus length. */
+ if (authent->length <= 0)
+ return RD_AP_MODIFIED;
+
+ ptr = (char *) authent->dat;
+
+ /* check version */
+ if (KRB_PROT_VERSION != (unsigned int) *ptr++)
+ return RD_AP_VERSION;
+
+ /* Make sure msg type is ok. */
+ switch (*ptr++ & ~1) {
+ case AUTH_MSG_APPL_REQUEST:
+ case AUTH_MSG_APPL_REQUEST_MUTUAL:
+ break;
+ default:
+ return RD_AP_MSG_TYPE;
+ }
+
+ *ptr++; /* skip server key version */
+ ptr += strlen(ptr) + 1; /* skip the realm "hint" */
+
+ /* Get ticket from authenticator */
+ ticket->length = (int) *ptr++;
+ if ((ticket->length + (ptr + 1 - (char *) authent->dat)) > authent->length)
+ return RD_AP_MODIFIED;
+ memcpy((char *)(ticket->dat),ptr+1,ticket->length);
+
+ return RD_AP_OK;
+}
+
+static char local_realm_buffer[REALM_SZ+1];
+
+int
+krb_get_lrealm(r,n)
+ char *r;
+ int n;
+{
+ FILE *cnffile, *fopen();
+
+ if (n > 1)
+ return KFAILURE; /* Temporary restriction */
+
+ if (my_realm[0]) {
+ strcpy(r, my_realm);
+ return KSUCCESS;
+ }
+
+ if (local_realm_buffer[0]) {
+ strcpy(r, local_realm_buffer);
+ return KSUCCESS;
+ }
+
+ cnffile = fopen(KRB_CONF, "r");
+ if (cnffile == NULL) {
+ if (n == 1) {
+ strcpy(r, KRB_REALM);
+ return KSUCCESS;
+ } else {
+ return KFAILURE;
+ }
+ }
+
+ if (fscanf(cnffile,"%s",r) != 1) {
+ fclose(cnffile);
+ return KFAILURE;
+ }
+ fclose(cnffile);
+ return KSUCCESS;
+}
+
+int
+decomp_ticket(tkt, flags, pname, pinstance, prealm, paddress, session,
+ life, time_sec, sname, sinstance, key, key_s)
+ KTEXT tkt; /* The ticket to be decoded */
+ unsigned char *flags; /* Kerberos ticket flags */
+ char *pname; /* Authentication name */
+ char *pinstance; /* Principal's instance */
+ char *prealm; /* Principal's authentication domain */
+ unsigned long *paddress; /* Net address of entity
+ * requesting ticket */
+ C_Block session; /* Session key inserted in ticket */
+ int *life; /* Lifetime of the ticket */
+ unsigned long *time_sec; /* Issue time and date */
+ char *sname; /* Service name */
+ char *sinstance; /* Service instance */
+ C_Block key; /* Service's secret key
+ * (to decrypt the ticket) */
+ des_key_schedule key_s; /* The precomputed key schedule */
+{
+ static int tkt_swap_bytes;
+ unsigned char *uptr;
+ char *ptr = (char *)tkt->dat;
+
+#ifndef NOENCRYPTION
+ /* Do the decryption */
+ pcbc_encrypt((C_Block *)tkt->dat,(C_Block *)tkt->dat,
+ (long) tkt->length,key_s,(C_Block *) key,0);
+#endif /* ! NOENCRYPTION */
+
+ *flags = *ptr; /* get flags byte */
+ ptr += sizeof(*flags);
+ tkt_swap_bytes = 0;
+ if (HOST_BYTE_ORDER != ((*flags >> K_FLAG_ORDER)& 1))
+ tkt_swap_bytes++;
+
+ if (strlen(ptr) > ANAME_SZ)
+ return(KFAILURE);
+ strcpy(pname,ptr); /* pname */
+ ptr += strlen(pname) + 1;
+
+ if (strlen(ptr) > INST_SZ)
+ return(KFAILURE);
+ strcpy(pinstance,ptr); /* instance */
+ ptr += strlen(pinstance) + 1;
+
+ if (strlen(ptr) > REALM_SZ)
+ return(KFAILURE);
+ strcpy(prealm,ptr); /* realm */
+ ptr += strlen(prealm) + 1;
+ /* temporary hack until realms are dealt with properly */
+ if (*prealm == 0)
+ strcpy(prealm, ZGetRealm());
+
+ memcpy((char *)paddress, ptr, 4); /* net address */
+ ptr += 4;
+
+ memcpy((char *)session, ptr, 8); /* session key */
+ ptr+= 8;
+
+ /* get lifetime, being certain we don't get negative lifetimes */
+ uptr = (unsigned char *) ptr++;
+ *life = (int) *uptr;
+
+ memcpy((char *) time_sec, ptr, 4); /* issue time */
+ ptr += 4;
+ if (tkt_swap_bytes)
+ swap_u_long(*time_sec);
+
+ strcpy(sname,ptr); /* service name */
+ ptr += 1 + strlen(sname);
+
+ strcpy(sinstance,ptr); /* instance */
+ ptr += 1 + strlen(sinstance);
+
+ return(KSUCCESS);
+}
+#endif /* HAVE_KRB4 */
+
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains functions for dealing with Kerberos functions in the server.
+ *
+ * Created by: John T Kohl
+ *
+ * Copyright (c) 1988 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+/*
+ * $Id: kstuff.c,v 1.22 2000/02/11 20:59:35 ghudson Exp $
+ */
+
+#include "zserver.h"
+
+#ifndef lint
+#ifndef SABER
+static const char rcsid_kstuff_c[] = "$Id: kstuff.c,v 1.22 2000/02/11 20:59:35 ghudson Exp $";
+#endif
+#endif
+
+#ifdef HAVE_KRB4
+
+/* Keep a hash table mapping tickets to session keys, so we can do a fast
+ * check of the cryptographic checksum without doing and DES decryptions.
+ * Also remember the expiry time of the ticket, so that we can sweep the
+ * table periodically. */
+
+#define HASHTAB_SIZE 4091
+
+typedef struct hash_entry Hash_entry;
+
+/* The ticket comes at the end, in a variable-length array. */
+struct hash_entry {
+ C_Block session_key;
+ time_t expires;
+ char srcprincipal[ANAME_SZ+INST_SZ+REALM_SZ+4];
+ Hash_entry *next;
+ int ticket_len;
+ unsigned char ticket[1];
+};
+
+Hash_entry *hashtab[HASHTAB_SIZE];
+
+static int hash_ticket __P((unsigned char *, int));
+static void add_session_key __P((KTEXT, C_Block, char *, time_t));
+static int find_session_key __P((KTEXT, C_Block, char *));
+static ZChecksum_t compute_checksum __P((ZNotice_t *, C_Block));
+static ZChecksum_t compute_rlm_checksum __P((ZNotice_t *, C_Block));
+
+/*
+ * GetKerberosData
+ *
+ * get ticket from file descriptor and decode it.
+ * Return KFAILURE if we barf on reading the ticket, else return
+ * the value of rd_ap_req() applied to the ticket.
+ */
+int
+GetKerberosData(fd, haddr, kdata, service, srvtab)
+ int fd; /* file descr. to read from */
+ struct in_addr haddr; /* address of foreign host on fd */
+ AUTH_DAT *kdata; /* kerberos data (returned) */
+ char *service; /* service principal desired */
+ char *srvtab; /* file to get keys from */
+{
+ char p[20];
+ KTEXT_ST ticket; /* will get Kerberos ticket from client */
+ int i;
+ char instance[INST_SZ];
+
+ /*
+ * Get the Kerberos ticket. The first few characters, terminated
+ * by a blank, should give us a length; then get than many chars
+ * which will be the ticket proper.
+ */
+ for (i=0; i<20; i++) {
+ if (read(fd, &p[i], 1) != 1) {
+ syslog(LOG_WARNING,"bad read tkt len");
+ return(KFAILURE);
+ }
+ if (p[i] == ' ') {
+ p[i] = '\0';
+ break;
+ }
+ }
+ ticket.length = atoi(p);
+ if ((i==20) || (ticket.length<=0) || (ticket.length>MAX_KTXT_LEN)) {
+ syslog(LOG_WARNING,"bad tkt len %d",ticket.length);
+ return(KFAILURE);
+ }
+ for (i=0; i<ticket.length; i++) {
+ if (read(fd, (caddr_t) &(ticket.dat[i]), 1) != 1) {
+ syslog(LOG_WARNING,"bad tkt read");
+ return(KFAILURE);
+ }
+ }
+ /*
+ * now have the ticket. use it to get the authenticated
+ * data from Kerberos.
+ */
+ (void) strcpy(instance,"*"); /* let Kerberos fill it in */
+
+ return(krb_rd_req(&ticket, service, instance, haddr.s_addr,
+ kdata, srvtab ? srvtab : ""));
+}
+
+/*
+ * SendKerberosData
+ *
+ * create and transmit a ticket over the file descriptor for service.host
+ * return failure codes if appropriate, or 0 if we
+ * get the ticket and write it to the file descriptor
+ */
+
+Code_t
+SendKerberosData(fd, ticket, service, host)
+ int fd; /* file descriptor to write onto */
+ KTEXT ticket; /* where to put ticket (return) */
+ char *service; /* service name, foreign host */
+ char *host;
+{
+ int rem;
+ char p[32];
+ char krb_realm[REALM_SZ];
+ int written;
+ int size_to_write;
+
+ rem = krb_mk_req(ticket, service, host, ZGetRealm(), (u_long) 0);
+ if (rem != KSUCCESS)
+ return rem + krb_err_base;
+
+ (void) sprintf(p,"%d ",ticket->length);
+ size_to_write = strlen (p);
+ if ((written = write(fd, p, size_to_write)) != size_to_write)
+ return (written < 0) ? errno : ZSRV_PKSHORT;
+ if ((written = write(fd, (caddr_t) (ticket->dat), ticket->length))
+ != ticket->length)
+ return (written < 0) ? errno : ZSRV_PKSHORT;
+
+ return 0;
+}
+
+#endif /* HAVE_KRB4 */
+
+Code_t
+ZCheckRealmAuthentication(notice, from, realm)
+ ZNotice_t *notice;
+ struct sockaddr_in *from;
+ char *realm;
+{
+#ifdef HAVE_KRB4
+ int result;
+ char rlmprincipal[ANAME_SZ+INST_SZ+REALM_SZ+4];
+ char srcprincipal[ANAME_SZ+INST_SZ+REALM_SZ+4];
+ KTEXT_ST authent, ticket;
+ AUTH_DAT dat;
+ ZChecksum_t checksum;
+ CREDENTIALS cred;
+ C_Block session_key;
+
+ if (!notice->z_auth)
+ return ZAUTH_NO;
+
+ /* Check for bogus authentication data length. */
+ if (notice->z_authent_len <= 0)
+ return ZAUTH_FAILED;
+
+ /* Read in the authentication data. */
+ if (ZReadAscii(notice->z_ascii_authent,
+ strlen(notice->z_ascii_authent)+1,
+ (unsigned char *)authent.dat,
+ notice->z_authent_len) == ZERR_BADFIELD) {
+ return ZAUTH_FAILED;
+ }
+ authent.length = notice->z_authent_len;
+
+ /* Copy the ticket out of the authentication data. */
+ if (krb_find_ticket(&authent, &ticket) != RD_AP_OK)
+ return ZAUTH_FAILED;
+
+ (void) sprintf(rlmprincipal, "%s.%s@%s", SERVER_SERVICE,
+ SERVER_INSTANCE, realm);
+
+ /* Try to do a fast check against the cryptographic checksum. */
+ if (find_session_key(&ticket, session_key, srcprincipal) >= 0) {
+ if (strcmp(srcprincipal, rlmprincipal) != 0)
+ return ZAUTH_FAILED;
+ if (notice->z_time.tv_sec - NOW > CLOCK_SKEW)
+ return ZAUTH_FAILED;
+ checksum = compute_rlm_checksum(notice, session_key);
+
+ /* If checksum matches, packet is authentic. If not, we might
+ * have an outdated session key, so keep going the slow way.
+ */
+ if (checksum == notice->z_checksum) {
+ memcpy(__Zephyr_session, session_key, sizeof(C_Block));
+ return ZAUTH_YES;
+ }
+ }
+
+ /* We don't have the session key cached; do it the long way. */
+ result = krb_rd_req(&authent, SERVER_SERVICE, SERVER_INSTANCE,
+ from->sin_addr.s_addr, &dat, srvtab_file);
+ if (result == RD_AP_OK) {
+ sprintf(srcprincipal, "%s%s%s@%s", dat.pname, dat.pinst[0] ? "." : "",
+ dat.pinst, dat.prealm);
+ if (strcmp(rlmprincipal, srcprincipal))
+ return ZAUTH_FAILED;
+ } else {
+ return ZAUTH_FAILED; /* didn't decode correctly */
+ }
+
+ /* Check the cryptographic checksum. */
+#ifdef NOENCRYPTION
+ our_checksum = 0;
+#else
+ checksum = compute_rlm_checksum(notice, dat.session);
+#endif
+ if (checksum != notice->z_checksum)
+ return ZAUTH_FAILED;
+
+ /* Record the session key, expiry time, and source principal in the
+ * hash table, so we can do a fast check next time. */
+ add_session_key(&ticket, dat.session, srcprincipal,
+ (time_t)(dat.time_sec + dat.life * 5 * 60));
+
+ return ZAUTH_YES;
+
+#else /* !HAVE_KRB4 */
+ return (notice->z_auth) ? ZAUTH_YES : ZAUTH_NO;
+#endif
+}
+
+Code_t
+ZCheckAuthentication(notice, from)
+ ZNotice_t *notice;
+ struct sockaddr_in *from;
+{
+#ifdef HAVE_KRB4
+ int result;
+ char srcprincipal[ANAME_SZ+INST_SZ+REALM_SZ+4];
+ KTEXT_ST authent, ticket;
+ AUTH_DAT dat;
+ ZChecksum_t checksum;
+ C_Block session_key;
+
+ if (!notice->z_auth)
+ return ZAUTH_NO;
+
+ /* Check for bogus authentication data length. */
+ if (notice->z_authent_len <= 0)
+ return ZAUTH_FAILED;
+
+ /* Read in the authentication data. */
+ if (ZReadAscii(notice->z_ascii_authent,
+ strlen(notice->z_ascii_authent)+1,
+ (unsigned char *)authent.dat,
+ notice->z_authent_len) == ZERR_BADFIELD) {
+ return ZAUTH_FAILED;
+ }
+ authent.length = notice->z_authent_len;
+
+ /* Copy the ticket out of the authentication data. */
+ if (krb_find_ticket(&authent, &ticket) != RD_AP_OK)
+ return ZAUTH_FAILED;
+
+ /* Try to do a fast check against the cryptographic checksum. */
+ if (find_session_key(&ticket, session_key, srcprincipal) >= 0) {
+ if (strcmp(srcprincipal, notice->z_sender) != 0)
+ return ZAUTH_FAILED;
+ if (notice->z_time.tv_sec - NOW > CLOCK_SKEW)
+ return ZAUTH_FAILED;
+ checksum = compute_checksum(notice, session_key);
+
+ /* If checksum matches, packet is authentic. If not, we might
+ * have an outdated session key, so keep going the slow way.
+ */
+ if (checksum == notice->z_checksum) {
+ memcpy(__Zephyr_session, session_key, sizeof(C_Block));
+ return ZAUTH_YES;
+ }
+ }
+
+ /* We don't have the session key cached; do it the long way. */
+ result = krb_rd_req(&authent, SERVER_SERVICE, SERVER_INSTANCE,
+ from->sin_addr.s_addr, &dat, srvtab_file);
+ if (result == RD_AP_OK) {
+ memcpy(__Zephyr_session, dat.session, sizeof(C_Block));
+ sprintf(srcprincipal, "%s%s%s@%s", dat.pname, dat.pinst[0] ? "." : "",
+ dat.pinst, dat.prealm);
+ if (strcmp(srcprincipal, notice->z_sender))
+ return ZAUTH_FAILED;
+ } else {
+ return ZAUTH_FAILED; /* didn't decode correctly */
+ }
+
+ /* Check the cryptographic checksum. */
+#ifdef NOENCRYPTION
+ our_checksum = 0;
+#else
+ checksum = compute_checksum(notice, dat.session);
+#endif
+ if (checksum != notice->z_checksum)
+ return ZAUTH_FAILED;
+
+ /* Record the session key, expiry time, and source principal in the
+ * hash table, so we can do a fast check next time. */
+ add_session_key(&ticket, dat.session, srcprincipal,
+ (time_t)(dat.time_sec + dat.life * 5 * 60));
+
+ return ZAUTH_YES;
+
+#else /* !HAVE_KRB4 */
+ return (notice->z_auth) ? ZAUTH_YES : ZAUTH_NO;
+#endif
+}
+
+#ifdef HAVE_KRB4
+
+static int hash_ticket(p, len)
+ unsigned char *p;
+ int len;
+{
+ unsigned long hashval = 0, g;
+
+ for (; len > 0; p++, len--) {
+ hashval = (hashval << 4) + *p;
+ g = hashval & 0xf0000000;
+ if (g) {
+ hashval ^= g >> 24;
+ hashval ^= g;
+ }
+ }
+ return hashval % HASHTAB_SIZE;
+}
+
+static void add_session_key(ticket, session_key, srcprincipal, expires)
+ KTEXT ticket;
+ C_Block session_key;
+ char *srcprincipal;
+ time_t expires;
+{
+ Hash_entry *entry;
+ int hashval;
+
+ /* If we can't allocate memory for the hash table entry, just forget
+ * about it. */
+ entry = (Hash_entry *) malloc(sizeof(Hash_entry) - 1 + ticket->length);
+ if (!entry)
+ return;
+
+ /* Initialize the new entry. */
+ memcpy(entry->session_key, session_key, sizeof(entry->session_key));
+ strcpy(entry->srcprincipal, srcprincipal);
+ entry->expires = expires;
+ entry->ticket_len = ticket->length;
+ memcpy(entry->ticket, ticket->dat, ticket->length * sizeof(unsigned char));
+
+ /* Insert the new entry in the hash table. */
+ hashval = hash_ticket(ticket->dat, ticket->length);
+ entry->next = hashtab[hashval];
+ hashtab[hashval] = entry;
+}
+
+static int find_session_key(ticket, key, srcprincipal)
+ KTEXT ticket;
+ C_Block key;
+ char *srcprincipal;
+{
+ unsigned char *dat;
+ int hashval, len;
+ Hash_entry *entry;
+
+ dat = ticket->dat;
+ len = ticket->length;
+ hashval = hash_ticket(dat, len);
+
+ for (entry = hashtab[hashval]; entry; entry = entry->next) {
+ if (entry->ticket_len == len && memcmp(entry->ticket, dat, len) == 0) {
+ memcpy(key, entry->session_key, sizeof(entry->session_key));
+ strcpy(srcprincipal, entry->srcprincipal);
+ return 0;
+ }
+ }
+ return -1;
+}
+
+static ZChecksum_t compute_checksum(notice, session_key)
+ ZNotice_t *notice;
+ C_Block session_key;
+{
+#ifdef NOENCRYPTION
+ return 0;
+#else
+ ZChecksum_t checksum;
+ char *cstart, *cend, *hstart = notice->z_packet, *hend = notice->z_message;
+
+ cstart = notice->z_default_format + strlen(notice->z_default_format) + 1;
+ cend = cstart + strlen(cstart) + 1;
+ checksum = des_quad_cksum(hstart, NULL, cstart - hstart, 0, session_key);
+ checksum ^= des_quad_cksum(cend, NULL, hend - cend, 0, session_key);
+ checksum ^= des_quad_cksum(notice->z_message, NULL, notice->z_message_len,
+ 0, session_key);
+ return checksum;
+#endif
+}
+
+static ZChecksum_t compute_rlm_checksum(notice, session_key)
+ ZNotice_t *notice;
+ C_Block session_key;
+{
+#ifdef NOENCRYPTION
+ return 0;
+#else
+ ZChecksum_t checksum;
+ char *cstart, *cend, *hstart = notice->z_packet, *hend = notice->z_message;
+
+ cstart = notice->z_default_format + strlen(notice->z_default_format) + 1;
+ cend = cstart + strlen(cstart) + 1;
+ checksum = des_quad_cksum(hstart, NULL, cstart - hstart, 0, session_key);
+ return checksum;
+#endif
+}
+
+void sweep_ticket_hash_table(arg)
+ void *arg;
+{
+ int i;
+ Hash_entry **ptr, *entry;
+
+ for (i = 0; i < HASHTAB_SIZE; i++) {
+ ptr = &hashtab[i];
+ while (*ptr) {
+ entry = *ptr;
+ if (entry->expires < NOW) {
+ *ptr = entry->next;
+ free(entry);
+ } else {
+ ptr = &(*ptr)->next;
+ }
+ }
+ }
+ timer_set_rel(SWEEP_INTERVAL, sweep_ticket_hash_table, NULL);
+}
+
+#endif /* HAVE_KRB4 */
+
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains the main loop of the Zephyr server
+ *
+ * Created by: John T. Kohl
+ *
+ * $Id: main.c,v 1.68 1999/01/22 23:19:45 ghudson Exp $
+ *
+ * Copyright (c) 1987,1988,1991 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <zephyr/mit-copyright.h>
+#include "zserver.h"
+#include <sys/socket.h>
+#include <sys/resource.h>
+
+#ifndef lint
+#ifndef SABER
+static const char rcsid_main_c[] =
+ "$Id: main.c,v 1.68 1999/01/22 23:19:45 ghudson Exp $";
+#endif
+#endif
+
+/*
+ * Server loop for Zephyr.
+ */
+
+/*
+ The Zephyr server maintains several linked lists of information.
+
+ There is an array of servers (otherservers) initialized and maintained
+ by server_s.c.
+
+ Each server descriptor contains a pointer to a linked list of hosts
+ which are ``owned'' by that server. The first server is the ``limbo''
+ server which owns any host which was formerly owned by a dead server.
+
+ Each of these host list entries has an IP address and a pointer to a
+ linked list of clients on that host.
+
+ Each client has a sockaddr_in, a list of subscriptions, and possibly
+ a session key.
+
+ In addition, the class manager has copies of the pointers to the
+ clients which are registered with a particular class, the
+ not-yet-acknowledged list has copies of pointers to some clients,
+ and the hostm manager may have copies of pointers to some clients
+ (if the client has not acknowledged a packet after a given timeout).
+*/
+
+#define EVER (;;) /* don't stop looping */
+
+static int do_net_setup __P((void));
+static int initialize __P((void));
+static void usage __P((void));
+static void do_reset __P((void));
+static RETSIGTYPE bye __P((int));
+static RETSIGTYPE dbug_on __P((int));
+static RETSIGTYPE dbug_off __P((int));
+static RETSIGTYPE sig_dump_db __P((int));
+static RETSIGTYPE sig_dump_strings __P((int));
+static RETSIGTYPE reset __P((int));
+static RETSIGTYPE reap __P((int));
+static void read_from_dump __P((char *dumpfile));
+static void dump_db __P((void));
+static void dump_strings __P((void));
+
+#ifndef DEBUG
+static void detach __P((void));
+#endif
+
+static short doreset = 0; /* if it becomes 1, perform
+ reset functions */
+
+int nfds; /* max file descriptor for select() */
+int srv_socket; /* dgram socket for clients
+ and other servers */
+int bdump_socket = -1; /* brain dump socket fd
+ (closed most of the time) */
+fd_set interesting; /* the file descrips we are listening
+ to right now */
+struct sockaddr_in srv_addr; /* address of the socket */
+
+Unacked *nacklist = NULL; /* list of packets waiting for ack's */
+
+unsigned short hm_port; /* host manager receiver port */
+unsigned short hm_srv_port; /* host manager server sending port */
+
+char *programname; /* set to the basename of argv[0] */
+char myname[MAXHOSTNAMELEN]; /* my host name */
+
+#ifndef HAVE_HESIOD
+char list_file[128];
+#endif
+#ifdef HAVE_KRB4
+char srvtab_file[128];
+char my_realm[REALM_SZ];
+static char tkt_file[128];
+#endif
+char acl_dir[128];
+char subs_file[128];
+
+int zdebug;
+#ifdef DEBUG_MALLOC
+int dump_malloc_stats = 0;
+unsigned long m_size;
+#endif
+#ifdef DEBUG
+int zalone;
+#endif
+
+struct timeval t_local; /* store current time for other uses */
+
+static int dump_db_flag = 0;
+static int dump_strings_flag = 0;
+
+u_long npackets; /* number of packets processed */
+time_t uptime; /* when we started operations */
+static int nofork;
+struct in_addr my_addr;
+char *bdump_version = "1.2";
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int nfound; /* #fildes ready on select */
+ fd_set readable;
+ struct timeval tv;
+ int init_from_dump = 0;
+ char *dumpfile;
+#ifdef _POSIX_VERSION
+ struct sigaction action;
+#endif
+ int optchar; /* option processing */
+ extern char *optarg;
+ extern int optind;
+
+#ifndef HAVE_HESIOD
+ sprintf(list_file, "%s/zephyr/%s", SYSCONFDIR, SERVER_LIST_FILE);
+#endif
+#ifdef HAVE_KRB4
+ sprintf(srvtab_file, "%s/zephyr/%s", SYSCONFDIR, ZEPHYR_SRVTAB);
+ sprintf(tkt_file, "%s/zephyr/%s", SYSCONFDIR, ZEPHYR_TKFILE);
+#endif
+ sprintf(acl_dir, "%s/zephyr/%s", SYSCONFDIR, ZEPHYR_ACL_DIR);
+ sprintf(subs_file, "%s/zephyr/%s", SYSCONFDIR, DEFAULT_SUBS_FILE);
+
+ /* set name */
+ programname = strrchr(argv[0],'/');
+ programname = (programname) ? programname + 1 : argv[0];
+
+ /* process arguments */
+ while ((optchar = getopt(argc, argv, "dsnv:f:k:")) != EOF) {
+ switch(optchar) {
+ case 'd':
+ zdebug = 1;
+ break;
+#ifdef DEBUG
+ case 's':
+ zalone = 1;
+ break;
+#endif
+ case 'n':
+ nofork = 1;
+ break;
+ case 'k':
+#ifdef HAVE_KRB4
+ strncpy(my_realm, optarg, REALM_SZ);
+#endif
+ break;
+ case 'v':
+ bdump_version = optarg;
+ break;
+ case 'f':
+ init_from_dump = 0;
+ dumpfile = optarg;
+ break;
+ case '?':
+ default:
+ usage();
+ /*NOTREACHED*/
+ }
+ }
+
+#ifdef HAVE_KRB4
+ /* if there is no readable srvtab and we are not standalone, there
+ is no possible way we can succeed, so we exit */
+
+ if (access(srvtab_file, R_OK)
+#ifdef DEBUG
+ && !zalone
+#endif /* DEBUG */
+ ) {
+ fprintf(stderr, "NO ZEPHYR SRVTAB (%s) available; exiting\n",
+ srvtab_file);
+ exit(1);
+ }
+ /* Use local realm if not specified on command line. */
+ if (!*my_realm) {
+ if (krb_get_lrealm(my_realm, 1) != KSUCCESS) {
+ fputs("Couldn't get local Kerberos realm; exiting.\n", stderr);
+ exit(1);
+ }
+ }
+#endif /* HAVE_KRB4 */
+
+#ifndef DEBUG
+ if (!nofork)
+ detach();
+#endif /* DEBUG */
+
+ /* open log */
+ OPENLOG(programname, LOG_PID, LOG_LOCAL6);
+
+#if defined (DEBUG) && 0
+ if (zalone)
+ syslog(LOG_DEBUG, "standalone operation");
+#endif
+#if 0
+ if (zdebug)
+ syslog(LOG_DEBUG, "debugging on");
+#endif
+
+ /* set up sockets & my_addr and myname,
+ find other servers and set up server table, initialize queues
+ for retransmits, initialize error tables,
+ set up restricted classes */
+
+ /* Initialize t_local for other uses */
+ gettimeofday(&t_local, NULL);
+
+ if (initialize())
+ exit(1);
+
+ if (init_from_dump)
+ read_from_dump(dumpfile);
+
+ /* Seed random number set. */
+ srandom(getpid() ^ time(0));
+
+ /* chdir to somewhere where a core dump will survive */
+ if (chdir(TEMP_DIRECTORY) != 0)
+ syslog(LOG_ERR, "chdir failed (%m) (execution continuing)");
+
+ FD_ZERO(&interesting);
+ FD_SET(srv_socket, &interesting);
+
+ nfds = srv_socket + 1;
+
+
+#ifdef _POSIX_VERSION
+ action.sa_flags = 0;
+ sigemptyset(&action.sa_mask);
+
+ action.sa_handler = bye;
+ sigaction(SIGINT, &action, NULL);
+ sigaction(SIGTERM, &action, NULL);
+
+ action.sa_handler = dbug_on;
+ sigaction(SIGUSR1, &action, NULL);
+
+ action.sa_handler = dbug_off;
+ sigaction(SIGUSR2, &action, NULL);
+
+ action.sa_handler = reap;
+ sigaction(SIGCHLD, &action, NULL);
+
+ action.sa_handler = sig_dump_db;
+ sigaction(SIGFPE, &action, NULL);
+
+#ifdef SIGEMT
+ action.sa_handler = sig_dump_strings;
+ sigaction(SIGEMT, &action, NULL);
+#endif
+
+ action.sa_handler = reset;
+ sigaction(SIGHUP, &action, NULL);
+#else /* !posix */
+ signal(SIGINT, bye);
+ signal(SIGTERM, bye);
+ signal(SIGUSR1, dbug_on);
+ signal(SIGUSR2, dbug_off);
+ signal(SIGCHLD, reap);
+ signal(SIGFPE, sig_dump_db);
+#ifdef SIGEMT
+ signal(SIGEMT, sig_dump_strings);
+#endif
+ signal(SIGHUP, reset);
+#endif /* _POSIX_VERSION */
+
+ syslog(LOG_NOTICE, "Ready for action");
+
+ /* Reinitialize t_local now that initialization is done. */
+ gettimeofday(&t_local, NULL);
+ uptime = NOW;
+#ifdef HAVE_KRB4
+ timer_set_rel(SWEEP_INTERVAL, sweep_ticket_hash_table, NULL);
+#endif
+
+#ifdef DEBUG_MALLOC
+ malloc_inuse(&m_size);
+#endif
+ for EVER {
+ if (doreset)
+ do_reset();
+
+ if (dump_db_flag)
+ dump_db();
+ if (dump_strings_flag)
+ dump_strings();
+
+ timer_process();
+
+ readable = interesting;
+ if (msgs_queued()) {
+ /* when there is input in the queue, we
+ artificially set up to pick up the input */
+ nfound = 1;
+ FD_ZERO(&readable);
+ } else {
+ nfound = select(nfds, &readable, NULL, NULL, timer_timeout(&tv));
+ }
+
+ /* Initialize t_local for other uses */
+ gettimeofday(&t_local, (struct timezone *)0);
+
+ /* don't flame about EINTR, since a SIGUSR1 or SIGUSR2
+ can generate it by interrupting the select */
+ if (nfound < 0) {
+ if (errno != EINTR)
+ syslog(LOG_WARNING, "select error: %m");
+#ifdef DEBUG_MALLOC
+ if (dump_malloc_stats) {
+ unsigned long foo,histid2;
+
+ dump_malloc_stats = 0;
+ foo = malloc_inuse(&histid2);
+ printf("Total inuse: %d\n",foo);
+ malloc_list(2,m_size,histid2);
+ }
+#endif
+ continue;
+ }
+
+ if (nfound == 0) {
+ /* either we timed out or we were just
+ polling for input. Either way we want to continue
+ the loop, and process the next timeout */
+ continue;
+ } else {
+ if (bdump_socket >= 0 && FD_ISSET(bdump_socket,&readable))
+ bdump_send();
+ else if (msgs_queued() || FD_ISSET(srv_socket, &readable))
+ handle_packet();
+ else
+ syslog(LOG_ERR, "select weird?!?!");
+ }
+ }
+}
+
+/* Initialize net stuff.
+ Set up the server array.
+ Initialize the packet ack queues to be empty.
+ Initialize the error tables.
+ Restrict certain classes.
+ */
+
+static int
+initialize()
+{
+ if (do_net_setup())
+ return(1);
+
+ server_init();
+
+#ifdef HAVE_KRB4
+ krb_set_tkt_string(tkt_file);
+#endif
+ realm_init();
+
+ ZSetServerState(1);
+ ZInitialize(); /* set up the library */
+ init_zsrv_err_tbl(); /* set up err table */
+
+ ZSetFD(srv_socket); /* set up the socket as the input fildes */
+
+ /* set up default strings */
+
+ class_control = make_string(ZEPHYR_CTL_CLASS, 1);
+ class_admin = make_string(ZEPHYR_ADMIN_CLASS, 1);
+ class_hm = make_string(HM_CTL_CLASS, 1);
+ class_ulogin = make_string(LOGIN_CLASS, 1);
+ class_ulocate = make_string(LOCATE_CLASS, 1);
+ wildcard_instance = make_string(WILDCARD_INSTANCE, 1);
+ empty = make_string("", 0);
+
+ /* restrict certain classes */
+ access_init();
+ return 0;
+}
+
+/*
+ * Set up the server and client sockets, and initialize my_addr and myname
+ */
+
+static int
+do_net_setup()
+{
+ struct servent *sp;
+ struct hostent *hp;
+ char hostname[MAXHOSTNAMELEN+1];
+ int flags;
+
+ if (gethostname(hostname, MAXHOSTNAMELEN + 1)) {
+ syslog(LOG_ERR, "no hostname: %m");
+ return 1;
+ }
+ hp = gethostbyname(hostname);
+ if (!hp) {
+ syslog(LOG_ERR, "no gethostbyname repsonse");
+ strncpy(myname, hostname, MAXHOSTNAMELEN);
+ return 1;
+ }
+ strncpy(myname, hp->h_name, MAXHOSTNAMELEN);
+ memcpy(&my_addr, hp->h_addr, sizeof(hp->h_addr));
+
+ setservent(1); /* keep file/connection open */
+
+ memset(&srv_addr, 0, sizeof(srv_addr));
+ srv_addr.sin_family = AF_INET;
+ sp = getservbyname(SERVER_SVCNAME, "udp");
+ srv_addr.sin_port = (sp) ? sp->s_port : SERVER_SVC_FALLBACK;
+
+ sp = getservbyname(HM_SVCNAME, "udp");
+ hm_port = (sp) ? sp->s_port : HM_SVC_FALLBACK;
+
+ sp = getservbyname(HM_SRV_SVCNAME, "udp");
+ hm_srv_port = (sp) ? sp->s_port : HM_SRV_SVC_FALLBACK;
+
+ srv_socket = socket(AF_INET, SOCK_DGRAM, 0);
+ if (srv_socket < 0) {
+ syslog(LOG_ERR, "client_sock failed: %m");
+ return 1;
+ }
+ if (bind(srv_socket, (struct sockaddr *) &srv_addr,
+ sizeof(srv_addr)) < 0) {
+ syslog(LOG_ERR, "client bind failed: %m");
+ return 1;
+ }
+
+ /* set not-blocking */
+#ifdef _POSIX_VERSION
+ flags = fcntl(srv_socket, F_GETFL);
+ flags |= O_NONBLOCK;
+ fcntl(srv_socket, F_SETFL, flags);
+#else
+ flags = 1;
+ ioctl(srv_socket, FIONBIO, &flags);
+#endif
+
+ return 0;
+}
+
+
+/*
+ * print out a usage message.
+ */
+
+static void
+usage()
+{
+#ifdef DEBUG
+ fprintf(stderr, "Usage: %s [-d] [-s] [-n] [-k realm] [-f dumpfile]\n",
+ programname);
+#else
+ fprintf(stderr, "Usage: %s [-d] [-n] [-k realm] [-f dumpfile]\n",
+ programname);
+#endif /* DEBUG */
+ exit(2);
+}
+
+int
+packets_waiting()
+{
+ fd_set readable, initial;
+ struct timeval tv;
+
+ if (msgs_queued())
+ return 1;
+ FD_ZERO(&initial);
+ FD_SET(srv_socket, &initial);
+ readable = initial;
+ tv.tv_sec = tv.tv_usec = 0;
+ return (select(srv_socket + 1, &readable, NULL, NULL, &tv) > 0);
+}
+
+static RETSIGTYPE
+bye(sig)
+ int sig;
+{
+ server_shutdown(); /* tell other servers */
+ hostm_shutdown(); /* tell our hosts */
+#ifdef HAVE_KRB4
+ dest_tkt();
+#endif
+ syslog(LOG_NOTICE, "goodbye (sig %d)", sig);
+ exit(0);
+}
+
+static RETSIGTYPE
+dbug_on(sig)
+ int sig;
+{
+ syslog(LOG_DEBUG, "debugging turned on");
+#ifdef DEBUG_MALLOC
+ dump_malloc_stats = 1;
+#endif
+ zdebug = 1;
+}
+
+static RETSIGTYPE
+dbug_off(sig)
+ int sig;
+{
+ syslog(LOG_DEBUG, "debugging turned off");
+#ifdef DEBUG_MALLOC
+ malloc_inuse(&m_size);
+#endif
+ zdebug = 0;
+}
+
+int fork_for_dump = 0;
+
+static RETSIGTYPE
+sig_dump_strings(sig)
+ int sig;
+{
+ dump_strings_flag = 1;
+}
+
+static void dump_strings()
+{
+ char filename[128];
+
+ FILE *fp;
+ int oerrno = errno;
+
+ sprintf(filename, "%szephyr.strings", TEMP_DIRECTORY);
+ fp = fopen (filename, "w");
+ if (!fp) {
+ syslog(LOG_ERR, "can't open strings dump file: %m");
+ errno = oerrno;
+ dump_strings_flag = 0;
+ return;
+ }
+ syslog(LOG_INFO, "dumping strings to disk");
+ print_string_table(fp);
+ if (fclose(fp) == EOF)
+ syslog(LOG_ERR, "error writing strings dump file");
+ else
+ syslog(LOG_INFO, "dump done");
+ oerrno = errno;
+ dump_strings_flag = 0;
+ return;
+}
+
+static RETSIGTYPE
+sig_dump_db(sig)
+ int sig;
+{
+ dump_db_flag = 1;
+}
+
+static void dump_db()
+{
+ /* dump the in-core database to human-readable form on disk */
+ FILE *fp;
+ int oerrno = errno;
+ int pid;
+ char filename[128];
+
+ pid = (fork_for_dump) ? fork() : -1;
+ if (pid > 0) {
+ dump_db_flag = 0;
+ return;
+ }
+ sprintf(filename, "%szephyr.db", TEMP_DIRECTORY);
+ fp = fopen(filename, "w");
+ if (!fp) {
+ syslog(LOG_ERR, "can't open dump database");
+ errno = oerrno;
+ dump_db_flag = 0;
+ return;
+ }
+ syslog(LOG_INFO, "dumping to disk");
+ server_dump_servers(fp);
+ uloc_dump_locs(fp);
+ client_dump_clients(fp);
+ triplet_dump_subs(fp);
+ realm_dump_realms(fp);
+ syslog(LOG_INFO, "dump done");
+ if (fclose(fp) == EOF)
+ syslog(LOG_ERR, "can't close dump db");
+ if (pid == 0)
+ exit(0);
+ errno = oerrno;
+ dump_db_flag = 0;
+}
+
+static RETSIGTYPE
+reset(sig)
+ int sig;
+{
+#if 1
+ zdbug((LOG_DEBUG,"reset()"));
+#endif
+ doreset = 1;
+}
+
+static RETSIGTYPE
+reap(sig)
+ int sig;
+{
+ int oerrno = errno;
+
+#ifdef _POSIX_VERSION
+ int waitb;
+ while (waitpid(-1, &waitb, WNOHANG) == 0) ;
+#else
+ union wait waitb;
+ while (wait3 (&waitb, WNOHANG, (struct rusage*) 0) == 0) ;
+#endif
+
+ errno = oerrno;
+}
+
+static void
+do_reset()
+{
+ int oerrno = errno;
+#ifdef _POSIX_VERSION
+ sigset_t mask, omask;
+#else
+ int omask;
+#endif
+#if 0
+ zdbug((LOG_DEBUG,"do_reset()"));
+#endif
+#ifdef _POSIX_VERSION
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGHUP);
+ sigprocmask(SIG_BLOCK, &mask, &omask);
+#else
+ omask = sigblock(sigmask(SIGHUP));
+#endif
+
+ /* reset various things in the server's state */
+ subscr_reset();
+ server_reset();
+ access_reinit();
+ syslog(LOG_INFO, "restart completed");
+ doreset = 0;
+ errno = oerrno;
+#ifdef _POSIX_VERSION
+ sigprocmask(SIG_SETMASK, &omask, (sigset_t *)0);
+#else
+ sigsetmask(omask);
+#endif
+}
+
+#ifndef DEBUG
+/*
+ * detach from the terminal
+ */
+
+static void
+detach()
+{
+ /* detach from terminal and fork. */
+ int i;
+ long size;
+
+#ifdef _POSIX_VERSION
+ size = sysconf(_SC_OPEN_MAX);
+#else
+ size = getdtablesize();
+#endif
+ /* profiling seems to get confused by fork() */
+ i = fork ();
+ if (i) {
+ if (i < 0)
+ perror("fork");
+ exit(0);
+ }
+
+ for (i = 0; i < size; i++)
+ close(i);
+
+ i = open("/dev/tty", O_RDWR, 666);
+#ifdef TIOCNOTTY /* Only necessary on old systems. */
+ ioctl(i, TIOCNOTTY, NULL);
+#endif
+ close(i);
+#ifdef _POSIX_VERSION
+ setsid();
+#endif
+}
+#endif /* not DEBUG */
+
+static void
+read_from_dump(dumpfile)
+ char *dumpfile;
+{
+ /* Not yet implemented. */
+ return;
+}
+
--- /dev/null
+#!/bin/sh
+#
+
+u=${USER-the_zephyr_builder}
+h=`hostname`
+t=`date`
+
+umask 002
+/bin/echo "#define ZSERVER_VERSION_STRING \"(${t}) ${u}@${h}\"" >version.h
--- /dev/null
+#include "zserver.h"
+#include <sys/socket.h>
+
+Unacked *rlm_nacklist = NULL; /* not acked list for realm-realm
+ packets */
+Realm *otherrealms; /* points to an array of the known
+ servers */
+int nrealms = 0; /* number of other realms */
+
+static void get_realm_addrs __P(());
+static void realm_sendit __P((ZNotice_t *notice, struct sockaddr_in *who, int auth, Realm *realm, int ack_to_sender));
+static void realm_sendit_auth __P((ZNotice_t *notice, struct sockaddr_in *who, int auth, Realm *realm, int ack_to_sender));
+static void rlm_ack __P((ZNotice_t *notice, Unacked *nacked));
+static void rlm_nack_cancel __P((ZNotice_t *notice, struct sockaddr_in *who));
+static void rlm_new_ticket __P(());
+static void rlm_rexmit __P((void *arg));
+static Code_t realm_ulocate_dispatch __P((ZNotice_t *notice,int auth,struct sockaddr_in *who,Server *server,Realm *realm));
+#ifdef HAVE_KRB4
+static Code_t ticket_retrieve __P((Realm *realm));
+#endif
+
+char *
+realm_expand_realm(realmname)
+char *realmname;
+{
+ static char expand[REALM_SZ];
+ static char krb_realm[REALM_SZ+1];
+ char *cp1, *cp2;
+ int retval;
+ FILE *rlm_file;
+ char list_file[128];
+ char linebuf[BUFSIZ];
+ char scratch[128];
+
+ /* upcase what we got */
+ cp2 = realmname;
+ cp1 = expand;
+ while (*cp2) {
+ *cp1++ = toupper(*cp2++);
+ }
+ *cp1 = '\0';
+
+ sprintf(list_file, "%s/zephyr/%s", SYSCONFDIR, REALM_LIST_FILE);
+
+ if ((rlm_file = fopen(list_file, "r")) == (FILE *) 0) {
+ return(expand);
+ }
+
+ if (fgets(linebuf, BUFSIZ, rlm_file) == NULL) {
+ /* error reading */
+ (void) fclose(rlm_file);
+ return(expand);
+ }
+
+ while (1) {
+ /* run through the file, looking for admin host */
+ if (fgets(linebuf, BUFSIZ, rlm_file) == NULL) {
+ (void) fclose(rlm_file);
+ return(expand);
+ }
+
+ if (sscanf(linebuf, "%s %s", krb_realm, scratch) < 2)
+ continue;
+ if (!strncmp(krb_realm, expand, strlen(expand))) {
+ (void) fclose(rlm_file);
+ return(krb_realm);
+ }
+ }
+#ifdef KERBEROS
+ if (!strncmp(my_realm, expand, strlen(expand)))
+ return(my_realm);
+#endif
+ return(expand);
+}
+
+Realmname *
+get_realm_lists(file)
+ char *file;
+{
+ Realmname *rlm_list, *rlm;
+ int ii, nused, ntotal;
+ FILE *fp;
+ char buf[REALM_SZ + MAXHOSTNAMELEN + 1]; /* one for newline */
+ char realm[REALM_SZ], server[MAXHOSTNAMELEN + 1];
+
+ nused = 0;
+ if (!(fp = fopen(file, "r")))
+ return((Realmname *)0);
+
+ /* start with 16, realloc if necessary */
+ ntotal = 16;
+ rlm_list = (Realmname *)malloc(ntotal * sizeof(Realmname));
+ if (!rlm_list) {
+ syslog(LOG_CRIT, "get_realm_lists malloc");
+ abort();
+ }
+
+ while (fgets(buf, REALM_SZ + MAXHOSTNAMELEN + 1, fp)) {
+ if (sscanf(buf, "%s %s", realm, server) != 2) {
+ syslog(LOG_CRIT, "bad format in %s", file);
+ abort();
+ }
+ for (ii = 0; ii < nused; ii++) {
+ /* look for this realm */
+ if (!strcmp(rlm_list[ii].name, realm))
+ break;
+ }
+ if (ii < nused) {
+ rlm = &rlm_list[ii];
+ if (rlm->nused +1 >= rlm->nservers) {
+ /* make more space */
+ rlm->servers = (char **)realloc((char *)rlm->servers,
+ (unsigned)rlm->nservers * 2 *
+ sizeof(char *));
+ if (!rlm->servers) {
+ syslog(LOG_CRIT, "get_realm_lists realloc");
+ abort();
+ }
+ rlm->nservers *= 2;
+ }
+ rlm->servers[rlm->nused++] = strsave(server);
+ } else {
+ /* new realm */
+ if (nused + 1 >= ntotal) {
+ /* make more space */
+ rlm_list = (Realmname *)realloc((char *)rlm_list,
+ (unsigned)ntotal * 2 *
+ sizeof(Realmname));
+ if (!rlm_list) {
+ syslog(LOG_CRIT, "get_realm_lists realloc");
+ abort();
+ }
+ ntotal *= 2;
+ }
+ rlm = &rlm_list[nused++];
+ strcpy(rlm->name, realm);
+ rlm->nused = 0;
+ rlm->nservers = 16;
+ rlm->servers = (char **)malloc(rlm->nservers * sizeof(char *));
+ if (!rlm->servers) {
+ syslog(LOG_CRIT, "get_realm_lists malloc");
+ abort();
+ }
+ rlm->servers[rlm->nused++] = strsave(server);
+ }
+ }
+ if (nused + 1 >= ntotal) {
+ rlm_list = (Realmname *)realloc((char *)rlm_list,
+ (unsigned)(ntotal + 1) *
+ sizeof(Realmname));
+ if (!rlm_list) {
+ syslog(LOG_CRIT, "get_realm_lists realloc");
+ abort();
+ }
+ }
+ *rlm_list[nused].name = '\0';
+
+ return(rlm_list);
+}
+
+Code_t
+realm_send_realms()
+{
+ int cnt, retval;
+ for (cnt = 0; cnt < nrealms; cnt++) {
+ if (retval = (subscr_send_realm_subs(&otherrealms[cnt])) != ZERR_NONE)
+ return(retval);
+ }
+ return ZERR_NONE;
+}
+
+int
+bound_for_local_realm(notice)
+ ZNotice_t *notice;
+{
+ char *realm;
+
+ realm = strchr(notice->z_recipient, '@');
+
+ if (!realm || !strcmp(realm_expand_realm(realm + 1), ZGetRealm()))
+ return 1;
+
+ return 0;
+}
+
+int
+sender_in_realm(notice)
+ ZNotice_t *notice;
+{
+ char *realm;
+
+ realm = strchr(notice->z_sender, '@');
+
+ if (!realm || !strcmp(realm + 1, ZGetRealm()))
+ return 1;
+
+ return 0;
+}
+
+Realm *
+realm_which_realm(who)
+ struct sockaddr_in *who;
+{
+ Realm *realm;
+ struct sockaddr_in *addr;
+ int a, b;
+
+ /* loop through the realms */
+ for (realm = otherrealms, a = 0; a < nrealms; a++, realm++)
+ /* loop through the addresses for the realm */
+ for (addr = realm->addrs, b = 0; b < realm->count; b++, addr++)
+ if (addr->sin_addr.s_addr == who->sin_addr.s_addr)
+ return(realm);
+
+ return 0;
+}
+
+Realm *
+realm_get_realm_by_name(name)
+char *name;
+{
+ int a;
+ Realm *realm;
+
+ for (realm = otherrealms, a = 0; a < nrealms; a++, realm++)
+ if (!strcmp(realm->name, name))
+ return(realm);
+
+ return 0;
+}
+
+static void
+rlm_nack_cancel(notice, who)
+ register ZNotice_t *notice;
+ struct sockaddr_in *who;
+{
+ register Realm *which = realm_which_realm(who);
+ register Unacked *nacked, *next;
+ ZNotice_t acknotice;
+ ZPacket_t retval;
+
+#if 0
+ zdbug((LOG_DEBUG, "rlm_nack_cancel: %s:%08X,%08X",
+ inet_ntoa(notice->z_uid.zuid_addr),
+ notice->z_uid.tv.tv_sec, notice->z_uid.tv.tv_usec));
+#endif
+ if (!which) {
+ syslog(LOG_ERR, "non-realm ack?");
+ return;
+ }
+
+ for (nacked = rlm_nacklist; nacked; nacked = nacked->next) {
+ if (&otherrealms[nacked->dest.rlm.rlm_idx] == which) {
+ if (ZCompareUID(&nacked->uid, ¬ice->z_uid)) {
+ timer_reset(nacked->timer);
+
+ if (nacked->ack_addr.sin_addr.s_addr)
+ rlm_ack(notice, nacked);
+
+ /* free the data */
+ free(nacked->packet);
+ LIST_DELETE(nacked);
+ free(nacked);
+ return;
+ }
+ }
+ }
+#if 0
+ zdbug((LOG_DEBUG,"nack_cancel: nack not found %s:%08X,%08X",
+ inet_ntoa (notice->z_uid.zuid_addr),
+ notice->z_uid.tv.tv_sec, notice->z_uid.tv.tv_usec));
+#endif
+ return;
+}
+
+static void
+rlm_ack(notice, nacked)
+ ZNotice_t *notice;
+ Unacked *nacked;
+{
+ ZNotice_t acknotice;
+ ZPacket_t ackpack;
+ int packlen;
+ Code_t retval;
+
+ /* tell the original sender the result */
+ acknotice = *notice;
+ acknotice.z_message_len = strlen(acknotice.z_message) + 1;
+
+ packlen = sizeof(ackpack);
+
+ if ((retval = ZFormatSmallRawNotice(&acknotice, ackpack, &packlen)) != ZERR_NONE) {
+ syslog(LOG_ERR, "rlm_ack format: %s",
+ error_message(retval));
+ return;
+ }
+ zdbug((LOG_DEBUG, "rlm_ack sending to %s/%d",
+ inet_ntoa(nacked->ack_addr.sin_addr),
+ ntohs(nacked->ack_addr.sin_port)));
+ if ((retval = ZSetDestAddr(&nacked->ack_addr)) != ZERR_NONE) {
+ syslog(LOG_WARNING, "rlm_ack set addr: %s",
+ error_message(retval));
+ return;
+ }
+ if ((retval = ZSendPacket(ackpack, packlen, 0)) != ZERR_NONE) {
+ syslog(LOG_WARNING, "rlm_ack xmit: %s", error_message(retval));
+ return;
+ }
+}
+
+
+Code_t
+realm_dispatch(notice, auth, who, server)
+ ZNotice_t *notice;
+ int auth;
+ struct sockaddr_in *who;
+ Server *server;
+{
+ Realm *realm;
+ struct sockaddr_in newwho;
+ Code_t status = ZERR_NONE;
+ char rlm_recipient[REALM_SZ + 1];
+ int external = 0;
+ String *notice_class;
+
+ if (notice->z_kind == SERVACK || notice->z_kind == SERVNAK) {
+ rlm_nack_cancel(notice, who);
+ return(ZERR_NONE);
+ }
+ /* set up a who for the real origin */
+ memset((caddr_t) &newwho, 0, sizeof(newwho));
+ newwho.sin_family = AF_INET;
+ newwho.sin_addr.s_addr = notice->z_sender_addr.s_addr;
+ newwho.sin_port = hm_port;
+
+ /* check if it's a control message */
+ realm = realm_which_realm(who);
+
+ notice_class = make_string(notice->z_class,1);
+
+ if (class_is_admin(notice_class)) {
+ syslog(LOG_WARNING, "%s sending admin opcode %s",
+ realm->name, notice->z_opcode);
+ } else if (class_is_hm(notice_class)) {
+ syslog(LOG_WARNING, "%s sending hm opcode %s",
+ realm->name, notice->z_opcode);
+ } else if (class_is_control(notice_class)) {
+ status = realm_control_dispatch(notice, auth, who,
+ server, realm);
+ } else if (class_is_ulogin(notice_class)) {
+ /* don't need to forward this */
+ if (server == me_server) {
+ sprintf(rlm_recipient, "@%s", realm->name);
+ notice->z_recipient = rlm_recipient;
+
+ sendit(notice, 1, who, 0);
+ }
+ } else if (class_is_ulocate(notice_class)) {
+ status = realm_ulocate_dispatch(notice, auth, who, server, realm);
+ } else {
+ /* redo the recipient */
+ if (*notice->z_recipient == '\0') {
+ sprintf(rlm_recipient, "@%s", realm->name);
+ notice->z_recipient = rlm_recipient;
+ /* only send to our realm */
+ external = 0;
+ } else if (bound_for_local_realm(notice) && *notice->z_recipient
+ == '@')
+ {
+ /* we're responsible for getting this message out */
+ external = 1;
+ notice->z_recipient = "";
+ }
+
+ /* otherwise, send to local subscribers */
+ sendit(notice, auth, who, external);
+ }
+
+ return(status);
+}
+
+void
+realm_init()
+{
+ Client *client;
+ Realmname *rlmnames;
+ Realm *rlm;
+ int ii, jj, found;
+ struct in_addr *addresses;
+ struct hostent *hp;
+ char list_file[128];
+ char rlmprinc[ANAME_SZ+INST_SZ+REALM_SZ+3];
+
+ sprintf(list_file, "%s/zephyr/%s", SYSCONFDIR, REALM_LIST_FILE);
+ rlmnames = get_realm_lists(list_file);
+ if (!rlmnames) {
+ zdbug((LOG_DEBUG, "No other realms"));
+ nrealms = 0;
+ return;
+ }
+
+ for (nrealms = 0; *rlmnames[nrealms].name; nrealms++);
+
+ otherrealms = (Realm *)malloc(nrealms * sizeof(Realm));
+ if (!otherrealms) {
+ syslog(LOG_CRIT, "malloc failed in get_realm_addrs");
+ abort();
+ }
+
+ for (ii = 0; ii < nrealms; ii++) {
+ rlm = &otherrealms[ii];
+ strcpy(rlm->name, rlmnames[ii].name);
+
+ addresses = (struct in_addr *)malloc(rlmnames[ii].nused * sizeof(struct in_addr));
+ if (!addresses) {
+ syslog(LOG_CRIT, "malloc failed in get_realm_addrs");
+ abort();
+ }
+ /* convert names to addresses */
+ found = 0;
+ for (jj = 0; jj < rlmnames[ii].nused; jj++) {
+ hp = gethostbyname(rlmnames[ii].servers[jj]);
+ if (hp) {
+ memmove((caddr_t) &addresses[found], (caddr_t)hp->h_addr, sizeof(struct in_addr));
+ found++;
+ } else
+ syslog(LOG_WARNING, "hostname failed, %s", rlmnames[ii].servers[jj]);
+ /* free the hostname */
+ free(rlmnames[ii].servers[jj]);
+ }
+ rlm->count = found;
+ rlm->addrs = (struct sockaddr_in *)malloc(found * sizeof (struct sockaddr_in));
+ if (!rlm->addrs) {
+ syslog(LOG_CRIT, "malloc failed in get_realm_addrs");
+ abort();
+ }
+ for (jj = 0; jj < rlm->count; jj++) {
+ rlm->addrs[jj].sin_family = AF_INET;
+ /* use the server port */
+ rlm->addrs[jj].sin_port = srv_addr.sin_port;
+ rlm->addrs[jj].sin_addr = addresses[jj];
+ }
+ client = (Client *) malloc(sizeof(Client));
+ if (!client) {
+ syslog(LOG_CRIT, "malloc failed in get_realm_addrs");
+ abort();
+ }
+ memset(&client->addr, 0, sizeof(struct sockaddr_in));
+#ifdef HAVE_KRB4
+ memset(&client->session_key, 0, sizeof(client->session_key));
+#endif
+ sprintf(rlmprinc, "%s.%s@%s", SERVER_SERVICE, SERVER_INSTANCE, rlm->name);
+ client->principal = make_string(rlmprinc, 0);
+ client->last_send = 0;
+ client->last_ack = NOW;
+ client->subs = NULL;
+ client->realm = rlm;
+ client->addr.sin_family = 0;
+ client->addr.sin_port = 0;
+ client->addr.sin_addr.s_addr = 0;
+
+ rlm->client = client;
+ rlm->idx = random() % rlm->count;
+ rlm->subs = NULL;
+ rlm->tkt_try = 0;
+ free(rlmnames[ii].servers);
+ free(addresses);
+ }
+ free(rlmnames);
+}
+
+static Code_t
+realm_ulocate_dispatch(notice, auth, who, server, realm)
+ ZNotice_t *notice;
+ int auth;
+ struct sockaddr_in *who;
+ Server *server;
+ Realm *realm;
+{
+ register char *opcode = notice->z_opcode;
+ Code_t status;
+
+ if (!auth) {
+ syslog(LOG_WARNING, "unauth locate msg from %s",
+ inet_ntoa(who->sin_addr));
+ clt_ack(notice, who, AUTH_FAILED);
+ return(ZERR_NONE);
+ }
+
+ if (!strcmp(opcode, REALM_REQ_LOCATE)) {
+ ack(notice, who);
+ ulogin_realm_locate(notice, who, realm);
+ } else if (!strcmp(opcode, REALM_ANS_LOCATE)) {
+ ack(notice, who);
+ ulogin_relay_locate(notice, who);
+ } else {
+ syslog(LOG_WARNING, "%s unknown/illegal loc opcode %s",
+ realm->name, opcode);
+ nack(notice, who);
+ }
+
+ return(ZERR_NONE);
+}
+
+
+Code_t
+realm_control_dispatch(notice, auth, who, server, realm)
+ ZNotice_t *notice;
+ int auth;
+ struct sockaddr_in *who;
+ Server *server;
+ Realm *realm;
+{
+ register char *opcode = notice->z_opcode;
+ Code_t status;
+
+ if (!auth) {
+ syslog(LOG_WARNING, "unauth ctl msg from %s",
+ inet_ntoa(who->sin_addr));
+ if (server == me_server)
+ clt_ack(notice, who, AUTH_FAILED);
+ return(ZERR_NONE);
+ }
+
+ if (strcmp(notice->z_class_inst, ZEPHYR_CTL_REALM)) {
+ syslog(LOG_WARNING, "Invalid rlm_dispatch instance %s",
+ notice->z_class_inst);
+ return(ZERR_NONE);
+ }
+
+ if (!strcmp(opcode, REALM_REQ_SUBSCRIBE) || !strcmp(opcode, REALM_ADD_SUBSCRIBE)) {
+ /* try to add subscriptions */
+ /* attempts to get defaults are ignored */
+ if ((status = subscr_foreign_user(notice, who, realm)) != ZERR_NONE) {
+ clt_ack(notice, who, AUTH_FAILED);
+ } else if (server == me_server) {
+ server_forward(notice, auth, who);
+ ack(notice, who);
+ }
+ } else if (!strcmp(opcode, REALM_UNSUBSCRIBE)) {
+ /* try to remove subscriptions */
+ if ((status = subscr_realm_cancel(who, notice, realm)) != ZERR_NONE) {
+ clt_ack(notice, who, NOT_FOUND);
+ } else if (server == me_server) {
+ server_forward(notice, auth, who);
+ ack(notice, who);
+ }
+ } else {
+ syslog(LOG_WARNING, "%s unknown/illegal ctl opcode %s",
+ realm->name, opcode);
+ if (server == me_server)
+ nack(notice, who);
+ return(ZERR_NONE);
+ }
+ return(ZERR_NONE);
+}
+
+void
+realm_handoff(notice, auth, who, realm, ack_to_sender)
+ ZNotice_t *notice;
+ int auth;
+ struct sockaddr_in *who;
+ Realm *realm;
+ int ack_to_sender;
+{
+#ifdef HAVE_KRB4
+ Code_t retval;
+
+ if (!auth) {
+ zdbug((LOG_DEBUG, "realm_sendit unauthentic to realm %s", realm->name))
+ realm_sendit(notice, who, auth, realm, ack_to_sender);
+ }
+
+ if (!ticket_lookup(realm->name))
+ if ((retval = ticket_retrieve(realm)) != ZERR_NONE) {
+ syslog(LOG_WARNING, "rlm_handoff failed: %s", error_message(retval));
+ return;
+ }
+
+ zdbug((LOG_DEBUG, "realm_sendit to realm %s auth %d", realm->name, auth));
+ /* valid ticket available now, send the message */
+ realm_sendit_auth(notice, who, auth, realm, ack_to_sender);
+#else /* HAVE_KRB4 */
+ realm_sendit(notice, who, auth, realm, ack_to_sender);
+#endif /* HAVE_KRB4 */
+}
+
+static void
+realm_sendit(notice, who, auth, realm, ack_to_sender)
+ ZNotice_t *notice;
+ struct sockaddr_in *who;
+ int auth;
+ Realm *realm;
+ int ack_to_sender;
+{
+ caddr_t pack;
+ int packlen;
+ Code_t retval;
+ Unacked *nacked;
+
+ notice->z_auth = auth;
+
+ /* format the notice */
+ if ((retval = ZFormatRawNotice(notice, &pack, &packlen)) != ZERR_NONE) {
+ syslog(LOG_WARNING, "rlm_sendit format: %s",
+ error_message(retval));
+ return;
+ }
+
+ /* now send */
+ if ((retval = ZSetDestAddr(&realm->addrs[realm->idx])) != ZERR_NONE) {
+ syslog(LOG_WARNING, "rlm_sendit set addr: %s",
+ error_message(retval));
+ free(pack);
+ return;
+ }
+ if ((retval = ZSendPacket(pack, packlen, 0)) != ZERR_NONE) {
+ syslog(LOG_WARNING, "rlm_sendit xmit: %s", error_message(retval));
+ free(pack);
+ return;
+ }
+
+ /* now we've sent it, mark it as not ack'ed */
+
+ if (!(nacked = (Unacked *)malloc(sizeof(Unacked)))) {
+ /* no space: just punt */
+ syslog(LOG_ERR, "rlm_sendit nack malloc");
+ free(pack);
+ return;
+ }
+
+ nacked->client = NULL;
+ nacked->rexmits = 0;
+ nacked->packet = pack;
+ nacked->dest.rlm.rlm_idx = realm - otherrealms;
+ nacked->dest.rlm.rlm_srv_idx = realm->idx;
+ nacked->packsz = packlen;
+ nacked->uid = notice->z_uid;
+ if (ack_to_sender)
+ nacked->ack_addr = *who;
+ else
+ nacked->ack_addr.sin_addr.s_addr = 0;
+
+ /* set a timer to retransmit */
+ nacked->timer = timer_set_rel(rexmit_times[0], rlm_rexmit, nacked);
+ /* chain in */
+ LIST_INSERT(&rlm_nacklist, nacked);
+ return;
+}
+
+static void
+packet_ctl_nack(nackpacket)
+ Unacked *nackpacket;
+{
+ ZNotice_t notice;
+
+ /* extract the notice */
+ ZParseNotice(nackpacket->packet, nackpacket->packsz, ¬ice);
+ nack(¬ice, &nackpacket->ack_addr);
+}
+
+static void
+rlm_rexmit(arg)
+ void *arg;
+{
+ Unacked *nackpacket = (Unacked *) arg;
+ Code_t retval;
+ register Realm *realm;
+ int new_srv_idx;
+
+ zdbug((LOG_DEBUG,"rlm_rexmit"));
+
+ realm = &otherrealms[nackpacket->dest.rlm.rlm_idx];
+
+ zdbug((LOG_DEBUG, "rlm_rexmit: sending to %s", realm->name));
+
+ if (rexmit_times[(nackpacket->rexmits + 1)/(realm->count)] == -1) {
+ /* give a server ack that the packet is lost/realm dead */
+ packet_ctl_nack(nackpacket);
+ LIST_DELETE(nackpacket);
+ free(nackpacket->packet);
+ free(nackpacket);
+
+ zdbug((LOG_DEBUG, "rlm_rexmit: %s appears dead", realm->name));
+ return;
+ }
+
+ /* retransmit the packet, trying each server in the realm multiple times */
+#if 0
+ new_srv_idx = ((nackpacket->rexmits / NUM_REXMIT_TIMES)
+ + nackpacket->rlm.rlm_srv_idx) % realm->count;
+#else
+ new_srv_idx = nackpacket->rexmits % realm->count;
+#endif
+ if (new_srv_idx != realm->idx)
+ realm->idx = new_srv_idx;
+
+ retval = ZSetDestAddr(&realm->addrs[realm->idx]);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_WARNING, "rlm_rexmit set addr: %s", error_message(retval));
+ } else {
+ retval = ZSendPacket(nackpacket->packet, nackpacket->packsz, 0);
+ if (retval != ZERR_NONE)
+ syslog(LOG_WARNING, "rlm_rexmit xmit: %s", error_message(retval));
+ }
+ /* reset the timer */
+ if (rexmit_times[(nackpacket->rexmits + 1)/(realm->count)] != -1)
+ nackpacket->rexmits++;
+
+ nackpacket->timer = timer_set_rel(rexmit_times[(nackpacket->rexmits)/(realm->count)], rlm_rexmit, nackpacket);
+ return;
+}
+
+void
+realm_dump_realms(fp)
+ FILE *fp;
+{
+ register int ii, jj;
+
+ for (ii = 0; ii < nrealms; ii++) {
+ (void) fprintf(fp, "%d:%s\n", ii, otherrealms[ii].name);
+ for (jj = 0; jj < otherrealms[ii].count; jj++) {
+ (void) fprintf(fp, "\t%s\n",
+ inet_ntoa(otherrealms[ii].addrs[jj].sin_addr));
+ }
+ /* dump the subs */
+ subscr_dump_subs(fp, otherrealms[ii].subs);
+ }
+}
+
+
+#ifdef HAVE_KRB4
+static void
+realm_sendit_auth(notice, who, auth, realm, ack_to_sender)
+ ZNotice_t *notice;
+ int auth;
+ struct sockaddr_in *who;
+ Realm *realm;
+ int ack_to_sender;
+{
+ char *buffer, *ptr;
+ caddr_t pack;
+ int buffer_len, hdrlen, offset, fragsize, ret_len, message_len;
+ int origoffset, origlen;
+ Code_t retval;
+ Unacked *nacked;
+ char buf[1024], multi[64];
+ CREDENTIALS cred;
+ KTEXT_ST authent;
+ ZNotice_t partnotice, newnotice;
+
+ offset = 0;
+
+ /* first, build an authent */
+ retval = krb_get_cred(SERVER_SERVICE, SERVER_INSTANCE, realm->name, &cred);
+ if (retval != GC_OK) {
+ syslog(LOG_WARNING, "rlm_sendit_auth get_cred: %s",
+ error_message(retval+krb_err_base));
+ return;
+ }
+
+ retval = krb_mk_req(&authent, SERVER_SERVICE, SERVER_INSTANCE,
+ realm->name, 1);
+ if (retval != MK_AP_OK) {
+ syslog(LOG_WARNING, "rlm_sendit_auth mk_req: %s",
+ error_message(retval+krb_err_base));
+ return;
+ }
+
+ retval = ZMakeAscii(buf, sizeof(buf), authent.dat, authent.length);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_WARNING, "rlm_sendit_auth mk_ascii: %s",
+ error_message(retval));
+ return;
+ }
+
+ /* set the dest addr */
+ retval = ZSetDestAddr(&realm->addrs[realm->idx]);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_WARNING, "rlm_sendit_auth set addr: %s", error_message(retval));
+ return;
+ }
+
+ /* now format the notice, refragmenting if needed */
+ newnotice = *notice;
+ newnotice.z_auth = 1;
+ newnotice.z_ascii_authent = buf;
+ newnotice.z_authent_len = authent.length;
+
+ buffer = (char *) malloc(sizeof(ZPacket_t));
+ if (!buffer) {
+ syslog(LOG_ERR, "realm_sendit_auth malloc");
+ return; /* DON'T put on nack list */
+ }
+
+ buffer_len = sizeof(ZPacket_t);
+
+ retval = Z_FormatRawHeader(&newnotice, buffer, buffer_len, &hdrlen, &ptr,
+ NULL);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_WARNING, "rlm_sendit_auth raw: %s", error_message(retval));
+ free(buffer);
+ return;
+ }
+
+#ifdef NOENCRYPTION
+ newnotice.z_checksum = 0;
+#else
+ newnotice.z_checksum =
+ (ZChecksum_t)des_quad_cksum(buffer, NULL, ptr - buffer, 0, cred.session);
+#endif
+
+ retval = Z_FormatRawHeader(&newnotice, buffer, buffer_len, &hdrlen,
+ NULL, NULL);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_WARNING, "rlm_sendit_auth raw: %s", error_message(retval));
+ free(buffer);
+ return;
+ }
+
+ /* This is not terribly pretty, but it does do its job.
+ * If a packet we get that needs to get sent off to another realm is
+ * too big after we slap on our authent, we refragment it further,
+ * a la Z_SendFragmentedNotice. This obliviates the need for what
+ * used to be done in ZFormatAuthenticRealmNotice, as we do it here.
+ * At some point it should be pulled back out into its own function,
+ * but only the server uses it.
+ */
+
+ if ((newnotice.z_message_len+hdrlen > buffer_len) ||
+ (newnotice.z_message_len+hdrlen > Z_MAXPKTLEN)){
+ /* Deallocate buffer, use a local one */
+ free(buffer);
+
+ partnotice = *notice;
+
+ partnotice.z_auth = 1;
+ partnotice.z_ascii_authent = buf;
+ partnotice.z_authent_len = authent.length;
+
+ origoffset = 0;
+ origlen = notice->z_message_len;
+
+ if (notice->z_multinotice && strcmp(notice->z_multinotice, ""))
+ if (sscanf(notice->z_multinotice, "%d/%d", &origoffset, &origlen) != 2) {
+ syslog(LOG_WARNING, "rlm_sendit_auth frag: parse failed");
+ return;
+ }
+
+#if 0
+ zdbug((LOG_DEBUG,"rlm_send_auth: orig: %d-%d/%d", origoffset, notice->z_message_len, origlen));
+#endif
+
+ fragsize = Z_MAXPKTLEN-hdrlen-Z_FRAGFUDGE;
+
+ while (offset < notice->z_message_len || !notice->z_message_len) {
+ (void) sprintf(multi, "%d/%d", offset+origoffset, origlen);
+ partnotice.z_multinotice = multi;
+ if (offset > 0) {
+ (void) gettimeofday(&partnotice.z_uid.tv, (struct timezone *)0);
+ partnotice.z_uid.tv.tv_sec = htonl((u_long)
+ partnotice.z_uid.tv.tv_sec);
+ partnotice.z_uid.tv.tv_usec = htonl((u_long)
+ partnotice.z_uid.tv.tv_usec);
+ (void) memcpy((char *)&partnotice.z_uid.zuid_addr, &__My_addr,
+ sizeof(__My_addr));
+ }
+ message_len = min(notice->z_message_len-offset, fragsize);
+ partnotice.z_message = notice->z_message+offset;
+ partnotice.z_message_len = message_len;
+
+#if 0
+ zdbug((LOG_DEBUG,"rlm_send_auth: new: %d-%d/%d", origoffset+offset, message_len, origlen));
+#endif
+
+ buffer = (char *) malloc(sizeof(ZPacket_t));
+ if (!buffer) {
+ syslog(LOG_ERR, "realm_sendit_auth malloc");
+ return; /* DON'T put on nack list */
+ }
+
+ retval = Z_FormatRawHeader(&partnotice, buffer, buffer_len, &hdrlen,
+ &ptr, NULL);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_WARNING, "rlm_sendit_auth raw: %s", error_message(retval));
+ free(buffer);
+ return;
+ }
+
+#ifdef NOENCRYPTION
+ partnotice.z_checksum = 0;
+#else
+ partnotice.z_checksum =
+ (ZChecksum_t)des_quad_cksum(buffer, NULL, ptr - buffer, 0,
+ cred.session);
+#endif
+
+ retval = Z_FormatRawHeader(&partnotice, buffer, buffer_len, &hdrlen,
+ NULL, NULL);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_WARNING, "rlm_sendit_auth raw: %s", error_message(retval));
+ free(buffer);
+ return;
+ }
+
+ ptr = buffer+hdrlen;
+
+ (void) memcpy(ptr, partnotice.z_message, partnotice.z_message_len);
+
+ buffer_len = hdrlen+partnotice.z_message_len;
+
+ /* now send */
+ if ((retval = ZSendPacket(buffer, buffer_len, 0)) != ZERR_NONE) {
+ syslog(LOG_WARNING, "rlm_sendit_auth xmit: %s", error_message(retval));
+ free(buffer);
+ return;
+ }
+
+ offset += fragsize;
+
+ if (!(nacked = (Unacked *)malloc(sizeof(Unacked)))) {
+ /* no space: just punt */
+ syslog(LOG_ERR, "rlm_sendit_auth nack malloc");
+ free(buffer);
+ return;
+ }
+
+ nacked->rexmits = 0;
+ nacked->packet = buffer;
+ nacked->dest.rlm.rlm_idx = realm - otherrealms;
+ nacked->dest.rlm.rlm_srv_idx = realm->idx;
+ nacked->packsz = buffer_len;
+ nacked->uid = partnotice.z_uid;
+
+ /* Do the ack for the last frag, below */
+ if (ack_to_sender)
+ nacked->ack_addr = *who;
+ else
+ nacked->ack_addr.sin_addr.s_addr = 0;
+
+ /* set a timer to retransmit */
+ nacked->timer = timer_set_rel(rexmit_times[0], rlm_rexmit, nacked);
+
+ /* chain in */
+ LIST_INSERT(&rlm_nacklist, nacked);
+
+ if (!notice->z_message_len)
+ break;
+ }
+#if 0
+ zdbug((LOG_DEBUG, "rlm_sendit_auth frag message sent"));
+#endif
+ } else {
+ /* This is easy, no further fragmentation needed */
+ ptr = buffer+hdrlen;
+
+ (void) memcpy(ptr, newnotice.z_message, newnotice.z_message_len);
+
+ buffer_len = hdrlen+newnotice.z_message_len;
+
+ /* now send */
+ if ((retval = ZSendPacket(buffer, buffer_len, 0)) != ZERR_NONE) {
+ syslog(LOG_WARNING, "rlm_sendit_auth xmit: %s", error_message(retval));
+ free(buffer);
+ return;
+ }
+
+#if 0
+ zdbug((LOG_DEBUG, "rlm_sendit_auth message sent"));
+#endif
+ /* now we've sent it, mark it as not ack'ed */
+
+ if (!(nacked = (Unacked *)malloc(sizeof(Unacked)))) {
+ /* no space: just punt */
+ syslog(LOG_ERR, "rlm_sendit_auth nack malloc");
+ free(buffer);
+ return;
+ }
+
+ nacked->rexmits = 0;
+ nacked->packet = buffer;
+ nacked->dest.rlm.rlm_idx = realm - otherrealms;
+ nacked->dest.rlm.rlm_srv_idx = realm->idx;
+ nacked->packsz = buffer_len;
+ nacked->uid = notice->z_uid;
+
+ /* Do the ack for the last frag, below */
+ if (ack_to_sender)
+ nacked->ack_addr = *who;
+ else
+ nacked->ack_addr.sin_addr.s_addr = 0;
+
+ /* set a timer to retransmit */
+ nacked->timer = timer_set_rel(rexmit_times[0], rlm_rexmit, nacked);
+ /* chain in */
+ LIST_INSERT(&rlm_nacklist, nacked);
+ }
+#if 0
+ if (ack_to_sender)
+ nacked->ack_addr = *who;
+#endif
+ return;
+}
+
+int
+ticket_expired(cred)
+CREDENTIALS *cred;
+{
+ /* extra 15 minutes for safety margin */
+#ifdef AFS_LIFETIMES
+ return (krb_life_to_time(cred->issue_date, cred->lifetime) < NOW + 15*60);
+#else /* AFS_LIFETIMES */
+ return (cred->issue_date + cred->lifetime*5*60 < NOW + 15*60);
+#endif /* AFS_LIFETIMES */
+}
+
+int
+ticket_lookup(realm)
+char *realm;
+{
+ CREDENTIALS cred;
+ KTEXT_ST authent;
+ int retval;
+
+ retval = krb_get_cred(SERVER_SERVICE, SERVER_INSTANCE, realm, &cred);
+ if (retval == GC_OK && !ticket_expired(&cred))
+ /* good ticket */
+ return(1);
+
+ if (!strcmp(realm, ZGetRealm())) {
+ get_tgt();
+
+ /* For Putrify */
+ memset(&authent.dat,0,MAX_KTXT_LEN);
+ authent.mbz=0;
+
+ /* this is local, so try to contact the Kerberos server */
+ retval = krb_mk_req(&authent, SERVER_SERVICE, SERVER_INSTANCE,
+ realm, 0);
+ if (retval != KSUCCESS) {
+ syslog(LOG_ERR, "tkt_lookup: local: %s",
+ krb_err_txt[retval]);
+ return(0);
+ } else {
+ return(1);
+ }
+ }
+
+ return (0);
+}
+
+static Code_t
+ticket_retrieve(realm)
+ Realm *realm;
+{
+ int pid, retval;
+ KTEXT_ST authent;
+
+ get_tgt();
+
+ /* For Putrify */
+ memset(&authent.dat,0,MAX_KTXT_LEN);
+ authent.mbz=0;
+
+ /* Don't lose by trying too often. */
+ if (NOW - realm->tkt_try > 5 * 60) {
+ retval = krb_mk_req(&authent, SERVER_SERVICE, SERVER_INSTANCE,
+ realm->name, 0);
+ realm->tkt_try = NOW;
+ if (retval != KSUCCESS) {
+ syslog(LOG_WARNING, "tkt_rtrv: %s: %s", realm,
+ krb_err_txt[retval]);
+ return (retval+krb_err_base);
+ }
+ return (0);
+ } else {
+ return (1);
+ }
+}
+#endif /* HAVE_KRB4 */
+
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains functions for communication with other servers.
+ *
+ * Created by: John T. Kohl
+ *
+ * $Id: server.c,v 1.65 1999/01/22 23:19:47 ghudson Exp $
+ *
+ * Copyright (c) 1987, 1991 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <zephyr/mit-copyright.h>
+#include "zserver.h"
+#include <sys/socket.h>
+
+#ifndef lint
+#ifndef SABER
+static const char rcsid_server_c[] = "$Id: server.c,v 1.65 1999/01/22 23:19:47 ghudson Exp $";
+#endif
+#endif
+
+#define SRV_NACKTAB_HASHSIZE 1023
+#define SRV_NACKTAB_HASHVAL(which, uid) (((which) ^ (uid).zuid_addr.s_addr ^ \
+ (uid).tv.tv_sec ^ (uid).tv.tv_usec) \
+ % SRV_NACKTAB_HASHSIZE)
+/*
+ * Server manager. Deal with traffic to and from other servers.
+ *
+ * void server_init()
+ *
+ * void server_shutdown()
+ *
+ * void server_timo(which)
+ * Server *which;
+ *
+ * void server_dispatch(notice, auth, who)
+ * ZNotice_t *notice;
+ * int auth;
+ * struct sockaddr_in *who;
+ *
+ * void server_recover(client)
+ * Client *client;
+ *
+ * void server_adispatch(notice, auth, who, server)
+ * ZNotice_t *notice;
+ * int auth;
+ * struct sockaddr_in *who;
+ * Server *server;
+ *
+ * void server_forward(notice, auth, who)
+ * ZNotice_t *notice;
+ * int auth;
+ * struct sockaddr_in *who;
+ *
+ * Server *server_which_server(who)
+ * struct sockaddr_in *who;
+ *
+ * void server_kill_clt(client);
+ * Client *client;
+ *
+ * void server_dump_servers(fp);
+ * FILE *fp;
+ *
+ * void server_reset();
+ */
+
+static void server_flush __P((Server *));
+static void hello_respond __P((struct sockaddr_in *, int, int));
+static void srv_responded __P((struct sockaddr_in *));
+static void send_msg __P((struct sockaddr_in *, char *, int));
+static void send_msg_list __P((struct sockaddr_in *, char *, char **, int,
+ int));
+static void srv_nack_cancel __P((ZNotice_t *, struct sockaddr_in *));
+static void srv_nack_release __P((Server *));
+static void srv_nack_renumber __P((int *));
+static void send_stats __P((struct sockaddr_in *));
+static void server_queue __P((Server *, int, void *, int,
+ struct sockaddr_in *));
+static void server_hello __P((Server *, int));
+static void setup_server __P((Server *, struct in_addr *));
+static void srv_rexmit __P((void *));
+static void server_forw_reliable __P((Server *, caddr_t, int, ZNotice_t *));
+static Code_t admin_dispatch __P((ZNotice_t *, int, struct sockaddr_in *,
+ Server *));
+static Code_t kill_clt __P((ZNotice_t *, Server *));
+static Code_t extract_addr __P((ZNotice_t *, struct sockaddr_in *));
+
+#ifdef notdef
+static Code_t server_register();
+#endif
+
+static struct in_addr *get_server_addrs __P((int *number));
+#ifndef HAVE_HESIOD
+static char **get_server_list __P((char *file));
+static void free_server_list __P((char **list));
+#endif
+
+static Unacked *srv_nacktab[SRV_NACKTAB_HASHSIZE];
+Server *otherservers; /* points to an array of the known
+ servers */
+int nservers; /* number of other servers */
+int me_server_idx; /* # of my entry in the array */
+
+#define ADJUST (1) /* adjust timeout on hello input */
+#define DONT_ADJUST (0) /* don't adjust timeout */
+
+/* parameters controlling the transitions of the FSM's--patchable with adb */
+long timo_up = TIMO_UP;
+long timo_tardy = TIMO_TARDY;
+long timo_dead = TIMO_DEAD;
+
+/* counters to measure old protocol use */
+#ifdef OLD_COMPAT
+int old_compat_count_uloc = 0;
+int old_compat_count_ulocate = 0;
+int old_compat_count_subscr = 0;
+#endif /* OLD_COMPAT */
+#ifdef NEW_COMPAT
+int new_compat_count_uloc = 0;
+int new_compat_count_subscr = 0;
+#endif /* NEW_COMPAT */
+
+#ifdef DEBUG
+int zalone;
+#endif /* DEBUG */
+/*
+ * Initialize the array of servers. The `limbo' server goes in the first
+ * slot (otherservers[0]).
+ * Contact Hesiod to find all the other servers, allocate space for the
+ * structure, initialize them all to SERV_DEAD with expired timeouts.
+ * Set up a list header for server_forward retransmits.
+ */
+
+void
+server_init()
+{
+ int i;
+ struct in_addr *serv_addr, *server_addrs, limbo_addr;
+
+ /* we don't need to mask SIGFPE here since when we are called,
+ the signal handler isn't set up yet. */
+
+ /* talk to hesiod here, set nservers */
+ server_addrs = get_server_addrs(&nservers);
+ if (!server_addrs) {
+ syslog(LOG_ERR, "No servers?!?");
+ exit(1);
+ }
+
+#ifdef DEBUG
+ if (zalone)
+ nservers = 1;
+ else
+#endif /* DEBUG */
+ /* increment servers to make room for 'limbo' */
+ nservers++;
+
+ otherservers = (Server *) malloc(nservers * sizeof(Server));
+ me_server_idx = -1;
+
+ /* set up limbo */
+ limbo_addr.s_addr = 0;
+ setup_server(otherservers, &limbo_addr);
+ timer_reset(otherservers[0].timer);
+ otherservers[0].timer = NULL;
+ otherservers[0].queue = NULL;
+ otherservers[0].dumping = 0;
+
+ for (serv_addr = server_addrs, i = 1; i < nservers; serv_addr++, i++) {
+ setup_server(&otherservers[i], serv_addr);
+ /* is this me? */
+ if (serv_addr->s_addr == my_addr.s_addr) {
+ me_server_idx = i;
+ otherservers[i].state = SERV_UP;
+ timer_reset(otherservers[i].timer);
+ otherservers[i].timer = NULL;
+ otherservers[i].queue = NULL;
+ otherservers[i].dumping = 0;
+#if 0
+ zdbug((LOG_DEBUG,"found myself"));
+#endif
+ }
+ }
+
+ /* free up the addresses */
+ free(server_addrs);
+
+ if (me_server_idx == -1) {
+ syslog(LOG_WARNING, "I'm a renegade server!");
+ otherservers = (Server *) realloc(otherservers,
+ ++nservers * sizeof(Server));
+ if (!otherservers) {
+ syslog(LOG_CRIT, "renegade realloc");
+ abort();
+ }
+ setup_server(&otherservers[nservers - 1], &my_addr);
+ /* we are up. */
+ otherservers[nservers - 1].state = SERV_UP;
+
+ /* I don't send hello's to myself--cancel the timer */
+ timer_reset(otherservers[nservers - 1].timer);
+ otherservers[nservers - 1].timer = NULL;
+
+ /* cancel and reschedule all the timers--pointers need
+ adjusting */
+ /* don't reschedule limbo's timer, so start i=1 */
+ for (i = 1; i < nservers - 1; i++) {
+ timer_reset(otherservers[i].timer);
+ /* all the HELLO's are due now */
+ otherservers[i].timer = timer_set_rel(0L, server_timo,
+ &otherservers[i]);
+ }
+ me_server_idx = nservers - 1;
+ }
+
+}
+
+/*
+ * server_reset: re-initializes otherservers array by refreshing from Hesiod
+ * or disk file.
+ *
+ * If any server is no longer named in the new list, and that server is in
+ * state SERV_DEAD, it is dropped from the server list.
+ * All other currently-known servers are retained.
+ * Any additional servers not previously known are added to the table.
+ *
+ * WARNING: Don't call this routine if any of the ancestor procedures have a
+ * handle on a particular server other than by indexing on otherservers[].
+ */
+void
+server_reset()
+{
+ int num_servers;
+ struct in_addr *server_addrs;
+ struct in_addr *serv_addr;
+ Server *servers;
+ int i, j;
+ int *ok_list_new, *ok_list_old;
+ int num_ok, new_num;
+
+#if 0
+ zdbug((LOG_DEBUG, "server_reset"));
+#endif
+#ifdef DEBUG
+ if (zalone) {
+ syslog(LOG_INFO, "server_reset while alone, punt");
+ return;
+ }
+#endif /* DEBUG */
+
+ /* Find out what servers are supposed to be known. */
+ server_addrs = get_server_addrs(&num_servers);
+ if (!server_addrs) {
+ syslog(LOG_ERR, "server_reset no servers. nothing done.");
+ return;
+ }
+ ok_list_new = (int *) malloc(num_servers * sizeof(int));
+ if (!ok_list_new) {
+ syslog(LOG_ERR, "server_reset no mem new");
+ return;
+ }
+ ok_list_old = (int *) malloc(nservers * sizeof(int));
+ if (!ok_list_old) {
+ syslog(LOG_ERR, "server_reset no mem old");
+ free(ok_list_new);
+ return;
+ }
+
+ memset(ok_list_old, 0, nservers * sizeof(int));
+ memset(ok_list_new, 0, num_servers * sizeof(int));
+
+ /* reset timers--pointers will move */
+ for (j = 1; j < nservers; j++) { /* skip limbo */
+ if (j == me_server_idx)
+ continue;
+ timer_reset(otherservers[j].timer);
+ otherservers[j].timer = NULL;
+ }
+
+ /* check off entries on new list which are on old list.
+ check off entries on old list which are on new list. */
+
+ /* count limbo as "OK" */
+ num_ok = 1;
+ ok_list_old[0] = 1; /* limbo is OK */
+
+ for (serv_addr = server_addrs, i = 0; i < num_servers; serv_addr++, i++) {
+ for (j = 1; j < nservers; j++) { /* j = 1 since we skip limbo */
+ if (otherservers[j].addr.sin_addr.s_addr == serv_addr->s_addr) {
+ /* if server is on both lists, mark */
+ ok_list_new[i] = 1;
+ ok_list_old[j] = 1;
+ num_ok++;
+ break; /* for j loop */
+ }
+ }
+ }
+
+ /* remove any dead servers on old list not on new list. */
+ if (num_ok < nservers) {
+ int *srv;
+
+ new_num = 1; /* limbo */
+ /* count number of servers to keep */
+ for (j = 1; j < nservers; j++) {
+ /* since we are never SERV_DEAD, the following
+ test prevents removing ourself from the list */
+ if (ok_list_old[j] || (otherservers[j].state != SERV_DEAD)) {
+ syslog(LOG_INFO, "keeping server %s",
+ otherservers[j].addr_str);
+ new_num++;
+ }
+ }
+ if (new_num < nservers) {
+ servers = (Server *) malloc(new_num * sizeof(Server));
+ if (!servers) {
+ syslog(LOG_CRIT, "server_reset server malloc");
+ abort();
+ }
+ i = 1;
+ servers[0] = otherservers[0]; /* copy limbo */
+
+ srv = (int *) malloc(nservers * sizeof(int));
+ memset(srv, 0, nservers * sizeof(int));
+
+ /* copy the kept servers */
+ for (j = 1; j < nservers; j++) { /* skip limbo */
+ if (ok_list_old[j] ||
+ otherservers[j].state != SERV_DEAD) {
+ servers[i] = otherservers[j];
+ srv[j] = i;
+ i++;
+ } else {
+ syslog(LOG_INFO, "flushing server %s",
+ otherservers[j].addr_str);
+ server_flush(&otherservers[j]);
+ srv[j] = -1;
+ }
+
+ }
+ srv_nack_renumber(srv);
+
+ free(srv);
+ free(otherservers);
+ otherservers = servers;
+ nservers = new_num;
+ }
+ }
+
+ /* add any new servers on new list not on old list. */
+ new_num = 0;
+ for (i = 0; i < num_servers; i++) {
+ if (!ok_list_new[i])
+ new_num++;
+ }
+
+ /* new_num is number of extras. */
+ nservers += new_num;
+ otherservers = (Server *) realloc(otherservers, nservers * sizeof(Server));
+ if (!otherservers) {
+ syslog(LOG_CRIT, "server_reset realloc");
+ abort();
+ }
+
+ me_server_idx = 0;
+ for (j = 1; j < nservers - new_num; j++) {
+ if (otherservers[j].addr.sin_addr.s_addr == my_addr.s_addr) {
+ me_server_idx = j;
+ break;
+ }
+ }
+ if (!me_server_idx) {
+ syslog(LOG_CRIT, "can't find myself");
+ abort();
+ }
+
+ /* fill in otherservers with the new servers */
+ for (i = 0; i < num_servers; i++) {
+ if (!ok_list_new[i]) {
+ setup_server(&otherservers[nservers - (new_num--)],
+ &server_addrs[i]);
+ syslog(LOG_INFO, "adding server %s", inet_ntoa(server_addrs[i]));
+ }
+ }
+
+ free(server_addrs);
+ /* reset timers, to go off now.
+ We can't get a time-left indication (bleagh!)
+ so we expire them all now. This will generally
+ be non-destructive. We assume that when this code is
+ entered via a SIGHUP trigger that a system wizard
+ is watching the goings-on to make sure things straighten
+ themselves out.
+ */
+ for (i = 1; i < nservers; i++) { /* skip limbo */
+ if (i != me_server_idx && !otherservers[i].timer) {
+ otherservers[i].timer =
+ timer_set_rel(0L, server_timo, &otherservers[i]);
+#if 0
+ zdbug((LOG_DEBUG, "reset timer for %s",
+ otherservers[i].addr_str));
+#endif
+ }
+ }
+ free(ok_list_old);
+ free(ok_list_new);
+
+#if 0
+ zdbug((LOG_DEBUG, "server_reset: %d servers now", nservers));
+#endif
+}
+
+/* note: these must match the order given in zserver.h */
+static char *
+srv_states[] = {
+ "SERV_UP",
+ "SERV_TARDY",
+ "SERV_DEAD",
+ "SERV_STARTING"
+};
+
+/*
+ * A server timout has expired. If enough hello's have been unanswered,
+ * change state and act accordingly. Send a "hello" and reset the timer,
+ * incrementing the number of hello's sent.
+ *
+ * See the FSM in the Zephyr document for a better picture of what's
+ * happening here.
+ */
+
+void
+server_timo(arg)
+ void *arg;
+{
+ Server *which = (Server *) arg;
+ int auth = 0;
+
+#if 0
+ zdbug((LOG_DEBUG,"srv_timo: %s", which->addr_str));
+#endif
+ /* change state and reset if appropriate */
+ switch(which->state) {
+ case SERV_DEAD: /* leave him dead */
+ server_flush(which);
+ auth = 1;
+ break;
+ case SERV_UP: /* he's now tardy */
+ which->state = SERV_TARDY;
+ which->num_hello_sent = 0;
+ which->timeout = timo_tardy;
+ auth = 0;
+ break;
+ case SERV_TARDY:
+ case SERV_STARTING:
+ if (which->num_hello_sent >= ((which->state == SERV_TARDY) ?
+ H_NUM_TARDY :
+ H_NUM_STARTING)) {
+ /* he hasn't answered, assume DEAD */
+ which->state = SERV_DEAD;
+ which->num_hello_sent = 0;
+ which->timeout = timo_dead;
+ srv_nack_release(which);
+ }
+ auth = 0;
+ break;
+ default:
+ syslog(LOG_ERR,"Bad server state, server 0x%x\n",which);
+ abort();
+ }
+ /* now he's either TARDY, STARTING, or DEAD
+ We send a "hello," which increments the counter */
+#if 0
+ zdbug((LOG_DEBUG, "srv %s is %s", which->addr_str,
+ srv_states[which->state]));
+#endif
+ server_hello(which, auth);
+ /* reschedule the timer */
+ which->timer = timer_set_rel(which->timeout, server_timo, which);
+}
+
+/*
+ * Dispatch a notice from some other server
+ */
+
+/*ARGSUSED*/
+Code_t
+server_dispatch(notice, auth, who)
+ ZNotice_t *notice;
+ int auth;
+ struct sockaddr_in *who;
+{
+ Server *server;
+ struct sockaddr_in newwho;
+ Code_t status;
+ String *notice_class;
+
+#if 0
+ zdbug((LOG_DEBUG, "server_dispatch"));
+#endif
+
+ if (notice->z_kind == SERVACK) {
+ srv_nack_cancel(notice, who);
+ srv_responded(who);
+ return ZERR_NONE;
+ }
+ /* set up a who for the real origin */
+ memset(&newwho, 0, sizeof(newwho));
+ newwho.sin_family = AF_INET;
+ newwho.sin_addr.s_addr = notice->z_sender_addr.s_addr;
+ newwho.sin_port = notice->z_port;
+
+ server = server_which_server(who);
+
+ /* we can dispatch to routines safely here, since they will
+ return ZSRV_REQUEUE if appropriate. We bounce this back
+ to the caller, and the caller will re-queue the message
+ for us to process later. */
+
+ notice_class = make_string(notice->z_class, 1);
+
+ if (realm_which_realm(&newwho))
+ status = realm_dispatch(notice, auth, &newwho, server);
+ else if (class_is_admin(notice_class)) {
+ /* admins don't get acked, else we get a packet loop */
+ /* will return requeue if bdump request and dumping */
+ i_s_admins.val++;
+ return admin_dispatch(notice, auth, who, server);
+ } else if (class_is_control(notice_class)) {
+ status = control_dispatch(notice, auth, &newwho, server);
+ i_s_ctls.val++;
+ } else if (class_is_ulogin(notice_class)) {
+ status = ulogin_dispatch(notice, auth, &newwho, server);
+ i_s_logins.val++;
+ } else if (class_is_ulocate(notice_class)) {
+ status = ulocate_dispatch(notice, auth, &newwho, server);
+ i_s_locates.val++;
+ } else {
+ /* shouldn't come from another server */
+ syslog(LOG_WARNING, "srv_disp: pkt cls %s", notice->z_class);
+ status = ZERR_NONE; /* XXX */
+ }
+ if (status != ZSRV_REQUEUE)
+ ack(notice, who); /* acknowledge it if processed */
+ free_string(notice_class);
+ return status;
+}
+
+#ifdef notdef
+/*
+ * Register a new server (one not in our list). This MUST be authenticated.
+ */
+
+/*ARGSUSED*/
+static Code_t
+server_register(notice, auth, who)
+ ZNotice_t *notice;
+ int auth;
+ struct sockaddr_in *who;
+{
+ Server *temp;
+ int i;
+ long timerval;
+
+ if (who->sin_port != srv_addr.sin_port) {
+#if 0
+ zdbug((LOG_DEBUG, "srv_wrong port %d", ntohs(who->sin_port)));
+#endif
+ return 1;
+ }
+ /* Not yet... talk to ken about authenticators */
+#ifdef notdef
+ if (!auth) {
+#if 0
+ zdbug((LOG_DEBUG, "srv_unauth"));
+#endif
+ return 1;
+ }
+#endif /* notdef */
+ /* OK, go ahead and set him up. */
+ temp = (Server *) malloc((nservers + 1) * sizeof(Server));
+ if (!temp) {
+ syslog(LOG_CRIT, "srv_reg malloc");
+ return 1;
+ }
+
+ memcpy(temp, otherservers, nservers * sizeof(Server));
+ free(otherservers);
+ otherservers = temp;
+ /* don't reschedule limbo's timer, so start i=1 */
+ for (i = 1; i < nservers; i++) {
+ if (i == me_server_idx) /* don't reset myself */
+ continue;
+ /* reschedule the timers--we moved otherservers */
+ timerval = timer_when(otherservers[i].timer);
+ timer_reset(otherservers[i].timer);
+ otherservers[i].timer = timer_set_abs(timerval, server_timo,
+ &otherservers[i]);
+ }
+ setup_server(&otherservers[nservers], &who->sin_addr);
+ otherservers[nservers].state = SERV_STARTING;
+ otherservers[nservers].timeout = timo_tardy;
+ otherservers[nservers].update_queue = NULL;
+ otherservers[nservers].dumping = 0;
+
+ nservers++;
+#if 0
+ zdbug((LOG_DEBUG, "srv %s is %s", otherservers[nservers].addr_str,
+ srv_states[otherservers[nservers].state]));
+#endif
+
+ return 0;
+}
+#endif
+
+/*
+ * Tell the other servers that this client died.
+ */
+
+void
+server_kill_clt(client)
+ Client *client;
+{
+ int i;
+ char buf[512], *lyst[2];
+ ZNotice_t notice;
+ ZNotice_t *pnotice; /* speed hack */
+ caddr_t pack;
+ int packlen, auth;
+ Code_t retval;
+
+ lyst[0] = inet_ntoa(client->addr.sin_addr),
+ sprintf(buf, "%d", ntohs(client->addr.sin_port));
+ lyst[1] = buf;
+
+#if 0
+ zdbug((LOG_DEBUG, "server kill clt %s/%s", lyst[0], lyst[1]));
+#endif
+
+ pnotice = ¬ice;
+
+ pnotice->z_kind = ACKED;
+
+ pnotice->z_port = srv_addr.sin_port;
+ pnotice->z_class = ZEPHYR_ADMIN_CLASS;
+ pnotice->z_class_inst = "";
+ pnotice->z_opcode = ADMIN_KILL_CLT;
+ pnotice->z_sender = myname; /* myname is the hostname */
+ pnotice->z_recipient = "";
+ pnotice->z_default_format = "";
+ pnotice->z_num_other_fields = 0;
+
+ /* XXX */
+ auth = 0;
+
+ /* don't tell limbo to flush, start at 1*/
+ for (i = 1; i < nservers; i++) {
+ if (i == me_server_idx) /* don't xmit to myself */
+ continue;
+ if (otherservers[i].state == SERV_DEAD)
+ continue;
+
+ retval = ZFormatNoticeList(pnotice, lyst, 2, &pack, &packlen,
+ auth ? ZAUTH : ZNOAUTH);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_WARNING, "kill_clt format: %s", error_message(retval));
+ return;
+ }
+ server_forw_reliable(&otherservers[i], pack, packlen, pnotice);
+ }
+}
+
+/*
+ * A client has died. remove it
+ */
+
+static Code_t
+kill_clt(notice, server)
+ ZNotice_t *notice;
+ Server *server;
+{
+ struct sockaddr_in who;
+ Client *client;
+
+#if 0
+ zdbug((LOG_DEBUG, "kill_clt"));
+#endif
+ if (extract_addr(notice, &who) != ZERR_NONE)
+ return ZERR_NONE; /* XXX */
+ client = client_find(&who.sin_addr, notice->z_port);
+ if (!client) {
+ syslog(LOG_NOTICE, "kill_clt: no such client (%s/%d) from %s",
+ inet_ntoa(who.sin_addr), ntohs(who.sin_port),
+ server->addr_str);
+ return ZERR_NONE; /* XXX */
+ }
+#if 1
+ if (zdebug || 1) {
+ syslog(LOG_DEBUG, "kill_clt clt_dereg %s/%d from %s",
+ inet_ntoa(who.sin_addr), ntohs(who.sin_port), server->addr_str);
+ }
+#endif
+
+ /* remove the locations, too */
+ client_deregister(client, 1);
+ return ZERR_NONE;
+}
+
+/*
+ * extract a sockaddr_in from a message body
+ */
+
+static Code_t
+extract_addr(notice, who)
+ ZNotice_t *notice;
+ struct sockaddr_in *who;
+{
+ char *cp = notice->z_message;
+
+ if (!notice->z_message_len) {
+ syslog(LOG_WARNING, "bad addr pkt");
+ return ZSRV_PKSHORT;
+ }
+ who->sin_addr.s_addr = inet_addr(notice->z_message);
+
+ cp += strlen(cp) + 1;
+ if (cp >= notice->z_message + notice->z_message_len) {
+ syslog(LOG_WARNING, "short addr pkt");
+ return ZSRV_PKSHORT;
+ }
+ who->sin_port = notice->z_port = htons((u_short) atoi(cp));
+ who->sin_family = AF_INET;
+#if 0
+ zdbug((LOG_DEBUG,"ext %s/%d", inet_ntoa(who->sin_addr),
+ ntohs(who->sin_port)));
+#endif
+ return ZERR_NONE;
+}
+
+/*
+ * Flush all data associated with the server which
+ */
+
+static void
+server_flush(which)
+ Server *which;
+{
+#if 0
+ if (zdebug)
+ syslog(LOG_DEBUG, "server_flush %s", which->addr_str);
+#endif
+ srv_nack_release(which);
+}
+
+/*
+ * send a hello to which, updating the count of hello's sent
+ * Authenticate if auth is set.
+ */
+
+static void
+server_hello(which, auth)
+ Server *which;
+ int auth;
+{
+ send_msg(&which->addr, ADMIN_HELLO, auth);
+ which->num_hello_sent++;
+}
+
+/*
+ * Handle an ADMIN message from a server
+ */
+
+/*ARGSUSED*/
+static Code_t
+admin_dispatch(notice, auth, who, server)
+ ZNotice_t *notice;
+ int auth;
+ struct sockaddr_in *who;
+ Server *server;
+{
+ char *opcode = notice->z_opcode;
+ Code_t status = ZERR_NONE;
+
+#if 0
+ zdbug((LOG_DEBUG, "ADMIN received"));
+#endif
+
+ if (strcmp(opcode, ADMIN_HELLO) == 0) {
+ hello_respond(who, ADJUST, auth);
+ } else if (strcmp(opcode, ADMIN_IMHERE) == 0) {
+ srv_responded(who);
+ } else if (strcmp(opcode, ADMIN_SHUTDOWN) == 0) {
+#if 0
+ zdbug((LOG_DEBUG, "server shutdown"));
+#endif
+ if (server) {
+ srv_nack_release(server);
+ server->state = SERV_DEAD;
+ server->timeout = timo_dead;
+ /* don't worry about the timer, it will
+ be set appropriately on the next send */
+#if 0
+ zdbug((LOG_DEBUG, "srv %s is %s", server->addr_str,
+ srv_states[server->state]));
+#endif
+ }
+ } else if (strcmp(opcode, ADMIN_BDUMP) == 0) {
+ /* Ignore a brain dump request if this is a brain dump packet
+ * or a packet being processed concurrently during a brain
+ * dump. */
+ if (bdumping || bdump_concurrent)
+ return ZERR_NONE;
+ bdump_get(notice, auth, who, server);
+ } else if (strcmp(opcode, ADMIN_KILL_CLT) == 0) {
+ status = kill_clt(notice, server);
+ if (status == ZERR_NONE)
+ ack(notice, who);
+ } else {
+ syslog(LOG_WARNING, "ADMIN unknown opcode %s",opcode);
+ }
+ return status;
+}
+
+
+/*
+ * Handle an ADMIN message from some random client.
+ * For now, assume it's a registration-type message from some other
+ * previously unknown server
+ */
+
+/*ARGSUSED*/
+Code_t
+server_adispatch(notice, auth, who, server)
+ ZNotice_t *notice;
+ int auth;
+ struct sockaddr_in *who;
+ Server *server;
+{
+
+ /* this had better be a HELLO message--start of acquisition
+ protocol, OR a status req packet */
+
+ if (strcmp(notice->z_opcode, ADMIN_STATUS) == 0) {
+ /* status packet */
+ send_stats(who);
+ return ZERR_NONE;
+ }
+
+#ifdef notdef
+ syslog(LOG_INFO, "disp: new server?");
+ if (server_register(notice, auth, who) != ZERR_NONE) {
+ syslog(LOG_INFO, "new server failed");
+ } else {
+ syslog(LOG_INFO, "new server %s, %d", inet_ntoa(who->sin_addr),
+ ntohs(who->sin_port));
+ hello_respond(who, DONT_ADJUST, auth);
+ }
+#else
+ syslog(LOG_INFO, "srv_adisp: server attempt from %s",
+ inet_ntoa(who->sin_addr));
+#endif /* notdef */
+
+ return ZERR_NONE;
+}
+
+static void
+send_stats(who)
+ struct sockaddr_in *who;
+{
+ int i;
+ char buf[BUFSIZ];
+ char **responses;
+ int num_resp;
+ char *vers, *pkts, *upt;
+
+#if defined(OLD_COMPAT) || defined(NEW_COMPAT)
+ int extrafields = 0;
+#endif /* OLD_ or NEW_COMPAT */
+#define NUM_FIXED 3 /* 3 fixed fields, plus server info */
+ /* well, not really...but for
+ backward compatibility, we gotta
+ do it this way. */
+ vers = get_version();
+
+ sprintf(buf, "%d pkts", npackets);
+ pkts = strsave(buf);
+ sprintf(buf, "%d seconds operational",NOW - uptime);
+ upt = strsave(buf);
+
+#ifdef OLD_COMPAT
+ if (old_compat_count_uloc)
+ extrafields++;
+ if (old_compat_count_ulocate)
+ extrafields++;
+ if (old_compat_count_subscr)
+ extrafields++;
+#endif /* OLD_COMPAT */
+#ifdef NEW_COMPAT
+ if (new_compat_count_uloc)
+ extrafields++;
+ if (new_compat_count_subscr)
+ extrafields++;
+#endif /* NEW_COMPAT */
+#if defined(OLD_COMPAT) || defined(NEW_COMPAT)
+ responses = (char **) malloc((NUM_FIXED + nservers + extrafields) *
+ sizeof(char *));
+#else
+ responses = (char **) malloc((NUM_FIXED + nservers) * sizeof(char *));
+#endif /* OLD_ or NEW_COMPAT */
+ responses[0] = vers;
+ responses[1] = pkts;
+ responses[2] = upt;
+
+ num_resp = NUM_FIXED;
+ /* start at 1 and ignore limbo */
+ for (i = 1; i < nservers ; i++) {
+ sprintf(buf, "%s/%s%s", otherservers[i].addr_str,
+ srv_states[(int) otherservers[i].state],
+ otherservers[i].dumping ? " (DUMPING)" : "");
+ responses[num_resp++] = strsave(buf);
+ }
+#ifdef OLD_COMPAT
+ if (old_compat_count_uloc) {
+ sprintf(buf, "%d old old location requests", old_compat_count_uloc);
+ responses[num_resp++] = strsave(buf);
+ }
+ if (old_compat_count_ulocate) {
+ sprintf(buf, "%d old old loc lookup requests",
+ old_compat_count_ulocate);
+ responses[num_resp++] = strsave(buf);
+ }
+ if (old_compat_count_subscr) {
+ sprintf(buf, "%d old old subscr requests", old_compat_count_subscr);
+ responses[num_resp++] = strsave(buf);
+ }
+#endif /* OLD_COMPAT */
+#ifdef NEW_COMPAT
+ if (new_compat_count_uloc) {
+ sprintf(buf, "%d new old location requests", new_compat_count_uloc);
+ responses[num_resp++] = strsave(buf);
+ }
+ if (new_compat_count_subscr) {
+ sprintf(buf, "%d new old subscr requests", new_compat_count_subscr);
+ responses[num_resp++] = strsave(buf);
+ }
+#endif /* NEW_COMPAT */
+
+ send_msg_list(who, ADMIN_STATUS, responses, num_resp, 0);
+
+ /* Start at one; don't try to free static version string */
+ for (i = 1; i < num_resp; i++)
+ free(responses[i]);
+ free(responses);
+}
+
+/*
+ * Get a list of server addresses.
+#ifdef HAVE_HESIOD
+ * This list is retrieved from Hesiod.
+#else
+ * This list is read from a file.
+#endif
+ * Return a pointer to an array of allocated storage. This storage is
+ * freed by the caller.
+ */
+
+static struct in_addr *
+get_server_addrs(number)
+ int *number; /* RETURN */
+{
+ int i;
+ char **server_hosts;
+ char **cpp;
+ struct in_addr *addrs;
+ struct in_addr *addr;
+ struct hostent *hp;
+
+#ifdef HAVE_HESIOD
+ /* get the names from Hesiod */
+ server_hosts = hes_resolve("zephyr","sloc");
+ if (!server_hosts)
+ return NULL;
+#else
+ server_hosts = get_server_list(list_file);
+ if (!server_hosts)
+ return NULL;
+#endif
+ /* count up */
+ i = 0;
+ for (cpp = server_hosts; *cpp; cpp++)
+ i++;
+
+ addrs = (struct in_addr *) malloc(i * sizeof(struct in_addr));
+
+ /* Convert to in_addr's */
+ for (cpp = server_hosts, addr = addrs, i = 0; *cpp; cpp++) {
+ hp = gethostbyname(*cpp);
+ if (hp) {
+ memcpy(addr, hp->h_addr, sizeof(struct in_addr));
+ addr++, i++;
+ } else {
+ syslog(LOG_WARNING, "hostname failed, %s", *cpp);
+ }
+ }
+ *number = i;
+#ifndef HAVE_HESIOD
+ free_server_list(server_hosts);
+#endif
+ return addrs;
+}
+
+#ifndef HAVE_HESIOD
+
+static int nhosts = 0;
+
+/*
+ * read "file" to get a list of names of hosts to peer with.
+ * The file should contain a list of host names, one per line.
+ */
+
+static char **
+get_server_list(file)
+ char *file;
+{
+ FILE *fp;
+ char buf[MAXHOSTNAMELEN];
+ char **ret_list;
+ int nused = 0;
+ char *newline;
+
+ /* start with 16, realloc if necessary */
+ nhosts = 16;
+ ret_list = (char **) malloc(nhosts * sizeof(char *));
+
+ fp = fopen(file, "r");
+ if (fp) {
+ while (fgets(buf, MAXHOSTNAMELEN, fp)) {
+ /* nuke the newline, being careful not to overrun
+ the buffer searching for it with strlen() */
+ buf[MAXHOSTNAMELEN - 1] = '\0';
+ newline = strchr(buf, '\n');
+ if (newline)
+ *newline = '\0';
+
+ if (nused + 1 >= nhosts) {
+ /* get more pointer space if necessary */
+ /* +1 to leave room for null pointer */
+ ret_list = (char **) realloc(ret_list, nhosts * 2);
+ nhosts = nhosts * 2;
+ }
+ ret_list[nused++] = strsave(buf);
+ }
+ fclose(fp);
+ } else {
+ if (gethostname(buf, sizeof(buf)) < 0) {
+ free(ret_list);
+ return NULL;
+ }
+ ret_list[nused++] = strsave(buf);
+ }
+ ret_list[nused] = NULL;
+ return ret_list;
+}
+
+/*
+ * free storage allocated by get_server_list
+ */
+static void
+free_server_list(list)
+ char **list;
+{
+ char **orig_list = list;
+
+ if (!nhosts) /* nothing allocated */
+ return;
+ for (; *list; list++)
+ free(*list);
+ free(orig_list);
+ return;
+}
+#endif
+
+/*
+ * initialize the server structure for address addr, and set a timer
+ * to go off immediately to send hello's to other servers.
+ */
+
+static void
+setup_server(server, addr)
+ Server *server;
+ struct in_addr *addr;
+{
+ server->state = SERV_DEAD;
+ server->timeout = timo_dead;
+ server->num_hello_sent = 0;
+ server->addr.sin_family = AF_INET;
+ /* he listens to the same port we do */
+ server->addr.sin_port = srv_addr.sin_port;
+ server->addr.sin_addr = *addr;
+ strcpy(server->addr_str, inet_ntoa(*addr));
+ server->timer = timer_set_rel(0L, server_timo, server);
+ server->queue = NULL;
+ server->dumping = 0;
+}
+
+/*
+ * Someone sent us a hello message, respond to them.
+ */
+
+static void
+hello_respond(who, adj, auth)
+ struct sockaddr_in *who;
+ int adj;
+ int auth;
+{
+ Server *which;
+
+#if 0
+ zdbug((LOG_DEBUG, "hello from %s", inet_ntoa(who->sin_addr)));
+#endif
+
+ send_msg(who, ADMIN_IMHERE, auth);
+ if (adj != ADJUST)
+ return;
+
+ /* If we think he's down, schedule an immediate HELLO. */
+
+ which = server_which_server(who);
+ if (!which)
+ return;
+
+ switch (which->state) {
+ case SERV_DEAD:
+ /* he said hello, we thought he was dead.
+ reschedule his hello for now. */
+ timer_reset(which->timer);
+ which->timer = timer_set_rel(0L, server_timo, which);
+ break;
+ case SERV_STARTING:
+ case SERV_TARDY:
+ case SERV_UP:
+ default:
+ break;
+ }
+}
+
+/*
+ * return the server descriptor for server at who
+ */
+
+Server *
+server_which_server(who)
+ struct sockaddr_in *who;
+{
+ Server *server;
+ int i;
+
+ if (who->sin_port != srv_addr.sin_port)
+ return NULL;
+
+ /* don't check limbo */
+ for (server = &otherservers[1], i = 1; i < nservers; i++, server++) {
+ if (server->addr.sin_addr.s_addr == who->sin_addr.s_addr)
+ return server;
+ }
+ return NULL;
+}
+
+/*
+ * We received a response to a hello packet or an ack. Adjust server state
+ * appropriately.
+ */
+static void
+srv_responded(who)
+ struct sockaddr_in *who;
+{
+ Server *which = server_which_server(who);
+
+#if 0
+ zdbug((LOG_DEBUG, "srv_responded %s", inet_ntoa(who->sin_addr)));
+#endif
+
+ if (!which) {
+ syslog(LOG_ERR, "hello input from non-server?!");
+ return;
+ }
+
+ switch (which->state) {
+ case SERV_DEAD:
+ /* he responded, we thought he was dead. mark as starting
+ and negotiate */
+ which->state = SERV_STARTING;
+ which->timeout = timo_tardy;
+ timer_reset(which->timer);
+ which->timer = timer_set_rel(0L, server_timo, which);
+
+ case SERV_STARTING:
+ /* here we negotiate and set up a braindump */
+ if (bdump_socket < 0)
+ bdump_offer(who);
+ break;
+
+ case SERV_TARDY:
+ which->state = SERV_UP;
+ /* Fall through. */
+
+ case SERV_UP:
+ /* reset the timer and counts */
+ which->num_hello_sent = 0;
+ which->timeout = timo_up;
+ timer_reset(which->timer);
+ which->timer = timer_set_rel(which->timeout, server_timo, which);
+ break;
+ }
+#if 0
+ zdbug((LOG_DEBUG, "srv %s is %s", which->addr_str,
+ srv_states[which->state]));
+#endif
+}
+
+/*
+ * Send each of the other servers a shutdown message.
+ */
+
+void
+server_shutdown()
+{
+ int i;
+
+ /* don't tell limbo to go away, start at 1*/
+ for (i = 1; i < nservers; i++)
+ send_msg(&otherservers[i].addr, ADMIN_SHUTDOWN, 1);
+}
+
+/*
+ * send a message to who with admin class and opcode and clinst as specified.
+ * auth is set if we want to send authenticated
+ */
+
+static void
+send_msg(who, opcode, auth)
+ struct sockaddr_in *who;
+ char *opcode;
+ int auth;
+{
+ ZNotice_t notice;
+ ZNotice_t *pnotice; /* speed hack */
+ char *pack;
+ int packlen;
+ Code_t retval;
+
+ pnotice = ¬ice;
+
+ pnotice->z_kind = ACKED;
+
+ pnotice->z_port = srv_addr.sin_port;
+ pnotice->z_class = ZEPHYR_ADMIN_CLASS;
+ pnotice->z_class_inst = "";
+ pnotice->z_opcode = opcode;
+ pnotice->z_sender = myname; /* myname is the hostname */
+ pnotice->z_recipient = "";
+ pnotice->z_default_format = "";
+ pnotice->z_message = NULL;
+ pnotice->z_message_len = 0;
+ pnotice->z_num_other_fields = 0;
+
+ /* XXX for now, we don't do authentication */
+ auth = 0;
+
+ retval = ZFormatNotice(pnotice, &pack, &packlen, auth ? ZAUTH : ZNOAUTH);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_WARNING, "snd_msg format: %s", error_message(retval));
+ return;
+ }
+ retval = ZSetDestAddr(who);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_WARNING, "snd_msg set addr: %s", error_message(retval));
+ free(pack);
+ return;
+ }
+ /* don't wait for ack */
+ retval = ZSendPacket(pack, packlen, 0);
+ if (retval != ZERR_NONE)
+ syslog(LOG_WARNING, "snd_msg xmit: %s", error_message(retval));
+ free(pack);
+}
+
+/*
+ * send a notice with a message to who with admin class and opcode and
+ * message body as specified.
+ * auth is set if we want to send authenticated
+ * server_idx is -1 if we are sending to a client, or the server index
+ * if we are sending to a server.
+ */
+
+static void
+send_msg_list(who, opcode, lyst, num, auth)
+ struct sockaddr_in *who;
+ char *opcode;
+ char **lyst;
+ int num;
+ int auth;
+{
+ ZNotice_t notice;
+ char *pack;
+ int packlen;
+ Code_t retval;
+ Unacked *nacked;
+
+ notice.z_kind = UNSAFE;
+ notice.z_port = srv_addr.sin_port;
+ notice.z_class = ZEPHYR_ADMIN_CLASS;
+ notice.z_class_inst = "";
+ notice.z_opcode = opcode;
+ notice.z_sender = myname; /* myname is the hostname */
+ notice.z_recipient = "";
+ notice.z_default_format = "";
+ notice.z_message = NULL;
+ notice.z_message_len = 0;
+ notice.z_num_other_fields = 0;
+
+ /* XXX for now, we don't do authentication */
+ auth = 0;
+
+ retval = ZFormatNoticeList(¬ice, lyst, num, &pack, &packlen,
+ auth ? ZAUTH : ZNOAUTH);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_WARNING, "snd_msg_lst format: %s", error_message(retval));
+ return;
+ }
+ retval = ZSetDestAddr(who);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_WARNING, "snd_msg_lst set addr: %s", error_message(retval));
+ free(pack);
+ return;
+ }
+ xmit_frag(¬ice, pack, packlen, 0);
+ free(pack);
+}
+
+/*
+ * Forward the notice to the other servers
+ */
+/*ARGSUSED*/
+void
+server_forward(notice, auth, who)
+ ZNotice_t *notice;
+ int auth;
+ struct sockaddr_in *who;
+{
+ int i;
+ caddr_t pack;
+ int packlen;
+ Code_t retval;
+
+#if 0
+ zdbug((LOG_DEBUG, "srv_forw"));
+#endif
+ /* don't send to limbo */
+ for (i = 1; i < nservers; i++) {
+ if (i == me_server_idx) /* don't xmit to myself */
+ continue;
+ if (otherservers[i].state == SERV_DEAD &&
+ otherservers[i].dumping == 0) {
+ /* if we are dumping to him, we want to
+ queue it, even if he's dead */
+ continue;
+ }
+
+ pack = malloc(sizeof(ZPacket_t));
+ if (!pack) {
+ syslog(LOG_CRIT, "srv_fwd malloc");
+ abort();
+ }
+ retval = ZFormatSmallRawNotice(notice, pack, &packlen);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_WARNING, "srv_fwd format: %s", error_message(retval));
+ continue;
+ }
+ if (otherservers[i].dumping) {
+ server_queue(&otherservers[i], packlen, pack, auth, who);
+ continue;
+ }
+ server_forw_reliable(&otherservers[i], pack, packlen, notice);
+ }
+}
+
+static void
+server_forw_reliable(server, pack, packlen, notice)
+ Server *server;
+ caddr_t pack;
+ int packlen;
+ ZNotice_t *notice;
+{
+ Code_t retval;
+ Unacked *nacked;
+ int hashval;
+
+ retval = ZSetDestAddr(&server->addr);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_WARNING, "srv_fwd_rel set addr: %s", error_message(retval));
+ free(pack);
+ return;
+ }
+ retval = ZSendPacket(pack, packlen, 0);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_WARNING, "srv_fwd xmit: %s", error_message(retval));
+ free(pack);
+ return;
+ }
+ /* now we've sent it, mark it as not ack'ed */
+
+ nacked = (Unacked *) malloc(sizeof(Unacked));
+ if (!nacked) {
+ /* no space: just punt */
+ syslog(LOG_ERR, "srv_forw_rel nack malloc");
+ free(pack);
+ return;
+ }
+
+ nacked->client = NULL;
+ nacked->rexmits = 0;
+ nacked->packet = pack;
+ nacked->dest.srv_idx = server - otherservers;
+ nacked->packsz = packlen;
+ nacked->uid = notice->z_uid;
+ nacked->timer = timer_set_rel(rexmit_times[0], srv_rexmit, nacked);
+ hashval = SRV_NACKTAB_HASHVAL(nacked->dest.srv_idx, nacked->uid);
+ LIST_INSERT(&srv_nacktab[hashval], nacked);
+}
+
+/*
+ * send the queued message for the server.
+ */
+
+void
+server_send_queue(server)
+ Server *server;
+{
+ Pending *pending;
+ ZNotice_t notice;
+ Code_t status;
+
+ while (server->queue) {
+ pending = server_dequeue(server);
+ status = ZParseNotice(pending->packet, pending->len, ¬ice);
+ if (status != ZERR_NONE) {
+ syslog(LOG_ERR, "ssq bad notice parse (%s): %s",
+ inet_ntoa(pending->who.sin_addr), error_message(status));
+ } else {
+ server_forw_reliable(server, pending->packet, pending->len,
+ ¬ice);
+ free(pending);
+ /* ACK handling routines will free the packet */
+ }
+ }
+}
+
+/*
+ * a server has acknowledged a message we sent to him; remove it from
+ * server unacked queue
+ */
+
+static void
+srv_nack_cancel(notice, who)
+ ZNotice_t *notice;
+ struct sockaddr_in *who;
+{
+ Server *server = server_which_server(who);
+ Unacked *nacked;
+ int hashval;
+
+ if (!server) {
+ syslog(LOG_ERR, "non-server ack?");
+ return;
+ }
+ hashval = SRV_NACKTAB_HASHVAL(server - otherservers, notice->z_uid);
+ for (nacked = srv_nacktab[hashval]; nacked; nacked = nacked->next) {
+ if (nacked->dest.srv_idx == server - otherservers
+ && ZCompareUID(&nacked->uid, ¬ice->z_uid)) {
+ timer_reset(nacked->timer);
+ free(nacked->packet);
+ LIST_DELETE(nacked);
+ free(nacked);
+ return;
+ }
+ }
+#if 0
+ zdbug((LOG_DEBUG, "srv_nack not found"));
+#endif
+}
+
+/*
+ * retransmit a message to another server
+ */
+
+static void
+srv_rexmit(arg)
+ void *arg;
+{
+ Unacked *packet = (Unacked *) arg;
+ Code_t retval;
+ /* retransmit the packet */
+
+#if 0
+ zdbug((LOG_DEBUG,"srv_rexmit to %s/%d",
+ otherservers[packet->dest.srv_idx].addr_str,
+ ntohs(otherservers[packet->dest.srv_idx].addr.sin_port)));
+#endif
+ if (otherservers[packet->dest.srv_idx].state == SERV_DEAD) {
+#if 0
+ zdbug((LOG_DEBUG, "cancelling send to dead server"));
+#endif
+ LIST_DELETE(packet);
+ free(packet->packet);
+ srv_nack_release(&otherservers[packet->dest.srv_idx]);
+ free(packet);
+ return;
+ }
+ retval = ZSetDestAddr(&otherservers[packet->dest.srv_idx].addr);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_WARNING, "srv_rexmit set addr: %s", error_message(retval));
+ } else {
+ retval = ZSendPacket(packet->packet, packet->packsz, 0);
+ if (retval != ZERR_NONE)
+ syslog(LOG_WARNING, "srv_rexmit xmit: %s",
+ error_message(retval));
+ }
+
+ /* reset the timer */
+ if (rexmit_times[packet->rexmits + 1] != -1)
+ packet->rexmits++;
+ packet->timer = timer_set_rel(rexmit_times[packet->rexmits], srv_rexmit,
+ packet);
+}
+
+/*
+ * Clean up the not-yet-acked queue and release anything destined
+ * to the server.
+ */
+
+static void
+srv_nack_release(server)
+ Server *server;
+{
+ int i;
+ Unacked *nacked, *next;
+
+ for (i = 0; i < SRV_NACKTAB_HASHSIZE; i++) {
+ for (nacked = srv_nacktab[i]; nacked; nacked = next) {
+ next = nacked->next;
+ if (nacked->dest.srv_idx == server - otherservers) {
+ timer_reset(nacked->timer);
+ LIST_DELETE(nacked);
+ free(nacked->packet);
+ free(nacked);
+ }
+ }
+ }
+}
+
+/*
+ * Adjust indices of not-yet-acked packets sent to other servers to
+ * continue to refer to the correct server.
+ */
+
+static void
+srv_nack_renumber (new_idx)
+ int *new_idx;
+{
+ /* XXX release any private queue for this server */
+ Unacked *nacked;
+ int idx, i;
+
+ /* search the not-yet-acked list for anything destined to 'from', and
+ change the index to 'to'. */
+ for (i = 0; i < SRV_NACKTAB_HASHSIZE; i++) {
+ for (nacked = srv_nacktab[i]; nacked; nacked = nacked->next) {
+ idx = new_idx[nacked->dest.srv_idx];
+ if (idx < 0) {
+ syslog(LOG_ERR, "srv_nack_renumber error: [%d]=%d",
+ nacked->dest.srv_idx, idx);
+ idx = 0;
+ }
+ nacked->dest.srv_idx = idx;
+ }
+ }
+}
+
+/*
+ * Queue this notice to be transmitted to the server when it is ready.
+ */
+static void
+server_queue(server, len, pack, auth, who)
+ Server *server;
+ int len;
+ void *pack;
+ int auth;
+ struct sockaddr_in *who;
+{
+ Pending *pending;
+
+ pending = (Pending *) malloc(sizeof(Pending));
+ if (!pending) {
+ syslog(LOG_CRIT, "update_queue malloc");
+ abort();
+ }
+ pending->packet = pack;
+ pending->len = len;
+ pending->auth = auth;
+ pending->who = *who;
+ pending->next = NULL;
+
+ /* put it on the end of the list */
+ if (server->queue)
+ server->queue_last->next = pending;
+ else
+ server->queue = server->queue_last = pending;
+}
+
+/*
+ * Pull a notice off the hold queue.
+ */
+
+Pending *
+server_dequeue(server)
+ Server *server;
+{
+ Pending *pending;
+
+ if (!server->queue)
+ return NULL;
+ pending = server->queue;
+ server->queue = pending->next;
+ return pending;
+}
+
+/*
+ * free storage used by a pending queue entry.
+ */
+
+void
+server_pending_free(pending)
+ Pending *pending;
+{
+ free(pending->packet);
+ free(pending);
+ return;
+}
+
+/*
+ * Queue something to be handled later by this server.
+ */
+
+void
+server_self_queue(notice, auth, who)
+ ZNotice_t* notice;
+ int auth;
+ struct sockaddr_in * who;
+{
+ char *pack;
+ int packlen;
+ Code_t retval;
+
+ retval = ZFormatRawNotice(notice, &pack, &packlen);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_CRIT, "srv_self_queue format: %s", error_message(retval));
+ abort();
+ }
+ server_queue(me_server, packlen, pack, auth, who);
+}
+
+/*
+ * dump info about servers onto the fp.
+ * assumed to be called with SIGFPE blocked
+ * (true if called from signal handler)
+ */
+void
+server_dump_servers(fp)
+ FILE *fp;
+{
+ int i;
+
+ for (i = 0; i < nservers ; i++) {
+ fprintf(fp, "%d:%s/%s%s\n", i, otherservers[i].addr_str,
+ srv_states[otherservers[i].state],
+ otherservers[i].dumping ? " (DUMPING)" : "");
+ }
+}
+
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains functions for managing subscription lists.
+ *
+ * Created by: John T. Kohl
+ *
+ * $Id: subscr.c,v 1.56 1999/01/22 23:19:48 ghudson Exp $
+ *
+ * Copyright (c) 1987,1988 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <zephyr/mit-copyright.h>
+#include "zserver.h"
+
+#ifndef lint
+#ifndef SABER
+static const char rcsid_subscr_c[] = "$Id: subscr.c,v 1.56 1999/01/22 23:19:48 ghudson Exp $";
+#endif
+#endif
+
+/*
+ * The subscription manager.
+ *
+ * External functions:
+ *
+ * Code_t subscr_subscribe(who, notice)
+ * Client *who;
+ * ZNotice_t *notice;
+ *
+ * Code_t subscr_cancel(sin, notice)
+ * struct sockaddr_in *sin;
+ * ZNotice_t *notice;
+ *
+ * Code_t subscr_cancel_client(client)
+ * Client *client;
+ *
+ * Code_t subscr_cancel_host(addr)
+ * struct in_addr *addr;
+ *
+ * Client *subscr_match_list(notice)
+ * ZNotice_t *notice;
+ *
+ * void subscr_free_list(list)
+ * Client *list;
+ *
+ * void subscr_sendlist(notice, auth, who)
+ * ZNotice_t *notice;
+ * int auth;
+ * struct sockaddr_in *who;
+ *
+ * Code_t subscr_send_subs(client, vers)
+ * Client *client;
+ * char *vers;
+ *
+ * Code_t subscr_def_subs(who)
+ * Client *who;
+ *
+ * void subscr_reset();
+ *
+ */
+
+#ifdef HAVE_KRB4
+#ifndef NOENCRYPTION
+C_Block serv_key;
+Sched serv_ksched;
+#endif
+#endif
+
+/* for compatibility when sending subscription information to old clients */
+
+#ifdef OLD_COMPAT
+#define OLD_ZEPHYR_VERSION "ZEPH0.0"
+#define OLD_CLIENT_INCOMPSUBS "INCOMP"
+static void old_compat_subscr_sendlist __P((ZNotice_t *notice, int auth,
+ struct sockaddr_in *who));
+extern int old_compat_count_subscr; /* counter of old use */
+#endif /* OLD_COMPAT */
+#ifdef NEW_COMPAT
+#define NEW_OLD_ZEPHYR_VERSION "ZEPH0.1"
+static void new_old_compat_subscr_sendlist __P((ZNotice_t *notice, int auth,
+ struct sockaddr_in *who));
+extern int new_compat_count_subscr; /* counter of old use */
+#endif /* NEW_COMPAT */
+
+extern char *re_comp(), *re_conv();
+static Code_t add_subscriptions __P((Client *who, Destlist *subs_queue,
+ ZNotice_t *notice));
+static Destlist *extract_subscriptions __P((ZNotice_t *notice));
+static void free_subscriptions __P((Destlist *subs));
+static char **subscr_marshal_subs __P((ZNotice_t *notice, int auth,
+ struct sockaddr_in *who,
+ int *found));
+static Destlist *subscr_copy_def_subs __P((char *person));
+static Code_t subscr_subscribe_realms __P((struct sockaddr_in *who,
+ Destlist *newsubs,
+ ZNotice_t *notice));
+static Code_t subscr_realm_sendit __P((Client *who, Destlist *subs,
+ ZNotice_t *notice, Realm *realm));
+static void subscr_unsub_realms __P((Destlist *newsubs));
+static void subscr_unsub_sendit __P((Destlist *subs, Realm *realm));
+static int cl_match __P((Destlist*, Client *));
+
+static int defaults_read = 0; /* set to 1 if the default subs
+ are in memory */
+static ZNotice_t default_notice; /* contains default subscriptions */
+
+String *wildcard_instance;
+String *empty;
+
+/* WARNING: make sure this is the same as the number of strings you */
+/* plan to hand back to the user in response to a subscription check, */
+/* else you will lose. See subscr_sendlist() */
+#define NUM_FIELDS 3
+
+/*
+ * subscribe the client to types described in notice.
+ */
+
+Code_t
+subscr_subscribe(who, notice)
+ Client *who;
+ ZNotice_t *notice;
+{
+ Destlist *subs;
+
+ subs = extract_subscriptions(notice);
+ return add_subscriptions(who, subs, notice);
+}
+
+static Code_t
+add_subscriptions(who, subs, notice)
+ Client *who;
+ Destlist *subs;
+ ZNotice_t *notice;
+{
+ Destlist *next;
+ Code_t retval;
+ Acl *acl;
+ String *sender;
+ Realm *realm = NULL;
+
+ if (!subs)
+ return ZERR_NONE; /* no subscr -> no error */
+
+ sender = make_string(notice->z_sender, 0);
+
+ /* Loop over the new subscriptions. */
+ for (; subs; subs = next) {
+ next = subs->next;
+#if 0
+ zdbug ((LOG_DEBUG, "subscr: %s/%s/%s", subs->dest.classname->string,
+ subs->dest.inst->string, subs->dest.recip->string));
+#endif
+ if (!bdumping) {
+ if (subs->dest.recip != empty && subs->dest.recip != sender
+ && subs->dest.recip->string[0] != '@') {
+ syslog(LOG_WARNING, "subscr unauth %s recipient %s",
+ sender->string, subs->dest.recip->string);
+ continue;
+ }
+ acl = class_get_acl(subs->dest.classname);
+ if (acl) {
+ if (!access_check(sender->string, acl, SUBSCRIBE)) {
+ syslog(LOG_WARNING, "subscr unauth %s class %s",
+ sender->string, subs->dest.classname->string);
+ continue; /* the for loop */
+ }
+ if (wildcard_instance == subs->dest.inst) {
+ if (!access_check(sender->string, acl, INSTWILD)) {
+ syslog(LOG_WARNING,
+ "subscr unauth %s class %s wild inst",
+ sender->string, subs->dest.classname->string);
+ continue;
+ }
+ }
+ }
+ }
+ /* check the recipient for a realm which isn't ours */
+ realm = NULL;
+ if (subs->dest.recip->string[0] == '@' &&
+ strcmp((subs->dest.recip->string + 1), ZGetRealm()) != 0)
+ realm = realm_get_realm_by_name(subs->dest.recip->string + 1);
+ if (realm) {
+ retval = subscr_realm_sendit(who, subs, notice, realm);
+ if (retval != ZERR_NONE) {
+ free(subs);
+ return(retval);
+ }
+ }
+ retval = triplet_register(who, &subs->dest, realm);
+ if (retval != ZERR_NONE) {
+ free(subs);
+ if (retval == ZSRV_CLASSXISTS) {
+ continue;
+ } else {
+ free_subscriptions(next);
+ return retval;
+ }
+ }
+ LIST_INSERT(&who->subs, subs);
+ }
+
+ return ZERR_NONE;
+}
+
+/*
+ * add default subscriptions to the client's subscription chain.
+ */
+
+Code_t
+subscr_def_subs(who)
+ Client *who;
+{
+ Destlist *subs;
+
+ subs = subscr_copy_def_subs(who->principal->string);
+ return add_subscriptions(who, subs, &default_notice);
+}
+
+void
+subscr_reset()
+{
+#if 0
+ zdbug((LOG_DEBUG, "subscr_reset()"));
+#endif
+ free(default_notice.z_message);
+ default_notice.z_message = NULL;
+ defaults_read = 0;
+}
+
+static Destlist *
+subscr_copy_def_subs(person)
+ char *person;
+{
+ int retval, fd;
+ struct stat statbuf;
+ char *def_sub_area, *cp;
+ Destlist *subs, *sub;
+
+ if (!defaults_read) {
+#if 0
+ zdbug((LOG_DEBUG, "reading default subscription file"));
+#endif
+ fd = open(subs_file, O_RDONLY, 0666);
+ if (fd < 0) {
+ syslog(LOG_ERR, "can't open %s:%m", subs_file);
+ return NULL;
+ }
+ retval = fstat(fd, &statbuf);
+ if (retval < 0) {
+ syslog(LOG_ERR, "fstat failure on %s:%m", subs_file);
+ close(fd);
+ return NULL;
+ }
+ def_sub_area = (char *) malloc(statbuf.st_size + 1);
+ if (!def_sub_area) {
+ syslog(LOG_ERR, "no mem copy_def_subs");
+ close(fd);
+ return NULL;
+ }
+ retval = read(fd, def_sub_area, (size_t) statbuf.st_size);
+ if (retval != statbuf.st_size) {
+ syslog(LOG_ERR, "short read in copy_def_subs");
+ close(fd);
+ return NULL;
+ }
+
+ close(fd);
+ def_sub_area[statbuf.st_size] = '\0'; /* null-terminate it */
+
+ /*
+ def_subs_area now points to a buffer full of subscription info.
+ Each line of the stuff is of the form:
+ class,inst,recipient
+
+ Commas and newlines may not appear as part of the class,
+ instance, or recipient. XXX!
+ */
+
+ /* split up the subscription info */
+ for (cp = def_sub_area; cp < def_sub_area + statbuf.st_size; cp++) {
+ if (*cp == '\n' || *cp == ',')
+ *cp = '\0';
+ }
+ default_notice.z_message = def_sub_area;
+ default_notice.z_message_len = statbuf.st_size + 1;
+ default_notice.z_auth = 1;
+ defaults_read = 1;
+ }
+
+ /* needed later for access_check() */
+ default_notice.z_sender = person;
+ subs = extract_subscriptions(&default_notice);
+ /* replace any non-* recipients with "person" */
+
+ for (sub = subs; sub; sub = sub->next) {
+ /* if not a wildcard, replace it with person */
+ if (strcmp(sub->dest.recip->string, "*")) {
+ free_string(sub->dest.recip);
+ sub->dest.recip = make_string(person, 0);
+ } else { /* replace with null recipient */
+ free_string(sub->dest.recip);
+ sub->dest.recip = dup_string(empty);
+ }
+ }
+ return subs;
+}
+
+/*
+ * Cancel a specific set of subscriptions.
+ */
+
+Code_t
+subscr_cancel(sin, notice)
+ struct sockaddr_in *sin;
+ ZNotice_t *notice;
+{
+ Realm *realm;
+ Client *who;
+ Destlist *cancel_subs, *subs, *cancel_next, *client_subs, *client_next;
+ Code_t retval;
+ int found = 0;
+ int relation;
+
+#if 0
+ zdbug((LOG_DEBUG,"subscr_cancel"));
+#endif
+ who = client_find(&sin->sin_addr, notice->z_port);
+ if (!who)
+ return ZSRV_NOCLT;
+
+ if (!who->subs)
+ return ZSRV_NOSUB;
+
+ cancel_subs = extract_subscriptions(notice);
+ if (!cancel_subs)
+ return ZERR_NONE; /* no subscr -> no error */
+
+ for (subs = cancel_subs; subs; subs = cancel_next) {
+ cancel_next = subs->next;
+ for (client_subs = who->subs; client_subs; client_subs = client_next) {
+ client_next = client_subs->next;
+ if (ZDest_eq(&client_subs->dest, &subs->dest)) {
+ LIST_DELETE(client_subs);
+ triplet_deregister(who, &client_subs->dest, NULL);
+ if (retval == ZSRV_EMPTYCLASS &&
+ client_subs->dest.recip->string[0] == '@') {
+ realm =
+ realm_get_realm_by_name(client_subs->dest.recip->string
+ + 1);
+ if (realm)
+ subscr_unsub_sendit(client_subs, realm);
+ realm = NULL;
+ }
+ free_string(client_subs->dest.classname);
+ free_string(client_subs->dest.inst);
+ free_string(client_subs->dest.recip);
+ free(client_subs);
+ found = 1;
+ break;
+ }
+ }
+ }
+
+ free_subscriptions(cancel_subs);
+
+ if (found) {
+#if 0
+ zdbug((LOG_DEBUG, "found & removed"));
+#endif
+ return ZERR_NONE;
+ } else {
+#if 0
+ zdbug((LOG_DEBUG, "not found"));
+#endif
+ return ZSRV_NOSUB;
+ }
+}
+
+Code_t
+subscr_realm_cancel(sin, notice, realm)
+ struct sockaddr_in *sin;
+ ZNotice_t *notice;
+ Realm *realm;
+{
+ Client *who;
+ Destlist *cancel_subs, *subs, *client_subs, *next, *next2;
+ Code_t retval;
+ int found = 0;
+
+ if (!realm)
+ return ZSRV_NORLM;
+
+ if (!realm->subs)
+ return ZSRV_NOSUB;
+
+ cancel_subs = extract_subscriptions(notice);
+ if (!cancel_subs)
+ return ZERR_NONE; /* no subscr -> no error */
+
+ for (subs = cancel_subs; subs; subs = next) {
+ next = subs->next;
+ for (client_subs = realm->subs; client_subs; client_subs = next2) {
+ next2 = client_subs->next;
+ if (ZDest_eq(&client_subs->dest, &subs->dest)) {
+ LIST_DELETE(client_subs);
+ retval = triplet_deregister(realm->client, &client_subs->dest, realm);
+ free_string(client_subs->dest.classname);
+ free_string(client_subs->dest.inst);
+ free_string(client_subs->dest.recip);
+ free(client_subs);
+ found = 1;
+ break;
+ }
+ }
+ }
+
+ free_subscriptions(cancel_subs);
+
+ if (found) {
+#if 0
+ zdbug((LOG_DEBUG, "found & removed"));
+#endif
+ return ZERR_NONE;
+ } else {
+#if 0
+ zdbug((LOG_DEBUG, "not found"));
+#endif
+ return ZSRV_NOSUB;
+ }
+}
+
+/*
+ * Cancel all the subscriptions for this client.
+ */
+
+void
+subscr_cancel_client(client)
+ Client *client;
+{
+ Destlist *subs, *next;
+ Code_t retval;
+ Realm *realm;
+
+#if 0
+ zdbug((LOG_DEBUG,"subscr_cancel_client %s",
+ inet_ntoa(client->addr.sin_addr)));
+#endif
+ if (!client->subs)
+ return;
+
+ for (subs = client->subs; subs; subs = next) {
+ next = subs->next;
+#if 0
+ zdbug((LOG_DEBUG,"sub_can %s", subs->dest.classname->string));
+#endif
+ retval = triplet_deregister(client, &subs->dest, NULL);
+ if (retval == ZSRV_EMPTYCLASS &&
+ subs->dest.recip->string[0] == '@') {
+ realm = realm_get_realm_by_name(subs->dest.recip->string + 1);
+ if (realm)
+ subscr_unsub_sendit(subs, realm);
+ realm = NULL;
+ }
+ free_string(subs->dest.classname);
+ free_string(subs->dest.inst);
+ free_string(subs->dest.recip);
+ free(subs);
+ }
+
+ client->subs = NULL;
+}
+
+/*
+ * Send the requester a list of his current subscriptions
+ */
+
+void
+subscr_sendlist(notice, auth, who)
+ ZNotice_t *notice;
+ int auth;
+ struct sockaddr_in *who;
+{
+ char **answer;
+ int found;
+ struct sockaddr_in send_to_who;
+ Code_t retval;
+
+#ifdef OLD_COMPAT
+ if (strcmp(notice->z_version, OLD_ZEPHYR_VERSION) == 0) {
+ /* we are talking to an old client; use the old-style
+ acknowledgement-message */
+ old_compat_subscr_sendlist(notice, auth, who);
+ return;
+ }
+#endif /* OLD_COMPAT */
+#ifdef NEW_COMPAT
+ if (strcmp(notice->z_version, NEW_OLD_ZEPHYR_VERSION) == 0) {
+ /* we are talking to a new old client; use the new-old-style
+ acknowledgement-message */
+ new_old_compat_subscr_sendlist(notice, auth, who);
+ return;
+ }
+#endif /* NEW_COMPAT */
+ answer = subscr_marshal_subs(notice, auth, who, &found);
+ send_to_who = *who;
+ send_to_who.sin_port = notice->z_port; /* Return port */
+
+ retval = ZSetDestAddr(&send_to_who);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_WARNING, "subscr_sendlist set addr: %s",
+ error_message(retval));
+ if (answer)
+ free(answer);
+ return;
+ }
+
+ /* XXX for now, don't do authentication */
+ auth = 0;
+
+ notice->z_kind = ACKED;
+
+ /* use xmit_frag() to send each piece of the notice */
+
+ retval = ZSrvSendRawList(notice, answer, found * NUM_FIELDS, xmit_frag);
+ if (retval != ZERR_NONE)
+ syslog(LOG_WARNING, "subscr_sendlist xmit: %s", error_message(retval));
+ if (answer)
+ free(answer);
+}
+
+static char **
+subscr_marshal_subs(notice, auth, who, found)
+ ZNotice_t *notice;
+ int auth;
+ struct sockaddr_in *who;
+ int *found;
+{
+ char **answer = NULL;
+ unsigned short temp;
+ Code_t retval;
+ Client *client;
+ Destlist *subs = NULL, *sub;
+ int i;
+ int defsubs = 0;
+
+#if 0
+ zdbug((LOG_DEBUG, "subscr_marshal"));
+#endif
+ *found = 0;
+
+ /* Note that the following code is an incredible crock! */
+
+ /* We cannot send multiple packets as acknowledgements to the client,
+ since the hostmanager will ignore the later packets. So we need
+ to send directly to the client. */
+
+ /* Make our own copy so we can send directly back to the client */
+ /* RSF 11/07/87 */
+
+ if (strcmp(notice->z_opcode, CLIENT_GIMMESUBS) == 0) {
+ /* If the client has requested his current subscriptions,
+ the message field of the notice contains the port number
+ of the client for which the sender desires the subscription
+ list. The port field is the port of the sender. */
+
+ retval = ZReadAscii16(notice->z_message, notice->z_message_len, &temp);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_WARNING, "subscr_marshal read port num: %s",
+ error_message(retval));
+ return(NULL);
+ }
+
+ client = client_find(&who->sin_addr, htons(temp));
+
+ if (client)
+ subs = client->subs;
+ } else if (strcmp(notice->z_opcode, CLIENT_GIMMEDEFS) == 0) {
+#if 0
+ zdbug((LOG_DEBUG, "gimmedefs"));
+#endif
+ /* subscr_copy_def_subs allocates new pointer rings, so
+ it must be freed when finished.
+ the string areas pointed to are static, however.*/
+ subs = subscr_copy_def_subs(notice->z_sender);
+ defsubs = 1;
+ } else {
+ syslog(LOG_ERR, "subscr_marshal bogus opcode %s",
+ notice->z_opcode);
+ return(NULL);
+ }
+
+ if (subs) {
+
+ /* check authenticity here. The user must be authentic to get
+ a list of subscriptions. If he is not subscribed to
+ anything, this if-clause fails, and he gets a response
+ indicating no subscriptions.
+ if retrieving default subscriptions, don't care about
+ authentication. */
+
+ if (!auth && !defsubs)
+ return(NULL);
+ if (!defsubs) {
+ if (client && (strcmp(client->principal->string,
+ notice->z_sender) != 0)) {
+ zdbug ((LOG_DEBUG,
+ "subscr_marshal: %s requests subs for %s at %s/%d",
+ notice->z_sender, client->principal->string,
+ inet_ntoa(who->sin_addr), ntohs(who->sin_port)));
+ return 0;
+ }
+ }
+
+ for (sub = subs; sub; sub = sub->next)
+ (*found)++;
+
+ /* found is now the number of subscriptions */
+
+ /* coalesce the subscription information into a list of char *'s */
+ answer = (char **) malloc((*found) * NUM_FIELDS * sizeof(char *));
+ if (answer == NULL) {
+ syslog(LOG_ERR, "subscr no mem(answer)");
+ *found = 0;
+ } else {
+ i = 0;
+ for (sub = subs; sub; sub = sub->next) {
+ answer[i * NUM_FIELDS] = sub->dest.classname->string;
+ answer[i * NUM_FIELDS + 1] = sub->dest.inst->string;
+ answer[i * NUM_FIELDS + 2] = sub->dest.recip->string;
+ i++;
+ }
+ }
+ }
+ if (defsubs)
+ free_subscriptions(subs);
+ return answer;
+}
+
+#ifdef NEW_COMPAT
+static void
+new_old_compat_subscr_sendlist(notice, auth, who)
+ ZNotice_t *notice;
+ int auth;
+ struct sockaddr_in *who;
+{
+ Code_t retval;
+ ZNotice_t reply;
+ ZPacket_t reppacket;
+ int packlen, found, count, initfound, zerofound;
+ char buf[64];
+ const char **answer;
+ struct sockaddr_in send_to_who;
+ int i;
+
+ new_compat_count_subscr++;
+
+ syslog(LOG_INFO, "new old subscr, %s", inet_ntoa(who->sin_addr));
+ reply = *notice;
+ reply.z_kind = SERVACK;
+ reply.z_authent_len = 0; /* save some space */
+ reply.z_auth = 0;
+
+ send_to_who = *who;
+ send_to_who.sin_port = notice->z_port; /* Return port */
+
+ retval = ZSetDestAddr(&send_to_who);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_WARNING, "new_old_subscr_sendlist set addr: %s",
+ error_message(retval));
+ return;
+ }
+
+ /* retrieve the subscriptions */
+ answer = subscr_marshal_subs(notice, auth, who, &found);
+
+ /* note that when there are no subscriptions, found == 0, so
+ we needn't worry about answer being NULL since
+ ZFormatSmallRawNoticeList won't reference the pointer */
+
+ /* send 5 at a time until we are finished */
+ count = found?((found-1) / 5 + 1):1; /* total # to be sent */
+ i = 0; /* pkt # counter */
+#if 0
+ zdbug((LOG_DEBUG,"Found %d subscriptions for %d packets", found, count));
+#endif
+ initfound = found;
+ zerofound = (found == 0);
+ while (found > 0 || zerofound) {
+ packlen = sizeof(reppacket);
+ sprintf(buf, "%d/%d", ++i, count);
+ reply.z_opcode = buf;
+ retval = ZFormatSmallRawNoticeList(&reply,
+ answer + (initfound - found)
+ * NUM_FIELDS,
+ ((found > 5) ? 5 : found)
+ * NUM_FIELDS,
+ reppacket, &packlen);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_ERR, "subscr_sendlist format: %s",
+ error_message(retval));
+ if (answer)
+ free(answer);
+ return;
+ }
+ retval = ZSendPacket(reppacket, packlen, 0);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_WARNING, "subscr_sendlist xmit: %s",
+ error_message(retval));
+ if (answer)
+ free(answer);
+ return;
+ }
+ found -= 5;
+ zerofound = 0;
+ }
+#if 0
+ zdbug((LOG_DEBUG,"subscr_sendlist acked"));
+#endif
+ if (answer)
+ free(answer);
+}
+#endif /* NEW_COMPAT */
+
+#ifdef OLD_COMPAT
+static void
+old_compat_subscr_sendlist(notice, auth, who)
+ ZNotice_t *notice;
+ int auth;
+ struct sockaddr_in *who;
+{
+ Client *client = client_find(&who->sin_addr, notice->z_port);
+ Destlist *subs;
+ Code_t retval;
+ ZNotice_t reply;
+ ZPacket_t reppacket;
+ int packlen, i, found = 0;
+ char **answer = NULL;
+
+ old_compat_count_subscr++;
+
+ syslog(LOG_INFO, "old old subscr, %s", inet_ntoa(who->sin_addr));
+ if (client && client->subs) {
+
+ /* check authenticity here. The user must be authentic to get
+ a list of subscriptions. If he is not subscribed to
+ anything, the above test fails, and he gets a response
+ indicating no subscriptions */
+
+ if (!auth) {
+ clt_ack(notice, who, AUTH_FAILED);
+ return;
+ }
+
+ for (subs = client->subs; subs; subs = subs->next)
+ found++;
+ /* found is now the number of subscriptions */
+
+ /* coalesce the subscription information into a list of char *'s */
+ answer = (char **) malloc(found * NUM_FIELDS * sizeof(char *));
+ if (!answer) {
+ syslog(LOG_ERR, "old_subscr_sendlist no mem(answer)");
+ found = 0;
+ } else {
+ i = 0;
+ for (subs = client->subs; subs; subs = subs->next) {
+ answer[i*NUM_FIELDS] = subs->dest.classname->string;
+ answer[i*NUM_FIELDS + 1] = subs->dest.inst->string;
+ answer[i*NUM_FIELDS + 2] = subs->dest.recip->string;
+ i++;
+ }
+ }
+ }
+
+ /* note that when there are no subscriptions, found == 0, so
+ we needn't worry about answer being NULL */
+
+ reply = *notice;
+ reply.z_kind = SERVACK;
+ reply.z_authent_len = 0; /* save some space */
+ reply.z_auth = 0;
+
+ /* if it's too long, chop off one at a time till it fits */
+ while ((retval = ZFormatSmallRawNoticeList(&reply, answer,
+ found * NUM_FIELDS,
+ reppacket,
+ &packlen)) != ZERR_PKTLEN) {
+ found--;
+ reply.z_opcode = OLD_CLIENT_INCOMPSUBS;
+ }
+ if (retval != ZERR_NONE) {
+ syslog(LOG_ERR, "old_subscr_sendlist format: %s",
+ error_message(retval));
+ if (answer)
+ free(answer);
+ return;
+ }
+ retval = ZSetDestAddr(who);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_WARNING, "subscr_sendlist set addr: %s",
+ error_message(retval));
+ if (answer)
+ free(answer);
+ return;
+ }
+ retval = ZSendPacket(reppacket, packlen, 0);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_WARNING, "subscr_sendlist xmit: %s",
+ error_message(retval));
+ if (answer)
+ free(answer);
+ return;
+ }
+#if 0
+ zdbug((LOG_DEBUG,"subscr_sendlist acked"));
+#endif
+ if (answer)
+ free(answer);
+}
+#endif /* OLD_COMPAT */
+
+/*
+ * Send the client's subscriptions to another server
+ */
+
+/* version is currently unused; if necessary later versions may key off it
+ to determine what to send to the peer (protocol changes) */
+
+/*ARGSUSED*/
+Code_t
+subscr_send_subs(client)
+ Client *client;
+{
+ int i = 0;
+ Destlist *subs;
+#ifdef HAVE_KRB4
+ char buf[512];
+ C_Block cblock;
+#endif /* HAVE_KRB4 */
+ char buf2[512];
+ char *list[7 * NUM_FIELDS];
+ int num = 0;
+ Code_t retval;
+
+#if 0
+ zdbug((LOG_DEBUG, "send_subs"));
+#endif
+ sprintf(buf2, "%d",ntohs(client->addr.sin_port));
+
+ list[num++] = buf2;
+
+#ifdef HAVE_KRB4
+#ifdef NOENCRYPTION
+ memcpy(cblock, client->session_key, sizeof(C_Block));
+#else
+ des_ecb_encrypt(client->session_key, cblock, serv_ksched.s, DES_ENCRYPT);
+#endif
+
+ retval = ZMakeAscii(buf, sizeof(buf), cblock, sizeof(C_Block));
+ if (retval != ZERR_NONE) {
+#if 0
+ zdbug((LOG_DEBUG,"zmakeascii failed: %s", error_message(retval)));
+#endif
+ } else {
+ list[num++] = buf;
+#if 0
+ zdbug((LOG_DEBUG, "cblock %s", buf));
+#endif
+ }
+#endif /* HAVE_KRB4 */
+ retval = bdump_send_list_tcp(SERVACK, &client->addr, ZEPHYR_ADMIN_CLASS,
+ num > 1 ? "CBLOCK" : "", ADMIN_NEWCLT,
+ client->principal->string, "", list, num);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_ERR, "subscr_send_subs newclt: %s", error_message(retval));
+ return retval;
+ }
+
+ if (!client->subs)
+ return ZERR_NONE;
+
+ for (subs = client->subs; subs; subs = subs->next) {
+ /* for each subscription */
+ list[i * NUM_FIELDS] = subs->dest.classname->string;
+ list[i * NUM_FIELDS + 1] = subs->dest.inst->string;
+ list[i * NUM_FIELDS + 2] = subs->dest.recip->string;
+ i++;
+ if (i >= 7) {
+ /* we only put 7 in each packet, so we don't run out of room */
+ retval = bdump_send_list_tcp(ACKED, &client->addr,
+ ZEPHYR_CTL_CLASS, "",
+ CLIENT_SUBSCRIBE, "", "", list,
+ i * NUM_FIELDS);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_ERR, "subscr_send_subs subs: %s",
+ error_message(retval));
+ return retval;
+ }
+ i = 0;
+ }
+ }
+ if (i) {
+ retval = bdump_send_list_tcp(ACKED, &client->addr, ZEPHYR_CTL_CLASS,
+ "", CLIENT_SUBSCRIBE, "", "", list,
+ i * NUM_FIELDS);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_ERR, "subscr_send_subs subs: %s",
+ error_message(retval));
+ return retval;
+ }
+ }
+
+ return ZERR_NONE;
+}
+
+/*
+ * free the memory allocated for the list of subscriptions.
+ */
+
+static void
+free_subscriptions(subs)
+ Destlist *subs;
+{
+ Destlist *next;
+
+ for (; subs; subs = next) {
+ next = subs->next;
+ free_string(subs->dest.classname);
+ free_string(subs->dest.inst);
+ free_string(subs->dest.recip);
+ free(subs);
+ }
+}
+
+#define ADVANCE(xx) { cp += (strlen(cp) + 1); \
+ if (cp >= notice->z_message + notice->z_message_len) { \
+ syslog(LOG_WARNING, "malformed subscription %d", \
+ xx); \
+ return subs; \
+ }}
+
+/*
+ * Parse the message body, returning a linked list of subscriptions, or
+ * NULL if there are no subscriptions there.
+ */
+
+static Destlist *
+extract_subscriptions(notice)
+ ZNotice_t *notice;
+{
+ Destlist *subs = NULL, *sub;
+ char *recip, *class_name, *classinst;
+ char *cp = notice->z_message;
+
+ /* parse the data area for the subscriptions */
+ while (cp < notice->z_message + notice->z_message_len) {
+ class_name = cp;
+ if (*cp == '\0') /* we've exhausted the subscriptions */
+ return(subs);
+ ADVANCE(1);
+ classinst = cp;
+ ADVANCE(2);
+ recip = cp;
+#if 0
+ zdbug((LOG_DEBUG, "ext_sub: CLS %s INST %s RCPT %s",
+ class_name, classinst, cp));
+#endif
+ cp += (strlen(cp) + 1);
+ if (cp > notice->z_message + notice->z_message_len) {
+ syslog(LOG_WARNING, "malformed sub 3");
+ return subs;
+ }
+ sub = (Destlist *) malloc(sizeof(Destlist));
+ if (!sub) {
+ syslog(LOG_WARNING, "ex_subs: no mem 2");
+ return subs;
+ }
+ sub->dest.classname = make_string(class_name, 1);
+ sub->dest.inst = make_string(classinst, 1);
+ /* Nuke @REALM if REALM is us. */
+ if (recip[0] == '@' && !strcmp(recip + 1, ZGetRealm()))
+ sub->dest.recip = make_string("", 0);
+ else
+ sub->dest.recip = make_string(recip, 0);
+ LIST_INSERT(&subs, sub);
+ }
+ return subs;
+}
+
+/*
+ * print subscriptions in subs onto fp.
+ * assumed to be called with SIGFPE blocked
+ * (true if called from signal handler)
+ */
+
+void
+subscr_dump_subs(fp, subs)
+ FILE *fp;
+ Destlist *subs;
+{
+ char *p;
+
+ if (!subs) /* no subscriptions to dump */
+ return;
+
+ for (; subs; subs = subs->next) {
+ fputs("\t'", fp);
+ dump_quote(subs->dest.classname->string, fp);
+ fputs("' '", fp);
+ dump_quote(subs->dest.inst->string, fp);
+ fputs("' '", fp);
+ dump_quote(subs->dest.recip->string, fp);
+ fputs("'\n", fp);
+ }
+}
+
+#define I_ADVANCE(xx) { cp += (strlen(cp) + 1); \
+ if (cp >= notice->z_message + notice->z_message_len) { \
+ syslog(LOG_WARNING, "malformed subscription %d", \
+ xx); \
+ return (ZERR_NONE); \
+ }}
+
+/* As it exists, this function expects to take only the first sub from the
+ * Destlist. At some point, it and the calling code should be replaced */
+static Code_t
+subscr_realm_sendit(who, subs, notice, realm)
+ Client *who;
+ Destlist *subs;
+ ZNotice_t *notice;
+ Realm *realm;
+{
+#if 0
+ Destlist *subs2;
+#endif
+ ZNotice_t snotice;
+ char *pack;
+ int packlen;
+ int found = 0, i;
+ char **text;
+ Code_t retval;
+ char addr[16]; /* xxx.xxx.xxx.xxx max */
+ char port[16];
+
+#if 0
+ zdbug((LOG_DEBUG, "subscr_rlm_sendit"));
+#endif
+
+
+#ifdef notdef
+ for (subs2 = subs; subs2; subs2 = subs2->next, found++);
+ /* found is now the number of subscriptions */
+
+ /* coalesce the subscription information into a list of char *'s */
+ /* one extra for client information */
+ if ((text = (char **) malloc((found * NUM_FIELDS + 2)
+ * sizeof(char *))) == (char **) 0)
+ {
+ syslog(LOG_ERR, "subscr_rlm_sendit malloc");
+ return(ENOMEM);
+ }
+#endif /* notdef */
+
+ if ((text=(char **)malloc((NUM_FIELDS + 2)*sizeof(char *))) == (char **)0) {
+ syslog(LOG_ERR, "subscr_rlm_sendit malloc");
+ return(ENOMEM);
+ }
+ /* convert the address to a string of the form x.x.x.x/port */
+ strcpy(addr, inet_ntoa(notice->z_sender_addr));
+ if ((retval = ZMakeAscii(port, sizeof(port), (unsigned char *)
+ ¬ice->z_port, sizeof(u_short))) != ZERR_NONE)
+ {
+ syslog(LOG_ERR, "subscr_rlm_sendit make ascii: %s",
+ error_message(retval));
+ return(ZERR_NONE);
+ }
+ text[0] = addr;
+ text[1] = port;
+
+#ifdef notdef
+ for (i = 0, subs2 = subs; subs2, i < found ; i++, subs2 = subs2->next) {
+ text[i*NUM_FIELDS + 2] = subs2->dest.classname->string;
+ text[i*NUM_FIELDS + 3] = subs2->dest.inst->string;
+ text[i*NUM_FIELDS + 4] = subs2->dest.recip->string;
+ }
+#endif /* notdef */
+
+ text[2] = subs->dest.classname->string;
+ text[3] = subs->dest.inst->string;
+ text[4] = subs->dest.recip->string;
+
+ /* format snotice */
+ snotice.z_class_inst = ZEPHYR_CTL_REALM;
+ snotice.z_opcode = REALM_REQ_SUBSCRIBE;
+ snotice.z_port = srv_addr.sin_port;
+
+ snotice.z_class = ZEPHYR_CTL_CLASS;
+
+ snotice.z_recipient = "";
+ snotice.z_kind = ACKED;
+ snotice.z_num_other_fields = 0;
+ snotice.z_default_format = "";
+ snotice.z_sender = notice->z_sender;
+ snotice.z_recipient = notice->z_recipient;
+ snotice.z_default_format = notice->z_default_format;
+
+#ifdef notdef
+ if ((retval = ZFormatNoticeList(&snotice, text, found * NUM_FIELDS + 2,
+ &pack, &packlen, ZNOAUTH)) != ZERR_NONE)
+#else
+ if ((retval = ZFormatNoticeList(&snotice, text, NUM_FIELDS + 2,
+ &pack, &packlen, ZNOAUTH)) != ZERR_NONE)
+#endif
+ {
+ syslog(LOG_WARNING, "subscr_rlm_sendit format: %s",
+ error_message(retval));
+ free(text);
+ return(ZERR_NONE);
+ }
+ free(text);
+
+ if ((retval = ZParseNotice(pack, packlen, &snotice)) != ZERR_NONE) {
+ syslog(LOG_WARNING, "subscr_rlm_sendit parse: %s",
+ error_message(retval));
+ free(pack);
+ return(ZERR_NONE);
+ }
+
+#if 0
+ zdbug((LOG_DEBUG,"subscr_rlm_sendit len: %d", snotice.z_message_len));
+#endif
+ realm_handoff(&snotice, 1, &(who->addr), realm, 0);
+ free(pack);
+
+ return(ZERR_NONE);
+}
+
+static Code_t
+subscr_add_raw(client, realm, newsubs)
+ Client *client;
+ Realm *realm;
+ Destlist *newsubs;
+{
+ Destlist *subs, *subs2, *subs3, **head;
+ Code_t retval;
+
+#if 0
+ zdbug((LOG_DEBUG, "subscr_add_raw"));
+#endif
+ head = (realm) ? &realm->subs : &client->subs;
+
+ /* Loop over the new subscriptions. */
+ for (subs = newsubs; subs; subs = subs2) {
+ subs2 = subs->next;
+#ifdef DEBUG
+ zdbug((LOG_DEBUG,"subscr_add_raw: %s/%s/%s", subs->dest.classname->string, subs->dest.inst->string, subs->dest.recip->string));
+ if (realm)
+ zdbug((LOG_DEBUG,"subscr_add_raw: realm is %s", realm->name));
+#endif
+ retval = triplet_register(client, &subs->dest, realm);
+ if (retval != ZERR_NONE) {
+ free(subs);
+ if (retval == ZSRV_CLASSXISTS) {
+ continue;
+ } else {
+ free_subscriptions(subs2);
+ return retval;
+ }
+ }
+ LIST_INSERT(head, subs);
+ }
+ return ZERR_NONE;
+}
+
+Code_t
+subscr_realm(realm, notice)
+ Realm *realm;
+ ZNotice_t *notice;
+{
+ Destlist *newsubs;
+
+ newsubs = extract_subscriptions(notice);
+
+ if (!newsubs) {
+ syslog(LOG_WARNING, "empty subs in subscr_realm");
+ return(ZERR_NONE);
+ }
+
+ return(subscr_add_raw(realm->client, realm, newsubs));
+}
+
+/* Like realm_sendit, this only takes one item from subs */
+static void
+subscr_unsub_sendit(subs, realm)
+ Destlist *subs;
+ Realm *realm;
+{
+ ZNotice_t unotice;
+ Code_t retval;
+#ifdef notdef
+ char *list[7 * NUM_FIELDS];
+#else /* notdef */
+ char *list[NUM_FIELDS];
+#endif /* notdef */
+ char *pack;
+ int packlen;
+ int found = 0;
+ Destlist *subs2;
+
+ unotice.z_class = ZEPHYR_CTL_CLASS;
+ unotice.z_class_inst = ZEPHYR_CTL_REALM;
+ unotice.z_opcode = REALM_UNSUBSCRIBE;
+ unotice.z_recipient = "";
+ unotice.z_kind = ACKED;
+
+ unotice.z_sender = "";
+ unotice.z_port = srv_addr.sin_port;
+ unotice.z_num_other_fields = 0;
+ unotice.z_default_format = "";
+
+#ifdef notdef
+ found = 0;
+ for (subs2 = subs; subs2; subs2 = subs2->next) {
+ list[found * NUM_FIELDS] = subs2->dest.classname->string;
+ list[found * NUM_FIELDS + 1] = subs2->dest.inst->string;
+ list[found * NUM_FIELDS + 2] = "";
+
+ found++;
+
+ if (found >= 7) {
+ if ((retval = ZFormatNoticeList(&unotice, list, found * NUM_FIELDS, &pack, &packlen, ZNOAUTH)) != ZERR_NONE) {
+ syslog(LOG_WARNING, "subscr_unsub_sendit format: %s",
+ error_message(retval));
+ return;
+ }
+ if ((retval = ZParseNotice(pack, packlen, &unotice)) != ZERR_NONE) {
+ syslog(LOG_WARNING, "subscr_unsub_sendit parse: %s",
+ error_message(retval));
+ free(pack);
+ return;
+ }
+ free(pack);
+ realm_handoff(&unotice, 1, (struct sockaddr_in *) 0, realm, 0);
+ found = 0;
+ }
+ }
+
+ if (found == 0)
+ return;
+
+ if ((retval = ZFormatNoticeList(&unotice, list, found * NUM_FIELDS, &pack, &packlen, ZNOAUTH)) != ZERR_NONE) {
+ syslog(LOG_WARNING, "subscr_unsub_sendit format: %s",
+ error_message(retval));
+ return;
+ }
+#else /* notdef */
+ list[0] = subs->dest.classname->string;
+ list[1] = subs->dest.inst->string;
+ list[2] = "";
+
+ if ((retval = ZFormatNoticeList(&unotice, list, NUM_FIELDS, &pack, &packlen, ZNOAUTH)) != ZERR_NONE) {
+ syslog(LOG_WARNING, "subscr_unsub_sendit format: %s",
+ error_message(retval));
+ return;
+ }
+#endif /* notdef */
+
+ if ((retval = ZParseNotice(pack, packlen, &unotice)) != ZERR_NONE) {
+ syslog(LOG_WARNING, "subscr_unsub_sendit parse: %s",
+ error_message(retval));
+ free(pack);
+ return;
+ }
+ free(pack);
+ realm_handoff(&unotice, 1, (struct sockaddr_in *) 0, realm, 0);
+}
+
+Code_t
+subscr_send_realm_subs(realm)
+ Realm *realm;
+{
+ int i = 0;
+ Destlist *subs, *next;
+ char buf[512];
+ char *list[7 * NUM_FIELDS];
+ int num = 0;
+ Code_t retval;
+
+#if 0
+ zdbug((LOG_DEBUG, "send_realm_subs"));
+#endif
+
+ strcpy(buf, realm->name);
+ list[num++] = buf;
+
+ retval = bdump_send_list_tcp(SERVACK, &srv_addr, ZEPHYR_ADMIN_CLASS,
+ "", ADMIN_NEWREALM, "", "", list, num);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_ERR, "subscr_send_realm_subs newclt: %s", error_message(retval));
+ return retval;
+ }
+
+ if (!realm->subs)
+ return ZERR_NONE;
+
+ for (subs=realm->subs; subs; subs = next) {
+ next = subs->next;
+#ifdef DEBUG
+ zdbug ((LOG_DEBUG, "send_realm_subs: %s/%s/%s", subs->dest.classname->string,
+ subs->dest.inst->string, subs->dest.recip->string));
+#endif
+ /* for each subscription */
+ list[i * NUM_FIELDS] = subs->dest.classname->string;
+ list[i * NUM_FIELDS + 1] = subs->dest.inst->string;
+ list[i * NUM_FIELDS + 2] = subs->dest.recip->string;
+ i++;
+ if (i >= 7) {
+ /* we only put 7 in each packet, so we don't run out of room */
+ retval = bdump_send_list_tcp(ACKED, &srv_addr,
+ ZEPHYR_CTL_CLASS, "",
+ REALM_SUBSCRIBE, "", "", list,
+ i * NUM_FIELDS);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_ERR, "subscr_send_realm_subs subs: %s",
+ error_message(retval));
+ return retval;
+ }
+ i = 0;
+ }
+ }
+ if (i) {
+ retval = bdump_send_list_tcp(ACKED, &srv_addr, ZEPHYR_CTL_CLASS,
+ "", REALM_SUBSCRIBE, "", "", list,
+ i * NUM_FIELDS);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_ERR, "subscr_send_realm_subs subs: %s",
+ error_message(retval));
+ return retval;
+ }
+ }
+
+ return ZERR_NONE;
+}
+
+static Code_t
+subscr_check_foreign_subs(notice, who, realm, newsubs)
+ ZNotice_t *notice;
+ struct sockaddr_in *who;
+ Realm *realm;
+ Destlist *newsubs;
+{
+ Destlist *subs, *subs2, *next;
+ Acl *acl;
+ char **text;
+ int found = 0;
+ ZNotice_t snotice;
+ char *pack, *cp;
+ int packlen;
+ Code_t retval;
+ String *sender;
+
+ for (subs = newsubs; subs; subs = subs->next)
+ found++;
+
+ if (found == 0)
+ return(ZERR_NONE);
+
+ sender = make_string(notice->z_sender, 0);
+
+ if ((text = (char **)malloc((found * NUM_FIELDS + 2) * sizeof(char *))) == (char **) 0) {
+ syslog(LOG_ERR, "subscr_ck_forn_subs no mem(text)");
+ return(ENOMEM);
+ }
+
+ /* grab the client information from the incoming message */
+ cp = notice->z_message;
+ text[0] = cp;
+
+ I_ADVANCE(2);
+ text[1] = cp;
+
+ I_ADVANCE(3);
+
+ found = 0;
+ for (subs = newsubs; subs; subs = next) {
+ next=subs->next;
+ acl = class_get_acl(subs->dest.classname);
+ if (acl) {
+ if (!access_check(sender->string, acl, SUBSCRIBE)) {
+ syslog(LOG_WARNING, "subscr unauth %s class %s",
+ sender->string, subs->dest.classname->string);
+ continue; /* the for loop */
+ }
+ if (wildcard_instance == subs->dest.inst) {
+ if (!access_check(sender->string, acl, INSTWILD)) {
+ syslog(LOG_WARNING,
+ "subscr unauth %s class %s wild inst",
+ sender->string, subs->dest.classname->string);
+ continue;
+ }
+ }
+ }
+
+ /* okay to subscribe. save for return trip */
+ text[found*NUM_FIELDS + 2] = subs->dest.classname->string;
+ text[found*NUM_FIELDS + 3] = subs->dest.inst->string;
+ text[found*NUM_FIELDS + 4] = "";
+ found++;
+
+ retval = triplet_register(realm->client, &subs->dest, realm);
+#ifdef DEBUG
+ zdbug ((LOG_DEBUG, "ck_frn_subs: %s/%s/%s", subs->dest.classname->string,
+ subs->dest.inst->string, subs->dest.recip->string));
+#endif
+
+ if (retval != ZERR_NONE) {
+ if (retval == ZSRV_CLASSXISTS) {
+ continue;
+ } else {
+ free_subscriptions(subs->next);
+ free(text);
+ return retval;
+ }
+ }
+ LIST_INSERT(&realm->subs, subs);
+ }
+ /* don't send confirmation if we're not the initial server contacted */
+ if (!(server_which_server(who) || found == 0)) {
+ snotice = *notice;
+ snotice.z_opcode = REALM_ADD_SUBSCRIBE;
+ snotice.z_class_inst = ZEPHYR_CTL_REALM;
+ snotice.z_port = srv_addr.sin_port;
+ if ((retval = ZFormatNoticeList(&snotice, text, found * NUM_FIELDS + 2, &pack, &packlen, ZNOAUTH)) != ZERR_NONE) {
+ syslog(LOG_WARNING, "subscr_ck_forn_subs format: %s",
+ error_message(retval));
+ free(text);
+ return(ZERR_NONE);
+ }
+ if ((retval = ZParseNotice(pack, packlen, &snotice)) != ZERR_NONE) {
+ syslog(LOG_WARNING, "subscr_ck_forn_subs parse: %s",
+ error_message(retval));
+ free(text);
+ free(pack);
+ return(ZERR_NONE);
+ }
+ realm_handoff(&snotice, 1, who, realm, 0);
+ free(pack);
+ }
+ free(text);
+ return ZERR_NONE;
+}
+
+Code_t subscr_foreign_user(notice, who, realm)
+ ZNotice_t *notice;
+ struct sockaddr_in *who;
+ Realm *realm;
+{
+ Destlist *newsubs, *temp;
+ Acl *acl;
+ Code_t status;
+ Client *client;
+ ZNotice_t snotice;
+ struct sockaddr_in newwho;
+ char *cp;
+ char rlm_recipient[REALM_SZ + 1];
+
+#if 0
+ zdbug((LOG_DEBUG, "subscr_foreign_user"));
+#endif
+
+ cp = notice->z_message;
+
+ newwho.sin_addr.s_addr = inet_addr(cp);
+ if (newwho.sin_addr.s_addr == -1) {
+ syslog(LOG_ERR, "malformed addr from %s, notice->z_sender");
+ return(ZERR_NONE);
+ }
+
+ I_ADVANCE(0);
+
+ snotice = *notice;
+
+ if ((status = ZReadAscii(cp, strlen(cp), (unsigned char *)&snotice.z_port, sizeof(u_short)))
+ != ZERR_NONE)
+ {
+ syslog(LOG_ERR, "subscr_foreign_user read ascii: %s",
+ error_message(status));
+ return(ZERR_NONE);
+ }
+
+ I_ADVANCE(1);
+
+ snotice.z_message = cp;
+ snotice.z_message_len = notice->z_message_len - (cp - notice->z_message);
+
+ newsubs = extract_subscriptions(&snotice);
+ if (!newsubs) {
+ syslog(LOG_WARNING, "empty subscr for %s", notice->z_sender);
+ return(ZERR_NONE);
+ }
+
+ if (!strcmp(snotice.z_opcode, REALM_ADD_SUBSCRIBE)) {
+ /* this was approved by the other realm, add subscriptions */
+
+ client = client_find(&newwho.sin_addr, snotice.z_port);
+ if (client == (Client *)0) {
+ syslog(LOG_WARNING, "no client at %s/%d",
+ inet_ntoa(newwho.sin_addr), ntohs(snotice.z_port));
+ free_subscriptions(newsubs);
+ return(ZERR_NONE);
+ }
+
+ /* translate the recipient to represent the foreign realm */
+ sprintf(rlm_recipient, "@%s", realm->name);
+ for (temp = newsubs; temp; temp = temp->next) {
+#if 0
+ syslog(LOG_DEBUG, "in foreign_user: class is %s", temp->dest.classname->string);
+#endif
+ temp->dest.recip = make_string(rlm_recipient, 0);
+ }
+
+ status = subscr_add_raw(client, (Realm *)0, newsubs);
+ } else if (!strcmp(snotice.z_opcode, REALM_REQ_SUBSCRIBE)) {
+ status = subscr_check_foreign_subs(notice, who, realm, newsubs);
+ } else {
+ syslog(LOG_ERR, "bogus opcode %s in subscr_forn_user",
+ snotice.z_opcode);
+ status = ZERR_NONE;
+ }
+ return(status);
+}
+
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains functions for managing multiple timeouts.
+ *
+ * Created by: John T. Kohl
+ * Derived from timer_manager_ by Ken Raeburn
+ *
+ * $Id: timer.c,v 1.18 1999/01/22 23:19:49 ghudson Exp $
+ *
+ */
+
+#include "zserver.h"
+
+#ifndef SABER
+#ifndef lint
+static const char rcsid[] =
+"$Id: timer.c,v 1.18 1999/01/22 23:19:49 ghudson Exp $";
+#endif /* lint */
+#endif /* SABER */
+
+/*
+ * timer_manager_ -- routines for handling timers in login_shell
+ * (and elsewhere)
+ *
+ * Copyright 1986 Student Information Processing Board,
+ * Massachusetts Institute of Technology
+ *
+ * written by Ken Raeburn
+
+ Permission to use, copy, modify, and distribute this
+ software and its documentation for any purpose and without
+ fee is hereby granted, provided that the above copyright
+ notice appear in all copies and that both that copyright
+ notice and this permission notice appear in supporting
+ documentation, and that the name of M.I.T. and the Student
+ Information Processing Board not be used in
+ advertising or publicity pertaining to distribution of the
+ software without specific, written prior permission.
+ M.I.T. and the Student Information Processing Board
+ make no representations about the suitability of
+ this software for any purpose. It is provided "as is"
+ without express or implied warranty.
+
+ */
+
+
+/*
+ * External functions:
+ *
+ * Timer *timer_set_rel (time_rel, proc, arg)
+ * long time_rel;
+ * void (*proc)();
+ * caddr_t arg;
+ * Timer *timer_set_abs (time_abs, proc, arg)
+ * long time_abs;
+ * void (*proc)();
+ * caddr_t arg;
+ *
+ * void timer_reset(tmr)
+ * Timer *tmr;
+ *
+ * void timer_process()
+ *
+ */
+
+/* DELTA is just an offset to keep the size a bit less than a power
+ * of two. It's measured in pointers, so it's 32 bytes on most
+ * systems. */
+#define DELTA 8
+#define INITIAL_HEAP_SIZE (1024 - DELTA)
+
+/* We have three operations which we need to be able to perform
+ * quickly: adding a timer, deleting a timer given a pointer to
+ * it, and determining which timer will be the next to go off. A
+ * heap is an ideal data structure for these purposes, so we use
+ * one. The heap is an array of pointers to timers, and each timer
+ * knows the position of its pointer in the heap.
+ *
+ * Okay, what is the heap, exactly? It's a data structure,
+ * represented as an array, with the invariant condition that
+ * the timeout of heap[i] is less than or equal to the timeout of
+ * heap[i * 2 + 1] and heap[i * 2 + 2] (assuming i * 2 + 1 and
+ * i * 2 + 2 are valid * indices). An obvious consequence of this
+ * is that heap[0] has the lowest timer value, so finding the first
+ * timer to go off is easy. We say that an index i has "children"
+ * i * 2 + 1 and i * 2 + 1, and the "parent" (i - 1) / 2.
+ *
+ * To add a timer to the heap, we start by adding it to the end, and
+ * then keep swapping it with its parent until it has a parent with
+ * a timer value less than its value. With a little bit of thought,
+ * you can see that this preserves the heap property on all indices
+ * of the array.
+ *
+ * To delete a timer at position i from the heap, we discard it and
+ * fill in its position with the last timer in the heap. In order
+ * to restore the heap, we have to consider two cases: the timer
+ * value at i is less than that of its parent, or the timer value at
+ * i is greater than that of one of its children. In the first case,
+ * we propagate the timer at i up the tree, swapping it with its
+ * parent, until the heap is restored; in the second case, we
+ * propagate the timer down the tree, swapping it with its least
+ * child, until the heap is restored. */
+
+/* In order to ensure that the back pointers from timers are consistent
+ * with the heap pointers, all heap assignments should be done with the
+ * HEAP_ASSIGN() macro, which sets the back pointer and updates the
+ * heap at the same time. */
+#define PARENT(i) (((i) - 1) / 2)
+#define CHILD1(i) ((i) * 2 + 1)
+#define CHILD2(i) ((i) * 2 + 2)
+#define TIME(i) (heap[i]->abstime)
+#define HEAP_ASSIGN(pos, tmr) ((heap[pos] = (tmr))->heap_pos = (pos))
+
+static Timer **heap;
+static int num_timers = 0;
+static int heap_size = 0;
+
+static void timer_botch __P((void*));
+static Timer *add_timer __P((Timer *));
+
+Timer *timer_set_rel(time_rel, proc, arg)
+ long time_rel;
+ void (*proc) __P((void *));
+ void *arg;
+{
+ Timer *new_t;
+
+ new_t = (Timer *) malloc(sizeof(*new_t));
+ if (new_t == NULL)
+ return(NULL);
+ new_t->abstime = time_rel + NOW;
+ new_t->func = proc;
+ new_t->arg = arg;
+ return add_timer(new_t);
+}
+
+void
+timer_reset(tmr)
+ Timer *tmr;
+{
+ int pos, min;
+
+ /* Free the timer, saving its heap position. */
+ pos = tmr->heap_pos;
+ free(tmr);
+
+ if (pos != num_timers - 1) {
+ /* Replace the timer with the last timer in the heap and
+ * restore the heap, propagating the timer either up or
+ * down, depending on which way it violates the heap
+ * property to insert the last timer in place of the
+ * deleted timer. */
+ if (pos > 0 && TIME(num_timers - 1) < TIME(PARENT(pos))) {
+ do {
+ HEAP_ASSIGN(pos, heap[PARENT(pos)]);
+ pos = PARENT(pos);
+ } while (pos > 0 && TIME(num_timers - 1) < TIME(PARENT(pos)));
+ HEAP_ASSIGN(pos, heap[num_timers - 1]);
+ } else {
+ while (CHILD2(pos) < num_timers) {
+ min = num_timers - 1;
+ if (TIME(CHILD1(pos)) < TIME(min))
+ min = CHILD1(pos);
+ if (TIME(CHILD2(pos)) < TIME(min))
+ min = CHILD2(pos);
+ HEAP_ASSIGN(pos, heap[min]);
+ pos = min;
+ }
+ if (pos != num_timers - 1)
+ HEAP_ASSIGN(pos, heap[num_timers - 1]);
+ }
+ }
+ num_timers--;
+}
+
+
+#define set_timeval(t,s) ((t).tv_sec=(s),(t).tv_usec=0,(t))
+
+static Timer *
+add_timer(new)
+ Timer *new;
+{
+ int pos;
+
+ /* Create or resize the heap as necessary. */
+ if (heap_size == 0) {
+ heap_size = INITIAL_HEAP_SIZE;
+ heap = (Timer **) malloc(heap_size * sizeof(Timer *));
+ } else if (num_timers >= heap_size) {
+ heap_size = heap_size * 2 + DELTA;
+ heap = (Timer **) realloc(heap, heap_size * sizeof(Timer *));
+ }
+ if (!heap) {
+ free(new);
+ return NULL;
+ }
+
+ /* Insert the Timer *into the heap. */
+ pos = num_timers;
+ while (pos > 0 && new->abstime < TIME(PARENT(pos))) {
+ HEAP_ASSIGN(pos, heap[PARENT(pos)]);
+ pos = PARENT(pos);
+ }
+ HEAP_ASSIGN(pos, new);
+ num_timers++;
+
+ return new;
+}
+
+void
+timer_process()
+{
+ Timer *t;
+ timer_proc func;
+ void *arg;
+ int valid = 0;
+
+ if (num_timers == 0 || heap[0]->abstime > NOW)
+ return;
+
+ /* Remove the first timer from the heap, remembering its
+ * function and argument. */
+ t = heap[0];
+ func = t->func;
+ arg = t->arg;
+ t->func = timer_botch;
+ t->arg = NULL;
+ timer_reset(t);
+
+ /* Run the function. */
+ func(arg);
+}
+
+struct timeval *
+timer_timeout(tvbuf)
+ struct timeval *tvbuf;
+{
+ if (num_timers > 0) {
+ tvbuf->tv_sec = heap[0]->abstime - NOW;
+ if (tvbuf->tv_sec < 0)
+ tvbuf->tv_sec = 0;
+ tvbuf->tv_usec = 0;
+ return tvbuf;
+ } else {
+ return NULL;
+ }
+}
+
+static void
+timer_botch(arg)
+ void *arg;
+{
+ syslog(LOG_CRIT, "timer botch\n");
+ abort();
+}
+
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains definitions used by timer.c
+ *
+ * Created by: John T. Kohl
+ * Derived from timer_manager_.h by Ken Raeburn
+ *
+ * $Id: timer.h,v 1.12 1999/01/22 23:19:49 ghudson Exp $
+ *
+ */
+
+#ifndef __TIMER_H
+
+/*
+ * timer_manager_ -- routines for handling timers in login_shell
+ * (and elsewhere)
+ *
+ * Copyright 1986 Student Information Processing Board,
+ * Massachusetts Institute of Technology
+ *
+ * written by Ken Raeburn
+
+Permission to use, copy, modify, and distribute this
+software and its documentation for any purpose and without
+fee is hereby granted, provided that the above copyright
+notice appear in all copies and that both that copyright
+notice and this permission notice appear in supporting
+documentation, and that the name of M.I.T. and the Student
+Information Processing Board not be used in
+advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+M.I.T. and the Student Information Processing Board
+make no representations about the suitability of
+this software for any purpose. It is provided "as is"
+without express or implied warranty.
+
+ */
+
+typedef void (*timer_proc) __P((void *));
+
+typedef struct _Timer {
+ int heap_pos; /* Position in timer heap */
+ long abstime;
+ timer_proc func;
+ void *arg;
+} Timer;
+
+Timer *timer_set_rel __P((long, timer_proc, void *));
+Timer *timer_set_abs __P((long, timer_proc, void *));
+void timer_reset __P((Timer *));
+void timer_process __P((void));
+struct timeval *timer_timeout __P((struct timeval *tvbuf));
+
+#endif /* __TIMER_H */
+
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains functions for the User Locator service.
+ *
+ * Created by: John T. Kohl
+ *
+ * $Id: uloc.c,v 1.59 2000/01/22 18:36:02 ghudson Exp $
+ *
+ * Copyright (c) 1987,1988 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <zephyr/mit-copyright.h>
+#include "zserver.h"
+#include <sys/socket.h>
+
+#ifndef lint
+#ifndef SABER
+static const char rcsid_uloc_c[] =
+"$Id: uloc.c,v 1.59 2000/01/22 18:36:02 ghudson Exp $";
+#endif /* SABER */
+#endif /* lint */
+
+/*
+ * The user locator functions.
+ *
+ * External functions:
+ *
+ * void ulocate_dispatch(notice, auth, who, server)
+ * ZNotice_t *notice;
+ * int auth;
+ * struct sockaddr_in *who;
+ * Server *server;
+ *
+ * void ulogin_dispatch(notice, auth, who, server)
+ * ZNotice_t *notice;
+ * int auth;
+ * struct sockaddr_in *who;
+ * Server *server;
+ *
+ * void uloc_hflush(addr)
+ * struct in_addr *addr;
+ *
+ * void uloc_flush_client(sin)
+ * struct sockaddr_in *sin;
+ *
+ * Code_t uloc_send_locations()
+ *
+ * void uloc_dump_locs(fp)
+ * FILE *fp;
+ */
+
+/*
+ * The user locator.
+ * We maintain an array of Location sorted by user (so we can do
+ * binary searches), growing and shrinking it as necessary.
+ */
+
+/* WARNING: make sure this is the same as the number of strings you */
+/* plan to hand back to the user in response to a locate request, */
+/* else you will lose. See ulogin_locate() and uloc_send_locations() */
+#define NUM_FIELDS 3
+
+typedef enum _Exposure_type {
+ NONE,
+ OPSTAFF_VIS,
+ REALM_VIS,
+ REALM_ANN,
+ NET_VIS,
+ NET_ANN
+} Exposure_type;
+
+typedef struct _Location {
+ String *user;
+ String *machine;
+ char *time; /* in ctime format */
+ String *tty;
+ struct sockaddr_in addr; /* IP address and port of location */
+ Exposure_type exposure;
+} Location;
+
+#define NOLOC 1
+#define QUIET -1
+#define UNAUTH -2
+
+static void ulogin_locate __P((ZNotice_t *notice, struct sockaddr_in *who,
+ int auth)),
+ulogin_flush_user __P((ZNotice_t *notice));
+static Location *ulogin_find __P((char *user, struct in_addr *host,
+ unsigned int port));
+static Location *ulogin_find_user __P((char *user));
+static int ulogin_setup __P((ZNotice_t *notice, Location *locs,
+ Exposure_type exposure, struct sockaddr_in *who)),
+ulogin_add_user __P((ZNotice_t *notice, Exposure_type exposure,
+ struct sockaddr_in *who)),
+ulogin_parse __P((ZNotice_t *notice, Location *locs));
+static Exposure_type ulogin_remove_user __P((ZNotice_t *notice,
+ struct sockaddr_in *who,
+ int *err_return));
+static void login_sendit __P((ZNotice_t *notice, int auth,
+ struct sockaddr_in *who, int external));
+static char **ulogin_marshal_locs __P((ZNotice_t *notice, int *found,
+ int auth));
+
+static int ul_equiv __P((Location *l1, Location *l2));
+
+static void free_loc __P((Location *loc));
+static void ulogin_locate_forward __P((ZNotice_t *notice,
+ struct sockaddr_in *who, Realm *realm));
+
+static Location *locations = NULL; /* ptr to first in array */
+static int num_locs = 0; /* number in array */
+
+/*
+ * Dispatch a LOGIN notice.
+ */
+
+Code_t
+ulogin_dispatch(notice, auth, who, server)
+ ZNotice_t *notice;
+ int auth;
+ struct sockaddr_in *who;
+ Server *server;
+{
+ Exposure_type retval;
+ int err_ret;
+
+ if (strcmp(notice->z_opcode, LOGIN_USER_LOGOUT) == 0) {
+ retval = ulogin_remove_user(notice, who, &err_ret);
+ switch (retval) {
+ case NONE:
+ if (err_ret == UNAUTH) {
+ if (server == me_server)
+ clt_ack(notice, who, AUTH_FAILED);
+ return ZERR_NONE;
+ } else if (err_ret == NOLOC) {
+ if (server == me_server)
+ clt_ack(notice, who, NOT_FOUND);
+ return ZERR_NONE;
+ }
+ syslog(LOG_ERR,"bogus location exposure NONE, %s",
+ notice->z_sender);
+ break;
+ case OPSTAFF_VIS:
+ case REALM_VIS:
+ /* he is not announced to people. Silently ack */
+ if (server == me_server)
+ ack(notice, who);
+ break;
+ case REALM_ANN:
+ case NET_VIS:
+ if (server == me_server)
+ sendit(notice, 1, who, 0);
+ break;
+ case NET_ANN:
+ /* currently no distinction between these.
+ just announce */
+ /* we assume that if this user is at a certain
+ IP address, we can trust the logout to be
+ authentic. ulogin_remove_user checks the
+ ip addrs */
+ if (server == me_server)
+ sendit(notice, 1, who, 1);
+ break;
+ default:
+ syslog(LOG_ERR,"bogus location exposure %d/%s",
+ (int) retval, notice->z_sender);
+ break;
+ }
+ if (server == me_server) /* tell the other servers */
+ server_forward(notice, auth, who);
+ return ZERR_NONE;
+ }
+ if (!bdumping &&
+ (!auth || strcmp(notice->z_sender, notice->z_class_inst) != 0)) {
+ zdbug((LOG_DEBUG,"unauthentic ulogin: %d %s %s", auth,
+ notice->z_sender, notice->z_class_inst));
+ if (server == me_server)
+ clt_ack(notice, who, AUTH_FAILED);
+ return ZERR_NONE;
+ }
+ if (strcmp(notice->z_opcode, LOGIN_USER_FLUSH) == 0) {
+ ulogin_flush_user(notice);
+ if (server == me_server)
+ ack(notice, who);
+ } else if (strcmp(notice->z_opcode, EXPOSE_NONE) == 0) {
+ ulogin_remove_user(notice, who, &err_ret);
+ if (err_ret == UNAUTH) {
+ if (server == me_server)
+ clt_ack(notice, who, AUTH_FAILED);
+ return ZERR_NONE;
+ } else if (err_ret == NOLOC) {
+ if (server == me_server)
+ clt_ack(notice, who, NOT_FOUND);
+ return ZERR_NONE;
+ }
+ if (server == me_server) {
+ ack(notice, who);
+ server_forward(notice, auth, who);
+ }
+ return ZERR_NONE;
+ } else if (strcmp(notice->z_opcode, EXPOSE_OPSTAFF) == 0) {
+ err_ret = ulogin_add_user(notice, OPSTAFF_VIS, who);
+ if (server == me_server) {
+ if (err_ret)
+ nack(notice, who);
+ else
+ ack(notice, who);
+ }
+ } else if (strcmp(notice->z_opcode, EXPOSE_REALMVIS) == 0) {
+ err_ret = ulogin_add_user(notice, REALM_VIS, who);
+ if (server == me_server) { /* realm vis is not broadcast,
+ so we ack it here */
+ if (err_ret)
+ nack(notice, who);
+ else
+ ack(notice, who);
+ }
+ } else if (!strcmp(notice->z_opcode, EXPOSE_REALMANN)) {
+ err_ret = ulogin_add_user(notice, REALM_ANN, who);
+ if (server == me_server) { /* announce to the realm */
+ if (err_ret)
+ nack(notice, who);
+ else
+ login_sendit(notice, auth, who, 0);
+ }
+ } else if (!strcmp(notice->z_opcode, EXPOSE_NETVIS)) {
+ err_ret = ulogin_add_user(notice, NET_VIS, who);
+ if (server == me_server) { /* announce to the realm */
+ if (err_ret)
+ nack(notice, who);
+ else
+ login_sendit(notice, auth, who, 0);
+ }
+ } else if (!strcmp(notice->z_opcode, EXPOSE_NETANN)) {
+ err_ret = ulogin_add_user(notice, NET_ANN, who);
+ if (server == me_server) { /* tell the world */
+ if (err_ret)
+ nack(notice, who);
+ else
+ login_sendit(notice, auth, who, 1);
+ }
+ } else {
+ syslog(LOG_ERR, "unknown ulog opcode %s", notice->z_opcode);
+ if (server == me_server)
+ nack(notice, who);
+ return ZERR_NONE;
+ }
+ if (server == me_server)
+ server_forward(notice, auth, who);
+ return ZERR_NONE;
+}
+
+static void
+login_sendit(notice, auth, who, external)
+ ZNotice_t *notice;
+ int auth;
+ struct sockaddr_in *who;
+ int external;
+{
+ ZNotice_t log_notice;
+
+ /* we must copy the notice struct here because we need the original
+ for forwarding. We needn't copy the private data of the notice,
+ since that isn't modified by sendit and its subroutines. */
+
+ log_notice = *notice;
+
+ log_notice.z_opcode = LOGIN_USER_LOGIN;
+ sendit(&log_notice, auth, who, external);
+}
+
+
+/*
+ * Dispatch a LOCATE notice.
+ */
+Code_t
+ulocate_dispatch(notice, auth, who, server)
+ ZNotice_t *notice;
+ int auth;
+ struct sockaddr_in *who;
+ Server *server;
+{
+ char *cp;
+ Realm *realm;
+
+ if (!strcmp(notice->z_opcode, LOCATE_LOCATE)) {
+ /* we are talking to a current-rev client; send an ack */
+ ack(notice, who);
+ cp = strchr(notice->z_class_inst, '@');
+ if (cp && (realm = realm_get_realm_by_name(cp + 1)))
+ ulogin_locate_forward(notice, who, realm);
+ else
+ ulogin_locate(notice, who, auth);
+ return ZERR_NONE;
+ } else {
+ syslog(LOG_ERR, "unknown uloc opcode %s", notice->z_opcode);
+ if (server == me_server)
+ nack(notice, who);
+ return ZERR_NONE;
+ }
+}
+
+/*
+ * Flush all locations at the address.
+ */
+
+void
+uloc_hflush(addr)
+ struct in_addr *addr;
+{
+ Location *loc;
+ int i = 0, new_num = 0;
+
+ if (num_locs == 0)
+ return; /* none to flush */
+
+ /* slightly inefficient, assume the worst, and allocate enough space */
+ loc = (Location *) malloc(num_locs *sizeof(Location));
+ if (!loc) {
+ syslog(LOG_CRIT, "uloc_flush alloc");
+ abort();
+ }
+
+ /* copy entries which don't match */
+ while (i < num_locs) {
+ if (locations[i].addr.sin_addr.s_addr != addr->s_addr)
+ loc[new_num++] = locations[i];
+ else
+ free_loc(&locations[i]);
+ i++;
+ }
+
+ free(locations);
+ locations = NULL;
+
+ if (!new_num) {
+ free(loc);
+ loc = NULL;
+ num_locs = new_num;
+
+ return;
+ }
+ locations = loc;
+ num_locs = new_num;
+
+ /* all done */
+ return;
+}
+
+void
+uloc_flush_client(sin)
+ struct sockaddr_in *sin;
+{
+ Location *loc;
+ int i = 0, new_num = 0;
+
+ if (num_locs == 0)
+ return; /* none to flush */
+
+ /* slightly inefficient, assume the worst, and allocate enough space */
+ loc = (Location *) malloc(num_locs *sizeof(Location));
+ if (!loc) {
+ syslog(LOG_CRIT, "uloc_flush_clt alloc");
+ abort();
+ }
+
+ /* copy entries which don't match */
+ while (i < num_locs) {
+ if ((locations[i].addr.sin_addr.s_addr != sin->sin_addr.s_addr)
+ || (locations[i].addr.sin_port != sin->sin_port)) {
+ loc[new_num++] = locations[i];
+ } else {
+ free_loc(&locations[i]);
+ }
+ i++;
+ }
+
+ free(locations);
+ locations = NULL;
+
+ if (!new_num) {
+ free(loc);
+ loc = NULL;
+ num_locs = new_num;
+
+ return;
+ }
+ locations = loc;
+ num_locs = new_num;
+
+#ifdef DEBUG
+ if (zdebug) {
+ int i;
+
+ for (i = 0; i < num_locs; i++) {
+ syslog(LOG_DEBUG, "%s/%d", locations[i].user->string,
+ (int) locations[i].exposure);
+ }
+ }
+#endif
+ /* all done */
+ return;
+}
+
+/*
+ * Send the locations for host for a brain dump
+ */
+
+/*ARGSUSED*/
+Code_t
+uloc_send_locations()
+{
+ Location *loc;
+ int i;
+ char *lyst[NUM_FIELDS];
+ char *exposure_level;
+ Code_t retval;
+
+ for (i = 0, loc = locations; i < num_locs; i++, loc++) {
+ lyst[0] = (char *) loc->machine->string;
+ lyst[1] = (char *) loc->time;
+ lyst[2] = (char *) loc->tty->string;
+
+ switch (loc->exposure) {
+ case OPSTAFF_VIS:
+ exposure_level = EXPOSE_OPSTAFF;
+ break;
+ case REALM_VIS:
+ exposure_level = EXPOSE_REALMVIS;
+ break;
+ case REALM_ANN:
+ exposure_level = EXPOSE_REALMANN;
+ break;
+ case NET_VIS:
+ exposure_level = EXPOSE_NETVIS;
+ break;
+ case NET_ANN:
+ exposure_level = EXPOSE_NETANN;
+ break;
+ default:
+ syslog(LOG_ERR,"broken location state %s/%d",
+ loc->user->string, (int) loc->exposure);
+ break;
+ }
+ retval = bdump_send_list_tcp(ACKED, &loc->addr, LOGIN_CLASS,
+ loc->user->string, exposure_level, myname,
+ "", lyst, NUM_FIELDS);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_ERR, "uloc_send_locs: %s", error_message(retval));
+ return(retval);
+ }
+ }
+ return ZERR_NONE;
+}
+
+/*
+ * Add the user to the internal table of locations.
+ */
+
+static int
+ulogin_add_user(notice, exposure, who)
+ ZNotice_t *notice;
+ Exposure_type exposure;
+ struct sockaddr_in *who;
+{
+ Location *loc, *oldlocs, newloc;
+ int i;
+
+ loc = ulogin_find(notice->z_class_inst, &who->sin_addr, notice->z_port);
+ if (loc) {
+ /* Update the time, tty, and exposure on the existing location. */
+ loc->exposure = exposure;
+ if (ulogin_parse(notice, &newloc) == 0) {
+ free_string(loc->tty);
+ loc->tty = dup_string(newloc.tty);
+ free(loc->time);
+ loc->time = strsave(newloc.time);
+ free_loc(&newloc);
+ }
+ return 0;
+ }
+
+ oldlocs = locations;
+
+ locations = (Location *) malloc((num_locs + 1) * sizeof(Location));
+ if (!locations) {
+ syslog(LOG_ERR, "zloc mem alloc");
+ locations = oldlocs;
+ return 1;
+ }
+
+ if (num_locs == 0) { /* first one */
+ if (ulogin_setup(notice, locations, exposure, who)) {
+ free(locations);
+ locations = NULL;
+ return 1;
+ }
+ num_locs = 1;
+ goto dprnt;
+ }
+
+ /* not the first one, insert him */
+
+ if (ulogin_setup(notice, &newloc, exposure, who)) {
+ free(locations);
+ locations = oldlocs;
+ return 1;
+ }
+ num_locs++;
+
+ /* copy old locs */
+ i = 0;
+ while ((i < num_locs-1) &&
+ (comp_string(oldlocs[i].user,newloc.user) < 0)) {
+ locations[i] = oldlocs[i];
+ i++;
+ }
+
+ /* add him in here */
+ locations[i++] = newloc;
+
+ /* copy the rest */
+ while (i < num_locs) {
+ locations[i] = oldlocs[i - 1];
+ i++;
+ }
+ if (oldlocs)
+ free(oldlocs);
+
+ dprnt:
+ return 0;
+}
+
+/*
+ * Set up the location locs with the information in the notice.
+ */
+
+static int
+ulogin_setup(notice, locs, exposure, who)
+ ZNotice_t *notice;
+ Location *locs;
+ Exposure_type exposure;
+ struct sockaddr_in *who;
+{
+ if (ulogin_parse(notice, locs))
+ return 1;
+
+ locs->exposure = exposure;
+ locs->addr.sin_family = AF_INET;
+ locs->addr.sin_addr.s_addr = who->sin_addr.s_addr;
+ locs->addr.sin_port = notice->z_port;
+ return(0);
+}
+
+/*
+ * Parse the location information in the notice, and fill it into *locs
+ */
+
+static int
+ulogin_parse(notice, locs)
+ ZNotice_t *notice;
+ Location *locs;
+{
+ char *cp, *base;
+ int nulls = 0;
+
+ if (!notice->z_message_len) {
+ syslog(LOG_ERR, "short ulogin");
+ return 1;
+ }
+
+ base = notice->z_message;
+ for (cp = base; cp < base + notice->z_message_len; cp++) {
+ if (!*cp)
+ nulls++;
+ }
+ if (nulls < 3) {
+ syslog(LOG_ERR, "zloc bad format from user %s (only %d fields)",
+ notice->z_sender, nulls);
+ return 1;
+ }
+
+ locs->user = make_string(notice->z_class_inst,0);
+
+ cp = base;
+ locs->machine = make_string(cp,0);
+
+ cp += (strlen(cp) + 1);
+ locs->time = strsave(cp);
+
+ /* This field might not be null-terminated */
+ cp += (strlen(cp) + 1);
+ locs->tty = make_string(cp, 0);
+
+ return 0;
+}
+
+
+static Location *
+ulogin_find(user, host, port)
+ char *user;
+ struct in_addr *host;
+ unsigned int port;
+{
+ Location *loc;
+ String *str;
+
+ /* Find the first location for this user. */
+ loc = ulogin_find_user(user);
+ if (!loc)
+ return NULL;
+
+ /* Look for a location which matches the host and port. */
+ str = make_string(user, 0);
+ while (loc < locations + num_locs && loc->user == str) {
+ if (loc->addr.sin_addr.s_addr == host->s_addr
+ && loc->addr.sin_port == port) {
+ free_string(str);
+ return loc;
+ }
+ loc++;
+ }
+
+ free_string(str);
+ return NULL;
+}
+
+/*
+ * Return a pointer to the first instance of this user@realm in the
+ * table.
+ */
+
+static Location *
+ulogin_find_user(user)
+ char *user;
+{
+ int i, rlo, rhi;
+ int compar;
+ String *str;
+
+ if (!locations)
+ return(NULL);
+
+ str = make_string(user, 0);
+
+ /* i is the current midpoint location, rlo is the lowest we will
+ * still check, and rhi is the highest we will still check. */
+
+ i = num_locs / 2;
+ rlo = 0;
+ rhi = num_locs - 1;
+
+ while ((compar = comp_string(locations[i].user, str)) != 0) {
+ if (compar < 0)
+ rlo = i + 1;
+ else
+ rhi = i - 1;
+ if (rhi - rlo < 0) {
+ free_string(str);
+ return NULL;
+ }
+ i = (rhi + rlo) / 2;
+ }
+
+ /* Back up to the first location for this user. */
+ while (i > 0 && locations[i - 1].user == str)
+ i--;
+ free_string(str);
+ return &locations[i];
+}
+
+static int
+ul_equiv(l1, l2)
+ Location *l1, *l2;
+{
+ if (l1->machine != l2->machine)
+ return 0;
+ if (l1->tty != l2->tty)
+ return 0;
+ return 1;
+}
+
+/*
+ * remove the user specified in notice from the internal table
+ */
+
+static Exposure_type
+ulogin_remove_user(notice, who, err_return)
+ ZNotice_t *notice;
+ struct sockaddr_in *who;
+ int *err_return;
+{
+ Location *new_locs, *loc;
+ int i = 0;
+ Exposure_type quiet;
+
+ *err_return = 0;
+ loc = ulogin_find(notice->z_class_inst, &who->sin_addr, notice->z_port);
+ if (!loc) {
+ *err_return = NOLOC;
+ return NONE;
+ }
+
+ quiet = loc->exposure;
+
+ if (--num_locs == 0) { /* last one */
+ free_loc(locations);
+ free(locations);
+ locations = NULL;
+ return quiet;
+ }
+
+ new_locs = (Location *) malloc(num_locs * sizeof(Location));
+ if (!new_locs) {
+ syslog(LOG_CRIT, "ul_rem alloc");
+ abort();
+ }
+
+ /* copy old entries */
+ while (i < num_locs && &locations[i] < loc) {
+ new_locs[i] = locations[i];
+ i++;
+ }
+
+ /* free up this one */
+ free_loc(&locations[i]);
+ i++; /* skip over this one */
+
+ /* copy the rest */
+ while (i <= num_locs) {
+ new_locs[i - 1] = locations[i];
+ i++;
+ }
+
+ free(locations);
+
+ locations = new_locs;
+
+ /* all done */
+ return quiet;
+}
+
+/*
+ * remove all locs of the user specified in notice from the internal table
+ */
+
+static void
+ulogin_flush_user(notice)
+ ZNotice_t *notice;
+{
+ Location *loc, *loc2;
+ int i, j, num_match, num_left;
+
+ i = num_match = num_left = 0;
+
+ if (!(loc2 = ulogin_find_user(notice->z_class_inst)))
+ return;
+
+ /* compute # locations left in the list, after loc2 (inclusive) */
+ num_left = num_locs - (loc2 - locations);
+
+ while (num_left &&
+ !strcasecmp(loc2[num_match].user->string,
+ notice->z_class_inst)) {
+ /* as long as we keep matching, march up the list */
+ num_match++;
+ num_left--;
+ }
+ if (num_locs == num_match) { /* no other locations left */
+ for (j = 0; j < num_match; j++)
+ free_loc(&locations[j]); /* free storage */
+ free (locations);
+ locations = NULL;
+ num_locs = 0;
+ return;
+ }
+
+ loc = (Location *) malloc((num_locs - num_match) * sizeof(Location));
+ if (!loc) {
+ syslog(LOG_CRIT, "ul_rem alloc");
+ abort();
+ }
+
+ /* copy old entries */
+ while (i < num_locs && &locations[i] < loc2) {
+ loc[i] = locations[i];
+ i++;
+ }
+
+ for(j = 0; j < num_match; j++) {
+ free_loc(&locations[i]);
+ i++;
+ }
+
+ /* copy the rest */
+ while (i < num_locs) {
+ loc[i - num_match] = locations[i];
+ i++;
+ }
+
+ free(locations);
+
+ locations = loc;
+ num_locs -= num_match;
+
+#ifdef DEBUG
+ if (zdebug) {
+ int i;
+
+ for (i = 0; i < num_locs; i++) {
+ syslog(LOG_DEBUG, "%s/%d", locations[i].user->string,
+ (int) locations[i].exposure);
+ }
+ }
+#endif
+}
+
+
+static void
+ulogin_locate(notice, who, auth)
+ ZNotice_t *notice;
+ struct sockaddr_in *who;
+ int auth;
+{
+ char **answer;
+ int found;
+ Code_t retval;
+ struct sockaddr_in send_to_who;
+
+ answer = ulogin_marshal_locs(notice, &found, auth);
+
+ send_to_who = *who;
+ send_to_who.sin_port = notice->z_port;
+
+ retval = ZSetDestAddr(&send_to_who);
+ if (retval != ZERR_NONE) {
+ syslog(LOG_WARNING, "ulogin_locate set addr: %s",
+ error_message(retval));
+ if (answer)
+ free(answer);
+ return;
+ }
+
+ notice->z_kind = ACKED;
+
+ /* use xmit_frag() to send each piece of the notice */
+
+ retval = ZSrvSendRawList(notice, answer, found * NUM_FIELDS, xmit_frag);
+ if (retval != ZERR_NONE)
+ syslog(LOG_WARNING, "ulog_locate xmit: %s", error_message(retval));
+ if (answer)
+ free(answer);
+}
+
+/*
+ * Locate the user and collect the locations into an array. Return the # of
+ * locations in *found.
+ */
+
+static char **
+ulogin_marshal_locs(notice, found, auth)
+ ZNotice_t *notice;
+ int *found;
+ int auth;
+{
+ Location **matches = (Location **) 0;
+ Location *loc;
+ char **answer;
+ int i = 0;
+ String *inst;
+ int local = (auth && sender_in_realm(notice));
+
+ *found = 0; /* # of matches */
+
+ loc = ulogin_find_user(notice->z_class_inst);
+ if (!loc)
+ return(NULL);
+
+ i = loc - locations;
+
+ inst = make_string(notice->z_class_inst,0);
+ while (i < num_locs && (inst == locations[i].user)) {
+ /* these locations match */
+ switch (locations[i].exposure) {
+ case OPSTAFF_VIS:
+ i++;
+ continue;
+ case REALM_VIS:
+ case REALM_ANN:
+ if (!local) {
+ i++;
+ continue;
+ }
+ case NET_VIS:
+ case NET_ANN:
+ default:
+ break;
+ }
+ if (!*found) {
+ matches = (Location **) malloc(sizeof(Location *));
+ if (!matches) {
+ syslog(LOG_ERR, "ulog_loc: no mem");
+ break; /* from the while */
+ }
+ matches[0] = &locations[i];
+ (*found)++;
+ } else {
+ matches = (Location **) realloc(matches,
+ ++(*found) * sizeof(Location *));
+ if (!matches) {
+ syslog(LOG_ERR, "ulog_loc: realloc no mem");
+ *found = 0;
+ break; /* from the while */
+ }
+ matches[*found - 1] = &locations[i];
+ }
+ i++;
+ }
+ free_string(inst);
+
+ /* OK, now we have a list of user@host's to return to the client
+ in matches */
+
+
+#ifdef DEBUG
+ if (zdebug) {
+ for (i = 0; i < *found ; i++)
+ zdbug((LOG_DEBUG,"found %s",
+ matches[i]->user->string));
+ }
+#endif
+
+ /* coalesce the location information into a list of char *'s */
+ answer = (char **) malloc((*found) * NUM_FIELDS * sizeof(char *));
+ if (!answer) {
+ syslog(LOG_ERR, "zloc no mem(answer)");
+ *found = 0;
+ } else
+ for (i = 0; i < *found ; i++) {
+ answer[i * NUM_FIELDS] = matches[i]->machine->string;
+ answer[i * NUM_FIELDS + 1] = matches[i]->time;
+ answer[i * NUM_FIELDS + 2] = matches[i]->tty->string;
+ }
+
+ if (matches)
+ free(matches);
+ return answer;
+}
+
+void
+uloc_dump_locs(fp)
+ FILE *fp;
+{
+ int i;
+
+ for (i = 0; i < num_locs; i++) {
+ fputs("'", fp);
+ dump_quote(locations[i].user->string, fp);
+ fputs("' '", fp);
+ dump_quote(locations[i].machine->string, fp);
+ fputs("' '", fp);
+ dump_quote(locations[i].time, fp);
+ fputs("' '", fp);
+ dump_quote(locations[i].tty->string, fp);
+ fputs("' ", fp);
+ switch (locations[i].exposure) {
+ case OPSTAFF_VIS:
+ fputs("OPSTAFF", fp);
+ break;
+ case REALM_VIS:
+ fputs("RLM_VIS", fp);
+ break;
+ case REALM_ANN:
+ fputs("RLM_ANN", fp);
+ break;
+ case NET_VIS:
+ fputs("NET_VIS", fp);
+ break;
+ case NET_ANN:
+ fputs("NET_ANN", fp);
+ break;
+ default:
+ fprintf(fp, "? %d ?", locations[i].exposure);
+ break;
+ }
+ fprintf(fp, " %s/%d\n", inet_ntoa(locations[i].addr.sin_addr),
+ ntohs(locations[i].addr.sin_port));
+ }
+}
+
+static void
+free_loc(loc)
+ Location *loc;
+{
+ free_string(loc->user);
+ free_string(loc->machine);
+ free_string(loc->tty);
+ free(loc->time);
+ return;
+}
+
+static void
+ulogin_locate_forward(notice, who, realm)
+ ZNotice_t *notice;
+ struct sockaddr_in *who;
+ Realm *realm;
+{
+ ZNotice_t lnotice;
+
+ lnotice = *notice;
+ lnotice.z_opcode = REALM_REQ_LOCATE;
+
+ realm_handoff(&lnotice, 1, who, realm, 0);
+}
+
+void
+ulogin_realm_locate(notice, who, realm)
+ ZNotice_t *notice;
+ struct sockaddr_in *who;
+ Realm *realm;
+{
+ char **answer;
+ int found;
+ Code_t retval;
+ ZNotice_t lnotice;
+ char *pack;
+ int packlen;
+
+#ifdef DEBUG
+ if (zdebug)
+ zdbug((LOG_DEBUG, "ulogin_realm_locate"));
+#endif
+
+ answer = ulogin_marshal_locs(notice, &found, 0/*AUTH*/);
+
+ lnotice = *notice;
+ lnotice.z_opcode = REALM_ANS_LOCATE;
+
+ if ((retval = ZFormatRawNoticeList(&lnotice, answer, found * NUM_FIELDS, &pack, &packlen)) != ZERR_NONE) {
+ syslog(LOG_WARNING, "ulog_rlm_loc format: %s",
+ error_message(retval));
+
+ if (answer)
+ free(answer);
+ return;
+ }
+ if (answer)
+ free(answer);
+
+ if ((retval = ZParseNotice(pack, packlen, &lnotice)) != ZERR_NONE) {
+ syslog(LOG_WARNING, "subscr_rlm_sendit parse: %s",
+ error_message(retval));
+ free(pack);
+ return;
+ }
+
+ realm_handoff(&lnotice, 1, who, realm, 0);
+ free(pack);
+
+ return;
+}
+
+void
+ulogin_relay_locate(notice, who)
+ ZNotice_t *notice;
+ struct sockaddr_in *who;
+{
+ ZNotice_t lnotice;
+ Code_t retval;
+ struct sockaddr_in newwho;
+ char *pack;
+ int packlen;
+
+ newwho.sin_addr.s_addr = notice->z_sender_addr.s_addr;
+ newwho.sin_port = notice->z_port;
+ newwho.sin_family = AF_INET;
+
+ if ((retval = ZSetDestAddr(&newwho)) != ZERR_NONE) {
+ syslog(LOG_WARNING, "uloc_relay_loc set addr: %s",
+ error_message(retval));
+ return;
+ }
+
+ lnotice = *notice;
+ lnotice.z_opcode = LOCATE_LOCATE;
+ lnotice.z_kind = ACKED;
+
+ if ((retval = ZFormatRawNotice(&lnotice, &pack, &packlen)) != ZERR_NONE) {
+ syslog(LOG_WARNING, "ulog_relay_loc format: %s",
+ error_message(retval));
+ return;
+ }
+
+ if ((retval = ZSendPacket(pack, packlen, 0)) != ZERR_NONE) {
+ syslog(LOG_WARNING, "ulog_relay_loc xmit: %s",
+ error_message(retval));
+ }
+ free(pack);
+}
+
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains the version identification of the Zephyr server
+ *
+ * Created by: John T. Kohl
+ *
+ * $Id: version.c,v 3.24 1999/01/22 23:19:51 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <zephyr/mit-copyright.h>
+
+#include "zserver.h"
+#include "version.h"
+
+const char zephyr_version[] = "Zephyr system version 2.0";
+
+#ifdef DEBUG
+const char version[] = "Zephyr server (DEBUG) $Revision: 3.24 $";
+#else
+const char version[] = "Zephyr server $Revision: 3.24 $";
+#endif
+
+#if !defined (lint) && !defined (SABER)
+static const char rcsid_version_c[] =
+ "$Id: version.c,v 3.24 1999/01/22 23:19:51 ghudson Exp $";
+static const char copyright[] =
+ "Copyright (c) 1987,1988,1989,1990 Massachusetts Institute of Technology.\n";
+#endif
+
+char *
+get_version()
+{
+ static char vers_buf[256];
+
+ if (vers_buf[0] == '\0') {
+#ifdef DEBUG
+ sprintf(vers_buf,"Zephyr Server (DEBUG) $Revision: 3.24 $: %s",
+ ZSERVER_VERSION_STRING);
+#else
+ sprintf(vers_buf,"Zephyr Server $Revision: 3.24 $: %s",
+ ZSERVER_VERSION_STRING);
+#endif /* DEBUG */
+
+ (void) strcat(vers_buf, "/");
+#ifdef vax
+ (void) strcat(vers_buf, "VAX");
+#endif /* vax */
+#ifdef ibm032
+ (void) strcat(vers_buf, "IBM RT");
+#endif /* ibm032 */
+#ifdef _IBMR2
+ (void) strcat(vers_buf, "IBM RS/6000");
+#endif /* _IBMR2 */
+#ifdef sun
+ (void) strcat(vers_buf, "SUN");
+#ifdef sparc
+ (void) strcat (vers_buf, "-4");
+#endif /* sparc */
+#ifdef sun386
+ (void) strcat (vers_buf, "-386I");
+#endif /* sun386 */
+#endif /* sun */
+
+#ifdef mips
+#ifdef ultrix /* DECstation */
+ (void) strcat (vers_buf, "DEC-");
+#endif /* ultrix */
+ (void) strcat(vers_buf, "MIPS");
+#endif /* mips */
+#ifdef NeXT
+ (void) strcat(vers_buf, "NeXT");
+#endif /* NeXT */
+ }
+ return(vers_buf);
+}
+
+
+
+
+
--- /dev/null
+.\" $Id: zephyrd.8,v 1.9 1999/11/29 19:46:11 ghudson Exp $
+.\"
+.\" Copyright 1987 by the Massachusetts Institute of Technology
+.\" All rights reserved. The file /usr/include/zephyr/mit-copyright.h
+.\" specifies the terms and conditions for redistribution.
+.\"
+.TH ZEPHYRD 8 "July 1, 1988" "MIT Project Athena"
+.ds ]W MIT Project Athena
+.SH NAME
+zephyrd \- Zephyr server daemon
+.SH SYNOPSIS
+.I /usr/etc/zephyrd
+[
+.BI \-d
+]
+.SH DESCRIPTION
+.I zephyrd
+is the central server for the Zephyr Notification System.
+It maintains a location database of all currently logged-in users, and a
+subscription database for each user's Zephyr clients.
+.PP
+.I zephyrd
+communicates with daemons running on other Zephyr server hosts, to
+provide a reliable service.
+.PP
+While running, any unusual conditions are recorded via
+.I syslog(3)
+to facility local6 at various levels.
+The
+.BI \-d
+option enables logging of additional debugging information.
+.PP
+When a
+.B zephyrd
+is executed, it requests a list of server machines from Hesiod and
+initializes its state from any
+\fIzephyrd\fRs executing on the other known servers. This initialization
+is only performed after the \fIzephyrd\fRs have authenticated themselves
+to each other via Kerberos.
+The server then enters a dispatch loop, servicing requests from clients and
+other servers.
+.SH SIGNALS
+.B SIGUSR1
+enables logging of additional debugging information.
+.br
+.B SIGUSR2
+disables the logging of additional debugging information.
+.br
+.B SIGHUP
+causes
+.I zephyrd
+to re-read the default subscription file and to re-query Hesiod about
+valid peers. Any peers which are not responding and no longer
+mentioned in Hesiod are flushed; any peers not previously named by
+Hesiod are added.
+.br
+.B SIGINT \fRand\fB SIGTERM
+cause
+.I zephyrd
+to gracefully shut down.
+.br
+.B SIGFPE
+causes
+.I zephyrd
+to dump the location and subscription databases to
+.I /var/tmp/zephyr.db
+in an ASCII format.
+.SH ACCESS CONTROL
+Certain notice classes are restricted by the Zephyr server. Each such
+class has access control lists enumerating who may transmit (xmt-*.acl) or
+subscribe to that particular class. Subscriptions may be
+restricted either absolutely (sub-*.acl files), or by instance restrictions.
+iws-*.acl files control subscriptions to wildcarded instances.
+iui-*.acl files control subscriptions to instances which are not the
+Kerberos principal identity of the subscriber.
+If an access control list of a given type is absent, there is no
+restriction of that type on the class, except that any notices of the
+class must be authenticated.
+The class registry lists all classes which are restricted.
+.SH FILES
+.TP 10
+.I /usr/athena/lib/zephyr/class-registry.acl:
+List of classes which are restricted
+.TP
+.I /usr/athena/lib/zephyr/iws-*.acl:
+Access Control Lists for instance-wildcard restrictions
+.TP
+.I /usr/athena/lib/zephyr/iui-*.acl:
+Access Control Lists for instance-identity restrictions
+.TP
+.I /usr/athena/lib/zephyr/sub-*.acl:
+Access Control Lists for subscribing
+.TP
+.I /usr/athena/lib/zephyr/xmt-*.acl:
+Access Control Lists for transmitting
+.TP
+.I /usr/athena/lib/zephyr/srvtab:
+Kerberos Service keys
+.TP
+.I /usr/athena/lib/zephyr/ztkts:
+Current Kerberos tickets for exchange with other servers
+.TP
+.I /usr/tmp/zephyr.db:
+File containing an ASCII dump of the database.
+.SH BUGS
+The current implementation of the Zephyr server (\fIzephyrd(8)\fR) makes
+no distinction between realm-announced, net-visible and net-announced
+exposure levels.
+.SH SEE ALSO
+zephyr(1), zhm(8), kerberosintro(1), hesiod(3), access_control_lists(?),
+syslog(3)
+.br
+Athena Technical Plan, Sections E.4.1 (Zephyr Notification Service) and
+E.2.1 (Kerberos Authentication and Authorization System)
+.SH AUTHOR
+.PP
+John T. Kohl, MIT Project Athena and Digital Equipment Corporation
+.SH RESTRICTIONS
+Copyright (c) 1987,1988 by the Massachusetts Institute of Technology.
+All Rights Reserved.
+.br
+.I zephyr(1)
+specifies the terms and conditions for redistribution.
--- /dev/null
+#ifndef __ZSERVER_H__
+#define __ZSERVER_H__
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains declarations for use in the server.
+ *
+ * Created by: John T. Kohl
+ *
+ * $Id: zserver.h,v 1.51 1999/01/22 23:19:53 ghudson Exp $
+ *
+ * Copyright (c) 1987,1988,1991 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <zephyr/mit-copyright.h>
+
+#include <internal.h>
+#include <arpa/inet.h>
+
+#include "zsrv_err.h"
+
+#include "timer.h"
+#include "zsrv_conf.h" /* configuration params */
+
+#include "zstring.h"
+#include "access.h"
+#include "acl.h"
+
+#ifdef HAVE_KRB4
+/* Kerberos-specific library interfaces used only by the server. */
+extern C_Block __Zephyr_session;
+#define ZGetSession() (__Zephyr_session)
+Code_t ZFormatAuthenticNotice __P((ZNotice_t*, char*, int, int*, C_Block));
+#endif
+
+/* For krb_rd_req prototype and definition. */
+#ifndef KRB_INT32
+#define KRB_INT32 ZEPHYR_INT32
+#endif
+
+/* These macros are for insertion into and deletion from a singly-linked list
+ * with back pointers to the previous element's next pointer. In order to
+ * make these macros act like expressions, they use the comma operator for
+ * sequenced evaluations of assignment, and "a && b" for "evaluate assignment
+ * b if expression a is true". */
+#define LIST_INSERT(head, elem) \
+ ((elem)->next = *(head), \
+ (*head) && ((*(head))->prev_p = &(elem)->next), \
+ (*head) = (elem), (elem)->prev_p = (head))
+#define LIST_DELETE(elem) \
+ (*(elem)->prev_p = (elem)->next, \
+ (elem)->next && ((elem)->next->prev_p = (elem)->prev_p))
+
+/* Current time as cached by main(); use instead of time(). */
+#define NOW t_local.tv_sec
+
+#ifdef HAVE_KRB4
+#ifndef NOENCRYPTION
+/* Kerberos shouldn't stick us with array types... */
+typedef struct {
+ des_key_schedule s;
+} Sched;
+#endif
+#endif
+
+typedef struct _Destination Destination;
+typedef struct _Destlist Destlist;
+typedef struct _Realm Realm;
+typedef struct _Realmname Realmname;
+typedef struct _Client Client;
+typedef struct _Triplet Triplet;
+typedef enum _Server_state Server_state;
+typedef struct _Unacked Unacked;
+typedef struct _Pending Pending;
+typedef struct _Server Server;
+typedef enum _Sent_type Sent_type;
+typedef struct _Statistic Statistic;
+
+struct _Destination {
+ String *classname;
+ String *inst;
+ String *recip;
+};
+
+struct _Destlist {
+ Destination dest;
+ struct _Destlist *next, **prev_p;
+};
+
+struct _Realm {
+ char name[REALM_SZ];
+ int count;
+ struct sockaddr_in *addrs;
+ int idx; /* which server we are connected to */
+ Destlist *subs;
+ Client *client;
+ long tkt_try;
+};
+
+struct _Realmname {
+ char name[REALM_SZ];
+ char **servers;
+ int nused;
+ int nservers;
+};
+
+struct _Client {
+ struct sockaddr_in addr; /* ipaddr/port of client */
+ Destlist *subs ; /* subscriptions */
+#ifdef HAVE_KRB4
+ C_Block session_key; /* session key for this client */
+#endif /* HAVE_KRB4 */
+ String *principal; /* krb principal of user */
+ int last_send; /* Counter for last sent packet. */
+ time_t last_ack; /* Time of last received ack */
+ Realm *realm;
+ struct _Client *next, **prev_p;
+};
+
+struct _Triplet {
+ Destination dest;
+ Acl *acl;
+ Client **clients;
+ int clients_size;
+ struct _Triplet *next, **prev_p;
+};
+
+enum _Server_state {
+ SERV_UP, /* Server is up */
+ SERV_TARDY, /* Server due for a hello */
+ SERV_DEAD, /* Server is considered dead */
+ SERV_STARTING /* Server is between dead and up */
+};
+
+struct _Unacked {
+ Timer *timer; /* timer for retransmit */
+ Client *client; /* responsible client, or NULL */
+ short rexmits; /* number of retransmits */
+ short packsz; /* size of packet */
+ char *packet; /* ptr to packet */
+ ZUnique_Id_t uid; /* uid of packet */
+ struct sockaddr_in ack_addr;
+ union { /* address to send to */
+ struct sockaddr_in addr; /* client address */
+ int srv_idx; /* index of server */
+ struct {
+ int rlm_idx; /* index of realm */
+ int rlm_srv_idx; /* index of server in realm */
+ } rlm;
+ } dest;
+ struct _Unacked *next, **prev_p;
+};
+
+struct _Pending {
+ char *packet; /* the notice (in pkt form) */
+ short len; /* len of pkt */
+ unsigned int auth; /* whether it is authentic */
+ struct sockaddr_in who; /* the addr of the sender */
+ struct _Pending *next;
+};
+
+struct _Server {
+ Server_state state; /* server's state */
+ struct sockaddr_in addr; /* server's address */
+ long timeout; /* Length of timeout in sec */
+ Timer *timer; /* timer for this server */
+ Pending *queue; /* queue of packets to send
+ to this server when done dumping */
+ Pending *queue_last; /* last packet on queue */
+ short num_hello_sent; /* number of hello's sent */
+ unsigned int dumping; /* 1 if dumping, so we should queue */
+ char addr_str[16]; /* text version of address */
+};
+
+enum _Sent_type {
+ NOT_SENT, /* message was not xmitted */
+ SENT, /* message was xmitted */
+ AUTH_FAILED, /* authentication failed */
+ NOT_FOUND /* user not found for uloc */
+};
+
+/* statistics gathering */
+struct _Statistic {
+ int val;
+ char *str;
+};
+
+/* Function declarations */
+
+/* found in bdump.c */
+void bdump_get __P((ZNotice_t *notice, int auth, struct sockaddr_in *who,
+ Server *server));
+void bdump_send __P((void));
+void bdump_offer __P((struct sockaddr_in *who));
+Code_t bdump_send_list_tcp __P((ZNotice_Kind_t kind, struct sockaddr_in *addr,
+ char *class_name, char *inst, char *opcode,
+ char *sender, char *recip, char **lyst,
+ int num));
+int get_tgt __P((void));
+
+/* found in class.c */
+extern String *class_control, *class_admin, *class_hm;
+extern String *class_ulogin, *class_ulocate;
+int ZDest_eq __P((Destination *d1, Destination *d2));
+Code_t triplet_register __P((Client *client, Destination *dest, Realm *realm));
+Code_t triplet_deregister __P((Client *client, Destination *dest,
+ Realm *realm));
+Code_t class_restrict __P((char *class, Acl *acl));
+Code_t class_setup_restricted __P((char *class, Acl *acl));
+Client **triplet_lookup __P((Destination *dest));
+Acl *class_get_acl __P((String *class));
+int dest_eq __P((Destination *d1, Destination *d2));
+int order_dest_strings __P((Destination *d1, Destination *d2));
+void triplet_dump_subs __P((FILE *fp));
+
+/* found in client.c */
+Code_t client_register __P((ZNotice_t *notice, struct in_addr *host,
+ Client **client_p, int wantdefaults));
+void client_deregister __P((Client *client, int flush));
+void client_flush_host __P((struct in_addr *host));
+void client_dump_clients __P((FILE *fp));
+Client *client_find __P((struct in_addr *host, unsigned int port));
+Code_t client_send_clients __P((void));
+
+/* found in common.c */
+char *strsave __P((const char *str));
+unsigned long hash __P((const char *));
+void dump_quote __P((char *p, FILE *fp));
+
+/* found in dispatch.c */
+void handle_packet __P((void));
+void clt_ack __P((ZNotice_t *notice, struct sockaddr_in *who, Sent_type sent));
+void nack_release __P((Client *client));
+void sendit __P((ZNotice_t *notice, int auth, struct sockaddr_in *who,
+ int external));
+void rexmit __P((void *));
+void xmit __P((ZNotice_t *notice, struct sockaddr_in *dest, int auth,
+ Client *client));
+Code_t hostm_dispatch __P((ZNotice_t *notice, int auth,
+ struct sockaddr_in *who, Server *server));
+Code_t control_dispatch __P((ZNotice_t *notice, int auth,
+ struct sockaddr_in *who, Server *server));
+Code_t xmit_frag __P((ZNotice_t *notice, char *buf, int len, int waitforack));
+void hostm_shutdown __P((void));
+
+/* found in kstuff.c */
+#ifdef HAVE_KRB4
+int GetKerberosData __P((int, struct in_addr, AUTH_DAT *, char *, char *));
+Code_t SendKerberosData __P((int, KTEXT, char *, char *));
+void sweep_ticket_hash_table __P((void *));
+#endif
+
+/* found in kopt.c */
+#ifdef HAVE_KRB4
+#ifndef NOENCRYPTION
+Sched *check_key_sched_cache __P((des_cblock key));
+void add_to_key_sched_cache __P((des_cblock key, Sched *sched));
+int krb_set_key __P((char *key, int cvt));
+int krb_rd_req __P((KTEXT authent, char *service, char *instance,
+ unsigned KRB_INT32 from_addr, AUTH_DAT *ad, char *fn));
+int krb_find_ticket __P((KTEXT authent, KTEXT ticket));
+int krb_get_lrealm __P((char *r, int n));
+#endif
+#endif
+
+/* found in server.c */
+void server_timo __P((void *which));
+void server_dump_servers __P((FILE *fp));
+void server_init __P((void));
+void server_shutdown __P((void));
+void server_forward __P((ZNotice_t *notice, int auth,
+ struct sockaddr_in *who));
+void server_kill_clt __P((Client *client));
+void server_pending_free __P((Pending *pending));
+void server_self_queue __P((ZNotice_t *, int, struct sockaddr_in *));
+void server_send_queue __P((Server *));
+void server_reset __P((void));
+int is_server();
+Server *server_which_server __P((struct sockaddr_in *who));
+Pending *server_dequeue __P((Server *server));
+Code_t server_dispatch __P((ZNotice_t *notice, int auth,
+ struct sockaddr_in *who));
+Code_t server_adispatch __P((ZNotice_t *notice, int auth,
+ struct sockaddr_in *who, Server *server));
+
+/* found in subscr.c */
+Code_t subscr_cancel __P((struct sockaddr_in *sin, ZNotice_t *notice));
+Code_t subscr_subscribe __P((Client *who, ZNotice_t *notice));
+Code_t subscr_send_subs __P((Client *client));
+void subscr_cancel_client __P((Client *client));
+void subscr_sendlist __P((ZNotice_t *notice, int auth,
+ struct sockaddr_in *who));
+void subscr_dump_subs __P((FILE *fp, Destlist *subs));
+void subscr_reset __P((void));
+Code_t subscr_def_subs __P((Client *who));
+
+/* found in uloc.c */
+void uloc_hflush __P((struct in_addr *addr));
+void uloc_flush_client __P((struct sockaddr_in *sin));
+void uloc_dump_locs __P((FILE *fp));
+Code_t ulogin_dispatch __P((ZNotice_t *notice, int auth,
+ struct sockaddr_in *who, Server *server));
+Code_t ulocate_dispatch __P((ZNotice_t *notice, int auth,
+ struct sockaddr_in *who, Server *server));
+Code_t uloc_send_locations __P((void));
+
+/* found in realm.c */
+Realm *realm_which_realm __P((struct sockaddr_in *who));
+Realm *realm_get_realm_by_name __P((char *name));
+void realm_handoff(ZNotice_t *, int, struct sockaddr_in *, Realm *, int);
+char *realm_expand_realm(char *);
+void realm_init __P((void));
+Code_t ZCheckRealmAuthentication __P((ZNotice_t *, struct sockaddr_in *,
+ char *));
+Code_t realm_control_dispatch __P((ZNotice_t *, int, struct sockaddr_in *,
+ Server *, Realm *));
+
+/* found in version.c */
+char *get_version __P((void));
+
+/* global identifiers */
+
+/* found in main.c */
+int packets_waiting __P((void));
+extern struct sockaddr_in srv_addr; /* server socket address */
+extern unsigned short hm_port; /* host manager receiver port */
+extern unsigned short hm_srv_port; /* host manager server sending port */
+extern int srv_socket; /* dgram sockets for clients
+ and other servers */
+extern int bdump_socket; /* brain dump socket
+ (closed most of the time) */
+
+extern fd_set interesting; /* the file descrips we are listening
+ to right now */
+extern int nfds; /* number to look at in select() */
+extern int zdebug;
+extern char myname[]; /* domain name of this host */
+#ifndef HAVE_HESIOD
+extern char list_file[];
+#endif
+#ifdef HAVE_KRB4
+extern char srvtab_file[];
+extern char my_realm[];
+#endif
+extern char acl_dir[];
+extern char subs_file[];
+extern const char version[];
+extern u_long npackets; /* num of packets processed */
+extern time_t uptime; /* time we started */
+extern struct in_addr my_addr;
+extern struct timeval t_local; /* current time */
+
+/* found in bdump.c */
+extern int bdumping; /* are we processing a bdump packet? */
+extern int bdump_concurrent; /* set while processing a packet
+ * concurrently during a braindump. */
+
+/* found in dispatch.c */
+extern Statistic i_s_ctls, i_s_logins, i_s_admins, i_s_locates;
+extern int rexmit_times[];
+
+/* found in server.c */
+extern Server *otherservers; /* array of servers */
+extern int me_server_idx; /* me (in the array of servers) */
+extern int nservers; /* number of other servers*/
+
+/* found in subscr.c */
+extern String *empty;
+extern String *wildcard_instance;
+
+extern struct in_addr my_addr; /* my inet address */
+
+#define class_is_control(classname) (classname == class_control)
+#define class_is_admin(classname) (classname == class_admin)
+#define class_is_hm(classname) (classname == class_hm)
+#define class_is_ulogin(classname) (classname == class_ulogin)
+#define class_is_ulocate(classname) (classname == class_ulocate)
+
+#define ADMIN_HELLO "HELLO" /* Opcode: hello, are you there */
+#define ADMIN_IMHERE "IHEARDYOU" /* Opcode: yes, I am here */
+#define ADMIN_SHUTDOWN "GOODBYE" /* Opcode: I am shutting down */
+#define ADMIN_BDUMP "DUMP_AVAIL" /* Opcode: I will give you a dump */
+#define ADMIN_DONE "DUMP_DONE" /* Opcode: brain dump for this server
+ is complete */
+#define ADMIN_NEWCLT "NEXT_CLIENT" /* Opcode: this is a new client */
+#define ADMIN_KILL_CLT "KILL_CLIENT" /* Opcode: client is dead, remove */
+#define ADMIN_STATUS "STATUS" /* Opcode: please send status */
+
+#define ADMIN_NEWREALM "NEXT_REALM" /* Opcode: this is a new realm */
+#define REALM_REQ_LOCATE "REQ_LOCATE" /* Opcode: request a location */
+#define REALM_ANS_LOCATE "ANS_LOCATE" /* Opcode: answer to location */
+
+/* me_server_idx is the index into otherservers of this server descriptor. */
+/* the 'limbo' server is always the first server */
+
+#define me_server (&otherservers[me_server_idx])
+#define limbo_server_idx() (0)
+#define limbo_server (&otherservers[limbo_server_idx()])
+
+#define msgs_queued() (ZQLength() || otherservers[me_server_idx].queue)
+
+#define ack(a,b) clt_ack(a,b,SENT)
+#define nack(a,b) clt_ack(a,b,NOT_SENT)
+
+#define min(a,b) ((a) < (b) ? (a) : (b))
+#define max(a,b) ((a) > (b) ? (a) : (b))
+
+#define START_CRITICAL_CODE
+#define END_CRITICAL_CODE
+
+/* the instance that matches all instances */
+#define WILDCARD_INSTANCE "*"
+
+/* debugging macros */
+#ifdef DEBUG
+#define zdbug(s1) if (zdebug) syslog s1;
+#else /* !DEBUG */
+#define zdbug(s1)
+#endif /* DEBUG */
+
+#endif /* !__ZSERVER_H__ */
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains site-specific definitions for use in the server.
+ *
+ * Created by: John T. Kohl
+ *
+ * $Id: zsrv_conf.h,v 1.13 1999/01/22 23:19:54 ghudson Exp $
+ *
+ * Copyright (c) 1988 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#ifndef __ZSRV_CONF_H__
+#define __ZSRV_CONF_H__
+#include <zephyr/mit-copyright.h>
+
+/* Path names are relative to CONFDIR, except for the class registry. */
+
+#ifndef HAVE_HESIOD
+#define SERVER_LIST_FILE "server.list"
+#endif
+#define REALM_LIST_FILE "realm.list"
+#ifdef HAVE_KRB4
+#define ZEPHYR_SRVTAB "srvtab"
+#define ZEPHYR_TKFILE "ztkts"
+#endif
+#define ZEPHYR_ACL_DIR "acl/"
+#define ZEPHYR_CLASS_REGISTRY "class-registry.acl"
+#define DEFAULT_SUBS_FILE "default.subscriptions"
+
+#define REXMIT_TIMES { 2, 2, 4, 4, 8, 8, 16, 32, 64, 128, 256, 512, -1 }
+#define NUM_REXMIT_TIMES 12
+#define CLIENT_GIVEUP_MIN 512
+
+/* hostmanager defines */
+#define LOSE_TIMO (60) /* time during which a losing host
+ must respond to a ping */
+
+/* server-server defines */
+#define TIMO_UP ((long) 60) /* timeout between up and tardy */
+#define TIMO_TARDY ((long) 120) /* timeout btw tardy hellos */
+#define TIMO_DEAD ((long)(15*60)) /* timeout between hello's for dead */
+
+#define H_NUM_TARDY 5 /* num hello's before going dead
+ when tardy */
+#define H_NUM_STARTING 2 /* num hello's before going dead
+ when starting */
+
+#define SWEEP_INTERVAL 3600 /* Time between sweeps of the ticket
+ hash table */
+
+#endif /* __ZSRV_CONF_H__ */
--- /dev/null
+# Copyright (c) 1987,1988 Massachusetts Institute of Technology
+#
+# For copying and distribution information, see the file
+# "mit-copyright.h".
+#
+# $Id: zsrv_err.et,v 1.10 1999/01/22 23:19:54 ghudson Exp $
+ et zsrv
+
+ec ZSRV_BADASSOC,
+ "Client not associated with class"
+ec ZSRV_NOCLT,
+ "No such client"
+ec ZSRV_NOSUB,
+ "No such subscription"
+ec ZSRV_NOCLASS,
+ "Class unkown"
+ec ZSRV_CLASSXISTS,
+ "Class already registered"
+ec ZSRV_CLASSRESTRICTED,
+ "Class already restricted"
+ec ZSRV_HNOTFOUND,
+ "Host manager unknown"
+ec ZSRV_WRONGSRV,
+ "Host not on this server"
+ec ZSRV_PKSHORT,
+ "Pkt length too short"
+ec ZSRV_BUFSHORT,
+ "Buffer too short"
+ec ZSRV_LEN,
+ "Read/Write length wrong"
+ec ZSRV_UNKNOWNOPCODE,
+ "Unknown opcode"
+ec ZSRV_REQUEUE,
+ "Requeue for later processing"
+ec ZSRV_RCSID,
+ "$Id: zsrv_err.et,v 1.10 1999/01/22 23:19:54 ghudson Exp $"
+ec ZSRV_BADSUBPORT,
+ "Illegal port specified in subscription"
+ec ZSRV_NORLM,
+ "No such realm"
+ec ZSRV_EMPTYCLASS,
+ "Class is now empty"
+ end
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains the main loop of the Zephyr server
+ *
+ * Created by: Lucien W. Van Elsen
+ *
+ * $Id: zstring.c,v 1.5 1999/01/22 23:19:55 ghudson Exp $
+ *
+ * Copyright (c) 1991 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <zephyr/mit-copyright.h>
+#include "zserver.h"
+
+#ifndef lint
+#ifndef SABER
+static const char rcsid_zstring_c[] =
+"$Id: zstring.c,v 1.5 1999/01/22 23:19:55 ghudson Exp $";
+#endif
+#endif
+
+static String *zhash[STRING_HASH_TABLE_SIZE];
+
+String *
+make_string(s, downcase)
+ char *s;
+ int downcase;
+{
+ char *new_s,*p;
+ String *new_z,*hp;
+ int i;
+
+ if (downcase) {
+ new_s = strsave(s);
+ p = new_s;
+ while(*p) {
+ if (isascii(*p) && isupper(*p))
+ *p = tolower(*p);
+ p++;
+ }
+ } else {
+ new_s = s;
+ }
+
+ new_z = find_string(new_s,0);
+ if (new_z != NULL) {
+ if (downcase)
+ free(new_s);
+ new_z->ref_count++;
+ return(new_z);
+ }
+
+ /* Initialize new String */
+
+ if (!downcase)
+ new_s = strsave(s);
+ new_z = (String *) malloc(sizeof(String));
+ new_z->string = new_s;
+ new_z->ref_count = 1;
+
+ /* Add to beginning of hash table */
+ new_z->hash_val = hash(new_s);
+ i = new_z->hash_val % STRING_HASH_TABLE_SIZE;
+ hp = zhash[i];
+ new_z->next = hp;
+ if (hp != NULL)
+ hp->prev = new_z;
+ new_z->prev = NULL;
+ zhash[i] = new_z;
+
+ return new_z;
+}
+
+void
+free_string(z)
+ String *z;
+{
+ if (z == (String *) NULL)
+ return;
+
+ z->ref_count--;
+ if (z->ref_count > 0)
+ return;
+
+ /* delete string completely */
+ if(z->prev == NULL)
+ zhash[hash(z->string) % STRING_HASH_TABLE_SIZE] = z->next;
+ else
+ z->prev->next = z->next;
+
+ if (z->next != NULL)
+ z->next->prev = z->prev;
+
+ free(z->string);
+ free(z);
+}
+
+String *
+find_string(s,downcase)
+ char *s;
+ int downcase;
+{
+ char *new_s,*p;
+ String *z;
+
+ if (downcase) {
+ new_s = strsave(s);
+ p = new_s;
+ while (*p) {
+ if (isascii(*p) && isupper(*p))
+ *p = tolower(*p);
+ p++;
+ }
+ } else {
+ new_s = s;
+ }
+
+ z = zhash[hash(new_s) % STRING_HASH_TABLE_SIZE];
+ while (z != NULL) {
+ if (strcmp(new_s, z->string) == 0)
+ break;
+ z = z->next;
+ }
+
+ if (downcase)
+ free(new_s);
+
+ return z;
+}
+
+int
+comp_string(a,b)
+ String *a, *b;
+{
+ if (a->hash_val > b->hash_val)
+ return 1;
+ if (a->hash_val < b->hash_val)
+ return -1;
+ return strcmp(a->string,b->string);
+}
+
+void
+print_string_table(f)
+ FILE *f;
+{
+ String *p;
+ int i;
+
+ for(i = 0; i < STRING_HASH_TABLE_SIZE; i++) {
+ p = zhash[i];
+ while (p != (String *) NULL) {
+ fprintf(f,"[%d] %s\n",p->ref_count,p->string);
+ p = p->next;
+ }
+ }
+}
+
+String *
+dup_string(z)
+ String *z;
+{
+ z->ref_count++;
+ return z;
+}
+
--- /dev/null
+/*
+ * Copyright (C) 1991 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file "mit-copyright.h".
+ *
+ * $Id: zstring.h,v 1.5 1999/01/22 23:19:56 ghudson Exp $
+ */
+
+#include <zephyr/mit-copyright.h>
+
+#ifndef __zstring_h
+#define __zstring_h __FILE__
+
+#define STRING_HASH_TABLE_SIZE 1024
+
+#include <stdio.h>
+
+typedef struct _String
+{
+ char *string; /* the string itself */
+ int ref_count; /* for gc */
+ unsigned long hash_val; /* hash value for this string */
+ struct _String *next, *prev; /* for linking in hash table */
+} String;
+
+String *make_string __P((char *s, int downcase));
+void free_string __P((String *z));
+String *find_string __P((char *s, int downcase));
+String *dup_string __P((String *z));
+int comp_string __P((String *a, String *b));
+void print_string_table __P((FILE *f));
+
+#endif /* __zstring_h */
+
--- /dev/null
+SHELL = /bin/sh
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+datadir=@datadir@
+sysconfdir=@sysconfdir@
+sbindir=@sbindir@
+lsbindir=@lsbindir@
+
+includedir=${prefix}/include
+mandir=${prefix}/man
+libdir=${exec_prefix}/lib
+
+srcdir=@srcdir@
+top_srcdir=@top_srcdir@
+BUILDTOP=..
+VPATH=@srcdir@
+CC=@CC@
+INSTALL=@INSTALL@
+
+CPPFLAGS=@CPPFLAGS@
+CFLAGS=@CFLAGS@
+ALL_CFLAGS=${CFLAGS} -I${top_srcdir}/h -I${BUILDTOP}/h ${CPPFLAGS}
+LDFLAGS=-L${BUILDTOP}/lib @LDFLAGS@
+LIBS=-lzephyr @LIBS@ -lcom_err
+
+OBJS= timer.o queue.o zhm.o zhm_client.o zhm_server.o
+
+all: zhm
+
+zhm: ${OBJS} ${BUILDTOP}/lib/libzephyr.a
+ ${CC} ${LDFLAGS} -o $@ ${OBJS} ${LIBS}
+
+.c.o:
+ ${CC} -c ${ALL_CFLAGS} $<
+
+check:
+
+install: zhm
+ ${INSTALL} -m 755 -s zhm ${DESTDIR}${lsbindir}
+ ${INSTALL} -m 644 ${srcdir}/zhm.8 ${DESTDIR}${mandir}/man8
+
+clean:
+ rm -f ${OBJS} zhm
+
+${OBJS}: zhm.h timer.h ${top_srcdir}/h/internal.h ${top_srcdir}/h/sysdep.h
+${OBJS}: ${BUILDTOP}/h/config.h ${BUILDTOP}/h/zephyr/zephyr.h
+${OBJS}: ${BUILDTOP}/h/zephyr/zephyr_err.h
+
+.PHONY: all check install clean
+
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains the hostmanager queueing routines.
+ *
+ * Created by: David C. Jedlinsky
+ *
+ * $Id: queue.c,v 1.22 1999/10/20 16:44:11 ghudson Exp $
+ *
+ * Copyright (c) 1987 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include "zhm.h"
+
+#ifndef lint
+#ifndef SABER
+static char rcsid_queue_c[] = "$Id: queue.c,v 1.22 1999/10/20 16:44:11 ghudson Exp $";
+#endif /* SABER */
+#endif /* lint */
+
+typedef struct _Queue {
+ Timer *timer;
+ int retries;
+ ZNotice_t notice;
+ caddr_t packet;
+ struct sockaddr_in reply;
+ struct _Queue *next, **prev_p;
+} Queue;
+
+static Queue *hm_queue;
+static int retransmits_enabled = 0;
+
+static Queue *find_notice_in_queue __P((ZNotice_t *notice));
+static Code_t dump_queue __P((void));
+static void queue_timeout __P((void *arg));
+
+int rexmit_times[] = { 2, 2, 4, 4, 8, -1 };
+
+#ifdef DEBUG
+Code_t dump_queue();
+#endif
+
+void init_queue()
+{
+ Queue *q;
+
+ while (hm_queue) {
+ q = hm_queue;
+ if (q->timer)
+ timer_reset(q->timer);
+ free(q->packet);
+ hm_queue = q->next;
+ free(q);
+ }
+
+ DPR("Queue initialized and flushed.\n");
+}
+
+Code_t add_notice_to_queue(notice, packet, repl, len)
+ ZNotice_t *notice;
+ char * packet;
+ struct sockaddr_in *repl;
+ int len;
+{
+ Queue *entry;
+
+ DPR("Adding notice to queue...\n");
+ if (!find_notice_in_queue(notice)) {
+ entry = (Queue *) malloc(sizeof(Queue));
+ if (entry == NULL)
+ return(ZERR_NONOTICE);
+ entry->retries = 0;
+ entry->packet = (char *) malloc(Z_MAXPKTLEN);
+ if (entry->packet == NULL) {
+ free(entry);
+ return(ZERR_NONOTICE);
+ }
+ memcpy(entry->packet, packet, Z_MAXPKTLEN);
+ if (ZParseNotice(entry->packet, len, &entry->notice) != ZERR_NONE) {
+ syslog(LOG_ERR, "ZParseNotice failed, but succeeded before");
+ free(entry->packet);
+ } else {
+ entry->reply = *repl;
+ LIST_INSERT(&hm_queue, entry);
+ }
+ entry->timer = (retransmits_enabled) ?
+ timer_set_rel(rexmit_times[0], queue_timeout, entry) : NULL;
+ }
+ return(ZERR_NONE);
+}
+
+Code_t remove_notice_from_queue(notice, kind, repl)
+ ZNotice_t *notice;
+ ZNotice_Kind_t *kind;
+ struct sockaddr_in *repl;
+{
+ Queue *entry;
+
+ DPR("Removing notice from queue...\n");
+ entry = find_notice_in_queue(notice);
+ if (entry == NULL)
+ return(ZERR_NONOTICE);
+
+ *kind = entry->notice.z_kind;
+ *repl = entry->reply;
+ if (entry->timer)
+ timer_reset(entry->timer);
+ free(entry->packet);
+ LIST_DELETE(entry);
+#ifdef DEBUG
+ dump_queue();
+#endif /* DEBUG */
+ free(entry);
+ return(ZERR_NONE);
+}
+
+/* We have a server; transmit all of our packets. */
+void retransmit_queue(sin)
+ struct sockaddr_in *sin;
+{
+ Queue *entry;
+ Code_t ret;
+
+ DPR("Retransmitting queue to new server...\n");
+ ret = ZSetDestAddr(sin);
+ if (ret != ZERR_NONE) {
+ Zperr (ret);
+ com_err("queue", ret, "setting destination");
+ }
+ for (entry = hm_queue; entry; entry = entry->next) {
+ DPR("notice:\n");
+ DPR2("\tz_kind: %d\n", entry->notice.z_kind);
+ DPR2("\tz_port: %u\n", ntohs(entry->notice.z_port));
+ DPR2("\tz_class: %s\n", entry->notice.z_class);
+ DPR2("\tz_clss_inst: %s\n", entry->notice.z_class_inst);
+ DPR2("\tz_opcode: %s\n", entry->notice.z_opcode);
+ DPR2("\tz_sender: %s\n", entry->notice.z_sender);
+ DPR2("\tz_recip: %s\n", entry->notice.z_recipient);
+ ret = send_outgoing(&entry->notice);
+ if (ret != ZERR_NONE) {
+ Zperr(ret);
+ com_err("queue", ret, "sending raw notice");
+ }
+ entry->timer = timer_set_rel(rexmit_times[0], queue_timeout, entry);
+ entry->retries = 0;
+ }
+ retransmits_enabled = 1;
+}
+
+/* We lost our server; nuke all of our timers. */
+void disable_queue_retransmits()
+{
+ Queue *entry;
+
+ for (entry = hm_queue; entry; entry = entry->next) {
+ if (entry->timer)
+ timer_reset(entry->timer);
+ entry->timer = NULL;
+ }
+ retransmits_enabled = 0;
+}
+
+#ifdef DEBUG
+static Code_t dump_queue()
+{
+ Queue *entry;
+ caddr_t mp;
+ int ml;
+
+ DPR("Dumping queue...\n");
+ if (!hm_queue) {
+ printf("Queue is empty.\n");
+ return;
+ }
+
+ for (entry = hm_queue; entry; entry = entry->next) {
+ printf("notice:\n");
+ printf("\tz_kind: %d\n", entry->notice.z_kind);
+ printf("\tz_port: %u\n", ntohs(entry->notice.z_port));
+ printf("\tz_class: %s\n", entry->notice.z_class);
+ printf("\tz_clss_inst: %s\n", entry->notice.z_class_inst);
+ printf("\tz_opcode: %s\n", entry->notice.z_opcode);
+ printf("\tz_sender: %s\n", entry->notice.z_sender);
+ printf("\tz_recip: %s\n", entry->notice.z_recipient);
+ printf("\tMessage:\n");
+ mp = entry->notice.z_message;
+ for (ml = strlen(mp) + 1; ml <= entry->notice.z_message_len; ml++) {
+ printf("\t%s\n", mp);
+ mp += strlen(mp)+1;
+ ml += strlen(mp);
+ }
+ }
+}
+#endif /* DEBUG */
+
+int queue_len()
+{
+ int length = 0;
+ Queue *entry;
+
+ for (entry = hm_queue; entry; entry = entry->next)
+ length++;
+ return length;
+}
+
+static Queue *find_notice_in_queue(notice)
+ ZNotice_t *notice;
+{
+ Queue *entry;
+
+ for (entry = hm_queue; entry; entry = entry->next) {
+ if (ZCompareUID(&entry->notice.z_uid, ¬ice->z_uid))
+ return entry;
+ }
+ return NULL;
+}
+
+static void queue_timeout(arg)
+ void *arg;
+{
+ Queue *entry = (Queue *) arg;
+ Code_t ret;
+
+ entry->timer = NULL;
+ ret = ZSetDestAddr(&serv_sin);
+ if (ret != ZERR_NONE) {
+ Zperr(ret);
+ com_err("queue", ret, "setting destination");
+ }
+ entry->retries++;
+ if (rexmit_times[entry->retries] == -1) {
+ new_server(NULL);
+ return;
+ }
+ DPR("Resending notice:\n");
+ DPR2("\tz_kind: %d\n", entry->notice.z_kind);
+ DPR2("\tz_port: %u\n", ntohs(entry->notice.z_port));
+ DPR2("\tz_class: %s\n", entry->notice.z_class);
+ DPR2("\tz_clss_inst: %s\n", entry->notice.z_class_inst);
+ DPR2("\tz_opcode: %s\n", entry->notice.z_opcode);
+ DPR2("\tz_sender: %s\n", entry->notice.z_sender);
+ DPR2("\tz_recip: %s\n", entry->notice.z_recipient);
+ ret = send_outgoing(&entry->notice);
+ if (ret != ZERR_NONE) {
+ Zperr(ret);
+ com_err("queue", ret, "sending raw notice");
+ }
+ entry->timer = timer_set_rel(rexmit_times[entry->retries], queue_timeout,
+ entry);
+}
+
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains functions for managing multiple timeouts.
+ *
+ * Created by: John T. Kohl
+ * Derived from timer_manager_ by Ken Raeburn
+ *
+ * $Id: timer.c,v 1.2 1999/01/22 23:19:58 ghudson Exp $
+ *
+ */
+
+#include "internal.h"
+#include "timer.h"
+
+#ifndef SABER
+#ifndef lint
+static const char rcsid[] =
+"$Id: timer.c,v 1.2 1999/01/22 23:19:58 ghudson Exp $";
+#endif /* lint */
+#endif /* SABER */
+
+/*
+ * timer_manager_ -- routines for handling timers in login_shell
+ * (and elsewhere)
+ *
+ * Copyright 1986 Student Information Processing Board,
+ * Massachusetts Institute of Technology
+ *
+ * written by Ken Raeburn
+
+ Permission to use, copy, modify, and distribute this
+ software and its documentation for any purpose and without
+ fee is hereby granted, provided that the above copyright
+ notice appear in all copies and that both that copyright
+ notice and this permission notice appear in supporting
+ documentation, and that the name of M.I.T. and the Student
+ Information Processing Board not be used in
+ advertising or publicity pertaining to distribution of the
+ software without specific, written prior permission.
+ M.I.T. and the Student Information Processing Board
+ make no representations about the suitability of
+ this software for any purpose. It is provided "as is"
+ without express or implied warranty.
+
+ */
+
+
+/*
+ * External functions:
+ *
+ * Timer *timer_set_rel (time_rel, proc, arg)
+ * long time_rel;
+ * void (*proc)();
+ * caddr_t arg;
+ * Timer *timer_set_abs (time_abs, proc, arg)
+ * long time_abs;
+ * void (*proc)();
+ * caddr_t arg;
+ *
+ * void timer_reset(tmr)
+ * Timer *tmr;
+ *
+ * void timer_process()
+ *
+ */
+
+/* DELTA is just an offset to keep the size a bit less than a power
+ * of two. It's measured in pointers, so it's 32 bytes on most
+ * systems. */
+#define DELTA 8
+#define INITIAL_HEAP_SIZE (1024 - DELTA)
+
+/* We have three operations which we need to be able to perform
+ * quickly: adding a timer, deleting a timer given a pointer to
+ * it, and determining which timer will be the next to go off. A
+ * heap is an ideal data structure for these purposes, so we use
+ * one. The heap is an array of pointers to timers, and each timer
+ * knows the position of its pointer in the heap.
+ *
+ * Okay, what is the heap, exactly? It's a data structure,
+ * represented as an array, with the invariant condition that
+ * the timeout of heap[i] is less than or equal to the timeout of
+ * heap[i * 2 + 1] and heap[i * 2 + 2] (assuming i * 2 + 1 and
+ * i * 2 + 2 are valid * indices). An obvious consequence of this
+ * is that heap[0] has the lowest timer value, so finding the first
+ * timer to go off is easy. We say that an index i has "children"
+ * i * 2 + 1 and i * 2 + 1, and the "parent" (i - 1) / 2.
+ *
+ * To add a timer to the heap, we start by adding it to the end, and
+ * then keep swapping it with its parent until it has a parent with
+ * a timer value less than its value. With a little bit of thought,
+ * you can see that this preserves the heap property on all indices
+ * of the array.
+ *
+ * To delete a timer at position i from the heap, we discard it and
+ * fill in its position with the last timer in the heap. In order
+ * to restore the heap, we have to consider two cases: the timer
+ * value at i is less than that of its parent, or the timer value at
+ * i is greater than that of one of its children. In the first case,
+ * we propagate the timer at i up the tree, swapping it with its
+ * parent, until the heap is restored; in the second case, we
+ * propagate the timer down the tree, swapping it with its least
+ * child, until the heap is restored. */
+
+/* In order to ensure that the back pointers from timers are consistent
+ * with the heap pointers, all heap assignments should be done with the
+ * HEAP_ASSIGN() macro, which sets the back pointer and updates the
+ * heap at the same time. */
+#define PARENT(i) (((i) - 1) / 2)
+#define CHILD1(i) ((i) * 2 + 1)
+#define CHILD2(i) ((i) * 2 + 2)
+#define TIME(i) (heap[i]->abstime)
+#define HEAP_ASSIGN(pos, tmr) ((heap[pos] = (tmr))->heap_pos = (pos))
+
+static Timer **heap;
+static int num_timers = 0;
+static int heap_size = 0;
+
+static void timer_botch __P((void*));
+static Timer *add_timer __P((Timer *));
+
+Timer *timer_set_rel(time_rel, proc, arg)
+ long time_rel;
+ void (*proc) __P((void *));
+ void *arg;
+{
+ Timer *new_t;
+
+ new_t = (Timer *) malloc(sizeof(*new_t));
+ if (new_t == NULL)
+ return(NULL);
+ new_t->abstime = time_rel + time(NULL);
+ new_t->func = proc;
+ new_t->arg = arg;
+ return add_timer(new_t);
+}
+
+void
+timer_reset(tmr)
+ Timer *tmr;
+{
+ int pos, min;
+
+ /* Free the timer, saving its heap position. */
+ pos = tmr->heap_pos;
+ free(tmr);
+
+ if (pos != num_timers - 1) {
+ /* Replace the timer with the last timer in the heap and
+ * restore the heap, propagating the timer either up or
+ * down, depending on which way it violates the heap
+ * property to insert the last timer in place of the
+ * deleted timer. */
+ if (pos > 0 && TIME(num_timers - 1) < TIME(PARENT(pos))) {
+ do {
+ HEAP_ASSIGN(pos, heap[PARENT(pos)]);
+ pos = PARENT(pos);
+ } while (pos > 0 && TIME(num_timers - 1) < TIME(PARENT(pos)));
+ HEAP_ASSIGN(pos, heap[num_timers - 1]);
+ } else {
+ while (CHILD2(pos) < num_timers) {
+ min = num_timers - 1;
+ if (TIME(CHILD1(pos)) < TIME(min))
+ min = CHILD1(pos);
+ if (TIME(CHILD2(pos)) < TIME(min))
+ min = CHILD2(pos);
+ HEAP_ASSIGN(pos, heap[min]);
+ pos = min;
+ }
+ if (pos != num_timers - 1)
+ HEAP_ASSIGN(pos, heap[num_timers - 1]);
+ }
+ }
+ num_timers--;
+}
+
+
+#define set_timeval(t,s) ((t).tv_sec=(s),(t).tv_usec=0,(t))
+
+static Timer *
+add_timer(new)
+ Timer *new;
+{
+ int pos;
+
+ /* Create or resize the heap as necessary. */
+ if (heap_size == 0) {
+ heap_size = INITIAL_HEAP_SIZE;
+ heap = (Timer **) malloc(heap_size * sizeof(Timer *));
+ } else if (num_timers >= heap_size) {
+ heap_size = heap_size * 2 + DELTA;
+ heap = (Timer **) realloc(heap, heap_size * sizeof(Timer *));
+ }
+ if (!heap) {
+ free(new);
+ return NULL;
+ }
+
+ /* Insert the Timer *into the heap. */
+ pos = num_timers;
+ while (pos > 0 && new->abstime < TIME(PARENT(pos))) {
+ HEAP_ASSIGN(pos, heap[PARENT(pos)]);
+ pos = PARENT(pos);
+ }
+ HEAP_ASSIGN(pos, new);
+ num_timers++;
+
+ return new;
+}
+
+void
+timer_process()
+{
+ Timer *t;
+ timer_proc func;
+ void *arg;
+ int valid = 0;
+
+ if (num_timers == 0 || heap[0]->abstime > time(NULL))
+ return;
+
+ /* Remove the first timer from the heap, remembering its
+ * function and argument. */
+ t = heap[0];
+ func = t->func;
+ arg = t->arg;
+ t->func = timer_botch;
+ t->arg = NULL;
+ timer_reset(t);
+
+ /* Run the function. */
+ func(arg);
+}
+
+struct timeval *
+timer_timeout(tvbuf)
+ struct timeval *tvbuf;
+{
+ if (num_timers > 0) {
+ tvbuf->tv_sec = heap[0]->abstime - time(NULL);
+ if (tvbuf->tv_sec < 0)
+ tvbuf->tv_sec = 0;
+ tvbuf->tv_usec = 0;
+ return tvbuf;
+ } else {
+ return NULL;
+ }
+}
+
+static void
+timer_botch(arg)
+ void *arg;
+{
+ syslog(LOG_CRIT, "timer botch\n");
+ abort();
+}
+
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains definitions used by timer.c
+ *
+ * Created by: John T. Kohl
+ * Derived from timer_manager_.h by Ken Raeburn
+ *
+ * $Id: timer.h,v 1.2 1999/01/22 23:19:59 ghudson Exp $
+ *
+ */
+
+#ifndef __TIMER_H
+
+/*
+ * timer_manager_ -- routines for handling timers in login_shell
+ * (and elsewhere)
+ *
+ * Copyright 1986 Student Information Processing Board,
+ * Massachusetts Institute of Technology
+ *
+ * written by Ken Raeburn
+
+Permission to use, copy, modify, and distribute this
+software and its documentation for any purpose and without
+fee is hereby granted, provided that the above copyright
+notice appear in all copies and that both that copyright
+notice and this permission notice appear in supporting
+documentation, and that the name of M.I.T. and the Student
+Information Processing Board not be used in
+advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+M.I.T. and the Student Information Processing Board
+make no representations about the suitability of
+this software for any purpose. It is provided "as is"
+without express or implied warranty.
+
+ */
+
+typedef void (*timer_proc) __P((void *));
+
+typedef struct _Timer {
+ int heap_pos; /* Position in timer heap */
+ long abstime;
+ timer_proc func;
+ void *arg;
+} Timer;
+
+Timer *timer_set_rel __P((long, timer_proc, void *));
+Timer *timer_set_abs __P((long, timer_proc, void *));
+void timer_reset __P((Timer *));
+void timer_process __P((void));
+struct timeval *timer_timeout __P((struct timeval *tvbuf));
+
+#endif /* __TIMER_H */
+
--- /dev/null
+.\" $Id: zhm.8,v 1.10 2000/04/05 14:57:36 ghudson Exp $
+.\"
+.\" Copyright 1987, 1988 by the Massachusetts Institute of Technology
+.\" All rights reserved. The file /usr/include/zephyr/mit-copyright.h
+.\" specifies the terms and conditions for redistribution.
+.\"
+.\"
+.TH ZHM 8 "November 1, 1988" "MIT Project Athena"
+.ds ]W MIT Project Athena
+.SH NAME
+zhm \- Zephyr HostManager
+.SH SYNOPSIS
+.B /etc/athena/zhm
+[
+.BI -d
+] [
+.BI -h
+] [
+.BI -r
+] [
+.BI -i
+] [
+.BI -f
+] [
+.BI server
+.BI ...
+]
+.SH DESCRIPTION
+.I Zhm
+is the link between a client machine and the zephyr server. All
+notices sent from programs on the client are funneled through
+.I zhm.
+This allows all client programs to be much simpler in function, since
+the HostManager is responsible for handling errors, retransmitting
+lost notices, and holding all notices until they are acknowledged.
+.PP
+The
+.I -d
+option turns on debugging mode, and sends its information to syslog
+LOG_DAEMON messages.
+.PP
+The
+.I -h
+option causes
+.I zhm
+to send a shutdown message and exit upon delivery of a SIGHUP signal.
+The normal action on SIGHUP is to send a flush notice to the zephyr server.
+.PP
+The
+.I -r
+option causes
+.I zhm
+to send a boot notice to the server and exit when the notice is acknowledged.
+.PP
+The
+.I -i
+option indicates that
+.I zhm
+is being started by
+.I inetd(8).
+When this option is specified,
+.I zhm
+assumes that file descriptor zero (0) is bound to the UDP datagram port
+designated for hostmanager use. In this mode, SIGHUP is handled as if the
+.I -h
+option were specified.
+.PP
+The
+.I -f
+option disables the "flush" operation which allows any client to flush
+all subscriptions for the host.
+.PP
+The optional
+.I server
+arguments are used to replace the set of server names supplied by
+the
+.I hesiod(3)
+name server.
+.SH SEE ALSO
+zephyr(1), zephyrd(8), inetd(8)
+.br
+Project Athena Technical Plan Section E.4.1, `Zephyr Notification
+Service'
+.SH AUTHOR
+.PP
+David C. Jedlinsky, MIT Project Athena
+.SH RESTRICTIONS
+Copyright (c) 1987,1988 by the Massachusetts Institute of Technology.
+All Rights Reserved.
+.br
+.I zephyr(1)
+specifies the terms and conditions for redistribution.
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains the hostmanager client program.
+ *
+ * Created by: David C. Jedlinsky
+ *
+ * $Id: zhm.c,v 1.61 2000/04/05 14:57:36 ghudson Exp $
+ *
+ * Copyright (c) 1987,1991 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include "zhm.h"
+
+static const char rcsid_hm_c[] = "$Id: zhm.c,v 1.61 2000/04/05 14:57:36 ghudson Exp $";
+
+#ifdef HAVE_HESIOD
+int use_hesiod = 0;
+#endif
+
+#ifdef macII
+#define srandom srand48
+#endif
+
+#define PIDDIR "/var/athena/"
+
+int hmdebug, rebootflag, noflushflag, errflg, dieflag, inetd, oldpid, nofork;
+int no_server = 1, nservchang, nserv, nclt;
+int booting = 1, timeout_type, deactivated = 1;
+long starttime;
+u_short cli_port;
+struct sockaddr_in cli_sin, serv_sin, from;
+int numserv;
+char **serv_list = NULL;
+char prim_serv[MAXHOSTNAMELEN], cur_serv[MAXHOSTNAMELEN];
+char *zcluster;
+int deactivating = 0;
+int terminating = 0;
+struct hostent *hp;
+char hostname[MAXHOSTNAMELEN], loopback[4];
+char PidFile[128];
+
+static RETSIGTYPE deactivate __P((void));
+static RETSIGTYPE terminate __P((void));
+static void choose_server __P((void));
+static void init_hm __P((void));
+static void detach __P((void));
+static void send_stats __P((ZNotice_t *, struct sockaddr_in *));
+static char *strsave __P((const char *));
+extern int optind;
+
+static RETSIGTYPE deactivate()
+{
+ deactivating = 1;
+}
+
+static RETSIGTYPE terminate()
+{
+ terminating = 1;
+}
+
+main(argc, argv)
+char *argv[];
+{
+ ZNotice_t notice;
+ ZPacket_t packet;
+ Code_t ret;
+ int opt, pak_len, i, j = 0, fd, count;
+ fd_set readers;
+ struct timeval tv;
+
+ sprintf(PidFile, "%szhm.pid", PIDDIR);
+
+ if (gethostname(hostname, MAXHOSTNAMELEN) < 0) {
+ printf("Can't find my hostname?!\n");
+ exit(-1);
+ }
+ prim_serv[0] = '\0';
+ while ((opt = getopt(argc, argv, "drhinf")) != EOF)
+ switch(opt) {
+ case 'd':
+ hmdebug = 1;
+ break;
+ case 'h':
+ /* Die on SIGHUP */
+ dieflag = 1;
+ break;
+ case 'r':
+ /* Reboot host -- send boot notice -- and exit */
+ rebootflag= 1;
+ break;
+ case 'i':
+ /* inetd operation: don't do bind ourselves, fd 0 is
+ already connected to a socket. Implies -h */
+ inetd = 1;
+ dieflag = 1;
+ break;
+ case 'n':
+ nofork = 1;
+ break;
+ case 'f':
+ noflushflag = 1;
+ break;
+ case '?':
+ default:
+ errflg++;
+ break;
+ }
+ if (errflg) {
+ fprintf(stderr, "Usage: %s [-d] [-h] [-r] [-n] [server]\n", argv[0]);
+ exit(2);
+ }
+
+ numserv = 0;
+
+ /* Override server argument? */
+ if (optind < argc) {
+ if ((hp = gethostbyname(argv[optind++])) == NULL) {
+ printf("Unknown server name: %s\n", argv[optind-1]);
+ } else {
+ strncpy(prim_serv, hp->h_name, sizeof(prim_serv));
+ prim_serv[sizeof(prim_serv) - 1] = '\0';
+ }
+
+ /* argc-optind is the # of other servers on the command line */
+ serv_list = (char **) malloc((argc - optind + 2) * sizeof(char *));
+ if (serv_list == NULL) {
+ printf("Out of memory.\n");
+ exit(-5);
+ }
+ serv_list[numserv++] = prim_serv;
+ for (; optind < argc; optind++) {
+ if ((hp = gethostbyname(argv[optind])) == NULL) {
+ printf("Unknown server name '%s', ignoring\n", argv[optind]);
+ continue;
+ }
+ serv_list[numserv++] = strsave(hp->h_name);
+ }
+ serv_list[numserv] = NULL;
+ }
+#ifdef HAVE_HESIOD
+ else
+ use_hesiod = 1;
+#endif
+
+ choose_server();
+ if (*prim_serv == '\0') {
+ printf("No valid primary server found, exiting.\n");
+ exit(ZERR_SERVNAK);
+ }
+ init_hm();
+
+ DPR2("zephyr server port: %u\n", ntohs(serv_sin.sin_port));
+ DPR2("zephyr client port: %u\n", ntohs(cli_port));
+
+ /* Main loop */
+ for ever {
+ /* Wait for incoming packets or queue timeouts. */
+ DPR("Waiting for a packet...");
+ fd = ZGetFD();
+ FD_ZERO(&readers);
+ FD_SET(fd, &readers);
+ count = select(fd + 1, &readers, NULL, NULL, timer_timeout(&tv));
+ if (count == -1 && errno != EINTR) {
+ syslog(LOG_CRIT, "select() failed: %m");
+ die_gracefully();
+ }
+
+ if (terminating)
+ die_gracefully();
+
+ if (deactivating) {
+ deactivating = 0;
+ if (dieflag) {
+ die_gracefully();
+ } else {
+ choose_server();
+ send_flush_notice(HM_FLUSH);
+ deactivated = 1;
+ }
+ }
+
+ timer_process();
+
+ if (count > 0) {
+ ret = ZReceivePacket(packet, &pak_len, &from);
+ if ((ret != ZERR_NONE) && (ret != EINTR)){
+ Zperr(ret);
+ com_err("hm", ret, "receiving notice");
+ } else if (ret != EINTR) {
+ /* Where did it come from? */
+ if ((ret = ZParseNotice(packet, pak_len, ¬ice))
+ != ZERR_NONE) {
+ Zperr(ret);
+ com_err("hm", ret, "parsing notice");
+ } else {
+ DPR("Got a packet.\n");
+ DPR("notice:\n");
+ DPR2("\tz_kind: %d\n", notice.z_kind);
+ DPR2("\tz_port: %u\n", ntohs(notice.z_port));
+ DPR2("\tz_class: %s\n", notice.z_class);
+ DPR2("\tz_class_inst: %s\n", notice.z_class_inst);
+ DPR2("\tz_opcode: %s\n", notice.z_opcode);
+ DPR2("\tz_sender: %s\n", notice.z_sender);
+ DPR2("\tz_recip: %s\n", notice.z_recipient);
+ DPR2("\tz_def_format: %s\n", notice.z_default_format);
+ DPR2("\tz_message: %s\n", notice.z_message);
+ if (memcmp(loopback, &from.sin_addr, 4) &&
+ ((notice.z_kind == SERVACK) ||
+ (notice.z_kind == SERVNAK) ||
+ (notice.z_kind == HMCTL))) {
+ server_manager(¬ice);
+ } else {
+ if (!memcmp(loopback, &from.sin_addr, 4) &&
+ ((notice.z_kind == UNSAFE) ||
+ (notice.z_kind == UNACKED) ||
+ (notice.z_kind == ACKED) ||
+ (notice.z_kind == HMCTL))) {
+ /* Client program... */
+ if (deactivated) {
+ send_boot_notice(HM_BOOT);
+ deactivated = 0;
+ }
+ transmission_tower(¬ice, packet, pak_len);
+ DPR2("Pending = %d\n", ZPending());
+ } else {
+ if (notice.z_kind == STAT) {
+ send_stats(¬ice, &from);
+ } else {
+ syslog(LOG_INFO,
+ "Unknown notice type: %d",
+ notice.z_kind);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+static void choose_server()
+{
+ int i = 0;
+ char **clust_info, **cpp;
+
+#ifdef HAVE_HESIOD
+ if (use_hesiod) {
+
+ /* Free up any previously used resources */
+ if (prim_serv[0])
+ i = 1;
+ while (i < numserv)
+ free(serv_list[i++]);
+ if (serv_list)
+ free(serv_list);
+
+ numserv = 0;
+ prim_serv[0] = '\0';
+
+ if ((clust_info = hes_resolve(hostname, "CLUSTER")) == NULL) {
+ zcluster = NULL;
+ } else {
+ for (cpp = clust_info; *cpp; cpp++) {
+ /* Remove the following check once we have changed over to
+ * new Hesiod format (i.e. ZCLUSTER.sloc lookup, no primary
+ * server
+ */
+ if (!strncasecmp("ZEPHYR", *cpp, 6)) {
+ register char *c;
+
+ if ((c = strchr(*cpp, ' ')) == 0) {
+ printf("Hesiod error getting primary server info.\n");
+ } else {
+ strncpy(prim_serv, c+1, sizeof(prim_serv));
+ prim_serv[sizeof(prim_serv) - 1] = '\0';
+ }
+ break;
+ }
+ if (!strncasecmp("ZCLUSTER", *cpp, 9)) {
+ register char *c;
+
+ if ((c = strchr(*cpp, ' ')) == 0) {
+ printf("Hesiod error getting zcluster info.\n");
+ } else {
+ if ((zcluster = malloc((unsigned)(strlen(c+1)+1)))
+ != NULL) {
+ strcpy(zcluster, c+1);
+ } else {
+ printf("Out of memory.\n");
+ exit(-5);
+ }
+ }
+ break;
+ }
+ }
+ for (cpp = clust_info; *cpp; cpp++)
+ free(*cpp);
+ }
+
+ if (zcluster == NULL) {
+ if ((zcluster = malloc((unsigned)(strlen("zephyr")+1))) != NULL)
+ strcpy(zcluster, "zephyr");
+ else {
+ printf("Out of memory.\n");
+ exit(-5);
+ }
+ }
+ while ((serv_list = hes_resolve(zcluster, "sloc")) == (char **)NULL) {
+ syslog(LOG_ERR, "No servers or no hesiod");
+ /* wait a bit, and try again */
+ sleep(30);
+ }
+ cpp = (char **) malloc(2 * sizeof(char *));
+ if (cpp == NULL) {
+ printf("Out of memory.\n");
+ exit(-5);
+ }
+ if (prim_serv[0])
+ cpp[numserv++] = prim_serv;
+ for (i = 0; serv_list[i]; i++) {
+ /* copy in non-duplicates */
+ /* assume the names returned in the sloc are full domain names */
+ if (!prim_serv[0] || strcasecmp(prim_serv, serv_list[i])) {
+ cpp = (char **) realloc(cpp, (numserv+2) * sizeof(char *));
+ if (cpp == NULL) {
+ printf("Out of memory.\n");
+ exit(-5);
+ }
+ cpp[numserv++] = strsave(serv_list[i]);
+ }
+ }
+ for (i = 0; serv_list[i]; i++)
+ free(serv_list[i]);
+ cpp[numserv] = NULL;
+ serv_list = cpp;
+ }
+#endif
+
+ if (!prim_serv[0] && numserv) {
+ srandom(time(NULL));
+ strncpy(prim_serv, serv_list[random() % numserv], sizeof(prim_serv));
+ prim_serv[sizeof(prim_serv) - 1] = '\0';
+ }
+}
+
+static void init_hm()
+{
+ struct servent *sp;
+ Code_t ret;
+ FILE *fp;
+#ifdef _POSIX_VERSION
+ struct sigaction sa;
+#endif
+
+ starttime = time((time_t *)0);
+ OPENLOG("hm", LOG_PID, LOG_DAEMON);
+
+ ZSetServerState(1); /* Aargh!!! */
+ if ((ret = ZInitialize()) != ZERR_NONE) {
+ Zperr(ret);
+ com_err("hm", ret, "initializing");
+ closelog();
+ exit(-1);
+ }
+ init_queue();
+
+ if (*prim_serv == '\0') {
+ strncpy(prim_serv, *serv_list, sizeof(prim_serv));
+ prim_serv[sizeof(prim_serv) - 1] = '\0';
+ }
+
+ loopback[0] = 127;
+ loopback[1] = 0;
+ loopback[2] = 0;
+ loopback[3] = 1;
+
+ if (inetd) {
+ ZSetFD(0); /* fd 0 is on the socket, thanks to inetd */
+ } else {
+ /* Open client socket, for receiving client and server notices */
+ sp = getservbyname(HM_SVCNAME, "udp");
+ cli_port = (sp) ? sp->s_port : HM_SVC_FALLBACK;
+
+ if ((ret = ZOpenPort(&cli_port)) != ZERR_NONE) {
+ Zperr(ret);
+ com_err("hm", ret, "opening port");
+ exit(ret);
+ }
+ }
+ cli_sin = ZGetDestAddr();
+
+ sp = getservbyname(SERVER_SVCNAME, "udp");
+ memset(&serv_sin, 0, sizeof(struct sockaddr_in));
+ serv_sin.sin_port = (sp) ? sp->s_port : SERVER_SVC_FALLBACK;
+
+#ifndef DEBUG
+ if (!inetd && !nofork)
+ detach();
+
+ /* Write pid to file */
+ fp = fopen(PidFile, "w");
+ if (fp != NULL) {
+ fprintf(fp, "%d\n", getpid());
+ fclose(fp);
+ }
+#endif /* DEBUG */
+
+ if (hmdebug) {
+ syslog(LOG_INFO, "Debugging on.");
+ }
+
+ /* Set up communications with server */
+ /* target is SERVER_SVCNAME port on server machine */
+
+ serv_sin.sin_family = AF_INET;
+
+ /* who to talk to */
+ if ((hp = gethostbyname(prim_serv)) == NULL) {
+ DPR("gethostbyname failed\n");
+ find_next_server(NULL);
+ } else {
+ DPR2("Server = %s\n", prim_serv);
+ strncpy(cur_serv, prim_serv, sizeof(cur_serv));
+ cur_serv[sizeof(cur_serv) - 1] = '\0';
+ memcpy(&serv_sin.sin_addr, hp->h_addr, 4);
+ }
+
+ send_boot_notice(HM_BOOT);
+ deactivated = 0;
+
+#ifdef _POSIX_VERSION
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sa.sa_handler = deactivate;
+ sigaction(SIGHUP, &sa, (struct sigaction *)0);
+ sa.sa_handler = terminate;
+ sigaction(SIGTERM, &sa, (struct sigaction *)0);
+#else
+ signal(SIGHUP, deactivate);
+ signal(SIGTERM, terminate);
+#endif
+}
+
+static void detach()
+{
+ /* detach from terminal and fork. */
+ register int i, x = ZGetFD();
+ register long size;
+
+ if (i = fork()) {
+ if (i < 0)
+ perror("fork");
+ exit(0);
+ }
+#ifdef _POSIX_VERSION
+ size = sysconf(_SC_OPEN_MAX);
+#else
+ size = getdtablesize();
+#endif
+ for (i = 0; i < size; i++)
+ if (i != x)
+ close(i);
+
+ if ((i = open("/dev/tty", O_RDWR, 0666)) < 0)
+ ; /* Can't open tty, but don't flame about it. */
+ else {
+#ifdef TIOCNOTTY
+ /* Necessary for old non-POSIX systems which automatically assign
+ * an opened tty as the controlling terminal of a process which
+ * doesn't already have one. POSIX systems won't include
+ * <sys/ioctl.h> (see ../h/sysdep.h); if TIOCNOTTY is defined anyway,
+ * this is unnecessary but won't hurt. */
+ ioctl(i, TIOCNOTTY, (caddr_t) 0);
+#endif
+ close(i);
+ }
+#ifdef _POSIX_VERSION
+ setsid();
+#endif
+}
+
+static char version[BUFSIZ];
+
+static void send_stats(notice, sin)
+ ZNotice_t *notice;
+ struct sockaddr_in *sin;
+{
+ ZNotice_t newnotice;
+ Code_t ret;
+ char *bfr;
+ char *list[20];
+ int len, i, nitems = 10;
+ unsigned long size;
+
+ newnotice = *notice;
+
+ if ((ret = ZSetDestAddr(sin)) != ZERR_NONE) {
+ Zperr(ret);
+ com_err("hm", ret, "setting destination");
+ }
+ newnotice.z_kind = HMACK;
+
+ list[0] = (char *) malloc(MAXHOSTNAMELEN);
+ if (list[0] == NULL) {
+ printf("Out of memory.\n");
+ exit(-5);
+ }
+ strcpy(list[0], cur_serv);
+ list[1] = (char *) malloc(64);
+ if (list[1] == NULL) {
+ printf("Out of memory.\n");
+ exit(-5);
+ }
+ sprintf(list[1], "%d", queue_len());
+ list[2] = (char *) malloc(64);
+ if (list[2] == NULL) {
+ printf("Out of memory.\n");
+ exit(-5);
+ }
+ sprintf(list[2], "%d", nclt);
+ list[3] = (char *) malloc(64);
+ if (list[3] == NULL) {
+ printf("Out of memory.\n");
+ exit(-5);
+ }
+ sprintf(list[3], "%d", nserv);
+ list[4] = (char *) malloc(64);
+ if (list[4] == NULL) {
+ printf("Out of memory.\n");
+ exit(-5);
+ }
+ sprintf(list[4], "%d", nservchang);
+ list[5] = (char *) malloc(64);
+ if (list[5] == NULL) {
+ printf("Out of memory.\n");
+ exit(-5);
+ }
+ strncpy(list[5], rcsid_hm_c, 64);
+ list[5][63] = '\0';
+
+ list[6] = (char *) malloc(64);
+ if (list[6] == NULL) {
+ printf("Out of memory.\n");
+ exit(-5);
+ }
+ if (no_server)
+ sprintf(list[6], "yes");
+ else
+ sprintf(list[6], "no");
+ list[7] = (char *) malloc(64);
+ if (list[7] == NULL) {
+ printf("Out of memory.\n");
+ exit(-5);
+ }
+ sprintf(list[7], "%ld", time((time_t *)0) - starttime);
+#ifdef adjust_size
+ size = (unsigned long)sbrk(0);
+ adjust_size (size);
+#else
+ size = -1;
+#endif
+ list[8] = (char *)malloc(64);
+ if (list[8] == NULL) {
+ printf("Out of memory.\n");
+ exit(-5);
+ }
+ sprintf(list[8], "%ld", size);
+ list[9] = (char *)malloc(32);
+ if (list[9] == NULL) {
+ printf("Out of memory.\n");
+ exit(-5);
+ }
+ strncpy(list[9], MACHINE_TYPE, 32);
+ list[9][31] = '\0';
+
+ /* Since ZFormatRaw* won't change the version number on notices,
+ we need to set the version number explicitly. This code is taken
+ from Zinternal.c, function Z_FormatHeader */
+ if (!*version)
+ sprintf(version, "%s%d.%d", ZVERSIONHDR, ZVERSIONMAJOR,
+ ZVERSIONMINOR);
+ newnotice.z_version = version;
+
+ if ((ret = ZFormatRawNoticeList(&newnotice, list, nitems, &bfr,
+ &len)) != ZERR_NONE) {
+ syslog(LOG_INFO, "Couldn't format stats packet");
+ } else {
+ if ((ret = ZSendPacket(bfr, len, 0)) != ZERR_NONE) {
+ Zperr(ret);
+ com_err("hm", ret, "sending stats");
+ }
+ }
+ free(bfr);
+ for(i=0;i<nitems;i++)
+ free(list[i]);
+}
+
+void die_gracefully()
+{
+ syslog(LOG_INFO, "Terminate signal caught...");
+ send_flush_notice(HM_FLUSH);
+ unlink(PidFile);
+ closelog();
+ exit(0);
+}
+
+static char *strsave(sp)
+ const char *sp;
+{
+ register char *ret;
+
+ if((ret = malloc((unsigned) strlen(sp)+1)) == NULL) {
+ abort();
+ }
+ strcpy(ret,sp);
+ return(ret);
+}
--- /dev/null
+#ifndef __HM_H__
+#define __HM_H__
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains the hostmanager header file.
+ *
+ * Created by: David C. Jedlinsky
+ *
+ * $Id: zhm.h,v 1.22 1999/01/22 23:20:03 ghudson Exp $
+ * $Zephyr: /mit/zephyr/src.rw/zhm/RCS/zhm.h,v 1.13 90/10/19 07:11:48 raeburn Exp $
+ *
+ * Copyright (c) 1987, 1991 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <zephyr/mit-copyright.h>
+#include <internal.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include "timer.h"
+
+/* These macros are for insertion into and deletion from a singly-linked list
+ * with back pointers to the previous element's next pointer. In order to
+ * make these macros act like expressions, they use the comma operator for
+ * sequenced evaluations of assignment, and "a && b" for "evaluate assignment
+ * b if expression a is true". */
+#define LIST_INSERT(head, elem) \
+ ((elem)->next = *(head), \
+ (*head) && ((*(head))->prev_p = &(elem)->next), \
+ (*head) = (elem), (elem)->prev_p = (head))
+#define LIST_DELETE(elem) \
+ (*(elem)->prev_p = (elem)->next, \
+ (elem)->next && ((elem)->next->prev_p = (elem)->prev_p))
+
+#ifdef DEBUG
+#define DPR(a) fprintf(stderr, a); fflush(stderr)
+#define DPR2(a,b) fprintf(stderr, a, b); fflush(stderr)
+#define Zperr(e) fprintf(stderr, "Error = %d\n", e)
+#else
+#define DPR(a)
+#define DPR2(a,b)
+#define Zperr(e)
+#endif
+
+#define ever (;;)
+
+#define SERV_TIMEOUT 5
+#define BOOTING 1
+#define NOTICES 2
+
+/* main.c */
+void die_gracefully __P((void));
+
+/* zhm_client.c */
+void transmission_tower __P((ZNotice_t *, char *, int));
+Code_t send_outgoing __P((ZNotice_t *));
+
+/* queue.c */
+void init_queue __P((void));
+Code_t add_notice_to_queue __P((ZNotice_t *, char *, struct sockaddr_in *,
+ int));
+Code_t remove_notice_from_queue __P((ZNotice_t *, ZNotice_Kind_t *,
+ struct sockaddr_in *));
+void retransmit_queue __P((struct sockaddr_in *));
+void disable_queue_retransmits __P((void));
+int queue_len __P((void));
+
+struct sockaddr_in serv_sin;
+extern int rexmit_times[];
+
+#ifdef vax
+#define use_etext
+#endif /* vax */
+
+#ifdef ibm032
+#define adjust_size(size) size -= 0x10000000
+#endif /* ibm032 */
+
+#if defined(sun) && (defined (SUN4_ARCH) || defined (sparc))
+#define use_etext
+#endif
+
+#ifdef _AIX
+#ifdef i386
+#define adjust_size(size) size -= 0x400000
+#endif
+#ifdef _IBMR2
+#define adjust_size(size) size -= 0x20000000
+#endif
+#endif
+
+#if (defined(ultrix) || defined(sgi)) && defined(mips)
+#define adjust_size(size) size -= 0x10000000
+#endif /* (ultrix || sgi) && mips */
+
+#if defined(__alpha)
+#define adjust_size(size) size -= 0x140000000
+#endif /* alpha */
+
+#ifdef use_etext
+extern int etext;
+#define adjust_size(size) size -= (unsigned int) &etext;
+#undef use_etext
+#endif
+
+#endif
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains the hostmanager <--> client interaction routines.
+ *
+ * Created by: David C. Jedlinsky
+ *
+ * $Id: zhm_client.c,v 1.12 2000/04/05 14:57:37 ghudson Exp $
+ *
+ * Copyright (c) 1987 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include "zhm.h"
+
+#ifndef lint
+#ifndef SABER
+static char rcsid_hm_client_c[] = "$Id: zhm_client.c,v 1.12 2000/04/05 14:57:37 ghudson Exp $";
+#endif /* SABER */
+#endif /* lint */
+
+extern int no_server, nclt, deactivated, noflushflag;
+extern struct sockaddr_in cli_sin, serv_sin, from;
+
+void transmission_tower(notice, packet, pak_len)
+ ZNotice_t *notice;
+ char *packet;
+ int pak_len;
+{
+ ZNotice_t gack;
+ Code_t ret;
+ struct sockaddr_in gsin;
+
+ nclt++;
+ if (notice->z_kind == HMCTL) {
+ if (!strcmp(notice->z_opcode, CLIENT_FLUSH)) {
+ if (noflushflag)
+ syslog(LOG_INFO, "Client requested hm flush (disabled).");
+ else {
+ send_flush_notice(HM_FLUSH);
+ deactivated = 1;
+ }
+ } else if (!strcmp(notice->z_opcode, CLIENT_NEW_SERVER)) {
+ new_server((char *)NULL);
+ } else {
+ syslog (LOG_INFO, "Bad control notice from client.");
+ }
+ return;
+ } else {
+ if (notice->z_kind != UNSAFE) {
+ gack = *notice;
+ gack.z_kind = HMACK;
+ gack.z_message_len = 0;
+ gack.z_multinotice = "";
+ gsin = cli_sin;
+ gsin.sin_port = from.sin_port;
+ if (gack.z_port == 0)
+ gack.z_port = from.sin_port;
+ DPR2 ("Client Port = %u\n", ntohs(gack.z_port));
+ notice->z_port = gack.z_port;
+ if ((ret = ZSetDestAddr(&gsin)) != ZERR_NONE) {
+ Zperr(ret);
+ com_err("hm", ret, "setting destination");
+ }
+ /* Bounce ACK to library */
+ if ((ret = send_outgoing(&gack)) != ZERR_NONE) {
+ Zperr(ret);
+ com_err("hm", ret, "sending raw notice");
+ }
+ }
+ }
+ if (!no_server) {
+ DPR2 ("Server Port = %u\n", ntohs(serv_sin.sin_port));
+ if ((ret = ZSetDestAddr(&serv_sin)) != ZERR_NONE) {
+ Zperr(ret);
+ com_err("hm", ret, "setting destination");
+ }
+ if ((ret = send_outgoing(notice)) != ZERR_NONE) {
+ Zperr(ret);
+ com_err("hm", ret, "while sending raw notice");
+ }
+ }
+ if (add_notice_to_queue(notice, packet, &gsin, pak_len) != ZERR_NONE)
+ syslog(LOG_INFO, "Hey! Insufficient memory to add notice to queue!");
+}
+
+Code_t
+send_outgoing(notice)
+ZNotice_t *notice;
+{
+ Code_t retval;
+ char *packet;
+ int length;
+
+ if (!(packet = (char *) malloc((unsigned)sizeof(ZPacket_t))))
+ return(ENOMEM);
+
+ if ((retval = ZFormatSmallRawNotice(notice, packet, &length))
+ != ZERR_NONE) {
+ free(packet);
+ return(retval);
+ }
+ retval = ZSendPacket(packet, length, 0);
+ free(packet);
+ return(retval);
+}
+
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains the hostmanager <--> server interaction routines.
+ *
+ * Created by: David C. Jedlinsky
+ *
+ * $Id: zhm_server.c,v 1.17 1999/10/14 18:37:35 ghudson Exp $
+ *
+ * Copyright (c) 1987 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include "zhm.h"
+
+#ifndef lint
+#ifndef SABER
+static char rcsid_hm_server_c[] = "$Id: zhm_server.c,v 1.17 1999/10/14 18:37:35 ghudson Exp $";
+#endif /* SABER */
+#endif /* lint */
+
+static void boot_timeout __P((void *));
+
+static Timer *boot_timer = NULL;
+
+int serv_loop = 0;
+extern u_short cli_port;
+extern struct sockaddr_in serv_sin, from;
+extern int timeout_type, hmdebug, nservchang, booting, nserv, no_server;
+extern int deactivated, rebootflag;
+extern int numserv;
+extern char **serv_list;
+extern char cur_serv[], prim_serv[];
+extern void die_gracefully();
+
+void hm_control(), send_back(), new_server();
+
+/* Argument is whether we are actually booting, or just attaching
+ * after a server switch */
+void
+send_boot_notice(op)
+char *op;
+{
+ ZNotice_t notice;
+ Code_t ret;
+
+ /* Set up server notice */
+ notice.z_kind = HMCTL;
+ notice.z_port = cli_port;
+ notice.z_class = ZEPHYR_CTL_CLASS;
+ notice.z_class_inst = ZEPHYR_CTL_HM;
+ notice.z_opcode = op;
+ notice.z_sender = "HM";
+ notice.z_recipient = "";
+ notice.z_default_format = "";
+ notice.z_num_other_fields = 0;
+ notice.z_message_len = 0;
+
+ /* Notify server that this host is here */
+ if ((ret = ZSetDestAddr(&serv_sin)) != ZERR_NONE) {
+ Zperr(ret);
+ com_err("hm", ret, "setting destination");
+ }
+ if ((ret = ZSendNotice(¬ice, ZNOAUTH)) != ZERR_NONE) {
+ Zperr(ret);
+ com_err("hm", ret, "sending startup notice");
+ }
+ boot_timer = timer_set_rel(SERV_TIMEOUT, boot_timeout, NULL);
+}
+
+/* Argument is whether we are detaching or really going down */
+void
+send_flush_notice(op)
+char *op;
+{
+ ZNotice_t notice;
+ Code_t ret;
+
+ /* Set up server notice */
+ notice.z_kind = HMCTL;
+ notice.z_port = cli_port;
+ notice.z_class = ZEPHYR_CTL_CLASS;
+ notice.z_class_inst = ZEPHYR_CTL_HM;
+ notice.z_opcode = op;
+ notice.z_sender = "HM";
+ notice.z_recipient = "";
+ notice.z_default_format = "";
+ notice.z_num_other_fields = 0;
+ notice.z_message_len = 0;
+
+ /* Tell server to lose us */
+ if ((ret = ZSetDestAddr(&serv_sin)) != ZERR_NONE) {
+ Zperr(ret);
+ com_err("hm", ret, "setting destination");
+ }
+ if ((ret = ZSendNotice(¬ice, ZNOAUTH)) != ZERR_NONE) {
+ Zperr(ret);
+ com_err("hm", ret, "sending flush notice");
+ }
+}
+
+void
+find_next_server(sugg_serv)
+char *sugg_serv;
+{
+ struct hostent *hp;
+ int done = 0;
+ char **parse = serv_list;
+ char *new_serv;
+
+ if (sugg_serv) {
+ do {
+ if (!strcmp(*parse, sugg_serv))
+ done = 1;
+ } while ((done == 0) && (*++parse != NULL));
+ }
+ if (done) {
+ if ((hp = gethostbyname(sugg_serv)) != NULL) {
+ DPR2 ("Server = %s\n", sugg_serv);
+ (void)strncpy(cur_serv, sugg_serv, MAXHOSTNAMELEN);
+ cur_serv[MAXHOSTNAMELEN - 1] = '\0';
+ if (hmdebug)
+ syslog(LOG_DEBUG, "Suggested server: %s\n", sugg_serv);
+ } else {
+ done = 0;
+ }
+ }
+ while (!done) {
+ if ((++serv_loop > 3) && (strcmp(cur_serv, prim_serv))) {
+ serv_loop = 0;
+ if ((hp = gethostbyname(prim_serv)) != NULL) {
+ DPR2 ("Server = %s\n", prim_serv);
+ (void)strncpy(cur_serv, prim_serv, MAXHOSTNAMELEN);
+ cur_serv[MAXHOSTNAMELEN - 1] = '\0';
+ done = 1;
+ break;
+ }
+ }
+
+ switch (numserv) {
+ case 1:
+ if ((hp = gethostbyname(*serv_list)) != NULL) {
+ DPR2 ("Server = %s\n", *serv_list);
+ (void)strncpy(cur_serv, *serv_list, MAXHOSTNAMELEN);
+ cur_serv[MAXHOSTNAMELEN - 1] = '\0';
+ done = 1;
+ break;
+ }
+ /* fall through */
+ case 0:
+ if (rebootflag)
+ die_gracefully();
+ else
+ sleep(1);
+ break;
+ default:
+ do {
+ new_serv = serv_list[random() % numserv];
+ } while (!strcmp(new_serv, cur_serv));
+
+ if ((hp = gethostbyname(new_serv)) != NULL) {
+ DPR2 ("Server = %s\n", new_serv);
+ (void)strncpy(cur_serv, new_serv, MAXHOSTNAMELEN);
+ cur_serv[MAXHOSTNAMELEN - 1] = '\0';
+ done = 1;
+ } else
+ sleep(1);
+
+ break;
+ }
+ }
+ (void) memcpy((char *)&serv_sin.sin_addr, hp->h_addr, 4);
+ nservchang++;
+}
+
+void
+server_manager(notice)
+ZNotice_t *notice;
+{
+ if (memcmp((char *)&serv_sin.sin_addr, (char *)&from.sin_addr, 4) ||
+ (serv_sin.sin_port != from.sin_port)) {
+ syslog (LOG_INFO, "Bad notice from port %u.", notice->z_port);
+ } else {
+ /* This is our server, handle the notice */
+ booting = 0;
+ if (boot_timer) {
+ timer_reset(boot_timer);
+ boot_timer = NULL;
+ }
+ DPR ("A notice came in from the server.\n");
+ nserv++;
+ switch(notice->z_kind) {
+ case HMCTL:
+ hm_control(notice);
+ break;
+ case SERVNAK:
+ case SERVACK:
+ send_back(notice);
+ break;
+ default:
+ syslog (LOG_INFO, "Bad notice kind!?");
+ break;
+ }
+ }
+}
+
+void
+hm_control(notice)
+ZNotice_t *notice;
+{
+ Code_t ret;
+ struct hostent *hp;
+ char suggested_server[MAXHOSTNAMELEN];
+ unsigned long addr;
+
+ DPR("Control message!\n");
+ if (!strcmp(notice->z_opcode, SERVER_SHUTDOWN)) {
+ if (notice->z_message_len) {
+ addr = inet_addr(notice->z_message);
+ hp = gethostbyaddr((char *) &addr, sizeof(addr), AF_INET);
+ if (hp != NULL) {
+ strncpy(suggested_server, hp->h_name, sizeof(suggested_server));
+ suggested_server[sizeof(suggested_server) - 1] = '\0';
+ new_server(suggested_server);
+ } else {
+ new_server(NULL);
+ }
+ } else {
+ new_server((char *)NULL);
+ }
+ } else if (!strcmp(notice->z_opcode, SERVER_PING)) {
+ notice->z_kind = HMACK;
+ if ((ret = ZSetDestAddr(&serv_sin)) != ZERR_NONE) {
+ Zperr(ret);
+ com_err("hm", ret, "setting destination");
+ }
+ if ((ret = send_outgoing(notice)) != ZERR_NONE) {
+ Zperr(ret);
+ com_err("hm", ret, "sending ACK");
+ }
+ if (no_server) {
+ no_server = 0;
+ retransmit_queue(&serv_sin);
+ }
+ } else {
+ syslog (LOG_INFO, "Bad control message.");
+ }
+}
+
+void
+send_back(notice)
+ZNotice_t *notice;
+{
+ ZNotice_Kind_t kind;
+ struct sockaddr_in repl;
+ Code_t ret;
+
+ if (!strcmp(notice->z_opcode, HM_BOOT) ||
+ !strcmp(notice->z_opcode, HM_ATTACH)) {
+ /* ignore message, just an ack from boot, but exit if we
+ * are rebooting.
+ */
+ if (rebootflag)
+ die_gracefully();
+ } else {
+ if (remove_notice_from_queue(notice, &kind, &repl) != ZERR_NONE) {
+ syslog (LOG_INFO, "Hey! This packet isn't in my queue!");
+ } else {
+ /* check if client wants an ACK, and send it */
+ if (kind == ACKED) {
+ DPR2 ("Client ACK port: %u\n", ntohs(repl.sin_port));
+ if ((ret = ZSetDestAddr(&repl)) != ZERR_NONE) {
+ Zperr(ret);
+ com_err("hm", ret, "setting destination");
+ }
+ if ((ret = send_outgoing(notice)) != ZERR_NONE) {
+ Zperr(ret);
+ com_err("hm", ret, "sending ACK");
+ }
+ }
+ }
+ }
+ if (no_server) {
+ no_server = 0;
+ retransmit_queue(&serv_sin);
+ }
+}
+
+void
+new_server(sugg_serv)
+char *sugg_serv;
+{
+ no_server = 1;
+ syslog (LOG_INFO, "Server went down, finding new server.");
+ send_flush_notice(HM_DETACH);
+ find_next_server(sugg_serv);
+ if (booting) {
+ send_boot_notice(HM_BOOT);
+ deactivated = 0;
+ } else {
+ send_boot_notice(HM_ATTACH);
+ }
+ disable_queue_retransmits();
+}
+
+static void boot_timeout(arg)
+void *arg;
+{
+ new_server(NULL);
+}
+
--- /dev/null
+SHELL = /bin/sh
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+datadir=@datadir@
+sysconfdir=@sysconfdir@
+sbindir=@sbindir@
+lsbindir=@lsbindir@
+
+includedir=${prefix}/include
+mandir=${prefix}/man
+libdir=${exec_prefix}/lib
+bindir=${exec_prefix}/bin
+
+srcdir=@srcdir@
+top_srcdir=@top_srcdir@
+BUILDTOP=..
+VPATH=@srcdir@
+CC=@CC@
+YACC=@YACC@
+INSTALL=@INSTALL@
+INSTANTIATE=${srcdir}/instantiate
+
+CPPFLAGS=@CPPFLAGS@
+CFLAGS=@CFLAGS@
+ALL_CFLAGS=${CFLAGS} -DDATADIR=\"${datadir}\" -I${top_srcdir}/h \
+ -I${BUILDTOP}/h -I${srcdir} -I. @X_CFLAGS@ ${CPPFLAGS}
+YFLAGS=-d
+LDFLAGS=-L${BUILDTOP}/lib @X_LIBS@ @LDFLAGS@
+LIBS=-lzephyr @LIBS@ -lcom_err @X_PRE_LIBS@ @ZWGC_LIBX11@ @X_EXTRA_LIBS@ \
+ @TLIB@ @REGEX_LIBS@ @ARES_LIBS@
+
+OBJS= port_dictionary.o pointer_dictionary.o unsigned_long_dictionary.o \
+ string_dictionary.o int_dictionary.o string_dictionary_aux.o \
+ parser.o lexer.o node.o exec.o buffer.o main.o zephyr.o X_driver.o \
+ substitute.o port.o xshow.o mux.o eval.o subscriptions.o notice.o \
+ xcut.o regexp.o character_class.o text_operations.o file.o error.o \
+ variables.o formatter.o X_fonts.o X_gram.o tty_filter.o \
+ standard_ports.o xselect.o xmark.o xrevstack.o xerror.o \
+ new_string.o new_memory.o
+
+all: zwgc
+
+zwgc: ${OBJS} ${BUILDTOP}/lib/libzephyr.a
+ ${CC} ${LDFLAGS} -o $@ ${OBJS} ${LIBS}
+
+port_dictionary.c port_dictionary.h: dictionary.c dictionary.h
+ ${INSTANTIATE} ${srcdir} dictionary port port.h
+
+pointer_dictionary.c pointer_dictionary.h: dictionary.c dictionary.h
+ ${INSTANTIATE} ${srcdir} dictionary pointer pointer.h
+
+unsigned_long_dictionary.c unsigned_long_dictionary.h: dictionary.c \
+ dictionary.h
+ ${INSTANTIATE} ${srcdir} dictionary unsigned_long unsigned_long.h
+
+string_dictionary.c string_dictionary.h: dictionary.c dictionary.h
+ ${INSTANTIATE} ${srcdir} dictionary string new_string.h
+
+int_dictionary.c int_dictionary.h: dictionary.c dictionary.h
+ ${INSTANTIATE} ${srcdir} dictionary int
+
+char_stack.h: stack.h
+ ${INSTANTIATE} ${srcdir} stack char
+
+string_stack.h: stack.h
+ ${INSTANTIATE} ${srcdir} stack string
+
+xmode_stack.h: stack.h
+ ${INSTANTIATE} ${srcdir} stack xmode
+
+lexer.o: y.tab.h
+
+parser.o: y.tab.c y.tab.h
+ ${CC} -c ${ALL_CFLAGS} -o $@ y.tab.c
+
+y.tab.c y.tab.h: parser.y
+ ${YACC} ${YFLAGS} ${srcdir}/parser.y
+
+.c.o:
+ ${CC} -c ${ALL_CFLAGS} $<
+
+check:
+
+install: zwgc
+ ${INSTALL} -m 755 -s zwgc ${DESTDIR}${bindir}
+ ${INSTALL} -m 644 ${srcdir}/zwgc.1 ${DESTDIR}${mandir}/man1
+ ${INSTALL} -m 644 ${srcdir}/zwgc.desc ${DESTDIR}${datadir}/zephyr
+ ${INSTALL} -m 644 ${srcdir}/zwgc_resources ${DESTDIR}${datadir}/zephyr
+
+clean:
+ rm -f ${OBJS} zwgc port_dictionary.[ch] pointer_dictionary.[ch]
+ rm -f unsigned_long_dictionary.[ch] string_dictionary.[ch]
+ rm -f int_dictionary.[ch] char_stack.h string_stack.h xmode_stack.h
+ rm -f y.tab.[ch]
+
+${OBJS}: ${top_srcdir}/h/sysdep.h ${BUILDTOP}/h/config.h
+zephyr.o: ${BUILDTOP}/h/zephyr/zephyr.h ${BUILDTOP}/h/zephyr/zephyr_err.h
+
+port_dictionary.o: port.h string_stack.h new_string.h new_memory.h
+pointer_dictionary.o: pointer.h new_string.h new_memory.h
+unsigned_long_dictionary.o: new_string.h new_memory.h
+string_dictionary.o: new_string.h new_memory.h
+int_dictionary.o: new_string.h new_memory.h
+X_driver.o: X_driver.h new_memory.h formatter.h mux.h variables.h error.h
+X_driver.o: X_gram.h xselect.h unsigned_long_dictionary.h
+X_fonts.o: X_fonts.h new_memory.h new_string.h error.h pointer_dictionary.h
+X_fonts.o: zwgc.h
+X_gram.o: X_gram.h xmark.h zwgc.h X_driver.h X_fonts.h error.h new_string.h
+X_gram.o: xrevstack.h xerror.h xselect.h
+browser.o: zwgc.h
+buffer.o: new_memory.h buffer.h
+character_class.o: character_class.h
+display.o: new_memory.h new_string.h variables.h display.h
+eval.o: new_memory.h node.h eval.h substitute.h port.h buffer.h regexp.h
+eval.o: text_operations.h zwgc.h variables.h
+exec.o: new_memory.h exec.h eval.h node.h buffer.h port.h variables.h notice.h
+file.o: new_memory.h new_string.h error.h
+formatter.o: new_memory.h char_stack.h string_dictionary.h formatter.h
+formatter.o: text_operations.h
+lexer.o: new_memory.h new_string.h int_dictionary.h lexer.h parser.h
+main.o: new_memory.h zwgc.h parser.h node.h exec.h zephyr.h notice.h
+main.o: subscriptions.h file.h mux.h port.h variables.h main.h
+mux.o: mux.h error.h zwgc.h pointer.h
+new_memory.o: new_memory.h int_dictionary.h
+new_string.o: new_memory.h
+node.o: new_memory.h node.h
+notice.o: new_memory.h error.h variables.h notice.h
+port.o: new_string.h port_dictionary.h port.h notice.h variables.h
+regexp.o: regexp.h
+standard_ports.o: new_memory.h port.h variables.h error.h main.h
+string_dictionary_aux.o: new_memory.h string_dictionary.h
+subscriptions.o: new_memory.h new_string.h int_dictionary.h zwgc.h
+subscriptions.o: subscriptions.h error.h file.h main.h
+substitute.o: new_memory.h lexer.h substitute.h
+text_operations.o: new_memory.h text_operations.h char_stack.h
+tty_filter.o: new_memory.h new_string.h string_dictionary_aux.h formatter.h
+tty_filter.o: zwgc.h error.h
+variables.o: new_memory.h notice.h string_dictionary_aux.h variables.h
+xcut.o: new_memory.h new_string.h X_gram.h zwgc.h xselect.h xmark.h error.h
+xcut.o: xrevstack.h
+xerror.o: mux.h
+xmark.o: X_gram.h X_fonts.h xmark.h new_string.h
+xrevstack.o: X_gram.h zwgc.h
+xselect.o: new_string.h xselect.h
+xshow.o: pointer_dictionary.h new_memory.h formatter.h variables.h zwgc.h
+xshow.o: X_fonts.h X_gram.h xmode_stack.h
+zephyr.o: new_string.h zephyr.h error.h mux.h subscriptions.h variables.h
+zephyr.o: pointer.h X_driver.h
+
+.PHONY: all check install clean
+
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: X_driver.c,v 1.18 1999/01/22 23:20:06 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <sysdep.h>
+
+#if (!defined(lint) && !defined(SABER))
+static const char rcsid_X_driver_c[] = "$Id: X_driver.c,v 1.18 1999/01/22 23:20:06 ghudson Exp $";
+#endif
+
+#include <zephyr/mit-copyright.h>
+
+/****************************************************************************/
+/* */
+/* The X driver: */
+/* */
+/****************************************************************************/
+
+#ifndef X_DISPLAY_MISSING
+
+#include "X_driver.h"
+#include <X11/Xresource.h>
+#include "new_memory.h"
+#include "formatter.h"
+#include "mux.h"
+#include "variables.h"
+#include "error.h"
+#include "X_gram.h"
+#include "xselect.h"
+#include "unsigned_long_dictionary.h"
+
+char *app_instance;
+
+/*
+ * dpy - the display we are outputting to
+ */
+
+Display *dpy = NULL;
+
+/****************************************************************************/
+/* */
+/* Code to deal with getting X resources: */
+/* */
+/****************************************************************************/
+
+/*
+ *
+ */
+
+#ifndef APPNAME
+#define APPNAME "zwgc"
+#endif
+
+/*
+ *
+ */
+
+#ifndef APPCLASS
+#define APPCLASS "Zwgc"
+#endif
+
+/*
+ * x_resources - our X resources from application resources, command line,
+ * and user's X resources.
+ */
+
+static XrmDatabase x_resources = NULL;
+
+/*
+ * Internal Routine:
+ *
+ * int convert_string_to_bool(string text)
+ * Effects: If text represents yes/true/on, return 1. If text
+ * representes no/false/off, return 0. Otherwise,
+ * returns -1.
+ */
+
+static int convert_string_to_bool(text)
+ string text;
+{
+ if (!strcasecmp("yes", text) || !strcasecmp("y", text) ||
+ !strcasecmp("true", text) || !strcasecmp("t", text) ||
+ !strcasecmp("on", text))
+ return(1);
+ else if (!strcasecmp("no", text) || !strcasecmp("n", text) ||
+ !strcasecmp("false", text) || !strcasecmp("f", text) ||
+ !strcasecmp("off", text))
+ return(0);
+ else
+ return(-1);
+}
+
+/*
+ *
+ */
+
+char *get_string_resource(name, class)
+ string name;
+ string class;
+{
+ string full_name, full_class;
+ int status;
+ char *type;
+ XrmValue value;
+
+ full_name = string_Concat(APPNAME, ".");
+ full_name = string_Concat2(full_name, name);
+ full_class = string_Concat(APPCLASS, ".");
+ full_class = string_Concat2(full_class, class);
+
+ status = XrmGetResource(x_resources, full_name, full_class, &type, &value);
+ free(full_name);
+ free(full_class);
+
+ if (status != True)
+ return(NULL);
+
+ if (string_Neq(type, "String"))
+ return(NULL);
+
+ return(value.addr);
+}
+
+/*
+ *
+ */
+
+int get_bool_resource(name, class, default_value)
+ string name;
+ string class;
+ int default_value;
+{
+ int result;
+ char *temp;
+
+ if (!(temp = get_string_resource(name, class)))
+ return(default_value);
+
+ result = convert_string_to_bool(temp);
+ if (result == -1)
+ result = default_value;
+
+ return(result);
+}
+
+static unsigned_long_dictionary color_dict = NULL;
+
+/* Requires: name points to color name or hex string. name must be free'd
+ * eventually by the caller.
+ * Effects: returns unsigned long pixel value, or default if the
+ * color is not known by the server. If name is NULL, returns
+ * default;
+ *
+ * comment: caches return values from X server round trips. If name does
+ * not resolve, this fact is NOT cached, and will result in a round
+ * trip each time.
+ */
+
+unsigned long x_string_to_color(name,def)
+ char *name;
+ unsigned long def;
+{
+ unsigned_long_dictionary_binding *binding;
+ int exists;
+ XColor xc;
+
+ if (name == NULL)
+ return(def);
+
+ binding = unsigned_long_dictionary_Define(color_dict,name,&exists);
+
+ if (exists) {
+ return((unsigned long) binding->value);
+ } else {
+ if (XParseColor(dpy,DefaultColormapOfScreen(DefaultScreenOfDisplay(dpy)),
+ name,&xc)) {
+ if (XAllocColor(dpy,
+ DefaultColormapOfScreen(DefaultScreenOfDisplay(dpy)),
+ &xc)) {
+ binding->value = (unsigned long) xc.pixel;
+ return(xc.pixel);
+ } else {
+ ERROR2("Error in XAllocColor on \"%s\": using default color\n",
+ name);
+ }
+ } else {
+ ERROR2("Error in XParseColor on \"%s\": using default color\n",
+ name);
+ }
+ unsigned_long_dictionary_Delete(color_dict,binding);
+ return(def);
+ }
+ /*NOTREACHED*/
+}
+
+/*
+ * Standard X Toolkit command line options:
+ */
+
+static XrmOptionDescRec cmd_options[] = {
+ {"+rv", "*reverseVideo", XrmoptionNoArg, (caddr_t) "off"},
+ {"+synchronous", "*synchronous", XrmoptionNoArg, (caddr_t) "off"},
+ {"-background", "*background", XrmoptionSepArg, (caddr_t) NULL},
+ {"-bd", "*borderColor", XrmoptionSepArg, (caddr_t) NULL},
+ {"-bg", "*background", XrmoptionSepArg, (caddr_t) NULL},
+ {"-bordercolor", "*borderColor", XrmoptionSepArg, (caddr_t) NULL},
+ {"-borderwidth", ".borderWidth", XrmoptionSepArg, (caddr_t) NULL},
+ {"-bw", ".borderWidth", XrmoptionSepArg, (caddr_t) NULL},
+ {"-display", ".display", XrmoptionSepArg, (caddr_t) NULL},
+ {"-fg", "*foreground", XrmoptionSepArg, (caddr_t) NULL},
+ {"-fn", "*font", XrmoptionSepArg, (caddr_t) NULL},
+ {"-font", "*font", XrmoptionSepArg, (caddr_t) NULL},
+ {"-foreground", "*foreground", XrmoptionSepArg, (caddr_t) NULL},
+ {"-geometry", ".geometry", XrmoptionSepArg, (caddr_t) NULL},
+ {"-iconname", ".iconName", XrmoptionSepArg, (caddr_t) NULL},
+ {"-name", ".name", XrmoptionSepArg, (caddr_t) NULL},
+ {"-reverse", "*reverseVideo", XrmoptionNoArg, (caddr_t) "on"},
+ {"-rv", "*reverseVideo", XrmoptionNoArg, (caddr_t) "on"},
+ {"-transient", "*transient", XrmoptionNoArg, (caddr_t) "on"},
+ {"-synchronous", "*synchronous", XrmoptionNoArg, (caddr_t) "on"},
+ {"-title", ".title", XrmoptionSepArg, (caddr_t) NULL},
+ {"-xrm", NULL, XrmoptionResArg, (caddr_t) NULL} };
+
+#define NUMBER_OF_OPTIONS ((sizeof (cmd_options))/ sizeof(cmd_options[0]))
+
+/*
+ *
+ */
+
+int open_display_and_load_resources(pargc, argv)
+ int *pargc;
+ char **argv;
+{
+ XrmDatabase temp_db1, temp_db2, temp_db3;
+ char *filename, *res, *xdef;
+ char dbasename[128];
+ extern char *getenv();
+
+ /* Initialize X resource manager: */
+ XrmInitialize();
+
+ /*
+ * Parse X toolkit command line arguments (including -display)
+ * into resources:
+ */
+ XrmParseCommand(&x_resources, cmd_options, NUMBER_OF_OPTIONS, APPNAME,
+ pargc, argv);
+
+ /*
+ * Try and open the display using the display specified if given.
+ * If can't open the display, return an error code.
+ */
+ dpy = XOpenDisplay(get_string_resource("display", "display"));
+ if (!dpy)
+ return(1);
+
+ /* Read in our application-specific resources: */
+ sprintf(dbasename, "%s/zephyr/zwgc_resources", DATADIR);
+ temp_db1 = XrmGetFileDatabase(dbasename);
+
+ /*
+ * Get resources from the just opened display:
+ */
+ xdef = XResourceManagerString(dpy);
+ if (xdef)
+ temp_db2 = XrmGetStringDatabase(xdef);
+ else
+ temp_db2 = NULL;
+
+ /*
+ * Merge the 4 sets of resources together such that when searching
+ * for resources, they are checking in the following order:
+ * command arguments, XENVIRONMENT resources, server resources,
+ * application resources
+ */
+ XrmMergeDatabases(temp_db2, &temp_db1);
+
+#if XlibSpecificationRelease > 4
+ /* X11 R5 per-screen resources */
+ res = XScreenResourceString (DefaultScreenOfDisplay (dpy));
+ if (res != NULL)
+ XrmMergeDatabases(XrmGetStringDatabase(res), &temp_db1);
+#endif
+
+ /*
+ * Get XENVIRONMENT resources, if they exist, and merge
+ */
+ if (filename = getenv("XENVIRONMENT"))
+ {
+ temp_db3 = XrmGetFileDatabase(filename);
+ XrmMergeDatabases(temp_db3, &temp_db1);
+ }
+ XrmMergeDatabases(x_resources, &temp_db1);
+ x_resources = temp_db1;
+
+ return(0);
+}
+
+/*
+ * X_driver_ioerror: called by Xlib in case of an X IO error.
+ * Shouldn't return (according to man page).
+ *
+ * on IO error, we clean up and exit.
+ *
+ * XXX it would be better to set mux_end_loop_p, but we can't return to
+ * get there (Xlib will exit if this routine returns).
+ *
+ */
+
+int X_driver_ioerror(display)
+Display *display;
+{
+ extern void finalize_zephyr();
+
+ ERROR2("X IO error on display '%s'--exiting\n", DisplayString(display));
+ finalize_zephyr();
+ exit(1);
+}
+/****************************************************************************/
+/* */
+/* Code to deal with initializing the driver: */
+/* */
+/****************************************************************************/
+
+extern void x_get_input();
+
+/*ARGSUSED*/
+int X_driver_init(drivername, notfirst, pargc, argv)
+ char *drivername;
+ char notfirst;
+ int *pargc;
+ char **argv;
+{
+ string temp;
+ int sync;
+
+ /*
+ * Attempt to open display and read resources, including from the
+ * command line. If fail, exit with error code, disabling this
+ * driver:
+ */
+ if (open_display_and_load_resources(pargc, argv)) {
+ ERROR("Unable to open X display -- disabling X driver.\n");
+ return(1);
+ }
+
+ XSetIOErrorHandler(X_driver_ioerror);
+
+ /*
+ * For now, set some useful variables using resources:
+ */
+ if (sync=get_bool_resource("synchronous", "Synchronous", 0))
+ XSynchronize(dpy,sync);
+ if (temp = get_string_resource("geometry", "Geometry"))
+ var_set_variable("default_X_geometry", temp);
+
+ temp=strrchr(argv[0],'/');
+
+ app_instance=string_Copy(temp?temp+1:argv[0]);
+
+ color_dict = unsigned_long_dictionary_Create(37);
+
+ xshowinit();
+ x_gram_init(dpy);
+ xicccmInitAtoms(dpy);
+
+ mux_add_input_source(ConnectionNumber(dpy), x_get_input, dpy);
+
+ return(0);
+}
+
+void X_driver_reset()
+{
+}
+
+/****************************************************************************/
+/* */
+/* The display routine itself: */
+/* */
+/****************************************************************************/
+
+char *X_driver(text)
+ string text;
+{
+ string text_copy;
+ desctype *desc;
+ int numstr, numnl;
+
+ text_copy = string_Copy(text);
+ desc = disp_get_cmds(text_copy, &numstr, &numnl);
+
+ xshow(dpy, desc, numstr, numnl);
+
+ free(text_copy);
+ free_desc(desc);
+ return(NULL);
+}
+
+#endif /* X_DISPLAY_MISSING */
+
--- /dev/null
+#ifndef x_driver_MODULE
+#define x_driver_MODULE
+
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: X_driver.h,v 1.6 1999/01/22 23:20:07 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+
+#include <zephyr/mit-copyright.h>
+
+#include <X11/Xlib.h>
+
+extern Display *dpy;
+
+extern char *get_string_resource();
+extern int get_bool_resource();
+
+#endif
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: X_fonts.c,v 1.6 1999/01/22 23:20:08 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <sysdep.h>
+
+#if (!defined(lint) && !defined(SABER))
+static const char rcsid_X_fonts_c[] = "$Id: X_fonts.c,v 1.6 1999/01/22 23:20:08 ghudson Exp $";
+#endif
+
+#include <zephyr/mit-copyright.h>
+
+/****************************************************************************/
+/* */
+/* Code dealing with X fonts: */
+/* */
+/****************************************************************************/
+
+#ifndef X_DISPLAY_MISSING
+
+#include "X_fonts.h"
+#include "new_memory.h"
+#include "new_string.h"
+#include "error.h"
+#include "pointer_dictionary.h"
+#include "zwgc.h"
+
+/*
+ * font_dict - Lookup cache for fonts (the value pointers are XFontStruct *'s)
+ */
+
+static pointer_dictionary family_dict = NULL;
+static pointer_dictionary fontname_dict = NULL;
+static pointer_dictionary fontst_dict = NULL;
+static pointer_dictionary fidst_dict = NULL;
+
+/*
+ * {face,size}_to_string - lookup tables for converting {face,size} int
+ * constants to ascii strings:
+ */
+
+static string face_to_string[] = { "roman", "bold", "italic", "bolditalic" };
+static string size_to_string[] = { "small", "medium", "large" };
+
+extern char *get_string_resources();
+
+static char *get_family(style,substyle)
+ char *style;
+ char *substyle;
+{
+ char *desc;
+ pointer_dictionary_binding *binding;
+ int exists;
+ char *family;
+
+ desc=string_Concat("style.",style);
+ desc=string_Concat2(desc,".substyle.");
+ desc=string_Concat2(desc,substyle);
+ desc=string_Concat2(desc,".fontfamily");
+
+ if (!family_dict)
+ family_dict = pointer_dictionary_Create(37);
+ binding = pointer_dictionary_Define(family_dict,desc,&exists);
+
+ if (exists) {
+ free(desc);
+ return((string) binding->value);
+ } else {
+#define STYLE_CLASS "StyleKey.Style1.Style2.Style3.SubstyleKey.Substyle.FontfamilyKey"
+ family=get_string_resource(desc,STYLE_CLASS);
+#undef STYLE_CLASS
+ free(desc);
+ if (family==NULL)
+ pointer_dictionary_Delete(family_dict,binding);
+ else
+ binding->value=(pointer) family;
+ return(family); /* If resource returns NULL, return NULL also */
+ }
+}
+
+static char *get_specific_fontname(family,size,face)
+ char *family;
+ int size;
+ int face;
+{
+ char *desc;
+ pointer_dictionary_binding *binding;
+ int exists;
+ char *fontname;
+
+ desc = string_Concat("fontfamily.",family);
+ desc = string_Concat2(desc, ".");
+ desc = string_Concat2(desc, size_to_string[size]);
+ desc = string_Concat2(desc, ".");
+ desc = string_Concat2(desc, face_to_string[face]);
+
+ if (!fontname_dict)
+ fontname_dict = pointer_dictionary_Create(37);
+ binding = pointer_dictionary_Define(fontname_dict,desc,&exists);
+
+ if (exists) {
+ free(desc);
+ return((string) binding->value);
+ } else {
+#define FAMILY_CLASS "FontfamilyKey.Fontfamily.Size.Face"
+ fontname=get_string_resource(desc,FAMILY_CLASS);
+ free(desc);
+ if (fontname==NULL)
+ pointer_dictionary_Delete(fontname_dict,binding);
+ else
+ binding->value=(pointer) fontname;
+ return(fontname); /* If resource returns NULL, return NULL also */
+ }
+}
+
+/* fast function to convert Font to hex. Return value
+ * is on the heap and must be freed. I'm cheating in
+ * that I know that Font us really an unsigned long. */
+
+static char hexdigits[] = {"0123456789ABCDEF"};
+static char *Font_to_hex(num)
+ Font num;
+{
+ char *temp;
+ int i;
+
+ temp=(char *) malloc((sizeof(Font)<<1)+2);
+
+ for (i=0;i<((sizeof(Font)<<1)+1);i++)
+ temp[i] = hexdigits[(num>>(i*4))&0x0f];
+ temp[i] = '\0';
+
+ return(temp);
+}
+
+void add_fid(font)
+ XFontStruct *font;
+{
+
+ char *fidstr;
+ pointer_dictionary_binding *binding;
+ int exists;
+
+ if (!fidst_dict)
+ fidst_dict = pointer_dictionary_Create(37);
+ fidstr=Font_to_hex(font->fid);
+ binding = pointer_dictionary_Define(fidst_dict,fidstr,&exists);
+ free(fidstr);
+
+ if (!exists)
+ binding->value=(pointer) font;
+}
+
+/* requires that the font already be cached. */
+XFontStruct *get_fontst_from_fid(fid)
+ Font fid;
+{
+ char *fidstr;
+ pointer_dictionary_binding *binding;
+ int exists;
+
+ fidstr=Font_to_hex(fid);
+
+ binding = pointer_dictionary_Define(fidst_dict,fidstr,&exists);
+ free(fidstr);
+#ifdef DEBUG
+ if (exists) {
+ return((XFontStruct *) binding->value);
+ } else {
+ printf("Font fid=0x%s not cached. Oops.\n",fidstr);
+ abort();
+ }
+#else
+ return((XFontStruct *) binding->value);
+#endif
+}
+
+static XFontStruct *get_fontst(dpy,fontname)
+ Display *dpy;
+ char *fontname;
+{
+ pointer_dictionary_binding *binding;
+ int exists;
+ XFontStruct *fontst;
+
+ if (!fontst_dict)
+ fontst_dict = pointer_dictionary_Create(37);
+ binding = pointer_dictionary_Define(fontst_dict,fontname,&exists);
+
+ if (exists) {
+ return((XFontStruct *) binding->value);
+ } else {
+ fontst=XLoadQueryFont(dpy,fontname);
+ if (fontst==NULL) {
+ pointer_dictionary_Delete(fontst_dict,binding);
+ } else {
+ binding->value=(pointer) fontst;
+ add_fid(fontst);
+ } return(fontst); /* If resource returns NULL, return NULL also */
+ }
+}
+
+static char *get_fontname(family,size,face)
+ char *family;
+ int size;
+ int face;
+{
+ char *fontname;
+
+ if (!(fontname=get_specific_fontname(family,size,face)))
+ if (!(fontname=get_specific_fontname(family,size,ROMAN_FACE)))
+ if (!(fontname=get_specific_fontname(family,MEDIUM_SIZE,face)))
+ fontname=get_specific_fontname(family,MEDIUM_SIZE,ROMAN_FACE);
+ return(fontname);
+}
+
+static XFontStruct *complete_get_fontst(dpy,style,substyle,size,face)
+ Display *dpy;
+ string style;
+ string substyle;
+ int size;
+ int face;
+{
+ char *family,*fontname;
+ XFontStruct *fontst;
+
+ if (family=get_family(style,substyle))
+ if (fontname=get_fontname(family,size,face))
+ if (fontst=get_fontst(dpy,fontname))
+ return(fontst);
+ /* If any part fails, */
+ return(NULL);
+}
+
+/*
+ * XFontStruct *get_font(string style, substyle; int size, face)
+ * Requires: size is one of SMALL_SIZE, MEDIUM_SIZE, LARGE_SIZE and
+ * face is one of ROMAN_FACE, BOLD_FACE, ITALIC_FACE,
+ * BOLDITALIC_FACE.
+ * Effects: unknown
+ */
+
+XFontStruct *get_font(dpy,style,substyle,size,face)
+ Display *dpy;
+ string style;
+ string substyle;
+ int size;
+ int face;
+{
+ char *family,*fontname;
+ XFontStruct *fontst;
+
+ if (size == SPECIAL_SIZE) {
+ /* attempt to process @font explicitly */
+ if (fontst=get_fontst(dpy,substyle))
+ return(fontst);
+ } else {
+ if (family=get_family(style,substyle)) {
+ if (fontname=get_fontname(family,size,face))
+ if (fontst=get_fontst(dpy,fontname))
+ return(fontst);
+ } else {
+ if (fontname=get_fontname(substyle,size,face))
+ if (fontst=get_fontst(dpy,fontname))
+ return(fontst);
+ }
+
+ /* At this point, the no-failure case didn't happen, and the case
+ of substyle being the fontfamily didn't happen, either. */
+
+ fontst=NULL;
+ if (!(fontst=complete_get_fontst(dpy,style,"text",size,face)))
+ if (!(fontst=complete_get_fontst(dpy,"default",substyle,size,face)))
+ if (!(fontst=complete_get_fontst(dpy,"default","text",size,face)))
+ if (fontname=get_fontname("default",size,face))
+ fontst=get_fontst(dpy,fontname);
+ if (fontst) return(fontst);
+ }
+
+ /* If all else fails, try fixed */
+
+ if (fontst=get_fontst(dpy,"fixed")) return(fontst);
+
+ /* No fonts available. Die. */
+
+ ERROR("Unable to open font \"fixed\". Aborting...");
+#ifdef DEBUG
+ abort();
+#else
+ exit(1);
+#endif
+}
+
+#endif /* X_DISPLAY_MISSING */
+
--- /dev/null
+#ifndef x_fonts_MODULE
+#define x_fonts_MODULE
+
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: X_fonts.h,v 1.6 1999/01/22 23:20:08 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+
+#include <zephyr/mit-copyright.h>
+
+#include "X_driver.h"
+
+#define SPECIAL_FACE -1
+#define ROMAN_FACE 0
+#define BOLD_FACE 1
+#define ITALIC_FACE 2
+#define BOLD_ITALIC_FACE 3
+
+#define SPECIAL_SIZE -1
+#define SMALL_SIZE 0
+#define MEDIUM_SIZE 1
+#define LARGE_SIZE 2
+
+/*
+ * XFontStruct *get_font(string family; int size, face)
+ * Requires: size is one of SMALL_SIZE, MEDIUM_SIZE, LARGE_SIZE and
+ * face is one of ROMAN_FACE, BOLD_FACE, ITALIC_FACE,
+ * BOLDITALIC_FACE.
+ * Effects: Looks up the font specified by the above in the
+ * X resources. If that font is not specified by in
+ * the X resources or it can't be loaded, the font
+ * specified by default.medium.roman is used. <<<>>>
+ */
+
+extern XFontStruct *get_font();
+extern XFontStruct *get_fontst_from_fid();
+
+#endif
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: X_gram.c,v 1.22 1999/01/22 23:20:09 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <sysdep.h>
+
+#if (!defined(lint) && !defined(SABER))
+static const char rcsid_X_gram_c[] = "$Id: X_gram.c,v 1.22 1999/01/22 23:20:09 ghudson Exp $";
+#endif
+
+#include <zephyr/mit-copyright.h>
+
+#ifndef X_DISPLAY_MISSING
+
+#include "X_gram.h"
+#include "xmark.h"
+#include <X11/Xutil.h>
+#include <X11/cursorfont.h>
+#include "zwgc.h"
+#include "X_driver.h"
+#include "X_fonts.h"
+#include "error.h"
+#include "new_string.h"
+#include "xrevstack.h"
+#include "xerror.h"
+#include "xselect.h"
+
+extern XContext desc_context;
+extern char *app_instance;
+extern unsigned long x_string_to_color();
+extern char *getenv();
+
+/*
+ *
+ */
+
+int internal_border_width = 2;
+
+unsigned long default_fgcolor;
+unsigned long default_bgcolor;
+unsigned long default_bordercolor;
+long ttl = 0;
+static int reset_saver;
+static int border_width = 1;
+static int cursor_code = XC_sailboat;
+static int set_transient;
+static int enable_delete;
+static char *title_name,*icon_name;
+static Cursor cursor;
+static Window group_leader; /* In order to have transient windows,
+ * I need a top-level window to always exist
+ */
+static XClassHint classhint;
+static XSetWindowAttributes xattributes;
+static unsigned long xattributes_mask;
+
+/* ICCCM note:
+ *
+ * the following properties must be set on all top-level windows:
+ *
+ * WM_NAME XStoreName(dpy,w,name);
+ * WM_ICON_NAME XSetIconName(dpy,w,name);
+ * WM_NORMAL_HINTS XSetNormalHints(dpy,w,sizehints);
+ * WM_HINTS XSetWMHints(dpy,w,wmhints);
+ * WM_CLASS XSetClassHint(dpy,w,classhint);
+ *
+ * and for individual zgrams:
+ *
+ * WM_TRANSIENT_FOR XSetTransientForHint(dpy,w,main_window);
+ * WM_PROTOCOLS XSetWMProtocols(dpy,w,protocols,cnt);
+ */
+
+/* set all properties defined in ICCCM. If main_window == 0,
+ * per-zgram initialization is not done.
+ */
+
+/*ARGSUSED*/
+void x_set_icccm_hints(dpy,w,name,icon_name,psizehints,pwmhints,main_window)
+ Display *dpy;
+ Window w;
+ char *name;
+ char *icon_name;
+ XSizeHints *psizehints;
+ XWMHints *pwmhints;
+ Window main_window;
+{
+ XStoreName(dpy,w,name);
+ XSetIconName(dpy,w,icon_name);
+ XSetNormalHints(dpy,w,psizehints);
+ XSetWMHints(dpy,w,pwmhints);
+ XSetClassHint(dpy,w,&classhint);
+ /* in order for some wm's to iconify, the window shouldn't be transient.
+ e.g. Motif wm */
+ if (main_window != None) {
+ if (set_transient)
+ XSetTransientForHint(dpy,w,main_window);
+ }
+ if (enable_delete)
+ XSetWMProtocols(dpy,w,&XA_WM_DELETE_WINDOW,1);
+}
+
+void x_gram_init(dpy)
+ Display *dpy;
+{
+ char *temp;
+ XSizeHints sizehints;
+ XWMHints wmhints;
+ unsigned long rv,tc;
+
+ default_fgcolor = BlackPixelOfScreen(DefaultScreenOfDisplay(dpy));
+ default_bgcolor = WhitePixelOfScreen(DefaultScreenOfDisplay(dpy));
+ rv = get_bool_resource("reverseVideo", "ReverseVideo", 0);
+ if (rv) {
+ tc = default_fgcolor;
+ default_fgcolor = default_bgcolor;
+ default_bgcolor = tc;
+ }
+ if (temp = get_string_resource("foreground","Foreground"))
+ default_fgcolor = x_string_to_color(temp,default_fgcolor);
+ if (temp = get_string_resource("background","Background"))
+ default_bgcolor = x_string_to_color(temp,default_bgcolor);
+ default_bordercolor = default_fgcolor;
+ if (temp = get_string_resource("borderColor","BorderColor"))
+ default_bordercolor = x_string_to_color(temp,default_bordercolor);
+
+ temp = get_string_resource("minTimeToLive","MinTimeToLive");
+ if (temp && atoi(temp)>=0)
+ ttl = atoi(temp);
+
+ reverse_stack = get_bool_resource("reverseStack", "ReverseStack", 0);
+ reset_saver = get_bool_resource("resetSaver", "ResetSaver", 1);
+ /* The default here should be 1, but mwm sucks */
+ set_transient = get_bool_resource("transient", "Transient", 0);
+ enable_delete = get_bool_resource("enableDelete", "EnableDelete", 1);
+
+ temp = get_string_resource("borderWidth", "BorderWidth");
+ /* <<<>>> */
+ if (temp && atoi(temp)>=0)
+ border_width = atoi(temp);
+
+ temp = get_string_resource("internalBorder", "InternalBorder");
+ /* <<<>>> */
+ if (temp && atoi(temp)>=0)
+ internal_border_width = atoi(temp);
+
+ temp = get_string_resource("cursorCode", "CursorCode");
+ /* <<<>>> */
+ if (temp && atoi(temp))
+ cursor_code = atoi(temp);
+
+ cursor = XCreateFontCursor(dpy, cursor_code);
+ if (!cursor)
+ cursor = XCreateFontCursor(dpy, XC_sailboat);
+
+ temp = get_string_resource("pointerColor", "Foreground");
+ if (temp) {
+ char *temp2;
+ XColor cursor_fore, cursor_back;
+ /* XXX need to do our own parsing here, since the RecolorCursor
+ routine requires an XColor, not an unsigned long (pixel) */
+ if (!(temp2 = get_string_resource("background","Background"))) {
+ if (default_bgcolor == WhitePixelOfScreen(DefaultScreenOfDisplay(dpy)))
+ temp2 = "white";
+ else
+ temp2 = "black";
+ }
+ if (XParseColor(dpy,
+ DefaultColormapOfScreen(DefaultScreenOfDisplay(dpy)),
+ temp, &cursor_fore) &&
+ XParseColor(dpy,
+ DefaultColormapOfScreen(DefaultScreenOfDisplay(dpy)),
+ temp2, &cursor_back)) {
+ XRecolorCursor(dpy, cursor, &cursor_fore, &cursor_back);
+ }
+ }
+ if (!(title_name=get_string_resource("title","Title")))
+ if (!(title_name=get_string_resource("name","Name")))
+ title_name=app_instance;
+
+ if (!(icon_name=get_string_resource("iconName","IconName")))
+ if (!(icon_name=get_string_resource("name","Name")))
+ icon_name=app_instance;
+
+ if (!(temp=get_string_resource("name","Name")))
+ if (!(temp=(char *) getenv("RESOURCE_NAME")))
+ temp=app_instance;
+ classhint.res_name=string_Copy(temp);
+ classhint.res_class="Zwgc";
+
+ if (set_transient) {
+ group_leader=XCreateSimpleWindow(dpy,DefaultRootWindow(dpy),0,0,100,100,
+ 0,default_bordercolor,default_bgcolor);
+ sizehints.x = 0;
+ sizehints.y = 0;
+ sizehints.width = 100;
+ sizehints.height = 100;
+ sizehints.flags = PPosition | PSize;
+
+ wmhints.input = False;
+ wmhints.initial_state = DontCareState;
+ wmhints.flags = InputHint | StateHint;
+
+ x_set_icccm_hints(dpy,group_leader,"ZwgcGroup","ZwgcGroup",&sizehints,
+ &wmhints,0);
+ }
+ xattributes.border_pixel = default_bordercolor;
+ xattributes.cursor = cursor;
+ xattributes.event_mask = (ExposureMask|ButtonReleaseMask|ButtonPressMask
+ |LeaveWindowMask|Button1MotionMask
+ |Button3MotionMask|StructureNotifyMask);
+ xattributes_mask = (CWBackPixel|CWBorderPixel|CWEventMask|CWCursor);
+ temp = get_string_resource ("backingStore", "BackingStore");
+ if (!temp)
+ return;
+ xattributes_mask |= CWBackingStore;
+ if (!strcasecmp (temp, "notuseful"))
+ xattributes.backing_store = NotUseful;
+ else if (!strcasecmp (temp, "whenmapped"))
+ xattributes.backing_store = WhenMapped;
+ else if (!strcasecmp (temp, "always"))
+ xattributes.backing_store = Always;
+ else if (!strcasecmp (temp, "default"))
+ xattributes_mask &= ~CWBackingStore;
+ else {
+ switch (get_bool_resource ("backingStore", "BackingStore", -1)) {
+ case 0:
+ xattributes.backing_store = NotUseful;
+ break;
+ case 1:
+ xattributes.backing_store = WhenMapped;
+ break;
+ case -1:
+ fprintf (stderr,
+ "zwgc: Cannot interpret backing-store resource value `%s'.\n",
+ temp);
+ xattributes_mask &= ~CWBackingStore;
+ break;
+ }
+ }
+}
+
+void x_gram_create(dpy, gram, xalign, yalign, xpos, ypos, xsize, ysize,
+ beepcount)
+ Display *dpy;
+ x_gram *gram;
+ int xalign, yalign;
+ int xpos, ypos;
+ int xsize, ysize;
+ int beepcount;
+{
+ Window w;
+ XSizeHints sizehints;
+ XWMHints wmhints;
+ XSetWindowAttributes attributes;
+ extern void x_get_input();
+
+ /*
+ * Adjust xpos, ypos based on the alignments xalign, yalign and the sizes:
+ */
+ if (xalign<0)
+ xpos = WidthOfScreen(DefaultScreenOfDisplay(dpy)) - xpos - xsize
+ - 2*border_width;
+ else if (xalign == 0)
+ xpos = (WidthOfScreen(DefaultScreenOfDisplay(dpy)) - xsize
+ - 2*border_width)>>1 + xpos;
+
+ if (yalign<0)
+ ypos = HeightOfScreen(DefaultScreenOfDisplay(dpy)) - ypos - ysize
+ - 2*border_width;
+ else if (yalign == 0)
+ ypos = (HeightOfScreen(DefaultScreenOfDisplay(dpy)) - ysize
+ - 2*border_width)>>1 + ypos;
+
+ /*
+ * Create the window:
+ */
+ attributes = xattributes;
+ attributes.background_pixel = gram->bgcolor;
+
+ gram->w = w = XCreateWindow (dpy, DefaultRootWindow (dpy), xpos, ypos,
+ xsize, ysize, border_width, 0,
+ CopyFromParent, CopyFromParent,
+ xattributes_mask, &attributes);
+
+ sizehints.x = xpos;
+ sizehints.y = ypos;
+ sizehints.width = xsize;
+ sizehints.height = ysize;
+ sizehints.flags = USPosition|USSize;
+
+ wmhints.input = True;
+ wmhints.initial_state = NormalState;
+ if (set_transient) {
+ wmhints.window_group = group_leader;
+ wmhints.flags = InputHint | StateHint | WindowGroupHint;
+
+ x_set_icccm_hints(dpy,w,title_name,icon_name,&sizehints,&wmhints,
+ group_leader);
+ } else {
+ wmhints.flags = InputHint | StateHint;
+
+ x_set_icccm_hints(dpy,w,title_name,icon_name,&sizehints,&wmhints,0);
+ }
+
+
+ XSaveContext(dpy, w, desc_context, (caddr_t)gram);
+
+ gram->can_die.tv_sec = 0;
+
+ XMapWindow(dpy, w);
+
+ if (beepcount)
+ XBell(dpy, 0);
+
+ xerror_happened = 0;
+ if (reverse_stack && bottom_gram) {
+ XWindowChanges winchanges;
+
+ winchanges.sibling=bottom_gram->w;
+ winchanges.stack_mode=Below;
+ begin_xerror_trap (dpy);
+ XReconfigureWMWindow (dpy, w, DefaultScreen (dpy),
+ CWSibling|CWStackMode, &winchanges);
+ end_xerror_trap (dpy);
+ if (xerror_happened) {
+ /* The event didn't go. Print an error message, and continue. */
+ ERROR ("Error configuring window to the bottom of the stack.\n");
+ }
+ }
+ /* we always need to keep a linked list of windows */
+ add_to_bottom(gram);
+ if (xerror_happened)
+ pull_to_top(gram);
+
+ if (reset_saver)
+ XResetScreenSaver(dpy);
+
+ XFlush(dpy);
+ /* Because the flushing/syncing/etc with the error trapping can cause
+ events to be read into the Xlib queue, we need to go through the queue
+ here before exiting so that any pending events get processed.
+ */
+ x_get_input(dpy);
+}
+
+void x_gram_draw(dpy, w, gram, region)
+ Display *dpy;
+ Window w;
+ x_gram *gram;
+ Region region;
+{
+ int i;
+ GC gc;
+ XGCValues gcvals;
+ xblock *xb;
+ XTextItem text;
+ int startblock,endblock,startpixel,endpixel;
+
+#define SetFG(fg) \
+ gcvals.foreground=fg; \
+ XChangeGC(dpy,gc,GCForeground,&gcvals)
+
+ gc = XCreateGC(dpy, w, 0, &gcvals);
+ XSetRegion(dpy,gc,region);
+
+ if ((markgram == gram) && (STARTBLOCK != -1) && (ENDBLOCK != -1)) {
+ if (xmarkSecond() == XMARK_END_BOUND) {
+ startblock=STARTBLOCK;
+ endblock=ENDBLOCK;
+ startpixel=STARTPIXEL;
+ endpixel=ENDPIXEL;
+ } else {
+ startblock=ENDBLOCK;
+ endblock=STARTBLOCK;
+ startpixel=ENDPIXEL;
+ endpixel=STARTPIXEL;
+ }
+ } else {
+ startblock = -1;
+ endblock = -1;
+ }
+
+ for (i=0,xb=gram->blocks ; i<gram->numblocks ; i++,xb++) {
+ if (XRectInRegion(region,xb->x1,xb->y1,xb->x2-xb->x1,
+ xb->y2-xb->y1) != RectangleOut) {
+ if (i==startblock) {
+ if (i==endblock) {
+ SetFG(gram->bgcolor);
+ XFillRectangle(dpy,w,gc,xb->x1,xb->y1,startpixel,
+ (xb->y2-xb->y1));
+ SetFG(xb->fgcolor);
+ XFillRectangle(dpy,w,gc,xb->x1+startpixel,xb->y1,
+ (endpixel-startpixel),(xb->y2-xb->y1));
+ SetFG(gram->bgcolor);
+ XFillRectangle(dpy,w,gc,xb->x1+endpixel,xb->y1,
+ (xb->x2-xb->x1-endpixel),(xb->y2-xb->y1));
+ } else {
+ SetFG(gram->bgcolor);
+ XFillRectangle(dpy,w,gc,xb->x1,xb->y1,startpixel,
+ (xb->y2-xb->y1));
+ SetFG(xb->fgcolor);
+ XFillRectangle(dpy,w,gc,xb->x1+startpixel,xb->y1,
+ (xb->x2-xb->x1-startpixel),(xb->y2-xb->y1));
+ }
+ } else if (i==endblock) {
+ SetFG(xb->fgcolor);
+ XFillRectangle(dpy,w,gc,xb->x1,xb->y1,endpixel,
+ (xb->y2-xb->y1));
+ SetFG(gram->bgcolor);
+ XFillRectangle(dpy,w,gc,xb->x1+endpixel,xb->y1,
+ (xb->x2-xb->x1-endpixel),(xb->y2-xb->y1));
+ } else {
+ if ((startblock < i) && (i < endblock)) {
+ SetFG(xb->fgcolor);
+ } else {
+ SetFG(gram->bgcolor);
+ }
+ XFillRectangle(dpy,w,gc,xb->x1,xb->y1,(xb->x2-xb->x1),
+ (xb->y2-xb->y1));
+ }
+ }
+ }
+
+ gcvals.function=GXxor;
+ XChangeGC(dpy,gc,GCFunction,&gcvals);
+
+ for (i=0,xb=gram->blocks ; i<gram->numblocks ; i++,xb++) {
+ if (XRectInRegion(region,xb->x1,xb->y1,xb->x2-xb->x1,
+ xb->y2-xb->y1) != RectangleOut) {
+ SetFG(gram->bgcolor^xb->fgcolor);
+ text.chars=gram->text+xb->strindex;
+ text.nchars=xb->strlen;
+ text.delta=0;
+ text.font=xb->fid;
+ XDrawText(dpy,w,gc,xb->x,xb->y,&text,1);
+ }
+ }
+
+ XFreeGC(dpy,gc);
+}
+
+void x_gram_expose(dpy,w,gram,event)
+ Display *dpy;
+ Window w;
+ x_gram *gram;
+ XExposeEvent *event;
+{
+ static Region region;
+ static int partregion;
+ XRectangle rect;
+
+ rect.x = (short) event->x;
+ rect.y = (short) event->y;
+ rect.width = (unsigned short) event->width;
+ rect.height = (unsigned short) event->height;
+
+#ifdef MARK_DEBUG
+ printf("----- xeventExpose:\nx=%d y=%d w=%d h=%d\n-----",
+ event->x,event->y,event->width,event->height);
+#endif
+
+ if (! partregion) {
+ region=XCreateRegion();
+ partregion = 1;
+ }
+
+ if (rect.width && rect.height) XUnionRectWithRegion(&rect,region,region);
+
+ if (event->count == 0) {
+ x_gram_draw(dpy,w,gram,region);
+ partregion = 0;
+ XDestroyRegion(region);
+ }
+}
+
+#endif /* X_DISPLAY_MISSING */
+
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: X_gram.h,v 1.8 1999/01/22 23:20:10 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+
+#include <zephyr/mit-copyright.h>
+
+#ifndef x_gram_TYPE
+#define x_gram_TYPE
+
+#include <X11/Xlib.h>
+#include <sys/time.h>
+
+typedef struct _xblock {
+ unsigned long fgcolor;
+ Font fid;
+ int x,y;
+ int x1,y1,x2,y2; /* bounds of block. used for cut and paste. */
+ int strindex;
+ int strlen;
+} xblock;
+
+typedef struct _x_gram {
+ unsigned long bgcolor;
+ int numblocks;
+ xblock *blocks;
+ char *text;
+ struct _x_gram *below,*above;
+ Window w;
+ struct timeval can_die;
+} x_gram;
+
+typedef struct _xauxblock {
+ int align;
+ XFontStruct *font;
+ char *str;
+ int len;
+ int width;
+} xauxblock;
+
+typedef struct _xmode {
+ int bold;
+ int italic;
+ int size;
+ int align;
+ int expcolor;
+ unsigned long color;
+ char *substyle;
+ char *font;
+} xmode;
+
+typedef struct _xlinedesc {
+ int startblock;
+ int numblock;
+ int lsize;
+ int csize;
+ int rsize;
+ int ascent;
+ int descent;
+} xlinedesc;
+
+/* alignment values: */
+#define LEFTALIGN 0
+#define CENTERALIGN 1
+#define RIGHTALIGN 2
+
+extern void x_gram_init();
+extern void x_gram_create();
+extern void x_gram_expose();
+extern void xshow();
+extern void xcut();
+
+#endif
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: browser.c,v 1.4 1999/01/22 23:20:10 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#if (!defined(lint) && !defined(SABER))
+static char rcsid_browser_c[] = "$Id: browser.c,v 1.4 1999/01/22 23:20:10 ghudson Exp $";
+#endif
+
+#include <zephyr/mit-copyright.h>
+
+#include <sysdep.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include "zwgc.h"
+
+static int browser_fd;
+struct sockaddr_un sun;
+
+int BOpenSocket()
+{
+ int fd,len;
+ char *temp;
+
+ if ((fd=socket(PF_UNIX,SOCK_STREAM,0)) == -1)
+ return(-1);
+
+ sun.sun_family=AF_UNIX;
+ if (temp=getenv("WGSOCK"))
+ strncpy(sun.sunpath,temp,sizeof(sun.sunpath));
+ else
+ sprintf(sun.sun_path,"/tmp/.zwgc.%d",getuid());
+ if (bind(fd,(struct sockaddr *) &sun,
+ (len=strlen(sun.sunpath)) > sizeof(sun.sunpath)?
+ sizeof(sun.sunpath):len) == -1) {
+ close(fd);
+ return(-1);
+ }
+
+ if (listen(fd,5) == -1) {
+ unlink(sun.sunpath);
+ close(fd);
+ return(-1);
+ }
+
+ return(fd);
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: browser.h,v 1.5 1999/01/22 23:20:11 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+
+#include <zephyr/mit-copyright.h>
+
+#define BROWSER_NEW_REQ 1
+#define BROWSER_NEW_REQ_RESP 2
+#define BROWSER_ZPACKET 3
+#define BROWSER_ZPACKET_RESP 4
+#define BROWSER_TEXT 5
+#define BROWSER_WINDOW_ID 6
+#define BROWSER_VAR_REQ 7
+#define BROWSER_VAR_REQ_RESP 8
+
+
+#define BROWSER_TYPE_OVERRIDE 11
+#define BROWSER_TYPE_DRIVER 12
+#define BROWSER_TYPE_WM 13
+#define BROWSER_TYPE_SIMPLE 14
+
+#define BROWSER_ACK 21
+#define BROWSER_NAK 22
+
+#define BROWSER_KEEP 31
+#define BROWSER_LOSE 32
+
+extern int ZBOpenConnection();
+extern void ZBCloseConnection( /* int fd */ );
+extern char *var_get_variable( /* char *varname */ );
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: buffer.c,v 1.5 1999/01/22 23:20:11 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <sysdep.h>
+
+#if (!defined(lint) && !defined(SABER))
+static const char rcsid_buffer_c[] = "$Id: buffer.c,v 1.5 1999/01/22 23:20:11 ghudson Exp $";
+#endif
+
+#include <zephyr/mit-copyright.h>
+
+#include "new_memory.h"
+#include "buffer.h"
+
+static char *buffer = 0;
+
+string buffer_to_string()
+{
+ return(buffer);
+}
+
+void clear_buffer()
+{
+ if (buffer)
+ free(buffer);
+
+ buffer = string_Copy("");
+}
+
+void append_buffer(str)
+ char *str;
+{
+ buffer = string_Concat2(buffer, str);
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: buffer.h,v 1.5 1999/01/22 23:20:12 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+
+#include <zephyr/mit-copyright.h>
+
+#ifndef buffer_MODULE
+#define buffer_MODULE
+
+#include "new_string.h"
+
+extern string buffer_to_string();
+extern void clear_buffer();
+extern void append_buffer();
+
+#endif
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: character_class.c,v 1.8 1999/01/22 23:20:13 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <sysdep.h>
+
+#if (!defined(lint) && !defined(SABER))
+static const char rcsid_character_class_c[] = "$Id: character_class.c,v 1.8 1999/01/22 23:20:13 ghudson Exp $";
+#endif
+
+#include <zephyr/mit-copyright.h>
+#include <zephyr/zephyr.h>
+#include "character_class.h"
+
+/*
+ * It may look like we are passing the cache by value, but since it's
+ * really an array we are passing by reference. C strikes again....
+ */
+
+static character_class cache;
+
+/* character_class */
+char * string_to_character_class(str)
+ string str;
+{
+ int i;
+
+ (void) memset(cache, 0, sizeof(cache));
+
+ for (i=0; i<strlen(str); i++)
+ cache[str[i]] = 1;
+
+ return(cache);
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: character_class.h,v 1.6 1999/01/22 23:20:13 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+
+#include <zephyr/mit-copyright.h>
+
+#ifndef character_class_TYPE
+#define character_class_TYPE
+
+#include "new_string.h"
+
+#define NUMBER_OF_CHARACTERS 256
+
+typedef char character_class[NUMBER_OF_CHARACTERS];
+
+extern /* character_class */ char * string_to_character_class();
+
+#endif
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: dictionary.c,v 1.2 1999/01/22 23:20:14 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <sysdep.h>
+
+#if (!defined(lint) && !defined(SABER))
+static const char rcsid_dictionary_c[] = "$Id: dictionary.c,v 1.2 1999/01/22 23:20:14 ghudson Exp $";
+#endif
+
+/*
+ * dictionary - a module implementing a generic dictionary. That is,
+ * any type can be used for the values that keys are bound to.
+ * Keys are always strings.
+ *
+ * Overview:
+ *
+ * A dictionary is a set of bindings which bind values of some
+ * type (this type is the generic parameter of the dictionary) to
+ * strings. At most one value can be bound to any one string.
+ * The value that a string is bound to can be changed later.
+ * Bindings can also be deleted later. It is also possible to
+ * enumerate all of the bindings in a dictionary. Dictionarys
+ * are heap based and must be created & destroyed accordingly.
+ *
+ * Note: This module assumes that malloc NEVER returns 0 for reasonable
+ * requests. It is the users responsibility to either ensure that
+ * this happens or supply a version of malloc with error
+ * handling.
+ *
+ * Dictionarys are mutable.
+ *
+ * Implementation:
+ *
+ * A standard chaining hash table is used to implement dictionarys.
+ * Each dictionary has an associated size (# of slots), allowing
+ * different size dictionaries as needed.
+ */
+
+#include "TYPE_T_dictionary.h"
+#include "new_string.h"
+#include "new_memory.h"
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+/*
+ * TYPE_T_dictionary TYPE_T_dictionary_Create(int size):
+ * Requires: size > 0
+ * Effects: Returns a new empty dictionary containing no bindings.
+ * The returned dictionary must be destroyed using
+ * TYPE_T_dictionary_Destroy. Size is a time vs space
+ * parameter. For this implementation, space used is
+ * proportional to size and time used is proportional
+ * to number of bindings divided by size. It is preferable
+ * that size is a prime number.
+ */
+
+TYPE_T_dictionary TYPE_T_dictionary_Create(size)
+ int size;
+{
+ int i;
+ TYPE_T_dictionary result;
+
+ result = (TYPE_T_dictionary)malloc(sizeof(struct _TYPE_T_dictionary));
+ result->size = size;
+ result->slots = (TYPE_T_dictionary_binding **)malloc(
+ size*sizeof(TYPE_T_dictionary_binding *));
+
+ for (i=0; i<size; i++)
+ result->slots[i] = NULL;
+
+ return(result);
+}
+
+/*
+ * void TYPE_T_dictionary_Destroy(TYPE_T_dictionary d):
+ * Requires: d is a non-destroyed TYPE_T_dictionary
+ * Modifies: d
+ * Effects: Destroys dictionary d freeing up the space it consumes.
+ * Dictionary d should never be referenced again. Note that
+ * free is NOT called on the values of the bindings. If
+ * this is needed, the client must do this first using
+ * TYPE_T_dictionary_Enumerate.
+ */
+
+void TYPE_T_dictionary_Destroy(d)
+ TYPE_T_dictionary d;
+{
+ int i;
+ TYPE_T_dictionary_binding *binding_ptr, *new_binding_ptr;
+
+ for (i=0; i<d->size; i++) {
+ binding_ptr = d->slots[i];
+ while (binding_ptr) {
+ new_binding_ptr = binding_ptr->next;
+ free(binding_ptr->key);
+ free(binding_ptr);
+ binding_ptr = new_binding_ptr;
+ }
+ }
+ free(d->slots);
+ free(d);
+}
+
+/*
+ * void TYPE_T_dictionary_Enumerate(TYPE_T_dictionary d; void (*proc)()):
+ * Requires: proc is a void procedure taking 1 argument, a
+ * TYPE_T_dictionary_binding pointer, which does not
+ * make any calls using dictionary d.
+ * Effects: Calls proc once with each binding in dictionary d.
+ * Order of bindings passed is undefined. Note that
+ * only the value field of the binding should be considered
+ * writable by proc.
+ */
+
+void TYPE_T_dictionary_Enumerate(d, proc)
+ TYPE_T_dictionary d;
+ void (*proc)(/* TYPE_T_dictionary_binding *b */);
+{
+ int i;
+ TYPE_T_dictionary_binding *binding_ptr;
+
+ for (i=0; i<d->size; i++) {
+ binding_ptr = d->slots[i];
+ while (binding_ptr) {
+ proc(binding_ptr);
+ binding_ptr = binding_ptr->next;
+ }
+ }
+}
+
+/*
+ * Private routine:
+ *
+ * unsigned int dictionary__hash(char *s):
+ * Effects: Hashs s to an unsigned integer. This number mod the
+ * hash table size is supposed to roughly evenly distribute
+ * keys over the table's slots.
+ */
+
+static unsigned int dictionary__hash(s)
+ char *s;
+{
+ unsigned int result = 0;
+
+ if (!s)
+ return(result);
+
+ while (s[0]) {
+ result <<= 1;
+ result += s[0];
+ s++;
+ }
+
+ return(result);
+}
+
+/*
+ * TYPE_T_dictionary_binding *TYPE_T_dictionary_Lookup(TYPE_T_dictionary d,
+ * char *key):
+ * Effects: If key is not bound in d, returns 0. Othersize,
+ * returns a pointer to the binding that binds key.
+ * Note the access restrictions on bindings...
+ */
+
+TYPE_T_dictionary_binding *TYPE_T_dictionary_Lookup(d, key)
+ TYPE_T_dictionary d;
+ char *key;
+{
+ TYPE_T_dictionary_binding *binding_ptr;
+
+ binding_ptr = d->slots[dictionary__hash(key)%(d->size)];
+ while (binding_ptr) {
+ if (string_Eq(key, binding_ptr->key))
+ return(binding_ptr);
+ binding_ptr = binding_ptr->next;
+ }
+
+ return(NULL);
+}
+
+/*
+ * TYPE_T_dictionary_binding *TYPE_T_dictionary_Define(TYPE_T_dictionary d,
+ * char *key,
+ * int *already_existed):
+ * Modifies: d
+ * Effects: If key is bound in d, returns a pointer to the binding
+ * that binds key. Otherwise, adds a binding of key to
+ * d and returns its address. If already_existed is non-zero
+ * then *already_existed is set to 0 if key was not
+ * previously bound in d and 1 otherwise.
+ * Note the access restrictions on bindings... Note also
+ * that the value that key is bounded to if a binding is
+ * created is undefined. The caller should set the value
+ * in this case.
+ */
+
+TYPE_T_dictionary_binding *TYPE_T_dictionary_Define(d, key, already_existed)
+ TYPE_T_dictionary d;
+ char *key;
+ int *already_existed;
+{
+ TYPE_T_dictionary_binding **ptr_to_the_slot, *binding_ptr;
+
+ ptr_to_the_slot = &(d->slots[dictionary__hash(key)%(d->size)]);
+
+ binding_ptr = *ptr_to_the_slot;
+ while (binding_ptr) {
+ if (string_Eq(binding_ptr->key, key)) {
+ if (already_existed)
+ *already_existed = 1;
+ return(binding_ptr);
+ }
+ binding_ptr = binding_ptr->next;
+ }
+
+ if (already_existed)
+ *already_existed = 0;
+ binding_ptr = (TYPE_T_dictionary_binding *)malloc(
+ sizeof(TYPE_T_dictionary_binding));
+ binding_ptr->next = *ptr_to_the_slot;
+ binding_ptr->key = string_Copy(key);
+ *ptr_to_the_slot = binding_ptr;
+ return(binding_ptr);
+}
+
+/*
+ * void TYPE_T_dictionary_Delete(TYPE_T_dictionary d,
+ * TYPE_T_dictionary_binding *b):
+ * Requires: *b is a binding in d.
+ * Modifies: d
+ * Effects: Removes the binding *b from d. Note that if
+ * b->value needs to be freed, it should be freed
+ * before making this call.
+ */
+
+void TYPE_T_dictionary_Delete(d, b)
+ TYPE_T_dictionary d;
+ TYPE_T_dictionary_binding *b;
+{
+ TYPE_T_dictionary_binding **ptr_to_binding_ptr;
+
+ ptr_to_binding_ptr = &(d->slots[dictionary__hash(b->key)%(d->size)]);
+
+ while (*ptr_to_binding_ptr != b)
+ ptr_to_binding_ptr = &((*ptr_to_binding_ptr)->next);
+
+ *ptr_to_binding_ptr = b->next;
+ free(b->key);
+ free(b);
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: dictionary.h,v 1.2 1999/01/22 23:20:14 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#ifndef TYPE_T_dictionary_TYPE
+#define TYPE_T_dictionary_TYPE
+
+typedef struct _TYPE_T_dictionary_binding {
+ struct _TYPE_T_dictionary_binding *next; /* PRIVATE */
+ char *key; /* READ-ONLY */
+ TYPE_T value;
+} TYPE_T_dictionary_binding;
+
+typedef struct _TYPE_T_dictionary { /* PRIVATE */
+ int size;
+ TYPE_T_dictionary_binding **slots;
+} *TYPE_T_dictionary;
+
+/*
+ * TYPE_T_dictionary TYPE_T_dictionary_Create(int size):
+ * Requires: size > 0
+ * Effects: Returns a new empty dictionary containing no bindings.
+ * The returned dictionary must be destroyed using
+ * TYPE_T_dictionary_Destroy. Size is a time vs space
+ * parameter. For this implementation, space used is
+ * proportional to size and time used is proportional
+ * to number of bindings divided by size. It is preferable
+ * that size is a prime number.
+ */
+
+extern TYPE_T_dictionary TYPE_T_dictionary_Create(/* int size */);
+
+/*
+ * void TYPE_T_dictionary_Destroy(TYPE_T_dictionary d):
+ * Requires: d is a non-destroyed TYPE_T_dictionary
+ * Modifies: d
+ * Effects: Destroys dictionary d freeing up the space it consumes.
+ * Dictionary d should never be referenced again. Note that
+ * free is NOT called on the values of the bindings. If
+ * this is needed, the client must do this first using
+ * TYPE_T_dictionary_Enumerate.
+ */
+
+extern void TYPE_T_dictionary_Destroy(/* TYPE_T_dictionary d */);
+
+/*
+ * void TYPE_T_dictionary_Enumerate(TYPE_T_dictionary d; void (*proc)()):
+ * Requires: proc is a void procedure taking 1 argument, a
+ * TYPE_T_dictionary_binding pointer, which does not
+ * make any calls using dictionary d.
+ * Effects: Calls proc once with each binding in dictionary d.
+ * Order of bindings passed is undefined. Note that
+ * only the value field of the binding should be considered
+ * writable by proc.
+ */
+
+extern void TYPE_T_dictionary_Enumerate(/* TYPE_T_dictionary d,
+ void (*proc)() */);
+
+/*
+ * TYPE_T_dictionary_binding *TYPE_T_dictionary_Lookup(TYPE_T_dictionary d,
+ * char *key):
+ * Effects: If key is not bound in d, returns 0. Othersize,
+ * returns a pointer to the binding that binds key.
+ * Note the access restrictions on bindings...
+ */
+
+extern TYPE_T_dictionary_binding *TYPE_T_dictionary_Lookup(/* d, key */);
+
+/*
+ * TYPE_T_dictionary_binding *TYPE_T_dictionary_Define(TYPE_T_dictionary d,
+ * char *key,
+ * int *already_existed):
+ * Modifies: d
+ * Effects: If key is bound in d, returns a pointer to the binding
+ * that binds key. Otherwise, adds a binding of key to
+ * d and returns its address. If already_existed is non-zero
+ * then *already_existed is set to 0 if key was not
+ * previously bound in d and 1 otherwise.
+ * Note the access restrictions on bindings... Note also
+ * that the value that key is bounded to if a binding is
+ * created is undefined. The caller should set the value
+ * in this case.
+ */
+
+extern TYPE_T_dictionary_binding *TYPE_T_dictionary_Define();
+
+/*
+ * void TYPE_T_dictionary_Delete(TYPE_T_dictionary d,
+ * TYPE_T_dictionary_binding *b):
+ * Requires: *b is a binding in d.
+ * Modifies: d
+ * Effects: Removes the binding *b from d. Note that if
+ * b->value needs to be freed, it should be freed
+ * before making this call.
+ */
+
+extern void TYPE_T_dictionary_Delete();
+
+#endif
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: display.c,v 1.4 1999/01/22 23:20:15 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#if (!defined(lint) && !defined(SABER))
+static char rcsid_display_c[] = "$Id: display.c,v 1.4 1999/01/22 23:20:15 ghudson Exp $";
+#endif
+
+#include <zephyr/mit-copyright.h>
+
+/****************************************************************************/
+/* */
+/* "Bus" module for plug in output driver modules: */
+/* */
+/****************************************************************************/
+
+#include <sysdep.h>
+#include "new_memory.h"
+#include "new_string.h"
+#include "variables.h"
+#include "display.h"
+
+/*
+ * driver_table - <<<>>>
+ */
+
+extern void tty_driver();
+extern void plain_driver();
+extern void raw_driver();
+
+extern int tty_driver_init();
+
+#ifndef X_DISPLAY_MISSING
+extern int X_driver_init();
+extern void X_driver();
+#endif
+
+static struct driver_info {
+ string driver_name;
+ void (*driver)();
+ int (*driver_init)();
+ void (*driver_reset)();
+} driver_table[] = {
+#ifndef X_DISPLAY_MISSING
+ {"X", X_driver, X_driver_init, X_driver_reset},
+#endif
+ {"tty", tty_driver, tty_driver_init, NULL},
+ {"plain", plain_driver, NULL, NULL},
+ {"raw", raw_driver, NULL, NULL},
+ {NULL, NULL, NULL, NULL}
+};
+
+/*
+ * <<<>>>
+ */
+
+struct driver_info *get_driver_info(driver_name)
+ string driver_name;
+{
+ struct driver_info *d;
+
+ for (d=driver_table; d->driver_name; d++)
+ if (string_Eq(d->driver_name, driver_name) && d->driver)
+ return(d);
+
+ return(NULL);
+}
+
+/*
+ * void init_display(int *pargc; char **argv)
+ * Effects: <<<>>>
+ */
+
+void display_init(pargc, argv)
+ int *pargc;
+ char **argv;
+{
+ char **new, **current;
+ struct driver_info *d;
+ string first_working_driver = "";
+ string default_driver = "";
+
+ /*
+ * Process argument list handling "-disable <driver>" and
+ * "-default <driver>" arguments:
+ */
+ for (new=current=argv+1; *current; current++) {
+ if (string_Eq(*current, "-disable")) {
+ current++; *pargc -= 2;
+ if (!*current)
+ usage();
+ if (d = get_driver_info(*current))
+ d->driver = NULL;
+ } else if (string_Eq(*current, "-default")) {
+ current++; *pargc -= 2;
+ if (!*current)
+ usage();
+ default_driver = *current;
+ } else
+ *(new++) = *current;
+ }
+ *new = *current;
+
+ /*
+ * Initialize all non-disabled drivers. If a driver reports an error,
+ * disable that driver. Set default_driver if not already set
+ * by the -default argument to the first non-disabled driver.
+ */
+ for (d = driver_table; d->driver_name; d++) {
+ if (!d->driver)
+ continue;
+
+ if (d->driver_init && d->driver_init(pargc, argv)) {
+ d->driver = NULL;
+ continue;
+ }
+
+ if (!*first_working_driver)
+ first_working_driver = d->driver_name;
+ }
+
+ if (!get_driver_info(default_driver))
+ default_driver = first_working_driver;
+
+ var_set_variable("output_driver", default_driver);
+}
+
+void display_reset()
+{
+ for (d = driver_table; d->driver_name; d++)
+ if (d->driver) d->driver_reset();
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: error.c,v 1.5 1999/01/22 23:20:15 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <sysdep.h>
+
+#if (!defined(lint) && !defined(SABER))
+static const char rcsid_error_c[] = "$Id: error.c,v 1.5 1999/01/22 23:20:15 ghudson Exp $";
+#endif
+
+#include <zephyr/mit-copyright.h>
+
+int error_code;
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: error.h,v 1.6 1999/10/15 17:15:45 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+
+#include <zephyr/mit-copyright.h>
+
+#ifndef error_MODULE
+#define error_MODULE
+
+#include <stdio.h>
+#include <errno.h>
+#include <com_err.h>
+
+extern int error_code;
+
+#define FATAL_TRAP(function_call, message) \
+ { error_code = (function_call);\
+ if (error_code) {\
+ com_err("zwgc", error_code,\
+ message);\
+ exit(3);\
+ }\
+ }
+
+#define TRAP(function_call, message) \
+ { error_code = (function_call);\
+ if (error_code) {\
+ com_err("zwgc", error_code,\
+ message);\
+ }\
+ }
+
+#define ERROR(a) { fprintf(stderr, "zwgc: "); \
+ fprintf(stderr, a);\
+ fflush(stderr); }
+
+#define ERROR2(a,b) { fprintf(stderr, "zwgc: "); \
+ fprintf(stderr, a, b);\
+ fflush(stderr); }
+
+#define ERROR3(a,b,c) { fprintf(stderr, "zwgc: "); \
+ fprintf(stderr, a, b, c);\
+ fflush(stderr); }
+
+#define ERROR4(a,b,c,d) { fprintf(stderr, "zwgc: "); \
+ fprintf(stderr, a, b, c, d);\
+ fflush(stderr); }
+
+#endif
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: eval.c,v 1.6 1999/01/22 23:20:16 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <sysdep.h>
+
+#if (!defined(lint) && !defined(SABER))
+static const char rcsid_eval_c[] = "$Id: eval.c,v 1.6 1999/01/22 23:20:16 ghudson Exp $";
+#endif
+
+#include <zephyr/mit-copyright.h>
+
+/****************************************************************************/
+/* */
+/* Code to evaluate an expression: */
+/* */
+/****************************************************************************/
+
+#include <zephyr/zephyr.h>
+#include "new_memory.h"
+#include "node.h"
+#include "eval.h"
+#include "substitute.h"
+#include "port.h"
+#include "buffer.h"
+#include "regexp.h"
+#include "text_operations.h"
+#include "zwgc.h"
+#include "variables.h"
+
+/****************************************************************************/
+/* */
+/* Code to deal with string/boolean conversion: */
+/* */
+/****************************************************************************/
+
+/*
+ * Internal Routine:
+ *
+ * int string_to_bool(string str)
+ * Effects: Returns true iff the string str represents true.
+ * True is represented by any string which is equal to
+ * "true" when case is disregraded.
+ */
+
+#define string_to_bool(str) (!strcasecmp(str,"true"))
+
+/*
+ * Internal Routine:
+ *
+ * string bool_to_string(int bool)
+ * Effects: Returns a string representive for the C boolean bool.
+ * (In C, true == non-zero) I.e.,
+ * string_to_bool(bool_to_string(x)) == !!x.
+ * The string returned is on the heap & must be freed
+ * eventually.
+ */
+
+static string bool_to_string(bool)
+ int bool;
+{
+ return(bool ? string_Copy("TRUE") : string_Copy("FALSE"));
+}
+
+/*
+ * int eval_bool_expr(Node *expr)
+ * Modifies: dict
+ * Requires: expr is a proper expression or NULL. (see node.c)
+ * Effects: Evaluates expr to its boolean value which is returned.
+ * NULL is defined to have the boolean value true.
+ */
+
+int eval_bool_expr(expr)
+ Node *expr;
+{
+ string temp;
+ int result;
+
+ if (!expr)
+ return(1);
+
+ temp = eval_expr(expr);
+ result = string_to_bool(temp);
+ free(temp);
+
+ return(result);
+}
+
+/****************************************************************************/
+/* */
+/* Code to evaluate an expression: */
+/* */
+/****************************************************************************/
+
+/*
+ * string eval_expr(Node *expr)
+ * Modifies: dict
+ * Requires: expr is a proper expression (NOT NULL). (see node.c)
+ * Effects: Evaluates expr to its string value which is returned.
+ * The returned string is on the heap and must be freed
+ * eventually.
+ */
+
+string eval_expr(expr)
+ Node *expr;
+{
+ int opcode = expr->opcode;
+ int bool_result;
+ string first, second;
+ char *result;
+ string *text_ptr;
+ char *getenv(); /* UNIX get environment variable function */
+
+ /*
+ * Dispatch based on the opcode of the top node in the expression:
+ */
+ switch (opcode) {
+ case STRING_CONSTANT_OPCODE:
+ return(string_Copy(expr->d.string_constant));
+
+ case VARREF_OPCODE:
+ return(string_Copy(var_get_variable(expr->d.string_constant)));
+
+ case BUFFER_OPCODE:
+ return(string_Copy(buffer_to_string()));
+
+ /*
+ * Handle unary expressions:
+ */
+ case NOT_OPCODE:
+ case SUBSTITUTE_OPCODE:
+ case PROTECT_OPCODE:
+ case VERBATIM_OPCODE:
+ case STYLESTRIP_OPCODE:
+ case GETENV_OPCODE:
+ case UPCASE_OPCODE:
+ case DOWNCASE_OPCODE:
+ case ZVAR_OPCODE:
+ case GET_OPCODE:
+ first = eval_expr(expr->d.nodes.first);
+
+ switch (opcode) {
+ case NOT_OPCODE:
+ result = bool_to_string(! string_to_bool(first));
+ break;
+
+ case SUBSTITUTE_OPCODE:
+ result = substitute(var_get_variable, first);
+ break;
+
+ case PROTECT_OPCODE:
+ result=protect(first);
+ break;
+
+ case VERBATIM_OPCODE:
+ return(verbatim(first,0));
+
+ case STYLESTRIP_OPCODE:
+ return(stylestrip(first));
+
+ case GETENV_OPCODE:
+ result = getenv(first);
+ if (!result)
+ result = string_Copy("");
+ else
+ result = string_Copy(result);
+ break;
+
+ case UPCASE_OPCODE:
+ return(string_Upcase(first));
+
+ case DOWNCASE_OPCODE:
+ return(string_Downcase(first));
+
+ case ZVAR_OPCODE:
+ result = ZGetVariable(first);
+ if (!result)
+ result = string_Copy("");
+ else
+ result = string_Copy(result);
+ break;
+
+ case GET_OPCODE:
+ result = read_from_port(first);
+ break;
+ }
+ free(first);
+ break;
+
+ /*
+ * Handle binary operators:
+ */
+ case PLUS_OPCODE:
+ case AND_OPCODE:
+ case OR_OPCODE:
+ case EQ_OPCODE:
+ case NEQ_OPCODE:
+ case REGEQ_OPCODE:
+ case REGNEQ_OPCODE:
+ first = eval_expr(expr->d.nodes.first);
+ second = eval_expr(expr->d.nodes.second);
+
+ switch (opcode) {
+ case PLUS_OPCODE:
+ result = string_Concat(first, second);
+ free(first);
+ free(second);
+ return(result);
+
+ case AND_OPCODE:
+ bool_result = string_to_bool(first) && string_to_bool(second);
+ break;
+
+ case OR_OPCODE:
+ bool_result = string_to_bool(first) || string_to_bool(second);
+ break;
+
+ case EQ_OPCODE:
+ bool_result = string_Eq(first, second);
+ break;
+
+ case NEQ_OPCODE:
+ bool_result = string_Neq(first, second);
+ break;
+
+ case REGEQ_OPCODE:
+ bool_result = ed_regexp_match_p(first, second);
+ break;
+
+ case REGNEQ_OPCODE:
+ bool_result = !ed_regexp_match_p(first, second);
+ break;
+ }
+ free(first);
+ free(second);
+ result = bool_to_string(bool_result);
+ break;
+
+ /*
+ * Handle text-manipulation operators:
+ */
+ case LANY_OPCODE: case RANY_OPCODE:
+ case LBREAK_OPCODE: case RBREAK_OPCODE:
+ case LSPAN_OPCODE: case RSPAN_OPCODE:
+ first = eval_expr(expr->d.nodes.first);
+ second = eval_expr(expr->d.nodes.second);
+ text_ptr = &first;
+
+ switch (opcode) {
+ case LANY_OPCODE:
+ result = lany(text_ptr, second);
+ break;
+
+ case RANY_OPCODE:
+ result = rany(text_ptr, second);
+ break;
+
+ case LBREAK_OPCODE:
+ result = lbreak(text_ptr, string_to_character_class(second));
+ break;
+
+ case RBREAK_OPCODE:
+ result = rbreak(text_ptr, string_to_character_class(second));
+ break;
+
+ case LSPAN_OPCODE:
+ result = lspan(text_ptr, string_to_character_class(second));
+ break;
+
+ case RSPAN_OPCODE:
+ result = rspan(text_ptr, string_to_character_class(second));
+ break;
+ }
+
+ if (expr->d.nodes.first->opcode == VARREF_OPCODE)
+ var_set_variable(expr->d.nodes.first->d.string_constant, first);
+ free(first);
+ free(second);
+ break;
+
+#ifdef DEBUG
+ default:
+ printf("zwgc: internal error: attempt to evaluate the following non-expression:\n"); fflush(stdout);
+ node_display(expr);
+ printf("\n\n");
+ exit(2);
+#endif
+ }
+
+ return(result);
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: eval.h,v 1.5 1999/01/22 23:20:17 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+
+#include <zephyr/mit-copyright.h>
+
+#ifndef eval_MODULE
+#define eval_MODULE
+
+#include "new_string.h"
+
+/*
+ * string eval_expr(Node *expr)
+ * Modifies: dict
+ * Requires: expr is a proper expression (NOT NULL). (see node.c)
+ * Effects: Evaluates expr to its string value which is returned.
+ * The returned string is on the heap and must be freed
+ * eventually.
+ */
+
+extern string eval_expr();
+
+/*
+ * int eval_bool_expr(Node *expr)
+ * Modifies: dict
+ * Requires: expr is a proper expression or NULL. (see node.c)
+ * Effects: Evaluates expr to its boolean value which is returned.
+ * NULL is defined to have the boolean value true.
+ */
+
+extern int eval_bool_expr();
+
+#endif
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: exec.c,v 1.10 1999/08/13 00:19:51 danw Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <sysdep.h>
+
+#if (!defined(lint) && !defined(SABER))
+static const char rcsid_exec_c[] = "$Id: exec.c,v 1.10 1999/08/13 00:19:51 danw Exp $";
+#endif
+
+#include <zephyr/mit-copyright.h>
+
+/****************************************************************************/
+/* */
+/* Module containing code to execute a program: */
+/* */
+/****************************************************************************/
+
+#include <zephyr/zephyr.h>
+#include "new_memory.h"
+#include "exec.h"
+#include "eval.h"
+#include "node.h"
+#include "buffer.h"
+#include "port.h"
+#include "variables.h"
+#include "notice.h"
+
+static int exec_subtree(), exec_fields();
+
+/****************************************************************************/
+/* */
+/* Utility subroutines: */
+/* */
+/****************************************************************************/
+
+static string eval_exprlist_to_string(exprlist)
+ Node *exprlist;
+{
+ string result = string_Copy("");
+ string temp;
+ int first_time = 1;
+
+ for (; exprlist; exprlist=exprlist->next) {
+ if (!first_time)
+ result = string_Concat2(result, " ");
+ else
+ first_time = 0;
+
+ temp = eval_expr(exprlist);
+ result = string_Concat2(result, temp);
+ free(temp);
+ }
+
+ return(result);
+}
+
+static char **eval_exprlist_to_args(exprlist)
+ Node *exprlist;
+{
+ char **result = (char **)malloc(sizeof(char *));
+ int argc = 0;
+
+ for (; exprlist; exprlist=exprlist->next) {
+ result[argc] = eval_expr(exprlist);
+ argc++;
+ result = (char **)realloc(result, (argc+1)*sizeof(char *));
+ }
+
+ result[argc] = NULL;
+ return(result);
+}
+
+static void free_args(args)
+ char **args;
+{
+ char **p;
+
+ for (p=args; *p; p++) {
+ free(*p);
+ }
+
+ free(args);
+}
+
+/****************************************************************************/
+/* */
+/* Subroutines to handle each particular statement type: */
+/* */
+/****************************************************************************/
+
+#define NOBREAK 0
+#define BREAK 1
+#define EXIT 2
+
+/*ARGSUSED*/
+static int exec_noop(node)
+ Node *node;
+{
+ return(NOBREAK);
+}
+
+/*ARGSUSED*/
+static int exec_break(node)
+ Node *node;
+{
+ return(BREAK);
+}
+
+/*ARGSUSED*/
+static int exec_exit(node)
+ Node *node;
+{
+ return(EXIT);
+}
+
+static int exec_set(node)
+ Node *node;
+{
+ var_set_variable_then_free_value(node->d.nodes.first->d.string_constant,
+ eval_expr(node->d.nodes.second));
+
+ return(NOBREAK);
+}
+
+static int exec_execport(node)
+ Node *node;
+{
+ string name = eval_expr(node->d.nodes.first);
+ char **argv = eval_exprlist_to_args(node->d.nodes.second);
+
+ create_subprocess_port(name, argv);
+
+ free(name);
+ free_args(argv);
+ return(NOBREAK);
+}
+
+static int exec_appendport(node)
+ Node *node;
+{
+ string name, filename;
+
+ name = eval_expr(node->d.nodes.first);
+ filename = eval_expr(node->d.nodes.second);
+
+ create_file_append_port(name, filename);
+
+ free(name);
+ free(filename);
+ return(NOBREAK);
+}
+
+static int exec_inputport(node)
+ Node *node;
+{
+ string name, filename;
+
+ name = eval_expr(node->d.nodes.first);
+ filename = eval_expr(node->d.nodes.second);
+
+ create_file_input_port(name, filename);
+
+ free(name);
+ free(filename);
+ return(NOBREAK);
+}
+
+static int exec_outputport(node)
+ Node *node;
+{
+ string name, filename;
+
+ name = eval_expr(node->d.nodes.first);
+ filename = eval_expr(node->d.nodes.second);
+
+ create_file_output_port(name, filename);
+
+ free(name);
+ free(filename);
+ return(NOBREAK);
+}
+
+static int exec_closeinput(node)
+ Node *node;
+{
+ string name;
+
+ name = eval_expr(node->d.nodes.first);
+ close_port_input(name);
+
+ free(name);
+ return(NOBREAK);
+}
+
+static int exec_closeoutput(node)
+ Node *node;
+{
+ string name;
+
+ name = eval_expr(node->d.nodes.first);
+ close_port_output(name);
+
+ free(name);
+ return(NOBREAK);
+}
+
+static int exec_closeport(node)
+ Node *node;
+{
+ string name;
+
+ name = eval_expr(node->d.nodes.first);
+ close_port_input(name);
+ close_port_output(name);
+
+ free(name);
+ return(NOBREAK);
+}
+
+static int exec_put(node)
+ Node *node;
+{
+ string name, temp;
+
+ if (node->d.nodes.second)
+ temp = eval_exprlist_to_string(node->d.nodes.second);
+ else
+ temp = string_Copy(buffer_to_string());
+
+ if (node->d.nodes.first) {
+ name = eval_expr(node->d.nodes.first);
+
+ write_on_port(name, temp, strlen(temp));
+ free(name);
+ } else
+ write_on_port(var_get_variable("output_driver"), temp, strlen(temp));
+
+ free(temp);
+ return(NOBREAK);
+}
+
+static int exec_print(node)
+ Node *node;
+{
+ string temp;
+
+ temp = eval_exprlist_to_string(node->d.nodes.first);
+ append_buffer(temp);
+ free(temp);
+
+ return(NOBREAK);
+}
+
+/*ARGSUSED*/
+static int exec_clearbuf(node)
+ Node *node;
+{
+ clear_buffer();
+
+ return(NOBREAK);
+}
+
+static int exec_case(node)
+ Node *node;
+{
+ string constant,temp;
+ Node *match, *cond;
+ int equal_p;
+
+ constant = string_Downcase(eval_expr(node->d.nodes.first));
+
+ for (match=node->d.nodes.second; match; match=match->next) {
+ cond = match->d.nodes.first;
+ if (!cond) { /* default case */
+ free(constant);
+ return(exec_subtree(match->d.nodes.second));
+ }
+ for (; cond; cond=cond->next) {
+ temp = string_Downcase(eval_expr(cond));
+ equal_p = string_Eq(constant, temp);
+ free(temp);
+ if (equal_p) {
+ free(constant);
+ return(exec_subtree(match->d.nodes.second));
+ }
+ }
+ }
+
+ free(constant);
+ return(NOBREAK);
+}
+
+static int exec_while(node)
+ Node *node;
+{
+ int continue_code = NOBREAK;
+
+ while (eval_bool_expr(node->d.nodes.first)) {
+ continue_code = exec_subtree(node->d.nodes.second);
+ if (continue_code != NOBREAK)
+ break;
+ }
+
+ if (continue_code == BREAK)
+ continue_code = NOBREAK;
+
+ return(continue_code);
+}
+
+static int exec_if(node)
+ Node *node;
+{
+ Node *conds;
+
+ for (conds=node->d.nodes.first; conds; conds=conds->next)
+ if (eval_bool_expr(conds->d.nodes.first))
+ return(exec_subtree(conds->d.nodes.second));
+
+ return(NOBREAK);
+}
+
+static int exec_exec(node)
+ Node *node;
+{
+ int pid;
+ char **argv = eval_exprlist_to_args(node->d.nodes.first);
+
+ pid = fork();
+ if (pid == -1) {
+ fprintf(stderr, "zwgc: error while attempting to fork: ");
+ perror("");
+ } else if (pid == 0) { /* in child */
+ execvp(argv[0], argv);
+ fprintf(stderr,"zwgc: unable to exec %s: ", argv[0]);
+ perror("");
+ _exit(errno);
+ }
+
+ free_args(argv);
+ return(NOBREAK);
+}
+
+static struct _Opstuff {
+ int (*exec)();
+} const opstuff[] = {
+ { exec_noop }, /* string_constant */
+ { exec_noop }, /* varref */
+ { exec_noop }, /* varname */
+ { exec_noop }, /* not */
+ { exec_noop }, /* plus */
+ { exec_noop }, /* and */
+ { exec_noop }, /* or */
+ { exec_noop }, /* eq */
+ { exec_noop }, /* neq */
+ { exec_noop }, /* regeq */
+ { exec_noop }, /* regneq */
+ { exec_noop }, /* buffer */
+ { exec_noop }, /* substitute */
+ { exec_noop }, /* protect */
+ { exec_noop }, /* verbatim */
+ { exec_noop }, /* stylestrip */
+ { exec_noop }, /* getenv */
+ { exec_noop }, /* upcase */
+ { exec_noop }, /* downcase */
+ { exec_noop }, /* zvar */
+ { exec_noop }, /* get */
+ { exec_noop }, /* lany */
+ { exec_noop }, /* rany */
+ { exec_noop }, /* lbreak */
+ { exec_noop }, /* rbreak */
+ { exec_noop }, /* lspan */
+ { exec_noop }, /* rspan */
+
+ { exec_noop }, /* noop statement */
+ { exec_set },
+ { exec_fields },
+
+ { exec_print },
+ { exec_clearbuf },
+
+ { exec_appendport },
+ { exec_execport },
+ { exec_inputport },
+ { exec_outputport },
+ { exec_put },
+ { exec_closeinput },
+ { exec_closeoutput },
+ { exec_closeport },
+
+ { exec_exec },
+
+ { exec_if },
+ { exec_case },
+ { exec_while },
+ { exec_break },
+ { exec_exit },
+
+ { exec_noop }, /* if */
+ { exec_noop }, /* elseif */
+ { exec_noop }, /* else */
+ { exec_noop }, /* matchlist */
+ { exec_noop }, /* default */
+};
+
+static int exec_subtree(node)
+ Node *node;
+{
+ int retval = NOBREAK;
+
+ for (; node; node=node->next) {
+ retval = (opstuff[node->opcode].exec)(node);
+ if (retval != NOBREAK)
+ return(retval);
+ }
+
+ return(NOBREAK);
+}
+
+/***************************************************************************/
+
+static char *notice_fields;
+static int notice_fields_length = 0;
+static int number_of_fields = 0;
+
+static int exec_fields(node)
+ Node *node;
+{
+ for (node=node->d.nodes.first; node; node=node->next) {
+ var_set_variable_then_free_value(node->d.string_constant,
+ get_next_field(¬ice_fields,
+ ¬ice_fields_length));
+ if (number_of_fields)
+ number_of_fields--;
+ }
+
+ var_set_variable_to_number("number_of_fields", number_of_fields);
+
+ return(NOBREAK);
+}
+
+void exec_process_packet(program, notice)
+ Node *program;
+ ZNotice_t *notice;
+{
+ notice_fields = notice->z_message;
+ notice_fields_length = notice->z_message_len;
+
+ var_set_number_variables_to_fields(notice_fields, notice_fields_length);
+
+ number_of_fields = count_nulls(notice_fields, notice_fields_length)+1;
+ /* workaround for bug in old zwrite */
+ if (notice_fields[notice_fields_length-1] == '\0')
+ number_of_fields--;
+ var_set_variable_to_number("number_of_fields", number_of_fields);
+
+ clear_buffer();
+ (void)exec_subtree(program);
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: exec.h,v 1.5 1999/01/22 23:20:18 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+
+#include <zephyr/mit-copyright.h>
+
+#ifndef exec_MODULE
+#define exec_MODULE
+
+extern void exec_process_packet();
+
+#endif
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: file.c,v 1.5 1999/01/22 23:20:19 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <sysdep.h>
+
+#if (!defined(lint) && !defined(SABER))
+static const char rcsid_file_c[] = "$Id: file.c,v 1.5 1999/01/22 23:20:19 ghudson Exp $";
+#endif
+
+#include <zephyr/mit-copyright.h>
+
+#include <pwd.h>
+#include "new_memory.h"
+#include "new_string.h"
+#include "error.h"
+
+/*
+ * char *get_home_directory()
+ *
+ * Effects: Attempts to locate the user's (by user, the owner of this
+ * process is meant) home directory & return its pathname.
+ * Returns NULL if unable to do so. Does so by first checking
+ * the environment variable HOME. If it is unset, falls back
+ * on the user's password entry.
+ * Note: The returned pointer may point to a static buffer & hence
+ * go away on further calls to getenv, get_home_directory,
+ * getpwuid, or related calls. The caller should copy it
+ * if necessary.
+ */
+
+char *get_home_directory()
+{
+ char *result;
+ char *getenv();
+ struct passwd *passwd_entry;
+
+ if (result = getenv("HOME"))
+ return(result);
+
+ if (!(passwd_entry = getpwuid(getuid())))
+ return(NULL);
+
+ return(passwd_entry->pw_dir);
+}
+
+/*
+ *
+ */
+
+FILE *locate_file(override_filename, home_dir_filename, fallback_filename)
+ char *override_filename;
+ char *home_dir_filename;
+ char *fallback_filename;
+{
+ char *filename;
+ FILE *result;
+
+ errno = 0;
+
+ if (override_filename) {
+ if (string_Eq(override_filename, "-"))
+ return(stdin);
+
+ result = fopen(override_filename, "r");
+ if (!(result = fopen(override_filename, "r"))) {
+ /* <<<>>> */
+ fprintf(stderr, "zwgc: error while opening %s for reading: ",
+ override_filename);
+ perror("");
+ }
+ return(result);
+ }
+
+ if (home_dir_filename) {
+ if (filename = get_home_directory()) {
+ filename = string_Concat(filename, "/");
+ filename = string_Concat2(filename, home_dir_filename);
+ result = fopen(filename, "r");
+ if (result) {
+ free(filename);
+ return(result);
+ }
+ if (errno != ENOENT) {
+ /* <<<>>> */
+ fprintf(stderr, "zwgc: error while opening %s for reading: ",
+ filename);
+ perror("");
+ free(filename);
+ return(result);
+ }
+ free(filename);
+ } else
+ ERROR("unable to find your home directory.\n");
+ }
+
+ if (fallback_filename) {
+ if (!(result = fopen(fallback_filename, "r"))) {
+ /* <<<>>> */
+ fprintf(stderr, "zwgc: error while opening %s for reading: ",
+ fallback_filename);
+ perror("");
+ }
+ return(result);
+ }
+
+ return(NULL);
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: file.h,v 1.5 1999/01/22 23:20:19 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+
+#include <zephyr/mit-copyright.h>
+
+#ifndef file_MODULE
+#define file_MODULE
+
+#include <stdio.h>
+
+extern FILE *locate_file();
+
+#endif
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: formatter.c,v 1.15 1999/01/22 23:20:20 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <sysdep.h>
+
+#if (!defined(lint) && !defined(SABER))
+static const char rcsid_formatter_c[] = "$Id: formatter.c,v 1.15 1999/01/22 23:20:20 ghudson Exp $";
+#endif
+
+#include <zephyr/mit-copyright.h>
+#include <zephyr/zephyr.h>
+
+#include "new_memory.h"
+#include "char_stack.h"
+#include "string_dictionary.h"
+#include "formatter.h"
+#include "text_operations.h"
+
+#if !defined(__STDC__) && !defined(const)
+#define const
+#endif
+
+static int pure_text_length(), env_length();
+
+#ifdef notdef
+static character_class atsign_set = { /* '@' = 0x40 */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+ };
+#endif
+
+static const character_class paren_set = { /* '(' = 0x28, ')' = 0x29 */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+ };
+
+static const character_class sbracket_set = { /* '[' = 0x5b, ']' = 0x5d */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+ };
+
+static const character_class abracket_set = { /* '<' = 0x3c, '>' = 0x3e */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+ };
+
+static const character_class cbracket_set = { /* '{' = 0x7b, '}' = 0x7d */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+ };
+
+static const character_class allbracket_set = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+ };
+
+static const character_class allmaskable_set = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,
+ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+ };
+
+static char brackets[]="()<>[]{}@";
+static char *openbracket[]={"@<","@<","@[","@[","@{","@{","@(","@(","@("};
+static char *closebracket[]={">",">","]","]","}","}",")",")",")"};
+
+static int not_contains(str, set)
+ string str;
+ const character_class set;
+{
+ while (*str && ! set[*str]) str++;
+ return (! *str);
+}
+
+static int pure_text_length(text,terminator)
+ char *text;
+ char terminator;
+{
+ int len=0;
+
+ while (1) {
+ while (*text!='@' && *text!=terminator && *text) {
+ text++;
+ len++;
+ }
+
+ if (*text!='@')
+ return(len);
+
+ if (*(text+1)=='@') {
+ text++;
+ len++;
+ } else if (env_length(text+1) != -1)
+ return(len);
+
+ text++;
+ len++;
+ }
+}
+
+static char otherside(opener)
+char opener;
+{
+ switch (opener) {
+ case '(':
+ return(')');
+ case '{':
+ return('}');
+ case '[':
+ return(']');
+ case '<':
+ return('>');
+ }
+
+#ifdef DEBUG
+ abort();
+#endif
+}
+
+/* the char * that str points to is free'd by this function.
+ * if you want to keep it, save it yourself
+ */
+string verbatim(str, bracketsonly)
+ string str;
+ int bracketsonly;
+{
+ char *temp,*temp2;
+ int bracketnum,len;
+
+ if (strlen(str) == pure_text_length(str,0)) {
+ /* No environments, so consider the fast-and-easy methods */
+
+ if (not_contains(str,allbracket_set)) {
+ temp = string_Copy(str);
+ free(str);
+ return(temp);
+ }
+
+ if (not_contains(str,abracket_set)) {
+ temp=(char *) malloc((len=strlen(str))+4);
+ temp[0]='@';
+ temp[1]='<';
+ (void) memcpy(temp+2,str,len);
+ temp[len+2]='>';
+ temp[len+3]='\0';
+ free(str);
+ return(temp);
+ }
+ if (not_contains(str,sbracket_set)) {
+ temp=(char *) malloc((len=strlen(str))+4);
+ temp[0]='@';
+ temp[1]='[';
+ (void) memcpy(temp+2,str,len);
+ temp[len+2]=']';
+ temp[len+3]='\0';
+ free(str);
+ return(temp);
+ }
+ if (not_contains(str,cbracket_set)) {
+ temp=(char *) malloc((len=strlen(str))+4);
+ temp[0]='@';
+ temp[1]='{';
+ (void) memcpy(temp+2,str,len);
+ temp[len+2]='}';
+ temp[len+3]='\0';
+ free(str);
+ return(temp);
+ }
+ if (not_contains(str,paren_set)) {
+ temp=(char *) malloc((len=strlen(str))+4);
+ temp[0]='@';
+ temp[1]='(';
+ (void) memcpy(temp+2,str,len);
+ temp[len+2]=')';
+ temp[len+3]='\0';
+ free(str);
+ return(temp);
+ }
+ }
+
+ temp=lbreak(&str,bracketsonly?allbracket_set:allmaskable_set);
+ while(*str) {
+ bracketnum=(int) (strchr(brackets,str[0])-brackets);
+ temp=string_Concat2(temp,openbracket[bracketnum]);
+ temp=string_Concat2(temp,temp2=lany(&str," "));
+ free(temp2);
+ temp=string_Concat2(temp,closebracket[bracketnum]);
+ temp=string_Concat2(temp,temp2=lbreak(&str,bracketsonly?
+ allbracket_set:allmaskable_set));
+ free(temp2);
+ }
+ free(str); /* str is "" at this point, anyway */
+
+ return(temp);
+}
+
+/* text points to beginning of text string. return value is
+ length of string, up to but not including the passed terminator
+ or the default terminator \0. The text will not be modified,
+ and @@ will be counted twice */
+
+string protect(str)
+ string str;
+{
+ string temp,temp2,temp3;
+ int len,templen;
+ char_stack chs;
+ char tos;
+
+ temp = string_Copy("");
+ templen = 1;
+ chs = char_stack_create();
+
+ while(*str) {
+ tos = (char_stack_empty(chs)?0:char_stack_top(chs));
+
+ if (*str == tos) {
+ /* if the character is the next terminator */
+
+ temp = (char *) realloc(temp,++templen);
+ temp[templen-2] = *str++;
+ char_stack_pop(chs);
+ temp[templen-1] = '\0';
+ } else if (len = pure_text_length(str,tos)) {
+ if (tos) {
+ /* if the block is text in an environment, just copy it */
+
+ temp2 = string_CreateFromData(str,len);
+ str += len;
+ temp = string_Concat2(temp,temp2);
+ templen += len;
+ free(temp2);
+ } else {
+ /* if the block is top level text, verbatim brackets only
+ (not @'s) and add text to temp */
+
+ temp2 = string_CreateFromData(str,len);
+ str += len;
+ temp3 = verbatim(temp2,1);
+ temp = string_Concat2(temp,temp3);
+ templen += strlen(temp3);
+ free(temp3);
+ }
+ } else {
+ /* if the block is an environment, copy it, push delimiter */
+
+ len = env_length(str+1);
+ char_stack_push(chs,otherside(str[len+1]));
+ len += 2;
+ temp2 = string_CreateFromData(str,len);
+ str += len;
+ temp = string_Concat2(temp,temp2);
+ templen += len;
+ free(temp2);
+ }
+ }
+ /* all blocks have been copied. */
+
+ while (!char_stack_empty(chs)) {
+ temp = (char *) realloc(temp,++templen);
+ temp[templen-2] = char_stack_top(chs);
+ char_stack_pop(chs);
+ }
+ temp[templen-1] = '\0';
+
+ return(temp);
+}
+
+/* str points to a string. return value is another string
+ which is the original with all styles removed. */
+string stylestrip(str)
+ string str;
+{
+ int templen = 0, otherchar;
+ char *temp = (char *) malloc(string_Length(str) + 1);
+ char_stack chs;
+ string ostr = str;
+
+ chs = char_stack_create();
+
+ while (*str) {
+ if (*str == '@') {
+ int len = env_length(str + 1);
+ if (len != -1) {
+ otherchar = 0;
+ if ((len == 4 && !strncasecmp(str + 1, "font", 4))
+ || (len == 5 && !strncasecmp(str + 1, "color", 5)))
+ otherchar = 0x80;
+ otherchar |= otherside(str[len + 1]);
+ char_stack_push(chs, otherchar);
+ str += len + 2;
+ continue;
+ }
+ }
+ if (!char_stack_empty(chs) && *str == (char_stack_top(chs) & 0x7f)) {
+ char_stack_pop(chs);
+ str++;
+ continue;
+ }
+ if (!char_stack_empty(chs) && (char_stack_top(chs) & 0x80))
+ str++;
+ else
+ temp[templen++] = *str++;
+ }
+ temp[templen] = 0;
+
+ while (!char_stack_empty(chs))
+ char_stack_pop(chs);
+ free(ostr);
+
+ return(temp);
+}
+
+void free_desc(desc)
+ desctype *desc;
+{
+ desctype *next_desc;
+
+ while (desc->code != DT_EOF) {
+ next_desc = desc->next;
+ free(desc);
+ desc = next_desc;
+ }
+ free(desc);
+}
+
+/* text points to beginning of possible env name. return value is
+ length of env name, not including @ or opener, or -1 if not a
+ possible env name. */
+static int env_length(text)
+ char *text;
+{
+ int len=0;
+
+ while (*text && (isalnum(*text) || *text=='_')) {
+ text++;
+ len++;
+ }
+
+ if ((*text=='(') || (*text=='{') || (*text=='[') || (*text=='<'))
+ return(len);
+ else
+ return(-1);
+}
+
+/* text points to beginning of text string. return value is
+ length of string, up to but not including the passed terminator
+ or the default terminators \0 \n @. This can modify text, and 0
+ is a valid return value. */
+static int text_length(text,terminator)
+ char *text;
+ char terminator;
+{
+ int len=0;
+
+ while (1) {
+ while (*text!='@' && *text!='\n' && *text!=terminator && *text) {
+ text++;
+ len++;
+ }
+
+ if (*text!='@')
+ return(len);
+
+ if (*(text+1)=='@')
+ (void) memmove(text+1,text+2,strlen(text+1));
+ else if (env_length(text+1) != -1)
+ return(len);
+
+ text++;
+ len++;
+ }
+}
+
+/* parses str into a desc linked list. Returns number of strings and
+ newlines in *pstr and *pnl */
+
+desctype *disp_get_cmds(str,pstr,pnl)
+char *str;
+int *pstr,*pnl;
+{
+ desctype *desc,*here;
+ int len;
+ char_stack terminators = char_stack_create();
+ char terminator;
+ int nstr=0, nnl=0;
+ char *curstr;
+
+ desc=(desctype *) malloc(sizeof(desctype));
+ here=desc;
+ curstr=str;
+ terminator = '\0';
+
+ while (*curstr) {
+ if (*curstr=='\n') {
+ here->code=DT_NL;
+ curstr++;
+ nnl++;
+ } else if (*curstr==terminator) { /* if this is the end of an env */
+ here->code=DT_END;
+ terminator = char_stack_top(terminators);
+ char_stack_pop(terminators);
+ curstr++;
+ } else if (len=text_length(curstr,terminator)) { /* if there is a text
+ block here */
+ here->code=DT_STR;
+ here->str=curstr;
+ here->len=len;
+ curstr+=len;
+ nstr++;
+ } else if (*curstr=='@') { /* if this is the beginning of an env */
+ len=env_length(curstr+1);
+ here->code=DT_ENV;
+ here->str=curstr+1;
+ here->len=len;
+ char_stack_push(terminators, terminator);
+ terminator=otherside(*(curstr+1+len));
+ curstr+=(len+2); /* jump over @, env name, and opener */
+ }
+
+ here->next=(desctype *) malloc(sizeof(desctype));
+ here=here->next;
+ }
+
+ while (!char_stack_empty(terminators)) {
+ here->code=DT_END;
+ terminator = char_stack_top(terminators);
+ char_stack_pop(terminators);
+ here->next=(desctype *) malloc(sizeof(desctype));
+ here=here->next;
+ }
+ here->code=DT_EOF;
+ *pstr=nstr;
+ *pnl=nnl;
+
+#ifdef DEBUG_PRINTOUT
+ { string temp;
+ here = desc;
+ while (here->code != DT_EOF) {
+ if (here->code == DT_STR || here->code == DT_ENV) {
+ temp = string_CreateFromData(here->str, here->len);
+ printf("[%d <%s>]\n", here->code, temp);
+ free(temp);
+ } else
+ printf("[%d]\n", here->code);
+ here=here->next;
+ }
+ }
+#endif
+
+ return(desc);
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: formatter.h,v 1.6 1999/05/19 19:43:38 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+
+#include <zephyr/mit-copyright.h>
+
+#ifndef formatter_MODULE
+#define formatter_MODULE
+
+typedef struct _desctype {
+ struct _desctype *next;
+
+ short int code;
+#define DT_EOF 0 /* End of message. */
+#define DT_ENV 1 /* Open environment. */
+#define DT_STR 2 /* Display string. */
+#define DT_END 3 /* Close environment. */
+#define DT_NL 4 /* Newline. */
+
+ char *str; /* Name of environment, string to be displayed. */
+ int len; /* Length of string/environment name for
+ ENV, STR, END. Undefined for EOF */
+} desctype;
+
+extern desctype *disp_get_cmds();
+extern void free_desc();
+
+#endif
--- /dev/null
+#!/bin/sh -
+
+# This file is part of the Project Athena Zephyr Notification System.
+# It is one of the source files comprising zwgc, the Zephyr WindowGram
+# client.
+#
+# $Id: instantiate,v 1.1 1997/09/14 22:14:07 ghudson Exp $
+#
+# Copyright (c) 1989,1993 by the Massachusetts Institute of Technology.
+# For copying and distribution information, see the file
+# "mit-copyright.h".
+#
+
+if [ "$1" = "" ]; then
+ echo "Usage: generate_instance <srcdir> <type> <name> [<include file>]"
+ exit 1
+fi
+
+source=$1
+type=$2
+name=$3
+incfile=$4
+
+if [ "$type" != "stack" ]; then
+ if [ ! -f ${source}/${type}.c ]; then
+ echo "$0: unable to open ${source}/${type}.c"
+ exit 2
+ fi
+ sed "s/TYPE_T/$name/g" ${source}/${type}.c > ${name}_${type}.c
+fi
+
+if [ "$incfile" != "" ]; then
+ echo "#include \"$incfile\"" > ${name}_${type}.h
+fi
+if [ ! -f ${source}/${type}.h ]; then
+ echo "$0: unable to open ${source}/${type}.h"
+ exit 2
+fi
+sed "s/TYPE_T/$name/g" ${source}/${type}.h >> ${name}_${type}.h
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: lexer.c,v 1.5 1999/01/22 23:20:21 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <sysdep.h>
+
+#if (!defined(lint) && !defined(SABER))
+static const char rcsid_lexer_c[] = "$Id: lexer.c,v 1.5 1999/01/22 23:20:21 ghudson Exp $";
+#endif
+
+#include <zephyr/mit-copyright.h>
+
+/****************************************************************************/
+/* */
+/* The lexer for the zwgc description language: */
+/* */
+/****************************************************************************/
+
+#include "new_memory.h"
+#include "new_string.h"
+#include "int_dictionary.h"
+#include "lexer.h"
+#include "parser.h"
+#include "y.tab.h"
+
+/*
+ * yylineno - this holds the current line # we are on. Updated automatically
+ * by input() and unput().
+ */
+
+int yylineno;
+
+/*
+ * keyword_dict - this dictionary maps keyword names to their token numbers.
+ */
+
+static int_dictionary keyword_dict = NULL;
+
+/****************************************************************************/
+/* */
+/* I/O functions: */
+/* */
+/****************************************************************************/
+
+/*
+ * input_file - this holds the FILE pointer to the file currently being lexed.
+ */
+
+static FILE *input_file;
+
+/*
+ * pushback - if not -1, holds a character that was pushed back by unput but
+ * not yet read by input.
+ */
+
+static int pushback = -1;
+
+static char input()
+{
+ int c;
+
+ if (pushback != -1) {
+ c = pushback;
+ pushback = -1;
+ if (c=='\n')
+ yylineno++;
+ return(c);
+ }
+
+ c = getc(input_file);
+ if (c=='\n')
+ yylineno++;
+ if (c==EOF)
+ c = 0;
+
+ return(c);
+}
+
+static void unput(c)
+ int c;
+{
+#ifdef DEBUG
+ if (pushback != -1) {
+ printf("Attempt to push back 2 characters at one time!\n");
+ exit(1);
+ }
+#endif
+
+ pushback = c;
+ if (c == '\n')
+ yylineno--;
+}
+
+/****************************************************************************/
+/* */
+/* Initialization routines: */
+/* */
+/****************************************************************************/
+
+struct keyword_info {
+ string keyword;
+ int keyword_number;
+};
+
+/*
+ * keywords - This table holds a copy of the mapping from keyword name to
+ * token number and is used to initialize keyword_dict:
+ */
+
+static struct keyword_info keywords[] = {
+ { "and", '&' },
+ { "appendport", APPENDPORT },
+ { "buffer", BUFFER },
+ { "break", BREAK },
+ { "closeinput", CLOSEINPUT },
+ { "closeoutput", CLOSEOUTPUT },
+ { "closeport", CLOSEPORT },
+ { "case", CASE },
+ { "clearbuf", CLEARBUF },
+ { "default", DEFAULT },
+ { "do", DO },
+ { "downcase", DOWNCASE },
+ { "else", ELSE },
+ { "elseif", ELSEIF },
+ { "endcase", ENDCASE },
+ { "endif", ENDIF },
+ { "endwhile", ENDWHILE },
+ { "exec", EXEC },
+ { "execport", EXECPORT },
+ { "exit", EXIT },
+ { "fields", FIELDS },
+ { "get", GET },
+ { "getenv", GETENV },
+ { "if", IF },
+ { "inputport", INPUTPORT },
+ { "lany", LANY },
+ { "lbreak", LBREAK },
+ { "lspan", LSPAN },
+ { "match", MATCH },
+ { "noop", NOOP },
+ { "not", '!' },
+ { "or", '|' },
+ { "outputport", OUTPUTPORT },
+ { "print", PRINT },
+ { "protect", PROTECT },
+ { "put", PUT },
+ { "rany", RANY },
+ { "rbreak", RBREAK },
+ { "rspan", RSPAN },
+ { "set", SET },
+ { "show", SHOW },
+ { "stylestrip", STYLESTRIP },
+ { "substitute", SUBSTITUTE },
+ { "then", THEN },
+ { "upcase", UPCASE },
+ { "while", WHILE },
+ { "verbatim", VERBATIM },
+ { "zvar", ZVAR } };
+
+/*
+ * lex_open - this routine [re]initializes the lexer & prepares it to lex
+ * a file. Resets current line # to 1.
+ */
+
+void lex_open(file)
+ FILE *file;
+{
+ /*
+ * Initialize I/O:
+ */
+ input_file = file;
+ yylineno = 1;
+ pushback = -1;
+
+ /*
+ * Initialize keyword_dict from keywords if needed:
+ */
+ if (!keyword_dict) {
+ int i;
+
+ keyword_dict = int_dictionary_Create(101);
+
+ for (i=0; i<sizeof(keywords)/sizeof(struct keyword_info); i++)
+ int_dictionary_Define(keyword_dict, keywords[i].keyword,
+ 0)->value = keywords[i].keyword_number;
+ }
+}
+
+/****************************************************************************/
+/* */
+/* lex subroutines: */
+/* */
+/****************************************************************************/
+
+/*
+ * eat_escape_code - this rountine eats an escape code & returns the character
+ * it codes for or 0 if it codes for "".
+ * (an escape code is what follows a '\\' in a quoted
+ * string) Current escape codes are:
+ *
+ * "n" == '\n'
+ * "t" == '\t'
+ * "b" == '\b'
+ * "\n" == "" (i.e., returns 0)
+ * <EOF> == ""
+ * [0-7]{1,3} == the character represented by the code
+ * interpreted as an octal number.
+ * [^ntb0-7\n] == the same character. I.e., "*" == '*'
+ */
+
+#define is_octal_digit(c) (((c)>='0') && ((c)<='7'))
+
+static char eat_escape_code()
+{
+ int c, coded_char;
+
+ c = input();
+
+ switch (c) {
+ case 0: /* i.e., EOF */
+ unput(c);
+ return(c);
+ case '\n':
+ return(0);
+ case 'n':
+ return('\n');
+ case 't':
+ return('\t');
+ case 'b':
+ return('\b');
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ coded_char = c - '0';
+ c = input();
+ if (!is_octal_digit(c)) {
+ unput(c);
+ return(coded_char);
+ }
+ coded_char = coded_char*8 + c-'0';
+ c = input();
+ if (!is_octal_digit(c)) {
+ unput(c);
+ return(coded_char);
+ }
+ return(coded_char*8 + c-'0');
+ default:
+ return(c);
+ }
+}
+
+/*
+ * eat_string - this routine eats characters allowing escape codes via '\\'
+ * until a '"' is eaten. If no '"' is seen before a '\n' or
+ * the <EOF>, a parse_error is set & 0 is returned. Otherwise,
+ * the string represented by what has been eaten is returned.
+ * I.e., 'hello \n there"' would cause "hello \n there" to be
+ * returned. (thats not a <cr> in the first case, a <cr> in the
+ * second) The returned string is on the heap & must be freed
+ * eventually. This routine should be passed the line # that the
+ * string we are eating started on.
+ */
+
+static char *eat_string(starting_line)
+ int starting_line;
+{
+ int c;
+ char buffer[500];
+ char *ptr = buffer;
+
+ for (;;) {
+ /*
+ * Get the next input character, handling EOF:
+ */
+ c = input();
+ if (!c) {
+ unput(c);
+ report_parse_error("unterminated string found beginning",
+ starting_line);
+ return(0);
+ }
+
+ /*
+ * Deal with special characters ('\\', '"', and '\n'):
+ */
+ if (c=='\\') {
+ c = eat_escape_code();
+ if (!c)
+ continue;
+ } else if (c == '"') {
+ *ptr = 0;
+ return(string_Copy(buffer));
+ } else if (c == '\n') {
+ unput(c); /* fix line # reference to right line # */
+ report_parse_error("carriage return found in string", yylineno);
+ return(0);
+ }
+
+ /*
+ * Add the character c to the current string:
+ */
+ *ptr = c;
+ ptr++;
+
+ /*
+ * If out of buffer space, do a recursive call then
+ * concatanate the result to the string read in so far to get the
+ * entire string and return that:
+ */
+ if (ptr>buffer+sizeof(buffer)-20) {
+ string rest_of_string, result;
+
+ rest_of_string = eat_string(starting_line);
+ if (!rest_of_string)
+ return(0);
+
+ *ptr = 0;
+ result = string_Concat(buffer, rest_of_string);
+ free(rest_of_string);
+ return(result);
+ }
+ }
+}
+
+/*
+ * eat_show_line - internal routine for eat_show:
+ *
+ * This routine reads in a physical line of text allowing escape
+ * codes via '\\'. If the line ends with a newline, the newline is eaten.
+ * If the line ends with a EOF, the EOF is not eaten. The string
+ * represented by what has been eaten is returned. The returned string
+ * is on the heap & must be freed eventually. If test_for_endshow is
+ * true and the line read in starts off with "endshow" exactly
+ * (i.e., no escape codes) followed by any non-identifier-char, then
+ * instead of doing the above, we just eat the "endshow" & return 0.
+ */
+
+static char *eat_show_line(test_for_endshow)
+ int test_for_endshow;
+{
+ int c;
+ int saw_escape_code = 0;
+ int starting_line = yylineno;
+ char buffer[200]; /* This must be large enough to hold "endshow" */
+ char *ptr = buffer;
+
+ while (yylineno == starting_line) {
+ c = input();
+ if (!c) {
+ unput(c);
+ *ptr = '\0';
+ return(string_Copy(buffer));
+ } else if (c == '\\') {
+ saw_escape_code = 1;
+ c = eat_escape_code();
+ if (!c)
+ continue;
+ }
+
+ *ptr = c;
+ ptr++;
+
+ if ((ptr==buffer+strlen("endshow")) && test_for_endshow)
+ if (!strncmp(buffer, "endshow", strlen("endshow"))
+ && !saw_escape_code) {
+ c = input();
+ unput(c);
+ if (!is_identifier_char(c))
+ return(0);
+ }
+
+ if (ptr>buffer+sizeof(buffer)-2) {
+ string the_line;
+ string rest_of_line = eat_show_line(0);
+
+ *ptr = '\0';
+ the_line = string_Concat(buffer, rest_of_line);
+ free(rest_of_line);
+ return(the_line);
+ }
+ }
+
+ *ptr = '\0';
+ return(string_Copy(buffer));
+}
+
+/*
+ * eat_til_endshow - this routine eats characters allowing escape codes via
+ * '\\' up to a endshow\{nonalpha} found at the
+ * start of a line not counting leading whitespace.
+ * If <EOF> is seen before the terminator, a parse_error
+ * is set & 0 returned. Otherwise, the string represented
+ * by what has been eaten (escape codes replaced by what
+ * they stand for and leading spaces and tabs removed from
+ * each physical line) is returned. The returned string
+ * is on the heap & must be freed eventually. Note that
+ * to embed endshow in a message, endsho\w can be used.
+ * This routine should be passed the line # of the show
+ * command it is being used to process for use in error
+ * messages.
+ */
+
+static char *eat_til_endshow(start_line_no)
+ int start_line_no;
+{
+ register int c;
+ string text_so_far = string_Copy("");
+ string next_line;
+
+ for (;;) {
+ /*
+ * Skip the spaces & tabs at the start of the current line:
+ */
+ while ((c=input()), c==' ' || c=='\t') ;
+ unput(c);
+
+ /*
+ * Handle unterminated shows:
+ */
+ if (!c) {
+ report_parse_error("unterminated show beginning", start_line_no);
+ free(text_so_far);
+ return(0);
+ }
+
+ /*
+ * Read in rest of the line (including the <cr> at end), allowing
+ * for escape codes and checking for "endshow{nonalpha}" at the
+ * start of the line. (Note: \<newline> is considered the
+ * end of a line here!)
+ */
+ next_line = eat_show_line(1);
+
+ if (!next_line) /* i.e., is this the endshow line? */
+ return(text_so_far);
+
+ text_so_far = string_Concat2(text_so_far, next_line);
+ free(next_line);
+ }
+}
+
+/*
+ * handle_show - this routine is called after "show"\{nonalpha} is
+ * found to handle up to the endshow. The token # is
+ * returned.
+ */
+
+static int handle_show()
+{
+ int c;
+ int start_line_no = yylineno;
+
+ /*
+ * Eat up ' ' and '\t's after show. If the next character is a newline,
+ * eat it. This is so we don't get an extra newline when we call
+ * eat_til_endshow:
+ */
+ while (c=input(), c==' ' || c=='\t') ;
+ if (c!='\n')
+ unput(c);
+
+ if (yylval.text = eat_til_endshow(start_line_no))
+ return(SHOW);
+ else
+ return(ERROR);
+}
+
+/****************************************************************************/
+/* */
+/* The main lexer itself: */
+/* */
+/****************************************************************************/
+
+/*
+ * yylex - performs as per. the yacc manual's requirements
+ */
+
+int yylex()
+{
+ register int c, last_char;
+ register char *ptr;
+ int start_line_no;
+ int_dictionary_binding *binding;
+ char varname[MAX_IDENTIFIER_LENGTH+1];
+
+ for (;;) {
+ switch (c = input()) {
+
+ /*
+ * Skip whitespace:
+ */
+ case ' ': case '\t': case '\n':
+ continue;
+
+ /*
+ * '#' comments out everything up to the and including
+ * the next <cr>:
+ */
+ case '#':
+ while ( (c=input()) && (c!='\n') ) ;
+ if (!c)
+ unput(c);
+ continue;
+
+ /*
+ * Handle c-style comments. Note that "/[^*]" is not the start
+ * of any valid token.
+ */
+ case '/':
+ start_line_no = yylineno;
+
+ /* verify that next character is a '*': */
+ if ((c=input()) != '*')
+ return(ERROR);
+
+ /* Scan until "*\/" or <EOF>: */
+ for (last_char=0; ; last_char=c) {
+ c = input();
+ if (c == '/' && (last_char=='*'))
+ break;
+ if (!c) {
+ unput(c);
+ report_parse_error("unterminated c style comment found beginning", start_line_no);
+ return(ERROR);
+ }
+ }
+ continue;
+
+ /*
+ * The following characters lex as themselves:
+ * '+', '|', '&', '(', ')', '.', ',' and <EOF>:
+ */
+ case 0: case '+': case '|': case '&': case '(':
+ case ')': case '.': case ',':
+ return(c);
+
+ /*
+ * Handle "=[^~=]", "=~", and "==":
+ */
+ case '=':
+ switch (c = input()) {
+ case '~':
+ return(REGEQ);
+ case '=':
+ return(EQ);
+ default:
+ unput(c);
+ return('=');
+ }
+
+ /*
+ * Handle "![^~=]", "!~", and "!=":
+ */
+ case '!':
+ switch (c = input()) {
+ case '~':
+ return(REGNEQ);
+ case '=':
+ return(NEQ);
+ default:
+ unput(c);
+ return('!');
+ }
+
+ /*
+ * Handle identifiers and keywords:
+ *
+ * Note that the below set of characters is hard coded from
+ * is_identifier_char from parser.h.
+ */
+ case 'a': case 'b': case 'c': case 'd': case 'e':
+ case 'f': case 'g': case 'h': case 'i': case 'j':
+ case 'k': case 'l': case 'm': case 'n': case 'o':
+ case 'p': case 'q': case 'r': case 's': case 't':
+ case 'u': case 'v': case 'w': case 'x': case 'y':
+ case 'z':
+ case 'A': case 'B': case 'C': case 'D': case 'E':
+ case 'F': case 'G': case 'H': case 'I': case 'J':
+ case 'K': case 'L': case 'M': case 'N': case 'O':
+ case 'P': case 'Q': case 'R': case 'S': case 'T':
+ case 'U': case 'V': case 'W': case 'X': case 'Y':
+ case 'Z':
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ case '_':
+ /*
+ * Read in the first MAX_IDENTIFIER_LENGTH characters of the
+ * identifier into varname null terminated. Eat
+ * the rest of the characters of the identifier:
+ */
+ for (ptr = varname;;) {
+ if (ptr<varname+MAX_IDENTIFIER_LENGTH)
+ *(ptr++) = c;
+ c = input();
+ if (!is_identifier_char(c))
+ break;
+ }
+ unput(c);
+ *ptr = '\0';
+
+ /*
+ * Look up the identifier in the keyword dictionary.
+ * If its a match, return the keyword's #. In the case
+ * of show, call handle_show to do more processing.
+ * If not a match, treat as a variable name.
+ */
+ binding = int_dictionary_Lookup(keyword_dict, varname);
+ if (!binding) {
+ yylval.text = string_Copy(varname);
+ return(VARNAME);
+ }
+ if (binding->value == SHOW)
+ return(handle_show());
+ else
+ return(binding->value);
+
+ /*
+ * Handle "${identifier}". Note that $ followed by a
+ * non-identifier character is not the start of any valid token.
+ */
+ case '$':
+ c = input();
+ if (!is_identifier_char(c))
+ return(ERROR);
+
+ /*
+ * Read in the first MAX_IDENTIFIER_LENGTH characters of the
+ * identifier into varname null terminated. Eat
+ * the rest of the characters of the identifier:
+ */
+ for (ptr = varname;;) {
+ if (ptr<varname+MAX_IDENTIFIER_LENGTH)
+ *(ptr++) = c;
+ c = input();
+ if (!is_identifier_char(c))
+ break;
+ }
+ unput(c);
+ *ptr = '\0';
+
+ yylval.text = string_Copy(varname);
+ return(VARREF);
+
+ /*
+ * Handle constant strings:
+ */
+ case '"':
+ if (yylval.text = eat_string(yylineno))
+ return(STRING);
+ else
+ return(ERROR);
+
+ /*
+ * All other characters do not start valid tokens:
+ */
+ default:
+ return(ERROR);
+ }
+ }
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: lexer.h,v 1.5 1999/01/22 23:20:22 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+
+#include <zephyr/mit-copyright.h>
+
+#ifndef lexer_MODULE
+#define lexer_MODULE
+
+#include <ctype.h>
+
+/*
+ * is_identifier_char(c) - is c a character that could be part of
+ * an identifier?
+ *
+ * NOTE: this information is hardwired into yylex() in lexer.c!
+ */
+
+#define is_identifier_char(c) (isalnum(c) || (c)=='_')
+
+/*
+ * The maximum # of significant letters in an identifier:
+ *
+ * Note: in order for all keywords to be recognized, this must be at least 20.
+ */
+
+#define MAX_IDENTIFIER_LENGTH 128
+
+/*
+ * yylineno - this holds the current line # we are on. Updated automatically
+ * by yylex.
+ */
+
+extern int yylineno;
+
+/*
+ * lex_open - this routine [re]initializes the lexer & prepares it to lex
+ * a file. Resets current line # to 1.
+ */
+
+extern void lex_open(/* FILE *file */);
+
+/*
+ * yylex - performs as per. the yacc manual's requirements
+ */
+
+extern int yylex();
+
+#endif
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: main.c,v 1.36 1999/01/22 23:20:22 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <sysdep.h>
+#ifdef HAVE_ARES
+#include <ares.h>
+#endif
+
+#if (!defined(lint) && !defined(SABER))
+static const char rcsid_main_c[] = "$Id: main.c,v 1.36 1999/01/22 23:20:22 ghudson Exp $";
+#endif
+
+#include <netdb.h>
+#include <sys/socket.h>
+#include <sys/resource.h>
+#include <zephyr/mit-copyright.h>
+#include <zephyr/zephyr.h>
+
+#include "new_memory.h"
+#include "zwgc.h"
+#include "parser.h"
+#include "node.h"
+#include "exec.h"
+#include "zephyr.h"
+#include "notice.h"
+#include "subscriptions.h"
+#include "file.h"
+#include "mux.h"
+#include "port.h"
+#include "variables.h"
+#include "main.h"
+
+extern void notice_handler();
+static void process_notice(), setup_signals(), detach(), signal_exit();
+#ifdef HAVE_ARES
+static void notice_callback();
+#endif
+
+/*
+ * Global zwgc-wide variables:
+ */
+
+#ifdef DEBUG
+int zwgc_debug = 0;
+#endif
+
+static char *zwgc_version_string = "1.0";
+
+/*
+ * description_filename_override - <<<>>>
+ */
+
+static char *description_filename_override = NULL;
+
+/*
+ * progname - <<<>>> export!
+ */
+
+char *progname = NULL;
+
+/*
+ * subscriptions_filename_override - <<<>>> export!
+ */
+
+char *subscriptions_filename_override = NULL;
+
+/*
+ * location_override - <<<>>> export!
+ */
+
+char *location_override = NULL;
+
+#ifdef HAVE_ARES
+/*
+ * achannel - <<<>>> export!
+ */
+
+ares_channel achannel;
+#endif
+
+/****************************************************************************/
+/* */
+/* Code to deal with reading in the description file: */
+/* */
+/****************************************************************************/
+
+/*
+ * program - this holds a pointer to the node representation of the
+ * description file once it has been read in.
+ */
+
+static struct _Node *program = NULL;
+
+/*
+ * <<<>>>
+ */
+
+static void fake_startup_packet()
+{
+ ZNotice_t notice;
+ struct timezone tz;
+ char msgbuf[BUFSIZ];
+
+ var_set_variable("version", zwgc_version_string);
+
+ (void) memset(¬ice, 0, sizeof(notice));
+
+ notice.z_version = "";
+ notice.z_class = "WG_CTL_CLASS";
+ notice.z_class_inst = "WG_CTL_USER<<<>>>";
+ notice.z_opcode = "WG_STARTUP";
+ notice.z_default_format = "Zwgc mark II version $version now running...\n";
+ notice.z_recipient = "";
+ notice.z_sender = "ZWGC";
+ gettimeofday(¬ice.z_time,&tz);
+ notice.z_port = 0;
+ notice.z_kind = ACKED;
+ notice.z_auth = ZAUTH_YES;
+ sprintf(msgbuf,"Zwgc mark II version %s now running...",
+ zwgc_version_string);
+ notice.z_message = msgbuf;
+ notice.z_message_len = strlen(notice.z_message)+1;
+
+ process_notice(¬ice, NULL);
+}
+
+static void read_in_description_file()
+{
+ FILE *input_file;
+ char defdesc[128];
+
+/* var_clear_all_variables(); <<<>>> */
+
+ sprintf(defdesc, "%s/zephyr/%s", DATADIR, DEFDESC);
+ input_file = locate_file(description_filename_override, USRDESC, defdesc);
+ if (input_file)
+ program = parse_file(input_file);
+ else
+ program = NULL;
+
+ fake_startup_packet();
+}
+
+/****************************************************************************/
+/* */
+/* Code to deal with argument parsing & overall control: */
+/* */
+/****************************************************************************/
+
+/*
+ * void usage()
+ * Effects: Prints out an usage message on stderr then exits the
+ * program with error code 1.
+ */
+
+void usage()
+{
+#ifdef DEBUG
+ fprintf(stderr, "\
+zwgc: usage: zwgc [-debug] [-f <filename>] [-subfile <filename>]\n\
+ [-ttymode] [-nofork] [-reenter] [-loc text]\n\
+ [-default <driver>] {-disable <driver>}*\n\
+ [output driver options]\n");
+#else
+ fprintf(stderr, "\
+zwgc: usage: zwgc [-f <filename>] [-subfile <filename>]\n\
+ [-ttymode] [-nofork] [-reenter] [-loc text]\n\
+ [-default <driver>] {-disable <driver>}*\n\
+ [output driver options]\n");
+#endif
+ exit(1);
+}
+
+/*
+ * <<<>>>
+ */
+
+static void run_initprogs()
+{
+ /*
+ * This code stolen from old zwgc: yuck. Clean up & fix. <<<>>>
+ * Should this fork instead of just systeming?
+ */
+
+ int status;
+ char *progname = ZGetVariable("initprogs");
+
+ if (!progname)
+ return;
+
+ status = system(progname);
+ if (status == 127) {
+ perror("zwgc initprog exec");
+ fprintf(stderr,"zwgc initprog of <%s> failed: no shell.\n",
+ progname);
+ } else if (status!=-1 && status>>8) {
+ perror("zwgc initprog exec");
+ fprintf(stderr,"zwgc initprog of <%s> failed with status [%d].\n",
+ progname, status>>8);
+ }
+}
+
+/*
+ * main -- the program entry point. Does parsing & top level control.
+ */
+
+int main(argc, argv)
+ int argc;
+ char **argv;
+{
+ char **new;
+ register char **current;
+ int dofork = 1;
+#ifdef HAVE_ARES
+ char *errmem;
+ int status;
+#endif
+
+ progname = argv[0];
+
+ /*
+ * Process "-f <filename>", "-subfile <filename>", "-nofork",
+ * "-reenter" (which is ignored) and (if DEBUG) "-debug"
+ * arguments, removing then from argc, argv:
+ */
+ for (new=current=argv+1; *current; current++) {
+ if (string_Eq(*current, "-debug")) {
+ argc--;
+#ifdef DEBUG
+ zwgc_debug = 1;
+#endif
+ } else if (string_Eq(*current, "-f")) {
+ argc -= 2; current++;
+ if (!*current)
+ usage();
+ description_filename_override = *current;
+ } else if (string_Eq(*current, "-subfile")) {
+ argc -= 2; current++;
+ if (!*current)
+ usage();
+ subscriptions_filename_override = *current;
+ } else if (string_Eq(*current, "-nofork")) {
+ argc--;
+ dofork = 0;
+ } else if (string_Eq(*current, "-reenter")) {
+ argc--; /* just throw it away */
+ } else if (string_Eq(*current, "-loc")) {
+ argc -= 2; current++;
+ if (!*current)
+ usage();
+ location_override = *current;
+ } else
+ *(new)++ = *current;
+ }
+ *new = *current;
+
+#ifdef HAVE_ARES
+ /*
+ * Initialize resolver library
+ */
+ status = ares_init(&achannel);
+ if (status != ARES_SUCCESS) {
+ fprintf(stderr, "Couldn't initialize resolver: %s\n",
+ ares_strerror(status, &errmem));
+ ares_free_errmem(errmem);
+ return(1);
+ }
+#endif
+
+ /*
+ * Initialize various subsystems in proper order:
+ */
+ dprintf("Initializing subsystems...\n"); /*<<<>>>*/
+ mux_init();
+ var_clear_all_variables(); /* <<<>>> */
+ init_ports(); /* <<<>>> */
+ dprintf("Initializing standard ports...\n");
+ init_standard_ports(&argc, argv);
+ if (argc>1)
+ usage();
+ dprintf("Initializing zephyr...\n");
+ setup_signals(dofork);
+ zephyr_init(notice_handler);
+
+ if (dofork)
+ detach();
+ /*
+ * Run the initprogs program(s) now that we are all set to deal:
+ */
+ dprintf("Running initprogs program...\n");
+ run_initprogs();
+
+ dprintf("Test Zwgc parser.\n\n");
+ read_in_description_file();
+
+ dprintf("Entering main loop\n");
+ mux_loop();
+
+ dprintf("Returning from main loop\n");
+ finalize_zephyr();
+
+ return(0);
+}
+
+/****************************************************************************/
+/* */
+/* : */
+/* */
+/****************************************************************************/
+
+#define USER_SUPPRESS "SUPPRESS"
+#define USER_UNSUPPRESS "UNSUPPRESS"
+
+void notice_handler(notice)
+ ZNotice_t *notice;
+{
+ struct hostent *fromhost = NULL;
+
+ if (notice->z_sender_addr.s_addr) {
+#ifdef HAVE_ARES
+ ares_gethostbyaddr(achannel, &(notice->z_sender_addr),
+ sizeof(notice->z_sender_addr), AF_INET,
+ notice_callback, notice);
+ return;
+#else
+ fromhost = gethostbyaddr((char *) &(notice->z_sender_addr),
+ sizeof(struct in_addr), AF_INET);
+#endif
+ }
+ process_notice(notice, fromhost ? fromhost->h_name : NULL);
+ ZFreeNotice(notice);
+ free(notice);
+}
+
+#ifdef HAVE_ARES
+static void notice_callback(arg, status, fromhost)
+ void *arg;
+ int status;
+ struct hostent *fromhost;
+{
+ ZNotice_t *notice = (ZNotice_t *) arg;
+
+ process_notice(notice, fromhost ? fromhost->h_name : NULL);
+ ZFreeNotice(notice);
+ free(notice);
+}
+#endif
+
+static void process_notice(notice, hostname)
+ ZNotice_t *notice;
+ char *hostname;
+{
+ char *control_opcode;
+
+ dprintf("Got a message\n");
+
+ if (control_opcode = decode_notice(notice, hostname)) {
+#ifdef DEBUG
+ printf("got control opcode <%s>.\n", control_opcode);
+#endif
+ if (!strcasecmp(control_opcode, USER_REREAD)) {
+ read_in_description_file();
+ } else if (!strcasecmp(control_opcode, USER_SHUTDOWN))
+ zwgc_shutdown();
+ else if (!strcasecmp(control_opcode, USER_STARTUP)) {
+#ifdef DEBUG_MEMORY
+ report_memory_usage(); /* <<<>>> */
+#endif
+ zwgc_startup();
+ } else if (!strcasecmp(control_opcode, USER_SUPPRESS)) {
+ string class = get_field(notice->z_message,
+ notice->z_message_len, 1);
+ string instance = get_field(notice->z_message,
+ notice->z_message_len, 2);
+ string recipient = get_field(notice->z_message,
+ notice->z_message_len, 3);
+ punt(class, instance, recipient);
+ free(class);
+ free(instance);
+ free(recipient);
+ } else if (!strcasecmp(control_opcode, USER_UNSUPPRESS)) {
+ string class = get_field(notice->z_message,
+ notice->z_message_len, 1);
+ string instance = get_field(notice->z_message,
+ notice->z_message_len, 2);
+ string recipient = get_field(notice->z_message,
+ notice->z_message_len, 3);
+ unpunt(class, instance, recipient);
+ free(class);
+ free(instance);
+ free(recipient);
+ } else if (!strcasecmp(control_opcode, USER_EXIT)) {
+ signal_exit();
+ } else
+ printf("zwgc: unknown control opcode %s.\n", control_opcode);
+
+ return;
+ }
+
+ if (!zwgc_active) {
+#ifdef DEBUG
+ if (zwgc_debug)
+ printf("NON-ACTIVE: PUNTED <%s>!!!!\n", notice->z_class_inst);
+#endif
+ return;
+ }
+
+ if (puntable_address_p(notice->z_class,
+ notice->z_class_inst,
+ notice->z_recipient)) {
+#ifdef DEBUG
+ if (zwgc_debug)
+ printf("PUNTED <%s>!!!!\n", notice->z_class_inst);
+#endif
+ return;
+ }
+
+ exec_process_packet(program, notice);
+}
+
+/***************************************************************************/
+
+/*
+ *
+ */
+
+static void signal_exit()
+{
+ mux_end_loop_p = 1;
+}
+
+/* clean up ALL the waiting children, in case we get hit with
+ multiple SIGCHLD's at once, and don't process in time. */
+static RETSIGTYPE signal_child()
+{
+#ifdef HAVE_WAITPID
+ int status;
+#else
+ union wait status;
+#endif
+ extern int errno;
+ int pid, old_errno = errno;
+
+ do {
+#ifdef HAVE_WAITPID
+ pid = waitpid(-1, &status, WNOHANG);
+#else
+ pid = wait3(&status, WNOHANG, (struct rusage *)0);
+#endif
+ } while (pid != 0 && pid != -1);
+ errno = old_errno;
+}
+
+static void setup_signals(dofork)
+ int dofork;
+{
+#ifdef _POSIX_VERSION
+ struct sigaction sa;
+
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+
+ if (dofork) {
+ sa.sa_handler = SIG_IGN;
+ sigaction(SIGINT, &sa, (struct sigaction *)0);
+ sigaction(SIGTSTP, &sa, (struct sigaction *)0);
+ sigaction(SIGQUIT, &sa, (struct sigaction *)0);
+ sigaction(SIGTTOU, &sa, (struct sigaction *)0);
+ } else {
+ /* clean up on SIGINT; exiting on logout is the user's problem, now. */
+ sa.sa_handler = signal_exit;
+ sigaction(SIGINT, &sa, (struct sigaction *)0);
+ }
+
+ /* behavior never changes */
+ sa.sa_handler = signal_exit;
+ sigaction(SIGTERM, &sa, (struct sigaction *)0);
+ sigaction(SIGHUP, &sa, (struct sigaction *)0);
+
+ sa.sa_handler = SIG_IGN;
+ sigaction(SIGPIPE, &sa, (struct sigaction *)0);
+
+ sa.sa_handler = signal_child;
+ sigaction(SIGCHLD, &sa, (struct sigaction *)0);
+
+#else /* !POSIX */
+ if (dofork) {
+ /* Ignore keyboard signals if forking. Bad things will happen. */
+ signal(SIGINT, SIG_IGN);
+ signal(SIGTSTP, SIG_IGN);
+ signal(SIGTTOU, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ } else {
+ /* clean up on SIGINT; exiting on logout is the user's problem, now. */
+ signal(SIGINT, signal_exit);
+ }
+
+ /* behavior never changes */
+ signal(SIGTERM, signal_exit);
+ signal(SIGHUP, signal_exit);
+ signal(SIGCHLD, signal_child);
+ signal(SIGPIPE, SIG_IGN); /* so that Xlib gets an error */
+#endif
+}
+
+/* detach() taken from old zwgc, with lots of stuff ripped out */
+
+static void detach()
+{
+ /* detach from terminal and fork. */
+ register int i;
+
+ /* Attempt to join the process group of the session leader. This
+ * will get us a HUP if the session leader is in the foreground at
+ * logout time (which is often the case) or if the shell is running
+ * under telnetd or xterm (both of which HUP the process group of
+ * their child process). If we have getsid(), that's the best way
+ * of finding the session leader; otherwise use the process group of
+ * the parent process, which is a good guess. */
+#if defined(HAVE_GETSID)
+ setpgid(0, getsid(0));
+#elif defined(HAVE_GETPGID)
+ setpgid(0, getpgid(getppid()));
+#elif !defined(GETPGRP_VOID)
+ setpgid(0, getpgrp(getppid()));
+#endif
+
+ /* fork off and let parent exit... */
+ if (i = fork()) {
+ if (i < 0) {
+ perror("zwgc: cannot fork, aborting:");
+ exit(1);
+ }
+ exit(0);
+ }
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: main.h,v 1.9 1999/01/22 23:20:23 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+
+#include <zephyr/mit-copyright.h>
+
+#ifndef main_MODULE
+#define main_MODULE
+
+#ifdef HAVE_ARES
+#include <ares.h>
+
+extern ares_channel achannel;
+#endif
+
+extern char *progname;
+extern char *subscriptions_filename_override;
+extern char *location_override;
+
+/*
+ * void usage()
+ * Effects: Prints out a usage message on stderr then exits the
+ * program with error code 1.
+ */
+
+extern void usage();
+
+/* USRDESC points to a file (relative to user's homedir) which has a user's
+ description file */
+
+#define USRDESC ".zwgc.desc"
+
+/* DEFDESC points to a file (relative to the data directory) which has the
+ * system default description file */
+
+#ifndef DEFDESC
+#define DEFDESC "zwgc.desc"
+#endif
+
+#endif
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: mux.c,v 1.13 1999/01/22 23:20:23 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <sysdep.h>
+
+#if (!defined(lint) && !defined(SABER))
+static const char rcsid_mux_c[] = "$Id: mux.c,v 1.13 1999/01/22 23:20:23 ghudson Exp $";
+#endif
+
+/****************************************************************************/
+/* */
+/* Module containing code to wait on multiple file descriptors: */
+/* */
+/****************************************************************************/
+
+#include <zephyr/zephyr.h>
+#include "main.h"
+#include "mux.h"
+#include "error.h"
+#include "zwgc.h"
+#include "pointer.h"
+
+#ifdef _AIX
+#include <sys/select.h>
+#endif
+
+/*
+ * mux_end_loop_p - Setting this to true during a mux_loop causes the mux_loop
+ * to be exited.
+ */
+
+int mux_end_loop_p;
+
+/*
+ * have_tty - is defined to be true if there is a controlling tty for this
+ * process. When we can no longer access the controlling tty,
+ * the process will die.
+ */
+
+static int have_tty = 0;
+
+/*
+ * max_source - the maximum file descriptor that a handler was ever
+ * registered for:
+ */
+
+static int max_source = -1;
+
+/*
+ * Which file descriptors we're waiting on for input & the accompanying
+ * input handlers & their arguments:
+ */
+
+static fd_set input_sources;
+static void (*input_handler[MAX_SOURCES])();
+static pointer input_handler_arg[MAX_SOURCES];
+
+static int check_tty();
+
+/*
+ * void mux_init()
+ * Requires: mux_init has never been called before
+ * Effects: Initializes the mux module. Must be called before
+ * any other mux call.
+ */
+
+void mux_init()
+{
+ int i;
+
+ FD_ZERO(&input_sources);
+
+ for (i=0; i<MAX_SOURCES; i++)
+ input_handler[i] = NULL;
+
+ have_tty = check_tty();
+}
+
+/*
+ * void mux_add_input_source(int descriptor; void (*handler)(); pointer arg)
+ * Requires: 0<=descriptor<MAX_SOURCES, mux_init has been called
+ * Modifies: Removes the previous input handler if any for descriptor
+ * Effects: Registers handler as the input handler for file descriptor
+ * descriptor. When mux_loop() is running and input is
+ * available on descriptor, handler will be called with
+ * argument arg.
+ */
+
+void mux_add_input_source(descriptor, handler, arg)
+ int descriptor;
+ void (*handler)();
+ pointer arg;
+{
+#ifdef DEBUG
+ if(descriptor < 0 || descriptor >= MAX_SOURCES)
+ abort(); /* <<<>>> */
+#endif
+
+ input_handler[descriptor] = handler;
+ input_handler_arg[descriptor] = arg;
+ FD_SET(descriptor, &input_sources);
+ if(descriptor > max_source)
+ max_source = descriptor;
+}
+
+/*
+ * void mux_loop()
+ * Requires: mux_init has been called.
+ * Effects: Loops until mux_end_loop_p becomes true. (Sets
+ * mux_end_loop_p false to start). Whenever input is
+ * available on an input source which has a registered
+ * handler (see mux_add_input_source), that handler is
+ * called with its argument. It is guaranteed that if
+ * input is available on a source, its respective input
+ * handler, if any, will eventually be called. No other
+ * ordering guarantees are made. When some signal handler
+ * or input handler eventually sets mux_end_loop_p to
+ * true, we return.
+ */
+
+void mux_loop()
+{
+ int i, nfds;
+ fd_set inputs, outputs;
+ struct timeval tv, *tvp;
+
+ mux_end_loop_p = 0;
+
+ for (;;) {
+ /*
+ * Exit if mux_end_loop_p has been set to true by a handler:
+ */
+ if (mux_end_loop_p)
+ break;
+
+ if (have_tty) {
+ tv.tv_sec = 10;
+ tv.tv_usec = 0;
+ tvp = &tv;
+ } else {
+ tvp = NULL;
+ }
+
+ /*
+ * Do a select on all the file descriptors we care about to
+ * wait until at least one of them has input available:
+ */
+ inputs = input_sources;
+ FD_ZERO(&outputs);
+
+#ifdef HAVE_ARES
+ nfds = ares_fds(achannel, &inputs, &outputs);
+ if (nfds < max_source + 1)
+ nfds = max_source + 1;
+ tvp = ares_timeout(achannel, tvp, &tv);
+#else
+ nfds = max_source + 1;
+#endif
+
+ i = select(nfds, &inputs, &outputs, NULL, tvp);
+
+ if (i == -1) {
+ if (errno == EINTR)
+ continue; /* on a signal restart checking mux_loop_end_p */
+ else
+ FATAL_TRAP( errno, "while selecting" );
+ }
+ else if (i == 0) {
+ if (have_tty && !check_tty()) {
+ mux_end_loop_p = 1;
+ continue;
+ }
+ }
+
+#ifdef HAVE_ARES
+ ares_process(achannel, &inputs, &outputs);
+#endif
+
+ /*
+ * Call all input handlers whose corresponding file descriptors have
+ * input:
+ */
+ for(i=0; i<=max_source; i++)
+ if (FD_ISSET(i, &inputs) && input_handler[i]) {
+#ifdef DEBUG
+ if (zwgc_debug)
+ fprintf(stderr,
+ "mux_loop...activity on fd %d, calling %x(%x)\n",
+ i,input_handler[i],input_handler_arg[i]);
+#endif
+ input_handler[i](input_handler_arg[i]);
+ }
+ }
+}
+
+static int check_tty()
+{
+ register int result;
+ int pgrp;
+ int tty = open("/dev/tty", O_RDONLY|O_NDELAY);
+
+ if (tty < 0) return 0;
+
+#if defined(_POSIX_VERSION)
+ result = ( ((pgrp = tcgetpgrp(tty)) < 0) ? 0 : 1 );
+#else
+ result = ( (ioctl(tty, TIOCGPGRP, &pgrp) < 0) ? 0 : 1 );
+#endif
+
+ close(tty);
+ return(result);
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: mux.h,v 1.5 1999/01/22 23:20:24 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+
+#include <zephyr/mit-copyright.h>
+
+#ifndef mux_MODULE
+#define mux_MODULE
+
+/*
+ * MAX_SOURCES - the greatest file descriptor # that can be waited on minus one
+ * This can not exceed FD_SETSIZE from <sys/types.h>.
+ */
+
+#define MAX_SOURCES 32
+
+/*
+ * mux_end_loop_p - Setting this to true during a mux_loop causes the mux_loop
+ * to be exited.
+ */
+
+extern int mux_end_loop_p;
+
+/*
+ * void mux_init()
+ * Requires: mux_init has never been called before
+ * Effects: Initializes the mux module. Must be called before
+ * any other mux call.
+ */
+
+extern void mux_init();
+
+/*
+ * void mux_add_input_source(int descriptior; void (*handler)(); void *arg)
+ * Requires: 0<=descriptor<MAX_SOURCES, mux_init has been called
+ * Modifies: Removes the previous input handler if any for descriptor
+ * Effects: Registers handler as the input handler for file descriptor
+ * descriptor. When mux_loop() is running and input is
+ * available on descriptor, handler will be called with
+ * argument arg.
+ */
+
+extern void mux_add_input_source();
+
+/*
+ * void mux_loop()
+ * Requires: mux_init has been called.
+ * Effects: Loops until mux_end_loop_p becomes true. (Sets
+ * mux_end_loop_p false to start). Whenever input is
+ * available on an input source which has a registered
+ * handler (see mux_add_input_source), that handler is
+ * called with its argument. It is guarenteed that if
+ * input is available on a source, its respective input
+ * handler, if any, will eventually be called. No other
+ * ordering guarentees are made. When some signal handler
+ * or input handler eventually sets mux_end_loop_p to
+ * true, we return.
+ */
+
+extern void mux_loop();
+
+#endif
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: new_memory.c,v 1.2 1999/01/22 23:20:25 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <sysdep.h>
+
+#if (!defined(lint) && !defined(SABER))
+static const char rcsid_new_memory_c[] = "$Id: new_memory.c,v 1.2 1999/01/22 23:20:25 ghudson Exp $";
+#endif
+
+#if !defined(SABER) && (defined(DEBUG) || defined(MEMORY_DEBUG))
+
+/*
+ * memory - module wrapping debugging code around normal malloc/free/etc.
+ * routines.
+ *
+ * Overview:
+ *
+ * ...
+ */
+
+#define memory__PROVIDER
+#include "new_memory.h"
+
+/*
+ *
+ */
+#ifdef DEBUG
+#define assert(x) if (!(x)) abort()
+#else
+#define assert(x)
+#endif
+
+/*
+ *
+ */
+#ifdef DEBUG_MEMORY
+
+extern void record_request();
+char *current_module = 0;
+int current_line = -1;
+
+#endif
+
+/*
+ * string string_CreateFromData(char *data, int length):
+ * Requires: data[0], data[1], ..., data[length-1] != 0
+ * Effects: Takes the first length characters at data and
+ * creates a string containing them. The returned string
+ * is on the heap & must be freed eventually.
+ * I.e., if passed "foobar" and 3, it would return
+ * string_Copy("foo").
+ */
+
+char *memory__malloc(size)
+ unsigned size;
+{
+ char *result;
+
+ result = malloc(size + memory__size_of_header);
+ if (!result)
+ abort(); /* <<<>>> */
+
+#ifdef DEBUG_MEMORY
+ ((memory_block_header *)result)->size = size;
+ ((memory_block_header *)result)->creating_module = current_module;
+ ((memory_block_header *)result)->line_number_in_creating_module =
+ current_line;
+ ((memory_block_header *)result)->check_field = CHECK_FIELD_VALUE;
+ result += memory__size_of_header;
+
+ record_request(current_module, current_line, 1, size);
+#endif
+
+ return(result);
+}
+
+char *memory__realloc(ptr, size)
+ char *ptr;
+ unsigned size;
+{
+ char *result;
+
+ assert(ptr);
+
+#ifdef DEBUG_MEMORY
+ if (!memory__on_heap_p(ptr)) {
+ printf("realloced non-memory block in %s on line %d!\n",
+ current_module, current_line);
+ fflush(stdout);
+ return(realloc(ptr, size));
+ }
+#endif
+
+ result = realloc(ptr-memory__size_of_header, size+memory__size_of_header);
+ if (!result)
+ abort(); /* <<<>>> */
+
+ return(result+memory__size_of_header);
+}
+
+char *memory__calloc(nelem, elsize)
+ unsigned nelem;
+ unsigned elsize;
+{
+ char *result;
+
+#ifdef DEBUG_MEMORY
+ printf("in calloc\n"); fflush(stdout);
+#endif
+
+ abort();
+
+#ifdef FRED
+ result = calloc(nelem, elsize);
+ if (!result)
+ abort();
+
+ record_request(1);
+#endif
+
+ return(result);
+}
+
+void memory__free(ptr)
+ char *ptr;
+{
+ assert(ptr);
+
+#ifdef DEBUG_MEMORY
+ if (!memory__on_heap_p(ptr)) {
+ printf("freed non-memory block in %s on line %d!\n", current_module,
+ current_line);
+ fflush(stdout);
+ (void)free(ptr);
+ return;
+ }
+
+ record_request(memory__get_header(ptr)->creating_module,
+ memory__get_header(ptr)->line_number_in_creating_module,
+ -1,
+ memory__get_header(ptr)->size);
+#endif
+
+ (void)free(ptr-memory__size_of_header);
+}
+
+#ifdef DEBUG_MEMORY
+
+#include "int_dictionary.h"
+
+static int request_off = 0;
+static int_dictionary requests = 0;
+static int outstanding_requests = 0;
+static int outstanding_memory = 0;
+
+void record_request(module, line_number, dir, size)
+ char *module;
+ int line_number;
+ int dir;
+ unsigned int size;
+{
+ int_dictionary_binding *binding;
+ int already_exists;
+#ifdef LINE
+ char buffer[20];
+#endif
+
+ if (request_off)
+ return;
+ request_off = 1;
+
+ if (!requests)
+ requests = int_dictionary_Create(101);
+
+#ifdef LINE
+ module = string_Concat(module, ":");
+ sprintf(buffer, "%d", line_number);
+ module = string_Concat2(module, buffer);
+#endif
+
+ binding = int_dictionary_Define(requests, module, &already_exists);
+ if (!already_exists)
+ binding->value = 0;
+
+#ifdef LINE
+ free(module);
+#endif
+
+ binding->value += dir;
+ outstanding_requests += dir;
+ outstanding_memory += size*dir;
+
+ request_off = 0;
+}
+
+void proc(binding)
+ int_dictionary_binding *binding;
+{
+ if (binding->value)
+ printf(" %-30s %6d blocks allocated\n", binding->key, binding->value);
+}
+
+void report_memory_usage()
+{
+ printf("\n# of blocks on the heap = %d\n", outstanding_requests);
+ printf("Total heap space in use: %d bytes\n", outstanding_memory);
+
+ printf("\nHeap Allocations by module:\n");
+ int_dictionary_Enumerate(requests, proc);
+ printf("\n");
+
+ fflush(stdout);
+}
+
+void set_module(file, line)
+ char *file;
+ int line;
+{
+ if (request_off)
+ return;
+
+ if (!strcmp(file, "new_string.c"))
+ return;
+ if (!strcmp(file, "string_dictionary_aux.c"))
+ return;
+
+ current_line = line;
+ current_module = file;
+}
+
+#endif
+
+#endif /* SABER */
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: new_memory.h,v 1.2 1999/01/22 23:20:25 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+/* This entire module goes out the window in saber */
+#if !defined(SABER) && (defined(DEBUG) || defined(DEBUG_MEMORY))
+
+#ifndef memory_MODULE
+#define memory_MODULE
+
+extern char *memory__malloc(); /* PRIVATE */
+extern char *memory__realloc(); /* PRIVATE */
+extern char *memory__calloc(); /* PRIVATE */
+extern void memory__free(); /* PRIVATE */
+
+#ifdef DEBUG_MEMORY
+
+#define CHECK_FIELD_VALUE 0xe5e7e3e9
+
+typedef struct _memory_block_header {
+ unsigned size;
+ char *creating_module;
+ int line_number_in_creating_module;
+ unsigned int check_field;
+} memory_block_header;
+
+#define memory__size_of_header (sizeof(struct _memory_block_header))
+
+#define memory__get_header(block) \
+ ((struct _memory_block_header *)((block)-memory__size_of_header))
+
+#define memory__on_heap_p(block) \
+ (memory__get_header(block)->check_field==CHECK_FIELD_VALUE)
+
+#else
+
+#define memory__size_of_header 0
+
+#define memory__on_heap_p(block) 1
+
+#endif
+
+/*
+ * int string_Length(string s):
+ * Effects: Returns the number of non-null characters in s.
+ */
+
+#ifndef memory__PROVIDER
+#ifdef DEBUG_MEMORY
+
+extern char *current_module;
+extern void set_module();
+
+#define malloc(size) (set_module(__FILE__,__LINE__),\
+ memory__malloc(size))
+#define realloc(ptr, size) (set_module(__FILE__,__LINE__),\
+ memory__realloc((char *) ptr, size))
+#define calloc(nelem, elsize) (set_module(__FILE__,__LINE__),\
+ memory__calloc(nelem, elsize))
+#define free(ptr) (set_module(__FILE__,__LINE__),\
+ memory__free((char *) ptr))
+#else
+
+#define malloc(size) memory__malloc(size)
+#define realloc(ptr, size) memory__realloc((char *) ptr, size)
+#define calloc(nelem, elsize) memory__calloc(nelem, elsize)
+#define free(ptr) memory__free((char *) ptr)
+
+#endif /* DEBUG_MEMORY */
+
+#endif /* memory__PROVIDER */
+
+#endif /* memory_MODULE */
+
+#endif /* SABER */
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: new_string.c,v 1.2 1999/01/22 23:20:26 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <sysdep.h>
+
+#if (!defined(lint) && !defined(SABER))
+static const char rcsid_new_string_c[] = "$Id: new_string.c,v 1.2 1999/01/22 23:20:26 ghudson Exp $";
+#endif
+
+/*
+ * string - a module providing operations on C strings. (i.e., char *'s)
+ *
+ * Overview:
+ *
+ * A string is a standard C string. I.e., a char pointer to a
+ * null-terminated sequence of characters. 0 is NOT considered a valid
+ * string! Various operations are available. See the string_spec file
+ * for details.
+ *
+ * Note: This module assumes that malloc NEVER returns 0 for reasonable
+ * requests. It is the users responsibility to either ensure that
+ * this happens or supply a version of malloc with error
+ * handling.
+ *
+ * Some strings are mutable.
+ */
+
+#ifdef DEBUG
+#define assert(x) if (!(x)) abort()
+#else
+#define assert(x)
+#endif
+
+#include "new_memory.h"
+
+#define string_Length(s) strlen(s)
+typedef char *string;
+
+/*
+ * string string_CreateFromData(char *data, int length):
+ * Requires: data[0], data[1], ..., data[length-1] != 0
+ * Effects: Takes the first length characters at data and
+ * creates a string containing them. The returned string
+ * is on the heap & must be freed eventually.
+ * I.e., if passed "foobar" and 3, it would return
+ * string_Copy("foo").
+ */
+
+string string__CreateFromData(data, length)
+ char *data;
+ int length;
+{
+ string result;
+
+ assert(length>=0);
+
+ result = (string)malloc(length+1);
+ assert(result);
+
+ (void) memcpy(result, data, length);
+ result[length] = 0;
+
+ return(result);
+}
+
+/*
+ * string string_Copy(string s):
+ * Effects: Returns a copy of s on the heap. The copy must be
+ * freed eventually.
+ */
+
+string string__Copy(s)
+ string s;
+{
+ int length;
+ string result;
+
+ assert(s);
+
+ length = string_Length(s)+1;
+ result = (string)malloc(length);
+ assert(result);
+
+ (void) memcpy(result, s, length);
+ return(result);
+}
+
+/*
+ * string string_Concat(string a, b):
+ * Effects: Returns a string equal to a concatenated to b.
+ * The returned string is on the heap and must be
+ * freed eventually. I.e., given "abc" and "def",
+ * returns string_Copy("abcdef").
+ */
+
+string string__Concat(a, b)
+ string a, b;
+{
+ string result;
+ int a_length, b_size, result_size;
+
+ a_length = string_Length(a);
+ b_size = string_Length(b)+1;
+ result_size = a_length+b_size;
+ result = (string)malloc(result_size);
+ assert(result);
+
+ (void) memcpy(result, a, a_length);
+ (void) memcpy(result+a_length, b, b_size);
+
+ return(result);
+}
+
+/*
+ * string string_Concat2(string a, b):
+ * Modifies: a
+ * Requires: a is on the heap, b does not point into a.
+ * Effects: Equivalent to:
+ * string temp;
+ * temp = string_Concat(a,b);
+ * free(a);
+ * return(temp);
+ * only faster. I.e., uses realloc instead of malloc+memcpy.
+ */
+
+string string__Concat2(a, b)
+ string a, b;
+{
+ int a_length = string_Length(a);
+ int b_size = string_Length(b)+1;
+
+#ifdef DEBUG_MEMORY
+ assert(memory__on_heap_p(a));
+#endif
+
+ a = (string)realloc(a, a_length+b_size);
+ assert(a);
+ (void) memcpy(a+a_length, b, b_size);
+
+ return(a);
+}
+
+/*
+ * string string_Downcase(string s):
+ * Modifies: s
+ * Effects: Modifies s by changing every uppercase character in s
+ * to the corresponding lowercase character. Nothing else
+ * is changed. I.e., "FoObAr19." is changed to "foobar19.".
+ * S is returned as a convenience.
+ */
+
+string string_Downcase(s)
+ string s;
+{
+ char *ptr;
+
+ for (ptr=s; *ptr; ptr++) {
+ if (isupper(*ptr))
+ *ptr = tolower(*ptr);
+ }
+
+ return(s);
+}
+
+/*
+ * string string_Upcase(string s):
+ * Modifies: s
+ * Effects: Modifies s by changing every lowercase character in s
+ * to the corresponding uppercase character. Nothing else
+ * is changed. I.e., "FoObAr19." is changed to "FOOBAR19.".
+ * S is returned as a convenience.
+ */
+
+string string_Upcase(s)
+ string s;
+{
+ char *ptr;
+
+ for (ptr=s; *ptr; ptr++) {
+ if (islower(*ptr))
+ *ptr = toupper(*ptr);
+ }
+
+ return(s);
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: new_string.h,v 1.2 1999/01/22 23:20:26 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#ifndef string_TYPE
+#define string_TYPE
+
+#include <string.h>
+#include "new_memory.h"
+
+typedef char *string;
+
+/*
+ * int string_Length(string s):
+ * Effects: Returns the number of non-null characters in s.
+ */
+
+#define string_Length(s) strlen(s)
+
+/*
+ * int string_Eq(string a, b):
+ * Effects: Returns true iff strings a & b are equal. I.e., have the
+ * same character contents.
+ */
+
+#define string_Eq(a,b) (!strcmp(a,b))
+
+/*
+ * int string_Neq(string a, b):
+ * Effects: Returns true iff strings a & b are not equal.
+ */
+
+#define string_Neq(a,b) (strcmp(a,b))
+
+/*
+ * string string_CreateFromData(char *data, int length):
+ * Requires: data[0], data[1], ..., data[length-1] != 0
+ * Effects: Takes the first length characters at data and
+ * creates a string containing them. The returned string
+ * is on the heap & must be freed eventually.
+ * I.e., if passed "foobar" and 3, it would return
+ * string_Copy("foo").
+ */
+
+extern string string__CreateFromData();
+#ifdef DEBUG_MEMORY
+#define string_CreateFromData(data,length) (set_module(__FILE__,__LINE__),\
+ string__CreateFromData(data,length))
+#else
+#define string_CreateFromData(data,length) string__CreateFromData(data,length)
+#endif
+
+/*
+ * string string_Copy(string s):
+ * Effects: Returns a copy of s on the heap. The copy must be
+ * freed eventually.
+ */
+
+extern string string__Copy(/* string s */);
+#ifdef DEBUG_MEMORY
+#define string_Copy(data) (set_module(__FILE__,__LINE__),\
+ string__Copy(data))
+#else
+#define string_Copy(data) string__Copy(data)
+#endif
+
+/*
+ * string string_Concat(string a, b):
+ * Effects: Returns a string equal to a concatenated to b.
+ * The returned string is on the heap and must be
+ * freed eventually. I.e., given "abc" and "def",
+ * returns string_Copy("abcdef").
+ */
+
+extern string string__Concat(/* string a, b */);
+#ifdef DEBUG_MEMORY
+#define string_Concat(a,b) (set_module(__FILE__,__LINE__),\
+ string__Concat(a,b))
+#else
+#define string_Concat(a,b) string__Concat(a,b)
+#endif
+
+/*
+ * string string_Concat2(string a, b):
+ * Modifies: a
+ * Requires: a is on the heap, b does not point into a.
+ * Effects: Equivalent to:
+ * string temp;
+ * temp = string_Concat(a,b);
+ * free(a);
+ * return(temp);
+ * only faster. I.e., uses realloc instead of malloc+bcopy.
+ */
+
+extern string string__Concat2(/* string a, b */);
+#ifdef DEBUG_MEMORY
+#define string_Concat2(a,b) (set_module(__FILE__,__LINE__),\
+ string__Concat2(a,b))
+#else
+#define string_Concat2(a,b) string__Concat2(a,b)
+#endif
+
+/*
+ * string string_Downcase(string s):
+ * Modifies: s
+ * Effects: Modifies s by changing every uppercase character in s
+ * to the corresponding lowercase character. Nothing else
+ * is changed. I.e., "FoObAr19." is changed to "foobar19.".
+ * S is returned as a convenience.
+ */
+
+extern string string_Downcase();
+
+/*
+ * string string_Upcase(string s):
+ * Modifies: s
+ * Effects: Modifies s by changing every lowercase character in s
+ * to the corresponding uppercase character. Nothing else
+ * is changed. I.e., "FoObAr19." is changed to "FOOBAR19.".
+ * S is returned as a convenience.
+ */
+
+extern string string_Upcase();
+
+#endif
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: node.c,v 1.5 1999/01/22 23:20:27 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <sysdep.h>
+
+#if (!defined(lint) && !defined(SABER))
+static const char rcsid_node_c[] = "$Id: node.c,v 1.5 1999/01/22 23:20:27 ghudson Exp $";
+#endif
+
+#include <zephyr/mit-copyright.h>
+
+#include "new_memory.h"
+#include "node.h"
+
+/****************************************************************************/
+/* */
+/* Internal node construction & destruction functions: */
+/* */
+/****************************************************************************/
+
+/*
+ * NODE_BATCH_SIZE - the number of nodes to malloc at once to save overhead:
+ */
+
+#define NODE_BATCH_SIZE 100
+
+/*
+ * The nodes we have malloced are kept in a linked list of bunches of
+ * NODE_BATCH_SIZE nodes. Nodes points to the first bunch on the list
+ * and current_bunch to the last. All nodes from the first one in the first
+ * bunch to the last_node_in_current_bunch_used'th one in the last bunch
+ * are in use. The others have not been used yet.
+ */
+
+static struct _bunch_of_nodes {
+ struct _bunch_of_nodes *next_bunch;
+ Node nodes[NODE_BATCH_SIZE];
+} *nodes = NULL;
+static struct _bunch_of_nodes *current_bunch = NULL;
+static int last_node_in_current_bunch_used = -1;
+
+/*
+ * Internal Routine:
+ *
+ * Node *node_create(int opcode)
+ * Effects: Creates a node with opcode opcode and returns a pointer
+ * to it. The next pointer of the returned node is NULL.
+ * If the opcode is STRING_CONSTANT_OPCODE the caller must
+ * ensure that the string_constant field points to a valid
+ * string on the heap when node_DestroyAllNodes is called.
+ */
+
+static Node *node_create(opcode)
+ int opcode;
+{
+ Node *result;
+
+ if (!nodes) {
+ /*
+ * Handle special case where no nodes allocated yet:
+ */
+ current_bunch = nodes = (struct _bunch_of_nodes *)
+ malloc(sizeof(struct _bunch_of_nodes));
+ nodes->next_bunch = NULL;
+ last_node_in_current_bunch_used = -1;
+ }
+
+ /*
+ * If all nodes allocated so far in use, allocate another
+ * bunch of NODE_BATCH_SIZE nodes:
+ */
+ if (last_node_in_current_bunch_used == NODE_BATCH_SIZE-1) {
+ current_bunch->next_bunch = (struct _bunch_of_nodes *)
+ malloc(sizeof(struct _bunch_of_nodes));
+ current_bunch = current_bunch->next_bunch;
+ current_bunch->next_bunch = NULL;
+ last_node_in_current_bunch_used = -1;
+ }
+
+ /*
+ * Get next not already used node & ready it for use:
+ */
+ last_node_in_current_bunch_used++;
+ result = &(current_bunch->nodes[last_node_in_current_bunch_used]);
+ result->opcode = opcode;
+ result->next = NULL;
+
+ return(result);
+}
+
+/*
+ *
+ */
+
+void node_DestroyAllNodes()
+{
+ struct _bunch_of_nodes *next_bunch;
+ int i, last_node_used_in_this_bunch;
+
+ while (nodes) {
+ next_bunch = nodes->next_bunch;
+ last_node_used_in_this_bunch = next_bunch ?
+ NODE_BATCH_SIZE-1 : last_node_in_current_bunch_used;
+ for (i=0; i<=last_node_used_in_this_bunch; i++) {
+ if (nodes->nodes[i].opcode==STRING_CONSTANT_OPCODE)
+ free(nodes->nodes[i].d.string_constant);
+ else if (nodes->nodes[i].opcode==VARREF_OPCODE)
+ free(nodes->nodes[i].d.string_constant);
+ else if (nodes->nodes[i].opcode==VARNAME_OPCODE)
+ free(nodes->nodes[i].d.string_constant);
+ }
+ free(nodes);
+ nodes = next_bunch;
+ }
+
+ current_bunch = nodes;
+}
+
+/****************************************************************************/
+/* */
+/* Node construction functions: */
+/* */
+/****************************************************************************/
+
+Node *node_create_string_constant(opcode, text)
+ int opcode;
+ string text;
+{
+ Node *n;
+
+ n = node_create(opcode);
+ n->d.string_constant = text;
+ return(n);
+}
+
+Node *node_create_noary(opcode)
+ int opcode;
+{
+ Node *n;
+
+ n = node_create(opcode);
+ return(n);
+}
+
+Node *node_create_unary(opcode, arg)
+ int opcode;
+ Node *arg;
+{
+ Node *n;
+
+ n = node_create(opcode);
+ n->d.nodes.first = arg;
+ return(n);
+}
+
+Node *node_create_binary(opcode, first_arg, second_arg)
+ int opcode;
+ Node *first_arg;
+ Node *second_arg;
+{
+ Node *n;
+
+ n = node_create(opcode);
+ n->d.nodes.first = first_arg;
+ n->d.nodes.second = second_arg;
+ return(n);
+}
+
+/****************************************************************************/
+/* */
+/* Node utility functions: */
+/* */
+/****************************************************************************/
+
+/*
+ * Node *reverse_list_of_nodes(Node *list)
+ * Modifies: the nodes on the linked list list
+ * Effects: Reverses the linked list list and returns it.
+ * This is done by modifing the next pointers of the
+ * list elements to point to the previous node & returning
+ * the address of the (previously) last node.
+ */
+
+Node *reverse_list_of_nodes(list)
+ Node *list;
+{
+ Node *next_node;
+ Node *head = NULL;
+
+ while (list) {
+ next_node = list->next;
+
+ /*
+ * Add the node list to the beginning of linked list head:
+ */
+ list->next = head;
+ head = list;
+
+ list = next_node;
+ }
+
+ return(head);
+}
+
+/****************************************************************************/
+/* */
+/* Node display functions: */
+/* */
+/****************************************************************************/
+
+#ifdef DEBUG
+
+static void print_stuff(node, format_string)
+ Node *node;
+ string format_string;
+{
+ char c;
+
+ for (c=(*(format_string++)); c; c=(*(format_string++))) {
+ if (c!='%') {
+ putchar(c);
+ continue;
+ }
+ c=(*(format_string++));
+ if (!c) {
+ format_string--;
+ continue;
+ }
+ if (c=='s')
+ printf("%s", node->d.string_constant);
+ else if (c=='1')
+ node_display(node->d.nodes.first);
+ else if (c=='2')
+ node_display(node->d.nodes.second);
+ else
+ putchar(c);
+ }
+}
+
+static string how_to_print[] = {
+ "\"%s\"", /* constant string */
+ "$%s", /* varref */
+ "%s", /* varname */
+
+ "!%1",
+
+ "( %1 + %2 )",
+ "( %1 and %2 )",
+ "( %1 or %2 )",
+ "( %1 == %2 )",
+ "( %1 != %2 )",
+ "( %1 =~ %2 )",
+ "( %1 !~ %2 )",
+
+ "buffer()",
+
+ "substitute(%1)",
+ "protect(%1)",
+ "verbatim(%1)",
+ "stylestrip(%1)",
+ "getenv(%1)",
+ "upcase(%1)",
+ "downcase(%1)",
+ "zvar(%1)",
+ "get(%1)",
+
+ "lany(%1, %2)",
+ "rany(%1, %2)",
+ "lbreak(%1, %2)",
+ "rbreak(%1, %2)",
+ "lspan(%1, %2)",
+ "rspan(%1, %2)",
+
+ "noop\n",
+ "set %1 = %2\n",
+ "fields %1\n",
+
+ "print %1\n",
+ "clearbuf\n",
+
+ "appendport %1 %2\n",
+ "execport %1 %2\n",
+ "inputport %1 %2\n",
+ "outputport %1 %2\n",
+ "put %1 %2\n",
+ "closeinput %1\n",
+ "closeoutput %1\n",
+ "closeport %1\n",
+
+ "exec %1 %2\n",
+
+ "%1endif\n",
+ "case %1\n%2endcase\n",
+ "while %1 do\n%2endwhile\n",
+ "break\n",
+ "exit\n",
+
+ "if %1 then\n%2",
+ "elseif %1 then\n%2",
+ "else\n%2",
+ "match %1\n%2",
+ "default\n%2" };
+
+void node_display(node)
+ Node *node;
+{
+ int opcode = LAST_EXPR_OPCODE + 1;
+
+ for (; node; node=node->next) {
+ if (opcode<=LAST_EXPR_OPCODE)
+ printf(" ");
+
+ opcode = node->opcode;
+ if (opcode>=0 && opcode<NUMBER_OF_OPCODES)
+ print_stuff(node, how_to_print[opcode]);
+ else
+ printf("[opcode %d]", opcode);
+ }
+}
+
+#endif
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: node.h,v 1.5 1999/01/22 23:20:27 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+
+#include <zephyr/mit-copyright.h>
+
+#ifndef node_MODULE
+#define node_MODULE
+
+#include "new_string.h"
+
+#define STRING_CONSTANT_OPCODE 0
+#define VARREF_OPCODE 1
+#define VARNAME_OPCODE 2
+
+#define NOT_OPCODE 3
+
+#define PLUS_OPCODE 4
+#define AND_OPCODE 5
+#define OR_OPCODE 6
+#define EQ_OPCODE 7
+#define NEQ_OPCODE 8
+#define REGEQ_OPCODE 9
+#define REGNEQ_OPCODE 10
+
+#define BUFFER_OPCODE 11
+
+#define SUBSTITUTE_OPCODE 12
+#define PROTECT_OPCODE 13
+#define VERBATIM_OPCODE 14
+#define STYLESTRIP_OPCODE 15
+#define GETENV_OPCODE 16
+#define UPCASE_OPCODE 17
+#define DOWNCASE_OPCODE 18
+#define ZVAR_OPCODE 19
+#define GET_OPCODE 20
+
+#define LANY_OPCODE 21
+#define RANY_OPCODE 22
+#define LBREAK_OPCODE 23
+#define RBREAK_OPCODE 24
+#define LSPAN_OPCODE 25
+#define RSPAN_OPCODE 26
+
+#define LAST_EXPR_OPCODE 26
+
+#define NOOP_OPCODE 27
+#define SET_OPCODE 28
+#define FIELDS_OPCODE 29
+
+#define PRINT_OPCODE 30
+#define CLEARBUF_OPCODE 31
+
+#define APPENDPORT_OPCODE 32
+#define EXECPORT_OPCODE 33
+#define INPUTPORT_OPCODE 34
+#define OUTPUTPORT_OPCODE 35
+#define PUT_OPCODE 36
+#define CLOSEINPUT_OPCODE 37
+#define CLOSEOUTPUT_OPCODE 38
+#define CLOSEPORT_OPCODE 39
+
+#define EXEC_OPCODE 40
+
+#define IF_STMT_OPCODE 41
+#define CASE_OPCODE 42
+#define WHILE_OPCODE 43
+#define BREAK_OPCODE 44
+#define EXIT_OPCODE 45
+
+#define IF_OPCODE 46
+#define ELSEIF_OPCODE 47
+#define ELSE_OPCODE 48
+#define MATCHLIST_OPCODE 49
+#define DEFAULT_OPCODE 50
+
+#define NUMBER_OF_OPCODES 51
+
+typedef struct _Node {
+ int opcode; /* Read-only */
+ struct _Node *next;
+ union {
+ string string_constant;
+ struct {
+ struct _Node *first;
+ struct _Node *second;
+ } nodes;
+ } d;
+} Node;
+
+/* Function externs */
+
+extern void node_DestroyAllNodes();
+
+extern Node *node_create_string_constant();
+
+extern Node *node_create_noary();
+extern Node *node_create_unary();
+extern Node *node_create_binary();
+
+/*
+ * Node *reverse_list_of_nodes(Node *list)
+ * Modifies: the nodes on the linked list list
+ * Effects: Reverses the linked list list and returns it.
+ * This is done by modifing the next pointers of the
+ * list elements to point to the previous node & returning
+ * the address of the (previously) last node.
+ */
+
+extern Node *reverse_list_of_nodes();
+
+#ifdef DEBUG
+extern void node_display();
+#endif
+
+#endif
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: notice.c,v 1.12 1999/01/22 23:20:28 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <sysdep.h>
+
+#if (!defined(lint) && !defined(SABER))
+static const char rcsid_notice_c[] = "$Id: notice.c,v 1.12 1999/01/22 23:20:28 ghudson Exp $";
+#endif
+
+#include <zephyr/mit-copyright.h>
+
+/****************************************************************************/
+/* */
+/* Module containing code to extract a notice's fields: */
+/* */
+/****************************************************************************/
+
+#include <zephyr/zephyr.h>
+#include <arpa/inet.h>
+#include "new_memory.h"
+#include "error.h"
+#include "variables.h"
+#include "notice.h"
+
+/*
+ * int count_nulls(char *data, int length)
+ * Requires: length>=0
+ * Effects: Returns the # of nulls in data[0]..data[length-1]
+ */
+
+int count_nulls(data, length)
+ char *data;
+ int length;
+{
+ int count = 0;
+
+ for (; length; data++, length--)
+ if (!*data)
+ count++;
+
+ return(count);
+}
+
+/*
+ * string get_next_field(char **data_p, int *length_p)
+ * Requires: *length_p >= 0
+ * Modifies: *data_p, *length_p
+ * Effects: Treats (*data_p)[0], (*data_p)[1], ... (*data_p)[length-1]
+ * as a series of null-seperated fields. This function
+ * returns a copy of the first field on the heap. This
+ * string must eventually be freed. Also, *data_p is
+ * advanced and *length_p decreased so that another
+ * call to this procedure with the same arguments will
+ * return the second field. The next call will return
+ * the third field, etc. "" is returned if 0 fields
+ * remain. (this is the case when *length_p == 0)
+ */
+
+string get_next_field(data_p, length_p)
+ char **data_p;
+ int *length_p;
+{
+ char *data = *data_p;
+ int length = *length_p;
+ char *ptr;
+
+ for (ptr=data; length; ptr++, length--)
+ if (!*ptr) {
+ *data_p = ptr+1;
+ *length_p = length-1;
+ return(string_Copy(data));
+ }
+
+ length = *length_p;
+ *data_p = ptr;
+ *length_p = 0;
+ return(string_CreateFromData(data, length));
+}
+
+/*
+ * string get_field(char *data, int length, int num)
+ * Requires: length>=0, num>0
+ * Effects: Treats data[0]..data[length-1] as a series of
+ * null-seperated fields. This function returns a copy of
+ * the num'th field (numbered from 1 in this case) on the
+ * heap. This string must eventually be freed. If there
+ * is no num'th field (because num<1 or num># of fields),
+ * "" is returned.
+ */
+
+string get_field(data, length, num)
+ char *data;
+ int length;
+ int num;
+{
+ /*
+ * While num>1 and there are fields left, skip a field & decrement num:
+ */
+ while (length && num>1) {
+ if (!*data)
+ num--;
+ length--;
+ data++;
+ }
+
+ /*
+ * If any more fields left, the first field is the one we want.
+ * Otherwise, there is no such field as num -- return "".
+ */
+ if (length)
+ return(get_next_field(&data, &length));
+ else
+ return(string_Copy(""));
+}
+
+/*
+ * string convert_nulls_to_newlines(data, length)
+ * Requires: length>=0, malloc never returns NULL
+ * Effects: Takes data[0]..data[length-1], converts all nulls to
+ * newlines ('\n') and returns the result as a null-terminated
+ * string on the heap. The returned string must eventually
+ * be freed.
+ */
+
+string convert_nulls_to_newlines(data, length)
+ char *data;
+ int length;
+{
+ char *result, *ptr;
+ char c;
+
+ result = (char *) malloc(length+1);
+ result[length] = '\0';
+
+ for (ptr=result; length; data++, ptr++, length--)
+ *ptr = (c = *data) ? c : '\n';
+
+ return(result);
+}
+
+
+/*
+ * Internal Routine:
+ *
+ * string z_kind_to_ascii(ZNotice_Kind_t z_kind)
+ * Effects: Returns an ascii representation for z_kind.
+ * The string returned is on the heap and must be freed
+ * eventually.
+ */
+
+static string z_kind_to_ascii(z_kind)
+ ZNotice_Kind_t z_kind;
+{
+ string result;
+
+ switch (z_kind) {
+ case UNSAFE:
+ result = "unsafe";
+ break;
+
+ case UNACKED:
+ result = "unacked";
+ break;
+
+ case ACKED:
+ result = "acked";
+ break;
+
+ case HMACK:
+ result = "hmack";
+ break;
+
+ case HMCTL:
+ result = "hmctl";
+ break;
+
+ case SERVACK:
+ result = "servack";
+ break;
+
+ case SERVNAK:
+ result = "servnak";
+ break;
+
+ case CLIENTACK:
+ result = "clientack";
+ break;
+
+ case STAT:
+ result = "stat";
+ break;
+
+ default:
+ result = "<unknown kind>";
+ break;
+ }
+
+ return(string_Copy(result));
+}
+
+/*
+ * Internal Routine:
+ *
+ * string z_auth_to_ascii(int z_auth)
+ * Effects: Returns an ascii representation for z_auth.
+ * The string returned is on the heap and must be freed
+ * eventually.
+ */
+
+static string z_auth_to_ascii(z_auth)
+ int z_auth;
+{
+ string result;
+
+ switch (z_auth) {
+ case ZAUTH_FAILED:
+ result = "forged";
+ break;
+
+ case ZAUTH_NO:
+ result = "no";
+ break;
+
+ case ZAUTH_YES:
+ result = "yes";
+ break;
+
+ default:
+ result = "unknown";
+ break;
+ }
+
+ return(string_Copy(result));
+}
+
+/*
+ * char *decode_notice(ZNotice_t *notice)
+ * Modifies: various description language variables
+ * Effects:
+ */
+
+char *decode_notice(notice, hostname)
+ ZNotice_t *notice;
+ char *hostname;
+{
+ char *temp;
+ string time, notyear, year, date_string, time_string;
+
+ /*
+ * Convert useful notice fields to ascii and store away in
+ * description language variables for later use by the
+ * the user's program:
+ */
+ var_set_variable("zephyr_version", notice->z_version);
+ var_set_variable("class", notice->z_class);
+ var_set_variable("instance", notice->z_class_inst);
+ var_set_variable("opcode", notice->z_opcode);
+ var_set_variable("default", notice->z_default_format);
+ var_set_variable("recipient",
+ (notice->z_recipient[0] ? notice->z_recipient : "*"));
+ var_set_variable("fullsender", notice->z_sender);
+ var_set_variable_to_number("port", (int)notice->z_port);
+ var_set_variable_then_free_value("kind", z_kind_to_ascii(notice->z_kind));
+ var_set_variable_then_free_value("auth", z_auth_to_ascii(notice->z_auth));
+
+ /*
+ * Set $sender to the name of the notice sender except first strip off the
+ * realm name if it is the local realm:
+ */
+ if ( (temp=strchr(notice->z_sender,'@')) && string_Eq(temp+1, ZGetRealm()) )
+ var_set_variable_then_free_value("sender",
+ string_CreateFromData(notice->z_sender,
+ temp-notice->z_sender));
+ else
+ var_set_variable("sender", notice->z_sender);
+
+ /*
+ * Convert time & date notice was sent to ascii. The $time
+ * has the format "01:03:52" while $date has the format
+ * "Sun Sep 16 1973".
+ */
+ {
+ /* the fields of struct timeval might not be the right type to pass
+ to ctime, so use a temporary */
+ time_t sec = notice->z_time.tv_sec;
+ time = ctime(&sec);
+ }
+ time_string = string_CreateFromData(time+11,8);
+ var_set_variable_then_free_value("time", time_string);
+ date_string = string_Concat(notyear=string_CreateFromData(time,11),
+ year=string_CreateFromData(time+20,4));
+ var_set_variable_then_free_value("date", date_string);
+ free(notyear);
+ free(year);
+
+ /*
+ * Convert host notice sent from to ascii:
+ */
+ var_set_variable("fromhost", hostname ? hostname :
+ inet_ntoa(notice->z_sender_addr));
+
+ /*
+ * Set $message to the message field of the notice with nulls changed
+ * to newlines:
+ */
+ var_set_variable_then_free_value("message",
+ convert_nulls_to_newlines(notice->z_message,
+ notice->z_message_len));
+
+ /*
+ * Decide if its a control notice. If so, return the notice's
+ * opcode. Otherwise, return NULL:
+ */
+ if ((strcasecmp(notice->z_class, WG_CTL_CLASS)==0) && /* <<<>>> */
+ (strcasecmp(notice->z_class_inst, WG_CTL_USER)==0))
+ return(notice->z_opcode);
+ return(0);
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: notice.h,v 1.5 1999/01/22 23:20:29 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+
+#include <zephyr/mit-copyright.h>
+
+#ifndef notice_MODULE
+#define notice_MODULE
+
+#include "new_string.h"
+
+/*
+ * int count_nulls(char *data, int length)
+ * Requires: length>=0
+ * Effects: Returns the # of nulls in data[0]..data[length-1]
+ */
+
+extern int count_nulls();
+
+/*
+ * string get_next_field(char **data_p, int *length_p)
+ * Requires: *length_p >= 0
+ * Modifies: *data_p, *length_p
+ * Effects: Treats (*data_p)[0], (*data_p)[1], ... (*data_p)[length-1]
+ * as a series of null-seperated fields. This function
+ * returns a copy of the first field on the heap. This
+ * string must eventually be freed. Also, *data_p is
+ * advanced and *length_p decreased so that another
+ * call to this procedure with the same arguments will
+ * return the second field. The next call will return
+ * the third field, etc. "" is returned if 0 fields
+ * remain. (this is the case when *length_p == 0)
+ */
+
+extern string get_next_field();
+
+/*
+ * string get_field(char *data, int length, int num)
+ * Requires: length>=0, num>0
+ * Effects: Treats data[0]..data[length-1] as a series of
+ * null-seperated fields. This function returns a copy of
+ * the num'th field (numbered from 1 in this case) on the
+ * heap. This string must eventually be freed. If there
+ * is no num'th field (because num<1 or num># of fields),
+ * "" is returned.
+ */
+
+extern string get_field();
+
+/*
+ * string convert_nulls_to_newlines(data, length)
+ * Requires: length>=0, malloc never returns NULL
+ * Effects: Takes data[0]..data[length-1], converts all nulls to
+ * newlines ('\n') and returns the result as a null-terminated
+ * string on the heap. The returned string must eventually
+ * be freed.
+ */
+
+extern string convert_nulls_to_newlines();
+
+
+extern char *decode_notice();
+
+#endif
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: parser.h,v 1.5 1999/01/22 23:20:29 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+
+#include <zephyr/mit-copyright.h>
+
+#ifndef parser_MODULE
+#define parser_MODULE
+
+/*
+ * Parser-Lexer Internal Routine:
+ *
+ * void report_parse_error(char *error_message, int line_number)
+ * Modifies: error_occured, stderr
+ * Effects: This routine is called to report a parser or lexer
+ * error. Error_message is the error message and line_number
+ * the line number it occured on. The reported error message
+ * is of the form "....<error_message> on line <line #>.\n".
+ * This routine sets error_occured (local to parser.y) to
+ * true. If it was previously false, the error message
+ * is reported to the user via stderr.
+ */
+
+extern void report_parse_error();
+
+/*
+ * struct _Node *parse_file(FILE *input_file)
+ * Requires: input_file is opened for reading, no pointers to
+ * existing nodes will ever be dereferened.
+ * Modifies: *input_file, stderr, all existing nodes
+ * Effects: First this routine destroys all nodes. Then it parses
+ * input_file as a zwgc description langauge file. If
+ * an error is encountered, an error message is printed
+ * on stderr and NULL is returned. If no error is
+ * encountered, a pointer to the node representation of
+ * the parsed program is returned, suitable for passing to
+ * exec.c. Note that NULL will also be returned for a
+ * empty file & is a valid program. Either way, input_file
+ * is closed before this routine returns.
+ */
+
+extern struct _Node *parse_file();
+
+#endif
--- /dev/null
+%{
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: parser.y,v 1.6 1999/01/22 23:20:30 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <sysdep.h>
+
+#if (!defined(lint) && !defined(SABER))
+static const char rcsid_parser_y[] = "$Id: parser.y,v 1.6 1999/01/22 23:20:30 ghudson Exp $";
+#endif
+
+#include <zephyr/mit-copyright.h>
+
+/* Saber-C suppressions because yacc loses */
+
+/*SUPPRESS 288*/
+/*SUPPRESS 287*/
+
+#include <stdio.h>
+#include "lexer.h"
+#include "parser.h"
+#include "node.h"
+#include "zwgc.h"
+
+static void yyerror();
+
+/*
+ * the_program - local variable used to communicate the program's node
+ * representation from the program action to the parse_file
+ * function.
+ */
+
+static Node *the_program;
+%}
+
+%union{
+ char *text;
+ struct _Node *node;
+}
+
+%start program
+
+%token ERROR
+%token <text> VARNAME VARREF STRING SHOW
+
+%token APPENDPORT BUFFER BREAK CLOSEINPUT CLOSEOUTPUT
+%token CLOSEPORT CASE CLEARBUF DEFAULT DISPLAY DO DOWNCASE
+%token ELSE ELSEIF ENDCASE ENDIF ENDWHILE EXEC EXECPORT EXIT
+%token FIELDS GET GETENV IF INPUTPORT LANY LBREAK LSPAN
+%token MATCH NOOP NOT OUTPUTPORT PRINT PROTECT VERBATIM PUT RANY RBREAK
+%token RSPAN SET SUBSTITUTE THEN UPCASE WHILE ZVAR STYLESTRIP
+
+%type <node> expr varname string
+%type <node> exprlist comma_exprlist varnamelist
+%type <node> statement statements program elseparts elseifparts
+%type <node> match matchlist
+
+%left '|'
+%left '&'
+%left EQ NEQ REGEQ REGNEQ
+%left '+'
+%left '!'
+
+%%
+
+/*
+ * A program is simply a list of statements: (may be NULL if no statements...)
+ */
+program : statements
+ { the_program = reverse_list_of_nodes($1);
+ $$ = the_program; }
+ ;
+
+varname : VARNAME
+ { $$ = node_create_string_constant(VARNAME_OPCODE, $1); }
+ ;
+
+string : STRING
+ { $$ = node_create_string_constant(STRING_CONSTANT_OPCODE, $1); }
+ ;
+
+expr : '(' expr ')'
+ { $$ = $2; }
+
+ | string
+ { $$ = $1; }
+ | VARREF
+ { $$ = node_create_string_constant(VARREF_OPCODE, $1); }
+
+ | '!' expr
+ { $$ = node_create_unary(NOT_OPCODE, $2); }
+
+ | expr '+' expr
+ { $$ = node_create_binary(PLUS_OPCODE, $1, $3); }
+ | expr '|' expr /* note "or" == '|' */
+ { $$ = node_create_binary(OR_OPCODE, $1, $3); }
+ | expr '&' expr /* note "and" == '&' */
+ { $$ = node_create_binary(AND_OPCODE, $1, $3); }
+ | expr EQ expr
+ { $$ = node_create_binary(EQ_OPCODE, $1, $3); }
+ | expr NEQ expr
+ { $$ = node_create_binary(NEQ_OPCODE, $1, $3); }
+ | expr REGEQ expr
+ { $$ = node_create_binary(REGEQ_OPCODE, $1, $3); }
+ | expr REGNEQ expr
+ { $$ = node_create_binary(REGNEQ_OPCODE, $1, $3); }
+
+ | BUFFER '(' ')'
+ { $$ = node_create_noary(BUFFER_OPCODE); }
+
+ | SUBSTITUTE '(' expr ')'
+ { $$ = node_create_unary(SUBSTITUTE_OPCODE, $3); }
+ | PROTECT '(' expr ')'
+ { $$ = node_create_unary(PROTECT_OPCODE, $3); }
+ | VERBATIM '(' expr ')'
+ { $$ = node_create_unary(VERBATIM_OPCODE, $3); }
+ | GETENV '(' expr ')'
+ { $$ = node_create_unary(GETENV_OPCODE, $3); }
+ | UPCASE '(' expr ')'
+ { $$ = node_create_unary(UPCASE_OPCODE, $3); }
+ | DOWNCASE '(' expr ')'
+ { $$ = node_create_unary(DOWNCASE_OPCODE, $3); }
+ | ZVAR '(' expr ')'
+ { $$ = node_create_unary(ZVAR_OPCODE, $3); }
+ | GET '(' expr ')'
+ { $$ = node_create_unary(GET_OPCODE, $3); }
+ | STYLESTRIP '(' expr ')'
+ { $$ = node_create_unary(STYLESTRIP_OPCODE, $3); }
+
+ | LANY '(' expr ',' expr ')'
+ { $$ = node_create_binary(LANY_OPCODE, $3, $5 ); }
+ | RANY '(' expr ',' expr ')'
+ { $$ = node_create_binary(RANY_OPCODE, $3, $5 ); }
+ | LBREAK '(' expr ',' expr ')'
+ { $$ = node_create_binary(LBREAK_OPCODE, $3, $5 ); }
+ | RBREAK '(' expr ',' expr ')'
+ { $$ = node_create_binary(RBREAK_OPCODE, $3, $5 ); }
+ | LSPAN '(' expr ',' expr ')'
+ { $$ = node_create_binary(LSPAN_OPCODE, $3, $5 ); }
+ | RSPAN '(' expr ',' expr ')'
+ { $$ = node_create_binary(RSPAN_OPCODE, $3, $5 ); }
+ ;
+
+statement : NOOP
+ { $$ = node_create_noary(NOOP_OPCODE); }
+ | SET varname '=' expr
+ { $$ = node_create_binary(SET_OPCODE, $2, $4); }
+ | FIELDS varnamelist
+ { $$ = node_create_unary(FIELDS_OPCODE,
+ reverse_list_of_nodes($2)); }
+
+ /*
+ * Output to & control of output buffer statements:
+ */
+ | PRINT exprlist
+ { $$ = node_create_unary(PRINT_OPCODE,
+ reverse_list_of_nodes($2)); }
+ | SHOW
+ { $$ = node_create_unary(PRINT_OPCODE,
+ node_create_unary(SUBSTITUTE_OPCODE,
+ node_create_string_constant(STRING_CONSTANT_OPCODE,
+ $1))); }
+ | CLEARBUF
+ { $$ = node_create_noary(CLEARBUF_OPCODE); }
+
+ /*
+ * Statements to manage ports:
+ */
+ | APPENDPORT expr expr
+ { $$ = node_create_binary(APPENDPORT_OPCODE, $2, $3); }
+ | EXECPORT expr expr exprlist
+ { $3->next = reverse_list_of_nodes($4);
+ $$ = node_create_binary(EXECPORT_OPCODE, $2, $3); }
+ | INPUTPORT expr expr
+ { $$ = node_create_binary(INPUTPORT_OPCODE, $2, $3); }
+ | OUTPUTPORT expr expr
+ { $$ = node_create_binary(OUTPUTPORT_OPCODE, $2, $3); }
+ | PUT expr exprlist
+ { $$ = node_create_binary(PUT_OPCODE, $2,
+ reverse_list_of_nodes($3)); }
+ | PUT
+ { $$ = node_create_binary(PUT_OPCODE, 0, 0); }
+ | CLOSEINPUT expr
+ { $$ = node_create_unary(CLOSEINPUT_OPCODE, $2); }
+ | CLOSEOUTPUT expr
+ { $$ = node_create_unary(CLOSEOUTPUT_OPCODE, $2); }
+ | CLOSEPORT expr
+ { $$ = node_create_unary(CLOSEPORT_OPCODE, $2); }
+
+ /*
+ * Statements to run subprocesses without I/O to them:
+ */
+ | EXEC expr exprlist
+ { $2->next = reverse_list_of_nodes($3);
+ $$ = node_create_unary(EXEC_OPCODE, $2); }
+
+ /*
+ * Control statements:
+ */
+ | IF expr THEN statements elseparts ENDIF
+ { Node *n = node_create_binary(IF_OPCODE, $2,
+ reverse_list_of_nodes($4));
+ n->next = $5;
+ $$ = node_create_unary(IF_STMT_OPCODE, n); }
+ | CASE expr matchlist ENDCASE
+ { $$ = node_create_binary(CASE_OPCODE, $2,
+ reverse_list_of_nodes($3)); }
+ | WHILE expr DO statements ENDWHILE
+ { $$ = node_create_binary(WHILE_OPCODE, $2,
+ reverse_list_of_nodes($4)); }
+ | BREAK
+ { $$ = node_create_noary(BREAK_OPCODE); }
+ | EXIT
+ { $$ = node_create_noary(EXIT_OPCODE); }
+ ;
+
+elseparts : elseifparts
+ { $$ = reverse_list_of_nodes($1); }
+ | elseifparts ELSE statements
+ { $$ = node_create_binary(ELSE_OPCODE, 0,
+ reverse_list_of_nodes($3));
+ $$->next = $1;
+ $$ = reverse_list_of_nodes($$); }
+
+/* elseifparts needs to be reversed before using... */
+elseifparts : /* empty */
+ { $$ = 0; }
+ | elseifparts ELSEIF expr THEN statements
+ { $$ = node_create_binary(ELSEIF_OPCODE, $3,
+ reverse_list_of_nodes($5));
+ $$->next = $1; }
+ ;
+
+match : MATCH comma_exprlist statements
+ { $$ = node_create_binary(MATCHLIST_OPCODE,
+ reverse_list_of_nodes($2),
+ reverse_list_of_nodes($3)); }
+ | DEFAULT statements
+ { $$ = node_create_binary(DEFAULT_OPCODE, 0,
+ reverse_list_of_nodes($2)); }
+ ;
+
+/*
+ * Various lists of non-terminals like expr's and varname's. Each is
+ * built up as a linked list using the nodes' next fields. To prevent
+ * Yacc stack overflow on long lists, these are put on the linked list
+ * BACKWARDS. The user of these must first call reverse_list_of_nodes
+ * on one of these before using it. All except comma_exprlist
+ * allow 0 elements on the list in which case their value is NULL.
+ * (comma_exprlist requires at least one element)
+ */
+
+exprlist : /* empty */
+ { $$ = 0; }
+ | exprlist expr
+ { $$ = $2;
+ $$->next = $1; }
+ ;
+
+comma_exprlist : expr
+ { $$ = $1; }
+ | comma_exprlist ',' expr
+ { $$ = $3;
+ $$->next = $1; }
+ ;
+
+varnamelist : /* empty */
+ { $$ = 0; }
+ | varnamelist varname
+ { $$ = $2;
+ $$->next = $1; }
+ ;
+
+matchlist : /* empty */
+ { $$ = 0; }
+ | matchlist match
+ { $$ = $2;
+ $$->next = $1; }
+ ;
+
+statements : /* empty */
+ { $$ = 0; }
+ | statements statement
+ { $$ = $2;
+ $$->next = $1; }
+ ;
+
+%%
+
+/*
+ * error_occured - Set to true when a parse error is reported. If it is false
+ * at the time a parse error is reported, a message is
+ * printed on stderr. See report_parse_error for more
+ * details.
+ */
+
+static int error_occured = 0;
+
+/*
+ * Parser-Lexer Internal Routine:
+ *
+ * void report_parse_error(char *error_message, int line_number)
+ * Modifies: error_occured, stderr
+ * Effects: This routine is called to report a parser or lexer
+ * error. Error_message is the error message and line_number
+ * the line number it occured on. The reported error message
+ * is of the form "....<error_message> on line <line #>.\n".
+ * This routine sets error_occured (local to parser.y) to
+ * true. If it was previously false, the error message
+ * is reported to the user via stderr.
+ */
+
+void report_parse_error(error_message, line_number)
+ char *error_message;
+ int line_number;
+{
+ if (error_occured)
+ return;
+ error_occured = 1;
+
+ fprintf(stderr, "zwgc: error in description file: %s on line %d.\n",
+ error_message, line_number);
+ fflush(stderr);
+}
+
+/*
+ * yyerror - internal routine - used by yacc to report syntax errors and
+ * stack overflow errors.
+ */
+
+static void yyerror(message)
+ char *message;
+{
+ report_parse_error(message, yylineno);
+}
+
+/*
+ * struct _Node *parse_file(FILE *input_file)
+ * Requires: input_file is opened for reading, no pointers to
+ * existing nodes will ever be dereferened.
+ * Modifies: *input_file, stderr, all existing nodes
+ * Effects: First this routine destroys all nodes. Then it parses
+ * input_file as a zwgc description langauge file. If
+ * an error is encountered, an error message is printed
+ * on stderr and NULL is returned. If no error is
+ * encountered, a pointer to the node representation of
+ * the parsed program is returned, suitable for passing to
+ * exec.c. Note that NULL will also be returned for a
+ * empty file & is a valid program. Either way, input_file
+ * is closed before this routine returns.
+ */
+
+struct _Node *parse_file(input_file)
+ FILE *input_file;
+{
+ the_program = NULL;
+ error_occured = 0;
+ node_DestroyAllNodes();
+
+ lex_open(input_file);
+ yyparse();
+ fclose(input_file);
+
+ if (error_occured) {
+ node_DestroyAllNodes();
+ the_program = NULL;
+ }
+
+#ifdef DEBUG
+ if (zwgc_debug) {
+ printf("****************************************************************************\n");
+ node_display(the_program);
+ printf("****************************************************************************\n");
+ }
+#endif
+
+ return(the_program);
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: pointer.h,v 1.6 1999/01/22 23:20:30 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+
+#include <zephyr/mit-copyright.h>
+
+#ifndef pointer_MODULE
+#define pointer_MODULE
+
+#ifdef __STDC__
+typedef void *pointer;
+#else
+typedef char *pointer;
+#endif
+
+#endif
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: port.c,v 1.11 1999/01/22 23:20:31 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <sysdep.h>
+
+#if (!defined(lint) && !defined(SABER))
+static const char rcsid_port_c[] = "$Id: port.c,v 1.11 1999/01/22 23:20:31 ghudson Exp $";
+#endif
+
+#include <zephyr/mit-copyright.h>
+
+/****************************************************************************/
+/* */
+/* The Implementation of the port type: */
+/* */
+/****************************************************************************/
+
+#include "new_string.h"
+#include "port_dictionary.h"
+#include "port.h"
+#include "notice.h"
+#include "variables.h"
+
+/****************************************************************************/
+/* */
+/* Port methods (internal): */
+/* */
+/****************************************************************************/
+
+static string port_get(p)
+ port *p;
+{
+ char *(*get_proc)();
+ char *error = NULL;
+ char *result;
+
+ if (p->status & INPUT_CLOSED) {
+ var_set_variable("error",
+ "Attempt to read from a port whose input has been closed");
+ return(string_Copy(""));
+ }
+
+ get_proc = p->get;
+ if (!get_proc) {
+ var_set_variable("error",
+ "Attempt to read from a port which does not support reading");
+ return(string_Copy(""));
+ }
+
+ result = get_proc(p, &error);
+ if (!result) {
+ var_set_variable("error", error);
+ return(string_Copy(""));
+ } else
+ return(result);
+}
+
+static void port_put(p, data, length)
+ port *p;
+ char *data;
+ int length;
+{
+ char *(*put_proc)();
+ char *error;
+
+ if (p->status & OUTPUT_CLOSED) {
+ var_set_variable("error",
+ "Attempt to write to a port whose output has been closed");
+ return;
+ }
+
+ put_proc = p->put;
+ if (!put_proc) {
+ var_set_variable("error",
+ "Attempt to write to a port which does not support writing");
+ return;
+ }
+
+ error = put_proc(p, data, length);
+ if (error)
+ var_set_variable("error", error);
+}
+
+static void port_close_input(p)
+ port *p;
+{
+ char *(*close_input_proc)();
+ char *error;
+
+ if (p->status & INPUT_CLOSED)
+ return;
+ p->status |= INPUT_CLOSED;
+
+ close_input_proc = p->close_input;
+ if (!close_input_proc)
+ return;
+
+ if (error = close_input_proc(p))
+ var_set_variable("error", error);
+}
+
+static void port_close_output(p)
+ port *p;
+{
+ char *(*close_output_proc)();
+ char *error;
+
+ if (p->status & OUTPUT_CLOSED)
+ return;
+ p->status |= OUTPUT_CLOSED;
+
+ close_output_proc = p->close_output;
+ if (!close_output_proc)
+ return;
+
+ if (error = close_output_proc(p))
+ var_set_variable("error", error);
+}
+
+/****************************************************************************/
+/* */
+/* Code to implement a namespace of ports: */
+/* */
+/****************************************************************************/
+
+/*
+ * port_dict - the dictionary mapping portnames to ports
+ */
+
+static port_dictionary port_dict = NULL;
+
+/*
+ * void init_ports()
+ * Modifies: all ports
+ * Effects: Closes all existing ports. Must be called before
+ * any other port call is made.
+ */
+
+static void close_port_from_binding(b)
+ port_dictionary_binding *b;
+{
+ port_close_input(&(b->value));
+ port_close_output(&(b->value));
+}
+
+void init_ports()
+{
+ if (port_dict) {
+ port_dictionary_Enumerate(port_dict, close_port_from_binding);
+ port_dictionary_Destroy(port_dict);
+ }
+
+ port_dict = port_dictionary_Create(31);
+}
+
+/*
+ * Internal Routine:
+ *
+ * port *create_named_port(string name)
+ * Modifies: the port named name
+ * Requires: init_ports has been called
+ * Effects: If a port with name name already exists, it is first
+ * closed (& destroyed). A new unfilled in port is then
+ * created and assigned the name name. Its address is
+ * then returned. It is up to the caller to fill in its
+ * various fields correctly.
+ */
+
+static port *create_named_port(name)
+ string name;
+{
+ int already_exists;
+ port_dictionary_binding *binding;
+
+ binding = port_dictionary_Define(port_dict, name, &already_exists);
+ if (already_exists) {
+ port_close_input(&(binding->value));
+ port_close_output(&(binding->value));
+ }
+
+ return(&(binding->value));
+}
+
+/*
+ * Internal Routine:
+ *
+ * port *get_named_port(string name)
+ * Requires: init_ports has been called
+ * Effects: If there is a port by name name, returns a pointer to
+ * it. Otherwise returns NULL.
+ */
+
+static port *get_named_port(name)
+ string name;
+{
+ port_dictionary_binding *binding;
+
+ binding = port_dictionary_Lookup(port_dict, name);
+ if (!binding)
+ return(NULL);
+
+ return(&(binding->value));
+}
+
+/****************************************************************************/
+/* */
+/* External interface to named ports: */
+/* */
+/****************************************************************************/
+
+/*
+ * string read_from_port(string name)
+ * Requires: init_ports has been called
+ * Modifies: the port named name if any, $error
+ * Effects: If a port by name name does not exist, sets $error to
+ * "No such port" & returns "". Otherwise, attempts to
+ * read from that port. If an error occurs, $error is
+ * set to the error message and "" returned. Otherwise
+ * the read string is returned. The returned string is
+ * on the heap & must be eventually freed.
+ */
+
+string read_from_port(name)
+ string name;
+{
+ port *p;
+
+ if (!(p = get_named_port(name))) {
+ var_set_variable("error", "No such port");
+ return(string_Copy(""));
+ }
+
+ return(port_get(p));
+}
+
+/*
+ * void write_on_port(string name, char *text, int length)
+ * Requires: init_ports has been called, length>=0
+ * Modifies: the port named name if any, $error
+ * Effects: If a port by name name does not exist, sets $error to
+ * "No such port" & returns. Otherwise, attempts to
+ * write text[0..length-1] on that port. If an error
+ * occurs, $error is set to the error message.
+ */
+
+void write_on_port(name, text, length)
+ string name;
+ char *text;
+ int length;
+{
+ port *p;
+
+ if (!(p = get_named_port(name))) {
+ var_set_variable("error", "No such port");
+ return;
+ }
+
+ port_put(p, text, length);
+}
+
+/*
+ * void close_port_input(string name)
+ * Requires: init_ports has been called
+ * Modifies: the port named name if any, $error
+ * Effects: If a port by name name does not exist, sets $error to
+ * "No such port" & returns. Otherwise, closes the
+ * input part of the port by name name. When both a
+ * port's input & output parts have been closed, the
+ * port is deleted to save space. If an error
+ * occurs, $error is set to the error message.
+ */
+
+void close_port_input(name)
+ string name;
+{
+ port_dictionary_binding *binding;
+
+ binding = port_dictionary_Lookup(port_dict, name);
+ if (!binding)
+ return;
+
+ port_close_input(&(binding->value));
+ if (binding->value.status == PORT_CLOSED)
+ port_dictionary_Delete(port_dict, binding);
+}
+
+/*
+ * void close_port_output(string name)
+ * Requires: init_ports has been called
+ * Modifies: the port named name if any, $error
+ * Effects: If a port by name name does not exist, sets $error to
+ * "No such port" & returns. Otherwise, closes the
+ * output part of the port by name name. When both a
+ * port's input & output parts have been closed, the
+ * port is deleted to save space. If an error
+ * occurs, $error is set to the error message.
+ */
+
+void close_port_output(name)
+ string name;
+{
+ port_dictionary_binding *binding;
+
+ binding = port_dictionary_Lookup(port_dict, name);
+ if (!binding)
+ return;
+
+ port_close_output(&(binding->value));
+ if (binding->value.status == PORT_CLOSED)
+ port_dictionary_Delete(port_dict, binding);
+}
+
+/****************************************************************************/
+/* */
+/* Code to implement a port given some FILE *'s: */
+/* */
+/****************************************************************************/
+
+static string get_file(p, error_p)
+ port *p;
+ char **error_p;
+{
+ char buffer[10000]; /* <<<>>> */
+
+ if (!p->data.file.input_connector) {
+ *error_p = "Attempt to read past end of file";
+ return(NULL);
+ }
+
+ buffer[0] = 0;
+ errno = 0;
+ if (!fgets(buffer, 9999, p->data.file.input_connector)) {
+ if (errno)
+ *error_p = strerror(errno);
+ else
+ *error_p = "Attempt to read past end of file";
+
+ return(NULL);
+ }
+
+ buffer[9999] = 0;
+ return(string_Copy(buffer));
+}
+
+static char *put_file(p, text, length)
+ port *p;
+ string text;
+ int length;
+{
+ if (!p->data.file.output_connector)
+ return(NULL);
+
+ errno = 0;
+ fwrite(text, 1, length, p->data.file.output_connector);
+ fflush(p->data.file.output_connector);
+
+ if (errno)
+ return(strerror(errno));
+
+ return(NULL);
+}
+
+static char *close_file_input(p)
+ port *p;
+{
+ errno = 0;
+ if (p->data.file.input_connector) {
+ fclose(p->data.file.input_connector);
+ p->data.file.input_connector = 0;
+ }
+
+ if (errno)
+ return(strerror(errno));
+
+ return(NULL);
+}
+
+static char *close_file_output(p)
+ port *p;
+{
+ errno = 0;
+ if (p->data.file.output_connector) {
+ fclose(p->data.file.output_connector);
+ p->data.file.output_connector = 0;
+ }
+
+ if (errno)
+ return(strerror(errno));
+
+ return(NULL);
+}
+
+void create_port_from_files(name, input, output)
+ string name;
+ FILE *input;
+ FILE *output;
+{
+ port *p = create_named_port(name);
+
+#if !defined(__HIGHC__)
+ p->get = input ? get_file : NULL;
+ p->put = output ? put_file : NULL;
+#else
+ /* RT compiler (hc2.1y) bug workaround */
+ if (input)
+ p->get = get_file;
+ else
+ p->get = NULL;
+ if (output)
+ p->put = put_file;
+ else
+ p->put = NULL;
+#endif
+ p->close_input = close_file_input;
+ p->close_output = close_file_output;
+ p->status = 0;
+ p->data.file.input_connector = input;
+ p->data.file.output_connector = output;
+}
+
+/****************************************************************************/
+/* */
+/* Code for creating various types of FILE * ports: */
+/* */
+/****************************************************************************/
+
+void create_subprocess_port(name, argv)
+ string name;
+ char **argv;
+{
+ int pid;
+ int to_child_descriptors[2];
+ int to_parent_descriptors[2];
+ FILE *in = 0;
+ FILE *out = 0;
+
+ /* <<<>>> (file leak) */
+ if (pipe(to_child_descriptors)!=0 || pipe(to_parent_descriptors)!=0)
+ return;
+
+ pid = fork();
+ if (pid == -1) {
+ fprintf(stderr, "zwgc: error while attempting to fork: ");
+ perror("");
+ return; /* <<<>>> */
+ } else if (pid == 0) { /* in child */
+ close(0);
+ close(1);
+ dup2(to_child_descriptors[0], 0);
+ dup2(to_parent_descriptors[1], 1);
+ close(to_child_descriptors[1]);
+ close(to_parent_descriptors[0]);
+
+ execvp(argv[0], argv);
+ fprintf(stderr,"zwgc: unable to exec %s: ", argv[0]);
+ perror("");
+ _exit(errno);
+ }
+
+ fcntl(to_parent_descriptors[0], F_SETFD, 1);
+ fcntl(to_child_descriptors[1], F_SETFD, 1);
+ in = fdopen(to_parent_descriptors[0],"r");
+ out = fdopen(to_child_descriptors[1],"w");
+ close(to_child_descriptors[0]);
+ close(to_parent_descriptors[1]);
+
+ create_port_from_files(name, in, out);
+}
+
+void create_file_append_port(name, filename)
+ string name;
+ string filename;
+{
+ FILE *out;
+ int oumask;
+
+ errno = 0;
+
+ oumask = umask(077); /* allow read/write for us only */
+ out = fopen(filename, "a");
+ (void) umask(oumask);
+ if (out == NULL) {
+ var_set_variable("error", strerror(errno));
+ return;
+ }
+
+ create_port_from_files(name, 0, out);
+}
+
+void create_file_input_port(name, filename)
+ string name;
+ string filename;
+{
+ FILE *in;
+
+ errno = 0;
+ in = fopen(filename, "r");
+ if (in == NULL) {
+ var_set_variable("error", strerror(errno));
+ return;
+ }
+
+ create_port_from_files(name, in, 0);
+}
+
+void create_file_output_port(name, filename)
+ string name;
+ string filename;
+{
+ FILE *out;
+ int oumask;
+
+ errno = 0;
+
+ oumask = umask(077); /* allow read/write for us only */
+ out = fopen(filename, "w");
+ (void) umask(oumask);
+ if (out == NULL) {
+ var_set_variable("error", strerror(errno));
+ return;
+ }
+
+ create_port_from_files(name, 0, out);
+}
+
+/****************************************************************************/
+/* */
+/* Code to implement a port given a filter function: */
+/* */
+/****************************************************************************/
+
+static string get_filter(p, error_p)
+ port *p;
+ char **error_p;
+{
+ string result;
+
+ if (string_stack_empty(p->data.filter.waiting_packets)) {
+ *error_p = "Attempt to read from port when no data available";
+ return(NULL);
+ }
+
+ result = string_stack_top(p->data.filter.waiting_packets);
+ string_stack_pop(p->data.filter.waiting_packets);
+ return(result);
+}
+
+static char *put_filter(p, text, length)
+ port *p;
+ string text;
+ int length;
+{
+ string input;
+ string output;
+
+ if (p->status & INPUT_CLOSED)
+ return(NULL);
+
+ input = convert_nulls_to_newlines(text, length);
+ output = (*(p->data.filter.filter))(input);
+ free(input);
+ string_stack_push(p->data.filter.waiting_packets, output);
+ return(NULL);
+}
+
+static char *close_filter_input(p)
+ port *p;
+{
+ while (!string_stack_empty(p->data.filter.waiting_packets))
+ string_stack_pop(p->data.filter.waiting_packets);
+
+ return(NULL);
+}
+
+/*ARGSUSED*/
+static char *close_filter_output(p)
+ port *p;
+{
+ return(NULL);
+}
+
+void create_port_from_filter(name, filter)
+ string name;
+ string (*filter)();
+{
+ port *p = create_named_port(name);
+
+ p->get = get_filter;
+ p->put = put_filter;
+ p->close_input = close_filter_input;
+ p->close_output = close_filter_output;
+ p->status = 0;
+ p->data.filter.waiting_packets = string_stack_create();
+ p->data.filter.filter = filter;
+}
+
+/****************************************************************************/
+/* */
+/* Code to implement a port given an output function: */
+/* */
+/****************************************************************************/
+
+static char *put_output(p, text, length)
+ port *p;
+ string text;
+ int length;
+{
+ string input;
+ char *error;
+
+ input = convert_nulls_to_newlines(text, length);
+ error = p->data.output.output(input);
+ free(input);
+ return(error);
+}
+
+/*ARGSUSED*/
+static char *close_output(p)
+ port *p;
+{
+ return(NULL);
+}
+
+void create_port_from_output_proc(name, output)
+ string name;
+ char *(*output)();
+{
+#ifdef SABER /* Yes, it's another ANSI incompatiblity */
+ port *p;
+#else
+ port *p = create_named_port(name);
+#endif
+
+#ifdef SABER
+ p = create_named_port(name);
+#endif
+
+ p->get = NULL;
+ p->put = put_output;
+ p->close_input = close_output;
+ p->close_output = close_output;
+ p->status = 0;
+ p->data.output.output = output;
+}
--- /dev/null
+#ifndef port_TYPE
+#define port_TYPE
+
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: port.h,v 1.6 1999/01/22 23:20:32 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+
+#include <zephyr/mit-copyright.h>
+
+#include <stdio.h>
+#include "new_string.h"
+#include "string_stack.h"
+
+union port__data {
+ struct {
+ FILE *input_connector;
+ FILE *output_connector;
+ } file;
+ struct {
+ string_stack waiting_packets;
+ string (*filter)();
+ } filter;
+ struct {
+ char *(*output)();
+ } output;
+};
+
+typedef struct { /* PRIVATE */
+ char *(*get)();
+ char *(*put)();
+ char *(*close_input)();
+ char *(*close_output)();
+#define INPUT_CLOSED 0x1
+#define OUTPUT_CLOSED 0x2
+#define PORT_CLOSED 0x3
+ int status;
+ union port__data data;
+} port;
+
+/*
+ * void init_ports()
+ * Modifies: all ports
+ * Effects: Closes all existing ports. Must be called before
+ * any other port call is made.
+ */
+
+extern void init_ports();
+
+/*
+ * string read_from_port(string name)
+ * Requires: init_ports has been called
+ * Modifies: the port named name if any, $error
+ * Effects: If a port by name name does not exist, sets $error to
+ * "No such port" & returns "". Otherwise, attempts to
+ * read from that port. If an error occurs, $error is
+ * set to the error message and "" returned. Otherwise
+ * the read string is returned. The returned string is
+ * on the heap & must be eventually freed.
+ */
+
+extern string read_from_port();
+
+/*
+ * void write_on_port(string name, char *text, int length)
+ * Requires: init_ports has been called, length>=0
+ * Modifies: the port named name if any, $error
+ * Effects: If a port by name name does not exist, sets $error to
+ * "No such port" & returns. Otherwise, attempts to
+ * write text[0..length-1] on that port. If an error
+ * occurs, $error is set to the error message.
+ */
+
+extern void write_on_port();
+
+/*
+ * void close_port_input(string name)
+ * Requires: init_ports has been called
+ * Modifies: the port named name if any, $error
+ * Effects: If a port by name name does not exist, sets $error to
+ * "No such port" & returns. Otherwise, closes the
+ * input part of the port by name name. When both a
+ * port's input & output parts have been closed, the
+ * port is deleted to save space. If an error
+ * occurs, $error is set to the error message.
+ */
+
+extern void close_port_input();
+
+/*
+ * void close_port_output(string name)
+ * Requires: init_ports has been called
+ * Modifies: the port named name if any, $error
+ * Effects: If a port by name name does not exist, sets $error to
+ * "No such port" & returns. Otherwise, closes the
+ * output part of the port by name name. When both a
+ * port's input & output parts have been closed, the
+ * port is deleted to save space. If an error
+ * occurs, $error is set to the error message.
+ */
+
+extern void close_port_output();
+
+
+extern void create_subprocess_port();
+extern void create_file_append_port();
+extern void create_file_input_port();
+extern void create_file_output_port();
+extern void create_port_from_filter();
+extern void create_port_from_output_proc();
+
+extern void init_standard_ports();
+extern void create_port_from_files();
+
+#endif
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: regexp.c,v 1.9 1999/01/22 23:20:32 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <sysdep.h>
+#include <regex.h>
+
+#if (!defined(lint) && !defined(SABER))
+static const char rcsid_regexp_c[] = "$Id: regexp.c,v 1.9 1999/01/22 23:20:32 ghudson Exp $";
+#endif
+
+#include "regexp.h"
+
+int ed_regexp_match_p(test_string, pattern)
+ string test_string;
+ string pattern;
+{
+ regex_t RE;
+ int retval;
+ char errbuf[512];
+
+ retval = regcomp(&RE, pattern, REG_NOSUB);
+ if (retval != 0) {
+ regerror(retval, &RE, errbuf, sizeof(errbuf));
+ fprintf(stderr,"%s in regcomp %s\n",errbuf,pattern);
+ return(0);
+ }
+ retval = regexec(&RE, test_string, 0, NULL, 0);
+ if (retval != 0 && retval != REG_NOMATCH) {
+ regerror(retval, &RE, errbuf, sizeof(errbuf));
+ fprintf(stderr,"%s in regexec %s\n",errbuf,pattern);
+ regfree(&RE);
+ return(0);
+ }
+ regfree(&RE);
+ return(retval == 0 ? 1 : 0);
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: regexp.h,v 1.5 1999/01/22 23:20:33 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+
+#include <zephyr/mit-copyright.h>
+
+#ifndef regexp_MODULE
+#define regexp_MODULE
+
+#include "new_string.h"
+
+extern int ed_regexp_match_p();
+
+#endif
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: stack.h,v 1.5 1999/01/22 23:20:33 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+
+#include <zephyr/mit-copyright.h>
+
+/****************************************************************************/
+/* */
+/* A generic stack type based on linked lists: */
+/* */
+/****************************************************************************/
+
+#ifndef TYPE_T_stack_TYPE
+#define TYPE_T_stack_TYPE
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+typedef struct _TYPE_T_stack {
+ struct _TYPE_T_stack *next;
+ TYPE_T data;
+} *TYPE_T_stack;
+
+#define TYPE_T_stack_create() ((struct _TYPE_T_stack *) NULL)
+
+#define TYPE_T_stack_empty(stack) (!(stack))
+
+#ifdef DEBUG
+#define TYPE_T_stack_top(stack) ((stack) ? (stack)->data :\
+ (abort(),(stack)->data))
+#else
+#define TYPE_T_stack_top(stack) ((stack)->data)
+#endif
+
+#ifdef DEBUG
+#define TYPE_T_stack_pop(stack) { TYPE_T_stack old = (stack);\
+ if (!old)\
+ abort(); /*<<<>>>*/\
+ (stack) = old->next;\
+ free(old); }
+#else
+#define TYPE_T_stack_pop(stack) { TYPE_T_stack old = (stack);\
+ (stack) = old->next;\
+ free(old); }
+#endif
+
+#define TYPE_T_stack_push(stack,object) \
+ { TYPE_T_stack new = (struct _TYPE_T_stack *)\
+ malloc(sizeof (struct _TYPE_T_stack));\
+ new->next = (stack);\
+ new->data = object;\
+ (stack) = new; }
+
+#endif
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: standard_ports.c,v 1.13 1999/01/22 23:20:34 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <sysdep.h>
+
+#if (!defined(lint) && !defined(SABER))
+static const char rcsid_standard_ports_c[] = "$Id: standard_ports.c,v 1.13 1999/01/22 23:20:34 ghudson Exp $";
+#endif
+
+#include <zephyr/mit-copyright.h>
+
+/****************************************************************************/
+/* */
+/* Code to setup standard ports: */
+/* */
+/****************************************************************************/
+
+#include "new_memory.h"
+#include "port.h"
+#include "variables.h"
+#include "error.h"
+#include "main.h"
+#include <zephyr/zephyr.h>
+
+extern string tty_filter();
+extern int tty_filter_init();
+
+#ifndef X_DISPLAY_MISSING
+extern char *X_driver();
+extern int X_driver_init();
+#endif
+
+extern void usage();
+
+/*
+ *
+ */
+
+char *plain_driver(input)
+ string input;
+{
+ string processed_input = tty_filter(input, 0);
+
+ fputs(processed_input, stdout);
+ fflush(stdout);
+ free(processed_input);
+ return(NULL);
+}
+
+/*
+ *
+ */
+
+char *tty_driver(input)
+ string input;
+{
+ string processed_input = tty_filter(input, 1);
+
+ fputs(processed_input, stdout);
+ fflush(stdout);
+ free(processed_input);
+ return(NULL);
+}
+
+/*
+ *
+ */
+
+string noop_filter(input)
+ string input;
+{
+ return(input);
+}
+
+/*
+ *
+ */
+
+string plain_filter(input)
+ string input;
+{
+ return(tty_filter(input, 0));
+}
+
+/*
+ *
+ */
+
+string fancy_filter(input)
+ string input;
+{
+ return(tty_filter(input, 1));
+}
+
+/*
+ *
+ */
+
+static struct standard_port_info {
+ char *port_name;
+/*
+ * 0 = ok to use as the default output port
+ * 1 = not ok to use as the default output port
+ * 2 = disabled
+ */
+#define DEFAULT_OK 0
+#define DEFAULT_NOTOK 1
+#define DISABLED 2
+
+ int port_setup_status;
+ int (*port_init)();
+#define INPUT_DESC 0
+#define OUTPUT_DESC 1
+#define FILTER 2
+#define OUTPUT_PROC 3
+ int type;
+ char *(*function)();
+ int setup_arg;
+} standard_port_info_table[] = {
+#ifndef X_DISPLAY_MISSING
+{ "X", DEFAULT_OK, X_driver_init, OUTPUT_PROC, X_driver, 0},
+{ "tty", DEFAULT_NOTOK, tty_filter_init, OUTPUT_PROC, tty_driver, 0},
+#else
+{ "tty", DEFAULT_OK, tty_filter_init, OUTPUT_PROC, tty_driver, 0},
+#endif
+{ "plain", DEFAULT_NOTOK, tty_filter_init, OUTPUT_PROC, plain_driver, 0},
+{ "stdout", DEFAULT_NOTOK, NULL, OUTPUT_DESC, NULL, 1},
+{ "stderr", DEFAULT_NOTOK, NULL, OUTPUT_DESC, NULL, 2},
+
+{ "stdin", DEFAULT_NOTOK, NULL, INPUT_DESC, NULL, 0},
+{ "loopback", DEFAULT_NOTOK, NULL, FILTER, noop_filter, 0},
+{ "plain_filter", DEFAULT_NOTOK, tty_filter_init, FILTER, plain_filter, 0},
+{ "tty_filter", DEFAULT_NOTOK, tty_filter_init, FILTER, fancy_filter, 0},
+
+{ NULL, DISABLED, NULL, FILTER, NULL, 0} };
+
+/*
+ * <<<>>>
+ */
+
+static struct standard_port_info *get_standard_port_info(port_name)
+ string port_name;
+{
+ struct standard_port_info *p;
+
+ for (p=standard_port_info_table; p->port_name; p++)
+ if (string_Eq(p->port_name, port_name) && p->port_setup_status!=DISABLED)
+ return(p);
+
+ return(NULL);
+}
+
+/*
+ * Internal Routine:
+ *
+ * int boolean_value_of(string text)
+ * Effects: If text represents yes/true/on, return 1. If text
+ * representes no/false/off, return 0. Otherwise,
+ * returns -1.
+ */
+
+static int boolean_value_of(text)
+ string text;
+{
+ if (!text)
+ return(-1); /* not set */
+ if (!strcasecmp("yes", text) || !strcasecmp("y", text) ||
+ !strcasecmp("true", text) || !strcasecmp("t", text) ||
+ !strcasecmp("on", text))
+ return(1);
+ else if (!strcasecmp("no", text) || !strcasecmp("n", text) ||
+ !strcasecmp("false", text) || !strcasecmp("f", text) ||
+ !strcasecmp("off", text))
+ return(0);
+ else
+ return(-1);
+}
+
+/*
+ *
+ */
+
+void init_standard_ports(pargc, argv)
+ int *pargc;
+ char **argv;
+{
+ struct standard_port_info *p;
+ string first_working_port = "";
+ string default_port = "";
+ char **new, **current;
+ int fallback;
+
+ /*
+ * Process argument list handling "-disable <port>" and
+ * "-default <output port>" arguments, as well as "-ttymode"
+ */
+ for (new=current=argv+1; *current; current++) {
+ if (string_Eq((string) *current, "-disable")) {
+ current++; *pargc -= 2;
+ if (!*current)
+ usage();
+ if (p = get_standard_port_info((string) *current))
+ p->port_setup_status = DISABLED;
+ } else if (string_Eq((string) *current, "-default")) {
+ current++; *pargc -= 2;
+ if (!*current)
+ usage();
+ default_port = (string) *current;
+ if (p = get_standard_port_info((string) *current))
+ p->port_setup_status = DEFAULT_OK;
+ } else if (string_Eq((string) *current, "-ttymode")) {
+ default_port = (string) "tty";
+ (*pargc)--;
+ if (p = get_standard_port_info(default_port)) {
+ p->port_setup_status = DEFAULT_OK;
+ if (p = get_standard_port_info ((string) "X"))
+ p->port_setup_status = DISABLED;
+ }
+ } else
+ *(new++) = *current;
+ }
+ *new = *current;
+
+ fallback = boolean_value_of(ZGetVariable("fallback"));
+ /*
+ * Initialize all non-disabled ports. If a port reports an error,
+ * disable that port. Set default_port if not already set
+ * by the -default argument to the first non-disabled port.
+ */
+ for (p = standard_port_info_table; p->port_name; p++) {
+ if (p->port_setup_status == DISABLED)
+ continue;
+
+ if (p->port_init && (*(p->port_init))(p->port_name,
+ *first_working_port,
+ pargc, argv)) {
+ p->port_setup_status = DISABLED;
+ continue;
+ }
+
+ if (fallback == 1) {
+ /* we are doing fallback, make DEFAULT_NOTOK ports OK */
+ p->port_setup_status = DEFAULT_OK;
+ }
+ if (!*first_working_port)
+ first_working_port = p->port_name;
+ switch (p->type) {
+ case INPUT_DESC:
+ create_port_from_files(p->port_name, fdopen(p->setup_arg, "r"),0);
+ break;
+
+ case OUTPUT_DESC:
+ create_port_from_files(p->port_name, 0, fdopen(p->setup_arg, "w"));
+ break;
+
+ case FILTER:
+ create_port_from_filter(p->port_name, p->function);
+ break;
+
+ case OUTPUT_PROC:
+ create_port_from_output_proc(p->port_name, p->function);
+ break;
+ }
+ }
+
+ if (!default_port[0]) {
+ /* no default port has been set */
+ for (p = get_standard_port_info(first_working_port); p->port_name; p++)
+ if ((p->port_setup_status == DEFAULT_OK))
+ break;
+ if (p->port_name)
+ var_set_variable("output_driver", p->port_name);
+ else { /* no suitable default has been found */
+ if (fallback == -1) /* complain, since indeterminate */
+ ERROR2(
+"To receive Zephyrgrams, (type `%s -ttymode').\n",
+ progname);
+ exit(1);
+ }
+ } else
+ var_set_variable("output_driver", default_port);
+
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: string_dictionary_aux.c,v 1.2 1999/01/22 23:20:35 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#if (!defined(lint) && !defined(SABER))
+static char rcsid_string_dictionary_aux_c[] = "$Id: string_dictionary_aux.c,v 1.2 1999/01/22 23:20:35 ghudson Exp $";
+#endif
+
+/*
+ * string_dictionary_aux - a module implementing convenience routines for use
+ * with string_dictionarys
+ *
+ * Overview:
+ *
+ * This module implements Fetch and Set operations on
+ * string_dictionaries which take the place of Define and Lookup for
+ * most uses. The importance difference between them and Define and
+ * Lookup is that they maintain the invariant that all the value strings
+ * in a string_dictionary are on the heap. In particular, they do
+ * free's and string_Copy's whenever needed. Also implemented is
+ * SafeDestroy which does a Destroy after freeing all the value strings
+ * in a string_dictionary.
+ */
+
+#include <sysdep.h>
+#include "new_memory.h"
+#include "string_dictionary.h"
+
+/*
+ * void string_dictionary_Set(string_dictionary d, string key,string value):
+ * Modifies: d
+ * Effects: Binds key to value in d. Automatically free's the
+ * previous value of key, if any. Value is copied on the
+ * heap.
+ */
+
+void string__dictionary_Set(d, key, value)
+ string_dictionary d;
+ string key;
+ string value;
+{
+ string_dictionary_binding *binding;
+ int already_exists;
+
+ binding = string_dictionary_Define(d, key, &already_exists);
+ if (already_exists)
+ free(binding->value);
+
+ binding->value = string_Copy(value);
+}
+
+/*
+ * char *string_dictionary_Fetch(string_dictionary d, string key)
+ * Effects: If key is not bound in d, returns 0. Otherwise,
+ * returns the value that key is bound to.
+ * Note that the returned string if any should not be
+ * freed or modified in any way. Note also that it may
+ * disappear later if key is rebound.
+ */
+
+char *string_dictionary_Fetch(d, key)
+ string_dictionary d;
+ string key;
+{
+ string_dictionary_binding *binding;
+
+ binding = string_dictionary_Lookup(d, key);
+ if (!binding)
+ return(0);
+
+ return(binding->value);
+}
+
+/*
+ * void string_dictionary_SafeDestroy(string_dictionary d)
+ * Modifies: d
+ * Effects: Like string_dictionary_Destroy except first frees
+ * all value's in the dictionary.
+ */
+
+static void free_value_of_binding(b)
+ string_dictionary_binding *b;
+{
+ free(b->value);
+}
+
+void string_dictionary_SafeDestroy(d)
+ string_dictionary d;
+{
+ string_dictionary_Enumerate(d, free_value_of_binding);
+ string_dictionary_Destroy(d);
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: string_dictionary_aux.h,v 1.2 1999/01/22 23:20:37 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#ifndef string_dictionary_aux_MODULE
+#define string_dictionary_aux_MODULE
+
+#include "new_memory.h"
+#include "string_dictionary.h"
+
+/*
+ * void string_dictionary_Set(string_dictionary d, string key,string value):
+ * Modifies: d
+ * Effects: Binds key to value in d. Automatically free's the
+ * previous value of key, if any. Value is copied on the
+ * heap.
+ */
+
+extern void string__dictionary_Set();
+#ifdef DEBUG_MEMORY
+#define string_dictionary_Set(a,b,c) (set_module(__FILE__,__LINE__),\
+ string__dictionary_Set(a,b,c))
+#else
+#define string_dictionary_Set(a,b,c) string__dictionary_Set(a,b,c)
+#endif
+
+/*
+ * char *string_dictionary_Fetch(string_dictionary d, string key)
+ * Effects: If key is not bound in d, returns 0. Otherwise,
+ * returns the value that key is bound to.
+ * Note that the returned string if any should not be
+ * freed or modified in any way. Note also that it may
+ * disappear later if key is rebound.
+ */
+
+extern char *string_dictionary_Fetch();
+
+/*
+ * void string_dictionary_SafeDestroy(string_dictionary d)
+ * Modifies: d
+ * Effects: Like string_dictionary_Destroy except first frees
+ * all value's in the dictionary.
+ */
+
+extern void string_dictionary_SafeDestroy();
+
+#endif
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: subscriptions.c,v 1.13 1999/06/01 19:01:19 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#if (!defined(lint) && !defined(SABER))
+static char rcsid_subscriptions_c[] = "$Id: subscriptions.c,v 1.13 1999/06/01 19:01:19 ghudson Exp $";
+#endif
+
+/****************************************************************************/
+/* */
+/* Subscriptions.c: code to deal with subscriptions & punting: */
+/* */
+/****************************************************************************/
+
+#include <sysdep.h>
+#include <zephyr/zephyr.h>
+#include <netdb.h>
+#include "new_memory.h"
+#include "new_string.h"
+#include "int_dictionary.h"
+#include "zwgc.h"
+#include "subscriptions.h"
+#include "error.h"
+#include "file.h"
+#include "main.h"
+
+/****************************************************************************/
+/* */
+/* Code to implement punting of notices: */
+/* */
+/****************************************************************************/
+
+/*
+ *
+ */
+static int_dictionary puntable_addresses_dict = 0;
+
+static void init_puntable_dict()
+{
+ puntable_addresses_dict = int_dictionary_Create(33);
+}
+
+static string address_to_string(class, instance, recipient)
+ string class;
+ string instance;
+ string recipient;
+{
+ string result;
+
+ /*
+ * Treat a recipient of "" as "*":
+ */
+ if (string_Eq(recipient,""))
+ recipient = "*";
+
+ /*
+ * The following is a hack for now only. It should be replaced with
+ * several calls to escape_code... <<<>>>
+ */
+ result = string_Concat(class, "\001");
+ result = string_Concat2(result, instance);
+ result = string_Concat2(result, "\001");
+ result = string_Concat2(result, recipient);
+ string_Downcase(result);
+
+ return(result);
+}
+
+int puntable_address_p(class, instance, recipient)
+ string class;
+ string instance;
+ string recipient;
+{
+ string temp;
+
+ if (!puntable_addresses_dict)
+ init_puntable_dict();
+
+ temp = address_to_string(class, instance, recipient);
+ if (int_dictionary_Lookup(puntable_addresses_dict, temp)) {
+ free(temp);
+ return(1);
+ }
+
+ free(temp);
+ return(0);
+}
+
+void punt(class, instance, recipient)
+ string class;
+ string instance;
+ string recipient;
+{
+ string temp;
+
+ if (!puntable_addresses_dict)
+ init_puntable_dict();
+
+ temp = address_to_string(class, instance, recipient);
+ (void)int_dictionary_Define(puntable_addresses_dict, temp, 0);
+ free(temp);
+}
+
+void unpunt(class, instance, recipient)
+ string class;
+ string instance;
+ string recipient;
+{
+ string temp;
+ int_dictionary_binding *binding;
+
+ if (!puntable_addresses_dict)
+ init_puntable_dict();
+
+ temp = address_to_string(class, instance, recipient);
+ binding = int_dictionary_Define(puntable_addresses_dict, temp, 0);
+ free(temp);
+ if (binding)
+ int_dictionary_Delete(puntable_addresses_dict, binding);
+}
+
+/****************************************************************************/
+/* */
+/* Code to implement batching [un]subscription requests: */
+/* */
+/****************************************************************************/
+
+/*
+ * <<<>>> these routines require zwgc_active to be false (0)
+ */
+
+#define BATCH_SIZE 20
+
+static int subscription_list_size = 0;
+static ZSubscription_t subscription_list[BATCH_SIZE];
+
+static int unsubscription_list_size = 0;
+static ZSubscription_t unsubscription_list[BATCH_SIZE];
+
+static void free_subscription_list(list, number_of_elements)
+ ZSubscription_t *list;
+ int number_of_elements;
+{
+ int i;
+
+ for (i=0; i<number_of_elements; i++) {
+ free(list[i].zsub_class);
+ free(list[i].zsub_classinst);
+ free(list[i].zsub_recipient);
+ }
+}
+
+static void flush_subscriptions()
+{
+ TRAP(ZSubscribeTo(subscription_list,subscription_list_size, 0),
+ "while subscribing");
+
+ free_subscription_list(subscription_list, subscription_list_size);
+ subscription_list_size = 0;
+}
+
+static void flush_unsubscriptions()
+{
+ if (unsubscription_list_size)
+ TRAP(ZUnsubscribeTo(unsubscription_list, unsubscription_list_size, 0),
+ "while unsubscribing");
+
+ free_subscription_list(unsubscription_list, unsubscription_list_size);
+ unsubscription_list_size = 0;
+}
+
+static void subscribe(class, instance, recipient)
+ string class;
+ string instance;
+ string recipient;
+{
+ subscription_list[subscription_list_size].zsub_class = string_Copy(class);
+ subscription_list[subscription_list_size].zsub_classinst= string_Copy(instance);
+ subscription_list[subscription_list_size].zsub_recipient=string_Copy(recipient);
+
+ if (++subscription_list_size == BATCH_SIZE)
+ flush_subscriptions();
+}
+
+static void unsubscribe(class, instance, recipient)
+ string class;
+ string instance;
+ string recipient;
+{
+ unsubscription_list[unsubscription_list_size].zsub_class = string_Copy(class);
+ unsubscription_list[unsubscription_list_size].zsub_classinst
+ = string_Copy(instance);
+ unsubscription_list[unsubscription_list_size].zsub_recipient
+ = string_Copy(recipient);
+
+ if (++unsubscription_list_size == BATCH_SIZE)
+ flush_unsubscriptions();
+}
+
+/****************************************************************************/
+/* */
+/* Code to implement reading [un]subscriptions from a file: */
+/* */
+/****************************************************************************/
+
+#define TOKEN_HOSTNAME "%host%"
+#define TOKEN_CANONNAME "%canon%"
+#define TOKEN_ME "%me%"
+#define TOKEN_WILD "*"
+
+char ourhost[MAXHOSTNAMELEN],ourhostcanon[MAXHOSTNAMELEN];
+
+static void inithosts()
+{
+ struct hostent *hent;
+ if (gethostname(ourhost, sizeof(ourhost)-1) == -1) {
+ ERROR3("unable to retrieve hostname, %s and %s will be wrong in subscriptions.\n", TOKEN_HOSTNAME, TOKEN_CANONNAME);
+ return;
+ }
+
+ if (!(hent = gethostbyname(ourhost))) {
+ ERROR2("unable to resolve hostname, %s will be wrong in subscriptions.\n", TOKEN_CANONNAME);
+ strncpy(ourhostcanon, ourhost, sizeof(ourhostcanon)-1);
+ return;
+ }
+ strncpy(ourhostcanon, hent->h_name, sizeof(ourhostcanon)-1);
+ return;
+}
+
+static void macro_sub(str)
+ char *str;
+{
+ static int initedhosts = 0;
+
+ if (!initedhosts) {
+ inithosts();
+ initedhosts = 1;
+ }
+ if (string_Eq(str, TOKEN_ME))
+ strcpy(str, ZGetSender());
+ else if (string_Eq(str, TOKEN_HOSTNAME))
+ strcpy(str, ourhost);
+ else if (string_Eq(str, TOKEN_CANONNAME))
+ strcpy(str, ourhostcanon);
+}
+
+#define UNSUBSCRIBE_CHARACTER '!'
+#define PUNT_CHARACTER '-'
+
+static void load_subscriptions_from_file(file)
+ FILE *file;
+{
+ char line[BUFSIZ];
+ char class_buffer[BUFSIZ], instance[BUFSIZ], recipient[BUFSIZ];
+ char *class, *temp;
+ char c;
+
+ while ((!feof(file)) && (!ferror(file))) {
+ if (fgets(line, BUFSIZ, file)) {
+ class = class_buffer;
+ /* Parse line */
+ /* <<<>>>
+ * The below does NOT work is the recipient field is "":
+ */
+ if (temp = strchr(line, '#'))
+ *temp = '\0';
+ for (temp=line; *temp && *temp==' '; temp++) ;
+ if (!*temp || *temp=='\n')
+ continue;
+
+ sscanf(temp,"%[^,],%[^,],%s", class, instance, recipient);
+
+ /* skip type indicator if any: */
+ c = class[0];
+ if (c==UNSUBSCRIBE_CHARACTER || c==PUNT_CHARACTER)
+ class++;
+
+ /* perform macro substitutions */
+ macro_sub(class);
+ macro_sub(instance);
+ macro_sub(recipient);
+
+ /* do the right thing with it */
+ switch (c) {
+ case UNSUBSCRIBE_CHARACTER:
+ unsubscribe(class, instance, recipient);
+ break;
+ case PUNT_CHARACTER:
+ punt(class, instance, recipient);
+ break;
+ default:
+ subscribe(class, instance, recipient);
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+
+ if (ferror(file)) {
+ com_err("zwgc", errno, "while reading from subscription file");
+ exit(1);
+ }
+
+ flush_subscriptions();
+ flush_unsubscriptions();
+
+ fclose(file);
+}
+
+#define DEFSUBS "/dev/null"
+
+static void load_subscriptions()
+{
+ FILE *subscriptions_file;
+
+ /* no system default sub file on client--they live on the server */
+ /* BUT...we need to use something to call load_subscriptions_from_file,
+ so we use /dev/null */
+ subscriptions_file = locate_file(subscriptions_filename_override,
+ USRSUBS, DEFSUBS);
+ if (subscriptions_file)
+ load_subscriptions_from_file(subscriptions_file);
+}
+
+/****************************************************************************/
+/* */
+/* Code to implement shutdown and startup: */
+/* */
+/****************************************************************************/
+
+int zwgc_active = 0;
+
+static ZSubscription_t *saved_subscriptions = NULL;
+static int number_of_saved_subscriptions;
+
+void zwgc_shutdown()
+{
+ if (!zwgc_active)
+ return;
+
+ TRAP(ZRetrieveSubscriptions(0, &number_of_saved_subscriptions),
+ "while retrieving zephyr subscription list");
+ if (error_code)
+ return;
+ saved_subscriptions = (ZSubscription_t *)
+ malloc(number_of_saved_subscriptions*sizeof(ZSubscription_t));
+ if (number_of_saved_subscriptions)
+ TRAP(ZGetSubscriptions(saved_subscriptions,
+ &number_of_saved_subscriptions),
+ "while getting subscriptions");
+ if (error_code) {
+ free(saved_subscriptions);
+ saved_subscriptions = NULL;
+ }
+ TRAP(ZCancelSubscriptions(0), "while canceling subscriptions") ;
+
+ zwgc_active = 0;
+}
+
+void zwgc_startup()
+{
+ if (zwgc_active)
+ return;
+
+ if (saved_subscriptions) {
+ TRAP(ZSubscribeTo(saved_subscriptions,number_of_saved_subscriptions,0),
+ "while resubscribing to zephyr messages");
+ free(saved_subscriptions);
+ saved_subscriptions = NULL;
+ } else
+ load_subscriptions();
+
+ zwgc_active = 1;
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: subscriptions.h,v 1.6 1999/01/22 23:20:38 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+
+#include <zephyr/mit-copyright.h>
+
+#ifndef subscriptions_MODULE
+#define subscriptions_MODULE
+
+extern int zwgc_active;
+
+extern int puntable_address_p(/* string class, instance, recipient */);
+extern void punt();
+extern void unpunt();
+extern void zwgc_shutdown();
+extern void zwgc_startup();
+
+#define USRSUBS ".zephyr.subs"
+
+#endif
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: substitute.c,v 1.5 1999/01/22 23:20:38 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#if (!defined(lint) && !defined(SABER))
+static char rcsid_substitute_c[] = "$Id: substitute.c,v 1.5 1999/01/22 23:20:38 ghudson Exp $";
+#endif
+
+#include <zephyr/mit-copyright.h>
+
+#include <sysdep.h>
+#include "new_memory.h"
+#include "lexer.h"
+#include "substitute.h"
+
+/*
+ * Internal Routine:
+ *
+ * string eat_dollar_sign_stuff(string (*lookup)(string); string *text_ptr)
+ * Modifies: *text_ptr
+ * Effects: This routine deals with handling the stuff after a '$'
+ * for substitute. If *text_ptr starts with a valid
+ * variable reference (minus the leading '$'), we look up
+ * the variable using lookup and return its value.
+ * *text_ptr is also advanced past the variable reference.
+ * If a '$' starts *text_ptr, *text_ptr is advanced past it &
+ * "$" returned. (This handles "$$" -> "$") Otherwise,
+ * "$" is returned and *text_ptr is not advanced.
+ * The returned string must not be freed.
+ */
+
+static string eat_dollar_sign_stuff(lookup, text_ptr)
+ string (*lookup)();
+ string *text_ptr; /* Input/Output parameter */
+{
+ char c;
+ char closing_brace = 0;
+ char *p = *text_ptr;
+ char *variable_name_start;
+ int variable_name_length;
+
+ /*
+ * Handle "$$" -> "$" translation:
+ */
+ c = *p;
+ if (c=='$') {
+ *text_ptr = p+1;
+ return("$");
+ }
+
+ /*
+ * If opening brace present (i.e., '(' or '{'), skip it and save away
+ * what closing brace we must see at the end of the variable reference:
+ */
+ if (c=='{') {
+ closing_brace = '}';
+ c = *++p;
+ } else if (c=='(') {
+ closing_brace = ')';
+ c = *++p;
+ }
+
+ /*
+ * Eat {identifier_char}* keeping track of what we ate:
+ */
+ variable_name_start = p;
+ variable_name_length = 0;
+ while (c = *p, is_identifier_char(c)) {
+ p++;
+ variable_name_length++;
+ }
+
+ /*
+ * If there was an opening brace, there had better be a comparable
+ * closing brace. If so, skip it. If not, we have an invalid variable
+ * reference so return '$' without advancing *text_ptr.
+ */
+ if (closing_brace) {
+ if (c==closing_brace)
+ c = *++p;
+ else
+ return("$");
+ }
+
+ /*
+ * Zero length variable names are not valid:
+ */
+ if (!variable_name_length)
+ return("$");
+
+ /*
+ * We have a valid variable reference. Advance past it then lookup
+ * its value and return it:
+ */
+ *text_ptr = p;
+ if (variable_name_length > MAX_IDENTIFIER_LENGTH)
+ variable_name_length = MAX_IDENTIFIER_LENGTH;
+ variable_name_start = string_CreateFromData(variable_name_start,
+ variable_name_length);
+ p = lookup(variable_name_start);
+ free(variable_name_start);
+ return(p);
+}
+
+/*
+ * string substitute(string (*lookup)(string); string text)
+ * Effects: returns the result of expanding all variable
+ * references in text using lookup. Example:
+ * "test $foo.$bar baz" would be translated to
+ * "text <foo>.<bar> baz" where "<foo>" is the value of
+ * lookup("foo") and "<bar>" is the value of lookup("bar").
+ * Variables are case sensitive and have the form
+ * {identifier_char}+ where identifier_char is defined
+ * in lexer.h by is_identifier_char. $(foo) and
+ * ${foo} are alternate forms for $foo. In particular,
+ * ${foo}bar is a reference to foo followed by "bar" while
+ * $foobar is a reference to foobar. Incomplete variable
+ * references like $(foo bar are displayed as if they
+ * were not variable references. To allow quoting, "$$"
+ * is translated to "$". Only the first
+ * MAX_IDENTIFIER_LENGTH characters of an identifier are
+ * significant. The strings returned by lookup are not
+ * modified in any way or freed.
+ */
+
+string substitute(lookup, text)
+ string (*lookup)();
+ string text;
+{
+ string result_so_far = string_Copy("");
+ char *p, *temp;
+
+ for (;;) {
+ /*
+ * Move [^$]* from start of text to end of result_so_far:
+ */
+ for (p=text; *p && (*p)!='$'; p++) ;
+ if (text != p) {
+ temp = string_CreateFromData(text, p-text);
+ text = p;
+ result_so_far = string_Concat2(result_so_far, temp);
+ free(temp);
+ }
+
+ /*
+ * If text now empty, exit -- the result is in result_so_far:
+ */
+ if (!*text)
+ return(result_so_far);
+
+ /*
+ * Otherwise, text begins with a '$'. Eat it then call
+ * eat_dollar_sign_stuff to process stuff after it.
+ * Append result to result_so_far, update text, & continue.
+ */
+ text++;
+ p = eat_dollar_sign_stuff(lookup, &text);
+ result_so_far = string_Concat2(result_so_far, p);
+ }
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: substitute.h,v 1.5 1999/01/22 23:20:39 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+
+#include <zephyr/mit-copyright.h>
+
+#ifndef substitute_MODULE
+#define substitute_MODULE
+
+#include "new_string.h"
+
+/*
+ * string substitute(string (*lookup)(string); string text)
+ * Effects: returns the result of expanding all variable
+ * references in text using lookup. Example:
+ * "test $foo.$bar baz" would be translated to
+ * "text <foo>.<bar> baz" where "<foo>" is the value of
+ * lookup("foo") and "<bar>" is the value of lookup("bar").
+ * Variables are case sensitive and have the form
+ * {identifier_char}+ where identifier_char is defined
+ * in lexer.h by is_identifier_char. $(foo) and
+ * ${foo} are alternate forms for $foo. In particular,
+ * ${foo}bar is a reference to foo followed by "bar" while
+ * $foobar is a reference to foobar. Incomplete variable
+ * references like $(foo bar are displayed as if they
+ * were not variable references. To allow quoting, "$$"
+ * is translated to "$". Only the first
+ * MAX_IDENTIFIER_LENGTH characters of an identifier are
+ * significant. The strings returned by lookup are not
+ * modified in any way or freed.
+ */
+
+extern string substitute();
+
+#endif
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: text_operations.c,v 1.6 1999/01/22 23:20:39 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <sysdep.h>
+
+#if (!defined(lint) && !defined(SABER))
+static const char rcsid_text_operations_c[] = "$Id: text_operations.c,v 1.6 1999/01/22 23:20:39 ghudson Exp $";
+#endif
+
+#include <zephyr/mit-copyright.h>
+
+#include "new_memory.h"
+#include "text_operations.h"
+#include "char_stack.h"
+
+string lany(text_ptr, str)
+ string *text_ptr;
+ string str;
+{
+ string result, whats_left;
+ char *p = *text_ptr;
+
+ while (*p && *str) p++, str++;
+
+ result = string_CreateFromData(*text_ptr, p - *text_ptr);
+ whats_left = string_Copy(p);
+ free(*text_ptr);
+ *text_ptr = whats_left;
+
+ return(result);
+}
+
+string lbreak(text_ptr, set)
+ string *text_ptr;
+ character_class set;
+{
+ string result, whats_left;
+ char *p = *text_ptr;
+
+ while (*p && !set[*p]) p++;
+
+ result = string_CreateFromData(*text_ptr, p - *text_ptr);
+ whats_left = string_Copy(p);
+ free(*text_ptr);
+ *text_ptr = whats_left;
+
+ return(result);
+}
+
+string lspan(text_ptr, set)
+ string *text_ptr;
+ character_class set;
+{
+ string result, whats_left;
+ char *p = *text_ptr;
+
+ while (*p && set[*p]) p++;
+
+ result = string_CreateFromData(*text_ptr, p - *text_ptr);
+ whats_left = string_Copy(p);
+ free(*text_ptr);
+ *text_ptr = whats_left;
+
+ return(result);
+}
+
+string rany(text_ptr, str)
+ string *text_ptr;
+ string str;
+{
+ string result, whats_left;
+ string text = *text_ptr;
+ char *p = text + strlen(text);
+
+ while (text<p && *str) p--, str++;
+
+ result = string_Copy(p);
+ whats_left = string_CreateFromData(text, p - text);
+ free(text);
+ *text_ptr = whats_left;
+
+ return(result);
+}
+
+string rbreak(text_ptr, set)
+ string *text_ptr;
+ character_class set;
+{
+ string result, whats_left;
+ string text = *text_ptr;
+ char *p = text + strlen(text);
+
+ while (text<p && !set[p[-1]]) p--;
+
+ result = string_Copy(p);
+ whats_left = string_CreateFromData(text, p - text);
+ free(text);
+ *text_ptr = whats_left;
+
+ return(result);
+}
+
+string rspan(text_ptr, set)
+ string *text_ptr;
+ character_class set;
+{
+ string result, whats_left;
+ string text = *text_ptr;
+ char *p = text + strlen(text);
+
+ while (text<p && set[p[-1]]) p--;
+
+ result = string_Copy(p);
+ whats_left = string_CreateFromData(text, p - text);
+ free(text);
+ *text_ptr = whats_left;
+
+ return(result);
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: text_operations.h,v 1.6 1999/01/22 23:20:40 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+
+#include <zephyr/mit-copyright.h>
+
+#ifndef text_operations_MODULE
+#define text_operations_MODULE
+
+#include "character_class.h"
+
+extern string protect();
+extern string verbatim();
+extern string stylestrip();
+extern string lany();
+extern string lbreak();
+extern string lspan();
+extern string rany();
+extern string rbreak();
+extern string rspan();
+
+#endif
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: tty_filter.c,v 1.18 1999/08/13 00:19:51 danw Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <sysdep.h>
+
+#if (!defined(lint) && !defined(SABER))
+static const char rcsid_tty_filter_c[] = "$Id: tty_filter.c,v 1.18 1999/08/13 00:19:51 danw Exp $";
+#endif
+
+#include <zephyr/mit-copyright.h>
+
+/****************************************************************************/
+/* */
+/* The tty & plain filters: */
+/* */
+/****************************************************************************/
+
+#include "new_memory.h"
+#include "new_string.h"
+#include "string_dictionary_aux.h"
+#include "formatter.h"
+#include "zwgc.h"
+#include "error.h"
+
+/***************************************************************************/
+
+extern int tgetent();
+extern char *tgetstr(),*getenv();
+short ospeed;
+char PC;
+
+/* Dictionary naming convention:
+
+ B.xxx is the termcap sequence to begin environment xxx.
+ E.xxx is the termcap sequence to end environment xxx.
+
+ */
+
+static string_dictionary termcap_dict;
+static char code_buf[10240], *code_buf_pos = code_buf, *code;
+
+/* Define the following commands:
+
+ (Hopefully) shared with all devices:
+
+ @center Guess.
+
+ @em Emphasis. User underline if available, else reverse video.
+ @bold Bold letters. If not available, reverse video, else underline.
+ @beep "bl" termcap entry, else "^G"
+
+ Other:
+
+ @blink "mb"/"me" termcap entry, else nothing.
+ @rv "so"/"se" termcap entry.
+ @u "us"/"ue" termcap entry.
+ */
+
+#define TD_SET(k,v) (string_dictionary_Define(termcap_dict, (k), &ex)->value \
+ = (v))
+#define EXPAND(k) (code = code_buf_pos, tputs(tmp, 1, tty_outc), \
+ *code_buf_pos++ = 0, TD_SET(k, code))
+
+static int
+tty_outc(c)
+ int c;
+{
+ *code_buf_pos++ = c;
+ return 0;
+}
+
+/* ARGSUSED */
+int tty_filter_init(drivername, notfirst, pargc, argv)
+char *drivername;
+char notfirst;
+int *pargc;
+char **argv;
+{
+ static char st_buf[128];
+ char tc_buf[1024], *p = st_buf, *tmp, *term;
+ int ex;
+ string_dictionary_binding *b;
+ int isrealtty = string_Eq(drivername, "tty");
+#ifdef HAVE_TERMIOS_H
+ struct termios tbuf;
+
+ ospeed = (tcgetattr(STDIN_FILENO, &tbuf) == 0) ? cfgetospeed(&tbuf) : 2400;
+#else
+ struct sgttyb sgttyb;
+
+ ospeed = (ioctl(0, TIOCGETP, &sgttyb) == 0) ? sgttyb.sg_ospeed : 2400;
+#endif
+
+ if (termcap_dict == (string_dictionary) NULL)
+ termcap_dict = string_dictionary_Create(7);
+
+ if (!(term = getenv("TERM"))) { /* Only use termcap if $TERM. */
+ if (isrealtty && !notfirst)
+ /* only complain if initializing tty mode, and would be first
+ available port */
+ ERROR("$TERM not set. tty mode will be plain.\n");
+ }
+#ifdef _AIX
+ /*
+ * This is a temporary KLUDGE to get around the problem where some people
+ * might start zwgc in their ~/.startup.X and it hangs on the RISC/6000.
+ * Apparently, the call to tgetent() with the Athena console window causes
+ * the process to get stopped on tty access. Since the terminal type is
+ * "dumb" (set by tcsh), we can pretty much assume there isn't anything
+ * to setup from the termcap information.
+ */
+ else if (!strcmp(term, "dumb")) { }
+#endif
+ else {
+ tgetent(tc_buf, term);
+
+ /* Step 1: get all of {rv,bold,u,bell,blink} that are available. */
+
+ /* We cheat here, and ignore the padding (if any) specified for
+ the mode-change strings (it's a real pain to do "right") */
+
+ tmp = tgetstr("pc", &p);
+ PC = (tmp) ? *tmp : 0;
+ if (tmp = tgetstr("md",&p)) { /* bold ? */
+ EXPAND("B.bold");
+ tmp = tgetstr("me",&p);
+ EXPAND("E.bold");
+ }
+ if (tmp = tgetstr("mr",&p)) { /* reverse video? */
+ EXPAND("B.rw");
+ tmp = tgetstr("me",&p);
+ EXPAND("E.rw");
+ }
+ if (tmp = tgetstr("bl",&p)) { /* Bell ? */
+ EXPAND("B.bell");
+ TD_SET("E.bell", NULL);
+ }
+ if (tmp = tgetstr("mb",&p)) { /* Blink ? */
+ EXPAND("B.blink");
+ tmp = tgetstr("me",&p);
+ EXPAND("E.blink");
+ }
+ if (tmp = tgetstr("us",&p)) { /* Underline ? */
+ EXPAND("B.u");
+ tmp = tgetstr("ue",&p);
+ EXPAND("E.u");
+ }
+ if (tmp = tgetstr("so",&p)) { /* Standout ? */
+ EXPAND("B.so");
+ tmp = tgetstr("se",&p);
+ EXPAND("E.so");
+ }
+ }
+ /* Step 2: alias others to the nearest substitute */
+
+ /* Bold = so, else rv, else ul */
+ if (NULL == string_dictionary_Lookup(termcap_dict,"B.bold")) {
+ if(b = string_dictionary_Lookup(termcap_dict,"B.so")) {
+ TD_SET("B.bold",b->value);
+ TD_SET("E.bold",
+ string_dictionary_Lookup(termcap_dict,"E.so")->value);
+ } else if (b = string_dictionary_Lookup(termcap_dict,"B.rv")) {
+ TD_SET("B.bold",b->value);
+ TD_SET("E.bold",
+ string_dictionary_Lookup(termcap_dict,"E.rv")->value);
+ } else if (b = string_dictionary_Lookup(termcap_dict,"B.u")) {
+ TD_SET("B.bold",b->value);
+ TD_SET("E.bold",
+ string_dictionary_Lookup(termcap_dict,"E.u")->value);
+ }
+ }
+
+ /* Bell = ^G */
+ if (NULL == string_dictionary_Lookup(termcap_dict,"B.bell")) {
+ TD_SET("B.bell","\007");
+ TD_SET("E.bell",NULL);
+ }
+
+ /* Underline -> nothing */
+ /* Blink -> nothing */
+
+ return(0);
+}
+
+/***************************************************************************/
+
+
+
+
+static int fixed_string_eq(pattern, text, text_length)
+ string pattern;
+ char *text;
+ int text_length;
+{
+ while (*pattern && text_length>0 && *pattern == *text) {
+ pattern++;
+ text++;
+ text_length--;
+ }
+
+ return(!*pattern && !text_length);
+}
+
+typedef struct _tty_str_info {
+ struct _tty_str_info *next;
+
+ char *str;
+ int len;
+
+ char alignment; /* 'l', 'c', 'r', or ' ' to indicate newline */
+ unsigned int bold_p : 1;
+ unsigned int italic_p : 1;
+ unsigned int bell_p : 1;
+ unsigned int ignore: 1;
+} tty_str_info;
+
+static void free_info(info)
+ tty_str_info *info;
+{
+ tty_str_info *next_info;
+
+ while (info) {
+ next_info = info->next;
+ free(info);
+ info = next_info;
+ }
+}
+
+static int do_mode_change(current_mode_p, text, text_length)
+ tty_str_info *current_mode_p;
+ char *text;
+ int text_length;
+{
+ /* alignment commands: */
+ if (fixed_string_eq("left", text, text_length) ||
+ fixed_string_eq("l", text, text_length))
+ current_mode_p->alignment = 'l';
+ else if (fixed_string_eq("center", text, text_length) ||
+ fixed_string_eq("c", text, text_length))
+ current_mode_p->alignment = 'c';
+ else if (fixed_string_eq("right", text, text_length) ||
+ fixed_string_eq("r", text, text_length))
+ current_mode_p->alignment = 'r';
+
+ /* font commands: */
+ else if (fixed_string_eq("bold", text, text_length) ||
+ fixed_string_eq("b", text, text_length))
+ current_mode_p->bold_p = 1;
+ else if (fixed_string_eq("italic", text, text_length) ||
+ fixed_string_eq("i", text, text_length))
+ current_mode_p->italic_p = 1;
+ else if (fixed_string_eq("roman", text, text_length)) {
+ current_mode_p->bold_p = 0;
+ current_mode_p->italic_p = 0;
+ } else if (fixed_string_eq("beep", text, text_length)) {
+ current_mode_p->bell_p = 1;
+ return 1;
+ }
+
+ /* commands ignored in tty mode: */
+ else if (fixed_string_eq("color", text, text_length) ||
+ fixed_string_eq("font", text, text_length)) {
+ current_mode_p->ignore = 1;
+ }
+ return 0;
+}
+
+static tty_str_info *convert_desc_to_tty_str_info(desc)
+ desctype *desc;
+{
+ tty_str_info *temp;
+ tty_str_info *result = NULL;
+ tty_str_info *last_result_block = NULL;
+ int isbeep, did_beep = 0;
+
+#if !defined(SABER) && defined(__STDC__)
+ tty_str_info current_mode = { NULL, "", 0, 'l', 0 , 0, 0, 0};
+#else
+ /* This is needed due to a bug in saber, and lack of pre-ANSI support. */
+ tty_str_info current_mode;
+
+ current_mode.next = NULL;
+ current_mode.str = "";
+ current_mode.len = 0;
+ current_mode.alignment = 'l';
+ current_mode.bold_p = 0;
+ current_mode.italic_p = 0;
+ current_mode.bell_p = 0;
+ current_mode.ignore = 0;
+#endif
+
+ for (; desc->code!=DT_EOF; desc=desc->next) {
+ isbeep = 0;
+ /* Handle environments: */
+ if (desc->code == DT_ENV) {
+ /* PUSH! */
+ temp = (tty_str_info *)malloc(sizeof(struct _tty_str_info));
+ *temp = current_mode;
+ current_mode.next = temp;
+
+ isbeep = do_mode_change(¤t_mode, desc->str, desc->len);
+ if (!isbeep || did_beep)
+ continue; /* process one beep, ignore other envs */
+ } else if (desc->code == DT_END) {
+ /* POP! */
+ temp = current_mode.next;
+ current_mode = *temp;
+ free(temp);
+ continue;
+ }
+
+ /* Add new block (call it temp) to result: */
+ temp = (tty_str_info *)malloc(sizeof(struct _tty_str_info));
+ if (last_result_block) {
+ last_result_block->next = temp;
+ last_result_block = temp;
+ } else {
+ result = temp;
+ last_result_block = temp;
+ }
+
+ if (isbeep) {
+ /* special processing: need to insert a bell */
+ string_dictionary_binding *b;
+ b = string_dictionary_Lookup(termcap_dict,"B.bell");
+ if (b) {
+ temp->str = b->value;
+ temp->len = string_Length(temp->str);
+ } else
+ /* shouldn't get here! */
+ abort();
+ did_beep++;
+ continue;
+ }
+ if (desc->code == DT_STR) {
+ /* just combine string info with current mode: */
+ *temp = current_mode;
+ temp->str = desc->str;
+ temp->len = desc->len;
+ } else if (desc->code == DT_NL) {
+ /* make the new block a ' ' alignment block with an empty string */
+ temp->alignment = ' ';
+ temp->len = 0;
+ }
+ }
+
+ if (last_result_block)
+ last_result_block->next = NULL;
+
+ return(result);
+}
+
+#define max(a,b) ((a)>(b)?(a):(b))
+
+static int line_width(left_width, center_width, right_width)
+ int left_width;
+ int center_width;
+ int right_width;
+{
+ if (center_width>0) {
+ if (left_width==0 && right_width==0)
+ return(center_width);
+ return(center_width+2+max(left_width,right_width)*2);
+ } else {
+ if (left_width && right_width)
+ return(1+left_width+right_width);
+ else
+ return(left_width+right_width);
+ }
+}
+
+static int calc_max_line_width(info)
+ tty_str_info *info;
+{
+ int max_line_width = 0;
+ int left = 0;
+ int center = 0;
+ int right = 0;
+
+ for (; info; info=info->next) {
+ if (info->ignore)
+ continue;
+ switch (info->alignment) {
+ case 'l':
+ left += info->len;
+ break;
+
+ case 'c':
+ center += info->len;
+ break;
+
+ case 'r':
+ right += info->len;
+ break;
+
+ case ' ':
+#ifdef DEBUG
+ if (zwgc_debug)
+ printf("width: %d %d %d = %d\n", left, center, right,
+ line_width(left, center, right));
+#endif
+ max_line_width = max(max_line_width,
+ line_width(left, center, right));
+ left = center = right = 0;
+ break;
+ }
+ }
+
+#ifdef DEBUG
+ if (zwgc_debug)
+ printf("width: %d %d %d = %d\n", left, center, right,
+ line_width(left, center, right));
+#endif
+ max_line_width = max(max_line_width,
+ line_width(left, center, right));
+
+ return(max_line_width);
+}
+
+string tty_filter(text, use_fonts)
+ string text;
+ int use_fonts;
+{
+ string text_copy = string_Copy(text);
+ string result_so_far = string_Copy("");
+ desctype *desc;
+ int number_of_strs;
+ int number_of_lines;
+ tty_str_info *info, *info_head;
+ int max_line_width;
+
+ desc = disp_get_cmds(text_copy, &number_of_strs, &number_of_lines);
+ info_head = info = convert_desc_to_tty_str_info(desc);
+ free_desc(desc);
+
+#ifdef DEBUG
+ if (zwgc_debug)
+ { tty_str_info *ptr;
+ for (ptr=info; ptr; ptr=ptr->next) {
+ printf("%c: %s %s %s <%s>\n", ptr->alignment,
+ ptr->bold_p ? "(bold)" : "",
+ ptr->italic_p ? "(italic)" : "",
+ ptr->bell_p ? "(bell)" : "",
+ string_CreateFromData(ptr->str, ptr->len));
+ }
+ }
+#endif
+
+ max_line_width = calc_max_line_width(info);
+ dprintf1("max width = %d\n", max_line_width);
+
+ while (info) {
+ string left, center, right;
+ int left_width, center_width, right_width;
+ char *temp;
+
+ left_width = center_width = right_width = 0;
+ left = string_Copy("");
+ center = string_Copy("");
+ right = string_Copy("");
+
+ for (; info && info->alignment!=' '; info=info->next) {
+ string item;
+
+ if (info->ignore)
+ continue;
+
+ item = string_Copy("");
+
+ if (info->bold_p && use_fonts) {
+ if (temp = string_dictionary_Fetch(termcap_dict, "B.bold"))
+ item = string_Concat2(item, temp);
+ } else if (info->italic_p && use_fonts) {
+ if (temp = string_dictionary_Fetch(termcap_dict, "B.u"))
+ item = string_Concat2(item, temp);
+ }
+ temp = string_CreateFromData(info->str, info->len);
+ item = string_Concat2(item, temp);
+ free(temp);
+
+ if (info->bold_p && use_fonts) {
+ if (temp = string_dictionary_Fetch(termcap_dict, "E.bold"))
+ item = string_Concat2(item, temp);
+ } else if (info->italic_p && use_fonts) {
+ if (temp = string_dictionary_Fetch(termcap_dict, "E.u"))
+ item = string_Concat2(item, temp);
+ }
+
+ switch (info->alignment) {
+ default:
+ case 'l':
+ left = string_Concat2(left, item);
+ left_width += info->len;
+ break;
+
+ case 'c':
+ center = string_Concat2(center, item);
+ center_width += info->len;
+ break;
+
+ case 'r':
+ right = string_Concat2(right, item);
+ right_width += info->len;
+ break;
+ }
+ free(item);
+ }
+
+ result_so_far = string_Concat2(result_so_far, left);
+ if (center_width)
+ while (left_width < (max_line_width-center_width)/2 ) {
+ result_so_far = string_Concat2(result_so_far, " ");
+ left_width++;
+ }
+ result_so_far = string_Concat2(result_so_far, center);
+ left_width += center_width;
+
+ if (right_width)
+ while (left_width<max_line_width-right_width) {
+ result_so_far = string_Concat2(result_so_far, " ");
+ left_width++;
+ }
+ result_so_far = string_Concat2(result_so_far, right);
+ free(left); free(center); free(right);
+
+ if (info && info->alignment == ' ') {
+ info = info->next;
+ result_so_far = string_Concat2(result_so_far, "\r\n");
+ }
+ }
+
+ free_info(info_head);
+ free(text_copy);
+ if (number_of_lines &&
+ (result_so_far[string_Length(result_so_far)-1] != '\n'))
+ /* CRLF-terminate all results */
+ result_so_far = string_Concat2(result_so_far, "\r\n");
+ return(result_so_far);
+}
--- /dev/null
+/* $Id: unsigned_long.h,v 1.2 1999/01/22 23:20:41 ghudson Exp $
+ */
+
+#define unsigned_long unsigned long
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: variables.c,v 1.5 1999/01/22 23:20:42 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <sysdep.h>
+
+#if (!defined(lint) && !defined(SABER))
+static const char rcsid_variables_c[] = "$Id: variables.c,v 1.5 1999/01/22 23:20:42 ghudson Exp $";
+#endif
+
+#include <zephyr/mit-copyright.h>
+
+/****************************************************************************/
+/* */
+/* Module containing code to deal with description langauge variables: */
+/* */
+/****************************************************************************/
+
+#include "new_memory.h"
+#include "notice.h"
+#include "string_dictionary_aux.h"
+#include "variables.h"
+
+/*
+ * fields_data[_length] - these point to the field data that the number
+ * variables were last set to using
+ * var_set_number_variables_to_fields.
+ */
+
+static char *fields_data;
+static int fields_data_length = 0;
+
+/*
+ * [non_]number_variable_dict - contains the values of all the [non-]number
+ * variables that have been set since the last
+ * var_clear_all_variables call or (for numbers
+ * only) var_set_number_variables_to_fields call.
+ */
+
+static string_dictionary non_number_variable_dict = NULL;
+static string_dictionary number_variable_dict = NULL;
+
+/*
+ * Internal Routine:
+ *
+ * int is_digits(string text)
+ * Effects: Returns true iff text matches [0-9]*. ("" matches...)
+ */
+
+static int is_digits(text)
+ string text;
+{
+ for (; *text; text++)
+ if (!isdigit(*text))
+ return(0);
+
+ return(1);
+}
+
+/*
+ * Internal Routine:
+ *
+ * int is_number_variable(string text)
+ * Effects: Returns true iff text matches [0-9]+.
+ */
+
+#define is_number_variable(x) (isdigit(*(x)) && is_digits((x)))
+
+/*
+ * void var_clear_all_variables()
+ * Requires: This routine must be called before any other
+ * var module routine is called.
+ * Modifies: All description language variables
+ * Effects: Sets all description langauge variables to "".
+ */
+
+void var_clear_all_variables()
+{
+ if (non_number_variable_dict) {
+ string_dictionary_SafeDestroy(non_number_variable_dict);
+ string_dictionary_SafeDestroy(number_variable_dict);
+ }
+
+ non_number_variable_dict = string_dictionary_Create(101);
+ number_variable_dict = string_dictionary_Create(11);
+ fields_data_length = 0;
+}
+
+/*
+ * string var_get_variable(string name)
+ * Requires: var_clear_all_variables has been called
+ * Effects: Returns the value of the description langauge variable
+ * named name. The returned string is read-only and is
+ * guarenteed to last only until the next var module
+ * call. DO NOT FREE THIS STRING.
+ */
+
+string var_get_variable(name)
+ string name;
+{
+ char *result;
+ int field_to_get;
+ static string last_get_field_call_result = NULL;
+
+ if (is_number_variable(name)) {
+ if (result = string_dictionary_Fetch(number_variable_dict, name))
+ return(result);
+
+ /*
+ * Convert name to an integer avoiding overflow:
+ */
+ while (*name=='0')
+ name++;
+ if (strlen(name)>12)
+ field_to_get = 0; /* no way we'll have > 1 trillian fields... */
+ else
+ field_to_get = atoi(name);
+
+ if (!field_to_get)
+ return("");
+ if (last_get_field_call_result)
+ free(last_get_field_call_result);
+ last_get_field_call_result = get_field(fields_data,
+ fields_data_length,
+ field_to_get);
+ return(last_get_field_call_result);
+ }
+
+ if (!(result = string_dictionary_Fetch(non_number_variable_dict, name)))
+ result = "";
+
+ return(result);
+}
+
+/*
+ * void var_set_variable(string name, value)
+ * Requires: var_clear_all_variables has been called
+ * Modifies: The value of description langauge variable
+ * named name.
+ * Effects: Sets the description langauge variable named name
+ * to have the value value.
+ */
+
+void var_set_variable(name, value)
+ string name;
+ string value;
+{
+ string_dictionary_Set(is_number_variable(name) ? number_variable_dict
+ : non_number_variable_dict, name, value);
+}
+
+/*
+ * void var_set_variable_to_number(string name; int number)
+ * Requires: var_clear_all_variables has been called
+ * Modifies: The value of description langauge variable
+ * named name.
+ * Effects: Sets the description langauge variable named name
+ * to have as its value number's ascii representation.
+ */
+
+void var_set_variable_to_number(name, number)
+ string name;
+ int number;
+{
+ char buffer[20];
+
+ sprintf(buffer, "%d", number);
+ var_set_variable(name, buffer);
+}
+
+/*
+ * void var_set_variable_then_free_value(string name, value)
+ * Requires: var_clear_all_variables has been called, value is
+ * on the heap.
+ * Modifies: The value of description langauge variable
+ * named name, value
+ * Effects: Sets the description langauge variable named name
+ * to have the value value then frees value. This
+ * routine is slightly faster than calling var_set_variable
+ * then freeing value. It is provided mainly for
+ * convenience reasons.
+ */
+
+void var_set_variable_then_free_value(name, value)
+ string name;
+ string value;
+{
+ string_dictionary_binding *binding;
+ int exists;
+
+#ifdef DEBUG_MEMORY
+ if (!memory__on_heap_p(value))
+ abort(); /* <<<>>> */
+#endif
+
+ if (is_number_variable(name)) {
+ var_set_variable(name, value);
+ free(value);
+ return;
+ }
+
+ binding = string_dictionary_Define(non_number_variable_dict, name,
+ &exists);
+ if (exists)
+ free(binding->value);
+ binding->value = value;
+}
+
+/*
+ * void var_set_number_variables_to_fields(char *data, int length)
+ * Requires: var_clear_all_variables has been called
+ * Modifies: All numeric description language variables
+ * Effects: Treats data[0]..data[length-1] as a series of
+ * null-seperated fields. Sets $<number> (<number>
+ * here means [0-9]+ to field # <number> in data.
+ * Field 0 is defined to be "" as are all field #'s
+ * greater than the number of fields in data.
+ * Data[0]..data[length-1] must not be changed (or freed)
+ * until either this call is made again with different
+ * data or var_clear_all_variables is called.
+ */
+
+void var_set_number_variables_to_fields(data, length)
+ char *data;
+ int length;
+{
+ fields_data = data;
+ fields_data_length = length;
+
+ string_dictionary_SafeDestroy(number_variable_dict);
+ number_variable_dict = string_dictionary_Create(11);
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: variables.h,v 1.5 1999/01/22 23:20:42 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+
+#include <zephyr/mit-copyright.h>
+
+#ifndef var_MODULE
+#define var_MODULE
+
+#include "new_string.h"
+
+/*
+ * void var_clear_all_variables()
+ * Requires: This routine must be called before any other
+ * var module routine is called.
+ * Modifies: All description language variables
+ * Effects: Sets all description langauge variables to "".
+ */
+
+extern void var_clear_all_variables();
+
+/*
+ * string var_get_variable(string name)
+ * Requires: var_clear_all_variables has been called
+ * Effects: Returns the value of the description langauge variable
+ * named name. The returned string is read-only and is
+ * guarenteed to last only until the next var module
+ * call. DO NOT FREE THIS STRING.
+ */
+
+extern string var_get_variable();
+
+/*
+ * void var_set_variable(string name, value)
+ * Requires: var_clear_all_variables has been called
+ * Modifies: The value of description langauge variable
+ * named name.
+ * Effects: Sets the description langauge variable named name
+ * to have the value value.
+ */
+
+extern void var_set_variable();
+
+/*
+ * void var_set_variable_to_number(string name; int number)
+ * Requires: var_clear_all_variables has been called
+ * Modifies: The value of description langauge variable
+ * named name.
+ * Effects: Sets the description langauge variable named name
+ * to have as its value number's ascii representation.
+ */
+
+extern void var_set_variable_to_number();
+
+/*
+ * void var_set_variable_then_free_value(string name, value)
+ * Requires: var_clear_all_variables has been called, value is
+ * on the heap.
+ * Modifies: The value of description langauge variable
+ * named name, value
+ * Effects: Sets the description langauge variable named name
+ * to have the value value then frees value. This
+ * routine is slightly faster than calling var_set_variable
+ * then freeing value. It is provided mainly for
+ * convenience reasons.
+ */
+
+extern void var_set_variable_then_free_value();
+
+/*
+ * void var_set_number_variables_to_fields(char *data, int length)
+ * Requires: var_clear_all_variables has been called
+ * Modifies: All numeric description language variables
+ * Effects: Treats data[0]..data[length-1] as a series of
+ * null-seperated fields. Sets $<number> (<number>
+ * here means [0-9]+ to field # <number> in data.
+ * Field 0 is defined to be "" as are all field #'s
+ * greater than the number of fields in data.
+ * Data[0]..data[length-1] must not be changed (or freed)
+ * until either this call is made again with different
+ * data or var_clear_all_variables is called.
+ */
+
+extern void var_set_number_variables_to_fields();
+
+#endif
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: xcut.c,v 1.11 1999/01/22 23:20:43 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <sysdep.h>
+
+#if (!defined(lint) && !defined(SABER))
+static const char rcsid_xcut_c[] = "$Id: xcut.c,v 1.11 1999/01/22 23:20:43 ghudson Exp $";
+#endif
+
+#include <zephyr/mit-copyright.h>
+
+/****************************************************************************/
+/* */
+/* Code to deal with handling X events: */
+/* */
+/****************************************************************************/
+
+#ifndef X_DISPLAY_MISSING
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include "new_memory.h"
+#include "new_string.h"
+#include "X_gram.h"
+#include "zwgc.h"
+#include "xselect.h"
+#include "xmark.h"
+#include "error.h"
+#include "xrevstack.h"
+
+/*
+ *
+ */
+
+extern char *xmarkGetText();
+
+extern long ttl;
+
+static char *selected_text=NULL;
+static Window selecting_in = 0;
+
+char *getSelectedText()
+{
+ return(selected_text);
+}
+
+#ifdef notdef
+static string x_gram_to_string(gram)
+ x_gram *gram;
+{
+ int i, index, len;
+ int last_y = -1;
+ string temp;
+ string text_so_far = string_Copy("");
+ char *text;
+
+ text = gram->text;
+ for (i=0; i<gram->numblocks; i++) {
+ if (last_y != -1 && last_y != gram->blocks[i].y)
+ text_so_far = string_Concat2(text_so_far, "\n");
+ index = gram->blocks[i].strindex;
+ len = gram->blocks[i].strlen;
+ temp = string_CreateFromData(text+index, len);
+ text_so_far = string_Concat2(text_so_far, temp);
+ free(temp);
+ last_y = gram->blocks[i].y;
+ }
+
+ text_so_far = string_Concat2(text_so_far, "\n");
+ return(text_so_far);
+}
+#endif
+
+/*
+ *
+ */
+
+/*ARGSUSED*/
+Bool isShiftButton1(dpy,event,arg)
+ Display *dpy;
+ XEvent *event;
+ char *arg;
+{
+ return(event->xbutton.state & (ShiftMask|Button1Mask));
+}
+
+/*ARGSUSED*/
+Bool isShiftButton3(dpy,event,arg)
+ Display *dpy;
+ XEvent *event;
+ char *arg;
+{
+ return(event->xbutton.state & (ShiftMask|Button3Mask));
+}
+
+void getLastEvent(dpy,state,event)
+ Display *dpy;
+ unsigned int state;
+ XEvent *event;
+{
+ XEvent xev;
+
+ if (state & Button1Mask) {
+ while(XCheckIfEvent(dpy,&xev,isShiftButton1,NULL))
+ *event=xev;
+ } else if (state & Button3Mask) {
+ while(XCheckIfEvent(dpy,&xev,isShiftButton3,NULL))
+ *event=xev;
+ }
+}
+
+void xunmark(dpy,w,gram,desc_context)
+ Display *dpy;
+ Window w;
+ x_gram *gram;
+ XContext desc_context;
+{
+ if (gram == NULL)
+ if (XFindContext(dpy, w, desc_context, (caddr_t *) &gram))
+ return;
+
+ xmarkClear();
+ xmarkRedraw(dpy,w,gram,XMARK_REDRAW_OLD);
+}
+
+/* This is out here so xdestroygram can get at it */
+
+#define PRESSOP_NONE 0 /* nothing */
+#define PRESSOP_KILL 1 /* normal click */
+#define PRESSOP_SEL 2 /* shift left */
+#define PRESSOP_EXT 3 /* shift right */
+#define PRESSOP_NUKE 4 /* ctrl */
+#define PRESSOP_STOP 5 /* pressop cancelled by moving out of window */
+
+static int current_pressop = PRESSOP_NONE;
+
+void xdestroygram(dpy,w,desc_context,gram)
+ Display *dpy;
+ Window w;
+ XContext desc_context;
+ x_gram *gram;
+{
+ struct timeval now;
+
+ gettimeofday(&now,NULL);
+ if ((gram->can_die.tv_sec == 0) ||
+ (gram->can_die.tv_sec > now.tv_sec) ||
+ ((gram->can_die.tv_sec == now.tv_sec) &&
+ (gram->can_die.tv_usec > now.tv_usec)))
+ return;
+
+ if (w == selecting_in) {
+ selecting_in = 0;
+ xmarkClear();
+ }
+ current_pressop = PRESSOP_NONE;
+ XDeleteContext(dpy, w, desc_context);
+ XDestroyWindow(dpy, w);
+ delete_gram(gram);
+ free(gram->text);
+ free(gram->blocks);
+ free(gram);
+
+ if (bottom_gram == NULL && unlinked == NULL) {
+ /* flush colormap here */
+ }
+}
+
+void xcut(dpy,event,desc_context)
+ Display *dpy;
+ XEvent *event;
+ XContext desc_context;
+{
+ x_gram *gram;
+ Window w = event->xany.window;
+ int changedbound;
+
+ /*
+ * If event is for a window that's not ours anymore (say we're
+ * in the process of deleting it...), ignore it:
+ */
+ if (XFindContext(dpy, w, desc_context, (caddr_t *) &gram))
+ return;
+
+ /*
+ * Dispatch on the event type:
+ */
+ switch(event->type) {
+ case ClientMessage:
+ if ((event->xclient.message_type == XA_WM_PROTOCOLS) &&
+ (event->xclient.format == 32) &&
+ (event->xclient.data.l[0] == XA_WM_DELETE_WINDOW))
+ xdestroygram(dpy,w,desc_context,gram);
+ break;
+
+ case MapNotify:
+ /* I don't like using the local time, but MapNotify events don't
+ * come with a timestamp, and there's no way to query the server
+ */
+
+ if (gram->can_die.tv_sec == 0) {
+ gettimeofday(&(gram->can_die),NULL);
+ gram->can_die.tv_sec += (int) (ttl/1000);
+ gram->can_die.tv_usec += (ttl%1000)*1000;
+ }
+ break;
+
+ case UnmapNotify:
+ unlink_gram(gram);
+ break;
+
+ case LeaveNotify:
+ if (current_pressop == PRESSOP_KILL ||
+ current_pressop == PRESSOP_NUKE)
+ current_pressop = PRESSOP_STOP;
+ break;
+
+ case MotionNotify:
+ if (current_pressop == PRESSOP_SEL) {
+ /* getLastEvent(dpy,Button1Mask,event); */
+ changedbound=xmarkExtendFromFirst(gram,event->xmotion.x,
+ event->xmotion.y);
+ xmarkRedraw(dpy,w,gram,changedbound);
+ } else if (current_pressop == PRESSOP_EXT) {
+ /* getLastEvent(dpy,Button3Mask,event); */
+ changedbound=xmarkExtendFromNearest(gram,event->xmotion.x,
+ event->xmotion.y);
+ xmarkRedraw(dpy,w,gram,changedbound);
+ }
+ break;
+
+ case ButtonPress:
+ if (current_pressop != PRESSOP_NONE) {
+ current_pressop = PRESSOP_STOP;
+ } else if ( (event->xbutton.state)&ShiftMask ) {
+ if (event->xbutton.button==Button1) {
+ if (selecting_in)
+ xunmark(dpy,selecting_in,NULL,desc_context);
+ if (selected_text) free(selected_text);
+ selected_text = NULL;
+ if (! xselGetOwnership(dpy,w,event->xbutton.time)) {
+ XBell(dpy,0);
+ ERROR("Unable to get ownership of PRIMARY selection.\n");
+ selecting_in = 0;
+ current_pressop = PRESSOP_STOP;
+ } else {
+ selecting_in = w;
+ xmarkStart(gram,event->xbutton.x,event->xbutton.y);
+ current_pressop = PRESSOP_SEL;
+ }
+ } else if ((event->xbutton.button==Button3) &&
+ (w == selecting_in)) {
+ if (selected_text) free(selected_text);
+ selected_text = NULL;
+ changedbound=xmarkExtendFromNearest(gram,event->xbutton.x,
+ event->xbutton.y);
+ xmarkRedraw(dpy,w,gram,changedbound);
+ selected_text = xmarkGetText();
+ /* this is ok, since to get here, the selection must be owned */
+ current_pressop = PRESSOP_EXT;
+ }
+ } else if ( (event->xbutton.state)&ControlMask ) {
+ current_pressop = PRESSOP_NUKE;
+ } else {
+ current_pressop = PRESSOP_KILL;
+ }
+ break;
+
+ case ButtonRelease:
+ if (current_pressop == PRESSOP_KILL) {
+ xdestroygram(dpy,w,desc_context,gram);
+ } else if (current_pressop == PRESSOP_SEL ||
+ current_pressop == PRESSOP_EXT) {
+ if (selected_text) free(selected_text);
+ selected_text = xmarkGetText();
+ } else if (current_pressop == PRESSOP_NUKE) {
+ XWindowAttributes wa;
+ int gx,gy;
+ Window temp;
+ x_gram *next;
+
+ for (gram = bottom_gram ; gram ; gram = next) {
+ XGetWindowAttributes(dpy,gram->w,&wa);
+ XTranslateCoordinates(dpy,gram->w,wa.root,0,0,&gx,&gy,
+ &temp);
+
+ next = gram->above;
+
+ if ((wa.map_state == IsViewable) &&
+ (gx <= event->xbutton.x_root) &&
+ (event->xbutton.x_root < gx+wa.width) &&
+ (gy <= event->xbutton.y_root) &&
+ (event->xbutton.y_root < gy+wa.height)) {
+ xdestroygram(dpy,gram->w,desc_context,gram);
+ }
+ }
+ for (gram = unlinked ; gram ; gram = next) {
+ XGetWindowAttributes(dpy,gram->w,&wa);
+ XTranslateCoordinates(dpy,gram->w,wa.root,0,0,&gx,&gy,
+ &temp);
+
+ next = gram->above;
+
+ if ((wa.map_state == IsViewable) &&
+ (gx <= event->xbutton.x_root) &&
+ (event->xbutton.x_root < gx+wa.width) &&
+ (gy <= event->xbutton.y_root) &&
+ (event->xbutton.y_root < gy+wa.height)) {
+ xdestroygram(dpy,gram->w,desc_context,gram);
+ }
+ }
+ }
+ current_pressop = PRESSOP_NONE;
+ break;
+
+ case SelectionRequest:
+ xselProcessSelection(dpy,w,event);
+ break;
+
+ case SelectionClear:
+ xselOwnershipLost(event->xselectionclear.time);
+ if (w == selecting_in) {
+ selecting_in = 0;
+ xunmark(dpy,w,gram,desc_context);
+ if (selected_text) free(selected_text);
+ selected_text = NULL;
+ }
+ break;
+
+#ifdef notdef
+ case ConfigureNotify:
+#ifdef DEBUG
+ if (zwgc_debug)
+ printf("ConfigureNotify received for wid %lx above wid %lx\n",
+ (long) w,(long) event->xconfigure.above);
+#endif
+ if (gram->above==gram) {
+ /* a new zgram. Straight to the bottom! */
+ add_to_bottom(gram);
+ } else if (event->xconfigure.above) {
+ /* some zgram was pulled to the top */
+ pull_to_top(gram);
+ } else {
+ /* Some zgram was pushed to the bottom */
+ push_to_bottom(gram);
+ }
+ /* Note that there is no option to configure a zgram to the middle */
+ break;
+#endif
+ default:
+ break;
+ }
+
+ XFlush(dpy);
+}
+
+#endif
+
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: xerror.c,v 1.5 1999/08/13 00:19:51 danw Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <sysdep.h>
+
+#if (!defined(lint) && !defined(SABER))
+static const char rcsid_xerror_c[] = "$Id: xerror.c,v 1.5 1999/08/13 00:19:51 danw Exp $";
+#endif
+
+#include <zephyr/mit-copyright.h>
+
+#ifndef X_DISPLAY_MISSING
+
+#include <X11/Xlib.h>
+#include "mux.h"
+
+int xerror_happened;
+
+/*ARGSUSED*/
+static int xerrortrap(dpy,xerrev)
+ Display *dpy;
+ XErrorEvent *xerrev;
+{
+ xerror_happened = 1;
+ return 0;
+}
+
+/*ARGSUSED*/
+void begin_xerror_trap(dpy)
+ Display *dpy;
+{
+ xerror_happened = 0;
+ XSetErrorHandler(xerrortrap);
+}
+
+void end_xerror_trap(dpy)
+ Display *dpy;
+{
+ XSync(dpy,False);
+ XSetErrorHandler(NULL);
+}
+
+#endif
+
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: xerror.h,v 1.3 1999/01/22 23:20:44 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#ifndef _XERROR_H_
+#define _XERROR_H_
+
+#if (!defined(lint) && !defined(SABER))
+static const char rcsid_xerror_h[] = "$Id: xerror.h,v 1.3 1999/01/22 23:20:44 ghudson Exp $";
+#endif
+
+#include <zephyr/mit-copyright.h>
+
+extern int xerror_happened;
+
+void begin_xerror_trap();
+void end_xerror_trap();
+
+#endif
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: xmark.c,v 1.9 1999/01/22 23:20:44 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#if (!defined(lint) && !defined(SABER))
+static char rcsid_xmark_c[] = "$Id: xmark.c,v 1.9 1999/01/22 23:20:44 ghudson Exp $";
+#endif
+
+#include <zephyr/mit-copyright.h>
+
+#include <sysdep.h>
+
+#ifndef X_DISPLAY_MISSING
+
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include "X_gram.h"
+#include "X_fonts.h"
+#include "xmark.h"
+#include "new_string.h"
+
+int markblock[3] = { -1 , -1 , -1 };
+int markchar[3] = { -1 , -1 , -1 };
+int markpixel[3] = { -1 , -1 , -1 };
+x_gram *markgram = NULL;
+
+int oldblock[2] = { -1 , -1 };
+int oldpixel[2] = { -1 , -1 };
+x_gram *oldgram = NULL;
+
+#define xmarkValid() \
+ ((markgram) && \
+ (STARTBLOCK != -1) && (ENDBLOCK != -1) && \
+ (STARTCHAR != -1) && (ENDCHAR != -1) && \
+ (STARTPIXEL != -1) && (ENDPIXEL != -1))
+
+void xmarkSetBound(gram,x,y,which)
+ x_gram *gram;
+ int x,y;
+ int which;
+{
+ int i,xofs,yofs;
+ XFontStruct *font;
+ xblock *xb;
+ unsigned char *s;
+
+#ifdef MARK_DEBUG
+#define RETURN \
+ if ((oldblock[which] != markblock[which]) || \
+ (oldpixel[which] != markpixel[which])) { \
+ printf("----- SetBound:\noldblock[%d]=%d, oldpixel[%d]=%d\nmarkblock[%d]=%d, markpixel[%d]=%d\n-----", \
+ which,oldblock[which],which,oldpixel[which], \
+ which,markblock[which],which,markpixel[which]); \
+ } \
+ return
+#else
+#define RETURN return
+#endif
+
+ if (markgram != gram) {
+ xmarkClear();
+ markgram = gram;
+ } else if (which < XMARK_TEMP_BOUND) {
+ oldblock[which]=markblock[which];
+ oldpixel[which]=markpixel[which];
+ }
+
+ /* Start at the top, fastforward to first span not too high. */
+ for (i=0,xb=gram->blocks ;
+ (i<gram->numblocks) && (xb->y2 < y) ;
+ i++,xb++) ;
+
+ /* the point is after the end */
+ if (i==gram->numblocks) {
+ markblock[which]=i;
+ markchar[which]=0;
+ markpixel[which]=0;
+ RETURN;
+ }
+
+ /* is the point before the beginning of the line? */
+ if (x <= xb->x1) {
+ markblock[which]=i;
+ markchar[which]=0;
+ markpixel[which]=0;
+ RETURN;
+ }
+
+ /* is the point in the nether space between this line and the last? */
+ if (y < xb->y1) {
+ markblock[which]=i;
+ markchar[which]=0;
+ markpixel[which]=0;
+ RETURN;
+ }
+
+ for (yofs=xb->y1;(i<gram->numblocks) && (xb->y1 == yofs);i++,xb++) {
+
+ if (x <= xb->x2) {
+ markblock[which]=i;
+
+ xofs=xb->x1;
+ if ((x < xofs) || (y < xb->y1)) {
+ markchar[which]=0;
+ RETURN;
+ }
+ font=get_fontst_from_fid(xb->fid);
+ for (i=0,s=(unsigned char *)((gram->text)+(xb->strindex));
+ xofs<x && i<xb->strlen;
+ i++,s++) {
+ /* if font->per_char is NULL, then we should use min_bounds */
+ short usewidth = font->per_char ? font->per_char[*s - font->min_char_or_byte2].width : font->min_bounds.width;
+ if (x <= (xofs+=usewidth)) {
+ markchar[which]=i;
+ markpixel[which]=xofs - xb->x1 - usewidth;
+ RETURN;
+ }
+ }
+ }
+ }
+
+ /* The endpoint is after the end of the block if the loop ends */
+ markblock[which]=i;
+ markchar[which]=0;
+ markpixel[which]=0;
+ RETURN;
+}
+
+/* needs both bounds to be valid (!= -1) */
+static int xmarkNearest(x,y)
+ int x,y;
+{
+ int middle;
+
+ xmarkSetBound(markgram,x,y,XMARK_TEMP_BOUND);
+ middle=(ENDBLOCK+STARTBLOCK)/2;
+
+ if (markblock[XMARK_TEMP_BOUND] < middle)
+ return(XMARK_START_BOUND);
+ else if (markblock[XMARK_TEMP_BOUND] > middle)
+ return(XMARK_END_BOUND);
+ else {
+ middle=(ENDCHAR+STARTCHAR)/2;
+ if (markchar[XMARK_TEMP_BOUND] < middle)
+ return(XMARK_START_BOUND);
+ else
+ return(XMARK_END_BOUND);
+ }
+}
+
+void xmarkExpose(dpy,w,gram,b1,p1,b2,p2)
+ Display *dpy;
+ Window w;
+ x_gram *gram;
+ unsigned int b1,p1,b2,p2;
+{
+#define swap(x,y) temp=(x); (x)=(y); (y)=temp
+ int i,temp;
+ XEvent event;
+#define expose (event.xexpose)
+
+ if ((b1==-1) || (p1==-1) || (b2==-1) || (p2==-1)) return;
+
+ if ((b1 > b2) || ((b1 == b2) && (p1 > p2))) {
+ swap(b1,b2);
+ swap(p1,p2);
+ }
+
+#if defined(_IBMR2) && !defined(__GNUC__) && defined(RS6000_OPT_BUG)
+ /* A version of the AIX 3.1 RS/6000 C compiler needs this to prevent
+ a core dump in the loop below. */
+ &b1;
+#endif
+
+ expose.type=Expose;
+ expose.display=dpy;
+ expose.window=w;
+
+ for (i=b1;i<=b2;i++) {
+ if (b1==b2) {
+ expose.x=gram->blocks[i].x1+p1;
+ expose.y=gram->blocks[i].y1;
+ expose.width=p2-p1;
+ expose.height=gram->blocks[i].y2-gram->blocks[i].y1;
+ expose.count=0;
+ } else if (i==b1) {
+ expose.x=gram->blocks[i].x1+p1;
+ expose.y=gram->blocks[i].y1;
+ expose.width=gram->blocks[i].x2-p1;
+ expose.height=gram->blocks[i].y2-gram->blocks[i].y1;
+ expose.count=b2-i;
+ } else if (i==b2) {
+ expose.x=gram->blocks[i].x1;
+ expose.y=gram->blocks[i].y1;
+ expose.width=p2;
+ expose.height=gram->blocks[i].y2-gram->blocks[i].y1;
+ expose.count=b2-i;
+ } else {
+ expose.x=gram->blocks[i].x1;
+ expose.y=gram->blocks[i].y1;
+ expose.width=gram->blocks[i].x2-gram->blocks[i].x1;
+ expose.height=gram->blocks[i].y2-gram->blocks[i].y1;
+ expose.count=b2-i;
+ }
+
+#ifdef MARK_DEBUG
+ if (expose.width && expose.height) {
+ printf("---- markExpose:\nb1=%d p1=%d b2=%d p2=%d\n",b1,p1,b2,p2);
+ printf("x=%d y=%d w=%d h=%d\n-----",
+ expose.x,expose.y,expose.width,expose.height);
+ }
+#endif
+ if ((expose.width && expose.height) || (expose.count == 0))
+ XSendEvent(dpy,w,True,ExposureMask,&event);
+ }
+}
+
+/* Public functions: */
+
+void xmarkRedraw(dpy,w,gram,range)
+ Display *dpy;
+ Window w;
+ x_gram *gram;
+ int range;
+{
+#define ob1 ((unsigned int) oldblock[XMARK_START_BOUND])
+#define ob2 ((unsigned int) oldblock[XMARK_END_BOUND])
+#define nb1 ((unsigned int) markblock[XMARK_START_BOUND])
+#define nb2 ((unsigned int) markblock[XMARK_END_BOUND])
+#define op1 ((unsigned int) oldpixel[XMARK_START_BOUND])
+#define op2 ((unsigned int) oldpixel[XMARK_END_BOUND])
+#define np1 ((unsigned int) markpixel[XMARK_START_BOUND])
+#define np2 ((unsigned int) markpixel[XMARK_END_BOUND])
+
+ if (range==XMARK_REDRAW_CURRENT) {
+ if (!markgram) return;
+ xmarkExpose(dpy,w,gram,nb1,np1,nb2,np2);
+ } else if (range==XMARK_REDRAW_OLD) {
+ if (!oldgram) return;
+ xmarkExpose(dpy,w,gram,ob1,op1,ob2,op2);
+ } else if (range==XMARK_REDRAW_START) {
+ if (!markgram) return;
+ xmarkExpose(dpy,w,gram,ob1,op1,nb1,np1);
+ } else if (range==XMARK_REDRAW_END) {
+ if (!markgram) return;
+ xmarkExpose(dpy,w,gram,ob2,op2,nb2,np2);
+ }
+#ifdef DEBUG
+ else {
+ printf("xmarkRedraw: This shouldn't happen!\n");
+ }
+#endif
+}
+
+/* needs both bounds to be valid (!= -1) */
+int xmarkSecond()
+{
+ if (STARTBLOCK > ENDBLOCK)
+ return(XMARK_START_BOUND);
+ else if (STARTBLOCK < ENDBLOCK)
+ return(XMARK_END_BOUND);
+ else {
+ if (STARTCHAR > ENDCHAR)
+ return(XMARK_START_BOUND);
+ else if (STARTCHAR < ENDCHAR)
+ return(XMARK_END_BOUND);
+ else
+ return(XMARK_END_BOUND);
+ }
+}
+
+void xmarkClear()
+{
+ oldblock[0]=markblock[0];
+ oldblock[1]=markblock[1];
+ oldpixel[0]=markpixel[0];
+ oldpixel[1]=markpixel[1];
+ oldgram=markgram;
+
+ markblock[0] = -1;
+ markblock[1] = -1;
+ markchar[0] = -1;
+ markchar[1] = -1;
+ markpixel[0] = -1;
+ markpixel[1] = -1;
+ markgram=NULL;
+}
+
+int xmarkExtendFromFirst(gram,x,y)
+ x_gram *gram;
+ int x,y;
+{
+ if (markgram != gram) {
+ xmarkClear();
+ markgram = gram;
+ }
+
+ if (STARTBLOCK == -1) {
+ xmarkStart(gram,x,y);
+ xmarkEnd(gram,x,y);
+ return(XMARK_REDRAW_CURRENT);
+ } else if (ENDBLOCK == -1) {
+ xmarkEnd(gram,x,y);
+ return(XMARK_REDRAW_CURRENT);
+ } else {
+ xmarkSetBound(gram,x,y,XMARK_END_BOUND);
+ return(XMARK_REDRAW_END);
+ }
+}
+
+int xmarkExtendFromNearest(gram,x,y)
+ x_gram *gram;
+ int x,y;
+{
+ int bound;
+
+ if (markgram != gram) {
+ xmarkClear();
+ markgram = gram;
+ }
+
+ if (STARTBLOCK == -1) {
+ xmarkStart(gram,x,y);
+ xmarkEnd(gram,x,y);
+ return(XMARK_REDRAW_CURRENT);
+ } else if (ENDBLOCK == -1) {
+ xmarkEnd(gram,x,y);
+ return(XMARK_REDRAW_CURRENT);
+ } else {
+ xmarkSetBound(gram,x,y,bound=xmarkNearest(x,y));
+ return(bound==XMARK_START_BOUND?XMARK_REDRAW_START:XMARK_REDRAW_END);
+ }
+}
+
+char *xmarkGetText()
+{
+ int i, index, len;
+ int last_y = -1;
+ string temp;
+ string text_so_far = string_Copy("");
+ char *text = markgram->text;
+ int startblock,endblock,startchar,endchar;
+
+ if (xmarkValid()) {
+ if (xmarkSecond() == XMARK_END_BOUND) {
+ startblock=STARTBLOCK;
+ endblock=ENDBLOCK;
+ startchar=STARTCHAR;
+ endchar=ENDCHAR;
+ } else {
+ startblock=ENDBLOCK;
+ endblock=STARTBLOCK;
+ startchar=ENDCHAR;
+ endchar=STARTCHAR;
+ }
+
+ for (i=startblock; i<=endblock; i++) {
+ if (last_y != -1 && last_y != markgram->blocks[i].y)
+ text_so_far = string_Concat2(text_so_far, "\n");
+ index = markgram->blocks[i].strindex;
+ len = markgram->blocks[i].strlen;
+ if (startblock == endblock)
+ temp = string_CreateFromData(text+index+startchar,
+ endchar-startchar);
+ else if (i==startblock)
+ temp = string_CreateFromData(text+index+startchar,len-startchar);
+ else if (i==endblock)
+ temp = string_CreateFromData(text+index,endchar);
+ else
+ temp = string_CreateFromData(text+index,len);
+ text_so_far = string_Concat2(text_so_far, temp);
+ free(temp);
+ last_y = markgram->blocks[i].y;
+ }
+ }
+
+ return(text_so_far);
+}
+
+#endif /* X_DISPLAY_MISSING */
+
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: xmark.h,v 1.6 1999/01/22 23:20:45 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+
+#include <zephyr/mit-copyright.h>
+
+#ifndef _XMARK_H_
+#define _XMARK_H_
+
+#define XMARK_START_BOUND 0
+#define XMARK_END_BOUND 1
+#define XMARK_TEMP_BOUND 2
+
+#define XMARK_REDRAW_CURRENT 1
+#define XMARK_REDRAW_OLD 2
+#define XMARK_REDRAW_START 3
+#define XMARK_REDRAW_END 4
+
+#define xmarkStart(gram,x,y) xmarkSetBound(gram,x,y,XMARK_START_BOUND)
+#define xmarkEnd(gram,x,y) xmarkSetBound(gram,x,y,XMARK_END_BOUND)
+
+extern int markblock[];
+extern int markchar[];
+extern int markpixel[];
+extern x_gram *markgram;
+
+#define STARTBLOCK (markblock[XMARK_START_BOUND])
+#define ENDBLOCK (markblock[XMARK_END_BOUND])
+#define STARTCHAR (markchar[XMARK_START_BOUND])
+#define ENDCHAR (markchar[XMARK_END_BOUND])
+#define STARTPIXEL (markpixel[XMARK_START_BOUND])
+#define ENDPIXEL (markpixel[XMARK_END_BOUND])
+
+extern void xmarkSetBound();
+extern int xmarkSecond();
+extern void xmarkRedraw();
+extern void xmarkClear();
+extern int xmarkExtendFromStart();
+extern int xmarkExtendFromNearest();
+
+#endif
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: xrevstack.c,v 1.9 1999/01/22 23:20:46 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <sysdep.h>
+
+#if (!defined(lint) && !defined(SABER))
+static const char rcsid_xrevstack_c[] = "$Id: xrevstack.c,v 1.9 1999/01/22 23:20:46 ghudson Exp $";
+#endif
+
+#include <zephyr/mit-copyright.h>
+
+#ifndef X_DISPLAY_MISSING
+
+#ifndef TRUEREVSTACK
+#include "X_gram.h"
+
+x_gram *bottom_gram = NULL;
+x_gram *unlinked = NULL;
+int reverse_stack = 0;
+
+void add_to_bottom(gram)
+ x_gram *gram;
+{
+ if (bottom_gram) {
+ bottom_gram->below = gram;
+ gram->below = NULL;
+ gram->above = bottom_gram;
+ bottom_gram = gram;
+ } else {
+ gram->above = NULL;
+ gram->below = NULL;
+ bottom_gram = gram;
+ }
+}
+
+/*ARGSUSED*/
+void pull_to_top(gram)
+ x_gram *gram;
+{}
+
+/*ARGSUSED*/
+void push_to_bottom(gram)
+ x_gram *gram;
+{}
+
+void delete_gram(gram)
+ x_gram *gram;
+{
+ if (gram == bottom_gram) {
+ if (gram->above) {
+ bottom_gram = gram->above;
+ bottom_gram->below = NULL;
+ } else {
+ bottom_gram = NULL;
+ }
+ } else if (gram == unlinked) {
+ if (gram->above) {
+ unlinked = gram->above;
+ unlinked->below = NULL;
+ } else {
+ unlinked = NULL;
+ }
+ } else {
+ if (gram->above)
+ gram->above->below = gram->below;
+ gram->below->above = gram->above;
+ }
+
+ /* fix up above & below pointers so that calling delete_gram
+ again is safe */
+ gram->below = gram;
+ gram->above = gram;
+}
+
+void unlink_gram(gram)
+ x_gram *gram;
+{
+ delete_gram(gram);
+
+ if (unlinked) {
+ unlinked->below = gram;
+ gram->below = NULL;
+ gram->above = unlinked;
+ unlinked = gram;
+ } else {
+ gram->above = NULL;
+ gram->below = NULL;
+ unlinked = gram;
+ }
+}
+
+#endif
+
+#ifdef TRUEREVSTACK
+
+#include "X_gram.h"
+#include "zwgc.h"
+#include <stdio.h>
+
+x_gram *bottom_gram=NULL;
+static x_gram *top_gram=NULL;
+
+#ifdef DEBUG
+void print_gram_list(str)
+char *str;
+{
+ x_gram *gram;
+ char buf[80];
+
+ if (zwgc_debug) {
+ printf("----- From function %s: Top of tree\n",str);
+ if (top_gram)
+ if (top_gram->above)
+ printf("Tree munged: something above top_gram\n");
+ for (gram=top_gram;gram;gram=gram->below) {
+ strncpy(buf,gram->text,63);
+ buf[63]='\0';
+ printf("wid %lx txt: %s\n",(long) gram->w,buf);
+ }
+ if (bottom_gram)
+ if (bottom_gram->below)
+ printf("Tree munged: something below bottom_gram\n");
+ printf("----- Bottom of tree\n");
+ }
+}
+#endif
+
+void pull_to_top(gram)
+x_gram *gram;
+{
+ if (gram==top_gram) {
+ /* already here */
+ return;
+ } else if (top_gram==NULL) {
+ /* no grams at all. Make gram both top and bottom */
+ top_gram=gram;
+ bottom_gram=gram;
+ } else if (gram==bottom_gram) {
+ /* bottom gram is special case */
+ bottom_gram=bottom_gram->above;
+ bottom_gram->below=NULL;
+ top_gram->above=gram;
+ gram->below=top_gram;
+ top_gram=gram;
+ } else {
+ /* normal case of a gram in the middle */
+ gram->above->below=gram->below;
+ gram->below->above=gram->above;
+ top_gram->above=gram;
+ gram->below=top_gram;
+ gram->above=NULL;
+ top_gram=gram;
+ }
+#ifdef DEBUG
+ print_gram_list("pull_to_top");
+#endif
+}
+
+void push_to_bottom(gram)
+x_gram *gram;
+{
+ if (gram==bottom_gram) {
+ /* already here */
+ return;
+ } else if (bottom_gram==NULL) {
+ /* no grams at all. Make gram both top and bottom */
+ gram->above=NULL;
+ gram->below=NULL;
+ top_gram=gram;
+ bottom_gram=gram;
+ } else if (gram==top_gram) {
+ /* top gram is special case */
+ top_gram=top_gram->below;
+ top_gram->above=NULL;
+ bottom_gram->below=gram;
+ gram->above=bottom_gram;
+ bottom_gram=gram;
+ } else {
+ /* normal case of a gram in the middle */
+ gram->above->below=gram->below;
+ gram->below->above=gram->above;
+ bottom_gram->below=gram;
+ gram->above=bottom_gram;
+ gram->below=NULL;
+ bottom_gram=gram;
+ }
+#ifdef DEBUG
+ print_gram_list("push_to_bottom");
+#endif
+}
+
+void unlink_gram(gram)
+x_gram *gram;
+{
+ if (top_gram==bottom_gram) {
+ /* the only gram in the stack */
+ top_gram=NULL;
+ bottom_gram=NULL;
+ } else if (gram==top_gram) {
+ top_gram=gram->below;
+ top_gram->above=NULL;
+ } else if (gram==bottom_gram) {
+ bottom_gram=gram->above;
+ bottom_gram->below=NULL;
+ } else {
+ gram->above->below=gram->below;
+ gram->below->above=gram->above;
+ }
+#ifdef DEBUG
+ print_gram_list("unlink_gram");
+#endif
+}
+
+#ifdef notdef
+void add_to_top(gram)
+x_gram *gram;
+{
+ if (top_gram==NULL) {
+ gram->above=NULL;
+ gram->below=NULL;
+ top_gram=gram;
+ bottom_gram=gram;
+ } else {
+ top_gram->above=gram;
+ gram->above=NULL;
+ gram->below=top_gram;
+ top_gram=gram;
+ }
+#ifdef DEBUG
+ print_gram_list("add_to_top");
+#endif
+}
+#endif
+
+void add_to_bottom(gram)
+x_gram *gram;
+{
+ if (bottom_gram==NULL) {
+ gram->above=NULL;
+ gram->below=NULL;
+ top_gram=gram;
+ bottom_gram=gram;
+ } else {
+ bottom_gram->below=gram;
+ gram->above=bottom_gram;
+ gram->below=NULL;
+ bottom_gram=gram;
+ }
+#ifdef DEBUG
+ print_gram_list("add_to_bottom");
+#endif
+}
+
+#endif /* TRUEREVSTACK */
+
+#endif /* X_DISPLAY_MISSING */
+
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: xrevstack.h,v 1.5 1999/01/22 23:20:46 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#ifndef _XREVSTACK_H_
+#define _XREVSTACK_H_
+
+#if (!defined(lint) && !defined(SABER))
+static char rcsid_xrevstack_h[] = "$Id: xrevstack.h,v 1.5 1999/01/22 23:20:46 ghudson Exp $";
+#endif
+
+#include <zephyr/mit-copyright.h>
+
+extern x_gram *bottom_gram; /* for testing against NULL */
+extern x_gram *unlinked;
+extern int reverse_stack; /* is reverse stack on? */
+
+extern void add_to_bottom(/* x_gram */);
+extern void delete_gram(/* x_gram */);
+extern void unlink_gram(/* x_gram */);
+extern void pull_to_top(/* x_gram */);
+extern void push_to_bottom(/* x_gram */);
+
+#endif /* _XREVSTACK_H_ */
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: xselect.c,v 1.12 1999/01/22 23:20:47 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <sysdep.h>
+
+#if (!defined(lint) && !defined(SABER))
+static const char rcsid_xselect_c[] = "$Id: xselect.c,v 1.12 1999/01/22 23:20:47 ghudson Exp $";
+#endif
+
+#include <zephyr/mit-copyright.h>
+
+/* xselect.c - ICCCM compliant cut-and-paste */
+/* also includes some other ICCCMisms, such as the WM_PROTOCOL handling */
+
+#ifndef X_DISPLAY_MISSING
+
+#include <X11/Xlib.h>
+#include <X11/Xproto.h>
+#include <X11/Xatom.h>
+#include "new_string.h"
+#include "xselect.h"
+
+extern char *getSelectedText();
+
+static Time ownership_start = CurrentTime;
+static Time ownership_end = CurrentTime;
+Atom XA_WM_PROTOCOLS,XA_WM_DELETE_WINDOW;
+static Atom ZA_TARGETS,ZA_MULTIPLE,ZA_TIMESTAMP,ZA_ATOM_PAIR;
+
+static struct _ZAtom {
+ Atom *patom;
+ char *name;
+} ZAtom[] = {
+ {&XA_WM_PROTOCOLS,"WM_PROTOCOLS"},
+ {&XA_WM_DELETE_WINDOW,"WM_DELETE_WINDOW"},
+ {&ZA_TARGETS,"TARGETS"},
+ {&ZA_MULTIPLE,"MULTIPLE"},
+ {&ZA_TIMESTAMP,"TIMESTAMP"},
+ {&ZA_ATOM_PAIR,"ATOM_PAIR"}
+};
+#define NumZAtoms (sizeof(ZAtom)/sizeof(struct _ZAtom))
+
+/* internal static functions */
+
+static void xselNotify(dpy,selreq,property)
+ Display *dpy;
+ XSelectionRequestEvent *selreq;
+ Atom property;
+{
+ XSelectionEvent ev;
+
+ ev.type=SelectionNotify;
+ ev.requestor=selreq->requestor;
+ ev.selection=selreq->selection;
+ ev.target=selreq->target;
+ ev.property=property;
+ ev.time=selreq->time;
+
+ XSendEvent(dpy,ev.requestor,False,0,(XEvent *) &ev);
+}
+
+/* pRequestAtoms and RequestAtoms should have the same size. */
+static Atom *pRequestAtoms[] = {
+ &ZA_TARGETS,&ZA_MULTIPLE,&ZA_TIMESTAMP,NULL
+};
+static Atom RequestAtoms[] = {
+ None,None,None,XA_STRING
+};
+#define NumRequestAtoms (sizeof(RequestAtoms)/sizeof(Atom))
+#define PROP(prop,targ) ((prop)!=None?(prop):(targ))
+#define ChangeProp(type,format,data,size) \
+ XChangeProperty(dpy,w,PROP(property,target),(type),(format), \
+ PropModeReplace, (unsigned char *) (data),(size))
+
+static void xselSetProperties(dpy,w,property,target,selreq)
+ Display *dpy;
+ Window w;
+ Atom property,target;
+ XSelectionRequestEvent *selreq;
+{
+ if (target==ZA_TARGETS) {
+
+ ChangeProp(XA_ATOM,32,RequestAtoms,NumRequestAtoms);
+ XSync(dpy,0);
+ } else if (target==ZA_MULTIPLE) {
+ Atom atype;
+ int aformat;
+ Atom *alist;
+ unsigned long alistsize,i;
+
+ XGetWindowProperty(dpy,w,property,0L,0L,False,ZA_ATOM_PAIR,&atype,
+ &aformat,&i,&alistsize,(unsigned char **) &alist);
+
+ if (alistsize)
+ XGetWindowProperty(dpy,w,property,0L,alistsize/sizeof(Atom),False,
+ ZA_ATOM_PAIR,&atype,&aformat,&alistsize,&i,
+ (unsigned char **) &alist);
+
+ alistsize/=(sizeof(Atom)/4);
+ for (i=0;i<alistsize;i+=2)
+ xselSetProperties(dpy,w,alist[i+1],alist[i],selreq);
+
+ XFree((char *) alist);
+ } else if (target==ZA_TIMESTAMP) {
+ ChangeProp(XA_INTEGER,32,&ownership_start,1);
+ XSync(dpy,0);
+ } else if (target==XA_STRING) {
+ char *selected;
+
+ if (selected = getSelectedText()) {
+ ChangeProp(XA_STRING,8,selected,string_Length(selected));
+ } else {
+ /* This should only happen if the pasting client is out of
+ spec (or if this program is buggy), but it could happen */
+#ifdef DEBUG
+ fprintf(stderr,
+ "SelectionRequest event received for unowned selection: requestor wid=0x%x", w);
+#endif
+ ChangeProp(XA_STRING,8,"",0);
+ }
+ XSync(dpy,0);
+ }
+
+ xselNotify(dpy,selreq,property);
+}
+
+/* global functions */
+
+void xicccmInitAtoms(dpy)
+ Display *dpy;
+{
+ int i;
+
+ for (i=0;i<NumZAtoms;i++)
+ *(ZAtom[i].patom)=XInternAtom(dpy,ZAtom[i].name,False);
+ for (i=0;i<NumRequestAtoms;i++)
+ if (pRequestAtoms[i])
+ RequestAtoms[i] = *(pRequestAtoms[i]);
+}
+
+int xselGetOwnership(dpy,w,time)
+ Display *dpy;
+ Window w;
+ Time time;
+{
+ int temp;
+
+ XSetSelectionOwner(dpy,XA_PRIMARY,w,time);
+ temp=(w == XGetSelectionOwner(dpy,XA_PRIMARY));
+
+ if (temp)
+ ownership_start = time;
+
+ return(temp);
+}
+
+/* Get the selection. Return !0 if success, 0 if fail */
+int xselProcessSelection(dpy,w,event)
+ Display *dpy;
+ Window w;
+ XEvent *event;
+{
+ XSelectionRequestEvent *selreq = &(event->xselectionrequest);
+
+#ifdef DEBUG
+ if ((selreq->owner != w) || (selreq->selection != XA_PRIMARY))
+ fprintf(stderr,"SelectionRequest event has bogus field values\n");
+#endif
+
+ if ((ownership_start == CurrentTime) ||
+ ((selreq->time != CurrentTime) &&
+ (selreq->time < ownership_start) ||
+ ((ownership_end != CurrentTime) &&
+ (ownership_end > ownership_start) &&
+ (selreq->time > ownership_end))))
+ xselNotify(dpy,selreq,None);
+ else
+ xselSetProperties(dpy,selreq->requestor,selreq->property,selreq->target,
+ selreq);
+
+ return(1);
+}
+
+void xselOwnershipLost(time)
+ Time time;
+{
+ ownership_end = time;
+}
+
+/*ARGSUSED*/
+void xselGiveUpOwnership(dpy,w)
+ Display *dpy;
+ Window w;
+{
+ XSetSelectionOwner(dpy,XA_PRIMARY,None,ownership_start);
+
+ ownership_end=ownership_start; /* Is this right? what should I use? */
+}
+
+#endif /* X_DISPLAY_MISSING */
+
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: xselect.h,v 1.6 1999/01/22 23:20:47 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+
+#include <zephyr/mit-copyright.h>
+
+#ifndef _XSELECT_H_
+#define _XSELECT_H_
+
+extern void xicccmInitAtoms();
+extern int xselGetOwnership();
+extern int xselProcessSelection();
+extern void xselOwnershipLost();
+extern void xselGiveUpOwnership();
+
+extern int xwmprotoDelete();
+
+extern Atom XA_WM_PROTOCOLS,XA_WM_DELETE_WINDOW;
+
+#endif
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: xshow.c,v 1.15 1999/08/13 00:19:52 danw Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <sysdep.h>
+
+#if (!defined(lint) && !defined(SABER))
+static const char rcsid_xshow_c[] = "$Id: xshow.c,v 1.15 1999/08/13 00:19:52 danw Exp $";
+#endif
+
+#include <zephyr/mit-copyright.h>
+
+#ifndef X_DISPLAY_MISSING
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xresource.h>
+#include "pointer_dictionary.h"
+#include "new_memory.h"
+#include "formatter.h"
+#include "variables.h"
+#include "zwgc.h"
+#include "X_fonts.h"
+#include "X_gram.h"
+#include "xmode_stack.h"
+
+#define max(a,b) ((a)>(b)?(a):(b))
+
+XContext desc_context;
+static pointer_dictionary colorname_dict = NULL;
+
+extern int internal_border_width;
+extern unsigned long default_bgcolor;
+extern unsigned long default_fgcolor;
+extern unsigned long x_string_to_color();
+
+void
+xshowinit()
+{
+ desc_context = XUniqueContext();
+}
+
+struct res_dict_type {
+ pointer_dictionary dict;
+ char * resname_suffix;
+ char * resclass;
+};
+
+static char *xres_get_resource (restype, style)
+ struct res_dict_type *restype;
+ char *style;
+{
+ char *desc;
+ pointer_dictionary_binding *binding;
+ int exists;
+ char *value;
+
+ desc=string_Concat("style.", style);
+ desc=string_Concat2(desc, restype->resname_suffix);
+
+ if (!restype->dict)
+ restype->dict = pointer_dictionary_Create(37);
+ binding = pointer_dictionary_Define(restype->dict, desc, &exists);
+
+ if (exists) {
+ free(desc);
+ return((string) binding->value);
+ } else {
+ value=get_string_resource(desc, restype->resclass);
+ free(desc);
+ if (value==NULL)
+ pointer_dictionary_Delete(restype->dict, binding);
+ else
+ binding->value=(pointer) value;
+ return value; /* If resource returns NULL, return NULL also */
+ }
+}
+
+static struct res_dict_type geometry_resources = {
+ NULL, ".geometry", "StyleKey.Style1.Style2.Style3.GeometryKey",
+};
+
+static struct res_dict_type bgcolor_resources = {
+ NULL, ".background", "StyleKey.Style1.Style2.Style3.BackgroundKey",
+};
+
+#define xres_get_geometry(style) xres_get_resource(&geometry_resources,style)
+#define xres_get_bgcolor(style) xres_get_resource(&bgcolor_resources,style)
+
+static struct res_dict_type fgcolor_resources = {
+ NULL, ".foreground",
+ "StyleKey.Style1.Style2.Style3.SubstyleKey.Substyle.ForegroundKey",
+};
+
+/*ARGSUSED*/
+char *mode_to_colorname (dpy, style, mode)
+ Display *dpy;
+ char *style;
+ xmode *mode;
+{
+ char *desc, *result;
+
+ desc = string_Concat (style, ".substyle.");
+ desc = string_Concat2 (desc, mode->substyle);
+ result = xres_get_resource (&fgcolor_resources, desc);
+ free (desc);
+ return result;
+}
+
+void fixup_and_draw(dpy, style, auxblocks, blocks, num, lines, numlines,
+ beepcount)
+ Display *dpy;
+ char *style;
+ xblock *blocks;
+ xauxblock *auxblocks;
+ int num;
+ xlinedesc *lines;
+ int numlines;
+ int beepcount;
+{
+ int gram_xalign = 1;
+ int gram_yalign = 1;
+ int gram_xpos, gram_ypos, gram_xsize, gram_ysize;
+
+ x_gram *gram;
+ int strindex = 0;
+
+ int line, block=0;
+ int maxwidth=0, chars=0, maxascent, maxdescent;
+ int ssize, lsize,csize, rsize, width;
+ int i, ascent, descent;
+
+ int yofs = internal_border_width;
+ int lofs, cofs, rofs;
+ int ystart,yend;
+
+ char *bgstr, *geometry, xpos[10], ypos[10], xfrom, yfrom;
+
+ gram = (x_gram *)malloc(sizeof(x_gram));
+
+ /* Find total lengths of left, center, and right parts. Also find the
+ length of the longest line and the total number of characters. */
+
+ for (line=0; line<numlines; line++) {
+ lsize = csize = rsize = 0;
+ maxascent = maxdescent = 0;
+
+ /* add up sizes for each block, get max ascent and descent */
+
+ for (i=0; i<lines[line].numblock; i++,block++) {
+ chars += auxblocks[block].len;
+ ssize = XTextWidth(auxblocks[block].font, auxblocks[block].str,
+ auxblocks[block].len);
+ auxblocks[block].width = ssize;
+ ascent = auxblocks[block].font->ascent;
+ descent = auxblocks[block].font->descent;
+ if (ascent>maxascent)
+ maxascent = ascent;
+ if (descent>maxdescent)
+ maxdescent = descent;
+ switch (auxblocks[block].align) {
+ case LEFTALIGN:
+ lsize += ssize;
+ break;
+
+ case CENTERALIGN:
+ csize += ssize;
+ break;
+
+ case RIGHTALIGN:
+ rsize += ssize;
+ break;
+ }
+ }
+
+ /* save what we need to do size fixups */
+
+ if (maxascent>lines[line].ascent)
+ lines[line].ascent = maxascent;
+ if (maxdescent>lines[line].descent)
+ lines[line].descent = maxdescent;
+ lines[line].lsize = lsize;
+ lines[line].csize = csize;
+ lines[line].rsize = rsize;
+
+ /* get width of line and see if it is bigger than the max width */
+
+ switch ((lsize?1:0)+(csize?2:0)+(rsize?4:0)) {
+#ifdef DEBUG
+ default:
+ abort();
+#endif
+
+ case 0:
+ width = 0;
+ break;
+
+ case 1:
+ width = lsize;
+ break;
+
+ case 2:
+ width = csize;
+ break;
+
+ case 3:
+ /* in all these cases, we just want to add the width of *any*
+ space, so the first font will do just fine. */
+ /* XXX implicit assumption that a line must have at least one
+ block, so that there is indeed a reasonable font in
+ auxblocks[0].font */
+ width = lsize*2+csize+XTextWidth(auxblocks[0].font," ",1);
+ break;
+
+ case 4:
+ width = rsize;
+ break;
+
+ case 5:
+ width = lsize+rsize+XTextWidth(auxblocks[0].font, " ", 1);
+ break;
+
+ case 6:
+ width = csize+rsize*2+XTextWidth(auxblocks[0].font, " ", 1);
+ break;
+
+ case 7:
+ width = max(lsize,rsize)*2+csize+
+ XTextWidth(auxblocks[0].font," ",1)*2;
+ break;
+ }
+ if (width>maxwidth)
+ maxwidth = width;
+ }
+
+ /* fixup x,y for each block, create big string and indices into it */
+ /* set x1,y1,x2,y2 of each block also. */
+
+ gram->text = (char *)malloc(chars);
+ block = 0;
+
+ for (line=0; line<numlines; line++) {
+ lofs = internal_border_width;
+ cofs = ((maxwidth-lines[line].csize)>>1) + internal_border_width;
+ rofs = maxwidth-lines[line].rsize + internal_border_width;
+ ystart = yofs;
+ yofs += lines[line].ascent;
+ yend = yofs+lines[line].descent+1; /* +1 because lines look scrunched
+ without it. */
+
+ for (i=0; i<lines[line].numblock; i++,block++) {
+ blocks[block].fid = auxblocks[block].font->fid;
+ switch (auxblocks[block].align) {
+ case LEFTALIGN:
+ blocks[block].x = lofs;
+ blocks[block].x1 = lofs;
+ lofs += auxblocks[block].width;
+ blocks[block].x2 = lofs;
+ break;
+
+ case CENTERALIGN:
+ blocks[block].x = cofs;
+ blocks[block].x1 = cofs;
+ cofs += auxblocks[block].width;
+ blocks[block].x2 = cofs;
+ break;
+
+ case RIGHTALIGN:
+ blocks[block].x = rofs;
+ blocks[block].x1 = rofs;
+ rofs += auxblocks[block].width;
+ blocks[block].x2 = rofs;
+ break;
+ }
+ blocks[block].y = yofs;
+ blocks[block].y1 = ystart;
+ blocks[block].y2 = yend;
+ blocks[block].strindex = strindex;
+ blocks[block].strlen = auxblocks[block].len;
+ strncpy(gram->text+strindex, auxblocks[block].str,
+ auxblocks[block].len);
+ strindex += blocks[block].strlen;
+ }
+
+ yofs = yend;
+
+ }
+
+ if ((geometry = var_get_variable("X_geometry")),(geometry[0]=='\0'))
+ if ((geometry = xres_get_geometry(style))==NULL)
+ if ((geometry = var_get_variable("default_X_geometry")),
+ (geometry[0]=='\0'))
+ geometry = "+0+0";
+ sscanf(geometry, "%c%[0123456789c]%c%[0123456789c]", &xfrom, xpos,
+ &yfrom, ypos);
+
+ if (xpos[0]=='c') {
+ gram_xalign = 0;
+ gram_xpos = 0;
+ } else
+ gram_xpos = atoi(xpos);
+ if (xfrom=='-')
+ gram_xalign *= -1;
+
+ if (ypos[0]=='c') {
+ gram_yalign = 0;
+ gram_ypos = 0;
+ } else
+ gram_ypos = atoi(ypos);
+ if (yfrom=='-')
+ gram_yalign *= -1;
+
+ if ((bgstr = var_get_variable("X_background")),(bgstr[0]=='\0'))
+ if ((bgstr = xres_get_bgcolor(style))==NULL)
+ if ((bgstr = var_get_variable("default_X_background")),
+ (bgstr[0]=='\0'))
+ gram->bgcolor = default_bgcolor;
+ if (bgstr && bgstr[0])
+ gram->bgcolor = x_string_to_color(bgstr,default_bgcolor);
+
+
+ gram_xsize = maxwidth+(internal_border_width<<1);
+ gram_ysize = yofs+internal_border_width;
+ gram->numblocks = num;
+ gram->blocks = blocks;
+
+ x_gram_create(dpy, gram, gram_xalign, gram_yalign, gram_xpos,
+ gram_ypos, gram_xsize, gram_ysize, beepcount);
+}
+
+/* Silly almost-but-not-quite-useless helper function */
+char *no_dots_downcase_var(str)
+ char *str;
+{
+ register char *var, *var2;
+
+ var = string_Downcase(var_get_variable(str));
+ var2 = var;
+ while (*var++)
+ if (*var == '.')
+ *var = '_';
+ return(var2);
+}
+
+#define MODE_TO_FONT(dpy,style,mode) \
+ get_font((dpy),(style),(mode)->font?(mode)->font:(mode)->substyle, \
+ (mode)->size, (mode)->bold+(mode)->italic*2)
+void xshow(dpy, desc, numstr, numnl)
+ Display *dpy;
+ desctype *desc;
+ int numstr;
+ int numnl;
+{
+ XFontStruct *font;
+ xmode_stack modes = xmode_stack_create();
+ xmode curmode;
+ xlinedesc *lines;
+ xblock *blocks;
+ xauxblock *auxblocks;
+ int nextblock=0;
+ int line=0,linestart=0;
+ char *style;
+ int free_style = 0;
+ int beepcount = 0;
+
+ lines = (xlinedesc *)malloc(sizeof(xlinedesc)*(numnl+1));
+
+ blocks = (xblock *)malloc(sizeof(xblock)*numstr);
+ auxblocks = (xauxblock *)malloc(sizeof(xauxblock)*numstr);
+
+ curmode.bold = 0;
+ curmode.italic = 0;
+ curmode.size = MEDIUM_SIZE;
+ curmode.align = LEFTALIGN;
+ curmode.expcolor = 0;
+ curmode.substyle = string_Copy("default");
+ curmode.font = NULL;
+
+ style = var_get_variable("style");
+ if (style[0] == '\0') {
+ style = string_Concat(no_dots_downcase_var("class"),".");
+ style = string_Concat2(style,no_dots_downcase_var("instance"));
+ style = string_Concat2(style,".");
+ style = string_Concat2(style,no_dots_downcase_var("sender"));
+ string_Downcase(style);
+ free_style = 1;
+ }
+
+ for (; desc->code!=DT_EOF; desc=desc->next) {
+ switch (desc->code) {
+ case DT_ENV:
+ xmode_stack_push(modes, curmode);
+ curmode.substyle = string_Copy(curmode.substyle);
+ if (curmode.font)
+ curmode.font = string_Copy(curmode.font);
+
+#define envmatch(string,length) ((desc->len==(length)) && (strncasecmp(desc->str,(string),(length))==0))
+
+ if (envmatch("roman",5)) {
+ curmode.bold = 0;
+ curmode.italic = 0;
+ } else if (envmatch("bold",4) || envmatch("b",1))
+ curmode.bold = 1;
+ else if (envmatch("italic",6)||envmatch("i",1))
+ curmode.italic = 1;
+ else if (envmatch("large",5))
+ curmode.size = LARGE_SIZE;
+ else if (envmatch("medium",6))
+ curmode.size = MEDIUM_SIZE;
+ else if (envmatch("small",5))
+ curmode.size = SMALL_SIZE;
+ else if (envmatch("left",4)||envmatch("l",1))
+ curmode.align = LEFTALIGN;
+ else if (envmatch("center",6)||envmatch("c",1))
+ curmode.align = CENTERALIGN;
+ else if (envmatch("right",5)||envmatch("r",1))
+ curmode.align = RIGHTALIGN;
+ else if (envmatch("beep",4))
+ beepcount++;
+ else if (envmatch("font",4)) {
+ /* lookahead needed. desc->next->str should be the
+ font name, and desc->next->next->code should be
+ a DT_END*/
+ if ((desc->next) &&
+ (desc->next->next) &&
+ (desc->next->code == DT_STR) &&
+ (desc->next->next->code==DT_END)) {
+
+ /* Since @font mutates the current environment, we have
+ to pop the environment that this case usually pushes */
+ free(curmode.substyle);
+ curmode = xmode_stack_top(modes);
+ xmode_stack_pop(modes);
+
+ /* mutating... */
+ curmode.size=SPECIAL_SIZE; /* This is an @font() */
+ curmode.font=string_CreateFromData(desc->next->str,
+ desc->next->len);
+ /* skip over the rest of the @font */
+ desc=desc->next->next;
+ }
+ } else if (envmatch("color",5)) {
+ /* lookahead needed. desc->next->str should be the
+ font name, and desc->next->next->code should be
+ a DT_END*/
+ if ((desc->next) &&
+ (desc->next->next) &&
+ (desc->next->code == DT_STR) &&
+ (desc->next->next->code==DT_END)) {
+ char *colorname;
+
+ /* Since @font mutates the current environment, we have
+ to pop the environment that this case usually pushes */
+ free(curmode.substyle);
+ curmode = xmode_stack_top(modes);
+ xmode_stack_pop(modes);
+
+ /* mutating... */
+ colorname=string_CreateFromData(desc->next->str,
+ desc->next->len);
+ curmode.color = x_string_to_color(colorname,default_fgcolor);
+ free(colorname);
+ curmode.expcolor = 1;
+ /* skip over the rest of the @font */
+ desc=desc->next->next;
+ }
+ } else if (desc->len > 0) { /* avoid @{...} */
+ free(curmode.substyle);
+ if (curmode.font) {
+ free(curmode.font);
+ curmode.font = NULL;
+ }
+ curmode.substyle = string_CreateFromData(desc->str, desc->len);
+ }
+ break;
+
+ case DT_STR:
+ auxblocks[nextblock].align = curmode.align;
+ auxblocks[nextblock].font = MODE_TO_FONT(dpy,style,&curmode);
+ auxblocks[nextblock].str = desc->str;
+ auxblocks[nextblock].len = desc->len;
+ if (curmode.expcolor)
+ blocks[nextblock].fgcolor = curmode.color;
+ else
+ blocks[nextblock].fgcolor =
+ x_string_to_color(mode_to_colorname(dpy,style,&curmode),
+ default_fgcolor);
+ nextblock++;
+ break;
+
+ case DT_END:
+ free(curmode.substyle);
+ curmode = xmode_stack_top(modes);
+ xmode_stack_pop(modes);
+ break;
+
+ case DT_NL:
+ lines[line].startblock = linestart;
+ lines[line].numblock = nextblock-linestart;
+ font = MODE_TO_FONT(dpy,style,&curmode);
+ lines[line].ascent = font->ascent;
+ lines[line].descent = font->descent;
+ line++;
+ linestart = nextblock;
+ break;
+ }
+ }
+
+ /* case DT_EOF: will drop through to here. */
+
+ if (linestart != nextblock) {
+ lines[line].startblock = linestart;
+ lines[line].numblock = nextblock-linestart;
+ font = MODE_TO_FONT(dpy,style,&curmode);
+ lines[line].ascent = 0;
+ lines[line].descent = 0;
+ line++;
+ }
+
+ free(curmode.substyle);
+ fixup_and_draw(dpy, style, auxblocks, blocks, nextblock, lines, line,
+ beepcount);
+ free(lines);
+ free(auxblocks);
+ if (free_style)
+ free(style);
+}
+
+static void xhandleevent(dpy, w, event)
+ Display *dpy;
+ Window w;
+ XEvent *event;
+{
+ x_gram *gram;
+
+ if (XFindContext(dpy, w, desc_context, (caddr_t *)&gram))
+ return;
+
+ if (event->type == Expose)
+ x_gram_expose(dpy, w, gram,&(event->xexpose));
+ else
+ xcut(dpy, event, desc_context);
+
+ XFlush(dpy);
+}
+
+void x_get_input(dpy)
+ Display *dpy;
+{
+ XEvent event;
+
+ dprintf1("Entering x_get_input(%x).\n",dpy);
+
+ /*
+ * Kludge to get around lossage in XPending:
+ *
+ * (the problem: XPending on a partial packet returns 0 without
+ * reading in the packet. This causes a problem when the X server
+ * dies in the middle of sending a packet.)
+ */
+ if (XPending(dpy)==0)
+ XNoOp(dpy); /* Ensure server is still with us... */
+
+ while (XPending(dpy)) {
+ XNextEvent(dpy,&event);
+ xhandleevent(dpy, event.xany.window, &event);
+ }
+}
+
+#endif /* X_DISPLAY_MISSING */
+
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: xshow.h,v 1.5 1999/01/22 23:20:49 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+
+#include <zephyr/mit-copyright.h>
+
+#ifndef _ZWGC_XSHOW_H_
+#define _ZWGC_XSHOW_H_
+
+#include <X11/Xlib.h>
+
+typedef struct _xblock {
+ unsigned long fgcolor;
+ Font fid;
+ int x,y;
+ int x1,y1,x2,y2; /* bounds of block. used for cut and paste. */
+ int strindex;
+ int strlen;
+} xblock;
+
+typedef struct _xwin {
+ unsigned long bgcolor;
+ int xpos,ypos,xsize,ysize;
+ int numblocks;
+ xblock *blocks;
+ char *text;
+} xwin;
+
+typedef struct _xauxblock {
+ int align;
+ XFontStruct *font;
+ char *str;
+ int len;
+ int width;
+} xauxblock;
+
+typedef struct _xmode {
+ int bold;
+ int italic;
+ int size;
+ int align;
+ char *substyle;
+} xmode;
+
+typedef struct _xlinedesc {
+ int startblock;
+ int numblock;
+ int lsize;
+ int csize;
+ int rsize;
+ int ascent;
+ int descent;
+} xlinedesc;
+
+/* alignment values: */
+#define LEFTALIGN 0
+#define CENTERALIGN 1
+#define RIGHTALIGN 2
+
+#endif /* _ZWGC_XSHOW_H_ */
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: zephyr.c,v 1.10 1999/08/13 00:19:52 danw Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <sysdep.h>
+
+#if (!defined(lint) && !defined(SABER))
+static const char rcsid_zephyr_c[] = "$Id: zephyr.c,v 1.10 1999/08/13 00:19:52 danw Exp $";
+#endif
+
+#include <zephyr/mit-copyright.h>
+
+/****************************************************************************/
+/* */
+/* Module containing code dealing with zephyr: */
+/* */
+/****************************************************************************/
+
+#include <zephyr/zephyr.h>
+#include <sys/socket.h>
+#include "new_string.h"
+#include "zephyr.h"
+#include "error.h"
+#include "mux.h"
+#include "subscriptions.h"
+#include "variables.h"
+#include "pointer.h"
+#include "main.h"
+#ifndef X_DISPLAY_MISSING
+#include "X_driver.h"
+#endif
+
+#ifdef DEBUG
+extern int zwgc_debug;
+#endif /* DEBUG */
+
+/*
+ * Internal Routine:
+ *
+ * string get_zwgc_port_number_filename()
+ * Effects: Returns the filename that the zwgc port # is/should be
+ * stored in, based on the user's uid & the environment
+ * variable WGFILE. The returned string points into a
+ * static buffer that may change on further calls to this
+ * routine or getenv. The returned string should not be
+ * modified in any way.
+ */
+
+static string get_zwgc_port_number_filename()
+{
+ static char buffer[40];
+ char *temp;
+ char *getenv();
+
+ if (temp = getenv("WGFILE"))
+ return(temp);
+ else {
+ sprintf(buffer, "/tmp/wg.%d", getuid());
+ return(buffer);
+ }
+}
+
+/*
+ *
+ */
+
+static void handle_zephyr_input(notice_handler)
+ void (*notice_handler)();
+{
+ ZNotice_t *notice;
+ struct sockaddr_in from;
+ int complete_packets_ready;
+
+ for (;;) {
+ errno = 0;
+ if ( (complete_packets_ready=ZPending()) < 0 )
+ FATAL_TRAP( errno, "while calling ZPending()" );
+
+ if (complete_packets_ready==0)
+ return;
+
+ notice = malloc(sizeof(ZNotice_t));
+
+ TRAP( ZReceiveNotice(notice, &from), "while getting zephyr notice" );
+ if (!error_code) {
+ notice->z_auth = ZCheckAuthentication(notice, &from);
+ notice_handler(notice);
+ }
+ }
+}
+
+static int zephyr_inited = 0;
+
+/*
+ *
+ */
+
+void zephyr_init(notice_handler)
+ void (*notice_handler)();
+{
+ unsigned short port = 0; /* Use any old port */
+ char *temp;
+ char *exposure;
+ char *tty = NULL;
+ FILE *port_file;
+
+ /*
+ * Initialize zephyr. If error, print error message & exit.
+ */
+ FATAL_TRAP( ZInitialize(), "while initializing Zephyr" );
+ FATAL_TRAP( ZOpenPort(&port), "while opening Zephyr port" );
+
+ /*
+ * Save away our port number in a special place so that zctl and
+ * other clients can send us control messages: <<<>>>
+ */
+ temp = get_zwgc_port_number_filename();
+ errno = 0;
+ port_file = fopen(temp, "r");
+ if (port_file) {
+ fprintf(stderr, "zwgc: windowgram file already exists. If you are\n");
+ fprintf(stderr, "zwgc: not already running zwgc, delete %s\n", temp);
+ fprintf(stderr, "zwgc: and try again.\n");
+ exit(1);
+ }
+ port_file = fopen(temp, "w");
+ if (port_file) {
+ fprintf(port_file, "%d\n", port);
+ fclose(port_file);
+ } else {
+ fprintf(stderr, "zwgc: error while opening %s for writing: ", temp);
+ perror("");
+ }
+
+ /* Set hostname and tty for locations. If we support X, use the
+ * display string for the default tty name. */
+ if (location_override)
+ tty = location_override;
+#ifndef X_DISPLAY_MISSING
+ else if (dpy)
+ tty = DisplayString(dpy);
+#endif
+ error_code = ZInitLocationInfo(NULL, tty);
+ TRAP( error_code, "while initializing location information" );
+
+ /*
+ * Retrieve the user's desired exposure level (from the zephyr variable
+ * "exposure"), convert it to the proper internal form then
+ * set the user's location using it. If the exposure level is
+ * not one of the allowed ones, print an error and treat it as
+ * EXPOSE_NONE.
+ */
+ if (temp = ZGetVariable("exposure")) {
+ if (!(exposure = ZParseExposureLevel(temp))) {
+ ERROR2("invalid exposure level %s, using exposure level none instead.\n", temp);
+ exposure = EXPOSE_NONE;
+ }
+ } else
+ exposure = EXPOSE_NONE;
+ error_code = ZSetLocation(exposure); /* <<<>>> */
+ if (error_code != ZERR_LOGINFAIL)
+ TRAP( error_code, "while setting location" );
+
+ /*
+ * If the exposure level isn't EXPOSE_NONE, turn on recieving notices.
+ * (this involves reading in the subscription file, etc.)
+ */
+ if (string_Neq(exposure, EXPOSE_NONE))
+ zwgc_startup();
+
+ /*
+ * Set $realm to our realm and $user to our zephyr username:
+ */
+ var_set_variable("realm", ZGetRealm());
+ var_set_variable("user", ZGetSender());
+
+ /*
+ * <<<>>>
+ */
+ mux_add_input_source(ZGetFD(), (void (*)())handle_zephyr_input,
+ notice_handler);
+ zephyr_inited = 1;
+ return;
+}
+
+/*
+ *
+ */
+
+void finalize_zephyr() /* <<<>>> */
+{
+ string temp;
+
+ if (zephyr_inited) {
+ /*
+ * Remove the file containing our port # since it is no longer needed:
+ */
+ errno = 0;
+ temp = get_zwgc_port_number_filename();
+ unlink(temp);
+ if (errno) {
+ fprintf(stderr, "zwgc: error while trying to delete %s: ", temp);
+ perror("");
+ }
+
+ /*
+ * Cancel our subscriptions, unset our location, and close our zephyr
+ * connection:
+ */
+#ifdef DEBUG
+ if (zwgc_debug) {
+ TRAP( ZCancelSubscriptions(0), "while canceling subscriptions" );
+ TRAP( ZUnsetLocation(), "while unsetting location" );
+ } else {
+#endif /* DEBUG */
+ (void) ZCancelSubscriptions(0);
+ (void) ZUnsetLocation();
+#ifdef DEBUG
+ }
+#endif /* DEBUG */
+ ZClosePort();
+ }
+ return;
+}
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: zephyr.h,v 1.5 1999/01/22 23:20:50 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+
+#include <zephyr/mit-copyright.h>
+
+#ifndef zephyr_MODULE
+#define zephyr_MODULE
+
+extern void zephyr_init();
+extern void finalize_zephyr();
+
+#endif
--- /dev/null
+.\" $Id: zwgc.1,v 1.23 1999/01/22 23:20:50 ghudson Exp $
+' # end of TP (cf }N below)
+' # copied here, since we use @ in some of our tags, and that
+' # messes up \w and \h
+.de }1
+.ds ]X \&\\*(]B\\
+.nr )E 0
+.if !"\\$1"" .nr )I \\$1n
+.}f
+.ll \\n(LLu
+.in \\n()Ru+\\n(INu+\\n()Iu
+.ti \\n(INu
+.ie !\\n()Iu+\\n()Ru-\w'\\*(]X'u-3p \{\\*(]X
+.br\}
+.el \\*(]X\h@|\\n()Iu+\\n()Ru@\c
+.}f
+..
+.de }N
+.if \\n()E .br
+.di
+.if "\\n()E"0" .}f
+.if "\\n()E"1" .}1
+.if "\\n()E"2" .}2
+.nr )E 0
+..
+' # tagged paragraph (paragraph with hanging label, but no para spacing)
+.de TQ
+.if !"\\$1"" .nr )I \\$1n
+.ne 1.1v
+.in \\n()Ru
+.nr )E 1
+.ns
+.it 1 }N
+.di ]B
+..
+.TH ZWGC 1 "November 30, 1989" "MIT Project Athena"
+.SH NAME
+zwgc \- Zephyr Windowgram Client program
+.SH SYNOPSIS
+.B zwgc
+[ \-reenter ] [ \-nofork ] [ \-ttymode ] [ \-f
+.I filename
+] [ \-subfile
+.I filename
+] [ \-loc
+.I text
+] [ \-default
+.I portname
+] [ \-disable
+.I portname
+] ... [ output driver options ]
+[ X Toolkit options... ]
+.SH DESCRIPTION
+.I Zwgc
+is the main
+.I zephyr(1)
+client. It is responsible for receiving selected zephyr notices on
+behalf of the user, formatting them, and displaying them using
+one or more of the output devices.
+
+.SS "Selection of Zephyr Notices"
+.PP
+.I Zwgc
+subscribes to various notice classes and instances on behalf of the
+user. Only notices in the subscription list will be received. The
+subscription list is composed of the default subscriptions (stored on
+the server), the user's subscriptions file, and any subscriptions made
+using
+.IR zctl (1).
+The user's subscription file defaults to
+.IR $HOME/.zephyr.subs ,
+or it can be specified with the \-subfile
+option. If "\-" is specified as the subscription filename, the
+subscriptions will be read from standard input.
+
+.PP
+The
+.I zctl
+command is used to manipulate and change subscriptions. See the
+.IR zctl (1)
+man page for details.
+
+.SS "Zephyr Description Files"
+.PP
+.I Zwgc
+formats its output messages according to the commands in its
+description file. The user's description file
+.RI ( $HOME/.zwgc.desc
+by default, or whatever is specified by -f) is read, or the system file
+is read if the user's does not exist.
+.PP
+Every time a notice is received,
+.I zwgc
+runs through the description file, and executes the appropriate commands.
+
+.SS "Zephyr Description File Syntax"
+.PP
+A description file is simply a list of commands. Whitespace (spaces,
+tabs, and line breaks) is used
+to separate tokens. The type and amount of whitespace separating tokens
+is irrelevant.
+Comments can be delimited by # and newline (for line-oriented comments,
+e.g. "# this is a comment" on a line by itself) or by /* and */ (e.g. "/*
+this is a comment */").
+
+.SH "DESCRIPTION LANGUAGE"
+.SS Expressions
+Expressions are used by certain commands.
+They are composed from string literals, variable references,
+function calls, and operators. Parentheses can be used anywhere in an
+expression to group expressions or increase readability.
+.PP
+String literals are specified by putting the contents in "double quotes".
+.PP
+Variables are set using the
+.B set
+command (see "COMMANDS", below). They are
+referenced in an expression by using the form
+.IR $varname .
+Some variables are set by default for each notice.
+All other variables retain their values between notice interpretations,
+so that if you set a variable, it retains that value until later
+modified.
+.PP
+Functions are called using a C-like syntax,
+\fBfname\fR(\fIexpr1\fR,\fIexpr2\fR), where
+.B fname
+is the
+function name and
+.IB expr n
+are the arguments.
+.PP
+Binary operators use infix notation, such as "a == b".
+.PP
+Some commands use an expression list (exprlist), which is simply a set
+of expressions separated by whitespace (e.g. $var1 "lit1" $var2).
+
+.SS "Default variables"
+.PP
+The following variables are always available:
+.TP 5
+.B 1, ...
+Numeric variables are assigned values corresponding to that field in the
+notice (the body of each notice is conceptually an array of fields, each
+terminated with a null character). If the number is greater than the
+number of fields actually in the notice, the value is "". For example,
+the standard zwrite messages have two fields: $1 is the signature, and
+$2 is the text of the message.
+.TP 5
+.B auth
+An indication of the authenticity of the notice. ``yes'' means the
+notice is authentic, ``no'' means it is not, and ``forged'' means that
+the message claimed to be authentic but the verification of the claim
+failed. The ``forged'' indication usually appears when a user has
+changed his Kerberos tickets with
+.IR kinit (1)
+but has not run ``zctl sub'' to
+register this change with the Zephyr servers.
+.TP
+.B class
+The class of the current notice.
+.TP
+.B date
+The date on which the notice was sent.
+.TP
+.B default
+The default output format for the current notice
+.TP
+.B error
+An error message from the port read/write commands.
+.TP
+.B fromhost
+The full name of the host from which the notice appears to have been
+sent.
+.I This is not fully reliable,
+as the information used to determine this hostname is not guaranteed to
+be correct (even for authentic messages).
+.TP
+.B fullsender
+The notice sender's name, including the zephyr realm name.
+.TP
+.B instance
+The instance of the current notice.
+.TP
+.B kind
+The kind of notice.
+.TP
+.B message
+The full text of the message, with nulls converted to newlines.
+.TP
+.B number_of_fields
+The number of fields in the message (a string representation of a
+decimal number).
+.TP
+.B opcode
+The opcode of the current notice.
+.TP
+.B output_driver
+The name of the output driver in use.
+.TP
+.B port
+The port from which the notice was sent.
+.TP
+.B realm
+The local zephyr realm.
+.TP
+.B recipient
+The recipient for the current notice. If the notice is a multicast
+(sent to several people), the recipient is set to ``*''.
+.TP
+.B sender
+Usually a shortened version of fullsender. If the realm of the sender
+is equal to the realm of the recipient,
+.I sender
+omits the realm name.
+.TP
+.B time
+The time of day at which the notice was sent.
+.TP
+.B user
+The full zephyr name of the user (e.g. marc@ATHENA.MIT.EDU).
+.TP
+.B version
+The current version of
+.IR zwgc .
+.TP
+.B zephyr_version
+The protocol version of the notice.
+.PP
+All of these variables (except for error, output_driver, and version)
+are re-set before each notice is processed.
+
+.SS Functions
+.PP
+Following is a list of functions available for use in the description
+file.
+.TP 5
+.BI buffer ()
+The contents of the current output buffer.
+.TP
+.BI downcase (expr)
+Returns the value of \fIexpr\fR, converted to lower case.
+.TP
+.BI get (expr)
+Returns a line from the port named \fIexpr\fR. If there is no text
+waiting on the port (e.g. the program connected to the port has not
+printed any output), this function will wait until it can read a line of
+text from the port.
+.TP
+.BI getenv (expr)
+Returns the value of the environment variable \fIexpr\fR, or the empty
+string if it does not exist.
+
+.TP
+.BI lany "(expr1, expr2), " rany "(expr1, expr2)"
+Return a number of characters equal to the length of
+.I expr2
+from the beginning
+.RB ( lany )
+or end
+.RB ( rany )
+of
+.I expr1
+(e.g. lany("1234567890","foo") would return "123").
+If
+.I expr1
+is a variable reference, the variable
+is modified to remove the characters returned.
+If
+.I expr2
+is longer than
+.IR expr1 ,
+the value of
+.I expr1
+is returned (and
+.I expr1
+is set to "", if a variable).
+.TP
+.BI lbreak "(expr1, expr2), " rbreak "(expr1, expr2)"
+.I Expr2
+defines a set of characters. The function returns the longest
+initial
+.RB ( lbreak )
+or final
+.RB ( rbreak )
+string from
+.I expr1
+composed of characters
+.I not
+in this set (e.g. lbreak("characters", "tuv") would return "charac"). If
+.I expr1
+is a variable reference, the variable
+is modified to remove the characters returned. If no characters
+in
+.IR expr2 " are in " "expr1, " then " expr1 "
+is returned (and
+.I expr1
+is set to "", if a variable).
+.TP
+.BI lspan "(expr1, expr2), " rspan "(expr1, expr2)"
+These functions are the negation of the
+.B break
+functions; the returned string consists of characters
+.I in
+the set defined by
+.I expr2
+.TP
+.BI protect (expr)
+Returns a string which will be evaluated identically to \fIexpr\fR,
+but will not affect any surrounding environments. That is, any
+characters which could close outside environments are quoted, and any
+environments in \fIexpr\fR which are not closed at the end are closed.
+.TP
+.BI substitute (expr)
+Evaluates variable references of the form \fI$variable\fR in expr and
+converts $$ to $.
+.TP
+.BI upcase (expr)
+Returns the value of \fIexpr\fR, converted to upper case.
+.TP
+.BI verbatim (expr)
+Returns a string that will be displayed exactly as \fIexpr\fR looks.
+Anything which could be mistaken for an environment is quoted.
+.TP
+.BI stylestrip (expr)
+Returns \fIexpr\fR with all environments stripped out.
+.TP
+.BI zvar (expr)
+Returns the value of the zephyr variable \fIexpr\fR,
+or the empty
+string if it does not exist. [Zephyr variables
+can be set and examined with
+.IR zctl (1).]
+
+.SS Operators
+.PP
+Following is a list of operators which can be used in the description
+file to compose expressions:
+.TP
+.IB expr1 " + " expr2
+String concatenation of
+.IR expr1 " and " expr2
+.TP
+.IB expr1 " == " expr2
+True if the two expressions are equal, false otherwise.
+.TP
+.IB expr1 " =~ " expr2
+True if the regular expression pattern
+.IR expr2 " matches " expr1.
+.TP
+.IB expr1 " !~ " expr2
+Negation of "=~".
+.TP
+.IB expr1 " != " expr2
+Negation of "=="
+.TP
+\fIexpr1\fB and \fIexpr2\fR, \fIexpr1\fB & \fIexpr2\fR
+True if
+.IR expr1 " and " expr2
+are both true.
+.TP
+\fIexpr1\fB or \fIexpr2\fR, \fIexpr1\fB | \fIexpr2\fR
+True if either of
+.IR expr1 " or " expr2
+are true.
+.TP
+\fB! \fIexpr1\fR, \fBnot \fIexpr1\fR
+The logical negation of
+.I expr1.
+
+.SS Commands
+.PP
+Following is a list of the commands usable in the description
+language:
+.TP 5
+.BI appendport " expr1 expr2"
+Creates a port called \fIexpr1\fR. All output to the port will be
+appended to the file \fIexpr2\fR. There is no input. If the file is
+created, its mode is set to read-write, owner only (no access for others).
+.TP
+.B break
+Exits the innermost if, case, or while block.
+.if n .ll +2in
+.TP
+\fBcase \fIexpr1\fR [ ((\fBmatch \fIexpr\fR [,\fIexpr ...\fR]) | \fBdefault\fR)\fI commands \fR] ... \fBendcase\fR
+Evaluates \fIexpr1\fR. Then, each of the match expressions is
+evaluated in order. The first time an expression matches \fIexpr1\fR,
+then the body of commands under it is executed, and the rest of the case
+statement is skipped. This compare is case-insensitive. default always
+matches, so it should always appear as the last set of commands. See
+the default description file for an example of use.
+.TP
+.B clearbuf
+Clears the output buffer (see below for details on buffering).
+.TP
+.BI closeinput " expr"
+Closes the file associated with \fIexpr\fR.
+.TP
+.BI closeoutput " expr"
+Sends an EOF (end-of-file) to the process if \fIexpr\fR was a port created by
+execport, or closes the file if it was created by outputport or
+appendport.
+.TP
+.BI closeport " expr"
+Closes both input and output of \fIexpr\fR as defined above.
+.TP
+.BI fields " variable1 ..."
+sets the list of variables to be equal to the fields in the
+notice. If there are more variables than fields, the extra
+variables are left empty.
+.TP
+.BI exec " exprlist"
+Executes a program without any input or output. A command named by
+\fIexprlist\fR is executed. Each expression is used as an argument to
+the program; the first expression names the program (it may be either an
+absolute pathname, or a program name; the user's PATH is searched to
+find simple program names).
+.TP
+.BI execport " expr1 exprlist"
+Creates a port called \fIexpr1\fR. A command named by \fIexprlist\fR
+is executed, as described above for \fBexec\fR.
+All output to the port is sent to the standard input
+of the process. Reading from the port will return the standard output
+of the process.
+.TP
+.B exit
+Completes processing of the current notice. The remainder of the
+description file is ignored after execution of this command.
+.\" hack because the following line otherwise breaks because it is too long.
+.if n .ll +2in
+.TP
+\fBif \fIexpr1 \fBthen \fIcommands1\fR [\fBelseif \fIexpr2 \fBthen \fIcommands2\fR] ... [\fBelse \fIcommandsn\fR] \fBendif\fR
+If \fIexpr1\fR evaluates to true, execute \fIcommands1\fI, etc. [A conditional
+construct, similar to the constructs in the C shell (csh).]
+.TP
+.BI inputport " expr1 expr2"
+Creates a port called \fIexpr1\fR. All input from the port comes from
+the file \fIexpr2\fR. There is no output.
+.TP
+.B noop
+does nothing
+.TP
+.BI outputport " expr1 expr2"
+Creates a port called \fIexpr1\fR. The file \fIexpr2\fR will be
+truncated, or created if it does not exist. All output to the port
+will be appended to the file \fIexpr2\fR. There is no input. If the file is
+created, its mode is set to read-write, owner only (no access for others).
+.TP
+.BI print " expr1 ..."
+adds the values of the expressions to the current output buffer. The
+values of the expressions are separated by spaces in the output.
+.TP
+.B put \fR[\fIexpr \fR[\fIexprlist\fR]]
+Sends data to a port. If \fIexpr\fR is provided, then it is used as the
+port, otherwise the port used is the
+port corresponding to the default output device.
+If \fIexprlist\fR is provided, the expressions in the list are sent to
+the port, separated by spaces. If it is omitted, then the contents
+of the output buffer are sent as the data.
+.TP
+.BI set " variable " = " expr"
+sets
+.I variable
+equal to
+.IR expr .
+Variable can later be
+referenced by
+.IR $variable .
+.TP
+.BI show " text " endshow
+Appends text to the output buffer. This command is special, because
+the string does not need to be quoted. Whitespace at the beginning or
+end of the lines of text is ignored. The \fIendshow\fR must appear as
+the first token on a line (it may only be preceded on that line by whitespace).
+Variable substitutions and formatting commands
+(but not expressions or functions) are processed in the text. Example:
+.nf
+show
+ this is some text
+ from: $sender
+endshow
+.fi
+.TP
+.BI while " expr " do " statements " endwhile
+Executes \fIstatements\fR until \fIexpr\fR is false.
+
+.SH PORTS
+.PP
+Ports are an abstraction encompassing all I/O forms of which
+zwgc is capable. There are pre-existing output ports corresponding to each
+of the output devices, and more ports can be created with the
+port commands described above.
+
+.SH OUTPUT
+The output is usually collected in the
+.I "output buffer"
+and saved until a
+.I put
+command sends the output to an output device (such as an X display or a
+terminal). The output buffer is implicitly cleared after each notice is
+completely processed.
+
+.PP
+Output devices are implemented as output ports. A message is
+displayed in a device-dependent manner when a string is output to the
+port corresponding to the output device. Formatting commands are
+embedded in the text as @ commands of the form @command(text).
+Command names are case-insensitive and consist of alphanumeric
+characters and underscores. Valid brackets are () [] {} and <>.
+If the command name is empty (such as in
+.RB `` @(foo) ''),
+then a new
+environment with no changes is created (This is useful to temporarily
+change some parameter of the output, such as the font).
+.PP
+The following output devices are supported:
+.TP 5
+stdout
+Sends the string to standard output exactly as is.
+.TP
+stderr
+Sends the string to standard error exactly as is.
+.TP
+plain
+Sends the string with all formatting environments removed to standard
+output.
+.TP
+tty
+Does formatting on the message according to @ commands embedded in the
+text. The output,
+with appropriate mode-changing sequences, is sent to the standard output.
+The appropriate characteristics of the display are taken from
+the TERMCAP entry (see
+.IR termcap (5))
+for the terminal named by the TERM environment variable.
+Supported @ commands are:
+.RS 10
+.\" .TQ 15
+.\" @em
+.\" Emphasis. Use underline if available, else reverse video.
+.TQ 15
+@roman
+Roman (plain) letters (turns off all special modes).
+.TQ
+@b or @bold
+Bold letters. If not available, reverse video, else underline.
+.TQ
+@i or @italic
+Italic letters (underlining, if available).
+.TQ
+@beep
+"bl" termcap entry, else "^G" (beep the terminal); limited to once per
+message.
+.\" .TQ
+.\" @blink
+.\" "mb"/"me" termcap entry, else nothing.
+.\" .TQ
+.\" @rv
+.\" "so"/"se" termcap entry.
+.\" .TQ
+.\" @u
+.\" "us"/"ue" termcap entry.
+.TQ
+@l or @left
+left aligned
+.TQ
+@c or @center
+center aligned
+.TQ
+@r or @right
+right aligned
+.RE
+.IP "" 5
+Other @-commands are silently ignored.
+.TP 5
+X
+Displays one window per string output to the port. The output is
+formatted according to @ commands embedded in the string. Supported
+@ commands are:
+.RS 10
+.TQ 15
+@roman
+turns off @italic and @bold
+.TQ
+@b or @bold
+turns on boldface
+.TQ
+@i or @italic
+turns on italics
+.TQ
+@l or @left
+left aligned
+.TQ
+@c or @center
+center aligned
+.TQ
+@r or @right
+right aligned
+.TQ
+@large
+large type size
+.TQ
+@medium
+medium type size
+.TQ
+@small
+small type size
+.TQ
+@beep
+Ring the X bell (limited to once per message)
+.TP
+@font
+sets the current font to the font specified in the contents of the
+environment (e.g. @font(fixed)). This will remain in effect for the
+rest of the environment (a temporary change can be achieved by enclosing the
+font-change in an @(...) environment). If the named font is not
+available, the font ``fixed'' is used instead.
+.TP
+@color
+sets the color to the color specified in the contents of the
+environment. The color name should appear in the X color name database.
+This color will remain in effect for the rest of the environment. If
+the named color is not available, the default foreground color is used.
+.RE
+.IP "" 5
+Any other environment name not corresponding to the above environment
+names will set the current ``substyle.''
+.IP
+The attributes of a given block of text are determined by any active
+environments, evaluated in the context of the current style and
+substyle.
+.IP
+The style is specific to each window. Its name has three dot
+(``.'') separated fields, which are by default the values of the class,
+instance, and recipient variables, with all dots changed to underscores
+(``_'') and all letters converted to lowercase. The style can be
+altered by setting the
+.I style
+variable. Note that it \fBmust always\fR have exactly two ``.''
+characters in it.
+.IP
+The substyle is determined by @ commands in the message text.
+.IP
+Zwgc variables which the X output device reads are:
+.RS 10
+.TQ 15
+default_X_geometry
+default geometry for notices, set from resources
+.TQ
+X_geometry
+overrides geometry in resource file, if set
+.TQ
+default_X_background
+default background color for notices, set from resources
+.TQ
+X_background
+overrides bgcolor in resource file, if set
+.TQ
+style
+style, as described above
+.RE
+.IP "" 5
+The expected geometry values are described below.
+.IP
+The fonts and color for a piece of text are determined by the styles
+defined in the X resources file. The following resources relating to
+text style are used by zwgc:
+.RS 10
+.TP 10
+zwgc.style.\fIstylenames\fR.geometry
+geometry for messages of the specified style
+.TP
+zwgc.style.\fIstylenames\fR.background
+background color for messages of the specified style
+.TP
+zwgc.style.\fIstylenames\fR.substyle.\fIsubstylename\fR.fontfamily
+fontfamily name for the specified style and substyle
+.TP
+zwgc.style.\fIstylenames\fR.substyle.\fIsubstylename\fR.foreground
+foreground color for the specified style and substyle
+.TP
+zwgc.fontfamily.\fIfontfamilyname\fR.\fIsize\fR.\fIface\fR
+specifies the fonts for a given fontfamily. \fIsize\fR is one
+of small, medium, or large, and \fIface\fR is one of roman,
+bold, italic, or bolditalic.
+.RE
+.IP "" 5
+The best way to get started in customizing X resources for
+.I zwgc
+is to examine the default application resources and other users'
+resources to understand how they specify the default appearance.
+
+.SH "X RESOURCES"
+Other X resources used by
+.I zwgc
+are listed below.
+Entries like
+.sp
+.nf
+.in +5
+zwgc*option: value
+Zwgc*option: value
+zwgc.option: value
+*option: value
+.option: value
+.in -5
+.fi
+.sp
+will work.
+.PP
+An entry labeled with zwgc*option in any of the sources takes precedence
+over Zwgc*option, which takes precedence over *option entries.
+The following sources are searched in order:
+.nf
+.in +5
+command-line arguments (-xrm)
+contents of file named by XENVIRONMENT environment variable
+X server resource database (see \fIxrdb\fR(1))
+application resources file
+.in -5
+.fi
+.PP
+Logical values can be ( Yes On True T ) or ( No Off False nil ).
+.TP 15
+\fBOPTION:\fR
+\fBMEANING [default]:\fR
+.TP
+cursorCode
+number of a code from the cursorfont (should be an even integer, see
+\fI<X11/cursorfont.h>\fR) to use for the windows.
+.TP
+foreground
+Primary foreground color
+.TP
+Foreground
+Secondary foreground color (if foreground not set) [BlackPixel is the default if neither is set]
+.TP
+background
+Primary background color
+.TP
+Background
+Secondary background color (if background not set) [WhitePixel is the
+default if neither is set]
+.TP
+borderColor
+Primary border color
+.TP
+BorderColor
+Secondary border color (if borderColor not set) [BlackPixel is the
+default if neither is set]
+.TP
+pointerColor
+Primary mouse pointer color [foreground color is the default if not set]
+.TP
+reverseVideo
+(logical) Toggles foreground and background (and border, if it matches
+foreground or background).
+.TP
+ReverseVideo
+Secondary toggle, if reverseVideo is not set. [off is the default if
+neither is set]
+.TP
+borderWidth
+Primary border width selector
+.TP
+BorderWidth
+Secondary border width selector (if borderWidth is not set) [1 is the
+default value if neither is set]
+.TP
+internalBorder
+Primary border between edge and text
+.TP
+InternalBorder
+Secondary selector (if internalBorder not set) [2 is the default value
+if neither is set]
+.TP
+geometry
+Primary POSITION (not size) geometry specifier.
+The geometry should be of the form "{+|\-}x{+|\-}y", specifying an (x,y)
+coordinate for a corner of the window displaying the notice. The
+interpretation of positive and negative location specifications follows
+the X conventions. A special location of `c' for either x or y
+indicates that the window should be centered along that axis. Example:
+a geometry of "+0+c" specifies the window should be at the top of the
+screen, centered horizontally.
+.TP
+Geometry
+Secondary position specifer. [+0+0 is the default if neither is set.]
+.TP
+resetSaver
+(logical) Primary value to force screen to unsave when a message first
+appears.
+.TP
+ResetSaver
+(logical) Secondary value to force screen to unsave. [default True]
+.TP
+reverseStack
+(logical) Primary value to specify that zwgc should attempt to stack
+WindowGram windows such that the oldest messages
+normally show on top. Some X window managers may silently ignore
+.IR zwgc 's
+attempts to restack its windows. This option can cause some unusual
+interactions with other windows if the user manually restacks either the
+other windows or the WindowGram windows.
+.TP
+ReverseStack
+Secondary value to enable reverse stacking. [default False]
+.TP
+title
+(string) Primary window title
+.TP
+Title
+Secondary window title [defaults to the last pathname component
+of the program name, usually "zwgc"]
+.TP
+transient
+(logical) Primary value which determines if zephyrgram windows will be
+created with the \fBWM_TRANSIENT_FOR\fR property set. If this
+resource is true, the property will be set, telling certain
+windowmanagers to treat zephyrgram windows specially. For instance,
+\fItwm\fR will not put decorations on transient windows, \fImwm\fR
+will not let you iconify them, and \fIuwm\fR ignores the resource
+entirely.
+.TP
+Transient
+Secondary transient determining value [default False]
+.TP
+enableDelete
+(logical) If true, zwgc creates a WM_PROTOCOLS property on all zgrams, with
+WM_DELETE_WINDOW as contents.
+.TP
+EnableDelete
+Secondary value to enable WM_DELETE_WINDOW protocol on zgrams [default False]
+.TP
+minTimeToLive
+Primary value which specifies the minimum amount of time (``minimum time to
+live'') a WindowGram must be on-screen (in milliseconds) until it can
+be destroyed. This feature is useful to avoid accidentally clicking
+on new WindowGrams when trying to delete old ones.
+.TP
+MinTimeToLive
+Secondary value of ``minimum time to live.''
+.TP
+iconName
+(string) Primary icon name
+.TP
+IconName
+Secondary icon name [defaults to the last pathname component
+of the program name, usually "zwgc"]
+.TP
+name
+(string) Primary window class name
+.TP
+name
+Secondary window class name [defaults to the last pathname component
+of the program name, usually "zwgc"]
+.TP
+synchronous
+(logical) Primary X synchronous mode specifier. On means to put the X
+library into synchronous mode.
+.TP
+Synchronous
+Secondary X synchronous mode specifier. [default is `off']
+.PP
+The window class is always "Zwgc".
+.SH X BUTTONS
+.PP
+Clicking and releasing any button without the shift key depressed while
+the pointer remains inside a WindowGram window will cause it to
+disappear. If the pointer leaves the window
+while the button is depressed, the window does not disappear; this
+provides a way to avoid accidentally losing messages.
+.PP
+If the control button is held down while clicking on a WindowGram,
+then that WindowGram and all windowgrams under the point where the
+button is released will be erased.
+.PP
+.B WARNING:
+If you do this with too many WindowGrams under the mouse, it is
+possible for your subscriptions to be lost. If \fIzctl retrieve\fR
+returns nothing, then issue a \fIzctl load\fR command to re-subscribe
+to your default set of subscriptions. If you use znol, then \fIznol
+-q &\fR will restore the subscriptions you need for \fIznol\fR.
+.PP
+Portions of the text of a message may be selected for "pasting" into other X
+applications by using the shift key in cooperation with the pointer
+buttons.
+Holding the Shift key while depressing Button1 (usually the left button)
+will set a marker at the
+text under the pointer. Dragging the pointer with Shift-Button1 still
+depressed extends the selection from the start point, until the button
+is released. The end of the selection may also be
+indicated by releasing Button1, holding down the Shift key, and pressing
+Button3 (usually the right button) at the desired endpoint of the selection.
+The selection will appear with the text and background colors reversed.
+
+.SH ADDITIONAL X FEATURES
+If
+.I zwgc
+receives a WM_DELETE_WINDOW, it destroys the zephyrgram as if it were
+clicked on.
+.PP
+If a zephyrgram is unmapped, it is removed from the stacking order
+used by reverseStack.
+
+.SH COMMAND LINE
+.I zwgc
+is normally invoked from
+.IR /usr/athena/lib/init/login ,
+.IR $HOME/.xsession ,
+or
+.I /usr/athena/lib/init/xsession
+in the foreground. When it has successfully set your location and
+obtained subscriptions, it will put itself into the background (unless
+the \-nofork option has been specified). At this point it is safe to
+invoke additional zephyr commands, such as
+.IR znol (1).
+(You can also put these commands in the
+.I initprogs
+Zephyr variable; the value of this variable is passed as the argument to
+the
+.IR system (3)
+library call during initialization.)
+.I zwgc
+will exit with an exit
+status of 0 if it was able to open the X display successfully or 1 if it
+couldn't open the display and the Zephyr variable
+.I fallback
+was set to ``false''. If
+.I fallback
+is set to ``true'',
+.I zwgc
+will fall back to ``ttymode'' (making the tty driver the default output
+device) if it can't open the X display. If
+.I fallback
+is not set and the display cannot be opened,
+.I zwgc
+prints an explanatory message and exits with a status of 1.
+.PP
+If the
+.I \-ttymode
+option is specified,
+.I zwgc
+will ignore any X display and use the terminal as its primary output
+device. This flag overrides any setting of the fallback variable.
+.PP
+If the
+.I \-loc
+option is specified,
+.I zwgc
+will use the specified string as the tty field for the location it
+sets. This allows users to potentially specify more useful auxiliary
+information than their ttys or display names.
+.PP
+The
+.I \-reenter
+option is provided for compatibility with the previous version of
+.IR zwgc .
+.PP
+.I zwgc
+will exit cleanly (unset location and cancel subscriptions) on:
+.nf
+ SIGTERM
+ SIGHUP
+ XIOError (with a message to stderr)
+.fi
+SIGHUP is what it expects to get upon logout. Also, the signals
+SIGINT, SIGQUIT, and SIGTSTP are ignored because they can be sent
+inadvertently, and bizarre side-effects can result. If you want them
+to be acted on, then run
+.I zwgc -nofork &
+
+.SH CONTROL MESSAGES
+In order to allow some special user controls over the behavior of
+.IR zwgc ,
+certain Zephyr control notices can be sent directly to
+.I zwgc
+using the
+.IR zctl (1)
+program. Currently implemented controls are
+.TP 15
+wg_read
+tell
+.I zwgc
+to re-read the current description file.
+.TP
+wg_shutdown
+tell
+.I zwgc
+to cancel all subscriptions and stop acting on incoming notices.
+.I zwgc
+saves the subscriptions that were in effect at the time of the shutdown
+so that it can restore them later if needed.
+.TP
+wg_startup
+tell
+.I zwgc
+to restart from being shutdown and reinstall the saved subscriptions.
+.PP
+Other control messages may be implemented in the future.
+
+.SH EXAMPLES
+For an example of a description file, see
+.IR /usr/athena/lib/zephyr/zwgc.desc .
+For an example of X resources, see
+.IR /usr/athena/lib/zephyr/zwgc_resources .
+
+.SH BUGS
+The X selection code can highlight the wrong portions of messages
+containing formatted text placed with the @center() or @right()
+directives.
+.PP
+If you are using Kerberos support and get new tickets (using
+``kinit''), you must send a subscription notice to the server (using a
+command such as ``zctl load /dev/null'') or all received Zephyr
+notices will appear to be unauthentic. (If all received Zephyr
+notices appear to be forged, your tickets have probably expired, in
+which case you must get new tickets and then run ``zctl load
+/dev/null''.)
+.SH FILES
+.TQ 15
+$HOME/.zwgc.desc
+Default location of user's description file
+.TQ
+/usr/athena/lib/zephyr/zwgc.desc
+System-wide description file
+.TQ
+/usr/athena/lib/zephyr/zwgc_resources
+Default X application resources.
+.TQ
+$HOME/.zephyr.vars
+File containing variable definitions
+.TQ
+$HOME/.zephyr.subs
+Supplementary subscription file
+.TQ
+$HOME/.Xresources
+Standard X resources file
+.TQ
+$WGFILE or /tmp/wg.\fIuid\fR
+File used to store WindowGram port number for other clients
+.SH SEE ALSO
+csh(1), kinit(1), xrdb(1), zctl(1), zephyr(1), znol(1), X(1), getenv(3),
+system(3), termcap(5), zephyrd(8), zhm(8)
+.br
+Project Athena Technical Plan Section E.4.1, `Zephyr Notification Service'
+.SH AUTHORS
+.nf
+John Carr (MIT/Project Athena) <jfc@athena.mit.edu>
+Marc Horowitz (MIT/Project Athena) <marc@athena.mit.edu>
+Mark Lillibridge (MIT/Project Athena) <mdl@CS.CMU.EDU>
+.fi
+.SH RESTRICTIONS
+Copyright (c) 1989 by the Massachusetts Institute of Technology.
+All Rights Reserved.
+.br
+.I zephyr(1)
+specifies the terms and conditions for redistribution.
--- /dev/null
+# Copyright 1989, 1990 Massachusetts Institute of Technology
+#
+# For copying and distribution information, see the file
+# "mit-copyright.h".
+#
+# $Id: zwgc.desc,v 1.15 1999/01/22 23:20:51 ghudson Exp $
+#
+#
+# Default WindowGram description file
+#
+
+# Opcode "ping" is used by sender programs to see if the message would
+# really get sent, or if the recipient has logged out. No useful
+# information is normally contained in these messages, so we discard them.
+if (upcase($opcode) == "PING") then exit endif
+
+#
+# AUTHENTICATION information
+#
+# $auth can be either Yes, No, or Forged
+#
+# "Yes" means that the sender field present in the notice was verified by
+# Kerberos authentication
+#
+# "No" means that either the sender did not include any authentication
+# information, or the authentication information was not verified by the
+# Zephyr Server before the notice was sent to you.
+#
+# "Forged" means that the Server claims that the sender of the notice
+# was verified by Kerberos authentication, but your WindowGram client
+# could not verify this. This stage of verification is done by a cryptographic
+# checksum. The most probable cause of the failure of the checksum
+# provided by the Server to match the checksum generated by your
+# WindowGram client is that you changed Kerberos tickets, and the Server
+# was using an old value to compute the cryptographic checksum. You can
+# update the Server's value by typing 'zctl load' to your prompt.
+#
+# By default, notices which appear forged are labeled as 'UNAUTHENTIC'
+# to avoid confusion as to what 'Forged' really means.
+# To change this display, change the last word in the line following
+# 'match "forged" to something other than "UNAUTHENTIC".
+case $auth
+match "yes"
+ set aval = "Authentic"
+match "no","forged"
+ set aval = "@b(@large(UNAUTHENTIC))"
+endcase
+
+case $class
+match "WG_CTL_CLASS"
+ exit
+#
+# MAIL NOTIFICATION
+#
+# To receive mail notifications, you need to do several things:
+# 1) subscribe to MAIL,POP messages. You do this by typing:
+# zctl add mail pop
+# to your prompt. By doing this, you will get a simple notice every
+# time you are logged in and more mail arrives for you at your post office.
+#
+# 2) If you wish to be notified of the sender, recipient and subject of the
+# new mail, remove the pound-signs from the beginning of the 10 lines below
+# between 'match "MAIL"' and 'exit', inclusive, and type the command
+# zctl add mail popret
+# to your prompt.
+#
+# Note: The use of the following lines is NOT necessary to receive
+# notifications of new mail. The only effect of uncommenting these
+# lines is to display on your screen the sender, recipient and subject
+# of the mail (In addition, uncommenting these lines will add extra load
+# to the post office servers, making them run slower.).
+# If you do not wish this information to be displayed where other users
+# might possibly read it, or you wish to avoid loading down the post
+# office servers, you need not uncomment these lines. Just follow step
+# 1 above.
+#
+#match "MAIL"
+# case $instance
+# match "pop"
+# exec "zmailnotify"
+# exit
+# endcase
+# print "(Authentication: @bold("+$aval+"))\n"
+# print substitute($default)
+# put
+# exit
+match "message"
+ if (downcase($recipient) == downcase($user)) then
+ case $instance
+ match "PERSONAL"
+ set type = "Personal"
+ match "URGENT"
+ set type = "Urgent"
+ default
+ set type = $instance
+ endcase
+ else
+ set type = "Instance "+$instance
+ endif
+
+ fields signature body
+ if ($body == "") then
+ set body = $signature
+ set signature = ""
+ endif
+ if ($signature =~ "^[Ff]rom: .*") then
+ set dummy = lany($signature,"From: ")
+ endif
+ if ($signature =~ "\n$") then
+ set dummy = rany($signature,"\n")
+ endif
+ if ($signature == "") then
+ set ftext = "From: @bold("+protect($sender)+")"
+ else
+ set ftext = "From: @bold(@{"+protect($signature)+"} <"+
+ protect($sender)+">)"
+ endif
+
+ print "@center(@bold("+$aval+") "+$type+" message at "+$time+
+ " on "+$date+"\n"+$ftext+" on "+$fromhost+"\nTo: "+
+ $recipient+")\n\n"
+ print $body
+ put
+ exit
+
+match "login"
+ case $opcode
+ match "USER_LOGIN"
+ set log = "logged in"
+ match "USER_LOGOUT"
+ set log = "logged out"
+ default
+ set log = "unknown opcode"
+ endcase
+
+ fields host when tty
+ print "@center(@bold("+$sender+") "+$log+")\n"
+ print "@center(on @bold("+$host+") on "+$tty+")\n"
+ print "@center(at "+$when+")"
+ put
+ exit
+
+default
+ print "(Authentication: @bold("+$aval+") from host: "+$fromhost+")\n"
+ print substitute($default)
+ put
+ exit
+
+endcase
--- /dev/null
+; zwgc.el
+;
+; This file is part of the Project Athena Zephyr Notification System.
+; Created by: Mark W. Eichin <eichin@athena.mit.edu>
+; $Id: zwgc.el,v 1.4 1999/01/22 23:20:51 ghudson Exp $
+; Copyright (c) 1988 by the Massachusetts Institute of Technology.
+; For copying and distribution information, see the file
+; "mit-copyright.h".
+;
+; Emacs mode for running zwgc in a sub process of emacs. It pops up a
+; window for every new message; if you make bells appear between each
+; message, it will be able to seperate them. If you move the mouse
+; into the message window and hit `delete' it will delete the current
+; message; if there are other messages, it will show them, if not, it
+; will make the window go away.
+;
+; Invoke with M-x zwgc.
+;
+; Also included is M-x zsend, which prompts for a user name and a
+; message to send to them. If the message is blank, a buffer is popped
+; up to edit the message in. If a prefix argument is given, zsend
+; prompts for an instance instead. If the user name is blank, the last
+; one is reused.
+;
+; The following should be added to your .zephyr.desc file if you want
+; to take advantage of the zwgc message seperation features:
+; does $mode
+; match tty
+; beep
+; endmatch
+; enddoes
+;
+(defvar zwgc_el-RCS-id)
+(setq zwgc_el-RCS-id "$Id: zwgc.el,v 1.4 1999/01/22 23:20:51 ghudson Exp $")
+;
+;
+
+(defun narrow-to-string (str)
+ "narrow and put up a string..."
+ (interactive "sString: ")
+ (narrow-to-region (point) (point))
+ (insert str))
+
+(defvar zwgc-prog "/usr/etc/zwgc"
+ "*Program to run as the zwgc process. Should set it for the machine type.")
+
+(defun zwgc-wakeup (proc string)
+ "Procedure called when zwgc spits something out"
+ (let (start-limit)
+ (save-excursion (set-buffer (get-buffer "*zwgc*"))
+ (setq start-limit (point))
+ (goto-char (point-max))
+ (if (= 7 (string-to-char string))
+ (progn
+ (ding 1)
+ (message "got one!")
+ (narrow-to-string string))
+ (insert string))
+ (search-backward "\007" start-limit t)
+ (while (search-forward "\015" (point-max) t) ;flush ^M's
+ (delete-backward-char 1)))
+ (Special-pop-up-window (get-buffer "*zwgc*"))
+ ))
+
+(defun zwgc ()
+ "emacs mode for running zwgc in a sub process of emacs. It pops up a
+window for every new message; if you make bells appear between each
+message, it will be able to seperate them. If you move the mouse into
+the message window and hit `delete' it will delete the current
+message; if there are other messages, it will show them, if not, it
+will make the window go away."
+ (interactive)
+ (require 'shell)
+ (let ((buffer (get-buffer-create "*zwgc*")) proc status)
+ (setq proc (get-buffer-process buffer))
+ (if proc
+ (setq status (process-status proc)))
+ (save-excursion
+ (set-buffer buffer)
+ (if (memq status '(run stop))
+ nil
+ (if proc (delete-process proc))
+ (setq proc (start-process "Zwgc" buffer
+ zwgc-prog "-disable" "X"
+ "-default" "plain" "-nofork"))
+ (set-process-filter proc 'zwgc-wakeup))
+ (shell-mode)
+ (local-set-key "\177" 'zwgc-punt)
+ )
+ ))
+
+
+(defun Special-pop-up-window (buffer &optional max-height)
+ "Pop up a window that is just big enough to hold the current buffer."
+ (interactive "bBuffer to pop: ")
+ (let* ((retwin (selected-window))
+ (pop-up-windows t)
+ (window-min-height 1))
+ (pop-to-buffer buffer)
+ (setq lines (1+ (count-lines (point-min) (point-max))))
+ (enlarge-window (- lines (window-height (selected-window))))
+ (goto-char (point-min))
+ (other-window 1)
+ ))
+
+(defun zwgc-punt ()
+ "Delete the current ZephyrGram from the *zwgc* buffer."
+ (interactive)
+ (let ((window-min-height 1))
+ (display-buffer (get-buffer "*zwgc*"))
+ (delete-region (point-min) (point-max))
+ (widen)
+ (if (not (search-backward "\007" nil t))
+ (delete-windows-on "*zwgc*")
+ (narrow-to-region (point) (point-max))
+ (enlarge-window (- (1+ (count-lines (point-min) (point-max)))
+ (window-height (selected-window))))
+ (goto-char (point-min))
+ )))
+;;
+;; [eichin:19880309.2005EST]
+;; zsend.el
+;; Send zephyrgrams from emacs...
+;;
+
+(defvar *who* "" "last user sent to with zsend")
+
+(defun zsend (&optional who message)
+ "zsend prompts for a user name and a message to send to them as a
+ZephyrGram. If the message is blank, a buffer is popped up to edit the
+message in. If a prefix argument is given, zsend prompts for an
+instance instead. If the user name is blank, the last one is reused."
+ (interactive
+ (list (if current-prefix-arg ; is this portable???
+ (cons 'instance (read-input "Instance:"))
+ (cons 'who (read-input "Who:")))
+; (select-window (minibuffer-window))
+; (enlarge-window 4)
+ (read-input "Message:")))
+ (save-excursion
+ (let ((tempbuf (get-buffer-create " *zephyr*send*")))
+ (switch-to-buffer tempbuf)
+ (local-set-key "\C-c\C-c" 'exit-recursive-edit)
+ (erase-buffer)
+ (if (and (equal (cdr who) "")
+ (equal *who* ""))
+ (message "Please specify user at least once.")
+ (if (not (equal (cdr who) ""))
+ (setq *who* who) ; save *who* for next time
+ (setq who *who*)) ; or, use the old value
+ (if (not (equal message ""))
+ (progn
+ (insert message)
+ (zwrite who))
+ (progn
+ (recursive-edit)
+ (zwrite who)))))))
+
+
+(defun zwrite (who)
+ "Send a ZephyrGram to user WHO, zsend is the user interface to this."
+ (if (eq 'who (car who))
+ (call-process-region (point-min) (point-max) ;range
+ "/usr/athena/zwrite" ;process
+ t ;delete-p
+ t ;output-p
+ nil ;redisplay-p
+ "-q" ;args -- ignore server responses.
+ (cdr who))
+ (call-process-region (point-min) (point-max) ;range
+ "/usr/athena/zwrite" ;process
+ t ;delete-p
+ t ;output-p
+ nil ;redisplay-p
+ "-q" ;args -- ignore server responses.
+ "-i" ;[eichin:19880312.0015EST]
+ (cdr who)))
+ (if (not (equal (point-max) 1))
+ (message (buffer-substring 1 (1- (point-max))))))
+
+; suggested binding (control-meta-z)
+;(global-set-key "\M-\C-z" 'zsend)
+
+
--- /dev/null
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It is one of the source files comprising zwgc, the Zephyr WindowGram
+ * client.
+ *
+ * Created by: Marc Horowitz <marc@athena.mit.edu>
+ *
+ * $Id: zwgc.h,v 1.5 1999/01/22 23:20:52 ghudson Exp $
+ *
+ * Copyright (c) 1989 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+
+#include <zephyr/mit-copyright.h>
+
+#ifdef DEBUG
+
+extern int zwgc_debug;
+#define dprintf(x) if (zwgc_debug) printf(x)
+#define dprintf1(x,y) if (zwgc_debug) printf(x,y)
+
+#else
+
+#define dprintf(x)
+#define dprintf1(x,y)
+
+#endif
--- /dev/null
+! Copyright 1989 Massachusetts Institute of Technology
+!
+! For copying and distribution information, see the file
+! "mit-copyright.h".
+!
+! $Id: zwgc_resources,v 1.7 1999/01/22 23:20:53 ghudson Exp $
+!
+!
+! Zwgc application specific global resources:
+!
+
+*style*substyle.default.fontfamily: default
+*style.message.personal*substyle.title.fontfamily: huge
+
+*style*geometry: +0+0
+
+!
+! The following is the adobe-courier font family. Availiable sizes are
+! 80, 100, 120, 140, 180, and 240. This family used to be courier.
+!
+
+*fontfamily.default.small.roman: -adobe-courier-medium-r-*-80-*-m-*
+*fontfamily.default.small.bold: -adobe-courier-bold-r-*-80-*-m-*
+*fontfamily.default.small.italic: -adobe-courier-medium-o-*-80-*-m-*
+*fontfamily.default.small.bolditalic: -adobe-courier-bold-o-*-80-*-m-*
+
+*fontfamily.default.medium.roman: -adobe-courier-medium-r-*-120-*-m-*
+*fontfamily.default.medium.bold: -adobe-courier-bold-r-*-120-*-m-*
+*fontfamily.default.medium.italic: -adobe-courier-medium-o-*-120-*-m-*
+*fontfamily.default.medium.bolditalic: -adobe-courier-bold-o-*-120-*-m-*
+
+*fontfamily.default.large.roman: -adobe-courier-medium-r-*-240-*-m-*
+*fontfamily.default.large.bold: -adobe-courier-bold-r-*-240-*-m-*
+*fontfamily.default.large.italic: -adobe-courier-medium-o-*-240-*-m-*
+*fontfamily.default.large.bolditalic: -adobe-courier-bold-o-*-240-*-m-*
+
+!
+! The following is the adobe-courier font family. Availiable sizes are
+! 80, 100, 120, 140, 180, and 240. This family used to be courier.
+!
+
+*fontfamily.courier.small.roman: *adobe-courier-medium-r-*-80-*-m-*
+*fontfamily.courier.small.bold: *adobe-courier-bold-r-*-80-*-m-*
+*fontfamily.courier.small.italic: *adobe-courier-medium-o-*-80-*-m-*
+*fontfamily.courier.small.bolditalic: *adobe-courier-bold-o-*-80-*-m-*
+
+*fontfamily.courier.medium.roman: *adobe-courier-medium-r-*-120-*-m-*
+*fontfamily.courier.medium.bold: *adobe-courier-bold-r-*-120-*-m-*
+*fontfamily.courier.medium.italic: *adobe-courier-medium-o-*-120-*-m-*
+*fontfamily.courier.medium.bolditalic: *adobe-courier-bold-o-*-120-*-m-*
+
+*fontfamily.courier.large.roman: *adobe-courier-medium-r-*-240-*-m-*
+*fontfamily.courier.large.bold: *adobe-courier-bold-r-*-240-*-m-*
+*fontfamily.courier.large.italic: *adobe-courier-medium-o-*-240-*-m-*
+*fontfamily.courier.large.bolditalic: *adobe-courier-bold-o-*-240-*-m-*
+
+!
+! The following is the adobe-times font family. Availiable sizes are
+! 80, 100, 120, 140, 180, and 240. This family used to be times-roman.
+!
+*fontfamily.times.small.roman: *adobe-times-medium-r-*-80-*-p-*
+*fontfamily.times.small.bold: *adobe-times-bold-r-*-80-*-p-*
+*fontfamily.times.small.italic: *adobe-times-medium-i-*-80-*-p-*
+*fontfamily.times.small.bolditalic: *adobe-times-bold-i-*-80-*-p-*
+
+*fontfamily.times.medium.roman: *adobe-times-medium-r-*-120-*-p-*
+*fontfamily.times.medium.bold: *adobe-times-bold-r-*-120-*-p-*
+*fontfamily.times.medium.italic: *adobe-times-medium-i-*-120-*-p-*
+*fontfamily.times.medium.bolditalic: *adobe-times-bold-i-*-120-*-p-*
+
+*fontfamily.times.large.roman: *adobe-times-medium-r-*-240-*-p-*
+*fontfamily.times.large.bold: *adobe-times-bold-r-*-240-*-p-*
+*fontfamily.times.large.italic: *adobe-times-medium-i-*-240-*-p-*
+*fontfamily.times.large.bolditalic: *adobe-times-bold-i-*-240-*-p-*
+
+!
+! The following is the adobe-helvetica font family. Availiable sizes are
+! 80, 100, 120, 140, 180, and 240. This family used to be helvetica.
+!
+*fontfamily.helvetica.small.roman: *adobe-helvetica-medium-r-*-80-*-p-*
+*fontfamily.helvetica.small.bold: *adobe-helvetica-bold-r-*-80-*-p-*
+*fontfamily.helvetica.small.italic: *adobe-helvetica-medium-o-*-80-*-p-*
+*fontfamily.helvetica.small.bolditalic: *adobe-helvetica-bold-o-*-80-*-p-*
+
+*fontfamily.helvetica.medium.roman: *adobe-helvetica-medium-r-*-120-*-p-*
+*fontfamily.helvetica.medium.bold: *adobe-helvetica-bold-r-*-120-*-p-*
+*fontfamily.helvetica.medium.italic: *adobe-helvetica-medium-o-*-120-*-p-*
+*fontfamily.helvetica.medium.bolditalic:*adobe-helvetica-bold-o-*-120-*-p-*
+
+*fontfamily.helvetica.large.roman: *adobe-helvetica-medium-r-*-240-*-p-*
+*fontfamily.helvetica.large.bold: *adobe-helvetica-bold-r-*-240-*-p-*
+*fontfamily.helvetica.large.italic: *adobe-helvetica-medium-o-*-240-*-p-*
+*fontfamily.helvetica.large.bolditalic: *adobe-helvetica-bold-o-*-240-*-p-*
+
+!
+! Quick hack...
+!
+
+*fontfamily.huge*roman: *bitstream-charter-medium-r-*-33-*-p-*
+*fontfamily.huge*bold: *bitstream-charter-bold-r-*-33-*-p-*
+*fontfamily.huge*italic: *bitstream-charter-medium-i-*-33-*-p-*
+*fontfamily.huge*bolditalic: *bitstream-charter-bold-i-*-33-*-p-*