996
|
1 /* -*- mode: C; coding: utf-8; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*-
|
|
2 *
|
|
3 * cfunge - A standard-conforming Befunge93/98/109 interpreter in C.
|
|
4 * Copyright (C) 2008-2009 Arvid Norlander <anmaster AT tele2 DOT se>
|
|
5 *
|
|
6 * This program is free software: you can redistribute it and/or modify
|
|
7 * it under the terms of the GNU General Public License as published by
|
|
8 * the Free Software Foundation, either version 3 of the License, or
|
|
9 * (at the proxy's option) any later version. Arvid Norlander is a
|
|
10 * proxy who can decide which future versions of the GNU General Public
|
|
11 * License can be used.
|
|
12 *
|
|
13 * This program is distributed in the hope that it will be useful,
|
|
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
16 * GNU General Public License for more details.
|
|
17 *
|
|
18 * You should have received a copy of the GNU General Public License
|
|
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
20 */
|
|
21
|
|
22 #include "global.h"
|
|
23 #include "input.h"
|
|
24
|
|
25 #include <assert.h>
|
|
26 #include <ctype.h> /* isdigit, isxdigit */
|
|
27 #include <stdbool.h>
|
|
28 #include <stddef.h> /* ptrdiff_t */
|
|
29 #include <stdlib.h>
|
|
30
|
|
31 // We use static buffer for input to save input
|
|
32 // from one read to the next if there was any
|
|
33 // left.
|
|
34 static char* lastline = NULL;
|
|
35 static size_t linelength = 0;
|
|
36 // Pointer to how far we consumed the current line.
|
|
37 static char* lastline_current = NULL;
|
|
38
|
|
39 FUNGE_ATTR_FAST FUNGE_ATTR_WARN_UNUSED
|
|
40 static inline bool get_line(void)
|
|
41 {
|
|
42 if (!lastline || !lastline_current || (*lastline_current == '\0')) {
|
|
43 ssize_t retval;
|
|
44 fflush(stdout);
|
|
45 retval = cf_getline(&lastline, &linelength, stdin);
|
|
46 if (retval == -1)
|
|
47 return false;
|
|
48 lastline_current = lastline;
|
|
49 }
|
|
50 return true;
|
|
51 }
|
|
52 FUNGE_ATTR_FAST static inline void discard_line(void)
|
|
53 {
|
|
54 if (lastline != NULL)
|
|
55 cf_free(lastline);
|
|
56 lastline = NULL;
|
|
57 lastline_current = NULL;
|
|
58 }
|
|
59
|
|
60
|
|
61 FUNGE_ATTR_FAST bool input_getchar(funge_cell * restrict chr)
|
|
62 {
|
|
63 unsigned char tmp;
|
|
64 if (!get_line())
|
|
65 return false;
|
|
66 tmp = *((unsigned char*)lastline_current);
|
|
67 lastline_current++;
|
|
68 if (lastline_current && (*lastline_current == '\0'))
|
|
69 discard_line();
|
|
70 *chr = (funge_cell)tmp;
|
|
71 return true;
|
|
72 }
|
|
73
|
|
74 FUNGE_ATTR_FAST bool input_getline(unsigned char ** str)
|
|
75 {
|
|
76 unsigned char * tmp;
|
|
77 if (!get_line())
|
|
78 return false;
|
|
79 tmp = (unsigned char*)cf_strdup(lastline_current);
|
|
80 *str = tmp;
|
|
81 discard_line();
|
|
82 return true;
|
|
83 }
|
|
84
|
|
85
|
|
86 static const char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
|
|
87
|
|
88 // Start of s as if it is in base.
|
|
89 // Unlike strtoll this does not clamp on overflow but stop reading just before
|
|
90 // a overflow would happen.
|
|
91 // Converted value is returned in *value.
|
|
92 // Return value is last index used in string.
|
|
93 FUNGE_ATTR_FAST FUNGE_ATTR_NONNULL FUNGE_ATTR_WARN_UNUSED
|
|
94 static inline ptrdiff_t parse_int(const char * restrict s,
|
|
95 funge_cell * restrict value, funge_cell base)
|
|
96 {
|
|
97 funge_cell result = 0;
|
|
98 size_t i;
|
|
99 size_t length = strlen(s);
|
|
100
|
|
101 assert(s != NULL);
|
|
102 assert(value != NULL);
|
|
103
|
|
104 for (i = 0; i <= length; i++) {
|
|
105 // Will it overflow?
|
|
106 if (result > (FUNGECELL_MAX / base)) {
|
|
107 break;
|
|
108 } else {
|
|
109 funge_cell tmp;
|
|
110 char c = s[i];
|
|
111 // Pointer into digits.
|
|
112 const char * p = strchr(digits, c);
|
|
113 // Still a digit?
|
|
114 if (!p || ((p - digits) >= (ptrdiff_t)base))
|
|
115 break;
|
|
116
|
|
117 tmp = (funge_cell)(p - digits);
|
|
118 // Break if it will overflow!
|
|
119 if ((result * base) > (FUNGECELL_MAX - tmp))
|
|
120 break;
|
|
121 result = (result * base) + tmp;
|
|
122 }
|
|
123 }
|
|
124 *value = result;
|
|
125 return (ptrdiff_t)i;
|
|
126 }
|
|
127
|
|
128 // Note, no need to optimise really, this is user input
|
|
129 // bound anyway.
|
|
130 FUNGE_ATTR_FAST ret_getint input_getint(funge_cell * restrict value, int base)
|
|
131 {
|
|
132 bool found = false;
|
|
133 char * endptr = NULL;
|
|
134 assert(value != NULL);
|
|
135
|
|
136 if (!get_line())
|
|
137 return rgi_eof;
|
|
138 // Find first char that is a number, then convert number.
|
|
139 do {
|
|
140 if (base == 10) {
|
|
141 if (!isdigit(*lastline_current))
|
|
142 continue;
|
|
143 } else if (base == 16) {
|
|
144 if (!isxdigit(*lastline_current))
|
|
145 continue;
|
|
146 } else {
|
|
147 const char * p = strchr(digits, *lastline_current);
|
|
148 if (!p || ((p - digits) >= (ptrdiff_t)base))
|
|
149 continue;
|
|
150 }
|
|
151 found = true;
|
|
152 // Ok, we found it, lets convert it.
|
|
153 endptr = lastline_current + parse_int(lastline_current, value,
|
|
154 (funge_cell)base);
|
|
155 break;
|
|
156 } while (*(lastline_current++) != '\0');
|
|
157 // Discard rest of line if it is just newline, otherwise keep it.
|
|
158 if (endptr && ((*endptr == '\n') || (*endptr == '\r') || (*endptr == '\0')))
|
|
159 discard_line();
|
|
160 else
|
|
161 lastline_current = endptr;
|
|
162 return found ? rgi_success : rgi_noint;
|
|
163 }
|