PostgreSQL Source Code git master
Loading...
Searching...
No Matches
dmetaphone.c File Reference
#include "postgres.h"
#include "utils/builtins.h"
#include "utils/formatting.h"
#include <assert.h>
#include <ctype.h>
Include dependency graph for dmetaphone.c:

Go to the source code of this file.

Data Structures

struct  metastring
 

Macros

#define NDEBUG
 
#define META_MALLOC(v, n, t)    (v = (t*)palloc(((n)*sizeof(t))))
 
#define META_REALLOC(v, n, t)    (v = (t*)repalloc((v),((n)*sizeof(t))))
 
#define META_FREE(x)   ((void)true) /* pfree((x)) */
 

Functions

static void DoubleMetaphone (const char *str, Oid collid, char **codes)
 
 PG_FUNCTION_INFO_V1 (dmetaphone)
 
Datum dmetaphone (PG_FUNCTION_ARGS)
 
 PG_FUNCTION_INFO_V1 (dmetaphone_alt)
 
Datum dmetaphone_alt (PG_FUNCTION_ARGS)
 
static metastringNewMetaString (const char *init_str)
 
static void DestroyMetaString (metastring *s)
 
static void IncreaseBuffer (metastring *s, int chars_needed)
 
static metastringMakeUpper (metastring *s, Oid collid)
 
static int IsVowel (metastring *s, int pos)
 
static int SlavoGermanic (metastring *s)
 
static char GetAt (metastring *s, int pos)
 
static void SetAt (metastring *s, int pos, char c)
 
static int StringAt (metastring *s, int start, int length,...)
 
static void MetaphAdd (metastring *s, const char *new_str)
 

Macro Definition Documentation

◆ META_FREE

#define META_FREE (   x)    ((void)true) /* pfree((x)) */

Definition at line 201 of file dmetaphone.c.

◆ META_MALLOC

#define META_MALLOC (   v,
  n,
 
)     (v = (t*)palloc(((n)*sizeof(t))))

Definition at line 188 of file dmetaphone.c.

