[comp.sources.x] v09i085: xrolo -- an XView rolodex, Part02/03

luis@rice.edu (Luis Soltero) (10/11/90)

Submitted-by: Luis Soltero <luis@rice.edu>
Posting-number: Volume 9, Issue 85
Archive-name: xrolo/part02

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 2 (of 3)."
# Contents:  Makefile cards.c
# Wrapped by luis@oort on Wed Oct 10 15:56:18 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(9108 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
X# Makefile generated by imake - do not edit!
X# $XConsortium: imake.c,v 1.51 89/12/12 12:37:30 jim Exp $
X#
X# The cpp used on this machine replaces all newlines and multiple tabs and
X# spaces in a macro expansion with a single space.  Imake tries to compensate
X# for this, but is not always successful.
X#
X
X###########################################################################
X# Makefile generated from "Imake.tmpl" and </tmp/IIf.a13794>
X# $XConsortium: Imake.tmpl,v 1.77 89/12/18 17:01:37 jim Exp $
X#
X# Platform-specific parameters may be set in the appropriate .cf
X# configuration files.  Site-wide parameters may be set in the file
X# site.def.  Full rebuilds are recommended if any parameters are changed.
X#
X# If your C preprocessor doesn't define any unique symbols, you'll need
X# to set BOOTSTRAPCFLAGS when rebuilding imake (usually when doing
X# "make Makefile", "make Makefiles", or "make World").
X#
X# If you absolutely can't get imake to work, you'll need to set the
X# variables at the top of each Makefile as well as the dependencies at the
X# bottom (makedepend will do this automatically).
X#
X
X###########################################################################
X# platform-specific configuration parameters - edit sun.cf to change
X
X# platform:  $XConsortium: sun.cf,v 1.38 89/12/23 16:10:10 jim Exp $
X# operating system:  SunOS 4.0.3
X
X###########################################################################
X# site-specific configuration parameters - edit site.def to change
X
X# site:  $XConsortium: site.def,v 1.21 89/12/06 11:46:50 jim Exp $
X
X            SHELL = /bin/sh
X
X              TOP = .
X      CURRENT_DIR = .
X
X               AR = ar cq
X  BOOTSTRAPCFLAGS =
X               CC = cc
X
X         COMPRESS = compress
X              CPP = /lib/cpp $(STD_CPP_DEFINES)
X    PREPROCESSCMD = cc -E $(STD_CPP_DEFINES)
X          INSTALL = install
X               LD = ld
X             LINT = lint
X      LINTLIBFLAG = -C
X         LINTOPTS = -axz
X               LN = ln -s
X             MAKE = make
X               MV = mv
X               CP = cp
X           RANLIB = ranlib
X  RANLIBINSTFLAGS =
X               RM = rm -f
X     STD_INCLUDES =
X  STD_CPP_DEFINES =
X      STD_DEFINES =
X EXTRA_LOAD_FLAGS =
X  EXTRA_LIBRARIES =
X             TAGS = ctags
X
X    SHAREDCODEDEF = -DSHAREDCODE
X         SHLIBDEF = -DSUNSHLIB
X
X    PROTO_DEFINES =
X
X     INSTPGMFLAGS =
X
X     INSTBINFLAGS = -m 0755
X     INSTUIDFLAGS = -m 4755
X     INSTLIBFLAGS = -m 0664
X     INSTINCFLAGS = -m 0444
X     INSTMANFLAGS = -m 0444
X     INSTDATFLAGS = -m 0444
X    INSTKMEMFLAGS = -m 4755
X
X          DESTDIR =
X
X     TOP_INCLUDES = -I$(INCROOT)
X
X      CDEBUGFLAGS = -O
X        CCOPTIONS = -pipe
X      COMPATFLAGS =
X
X      ALLINCLUDES = $(STD_INCLUDES) $(TOP_INCLUDES) $(INCLUDES) $(EXTRA_INCLUDES)
X       ALLDEFINES = $(ALLINCLUDES) $(STD_DEFINES) $(PROTO_DEFINES) $(DEFINES) $(COMPATFLAGS)
X           CFLAGS = $(CDEBUGFLAGS) $(CCOPTIONS) $(ALLDEFINES)
X        LINTFLAGS = $(LINTOPTS) -DLINT $(ALLDEFINES)
X           LDLIBS = $(SYS_LIBRARIES) $(EXTRA_LIBRARIES)
X        LDOPTIONS = $(CDEBUGFLAGS) $(CCOPTIONS)
X   LDCOMBINEFLAGS = -X -r
X
X        MACROFILE = sun.cf
X           RM_CMD = $(RM) *.CKP *.ln *.BAK *.bak *.o core errs ,* *~ *.a .emacs_* tags TAGS make.log MakeOut
X
X    IMAKE_DEFINES =
X
X         IRULESRC = $(CONFIGDIR)
X        IMAKE_CMD = $(IMAKE) -DUseInstalled -I$(IRULESRC) $(IMAKE_DEFINES)
X
X     ICONFIGFILES = $(IRULESRC)/Imake.tmpl $(IRULESRC)/Imake.rules \
X			$(IRULESRC)/Project.tmpl $(IRULESRC)/site.def \
X			$(IRULESRC)/$(MACROFILE) $(EXTRA_ICONFIGFILES)
X
X###########################################################################
X# X Window System Build Parameters
X# $XConsortium: Project.tmpl,v 1.63 89/12/18 16:46:44 jim Exp $
X
X###########################################################################
X# X Window System make variables; this need to be coordinated with rules
X# $XConsortium: Project.tmpl,v 1.63 89/12/18 16:46:44 jim Exp $
X
X          PATHSEP = /
X        USRLIBDIR = $(DESTDIR)/usr/lib
X           BINDIR = $(DESTDIR)/usr/bin/X11
X          INCROOT = $(DESTDIR)/usr/include
X     BUILDINCROOT = $(TOP)
X      BUILDINCDIR = $(BUILDINCROOT)/X11
X      BUILDINCTOP = ..
X           INCDIR = $(INCROOT)/X11
X           ADMDIR = $(DESTDIR)/usr/adm
X           LIBDIR = $(USRLIBDIR)/X11
X        CONFIGDIR = $(LIBDIR)/config
X       LINTLIBDIR = $(USRLIBDIR)/lint
X
X          FONTDIR = $(LIBDIR)/fonts
X         XINITDIR = $(LIBDIR)/xinit
X           XDMDIR = $(LIBDIR)/xdm
X           AWMDIR = $(LIBDIR)/awm
X           TWMDIR = $(LIBDIR)/twm
X           GWMDIR = $(LIBDIR)/gwm
X          MANPATH = $(DESTDIR)/usr/man
X    MANSOURCEPATH = $(MANPATH)/man
X           MANDIR = $(MANSOURCEPATH)n
X        LIBMANDIR = $(MANSOURCEPATH)3
X      XAPPLOADDIR = $(LIBDIR)/app-defaults
X
X        SOXLIBREV = 4.2
X          SOXTREV = 4.0
X         SOXAWREV = 4.0
X        SOOLDXREV = 4.0
X         SOXMUREV = 4.0
X        SOXEXTREV = 4.0
X
X       FONTCFLAGS = -t
X
X     INSTAPPFLAGS = $(INSTDATFLAGS)
X
X            IMAKE = imake
X           DEPEND = makedepend
X              RGB = rgb
X            FONTC = bdftosnf
X        MKFONTDIR = mkfontdir
X        MKDIRHIER = /bin/sh $(BINDIR)/mkdirhier.sh
X
X        CONFIGSRC = $(TOP)/config
X        CLIENTSRC = $(TOP)/clients
X          DEMOSRC = $(TOP)/demos
X           LIBSRC = $(TOP)/lib
X          FONTSRC = $(TOP)/fonts
X       INCLUDESRC = $(TOP)/X11
X        SERVERSRC = $(TOP)/server
X          UTILSRC = $(TOP)/util
X        SCRIPTSRC = $(UTILSRC)/scripts
X       EXAMPLESRC = $(TOP)/examples
X       CONTRIBSRC = $(TOP)/../contrib
X           DOCSRC = $(TOP)/doc
X           RGBSRC = $(TOP)/rgb
X        DEPENDSRC = $(UTILSRC)/makedepend
X         IMAKESRC = $(CONFIGSRC)
X         XAUTHSRC = $(LIBSRC)/Xau
X          XLIBSRC = $(LIBSRC)/X
X           XMUSRC = $(LIBSRC)/Xmu
X       TOOLKITSRC = $(LIBSRC)/Xt
X       AWIDGETSRC = $(LIBSRC)/Xaw
X       OLDXLIBSRC = $(LIBSRC)/oldX
X      XDMCPLIBSRC = $(LIBSRC)/Xdmcp
X      BDFTOSNFSRC = $(FONTSRC)/bdftosnf
X     MKFONTDIRSRC = $(FONTSRC)/mkfontdir
X     EXTENSIONSRC = $(TOP)/extensions
X
X  DEPEXTENSIONLIB = $(USRLIBDIR)/libXext.a
X     EXTENSIONLIB =  -lXext
X
X          DEPXLIB = $(DEPEXTENSIONLIB)
X             XLIB = $(EXTENSIONLIB) -lX11
X
X      DEPXAUTHLIB = $(USRLIBDIR)/libXau.a
X         XAUTHLIB =  -lXau
X
X        DEPXMULIB =
X           XMULIB = -lXmu
X
X       DEPOLDXLIB =
X          OLDXLIB = -loldX
X
X      DEPXTOOLLIB =
X         XTOOLLIB = -lXt
X
X        DEPXAWLIB =
X           XAWLIB = -lXaw
X
X LINTEXTENSIONLIB = $(USRLIBDIR)/llib-lXext.ln
X         LINTXLIB = $(USRLIBDIR)/llib-lX11.ln
X          LINTXMU = $(USRLIBDIR)/llib-lXmu.ln
X        LINTXTOOL = $(USRLIBDIR)/llib-lXt.ln
X          LINTXAW = $(USRLIBDIR)/llib-lXaw.ln
X
X        XWLIBSRC = $(CONTRIBSRC)/toolkits/Xw
X        DEPXWLIB = $(USRLIBDIR)/libXw.a
X        XWLIB =  -lXw
X
X          DEPLIBS = $(DEPXAWLIB) $(DEPXMULIB) $(DEPXTOOLLIB) $(DEPXLIB)
X
X         DEPLIBS1 = $(DEPLIBS)
X         DEPLIBS2 = $(DEPLIBS)
X         DEPLIBS3 = $(DEPLIBS)
X
X###########################################################################
X# Imake rules for building libraries, programs, scripts, and data files
X# rules:  $XConsortium: Imake.rules,v 1.67 89/12/18 17:14:15 jim Exp $
X
X###########################################################################
X# start of Imakefile
X
X#
X# xrolo - an XView tool to implement a rolodex-style list of notes.
X#
X# --Luis Soltero (luis@rice.edu), 10/10/90
X#
X
X#
X# linking against openlook libraries on suns after patching ol_button.c.
X# see README for details.
XLOCAL_LIBRARIES = -L$$OPENWINHOME/lib -lxview -Bstatic -lolgx -Bdynamic
X
X#
X# linking against XView libraries compiled from mit distribution after
X# patching ol_button.c. see README for details.
X# LOCAL_LIBRARIES = -lxview -lolgx
X
X           SRCS = main.c panel.c cards.c popup.c
X           OBJS = main.o panel.o cards.o popup.o
X       INCLUDES = -I$$OPENWINHOME/include
X        DEFINES = -DSTANDALONE
X
Xall:: xrolo
X
Xxrolo: $(OBJS) $(DEPLIBS)
X	$(RM) $@
X	$(CC) -o $@ $(OBJS) $(LDOPTIONS)  $(LOCAL_LIBRARIES) $(LDLIBS)  $(XLIB) $(EXTRA_LOAD_FLAGS)
X
Xclean::
X	$(RM) xrolo
X
Xinstall:: xrolo
X	$(INSTALL) -c $(INSTPGMFLAGS)   xrolo  $(BINDIR)
X
Xinstall.man:: xrolo.man
X	$(INSTALL) -c $(INSTMANFLAGS) xrolo.man  $(MANDIR)/xrolo.n
X
Xdepend::
X	$(DEPEND) -s "# DO NOT DELETE" -- $(ALLDEFINES) -- $(SRCS)
X
X###########################################################################
X# common rules for all Makefiles - do not edit
X
Xemptyrule::
X
Xclean::
X	$(RM_CMD) \#*
X
XMakefile::
X	-@if [ -f Makefile ]; then \
X	echo "	$(RM) Makefile.bak; $(MV) Makefile Makefile.bak"; \
X	$(RM) Makefile.bak; $(MV) Makefile Makefile.bak; \
X	else exit 0; fi
X	$(IMAKE_CMD) -DTOPDIR=$(TOP) -DCURDIR=$(CURRENT_DIR)
X
Xtags::
X	$(TAGS) -w *.[ch]
X	$(TAGS) -xw *.[ch] > TAGS
X
X###########################################################################
X# empty rules for directories that do not have SUBDIRS - do not edit
X
Xinstall::
X	@echo "install in $(CURRENT_DIR) done"
X
Xinstall.man::
X	@echo "install.man in $(CURRENT_DIR) done"
X
XMakefiles::
X
Xincludes::
X
X###########################################################################
X# dependencies generated by makedepend
X
END_OF_FILE
if test 9108 -ne `wc -c <'Makefile'`; then
    echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
