// This file is part of The New Aspell // Copyright (C) 2001 by Kevin Atkinson under the GNU LGPL license // version 2.0 or 2.1. You should have received a copy of the LGPL // license along with this library if you did not you can find // it at http://www.gnu.org/. #include "asc_ctype.hpp" #include "config.hpp" #include "string.hpp" #include "indiv_filter.hpp" #include "mutable_container.hpp" #include "string_map.hpp" #include "clone_ptr-t.hpp" #include "vector.hpp" #include "errors.hpp" #include "convert.hpp" //#define FILTER_PROGRESS_CONTROL "tex-filter-debug.log" //#include "filter_debug.hpp" //#include //#include #include "filter_char_vector.hpp" #include "string_list.hpp" #include "string_enumeration.hpp" #include "gettext.h" namespace { using namespace acommon; class TexFilter : public IndividualFilter { private: enum InWhat {Name, Opt, Parm, Other, Swallow}; struct Command { InWhat in_what; String name; const char * do_check; Command() {} Command(InWhat w) : in_what(w), do_check("P") {} }; bool in_comment; bool prev_backslash; Vector stack; class Commands : public StringMap { public: PosibErr add(ParmStr to_add); PosibErr remove(ParmStr to_rem); }; Commands commands; bool check_comments; inline void push_command(InWhat); inline void pop_command(); bool end_option(char u, char l); inline bool process_char(FilterChar::Chr c); public: PosibErr setup(Config *); void reset(); void process(FilterChar * &, FilterChar * &); }; // // // inline void TexFilter::push_command(InWhat w) { stack.push_back(Command(w)); } inline void TexFilter::pop_command() { stack.pop_back(); if (stack.empty()) push_command(Parm); } // // // PosibErr TexFilter::setup(Config * opts) { name_ = "tex-filter"; order_num_ = 0.35; //fprintf(stderr,"name %s \n",name_); commands.clear(); opts->retrieve_list("f-tex-command", &commands); check_comments = opts->retrieve_bool("f-tex-check-comments"); reset(); return true; } void TexFilter::reset() { in_comment = false; prev_backslash = false; stack.resize(0); push_command(Parm); } # define top stack.back() // yes this should be inlined, it is only called once inline bool TexFilter::process_char(FilterChar::Chr c) { // deal with comments if (c == '%' && !prev_backslash) in_comment = true; if (in_comment && c == '\n') in_comment = false; if (in_comment) return !check_comments; if (top.in_what == Name) { if (asc_isalpha(c)) { top.name += c; return true; } else { if (top.name.empty() && (c == '@')) { top.name += c; return true; } top.in_what = Other; if (top.name.empty()) { top.name.clear(); top.name += c; top.do_check = commands.lookup(top.name.c_str()); if (top.do_check == 0) top.do_check = ""; return !asc_isspace(c); } top.do_check = commands.lookup(top.name.c_str()); if (top.do_check == 0) top.do_check = ""; if (asc_isspace(c)) { // swallow extra spaces top.in_what = Swallow; return true; } else if (c == '*') { // ignore * at end of commands return true; } // continue o... } } else if (top.in_what == Swallow) { if (asc_isspace(c)) return true; else top.in_what = Other; } if (c == '{') while (*top.do_check == 'O' || *top.do_check == 'o') ++top.do_check; if (*top.do_check == '\0') pop_command(); if (c == '{') { if (top.in_what == Parm || top.in_what == Opt || top.do_check == '\0') push_command(Parm); top.in_what = Parm; return true; } if (top.in_what == Other) { if (c == '[') { top.in_what = Opt; return true; } else if (asc_isspace(c)) { return true; } else { pop_command(); } } if (c == '\\') { push_command(Name); return true; } if (top.in_what == Parm) { if (c == '}') return end_option('P','p'); else return *top.do_check == 'p'; } else if (top.in_what == Opt) { if (c == ']') return end_option('O', 'o'); else return *top.do_check == 'o'; } return false; } void TexFilter::process(FilterChar * & str, FilterChar * & stop) { FilterChar * cur = str; while (cur != stop) { if (process_char(*cur)) *cur = ' '; ++cur; } } bool TexFilter::end_option(char u, char l) { top.in_what = Other; if (*top.do_check == u || *top.do_check == l) ++top.do_check; return true; } // // TexFilter::Commands // PosibErr TexFilter::Commands::add(ParmStr value) { int p1 = 0; while (!asc_isspace(value[p1])) { if (value[p1] == '\0') return make_err(bad_value, value,"", _("a string of 'o','O','p',or 'P'")); ++p1; } int p2 = p1 + 1; while (asc_isspace(value[p2])) { if (value[p2] == '\0') return make_err(bad_value, value,"", _("a string of 'o','O','p',or 'P'")); ++p2; } String t1; t1.assign(value,p1); String t2; t2.assign(value+p2); return StringMap::replace(t1, t2); } PosibErr TexFilter::Commands::remove(ParmStr value) { int p1 = 0; while (!asc_isspace(value[p1]) && value[p1] != '\0') ++p1; String temp; temp.assign(value,p1); return StringMap::remove(temp); } #if 0 // // class Recode { Vector code; int minlength; public: Recode(); Recode(const Recode & recode) {*this=recode;} void reset(); bool setExpansion(const char * expander); virtual int encode(FilterChar * & begin,FilterChar * & end, FilterCharVector * external); virtual int decode(FilterChar * & begin,FilterChar * & end, FilterCharVector * internal); bool operator == (char test) ; bool operator != (char test) ; Recode & operator = (const Recode & rec); virtual ~Recode(); }; Recode::Recode() : code(0) { minlength=0; reset(); } Recode & Recode::operator = (const Recode & rec) { reset(); code.resize(rec.code.size()); int countrec=0; for (countrec=0; countrec < code.size(); countrec++) { code[countrec]=NULL; if (!rec.code[countrec]) { continue; } int length=strlen(rec.code[countrec]); code[countrec]= new char[length+1]; strncpy(code[countrec],rec.code[countrec],length); code[countrec][length]='\0'; } return *this; } bool Recode::operator == (char test) { return (code.size() && (code[0][0] == test)); } bool Recode::operator != (char test) { return (!code.size() || (code[0][0] != test)); } void Recode::reset() { unsigned int countcode=0; for (countcode=0; countcode < code.size(); countcode++) { if (code[countcode]) { delete[] code[countcode]; } code[countcode] = NULL; } code.resize(0); minlength=0; } bool Recode::setExpansion(const char * expander) { char * begin=(char *)expander; char * end=begin; char * start=begin; int keylen=0; if (code.size() > 0) { keylen=strlen(code[0]); } while (begin && end && *begin) { while (end && *end && (*end != ',')) { end++; } if (end && (begin != end)) { char hold = *end; *end='\0'; int length=strlen(begin); if ((begin == start) && (keylen || (length != 1))) { if ((length != 1) || (keylen != length) || strncmp(code[0],begin,length)) { *end=hold; return false; } *end=hold; if (*end) { end++; } begin=end; continue; } else if (minlength < length) { minlength=0; } code.resize(code.size()+1); code[code.size()-1] = new char[length+1]; strncpy(code[code.size()-1],begin,length); code[code.size()-1][length]='\0'; *end=hold; if (*end) { end++; } begin=end; } } return true; } int Recode::encode(FilterChar * & begin,FilterChar * & end, FilterCharVector * external) { if ((code.size() < 2) || (begin == NULL) || (begin == end) || (begin->width < (unsigned int) minlength )) { return 0; } if (code[0][0] != (char)*begin) { return 0; } external->append(code[1],1); return strlen(code[0]); } int Recode::decode(FilterChar * & begin,FilterChar * & end, FilterCharVector * internal) { FilterChar * i = begin; char * codepointer = NULL; char * codestart = NULL; if (code.size() < 2) { return 0; } while((i != end) && ((codepointer == NULL) || (*codepointer))) { if (codepointer == NULL) { int countcodes=0; for (countcodes=1; countcodes < (int) code.size(); countcodes++) { if (*i == code[countcodes][0]) { codestart=codepointer=code[countcodes]; break; } } if (codepointer == NULL) { return 0; } } if (*codepointer != *i) { return 0; } i++; codepointer++; } if ((codepointer == NULL) || (*codepointer)) { return 0; } int length=strlen(codestart); internal->append(code[0],length); return length; } Recode::~Recode() { reset(); } class TexEncoder : public IndividualFilter { Vector multibytes; FilterCharVector buf; public: TexEncoder(); virtual PosibErr setup(Config * config); virtual void process(FilterChar * & start, FilterChar * & stop); virtual void reset() ; virtual ~TexEncoder(); }; TexEncoder::TexEncoder() : multibytes(0), buf() { } PosibErr TexEncoder::setup(Config * config) { name_ = "tex-encoder"; order_num_ = 0.40; StringList multibytechars; config->retrieve_list("f-tex-multi-byte", &multibytechars); Conv conv; // this a quick and dirty fix witch will only work for // iso-8859-1. Full unicode support needs to be // implemented conv.setup(*config, "utf-8", "iso-8859-1", NormNone); StringEnumeration * multibytelist=multibytechars.elements(); const char * multibyte0=NULL; const char * multibyte=NULL; while ((multibyte0=multibytelist->next())) { multibyte = conv(multibyte0); if (strlen(multibyte) < 3) { fprintf(stderr,"Filter: %s ignoring multi byte encoding `%s'\n", name_,multibyte); continue; } int countmulti=0; while ((countmulti < multibytes.size()) && !multibytes[countmulti].setExpansion(multibyte)) { countmulti++; } if (countmulti >= multibytes.size()) { multibytes.resize(multibytes.size()+1); if (!multibytes[multibytes.size()-1].setExpansion(multibyte)) { fprintf(stderr,"Filter: %s ignoring multi byte encoding `%s'\n",name_,multibyte); continue; } } } return true; } void TexEncoder::process(FilterChar * & start, FilterChar * & stop) { buf.clear(); FilterChar * i=start; while (i && (i != stop)) { FilterChar * old = i; int count=0; for (count=0; count < multibytes.size(); count++) { i+=multibytes[count].encode(i,stop,&buf); } if (i == old) { buf.append(*i); i++; } } buf.append('\0'); start = buf.pbegin(); stop = buf.pend() - 1; } void TexEncoder::reset() { multibytes.resize(0); buf.clear(); } TexEncoder::~TexEncoder(){ } class TexDecoder : public IndividualFilter { Vector multibytes; FilterCharVector buf; Vectorhyphens; public: TexDecoder(); virtual PosibErr setup(Config * config); virtual void process(FilterChar * & start, FilterChar * & stop); virtual void reset() ; virtual ~TexDecoder(); }; TexDecoder::TexDecoder() : multibytes(0), buf(), hyphens() { FDEBUGNOTOPEN; } PosibErr TexDecoder::setup(Config * config) { name_ = "tex-decoder"; order_num_ = 0.3; StringList multibytechars; config->retrieve_list("f-tex-multi-byte", &multibytechars); Conv conv; // this a quick and dirty fix witch will only work for // iso-8859-1. Full unicode support needs to be // implemented conv.setup(*config, "utf-8", "iso-8859-1", NormNone); StringEnumeration * multibytelist=multibytechars.elements(); const char * multibyte0=NULL; const char * multibyte=NULL; multibytes.resize(0); while ((multibyte0=multibytelist->next())) { multibyte = conv(multibyte0); if (strlen(multibyte) < 3) { fprintf(stderr,"Filter: %s ignoring multi byte encoding `%s'\n", name_,multibyte); continue; } FDEBUGPRINTF("next multibyte chars `"); FDEBUGPRINTF(multibyte); FDEBUGPRINTF("'\n"); int countmulti=0; while ((countmulti < multibytes.size()) && !multibytes[countmulti].setExpansion(multibyte)) { countmulti++; } FDEBUGPRINTF("next multibyte chars `"); FDEBUGPRINTF(multibyte); FDEBUGPRINTF("'\n"); if (countmulti >= multibytes.size()) { multibytes.resize(multibytes.size()+1); if (!multibytes[multibytes.size()-1].setExpansion(multibyte)) { fprintf(stderr,"Filter: %s ignoring multi byte encoding `%s'\n",name_,multibyte); continue; } } FDEBUGPRINTF("next multibyte chars `"); FDEBUGPRINTF(multibyte); FDEBUGPRINTF("'\n"); } StringList hyphenChars; config->retrieve_list("f-tex-hyphen", &hyphenChars); StringEnumeration * hyphenList=hyphenChars.elements(); const char * hyphen=NULL; hyphens.resize(0); while ((hyphen=hyphenList->next())) { FDEBUGPRINTF("next hyphen char `"); FDEBUGPRINTF(hyphen); FDEBUGPRINTF("'\n"); hyphens.push_back(strdup(hyphen)); } return true; } void TexDecoder::process(FilterChar * & start, FilterChar * & stop) { buf.clear(); FilterChar * i=start; FDEBUGPRINTF("filtin `"); while (i && (i != stop)) { FilterChar * old = i; int count=0; for (count=0; count < multibytes.size(); count++) { FilterChar * j = i; i+=multibytes[count].decode(i,stop,&buf); while (j != i) { char jp[2]={'\0','\0'}; jp[0]=*j; FDEBUGPRINTF(jp); j++; } } for(count=0; count < hyphens.size(); count++) { if (!hyphens[count]) { continue; } char * hp = &hyphens[count][0]; char * hpo = hp; FilterChar * j = i; while (*hp && (j != stop) && (*hp == *j)) { hp++; j++; } if (!*hp) { FDEBUGPRINTF("{"); FilterChar * k = i; while (k != j) { char kp[2]={'\0','\0'}; kp[0]=*k; FDEBUGPRINTF(kp); k++; } FDEBUGPRINTF("}"); if (buf.size()) { buf[buf.size()-1].width+=strlen(hpo); // buf.append(*i,strlen(hpo)+1); } else { //FIXME better solution for illegal hyphenation at begin of line // illegal as new line chars are whitespace for latex buf.append(*i,strlen(hpo)); char ip[2]={'\0','\0'}; ip[0]=*i; FDEBUGPRINTF(ip); } i=j; } } if (i == old) { char ip[2]={'\0','\0'}; ip[0]=*i; FDEBUGPRINTF(ip); buf.append(*i); i++; } } buf.append('\0'); start = buf.pbegin(); stop = buf.pend() - 1; FDEBUGPRINTF("'\nfiltout `"); i = start; while (i != stop) { char ip[2]={'\0','\0'}; ip[0]=*i; FDEBUGPRINTF(ip); i++; } FDEBUGPRINTF("'\n"); } void TexDecoder::reset() { multibytes.resize(0); buf.clear(); } TexDecoder::~TexDecoder(){ FDEBUGCLOSE; } #endif } C_EXPORT IndividualFilter * new_aspell_tex_filter() {return new TexFilter;} #if 0 C_EXPORT IndividualFilter * new_aspell_tex_decoder() {return new TexDecoder;} C_EXPORT IndividualFilter * new_aspell_tex_encoder() {return new TexEncoder;} #endif