219{
220 char *str;
221 int length;
222 int bufsize;
223 int free_string_on_destroy;
224}
225
227
228/*
229 * remaining perl module funcs unchanged except for declaring them static
230 * and reformatting to PostgreSQL indentation and to fit in 80 cols.
231 *
232 */
233
234static metastring *
235NewMetaString(const char *init_str)
236{
237 metastring *s;
238 char empty_string[] = "";
239
240 META_MALLOC(s, 1, metastring);
241 assert(s != NULL);
242
243 if (init_str == NULL)
245 s->length = strlen(init_str);
246 /* preallocate a bit more for potential growth */
247 s->bufsize = s->length + 7;
248
249 META_MALLOC(s->str, s->bufsize, char);
250 assert(s->str != NULL);
251
252 memcpy(s->str, init_str, s->length + 1);
254
255 return s;
256}
257
258
259static void
261{
262 if (s == NULL)
263 return;
264
265 if (s->free_string_on_destroy && (s->str != NULL))
266 META_FREE(s->str);
267
268 META_FREE(s);
269}
270
271
272static void
274{
275 META_REALLOC(s->str, (s->bufsize + chars_needed + 10), char);
276 assert(s->str != NULL);
277 s->bufsize = s->bufsize + chars_needed + 10;
278}
279
280
281static metastring *
283{
284 char *newstr;
286
290
291 return newms;
292}
293
294
295static int
296IsVowel(metastring *s, int pos)
297{
298 char c;
299
300 if ((pos < 0) || (pos >= s->length))
301 return 0;
302
303 c = *(s->str + pos);
304 if ((c == 'A') || (c == 'E') || (c == 'I') || (c == 'O') ||
305 (c == 'U') || (c == 'Y'))
306 return 1;
307
308 return 0;
309}
310
311
312static int
314{
315 if (strstr(s->str, "W"))
316 return 1;
317 else if (strstr(s->str, "K"))
318 return 1;
319 else if (strstr(s->str, "CZ"))
320 return 1;
321 else if (strstr(s->str, "WITZ"))
322 return 1;
323 else
324 return 0;
325}
326
327
328static char
329GetAt(metastring *s, int pos)
330{
331 if ((pos < 0) || (pos >= s->length))
332 return '\0';
333
334 return *(s->str + pos);
335}
336
337
338static void
339SetAt(metastring *s, int pos, char c)
340{
341 if ((pos < 0) || (pos >= s->length))
342 return;
343
344 *(s->str + pos) = c;
345}
346
347
348/*
349 Caveats: the START value is 0 based
350*/
351static int
352StringAt(metastring *s, int start, int length,...)
353{
354 char *test;
355 char *pos;
356 va_list ap;
357
358 if ((start < 0) || (start >= s->length))
359 return 0;
360
361 pos = (s->str + start);
362 va_start(ap, length);
363
364 do
365 {
366 test = va_arg(ap, char *);
367 if (*test && (strncmp(pos, test, length) == 0))
368 {
369 va_end(ap);
370 return 1;
371 }
372 }
373 while (strcmp(test, "") != 0);
374
375 va_end(ap);
376
377 return 0;
378}
379
380
381static void
382MetaphAdd(metastring *s, const char *new_str)
383{
384 int add_length;
385
386 if (new_str == NULL)
387 return;
388
390 if ((s->length + add_length) > (s->bufsize - 1))
392
393 strcat(s->str, new_str);
394 s->length += add_length;
395}
396
397
398static void
399DoubleMetaphone(const char *str, Oid collid, char **codes)
400{
401 int length;
402 metastring *original;
403 metastring *primary;
405 int current;
406 int last;
407
408 current = 0;
409 /* we need the real length and last prior to padding */
410 length = strlen(str);
411 last = length - 1;
412 original = NewMetaString(str);
413 /* Pad original so we can index beyond end */
414 MetaphAdd(original, " ");
415
416 primary = NewMetaString("");
418 primary->free_string_on_destroy = 0;
419 secondary->free_string_on_destroy = 0;
420
421 original = MakeUpper(original, collid);
422
423 /* skip these when at start of word */
424 if (StringAt(original, 0, 2, "GN", "KN", "PN", "WR", "PS", ""))
425 current += 1;
426
427 /* Initial 'X' is pronounced 'Z' e.g. 'Xavier' */
428 if (GetAt(original, 0) == 'X')
429 {
430 MetaphAdd(primary, "S"); /* 'Z' maps to 'S' */
431 MetaphAdd(secondary, "S");
432 current += 1;
433 }
434
435 /* main loop */
436 while ((primary->length < 4) || (secondary->length < 4))
437 {
438 if (current >= length)
439 break;
440
441 switch (GetAt(original, current))
442 {
443 case 'A':
444 case 'E':
445 case 'I':
446 case 'O':
447 case 'U':
448 case 'Y':
449 if (current == 0)
450 {
451 /* all init vowels now map to 'A' */
452 MetaphAdd(primary, "A");
453 MetaphAdd(secondary, "A");
454 }
455 current += 1;
456 break;
457
458 case 'B':
459
460 /* "-mb", e.g", "dumb", already skipped over... */
461 MetaphAdd(primary, "P");
462 MetaphAdd(secondary, "P");
463
464 if (GetAt(original, current + 1) == 'B')
465 current += 2;
466 else
467 current += 1;
468 break;
469
470 case '\xc7': /* C with cedilla */
471 MetaphAdd(primary, "S");
472 MetaphAdd(secondary, "S");
473 current += 1;
474 break;
475
476 case 'C':
477 /* various germanic */
478 if ((current > 1)
479 && !IsVowel(original, current - 2)
480 && StringAt(original, (current - 1), 3, "ACH", "")
481 && ((GetAt(original, current + 2) != 'I')
482 && ((GetAt(original, current + 2) != 'E')
483 || StringAt(original, (current - 2), 6, "BACHER",
484 "MACHER", ""))))
485 {
486 MetaphAdd(primary, "K");
487 MetaphAdd(secondary, "K");
488 current += 2;
489 break;
490 }
491
492 /* special case 'caesar' */
493 if ((current == 0)
494 && StringAt(original, current, 6, "CAESAR", ""))
495 {
496 MetaphAdd(primary, "S");
497 MetaphAdd(secondary, "S");
498 current += 2;
499 break;
500 }
501
502 /* italian 'chianti' */
503 if (StringAt(original, current, 4, "CHIA", ""))
504 {
505 MetaphAdd(primary, "K");
506 MetaphAdd(secondary, "K");
507 current += 2;
508 break;
509 }
510
511 if (StringAt(original, current, 2, "CH", ""))
512 {
513 /* find 'michael' */
514 if ((current > 0)
515 && StringAt(original, current, 4, "CHAE", ""))
516 {
517 MetaphAdd(primary, "K");
518 MetaphAdd(secondary, "X");
519 current += 2;
520 break;
521 }
522
523 /* greek roots e.g. 'chemistry', 'chorus' */
524 if ((current == 0)
525 && (StringAt(original, (current + 1), 5,
526 "HARAC", "HARIS", "")
527 || StringAt(original, (current + 1), 3, "HOR",
528 "HYM", "HIA", "HEM", ""))
529 && !StringAt(original, 0, 5, "CHORE", ""))
530 {
531 MetaphAdd(primary, "K");
532 MetaphAdd(secondary, "K");
533 current += 2;
534 break;
535 }
536
537 /* germanic, greek, or otherwise 'ch' for 'kh' sound */
538 if ((StringAt(original, 0, 4, "VAN ", "VON ", "")
539 || StringAt(original, 0, 3, "SCH", ""))
540 /* 'architect but not 'arch', 'orchestra', 'orchid' */
541 || StringAt(original, (current - 2), 6, "ORCHES",
542 "ARCHIT", "ORCHID", "")
543 || StringAt(original, (current + 2), 1, "T", "S",
544 "")
545 || ((StringAt(original, (current - 1), 1,
546 "A", "O", "U", "E", "")
547 || (current == 0))
548
549 /*
550 * e.g., 'wachtler', 'wechsler', but not 'tichner'
551 */
552 && StringAt(original, (current + 2), 1, "L", "R",
553 "N", "M", "B", "H", "F", "V", "W",
554 " ", "")))
555 {
556 MetaphAdd(primary, "K");
557 MetaphAdd(secondary, "K");
558 }
559 else
560 {
561 if (current > 0)
562 {
563 if (StringAt(original, 0, 2, "MC", ""))
564 {
565 /* e.g., "McHugh" */
566 MetaphAdd(primary, "K");
567 MetaphAdd(secondary, "K");
568 }
569 else
570 {
571 MetaphAdd(primary, "X");
572 MetaphAdd(secondary, "K");
573 }
574 }
575 else
576 {
577 MetaphAdd(primary, "X");
578 MetaphAdd(secondary, "X");
579 }
580 }
581 current += 2;
582 break;
583 }
584 /* e.g, 'czerny' */
585 if (StringAt(original, current, 2, "CZ", "")
586 && !StringAt(original, (current - 2), 4, "WICZ", ""))
587 {
588 MetaphAdd(primary, "S");
589 MetaphAdd(secondary, "X");
590 current += 2;
591 break;
592 }
593
594 /* e.g., 'focaccia' */
595 if (StringAt(original, (current + 1), 3, "CIA", ""))
596 {
597 MetaphAdd(primary, "X");
598 MetaphAdd(secondary, "X");
599 current += 3;
600 break;
601 }
602
603 /* double 'C', but not if e.g. 'McClellan' */
604 if (StringAt(original, current, 2, "CC", "")
605 && !((current == 1) && (GetAt(original, 0) == 'M')))
606 {
607 /* 'bellocchio' but not 'bacchus' */
608 if (StringAt(original, (current + 2), 1, "I", "E", "H", "")
609 && !StringAt(original, (current + 2), 2, "HU", ""))
610 {
611 /* 'accident', 'accede' 'succeed' */
612 if (((current == 1)
613 && (GetAt(original, current - 1) == 'A'))
614 || StringAt(original, (current - 1), 5, "UCCEE",
615 "UCCES", ""))
616 {
617 MetaphAdd(primary, "KS");
618 MetaphAdd(secondary, "KS");
619 /* 'bacci', 'bertucci', other italian */
620 }
621 else
622 {
623 MetaphAdd(primary, "X");
624 MetaphAdd(secondary, "X");
625 }
626 current += 3;
627 break;
628 }
629 else
630 { /* Pierce's rule */
631 MetaphAdd(primary, "K");
632 MetaphAdd(secondary, "K");
633 current += 2;
634 break;
635 }
636 }
637
638 if (StringAt(original, current, 2, "CK", "CG", "CQ", ""))
639 {
640 MetaphAdd(primary, "K");
641 MetaphAdd(secondary, "K");
642 current += 2;
643 break;
644 }
645
646 if (StringAt(original, current, 2, "CI", "CE", "CY", ""))
647 {
648 /* italian vs. english */
649 if (StringAt
650 (original, current, 3, "CIO", "CIE", "CIA", ""))
651 {
652 MetaphAdd(primary, "S");
653 MetaphAdd(secondary, "X");
654 }
655 else
656 {
657 MetaphAdd(primary, "S");
658 MetaphAdd(secondary, "S");
659 }
660 current += 2;
661 break;
662 }
663
664 /* else */
665 MetaphAdd(primary, "K");
666 MetaphAdd(secondary, "K");
667
668 /* name sent in 'mac caffrey', 'mac gregor */
669 if (StringAt(original, (current + 1), 2, " C", " Q", " G", ""))
670 current += 3;
671 else if (StringAt(original, (current + 1), 1, "C", "K", "Q", "")
672 && !StringAt(original, (current + 1), 2,
673 "CE", "CI", ""))
674 current += 2;
675 else
676 current += 1;
677 break;
678
679 case 'D':
680 if (StringAt(original, current, 2, "DG", ""))
681 {
682 if (StringAt(original, (current + 2), 1,
683 "I", "E", "Y", ""))
684 {
685 /* e.g. 'edge' */
686 MetaphAdd(primary, "J");
687 MetaphAdd(secondary, "J");
688 current += 3;
689 break;
690 }
691 else
692 {
693 /* e.g. 'edgar' */
694 MetaphAdd(primary, "TK");
695 MetaphAdd(secondary, "TK");
696 current += 2;
697 break;
698 }
699 }
700
701 if (StringAt(original, current, 2, "DT", "DD", ""))
702 {
703 MetaphAdd(primary, "T");
704 MetaphAdd(secondary, "T");
705 current += 2;
706 break;
707 }
708
709 /* else */
710 MetaphAdd(primary, "T");
711 MetaphAdd(secondary, "T");
712 current += 1;
713 break;
714
715 case 'F':
716 if (GetAt(original, current + 1) == 'F')
717 current += 2;
718 else
719 current += 1;
720 MetaphAdd(primary, "F");
721 MetaphAdd(secondary, "F");
722 break;
723
724 case 'G':
725 if (GetAt(original, current + 1) == 'H')
726 {
727 if ((current > 0) && !IsVowel(original, current - 1))
728 {
729 MetaphAdd(primary, "K");
730 MetaphAdd(secondary, "K");
731 current += 2;
732 break;
733 }
734
735 if (current < 3)
736 {
737 /* 'ghislane', ghiradelli */
738 if (current == 0)
739 {
740 if (GetAt(original, current + 2) == 'I')
741 {
742 MetaphAdd(primary, "J");
743 MetaphAdd(secondary, "J");
744 }
745 else
746 {
747 MetaphAdd(primary, "K");
748 MetaphAdd(secondary, "K");
749 }
750 current += 2;
751 break;
752 }
753 }
754
755 /*
756 * Parker's rule (with some further refinements) - e.g.,
757 * 'hugh'
758 */
759 if (((current > 1)
760 && StringAt(original, (current - 2), 1,
761 "B", "H", "D", ""))
762 /* e.g., 'bough' */
763 || ((current > 2)
764 && StringAt(original, (current - 3), 1,
765 "B", "H", "D", ""))
766 /* e.g., 'broughton' */
767 || ((current > 3)
768 && StringAt(original, (current - 4), 1,
769 "B", "H", "")))
770 {
771 current += 2;
772 break;
773 }
774 else
775 {
776 /*
777 * e.g., 'laugh', 'McLaughlin', 'cough', 'gough',
778 * 'rough', 'tough'
779 */
780 if ((current > 2)
781 && (GetAt(original, current - 1) == 'U')
782 && StringAt(original, (current - 3), 1, "C",
783 "G", "L", "R", "T", ""))
784 {
785 MetaphAdd(primary, "F");
786 MetaphAdd(secondary, "F");
787 }
788 else if ((current > 0)
789 && GetAt(original, current - 1) != 'I')
790 {
791
792
793 MetaphAdd(primary, "K");
794 MetaphAdd(secondary, "K");
795 }
796
797 current += 2;
798 break;
799 }
800 }
801
802 if (GetAt(original, current + 1) == 'N')
803 {
804 if ((current == 1) && IsVowel(original, 0)
805 && !SlavoGermanic(original))
806 {
807 MetaphAdd(primary, "KN");
808 MetaphAdd(secondary, "N");
809 }
810 else
811 /* not e.g. 'cagney' */
812 if (!StringAt(original, (current + 2), 2, "EY", "")
813 && (GetAt(original, current + 1) != 'Y')
814 && !SlavoGermanic(original))
815 {
816 MetaphAdd(primary, "N");
817 MetaphAdd(secondary, "KN");
818 }
819 else
820 {
821 MetaphAdd(primary, "KN");
822 MetaphAdd(secondary, "KN");
823 }
824 current += 2;
825 break;
826 }
827
828 /* 'tagliaro' */
829 if (StringAt(original, (current + 1), 2, "LI", "")
830 && !SlavoGermanic(original))
831 {
832 MetaphAdd(primary, "KL");
833 MetaphAdd(secondary, "L");
834 current += 2;
835 break;
836 }
837
838 /* -ges-,-gep-,-gel-, -gie- at beginning */
839 if ((current == 0)
840 && ((GetAt(original, current + 1) == 'Y')
841 || StringAt(original, (current + 1), 2, "ES", "EP",
842 "EB", "EL", "EY", "IB", "IL", "IN", "IE",
843 "EI", "ER", "")))
844 {
845 MetaphAdd(primary, "K");
846 MetaphAdd(secondary, "J");
847 current += 2;
848 break;
849 }
850
851 /* -ger-, -gy- */
852 if ((StringAt(original, (current + 1), 2, "ER", "")
853 || (GetAt(original, current + 1) == 'Y'))
854 && !StringAt(original, 0, 6,
855 "DANGER", "RANGER", "MANGER", "")
856 && !StringAt(original, (current - 1), 1, "E", "I", "")
857 && !StringAt(original, (current - 1), 3, "RGY", "OGY", ""))
858 {
859 MetaphAdd(primary, "K");
860 MetaphAdd(secondary, "J");
861 current += 2;
862 break;
863 }
864
865 /* italian e.g, 'biaggi' */
866 if (StringAt(original, (current + 1), 1, "E", "I", "Y", "")
867 || StringAt(original, (current - 1), 4,
868 "AGGI", "OGGI", ""))
869 {
870 /* obvious germanic */
871 if ((StringAt(original, 0, 4, "VAN ", "VON ", "")
872 || StringAt(original, 0, 3, "SCH", ""))
873 || StringAt(original, (current + 1), 2, "ET", ""))
874 {
875 MetaphAdd(primary, "K");
876 MetaphAdd(secondary, "K");
877 }
878 else
879 {
880 /* always soft if french ending */
881 if (StringAt
882 (original, (current + 1), 4, "IER ", ""))
883 {
884 MetaphAdd(primary, "J");
885 MetaphAdd(secondary, "J");
886 }
887 else
888 {
889 MetaphAdd(primary, "J");
890 MetaphAdd(secondary, "K");
891 }
892 }
893 current += 2;
894 break;
895 }
896
897 if (GetAt(original, current + 1) == 'G')
898 current += 2;
899 else
900 current += 1;
901 MetaphAdd(primary, "K");
902 MetaphAdd(secondary, "K");
903 break;
904
905 case 'H':
906 /* only keep if first & before vowel or btw. 2 vowels */
907 if (((current == 0) || IsVowel(original, current - 1))
908 && IsVowel(original, current + 1))
909 {
910 MetaphAdd(primary, "H");
911 MetaphAdd(secondary, "H");
912 current += 2;
913 }
914 else
915 /* also takes care of 'HH' */
916 current += 1;
917 break;
918
919 case 'J':
920 /* obvious spanish, 'jose', 'san jacinto' */
921 if (StringAt(original, current, 4, "JOSE", "")
922 || StringAt(original, 0, 4, "SAN ", ""))
923 {
924 if (((current == 0)
925 && (GetAt(original, current + 4) == ' '))
926 || StringAt(original, 0, 4, "SAN ", ""))
927 {
928 MetaphAdd(primary, "H");
929 MetaphAdd(secondary, "H");
930 }
931 else
932 {
933 MetaphAdd(primary, "J");
934 MetaphAdd(secondary, "H");
935 }
936 current += 1;
937 break;
938 }
939
940 if ((current == 0)
941 && !StringAt(original, current, 4, "JOSE", ""))
942 {
943 MetaphAdd(primary, "J"); /* Yankelovich/Jankelowicz */
944 MetaphAdd(secondary, "A");
945 }
946 else
947 {
948 /* spanish pron. of e.g. 'bajador' */
949 if (IsVowel(original, current - 1)
950 && !SlavoGermanic(original)
951 && ((GetAt(original, current + 1) == 'A')
952 || (GetAt(original, current + 1) == 'O')))
953 {
954 MetaphAdd(primary, "J");
955 MetaphAdd(secondary, "H");
956 }
957 else
958 {
959 if (current == last)
960 {
961 MetaphAdd(primary, "J");
962 MetaphAdd(secondary, "");
963 }
964 else
965 {
966 if (!StringAt(original, (current + 1), 1, "L", "T",
967 "K", "S", "N", "M", "B", "Z", "")
968 && !StringAt(original, (current - 1), 1,
969 "S", "K", "L", ""))
970 {
971 MetaphAdd(primary, "J");
972 MetaphAdd(secondary, "J");
973 }
974 }
975 }
976 }
977
978 if (GetAt(original, current + 1) == 'J') /* it could happen! */
979 current += 2;
980 else
981 current += 1;
982 break;
983
984 case 'K':
985 if (GetAt(original, current + 1) == 'K')
986 current += 2;
987 else
988 current += 1;
989 MetaphAdd(primary, "K");
990 MetaphAdd(secondary, "K");
991 break;
992
993 case 'L':
994 if (GetAt(original, current + 1) == 'L')
995 {
996 /* spanish e.g. 'cabrillo', 'gallegos' */
997 if (((current == (length - 3))
998 && StringAt(original, (current - 1), 4, "ILLO",
999 "ILLA", "ALLE", ""))
1000 || ((StringAt(original, (last - 1), 2, "AS", "OS", "")
1001 || StringAt(original, last, 1, "A", "O", ""))
1002 && StringAt(original, (current - 1), 4,
1003 "ALLE", "")))
1004 {
1005 MetaphAdd(primary, "L");
1006 MetaphAdd(secondary, "");
1007 current += 2;
1008 break;
1009 }
1010 current += 2;
1011 }
1012 else
1013 current += 1;
1014 MetaphAdd(primary, "L");
1015 MetaphAdd(secondary, "L");
1016 break;
1017
1018 case 'M':
1019 if ((StringAt(original, (current - 1), 3, "UMB", "")
1020 && (((current + 1) == last)
1021 || StringAt(original, (current + 2), 2, "ER", "")))
1022 /* 'dumb','thumb' */
1023 || (GetAt(original, current + 1) == 'M'))
1024 current += 2;
1025 else
1026 current += 1;
1027 MetaphAdd(primary, "M");
1028 MetaphAdd(secondary, "M");
1029 break;
1030
1031 case 'N':
1032 if (GetAt(original, current + 1) == 'N')
1033 current += 2;
1034 else
1035 current += 1;
1036 MetaphAdd(primary, "N");
1037 MetaphAdd(secondary, "N");
1038 break;
1039
1040 case '\xd1': /* N with tilde */
1041 current += 1;
1042 MetaphAdd(primary, "N");
1043 MetaphAdd(secondary, "N");
1044 break;
1045
1046 case 'P':
1047 if (GetAt(original, current + 1) == 'H')
1048 {
1049 MetaphAdd(primary, "F");
1050 MetaphAdd(secondary, "F");
1051 current += 2;
1052 break;
1053 }
1054
1055 /* also account for "campbell", "raspberry" */
1056 if (StringAt(original, (current + 1), 1, "P", "B", ""))
1057 current += 2;
1058 else
1059 current += 1;
1060 MetaphAdd(primary, "P");
1061 MetaphAdd(secondary, "P");
1062 break;
1063
1064 case 'Q':
1065 if (GetAt(original, current + 1) == 'Q')
1066 current += 2;
1067 else
1068 current += 1;
1069 MetaphAdd(primary, "K");
1070 MetaphAdd(secondary, "K");
1071 break;
1072
1073 case 'R':
1074 /* french e.g. 'rogier', but exclude 'hochmeier' */
1075 if ((current == last)
1076 && !SlavoGermanic(original)
1077 && StringAt(original, (current - 2), 2, "IE", "")
1078 && !StringAt(original, (current - 4), 2, "ME", "MA", ""))
1079 {
1080 MetaphAdd(primary, "");
1081 MetaphAdd(secondary, "R");
1082 }
1083 else
1084 {
1085 MetaphAdd(primary, "R");
1086 MetaphAdd(secondary, "R");
1087 }
1088
1089 if (GetAt(original, current + 1) == 'R')
1090 current += 2;
1091 else
1092 current += 1;
1093 break;
1094
1095 case 'S':
1096 /* special cases 'island', 'isle', 'carlisle', 'carlysle' */
1097 if (StringAt(original, (current - 1), 3, "ISL", "YSL", ""))
1098 {
1099 current += 1;
1100 break;
1101 }
1102
1103 /* special case 'sugar-' */
1104 if ((current == 0)
1105 && StringAt(original, current, 5, "SUGAR", ""))
1106 {
1107 MetaphAdd(primary, "X");
1108 MetaphAdd(secondary, "S");
1109 current += 1;
1110 break;
1111 }
1112
1113 if (StringAt(original, current, 2, "SH", ""))
1114 {
1115 /* germanic */
1116 if (StringAt
1117 (original, (current + 1), 4, "HEIM", "HOEK", "HOLM",
1118 "HOLZ", ""))
1119 {
1120 MetaphAdd(primary, "S");
1121 MetaphAdd(secondary, "S");
1122 }
1123 else
1124 {
1125 MetaphAdd(primary, "X");
1126 MetaphAdd(secondary, "X");
1127 }
1128 current += 2;
1129 break;
1130 }
1131
1132 /* italian & armenian */
1133 if (StringAt(original, current, 3, "SIO", "SIA", "")
1134 || StringAt(original, current, 4, "SIAN", ""))
1135 {
1136 if (!SlavoGermanic(original))
1137 {
1138 MetaphAdd(primary, "S");
1139 MetaphAdd(secondary, "X");
1140 }
1141 else
1142 {
1143 MetaphAdd(primary, "S");
1144 MetaphAdd(secondary, "S");
1145 }
1146 current += 3;
1147 break;
1148 }
1149
1150 /*
1151 * german & anglicisations, e.g. 'smith' match 'schmidt',
1152 * 'snider' match 'schneider' also, -sz- in slavic language
1153 * although in hungarian it is pronounced 's'
1154 */
1155 if (((current == 0)
1156 && StringAt(original, (current + 1), 1,
1157 "M", "N", "L", "W", ""))
1158 || StringAt(original, (current + 1), 1, "Z", ""))
1159 {
1160 MetaphAdd(primary, "S");
1161 MetaphAdd(secondary, "X");
1162 if (StringAt(original, (current + 1), 1, "Z", ""))
1163 current += 2;
1164 else
1165 current += 1;
1166 break;
1167 }
1168
1169 if (StringAt(original, current, 2, "SC", ""))
1170 {
1171 /* Schlesinger's rule */
1172 if (GetAt(original, current + 2) == 'H')
1173 {
1174 /* dutch origin, e.g. 'school', 'schooner' */
1175 if (StringAt(original, (current + 3), 2,
1176 "OO", "ER", "EN",
1177 "UY", "ED", "EM", ""))
1178 {
1179 /* 'schermerhorn', 'schenker' */
1180 if (StringAt(original, (current + 3), 2,
1181 "ER", "EN", ""))
1182 {
1183 MetaphAdd(primary, "X");
1184 MetaphAdd(secondary, "SK");
1185 }
1186 else
1187 {
1188 MetaphAdd(primary, "SK");
1189 MetaphAdd(secondary, "SK");
1190 }
1191 current += 3;
1192 break;
1193 }
1194 else
1195 {
1196 if ((current == 0) && !IsVowel(original, 3)
1197 && (GetAt(original, 3) != 'W'))
1198 {
1199 MetaphAdd(primary, "X");
1200 MetaphAdd(secondary, "S");
1201 }
1202 else
1203 {
1204 MetaphAdd(primary, "X");
1205 MetaphAdd(secondary, "X");
1206 }
1207 current += 3;
1208 break;
1209 }
1210 }
1211
1212 if (StringAt(original, (current + 2), 1,
1213 "I", "E", "Y", ""))
1214 {
1215 MetaphAdd(primary, "S");
1216 MetaphAdd(secondary, "S");
1217 current += 3;
1218 break;
1219 }
1220 /* else */
1221 MetaphAdd(primary, "SK");
1222 MetaphAdd(secondary, "SK");
1223 current += 3;
1224 break;
1225 }
1226
1227 /* french e.g. 'resnais', 'artois' */
1228 if ((current == last)
1229 && StringAt(original, (current - 2), 2, "AI", "OI", ""))
1230 {
1231 MetaphAdd(primary, "");
1232 MetaphAdd(secondary, "S");
1233 }
1234 else
1235 {
1236 MetaphAdd(primary, "S");
1237 MetaphAdd(secondary, "S");
1238 }
1239
1240 if (StringAt(original, (current + 1), 1, "S", "Z", ""))
1241 current += 2;
1242 else
1243 current += 1;
1244 break;
1245
1246 case 'T':
1247 if (StringAt(original, current, 4, "TION", ""))
1248 {
1249 MetaphAdd(primary, "X");
1250 MetaphAdd(secondary, "X");
1251 current += 3;
1252 break;
1253 }
1254
1255 if (StringAt(original, current, 3, "TIA", "TCH", ""))
1256 {
1257 MetaphAdd(primary, "X");
1258 MetaphAdd(secondary, "X");
1259 current += 3;
1260 break;
1261 }
1262
1263 if (StringAt(original, current, 2, "TH", "")
1264 || StringAt(original, current, 3, "TTH", ""))
1265 {
1266 /* special case 'thomas', 'thames' or germanic */
1267 if (StringAt(original, (current + 2), 2, "OM", "AM", "")
1268 || StringAt(original, 0, 4, "VAN ", "VON ", "")
1269 || StringAt(original, 0, 3, "SCH", ""))
1270 {
1271 MetaphAdd(primary, "T");
1272 MetaphAdd(secondary, "T");
1273 }
1274 else
1275 {
1276 MetaphAdd(primary, "0");
1277 MetaphAdd(secondary, "T");
1278 }
1279 current += 2;
1280 break;
1281 }
1282
1283 if (StringAt(original, (current + 1), 1, "T", "D", ""))
1284 current += 2;
1285 else
1286 current += 1;
1287 MetaphAdd(primary, "T");
1288 MetaphAdd(secondary, "T");
1289 break;
1290
1291 case 'V':
1292 if (GetAt(original, current + 1) == 'V')
1293 current += 2;
1294 else
1295 current += 1;
1296 MetaphAdd(primary, "F");
1297 MetaphAdd(secondary, "F");
1298 break;
1299
1300 case 'W':
1301 /* can also be in middle of word */
1302 if (StringAt(original, current, 2, "WR", ""))
1303 {
1304 MetaphAdd(primary, "R");
1305 MetaphAdd(secondary, "R");
1306 current += 2;
1307 break;
1308 }
1309
1310 if ((current == 0)
1311 && (IsVowel(original, current + 1)
1312 || StringAt(original, current, 2, "WH", "")))
1313 {
1314 /* Wasserman should match Vasserman */
1315 if (IsVowel(original, current + 1))
1316 {
1317 MetaphAdd(primary, "A");
1318 MetaphAdd(secondary, "F");
1319 }
1320 else
1321 {
1322 /* need Uomo to match Womo */
1323 MetaphAdd(primary, "A");
1324 MetaphAdd(secondary, "A");
1325 }
1326 }
1327
1328 /* Arnow should match Arnoff */
1329 if (((current == last) && IsVowel(original, current - 1))
1330 || StringAt(original, (current - 1), 5, "EWSKI", "EWSKY",
1331 "OWSKI", "OWSKY", "")
1332 || StringAt(original, 0, 3, "SCH", ""))
1333 {
1334 MetaphAdd(primary, "");
1335 MetaphAdd(secondary, "F");
1336 current += 1;
1337 break;
1338 }
1339
1340 /* polish e.g. 'filipowicz' */
1341 if (StringAt(original, current, 4, "WICZ", "WITZ", ""))
1342 {
1343 MetaphAdd(primary, "TS");
1344 MetaphAdd(secondary, "FX");
1345 current += 4;
1346 break;
1347 }
1348
1349 /* else skip it */
1350 current += 1;
1351 break;
1352
1353 case 'X':
1354 /* french e.g. breaux */
1355 if (!((current == last)
1356 && (StringAt(original, (current - 3), 3,
1357 "IAU", "EAU", "")
1358 || StringAt(original, (current - 2), 2,
1359 "AU", "OU", ""))))
1360 {
1361 MetaphAdd(primary, "KS");
1362 MetaphAdd(secondary, "KS");
1363 }
1364
1365
1366 if (StringAt(original, (current + 1), 1, "C", "X", ""))
1367 current += 2;
1368 else
1369 current += 1;
1370 break;
1371
1372 case 'Z':
1373 /* chinese pinyin e.g. 'zhao' */
1374 if (GetAt(original, current + 1) == 'H')
1375 {
1376 MetaphAdd(primary, "J");
1377 MetaphAdd(secondary, "J");
1378 current += 2;
1379 break;
1380 }
1381 else if (StringAt(original, (current + 1), 2,
1382 "ZO", "ZI", "ZA", "")
1383 || (SlavoGermanic(original)
1384 && ((current > 0)
1385 && GetAt(original, current - 1) != 'T')))
1386 {
1387 MetaphAdd(primary, "S");
1388 MetaphAdd(secondary, "TS");
1389 }
1390 else
1391 {
1392 MetaphAdd(primary, "S");
1393 MetaphAdd(secondary, "S");
1394 }
1395
1396 if (GetAt(original, current + 1) == 'Z')
1397 current += 2;
1398 else
1399 current += 1;
1400 break;
1401
1402 default:
1403 current += 1;
1404 }
1405
1406 /*
1407 * printf("PRIMARY: %s\n", primary->str); printf("SECONDARY: %s\n",
1408 * secondary->str);
1409 */
1410 }
1411
1412
1413 if (primary->length > 4)
1414 SetAt(primary, 4, '\0');
1415
1416 if (secondary->length > 4)
1417 SetAt(secondary, 4, '\0');
1418
1419 *codes = primary->str;
1420 *++codes = secondary->str;
1421
1422 DestroyMetaString(original);
1423 DestroyMetaString(primary);
1425}
1426
1427#ifdef DMETAPHONE_MAIN
1428
1429/* just for testing - not part of the perl code */
1430
1431main(int argc, char **argv)
1432{
1433 char *codes[2];
1434
1435 if (argc > 1)
1436 {
1438 printf("%s|%s\n", codes[0], codes[1]);
1439 }
1440}
1441
1442#endif
Oid collid
int main(void)
static char GetAt(metastring *s, int pos)
Definition dmetaphone.c:330
static void SetAt(metastring *s, int pos, char c)
Definition dmetaphone.c:340
static void MetaphAdd(metastring *s, const char *new_str)
Definition dmetaphone.c:383
static int SlavoGermanic(metastring *s)
Definition dmetaphone.c:314
static metastring * MakeUpper(metastring *s, Oid collid)
Definition dmetaphone.c:283
static void IncreaseBuffer(metastring *s, int chars_needed)
Definition dmetaphone.c:274
static int StringAt(metastring *s, int start, int length,...)
Definition dmetaphone.c:353
static void DoubleMetaphone(const char *str, Oid collid, char **codes)
Definition dmetaphone.c:400
static int IsVowel(metastring *s, int pos)
Definition dmetaphone.c:297
#define META_FREE(x)
Definition dmetaphone.c:201
#define META_REALLOC(v, n, t)
Definition dmetaphone.c:191
static void DestroyMetaString(metastring *s)
Definition dmetaphone.c:261
#define META_MALLOC(v, n, t)
Definition dmetaphone.c:188
static metastring * NewMetaString(const char *init_str)
Definition dmetaphone.c:236
char * str_toupper(const char *buff, size_t nbytes, Oid collid)
return str start
const char * str
#define bufsize
#define printf(...)
Definition port.h:266
unsigned int Oid
static void test(void)
char * c
static int fb(int x)
#define assert(x)
Definition regcustom.h:57
char * str
Definition dmetaphone.c:221
int free_string_on_destroy
Definition dmetaphone.c:224