fi
if test -f 'cards.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'cards.c'\"
else
echo shar: Extracting \"'cards.c'\" \(20514 characters\)
sed "s/^X//" >'cards.c' <<'END_OF_FILE'
X#ifndef lint
Xstatic char sccsid[] = "@(#)cards.c	2.2 8/14/88";
X#endif
X
X/*
X *	Routines for manipulating cards
X */
X
X
X/*
X * -------------------------------------------------------------------------
X *	ROLO - A Sun Tool to implement a Rolodex-style list of notes
X *
X *	This code manipulates "cards" in a visual manner approximating
X *	a rolodex file.  All the cards are stored in one real file, the
X *	cards are seperated by a ^L (form-feed).  The default path
X *	name is $HOME/.rolo.  A different pathname may be specified at
X *	startup on the command line.  The pathname is relative to the
X *	user's home directory.
X *
X *	Due to bugs in the 3.0 distribution, especially with text subwindows,
X *	this code is only guaranteed to compile and run properly with 3.2
X *	or greater.
X *
X *	This code is public domain, anyone and everyone is welcome to it.
X *	All I ask is that my name and this notice remain on it.  If Sun would
X *	like to bundle it with their product they are welcome to do so,
X *	I only ask that the sources be included in the binary distribution.
X *
X *	Please return any fixes, improvements, gripes, etc to me.
X *
X *	Ron Hitchens		ronbo@vixen.uucp
X *	March 1987 (V1.0)	hitchens@cs.utexas.edu
X *	August 1988 (V2.0)
X * -------------------------------------------------------------------------
X */
X
X
X
X#include <stdio.h>
X#include <xview/xview.h>
X#include <sys/file.h>
X#include <ctype.h>
X
X#include "defs.h"
X
X
X/* --------------------------- Exports ------------------------------------- */
X
Xint			need_save = FALSE;
X
Xstruct card		*first = NULL_CARD, *last, *current;
X
Xstruct card		*make_card (), *insert_card (), *undelete_card (),
X			*pop_card ();
X
Xvoid			dispose_card (), push_card (), nuke_active_cards ();
X
Xchar			*first_char (), *check_args ();
X
Xcaddr_t			undel_menu_card ();
X
X/* --------------------------- Imports ------------------------------------- */
X
Xextern char		*rolofile;
X
Xextern void		show_card (), set_slider_max ();
X
Xextern char		*malloc(), *realloc (), *calloc (), *getenv(),
X			*strcpy(), *strcat(), *strncpy (), *index (),
X			*sys_errlist [];
X
Xextern int		errno;
X
X
X/* --------------------------- Locals -------------------------------------- */
X
Xstatic struct card	*dead;
X
Xstatic int		eof = FALSE;
X
Xstatic char		*dummy_card = DUMMY_CARD_CONTENTS;
X
Xstatic char		*trim (), *catbuf (), *get_card ();
X
Xstatic void		load_rolo (), write_err ();
X
X
X/* ------------------------------------------------------------------------- */
X
X
X/*
X *	Initialize the card list.  First, we cd to the home directory,
X *	then we try to open the named file.  If we don't find one then
X *	we create a new empty one and re-open it.  Once we have the file
X *	open, we try to load cards in.  If no cards are loaded by load_rolo(),
X *	then we create a dummy card out of thin air.
X */
X
Xvoid
Xinit_rolo (filename)
X	char	*filename;
X{
X	char	*c;
X	int	fd;
X	FILE	*f;
X
X	(void) chdir (getenv ("HOME"));
X	if ((f = fopen (filename, "r")) == NULL) {
X		if ((fd = open (filename, O_CREAT, NEWMODE)) < 0) {
X			msg ("init_rolo: can't create %s: %s", filename,
X				sys_errlist [errno]);
X			goto skipload;
X		}
X		(void) close (fd);
X		if ((f = fopen (filename, "r")) == NULL) {
X			msg ("init_rolo: can't reopen new %s\n",
X				filename);
X			goto skipload;
X		}
X	}
X	load_rolo (f);
X	(void) fclose (f);
X
Xskipload:
X	if (first == NULL_CARD) {
X		c = malloc (strlen (dummy_card) + 1);
X		(void) strcpy (c, dummy_card);
X		(void) insert_card (make_card (c), NULL_CARD);
X		set_slider_max (renumber (first));
X	}
X
X	show_card (first);
X}
X
X
X/*
X *	Concatenate the second string onto the first.  If the first string
X *	pointer is null, copy and return the second.
X */
X
Xstatic
Xchar *
Xcatbuf (c, p)
X	char	*c, *p;
X{
X	if (c == NULL) {
X		c = malloc (strlen (p) + 1);
X		return (strcpy (c, p));
X	} else {
X		c = realloc (c, strlen (c) + strlen (p) + 1);
X		return (strcat (c, p));
X	}
X}
X
X
X/*
X *	Return the pointer to the first non-blank char in the given string.
X *	If the string is all white space, a pointer to the trailing null
X *	will be returned.
X */
X
Xchar *
Xfirst_char (c)
X	char	*c;
X{
X	while ((*c != '\0') && (isspace(*c)))
X		c++;
X	return (c);
X}
X
X
X/*
X *	Trim trailing blanks from a string.  The trimmed blanks, if any, are
X *	not freed.  Return the address of the string passed.  If a null
X *	pointer is passed, this is a no-op.
X */
X
Xstatic
Xchar *
Xtrim (c)
X	char	*c;
X{
X	int	i, l;
X
X	if (c == NULL) {
X		return (c);
X	}
X	l = strlen (c) - 1;
X
X	for (i = l; i >= 0; i--) {
X		if ( ! isspace(c [i]))
X			break;
X	}
X
X	c [++i] = '\0';
X	return (c);
X}
X
X
X/*
X *	Get one card from the provided file.  Data is read, a line at a time,
X *	until a form-feed char is encountered.  The FF is expected to be
X *	on a line by itself, the cards are written out that way.  Blanks cards
X *	will be discarded.
X *	The #ifdef'ed code will trim off leading blank lines, it is disabled
X *	since users may wish to include leading white space in a card.  The
X *	routines which look at card values (such as for sorting) will skip
X *	leading white space anyway.
X */
X
Xstatic
Xchar *
Xget_card (f)
X	FILE	*f;
X{
X	char	*c;
X	char	buf [1024];
X
X	if (eof) {
X		return (NULL);
X	}
X	c = NULL;
X	while (fgets (buf, sizeof buf, f) != NULL) {
X		if (buf [0] == '\f') {		/* card separator */
X			if (c == NULL || strlen (c) == 0) {
X				if (c != NULL) {
X					free (c);
X				}
X				c = NULL;
X				continue;
X			}
X			return (trim(c));
X		}
X#ifdef notdef
X		if (c == NULL) {		/* drop leading blank lines */
X			int	i, j, allblank;
X
X			j = strlen (buf);
X			allblank = TRUE;
X			for (i = 0; i < j; i++) {
X				if ( ! isspace(buf[i])) {
X					allblank = FALSE;
X					break;
X				}
X			}
X			if (allblank) {
X				continue;
X			}
X		}
X#endif
X		c = catbuf (c, buf);
X	}
X	eof = TRUE;
X	return (trim(c));
X}
X
X
X/*
X *	Load all the cards from the provided file.
X */
X
Xstatic
Xvoid
Xload_rolo (f)
X	FILE	*f;
X{
X	char	*c;
X
X	eof = FALSE;
X	first = last = current = dead = NULL_CARD;
X	while ((c = get_card (f)) != NULL) {
X		(void) insert_card (make_card (c), last);
X	}
X	set_slider_max (renumber (first));
X	need_save = FALSE;
X}
X
X
X/*
X *	Renumber the cards in the list, begnning with the card pointed to
X *	by p (this should normally be the first card in the list).  This must
X *	be done whenever the population of the list changes, such as adding
X *	or deleting a card.  The first card is number 1.  The card number
X *	is generally used for display to human types.  The number of cards
X *	in the list is returned.
X */
X
Xint
Xrenumber (p)
X	struct	card	*p;
X{
X	int	i;
X
X	for (i = 0; p != NULL_CARD; p = p->c_next)
X		p->c_num = ++i;
X
X	return (i);
X}
X
X
X/*
X *	Make a new card, and place the text pointed to by c into it.  If
X *	the text pointer is null, allocate an empty string for the text,
X *	the text pointer in the card's c_text slot must be free-able later
X *	if the user modifies the text.  If a text pointer is provided it
X *	is assumed to have been malloc'ed and is not copied.  The address of
X *	the new card is returned, it is not inserted into the list.
X */
X
Xstruct card *
Xmake_card (c)
X	char	*c;
X{
X	struct	card	*p;
X
X	p = (struct card *) malloc (sizeof (struct card));
X	if (p == NULL_CARD) {
X		msg ("Can't allocate memory for new card");
X		return (NULL);
X	}
X
X	p->c_next = p->c_prev = NULL_CARD;
X	if (c == NULL) {
X		p->c_text = malloc (1);
X		*p->c_text = '\0';
X	} else {
X		p->c_text = c;
X	}
X	return (p);
X}
X
X
X/*
X *	Insert the card p into the list of cards after the card pointed to
X *	by after.  If after is null, then insert before the first card.  If
X *	the list is initially empty, after is ignored and p is made the only
X *	card in the list.  The card list is doubly linked, standard textbook
X *	stuff.  The global pointers first, last and current are set as
X *	side-effects by this function.  The address of the card being
X *	inserted is returned as a convenience to the caller (see undelete_card)
X */
X
Xstruct card *
Xinsert_card (p, after)
X	struct	card	*p, *after;
X{
X	if (p == NULL_CARD) {
X		return (p);		/* paranoid programming */
X	}
X
X	if (first == NULL_CARD) {	/* list is initially empty */
X		p->c_next = p->c_prev = NULL;
X		first = last = current = p;
X		return (p);
X	}
X
X	if (after == NULL_CARD) {	/* at front of list */
X		first->c_prev = p;
X		p->c_next = first;
X		p->c_prev = NULL_CARD;
X		first = p;
X		return (p);
X	}
X
X	p->c_next = after->c_next;
X	p->c_prev = after;
X
X	if (after == last) {
X		last = p;		/* at end of list */
X	} else {
X		after->c_next->c_prev = p;
X	}
X
X	after->c_next = p;
X
X	return (p);
X}
X
X
X/*
X *	Delete the card pointed to by p from the list.  More textbook stuff
X *	here.  The card is not destroyed, it is clipped out of the list and
X *	pushed onto a stack of deleted cards.
X */
X
Xvoid
Xdelete_card (p)
X	struct	card	*p;
X{
X	if (p == first) {
X		first = p->c_next;
X	} else {
X		p->c_prev->c_next = p->c_next;
X	}
X
X	if (p == last) {
X		last = p->c_prev;
X	} else {
X		p->c_next->c_prev = p->c_prev;
X	}
X
X	push_card (p);
X}
X
X
X/*
X *	Undelete a card.  The top card on the deleted stack is popped and
X *	inserted into the list after the card pointed to by after.
X */
X
Xstruct card *
Xundelete_card (after)
X	struct	card	*after;
X{
X	struct	card	*p;
X
X	p = pop_card ();
X	if (p == NULL_CARD) {
X		return (NULL_CARD);
X	}
X	return (insert_card (p, after));
X}
X
X
X/*
X *	Destroy the entire list and all the cards in it.  The cards on the
X *	deleted stack are not disturbed.
X */
X
Xvoid
Xnuke_active_cards ()
X{
X	struct card	*p = first;
X
X	while (p != NULL_CARD) {
X		struct card	*q;
X
X		q = p->c_next;
X		dispose_card (p);
X		p = q;
X	}
X
X	first = last = current = NULL_CARD;
X}
X
X
X/*
X *	Destroy the card pointed to by p, and free its storage.  The pointer
X *	p is no longer valid after calling this function.
X */
X
Xvoid
Xdispose_card (p)
X	struct	card	*p;
X{
X	free (p->c_text);
X	free (p);
X}
X
X
X/*
X *	Push the card pointed to by p onto the stack of deleted cards. Double
X *	link the new node onto the front of the list so that any arbitrary
X *	card can be easily unlinked without needing to scan the list.
X */
X
Xvoid
Xpush_card (p)
X	struct	card	*p;
X{
X	if (strlen (first_char (p->c_text)) == 0) {
X		dispose_card (p);
X		return;			/* don't keep empty cards */
X	}
X
X	p->c_prev = NULL_CARD;		/* this guy will be first in the list */
X	p->c_next = dead;		/* current #1 will be new #2 */
X	if (dead != NULL_CARD) {	/* if list was non-empty, back-point */
X		dead->c_prev = p;	/* the first one to the new one */
X	}
X	dead = p;			/* head of list is now the new one */
X}
X
X
X/*
X *	Pop the top card off the deleted stack and and return its address,
X *	or a nil pointer if the stack is empty.
X */
X
Xstruct card *
Xpop_card ()
X{
X	struct	card	*p;
X
X	if (dead == NULL_CARD) {
X		return (NULL_CARD);	/* sorry, fresh out */
X	}
X
X	p = dead;			/* returning first one in the list */
X	dead = p->c_next;		/* head of list is now second node */
X	if (dead != NULL_CARD) {	/* If any nodes left in the list, */
X		dead->c_prev = NULL_CARD; /* clear back pointer of first one */
X	}
X
X	return (p);			/* here's the top card, Bud */
X}
X
X
X/*
X *	Remove the card pointed to by p from the deleted stack.  The function
X *	is called by the action proc for the undeleted pullright menu.  The
X *	node pointed to may be any one of the cards on the stack.  The node
X *	is clipped out of the deleted list.
X */
X
Xvoid
Xunstack_card (p)
X	struct	card	*p;
X{
X	if (p == dead) {		/* is it the first one? */
X		dead = p->c_next;		/* head of list is now next */
X	} else {
X		/* point the upstream guy at the downstream guy */
X		p->c_prev->c_next = p->c_next;
X	}
X
X	if (p->c_next != NULL_CARD) {
X		/* point the downstream guy at the one upstream of me */
X		p->c_next->c_prev = p->c_prev;
X	}
X
X	p->c_next = p->c_prev = NULL_CARD;	/* this guy is alone now */
X}
X
X
X/*
X *	Write the contents of all the cards out to disk.  Blank cards are
X *	not written out, and trailing white space is trimmed from the ones
X *	which are written.  The cards are seperated in the file by a
X *	form-feed (^L) on a line by itself.
X */
X
Xvoid
Xdump_rolo (p, fn)
X	struct	card	*p;
X	char	*fn;
X{
X	int	fd;
X
X	fd = open (fn, O_WRONLY|O_TRUNC, NEWMODE);
X	if (fd < 0) {
X		msg ("Can't open %s for save: %s", fn,
X			sys_errlist [errno]);
X		return;
X	}
X
X	for ( ; p != NULL_CARD; p = p->c_next) {
X		if (strlen (trim (p->c_text)) == 0)
X			continue;
X		if (write (fd, p->c_text, strlen (p->c_text)) == -1) {
X			write_err (fd, fn);
X			return;
X		}
X		if (write (fd, "\n\f\n", 3) == -1) {
X			write_err (fd, fn);
X			return;
X		}
X	}
X
X	(void) close (fd);
X	need_save = FALSE;
X}
X
X
X/*
X *	Write the cards out to the named file.  If the file exists already,
X *	ask if it's ok to overwrite.  Check permissions.  Pre-create the
X *	file if it doesn't already exist.
X */
X
Xvoid
Xwrite_rolo (filename)
X	char	*filename;
X{
X	int	fd;
X
X	if (access (filename, F_OK) == -1) {
X		if ((fd = open (filename, O_CREAT, NEWMODE)) < 0) {
X			msg ("write_rolo: can't create %s: %s", filename,
X				sys_errlist [errno]);
X			return;
X		}
X		(void) close (fd);
X		goto do_dump;
X	}
X
X	if (access (filename, W_OK) == -1) {
X		msg ("Sorry, you don't have write access to %s", filename);
X		return;
X	}
X
X	if (confirm ("%s exists, ok to overwrite?", filename) == FALSE) {
X		return;
X	}
X
Xdo_dump:
X	dump_rolo (first, filename);
X	rolofile = filename;
X	update_num_display (current->c_num);
X}
X
X
X/*
X *	Read in a new Rolo card file.  Check for readability before throwing
X *	away the old cards.
X */
X
Xvoid
Xread_rolo (filename)
X	char	*filename;
X{
X	if (access (filename, F_OK) == -1) {
X		msg ("Sorry, %s doesn't exist.", filename);
X		return;
X	}
X
X	if (access (filename, R_OK) == -1) {
X		msg ("Sorry, you don't have permission to read %s", filename);
X		return;
X	}
X
X	nuke_active_cards ();
X	init_rolo (filename);
X	rolofile = filename;
X	update_num_display (current->c_num);
X}
X
X
X/*
X *	Common error complaint routine used by dump_rolo above.
X */
X
Xstatic
Xvoid
Xwrite_err (fd, fn)
X	int	fd;
X	char	*fn;
X{
X	(void) close (fd);
X	msg ("Couldn't save to %s: %s", fn, sys_errlist [errno]);
X}
X
X
X/*
X *	Glue routine for the qsort compare routine.  Dereference the card
X *	pointers and call strcmp to compare the text of the cards, beginning
X *	with the first non-blank character in the card.
X */
X
Xstatic
Xint
Xsort_up (pp1, pp2)
X	struct card	**pp1, **pp2;
X{
X	return (strcmp (first_char ((*pp1)->c_text),
X		first_char ((*pp2)->c_text)));
X}
X
X
X/*
X *	Same as above, but for sort descending.  Strcmp is called with the
X *	string pointers in the opposite order to invert the sense of the
X *	comparison.
X */
X
Xstatic
Xint
Xsort_down (pp1, pp2)
X	struct card	**pp1, **pp2;
X{
X	return (strcmp (first_char ((*pp2)->c_text),
X		first_char ((*pp1)->c_text)));
X}
X
X
X/*
X *	Sort the cards in the list into alphabetical order (actually plain
X *	ascii order).  Space is allocated for an array of all the pointers
X *	to the cards in the list.  The addresses of all the cards are then
X *	placed in the array and qsort is called to sort them in order.  The
X *	list is then made empty by simply clearing the anchor pointers to
X *	the list.  Each card is then inserted back into the list in the order
X *	they now occupy in the sorted array of pointers.  The array is then
X *	freed.
X */
X
Xvoid
Xsort_cards (descend)
X	int		descend;
X{
X	struct	card	**a, *p;
X	int		i, cards = last->c_num;
X
X	if (cards == 1) {
X		msg ("Only have one active card, no need to sort");
X		return;
X	}
X
X	a = (struct card **) calloc (cards, sizeof (struct card *));
X	for (p = first, i = 0; p != NULL_CARD; p = p->c_next, i++)
X		a [i] = p;
X
X	if (descend == TRUE) {
X		qsort ((char *)a, cards, sizeof (struct card *), sort_down);
X	} else {
X		qsort ((char *)a, cards, sizeof (struct card *), sort_up);
X	}
X
X	first = last = NULL_CARD;
X	for (i = 0; i < cards; i++) {
X		(a [i])->c_num = i + 1;			/* renumber as we go */
X		(void) insert_card (a [i], last);
X	}
X
X	show_card (first);				/* display the first */
X
X	free (a);
X}
X
X
X/*
X *	Menu item gen proc for the undelete pullright item.  Simply
X *	checks to see if the delete stack is empty.  If so, the item
X *	is disabled, else it is enabled.
X *	Due to the exceedingly brain-damaged way that notification is
X *	done for pullright menus, it is necesssary to do some rather
X *	silly tap-dancing to make everything work.  One such kludge is
X *	to use the client data slot in the base menu on the delete menu
X *	as a temporary storage area for the selected item from the pullright.
X *	So, when this function is called with MENU_DISPLAY, we clear the
X *	client data slot of our parent, since we are beginning a new menu
X *	cycle.  Any snide remarks about the convolution of the following
X *	three menu functions will be cheerfully ignored until and unless
X *	you can show me some better code that implements the same
X *	functionality and gets all the combinations right.  It's much
X *	trickier that it seems on the surface.  Sun really blew it on
X *	this one, it's basically impossible to properly manage dynamically
X *	generated menus with the notification scheme as it is.
X */
X
XMenu_item
Xcheck_stack (menu_item, operation)
X	Menu_item	menu_item;
X	Menu_generate	operation;
X{
X	if (operation == MENU_DISPLAY) {
X		if (dead == NULL_CARD) {
X			xv_set (menu_item, MENU_INACTIVE, TRUE, 0);
X		} else {
X			xv_set (menu_item, MENU_INACTIVE, FALSE, 0);
X		}
X	}
X
X	return (menu_item);
X}
X
Xvoid undel_before_proc(menu, menu_item)
XMenu menu;
XMenu menu_item;
X{
X	undel_menu_card(menu_item, 1);
X}
X
Xvoid undel_proc(menu, menu_item)
XMenu menu;
XMenu_item menu_item;
X{
X	undel_menu_card(menu_item, 0);
X}
X
XMenu gen_undelete_menu(proc)
Xvoid *proc;
X{
X	/*
X	 * The user has just pulled right on the item, so now we need
X	 * to build the menu which will be displayed to the right.
X	 * First we create a menu.  Then we walk the deleted list
X	 * and make a menu item for each card on the stack and append
X	 * it to the new menu.
X	 */
X	Menu_item menu_item;
X	Menu menu = xv_create(NULL, MENU, 
X						  MENU_NOTIFY_PROC, proc,
X						  NULL);
X	struct card	*p;
X	
X	for (p = dead; p != NULL_CARD; p = p->c_next) {
X		char	*q, buf [MAX_MENU_LEN + 1];
X		
X		q = first_char (p->c_text);
X		if (strlen (q) == 0) {
X			/* unlikely, push_card filters empty cards */
X			continue;
X		}
X
X		/* yank and chop text from the card */
X		(void) strncpy (buf, q, MAX_MENU_LEN);
X		buf [MAX_MENU_LEN] = '\0';
X		q = index (buf, '\n');
X		if (q != NULL) {
X			*q = '\0';
X		}
X
X		/* allocate and copy the string for use by the menu */
X		q = malloc (strlen (buf) + 1);
X		(void) strcpy (q, buf);
X
X		menu_item = xv_create (NULL, MENUITEM,
X							   MENU_STRING,		q,
X							   MENU_VALUE,		p, 
X							   /* menu_destroy() will free() name string */
X							   MENU_RELEASE_IMAGE, 
X							   /* goes away when parent menu does */
X							   MENU_RELEASE,
X							   0);
X
X		xv_set (menu, MENU_APPEND_ITEM, menu_item, 0);
X	}
X	return(menu);
X}
X
X
XMenu
Xgen_undelete (menu_item, operation)
X	Menu_item	menu_item;
X	Menu_generate	operation;
X{
X	static Menu menu;
X	switch (operation) {
X	  case MENU_DISPLAY:
X		return (gen_undelete_menu(undel_proc));
X
X	  case MENU_DISPLAY_DONE:
X		xv_destroy_safe(menu);
X		return (MENU_NULL);
X	}
X	return (MENU_NULL);
X}
X
XMenu
Xgen_undelete_before (menu_item, operation)
X	Menu_item	menu_item;
X	Menu_generate	operation;
X{
X	static Menu menu; 
X	switch (operation) {
X	  case MENU_DISPLAY:
X		return (menu=gen_undelete_menu(undel_before_proc));
X
X	  case MENU_DISPLAY_DONE:
X		xv_destroy_safe(menu);
X		return (MENU_NULL);
X	}
X	return(MENU_NULL);
X}
X
X
X/*
X *	Action proc for the undelete pullright menu item.  This
X *	proc removes the card from the stack and inserts it back into the
X *	active list.  The pointer to the card selected from the menu is
X *	the value of the menu item we're being called with.  The menu
X *	that this action proc is called from, the args passed to the proc,
X *	is the dummy menu created in the MENU_NOTIFY step of the gen
X *	proc above.  Since this proc actually does the undelete, we want
X *	to clear the menu value so that the event func which trapped the
X *	right button event and brought the menu up in the first place
X *	doesn't fake a button click after we've finished.
X */
X
X/*ARGSUSED*/
Xcaddr_t
Xundel_menu_card (menu_item, before)
X	Menu_item		menu_item;
X	int before;
X{
X	struct card	*p;
X	save_card (current);
X
X	p = (struct card *) xv_get (menu_item, MENU_VALUE);
X
X	if (p == NULL_CARD) {
X		p = dead;
X	}
X	unstack_card (p);
X	(void) insert_card (p, (before == TRUE) ? current->c_prev : current);
X	need_save = TRUE;
X	set_slider_max (renumber (first));
X	show_card (p);
X	return ((caddr_t)0);
X}
X
X
X/*
X *	Check the arguments which SunView didn't consume to see if a filename
X *	was provided.
X */
X
Xchar *
Xcheck_args (argc, argv)
X	int	argc;
X	char	**argv;
X{
X	/* only know about a filename argument */
X	if (argc < 2) {
X		return (NULL);
X	}
X
X	argv++;	argc--;
X	if (**argv == '-') {		/* ignore a switch if given */
X		argv++;	argc--;
X	}
X
X	if (argc == 0) {
X		return (NULL);
X	}
X
X	return (*argv);
X}
END_OF_FILE
if test 20514 -ne `wc -c <'cards.c'`; then
    echo shar: \"'cards.c'\" unpacked with wrong size!
fi
# end of 'cards.c'
fi
echo shar: End of archive 2 \(of 3\).
cp /dev/null ark2isdone
MISSING=""
for I in 1 2 3 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 3 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0

dan
----------------------------------------------------
O'Reilly && Associates   argv@sun.com / argv@ora.com
Opinions expressed reflect those of the author only.