source: branches/branch-1-0/src/helpers/encodings.c@ 302

Last change on this file since 302 was 258, checked in by umoeller, 22 years ago

Sources as of 1.0.2 release.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 21.4 KB
Line 
1
2/*
3 *@@sourcefile encodings.c:
4 * character encoding support. Handles all kinds
5 * of legacy codepages (including most OS/2 codepages)
6 * and Unicode in the form of UTF-8 and translations
7 * between then.
8 *
9 * See encCreateCodec for an introduction.
10 *
11 * See http://www.ietf.org/rfc/rfc2279.txt for
12 * RFC 2279, which defines UTF-8.
13 *
14 * Be warned, compilation of this file takes a long
15 * time because this includes all the complex codepages
16 * from include\encodings.
17 *
18 *@@header "encodings\base.h"
19 *@@added V0.9.9 (2001-02-14) [umoeller]
20 */
21
22/*
23 * Copyright (C) 2001-2002 Ulrich M”ller.
24 * This file is part of the "XWorkplace helpers" source package.
25 * This is free software; you can redistribute it and/or modify
26 * it under the terms of the GNU General Public License as published
27 * by the Free Software Foundation, in version 2 as it comes in the
28 * "COPYING" file of the XWorkplace main distribution.
29 * This program is distributed in the hope that it will be useful,
30 * but WITHOUT ANY WARRANTY; without even the implied warranty of
31 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32 * GNU General Public License for more details.
33 */
34
35#define OS2EMX_PLAIN_CHAR
36 // this is needed for "os2emx.h"; if this is defined,
37 // emx will define PSZ as _signed_ char, otherwise
38 // as unsigned char
39
40#include <stdlib.h>
41#include <string.h>
42
43#include "setup.h" // code generation and debugging options
44
45#include "helpers\standards.h"
46
47#include "encodings\base.h"
48
49#include "encodings\unicase.h"
50
51#include "encodings\alltables.h" // this takes a very long time
52
53#pragma hdrstop
54
55/*
56 *@@category: Helpers\National Language Support\Encodings
57 * See encodings.c.
58 */
59
60/*
61 *@@ G_aEncodings:
62 * list of all encodings supported by this engine
63 * (i.e. we have a corresponding codepage in
64 * include\encodings\*.h) together with some
65 * additional information for each encoding,
66 * such as the corresponding OS/2 codepage
67 * number and a descriptive string.
68 *
69 * For a way too extensive list of codepage
70 * names, see "http://www.iana.org/assignments/character-sets".
71 *
72 *@@added V [umoeller]
73 */
74
75struct
76{
77 ENCID id; // engine ID (enum)
78 PXWPENCODINGMAP pMap; // ptr to map from include\encodings\*.h
79 unsigned long cEntries; // entries in map (array item count)
80 unsigned short usCodepageOS2; // corresponding OS/2 codepage or 0 if none
81 // V1.0.0 (2002-08-21) [umoeller]
82 unsigned short usLatin; // ISO 8859-X correspondance or 0
83 ENCBYTECOUNT bc;
84 const char *pcszDescription; // description
85} G_aEncodings[] =
86 {
87 #define ENCODINGENTRY(id) enc_ ## id, G_ ## id, ARRAYITEMCOUNT(G_ ## id)
88
89 ENCODINGENTRY(cp437), 437, 0, SINGLE, "DOS Latin US",
90 ENCODINGENTRY(cp737), 737, 0, SINGLE, "DOS Greek",
91 ENCODINGENTRY(cp775), 775, 0, SINGLE, "DOS BaltRim",
92 ENCODINGENTRY(cp850), 850, 0, SINGLE, "DOS Latin 1",
93 ENCODINGENTRY(cp852), 852, 0, SINGLE, "DOS Latin 2", // default in Hungary,
94 // Romania, Poland
95 ENCODINGENTRY(cp855), 855, 0, SINGLE, "DOS Cyrillic",
96 ENCODINGENTRY(cp857), 857, 0, SINGLE, "DOS Latin 5 (Turkish)",
97 ENCODINGENTRY(cp860), 860, 0, SINGLE, "DOS Portuguese",
98 ENCODINGENTRY(cp861), 861, 0, SINGLE, "DOS Icelandic",
99 ENCODINGENTRY(cp862), 862, 0, SINGLE, "DOS Hebrew",
100 ENCODINGENTRY(cp863), 863, 0, SINGLE, "DOS Canadian French",
101 ENCODINGENTRY(cp864), 864, 0, SINGLE, "DOS Arabic", // default in Egypt
102 ENCODINGENTRY(cp865), 865, 0, SINGLE, "DOS Nordic",
103 ENCODINGENTRY(cp866), 866, 0, SINGLE, "DOS Cyrillic Russian", // default in Russia
104 ENCODINGENTRY(cp869), 869, 0, SINGLE, "DOS Greek2",
105 ENCODINGENTRY(cp874), 874, 0, SINGLE, "DOS Thai (TIS-620)", // default in Thailand
106
107 ENCODINGENTRY(cp932), 932 /* or 943?*/ ,
108 0, DOUBLE, "Japanese Windows",
109 ENCODINGENTRY(cp936), 936 /* or 946?*/ ,
110 0, DOUBLE, "Chinese",
111 ENCODINGENTRY(cp949), 949 /* was 951, fixed V1.0.2 (2003-09-19) [umoeller] */ ,
112 0, DOUBLE, "Korean",
113 ENCODINGENTRY(cp950), 950 /* was 947, fixed V1.0.2 (2003-09-19) [umoeller] */ ,
114 0, DOUBLE, "Taiwan Big-5", // default in China?
115
116 ENCODINGENTRY(cp1004), 1004, 0, SINGLE, "Windows Extended",
117 ENCODINGENTRY(cp1250), 1250, 0, SINGLE, "Windows Latin 2",
118 ENCODINGENTRY(cp1251), 1251, 0, SINGLE, "Windows Cyrillic",
119 ENCODINGENTRY(cp1252), 1252, 0, SINGLE, "Windows Latin 1",
120 ENCODINGENTRY(cp1253), 1253, 0, SINGLE, "Windows Greek",
121 ENCODINGENTRY(cp1254), 1254, 0, SINGLE, "Windows Turkish",
122 ENCODINGENTRY(cp1255), 1255, 0, SINGLE, "Windows Hebrew",
123 ENCODINGENTRY(cp1256), 1256, 0, SINGLE, "Windows Arabic",
124 ENCODINGENTRY(cp1257), 1257, 0, SINGLE, "Windows Latin-4",
125 ENCODINGENTRY(cp1258), 1258, 0, UNKNOWN, "unknown",
126 ENCODINGENTRY(iso8859_1), 819, 1, SINGLE, "ISO/IEC 8859-1:1998 (Latin-1)",
127 ENCODINGENTRY(iso8859_2), 912, 2, SINGLE, "ISO 8859-2:1999 (Latin-2)",
128 ENCODINGENTRY(iso8859_3), 913, 3, SINGLE, "ISO/IEC 8859-3:1999 (Latin-3)",
129 ENCODINGENTRY(iso8859_4), 914, 4, SINGLE, "ISO/IEC 8859-4:1998 (Latin-4)",
130 ENCODINGENTRY(iso8859_5), 915, 5, SINGLE, "ISO 8859-5:1999 (Cyrillic)",
131 ENCODINGENTRY(iso8859_6), 1089, 6, SINGLE, "ISO 8859-6:1999 (Arabic)",
132 ENCODINGENTRY(iso8859_7), 813, 7, SINGLE, "ISO 8859-7:1987 (Greek)", // default in Greece
133 ENCODINGENTRY(iso8859_8), 916, 8, SINGLE, "ISO/IEC 8859-8:1999 (Hebrew)",
134 ENCODINGENTRY(iso8859_9), 920, 9, SINGLE, "ISO/IEC 8859-9:1999 (Latin-5)",
135 ENCODINGENTRY(iso8859_10), 0, 10, SINGLE, "ISO/IEC 8859-10:1998",
136 ENCODINGENTRY(iso8859_13), 0, 13, SINGLE, "ISO/IEC 8859-13:1998",
137 ENCODINGENTRY(iso8859_14), 0, 14, SINGLE, "ISO/IEC 8859-14:1998",
138 ENCODINGENTRY(iso8859_15), 923, 15, SINGLE, "ISO/IEC 8859-15:1999",
139
140 UNSUPPORTED, NULL, 0, 1200, 0, MULTI_UNICODE, "Unicode UCS-2",
141 UNSUPPORTED, NULL, 0, 1208, 0, MULTI_UNICODE, "Unicode UTF-8"
142 };
143
144/*
145 *@@ ENCCASEFOLD:
146 *
147 *@@added V0.9.20 (2002-07-03) [umoeller]
148 */
149
150typedef struct _ENCCASEFOLD
151{
152 unsigned long cEntries;
153 unsigned long aulFolds[1];
154} ENCCASEFOLD, *PENCCASEFOLD;
155
156STATIC PENCCASEFOLD G_pFold = NULL;
157
158/*
159 *@@ encGetTable:
160 *
161 *@@added V0.9.18 (2002-03-08) [umoeller]
162 */
163
164int encGetTable(ENCID id,
165 PXWPENCODINGMAP *ppMap,
166 unsigned long *pcEntries)
167{
168 unsigned long ul;
169 for (ul = 0;
170 ul < ARRAYITEMCOUNT(G_aEncodings);
171 ul++)
172 {
173 if (G_aEncodings[ul].id == id)
174 {
175 *ppMap = G_aEncodings[ul].pMap;
176 *pcEntries = G_aEncodings[ul].cEntries;
177 return 1;
178 }
179 }
180
181 return 0;
182}
183
184/*
185 *@@ encFindIdForCodepage:
186 * returns the ENCID for the given OS/2
187 * codepage, or UNSUPPORTED if there's none.
188 *
189 *@@added V0.9.18 (2002-03-08) [umoeller]
190 *@@changed V1.0.2 (2003-09-19) [umoeller]: fixed Korean codepage from 951 to 949
191 */
192
193ENCID encFindIdForCodepage(unsigned short usCodepage, // in: codepage to find
194 const char **ppcszDescription, // out: codepage description; ptr can be NULL
195 ENCBYTECOUNT *pByteCount) // out: SINGLE or DOUBLE; ptr can be NULL
196{
197 unsigned long ul;
198 for (ul = 0;
199 ul < ARRAYITEMCOUNT(G_aEncodings);
200 ul++)
201 {
202 if (G_aEncodings[ul].usCodepageOS2 == usCodepage)
203
204 {
205 if (ppcszDescription)
206 *ppcszDescription = G_aEncodings[ul].pcszDescription;
207 if (pByteCount)
208 *pByteCount = G_aEncodings[ul].bc;
209 return G_aEncodings[ul].id;
210 }
211 }
212
213 return UNSUPPORTED;
214}
215
216/*
217 *@@ encCreateCodec:
218 * creates a codec that can be used for conversion between
219 * Unicode and codepaged characters (and vice versa).
220 *
221 * A codec essentially consists of two tables which can
222 * be used for quick index-based lookups in both directions.
223 * This function goes thru the tables provided in
224 * include\encodings\*.h and builds the codec tables
225 * from them.
226 *
227 * This function takes an encoding ID as input. Each
228 * codepage table in include\encodings\*.h has one
229 * of those IDs assigned. Use encFindIdForCodepage
230 * to find the ID for a given OS/2 codepage.
231 *
232 * Use codecs carefully and only when they are really
233 * needed for a specific conversion. Building a codec
234 * is expensive, so you should create a codec once
235 * and reuse it for future conversions. In addition,
236 * create codecs only for the codepages that are
237 * actually used. Each codec will take up to
238 * n * sizeof(USHORT) bytes, where n is the highest
239 * Unicode character used in the codepage.
240 *
241 * Codec remarks:
242 *
243 * -- All codepages share the first 128 characters
244 * (0-0x7F) with ASCII.
245 *
246 * -- Since the first 128 characters (0-0x7F) in
247 * Unicode are equivalent to ASCII also, codecs
248 * are not needed if you process ASCII strings
249 * only.
250 *
251 * -- Since the next 128 characters (0x80-0xFF) in
252 * Unicode are equivalent to ISO/IEC 8859-1
253 * (Latin-1), codecs aren't needed for those
254 * strings either.
255 *
256 * Note that codepoints 0x80-0x9F are undefined
257 * in Latin-1 but used as control sequences in
258 * Unicode.
259 *
260 * -- As far as I know, codepage 1252, which is
261 * used per default under Windows, is equivalent
262 * to Latin 1 except that it also defines
263 * codepoints 0x80-0x9F to certain DTP characters.
264 *
265 * -- From my testing, codepage 1004 (which is
266 * described as "Windows-compatible" in most OS/2
267 * docs) is the same as codepage 1252, except for
268 * character 0xAF.
269 *
270 * Unfortunately, OS/2 uses codepage 850 on most
271 * systems (and Windows uses OS/2 codepage 1252),
272 * so for conversion between those, codecs are needed.
273 *
274 * This works and is presently used in WarpIN.
275 */
276
277PCONVERSION encCreateCodec(ENCID id)
278{
279 PXWPENCODINGMAP pEncodingMap;
280 unsigned long cArrayEntries;
281
282 if (encGetTable(id,
283 &pEncodingMap,
284 &cArrayEntries))
285 {
286 unsigned short usHighestCP = 0,
287 usHighestUni = 0;
288 unsigned long ul;
289
290 // step 1:
291 // run through the table and calculate the highest
292 // character entry used
293 for (ul = 0;
294 ul < cArrayEntries;
295 ul++)
296 {
297 if (pEncodingMap[ul].usCP > usHighestCP)
298 usHighestCP = pEncodingMap[ul].usCP;
299 if (pEncodingMap[ul].usUni > usHighestUni)
300 usHighestUni = pEncodingMap[ul].usUni;
301 }
302
303 // step 2: allocate encoding table
304 if (usHighestCP && usHighestUni)
305 {
306 PCONVERSION pTableNew;
307 if (pTableNew = NEW(CONVERSION))
308 {
309 unsigned long cbEntriesUniFromCP
310 = (usHighestCP + 1) * sizeof(unsigned short);
311 unsigned long cbEntriesCPFromUni
312 = (usHighestUni + 1) * sizeof(unsigned short);
313
314 ZERO(pTableNew);
315
316 pTableNew->usHighestCP = usHighestCP;
317 pTableNew->usHighestUni = usHighestUni;
318
319 if ( (pTableNew->ausEntriesUniFromCP
320 = (unsigned short*)malloc(cbEntriesUniFromCP))
321 && (pTableNew->ausEntriesCPFromUni
322 = (unsigned short*)malloc(cbEntriesCPFromUni))
323 )
324 {
325 // step 3: fill encoding tables
326
327 memset(pTableNew->ausEntriesUniFromCP,
328 0xFF,
329 cbEntriesUniFromCP);
330 memset(pTableNew->ausEntriesCPFromUni,
331 0xFF,
332 cbEntriesCPFromUni);
333
334 for (ul = 0;
335 ul < cArrayEntries;
336 ul++)
337 {
338 PXWPENCODINGMAP pEntry = &pEncodingMap[ul];
339
340 pTableNew->ausEntriesUniFromCP[pEntry->usCP] = pEntry->usUni;
341
342 pTableNew->ausEntriesCPFromUni[pEntry->usUni] = pEntry->usCP;
343 }
344
345 return pTableNew;
346 }
347
348 free(pTableNew);
349 }
350 }
351 }
352
353 return NULL;
354}
355
356/*
357 *@@ encFreeCodec:
358 * frees a codec created with encCreateCodec
359 * and sets the given pointer to NULL.
360 *
361 * This works and is presently used in WarpIN.
362 *
363 *@@added V0.9.18 (2002-03-08) [umoeller]
364 */
365
366void encFreeCodec(PCONVERSION *ppTable) // in: ptr to codec ptr returned by encCreateCodec
367{
368 PCONVERSION pTable;
369 if (pTable = *ppTable)
370 {
371 if (pTable->ausEntriesUniFromCP)
372 free(pTable->ausEntriesUniFromCP);
373 if (pTable->ausEntriesCPFromUni)
374 free(pTable->ausEntriesCPFromUni);
375 free(pTable);
376 *ppTable = NULL;
377 }
378}
379
380/*
381 *@@ encChar2Uni:
382 * converts a codepage-specific character
383 * to Unicode, using the given conversion
384 * table from encCreateCodec().
385 *
386 * Returns 0xFFFF on errors, which is unlikely
387 * with Unicode though.
388 *
389 * This works and is presently used in WarpIN.
390 *
391 *@@added V0.9.18 (2002-03-08) [umoeller]
392 */
393
394unsigned long encChar2Uni(PCONVERSION pTable,
395 unsigned short c)
396{
397 if ( (pTable)
398 && (c <= pTable->usHighestCP)
399 )
400 return pTable->ausEntriesUniFromCP[c];
401
402 return 0xFFFF;
403}
404
405/*
406 *@@ encUni2Char:
407 * converts a Unicode character to the
408 * codepage specified by the given
409 * conversion table from encCreateCodec().
410 *
411 * Returns 0xFFFF if the Unicode character
412 * has no codepage equivalent.
413 *
414 * This works and is presently used in WarpIN.
415 *
416 *@@added V0.9.18 (2002-03-08) [umoeller]
417 */
418
419unsigned short encUni2Char(PCONVERSION pTable,
420 unsigned long ulUni)
421{
422 if ( (pTable)
423 && (ulUni <= pTable->usHighestUni)
424 )
425 return pTable->ausEntriesCPFromUni[ulUni];
426
427 return 0xFFFF;
428}
429
430/*
431 *@@ encDecodeUTF8:
432 * decodes one UTF-8 character and returns
433 * the Unicode value or -1 if the character
434 * is invalid.
435 *
436 * On input, *ppch is assumed to point to
437 * the first byte of the UTF-8 char to be
438 * read.
439 *
440 * This function will advance *ppch by at
441 * least one byte (or more if the UTF-8
442 * char initially pointed to introduces
443 * a multi-byte sequence).
444 *
445 * This returns -1 if *ppch points to an
446 * invalid encoding (in which case the
447 * pointer is advanced anyway).
448 *
449 * This returns 0 if *ppch points to a
450 * null character.
451 *
452 * This works and is presently used in WarpIN.
453 *
454 *@@added V0.9.14 (2001-08-09) [umoeller]
455 */
456
457unsigned long encDecodeUTF8(const char **ppch)
458{
459 unsigned long ulChar;
460 unsigned long ulCount;
461 int fIllegal;
462
463 if (!(ulChar = **ppch))
464 // null is null
465 return 0;
466
467 // if (ulChar < 0x80): simple, one byte only... use that
468
469 if (ulChar < 0x80)
470 {
471 (*ppch)++;
472 return ulChar;
473 }
474
475 ulCount = 1;
476 fIllegal = 0;
477
478 // note: 0xc0 and 0xc1 are reserved and
479 // cannot appear as the first UTF-8 byte
480
481 if ( (ulChar >= 0xc2)
482 && (ulChar < 0xe0)
483 )
484 {
485 // that's two bytes
486 ulCount = 2;
487 ulChar &= 0x1f;
488 }
489 else if ((ulChar & 0xf0) == 0xe0)
490 {
491 // three bytes
492 ulCount = 3;
493 ulChar &= 0x0f;
494 }
495 else if ((ulChar & 0xf8) == 0xf0)
496 {
497 // four bytes
498 ulCount = 4;
499 ulChar &= 0x07;
500 }
501 else if ((ulChar & 0xfc) == 0xf8)
502 {
503 // five bytes
504 ulCount = 5;
505 ulChar &= 0x03;
506 }
507 else if ((ulChar & 0xfe) == 0xfc)
508 {
509 // six bytes
510 ulCount = 6;
511 ulChar &= 0x01;
512 }
513 else
514 ++fIllegal;
515
516 if (!fIllegal)
517 {
518 // go for the second and more bytes then
519 int ul2;
520
521 for (ul2 = 1;
522 ul2 < ulCount;
523 ++ul2)
524 {
525 unsigned long ulChar2 = *((*ppch) + ul2);
526
527 if (!(ulChar2 & 0xc0)) // != 0x80)
528 {
529 ++fIllegal;
530 break;
531 }
532
533 ulChar <<= 6;
534 ulChar |= ulChar2 & 0x3f;
535 }
536 }
537
538 if (fIllegal)
539 {
540 // skip all the following characters
541 // until we find something with bit 7 off
542 do
543 {
544 ulChar = *(++(*ppch));
545 if (!ulChar)
546 break;
547 } while (ulChar & 0x80);
548 }
549 else
550 *ppch += ulCount;
551
552 return ulChar;
553}
554
555/*
556 *@@ encInitCase:
557 * creates a casefold for later use with
558 * encToUpper.
559 *
560 * This only uses one-byte sequences from
561 * the Unicode case folding table (see
562 * include\encodings\unicase.h), so this
563 * cannot be used for expanding characters
564 * at this point.
565 *
566 * Returns 1 (TRUE) on success.
567 *
568 * This works and is presently used in WarpIN.
569 *
570 *@@added V0.9.20 (2002-07-03) [umoeller]
571 */
572
573int encInitCase(void)
574{
575 unsigned long ul,
576 cEntries = 0,
577 cb;
578
579 for (ul = 0;
580 ul < ARRAYITEMCOUNT(G_aCaseFolds);
581 ++ul)
582 {
583 // ignore CASEFL_T (duplicate entries for i chars)
584 // and CASEFL_F (expansions)
585 if ( (G_aCaseFolds[ul].fl & (CASEFL_C | CASEFL_S))
586 && (G_aCaseFolds[ul].ulLow > cEntries)
587 )
588 cEntries = G_aCaseFolds[ul].ulLow;
589 }
590
591 cb = sizeof(ENCCASEFOLD) + cEntries * sizeof(unsigned long);
592 if (G_pFold = (PENCCASEFOLD)malloc(cb))
593 {
594 memset(G_pFold, 0, cb);
595 G_pFold->cEntries = cEntries;
596
597 for (ul = 0;
598 ul < ARRAYITEMCOUNT(G_aCaseFolds);
599 ++ul)
600 {
601 if (G_aCaseFolds[ul].fl & (CASEFL_C | CASEFL_S))
602 G_pFold->aulFolds[G_aCaseFolds[ul].ulLow] = G_aCaseFolds[ul].c1;
603 }
604
605 return 1;
606 }
607
608 return 0;
609}
610
611/*
612 *@@ encToUpper:
613 * converts the given unicode character to
614 * upper case, if possible, or returns
615 * ulUni back if Unicode doesn't define
616 * an upper-case character for it.
617 *
618 * Special cases:
619 *
620 * -- Returns 0 for 0.
621 *
622 * Preconditions:
623 *
624 * -- You must call encInitCase before
625 * the first call.
626 *
627 * This works and is presently used in WarpIN.
628 *
629 *@@added V0.9.20 (2002-07-03) [umoeller]
630 */
631
632unsigned long encToUpper(unsigned long ulUni)
633{
634 unsigned long ulFold;
635
636 if ( (ulUni < G_pFold->cEntries)
637 && (ulFold = G_pFold->aulFolds[ulUni])
638 )
639 return ulFold;
640
641 return ulUni;
642}
643
644/*
645 *@@ encicmp:
646 * like stricmp, but for UTF-8 strings.
647 * This uses encToUpper for the comparisons.
648 *
649 * Like stricmp, this returns:
650 *
651 * -- -1 if pcsz1 is less than pcsz2
652 * -- 0 if pcsz1 is equal to pcsz2
653 * -- +1 if pcsz1 is greater than pcsz2
654 *
655 * However, this does not crash on passing
656 * in NULL strings.
657 *
658 * Preconditions:
659 *
660 * -- You must call encInitCase before
661 * the first call.
662 *
663 * This works and is presently used in WarpIN.
664 *
665 *@@added V0.9.20 (2002-07-03) [umoeller]
666 */
667
668int encicmp(const char *pcsz1,
669 const char *pcsz2)
670{
671 const char *p1 = pcsz1,
672 *p2 = pcsz2;
673
674 unsigned long ul1, ul2;
675
676 do
677 {
678 // encDecodeUTF8 returns null for null, so this is safe
679 ul1 = encToUpper(encDecodeUTF8(&p1));
680 ul2 = encToUpper(encDecodeUTF8(&p2));
681
682 if (ul1 < ul2)
683 return -1;
684 if (ul1 > ul2)
685 return +1;
686
687 // both are equal: check for null bytes then
688 if (!ul1)
689 if (!ul2)
690 return 0;
691 else
692 // ul1 is null, but ul2 isn't:
693 return -1;
694 else
695 if (!ul2)
696 // ul1 is not null, but ul2 is:
697 return +1;
698
699 // both are non-null: continue
700
701 } while (1);
702
703 return 0;
704}
705
Note: See TracBrowser for help on using the repository browser.