◆ META_REALLOC

#define META_REALLOC (   v,
  n,
 
)     (v = (t*)repalloc((v),((n)*sizeof(t))))

Definition at line 191 of file dmetaphone.c.

◆ NDEBUG

#define NDEBUG

Definition at line 105 of file dmetaphone.c.

Function Documentation

◆ DestroyMetaString()

static void DestroyMetaString ( metastring s)
static

Definition at line 261 of file dmetaphone.c.

262{
263 if (s == NULL)
264 return;
265
266 if (s->free_string_on_destroy && (s->str != NULL))
267 META_FREE(s->str);
268
269 META_FREE(s);
270}

References fb(), metastring::free_string_on_destroy, META_FREE, and metastring::str.

Referenced by DoubleMetaphone(), and MakeUpper().

◆ dmetaphone()

Datum dmetaphone ( PG_FUNCTION_ARGS  )

Definition at line 132 of file dmetaphone.c.

133{
134 text *arg;
135 char *aptr,
136 *codes[2],
137 *code;
138
139#ifdef DMETAPHONE_NOSTRICT
140 if (PG_ARGISNULL(0))
142#endif
145
147 code = codes[0];
148 if (!code)
149 code = "";
150
152}
#define PG_GETARG_TEXT_PP(n)
Definition fmgr.h:310
#define PG_ARGISNULL(n)
Definition fmgr.h:209
#define PG_RETURN_NULL()
Definition fmgr.h:346
#define PG_RETURN_TEXT_P(x)
Definition fmgr.h:374
#define PG_GET_COLLATION()
Definition fmgr.h:198
void * arg
Definition c.h:706
text * cstring_to_text(const char *s)
Definition varlena.c:181
char * text_to_cstring(const text *t)
Definition varlena.c:214

