1 // Copyright 2020 Michael Reilly (mreilly@resiliware.com).
2 //
3 // Redistribution and use in source and binary forms, with or without
4 // modification, are permitted provided that the following conditions
5 // are met:
6 // 1. Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer.
8 // 2. Redistributions in binary form must reproduce the above copyright
9 // notice, this list of conditions and the following disclaimer in the
10 // documentation and/or other materials provided with the distribution.
11 // 3. Neither the names of the copyright holders nor the names of the
12 // contributors may be used to endorse or promote products derived from
13 // this software without specific prior written permission.
14 //
15 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 // ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
18 // PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
19 // OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27 #define SRCNAME "hexpeek_console.c"
28
29 #include <hexpeek.h>
30
31 #include <stdlib.h>
32 #include <stdarg.h>
33 #include <string.h>
34 #include <errno.h>
35
36 #ifdef HEXPEEK_EDITABLE_CONSOLE
37
38 #include <histedit.h>
39
40 static EditLine *ConsoleInfo = NULL;
41
42 static History *ConsoleHistory = NULL;
43
consolePrompt(void * UNUSED)44 static char *consolePrompt(void *UNUSED)
45 {
46 return PromptString;
47 }
48
49 #endif
50
consoleInit()51 void consoleInit()
52 {
53 #ifdef HEXPEEK_EDITABLE_CONSOLE
54 if(Params.editable_console)
55 {
56 HistEvent UNUSED;
57
58 ConsoleHistory = history_init();
59 assert(ConsoleHistory);
60 assert(history(ConsoleHistory, &UNUSED, H_SETSIZE, 64) >= 0);
61 assert(history(ConsoleHistory, &UNUSED, H_SETUNIQUE, 1) >= 0);
62
63 ConsoleInfo = el_init(PRGNM, stdin, stdout, stderr);
64 assert(ConsoleInfo);
65 assert(el_set(ConsoleInfo, EL_EDITOR, "emacs") == 0);
66 assert(el_set(ConsoleInfo, EL_EDITMODE, 1) == 0);
67 assert(el_set(ConsoleInfo, EL_PROMPT, consolePrompt) == 0);
68 assert(el_set(ConsoleInfo, EL_SIGNAL, 1) == 0);
69 assert(el_set(ConsoleInfo, EL_HIST, history, ConsoleHistory) == 0);
70
71 el_source(ConsoleInfo, NULL); // @!!IGNORE_RESULT
72 int editable = 0;
73 if(el_get(ConsoleInfo, EL_EDITMODE, &editable) == 0 && editable == 0)
74 consoleClose();
75 else
76 assert(atexit(consoleClose) == 0);
77 }
78 #endif
79 }
80
consoleClose()81 void consoleClose()
82 {
83 #ifdef HEXPEEK_EDITABLE_CONSOLE
84 if(ConsoleInfo)
85 {
86 el_end(ConsoleInfo);
87 ConsoleInfo = NULL;
88 }
89 if(ConsoleHistory)
90 {
91 history_end(ConsoleHistory);
92 ConsoleHistory = NULL;
93 }
94 #endif
95 }
96
consoleFlush()97 void consoleFlush()
98 {
99 assert(fflush(stdout) == 0);
100 }
101
consoleIn()102 char *consoleIn()
103 {
104 if(interactive())
105 {
106 #ifdef HEXPEEK_EDITABLE_CONSOLE
107 if(ConsoleInfo)
108 {
109 HistEvent UNUSED;
110 int count = 0;
111 char const *result = el_gets(ConsoleInfo, &count);
112 if(result == NULL || count <= 0)
113 return NULL;
114 assert(history(ConsoleHistory, &UNUSED, H_ENTER, result) >= 0);
115 // Copy result because el_gets() returns a const string
116 count++; // for '\0'
117 if(LnInputSz < count)
118 {
119 if(LnInput_mal)
120 free(LnInput_mal);
121 assert(LnInputSz > 0);
122 for( ; LnInputSz < count; LnInputSz *= 2);
123 LnInput_mal = Malloc(LnInputSz * sizeof *LnInput_mal);
124 }
125 assert(LnInput_mal);
126 strncpy(LnInput_mal, result, LnInputSz);
127 LnInput_mal[LnInputSz - 1] = '\0';
128 return LnInput_mal;
129 }
130 #endif
131 console("%s", PromptString);
132 consoleFlush();
133 }
134
135 if(getline(&LnInput_mal, &LnInputSz, stdin) <= 0)
136 return NULL;
137 return LnInput_mal;
138 }
139
140 #define VARIADIC_PRINTF(s, f) \
141 va_list vl; \
142 va_start(vl, (s)); \
143 if(vprintf((f), vl) < 0) \
144 { \
145 prerr("error writing output, aborting\n"); \
146 terminate(RC_CRIT); \
147 } \
148 va_end(vl);
149
150 #define VARIADIC_EXTRA_PRINTF(f) \
151 char changed[strlen((f)) + 1]; \
152 hexpeek_genf(changed, (f)); \
153 VARIADIC_PRINTF((f), changed);
154
155 // Handles ordinary format strings, but not the PRI_hxpkint format identifier
console(char const * format,...)156 void console(char const *format, ...)
157 {
158 VARIADIC_PRINTF(format, format);
159 }
160
161 // Out must be at least as long (including '\0') as in
hexpeek_genf(char * out,char const * in)162 void hexpeek_genf(char *out, char const *in)
163 {
164 char *cur = out;
165 char const *last = in;
166 for(char const *ptr = in;
167 (ptr = strstr(ptr, PRI_hxpkint)) != NULL;
168 last = ptr)
169 {
170 strncpy(cur, last, ptr - last);
171 cur += (ptr - last);
172 *(cur++) = (Params.hexlower ? 'x' : 'X');
173 ptr += strlen(PRI_hxpkint);
174 }
175 strcpy(cur, last);
176 }
177
178 // This function can handle any format string, but it is only used for strings
179 // requiring processing of PRI_hxpkint due to the performance degradation
180 // resulting from the copying of the format string.
consoleOutf(char const * format,...)181 void consoleOutf(char const *format, ...)
182 {
183 VARIADIC_EXTRA_PRINTF(format);
184 }
185
186 // Prompt the user for a response
consoleAsk(char const * format,...)187 int consoleAsk(char const *format, ...)
188 {
189 VARIADIC_EXTRA_PRINTF(format);
190 console(" ('y' or 'n')? ");
191 consoleFlush();
192 if(interactive())
193 {
194 if(getline(&LnInput_mal, &LnInputSz, stdin) <= 0)
195 return 1;
196 stripTrailingSpaces(LnInput_mal);
197 return strcmp(LnInput_mal, "y");
198 }
199 return 1;
200 }
201