#include #include #include #include #include #include #include "gemtext.h" static struct gemtext * _case_default(char **text) { struct gemtext_text *ret; char *ptr; char *cpy; ptr = *text; cpy = ptr; ret = malloc(sizeof(*ret)); if (ret == NULL) return NULL; memset(ret, 0, sizeof(*ret)); ret->type = GEMTEXT_TEXT; while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++; ret->text = strndup(cpy, ptr-cpy); if (ret->text == NULL) { gemtext_free((struct gemtext *)ret); return NULL; } if (*ptr == '\r') { if (strncmp(ptr, "\r\n", 2) == 0) ptr += 2; else ptr++; } else if (*ptr == '\n') { ptr++; } *text = ptr; return (struct gemtext *)ret; } static struct gemtext * _case_link(char **text) { struct gemtext_link *ret; char *ptr; char *cpy; ptr = *text; if (*(ptr+1) != '>') return _case_default(text); ret = malloc(sizeof(*ret)); if (ret == NULL) return NULL; memset(ret, 0, sizeof(*ret)); ret->type = GEMTEXT_LINK; ptr += 2; /* skip "=>" */ while (*ptr != '\0' && (*ptr == ' ' || *ptr == '\t')) ptr++; cpy = ptr; while (*ptr != '\r' && *ptr != '\n' && *ptr != ' ' && *ptr != '\t' && *ptr != '\0') ptr++; ret->link = strndup(cpy, ptr-cpy); if (*ptr == ' ' || *ptr == '\t') { while(*ptr == ' ' || *ptr == '\t') ptr++; cpy = ptr; while (*ptr != '\r' && *ptr != '\n' && *ptr != '\0') ptr++; ret->name = strndup(cpy, ptr-cpy); } if (*ptr == '\r') { if (strncmp(ptr, "\r\n", 2) == 0) ptr += 2; else ptr++; } else if (*ptr == '\n') { ptr++; } *text = ptr; return (struct gemtext *)ret; } static struct gemtext * _case_h1(char **text) { struct gemtext_h1 *ret; char *ptr; char *cpy; ptr = *text; ret = malloc(sizeof(*ret)); if (ret == NULL) return NULL; memset(ret, 0, sizeof(*ret)); ret->type = GEMTEXT_H1; ptr++; while (*ptr != '\0' && (*ptr == ' ' || *ptr == '\t')) ptr++; cpy = ptr; while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++; ret->text = strndup(cpy, ptr-cpy); if (ret->text == NULL) { gemtext_free((struct gemtext *)ret); return NULL; } if (*ptr == '\r') { if (strncmp(ptr, "\r\n", 2) == 0) ptr += 2; else ptr++; } else if (*ptr == '\n') { ptr++; } *text = ptr; return (struct gemtext *)ret; } static struct gemtext * _case_h2(char **text) { struct gemtext_h2 *ret; char *ptr; char *cpy; ptr = *text; ret = malloc(sizeof(*ret)); if (ret == NULL) return NULL; memset(ret, 0, sizeof(*ret)); ret->type = GEMTEXT_H2; ptr += 2; while (*ptr != '\0' && (*ptr == ' ' || *ptr == '\t')) ptr++; cpy = ptr; while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++; ret->text = strndup(cpy, ptr-cpy); if (ret->text == NULL) { gemtext_free((struct gemtext *)ret); return NULL; } if (*ptr == '\r') { if (strncmp(ptr, "\r\n", 2) == 0) ptr += 2; else ptr++; } else if (*ptr == '\n') { ptr++; } *text = ptr; return (struct gemtext *)ret; } static struct gemtext * _case_h3(char **text) { struct gemtext_h3 *ret; char *ptr; char *cpy; ptr = *text; ret = malloc(sizeof(*ret)); if (ret == NULL) return NULL; memset(ret, 0, sizeof(*ret)); ret->type = GEMTEXT_H3; ptr += 3; while (*ptr != '\0' && (*ptr == ' ' || *ptr == '\t')) ptr++; cpy = ptr; while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++; ret->text = strndup(cpy, ptr-cpy); if (ret->text == NULL) { gemtext_free((struct gemtext *)ret); return NULL; } if (*ptr == '\r') { if (strncmp(ptr, "\r\n", 2) == 0) ptr += 2; else ptr++; } else if (*ptr == '\n') { ptr++; } *text = ptr; return (struct gemtext *)ret; } static struct gemtext * _case_h(char **text) { char *ptr; ptr = *text; if (*(ptr+1) == '#') { if (*(ptr+2) == '#') return _case_h3(text); else return _case_h2(text); } else { return _case_h1(text); } /* NOT REACHED */ return NULL; } static struct gemtext * _case_pre(char **text) { struct gemtext_pre *ret; char *ptr; char *cpy; ptr = *text; if (strncmp(ptr, "```", 3) != 0) return _case_default(text); ret = malloc(sizeof(*ret)); if (ret == NULL) return NULL; memset(ret, 0, sizeof(*ret)); ret->type = GEMTEXT_PRE; ptr += 3; if (*ptr == '\r') { if (strncmp(ptr, "\r\n", 2) == 0) ptr += 2; else ptr++; } else if (*ptr == '\n') { ptr++; } cpy = ptr; while (*ptr != '\0') { if (*ptr == '\n' || *ptr == '\r') { if (strncmp(ptr+1, "```", 3) == 0) { ptr++; ret->text = strndup(cpy, ptr-cpy); if (ret->text == NULL) { gemtext_free((struct gemtext *)ret); return NULL; } break; } } ptr++; } /* true if closing "```" was never found in while loop above */ if (*ptr == '\0') { ret->text = strndup(cpy, ptr-cpy); if (ret->text == NULL) { gemtext_free((struct gemtext *)ret); return NULL; } } /* should be always true if above statement is false */ if (*ptr == '`') { while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++; } if (*ptr == '\r') { if (strncmp(ptr, "\r\n", 2) == 0) ptr += 2; else ptr++; } else if (*ptr == '\n') { ptr++; } *text = ptr; return (struct gemtext *)ret; } static struct gemtext * _case_ul(char **text) { struct gemtext_ul *ret; char *ptr; char *cpy; ptr = *text; ret = malloc(sizeof(*ret)); if (ret == NULL) return NULL; memset(ret, 0, sizeof(*ret)); ret->type = GEMTEXT_UL; if (*(ptr+1) != ' ') return _case_default(text); ptr += 2; /* skip whitespace after '*' */ cpy = ptr; while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++; ret->text = strndup(cpy, ptr-cpy); if (ret->text == NULL) { gemtext_free((struct gemtext *)ret); return NULL; } if (*ptr == '\r') { if (strncmp(ptr, "\r\n", 2) == 0) ptr += 2; else ptr++; } else if (*ptr == '\n') { ptr++; } *text = ptr; return (struct gemtext *)ret; } static struct gemtext * _case_qt(char **text) { struct gemtext_qt *ret; char *ptr; char *cpy; ptr = *text; ret = malloc(sizeof(*ret)); if (ret == NULL) return NULL; memset(ret, 0, sizeof(*ret)); ret->type = GEMTEXT_QT; ptr++; cpy = ptr; while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++; ret->text = strndup(cpy, ptr-cpy); if (ret->text == NULL) { gemtext_free((struct gemtext *)ret); return NULL; } if (*ptr == '\r') { if (strncmp(ptr, "\r\n", 2) == 0) ptr += 2; else ptr++; } else if (*ptr == '\n') { ptr++; } *text = ptr; return (struct gemtext *)ret; } static struct gemtext * _gemtext_decode(char **text) { switch (**text) { case '=': return _case_link(text); case '#': return _case_h(text); case '`': return _case_pre(text); case '*': return _case_ul(text); case '>': return _case_qt(text); default: return _case_default(text); } /* NOT REACHED */ return NULL; } struct gemtext * gemtext_decode(char *text) { return _gemtext_decode(&text); } struct gemtext * gemtext_decode_fd(int fd) { struct gemtext *ret; struct stat fst; char *tbuf; if (fstat(fd, &fst) == -1) return NULL; tbuf = malloc(fst.st_size+1); if (tbuf == NULL) return NULL; memset(tbuf, 0, fst.st_size+1); if (read(fd, tbuf, fst.st_size) == -1) { free(tbuf); return NULL; } ret = gemtext_decode(tbuf); free(tbuf); if (ret == NULL) return NULL; return ret; } struct gemtext * gemtext_decode_file(const char *path) { struct gemtext *ret; int fd; fd = open(path, O_RDONLY); if (fd == -1) return NULL; ret = gemtext_decode_fd(fd); close(fd); if (ret == NULL) return NULL; return ret; } struct gemtext ** gemtext_list_decode(char *text) { struct gemtext **ret; struct gemtext **append; struct gemtext *item; char *ptr; ret = NULL; ptr = text; while (*ptr) { item = _gemtext_decode(&ptr); if (item == NULL) { gemtext_list_free(ret); return NULL; } append = gemtext_list_append(ret, item); if (append == NULL) { gemtext_list_free(ret); return NULL; } ret = append; } /* * if this is reached, it means there was no text to parse * therefore NULL would be returned, but we don't want that * because it is the error indicator, so we'll create an * empty list */ if (ret == NULL) { ret = malloc(sizeof(*ret)); if (ret == NULL) return NULL; ret[0] = NULL; } return ret; } struct gemtext ** gemtext_list_decode_fd(int fd) { struct gemtext **ret; struct stat fst; char *tbuf; if (fstat(fd, &fst) == -1) return NULL; tbuf = malloc(fst.st_size+1); if (tbuf == NULL) return NULL; memset(tbuf, 0, fst.st_size+1); if (read(fd, tbuf, fst.st_size) == -1) { free(tbuf); return NULL; } ret = gemtext_list_decode(tbuf); free(tbuf); if (ret == NULL) return NULL; return ret; } struct gemtext ** gemtext_list_decode_file(const char *path) { struct gemtext **ret; int fd; fd = open(path, O_RDONLY); if (fd == -1) return NULL; ret = gemtext_list_decode_fd(fd); close(fd); if (ret == NULL) return NULL; return ret; }