References arg, cstring_to_text(), DoubleMetaphone(), fb(), PG_ARGISNULL, PG_GET_COLLATION, PG_GETARG_TEXT_PP, PG_RETURN_NULL, PG_RETURN_TEXT_P, and text_to_cstring().

◆ dmetaphone_alt()

Datum dmetaphone_alt ( PG_FUNCTION_ARGS  )

Definition at line 161 of file dmetaphone.c.

162{
163 text *arg;
164 char *aptr,
165 *codes[2],
166 *code;
167
168#ifdef DMETAPHONE_NOSTRICT
169 if (PG_ARGISNULL(0))
171#endif
174
176 code = codes[1];
177 if (!code)
178 code = "";
179
181}

References arg, cstring_to_text(), DoubleMetaphone(), fb(), PG_ARGISNULL, PG_GET_COLLATION, PG_GETARG_TEXT_PP, PG_RETURN_NULL, PG_RETURN_TEXT_P, and text_to_cstring().

◆ DoubleMetaphone()

static void DoubleMetaphone ( const char str,
Oid  collid,
char **  codes 
)
static

Definition at line 400 of file dmetaphone.c.

401{
402 int length;
403 metastring *original;
404 metastring *primary;
406 int current;
407 int last;
408
409 current = 0;
410 /* we need the real length and last prior to padding */
411 length = strlen(str);
412 last = length - 1;
413 original = NewMetaString(str);
414 /* Pad original so we can index beyond end */
415 MetaphAdd(original, " ");
416
417 primary = NewMetaString("");
419 primary->free_string_on_destroy = 0;
420 secondary->free_string_on_destroy = 0;
421
422 original = MakeUpper(original, collid);
423
424 /* skip these when at start of word */
425 if (StringAt(original, 0, 2, "GN", "KN", "PN", "WR", "PS", ""))
426 current += 1;
427
428 /* Initial 'X' is pronounced 'Z' e.g. 'Xavier' */
429 if (GetAt(original, 0) == 'X')
430 {
431 MetaphAdd(primary, "S"); /* 'Z' maps to 'S' */
432 MetaphAdd(secondary, "S");
433 current += 1;
434 }
435
436 /* main loop */
437 while ((primary->length < 4) || (secondary->length < 4))
438 {
439 if (current >= length)
440 break;
441
442 switch (GetAt(original, current))
443 {
444 case 'A':
445 case 'E':
446 case 'I':
447 case 'O':
448 case 'U':
449 case 'Y':
450 if (current == 0)
451 {
452 /* all init vowels now map to 'A' */
453 MetaphAdd(primary, "A");
454 MetaphAdd(secondary, "A");
455 }
456 current += 1;
457 break;
458
459 case 'B':
460
461 /* "-mb", e.g", "dumb", already skipped over... */
462 MetaphAdd(primary, "P");
463 MetaphAdd(secondary, "P");
464
465 if (GetAt(original, current + 1) == 'B')
466 current += 2;
467 else
468 current += 1;
469 break;
470
471 case '\xc7': /* C with cedilla */
472 MetaphAdd(primary, "S");
473 MetaphAdd(secondary, "S");
474 current += 1;
475 break;
476
477 case 'C':
478 /* various germanic */
479 if ((current > 1)
480 && !IsVowel(original, current - 2)
481 && StringAt(original, (current - 1), 3, "ACH", "")
482 && ((GetAt(original, current + 2) != 'I')
483 && ((GetAt(original, current + 2) != 'E')
484 || StringAt(original, (current - 2), 6, "BACHER",
485 "MACHER", ""))))
486 {
487 MetaphAdd(primary, "K");
488 MetaphAdd(secondary, "K");
489 current += 2;
490 break;
491 }
492
493 /* special case 'caesar' */
494 if ((current == 0)
495 && StringAt(original, current, 6, "CAESAR", ""))
496 {
497 MetaphAdd(primary, "S");
498 MetaphAdd(secondary, "S");
499 current += 2;
500 break;
501 }
502
503 /* italian 'chianti' */
504 if (StringAt(original, current, 4, "CHIA", ""))
505 {
506 MetaphAdd(primary, "K");
507 MetaphAdd(secondary, "K");
508 current += 2;
509 break;
510 }
511
512 if (StringAt(original, current, 2, "CH", ""))
513 {
514 /* find 'michael' */
515 if ((current > 0)
516 && StringAt(original, current, 4, "CHAE", ""))
517 {
518 MetaphAdd(primary, "K");
519 MetaphAdd(secondary, "X");
520 current += 2;
521 break;
522 }
523
524 /* greek roots e.g. 'chemistry', 'chorus' */
525 if ((current == 0)
526 && (StringAt(original, (current + 1), 5,
527 "HARAC", "HARIS", "")
528 || StringAt(original, (current + 1), 3, "HOR",
529 "HYM", "HIA", "HEM", ""))
530 && !StringAt(original, 0, 5, "CHORE", ""))
531 {
532 MetaphAdd(primary, "K");
533 MetaphAdd(secondary, "K");
534 current += 2;
535 break;
536 }
537
538 /* germanic, greek, or otherwise 'ch' for 'kh' sound */
539 if ((StringAt(original, 0, 4, "VAN ", "VON ", "")
540 || StringAt(original, 0, 3, "SCH", ""))
541 /* 'architect but not 'arch', 'orchestra', 'orchid' */
542 || StringAt(original, (current - 2), 6, "ORCHES",
543 "ARCHIT", "ORCHID", "")
544 || StringAt(original, (current + 2), 1, "T", "S",
545 "")
546 || ((StringAt(original, (current - 1), 1,
547 "A", "O", "U", "E", "")
548 || (current == 0))
549
550 /*
551 * e.g., 'wachtler', 'wechsler', but not 'tichner'
552 */
553 && StringAt(original, (current + 2), 1, "L", "R",
554 "N", "M", "B", "H", "F", "V", "W",
555 " ", "")))
556 {
557 MetaphAdd(primary, "K");
558 MetaphAdd(secondary, "K");
559 }
560 else
561 {
562 if (current > 0)
563 {
564 if (StringAt(original, 0, 2, "MC", ""))
565 {
566 /* e.g., "McHugh" */
567 MetaphAdd(primary, "K");
568 MetaphAdd(secondary, "K");
569 }
570 else
571 {
572 MetaphAdd(primary, "X");
573 MetaphAdd(secondary, "K");
574 }
575 }
576 else
577 {
578 MetaphAdd(primary, "X");
579 MetaphAdd(secondary, "X");
580 }
581 }
582 current += 2;
583 break;
584 }
585 /* e.g, 'czerny' */
586 if (StringAt(original, current, 2, "CZ", "")
587 && !StringAt(original, (current - 2), 4, "WICZ", ""))
588 {
589 MetaphAdd(primary, "S");
590 MetaphAdd(secondary, "X");
591 current += 2;
592 break;
593 }
594
595 /* e.g., 'focaccia' */
596 if (StringAt(original, (current + 1), 3, "CIA", ""))
597 {
598 MetaphAdd(primary, "X");
599 MetaphAdd(secondary, "X");
600 current += 3;
601 break;
602 }
603
604 /* double 'C', but not if e.g. 'McClellan' */
605 if (StringAt(original, current, 2, "CC", "")
606 && !((current == 1) && (GetAt(original, 0) == 'M')))
607 {
608 /* 'bellocchio' but not 'bacchus' */
609 if (StringAt(original, (current + 2), 1, "I", "E", "H", "")
610 && !StringAt(original, (current + 2), 2, "HU", ""))
611 {
612 /* 'accident', 'accede' 'succeed' */
613 if (((current == 1)
614 && (GetAt(original, current - 1) == 'A'))
615 || StringAt(original, (current - 1), 5, "UCCEE",
616 "UCCES", ""))
617 {
618 MetaphAdd(primary, "KS");
619 MetaphAdd(secondary, "KS");
620 /* 'bacci', 'bertucci', other italian */
621 }
622 else
623 {
624 MetaphAdd(primary, "X");
625 MetaphAdd(secondary, "X");
626 }
627 current += 3;
628 break;
629 }
630 else
631 { /* Pierce's rule */
632 MetaphAdd(primary, "K");
633 MetaphAdd(secondary, "K");
634 current += 2;
635 break;
636 }
637 }
638
639 if (StringAt(original, current, 2, "CK", "CG", "CQ", ""))
640 {
641 MetaphAdd(primary, "K");
642 MetaphAdd(secondary, "K");
643 current += 2;
644 break;
645 }
646
647 if (StringAt(original, current, 2, "CI", "CE", "CY", ""))
648 {
649 /* italian vs. english */
650 if (StringAt
651 (original, current, 3, "CIO", "CIE", "CIA", ""))
652 {
653 MetaphAdd(primary, "S");
654 MetaphAdd(secondary, "X");
655 }
656 else
657 {
658 MetaphAdd(primary, "S");
659 MetaphAdd(secondary, "S");
660 }
661 current += 2;
662 break;
663 }
664
665 /* else */
666 MetaphAdd(primary, "K");
667 MetaphAdd(secondary, "K");
668
669 /* name sent in 'mac caffrey', 'mac gregor */
670 if (StringAt(original, (current + 1), 2, " C", " Q", " G", ""))
671 current += 3;
672 else if (StringAt(original, (current + 1), 1, "C", "K", "Q", "")
673 && !StringAt(original, (current + 1), 2,
674 "CE", "CI", ""))
675 current += 2;
676 else
677 current += 1;
678 break;
679
680 case 'D':
681 if (StringAt(original, current, 2, "DG", ""))
682 {
683 if (StringAt(original, (current + 2), 1,
684 "I", "E", "Y", ""))
685 {
686 /* e.g. 'edge' */
687 MetaphAdd(primary, "J");
688 MetaphAdd(secondary, "J");
689 current += 3;
690 break;
691 }
692 else
693 {
694 /* e.g. 'edgar' */
695 MetaphAdd(primary, "TK");
696 MetaphAdd(secondary, "TK");
697 current += 2;
698 break;
699 }
700 }
701
702 if (StringAt(original, current, 2, "DT", "DD", ""))
703 {
704 MetaphAdd(primary, "T");
705 MetaphAdd(secondary, "T");
706 current += 2;
707 break;
708 }
709
710 /* else */
711 MetaphAdd(primary, "T");
712 MetaphAdd(secondary, "T");
713 current += 1;
714 break;
715
716 case 'F':
717 if (GetAt(original, current + 1) == 'F')
718 current += 2;
719 else
720 current += 1;
721 MetaphAdd(primary, "F");
722 MetaphAdd(secondary, "F");
723 break;
724
725 case 'G':
726 if (GetAt(original, current + 1) == 'H')
727 {
728 if ((current > 0) && !IsVowel(original, current - 1))
729 {
730 MetaphAdd(primary, "K");
731 MetaphAdd(secondary, "K");
732 current += 2;
733 break;
734 }
735
736 if (current < 3)
737 {
738 /* 'ghislane', ghiradelli */
739 if (current == 0)
740 {
741 if (GetAt(original, current + 2) == 'I')
742 {
743 MetaphAdd(primary, "J");
744 MetaphAdd(secondary, "J");
745 }
746 else
747 {
748 MetaphAdd(primary, "K");
749 MetaphAdd(secondary, "K");
750 }
751 current += 2;
752 break;
753 }
754 }
755
756 /*
757 * Parker's rule (with some further refinements) - e.g.,
758 * 'hugh'
759 */
760 if (((current > 1)
761 && StringAt(original, (current - 2), 1,
762 "B", "H", "D", ""))
763 /* e.g., 'bough' */
764 || ((current > 2)
765 && StringAt(original, (current - 3), 1,
766 "B", "H", "D", ""))
767 /* e.g., 'broughton' */
768 || ((current > 3)
769 && StringAt(original, (current - 4), 1,
770 "B", "H", "")))
771 {
772 current += 2;
773 break;
774 }
775 else
776 {
777 /*
778 * e.g., 'laugh', 'McLaughlin', 'cough', 'gough',
779 * 'rough', 'tough'
780 */
781 if ((current > 2)
782 && (GetAt(original, current - 1) == 'U')
783 && StringAt(original, (current - 3), 1, "C",
784 "G", "L", "R", "T", ""))
785 {
786 MetaphAdd(primary, "F");
787 MetaphAdd(secondary, "F");
788 }
789 else if ((current > 0)
790 && GetAt(original, current - 1) != 'I')
791 {
792
793
794 MetaphAdd(primary, "K");
795 MetaphAdd(secondary, "K");
796 }
797
798 current += 2;
799 break;
800 }
801 }
802
803 if (GetAt(original, current + 1) == 'N')
804 {
805 if ((current == 1) && IsVowel(original, 0)
806 && !SlavoGermanic(original))
807 {
808 MetaphAdd(primary, "KN");
809 MetaphAdd(secondary, "N");
810 }
811 else
812 /* not e.g. 'cagney' */
813 if (!StringAt(original, (current + 2), 2, "EY", "")
814 && (GetAt(original, current + 1) != 'Y')
815 && !SlavoGermanic(original))
816 {
817 MetaphAdd(primary, "N");
818 MetaphAdd(secondary, "KN");
819 }
820 else
821 {
822 MetaphAdd(primary, "KN");
823 MetaphAdd(secondary, "KN");
824 }
825 current += 2;
826 break;
827 }
828
829 /* 'tagliaro' */
830 if (StringAt(original, (current + 1), 2, "LI", "")
831 && !SlavoGermanic(original))
832 {
833 MetaphAdd(primary, "KL");
834 MetaphAdd(secondary, "L");
835 current += 2;
836 break;
837 }
838
839 /* -ges-,-gep-,-gel-, -gie- at beginning */
840 if ((current == 0)
841 && ((GetAt(original, current + 1) == 'Y')
842 || StringAt(original, (current + 1), 2, "ES", "EP",
843 "EB", "EL", "EY", "IB", "IL", "IN", "IE",
844 "EI", "ER", "")))
845 {
846 MetaphAdd(primary, "K");
847 MetaphAdd(secondary, "J");
848 current += 2;
849 break;
850 }
851
852 /* -ger-, -gy- */
853 if ((StringAt(original, (current + 1), 2, "ER", "")
854 || (GetAt(original, current + 1) == 'Y'))
855 && !StringAt(original, 0, 6,
856 "DANGER", "RANGER", "MANGER", "")
857 && !StringAt(original, (current - 1), 1, "E", "I", "")
858 && !StringAt(original, (current - 1), 3, "RGY", "OGY", ""))
859 {
860 MetaphAdd(primary, "K");
861 MetaphAdd(secondary, "J");
862 current += 2;
863 break;
864 }
865
866 /* italian e.g, 'biaggi' */
867 if (StringAt(original, (current + 1), 1, "E", "I", "Y", "")
868 || StringAt(original, (current - 1), 4,
869 "AGGI", "OGGI", ""))
870 {
871 /* obvious germanic */
872 if ((StringAt(original, 0, 4, "VAN ", "VON ", "")
873 || StringAt(original, 0, 3, "SCH", ""))
874 || StringAt(original, (current + 1), 2, "ET", ""))
875 {
876 MetaphAdd(primary, "K");
877 MetaphAdd(secondary, "K");
878 }
879 else
880 {
881 /* always soft if french ending */
882 if (StringAt
883 (original, (current + 1), 4, "IER ", ""))
884 {
885 MetaphAdd(primary, "J");
886 MetaphAdd(secondary, "J");
887 }
888 else
889 {
890 MetaphAdd(primary, "J");
891 MetaphAdd(secondary, "K");
892 }
893 }
894 current += 2;
895 break;
896 }
897
898 if (GetAt(original, current + 1) == 'G')
899 current += 2;
900 else
901 current += 1;
902 MetaphAdd(primary, "K");
903 MetaphAdd(secondary, "K");
904 break;
905
906 case 'H':
907 /* only keep if first & before vowel or btw. 2 vowels */
908 if (((current == 0) || IsVowel(original, current - 1))
909 && IsVowel(original, current + 1))
910 {
911 MetaphAdd(primary, "H");
912 MetaphAdd(secondary, "H");
913 current += 2;
914 }
915 else
916 /* also takes care of 'HH' */
917 current += 1;
918 break;
919
920 case 'J':
921 /* obvious spanish, 'jose', 'san jacinto' */
922 if (StringAt(original, current, 4, "JOSE", "")
923 || StringAt(original, 0, 4, "SAN ", ""))
924 {
925 if (((current == 0)
926 && (GetAt(original, current + 4) == ' '))
927 || StringAt(original, 0, 4, "SAN ", ""))
928 {
929 MetaphAdd(primary, "H");
930 MetaphAdd(secondary, "H");
931 }
932 else
933 {
934 MetaphAdd(primary, "J");
935 MetaphAdd(secondary, "H");
936 }
937 current += 1;
938 break;
939 }
940
941 if ((current == 0)
942 && !StringAt(original, current, 4, "JOSE", ""))
943 {
944 MetaphAdd(primary, "J"); /* Yankelovich/Jankelowicz */
945 MetaphAdd(secondary, "A");
946 }
947 else
948 {
949 /* spanish pron. of e.g. 'bajador' */
950 if (IsVowel(original, current - 1)
951 && !SlavoGermanic(original)
952 && ((GetAt(original, current + 1) == 'A')
953 || (GetAt(original, current + 1) == 'O')))
954 {
955 MetaphAdd(primary, "J");
956 MetaphAdd(secondary, "H");
957 }
958 else
959 {
960 if (current == last)
961 {
962 MetaphAdd(primary, "J");
963 MetaphAdd(secondary, "");
964 }
965 else
966 {
967 if (!StringAt(original, (current + 1), 1, "L", "T",
968 "K", "S", "N", "M", "B", "Z", "")
969 && !StringAt(original, (current - 1), 1,
970 "S", "K", "L", ""))
971 {
972 MetaphAdd(primary, "J");
973 MetaphAdd(secondary, "J");
974 }
975 }
976 }
977 }
978
979 if (GetAt(original, current + 1) == 'J') /* it could happen! */
980 current += 2;
981 else
982 current += 1;
983 break;
984
985 case 'K':
986 if (GetAt(original, current + 1) == 'K')
987 current += 2;
988 else
989 current += 1;
990 MetaphAdd(primary, "K");
991 MetaphAdd(secondary, "K");
992 break;
993
994 case 'L':
995 if (GetAt(original, current + 1) == 'L')
996 {
997 /* spanish e.g. 'cabrillo', 'gallegos' */
998 if (((current == (length - 3))
999 && StringAt(original, (current - 1), 4, "ILLO",
1000 "ILLA", "ALLE", ""))
1001 || ((StringAt(original, (last - 1), 2, "AS", "OS", "")
1002 || StringAt(original, last, 1, "A", "O", ""))
1003 && StringAt(original, (current - 1), 4,
1004 "ALLE", "")))
1005 {
1006 MetaphAdd(primary, "L");
1007 MetaphAdd(secondary, "");
1008 current += 2;
1009 break;
1010 }
1011 current += 2;
1012 }
1013 else
1014 current += 1;
1015 MetaphAdd(primary, "L");
1016 MetaphAdd(secondary, "L");
1017 break;
1018
1019 case 'M':
1020 if ((StringAt(original, (current - 1), 3, "UMB", "")
1021 && (((current + 1) == last)
1022 || StringAt(original, (current + 2), 2, "ER", "")))
1023 /* 'dumb','thumb' */
1024 || (GetAt(original, current + 1) == 'M'))
1025 current += 2;
1026 else
1027 current += 1;
1028 MetaphAdd(primary, "M");
1029 MetaphAdd(secondary, "M");
1030 break;
1031
1032 case 'N':
1033 if (GetAt(original, current + 1) == 'N')
1034 current += 2;
1035 else
1036 current += 1;
1037 MetaphAdd(primary, "N");
1038 MetaphAdd(secondary, "N");
1039 break;
1040
1041 case '\xd1': /* N with tilde */
1042 current += 1;
1043 MetaphAdd(primary, "N");
1044 MetaphAdd(secondary, "N");
1045 break;
1046
1047 case 'P':
1048 if (GetAt(original, current + 1) == 'H')
1049 {
1050 MetaphAdd(primary, "F");
1051 MetaphAdd(secondary, "F");
1052 current += 2;
1053 break;
1054 }
1055
1056 /* also account for "campbell", "raspberry" */
1057 if (StringAt(original, (current + 1), 1, "P", "B", ""))
1058 current += 2;
1059 else
1060 current += 1;
1061 MetaphAdd(primary, "P");
1062 MetaphAdd(secondary, "P");
1063 break;
1064
1065 case 'Q':
1066 if (GetAt(original, current + 1) == 'Q')
1067 current += 2;
1068 else
1069 current += 1;
1070 MetaphAdd(primary, "K");
1071 MetaphAdd(secondary, "K");
1072 break;
1073
1074 case 'R':
1075 /* french e.g. 'rogier', but exclude 'hochmeier' */
1076 if ((current == last)
1077 && !SlavoGermanic(original)
1078 && StringAt(original, (current - 2), 2, "IE", "")
1079 && !StringAt(original, (current - 4), 2, "ME", "MA", ""))
1080 {
1081 MetaphAdd(primary, "");
1082 MetaphAdd(secondary, "R");
1083 }
1084 else
1085 {
1086 MetaphAdd(primary, "R");
1087 MetaphAdd(secondary, "R");
1088 }
1089
1090 if (GetAt(original, current + 1) == 'R')
1091 current += 2;
1092 else
1093 current += 1;
1094 break;
1095
1096 case 'S':
1097 /* special cases 'island', 'isle', 'carlisle', 'carlysle' */
1098 if (StringAt(original, (current - 1), 3, "ISL", "YSL", ""))
1099 {
1100 current += 1;
1101 break;
1102 }
1103
1104 /* special case 'sugar-' */
1105 if ((current == 0)
1106 && StringAt(original, current, 5, "SUGAR", ""))
1107 {
1108 MetaphAdd(primary, "X");
1109 MetaphAdd(secondary, "S");
1110 current += 1;
1111 break;
1112 }
1113
1114 if (StringAt(original, current, 2, "SH", ""))
1115 {
1116 /* germanic */
1117 if (StringAt
1118 (original, (current + 1), 4, "HEIM", "HOEK", "HOLM",
1119 "HOLZ", ""))
1120 {
1121 MetaphAdd(primary, "S");
1122 MetaphAdd(secondary, "S");
1123 }
1124 else
1125 {
1126 MetaphAdd(primary, "X");
1127 MetaphAdd(secondary, "X");
1128 }
1129 current += 2;
1130 break;
1131 }
1132
1133 /* italian & armenian */
1134 if (StringAt(original, current, 3, "SIO", "SIA", "")
1135 || StringAt(original, current, 4, "SIAN", ""))
1136 {
1137 if (!SlavoGermanic(original))
1138 {
1139 MetaphAdd(primary, "S");
1140 MetaphAdd(secondary, "X");
1141 }
1142 else
1143 {
1144 MetaphAdd(primary, "S");
1145 MetaphAdd(secondary, "S");
1146 }
1147 current += 3;
1148 break;
1149 }
1150
1151 /*
1152 * german & anglicisations, e.g. 'smith' match 'schmidt',
1153 * 'snider' match 'schneider' also, -sz- in slavic language
1154 * although in hungarian it is pronounced 's'
1155 */
1156 if (((current == 0)
1157 && StringAt(original, (current + 1), 1,
1158 "M", "N", "L", "W", ""))
1159 || StringAt(original, (current + 1), 1, "Z", ""))
1160 {
1161 MetaphAdd(primary, "S");
1162 MetaphAdd(secondary, "X");
1163 if (StringAt(original, (current + 1), 1, "Z", ""))
1164 current += 2;
1165 else
1166 current += 1;
1167 break;
1168 }
1169
1170 if (StringAt(original, current, 2, "SC", ""))
1171 {
1172 /* Schlesinger's rule */
1173 if (GetAt(original, current + 2) == 'H')
1174 {
1175 /* dutch origin, e.g. 'school', 'schooner' */
1176 if (StringAt(original, (current + 3), 2,
1177 "OO", "ER", "EN",
1178 "UY", "ED", "EM", ""))
1179 {
1180 /* 'schermerhorn', 'schenker' */
1181 if (StringAt(original, (current + 3), 2,
1182 "ER", "EN", ""))
1183 {
1184 MetaphAdd(primary, "X");
1185 MetaphAdd(secondary, "SK");
1186 }
1187 else
1188 {
1189 MetaphAdd(primary, "SK");
1190 MetaphAdd(secondary, "SK");
1191 }
1192 current += 3;
1193 break;
1194 }
1195 else
1196 {
1197 if ((current == 0) && !IsVowel(original, 3)
1198 && (GetAt(original, 3) != 'W'))
1199 {
1200 MetaphAdd(primary, "X");
1201 MetaphAdd(secondary, "S");
1202 }
1203 else
1204 {
1205 MetaphAdd(primary, "X");
1206 MetaphAdd(secondary, "X");
1207 }
1208 current += 3;
1209 break;
1210 }
1211 }
1212
1213 if (StringAt(original, (current + 2), 1,
1214 "I", "E", "Y", ""))
1215 {
1216 MetaphAdd(primary, "S");
1217 MetaphAdd(secondary, "S");
1218 current += 3;
1219 break;
1220 }
1221 /* else */
1222 MetaphAdd(primary, "SK");
1223 MetaphAdd(secondary, "SK");
1224 current += 3;
1225 break;
1226 }
1227
1228 /* french e.g. 'resnais', 'artois' */
1229 if ((current == last)
1230 && StringAt(original, (current - 2), 2, "AI", "OI", ""))
1231 {
1232 MetaphAdd(primary, "");
1233 MetaphAdd(secondary, "S");
1234 }
1235 else
1236 {
1237 MetaphAdd(primary, "S");
1238 MetaphAdd(secondary, "S");
1239 }
1240
1241 if (StringAt(original, (current + 1), 1, "S", "Z", ""))
1242 current += 2;
1243 else
1244 current += 1;
1245 break;
1246
1247 case 'T':
1248 if (StringAt(original, current, 4, "TION", ""))
1249 {
1250 MetaphAdd(primary, "X");
1251 MetaphAdd(secondary, "X");
1252 current += 3;
1253 break;
1254 }
1255
1256 if (StringAt(original, current, 3, "TIA", "TCH", ""))
1257 {
1258 MetaphAdd(primary, "X");
1259 MetaphAdd(secondary, "X");
1260 current += 3;
1261 break;
1262 }
1263
1264 if (StringAt(original, current, 2, "TH", "")
1265 || StringAt(original, current, 3, "TTH", ""))
1266 {
1267 /* special case 'thomas', 'thames' or germanic */
1268 if (StringAt(original, (current + 2), 2, "OM", "AM", "")
1269 || StringAt(original, 0, 4, "VAN ", "VON ", "")
1270 || StringAt(original, 0, 3, "SCH", ""))
1271 {
1272 MetaphAdd(primary, "T");
1273 MetaphAdd(secondary, "T");
1274 }
1275 else
1276 {
1277 MetaphAdd(primary, "0");
1278 MetaphAdd(secondary, "T");
1279 }
1280 current += 2;
1281 break;
1282 }
1283
1284 if (StringAt(original, (current + 1), 1, "T", "D", ""))
1285 current += 2;
1286 else
1287 current += 1;
1288 MetaphAdd(primary, "T");
1289 MetaphAdd(secondary, "T");
1290 break;
1291
1292 case 'V':
1293 if (GetAt(original, current + 1) == 'V')
1294 current += 2;
1295 else
1296 current += 1;
1297 MetaphAdd(primary, "F");
1298 MetaphAdd(secondary, "F");
1299 break;
1300
1301 case 'W':
1302 /* can also be in middle of word */
1303 if (StringAt(original, current, 2, "WR", ""))
1304 {
1305 MetaphAdd(primary, "R");
1306 MetaphAdd(secondary, "R");
1307 current += 2;
1308 break;
1309 }
1310
1311 if ((current == 0)
1312 && (IsVowel(original, current + 1)
1313 || StringAt(original, current, 2, "WH", "")))
1314 {
1315 /* Wasserman should match Vasserman */
1316 if (IsVowel(original, current + 1))
1317 {
1318 MetaphAdd(primary, "A");
1319 MetaphAdd(secondary, "F");
1320 }
1321 else
1322 {
1323 /* need Uomo to match Womo */
1324 MetaphAdd(primary, "A");
1325 MetaphAdd(secondary, "A");
1326 }
1327 }
1328
1329 /* Arnow should match Arnoff */
1330 if (((current == last) && IsVowel(original, current - 1))
1331 || StringAt(original, (current - 1), 5, "EWSKI", "EWSKY",
1332 "OWSKI", "OWSKY", "")
1333 || StringAt(original, 0, 3, "SCH", ""))
1334 {
1335 MetaphAdd(primary, "");
1336 MetaphAdd(secondary, "F");
1337 current += 1;
1338 break;
1339 }
1340
1341 /* polish e.g. 'filipowicz' */
1342 if (StringAt(original, current, 4, "WICZ", "WITZ", ""))
1343 {
1344 MetaphAdd(primary, "TS");
1345 MetaphAdd(secondary, "FX");
1346 current += 4;
1347 break;
1348 }
1349
1350 /* else skip it */
1351 current += 1;
1352 break;
1353
1354 case 'X':
1355 /* french e.g. breaux */
1356 if (!((current == last)
1357 && (StringAt(original, (current - 3), 3,
1358 "IAU", "EAU", "")
1359 || StringAt(original, (current - 2), 2,
1360 "AU", "OU", ""))))
1361 {
1362 MetaphAdd(primary, "KS");
1363 MetaphAdd(secondary, "KS");
1364 }
1365
1366
1367 if (StringAt(original, (current + 1), 1, "C", "X", ""))
1368 current += 2;
1369 else
1370 current += 1;
1371 break;
1372
1373 case 'Z':
1374 /* chinese pinyin e.g. 'zhao' */
1375 if (GetAt(original, current + 1) == 'H')
1376 {
1377 MetaphAdd(primary, "J");
1378 MetaphAdd(secondary, "J");
1379 current += 2;
1380 break;
1381 }
1382 else if (StringAt(original, (current + 1), 2,
1383 "ZO", "ZI", "ZA", "")
1384 || (SlavoGermanic(original)
1385 && ((current > 0)
1386 && GetAt(original, current - 1) != 'T')))
1387 {
1388 MetaphAdd(primary, "S");
1389 MetaphAdd(secondary, "TS");
1390 }
1391 else
1392 {
1393 MetaphAdd(primary, "S");
1394 MetaphAdd(secondary, "S");
1395 }
1396
1397 if (GetAt(original, current + 1) == 'Z')
1398 current += 2;
1399 else
1400 current += 1;
1401 break;
1402
1403 default:
1404 current += 1;
1405 }
1406
1407 /*
1408 * printf("PRIMARY: %s\n", primary->str); printf("SECONDARY: %s\n",
1409 * secondary->str);
1410 */
1411 }
1412
1413
1414 if (primary->length > 4)
1415 SetAt(primary, 4, '\0');
1416
1417 if (secondary->length > 4)
1418 SetAt(secondary, 4, '\0');
1419
1420 *codes = primary->str;
1421 *++codes = secondary->str;
1422
1423 DestroyMetaString(original);
1424 DestroyMetaString(primary);
1426}

