~ubuntu-branches/ubuntu/wily/gargoyle-free/wily-proposed

« back to all changes in this revision

Viewing changes to tads/tads2/mkchrtab.c

  • Committer: Bazaar Package Importer
  • Author(s): Sylvain Beucler
  • Date: 2009-09-11 20:09:43 UTC
  • Revision ID: james.westby@ubuntu.com-20090911200943-idgzoyupq6650zpn
Tags: upstream-2009-08-25
ImportĀ upstreamĀ versionĀ 2009-08-25

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#ifdef RCSID
 
2
static char RCSid[] =
 
3
"$Header: d:/cvsroot/tads/TADS2/mkchrtab.c,v 1.2 1999/05/17 02:52:14 MJRoberts Exp $";
 
4
#endif
 
5
 
 
6
/* 
 
7
 *   Copyright (c) 1998, 2002 Michael J. Roberts.  All Rights Reserved.
 
8
 *   
 
9
 *   Please see the accompanying license file, LICENSE.TXT, for information
 
10
 *   on using and copying this software.  
 
11
 */
 
12
/*
 
13
Name
 
14
  mkchrtab.c - TADS character table generator
 
15
Function
 
16
  
 
17
Notes
 
18
  
 
19
Modified
 
20
  05/31/98 MJRoberts  - Creation
 
21
*/
 
22
 
 
23
#include <stdio.h>
 
24
#include <stdlib.h>
 
25
#include <ctype.h>
 
26
#include <string.h>
 
27
 
 
28
#include "os.h"
 
29
#include "cmap.h"
 
30
 
 
31
#ifndef TRUE
 
32
#define TRUE 1
 
33
#endif
 
34
#ifndef FALSE
 
35
#define FALSE 0
 
36
#endif
 
37
 
 
38
/*
 
39
 *   Read a number.  Returns zero on success, non-zero on error.
 
40
 */
 
41
static int read_number(unsigned int *result, char **p,
 
42
                       char *infile, int linenum, int is_char)
 
43
{
 
44
    unsigned int base;
 
45
    unsigned int acc;
 
46
    int digcnt;
 
47
    
 
48
    /* skip any leading spaces */
 
49
    while (isspace(**p))
 
50
        ++(*p);
 
51
 
 
52
    /*
 
53
     *   Check for a character value 
 
54
     */
 
55
    if (**p == '\'')
 
56
    {
 
57
        unsigned char c;
 
58
        
 
59
        /* get the next character */
 
60
        ++(*p);
 
61
 
 
62
        /* if it's a backslash, escape the next character */
 
63
        if (**p == '\\')
 
64
        {
 
65
            /* skip the backslash */
 
66
            ++(*p);
 
67
 
 
68
            /* the next character gives the value */
 
69
            switch(**p)
 
70
            {
 
71
            case 'n':
 
72
                c = '\n';
 
73
                break;
 
74
 
 
75
            case 'r':
 
76
                c = '\r';
 
77
                break;
 
78
 
 
79
            case 't':
 
80
                c = '\t';
 
81
                break;
 
82
 
 
83
            case '\\':
 
84
                c = '\\';
 
85
                break;
 
86
 
 
87
            case '\'':
 
88
                c = '\'';
 
89
                break;
 
90
 
 
91
            default:
 
92
                printf("%s: line %d: invalid backslash sequence '\\%c'\n",
 
93
                       infile, linenum, **p);
 
94
                return 4;
 
95
            }
 
96
        }
 
97
        else
 
98
        {
 
99
            /* use this value as the character code */
 
100
            c = (unsigned char)**p;
 
101
        }
 
102
 
 
103
        /* skip the character, and make sure we have the closing quote */
 
104
        ++(*p);
 
105
        if (**p != '\'')
 
106
        {
 
107
            printf("%s: line %d: missing close quote\n", infile, linenum);
 
108
            return 5;
 
109
        }
 
110
 
 
111
        /* skip the close quote */
 
112
        ++(*p);
 
113
 
 
114
        /* skip trailing spaces */
 
115
        while (isspace(**p))
 
116
            ++(*p);
 
117
 
 
118
        /* return the result */
 
119
        *result = (unsigned int)c;
 
120
        return 0;
 
121
    }
 
122
 
 
123
    /* 
 
124
     *   determine the base - if there's a leading zero, it's hex or
 
125
     *   octal; otherwise, it's decimal 
 
126
     */
 
127
    if (**p == '0')
 
128
    {
 
129
        /* skip the leading zero */
 
130
        ++(*p);
 
131
        
 
132
        /* if the next character is 'x', it's hex, otherwise it's octal */
 
133
        if (**p == 'x' || **p == 'X')
 
134
        {
 
135
            /* skip the 'x' */
 
136
            ++(*p);
 
137
 
 
138
            /* it's hex */
 
139
            base = 16;
 
140
        }
 
141
        else
 
142
        {
 
143
            /* it's octal */
 
144
            base = 8;
 
145
        }
 
146
    }
 
147
    else
 
148
    {
 
149
        /* no leading zero - it's a decimal number */
 
150
        base = 10;
 
151
    }
 
152
 
 
153
    /* read digits */
 
154
    for (acc = 0, digcnt = 0 ;; ++(*p), ++digcnt)
 
155
    {
 
156
        /* see if we still have a digit */
 
157
        if (isdigit(**p) || (base == 16 && isxdigit(**p)))
 
158
        {
 
159
            unsigned int dig;
 
160
            
 
161
            /* get this digit's value */
 
162
            dig = (unsigned int)(isdigit(**p)
 
163
                                 ? **p - '0'
 
164
                                 : **p - (isupper(**p) ? 'A' : 'a') + 10);
 
165
 
 
166
            /* make sure it's in range for our radix */
 
167
            if (dig >= base)
 
168
            {
 
169
                printf("%s: line %d: invalid digit for radix\n",
 
170
                       infile, linenum);
 
171
                return 3;
 
172
            }
 
173
 
 
174
            /* accumulate this digit */
 
175
            acc *= base;
 
176
            acc += dig;
 
177
        }
 
178
        else
 
179
        {
 
180
            /* that's the end of the number */
 
181
            break;
 
182
        }
 
183
    }
 
184
 
 
185
    /* if we didn't get any valid digits, it's an error */
 
186
    if (digcnt == 0)
 
187
    {
 
188
        printf("%s: line %d: invalid number\n", infile, linenum);
 
189
        return 1;
 
190
    }
 
191
 
 
192
    /* skip any trailing spaces */
 
193
    while (isspace(**p))
 
194
        ++(*p);
 
195
 
 
196
    /* make sure the result isn't too large */
 
197
    if ((is_char && acc > (unsigned int)(unsigned char)0xff)
 
198
        || acc > (unsigned int)0xffff)
 
199
    {
 
200
        printf("%s: line %d: value out of range\n", infile, linenum);
 
201
        return 2;
 
202
    }
 
203
 
 
204
    /* return the accumulated value */
 
205
    *result = acc;
 
206
 
 
207
    /* success */
 
208
    return 0;
 
209
}
 
210
 
 
211
 
 
212
/*
 
213
 *   Check to see if an identifier matches a given string 
 
214
 */
 
215
static int id_matches(const char *idp, size_t idlen, const char *str)
 
216
{
 
217
    return (idlen == strlen(str) && memicmp(idp, str, idlen) == 0);
 
218
}
 
219
 
 
220
/*
 
221
 *   HTML Entity mapping structure 
 
222
 */
 
223
struct entity_t
 
224
{
 
225
    const char *ename;
 
226
    unsigned int charval;
 
227
};
 
228
 
 
229
/*
 
230
 *   List of HTML TADS entity names and the corresponding character codes 
 
231
 */
 
232
static const struct entity_t entities[] =
 
