1 //----------------------------------------------------------------------------
2 // EDGE Data Definition File Code (Language handling settings)
3 //----------------------------------------------------------------------------
4 //
5 // Copyright (c) 1999-2008 The EDGE Team.
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License
9 // as published by the Free Software Foundation; either version 2
10 // of the License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License for more details.
16 //
17 //----------------------------------------------------------------------------
18 //
19 // Language handling Setup and Parser Code
20 //
21 // 1998/10/29 -KM- Allow cmd line selection of language.
22 //
23 // This is somewhat different to most DDF reading files. In order to read the
24 // language specific strings, it uses the format:
25 //
26 // <RefName>=<String>;
27 //
28 // as opposed to the normal entry, which should be:
29 //
30 // [<Refname>]
31 // STRING=<string>;
32 //
33 // also the file suffix is LDF (Language Def File), this is to avoid confusion with
34 // the oridnary DDF files. The default file is DEFAULT.LDF, which can be subbed by
35 // using -lang <NameOfLangFile>.
36 //
37
38 #include "local.h"
39
40 #include "language.h"
41
42 // ---> ddf buildinfo for language class
43 class ddf_bi_lang_c
44 {
45 public:
ddf_bi_lang_c()46 ddf_bi_lang_c()
47 {
48 treehead = NULL;
49 }
50
~ddf_bi_lang_c()51 ~ddf_bi_lang_c()
52 {
53 DeleteLangNodes();
54 }
55
56 struct langentry_s
57 {
langentry_sddf_bi_lang_c::langentry_s58 langentry_s()
59 {
60 // Binary tree for sorting
61 left = NULL;
62 right = NULL;
63
64 // Next entry with matching ref
65 lang_next = NULL;
66
67 // Linked list
68 prev = NULL;
69 next = NULL;
70 }
71
72 int lang;
73
74 epi::strent_c ref;
75 epi::strent_c value;
76
77 // A linked list + binary tree is wasteful, but speed efficent and
78 // avoids stack overflow issues that may occur
79 struct langentry_s* left;
80 struct langentry_s* right;
81
82 struct langentry_s* prev;
83 struct langentry_s* next;
84
85 // Next entry
86 struct langentry_s* lang_next;
87 };
88
89 private:
90 langentry_s *treehead;
91
92 public:
93 epi::strlist_c langnames;
94 int currlang;
95
96 epi::strlist_c comp_langrefs; // Compiled language references
97 epi::strlist_c comp_langvalues; // Compiled language values (1 per lang)
98
99 // LANGUAGE ENTRY NODE HANDLING
100
AddLangNode(const char * _ref,const char * value)101 void AddLangNode(const char *_ref, const char *value)
102 {
103 langentry_s *currnode, *node, *newnode;
104
105 int cmp;
106 enum { CREATE_HEAD, TO_LEFT, TO_RIGHT, ADD, REPLACE } act;
107
108 act = CREATE_HEAD;
109 currnode = NULL;
110 node = treehead;
111
112 while (node && act != REPLACE)
113 {
114 currnode = node;
115
116 if (act != ADD)
117 cmp = stricmp(_ref, node->ref);
118 else
119 cmp = 0; // Has to be a match if the last act was ADD
120
121 if (cmp == 0)
122 {
123 //
124 // Check to see if the language id matches, if it does then
125 // replace the entry, else add the node.
126 //
127 if (node->lang == currlang)
128 {
129 act = REPLACE;
130 }
131 else
132 {
133 act = ADD;
134 node = node->lang_next;
135 }
136 }
137 else if (cmp < 0)
138 {
139 act = TO_LEFT;
140 node = node->left;
141 }
142 else if (cmp > 0)
143 {
144 act = TO_RIGHT;
145 node = node->right;
146 }
147 }
148
149 // Handle replace seperately, since we not creating anything
150 if (act == REPLACE)
151 {
152 node->value.Set(value);
153 return;
154 }
155
156 newnode = new langentry_s;
157
158 if (act != ADD) { newnode->ref.Set(_ref); }
159
160 newnode->value.Set(value);
161 newnode->lang = currlang;
162
163 switch (act)
164 {
165 case CREATE_HEAD:
166 {
167 treehead = newnode;
168 break;
169 }
170
171 case TO_LEFT:
172 {
173 // Update the binary tree
174 currnode->left = newnode;
175
176 // Update the linked list (Insert behind current node)
177 if (currnode->prev)
178 {
179 currnode->prev->next = newnode;
180 newnode->prev = currnode->prev;
181 }
182
183 currnode->prev = newnode;
184 newnode->next = currnode;
185 break;
186 }
187
188 case TO_RIGHT:
189 {
190 // Update the binary tree
191 currnode->right = newnode;
192
193 // Update the linked list (Insert infront of current node)
194 if (currnode->next)
195 {
196 currnode->next->prev = newnode;
197 newnode->next = currnode->next;
198 }
199
200 currnode->next = newnode;
201 newnode->prev = currnode;
202 break;
203 }
204
205 case ADD:
206 {
207 currnode->lang_next = newnode;
208 break;
209 }
210
211 default:
212 {
213 break; /* FIXME!! Throw error */
214 }
215 }
216 }
217
DeleteLangNodes()218 void DeleteLangNodes()
219 {
220 if (!treehead)
221 return;
222
223 // The node head is the head for the binary tree, so
224 // we have to find the head by going backwards from
225 // the treehead
226
227 langentry_s *currnode, *savenode;
228
229 currnode = treehead;
230 while (currnode->prev)
231 currnode = currnode->prev;
232
233 while (currnode)
234 {
235 savenode = currnode->next;
236 delete currnode;
237 currnode = savenode;
238 }
239 }
240
241 // LANGUAGES
242
243 //
244 // AddLanguage()
245 //
246 // Returns if the language already exists
247 //
AddLanguage(const char * name)248 bool AddLanguage(const char *name)
249 {
250 int langsel = -1;
251
252 // Look for an existing language entry if one exists
253 {
254 epi::array_iterator_c it;
255
256 for (it = langnames.GetBaseIterator(); it.IsValid(); it++)
257 {
258 char *it2 = ITERATOR_TO_TYPE(it, char*);
259
260 if (stricmp(it2, name) == 0)
261 {
262 langsel = it.GetPos();
263 break;
264 }
265 }
266 }
267
268 // Setup the current language index, adding the new entry if needs be
269 if (langsel < 0)
270 {
271 langnames.Insert(name);
272
273 currlang = langnames.GetSize() - 1;
274 }
275 else
276 {
277 currlang = langsel;
278 }
279
280
281 return (langsel < 0);
282 }
283
284 // LIST COMPILING
285
CompileLanguageReferences()286 void CompileLanguageReferences()
287 {
288 comp_langrefs.Clear();
289
290 if (!treehead)
291 return; // Nothing to do
292
293 langentry_s *currnode, *head;
294 int count;
295
296 currnode = treehead;
297 while (currnode->prev)
298 currnode = currnode->prev;
299
300 head = currnode;
301
302 // Count the entries so strlist_c does not over-fragment memory
303 count = 0;
304 while (currnode)
305 {
306 currnode = currnode->next;
307 count++;
308 }
309
310 comp_langrefs.Size(count);
311
312 // Add entries
313 for (currnode = head; currnode; currnode = currnode->next)
314 comp_langrefs.Insert(currnode->ref);
315
316 return;
317 }
318
CompileLanguageValues(int lang)319 void CompileLanguageValues(int lang)
320 {
321 SYS_ASSERT(lang >=0 && lang <= langnames.GetSize());
322
323 comp_langvalues.Clear();
324
325 if (!treehead)
326 return; // Nothing to do
327
328 langentry_s *currnode, *head, *langnode;
329 int count;
330
331 // Find me head, son!
332 currnode = treehead;
333 while (currnode->prev)
334 currnode = currnode->prev;
335
336 head = currnode;
337
338 // Count the entries so strlist_c does not over-fragment memory
339 count = 0;
340 while (currnode)
341 {
342 currnode = currnode->next;
343 count++;
344 }
345
346 comp_langvalues.Size(count);
347
348 // Add entries
349 for (currnode = head; currnode; currnode = currnode->next)
350 {
351 langnode = currnode;
352 while (langnode && langnode->lang != lang)
353 langnode = langnode->lang_next;
354
355 if (langnode)
356 comp_langvalues.Insert(langnode->value);
357 else
358 comp_langvalues.Insert(NULL);
359 }
360
361 return;
362 }
363 };
364
365 // Globals
366 language_c language; // -ACB- 2004/07/28 Languages instance
367
368 // Locals
369 ddf_bi_lang_c* lang_buildinfo;
370
371 //
372 // DDF PARSING ROUTINES
373 //
LanguageStartEntry(const char * name,bool extend)374 static void LanguageStartEntry(const char *name, bool extend)
375 {
376 if (!name || !name[0])
377 {
378 DDF_WarnError("New language entry is missing a name!");
379 name = "DEAD_LANGUAGE";
380 }
381
382 // Note: extension is the norm for LANGUAGES.LDF
383
384 lang_buildinfo->AddLanguage(name);
385 }
386
387
LanguageParseField(const char * field,const char * contents,int index,bool is_last)388 static void LanguageParseField(const char *field, const char *contents,
389 int index, bool is_last)
390 {
391 #if (DEBUG_DDF)
392 I_Debugf("LANGUAGE_PARSE: %s = %s;\n", field, contents);
393 #endif
394
395 if (! is_last)
396 {
397 DDF_WarnError("Unexpected comma `,' in LANGUAGE.LDF\n");
398 return;
399 }
400
401 lang_buildinfo->AddLangNode(field, contents);
402 }
403
LanguageFinishEntry(void)404 static void LanguageFinishEntry(void)
405 {
406 /* ... */
407 }
408
LanguageClearAll(void)409 static void LanguageClearAll(void)
410 {
411 // safe to delete all language entries
412 language.Clear();
413 }
414
415
DDF_ReadLangs(void * data,int size)416 bool DDF_ReadLangs(void *data, int size)
417 {
418 readinfo_t languages;
419
420 languages.memfile = (char*)data;
421 languages.memsize = size;
422 languages.tag = "LANGUAGES";
423 languages.entries_per_dot = 1;
424
425 if (languages.memfile)
426 {
427 languages.message = NULL;
428 languages.filename = NULL;
429 languages.lumpname = "DDFLANG";
430 }
431 else
432 {
433 languages.message = "DDF_InitLanguage";
434 languages.filename = "language.ldf";
435 languages.lumpname = NULL;
436 }
437
438 languages.start_entry = LanguageStartEntry;
439 languages.parse_field = LanguageParseField;
440 languages.finish_entry = LanguageFinishEntry;
441 languages.clear_all = LanguageClearAll;
442
443 return DDF_MainReadFile(&languages);
444 }
445
DDF_LanguageInit(void)446 void DDF_LanguageInit(void)
447 {
448 // FIXME! Copy any existing entries into the new buildinfo
449
450 // Clear any existing
451 language.Clear();
452
453 // Create a build info object
454 lang_buildinfo = new ddf_bi_lang_c;
455 }
456
DDF_LanguageCleanUp(void)457 void DDF_LanguageCleanUp(void)
458 {
459 // Convert build info into the language structure
460 int langcount = lang_buildinfo->langnames.GetSize();
461 if (langcount == 0)
462 I_Error("Missing languages !\n");
463
464 // Load the choice of languages
465 language.LoadLanguageChoices(lang_buildinfo->langnames);
466
467 // Load the reference table
468 lang_buildinfo->CompileLanguageReferences();
469 language.LoadLanguageReferences(lang_buildinfo->comp_langrefs);
470
471 // Load the value table for each one of the languages
472 int i;
473 for (i=0; i<langcount; i++)
474 {
475 lang_buildinfo->CompileLanguageValues(i);
476 language.LoadLanguageValues(i, lang_buildinfo->comp_langvalues);
477 }
478
479 //language.Dump();
480 //exit(1);
481
482 // Dispose of the data
483 delete lang_buildinfo;
484 }
485
486 //
487 // language_c Constructor
488 //
language_c()489 language_c::language_c()
490 {
491 current = -1;
492 }
493
494 //
495 // language_c Destructor
496 //
~language_c()497 language_c::~language_c()
498 {
499 Clear();
500 }
501
502 //
503 // language_c::Clear()
504 //
505 // Clear the contents
506 //
Clear()507 void language_c::Clear()
508 {
509 choices.Clear();
510 refs.Clear();
511
512 if (values)
513 {
514 delete [] values;
515 values = NULL;
516 }
517
518 current = -1;
519 }
520
521 //
522 // int language_c::Find(const char* ref)
523 //
Find(const char * name)524 int language_c::Find(const char *name)
525 {
526 if (!values || !name)
527 return -1;
528
529 // TODO Optimise search - this list is sorted in order
530
531 for (int i = refs.GetSize()-1; i >= 0; i--)
532 if (stricmp(refs[i], name) == 0)
533 return i;
534
535 return -1; // not found
536 }
537
538 //
539 // const char* language_c::GetName()
540 //
541 // Returns the current name if idx is isvalid, can be
542 // NULL if the choices table has not been setup
543 //
GetName(int idx)544 const char* language_c::GetName(int idx)
545 {
546 if (idx < 0)
547 idx = current;
548
549 if (idx < 0 || idx >= choices.GetSize())
550 return NULL;
551
552 return choices[idx];
553 }
554
555 //
556 // bool language_c::IsValidRef()
557 //
IsValidRef(const char * refname)558 bool language_c::IsValidRef(const char *refname)
559 {
560 return (Find(refname)>=0);
561 }
562
563 //
564 // language_c::LoadLanguageChoices()
565 //
LoadLanguageChoices(epi::strlist_c & _langnames)566 void language_c::LoadLanguageChoices(epi::strlist_c& _langnames)
567 {
568 choices.Set(_langnames);
569 }
570
571 //
572 // language_c::LoadLanguageReferences()
573 //
LoadLanguageReferences(epi::strlist_c & _refs)574 void language_c::LoadLanguageReferences(epi::strlist_c& _refs)
575 {
576 if (values)
577 delete [] values;
578
579 refs.Set(_refs);
580 values = new epi::strbox_c[refs.GetSize()];
581
582 return;
583 }
584
585 //
586 // language_c::LoadLanguageValues()
587 //
LoadLanguageValues(int lang,epi::strlist_c & _values)588 void language_c::LoadLanguageValues(int lang, epi::strlist_c& _values)
589 {
590 if (_values.GetSize() != refs.GetSize())
591 return; // FIXME!! Throw error
592
593 if (lang < 0 || lang >= choices.GetSize())
594 return; // FIXME!! Throw error
595
596 values[lang].Set(_values);
597
598 return;
599 }
600
601 //
602 // language_c::Select() Named Select
603 //
Select(const char * name)604 bool language_c::Select(const char *name)
605 {
606 int i, max;
607
608 for(i=0, max=choices.GetSize(); i<max; i++)
609 {
610 if (! DDF_CompareName(name, choices[i]))
611 {
612 current = i;
613 return true;
614 }
615 }
616
617 // FIXME!! Throw error
618 return false;
619 }
620
621 //
622 // language_c::Select() Index Select
623 //
Select(int idx)624 bool language_c::Select(int idx)
625 {
626 if (idx < 0 || idx >= choices.GetSize())
627 return false; // FIXME!! Throw error
628
629 current = idx;
630 return true;
631 }
632
633 //
634 // const char* language_c::operator[]()
635 //
operator [](const char * refname)636 const char* language_c::operator[](const char *refname)
637 {
638 int idx;
639
640 idx = Find(refname);
641 if (idx>=0)
642 {
643 if (current < 0 || current >= choices.GetSize())
644 return refname; // -AJA- preserve old behaviour
645
646 char *s = values[current][idx];
647 if (s != NULL)
648 return s;
649
650 // Look through other language definitions is one does not exist
651 int i, max;
652 for (i=0, max=choices.GetSize(); i<max; i++)
653 {
654 if (i != current)
655 {
656 char *s = values[i][idx];
657 if (s != NULL)
658 return values[i][idx];
659 }
660 }
661 }
662
663 return refname; // -AJA- preserve old behaviour
664 }
665
666 /*
667 void language_c::Dump(void)
668 {
669 int i,j;
670 for (i=0; i<choices.GetSize(); i++)
671 {
672 I_Printf("Language %d is %s\n", i, choices[i]);
673 }
674 I_Printf("\n");
675
676 for (i=0; i<refs.GetSize(); i++)
677 {
678 I_Printf("Ref %d is %s\n", i, refs[i]);
679 }
680
681 I_Printf("\n");
682 for (i=0; i<choices.GetSize(); i++)
683 {
684 for (j=0; j<values[i].GetSize(); j++)
685 I_Printf("Value %d/%d (%s) is %s\n", i, j, refs[j], values[i][j]);
686
687 I_Printf("\n");
688 }
689 }
690 */
691
692 //--- editor settings ---
693 // vi:ts=4:sw=4:noexpandtab
694