References collid, DestroyMetaString(), fb(), metastring::free_string_on_destroy, GetAt(), IsVowel(), metastring::length, MakeUpper(), MetaphAdd(), NewMetaString(), SetAt(), SlavoGermanic(), metastring::str, str, and StringAt().

Referenced by dmetaphone(), and dmetaphone_alt().

◆ GetAt()

static char GetAt ( metastring s,
int  pos 
)
static

Definition at line 330 of file dmetaphone.c.

331{
332 if ((pos < 0) || (pos >= s->length))
333 return '\0';
334
335 return *(s->str + pos);
336}

References metastring::length, and metastring::str.

Referenced by DoubleMetaphone().

◆ IncreaseBuffer()

static void IncreaseBuffer ( metastring s,
int  chars_needed 
)
static

Definition at line 274 of file dmetaphone.c.

275{
276 META_REALLOC(s->str, (s->bufsize + chars_needed + 10), char);
277 assert(s->str != NULL);
278 s->bufsize = s->bufsize + chars_needed + 10;
279}

References assert, metastring::bufsize, fb(), META_REALLOC, and metastring::str.

Referenced by MetaphAdd().

◆ IsVowel()

static int IsVowel ( metastring s,
int  pos 
)
static

Definition at line 297 of file dmetaphone.c.