233
{
 
234
    { "nbsp", 160 },
 
235
    { "iexcl", 161 },
 
236
    { "cent", 162 },
 
237
    { "pound", 163 },
 
238
    { "curren", 164 },
 
239
    { "yen", 165 },
 
240
    { "brvbar", 166 },
 
241
    { "sect", 167 },
 
242
    { "uml", 168 },
 
243
    { "copy", 169 },
 
244
    { "ordf", 170 },
 
245
    { "laquo", 171 },
 
246
    { "not", 172 },
 
247
    { "shy", 173 },
 
248
    { "reg", 174 },
 
249
    { "macr", 175 },
 
250
    { "deg", 176 },
 
251
    { "plusmn", 177 },
 
252
    { "sup2", 178 },
 
253
    { "sup3", 179 },
 
254
    { "acute", 180 },
 
255
    { "micro", 181 },
 
256
    { "para", 182 },
 
257
    { "middot", 183 },
 
258
    { "cedil", 184 },
 
259
    { "sup1", 185 },
 
260
    { "ordm", 186 },
 
261
    { "raquo", 187 },
 
262
    { "frac14", 188 },
 
263
    { "frac12", 189 },
 
264
    { "frac34", 190 },
 
265
    { "iquest", 191 },
 
266
    { "times", 215 },
 
267
    { "divide", 247 },
 
268
    { "AElig", 198 },
 
269
    { "Aacute", 193 },
 
270
    { "Acirc", 194 },
 
271
    { "Agrave", 192 },
 
272
    { "Aring", 197 },
 
273
    { "Atilde", 195 },
 
274
    { "Auml", 196 },
 
275
    { "Ccedil", 199 },
 
276
    { "ETH", 208 },
 
277
    { "Eacute", 201 },
 
278
    { "Ecirc", 202 },
 
279
    { "Egrave", 200 },
 
280
    { "Euml", 203 },
 
281
    { "Iacute", 205 },
 
282
    { "Icirc", 206 },
 
283
    { "Igrave", 204 },
 
284
    { "Iuml", 207 },
 
285
    { "Ntilde", 209 },
 
286
    { "Oacute", 211 },
 
287
    { "Ocirc", 212 },
 
288
    { "Ograve", 210 },
 
289
    { "Oslash", 216 },
 
290
    { "Otilde", 213 },
 
291
    { "Ouml", 214 },
 
292
    { "THORN", 222 },
 
293
    { "Uacute", 218 },
 
294
    { "Ucirc", 219 },
 
295
    { "Ugrave", 217 },
 
296
    { "Uuml", 220 },
 
297
    { "Yacute", 221 },
 
298
    { "aacute", 225 },
 
299
    { "acirc", 226 },
 
300
    { "aelig", 230 },
 
301
    { "agrave", 224 },
 
302
    { "aring", 229 },
 
303
    { "atilde", 227 },
 
304
    { "auml", 228 },
 
305
    { "ccedil", 231 },
 
306
    { "eacute", 233 },
 
307
    { "ecirc", 234 },
 
308
    { "egrave", 232 },
 
309
    { "eth", 240 },
 
310
    { "euml", 235 },
 
311
    { "iacute", 237 },
 
312
    { "icirc", 238 },
 
313
    { "igrave", 236 },
 
314
    { "iuml", 239 },
 
315
    { "ntilde", 241 },
 
316
    { "oacute", 243 },
 
317
    { "ocirc", 244 },
 
318
    { "ograve", 242 },
 
319
    { "oslash", 248 },
 
320
    { "otilde", 245 },
 
321
    { "ouml", 246 },
 
322
    { "szlig", 223 },
 
323
    { "thorn", 254 },
 
324
    { "uacute", 250 },
 
325
    { "ucirc", 251 },
 
326
    { "ugrave", 249 },
 
327
    { "uuml", 252 },
 
328
    { "yacute", 253 },
 
329
    { "thorn", 254 },
 
330
    { "yuml", 255 },
 
331
 
 
332
    /* TADS extensions to the standard characters */
 
333
    { "lsq", 8216 },
 
334
    { "rsq", 8217 },
 
335
    { "ldq", 8220 },
 
336
    { "rdq", 8221 },
 
337
    { "endash", 8211 },
 
338
    { "emdash", 8212 },
 
339
    { "trade", 8482 },
 
340
 
 
341
    /* HTML 4.0 character extensions */
 
342
    { "ndash", 8211 },
 
343
    { "mdash", 8212 },
 
344
    { "lsquo", 8216 },
 
345
    { "rsquo", 8217 },
 
346
    { "ldquo", 8220 },
 
347
    { "rdquo", 8221 },
 
348
    { "sbquo", 8218 },
 
349
    { "bdquo", 8222 },
 
350
    { "lsaquo", 8249 },
 
351
    { "rsaquo", 8250 },
 
352
    { "dagger", 8224 },
 
353
    { "Dagger", 8225 },
 
354
    { "OElig", 338 },
 
355
    { "oelig", 339 },
 
356
    { "permil", 8240 },
 
357
    { "Yuml", 376 },
 
358
    { "scaron", 353 },
 
359
    { "Scaron", 352 },
 
360
    { "circ", 710 },
 
361
    { "tilde", 732 },
 
362
 
 
363
    /* Greek letters */
 
364
    { "Alpha", 913 },
 
365
    { "Beta", 914 },
 
366
    { "Gamma", 915 },
 
367
    { "Delta", 916 },
 
368
    { "Epsilon", 917 },
 
369
    { "Zeta", 918 },
 
370
    { "Eta", 919 },
 
371
    { "Theta", 920 },
 
372
    { "Iota", 921 },
 
373
    { "Kappa", 922 },
 
374
    { "Lambda", 923 },
 
375
    { "Mu", 924 },
 
376
    { "Nu", 925 },
 
377
    { "Xi", 926 },
 
378
    { "Omicron", 927 },
 
379
    { "Pi", 928 },
 
380
    { "Rho", 929 },
 
381
    { "Sigma", 931 },
 
382
    { "Tau", 932 },
 
383
    { "Upsilon", 933 },
 
384
    { "Phi", 934 },
 
385
    { "Chi", 935 },
 
386
    { "Psi", 936 },
 
387
    { "Omega", 937 },
 
388
    { "alpha", 945 },
 
389
    { "beta", 946 },
 
390
    { "gamma", 947 },
 
391
    { "delta", 948 },
 
392
    { "epsilon", 949 },
 
393
    { "zeta", 950 },
 
394
    { "eta", 951 },
 
395
    { "theta", 952 },
 
396
    { "iota", 953 },
 
397
    { "kappa", 954 },
 
398
    { "lambda", 955 },
 
399
    { "mu", 956 },
 
400
    { "nu", 957 },
 
401
    { "xi", 958 },
 
402
    { "omicron", 959 },
 
403
    { "pi", 960 },
 
404
    { "rho", 961 },
 
405
    { "sigmaf", 962 },
 
406
    { "sigma", 963 },
 
407
    { "tau", 964 },
 
408
    { "upsilon", 965 },
 
409
    { "phi", 966 },
 
410
    { "chi", 967 },
 
411
    { "psi", 968 },
 
412
    { "omega", 969 },
 
413
    { "thetasym", 977 },
 
414
    { "upsih", 978 },
 
415
    { "piv", 982 },
 
416
 
 
417
    /* general punctuation marks */
 
418
    { "bull", 8226 },
 
419
    { "hellip", 8230 },
 
420
    { "prime", 8242 },
 
421
    { "Prime", 8243 },
 
422
    { "oline", 8254 },
 
423
    { "frasl", 8260 },
 
424
 
 
425
    /* letter-like symbols */
 
426
    { "weierp", 8472 },
 
427
    { "image", 8465 },
 
428
    { "real", 8476 },
 
429
    { "alefsym", 8501 },
 
430
 
 
431
    /* arrows */
 
432
    { "larr", 8592 },
 
433
    { "uarr", 8593 },
 
434
    { "rarr", 8594 },
 
435
    { "darr", 8595 },
 
436
    { "harr", 8596 },
 
437
    { "crarr", 8629 },
 
438
    { "lArr", 8656 },
 
439
    { "uArr", 8657 },
 
440
    { "rArr", 8658 },
 
441
    { "dArr", 8659 },
 
442
    { "hArr", 8660 },
 
443
 
 
444
    /* mathematical operators */
 
445
    { "forall", 8704 },
 
446
    { "part", 8706 },
 
447
    { "exist", 8707 },
 
448
    { "empty", 8709 },
 
449
    { "nabla", 8711 },
 
450
    { "isin", 8712 },
 
451
    { "notin", 8713 },
 
452
    { "ni", 8715 },
 
453
    { "prod", 8719 },
 
454
    { "sum", 8721 },
 
455
    { "minus", 8722 },
 
456
    { "lowast", 8727 },
 
457
    { "radic", 8730 },
 
458
    { "prop", 8733 },
 
459
    { "infin", 8734 },
 
460
    { "ang", 8736 },
 
461
    { "and", 8743 },
 
462
    { "or", 8744 },
 
463
    { "cap", 8745 },
 
464
    { "cup", 8746 },
 
465
    { "int", 8747 },
 
466
    { "there4", 8756 },
 
467
    { "sim", 8764 },
 
468
    { "cong", 8773 },
 
469
    { "asymp", 8776 },
 
470
    { "ne", 8800 },
 
471
    { "equiv", 8801 },
 
472
    { "le", 8804 },
 
473
    { "ge", 8805 },
 
474
    { "sub", 8834 },
 
475
    { "sup", 8835 },
 
476
    { "nsub", 8836 },
 
477
    { "sube", 8838 },
 
478
    { "supe", 8839 },
 
479
    { "oplus", 8853 },
 
480
    { "otimes", 8855 },
 
481
    { "perp", 8869 },
 
482
    { "sdot", 8901 },
 
483
    { "lceil", 8968 },
 
484
    { "rceil", 8969 },
 
485
    { "lfloor", 8970 },
 
486
    { "rfloor", 8971 },
 
487
    { "lang", 9001 },
 
488
    { "rang", 9002 },
 
489
 
 
490
    /* geometric shapes */
 
491
    { "loz", 9674 },
 
492
 
 
493
    /* miscellaneous symbols */
 
494
    { "spades", 9824 },
 
495
    { "clubs", 9827 },
 
496
    { "hearts", 9829 },
 
497
    { "diams", 9830 },
 
498
 
 
499
    /* Latin-extended B */
 
500
    { "fnof", 402 },
 
501
 
 
502
    /* Latin-2 characters */
 
503
    { "Aogon", 260 },
 
504
    { "breve", 728 },
 
505
    { "Lstrok", 321 },
 
506
    { "Lcaron", 317 },
 
507
    { "Sacute", 346 },
 
508
    { "Scedil", 350 },
 
509
    { "Tcaron", 356 },
 
510
    { "Zacute", 377 },
 
511
    { "Zcaron", 381 },
 
512
    { "Zdot", 379 },
 
513
    { "aogon", 261 },
 
514
    { "ogon", 731 },
 
515
    { "lstrok", 322 },
 
516
    { "lcaron", 318 },
 
517
    { "sacute", 347 },
 
518
    { "caron", 711 },
 
519
    { "scedil", 351 },
 
520
    { "tcaron", 357 },
 
521
    { "zacute", 378 },
 
522
    { "dblac", 733 },
 
523
    { "zcaron", 382 },
 
524
    { "zdot", 380 },
 
525
    { "Racute", 340 },
 
526
    { "Abreve", 258 },
 
527
    { "Lacute", 313 },
 
528
    { "Cacute", 262 },
 
529
    { "Ccaron", 268 },
 
530
    { "Eogon", 280 },
 
531
    { "Ecaron", 282 },
 
532
    { "Dcaron", 270 },
 
533
    { "Dstrok", 272 },
 
534
    { "Nacute", 323 },
 
535
    { "Ncaron", 327 },
 
536
    { "Odblac", 336 },
 
537
    { "Rcaron", 344 },
 
538
    { "Uring", 366 },
 
539
    { "Udblac", 368 },
 
540
    { "Tcedil", 354 },
 
541
    { "racute", 341 },
 
542
    { "abreve", 259 },
 
543
    { "lacute", 314 },
 
544
    { "cacute", 263 },
 
545
    { "ccaron", 269 },
 
546
    { "eogon", 281 },
 
547
    { "ecaron", 283 },
 
548
    { "dcaron", 271 },
 
549
    { "dstrok", 273 },
 
550
    { "nacute", 324 },
 
551
    { "ncaron", 328 },
 
552
    { "odblac", 337 },
 
553
    { "rcaron", 345 },
 
554
    { "uring", 367 },
 
555
    { "udblac", 369 },
 
556
    { "tcedil", 355 },
 
557
    { "dot", 729 },
 
558
    { 0, 0 }
 
559
};
 
