home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume1 / 8708 / 17 < prev    next >
Encoding:
Internet Message Format  |  1990-07-13  |  23.5 KB

  1. From: root@hobbes.UUCP (John Plocher)
  2. Newsgroups: comp.sources.misc
  3. Subject: string compare for 8-bit non-English characters (accents...)
  4. Message-ID: <4194@ncoast.UUCP>
  5. Date: 17 Aug 87 01:06:06 GMT
  6. Sender: allbery@ncoast.UUCP
  7. Organization: U of Wisconsin - Madison  Spanish Department
  8. Lines: 592
  9. Approved: allbery@ncoast.UUCP
  10. X-Archive: comp.sources.misc/8708/17
  11.  
  12. In response to the discussion in comp.std.internat and the amiga group
  13. about how programmers don't take into account non-English character sets
  14. and their properties, I submit the following:  A strcmp() replacement
  15. which correctly handles accented characters and non-ASCII collating.
  16.  
  17.     -John Plocher
  18.  
  19. /* This is NOT a shar */
  20.  
  21. /****************************************************************************
  22.  *
  23.  *    stracmp.c    string compare with accented characters and
  24.  *            non-ASCII collating sequences
  25.  *
  26.  *    Copyright 1985, 1987, 1987 by John Plocher    (plocher@hobbes.UUCP)
  27.  *    May be used in any product as long as this notice is retained and
  28.  *    credit is given.
  29.  *
  30.  ****************************************************************************
  31.  *
  32.  *    Revision Control Information
  33.  *
  34.  *    By:        $Author: plocher $
  35.  *            $Revision: 1.3 $
  36.  *    Last modified:    $Date: 87/08/15 01:32:36 $
  37.  *    Source is in:    $Source: /usr/src/local/biblos/RCS/stracmp.c,v $
  38.  *    Release state:    $State: Usenet $
  39.  *
  40.  *    Library module
  41.  *
  42.  *    Modification Log
  43.  *    ----------------
  44.  *
  45.  *    $Log:    stracmp.c,v $
  46.  *    Revision 1.3  87/08/15  01:32:36  plocher
  47.  *    fixed crt-independent 8-bit character output
  48.  *    
  49.  *    Revision 1.2  87/08/15  01:17:28  plocher
  50.  *    passes lint with no complaints
  51.  *
  52.  *    Revision 1.1  86/04/12
  53.  *    Revision 1.0  85/05/27
  54.  *    
  55.  *
  56.  ****************************************************************************
  57.  *
  58.  *    Compile with
  59.  *
  60.  *        cc -c stracmp.c            # for a library object file
  61.  *          - or -
  62.  *              cc -o stracmp -DMAIN stracmp.c    # for a standalone testbed
  63.  *
  64.  ****************************************************************************
  65.  *
  66.  *    stracmp() implements a string compare which correctly handles
  67.  *    accented (non English) characters which have been encoded using
  68.  *    8-bit characters.  It uses character lookup tables for doing 
  69.  *    string compares when accented characters are present and/or a
  70.  *    non-ASCII collating sequence is desired.
  71.  *
  72.  *    Also, because this is used in bibliographic lookups, this routine
  73.  *    supports the concept of comments within a string.  Everything
  74.  *    between [ and ] (inclusive) is ignored for all comparisons.
  75.  *    Comments may NOT be nested.  Comments are also delimited by
  76.  *    an end of string ('\0'), but that is not the "correct" way.
  77.  *
  78.  *    Reference:
  79.  *
  80.  *    Gibaldi, and Walter S. Achtert.  _MLA_Handbook_for_Writers_of_Research_
  81.  *        Papers_.  New York: Modern Language Association of America, 1984.
  82.  *        Page 76.
  83.  *
  84.  ****************************************************************************
  85.  *
  86.  *    Theory:
  87.  *      The correct way of sorting (or comparing) strings which contain
  88.  *    accented characters is to first compare the strings with all accents
  89.  *    stripped. If the two strings are the same, then and only then are the
  90.  *    accents used.  This second comparison involves only the accents.
  91.  *    You can think of this as comparing the two strings with all the letters
  92.  *    stripped.
  93.  *
  94.  *      Also, there are times when the "normal" ASCII collating sequence is
  95.  *    not appropriate for lexical ordering.  (ie.  A <AE> B C <CEDILLA> D ...>
  96.  *
  97.  ****************************************************************************
  98.  *    Examples:
  99.  *****
  100.  *                 ,  :
  101.  *    Comparing Junta and Junta    (the second word has diacritical
  102.  *                     marks over the two vowels)
  103.  *
  104.  *        first we compare("Junta", "Junta")    which shows them EQUAL
  105.  *    then we must compare("     ", " '  :")
  106.  *
  107.  *                  ,  :
  108.  *    Thus, Junta comes before Junta in the lexical ordering of the two words.
  109.  *
  110.  *****
  111.  *           ,          ,
  112.  *    Comparing Junta  and Junto    (both words have accented 'u's)
  113.  *
  114.  *        first we compare("Junta", "Junto"); since they are
  115.  *    different  we do not need to do anything more with the accents:
  116.  *      ,                    ,
  117.  *    "Junta" is less than "Junto".
  118.  *
  119.  ****************************************************************************
  120.  *
  121.  *    Implementation:
  122.  *
  123.  *    The accented string is broken into two strings:
  124.  *        1) a string of letter values with accents stripped, and
  125.  *        2) a string of accent values with letters stripped.
  126.  *
  127.  *    The comparison is table based in order to speed things up and
  128.  *    allow arbitrary collating sequences.
  129.  *
  130.  *    For a given character x, translate[x] is the "value"
  131.  *    used for sorting with strcmp(), and accent[x]
  132.  *    tells whether the character carries an accent, should 
  133.  *    be ignored, or is a normal character.  If accent[] indicates
  134.  *    that the character carries a diacritical, the value of accent[]
  135.  *    is used to rank the accented character against the same letter
  136.  *    but different diacritics:
  137.  *               ,              .
  138.  *        ie. The letter a differs from a; which is less depends on the
  139.  *    values of accent[].  If the values in accent[] for these two letters
  140.  *      are the same, the accented letters are considered identical.
  141.  *
  142.  *    The stracmp() routine is fully protected against NUL pointers
  143.  *    being passed as parameters,
  144.  *    All internal space needed is taken from the heap with a single malloc()
  145.  *    and free()'d on exit.  The heap space needed is
  146.  *        2 * ( strlen(s1) + strlen(s2) ) + 4
  147.  *    The stack space needed is 3 ints and 4 pointers.
  148.  *    There are two static 256 element arrays of unsigned chars used for
  149.  *    defining the accents and collation sequence.
  150.  *
  151.  *    The runtime time is
  152.  *                   TIME( strlen(s1) )
  153.  *                 + TIME( strcpy(x,s1) ) * K
  154.  *                 + TIME( strlen(s2) )
  155.  *                 + TIME( strcpy(x,s2) ) * K
  156.  *                 + TIME( strcmp(t1,t2) )
  157.  *                 +[TIME( strcmp(a1,a2) )] (* iff needed *)
  158.  *                 + TIME( malloc() )
  159.  *                 + TIME( free() )
  160.  *     where 1 < K < 2
  161.  *
  162.  ****************************************************************************
  163.  */
  164.  
  165. #define VERSION        "$Revision: 1.3 $"
  166. /* #define MAIN            /* compile as a test program, not a library */
  167. /* #define ON_IBMPC        /* iff MAIN is defined does your crt show */
  168.                 /* the IBM character set?          */
  169. #define BRACKET_COMMENTS    /* if defined, stuff within [ ]'s is ignored */
  170.  
  171. #define IBMPC_ROM        /* Tables match the IBM PC ROM tables */
  172. /* #define ISO_LATIN_1        /* Tables for ISO LATIN-1 (ISO 8859-1) */
  173.  
  174. /***************************************************************************/
  175.  
  176. #if defined(IBMPC_ROM) + defined(ISO_LATIN_1) != 1
  177.  
  178.    One and only one of these may be defined.
  179.  
  180. #endif
  181.  
  182.  
  183. #ifdef MAIN
  184. #  include <stdio.h>        /* For confidence test */
  185. #  ifdef ON_IBMPC
  186. #    define PRINT printf
  187. #  else
  188. #    define PRINT crtaccent
  189. #  endif
  190. #endif
  191.  
  192. #ifndef lint
  193.     static char rcsid[] =
  194.       "$Header: stracmp.c,v 1.3 87/08/15 01:32:36 plocher Usenet $";
  195. #endif
  196.  
  197. extern char *malloc();
  198. extern void exit();
  199. extern void free();
  200.  
  201.  
  202. #ifdef IBMPC_ROM
  203.  
  204. /* IBM-PC ROM based character set */
  205.  
  206. /* The translate table maps from a printable character to a "value".   This
  207.  * "value" is used to determine sorting order ( a smaller "value" is less
  208.  * than a larger "value" ).  
  209.  *
  210.  *  Note that in the following table, the letters 'C' and <Cedilla> are
  211.  * both given the same "value".  This is because these two letters are
  212.  * "the same" WHEN ACCENT MARKS ARE IGNORED.  (Same for all other accented
  213.  * characters - they share the same value with the underlying character.)
  214.  *
  215.  *  The table following this has the entry for <Cedilla> flagged as an
  216.  * accent, the entry for 'C' does not.  Therefore, when sorting, a 
  217.  * <Cedilla> will sort with, but following, the entries beginning with 'C'.
  218.  *
  219.  *  The accent table is used solely to differentiate between letters which
  220.  * have the same value in the translate table.  The reasons for two tables
  221.  * instead of one table of shorts are that strcmp() works with char*'s, not
  222.  * short*'s, and that the tables are easier to understand this way.
  223.  *
  224.  * One could also increment the values for 'D'..'~' by 1 and give the value of
  225.  * <Cedilla> as value('C') + 1.  In this case the accent table would not be
  226.  * needed to distinguish between the two.
  227.  */
  228.  
  229. static unsigned char translate[256] = {
  230. /*     0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F  */
  231. /*     -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -  */
  232. /*0*/  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
  233. /*1*/ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
  234. /*2*/ ' ','!',34, '#','$','%','&',39, '(',')','*','+',',','-','.','/',
  235. /*3*/ '0','1','2','3','4','5','6','7','8','9',':',';','<','=','>','?',
  236. /*4*/ '@','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O',
  237. /*5*/ 'P','Q','R','S','T','U','V','W','X','Y','Z','[','\\',']','^','_',
  238. /*6*/ '`','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o',
  239. /*7*/ 'p','q','r','s','t','u','v','w','x','y','z','{','|','}','~',127,
  240.  
  241. /*8*/ 'C','u','e','a','a','a','a','c','e','e','e','i','i','i','A','A',
  242. /*9*/ 'E',145,146,'o','o','o','u','u','y','O','U',155,156,157,158,159,
  243. /*A*/ 'a','i','o','u','n','N','a','o','?',169,170,171,172,'!',174,175,
  244. /*B*/ 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,
  245. /*C*/ 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,
  246. /*D*/ 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,
  247. /*E*/ 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,
  248. /*F*/ 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255
  249. };
  250.  
  251.  
  252. /*
  253.  * for a given character x, accent[x] determines if the
  254.  * character should be ignored (0), or used as given by translate[x] but
  255.  * marked as an accent (1..n).
  256.  *
  257.  * Accents have a sorting order given by the value stored in this table.
  258.  *  (This feature is currently used in the following way:  Accent value=
  259.  *        0    Character is totally ignored in all sorting operations
  260.  *        1    Normal unaccented character (ASCII)
  261.  *        2..n    accents from the extended IBM charset
  262.  */
  263.  
  264. static unsigned char accent[256] = {
  265. /*     0  1  2  3   4  5  6  7      8  9  A  B   C  D  E  F  */
  266. /*     -  -  -  -   -  -  -  -      -  -  -  -   -  -  -  -  */
  267. /*0*/  0, 0, 0, 0,  0, 0, 0, 0,     0, 0, 0, 0,  0, 0, 0, 0, /* control */
  268. /*1*/  0, 0, 0, 0,  0, 0, 0, 0,     0, 0, 0, 0,  0, 0, 0, 0, /* chars   */
  269. /*2*/  1, 1, 1, 1,  1, 1, 1, 1,     1, 1, 1, 1,  1, 1, 1, 1, /* alphanumerics */
  270. /*3*/  1, 1, 1, 1,  1, 1, 1, 1,     1, 1, 1, 1,  1, 1, 1, 1,
  271. /*4*/  1, 1, 1, 1,  1, 1, 1, 1,     1, 1, 1, 1,  1, 1, 1, 1,
  272. /*5*/  1, 1, 1, 1,  1, 1, 1, 1,     1, 1, 1, 1,  1, 1, 1, 1,
  273. /*6*/  1, 1, 1, 1,  1, 1, 1, 1,     1, 1, 1, 1,  1, 1, 1, 1,
  274. /*7*/  1, 1, 1, 1,  1, 1, 1, 1,     1, 1, 1, 1,  1, 1, 1, 0, /* DEL */
  275.  
  276. /*8*/  2, 2, 2, 2,  2, 2, 2, 2,     2, 2, 2, 2,  2, 2, 2, 2, /* accented chars*/
  277. /*9*/  2, 0, 0, 2,  2, 2, 2, 2,     2, 2, 2, 0,  0, 0, 0, 0,
  278. /*A*/  2, 2, 2, 2,  2, 2, 0, 0,     2, 0, 0, 0,  0, 2, 0, 0, /* aeiou ? ! */
  279. /*B*/  0, 0, 0, 0,  0, 0, 0, 0,     0, 0, 0, 0,  0, 0, 0, 0, /* line graphics */
  280. /*C*/  0, 0, 0, 0,  0, 0, 0, 0,     0, 0, 0, 0,  0, 0, 0, 0, /* line graphics */
  281. /*D*/  0, 0, 0, 0,  0, 0, 0, 0,     0, 0, 0, 0,  0, 0, 0, 0, /* line graphics */
  282. /*E*/  0, 0, 0, 0,  0, 0, 0, 0,     0, 0, 0, 0,  0, 0, 0, 0, /* greek */
  283. /*F*/  0, 0, 0, 0,  0, 0, 0, 0,     0, 0, 0, 0,  0, 0, 0, 0  /* math */
  284. };
  285.  
  286. #endif
  287.  
  288. #ifdef ISO_LATIN_1
  289.  
  290. /* ISO Latin-1 character set */
  291.  
  292. /* 
  293.  * Attached is the repertoire of ISO Latin Alphabet Nr 1 (IS 8859-1). I have
  294.  * indicated an alternate name where there might be confusion in the U.S..
  295.  *
  296.  *  List is from  Tim Lasko  Digital Equipment Corporation  Maynard, MA
  297.  * 
  298.  * R/C - row/column of code table
  299.  * Dec - Decimal
  300.  * Oct - Octal
  301.  *
  302.  * R/C  Dec Oct Symbol Name 
  303.  *
  304.  * 02/00 032 040   SP   SPACE
  305.  *  ... same as USASCII ...
  306.  * 07/14 126 176   ~    TILDE
  307.  *  
  308.  * 10/00 160 240  NBSP  NO-BREAK SPACE 
  309.  * 10/01 161 241        INVERTED EXCLAMATION MARK
  310.  * 10/02 162 242        CENT SIGN
  311.  * 10/03 163 243        POUND SIGN
  312.  * 10/04 164 244        CURRENCY SIGN                                
  313.  * 10/05 165 245        YEN SIGN
  314.  * 10/06 166 246        BROKEN BAR                                   
  315.  * 10/07 167 247        PARAGRAPH SIGN, (U.S.) SECTION SIGN 
  316.  * 10/08 168 250        DIERESIS                                    
  317.  * 10/09 169 251        COPYRIGHT SIGN
  318.  * 10/10 170 252        FEMININE ORDINAL INDICATOR
  319.  * 10/11 171 253        LEFT ANGLE QUOTATION MARK
  320.  * 10/12 172 254        NOT SIGN                                     
  321.  * 10/13 173 255   SHY  SOFT HYPHEN                               
  322.  * 10/14 174 256        REGISTERED TRADEMARK SIGN                   
  323.  * 10/15 175 257        MACRON                                       
  324.  *  
  325.  * 11/00 176 260        RING ABOVE, DEGREE SIGN
  326.  * 11/01 177 261        PLUS-MINUS SIGN
  327.  * 11/02 178 262        SUPERSCRIPT TWO
  328.  * 11/03 179 263        SUPERSCRIPT THREE
  329.  * 11/04 180 264        ACUTE ACCENT                                 
  330.  * 11/05 181 265        MICRO SIGN
  331.  * 11/06 182 266        PILCROW SIGN, (U.S.) PARAGRAPH
  332.  * 11/07 183 267        MIDDLE DOT                      
  333.  * 11/08 184 270        CEDILLA
  334.  * 11/09 185 271        SUPERSCRIPT ONE
  335.  * 11/10 186 272        MASCULINE ORDINAL INDICATOR
  336.  * 11/11 187 273        RIGHT ANGLE QUOTATION MARK
  337.  * 11/12 188 274        VULGAR FRACTION ONE QUARTER
  338.  * 11/13 189 275        VULGAR FRACTION ONE HALF
  339.  * 11/14 190 276        VULGAR FRACTION THREE QUARTERS               
  340.  * 11/15 191 277        INVERTED QUESTION MARK
  341.  *  
  342.  * 12/00 192 300        LATIN CAPITAL LETTER A WITH GRAVE ACCENT
  343.  * 12/01 193 301        LATIN CAPITAL LETTER A WITH ACUTE ACCENT
  344.  * 12/02 194 302        LATIN CAPITAL LETTER A WITH CIRCUMFLEX ACCENT
  345.  * 12/03 195 303        LATIN CAPITAL LETTER A WITH TILDE
  346.  * 12/04 196 304        LATIN CAPITAL LETTER A WITH DIAERESIS
  347.  * 12/05 197 305        LATIN CAPITAL LETTER A WITH RING ABOVE
  348.  * 12/06 198 306        CAPITAL DIPHTHONG AE
  349.  * 12/07 199 307        LATIN CAPITAL LETTER C WITH CEDILLA
  350.  * 12/08 200 310        LATIN CAPITAL LETTER E WITH GRAVE ACCENT 
  351.  * 12/09 201 311        LATIN CAPITAL LETTER E WITH ACUTE ACCENT 
  352.  * 12/10 202 312        LATIN CAPITAL LETTER E WITH CIRCUMFLEX ACCENT
  353.  * 12/11 203 313        LATIN CAPITAL LETTER E WITH DIAERESIS
  354.  * 12/12 204 314        LATIN CAPITAL LETTER I WITH GRAVE ACCENT 
  355.  * 12/13 205 315        LATIN CAPITAL LETTER I WITH ACUTE ACCENT 
  356.  * 12/14 206 316        LATIN CAPITAL LETTER I WITH CIRCUMFLEX ACCENT
  357.  * 12/15 207 317        LATIN CAPITAL LETTER I WITH DIAERESIS
  358.  *  
  359.  * 13/00 208 320        CAPITAL ICELANDIC LETTER ETH                 
  360.  * 13/01 209 321        LATIN CAPITAL LETTER N WITH TILDE
  361.  * 13/02 210 322        LATIN CAPITAL LETTER O WITH GRAVE ACCENT 
  362.  * 13/03 211 323        LATIN CAPITAL LETTER O WITH ACUTE ACCENT 
  363.  * 13/04 212 324        LATIN CAPITAL LETTER O WITH CIRCUMFLEX ACCENT
  364.  * 13/05 213 325        LATIN CAPITAL LETTER O WITH TILDE
  365.  * 13/06 214 326        LATIN CAPITAL LETTER O WITH DIAERESIS
  366.  * 13/07 215 327        MULTIPLICATION SIGN                          
  367.  * 13/08 216 330        LATIN CAPITAL LETTER O WITH OBLIQUE STROKE
  368.  * 13/09 217 331        LATIN CAPITAL LETTER U WITH GRAVE ACCENT 
  369.  * 13/10 218 332        LATIN CAPITAL LETTER U WITH ACUTE ACCENT 
  370.  * 13/11 219 333        LATIN CAPITAL LETTER U WITH CIRCUMFLEX
  371.  * 13/12 220 334        LATIN CAPITAL LETTER U WITH DIAERESIS
  372.  * 13/13 221 335        LATIN CAPITAL LETTER Y WITH ACUTE ACCENT  
  373.  * 13/14 222 336        CAPITAL ICELANDIC LETTER THORN               
  374.  * 13/15 223 337        SMALL GERMAN LETTER SHARP s
  375.  *  
  376.  * 14/00 224 340        LATIN SMALL LETTER a WITH GRAVE ACCENT
  377.  * 14/01 225 341        LATIN SMALL LETTER a WITH ACUTE ACCENT
  378.  * 14/02 226 342        LATIN SMALL LETTER a WITH CIRCUMFLEX ACCENT
  379.  * 14/03 227 343        LATIN SMALL LETTER a WITH TILDE
  380.  * 14/04 228 344        LATIN SMALL LETTER a WITH DIAERESIS
  381.  * 14/05 229 345        LATIN SMALL LETTER a WITH RING ABOVE
  382.  * 14/06 230 346        SMALL DIPHTHONG ae
  383.  * 14/07 231 347        LATIN SMALL LETTER c WITH CEDILLA
  384.  * 14/08 232 350        LATIN SMALL LETTER e WITH GRAVE ACCENT
  385.  * 14/09 233 351        LATIN SMALL LETTER e WITH ACUTE ACCENT
  386.  * 14/10 234 352        LATIN SMALL LETTER e WITH CIRCUMFLEX ACCENT
  387.  * 14/11 235 353        LATIN SMALL LETTER e WITH DIAERESIS
  388.  * 14/12 236 354        LATIN SMALL LETTER i WITH GRAVE ACCENT
  389.  * 14/13 237 355        LATIN SMALL LETTER i WITH ACUTE ACCENT
  390.  * 14/14 238 356        LATIN SMALL LETTER i WITH CIRCUMFLEX ACCENT
  391.  * 14/15 239 357        LATIN SMALL LETTER i WITH DIAERESIS
  392.  *  
  393.  * 15/00 240 360        SMALL ICELANDIC LETTER ETH                   
  394.  * 15/01 241 361        LATIN SMALL LETTER n WITH TILDE
  395.  * 15/02 242 362        LATIN SMALL LETTER o WITH GRAVE ACCENT
  396.  * 15/03 243 363        LATIN SMALL LETTER o WITH ACUTE ACCENT
  397.  * 15/04 244 364        LATIN SMALL LETTER o WITH CIRCUMFLEX ACCENT
  398.  * 15/05 245 365        LATIN SMALL LETTER o WITH TILDE
  399.  * 15/06 246 366        LATIN SMALL LETTER o WITH DIAERESIS
  400.  * 15/07 247 367        DIVISION SIGN                                
  401.  * 15/08 248 370        LATIN SMALL LETTER o WITH OBLIQUE STROKE
  402.  * 15/09 249 371        LATIN SMALL LETTER u WITH GRAVE ACCENT
  403.  * 15/10 250 372        LATIN SMALL LETTER u WITH ACUTE ACCENT
  404.  * 15/11 251 373        LATIN SMALL LETTER u WITH CIRCUMFLEX ACCENT
  405.  * 15/12 252 374        LATIN SMALL LETTER u WITH DIAERESIS
  406.  * 15/13 253 375        LATIN SMALL LETTER y WITH ACUTE ACCENT       
  407.  * 15/14 254 376        SMALL ICELANDIC LETTER THORN                 
  408.  * 15/15 255 377        LATIN SMALL LETTER y WITH DIAERESIS          
  409.  */
  410.  
  411.  
  412. unsigned char translate[256] = {
  413. /*     0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F  */
  414. /*     -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -  */
  415. /*0*/  0 ,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',
  416. /*1*/ ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',
  417. /*2*/ ' ','!',34, '#','$','%','&',39, '(',')','*','+',',','-','.','/',
  418. /*3*/ '0','1','2','3','4','5','6','7','8','9',':',';','<','=','>','?',
  419. /*4*/ '@','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O',
  420. /*5*/ 'P','Q','R','S','T','U','V','W','X','Y','Z','[','\\',']','^','_',
  421. /*6*/ '`','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o',
  422. /*7*/ 'p','q','r','s','t','u','v','w','x','y','z','{','|','}','~',127,
  423.  
  424. /*8*/ ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',
  425. /*9*/ ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',
  426. /*A*/ 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,
  427. /*B*/ 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,
  428. /*C*/ 'A','A','A','A','A','A',198,'C','E','E','E','E','I','I','I','I',
  429. /*D*/ 208,'N','O','O','O','O','O',215,'O','U','U','U','U','Y',222,'s',
  430. /*E*/ 'a','a','a','a','a','a',230,'c','e','e','e','e','i','i','i','i',
  431. /*F*/ 240,'n','o','o','o','o','o',247,'o','u','u','u','u','y',254,'y'
  432. };
  433.  
  434. static unsigned char accent[256] = {
  435. /*     0  1  2  3   4  5  6  7      8  9  A  B   C  D  E  F  */
  436. /*     -  -  -  -   -  -  -  -      -  -  -  -   -  -  -  -  */
  437. /*0*/  0, 0, 0, 0,  0, 0, 0, 0,     0, 0, 0, 0,  0, 0, 0, 0, /* control */
  438. /*1*/  0, 0, 0, 0,  0, 0, 0, 0,     0, 0, 0, 0,  0, 0, 0, 0, /* chars   */
  439. /*2*/  1, 1, 1, 1,  1, 1, 1, 1,     1, 1, 1, 1,  1, 1, 1, 1, /* alphanumerics */
  440. /*3*/  1, 1, 1, 1,  1, 1, 1, 1,     1, 1, 1, 1,  1, 1, 1, 1,
  441. /*4*/  1, 1, 1, 1,  1, 1, 1, 1,     1, 1, 1, 1,  1, 1, 1, 1,
  442. /*5*/  1, 1, 1, 1,  1, 1, 1, 1,     1, 1, 1, 1,  1, 1, 1, 1,
  443. /*6*/  1, 1, 1, 1,  1, 1, 1, 1,     1, 1, 1, 1,  1, 1, 1, 1,
  444. /*7*/  1, 1, 1, 1,  1, 1, 1, 1,     1, 1, 1, 1,  1, 1, 1, 0, /* DEL */
  445.  
  446. /*8*/  0, 0, 0, 0,  0, 0, 0, 0,     0, 0, 0, 0,  0, 0, 0, 0, /* control */
  447. /*9*/  0, 0, 0, 0,  0, 0, 0, 0,     0, 0, 0, 0,  0, 0, 0, 0, /* chars   */
  448. /*A*/  0, 0, 0, 0,  0, 0, 0, 0,     0, 0, 0, 0,  0, 0, 0, 0, /* punctuation */
  449. /*B*/  0, 0, 0, 0,  0, 0, 0, 0,     0, 0, 0, 0,  0, 0, 0, 0, /* punctuation */
  450. /*C*/  2, 2, 2, 2,  2, 2, 3, 2,     2, 2, 2, 2,  2, 2, 2, 2, /* A C, E I */
  451. /*D*/  2, 2, 2, 2,  2, 2, 2, 0,     2, 2, 2, 2,  2, 2, 0, 2, /* O N U Y */
  452. /*E*/  2, 2, 2, 2,  2, 2, 3, 2,     2, 2, 2, 2,  2, 2, 2, 2, /* a c, e i */
  453. /*F*/  2, 2, 2, 2,  2, 2, 2, 0,     2, 2, 2, 2,  2, 2, 0, 2  /* o n u y */
  454. };
  455.  
  456. #endif
  457.  
  458. #ifdef BRACKET_COMMENTS
  459. #define REDUCE(ORIG, ACCENTS, ASCII)    {                \
  460.     char *pa, *pt;                            \
  461.     pa = ACCENTS;                            \
  462.     pt = ASCII;                                \
  463.     while (*ORIG) {                            \
  464.     if ( *ORIG == '[' ) {                        \
  465.         while ( *ORIG && *ORIG != ']' )                \
  466.         ORIG++;        /* ignore anything within []'s */    \
  467.         if (*ORIG)                            \
  468.         ORIG++;        /* skip trailing ] */            \
  469.         continue;                            \
  470.     }                                \
  471.     if (accent[ *ORIG ]) {                        \
  472.         *pa++ = accent[ (unsigned)(*ORIG) ];            \
  473.         *pt++ = translate[ (unsigned)(*ORIG) ]; /* set collating seq */ \
  474.     }                                \
  475.     ORIG++;                                \
  476.     }                                    \
  477.     *pa = *pt = '\0';                            \
  478. }
  479. #else
  480. #define REDUCE(ORIG, ACCENTS, ASCII)    {                \
  481.     char *pa, *pt;                            \
  482.     pa = ACCENTS;                            \
  483.     pt = ASCII;                                \
  484.     while (*ORIG) {                            \
  485.     if (accent[ *ORIG ]) {                        \
  486.         *pa++ = accent[ (unsigned)(*ORIG) ];            \
  487.         *pt++ = translate[ (unsigned)(*ORIG) ]; /* set collating seq */ \
  488.     }                                \
  489.     ORIG++;                                \
  490.     }                                    \
  491.     *pa = *pt = '\0';                            \
  492. }
  493. #endif
  494.  
  495. #define MALLOC( pointer, type, size )            \
  496.     pointer = ( type *)malloc((unsigned) size );    \
  497.     if ( pointer == (type *)NULL) {            \
  498.     (void)printf("\n MALLOC returned NULL:  pointer (size)");    \
  499.     exit(0);                    \
  500.     }
  501.  
  502. #define FREE( pointer )        \
  503.     (void)free( (char *)pointer );
  504.  
  505. int stracmp(s1,s2)
  506. unsigned char *s1, *s2;
  507. {
  508.     int value;
  509.     unsigned int i1,  i2;    /* length of given strings */
  510.     char *as1, *as2;        /* accent strings */
  511.     char *ts1, *ts2;        /* strings with accent marks stripped */
  512.  
  513.  
  514.     if (s1 == NULL)            /* cover our ass */
  515.     if (s2 == NULL)
  516.         return 0;        /* NULL == NULL :-) */
  517.     else return -1;        /* NULL < "anything" */
  518.     else if (s2 == NULL)
  519.     return 1;        /* "anything > NULL */
  520.     
  521.     i1 = strlen((char *)s1) + 1;
  522.     i2 = strlen((char *)s2) + 1;
  523.  
  524.     MALLOC(as1, char, 2 * (i1 + i2) + 4);    /* accent chars */
  525.     ts1 = as1 + i1 + 1;
  526.     as2 = ts1 + i1 + 1;
  527.     ts2 = as2 + i2 + 1;
  528.  
  529.     REDUCE( s1, as1, ts1);
  530.     REDUCE( s2, as2, ts2);
  531.     
  532.     if ( (value = strcmp(ts1, ts2) ) ) {
  533.     FREE(as1);
  534.     return( value );        /* strings differ already */
  535.     }
  536.     /*
  537.      *    at this point, ts1 == ts2, and we need to decide if
  538.      *  the accents (if any) break the tie.
  539.      */
  540.     value = strcmp( as1, as2 );
  541.     FREE(as1);
  542.     return value;
  543. }
  544.  
  545.  
  546. #ifdef MAIN
  547.  
  548. #ifndef ON_IBMPC
  549. #include <ctype.h>
  550.  
  551. void crtaccents(s)
  552. unsigned char *s;
  553. {
  554.     while ( s && *s ) {
  555.     switch( *s ) {
  556.         case '\r':    (void)printf("\\r");        break;
  557.         case '\b':    (void)printf("\\b");        break;
  558.         case '\t':    (void)printf("\\t");        break;
  559.         case '\f':    (void)printf("\\f");        break;
  560.         default  :  if (isascii( *s ))
  561.                         (void)putchar(*s);
  562.             else
  563.                         (void)printf("\\%03o",*s);
  564.             break;
  565.     }
  566.     s++;
  567.     }
  568. }
  569. #endif
  570.  
  571. #define COMPARE( check, s1, s2 )    {    \
  572.     (void)PRINT(s1) ;                \
  573.     result = stracmp( (unsigned char *)s1, (unsigned char *)s2 );    \
  574.     if (result < 0) (void)printf(" < ");    \
  575.     else if (result > 0) (void)printf(" > ");    \
  576.     else (void)printf(" = ");            \
  577.     (void)PRINT(s2); (void)putchar('\t');    \
  578.     if (result == check) (void)printf("OK\n");    \
  579.     else (void)printf("WRONG!!!\n");        \
  580. }
  581.  
  582. main() {
  583.     int  result;
  584.     
  585.     (void)printf("stracmp demo - version %s\n", VERSION);
  586.     
  587.     /* These tests assume IBM ROM tables */
  588.  
  589.     COMPARE( 0,"John Plocher", "John Plocher");            /* = */
  590.     COMPARE( 0,"John[ Michael] Plocher[@hobbes.UUCP]", "John Plocher");    /* = */
  591.     COMPARE( 0,"John Plocher", "John[ Michael] Plocher");    /* = */
  592.     COMPARE(-1,"John Plocher", "J\242hn Plocher");        /* < */
  593.     COMPARE( 1,"J\242hn Pl\242cher", "J\242hn Plocher");    /* > */
  594.     COMPARE( 1,"J\242hn P\242lcher", "J\242hn Pl\242cher");    /* > */
  595.     COMPARE( 0,"J\242hn Pl\242cher", "J\242hn Pl\242cher");    /* = */
  596.     COMPARE( 1,"J\242\242n Pl\242cher", "J\242hn Pl\242cher");    /* > */
  597.     return(0);
  598. }
  599.  
  600. #endif
  601.  
  602. -- 
  603. John Plocher uwvax!geowhiz!uwspan!plocher  plocher%uwspan.UUCP@uwvax.CS.WISC.EDU
  604.