/*
This file is hereby released to the public domain.
~aaaaaa123456789, 2015-01-21
last updated 2015-01-23
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "common.h"
static char ** get_lines_data(unsigned, const char *, unsigned char *);
static void flatten_line_pointers(char ***, unsigned);
static int add_line_count(void *, void *);
static int array_all_callback(void *, void *);
struct array_all_struct {
int (* callback) (void *, void *);
void * data;
};
char * get_line (unsigned char * reachedEOF) {
char * line = NULL;
unsigned length = 0;
int newchar;
for (newchar = getchar(); (newchar != EOF) && (newchar != '\n'); newchar = getchar()) {
line = realloc(line, ++ length);
line[length - 1] = newchar;
}
line = realloc(line, length + 1);
line[length] = 0;
if (reachedEOF) *reachedEOF = newchar == EOF;
return line;
}
char ** get_lines (unsigned lines, const char * stopper, int single_pointer, unsigned char * reachedEOF) {
if (reachedEOF) *reachedEOF = 0;
char ** result = get_lines_data(lines, stopper, reachedEOF);
if (single_pointer) flatten_line_pointers(&result, count_lines(result));
return result;
}
char * read_all (FILE * stream) {
char * result;
unsigned length = read_all_binary(stream, &result);
if (!result) return NULL;
result = realloc(result, length + 1);
result[length] = 0;
return result;
}
unsigned read_all_binary (FILE * stream, char ** result) {
if (!result) return -1;
*result = NULL;
if (!stream) return -2;
char * data = NULL;
unsigned length = 0;
char buffer[16384];
unsigned short read_length;
rewind(stream);
while (!(feof(stream) || ferror(stream))) {
read_length = fread(buffer, 1, 16384, stream);
data = realloc(data, length + read_length);
memcpy(data + length, buffer, read_length);
length += read_length;
}
if (ferror(stream)) {
free(data);
return -3;
}
*result = data;
return length;
}
void free_lines_array (char ** lines, unsigned size) {
if (!size) size = -1;
unsigned line;
for (line = 0; line < size; line ++) {
if (!lines[line]) break;
free(lines[line]);
}
free(lines);
}
int check_line (char ** line, unsigned expected_size, const char * valid_chars, char replacement, FILE * error_stream, unsigned line_number) {
int errors = LCERR_OK;
unsigned pos, length;
char * current;
char lnbuf[30];
if (line_number)
sprintf(lnbuf, "Line %u", line_number);
else
strcpy(lnbuf, "Line");
if ((valid_chars || expected_size) && (!replacement)) return LCERR_INVALID_REPLACEMENT;
if (!expected_size) goto size_checked;
length = strlen(*line);
if (length > expected_size) {
if (error_stream) fprintf(error_stream, "%s is %u characters long (expected: %u), truncating\n", lnbuf, length, expected_size);
*line = realloc(*line, expected_size + 1);
expected_size[*line] = 0;
errors |= LCERR_LINE_TOO_LONG;
} else if (length < expected_size) {
if (error_stream)
fprintf(error_stream, "%s is %u characters long (expected: %u), padding with '%c'\n", lnbuf, length, expected_size, replacement);
*line = realloc(*line, expected_size + 1);
memset(*line + length, replacement, expected_size - length);
expected_size[*line] = 0;
errors |= LCERR_LINE_TOO_SHORT;
}
size_checked:
if (!valid_chars) goto chars_checked;
for (current = *line, pos = 0; current < (*line + expected_size); current += pos + 1) {
pos = strspn(current, valid_chars);
if ((current + pos) >= (*line + expected_size)) break;
length = current - *line;
if (error_stream)
fprintf(error_stream, "%s: spurious character 0x%02x at column %u, inserting '%c'\n", lnbuf, current[pos], pos + length + 1, replacement);
current[pos] = replacement;
errors |= LCERR_SPURIOUS_CHARS;
}
chars_checked:
return errors;
}
unsigned get_number (void) {
char * line = get_line(NULL);
if (!*line) {
free(line);
return 0;
}
char * end;
unsigned number = strtoul(line, &end, 10);
if (*end) number = 0;
free(line);
return number;
}
int array_each (void * array, unsigned item_size, unsigned count, void * stopper, int (* callback) (void *, void *), void * data) {
char * current;
int value;
if (!array) return 0;
if (!count) count = -1;
for (current = array; count; count --, current += item_size) {
if ((stopper) && (!memcmp(current, stopper, item_size))) return 0;
value = (*callback)(current, data);
if (value) return value;
}
return 0;
}
int array_all (void * array, unsigned item_size, unsigned count, void * stopper, int (* callback) (void *, void *), void * data) {
struct array_all_struct cbdata = {.callback = callback, .data = data};
return !array_each(array, item_size, count, stopper, &array_all_callback, &cbdata);
}
unsigned count_lines (char ** lines) {
unsigned count = 0;
array_each(lines, sizeof(char *), 0, NULL, &add_line_count, &count);
return count;
}
char ** tokenize_string (const char * string, const char * delimiters, int single_pointer) {
if (!string) return NULL;
if (!delimiters) delimiters = " \f\n\r\t\v";
char ** result = malloc(sizeof(char *));
const char * current = string + strspn(string, delimiters);
unsigned count = 0;
unsigned length;
while (*current) {
length = strcspn(current, delimiters);
result[count] = malloc(length + 1);
memcpy(result[count], current, length);
result[count][length] = 0;
result = realloc(result, sizeof(char *) * (count + 2));
count ++;
current += length;
length = strspn(current, delimiters);
current += length;
}
result[count] = NULL;
if (single_pointer) flatten_line_pointers(&result, count_lines(result));
return result;
}
void * xmalloc (size_t size) {
if (!size) return NULL;
void * rv = malloc(size);
if (!rv) {
fputs("ERROR: Out of memory.\n", stderr);
abort();
}
return rv;
}
void * xrealloc (void * buf, size_t size) {
void * rv = realloc(buf, size);
if (!rv) {
fputs("ERROR: Out of memory.\n", stderr);
abort();
}
return rv;
}
static char ** get_lines_data (unsigned lines, const char * stopper, unsigned char * reachedEOF) {
char ** result = malloc(sizeof(char *));
unsigned count = 0;
char * line;
unsigned char eofstatus = 0;
if (!reachedEOF) reachedEOF = &eofstatus;
while (1) {
if (lines && (count >= lines)) break;
line = get_line(reachedEOF);
if (stopper && !strcmp(stopper, line)) {
free(line);
break;
}
result[count] = line;
result = realloc(result, sizeof(char *) * (count + 2));
count ++;
if (*reachedEOF) break;
}
result[count] = NULL;
return result;
}
static void flatten_line_pointers (char *** pointers, unsigned lines) {
unsigned offset = sizeof(char *) * (lines + 1);
unsigned line;
unsigned total = 0;
for (line = 0; line < lines; line ++) total += strlen(line[*pointers]);
char ** result = malloc(offset + total + lines);
char * lineptr = ((char *) result) + offset;
result[lines] = NULL;
for (line = 0; line < lines; line ++) {
offset = strlen(line[*pointers]) + 1;
memcpy(lineptr, line[*pointers], offset);
free(line[*pointers]);
result[line] = lineptr;
lineptr += offset;
}
free(*pointers);
*pointers = result;
}
static int add_line_count (void * line, void * count) {
if (!*((char **) line)) return 1;
unsigned * c = count;
(*c) ++;
return 0;
}
static int array_all_callback (void * item, void * data) {
return !(((struct array_all_struct *) data -> callback)(item, (struct array_all_struct *) data -> data));
}
Comments