560
 
 
561
/*
 
562
 *   Entity mapping list entry.  We store each entity mapping we find in
 
563
 *   the file in one of these structures, and link the structures together
 
564
 *   into a list. 
 
565
 */
 
566
typedef struct entity_map_t entity_map_t;
 
567
struct entity_map_t
 
568
{
 
569
    /* next mapping entry in the list */
 
570
    entity_map_t *nxt;
 
571
 
 
572
    /* HTML entity character code (Unicode value) */
 
573
    unsigned int html_char;
 
574
 
 
575
    /* length of the expansion */
 
576
    size_t exp_len;
 
577
 
 
578
    /* local character set expansion */
 
579
    unsigned char expansion[1];
 
580
};
 
581
 
 
582
/*
 
583
 *   Parse an entity name mapping 
 
584
 */
 
585
static entity_map_t *parse_entity(char *p, char *infile, int linenum)
 
586
{
 
587
    const char *start;
 
588
    const struct entity_t *entp;
 
589
    unsigned char buf[CMAP_MAX_ENTITY_EXPANSION];
 
590
    unsigned char *dstp;
 
591
    entity_map_t *mapp;
 
592
    
 
593
    /* find the end of the entity name */
 
594
    start = p;
 
595
    for ( ; isalpha(*p) || isdigit(*p) ; ++p) ;
 
596
 
 
597
    /* scan the list for the entity name */
 
598
    for (entp = entities ; entp->ename != 0 ; ++entp)
 
599
    {
 
600
        /* see if this one is a match - note that case is significant */
 
601
        if (strlen(entp->ename) == (size_t)(p - start)
 
602
            && memcmp(entp->ename, start, p - start) == 0)
 
603
            break;
 
604
    }
 
605
 
 
606
    /* if we didn't find it, it's an error */
 
607
    if (entp->ename == 0)
 
608
    {
 
609
        printf("%s: line %d: unknown entity name \"%.*s\"\n",
 
610
               infile, linenum, p - start, start);
 
611
        return 0;
 
612
    }
 
613
 
 
614
    /* skip any intervening whitespace and check for the '=' sign */
 
615
    for ( ; isspace(*p) ; ++p) ;
 
616
    if (*p != '=')
 
617
    {
 
618
        printf("%s: line %d: expected '=' after entity name\n",
 
619
               infile, linenum);
 
620
        return 0;
 
621
    }
 
622
 
 
623
    /* skip the '=' */
 
624
    ++p;
 
625
 
 
626
    /* read the series of numbers */
 
627
    for (dstp = buf ; *p != '\0' ; )
 
628
    {
 
629
        unsigned int intval;
 
630
        unsigned char c;
 
631
 
 
632
        /* skip any leading whitespace */
 
633
        while (isspace(*p))
 
634
            ++p;
 
635
 
 
636
        /* if it's the end of the line or a comment, stop */
 
637
        if (*p == '\0' || *p == '#')
 
638
            break;
 
639
 
 
640
        /* scan the character code */
 
641
        if (read_number(&intval, &p, infile, linenum, TRUE))
 
642
            return 0;
 
643
        c = (unsigned char)intval;
 
644
 
 
645
        /* make sure we haven't overflowed our buffer */
 
646
        if (dstp >= buf + sizeof(buf))
 
647
        {
 
648
            printf("%s: line %d: entity mapping is too long (maximum of %d "
 
649
                   "characters are allowed\n",
 
650
                   infile, linenum, sizeof(buf));
 
651
            return 0;
 
652
        }
 
653
 
 
654
        /* add it to the output list */
 
655
        *dstp++ = c;
 
656
    }
 
657
 
 
658
    /* if we didn't get any characters, it's an error */
 
659
    if (dstp == buf)
 
660
    {
 
661
        printf("%s: line %d: no local character codes found after '='\n",
 
662
               infile, linenum);
 
663
        return 0;
 
664
    }
 
665
 
 
666
    /* create a new mapping structure and set it up */
 
667
    mapp = (entity_map_t *)malloc(sizeof(entity_map_t) + dstp - buf);
 
668
    mapp->nxt = 0;
 
669
    mapp->html_char = entp->charval;
 
670
    mapp->exp_len = dstp - buf;
 
671
    memcpy(mapp->expansion, buf, dstp - buf);
 
672
 
 
673
    /* return the new mapping structure */
 
674
    return mapp;
 
675
}
 
