Source:NetHack 3.3.0/mail.c

Below is the full text to mail.c from the source code of NetHack 3.3.0. To link to a particular line, write [[NetHack 3.3.0/mail.c#line123 ]], for example.

Warning! This is the source code from an old release. For the latest release, see Source code

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