home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Garbo
/
Garbo.cdr
/
pc
/
source
/
mush.lzh
/
mush.16
< prev
next >
Wrap
Text File
|
1990-05-06
|
55KB
|
1,783 lines
#! /bin/sh
# This is a shell archive. Remove anything before this line, then feed it
# into a shell via "sh file" or similar. To overwrite existing files,
# type "sh file -c".
# The tool that generated this appeared in the comp.sources.unix newsgroup;
# send mail to comp-sources-unix@uunet.uu.net if you want that tool.
# If this archive is complete, you will see the following message at the end:
# "End of archive 16 (of 19)."
# Contents: mush/dates.c mush/file.c mush/sample.mushrc mush/tool.c
# Wrapped by argv@turnpike on Wed May 2 13:59:48 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'mush/dates.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'mush/dates.c'\"
else
echo shar: Extracting \"'mush/dates.c'\" \(14562 characters\)
sed "s/^X//" >'mush/dates.c' <<'END_OF_FILE'
X/* @(#)dates.c 3.0 (c) copyright 3/01/90 (Dan Heller, Bart Schaefer) */
X
X#include "mush.h"
X
X/*
X * %ld%3c%s gmt_in_secs weekday orig_timezone
X * The standard "date format" stored in the msg data structure.
X */
Xchar *day_names[] = {
X "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
X};
Xchar *month_names[] = { /* imported in pick.c */
X "Jan", "Feb", "Mar", "Apr", "May", "Jun",
X "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
X};
X
Xstatic int mtbl[] = { 0,31,59,90,120,151,181,212,243,273,304,334 };
X
X/* Time Zone Stuff */
Xstruct zoneoff {
X char *zname;
X int hr_off;
X int mn_off;
X} time_zones[] = {
X /* Universal Time */
X { "UT", 0, 0 }, { "GMT", 0, 0 },
X /* European Time */
X { "BST", 1, 0 }, /* Brit. Summer */
X { "EET", 0, 0 }, { "EEST", 1, 0 }, /* Eastern */
X { "MET", -1, 0 }, { "MEST", 0, 0 }, /* Middle */
X { "WET", -2, 0 }, { "WEST", -1, 0 }, /* Western */
X /* North American Time */
X { "NST", -3,-30 }, /* Newfoundland */
X { "AST", -4, 0 }, { "ADT", -3, 0 }, /* Atlantic */
X { "EST", -5, 0 }, { "EDT", -4, 0 }, /* Eastern */
X { "CST", -6, 0 }, { "CDT", -5, 0 }, /* Central */
X { "MST", -7, 0 }, { "MDT", -6, 0 }, /* Mountain */
X { "PST", -8, 0 }, { "PDT", -7, 0 }, /* Pacific */
X { "YST", -9, 0 }, { "YDT", -8, 0 }, /* Yukon */
X { "HST", -10, 0 }, { "HDT", -9, 0 }, /* Hawaii */
X /* Japan and Australia Time */
X {"JST", 9, 0 }, /* Japan */
X {"AEST", 10, 0 }, {"AESST", 11, 0 }, /* Eastern */
X {"ACST", 9, 30 }, {"ACSST", 10, 30 }, /* Central */
X {"AWST", 8, 0 }, /* Western */
X /* Military Time */
X { "A", 1, 0 }, { "N", -1, 0 },
X { "B", 2, 0 }, { "O", -2, 0 },
X { "C", 3, 0 }, { "P", -3, 0 },
X { "D", 4, 0 }, { "Q", -4, 0 },
X { "E", 5, 0 }, { "R", -5, 0 },
X { "F", 6, 0 }, { "S", -6, 0 },
X { "G", 7, 0 }, { "T", -7, 0 },
X { "H", 8, 0 }, { "U", -8, 0 },
X { "I", 9, 0 }, { "V", -9, 0 },
X { "K", 10, 0 }, { "W", -10, 0 },
X { "L", 11, 0 }, { "X", -11, 0 },
X { "M", 12, 0 }, { "Y", -12, 0 },
X { "Z", 0, 0 },
X /* Also legal is +/- followed by hhmm offset from UT */
X { 0, 0, 0 }
X};
X
Xlong
Xgetzoff(zone)
Xchar *zone;
X{
X struct zoneoff *z;
X int hours, mins;
X char sign[2];
X
X if (!zone || !*zone)
X return 0;
X if (sscanf(zone, "%1[-+]%2d%2d", sign, &hours, &mins) == 3)
X return (hours * 3600 + mins * 60) * (*sign == '-' ? -1 : 1);
X for (z = time_zones; z->zname; z++)
X if (lcase_strncmp(zone, z->zname, -1) == 0)
X return z->hr_off * 3600 + z->mn_off * 60;
X return 0;
X}
X
X/*
X * Kind of the reverse of localtime() and gmtime() -- converts a struct tm
X * to time in seconds since 1970. Valid until 2038.
X * If the "zone" argument is present, it modifies the return value.
X * The zone should be a string, either +/-hhmm or symbolic (above).
X * The "how" argument should be -1 to convert FROM gmt, 1 to convert TO gmt,
X * and (as a "side-effect") 0 if the Zone parameter is to be ignored.
X *
X * Thanks to ktl@wag240.caltech.edu (Kian-Tat Lim) for similar algorithm
X * written in perl from which this was derived.
X */
Xlong
Xtime2gmt(tym, zone, how)
Xstruct tm *tym;
Xchar *zone;
Xint how;
X{
X long year, julian;
X
X if (tym->tm_year < 100)
X year = tym->tm_year + 1900;
X if (year < 69)
X year += 100;
X
X julian = 365 * (year - 1970) + (int)((year - 1970 + 1) / 4) +
X mtbl[tym->tm_mon] + tym->tm_mday - 1;
X /* tym->tm_yday might not be valid */
X if (tym->tm_mon > 1 && year%4 == 0 && (year%100 != 0 || year%400 == 0))
X julian++;
X julian *= 86400; /* convert to seconds */
X julian += (tym->tm_hour * 60 + tym->tm_min) * 60 + tym->tm_sec;
X return julian - getzoff(zone) * how;
X}
X
Xstruct tm *
Xtime_n_zone(zone)
Xchar *zone;
X{
X struct tm *T;
X char *tz;
X#if defined(SYSV) || defined(TIMEZONE)
X long x;
X
X (void) time(&x);
X T = localtime(&x);
X#ifndef TIMEZONE
X {
X extern char *tzname[];
X tz = tzname[T->tm_isdst];
X }
X#endif /* TIMEZONE */
X#else /* SYSV || TIMEZONE */
X extern char *timezone();
X struct timeval mytime;
X struct timezone myzone;
X
X (void) gettimeofday(&mytime, &myzone);
X T = localtime(&mytime.tv_sec);
X tz = timezone(myzone.tz_minuteswest, (T->tm_isdst && myzone.tz_dsttime));
X#endif /* !SYSV */
X
X#ifdef TIMEZONE
X#ifdef DAYLITETZ
X if (T->tm_isdst)
X tz = DAYLITETZ;
X else
X#endif /* DAYLITETZ */
X tz = TIMEZONE;
X#endif /* TIMEZONE */
X
X (void) strncpy(zone, tz, 7), zone[7] = 0;
X return T;
X}
X
X/* Time() returns a string according to criteria:
X * if "now" is 0, then the current time is gotten and used.
X * else, use the time described by now
X * opts points to a string of args which is parsed until an unknown
X * arg is found and opts will point to that upon return.
X * valid args are T (time of day), D (day of week), M (month), Y (year),
X * N (number of day in month -- couldn't think of a better letter).
X */
Xchar *
XTime(opts, now)
Xregister char *opts;
Xlong now;
X{
X static char time_buf[30];
X struct tm *T;
X register char *p = time_buf;
X long x;
X
X if (!opts)
X return NULL;
X if (now)
X x = now;
X else
X (void) time(&x);
X T = localtime(&x);
X for (;; opts++) {
X switch(*opts) {
X case 'T':
X if (ison(glob_flags, MIL_TIME))
X (void) sprintf(p, "%2d:%02d", T->tm_hour, T->tm_min);
X else
X (void) sprintf(p, "%d:%02d", (T->tm_hour) ?
X ((T->tm_hour <= 12) ? T->tm_hour : T->tm_hour - 12) :
X 12, T->tm_min);
X when 'D': case 'W': (void) strcpy(p, day_names[T->tm_wday]);
X when 'M': (void) strcpy(p, month_names[T->tm_mon]);
X when 'y': (void) sprintf(p, "%d", T->tm_year);
X when 'Y': (void) sprintf(p, "%d", T->tm_year + 1900);
X when 'N': (void) sprintf(p, "%d", T->tm_mday);
X otherwise: *--p = 0; return time_buf;
X }
X p += strlen(p);
X *p++ = ' ';
X }
X}
X
X/* parse date and return a string that looks like
X * %ld%3c%s gmt_in_secs weekday orig_timezone
X * This function is a bunch of scanfs on known date formats. Don't
X * trust the "weekday" name fields because they may not be spelled
X * right, or have the correct punctuation. Figure it out once the
X * year and month and date have been determined.
X */
Xchar *
Xparse_date(p)
Xregister char *p;
X{
X /* When scanf-ing if month isn't a month, it could be a _long_ string.
X * this is also the static buffer whose address we return.
X */
X static char month[64];
X char Wkday[4], Zone[8];
X char a_or_p;
X int Month = 0, Day = 0, Year = 0;
X int Uhour = 0, Umin = 0, Hours = -1, Mins = -1;
X struct tm T;
X
X Zone[0] = 0;
X skipspaces(0);
X
X /* programmer's note -- there are too many scanfs here for some compilers
X * to put them all into one if statement. Use goto's :-( Also reset
X * Zone[0] after any sscanf() that could corrupt it on a partial match.
X */
X
X /* RFC822 formats and minor variations -- order important */
X
X /* day_number month_name year_number time timezone */
X if (sscanf(p, "%d %s %d %d:%d:%*d %7s",
X &Day, month, &Year, &Hours, &Mins, Zone) >= 5 && Day)
X goto gotit;
X Zone[0] = 0;
X if (sscanf(p, "%d %s %d %d:%d %7s",
X &Day, month, &Year, &Hours, &Mins, Zone) >= 5 && Day)
X goto gotit;
X Zone[0] = 0;
X /* day_name day_number month_name year_number time timezone */
X if (sscanf(p, "%*s %d %s %d %d:%d:%*d %7s",
X &Day, month, &Year, &Hours, &Mins, Zone) >= 5 && Day)
X goto gotit;
X Zone[0] = 0;
X if (sscanf(p, "%*s %d %s %d %d:%d %7s",
X &Day, month, &Year, &Hours, &Mins, Zone) >= 5 && Day)
X goto gotit;
X Zone[0] = 0;
X
X /* Ctime format (From_ lines) -- timezone almost never found */
X
X /* day_name month_name day_number time year_number */
X if (sscanf(p, "%*s %s %d %d:%d:%*d %d %7s",
X month, &Day, &Hours, &Mins, &Year, Zone) >= 5)
X goto gotit;
X Zone[0] = 0;
X
X /* Other common variants */
X
X /* day_number month_name year_number time-timezone (day) */
X /* ^no colon separator */
X if (sscanf(p, "%d %s %d %2d%2d-%6[0123456789]",
X &Day, month, &Year, &Hours, &Mins, &Zone[1]) == 6) {
X Zone[0] = '-';
X goto gotit;
X }
X if (sscanf(p, "%d %s %d %2d%2d-%7s",
X &Day, month, &Year, &Hours, &Mins, Zone) == 6)
X goto gotit;
X Zone[0] = 0;
X
X /* day_number month_name year_number time timezone */
X /* ^no colon separator */
X /* (This is the odd one in the RFC822 examples section; */
X /* also catches the slop from partial hits above.) */
X if (sscanf(p, "%d %s %d %2d%2d %7s",
X &Day, month, &Year, &Hours, &Mins, Zone) >= 5 && Day)
X goto gotit;
X Zone[0] = 0;
X
X Zone[1] = 0; /* Yes, Zone[1] -- tested below */
X
X /* day_number month_name year_number, time "-" ?? */
X if (sscanf(p,"%d %s %d, %d:%d:%*d -%6[0123456789]",
X &Day, month, &Year, &Hours, &Mins, &Zone[1]) >= 5 && Day) {
X if (Zone[1])
X Zone[0] = '-';
X goto gotit;
X }
X
X /* day_number month_name year_number 12_hour_time a_or_p */
X if (sscanf(p, "%d %s %d %d:%d:%*d %cm %7s",
X &Day, month, &Year, &Hours, &Mins, &a_or_p, Zone) >= 6) {
X if (a_or_p == 'p')
X Hours += 12;
X goto gotit;
X }
X
X /* day_name month_name day_number year_number time */
X if (sscanf(p, "%*s %s %d %d %d:%d:%*d %7s",
X month, &Day, &Year, &Hours, &Mins, Zone) >= 5)
X goto gotit;
X Zone[0] = 0;
X if (sscanf(p, "%*s %s %d %d %d:%d %7s",
X month, &Day, &Year, &Hours, &Mins, Zone) >= 5)
X goto gotit;
X Zone[0] = 0;
X
X /* day_name month_name day_number time timezone year_number */
X if (sscanf(p, "%*s %s %d %d:%d:%*d %7s %d",
X month, &Day, &Hours, &Mins, Zone, &Year) == 6)
X goto gotit;
X Zone[0] = 0;
X if (sscanf(p, "%*s %s %d %d:%d %7s %d",
X month, &Day, &Hours, &Mins, Zone, &Year) == 6)
X goto gotit;
X Zone[0] = 0;
X
X /* day_number-month_name-year time */
X if (sscanf(p,"%d-%[^-]-%d %d:%d", &Day, month, &Year, &Hours, &Mins) == 5)
X goto gotit;
X
X /* day_name, day_number-month_name-year time */
X if (sscanf(p,"%*s %d-%[^-]-%d %d:%d",
X &Day, month, &Year, &Hours, &Mins) == 5)
X goto gotit;
X
X /* year_number-month_number-day_number time */
X if (sscanf(p, "%d-%d-%d %d:%d", &Year, &Month, &Day, &Hours, &Mins) == 5)
X goto gotit;
X
X /* month_name day_number time year Zone */
X /* (ctime, but without the day name) */
X if (sscanf(p, "%s %d %d:%d:%*d %d %7s",
X month, &Day, &Hours, &Mins, &Year, Zone) >= 5)
X goto gotit;
X Zone[0] = 0;
X
X goto didnt_getit;
X
Xgotit:
X if (Year > 1900)
X Year -= 1900;
X if (!Month && (Month = month_to_n(month)) == -1) {
X print("bad month: %s\n", p);
X return NULL;
X }
X if (Zone[0] == 0) {
X /* Use local time zone if none found -- important for date_recv */
X (void) time_n_zone(Zone);
X }
X {
X /* Lots of foolishness with casts for Xenix-286 16-bit ints */
X
X long days_ctr; /* 16-bit ints overflowed Sept 12, 1989 */
X
X days_ctr = ((long)Year * 365L) + ((Year + 3) / 4);
X days_ctr += mtbl[Month-1] + Day + 6;
X if (Month > 2 && (Year % 4 == 0))
X days_ctr++;
X (void) (sprintf(Wkday, "%.3s", day_names[(int)(days_ctr % 7L)]));
X }
X T.tm_sec = 0; /* not recorded, so ignore it */
X T.tm_min = Mins;
X T.tm_hour = Hours;
X T.tm_mday = Day;
X T.tm_mon = Month - 1;
X T.tm_year = Year;
X T.tm_wday = T.tm_yday = 0; /* not used in time2gmt() */
X T.tm_isdst = 0; /* determined from Zone */
X return sprintf(month, "%ld%s%s", time2gmt(&T, Zone, 1), Wkday, Zone);
Xdidnt_getit:
X if (ison(glob_flags, WARNING))
X print("Unknown date format: %s\n", p);
X return NULL;
X}
X
X/* pass a string in the standard date format, put into string.
X * return values in buffers provided they are not null.
X */
Xchar *
Xdate_to_string(Date, Yr, Mon, Day, Wkday, Tm, Zone, ret_buf)
Xchar *Date, *Yr, *Mon, *Day, *Wkday, *Tm, *Zone, *ret_buf;
X{
X long gmt;
X struct tm *T;
X char a_or_p, *p = ret_buf;
X
X Zone[0] = 0;
X (void) sscanf(Date, "%ld%3c%s", &gmt, Wkday, Zone);
X Wkday[3] = 0;
X gmt += getzoff(Zone);
X T = gmtime(&gmt);
X a_or_p = (T->tm_hour < 12)? 'a': 'p';
X
X (void) sprintf(Yr, "%d", T->tm_year + 1900);
X (void) sprintf(Day, "%d", T->tm_mday);
X (void) strcpy(Mon, month_names[T->tm_mon]);
X p += strlen(sprintf(p, "%s %2.d, ", Mon, T->tm_mday));
X
X if (ison(glob_flags, MIL_TIME))
X (void) sprintf(p, "%2d:%02d",T->tm_hour,T->tm_min);
X else
X (void) sprintf(p, "%2.d:%02d%cm",
X (T->tm_hour)? (T->tm_hour <= 12)? T->tm_hour: T->tm_hour-12: 12,
X T->tm_min, a_or_p);
X (void) strcpy(Tm, p);
X
X return ret_buf;
X}
X
X/* pass a string in the internal mush date format.
X * return pointer to static buffer holding ctime-format date.
X */
Xchar *
Xdate_to_ctime(Date)
Xchar *Date;
X{
X static char ret_buf[32];
X long gmt;
X
X ret_buf[0] = 0;
X (void) sscanf(Date, "%ld", &gmt);
X (void) strcpy(ret_buf, ctime(&gmt));
X
X return ret_buf;
X}
X
X/*
X * Build a date string according to the specification in the RFC for Date:
X */
Xchar *
Xrfc_date(buf)
Xchar buf[];
X{
X struct tm *T;
X char zone[8];
X
X T = time_n_zone(zone);
X return sprintf(buf, "%s, %d %s %d %02d:%02d:%02d %s",
X day_names[T->tm_wday], /* day name */
X T->tm_mday, /* day of the month */
X month_names[T->tm_mon], /* month name */
X T->tm_year + 1900, /* year number */
X T->tm_hour, /* hours (24hr) */
X T->tm_min, T->tm_sec, /* mins/secs */
X zone); /* timezone */
X}
X
X#define JAN 1
X#define FEB 2
X#define MAR 3
X#define APR 4
X#define MAY 5
X#define JUN 6
X#define JUL 7
X#define AUG 8
X#define SEP 9
X#define OCT 10
X#define NOV 11
X#define DEC 12
X
X/* stolen direct from ELM */
Xmonth_to_n(name)
Xregister char *name;
X{
X /** return the month number given the month name... **/
X
X register char ch;
X
X switch (lower(*name)) {
X case 'a' : if ((ch = lower(name[1])) == 'p')
X return(APR);
X else if (ch == 'u')
X return(AUG);
X else return(-1); /* error! */
X case 'd' : return(DEC);
X case 'f' : return(FEB);
X case 'j' : if ((ch = lower(name[1])) == 'a')
X return(JAN);
X else if (ch == 'u') {
X if ((ch = lower(name[2])) == 'n')
X return(JUN);
X else if (ch == 'l')
X return(JUL);
X else return(-1); /* error! */
X }
X else return(-1); /* error */
X case 'm' : if ((ch = lower(name[2])) == 'r')
X return(MAR);
X else if (ch == 'y')
X return(MAY);
X else return(-1); /* error! */
X case 'n' : return(NOV);
X case 'o' : return(OCT);
X case 's' : return(SEP);
X default : return(-1);
X }
X}
END_OF_FILE
if test 14562 -ne `wc -c <'mush/dates.c'`; then
echo shar: \"'mush/dates.c'\" unpacked with wrong size!
fi
# end of 'mush/dates.c'
fi
if test -f 'mush/file.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'mush/file.c'\"
else
echo shar: Extracting \"'mush/file.c'\" \(14337 characters\)
sed "s/^X//" >'mush/file.c' <<'END_OF_FILE'
X/* file.c -- Copyright (1988) Dan Heller */
X
X#include "mush.h"
X#include <pwd.h>
X
X/* takes string 'p' and address of int (isdir). If p uses the ~ to reference
X * a home directory of some sort, then expand it. find out what sort of
X * file final path is. set isdir to 1 if a directory, 0 if not, -1 on error
X * return final path. If an error occurs, return string indicating error.
X * if isdir has a value of 1 when passed, it ignores "No such file or directory"
X */
Xchar *
Xgetpath(p, isdir)
Xregister char *p;
Xint *isdir;
X{
X static char buf[MAXPATHLEN];
X struct stat stat_buf;
X
X if (!p || !*p || !strcmp(p, "~")) {
X char *home = do_set(set_options, "home");
X if (!home || !*home)
X home = ALTERNATE_HOME;
X (void) strcpy(buf, home); /* no arg means home */
X } else if (*p == '~') {
X if (p[1] != '/') {
X /* not our home, but someone else's
X * look for ~user or ~user/subpath
X * if '/' exists, separate into tmp="user" p="subpath"
X */
X struct passwd *ent, *getpwnam();
X char *p2 = p+1;
X if (p = index(p2, '/'))
X *p++ = 0;
X if (!(ent = getpwnam(p2))) {
X *isdir = -1;
X return sprintf(buf, "no such user: %s", p2);
X }
X /* append subpath to pathname */
X if (p && *p)
X (void) sprintf(buf, "%s/%s", ent->pw_dir, p);
X /* if *p == NULL, pathname is done (buf), set isdir = 1 */
X else {
X *isdir = 1;
X return strcpy(buf, ent->pw_dir);
X }
X } else {
X char *home = do_set(set_options, "home");
X if (!home || !*home)
X home = ALTERNATE_HOME;
X (void) sprintf(buf, "%s/%s", home, p+2);
X }
X } else if (*p == '%') {
X /* if %user, append user name... else, it's just us */
X if (!*++p || *p == ' ' || *p == '\t')
X (void) strcpy(buf, spoolfile);
X else
X#ifndef HOMEMAIL
X (void) sprintf(buf, "%s/%s", MAILDIR, p);
X#else /* HOMEMAIL */
X {
X /* If it's NOT us, recur to get the path for ~user/MAILFILE */
X int t_isdir = *isdir;
X char *t, tmp[MAXPATHLEN];
X (void) sprintf(tmp, "~%s/%s", p, MAILFILE);
X t = getpath(tmp, &t_isdir);
X if (t_isdir == -1) {
X *isdir = -1;
X return t;
X }
X /* strcpy(buf, t); --buf already has info because it's static */
X }
X#endif /* HOMEMAIL */
X } else if (*p == '+') {
X register char *p2 = do_set(set_options, "folder");
X if (!p2 || !*p2)
X p2 = DEF_FOLDER;
X if (*++p)
X (void) sprintf(buf, "%s/%s", p2, p);
X else
X (void) strcpy(buf, p2);
X if (*buf != '/') {
X int t_isdir = *isdir;
X char *t, tmp[MAXPATHLEN];
X if (*buf != '~')
X (void) sprintf(tmp, "~/%s", buf);
X else
X (void) strcpy(tmp, buf);
X t = getpath(tmp, &t_isdir);
X if (t_isdir == -1) {
X *isdir = -1;
X return t;
X }
X /* strcpy(buf, t); --buf already has info because it's static */
X }
X } else { /* allow \ to escape the special chars, +, %, ~ */
X if (*p == '\\')
X p++;
X (void) strcpy(buf, p);
X }
X if (stat(buf, &stat_buf)) {
X (void) access(buf, F_OK); /* set errno to the "real" reason */
X if (errno == ENOENT && *isdir == 1) {
X *isdir = 0; /* say it's a regular file even tho it doesn't exist */
X return buf; /* it may be wanted for creating */
X }
X *isdir = -1;
X return sys_errlist[errno];
X }
X *isdir = ((stat_buf.st_mode & S_IFMT) == S_IFDIR);
X return buf;
X}
X
X/*
X * Given a (possibly NULL or empty) string, return the name of a a valid
X * directory. The string may contain the usual filename metachars (see
X * above). Returns the current user's home directory if the input string
X * does not refer to a directory, the ALTERNATE_HOME if the user's home
X * directory cannot be found, or NULL if none of the above are accessible.
X *
X * NOTE: Returns the getpath() static buffer, so the same caveats apply.
X */
Xchar *
Xgetdir(path)
Xchar *path;
X{
X int isdir = 0;
X
X /* getpath() already handles the NULL and empty cases */
X if (!(path = getpath(path, &isdir)) || isdir != 1) {
X isdir = 0;
X path = getpath(ALTERNATE_HOME, &isdir);
X if (isdir != 1)
X path = NULL;
X }
X return path;
X}
X
X/*
X * Given a filename[pointer] (p), a file pointer, and a mode, file_to_fp
X * opens the file with the mode.
X * If the mode is "r" then we read the file into the file pointer at the
X * end (fseek(fp, 2, 0)). If the file is opened for writing, then read
X * from the beginning of fp and write it into the file.
X * This is usually called to read .signatures into messages (thus,
X * opening .signature with "r" and writing to the end of fp which is probably
X * the sendmail process or the message file pointer) or to write fortunes into
X * the message buffer: reading fp (the popened fortune) and writing into file.
X */
Xfile_to_fp(p, fp, mode)
Xregister char *p;
Xregister FILE *fp;
Xchar *mode;
X{
X int x = 1;
X char *file, buf[BUFSIZ];
X FILE *tmp_fp;
X
X if (!p || !*p) {
X print("specify filename");
X return -1;
X }
X /* Special case for IS_SENDING && !IS_GETTING should eventually go away */
X if (ison(glob_flags, IS_SENDING) && isoff(glob_flags, IS_GETTING) &&
X strcmp(p, "-") == 0) {
X file = p;
X if (*mode == 'r')
X tmp_fp = stdin;
X else
X tmp_fp = stdout;
X } else {
X file = getpath(p, &x);
X if (x == -1) { /* on error, file contains error message */
X wprint(file);
X return -1;
X }
X wprint("%s: ", file);
X if (x) {
X /* if x == 1, then path is a directory */
X wprint("is a directory.\n");
X return -1;
X } else if (!(tmp_fp = fopen(file, mode))) {
X wprint("%s\n", sys_errlist[errno]);
X return -1;
X }
X }
X if (*mode != 'r') {
X rewind(fp);
X for(x = 0; fgets(buf, BUFSIZ, fp); x++)
X (void) fputs(buf, tmp_fp);
X } else {
X for(x = 0; fgets(buf, BUFSIZ, tmp_fp); x++)
X (void) fputs(buf, fp);
X (void) fflush(fp);
X }
X wprint("%s%d line%s\n", (*mode == 'a')? "added ": "",
X x, (x == 1)? "": "s");
X if (file != p || strcmp(file, "-") != 0)
X (void) fclose(tmp_fp);
X return 0;
X}
X
X/* clear all contents of the file. Careful that the file is opened for
X * _writing_ --tempfile is opened for reading, so don't try to empty it
X * if you're using ftruncate. Return -1 on error, 0 on success.
X */
Xemptyfile(fp, fname)
Xregister FILE **fp;
Xregister char *fname;
X{
X Debug("Emptying \"%s\"\n", fname);
X#ifndef SYSV
X return ftruncate(fileno(*fp), 0L);
X#else
X {
X int omask = umask(077), ret;
X (void) fclose(*fp);
X if (!(*fp = fopen(fname, "w")))
X ret = -1;
X ret = 0;
X (void) umask(omask);
X return ret;
X }
X#endif /* SYSV */
X}
X
X/*
X * Finds out how many file descriptors are opened. Useful for making sure
X * no files got opened in subprocedures which were not subsequently closed.
X * If argc is 0, returns the number of available fds.
X */
Xnopenfiles(argc)
X{
X#ifdef MAXFILES
X register int size = MAXFILES;
X#else
X register int size = getdtablesize();
X#endif /* MAXFILES */
X register int nfiles = 0, totalfiles = size;
X
X if (argc > 1)
X return -1;
X
X if (argc == 1)
X wprint("open file descriptors:");
X while (--size >= 0)
X if (fcntl(size, F_GETFL, 0) != -1) {
X if (argc == 1)
X wprint(" %d", size);
X ++nfiles;
X }
X if (argc == 1) {
X wprint("\n");
X return 0;
X }
X return totalfiles - nfiles;
X}
X
X/*
X * Close all "extraneous" file descriptors; return the number closed
X */
Xclosefileds (n)
X{
X register int nfiles = 0;
X#ifdef MAXFILES
X register int size = MAXFILES;
X#else
X register int size = getdtablesize();
X#endif /* MAXFILES */
X
X while (--size >= n)
X if (fcntl(size, F_GETFL, 0) != -1) {
X (void) close(size);
X ++nfiles;
X }
X return nfiles;
X}
X
X/*
X * Open a path for writing or appending -- return a FILE pointer.
X * If program is TRUE, then use popen, not fopen and don't check
X * to see if the file is writable. If program is FALSE and lockit
X * is TRUE, then lock on open.
X */
XFILE *
Xopen_file(p, program, lockit)
Xregister char *p;
X{
X register FILE *newfile = NULL_FILE;
X register char *tmp;
X int x = 1;
X
X if (program || *p == '/')
X tmp = p, x = 0;
X else
X tmp = getpath(p, &x);
X if (x == 1)
X print("%s is a directory.\n", tmp);
X else if (x == -1)
X print("%s: %s\n", p, tmp);
X else {
X register char *mode = NULL;
X /* if it doesn't exist open for "w" */
X if (program || Access(tmp, F_OK))
X mode = "w";
X /* if we can't write to it, forget it */
X else if (Access(tmp, W_OK))
X error(tmp);
X else
X mode = "a";
X if (mode)
X if (program) {
X if (!(newfile = popen(tmp, mode)))
X error("Can't execute %s\n", tmp);
X } else if (lockit) {
X /* Lock on open */
X if (!(newfile = lock_fopen(tmp, mode)))
X error("Can't write to %s", tmp);
X } else {
X /* Ordinary open */
X if (!(newfile = mask_fopen(tmp, mode)))
X error("Can't write to %s", tmp);
X }
X if (newfile != NULL_FILE)
X Debug("Successfully opened %s\n", tmp);
X }
X return newfile;
X}
X
X/*
X * Open each file in the vector names[] and place the corresponding
X * file descriptor in files[]. If the file is really a program (pipe),
X * delete the name after opening; otherwise lock the file.
X * Tokens beginning with a "/, ~, or + are files; tokens beginning
X * with a | are programs.
X */
Xopen_list(names, files, size)
Xchar *names[];
XFILE *files[];
X{
X register int total = 0, prog;
X register char *fpath;
X
X Debug("opening "), print_argv(names);
X for (total = 0; size && total < size; ) {
X fpath = names[total] + (prog = (names[total][0] == '|'));
X /* open_file() locks the file here only if prog is false */
X if ((files[total] = open_file(fpath, prog, TRUE))) {
X if (prog) {
X xfree(names[total]);
X names[total++] = NULL;
X } else {
X /* Seek to end of file AFTER locking */
X (void) fseek(files[total++], 0L, 2);
X }
X } else {
X Debug("Failed to open %s\n", names[total]);
X /* Swap the failed file with the last in the list */
X if (size--) {
X xfree(names[total]);
X names[total] = names[size];
X names[size] = NULL;
X }
X }
X }
X return size;
X}
X
X/*
X * find_files gets a set of addresses and an array of
X * char pointers and the maximum size that array can be.
X * The object is to find the files or programs listed in "s". If the
X * size is 0, then just extract the file names and give error messages
X * for each one since they will not be opened. Return the number of
X * files found and delete all files from the list in * "s".
X * The string "s" is modified to be a list of address -- all names AND
X * files are stripped out of the list.
X * The force parameter causes names to be interpreted as files even if
X * they would normally appear to be addresses.
X */
Xfind_files(s, names, size, force)
Xregister char *s;
Xchar *names[];
X{
X register int total = 0;
X char file[MAXPATHLEN], buf[HDRSIZ], *start = s, c;
X register char *p, *b = buf, *fpath;
X
X do {
X if (!(p = get_name_n_addr(s, NULL, file)))
X break;
X c = *p, *p = 0;
X /* See if it's a file. This doesn't get written back
X * onto "buf" since it is supposed to be extracted anyway.
X */
X if (force || *file == '+' || *file == '~' ||
X *file == '|' || *file == '/') {
X int isdir;
X /* open either "file" or &file[1] */
X if (*file == '|') {
X isdir = 0;
X fpath = file;
X } else {
X isdir = 1;
X /* if successful, getpath will reset isdir to 0 */
X fpath = getpath(file, &isdir);
X }
X if (!isdir) {
X if (size && total < size)
X names[total++] = savestr(fpath);
X else
X print("No open space for %s\n", file);
X } else if (isdir == 1)
X print("%s: is a directory\n", file);
X else
X print("%s: %s\n", file, fpath);
X } else {
X b += Strcpy(b, s);
X *b++ = ',', *b++ = ' ';
X }
X for (*p = c, s = p; *s == ',' || isspace(*s); s++)
X ;
X } while (*s);
X for (*b-- = 0; b > buf && (*b == ',' || isspace(*b)); b--)
X *b = 0;
X (void) strcpy(start, buf);
X names[total] = NULL; /* for free_vec() */
X return total;
X}
X
X/*
X * access(2) has an undocumented feature which ignores suid. If you are
X * su'ed and try to read your mail, you will be unable to because access()
X * will give the illusion that you cannot read/write to your mbox. Solve
X * the problem by using stat() instead.
X */
XAccess(file, mode)
Xregister char *file;
X{
X struct stat buf;
X
X if (stat(file, &buf) == -1)
X return -1;
X if (mode == R_OK)
X return (buf.st_mode & 0400)? 0 : -1;
X if (mode == W_OK)
X return (buf.st_mode & 0200)? 0 : -1;
X return 0;
X}
X
X/*
X * Open a file for read/write/whatever but make sure umask is rw by user only.
X */
XFILE *
Xmask_fopen(file, mode)
Xchar *file, *mode;
X{
X int omask = umask(077);
X FILE *fp = fopen(file, mode);
X (void) umask(omask);
X return fp;
X}
X
X/*
X * Shorten a file name, replacing its full path name with one using an
X * accepted mush abbreviation:
X * ~ home directory
X * + folder directory
X * For files in the current directory, the path is simply skipped.
X * Returns a pointer into a static buffer holding the trimmed path.
X */
Xchar *
Xtrim_filename(name)
Xchar *name;
X{
X static char buf[MAXPATHLEN];
X char *fldr = do_set(set_options, "folder"),
X *home = do_set(set_options, "home");
X int len;
X
X /* Handling $folder is tough, because if it is not set then we should
X * trim DEF_FOLDER; but DEF_FOLDER may not be a full path, and we can't
X * call getpath() because the "name" parameter may point to gepath()'s
X * static buffer. So we handle the special case of DEF_FOLDER starting
X * with a tilde ($home), and forget about it otherwise. Yuck.
X */
X if ((!fldr || !*fldr) && (fldr = DEF_FOLDER) && *fldr == '~' && home) {
X (void) sprintf(buf, "%s%s", home, fldr + 1);
X fldr = buf; /* buf will get overwritten again below */
X }
X /* One more special case: if $folder and $home are the same, then we
X * trim as $home, otherwise we trim as $folder. This prevents strange
X * contractions like "+.cshrc" for "~/.cshrc".
X */
X if ((!home || strcmp(home, fldr)) && (len = strlen(fldr)) &&
X !strncmp(fldr, name, len) && (name[len] == '/' || !name[len])) {
X buf[0] = '+';
X if (name[len] && name[len + 1])
X (void) strcpy(buf + 1, name + len + 1);
X else
X buf[1] = 0;
X return buf;
X } else if (home && (len = strlen(home)) && !strncmp(home, name, len) &&
X (name[len] == '/' || !name[len])) {
X buf[0] = '~';
X (void) strcpy(buf + 1, name + len);
X return buf;
X } else if ((fldr = do_set(set_options, "cwd")) &&
X (len = strlen(fldr)) && !strncmp(fldr, name, len) &&
X name[len] == '/')
X return strcpy(buf, name + len + 1);
X return strcpy(buf, name);
X}
END_OF_FILE
if test 14337 -ne `wc -c <'mush/file.c'`; then
echo shar: \"'mush/file.c'\" unpacked with wrong size!
fi
# end of 'mush/file.c'
fi
if test -f 'mush/sample.mushrc' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'mush/sample.mushrc'\"
else
echo shar: Extracting \"'mush/sample.mushrc'\" \(6541 characters\)
sed "s/^X//" >'mush/sample.mushrc' <<'END_OF_FILE'
X# sample.mushrc
X# By Bart Schaefer and Dan Heller
X#
X# Change mush's temp file directory, to avoid quota collisions.
X# /usr/tmp so tmpfiles won't be rm'd before they can be recovered.
Xset tmpdir=/usr/tmp
X
X# Set the folder and mbox locations; the + expands to value of "folder".
Xset folder=$HOME/Mail mbox=+mbox
X
X# Set up the display early to allow quick exit in headers-only mode.
X# The hdrs_only flag is true if the command line was: "mush -H".
X# The variable hdr_format is set to change the format of the header
X# summaries that are displayed.
Xif hdrs_only
X set hdr_format='%28a %M %-2N %.33s'
X exit # Quits reading this file
Xelse
X set hdr_format='%28a %M %-2N (%3.5l li) %.25s'
Xendif
X
X# Set the prompt to show current time, name of the current folder,
X# current message number, and count of total messages.
Xset prompt="(%T) %f: #%m of %t> "
X
X# Hitting <CR> should do nothing (helps make mush more shell-like). If
X# newline is not set, hitting <CR> prints the next message (like Mail).
X# This variable could be set to any mush command.
Xset newline
X
X# These variables are helpful for new users:
X# ask -- always prompt for Subject: of mail
X# ignoreeof -- ignore end-of-file from keyboard
X# verify -- query that all is well before sending mail
X# warning -- report miscellaneous possible problems
Xset ask verify warning
Xset ignoreeof="echo 'Use "'"'quit'"'" to quit.'"
X
X# When reading messages, don't bother looking at lengthy, boring headers.
Xignore message-id received via status
X
X# Since mush has csh-like history, you might find it annoying to type
X# things like "mail host\!host1\!host2\!user" from within the mush shell.
X# Setting nonobang will prevent the "unknown event" and allow the !'s to
X# be typed without having to be preceded by backslashes.
Xset nonobang
X
X# By default, mush's history is set to the last command only. Set it to
X# remember the last 100 commands.
Xset history = 100
X
X# If the variable "unix" is set, then any command that isn't a mush command
X# will execute the command as if you typed it from the shell. Note, such
X# commands will not go through another shell -- this is it.
Xset unix
X
X# Mush tries to read ~/.mushrc first, then it tries ~/.mailrc. Assuming
X# you use *this* file as your .mushrc, source the contents of .mailrc as
X# well in case there are Mail aliases that are set there.
Xsource $HOME/.mailrc
X
X# Use a real pager.
Xset pager=less
X
X# When typing in a letter, it is sometimes convenient to have lines wrap
X# automatically similar to editors like vi and emacs. In this example, if
X# the user types past column 74, a newline will automatically be inserted.
Xset wrapcolumn=74
X
X# If "autosign" is set, then a file can be read in automatically whenever
X# mail is sent. This file is normally your "signature," that is, your
X# name and other information you want included in every message.
Xset autosign = ~/.signature
X
X# When you use the -i option to reply, or use the ~i tilde escape in a letter
X# when in compose mode, the current message will be included in your text.
X# Put a nice wrapper around those included messages. Here, show the author's
X# name and the subject of his letter, label the end, and add a trailing blank
X# to separate each inclusion and make finding the end easier.
Xset pre_indent_str='On %M %N, %T, %.50n wrote:\n} Subject: %.65s'
Xset indent_str='} ' # actual message text is preceded by a "}"
Xset post_indent_str='}-- End of excerpt from %.50n\n'
X
X# Label replies with a header showing the who, what, and when of the
X# message being replied-to.
Xset in_reply_to='%f\n\t"%s" (%d)'
X
X# Mail routing and address-fixing conveniences. If auto_route is set, then
X# replies to messages take a closer look at the addresses of the recipients.
X# If any redundant paths are present, they are pruned. Also, the path that
X# precedes any hosts listed in the "known_hosts" list is truncated. This is
X# useful for uucp sites only, and is therefore commented out in this sample.
X# set auto_route known_hosts="sun ucbcad well unicom"
X
X# The "alts" command specifies alternate addresses that I have. Here,
X# "*" expands to any "path" whose recipient ends with the user's current
X# login name. If another login name is desired, the login and/or path
X# to that login must be preceded by a !. Otherwise, standard paths are used.
Xalts "*"
X
X# The "map" command can rebind certain key sequences in tty-mode only.
X# Here, if the user types two R's in a row at the prompt, then the string
X# "reply -ei " will be echoed as if the user typed it.
Xmap RR "reply -ei "
X# "rr" will do a reply and do the newline for you so you don't have to.
Xmap rr "reply\n"
X
X# The "map!" command is similar to "map" in that you can do keyboard
X# acceleration, but map! occurs during letter composition mode only.
Xmap! '\CT' ' ' # ^T generates 4 spaces in composition mode.
X# Here, hitting * twice will append a pre-signature.
Xmap! ** "\n Later,\n"
X
X# Be careful with map and map! -- you can cause an infinite loop.
X# Your interrupt key (usually ^C) will stop such loops.
X
X# The curses mode allows the screen to be set up like a full screen editor.
X# There are basic "curses commands" which are bound to keyboard key-sequences
X# (usually one character). The user can rebind these keys to suit his tastes.
X# Note that the binding for R below removes the binding of reply-all.
X#
Xset curses_help # Unset this to remove help message in curses.
Xbind \n display # Hit return to display the next message.
Xbind t top # Make it easier to see the top few lines.
Xbind e macro "[line-mode]edit\n" # Quick edit from curses.
Xbind P macro "[line-mode]Print\n" # Show me all the headers.
Xbind R macro "[line-mode]reply -ei " # Reply with inclusion and edit.
Xbind A macro "R[getline]~t\n\CUargv\n" # R to Dan w/auto address fix.
X
X# "cmd" is used to set command line aliases similar to the way "csh"
X# does it. The only difference is that "alias" is a reserved word in
X# Mush and Mail, so cmd is used.
X#
Xcmd dq 'd \!*; q' # Delete a message list, then quit.
Xcmd unread 'flags \!* U O' # Mark messages unread.
Xcmd : curses # Colon now "toggles" curses mode.
X
X# Find messages from mailer-daemon (ignore upper/lower case).
Xcmd md 'pick -i -f mailer-daemon'
X# Because mush can pipe commands to one another, including "cmd"'s, this
X# example will delete all messages from mailer-daemon
Xcmd dmd 'md | delete'
X
X# aliases -- just like Mail's, but you can specify "names"
Xalias argv Dan Heller <argv@sun.com>
Xalias bart Bart Schaefer <schaefer@ogicse.ogi.edu>
Xalias mush-users Mush Users <mush-users-request@garp.mit.edu>
END_OF_FILE
if test 6541 -ne `wc -c <'mush/sample.mushrc'`; then
echo shar: \"'mush/sample.mushrc'\" unpacked with wrong size!
fi
# end of 'mush/sample.mushrc'
fi
if test -f 'mush/tool.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'mush/tool.c'\"
else
echo shar: Extracting \"'mush/tool.c'\" \(15050 characters\)
sed "s/^X//" >'mush/tool.c' <<'END_OF_FILE'
X/* @(#)tool.c (c) copyright 10/15/86 (Dan Heller) */
X
X/* tool.c --make the mailtool windows, panels, etc... */
X#include "mush.h"
X
X#ifndef FONTDIR
X#define FONTDIR "/usr/lib/fonts/fixedwidthfonts/"
X#endif /* FONTDIR */
X
Xextern void print_sigwinch(), make_hdr_sw();
Xextern Notify_value fkey_interposer();
X
Xstatic void init_cursors(), geticon();
X
Xextern Panel /* panels.c */
X make_hdr_panel(), make_main_panel();
X
XPanel
X main_panel, /* the main panel dealing with generic items */
X hdr_panel; /* panel which contains message header specific items */
X
XTextsw cprint_sw, mfprint_sw, wprint_sw;
XFrame compose_frame;
X
Xstatic char **choice_args, **button_args;
X
Xshort dat_mail_icon_1[] = {
X#include "mail.icon.1"
X};
X
Xshort dat_mail_icon_2[] = {
X#include "mail.icon.2"
X};
X
Xshort dat_coffee_cup[] = {
X 0x0200,0x0100,0x0600,0x0800,0x0600,0x0100,0xFFF8,0x800C,
X 0x800A,0x4012,0x401C,0x2020,0x9048,0x7FF0,0x3FE0,0x0000
X};
X
Xmpr_static(mail_icon_image1, 64, 64, 1, dat_mail_icon_1);
Xmpr_static(mail_icon_image2, 64, 64, 1, dat_mail_icon_2);
X
Xmpr_static(coffee_cup, 16, 16, 1, dat_coffee_cup);
X
X/* public for hdr_sw.c */
XCursor l_cursor, m_cursor, r_cursor;
Xstatic Cursor coffee;
X
X/* text and font will be set in mail_status() */
XIcon mail_icon;
X
Xstatic Notify_value scroll_hdr();
X
Xmake_tool()
X{
X Rect mrect; /* Position and size of icon label. */
X struct stat rootbuf, tmpbuf;
X char *p;
X struct pixfont *pf_open();
X
X if (p = do_set(set_options, "font")) {
X char buf[MAXPATHLEN];
X (void) sprintf(buf, "%s%s", FONTDIR, p);
X if (!(mush_font = pf_open(buf)))
X print("couldn't open font \"%s\"\nUsing default font.\n", buf);
X }
X if (!mush_font)
X mush_font = pf_default();
X
X geticon();
X check_icons();
X
X if (p = do_set(set_options, "screen_win"))
X screen = atoi(p);
X else
X screen = 6;
X
X /* where to place text on mail icon -- how many messages there are */
X mrect.r_left = l_width();
X mrect.r_top = 58-l_height();
X mrect.r_width = 3*l_width();
X mrect.r_height = l_height();
X (void) icon_set(mail_icon, ICON_LABEL_RECT, &mrect, NULL);
X
X (void) window_set(tool,
X FRAME_ICON, mail_icon,
X WIN_CONSUME_KBD_EVENTS,
X WIN_LEFT_KEYS, WIN_TOP_KEYS, WIN_RIGHT_KEYS, NULL,
X NULL);
X (void) notify_interpose_destroy_func(tool, destroy_proc);
X
X choice_args = panel_make_list(
X PANEL_MENU_TITLE_FONT, mush_font,
X PANEL_DISPLAY_LEVEL, PANEL_NONE,
X PANEL_SHOW_MENU, TRUE,
X PANEL_SHOW_MENU_MARK, FALSE,
X NULL);
X
X button_args = panel_make_list(
X PANEL_FEEDBACK, PANEL_INVERTED,
X PANEL_SHOW_MENU, FALSE,
X NULL);
X
X hdr_panel = make_hdr_panel(tool, choice_args, button_args);
X
X make_hdr_sw(tool);
X
X main_panel = make_main_panel(tool, choice_args, button_args);
X
X /* main mush frame text subwindow for wprint() */
X mfprint_sw = window_create(tool, TEXTSW,
X WIN_BELOW, main_panel,
X WIN_HEIGHT, l_height() * 4,
X TEXTSW_READ_ONLY, TRUE,
X TEXTSW_BLINK_CARET, FALSE,
X TEXTSW_LINE_BREAK_ACTION, TEXTSW_WRAP_AT_CHAR,
X NULL);
X /* order is important -- scroll_textwin should be called first! */
X (void) notify_interpose_event_func(mfprint_sw,
X fkey_interposer, NOTIFY_SAFE);
X (void) notify_interpose_event_func(mfprint_sw,
X scroll_textwin, NOTIFY_SAFE);
X
X /* text subwindow for paging messages */
X p = do_set(set_options, "crt_win");
X pager_textsw = window_create(tool, TEXTSW,
X WIN_HEIGHT, l_height() * (p? atoi(p) : 12),
X TEXTSW_READ_ONLY, TRUE,
X TEXTSW_BLINK_CARET, FALSE,
X#ifdef SUN_4_0 /* SunOS 4.0+ */
X TEXTSW_LINE_BREAK_ACTION, TEXTSW_WRAP_AT_WORD,
X#else /* SUN_4_0 */
X TEXTSW_LINE_BREAK_ACTION, TEXTSW_WRAP_AT_CHAR,
X#endif /* SUN_4_0 */
X NULL);
X /* order is important -- scroll_textwin should be called first! */
X (void) notify_interpose_event_func(pager_textsw,
X fkey_interposer, NOTIFY_SAFE);
X (void) notify_interpose_event_func(pager_textsw,
X scroll_textwin, NOTIFY_SAFE);
X
X (void) sprintf(blank, "%128c", ' ');
X mrect = *(Rect *)window_get(hdr_sw, WIN_RECT);
X pw_writebackground(hdr_win, 0,0, mrect.r_width, mrect.r_height, PIX_CLR);
X istool = 2;
X (void) fclose(stdin);
X (void) fclose(stdout);
X#ifndef SUN_3_5
X /* SunOS 3.5 takes tty modes for ttysws from stderr. */
X (void) fclose(stderr);
X#endif /* SUN_3_5 */
X (void) do_version();
X init_cursors();
X timeout_cursors(TRUE);
X window_fit_height(tool);
X}
X
Xvoid
Xclose_frame()
X{
X window_set(compose_frame, WIN_SHOW, FALSE, NULL);
X}
X
Xvoid
Xmake_compose_frame()
X{
X char *p;
X Textsw msg_sw;
X Panel panel, make_compose_panel();
X
X#ifdef SUN_3_5
X if (nopenfiles(0) < 8) {
X ok_box("Too many frames; close one, please.\n");
X return;
X }
X#endif /* SUN_3_5 */
X
X compose_frame = window_create(tool, FRAME,
X FRAME_LABEL, "Compose Letter",
X FRAME_SHOW_LABEL, TRUE,
X FRAME_NO_CONFIRM, TRUE,
X FRAME_DONE_PROC, close_frame,
X WIN_SHOW, TRUE,
X NULL);
X
X panel = make_compose_panel(compose_frame, choice_args, button_args);
X
X#ifdef CPRINT_SW
X /* text subwindow for wprint() */
X cprint_sw = window_create(compose_frame, TEXTSW,
X WIN_HEIGHT, l_height() * 4,
X WIN_BELOW, panel,
X TEXTSW_READ_ONLY, TRUE,
X TEXTSW_BLINK_CARET, FALSE,
X TEXTSW_LINE_BREAK_ACTION, TEXTSW_WRAP_AT_CHAR,
X NULL);
X notify_interpose_event_func(cprint_sw, scroll_textwin, NOTIFY_SAFE);
X (void) notify_interpose_event_func(cprint_sw,
X fkey_interposer, NOTIFY_SAFE);
X#else /* CPRINT_SW */
X /* not enough fd's in SunOS 3.X for the extra textsw */
X cprint_sw = (Textsw) 0;
X#endif /* CPRINT_SW */
X
X /* text subwindow for composing messages */
X p = do_set(set_options, "msg_win");
X msg_sw = window_create(compose_frame, TEXTSW,
X#ifdef CPRINT_SW
X WIN_BELOW, cprint_sw,
X#else /* CPRINT_SW */
X WIN_BELOW, panel,
X#endif /* CPRINT_SW */
X WIN_HEIGHT, l_height() * (p? atoi(p) : 24),
X TEXTSW_READ_ONLY, TRUE, /* set to false later */
X TEXTSW_BLINK_CARET, FALSE,
X TEXTSW_LINE_BREAK_ACTION, TEXTSW_WRAP_AT_CHAR,
X TEXTSW_IGNORE_LIMIT, TEXTSW_INFINITY,
X NULL);
X notify_interpose_event_func(msg_sw, fkey_interposer, NOTIFY_SAFE);
X notify_interpose_event_func(msg_sw, edit_msg_textwin, NOTIFY_SAFE);
X
X /* Assign textsw (msg_sw) to panel for panel items' callbacks */
X panel_set(panel, PANEL_CLIENT_DATA, msg_sw, NULL);
X
X /* tty subwindow */
X if (!(tty_sw = window_create(compose_frame, TTY,
X TTY_QUIT_ON_CHILD_DEATH, FALSE,
X TTY_ARGV, TTY_ARGV_DO_NOT_FORK,
X WIN_CLIENT_DATA, msg_sw,
X WIN_SHOW, FALSE,
X WIN_WIDTH, WIN_EXTEND_TO_EDGE,
X WIN_BELOW, msg_sw,
X NULL)))
X perror("tty_sw"), cleanup(0);
X
X window_fit_height(compose_frame);
X}
X
X/*ARGSUSED*/
Xvoid
Xopen_compose()
X{
X if (compose_frame)
X window_set(compose_frame, WIN_SHOW, TRUE, NULL);
X else
X make_compose_frame();
X}
X
Xparse_tool_opts(argcp, argv)
Xint *argcp;
Xchar **argv;
X{
X if (!(tool = window_create((Window) 0, FRAME,
X FRAME_ARGC_PTR_ARGV, argcp, argv,
X NULL)))
X cleanup(0);
X}
X
X/*
X * used by both the hdr_sw (to scroll canvas) and by textsw's.
X * Return values:
X * MUSH_SCROLL_TO (to scroll _to_ a particular location)
X * MUSH_SCROLL_RELATIVE (scroll relative our current location)
X * MUSH_SCROLL_IGNORE (not a scroll event and ignore it entirely (up events))
X * MUSH_SCROLL_PASS_EVENT (not a scroll event; pass it to next event handler)
X * If absolute scrolling (scroll_to) then "amount" is set to 1 for beginning
X * of textsw or 2 for end of textsw.
X * User can precede a keyboard scrolling request by a "count" -- the
X * count is typed by the user in digits: 5j = move 5 lines down.
X * It is assumed that if the user moves the mouse after a digit is pressed,
X * then he doesn't really want to adjust the scrolling amount and the
X * count is reset to the default (1).
X */
XScroll_action
Xdecode_scroll(client, event, len, amount)
XNotify_client client;
XEvent *event;
Xint len, *amount;
X{
X static int count;
X
X if (event_id(event) == LOC_MOVE) {
X count = 0;
X return MUSH_SCROLL_PASS_EVENT;
X }
X
X *amount = 0; /* Assume relative scroll */
X if (ID == SCROLL_REQUEST)
X return MUSH_SCROLL_PASS_EVENT;
X
X if (event_is_up(event))
X return MUSH_SCROLL_PASS_EVENT;
X if (event_is_ascii(event) && isdigit(event_id(event))) {
X count = (count * 10) + event_id(event) - '0';
X return MUSH_SCROLL_IGNORE;
X }
X#ifdef SUN_4_0 /* SunOS 4.0+ */
X /* returns sunview events for some ctl chars */
X switch (event_action(event)) {
X case ACTION_GO_LINE_FORWARD:
X case ACTION_GO_COLUMN_FORWARD:
X *amount = count ? count : 1;
X count = 0;
X return MUSH_SCROLL_RELATIVE;
X case ACTION_GO_LINE_BACKWARD:
X case ACTION_GO_COLUMN_BACKWARD:
X *amount = count ? -count : -1;
X count = 0;
X return MUSH_SCROLL_RELATIVE;
X case ACTION_GO_DOCUMENT_START:
X *amount = 1;
X count = 0;
X return MUSH_SCROLL_TO;
X case ACTION_GO_DOCUMENT_END:
X *amount = 2;
X count = 0;
X return MUSH_SCROLL_TO;
X }
X#endif /* SUN_4_0 */
X /* for SunOS 3.5, assume default SunView key mapping */
X /* a little redundancy for 4.0, but it's ok */
X if (!event_is_ascii(event) && ID != KEY_RIGHT(14) && ID != KEY_RIGHT(8)
X && ID != KEY_RIGHT(7) && ID != KEY_RIGHT(13))
X /* may have to reset "count" here */
X return MUSH_SCROLL_PASS_EVENT; /* Not a scroll event */
X switch (event_id(event)) {
X case KEY_RIGHT(7): /* Home on new keyboards */
X *amount = 1, count = 0;
X return MUSH_SCROLL_TO;
X case KEY_RIGHT(13): /* End on new keyboards */
X *amount = 2, count = 0;
X return MUSH_SCROLL_TO;
X case 'j': case KEY_RIGHT(14): /* downarrow */
X *amount = count ? count : 1;
X when 'k': case KEY_RIGHT(8): /* uparrow */
X *amount = count ? -count : -1;
X when 'F'-'@': /* ^f */
X *amount = count ? count * (len-1) : len - 1;
X when 'D'-'@': /* ^d */
X *amount = len/2;
X when 'B'-'@': /* ^b */
X *amount = -(count ? count * (1-len) : len - 1);
X when 'U'-'@': /* ^u */
X *amount = -len/2;
X when '\033': /* Escape */
X /* For SunOS 3.5 check to see if this is a cursor
X * move key, i.e. ESC[A or ESC[B.
X */
X if (!(int)window_read_event(client, event) &&
X event_id(event) == '[' &&
X !(int)window_read_event(client, event))
X if (event_id(event) == 'A') {
X *amount = -1;
X break;
X } else if (event_id(event) == 'B') {
X *amount = 1;
X break;
X }
X default:
X count = 0;
X return event_is_ascii(event) ?
X MUSH_SCROLL_IGNORE : MUSH_SCROLL_PASS_EVENT;
X }
X count = 0;
X /* Scroll indicated amount if event is down, ignore up events */
X return MUSH_SCROLL_RELATIVE;
X}
X
XNotify_value
Xscroll_textwin(textsw, event, arg, type)
XTextsw textsw;
XEvent *event;
XNotify_arg arg;
XNotify_event_type type;
X{
X int scroll_amount;
X
X switch (decode_scroll(textsw, event, textsw_screen_line_count(textsw),
X &scroll_amount)) {
X case MUSH_SCROLL_PASS_EVENT :
X return notify_next_event_func(textsw, event, arg, type);
X case MUSH_SCROLL_IGNORE:
X return NOTIFY_IGNORED;
X case MUSH_SCROLL_TO:
X if (scroll_amount == 1)
X window_set(textsw, TEXTSW_FIRST, 0, NULL);
X else if (scroll_amount == 2)
X window_set(textsw, TEXTSW_FIRST, TEXTSW_INFINITY, NULL);
X textsw_scroll_lines(textsw, -textsw_screen_line_count(textsw));
X break;
X case MUSH_SCROLL_RELATIVE :
X textsw_scroll_lines(textsw, scroll_amount);
X }
X window_set(textsw, TEXTSW_UPDATE_SCROLLBAR, NULL);
X return NOTIFY_DONE;
X}
X
X/*ARGSUSED*/
XNotify_value
Xdestroy_proc(frame, status)
XFrame frame;
XDestroy_status status;
X{
X if (ison(glob_flags, IS_GETTING))
X rm_edfile(-1);
X /* status is ignored -- maybe post notice asking to confirm quit? */
X if (status == DESTROY_CHECKING && ison(glob_flags, DO_UPDATE) &&
X !ask("Your folder has been modified. Quit anyway?"))
X (void) notify_veto_destroy(frame);
X else
X cleanup(0); /* doesn't return */
X return NOTIFY_DONE;
X}
X
X/* Initialise the Mush mail icon. */
Xstatic void
Xgeticon()
X{
X static Rect lrect = { 5, 5, 26, 12 };
X
X mail_icon = icon_create(
X ICON_WIDTH, 64,
X ICON_HEIGHT, 64,
X ICON_FONT, mush_font,
X ICON_IMAGE, &mail_icon_image1,
X ICON_LABEL, "",
X ICON_LABEL_RECT, &lrect,
X 0);
X}
X
X/* Initialise all the cursors used. */
Xstatic void
Xinit_cursors()
X{
X extern Pixrect mouse_left, mouse_middle, mouse_right, bent_arrow;
X extern Cursor bentarrow;
X
X l_cursor = cursor_create(
X CURSOR_XHOT, 3,
X CURSOR_YHOT, 3,
X CURSOR_OP, PIX_SRC,
X CURSOR_IMAGE, &mouse_left,
X NULL);
X m_cursor = cursor_create(
X CURSOR_XHOT, 3,
X CURSOR_YHOT, 3,
X CURSOR_OP, PIX_SRC,
X CURSOR_IMAGE, &mouse_middle,
X NULL);
X r_cursor = cursor_create(
X CURSOR_XHOT, 3,
X CURSOR_YHOT, 3,
X CURSOR_OP, PIX_SRC,
X CURSOR_IMAGE, &mouse_right,
X NULL);
X bentarrow = cursor_create(
X CURSOR_XHOT, 8,
X CURSOR_YHOT, 8,
X CURSOR_OP, PIX_SRC|PIX_DST,
X CURSOR_IMAGE, &bent_arrow,
X NULL);
X coffee = cursor_create(
X CURSOR_XHOT, 8,
X CURSOR_YHOT, 8,
X CURSOR_OP, PIX_SRC,
X CURSOR_IMAGE, &coffee_cup,
X NULL);
X}
X
X/* show the timeout cursor (coffee cup) when "on" is TRUE. This routine
X * may be called many times in layers of locking mechanisms, so be careful
X * not to unlock cursors until "on" has been FALSE as many times as it has
X * been TRUE.
X */
Xvoid
Xtimeout_cursors(on)
Xint on;
X{
X Window win;
X Frame subframe;
X static int locked, numwins;
X int i = 0, j;
X static struct {
X Cursor cursor;
X Window win;
X } win_curs[64];
X
X on? locked++ : locked--;
X if (istool < 2 || locked > 1 || locked == 1 && on == 0)
X return;
X if (on) {
X for (numwins = 0; win = window_get(tool, FRAME_NTH_SUBWINDOW, numwins);
X numwins++) {
X win_curs[numwins].cursor =
X cursor_copy((Cursor) window_get(win, WIN_CURSOR));
X win_curs[numwins].win = win;
X window_set(win, WIN_CURSOR, coffee, NULL);
X }
X while (subframe = window_get(tool, FRAME_NTH_SUBFRAME, i++))
X for (j = 0; win = window_get(subframe, FRAME_NTH_SUBWINDOW, j);
X j++,numwins++) {
X win_curs[numwins].cursor =
X cursor_copy((Cursor) window_get(win, WIN_CURSOR));
X win_curs[numwins].win = win;
X window_set(win, WIN_CURSOR, coffee, NULL);
X }
X } else {
X for (j = 0; j < numwins; j++) {
X window_set(win_curs[j].win, WIN_CURSOR, win_curs[j].cursor, NULL);
X cursor_destroy(win_curs[j].cursor);
X }
X }
X}
X
X/*
X * If the user has specified an alternate icon or set of icons in
X * his .mushrc file, copy the image(s) over the defaults.
X */
Xvoid
Xcheck_icons()
X{
X Pixrect *icon_mpr;
X char errbuf[256], *icon_file, *icon_path;
X int isdir;
X
X if ((icon_file = do_set(set_options, "mail_icon")) && *icon_file) {
X isdir = 0;
X icon_path = getpath(icon_file, &isdir);
X if (isdir == 0) {
X if (!(icon_mpr = icon_load_mpr(icon_path, errbuf)))
X error("Error loading mail icon file:\n%s",errbuf);
X else
X pr_rop(&mail_icon_image1, 0,0,64,64, PIX_SRC, icon_mpr, 0, 0);
X }
X }
X if ((icon_file = do_set(set_options, "newmail_icon")) && *icon_file) {
X isdir = 0;
X icon_path = getpath(icon_file, &isdir);
X if (isdir == 0) {
X if (!(icon_mpr = icon_load_mpr(icon_path, errbuf)))
X error("Error loading newmail icon file:\n%s",errbuf);
X else
X pr_rop(&mail_icon_image2, 0,0,64,64, PIX_SRC, icon_mpr, 0, 0);
X }
X }
X}
END_OF_FILE
if test 15050 -ne `wc -c <'mush/tool.c'`; then
echo shar: \"'mush/tool.c'\" unpacked with wrong size!
fi
# end of 'mush/tool.c'
fi
echo shar: End of archive 16 \(of 19\).
cp /dev/null ark16isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ; do
if test ! -f ark${I}isdone ; then
MISSING="${MISSING} ${I}"
fi
done
if test "${MISSING}" = "" ; then
echo You have unpacked all 19 archives.
rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
echo You still need to unpack the following archives:
echo " " ${MISSING}
fi
## End of shell archive.
exit 0