home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume12 / mush / part16 < prev    next >
Encoding:
Text File  |  1990-05-05  |  53.8 KB  |  1,790 lines

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