1 /* File: util.c */
2 
3 /* Purpose: Angband utilities -BEN- */
4 
5 
6 #include "angband.h"
7 
8 
9 #ifdef SET_UID
10 
11 #ifdef PRIVATE_USER_PATH
12 
13 /*
14  * Create an ".angband/" directory in the users home directory.
15  *
16  * ToDo: Add error handling.
17  * ToDo: Only create the directories when actually writing files.
18  */
create_user_dirs(void)19 void create_user_dirs(void)
20 {
21 	char dirpath[1024];
22 	char subdirpath[1024];
23 
24 
25 	/* Get an absolute path from the filename */
26 	path_parse(dirpath, 1024, PRIVATE_USER_PATH);
27 
28 	/* Create the ~/.angband/ directory */
29 	mkdir(dirpath, 0700);
30 
31 	/* Build the path to the variant-specific sub-directory */
32 	path_make(subdirpath, dirpath, VERSION_NAME);
33 
34 	/* Create the directory */
35 	mkdir(subdirpath, 0700);
36 
37 #ifdef USE_PRIVATE_PATHS
38 
39 	/* Build the path to the scores sub-directory */
40 	path_build(dirpath, sizeof(dirpath), subdirpath, "scores");
41 
42 	/* Create the directory */
43 	mkdir(dirpath, 0700);
44 
45 	/* Build the path to the savefile sub-directory */
46 	path_build(dirpath, sizeof(dirpath), subdirpath, "bone");
47 
48 	/* Create the directory */
49 	mkdir(dirpath, 0700);
50 
51 	/* Build the path to the savefile sub-directory */
52 	path_build(dirpath, sizeof(dirpath), subdirpath, "data");
53 
54 	/* Create the directory */
55 	mkdir(dirpath, 0700);
56 
57 	/* Build the path to the savefile sub-directory */
58 	path_build(dirpath, sizeof(dirpath), subdirpath, "save");
59 
60 	/* Create the directory */
61 	mkdir(dirpath, 0700);
62 
63 #endif /* USE_PRIVATE_PATHS */
64 
65 }
66 
67 #endif /* PRIVATE_USER_PATH */
68 
69 
70 /*
71  * Hack -- drop permissions
72  */
safe_setuid_drop(void)73 void safe_setuid_drop(void)
74 {
75 
76 #ifdef SAFE_SETUID
77 
78 #ifdef HAVE_SETEGID
79 
80 	if (setegid(getgid()) != 0)
81 	{
82 		quit("setegid(): cannot set permissions correctly!");
83 	}
84 
85 #else  /* HAVE_SETEGID */
86 
87 #ifdef SAFE_SETUID_POSIX
88 
89 	if (setgid(getgid()) != 0)
90 	{
91 		quit("setgid(): cannot set permissions correctly!");
92 	}
93 
94 #else  /* SAFE_SETUID_POSIX */
95 
96 	if (setregid(getegid(), getgid()) != 0)
97 	{
98 		quit("setregid(): cannot set permissions correctly!");
99 	}
100 
101 #endif /* SAFE_SETUID_POSIX */
102 
103 #endif /* HAVE_SETEGID */
104 
105 #endif /* SAFE_SETUID */
106 
107 }
108 
109 
110 /*
111  * Hack -- grab permissions
112  */
safe_setuid_grab(void)113 void safe_setuid_grab(void)
114 {
115 
116 #ifdef SAFE_SETUID
117 
118 #ifdef HAVE_SETEGID
119 
120 	if (setegid(player_egid) != 0)
121 	{
122 		quit("setegid(): cannot set permissions correctly!");
123 	}
124 
125 #else  /* HAVE_SETEGID */
126 
127 #ifdef SAFE_SETUID_POSIX
128 
129 	if (setgid(player_egid) != 0)
130 	{
131 		quit("setgid(): cannot set permissions correctly!");
132 	}
133 
134 #else  /* SAFE_SETUID_POSIX */
135 
136 	if (setregid(getegid(), getgid()) != 0)
137 	{
138 		quit("setregid(): cannot set permissions correctly!");
139 	}
140 
141 #endif /* SAFE_SETUID_POSIX */
142 
143 #endif /* HAVE_SETEGID */
144 
145 #endif /* SAFE_SETUID */
146 
147 }
148 
149 
150 /*
151  * Initialise things for multiuser machines
152  * Pay special attention to permisions.
153  */
init_setuid(void)154 void init_setuid(void)
155 {
156 	/* Default permissions on files */
157 	(void)umask(022);
158 
159 	/* Get the user id (?) */
160 	player_uid = getuid();
161 
162 #ifdef VMS
163 	/* Mega-Hack -- Factor group id */
164 	player_uid += (getgid() * 1000);
165 #endif /* VMS */
166 
167 #ifdef SAFE_SETUID
168 
169 #if defined(HAVE_SETEGID) || defined(SAFE_SETUID_POSIX)
170 
171 	/* Save some info for later */
172 	player_euid = geteuid();
173 	player_egid = getegid();
174 
175 #endif /* defined(HAVE_SETEGID) || defined(SAFE_SETUID_POSIX) */
176 
177 	/* XXX XXX XXX */
178 #if 0
179 
180 	/* Redundant setting necessary in case root is running the game */
181 	/* If not root or game not setuid the following two calls do nothing */
182 
183 	if (setgid(getegid()) != 0)
184 	{
185 		quit("setgid(): cannot set permissions correctly!");
186 	}
187 
188 	if (setuid(geteuid()) != 0)
189 	{
190 		quit("setuid(): cannot set permissions correctly!");
191 	}
192 
193 #endif /* 0 */
194 
195 #endif /* SAFE_SETUID */
196 
197 	/* Drop permissions */
198 	safe_setuid_drop();
199 
200 	/* Get the "user name" as a default player name */
201 	user_name(player_name, player_uid);
202 
203 #ifdef PRIVATE_USER_PATH
204 
205 	/* Create a directory for the users files. */
206 	create_user_dirs();
207 
208 #endif /* PRIVATE_USER_PATH */
209 }
210 
211 
212 #else /* SET_UID */
213 
safe_setuid_drop(void)214 void safe_setuid_drop(void)
215 {
216 
217 }
218 
safe_setuid_grab(void)219 void safe_setuid_grab(void)
220 {
221 
222 }
223 
init_setuid(void)224 void init_setuid(void)
225 {
226 
227 }
228 
229 #endif /* SET_UID */
230 
231 
232 
233 #ifdef HANDLE_SIGNALS
234 
235 
236 #include <signal.h>
237 
238 
239 /*
240  * Handle signals -- suspend
241  *
242  * Actually suspend the game, and then resume cleanly
243  */
handle_signal_suspend(int sig)244 static void handle_signal_suspend(int sig)
245 {
246 	/* Disable handler */
247 	(void)signal(sig, SIG_IGN);
248 
249 #ifdef SIGSTOP
250 
251 	/* Flush output */
252 	Term_fresh();
253 
254 	/* Suspend the "Term" */
255 	Term_xtra(TERM_XTRA_ALIVE, 0);
256 
257 	/* Suspend ourself */
258 	(void)kill(0, SIGSTOP);
259 
260 	/* Resume the "Term" */
261 	Term_xtra(TERM_XTRA_ALIVE, 1);
262 
263 	/* Redraw the term */
264 	Term_redraw();
265 
266 	/* Flush the term */
267 	Term_fresh();
268 
269 #endif
270 
271 	/* Restore handler */
272 	(void)signal(sig, handle_signal_suspend);
273 }
274 
275 
276 /*
277  * Handle signals -- simple (interrupt and quit)
278  *
279  * This function was causing a *huge* number of problems, so it has
280  * been simplified greatly.  We keep a global variable which counts
281  * the number of times the user attempts to kill the process, and
282  * we commit suicide if the user does this a certain number of times.
283  *
284  * We attempt to give "feedback" to the user as he approaches the
285  * suicide thresh-hold, but without penalizing accidental keypresses.
286  *
287  * To prevent messy accidents, we should reset this global variable
288  * whenever the user enters a keypress, or something like that.
289  */
handle_signal_simple(int sig)290 static void handle_signal_simple(int sig)
291 {
292 	/* Disable handler */
293 	(void)signal(sig, SIG_IGN);
294 
295 
296 	/* Nothing to save, just quit */
297 	if (!character_generated || character_saved) quit(NULL);
298 
299 
300 	/* Count the signals */
301 	signal_count++;
302 
303 
304 	/* Terminate dead characters */
305 	if (p_ptr->state.is_dead)
306 	{
307 		/* Mark the savefile */
308 		(void)strcpy(p_ptr->state.died_from, "Abortion");
309 
310 		/* Close stuff */
311 		close_game();
312 
313 		/* Quit */
314 		quit("interrupt");
315 	}
316 
317 	/* Allow suicide (after 5) */
318 	else if (signal_count >= 5)
319 	{
320 		/* Cause of "death" */
321 		(void)strcpy(p_ptr->state.died_from, "Interrupting");
322 
323 		/* Stop playing */
324 		p_ptr->state.playing = FALSE;
325 
326 		/* Suicide */
327 		p_ptr->state.is_dead = TRUE;
328 
329 		/* Leaving */
330 		p_ptr->state.leaving = TRUE;
331 
332 		/* Close stuff */
333 		close_game();
334 
335 		/* Quit */
336 		quit("interrupt");
337 	}
338 
339 	/* Give warning (after 4) */
340 	else if (signal_count >= 4)
341 	{
342 		/* Make a noise */
343 		Term_xtra(TERM_XTRA_NOISE, 0);
344 
345 		/* Display the cause */
346 		prtf(0, 0, "Contemplating suicide!");
347 
348 		/* Flush */
349 		Term_fresh();
350 	}
351 
352 	/* Give warning (after 2) */
353 	else if (signal_count >= 2)
354 	{
355 		/* Make a noise */
356 		Term_xtra(TERM_XTRA_NOISE, 0);
357 	}
358 
359 	/* Restore handler */
360 	(void)signal(sig, handle_signal_simple);
361 }
362 
363 
364 /*
365  * Handle signal -- abort, kill, etc
366  */
handle_signal_abort(int sig)367 static void handle_signal_abort(int sig)
368 {
369 	/* Disable handler */
370 	(void)signal(sig, SIG_IGN);
371 
372 
373 	/* Nothing to save, just quit */
374 	if (!character_generated || character_saved) quit(NULL);
375 
376 	/* Give a warning */
377 	prtf(0, 23, CLR_RED "A gruesome software bug LEAPS out at you!");
378 
379 	/* Message */
380 	put_fstr(45, 23, CLR_RED "Panic save...");
381 
382 	/* Flush output */
383 	Term_fresh();
384 
385 	/* Panic Save */
386 	p_ptr->state.panic_save = 1;
387 
388 	/* Panic save */
389 	(void)strcpy(p_ptr->state.died_from, "(panic save)");
390 
391 	/* Forbid suspend */
392 	signals_ignore_tstp();
393 
394 	/* Attempt to save */
395 	if (save_player())
396 	{
397 		put_fstr(45, 23, CLR_RED "Panic save succeeded!");
398 	}
399 
400 	/* Save failed */
401 	else
402 	{
403 		put_fstr(45, 23, CLR_RED "Panic save failed!");
404 	}
405 
406 	/* Flush output */
407 	Term_fresh();
408 
409 	/* Quit */
410 	quit("software bug");
411 }
412 
413 
414 
415 
416 /*
417  * Ignore SIGTSTP signals (keyboard suspend)
418  */
signals_ignore_tstp(void)419 void signals_ignore_tstp(void)
420 {
421 
422 #ifdef SIGTSTP
423 	(void)signal(SIGTSTP, SIG_IGN);
424 #endif
425 
426 }
427 
428 /*
429  * Handle SIGTSTP signals (keyboard suspend)
430  */
signals_handle_tstp(void)431 void signals_handle_tstp(void)
432 {
433 
434 #ifdef SIGTSTP
435 	(void)signal(SIGTSTP, handle_signal_suspend);
436 #endif
437 
438 }
439 
440 
441 /*
442  * Prepare to handle the relevant signals
443  */
signals_init(void)444 void signals_init(void)
445 {
446 
447 #ifdef SIGHUP
448 	(void)signal(SIGHUP, SIG_IGN);
449 #endif
450 
451 
452 #ifdef SIGTSTP
453 	(void)signal(SIGTSTP, handle_signal_suspend);
454 #endif
455 
456 
457 #ifdef SIGINT
458 	(void)signal(SIGINT, handle_signal_simple);
459 #endif
460 
461 #ifdef SIGQUIT
462 	(void)signal(SIGQUIT, handle_signal_simple);
463 #endif
464 
465 
466 #ifdef SIGFPE
467 	(void)signal(SIGFPE, handle_signal_abort);
468 #endif
469 
470 #ifdef SIGILL
471 	(void)signal(SIGILL, handle_signal_abort);
472 #endif
473 
474 #ifdef SIGTRAP
475 	(void)signal(SIGTRAP, handle_signal_abort);
476 #endif
477 
478 #ifdef SIGIOT
479 	(void)signal(SIGIOT, handle_signal_abort);
480 #endif
481 
482 #ifdef SIGKILL
483 	(void)signal(SIGKILL, handle_signal_abort);
484 #endif
485 
486 #ifdef SIGBUS
487 	(void)signal(SIGBUS, handle_signal_abort);
488 #endif
489 
490 #ifdef SIGSEGV
491 	(void)signal(SIGSEGV, handle_signal_abort);
492 #endif
493 
494 #ifdef SIGTERM
495 	(void)signal(SIGTERM, handle_signal_abort);
496 #endif
497 
498 #ifdef SIGPIPE
499 	(void)signal(SIGPIPE, handle_signal_abort);
500 #endif
501 
502 #ifdef SIGEMT
503 	(void)signal(SIGEMT, handle_signal_abort);
504 #endif
505 
506 #ifdef SIGDANGER
507 	(void)signal(SIGDANGER, handle_signal_abort);
508 #endif
509 
510 #ifdef SIGSYS
511 	(void)signal(SIGSYS, handle_signal_abort);
512 #endif
513 
514 #ifdef SIGXCPU
515 	(void)signal(SIGXCPU, handle_signal_abort);
516 #endif
517 
518 #ifdef SIGPWR
519 	(void)signal(SIGPWR, handle_signal_abort);
520 #endif
521 
522 }
523 
524 
525 #else  /* HANDLE_SIGNALS */
526 
527 
528 /*
529  * Do nothing
530  */
signals_ignore_tstp(void)531 void signals_ignore_tstp(void)
532 {
533 }
534 
535 /*
536  * Do nothing
537  */
signals_handle_tstp(void)538 void signals_handle_tstp(void)
539 {
540 }
541 
542 /*
543  * Do nothing
544  */
signals_init(void)545 void signals_init(void)
546 {
547 }
548 
549 #endif /* HANDLE_SIGNALS */
550 
551 
552 #ifdef SET_UID
553 
554 # ifndef HAS_USLEEP
555 
556 /*
557  * For those systems that don't have "usleep()" but need it.
558  *
559  * Fake "usleep()" function grabbed from the inl netrek server -cba
560  */
usleep(huge usecs)561 int usleep(huge usecs)
562 {
563 	struct timeval Timer;
564 
565 	int nfds = 0;
566 
567 #ifdef FD_SET
568 	fd_set *no_fds = NULL;
569 #else
570 	int *no_fds = NULL;
571 #endif
572 
573 
574 	/* Was: int readfds, writefds, exceptfds; */
575 	/* Was: readfds = writefds = exceptfds = 0; */
576 
577 
578 	/* Paranoia -- No excessive sleeping */
579 	if (usecs > 4000000L) core("Illegal usleep() call");
580 
581 
582 	/* Wait for it */
583 	Timer.tv_sec = (usecs / 1000000L);
584 	Timer.tv_usec = (usecs % 1000000L);
585 
586 	/* Wait for it */
587 	if (select(nfds, no_fds, no_fds, no_fds, &Timer) < 0)
588 	{
589 		/* Hack -- ignore interrupts */
590 		if (errno != EINTR) return -1;
591 	}
592 
593 	/* Success */
594 	return 0;
595 }
596 
597 # endif /* !HAS_USLEEP */
598 
599 /*
600  * Hack -- External functions
601  */
602 #ifndef	_PWD_H
603 # ifndef HAVE_GETPWUID
604 extern struct passwd *getpwuid();
605 # endif
606 # ifndef HAVE_GETPWNAM
607 extern struct passwd *getpwnam();
608 # endif
609 #endif /* _PWD_H */
610 
611 /*
612  * Find a default user name from the system.
613  */
user_name(char * buf,int id)614 void user_name(char *buf, int id)
615 {
616 	struct passwd *pw;
617 
618 	/* Look up the user name */
619 	if ((pw = getpwuid(id)))
620 	{
621 		/* Get the first 15 characters of the user name */
622 		(void)strncpy(buf, pw->pw_name, 16);
623 		buf[15] = '\0';
624 
625 #ifdef CAPITALIZE_USER_NAME
626 		/* Hack -- capitalize the user name */
627 		if (islower(buf[0])) buf[0] = toupper(buf[0]);
628 #endif /* CAPITALIZE_USER_NAME */
629 
630 		return;
631 	}
632 
633 	/* Oops.  Hack -- default to "PLAYER" */
634 	strcpy(buf, "PLAYER");
635 }
636 
637 #endif /* SET_UID */
638 
639 /*
640  * Helper function to assert something inside an expression
641  *
642  * (Note that the normal assert macro can only be used
643  *  as a statement - which prevents debugging via
644  *  function wrappers.)
645  */
assert_helper(cptr expr,cptr file,int line,bool result)646 bool assert_helper(cptr expr, cptr file, int line, bool result)
647 {
648 	if (!result)
649 	{
650 		signals_ignore_tstp();\
651 		ANG__assert_save;\
652 		ANG__assert_fmt("\n"
653 						"Assertion failed:%s\n"
654 						"in file %s\n"
655 						"on line %d\n\n", expr, file, line);
656 	}
657 
658 	return (result);
659 }
660 
661 
662 /*
663  * The concept of the "file" routines below (and elsewhere) is that all
664  * file handling should be done using as few routines as possible, since
665  * every machine is slightly different, but these routines always have the
666  * same semantics.
667  *
668  * In fact, perhaps we should use the "path_parse()" routine below to convert
669  * from "canonical" filenames (optional leading tilde's, internal wildcards,
670  * slash as the path seperator, etc) to "system" filenames (no special symbols,
671  * system-specific path seperator, etc).  This would allow the program itself
672  * to assume that all filenames are "Unix" filenames, and explicitly "extract"
673  * such filenames if needed (by "path_parse()", or perhaps "path_canon()").
674  *
675  * Note that "path_temp" should probably return a "canonical" filename.
676  *
677  * Note that "my_fopen()" and "my_open()" and "my_make()" and "my_kill()"
678  * and "my_move()" and "my_copy()" should all take "canonical" filenames.
679  *
680  * Note that "canonical" filenames use a leading "slash" to indicate an absolute
681  * path, and a leading "tilde" to indicate a special directory, and default to a
682  * relative path, but MSDOS uses a leading "drivename plus colon" to indicate the
683  * use of a "special drive", and then the rest of the path is parsed "normally",
684  * and MACINTOSH uses a leading colon to indicate a relative path, and an embedded
685  * colon to indicate a "drive plus absolute path", and finally defaults to a file
686  * in the current working directory, which may or may not be defined.
687  *
688  * We should probably parse a leading "~~/" as referring to "ANGBAND_DIR". (?)
689  */
690 
691 
692 #ifdef ACORN
693 
694 
695 /*
696  * Most of the "file" routines for "ACORN" should be in "main-acn.c"
697  */
698 
699 
700 #else  /* ACORN */
701 
702 
703 #ifdef SET_UID
704 
705 /*
706  * Extract a "parsed" path from an initial filename
707  * Normally, we simply copy the filename into the buffer
708  * But leading tilde symbols must be handled in a special way
709  * Replace "~user/" by the home directory of the user named "user"
710  * Replace "~/" by the home directory of the current user
711  */
path_parse(char * buf,int max,cptr file)712 errr path_parse(char *buf, int max, cptr file)
713 {
714 	cptr u, s;
715 	struct passwd *pw;
716 	char user[128];
717 
718 	/* Assume no result */
719 	buf[0] = '\0';
720 
721 	/* No file? */
722 	if (!file) return (-1);
723 
724 	/* File needs no parsing */
725 	if (file[0] != '~')
726 	{
727 		strncpy(buf, file, max - 1);
728 
729 		/* Terminate */
730 		buf[max - 1] = '\0';
731 
732 		return (0);
733 	}
734 
735 	/* Point at the user */
736 	u = file + 1;
737 
738 	/* Look for non-user portion of the file */
739 	s = strstr(u, PATH_SEP);
740 
741 	/* Hack -- no long user names */
742 	if (s && (s >= u + sizeof(user))) return (1);
743 
744 	/* Extract a user name */
745 	if (s)
746 	{
747 		int i;
748 		for (i = 0; u < s; ++i) user[i] = *u++;
749 		user[i] = '\0';
750 		u = user;
751 	}
752 
753 	/* Look up the "current" user */
754 	if (u[0] == '\0') u = getlogin();
755 
756 	/* Look up a user (or "current" user) */
757 	if (u) pw = getpwnam(u);
758 	else
759 		pw = getpwuid(getuid());
760 
761 	/* Nothing found? */
762 	if (!pw) return (1);
763 
764 	/* Make use of the info */
765 	(void)strncpy(buf, pw->pw_dir, max - 1);
766 
767 	/* Append the rest of the filename, if any */
768 	if (s) (void)strncat(buf, s, max - 1);
769 
770 	/* Terminate */
771 	buf[max - 1] = '\0';
772 
773 	/* Success */
774 	return (0);
775 }
776 
777 
778 #else  /* SET_UID */
779 
780 
781 /*
782  * Extract a "parsed" path from an initial filename
783  *
784  * This requires no special processing on simple machines,
785  * except for verifying the size of the filename.
786  */
path_parse(char * buf,int max,cptr file)787 errr path_parse(char *buf, int max, cptr file)
788 {
789 	/* Accept the filename */
790 	(void)strnfmt(buf, max, "%s", file);
791 
792 	/* Success */
793 	return (0);
794 }
795 
796 
797 #endif /* SET_UID */
798 
799 
800 #ifndef HAVE_MKSTEMP
801 
802 /*
803  * Hack -- acquire a "temporary" file name if possible
804  *
805  * This filename is always in "system-specific" form.
806  */
path_temp(char * buf,int max)807 static errr path_temp(char *buf, int max)
808 {
809 	cptr s;
810 
811 	/*
812 	 * Temp file
813 	 *
814 	 * If the following line gives you a compile-time warning,
815 	 * then turn on the HAVE_MKSTEMP if you have mkstemp().
816 	 */
817 	s = tmpnam(NULL);
818 
819 	/* Oops */
820 	if (!s) return (-1);
821 
822 	/* Format to length */
823 	(void)strnfmt(buf, max, "%s", s);
824 
825 	/* Success */
826 	return (0);
827 }
828 
829 #endif /* HAVE_MKSTEMP */
830 
831 
832 /*
833  * Create a new path by appending a file (or directory) to a path.
834  *
835  * This requires no special processing on simple machines, except
836  * for verifying the size of the filename, but note the ability to
837  * bypass the given "path" with certain special file-names.
838  *
839  * Note that the "file" may actually be a "sub-path", including
840  * a path and a file.
841  *
842  * Note that this function yields a path which must be "parsed"
843  * using the "parse" function above.
844  */
path_build(char * buf,int max,cptr path,cptr file)845 void path_build(char *buf, int max, cptr path, cptr file)
846 {
847 	/* Special file */
848 	if (file[0] == '~')
849 	{
850 		/* Use the file itself */
851 		(void)strnfmt(buf, max, "%s", file);
852 	}
853 
854 	/* Absolute file, on "normal" systems */
855 	else if (prefix(file, PATH_SEP) && !streq(PATH_SEP, ""))
856 	{
857 		/* Use the file itself */
858 		(void)strnfmt(buf, max, "%s", file);
859 	}
860 
861 	/* No path given */
862 	else if (!path[0])
863 	{
864 		/* Use the file itself */
865 		(void)strnfmt(buf, max, "%s", file);
866 	}
867 
868 	/* Path and File */
869 	else
870 	{
871 		/* Build the new path */
872 		(void)strnfmt(buf, max, "%s%s%s", path, PATH_SEP, file);
873 	}
874 }
875 
876 
877 /*
878  * Hack -- replacement for "fopen()"
879  */
my_fopen(cptr file,cptr mode)880 FILE *my_fopen(cptr file, cptr mode)
881 {
882 	char buf[1024];
883 	FILE *fff;
884 
885 	/* Hack -- Try to parse the path */
886 	if (path_parse(buf, 1024, file)) return (NULL);
887 
888 	/* Attempt to fopen the file anyway */
889 	fff = fopen(buf, mode);
890 
891 #if defined(MAC_MPW) || defined(MACH_O_CARBON)
892 
893 	/* Set file creator and type */
894 	if (fff && strchr(mode, 'w')) fsetfileinfo(buf, _fcreator, _ftype);
895 
896 #endif
897 
898 	/* Return open file or NULL */
899 	return (fff);
900 }
901 
902 
903 /*
904  * Hack -- replacement for "fclose()"
905  */
my_fclose(FILE * fff)906 void my_fclose(FILE *fff)
907 {
908 	/* Require a file */
909 	if (!fff) return;
910 
911 	/* Close, check for error */
912 	(void)fclose(fff);
913 }
914 
915 
916 #endif /* ACORN */
917 
918 
919 #ifdef HAVE_MKSTEMP
920 
my_fopen_temp(char * buf,int max)921 FILE *my_fopen_temp(char *buf, int max)
922 {
923 	int fd;
924 
925 	/* Prepare the buffer for mkstemp */
926 	strncpy(buf, "/tmp/anXXXXXX", max);
927 
928 	/* Secure creation of a temporary file */
929 	fd = mkstemp(buf);
930 
931 	/* Check the file-descriptor */
932 	if (fd < 0) return (NULL);
933 
934 	/* Return a file stream */
935 	return (fdopen(fd, "w"));
936 }
937 
938 #else  /* HAVE_MKSTEMP */
939 
my_fopen_temp(char * buf,int max)940 FILE *my_fopen_temp(char *buf, int max)
941 {
942 	/* Generate a temporary filename */
943 	if (path_temp(buf, max)) return (NULL);
944 
945 	/* Open the file */
946 	return (my_fopen(buf, "w"));
947 }
948 
949 #endif /* HAVE_MKSTEMP */
950 
951 
952 /*
953  * Hack -- replacement for "fgets()"
954  *
955  * Read a string, without a newline, to a file
956  *
957  * Process tabs, strip internal non-printables if told.
958  */
my_fgets_aux(FILE * fff,char * buf,huge n,bool strip)959 static errr my_fgets_aux(FILE *fff, char *buf, huge n, bool strip)
960 {
961 	huge i = 0;
962 
963 	char *s;
964 
965 	char tmp[1024];
966 
967 	/* Read a line */
968 	if (fgets(tmp, 1024, fff))
969 	{
970 		/* Convert weirdness */
971 		for (s = tmp; *s; s++)
972 		{
973 			/* Handle newline */
974 			if (*s == '\n')
975 			{
976 				/* Terminate */
977 				buf[i] = '\0';
978 
979 				/* Success */
980 				return (0);
981 			}
982 
983 			/* Handle tabs */
984 			else if (*s == '\t')
985 			{
986 				/* Hack -- require room */
987 				if (i + 8 >= n) break;
988 
989 				/* Append a space */
990 				buf[i++] = ' ';
991 
992 				/* Append some more spaces */
993 				while (!(i % 8)) buf[i++] = ' ';
994 			}
995 
996 			/* Strip non-printables if asked */
997 			else if(!strip || isprint(*s))
998 			{
999 				/* Copy */
1000 				buf[i++] = *s;
1001 
1002 				/* Check length */
1003 				if (i >= n) break;
1004 			}
1005 		}
1006 	}
1007 
1008 	/* Nothing */
1009 	buf[0] = '\0';
1010 
1011 	/* Failure */
1012 	return (1);
1013 }
1014 
1015 
1016 /*
1017  * Hack -- replacement for "fgets()"
1018  *
1019  * Read a string, without a newline, to a file
1020  *
1021  * Process tabs, strip internal non-printables
1022  */
my_fgets(FILE * fff,char * buf,huge n)1023 errr my_fgets(FILE *fff, char *buf, huge n)
1024 {
1025 	return (my_fgets_aux(fff, buf, n, TRUE));
1026 }
1027 
1028 /*
1029  * Hack -- replacement for "fgets()"
1030  *
1031  * Read a string, without a newline, to a file
1032  *
1033  * Process tabs, do not strip internal non-printables
1034  */
my_raw_fgets(FILE * fff,char * buf,huge n)1035 errr my_raw_fgets(FILE *fff, char *buf, huge n)
1036 {
1037 	return (my_fgets_aux(fff, buf, n, FALSE));
1038 }
1039 
1040 
1041 
1042 #ifdef ACORN
1043 
1044 
1045 /*
1046  * Most of the "file" routines for "ACORN" should be in "main-acn.c"
1047  *
1048  * Many of them can be rewritten now that only "fd_open()" and "fd_make()"
1049  * and "my_fopen()" should ever create files.
1050  */
1051 
1052 
1053 #else  /* ACORN */
1054 
1055 
1056 /*
1057  * Code Warrior is a little weird about some functions
1058  */
1059 #ifdef BEN_HACK
1060 extern int open(const char *, int, ...);
1061 extern int close(int);
1062 extern int read(int, void *, unsigned int);
1063 extern int write(int, const void *, unsigned int);
1064 extern long lseek(int, long, int);
1065 #endif /* BEN_HACK */
1066 
1067 
1068 /*
1069  * The Macintosh is a little bit brain-dead sometimes
1070  */
1071 #ifdef MACINTOSH
1072 # define open(N,F,M) \
1073 ((M), open((char*)(N),F))
1074 # define write(F,B,S) \
1075 write(F,(char*)(B),S)
1076 #endif /* MACINTOSH */
1077 
1078 
1079 /*
1080  * Several systems have no "O_BINARY" flag
1081  */
1082 #ifndef O_BINARY
1083 # define O_BINARY 0
1084 #endif /* O_BINARY */
1085 
1086 
1087 /*
1088  * Hack -- attempt to delete a file
1089  */
fd_kill(cptr file)1090 errr fd_kill(cptr file)
1091 {
1092 	char buf[1024];
1093 
1094 	/* Hack -- Try to parse the path */
1095 	if (path_parse(buf, 1024, file)) return (-1);
1096 
1097 	/* Remove */
1098 	(void)remove(buf);
1099 
1100 	/* Assume success XXX XXX XXX */
1101 	return (0);
1102 }
1103 
1104 
1105 /*
1106  * Hack -- attempt to move a file
1107  */
fd_move(cptr file,cptr what)1108 errr fd_move(cptr file, cptr what)
1109 {
1110 	char buf[1024];
1111 	char aux[1024];
1112 
1113 	/* Hack -- Try to parse the path */
1114 	if (path_parse(buf, 1024, file)) return (-1);
1115 
1116 	/* Hack -- Try to parse the path */
1117 	if (path_parse(aux, 1024, what)) return (-1);
1118 
1119 	/* Rename */
1120 	(void)rename(buf, aux);
1121 
1122 	/* Assume success XXX XXX XXX */
1123 	return (0);
1124 }
1125 
1126 
1127 /*
1128  * Hack -- attempt to open a file descriptor (create file)
1129  *
1130  * This function should fail if the file already exists
1131  *
1132  * Note that we assume that the file should be "binary"
1133  */
fd_make(cptr file,int mode)1134 int fd_make(cptr file, int mode)
1135 {
1136 	char buf[1024];
1137 	int fd;
1138 
1139 	/* Hack -- Try to parse the path */
1140 	if (path_parse(buf, sizeof(buf), file)) return (-1);
1141 
1142 #if defined(MACINTOSH)
1143 
1144 	/* Create the file, fail if exists, write-only, binary */
1145 	fd = open(buf, O_CREAT | O_EXCL | O_WRONLY | O_BINARY);
1146 
1147 #else
1148 
1149 	/* Create the file, fail if exists, write-only, binary */
1150 	fd = open(buf, O_CREAT | O_EXCL | O_WRONLY | O_BINARY, mode);
1151 
1152 #endif
1153 
1154 #if defined(MAC_MPW) || defined(MACH_O_CARBON)
1155 
1156 	/* Set file creator and type */
1157 	if (fd >= 0) fsetfileinfo(buf, _fcreator, _ftype);
1158 
1159 #endif
1160 
1161 	/* Return descriptor */
1162 	return (fd);
1163 }
1164 
1165 
1166 /*
1167  * Hack -- attempt to open a file descriptor (existing file)
1168  *
1169  * Note that we assume that the file should be "binary"
1170  */
fd_open(cptr file,int flags)1171 int fd_open(cptr file, int flags)
1172 {
1173 	char buf[1024];
1174 
1175 	/* Hack -- Try to parse the path */
1176 	if (path_parse(buf, 1024, file)) return (-1);
1177 
1178 	/* Attempt to open the file */
1179 	return (open(buf, flags | O_BINARY, 0));
1180 }
1181 
1182 
1183 /*
1184  * Hack -- attempt to lock a file descriptor
1185  *
1186  * Legal lock types -- F_UNLCK, F_RDLCK, F_WRLCK
1187  */
fd_lock(int fd,int what)1188 errr fd_lock(int fd, int what)
1189 {
1190 	/* XXX XXX */
1191 	what = what ? what : 0;
1192 
1193 	/* Verify the fd */
1194 	if (fd < 0) return (-1);
1195 
1196 #ifdef SET_UID
1197 
1198 # ifdef USG
1199 
1200 #  if defined(F_ULOCK) && defined(F_LOCK)
1201 
1202 	/* Un-Lock */
1203 	if (what == F_UNLCK)
1204 	{
1205 		/* Unlock it, Ignore errors */
1206 		lockf(fd, F_ULOCK, 0);
1207 	}
1208 
1209 	/* Lock */
1210 	else
1211 	{
1212 		/* Lock the score file */
1213 		if (lockf(fd, F_LOCK, 0) != 0) return (1);
1214 	}
1215 
1216 #  endif
1217 
1218 # else
1219 
1220 #  if defined(LOCK_UN) && defined(LOCK_EX)
1221 
1222 	/* Un-Lock */
1223 	if (what == F_UNLCK)
1224 	{
1225 		/* Unlock it, Ignore errors */
1226 		(void)flock(fd, LOCK_UN);
1227 	}
1228 
1229 	/* Lock */
1230 	else
1231 	{
1232 		/* Lock the score file */
1233 		if (flock(fd, LOCK_EX) != 0) return (1);
1234 	}
1235 
1236 #  endif
1237 
1238 # endif
1239 
1240 #endif
1241 
1242 	/* Success */
1243 	return (0);
1244 }
1245 
1246 
1247 /*
1248  * Hack -- attempt to seek on a file descriptor
1249  */
fd_seek(int fd,huge n)1250 errr fd_seek(int fd, huge n)
1251 {
1252 	huge p;
1253 
1254 	/* Verify fd */
1255 	if (fd < 0) return (-1);
1256 
1257 	/* Seek to the given position */
1258 	p = lseek(fd, n, SEEK_SET);
1259 
1260 	/* Failure */
1261 	if (p != n) return (1);
1262 
1263 	/* Success */
1264 	return (0);
1265 }
1266 
1267 
1268 /*
1269  * Hack -- attempt to read data from a file descriptor
1270  */
fd_read(int fd,char * buf,huge n)1271 errr fd_read(int fd, char *buf, huge n)
1272 {
1273 	/* Verify the fd */
1274 	if (fd < 0) return (-1);
1275 
1276 #ifndef SET_UID
1277 
1278 	/* Read pieces */
1279 	while (n >= 16384)
1280 	{
1281 		/* Read a piece */
1282 		if (read(fd, buf, 16384) != 16384) return (1);
1283 
1284 		/* Shorten the task */
1285 		buf += 16384;
1286 
1287 		/* Shorten the task */
1288 		n -= 16384;
1289 	}
1290 
1291 #endif
1292 
1293 	/* Read the final piece */
1294 	if (read(fd, buf, n) != (int)n) return (1);
1295 
1296 	/* Success */
1297 	return (0);
1298 }
1299 
1300 
1301 /*
1302  * Hack -- Attempt to write data to a file descriptor
1303  */
fd_write(int fd,cptr buf,huge n)1304 errr fd_write(int fd, cptr buf, huge n)
1305 {
1306 	/* Verify the fd */
1307 	if (fd < 0) return (-1);
1308 
1309 #ifndef SET_UID
1310 
1311 	/* Write pieces */
1312 	while (n >= 16384)
1313 	{
1314 		/* Write a piece */
1315 		if (write(fd, buf, 16384) != 16384) return (1);
1316 
1317 		/* Shorten the task */
1318 		buf += 16384;
1319 
1320 		/* Shorten the task */
1321 		n -= 16384;
1322 	}
1323 
1324 #endif
1325 
1326 	/* Write the final piece */
1327 	if (write(fd, buf, n) != (int)n) return (1);
1328 
1329 	/* Success */
1330 	return (0);
1331 }
1332 
1333 
1334 /*
1335  * Hack -- attempt to close a file descriptor
1336  */
fd_close(int fd)1337 errr fd_close(int fd)
1338 {
1339 	/* Verify the fd */
1340 	if (fd < 0) return (-1);
1341 
1342 	/* Close */
1343 	(void)close(fd);
1344 
1345 	/* XXX XXX XXX */
1346 	return (0);
1347 }
1348 
1349 
1350 #endif /* ACORN */
1351 
1352 
1353 /* This works by masking off the lowest order set bits one at a time */
count_bits(u32b x)1354 int count_bits(u32b x)
1355 {
1356 	int n = 0;
1357 
1358 	if (x)
1359 	{
1360 		do
1361 		{
1362 			n++;
1363 		}
1364 		while (0 != (x = x & (x - 1)));
1365 	}
1366 
1367 	return (n);
1368 }
1369 
1370 
1371 /*
1372  * Convert a decimal to a single digit hex number
1373  */
hexify(uint i)1374 static char hexify(uint i)
1375 {
1376 	return (hexsym[i % 16]);
1377 }
1378 
1379 
1380 /*
1381  * Convert a hexidecimal-digit into a decimal
1382  */
dehex(char c)1383 static int dehex(char c)
1384 {
1385 	if (isdigit(c)) return (D2I(c));
1386 	if (islower(c)) return (A2I(c) + 10);
1387 	if (isupper(c)) return (A2I(tolower(c)) + 10);
1388 	return (0);
1389 }
1390 
1391 
1392 /*
1393  * Hack -- convert a printable string into real ascii
1394  *
1395  * I have no clue if this function correctly handles, for example,
1396  * parsing "\xFF" into a (signed) char.  Whoever thought of making
1397  * the "sign" of a "char" undefined is a complete moron.  Oh well.
1398  */
text_to_ascii(char * buf,cptr str)1399 void text_to_ascii(char *buf, cptr str)
1400 {
1401 	char *s = buf;
1402 
1403 	/* Analyze the "ascii" string */
1404 	while (*str)
1405 	{
1406 		/* Backslash codes */
1407 		if (*str == '\\')
1408 		{
1409 			/* Skip the backslash */
1410 			str++;
1411 
1412 			/* Hex-mode XXX */
1413 			if (*str == 'x')
1414 			{
1415 				*s = 16 * dehex(*++str);
1416 				*s++ += dehex(*++str);
1417 			}
1418 
1419 			/* Hack -- simple way to specify "backslash" */
1420 			else if (*str == '\\')
1421 			{
1422 				*s++ = '\\';
1423 			}
1424 
1425 			/* Hack -- simple way to specify "caret" */
1426 			else if (*str == '^')
1427 			{
1428 				*s++ = '^';
1429 			}
1430 
1431 			/* Hack -- simple way to specify "space" */
1432 			else if (*str == 's')
1433 			{
1434 				*s++ = ' ';
1435 			}
1436 
1437 			/* Hack -- simple way to specify Escape */
1438 			else if (*str == 'e')
1439 			{
1440 				*s++ = ESCAPE;
1441 			}
1442 
1443 			/* Backspace */
1444 			else if (*str == 'b')
1445 			{
1446 				*s++ = '\b';
1447 			}
1448 
1449 			/* Newline */
1450 			else if (*str == 'n')
1451 			{
1452 				*s++ = '\n';
1453 			}
1454 
1455 			/* Return */
1456 			else if (*str == 'r')
1457 			{
1458 				*s++ = '\r';
1459 			}
1460 
1461 			/* Tab */
1462 			else if (*str == 't')
1463 			{
1464 				*s++ = '\t';
1465 			}
1466 
1467 			/* Skip the final char */
1468 			str++;
1469 		}
1470 
1471 		/* Normal Control codes */
1472 		else if (*str == '^')
1473 		{
1474 			str++;
1475 			*s++ = (*str++ & 037);
1476 		}
1477 
1478 		/* Normal chars */
1479 		else
1480 		{
1481 			*s++ = *str++;
1482 		}
1483 	}
1484 
1485 	/* Terminate */
1486 	*s = '\0';
1487 }
1488 
1489 
1490 /*
1491  * Hack -- convert a string into a printable form
1492  */
ascii_to_text(char * buf,cptr str)1493 void ascii_to_text(char *buf, cptr str)
1494 {
1495 	char *s = buf;
1496 
1497 	/* Analyze the "ascii" string */
1498 	while (*str)
1499 	{
1500 		byte i = (byte)(*str++);
1501 
1502 		if (i == ESCAPE)
1503 		{
1504 			*s++ = '\\';
1505 			*s++ = 'e';
1506 		}
1507 		else if (i == ' ')
1508 		{
1509 			*s++ = '\\';
1510 			*s++ = 's';
1511 		}
1512 		else if (i == '\b')
1513 		{
1514 			*s++ = '\\';
1515 			*s++ = 'b';
1516 		}
1517 		else if (i == '\t')
1518 		{
1519 			*s++ = '\\';
1520 			*s++ = 't';
1521 		}
1522 		else if (i == '\n')
1523 		{
1524 			*s++ = '\\';
1525 			*s++ = 'n';
1526 		}
1527 		else if (i == '\r')
1528 		{
1529 			*s++ = '\\';
1530 			*s++ = 'r';
1531 		}
1532 		else if (i == '^')
1533 		{
1534 			*s++ = '\\';
1535 			*s++ = '^';
1536 		}
1537 		else if (i == '\\')
1538 		{
1539 			*s++ = '\\';
1540 			*s++ = '\\';
1541 		}
1542 		else if (i < 32)
1543 		{
1544 			*s++ = '^';
1545 			*s++ = i + 64;
1546 		}
1547 		else if (i < 127)
1548 		{
1549 			*s++ = i;
1550 		}
1551 		else
1552 		{
1553 			*s++ = '\\';
1554 			*s++ = 'x';
1555 			*s++ = hexify(i / 16);
1556 			*s++ = hexify(i % 16);
1557 		}
1558 	}
1559 
1560 	/* Terminate */
1561 	*s = '\0';
1562 }
1563 
1564 
1565 
1566 /*
1567  * The "macro" package
1568  *
1569  * Functions are provided to manipulate a collection of macros, each
1570  * of which has a trigger pattern string and a resulting action string
1571  * and a small set of flags.
1572  */
1573 
1574 
1575 
1576 /*
1577  * Determine if any macros have ever started with a given character.
1578  */
1579 static bool macro__use[256];
1580 
1581 
1582 /*
1583  * Find the macro (if any) which exactly matches the given pattern
1584  */
macro_find_exact(cptr pat)1585 sint macro_find_exact(cptr pat)
1586 {
1587 	int i;
1588 
1589 	/* Nothing possible */
1590 	if (!macro__use[(byte)(pat[0])])
1591 	{
1592 		return (-1);
1593 	}
1594 
1595 	/* Scan the macros */
1596 	for (i = 0; i < macro__num; ++i)
1597 	{
1598 		/* Skip macros which do not match the pattern */
1599 		if (!streq(macro__pat[i], pat)) continue;
1600 
1601 		/* Found one */
1602 		return (i);
1603 	}
1604 
1605 	/* No matches */
1606 	return (-1);
1607 }
1608 
1609 
1610 /*
1611  * Find the first macro (if any) which contains the given pattern
1612  */
macro_find_check(cptr pat)1613 static sint macro_find_check(cptr pat)
1614 {
1615 	int i;
1616 
1617 	/* Nothing possible */
1618 	if (!macro__use[(byte)(pat[0])])
1619 	{
1620 		return (-1);
1621 	}
1622 
1623 	/* Scan the macros */
1624 	for (i = 0; i < macro__num; ++i)
1625 	{
1626 		/* Skip macros which do not contain the pattern */
1627 		if (!prefix(macro__pat[i], pat)) continue;
1628 
1629 		/* Found one */
1630 		return (i);
1631 	}
1632 
1633 	/* Nothing */
1634 	return (-1);
1635 }
1636 
1637 
1638 /*
1639  * Find the first macro (if any) which contains the given pattern and more
1640  */
macro_find_maybe(cptr pat)1641 static sint macro_find_maybe(cptr pat)
1642 {
1643 	int i;
1644 
1645 	/* Nothing possible */
1646 	if (!macro__use[(byte)(pat[0])])
1647 	{
1648 		return (-1);
1649 	}
1650 
1651 	/* Scan the macros */
1652 	for (i = 0; i < macro__num; ++i)
1653 	{
1654 		/* Skip macros which do not contain the pattern */
1655 		if (!prefix(macro__pat[i], pat)) continue;
1656 
1657 		/* Skip macros which exactly match the pattern XXX XXX */
1658 		if (streq(macro__pat[i], pat)) continue;
1659 
1660 		/* Found one */
1661 		return (i);
1662 	}
1663 
1664 	/* Nothing */
1665 	return (-1);
1666 }
1667 
1668 
1669 /*
1670  * Find the longest macro (if any) which starts with the given pattern
1671  */
macro_find_ready(cptr pat)1672 static sint macro_find_ready(cptr pat)
1673 {
1674 	int i, t, n = -1, s = -1;
1675 
1676 	/* Nothing possible */
1677 	if (!macro__use[(byte)(pat[0])])
1678 	{
1679 		return (-1);
1680 	}
1681 
1682 	/* Scan the macros */
1683 	for (i = 0; i < macro__num; ++i)
1684 	{
1685 		/* Skip macros which are not contained by the pattern */
1686 		if (!prefix(pat, macro__pat[i])) continue;
1687 
1688 		/* Obtain the length of this macro */
1689 		t = strlen(macro__pat[i]);
1690 
1691 		/* Only track the "longest" pattern */
1692 		if ((n >= 0) && (s > t)) continue;
1693 
1694 		/* Track the entry */
1695 		n = i;
1696 		s = t;
1697 	}
1698 
1699 	/* Result */
1700 	return (n);
1701 }
1702 
1703 
1704 /*
1705  * Add a macro definition (or redefinition).
1706  *
1707  * We should use "act == NULL" to "remove" a macro, but this might make it
1708  * impossible to save the "removal" of a macro definition.  XXX XXX XXX
1709  *
1710  * We should consider refusing to allow macros which contain existing macros,
1711  * or which are contained in existing macros, because this would simplify the
1712  * macro analysis code.  XXX XXX XXX
1713  *
1714  * We should consider removing the "command macro" crap, and replacing it
1715  * with some kind of "powerful keymap" ability, but this might make it hard
1716  * to change the "roguelike" option from inside the game.  XXX XXX XXX
1717  */
macro_add(cptr pat,cptr act)1718 void macro_add(cptr pat, cptr act)
1719 {
1720 	int n;
1721 
1722 	/* Paranoia -- require data */
1723 	if (!pat || !act) return;
1724 
1725 
1726 	/* Look for any existing macro */
1727 	n = macro_find_exact(pat);
1728 
1729 	/* Replace existing macro */
1730 	if (n >= 0)
1731 	{
1732 		/* Free the old macro action */
1733 		string_free(macro__act[n]);
1734 	}
1735 
1736 	/* Create a new macro */
1737 	else
1738 	{
1739 		/* Acquire a new index */
1740 		n = macro__num++;
1741 
1742 		/* Save the pattern */
1743 		macro__pat[n] = string_make(pat);
1744 	}
1745 
1746 	/* Save the action */
1747 	macro__act[n] = string_make(act);
1748 
1749 	/* Efficiency */
1750 	macro__use[(byte)(pat[0])] = TRUE;
1751 }
1752 
1753 /* This is never used. */
1754 #if 0
1755 
1756 /*
1757  * Initialize the "macro" package
1758  */
1759 errr macro_init(void)
1760 {
1761 	/* Macro patterns */
1762 	C_MAKE(macro__pat, MACRO_MAX, cptr);
1763 
1764 	/* Macro actions */
1765 	C_MAKE(macro__act, MACRO_MAX, cptr);
1766 
1767 	/* Success */
1768 	return (0);
1769 }
1770 
1771 #endif /* 0 */
1772 
1773 
1774 /*
1775  * Local variable -- we are inside a "macro action"
1776  *
1777  * Do not match any macros until "ascii 30" is found.
1778  */
1779 static bool parse_macro = FALSE;
1780 
1781 /*
1782  * Local variable -- we are inside a "macro trigger"
1783  *
1784  * Strip all keypresses until a low ascii value is found.
1785  */
1786 static bool parse_under = FALSE;
1787 
1788 
1789 /*
1790  * Flush all input chars.  Actually, remember the flush,
1791  * and do a "special flush" before the next "inkey()".
1792  *
1793  * This is not only more efficient, but also necessary to make sure
1794  * that various "inkey()" codes are not "lost" along the way.
1795  */
flush(void)1796 void flush(void)
1797 {
1798 	/* Do it later */
1799 	p_ptr->cmd.inkey_xtra = TRUE;
1800 }
1801 
1802 
1803 /*
1804  * Helper function called only from "inkey()"
1805  *
1806  * This function does almost all of the "macro" processing.
1807  *
1808  * We use the "Term_key_push()" function to handle "failed" macros, as well
1809  * as "extra" keys read in while choosing the proper macro, and also to hold
1810  * the action for the macro, plus a special "ascii 30" character indicating
1811  * that any macro action in progress is complete.  Embedded macros are thus
1812  * illegal, unless a macro action includes an explicit "ascii 30" character,
1813  * which would probably be a massive hack, and might break things.
1814  *
1815  * Only 500 (0+1+2+...+29+30) milliseconds may elapse between each key in
1816  * the macro trigger sequence.  If a key sequence forms the "prefix" of a
1817  * macro trigger, 500 milliseconds must pass before the key sequence is
1818  * known not to be that macro trigger.  XXX XXX XXX
1819  */
inkey_aux(void)1820 static char inkey_aux(void)
1821 {
1822 	int k = 0, n, p = 0, w = 0;
1823 
1824 	char ch;
1825 
1826 	cptr pat, act;
1827 
1828 	char buf[1024];
1829 
1830 
1831 	/* Wait for a keypress */
1832 	(void)(Term_inkey(&ch, TRUE, TRUE));
1833 
1834 
1835 	/* End "macro action" */
1836 	if (ch == 30) parse_macro = FALSE;
1837 
1838 	/* Inside "macro action" */
1839 	if (ch == 30) return (ch);
1840 
1841 	/* Inside "macro action" */
1842 	if (parse_macro) return (ch);
1843 
1844 	/* Inside "macro trigger" */
1845 	if (parse_under) return (ch);
1846 
1847 
1848 	/* Save the first key, advance */
1849 	buf[p++] = ch;
1850 	buf[p] = '\0';
1851 
1852 
1853 	/* Check for possible macro */
1854 	k = macro_find_check(buf);
1855 
1856 	/* No macro pending */
1857 	if (k < 0) return (ch);
1858 
1859 
1860 	/* Wait for a macro, or a timeout */
1861 	while (TRUE)
1862 	{
1863 		/* Check for pending macro */
1864 		k = macro_find_maybe(buf);
1865 
1866 		/* No macro pending */
1867 		if (k < 0) break;
1868 
1869 		/* Check for (and remove) a pending key */
1870 		if (0 == Term_inkey(&ch, FALSE, TRUE))
1871 		{
1872 			/* Append the key */
1873 			buf[p++] = ch;
1874 			buf[p] = '\0';
1875 
1876 			/* Restart wait */
1877 			w = 0;
1878 		}
1879 
1880 		/* No key ready */
1881 		else
1882 		{
1883 			/* Increase "wait" */
1884 			w += 10;
1885 
1886 			/* Excessive delay */
1887 			if (w >= 100) break;
1888 
1889 			/* Delay */
1890 			Term_xtra(TERM_XTRA_DELAY, w);
1891 		}
1892 	}
1893 
1894 
1895 	/* Check for available macro */
1896 	k = macro_find_ready(buf);
1897 
1898 	/* No macro available */
1899 	if (k < 0)
1900 	{
1901 		/* Push all the keys back on the queue */
1902 		while (p > 0)
1903 		{
1904 			/* Push the key, notice over-flow */
1905 			if (Term_key_push(buf[--p])) return (0);
1906 		}
1907 
1908 		/* Wait for (and remove) a pending key */
1909 		(void)Term_inkey(&ch, TRUE, TRUE);
1910 
1911 		/* Return the key */
1912 		return (ch);
1913 	}
1914 
1915 
1916 	/* Get the pattern */
1917 	pat = macro__pat[k];
1918 
1919 	/* Get the length of the pattern */
1920 	n = strlen(pat);
1921 
1922 	/* Push the "extra" keys back on the queue */
1923 	while (p > n)
1924 	{
1925 		/* Push the key, notice over-flow */
1926 		if (Term_key_push(buf[--p])) return (0);
1927 	}
1928 
1929 
1930 	/* Begin "macro action" */
1931 	parse_macro = TRUE;
1932 
1933 	/* Push the "end of macro action" key */
1934 	if (Term_key_push(30)) return (0);
1935 
1936 
1937 	/* Access the macro action */
1938 	act = macro__act[k];
1939 
1940 	/* Get the length of the action */
1941 	n = strlen(act);
1942 
1943 	/* Push the macro "action" onto the key queue */
1944 	while (n > 0)
1945 	{
1946 		/* Push the key, notice over-flow */
1947 		if (Term_key_push(act[--n])) return (0);
1948 	}
1949 
1950 
1951 	/* Hack -- Force "inkey()" to call us again */
1952 	return (0);
1953 }
1954 
1955 
1956 /*
1957  * Mega-Hack -- special "inkey_next" pointer.  XXX XXX XXX
1958  *
1959  * This special pointer allows a sequence of keys to be "inserted" into
1960  * the stream of keys returned by "inkey()".  This key sequence will not
1961  * trigger any macros, and cannot be bypassed by the Borg.  It is used
1962  * in Angband to handle "keymaps".
1963  */
1964 static cptr inkey_next = NULL;
1965 
1966 
1967 #ifdef ALLOW_BORG
1968 
1969 /*
1970  * Mega-Hack -- special "inkey_hack" hook.  XXX XXX XXX
1971  *
1972  * This special function hook allows the "Borg" (see elsewhere) to take
1973  * control of the "inkey()" function, and substitute in fake keypresses.
1974  */
1975 char (*inkey_hack) (int flush_first) = NULL;
1976 
1977 #endif /* ALLOW_BORG */
1978 
1979 
1980 
1981 /*
1982  * Get a keypress from the user.
1983  *
1984  * This function recognizes a few "global parameters".  These are variables
1985  * which, if set to TRUE before calling this function, will have an effect
1986  * on this function, and which are always reset to FALSE by this function
1987  * before this function returns.  Thus they function just like normal
1988  * parameters, except that most calls to this function can ignore them.
1989  *
1990  * If "inkey_xtra" is TRUE, then all pending keypresses will be flushed,
1991  * and any macro processing in progress will be aborted.  This flag is
1992  * set by the "flush()" function, which does not actually flush anything
1993  * itself, but rather, triggers delayed input flushing via "inkey_xtra".
1994  *
1995  * If "inkey_scan" is TRUE, then we will immediately return "zero" if no
1996  * keypress is available, instead of waiting for a keypress.
1997  *
1998  * If "inkey_base" is TRUE, then all macro processing will be bypassed.
1999  * If "inkey_base" and "inkey_scan" are both TRUE, then this function will
2000  * not return immediately, but will wait for a keypress for as long as the
2001  * normal macro matching code would, allowing the direct entry of macro
2002  * triggers.  The "inkey_base" flag is extremely dangerous!
2003  *
2004  * If "inkey_flag" is TRUE, then we will assume that we are waiting for a
2005  * normal command, and we will only show the cursor if "hilite_player" is
2006  * TRUE (or if the player is in a store), instead of always showing the
2007  * cursor.  The various "main-xxx.c" files should avoid saving the game
2008  * in response to a "menu item" request unless "inkey_flag" is TRUE, to
2009  * prevent savefile corruption.
2010  *
2011  * If we are waiting for a keypress, and no keypress is ready, then we will
2012  * refresh (once) the window which was active when this function was called.
2013  *
2014  * Note that "back-quote" is automatically converted into "escape" for
2015  * convenience on machines with no "escape" key.  This is done after the
2016  * macro matching, so the user can still make a macro for "backquote".
2017  *
2018  * Note the special handling of "ascii 30" (ctrl-caret, aka ctrl-shift-six)
2019  * and "ascii 31" (ctrl-underscore, aka ctrl-shift-minus), which are used to
2020  * provide support for simple keyboard "macros".  These keys are so strange
2021  * that their loss as normal keys will probably be noticed by nobody.  The
2022  * "ascii 30" key is used to indicate the "end" of a macro action, which
2023  * allows recursive macros to be avoided.  The "ascii 31" key is used by
2024  * some of the "main-xxx.c" files to introduce macro trigger sequences.
2025  *
2026  * Hack -- we use "ascii 29" (ctrl-right-bracket) as a special "magic" key,
2027  * which can be used to give a variety of "sub-commands" which can be used
2028  * any time.  These sub-commands could include commands to take a picture of
2029  * the current screen, to start/stop recording a macro action, etc.
2030  *
2031  * If "angband_term[0]" is not active, we will make it active during this
2032  * function, so that the various "main-xxx.c" files can assume that input
2033  * is only requested (via "Term_inkey()") when "angband_term[0]" is active.
2034  *
2035  * Mega-Hack -- This function is used as the entry point for clearing the
2036  * "signal_count" variable, and of the "character_saved" variable.
2037  *
2038  * Hack -- Note the use of "inkey_next" to allow "keymaps" to be processed.
2039  *
2040  * Mega-Hack -- Note the use of "inkey_hack" to allow the "Borg" to steal
2041  * control of the keyboard from the user.
2042  */
inkey(void)2043 char inkey(void)
2044 {
2045 	int v;
2046 	char kk;
2047 	char ch = 0;
2048 	bool done = FALSE;
2049 	term *old = Term;
2050 
2051 	/* Hack -- Use the "inkey_next" pointer */
2052 	if (inkey_next && *inkey_next && !p_ptr->cmd.inkey_xtra)
2053 	{
2054 		/* Get next character, and advance */
2055 		ch = *inkey_next++;
2056 
2057 		/* Cancel the various "global parameters" */
2058 		p_ptr->cmd.inkey_base = FALSE;
2059 		p_ptr->cmd.inkey_xtra = FALSE;
2060 		p_ptr->cmd.inkey_flag = FALSE;
2061 		p_ptr->cmd.inkey_scan = FALSE;
2062 
2063 		/* Accept result */
2064 		return (ch);
2065 	}
2066 
2067 	/* Forget pointer */
2068 	inkey_next = NULL;
2069 
2070 
2071 #ifdef ALLOW_BORG
2072 
2073 	/* Mega-Hack -- Use the special hook */
2074 	if (inkey_hack && ((ch = (*inkey_hack) (p_ptr->cmd.inkey_xtra)) != 0))
2075 	{
2076 		/* Cancel the various "global parameters" */
2077 		p_ptr->cmd.inkey_base = FALSE;
2078 		p_ptr->cmd.inkey_xtra = FALSE;
2079 		p_ptr->cmd.inkey_flag = FALSE;
2080 		p_ptr->cmd.inkey_scan = FALSE;
2081 
2082 		/* Accept result */
2083 		return (ch);
2084 	}
2085 
2086 #endif /* ALLOW_BORG */
2087 
2088 	/* Hack -- handle delayed "flush()" */
2089 	if (p_ptr->cmd.inkey_xtra)
2090 	{
2091 		/* End "macro action" */
2092 		parse_macro = FALSE;
2093 
2094 		/* End "macro trigger" */
2095 		parse_under = FALSE;
2096 
2097 		/* Forget old keypresses */
2098 		Term_flush();
2099 	}
2100 
2101 
2102 	/* Access cursor state */
2103 	(void)Term_get_cursor(&v);
2104 
2105 	/* Show the cursor if waiting, except sometimes in "command" mode */
2106 	if (!p_ptr->cmd.inkey_scan &&
2107 		(!p_ptr->cmd.inkey_flag || hilite_player || character_icky))
2108 	{
2109 		/* Show the cursor */
2110 		(void)Term_set_cursor(1);
2111 	}
2112 
2113 
2114 	/* Hack -- Activate main screen */
2115 	Term_activate(angband_term[0]);
2116 
2117 	/* Get a key */
2118 	while (!ch)
2119 	{
2120 		/* Hack -- Handle "inkey_scan" */
2121 		if (!p_ptr->cmd.inkey_base && p_ptr->cmd.inkey_scan &&
2122 			(0 != Term_inkey(&kk, FALSE, FALSE)))
2123 		{
2124 			break;
2125 		}
2126 
2127 
2128 		/* Hack -- Flush output once when no key ready */
2129 		if (!done && (0 != Term_inkey(&kk, FALSE, FALSE)))
2130 		{
2131 			/* Hack -- activate proper term */
2132 			Term_activate(old);
2133 
2134 			/* Flush output */
2135 			Term_fresh();
2136 
2137 			/* Hack -- activate main screen */
2138 			Term_activate(angband_term[0]);
2139 
2140 			/* Mega-Hack -- reset saved flag */
2141 			character_saved = FALSE;
2142 
2143 			/* Mega-Hack -- reset signal counter */
2144 			signal_count = 0;
2145 
2146 			/* Only once */
2147 			done = TRUE;
2148 		}
2149 
2150 
2151 		/* Hack -- Handle "inkey_base" */
2152 		if (p_ptr->cmd.inkey_base)
2153 		{
2154 			int w = 0;
2155 
2156 			/* Wait forever */
2157 			if (!p_ptr->cmd.inkey_scan)
2158 			{
2159 				/* Wait for (and remove) a pending key */
2160 				if (0 == Term_inkey(&ch, TRUE, TRUE))
2161 				{
2162 					/* Done */
2163 					break;
2164 				}
2165 
2166 				/* Oops */
2167 				break;
2168 			}
2169 
2170 			/* Wait */
2171 			while (TRUE)
2172 			{
2173 				/* Check for (and remove) a pending key */
2174 				if (0 == Term_inkey(&ch, FALSE, TRUE))
2175 				{
2176 					/* Done */
2177 					break;
2178 				}
2179 
2180 				/* No key ready */
2181 				else
2182 				{
2183 					/* Increase "wait" */
2184 					w += 10;
2185 
2186 					/* Excessive delay */
2187 					if (w >= 100) break;
2188 
2189 					/* Delay */
2190 					Term_xtra(TERM_XTRA_DELAY, w);
2191 				}
2192 			}
2193 
2194 			/* Done */
2195 			break;
2196 		}
2197 
2198 
2199 		/* Get a key (see above) */
2200 		ch = inkey_aux();
2201 
2202 
2203 		/* Handle "control-right-bracket" */
2204 		if (ch == 29)
2205 		{
2206 			/* Strip this key */
2207 			ch = 0;
2208 
2209 			/* Continue */
2210 			continue;
2211 		}
2212 
2213 
2214 		/* Treat back-quote as escape */
2215 		if (ch == '`') ch = ESCAPE;
2216 
2217 
2218 		/* End "macro trigger" */
2219 		if (parse_under && (ch <= 32))
2220 		{
2221 			/* Strip this key */
2222 			ch = 0;
2223 
2224 			/* End "macro trigger" */
2225 			parse_under = FALSE;
2226 		}
2227 
2228 
2229 		/* Handle "control-caret" */
2230 		if (ch == 30)
2231 		{
2232 			/* Strip this key */
2233 			ch = 0;
2234 		}
2235 
2236 		/* Handle "control-underscore" */
2237 		else if (ch == 31)
2238 		{
2239 			/* Strip this key */
2240 			ch = 0;
2241 
2242 			/* Begin "macro trigger" */
2243 			parse_under = TRUE;
2244 		}
2245 
2246 		/* Inside "macro trigger" */
2247 		else if (parse_under)
2248 		{
2249 			/* Strip this key */
2250 			ch = 0;
2251 		}
2252 	}
2253 
2254 
2255 	/* Hack -- restore the term */
2256 	Term_activate(old);
2257 
2258 
2259 	/* Restore the cursor */
2260 	(void)Term_set_cursor(v);
2261 
2262 
2263 	/* Cancel the various "global parameters" */
2264 	p_ptr->cmd.inkey_base = FALSE;
2265 	p_ptr->cmd.inkey_xtra = FALSE;
2266 	p_ptr->cmd.inkey_flag = FALSE;
2267 	p_ptr->cmd.inkey_scan = FALSE;
2268 
2269 	/* Return the keypress */
2270 	return (ch);
2271 }
2272 
2273 
2274 
2275 /*
2276  * The "quark" package
2277  *
2278  * This package is used to reduce the memory usage of object inscriptions.
2279  *
2280  * We use dynamic string allocation because otherwise it is necessary to
2281  * pre-guess the amount of quark activity.  We limit the total number of
2282  * quarks, but this is much easier to "expand" as needed.  XXX XXX XXX
2283  *
2284  * Two objects with the same inscription will have the same "quark" index.
2285  *
2286  * Some code uses "zero" to indicate the non-existance of a quark.
2287  *
2288  * Note that "quark zero" is NULL and should never be "dereferenced".
2289  *
2290  * ToDo: Automatically resize the array if necessary.
2291  */
2292 
2293 /*
2294  * The number of quarks
2295  */
2296 static s16b quark__num;
2297 
2298 /*
2299  * The pointers to the quarks [QUARK_MAX]
2300  */
2301 static cptr *quark__str;
2302 
2303 /*
2304  * Refcount for Quarks
2305  */
2306 static u16b *quark__ref;
2307 
2308 
2309 /*
2310  * Add a new "quark" to the set of quarks.
2311  */
quark_add(cptr str)2312 s16b quark_add(cptr str)
2313 {
2314 	int i;
2315 	int posn = 0;
2316 
2317 	/* Look for an existing quark */
2318 	for (i = 1; i < quark__num; i++)
2319 	{
2320 		/* Test refcount */
2321 		if (!quark__ref[i]) continue;
2322 
2323 		/* Check for equality */
2324 		if (streq(quark__str[i], str))
2325 		{
2326 			/* Increase refcount */
2327 			quark__ref[i]++;
2328 
2329 			return (i);
2330 		}
2331 	}
2332 
2333 	/* Look for an empty quark */
2334 	for (i = 1; i < quark__num; i++)
2335 	{
2336 		if (!quark__ref[i])
2337 		{
2338 			posn = i;
2339 			break;
2340 		}
2341 	}
2342 
2343 	/* Did we fail to find room? */
2344 	if (!posn)
2345 	{
2346 		/* Paranoia -- Require room */
2347 		if (quark__num == QUARK_MAX)
2348 		{
2349 			/* Paranoia - no room? */
2350 			return (0);
2351 		}
2352 		else
2353 		{
2354 			/* Use new quark */
2355 			posn = quark__num;
2356 
2357 			/* New maximal quark */
2358 			quark__num++;
2359 		}
2360 	}
2361 
2362 	/* Add a new quark */
2363 	quark__str[posn] = string_make(str);
2364 
2365 	/* One use of this quark */
2366 	quark__ref[posn] = 1;
2367 
2368 	/* Return the index */
2369 	return (posn);
2370 }
2371 
2372 /*
2373  * Like quark_add(), but take a format string.
2374  */
quark_fmt(cptr str,...)2375 s16b quark_fmt(cptr str, ...)
2376 {
2377 	va_list vp;
2378 
2379 	char buf[1024];
2380 
2381 	/* Begin the Varargs Stuff */
2382 	va_start(vp, str);
2383 
2384 	/* Format the args, save the length */
2385 	(void)vstrnfmt(buf, 1024, str, &vp);
2386 
2387 	/* End the Varargs Stuff */
2388 	va_end(vp);
2389 
2390 	/* Quark stuff */
2391 	return (quark_add(buf));
2392 }
2393 
2394 
2395 /*
2396  * Remove the quark
2397  */
quark_remove(s16b * i)2398 void quark_remove(s16b *i)
2399 {
2400 	/* Only need to remove real quarks */
2401 	if (!(*i)) return;
2402 
2403 	/* Verify */
2404 	if ((*i < 0) || (*i >= quark__num)) return;
2405 
2406 	/* Decrease refcount */
2407 	quark__ref[*i]--;
2408 
2409 	/* Deallocate? */
2410 	if (!quark__ref[*i])
2411 	{
2412 		string_free(quark__str[*i]);
2413 		quark__str[*i] = NULL;
2414 	}
2415 
2416 	/* No longer have a quark here */
2417 	*i = 0;
2418 }
2419 
2420 /*
2421  * Duplicate a quark
2422  */
quark_dup(s16b i)2423 void quark_dup(s16b i)
2424 {
2425 	/* Verify */
2426 	if ((i < 0) || (i >= quark__num)) return;
2427 
2428 	/* Paranoia */
2429 	if (!quark__ref[i]) return;
2430 
2431 	/* Increase refcount */
2432 	quark__ref[i]++;
2433 }
2434 
2435 
2436 /*
2437  * This function looks up a quark
2438  */
quark_str(s16b i)2439 cptr quark_str(s16b i)
2440 {
2441 	cptr q;
2442 
2443 	/* Verify */
2444 	if ((i < 0) || (i >= quark__num)) return (NULL);
2445 
2446 	/* Get the quark */
2447 	q = quark__str[i];
2448 
2449 	/* Return the quark */
2450 	return (q);
2451 }
2452 
2453 
2454 /*
2455  * Initialize the "quark" package
2456  */
quarks_init(void)2457 errr quarks_init(void)
2458 {
2459 	/* Quark variables */
2460 	C_MAKE(quark__str, QUARK_MAX, cptr);
2461 	C_MAKE(quark__ref, QUARK_MAX, u16b);
2462 
2463 	quark__num = 1;
2464 
2465 	/* Success */
2466 	return (0);
2467 }
2468 
2469 
2470 /*
2471  * Free the entire "quark" package
2472  */
quarks_free(void)2473 errr quarks_free(void)
2474 {
2475 	int i;
2476 
2477 	/* Free the "quarks" */
2478 	for (i = 1; i < quark__num; i++)
2479 	{
2480 		/* Paranoia - only try to free existing quarks */
2481 		if (quark__str[i])
2482 		{
2483 			string_free(quark__str[i]);
2484 		}
2485 	}
2486 
2487 	/* Free the list of "quarks" */
2488 	FREE((void *)quark__str);
2489 	FREE((void *)quark__ref);
2490 
2491 	/* Success */
2492 	return (0);
2493 }
2494 
2495 
2496 /*
2497  * The "message memorization" package.
2498  *
2499  * Each call to "message_add(s)" will add a new "most recent" message
2500  * to the "message recall list", using the contents of the string "s".
2501  *
2502  * The number of memorized messages is available as "message_num()".
2503  *
2504  * Old messages can be retrieved by "message_str(age)", where the "age"
2505  * of the most recently memorized message is zero, and the oldest "age"
2506  * which is available is "message_num() - 1".  Messages outside this
2507  * range are returned as the empty string.
2508  *
2509  * The messages are stored in a special manner that maximizes "efficiency",
2510  * that is, we attempt to maximize the number of semi-sequential messages
2511  * that can be retrieved, given a limited amount of storage space, without
2512  * causing the memorization of new messages or the recall of old messages
2513  * to be too expensive.
2514  *
2515  * We keep a buffer of chars to hold the "text" of the messages, more or
2516  * less in the order they were memorized, and an array of offsets into that
2517  * buffer, representing the actual messages, but we allow the "text" to be
2518  * "shared" by two messages with "similar" ages, as long as we never cause
2519  * sharing to reach too far back in the the buffer.
2520  *
2521  * The implementation is complicated by the fact that both the array of
2522  * offsets, and the buffer itself, are both treated as "circular arrays"
2523  * for efficiency purposes, but the strings may not be "broken" across
2524  * the ends of the array.
2525  *
2526  * When we want to memorize a new message, we attempt to "reuse" the buffer
2527  * space by checking for message duplication within the recent messages.
2528  *
2529  * Otherwise, if we need more buffer space, we grab a full quarter of the
2530  * total buffer space at a time, to keep the reclamation code efficient.
2531  *
2532  * The "message_add()" function is rather "complex", because it must be
2533  * extremely efficient, both in space and time, for use with the Borg.
2534  */
2535 
2536 
2537 /*
2538  * The next "free" index to use
2539  */
2540 static u16b message__next;
2541 
2542 /*
2543  * The index of the oldest message (none yet)
2544  */
2545 static u16b message__last;
2546 
2547 /*
2548  * The next "free" offset
2549  */
2550 static u16b message__head;
2551 
2552 /*
2553  * The offset to the oldest used char (none yet)
2554  */
2555 static u16b message__tail;
2556 
2557 /*
2558  * The array[MESSAGE_MAX] of offsets, by index
2559  */
2560 static u16b *message__ptr;
2561 
2562 /*
2563  * The array[MESSAGE_BUF] of chars, by offset
2564  */
2565 static char *message__buf;
2566 
2567 /*
2568  * The array[MESSAGE_MAX] of u16b for the types of messages
2569  */
2570 static u16b *message__type;
2571 
2572 
2573 /*
2574  * Table of colors associated to message-types
2575  */
2576 static byte message__color[MSG_MAX];
2577 
2578 /*
2579  * Wrapper function to get values out of message__color
2580  */
get_msg_type_color(byte a)2581 byte get_msg_type_color(byte a)
2582 {
2583 	/* Paranoia */
2584 	if (a >= MSG_MAX) return TERM_WHITE;
2585 
2586 	/* Return the color */
2587 	return (message__color[(int)a]);
2588 }
2589 
2590 
2591 /*
2592  * How many messages are "available"?
2593  */
message_num(void)2594 s16b message_num(void)
2595 {
2596 	/* Determine how many messages are "available" */
2597 	return (message__next + MESSAGE_MAX - message__last) % MESSAGE_MAX;
2598 }
2599 
2600 
2601 
2602 /*
2603  * Recall the "text" of a saved message
2604  */
message_str(s16b age)2605 cptr message_str(s16b age)
2606 {
2607 	s16b x;
2608 	s16b o;
2609 	cptr s;
2610 
2611 	/* Forgotten messages have no text */
2612 	if ((age < 0) || (age >= message_num())) return ("");
2613 
2614 	/* Get the "logical" index */
2615 	x = (message__next + MESSAGE_MAX - (age + 1)) % MESSAGE_MAX;
2616 
2617 	/* Get the "offset" for the message */
2618 	o = message__ptr[x];
2619 
2620 	/* Get the message text */
2621 	s = &message__buf[o];
2622 
2623 	/* Return the message text */
2624 	return (s);
2625 }
2626 
2627 
2628 /*
2629  * Recall the "type" of a saved message
2630  */
message_type(s16b age)2631 u16b message_type(s16b age)
2632 {
2633 	s16b x;
2634 
2635 	/* Forgotten messages have no special color */
2636 	if ((age < 0) || (age >= message_num())) return (TERM_WHITE);
2637 
2638 	/* Get the "logical" index */
2639 	x = (message__next + MESSAGE_MAX - (age + 1)) % MESSAGE_MAX;
2640 
2641 	/* Return the message type */
2642 	return (message__type[x]);
2643 }
2644 
2645 
2646 /*
2647  * Recall the "color" of a saved message
2648  */
message_color(s16b age)2649 byte message_color(s16b age)
2650 {
2651 	return message__color[message_type(age)];
2652 }
2653 
2654 
message_color_define(u16b type,byte color)2655 errr message_color_define(u16b type, byte color)
2656 {
2657 	/* Ignore illegal types */
2658 	if (type >= MSG_MAX) return (1);
2659 
2660 	/* Store the color */
2661 	message__color[type] = color % 16;
2662 
2663 	/* Success */
2664 	return (0);
2665 }
2666 
2667 
2668 /*
2669  * Add a new message, with great efficiency
2670  *
2671  * We must ignore long messages to prevent internal overflow, since we
2672  * assume that we can always get enough space by advancing "message__tail"
2673  * by one quarter the total buffer space.
2674  *
2675  * We must not attempt to optimize using a message index or buffer space
2676  * which is "far away" from the most recent entries, or we will lose a lot
2677  * of messages when we "expire" the old message index and/or buffer space.
2678  *
2679  * We attempt to minimize the use of "string compare" operations in this
2680  * function, because they are expensive when used in mass quantities.
2681  */
message_add(cptr str,u16b type)2682 void message_add(cptr str, u16b type)
2683 {
2684 	int m, n, k, i, x, o;
2685 
2686 	char w[1024];
2687 
2688 	cptr u;
2689 	char *v;
2690 
2691 
2692 	/*** Step 1 -- Analyze the message ***/
2693 
2694 	/* Hack -- Ignore "non-messages" */
2695 	if (!str) return;
2696 
2697 	/* Message length */
2698 	n = strlen(str);
2699 
2700 	/* Hack -- Ignore "long" messages */
2701 	if (n >= MESSAGE_BUF / 4) return;
2702 
2703 
2704 	/*** Step 2 -- Attempt to optimize ***/
2705 
2706 	/* Limit number of messages to check */
2707 	m = message_num();
2708 
2709 	/* Limit number of messages to check */
2710 	k = m / 4;
2711 
2712 	/* Limit number of messages to check */
2713 	if (k > 32) k = 32;
2714 
2715 	/* Check previous message */
2716 	for (i = message__next; m; m--)
2717 	{
2718 		int j = 1;
2719 
2720 		char buf[1024];
2721 		char *t;
2722 
2723 		cptr old;
2724 
2725 		/* Back up, wrap if needed */
2726 		if (i-- == 0) i = MESSAGE_MAX - 1;
2727 
2728 		/* Index */
2729 		o = message__ptr[i];
2730 
2731 		/* Get the old string */
2732 		old = &message__buf[o];
2733 
2734 		/* Skip small messages */
2735 		if (!old) continue;
2736 
2737 		strcpy(buf, old);
2738 
2739 		/* Find multiple */
2740 		for (t = buf; *t && (*t != '<'); t++) ;
2741 
2742 		if (*t)
2743 		{
2744 			/* Message is too small */
2745 			if (strlen(buf) < 6) break;
2746 
2747 			/* Drop the space */
2748 			*(t - 1) = '\0';
2749 
2750 			/* Get multiplier */
2751 			j = atoi(++t);
2752 		}
2753 
2754 		/* Limit the multiplier to 1000 */
2755 		if (buf && streq(buf, str) && (j < 1000))
2756 		{
2757 			j++;
2758 
2759 			/* Overwrite */
2760 			message__next = i;
2761 
2762 			str = w;
2763 
2764 			/* Write it out */
2765 			strnfmt(w, 1024, "%s <%dx>", buf, j);
2766 
2767 			/* Message length */
2768 			n = strlen(str);
2769 		}
2770 
2771 		/* Done */
2772 		break;
2773 	}
2774 
2775 	/* Start just after the most recent message */
2776 	i = message__next;
2777 
2778 	/* Check the last few messages for duplication */
2779 	for (; k; k--)
2780 	{
2781 		u16b q;
2782 
2783 		cptr old;
2784 
2785 		/* Back up, wrap if needed */
2786 		if (i-- == 0) i = MESSAGE_MAX - 1;
2787 
2788 		/* Stop before oldest message */
2789 		if (i == message__last) break;
2790 
2791 		/* Index */
2792 		o = message__ptr[i];
2793 
2794 		/* Extract "distance" from "head" */
2795 		q = (message__head + MESSAGE_BUF - o) % MESSAGE_BUF;
2796 
2797 		/* Do not optimize over large distances */
2798 		if (q >= MESSAGE_BUF / 4) continue;
2799 
2800 		/* Get the old string */
2801 		old = &message__buf[o];
2802 
2803 		/* Compare */
2804 		if (!streq(old, str)) continue;
2805 
2806 		/* Get the next available message index */
2807 		x = message__next;
2808 
2809 		/* Advance 'message__next', wrap if needed */
2810 		if (++message__next == MESSAGE_MAX) message__next = 0;
2811 
2812 		/* Kill last message if needed */
2813 		if (message__next == message__last)
2814 		{
2815 			/* Advance 'message__last', wrap if needed */
2816 			if (++message__last == MESSAGE_MAX) message__last = 0;
2817 		}
2818 
2819 		/* Assign the starting address */
2820 		message__ptr[x] = message__ptr[i];
2821 
2822 		/* Store the message type */
2823 		message__type[x] = type;
2824 
2825 		/* Success */
2826 		return;
2827 	}
2828 
2829 
2830 	/*** Step 3 -- Ensure space before end of buffer ***/
2831 
2832 	/* Kill messages, and wrap, if needed */
2833 	if (message__head + (n + 1) >= MESSAGE_BUF)
2834 	{
2835 		/* Kill all "dead" messages */
2836 		for (i = message__last; TRUE; i++)
2837 		{
2838 			/* Wrap if needed */
2839 			if (i == MESSAGE_MAX) i = 0;
2840 
2841 			/* Stop before the new message */
2842 			if (i == message__next) break;
2843 
2844 			/* Get offset */
2845 			o = message__ptr[i];
2846 
2847 			/* Kill "dead" messages */
2848 			if (o >= message__head)
2849 			{
2850 				/* Track oldest message */
2851 				message__last = i + 1;
2852 			}
2853 		}
2854 
2855 		/* Wrap "tail" if needed */
2856 		if (message__tail >= message__head) message__tail = 0;
2857 
2858 		/* Start over */
2859 		message__head = 0;
2860 	}
2861 
2862 
2863 	/*** Step 4 -- Ensure space for actual characters ***/
2864 
2865 	/* Kill messages, if needed */
2866 	if (message__head + (n + 1) > message__tail)
2867 	{
2868 		/* Advance to new "tail" location */
2869 		message__tail += (MESSAGE_BUF / 4);
2870 
2871 		/* Kill all "dead" messages */
2872 		for (i = message__last; TRUE; i++)
2873 		{
2874 			/* Wrap if needed */
2875 			if (i == MESSAGE_MAX) i = 0;
2876 
2877 			/* Stop before the new message */
2878 			if (i == message__next) break;
2879 
2880 			/* Get offset */
2881 			o = message__ptr[i];
2882 
2883 			/* Kill "dead" messages */
2884 			if ((o >= message__head) && (o < message__tail))
2885 			{
2886 				/* Track oldest message */
2887 				message__last = i + 1;
2888 			}
2889 		}
2890 	}
2891 
2892 
2893 	/*** Step 5 -- Grab a new message index ***/
2894 
2895 	/* Get the next available message index */
2896 	x = message__next;
2897 
2898 	/* Advance 'message__next', wrap if needed */
2899 	if (++message__next == MESSAGE_MAX) message__next = 0;
2900 
2901 	/* Kill last message if needed */
2902 	if (message__next == message__last)
2903 	{
2904 		/* Advance 'message__last', wrap if needed */
2905 		if (++message__last == MESSAGE_MAX) message__last = 0;
2906 	}
2907 
2908 
2909 	/*** Step 6 -- Insert the message text ***/
2910 
2911 	/* Assign the starting address */
2912 	message__ptr[x] = message__head;
2913 
2914 	/* Inline 'strcpy(message__buf + message__head, str)' */
2915 	v = message__buf + message__head;
2916 	for (u = str; *u;) *v++ = *u++;
2917 	*v = '\0';
2918 
2919 	/* Advance the "head" pointer */
2920 	message__head += (n + 1);
2921 
2922 	/* Store the message type */
2923 	message__type[x] = type;
2924 }
2925 
2926 
2927 /*
2928  * Initialize the "message" package
2929  */
messages_init(void)2930 errr messages_init(void)
2931 {
2932 	/* Message variables */
2933 	C_MAKE(message__ptr, MESSAGE_MAX, u16b);
2934 	C_MAKE(message__buf, MESSAGE_BUF, char);
2935 	C_MAKE(message__type, MESSAGE_MAX, u16b);
2936 
2937 	/* Init the message colors to white */
2938 	(void)C_BSET(message__color, TERM_WHITE, MSG_MAX, byte);
2939 
2940 	/* Hack -- No messages yet */
2941 	message__tail = MESSAGE_BUF;
2942 
2943 	/* Success */
2944 	return (0);
2945 }
2946 
2947 
2948 /*
2949  * Free the "message" package
2950  */
messages_free(void)2951 void messages_free(void)
2952 {
2953 	/* Free the messages */
2954 	FREE(message__ptr);
2955 	FREE(message__buf);
2956 	FREE(message__type);
2957 }
2958 
2959 
2960 /*
2961  * Hack -- flush
2962  */
msg_flush(int x)2963 static void msg_flush(int x)
2964 {
2965 	if (!p_ptr->state.skip_more && !auto_more)
2966 	{
2967 		/* Pause for response */
2968 		prtf(x, 0, CLR_L_BLUE "-more-");
2969 
2970 		/* Get an acceptable keypress */
2971 		while (1)
2972 		{
2973 			int cmd = inkey();
2974 
2975 			if (cmd == ESCAPE)
2976 			{
2977 				/* Skip all the prompt until player's turn */
2978 				p_ptr->state.skip_more = TRUE;
2979 				break;
2980 			}
2981 
2982 			if (quick_messages) break;
2983 			if (cmd == ' ') break;
2984 			if ((cmd == '\n') || (cmd == '\r')) break;
2985 			bell("Illegal response to a 'more' prompt!");
2986 		}
2987 	}
2988 
2989 	/* Clear the line */
2990 	Term_erase(0, 0, 255);
2991 }
2992 
2993 
2994 static int message_column = 0;
2995 
2996 
2997 /*
2998  * Output a message to the top line of the screen.
2999  *
3000  * Break long messages into multiple pieces (40-72 chars).
3001  *
3002  * Allow multiple short messages to "share" the top line.
3003  *
3004  * Prompt the user to make sure he has a chance to read them.
3005  *
3006  * These messages are memorized for later reference (see above).
3007  *
3008  * We could do a "Term_fresh()" to provide "flicker" if needed.
3009  *
3010  * The global "msg_flag" variable can be cleared to tell us to "erase" any
3011  * "pending" messages still on the screen, instead of using "msg_flush()".
3012  * This should only be done when the user is known to have read the message.
3013  *
3014  * We must be very careful about using the "msgf()" functions without
3015  * explicitly calling the special "message_flush()" function, since this may
3016  * result in the loss of information if the screen is cleared, or if anything
3017  * is displayed on the top line.
3018  *
3019  * Hack -- Note that "msgf(NULL)" will clear the top line
3020  * even if no messages are pending.  This is probably a hack.
3021  */
msg_print_aux(u16b type,cptr msg)3022 static void msg_print_aux(u16b type, cptr msg)
3023 {
3024 	int n;
3025 	char *t;
3026 	char buf[1024];
3027 	byte color = TERM_WHITE;
3028 
3029 
3030 	/* Hack -- fake monochrome */
3031 	if (!use_color) type = MSG_GENERIC;
3032 
3033 	/* Hack -- Reset */
3034 	if (!msg_flag) message_column = 0;
3035 
3036 	/* Message Length */
3037 	n = (msg ? strlen(msg) : 0);
3038 
3039 	/* Hack -- flush when requested or needed */
3040 	if (message_column && (!msg || ((message_column + n) > 72)))
3041 	{
3042 		/* Flush */
3043 		msg_flush(message_column);
3044 
3045 		/* Forget it */
3046 		msg_flag = FALSE;
3047 
3048 		/* Reset */
3049 		message_column = 0;
3050 	}
3051 
3052 
3053 	/* No message */
3054 	if (!msg) return;
3055 
3056 	/* Paranoia */
3057 	if (n > 1000) return;
3058 
3059 
3060 	/* Memorize the message (if legal) */
3061 	if (character_generated && !(p_ptr->state.is_dead))
3062 		message_add(msg, type);
3063 
3064 	/* Window stuff */
3065 	p_ptr->window |= (PW_MESSAGE);
3066 
3067 	/* Copy it */
3068 	strcpy(buf, msg);
3069 
3070 	/* Analyze the buffer */
3071 	t = buf;
3072 
3073 	/* Get the color of the message (if legal) */
3074 	if (message__color)
3075 		color = message__color[type];
3076 
3077 	/* HACK -- no "black" messages */
3078 	if (color == TERM_DARK) color = TERM_WHITE;
3079 
3080 	/* Split message */
3081 	while (n > 72)
3082 	{
3083 		char oops;
3084 
3085 		int check, split;
3086 
3087 		/* Default split */
3088 		split = 72;
3089 
3090 		/* Find the "best" split point */
3091 		for (check = 40; check < 72; check++)
3092 		{
3093 			/* Found a valid split point */
3094 			if (t[check] == ' ') split = check;
3095 		}
3096 
3097 		/* Save the split character */
3098 		oops = t[split];
3099 
3100 		/* Split the message */
3101 		t[split] = '\0';
3102 
3103 		/* Display part of the message */
3104 		prtf(0, 0, "%s%s", color_seq[color], t);
3105 
3106 		/* Flush it */
3107 		msg_flush(split + 1);
3108 
3109 		/* Restore the split character */
3110 		t[split] = oops;
3111 
3112 		/* Insert a space */
3113 		t[--split] = ' ';
3114 
3115 		/* Prepare to recurse on the rest of "buf" */
3116 		t += split;
3117 		n -= split;
3118 	}
3119 
3120 	/* Display the tail of the message */
3121 	prtf(message_column, 0, "%s%s", color_seq[color], t);
3122 
3123 	/* Remember the message */
3124 	msg_flag = TRUE;
3125 
3126 	/* Remember the position */
3127 	message_column += n + 1;
3128 
3129 	/* Optional refresh */
3130 	if (fresh_after) Term_fresh();
3131 }
3132 
3133 
3134 static u16b current_message_type = MSG_GENERIC;
3135 
3136 /*
3137  * Change the message type, and parse the following
3138  * format string.  See defines.h for the macro this
3139  * is used in.
3140  */
set_message_type(char * buf,uint max,cptr fmt,va_list * vp)3141 void set_message_type(char *buf, uint max, cptr fmt, va_list *vp)
3142 {
3143 	cptr str;
3144 
3145 	/* Unused parameter */
3146 	(void)fmt;
3147 
3148     /* Get the argument - and set the message type */
3149 	current_message_type = va_arg(*vp, int);
3150 
3151 	/* Get the string to format with. */
3152 	str = va_arg(*vp, cptr);
3153 
3154 	/* Expand the string */
3155 	vstrnfmt(buf, max, str, vp);
3156 }
3157 
3158 /*
3159  * Display a formatted message, using "vstrnfmt()" and "msg_print()".
3160  */
msgf(cptr fmt,...)3161 void msgf(cptr fmt, ...)
3162 {
3163 	va_list vp;
3164 
3165 	int i;
3166 
3167 	char buf[1024];
3168 
3169 	/* Set the message type */
3170 	current_message_type = MSG_GENERIC;
3171 
3172 	/* Begin the Varargs Stuff */
3173 	va_start(vp, fmt);
3174 
3175 	/* Format the args, save the length */
3176 	(void)vstrnfmt(buf, 1024, fmt, &vp);
3177 
3178 	/* End the Varargs Stuff */
3179 	va_end(vp);
3180 
3181 	sound(current_message_type);
3182 
3183 	/* Clean the string of '\n' characters */
3184 	for (i = 0; buf[i]; i++)
3185 	{
3186 		/* Erase carriage returns */
3187 		if (buf[i] == '\n') buf[i] = ' ';
3188 	}
3189 
3190 	/* Display */
3191 	msg_print_aux(current_message_type, buf);
3192 }
3193 
3194 /*
3195  * Process a message effect
3196  *
3197  * (The "extra" parameter is currently unused)
3198  */
msg_effect(u16b type,s16b extra)3199 void msg_effect(u16b type, s16b extra)
3200 {
3201 	/* Unused parameters */
3202 	(void)type;
3203 	(void)extra;
3204 }
3205 
3206 
3207 /*
3208  * Print the queued messages.
3209  */
message_flush(void)3210 void message_flush(void)
3211 {
3212 	/* Hack -- Reset */
3213 	if (!msg_flag) message_column = 0;
3214 
3215 	/* Flush when needed */
3216 	if (message_column)
3217 	{
3218 		/* Print pending messages */
3219 		msg_flush(message_column);
3220 
3221 		/* Forget it */
3222 		msg_flag = FALSE;
3223 
3224 		/* Reset */
3225 		message_column = 0;
3226 	}
3227 }
3228 
3229 
3230 /*
3231  * Check a char for "vowel-hood"
3232  */
is_a_vowel(int ch)3233 bool is_a_vowel(int ch)
3234 {
3235 	switch (ch)
3236 	{
3237 		case 'a':
3238 		case 'e':
3239 		case 'i':
3240 		case 'o':
3241 		case 'u':
3242 		case 'A':
3243 		case 'E':
3244 		case 'I':
3245 		case 'O':
3246 		case 'U':
3247 			return (TRUE);
3248 	}
3249 
3250 	return (FALSE);
3251 }
3252 
3253 
3254 /*
3255  * Hack -- special buffer to hold the action of the current keymap
3256  */
3257 static char request_command_buffer[256];
3258 
3259 
3260 
3261 /*
3262  * Request a command from the user.
3263  *
3264  * Sets p_ptr->cmd.cmd, p_ptr->cmd.dir, p_ptr->cmd.rep,
3265  * p_ptr->cmd.arg.  May modify p_ptr->cmd.new.
3266  *
3267  * Note that "caret" ("^") is treated specially, and is used to
3268  * allow manual input of control characters.  This can be used
3269  * on many machines to request repeated tunneling (Ctrl-H) and
3270  * on the Macintosh to request "Control-Caret".
3271  *
3272  * Note that "backslash" is treated specially, and is used to bypass any
3273  * keymap entry for the following character.  This is useful for macros.
3274  *
3275  * Note that this command is used both in the dungeon and in
3276  * stores, and must be careful to work in both situations.
3277  *
3278  * Note that "p_ptr->cmd.new" may not work any more.  XXX XXX XXX
3279  */
request_command(int shopping)3280 void request_command(int shopping)
3281 {
3282 	int i;
3283 
3284 	char cmd;
3285 
3286 	int mode;
3287 
3288 	cptr act;
3289 
3290 
3291 	/* Roguelike */
3292 	if (rogue_like_commands)
3293 	{
3294 		mode = KEYMAP_MODE_ROGUE;
3295 	}
3296 
3297 	/* Original */
3298 	else
3299 	{
3300 		mode = KEYMAP_MODE_ORIG;
3301 	}
3302 
3303 
3304 	/* No command yet */
3305 	p_ptr->cmd.cmd = 0;
3306 
3307 	/* No "argument" yet */
3308 	p_ptr->cmd.arg = 0;
3309 
3310 	/* No "direction" yet */
3311 	p_ptr->cmd.dir = 0;
3312 
3313 
3314 	/* Get command */
3315 	while (1)
3316 	{
3317 		/* Hack -- auto-commands */
3318 		if (p_ptr->cmd.new)
3319 		{
3320 			/* Flush messages */
3321 			message_flush();
3322 
3323 			/* Use auto-command */
3324 			cmd = (char)p_ptr->cmd.new;
3325 
3326 			/* Forget it */
3327 			p_ptr->cmd.new = 0;
3328 		}
3329 
3330 		/* Get a keypress in "command" mode */
3331 		else
3332 		{
3333 			/* Hack -- no flush needed */
3334 			msg_flag = FALSE;
3335 
3336 			/* Reset the skip_more flag */
3337 			p_ptr->state.skip_more = FALSE;
3338 
3339 			/* Activate "command mode" */
3340 			p_ptr->cmd.inkey_flag = TRUE;
3341 
3342 			/* Get a command */
3343 			cmd = inkey();
3344 		}
3345 
3346 		/* Clear top line */
3347 		clear_msg();
3348 
3349 
3350 		/* Command Count */
3351 		if (cmd == '0')
3352 		{
3353 			int old_arg = p_ptr->cmd.arg;
3354 
3355 			/* Reset */
3356 			p_ptr->cmd.arg = 0;
3357 
3358 			/* Begin the input */
3359 			prtf(0, 0, "Count: ");
3360 
3361 			/* Get a command count */
3362 			while (1)
3363 			{
3364 				/* Get a new keypress */
3365 				cmd = inkey();
3366 
3367 				/* Simple editing (delete or backspace) */
3368 				if ((cmd == 0x7F) || (cmd == KTRL('H')))
3369 				{
3370 					/* Delete a digit */
3371 					p_ptr->cmd.arg = p_ptr->cmd.arg / 10;
3372 
3373 					/* Show current count */
3374 					prtf(0, 0, "Count: %d", p_ptr->cmd.arg);
3375 				}
3376 
3377 				/* Actual numeric data */
3378 				else if (cmd >= '0' && cmd <= '9')
3379 				{
3380 					/* Stop count at 9999 */
3381 					if (p_ptr->cmd.arg >= 1000)
3382 					{
3383 						/* Warn */
3384 						bell("Invalid repeat count!");
3385 
3386 						/* Limit */
3387 						p_ptr->cmd.arg = 9999;
3388 					}
3389 
3390 					/* Increase count */
3391 					else
3392 					{
3393 						/* Incorporate that digit */
3394 						p_ptr->cmd.arg = p_ptr->cmd.arg * 10 + D2I(cmd);
3395 					}
3396 
3397 					/* Show current count */
3398 					prtf(0, 0, "Count: %d", p_ptr->cmd.arg);
3399 				}
3400 
3401 				/* Exit on "unusable" input */
3402 				else
3403 				{
3404 					break;
3405 				}
3406 			}
3407 
3408 			/* Hack -- Handle "zero" */
3409 			if (p_ptr->cmd.arg == 0)
3410 			{
3411 				/* Default to 99 */
3412 				p_ptr->cmd.arg = 99;
3413 
3414 				/* Show current count */
3415 				prtf(0, 0, "Count: %d", p_ptr->cmd.arg);
3416 			}
3417 
3418 			/* Hack -- Handle "old_arg" */
3419 			if (old_arg != 0)
3420 			{
3421 				/* Restore old_arg */
3422 				p_ptr->cmd.arg = old_arg;
3423 
3424 				/* Show current count */
3425 				prtf(0, 0, "Count: %d", p_ptr->cmd.arg);
3426 			}
3427 
3428 			/* Hack -- white-space means "enter command now" */
3429 			if ((cmd == ' ') || (cmd == '\n') || (cmd == '\r'))
3430 			{
3431 				/* Get a real command */
3432 				if (!get_com("Command: ", &cmd))
3433 				{
3434 					/* Clear count */
3435 					p_ptr->cmd.arg = 0;
3436 
3437 					/* Continue */
3438 					continue;
3439 				}
3440 			}
3441 		}
3442 
3443 
3444 		/* Allow "keymaps" to be bypassed */
3445 		if (cmd == '\\')
3446 		{
3447 			/* Get a real command */
3448 			(void)get_com("Command: ", &cmd);
3449 
3450 			/* Hack -- bypass keymaps */
3451 			if (!inkey_next) inkey_next = "";
3452 		}
3453 
3454 
3455 		/* Allow "control chars" to be entered */
3456 		if (cmd == '^')
3457 		{
3458 			/* Get a new command and controlify it */
3459 			if (get_com("Control: ", &cmd)) cmd = KTRL(cmd);
3460 		}
3461 
3462 
3463 		/* Look up applicable keymap */
3464 		act = keymap_act[mode][(byte)(cmd)];
3465 
3466 		/* Apply keymap if not inside a keymap already */
3467 		if (act && !inkey_next)
3468 		{
3469 			/* Install the keymap (limited buffer size) */
3470 			(void)strnfmt(request_command_buffer, 256, "%s", act);
3471 
3472 			/* Start using the buffer */
3473 			inkey_next = request_command_buffer;
3474 
3475 			/* Continue */
3476 			continue;
3477 		}
3478 
3479 
3480 		/* Paranoia */
3481 		if (!cmd) continue;
3482 
3483 
3484 		/* Use command */
3485 		p_ptr->cmd.cmd = cmd;
3486 
3487 		/* Done */
3488 		break;
3489 	}
3490 
3491 	/* Hack -- Auto-repeat certain commands */
3492 	if (p_ptr->cmd.arg <= 0)
3493 	{
3494 		/* Hack -- auto repeat certain commands */
3495 		if (strchr("TBDoc+", p_ptr->cmd.cmd))
3496 		{
3497 			/* Repeat 99 times */
3498 			p_ptr->cmd.arg = 99;
3499 		}
3500 	}
3501 
3502 	/* Shopping */
3503 	if (shopping == 1)
3504 	{
3505 		/* Convert */
3506 		switch (p_ptr->cmd.cmd)
3507 		{
3508 				/* Command "p" -> "purchase" (get) */
3509 			case 'p': p_ptr->cmd.cmd = 'g';
3510 				break;
3511 
3512 				/* Command "m" -> "purchase" (get) */
3513 			case 'm': p_ptr->cmd.cmd = 'g';
3514 				break;
3515 
3516 				/* Command "s" -> "sell" (drop) */
3517 			case 's': p_ptr->cmd.cmd = 'd';
3518 				break;
3519 		}
3520 	}
3521 
3522 	/* Hack -- Scan equipment */
3523 	for (i = 0; i < EQUIP_MAX; i++)
3524 	{
3525 		cptr s;
3526 
3527 		object_type *o_ptr = &p_ptr->equipment[i];
3528 
3529 		/* Skip non-objects */
3530 		if (!o_ptr->k_idx) continue;
3531 
3532 		/* No inscription */
3533 		if (!o_ptr->inscription) continue;
3534 
3535 		/* Obtain the inscription */
3536 		s = quark_str(o_ptr->inscription);
3537 
3538 		/* Find a '^' */
3539 		s = strchr(s, '^');
3540 
3541 		/* Process preventions */
3542 		while (s)
3543 		{
3544 			/* Check the "restriction" character */
3545 			if ((s[1] == p_ptr->cmd.cmd) || (s[1] == '*'))
3546 			{
3547 				/* Hack -- Verify command */
3548 				if (!get_check("Are you sure? "))
3549 				{
3550 					/* Hack -- Use space */
3551 					p_ptr->cmd.cmd = ' ';
3552 				}
3553 			}
3554 
3555 			/* Find another '^' */
3556 			s = strchr(s + 1, '^');
3557 		}
3558 	}
3559 
3560 
3561 	/* Hack -- erase the message line. */
3562 	clear_msg();
3563 }
3564 
3565 
3566 #define REPEAT_MAX		20
3567 
3568 /* Number of chars saved */
3569 static int repeat__cnt = 0;
3570 
3571 /* Current index */
3572 static int repeat__idx = 0;
3573 
3574 /* Saved "stuff" */
3575 static int repeat__key[REPEAT_MAX];
3576 
3577 
repeat_push(int what)3578 void repeat_push(int what)
3579 {
3580 	/* Too many keys */
3581 	if (repeat__cnt == REPEAT_MAX) return;
3582 
3583 	/* Push the "stuff" */
3584 	repeat__key[repeat__cnt++] = what;
3585 
3586 	/* Prevents us from pulling keys */
3587 	++repeat__idx;
3588 }
3589 
repeat_clear(void)3590 void repeat_clear(void)
3591 {
3592 	/* Start over from the failed pull */
3593 	if (repeat__idx)
3594 	{
3595 		/* Decrease the number of characters */
3596 		--repeat__idx;
3597 	}
3598 
3599 	/* Set the counter */
3600 	repeat__cnt = repeat__idx;
3601 }
3602 
3603 
repeat_pull(int * what)3604 bool repeat_pull(int *what)
3605 {
3606 	/* All out of keys */
3607 	if (repeat__idx == repeat__cnt) return (FALSE);
3608 
3609 	/* Grab the next key, advance */
3610 	*what = repeat__key[repeat__idx++];
3611 
3612 	/* Success */
3613 	return (TRUE);
3614 }
3615 
repeat_check(void)3616 void repeat_check(void)
3617 {
3618 	int what;
3619 
3620 	/* Ignore some commands */
3621 	if (p_ptr->cmd.cmd == ESCAPE) return;
3622 	if (p_ptr->cmd.cmd == ' ') return;
3623 	if (p_ptr->cmd.cmd == '\r') return;
3624 	if (p_ptr->cmd.cmd == '\n') return;
3625 
3626 	/* Repeat Last Command */
3627 	if (p_ptr->cmd.cmd == 'n')
3628 	{
3629 		/* Reset */
3630 		repeat__idx = 0;
3631 
3632 		/* Get the command */
3633 		if (repeat_pull(&what))
3634 		{
3635 			/* Save the command */
3636 			p_ptr->cmd.cmd = what;
3637 		}
3638 	}
3639 
3640 	/* Start saving new command */
3641 	else
3642 	{
3643 		/* Reset */
3644 		repeat__cnt = 0;
3645 		repeat__idx = 0;
3646 
3647 		what = p_ptr->cmd.cmd;
3648 
3649 		/* Save this command */
3650 		repeat_push(what);
3651 	}
3652 }
3653