/* This file is hereby released to the public domain. ~aaaaaa123456789, 2015-01-21 last updated 2015-01-23 */ #include #include #include #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)); }