home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #19 / NN_1992_19.iso / spool / comp / database / 6516 < prev    next >
Encoding:
Text File  |  1992-09-02  |  30.8 KB  |  1,221 lines

  1. Path: sparky!uunet!europa.asd.contel.com!darwin.sura.net!sgiblab!cs.uoregon.edu!ogicse!das-news.harvard.edu!spdcc!dirtydog.ima.isc.com!keps.kodak.com!cronkite!atar!umak
  2. From: umak@atar.epps.kodak.com (Uma Krishnan)
  3. Newsgroups: comp.databases
  4. Subject: Soundex Algorithm and SQL Parser Available
  5. Keywords: Soundex,SQL Parser
  6. Message-ID: <1992Sep2.142044.9443@APS.Atex.Kodak.COM>
  7. Date: 2 Sep 92 14:20:44 GMT
  8. Article-I.D.: APS.1992Sep2.142044.9443
  9. References: <1992Aug27.182721.27790@cbfsb.cb.att.com> <Btp2Bo.4yC@news.larc.nasa.gov> <mr.715283007@ogre> <1992Aug31.204025.23562@cbfsb.cb.att.com>
  10. Sender: news@APS.Atex.Kodak.COM
  11. Reply-To: umak@atar.Atex.Kodak.COM
  12. Organization: Atex Publishing Systems, Inc.
  13. Lines: 1206
  14.  
  15. Thanks to Ruth Iverson for pointers to SQL parser and to Walt for soundex
  16. algorithm.
  17.  
  18. I am forwarding the responses that I got from them.
  19.  
  20. ====== SQL LEX/YACC PARSER POINTERS ========
  21.  
  22.  
  23. Greetings to all of you who were interested in my findings regarding
  24. a generic public domain SQL interpreter.  Unfortunately, all I was able
  25. to come up with were a couple of fairly old, but still largely ANSI-compatible
  26. examples of lex/yacc grammars for SQL.  I am using these as a guide to
  27. "roll my own" SQL to B-tree interpreter.
  28.  
  29. One of these originated from eris.tar.Z on wilma.cs.brown.edu, which Warwick
  30. was kind enough to point me at [ thanks, Warwick! ].  The other sample
  31. came from /pub/sql.shar on the animal-farm.nevada.edu ftp site.
  32.  
  33.  
  34. There doesn't seem to be much of what we're all looking for out there
  35. in PD land, but I hope this information helps!
  36.  
  37.  
  38. Ruth Iverson                            iverson@nas.nasa.gov
  39. NASA Ames Research Center
  40.  
  41.  
  42.  
  43. ======= SOUNDEX ALGORITHM =======
  44.  
  45. #!/bin/sh
  46. #
  47. # This is a shell archive.  To extract its contents,
  48. # execute this file with /bin/sh to create the file(s):
  49. #
  50. # README            soundex.ec        soundex2b.c        soundex4.c
  51. # mds.globals.h        soundex1.c        soundex3.c        soundex5.c
  52. # soundex.4gl        soundex2a.c
  53. #
  54. # This shell archive created: Wed Feb 19 17:03:20 EST 1992
  55. #
  56. echo "Extracting file README"
  57. sed -e 's/^X//' <<\SHAR_EOF > README
  58. XREADME
  59. X
  60. XThis directory holds files that contain various routines that produce or use
  61. Xthe Soundex string matching code.  The files and their contents are:
  62. X
  63. X
  64. X  Programs and functions written in C
  65. X
  66. X    soundex1.c      Program to display soundex code for one string.
  67. X
  68. X    soundex2a.c     Function written by Jonathan Leffler.  Has #define's to
  69. X                    create a main() for testing.
  70. X
  71. X    soundex2b.c     A more recent version of the function in soundex2a.c.
  72. X
  73. X    soundex3.c      Program with a copy of the function in soundex2a.c that
  74. X                    displays soundex matches of a word from a list of words.
  75. X
  76. X    soundex4.c      Function callable from Informix-4GL.  Doesn't appear to
  77. X                    zero-pad the last soundex character correctly.
  78. X
  79. X    soundex5.c      Another function callable from C.
  80. X
  81. X
  82. X  Function writen in ESQL/C
  83. X
  84. X    soundex.ec      Function to return soundex code
  85. X
  86. X    mds.globals.h   Header file for soundex.ec
  87. X
  88. X
  89. X  Function written in Informix-4GL
  90. X    soundex.4gl     Function to return soundex code
  91. X
  92. X
  93. XThe following people contributed either directly or by referral to these
  94. Xfiles:
  95. X
  96. X    David I. Berg    <infmx!dberg@uunet.uu.net>
  97. X    Neil Briscoe     <nbriscoe@cix.compulink.co.uk>
  98. X    Luis P. Caamano  <ddssuprs!lpc@uunet.uu.net>
  99. X    John Gorman      <semantic!john@uunet.uu.net>
  100. X    Walt Hultgren    <walt@rmy.emory.edu>
  101. X    Jonathan Leffler <johnl@informix.com>
  102. SHAR_EOF
  103. if [ `wc -c < README` -ne     1405 ]
  104. then
  105.     echo "Lengths do not match -- Bad Copy of README"
  106. fi
  107. echo "Extracting file mds.globals.h"
  108. sed -e 's/^X//' <<\SHAR_EOF > mds.globals.h
  109. X/*
  110. X * mds.globals.h    Header file containing structures & definitions 
  111. X *                  needed by ALL of the subroutines for the mds database.
  112. X */
  113. X
  114. X/*
  115. X*
  116. X*   Set Xenix to 1 so SCO xenix precompiler picksup Xenix specific 
  117. X*  routines not Sun OS
  118. X*
  119. X*/
  120. X
  121. X
  122. X
  123. X#include <stdio.h>
  124. X#include <sys/types.h>
  125. X#include <ctype.h>
  126. X
  127. X#define FALSE   0
  128. X#define TRUE    !FALSE
  129. X/* #define  XENIX 1 */
  130. X
  131. X#ifdef XENIX
  132. X#include <sys/ndir.h>
  133. X#else
  134. X#include <sys/dir.h>
  135. X#endif
  136. X
  137. Xextern int errno;
  138. X
  139. X/*  
  140. X * Subroutine Declarations.
  141. X */
  142. X
  143. Xchar    *mktemp(), *strclip(), *strfromdate(), *index(), *getenv(), *malloc();
  144. Xchar *strchr();
  145. Xint strncmp(), int_search(), strlen();
  146. SHAR_EOF
  147. if [ `wc -c < mds.globals.h` -ne      644 ]
  148. then
  149.     echo "Lengths do not match -- Bad Copy of mds.globals.h"
  150. fi
  151. echo "Extracting file soundex.4gl"
  152. sed -e 's/^X//' <<\SHAR_EOF > soundex.4gl
  153. X{   soundex.4gl   Calculate Soundex Code   }
  154. X
  155. X
  156. X{
  157. X   Summary:  Calculate the 4-character Soundex code for a supplied string
  158. X
  159. X   Environment:  SunOS 4.1, Informix-4GL
  160. X
  161. X   Submitted by:  Walt Hultgren <walt@rmy.emory.edu>
  162. X
  163. X
  164. X   This function will return the 4-character Soundex code for the string
  165. X   supplied by the calling routine.  The "char(100)" definition of "str"
  166. X   should be tailored to fit your needs.
  167. X
  168. X}
  169. X
  170. X
  171. Xfunction soundex ( str )
  172. X
  173. X
  174. X    define  str       char(100),  { supplied string }
  175. X            str_leng  smallint,   { length of string }
  176. X            i_str     smallint,   { pointer to string characters }
  177. X
  178. X            s_code    char(4),    { soundex code }
  179. X            i_code    smallint,   { pointer to characters of soundex code }
  180. X
  181. X            char3    char(3),
  182. X            char1    char(1),
  183. X            p_char1  char(1)
  184. X
  185. X
  186. X    {
  187. X       Initilize soundex code and check length of supplied string
  188. X    }
  189. X
  190. X    let s_code = "0000"
  191. X
  192. X    if ( str is NULL ) then  let str_leng = 0
  193. X    else                     let str_leng = length ( str )
  194. X    end if
  195. X
  196. X
  197. X    {
  198. X       Calculate soundex code if string is non-NULL
  199. X    }
  200. X
  201. X    if ( str_leng > 0 )
  202. X    then
  203. X
  204. X        {
  205. X           Step through letters in string
  206. X        }
  207. X
  208. X        let i_code = 0
  209. X
  210. X        for i_str = 1 to str_leng
  211. X
  212. X            let char1 = upshift ( str [ i_str, i_str ] )
  213. X
  214. X            if ( char1 < "A" or char1 > "Z" )
  215. X            then
  216. X                continue for
  217. X            end if
  218. X
  219. X
  220. X            {
  221. X               If first letter, start soundex code with it
  222. X            }
  223. X
  224. X            if ( i_code = 0 )
  225. X            then
  226. X                let s_code[1,1] = char1
  227. X                let i_code      = 1
  228. X                let p_char1     = "0"
  229. X
  230. X                continue for
  231. X            end if
  232. X
  233. X
  234. X            {
  235. X               Get group code for this letter
  236. X            }
  237. X
  238. X            let char3 = "*", char1, "*"
  239. X
  240. X            case
  241. X                when ( "BFPV"    matches char3 )  let char1 = "1"  exit case
  242. X                when ( "CGJKSXZ" matches char3 )  let char1 = "2"  exit case
  243. X                when ( "DT"      matches char3 )  let char1 = "3"  exit case
  244. X                when ( "L"       matches char3 )  let char1 = "4"  exit case
  245. X                when ( "MN"      matches char3 )  let char1 = "5"  exit case
  246. X                when ( "R"       matches char3 )  let char1 = "6"  exit case
  247. X                otherwise                         let char1 = "0"  exit case
  248. X            end case
  249. X
  250. X
  251. X            {
  252. X               If group code is non-zero and not the same as the previous code,
  253. X               append it to soundex code.  Stop after 4 soundex characters.
  254. X            }
  255. X
  256. X            if ( char1 <> p_char1 )
  257. X            then
  258. X
  259. X                if ( char1 <> "0" )
  260. X                then
  261. X
  262. X                    let i_code = i_code + 1
  263. X                    let s_code[i_code,i_code] = char1
  264. X
  265. X                    if ( i_code = 4 )
  266. X                    then
  267. X                        exit for
  268. X                    end if
  269. X
  270. X                end if
  271. X
  272. X                let p_char1 = char1
  273. X
  274. X            end if
  275. X
  276. X        end for
  277. X
  278. X    end if
  279. X
  280. X
  281. X    {
  282. X       Return soundex code to calling routine
  283. X    }
  284. X
  285. X    return s_code
  286. X
  287. X
  288. Xend function
  289. SHAR_EOF
  290. if [ `wc -c < soundex.4gl` -ne     3168 ]
  291. then
  292.     echo "Lengths do not match -- Bad Copy of soundex.4gl"
  293. fi
  294. echo "Extracting file soundex.ec"
  295. sed -e 's/^X//' <<\SHAR_EOF > soundex.ec
  296. X/* I/O Routines for the reporting functions of the mds 4gl database */
  297. X
  298. X#include "mds.globals.h"
  299. X
  300. X$include sqltypes;
  301. X
  302. X#define SURNAMELEN  21
  303. X#define SOUNDEXLEN  5
  304. X#define UPSHIFT     ('a' - 'A')
  305. X
  306. X/*
  307. X * Args :   1st off - string to be `soundex'ed (usually a surname) 
  308. X */
  309. X
  310. Xmake_soundex (nargs)
  311. Xint nargs;
  312. X{
  313. X    char    inputstr[SURNAMELEN];
  314. X    char    workstr[SURNAMELEN];
  315. X    char    longsoundex[SURNAMELEN];
  316. X    char    outputstr[SOUNDEXLEN];
  317. X    char    nullstr[SOUNDEXLEN];
  318. X    char    *inptr, *workptr, *outworkptr, *longsoundptr, *nodupsptr, *outptr;
  319. X    char    ch, oldch;
  320. X
  321. X    rsetnull (CCHARTYPE, nullstr);
  322. X
  323. X    if (nargs != 1) {
  324. X
  325. X        /*
  326. X       * Close the database safely 
  327. X       */
  328. X
  329. X        retquote (nullstr);
  330. X        return (1);
  331. X    }
  332. X
  333. X    /*
  334. X    * Pop input string 
  335. X    */
  336. X
  337. X    popquote (inputstr, SURNAMELEN);
  338. X
  339. X    if ((risnull (inputstr)) || (*inputstr == '\0')) {
  340. X        retquote (nullstr);
  341. X        return (1);
  342. X    }
  343. X    inptr = inputstr;
  344. X    workptr = workstr;
  345. X
  346. X    /*
  347. X    * Remove ALL non alphabetic characters and force to uppercase 
  348. X    */
  349. X
  350. X    for (; ((ch = *inptr) != '\0'); inptr++)
  351. X        if ((ch >= 'A') && (ch <= 'Z'))
  352. X            *workptr++ = ch;
  353. X        else if ((ch >= 'a') && (ch <= 'z'))
  354. X            *workptr++ = (ch - UPSHIFT);
  355. X
  356. X    *workptr = '\0';
  357. X
  358. X    /*
  359. X    * Remove any duplicates at the beginning of the string 
  360. X    */
  361. X
  362. X    for (outworkptr = workptr = workstr, oldch = '\0'; ((ch = *workptr) != '\0'); workptr++) {
  363. X        if (ch != oldch)
  364. X            *outworkptr++ = ch;
  365. X        oldch = ch;
  366. X    }
  367. X
  368. X    *outworkptr = '\0';
  369. X
  370. X    /*
  371. X    * Test whether soundex string has any alphabetic characters in it 
  372. X    */
  373. X
  374. X    if (*workstr == '\0') {
  375. X        retquote (nullstr);
  376. X        return (1);
  377. X    }
  378. X    for (workptr = (workstr + 1), longsoundptr = longsoundex; ((ch = *workptr) != '\0'); workptr++)
  379. X        switch (ch) {
  380. X        case 'B':
  381. X        case 'F':
  382. X        case 'P':
  383. X        case 'V':
  384. X            *longsoundptr++ = '1';
  385. X            break;
  386. X
  387. X        case 'C':
  388. X        case 'G':
  389. X        case 'J':
  390. X        case 'K':
  391. X        case 'Q':
  392. X        case 'S':
  393. X        case 'X':
  394. X        case 'Z':
  395. X            *longsoundptr++ = '2';
  396. X            break;
  397. X
  398. X        case 'D':
  399. X        case 'T':
  400. X            *longsoundptr++ = '3';
  401. X            break;
  402. X
  403. X        case 'L':
  404. X            *longsoundptr++ = '4';
  405. X            break;
  406. X
  407. X        case 'M':
  408. X        case 'N':
  409. X            *longsoundptr++ = '5';
  410. X            break;
  411. X
  412. X        case 'R':
  413. X            *longsoundptr++ = '6';
  414. X            break;
  415. X        }
  416. X
  417. X    *longsoundptr = '\0';
  418. X
  419. X    /*
  420. X    * Remove any duplicates. eg. "11234453" --> "123453" 
  421. X    */
  422. X
  423. X    for (longsoundptr = nodupsptr = longsoundex, oldch = '0'; ((ch = *longsoundptr) != '\0'); longsoundptr++) {
  424. X        if (ch != oldch)
  425. X            *nodupsptr++ = ch;
  426. X        oldch = ch;
  427. X    }
  428. X
  429. X    *nodupsptr = '\0';
  430. X
  431. X    /*
  432. X    * Copy 1st character from upshifted original and then upto 3 digits from the longsoundex 
  433. X    */
  434. X
  435. X    outputstr[0] = *workstr;
  436. X
  437. X    for (outptr = (outputstr + 1), longsoundptr = longsoundex; (((ch = *longsoundptr) != '\0') && (longsoundptr <= (longsoundex +
  438. X        3))); longsoundptr++)
  439. X        *outptr++ = ch;
  440. X
  441. X    *outptr = '\0';
  442. X
  443. X
  444. X    retquote (outputstr);
  445. X    return (1);
  446. X}
  447. X
  448. X
  449. SHAR_EOF
  450. if [ `wc -c < soundex.ec` -ne     3234 ]
  451. then
  452.     echo "Lengths do not match -- Bad Copy of soundex.ec"
  453. fi
  454. echo "Extracting file soundex1.c"
  455. sed -e 's/^X//' <<\SHAR_EOF > soundex1.c
  456. X/***
  457. X* SOUNDEX ALGORITHM in C                                        *
  458. X*                                                               *
  459. X* The basic Algorithm source is taken from EDN Nov.             *
  460. X* 14, 1985 pg. 36.                                              *
  461. X*                                                               *
  462. X* As a test Those in Illinois will find that the                *
  463. X* first group of numbers in their drivers license               *
  464. X* number is the soundex number for their last name.             *
  465. X*                                                               *
  466. X* RHW  PC-IBBS ID. #1230                                        *
  467. X*                                                               *
  468. X****************************************************************/
  469. X
  470. X#include <ctype.h>
  471. X
  472. Xchar (*soundex(out_pntr, in_pntr))
  473. Xchar *in_pntr;
  474. Xchar *out_pntr;
  475. X{
  476. Xextern char get_scode();
  477. Xchar ch,last_ch;
  478. Xint count = 0;
  479. X
  480. X        strcpy(out_pntr,"0000");        /* Pre-fill output string for    */
  481. X                                        /* error and trailing zeros.     */
  482. X        *out_pntr = toupper(*in_pntr);  /* Copy first letter             */
  483. X        last_ch = get_scode(*in_pntr);  /* code of the first letter      */
  484. X                                        /* for the first 'double-letter  */
  485. X                                        /* check.                        */
  486. X                                        /* Loop on input letters until   */
  487. X                                        /* end of input (null) or output */
  488. X                                        /* letter code count = 3         */
  489. X
  490. X        while( (ch = get_scode(*(++in_pntr)) ) && (count < 3) )
  491. X        {
  492. X        if( (ch != '0') && (ch != last_ch) ) /* if not skipped or double */
  493. X                *(out_pntr+(++count)) = ch;  /* letter, copy to output   */
  494. X                last_ch = ch;      /* save code of last input letter for */
  495. X                                   /* next double-letter check           */
  496. X        }
  497. X        return(out_pntr); /* pointer to input string */
  498. X}
  499. X
  500. Xchar get_scode(ch)
  501. Xchar ch;
  502. X{
  503. X                            /* ABCDEFGHIJKLMNOPQRSTUVWXYZ */
  504. X                            /* :::::::::::::::::::::::::: */
  505. Xstatic char soundex_map[] =   "01230120022455012623010202";
  506. X
  507. X        /* If alpha, map input letter to soundex code. If not, return 0 */
  508. X
  509. X        if( !isalpha(ch) )  /*error if not alpha */
  510. X                return(0);
  511. X        else
  512. X                return(soundex_map[(toupper(ch) - 'A')] );
  513. X}
  514. X
  515. Xmain(argc, argv)
  516. Xint   argc;
  517. Xchar    *argv[];
  518. X{
  519. Xchar    *code[10];
  520. X
  521. Xint   i;
  522. X
  523. X        if(argc == 1) /* No arguments, give usage */
  524. X        {
  525. X        printf("\nUsage: soundex (name) (...)\n");
  526. X        exit(1);
  527. X        }
  528. X
  529. X
  530. X        for(i = 1; i < argc; i++)
  531. X        {
  532. X        soundex(code, argv[i]) ;
  533. X
  534. X        printf("The Soundex Code for \"%s\" is: %s\n", argv[i],code);
  535. X        }
  536. X
  537. X        exit(0);
  538. X}
  539. SHAR_EOF
  540. if [ `wc -c < soundex1.c` -ne     2918 ]
  541. then
  542.     echo "Lengths do not match -- Bad Copy of soundex1.c"
  543. fi
  544. echo "Extracting file soundex2a.c"
  545. sed -e 's/^X//' <<\SHAR_EOF > soundex2a.c
  546. X/*
  547. X**      SOUNDEX CODING
  548. X**
  549. X**      Rules:
  550. X**      1.      Retain the first letter; ignore non-alphabetic characters.
  551. X**      2.      Replace second and subsequent characters by a group code.
  552. X**              Group   Letters
  553. X**              1               BFPV
  554. X**              2               CGJKSXZ
  555. X**              3               DT
  556. X**              4               L
  557. X**              5               MN
  558. X**              6               R
  559. X**      3.      Do not repeat digits
  560. X**      4.      Truncate or ser-pad to 4-character result.
  561. X**
  562. X**      Originally formatted with tabstops set at 4 spaces -- you were
  563. Xwarned!
  564. X**
  565. X**      Code by: Jonathan Leffler (john@sphinx.co.uk)
  566. X**      This code is shareware -- I wrote it; you can have it for free
  567. X**      if you supply it to anyone else who wants it for free.
  568. X**
  569. X**      BUGS: Assumes ASCII
  570. X*/
  571. X
  572. X#include <ctype.h>
  573. Xstatic char     lookup[] = {
  574. X    '0',    /* A */
  575. X    '1',    /* B */
  576. X    '2',    /* C */
  577. X    '3',    /* D */
  578. X    '0',    /* E */
  579. X    '1',    /* F */
  580. X    '2',    /* G */
  581. X    '0',    /* H */
  582. X    '0',    /* I */
  583. X    '2',    /* J */
  584. X    '2',    /* K */
  585. X    '4',    /* L */
  586. X    '5',    /* M */
  587. X    '5',    /* N */
  588. X    '0',    /* O */
  589. X    '1',    /* P */
  590. X    '0',    /* Q */
  591. X    '6',    /* R */
  592. X    '2',    /* S */
  593. X    '3',    /* T */
  594. X    '0',    /* U */
  595. X    '1',    /* V */
  596. X    '0',    /* W */
  597. X    '2',    /* X */
  598. X    '0',    /* Y */
  599. X    '2',    /* Z */
  600. X};
  601. X
  602. X/*
  603. X**      Soundex for arbitrary number of characters of information
  604. X*/
  605. Xchar    *nsoundex(str, n)
  606. Xchar    *str;   /* In: String to be converted */
  607. Xint              n;             /* In: Number of characters in result string
  608. X*/
  609. X{
  610. X    static  char    buff[10];
  611. X    register char   *s;
  612. X    register char   *t;
  613. X    char    c;
  614. X    char    l;
  615. X
  616. X    if (n <= 0)
  617. X        n = 4;  /* Default */
  618. X    if (n > sizeof(buff) - 1)
  619. X        n = sizeof(buff) - 1;
  620. X    t = &buff[0];
  621. X
  622. X    for (s = str; ((c = *s) != '\0') && t < &buff[n]; s++)
  623. X    {
  624. X        if (!isascii(c))
  625. X            continue;
  626. X        if (!isalpha(c))
  627. X            continue;
  628. X        c = toupper(c);
  629. X        if (t == &buff[0])
  630. X        {
  631. X            l = *t++ = c;
  632. X            continue;
  633. X        }
  634. X        c = lookup[c-'A'];
  635. X        if (c != '0' && c != l)
  636. X            l = *t++ = c;
  637. X    }
  638. X    while (t < &buff[n])
  639. X        *t++ = '0';
  640. X    *t = '\0';
  641. X    return(&buff[0]);
  642. X}
  643. X
  644. X/* Normal external interface */
  645. Xchar    *soundex(str)
  646. Xchar    *str;
  647. X{
  648. X    return(nsoundex(str, 9));
  649. X}
  650. X
  651. X/*
  652. X**      Alternative interface:
  653. X**      void    soundex(given, gets)
  654. X**      char    *given;
  655. X**      char    *gets;
  656. X**      {
  657. X**              strcpy(gets, nsoundex(given, 4));
  658. X**      }
  659. X*/
  660. X
  661. X
  662. X#ifdef TEST
  663. X#include <stdio.h>
  664. Xmain()
  665. X{
  666. X    char    buff[30];
  667. X
  668. X    while (fgets(buff, sizeof(buff), stdin) != (char *)0)
  669. X        printf("Given: %s Soundex produces %s\n", buff,
  670. X        soundex(buff));
  671. X}
  672. X#endif
  673. SHAR_EOF
  674. if [ `wc -c < soundex2a.c` -ne     2839 ]
  675. then
  676.     echo "Lengths do not match -- Bad Copy of soundex2a.c"
  677. fi
  678. echo "Extracting file soundex2b.c"
  679. sed -e 's/^X//' <<\SHAR_EOF > soundex2b.c
  680. X/*
  681. X@(#)File:            soundex.c
  682. X@(#)Version:         1.2
  683. X@(#)Last changed:    89/12/18
  684. X@(#)Purpose:         Produce SOUNDEX code for string
  685. X@(#)Author:          Jonathan Leffler (john@sphinx.co.uk)
  686. X*/
  687. X
  688. X/*
  689. X**  SOUNDEX CODING
  690. X**
  691. X**  Rules:
  692. X**  1.  Retain the first letter; ignore non-alphabetic characters.
  693. X**  2.  Replace second and subsequent characters by a group code.
  694. X**      Group   Letters
  695. X**      1       BFPV
  696. X**      2       CGJKSXZ
  697. X**      3       DT
  698. X**      4       L
  699. X**      5       MN
  700. X**      6       R
  701. X**  3.  Do not repeat digits if they come from adjacent characters.
  702. X**      (Corrected by: Raymond Chen <reading!bosco.berkeley.edu!raymond>)
  703. X**  4.  Truncate or zero-pad to 4-character result.
  704. X**
  705. X**  Originally formatted with tabstops set at 4 spaces -- you were warned!
  706. X**
  707. X**  This code is shareware -- I wrote it; you can have it for free
  708. X**  if you supply it to anyone else who wants it for free.
  709. X**
  710. X**  BUGS: Assumes ASCII
  711. X*/
  712. X
  713. X#include <ctype.h>
  714. X
  715. X#ifndef lint
  716. Xstatic  char    sccs[] = "@(#)soundex.c 1.2 89/12/18";
  717. X#endif
  718. X
  719. Xstatic char lookup[] = {
  720. X    '0',    /* A */
  721. X    '1',    /* B */
  722. X    '2',    /* C */
  723. X    '3',    /* D */
  724. X    '0',    /* E */
  725. X    '1',    /* F */
  726. X    '2',    /* G */
  727. X    '0',    /* H */
  728. X    '0',    /* I */
  729. X    '2',    /* J */
  730. X    '2',    /* K */
  731. X    '4',    /* L */
  732. X    '5',    /* M */
  733. X    '5',    /* N */
  734. X    '0',    /* O */
  735. X    '1',    /* P */
  736. X    '0',    /* Q */
  737. X    '6',    /* R */
  738. X    '2',    /* S */
  739. X    '3',    /* T */
  740. X    '0',    /* U */
  741. X    '1',    /* V */
  742. X    '0',    /* W */
  743. X    '2',    /* X */
  744. X    '0',    /* Y */
  745. X    '2',    /* Z */
  746. X};
  747. X
  748. X/*
  749. X**  Soundex for arbitrary number of characters of information
  750. X*/
  751. Xchar    *nsoundex(str, n)
  752. Xchar    *str;   /* In: String to be converted */
  753. Xint      n;     /* In: Number of characters in result string */
  754. X{
  755. X    static  char    buff[10];
  756. X    register char   *s;
  757. X    register char   *t;
  758. X    char    c;
  759. X    char    l;
  760. X
  761. X    if (n <= 0)
  762. X        n = 4;  /* Default */
  763. X    if (n > sizeof(buff) - 1)
  764. X        n = sizeof(buff) - 1;
  765. X    t = &buff[0];
  766. X
  767. X    for (s = str; ((c = *s) != '\0') && t < &buff[n]; s++)
  768. X    {
  769. X        if (!isascii(c) || !isalpha(c))
  770. X            continue;
  771. X        c = toupper(c);
  772. X        if (t == &buff[0])
  773. X        {
  774. X            l = *t++ = c;
  775. X            continue;
  776. X        }
  777. X        c = lookup[c-'A'];  /* Assumes ASCII */
  778. X        if (c != '0' && c != l)
  779. X            *t++ = c;
  780. X        l = c;
  781. X    }
  782. X    while (t < &buff[n])
  783. X        *t++ = '0';
  784. X    *t = '\0';
  785. X    return(&buff[0]);
  786. X}
  787. X
  788. X/* Normal external interface */
  789. Xchar    *soundex(str)
  790. Xchar    *str;
  791. X{
  792. X    return(nsoundex(str, 4));
  793. X}
  794. X
  795. X/*
  796. X**  Alternative interface:
  797. X**  void    soundex(given, gets)
  798. X**  char    *given;
  799. X**  char    *gets;
  800. X**  {
  801. X**      strcpy(gets, nsoundex(given, 4));
  802. X**  }
  803. X*/
  804. X
  805. X
  806. X#ifdef TEST
  807. X#include <stdio.h>
  808. Xmain()
  809. X{
  810. X    char    buff[30];
  811. X
  812. X    printf("String? ");
  813. X    while (fgets(buff, sizeof(buff), stdin) != (char *)0)
  814. X    {
  815. X        printf("String : %sSoundex: %s\n", buff, soundex(buff));
  816. X        printf("String? ");
  817. X    }
  818. X    putchar('\n');
  819. X}
  820. X#endif
  821. SHAR_EOF
  822. if [ `wc -c < soundex2b.c` -ne     3032 ]
  823. then
  824.     echo "Lengths do not match -- Bad Copy of soundex2b.c"
  825. fi
  826. echo "Extracting file soundex3.c"
  827. sed -e 's/^X//' <<\SHAR_EOF > soundex3.c
  828. X#include <stdio.h>
  829. X#include <ctype.h>
  830. X
  831. X#define TRUE    1
  832. X#define FALSE   0
  833. X
  834. X#define DEFAULT_DICT    "/usr/dict/words"
  835. X#define PATTERN_SIZE    6
  836. X
  837. X#define my_toupper(x)   (islower(x) ? toupper(x) : (x))
  838. X
  839. Xstatic char     lookup[] = {
  840. X    '0',    /* A */
  841. X    '1',    /* B */
  842. X    '2',    /* C */
  843. X    '3',    /* D */
  844. X    '0',    /* E */
  845. X    '1',    /* F */
  846. X    '2',    /* G */
  847. X    '0',    /* H */
  848. X    '0',    /* I */
  849. X    '2',    /* J */
  850. X    '2',    /* K */
  851. X    '4',    /* L */
  852. X    '5',    /* M */
  853. X    '5',    /* N */
  854. X    '0',    /* O */
  855. X    '1',    /* P */
  856. X    '0',    /* Q */
  857. X    '6',    /* R */
  858. X    '2',    /* S */
  859. X    '3',    /* T */
  860. X    '0',    /* U */
  861. X    '1',    /* V */
  862. X    '0',    /* W */
  863. X    '2',    /* X */
  864. X    '0',    /* Y */
  865. X    '2',    /* Z */
  866. X};
  867. X
  868. Xchar    *soundex();
  869. X
  870. X
  871. Xmain (argc, argv)
  872. Xint argc;
  873. Xchar *argv[];
  874. X{
  875. X    int count;
  876. X    char pattern[PATTERN_SIZE];
  877. X    FILE *fp, *fopen();
  878. X
  879. X    if (argc < 2)
  880. X    {
  881. X        fprintf (stderr, "Usage: %s word [ - | wordlist ...]\n",
  882. X        argv[0]);
  883. X        fprintf (stderr, "       use \"-\" to read from stdin.\n");
  884. X        exit (1);
  885. X    }
  886. X
  887. X    strcpy (pattern, soundex(argv[1]));
  888. X
  889. X    if (argc == 2)          /* use default dictionary */
  890. X    {
  891. X        if ((fp = fopen (DEFAULT_DICT, "r")) == NULL)
  892. X        {
  893. X            fprintf (stderr, "%s: Cannot open %s for reading\n",
  894. X            argv[0], DEFAULT_DICT);
  895. X        }
  896. X        else
  897. X        {
  898. X            match (fp, pattern);
  899. X            fclose(fp);
  900. X        }
  901. X    }
  902. X    else                    /* use specified dictionaries */
  903. X    {
  904. X        for (count = 2 ; count < argc ; count++)
  905. X        {
  906. X            if (strcmp (argv[count], "-") == 0)
  907. X            {
  908. X                match (stdin, pattern);
  909. X            }
  910. X            else if ((fp = fopen (argv[count], "r")) == NULL)
  911. X            {
  912. X                fprintf (stderr, "%s: Cannot open %s for reading\n",
  913. X                         argv[0], argv[count]);
  914. X            }
  915. X            else
  916. X            {
  917. X                match (fp, pattern);
  918. X                fclose(fp);
  919. X            }
  920. X        }
  921. X    }
  922. X    exit (0);
  923. X}
  924. X
  925. X/************************************************************************
  926. X/*
  927. X/*
  928. X/************************************************************************/
  929. X
  930. Xint match (fp, pattern)
  931. Xregister FILE *fp;
  932. Xregister char *pattern;
  933. X{
  934. X    char    word[BUFSIZ];
  935. X    register char *wordp = &word[0];
  936. X
  937. X    /* read all words before our stuff */
  938. X    while (fgets(wordp, BUFSIZ - 1,fp) != NULL && *pattern != my_toupper
  939. X    (*wordp))
  940. X    ;
  941. X
  942. X    if (wordp == NULL)
  943. X        return (FALSE);
  944. X
  945. X    word[strlen(word) - 1] = '\0';          /* remove the \n */
  946. X    if (the_same (pattern, soundex(wordp)))
  947. X    {
  948. X        puts (word);
  949. X    }
  950. X
  951. X    while (fgets(wordp, BUFSIZ - 1,fp) != NULL)
  952. X    {
  953. X        if (*pattern != my_toupper (*wordp))    /* give it up */
  954. X        {
  955. X            break;
  956. X        }
  957. X        word[strlen(word) - 1] = '\0';          /* remove the \n */
  958. X        if (the_same (pattern, soundex(wordp)))
  959. X        {
  960. X            puts (word);
  961. X        }
  962. X    }
  963. X    return (TRUE);
  964. X}
  965. X
  966. X
  967. X
  968. X/************************************************************************
  969. X/*
  970. X/*
  971. X/************************************************************************/
  972. X
  973. Xint the_same (str1, str2)
  974. Xregister char *str1;
  975. Xregister char *str2;
  976. X{
  977. X    while (*(str1++) == *(str2++))
  978. X        if (*str1 == '\0')
  979. X            return (TRUE);
  980. X    return (FALSE);
  981. X}
  982. X
  983. X
  984. X/*
  985. X**      SOUNDEX CODING
  986. X**
  987. X**      Rules:
  988. X**      1.      Retain the first letter; ignore non-alphabetic characters.
  989. X**      2.      Replace second and subsequent characters by a group code.
  990. X**              Group   Letters
  991. X**              1               BFPV
  992. X**              2               CGJKSXZ
  993. X**              3               DT
  994. X**              4               L
  995. X**              5               MN
  996. X**              6               R
  997. X**      3.      Do not repeat digits
  998. X**      4.      Truncate or ser-pad to 4-character result.
  999. X**
  1000. X**      Originally formatted with tabstops set at 4 spaces -- you were
  1001. Xwarned!
  1002. X**
  1003. X**      Code by: Jonathan Leffler (john@sphinx.co.uk)
  1004. X**      This code is shareware -- I wrote it; you can have it for free
  1005. X**      if you supply it to anyone else who wants it for free.
  1006. X**
  1007. X**      BUGS: Assumes ASCII
  1008. X*/
  1009. X
  1010. X/*
  1011. X**      Soundex for arbitrary number of characters of information
  1012. X*/
  1013. Xchar    *soundex(str)
  1014. Xchar    *str;   /* In: String to be converted */
  1015. X{
  1016. X    static  char    buff[PATTERN_SIZE +1];
  1017. X    register char   *s;
  1018. X    register char   *t;
  1019. X    char    c;
  1020. X    char    l;
  1021. X
  1022. X    t = &buff[0];
  1023. X
  1024. X    for (s = str; ((c = *s) != '\0') && t < &buff[PATTERN_SIZE]; s++)
  1025. X    {
  1026. X        if (!isascii(c))
  1027. X            continue;
  1028. X        if (!isalpha(c))
  1029. X            continue;
  1030. X        if (islower(c))
  1031. X            c = toupper(c);
  1032. X        if (t == &buff[0])
  1033. X        {
  1034. X            l = *t++ = c;
  1035. X            continue;
  1036. X        }
  1037. X        c = lookup[c-'A'];
  1038. X        if (c != '0' && c != l)
  1039. X            l = *t++ = c;
  1040. X    }
  1041. X    while (t < &buff[PATTERN_SIZE])
  1042. X        *t++ = '0';
  1043. X    *t = '\0';
  1044. X    return(&buff[0]);
  1045. X}
  1046. SHAR_EOF
  1047. if [ `wc -c < soundex3.c` -ne     4996 ]
  1048. then
  1049.     echo "Lengths do not match -- Bad Copy of soundex3.c"
  1050. fi
  1051. echo "Extracting file soundex4.c"
  1052. sed -e 's/^X//' <<\SHAR_EOF > soundex4.c
  1053. X/***
  1054. X*
  1055. X*   soundex()
  1056. X*   adopted for 4gl
  1057. X*
  1058. X**/
  1059. X#include "stdio.h"
  1060. X#include "ctype.h"
  1061. X#define MAX_DIGITS  4   /** number of digits in code sequence **/
  1062. X
  1063. Xstatic char omit_letter[]   ={"AEHIOUWY"};
  1064. Xstatic char *code_group[] = 
  1065. X{
  1066. X    "BFPV", "CGJKQSXZ", "DT", "L", "MN", "R", NULL 
  1067. X};
  1068. X/*
  1069. XIf one wants to test this stand alone than remove the comments
  1070. X
  1071. X#define popquote(a,b) strcpy(a,str)
  1072. X#define pushquote(a) strcpy(rstr,a)
  1073. Xchar str[100];
  1074. Xchar rstr[100];
  1075. Xmain()
  1076. X{
  1077. X    while(gets(str))
  1078. X    {
  1079. X        if(strcmp (str,"end") == 0) break;
  1080. X        soundex(1);
  1081. X        *str = 0;
  1082. X        printf("code = %s for name %s\n",rstr,str);
  1083. X    }
  1084. X}
  1085. X*/
  1086. Xsoundex(args)
  1087. Xint args;
  1088. X{
  1089. X    char name_for_soundex[100];
  1090. X    char soundex[MAX_DIGITS];
  1091. X    char *p;
  1092. X    int k,i,j;
  1093. X
  1094. X    popquote(name_for_soundex,sizeof(name_for_soundex));
  1095. X
  1096. X    /**
  1097. X       first character not translated. But shouldn't we make sure
  1098. X       it is an alpha? In case a funny user enters "   Smith";
  1099. X    **/
  1100. X    for(i = j = 0; !isalpha(name_for_soundex[i]);i++)
  1101. X        ;
  1102. X
  1103. X    soundex[j++] = toupper(name_for_soundex[i]);
  1104. X
  1105. X    for (i++;j < MAX_DIGITS && name_for_soundex[i];i++)
  1106. X    {
  1107. X        /**
  1108. X           Only alpha's are allowed why this should return an error
  1109. X           if the letter isn't an alpha I don't know.
  1110. X           it would prohibit names like D'artagne. Who was 
  1111. X           one of the three musketeers
  1112. X        **/
  1113. X        if (isalpha(name_for_soundex[i]))
  1114. X        {
  1115. X            name_for_soundex[i] = toupper(name_for_soundex[i]);
  1116. X            if (strchr(omit_letter,name_for_soundex[i]) == NULL)
  1117. X            {
  1118. X                for(k = 0; code_group[k];k++)
  1119. X                {
  1120. X                    if(strchr(code_group[k],name_for_soundex[i]))
  1121. X                        soundex[j++] = (k + 1) + 48; 
  1122. X                }
  1123. X            }
  1124. X        }
  1125. X    }
  1126. X
  1127. X    /** fill string if neccesary **/
  1128. X    while (j < MAX_DIGITS)
  1129. X    {
  1130. X        soundex[j++] = '0';
  1131. X    }
  1132. X    soundex[j] = '\0';        
  1133. X    pushquote(soundex);
  1134. X    return(args);
  1135. X}
  1136. SHAR_EOF
  1137. if [ `wc -c < soundex4.c` -ne     1980 ]
  1138. then
  1139.     echo "Lengths do not match -- Bad Copy of soundex4.c"
  1140. fi
  1141. echo "Extracting file soundex5.c"
  1142. sed -e 's/^X//' <<\SHAR_EOF > soundex5.c
  1143. X/*
  1144. X * Reference: Adapted from Knuth, D.E. (1973) The art of computer programming;
  1145. X *    Volume 3: Sorting and searching.  Addison-Wesley Publishing Company:
  1146. X *    Reading, Mass. Page 392.
  1147. X *
  1148. X * 1. Retain the first letter of the name, and drop all occurrences of
  1149. X *    a, e, h, i, o, u, w, y in other positions.
  1150. X *
  1151. X * 2. Assign the following numbers to the remaining letters after the first:
  1152. X *      b, f, p, v -> 1                         l -> 4
  1153. X *      c, g, j, k, q, s, x, z -> 2             m, n -> 5
  1154. X *      d, t -> 3                               r -> 6
  1155. X *
  1156. X * 3. If two or more letters with the same code were adjacent in the original
  1157. X *    name (before step 1), omit all but the first.
  1158. X *
  1159. X * 4. Convert to the form ``letter, digit, digit, digit'' by adding trailing
  1160. X *    zeros (if there are less than three digits), or by dropping rightmost
  1161. X *    digits (if there are more than three).
  1162. X *
  1163. X * The examples given in the book are:
  1164. X *
  1165. X *      Euler, Ellery           E460
  1166. X *      Gauss, Ghosh            G200
  1167. X *      Hilbert, Heilbronn      H416
  1168. X *      Knuth, Kant             K530
  1169. X *      Lloyd, Ladd             L300
  1170. X *      Lukasiewicz, Lissajous  L222
  1171. X *
  1172. X * Most algorithms fail in two ways:
  1173. X *  1. they omit adjacent letters with the same code AFTER step 1, not before.
  1174. X *  2. they do not omit adjacent letters with the same code at the beginning
  1175. X *     of the name.
  1176. X *
  1177. X */
  1178. X
  1179. X#include <stdio.h>
  1180. X#include <ctype.h>
  1181. X
  1182. X#define SDXLEN  4
  1183. X
  1184. Xchar *soundex(name)
  1185. Xchar *name;
  1186. X{
  1187. Xstatic   char   buf[SDXLEN+1];
  1188. Xstatic   char   map[] = "01230120022455012623010202";
  1189. Xregister char   mc, uc, pc = '0';
  1190. Xregister int    idx;
  1191. X
  1192. Xstrcpy(buf,"Z000");
  1193. X
  1194. Xfor (idx = 0; *name && idx < SDXLEN; name++)
  1195. X    if (isalpha(*name)) {
  1196. X        uc = toupper(*name);
  1197. X        mc = map[uc-'A'];
  1198. X        if (idx == 0 || (mc != '0' && mc != pc)) {
  1199. X            buf[idx] = idx ? mc : uc;
  1200. X            idx++;
  1201. X            }
  1202. X        pc = mc;
  1203. X        }
  1204. Xreturn(buf);
  1205. X}
  1206. SHAR_EOF
  1207. if [ `wc -c < soundex5.c` -ne     1928 ]
  1208. then
  1209.     echo "Lengths do not match -- Bad Copy of soundex5.c"
  1210. fi
  1211. echo "Done."
  1212.  
  1213. ------------------------------------------------------------------------------
  1214.  
  1215. -- 
  1216. Walt Hultgren              Internet: walt@rmy.emory.edu       (IP 128.140.8.1)
  1217. Emory University               UUCP: {...,gatech,rutgers,uunet}!emory!rmy!walt
  1218. 954 Gatewood Road, NE        BITNET: walt@EMORY
  1219. Atlanta, GA  30329  USA       Voice: +1 404 727 0648
  1220.