source: gsdl/trunk/trunk/mgpp/text/mgpp_fast_comp_dict.cpp@ 16583

Last change on this file since 16583 was 16583, checked in by davidb, 16 years ago

Undoing change commited in r16582

  • Property svn:keywords set to Author Date Id Revision
File size: 17.0 KB
Line 
1/**************************************************************************
2 *
3 * mgpp_fast_comp_dict.cpp -- Program to generate a fast compression dictionary
4 * Copyright (C) 1994 Neil Sharman
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 *
20 **************************************************************************/
21
22#define _XOPEN_SOURCE 1
23#define _XOPEN_SOURCE_EXTENDED 1
24
25// need this to avoid bizarre compiler problems under VC++ 6.0
26#if defined (__WIN32__) && !defined (GSDL_USE_IOS_H)
27# include <iostream>
28#endif
29
30/* getopt is in posix.2, so cygwin should have it in unistd, but doesn't */
31#if defined (__WIN32__) || defined (__CYGWIN__)
32# include "getopt_old.h"
33#else
34# include <unistd.h>
35#endif
36
37#include "sysfuncs.h"
38#include "huffman.h"
39#include "messages.h"
40#include "memlib.h"
41#include "netorder.h" /* [RPAP - Jan 97: Endian Ordering] */
42
43#include "local_strings.h"
44#include "mg.h"
45#include "text.h"
46#include "invf.h"
47#include "mg_files.h"
48#include "locallib.h"
49#include "words.h"
50
51
52
53#define ALIGN_SIZE(p,t) (((p) + (sizeof(t)-1)) & (~(sizeof(t)-1)))
54
55#define WORDNO(p, base) ((((char*)(p))-((char*)(base)))/sizeof(u_char*))
56#define FIXUP(p) (fixup[WORDNO(p,buffer)/8] |= (1<<(WORDNO(p, buffer) & 7)))
57
58#define IS_FIXUP(p) ((fixup[WORDNO(p,buffer)/8] & (1<<(WORDNO(p, buffer) & 7))) != 0)
59
60#define FIXUP_VALS(vals) do { \
61 int i; \
62 for (i=0; i < MAX_HUFFCODE_LEN+1; ++i) \
63 FIXUP(&vals[i]); \
64 } while(0)
65
66
67
68static u_long mem_for_comp_dict (char *filename);
69static void load_comp_dict (char *filename);
70static void save_fast_dict (char *filename);
71static void unfixup_buffer (void);
72
73
74static void *buffer;
75static void *cur;
76static u_char *fixup;
77static u_long mem, fixup_mem;
78
79int main (int argc, char **argv)
80{
81 int ch;
82 char *filename = "";
83 opterr = 0;
84 msg_prefix = argv[0];
85 while ((ch = getopt (argc, argv, "f:d:h")) != -1)
86 switch (ch)
87 {
88 case 'f': /* input file */
89 filename = optarg;
90 break;
91 case 'd':
92 set_basepath (optarg);
93 break;
94 case 'h':
95 case '?':
96 fprintf (stderr, "usage: %s [-f input_file] [-d data directory] [-h]\n",
97 argv[0]);
98 exit (1);
99 }
100
101 mem = mem_for_comp_dict (filename);
102
103 fixup_mem = (ALIGN_SIZE (mem, u_char *) / sizeof (u_char *) + 7) / 8;
104
105 cur = buffer = Xmalloc (mem);
106 memset (buffer, 0, mem);
107 fixup = (u_char *) Xmalloc (fixup_mem);
108 memset (fixup, 0, fixup_mem);
109
110#ifndef SILENT
111 Message ("Estimated memory for fast_dict %u", mem);
112 Message ("Estimated memory for fixups %u", fixup_mem);
113#endif
114
115 load_comp_dict (filename);
116
117#ifndef SILENT
118 Message ("Actual memory for fast_dict %u", (char *) cur - (char *) buffer);
119#endif
120
121 if ((u_long) cur > (u_long) buffer + mem)
122 FatalError (1, "The buffer was not big enough for the dictionary");
123
124 {
125 /* [RPAP - Jan 97: Endian Ordering] */
126 compression_dict *cd = (compression_dict *) buffer;
127 int i, which;
128
129 /* cfh */
130 for (which = 0; which <= 1; ++which)
131 {
132 int j;
133
134 HTONSI(cd->cfh[which]->hd.num_codes);
135 HTONSI(cd->cfh[which]->hd.mincodelen);
136 HTONSI(cd->cfh[which]->hd.maxcodelen);
137 for (j = 0; j < MAX_HUFFCODE_LEN + 1; ++j)
138 {
139 HTONSI(cd->cfh[which]->hd.lencount[j]);
140 HTONUL(cd->cfh[which]->hd.min_code[j]);
141 }
142 HTONUL(cd->cfh[which]->uncompressed_size);
143 for (j = 0; j < MAX_HUFFCODE_LEN + 1; ++j)
144 HTONUL(cd->cfh[which]->huff_words_size[j]);
145 }
146 HTONUL(cd->MemForCompDict);
147 /* ad */
148 if (cd->cdh.novel_method == MG_NOVEL_DELTA ||
149 cd->cdh.novel_method == MG_NOVEL_HYBRID)
150 for (which = 0; which <= 1; ++which)
151 {
152 int j;
153
154 HTONUL(cd->ad->afh[which].num_frags);
155 HTONUL(cd->ad->afh[which].mem_for_frags);
156 for (j = 0; j < 33; ++j)
157 {
158 HTONSI(cd->ad->blk_start[which][j]);
159 HTONSI(cd->ad->blk_end[which][j]);
160 }
161 }
162 /* cdh */
163 HTONUL(cd->cdh.dict_type);
164 HTONUL(cd->cdh.novel_method);
165 for (i = 0; i < TEXT_PARAMS; ++i)
166 HTONUL(cd->cdh.params[which]);
167 HTONUL(cd->cdh.num_words[0]);
168 HTONUL(cd->cdh.num_words[1]);
169 HTONUL(cd->cdh.num_word_chars[0]);
170 HTONUL(cd->cdh.num_word_chars[1]);
171 HTONUL(cd->cdh.lookback);
172 HTONSI(cd->fast_loaded);
173 }
174
175 unfixup_buffer ();
176
177 save_fast_dict (filename);
178
179 return (0);
180}
181
182
183
184static void
185unfixup_buffer ()
186{
187 u_long *p;
188 for (p = (u_long *) buffer; (u_long) p < (u_long) cur; ++p)
189 {
190 if (IS_FIXUP (p))
191 *p = *p - (u_long) buffer;
192 }
193}
194
195
196
197
198static u_long
199mem_for_aux_dict (compression_dict_header * /*cdh*/, char *filename)
200{
201 int i;
202 u_long mem = sizeof (auxiliary_dict);
203 FILE *aux;
204
205 aux = open_file (filename, TEXT_DICT_AUX_SUFFIX, "rb",
206 MAGIC_AUX_DICT, MG_ABORT); /* [RPAP - Feb 97: WIN32 Port] */
207
208 for (i = 0; i <= 1; ++i)
209 {
210 aux_frags_header afh;
211 fread (&afh, sizeof (afh), 1, aux);
212 NTOHUL(afh.num_frags); /* [RPAP - Jan 97: Endian Ordering] */
213 NTOHUL(afh.mem_for_frags); /* [RPAP - Jan 97: Endian Ordering] */
214 mem += afh.num_frags * sizeof (u_char *);
215 mem = ALIGN_SIZE (mem + afh.mem_for_frags, u_char *);
216 fseek (aux, afh.mem_for_frags, SEEK_CUR);
217 }
218 fclose (aux);
219
220 return mem;
221}
222
223
224
225static u_long
226mem_for_words (FILE * dict, compression_dict_header * cdh,
227 comp_frags_header * cfh)
228{
229 u_long mem = 0;
230 long i, lookback;
231 int ptrs_reqd = 0;
232 int mem_reqd = 0;
233
234 lookback = cdh->lookback;
235
236 for (i = cfh->hd.mincodelen; i <= cfh->hd.maxcodelen; ++i)
237 {
238 ptrs_reqd += (cfh->hd.lencount[i] + ((1 << lookback) - 1)) >> lookback;
239 mem_reqd += cfh->huff_words_size[i];
240 }
241
242 mem += ptrs_reqd * sizeof (u_char *);
243 mem += (MAX_HUFFCODE_LEN + 1) * sizeof (u_char **);
244 mem += mem_reqd;
245
246 for (i = 0; i < cfh->hd.num_codes; ++i)
247 {
248 register int val;
249 for (val = getc (dict) & 0xf; val; --val)
250 getc (dict);
251 }
252 return ALIGN_SIZE (mem, u_char *);
253}
254
255static u_long mem_skip_hd(FILE *dict, u_long mem)
256{ huff_data hd;
257
258 mem += sizeof (hd);
259 Read_Huffman_Data (dict, &hd, NULL, NULL);
260 if (hd.clens)
261 delete []hd.clens;
262 mem += hd.num_codes * sizeof (unsigned long);
263 mem += (MAX_HUFFCODE_LEN + 1) * sizeof (unsigned long *);
264 return mem;
265}
266
267static u_long mem_read_cfh(FILE *dict, compression_dict_header *cdh, comp_frags_header *cfh,
268 u_long mem)
269{
270 mem += sizeof (comp_frags_header);
271 Read_cfh (dict, cfh, NULL, NULL);
272
273 /* Don't need to count the space for the clens of the huffman data */
274
275 mem += mem_for_words (dict, cdh, cfh);
276 if (cfh->hd.clens)
277 delete []cfh->hd.clens;
278
279 return mem;
280}
281
282
283static u_long
284mem_for_comp_dict (char *filename)
285{
286 u_long mem = sizeof (compression_dict);
287 compression_dict_header cdh;
288 comp_frags_header cfh;
289 FILE *dict;
290 int which;
291 int i; /* [RPAP - Jan 97: Endian Ordering] */
292
293 dict = open_file (filename, TEXT_DICT_SUFFIX, "rb",
294 MAGIC_DICT, MG_ABORT); /* [RPAP - Feb 97: WIN32 Port] */
295
296 fread (&cdh, sizeof (cdh), 1, dict);
297 /* [RPAP - Jan 97: Endian Ordering] */
298 NTOHUL(cdh.dict_type);
299 NTOHUL(cdh.novel_method);
300 for (i = 0; i < TEXT_PARAMS; ++i)
301 NTOHUL(cdh.params[i]);
302 NTOHUL(cdh.num_words[0]);
303 NTOHUL(cdh.num_words[1]);
304 NTOHUL(cdh.num_word_chars[0]);
305 NTOHUL(cdh.num_word_chars[1]);
306 NTOHUL(cdh.lookback);
307
308 for (which = 0; which < 2; ++which)
309 switch (cdh.dict_type)
310 {
311 case MG_COMPLETE_DICTIONARY:
312 {
313 mem = mem_read_cfh(dict, &cdh, &cfh, mem);
314 }
315 break;
316 case MG_PARTIAL_DICTIONARY:
317 {
318 // huff_data hd;
319 if (cdh.num_words[which])
320 {
321 mem = mem_read_cfh(dict, &cdh, &cfh, mem);
322 }
323
324 mem = mem_skip_hd(dict, mem);
325 mem = mem_skip_hd(dict, mem);
326 }
327 break;
328 case MG_SEED_DICTIONARY:
329 {
330 // huff_data hd;
331 if (cdh.num_words[which])
332 {
333 mem = mem_read_cfh(dict, &cdh, &cfh, mem);
334 }
335 switch (cdh.novel_method)
336 {
337 case MG_NOVEL_HUFFMAN_CHARS:
338 mem = mem_skip_hd(dict, mem);
339 mem = mem_skip_hd(dict, mem);
340 break;
341 case MG_NOVEL_DELTA:
342 break;
343 case MG_NOVEL_HYBRID:
344 break;
345 }
346 break;
347 }
348 }
349 fclose (dict);
350
351 if (cdh.novel_method == MG_NOVEL_DELTA ||
352 cdh.novel_method == MG_NOVEL_HYBRID)
353 mem += mem_for_aux_dict (&cdh, filename);
354
355 return ALIGN_SIZE (mem, u_char *);
356}
357
358
359
360void *
361getmem (u_long size, int align)
362{
363 void *res;
364 cur = (void *) (((u_long) cur + (align - 1)) & (~(align - 1)));
365 res = cur;
366 cur = (char *) cur + size;
367 if ((u_long) cur > (u_long)buffer + mem)
368 FatalError (1, "The buffer was not big enough for the dictionary");
369 return res;
370}
371
372
373
374
375
376
377
378
379
380
381
382static auxiliary_dict *
383LoadAuxDict (compression_dict_header * cdh,
384 char *filename)
385{
386 auxiliary_dict *ad;
387 int i;
388 FILE *aux;
389
390 aux = open_file (filename, TEXT_DICT_AUX_SUFFIX, "rb",
391 MAGIC_AUX_DICT, MG_ABORT); /* [RPAP - Feb 97: WIN32 Port] */
392
393 ad = (auxiliary_dict *) getmem (sizeof (auxiliary_dict), sizeof (u_char *));
394
395 for (i = 0; i <= 1; ++i)
396 {
397 unsigned int j;
398 u_char *pos;
399
400 fread (&ad->afh[i], sizeof (aux_frags_header), 1, aux);
401
402 /* [RPAP - Jan 97: Endian Ordering] */
403 NTOHUL(ad->afh[i].num_frags);
404 NTOHUL(ad->afh[i].mem_for_frags);
405
406 ad->word_data[i] = (u_char *) getmem (ad->afh[i].mem_for_frags, 1);
407 FIXUP (&ad->word_data[i]);
408
409 ad->words[i] = (u_char **) getmem (ad->afh[i].num_frags * sizeof (u_char *),
410 sizeof (u_char *));
411 FIXUP (&ad->words[i]);
412
413 fread (ad->word_data[i], ad->afh[i].mem_for_frags, sizeof (u_char), aux);
414
415 pos = ad->word_data[i];
416 for (j = 0; j < ad->afh[i].num_frags; ++j)
417 {
418 ad->words[i][j] = pos;
419 FIXUP (&ad->words[i][j]);
420 pos += *pos + 1;
421 }
422 if (cdh->novel_method == MG_NOVEL_HYBRID)
423 {
424 int num;
425 num = 1;
426 ad->blk_start[i][0] = 0;
427 ad->blk_end[i][0] = cdh->num_words[i] - 1;
428 while (num < 33)
429 {
430 ad->blk_start[i][num] = ad->blk_end[i][num - 1] + 1;
431 ad->blk_end[i][num] = ad->blk_start[i][num] +
432 (ad->blk_end[i][num - 1] - ad->blk_start[i][num - 1]) * 2;
433 ++num;
434 }
435 }
436 }
437 fclose (aux);
438 return (ad);
439}
440
441
442
443
444
445static u_char ***
446ReadInWords (FILE * dict, compression_dict * cd,
447 comp_frags_header * cfh, u_char ** escape)
448{
449 int i, lookback;
450 int ptrs_reqd = 0;
451 int mem_reqd = 0;
452 int num_set[MAX_HUFFCODE_LEN + 1];
453 u_char *next_word[MAX_HUFFCODE_LEN + 1];
454 u_char **vals;
455 u_char ***values;
456 u_char word[MAXWORDLEN + 1];
457 u_char last_word[MAX_HUFFCODE_LEN + 1][MAXWORDLEN + 1];
458
459 lookback = cd->cdh.lookback;
460
461 for (i = cfh->hd.mincodelen; i <= cfh->hd.maxcodelen; ++i)
462 {
463 ptrs_reqd += (cfh->hd.lencount[i] + ((1 << lookback) - 1)) >> lookback;
464 mem_reqd += cfh->huff_words_size[i];
465 }
466
467 vals = (u_char **) getmem (ptrs_reqd * sizeof (*vals), sizeof (u_char *));
468
469 values = (u_char ***) getmem ((MAX_HUFFCODE_LEN + 1) * sizeof (u_char **), sizeof (u_char **));
470
471 next_word[0] = (u_char *) getmem (mem_reqd, sizeof (char));
472
473 cd->MemForCompDict += ptrs_reqd * sizeof (*vals) +
474 (MAX_HUFFCODE_LEN + 1) * sizeof (u_char **) +
475 mem_reqd;
476
477 values[0] = vals;
478 FIXUP (&values[0]);
479 values[0][0] = next_word[0];
480 FIXUP (&values[0][0]);
481 for (i = 1; i <= cfh->hd.maxcodelen; ++i)
482 {
483 int next_start = (values[i - 1] - vals) +
484 ((cfh->hd.lencount[i - 1] + ((1 << lookback) - 1)) >> lookback);
485 values[i] = &vals[next_start];
486 FIXUP (&values[i]);
487 next_word[i] = next_word[i - 1] + cfh->huff_words_size[i - 1];
488 values[i][0] = next_word[i];
489 FIXUP (&values[i][0]);
490 }
491
492 memset (num_set, '\0', sizeof (num_set));
493
494 for (i = 0; i < cfh->hd.num_codes; ++i)
495 {
496 register int val, copy;
497 register int len = cfh->hd.clens[i];
498 val = getc (dict);
499 copy = (val >> 4) & 0xf;
500 val &= 0xf;
501
502 fread (word + copy + 1, sizeof (u_char), val, dict);
503 *word = val + copy;
504
505 if ((num_set[len] & ((1 << lookback) - 1)) == 0)
506 {
507 values[len][num_set[len] >> lookback] = next_word[len];
508 FIXUP (&values[len][num_set[len] >> lookback]);
509 memcpy (next_word[len], word, *word + 1);
510 if (escape && i == cfh->hd.num_codes - 1)
511 {
512 *escape = next_word[len];
513 FIXUP (escape);
514 }
515 next_word[len] += *word + 1;
516 }
517 else
518 {
519 copy = prefixlen (last_word[len], word);
520 memcpy (next_word[len] + 1, word + copy + 1, *word - copy);
521 *next_word[len] = (copy << 4) + (*word - copy);
522 if (escape && i == cfh->hd.num_codes - 1)
523 {
524 *escape = next_word[len];
525 FIXUP (escape);
526 }
527 next_word[len] += (*word - copy) + 1;
528 }
529 memcpy (last_word[len], word, *word + 1);
530 ++num_set[len];
531 }
532 if (cfh->hd.clens)
533 delete []cfh->hd.clens;
534 cfh->hd.clens = NULL;
535 return values;
536}
537
538
539static unsigned long **
540Generate_Fast_Huffman_Vals (huff_data * data, u_long * mem)
541{
542 int i;
543 unsigned long *fcode[MAX_HUFFCODE_LEN + 1];
544 unsigned long **values;
545 unsigned long *vals;
546
547 if (!data)
548 return (NULL);
549 vals = (unsigned long *) getmem (data->num_codes * sizeof (*vals), sizeof (long *));
550 values = (unsigned long **) getmem ((MAX_HUFFCODE_LEN + 1) * sizeof (unsigned long *),
551 sizeof (long *));
552
553 memset (values, '\0', (MAX_HUFFCODE_LEN + 1) * sizeof (unsigned long *));
554
555 if (mem)
556 *mem += data->num_codes * sizeof (*vals) +
557 (MAX_HUFFCODE_LEN + 1) * sizeof (unsigned long *);
558
559 fcode[0] = values[0] = &vals[0];
560 FIXUP (&values[0]);
561 for (i = 1; i <= data->maxcodelen; ++i)
562 {
563 fcode[i] = values[i] = &vals[(values[i - 1] - vals) + data->lencount[i - 1]];
564 FIXUP (&values[i]);
565 }
566
567 for (i = 0; i < data->num_codes; ++i)
568 if (data->clens[i])
569 *fcode[(int) (data->clens[i])]++ = i;
570 return (values);
571}
572
573
574static void load_read_huffman(FILE *dict, compression_dict *cd, int which)
575{
576 huff_data *hd;
577 u_long **vals;
578
579 hd = (huff_data *) getmem (sizeof (huff_data), sizeof (char *));
580 cd->MemForCompDict += sizeof (huff_data);
581 Read_Huffman_Data (dict, hd, &cd->MemForCompDict, NULL);
582 vals = Generate_Fast_Huffman_Vals (hd, &cd->MemForCompDict);
583 cd->chars_huff[which] = hd;
584 FIXUP (&cd->chars_huff[which]);
585 cd->chars_vals[which] = vals;
586 FIXUP (&cd->chars_vals[which]);
587 if (hd->clens)
588 delete []hd->clens;
589 hd->clens = NULL;
590}
591
592static void load_read_words(FILE *dict, compression_dict *cd, int which, int fixescape)
593{
594 cd->cfh[which] = (comp_frags_header *) getmem (sizeof (*cd->cfh[which]), sizeof (u_char *));
595 cd->MemForCompDict += sizeof (*cd->cfh[which]);
596 Read_cfh (dict, cd->cfh[which], &cd->MemForCompDict, NULL);
597
598 cd->values[which] = ReadInWords (dict, cd, cd->cfh[which], NULL);
599 FIXUP (&cd->cfh[which]);
600 FIXUP (&cd->values[which]);
601 if (fixescape)
602 { FIXUP(&cd->escape[which]);
603 }
604 else
605 {
606 cd->escape[which] = NULL;
607 }
608}
609
610static void
611load_comp_dict (char *filename)
612{
613 FILE *dict;
614 int which;
615 compression_dict *cd;
616
617 cd = (compression_dict *) getmem (sizeof (compression_dict), sizeof (u_char *));
618 cd->fast_loaded = 1;
619
620 dict = open_file (filename, TEXT_DICT_SUFFIX, "rb",
621 MAGIC_DICT, MG_ABORT); /* [RPAP - Feb 97: WIN32 Port] */
622
623 Read_cdh (dict, &cd->cdh, NULL, NULL);
624
625 for (which = 0; which < 2; ++which)
626 switch (cd->cdh.dict_type)
627 {
628 case MG_COMPLETE_DICTIONARY:
629 {
630 load_read_words(dict, cd, which, 0);
631 }
632 break;
633 case MG_PARTIAL_DICTIONARY:
634 {
635 if (cd->cdh.num_words[which])
636 {
637 load_read_words(dict, cd, which, 1);
638 }
639
640 load_read_huffman(dict, cd, which);
641 load_read_huffman(dict, cd, which);
642 }
643 break;
644 case MG_SEED_DICTIONARY:
645 {
646 if (cd->cdh.num_words[which])
647 {
648 load_read_words(dict, cd, which, 1);
649 }
650 switch (cd->cdh.novel_method)
651 {
652 case MG_NOVEL_HUFFMAN_CHARS:
653 load_read_huffman(dict, cd, which);
654 load_read_huffman(dict, cd, which);
655 break;
656 case MG_NOVEL_DELTA:
657 break;
658 case MG_NOVEL_HYBRID:
659 break;
660 }
661 break;
662 }
663 }
664 fclose (dict);
665
666
667 if (cd->cdh.novel_method == MG_NOVEL_DELTA ||
668 cd->cdh.novel_method == MG_NOVEL_HYBRID)
669 {
670 cd->ad = LoadAuxDict (&cd->cdh, filename);
671 FIXUP (&cd->ad);
672 }
673}
674
675static void
676save_fast_dict (char *filename)
677{
678 FILE *fdict;
679
680 fdict = create_file (filename, TEXT_DICT_FAST_SUFFIX, "wb",
681 MAGIC_FAST_DICT, MG_ABORT); /* [RPAP - Feb 97: WIN32 Port] */
682
683 {
684 u_long *p;
685 for (p = (u_long *) buffer; (u_long) p < (u_long) cur; ++p)
686 {
687 if (IS_FIXUP (p))
688 HTONUL(*p);
689 }
690 }
691
692 /* [RPAP - Jan 97: Endian Ordering] */
693 HTONUL(mem);
694 HTONUL(fixup_mem);
695
696 fwrite (&mem, sizeof (mem), 1, fdict);
697 fwrite (&fixup_mem, sizeof (fixup_mem), 1, fdict);
698
699 /* [RPAP - Jan 97: Endian Ordering] */
700 NTOHUL(mem);
701 NTOHUL(fixup_mem);
702
703 fwrite (buffer, sizeof (u_char), mem, fdict);
704 fwrite (fixup, sizeof (u_char), fixup_mem, fdict);
705
706 fclose (fdict);
707}
Note: See TracBrowser for help on using the repository browser.