OSDN Git Service

replace tray implementation.
[bbk/bchanl.git] / src / bbsmenuparser.c
1 /*
2  * bbsmenuparser.c
3  *
4  * Copyright (c) 2009-2012 project bchan
5  *
6  * This software is provided 'as-is', without any express or implied
7  * warranty. In no event will the authors be held liable for any damages
8  * arising from the use of this software.
9  *
10  * Permission is granted to anyone to use this software for any purpose,
11  * including commercial applications, and to alter it and redistribute it
12  * freely, subject to the following restrictions:
13  *
14  * 1. The origin of this software must not be misrepresented; you must not
15  *    claim that you wrote the original software. If you use this software
16  *    in a product, an acknowledgment in the product documentation would be
17  *    appreciated but is not required.
18  *
19  * 2. Altered source versions must be plainly marked as such, and must not be
20  *    misrepresented as being the original software.
21  *
22  * 3. This notice may not be removed or altered from any source
23  *    distribution.
24  *
25  */
26
27 #include        <basic.h>
28 #include        <bstdio.h>
29 #include        <bstdlib.h>
30 #include        <bstring.h>
31 #include        <bctype.h>
32 #include        <btron/tf.h>
33
34 #include    "bbsmenuparser.h"
35 #include        <parse/tokenchecker.h>
36
37 #ifdef BCHANL_CONFIG_DEBUG
38 # define DP(arg) printf arg
39 # define DP_ER(msg, err) printf("%s (%d/%x)\n", msg, err>>16, err)
40 #else
41 # define DP(arg) /**/
42 # define DP_ER(msg, err) /**/
43 #endif
44
45 LOCAL tokenchecker_valuetuple_t nList_element[] = {
46   {"A HREF", 1},
47   {"B", 2},
48 };
49 LOCAL B eToken_element[] = ">=";
50
51 struct bbsmnparser_t_ {
52         bbsmncache_t *cache;
53         TF_CTX ctx;
54         W i;
55         /**/
56         Bool isLSTN;
57         enum {
58                 STATE_SEARCHTAG,
59                 STATE_ELEMENTCHECK,
60                 STATE_READBOARDURL,
61                 STATE_READBOARDURL_2,
62                 STATE_READBOARDTITLE,
63                 STATE_READCATEGORY
64         } state;
65         tokenchecker_t tokenchecker;
66         bbsmnparser_item_t *itembuffer;
67 };
68
69 EXPORT bbsmnparser_item_t* bbsmnparser_item_new()
70 {
71         bbsmnparser_item_t *item;
72
73         item = malloc(sizeof(bbsmnparser_item_t));
74         if (item == NULL) {
75                 return NULL;
76         }
77         item->category = NULL;
78         item->category_len = NULL;
79         item->url = malloc(sizeof(UB)*1);
80         item->url_len = 0;
81         item->url[0] = '\0';
82         item->title = NULL;
83         item->title_len = 0;
84
85         return item;
86 }
87
88 LOCAL VOID bbsmnparser_item_clear(bbsmnparser_item_t *item)
89 {
90         if (item->category != NULL) {
91                 free(item->category);
92         }
93         if (item->url != NULL) {
94                 free(item->url);
95         }
96         if (item->title != NULL) {
97                 free(item->title);
98         }
99         item->url = malloc(sizeof(UB)*1);
100         item->url_len = 0;
101         item->url[0] = '\0';
102         item->title = NULL;
103         item->title_len = 0;
104         item->category = NULL;
105         item->category_len = 0;
106 }
107
108 EXPORT VOID bbsmnparser_item_delete(bbsmnparser_item_t *item)
109 {
110         if (item->category != NULL) {
111                 free(item->category);
112         }
113         if (item->url != NULL) {
114                 free(item->url);
115         }
116         if (item->title != NULL) {
117                 free(item->title);
118         }
119         free(item);
120 }
121
122 LOCAL W bbsmnparser_convert_str(bbsmnparser_t *parser, const UB *src, W slen, UW attr, TC **dest, W *dlen)
123 {
124         TC ch;
125         W dest_len = 1, err;
126
127         err = tf_strtotcs(parser->ctx, src, slen, attr, &ch, &dest_len);
128         if (err < 0) {
129                 DP_ER("tf_strtotcs error", err);
130                 return err;
131         }
132         if (dest_len == 0) {
133                 return 0;
134         }
135
136         *dlen += 1;
137         *dest = realloc(*dest, sizeof(TC)*(*dlen + 1));
138         (*dest)[*dlen - 1] = ch;
139         (*dest)[*dlen] = TNULL;
140
141         if (err == 0) {
142                 return 0;
143         }
144         for (;;) {
145                 dest_len = 1;
146                 err = tf_strtotcs(parser->ctx, NULL, slen, attr, &ch, &dest_len);
147                 if (err < 0) {
148                         DP_ER("tf_strtotcs error:", err);
149                         return err;
150                 }
151                 if (dest_len == 0) {
152                         return 0;
153                 }
154
155                 *dlen += 1;
156                 *dest = realloc(*dest, sizeof(TC)*(*dlen + 1));
157                 (*dest)[*dlen - 1] = ch;
158                 (*dest)[*dlen] = TNULL;
159
160                 if (err == 0) {
161                         break;
162                 }
163         }
164
165         return 0;
166 }
167
168 LOCAL W bbsmnparser_parsechar(bbsmnparser_t *parser, UB ch, bbsmnparser_item_t *item)
169 {
170         TC **str;
171         W *len, ret, err, val;
172
173         switch (parser->state) {
174         case STATE_READBOARDTITLE:
175                 str = &(item->title);
176                 len = &(item->title_len);
177                 break;
178         case STATE_READCATEGORY:
179                 str = &(item->category);
180                 len = &(item->category_len);
181                 break;
182         default:
183                 str = NULL;
184                 len = NULL;
185                 break;
186         }
187
188         switch (parser->state) {
189         case STATE_SEARCHTAG:
190                 if (ch == '<') {
191                         parser->state = STATE_ELEMENTCHECK;
192                 }
193                 break;
194         case STATE_ELEMENTCHECK:
195                 ret = tokenchecker_inputchar(&(parser->tokenchecker), ch, &val);
196                 if (ret == TOKENCHECKER_RESULT_CONTINUE) {
197                         break;
198                 }
199                 tokenchecker_clear(&(parser->tokenchecker));
200                 if (ret == TOKENCHECKER_RESULT_DETERMINE) {
201                         if (val == 1) {
202                                 /* "A HREF " */
203                                 parser->state = STATE_READBOARDURL;
204                                 break;
205                         } else if (val == 2) {
206                                 /* "B" */
207                                 parser->state = STATE_READCATEGORY;
208                                 break;
209                         }
210                 }
211                 parser->state = STATE_SEARCHTAG;
212                 break;
213         case STATE_READBOARDURL:
214                 if (ch == ' ') {
215                         parser->state = STATE_READBOARDURL_2;
216                         break;
217                 }
218                 if (ch == '>') {
219                         parser->state = STATE_READBOARDTITLE;
220                         break;
221                 }
222                 item->url_len++;
223                 item->url = realloc(item->url, item->url_len + 1);
224                 strncat(item->url, &ch, 1);
225                 break;
226         case STATE_READBOARDURL_2:
227                 if (ch == '>') {
228                         parser->state = STATE_READBOARDTITLE;
229                         break;
230                 }
231                 break;
232         case STATE_READBOARDTITLE:
233                 if (ch == '<') {
234                         parser->state = STATE_ELEMENTCHECK;
235                         err = bbsmnparser_convert_str(parser, NULL, 0, TF_ATTR_SUPPRESS_FUSEN, str, len);
236                         if (err < 0) {
237                                 return err;
238                         }
239                         return 1;
240                 }
241                 err = bbsmnparser_convert_str(parser, &ch, 1, TF_ATTR_CONT|TF_ATTR_SUPPRESS_FUSEN, str, len);
242                 if (err < 0) {
243                         return err;
244                 }
245                 break;
246         case STATE_READCATEGORY:
247                 if (ch == '<') {
248                         parser->state = STATE_ELEMENTCHECK;
249                         err = bbsmnparser_convert_str(parser, NULL, 0, TF_ATTR_SUPPRESS_FUSEN, str, len);
250                         if (err < 0) {
251                                 return err;
252                         }
253                         return 1;
254                 }
255                 err = bbsmnparser_convert_str(parser, &ch, 1, TF_ATTR_CONT|TF_ATTR_SUPPRESS_FUSEN, str, len);
256                 if (err < 0) {
257                         return err;
258                 }
259                 break;
260         }
261
262         return 0; /* TODO */
263 }
264
265 EXPORT VOID bbsmnparser_item_gethostboard(bbsmnparser_item_t *item, UB **host, W *host_len, UB **board, W *board_len)
266 {
267         W i = 0;
268         UB *host0 = NULL, *board0 = NULL;
269         W host_len0 = 0, board_len0 = 0;
270
271         host0 = item->url + 7;
272         for (i=7; i < item->url_len; i++) {
273                 if (item->url[i] == '/') {
274                         break;
275                 }
276                 host_len0++;
277         }
278
279         i++;
280         board0 = item->url + i;
281         for (; i < item->url_len; i++) {
282                 if (item->url[i] == '/') {
283                         break;
284                 }
285                 board_len0++;
286         }
287
288         *host = host0;
289         *host_len = host_len0;
290         *board = board0;
291         *board_len = board_len0;
292 }
293
294 EXPORT Bool bbsmnparser_item_checkboradurl(bbsmnparser_item_t *item)
295 {
296         W i = 0;
297         UB *host, *board;
298         W host_len = 0, board_len = 0;
299         int cmp;
300
301         if (item->category != NULL) {
302                 return True;
303         }
304         if (item->url == NULL) {
305                 /* error */
306                 return False;
307         }
308
309         if (item->url_len < 7) {
310                 /* this is not "http://". too short. */
311                 return False;
312         }
313         cmp = strncmp(item->url, "http://", 7);
314         if (cmp != 0) {
315                 /* this is not "http://" */
316                 return False;
317         }
318
319         host = item->url + 7;
320         for (i=7; i < item->url_len; i++) {
321                 if (item->url[i] == '/') {
322                         break;
323                 }
324                 host_len++;
325         }
326         if (host_len < 8) {
327                 return False;
328         }
329         cmp = strncmp(host + host_len - 8, ".2ch.net", 8);
330         if (cmp != 0) {
331                 return False;
332         }
333
334         i++;
335         board = item->url + i;
336         for (; i < item->url_len; i++) {
337                 if (item->url[i] == '/') {
338                         break;
339                 }
340                 board_len++;
341         }
342         if (board_len <= 0) {
343                 return False;
344         }
345
346         if ((i+1) != item->url_len) {
347                 return False;
348         }
349
350         return True;
351 }
352
353 EXPORT W bbsmnparser_getnextitem(bbsmnparser_t *parser, bbsmnparser_item_t **item)
354 {
355         bbsmncache_datareadcontext_t *context;
356         Bool cont;
357         UB *bin_cache;
358         W len_cache, i, err = 0;
359
360         *item = NULL;
361
362         context = bbsmncache_startdataread(parser->cache, parser->i);
363         if (context == NULL) {
364                 return -1; /* TODO */
365         }
366
367         for (;;) {
368                 cont = bbsmncache_datareadcontext_nextdata(context, &bin_cache, &len_cache);
369                 if (cont == False) {
370                         break;
371                 }
372
373                 for (i = 0; i < len_cache; i++) {
374                         err = bbsmnparser_parsechar(parser, bin_cache[i], parser->itembuffer);
375                         if (err < 0) {
376                                 i++;
377                                 break;
378                         }
379                         if (err == 1) {
380                                 i++;
381                                 break;
382                         }
383                 }
384
385                 parser->i += i;
386                 if (err < 0) {
387                         break;
388                 }
389                 if (err == 1) {
390                         *item = parser->itembuffer;
391                         parser->itembuffer = bbsmnparser_item_new();
392                         break;
393                 }
394         }
395
396         bbsmncache_enddataread(parser->cache, context);
397
398         return err;
399 }
400
401 EXPORT VOID bbsmnparser_clear(bbsmnparser_t *parser)
402 {
403         TC ch;
404         W len = 1, err;
405
406         err = tf_strtotcs(parser->ctx, NULL, 0, TF_ATTR_START, &ch, &len);
407         if (err != 0) {
408                 DP_ER("tf_strtotcs (clear)", err);
409         }
410
411         parser->i = 0;
412         parser->isLSTN = False;
413         parser->state = STATE_SEARCHTAG;
414
415         bbsmnparser_item_clear(parser->itembuffer);
416 }
417
418 EXPORT bbsmnparser_item_t* bbsmnparser_newcategoryitem(bbsmnparser_t *parser, TC *category, W category_len)
419 {
420         bbsmnparser_item_t *item;
421
422         item = bbsmnparser_item_new();
423         if (item == NULL) {
424                 return NULL;
425         }
426         item->category = malloc(sizeof(TC)*(category_len+1));
427         if (item->category == NULL) {
428                 bbsmnparser_item_delete(item);
429                 return NULL;
430         }
431         memcpy(item->category, category, sizeof(TC)*category_len);
432         item->category[category_len] = TNULL;
433         item->category_len = category_len;
434
435         return item;
436 }
437
438 EXPORT bbsmnparser_item_t* bbsmnparser_newboarditem(bbsmnparser_t *parser, TC *title, W title_len, UB *url, W url_len)
439 {
440         bbsmnparser_item_t *item;
441
442         item = bbsmnparser_item_new();
443         if (item == NULL) {
444                 return NULL;
445         }
446
447         free(item->url);
448         item->url = malloc(sizeof(UB)*(url_len+1));
449         if (item->url == NULL) {
450                 bbsmnparser_item_delete(item);
451                 return NULL;
452         }
453         memcpy(item->url, url, sizeof(UB)*url_len);
454         item->url[url_len] = '\0';
455         item->url_len = url_len;
456
457         item->title = malloc(sizeof(TC)*(title_len+1));
458         if (item->title == NULL) {
459                 bbsmnparser_item_delete(item);
460                 return NULL;
461         }
462         memcpy(item->title, title, sizeof(TC)*title_len);
463         item->title[title_len] = TNULL;
464         item->title_len = title_len;
465
466         return item;
467 }
468
469 EXPORT bbsmnparser_t* bbsmnparser_new(bbsmncache_t *cache)
470 {
471         bbsmnparser_t *parser;
472         W err, ctx_id;
473
474         parser = malloc(sizeof(bbsmnparser_t));
475         if (parser == NULL) {
476                 return NULL;
477         }
478         parser->cache = cache;
479
480         tf_open_ctx(&parser->ctx);
481         ctx_id = tf_to_id(TF_ID_PROFSET_CONVERTFROM, "Shift_JIS");
482         if (ctx_id < 0) {
483                 tf_close_ctx(parser->ctx);
484                 free(parser);
485                 return NULL;
486         }
487         err = tf_set_profile(parser->ctx, ctx_id);
488         if (err < 0) {
489                 tf_close_ctx(parser->ctx);
490                 free(parser);
491                 return NULL;
492         }
493
494         parser->i = 0;
495         parser->isLSTN = False;
496         parser->state = STATE_SEARCHTAG;
497
498         parser->itembuffer = bbsmnparser_item_new();
499         if (parser->itembuffer == NULL) {
500                 tf_close_ctx(parser->ctx);
501                 free(parser);
502                 return NULL;
503         }
504
505         tokenchecker_initialize(&(parser->tokenchecker), nList_element, 2, eToken_element);
506
507         return parser;
508 }
509
510 EXPORT VOID bbsmnparser_delete(bbsmnparser_t *parser)
511 {
512         bbsmnparser_item_delete(parser->itembuffer);
513         tf_close_ctx(parser->ctx);
514         free(parser);
515 }