xref: /PHP-7.4/ext/standard/soundex.c (revision 92ac598a)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 7                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) The PHP Group                                          |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 3.01 of the PHP license,      |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available through the world-wide-web at the following url:           |
10    | http://www.php.net/license/3_01.txt                                  |
11    | If you did not receive a copy of the PHP license and are unable to   |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@php.net so we can mail you a copy immediately.               |
14    +----------------------------------------------------------------------+
15    | Author: Bjørn Borud - Guardian Networks AS <borud@guardian.no>       |
16    +----------------------------------------------------------------------+
17  */
18 
19 #include "php.h"
20 #include <stdlib.h>
21 #include <errno.h>
22 #include <ctype.h>
23 #include "php_string.h"
24 
25 /* Simple soundex algorithm as described by Knuth in TAOCP, vol 3 */
26 /* {{{ proto string soundex(string str)
27    Calculate the soundex key of a string */
PHP_FUNCTION(soundex)28 PHP_FUNCTION(soundex)
29 {
30 	char	*str;
31 	size_t	i, _small, str_len, code, last;
32 	char	soundex[4 + 1];
33 
34 	static char soundex_table[26] =
35 	{0,							/* A */
36 	 '1',						/* B */
37 	 '2',						/* C */
38 	 '3',						/* D */
39 	 0,							/* E */
40 	 '1',						/* F */
41 	 '2',						/* G */
42 	 0,							/* H */
43 	 0,							/* I */
44 	 '2',						/* J */
45 	 '2',						/* K */
46 	 '4',						/* L */
47 	 '5',						/* M */
48 	 '5',						/* N */
49 	 0,							/* O */
50 	 '1',						/* P */
51 	 '2',						/* Q */
52 	 '6',						/* R */
53 	 '2',						/* S */
54 	 '3',						/* T */
55 	 0,							/* U */
56 	 '1',						/* V */
57 	 0,							/* W */
58 	 '2',						/* X */
59 	 0,							/* Y */
60 	 '2'};						/* Z */
61 
62 	ZEND_PARSE_PARAMETERS_START(1, 1)
63 		Z_PARAM_STRING(str, str_len)
64 	ZEND_PARSE_PARAMETERS_END();
65 
66 	if (str_len == 0) {
67 		RETURN_FALSE;
68 	}
69 
70 	/* build soundex string */
71 	last = -1;
72 	for (i = 0, _small = 0; i < str_len && _small < 4; i++) {
73 		/* convert chars to upper case and strip non-letter chars */
74 		/* BUG: should also map here accented letters used in non */
75 		/* English words or names (also found in English text!): */
76 		/* esstsett, thorn, n-tilde, c-cedilla, s-caron, ... */
77 		code = toupper((int)(unsigned char)str[i]);
78 		if (code >= 'A' && code <= 'Z') {
79 			if (_small == 0) {
80 				/* remember first valid char */
81 				soundex[_small++] = (char)code;
82 				last = soundex_table[code - 'A'];
83 			}
84 			else {
85 				/* ignore sequences of consonants with same soundex */
86 				/* code in trail, and vowels unless they separate */
87 				/* consonant letters */
88 				code = soundex_table[code - 'A'];
89 				if (code != last) {
90 					if (code != 0) {
91 						soundex[_small++] = (char)code;
92 					}
93 					last = code;
94 				}
95 			}
96 		}
97 	}
98 	/* pad with '0' and terminate with 0 ;-) */
99 	while (_small < 4) {
100 		soundex[_small++] = '0';
101 	}
102 	soundex[_small] = '\0';
103 
104 	RETURN_STRINGL(soundex, _small);
105 }
106 /* }}} */
107