#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// The runtime environment assumes ISO8859/1 (European including Spain)
// CH, LL, and RR are not supported glyphs so we handle them here using 2 letters
// Note that when compiled as a stand-alone .exe under DOS/Windows,
// the default DOS character set is *not* ISO8859/1 and therefore the
// accented characters will not be displayed correctly.
// Customise here:
#define TITLE "Juez Lingüístico FISE"
#define Version "1.0"
#define ENTER_WORD "Teclee la palabra por la que desea consultar"
#define SUBMIT "Consultar"
#define GOOD_WORD "PALABRA ACEPTADA"
#define BAD_WORD "PALABRA NO ACEPTADA"
#define INVALID_ENTRY "Caracteres no válidos"
#define WRONG_SIZE "La palabra consultada debe tener entre dos y quince fichas"
#define NO_ARG_GIVEN "(vacio)"
#define WORD "palabra"
#define PROGNAME "juez"
#include "FISE/FISE.h" // directory is protected. File is not available for download
#define LATIN_CAPITAL_LETTER_N_WITH_TILDE 0xD1
#define LATIN_SMALL_LETTER_N_WITH_TILDE 0xF1
#define LATIN_SMALL_LETTER_A_WITH_ACUTE 0xE1
#define LATIN_SMALL_LETTER_E_WITH_ACUTE 0xE9
#define LATIN_SMALL_LETTER_I_WITH_ACUTE 0xED
#define LATIN_SMALL_LETTER_O_WITH_ACUTE 0xF3
#define LATIN_SMALL_LETTER_U_WITH_ACUTE 0xFA
#define LATIN_SMALL_LETTER_U_WITH_DIAERESIS 0xFC
#define LATIN_SMALL_LETTER_E_WITH_GRAVE 0xE8
#define LATIN_SMALL_LETTER_I_WITH_CIRCUMFLEX 0xEE
#define LATIN_CAPITAL_LETTER_A_WITH_ACUTE 0xC1
#define LATIN_CAPITAL_LETTER_E_WITH_ACUTE 0xC9
#define LATIN_CAPITAL_LETTER_I_WITH_ACUTE 0xCD
#define LATIN_CAPITAL_LETTER_O_WITH_ACUTE 0xD3
#define LATIN_CAPITAL_LETTER_U_WITH_ACUTE 0xDA
#define LATIN_CAPITAL_LETTER_U_WITH_DIAERESIS 0xDC
#define LATIN_CAPITAL_LETTER_E_WITH_GRAVE 0xC8
#define LATIN_CAPITAL_LETTER_I_WITH_CIRCUMFLEX 0xCE
/***************************************************************************
* Web param stuff derived directly from code by Harry H. Cheng, 11/24/1995 *
****************************************************************************/
int unescapechar(char *url) {
int x = 0, y = 0;
char hex[3], *hexp=hex; /* contains xx of %xx */
for ( ; url[y]; ++x,++y) {
if (url[y] == '+') url[x] = ' '; else if(url[y] == '%') {
*hexp++ = url[++y]; *hexp++ = url[++y]; *hexp = '\0';
url[x] = strtol(hex, NULL, 16);
} else url[x] = url[y];
}
url[x] = '\0';
}
int getpairs(char ***name, char ***value)
{
int cl; /* content length */
int i, l1, l2, num=1;
char *qs, *clientinput, *token;
char **nam, **val;
if (!strcmp("POST", getenv("REQUEST_METHOD"))) {
/* POST */
if ((cl = atoi(getenv("CONTENT_LENGTH"))) == 0) return 0;
if ((clientinput = (char *) malloc(sizeof(char) * (cl+1))) == NULL) return -2;
fgets(clientinput, cl+1, stdin); /* get the client input */
} else {
/* GET */
if ((qs = getenv("QUERY_STRING")) == NULL) return 0; else {
cl = strlen(qs);
if ((clientinput = (char *) malloc(sizeof(char) * (cl+1))) == NULL) return -2;
strcpy(clientinput, qs);
}
}
token = strchr(clientinput, '&'); /* & is the name/value pair separator */
while (token != NULL) {token = strchr(++token, '&'); num++;} /* obtain the total number of pairs */
nam = (char **) malloc(sizeof(char*)*num);
val = (char **) malloc(sizeof(char*)*num);
if (nam == NULL || val == NULL) return -2;
for (i = 0, token = strtok(clientinput, "&"); token != NULL; i++) {
l1 = strlen(token); l2 = strcspn(token,"=");
nam[i] = (char *) malloc(sizeof(char) * (l2+1));
if (nam[i] == NULL) return -2;
strncpy(nam[i], token, l2);
nam[i][l2] = '\0';
unescapechar(nam[i]);
if (l1 != l2+1) { /* name=value& */
val[i] = (char *)malloc(sizeof(char) * (l1-l2));
if (val[i] == NULL) return -2;
strcpy(val[i], token+l2+1);
unescapechar(val[i]);
} else val[i] = NULL; /* special case of name=& */
token = strtok(NULL, "&");
}
free(clientinput);
*name = nam; *value = val;
return num; /* return the number of entries */
}
/*********************************************************************
* Dictionary lookup code. See also 'token.c' *
*********************************************************************/
typedef long NODE;
#define ROOT_NODE 1 /* Root of dag */
#define V_END_OF_WORD 23 /* Bit number of W */
#define M_END_OF_WORD (1L << V_END_OF_WORD)
#define V_END_OF_NODE 22 /* Bit number of N */
#define M_END_OF_NODE (1L << V_END_OF_NODE)
#define V_LETTER 24
#define M_LETTER 0xFF
#define M_NODE_POINTER 0x1FFFFFL /* Bit mask for node pointer */
int dawg_check(NODE * dict, char *word)
{
/* Simple check of word in dawg - must be exact match */
/* See http://www.gtoal.com/wordgames/spell for support code */
NODE *edge = dict + ROOT_NODE;
if (edge == dict) return (0 != 0);
for (;;) {
if (*word == (((*edge >> V_LETTER) & M_LETTER))) {
if (*++word == '\0') {
return ((*edge & M_END_OF_WORD) != 0);
} else {
if ((edge = dict + (*edge & M_NODE_POINTER)) == dict) break; else continue;
}
}
if (((*edge++) & M_END_OF_NODE) != 0) break;
}
return (0 != 0);
}
/* Support for typographical conventions used (see document entitled 'COMMENT') */
static char convert[256]; // canonicalize ISO Latin1
#define GOOD (0==0)
#define BAD (0!=0)
static char *reformat(char *tiles)
{
static char word[32];
char *wordp = word;
for (;;) {
int c = *tiles++;
if (c == '4') c = LATIN_SMALL_LETTER_N_WITH_TILDE;
*wordp = c; if (c == '\0') return word;
if (c == '1') {*wordp++ = 'c'; *wordp = 'h';}
else if (c == '2') {*wordp++ = 'l'; *wordp = 'l';}
else if (c == '3') {*wordp++ = 'r'; *wordp = 'r';}
wordp++;
}
}
static int standardize(char *inword, char *outword)
{
unsigned int c;
for (;;) {
c = (unsigned int) (*inword++) & 255U;
if (c == '\0') break;
c = convert[c]; // remove accents, convert to lower, leave
// tilde on n.
if (c == '?') return (BAD);
if ((c == 'c') && (convert[(unsigned int) (*inword) & 255U] == 'h')) { // ch
c = '1'; inword++;
} else if ((c == 'l') && (convert[(unsigned int) (*inword) & 255U] == 'l')) { // ll
c = '2'; inword++;
} else if ((c == 'r') && (convert[(unsigned int) (*inword) & 255U] == 'r')) { // rr
c = '3'; inword++;
}
*outword++ = (char) c;
}
*outword = '\0';
return (GOOD);
}
/* Main program works as both command-line (parameterised or interactive) and
web-based (standard cgi mechanism). Portable between unix (linux) and windows (dos) */
int main(int argc, char **argv)
{
static char word[32] = { '\0' };
int i;
for (i = 0; i < 256; i++) convert[i] = '?'; // rejected unless explicitly specified
for (i = 'a'; i <= 'z'; i++) convert[i] = i;
for (i = 'A'; i <= 'Z'; i++) convert[i] = tolower (i);
// K and W not present in Spanish Scrabble
convert['k'] = '?'; convert['w'] = '?';
convert['K'] = '?'; convert['W'] = '?';
// capital tilde N => lower tilde n, encoded here in 7-bit ascii as '4'
// because the dawg code may not be 8-bit clean (signed char problem? - debug later...)
convert[LATIN_CAPITAL_LETTER_N_WITH_TILDE] = '4';
convert[LATIN_SMALL_LETTER_N_WITH_TILDE] = '4';
// remove all accents and convert to lowercase
convert[LATIN_SMALL_LETTER_A_WITH_ACUTE] = 'a';
convert[LATIN_SMALL_LETTER_E_WITH_ACUTE] = 'e';
convert[LATIN_SMALL_LETTER_I_WITH_ACUTE] = 'i';
convert[LATIN_SMALL_LETTER_O_WITH_ACUTE] = 'o';
convert[LATIN_SMALL_LETTER_U_WITH_ACUTE] = 'u';
convert[LATIN_SMALL_LETTER_U_WITH_DIAERESIS] = 'u';
convert[LATIN_SMALL_LETTER_E_WITH_GRAVE] = 'e';
convert[LATIN_SMALL_LETTER_I_WITH_CIRCUMFLEX] = 'i';
convert[LATIN_CAPITAL_LETTER_A_WITH_ACUTE] = 'a';
convert[LATIN_CAPITAL_LETTER_E_WITH_ACUTE] = 'e';
convert[LATIN_CAPITAL_LETTER_I_WITH_ACUTE] = 'i';
convert[LATIN_CAPITAL_LETTER_O_WITH_ACUTE] = 'o';
convert[LATIN_CAPITAL_LETTER_U_WITH_ACUTE] = 'u';
convert[LATIN_CAPITAL_LETTER_U_WITH_DIAERESIS] = 'u';
convert[LATIN_CAPITAL_LETTER_E_WITH_GRAVE] = 'e';
convert[LATIN_CAPITAL_LETTER_I_WITH_CIRCUMFLEX] = 'i';
if (argc > 1) {
fprintf(stdout, "%s %s\n\n", TITLE, Version);
if (strcmp(argv[1], "-h") == 0) {
fprintf(stdout, "%s -h\n", PROGNAME);
fprintf(stdout, "%s\n", PROGNAME);
fprintf(stdout, "%s %s\n", PROGNAME, WORD);
fprintf(stdout, "%s %s1 %s2 ...\n", PROGNAME, WORD, WORD);
exit(0);
}
}
if (argc == 1) { // WEB or interactive command line
static char *s, line[32];
int c = 0;
if (getenv ("REQUEST_METHOD") != NULL) { /* Invoked from CGI */
/* WEB */
fprintf(stdout, "Content-type: text/html\r\n");
fprintf(stdout, "\r\n");
fprintf(stdout, "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\r\n");
fprintf(stdout, " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\r\n");
fprintf(stdout, "<html xmlns=\"http://www.w3.org/1999/xhtml\">\r\n");
fprintf(stdout, "<head>\r\n");
fprintf(stdout, "<meta http-equiv=\"Content-Type\" content=\"text/html;charset=iso-8859-1\" />\r\n");
fprintf(stdout, "<title>\r\n");
fprintf(stdout, "%s %s\r\n", TITLE, Version);
fprintf(stdout, "</title>\r\n");
fprintf(stdout, "</head>\r\n");
fprintf(stdout, "<body>\r\n");
fprintf(stdout, "<h1>%s %s</h1>\r\n", TITLE, Version);
// display form for user to enter word
fprintf(stdout, "<form action=\"/cgi-bin/%s\" method=\"post\">\r\n", PROGNAME);
fprintf(stdout, "%s: \r\n", ENTER_WORD);
fprintf(stdout, "<input type=\"text\" name=\"%s\" />\r\n", WORD);
fprintf(stdout, "<input type=\"submit\" value=\"%s\" />\r\n", SUBMIT);
fprintf(stdout, "\r\n");
fprintf(stdout, "\r\n");
fprintf(stdout, "</form>\r\n");
if (strcmp(getenv("REQUEST_METHOD"), "POST") == 0) {
int i, pairs;
char **name, **value;
char *GIVEN_ARG = NULL;
// word supplied, check it
if (pairs = getpairs(&name, &value) > 0) {
// TO DO: conversions into and out of quoted web format; hex entity refs for accented chars
for (i = 0; i < pairs; i++) if (strcmp(name[i], WORD) == 0) {GIVEN_ARG = value[i]; break;}
if ((GIVEN_ARG != NULL) && (!standardize (GIVEN_ARG, word))) {
fprintf(stdout, "<font color=\"red\">%s: %s</font>\n\n", INVALID_ENTRY, GIVEN_ARG);
} else if ((GIVEN_ARG == NULL) || (strlen (GIVEN_ARG) > 30) || (strlen (word) < 2) || (strlen (word) > 15)) {
fprintf(stdout, "<font color=\"red\">%s: %s</font>\n", WRONG_SIZE,
(GIVEN_ARG == NULL ? NO_ARG_GIVEN : GIVEN_ARG));
} else {
if (dawg_check(_token, word)) {
fprintf(stdout, "<font color=\"green\">%s: %s</font>\n", GOOD_WORD, reformat(word));
} else {
fprintf(stdout, "<font color=\"red\">%s: %s</font>\n", BAD_WORD, reformat(word));
}
}
for (i = 0; i < pairs; i++) {
if (name[i] != NULL) free(name[i]);
if (value[i] != NULL) free(value[i]);
}
if (name != NULL) free(name);
if (value != NULL) free(value);
} else {
fprintf(stdout, "Form does not seem to include the required field '%s'.\r\n", WORD);
}
}
fprintf(stdout, "</body>\r\n");
fprintf(stdout, "</html>\r\n");
} else {
/* Console/Interactive */
fprintf(stdout, "%s %s\n\n", TITLE, Version);
for (;;) {
fprintf(stderr, "%s: ", ENTER_WORD); fflush(stderr);
s = line; i = 0;
for (;;) {
int c = fgetc(stdin);
if ((c == ('Z' & 31)) || (c == ('D' & 31)) || ferror(stdin)) c = EOF;
if (c == EOF) {
fprintf(stdout, "\n\n");
exit(0);
}
if (c == '\n') break;
if ((c == '\r') || ((c == ' ') || (c == '\t'))) continue;
// allow 31 so we catch 'words' > 15 like chllrrchllrrchllrrchllrrchllrrx
if (i++ < 31) *s++ = c;
}
*s = '\0'; if (*line == '\0') continue;
if (!standardize (line, word)) {
fprintf(stdout, "\n%s: %s\n\n", INVALID_ENTRY, line);
} else if ((strlen(word) < 2) || (strlen(word) > 15)) {
fprintf(stdout, "\n%s: %s\n\n", WRONG_SIZE, reformat(word));
} else {
fprintf(stdout, "\n%s: %s\n\n", (dawg_check(_token, word) ? GOOD_WORD : BAD_WORD), reformat(word));
}
fflush(stdout);
if (c == EOF) exit (1);
}
fprintf(stdout, "\n");
}
} else {
/* Console/command-line */
for (i = 1; i < argc; i++) {
if ((strlen (argv[i]) <= 30) && (!standardize (argv[i], word))) {
// bad input (length test is so assignment to 'word' is not exceeded)
fprintf (stdout, "%s: %s\n", INVALID_ENTRY, argv[i]);
} else if ((strlen(argv[i]) > 30) || (strlen(word) < 2) || (strlen(word) > 15)) {
fprintf(stdout, "%s: %s\n", WRONG_SIZE, argv[i]);
} else fprintf(stdout, "%s: %s\n", (dawg_check(_token, word) ? GOOD_WORD : BAD_WORD), reformat(word));
}
}
exit (0);
}