/[public]/psiconv/trunk/lib/psiconv/parse_common.c
ViewVC logotype

Contents of /psiconv/trunk/lib/psiconv/parse_common.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 63 - (show annotations)
Wed Dec 13 16:30:21 2000 UTC (23 years, 3 months ago) by frodo
File MIME type: text/plain
File size: 22398 byte(s)
(Frodo) Updated all copyright notices

1 /*
2 parse_common.c - Part of psiconv, a PSION 5 file formats converter
3 Copyright (c) 1999, 2000 Frodo Looijaard <frodol@dds.nl>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU 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, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 #include "config.h"
21 #include "compat.h"
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include "data.h"
26 #include "parse_routines.h"
27
28 static int psiconv_parse_layout_section(const psiconv_buffer buf,
29 int lev,psiconv_u32 off,
30 int *length,
31 psiconv_text_and_layout result,
32 psiconv_word_styles_section styles,
33 int with_styles);
34
35 int psiconv_parse_header_section(const psiconv_buffer buf,int lev,
36 psiconv_u32 off, int *length,
37 psiconv_header_section *result)
38 {
39 int res=0;
40 int len=0;
41 psiconv_u32 temp;
42
43 psiconv_progress(lev+1,off+len,"Going to read the header section");
44 (*result) = malloc(sizeof(**result));
45
46 psiconv_progress(lev+2,off+len,"Going to read UID1 to UID3");
47 (*result)->uid1 = psiconv_read_u32(buf,lev+2,off+len);
48 psiconv_debug(lev+2,off+len,"UID1: %08x",(*result)->uid1);
49 if ((*result)->uid1 == PSICONV_ID_CLIPART) {
50 /* That's all folks... */
51 (*result)->file = psiconv_clipart_file;
52 (*result)->uid2 = 0;
53 (*result)->uid3 = 0;
54 (*result)->checksum = 0;
55 len += 4;
56 psiconv_debug(lev+2,off+len,"File is a Clipart file");
57 goto DONE;
58 }
59 if ((*result)->uid1 != PSICONV_ID_PSION5) {
60 psiconv_warn(lev+2,off+len,"UID1 has unknown value. This is probably "
61 "not a (parsable) Psion 5 file");
62 res = -1;
63 }
64 len += 4;
65 (*result)->uid2 = psiconv_read_u32(buf,lev+2,off+len);
66 psiconv_debug(lev+2,off+len,"UID2: %08x",(*result)->uid2);
67 len += 4;
68 (*result)->uid3 = psiconv_read_u32(buf,lev+2,off+len);
69 psiconv_debug(lev+2,off+len,"UID3: %08x",(*result)->uid3);
70 len += 4;
71
72 (*result)->file = psiconv_unknown_file;
73 if ((*result)->uid1 == PSICONV_ID_PSION5) {
74 if ((*result)->uid2 == PSICONV_ID_DATA_FILE) {
75 if ((*result)->uid3 == PSICONV_ID_WORD) {
76 (*result)->file = psiconv_word_file;
77 psiconv_debug(lev+2,off+len,"File is a Word file");
78 } else if ((*result)->uid3 == PSICONV_ID_TEXTED) {
79 (*result)->file = psiconv_texted_file;
80 psiconv_debug(lev+2,off+len,"File is a TextEd file");
81 } else if ((*result)->uid3 == PSICONV_ID_SKETCH) {
82 (*result)->file = psiconv_sketch_file;
83 psiconv_debug(lev+2,off+len,"File is a Sketch file");
84 }
85 } else if ((*result)->uid2 == PSICONV_ID_MBM_FILE) {
86 (*result)->file = psiconv_mbm_file;
87 if ((*result)->uid3 != 0x00)
88 psiconv_warn(lev+2,off+len,"UID3 set in MBM file?!?");
89 psiconv_debug(lev+2,off+len,"File is a MBM file");
90 }
91 }
92 if ((*result)->file == psiconv_unknown_file) {
93 psiconv_warn(lev+2,off+len,"Unknown file type");
94 (*result)->file = psiconv_unknown_file;
95 }
96
97 psiconv_progress(lev+2,off+len,"Checking UID4");
98 temp = psiconv_read_u32(buf,lev+2,off+len);
99 if (temp == psiconv_checkuid((*result)->uid1,(*result)->uid2,
100 (*result)->uid3))
101 psiconv_debug(lev+2,off+len,"Checksum %08x is correct",temp);
102 else {
103 psiconv_warn(lev+2,off+len,"Checksum failed, file corrupted!");
104 psiconv_debug(lev+2,off+len,"Expected checksum %08x, found %08x",
105 psiconv_checkuid((*result)->uid1,(*result)->uid2,
106 (*result)->uid3),temp);
107 res = -1;
108 }
109 len += 4;
110
111 DONE:
112 if (length)
113 *length = len;
114
115 psiconv_progress(lev+1,off+len-1,
116 "End of Header Section (total length: %08x)",len);
117
118 return res;
119 }
120
121 int psiconv_parse_section_table_section(const psiconv_buffer buf, int lev,
122 psiconv_u32 off, int *length,
123 psiconv_section_table_section *result)
124 {
125 int res=0;
126 int len=0;
127 psiconv_section_table_entry entry;
128
129 int i;
130 psiconv_u8 nr;
131
132 psiconv_progress(lev+1,off+len,"Going to read the section table section");
133 *result = psiconv_list_new(sizeof(*entry));
134
135 psiconv_progress(lev+2,off+len,"Going to read the section table length");
136 nr = psiconv_read_u8(buf,lev+2,off+len);
137 psiconv_debug(lev+2,off+len,"Length: %08x",nr);
138 if (nr & 0x01) {
139 psiconv_warn(lev+2,off+len,
140 "Section table length odd - ignoring last entry");
141 res = -1;
142 }
143 len ++;
144
145 psiconv_progress(lev+2,off+len,"Going to read the section table entries");
146 entry = malloc(sizeof(*entry));
147 for (i = 0; i < nr / 2; i++) {
148 entry->id = psiconv_read_u32(buf,lev+2,off + len);
149 psiconv_debug(lev+2,off + len,"Entry %d: ID = %08x",i,entry->id);
150 len += 0x04;
151 entry->offset = psiconv_read_u32(buf,lev+2,off + len);
152 psiconv_debug(lev+2,off +len,"Entry %d: Offset = %08x",i,entry->offset);
153 len += 0x04;
154 psiconv_list_add(*result,entry);
155 }
156
157 free(entry);
158
159 if (length)
160 *length = len;
161
162 psiconv_progress(lev+1,off+len-1,"End of section table section "
163 "(total length: %08x)", len);
164
165 return res;
166 }
167
168 int psiconv_parse_application_id_section(const psiconv_buffer buf, int lev,
169 psiconv_u32 off, int *length,
170 psiconv_application_id_section *result)
171 {
172 int res=0;
173 int len=0;
174 int leng;
175
176 psiconv_progress(lev+1,off,"Going to read the application id section");
177 (*result) = malloc(sizeof(**result));
178
179 psiconv_progress(lev+2,off+len,"Going to read the type identifier");
180 (*result)->id = psiconv_read_u32(buf,lev+2,off+len);
181 psiconv_debug(lev+2,off+len,"Identifier: %08x",(*result)->id);
182 len += 4;
183
184 psiconv_progress(lev+2,off+len,"Going to read the application id string");
185 (*result)->name = psiconv_read_string(buf,lev+2,off+len,&leng);
186 len += leng;
187
188 if (length)
189 *length = len;
190
191 psiconv_progress(lev+1,off+len-1,"End of application id section "
192 "(total length: %08x", len);
193
194 return res;
195 }
196
197 int psiconv_parse_text_section(const psiconv_buffer buf,int lev,psiconv_u32 off,
198 int *length,psiconv_text_and_layout *result)
199 {
200
201 int res = 0;
202 int len=0;
203
204 psiconv_u32 text_len;
205 psiconv_paragraph para;
206
207 int nr;
208 int i,j,start,leng;
209 char *str_copy;
210
211 psiconv_progress(lev+1,off,"Going to parse the text section");
212 psiconv_progress(lev+2,off,"Reading the text length");
213 text_len = psiconv_read_X(buf,lev+2,off,&leng);
214 psiconv_debug(lev+2,off,"Length: %08x",text_len);
215 len += leng;
216
217 *result = psiconv_list_new(sizeof(*para));
218 para = malloc(sizeof(*para));
219
220 psiconv_progress(lev+2,off+len,"Going to read all paragraph text");
221 nr = 0;
222 start = 0;
223 for (i = 0; i < text_len; i++)
224 if (psiconv_read_u8(buf,lev+2,off+len+i) == 0x06) {
225 para->text = malloc(i - start + 1);
226 for (j = 0; j < i - start; j++)
227 para->text[j] = psiconv_read_u8(buf,lev+1,off + len + start + j);
228 para->text[j] = 0;
229
230 psiconv_list_add(*result,para);
231
232 str_copy = psiconv_make_printable(para->text);
233 psiconv_debug(lev+2,off+i+len,"Line %d: %d characters",nr,
234 strlen(str_copy) +1);
235 psiconv_debug(lev+2,off+i+len,"Line %d: `%s'",nr,str_copy);
236 free(str_copy);
237
238 start = i + 1;
239 nr ++;
240 }
241
242 if (start != text_len) {
243 res = -1;
244 psiconv_warn(lev+2,off+start+len,
245 "Last line does not end on EOL (%d characters left)", len - start);
246 para->text = malloc(text_len - start + 1);
247 for (j = 0; j < text_len - start; j++)
248 para->text[j] = psiconv_read_u8(buf,lev+2,off + start + j + len);
249 para->text[text_len - start] = 0;
250 psiconv_list_add(*result,para);
251 str_copy = psiconv_make_printable(para->text);
252 psiconv_debug(lev+2,off+start+len,"Last line: %d characters",nr,
253 strlen(str_copy)+1);
254 psiconv_debug(lev+2,off+start+len,"Last line: `%s'",str_copy);
255 free(str_copy);
256 }
257
258 free(para);
259
260 /* Initialize the remaining parts of each paragraph */
261 for (i = 0; i < psiconv_list_length(*result); i ++) {
262 para = psiconv_list_get(*result,i);
263 para->in_lines = psiconv_list_new(sizeof(struct psiconv_in_line_layout_s));
264 para->replacements = psiconv_list_new(sizeof(struct psiconv_replacement_s));
265 para->base_style = 0;
266 para->base_character = psiconv_basic_character_layout();
267 para->base_paragraph = psiconv_basic_paragraph_layout();
268 }
269
270
271 len += text_len;
272
273 if (length)
274 *length = len;
275
276 psiconv_progress(lev+1,off+len-1,"End of text section (total length: %08x",
277 len);
278
279 return res;
280 }
281
282 /* First do a parse_text_section, or you will get into trouble here */
283 int psiconv_parse_layout_section(const psiconv_buffer buf,
284 int lev,psiconv_u32 off,
285 int *length,
286 psiconv_text_and_layout result,
287 psiconv_word_styles_section styles,
288 int with_styles)
289 {
290 int res = 0;
291 int len = 0;
292 psiconv_u32 temp;
293 int parse_styles,nr,i,j,total,leng,line_length;
294
295 typedef struct anon_style_s
296 {
297 int nr;
298 psiconv_s16 base_style;
299 psiconv_character_layout character;
300 psiconv_paragraph_layout paragraph;
301 } *anon_style;
302
303 typedef psiconv_list anon_style_list; /* of struct anon_style */
304
305 anon_style_list anon_styles;
306 struct anon_style_s anon;
307 anon_style anon_ptr=NULL;
308
309 psiconv_paragraph para;
310 struct psiconv_in_line_layout_s in_line;
311
312 int *inline_count;
313
314
315 psiconv_progress(lev+1,off,"Going to read the layout section");
316
317 psiconv_progress(lev+2,off,"Going to read the section type");
318 temp = psiconv_read_u16(buf,lev+2,off+len);
319 psiconv_debug(lev+2,off+len,"Type: %02x",temp);
320 parse_styles = with_styles;
321 if ((temp == 0x0001) && !with_styles) {
322 psiconv_warn(lev+2,off+len,"Styleless layout section expected, "
323 "but styled section found!");
324 parse_styles = 1;
325 res = -1;
326 } else if ((temp == 0x0000) && (with_styles)) {
327 psiconv_warn(lev+2,off+len,"Styled layout section expected, "
328 "but styleless section found!");
329 parse_styles = 0;
330 res = -1;
331 } else if ((temp != 0x0000) && (temp != 0x0001)) {
332 psiconv_warn(lev+2,off+len,
333 "Layout section type indicator has unknown value!");
334 res = -1;
335 }
336 len += 0x02;
337
338 psiconv_progress(lev+2,off+len,"Going to read paragraph type list");
339 anon_styles = psiconv_list_new(sizeof(anon));
340 psiconv_progress(lev+3,off+len,"Going to read paragraph type list length");
341 nr = psiconv_read_u8(buf,lev+3,off+len);
342 psiconv_debug(lev+3,off+len,"Length: %02x",nr);
343 len ++;
344
345 psiconv_progress(lev+3,off+len,
346 "Going to read the paragraph type list elements");
347 for (i = 0; i < nr; i ++) {
348 psiconv_progress(lev+3,off+len,"Element %d",i);
349 anon.nr = psiconv_read_u32(buf,lev+4,off+len);
350 psiconv_debug(lev+4,off+len,"Number: %08x",anon.nr);
351 len += 0x04;
352
353 psiconv_progress(lev+4,off,"Going to determine the base style");
354 if (parse_styles) {
355 anon.base_style = psiconv_read_u8(buf,lev+3,
356 off+len+4+psiconv_read_u32(buf,lev+4,
357 off+len));
358 psiconv_debug(lev+4,off+len+psiconv_read_u32(buf,lev+4,off+len),
359 "Style indicator: %02x",anon.base_style);
360 } else
361 anon.base_style = 0;
362 anon.paragraph = psiconv_clone_paragraph_layout(psiconv_get_style
363 (styles,anon.base_style)->paragraph);
364 anon.character = psiconv_clone_character_layout(psiconv_get_style
365 (styles,anon.base_style)->character);
366
367 psiconv_progress(lev+4,off+len,"Going to read the paragraph layout");
368 res |= psiconv_parse_paragraph_layout_list(buf,lev+4,off+len,&leng,
369 anon.paragraph);
370 len += leng;
371 if (parse_styles)
372 len ++;
373
374 psiconv_progress(lev+4,off+len,"Going to read the character layout");
375 res |= psiconv_parse_character_layout_list(buf,lev+4,off+len,&leng,
376 anon.character);
377 len += leng;
378 psiconv_list_add(anon_styles,&anon);
379 }
380
381 psiconv_progress(lev+2,off+len,"Going to parse the paragraph element list");
382 psiconv_progress(lev+3,off+len,"Going to read the number of paragraphs");
383 nr = psiconv_read_u32(buf,lev+3,off+len);
384 if (nr != psiconv_list_length(result)) {
385 psiconv_warn(lev+3,off+len,
386 "Number of text paragraphs and paragraph elements does not match");
387 psiconv_debug(lev+3,off+len,
388 "%d text paragraphs, %d paragraph elements",
389 psiconv_list_length(result),nr);
390 }
391 psiconv_debug(lev+3,off+len,"Number of paragraphs: %d",nr);
392 len += 4;
393 inline_count = malloc(nr * sizeof(*inline_count));
394
395 psiconv_progress(lev+3,off+len,"Going to read the paragraph elements");
396 for (i = 0; i < nr; i ++) {
397 psiconv_progress(lev+3,off+len,"Element %d",i);
398 if (i >= psiconv_list_length(result)) {
399 psiconv_debug(lev+4,off+len,"Going to allocate a new element");
400 para = malloc(sizeof(*para));
401 para->in_lines = psiconv_list_new(sizeof(struct psiconv_in_line_layout_s));
402 para->base_style = 0;
403 para->base_character = psiconv_basic_character_layout();
404 para->base_paragraph = psiconv_basic_paragraph_layout();
405 free(para);
406 }
407 para = psiconv_list_get(result,i);
408
409 psiconv_progress(lev+4,off+len,"Going to read the paragraph length");
410 temp = psiconv_read_u32(buf,lev+4,off+len);
411 if (temp != strlen(para->text)+1) {
412 res = -1;
413 psiconv_warn(lev+4,off+len,
414 "Disagreement of the length of paragraph in layout section");
415 psiconv_debug(lev+4,off+len,
416 "Paragraph length: layout section says %d, counted %d",
417 temp,strlen(para->text)+1);
418 } else
419 psiconv_debug(lev+4,off+len,"Paragraph length: %d",temp);
420 len += 4;
421
422 psiconv_progress(lev+4,off+len,"Going to read the paragraph type");
423 temp = psiconv_read_u8(buf,lev+4,off+len);
424 if (temp != 0x00) {
425 psiconv_debug(lev+4,off+len,"Type: %02x",temp);
426 for (j = 0; j < psiconv_list_length(anon_styles); j++) {
427 anon_ptr = psiconv_list_get(anon_styles,j);
428 if (temp == anon_ptr->nr)
429 break;
430 }
431 if (j == psiconv_list_length(anon_styles)) {
432 psiconv_warn(lev+4,off+len,"Layout section paragraph type unknown");
433 psiconv_debug(lev+4,off+len,"Unknown type - using base styles instead");
434 para->base_style = 0;
435 psiconv_free_paragraph_layout(para->base_paragraph);
436 psiconv_free_character_layout(para->base_character);
437 para->base_paragraph = psiconv_clone_paragraph_layout(psiconv_get_style
438 (styles,0)->paragraph);
439 para->base_character = psiconv_clone_character_layout(psiconv_get_style
440 (styles,0)->character);
441 } else {
442 para->base_style = anon_ptr->base_style;
443 psiconv_free_paragraph_layout(para->base_paragraph);
444 psiconv_free_character_layout(para->base_character);
445 para->base_paragraph = psiconv_clone_paragraph_layout
446 (anon_ptr->paragraph);
447 para->base_character = psiconv_clone_character_layout
448 (anon_ptr->character);
449 }
450 inline_count[i] = 0;
451 len += 0x01;
452 } else {
453 psiconv_debug(lev+4,off+len,"Type: %02x (not based on a paragraph type)"
454 ,temp);
455 len += 0x01;
456 if (parse_styles) {
457 psiconv_progress(lev+4,off+len+psiconv_read_u32(buf,lev+4,off+len)+4,
458 "Going to read the paragraph element base style");
459 temp = psiconv_read_u8(buf,lev+4,
460 off+len+psiconv_read_u32(buf,lev+4,off+len)+4);
461 psiconv_debug(lev+4,off+len+psiconv_read_u32(buf,lev+4,off+len)+4,
462 "Style: %02x",temp);
463 } else
464 temp = 0x00;
465 psiconv_free_paragraph_layout(para->base_paragraph);
466 psiconv_free_character_layout(para->base_character);
467 para->base_paragraph = psiconv_clone_paragraph_layout(psiconv_get_style
468 (styles,temp)->paragraph);
469 para->base_character = psiconv_clone_character_layout(psiconv_get_style
470 (styles,temp)->character);
471 para->base_style = temp;
472 psiconv_progress(lev+4,off+len,"Going to read paragraph layout");
473 psiconv_parse_paragraph_layout_list(buf,lev+4,off+len,&leng,
474 para->base_paragraph);
475 len += leng;
476 if (parse_styles)
477 len += 1;
478 psiconv_progress(lev+4,off+len,"Going to read number of in-line "
479 "layout elements");
480 inline_count[i] = psiconv_read_u32(buf,lev+4,off+len);
481 psiconv_debug(lev+4,off+len,"Nr: %08x",inline_count[i]);
482 len += 4;
483 }
484 }
485
486 psiconv_progress(lev+2,off+len,"Going to read the text layout inline list");
487
488 psiconv_progress(lev+3,off+len,"Going to read the number of elements");
489 nr = psiconv_read_u32(buf,lev+3,off+len);
490 psiconv_debug(lev+3,off,"Elements: %08x",nr);
491 len += 0x04;
492
493 psiconv_progress(lev+3,off+len,
494 "Going to read the text layout inline elements");
495 total = 0;
496 for (i = 0; i < psiconv_list_length(result); i++) {
497 para = psiconv_list_get(result,i);
498 line_length = -1;
499 for (j = 0; j < inline_count[i]; j++) {
500 psiconv_progress(lev+3,off+len,"Element %d: Paragraph %d, element %d",
501 total,i,j);
502 if (total >= nr) {
503 psiconv_warn(lev+3,off+len,
504 "Layout section inlines: not enough element");
505 res = -1;
506 psiconv_debug(lev+3,off+len,"Can't read element!");
507 } else {
508 total ++;
509 in_line.layout = psiconv_clone_character_layout(para->base_character);
510 psiconv_progress(lev+4,off+len,"Going to read the element type");
511 temp = psiconv_read_u8(buf,lev+4,len+off);
512 len += 1;
513 psiconv_debug(lev+4,off,"Type: %02x",temp);
514 psiconv_progress(lev+4,off,
515 "Going to read the number of characters it applies to");
516 in_line.length = psiconv_read_u32(buf,lev+4,len+off);
517 psiconv_debug(lev+4,off+len,"Length: %02x",in_line.length);
518 len += 4;
519 psiconv_progress(lev+4,off+len,"Going to read the character layout");
520 res |= psiconv_parse_character_layout_list(buf,lev+4,off+len,&leng,
521 in_line.layout);
522 len += leng;
523
524 if (temp == 0x01) {
525 psiconv_debug(lev+4,off+len,"Skipping object data");
526 len += 0x10;
527 } else if (temp != 0x00) {
528 psiconv_warn(lev+4,off+len,"Layout section unknown inline type");
529 res = -1;
530 }
531 if (line_length + in_line.length > strlen(para->text)) {
532 psiconv_warn(lev+4,off+len,
533 "Layout section inlines: line length mismatch");
534 res = -1;
535 in_line.length = strlen(para->text) - line_length;
536 }
537 line_length += in_line.length;
538 psiconv_list_add(para->in_lines,&in_line);
539 }
540 }
541 }
542
543 if (total != nr) {
544 psiconv_warn(lev+4,off+len,
545 "Layout section too many inlines, skipping remaining");
546 }
547
548 free(inline_count);
549
550 for (i = 0 ; i < psiconv_list_length(anon_styles); i ++) {
551 anon_ptr = psiconv_list_get(anon_styles,i);
552 psiconv_free_character_layout(anon_ptr->character);
553 psiconv_free_paragraph_layout(anon_ptr->paragraph);
554 }
555 psiconv_list_free(anon_styles);
556
557 if (length)
558 *length = len;
559
560 psiconv_progress(lev+1,off+len-1,"End of layout section (total length: %08x",
561 len);
562
563 return res;
564 }
565
566 int psiconv_parse_styled_layout_section(const psiconv_buffer buf,
567 int lev,psiconv_u32 off,
568 int *length,
569 psiconv_text_and_layout result,
570 psiconv_word_styles_section styles)
571 {
572 return psiconv_parse_layout_section(buf,lev,off,length,result,styles,1);
573 }
574
575 int psiconv_parse_styleless_layout_section(const psiconv_buffer buf,
576 int lev,psiconv_u32 off,
577 int *length,
578 psiconv_text_and_layout result,
579 psiconv_character_layout base_char,
580 psiconv_paragraph_layout base_para)
581 {
582 int res;
583 psiconv_word_styles_section styles_section;
584
585 styles_section = malloc(sizeof(*styles_section));
586 styles_section->normal = malloc(sizeof(*styles_section->normal));
587 styles_section->normal->character = psiconv_clone_character_layout(base_char);
588 styles_section->normal->paragraph = psiconv_clone_paragraph_layout(base_para);
589 styles_section->normal->hotkey = 0;
590 styles_section->normal->name = strdup("");
591 styles_section->styles = psiconv_list_new(sizeof(struct psiconv_word_style_s));
592
593 res = psiconv_parse_layout_section(buf,lev,off,length,result,
594 styles_section,0);
595
596 psiconv_free_word_styles_section(styles_section);
597 return res;
598 }
599

frodo@frodo.looijaard.name
ViewVC Help
Powered by ViewVC 1.1.26