tmsim

A fast turing machine simulator with graphviz export functionality

git clone https://git.8pit.net/tmsim.git

  1/*
  2 * Copyright © 2016-2019 Sören Tempel
  3 *
  4 * This program is free software: you can redistribute it and/or
  5 * modify it under the terms of the GNU Affero General Public
  6 * License as published by the Free Software Foundation, either
  7 * version 3 of the License, or (at your option) any later version.
  8 *
  9 * This program is distributed in the hope that it will be useful,
 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 12 * Affero General Public License for more details.
 13 *
 14 * You should have received a copy of the GNU Affero General Public
 15 * License along with this program. If not, see
 16 * <http://www.gnu.org/licenses/>.
 17 */
 18
 19#include <assert.h>
 20#include <errno.h>
 21#include <fcntl.h>
 22#include <pthread.h>
 23#include <semaphore.h>
 24#include <stdio.h>
 25#include <stdlib.h>
 26#include <string.h>
 27#include <unistd.h>
 28
 29#include <sys/mman.h>
 30#include <sys/stat.h>
 31#include <sys/types.h>
 32
 33#include "util.h"
 34
 35/**
 36 * Returns a string which 'marks' the given position in the given
 37 * string. Meaning it returns a string which is excactly as long as the
 38 * first 'pos' characters of the given string when printed to a terminal
 39 * (tab characters will be retained. The last character (ignoring the
 40 * null byte) of this returned string is '^' and will thus point to the
 41 * character at 'pos' in the original string if the two string are
 42 * printed to a terminal seperated by a newline character.
 43 *
 44 * @pre The length of the given string must be equal or greater than pos.
 45 * @param pos Position of the character in the given string the
 46 * 	returned string should point to. The first element is at
 47 * 	position 0 not at position 1.
 48 * @param str String to create marker for.
 49 * @returns Marker for the given string as described above.
 50 */
 51char *
 52mark(size_t pos, char *str)
 53{
 54	size_t i;
 55	char *res;
 56
 57	/* +2 for \0 and ^ character */
 58	res = emalloc(pos + 2);
 59
 60	for (i = 0; i < pos; i++) {
 61		if ((res[i] = str[i]) != '\t')
 62			res[i] = ' ';
 63	}
 64
 65	res[pos] = '^';
 66	res[++pos] = '\0';
 67
 68	return res;
 69}
 70
 71/**
 72 * Returns the content of the line with the given line number.
 73 *
 74 * @param str Input string containing line.
 75 * @param line Line number.
 76 * @return A pointer to a dynamically allocated string containing
 77 * 	the content of the given line if it exists. NULL if it doesn't.
 78 */
 79char *
 80linenum(char *str, unsigned int line)
 81{
 82	size_t len;
 83	char ch, *res;
 84	size_t end, prev, newline;
 85
 86	if (line < 1)
 87		return NULL;
 88	len = strlen(str);
 89
 90	newline = prev = 0;
 91	for (end = 0; end <= len; end++) {
 92		ch = str[end];
 93		if (ch != '\n' && ch != '\0')
 94			continue;
 95
 96		if (--line == 0) {
 97			prev = newline++;
 98			break;
 99		} else {
100			newline = end + 1;
101		}
102	}
103
104	if (line != 0)
105		return NULL;
106
107	len = end - prev;
108	if (len == 0) /* \n\0 */
109		return NULL;
110
111	res = estrndup(&str[prev], len);
112	return res;
113}
114
115/**
116 * Returns the index of the last character in the given line.
117 *
118 * @param line Line for which last character should be returned.
119 * @return Index of the last character in the given line.
120 */
121size_t
122endofline(char *line)
123{
124	char *pos;
125
126	if (!(pos = strchr(line, '\n')))
127		pos = strchr(line, '\0');
128	assert(pos != NULL);
129
130	assert(pos - line >= 0);
131	return (size_t)(pos - line);
132}
133
134/**
135 * Compares the first 'n' bytes of two strings (like strncmp(3)).
136 * However, unlike strncmp(3) it returns the position of the
137 * first character that differed.
138 *
139 * @param s1 First string to use for comparison.
140 * @param s2 Second string to use for comparison.
141 * @param n Amount of bytes to compare.
142 * @param res Pointer to an address where the position of
143 * 	the first character that differed should be stored.
144 * 	The first element is located at position 0.
145 * @returns An integer less than, equal to, or greater than
146 * 	zero if s1 (or the first n bytes thereof) is found,
147 *	respectively, to be less than, to match, or be greater than s2.
148 */
149int
150xstrncmp(char *s1, char *s2, size_t n, size_t *res)
151{
152	size_t i;
153
154	i = 1;
155	while (*s1 && *s1 == *s2 && i < n) {
156		s1++;
157		s2++;
158		i++;
159	}
160
161	*res = --i;
162	return *s1 - *s2;
163}
164
165/**
166 * Maps the given file to memory and returns the address of the mapped
167 * memory location. If the file is empty no mapping is performed and
168 * zero is returned.
169 *
170 * @param dest Memory location to which the address of the mapping is
171 * 	written. It is not initialized if zero is returned.
172 * @param fp Path to file which should be read.
173 * @returns Length of the mapped memory location or zero if no mapping
174 * 	has been performed. If an error occured while trying to read the
175 * 	given file NULL is returned and errno is set to indicate the error.
176 */
177ssize_t
178readfile(char **dest, char *fp)
179{
180	int fd;
181	off_t len;
182	struct stat st;
183
184	if ((fd = open(fp, O_RDONLY)) == -1)
185		return -1;
186	if (fstat(fd, &st))
187		return -1;
188	if ((len = st.st_size) <= 0)
189		return 0;
190
191	*dest = mmap(NULL, (size_t)len, PROT_READ, MAP_PRIVATE, fd, 0);
192	if (*dest == MAP_FAILED)
193		return -1;
194
195	close(fd);
196	return len;
197}
198
199/**
200 * Calls estrndup(3) but terminates the program EXIT_FAILURE if strndup returned
201 * an error.
202 *
203 * @param s Pointer to a string which should be duplicated.
204 * @param n Amount of bytes to copy.
205 * @returns Pointer to a new string which is a duplicate of the given one.
206 */
207char *
208estrndup(char *s, size_t n)
209{
210	char *r;
211
212	if (!(r = strndup(s, n)))
213		die("strndup failed");
214
215	return r;
216}
217
218/**
219 * Calls malloc(3) but terminates the program with EXIT_FAILURE if malloc
220 * returned an error.
221 *
222 * @param size Amount of memory (in bytes) which should be allocated.
223 * @returns Pointer to the allocated memory.
224 */
225void *
226emalloc(size_t size)
227{
228	void *r;
229
230	if (!(r = malloc(size)))
231		die("malloc failed");
232
233	return r;
234}
235
236/**
237 * Calls realloc(3) but terminates the program with EXIT_FAILURE if realloc
238 * returned an error.
239 *
240 * @param size Amount of memory (in bytes) which should be allocated.
241 * @returns Pointer to the allocated memory.
242 */
243void *
244erealloc(void *ptr, size_t size)
245{
246	void *r;
247
248	if (!(r = realloc(ptr, size)))
249		die("realloc failed");
250
251	return r;
252}
253
254/**
255 * Calls pthread_mutex_lock(3) but terminates the program with
256 * EXIT_FAILURE if pthread_mutex_lock returned an error.
257 *
258 * @param lock Spin lock which should be locked.
259 */
260void
261pthread_mutex_elock(pthread_mutex_t *lock)
262{
263	if ((errno = pthread_mutex_lock(lock)))
264		die("pthread_mutex_lock failed");
265}
266
267/**
268 * Calls pthread_mutex_unlock(3) but terminates the program with
269 * EXIT_FAILURE if pthread_mutex_unlock returned an error.
270 *
271 * @param lock Spin lock which should be unlocked.
272 */
273void
274pthread_mutex_eunlock(pthread_mutex_t *lock)
275{
276	if ((errno = pthread_mutex_unlock(lock)))
277		die("pthread_mutex_unlock failed");
278}
279
280/**
281 * Calls sem_wait(3) but terminates the program with EXIT_FAILURE if sem_wait
282 * returned an error.
283 *
284 * @param sem Semaphore to call sem_wait on.
285 */
286void
287sem_ewait(sem_t *sem)
288{
289	if (sem_wait(sem))
290		die("sem_wait failed");
291}
292
293/**
294 * Calls sem_post(3) but terminates the program with EXIT_FAILURE if sem_post
295 * returned an error.
296 *
297 * @param sem Semaphore to call sem_post on.
298 */
299void
300sem_epost(sem_t *sem)
301{
302	if (sem_post(sem))
303		die("sem_post failed");
304}