diff options
Diffstat (limited to 'src-old/sc_parse.c')
-rw-r--r-- | src-old/sc_parse.c | 856 |
1 files changed, 856 insertions, 0 deletions
diff --git a/src-old/sc_parse.c b/src-old/sc_parse.c new file mode 100644 index 0000000..78f5799 --- /dev/null +++ b/src-old/sc_parse.c @@ -0,0 +1,856 @@ +/* + * sc_parse.c + * + * Copyright © 2013-2018 Thomas White <taw@bitwiz.org.uk> + * + * This file is part of Colloquium. + * + * Colloquium is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <assert.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <ctype.h> +#include <glib.h> +#include <gio/gio.h> + +#include "sc_parse.h" +#include "utils.h" + +struct _scblock +{ + char *name; + char *options; + char *contents; + + SCBlock *next; + SCBlock *child; +}; + + +SCBlock *sc_block_new() +{ + SCBlock *bl; + + bl = calloc(1, sizeof(SCBlock)); + if ( bl == NULL ) return NULL; + + return bl; +} + + +SCBlock *sc_block_next(const SCBlock *bl) +{ + assert(bl != NULL); + return bl->next; +} + + +SCBlock *sc_block_child(const SCBlock *bl) +{ + return bl->child; +} + + +const char *sc_block_name(const SCBlock *bl) +{ + return bl->name; +} + + +const char *sc_block_options(const SCBlock *bl) +{ + return bl->options; +} + + +const char *sc_block_contents(const SCBlock *bl) +{ + return bl->contents; +} + + +static SCBlock *sc_find_previous(SCBlock *top, SCBlock *find) +{ + if ( top->next == find ) return top; + + if ( top->child != NULL ) { + SCBlock *t = sc_find_previous(top->child, find); + if ( t != NULL ) return t; + } + if ( top->next != NULL ) { + SCBlock *t = sc_find_previous(top->next, find); + if ( t != NULL ) return t; + } + return NULL; +} + + +/* Add a new block before "bl" */ +SCBlock *sc_block_prepend(SCBlock *bl, SCBlock *top) +{ + SCBlock *bln; + SCBlock *prev; + + prev = sc_find_previous(top, bl); + if ( prev == NULL ) { + fprintf(stderr, "Couldn't find previous\n"); + return NULL; + } + + bln = sc_block_new(); + if ( bln == NULL ) return NULL; + + prev->next = bln; + bln->next = bl; + return bln; +} + + +/* Append "bln" after "bl" */ +void sc_block_append_p(SCBlock *bl, SCBlock *bln) +{ + if ( bl != NULL ) { + bln->next = bl->next; + bl->next = bln; + } +} + + +/* Insert a new block after "bl". "name", "options" and "contents" + * will not be copied. Returns the block just created, or NULL on error. + * If *blfp points to NULL, it will updated to point at the new block. */ +SCBlock *sc_block_append(SCBlock *bl, char *name, char *opt, char *contents, + SCBlock **blfp) +{ + SCBlock *bln = sc_block_new(); + + if ( bln == NULL ) return NULL; + + bln->name = name; + bln->options = opt; + bln->contents = contents; + bln->child = NULL; + bln->next = NULL; + + sc_block_append_p(bl, bln); + + if ( (blfp != NULL) && (*blfp == NULL) ) { + *blfp = bln; + } + + return bln; +} + + +/* Insert a new block at the end of the chain starting 'bl'. + * "name", "options" and "contents" will not be copied. Returns the block just + * created, or NULL on error. */ +SCBlock *sc_block_append_end(SCBlock *bl, char *name, char *opt, char *contents) +{ + SCBlock *bln = sc_block_new(); + + if ( bln == NULL ) return NULL; + + while ( bl->next != NULL ) { + bln->next = bl->next; + bl = bl->next; + }; + + return sc_block_append(bl, name, opt, contents, NULL); +} + + +void sc_block_append_block(SCBlock *bl, SCBlock *bln) +{ + + if ( bl == NULL ) return; + + while ( bl->next != NULL ) bl = bl->next; + + bl->next = bln; + bln->next = NULL; +} + + +/* Append a new block to the chain inside "parent". + * "name", "options" and "contents" will not be copied. Returns the block just + * created, or NULL on error. */ +SCBlock *sc_block_append_inside(SCBlock *parent, + char *name, char *opt, char *contents) +{ + SCBlock *bln; + SCBlock *bl; + SCBlock **ptr; + + bl = parent->child; + if ( bl != NULL ) { + while ( bl->next != NULL ) bl = bl->next; + ptr = &bl->next; + } else { + ptr = &parent->child; + } + + bln = sc_block_new(); + if ( bln == NULL ) return NULL; + + bln->name = name; + bln->options = opt; + bln->contents = contents; + bln->child = NULL; + bln->next = NULL; + + *ptr = bln; + + return bln; +} + + +/* Insert a new block to the chain, just after "afterme". + * "name", "options" and "contents" will not be copied. Returns the block just + * created, or NULL on error. */ +SCBlock *sc_block_insert_after(SCBlock *afterme, + char *name, char *opt, char *contents) +{ + SCBlock *bl; + + bl = sc_block_new(); + if ( bl == NULL ) return NULL; + + bl->name = name; + bl->options = opt; + bl->contents = contents; + bl->child = NULL; + bl->next = afterme->next; + afterme->next = bl; + + return bl; +} + + +static SCBlock *sc_find_parent(SCBlock *top, SCBlock *find) +{ + if ( top->child == find ) return top; + if ( top->next == find ) return top; + + if ( top->child != NULL ) { + SCBlock *t = sc_find_parent(top->child, find); + if ( t != NULL ) return t; + } + if ( top->next != NULL ) { + SCBlock *t = sc_find_parent(top->next, find); + if ( t != NULL ) return t; + } + return NULL; +} + + +/* Unlink "deleteme", which is somewhere under "top" */ +static int sc_block_unlink(SCBlock **top, SCBlock *deleteme) +{ + SCBlock *parent = sc_find_parent(*top, deleteme); + if ( parent == NULL ) { + /* Maybe it's the first block? */ + if ( *top == deleteme ) { + fprintf(stderr, "Unlinking at top\n"); + *top = (*top)->next; + return 0; + } else { + fprintf(stderr, "Couldn't find block parent!\n"); + return 1; + } + } + + if ( parent->next == deleteme ) { + parent->next = deleteme->next; + } + + if ( parent->child == deleteme ) { + parent->child = deleteme->next; + } + return 0; +} + + +/* Delete "deleteme", which is somewhere under "top" */ +int sc_block_delete(SCBlock **top, SCBlock *deleteme) +{ + int r; + r = sc_block_unlink(top, deleteme); + if ( !r ) { + sc_block_free(deleteme); + } + return r; +} + + +/* Frees "bl" and all its children (but not the blocks following it) */ +void sc_block_free(SCBlock *bl) +{ + if ( bl == NULL ) return; + + if ( bl->child != NULL ) { + SCBlock *ch = bl->child; + while ( ch != NULL ) { + SCBlock *next = ch->next; + sc_block_free(ch); + ch = next; + } + } + + free(bl); +} + + +/* Serialise one block (including children) */ +char *serialise_sc_block(const SCBlock *bl) +{ + char *a; + SCBlock *ch; + size_t len = 3; + + if ( bl == NULL ) return strdup(""); + + if ( bl->name != NULL ) len += 1+strlen(bl->name); + if ( bl->options != NULL ) len += 2+strlen(bl->options); + if ( bl->contents != NULL ) len += 2+strlen(bl->contents); + a = malloc(len); + if ( a == NULL ) return NULL; + a[0] = '\0'; + + if ( bl->name == NULL ) { + strcat(a, bl->contents); + } else if ( strcmp(bl->name, "newpara") == 0 ) { + strcat(a, "\n"); + + } else { + + strcat(a, "\\"); + strcat(a, bl->name); + if ( bl->options != NULL ) { + strcat(a, "["); + strcat(a, bl->options); + strcat(a, "]"); + } + if ( (bl->contents != NULL) || (bl->child != NULL) ) { + strcat(a, "{"); + } + if ( bl->contents != NULL ) { + strcat(a, bl->contents); + } + + /* Special case to prevent "\somethingSome text" */ + if ( (bl->name != NULL) && (bl->options == NULL) + && (bl->contents == NULL) && (bl->next != NULL) + && (bl->next->name == NULL) && (bl->child == NULL) ) + { + strcat(a, "{}"); + } + + } + + /* Add ALL child blocks of this one */ + ch = bl->child; + while ( ch != NULL ) { + + char *anew; + char *c = serialise_sc_block(ch); + if ( c == NULL ) { + free(a); + return NULL; + } + + len += strlen(c); + + anew = realloc(a, len); + if ( anew == NULL ) { + return NULL; + } else { + a = anew; + } + + strcat(a, c); + free(c); + + ch = ch->next; + + } + + if ( (bl->name != NULL) && + ((bl->contents != NULL) || (bl->child != NULL)) ) { + strcat(a, "}"); + } + + return a; +} + + +int save_sc_block(GOutputStream *fh, const SCBlock *bl) +{ + while ( bl != NULL ) { + GError *error = NULL; + char *a = serialise_sc_block(bl); + gssize r; + if ( a == NULL ) { + fprintf(stderr, "Failed to serialise block\n"); + return 1; + } + r = g_output_stream_write(fh, a, strlen(a), NULL, &error); + if ( r == -1 ) { + fprintf(stderr, "Write failed: %s\n", error->message); + return 1; + } + free(a); + bl = bl->next; + } + return 0; +} + + +static void recursive_show_sc_blocks(const char *prefix, const SCBlock *bl) +{ + while ( bl != NULL ) { + show_sc_block(bl, prefix); + bl = bl->next; + } +} + + +void show_sc_block(const SCBlock *bl, const char *prefix) +{ + printf("%s (%p) ", prefix, bl); + if ( bl == NULL ) return; + if ( bl->name != NULL ) printf("\\%s ", bl->name); + if ( bl->options != NULL ) printf("[%s] ", bl->options); + if ( bl->contents != NULL ) printf("{%s} ", bl->contents); + printf("\n"); + + if ( bl->child != NULL ) { + char new_prefix[strlen(prefix)+3]; + strcpy(new_prefix, " "); + strcat(new_prefix, prefix); + recursive_show_sc_blocks(new_prefix, bl->child); + } +} + + +void show_sc_blocks(const SCBlock *bl) +{ + recursive_show_sc_blocks("", bl); +} + + +static int get_subexpr(const char *sc, char *bk, char **pcontents, int *err) +{ + size_t ml; + int i; + int bct = 1; + int found = 0; + char *contents; + + *err = 0; + + ml = strlen(sc); + contents = malloc(ml+1); + if ( contents == NULL ) { + *err = -1; + return 0; + } + *pcontents = contents; + + for ( i=0; i<ml; i++ ) { + if ( sc[i] == bk[0] ) { + bct++; + } else if ( sc[i] == bk[1] ) { + bct--; + } + if ( bct == 0 ) { + found = 1; + break; + } + contents[i] = sc[i]; + } + + if ( (!found) || (bct != 0) ) { + *err = 1; + return 0; + } + + contents[i] = '\0'; + return i+1; +} + + +static size_t read_block(const char *sc, char **pname, char **options, + char **contents, int *err) +{ + size_t l, i, j; + char *name; + int done; + + *err = 0; + + l = strlen(sc); + i = 0; j = 0; + name = malloc(l+1); + if ( name == NULL ) { + *err = 1; + return 0; + } + + done = 0; + do { + + char c = sc[i]; + + if ( isalnum(c) ) { + name[j++] = c; + i++; + } else { + /* Found the end of the name */ + done = 1; + } + + } while ( !done && (i<l) ); + + name[j] = '\0'; + *pname = name; + + if ( sc[i] == '[' ) { + + i += get_subexpr(sc+i+1, "[]", options, err) + 1; + if ( *err ) { + printf("Couldn't find end of options '%s'\n", sc+i); + return 0; + } + + } else { + *options = NULL; + } + + if ( sc[i] == '{' ) { + + i += get_subexpr(sc+i+1, "{}", contents, err) + 1; + if ( *err ) { + printf("Couldn't find end of content '%s'\n", sc+i); + return 0; + } + + } else { + *contents = NULL; + } + + return i+1; +} + + +static void separate_newlines(SCBlock *bl) +{ + while ( bl != NULL ) { + + const char *contents = sc_block_contents(bl); + + if ( contents != NULL ) { + char *npos = strchr(contents, '\n'); + if ( npos != NULL ) { + SCBlock *nb = NULL; + if ( npos == contents ) { + bl->name = strdup("newpara"); + bl->contents = NULL; + nb = bl; + } else { + sc_block_append(bl, strdup("newpara"), + NULL, NULL, &nb); + } + + /* Add any text after the \n */ + if ( strlen(npos+1) > 0 ) { + sc_block_append(nb, NULL, NULL, + strdup(npos+1), &nb); + } + npos[0] = '\0'; + } + } + + if ( sc_block_child(bl) != NULL ) { + separate_newlines(sc_block_child(bl)); + } + + bl = sc_block_next(bl); + + } +} + + +SCBlock *sc_parse(const char *sc) +{ + SCBlock *bl; + SCBlock *blf = NULL; + char *tbuf; + size_t len, i, j; + + if ( sc == NULL ) return NULL; + + if ( strlen(sc) == 0 ) { + SCBlock *bl = sc_block_new(); + sc_block_set_contents(bl, g_strdup("")); + return bl; + } + + bl = NULL; + + len = strlen(sc); + tbuf = malloc(len+1); + if ( tbuf == NULL ) { + sc_block_free(bl); + return NULL; + } + + i = 0; j = 0; + do { + + if ( sc[i] == '\\' ) { + + int err; + char *name = NULL; + char *opt = NULL; + char *contents = NULL; + + /* Is this an escaped backslash? */ + if ( sc[i+1] == '\\' ) { + tbuf[j++] = '\\'; + i += 2; /* Skip both backslashes */ + continue; + } + + /* No, it's a real block. Dispatch the previous block */ + if ( j != 0 ) { + tbuf[j] = '\0'; + bl = sc_block_append(bl, NULL, NULL, + strdup(tbuf), &blf); + if ( bl == NULL ) { + fprintf(stderr, "Block add failed.\n"); + sc_block_free(blf); + free(tbuf); + return NULL; + } + j = 0; + } + + i += read_block(sc+i+1, &name, &opt, &contents, &err); + if ( err ) { + printf(_("Parse error\n")); + sc_block_free(blf); + free(tbuf); + return NULL; + } + + bl = sc_block_append(bl, name, opt, contents, &blf); + if ( bl == NULL ) { + fprintf(stderr, "Block add failed.\n"); + sc_block_free(blf); + free(tbuf); + return NULL; + } + bl->child = sc_parse(contents); + free(bl->contents); + bl->contents = NULL; + + } else { + + tbuf[j++] = sc[i++]; + } + + } while ( i<len ); + + /* Add final block, if it exists */ + if ( j > 0 ) { + + /* Leftover buffer is empty? */ + if ( (j==1) && (tbuf[0]=='\0') ) return bl; + + tbuf[j] = '\0'; + bl = sc_block_append(bl, NULL, NULL, tbuf, &blf); + if ( bl == NULL ) { + fprintf(stderr, "Block add failed.\n"); + sc_block_free(blf); + free(tbuf); + return NULL; + } + j = 0; + } + + separate_newlines(blf); + + return blf; +} + + +void sc_block_set_name(SCBlock *bl, char *nam) +{ + if ( bl == NULL ) { + fprintf(stderr, "sc_block_set_name: NULL block\n"); + return; + } + free(bl->name); + bl->name = nam; +} + + +void sc_block_set_options(SCBlock *bl, char *opt) +{ + free(bl->options); + bl->options = opt; +} + + +void sc_block_set_contents(SCBlock *bl, char *con) +{ + g_free(bl->contents); + bl->contents = con; +} + + +void sc_insert_text(SCBlock *b1, size_t o1, const char *t) +{ + size_t len; + char *cnew; + char *tmp; + char *p1; + + if ( b1->contents == NULL ) { + b1->contents = strdup(t); + return; + } + len = strlen(b1->contents)+1+strlen(t); + + cnew = realloc(b1->contents, len); + if ( cnew == NULL ) return; + + tmp = malloc(len); + if ( tmp == NULL ) { + free(cnew); + return; + } + + p1 = cnew + o1; + strcpy(tmp, p1); + strcpy(p1, t); + strcpy(p1+strlen(t), tmp); + free(tmp); + b1->contents = cnew; +} + + +void sc_insert_block(SCBlock *b1, int o1, SCBlock *ins) +{ + SCBlock *second; + char *p1 = g_utf8_offset_to_pointer(b1->contents, o1); + + /* Create a new block containing the second half of b1 */ + second = sc_block_new(); + sc_block_set_contents(second, g_strdup(p1)); + + /* Chop off b1 at the insertion point */ + sc_block_set_contents(b1, g_utf8_substring(b1->contents, 0, o1)); + + /* Link the new block into the chain */ + SCBlock *old_next = b1->next; + b1->next = ins; + ins->next = second; + second->next = old_next; +} + + +/* Delete text from SCBlock contents. o2=-1 means "to the end". + * Returns the number of bytes deleted. */ +size_t scblock_delete_text(SCBlock *b, ssize_t o1, ssize_t o2) +{ + size_t len; + + if ( b->contents == NULL ) { + fprintf(stderr, "Deleting text from block \\%s\n", b->name); + return 0; + } + + if ( (o2 != -1) && (o1 > o2) ) { + ssize_t t = o2; + o2 = o1; + o1 = t; + } + + len = strlen(b->contents); + if ( o2 < 0 ) o2 = len; + if ( (o1 >= o2) || (o1 > len) || (o2 > len) ) { + fprintf(stderr, "Invalid delete: %i %i %i\n", + (int)o1, (int)o2, (int)len); + return 0; + } + memmove(b->contents+o1, b->contents+o2, len-o2+1); + + return o2-o1; +} + + +static char *s_strdup(const char *a) +{ + if ( a == NULL ) return NULL; + return strdup(a); +} + + +SCBlock *sc_block_split(SCBlock *bl, size_t pos) +{ + SCBlock *n = sc_block_new(); + + if ( bl->child != NULL ) { + fprintf(stderr, "Splitting a block with a child!\n"); + return NULL; + } + + /* Second block */ + n->name = s_strdup(bl->name); + n->options = s_strdup(bl->options); + if ( bl->contents != NULL ) { + n->contents = strdup(bl->contents+pos); + /* Truncate the first block */ + bl->contents[pos] = '\0'; + } else { + n->contents = NULL; + } + + n->next = bl->next; + bl->next = n; + + return n; +} + + +/* Return a new block which is the parent for "bl" */ +SCBlock *sc_block_new_parent(SCBlock *bl, const char *name) +{ + SCBlock *n = sc_block_new(); + if ( n == NULL ) return NULL; + n->name = s_strdup(name); + n->child = bl; + return n; +} |