diff options
Diffstat (limited to 'apps/plugins/textviewer/tv_readtext.c')
-rw-r--r-- | apps/plugins/textviewer/tv_readtext.c | 483 |
1 files changed, 0 insertions, 483 deletions
diff --git a/apps/plugins/textviewer/tv_readtext.c b/apps/plugins/textviewer/tv_readtext.c deleted file mode 100644 index 988ee637eb..0000000000 --- a/apps/plugins/textviewer/tv_readtext.c +++ /dev/null | |||
@@ -1,483 +0,0 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2002 Gilles Roux | ||
11 | * 2003 Garrett Derner | ||
12 | * 2010 Yoshihisa Uchida | ||
13 | * | ||
14 | * This program is free software; you can redistribute it and/or | ||
15 | * modify it under the terms of the GNU General Public License | ||
16 | * as published by the Free Software Foundation; either version 2 | ||
17 | * of the License, or (at your option) any later version. | ||
18 | * | ||
19 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
20 | * KIND, either express or implied. | ||
21 | * | ||
22 | ****************************************************************************/ | ||
23 | #include "plugin.h" | ||
24 | #include "tv_readtext.h" | ||
25 | |||
26 | #define WRAP_TRIM 44 /* Max number of spaces to trim (arbitrary) */ | ||
27 | #define NARROW_MAX_COLUMNS 64 /* Max displayable string len [narrow] (over-estimate) */ | ||
28 | #define WIDE_MAX_COLUMNS 128 /* Max displayable string len [wide] (over-estimate) */ | ||
29 | #define MAX_WIDTH 910 /* Max line length in WIDE mode */ | ||
30 | #define READ_PREV_ZONE (block_size*9/10) /* Arbitrary number less than SMALL_BLOCK_SIZE */ | ||
31 | #define SMALL_BLOCK_SIZE block_size /* Smallest file chunk we will read */ | ||
32 | #define LARGE_BLOCK_SIZE (block_size << 1) /* Preferable size of file chunk to read */ | ||
33 | #define TOP_SECTOR buffer | ||
34 | #define MID_SECTOR (buffer + SMALL_BLOCK_SIZE) | ||
35 | #define BOTTOM_SECTOR (buffer + (SMALL_BLOCK_SIZE << 1)) | ||
36 | #undef SCROLLBAR_WIDTH | ||
37 | #define SCROLLBAR_WIDTH rb->global_settings->scrollbar_width | ||
38 | #define MAX_PAGE 9999 | ||
39 | |||
40 | /* Out-Of-Bounds test for any pointer to data in the buffer */ | ||
41 | #define BUFFER_OOB(p) ((p) < buffer || (p) >= buffer_end) | ||
42 | |||
43 | /* Does the buffer contain the beginning of the file? */ | ||
44 | #define BUFFER_BOF() (file_pos==0) | ||
45 | |||
46 | /* Does the buffer contain the end of the file? */ | ||
47 | #define BUFFER_EOF() (file_size-file_pos <= buffer_size) | ||
48 | |||
49 | /* Formula for the endpoint address outside of buffer data */ | ||
50 | #define BUFFER_END() \ | ||
51 | ((BUFFER_EOF()) ? (file_size-file_pos+buffer) : (buffer+buffer_size)) | ||
52 | |||
53 | /* Is the entire file being shown in one screen? */ | ||
54 | #define ONE_SCREEN_FITS_ALL() \ | ||
55 | (next_screen_ptr==NULL && screen_top_ptr==buffer && BUFFER_BOF()) | ||
56 | |||
57 | #define ADVANCE_COUNTERS(c) { width += glyph_width(c); k++; } | ||
58 | #define LINE_IS_FULL ((k>=max_columns-1) ||( width >= max_width)) | ||
59 | #define LINE_IS_NOT_FULL ((k<max_columns-1) &&( width < max_width)) | ||
60 | |||
61 | |||
62 | static unsigned char file_name[MAX_PARH+1]; | ||
63 | static int fd = -1; | ||
64 | |||
65 | static unsigned char *buffer; | ||
66 | static long buffer_size; | ||
67 | static long block_size = 0x1000; | ||
68 | |||
69 | void viewer_init_buffer(void) | ||
70 | { | ||
71 | /* get the plugin buffer */ | ||
72 | buffer = rb->plugin_get_buffer((size_t *)&buffer_size); | ||
73 | if (buffer_size == 0) | ||
74 | { | ||
75 | rb->splash(HZ, "buffer does not allocate !!"); | ||
76 | return PLUGIN_ERROR; | ||
77 | } | ||
78 | block_size = buffer_size / 3; | ||
79 | buffer_size = 3 * block_size; | ||
80 | } | ||
81 | |||
82 | static unsigned char* crop_at_width(const unsigned char* p) | ||
83 | { | ||
84 | int k,width; | ||
85 | unsigned short ch; | ||
86 | const unsigned char *oldp = p; | ||
87 | |||
88 | k=width=0; | ||
89 | |||
90 | while (LINE_IS_NOT_FULL) { | ||
91 | oldp = p; | ||
92 | if (BUFFER_OOB(p)) | ||
93 | break; | ||
94 | p = get_ucs(p, &ch); | ||
95 | ADVANCE_COUNTERS(ch); | ||
96 | } | ||
97 | |||
98 | return (unsigned char*)oldp; | ||
99 | } | ||
100 | |||
101 | static unsigned char* find_first_feed(const unsigned char* p, int size) | ||
102 | { | ||
103 | int i; | ||
104 | |||
105 | for (i=0; i < size; i++) | ||
106 | if (p[i] == 0) | ||
107 | return (unsigned char*) p+i; | ||
108 | |||
109 | return NULL; | ||
110 | } | ||
111 | |||
112 | static unsigned char* find_last_feed(const unsigned char* p, int size) | ||
113 | { | ||
114 | int i; | ||
115 | |||
116 | for (i=size-1; i>=0; i--) | ||
117 | if (p[i] == 0) | ||
118 | return (unsigned char*) p+i; | ||
119 | |||
120 | return NULL; | ||
121 | } | ||
122 | |||
123 | static unsigned char* find_last_space(const unsigned char* p, int size) | ||
124 | { | ||
125 | int i, j, k; | ||
126 | |||
127 | k = (prefs.line_mode==JOIN) || (prefs.line_mode==REFLOW) ? 0:1; | ||
128 | |||
129 | if (!BUFFER_OOB(&p[size])) | ||
130 | for (j=k; j < ((int) sizeof(line_break)) - 1; j++) | ||
131 | if (p[size] == line_break[j]) | ||
132 | return (unsigned char*) p+size; | ||
133 | |||
134 | for (i=size-1; i>=0; i--) | ||
135 | for (j=k; j < (int) sizeof(line_break); j++) | ||
136 | { | ||
137 | if (!((p[i] == '-') && (prefs.word_mode == WRAP))) | ||
138 | if (p[i] == line_break[j]) | ||
139 | return (unsigned char*) p+i; | ||
140 | } | ||
141 | |||
142 | return NULL; | ||
143 | } | ||
144 | |||
145 | static unsigned char* find_next_line(const unsigned char* cur_line, bool *is_short) | ||
146 | { | ||
147 | const unsigned char *next_line = NULL; | ||
148 | int size, i, j, k, width, search_len, spaces, newlines; | ||
149 | bool first_chars; | ||
150 | unsigned char c; | ||
151 | |||
152 | if (is_short != NULL) | ||
153 | *is_short = true; | ||
154 | |||
155 | if BUFFER_OOB(cur_line) | ||
156 | return NULL; | ||
157 | |||
158 | if (prefs.view_mode == WIDE) { | ||
159 | search_len = MAX_WIDTH; | ||
160 | } | ||
161 | else { /* prefs.view_mode == NARROW */ | ||
162 | search_len = crop_at_width(cur_line) - cur_line; | ||
163 | } | ||
164 | |||
165 | size = BUFFER_OOB(cur_line+search_len) ? buffer_end-cur_line : search_len; | ||
166 | |||
167 | if ((prefs.line_mode == JOIN) || (prefs.line_mode == REFLOW)) { | ||
168 | /* Need to scan ahead and possibly increase search_len and size, | ||
169 | or possibly set next_line at second hard return in a row. */ | ||
170 | next_line = NULL; | ||
171 | first_chars=true; | ||
172 | for (j=k=width=spaces=newlines=0; ; j++) { | ||
173 | if (BUFFER_OOB(cur_line+j)) | ||
174 | return NULL; | ||
175 | if (LINE_IS_FULL) { | ||
176 | size = search_len = j; | ||
177 | break; | ||
178 | } | ||
179 | |||
180 | c = cur_line[j]; | ||
181 | switch (c) { | ||
182 | case ' ': | ||
183 | if (prefs.line_mode == REFLOW) { | ||
184 | if (newlines > 0) { | ||
185 | size = j; | ||
186 | next_line = cur_line + size; | ||
187 | return (unsigned char*) next_line; | ||
188 | } | ||
189 | if (j==0) /* i=1 is intentional */ | ||
190 | for (i=0; i<par_indent_spaces; i++) | ||
191 | ADVANCE_COUNTERS(' '); | ||
192 | } | ||
193 | if (!first_chars) spaces++; | ||
194 | break; | ||
195 | |||
196 | case 0: | ||
197 | if (newlines > 0) { | ||
198 | size = j; | ||
199 | next_line = cur_line + size - spaces; | ||
200 | if (next_line != cur_line) | ||
201 | return (unsigned char*) next_line; | ||
202 | break; | ||
203 | } | ||
204 | |||
205 | newlines++; | ||
206 | size += spaces -1; | ||
207 | if (BUFFER_OOB(cur_line+size) || size > 2*search_len) | ||
208 | return NULL; | ||
209 | search_len = size; | ||
210 | spaces = first_chars? 0:1; | ||
211 | break; | ||
212 | |||
213 | default: | ||
214 | if (prefs.line_mode==JOIN || newlines>0) { | ||
215 | while (spaces) { | ||
216 | spaces--; | ||
217 | ADVANCE_COUNTERS(' '); | ||
218 | if (LINE_IS_FULL) { | ||
219 | size = search_len = j; | ||
220 | break; | ||
221 | } | ||
222 | } | ||
223 | newlines=0; | ||
224 | } else if (spaces) { | ||
225 | /* REFLOW, multiple spaces between words: count only | ||
226 | * one. If more are needed, they will be added | ||
227 | * while drawing. */ | ||
228 | search_len = size; | ||
229 | spaces=0; | ||
230 | ADVANCE_COUNTERS(' '); | ||
231 | if (LINE_IS_FULL) { | ||
232 | size = search_len = j; | ||
233 | break; | ||
234 | } | ||
235 | } | ||
236 | first_chars = false; | ||
237 | ADVANCE_COUNTERS(c); | ||
238 | break; | ||
239 | } | ||
240 | } | ||
241 | } | ||
242 | else { | ||
243 | /* find first hard return */ | ||
244 | next_line = find_first_feed(cur_line, size); | ||
245 | } | ||
246 | |||
247 | if (next_line == NULL) | ||
248 | if (size == search_len) { | ||
249 | if (prefs.word_mode == WRAP) /* Find last space */ | ||
250 | next_line = find_last_space(cur_line, size); | ||
251 | |||
252 | if (next_line == NULL) | ||
253 | next_line = crop_at_width(cur_line); | ||
254 | else | ||
255 | if (prefs.word_mode == WRAP) | ||
256 | for (i=0; | ||
257 | i<WRAP_TRIM && isspace(next_line[0]) && !BUFFER_OOB(next_line); | ||
258 | i++) | ||
259 | next_line++; | ||
260 | } | ||
261 | |||
262 | if (prefs.line_mode == EXPAND) | ||
263 | if (!BUFFER_OOB(next_line)) /* Not Null & not out of bounds */ | ||
264 | if (next_line[0] == 0) | ||
265 | if (next_line != cur_line) | ||
266 | return (unsigned char*) next_line; | ||
267 | |||
268 | /* If next_line is pointing to a zero, increment it; i.e., | ||
269 | leave the terminator at the end of cur_line. If pointing | ||
270 | to a hyphen, increment only if there is room to display | ||
271 | the hyphen on current line (won't apply in WIDE mode, | ||
272 | since it's guarenteed there won't be room). */ | ||
273 | if (!BUFFER_OOB(next_line)) /* Not Null & not out of bounds */ | ||
274 | if (next_line[0] == 0)/* || | ||
275 | (next_line[0] == '-' && next_line-cur_line < draw_columns)) */ | ||
276 | next_line++; | ||
277 | |||
278 | if (BUFFER_OOB(next_line)) | ||
279 | { | ||
280 | if (BUFFER_EOF() && next_line != cur_line) | ||
281 | return (unsigned char*) next_line; | ||
282 | return NULL; | ||
283 | } | ||
284 | |||
285 | if (is_short) | ||
286 | *is_short = false; | ||
287 | |||
288 | return (unsigned char*) next_line; | ||
289 | } | ||
290 | |||
291 | static unsigned char* find_prev_line(const unsigned char* cur_line) | ||
292 | { | ||
293 | const unsigned char *prev_line = NULL; | ||
294 | const unsigned char *p; | ||
295 | |||
296 | if BUFFER_OOB(cur_line) | ||
297 | return NULL; | ||
298 | |||
299 | /* To wrap consistently at the same places, we must | ||
300 | start with a known hard return, then work downwards. | ||
301 | We can either search backwards for a hard return, | ||
302 | or simply start wrapping downwards from top of buffer. | ||
303 | If current line is not near top of buffer, this is | ||
304 | a file with long lines (paragraphs). We would need to | ||
305 | read earlier sectors before we could decide how to | ||
306 | properly wrap the lines above the current line, but | ||
307 | it probably is not worth the disk access. Instead, | ||
308 | start with top of buffer and wrap down from there. | ||
309 | This may result in some lines wrapping at different | ||
310 | points from where they wrap when scrolling down. | ||
311 | If buffer is at top of file, start at top of buffer. */ | ||
312 | |||
313 | if ((prefs.line_mode == JOIN) || (prefs.line_mode == REFLOW)) | ||
314 | prev_line = p = NULL; | ||
315 | else | ||
316 | prev_line = p = find_last_feed(buffer, cur_line-buffer-1); | ||
317 | /* Null means no line feeds in buffer above current line. */ | ||
318 | |||
319 | if (prev_line == NULL) | ||
320 | if (BUFFER_BOF() || cur_line - buffer > READ_PREV_ZONE) | ||
321 | prev_line = p = buffer; | ||
322 | /* (else return NULL and read previous block) */ | ||
323 | |||
324 | /* Wrap downwards until too far, then use the one before. */ | ||
325 | while (p != NULL && p < cur_line) { | ||
326 | prev_line = p; | ||
327 | p = find_next_line(prev_line, NULL); | ||
328 | } | ||
329 | |||
330 | if (BUFFER_OOB(prev_line)) | ||
331 | return NULL; | ||
332 | |||
333 | return (unsigned char*) prev_line; | ||
334 | } | ||
335 | |||
336 | static void check_bom(void) | ||
337 | { | ||
338 | unsigned char bom[BOM_SIZE]; | ||
339 | off_t orig = rb->lseek(fd, 0, SEEK_CUR); | ||
340 | |||
341 | is_bom = false; | ||
342 | |||
343 | rb->lseek(fd, 0, SEEK_SET); | ||
344 | |||
345 | if (rb->read(fd, bom, BOM_SIZE) == BOM_SIZE) | ||
346 | is_bom = !memcmp(bom, BOM, BOM_SIZE); | ||
347 | |||
348 | rb->lseek(fd, orig, SEEK_SET); | ||
349 | } | ||
350 | |||
351 | static void fill_buffer(long pos, unsigned char* buf, unsigned size) | ||
352 | { | ||
353 | /* Read from file and preprocess the data */ | ||
354 | /* To minimize disk access, always read on sector boundaries */ | ||
355 | unsigned numread, i; | ||
356 | bool found_CR = false; | ||
357 | off_t offset = rb->lseek(fd, pos, SEEK_SET); | ||
358 | |||
359 | if (offset == 0 && prefs.encoding == UTF_8 && is_bom) | ||
360 | rb->lseek(fd, BOM_SIZE, SEEK_SET); | ||
361 | |||
362 | numread = rb->read(fd, buf, size); | ||
363 | buf[numread] = 0; | ||
364 | rb->button_clear_queue(); /* clear button queue */ | ||
365 | |||
366 | for(i = 0; i < numread; i++) { | ||
367 | switch(buf[i]) { | ||
368 | case '\r': | ||
369 | if (mac_text) { | ||
370 | buf[i] = 0; | ||
371 | } | ||
372 | else { | ||
373 | buf[i] = ' '; | ||
374 | found_CR = true; | ||
375 | } | ||
376 | break; | ||
377 | |||
378 | case '\n': | ||
379 | buf[i] = 0; | ||
380 | found_CR = false; | ||
381 | break; | ||
382 | |||
383 | case 0: /* No break between case 0 and default, intentionally */ | ||
384 | buf[i] = ' '; | ||
385 | default: | ||
386 | if (found_CR) { | ||
387 | buf[i - 1] = 0; | ||
388 | found_CR = false; | ||
389 | mac_text = true; | ||
390 | } | ||
391 | break; | ||
392 | } | ||
393 | } | ||
394 | } | ||
395 | |||
396 | static int read_and_synch(int direction) | ||
397 | { | ||
398 | /* Read next (or prev) block, and reposition global pointers. */ | ||
399 | /* direction: 1 for down (i.e., further into file), -1 for up */ | ||
400 | int move_size, move_vector, offset; | ||
401 | unsigned char *fill_buf; | ||
402 | |||
403 | if (direction == -1) /* up */ { | ||
404 | move_size = SMALL_BLOCK_SIZE; | ||
405 | offset = 0; | ||
406 | fill_buf = TOP_SECTOR; | ||
407 | rb->memcpy(BOTTOM_SECTOR, MID_SECTOR, SMALL_BLOCK_SIZE); | ||
408 | rb->memcpy(MID_SECTOR, TOP_SECTOR, SMALL_BLOCK_SIZE); | ||
409 | } | ||
410 | else /* down */ { | ||
411 | if (prefs.view_mode == WIDE) { | ||
412 | /* WIDE mode needs more buffer so we have to read smaller blocks */ | ||
413 | move_size = SMALL_BLOCK_SIZE; | ||
414 | offset = LARGE_BLOCK_SIZE; | ||
415 | fill_buf = BOTTOM_SECTOR; | ||
416 | rb->memcpy(TOP_SECTOR, MID_SECTOR, SMALL_BLOCK_SIZE); | ||
417 | rb->memcpy(MID_SECTOR, BOTTOM_SECTOR, SMALL_BLOCK_SIZE); | ||
418 | } | ||
419 | else { | ||
420 | move_size = LARGE_BLOCK_SIZE; | ||
421 | offset = SMALL_BLOCK_SIZE; | ||
422 | fill_buf = MID_SECTOR; | ||
423 | rb->memcpy(TOP_SECTOR, BOTTOM_SECTOR, SMALL_BLOCK_SIZE); | ||
424 | } | ||
425 | } | ||
426 | move_vector = direction * move_size; | ||
427 | screen_top_ptr -= move_vector; | ||
428 | file_pos += move_vector; | ||
429 | buffer_end = BUFFER_END(); /* Update whenever file_pos changes */ | ||
430 | fill_buffer(file_pos + offset, fill_buf, move_size); | ||
431 | return move_vector; | ||
432 | } | ||
433 | |||
434 | static void get_next_line_position(unsigned char **line_begin, | ||
435 | unsigned char **line_end, | ||
436 | bool *is_short) | ||
437 | { | ||
438 | int resynch_move; | ||
439 | |||
440 | *line_begin = *line_end; | ||
441 | *line_end = find_next_line(*line_begin, is_short); | ||
442 | |||
443 | if (*line_end == NULL && !BUFFER_EOF()) | ||
444 | { | ||
445 | resynch_move = read_and_synch(1); /* Read block & move ptrs */ | ||
446 | *line_begin -= resynch_move; | ||
447 | if (next_line_ptr > buffer) | ||
448 | next_line_ptr -= resynch_move; | ||
449 | |||
450 | *line_end = find_next_line(*line_begin, is_short); | ||
451 | } | ||
452 | } | ||
453 | |||
454 | /* open, close, get file size functions */ | ||
455 | |||
456 | bool viewer_open(const unsigned char *fname) | ||
457 | { | ||
458 | if (fname == 0) | ||
459 | return false; | ||
460 | |||
461 | rb->strlcpy(file_name, fname, MAX_PATH+1); | ||
462 | fd = rb->open(fname, O_RDONLY); | ||
463 | return (fd >= 0); | ||
464 | } | ||
465 | |||
466 | void viewer_close(void) | ||
467 | { | ||
468 | if (fd >= 0) | ||
469 | rb->close(fd); | ||
470 | } | ||
471 | |||
472 | /* When a file is UTF-8 file with BOM, if prefs.encoding is UTF-8, | ||
473 | * then file size decreases only BOM_SIZE. | ||
474 | */ | ||
475 | static void get_filesize(void) | ||
476 | { | ||
477 | file_size = rb->filesize(fd); | ||
478 | if (file_size == -1) | ||
479 | return; | ||
480 | |||
481 | if (prefs.encoding == UTF_8 && is_bom) | ||
482 | file_size -= BOM_SIZE; | ||
483 | } | ||