676
 
 
677
 
 
678
/*
 
679
 *   Unicode mapping table entry 
 
680
 */
 
681
typedef struct unicode_map_t unicode_map_t;
 
682
struct unicode_map_t
 
683
{
 
684
    /* corresponding Unicode code point */
 
685
    unsigned int unicode_val;
 
686
};
 
687
 
 
688
/*
 
689
 *   Generate a mapping table by associating two Unicode mapping tables 
 
690
 */
 
691
static void gen_unicode_mapping(unicode_map_t map_from[256],
 
692
                                unicode_map_t map_to[256],
 
693
                                unsigned char result_fwd[256],
 
694
                                unsigned char result_fwd_set[256],
 
695
                                unsigned char result_rev[256],
 
696
                                unsigned char result_rev_set[256])
 
697
{
 
698
    int i;
 
699
    
 
700
    /*
 
701
     *   For each item in the 'from' map, find the entry in the 'to' map
 
702
     *   with the same Unicode value.  This gives the value that we store
 
703
     *   in the result table at the 'from' character index location. 
 
704
     */
 
705
    for (i = 0 ; i < 256 ; ++i)
 
706
    {
 
707
        int j;
 
708
        
 
709
        /* find this 'from' Unicode value in the 'to' table */
 
710
        for (j = 0 ; j < 256 ; ++j)
 
711
        {
 
712
            /* if they match, map it */
 
713
            if (map_to[j].unicode_val == map_from[i].unicode_val)
 
714
            {
 
715
                /* set the forward mapping, if it's not already set */
 
716
                if (!result_fwd_set[i])
 
717
                {
 
718
                    result_fwd[i] = j;
 
719
                    result_fwd_set[i] = TRUE;
 
720
                }
 
721
 
 
722
                /* set the reverse mapping, if it's not already set */
 
723
                if (!result_rev_set[j])
 
724
                {
 
725
                    result_rev[j] = i;
 
726
                    result_rev_set[j] = TRUE;
 
727
                }
 
728
 
 
729
                /* no need to look any further */
 
730
                break;
 
731
            }
 
732
        }
 
733
    }
 
734
}
 
735
 
 
736
/*
 
737
 *   Load a Unicode mapping file 
 
738
 */
 
739
static void load_unicode_file(char *filename, unicode_map_t map[256],
 
740
                              char *infile, int linenum)
 
741
{
 
742
    osfildef *fp;
 
743
    int linenum_u;
 
744
        
 
745
    /* open the unicode file */
 
746
    fp = osfoprs(filename, OSFTTEXT);
 
747
    if (fp == 0)
 
748
    {
 
749
        printf("%s: line %d: unable to open unicode mapping file \"%s\"\n",
 
750
               infile, linenum, filename);
 
751
        return;
 
752
    }
 
753
 
 
754
    /* read it */
 
755
    for (linenum_u = 1 ;; ++linenum_u)
 
756
    {
 
757
        char buf[256];
 
758
        char *p;
 
759
        unsigned int n1;
 
760
        unsigned int n2;
 
761
 
 
762
        /* read the next line */
 
763
        if (osfgets(buf, sizeof(buf), fp) == 0)
 
764
            break;
 
765
 
 
766
        /* skip leading spaces */
 
767
        for (p = buf ; isspace(*p) ; ++p) ;
 
768
 
 
769
        /* if it's blank or starts with a comment, ignore it */
 
770
        if (*p == 0x1a || *p == '#' || *p == '\n' || *p == '\r' || *p == '\0')
 
771
            continue;
 
772
 
 
773
        /* read the first number - this is the native character value */
 
774
        if (read_number(&n1, &p, filename, linenum_u, TRUE))
 
775
            break;
 
776
 
 
777
        /* read the second number */
 
778
        if (read_number(&n2, &p, filename, linenum_u, FALSE))
 
779
            break;
 
780
 
 
781
        /* set the association */
 
782
        map[n1].unicode_val = n2;
 
783
    }
 
784
 
 
785
    /* done with the file */
 
786
    osfcls(fp);
 
787
}
 
788
 
 
789
/*
 
790
 *   Generate the HTML named entity mappings for the native character set 
 
791
 */
 
792
static void gen_unicode_html_mapping(unicode_map_t map_native[256],
 
793
                                     entity_map_t **entity_first,
 
794
                                     entity_map_t **entity_last)
 
795
{
 
796
    unsigned int i;
 
797
    
 
798
    /* go through the native characters and find the named entity for each */
 
799
    for (i = 0 ; i < 255 ; ++i)
 
800
    {
 
801
        const struct entity_t *entity;
 
802
        
 
803
        /* 
 
804
         *   scan the named entity table to find an entity with the same
 
805
         *   Unicode value as this native character's Unicode mapping 
 
806
         */
 
807
        for (entity = entities ; entity->ename != 0 ; ++entity)
 
808
        {
 
809
            /* if this one matches the Unicode value, map it */
 
810
            if (entity->charval == map_native[i].unicode_val)
 
811
            {
 
812
                entity_map_t *mapp;
 
813
 
 
814
                /* create a new mapping structure */
 
815
                mapp = (entity_map_t *)malloc(sizeof(entity_map_t) + 1);
 
816
 
 
817
                /* set it up */
 
818
                mapp->nxt = 0;
 
819
                mapp->html_char = map_native[i].unicode_val;
 
820
                mapp->exp_len = 1;
 
821
                mapp->expansion[0] = (unsigned char)i;
 
822
 
 
823
                /* 
 
824
                 *   link it at the head of the list, so that any explicit
 
825
                 *   mapping specified by the user for the same entity
 
826
                 *   already or subsequently will override it (this will
 
827
                 *   happen because the users's entries always go at the
 
828
                 *   end of the list) 
 
829
                 */
 
830
                mapp->nxt = *entity_first;
 
831
                if (*entity_last == 0)
 
832
                    *entity_last = mapp;
 
833
                *entity_first = mapp;
 
834
                
 
835
                /* no need to look any further */
 
836
                break;
 
837
            }
 
838
        }
 
839
    }
 
840
}
 
841
 
 
842
 
 
843
/*
 
844
 *   Parse Unicode mapping files 
 
845
 */
 
846
static void parse_unicode_files(char *val, size_t vallen,
 
847
                                char *infile, int linenum,
 
848
                                unsigned char input_map[256],
 
849
                                unsigned char input_map_set[256],
 
850
                                unsigned char output_map[256],
 
851
                                unsigned char output_map_set[256],
 
852
                                entity_map_t **entity_first,
 
853
                                entity_map_t **entity_last)
 
