#include #include #include #include #include #include #include #include #include #define NO_FCGI_DEFINES #include #include #include #define ISXDIGIT(c) ( \ (c >= 48 && c <= 57) || \ ((c & ~0x20) >= 65 && (c & ~0x20) <= 70) \ ) void url_escape(char *str) { char *pr = str, *pw = str; size_t len = strlen(str); for (;;) { if (((size_t) (pr - str)) >= len) break; if (*pr == '%' && (((size_t) (pr - str))+2) < len && ISXDIGIT(*(pr+1)) && ISXDIGIT(*(pr+2))) { pr++; char hexstr[3]; hexstr[0] = *pr++; hexstr[1] = *pr++; hexstr[2] = 0; char *ptr; int ch = strtoul(hexstr, &ptr, 16); *pw++ = (char) (ch & 0x7f); } else if(*pr == '+') { *pw++ = ' '; } else { *pw++ = *pr++; } } *pw++ = 0; } static int render(PgfPGF* pgf, PgfExpr expr, GuPool* pool) { int pid; int pc[2]; /* Parent to child pipe */ int cp[2]; /* Child to parent pipe */ char ch; /* Make pipes */ if (pipe(pc) < 0) return 0; if (pipe(cp) < 0) return 0; /* Create a child to run command. */ switch (pid = fork()) { case -1: return 0; case 0: /* Child. */ dup2(cp[1], 1); /* Make stdout go to write end of pipe. */ dup2(pc[0], 0); /* Make stdin come from read end of pipe. */ close(pc[1]); close(cp[0]); char *args[] = {"dot", "-Tsvg", NULL}; execvp(args[0], args); exit(1); default: { /* Parent. */ FILE* fstream = fdopen(pc[1], "w"); GuOut* out = gu_file_out(fstream, pool); GuExn* err = gu_new_exn(NULL, gu_kind(type), pool); pgf_graphviz_abstract_tree(pgf, expr, out, err); fclose(fstream); close(cp[1]); while (read(cp[0], &ch, 1) == 1) { FCGI_putchar(ch); } return 1; } } } static void put_gu_string(GuString s) { GuWord w = s.w_; uint8_t buf[sizeof(GuWord)]; char* src; size_t len; if (w & 1) { len = (w & 0xff) >> 1; gu_assert(len <= sizeof(GuWord)); size_t i = len; while (i > 0) { w >>= 8; buf[--i] = w & 0xff; } src = (char*) buf; } else { uint8_t* p = (void*) w; len = (p[0] == 0) ? ((size_t*) p)[-1] : p[0]; src = (char*) &p[1]; } for (size_t i = 0; i < len; i++) { FCGI_putchar(src[i]); } } static void linearize(PgfConcr* concr, PgfExpr expr, GuPool* pool) { GuStringBuf* sbuf = gu_string_buf(pool); GuOut* out = gu_string_buf_out(sbuf); GuExn* err = gu_new_exn(NULL, gu_kind(type), pool); pgf_linearize(concr, expr, out, err); GuString s = gu_string_buf_freeze(sbuf, pool); put_gu_string(s); } static void print_lang(GuMapItor* fn, const void* key, void* value, GuExn* err) { PgfCId lang = *((PgfCId*) key); put_gu_string(lang); FCGI_putchar(' '); } int main () { // Create the pool that is used to allocate everything GuPool* pool = gu_new_pool(); int status = EXIT_SUCCESS; // Create an exception frame that catches all errors. GuExn* err = gu_new_exn(NULL, gu_kind(type), pool); char* grammar_path = getenv("GRAMMAR_PATH"); if (grammar_path == NULL) { fprintf(stderr, "Please set the GRAMMAR_PATH variable\n"); status = EXIT_FAILURE; goto fail; } // Read the PGF grammar. char* grammar_name = "/ParseEngAbs.pgf"; char* grammar_file = malloc(strlen(grammar_path)+strlen(grammar_name)+1); strcpy(grammar_file,grammar_path); strcat(grammar_file,grammar_name); PgfPGF* pgf = pgf_read(grammar_file, pool, err); free(grammar_file); // If an error occured, it shows in the exception frame if (!gu_ok(err)) { fprintf(stderr, "Reading PGF failed\n"); status = EXIT_FAILURE; goto fail; } // Read the extra probs. char* probs_name = "/ParseEngAbs3.probs"; char* probs_file = malloc(strlen(grammar_path)+strlen(probs_name)+1); strcpy(probs_file,grammar_path); strcat(probs_file,probs_name); pgf_load_meta_child_probs(pgf, probs_file, pool, err); free(probs_file); if (!gu_ok(err)) { fprintf(stderr, "Loading meta child probs failed\n"); status = EXIT_FAILURE; goto fail; } GuString cat = "Phr"; GuString from_lang = "ParseEng"; PgfConcr* from_concr = pgf_get_language(pgf, from_lang); if (!from_concr) { status = EXIT_FAILURE; goto fail; } // Register a callback for the literal category Symbol pgf_parser_add_literal(from_concr, "Symb", &pgf_nerc_literal_callback); while (FCGI_Accept() >= 0) { char* query = getenv("QUERY_STRING"); char sentence[202]; char to_lang_buf[51] = ""; if (query == NULL || sscanf(query, "sentence=%200[^&]&to=%50[^&]", sentence, to_lang_buf) < 1) { FCGI_printf("Status: 200 OK\r\n"); FCGI_printf("Content-type: text/plain; charset=utf-8\r\n\r\n"); GuMapItor clo = { print_lang }; pgf_iter_languages(pgf, &clo, NULL); FCGI_putchar('\n'); } else { // We create a temporary pool for translating a single // sentence, so our memory usage doesn't increase over time. GuPool* ppool = gu_new_pool(); PgfConcr* to_concr = NULL; if (strlen(to_lang_buf) > 0) { GuString to_lang = to_lang_buf; to_concr = pgf_get_language(pgf, to_lang); if (!to_concr) { status = EXIT_FAILURE; goto fail; } } url_escape(sentence); int len = strlen(sentence); sentence[len] = '\n'; sentence[len+1] = '\0'; GuReader *rdr = gu_string_reader(sentence, ppool); PgfLexer *lexer = pgf_new_simple_lexer(rdr, ppool); GuEnum* result = pgf_parse(from_concr, cat, lexer, ppool, ppool); if (result == NULL) { FCGI_printf("Status: 500 Internal Server Error\r\n"); FCGI_printf("Content-type: text/plain\r\n" "\r\n" "Parsing failed"); goto done; } PgfExprProb* ep = gu_next(result, PgfExprProb*, ppool); if (ep == NULL) { FCGI_printf("Status: 500 Internal Server Error\r\n"); FCGI_printf("Content-type: text/plain\r\n" "\r\n" "The sentence was parsed but there was no tree constructed"); goto done; } FCGI_printf("Status: 200 OK\r\n"); if (to_concr == NULL) { FCGI_printf("Content-type: image/svg+xml\r\n\r\n"); render(pgf, ep->expr, ppool); } else { FCGI_printf("Content-type: text/plain; charset=utf-8\r\n\r\n"); linearize(to_concr, ep->expr, ppool); } done: gu_pool_free(ppool); } } fail: gu_pool_free(pool); return status; }