RaaA: Fix write locations of plugins
[maemo-rb.git] / apps / plugins / text_viewer / tv_settings.c
blob3ed1576dc5038899b414c7963363f9ba7e34dee6
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2002 Gilles Roux
11 * 2003 Garrett Derner
12 * 2010 Yoshihisa Uchida
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.
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
22 ****************************************************************************/
23 #include "plugin.h"
24 #include "tv_bookmark.h"
25 #include "tv_settings.h"
27 /* global settings file
28 * binary file, so dont use .cfg
30 * setting file format
32 * part byte count
33 * -------------------------------
34 * 'TVGS' 4
35 * version 1
36 * word_mode 1
37 * line_mode 1
38 * windows 1 (when version <= 0x32, this value is view_mode)
39 * alignment 1
40 * encoding 1
41 * vertical_scrollbar 1
42 * (unused) 1 (for compatibility)
43 * overlap_page_mode 1
44 * header_mode 1
45 * footer_mode 1
46 * vertical_scroll_mode 1
47 * autoscroll_speed 1
48 * horizontal_scrollbar 1
49 * horizontal_scroll_mode 1
50 * narrow_mode 1
51 * indent_spaces 1
52 * statusbar 1
53 * (reserved) 11
54 * font name MAX_PATH
57 #define VIEWER_GLOBAL_SETTINGS_FILE VIEWERS_DATA_DIR "/viewer.dat"
58 #define TV_GLOBAL_SETTINGS_FILE VIEWERS_DATA_DIR "/tv_global.dat"
60 #define TV_GLOBAL_SETTINGS_HEADER "\x54\x56\x47\x53" /* "TVGS" */
61 #define TV_GLOBAL_SETTINGS_VERSION 0x38
62 #define TV_GLOBAL_SETTINGS_HEADER_SIZE 5
63 #define TV_GLOBAL_SETTINGS_FIRST_VERSION 0x31
65 /* preferences and bookmarks at each file
66 * binary file, so dont use .cfg
68 * setting file format
70 * part byte count
71 * -----------------------------------
72 * 'TVS' 3
73 * version 1
74 * file count 2
75 * [1st file]
76 * file path MAX_PATH
77 * next file pos 2 (prefences size + bookmark count * bookmark size + 1)
78 * [preferences]
79 * word_mode 1
80 * line_mode 1
81 * windows 1 (when version <= 0x33, this value is view_mode)
82 * alignment 1
83 * encoding 1
84 * vertical_scrollbar 1
85 * (unused) 1 (for compatibility)
86 * overlap_page_mode 1
87 * header_mode 1
88 * footer_mode 1
89 * vertical_scroll_mode 1
90 * autoscroll_speed 1
91 * horizontal_scrollbar 1
92 * horizontal_scroll_mode 1
93 * narrow_mode 1
94 * indent_spaces 1
95 * statusbar 1
96 * (reserved) 11
97 * font name MAX_PATH
98 * bookmark count 1
99 * [1st bookmark]
100 * file_position 4
101 * page 2
102 * line 1
103 * flag 1
104 * [2nd bookmark]
105 * ...
106 * [last bookmark]
107 * [2nd file]
108 * ...
109 * [last file]
111 #define VIEWER_SETTINGS_FILE VIEWERS_DATA_DIR "/viewer_file.dat"
112 #define TV_SETTINGS_FILE VIEWERS_DATA_DIR "/tv_file.dat"
114 /* temporary file */
115 #define TV_SETTINGS_TMP_FILE VIEWERS_DATA_DIR "/tv_file.tmp"
117 #define TV_SETTINGS_HEADER "\x54\x56\x53" /* "TVS" */
118 #define TV_SETTINGS_VERSION 0x39
119 #define TV_SETTINGS_HEADER_SIZE 4
120 #define TV_SETTINGS_FIRST_VERSION 0x32
122 #define TV_PREFERENCES_SIZE (28 + MAX_PATH)
123 #define TV_MAX_FILE_RECORD_SIZE (MAX_PATH+2 + TV_PREFERENCES_SIZE + TV_MAX_BOOKMARKS*SERIALIZE_BOOKMARK_SIZE+1)
125 static off_t stored_preferences_offset = 0;
126 static int stored_preferences_size = 0;
128 /* ----------------------------------------------------------------------------
129 * read/write the preferences
130 * ----------------------------------------------------------------------------
133 static bool tv_read_preferences(int pfd, int version, struct tv_preferences *prefs)
135 unsigned char buf[TV_PREFERENCES_SIZE];
136 const unsigned char *p = buf;
137 int read_size = TV_PREFERENCES_SIZE;
139 if (version == 0)
140 read_size -= 17;
141 else if (version == 1)
142 read_size -= 16;
144 if (rb->read(pfd, buf, read_size) < 0)
145 return false;
147 prefs->word_mode = *p++;
148 prefs->line_mode = *p++;
150 prefs->windows = *p++;
151 if (version <= 1)
152 prefs->windows++;
154 if (version > 0)
155 prefs->alignment = *p++;
156 else
157 prefs->alignment = AL_LEFT;
159 prefs->encoding = *p++;
160 prefs->vertical_scrollbar = (*p++ != 0);
161 /* skip need_scrollbar */
162 p++;
163 prefs->overlap_page_mode = (*p++ != 0);
165 if (version < 7)
167 prefs->statusbar = false;
168 if (*p > 1)
170 prefs->header_mode = ((*p & 1) != 0);
171 prefs->statusbar = true;
173 else
174 prefs->header_mode = (*p != 0);
176 if (*(++p) > 1)
178 prefs->footer_mode = ((*p & 1) != 0);
179 prefs->statusbar = true;
181 else
182 prefs->footer_mode = (*p != 0);
184 p++;
186 else
188 prefs->header_mode = (*p++ != 0);
189 prefs->footer_mode = (*p++ != 0);
192 prefs->vertical_scroll_mode = *p++;
193 prefs->autoscroll_speed = *p++;
195 if (version > 2)
196 prefs->horizontal_scrollbar = (*p++ != 0);
197 else
198 prefs->horizontal_scrollbar = false;
200 if (version > 3)
201 prefs->horizontal_scroll_mode = *p++;
202 else
203 prefs->horizontal_scroll_mode = HS_SCREEN;
205 if (version > 4)
206 prefs->narrow_mode = *p++;
207 else
208 prefs->narrow_mode = NM_PAGE;
210 if (version > 5)
211 prefs->indent_spaces = *p++;
212 else
213 prefs->indent_spaces = 2;
215 if (version > 6)
216 prefs->statusbar = (*p++ != 0);
218 #ifdef HAVE_LCD_BITMAP
219 rb->strlcpy(prefs->font_name, buf + read_size - MAX_PATH, MAX_PATH);
221 prefs->font = rb->font_get(FONT_UI);
222 #endif
224 return true;
227 static void tv_serialize_preferences(unsigned char *buf, const struct tv_preferences *prefs)
229 unsigned char *p = buf;
231 rb->memset(buf, 0, TV_PREFERENCES_SIZE);
232 *p++ = prefs->word_mode;
233 *p++ = prefs->line_mode;
234 *p++ = prefs->windows;
235 *p++ = prefs->alignment;
236 *p++ = prefs->encoding;
237 *p++ = prefs->vertical_scrollbar;
238 /* skip need_scrollbar */
239 p++;
240 *p++ = prefs->overlap_page_mode;
241 *p++ = prefs->header_mode;
242 *p++ = prefs->footer_mode;
243 *p++ = prefs->vertical_scroll_mode;
244 *p++ = prefs->autoscroll_speed;
245 *p++ = prefs->horizontal_scrollbar;
246 *p++ = prefs->horizontal_scroll_mode;
247 *p++ = prefs->narrow_mode;
248 *p++ = prefs->indent_spaces;
249 *p++ = prefs->statusbar;
251 #ifdef HAVE_LCD_BITMAP
252 rb->strlcpy(buf + 28, prefs->font_name, MAX_PATH);
253 #endif
256 static bool tv_write_preferences(int pfd, const struct tv_preferences *prefs)
258 unsigned char buf[TV_PREFERENCES_SIZE];
260 tv_serialize_preferences(buf, prefs);
262 return (rb->write(pfd, buf, TV_PREFERENCES_SIZE) >= 0);
265 /* ----------------------------------------------------------------------------
266 * convert vewer.rock's settings file to text_viewer.rock's settings file
267 * ----------------------------------------------------------------------------
270 static bool tv_convert_settings(int sfd, int dfd, int old_ver)
272 struct tv_preferences new_prefs;
273 off_t old_pos;
274 off_t new_pos;
275 unsigned char buf[MAX_PATH + 2];
276 int settings_size;
278 rb->read(sfd, buf, MAX_PATH + 2);
279 rb->write(dfd, buf, MAX_PATH + 2);
281 settings_size = (buf[MAX_PATH] << 8) | buf[MAX_PATH + 1];
283 old_pos = rb->lseek(sfd, 0, SEEK_CUR);
284 new_pos = rb->lseek(dfd, 0, SEEK_CUR);
287 * when the settings size != preferences size + bookmarks size,
288 * settings data are considered to be old version.
290 if (old_ver > 0 && ((settings_size - TV_PREFERENCES_SIZE) % 8) == 0)
291 old_ver = 0;
293 if (!tv_read_preferences(sfd, old_ver, &new_prefs))
294 return false;
296 if (!tv_write_preferences(dfd, &new_prefs))
297 return false;
299 settings_size -= (rb->lseek(sfd, 0, SEEK_CUR) - old_pos);
301 if (settings_size > 0)
303 rb->read(sfd, buf, settings_size);
304 rb->write(dfd, buf, settings_size);
307 settings_size = rb->lseek(dfd, 0, SEEK_CUR) - new_pos;
308 buf[0] = settings_size >> 8;
309 buf[1] = settings_size;
310 rb->lseek(dfd, new_pos - 2, SEEK_SET);
311 rb->write(dfd, buf, 2);
312 rb->lseek(dfd, settings_size, SEEK_CUR);
313 return true;
316 static void tv_convert_settings_file(void)
318 unsigned char buf[TV_SETTINGS_HEADER_SIZE + 2];
319 int sfd;
320 int tfd;
321 int i;
322 int fcount;
323 int version;
324 bool res = false;
326 if ((sfd = rb->open(VIEWER_SETTINGS_FILE, O_RDONLY)) < 0)
327 return;
329 if ((tfd = rb->open(TV_SETTINGS_TMP_FILE, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)
331 rb->close(sfd);
332 return;
335 if (rb->read(sfd, buf, TV_SETTINGS_HEADER_SIZE + 2) >= 0)
337 version = buf[TV_SETTINGS_HEADER_SIZE - 1] - TV_SETTINGS_FIRST_VERSION;
338 fcount = (buf[TV_SETTINGS_HEADER_SIZE] << 8) | buf[TV_SETTINGS_HEADER_SIZE + 1];
339 buf[TV_SETTINGS_HEADER_SIZE - 1] = TV_SETTINGS_VERSION;
341 if (rb->write(tfd, buf, TV_SETTINGS_HEADER_SIZE + 2) >= 0)
343 res = true;
344 for (i = 0; i < fcount; i++)
346 if (!tv_convert_settings(sfd, tfd, version))
348 res = false;
349 break;
355 rb->close(sfd);
356 rb->close(tfd);
358 if (res)
359 rb->rename(TV_SETTINGS_TMP_FILE, TV_SETTINGS_FILE);
360 else
361 rb->remove(TV_SETTINGS_TMP_FILE);
363 return;
366 /* ----------------------------------------------------------------------------
367 * load/save the global settings
368 * ----------------------------------------------------------------------------
371 bool tv_load_global_settings(struct tv_preferences *prefs)
373 unsigned char buf[TV_GLOBAL_SETTINGS_HEADER_SIZE];
374 int fd;
375 int version;
376 bool res = false;
379 * the viewer.rock's setting file read when the text_viewer.rock's setting file
380 * does not read.
382 if ((fd = rb->open(TV_GLOBAL_SETTINGS_FILE, O_RDONLY)) < 0)
383 fd = rb->open(VIEWER_GLOBAL_SETTINGS_FILE, O_RDONLY);
385 if (fd >= 0)
387 if ((rb->read(fd, buf, TV_GLOBAL_SETTINGS_HEADER_SIZE) > 0) &&
388 (rb->memcmp(buf, TV_GLOBAL_SETTINGS_HEADER, TV_GLOBAL_SETTINGS_HEADER_SIZE - 1) == 0))
390 version = buf[TV_GLOBAL_SETTINGS_HEADER_SIZE - 1] - TV_GLOBAL_SETTINGS_FIRST_VERSION;
391 res = tv_read_preferences(fd, version, prefs);
393 rb->close(fd);
395 return res;
398 bool tv_save_global_settings(const struct tv_preferences *prefs)
400 unsigned char buf[TV_GLOBAL_SETTINGS_HEADER_SIZE];
401 int fd;
402 bool res;
404 if ((fd = rb->open(TV_SETTINGS_TMP_FILE, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)
405 return false;
407 rb->memcpy(buf, TV_GLOBAL_SETTINGS_HEADER, TV_GLOBAL_SETTINGS_HEADER_SIZE - 1);
408 buf[TV_GLOBAL_SETTINGS_HEADER_SIZE - 1] = TV_GLOBAL_SETTINGS_VERSION;
410 res = (rb->write(fd, buf, TV_GLOBAL_SETTINGS_HEADER_SIZE) >= 0) &&
411 (tv_write_preferences(fd, prefs));
412 rb->close(fd);
414 if (res)
416 rb->remove(TV_GLOBAL_SETTINGS_FILE);
417 rb->rename(TV_SETTINGS_TMP_FILE, TV_GLOBAL_SETTINGS_FILE);
419 else
420 rb->remove(TV_SETTINGS_TMP_FILE);
422 return res;
425 /* ----------------------------------------------------------------------------
426 * load/save the settings
427 * ----------------------------------------------------------------------------
430 bool tv_load_settings(const unsigned char *file_name)
432 unsigned char buf[MAX_PATH+2];
433 unsigned int fcount;
434 unsigned int i;
435 bool res = false;
436 int fd;
437 int version;
438 unsigned int size;
439 struct tv_preferences prefs;
440 off_t current_pref_offset;
442 if (!rb->file_exists(TV_SETTINGS_FILE))
443 tv_convert_settings_file();
445 if ((fd = rb->open(TV_SETTINGS_FILE, O_RDONLY)) >= 0)
447 if ((rb->read(fd, buf, TV_SETTINGS_HEADER_SIZE + 2) >= 0) &&
448 (rb->memcmp(buf, TV_SETTINGS_HEADER, TV_SETTINGS_HEADER_SIZE - 1) == 0))
450 version = buf[TV_SETTINGS_HEADER_SIZE - 1] - TV_SETTINGS_FIRST_VERSION;
451 fcount = (buf[TV_SETTINGS_HEADER_SIZE] << 8) | buf[TV_SETTINGS_HEADER_SIZE+1];
453 current_pref_offset = rb->lseek(fd, 0, SEEK_CUR);
455 for (i = 0; i < fcount; i++)
457 if (rb->read(fd, buf, MAX_PATH+2) >= 0)
459 size = (buf[MAX_PATH] << 8) | buf[MAX_PATH+1];
460 if (rb->strcmp(buf, file_name) == 0)
462 if (tv_read_preferences(fd, version, &prefs))
463 res = tv_deserialize_bookmarks(fd);
465 if (res) {
466 stored_preferences_offset = current_pref_offset;
467 stored_preferences_size = size;
470 break;
472 current_pref_offset = rb->lseek(fd, size, SEEK_CUR);
475 rb->close(fd);
477 else
479 /* when the settings file is illegal, removes it */
480 rb->close(fd);
481 rb->remove(TV_SETTINGS_FILE);
484 if (!res)
486 /* specifications are acquired from the global settings */
487 if (!tv_load_global_settings(&prefs))
488 tv_set_default_preferences(&prefs);
490 rb->strlcpy(prefs.file_name, file_name, MAX_PATH);
491 return tv_set_preferences(&prefs);
494 bool tv_save_settings(void)
496 unsigned char buf[TV_MAX_FILE_RECORD_SIZE];
497 unsigned char preferences_buf[TV_MAX_FILE_RECORD_SIZE];
498 unsigned int fcount = 0;
499 unsigned int new_fcount = 0;
500 unsigned int i;
501 int ofd = -1;
502 int tfd;
503 off_t size;
504 off_t preferences_buf_size;
505 bool res = true;
507 /* add reading page to bookmarks */
508 tv_create_system_bookmark();
510 /* storing preferences record in memory */
511 rb->memset(preferences_buf, 0, MAX_PATH);
512 rb->strlcpy(preferences_buf, preferences->file_name, MAX_PATH);
513 preferences_buf_size = MAX_PATH + 2;
515 tv_serialize_preferences(preferences_buf + preferences_buf_size, preferences);
516 preferences_buf_size += TV_PREFERENCES_SIZE;
517 preferences_buf_size += tv_serialize_bookmarks(preferences_buf + preferences_buf_size);
518 size = preferences_buf_size - (MAX_PATH + 2);
519 preferences_buf[MAX_PATH + 0] = size >> 8;
520 preferences_buf[MAX_PATH + 1] = size;
523 /* Just overwrite preferences if possible*/
524 if ( (stored_preferences_offset > 0) && (stored_preferences_size == size) )
526 DEBUGF("Saving preferences: overwriting\n");
527 if ((tfd = rb->open(TV_SETTINGS_FILE, O_WRONLY)) < 0)
528 return false;
529 rb->lseek(tfd, stored_preferences_offset, SEEK_SET);
530 res = (rb->write(tfd, preferences_buf, preferences_buf_size) >= 0);
531 rb->close(tfd);
532 return res;
536 if (!rb->file_exists(TV_SETTINGS_FILE))
537 tv_convert_settings_file();
540 /* Try appending preferences */
541 if ( (stored_preferences_offset == 0) &&
542 ( (tfd = rb->open(TV_SETTINGS_FILE, O_RDWR)) >= 0) )
544 DEBUGF("Saving preferences: appending\n");
545 rb->lseek(tfd, 0, SEEK_END);
546 if (rb->write(tfd, preferences_buf, preferences_buf_size) < 0)
547 return false;
549 rb->lseek(tfd, TV_SETTINGS_HEADER_SIZE, SEEK_SET);
550 rb->read(tfd, buf, 2);
551 fcount = (buf[0] << 8) | buf[1];
552 fcount ++;
553 buf[0] = fcount >> 8;
554 buf[1] = fcount;
555 rb->lseek(tfd, TV_SETTINGS_HEADER_SIZE, SEEK_SET);
556 res = rb->write(tfd, buf, 2) >= 0;
558 rb->close(tfd);
559 return res;
562 /* create header for the temporary file */
563 rb->memcpy(buf, TV_SETTINGS_HEADER, TV_SETTINGS_HEADER_SIZE - 1);
564 buf[TV_SETTINGS_HEADER_SIZE - 1] = TV_SETTINGS_VERSION;
566 if ((tfd = rb->open(TV_SETTINGS_TMP_FILE, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)
567 return false;
569 if (rb->write(tfd, buf, TV_SETTINGS_HEADER_SIZE + 2) < 0)
571 rb->close(tfd);
572 return false;
575 if ((ofd = rb->open(TV_SETTINGS_FILE, O_RDONLY)) >= 0)
577 res = ((rb->read(ofd, buf, TV_SETTINGS_HEADER_SIZE + 2) >= 0) &&
578 (rb->memcmp(buf, TV_SETTINGS_HEADER, TV_SETTINGS_HEADER_SIZE - 1) == 0));
580 if (res)
582 fcount = (buf[TV_SETTINGS_HEADER_SIZE] << 8) | buf[TV_SETTINGS_HEADER_SIZE + 1];
583 for (i = 0; i < fcount; i++)
585 if (rb->read(ofd, buf, MAX_PATH + 2) < 0)
587 res = false;
588 break;
591 size = (buf[MAX_PATH] << 8) | buf[MAX_PATH + 1];
592 if (rb->strcmp(buf, preferences->file_name) == 0)
593 rb->lseek(ofd, size, SEEK_CUR);
594 else
596 if ((rb->read(ofd, buf + (MAX_PATH + 2), size) < 0) ||
597 (rb->write(tfd, buf, size + MAX_PATH + 2) < 0))
599 res = false;
600 break;
602 new_fcount++;
606 rb->close(ofd);
609 if (res)
611 /* save to current read file's preferences and bookmarks */
612 res = false;
614 if (rb->write(tfd, preferences_buf, preferences_buf_size) >= 0)
616 rb->lseek(tfd, TV_SETTINGS_HEADER_SIZE, SEEK_SET);
618 new_fcount++;
619 buf[0] = new_fcount >> 8;
620 buf[1] = new_fcount;
621 res = (rb->write(tfd, buf, 2) >= 0);
624 rb->close(tfd);
626 if (res)
628 rb->remove(TV_SETTINGS_FILE);
629 rb->rename(TV_SETTINGS_TMP_FILE, TV_SETTINGS_FILE);
631 else
632 rb->remove(TV_SETTINGS_TMP_FILE);
634 return res;