Fix memory leak issues
[platform/core/uifw/anthy.git] / src-util / egg.c
1 /*
2  * Copyright (C) 2002 The Free Software Initiative of Japan
3  * Author: NIIBE Yutaka
4  */
5
6 /*
7  * ANTHY Low Level Agent
8  */
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13
14 #include <anthy/anthy.h>
15
16 #include "config.h"
17
18 /*
19  *            connection        context 
20  *  S -- open -> [ ] -- new ----> [ ] -- convert --> [ ] --> get_candidates
21  *   <- close --     <- release -     <-- commit --      --> resize_segment
22  *                                    <-- cancel --      --> select_candidate
23  *                                                
24  */
25 \f
26 struct context {
27   struct anthy_context *ac;
28   int buflen;
29   unsigned char *buf;
30   int removed, inserted;
31   int sellen;
32   int *selection;
33 };
34
35 #define MAX_CONTEXT 16
36 static struct context contexts[MAX_CONTEXT];
37
38 extern int use_utf8;
39
40 #define INITIAL_BUFLEN 512
41 #define INITIAL_SELLEN 128
42
43 /*
44  * Returns -1 on error.
45  * Returns >= 0 on success, and the number is  context descriptor.
46  */
47 static int
48 new_context (void)
49 {
50   int i;
51
52   for (i = 0; i < MAX_CONTEXT; i++)
53     if (contexts[i].buflen == 0) { /* Found free one */
54       struct context *c = &contexts[i];
55
56       if ((c->buf = (unsigned char *)malloc (INITIAL_BUFLEN)) == NULL)
57         return -1;
58
59       if ((c->selection = (int *)malloc (INITIAL_SELLEN)) == NULL) {
60         free (c->buf);
61         c->buf = NULL;
62         return -1;
63       }
64
65       if ((c->ac = anthy_create_context ()) == NULL) {
66         free (c->buf);
67         c->buf = NULL;
68         free (c->selection);
69         c->selection = NULL;
70         return -1;
71       }
72       if (use_utf8) {
73         anthy_context_set_encoding(c->ac, ANTHY_UTF8_ENCODING);
74       }
75
76       c->buflen = INITIAL_BUFLEN;
77       c->sellen = INITIAL_SELLEN;
78       return i;
79     }
80
81   /* No free context to be used */
82   return -1;
83 }
84
85 static int
86 release_context (int c_desc)
87 {
88   struct context *c = &contexts[c_desc];
89
90   anthy_release_context(c->ac);
91   free (c->buf);
92   c->buf = NULL;
93   c->buflen = 0;
94   free (c->selection);
95   c->selection = NULL;
96   c->sellen = 0;
97
98   return 0;
99 }
100
101 static struct context *
102 c_desc_to_context (int c_desc)
103 {
104   return &contexts[c_desc];
105 }
106
107 static int
108 get_number_of_segments (struct context *c)
109 {
110   struct anthy_conv_stat cs;
111
112   if (anthy_get_stat(c->ac, &cs) < 0)
113     return -1;
114
115   return cs.nr_segment;
116 }
117
118 static int
119 begin_conversion (struct context *c, const char *input)
120 {
121   int i;
122   int seg_num;
123   if (anthy_set_string(c->ac, (char *)input) < 0)
124     return -1;
125
126   seg_num = get_number_of_segments (c);
127   if (seg_num >= c->sellen) {
128     c->sellen *= 2;
129     c->selection = realloc (c->selection, c->sellen);
130     if (c->selection == NULL) { /* Fatal */
131       c->sellen = -1;
132       return -1;
133     }
134   }
135
136   for (i = 0; i < seg_num; i++)
137     c->selection[i] = 0;
138
139   return 0;
140 }
141
142 static int
143 end_conversion (struct context *c, int cancel)
144 {
145   int n;
146   int seg_num;
147   int can_num;
148
149   if (!cancel) {
150     n = get_number_of_segments (c);
151     for (seg_num = 0; seg_num < n; seg_num++) {
152       can_num = c->selection[seg_num];
153       anthy_commit_segment(c->ac, seg_num, can_num);
154     }
155   }
156
157   return 0;
158 }
159
160 static int
161 get_segment_number_of_candidates (struct context *c, int seg_num)
162 {
163   struct anthy_segment_stat ss;
164
165   if (anthy_get_segment_stat (c->ac, seg_num, &ss) != 0)
166     return -1;
167
168   return ss.nr_candidate;
169 }
170
171 static const unsigned char *
172 get_segment_candidate (struct context *c, int seg_num, int cand_num)
173 {
174   int len;
175
176   while (1) {
177     len = anthy_get_segment (c->ac, seg_num, cand_num,
178                              (char *)c->buf, c->buflen);
179
180     if (len < 0)
181       return NULL;
182
183     if (len < c->buflen)
184       return c->buf;
185
186     c->buflen *= 2;
187     c->buf = realloc (c->buf, c->buflen);
188     if (c->buf == NULL) { /* Fatal */
189       c->buflen = -1;
190       return NULL;
191     }
192   }
193 }
194
195 static const unsigned char *
196 get_segment_yomi (struct context *c, int seg_num)
197 {
198   return get_segment_candidate (c, seg_num, NTH_UNCONVERTED_CANDIDATE);
199 }
200
201 static const unsigned char *
202 get_segment_converted (struct context *c, int seg_num)
203 {
204   return get_segment_candidate (c, seg_num, 0);
205 }
206
207 static int
208 resize_segment (struct context *c, int seg_num, int inc_dec)
209 {
210   int i;
211   struct anthy_conv_stat cs;
212
213   if (anthy_get_stat(c->ac, &cs) < 0)
214     return -1;
215
216   /* Replace all segments after SEG_NUM */
217   c->removed = cs.nr_segment - seg_num;
218   anthy_resize_segment(c->ac, seg_num, inc_dec?-1:1);
219   if (anthy_get_stat(c->ac, &cs) < 0)
220     return -1;
221   c->inserted = cs.nr_segment - seg_num;
222
223   if (cs.nr_segment >= c->sellen) {
224     c->sellen *= 2;
225     c->selection = realloc (c->selection, c->sellen);
226     if (c->selection == NULL) { /* Fatal */
227       c->sellen = -1;
228       return -1;
229     }
230   }
231   for (i = seg_num; i < cs.nr_segment; i++)
232     c->selection[i] = 0;
233
234   return seg_num;
235 }
236
237 /* Only valid after call of resize_segment or select_candidate */
238 static int
239 get_number_of_segments_removed (struct context *c, int seg_num)
240 {
241   (void)seg_num;
242   return c->removed;
243 }
244
245 /* Only valid after call of resize_segment or select_candidate */
246 static int
247 get_number_of_segments_inserted (struct context *c, int seg_num)
248 {
249   (void)seg_num;
250   return c->inserted;
251 }
252
253 static int
254 select_candidate (struct context *c, int seg_num, int can_num)
255 {
256   /*
257    * Anthy does not have capability to affect the result of selection
258    * to other segments.
259    */
260   c->removed = 0;
261   c->inserted = 0;
262
263   /*
264    * Record, but not call anthy_commit_segment.
265    */
266   c->selection[seg_num] = can_num;
267
268   return seg_num;
269 }
270 \f
271 static int
272 say_hello (void)
273 {
274   const char *options = "";
275
276   printf ("Anthy (Version %s) [%s] : Nice to meet you.\r\n", VERSION, 
277           options);
278   fflush (stdout);
279   return 0;
280 }
281
282 #define ERROR_CODE_UNKNOWN      400
283 #define ERROR_CODE_UNSUPPOTED   401
284
285 static int
286 say_unknown (void)
287 {
288   printf ("-ERR %d Unknown command.\r\n", ERROR_CODE_UNKNOWN);
289   fflush (stdout);
290   return 0;
291 }
292
293 static int
294 do_commit (const char *line)
295 {
296   char *p;
297   struct context *c;
298   int c_desc, cancel, r;
299
300   c_desc = strtol (line+7, &p, 10);
301   c = c_desc_to_context (c_desc);
302   cancel = strtol (p+1, &p, 10);
303   r = end_conversion (c, cancel);
304   if (r < 0)
305     printf ("-ERR %d commit failed.\r\n", -r);
306   else
307     printf ("+OK\r\n");
308   fflush (stdout);
309   return 0;
310 }
311
312 static void
313 output_segments (struct context *c, int seg_num, int removed, int inserted)
314 {
315   int i;
316
317   printf ("+DATA %d %d %d\r\n", seg_num, removed, inserted);
318   for (i = seg_num; i < seg_num + inserted; i++) {
319     int nc;
320
321     nc = get_segment_number_of_candidates (c, i);
322     printf ("%d " ,nc);
323     printf ("%s ", get_segment_converted (c, i));
324     printf ("%s\r\n", get_segment_yomi (c, i));
325   }
326   printf ("\r\n");
327 }
328
329 static int
330 do_convert (const char *line)
331 {
332   char *p;
333   struct context *c;
334   int c_desc, r;
335
336   c_desc = strtol (line+8, &p, 10);
337   c = c_desc_to_context (c_desc);
338   r = begin_conversion (c, p+1);
339   if (r < 0)
340     printf ("-ERR %d convert failed.\r\n", -r);
341   else
342     {
343       int n = get_number_of_segments (c);
344       output_segments (c, 0, 0, n);
345     }
346
347   fflush (stdout);
348   return 0;
349 }
350
351 static int
352 do_get_candidates (const char *line)
353 {
354   char *p;
355   struct context *c;
356   int c_desc, seg_num, cand_offset, max_cands;
357   int nc, i, max;
358
359   c_desc = strtol (line+15, &p, 10);
360   seg_num = strtol (p+1, &p, 10);
361   cand_offset = strtol (p+1, &p, 10);
362   max_cands = strtol (p+1, &p, 10);
363
364   c = c_desc_to_context (c_desc);
365   nc = get_segment_number_of_candidates (c, seg_num);
366
367   max = cand_offset + max_cands;
368   if (nc < cand_offset + max_cands)
369     max = nc;
370
371   printf ("+DATA %d %d\r\n", cand_offset, max);
372   for (i = cand_offset; i < max; i++)
373     printf ("%s\r\n", get_segment_candidate (c, seg_num, i));
374   printf ("\r\n");  
375
376   fflush (stdout);
377   return 0;
378 }
379
380 static int
381 do_new_context (const char *line)
382 {
383   int r;
384
385   /* XXX: Should check arguments */
386   if (strncmp (" INPUT=#18 OUTPUT=#18", line+11, 20) != 0) {
387     printf ("-ERR %d unsupported context\r\n", ERROR_CODE_UNSUPPOTED);
388     return 1;
389   }
390
391   r = new_context ();
392   if (r < 0)
393     printf ("-ERR %d new context failed.\r\n", -r);
394   else
395     printf ("+OK %d\r\n", r);
396
397   fflush (stdout);
398   return 0;
399 }
400
401 static int
402 do_release_context (const char *line)
403 {
404   int c_desc;
405   int r;
406   char *p;
407
408   c_desc = strtol (line+15, &p, 10);
409   r = release_context (c_desc);
410   if (r < 0)
411     printf ("-ERR %d release context failed.\r\n", -r);
412   else
413     printf ("+OK\r\n");
414
415   fflush (stdout);
416   return 0;
417 }
418
419 static int
420 do_resize_segment (const char *line)
421 {
422   char *p;
423   struct context *c;
424   int c_desc, seg_num, inc_dec, r;
425
426   c_desc = strtol (line+15, &p, 10);
427   seg_num = strtol (p+1, &p, 10);
428   inc_dec= strtol (p+1, &p, 10);
429   c = c_desc_to_context (c_desc);
430   r = resize_segment (c, seg_num, inc_dec);
431
432   if (r < 0)
433     printf ("-ERR %d resize failed.\r\n", -r);
434   else {
435     int removed, inserted;
436
437     seg_num = r;
438     removed = get_number_of_segments_removed (c, seg_num);
439     inserted = get_number_of_segments_inserted (c, seg_num);
440
441     output_segments (c, seg_num, removed, inserted);
442   }
443
444   fflush (stdout);
445   return 0;
446 }
447
448 static int
449 do_select_candidate (const char *line)
450 {
451   char *p;
452   struct context *c;
453   int c_desc, seg_num, cand_num, r;
454
455   c_desc = strtol (line+17, &p, 10);
456   seg_num = strtol (p+1, &p, 10);
457   cand_num = strtol (p+1, &p, 10);
458   c = c_desc_to_context (c_desc);
459   r = select_candidate (c, seg_num, cand_num);
460
461   if (r < 0)
462     printf ("-ERR %d select failed.\r\n", -r);
463   else {
464     int removed;
465
466     seg_num = r;
467     removed = get_number_of_segments_removed (c, seg_num);
468
469     if (removed == 0)
470       printf ("+OK\r\n");
471     else {
472       int inserted = get_number_of_segments_inserted (c, seg_num);
473
474       output_segments (c, seg_num, removed, inserted);
475     }
476   }
477
478   fflush (stdout);
479   return 0;
480 }
481
482 static int
483 do_quit (const char *line)
484 {
485   (void)line;
486   return 1;
487 }
488 \f
489 struct dispatch_table {
490   const char *command;
491   int size;
492   int (*func)(const char *line);
493 };
494
495 static struct dispatch_table dt[] = {
496   { "COMMIT", 6, do_commit },
497   { "CONVERT", 7, do_convert },
498   { "GET-CANDIDATES", 14, do_get_candidates },
499   { "NEW-CONTEXT", 11, do_new_context },
500   { "QUIT", 4, do_quit },
501   { "RELEASE-CONTEXT", 15, do_release_context },
502   { "RESIZE-SEGMENT", 14, do_resize_segment },
503   { "SELECT-CANDIDATE", 16, do_select_candidate },
504 };
505
506 static int
507 dt_cmp (const char *line, struct dispatch_table *d)
508 {
509   return strncmp (line, d->command, d->size);
510 }
511
512 #define MAX_LINE 512
513 static char line[MAX_LINE];
514
515 void egg_main (void);
516
517 void
518 egg_main (void)
519 {
520   int done = 0;
521   char *s, *p;
522
523   say_hello ();
524
525   while (!done) {
526     struct dispatch_table *d;
527
528     s = fgets (line, MAX_LINE, stdin);
529     if (s == NULL) {
530       fprintf (stderr, "null input\n");
531       break;
532     }
533     if ((p = (char *)memchr(s, '\n', MAX_LINE)) == NULL) {
534       fprintf (stderr, "no newline\n");
535       break;
536     }
537     if (p > s && *(p-1) == '\r')
538       *(p-1) = '\0';
539     else
540       *p = '\0';
541     d = (struct dispatch_table *)
542       bsearch (s, dt, 
543                sizeof (dt) / sizeof (struct dispatch_table), 
544                sizeof (struct dispatch_table), 
545                (int (*)(const void *, const void *))dt_cmp);
546     if (d != NULL)
547       done = d->func (s);
548     else
549       say_unknown ();
550   }
551 }