comparison interps/cfunge/cfunge-src/src/input.c @ 996:859f9b4339e6

<Gregor> tar xf egobot.tar.xz
author HackBot
date Sun, 09 Dec 2012 19:30:08 +0000
parents
children
comparison
equal deleted inserted replaced
995:6883f5911eb7 996:859f9b4339e6
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 }