74fb88734c22ef5aa1623f49beb6b75fc3671285
[platform/framework/web/livebox-viewer.git] / src / desc_parser.c
1 /*
2  * Copyright 2013  Samsung Electronics Co., Ltd
3  *
4  * Licensed under the Flora License, Version 1.1 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://floralicense.org/license/
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <stdio.h>
18 #include <errno.h>
19 #include <stdlib.h> /* malloc */
20 #include <string.h> /* strdup */
21 #include <ctype.h>
22
23 #include <dlog.h>
24 #include <livebox-errno.h>
25
26 #include "debug.h"
27 #include "livebox.h"
28 #include "livebox_internal.h"
29 #include "desc_parser.h"
30 #include "dlist.h"
31 #include "util.h"
32 #include "critical_log.h"
33
34 #define TYPE_TEXT "text"
35 #define TYPE_IMAGE "image"
36 #define TYPE_EDJE "edje"
37 #define TYPE_SIGNAL "signal"
38 #define TYPE_INFO "info"
39 #define TYPE_DRAG "drag"
40
41 #define INFO_SIZE "size"
42 #define INFO_CATEGORY "category"
43
44 struct block {
45         char *type;
46         int type_len;
47
48         char *part;
49         int part_len;
50
51         char *data;
52         int data_len;
53
54         char *file;
55         int file_len;
56
57         char *option;
58         int option_len;
59
60         char *id;
61         int id_len;
62 };
63
64 static int update_text(struct livebox *handle, struct block *block, int is_pd)
65 {
66         struct livebox_script_operators *ops;
67
68         if (!block || !block->part || !block->data) {
69                 ErrPrint("Invalid argument\n");
70                 return LB_STATUS_ERROR_INVALID;
71         }
72
73         ops = is_pd ? &handle->pd.data.ops : &handle->lb.data.ops;
74         if (ops->update_text) {
75                 ops->update_text(handle, (const char *)block->id, (const char *)block->part, (const char *)block->data);
76         }
77
78         return 0;
79 }
80
81 static int update_image(struct livebox *handle, struct block *block, int is_pd)
82 {
83         struct livebox_script_operators *ops;
84         if (!block || !block->part) {
85                 ErrPrint("Invalid argument\n");
86                 return LB_STATUS_ERROR_INVALID;
87         }
88
89         ops = is_pd ? &handle->pd.data.ops : &handle->lb.data.ops;
90         if (ops->update_image) {
91                 ops->update_image(handle, block->id, block->part, block->data, block->option);
92         }
93
94         return 0;
95 }
96
97 static int update_script(struct livebox *handle, struct block *block, int is_pd)
98 {
99         struct livebox_script_operators *ops;
100         if (!block || !block->part) {
101                 ErrPrint("Invalid argument\n");
102                 return LB_STATUS_ERROR_INVALID;
103         }
104
105         ops = is_pd ? &handle->pd.data.ops : &handle->lb.data.ops;
106         if (ops->update_script) {
107                 ops->update_script(handle, block->id, block->part, block->data, block->option);
108         }
109
110         return 0;
111 }
112
113 static int update_signal(struct livebox *handle, struct block *block, int is_pd)
114 {
115         struct livebox_script_operators *ops;
116
117         if (!block) {
118                 ErrPrint("Invalid argument\n");
119                 return LB_STATUS_ERROR_INVALID;
120         }
121
122         ops = is_pd ? &handle->pd.data.ops : &handle->lb.data.ops;
123         if (ops->update_signal) {
124                 ops->update_signal(handle, block->id, block->data, block->part);
125         }
126
127         return 0;
128 }
129
130 static int update_drag(struct livebox *handle, struct block *block, int is_pd)
131 {
132         double dx, dy;
133         struct livebox_script_operators *ops;
134
135         if (!block || !block->data || !block->part) {
136                 ErrPrint("Invalid argument\n");
137                 return LB_STATUS_ERROR_INVALID;
138         }
139
140         ops = is_pd ? &handle->pd.data.ops : &handle->lb.data.ops;
141
142         if (sscanf(block->data, "%lfx%lf", &dx, &dy) != 2) {
143                 ErrPrint("Invalid format of data\n");
144                 return LB_STATUS_ERROR_INVALID;
145         }
146
147         if (ops->update_drag) {
148                 ops->update_drag(handle, block->id, block->part, dx, dy);
149         }
150
151         return 0;
152 }
153
154 static int update_info(struct livebox *handle, struct block *block, int is_pd)
155 {
156         struct livebox_script_operators *ops;
157
158         if (!block || !block->part || !block->data) {
159                 ErrPrint("Invalid argument\n");
160                 return LB_STATUS_ERROR_INVALID;
161         }
162
163         ops = is_pd ? &handle->pd.data.ops : &handle->lb.data.ops;
164
165         if (!strcasecmp(block->part, INFO_SIZE)) {
166                 int w, h;
167
168                 if (sscanf(block->data, "%dx%d", &w, &h) != 2) {
169                         ErrPrint("Invalid format (%s)\n", block->data);
170                         return LB_STATUS_ERROR_INVALID;
171                 }
172
173                 if (ops->update_info_size) {
174                         ops->update_info_size(handle, block->id, w, h);
175                 }
176         } else if (!strcasecmp(block->part, INFO_CATEGORY)) {
177                 if (ops->update_info_category) {
178                         ops->update_info_category(handle, block->id, block->data);
179                 }
180         }
181
182         return 0;
183 }
184
185 static inline int update_begin(struct livebox *handle, int is_pd)
186 {
187         struct livebox_script_operators *ops;
188
189         ops = is_pd ? &handle->pd.data.ops : &handle->lb.data.ops;
190
191         if (ops->update_begin) {
192                 ops->update_begin(handle);
193         }
194
195         return 0;
196 }
197
198 static inline int update_end(struct livebox *handle, int is_pd)
199 {
200         struct livebox_script_operators *ops;
201
202         ops = is_pd ? &handle->pd.data.ops : &handle->lb.data.ops;
203
204         if (ops->update_end) {
205                 ops->update_end(handle);
206         }
207
208         return 0;
209 }
210
211 int parse_desc(struct livebox *handle, const char *descfile, int is_pd)
212 {
213         FILE *fp;
214         int ch;
215         enum state {
216                 UNKNOWN = 0x10,
217                 BLOCK_OPEN = 0x11,
218                 FIELD = 0x12,
219                 VALUE = 0x13,
220                 BLOCK_CLOSE = 0x14,
221
222                 VALUE_TYPE = 0x00,
223                 VALUE_PART = 0x01,
224                 VALUE_DATA = 0x02,
225                 VALUE_FILE = 0x03,
226                 VALUE_OPTION = 0x04,
227                 VALUE_ID = 0x05
228         };
229         const char *field_name[] = {
230                 "type",
231                 "part",
232                 "data",
233                 "file",
234                 "option",
235                 "id",
236                 NULL
237         };
238         enum state state;
239         register int field_idx;
240         register int idx = 0;
241         register int i;
242         struct block *block;
243         struct {
244                 const char *type;
245                 int (*handler)(struct livebox *handle, struct block *block, int is_pd);
246         } handlers[] = {
247                 {
248                         .type = TYPE_TEXT,
249                         .handler = update_text,
250                 },
251                 {
252                         .type = TYPE_IMAGE,
253                         .handler = update_image,
254                 },
255                 {
256                         .type = TYPE_EDJE,
257                         .handler = update_script,
258                 },
259                 {
260                         .type = TYPE_SIGNAL,
261                         .handler = update_signal,
262                 },
263                 {
264                         .type = TYPE_DRAG,
265                         .handler = update_drag,
266                 },
267                 {
268                         .type = TYPE_INFO,
269                         .handler = update_info,
270                 },
271                 {
272                         .type = NULL,
273                         .handler = NULL,
274                 },
275         };
276
277         fp = fopen(descfile, "rt");
278         if (!fp) {
279                 ErrPrint("Error: %s\n", strerror(errno));
280                 return LB_STATUS_ERROR_IO;
281         }
282
283         update_begin(handle, is_pd);
284
285         state = UNKNOWN;
286         field_idx = 0;
287
288         block = NULL;
289         while (!feof(fp)) {
290                 ch = getc(fp);
291
292                 switch (state) {
293                 case UNKNOWN:
294                         if (ch == '{') {
295                                 state = BLOCK_OPEN;
296                                 break;
297                         }
298
299                         if (!isspace(ch)) {
300                                 update_end(handle, is_pd);
301                                 if (fclose(fp) != 0) {
302                                         ErrPrint("fclose: %s\n", strerror(errno));
303                                 }
304                                 return LB_STATUS_ERROR_INVALID;
305                         }
306                         break;
307
308                 case BLOCK_OPEN:
309                         if (isblank(ch)) {
310                                 break;
311                         }
312
313                         if (ch != '\n') {
314                                 goto errout;
315                         }
316
317                         block = calloc(1, sizeof(*block));
318                         if (!block) {
319                                 CRITICAL_LOG("Heap: %s\n", strerror(errno));
320                                 update_end(handle, is_pd);
321                                 if (fclose(fp) != 0) {
322                                         ErrPrint("fclose: %s\n", strerror(errno));
323                                 }
324                                 return LB_STATUS_ERROR_MEMORY;
325                         }
326
327                         state = FIELD;
328                         idx = 0;
329                         field_idx = 0;
330                         break;
331
332                 case FIELD:
333                         if (isspace(ch)) {
334                                 break;
335                         }
336
337                         if (ch == '}') {
338                                 state = BLOCK_CLOSE;
339                                 break;
340                         }
341
342                         if (ch == '=') {
343                                 if (field_name[field_idx][idx] != '\0') {
344                                         goto errout;
345                                 }
346
347                                 switch (field_idx) {
348                                 case 0:
349                                         state = VALUE_TYPE;
350                                         if (block->type) {
351                                                 free(block->type);
352                                                 block->type = NULL;
353                                                 block->type_len = 0;
354                                         }
355                                         idx = 0;
356                                         break;
357                                 case 1:
358                                         state = VALUE_PART;
359                                         if (block->part) {
360                                                 free(block->part);
361                                                 block->part = NULL;
362                                                 block->part_len = 0;
363                                         }
364                                         idx = 0;
365                                         break;
366                                 case 2:
367                                         state = VALUE_DATA;
368                                         if (block->data) {
369                                                 free(block->data);
370                                                 block->data = NULL;
371                                                 block->data_len = 0;
372                                         }
373                                         idx = 0;
374                                         break;
375                                 case 3:
376                                         state = VALUE_FILE;
377                                         if (block->file) {
378                                                 free(block->file);
379                                                 block->file = NULL;
380                                                 block->file_len = 0;
381                                         }
382                                         idx = 0;
383                                         break;
384                                 case 4:
385                                         state = VALUE_OPTION;
386                                         if (block->option) {
387                                                 free(block->option);
388                                                 block->option = NULL;
389                                                 block->option_len = 0;
390                                         }
391                                         idx = 0;
392                                         break;
393                                 case 5:
394                                         state = VALUE_ID;
395                                         if (block->id) {
396                                                 free(block->id);
397                                                 block->id = NULL;
398                                                 block->id_len = 0;
399                                         }
400                                         idx = 0;
401                                         break;
402                                 default:
403                                         goto errout;
404                                 }
405
406                                 break;
407                         }
408
409                         if (ch == '\n') {
410                                 goto errout;
411                         }
412
413                         if (field_name[field_idx][idx] != ch) {
414                                 if (ungetc(ch, fp) != ch) {
415                                         ErrPrint("ungetc: %s\n", strerror(errno));
416                                 }
417
418                                 while (--idx >= 0) {
419                                         if (ungetc(field_name[field_idx][idx], fp) != field_name[field_idx][idx]) {
420                                                 ErrPrint("ungetc: %s\n", strerror(errno));
421                                         }
422                                 }
423
424                                 field_idx++;
425                                 if (field_name[field_idx] == NULL) {
426                                         goto errout;
427                                 }
428
429                                 idx = 0;
430                                 break;
431                         }
432
433                         idx++;
434                         break;
435
436                 case VALUE_TYPE:
437                         if (idx == block->type_len) {
438                                 block->type_len += 256;
439                                 block->type = realloc(block->type, block->type_len);
440                                 if (!block->type) {
441                                         CRITICAL_LOG("Heap: %s\n", strerror(errno));
442                                         goto errout;
443                                 }
444                         }
445
446                         if (ch == '\n') {
447                                 block->type[idx] = '\0';
448                                 state = FIELD;
449                                 idx = 0;
450                                 field_idx = 0;
451                                 break;
452                         }
453
454                         block->type[idx] = ch;
455                         idx++;
456                         break;
457
458                 case VALUE_PART:
459                         if (idx == block->part_len) {
460                                 block->part_len += 256;
461                                 block->part = realloc(block->part, block->part_len);
462                                 if (!block->part) {
463                                         CRITICAL_LOG("Heap: %s\n", strerror(errno));
464                                         goto errout;
465                                 }
466                         }
467
468                         if (ch == '\n') {
469                                 block->part[idx] = '\0';
470                                 state = FIELD;
471                                 idx = 0;
472                                 field_idx = 0;
473                                 break;
474                         }
475
476                         block->part[idx] = ch;
477                         idx++;
478                         break;
479
480                 case VALUE_DATA:
481                         if (idx == block->data_len) {
482                                 block->data_len += 256;
483                                 block->data = realloc(block->data, block->data_len);
484                                 if (!block->data) {
485                                         CRITICAL_LOG("Heap: %s\n", strerror(errno));
486                                         goto errout;
487                                 }
488                         }
489
490                         if (ch == '\n') {
491                                 block->data[idx] = '\0';
492                                 state = FIELD;
493                                 idx = 0;
494                                 field_idx = 0;
495                                 break;
496                         }
497
498                         block->data[idx] = ch;
499                         idx++;
500                         break;
501
502                 case VALUE_FILE:
503                         if (idx == block->file_len) {
504                                 block->file_len += 256;
505                                 block->file = realloc(block->file, block->file_len);
506                                 if (!block->file) {
507                                         CRITICAL_LOG("Heap: %s\n", strerror(errno));
508                                         goto errout;
509                                 }
510                         }
511
512                         if (ch == '\n') {
513                                 block->file[idx] = '\0';
514                                 state = FIELD;
515                                 idx = 0;
516                                 field_idx = 0;
517                                 break;
518                         }
519
520                         block->file[idx] = ch;
521                         idx++;
522                         break;
523
524                 case VALUE_OPTION:
525                         if (idx == block->option_len) {
526                                 block->option_len += 256;
527                                 block->option = realloc(block->option, block->option_len);
528                                 if (!block->option) {
529                                         CRITICAL_LOG("Heap: %s\n", strerror(errno));
530                                         goto errout;
531                                 }
532                         }
533
534                         if (ch == '\n') {
535                                 block->option[idx] = '\0';
536                                 state = FIELD;
537                                 idx = 0;
538                                 field_idx = 0;
539                                 break;
540                         }
541
542                         block->option[idx] = ch;
543                         idx++;
544                         break;
545                 case VALUE_ID:
546                         if (idx == block->id_len) {
547                                 block->id_len += 256;
548                                 block->id = realloc(block->id, block->id_len);
549                                 if (!block->id) {
550                                         CRITICAL_LOG("Heap: %s\n", strerror(errno));
551                                         goto errout;
552                                 }
553                         }
554
555                         if (ch == '\n') {
556                                 block->id[idx] = '\0';
557                                 state = FIELD;
558                                 idx = 0;
559                                 field_idx = 0;
560                                 break;
561                         }
562
563                         block->id[idx] = ch;
564                         idx++;
565                         break;
566                 case BLOCK_CLOSE:
567                         if (!block->file) {
568                                 block->file = strdup(util_uri_to_path(handle->id));
569                                 if (!block->file) {
570                                         goto errout;
571                                 }
572                         }
573
574                         i = 0;
575                         while (handlers[i].type) {
576                                 if (!strcasecmp(handlers[i].type, block->type)) {
577                                         handlers[i].handler(handle, block, is_pd);
578                                         break;
579                                 }
580                                 i++;
581                         }
582
583                         if (!handlers[i].type) {
584                                 ErrPrint("Unknown block type: %s\n", block->type);
585                         }
586
587                         free(block->file);
588                         free(block->type);
589                         free(block->part);
590                         free(block->data);
591                         free(block->option);
592                         free(block->id);
593                         free(block);
594                         block = NULL;
595
596                         state = UNKNOWN;
597                         break;
598
599                 default:
600                         break;
601                 } /* switch */
602         } /* while */
603
604         if (state != UNKNOWN) {
605                 goto errout;
606         }
607
608         update_end(handle, is_pd);
609
610         if (fclose(fp) != 0) {
611                 ErrPrint("fclose: %s\n", strerror(errno));
612         }
613         return 0;
614
615 errout:
616         ErrPrint("Parse error\n");
617         if (block) {
618                 free(block->file);
619                 free(block->type);
620                 free(block->part);
621                 free(block->data);
622                 free(block->option);
623                 free(block->id);
624                 free(block);
625         }
626
627         update_end(handle, is_pd);
628
629         if (fclose(fp) != 0) {
630                 ErrPrint("fclose: %s\n", strerror(errno));
631         }
632         return LB_STATUS_ERROR_INVALID;
633 }
634
635 /* End of a file */