298{
299 char c;
300
301 if ((pos < 0) || (pos >= s->length))
302 return 0;
303
304 c = *(s->str + pos);
305 if ((c == 'A') || (c == 'E') || (c == 'I') || (c == 'O') ||
306 (c == 'U') || (c == 'Y'))
307 return 1;
308
309 return 0;
310}

References metastring::length, and metastring::str.

Referenced by DoubleMetaphone().

◆ MakeUpper()

static metastring * MakeUpper ( metastring s,
Oid  collid 
)
static

Definition at line 283 of file dmetaphone.c.

284{
285 char *newstr;
287
291
292 return newms;
293}

References collid, DestroyMetaString(), fb(), metastring::length, NewMetaString(), metastring::str, and str_toupper().

Referenced by DoubleMetaphone().

◆ MetaphAdd()

static void MetaphAdd ( metastring s,
const char new_str 
)
static

Definition at line 383 of file dmetaphone.c.

384{
385 int add_length;
386
387 if (new_str == NULL)
388 return;
389
391 if ((s->length + add_length) > (s->bufsize - 1))
393
394 strcat(s->str, new_str);
395 s->length += add_length;
396}

References metastring::bufsize, fb(), IncreaseBuffer(), metastring::length, and metastring::str.

Referenced by DoubleMetaphone().

