home *** CD-ROM | disk | FTP | other *** search
/ Garbo / Garbo.cdr / pc / source / mush.lzh / mush.16 < prev    next >
Text File  |  1990-05-06  |  55KB  |  1,783 lines

  1.  
  2. #! /bin/sh
  3. # This is a shell archive.  Remove anything before this line, then feed it
  4. # into a shell via "sh file" or similar.  To overwrite existing files,
  5. # type "sh file -c".
  6. # The tool that generated this appeared in the comp.sources.unix newsgroup;
  7. # send mail to comp-sources-unix@uunet.uu.net if you want that tool.
  8. # If this archive is complete, you will see the following message at the end:
  9. #        "End of archive 16 (of 19)."
  10. # Contents:  mush/dates.c mush/file.c mush/sample.mushrc mush/tool.c
  11. # Wrapped by argv@turnpike on Wed May  2 13:59:48 1990
  12. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  13. if test -f 'mush/dates.c' -a "${1}" != "-c" ; then 
  14.   echo shar: Will not clobber existing file \"'mush/dates.c'\"
  15. else
  16. echo shar: Extracting \"'mush/dates.c'\" \(14562 characters\)
  17. sed "s/^X//" >'mush/dates.c' <<'END_OF_FILE'
  18. X/* @(#)dates.c    3.0    (c) copyright 3/01/90 (Dan Heller, Bart Schaefer) */
  19. X
  20. X#include "mush.h"
  21. X
  22. X/*
  23. X *   %ld%3c%s    gmt_in_secs weekday orig_timezone
  24. X * The standard "date format" stored in the msg data structure.
  25. X */
  26. Xchar *day_names[] = {
  27. X    "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
  28. X};
  29. Xchar *month_names[] = {     /* imported in pick.c */
  30. X    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  31. X    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
  32. X};
  33. X
  34. Xstatic int mtbl[] = { 0,31,59,90,120,151,181,212,243,273,304,334 };
  35. X
  36. X/* Time Zone Stuff */
  37. Xstruct zoneoff {
  38. X    char *zname;
  39. X    int hr_off;
  40. X    int mn_off;
  41. X} time_zones[] = {
  42. X    /* Universal Time */
  43. X    { "UT",      0,  0 },    { "GMT",      0,  0 },
  44. X    /* European Time */
  45. X    { "BST",      1,  0 },                    /* Brit. Summer */
  46. X    { "EET",      0,  0 },    { "EEST",      1,  0 },    /* Eastern */
  47. X    { "MET",     -1,  0 },    { "MEST",      0,  0 },    /* Middle */
  48. X    { "WET",     -2,  0 },    { "WEST",     -1,  0 },    /* Western */
  49. X    /* North American Time */
  50. X    { "NST",     -3,-30 },                    /* Newfoundland */
  51. X    { "AST",     -4,  0 },    { "ADT",     -3,  0 },    /* Atlantic */
  52. X    { "EST",     -5,  0 },    { "EDT",     -4,  0 },    /* Eastern */
  53. X    { "CST",     -6,  0 },    { "CDT",     -5,  0 },    /* Central */
  54. X    { "MST",     -7,  0 },    { "MDT",     -6,  0 },    /* Mountain */
  55. X    { "PST",     -8,  0 },    { "PDT",     -7,  0 },    /* Pacific */
  56. X    { "YST",     -9,  0 },    { "YDT",     -8,  0 },    /* Yukon */
  57. X    { "HST",    -10,  0 },    { "HDT",     -9,  0 },    /* Hawaii */
  58. X    /* Japan and Australia Time */
  59. X    {"JST",      9,  0 },                    /* Japan */
  60. X    {"AEST",     10,  0 },    {"AESST",     11,  0 },    /* Eastern */    
  61. X    {"ACST",      9, 30 },    {"ACSST",     10, 30 },    /* Central */
  62. X    {"AWST",      8,  0 },                    /* Western */
  63. X    /* Military Time */
  64. X    { "A",      1,  0 },    { "N",         -1,  0 },
  65. X    { "B",      2,  0 },    { "O",         -2,  0 },
  66. X    { "C",      3,  0 },    { "P",         -3,  0 },
  67. X    { "D",      4,  0 },    { "Q",         -4,  0 },
  68. X    { "E",      5,  0 },    { "R",         -5,  0 },
  69. X    { "F",      6,  0 },    { "S",         -6,  0 },
  70. X    { "G",      7,  0 },    { "T",         -7,  0 },
  71. X    { "H",      8,  0 },    { "U",         -8,  0 },
  72. X    { "I",      9,  0 },    { "V",         -9,  0 },
  73. X    { "K",     10,  0 },    { "W",        -10,  0 },
  74. X    { "L",     11,  0 },    { "X",        -11,  0 },
  75. X    { "M",     12,  0 },    { "Y",        -12,  0 },
  76. X    { "Z",      0,  0 },
  77. X    /* Also legal is +/- followed by hhmm offset from UT */
  78. X    { 0, 0, 0 }
  79. X};
  80. X
  81. Xlong
  82. Xgetzoff(zone)
  83. Xchar *zone;
  84. X{
  85. X    struct zoneoff *z;
  86. X    int hours, mins;
  87. X    char sign[2];
  88. X
  89. X    if (!zone || !*zone)
  90. X    return 0;
  91. X    if (sscanf(zone, "%1[-+]%2d%2d", sign, &hours, &mins) == 3)
  92. X    return (hours * 3600 + mins * 60) * (*sign == '-' ? -1 : 1);
  93. X    for (z = time_zones; z->zname; z++)
  94. X    if (lcase_strncmp(zone, z->zname, -1) == 0)
  95. X        return z->hr_off * 3600 + z->mn_off * 60;
  96. X    return 0;
  97. X}
  98. X
  99. X/*
  100. X * Kind of the reverse of localtime() and gmtime() -- converts a struct tm
  101. X * to time in seconds since 1970.  Valid until 2038.
  102. X * If the "zone" argument is present, it modifies the return value.
  103. X * The zone should be a string, either +/-hhmm or symbolic (above).
  104. X * The "how" argument should be -1 to convert FROM gmt, 1 to convert TO gmt,
  105. X * and (as a "side-effect") 0 if the Zone parameter is to be ignored.
  106. X *
  107. X * Thanks to ktl@wag240.caltech.edu (Kian-Tat Lim) for similar algorithm
  108. X * written in perl from which this was derived.
  109. X */
  110. Xlong
  111. Xtime2gmt(tym, zone, how)
  112. Xstruct tm *tym;
  113. Xchar *zone;
  114. Xint how;
  115. X{
  116. X    long year, julian;
  117. X
  118. X    if (tym->tm_year < 100)
  119. X    year = tym->tm_year + 1900;
  120. X    if (year < 69)
  121. X    year += 100;
  122. X
  123. X    julian = 365 * (year - 1970) + (int)((year - 1970 + 1) / 4) +
  124. X        mtbl[tym->tm_mon] + tym->tm_mday - 1;
  125. X        /* tym->tm_yday might not be valid */
  126. X    if (tym->tm_mon > 1 && year%4 == 0 && (year%100 != 0 || year%400 == 0))
  127. X    julian++;
  128. X    julian *= 86400;    /* convert to seconds */
  129. X    julian += (tym->tm_hour * 60 + tym->tm_min) * 60 + tym->tm_sec;
  130. X    return julian - getzoff(zone) * how;
  131. X}
  132. X
  133. Xstruct tm *
  134. Xtime_n_zone(zone)
  135. Xchar *zone;
  136. X{
  137. X    struct tm *T;
  138. X    char *tz;
  139. X#if defined(SYSV) || defined(TIMEZONE)
  140. X    long      x;
  141. X
  142. X    (void) time(&x);
  143. X    T = localtime(&x);
  144. X#ifndef TIMEZONE
  145. X    {
  146. X    extern char *tzname[];
  147. X    tz = tzname[T->tm_isdst];
  148. X    }
  149. X#endif /* TIMEZONE */
  150. X#else /* SYSV || TIMEZONE */
  151. X    extern char     *timezone();
  152. X    struct timeval  mytime;
  153. X    struct timezone myzone;
  154. X
  155. X    (void) gettimeofday(&mytime, &myzone);
  156. X    T = localtime(&mytime.tv_sec);
  157. X    tz = timezone(myzone.tz_minuteswest, (T->tm_isdst && myzone.tz_dsttime));
  158. X#endif /* !SYSV */
  159. X
  160. X#ifdef TIMEZONE
  161. X#ifdef DAYLITETZ
  162. X    if (T->tm_isdst)
  163. X    tz = DAYLITETZ;
  164. X    else
  165. X#endif /* DAYLITETZ */
  166. X    tz = TIMEZONE;
  167. X#endif /* TIMEZONE */
  168. X
  169. X    (void) strncpy(zone, tz, 7), zone[7] = 0;
  170. X    return T;
  171. X}
  172. X
  173. X/* Time() returns a string according to criteria:
  174. X *   if "now" is 0, then the current time is gotten and used.
  175. X *       else, use the time described by now
  176. X *   opts points to a string of args which is parsed until an unknown
  177. X *       arg is found and opts will point to that upon return.
  178. X *   valid args are T (time of day), D (day of week), M (month), Y (year),
  179. X *       N (number of day in month -- couldn't think of a better letter).
  180. X */
  181. Xchar *
  182. XTime(opts, now)
  183. Xregister char *opts;
  184. Xlong now;
  185. X{
  186. X    static char time_buf[30];
  187. X    struct tm       *T;
  188. X    register char *p = time_buf;
  189. X    long      x;
  190. X
  191. X    if (!opts)
  192. X    return NULL;
  193. X    if (now)
  194. X    x = now;
  195. X    else
  196. X    (void) time(&x);
  197. X    T = localtime(&x);
  198. X    for (;; opts++) {
  199. X    switch(*opts) {
  200. X        case 'T':
  201. X        if (ison(glob_flags, MIL_TIME))
  202. X            (void) sprintf(p, "%2d:%02d", T->tm_hour, T->tm_min);
  203. X        else
  204. X            (void) sprintf(p, "%d:%02d", (T->tm_hour) ?
  205. X              ((T->tm_hour <= 12) ? T->tm_hour : T->tm_hour - 12) :
  206. X              12, T->tm_min);
  207. X        when 'D': case 'W': (void) strcpy(p, day_names[T->tm_wday]);
  208. X        when 'M': (void) strcpy(p, month_names[T->tm_mon]);
  209. X        when 'y': (void) sprintf(p, "%d", T->tm_year);
  210. X        when 'Y': (void) sprintf(p, "%d", T->tm_year + 1900);
  211. X        when 'N': (void) sprintf(p, "%d", T->tm_mday);
  212. X        otherwise: *--p = 0; return time_buf;
  213. X    }
  214. X    p += strlen(p);
  215. X    *p++ = ' ';
  216. X    }
  217. X}
  218. X
  219. X/* parse date and return a string that looks like
  220. X *   %ld%3c%s    gmt_in_secs weekday orig_timezone
  221. X * This function is a bunch of scanfs on known date formats.  Don't
  222. X * trust the "weekday" name fields because they may not be spelled
  223. X * right, or have the correct punctuation.  Figure it out once the
  224. X * year and month and date have been determined.
  225. X */
  226. Xchar *
  227. Xparse_date(p)
  228. Xregister char *p;
  229. X{
  230. X    /* When scanf-ing if month isn't a month, it could be a _long_ string.
  231. X     * this is also the static buffer whose address we return.
  232. X     */
  233. X    static char month[64];
  234. X    char Wkday[4], Zone[8];
  235. X    char a_or_p;
  236. X    int Month = 0, Day = 0, Year = 0;
  237. X    int Uhour = 0, Umin = 0, Hours = -1, Mins = -1;
  238. X    struct tm T;
  239. X
  240. X    Zone[0] = 0;
  241. X    skipspaces(0);
  242. X
  243. X    /* programmer's note -- there are too many scanfs here for some compilers
  244. X     * to put them all into one if statement.  Use goto's :-(  Also reset
  245. X     * Zone[0] after any sscanf() that could corrupt it on a partial match.
  246. X     */
  247. X
  248. X    /* RFC822 formats and minor variations -- order important */
  249. X
  250. X    /*   day_number month_name year_number time timezone */
  251. X    if (sscanf(p, "%d %s %d %d:%d:%*d %7s",
  252. X        &Day, month, &Year, &Hours, &Mins, Zone) >= 5 && Day)
  253. X    goto gotit;
  254. X    Zone[0] = 0;
  255. X    if (sscanf(p, "%d %s %d %d:%d %7s",
  256. X        &Day, month, &Year, &Hours, &Mins, Zone) >= 5 && Day)
  257. X    goto gotit;
  258. X    Zone[0] = 0;
  259. X    /*   day_name day_number month_name year_number time timezone */
  260. X    if (sscanf(p, "%*s %d %s %d %d:%d:%*d %7s",
  261. X        &Day, month, &Year, &Hours, &Mins, Zone) >= 5 && Day)
  262. X    goto gotit;
  263. X    Zone[0] = 0;
  264. X    if (sscanf(p, "%*s %d %s %d %d:%d %7s",
  265. X        &Day, month, &Year, &Hours, &Mins, Zone) >= 5 && Day)
  266. X    goto gotit;
  267. X    Zone[0] = 0;
  268. X
  269. X    /* Ctime format (From_ lines) -- timezone almost never found */
  270. X
  271. X    /*   day_name month_name day_number time year_number */
  272. X    if (sscanf(p, "%*s %s %d %d:%d:%*d %d %7s",
  273. X        month, &Day, &Hours, &Mins, &Year, Zone) >= 5)
  274. X    goto gotit;
  275. X    Zone[0] = 0;
  276. X
  277. X    /* Other common variants */
  278. X
  279. X    /*   day_number month_name year_number time-timezone (day) */
  280. X    /*                                       ^no colon separator */
  281. X    if (sscanf(p, "%d %s %d %2d%2d-%6[0123456789]",
  282. X        &Day, month, &Year, &Hours, &Mins, &Zone[1]) == 6) {
  283. X    Zone[0] = '-';
  284. X    goto gotit;
  285. X    }
  286. X    if (sscanf(p, "%d %s %d %2d%2d-%7s",
  287. X        &Day, month, &Year, &Hours, &Mins, Zone) == 6)
  288. X    goto gotit;
  289. X    Zone[0] = 0;
  290. X
  291. X    /*   day_number month_name year_number time timezone    */
  292. X    /*                                      ^no colon separator */
  293. X    /*   (This is the odd one in the RFC822 examples section;    */
  294. X    /*    also catches the slop from partial hits above.)    */
  295. X    if (sscanf(p, "%d %s %d %2d%2d %7s",
  296. X        &Day, month, &Year, &Hours, &Mins, Zone) >= 5 && Day)
  297. X    goto gotit;
  298. X    Zone[0] = 0;
  299. X    
  300. X    Zone[1] = 0;    /* Yes, Zone[1] -- tested below */
  301. X
  302. X    /*   day_number month_name year_number, time "-" ?? */
  303. X    if (sscanf(p,"%d %s %d, %d:%d:%*d -%6[0123456789]",
  304. X        &Day, month, &Year, &Hours, &Mins, &Zone[1]) >= 5 && Day) {
  305. X    if (Zone[1])
  306. X        Zone[0] = '-';
  307. X    goto gotit;
  308. X    }
  309. X
  310. X    /*   day_number month_name year_number 12_hour_time a_or_p */
  311. X    if (sscanf(p, "%d %s %d %d:%d:%*d %cm %7s",
  312. X        &Day, month, &Year, &Hours, &Mins, &a_or_p, Zone) >= 6) {
  313. X    if (a_or_p == 'p')
  314. X        Hours += 12;
  315. X    goto gotit;
  316. X    }
  317. X
  318. X    /*   day_name month_name day_number year_number time */
  319. X    if (sscanf(p, "%*s %s %d %d %d:%d:%*d %7s",
  320. X        month, &Day, &Year, &Hours, &Mins, Zone) >= 5)
  321. X    goto gotit;
  322. X    Zone[0] = 0;
  323. X    if (sscanf(p, "%*s %s %d %d %d:%d %7s",
  324. X        month, &Day, &Year, &Hours, &Mins, Zone) >= 5)
  325. X    goto gotit;
  326. X    Zone[0] = 0;
  327. X
  328. X    /*   day_name month_name day_number time timezone year_number */
  329. X    if (sscanf(p, "%*s %s %d %d:%d:%*d %7s %d",
  330. X        month, &Day, &Hours, &Mins, Zone, &Year) == 6)
  331. X    goto gotit;
  332. X    Zone[0] = 0;
  333. X    if (sscanf(p, "%*s %s %d %d:%d %7s %d",
  334. X        month, &Day, &Hours, &Mins, Zone, &Year) == 6)
  335. X    goto gotit;
  336. X    Zone[0] = 0;
  337. X
  338. X    /*   day_number-month_name-year time */
  339. X    if (sscanf(p,"%d-%[^-]-%d %d:%d", &Day, month, &Year, &Hours, &Mins) == 5)
  340. X    goto gotit;
  341. X
  342. X    /*   day_name, day_number-month_name-year time */
  343. X    if (sscanf(p,"%*s %d-%[^-]-%d %d:%d",
  344. X        &Day, month, &Year, &Hours, &Mins) == 5)
  345. X    goto gotit;
  346. X
  347. X    /*   year_number-month_number-day_number time */
  348. X    if (sscanf(p, "%d-%d-%d %d:%d", &Year, &Month, &Day, &Hours, &Mins) == 5)
  349. X    goto gotit;
  350. X
  351. X    /*   month_name day_number time year Zone */
  352. X    /*   (ctime, but without the day name)    */
  353. X    if (sscanf(p, "%s %d %d:%d:%*d %d %7s",
  354. X        month, &Day, &Hours, &Mins, &Year, Zone) >= 5)
  355. X    goto gotit;
  356. X    Zone[0] = 0;
  357. X
  358. X    goto didnt_getit;
  359. X
  360. Xgotit:
  361. X    if (Year > 1900)
  362. X    Year -= 1900;
  363. X    if (!Month && (Month = month_to_n(month)) == -1) {
  364. X    print("bad month: %s\n", p);
  365. X    return NULL;
  366. X    }
  367. X    if (Zone[0] == 0) {
  368. X    /* Use local time zone if none found -- important for date_recv */
  369. X    (void) time_n_zone(Zone);
  370. X    }
  371. X    {
  372. X    /* Lots of foolishness with casts for Xenix-286 16-bit ints */
  373. X
  374. X    long days_ctr;    /* 16-bit ints overflowed Sept 12, 1989 */
  375. X
  376. X        days_ctr = ((long)Year * 365L) + ((Year + 3) / 4);
  377. X        days_ctr += mtbl[Month-1] + Day + 6;
  378. X        if (Month > 2 && (Year % 4 == 0))
  379. X        days_ctr++;
  380. X        (void) (sprintf(Wkday, "%.3s", day_names[(int)(days_ctr % 7L)]));
  381. X    }
  382. X    T.tm_sec = 0;        /* not recorded, so ignore it */
  383. X    T.tm_min = Mins;
  384. X    T.tm_hour = Hours;
  385. X    T.tm_mday = Day;
  386. X    T.tm_mon = Month - 1;
  387. X    T.tm_year = Year;
  388. X    T.tm_wday = T.tm_yday = 0;    /* not used in time2gmt() */
  389. X    T.tm_isdst = 0;        /* determined from Zone */
  390. X    return sprintf(month, "%ld%s%s", time2gmt(&T, Zone, 1), Wkday, Zone);
  391. Xdidnt_getit:
  392. X    if (ison(glob_flags, WARNING))
  393. X    print("Unknown date format: %s\n", p);
  394. X    return NULL;
  395. X}
  396. X
  397. X/* pass a string in the standard date format, put into string.
  398. X * return values in buffers provided they are not null.
  399. X */
  400. Xchar *
  401. Xdate_to_string(Date, Yr, Mon, Day, Wkday, Tm, Zone, ret_buf)
  402. Xchar *Date, *Yr, *Mon, *Day, *Wkday, *Tm, *Zone, *ret_buf;
  403. X{
  404. X    long gmt;
  405. X    struct tm *T;
  406. X    char a_or_p, *p = ret_buf;
  407. X
  408. X    Zone[0] = 0;
  409. X    (void) sscanf(Date, "%ld%3c%s", &gmt, Wkday, Zone);
  410. X    Wkday[3] = 0;
  411. X    gmt += getzoff(Zone);
  412. X    T = gmtime(&gmt);
  413. X    a_or_p = (T->tm_hour < 12)? 'a': 'p';
  414. X
  415. X    (void) sprintf(Yr, "%d", T->tm_year + 1900);
  416. X    (void) sprintf(Day, "%d", T->tm_mday);
  417. X    (void) strcpy(Mon, month_names[T->tm_mon]);
  418. X    p += strlen(sprintf(p, "%s %2.d, ", Mon, T->tm_mday));
  419. X
  420. X    if (ison(glob_flags, MIL_TIME))
  421. X    (void) sprintf(p, "%2d:%02d",T->tm_hour,T->tm_min);
  422. X    else
  423. X    (void) sprintf(p, "%2.d:%02d%cm",
  424. X          (T->tm_hour)? (T->tm_hour <= 12)? T->tm_hour: T->tm_hour-12: 12,
  425. X          T->tm_min, a_or_p);
  426. X    (void) strcpy(Tm, p);
  427. X
  428. X    return ret_buf;
  429. X}
  430. X
  431. X/* pass a string in the internal mush date format.
  432. X * return pointer to static buffer holding ctime-format date.
  433. X */
  434. Xchar *
  435. Xdate_to_ctime(Date)
  436. Xchar *Date;
  437. X{
  438. X    static char ret_buf[32];
  439. X    long gmt;
  440. X
  441. X    ret_buf[0] = 0;
  442. X    (void) sscanf(Date, "%ld", &gmt);
  443. X    (void) strcpy(ret_buf, ctime(&gmt));
  444. X
  445. X    return ret_buf;
  446. X}
  447. X
  448. X/*
  449. X * Build a date string according to the specification in the RFC for Date:
  450. X */
  451. Xchar *
  452. Xrfc_date(buf)
  453. Xchar buf[];
  454. X{
  455. X    struct tm *T;
  456. X    char zone[8];
  457. X
  458. X    T = time_n_zone(zone);
  459. X    return sprintf(buf, "%s, %d %s %d %02d:%02d:%02d %s",
  460. X    day_names[T->tm_wday],    /* day name */
  461. X    T->tm_mday,        /* day of the month */
  462. X    month_names[T->tm_mon],    /* month name */
  463. X    T->tm_year + 1900,    /* year number */
  464. X    T->tm_hour,        /* hours (24hr) */
  465. X    T->tm_min, T->tm_sec,    /* mins/secs */
  466. X    zone);            /* timezone */
  467. X}
  468. X
  469. X#define JAN    1
  470. X#define FEB    2
  471. X#define MAR    3
  472. X#define APR    4
  473. X#define MAY    5
  474. X#define JUN    6
  475. X#define JUL    7
  476. X#define AUG    8
  477. X#define SEP    9
  478. X#define OCT    10
  479. X#define NOV    11
  480. X#define DEC    12
  481. X
  482. X/* stolen direct from ELM */
  483. Xmonth_to_n(name)
  484. Xregister char *name;
  485. X{
  486. X    /** return the month number given the month name... **/
  487. X
  488. X    register char ch;
  489. X
  490. X    switch (lower(*name)) {
  491. X    case 'a' : if ((ch = lower(name[1])) == 'p')
  492. X               return(APR);
  493. X           else if (ch == 'u')
  494. X               return(AUG);
  495. X           else return(-1);    /* error! */
  496. X    case 'd' : return(DEC);
  497. X    case 'f' : return(FEB);
  498. X    case 'j' : if ((ch = lower(name[1])) == 'a')
  499. X               return(JAN);
  500. X           else if (ch == 'u') {
  501. X             if ((ch = lower(name[2])) == 'n')
  502. X             return(JUN);
  503. X             else if (ch == 'l')
  504. X             return(JUL);
  505. X             else return(-1);        /* error! */
  506. X           }
  507. X           else return(-1);        /* error */
  508. X    case 'm' : if ((ch = lower(name[2])) == 'r')
  509. X               return(MAR);
  510. X           else if (ch == 'y')
  511. X               return(MAY);
  512. X           else return(-1);        /* error! */
  513. X    case 'n' : return(NOV);
  514. X    case 'o' : return(OCT);
  515. X    case 's' : return(SEP);
  516. X    default  : return(-1);
  517. X    }
  518. X}
  519. END_OF_FILE
  520. if test 14562 -ne `wc -c <'mush/dates.c'`; then
  521.     echo shar: \"'mush/dates.c'\" unpacked with wrong size!
  522. fi
  523. # end of 'mush/dates.c'
  524. fi
  525. if test -f 'mush/file.c' -a "${1}" != "-c" ; then 
  526.   echo shar: Will not clobber existing file \"'mush/file.c'\"
  527. else
  528. echo shar: Extracting \"'mush/file.c'\" \(14337 characters\)
  529. sed "s/^X//" >'mush/file.c' <<'END_OF_FILE'
  530. X/* file.c -- Copyright (1988) Dan Heller */
  531. X
  532. X#include "mush.h"
  533. X#include <pwd.h>
  534. X
  535. X/* takes string 'p' and address of int (isdir).  If p uses the ~ to reference
  536. X * a home directory of some sort, then expand it.  find out what sort of
  537. X * file final path is. set isdir to 1 if a directory, 0 if not, -1 on error
  538. X * return final path. If an error occurs, return string indicating error.
  539. X * if isdir has a value of 1 when passed, it ignores "No such file or directory"
  540. X */
  541. Xchar *
  542. Xgetpath(p, isdir)
  543. Xregister char *p;
  544. Xint *isdir;
  545. X{
  546. X    static char buf[MAXPATHLEN];
  547. X    struct stat stat_buf;
  548. X
  549. X    if (!p || !*p || !strcmp(p, "~")) {
  550. X    char *home = do_set(set_options, "home");
  551. X    if (!home || !*home)
  552. X        home = ALTERNATE_HOME;
  553. X    (void) strcpy(buf, home);  /* no arg means home */
  554. X    } else if (*p == '~') {
  555. X    if (p[1] != '/') {
  556. X        /* not our home, but someone else's
  557. X         * look for ~user or ~user/subpath
  558. X         * if '/' exists, separate into tmp="user" p="subpath"
  559. X         */
  560. X        struct passwd *ent, *getpwnam();
  561. X        char *p2 = p+1;
  562. X        if (p = index(p2, '/'))
  563. X        *p++ = 0;
  564. X        if (!(ent = getpwnam(p2))) {
  565. X        *isdir = -1;
  566. X        return sprintf(buf, "no such user: %s", p2);
  567. X        }
  568. X        /* append subpath to pathname */
  569. X        if (p && *p)
  570. X        (void) sprintf(buf, "%s/%s", ent->pw_dir, p);
  571. X        /* if *p == NULL, pathname is done (buf), set isdir = 1 */
  572. X        else {
  573. X        *isdir = 1;
  574. X        return strcpy(buf, ent->pw_dir);
  575. X        }
  576. X    } else {
  577. X        char *home = do_set(set_options, "home");
  578. X        if (!home || !*home)
  579. X        home = ALTERNATE_HOME;
  580. X        (void) sprintf(buf, "%s/%s", home, p+2);
  581. X    }
  582. X    } else if (*p == '%') {
  583. X    /* if %user, append user name... else, it's just us */
  584. X    if (!*++p || *p == ' ' || *p == '\t')
  585. X        (void) strcpy(buf, spoolfile);
  586. X    else
  587. X#ifndef HOMEMAIL
  588. X        (void) sprintf(buf, "%s/%s", MAILDIR, p);
  589. X#else /* HOMEMAIL */
  590. X    {
  591. X        /* If it's NOT us, recur to get the path for ~user/MAILFILE */
  592. X        int t_isdir = *isdir;
  593. X        char *t, tmp[MAXPATHLEN];
  594. X        (void) sprintf(tmp, "~%s/%s", p, MAILFILE);
  595. X        t = getpath(tmp, &t_isdir);
  596. X        if (t_isdir == -1) {
  597. X        *isdir = -1;
  598. X        return t;
  599. X        }
  600. X        /* strcpy(buf, t); --buf already has info because it's static */
  601. X    }
  602. X#endif /* HOMEMAIL */
  603. X    } else if (*p == '+') {
  604. X    register char *p2 = do_set(set_options, "folder");
  605. X    if (!p2 || !*p2)
  606. X        p2 = DEF_FOLDER;
  607. X    if (*++p)
  608. X        (void) sprintf(buf, "%s/%s", p2, p);
  609. X    else
  610. X        (void) strcpy(buf, p2);
  611. X    if (*buf != '/') {
  612. X        int t_isdir = *isdir;
  613. X        char *t, tmp[MAXPATHLEN];
  614. X        if (*buf != '~')
  615. X        (void) sprintf(tmp, "~/%s", buf);
  616. X        else
  617. X        (void) strcpy(tmp, buf);
  618. X        t = getpath(tmp, &t_isdir);
  619. X        if (t_isdir == -1) {
  620. X        *isdir = -1;
  621. X        return t;
  622. X        }
  623. X        /* strcpy(buf, t); --buf already has info because it's static */
  624. X    }
  625. X    } else {  /* allow \ to escape the special chars, +, %, ~ */
  626. X    if (*p == '\\')
  627. X        p++;
  628. X    (void) strcpy(buf, p);
  629. X    }
  630. X    if (stat(buf, &stat_buf)) {
  631. X    (void) access(buf, F_OK); /* set errno to the "real" reason */
  632. X    if (errno == ENOENT && *isdir == 1) {
  633. X        *isdir = 0; /* say it's a regular file even tho it doesn't exist */
  634. X        return buf; /* it may be wanted for creating */
  635. X    }
  636. X    *isdir = -1;
  637. X    return sys_errlist[errno];
  638. X    }
  639. X    *isdir = ((stat_buf.st_mode & S_IFMT) == S_IFDIR);
  640. X    return buf;
  641. X}
  642. X
  643. X/*
  644. X * Given a (possibly NULL or empty) string, return the name of a a valid
  645. X * directory.  The string may contain the usual filename metachars (see
  646. X * above).  Returns the current user's home directory if the input string
  647. X * does not refer to a directory, the ALTERNATE_HOME if the user's home
  648. X * directory cannot be found, or NULL if none of the above are accessible.
  649. X *
  650. X * NOTE:  Returns the getpath() static buffer, so the same caveats apply.
  651. X */
  652. Xchar *
  653. Xgetdir(path)
  654. Xchar *path;
  655. X{
  656. X    int isdir = 0;
  657. X
  658. X    /* getpath() already handles the NULL and empty cases */
  659. X    if (!(path = getpath(path, &isdir)) || isdir != 1) {
  660. X    isdir = 0;
  661. X    path = getpath(ALTERNATE_HOME, &isdir);
  662. X    if (isdir != 1)
  663. X        path = NULL;
  664. X    }
  665. X    return path;
  666. X}
  667. X
  668. X/*
  669. X * Given a filename[pointer] (p), a file pointer, and a mode, file_to_fp
  670. X * opens the file with the mode.
  671. X * If the mode is "r" then we read the file into the file pointer at the
  672. X * end (fseek(fp, 2, 0)).  If the file is opened for writing, then read
  673. X * from the beginning of fp and write it into the file.
  674. X * This is usually called to read .signatures into messages (thus,
  675. X * opening .signature with "r" and writing to the end of fp which is probably
  676. X * the sendmail process or the message file pointer) or to write fortunes into
  677. X * the message buffer: reading fp (the popened fortune) and writing into file.
  678. X */
  679. Xfile_to_fp(p, fp, mode)
  680. Xregister char *p;
  681. Xregister FILE *fp;
  682. Xchar *mode;
  683. X{
  684. X    int     x = 1;
  685. X    char     *file, buf[BUFSIZ];
  686. X    FILE     *tmp_fp;
  687. X
  688. X    if (!p || !*p) {
  689. X    print("specify filename");
  690. X    return -1;
  691. X    }
  692. X    /* Special case for IS_SENDING && !IS_GETTING should eventually go away */
  693. X    if (ison(glob_flags, IS_SENDING) && isoff(glob_flags, IS_GETTING) &&
  694. X        strcmp(p, "-") == 0) {
  695. X    file = p;
  696. X    if (*mode == 'r')
  697. X        tmp_fp = stdin;
  698. X    else
  699. X        tmp_fp = stdout;
  700. X    } else {
  701. X    file = getpath(p, &x);
  702. X    if (x == -1) { /* on error, file contains error message */
  703. X        wprint(file);
  704. X        return -1;
  705. X    }
  706. X    wprint("%s: ", file);
  707. X    if (x) {
  708. X        /* if x == 1, then path is a directory */
  709. X        wprint("is a directory.\n");
  710. X        return -1;
  711. X    } else if (!(tmp_fp = fopen(file, mode))) {
  712. X        wprint("%s\n", sys_errlist[errno]);
  713. X        return -1;
  714. X    }
  715. X    }
  716. X    if (*mode != 'r') {
  717. X    rewind(fp);
  718. X    for(x = 0; fgets(buf, BUFSIZ, fp); x++)
  719. X        (void) fputs(buf, tmp_fp);
  720. X    } else {
  721. X    for(x = 0; fgets(buf, BUFSIZ, tmp_fp); x++)
  722. X        (void) fputs(buf, fp);
  723. X    (void) fflush(fp);
  724. X    }
  725. X    wprint("%s%d line%s\n", (*mode == 'a')? "added ": "",
  726. X                  x, (x == 1)? "": "s");
  727. X    if (file != p || strcmp(file, "-") != 0)
  728. X    (void) fclose(tmp_fp);
  729. X    return 0;
  730. X}
  731. X
  732. X/* clear all contents of the file.  Careful that the file is opened for
  733. X * _writing_ --tempfile is opened for reading, so don't try to empty it
  734. X * if you're using ftruncate.   Return -1 on error, 0 on success.
  735. X */
  736. Xemptyfile(fp, fname)
  737. Xregister FILE **fp;
  738. Xregister char *fname;
  739. X{
  740. X    Debug("Emptying \"%s\"\n", fname);
  741. X#ifndef SYSV
  742. X    return ftruncate(fileno(*fp), 0L);
  743. X#else
  744. X    {
  745. X    int omask = umask(077), ret;
  746. X    (void) fclose(*fp);
  747. X    if (!(*fp = fopen(fname, "w")))
  748. X        ret = -1;
  749. X    ret = 0;
  750. X    (void) umask(omask);
  751. X    return ret;
  752. X    }
  753. X#endif /* SYSV */
  754. X}
  755. X
  756. X/*
  757. X * Finds out how many file descriptors are opened.  Useful for making sure
  758. X * no files got opened in subprocedures which were not subsequently closed.
  759. X * If argc is 0, returns the number of available fds.
  760. X */
  761. Xnopenfiles(argc)
  762. X{
  763. X#ifdef MAXFILES
  764. X    register int size = MAXFILES;
  765. X#else
  766. X    register int size = getdtablesize();
  767. X#endif /* MAXFILES */
  768. X    register int nfiles = 0, totalfiles = size;
  769. X
  770. X    if (argc > 1)
  771. X    return -1;
  772. X
  773. X    if (argc == 1)
  774. X    wprint("open file descriptors:");
  775. X    while (--size >= 0)
  776. X    if (fcntl(size, F_GETFL, 0) != -1) {
  777. X        if (argc == 1)
  778. X        wprint(" %d", size);
  779. X        ++nfiles;
  780. X    }
  781. X    if (argc == 1) {
  782. X    wprint("\n");
  783. X    return 0;
  784. X    }
  785. X    return totalfiles - nfiles;
  786. X}
  787. X
  788. X/*
  789. X * Close all "extraneous" file descriptors; return the number closed
  790. X */
  791. Xclosefileds (n)
  792. X{
  793. X    register int nfiles = 0;
  794. X#ifdef MAXFILES
  795. X    register int size = MAXFILES;
  796. X#else
  797. X    register int size = getdtablesize();
  798. X#endif /* MAXFILES */
  799. X
  800. X    while (--size >= n)
  801. X    if (fcntl(size, F_GETFL, 0) != -1) {
  802. X        (void) close(size);
  803. X        ++nfiles;
  804. X    }
  805. X    return nfiles;
  806. X}
  807. X
  808. X/*
  809. X * Open a path for writing or appending -- return a FILE pointer.
  810. X * If program is TRUE, then use popen, not fopen and don't check 
  811. X * to see if the file is writable.  If program is FALSE and lockit
  812. X * is TRUE, then lock on open.
  813. X */
  814. XFILE *
  815. Xopen_file(p, program, lockit)
  816. Xregister char *p;
  817. X{
  818. X    register FILE *newfile = NULL_FILE;
  819. X    register char *tmp;
  820. X    int x = 1;
  821. X
  822. X    if (program || *p == '/')
  823. X    tmp = p, x = 0;
  824. X    else
  825. X    tmp = getpath(p, &x);
  826. X    if (x == 1)
  827. X    print("%s is a directory.\n", tmp);
  828. X    else if (x == -1)
  829. X    print("%s: %s\n", p, tmp);
  830. X    else {
  831. X    register char *mode = NULL;
  832. X    /* if it doesn't exist open for "w" */
  833. X    if (program || Access(tmp, F_OK))
  834. X        mode = "w";
  835. X    /* if we can't write to it, forget it */
  836. X    else if (Access(tmp, W_OK))
  837. X        error(tmp);
  838. X    else
  839. X        mode = "a";
  840. X    if (mode)
  841. X        if (program) {
  842. X        if (!(newfile = popen(tmp, mode)))
  843. X            error("Can't execute %s\n", tmp);
  844. X        } else if (lockit) {
  845. X        /* Lock on open */
  846. X        if (!(newfile = lock_fopen(tmp, mode)))
  847. X            error("Can't write to %s", tmp);
  848. X        } else {
  849. X        /* Ordinary open */
  850. X        if (!(newfile = mask_fopen(tmp, mode)))
  851. X            error("Can't write to %s", tmp);
  852. X        }
  853. X        if (newfile != NULL_FILE)
  854. X        Debug("Successfully opened %s\n", tmp);
  855. X    }
  856. X    return newfile;
  857. X}
  858. X
  859. X/*
  860. X * Open each file in the vector names[] and place the corresponding
  861. X * file descriptor in files[].  If the file is really a program (pipe),
  862. X * delete the name after opening; otherwise lock the file.
  863. X * Tokens beginning with a "/, ~, or + are files; tokens beginning
  864. X * with a | are programs.
  865. X */
  866. Xopen_list(names, files, size)
  867. Xchar *names[];
  868. XFILE *files[];
  869. X{
  870. X    register int total = 0, prog;
  871. X    register char *fpath;
  872. X
  873. X    Debug("opening "), print_argv(names);
  874. X    for (total = 0; size && total < size; ) {
  875. X    fpath = names[total] + (prog = (names[total][0] == '|'));
  876. X    /* open_file() locks the file here only if prog is false */
  877. X    if ((files[total] = open_file(fpath, prog, TRUE))) {
  878. X        if (prog) {
  879. X        xfree(names[total]);
  880. X        names[total++] = NULL;
  881. X        } else {
  882. X        /* Seek to end of file AFTER locking */
  883. X        (void) fseek(files[total++], 0L, 2);
  884. X        }
  885. X    } else {
  886. X        Debug("Failed to open %s\n", names[total]);
  887. X        /* Swap the failed file with the last in the list */
  888. X        if (size--) {
  889. X        xfree(names[total]);
  890. X        names[total] = names[size];
  891. X        names[size] = NULL;
  892. X        }
  893. X    }
  894. X    }
  895. X    return size;
  896. X}
  897. X
  898. X/*
  899. X * find_files gets a set of addresses and an array of
  900. X * char pointers and the maximum size that array can be.
  901. X * The object is to find the files or programs listed in "s".  If the
  902. X * size is 0, then just extract the file names and give error messages
  903. X * for each one since they will not be opened. Return the number of
  904. X * files found and delete all files from the list in * "s".
  905. X * The string "s" is modified to be a list of address -- all names AND
  906. X * files are stripped out of the list.
  907. X * The force parameter causes names to be interpreted as files even if
  908. X * they would normally appear to be addresses.
  909. X */
  910. Xfind_files(s, names, size, force)
  911. Xregister char *s;
  912. Xchar *names[];
  913. X{
  914. X    register int     total = 0;
  915. X    char          file[MAXPATHLEN], buf[HDRSIZ], *start = s, c;
  916. X    register char    *p, *b = buf, *fpath;
  917. X
  918. X    do  {
  919. X    if (!(p = get_name_n_addr(s, NULL, file)))
  920. X        break;
  921. X    c = *p, *p = 0;
  922. X    /* See if it's a file.  This doesn't get written back
  923. X     * onto "buf" since it is supposed to be extracted anyway.
  924. X     */
  925. X    if (force || *file == '+' || *file == '~' ||
  926. X        *file == '|' || *file == '/') {
  927. X        int isdir;
  928. X        /* open either "file" or &file[1] */
  929. X        if (*file == '|') {
  930. X        isdir = 0;
  931. X        fpath = file;
  932. X        } else {
  933. X        isdir = 1;
  934. X        /* if successful, getpath will reset isdir to 0 */
  935. X        fpath = getpath(file, &isdir);
  936. X        }
  937. X        if (!isdir) {
  938. X        if (size && total < size)
  939. X            names[total++] = savestr(fpath);
  940. X        else
  941. X            print("No open space for %s\n", file);
  942. X        } else if (isdir == 1)
  943. X        print("%s: is a directory\n", file);
  944. X        else
  945. X        print("%s: %s\n", file, fpath);
  946. X    } else {
  947. X        b += Strcpy(b, s);
  948. X        *b++ = ',', *b++ = ' ';
  949. X    }
  950. X    for (*p = c, s = p; *s == ',' || isspace(*s); s++)
  951. X        ;
  952. X    } while (*s);
  953. X    for (*b-- = 0; b > buf && (*b == ',' || isspace(*b)); b--)
  954. X    *b = 0;
  955. X    (void) strcpy(start, buf);
  956. X    names[total] = NULL; /* for free_vec() */
  957. X    return total;
  958. X}
  959. X
  960. X/*
  961. X * access(2) has an undocumented feature which ignores suid.  If you are
  962. X * su'ed and try to read your mail, you will be unable to because access()
  963. X * will give the illusion that you cannot read/write to your mbox.  Solve
  964. X * the problem by using stat() instead.
  965. X */
  966. XAccess(file, mode)
  967. Xregister char *file;
  968. X{
  969. X    struct stat buf;
  970. X
  971. X    if (stat(file, &buf) == -1)
  972. X    return -1;
  973. X    if (mode == R_OK)
  974. X    return (buf.st_mode & 0400)? 0 : -1;
  975. X    if (mode == W_OK)
  976. X    return (buf.st_mode & 0200)? 0 : -1;
  977. X    return 0;
  978. X}
  979. X
  980. X/*
  981. X * Open a file for read/write/whatever but make sure umask is rw by user only.
  982. X */
  983. XFILE *
  984. Xmask_fopen(file, mode)
  985. Xchar *file, *mode;
  986. X{
  987. X    int omask = umask(077);
  988. X    FILE *fp = fopen(file, mode);
  989. X    (void) umask(omask);
  990. X    return fp;
  991. X}
  992. X
  993. X/*
  994. X * Shorten a file name, replacing its full path name with one using an
  995. X *  accepted mush abbreviation:
  996. X *    ~    home directory
  997. X *    +    folder directory
  998. X *  For files in the current directory, the path is simply skipped.
  999. X * Returns a pointer into a static buffer holding the trimmed path.
  1000. X */
  1001. Xchar *
  1002. Xtrim_filename(name)
  1003. Xchar *name;
  1004. X{
  1005. X    static char buf[MAXPATHLEN];
  1006. X    char *fldr = do_set(set_options, "folder"),
  1007. X     *home = do_set(set_options, "home");
  1008. X    int len;
  1009. X
  1010. X    /* Handling $folder is tough, because if it is not set then we should
  1011. X     * trim DEF_FOLDER; but DEF_FOLDER may not be a full path, and we can't
  1012. X     * call getpath() because the "name" parameter may point to gepath()'s
  1013. X     * static buffer.  So we handle the special case of DEF_FOLDER starting
  1014. X     * with a tilde ($home), and forget about it otherwise.  Yuck.
  1015. X     */
  1016. X    if ((!fldr || !*fldr) && (fldr = DEF_FOLDER) && *fldr == '~' && home) {
  1017. X    (void) sprintf(buf, "%s%s", home, fldr + 1);
  1018. X    fldr = buf;  /* buf will get overwritten again below */
  1019. X    }
  1020. X    /* One more special case: if $folder and $home are the same, then we
  1021. X     * trim as $home, otherwise we trim as $folder.  This prevents strange
  1022. X     * contractions like "+.cshrc" for "~/.cshrc".
  1023. X     */
  1024. X    if ((!home || strcmp(home, fldr)) && (len = strlen(fldr)) &&
  1025. X        !strncmp(fldr, name, len) && (name[len] == '/' || !name[len])) {
  1026. X    buf[0] = '+';
  1027. X    if (name[len] && name[len + 1])
  1028. X        (void) strcpy(buf + 1, name + len + 1);
  1029. X    else
  1030. X        buf[1] = 0;
  1031. X    return buf;
  1032. X    } else if (home && (len = strlen(home)) && !strncmp(home, name, len) &&
  1033. X        (name[len] == '/' || !name[len])) {
  1034. X    buf[0] = '~';
  1035. X    (void) strcpy(buf + 1, name + len);
  1036. X    return buf;
  1037. X    } else if ((fldr = do_set(set_options, "cwd")) &&
  1038. X        (len = strlen(fldr)) && !strncmp(fldr, name, len) &&
  1039. X        name[len] == '/')
  1040. X    return strcpy(buf, name + len + 1);
  1041. X    return strcpy(buf, name);
  1042. X}
  1043. END_OF_FILE
  1044. if test 14337 -ne `wc -c <'mush/file.c'`; then
  1045.     echo shar: \"'mush/file.c'\" unpacked with wrong size!
  1046. fi
  1047. # end of 'mush/file.c'
  1048. fi
  1049. if test -f 'mush/sample.mushrc' -a "${1}" != "-c" ; then 
  1050.   echo shar: Will not clobber existing file \"'mush/sample.mushrc'\"
  1051. else
  1052. echo shar: Extracting \"'mush/sample.mushrc'\" \(6541 characters\)
  1053. sed "s/^X//" >'mush/sample.mushrc' <<'END_OF_FILE'
  1054. X# sample.mushrc
  1055. X# By Bart Schaefer and Dan Heller
  1056. X#
  1057. X# Change mush's temp file directory, to avoid quota collisions.
  1058. X# /usr/tmp so tmpfiles won't be rm'd before they can be recovered.
  1059. Xset tmpdir=/usr/tmp
  1060. X
  1061. X# Set the folder and mbox locations; the + expands to value of "folder".
  1062. Xset folder=$HOME/Mail mbox=+mbox
  1063. X
  1064. X# Set up the display early to allow quick exit in headers-only mode.
  1065. X# The hdrs_only flag is true if the command line was: "mush -H".
  1066. X# The variable hdr_format is set to change the format of the header
  1067. X# summaries that are displayed.
  1068. Xif hdrs_only
  1069. X    set hdr_format='%28a %M %-2N  %.33s'
  1070. X    exit    # Quits reading this file
  1071. Xelse
  1072. X    set hdr_format='%28a %M %-2N (%3.5l li) %.25s'
  1073. Xendif
  1074. X
  1075. X# Set the prompt to show current time, name of the current folder,
  1076. X# current message number, and count of total messages.
  1077. Xset prompt="(%T) %f: #%m of %t> "
  1078. X
  1079. X# Hitting <CR> should do nothing (helps make mush more shell-like).  If
  1080. X# newline is not set, hitting <CR> prints the next message (like Mail).
  1081. X# This variable could be set to any mush command.
  1082. Xset newline
  1083. X
  1084. X# These variables are helpful for new users:
  1085. X#    ask        -- always prompt for Subject: of mail
  1086. X#    ignoreeof    -- ignore end-of-file from keyboard
  1087. X#    verify        -- query that all is well before sending mail
  1088. X#    warning        -- report miscellaneous possible problems
  1089. Xset ask verify warning
  1090. Xset ignoreeof="echo 'Use "'"'quit'"'" to quit.'"
  1091. X
  1092. X# When reading messages, don't bother looking at lengthy, boring headers.
  1093. Xignore message-id received via status
  1094. X
  1095. X# Since mush has csh-like history, you might find it annoying to type
  1096. X# things like "mail host\!host1\!host2\!user" from within the mush shell.
  1097. X# Setting nonobang will prevent the "unknown event" and allow the !'s to
  1098. X# be typed without having to be preceded by backslashes.
  1099. Xset nonobang
  1100. X
  1101. X# By default, mush's history is set to the last command only.  Set it to
  1102. X# remember the last 100 commands.
  1103. Xset history = 100
  1104. X
  1105. X# If the variable "unix" is set, then any command that isn't a mush command
  1106. X# will execute the command as if you typed it from the shell.  Note, such
  1107. X# commands will not go through another shell -- this is it.
  1108. Xset unix
  1109. X
  1110. X# Mush tries to read ~/.mushrc first, then it tries ~/.mailrc.  Assuming
  1111. X# you use *this* file as your .mushrc, source the contents of .mailrc as
  1112. X# well in case there are Mail aliases that are set there.
  1113. Xsource $HOME/.mailrc
  1114. X
  1115. X# Use a real pager.
  1116. Xset pager=less
  1117. X
  1118. X# When typing in a letter, it is sometimes convenient to have lines wrap
  1119. X# automatically similar to editors like vi and emacs.  In this example, if
  1120. X# the user types past column 74, a newline will automatically be inserted.
  1121. Xset wrapcolumn=74
  1122. X
  1123. X# If "autosign" is set, then a file can be read in automatically whenever
  1124. X# mail is sent.  This file is normally your "signature," that is, your
  1125. X# name and other information you want included in every message.
  1126. Xset autosign = ~/.signature
  1127. X
  1128. X# When you use the -i option to reply, or use the ~i tilde escape in a letter
  1129. X# when in compose mode, the current message will be included in your text.
  1130. X# Put a nice wrapper around those included messages.  Here, show the author's
  1131. X# name and the subject of his letter, label the end, and add a trailing blank
  1132. X# to separate each inclusion and make finding the end easier.
  1133. Xset pre_indent_str='On %M %N, %T, %.50n wrote:\n} Subject: %.65s'
  1134. Xset indent_str='} '    # actual message text is preceded by a "}"
  1135. Xset post_indent_str='}-- End of excerpt from %.50n\n'
  1136. X
  1137. X# Label replies with a header showing the who, what, and when of the
  1138. X# message being replied-to.
  1139. Xset in_reply_to='%f\n\t"%s" (%d)'
  1140. X
  1141. X# Mail routing and address-fixing conveniences.  If auto_route is set, then
  1142. X# replies to messages take a closer look at the addresses of the recipients.
  1143. X# If any redundant paths are present, they are pruned.  Also, the path that
  1144. X# precedes any hosts listed in the "known_hosts" list is truncated.  This is
  1145. X# useful for uucp sites only, and is therefore commented out in this sample.
  1146. X# set auto_route known_hosts="sun ucbcad well unicom"
  1147. X
  1148. X# The "alts" command specifies alternate addresses that I have.  Here,
  1149. X# "*" expands to any "path" whose recipient ends with the user's current
  1150. X# login name.  If another login name is desired, the login and/or path
  1151. X# to that login must be preceded by a !.  Otherwise, standard paths are used.
  1152. Xalts "*"
  1153. X
  1154. X# The "map" command can rebind certain key sequences in tty-mode only.
  1155. X# Here, if the user types two R's in a row at the prompt, then the string
  1156. X# "reply -ei " will be echoed as if the user typed it.
  1157. Xmap RR "reply -ei "
  1158. X# "rr" will do a reply and do the newline for you so you don't have to.
  1159. Xmap rr "reply\n"
  1160. X
  1161. X# The "map!" command is similar to "map" in that you can do keyboard
  1162. X# acceleration, but map! occurs during letter composition mode only.
  1163. Xmap! '\CT' '    '    # ^T generates 4 spaces in composition mode.
  1164. X# Here, hitting * twice will append a pre-signature.
  1165. Xmap! ** "\n            Later,\n"
  1166. X
  1167. X# Be careful with map and map! -- you can cause an infinite loop.
  1168. X# Your interrupt key (usually ^C) will stop such loops.
  1169. X
  1170. X# The curses mode allows the screen to be set up like a full screen editor.
  1171. X# There are basic "curses commands" which are bound to keyboard key-sequences
  1172. X# (usually one character).  The user can rebind these keys to suit his tastes.
  1173. X# Note that the binding for R below removes the binding of reply-all.
  1174. X#
  1175. Xset curses_help        # Unset this to remove help message in curses.
  1176. Xbind \n display        # Hit return to display the next message.
  1177. Xbind t top        # Make it easier to see the top few lines.
  1178. Xbind e macro "[line-mode]edit\n"    # Quick edit from curses.
  1179. Xbind P macro "[line-mode]Print\n"    # Show me all the headers.
  1180. Xbind R macro "[line-mode]reply -ei "    # Reply with inclusion and edit.
  1181. Xbind A macro "R[getline]~t\n\CUargv\n"    # R to Dan w/auto address fix.
  1182. X
  1183. X# "cmd" is used to set command line aliases similar to the way "csh"
  1184. X# does it.  The only difference is that "alias" is a reserved word in
  1185. X# Mush and Mail, so cmd is used.
  1186. X#
  1187. Xcmd dq 'd \!*; q'        # Delete a message list, then quit.
  1188. Xcmd unread 'flags \!* U O'    # Mark messages unread.
  1189. Xcmd : curses            # Colon now "toggles" curses mode.
  1190. X
  1191. X# Find messages from mailer-daemon (ignore upper/lower case).
  1192. Xcmd md 'pick -i -f mailer-daemon'
  1193. X# Because mush can pipe commands to one another, including "cmd"'s, this
  1194. X# example will delete all messages from mailer-daemon
  1195. Xcmd dmd 'md | delete'
  1196. X
  1197. X# aliases -- just like Mail's, but you can specify "names"
  1198. Xalias argv Dan Heller <argv@sun.com>
  1199. Xalias bart Bart Schaefer <schaefer@ogicse.ogi.edu>
  1200. Xalias mush-users Mush Users <mush-users-request@garp.mit.edu>
  1201. END_OF_FILE
  1202. if test 6541 -ne `wc -c <'mush/sample.mushrc'`; then
  1203.     echo shar: \"'mush/sample.mushrc'\" unpacked with wrong size!
  1204. fi
  1205. # end of 'mush/sample.mushrc'
  1206. fi
  1207. if test -f 'mush/tool.c' -a "${1}" != "-c" ; then 
  1208.   echo shar: Will not clobber existing file \"'mush/tool.c'\"
  1209. else
  1210. echo shar: Extracting \"'mush/tool.c'\" \(15050 characters\)
  1211. sed "s/^X//" >'mush/tool.c' <<'END_OF_FILE'
  1212. X/* @(#)tool.c    (c) copyright    10/15/86 (Dan Heller) */
  1213. X
  1214. X/* tool.c --make the mailtool windows, panels, etc... */
  1215. X#include "mush.h"
  1216. X
  1217. X#ifndef FONTDIR
  1218. X#define FONTDIR          "/usr/lib/fonts/fixedwidthfonts/"
  1219. X#endif /* FONTDIR */
  1220. X
  1221. Xextern void print_sigwinch(), make_hdr_sw();
  1222. Xextern Notify_value fkey_interposer();
  1223. X
  1224. Xstatic void init_cursors(), geticon();
  1225. X
  1226. Xextern Panel /* panels.c */
  1227. X    make_hdr_panel(), make_main_panel();
  1228. X
  1229. XPanel
  1230. X    main_panel,        /* the main panel dealing with generic items */
  1231. X    hdr_panel;        /* panel which contains message header specific items */
  1232. X
  1233. XTextsw cprint_sw, mfprint_sw, wprint_sw;
  1234. XFrame compose_frame;
  1235. X
  1236. Xstatic char **choice_args, **button_args;
  1237. X
  1238. Xshort dat_mail_icon_1[] = {
  1239. X#include "mail.icon.1"
  1240. X};
  1241. X
  1242. Xshort dat_mail_icon_2[] = {
  1243. X#include "mail.icon.2"
  1244. X};
  1245. X
  1246. Xshort dat_coffee_cup[] = {
  1247. X    0x0200,0x0100,0x0600,0x0800,0x0600,0x0100,0xFFF8,0x800C,
  1248. X    0x800A,0x4012,0x401C,0x2020,0x9048,0x7FF0,0x3FE0,0x0000
  1249. X};
  1250. X
  1251. Xmpr_static(mail_icon_image1, 64, 64, 1, dat_mail_icon_1);
  1252. Xmpr_static(mail_icon_image2, 64, 64, 1, dat_mail_icon_2);
  1253. X
  1254. Xmpr_static(coffee_cup,      16, 16, 1, dat_coffee_cup);
  1255. X
  1256. X/* public for hdr_sw.c */
  1257. XCursor l_cursor, m_cursor, r_cursor;
  1258. Xstatic Cursor coffee;
  1259. X
  1260. X/* text and font will be set in mail_status() */
  1261. XIcon mail_icon;
  1262. X
  1263. Xstatic Notify_value scroll_hdr();
  1264. X
  1265. Xmake_tool()
  1266. X{
  1267. X    Rect    mrect;       /* Position and size of icon label. */
  1268. X    struct stat rootbuf, tmpbuf;
  1269. X    char    *p;
  1270. X    struct pixfont *pf_open();
  1271. X
  1272. X    if (p = do_set(set_options, "font")) {
  1273. X    char buf[MAXPATHLEN];
  1274. X    (void) sprintf(buf, "%s%s", FONTDIR, p);
  1275. X    if (!(mush_font = pf_open(buf)))
  1276. X        print("couldn't open font \"%s\"\nUsing default font.\n", buf);
  1277. X    }
  1278. X    if (!mush_font)
  1279. X    mush_font = pf_default();
  1280. X
  1281. X    geticon();
  1282. X    check_icons();
  1283. X
  1284. X    if (p = do_set(set_options, "screen_win"))
  1285. X    screen = atoi(p);
  1286. X    else
  1287. X    screen = 6;
  1288. X
  1289. X    /* where to place text on mail icon -- how many messages there are */
  1290. X    mrect.r_left = l_width();
  1291. X    mrect.r_top = 58-l_height();
  1292. X    mrect.r_width = 3*l_width();
  1293. X    mrect.r_height = l_height();
  1294. X    (void) icon_set(mail_icon, ICON_LABEL_RECT, &mrect, NULL);
  1295. X
  1296. X    (void) window_set(tool,
  1297. X    FRAME_ICON,    mail_icon,
  1298. X    WIN_CONSUME_KBD_EVENTS,
  1299. X        WIN_LEFT_KEYS, WIN_TOP_KEYS, WIN_RIGHT_KEYS, NULL,
  1300. X    NULL);
  1301. X    (void) notify_interpose_destroy_func(tool, destroy_proc);
  1302. X
  1303. X    choice_args = panel_make_list(
  1304. X    PANEL_MENU_TITLE_FONT, mush_font,
  1305. X    PANEL_DISPLAY_LEVEL,    PANEL_NONE,
  1306. X    PANEL_SHOW_MENU,    TRUE,
  1307. X    PANEL_SHOW_MENU_MARK,    FALSE,
  1308. X    NULL);
  1309. X
  1310. X    button_args = panel_make_list(
  1311. X    PANEL_FEEDBACK,        PANEL_INVERTED,
  1312. X    PANEL_SHOW_MENU,    FALSE,
  1313. X    NULL);
  1314. X
  1315. X    hdr_panel = make_hdr_panel(tool, choice_args, button_args);
  1316. X
  1317. X    make_hdr_sw(tool);
  1318. X
  1319. X    main_panel = make_main_panel(tool, choice_args, button_args);
  1320. X
  1321. X    /* main mush frame text subwindow for wprint() */
  1322. X    mfprint_sw = window_create(tool, TEXTSW,
  1323. X    WIN_BELOW,                      main_panel,
  1324. X    WIN_HEIGHT,                     l_height() * 4,
  1325. X    TEXTSW_READ_ONLY,               TRUE,
  1326. X    TEXTSW_BLINK_CARET,             FALSE,
  1327. X    TEXTSW_LINE_BREAK_ACTION,       TEXTSW_WRAP_AT_CHAR,
  1328. X    NULL);
  1329. X    /* order is important -- scroll_textwin should be called first! */
  1330. X    (void) notify_interpose_event_func(mfprint_sw,
  1331. X    fkey_interposer, NOTIFY_SAFE);
  1332. X    (void) notify_interpose_event_func(mfprint_sw,
  1333. X    scroll_textwin, NOTIFY_SAFE);
  1334. X
  1335. X    /* text subwindow for paging messages */
  1336. X    p = do_set(set_options, "crt_win");
  1337. X    pager_textsw = window_create(tool, TEXTSW,
  1338. X    WIN_HEIGHT,            l_height() * (p? atoi(p) : 12),
  1339. X    TEXTSW_READ_ONLY,        TRUE,
  1340. X    TEXTSW_BLINK_CARET,        FALSE,
  1341. X#ifdef SUN_4_0 /* SunOS 4.0+ */
  1342. X    TEXTSW_LINE_BREAK_ACTION,    TEXTSW_WRAP_AT_WORD,
  1343. X#else /* SUN_4_0 */
  1344. X    TEXTSW_LINE_BREAK_ACTION,    TEXTSW_WRAP_AT_CHAR,
  1345. X#endif /* SUN_4_0 */
  1346. X    NULL);
  1347. X    /* order is important -- scroll_textwin should be called first! */
  1348. X    (void) notify_interpose_event_func(pager_textsw,
  1349. X    fkey_interposer, NOTIFY_SAFE);
  1350. X    (void) notify_interpose_event_func(pager_textsw,
  1351. X    scroll_textwin, NOTIFY_SAFE);
  1352. X
  1353. X    (void) sprintf(blank, "%128c", ' ');
  1354. X    mrect = *(Rect *)window_get(hdr_sw, WIN_RECT);
  1355. X    pw_writebackground(hdr_win, 0,0, mrect.r_width, mrect.r_height, PIX_CLR);
  1356. X    istool = 2;
  1357. X    (void) fclose(stdin);
  1358. X    (void) fclose(stdout);
  1359. X#ifndef SUN_3_5
  1360. X    /* SunOS 3.5 takes tty modes for ttysws from stderr. */
  1361. X    (void) fclose(stderr);
  1362. X#endif /* SUN_3_5 */
  1363. X    (void) do_version();
  1364. X    init_cursors();
  1365. X    timeout_cursors(TRUE);
  1366. X    window_fit_height(tool);
  1367. X}
  1368. X
  1369. Xvoid
  1370. Xclose_frame()
  1371. X{
  1372. X    window_set(compose_frame, WIN_SHOW, FALSE, NULL);
  1373. X}
  1374. X
  1375. Xvoid
  1376. Xmake_compose_frame()
  1377. X{
  1378. X    char *p;
  1379. X    Textsw msg_sw;
  1380. X    Panel panel, make_compose_panel();
  1381. X
  1382. X#ifdef SUN_3_5
  1383. X    if (nopenfiles(0) < 8) {
  1384. X    ok_box("Too many frames; close one, please.\n");
  1385. X    return;
  1386. X    }
  1387. X#endif /* SUN_3_5 */
  1388. X
  1389. X    compose_frame = window_create(tool, FRAME,
  1390. X    FRAME_LABEL,        "Compose Letter",
  1391. X    FRAME_SHOW_LABEL,    TRUE,
  1392. X    FRAME_NO_CONFIRM,    TRUE,
  1393. X    FRAME_DONE_PROC,    close_frame,
  1394. X    WIN_SHOW,        TRUE,
  1395. X    NULL);
  1396. X
  1397. X    panel = make_compose_panel(compose_frame, choice_args, button_args);
  1398. X
  1399. X#ifdef CPRINT_SW
  1400. X    /* text subwindow for wprint() */
  1401. X    cprint_sw = window_create(compose_frame, TEXTSW,
  1402. X    WIN_HEIGHT,            l_height() * 4,
  1403. X    WIN_BELOW,            panel,
  1404. X    TEXTSW_READ_ONLY,        TRUE,
  1405. X    TEXTSW_BLINK_CARET,        FALSE,
  1406. X    TEXTSW_LINE_BREAK_ACTION,    TEXTSW_WRAP_AT_CHAR,
  1407. X    NULL);
  1408. X    notify_interpose_event_func(cprint_sw, scroll_textwin, NOTIFY_SAFE);
  1409. X    (void) notify_interpose_event_func(cprint_sw,
  1410. X    fkey_interposer, NOTIFY_SAFE);
  1411. X#else /* CPRINT_SW */
  1412. X    /* not enough fd's in SunOS 3.X for the extra textsw */
  1413. X    cprint_sw = (Textsw) 0;
  1414. X#endif /* CPRINT_SW */
  1415. X
  1416. X    /* text subwindow for composing messages */
  1417. X    p = do_set(set_options, "msg_win");
  1418. X    msg_sw = window_create(compose_frame, TEXTSW,
  1419. X#ifdef CPRINT_SW
  1420. X    WIN_BELOW,            cprint_sw,
  1421. X#else /* CPRINT_SW */
  1422. X    WIN_BELOW,            panel,
  1423. X#endif /* CPRINT_SW */
  1424. X    WIN_HEIGHT,            l_height() * (p? atoi(p) : 24),
  1425. X    TEXTSW_READ_ONLY,        TRUE, /* set to false later */
  1426. X    TEXTSW_BLINK_CARET,        FALSE,
  1427. X    TEXTSW_LINE_BREAK_ACTION,    TEXTSW_WRAP_AT_CHAR,
  1428. X    TEXTSW_IGNORE_LIMIT,        TEXTSW_INFINITY,
  1429. X    NULL);
  1430. X    notify_interpose_event_func(msg_sw, fkey_interposer, NOTIFY_SAFE);
  1431. X    notify_interpose_event_func(msg_sw, edit_msg_textwin, NOTIFY_SAFE);
  1432. X
  1433. X    /* Assign textsw (msg_sw) to panel for panel items' callbacks */
  1434. X    panel_set(panel, PANEL_CLIENT_DATA, msg_sw, NULL);
  1435. X
  1436. X    /* tty subwindow */
  1437. X    if (!(tty_sw = window_create(compose_frame, TTY,
  1438. X    TTY_QUIT_ON_CHILD_DEATH, FALSE,
  1439. X    TTY_ARGV,        TTY_ARGV_DO_NOT_FORK,
  1440. X    WIN_CLIENT_DATA,    msg_sw,
  1441. X    WIN_SHOW,        FALSE,
  1442. X    WIN_WIDTH,        WIN_EXTEND_TO_EDGE,
  1443. X    WIN_BELOW,        msg_sw,
  1444. X    NULL)))
  1445. X    perror("tty_sw"), cleanup(0);
  1446. X
  1447. X    window_fit_height(compose_frame);
  1448. X}
  1449. X
  1450. X/*ARGSUSED*/
  1451. Xvoid
  1452. Xopen_compose()
  1453. X{
  1454. X    if (compose_frame)
  1455. X    window_set(compose_frame, WIN_SHOW, TRUE, NULL);
  1456. X    else
  1457. X    make_compose_frame();
  1458. X}
  1459. X
  1460. Xparse_tool_opts(argcp, argv)
  1461. Xint *argcp;
  1462. Xchar **argv;
  1463. X{
  1464. X    if (!(tool = window_create((Window) 0, FRAME,
  1465. X    FRAME_ARGC_PTR_ARGV, argcp, argv,
  1466. X    NULL)))
  1467. X    cleanup(0);
  1468. X}
  1469. X
  1470. X/*
  1471. X * used by both the hdr_sw (to scroll canvas) and by textsw's.
  1472. X * Return values:
  1473. X *  MUSH_SCROLL_TO (to scroll _to_ a particular location)
  1474. X *  MUSH_SCROLL_RELATIVE (scroll relative our current location)
  1475. X *  MUSH_SCROLL_IGNORE (not a scroll event and ignore it entirely (up events))
  1476. X *  MUSH_SCROLL_PASS_EVENT (not a scroll event; pass it to next event handler)
  1477. X * If absolute scrolling (scroll_to) then "amount" is set to 1 for beginning
  1478. X * of textsw or 2 for end of textsw.
  1479. X * User can precede a keyboard scrolling request by a "count" -- the
  1480. X * count is typed by the user in digits: 5j = move 5 lines down.
  1481. X * It is assumed that if the user moves the mouse after a digit is pressed,
  1482. X * then he doesn't really want to adjust the scrolling amount and the 
  1483. X * count is reset to the default (1).
  1484. X */
  1485. XScroll_action
  1486. Xdecode_scroll(client, event, len, amount)
  1487. XNotify_client client;
  1488. XEvent    *event;
  1489. Xint len, *amount;
  1490. X{
  1491. X    static int count;
  1492. X
  1493. X    if (event_id(event) == LOC_MOVE) {
  1494. X    count = 0;
  1495. X    return MUSH_SCROLL_PASS_EVENT;
  1496. X    }
  1497. X
  1498. X    *amount = 0;  /* Assume relative scroll */
  1499. X    if (ID == SCROLL_REQUEST)
  1500. X    return MUSH_SCROLL_PASS_EVENT;
  1501. X
  1502. X    if (event_is_up(event))
  1503. X    return MUSH_SCROLL_PASS_EVENT;
  1504. X    if (event_is_ascii(event) && isdigit(event_id(event))) {
  1505. X    count = (count * 10) + event_id(event) - '0';
  1506. X    return MUSH_SCROLL_IGNORE;
  1507. X    }
  1508. X#ifdef SUN_4_0 /* SunOS 4.0+ */
  1509. X    /* returns sunview events for some ctl chars */
  1510. X    switch (event_action(event)) {
  1511. X    case ACTION_GO_LINE_FORWARD:
  1512. X    case ACTION_GO_COLUMN_FORWARD:
  1513. X        *amount = count ? count : 1;
  1514. X        count = 0;
  1515. X        return MUSH_SCROLL_RELATIVE;
  1516. X    case ACTION_GO_LINE_BACKWARD:
  1517. X    case ACTION_GO_COLUMN_BACKWARD:
  1518. X        *amount = count ? -count : -1;
  1519. X        count = 0;
  1520. X        return MUSH_SCROLL_RELATIVE;
  1521. X    case ACTION_GO_DOCUMENT_START:
  1522. X        *amount = 1;
  1523. X        count = 0;
  1524. X        return MUSH_SCROLL_TO;
  1525. X    case ACTION_GO_DOCUMENT_END:
  1526. X        *amount = 2;
  1527. X        count = 0;
  1528. X        return MUSH_SCROLL_TO;
  1529. X    }
  1530. X#endif /* SUN_4_0 */
  1531. X    /* for SunOS 3.5, assume default SunView key mapping */
  1532. X    /* a little redundancy for 4.0, but it's ok */
  1533. X    if (!event_is_ascii(event) && ID != KEY_RIGHT(14) && ID != KEY_RIGHT(8)
  1534. X    && ID != KEY_RIGHT(7) && ID != KEY_RIGHT(13))
  1535. X    /* may have to reset "count" here */
  1536. X    return MUSH_SCROLL_PASS_EVENT; /* Not a scroll event */
  1537. X    switch (event_id(event)) {
  1538. X    case KEY_RIGHT(7):    /* Home on new keyboards */
  1539. X        *amount = 1, count = 0;
  1540. X        return MUSH_SCROLL_TO;
  1541. X    case KEY_RIGHT(13):    /* End on new keyboards */
  1542. X        *amount = 2, count = 0;
  1543. X        return MUSH_SCROLL_TO;
  1544. X    case 'j': case KEY_RIGHT(14):    /* downarrow */
  1545. X        *amount = count ? count : 1;
  1546. X    when 'k': case KEY_RIGHT(8):    /* uparrow */
  1547. X        *amount = count ? -count : -1;
  1548. X    when 'F'-'@':    /* ^f */
  1549. X        *amount = count ? count * (len-1) : len - 1;
  1550. X    when 'D'-'@':    /* ^d */
  1551. X        *amount = len/2;
  1552. X    when 'B'-'@':    /* ^b */
  1553. X        *amount = -(count ? count * (1-len) : len - 1);
  1554. X    when 'U'-'@':    /* ^u */
  1555. X        *amount = -len/2;
  1556. X    when '\033':    /* Escape */
  1557. X        /* For SunOS 3.5 check to see if this is a cursor
  1558. X         * move key, i.e. ESC[A or ESC[B.
  1559. X         */
  1560. X        if (!(int)window_read_event(client, event) &&
  1561. X        event_id(event) == '[' &&
  1562. X        !(int)window_read_event(client, event))
  1563. X            if (event_id(event) == 'A') {
  1564. X            *amount = -1;
  1565. X            break;
  1566. X            } else if (event_id(event) == 'B') {
  1567. X            *amount = 1;
  1568. X            break;
  1569. X            }
  1570. X    default:
  1571. X        count = 0;
  1572. X        return event_is_ascii(event) ?
  1573. X        MUSH_SCROLL_IGNORE : MUSH_SCROLL_PASS_EVENT;
  1574. X    }
  1575. X    count = 0;
  1576. X    /* Scroll indicated amount if event is down, ignore up events */
  1577. X    return MUSH_SCROLL_RELATIVE;
  1578. X}
  1579. X
  1580. XNotify_value
  1581. Xscroll_textwin(textsw, event, arg, type)
  1582. XTextsw    textsw;
  1583. XEvent    *event;
  1584. XNotify_arg    arg;
  1585. XNotify_event_type    type;
  1586. X{
  1587. X    int scroll_amount;
  1588. X
  1589. X    switch (decode_scroll(textsw, event, textsw_screen_line_count(textsw),
  1590. X    &scroll_amount)) {
  1591. X    case MUSH_SCROLL_PASS_EVENT :
  1592. X        return notify_next_event_func(textsw, event, arg, type);
  1593. X    case MUSH_SCROLL_IGNORE:
  1594. X        return NOTIFY_IGNORED;
  1595. X    case MUSH_SCROLL_TO:
  1596. X        if (scroll_amount == 1)
  1597. X        window_set(textsw, TEXTSW_FIRST, 0, NULL);
  1598. X        else if (scroll_amount == 2)
  1599. X        window_set(textsw, TEXTSW_FIRST, TEXTSW_INFINITY, NULL);
  1600. X        textsw_scroll_lines(textsw, -textsw_screen_line_count(textsw));
  1601. X        break;
  1602. X    case MUSH_SCROLL_RELATIVE :
  1603. X        textsw_scroll_lines(textsw, scroll_amount);
  1604. X    }
  1605. X    window_set(textsw, TEXTSW_UPDATE_SCROLLBAR, NULL);
  1606. X    return NOTIFY_DONE;
  1607. X}
  1608. X
  1609. X/*ARGSUSED*/
  1610. XNotify_value
  1611. Xdestroy_proc(frame, status)
  1612. XFrame frame;
  1613. XDestroy_status status;
  1614. X{
  1615. X    if (ison(glob_flags, IS_GETTING))
  1616. X    rm_edfile(-1);
  1617. X    /* status is ignored -- maybe post notice asking to confirm quit? */
  1618. X    if (status == DESTROY_CHECKING && ison(glob_flags, DO_UPDATE) &&
  1619. X    !ask("Your folder has been modified.  Quit anyway?"))
  1620. X    (void) notify_veto_destroy(frame);
  1621. X    else
  1622. X    cleanup(0); /* doesn't return */
  1623. X    return NOTIFY_DONE;
  1624. X}
  1625. X
  1626. X/* Initialise the Mush mail icon. */
  1627. Xstatic void
  1628. Xgeticon()
  1629. X{
  1630. X    static Rect lrect = { 5, 5, 26, 12 };
  1631. X
  1632. X    mail_icon = icon_create(
  1633. X    ICON_WIDTH,        64,
  1634. X    ICON_HEIGHT,        64,
  1635. X    ICON_FONT,        mush_font,
  1636. X    ICON_IMAGE,        &mail_icon_image1,
  1637. X    ICON_LABEL,        "",
  1638. X    ICON_LABEL_RECT,    &lrect,
  1639. X    0);
  1640. X}
  1641. X
  1642. X/* Initialise all the cursors used. */
  1643. Xstatic void
  1644. Xinit_cursors()
  1645. X{
  1646. X    extern Pixrect mouse_left, mouse_middle, mouse_right, bent_arrow;
  1647. X    extern Cursor bentarrow;
  1648. X
  1649. X    l_cursor = cursor_create(
  1650. X    CURSOR_XHOT,    3,
  1651. X    CURSOR_YHOT,    3,
  1652. X    CURSOR_OP,    PIX_SRC,
  1653. X    CURSOR_IMAGE,    &mouse_left,
  1654. X    NULL);
  1655. X    m_cursor = cursor_create(
  1656. X    CURSOR_XHOT,    3,
  1657. X    CURSOR_YHOT,    3,
  1658. X    CURSOR_OP,    PIX_SRC,
  1659. X    CURSOR_IMAGE,    &mouse_middle,
  1660. X    NULL);
  1661. X    r_cursor = cursor_create(
  1662. X    CURSOR_XHOT,    3,
  1663. X    CURSOR_YHOT,    3,
  1664. X    CURSOR_OP,    PIX_SRC,
  1665. X    CURSOR_IMAGE,    &mouse_right,
  1666. X    NULL);
  1667. X    bentarrow = cursor_create(
  1668. X    CURSOR_XHOT,    8,
  1669. X    CURSOR_YHOT,    8,
  1670. X    CURSOR_OP,    PIX_SRC|PIX_DST,
  1671. X    CURSOR_IMAGE,    &bent_arrow,
  1672. X    NULL);
  1673. X    coffee = cursor_create(
  1674. X    CURSOR_XHOT,    8,
  1675. X    CURSOR_YHOT,    8,
  1676. X    CURSOR_OP,    PIX_SRC,
  1677. X    CURSOR_IMAGE,    &coffee_cup,
  1678. X    NULL);
  1679. X}
  1680. X
  1681. X/* show the timeout cursor (coffee cup) when "on" is TRUE.  This routine
  1682. X * may be called many times in layers of locking mechanisms, so be careful
  1683. X * not to unlock cursors until "on" has been FALSE as many times as it has
  1684. X * been TRUE.
  1685. X */
  1686. Xvoid
  1687. Xtimeout_cursors(on)
  1688. Xint on;
  1689. X{
  1690. X    Window win;
  1691. X    Frame subframe;
  1692. X    static int locked, numwins;
  1693. X    int i = 0, j;
  1694. X    static struct {
  1695. X    Cursor cursor;
  1696. X    Window win;
  1697. X    } win_curs[64];
  1698. X
  1699. X    on? locked++ : locked--;
  1700. X    if (istool < 2 || locked > 1 || locked == 1 && on == 0)
  1701. X    return;
  1702. X    if (on) {
  1703. X    for (numwins = 0; win = window_get(tool, FRAME_NTH_SUBWINDOW, numwins);
  1704. X        numwins++) {
  1705. X        win_curs[numwins].cursor =
  1706. X                cursor_copy((Cursor) window_get(win, WIN_CURSOR));
  1707. X        win_curs[numwins].win = win;
  1708. X        window_set(win, WIN_CURSOR, coffee, NULL);
  1709. X    }
  1710. X    while (subframe = window_get(tool, FRAME_NTH_SUBFRAME, i++))
  1711. X        for (j = 0; win = window_get(subframe, FRAME_NTH_SUBWINDOW, j);
  1712. X            j++,numwins++) {
  1713. X        win_curs[numwins].cursor =
  1714. X                cursor_copy((Cursor) window_get(win, WIN_CURSOR));
  1715. X        win_curs[numwins].win = win;
  1716. X        window_set(win, WIN_CURSOR, coffee, NULL);
  1717. X        }
  1718. X    } else {
  1719. X    for (j = 0; j < numwins; j++) {
  1720. X        window_set(win_curs[j].win, WIN_CURSOR, win_curs[j].cursor, NULL);
  1721. X        cursor_destroy(win_curs[j].cursor);
  1722. X    }
  1723. X    }
  1724. X}
  1725. X
  1726. X/*
  1727. X * If the user has specified an alternate icon or set of icons in
  1728. X * his .mushrc file, copy the image(s) over the defaults.
  1729. X */
  1730. Xvoid
  1731. Xcheck_icons()
  1732. X{
  1733. X    Pixrect *icon_mpr;
  1734. X    char errbuf[256], *icon_file, *icon_path;
  1735. X    int isdir;
  1736. X
  1737. X    if ((icon_file = do_set(set_options, "mail_icon")) && *icon_file) {
  1738. X    isdir = 0;
  1739. X    icon_path = getpath(icon_file, &isdir);
  1740. X    if (isdir == 0) {
  1741. X        if (!(icon_mpr = icon_load_mpr(icon_path, errbuf)))
  1742. X        error("Error loading mail icon file:\n%s",errbuf);
  1743. X        else
  1744. X        pr_rop(&mail_icon_image1, 0,0,64,64, PIX_SRC, icon_mpr, 0, 0);
  1745. X    }
  1746. X    }
  1747. X    if ((icon_file = do_set(set_options, "newmail_icon")) && *icon_file) {
  1748. X    isdir = 0;
  1749. X    icon_path = getpath(icon_file, &isdir);
  1750. X    if (isdir == 0) {
  1751. X        if (!(icon_mpr = icon_load_mpr(icon_path, errbuf)))
  1752. X        error("Error loading newmail icon file:\n%s",errbuf);
  1753. X        else
  1754. X        pr_rop(&mail_icon_image2, 0,0,64,64, PIX_SRC, icon_mpr, 0, 0);
  1755. X    }
  1756. X    }
  1757. X}
  1758. END_OF_FILE
  1759. if test 15050 -ne `wc -c <'mush/tool.c'`; then
  1760.     echo shar: \"'mush/tool.c'\" unpacked with wrong size!
  1761. fi
  1762. # end of 'mush/tool.c'
  1763. fi
  1764. echo shar: End of archive 16 \(of 19\).
  1765. cp /dev/null ark16isdone
  1766. MISSING=""
  1767. for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ; do
  1768.     if test ! -f ark${I}isdone ; then
  1769.     MISSING="${MISSING} ${I}"
  1770.     fi
  1771. done
  1772. if test "${MISSING}" = "" ; then
  1773.     echo You have unpacked all 19 archives.
  1774.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  1775. else
  1776.     echo You still need to unpack the following archives:
  1777.     echo "        " ${MISSING}
  1778. fi
  1779. ##  End of shell archive.
  1780. exit 0
  1781.  
  1782.  
  1783.