1/*2 * Copyright © 2016-2019 Sören Tempel3 *4 * This program is free software: you can redistribute it and/or5 * modify it under the terms of the GNU Affero General Public6 * License as published by the Free Software Foundation, either7 * 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 of11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU12 * Affero General Public License for more details.13 *14 * You should have received a copy of the GNU Affero General Public15 * License along with this program. If not, see16 * <http://www.gnu.org/licenses/>.17 */1819#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>2829#include <sys/mman.h>30#include <sys/stat.h>31#include <sys/types.h>3233#include "util.h"3435/**36 * Returns a string which 'marks' the given position in the given37 * string. Meaning it returns a string which is excactly as long as the38 * first 'pos' characters of the given string when printed to a terminal39 * (tab characters will be retained. The last character (ignoring the40 * null byte) of this returned string is '^' and will thus point to the41 * character at 'pos' in the original string if the two string are42 * 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 the46 * returned string should point to. The first element is at47 * 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;5657 /* +2 for \0 and ^ character */58 res = emalloc(pos + 2);5960 for (i = 0; i < pos; i++) {61 if ((res[i] = str[i]) != '\t')62 res[i] = ' ';63 }6465 res[pos] = '^';66 res[++pos] = '\0';6768 return res;69}7071/**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 containing77 * 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;8586 if (line < 1)87 return NULL;88 len = strlen(str);8990 newline = prev = 0;91 for (end = 0; end <= len; end++) {92 ch = str[end];93 if (ch != '\n' && ch != '\0')94 continue;9596 if (--line == 0) {97 prev = newline++;98 break;99 } else {100 newline = end + 1;101 }102 }103104 if (line != 0)105 return NULL;106107 len = end - prev;108 if (len == 0) /* \n\0 */109 return NULL;110111 res = estrndup(&str[prev], len);112 return res;113}114115/**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_t122endofline(char *line)123{124 char *pos;125126 if (!(pos = strchr(line, '\n')))127 pos = strchr(line, '\0');128 assert(pos != NULL);129130 assert(pos - line >= 0);131 return (size_t)(pos - line);132}133134/**135 * Compares the first 'n' bytes of two strings (like strncmp(3)).136 * However, unlike strncmp(3) it returns the position of the137 * 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 of143 * 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 than146 * 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 */149int150xstrncmp(char *s1, char *s2, size_t n, size_t *res)151{152 size_t i;153154 i = 1;155 while (*s1 && *s1 == *s2 && i < n) {156 s1++;157 s2++;158 i++;159 }160161 *res = --i;162 return *s1 - *s2;163}164165/**166 * Maps the given file to memory and returns the address of the mapped167 * memory location. If the file is empty no mapping is performed and168 * zero is returned.169 *170 * @param dest Memory location to which the address of the mapping is171 * 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 mapping174 * has been performed. If an error occured while trying to read the175 * given file NULL is returned and errno is set to indicate the error.176 */177ssize_t178readfile(char **dest, char *fp)179{180 int fd;181 off_t len;182 struct stat st;183184 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;190191 *dest = mmap(NULL, (size_t)len, PROT_READ, MAP_PRIVATE, fd, 0);192 if (*dest == MAP_FAILED)193 return -1;194195 close(fd);196 return len;197}198199/**200 * Calls estrndup(3) but terminates the program EXIT_FAILURE if strndup returned201 * 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;211212 if (!(r = strndup(s, n)))213 die("strndup failed");214215 return r;216}217218/**219 * Calls malloc(3) but terminates the program with EXIT_FAILURE if malloc220 * 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;229230 if (!(r = malloc(size)))231 die("malloc failed");232233 return r;234}235236/**237 * Calls realloc(3) but terminates the program with EXIT_FAILURE if realloc238 * 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;247248 if (!(r = realloc(ptr, size)))249 die("realloc failed");250251 return r;252}253254/**255 * Calls pthread_mutex_lock(3) but terminates the program with256 * EXIT_FAILURE if pthread_mutex_lock returned an error.257 *258 * @param lock Spin lock which should be locked.259 */260void261pthread_mutex_elock(pthread_mutex_t *lock)262{263 if ((errno = pthread_mutex_lock(lock)))264 die("pthread_mutex_lock failed");265}266267/**268 * Calls pthread_mutex_unlock(3) but terminates the program with269 * EXIT_FAILURE if pthread_mutex_unlock returned an error.270 *271 * @param lock Spin lock which should be unlocked.272 */273void274pthread_mutex_eunlock(pthread_mutex_t *lock)275{276 if ((errno = pthread_mutex_unlock(lock)))277 die("pthread_mutex_unlock failed");278}279280/**281 * Calls sem_wait(3) but terminates the program with EXIT_FAILURE if sem_wait282 * returned an error.283 *284 * @param sem Semaphore to call sem_wait on.285 */286void287sem_ewait(sem_t *sem)288{289 if (sem_wait(sem))290 die("sem_wait failed");291}292293/**294 * Calls sem_post(3) but terminates the program with EXIT_FAILURE if sem_post295 * returned an error.296 *297 * @param sem Semaphore to call sem_post on.298 */299void300sem_epost(sem_t *sem)301{302 if (sem_post(sem))303 die("sem_post failed");304}