854
{
 
855
    char fn_native[OSFNMAX];
 
856
    char fn_internal[OSFNMAX];
 
857
    char *dst;
 
858
    unicode_map_t map_native[256];
 
859
    unicode_map_t map_internal[256];
 
860
    
 
861
    /* retrieve the filenames */
 
862
    fn_native[0] = '\0';
 
863
    fn_internal[0] = '\0';
 
864
    while (vallen != 0)
 
865
    {
 
866
        char *id;
 
867
        size_t idlen;
 
868
        char qu;
 
869
 
 
870
        /* if we're at a comment, we're done */
 
871
        if (*val == '#')
 
872
            break;
 
873
        
 
874
        /* find the end of the current identifier */
 
875
        for (id = val ; vallen > 0 && isalpha(*val) ; ++val, --vallen) ;
 
876
        idlen = val - id;
 
877
 
 
878
        /* see what we have */
 
879
        if (id_matches(id, idlen, "native"))
 
880
            dst = fn_native;
 
881
        else if (id_matches(id, idlen, "internal"))
 
882
            dst = fn_internal;
 
883
        else
 
884
        {
 
885
            printf("%s: line %d: expected 'internal' or 'native', "
 
886
                   "found '%.*s'\n", infile, linenum, idlen, id);
 
887
            return;
 
888
        }
 
889
 
 
890
        /* if the current name has already been matched, it's an error */
 
891
        if (*dst != '\0')
 
892
        {
 
893
            printf("%s: line %d: '%.*s' specified more than once\n",
 
894
                   infile, linenum, idlen, id);
 
895
            return;
 
896
        }
 
897
 
 
898
        /* scan ahead to the '=' */
 
899
        while (vallen > 0 && isspace(*val))
 
900
            ++val, --vallen;
 
901
 
 
902
        /* make sure we have the '=' */
 
903
        if (vallen == 0 || *val != '=')
 
904
        {
 
905
            printf("%s: line %d: expected '=' after '%.*s'\n",
 
906
                   infile, linenum, idlen, id);
 
907
            return;
 
908
        }
 
909
 
 
910
        /* skip intervening spaces */
 
911
        ++val, --vallen;
 
912
        while (vallen > 0 && isspace(*val))
 
913
            ++val, --vallen;
 
914
 
 
915
        /* make sure we have a filename */
 
916
        if (vallen == 0)
 
917
        {
 
918
            printf("%s: line %d: expected filename after '='\n",
 
919
                   infile, linenum);
 
920
            return;
 
921
        }
 
922
 
 
923
        /* check for a quote */
 
924
        if (*val == '"' || *val == '\'')
 
925
        {
 
926
            /* remember the quote */
 
927
            qu = *val;
 
928
 
 
929
            /* skip it */
 
930
            ++val, --vallen;
 
931
        }
 
932
        else
 
933
        {
 
934
            /* we have no quote */
 
935
            qu = '\0';
 
936
        }
 
937
 
 
938
        /* scan the filename */
 
939
        for ( ; vallen > 0 ; ++val, --vallen)
 
940
        {
 
941
            /* check for a quote */
 
942
            if (qu != '\0' && *val == qu)
 
943
            {
 
944
                /* skip the quote */
 
945
                ++val;
 
946
                --vallen;
 
947
                
 
948
                /* 
 
949
                 *   if it's stuttered, keep a single copy and keep going;
 
950
                 *   otherwise, we're done 
 
951
                 */
 
952
                if (vallen > 1 && *(val+1) == qu)
 
953
                {
 
954
                    /* just skip the first quote, and keep the second */
 
955
                }
 
956
                else
 
957
                {
 
958
                    /* that's the matching close quote - we're done */
 
959
                    break;
 
960
                }
 
961
            }
 
962
 
 
963
            /* copy this character */
 
964
            *dst++ = *val;
 
965
        }
 
966
 
 
967
        /* null-terminate the filename */
 
968
        *dst = '\0';
 
969
 
 
970
        /* skip trailing spaces */
 
971
        while (vallen > 0 && isspace(*val))
 
972
            ++val, --vallen;
 
973
    }
 
974
 
 
975
    /* make sure we got both filenames */
 
976
    if (fn_internal[0] == '\0' || fn_native[0] == '\0')
 
977
    {
 
978
        printf("%s: line %d: must specify both 'native' and 'internal'"
 
979
               " with 'unicode'\n", infile, linenum);
 
980
        return;
 
981
    }
 
982
 
 
983
    /* load the two files */
 
984
    load_unicode_file(fn_internal, map_internal, infile, linenum);
 
985
    load_unicode_file(fn_native, map_native, infile, linenum);
 
986
 
 
987
    /* generate the forward and reverse mappings */
 
988
    gen_unicode_mapping(map_native, map_internal, input_map, input_map_set,
 
989
                        output_map, output_map_set);
 
990
    gen_unicode_mapping(map_internal, map_native, output_map, output_map_set,
 
991
                        input_map, input_map_set);
 
992
 
 
993
    /* generate the HTML named entity mappings */
 
994
    gen_unicode_html_mapping(map_native, entity_first, entity_last);
 
995
}
 
996
 
 
997
 
 
998
/*
 
999
 *   Main entrypoint 
 
1000
 */
 
1001
int main(int argc, char **argv)
 
