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