#ifndef lint static char Rcs_Id[] = "$Id: tgood.c,v 1.43 2005/04/14 14:38:23 geoff Exp $"; #endif /* * Copyright 1987-1989, 1992, 1993, 1999, 2001, 2005, Geoff Kuenning, * Claremont, CA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All modifications to the source code must be clearly marked as * such. Binary redistributions based on modified source code * must be clearly marked as modified versions in the documentation * and/or other materials provided with the distribution. * 4. The code that causes the 'ispell -v' command to display a prominent * link to the official ispell Web site may not be removed. * 5. The name of Geoff Kuenning may not be used to endorse or promote * products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY GEOFF KUENNING AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL GEOFF KUENNING OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Table-driven version of good.c. * * Geoff Kuenning, July 1987 */ /* * $Log: tgood.c,v $ * Revision 1.43 2005/04/14 14:38:23 geoff * Update license. Incorporate Ed Avis's changes, which primarily * involve making the expansion table a proper structure (almost a * C++-style object) and moving the code to handle it into a separate * file. * * Revision 1.42 2001/07/25 21:51:46 geoff * Minor license update. * * Revision 1.41 2001/07/23 22:09:15 geoff * Change the expansion code so that the scratch table is allocated * dynamically. This cures a bug that caused munchlist to fail for * languages that used affixes to encode parts of speech, rather than * string transformations, and generated huge numbers of affixes. While * fixing this bug, I also eliminated an obsolete unused parameter from * several functions. * * Revision 1.40 2001/07/23 20:24:04 geoff * Update the copyright and the license. * * Revision 1.39 2000/11/14 07:27:04 geoff * Use messages from msgs.h for the "too many expansions" errors. * * Revision 1.38 2000/08/22 10:52:25 geoff * Fix a whole bunch of signed/unsigned discrepancies. * * Revision 1.37 1999/01/18 03:28:43 geoff * Turn many char declarations into unsigned char, so that we won't have * sign-extension problems. * * Revision 1.36 1999/01/07 01:57:43 geoff * Update the copyright. * * Revision 1.35 1997/12/21 23:10:24 geoff * Fix the generation of expansions so that only one unique expansion is * produced for any given root/affix combination. For example, if both * the A and the N flags can add "n" to the end of a word (as in the * deutsch.aff file), expanding foo/AN will produce "foon" only once. * This has the side effect that the "-e4" option will generate a more * accurate ratio for flag productivity, which in turn means that * munchlist will do a better job of choosing an optimal set of roots and * flags. * * Revision 1.34 1997/12/03 05:28:13 geoff * Fix a bug that caused suffix expansions to be printed with the wrong * capitalization when all but one character of the root was stripped. * * Revision 1.33 1997/12/02 06:25:09 geoff * Get rid of some compile options that really shouldn't be optional. * * Revision 1.32 1994/11/02 06:56:16 geoff * Remove the anyword feature, which I've decided is a bad idea. * * Revision 1.31 1994/10/25 05:46:25 geoff * Add support for the FF_ANYWORD (affix applies to all words, even if * flag bit isn't set) flag option. * * Revision 1.30 1994/05/24 06:23:08 geoff * Don't create a hit if "allhits" is clear and capitalization * mismatches. This cures a bug where a word could be in the dictionary * and yet not found. * * Revision 1.29 1994/05/17 06:44:21 geoff * Add support for controlled compound formation and the COMPOUNDONLY * option to affix flags. * * Revision 1.28 1994/01/25 07:12:13 geoff * Get rid of all old RCS log lines in preparation for the 3.1 release. * */ #include #include "config.h" #include "ispell.h" #include "msgs.h" #include "proto.h" #include "exp_table.h" void chk_aff P ((ichar_t * word, ichar_t * ucword, int len, int ignoreflagbits, int allhits, int pfxopts, int sfxopts)); static void pfx_list_chk P ((ichar_t * word, ichar_t * ucword, int len, int optflags, int sfxopts, struct flagptr * ind, int ignoreflagbits, int allhits)); static void chk_suf P ((ichar_t * word, ichar_t * ucword, int len, int optflags, struct flagent * pfxent, int ignoreflagbits, int allhits)); static void suf_list_chk P ((ichar_t * word, ichar_t * ucword, int len, struct flagptr * ind, int optflags, struct flagent * pfxent, int ignoreflagbits, int allhits)); int expand_pre P ((unsigned char * croot, ichar_t * rootword, MASKTYPE mask[], int option, unsigned char * extra)); static void gen_pre_expansion P ((ichar_t * rootword, struct flagent * flent, MASKTYPE mask[], struct exp_table * exptable)); int expand_suf P ((unsigned char * croot, ichar_t * rootword, MASKTYPE mask[], int optflags, int option, unsigned char * extra)); static void expand_suf_into_table P ((ichar_t * rootword, MASKTYPE mask[], int optflags, struct exp_table * exptable, MASKTYPE existing_flags[])); static void gen_suf_expansion P ((ichar_t * rootword, struct flagent * flent, struct exp_table * exptable, MASKTYPE existing_flags[])); static void forcelc P ((ichar_t * dst, int len)); static char * flags_str P ((MASKTYPE flags)); static int output_expansions P ((struct exp_table * exptable, int option, unsigned char * croot, unsigned char * extra)); /* Check possible affixes */ void chk_aff (word, ucword, len, ignoreflagbits, allhits, pfxopts, sfxopts) ichar_t * word; /* Word to be checked */ ichar_t * ucword; /* Upper-case-only copy of word */ int len; /* The length of word/ucword */ int ignoreflagbits; /* Ignore whether affix is legal */ int allhits; /* Keep going after first hit */ int pfxopts; /* Options to apply to prefixes */ int sfxopts; /* Options to apply to suffixes */ { register ichar_t * cp; /* Pointer to char to index on */ struct flagptr * ind; /* Flag index table to test */ pfx_list_chk (word, ucword, len, pfxopts, sfxopts, &pflagindex[0], ignoreflagbits, allhits); cp = ucword; ind = &pflagindex[*cp++]; while (ind->numents == 0 && ind->pu.fp != NULL) { if (*cp == 0) return; if (ind->pu.fp[0].numents) { pfx_list_chk (word, ucword, len, pfxopts, sfxopts, &ind->pu.fp[0], ignoreflagbits, allhits); if (numhits && !allhits && !cflag && !ignoreflagbits) return; } ind = &ind->pu.fp[*cp++]; } pfx_list_chk (word, ucword, len, pfxopts, sfxopts, ind, ignoreflagbits, allhits); if (numhits && !allhits && !cflag && !ignoreflagbits) return; chk_suf (word, ucword, len, sfxopts, (struct flagent *) NULL, ignoreflagbits, allhits); } /* Check some prefix flags */ static void pfx_list_chk (word, ucword, len, optflags, sfxopts, ind, ignoreflagbits, allhits) ichar_t * word; /* Word to be checked */ ichar_t * ucword; /* Upper-case-only word */ int len; /* The length of ucword */ int optflags; /* Options to apply */ int sfxopts; /* Options to apply to suffixes */ struct flagptr * ind; /* Flag index table */ int ignoreflagbits; /* Ignore whether affix is legal */ int allhits; /* Keep going after first hit */ { int cond; /* Condition number */ register ichar_t * cp; /* Pointer into end of ucword */ struct dent * dent; /* Dictionary entry we found */ int entcount; /* Number of entries to process */ register struct flagent * flent; /* Current table entry */ int preadd; /* Length added to tword2 as prefix */ register int tlen; /* Length of tword */ ichar_t tword[INPUTWORDLEN + 4 * MAXAFFIXLEN + 4]; /* Tmp cpy */ ichar_t tword2[sizeof tword]; /* 2nd copy for ins_root_cap */ for (flent = ind->pu.ent, entcount = ind->numents; entcount > 0; flent++, entcount--) { /* * If this is a compound-only affix, ignore it unless we're * looking for that specific thing. */ if ((flent->flagflags & FF_COMPOUNDONLY) != 0 && (optflags & FF_COMPOUNDONLY) == 0) continue; /* * In COMPOUND_CONTROLLED mode, the FF_COMPOUNDONLY bit must * match exactly. */ if (compoundflag == COMPOUND_CONTROLLED && ((flent->flagflags ^ optflags) & FF_COMPOUNDONLY) != 0) continue; /* * See if the prefix matches. */ tlen = len - flent->affl; if (tlen > 0 && (flent->affl == 0 || icharncmp (flent->affix, ucword, flent->affl) == 0) && tlen + flent->stripl >= flent->numconds) { /* * The prefix matches. Remove it, replace it by the "strip" * string (if any), and check the original conditions. */ if (flent->stripl) (void) icharcpy (tword, flent->strip); (void) icharcpy (tword + flent->stripl, ucword + flent->affl); cp = tword; for (cond = 0; cond < flent->numconds; cond++) { if ((flent->conds[*cp++] & (1 << cond)) == 0) break; } if (cond >= flent->numconds) { /* * The conditions match. See if the word is in the * dictionary. */ tlen += flent->stripl; if (cflag) flagpr (tword, BITTOCHAR (flent->flagbit), flent->stripl, flent->affl, -1, 0); else if (ignoreflagbits) { if ((dent = lookup (tword, 1)) != NULL) { cp = tword2; if (flent->affl) { (void) icharcpy (cp, flent->affix); cp += flent->affl; *cp++ = '+'; } preadd = cp - tword2; (void) icharcpy (cp, tword); cp += tlen; if (flent->stripl) { *cp++ = '-'; (void) icharcpy (cp, flent->strip); } (void) ins_root_cap (tword2, word, flent->stripl, preadd, 0, (cp - tword2) - tlen - preadd, dent, flent, (struct flagent *) NULL); } } else if ((dent = lookup (tword, 1)) != NULL && TSTMASKBIT (dent->mask, flent->flagbit)) { if (numhits < MAX_HITS) { hits[numhits].dictent = dent; hits[numhits].prefix = flent; hits[numhits].suffix = NULL; numhits++; } if (!allhits) { if (cap_ok (word, &hits[0], len)) return; numhits = 0; } } /* * Handle cross-products. */ if (flent->flagflags & FF_CROSSPRODUCT) chk_suf (word, tword, tlen, sfxopts | FF_CROSSPRODUCT, flent, ignoreflagbits, allhits); } } } } /* Check possible suffixes */ static void chk_suf (word, ucword, len, optflags, pfxent, ignoreflagbits, allhits) ichar_t * word; /* Word to be checked */ ichar_t * ucword; /* Upper-case-only word */ int len; /* The length of ucword */ int optflags; /* Affix option flags */ struct flagent * pfxent; /* Prefix flag entry if cross-prod */ int ignoreflagbits; /* Ignore whether affix is legal */ int allhits; /* Keep going after first hit */ { register ichar_t * cp; /* Pointer to char to index on */ struct flagptr * ind; /* Flag index table to test */ suf_list_chk (word, ucword, len, &sflagindex[0], optflags, pfxent, ignoreflagbits, allhits); cp = ucword + len - 1; ind = &sflagindex[*cp]; while (ind->numents == 0 && ind->pu.fp != NULL) { if (cp == ucword) return; if (ind->pu.fp[0].numents) { suf_list_chk (word, ucword, len, &ind->pu.fp[0], optflags, pfxent, ignoreflagbits, allhits); if (numhits != 0 && !allhits && !cflag && !ignoreflagbits) return; } ind = &ind->pu.fp[*--cp]; } suf_list_chk (word, ucword, len, ind, optflags, pfxent, ignoreflagbits, allhits); } static void suf_list_chk (word, ucword, len, ind, optflags, pfxent, ignoreflagbits, allhits) ichar_t * word; /* Word to be checked */ ichar_t * ucword; /* Upper-case-only word */ int len; /* The length of ucword */ struct flagptr * ind; /* Flag index table */ int optflags; /* Affix option flags */ struct flagent * pfxent; /* Prefix flag entry if crossonly */ int ignoreflagbits; /* Ignore whether affix is legal */ int allhits; /* Keep going after first hit */ { register ichar_t * cp; /* Pointer into end of ucword */ int cond; /* Condition number */ struct dent * dent; /* Dictionary entry we found */ int entcount; /* Number of entries to process */ register struct flagent * flent; /* Current table entry */ int preadd; /* Length added to tword2 as prefix */ register int tlen; /* Length of tword */ ichar_t tword[INPUTWORDLEN + 4 * MAXAFFIXLEN + 4]; /* Tmp cpy */ ichar_t tword2[sizeof tword]; /* 2nd copy for ins_root_cap */ (void) icharcpy (tword, ucword); for (flent = ind->pu.ent, entcount = ind->numents; entcount > 0; flent++, entcount--) { if ((optflags & FF_CROSSPRODUCT) != 0 && (flent->flagflags & FF_CROSSPRODUCT) == 0) continue; /* * If this is a compound-only affix, ignore it unless we're * looking for that specific thing. */ if ((flent->flagflags & FF_COMPOUNDONLY) != 0 && (optflags & FF_COMPOUNDONLY) == 0) continue; /* * In COMPOUND_CONTROLLED mode, the FF_COMPOUNDONLY bit must * match exactly. */ if (compoundflag == COMPOUND_CONTROLLED && ((flent->flagflags ^ optflags) & FF_COMPOUNDONLY) != 0) continue; /* * See if the suffix matches. */ tlen = len - flent->affl; if (tlen > 0 && (flent->affl == 0 || icharcmp (flent->affix, ucword + tlen) == 0) && tlen + flent->stripl >= flent->numconds) { /* * The suffix matches. Remove it, replace it by the "strip" * string (if any), and check the original conditions. */ (void) icharcpy (tword, ucword); cp = tword + tlen; if (flent->stripl) { (void) icharcpy (cp, flent->strip); tlen += flent->stripl; cp = tword + tlen; } else *cp = '\0'; for (cond = flent->numconds; --cond >= 0; ) { if ((flent->conds[*--cp] & (1 << cond)) == 0) break; } if (cond < 0) { /* * The conditions match. See if the word is in the * dictionary. */ if (cflag) { if (optflags & FF_CROSSPRODUCT) flagpr (tword, BITTOCHAR (pfxent->flagbit), pfxent->stripl, pfxent->affl, BITTOCHAR (flent->flagbit), flent->affl); else flagpr (tword, -1, 0, 0, BITTOCHAR (flent->flagbit), flent->affl); } else if (ignoreflagbits) { if ((dent = lookup (tword, 1)) != NULL) { cp = tword2; if ((optflags & FF_CROSSPRODUCT) && pfxent->affl != 0) { (void) icharcpy (cp, pfxent->affix); cp += pfxent->affl; *cp++ = '+'; } preadd = cp - tword2; (void) icharcpy (cp, tword); cp += tlen; if ((optflags & FF_CROSSPRODUCT) && pfxent->stripl != 0) { *cp++ = '-'; (void) icharcpy (cp, pfxent->strip); cp += pfxent->stripl; } if (flent->stripl) { *cp++ = '-'; (void) icharcpy (cp, flent->strip); cp += flent->stripl; } if (flent->affl) { *cp++ = '+'; (void) icharcpy (cp, flent->affix); cp += flent->affl; } (void) ins_root_cap (tword2, word, (optflags & FF_CROSSPRODUCT) ? pfxent->stripl : 0, preadd, flent->stripl, (cp - tword2) - tlen - preadd, dent, pfxent, flent); } } else if ((dent = lookup (tword, 1)) != NULL && TSTMASKBIT (dent->mask, flent->flagbit) && ((optflags & FF_CROSSPRODUCT) == 0 || TSTMASKBIT (dent->mask, pfxent->flagbit))) { if (numhits < MAX_HITS) { hits[numhits].dictent = dent; hits[numhits].prefix = pfxent; hits[numhits].suffix = flent; numhits++; } if (!allhits) { if (cap_ok (word, &hits[0], len)) return; numhits = 0; } } } } } } /* * Expand a dictionary prefix entry */ int expand_pre (croot, rootword, mask, option, extra) unsigned char * croot; /* Char version of rootword */ ichar_t * rootword; /* Root word to expand */ register MASKTYPE mask[]; /* Mask bits to expand on */ int option; /* Option, see expandmode */ unsigned char * extra; /* Extra info to add to line */ { int entcount; /* No. of entries to process */ int explength; /* Length of expansions */ static struct exp_table exptable; /* Table of expansions */ register struct flagent * flent; /* Current table entry */ exp_table_init(&exptable, rootword); for (flent = pflaglist, entcount = numpflags; entcount > 0; flent++, entcount--) { if (TSTMASKBIT (mask, flent->flagbit)) gen_pre_expansion (rootword, flent, mask, &exptable); } explength = output_expansions (&exptable, option, croot, extra); (void) exp_table_empty (&exptable); return explength; } /* Generate a prefix expansion, modifying the table passed in */ static void gen_pre_expansion (rootword, flent, mask, exptable) register ichar_t * rootword; /* Root word to expand */ register struct flagent * flent; /* Current table entry */ MASKTYPE mask[]; /* Mask bits to expand on */ struct exp_table * exptable; /* Ptr to tbl of expansions */ { MASKTYPE applied[MASKSIZE]; /* Flag being applied */ int cond; /* Current condition number */ register ichar_t * nextc; /* Next case choice */ register unsigned char * result; /* Result of expansion */ int tlen; /* Length of tword */ ichar_t tword[INPUTWORDLEN + MAXAFFIXLEN]; /* Temp */ tlen = icharlen (rootword); if (flent->numconds > tlen) return; tlen -= flent->stripl; if (tlen <= 0) return; tlen += flent->affl; for (cond = 0, nextc = rootword; cond < flent->numconds; cond++) { if ((flent->conds[mytoupper (*nextc++)] & (1 << cond)) == 0) return; } /* * The conditions are satisfied. Copy the word, add the prefix, * and make it the proper case. This code is carefully written * to match that ins_cap and cap_ok. Note that the affix, as * inserted, is uppercase. * * There is a tricky bit here: if the root is capitalized, we * want a capitalized result. If the root is followcase, however, * we want to duplicate the case of the first remaining letter * of the root. In other words, "Loved/U" should generate "Unloved", * but "LOved/U" should generate "UNLOved" and "lOved/U" should * produce "unlOved". */ if (flent->affl) { (void) icharcpy (tword, flent->affix); nextc = tword + flent->affl; } (void) icharcpy (nextc, rootword + flent->stripl); if (myupper (rootword[0])) { /* We must distinguish followcase from capitalized and all-upper */ for (nextc = rootword + 1; *nextc; nextc++) { if (!myupper (*nextc)) break; } if (*nextc) { /* It's a followcase or capitalized word. Figure out which. */ for ( ; *nextc; nextc++) { if (myupper (*nextc)) break; } if (*nextc) { /* It's followcase. */ if (!myupper (tword[flent->affl])) forcelc (tword, flent->affl); } else { /* It's capitalized */ forcelc (tword + 1, tlen - 1); } } } else { /* Followcase or all-lower, we don't care which */ if (!myupper (*nextc)) forcelc (tword, flent->affl); } /* * We've generated the result. If it's not already in the * expansion table, add it at the end. */ result = ichartosstr (tword, 1); BZERO (applied, sizeof applied); SETMASKBIT (applied, flent->flagbit); add_expansion_copy(exptable, (char *) result, applied); if (flent->flagflags & FF_CROSSPRODUCT) expand_suf_into_table (tword, mask, FF_CROSSPRODUCT, exptable, applied); } /* * Expand a dictionary suffix entry */ int expand_suf (croot, rootword, mask, optflags, option, extra) unsigned char * croot; /* Char version of rootword */ ichar_t * rootword; /* Root word to expand */ register MASKTYPE mask[]; /* Mask bits to expand on */ int optflags; /* Affix option flags */ int option; /* Option, see expandmode */ unsigned char * extra; /* Extra info to add to line */ { int explength; /* Length of expansions */ struct exp_table exptable; /* Table of expansions */ int i; /* For zeroing flags */ MASKTYPE nullflags[MASKSIZE]; /* Flag being applied */ for (i = 0; i < MASKSIZE; i++) nullflags[i] = 0; exp_table_init (&exptable, rootword); expand_suf_into_table(rootword, mask, optflags, &exptable, nullflags); explength = output_expansions (&exptable, option, croot, extra); (void) exp_table_empty (&exptable); return explength; } /* * Expand a dictionary suffix entry and insert it into an expansion table */ static void expand_suf_into_table (rootword, mask, optflags, exptable, existing_flags) ichar_t * rootword; /* Root word to expand */ register MASKTYPE mask[]; /* Mask bits to expand on */ int optflags; /* Affix option flags */ struct exp_table * exptable; /* Table of expansions */ MASKTYPE existing_flags[]; /* Flags already applied */ { int entcount; /* No. of entries to process */ register struct flagent * flent; /* Current table entry */ for (flent = sflaglist, entcount = numsflags; entcount > 0; flent++, entcount--) { if (TSTMASKBIT (mask, flent->flagbit)) { if ((optflags & FF_CROSSPRODUCT) == 0 || (flent->flagflags & FF_CROSSPRODUCT)) (void) gen_suf_expansion (rootword, flent, exptable, existing_flags); } } } /* Generate a suffix expansion */ static void gen_suf_expansion (rootword, flent, exptable, existing_flags) register ichar_t * rootword; /* Root word to expand */ register struct flagent * flent; /* Current table entry */ struct exp_table * exptable; /* Table of expansions */ MASKTYPE * existing_flags; /* Flags already applied */ { int cond; /* Current condition number */ register ichar_t * nextc; /* Next case choice */ register unsigned char * result; /* Result of expansion */ int tlen; /* Length of tword */ ichar_t tword[INPUTWORDLEN + MAXAFFIXLEN]; /* Temp */ int wascapitalized; /* NZ if capitalized root */ tlen = icharlen (rootword); cond = flent->numconds; if (cond > tlen) return; if (tlen - flent->stripl <= 0) return; wascapitalized = myupper (rootword[0]); for (nextc = rootword + tlen; --cond >= 0; ) { if ((flent->conds[mytoupper (*--nextc)] & (1 << cond)) == 0) return; if (nextc > rootword && myupper (*nextc)) wascapitalized = 0; } while (--nextc > rootword) { if (myupper (*nextc)) wascapitalized = 0; } /* * The conditions are satisfied. Copy the word, add the suffix, * and make it match the case of the last remaining character of the * root. Again, this code carefully matches ins_cap and cap_ok. * * There is one tricky case to watch out for here. If the root * was simply capitalized, and the stripping removed all but the * initial character of the root, the above algorithm will * generate an all-caps result. However, ins_cap and cap_ok would * generate a simple capitalization, because a captype of * CAPITALIZED would override FOLLOWCASE and ALLCAPS. So we have * to deal with this as a special case. */ (void) icharcpy (tword, rootword); nextc = tword + tlen - flent->stripl; if (flent->affl) { (void) icharcpy (nextc, flent->affix); if ((nextc == tword + 1 && wascapitalized) || !myupper (nextc[-1])) forcelc (nextc, flent->affl); } else *nextc = 0; /* * We've generated the result. If it's not already in the * expansion table, add it at the end. */ result = ichartosstr (tword, 1); SETMASKBIT (existing_flags, flent->flagbit); (void) add_expansion_copy(exptable, (char *) result, existing_flags); } static void forcelc (dst, len) /* Force to lowercase */ register ichar_t * dst; /* Destination to modify */ register int len; /* Length to copy */ { for ( ; --len >= 0; dst++) *dst = mytolower (*dst); } /* * Convert a mask of flags to a string of flag chars, using a static * buffer. * * Assume 26 letters in the alphabet :-). In practice you probably * won't get more than two flags at once. */ static char * flags_str (flags) MASKTYPE flags; { static char flags_str_buf[MASKSIZE + 1]; int i; int length = 0; for (i = 0; i < MASKSIZE; i++) { if (TSTMASKBIT (&flags, i)) flags_str_buf[length++] = BITTOCHAR (i); } flags_str_buf[length] = '\0'; return flags_str_buf; } /* * Print an expansion table in the manner required by ispell's various * -e options (or at least do some of the printing required). Returns * the total length of all expansions. * * P.S. Yes, I'm embarrassed by the magic numbers. */ static int output_expansions (exptable, option, croot, extra) struct exp_table * exptable; /* Expansions to print */ int option; /* Num. of -e option */ unsigned char * croot; /* Original "word/flags" */ unsigned char * extra; /* Extra info to add to line */ { int explength = 0; /* Expansion size (chars) */ int i; /* Loop index */ const ichar_t * rootword; /* Root of the expansion */ rootword = get_orig_word (exptable); /* Print in reverse order to match old behavior */ for (i = num_expansions (exptable); --i >= 0; ) { const char * expansion = get_expansion (exptable, i); if (option == 3) (void) printf ("\n%s", (char *) croot); else if (option == 5) { char * fs = flags_str (get_flags (exptable, i)); if (strlen (fs) != 0) (void) printf ("\n%s+%s", ichartosstr (rootword, 1), fs); else (void) printf ("\n%s", ichartosstr (rootword, 1)); } if (option != 4) (void) printf (" %s%s", expansion, (char *) extra); explength += strlen (expansion); } return explength; }