blob: 07ad6bbf7939c51c2f638d5ba89dee607297640b [file] [log] [blame]
// 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;
}