Imported Upstream version 0.60.7
[platform/upstream/aspell.git] / prog / checker_string.cpp
1 // This file is part of The New Aspell
2 // Copyright (C) 2002,2011 by Kevin Atkinson under the GNU LGPL license
3 // version 2.0 or 2.1.  You should have received a copy of the LGPL
4 // license along with this library if you did not you can find
5 // it at http://www.gnu.org/.
6
7 #include <assert.h>
8
9 #include "checker_string.hpp"
10 #include "speller.hpp"
11 #include "document_checker.hpp"
12 #include "asc_ctype.hpp"
13 #include "convert.hpp"
14
15 extern Conv dconv;
16 extern Conv uiconv;
17
18 static int get_line(FILE * in, CheckerString::Line & d)
19 {
20   d.clear();
21   int i;
22   while ((i = getc(in)), i != EOF)
23   {
24     d.real.push_back(static_cast<char>(i));
25     if (i == '\n') break;
26   }
27   if (dconv.conv) {
28     dconv.conv->convert(d.real.str(), d.real.size(), d.buf, dconv.buf0);
29     d.disp.str = d.buf.str();
30     d.disp.size = d.buf.size();
31   } else {
32     d.disp.str = d.real.str();
33     d.disp.size = d.real.size();
34   }
35   return d.real.size();
36 }
37
38 CheckerString::CheckerString(AspellSpeller * speller, 
39                              FILE * in, FILE * out, 
40                              int num_lines)
41   : in_(in), out_(out), speller_(speller)
42 {
43   lines_.reserve(num_lines + 1);
44   for (; num_lines > 0; --num_lines)
45   {
46     lines_.resize(lines_.size() + 1);
47     int s = get_line(in_, lines_.back());
48     if (s == 0) break;
49   }
50   if (lines_.back().real.size() != 0)
51     lines_.resize(lines_.size() + 1);
52
53   end_ = lines_.end() - 1;
54   cur_line_ = lines_.begin();
55   diff_ = 0;
56   has_repl_ = false;
57
58   checker_.reset(new_document_checker(reinterpret_cast<Speller *>(speller)));
59   checker_->process(cur_line_->real.data(), cur_line_->real.size());
60 }
61
62 void CheckerString::abort() {
63   fclose(in_);
64   in_ = 0;
65   if (out_) {
66     fclose(out_);
67     out_ = 0;
68   }
69 }
70
71 CheckerString::~CheckerString()
72 {
73   if (!in_ && !out_)
74     return;
75   if (out_)
76     for (cur_line_ = first_line(); !off_end(cur_line_); next_line(cur_line_))
77     {
78       fwrite(cur_line_->real.data(), cur_line_->real.size(), 1, out_);
79       cur_line_->clear();
80     }
81   if (in_ != stdin)
82     fclose(in_);
83   if (out_ && out_ != stdout && out_ != stdout)
84     fclose(out_);
85 }
86
87 bool CheckerString::read_next_line()
88 {
89   if (feof(in_)) return false;
90   Lines::iterator next = end_;
91   inc(next);
92   if (next == cur_line_) return false;
93   int s = get_line(in_, *end_);
94   if (s == 0) return false;
95   end_ = next;
96   if (out_ && end_->real.size() > 0)
97     fwrite(end_->real.data(), end_->real.size(), 1, out_);
98   end_->clear();
99   return true;
100 }
101
102 bool CheckerString::next_misspelling()
103 {
104   if (off_end(cur_line_)) return false;
105   if (has_repl_) {
106     has_repl_ = false;
107     CharVector word;
108     bool correct = false;
109     // FIXME: This is a hack to avoid trying to check a word with a space
110     //        in it.  The correct action is to reparse to string and
111     //        check each word individually.  However doing so involves
112     //        an API enhancement in Checker.
113     for (int i = 0; i != real_word_size_; ++i) {
114       if (asc_isspace(*(real_word_begin_ + i)))
115         correct = true;
116     }
117     if (!correct)
118       correct = aspell_speller_check(speller_, &*real_word_begin_, real_word_size_);
119     diff_ += real_word_size_ - tok_.len;
120     tok_.len = real_word_size_;
121     if (!correct)
122       return true;
123   }
124   while ((tok_ = checker_->next_misspelling()).len == 0) {
125     next_line(cur_line_);
126     diff_ = 0;
127     if (off_end(cur_line_)) return false;
128     checker_->process(cur_line_->real.data(), cur_line_->real.size());
129   }
130   real_word_begin_ = cur_line_->real.begin() + tok_.offset + diff_;
131   real_word_size_  = tok_.len;
132   fix_display_str();
133   return true;
134 }
135
136 void CheckerString::replace(ParmString repl)
137 {
138   assert(real_word_size_ > 0);
139   int offset = real_word_begin_ - cur_line_->real.begin();
140   aspell_speller_store_replacement(speller_, &*real_word_begin_, real_word_size_,
141                                    repl.str(), repl.size());
142   cur_line_->real.replace(real_word_begin_, real_word_begin_ + real_word_size_,
143                           repl.str(), repl.str() + repl.size());
144   real_word_begin_ = cur_line_->real.begin() + offset;
145   real_word_size_ = repl.size();
146   fix_display_str();
147   has_repl_ = true;
148 }
149
150 void CheckerString::fix_display_str()
151 {
152   if (dconv.conv) {
153     cur_line_->buf.clear();
154     int s = real_word_begin_ - cur_line_->real.begin();
155     if (s > 0) dconv.conv->convert(cur_line_->real.data(), s, 
156                                    cur_line_->buf, dconv.buf0);
157     int off = cur_line_->buf.size();
158     dconv.conv->convert(real_word_begin_, real_word_size_, 
159                         cur_line_->buf, dconv.buf0);
160     disp_word_size_ = cur_line_->buf.size() - off;
161     s = cur_line_->real.end() - (real_word_begin_ + real_word_size_);
162     if (s > 0) dconv.conv->convert(cur_line_->real.data() + (cur_line_->real.size() - s), 
163                                   s, cur_line_->buf, dconv.buf0);
164     cur_line_->disp.str = cur_line_->buf.str();
165     cur_line_->disp.size = cur_line_->buf.size();
166     disp_word_begin_ = cur_line_->buf.data() + off;
167   } else {
168     cur_line_->disp.str  = cur_line_->real.str();
169     cur_line_->disp.size = cur_line_->real.size();
170     disp_word_size_  = real_word_size_;
171     disp_word_begin_ = real_word_begin_;
172   }
173 }
174