summaryrefslogtreecommitdiff
path: root/src/tools/hunspell.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/hunspell.cxx')
-rw-r--r--src/tools/hunspell.cxx1785
1 files changed, 1785 insertions, 0 deletions
diff --git a/src/tools/hunspell.cxx b/src/tools/hunspell.cxx
new file mode 100644
index 0000000..07ad6bb
--- /dev/null
+++ b/src/tools/hunspell.cxx
@@ -0,0 +1,1785 @@
+// glibc < 3.0 (for mkstemp)
+#ifndef __USE_MISC
+#define __USE_MISC
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "config.h"
+#include "hunspell.hxx"
+#include "csutil.hxx"
+
+#ifndef HUNSPELL_EXTRA
+#define suggest_auto suggest
+#endif
+
+#define HUNSPELL_VERSION VERSION
+#define INPUTLEN 50
+
+#define HUNSPELL_PIPE_HEADING "@(#) International Ispell Version 3.2.06 (but really Hunspell "VERSION")\n"
+#define HUNSPELL_HEADING "Hunspell "
+
+//for debugging only
+//#define LOG
+
+#define DEFAULTDICNAME "default"
+
+#ifdef WIN32
+
+#define LIBDIR "C:\\Hunspell\\"
+#define USEROOODIR "Application Data\\OpenOffice.org 2\\user\\wordbook"
+#define OOODIR \
+ "C:\\Program files\\OpenOffice.org 2.4\\share\\dict\\ooo\\;" \
+ "C:\\Program files\\OpenOffice.org 2.3\\share\\dict\\ooo\\;" \
+ "C:\\Program files\\OpenOffice.org 2.2\\share\\dict\\ooo\\;" \
+ "C:\\Program files\\OpenOffice.org 2.1\\share\\dict\\ooo\\;" \
+ "C:\\Program files\\OpenOffice.org 2.0\\share\\dict\\ooo\\"
+#define HOME "%USERPROFILE%\\"
+#define DICBASENAME "hunspell_"
+#define LOGFILE "C:\\Hunspell\\log"
+#define DIRSEPCH '\\'
+#define DIRSEP "\\"
+#define PATHSEP ";"
+
+#include "textparser.hxx"
+#include "htmlparser.hxx"
+#include "latexparser.hxx"
+#include "manparser.hxx"
+#include "firstparser.hxx"
+
+#else
+
+// Not Windows
+#include <sys/types.h>
+#include <dirent.h>
+#include <unistd.h>
+#include "textparser.hxx"
+#include "htmlparser.hxx"
+#include "latexparser.hxx"
+#include "manparser.hxx"
+#include "firstparser.hxx"
+
+#define LIBDIR \
+ "/usr/share/hunspell:" \
+ "/usr/share/myspell:" \
+ "/usr/share/myspell/dicts:" \
+ "/Library/Spelling"
+#define USEROOODIR \
+ ".openoffice.org/3/user/wordbook:" \
+ ".openoffice.org2/user/wordbook:" \
+ ".openoffice.org2.0/user/wordbook:" \
+ "Library/Spelling"
+#define OOODIR \
+ "/opt/openoffice.org/basis3.0/share/dict/ooo:" \
+ "/usr/lib/openoffice.org/basis3.0/share/dict/ooo:" \
+ "/opt/openoffice.org2.4/share/dict/ooo:" \
+ "/usr/lib/openoffice.org2.4/share/dict/ooo:" \
+ "/opt/openoffice.org2.3/share/dict/ooo:" \
+ "/usr/lib/openoffice.org2.3/share/dict/ooo:" \
+ "/opt/openoffice.org2.2/share/dict/ooo:" \
+ "/usr/lib/openoffice.org2.2/share/dict/ooo:" \
+ "/opt/openoffice.org2.1/share/dict/ooo:" \
+ "/usr/lib/openoffice.org2.1/share/dict/ooo:" \
+ "/opt/openoffice.org2.0/share/dict/ooo:" \
+ "/usr/lib/openoffice.org2.0/share/dict/ooo"
+#define HOME getenv("HOME")
+#define DICBASENAME ".hunspell_"
+#define LOGFILE "/tmp/hunspell.log"
+#define DIRSEPCH '/'
+#define DIRSEP "/"
+#define PATHSEP ":"
+#endif
+
+#ifdef HAVE_ICONV
+#include <iconv.h>
+char text_conv[MAXLNLEN];
+#endif
+
+#if ENABLE_NLS
+# ifdef HAVE_LOCALE_H
+# include <locale.h>
+# ifdef HAVE_LANGINFO_CODESET
+# include <langinfo.h>
+# endif
+# endif
+# ifdef HAVE_LIBINTL_H
+# include <libintl.h>
+# else
+# include <../../intl/libintl.h>
+# endif
+#else
+# define gettext
+# undef HAVE_LOCALE_H
+# undef HAVE_LIBINTL_H
+#endif
+
+#ifdef HAVE_CURSES_H
+#ifdef HAVE_NCURSESW_H
+#include <ncurses.h>
+#else
+#include <curses.h>
+#endif
+#endif
+
+#ifdef HAVE_READLINE
+#include <readline/readline.h>
+#else
+#define readline scanline
+#endif
+
+#define TEMPNAME "hunSPELL.bak"
+
+extern char * mystrdup(const char * s);
+
+// file formats:
+
+enum { FMT_TEXT, FMT_LATEX, FMT_HTML, FMT_MAN, FMT_FIRST };
+
+struct wordlist {
+ char * word;
+ wordlist * next;
+};
+
+// global variables
+
+char * wordchars = NULL;
+char * dicpath = NULL;
+int wordchars_len;
+unsigned short * wordchars_utf16 = NULL;
+int wordchars_utf16_free = 0;
+int wordchars_utf16_len;
+char * dicname = NULL;
+char * privdicname = NULL;
+const char * currentfilename = NULL;
+
+int modified; // modified file sign
+enum { NORMAL,
+ BADWORD, // print only bad words
+ WORDFILTER, // print only bad words from 1 word/line input
+ BADLINE, // print only lines with bad words
+ STEM, // stem input words
+ ANALYZE, // analyze input words
+ PIPE, // print only stars for LyX compatibility
+ AUTO0, // search typical error (based on SuggestMgr::suggest_auto())
+ AUTO, // automatic spelling to standard output
+ AUTO2, // automatic spelling to standard output with sed log
+ AUTO3 }; // automatic spelling to standard output with gcc error format
+int filter_mode = NORMAL;
+int printgood = 0; // print only good words and lines
+int showpath = 0; // show detected path of the dictionary
+int checkurl = 0; // check URLs and mail addresses
+int warn = 0; // warn potential mistakes (dictionary words with WARN flags)
+const char * ui_enc = NULL; // locale character encoding (default for I/O)
+const char * io_enc = NULL; // I/O character encoding
+
+#define DMAX 10 // maximal count of loaded dictionaries
+
+const char * dic_enc[DMAX]; // dictionary encoding
+char * path = NULL;
+int dmax = 0; // dictionary count
+
+// functions
+
+#ifdef HAVE_ICONV
+static const char* fix_encoding_name(const char *enc)
+{
+ if (strcmp(enc, "TIS620-2533") == 0)
+ enc = "TIS620";
+ return enc;
+}
+#endif
+
+/* change character encoding */
+char * chenc(char * st, const char * enc1, const char * enc2) {
+ char * out = st;
+#ifdef HAVE_ICONV
+ if (enc1 && enc2 && strcmp(enc1, enc2) != 0) {
+
+ size_t c1 = strlen(st) + 1;
+ size_t c2 = MAXLNLEN;
+ char * source = st;
+ char * dest = text_conv;
+ iconv_t conv = iconv_open(fix_encoding_name(enc2), fix_encoding_name(enc1));
+ if (conv == (iconv_t) -1) {
+ fprintf(stderr, gettext("error - iconv_open: %s -> %s\n"), enc2, enc1);
+ } else {
+ size_t res = iconv(conv, (ICONV_CONST char **) &source, &c1, &dest, &c2);
+ iconv_close(conv);
+ if (res != (size_t) -1) out = text_conv;
+ }
+ }
+#endif
+ return out;
+}
+
+TextParser * get_parser(int format, char * extension, Hunspell * pMS) {
+ TextParser * p = NULL;
+ int io_utf8 = 0;
+ char * denc = pMS->get_dic_encoding();
+#ifdef HAVE_ICONV
+ initialize_utf_tbl(); // also need for 8-bit tokenization
+ if (io_enc) {
+ if ((strcmp(io_enc, "UTF-8") == 0) ||
+ (strcmp(io_enc, "utf-8") == 0) ||
+ (strcmp(io_enc, "UTF8") == 0) ||
+ (strcmp(io_enc, "utf8") == 0)) {
+ io_utf8 = 1;
+ io_enc = "UTF-8";
+ }
+ } else if (ui_enc) {
+ io_enc = ui_enc;
+ if (strcmp(ui_enc, "UTF-8") == 0) io_utf8 = 1;
+ } else {
+ io_enc = denc;
+ if (strcmp(denc, "UTF-8") == 0) io_utf8 = 1;
+ }
+
+ if (io_utf8) {
+ wordchars_utf16 = pMS->get_wordchars_utf16(&wordchars_utf16_len);
+ if ((strcmp(denc, "UTF-8") != 0) && pMS->get_wordchars()) {
+ char * wchars = (char *) pMS->get_wordchars();
+ int wlen = strlen(wchars);
+ size_t c1 = wlen;
+ size_t c2 = MAXLNLEN;
+ char * dest = text_conv;
+ iconv_t conv = iconv_open("UTF-8", fix_encoding_name(denc));
+ if (conv == (iconv_t) -1) {
+ fprintf(stderr, gettext("error - iconv_open: UTF-8 -> %s\n"), denc);
+ wordchars_utf16 = NULL;
+ wordchars_utf16_len = 0;
+ } else {
+ iconv(conv, (ICONV_CONST char **) &wchars, &c1, &dest, &c2);
+ iconv_close(conv);
+ wordchars_utf16 = (unsigned short *) malloc(sizeof(unsigned short) * wlen);
+ int n = u8_u16((w_char *) wordchars_utf16, wlen, text_conv);
+ if (n > 0) flag_qsort(wordchars_utf16, 0, n);
+ wordchars_utf16_len = n;
+ wordchars_utf16_free = 1;
+ }
+ }
+ } else {
+ // 8-bit input encoding
+ // detect letters by unicodeisalpha() for tokenization
+ char letters[MAXLNLEN];
+ char * pletters = letters;
+ char ch[2];
+ char u8[10];
+ *pletters = '\0';
+ iconv_t conv = iconv_open("UTF-8", fix_encoding_name(io_enc));
+ if (conv == (iconv_t) -1) {
+ fprintf(stderr, gettext("error - iconv_open: UTF-8 -> %s\n"), io_enc);
+ } else {
+ for (int i = 32; i < 256; i++) {
+ size_t c1 = 1;
+ size_t c2 = 10;
+ char * dest = u8;
+ u8[0] = '\0';
+ char * ch8bit = ch;
+ ch[0] = (char) i;
+ ch[1] = '\0';
+ size_t res = iconv(conv, (ICONV_CONST char **) &ch8bit, &c1, &dest, &c2);
+ if (res != (size_t) -1) {
+ unsigned short idx;
+ w_char w;
+ w.l = 0;
+ w.h = 0;
+ u8_u16(&w, 1, u8);
+ idx = (w.h << 8) + w.l;
+ if (unicodeisalpha(idx)) {
+ *pletters = (char) i;
+ pletters++;
+ }
+ }
+ }
+ iconv_close(conv);
+ }
+ *pletters = '\0';
+
+ // UTF-8 wordchars -> 8 bit wordchars
+ int len = 0;
+ char * wchars = (char *) pMS->get_wordchars();
+ if (wchars) {
+ if ((strcmp(denc, "UTF-8")==0)) {
+ pMS->get_wordchars_utf16(&len);
+ } else {
+ len = strlen(wchars);
+ }
+ char * dest = letters + strlen(letters); // append wordchars
+ size_t c1 = len + 1;
+ size_t c2 = len + 1;
+ iconv_t conv = iconv_open(fix_encoding_name(io_enc), fix_encoding_name(denc));
+ if (conv == (iconv_t) -1) {
+ fprintf(stderr, gettext("error - iconv_open: %s -> %s\n"), io_enc, denc);
+ } else {
+ iconv(conv, (ICONV_CONST char **) &wchars, &c1, &dest, &c2);
+ iconv_close(conv);
+ *dest = '\0';
+ }
+ }
+ if (*letters) wordchars = mystrdup(letters);
+ }
+#else
+ if (strcmp(denc, "UTF-8") == 0) {
+ wordchars_utf16 = pMS->get_wordchars_utf16(&wordchars_utf16_len);
+ io_utf8 = 1;
+ } else {
+ char * casechars = get_casechars(denc);
+ wordchars = (char *) pMS->get_wordchars();
+ if (casechars && wordchars) {
+ casechars = (char *) realloc(casechars, strlen(casechars) + strlen(wordchars) + 1);
+ strcat(casechars, wordchars);
+ }
+ wordchars = casechars;
+ }
+ io_enc = denc;
+#endif
+
+ if (io_utf8) {
+ switch (format) {
+ case FMT_LATEX: p = new LaTeXParser(wordchars_utf16, wordchars_utf16_len); break;
+ case FMT_HTML: p = new HTMLParser(wordchars_utf16, wordchars_utf16_len); break;
+ case FMT_MAN: p = new ManParser(wordchars_utf16, wordchars_utf16_len); break;
+ case FMT_FIRST: p = new FirstParser(wordchars);
+ }
+ } else {
+ switch (format) {
+ case FMT_LATEX: p = new LaTeXParser(wordchars); break;
+ case FMT_HTML: p = new HTMLParser(wordchars); break;
+ case FMT_MAN: p = new ManParser(wordchars); break;
+ case FMT_FIRST: p = new FirstParser(wordchars);
+ }
+ }
+
+ if ((!p) && (extension)) {
+ if ((strcmp(extension, "html") == 0) ||
+ (strcmp(extension, "htm") == 0) ||
+ (strcmp(extension, "xml") == 0)) {
+ if (io_utf8) {
+ p = new HTMLParser(wordchars_utf16, wordchars_utf16_len);
+ } else {
+ p = new HTMLParser(wordchars);
+ }
+ } else if (((extension[0] > '0') && (extension[0] <= '9'))) {
+ if (io_utf8) {
+ p = new ManParser(wordchars_utf16, wordchars_utf16_len);
+ } else {
+ p = new ManParser(wordchars);
+ }
+ } else if ((strcmp(extension, "tex") == 0)) {
+ if (io_utf8) {
+ p = new LaTeXParser(wordchars_utf16, wordchars_utf16_len);
+ } else {
+ p = new LaTeXParser(wordchars);
+ }
+ }
+ }
+ if (!p) {
+ if (io_utf8) {
+ p = new TextParser(wordchars_utf16, wordchars_utf16_len);
+ } else {
+ p = new TextParser(wordchars);
+ }
+ }
+ p->set_url_checking(checkurl);
+ return p;
+}
+
+
+#ifdef LOG
+void log(char * message)
+{
+ FILE *f = fopen(LOGFILE,"a");
+ if (f) {
+ fprintf(f,"%s\n",message);
+ fclose(f);
+ } else {
+ fprintf(stderr,"Logfile...");
+ }
+}
+#endif
+
+int putdic(char * word, Hunspell * pMS)
+{
+ char * w;
+
+ word = chenc(word, ui_enc, dic_enc[0]);
+
+ if (((w = strstr(word + 1, "/")) == NULL)) {
+ if (*word == '*') return pMS->remove(word + 1);
+ else return pMS->add(word);
+ } else {
+ char c;
+ int ret;
+ c = *w;
+ *w = '\0';
+ if (*(w+1) == '/') {
+ ret = pMS->add_with_affix(word, w + 2); // word//pattern (back comp.)
+ } else {
+ ret = pMS->add_with_affix(word, w + 1); // word/pattern
+ }
+ *w = c;
+ return ret;
+ }
+}
+
+void load_privdic(char * filename, Hunspell * pMS)
+{
+ char buf[MAXLNLEN];
+ FILE *dic = fopen(filename,"r");
+ if (dic) {
+ while(fgets(buf,MAXLNLEN,dic)) {
+ if (*(buf + strlen(buf) - 1) == '\n') *(buf + strlen(buf) - 1) = '\0';
+ putdic(buf,pMS);
+ }
+ fclose(dic);
+ }
+}
+
+int exist(char * filename)
+{
+ FILE *f = fopen(filename,"r");
+ if (f) {
+ fclose(f);
+ return 1;
+ }
+ return 0;
+}
+
+int save_privdic(char * filename, char * filename2, wordlist * w)
+{
+ wordlist * r;
+ FILE *dic = fopen(filename,"r");
+ if (dic) {
+ fclose(dic);
+ dic = fopen(filename,"a");
+ } else {
+ dic = fopen(filename2,"a");
+ }
+ if (! dic) return 0;
+ while (w != NULL) {
+ char *word = chenc(w->word, io_enc, ui_enc);
+ fprintf(dic,"%s\n",word);
+#ifdef LOG
+ log(word);log("\n");
+#endif
+ r = w;
+ free(w->word);
+ w = w->next;
+ free(r);
+ }
+ fclose(dic);
+ return 1;
+}
+
+char * basename(char * s, char c) {
+ char * p = s + strlen(s);
+ while ((*p != c) && (p != s)) p--;
+ if (*p == c) p++;
+ return p;
+}
+
+#ifdef HAVE_CURSES_H
+char * scanline(char * message) {
+ char input[INPUTLEN];
+ printw(message);
+ echo();
+ getnstr(input, INPUTLEN);
+ noecho();
+ return mystrdup(input);
+}
+#endif
+
+// check words in the dictionaries (and set first checked dictionary)
+int check(Hunspell ** pMS, int * d, char * token, int * info, char ** root) {
+ for (int i = 0; i < dmax; i++) {
+ if (pMS[*d]->spell(chenc(token, io_enc, dic_enc[*d]), info, root) && !(warn && (*info & SPELL_WARN))) {
+ return 1;
+ }
+ if (++(*d) == dmax) *d = 0;
+ }
+ return 0;
+}
+
+void pipe_interface(Hunspell ** pMS, int format, FILE * fileid) {
+ char buf[MAXLNLEN];
+ char * buf2;
+ wordlist * dicwords = NULL;
+ char * token;
+ int pos;
+ int bad;
+ int lineno = 0;
+ int terse_mode = 0;
+ int verbose_mode = 0;
+ int d = 0;
+
+ TextParser * parser = get_parser(format, NULL, pMS[0]);
+
+ if ((filter_mode == NORMAL)) {
+ fprintf(stdout,gettext(HUNSPELL_HEADING));
+ fprintf(stdout,HUNSPELL_VERSION);
+ if (pMS[0]->get_version()) fprintf(stdout," - %s", pMS[0]->get_version());
+ fprintf(stdout,"\n");
+ fflush(stdout);
+ }
+
+nextline: while(fgets(buf, MAXLNLEN, fileid)) {
+ if (*(buf + strlen(buf) - 1) == '\n') *(buf + strlen(buf) - 1) = '\0';
+ lineno++;
+#ifdef LOG
+ log(buf);
+#endif
+ bad = 0;
+ pos = 0;
+
+ // execute commands
+ if (filter_mode == PIPE) {
+ pos = -1;
+ switch (buf[0]) {
+ case '%': { verbose_mode = terse_mode = 0; break; }
+ case '!': { terse_mode = 1; break; }
+ case '`': { verbose_mode = 1; break; }
+ case '+': {
+ delete parser;
+ parser = get_parser(FMT_LATEX, NULL, pMS[0]);
+ parser->set_url_checking(checkurl);
+ break;
+ }
+ case '-': {
+ delete parser;
+ parser = get_parser(format, NULL, pMS[0]);
+ break;
+ }
+ case '@': { putdic(buf+1, pMS[d]); break; }
+ case '*': {
+ struct wordlist* i =
+ (struct wordlist *) malloc (sizeof(struct wordlist));
+ i->word = mystrdup(buf+1);
+ i->next = dicwords;
+ dicwords = i;
+ putdic(buf+1, pMS[d]);
+ break;
+ }
+ case '#': {
+ if (HOME) strcpy(buf,HOME); else {
+ fprintf(stderr, gettext("error - missing HOME variable\n"));
+ continue;
+ }
+#ifndef WIN32
+ strcat(buf,"/");
+#endif
+ buf2 = buf+strlen(buf);
+ if (!privdicname) {
+ strcat(buf,DICBASENAME);
+ strcat(buf,basename(dicname,DIRSEPCH));
+ } else {
+ strcat(buf,privdicname);
+ }
+ if (save_privdic(buf2, buf, dicwords)) {
+ dicwords=NULL;
+ }
+ break;
+ }
+ case '^': {
+ pos = 1;
+ }
+
+ default: {
+ pos = 0;
+ }
+
+ } // end switch
+ } // end filter_mode == PIPE
+
+if (pos >= 0) {
+ parser->put_line(buf + pos);
+ while ((token = parser->next_token())) {
+ switch (filter_mode) {
+
+ case BADWORD: {
+ if (!check(pMS, &d, token, NULL, NULL)) {
+ bad = 1;
+ if (! printgood) fprintf(stdout,"%s\n", token);
+ } else {
+ if (printgood) fprintf(stdout,"%s\n", token);
+ }
+ free(token);
+ continue;
+ }
+
+ case WORDFILTER: {
+ if (!check(pMS, &d, token, NULL, NULL)) {
+ bad = 1;
+ if (! printgood) fprintf(stdout,"%s\n", buf);
+ } else {
+ if (printgood) fprintf(stdout,"%s\n", buf);
+ }
+ free(token);
+ goto nextline;
+ }
+
+ case BADLINE: {
+ if (!check(pMS, &d, token, NULL, NULL)) {
+ bad = 1;
+ }
+ free(token);
+ continue;
+ }
+
+ case AUTO0:
+ case AUTO:
+ case AUTO2:
+ case AUTO3: {
+ FILE * f = (filter_mode == AUTO) ? stderr : stdout;
+ if (!check(pMS, &d, token, NULL, NULL)) {
+ char ** wlst = NULL;
+ bad = 1;
+ int ns = pMS[d]->suggest_auto(&wlst, chenc(token, io_enc, dic_enc[d]));
+ if (ns > 0) {
+ parser->change_token(chenc(wlst[0], dic_enc[d], io_enc));
+ if (filter_mode == AUTO3) {
+ fprintf(f,"%s:%d: Locate: %s | Try: %s\n",
+ currentfilename, lineno,
+ token, chenc(wlst[0], dic_enc[d], io_enc));
+ } else if (filter_mode == AUTO2) {
+ fprintf(f,"%ds/%s/%s/g; # %s\n", lineno,
+ token, chenc(wlst[0], dic_enc[d], io_enc), buf);
+ } else {
+ fprintf(f,gettext("Line %d: %s -> "), lineno,
+ chenc(token, io_enc, ui_enc));
+ fprintf(f, "%s\n",
+ chenc(wlst[0], dic_enc[d], ui_enc));
+ }
+ }
+ pMS[d]->free_list(&wlst, ns);
+ }
+ free(token);
+ continue;
+ }
+
+ case STEM: {
+ char ** result;
+ int n = pMS[d]->stem(&result, chenc(token, io_enc, dic_enc[d]));
+ for (int i = 0; i < n; i++) {
+ fprintf(stdout, "%s %s\n", token, chenc(result[i], dic_enc[d], ui_enc));
+ }
+ pMS[d]->free_list(&result, n);
+ if (n == 0 && token[strlen(token) - 1] == '.') {
+ token[strlen(token) - 1] = '\0';
+ n = pMS[d]->stem(&result, token);
+ for (int i = 0; i < n; i++) {
+ fprintf(stdout, "%s %s\n", token, chenc(result[i], dic_enc[d], ui_enc));
+ }
+ pMS[d]->free_list(&result, n);
+ }
+ if (n == 0) fprintf(stdout, "%s\n", chenc(token, dic_enc[d], ui_enc));
+ fprintf(stdout, "\n");
+ free(token);
+ continue;
+ }
+
+ case ANALYZE: {
+ char ** result;
+ int n = pMS[d]->analyze(&result, chenc(token, io_enc, dic_enc[d]));
+ for (int i = 0; i < n; i++) {
+ fprintf(stdout, "%s %s\n", token, chenc(result[i], dic_enc[d], ui_enc));
+ }
+ pMS[d]->free_list(&result, n);
+ if (n == 0 && token[strlen(token) - 1] == '.') {
+ token[strlen(token) - 1] = '\0';
+ n = pMS[d]->analyze(&result, token);
+ for (int i = 0; i < n; i++) {
+ fprintf(stdout, "%s %s\n", token, chenc(result[i], dic_enc[d], ui_enc));
+ }
+ pMS[d]->free_list(&result, n);
+ }
+ if (n == 0) fprintf(stdout, "%s\n", chenc(token, dic_enc[d], ui_enc));
+ fprintf(stdout, "\n");
+ free(token);
+ continue;
+ }
+
+ case PIPE: {
+ int info;
+ char * root = NULL;
+ if (check(pMS, &d, token, &info, &root)) {
+ if (!terse_mode) {
+ if (verbose_mode) fprintf(stdout,"* %s\n", token);
+ else fprintf(stdout,"*\n");
+ }
+ fflush(stdout);
+ } else {
+ char ** wlst = NULL;
+ int ns = pMS[d]->suggest(&wlst, token);
+ if (ns == 0) {
+ fprintf(stdout,"# %s %d", token,
+ parser->get_tokenpos() + pos);
+ } else {
+ fprintf(stdout,"& %s %d %d: ", token, ns,
+ parser->get_tokenpos() + pos);
+ fprintf(stdout,"%s", chenc(wlst[0], dic_enc[d], io_enc));
+ }
+ for (int j = 1; j < ns; j++) {
+ fprintf(stdout, ", %s", chenc(wlst[j], dic_enc[d], io_enc));
+ }
+ pMS[d]->free_list(&wlst, ns);
+ fprintf(stdout, "\n");
+ fflush(stdout);
+ }
+ if (root) free(root);
+ free(token);
+ continue;
+ }
+ case NORMAL: {
+ int info;
+ char * root = NULL;
+ if (check(pMS, &d, token, &info, &root)) {
+ if (info & SPELL_COMPOUND) {
+ fprintf(stdout,"-\n");
+ } else if (root) {
+ fprintf(stdout,"+ %s\n", chenc(root, dic_enc[d], ui_enc));
+ } else {
+ fprintf(stdout,"*\n");
+ }
+ fflush(stdout);
+ if (root) free(root);
+ } else {
+ char ** wlst = NULL;
+ int ns = pMS[d]->suggest(&wlst, chenc(token, io_enc, dic_enc[d]));
+ if (ns == 0) {
+ fprintf(stdout,"# %s %d", chenc(token, io_enc, ui_enc),
+ parser->get_tokenpos() + pos);
+ } else {
+ fprintf(stdout,"& %s %d %d: ", chenc(token, io_enc, ui_enc), ns,
+ parser->get_tokenpos() + pos);
+ fprintf(stdout,"%s", chenc(wlst[0], dic_enc[d], ui_enc));
+ }
+ for (int j = 1; j < ns; j++) {
+ fprintf(stdout, ", %s", chenc(wlst[j], dic_enc[d], ui_enc));
+ }
+ pMS[d]->free_list(&wlst, ns);
+ fprintf(stdout, "\n");
+ fflush(stdout);
+ }
+ free(token);
+ }
+ }
+ }
+
+ switch (filter_mode) {
+ case AUTO: {
+ fprintf(stdout,"%s\n", parser->get_line());
+ break;
+ }
+
+ case BADLINE: {
+ if (((printgood) && (!bad)) ||
+ (!printgood && (bad))) fprintf(stdout,"%s\n",buf);
+ break;
+ }
+
+ case PIPE:
+ case NORMAL: {
+ fprintf(stdout,"\n");
+ fflush(stdout);
+ break;
+ }
+
+ }
+} // if
+} // while
+
+if (parser) delete(parser);
+
+} // pipe_interface
+
+#ifdef HAVE_READLINE
+
+#ifdef HAVE_CURSES_H
+static const char * rltext;
+
+// set base text of input line
+static int set_rltext ()
+{
+ if (rltext)
+ {
+ rl_insert_text (rltext);
+ rltext = NULL;
+ rl_startup_hook = (rl_hook_func_t *)NULL;
+ }
+ return 0;
+}
+
+#endif
+
+// Readline escape
+static int rl_escape (int count, int key)
+{
+ rl_delete_text(0, rl_end);
+ rl_done = 1;
+ return 0;
+}
+#endif
+
+#ifdef HAVE_CURSES_H
+int expand_tab(char * dest, char * src, int limit) {
+ int i = 0;
+ int u8 = ((ui_enc != NULL) && (strcmp(ui_enc, "UTF-8") == 0)) ? 1 : 0;
+ int chpos = 0;
+ for(int j = 0; (i < limit) && (src[j] != '\0') && (src[j] != '\r'); j++) {
+ dest[i] = src[j];
+ if (src[j] == '\t') {
+ int end = 8 - (chpos % 8);
+ for(int k = 0; k < end; k++) {
+ dest[i] = ' ';
+ i++;
+ chpos++;
+ }
+ } else {
+ i++;
+ if (!u8 || (src[j] & 0xc0) != 0x80) chpos++;
+ }
+ }
+ dest[i] = '\0';
+ return chpos;
+}
+
+// UTF-8-aware version of strncpy (but output is always null terminated)
+// What we should deal in is cursor position cells in a terminal emulator,
+// i.e. the number of visual columns occupied like wcwidth/wcswidth does
+// What we're really current doing is to deal in the number of characters,
+// like mbstowcs which isn't quite correct, but close enough for western
+// text in UTF-8
+void strncpyu8(char * dest, const char * src, int begin, int n) {
+ int u8 = ((ui_enc != NULL) && (strcmp(ui_enc, "UTF-8") == 0)) ? 1 : 0;
+ int i = 0;
+ while (i < begin + n) {
+ if (i >= begin)
+ {
+ if (!*src)
+ break;
+ *dest++ = *src;
+ }
+ if (!u8 || (*src & 0xc0) != 0x80)
+ i++;
+ ++src;
+ }
+ *dest = '\0';
+}
+
+//See strncpyu8 for gotchas
+int strlenu8(const char * src) {
+ int u8 = ((ui_enc != NULL) && (strcmp(ui_enc, "UTF-8") == 0)) ? 1 : 0;
+ int i = 0;
+ while (*src) {
+ if (!u8 || (*src & 0xc0) != 0x80)
+ i++;
+ ++src;
+ }
+ return i;
+}
+
+void dialogscreen(TextParser * parser, char * token,
+ char * filename, int forbidden, char ** wlst, int ns) {
+ int x, y;
+ char line[MAXLNLEN];
+ char line2[MAXLNLEN];
+ getmaxyx(stdscr,y,x);
+ clear();
+
+ if (forbidden & SPELL_FORBIDDEN) printw(gettext("FORBIDDEN!")); else
+ if (forbidden & SPELL_WARN) printw(gettext("Spelling mistake?"));
+ printw(gettext("\t%s\t\tFile: %s\n\n"), chenc(token, io_enc, ui_enc), filename);
+
+ // handle long lines and tabulators
+
+ char lines[MAXPREVLINE][MAXLNLEN];
+
+ for (int i = 0; i < MAXPREVLINE; i++) {
+ expand_tab(lines[i], chenc(parser->get_prevline(i), io_enc, ui_enc), MAXLNLEN);
+ }
+
+ int prevline = 0;
+
+ strncpy(line, parser->get_prevline(0), parser->get_tokenpos());
+ line[parser->get_tokenpos()] = '\0';
+ int tokenbeg = expand_tab(line2, chenc(line, io_enc, ui_enc), MAXLNLEN);
+
+ strncpy(line, parser->get_prevline(0), parser->get_tokenpos() + strlen(token));
+ line[parser->get_tokenpos() + strlen(token)] = '\0';
+ int tokenend = expand_tab(line2, chenc(line, io_enc, ui_enc), MAXLNLEN);
+
+ int rowindex = tokenend / x;
+ int beginrow = rowindex - tokenbeg / x;
+ if (beginrow >= MAXPREVLINE) beginrow = MAXPREVLINE - 1;
+
+ for (int i = 0; i < MAXPREVLINE; i++) {
+ strncpyu8(line, lines[prevline], x * rowindex, x);
+ mvprintw(MAXPREVLINE + 1 - i, 0, "%s", line);
+ rowindex--;
+ if (rowindex == -1) {
+ prevline++;
+ rowindex = strlenu8(lines[prevline]) / x;
+ }
+ }
+
+ int linestartpos = tokenbeg - (tokenbeg % x);
+ strncpyu8(line, lines[0], x * rowindex + linestartpos, tokenbeg % x);
+ mvprintw(MAXPREVLINE + 1 - beginrow, 0, "%s", line);
+ attron(A_REVERSE);
+ printw("%s", chenc(token, io_enc, ui_enc));
+ attroff(A_REVERSE);
+
+ mvprintw(MAXPREVLINE + 2, 0, "\n");
+ for (int i = 0; i < ns; i++) {
+ if ((ns > 10) && (i < 10)) {
+ printw(" 0%d: %s\n", i, chenc(wlst[i], io_enc, ui_enc));
+ } else {
+ printw(" %d: %s\n", i, chenc(wlst[i], io_enc, ui_enc));
+ }
+ }
+
+/* TRANSLATORS: the capital letters are shortcuts, mark one letter similarly
+ in your translation and translate the standalone letter accordingly later */
+ mvprintw(y-3, 0, "%s\n",
+ gettext("\n[SPACE] R)epl A)ccept I)nsert U)ncap S)tem Q)uit e(X)it or ? for help\n"));
+}
+
+char * lower_first_char(char *token, const char *io_enc, int langnum)
+{
+ const char *utf8str = chenc(token, io_enc, "UTF-8");
+ int max = strlen(utf8str);
+ w_char *u = new w_char[max];
+ int len = u8_u16(u, max, utf8str);
+ unsigned short idx = (u[0].h << 8) + u[0].l;
+ idx = unicodetolower(idx, langnum);
+ u[0].h = (unsigned char) (idx >> 8);
+ u[0].l = (unsigned char) (idx & 0x00FF);
+ char *scratch = (char*)malloc(max + 1 + 4);
+ u16_u8(scratch, max+4, u, len);
+ delete[] u;
+ char *result = chenc(scratch, "UTF-8", io_enc);
+ if (result != scratch)
+ {
+ free (scratch);
+ result = mystrdup(result);
+ }
+ return result;
+}
+
+ // for terminal interface
+int dialog(TextParser * parser, Hunspell * pMS, char * token, char * filename,
+ char ** wlst, int ns, int forbidden) {
+ char buf[MAXLNLEN];
+ char * buf2;
+ wordlist * dicwords = NULL;
+ int c;
+
+ dialogscreen(parser, token, filename, forbidden, wlst, ns);
+
+ char firstletter='\0';
+
+ while ((c=getch())) {
+ switch (c) {
+ case '0':
+ case '1': if ((firstletter=='\0') && (ns>10)) {
+ firstletter=c;
+ break;
+ }
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': {
+ modified=1;
+ if ((firstletter!='\0') && (firstletter=='1')) {
+ c += 10;
+ }
+ c -= '0';
+ if (c>=ns) break;
+ parser->change_token(wlst[c]);
+ goto ki;
+ }
+ case ' ': {
+ goto ki;
+ }
+ case '?': {
+ clear();
+printw(gettext("Whenever a word is found that is not in the dictionary\n"
+ "it is printed on the first line of the screen. If the dictionary\n"
+ "contains any similar words, they are listed with a number\n"
+ "next to each one. You have the option of replacing the word\n"
+ "completely, or choosing one of the suggested words.\n"));
+printw(gettext("\nCommands are:\n\n"));
+printw(gettext("R Replace the misspelled word completely.\n"));
+printw(gettext("Space Accept the word this time only.\n"));
+printw(gettext("A Accept the word for the rest of this session.\n"));
+printw(gettext("I Accept the word, and put it in your private dictionary.\n"));
+printw(gettext("U Accept and add lowercase version to private dictionary.\n"));
+printw(gettext(
+"S\tAsk a stem and a model word and store them in the private dictionary.\n"
+"\tThe stem will be accepted also with the affixes of the model word.\n"
+));
+printw(gettext("0-n Replace with one of the suggested words.\n"));
+printw(gettext("X Write the rest of this file, ignoring misspellings, and start next file.\n"));
+printw(gettext("Q Quit immediately. Asks for confirmation. Leaves file unchanged.\n"));
+printw(gettext("^Z Suspend program. Restart with fg command.\n"));
+printw(gettext("? Show this help screen.\n"));
+printw(gettext("\n-- Type space to continue -- \n"));
+ while (getch()!=' ');
+ }
+ case 12: {
+ dialogscreen(parser, token, filename, forbidden, wlst, ns);
+ break;
+ }
+ default: {
+/* TRANSLATORS: translate this letter according to the shortcut letter used
+ previously in the translation of "R)epl" before */
+ if (c==(gettext("r"))[0]) {
+ char i[MAXLNLEN];
+ char *temp;
+
+ modified=1;
+
+
+#ifdef HAVE_READLINE
+ endwin();
+ rltext = "";
+ if (rltext && *rltext) rl_startup_hook = set_rltext;
+#endif
+ temp = readline(gettext("Replace with: "));
+#ifdef HAVE_READLINE
+ initscr();
+ cbreak();
+#endif
+
+ if ((!temp) || (temp[0] == '\0')) {
+ free(temp);
+ dialogscreen(parser, token, filename, forbidden, wlst, ns);
+ break;
+ }
+
+ strncpy(i, temp, MAXLNLEN);
+ free(temp);
+
+ parser->change_token(i);
+
+ return 2; // replace
+ }
+/* TRANSLATORS: translate these letters according to the shortcut letter used
+ previously in the translation of "U)ncap" and I)nsert before */
+ int u_key = gettext("u")[0];
+ int i_key = gettext("i")[0];
+
+ if (c==u_key || c==i_key) {
+ struct wordlist* i = (struct wordlist *) malloc (sizeof(struct wordlist));
+ i->word = (c==i_key) ? mystrdup(token) : lower_first_char(token, io_enc, pMS->get_langnum());
+ i->next = dicwords;
+ dicwords = i;
+ // save
+ if (HOME) strcpy(buf,HOME); else {
+ fprintf(stderr, gettext("error - missing HOME variable\n"));
+ break;
+ }
+#ifndef WIN32
+ strcat(buf,"/");
+#endif
+ buf2 = buf+strlen(buf);
+ if (!privdicname) {
+ strcat(buf,DICBASENAME);
+ strcat(buf,basename(dicname,DIRSEPCH));
+ } else {
+ strcat(buf,privdicname);
+ }
+ if (save_privdic(buf2, buf, dicwords)) {
+ dicwords=NULL;
+ } else {
+ fprintf(stderr,gettext("Cannot update personal dictionary."));
+ break;
+ }
+ } // no break
+/* TRANSLATORS: translate this letter according to the shortcut letter used
+ previously in the translation of "U)ncap" and I)nsert before */
+ if ((c==(gettext("u"))[0]) || (c==(gettext("i"))[0]) || (c==(gettext("a"))[0])) {
+ modified=1;
+ putdic(token, pMS);
+ goto ki;
+ }
+/* TRANSLATORS: translate this letter according to the shortcut letter used
+ previously in the translation of "S)tem" before */
+ if (c==(gettext("s"))[0]) {
+ modified=1;
+
+ char w[MAXLNLEN], w2[MAXLNLEN], w3[MAXLNLEN];
+ char *temp;
+
+ strncpy(w, token, MAXLNLEN);
+ temp = basename(w, '-');
+ if (w < temp) {
+ *(temp-1) = '\0';
+ } else {
+ char ** poslst = NULL;
+#ifdef HUNSPELL_EXPERIMENTAL
+ int ps = pMS->suggest_pos_stems(&poslst, token);
+#else
+ int ps = 0;
+#endif
+ if (ps > 0) {
+ strcpy(buf, poslst[0]);
+ for (int i = 0; i < ps; i++) {
+ if (strlen(poslst[i]) <= strlen(buf)) strcpy(buf, poslst[i]);
+ free(poslst[i]);
+ }
+ strcpy(w, buf);
+ }
+ if (poslst) free(poslst);
+ }
+
+#ifdef HAVE_READLINE
+ endwin();
+ rltext = w;
+ if (rltext && *rltext) rl_startup_hook = set_rltext;
+#endif
+ temp = readline(gettext("New word (stem): "));
+
+ if ((!temp) || (temp[0] == '\0')) {
+ free(temp);
+#ifdef HAVE_READLINE
+ initscr();
+ cbreak();
+#endif
+ dialogscreen(parser, token, filename, forbidden, wlst, ns);
+ break;
+ }
+
+ strncpy(w, temp, MAXLNLEN);
+ free(temp);
+
+#ifdef HAVE_READLINE
+ initscr();
+ cbreak();
+#endif
+ dialogscreen(parser, token, filename, forbidden, wlst, ns);
+ refresh();
+
+#ifdef HAVE_READLINE
+ endwin();
+ rltext = "";
+ if (rltext && *rltext) rl_startup_hook = set_rltext;
+#endif
+ temp = readline(gettext("Model word (a similar dictionary word): "));
+
+#ifdef HAVE_READLINE
+ initscr();
+ cbreak();
+#endif
+
+ if ((!temp) || (temp[0] == '\0')) {
+ free(temp);
+ dialogscreen(parser, token, filename, forbidden, wlst, ns);
+ break;
+ }
+
+ strncpy(w2, temp, MAXLNLEN);
+ free(temp);
+
+ if (strlen(w) + strlen(w2) + 2 < MAXLNLEN) {
+ sprintf(w3, "%s/%s", w, w2);
+ } else break;
+
+ if (!putdic(w3, pMS)) {
+
+ struct wordlist* i =
+ (struct wordlist *) malloc (sizeof(struct wordlist));
+ i->word = mystrdup(w3);
+ i->next = dicwords;
+ dicwords = i;
+
+ if (strlen(w) + strlen(w2) + 4 < MAXLNLEN) {
+ sprintf(w3, "%s-/%s-", w, w2);
+ if (putdic(w3, pMS)) {
+ struct wordlist* i =
+ (struct wordlist *) malloc (sizeof(struct wordlist));
+ i->word = mystrdup(w3);
+ i->next = dicwords;
+ dicwords = i;
+ }
+ }
+ // save
+
+ if (HOME) strcpy(buf,HOME); else {
+ fprintf(stderr, gettext("error - missing HOME variable\n"));
+ continue;
+ }
+#ifndef WIN32
+ strcat(buf,"/");
+#endif
+ buf2 = buf + strlen(buf);
+ if (!privdicname) {
+ strcat(buf,DICBASENAME);
+ strcat(buf,basename(dicname,DIRSEPCH));
+ } else {
+ strcat(buf,privdicname);
+ }
+ if (save_privdic(buf2, buf, dicwords)) {
+ dicwords = NULL;
+ } else {
+ fprintf(stderr, gettext("Cannot update personal dictionary."));
+ break;
+ }
+
+ } else {
+ dialogscreen(parser, token, filename, forbidden, wlst, ns);
+ printw(gettext("Model word must be in the dictionary. Press any key!"));
+ getch();
+ dialogscreen(parser, token, filename, forbidden, wlst, ns);
+ break;
+ }
+ goto ki;
+ }
+/* TRANSLATORS: translate this letter according to the shortcut letter used
+ previously in the translation of "e(X)it" before */
+ if (c==(gettext("x"))[0]) {
+ return 1;
+ }
+/* TRANSLATORS: translate this letter according to the shortcut letter used
+ previously in the translation of "Q)uit" before */
+ if (c==(gettext("q"))[0]) {
+ if (modified) {
+ printw(gettext("Are you sure you want to throw away your changes? "));
+/* TRANSLATORS: translate this letter according to the shortcut letter y)es */
+ if (getch()==(gettext("y"))[0]) return -1;
+ dialogscreen(parser, token, filename, forbidden, wlst, ns);
+ break;
+ } else {
+ return -1;
+ }
+ }
+ }
+ }
+ }
+ ki: return 0;
+}
+
+int interactive_line(TextParser * parser, Hunspell ** pMS, char * filename, FILE * tempfile)
+{
+ char * token;
+ int dialogexit = 0;
+ int info;
+ int d = 0;
+ while ((token=parser->next_token())) {
+ if (!check(pMS, &d, token, &info, NULL)) {
+ dialogscreen(parser, token, filename, info, NULL, 0); // preview
+ refresh();
+ char ** wlst = NULL;
+ int ns = pMS[d]->suggest(&wlst, chenc(token, io_enc, dic_enc[d]));
+ if (ns==0) {
+ dialogexit = dialog(parser, pMS[d], token, filename, wlst, ns, info);
+ } else {
+ for (int j = 0; j < ns; j++) {
+ char d2io[MAXLNLEN];
+ strcpy(d2io, chenc(wlst[j], dic_enc[d], io_enc));
+ wlst[j] = (char *) realloc(wlst[j], strlen(d2io) + 1);
+ strcpy(wlst[j], d2io);
+ }
+ dialogexit = dialog(parser, pMS[d], token, filename, wlst, ns, info);
+ }
+ for (int j = 0; j < ns; j++) {
+ free(wlst[j]);
+ }
+ free(wlst);
+ }
+ free(token);
+ if ((dialogexit==-1) || (dialogexit==1)) goto ki2;
+ }
+
+ ki2: fprintf(tempfile,"%s\n",token=parser->get_line());
+ free(token);
+ return dialogexit;
+}
+
+void interactive_interface(Hunspell ** pMS, char * filename, int format)
+{
+ char buf[MAXLNLEN];
+
+ FILE *text;
+
+ text = fopen(filename, "r");
+
+ int dialogexit;
+ int check=1;
+
+ TextParser * parser;
+ char * extension = basename(filename, '.');
+ parser = get_parser(format, extension, pMS[0]);
+
+ char * tempname = (char *) malloc(strlen(filename) + strlen(TEMPNAME) + 1);
+ strcpy(tempname, filename);
+ strcpy(basename(tempname, DIRSEPCH), TEMPNAME);
+
+ FILE *tempfile;
+
+ if (!(tempfile = fopen(tempname, "w"))) {
+ fprintf(stderr, gettext("Can't create tempfile %s.\n"), tempname);
+ endwin();
+ exit(1);
+ }
+
+ while(fgets(buf,MAXLNLEN,text)) {
+ if (check) {
+ if (*(buf + strlen(buf) - 1) == '\n') *(buf + strlen(buf) - 1) = '\0';
+ parser->put_line(buf);
+ dialogexit = interactive_line(parser,pMS,filename,tempfile);
+ switch (dialogexit) {
+ case -1: {
+ clear();
+ refresh();
+ unlink(tempname);
+ endwin();
+ exit(0);
+ }
+ case 1: {
+ check = 0;
+ }
+ }
+ } else {
+ fprintf(tempfile,"%s",buf);
+ }
+ }
+ fclose(text);
+ fclose(tempfile);
+ delete parser;
+
+ if (! modified) {
+ unlink(tempname);
+ } else {
+ rename(tempname, filename);
+ }
+ free(tempname);
+}
+
+#endif
+
+char * add(char * dest, const char * st) {
+ if (!dest) {
+ dest = mystrdup(st);
+ } else {
+ dest = (char *) realloc(dest, strlen(dest) + strlen(st) + 1);
+ strcat(dest, st);
+ }
+ return dest;
+}
+
+char * exist2(char * dir, int len, const char * name, const char * ext) {
+ char buf[MAXLNLEN];
+ const char * sep = (len == 0) ? "": DIRSEP;
+ strncpy(buf, dir, len);
+ strcpy(buf + len, sep);
+ strcat(buf, name);
+ strcat(buf, ext);
+ if (exist(buf)) return mystrdup(buf);
+ strcat(buf, HZIP_EXTENSION);
+ if (exist(buf)) {
+ buf[strlen(buf) - strlen(HZIP_EXTENSION)] = '\0';
+ return mystrdup(buf);
+ }
+ return NULL;
+}
+
+#ifndef WIN32
+int listdicpath(char * dir, int len) {
+ char buf[MAXLNLEN];
+ const char * sep = (len == 0) ? "": DIRSEP;
+ strncpy(buf, dir, len);
+ strcpy(buf + len, sep);
+ DIR *d = opendir(buf);
+ if (!d) return 0;
+ struct dirent * de;
+ while ((de = readdir(d))) {
+ int len = strlen(de->d_name);
+ if ((len > 4 && strcmp(de->d_name + len - 4, ".dic") == 0) ||
+ (len > 7 && strcmp(de->d_name + len - 7, ".dic.hz") == 0)) {
+ char * s = mystrdup(de->d_name);
+ s[len - ((s[len - 1] == 'z') ? 7 : 4)] = '\0';
+ fprintf(stderr, "%s%s\n", buf, s);
+ free(s);
+ }
+ }
+ closedir(d);
+ return 1;
+}
+#endif
+
+// search existing path for file "name + ext"
+char * search(char * begin, char * name, const char * ext) {
+ char * end = begin;
+ while (1) {
+ while (!((*end == *PATHSEP) || (*end == '\0'))) end++;
+ char * res = NULL;
+ if (name) {
+ res = exist2(begin, end - begin, name, ext);
+ } else {
+#ifndef WIN32
+ listdicpath(begin, end - begin);
+#endif
+ }
+ if ((*end == '\0') || res) return res;
+ end++;
+ begin = end;
+ }
+}
+
+int main(int argc, char** argv)
+{
+ char buf[MAXLNLEN];
+ Hunspell * pMS[DMAX];
+ char * key = NULL;
+ int arg_files = -1; // first filename argumentum position in argv
+ int format = FMT_TEXT;
+ int argstate = 0;
+
+#ifdef ENABLE_NLS
+# ifdef HAVE_LOCALE_H
+ setlocale(LC_ALL, "");
+ textdomain("hunspell");
+# ifdef HAVE_LANGINFO_CODESET
+ ui_enc = nl_langinfo(CODESET);
+# endif
+# endif
+#endif
+
+#ifdef HAVE_READLINE
+ rl_set_key("", rl_escape, rl_get_keymap());
+ rl_bind_key('\t', rl_insert);
+#endif
+
+#ifdef LOG
+ log("START");
+#endif
+
+ for(int i=1; i<argc; i++) {
+#ifdef LOG
+ log(argv[i]);
+#endif
+
+ if (argstate == 1) {
+ if (dicname) free(dicname);
+ dicname = mystrdup(argv[i]);
+ argstate = 0;
+ } else if (argstate == 2) {
+ if (privdicname) free(privdicname);
+ privdicname = mystrdup(argv[i]);
+ argstate = 0;
+ } else if (argstate == 3) {
+ io_enc = argv[i];
+ argstate = 0;
+ } else if (argstate == 4) {
+ key = argv[i];
+ argstate = 0;
+ } else if (strcmp(argv[i],"-d")==0) argstate=1;
+ else if (strcmp(argv[i],"-p")==0) argstate=2;
+ else if (strcmp(argv[i],"-i")==0) argstate=3;
+ else if (strcmp(argv[i],"-P")==0) argstate=4;
+ else if ((strcmp(argv[i],"-h") == 0) || (strcmp(argv[i],"--help") == 0)) {
+ fprintf(stderr,gettext("Usage: hunspell [OPTION]... [FILE]...\n"));
+ fprintf(stderr,gettext("Check spelling of each FILE. Without FILE, check standard input.\n\n"));
+ fprintf(stderr,gettext(" -1\t\tcheck only first field in lines (delimiter = tabulator)\n"));
+ fprintf(stderr,gettext(" -a\t\tIspell's pipe interface\n"));
+ fprintf(stderr,gettext(" --check-url\tCheck URLs, e-mail addresses and directory paths\n"));
+ fprintf(stderr,gettext(" -d d[,d2,...]\tuse d (d2 etc.) dictionaries\n"));
+ fprintf(stderr,gettext(" -D\t\tshow available dictionaries\n"));
+ fprintf(stderr,gettext(" -G\t\tprint only correct words or lines\n"));
+ fprintf(stderr,gettext(" -h, --help\tdisplay this help and exit\n"));
+ fprintf(stderr,gettext(" -H\t\tHTML input file format\n"));
+ fprintf(stderr,gettext(" -i enc\tinput encoding\n"));
+ fprintf(stderr,gettext(" -l\t\tprint misspelled words\n"));
+ fprintf(stderr,gettext(" -L\t\tprint lines with misspelled words\n"));
+ fprintf(stderr,gettext(" -m \t\tanalyze the words of the input text\n"));
+ fprintf(stderr,gettext(" -n\t\tnroff/troff input file format\n"));
+ fprintf(stderr,gettext(" -p dict\tset dict custom dictionary\n"));
+ fprintf(stderr,gettext(" -r\t\twarn of the potential mistakes (rare words)\n"));
+ fprintf(stderr,gettext(" -P password\tset password for encrypted dictionaries\n"));
+ fprintf(stderr,gettext(" -s \t\tstem the words of the input text\n"));
+ fprintf(stderr,gettext(" -t\t\tTeX/LaTeX input file format\n"));
+// experimental functions: missing Unicode support
+// fprintf(stderr,gettext(" -u\t\tshow typical misspellings\n"));
+// fprintf(stderr,gettext(" -u2\t\tprint typical misspellings in sed format\n"));
+// fprintf(stderr,gettext(" -u3\t\tprint typical misspellings in gcc error format\n"));
+// fprintf(stderr,gettext(" -U\t\tautomatic correction of typical misspellings to stdout\n"));
+ fprintf(stderr,gettext(" -v, --version\tprint version number\n"));
+ fprintf(stderr,gettext(" -vv\t\tprint Ispell compatible version number\n"));
+ fprintf(stderr,gettext(" -w\t\tprint misspelled words (= lines) from one word/line input.\n\n"));
+ fprintf(stderr,gettext("Example: hunspell -d en_US file.txt # interactive spelling\n"
+ " hunspell -l file.txt # print misspelled words\n"
+ " hunspell -i utf-8 file.txt # check UTF-8 encoded file\n\n"));
+ fprintf(stderr,gettext("Bug reports: http://hunspell.sourceforge.net\n"));
+ exit(0);
+ } else if ((strcmp(argv[i],"-vv")==0) || (strcmp(argv[i],"-v")==0) || (strcmp(argv[i],"--version")==0)) {
+ fprintf(stdout,gettext(HUNSPELL_PIPE_HEADING));
+ fprintf(stdout,"\n");
+ if (strcmp(argv[i],"-vv")!=0) {
+ fprintf(stdout,gettext("\nCopyright (C) 2002-2008 L\303\241szl\303\263 N\303\251meth. License: MPL/GPL/LGPL.\n\n"
+ "Based on OpenOffice.org's Myspell library.\n"
+ "Myspell's copyright (C) Kevin Hendricks, 2001-2002, License: BSD.\n\n"));
+ fprintf(stdout,gettext("This is free software; see the source for copying conditions. There is NO\n"
+ "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE,\n"
+ "to the extent permitted by law.\n"));
+ }
+ exit(0);
+ } else if ((strcmp(argv[i],"-a")==0)) {
+ filter_mode = PIPE;
+ fprintf(stdout,gettext(HUNSPELL_PIPE_HEADING));
+ fflush(stdout);
+ } else if ((strcmp(argv[i],"-m")==0)) {
+ /*
+ if -a was used, don't override, i.e. keep ispell compatability
+ ispell: Make possible root/affix combinations that aren't in the dictionary.
+ hunspell: Analyze the words of the input text
+ */
+ if (filter_mode != PIPE)
+ filter_mode = ANALYZE;
+ } else if ((strcmp(argv[i],"-s")==0)) {
+ /*
+ if -a was used, don't override, i.e. keep ispell compatability
+ ispell: Stop itself with a SIGTSTP signal after each line of input.
+ hunspell: Stem the words of the input text
+ */
+ if (filter_mode != PIPE)
+ filter_mode = STEM;
+ } else if ((strcmp(argv[i],"-t")==0)) {
+ format = FMT_LATEX;
+ } else if ((strcmp(argv[i],"-n")==0)) {
+ format = FMT_MAN;
+ } else if ((strcmp(argv[i],"-H")==0)) {
+ format = FMT_HTML;
+ } else if ((strcmp(argv[i],"-l")==0)) {
+ filter_mode = BADWORD;
+ } else if ((strcmp(argv[i],"-w")==0)) {
+ /*
+ if -a was used, don't override, i.e. keep ispell compatability
+ ispell: Specify additional characters that can be part of a word.
+ hunspell: Print misspelled words (= lines) from one word/line input
+ */
+ if (filter_mode != PIPE)
+ filter_mode = WORDFILTER;
+ } else if ((strcmp(argv[i],"-L")==0)) {
+ /*
+ if -a was used, don't override, i.e. keep ispell compatability
+ ispell: Number of lines of context to be shown at the bottom of the screen
+ hunspell: Print lines with misspelled words
+ */
+ if (filter_mode != PIPE)
+ filter_mode = BADLINE;
+ } else if ((strcmp(argv[i],"-u")==0)) {
+ /*
+ if -a was used, don't override, i.e. keep ispell compatability
+ ispell: None
+ hunspell: Show typical misspellings
+ */
+ if (filter_mode != PIPE)
+ filter_mode = AUTO0;
+ } else if ((strcmp(argv[i],"-U")==0)) {
+ /*
+ if -a was used, don't override, i.e. keep ispell compatability
+ ispell: None
+ hunspell: Automatic correction of typical misspellings to stdout
+ */
+ if (filter_mode != PIPE)
+ filter_mode = AUTO;
+ } else if ((strcmp(argv[i],"-u2")==0)) {
+ /*
+ if -a was used, don't override, i.e. keep ispell compatability
+ ispell: None
+ hunspell: Print typical misspellings in sed format
+ */
+ if (filter_mode != PIPE)
+ filter_mode = AUTO2;
+ } else if ((strcmp(argv[i],"-u3")==0)) {
+ /*
+ if -a was used, don't override, i.e. keep ispell compatability
+ ispell: None
+ hunspell: Print typical misspellings in gcc error format
+ */
+ if (filter_mode != PIPE)
+ filter_mode = AUTO3;
+ } else if ((strcmp(argv[i],"-G")==0)) {
+ printgood = 1;
+ } else if ((strcmp(argv[i],"-1")==0)) {
+ format = FMT_FIRST;
+ } else if ((strcmp(argv[i],"-D")==0)) {
+ showpath = 1;
+ } else if ((strcmp(argv[i],"-r")==0)) {
+ warn = 1;
+fprintf(stderr, "BEKAPCS");
+ } else if ((strcmp(argv[i],"--check-url")==0)) {
+ checkurl = 1;
+ } else if ((arg_files==-1) && ((argv[i][0] != '-') && (argv[i][0] != '\0'))) {
+ arg_files = i;
+ if (! exist(argv[i])) { // first check (before time-consuming dic. load)
+ fprintf(stderr,gettext("Can't open %s.\n"),argv[i]);
+#ifdef HAVE_CURSES_H
+ endwin();
+#endif
+ exit(1);
+ }
+ }
+ }
+
+ if (printgood && (filter_mode == NORMAL)) filter_mode = BADWORD;
+
+ if (! dicname) {
+ if (! (dicname=getenv("DICTIONARY"))) {
+ /*
+ * Search in order of LC_ALL, LC_MESSAGES &
+ * LANG
+ */
+ const char *tests[] = { "LC_ALL", "LC_MESSAGES", "LANG" };
+ for (size_t i = 0; i < sizeof(tests) / sizeof(const char*); ++i) {
+ if ((dicname=getenv(tests[i])) && strcmp(dicname, "") != 0) {
+ dicname = mystrdup(dicname);
+ char * dot = strchr(dicname, '.');
+ if (dot) *dot = '\0';
+ char * at = strchr(dicname, '@');
+ if (at) *at = '\0';
+ break;
+ }
+ }
+
+ if (dicname && ((strcmp(dicname, "C") == 0) || (strcmp(dicname, "POSIX") == 0))) {
+ free(dicname);
+ dicname=mystrdup("en_US");
+ }
+
+ if (! dicname) {
+ dicname=mystrdup(DEFAULTDICNAME);
+ }
+ } else {
+ dicname = mystrdup(dicname);
+ }
+ }
+ path = add(mystrdup("."), PATHSEP); // <- check path in local directory
+ path = add(path, PATHSEP); // <- check path in root directory
+ if (getenv("DICPATH")) path = add(add(path, getenv("DICPATH")), PATHSEP);
+ path = add(add(path, LIBDIR), PATHSEP);
+ if (HOME) path = add(add(add(add(path, HOME), DIRSEP), USEROOODIR), PATHSEP);
+ path = add(path, OOODIR);
+
+ if (showpath) {
+ fprintf(stderr, gettext("SEARCH PATH:\n%s\n"), path);
+ fprintf(stderr, gettext("AVAILABLE DICTIONARIES (path is not mandatory for -d option):\n"));
+ search(path, NULL, NULL);
+ }
+
+ if (!privdicname) privdicname = mystrdup(getenv("WORDLIST"));
+
+ char * dicplus = strchr(dicname, ',');
+ if (dicplus) *dicplus = '\0';
+ char * aff = search(path, dicname, ".aff");
+ char * dic = search(path, dicname, ".dic");
+ if (aff && dic) {
+ if (showpath) {
+ fprintf(stderr, gettext("LOADED DICTIONARY:\n%s\n%s\n"), aff, dic);
+ }
+ pMS[0] = new Hunspell(aff, dic, key);
+ dic_enc[0] = pMS[0]->get_dic_encoding();
+ dmax = 1;
+ if (pMS[0] && dicplus) while (dicplus) {
+ char * dicname2 = dicplus + 1;
+ dicplus = strchr(dicname2, ',');
+ if (dicplus) *dicplus = '\0';
+ free(aff);
+ free(dic);
+ aff = search(path, dicname2, ".aff");
+ dic = search(path, dicname2, ".dic");
+ if (aff && dic) {
+ if (dmax < DMAX) {
+ pMS[dmax] = new Hunspell(aff, dic, key);
+ dic_enc[dmax] = pMS[dmax]->get_dic_encoding();
+ dmax++;
+ } else fprintf(stderr, gettext("error - %s exceeds dictionary limit.\n"), dicname2);
+ } else if (dic) pMS[dmax-1]->add_dic(dic);
+ }
+ } else {
+ fprintf(stderr,gettext("Can't open affix or dictionary files for dictionary named \"%s\".\n"), dicname);
+ exit(1);
+ }
+
+ /* open the private dictionaries */
+ if (HOME) {
+ strcpy(buf,HOME);
+#ifndef WIN32
+ strcat(buf,"/");
+#endif
+ if (!privdicname) {
+ strcat(buf,DICBASENAME);
+ strcat(buf,basename(dicname,DIRSEPCH));
+ load_privdic(buf, pMS[0]);
+ strcpy(buf,DICBASENAME);
+ strcat(buf,basename(dicname,DIRSEPCH));
+ load_privdic(buf, pMS[0]);
+ } else {
+ strcat(buf,privdicname);
+ load_privdic(buf, pMS[0]);
+ strcpy(buf,privdicname);
+ load_privdic(buf, pMS[0]);
+ }
+ }
+
+ if (arg_files==-1) {
+ pipe_interface(pMS, format, stdin);
+ } else if (filter_mode != NORMAL) {
+ for (int i = arg_files; i < argc; i++) {
+ if (exist(argv[i])) {
+ modified = 0;
+ currentfilename = argv[i];
+ FILE * f = fopen(argv[i], "r");
+ pipe_interface(pMS, format, f);
+ fclose(f);
+ } else {
+ fprintf(stderr, gettext("Can't open %s.\n"), argv[i]);
+ exit(1);
+ }
+ }
+ } else if (filter_mode == NORMAL) {
+#ifdef HAVE_CURSES_H
+ initscr();
+ cbreak();
+ noecho();
+ nonl();
+ intrflush(stdscr,FALSE);
+
+ for (int i = arg_files; i < argc; i++) {
+ if (exist(argv[i])) {
+ modified = 0;
+ interactive_interface(pMS, argv[i], format);
+ } else {
+ fprintf(stderr, gettext("Can't open %s.\n"), argv[i]);
+ endwin();
+ exit(1);
+ }
+ }
+
+ clear();
+ refresh();
+ endwin();
+#else
+ fprintf(stderr, gettext("Hunspell has been compiled without Ncurses user interface.\n"));
+#endif
+ }
+
+ if (dicname) free(dicname);
+ if (privdicname) free(privdicname);
+ if (path) free(path);
+ if (aff) free(aff);
+ if (dic) free(dic);
+ if (wordchars) free(wordchars);
+ if (wordchars_utf16_free) free(wordchars_utf16);
+#ifdef HAVE_ICONV
+ free_utf_tbl();
+#endif
+ for (int i = 0; i < dmax; i++) delete pMS[i];
+ return 0;
+}