/*
* Copyright (C) 2002 The Free Software Initiative of Japan
* Author: NIIBE Yutaka
*/
/*
* ANTHY Low Level Agent
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <anthy/anthy.h>
#include "config.h"
/*
* connection context
* S -- open -> [ ] -- new ----> [ ] -- convert --> [ ] --> get_candidates
* <- close -- <- release - <-- commit -- --> resize_segment
* <-- cancel -- --> select_candidate
*
*/
struct context {
struct anthy_context *ac;
int buflen;
unsigned char *buf;
int removed, inserted;
int sellen;
int *selection;
};
#define MAX_CONTEXT 16
static struct context contexts[MAX_CONTEXT];
extern int use_utf8;
#define INITIAL_BUFLEN 512
#define INITIAL_SELLEN 128
/*
* Returns -1 on error.
* Returns >= 0 on success, and the number is context descriptor.
*/
static int
new_context (void)
{
int i;
for (i = 0; i < MAX_CONTEXT; i++)
if (contexts[i].buflen == 0) { /* Found free one */
struct context *c = &contexts[i];
if ((c->buf = (unsigned char *)malloc (INITIAL_BUFLEN)) == NULL)
return -1;
if ((c->selection = (int *)malloc (INITIAL_SELLEN)) == NULL) {
free (c->buf);
c->buf = NULL;
return -1;
}
if ((c->ac = anthy_create_context ()) == NULL) {
free (c->buf);
c->buf = NULL;
free (c->selection);
c->selection = NULL;
return -1;
}
if (use_utf8) {
anthy_context_set_encoding(c->ac, ANTHY_UTF8_ENCODING);
}
c->buflen = INITIAL_BUFLEN;
c->sellen = INITIAL_SELLEN;
return i;
}
/* No free context to be used */
return -1;
}
static int
release_context (int c_desc)
{
struct context *c = &contexts[c_desc];
anthy_release_context(c->ac);
free (c->buf);
c->buf = NULL;
c->buflen = 0;
free (c->selection);
c->selection = NULL;
c->sellen = 0;
return 0;
}
static struct context *
c_desc_to_context (int c_desc)
{
return &contexts[c_desc];
}
static int
get_number_of_segments (struct context *c)
{
struct anthy_conv_stat cs;
if (anthy_get_stat(c->ac, &cs) < 0)
return -1;
return cs.nr_segment;
}
static int
begin_conversion (struct context *c, const char *input)
{
int i;
int seg_num;
if (anthy_set_string(c->ac, (char *)input) < 0)
return -1;
seg_num = get_number_of_segments (c);
if (seg_num >= c->sellen) {
c->sellen *= 2;
c->selection = realloc (c->selection, c->sellen);
if (c->selection == NULL) { /* Fatal */
c->sellen = -1;
return -1;
}
}
for (i = 0; i < seg_num; i++)
c->selection[i] = 0;
return 0;
}
static int
end_conversion (struct context *c, int cancel)
{
int n;
int seg_num;
int can_num;
if (!cancel) {
n = get_number_of_segments (c);
for (seg_num = 0; seg_num < n; seg_num++) {
can_num = c->selection[seg_num];
anthy_commit_segment(c->ac, seg_num, can_num);
}
}
return 0;
}
static int
get_segment_number_of_candidates (struct context *c, int seg_num)
{
struct anthy_segment_stat ss;
if (anthy_get_segment_stat (c->ac, seg_num, &ss) != 0)
return -1;
return ss.nr_candidate;
}
static const unsigned char *
get_segment_candidate (struct context *c, int seg_num, int cand_num)
{
int len;
while (1) {
len = anthy_get_segment (c->ac, seg_num, cand_num,
(char *)c->buf, c->buflen);
if (len < 0)
return NULL;
if (len < c->buflen)
return c->buf;
c->buflen *= 2;
c->buf = realloc (c->buf, c->buflen);
if (c->buf == NULL) { /* Fatal */
c->buflen = -1;
return NULL;
}
}
}
static const unsigned char *
get_segment_yomi (struct context *c, int seg_num)
{
return get_segment_candidate (c, seg_num, NTH_UNCONVERTED_CANDIDATE);
}
static const unsigned char *
get_segment_converted (struct context *c, int seg_num)
{
return get_segment_candidate (c, seg_num, 0);
}
static int
resize_segment (struct context *c, int seg_num, int inc_dec)
{
int i;
struct anthy_conv_stat cs;
if (anthy_get_stat(c->ac, &cs) < 0)
return -1;
/* Replace all segments after SEG_NUM */
c->removed = cs.nr_segment - seg_num;
anthy_resize_segment(c->ac, seg_num, inc_dec?-1:1);
if (anthy_get_stat(c->ac, &cs) < 0)
return -1;
c->inserted = cs.nr_segment - seg_num;
if (cs.nr_segment >= c->sellen) {
c->sellen *= 2;
c->selection = realloc (c->selection, c->sellen);
if (c->selection == NULL) { /* Fatal */
c->sellen = -1;
return -1;
}
}
for (i = seg_num; i < cs.nr_segment; i++)
c->selection[i] = 0;
return seg_num;
}
/* Only valid after call of resize_segment or select_candidate */
static int
get_number_of_segments_removed (struct context *c, int seg_num)
{
(void)seg_num;
return c->removed;
}
/* Only valid after call of resize_segment or select_candidate */
static int
get_number_of_segments_inserted (struct context *c, int seg_num)
{
(void)seg_num;
return c->inserted;
}
static int
select_candidate (struct context *c, int seg_num, int can_num)
{
/*
* Anthy does not have capability to affect the result of selection
* to other segments.
*/
c->removed = 0;
c->inserted = 0;
/*
* Record, but not call anthy_commit_segment.
*/
c->selection[seg_num] = can_num;
return seg_num;
}
static int
say_hello (void)
{
const char *options = "";
printf ("Anthy (Version %s) [%s] : Nice to meet you.\r\n", VERSION,
options);
fflush (stdout);
return 0;
}
#define ERROR_CODE_UNKNOWN 400
#define ERROR_CODE_UNSUPPOTED 401
static int
say_unknown (void)
{
printf ("-ERR %d Unknown command.\r\n", ERROR_CODE_UNKNOWN);
fflush (stdout);
return 0;
}
static int
do_commit (const char *line)
{
char *p;
struct context *c;
int c_desc, cancel, r;
c_desc = strtol (line+7, &p, 10);
c = c_desc_to_context (c_desc);
cancel = strtol (p+1, &p, 10);
r = end_conversion (c, cancel);
if (r < 0)
printf ("-ERR %d commit failed.\r\n", -r);
else
printf ("+OK\r\n");
fflush (stdout);
return 0;
}
static void
output_segments (struct context *c, int seg_num, int removed, int inserted)
{
int i;
printf ("+DATA %d %d %d\r\n", seg_num, removed, inserted);
for (i = seg_num; i < seg_num + inserted; i++) {
int nc;
nc = get_segment_number_of_candidates (c, i);
printf ("%d " ,nc);
printf ("%s ", get_segment_converted (c, i));
printf ("%s\r\n", get_segment_yomi (c, i));
}
printf ("\r\n");
}
static int
do_convert (const char *line)
{
char *p;
struct context *c;
int c_desc, r;
c_desc = strtol (line+8, &p, 10);
c = c_desc_to_context (c_desc);
r = begin_conversion (c, p+1);
if (r < 0)
printf ("-ERR %d convert failed.\r\n", -r);
else
{
int n = get_number_of_segments (c);
output_segments (c, 0, 0, n);
}
fflush (stdout);
return 0;
}
static int
do_get_candidates (const char *line)
{
char *p;
struct context *c;
int c_desc, seg_num, cand_offset, max_cands;
int nc, i, max;
c_desc = strtol (line+15, &p, 10);
seg_num = strtol (p+1, &p, 10);
cand_offset = strtol (p+1, &p, 10);
max_cands = strtol (p+1, &p, 10);
c = c_desc_to_context (c_desc);
nc = get_segment_number_of_candidates (c, seg_num);
max = cand_offset + max_cands;
if (nc < cand_offset + max_cands)
max = nc;
printf ("+DATA %d %d\r\n", cand_offset, max);
for (i = cand_offset; i < max; i++)
printf ("%s\r\n", get_segment_candidate (c, seg_num, i));
printf ("\r\n");
fflush (stdout);
return 0;
}
static int
do_new_context (const char *line)
{
int r;
/* XXX: Should check arguments */
if (strncmp (" INPUT=#18 OUTPUT=#18", line+11, 20) != 0) {
printf ("-ERR %d unsupported context\r\n", ERROR_CODE_UNSUPPOTED);
return 1;
}
r = new_context ();
if (r < 0)
printf ("-ERR %d new context failed.\r\n", -r);
else
printf ("+OK %d\r\n", r);
fflush (stdout);
return 0;
}
static int
do_release_context (const char *line)
{
int c_desc;
int r;
char *p;
c_desc = strtol (line+15, &p, 10);
r = release_context (c_desc);
if (r < 0)
printf ("-ERR %d release context failed.\r\n", -r);
else
printf ("+OK\r\n");
fflush (stdout);
return 0;
}
static int
do_resize_segment (const char *line)
{
char *p;
struct context *c;
int c_desc, seg_num, inc_dec, r;
c_desc = strtol (line+15, &p, 10);
seg_num = strtol (p+1, &p, 10);
inc_dec= strtol (p+1, &p, 10);
c = c_desc_to_context (c_desc);
r = resize_segment (c, seg_num, inc_dec);
if (r < 0)
printf ("-ERR %d resize failed.\r\n", -r);
else {
int removed, inserted;
seg_num = r;
removed = get_number_of_segments_removed (c, seg_num);
inserted = get_number_of_segments_inserted (c, seg_num);
output_segments (c, seg_num, removed, inserted);
}
fflush (stdout);
return 0;
}
static int
do_select_candidate (const char *line)
{
char *p;
struct context *c;
int c_desc, seg_num, cand_num, r;
c_desc = strtol (line+17, &p, 10);
seg_num = strtol (p+1, &p, 10);
cand_num = strtol (p+1, &p, 10);
c = c_desc_to_context (c_desc);
r = select_candidate (c, seg_num, cand_num);
if (r < 0)
printf ("-ERR %d select failed.\r\n", -r);
else {
int removed;
seg_num = r;
removed = get_number_of_segments_removed (c, seg_num);
if (removed == 0)
printf ("+OK\r\n");
else {
int inserted = get_number_of_segments_inserted (c, seg_num);
output_segments (c, seg_num, removed, inserted);
}
}
fflush (stdout);
return 0;
}
static int
do_quit (const char *line)
{
(void)line;
return 1;
}
struct dispatch_table {
const char *command;
int size;
int (*func)(const char *line);
};
static struct dispatch_table dt[] = {
{ "COMMIT", 6, do_commit },
{ "CONVERT", 7, do_convert },
{ "GET-CANDIDATES", 14, do_get_candidates },
{ "NEW-CONTEXT", 11, do_new_context },
{ "QUIT", 4, do_quit },
{ "RELEASE-CONTEXT", 15, do_release_context },
{ "RESIZE-SEGMENT", 14, do_resize_segment },
{ "SELECT-CANDIDATE", 16, do_select_candidate },
};
static int
dt_cmp (const char *line, struct dispatch_table *d)
{
return strncmp (line, d->command, d->size);
}
#define MAX_LINE 512
static char line[MAX_LINE];
void egg_main (void);
void
egg_main (void)
{
int done = 0;
char *s, *p;
say_hello ();
while (!done) {
struct dispatch_table *d;
s = fgets (line, MAX_LINE, stdin);
if (s == NULL) {
fprintf (stderr, "null input\n");
break;
}
if ((p = (char *)memchr(s, '\n', MAX_LINE)) == NULL) {
fprintf (stderr, "no newline\n");
break;
}
if (p > s && *(p-1) == '\r')
*(p-1) = '\0';
else
*p = '\0';
d = (struct dispatch_table *)
bsearch (s, dt,
sizeof (dt) / sizeof (struct dispatch_table),
sizeof (struct dispatch_table),
(int (*)(const void *, const void *))dt_cmp);
if (d != NULL)
done = d->func (s);
else
say_unknown ();
}
}
syntax highlighted by Code2HTML, v. 0.9.1