Source:SLASH'EM 0.0.7E7F2/mail.c

Below is the full text to mail.c from the source code of SLASH'EM 0.0.7E7F2. To link to a particular line, write [[SLASH'EM 0.0.7E7F2/mail.c#line123 ]], for example.

The latest source code for vanilla NetHack is at Source code.

1.   /*	SCCS Id: @(#)mail.c	3.4	2002/01/13	*/ 2.   /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 3.    /* NetHack may be freely redistributed. See license for details. */ 4.     5.    #include "hack.h"  6. 7.   #ifdef MAIL 8.   #include "mail.h"  9. 10.  /*  11.    * Notify user when new mail has arrived. Idea by Merlyn Leroy. 12.   *  13.    * The mail daemon can move with less than usual restraint. It can: 14.   *	- move diagonally from a door 15.   *	- use secret and closed doors 16.   *	- run through a monster ("Gangway!", etc.) 17.   *	- run over pools & traps 18.   *  19.    * Possible extensions: 20.   *	- Open the file MAIL and do fstat instead of stat for efficiency. 21.   *	  (But sh uses stat, so this cannot be too bad.) 22.   *	- Examine the mail and produce a scroll of mail named "From somebody". 23.   *	- Invoke MAILREADER in such a way that only this single letter is read. 24.   *	- Do something to the text when the scroll is enchanted or cancelled. 25.   *	- Make the daemon always appear at a stairwell, and have it find a  26. *	 path to the hero. 27.   *  28.    * Note by Olaf Seibert: On the Amiga, we usually don't get mail. So we go 29. *			 through most of the effects at 'random' moments. 30.   * Note by Paul Winner:  The MSDOS port also 'fakes' the mail daemon at  31. *			 random intervals. 32.   */  33.    34.   STATIC_DCL boolean FDECL(md_start,(coord *)); 35.  STATIC_DCL boolean FDECL(md_stop,(coord *, coord *)); 36.  STATIC_DCL boolean FDECL(md_rush,(struct monst *,int,int)); 37.  STATIC_DCL void FDECL(newmail, (struct mail_info *)); 38.   39.   extern char *viz_rmin, *viz_rmax;	/* line-of-sight limits (vision.c) */ 40.   41.   #ifdef OVL0 42.   43.   # if !defined(UNIX) && !defined(VMS) && !defined(LAN_MAIL) 44.  int mustgetmail = -1; 45.  # endif 46.   47.   #endif /* OVL0 */ 48.  #ifdef OVLB 49.   50.   # ifdef UNIX 51.  #include   52. #include  53. /* DON'T trust all Unices to declare getpwuid in  */ 54.  #  if !defined(_BULL_SOURCE) && !defined(__sgi) && !defined(_M_UNIX) 55.  #   if !defined(SUNOS4) && !(defined(ULTRIX) && defined(__GNUC__)) 56.  #    if !defined(LINUX) 57.  /* DO trust all SVR4 to typedef uid_t in  (probably to a long) */ 58.  #    if defined(POSIX_TYPES) || defined(SVR4) || defined(HPUX) 59.  extern struct passwd *FDECL(getpwuid,(uid_t)); 60.  #    else 61.  extern struct passwd *FDECL(getpwuid,(int)); 62.  #    endif 63.  #   endif 64.  #  endif 65.  #  endif 66.  static struct stat omstat,nmstat; 67.  static char *mailbox = (char *)0; 68.  static long laststattime; 69.   70.   # if !defined(MAILPATH) && defined(AMS)	/* Just a placeholder for AMS */ 71.  #  define MAILPATH "/dev/null" 72.  # endif 73.  # if !defined(MAILPATH) && (defined(LINUX) || defined(__osf__)) 74.  #  define MAILPATH "/var/spool/mail/" 75.  # endif 76.  # if !defined(MAILPATH) && defined(__FreeBSD__) 77.  #  define MAILPATH "/var/mail/" 78.  # endif 79.  # if !defined(MAILPATH) && (defined(BSD) || defined(ULTRIX)) 80.  #  define MAILPATH "/usr/spool/mail/" 81.  # endif 82.  # if !defined(MAILPATH) && (defined(SYSV) || defined(HPUX)) 83.  #  define MAILPATH "/usr/mail/" 84.  # endif 85.   86.   void 87.  getmailstatus 88.  {  89.   	if(!mailbox && !(mailbox = nh_getenv("MAIL"))) { 90.  #  ifdef MAILPATH 91.  #   ifdef AMS 92.  	        struct passwd ppasswd; 93.   94.   		(void) memcpy(&ppasswd, getpwuid(getuid), sizeof(struct passwd)); 95.  		if (ppasswd.pw_dir) { 96.  		     mailbox = (char *) alloc((unsigned) strlen(ppasswd.pw_dir)+sizeof(AMS_MAILBOX)); 97.  		     Strcpy(mailbox, ppasswd.pw_dir); 98.  		     Strcat(mailbox, AMS_MAILBOX); 99.  		} else 100. 		  return; 101. #   else 102. 		const char *pw_name = getpwuid(getuid)->pw_name; 103. 		mailbox = (char *) alloc(sizeof(MAILPATH)+strlen(pw_name)); 104. 		Strcpy(mailbox, MAILPATH); 105. 		Strcat(mailbox, pw_name); 106. #  endif /* AMS */ 107. #  else 108. 		return; 109. #  endif 110. 	}  111.  	if(stat(mailbox, &omstat)){ 112. #  ifdef PERMANENT_MAILBOX 113. 		pline("Cannot get status of MAIL=\"%s\".", mailbox); 114. 		mailbox = 0; 115. #  else 116. 		omstat.st_mtime = 0; 117. #  endif 118. 	}  119.  }  120.  # endif /* UNIX */ 121.  122.  #endif /* OVLB */ 123. #ifdef OVL0 124.  125.  /*  126.   * Pick coordinates for a starting position for the mail daemon. Called 127.  * from newmail and newphone. 128.  */  129.  STATIC_OVL boolean 130. md_start(startp) 131.     coord *startp; 132. {  133.      coord testcc;	/* scratch coordinates */ 134.     int row;		/* current row we are checking */ 135.     int lax;		/* if TRUE, pick a position in sight. */ 136.      int dd;		/* distance to current point */ 137.     int max_distance;	/* max distance found so far */ 138.  139.      /*  140.       * If blind and not telepathic, then it doesn't matter what we pick --- 141.      * the hero is not going to see it anyway. So pick a nearby position. 142.      */  143.      if (Blind && !Blind_telepat) { 144. 	if (!enexto(startp, u.ux, u.uy, (struct permonst *) 0)) 145. 	    return FALSE;	/* no good posiitons */ 146. 	return TRUE; 147.     }  148.   149.      /*  150.       * Arrive at an up or down stairwell if it is in line of sight from the 151.      * hero. 152.      */  153.      if (couldsee(upstair.sx, upstair.sy)) { 154. 	startp->x = upstair.sx; 155. 	startp->y = upstair.sy; 156. 	return TRUE; 157.     }  158.      if (couldsee(dnstair.sx, dnstair.sy)) { 159. 	startp->x = dnstair.sx; 160. 	startp->y = dnstair.sy; 161. 	return TRUE; 162.     }  163.   164.      /*  165.       * Try to pick a location out of sight next to the farthest position away 166.      * from the hero. If this fails, try again, just picking the farthest 167.      * position that could be seen. What we really ought to be doing is 168. * finding a path from a stairwell... 169. * 170.       * The arrays viz_rmin[] and viz_rmax[] are set even when blind. These 171.      * are the LOS limits for each row. 172.      */  173.      lax = 0;	/* be picky */ 174.     max_distance = -1; 175. retry: 176.     for (row = 0; row < ROWNO; row++) { 177. 	if (viz_rmin[row] < viz_rmax[row]) { 178. 	    /* There are valid positions on this row. */ 179.  	    dd = distu(viz_rmin[row],row); 180. 	    if (dd > max_distance) { 181. 		if (lax) { 182. 		    max_distance = dd; 183. 		    startp->y = row; 184. 		    startp->x = viz_rmin[row]; 185. 		  186.  		} else if (enexto(&testcc, (xchar)viz_rmin[row], row, 187. 						(struct permonst *) 0) &&  188.  			   !cansee(testcc.x, testcc.y) &&  189.  			   couldsee(testcc.x, testcc.y)) { 190. 		    max_distance = dd; 191. 		    *startp = testcc; 192. 		}  193.  	    }  194.  	    dd = distu(viz_rmax[row],row); 195. 	    if (dd > max_distance) { 196. 		if (lax) { 197. 		    max_distance = dd; 198. 		    startp->y = row; 199. 		    startp->x = viz_rmax[row]; 200. 		  201.  		} else if (enexto(&testcc, (xchar)viz_rmax[row], row, 202. 						(struct permonst *) 0) &&  203.  			   !cansee(testcc.x,testcc.y) &&  204.  			   couldsee(testcc.x, testcc.y)) { 205.  206.  		    max_distance = dd; 207. 		    *startp = testcc; 208. 		}  209.  	    }  210.  	}  211.      }  212.   213.      if (max_distance < 0) { 214. 	if (!lax) { 215. 	    lax = 1;		/* just find a position */ 216. 	    goto retry; 217. 	}  218.  	return FALSE; 219.     }  220.   221.      return TRUE; 222. }  223.   224.  /*  225.   * Try to choose a stopping point as near as possible to the starting 226.  * position while still adjacent to the hero. If all else fails, try 227.  * enexto. Use enexto as a last resort because enexto chooses 228.  * its point randomly, which is not what we want. 229.  */  230.  STATIC_OVL boolean 231. md_stop(stopp, startp) 232.     coord *stopp;	/* stopping position (we fill it in) */ 233.     coord *startp;	/* starting positon (read only) */ 234. {  235.      int x, y, distance, min_distance = -1; 236.  237.      for (x = u.ux-1; x <= u.ux+1; x++) 238. 	for (y = u.uy-1; y <= u.uy+1; y++) { 239. 	    if (!isok(x, y) || (x == u.ux && y == u.uy)) continue; 240.  241.  	    if (ACCESSIBLE(levl[x][y].typ) && !MON_AT(x,y)) { 242. 		distance = dist2(x,y,startp->x,startp->y); 243. 		if (min_distance < 0 || distance < min_distance ||  244.  			(distance == min_distance && rn2(2))) { 245. 		    stopp->x = x;  246. stopp->y = y; 247. min_distance = distance; 248. 		}  249.  	    }  250.  	}  251.   252.      /* If we didn't find a good spot, try enexto. */ 253.      if (min_distance < 0 &&  254.  		!enexto(stopp, u.ux, u.uy, &mons[PM_MAIL_DAEMON])) 255. 	return FALSE; 256.  257.      return TRUE; 258. }  259.   260.  /* Let the mail daemon have a larger vocabulary. */ 261.  static NEARDATA const char *mail_text[] = { 262.     "Gangway!", 263.     "Look out!", 264.     "Pardon me!" 265. };  266.  #define md_exclamations	(mail_text[rn2(3)]) 267.  268.  /*  269.   * Make the mail daemon run through the dungeon. The daemon will run over 270.  * any monsters that are in its path, but will replace them later. Return 271.  * FALSE if the md gets stuck in a position where there is a monster. Return 272.  * TRUE otherwise. 273.  */  274.  STATIC_OVL boolean 275. md_rush(md,tx,ty) 276.     struct monst *md; 277.     register int tx, ty;		/* destination of mail daemon */ 278. {  279.      struct monst *mon;			/* displaced monster */ 280.     register int dx, dy;		/* direction counters */ 281.     int fx = md->mx, fy = md->my;	/* current location */ 282.     int nfx = fx, nfy = fy,		/* new location */ 283. 	d1, d2;				/* shortest distances */ 284.  285.      /*  286.       * It is possible that the monster at (fx,fy) is not the md when: 287.      * the md rushed the hero and failed, and is now starting back. 288.      */  289.      if (m_at(fx, fy) == md) { 290. 	remove_monster(fx, fy);		/* pick up from orig position */ 291. 	newsym(fx, fy); 292.     }  293.   294.      /*  295.       * At the beginning and exit of this loop, md is not placed in the 296.      * dungeon. 297.      */  298.      while (1) { 299. 	/* Find a good location next to (fx,fy) closest to (tx,ty). */ 300.  	d1 = dist2(fx,fy,tx,ty); 301. 	for (dx = -1; dx <= 1; dx++) for(dy = -1; dy <= 1; dy++) 302. 	    if ((dx || dy) && isok(fx+dx,fy+dy) &&  303.  				       !IS_STWALL(levl[fx+dx][fy+dy].typ)) { 304. 		d2 = dist2(fx+dx,fy+dy,tx,ty); 305. 		if (d2 < d1) { 306. 		    d1 = d2; 307. 		    nfx = fx+dx; 308. 		    nfy = fy+dy; 309. 		}  310.  	    }  311.   312.  	/* Break if the md couldn't find a new position. */ 313.  	if (nfx == fx && nfy == fy) break; 314.  315.  	fx = nfx;			/* this is our new position */ 316. 	fy = nfy; 317.  318.  	/* Break if the md reaches its destination. */ 319.  	if (fx == tx && fy == ty) break; 320.  321.  	if ((mon = m_at(fx,fy)) != 0)	/* save monster at this position */ 322. 	    verbalize(md_exclamations); 323. 	else if (fx == u.ux && fy == u.uy) 324. 	    verbalize("Excuse me."); 325.  326.  	place_monster(md,fx,fy);	/* put md down */ 327. 	newsym(fx,fy);			/* see it */ 328. 	flush_screen(0);		/* make sure md shows up */ 329. 	delay_output;			/* wait a little bit */ 330.  331.  	/* Remove md from the dungeon. Restore original mon, if necessary. */ 332.  	if (mon) { 333. 	    if ((mon->mx != fx) || (mon->my != fy)) 334. 		place_worm_seg(mon, fx, fy); 335. 	    else 336. 		place_monster(mon, fx, fy); 337. 	} else 338. 	    remove_monster(fx, fy); 339. 	newsym(fx,fy); 340.     }  341.   342.      /*  343.       * Check for a monster at our stopping position (this is possible, but  344.       * very unlikely). If one exists, then have the md leave in disgust. 345.      */  346.      if ((mon = m_at(fx, fy)) != 0) { 347. 	place_monster(md, fx, fy);	/* display md with text below */ 348. 	newsym(fx, fy); 349. 	verbalize("This place's too crowded.  I'm outta here."); 350.  351.  	if ((mon->mx != fx) || (mon->my != fy))	/* put mon back */ 352. 	    place_worm_seg(mon, fx, fy); 353. 	else 354. 	    place_monster(mon, fx, fy); 355.  356.  	newsym(fx, fy); 357. 	return FALSE; 358.     }  359.   360.      place_monster(md, fx, fy);	/* place at final spot */ 361.     newsym(fx, fy); 362.     flush_screen(0); 363.     delay_output;			/* wait a little bit */ 364.  365.      return TRUE; 366. }  367.   368.  /* Deliver a scroll of mail. */ 369.  /*ARGSUSED*/ 370. STATIC_OVL void 371. newmail(info) 372. struct mail_info *info; 373. {  374.      struct monst *md; 375.     coord start, stop; 376.     boolean message_seen = FALSE; 377.  378.      /* Try to find good starting and stopping places. */ 379.      if (!md_start(&start) || !md_stop(&stop,&start)) goto give_up; 380.  381.      /* Make the daemon. Have it rush towards the hero. */ 382.      if (!(md = makemon(&mons[PM_MAIL_DAEMON], start.x, start.y, NO_MM_FLAGS))) 383. 	 goto give_up; 384.     if (!md_rush(md, stop.x, stop.y)) goto go_back; 385.  386.      message_seen = TRUE; 387.     verbalize("%s, %s!  %s.", Hello(md), plname, info->display_txt); 388.  389.      if (info->message_typ) { 390. 	struct obj *obj = mksobj(SCR_MAIL, FALSE, FALSE); 391. 	if (distu(md->mx,md->my) > 2) 392. 	    verbalize("Catch!"); 393. 	display_nhwindow(WIN_MESSAGE, FALSE); 394. 	if (info->object_nam) { 395. 	    obj = oname(obj, info->object_nam); 396. 	    if (info->response_cmd) {	/*(hide extension of the obj name)*/ 397. 		int namelth = info->response_cmd - info->object_nam - 1; 398. 		if ( namelth <= 0 || namelth >= (int) obj->onamelth ) 399. 		    impossible("mail delivery screwed up"); 400. 		else 401. 		    *(ONAME(obj) + namelth) = '\0'; 402. 		/* Note: renaming object will discard the hidden command. */ 403.  	    }  404.  	}  405.  	obj = hold_another_object(obj, "Oops!",  406.  				  (const char *)0, (const char *)0); 407.     }  408.   409.      /* zip back to starting location */ 410. go_back: 411.     (void) md_rush(md, start.x, start.y); 412.     mongone(md); 413.     /* deliver some classes of messages even if no daemon ever shows up */ 414. give_up: 415.     if (!message_seen && info->message_typ == MSG_OTHER) 416. 	pline("Hark!  \"%s.\"", info->display_txt); 417. }  418.   419.  # if !defined(UNIX) && !defined(VMS) && !defined(LAN_MAIL) 420.  421.  void 422. ckmailstatus 423. {  424.  	if (u.uswallow || !flags.biff) return; 425. 	if (mustgetmail < 0) { 426. #if defined(AMIGA) || defined(MSDOS) || defined(TOS) || defined(OS2) 427. 	    mustgetmail=(moves<2000)?(100+rn2(2000)):(2000+rn2(3000)); 428. #endif 429. 	    return; 430. 	}  431.  	if (--mustgetmail <= 0) { 432. 		static struct mail_info 433. 			deliver = {MSG_MAIL,"I have some mail for you",0,0}; 434. 		newmail(&deliver); 435. 		mustgetmail = -1; 436. 	}  437.  }  438.   439.  /*ARGSUSED*/ 440. void 441. readmail(otmp) 442. struct obj *otmp; 443. {  444.      const char *line; 445.     char buf[BUFSZ]; 446.     line = getrumor(bcsign(otmp), buf, TRUE); 447.     if (!*line) 448. 	   line = "NetHack rumors file closed for renovation."; 449.     if (Blind) { 450. 	pline("Unfortunately you cannot see what it says."); 451.     } else 452. 	pline("It reads:  \"%s\"", line); 453.  454.  }  455.   456.  # endif /* !UNIX && !VMS */ 457.  458.  # ifdef UNIX 459.  460.  void 461. ckmailstatus 462. {  463.  	if(!mailbox || u.uswallow || !flags.biff  464.  #  ifdef MAILCKFREQ  465.  		    || moves < laststattime + MAILCKFREQ  466.  #  endif  467.  							) 468. 		return; 469.  470.  	laststattime = moves; 471. 	if(stat(mailbox, &nmstat)){ 472. #  ifdef PERMANENT_MAILBOX 473. 		pline("Cannot get status of MAIL=\"%s\" anymore.", mailbox); 474. 		mailbox = 0; 475. #  else 476. 		nmstat.st_mtime = 0; 477. #  endif 478. 	} else if(nmstat.st_mtime > omstat.st_mtime) { 479. 		if (nmstat.st_size) { 480. 		    static struct mail_info deliver = { 481. #  ifndef NO_MAILREADER 482. 			MSG_MAIL, "I have some mail for you", 483. #  else 484. 			/* suppress creation and delivery of scroll of mail */ 485. 			MSG_OTHER, "You have some mail in the outside world", 486. #  endif 487. 			0, 0  488.  		    };  489.  		    newmail(&deliver); 490. 		}  491.  		getmailstatus;	/* might be too late ... */ 492.  	}  493.  }  494.   495.  /*ARGSUSED*/ 496. void 497. readmail(otmp) 498. struct obj *otmp; 499. {  500.  #  ifdef DEF_MAILREADER			/* This implies that UNIX is defined */ 501. 	register const char *mr = 0; 502.  503.  	display_nhwindow(WIN_MESSAGE, FALSE); 504. 	if(!(mr = nh_getenv("MAILREADER"))) 505. 		mr = DEF_MAILREADER; 506.  507.  	if(child(1)){ 508. 		(void) execl(mr, mr, (char *)0); 509. 		terminate(EXIT_FAILURE); 510. 	}  511.  #  else 512. #   ifndef AMS				/* AMS mailboxes are directories */ 513. 	display_file_area(NULL, mailbox, TRUE); 514. #   endif /* AMS */ 515. #  endif /* DEF_MAILREADER */ 516.  517.  	/* get new stat; not entirely correct: there is a small time 518. 	   window where we do not see new mail */ 519. 	getmailstatus; 520. }  521.   522.  # endif /* UNIX */ 523.  524.  # ifdef VMS 525.  526.  extern NDECL(struct mail_info *parse_next_broadcast); 527.  528.  volatile int broadcasts = 0; 529.  530.  void 531. ckmailstatus 532. {  533.      struct mail_info *brdcst; 534.  535.      if(u.uswallow || !flags.biff) return; 536.  537.      while (broadcasts > 0) {	/* process all trapped broadcasts [until] */ 538. 	broadcasts--; 539. 	if ((brdcst = parse_next_broadcast) != 0) { 540. 	    newmail(brdcst); 541. 	    break;		/* only handle one real message at a time */ 542. 	}  543.      }  544.  }  545.   546.  void 547. readmail(otmp) 548. struct obj *otmp; 549. {  550.  #  ifdef SHELL	/* can't access mail reader without spawning subprocess */ 551.     const char *txt, *cmd; 552.     char *p, buf[BUFSZ], qbuf[BUFSZ]; 553.     int len; 554.  555.      /* there should be a command hidden beyond the object name */ 556.     txt = otmp->onamelth ? ONAME(otmp) : ""; 557.     len = strlen(txt); 558.     cmd = (len + 1 < otmp->onamelth) ? txt + len + 1 : (char *) 0; 559.     if (!cmd || !*cmd) cmd = "SPAWN"; 560.  561.      Sprintf(qbuf, "System command (%s)", cmd); 562.     getlin(qbuf, buf); 563.     if (*buf != '\033') { 564. 	for (p = eos(buf); p > buf; *p = '\0') 565. 	    if (*--p != ' ') break;	/* strip trailing spaces */ 566. 	if (*buf) cmd = buf;		/* use user entered command */ 567. 	if (!strcmpi(cmd, "SPAWN") || !strcmp(cmd, "!")) 568. 	    cmd = (char *) 0;		/* interactive escape */ 569.  570.  	vms_doshell(cmd, TRUE); 571. 	(void) sleep(1); 572.     }  573.  #  endif /* SHELL */ 574. }  575.   576.  # endif /* VMS */ 577.  578.  # ifdef LAN_MAIL 579.  580.  void 581. ckmailstatus 582. {  583.  	static int laststattime = 0; 584. 	  585.  	if(u.uswallow || !flags.biff  586.  #  ifdef MAILCKFREQ  587.  		    || moves < laststattime + MAILCKFREQ  588.  #  endif  589.  							) 590. 		return; 591.  592.  	laststattime = moves; 593. 	if (lan_mail_check) { 594. 		    static struct mail_info deliver = { 595. #  ifndef NO_MAILREADER 596. 			MSG_MAIL, "I have some mail for you", 597. #  else 598. 			/* suppress creation and delivery of scroll of mail */ 599. 			MSG_OTHER, "You have some mail in the outside world", 600. #  endif 601. 			0, 0  602.  		    };  603.  		    newmail(&deliver); 604. 	}  605.  }  606.   607.  /*ARGSUSED*/ 608. void 609. readmail(otmp) 610. struct obj *otmp; 611. {  612.  	lan_mail_read(otmp); 613. }  614.   615.  # endif /* LAN_MAIL */ 616.  617.  #endif /* OVL0 */ 618.  619.  #endif /* MAIL */ 620.  621.  /*mail.c*/