1 /*
2 Copyright (C) 2004-2017,2018 John E. Davis
3 
4 This file is part of the S-Lang Library.
5 
6 The S-Lang Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License as
8 published by the Free Software Foundation; either version 2 of the
9 License, or (at your option) any later version.
10 
11 The S-Lang Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with this library; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
19 USA.
20 */
21 
22 #include "slinclud.h"
23 
24 #include "slang.h"
25 #include "_slang.h"
26 
27 #ifdef upcase
28 # undef upcase
29 #endif
30 
31 /* The Boyer-Moore search algorthm is used below under the following
32  * conditions:
33  *
34  *    1.  The search is case-sensitive.
35  *    3.  UTF-8 mode is not active.
36  *    2.  The search key contains only of single byte characters.
37  *    4.  The multi-byte characters in the key are case-less.
38  *
39  * Otherwise, the search is case-insensitive and the key consists of
40  * multi-byte characters.  In this case, a modified BM algorithm is used
41  * _if_ each character in the key is composed of multi-byte characters that
42  * have upper and lower case versions of the same length.
43  *
44  * Otherwise, the search search will be performed in a brute-force manner.
45  */
46 
47 typedef struct
48 {
49    SLuchar_Type *key;
50    size_t key_len;
51    size_t fskip_table[256];
52    size_t bskip_table[256];
53 }
54 BoyerMoore_Search_Type;
55 
56 typedef struct
57 {
58    SLuchar_Type **lower_chars;
59    SLuchar_Type **upper_chars;
60    SLstrlen_Type nlower_chars;
61    SLstrlen_Type nupper_chars;
62    SLsearch_Type *st;
63 }
64 BruteForce_Search_Type;
65 
66 struct _pSLsearch_Type
67 {
68    SLuchar_Type *(*search_fun)(SLsearch_Type *, SLuchar_Type *, SLuchar_Type *, SLuchar_Type *, int);
69    void (*free_fun) (SLsearch_Type *);
70    int flags;
71    size_t match_len;
72    union
73      {
74         BoyerMoore_Search_Type bm;
75         BruteForce_Search_Type bf;
76      }
77    s;
78 };
79 
80 /* Simple BM search--- case-sensitive, or case-less, or no UTF-8, or 7-bit ascii */
81 static SLuchar_Type *
bm_search_forward(SLsearch_Type * st,SLuchar_Type * beg,SLuchar_Type * end)82   bm_search_forward (SLsearch_Type *st, SLuchar_Type *beg, SLuchar_Type *end)
83 {
84    register unsigned char char1;
85    unsigned char *pos;
86    size_t *skip_table;
87    SLuchar_Type *key;
88    SLstrlen_Type key_len;
89    int case_fold;
90    BoyerMoore_Search_Type *bm;
91 
92    bm = &st->s.bm;
93    key_len = bm->key_len;
94 
95    st->match_len = 0;
96 
97    if ((key_len > (unsigned int) (end - beg))
98        || (key_len == 0))
99      return NULL;
100 
101    case_fold = st->flags & SLSEARCH_CASELESS;
102    key = bm->key;
103    skip_table = bm->fskip_table;
104 
105    char1 = key[key_len - 1];
106    beg += (key_len - 1);
107 
108    while(1)
109      {
110 	SLuchar_Type ch;
111         SLstrlen_Type j;
112 
113         while (beg < end)
114           {
115 	     SLstrlen_Type dbeg;
116              ch = *beg;
117              dbeg = skip_table[ch];
118              if ((dbeg < key_len)
119                  && ((ch == char1)
120                      || (case_fold && (char1 == UPPER_CASE(ch)))))
121                break;
122              beg += dbeg;
123           }
124 	if (beg >= end) return NULL;
125 
126 	pos = beg - (key_len - 1);
127 	for (j = 0; j < key_len; j++)
128 	  {
129              ch = pos[j];
130 	     if ((key[j] != ch)
131                  && ((case_fold == 0)
132                      || (key[j] != UPPER_CASE(ch))))
133                break;
134           }
135 
136 	if (j == key_len)
137           {
138              st->match_len = key_len;
139              return pos;
140           }
141 
142 	beg += 1;
143      }
144 }
145 
146 static SLuchar_Type *
bm_search_backward(SLsearch_Type * st,SLuchar_Type * beg,SLuchar_Type * start,SLuchar_Type * end)147   bm_search_backward (SLsearch_Type *st,
148                       SLuchar_Type *beg, SLuchar_Type *start, SLuchar_Type *end)
149 {
150    SLuchar_Type char1;
151    SLstrlen_Type j, ofs;
152    SLstrlen_Type key_len;
153    SLuchar_Type *key;
154    int case_fold;
155    size_t *skip_table;
156    BoyerMoore_Search_Type *bm;
157 
158    st->match_len = 0;
159    bm = &st->s.bm;
160 
161    key_len = bm->key_len;
162    if ((key_len > (unsigned int) (end - beg))
163        || (key_len == 0)
164        || (end <= beg)
165        || (start < beg)
166        || (start >= end))
167      return NULL;
168 
169    case_fold = st->flags & SLSEARCH_CASELESS;
170    key = bm->key;
171    skip_table = bm->bskip_table;
172 
173    if (start + key_len > end)
174      start = end - key_len;
175 
176    char1 = key[0];
177 
178     while(1)
179       {
180          SLuchar_Type ch;
181 
182 	 while (beg <= start)
183 	   {
184               ch = *start;
185 
186               if ((ch == char1)
187                   || (case_fold && (char1 == UPPER_CASE(ch))))
188                 break;
189 
190               ofs = skip_table[ch];
191 	      start -= ofs;
192 	   }
193 	 if (beg > start) return NULL;
194 
195 	 for (j = 1; j < key_len; j++)
196 	   {
197               ch = start[j];
198               if ((key[j] != ch)
199                   && ((case_fold == 0)
200                       || (key[j] != UPPER_CASE(ch))))
201                 break;
202 	   }
203 	 if (j == key_len)
204            {
205               st->match_len = key_len;
206               return start;
207            }
208 	 start--;
209       }
210 }
211 
bm_search(SLsearch_Type * st,SLuchar_Type * pmin,SLuchar_Type * p,SLuchar_Type * pmax,int dir)212 static SLuchar_Type *bm_search (SLsearch_Type *st,
213                                 SLuchar_Type *pmin, SLuchar_Type *p, SLuchar_Type *pmax,
214                                 int dir)
215 {
216    if (dir > 0)
217      return bm_search_forward (st, p, pmax);
218    else
219      return bm_search_backward (st, pmin, p, pmax);
220 }
221 
222 /* Return value, if non-NULL returns pointer to the end of the string */
223 static SLuchar_Type *
looking_at_bf(SLuchar_Type * pmin,SLuchar_Type * pmax,SLuchar_Type ** lower_chars,SLstrlen_Type nlower_chars,SLuchar_Type ** upper_chars,SLstrlen_Type nupper_chars)224   looking_at_bf (SLuchar_Type *pmin, SLuchar_Type *pmax,
225                  SLuchar_Type **lower_chars, SLstrlen_Type nlower_chars,
226                  SLuchar_Type **upper_chars, SLstrlen_Type nupper_chars)
227 
228 {
229    unsigned int n;
230 
231    n = 0;
232    while ((n < nupper_chars) && (n < nlower_chars))
233      {
234         SLuchar_Type *up, *lo, *p;
235         up = upper_chars[n];
236         lo = lower_chars[n];
237 
238         n++;
239 
240         p = pmin;
241         while ((p < pmax)
242                && (*up == *p)
243                && (*up != 0))
244           {
245              up++;
246              p++;
247           }
248 
249         if (*up == 0)
250           {
251              pmin = p;
252              continue;
253           }
254 
255         p = pmin;
256         while ((p < pmax)
257                && (*lo == *p)
258                && (*lo != 0))
259           {
260              lo++;
261              p++;
262           }
263 
264         if (*lo == 0)
265           {
266              pmin = p;
267              continue;
268           }
269 
270         return NULL;
271      }
272    return pmin;
273 }
274 
275 /* Brute force case-insensitive searches */
276 static SLuchar_Type *
bf_search_forward(SLsearch_Type * st,SLuchar_Type * pmin,SLuchar_Type * pmax)277   bf_search_forward (SLsearch_Type *st,
278                      SLuchar_Type *pmin, SLuchar_Type *pmax)
279 {
280    SLsearch_Type *bf_st;
281    SLuchar_Type chup, chlo;
282    SLuchar_Type **upper_chars;
283    SLuchar_Type **lower_chars;
284    SLstrlen_Type nupper_chars, nlower_chars;
285 
286    bf_st = st->s.bf.st;
287    upper_chars = st->s.bf.upper_chars;
288    lower_chars = st->s.bf.lower_chars;
289    nupper_chars = st->s.bf.nupper_chars;
290    nlower_chars = st->s.bf.nlower_chars;
291 
292    chup = upper_chars[0][0];
293    chlo = lower_chars[0][0];
294 
295    while (1)
296      {
297         SLuchar_Type *p;
298 
299         if (bf_st != NULL)
300           {
301              if (NULL == (pmin = SLsearch_forward (bf_st, pmin, pmax)))
302                {
303                   st->match_len = 0;
304                   return NULL;
305                }
306              p = pmin + bf_st->match_len;
307           }
308         else
309           {
310              while (pmin < pmax)
311                {
312                   if ((*pmin == chup) || (*pmin == chlo))
313                     break;
314 
315                   pmin++;
316                }
317 
318              if (pmin >= pmax)
319                {
320                   st->match_len = 0;
321                   return NULL;
322                }
323 
324              p = pmin;
325           }
326 
327         p = looking_at_bf (p, pmax, lower_chars, nlower_chars,
328                            upper_chars, nupper_chars);
329 
330         if (p != NULL)
331           {
332              st->match_len = p - pmin;
333              return pmin;
334           }
335 
336         pmin++;
337      }
338 }
339 
340 static SLuchar_Type *
bf_search_backward(SLsearch_Type * st,SLuchar_Type * pmin,SLuchar_Type * start,SLuchar_Type * pmax)341   bf_search_backward (SLsearch_Type *st,
342                       SLuchar_Type *pmin, SLuchar_Type *start, SLuchar_Type *pmax)
343 {
344    SLsearch_Type *bf_st;
345    SLuchar_Type chup, chlo;
346    SLuchar_Type **upper_chars;
347    SLuchar_Type **lower_chars;
348    SLstrlen_Type nupper_chars, nlower_chars;
349 
350    bf_st = st->s.bf.st;
351    upper_chars = st->s.bf.upper_chars;
352    lower_chars = st->s.bf.lower_chars;
353    nupper_chars = st->s.bf.nupper_chars;
354    nlower_chars = st->s.bf.nlower_chars;
355 
356    chup = upper_chars[0][0];
357    chlo = lower_chars[0][0];
358 
359    while (1)
360      {
361         SLuchar_Type *p;
362 
363         if (bf_st != NULL)
364           {
365              if (NULL == (start = SLsearch_backward (bf_st, pmin, start+1, pmax)))
366                {
367                   st->match_len = 0;
368                   return NULL;
369                }
370              p = start + bf_st->match_len;
371           }
372         else
373           {
374              /* start--; */
375              while (start >= pmin)
376                {
377                   if ((*start == chup) || (*start == chlo))
378                     break;
379                   start--;
380                }
381 
382              if (start < pmin)
383                {
384                   st->match_len = 0;
385                   return NULL;
386                }
387 
388              p = start;
389           }
390 
391         p = looking_at_bf (p, pmax, lower_chars, nlower_chars,
392                            upper_chars, nupper_chars);
393 
394         if (p != NULL)
395           {
396              st->match_len = p - start;
397              return start;
398           }
399 
400         start--;
401      }
402 }
403 
404 static SLuchar_Type *
bf_search(SLsearch_Type * st,SLuchar_Type * pmin,SLuchar_Type * p,SLuchar_Type * pmax,int dir)405   bf_search (SLsearch_Type *st,
406              SLuchar_Type *pmin, SLuchar_Type *p, SLuchar_Type *pmax,
407              int dir)
408 {
409    if (dir > 0)
410      return bf_search_forward (st, p, pmax);
411    else
412      return bf_search_backward (st, pmin, p, pmax);
413 }
414 
SLsearch_forward(SLsearch_Type * st,SLuchar_Type * pmin,SLuchar_Type * pmax)415 SLuchar_Type *SLsearch_forward (SLsearch_Type *st,
416                                 SLuchar_Type *pmin, SLuchar_Type *pmax)
417 {
418    if (st == NULL)
419      return NULL;
420 
421    return st->search_fun (st, pmin, pmin, pmax, 1);
422 }
423 
SLsearch_backward(SLsearch_Type * st,SLuchar_Type * pmin,SLuchar_Type * p,SLuchar_Type * pmax)424 SLuchar_Type *SLsearch_backward (SLsearch_Type *st, SLuchar_Type *pmin,
425                                  SLuchar_Type *p, SLuchar_Type *pmax)
426 {
427    if (st == NULL)
428      return NULL;
429 
430    /* For the backward search, the first character of the string
431     * is assumed satisfy pmin <= BEG < p
432     * and end satisfies pmin < END <= pmax
433     */
434    return st->search_fun (st, pmin, p-1, pmax, -1);
435 }
436 
437 static int Case_Tables_Ok;
438 
init_skip_table(SLuchar_Type * key,SLstrlen_Type key_len,size_t * skip_table,int dir,int flags)439 static void init_skip_table (SLuchar_Type *key, SLstrlen_Type key_len,
440 			     size_t *skip_table, int dir, int flags)
441 {
442    unsigned int i;
443 
444    for (i = 0; i < 256; i++)
445      skip_table[i] = key_len;
446 
447    if (dir < 0)
448      key += (key_len-1);
449 
450    /* For a case-insensitive search, the key here will be uppercased */
451    flags = flags & SLSEARCH_CASELESS;
452    i = 0;
453    while (i < key_len)
454      {
455 	i++;
456 	skip_table[*key] = key_len - i;
457 	if (flags)
458 	  skip_table[LOWER_CASE(*key)] = key_len - i;
459 	key += dir;
460      }
461 }
462 
bm_free(SLsearch_Type * st)463 static void bm_free (SLsearch_Type *st)
464 {
465    SLang_free_slstring ((char *) st->s.bm.key);
466 }
467 
bf_free(SLsearch_Type * st)468 static void bf_free (SLsearch_Type *st)
469 {
470    SLstrlen_Type i, n;
471    SLuchar_Type **a;
472 
473    if (NULL != (a = st->s.bf.lower_chars))
474      {
475         n = st->s.bf.nlower_chars;
476         for (i = 0; i < n; i++)
477           SLang_free_slstring ((char *) a[i]);
478         SLfree ((char *) a);
479      }
480 
481    if (NULL != (a = st->s.bf.upper_chars))
482      {
483         n = st->s.bf.nupper_chars;
484         for (i = 0; i < n; i++)
485           SLang_free_slstring ((char *) a[i]);
486         SLfree ((char *) a);
487      }
488 }
489 
SLsearch_match_len(SLsearch_Type * st)490 SLstrlen_Type SLsearch_match_len (SLsearch_Type *st)
491 {
492    if (st == NULL)
493      return 0;
494 
495    return st->match_len;
496 }
497 
SLsearch_delete(SLsearch_Type * st)498 void SLsearch_delete (SLsearch_Type *st)
499 {
500    if (st == NULL)
501      return;
502 
503    (*st->free_fun) (st);
504    SLfree ((char *)st);
505 }
506 
507 /* This is used if the key is not UTF-8, or it is but the search is case-sensitive */
bm_open_search(SLuchar_Type * key,int flags)508 static SLsearch_Type *bm_open_search (SLuchar_Type *key, int flags)
509 {
510    SLsearch_Type *st;
511    size_t keylen;
512 
513    keylen = strlen ((char *)key);
514    if (NULL == (st = (SLsearch_Type *)SLcalloc (1, sizeof (SLsearch_Type))))
515      return NULL;
516 
517    st->free_fun = bm_free;
518 
519    /* If the search is case-insensitive, then it must either be all ascii, or
520     * it is not unicode.  In either case, the UPPER_CASE and LOWER_CASE macros
521     * should be ok to use.
522     */
523    if (flags & SLSEARCH_CASELESS)
524      {
525 	char *keyup = SLmake_nstring ((char *)key, keylen);
526 	if (keyup != NULL)
527 	  {
528 	     unsigned char *k = (unsigned char *)keyup;
529 	     while (*k != 0)
530 	       {
531 		  *k = UPPER_CASE(*k);
532 		  k++;
533 	       }
534 	     st->s.bm.key = (SLuchar_Type *)SLang_create_slstring (keyup);
535 	     SLfree (keyup);
536 	  }
537 	else st->s.bm.key = NULL;
538      }
539    else st->s.bm.key = (SLuchar_Type*) SLang_create_slstring ((char *)key);
540 
541    if (st->s.bm.key == NULL)
542      {
543         SLsearch_delete (st);
544         return NULL;
545      }
546    st->s.bm.key_len = keylen;
547    st->flags = flags;
548 
549    st->search_fun = bm_search;
550 
551    init_skip_table (st->s.bm.key, st->s.bm.key_len, st->s.bm.fskip_table, 1, flags);
552    init_skip_table (st->s.bm.key, st->s.bm.key_len, st->s.bm.bskip_table, -1, flags);
553    return st;
554 }
555 
is_bm_ok(SLuchar_Type * key,SLstrlen_Type len,SLuchar_Type ** non_ascii)556 static int is_bm_ok (SLuchar_Type *key, SLstrlen_Type len, SLuchar_Type **non_ascii)
557 {
558    SLuchar_Type *keymax;
559 
560    /* See if the key is ascii-only */
561    keymax = key + len;
562 
563    while (key < keymax)
564      {
565         if (*key & 0x80)
566           {
567              *non_ascii = key;
568              return 0;
569           }
570 
571         key++;
572      }
573 
574    return 1;
575 }
576 
make_string_array(SLuchar_Type * u,SLstrlen_Type len,SLuindex_Type * nump)577 static SLuchar_Type **make_string_array (SLuchar_Type *u, SLstrlen_Type len, SLuindex_Type *nump)
578 {
579    SLuchar_Type *umax;
580    SLuchar_Type **a;
581    SLuindex_Type num, i;
582    int ignore_combining = 0;
583 
584    num = SLutf8_strlen (u, ignore_combining);
585    if (num == 0)
586      return NULL;                      /* should not happen */
587 
588    if (NULL == (a = (SLuchar_Type **) SLcalloc (sizeof (SLuchar_Type *), num)))
589      return NULL;
590 
591    umax = u + len;
592    for (i = 0; i < num; i++)
593      {
594         SLuchar_Type *u1 = SLutf8_skip_char (u, umax);
595         if (NULL == (a[i] = (SLuchar_Type *)SLang_create_nslstring ((char *)u, u1 - u)))
596           goto return_error;
597 	u = u1;
598      }
599    *nump = num;
600    return a;
601 
602    return_error:
603    for (i = 0; i < num; i++)
604      SLang_free_slstring ((char *) a[i]);
605    SLfree ((char *) a);
606    return NULL;
607 }
608 
SLsearch_new(SLuchar_Type * key,int flags)609 SLsearch_Type *SLsearch_new (SLuchar_Type *key, int flags)
610 {
611    SLsearch_Type *st, *bf_st;
612    SLuchar_Type *key_upper, *key_lower, *non_ascii;
613    size_t len, upper_len, lower_len;
614 
615    if (Case_Tables_Ok == 0)
616      SLang_init_case_tables ();
617 
618    if (key == NULL)
619      return NULL;
620 
621    if ((0 == (flags & SLSEARCH_CASELESS))
622        || (0 == (flags & SLSEARCH_UTF8)))
623      return bm_open_search (key, flags);
624 
625    /* Otherwise the key is UTF-8 and the search is case-insensitive */
626    len = strlen ((char *)key);
627    key_upper = SLutf8_strup (key, key + len);
628    if (key_upper == NULL)
629      return NULL;
630 
631    upper_len = strlen ((char *)key_upper);
632 
633    if (is_bm_ok (key_upper, upper_len, &non_ascii))
634      {
635         st = bm_open_search (key_upper, flags);
636         SLang_free_slstring ((char *)key_upper);
637         return st;
638      }
639 
640    /* Tricky part */
641 
642    if (NULL == (key_lower = SLutf8_strlo (key, key + len)))
643      {
644         SLang_free_slstring ((char *)key_upper);
645         return NULL;
646      }
647 
648    lower_len = strlen ((char *)key_lower);
649 
650    /* Try a case-less search */
651    if ((lower_len == upper_len)
652        && (0 == strcmp ((char *)key_upper, (char *)key_lower)))
653      {
654         flags &= ~SLSEARCH_CASELESS;
655         st = bm_open_search (key_upper, flags);
656         SLang_free_slstring ((char *)key_upper);
657         SLang_free_slstring ((char *)key_lower);
658         return st;
659      }
660 
661    /* Now Perform a brute-force search. */
662 
663    /* If the first few characters of the search string are ascii, then
664     * use BM for that portion
665     */
666    bf_st = NULL;
667    if (non_ascii - key_upper >= 3)
668      {
669         SLuchar_Type *key1 = (SLuchar_Type *) SLmake_nstring ((char *)key_upper, non_ascii - key_upper);
670 
671         /* ok to propagate NULL below */
672         bf_st = SLsearch_new (key1, flags);
673         SLfree ((char *)key1);
674         if (bf_st == NULL)
675           {
676              SLang_free_slstring ((char *)key_upper);
677              SLang_free_slstring ((char *)key_lower);
678              return NULL;
679           }
680 
681         key1 = (SLuchar_Type *) SLang_create_slstring ((char *)non_ascii);
682         non_ascii = key_lower + (non_ascii - key_upper);
683         SLang_free_slstring ((char *)key_upper);
684         key_upper = key1;
685 
686         key1 = (SLuchar_Type *)SLang_create_slstring ((char *)non_ascii);
687         SLang_free_slstring ((char *)key_lower);
688         key_lower = key1;
689 
690         if ((key_lower == NULL) || (key_upper == NULL))
691           {
692              SLang_free_slstring ((char *)key_upper);
693              SLang_free_slstring ((char *)key_lower);
694              SLsearch_delete (bf_st);
695              return NULL;
696           }
697         upper_len = strlen ((char *)key_upper);
698         lower_len = strlen ((char *)key_lower);
699      }
700 
701    st = (SLsearch_Type *)SLcalloc (sizeof (SLsearch_Type), 1);
702    if (st == NULL)
703      goto return_error;
704    st->free_fun = bf_free;
705    st->flags = flags;
706    st->search_fun = bf_search;
707 
708    st->s.bf.st = bf_st;  bf_st = NULL;
709 
710    if (NULL == (st->s.bf.lower_chars = make_string_array (key_lower, lower_len, &st->s.bf.nlower_chars)))
711      goto return_error;
712 
713    if (NULL == (st->s.bf.upper_chars = make_string_array (key_upper, upper_len, &st->s.bf.nupper_chars)))
714      goto return_error;
715 
716    SLang_free_slstring ((char *)key_upper);
717    SLang_free_slstring ((char *)key_lower);
718    return st;
719 
720    return_error:
721    SLsearch_delete (st);
722    SLsearch_delete (bf_st);
723    SLang_free_slstring ((char *)key_upper);
724    SLang_free_slstring ((char *)key_lower);
725    return NULL;
726 }
727 
728 /* 8bit clean upper and lowercase tables.  These are used _only_ when UTF-8
729  * mode is not active, or when uppercasing ASCII.
730  */
731 unsigned char _pSLChg_LCase_Lut[256] = {0};
732 unsigned char _pSLChg_UCase_Lut[256] = {0};
733 
SLang_define_case(int * u,int * l)734 void SLang_define_case (int *u, int *l)
735 {
736    unsigned char up = (unsigned char) *u, dn = (unsigned char) *l;
737 
738    _pSLChg_LCase_Lut[up] = dn;
739    _pSLChg_LCase_Lut[dn] = dn;
740    _pSLChg_UCase_Lut[dn] = up;
741    _pSLChg_UCase_Lut[up] = up;
742 }
743 
SLang_init_case_tables(void)744 void SLang_init_case_tables (void)
745 {
746    int i;
747    if (Case_Tables_Ok) return;
748 
749    for (i = 0; i < 256; i++)
750      {
751 	_pSLChg_UCase_Lut[i] = i;
752 	_pSLChg_LCase_Lut[i] = i;
753      }
754 
755    for (i = 'A'; i <= 'Z'; i++)
756      {
757 	int j = i + 32;
758 	_pSLChg_UCase_Lut[j] = i;
759 	_pSLChg_LCase_Lut[i] = j;
760      }
761 #ifdef PC_SYSTEM
762    /* Initialize for DOS code page 437. */
763    _pSLChg_UCase_Lut[135] = 128; _pSLChg_LCase_Lut[128] = 135;
764    _pSLChg_UCase_Lut[132] = 142; _pSLChg_LCase_Lut[142] = 132;
765    _pSLChg_UCase_Lut[134] = 143; _pSLChg_LCase_Lut[143] = 134;
766    _pSLChg_UCase_Lut[130] = 144; _pSLChg_LCase_Lut[144] = 130;
767    _pSLChg_UCase_Lut[145] = 146; _pSLChg_LCase_Lut[146] = 145;
768    _pSLChg_UCase_Lut[148] = 153; _pSLChg_LCase_Lut[153] = 148;
769    _pSLChg_UCase_Lut[129] = 154; _pSLChg_LCase_Lut[154] = 129;
770    _pSLChg_UCase_Lut[164] = 165; _pSLChg_LCase_Lut[165] = 164;
771 #else
772    /* ISO Latin */
773    for (i = 192; i <= 221; i++)
774      {
775 	int j = i + 32;
776 	_pSLChg_UCase_Lut[j] = i;
777 	_pSLChg_LCase_Lut[i] = j;
778      }
779    _pSLChg_UCase_Lut[215] = 215; _pSLChg_LCase_Lut[215] = 215;
780    _pSLChg_UCase_Lut[223] = 223; _pSLChg_LCase_Lut[223] = 223;
781    _pSLChg_UCase_Lut[247] = 247; _pSLChg_LCase_Lut[247] = 247;
782    _pSLChg_UCase_Lut[255] = 255; _pSLChg_LCase_Lut[255] = 255;
783 #endif
784    Case_Tables_Ok = 1;
785 }
786