thread.c (5468B)
1 #include "thread.h" 2 3 #include "util.h" 4 5 #include <fcntl.h> 6 #include <limits.h> 7 #include <stdlib.h> 8 #include <string.h> 9 #include <time.h> 10 11 #define MAX_COLUMN_LEN 78 12 13 enum read_state 14 { 15 HEADER, 16 MESSAGE 17 }; 18 19 /* 20 static void 21 fprint_tabs(FILE *stream, int n) 22 { 23 int i; 24 for (i = 0; i < n; i++) 25 fputc('\t', stream); 26 } 27 */ 28 29 static int 30 msg_push(struct thread *t, char c) 31 { 32 int newcap; 33 char *newmsg; 34 35 if (t->msglen == t->msgcap) { 36 newcap = t->msgcap * 2; 37 if ((newmsg = realloc(t->msg, newcap)) == NULL) 38 return -1; 39 t->msgcap = newcap; 40 t->msg = newmsg; 41 } 42 t->msg[t->msglen++] = c; 43 return 0; 44 } 45 46 int 47 thread_open(struct thread *t, char *path) 48 { 49 if ((t->stream = fopen(path, "r")) == NULL) 50 fatal("%s:", path); 51 t->msgcap = 1024; 52 t->msg = emalloc(t->msgcap); 53 thread_reset(t); 54 return 0; 55 } 56 57 void 58 thread_reset(struct thread *t) 59 { 60 t->msglen = 0; 61 t->depth = -1; 62 if (fseek(t->stream, 0, SEEK_SET) == -1) 63 fatal("fseek:"); 64 fscanf(t->stream, "%9d\n", &t->lastid); 65 t->lineno = 1; 66 } 67 68 int 69 thread_close(struct thread *t) 70 { 71 fclose(t->stream); 72 free(t->msg); 73 return 0; 74 } 75 76 int 77 thread_next(struct thread *t) 78 { 79 char c, lc; /* current char, last char */ 80 enum read_state state; 81 char *str; 82 int depth; 83 84 state = HEADER; 85 t->msglen = 0; 86 lc = '\0'; 87 depth = -1; 88 89 while ((c = fgetc(t->stream))) { 90 if (c == EOF && lc == '\0') 91 return EOF; 92 if (c == '\n') { 93 t->lineno++; 94 } 95 if (c == '\n' && state == HEADER) { 96 t->msg[t->msglen] = '\0'; 97 for(str = t->msg; *str == '#'; str++) 98 depth++; 99 if (depth == -1 || depth > t->depth+1) 100 goto err; 101 if (depth == 0) 102 t->pid = 0; 103 else 104 t->pid = t->depth_array[depth-1]; 105 t->depth = depth; 106 if (sscanf(str, 107 "%9d %31s %4d-%2d-%2d %2d:%2d:%2d %c", 108 &t->id, t->user, 109 &t->time[0], &t->time[1], &t->time[2], 110 &t->time[3], &t->time[4], &t->time[5], 111 &c) != 8) /* make sure c don't get assigned */ 112 goto err; 113 t->depth_array[t->depth] = t->id; 114 t->msglen = 0; 115 state = MESSAGE; 116 lc = c; 117 } else if (c == EOF || (lc == '\n' && c == '#')) { 118 if (state != MESSAGE || t->msglen == 0) 119 goto err; 120 if (c == '#') 121 ungetc(c, t->stream); 122 t->msg[t->msglen-1] = '\0'; 123 return 0; 124 } else { 125 msg_push(t, c); 126 lc = c; 127 } 128 } 129 err: 130 fatal("%d: syntax error", t->lineno); 131 return EOF; 132 } 133 134 static void 135 thread_print(FILE *stream, 136 int depth, int id, char *user, 137 int year, int mon, int day, 138 int hour, int min, int sec, 139 char *msg) 140 { 141 int i; 142 for (i = 0; i <= depth; i++) 143 fputc('#', stream); 144 fprintf(stream, "%d %s %04d-%02d-%02d %02d:%02d:%02d\n", 145 id, user, 146 year, mon, day, 147 hour, min, sec); 148 fprintf(stream, "%s\n", msg); 149 } 150 151 int 152 thread_add(struct thread *t, int pid, char *msg) 153 { 154 FILE *tmp; 155 time_t clock; 156 struct tm *tm; 157 int pdepth = -1; /* parent depth */ 158 int res, added = 0; 159 160 if ((tmp = fopen("tmp/tmp.db", "w")) == NULL) 161 fatal("%s:", "tmp.db"); 162 fprintf(tmp, "%d\n", t->lastid+1); 163 164 time(&clock); 165 tm = gmtime(&clock); 166 while (1) { 167 res = thread_next(t); 168 if ((pdepth != -1 && !added && 169 (res == EOF || t->depth <= pdepth)) || 170 (res == EOF && pid == 0)) { 171 thread_print(tmp, pdepth+1, t->lastid+1, "Anonymous", 172 tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, 173 tm->tm_hour, tm->tm_min, tm->tm_sec, 174 msg); 175 added = 1; 176 } 177 if (res == EOF) 178 break; 179 if (pdepth == -1 && t->id == pid) 180 pdepth = t->depth; 181 182 thread_print(tmp, t->depth, t->id, t->user, 183 t->time[0], t->time[1], t->time[2], 184 t->time[3], t->time[4], t->time[5], 185 t->msg); 186 } 187 fclose(tmp); 188 if (pid != 0 && pdepth == -1) 189 fatal("failed to find parent id: %d", pid); 190 191 if (renameat(AT_FDCWD, "./tmp/tmp.db", AT_FDCWD, "./threads/thread.db") == -1) 192 fatal("renameat:"); 193 return 0; 194 } 195 196 /* int 197 thread_write_plain(struct thread *t, FILE *stream) 198 { 199 char *s, *last_word; 200 int column; 201 202 fprint_tabs(stream, t->depth); 203 fprintf(stream, "* %s %d-%02d-%02d %02d:%02d:%02d\n", 204 t->user, 205 t->time[0], t->time[1], t->time[2], 206 t->time[3], t->time[4], t->time[5]); 207 208 fprint_tabs(stream, t->depth); 209 fputs("| ", stream); 210 column = 0; 211 last_word = t->msg; 212 for (s = t->msg;; s++) { 213 if (*s == ' ' || *s == '\n' || *s == '\0') { 214 if ((column + s - last_word) > MAX_COLUMN_LEN 215 || (*last_word == '\n')) { 216 last_word++; 217 fputc('\n', stream); 218 fprint_tabs(stream, t->depth); 219 fputs("| ", stream); 220 column = 0; 221 } 222 fprintf(stream, "%.*s", (int)(s - last_word), last_word); 223 column += s - last_word; 224 last_word = s; 225 226 if (*s == '\0') 227 break; 228 } 229 } 230 fputs("\n\n", stream); 231 return 0; 232 } */ 233 234 int 235 thread_write_html(struct thread *t, FILE *stream, int depth) 236 { 237 char *s; 238 239 fprintf(stream, "<div id=\"%d\" class=\"thread\" style=\"margin-left:%d%%;\">\n", 240 t->id, depth * 2); 241 242 fprintf(stream, "<p class=\"thread_header\">"); 243 fprintf(stream, "%s %d-%02d-%02d %02d:%02d:%02d | ", t->user, 244 t->time[0], t->time[1], t->time[2], 245 t->time[3], t->time[4], t->time[5]); 246 if (t->pid != 0) { 247 if (depth == 0) 248 fprintf(stream, "<a href=\"%d\">parent</a> | ", t->pid); 249 else 250 fprintf(stream, "<a href=\"#%d\">parent</a> | ", t->pid); 251 } 252 fprintf(stream, "<a href=\"#%d\">link</a> | ", t->id); 253 fprintf(stream, "<a href=\"%d\">reply</a>", t->id); 254 fprintf(stream, "</p>\n"); 255 256 fputs("<p>", stream); 257 for (s = t->msg; *s != '\0'; s++) { 258 if (*s == '\n') { 259 fputs("<br>", stream); 260 } 261 fputc(*s, stream); 262 } 263 fputs("</p>\n", stream); 264 fputs("</div>\n", stream); 265 return 0; 266 }