◆ NewMetaString()

static metastring * NewMetaString ( const char init_str)
static

Definition at line 236 of file dmetaphone.c.

237{
238 metastring *s;
239 char empty_string[] = "";
240
241 META_MALLOC(s, 1, metastring);
242 assert(s != NULL);
243
244 if (init_str == NULL)
246 s->length = strlen(init_str);
247 /* preallocate a bit more for potential growth */
248 s->bufsize = s->length + 7;
249
250 META_MALLOC(s->str, s->bufsize, char);
251 assert(s->str != NULL);
252
253 memcpy(s->str, init_str, s->length + 1);
255
256 return s;
257}

References assert, metastring::bufsize, fb(), metastring::free_string_on_destroy, metastring::length, META_MALLOC, and metastring::str.

Referenced by DoubleMetaphone(), and MakeUpper().

◆ PG_FUNCTION_INFO_V1() [1/2]

PG_FUNCTION_INFO_V1 ( dmetaphone  )

◆ PG_FUNCTION_INFO_V1() [2/2]

PG_FUNCTION_INFO_V1 ( dmetaphone_alt  )

◆ SetAt()

static void SetAt ( metastring s,
int  pos,
char  c 
)
static

Definition at line 340 of file dmetaphone.c.

341{
342 if ((pos < 0) || (pos >= s->length))
343 return;
344
345 *(s->str + pos) = c;
346}

