2
$Id: double_metaphone.c,v 1.2 2002/09/30 21:23:31 whmoseley Exp $
5
** August 20, 2002 moseley - first added to swish-e
7
** this is a very slightly modified version of the double_metaphone.c code
8
** from the Perl module Text::DoubleMetaphone by Maurice Aubrey, and based
9
** on the work of Lawrence Philips.
10
** See http://aspell.sourceforge.net/metaphone
12
** From the Text::DoubleMetaphone README file:
16
This module implements a "sounds like" algorithm developed
17
by Lawrence Philips which he published in the June, 2000 issue
18
of C/C++ Users Journal. Double Metaphone is an improved
19
version of Philips' original Metaphone algorithm.
23
Copyright 2000, Maurice Aubrey <maurice@hevanet.com>.
26
This code is based heavily on the C++ implementation by
27
Lawrence Philips and incorporates several bug fixes courtesy
28
of Kevin Atkinson <kevina@users.sourceforge.net>.
30
This module is free software; you may redistribute it and/or
31
modify it under the same terms as Perl itself.
46
#include "swish.h" // $$$ yikes, sure brings in a lot
47
#include "double_metaphone.h"
51
#define META_MALLOC(v,n,t) (v = (t*)emalloc(((n)*sizeof(t))))
53
#define META_REALLOC(v,n,t) (v = (t*)erealloc((v),((n)*sizeof(t))))
55
#define META_FREE(x) efree((x))
59
NewMetaString(char *init_str)
62
char empty_string[] = "";
64
META_MALLOC(s, 1, metastring);
68
init_str = empty_string;
69
s->length = strlen(init_str);
70
/* preallocate a bit more for potential growth */
71
s->bufsize = s->length + 7;
73
META_MALLOC(s->str, s->bufsize, char);
74
assert( s->str != NULL );
76
strncpy(s->str, init_str, s->length + 1);
77
s->free_string_on_destroy = 1;
84
DestroyMetaString(metastring * s)
89
if (s->free_string_on_destroy && (s->str != NULL))
97
IncreaseBuffer(metastring * s, int chars_needed)
99
META_REALLOC(s->str, (s->bufsize + chars_needed + 10), char);
100
assert( s->str != NULL );
101
s->bufsize = s->bufsize + chars_needed + 10;
106
MakeUpper(metastring * s)
110
for (i = s->str; *i; i++)
118
IsVowel(metastring * s, int pos)
122
if ((pos < 0) || (pos >= s->length))
126
if ((c == 'A') || (c == 'E') || (c == 'I') || (c =='O') ||
127
(c =='U') || (c == 'Y'))
135
SlavoGermanic(metastring * s)
137
if ((char *) strstr(s->str, "W"))
139
else if ((char *) strstr(s->str, "K"))
141
else if ((char *) strstr(s->str, "CZ"))
143
else if ((char *) strstr(s->str, "WITZ"))
151
GetLength(metastring * s)
158
GetAt(metastring * s, int pos)
160
if ((pos < 0) || (pos >= s->length))
163
return ((char) *(s->str + pos));
168
SetAt(metastring * s, int pos, char c)
170
if ((pos < 0) || (pos >= s->length))
178
Caveats: the START value is 0 based
181
StringAt(metastring * s, int start, int length, ...)
187
if ((start < 0) || (start >= s->length))
190
pos = (s->str + start);
191
va_start(ap, length);
195
test = va_arg(ap, char *);
196
if (*test && (strncmp(pos, test, length) == 0))
199
while (strcmp(test, ""));
208
MetaphAdd(metastring * s, char *new_str)
215
add_length = strlen(new_str);
216
if ((s->length + add_length) > (s->bufsize - 1))
218
IncreaseBuffer(s, add_length);
221
strcat(s->str, new_str);
222
s->length += add_length;
227
DoubleMetaphone(char *str, char **codes)
230
metastring *original;
232
metastring *secondary;
237
/* we need the real length and last prior to padding */
238
length = strlen(str);
240
original = NewMetaString(str);
241
/* Pad original so we can index beyond end */
242
MetaphAdd(original, " ");
244
primary = NewMetaString("");
245
secondary = NewMetaString("");
246
primary->free_string_on_destroy = 0;
247
secondary->free_string_on_destroy = 0;
251
/* skip these when at start of word */
252
if (StringAt(original, 0, 2, "GN", "KN", "PN", "WR", "PS", ""))
255
/* Initial 'X' is pronounced 'Z' e.g. 'Xavier' */
256
if (GetAt(original, 0) == 'X')
258
MetaphAdd(primary, "S"); /* 'Z' maps to 'S' */
259
MetaphAdd(secondary, "S");
264
while ((primary->length < 4) || (secondary->length < 4))
266
if (current >= length)
269
switch (GetAt(original, current))
279
/* all init vowels now map to 'A' */
280
MetaphAdd(primary, "A");
281
MetaphAdd(secondary, "A");
288
/* "-mb", e.g", "dumb", already skipped over... */
289
MetaphAdd(primary, "P");
290
MetaphAdd(secondary, "P");
292
if (GetAt(original, current + 1) == 'B')
299
MetaphAdd(primary, "S");
300
MetaphAdd(secondary, "S");
305
/* various germanic */
307
&& !IsVowel(original, current - 2)
308
&& StringAt(original, (current - 1), 3, "ACH", "")
309
&& ((GetAt(original, current + 2) != 'I')
310
&& ((GetAt(original, current + 2) != 'E')
311
|| StringAt(original, (current - 2), 6, "BACHER",
314
MetaphAdd(primary, "K");
315
MetaphAdd(secondary, "K");
320
/* special case 'caesar' */
322
&& StringAt(original, current, 6, "CAESAR", ""))
324
MetaphAdd(primary, "S");
325
MetaphAdd(secondary, "S");
330
/* italian 'chianti' */
331
if (StringAt(original, current, 4, "CHIA", ""))
333
MetaphAdd(primary, "K");
334
MetaphAdd(secondary, "K");
339
if (StringAt(original, current, 2, "CH", ""))
343
&& StringAt(original, current, 4, "CHAE", ""))
345
MetaphAdd(primary, "K");
346
MetaphAdd(secondary, "X");
351
/* greek roots e.g. 'chemistry', 'chorus' */
353
&& (StringAt(original, (current + 1), 5, "HARAC", "HARIS", "")
354
|| StringAt(original, (current + 1), 3, "HOR",
355
"HYM", "HIA", "HEM", ""))
356
&& !StringAt(original, 0, 5, "CHORE", ""))
358
MetaphAdd(primary, "K");
359
MetaphAdd(secondary, "K");
364
/* germanic, greek, or otherwise 'ch' for 'kh' sound */
366
(StringAt(original, 0, 4, "VAN ", "VON ", "")
367
|| StringAt(original, 0, 3, "SCH", ""))
368
/* 'architect but not 'arch', 'orchestra', 'orchid' */
369
|| StringAt(original, (current - 2), 6, "ORCHES",
370
"ARCHIT", "ORCHID", "")
371
|| StringAt(original, (current + 2), 1, "T", "S",
373
|| ((StringAt(original, (current - 1), 1, "A", "O", "U", "E", "")
375
/* e.g., 'wachtler', 'wechsler', but not 'tichner' */
376
&& StringAt(original, (current + 2), 1, "L", "R",
377
"N", "M", "B", "H", "F", "V", "W", " ", "")))
379
MetaphAdd(primary, "K");
380
MetaphAdd(secondary, "K");
386
if (StringAt(original, 0, 2, "MC", ""))
389
MetaphAdd(primary, "K");
390
MetaphAdd(secondary, "K");
394
MetaphAdd(primary, "X");
395
MetaphAdd(secondary, "K");
400
MetaphAdd(primary, "X");
401
MetaphAdd(secondary, "X");
408
if (StringAt(original, current, 2, "CZ", "")
409
&& !StringAt(original, (current - 2), 4, "WICZ", ""))
411
MetaphAdd(primary, "S");
412
MetaphAdd(secondary, "X");
417
/* e.g., 'focaccia' */
418
if (StringAt(original, (current + 1), 3, "CIA", ""))
420
MetaphAdd(primary, "X");
421
MetaphAdd(secondary, "X");
426
/* double 'C', but not if e.g. 'McClellan' */
427
if (StringAt(original, current, 2, "CC", "")
428
&& !((current == 1) && (GetAt(original, 0) == 'M')))
430
/* 'bellocchio' but not 'bacchus' */
431
if (StringAt(original, (current + 2), 1, "I", "E", "H", "")
432
&& !StringAt(original, (current + 2), 2, "HU", ""))
434
/* 'accident', 'accede' 'succeed' */
437
&& (GetAt(original, current - 1) == 'A'))
438
|| StringAt(original, (current - 1), 5, "UCCEE",
441
MetaphAdd(primary, "KS");
442
MetaphAdd(secondary, "KS");
443
/* 'bacci', 'bertucci', other italian */
447
MetaphAdd(primary, "X");
448
MetaphAdd(secondary, "X");
454
{ /* Pierce's rule */
455
MetaphAdd(primary, "K");
456
MetaphAdd(secondary, "K");
462
if (StringAt(original, current, 2, "CK", "CG", "CQ", ""))
464
MetaphAdd(primary, "K");
465
MetaphAdd(secondary, "K");
470
if (StringAt(original, current, 2, "CI", "CE", "CY", ""))
472
/* italian vs. english */
474
(original, current, 3, "CIO", "CIE", "CIA", ""))
476
MetaphAdd(primary, "S");
477
MetaphAdd(secondary, "X");
481
MetaphAdd(primary, "S");
482
MetaphAdd(secondary, "S");
489
MetaphAdd(primary, "K");
490
MetaphAdd(secondary, "K");
492
/* name sent in 'mac caffrey', 'mac gregor */
493
if (StringAt(original, (current + 1), 2, " C", " Q", " G", ""))
496
if (StringAt(original, (current + 1), 1, "C", "K", "Q", "")
497
&& !StringAt(original, (current + 1), 2, "CE", "CI", ""))
504
if (StringAt(original, current, 2, "DG", ""))
506
if (StringAt(original, (current + 2), 1, "I", "E", "Y", ""))
509
MetaphAdd(primary, "J");
510
MetaphAdd(secondary, "J");
517
MetaphAdd(primary, "TK");
518
MetaphAdd(secondary, "TK");
524
if (StringAt(original, current, 2, "DT", "DD", ""))
526
MetaphAdd(primary, "T");
527
MetaphAdd(secondary, "T");
533
MetaphAdd(primary, "T");
534
MetaphAdd(secondary, "T");
539
if (GetAt(original, current + 1) == 'F')
543
MetaphAdd(primary, "F");
544
MetaphAdd(secondary, "F");
548
if (GetAt(original, current + 1) == 'H')
550
if ((current > 0) && !IsVowel(original, current - 1))
552
MetaphAdd(primary, "K");
553
MetaphAdd(secondary, "K");
560
/* 'ghislane', ghiradelli */
563
if (GetAt(original, current + 2) == 'I')
565
MetaphAdd(primary, "J");
566
MetaphAdd(secondary, "J");
570
MetaphAdd(primary, "K");
571
MetaphAdd(secondary, "K");
577
/* Parker's rule (with some further refinements) - e.g., 'hugh' */
580
&& StringAt(original, (current - 2), 1, "B", "H", "D", ""))
583
&& StringAt(original, (current - 3), 1, "B", "H", "D", ""))
584
/* e.g., 'broughton' */
586
&& StringAt(original, (current - 4), 1, "B", "H", "")))
593
/* e.g., 'laugh', 'McLaughlin', 'cough', 'gough', 'rough', 'tough' */
595
&& (GetAt(original, current - 1) == 'U')
596
&& StringAt(original, (current - 3), 1, "C",
597
"G", "L", "R", "T", ""))
599
MetaphAdd(primary, "F");
600
MetaphAdd(secondary, "F");
602
else if ((current > 0)
603
&& GetAt(original, current - 1) != 'I')
607
MetaphAdd(primary, "K");
608
MetaphAdd(secondary, "K");
616
if (GetAt(original, current + 1) == 'N')
618
if ((current == 1) && IsVowel(original, 0)
619
&& !SlavoGermanic(original))
621
MetaphAdd(primary, "KN");
622
MetaphAdd(secondary, "N");
625
/* not e.g. 'cagney' */
626
if (!StringAt(original, (current + 2), 2, "EY", "")
627
&& (GetAt(original, current + 1) != 'Y')
628
&& !SlavoGermanic(original))
630
MetaphAdd(primary, "N");
631
MetaphAdd(secondary, "KN");
635
MetaphAdd(primary, "KN");
636
MetaphAdd(secondary, "KN");
643
if (StringAt(original, (current + 1), 2, "LI", "")
644
&& !SlavoGermanic(original))
646
MetaphAdd(primary, "KL");
647
MetaphAdd(secondary, "L");
652
/* -ges-,-gep-,-gel-, -gie- at beginning */
654
&& ((GetAt(original, current + 1) == 'Y')
655
|| StringAt(original, (current + 1), 2, "ES", "EP",
656
"EB", "EL", "EY", "IB", "IL", "IN", "IE",
659
MetaphAdd(primary, "K");
660
MetaphAdd(secondary, "J");
667
(StringAt(original, (current + 1), 2, "ER", "")
668
|| (GetAt(original, current + 1) == 'Y'))
669
&& !StringAt(original, 0, 6, "DANGER", "RANGER", "MANGER", "")
670
&& !StringAt(original, (current - 1), 1, "E", "I", "")
671
&& !StringAt(original, (current - 1), 3, "RGY", "OGY",
674
MetaphAdd(primary, "K");
675
MetaphAdd(secondary, "J");
680
/* italian e.g, 'biaggi' */
681
if (StringAt(original, (current + 1), 1, "E", "I", "Y", "")
682
|| StringAt(original, (current - 1), 4, "AGGI", "OGGI", ""))
684
/* obvious germanic */
686
(StringAt(original, 0, 4, "VAN ", "VON ", "")
687
|| StringAt(original, 0, 3, "SCH", ""))
688
|| StringAt(original, (current + 1), 2, "ET", ""))
690
MetaphAdd(primary, "K");
691
MetaphAdd(secondary, "K");
695
/* always soft if french ending */
697
(original, (current + 1), 4, "IER ", ""))
699
MetaphAdd(primary, "J");
700
MetaphAdd(secondary, "J");
704
MetaphAdd(primary, "J");
705
MetaphAdd(secondary, "K");
712
if (GetAt(original, current + 1) == 'G')
716
MetaphAdd(primary, "K");
717
MetaphAdd(secondary, "K");
721
/* only keep if first & before vowel or btw. 2 vowels */
722
if (((current == 0) || IsVowel(original, current - 1))
723
&& IsVowel(original, current + 1))
725
MetaphAdd(primary, "H");
726
MetaphAdd(secondary, "H");
729
else /* also takes care of 'HH' */
734
/* obvious spanish, 'jose', 'san jacinto' */
735
if (StringAt(original, current, 4, "JOSE", "")
736
|| StringAt(original, 0, 4, "SAN ", ""))
739
&& (GetAt(original, current + 4) == ' '))
740
|| StringAt(original, 0, 4, "SAN ", ""))
742
MetaphAdd(primary, "H");
743
MetaphAdd(secondary, "H");
747
MetaphAdd(primary, "J");
748
MetaphAdd(secondary, "H");
755
&& !StringAt(original, current, 4, "JOSE", ""))
757
MetaphAdd(primary, "J"); /* Yankelovich/Jankelowicz */
758
MetaphAdd(secondary, "A");
762
/* spanish pron. of e.g. 'bajador' */
763
if (IsVowel(original, current - 1)
764
&& !SlavoGermanic(original)
765
&& ((GetAt(original, current + 1) == 'A')
766
|| (GetAt(original, current + 1) == 'O')))
768
MetaphAdd(primary, "J");
769
MetaphAdd(secondary, "H");
775
MetaphAdd(primary, "J");
776
MetaphAdd(secondary, "");
780
if (!StringAt(original, (current + 1), 1, "L", "T",
781
"K", "S", "N", "M", "B", "Z", "")
782
&& !StringAt(original, (current - 1), 1,
785
MetaphAdd(primary, "J");
786
MetaphAdd(secondary, "J");
792
if (GetAt(original, current + 1) == 'J') /* it could happen! */
799
if (GetAt(original, current + 1) == 'K')
803
MetaphAdd(primary, "K");
804
MetaphAdd(secondary, "K");
808
if (GetAt(original, current + 1) == 'L')
810
/* spanish e.g. 'cabrillo', 'gallegos' */
811
if (((current == (length - 3))
812
&& StringAt(original, (current - 1), 4, "ILLO",
814
|| ((StringAt(original, (last - 1), 2, "AS", "OS", "")
815
|| StringAt(original, last, 1, "A", "O", ""))
816
&& StringAt(original, (current - 1), 4, "ALLE", "")))
818
MetaphAdd(primary, "L");
819
MetaphAdd(secondary, "");
827
MetaphAdd(primary, "L");
828
MetaphAdd(secondary, "L");
832
if ((StringAt(original, (current - 1), 3, "UMB", "")
833
&& (((current + 1) == last)
834
|| StringAt(original, (current + 2), 2, "ER", "")))
836
|| (GetAt(original, current + 1) == 'M'))
840
MetaphAdd(primary, "M");
841
MetaphAdd(secondary, "M");
845
if (GetAt(original, current + 1) == 'N')
849
MetaphAdd(primary, "N");
850
MetaphAdd(secondary, "N");
855
MetaphAdd(primary, "N");
856
MetaphAdd(secondary, "N");
860
if (GetAt(original, current + 1) == 'H')
862
MetaphAdd(primary, "F");
863
MetaphAdd(secondary, "F");
868
/* also account for "campbell", "raspberry" */
869
if (StringAt(original, (current + 1), 1, "P", "B", ""))
873
MetaphAdd(primary, "P");
874
MetaphAdd(secondary, "P");
878
if (GetAt(original, current + 1) == 'Q')
882
MetaphAdd(primary, "K");
883
MetaphAdd(secondary, "K");
887
/* french e.g. 'rogier', but exclude 'hochmeier' */
888
if ((current == last)
889
&& !SlavoGermanic(original)
890
&& StringAt(original, (current - 2), 2, "IE", "")
891
&& !StringAt(original, (current - 4), 2, "ME", "MA", ""))
893
MetaphAdd(primary, "");
894
MetaphAdd(secondary, "R");
898
MetaphAdd(primary, "R");
899
MetaphAdd(secondary, "R");
902
if (GetAt(original, current + 1) == 'R')
909
/* special cases 'island', 'isle', 'carlisle', 'carlysle' */
910
if (StringAt(original, (current - 1), 3, "ISL", "YSL", ""))
916
/* special case 'sugar-' */
918
&& StringAt(original, current, 5, "SUGAR", ""))
920
MetaphAdd(primary, "X");
921
MetaphAdd(secondary, "S");
926
if (StringAt(original, current, 2, "SH", ""))
930
(original, (current + 1), 4, "HEIM", "HOEK", "HOLM",
933
MetaphAdd(primary, "S");
934
MetaphAdd(secondary, "S");
938
MetaphAdd(primary, "X");
939
MetaphAdd(secondary, "X");
945
/* italian & armenian */
946
if (StringAt(original, current, 3, "SIO", "SIA", "")
947
|| StringAt(original, current, 4, "SIAN", ""))
949
if (!SlavoGermanic(original))
951
MetaphAdd(primary, "S");
952
MetaphAdd(secondary, "X");
956
MetaphAdd(primary, "S");
957
MetaphAdd(secondary, "S");
963
/* german & anglicisations, e.g. 'smith' match 'schmidt', 'snider' match 'schneider'
964
also, -sz- in slavic language altho in hungarian it is pronounced 's' */
966
&& StringAt(original, (current + 1), 1, "M", "N", "L", "W", ""))
967
|| StringAt(original, (current + 1), 1, "Z", ""))
969
MetaphAdd(primary, "S");
970
MetaphAdd(secondary, "X");
971
if (StringAt(original, (current + 1), 1, "Z", ""))
978
if (StringAt(original, current, 2, "SC", ""))
980
/* Schlesinger's rule */
981
if (GetAt(original, current + 2) == 'H')
983
/* dutch origin, e.g. 'school', 'schooner' */
984
if (StringAt(original, (current + 3), 2, "OO", "ER", "EN",
985
"UY", "ED", "EM", ""))
987
/* 'schermerhorn', 'schenker' */
988
if (StringAt(original, (current + 3), 2, "ER", "EN", ""))
990
MetaphAdd(primary, "X");
991
MetaphAdd(secondary, "SK");
995
MetaphAdd(primary, "SK");
996
MetaphAdd(secondary, "SK");
1003
if ((current == 0) && !IsVowel(original, 3)
1004
&& (GetAt(original, 3) != 'W'))
1006
MetaphAdd(primary, "X");
1007
MetaphAdd(secondary, "S");
1011
MetaphAdd(primary, "X");
1012
MetaphAdd(secondary, "X");
1018
if (StringAt(original, (current + 2), 1, "I", "E", "Y", ""))
1020
MetaphAdd(primary, "S");
1021
MetaphAdd(secondary, "S");
1026
MetaphAdd(primary, "SK");
1027
MetaphAdd(secondary, "SK");
1033
/* french e.g. 'resnais', 'artois' */
1034
if ((current == last)
1035
&& StringAt(original, (current - 2), 2, "AI", "OI", ""))
1037
MetaphAdd(primary, "");
1038
MetaphAdd(secondary, "S");
1042
MetaphAdd(primary, "S");
1043
MetaphAdd(secondary, "S");
1046
if (StringAt(original, (current + 1), 1, "S", "Z", ""))
1053
if (StringAt(original, current, 4, "TION", ""))
1055
MetaphAdd(primary, "X");
1056
MetaphAdd(secondary, "X");
1061
if (StringAt(original, current, 3, "TIA", "TCH", ""))
1063
MetaphAdd(primary, "X");
1064
MetaphAdd(secondary, "X");
1069
if (StringAt(original, current, 2, "TH", "")
1070
|| StringAt(original, current, 3, "TTH", ""))
1072
/* special case 'thomas', 'thames' or germanic */
1073
if (StringAt(original, (current + 2), 2, "OM", "AM", "")
1074
|| StringAt(original, 0, 4, "VAN ", "VON ", "")
1075
|| StringAt(original, 0, 3, "SCH", ""))
1077
MetaphAdd(primary, "T");
1078
MetaphAdd(secondary, "T");
1082
MetaphAdd(primary, "0");
1083
MetaphAdd(secondary, "T");
1089
if (StringAt(original, (current + 1), 1, "T", "D", ""))
1093
MetaphAdd(primary, "T");
1094
MetaphAdd(secondary, "T");
1098
if (GetAt(original, current + 1) == 'V')
1102
MetaphAdd(primary, "F");
1103
MetaphAdd(secondary, "F");
1107
/* can also be in middle of word */
1108
if (StringAt(original, current, 2, "WR", ""))
1110
MetaphAdd(primary, "R");
1111
MetaphAdd(secondary, "R");
1117
&& (IsVowel(original, current + 1)
1118
|| StringAt(original, current, 2, "WH", "")))
1120
/* Wasserman should match Vasserman */
1121
if (IsVowel(original, current + 1))
1123
MetaphAdd(primary, "A");
1124
MetaphAdd(secondary, "F");
1128
/* need Uomo to match Womo */
1129
MetaphAdd(primary, "A");
1130
MetaphAdd(secondary, "A");
1134
/* Arnow should match Arnoff */
1135
if (((current == last) && IsVowel(original, current - 1))
1136
|| StringAt(original, (current - 1), 5, "EWSKI", "EWSKY",
1137
"OWSKI", "OWSKY", "")
1138
|| StringAt(original, 0, 3, "SCH", ""))
1140
MetaphAdd(primary, "");
1141
MetaphAdd(secondary, "F");
1146
/* polish e.g. 'filipowicz' */
1147
if (StringAt(original, current, 4, "WICZ", "WITZ", ""))
1149
MetaphAdd(primary, "TS");
1150
MetaphAdd(secondary, "FX");
1160
/* french e.g. breaux */
1161
if (!((current == last)
1162
&& (StringAt(original, (current - 3), 3, "IAU", "EAU", "")
1163
|| StringAt(original, (current - 2), 2, "AU", "OU", ""))))
1165
MetaphAdd(primary, "KS");
1166
MetaphAdd(secondary, "KS");
1170
if (StringAt(original, (current + 1), 1, "C", "X", ""))
1177
/* chinese pinyin e.g. 'zhao' */
1178
if (GetAt(original, current + 1) == 'H')
1180
MetaphAdd(primary, "J");
1181
MetaphAdd(secondary, "J");
1185
else if (StringAt(original, (current + 1), 2, "ZO", "ZI", "ZA", "")
1186
|| (SlavoGermanic(original)
1188
&& GetAt(original, current - 1) != 'T')))
1190
MetaphAdd(primary, "S");
1191
MetaphAdd(secondary, "TS");
1195
MetaphAdd(primary, "S");
1196
MetaphAdd(secondary, "S");
1199
if (GetAt(original, current + 1) == 'Z')
1208
/* printf("PRIMARY: %s\n", primary->str);
1209
printf("SECONDARY: %s\n", secondary->str); */
1213
if (primary->length > 4)
1214
SetAt(primary, 4, '\0');
1216
if (secondary->length > 4)
1217
SetAt(secondary, 4, '\0');
1219
*codes = primary->str;
1220
*++codes = secondary->str;
1222
DestroyMetaString(original);
1223
DestroyMetaString(primary);
1224
DestroyMetaString(secondary);