3
"$Header: d:/cvsroot/tads/tads3/mkchrtab.cpp,v 1.3 1999/07/11 00:46:58 MJRoberts Exp $";
7
* Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved.
9
* Please see the accompanying license file, LICENSE.TXT, for information
10
* on using and copying this software.
14
mkchrtab.cpp - TADS character table generator
20
10/17/98 MJRoberts - creation (from TADS 2 mkchrtab.c)
31
* Read a number. Returns zero on success, non-zero on error.
33
static int read_number(unsigned short *result, char **p,
34
char *infile, int linenum, int optional)
40
/* skip any leading spaces */
45
* if the entry is optional, and we've reached a comment or the end
46
* of the line, return failure, but don't print an error (since the
47
* number is optional, it's not an error if it's not present)
50
(**p == '\0' || **p == '\n' || **p == '\r' || **p == '#'))
54
* Check for a character value
60
/* get the next character */
63
/* if it's a backslash, escape the next character */
66
/* skip the backslash */
69
/* the next character gives the value */
93
printf("%s: line %d: invalid backslash sequence '\\%c'\n",
94
infile, linenum, **p);
100
/* use this value as the character code */
101
c = (unsigned char)**p;
104
/* skip the character, and make sure we have the closing quote */
108
printf("%s: line %d: missing close quote\n", infile, linenum);
112
/* skip the close quote */
115
/* skip trailing spaces */
119
/* return the result */
120
*result = (unsigned short)c;
125
* determine the base - if there's a leading zero, it's hex or
126
* octal; otherwise, it's decimal
130
/* skip the leading zero */
133
/* if the next character is 'x', it's hex, otherwise it's octal */
134
if (**p == 'x' || **p == 'X')
150
/* no leading zero - it's a decimal number */
155
for (acc = 0, digcnt = 0 ;; ++(*p), ++digcnt)
157
/* see if we still have a digit */
158
if (isdigit(**p) || (base == 16 && isxdigit(**p)))
162
/* get this digit's value */
163
dig = (unsigned short)(isdigit(**p)
165
: **p - (isupper(**p) ? 'A' : 'a') + 10);
167
/* make sure it's in range for our radix */
170
printf("%s: line %d: invalid digit for radix\n",
175
/* accumulate this digit */
181
/* that's the end of the number */
186
/* if we didn't get any valid digits, it's an error */
189
printf("%s: line %d: invalid number\n", infile, linenum);
193
/* skip any trailing spaces */
197
/* return the accumulated value */
206
* HTML Entity mapping structure
215
* List of HTML TADS entity names and the corresponding Unicode
218
static const struct entity_t entities[] =
318
/* TADS extensions to the standard characters */
327
/* HTML 4.0 character extensions */
403
/* general punctuation marks */
411
/* letter-like symbols */
430
/* mathematical operators */
476
/* geometric shapes */
479
/* miscellaneous symbols */
485
/* Latin-extended B */
488
/* Latin-2 characters */
549
* Entity mapping list entry. We store each entity mapping we find in
550
* the file in one of these structures, and link the structures together
556
* Flag: this mapping is a display expansion only (not a round-trip
557
* mapping). If this is set, then we contain a display expansion;
558
* otherwise, we contain a round-trip mapping.
562
/* length in bytes of the local mapping for this character */
567
/* if disp_only is false, this gives the local character value */
568
unsigned short local_char;
570
/* if disp_only is true, this gives the display mapping */
573
/* length of the display expansion */
576
/* display expansion, in unicode characters */
577
unsigned short expansion[1];
583
* Mapping pages. Each page maps 256 unicode characters; the page is
584
* selected by the high 8 bits of the unicode code point. We don't
585
* allocate a page until we set a character on the page for the first
588
* Each page contains 256 slots for entity_map_t pointers, so each page
589
* is an array of (entity_map_t *) elements.
591
static entity_map_t **G_mapping[256];
594
* Construct a unicode character given a page number and index on the
597
static inline unsigned int make_unicode(int pagenum, int idx)
599
return ((unsigned int)pagenum * 256) + (unsigned int)idx;
603
* Get the mapping for a particular unicode character value
605
static entity_map_t *get_mapping(unsigned int unicode_char)
607
unsigned int pagenum;
610
pagenum = ((unicode_char >> 8) & 0xff);
612
/* if there's no page allocated here, there's no mapping */
613
if (G_mapping[pagenum] == 0)
616
/* return the entry at the correct location in this page */
617
return G_mapping[pagenum][unicode_char & 0xff];
621
* Set the mapping at a given unicode code point. If there is no page
622
* allocated at the appropriate slot yet, we'll allocate a page here.
624
static void set_mapping(unsigned int unicode_char, entity_map_t *mapping)
626
unsigned int pagenum;
628
/* get the page number - it's the high 8 bits of the unicode value */
629
pagenum = ((unicode_char >> 8) & 0xff);
631
/* if the page is not allocated, allocate it now */
632
if (G_mapping[pagenum] == 0)
634
/* allocate the page and clear it out */
636
(entity_map_t **)t3malloc(256 * sizeof(entity_map_t *));
637
memset(G_mapping[pagenum], 0, 256 * sizeof(entity_map_t *));
640
/* set the mapping */
641
G_mapping[pagenum][unicode_char & 0xff] = mapping;
646
* Read a translation string. This reads a unicode-to-unicode translation
647
* that gives the expansion of a unicode character to a string of unicode
648
* characters for display. The characters of the expansion can be
649
* specified with ASCII character enclosed in single quotes, or as a series
650
* of numbers giving Unicode code points.
652
static entity_map_t *read_translation(char *p, char *infile, int linenum)
654
unsigned short buf[256];
655
unsigned short *dstp;
658
/* skip any intervening whitespace */
659
for ( ; isspace(*p) ; ++p) ;
661
/* read the series of numbers or characters */
662
for (dstp = buf ; *p != '\0' ; )
664
unsigned short intval;
666
/* skip any leading whitespace */
670
/* if it's the end of the line or a comment, stop */
671
if (*p == '\0' || *p == '#')
674
/* if it's a string, read it */
677
/* scan until the closing quote */
678
for (++p ; *p != '\'' && *p != '\0' ; ++p)
680
/* if it's an escape, skip it and read the next char */
684
/* if we don't have room, abort */
685
if (dstp >= buf + sizeof(buf))
687
printf("%s: line %d: entity mapping is too long "
688
"(maximum of %d characters are allowed\n",
689
infile, linenum, sizeof(buf));
694
* store this character - since it's given as an ASCII
695
* character, it has the same representation in Unicode as
701
/* if we didn't find a closing quote, it's an error */
704
printf("%s: line %d: no closing quote found in string\n",
709
/* skip the closing quote */
714
/* scan the character code */
715
if (read_number(&intval, &p, infile, linenum, FALSE))
718
/* make sure we haven't overflowed our buffer */
719
if (dstp + 1 > buf + sizeof(buf))
721
printf("%s: line %d: entity mapping is too long "
722
"(maximum of %d characters are allowed\n",
723
infile, linenum, sizeof(buf)/sizeof(buf[0]));
727
/* add it to the output list */
732
/* if we didn't get any characters, it's an error */
735
printf("%s: line %d: no local character codes found after '='\n",
740
/* create a new mapping structure and set it up */
741
mapp = (entity_map_t *)t3malloc(sizeof(entity_map_t)
742
+ (dstp - buf)*sizeof(buf[0]));
743
mapp->mapping.disp.exp_len = dstp - buf;
744
memcpy(mapp->mapping.disp.expansion, buf, (dstp - buf)*sizeof(buf[0]));
746
/* return the mapping */
751
* Parse an entity name mapping
753
static entity_map_t *parse_entity(char *p, char *infile, int linenum,
754
unsigned short *unicode_char)
757
const struct entity_t *entp;
759
/* find the end of the entity name */
761
for ( ; isalpha(*p) || isdigit(*p) ; ++p) ;
763
/* scan the list for the entity name */
764
for (entp = entities ; entp->ename != 0 ; ++entp)
766
/* see if this one is a match - note that case is significant */
767
if (strlen(entp->ename) == (size_t)(p - start)
768
&& memcmp(entp->ename, start, p - start) == 0)
772
/* if we didn't find it, it's an error */
773
if (entp->ename == 0)
775
printf("%s: line %d: unknown entity name \"%.*s\"\n",
776
infile, linenum, p - start, start);
780
/* tell the caller what the unicode character is */
781
*unicode_char = entp->charval;
783
/* read and return the translation */
784
return read_translation(p, infile, linenum);
788
* Store a local character mapping in a buffer, using the variable-length
789
* notation. If the character value is in the range 0-255, we'll store one
790
* byte; otherwise, we'll store two bytes, high-order 8 bits first.
791
* Returns the number of bytes stored.
793
* If the output buffer is null, we'll simply return the storage length
794
* without actually storing anything.
796
size_t local_to_buf(unsigned char *dst, unsigned short c)
798
/* if it's in the 0-255 range, it takes one byte, otherwise two */
801
/* store it as a single byte */
803
dst[0] = (unsigned char)c;
805
/* we stored one byte */
810
/* store it as two bytes, high-order byte first */
813
dst[0] = (unsigned char)(c >> 8);
814
dst[1] = (unsigned char)(c & 0xFF);
817
/* we stored two bytes */
826
int main(int argc, char **argv)
833
int display_mappings;
843
unsigned short default_char;
844
int default_char_set;
849
/* no fatal errors yet */
852
/* no mappings yet */
854
default_char_set = 0;
858
/* start out in the round-trip unicode<->local section */
859
display_mappings = FALSE;
863
* there currently are no options, so there's no option scanning
864
* needed; just start at the first argument
870
for (curarg = 1 ; curarg < argc && argv[curarg][0] == '-' ; ++curarg)
874
/* consume all remaining options so we get a usage message */
881
/* check for required arguments */
882
if (curarg + 1 >= argc)
884
printf("usage: mkchrtab [options] <source> <dest>\n"
885
" <source> is the input file\n"
886
" <dest> is the output file\n"
891
/* get the input and output filenames */
892
infile = argv[curarg];
893
outfile = argv[curarg + 1];
895
/* open the input file */
896
fp = osfoprs(infile, OSFTTEXT);
899
printf("error: unable to open input file \"%s\"\n", infile);
903
/* parse the input file */
904
for (linenum = 1 ; ; ++linenum)
908
unsigned short unicode_char;
911
/* read the next line */
912
if (osfgets(buf, sizeof(buf), fp) == 0)
915
/* scan off leading spaces */
916
for (p = buf ; *p != '\0' && (isspace(*p) || iscntrl(*p)) ; ++p) ;
918
/* if this line is blank, or starts with a '#', ignore it */
919
if (*p == '\0' || *p == '\n' || *p == '\r' || *p == '#')
922
/* check for directives */
925
&& memicmp(p, "display_mappings", 16) == 0
926
&& (!isalpha(p[16]) && !isdigit(p[16])))
928
/* note that we're in display mapping mode */
929
display_mappings = TRUE;
931
/* no need to look any further at this line */
935
&& memicmp(p, "default_display", 15) == 0
940
/* read the local character value for the default mapping */
942
if (!read_number(&default_char, &nump, infile, linenum, FALSE))
944
/* if we already have a default character, warn about it */
945
if (default_char_set)
946
printf("%s: line %d: duplicate default_display\n",
949
/* note that we have the default character value */
950
default_char_set = TRUE;
953
/* no need to look any further at this line */
958
if (display_mappings)
961
* We're doing display mappings: each mapping specifies a
962
* sequence of one or more unicode characters to substitute for
963
* a single unicode character on display. These translations
964
* can use SGML entity names for the unicode characters.
967
/* check for an entity name */
973
/* parse the entity */
974
mapp = parse_entity(p, infile, linenum, &unicode_char);
978
/* read the Unicode character number */
979
if (read_number(&unicode_char, &p, infile, linenum, FALSE))
982
/* read the translation */
983
mapp = read_translation(p, infile, linenum);
987
* If the entry is already set, flag an error and delete the
988
* entry - if it's set as a round-trip entry, we don't want to
989
* set it in the display-only section
991
if (get_mapping(unicode_char) != 0)
994
printf("%s: line %d: mapping previously defined - "
995
"new mapping ignored\n", infile, linenum);
997
/* delete the mapping and forget it */
1002
/* if we created a mapping, mark it as display-only */
1005
/* mark it as display-only */
1006
mapp->disp_only = TRUE;
1009
* if this is an expansion into multiple unicode characters
1010
* for display, count it among the expansions
1012
if (mapp->mapping.disp.exp_len > 1)
1015
disp_exp_len += mapp->mapping.disp.exp_len;
1021
unsigned short local_char;
1022
unsigned short extra_char;
1025
* Doing regular translations. The format must be two
1026
* columns of numbers; the first number is the local
1027
* character code, and the second is the unicode character
1030
* Some mapping files contain variations on this format:
1032
* - Some files use three columns, where the first column is
1033
* another character set translation (for example, JIS0208
1034
* uses the first column to give the Shift-JIS code for each
1035
* character). In these cases, we'll skip the first column,
1036
* and use only the last two columns.
1038
* - Some files for multi-byte (i.e., mixed single- and
1039
* double-byte characters) have entries for DBCS leading
1040
* bytes with no Unicode character specified. In these
1041
* cases, only one column appears on some lines, with DBCS
1042
* lead byte value in the local character set, and no
1043
* Unicode value at all. We'll simply ignore these lines
1044
* entirely, since they contribute no information that we
1049
* read two numbers - the second one is optional, since
1050
* we'll skip any single-column entries as explained above
1052
if (read_number(&local_char, &p, infile, linenum, FALSE)
1053
|| read_number(&unicode_char, &p, infile, linenum, TRUE))
1057
* check for a third column - if it's present, drop the
1058
* first column and keep only the second and third columns
1060
if (!read_number(&extra_char, &p, infile, linenum, TRUE))
1062
/* shift everything over one column */
1063
local_char = unicode_char;
1064
unicode_char = extra_char;
1067
/* if the mapping is already in use, don't overwrite it */
1068
if (get_mapping(unicode_char) != 0)
1070
/* flag the error */
1071
printf("%s: line %d: mapping previously defined - "
1072
"new mapping ignored\n", infile, linenum);
1074
/* skip this mapping */
1078
/* create a reversible mapping entry */
1079
mapp = (entity_map_t *)t3malloc(sizeof(entity_map_t));
1080
mapp->disp_only = FALSE;
1081
mapp->mapping.local_char = local_char;
1083
/* count the round-trip mapping */
1087
/* add the mapping into our mapping list */
1089
set_mapping(unicode_char, mapp);
1092
/* we're done with the input file */
1095
/* open the output file */
1096
fp = osfopwb(outfile, OSFTCMAP);
1099
printf("error: unable to open output file \"%s\"\n", outfile);
1104
* Calculate where the unicode-to-local display mapping appears. This
1105
* mapping immediately follows the local-to-unicode round-trip mapping;
1106
* each entry in the round-trip mapping is of a fixed size, so we can
1107
* easily figure out how much space it will take up.
1109
* Each round-trip record takes up 4 bytes (two UINT2's)
1110
*. A UINT4 is needed for this header itself.
1112
*. The round-trip section contains at least one UINT2 (for its
1115
ofs = (roundtrip_count * 4) + 4 + 2;
1118
/* add in the count of round-trip records */
1119
oswp2(valbuf + 4, roundtrip_count);
1121
/* write out the header */
1122
if (osfwb(fp, valbuf, 6))
1124
printf("error writing header\n");
1130
* Write the round-trip mappings. Scan the entire set of pages, and
1131
* write out each entry not marked as "display-only".
1133
for (pagenum = 0 ; pagenum < 256 ; ++pagenum)
1135
/* if there's no page here, skip it */
1136
if (G_mapping[pagenum] == 0)
1139
/* go through the mappings in this page */
1140
for (i = 0 ; i < 256 ; ++i)
1142
/* get the mapping */
1143
entp = G_mapping[pagenum][i];
1146
* if we have no mapping, or this is a display-only mapping,
1147
* skip it - we're only interested in round-trip mappings here
1149
if (entp == 0 || entp->disp_only)
1153
* the entry has a UINT2 for the unicode value, followed by a
1154
* UINT2 for the local character code point
1156
oswp2(valbuf, make_unicode(pagenum, i));
1157
oswp2(valbuf + 2, entp->mapping.local_char);
1160
if (osfwb(fp, valbuf, 4))
1162
printf("error writing mapping to file\n");
1170
* Compile information on the display mappings. We must determine the
1171
* number of display mappings we have, and the number of bytes we'll
1172
* need for the display mappings in the output file.
1174
* Every entry is a display mapping, since a round-trip mapping
1175
* provides a unicode-to-local mapping.
1177
* For a round-trip mapping, we simply store the one-byte or two-byte
1178
* local character sequence given by the local mapping.
1180
* For a display-only mapping, we must scan the expansion, which is a
1181
* list of unicode characters. For each unicode character in the
1182
* expansion, we must find the round-trip mapping for that unicode
1183
* character to get its local mapping, and then count the one-byte or
1184
* two-byte local character sequence for that round-trip mapping.
1186
* Note that each entry stores a one-byte length prefix, so we must
1187
* count one additional byte per entry for this prefix.
1189
for (disp_count = 0, disp_bytes = 0, pagenum = 0 ;
1190
pagenum < 256 ; ++pagenum)
1192
/* if there's no page here, skip it */
1193
if (G_mapping[pagenum] == 0)
1196
/* go through the mappings in this page */
1197
for (i = 0 ; i < 256 ; ++i)
1201
/* get the mapping */
1202
entp = G_mapping[pagenum][i];
1204
/* if we don't have a mapping here, skip this unicode point */
1208
/* count the entry */
1211
/* count the length-prefix byte, which all entries have */
1214
/* we don't have any bytes in the local length yet */
1217
/* check the mapping type */
1218
if (entp->disp_only)
1221
unsigned short *expp;
1224
* This is a display-only mapping, so we have a list of
1225
* unicode characters that we substitute for this unicode
1226
* character, then translate to the local character set
1227
* for display. Scan the expansion list and include the
1228
* bytes for each character in the expansion list.
1230
for (idx = 0, expp = entp->mapping.disp.expansion ;
1231
idx < entp->mapping.disp.exp_len ;
1235
entity_map_t *entp_uc;
1237
/* get this unicode expansion character */
1240
/* look up the mapping for this expansion character */
1241
entp_uc = get_mapping(uc);
1244
* if we don't have a mapping, or it's not a
1245
* round-trip mapping, complain; otherwise, count the
1246
* local character expansion in our output length
1250
/* the unicode character is unmapped */
1251
printf("display mapping for unicode character 0x%X "
1252
"refers to unmapped unicode character 0x%X "
1253
"in its expansion\n",
1254
(unsigned)make_unicode(pagenum, i),
1257
/* note the error */
1260
/* don't scan any further in this expansion */
1263
else if (entp_uc->disp_only)
1266
* the unicode character itself has a display
1267
* mapping - recursive display mappings are not
1270
printf("display mapping for unicode character 0x%X "
1271
"refers to unicode character 0x%X, which "
1272
"itself has a display mapping - a display "
1273
"mappings must refer only to round-trip "
1274
"characters in its expansion\n",
1275
(unsigned)make_unicode(pagenum, i),
1278
/* note the error */
1281
/* don't scan any further in this expansion */
1286
/* add this local mapping to the total length */
1288
local_to_buf(0, entp_uc->mapping.local_char);
1295
* this is a round-trip mapping, so we simply store the
1296
* one or two bytes given for the local character in the
1299
local_len = local_to_buf(0, entp->mapping.local_char);
1303
* add local length of this mapping to the total display bytes
1304
* we've computed so far
1306
disp_bytes += local_len;
1309
* store the local length of this mapping for easy reference
1310
* when we write out the mapping
1312
entp->local_len = local_len;
1316
/* if we have a default character, add it to the display mapping count */
1317
if (default_char_set)
1319
/* count an additional display mapping */
1322
/* add the expansion bytes, including the length prefix */
1323
disp_bytes += 1 + local_to_buf(0, default_char);
1326
/* if we found errors compiling the display mappings, abort */
1331
* Write out the number of display mappings, and the number of bytes
1332
* of display mappings.
1334
oswp2(valbuf, disp_count);
1335
oswp4(valbuf + 2, disp_bytes);
1336
if (osfwb(fp, valbuf, 6))
1338
printf("error writing display translation header\n");
1343
/* check for a default display mapping */
1344
if (default_char_set)
1349
* Write out the default display entity. We store this mapping at
1350
* the translation for Unicode code point zero.
1353
len = local_to_buf(valbuf + 3, default_char);
1354
valbuf[2] = (uchar)len;
1357
if (osfwb(fp, valbuf, 3 + len))
1359
printf("error writing default_display mapping to output file\n");
1366
printf("error: no 'default_display' mapping specified\n");
1372
* write all of the mappings - both the display and round-trip
1373
* mappings provide unicode-to-local mappings
1376
/* go through all of the pages */
1377
for (pagenum = 0 ; pagenum < 256 ; ++pagenum)
1379
/* if this page isn't present, skip it */
1380
if (G_mapping[pagenum] == 0)
1383
/* go through the page */
1384
for (i = 0 ; i < 256 ; ++i)
1388
/* get the mapping */
1389
entp = G_mapping[pagenum][i];
1391
/* if the mapping doesn't exist, skip it */
1395
/* write out this entity's unicode value and local length */
1396
oswp2(valbuf, make_unicode(pagenum, i));
1397
valbuf[2] = (uchar)entp->local_len;
1398
if (osfwb(fp, valbuf, 3))
1400
printf("error writing display mapping to output file\n");
1404
/* check the mapping type */
1405
if (entp->disp_only)
1408
unsigned short *expp;
1411
* display-only mapping - translate the local expansion
1412
* into unicode character
1414
for (idx = 0, expp = entp->mapping.disp.expansion ;
1415
idx < entp->mapping.disp.exp_len ;
1419
entity_map_t *entp_uc;
1421
/* get this unicode expansion character */
1424
/* look up the mapping for this expansion character */
1425
entp_uc = get_mapping(uc);
1427
/* write this character's local mapping */
1428
len = local_to_buf(valbuf, entp_uc->mapping.local_char);
1429
if (osfwb(fp, valbuf, len))
1431
printf("error writing display mapping to file\n");
1439
/* it's a round-trip mapping - store the local mapping */
1440
len = local_to_buf(valbuf, entp->mapping.local_char);
1441
if (osfwb(fp, valbuf, len))
1443
printf("error writing display mapping to file\n");
1452
* Write out the display-only expansions. This is different than the
1453
* display mappings we just wrote in that this time we're going to
1454
* write out the *unicode* expansions for any sequences that turn into
1455
* multiple displayed unicode characters for a single unicode input
1458
oswp2(valbuf, disp_exp_cnt);
1459
oswp4(valbuf + 2, disp_exp_len);
1460
if (osfwb(fp, valbuf, 6))
1462
printf("error writing display expansion to file\n");
1467
/* scan the pages and write out the expansions */
1468
for (pagenum = 0 ; pagenum < 256 ; ++pagenum)
1470
/* if this page isn't present, skip it */
1471
if (G_mapping[pagenum] == 0)
1474
/* go through the page */
1475
for (i = 0 ; i < 256 ; ++i)
1479
/* get the mapping */
1480
entp = G_mapping[pagenum][i];
1483
* if the mapping doesn't exist, or it's not a display-only
1484
* mapping, or it doesn't expand to multiple unicode
1485
* characters, skip it
1489
|| entp->mapping.disp.exp_len < 2)
1492
/* write the unicode value and the expansion length */
1493
oswp2(valbuf, make_unicode(pagenum, i));
1494
valbuf[2] = (unsigned char)entp->mapping.disp.exp_len;
1495
if (osfwb(fp, valbuf, 3))
1497
printf("error writing display expansion to file\n");
1502
/* write the expansion characters */
1503
for (idx = 0 ; idx < entp->mapping.disp.exp_len ; ++idx)
1505
oswp2(valbuf, entp->mapping.disp.expansion[idx]);
1506
if (osfwb(fp, valbuf, 2))
1508
printf("error writing display expansion to file\n");
1516
/* write out the end-of-file marker */
1517
if (osfwb(fp, "$EOF", 4))
1518
printf("error writing end-of-file marker\n");
1521
/* delete the entity list */
1522
for (pagenum = 0 ; pagenum < 256 ; ++pagenum)
1524
/* if there's no mapping here, ignore it */
1525
if (G_mapping[pagenum] == 0)
1528
/* delete each entry on the page */
1529
for (i = 0 ; i < 256 ; ++i)
1531
if (G_mapping[pagenum][i] != 0)
1532
t3free(G_mapping[pagenum][i]);
1535
/* delete the page itself */
1536
t3free(G_mapping[pagenum]);
1539
/* done with the output file */
1542
/* if we had a fatal error, delete the file */