1 //
2 // This file is part of libyacurs.
3 // Copyright (C) 2013  Rafael Ostertag
4 //
5 // This program is free software: you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License as
7 // published by the Free Software Foundation, either version 3 of the
8 // License, or (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful, but
11 // WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 // General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program.  If not, see
17 // <http://www.gnu.org/licenses/>.
18 //
19 //
20 // $Id$
21 
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 
26 #include "gettext.h"
27 
28 #include <cassert>
29 #include <cerrno>
30 #include <stdexcept>
31 
32 #include "cursorbuf.h"
33 #include "yacursex.h"
34 
35 using namespace YACURS::INTERNAL;
36 //
37 // Private
38 //
39 
40 //
41 // Protected
42 //
43 
44 //
45 // Public
46 //
CursorBuffer(const std::wstring & buffer,tsz_t max_size)47 CursorBuffer::CursorBuffer(const std::wstring& buffer, tsz_t max_size)
48     : _buffer{buffer.length() > max_size ? buffer.substr(0, max_size) : buffer},
49       _mbs_cache{},
50       _mbs_cache_valid{false},
51       _changed{false},
52       _curs_pos{0},
53       _offset{0},
54       _max_size{max_size} {}
55 
CursorBuffer(const std::string & buffer,tsz_t max_size)56 CursorBuffer::CursorBuffer(const std::string& buffer, tsz_t max_size)
57     : _mbs_cache{},
58       _mbs_cache_valid{false},
59       _changed{false},
60       _curs_pos{0},
61       _offset{0},
62       _max_size{max_size} {
63     wchar_t* tmpbuf = new wchar_t[buffer.length() + 1];
64 
65     size_t len = buffer.length() > _max_size ? _max_size : buffer.length();
66 
67     size_t retval = std::mbstowcs(tmpbuf, buffer.c_str(), len + 1);
68 
69     if (retval == len + 1) tmpbuf[len] = L'\0';
70 
71     if (retval == (size_t)-1) {
72         delete[] tmpbuf;
73         throw EXCEPTIONS::SystemError(errno);
74     }
75 
76     _buffer = tmpbuf;
77 
78     delete[] tmpbuf;
79 }
80 
CursorBuffer(const CursorBuffer & cb)81 CursorBuffer::CursorBuffer(const CursorBuffer& cb)
82     : _buffer{cb._buffer},
83       _mbs_cache{cb._mbs_cache},
84       _mbs_cache_valid{cb._mbs_cache_valid},
85       _changed{cb._changed},
86       _curs_pos{cb._curs_pos},
87       _offset{cb._offset},
88       _max_size{cb._max_size} {}
89 
CursorBuffer(CursorBuffer && cb)90 CursorBuffer::CursorBuffer(CursorBuffer&& cb)
91     : _buffer{std::move(cb._buffer)},
92       _mbs_cache{cb._mbs_cache},
93       _mbs_cache_valid{cb._mbs_cache_valid},
94       _changed{cb._changed},
95       _curs_pos{cb._curs_pos},
96       _offset{cb._offset},
97       _max_size{cb._max_size} {}
98 
operator =(const CursorBuffer & cb)99 CursorBuffer& CursorBuffer::operator=(const CursorBuffer& cb) {
100     if (&cb == this) return *this;
101 
102     _buffer = cb._buffer;
103     _mbs_cache = cb._mbs_cache;
104     _mbs_cache_valid = cb._mbs_cache_valid;
105     _changed = cb._changed;
106     _curs_pos = cb._curs_pos;
107     _offset = cb._offset;
108     _max_size = cb._max_size;
109 
110     return *this;
111 }
112 
operator =(CursorBuffer && cb)113 CursorBuffer& CursorBuffer::operator=(CursorBuffer&& cb) {
114     if (&cb == this) return *this;
115 
116     _buffer = std::move(cb._buffer);
117     _mbs_cache = cb._mbs_cache;
118     _mbs_cache_valid = cb._mbs_cache_valid;
119     _changed = cb._changed;
120     _curs_pos = cb._curs_pos;
121     _offset = cb._offset;
122     _max_size = cb._max_size;
123 
124     return *this;
125 }
126 
set(const std::wstring & buffer)127 void CursorBuffer::set(const std::wstring& buffer) {
128     // Do nothing if the string is the same as the one we already have
129     if (buffer == _buffer) return;
130 
131     _curs_pos = 0;
132     _offset = 0;
133     if (buffer.length() > _max_size)
134         _buffer = buffer.substr(0, _max_size);
135     else
136         _buffer = buffer;
137 
138     _mbs_cache_valid = false;
139     _changed = false;
140 }
141 
set(const std::string & buffer)142 void CursorBuffer::set(const std::string& buffer) {
143     wchar_t* tmpbuf = new wchar_t[buffer.length() + 1];
144 
145     size_t retval = std::mbstowcs(tmpbuf, buffer.c_str(), buffer.length() + 1);
146 
147     if (retval == buffer.length() + 1) tmpbuf[buffer.length()] = L'\0';
148 
149     if (retval == (size_t)-1) {
150         delete[] tmpbuf;
151         throw EXCEPTIONS::SystemError(errno);
152     }
153 
154     set(tmpbuf);
155 
156     delete[] tmpbuf;
157 }
158 
length() const159 CursorBuffer::tsz_t CursorBuffer::length() const { return _buffer.length(); }
160 
max_size(CursorBuffer::tsz_t max_size)161 void CursorBuffer::max_size(CursorBuffer::tsz_t max_size) {
162     _max_size = max_size;
163 
164     // Re-set buffer, so it will be truncated.
165     if (_buffer.length() > max_size) {
166         set(_buffer.substr(0, max_size));
167         // set() does not set _changed state.  Since we know, the
168         // string has been changed, we set it here.
169         _changed = true;
170 
171         // reset scrolling information
172         _curs_pos = 0;
173         _offset = 0;
174     }
175 }
176 
max_size() const177 CursorBuffer::tsz_t CursorBuffer::max_size() const { return _max_size; }
178 
get_cursor() const179 CursorBuffer::tsz_t CursorBuffer::get_cursor() const { return _curs_pos; }
180 
clear()181 void CursorBuffer::clear() {
182     _buffer.clear();
183     _changed = true;
184     _curs_pos = 0;
185     _mbs_cache_valid = false;
186 }
187 
clear_eol()188 void CursorBuffer::clear_eol() {
189     if (_buffer.empty()) return;
190     tsz_t __vcurs_pos = _offset + _curs_pos;
191     _buffer = _buffer.erase(__vcurs_pos, _buffer.length() - __vcurs_pos);
192 
193     _mbs_cache_valid = false;
194     _changed = true;
195 }
196 
clear_sol()197 void CursorBuffer::clear_sol() {
198     if (_buffer.empty() || _curs_pos == 0) return;
199     tsz_t __vcurs_pos = _offset + _curs_pos;
200     _buffer = _buffer.erase(0, __vcurs_pos);
201 
202     _curs_pos = 0;
203     _offset = 0;
204 
205     _mbs_cache_valid = false;
206     _changed = true;
207 }
208 
backspace()209 void CursorBuffer::backspace() {
210     if (_curs_pos == 0 || _buffer.empty()) return;
211 
212     _curs_pos--;
213 
214     tsz_t __vcurs_pos = _offset + _curs_pos;
215 
216     _buffer = _buffer.erase(__vcurs_pos, 1);
217 
218     _mbs_cache_valid = false;
219     _changed = true;
220 }
221 
del()222 void CursorBuffer::del() {
223     tsz_t __vcurs_pos = _offset + _curs_pos;
224     if (__vcurs_pos >= _buffer.length() || _buffer.empty()) return;
225 
226     _buffer = _buffer.erase(__vcurs_pos, 1);
227 
228     _mbs_cache_valid = false;
229     _changed = true;
230 }
231 
home()232 void CursorBuffer::home() {
233     _curs_pos = 0;
234     _offset = 0;
235 }
236 
end()237 void CursorBuffer::end() {
238     if (_buffer.empty()) return;
239     _curs_pos = _buffer.length();
240 }
241 
back_step()242 void CursorBuffer::back_step() {
243     if (_curs_pos == 0 || _buffer.empty()) return;
244 
245     _curs_pos--;
246 }
247 
forward_step()248 void CursorBuffer::forward_step() {
249     if (_buffer.empty() || _offset + _curs_pos >= _buffer.length()) return;
250 
251     _curs_pos++;
252 }
253 
insert(std::wstring::value_type c)254 void CursorBuffer::insert(std::wstring::value_type c) {
255     // do not overrun the max size
256     if (_buffer.length() >= _max_size) return;
257 
258     _mbs_cache_valid = false;
259     _changed = true;
260 
261     tsz_t __vcurs_pos = _offset + _curs_pos;
262 
263     if (__vcurs_pos == _buffer.length())
264         _buffer.push_back(c);
265     else
266         _buffer.insert(__vcurs_pos, 1, c);
267 
268     _curs_pos++;
269 }
270 
271 /**
272  *
273  * Not sure fi *len is correct, but *curs_pos should fit...
274  *
275  */
info(int16_t _size,int16_t * len,int16_t * curs_pos) const276 void CursorBuffer::info(int16_t _size, int16_t* len, int16_t* curs_pos) const {
277     if (_size < 2) throw std::out_of_range(_("_size must not be <2"));
278 
279     if (curs_pos) *curs_pos = _curs_pos;
280 
281     if (len) {
282         if (_buffer.empty())
283             *len = 0;
284         else
285             *len =
286                 static_cast<uint16_t>(((_offset + _size > _buffer.length() - 1)
287                                            ? _buffer.length() - _offset
288                                            : _size) -
289                                       _offset);
290     }
291 }
292 
293 /**
294  * @author Markus Neumann
295  *
296  * @internal
297  *
298  * This implementation shows propper scrolling.
299  *
300  */
wstring(int16_t _size,int16_t * curs_pos)301 std::wstring CursorBuffer::wstring(int16_t _size, int16_t* curs_pos) {
302     if (_size < 2) throw std::out_of_range(_("_size must not be <2"));
303 
304     /**
305      *
306      * _offset needs to be changed, if _curs_pos exceeds window limit.
307      *
308      */
309     if (_curs_pos >= _size) {
310         _offset = _offset + _curs_pos - _size + 1;
311         _curs_pos = _size - 1;
312     }
313     /**
314      *
315      * display always 1char on the left, to see what you delete.
316      *
317      */
318     if (_curs_pos <= 0) {
319         if (_offset > 0) {
320             _offset = _offset + _curs_pos - 1;
321             _curs_pos = 1;
322         }
323     }
324 
325     if (curs_pos) *curs_pos = _curs_pos;
326 
327     return _buffer.substr(_offset, (_offset + _size > _buffer.length() - 1)
328                                        ? _buffer.length() - _offset
329                                        : _size);
330 }
331 
string(int16_t _size,int16_t * curs_pos)332 std::string CursorBuffer::string(int16_t _size, int16_t* curs_pos) {
333     std::wstring tmp_wbuf = wstring(_size, curs_pos);
334 
335     size_t mbr_len = std::wcstombs(0, tmp_wbuf.c_str(), 0);
336     char* tmp_mb_buff = new char[mbr_len + 1];
337 
338     if (std::wcstombs(tmp_mb_buff, tmp_wbuf.c_str(), mbr_len + 1) ==
339         (size_t)-1) {
340         delete[] tmp_mb_buff;
341         throw EXCEPTIONS::SystemError(errno);
342     }
343 
344     std::string retval(tmp_mb_buff);
345     delete[] tmp_mb_buff;
346 
347     return retval;
348 }
349 
wstring() const350 const std::wstring& CursorBuffer::wstring() const { return _buffer; }
351 
string() const352 const std::string& CursorBuffer::string() const {
353     if (_mbs_cache_valid) return _mbs_cache;
354 
355     size_t mbr_len = std::wcstombs(0, _buffer.c_str(), 0);
356     char* tmp_mb_buff = new char[mbr_len + 1];
357 
358     if (std::wcstombs(tmp_mb_buff, _buffer.c_str(), mbr_len + 1) ==
359         (size_t)-1) {
360         delete[] tmp_mb_buff;
361         throw EXCEPTIONS::SystemError(errno);
362     }
363 
364     _mbs_cache = tmp_mb_buff;
365     _mbs_cache_valid = true;
366 
367     delete[] tmp_mb_buff;
368 
369     return _mbs_cache;
370 }
371 
changed() const372 bool CursorBuffer::changed() const { return _changed; }
373