References metastring::length, and metastring::str.

Referenced by DoubleMetaphone().

◆ SlavoGermanic()

static int SlavoGermanic ( metastring s)
static

Definition at line 314 of file dmetaphone.c.

315{
316 if (strstr(s->str, "W"))
317 return 1;
318 else if (strstr(s->str, "K"))
319 return 1;
320 else if (strstr(s->str, "CZ"))
321 return 1;
322 else if (strstr(s->str, "WITZ"))
323 return 1;
324 else
325 return 0;
326}

References fb(), and metastring::str.

Referenced by DoubleMetaphone().

◆ StringAt()

static int StringAt ( metastring s,
int  start,
int  length,
  ... 
)
static

Definition at line 353 of file dmetaphone.c.

354{
355 char *test;
356 char *pos;
357 va_list ap;
358
359 if ((start < 0) || (start >= s->length))
360 return 0;
361
362 pos = (s->str + start);
363 va_start(ap, length);
364
365 do
366 {
367 test = va_arg(ap, char *);
368 if (*test && (strncmp(pos, test, length) == 0))
369 {
370 va_end(ap);
371 return 1;
372 }
373 }
374 while (strcmp(test, "") != 0);
375
376 va_end(ap);
377
378 return 0;
379}

References fb(), metastring::length, start, metastring::str, and test().

Referenced by DoubleMetaphone().