Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(806)

Side by Side Diff: third_party/hunspell/src/hunspell/hashmgr.cxx

Issue 2239005: Merges our hunspell change to hunspell 1.2.10.... (Closed) Base URL: svn://chrome-svn.corp.google.com/chrome/trunk/deps/
Patch Set: '' Created 10 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 #include "license.hunspell" 1 #include "license.hunspell"
2 #include "license.myspell" 2 #include "license.myspell"
3 3
4 #include <stdlib.h> 4 #include <stdlib.h>
5 #include <string.h> 5 #include <string.h>
6 #include <stdio.h> 6 #include <stdio.h>
7 #include <ctype.h> 7 #include <ctype.h>
8 8
9 #include "hashmgr.hxx" 9 #include "hashmgr.hxx"
10 #include "csutil.hxx" 10 #include "csutil.hxx"
11 #include "atypes.hxx" 11 #include "atypes.hxx"
12 12
13 // build a hash table from a munched word list 13 // build a hash table from a munched word list
14 14
15 #ifdef HUNSPELL_CHROME_CLIENT
16 HashMgr::HashMgr(hunspell::BDictReader* reader)
17 {
18 bdict_reader = reader;
19 #else
15 HashMgr::HashMgr(const char * tpath, const char * apath, const char * key) 20 HashMgr::HashMgr(const char * tpath, const char * apath, const char * key)
16 { 21 {
22 #endif
17 tablesize = 0; 23 tablesize = 0;
18 tableptr = NULL; 24 tableptr = NULL;
19 flag_mode = FLAG_CHAR; 25 flag_mode = FLAG_CHAR;
20 complexprefixes = 0; 26 complexprefixes = 0;
21 utf8 = 0; 27 utf8 = 0;
22 langnum = 0; 28 langnum = 0;
23 lang = NULL; 29 lang = NULL;
24 enc = NULL; 30 enc = NULL;
25 csconv = 0; 31 csconv = 0;
26 ignorechars = NULL; 32 ignorechars = NULL;
27 ignorechars_utf16 = NULL; 33 ignorechars_utf16 = NULL;
28 ignorechars_utf16_len = 0; 34 ignorechars_utf16_len = 0;
29 numaliasf = 0; 35 numaliasf = 0;
30 aliasf = NULL; 36 aliasf = NULL;
31 numaliasm = 0; 37 numaliasm = 0;
32 aliasm = NULL; 38 aliasm = NULL;
33 forbiddenword = FORBIDDENWORD; // forbidden word signing flag 39 forbiddenword = FORBIDDENWORD; // forbidden word signing flag
40 #ifdef HUNSPELL_CHROME_CLIENT
41 // No tables to load, just the AF lines.
42 load_config(NULL, NULL);
43 int ec = LoadAFLines();
44 #else
34 load_config(apath, key); 45 load_config(apath, key);
35 int ec = load_tables(tpath, key); 46 int ec = load_tables(tpath, key);
47 #endif
36 if (ec) { 48 if (ec) {
37 /* error condition - what should we do here */ 49 /* error condition - what should we do here */
38 HUNSPELL_WARNING(stderr, "Hash Manager Error : %d\n",ec); 50 HUNSPELL_WARNING(stderr, "Hash Manager Error : %d\n",ec);
39 if (tableptr) { 51 if (tableptr) {
40 free(tableptr); 52 free(tableptr);
41 tableptr = NULL; 53 tableptr = NULL;
42 } 54 }
43 tablesize = 0; 55 tablesize = 0;
44 } 56 }
45 } 57 }
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
84 if (utf8) free_utf_tbl(); 96 if (utf8) free_utf_tbl();
85 #endif 97 #endif
86 #endif 98 #endif
87 99
88 if (enc) free(enc); 100 if (enc) free(enc);
89 if (lang) free(lang); 101 if (lang) free(lang);
90 102
91 if (ignorechars) free(ignorechars); 103 if (ignorechars) free(ignorechars);
92 if (ignorechars_utf16) free(ignorechars_utf16); 104 if (ignorechars_utf16) free(ignorechars_utf16);
93 105
106 #ifdef HUNSPELL_CHROME_CLIENT
107 EmptyHentryCache();
108 for (std::vector<std::string*>::iterator it = pointer_to_strings_.begin();
109 it != pointer_to_strings_.end(); ++it) {
110 delete *it;
111 }
112 #endif
113
94 #ifdef MOZILLA_CLIENT 114 #ifdef MOZILLA_CLIENT
95 delete [] csconv; 115 delete [] csconv;
96 #endif 116 #endif
97 } 117 }
98 118
119 #ifdef HUNSPELL_CHROME_CLIENT
120 void HashMgr::EmptyHentryCache() {
121 // We need to delete each cache entry, and each additional one in the linked
122 // list of homonyms.
123 for (HEntryCache::iterator i = hentry_cache.begin();
124 i != hentry_cache.end(); ++i) {
125 hentry* cur = i->second;
126 while (cur) {
127 hentry* next = cur->next_homonym;
128 DeleteHashEntry(cur);
129 cur = next;
130 }
131 }
132 hentry_cache.clear();
133 }
134 #endif
135
99 // lookup a root word in the hashtable 136 // lookup a root word in the hashtable
100 137
101 struct hentry * HashMgr::lookup(const char *word) const 138 struct hentry * HashMgr::lookup(const char *word) const
102 { 139 {
140 #ifdef HUNSPELL_CHROME_CLIENT
141 int affix_ids[hunspell::BDict::MAX_AFFIXES_PER_WORD];
142 int affix_count = bdict_reader->FindWord(word, affix_ids);
143 if (affix_count == 0) { // look for custom added word
144 std::map<base::StringPiece, int>::const_iterator iter =
145 custom_word_to_affix_id_map_.find(word);
146 if (iter != custom_word_to_affix_id_map_.end()) {
147 affix_count = 1;
148 affix_ids[0] = iter->second;
149 }
150 }
151
152 static const int kMaxWordLen = 128;
153 static char word_buf[kMaxWordLen];
154 // To take account of null-termination, we use upto 127.
155 strncpy(word_buf, word, kMaxWordLen - 1);
156
157 return AffixIDsToHentry(word_buf, affix_ids, affix_count);
158 #else
103 struct hentry * dp; 159 struct hentry * dp;
104 if (tableptr) { 160 if (tableptr) {
105 dp = tableptr[hash(word)]; 161 dp = tableptr[hash(word)];
106 if (!dp) return NULL; 162 if (!dp) return NULL;
107 for ( ; dp != NULL; dp = dp->next) { 163 for ( ; dp != NULL; dp = dp->next) {
108 if (strcmp(word, dp->word) == 0) return dp; 164 if (strcmp(word, dp->word) == 0) return dp;
109 } 165 }
110 } 166 }
111 return NULL; 167 return NULL;
168 #endif
112 } 169 }
113 170
114 // add a word to the hash table (private) 171 // add a word to the hash table (private)
115 int HashMgr::add_word(const char * word, int wbl, int wcl, unsigned short * aff, 172 int HashMgr::add_word(const char * word, int wbl, int wcl, unsigned short * aff,
116 int al, const char * desc, bool onlyupcase) 173 int al, const char * desc, bool onlyupcase)
117 { 174 {
175 #ifndef HUNSPELL_CHROME_CLIENT
118 bool upcasehomonym = false; 176 bool upcasehomonym = false;
119 int descl = desc ? (aliasm ? sizeof(short) : strlen(desc) + 1) : 0; 177 int descl = desc ? (aliasm ? sizeof(short) : strlen(desc) + 1) : 0;
120 // variable-length hash record with word and optional fields 178 // variable-length hash record with word and optional fields
121 struct hentry* hp = 179 struct hentry* hp =
122 (struct hentry *) malloc (sizeof(struct hentry) + wbl + descl); 180 (struct hentry *) malloc (sizeof(struct hentry) + wbl + descl);
123 if (!hp) return 1; 181 if (!hp) return 1;
124 char * hpw = hp->word; 182 char * hpw = hp->word;
125 strcpy(hpw, word); 183 strcpy(hpw, word);
126 if (ignorechars != NULL) { 184 if (ignorechars != NULL) {
127 if (utf8) { 185 if (utf8) {
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after
199 upcasehomonym = true; 257 upcasehomonym = true;
200 } 258 }
201 } 259 }
202 if (!upcasehomonym) { 260 if (!upcasehomonym) {
203 dp->next = hp; 261 dp->next = hp;
204 } else { 262 } else {
205 // remove hidden onlyupcase homonym 263 // remove hidden onlyupcase homonym
206 if (hp->astr) free(hp->astr); 264 if (hp->astr) free(hp->astr);
207 free(hp); 265 free(hp);
208 } 266 }
267 #else
268 std::map<base::StringPiece, int>::iterator iter =
269 custom_word_to_affix_id_map_.find(word);
270 if(iter == custom_word_to_affix_id_map_.end()) { // word needs to be added
271 std::string* new_string_word = new std::string(word);
272 pointer_to_strings_.push_back(new_string_word);
273 base::StringPiece sp(*(new_string_word));
274 custom_word_to_affix_id_map_[sp] = 0; // no affixes for custom words
275 return 1;
276 }
277 #endif
209 return 0; 278 return 0;
210 } 279 }
211 280
212 int HashMgr::add_hidden_capitalized_word(char * word, int wbl, int wcl, 281 int HashMgr::add_hidden_capitalized_word(char * word, int wbl, int wcl,
213 unsigned short * flags, int al, char * dp, int captype) 282 unsigned short * flags, int al, char * dp, int captype)
214 { 283 {
215 // add inner capitalized forms to handle the following allcap forms: 284 // add inner capitalized forms to handle the following allcap forms:
216 // Mixed caps: OpenOffice.org -> OPENOFFICE.ORG 285 // Mixed caps: OpenOffice.org -> OPENOFFICE.ORG
217 // Allcaps with suffixes: CIA's -> CIA'S 286 // Allcaps with suffixes: CIA's -> CIA'S
218 if (((captype == HUHCAP) || (captype == HUHINITCAP) || 287 if (((captype == HUHCAP) || (captype == HUHINITCAP) ||
(...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after
332 } 401 }
333 return add_hidden_capitalized_word((char *) word, wbl, wcl, dp->astr, dp ->alen, NULL, captype); 402 return add_hidden_capitalized_word((char *) word, wbl, wcl, dp->astr, dp ->alen, NULL, captype);
334 } 403 }
335 return 1; 404 return 1;
336 } 405 }
337 406
338 // walk the hash table entry by entry - null at end 407 // walk the hash table entry by entry - null at end
339 // initialize: col=-1; hp = NULL; hp = walk_hashtable(&col, hp); 408 // initialize: col=-1; hp = NULL; hp = walk_hashtable(&col, hp);
340 struct hentry * HashMgr::walk_hashtable(int &col, struct hentry * hp) const 409 struct hentry * HashMgr::walk_hashtable(int &col, struct hentry * hp) const
341 { 410 {
411 #ifdef HUNSPELL_CHROME_CLIENT
412 // Return NULL if dictionary is not valid.
413 if (!bdict_reader->IsValid())
414 return NULL;
415
416 // This function is only ever called by one place and not nested. We can
417 // therefore keep static state between calls and use |col| as a "reset" flag
418 // to avoid changing the API. It is set to -1 for the first call.
419 static hunspell::WordIterator word_iterator =
420 bdict_reader->GetAllWordIterator();
421 if (col < 0) {
422 col = 1;
423 word_iterator = bdict_reader->GetAllWordIterator();
424 }
425
426 int affix_ids[hunspell::BDict::MAX_AFFIXES_PER_WORD];
427 static const int kMaxWordLen = 128;
428 static char word[kMaxWordLen];
429 int affix_count = word_iterator.Advance(word, kMaxWordLen, affix_ids);
430 if (affix_count == 0)
431 return NULL;
432 short word_len = static_cast<short>(strlen(word));
433
434 // Since hunspell 1.2.8, an hentry struct becomes a variable-length struct,
435 // i.e. a struct which uses its array 'word[1]' as a variable-length array.
436 // As noted above, this function is not nested. So, we just use a static
437 // struct which consists of an hentry and a char[kMaxWordLen], and initialize
438 // the static struct and return it for now.
439 // No need to create linked lists for the extra affixes.
440 static struct {
441 hentry entry;
442 char word[kMaxWordLen];
443 } hash_entry;
444
445 return InitHashEntry(&hash_entry.entry, sizeof(hash_entry),
446 &word[0], word_len, affix_ids[0]);
447 #else
342 if (hp && hp->next != NULL) return hp->next; 448 if (hp && hp->next != NULL) return hp->next;
343 for (col++; col < tablesize; col++) { 449 for (col++; col < tablesize; col++) {
344 if (tableptr[col]) return tableptr[col]; 450 if (tableptr[col]) return tableptr[col];
345 } 451 }
346 // null at end and reset to start 452 // null at end and reset to start
347 col = -1; 453 col = -1;
348 return NULL; 454 return NULL;
455 #endif
349 } 456 }
350 457
351 // load a munched word list and build a hash table on the fly 458 // load a munched word list and build a hash table on the fly
352 int HashMgr::load_tables(const char * tpath, const char * key) 459 int HashMgr::load_tables(const char * tpath, const char * key)
353 { 460 {
461 #ifndef HUNSPELL_CHROME_CLIENT
354 int al; 462 int al;
355 char * ap; 463 char * ap;
356 char * dp; 464 char * dp;
357 char * dp2; 465 char * dp2;
358 unsigned short * flags; 466 unsigned short * flags;
359 char * ts; 467 char * ts;
360 468
361 // open dictionary file 469 // open dictionary file
362 FileMgr * dict = new FileMgr(tpath, key); 470 FileMgr * dict = new FileMgr(tpath, key);
363 if (dict == NULL) return 1; 471 if (dict == NULL) return 1;
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after
463 int wcl = get_clen_and_captype(ts, wbl, &captype); 571 int wcl = get_clen_and_captype(ts, wbl, &captype);
464 // add the word and its index plus its capitalized form optionally 572 // add the word and its index plus its capitalized form optionally
465 if (add_word(ts,wbl,wcl,flags,al,dp, false) || 573 if (add_word(ts,wbl,wcl,flags,al,dp, false) ||
466 add_hidden_capitalized_word(ts, wbl, wcl, flags, al, dp, captype)) { 574 add_hidden_capitalized_word(ts, wbl, wcl, flags, al, dp, captype)) {
467 delete dict; 575 delete dict;
468 return 5; 576 return 5;
469 } 577 }
470 } 578 }
471 579
472 delete dict; 580 delete dict;
581 #endif
473 return 0; 582 return 0;
474 } 583 }
475 584
476 // the hash function is a simple load and rotate 585 // the hash function is a simple load and rotate
477 // algorithm borrowed 586 // algorithm borrowed
478 587
479 int HashMgr::hash(const char * word) const 588 int HashMgr::hash(const char * word) const
480 { 589 {
590 #ifdef HUNSPELL_CHROME_CLIENT
591 return 0;
592 #else
481 long hv = 0; 593 long hv = 0;
482 for (int i=0; i < 4 && *word != 0; i++) 594 for (int i=0; i < 4 && *word != 0; i++)
483 hv = (hv << 8) | (*word++); 595 hv = (hv << 8) | (*word++);
484 while (*word != 0) { 596 while (*word != 0) {
485 ROTATE(hv,ROTATE_LEN); 597 ROTATE(hv,ROTATE_LEN);
486 hv ^= (*word++); 598 hv ^= (*word++);
487 } 599 }
488 return (unsigned long) hv % tablesize; 600 return (unsigned long) hv % tablesize;
601 #endif
489 } 602 }
490 603
491 int HashMgr::decode_flags(unsigned short ** result, char * flags, FileMgr * af) { 604 int HashMgr::decode_flags(unsigned short ** result, char * flags, FileMgr * af) {
492 int len; 605 int len;
493 if (*flags == '\0') { 606 if (*flags == '\0') {
494 HUNSPELL_WARNING(stderr, "error: line %d: bad flagvector\n", af->getline num()); 607 HUNSPELL_WARNING(stderr, "error: line %d: bad flagvector\n", af->getline num());
495 *result = NULL; 608 *result = NULL;
496 return 0; 609 return 0;
497 } 610 }
498 switch (flag_mode) { 611 switch (flag_mode) {
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after
600 return mystrdup((char *) ch); 713 return mystrdup((char *) ch);
601 } 714 }
602 715
603 // read in aff file and set flag mode 716 // read in aff file and set flag mode
604 int HashMgr::load_config(const char * affpath, const char * key) 717 int HashMgr::load_config(const char * affpath, const char * key)
605 { 718 {
606 char * line; // io buffers 719 char * line; // io buffers
607 int firstline = 1; 720 int firstline = 1;
608 721
609 // open the affix file 722 // open the affix file
723 #ifdef HUNSPELL_CHROME_CLIENT
724 hunspell::LineIterator iterator = bdict_reader->GetOtherLineIterator();
725 FileMgr * afflst = new FileMgr(&iterator);
726 #else
610 FileMgr * afflst = new FileMgr(affpath, key); 727 FileMgr * afflst = new FileMgr(affpath, key);
728 #endif
611 if (!afflst) { 729 if (!afflst) {
612 HUNSPELL_WARNING(stderr, "Error - could not open affix description file %s\n ",affpath); 730 HUNSPELL_WARNING(stderr, "Error - could not open affix description file %s\n ",affpath);
613 return 1; 731 return 1;
614 } 732 }
615 733
616 // read in each line ignoring any that do not 734 // read in each line ignoring any that do not
617 // start with a known line type indicator 735 // start with a known line type indicator
618 736
619 while ((line = afflst->getline())) { 737 while ((line = afflst->getline())) {
620 mychomp(line); 738 mychomp(line);
(...skipping 174 matching lines...) Expand 10 before | Expand all | Expand 10 after
795 aliasf = NULL; 913 aliasf = NULL;
796 aliasflen = NULL; 914 aliasflen = NULL;
797 numaliasf = 0; 915 numaliasf = 0;
798 HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af-> getlinenum()); 916 HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af-> getlinenum());
799 return 1; 917 return 1;
800 } 918 }
801 } 919 }
802 return 0; 920 return 0;
803 } 921 }
804 922
923 #ifdef HUNSPELL_CHROME_CLIENT
924 int HashMgr::LoadAFLines()
925 {
926 utf8 = 1; // We always use UTF-8.
927
928 // Read in all the AF lines which tell us the rules for each affix group ID.
929 hunspell::LineIterator iterator = bdict_reader->GetAfLineIterator();
930 FileMgr afflst(&iterator);
931 while (char* line = afflst.getline()) {
932 int rv = parse_aliasf(line, &afflst);
933 if (rv)
934 return rv;
935 }
936
937 return 0;
938 }
939
940 hentry* HashMgr::InitHashEntry(hentry* entry,
941 size_t item_size,
942 const char* word,
943 int word_length,
944 int affix_index) const {
945 // Return if the given buffer doesn't have enough space for a hentry struct
946 // or the given word is too long.
947 // Our BDICT cannot handle words longer than (128 - 1) bytes. So, it is
948 // better to return an error if the given word is too long and prevent
949 // an unexpected result caused by a long word.
950 const int kMaxWordLen = 128;
951 if (item_size < sizeof(hentry) + word_length + 1 ||
952 word_length >= kMaxWordLen)
953 return NULL;
954
955 // Initialize a hentry struct with the given parameters, and
956 // append the given string at the end of this hentry struct.
957 memset(entry, 0, item_size);
958 FileMgr af(NULL);
959 entry->alen = static_cast<short>(
960 const_cast<HashMgr*>(this)->get_aliasf(affix_index, &entry->astr, &af));
961 entry->blen = static_cast<unsigned char>(word_length);
962 memcpy(&entry->word, word, word_length);
963
964 return entry;
965 }
966
967 hentry* HashMgr::CreateHashEntry(const char* word,
968 int word_length,
969 int affix_index) const {
970 // Return if the given word is too long.
971 // (See the comment in HashMgr::InitHashEntry().)
972 const int kMaxWordLen = 128;
973 if (word_length >= kMaxWordLen)
974 return NULL;
975
976 const size_t kEntrySize = sizeof(hentry) + word_length + 1;
977 struct hentry* entry = reinterpret_cast<hentry*>(malloc(kEntrySize));
978 if (entry)
979 InitHashEntry(entry, kEntrySize, word, word_length, affix_index);
980
981 return entry;
982 }
983
984 void HashMgr::DeleteHashEntry(hentry* entry) const {
985 free(entry);
986 }
987
988 hentry* HashMgr::AffixIDsToHentry(char* word,
989 int* affix_ids,
990 int affix_count) const
991 {
992 if (affix_count == 0)
993 return NULL;
994
995 HEntryCache& cache = const_cast<HashMgr*>(this)->hentry_cache;
996 std::string std_word(word);
997 HEntryCache::iterator found = cache.find(std_word);
998 if (found != cache.end()) {
999 // We must return an existing hentry for the same word if we've previously
1000 // handed one out. Hunspell will compare pointers in some cases to see if
1001 // two words it has found are the same.
1002 return found->second;
1003 }
1004
1005 short word_len = static_cast<short>(strlen(word));
1006
1007 // We can get a number of prefixes per word. There will normally be only one,
1008 // but if not, there will be a linked list of "hentry"s for the "homonym"s
1009 // for the word.
1010 struct hentry* first_he = NULL;
1011 struct hentry* prev_he = NULL; // For making linked list.
1012 for (int i = 0; i < affix_count; i++) {
1013 struct hentry* he = CreateHashEntry(word, word_len, affix_ids[i]);
1014 if (!he)
1015 break;
1016 if (i == 0)
1017 first_he = he;
1018 if (prev_he)
1019 prev_he->next_homonym = he;
1020 prev_he = he;
1021 }
1022
1023 cache[std_word] = first_he; // Save this word in the cache for later.
1024 return first_he;
1025 }
1026
1027 hentry* HashMgr::GetHentryFromHEntryCache(char* word) {
1028 HEntryCache& cache = const_cast<HashMgr*>(this)->hentry_cache;
1029 std::string std_word(word);
1030 HEntryCache::iterator found = cache.find(std_word);
1031 if (found != cache.end())
1032 return found->second;
1033 else
1034 return NULL;
1035 }
1036 #endif
1037
805 int HashMgr::is_aliasf() { 1038 int HashMgr::is_aliasf() {
806 return (aliasf != NULL); 1039 return (aliasf != NULL);
807 } 1040 }
808 1041
809 int HashMgr::get_aliasf(int index, unsigned short ** fvec, FileMgr * af) { 1042 int HashMgr::get_aliasf(int index, unsigned short ** fvec, FileMgr * af) {
810 if ((index > 0) && (index <= numaliasf)) { 1043 if ((index > 0) && (index <= numaliasf)) {
811 *fvec = aliasf[index - 1]; 1044 *fvec = aliasf[index - 1];
812 return aliasflen[index - 1]; 1045 return aliasflen[index - 1];
813 } 1046 }
814 HUNSPELL_WARNING(stderr, "error: line %d: bad flag alias index: %d\n", af->g etlinenum(), index); 1047 HUNSPELL_WARNING(stderr, "error: line %d: bad flag alias index: %d\n", af->g etlinenum(), index);
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after
919 1152
920 int HashMgr::is_aliasm() { 1153 int HashMgr::is_aliasm() {
921 return (aliasm != NULL); 1154 return (aliasm != NULL);
922 } 1155 }
923 1156
924 char * HashMgr::get_aliasm(int index) { 1157 char * HashMgr::get_aliasm(int index) {
925 if ((index > 0) && (index <= numaliasm)) return aliasm[index - 1]; 1158 if ((index > 0) && (index <= numaliasm)) return aliasm[index - 1];
926 HUNSPELL_WARNING(stderr, "error: bad morph. alias index: %d\n", index); 1159 HUNSPELL_WARNING(stderr, "error: bad morph. alias index: %d\n", index);
927 return NULL; 1160 return NULL;
928 } 1161 }
OLDNEW
« no previous file with comments | « third_party/hunspell/src/hunspell/hashmgr.hxx ('k') | third_party/hunspell/src/hunspell/htypes.hxx » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698