1002
{
 
1003
    int curarg;
 
1004
    unsigned char input_map[256];
 
1005
    unsigned char output_map[256];
 
1006
    unsigned char input_map_set[256];
 
1007
    unsigned char output_map_set[256];
 
1008
    unsigned char *p;
 
1009
    int i;
 
1010
    osfildef *fp;
 
1011
    char *infile;
 
1012
    char *outfile;
 
1013
    int linenum;
 
1014
    static char sig[] = CMAP_SIG_S100;
 
1015
    int strict_mode = FALSE;
 
1016
    char id[5];
 
1017
    char ldesc[CMAP_LDESC_MAX_LEN + 1];
 
1018
    size_t len;
 
1019
    unsigned char lenbuf[2];
 
1020
    char *sys_info;
 
1021
    entity_map_t *entity_first;
 
1022
    entity_map_t *entity_last;
 
1023
 
 
1024
    /* no parameters have been specified yet */
 
1025
    memset(id, 0, sizeof(id));
 
1026
    ldesc[0] = '\0';
 
1027
    sys_info = 0;
 
1028
 
 
1029
    /* we have no entities in our entity mapping list yet */
 
1030
    entity_first = entity_last = 0;
 
1031
 
 
1032
    /* scan options */
 
1033
    for (curarg = 1 ; curarg < argc && argv[curarg][0] == '-' ; ++curarg)
 
1034
    {
 
1035
        if (!stricmp(argv[curarg], "-strict"))
 
1036
        {
 
1037
            /* they want extra warnings */
 
1038
            strict_mode = TRUE;
 
1039
        }
 
1040
        else
 
1041
        {
 
1042
            /* consume all remaining options so we get a usage message */
 
1043
            curarg = argc;
 
1044
            break;
 
1045
        }
 
1046
    }
 
1047
 
 
1048
    /* check for required arguments */
 
1049
    if (curarg + 1 >= argc)
 
1050
    {
 
1051
        printf("usage: mkchrtab [options] <source> <dest>\n"
 
1052
               "  <source> is the input file\n"
 
1053
               "  <dest> is the output file\n"
 
1054
               "Options:\n"
 
1055
               "  -strict   warn if any codes 128-255 are unassigned\n");
 
1056
#if 0
 
1057
/* 
 
1058
 *   The information about what goes in the file made the message way too
 
1059
 *   long, so this has been removed.  Users will want to the documentation
 
1060
 *   instead of the usage message for information this detailed, so it
 
1061
 *   didn't seem useful to keep it in here.  
 
1062
 */
 
1063
        printf("\n"
 
1064
               "The source file contains one entry per line, as follows:\n"
 
1065
               "\n"
 
1066
               "Set the internal character set identifier, which can be up "
 
1067
               "to four letters long\n"
 
1068
               "(note that the mapping file MUST contain an ID entry):\n"
 
1069
               "   ID = id\n"
 
1070
               "\n");
 
1071
        printf("Set the internal character set's full display name:\n"
 
1072
               "   LDESC = full name of character set\n"
 
1073
               "\n"
 
1074
               "Set system-dependent extra information (the meaning varies "
 
1075
               "by system):\n"
 
1076
               "   EXTRA_SYSTEM_INFO = info-string\n"
 
1077
               "\n"
 
1078
               "Set the native default character:\n"
 
1079
               "   NATIVE_DEFAULT = charval\n"
 
1080
               "Set the internal default character:\n"
 
1081
               "   INTERNAL_DEFAULT = charval\n");
 
1082
        printf("Load Unicode mapping files:\n"
 
1083
               "   UNICODE NATIVE=native-mapping INTERNAL=internal-mapping\n"
 
1084
               "\n"
 
1085
               "Reversibly map a native character code to an internal code:\n"
 
1086
               "   native <-> internal\n"
 
1087
               "\n"
 
1088
               "Map a native code to an internal code, and map the internal "
 
1089
               "code back\nto a different native code:\n"
 
1090
               "   native -> internal -> native\n"
 
1091
               "\n"
 
1092
               "Map a native code to an internal code, where the internal "
 
1093
               "code is already\nmapped to a native code by a previous line:\n"
 
1094
               "   native -> internal\n"
 
1095
               "\n");
 
1096
        printf("Map an internal code to a native code, where the native "
 
1097
               "code is already\nmapped to an internal code by a previous "
 
1098
               "line:\n"
 
1099
               "   native <- internal\n"
 
1100
               "\n"
 
1101
               "Map an HTML entity name to a native code or string:\n"
 
1102
               "   &entity = internal-code [internal-code ...]\n"
 
1103
               "\n"
 
1104
               "Numbers can be specified in decimal (default), octal (by "
 
1105
               "prefixing the number\nwith a zero, as in '037'), or hex (by "
 
1106
               "prefixing the number with '0x', as in\n'0xb2').  A number "
 
1107
               "can also be entered as a character by enclosing the\n"
 
1108
               "character in single quotes.\n"
 
1109
               "\n"
 
1110
               "Blank lines and lines starting with a pound sign ('#') are "
 
1111
               "ignored.\n");
 
1112
#endif /* 0 */
 
1113
        os_term(OSEXFAIL);
 
1114
    }
 
1115
 
 
1116
    /* get the input and output filenames */
 
1117
    infile = argv[curarg];
 
1118
    outfile = argv[curarg + 1];
 
1119
 
 
1120
    /* 
 
1121
     *   initialize the tables - by default, a character code in one
 
1122
     *   character set maps to the same code in the other character set 
 
1123
     */
 
1124
    for (p = input_map, i = 0 ; i < sizeof(input_map)/sizeof(input_map[0]) ;
 
1125
         ++i, ++p)
 
1126
        *p = (unsigned char)i;
 
1127
 
 
1128
    for (p = output_map, i = 0 ;
 
1129
         i < sizeof(output_map)/sizeof(output_map[0]) ; ++i, ++p)
 
1130
        *p = (unsigned char)i;
 
1131
 
 
1132
    /* 
 
1133
     *   initialize the "set" flags all to false, since we haven't set any
 
1134
     *   of the values yet -- we'll use these flags to detect when the
 
1135
     *   user attempts to set the same value more than once, so that we
 
1136
     *   can issue a warning (multiple mappings are almost certainly in
 
1137
     *   error) 
 
1138
     */
 
1139
    for (i = 0 ; i < sizeof(input_map_set)/sizeof(input_map_set[0]) ; ++i)
 
1140
        input_map_set[i] = output_map_set[i] = FALSE;
 
1141
 
 
1142
    /* open the input file */
 
1143
    fp = osfoprs(infile, OSFTTEXT);
 
1144
    if (fp == 0)
 
1145
    {
 
1146
        printf("error: unable to open input file \"%s\"\n", infile);
 
1147
        os_term(OSEXFAIL);
 
1148
    }
 
1149
 
 
1150
    /* parse the input file */
 
1151
    for (linenum = 1 ; ; ++linenum)
 
1152
    {
 
1153
        char buf[256];
 
1154
        char *p;
 
1155
        unsigned int n1, n2, n3;
 
1156
        int set_input;
 
1157
        int set_output;
 
1158
 
 
1159
        /* presume we're going to set both values */
 
1160
        set_input = set_output = TRUE;
 
1161
 
 
1162
        /* read the next line */
 
1163
        if (osfgets(buf, sizeof(buf), fp) == 0)
 
1164
            break;
 
1165
 
 
1166
        /* scan off leading spaces */
 
1167
        for (p = buf ; isspace(*p) ; ++p) ;
 
1168
 
 
1169
        /* if this line is blank, or starts with a '#', ignore it */
 
1170
        if (*p == '\0' || *p == '\n' || *p == '\r' || *p == '#')
 
1171
            continue;
 
1172
 
 
1173
        /* check for special directives */
 
1174
        if (isalpha(*p) || *p == '_')
 
1175
        {
 
1176
            char *sp;
 
1177
            char *val;
 
1178
            size_t vallen;
 
1179
            size_t idlen;
 
1180
            
 
1181
            /* find the end of the directive name */
 
1182
            for (sp = p ; isalpha(*sp) || *sp == '_' ; ++sp) ;
 
1183
            idlen = sp - p;
 
1184
 
 
1185
            /* find the equals sign, if present */
 
1186
            for (val = sp ; isspace(*val) ; ++val) ;
 
1187
            if (*val == '=')
 
1188
            {
 
1189
                /* skip the '=' and any spaces that follow */
 
1190
                for (++val ; isspace(*val) ; ++val) ;
 
1191
 
 
1192
                /* find the end of the value */
 
1193
                for (sp = val ; *sp != '\n' && *sp != '\r' && *sp != '\0' ;
 
1194
                     ++sp) ;
 
1195
 
 
1196
                /* note its length */
 
1197
                vallen = sp - val;
 
1198
            }
 
1199
            else
 
1200
            {
 
1201
                /* there's no value */
 
1202
                val = 0;
 
1203
            }
 
1204
 
 
1205
            /* see what we have */
 
1206
            if (id_matches(p, idlen, "id"))
 
1207
            {
 
1208
                /* this directive requires a value */
 
1209
                if (val == 0)
 
1210
                    goto val_required;
 
1211
 
 
1212
                /* ID's can never be more than four characters long */
 
1213
                if (vallen > 4)
 
1214
                {
 
1215
                    printf("%s: line %d: ID too long - no more than four "
 
1216
                           "characters are allowed\n", infile, linenum);
 
1217
                }
 
1218
                else
 
1219
                {
 
1220
                    /* remember the ID */
 
1221
                    memcpy(id, val, vallen);
 
1222
                    id[vallen] = '\0';
 
1223
                }
 
1224
            }
 
1225
            else if (id_matches(p, idlen, "ldesc"))
 
1226
            {
 
1227
                /* this directive requires a value */
 
1228
                if (val == 0)
 
1229
                    goto val_required;
 
1230
 
 
1231
                /* make sure it fits */
 
1232
                if (vallen > sizeof(ldesc) - 1)
 
1233
                {
 
1234
                    printf("%s: line %d: LDESC too long - no more than %u "
 
1235
                           "characters are allowed\n", infile, linenum,
 
1236
                           sizeof(ldesc) - 1);
 
1237
                }
 
1238
                else
 
1239
                {
 
1240
                    /* remember the ldesc */
 
1241
                    memcpy(ldesc, val, vallen);
 
1242
                    ldesc[vallen] = '\0';
 
1243
                }
 
1244
            }
 
1245
            else if (id_matches(p, idlen, "extra_system_info"))
 
1246
            {
 
1247
                /* this directive requires a value */
 
1248
                if (val == 0)
 
1249
                    goto val_required;
 
1250
 
 
1251
                /* allocate space for it */
 
1252
                sys_info = (char *)malloc(vallen + 1);
 
1253
                memcpy(sys_info, val, vallen);
 
1254
                sys_info[vallen] = '\0';
 
1255
            }
 
1256
            else if (id_matches(p, idlen, "native_default"))
 
1257
            {
 
1258
                unsigned int nval;
 
1259
                int i;
 
1260
 
 
1261
                /* this directive requires a value */
 
1262
                if (val == 0)
 
1263
                    goto val_required;
 
1264
 
 
1265
                /* parse the character value */
 
1266
                if (read_number(&nval, &val, infile, linenum, TRUE))
 
1267
                    continue;
 
1268
 
 
1269
                /* apply the default */
 
1270
                for (i = 128 ; i < 256 ; ++i)
 
1271
                {
 
1272
                    /* set the default only if we haven't mapped this one */
 
1273
                    if (!output_map_set[i])
 
1274
                        output_map[i] = nval;
 
1275
                }
 
1276
            }
 
1277
            else if (id_matches(p, idlen, "internal_default"))
 
1278
            {
 
1279
                unsigned int nval;
 
1280
                int i;
 
1281
 
 
1282
                /* this directive requires a value */
 
1283
                if (val == 0)
 
1284
                    goto val_required;
 
1285
 
 
1286
                /* parse the character value */
 
1287
                if (read_number(&nval, &val, infile, linenum, TRUE))
 
1288
                    continue;
 
1289
 
 
1290
                /* apply the default */
 
1291
                for (i = 128 ; i < 256 ; ++i)
 
1292
                {
 
1293
                    /* apply the default only if we haven't set this one */
 
1294
                    if (!input_map_set[i])
 
1295
                        input_map[i] = nval;
 
1296
                }
 
1297
            }
 
1298
            else if (id_matches(p, idlen, "unicode"))
 
1299
            {
 
1300
                /* skip the 'unicode' string and any intervening spaces */
 
1301
                for (p += idlen ; isspace(*p) ; ++p) ;
 
1302
 
 
1303
                /* parse the unicode files */
 
1304
                parse_unicode_files(p, strlen(p), infile, linenum,
 
1305
                                    input_map, input_map_set,
 
1306
                                    output_map, output_map_set,
 
1307
                                    &entity_first, &entity_last);
 
1308
            }
 
1309
            else
 
1310
            {
 
1311
                /* unknown directive */
 
1312
                printf("%s: line %d: invalid directive '%.*s'\n",
 
1313
                       infile, linenum, idlen, p);
 
1314
            }
 
1315
 
 
1316
            /* done processing this line */
 
1317
            continue;
 
1318
 
 
1319
            /* come here if the directive needs a value and there isn't one */
 
1320
        val_required:
 
1321
            printf("%s: line %d: '=' required with directive '%.*s'\n",
 
1322
                   infile, linenum, idlen, p);
 
1323
            continue;
 
1324
        }
 
1325
 
 
1326
        /* check for an entity name */
 
1327
        if (*p == '&')
 
1328
        {
 
1329
            entity_map_t *mapp;
 
1330
 
 
1331
            /* skip the '&' */
 
1332
            ++p;
 
1333
 
 
1334
            /* 
 
1335
             *   parse the entity - if it succeeds, link the resulting
 
1336
             *   mapping entry into our list 
 
1337
             */
 
1338
            mapp = parse_entity(p, infile, linenum);
 
1339
            if (mapp != 0)
 
1340
            {
 
1341
                if (entity_last == 0)
 
1342
                    entity_first = mapp;
 
1343
                else
 
1344
                    entity_last->nxt = mapp;
 
1345
                entity_last = mapp;
 
1346
            }
 
1347
 
 
1348
            /* done */
 
1349
            continue;
 
1350
        }
 
1351
 
 
1352
        /* read the first number */
 
1353
        if (read_number(&n1, &p, infile, linenum, TRUE))
 
1354
            continue;
 
1355
 
 
1356
        /* determine which operator we have */
 
1357
        if (*p == '<')
 
1358
        {
 
1359
            /* make sure it's "<->" or "<-" */
 
1360
            if (*(p+1) == '-' && *(p+2) != '>')
 
1361
            {
 
1362
                /* skip the operator */
 
1363
                p += 2;
 
1364
 
 
1365
                /* 
 
1366
                 *   This is a "from" translation - it only affects the
 
1367
                 *   output mapping from the internal character set to the
 
1368
                 *   native character set.  Read the second number.  There
 
1369
                 *   is no third number, since we don't want to change the
 
1370
                 *   input mapping.
 
1371
                 */
 
1372
                if (read_number(&n2, &p, infile, linenum, TRUE))
 
1373
                    continue;
 
1374
 
 
1375
                /* 
 
1376
                 *   The forward translation is not affected; set only the
 
1377
                 *   output translation.  Note that the first number was
 
1378
                 *   the output (native) value for the internal index in
 
1379
                 *   the second number, so move the first value to n3.  
 
1380
                 */
 
1381
                n3 = n1;
 
1382
                set_input = FALSE;
 
1383
            }
 
1384
            else if (*(p+1) == '-' && *(p+2) == '>')
 
1385
            {
 
1386
                /* skip it */
 
1387
                p += 3;
 
1388
 
 
1389
                /* 
 
1390
                 *   this is a reversible translation, so we only need one
 
1391
                 *   more number - the third number is implicitly the same
 
1392
                 *   as the first 
 
1393
                 */
 
1394
                n3 = n1;
 
1395
                if (read_number(&n2, &p, infile, linenum, TRUE))
 
1396
                    continue;
 
1397
            }
 
1398
            else
 
1399
            {
 
1400
                printf("%s: line %d: invalid operator - expected <->\n",
 
1401
                       infile, linenum);
 
1402
                continue;
 
1403
            }
 
1404
        }
 
1405
        else if (*p == '-')
 
1406
        {
 
1407
            /* make sure it's "->" */
 
1408
            if (*(p+1) != '>')
 
1409
            {
 
1410
                printf("%s: line %d: invalid operator - expected ->\n",
 
1411
                       infile, linenum);
 
1412
                continue;
 
1413
            }
 
1414
 
 
1415
            /* skip it */
 
1416
            p += 2;
 
1417
 
 
1418
            /* get the next number */
 
1419
            if (read_number(&n2, &p, infile, linenum, TRUE))
 
1420
                continue;
 
1421
 
 
1422
            /* 
 
1423
             *   we may or may not have a third number - if we have
 
1424
             *   another -> operator, read the third number; if we don't,
 
1425
             *   the reverse translation is not affected by this entry 
 
1426
             */
 
1427
            if (*p == '-')
 
1428
            {
 
1429
                /* make sure it's "->" */
 
1430
                if (*(p+1) != '>')
 
1431
                {
 
1432
                    printf("%s: line %d: invalid operator - expected ->\n",
 
1433
                           infile, linenum);
 
1434
                    continue;
 
1435
                }
 
1436
 
 
1437
                /* skip it */
 
1438
                p += 2;
 
1439
 
 
1440
                /* read the third number */
 
1441
                if (read_number(&n3, &p, infile, linenum, TRUE))
 
1442
                    continue;
 
1443
            }
 
1444
            else
 
1445
            {
 
1446
                /*
 
1447
                 *   There's no third number - the reverse translation is
 
1448
                 *   not affected by this line.  
 
1449
                 */
 
1450
                set_output = FALSE;
 
1451
            }
 
1452
        }
 
1453
        else
 
1454
        {
 
1455
            printf("%s: line %d: invalid operator - expected "
 
1456
                   "-> or <-> or <-\n",
 
1457
                   infile, linenum);
 
1458
            continue;
 
1459
        }
 
1460
 
 
1461
        /* make sure we're at the end of the line, and warn if not */
 
1462
        if (*p != '\0' && *p != '\n' && *p != '\r' && *p != '#')
 
1463
            printf("%s: line %d: extra characters at end of line ignored\n",
 
1464
                   infile, linenum);
 
1465
 
 
1466
        /* set the input mapping, if necessary */
 
1467
        if (set_input)
 
1468
        {
 
1469
            /* warn the user if this value has already been set before */
 
1470
            if (input_map_set[n1])
 
1471
                printf("%s: line %d: warning - native character %u has "
 
1472
                       "already been\n    mapped to internal value %u\n",
 
1473
                       infile, linenum, n1, input_map[n1]);
 
1474
            
 
1475
            /* set it */
 
1476
            input_map[n1] = n2;
 
1477
 
 
1478
            /* note that it's been set */
 
1479
            input_map_set[n1] = TRUE;
 
1480
        }
 
1481
 
 
1482
        /* set the output mapping, if necessary */
 
1483
        if (set_output)
 
1484
        {
 
1485
            /* warn the user if this value has already been set before */
 
1486
            if (output_map_set[n2])
 
1487
                printf("%s: line %d: warning - internal character %u has "
 
1488
                       "already been\n    mapped to native value %u\n",
 
1489
                       infile, linenum, n2, input_map[n2]);
 
1490
 
 
1491
            /* set it */
 
1492
            output_map[n2] = n3;
 
1493
 
 
1494
            /* note that it's been set */
 
1495
            output_map_set[n2] = TRUE;
 
1496
        }
 
1497
    }
 
1498
 
 
1499
    /* we're done with the input file */
 
1500
    osfcls(fp);
 
1501
 
 
1502
    /*
 
1503
     *   It's an error if we didn't get an ID or LDESC 
 
1504
     */
 
1505
    if (id[0] == '\0')
 
1506
    {
 
1507
        printf("Error: No ID was specified.  An ID is required.\n");
 
1508
        os_term(OSEXFAIL);
 
1509
    }
 
1510
    else if (ldesc[0] == '\0')
 
1511
    {
 
1512
        printf("Error: No LDESC was specified.  An LDESC is required.\n");
 
1513
        os_term(OSEXFAIL);
 
1514
    }
 
1515
 
 
1516
    /* open the output file */
 
1517
    fp = osfopwb(outfile, OSFTCMAP);
 
1518
    if (fp == 0)
 
1519
    {
 
1520
        printf("error: unable to open output file \"%s\"\n", outfile);
 
1521
        os_term(OSEXFAIL);
 
1522
    }
 
1523
 
 
1524
    /* write our signature */
 
1525
    if (osfwb(fp, sig, sizeof(sig)))
 
1526
        printf("error writing signature to output file\n");
 
1527
 
 
1528
    /* write the ID and LDESC */
 
1529
    len = strlen(ldesc) + 1;
 
1530
    oswp2(lenbuf, len);
 
1531
    if (osfwb(fp, id, 4)
 
1532
        || osfwb(fp, lenbuf, 2)
 
1533
        || osfwb(fp, ldesc, len))
 
1534
        printf("error writing ID information to output file\n");
 
1535
 
 
1536
    /* write the mapping tables */
 
1537
    if (osfwb(fp, input_map, sizeof(input_map))
 
1538
        || osfwb(fp, output_map, sizeof(output_map)))
 
1539
        printf("error writing character maps to output file\n");
 
1540
 
 
1541
    /* write the extra system information if present */
 
1542
    if (sys_info != 0)
 
1543
    {
 
1544
        /* write it out, with the "SYSI" flag so we know it's there */
 
1545
        len = strlen(sys_info) + 1;
 
1546
        oswp2(lenbuf, len);
 
1547
        if (osfwb(fp, "SYSI", 4)
 
1548
            || osfwb(fp, lenbuf, 2)
 
1549
            || osfwb(fp, sys_info, len))
 
1550
            printf("error writing EXTRA_SYSTEM_INFO to output file\n");
 
1551
 
 
1552
        /* we're done with the allocated buffer now */
 
1553
        free(sys_info);
 
1554
    }
 
1555
 
 
1556
    /*
 
1557
     *   Write the entity mapping list, if we have any entities 
 
1558
     */
 
1559
    if (entity_first != 0)
 
1560
    {
 
1561
        entity_map_t *entp;
 
1562
        entity_map_t *next_entity;
 
1563
        char lenbuf[2];
 
1564
        char cvalbuf[2];
 
1565
 
 
1566
        /* write out the entity list header */
 
1567
        if (osfwb(fp, "ENTY", 4))
 
1568
            printf("error writing entity marker to output file\n");
 
1569
 
 
1570
        /* run through the list, writing out each entry */
 
1571
        for (entp = entity_first ; entp != 0 ; entp = next_entity)
 
1572
        {
 
1573
            /* write out this entity */
 
1574
            oswp2(lenbuf, entp->exp_len);
 
1575
            oswp2(cvalbuf, entp->html_char);
 
1576
            if (osfwb(fp, lenbuf, 2)
 
1577
                || osfwb(fp, cvalbuf, 2)
 
1578
                || osfwb(fp, entp->expansion, entp->exp_len))
 
1579
            {
 
1580
                printf("error writing entity mapping to output file\n");
 
1581
                break;
 
1582
            }
 
1583
 
 
1584
            /* remember the next entity before we delete this one */
 
1585
            next_entity = entp->nxt;
 
1586
 
 
1587
            /* we're done with this entity, so we can delete it now */
 
1588
            free(entp);
 
1589
        }
 
1590
 
 
1591
        /* 
 
1592
         *   write out the end marker, which is just a length marker and
 
1593
         *   character marker of zero 
 
1594
         */
 
1595
        oswp2(lenbuf, 0);
 
1596
        oswp2(cvalbuf, 0);
 
1597
        if (osfwb(fp, lenbuf, 2)
 
1598
            || osfwb(fp, cvalbuf, 2))
 
1599
            printf("error writing entity list end marker to output file\n");
 
1600
    }
 
1601
 
 
1602
    /* write the end-of-file marker */
 
1603
    if (osfwb(fp, "$EOF", 4))
 
1604
        printf("error writing end-of-file marker to output file\n");
 
1605
 
 
1606
    /* done with the output file */
 
1607
    osfcls(fp);
 
1608
 
 
1609
    /* if we're in strict mode, check for unassigned mappings */
 
1610
    if (strict_mode)
 
1611
    {
 
1612
        int in_cnt, out_cnt;
 
1613
        int cnt;
 
1614
 
 
1615
        /* count unassigned characters */
 
1616
        for (i = 128, in_cnt = out_cnt = 0 ; i < 256 ; ++i)
 
1617
        {
 
1618
            if (!input_map_set[i])
 
1619
                ++in_cnt;
 
1620
            if (!output_map_set[i])
 
1621
                ++out_cnt;
 
1622
        }
 
1623
 
 
1624
        /* if we have any unassigned native characters, list them */
 
1625
        if (in_cnt != 0)
 
1626
        {
 
1627
            printf("\nUnassigned native -> internal mappings:\n    ");
 
1628
            for (i = 128, cnt = 0 ; i < 256 ; ++i)
 
1629
            {
 
1630
                if (!input_map_set[i])
 
1631
                {
 
1632
                    /* go to a new line if necessary */
 
1633
                    if (cnt >= 16)
 
1634
                    {
 
1635
                        printf("\n    ");
 
1636
                        cnt = 0;
 
1637
                    }
 
1638
 
 
1639
                    /* display this item */
 
1640
                    printf("%3d ", i);
 
1641
                    ++cnt;
 
1642
                }
 
1643
            }
 
1644
            printf("\n");
 
1645
        }
 
1646
 
 
1647
        /* list unassigned internal characters */
 
1648
        if (out_cnt != 0)
 
1649
        {
 
1650
            printf("\nUnassigned internal -> native mappings:\n    ");
 
1651
            for (i = 128, cnt = 0 ; i < 256 ; ++i)
 
1652
            {
 
1653
                if (!output_map_set[i])
 
1654
                {
 
1655
                    /* go to a new line if necessary */
 
1656
                    if (cnt >= 16)
 
1657
                    {
 
1658
                        printf("\n    ");
 
1659
                        cnt = 0;
 
1660
                    }
 
1661
 
 
1662
                    /* display this item */
 
1663
                    printf("%3d ", i);
 
1664
                    ++cnt;
 
1665
                }
 
1666
            }
 
1667
            printf("\n");
 
1668
        }
 
1669
    }
 
1670
 
 
1671
    /* success */
 
1672
    os_term(OSEXSUCC);
 
1673
    return OSEXSUCC;
 
1674
}
 
1675