Coverage Report

Created: 2026-11-06 11:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/work/workdir/UnpackedTarball/hyphen/hyphen.c
Line
Count
Source
1
/* Libhnj is dual licensed under LGPL and MPL. Boilerplate for both
2
 * licenses follows.
3
 */
4
5
/* LibHnj - a library for high quality hyphenation and justification
6
 * Copyright (C) 1998 Raph Levien,
7
 *           (C) 2001 ALTLinux, Moscow (http://www.alt-linux.org),
8
 *           (C) 2001 Peter Novodvorsky (nidd@cs.msu.su)
9
 *           (C) 2006, 2007, 2008, 2010 László Németh (nemeth at OOo)
10
 *
11
 * This library is free software; you can redistribute it and/or
12
 * modify it under the terms of the GNU Library General Public
13
 * License as published by the Free Software Foundation; either
14
 * version 2 of the License, or (at your option) any later version.
15
 *
16
 * This library is distributed in the hope that it will be useful,
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19
 * Library General Public License for more details.
20
 *
21
 * You should have received a copy of the GNU Library General Public
22
 * License along with this library; if not, write to the
23
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24
 * Boston, MA  02111-1307  USA.
25
*/
26
27
/*
28
 * The contents of this file are subject to the Mozilla Public License
29
 * Version 1.0 (the "MPL"); you may not use this file except in
30
 * compliance with the MPL.  You may obtain a copy of the MPL at
31
 * http://www.mozilla.org/MPL/
32
 *
33
 * Software distributed under the MPL is distributed on an "AS IS" basis,
34
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the MPL
35
 * for the specific language governing rights and limitations under the
36
 * MPL.
37
 *
38
 */
39
#include <stdlib.h> /* for NULL, malloc */
40
#include <stdio.h>  /* for fprintf */
41
#include <string.h> /* for strdup */
42
#include <limits.h> /* for INT_MAX */
43
44
#ifdef UNX
45
#include <unistd.h> /* for exit */
46
#endif
47
48
#ifdef _WIN32
49
#include <windows.h>
50
#include <wchar.h>
51
#endif
52
53
#define noVERBOSE
54
55
/* calculate hyphenmin values with long ligature length (2 or 3 characters
56
 * instead of 1 or 2) for comparison with hyphenation without ligatures */
57
#define noLONG_LIGATURE
58
59
#ifdef LONG_LIGATURE
60
#define LIG_xx  1
61
#define LIG_xxx 2
62
#else
63
0
#define LIG_xx  0
64
0
#define LIG_xxx 1
65
#endif
66
67
#include "hnjalloc.h"
68
#include "hyphen.h"
69
70
static char *
71
hnj_strdup (const char *s)
72
0
{
73
0
  char *newstr;
74
0
  int l;
75
76
0
  l = strlen (s);
77
0
  newstr = (char *) hnj_malloc (l + 1);
78
0
  memcpy (newstr, s, l);
79
0
  newstr[l] = 0;
80
0
  return newstr;
81
0
}
82
83
/* remove cross-platform text line end characters */
84
static void hnj_strchomp(char * s)
85
0
{
86
0
  int k = strlen(s);
87
0
  if ((k > 0) && ((*(s+k-1)=='\r') || (*(s+k-1)=='\n'))) *(s+k-1) = '\0';
88
0
  if ((k > 1) && (*(s+k-2) == '\r')) *(s+k-2) = '\0';
89
0
}
90
91
/* a little bit of a hash table implementation. This simply maps strings
92
   to state numbers */
93
94
typedef struct _HashTab HashTab;
95
typedef struct _HashEntry HashEntry;
96
97
/* A cheap, but effective, hack. */
98
0
#define HASH_SIZE 31627
99
100
struct _HashTab {
101
  HashEntry *entries[HASH_SIZE];
102
};
103
104
struct _HashEntry {
105
  HashEntry *next;
106
  char *key;
107
  int val;
108
};
109
110
/* a char* hash function from ASU - adapted from Gtk+ */
111
static unsigned int
112
hnj_string_hash (const char *s)
113
0
{
114
0
  const char *p;
115
0
  unsigned int h=0, g;
116
0
  for(p = s; *p != '\0'; p += 1) {
117
0
    h = ( h << 4 ) + *p;
118
0
    if ( ( g = h & 0xf0000000 ) ) {
119
0
      h = h ^ (g >> 24);
120
0
      h = h ^ g;
121
0
    }
122
0
  }
123
0
  return h /* % M */;
124
0
}
125
126
static HashTab *
127
hnj_hash_new (void)
128
0
{
129
0
  HashTab *hashtab;
130
0
  int i;
131
132
0
  hashtab = (HashTab *) hnj_malloc (sizeof(HashTab));
133
0
  for (i = 0; i < HASH_SIZE; i++)
134
0
    hashtab->entries[i] = NULL;
135
136
0
  return hashtab;
137
0
}
138
139
static void
140
hnj_hash_free (HashTab *hashtab)
141
0
{
142
0
  int i;
143
0
  HashEntry *e, *next;
144
145
0
  for (i = 0; i < HASH_SIZE; i++)
146
0
    for (e = hashtab->entries[i]; e; e = next)
147
0
      {
148
0
  next = e->next;
149
0
  hnj_free (e->key);
150
0
  hnj_free (e);
151
0
      }
152
153
0
  hnj_free (hashtab);
154
0
}
155
156
/* assumes that key is not already present! */
157
static void
158
hnj_hash_insert (HashTab *hashtab, const char *key, int val)
159
0
{
160
0
  int i;
161
0
  HashEntry *e;
162
163
0
  i = hnj_string_hash (key) % HASH_SIZE;
164
0
  e = (HashEntry *) hnj_malloc (sizeof(HashEntry));
165
0
  e->next = hashtab->entries[i];
166
0
  e->key = hnj_strdup (key);
167
0
  e->val = val;
168
0
  hashtab->entries[i] = e;
169
0
}
170
171
/* return val if found, otherwise -1 */
172
static int
173
hnj_hash_lookup (HashTab *hashtab, const char *key)
174
0
{
175
0
  int i;
176
0
  HashEntry *e;
177
0
  i = hnj_string_hash (key) % HASH_SIZE;
178
0
  for (e = hashtab->entries[i]; e; e = e->next)
179
0
    if (!strcmp (key, e->key))
180
0
      return e->val;
181
0
  return -1;
182
0
}
183
184
/* Get the state number, allocating a new state if necessary. */
185
static int
186
hnj_get_state (HyphenDict *dict, HashTab *hashtab, const char *string)
187
0
{
188
0
  int state_num;
189
190
0
  state_num = hnj_hash_lookup (hashtab, string);
191
192
0
  if (state_num >= 0)
193
0
    return state_num;
194
195
0
  hnj_hash_insert (hashtab, string, dict->num_states);
196
  /* predicate is true if dict->num_states is a power of two */
197
0
  if (!(dict->num_states & (dict->num_states - 1)))
198
0
    {
199
0
      dict->states = (HyphenState *) hnj_realloc (dict->states,
200
0
          (dict->num_states << 1) *
201
0
          sizeof(HyphenState));
202
0
    }
203
0
  dict->states[dict->num_states].match = NULL;
204
0
  dict->states[dict->num_states].repl = NULL;
205
0
  dict->states[dict->num_states].fallback_state = -1;
206
0
  dict->states[dict->num_states].num_trans = 0;
207
0
  dict->states[dict->num_states].trans = NULL;
208
0
  return dict->num_states++;
209
0
}
210
211
/* add a transition from state1 to state2 through ch - assumes that the
212
   transition does not already exist */
213
static void
214
hnj_add_trans (HyphenDict *dict, int state1, int state2, char ch)
215
0
{
216
0
  int num_trans;
217
218
0
  num_trans = dict->states[state1].num_trans;
219
0
  if (num_trans == 0)
220
0
    {
221
0
      dict->states[state1].trans = (HyphenTrans *) hnj_malloc (sizeof(HyphenTrans));
222
0
    }
223
0
  else if (!(num_trans & (num_trans - 1)))
224
0
    {
225
0
      dict->states[state1].trans = (HyphenTrans *) hnj_realloc (dict->states[state1].trans,
226
0
            (num_trans << 1) *
227
0
            sizeof(HyphenTrans));
228
0
    }
229
0
  dict->states[state1].trans[num_trans].ch = ch;
230
0
  dict->states[state1].trans[num_trans].new_state = state2;
231
0
  dict->states[state1].num_trans++;
232
0
}
233
234
#ifdef VERBOSE
235
HashTab *global[1];
236
237
static char *
238
get_state_str (int state, int level)
239
{
240
  int i;
241
  HashEntry *e;
242
243
  for (i = 0; i < HASH_SIZE; i++)
244
    for (e = global[level]->entries[i]; e; e = e->next)
245
      if (e->val == state)
246
  return e->key;
247
  return NULL;
248
}
249
#endif
250
251
0
static void hnj_hyphen_load_line(char * buf, HyphenDict * dict, HashTab * hashtab) {
252
0
  int i, j;
253
0
  char word[MAX_CHARS];
254
0
  char pattern[MAX_CHARS];
255
0
  char * repl;
256
0
  signed char replindex;
257
0
  signed char replcut;
258
0
  int state_num = 0;
259
0
  int last_state;
260
0
  char ch;
261
0
  int found;
262
263
0
    if (strncmp(buf, "LEFTHYPHENMIN", 13) == 0) {
264
0
      dict->lhmin = atoi(buf + 13);
265
0
      return;
266
0
    } else if (strncmp(buf, "RIGHTHYPHENMIN", 14) == 0) {
267
0
      dict->rhmin = atoi(buf + 14);
268
0
      return;
269
0
    } else if (strncmp(buf, "COMPOUNDLEFTHYPHENMIN", 21) == 0) {
270
0
      dict->clhmin = atoi(buf + 21);
271
0
      return;
272
0
    } else if (strncmp(buf, "COMPOUNDRIGHTHYPHENMIN", 22) == 0) {
273
0
      dict->crhmin = atoi(buf + 22);
274
0
      return;
275
0
    } else if (strncmp(buf, "NOHYPHEN", 8) == 0) {
276
0
      char * space = buf + 8;
277
0
      while (*space != '\0' && (*space == ' ' || *space == '\t')) space++;
278
0
      if (dict->nohyphen) hnj_free(dict->nohyphen);
279
0
      dict->nohyphen = NULL;
280
0
      dict->nohyphenl = 0;
281
0
      if (*space != '\0') dict->nohyphen = hnj_strdup(space);
282
0
      if (dict->nohyphen) {
283
0
          char * nhe;
284
0
          hnj_strchomp(dict->nohyphen);
285
0
          for (nhe = dict->nohyphen + strlen(dict->nohyphen); nhe > dict->nohyphen; nhe--) {
286
0
                  if (*nhe == ',') {
287
0
                      dict->nohyphenl++;
288
0
                      *nhe = 0;
289
0
                  }
290
0
          }
291
0
      }
292
0
      return;
293
0
    }
294
0
    j = 0;
295
0
    pattern[j] = '0';
296
0
          repl = strchr(buf, '/');
297
0
          replindex = 0;
298
0
          replcut = 0;
299
0
          if (repl) {
300
0
            char * index = strchr(repl + 1, ',');
301
0
            *repl = '\0';
302
0
            if (index) {
303
0
                char * index2 = strchr(index + 1, ',');
304
0
                *index = '\0';
305
0
                if (index2) {
306
0
                    *index2 = '\0';
307
0
                    replindex = (signed char) atoi(index + 1) - 1;
308
0
                    replcut = (signed char) atoi(index2 + 1);
309
0
                }
310
0
            } else {
311
0
                hnj_strchomp(repl + 1);
312
0
                replindex = 0;
313
                /* the whole pattern is cut: count its characters, not its digits or dots */
314
0
                for (i = 0; (unsigned char) buf[i] > ' ' && replcut < 127; i++) {
315
0
                    unsigned char c = (unsigned char) buf[i];
316
0
                    if ((c < '0' || c > '9') && c != '.' && !(dict->utf8 && (c >> 6) == 2))
317
0
                        replcut++;
318
0
                }
319
0
            }
320
0
            repl = hnj_strdup(repl + 1);
321
0
          }
322
0
    for (i = 0; (unsigned char)buf[i] > (unsigned char)' ' && j < MAX_CHARS - 2; i++)
323
0
      {
324
0
        if (buf[i] >= '0' && buf[i] <= '9')
325
0
    pattern[j] = buf[i];
326
0
        else
327
0
    {
328
0
      word[j] = buf[i];
329
0
      pattern[++j] = '0';
330
0
    }
331
0
      }
332
0
    word[j] = '\0';
333
0
    pattern[j + 1] = '\0';
334
335
0
          i = 0;
336
0
    if (!repl) {
337
      /* Optimize away leading zeroes */
338
0
            for (; pattern[i] == '0'; i++);
339
0
          } else {
340
0
            if (*word == '.') i++;
341
            /* convert UTF-8 char. positions of discretionary hyph. replacements to 8-bit */
342
0
            if (dict->utf8) {
343
0
                int pu = -1;        /* unicode character position */
344
0
                int ps = -1;        /* unicode start position (original replindex) */
345
0
                size_t pc = (*word == '.') ? 1: 0; /* 8-bit character position */
346
0
                for (; pc < (strlen(word) + 1); pc++) {
347
                /* beginning of an UTF-8 character (not '10' start bits) */
348
0
                    if ((((unsigned char) word[pc]) >> 6) != 2) pu++;
349
0
                    if ((ps < 0) && (replindex == pu)) {
350
0
                        ps = replindex;
351
0
                        replindex = (signed char) pc;
352
0
                    }
353
0
                    if ((ps >= 0) && ((pu - ps) == replcut)) {
354
0
                        replcut = (signed char) (pc - replindex);
355
0
                        break;
356
0
                    }
357
0
                }
358
0
                if (*word == '.') replindex--;
359
0
            }
360
0
          }
361
362
#ifdef VERBOSE
363
    printf ("word %s pattern %s, j = %d  repl: %s\n", word, pattern + i, j, repl);
364
#endif
365
0
    found = hnj_hash_lookup (hashtab, word);
366
0
    state_num = hnj_get_state (dict, hashtab, word);
367
0
    if (dict->states[state_num].match) hnj_free (dict->states[state_num].match);
368
0
    if (dict->states[state_num].repl) hnj_free (dict->states[state_num].repl);
369
0
    dict->states[state_num].match = hnj_strdup (pattern + i);
370
0
    dict->states[state_num].repl = repl;
371
0
    dict->states[state_num].replindex = replindex;
372
0
          if (!replcut) {
373
            /* the whole pattern is cut, without its dots */
374
0
            for (i = 0; word[i]; i++)
375
0
                if (word[i] != '.') replcut++;
376
0
          }
377
0
          dict->states[state_num].replcut = replcut;
378
379
    /* now, put in the prefix transitions */
380
0
          for (; found < 0 && j > 0; --j)
381
0
      {
382
0
        last_state = state_num;
383
0
        ch = word[j - 1];
384
0
        word[j - 1] = '\0';
385
0
        found = hnj_hash_lookup (hashtab, word);
386
0
        state_num = hnj_get_state (dict, hashtab, word);
387
0
        hnj_add_trans (dict, state_num, last_state, ch);
388
0
      }
389
0
}
390
391
0
static FILE * hnj_fopen(const char * path, const char * mode) {
392
#ifdef _WIN32
393
#define WIN32_LONG_PATH_PREFIX "\\\\?\\"
394
    if (strncmp(path, WIN32_LONG_PATH_PREFIX, 4) == 0) {
395
        int len = MultiByteToWideChar(CP_UTF8, 0, path, -1, NULL, 0);
396
        wchar_t *buff = (wchar_t *) hnj_malloc(len * sizeof(wchar_t));
397
        wchar_t *buff2 = (wchar_t *) hnj_malloc(len * sizeof(wchar_t));
398
        FILE * f = NULL;
399
        MultiByteToWideChar(CP_UTF8, 0, path, -1, buff, len);
400
        if (_wfullpath( buff2, buff, len ) != NULL) {
401
          f = _wfopen(buff2, (strcmp(mode, "r") == 0) ? L"r" : L"rb");
402
        }
403
        free(buff);
404
        free(buff2);
405
        return f;
406
    }
407
#endif
408
0
    return fopen(path, mode);
409
0
}
410
411
HyphenDict *
412
hnj_hyphen_load (const char *fn)
413
0
{
414
0
  HyphenDict *result;
415
0
  FILE *f;
416
0
  f = hnj_fopen (fn, "r");
417
0
  if (f == NULL)
418
0
    return NULL;
419
420
0
  result = hnj_hyphen_load_file(f);
421
422
0
  fclose(f);
423
0
  return result;
424
0
}
425
426
/* Line-reader callback used by hnj_hyphen_load_impl. Returns buf on success
427
 * (line copied in, '\n' included if present, NUL-terminated), or NULL on EOF.
428
 * Overly long lines are skipped with a warning. */
429
typedef char *(*hnj_get_line_fn)(char *buf, int n, void *ctx);
430
431
typedef struct { const char *p; size_t n; } hnj_mem_ctx;
432
433
0
static char *hnj_get_line_file(char *buf, int n, void *ctx) {
434
0
  FILE *f = (FILE *)ctx;
435
0
  while (fgets(buf, n, f) != NULL) {
436
0
    if (!feof(f) && strchr(buf, '\n') == NULL) {
437
0
      int c;
438
0
      while ((c = fgetc(f)) != '\n' && c != EOF);
439
0
      if (buf[0] != '%')
440
0
        fprintf(stderr, "Warning: skipping too long pattern (more than %d chars)\n", n);
441
0
      continue;
442
0
    }
443
0
    return buf;
444
0
  }
445
0
  return NULL;
446
0
}
447
448
0
static char *hnj_get_line_mem(char *buf, int n, void *ctx) {
449
0
  hnj_mem_ctx *m = (hnj_mem_ctx *)ctx;
450
0
  while (m->n > 0) {
451
0
    size_t cap = (size_t)(n - 1);
452
0
    size_t i = 0;
453
0
    while (i < m->n && i < cap && m->p[i] != '\n') i++;
454
0
    int has_nl = (i < cap && i < m->n && m->p[i] == '\n');
455
0
    size_t copy = has_nl ? i + 1 : i;
456
0
    memcpy(buf, m->p, copy);
457
0
    buf[copy] = '\0';
458
0
    m->p += copy;
459
0
    m->n -= copy;
460
0
    if (!has_nl && m->n > 0) {
461
0
      while (m->n > 0 && *m->p != '\n') { m->p++; m->n--; }
462
0
      if (m->n > 0) { m->p++; m->n--; }
463
0
      if (buf[0] != '%')
464
0
        fprintf(stderr, "Warning: skipping too long pattern (more than %d chars)\n", n);
465
0
      continue;
466
0
    }
467
0
    return buf;
468
0
  }
469
0
  return NULL;
470
0
}
471
472
static HyphenDict *
473
hnj_hyphen_load_impl (hnj_get_line_fn get_line, void *ctx)
474
0
{
475
0
  HyphenDict *dict[2];
476
0
  HashTab *hashtab;
477
0
  char buf[MAX_CHARS];
478
0
  int nextlevel = 0;
479
0
  int i, j, k;
480
0
  HashEntry *e;
481
0
  int state_num = 0;
482
/* loading one or two dictionaries (separated by NEXTLEVEL keyword) */
483
0
for (k = 0; k < 2; k++) {
484
0
  hashtab = hnj_hash_new ();
485
#ifdef VERBOSE
486
  global[k] = hashtab;
487
#endif
488
0
  hnj_hash_insert (hashtab, "", 0);
489
0
  dict[k] = (HyphenDict *) hnj_malloc (sizeof(HyphenDict));
490
0
  dict[k]->num_states = 1;
491
0
  dict[k]->states = (HyphenState *) hnj_malloc (sizeof(HyphenState));
492
0
  dict[k]->states[0].match = NULL;
493
0
  dict[k]->states[0].repl = NULL;
494
0
  dict[k]->states[0].fallback_state = -1;
495
0
  dict[k]->states[0].num_trans = 0;
496
0
  dict[k]->states[0].trans = NULL;
497
0
  dict[k]->nextlevel = NULL;
498
0
  dict[k]->lhmin = 0;
499
0
  dict[k]->rhmin = 0;
500
0
  dict[k]->clhmin = 0;
501
0
  dict[k]->crhmin = 0;
502
0
  dict[k]->nohyphen = NULL;
503
0
  dict[k]->nohyphenl = 0;
504
505
  /* read in character set info */
506
0
  if (k == 0) {
507
0
    for (i=0;i<MAX_NAME;i++) dict[k]->cset[i]= 0;
508
0
    if (get_line(dict[k]->cset, sizeof(dict[k]->cset), ctx) != NULL) {
509
0
      for (i=0;i<MAX_NAME;i++)
510
0
        if ((dict[k]->cset[i] == '\r') || (dict[k]->cset[i] == '\n'))
511
0
          dict[k]->cset[i] = 0;
512
0
    } else {
513
0
      dict[k]->cset[0] = 0;
514
0
    }
515
0
    dict[k]->utf8 = (strcmp(dict[k]->cset, "UTF-8") == 0);
516
0
  } else {
517
0
    strncpy(dict[k]->cset, dict[0]->cset, sizeof(dict[k]->cset)-1);
518
0
    dict[k]->cset[sizeof(dict[k]->cset)-1] = '\0';
519
0
    dict[k]->utf8 = dict[0]->utf8;
520
0
  }
521
522
0
  if (k == 0 || nextlevel) {
523
0
    while (get_line(buf, sizeof(buf), ctx) != NULL) {
524
0
      if (strncmp(buf, "NEXTLEVEL", 9) == 0) {
525
0
        nextlevel = 1;
526
0
        break;
527
0
      } else if (buf[0] != '%') {
528
0
        hnj_hyphen_load_line(buf, dict[k], hashtab);
529
0
      }
530
0
    }
531
0
  } else if (k == 1) {
532
    /* default first level: hyphen and ASCII apostrophe */
533
0
    if (!dict[0]->utf8) hnj_hyphen_load_line("NOHYPHEN ',-\n", dict[k], hashtab);
534
0
    else hnj_hyphen_load_line("NOHYPHEN ',\xe2\x80\x93,\xe2\x80\x99,-\n", dict[k], hashtab);
535
0
    strncpy(buf, "1-1\n", MAX_CHARS-1); /* buf rewritten by hnj_hyphen_load here */
536
0
    buf[MAX_CHARS-1] = '\0';
537
0
    hnj_hyphen_load_line(buf, dict[k], hashtab); /* remove hyphen */
538
0
    hnj_hyphen_load_line("1'1\n", dict[k], hashtab); /* ASCII apostrophe */
539
0
    if (dict[0]->utf8) {
540
0
      hnj_hyphen_load_line("1\xe2\x80\x93" "1\n", dict[k], hashtab); /* endash */
541
0
      hnj_hyphen_load_line("1\xe2\x80\x99" "1\n", dict[k], hashtab); /* apostrophe */
542
0
    }
543
0
  }
544
545
  /* Could do unioning of matches here (instead of the preprocessor script).
546
     If we did, the pseudocode would look something like this:
547
548
     foreach state in the hash table
549
        foreach i = [1..length(state) - 1]
550
           state to check is substr (state, i)
551
           look it up
552
           if found, and if there is a match, union the match in.
553
554
     It's also possible to avoid the quadratic blowup by doing the
555
     search in order of increasing state string sizes - then you
556
     can break the loop after finding the first match.
557
558
     This step should be optional in any case - if there is a
559
     preprocessed rule table, it's always faster to use that.
560
561
*/
562
563
  /* put in the fallback states */
564
0
  for (i = 0; i < HASH_SIZE; i++)
565
0
    for (e = hashtab->entries[i]; e; e = e->next)
566
0
      {
567
0
  if (*(e->key)) for (j = 1; 1; j++)
568
0
    {
569
0
      state_num = hnj_hash_lookup (hashtab, e->key + j);
570
0
      if (state_num >= 0)
571
0
        break;
572
0
    }
573
        /* KBH: FIXME state 0 fallback_state should always be -1? */
574
0
  if (e->val)
575
0
    dict[k]->states[e->val].fallback_state = state_num;
576
0
      }
577
#ifdef VERBOSE
578
  for (i = 0; i < HASH_SIZE; i++)
579
    for (e = hashtab->entries[i]; e; e = e->next)
580
      {
581
  printf ("%d string %s state %d, fallback=%d\n", i, e->key, e->val,
582
    dict[k]->states[e->val].fallback_state);
583
  for (j = 0; j < dict[k]->states[e->val].num_trans; j++)
584
    printf (" %c->%d\n", dict[k]->states[e->val].trans[j].ch,
585
      dict[k]->states[e->val].trans[j].new_state);
586
      }
587
#endif
588
589
0
#ifndef VERBOSE
590
0
  hnj_hash_free (hashtab);
591
0
#endif
592
0
  state_num = 0;
593
0
}
594
0
  if (nextlevel) dict[0]->nextlevel = dict[1];
595
0
  else {
596
0
    dict[1] -> nextlevel = dict[0];
597
0
    dict[1]->lhmin = dict[0]->lhmin;
598
0
    dict[1]->rhmin = dict[0]->rhmin;
599
0
    dict[1]->clhmin = (dict[0]->clhmin) ? dict[0]->clhmin : ((dict[0]->lhmin) ? dict[0]->lhmin : 3);
600
0
    dict[1]->crhmin = (dict[0]->crhmin) ? dict[0]->crhmin : ((dict[0]->rhmin) ? dict[0]->rhmin : 3);
601
#ifdef VERBOSE
602
    HashTab *r = global[0];
603
    global[0] = global[1];
604
    global[1] = r;
605
#endif
606
0
    return dict[1];
607
0
  }
608
0
  return dict[0];
609
0
}
610
611
HyphenDict *
612
hnj_hyphen_load_file (FILE *f)
613
0
{
614
0
  return hnj_hyphen_load_impl(hnj_get_line_file, f);
615
0
}
616
617
HyphenDict *
618
hnj_hyphen_load_data (const char *fdata, size_t flen)
619
0
{
620
0
  hnj_mem_ctx ctx = { fdata, flen };
621
0
  return hnj_hyphen_load_impl(hnj_get_line_mem, &ctx);
622
0
}
623
624
void hnj_hyphen_free (HyphenDict *dict)
625
0
{
626
0
  int state_num;
627
0
  HyphenState *hstate;
628
629
0
  for (state_num = 0; state_num < dict->num_states; state_num++)
630
0
    {
631
0
      hstate = &dict->states[state_num];
632
0
      if (hstate->match)
633
0
  hnj_free (hstate->match);
634
0
      if (hstate->repl)
635
0
  hnj_free (hstate->repl);
636
0
      if (hstate->trans)
637
0
  hnj_free (hstate->trans);
638
0
    }
639
0
  if (dict->nextlevel) hnj_hyphen_free(dict->nextlevel);
640
641
0
  if (dict->nohyphen) hnj_free(dict->nohyphen);
642
643
0
  hnj_free (dict->states);
644
645
0
  hnj_free (dict);
646
0
}
647
648
#define MAX_WORD 256
649
650
/* the longest word that the hyphenate functions accept */
651
0
#define MAX_WORD_SIZE (INT_MAX / 8)
652
653
/* the deepest nesting of compound parts that is split again */
654
0
#define MAX_COMPOUND_DEPTH 32
655
656
int hnj_hyphen_hyphenate (HyphenDict *dict,
657
         const char *word, int word_size,
658
         char *hyphens)
659
0
{
660
0
  char *prep_word;
661
0
  int i, j, k;
662
0
  int state;
663
0
  char ch;
664
0
  HyphenState *hstate;
665
0
  char *match;
666
0
  int offset;
667
668
0
  if (word_size < 0 || word_size > MAX_WORD_SIZE)
669
0
    return 1;
670
671
0
  prep_word = (char*) hnj_malloc (word_size + 3);
672
673
0
  j = 0;
674
0
  prep_word[j++] = '.';
675
676
0
  for (i = 0; i < word_size; i++) {
677
0
    if (word[i] <= '9' && word[i] >= '0') {
678
0
      prep_word[j++] = '.';
679
0
    } else {
680
0
      prep_word[j++] = word[i];
681
0
    }
682
0
  }
683
684
0
  prep_word[j++] = '.';
685
0
  prep_word[j] = '\0';
686
687
0
  for (i = 0; i < word_size + 5; i++)
688
0
    hyphens[i] = '0';
689
690
#ifdef VERBOSE
691
  printf ("prep_word = %s\n", prep_word);
692
#endif
693
694
  /* now, run the finite state machine */
695
0
  state = 0;
696
0
  for (i = 0; i < j; i++)
697
0
    {
698
0
      ch = prep_word[i];
699
0
      for (;;)
700
0
  {
701
702
0
    if (state == -1) {
703
            /* return 1; */
704
      /*  KBH: FIXME shouldn't this be as follows? */
705
0
            state = 0;
706
0
            goto try_next_letter;
707
0
          }
708
709
#ifdef VERBOSE
710
    char *state_str;
711
    state_str = get_state_str (state, 0);
712
713
    for (k = 0; k < i - strlen (state_str); k++)
714
      putchar (' ');
715
    printf ("%s", state_str);
716
#endif
717
718
0
    hstate = &dict->states[state];
719
0
    for (k = 0; k < hstate->num_trans; k++)
720
0
      if (hstate->trans[k].ch == ch)
721
0
        {
722
0
    state = hstate->trans[k].new_state;
723
0
    goto found_state;
724
0
        }
725
0
    state = hstate->fallback_state;
726
#ifdef VERBOSE
727
    printf (" falling back, fallback_state %d\n", state);
728
#endif
729
0
  }
730
0
    found_state:
731
#ifdef VERBOSE
732
      printf ("found state %d\n",state);
733
#endif
734
      /* Additional optimization is possible here - especially,
735
   elimination of trailing zeroes from the match. Leading zeroes
736
   have already been optimized. */
737
0
      match = dict->states[state].match;
738
      /* replacing rules not handled by hyphen_hyphenate() */
739
0
      if (match && !dict->states[state].repl)
740
0
  {
741
0
    offset = i + 1 - strlen (match);
742
#ifdef VERBOSE
743
    for (k = 0; k < offset; k++)
744
      putchar (' ');
745
    printf ("%s\n", match);
746
#endif
747
    /* This is a linear search because I tried a binary search and
748
       found it to be just a teeny bit slower. */
749
0
    for (k = (offset < 0 ? -offset : 0); match[k]; k++)
750
0
      if (hyphens[offset + k] < match[k])
751
0
        hyphens[offset + k] = match[k];
752
0
  }
753
754
      /* KBH: we need this to make sure we keep looking in a word */
755
      /* for patterns even if the current character is not known in state 0 */
756
      /* since patterns for hyphenation may occur anywhere in the word */
757
0
      try_next_letter: ;
758
759
0
    }
760
#ifdef VERBOSE
761
  for (i = 0; i < j; i++)
762
    putchar (hyphens[i]);
763
  putchar ('\n');
764
#endif
765
766
0
  for (i = 0; i < j - 4; i++)
767
#if 0
768
    if (hyphens[i + 1] & 1)
769
      hyphens[i] = '-';
770
#else
771
0
    hyphens[i] = hyphens[i + 1];
772
0
#endif
773
0
  hyphens[0] = '0';
774
0
  for (; i < word_size; i++)
775
0
    hyphens[i] = '0';
776
0
  hyphens[word_size] = '\0';
777
778
0
  hnj_free (prep_word);
779
780
0
  return 0;
781
0
}
782
783
/* Unicode ligature length */
784
0
static int hnj_ligature(unsigned char c) {
785
0
    switch (c) {
786
0
        case 0x80:      /* ff */
787
0
        case 0x81:      /* fi */
788
0
        case 0x82: return LIG_xx; /* fl */
789
0
        case 0x83:      /* ffi */
790
0
        case 0x84: return LIG_xxx; /* ffl */
791
0
        case 0x85:      /* long st */
792
0
        case 0x86: return LIG_xx; /* st */
793
0
    }
794
0
    return 0;
795
0
}
796
797
/* character length of the first n byte of the input word */
798
static int hnj_hyphen_strnlen(const char * word, int n, int utf8)
799
0
{
800
0
    int i = 0;
801
0
    int j = 0;
802
0
    while (j < n && word[j] != '\0') {
803
0
      i++;
804
      /* Unicode ligature support */
805
0
      if (utf8 && ((unsigned char) word[j] == 0xEF) && ((unsigned char) word[j + 1] == 0xAC))  {
806
0
        i += hnj_ligature(word[j + 2]);
807
0
      }
808
0
      for (j++; utf8 && (word[j] & 0xc0) == 0x80; j++);
809
0
    }
810
0
    return i;
811
0
}
812
813
static int hnj_hyphen_lhmin(int utf8, const char *word, int word_size, char * hyphens,
814
  char *** rep, int ** pos, int ** cut, int lhmin)
815
0
{
816
0
    int i = 1, j;
817
818
    /* Unicode ligature support */
819
0
    if (utf8 && ((unsigned char) word[0] == 0xEF) && ((unsigned char) word[1] == 0xAC))  {
820
0
      i += hnj_ligature(word[2]);
821
0
    }
822
823
    /* ignore numbers */
824
0
    for (j = 0; word[j] <= '9' && word[j] >= '0'; j++) i--;
825
826
0
    for (j = 0; i < lhmin && j < word_size && word[j] != '\0'; i++) do {
827
      /* check length of the non-standard part */
828
0
      if (*rep && *pos && *cut && (*rep)[j]) {
829
0
        char * rh = strchr((*rep)[j], '=');
830
0
        if (rh && (hnj_hyphen_strnlen(word, j - (*pos)[j] + 1, utf8) +
831
0
          hnj_hyphen_strnlen((*rep)[j], rh - (*rep)[j], utf8)) < lhmin) {
832
0
            free((*rep)[j]);
833
0
            (*rep)[j] = NULL;
834
0
            hyphens[j] = '0';
835
0
          }
836
0
       } else {
837
0
         hyphens[j] = '0';
838
0
       }
839
0
       j++;
840
841
       /* Unicode ligature support */
842
0
       if (utf8 && ((unsigned char) word[j] == 0xEF) && ((unsigned char) word[j + 1] == 0xAC))  {
843
0
         i += hnj_ligature(word[j + 2]);
844
0
       }
845
0
    } while (j < word_size && utf8 && (word[j] & 0xc0) == 0x80);
846
0
    return 0;
847
0
}
848
849
static int hnj_hyphen_rhmin(int utf8, const char *word, int word_size, char * hyphens,
850
  char *** rep, int ** pos, int ** cut, int rhmin)
851
0
{
852
0
    int i = 0;
853
0
    int j;
854
855
    /* ignore numbers */
856
0
    for (j = word_size - 1; j > 0 && word[j] <= '9' && word[j] >= '0'; j--) i--;
857
858
0
    for (j = word_size - 1; i < rhmin && j > 0; j--) {
859
      /* check length of the non-standard part */
860
0
      if (*rep && *pos && *cut && (*rep)[j]) {
861
0
        char * rh = strchr((*rep)[j], '=');
862
0
        int start = j - (*pos)[j] + (*cut)[j] + 1;
863
0
        int word_len = (start >= 0 && start <= word_size) ?
864
0
            hnj_hyphen_strnlen(word + start, word_size - start, utf8) : 0;
865
0
        if (rh && (word_len +
866
0
          hnj_hyphen_strnlen(rh + 1, strlen(rh + 1), utf8)) < rhmin) {
867
0
            free((*rep)[j]);
868
0
            (*rep)[j] = NULL;
869
0
            hyphens[j] = '0';
870
0
          }
871
0
       } else {
872
0
         hyphens[j] = '0';
873
0
       }
874
0
       if (!utf8 || (word[j] & 0xc0) == 0xc0 || (word[j] & 0x80) != 0x80) i++;
875
0
    }
876
0
    return 0;
877
0
}
878
879
/* allocate, zeroed, whichever of the rep, pos and cut arrays is still NULL */
880
static void hnj_hyphen_alloc_replacements(int word_size, char *** rep, int ** pos, int ** cut)
881
0
{
882
0
    if (!*rep) {
883
0
        *rep = (char **) hnj_malloc(word_size * sizeof(char *));
884
0
        memset(*rep, 0, word_size * sizeof(char *));
885
0
    }
886
0
    if (!*pos) {
887
0
        *pos = (int *) hnj_malloc(word_size * sizeof(int));
888
0
        memset(*pos, 0, word_size * sizeof(int));
889
0
    }
890
0
    if (!*cut) {
891
0
        *cut = (int *) hnj_malloc(word_size * sizeof(int));
892
0
        memset(*cut, 0, word_size * sizeof(int));
893
0
    }
894
0
}
895
896
/* recursive function for compound level hyphenation */
897
/* work counts down the letters that may still be split into compound parts again, because a
898
   replacement at a boundary can leave every part nearly as long as the word */
899
static int hnj_hyphen_hyph_depth(HyphenDict *dict, const char *word, int word_size,
900
    char * hyphens, char *** rep, int ** pos, int ** cut,
901
    int clhmin, int crhmin, int lend, int rend, int depth, int * work)
902
0
{
903
0
  char *prep_word;
904
0
  int i, j, k;
905
0
  int state;
906
0
  char ch;
907
0
  HyphenState *hstate;
908
0
  char *match;
909
0
  char *repl;
910
0
  signed char replindex;
911
0
  signed char replcut;
912
0
  int offset;
913
0
  int * matchlen;
914
0
  int * matchindex;
915
0
  char ** matchrepl;
916
0
  int isrepl = 0;
917
918
0
  size_t prep_word_size = word_size + 3;
919
0
  prep_word = (char*) hnj_malloc (prep_word_size);
920
0
  matchlen = (int*) hnj_malloc ((word_size + 3) * sizeof(int));
921
0
  memset(matchlen, 0, (word_size + 3) * sizeof(int));
922
0
  matchindex = (int*) hnj_malloc ((word_size + 3) * sizeof(int));
923
0
  matchrepl = (char**) hnj_malloc ((word_size + 3) * sizeof(char *));
924
925
0
  j = 0;
926
0
  prep_word[j++] = '.';
927
928
0
  for (i = 0; i < word_size; i++) {
929
0
    if (word[i] <= '9' && word[i] >= '0') {
930
0
      prep_word[j++] = '.';
931
0
    } else {
932
0
      prep_word[j++] = word[i];
933
0
    }
934
0
  }
935
936
937
938
0
  prep_word[j++] = '.';
939
0
  prep_word[j] = '\0';
940
941
0
  for (i = 0; i < j; i++)
942
0
    hyphens[i] = '0';
943
944
#ifdef VERBOSE
945
  printf ("prep_word = %s\n", prep_word);
946
#endif
947
948
  /* now, run the finite state machine */
949
0
  state = 0;
950
0
  for (i = 0; i < j; i++)
951
0
    {
952
0
      ch = prep_word[i];
953
0
      for (;;)
954
0
  {
955
956
0
    if (state == -1) {
957
            /* return 1; */
958
      /*  KBH: FIXME shouldn't this be as follows? */
959
0
            state = 0;
960
0
            goto try_next_letter;
961
0
          }
962
963
#ifdef VERBOSE
964
    char *state_str;
965
    state_str = get_state_str (state, 1);
966
967
    for (k = 0; k < i - strlen (state_str); k++)
968
      putchar (' ');
969
    printf ("%s", state_str);
970
#endif
971
972
0
    hstate = &dict->states[state];
973
0
    for (k = 0; k < hstate->num_trans; k++)
974
0
      if (hstate->trans[k].ch == ch)
975
0
        {
976
0
    state = hstate->trans[k].new_state;
977
0
    goto found_state;
978
0
        }
979
0
    state = hstate->fallback_state;
980
#ifdef VERBOSE
981
    printf (" falling back, fallback_state %d\n", state);
982
#endif
983
0
  }
984
0
    found_state:
985
#ifdef VERBOSE
986
      printf ("found state %d\n",state);
987
#endif
988
      /* Additional optimization is possible here - especially,
989
   elimination of trailing zeroes from the match. Leading zeroes
990
   have already been optimized. */
991
0
      match = dict->states[state].match;
992
0
      repl = dict->states[state].repl;
993
0
      replindex = dict->states[state].replindex;
994
0
      replcut = dict->states[state].replcut;
995
      /* replacing rules not handled by hyphen_hyphenate() */
996
0
      if (match)
997
0
  {
998
0
    offset = i + 1 - strlen (match);
999
#ifdef VERBOSE
1000
    for (k = 0; k < offset; k++)
1001
      putchar (' ');
1002
    printf ("%s (%s)\n", match, repl);
1003
#endif
1004
0
          if (repl) {
1005
0
            if (!isrepl) for(; isrepl < word_size; isrepl++) {
1006
0
                matchrepl[isrepl] = NULL;
1007
0
                matchindex[isrepl] = -1;
1008
0
            }
1009
0
            if (offset + replindex >= 0 && offset + replindex < word_size + 3)
1010
0
                matchlen[offset + replindex] = replcut;
1011
0
          }
1012
    /* This is a linear search because I tried a binary search and
1013
       found it to be just a teeny bit slower. */
1014
0
    for (k = (offset < 0 ? -offset : 0); match[k]; k++) {
1015
0
      if ((hyphens[offset + k] < match[k])) {
1016
0
        hyphens[offset + k] = match[k];
1017
0
              if (match[k]&1) {
1018
0
                matchrepl[offset + k] = repl;
1019
0
                if (repl && (k >= replindex) && (k <= replindex + replcut)
1020
0
                    && offset + replindex >= 0) {
1021
0
                    matchindex[offset + replindex] = offset + k;
1022
0
                }
1023
0
              }
1024
0
            }
1025
0
          }
1026
1027
0
  }
1028
1029
      /* KBH: we need this to make sure we keep looking in a word */
1030
      /* for patterns even if the current character is not known in state 0 */
1031
      /* since patterns for hyphenation may occur anywhere in the word */
1032
0
      try_next_letter: ;
1033
1034
0
    }
1035
#ifdef VERBOSE
1036
  for (i = 0; i < j; i++)
1037
    putchar (hyphens[i]);
1038
  putchar ('\n');
1039
#endif
1040
1041
0
  for (i = 0; i < j - 3; i++)
1042
#if 0
1043
    if (hyphens[i + 1] & 1)
1044
      hyphens[i] = '-';
1045
#else
1046
0
    hyphens[i] = hyphens[i + 1];
1047
0
#endif
1048
0
  for (; i < word_size; i++)
1049
0
    hyphens[i] = '0';
1050
0
  hyphens[word_size] = '\0';
1051
1052
       /* hand back the replacements of the winning patterns */
1053
0
       for (i = 0; i < word_size; i++) {
1054
           /* the hyphenation point lies inside the cut, which starts at i */
1055
0
           if (isrepl && matchlen[i] >= 1 && matchindex[i] >= 1 && matchindex[i] <= word_size &&
1056
0
               matchlen[i] >= matchindex[i] - i && matchrepl[matchindex[i]]) {
1057
0
                if (rep && pos && cut) {
1058
0
                    hnj_hyphen_alloc_replacements(word_size, rep, pos, cut);
1059
0
                    hnj_free((*rep)[matchindex[i] - 1]);
1060
0
                    (*rep)[matchindex[i] - 1] = hnj_strdup(matchrepl[matchindex[i]]);
1061
0
                    (*pos)[matchindex[i] - 1] = matchindex[i] - i;
1062
                    /* the change starts at i, so it can cut at most the rest of the word */
1063
0
                    (*cut)[matchindex[i] - 1] =
1064
0
                        matchlen[i] <= word_size - i ? matchlen[i] : word_size - i;
1065
0
                }
1066
0
                i += matchlen[i] - 1;
1067
0
          }
1068
0
       }
1069
1070
0
  hnj_free (matchrepl);
1071
0
  hnj_free (matchlen);
1072
0
  hnj_free (matchindex);
1073
1074
  /* recursive hyphenation of the first (compound) level segments */
1075
0
  if (dict->nextlevel) {
1076
0
     char ** rep2;
1077
0
     int * pos2;
1078
0
     int * cut2;
1079
0
     char * hyphens2;
1080
0
     int begin = 0;
1081
0
     int split = depth < MAX_COMPOUND_DEPTH && *work >= word_size;
1082
1083
0
     if (split) *work -= word_size;
1084
0
     rep2 = (char**) hnj_malloc (word_size * sizeof(char *));
1085
0
     pos2 = (int*) hnj_malloc (word_size * sizeof(int));
1086
0
     cut2 = (int*) hnj_malloc (word_size * sizeof(int));
1087
0
     hyphens2 = (char*) hnj_malloc (word_size + 3);
1088
0
     for (i = 0; i < word_size; i++) rep2[i] = NULL;
1089
0
     for (i = 0; i < word_size && split; i++) if
1090
0
        (hyphens[i]&1 || (begin > 0 && i + 1 == word_size)) {
1091
0
        if (i - begin > 0) {
1092
0
            int hyph = 0;
1093
0
            prep_word[i + 2] = '\0';
1094
            /* non-standard hyphenation at compound boundary (Schiffahrt) */
1095
0
            if (rep && *rep && *pos && *cut && (*rep)[i]) {
1096
0
                char * l = strchr((*rep)[i], '=');
1097
0
                size_t offset = 2 + i - (*pos)[i];
1098
0
                strncpy(prep_word + offset, (*rep)[i], prep_word_size - offset - 1);
1099
0
                prep_word[prep_word_size - 1] = '\0';
1100
0
                if (l) {
1101
0
                    hyph = (l - (*rep)[i]) - (*pos)[i];
1102
0
                    if (2 + i + hyph < prep_word_size)
1103
0
                        prep_word[2 + i + hyph] = '\0';
1104
0
                }
1105
0
            }
1106
0
            int sub_size = i - begin + 1 + hyph;
1107
0
            if (sub_size >= word_size) sub_size = word_size - 1;
1108
0
            if ((size_t)sub_size + begin + 2 > prep_word_size)
1109
0
                sub_size = (int)(prep_word_size - begin - 2);
1110
0
            if (sub_size < 1) sub_size = 1;
1111
0
            for (j = 0; j < i - begin; j++) hyphens2[j] = '0';
1112
0
            hnj_hyphen_hyph_depth(dict, prep_word + begin + 1, sub_size,
1113
0
                hyphens2, &rep2, &pos2, &cut2, clhmin,
1114
0
                crhmin, (begin > 0 ? 0 : lend), (hyphens[i]&1 ? 0 : rend), depth + 1, work);
1115
            /* a replacement can make the part shorter than the span, and then its
1116
               terminator is inside the span and reads as no hyphenation point */
1117
0
            if (sub_size < i - begin) hyphens2[sub_size] = '0';
1118
0
            for (j = 0; j < i - begin; j++) {
1119
0
                hyphens[begin + j] = hyphens2[j];
1120
0
                if (rep2[j] && rep && pos && cut) {
1121
0
                    hnj_hyphen_alloc_replacements(word_size, rep, pos, cut);
1122
                    /* a replacement at the compound boundary can make the part longer than
1123
                       the rest of the word, so end the cut at the end of the word */
1124
0
                    int left = word_size - (begin + j + 1 - pos2[j]);
1125
0
                    hnj_free((*rep)[begin + j]);
1126
0
                    (*rep)[begin + j] = rep2[j];
1127
0
                    (*pos)[begin + j] = pos2[j];
1128
0
                    (*cut)[begin + j] = cut2[j] <= left ? cut2[j] : left;
1129
0
                    rep2[j] = NULL;
1130
0
                }
1131
0
            }
1132
0
            prep_word[i + 2] = word[i + 1];
1133
0
            if (rep && *rep && *pos && *cut && (*rep)[i]) {
1134
                /* put the word back as it was first copied, with its digits as dots */
1135
0
                for (j = 0; j < word_size; j++)
1136
0
                    prep_word[j + 1] = (word[j] <= '9' && word[j] >= '0') ? '.' : word[j];
1137
0
                prep_word[word_size + 1] = '.';
1138
0
                prep_word[word_size + 2] = '\0';
1139
0
            }
1140
0
        }
1141
0
        begin = i + 1;
1142
0
        for (j = 0; j < word_size; j++) {
1143
0
            hnj_free(rep2[j]);
1144
0
            rep2[j] = NULL;
1145
0
        }
1146
0
     }
1147
1148
     /* non-compound */
1149
0
     if (begin == 0) {
1150
0
        hnj_hyphen_hyph_depth(dict->nextlevel, word, word_size,
1151
0
            hyphens, rep, pos, cut, clhmin, crhmin, lend, rend, depth + 1, work);
1152
0
        if (!lend) hnj_hyphen_lhmin(dict->utf8, word, word_size, hyphens,
1153
0
            rep, pos, cut, clhmin);
1154
0
        if (!rend) hnj_hyphen_rhmin(dict->utf8, word, word_size, hyphens,
1155
0
            rep, pos, cut, crhmin);
1156
0
     }
1157
1158
0
     free(rep2);
1159
0
     free(cut2);
1160
0
     free(pos2);
1161
0
     free(hyphens2);
1162
0
  }
1163
1164
0
  hnj_free (prep_word);
1165
0
  return 0;
1166
0
}
1167
1168
static int hnj_hyphen_hyph_(HyphenDict *dict, const char *word, int word_size,
1169
    char * hyphens, char *** rep, int ** pos, int ** cut,
1170
    int clhmin, int crhmin, int lend, int rend)
1171
0
{
1172
0
  int work = MAX_COMPOUND_DEPTH * (word_size + MAX_CHARS);
1173
0
  return hnj_hyphen_hyph_depth(dict, word, word_size, hyphens, rep, pos, cut,
1174
0
    clhmin, crhmin, lend, rend, 0, &work);
1175
0
}
1176
1177
/* UTF-8 normalization of hyphen and non-standard positions */
1178
static int hnj_hyphen_norm(const char *word, int word_size, char * hyphens,
1179
  char *** rep, int ** pos, int ** cut)
1180
0
{
1181
0
  int i, j, k;
1182
0
  if ((((unsigned char) word[0]) >> 6) == 2) {
1183
0
    fprintf(stderr, "error - bad, non UTF-8 input: %s\n", word);
1184
0
    return 1;
1185
0
  }
1186
1187
  /* calculate UTF-8 character positions */
1188
0
  for (i = 0, j = -1; i < word_size; i++) {
1189
    /* beginning of an UTF-8 character (not '10' start bits) */
1190
0
    if ((((unsigned char) word[i]) >> 6) != 2) j++;
1191
0
    hyphens[j] = hyphens[i];
1192
0
    if (rep && pos && cut && *rep && *pos && *cut) {
1193
0
        int l = (*pos)[i];
1194
0
        (*pos)[j] = 0;
1195
0
        for (k = 0; k < l; k++) {
1196
0
            if ((((unsigned char) word[i - k]) >> 6) != 2) (*pos)[j]++;
1197
0
        }
1198
0
        k = i - l + 1;
1199
0
        l = k + (*cut)[i];
1200
0
        (*cut)[j] = 0;
1201
0
        for (; k < l && k < word_size; k++) {
1202
0
            if ((((unsigned char) word[k]) >> 6) != 2) (*cut)[j]++;
1203
0
        }
1204
0
        if (j != i) {
1205
0
            hnj_free((*rep)[j]);
1206
0
            (*rep)[j] = (*rep)[i];
1207
0
            (*rep)[i] = NULL;
1208
0
            (*pos)[i] = 0;
1209
0
            (*cut)[i] = 0;
1210
0
        }
1211
0
    }
1212
0
  }
1213
0
  hyphens[j + 1] = '\0';
1214
#ifdef VERBOSE
1215
  printf ("nums: %s\n", hyphens);
1216
#endif
1217
0
  return 0;
1218
0
}
1219
1220
/* get the word with all possible hyphenations (output: hyphword) */
1221
static void hnj_hyphen_hyphword(const char * word, int word_size, const char * hyphens,
1222
    char * hyphword, char *** rep, int ** pos, int ** cut)
1223
0
{
1224
1225
0
  if (word_size <= 0 || word_size > INT_MAX / 2) {
1226
0
    hyphword[0] = '\0';
1227
0
    return;
1228
0
  }
1229
1230
  /* hyphword buffer size must be at least 2 * l */
1231
0
  int hyphword_size = 2 * word_size - 1;
1232
1233
0
  int nonstandard = 0;
1234
0
  if (*rep && *pos && *cut) {
1235
0
    nonstandard = 1;
1236
0
  }
1237
1238
0
  int i;
1239
0
  int j = 0;
1240
0
  for (i = 0; i < word_size && j < hyphword_size; i++) {
1241
0
    hyphword[j++] = word[i];
1242
0
    if (hyphens[i]&1 && j < hyphword_size) {
1243
0
      if (nonstandard && (*rep)[i] && j >= (*pos)[i]) {
1244
        /* non-standard */
1245
0
        j -= (*pos)[i];
1246
0
        char *s = (*rep)[i];
1247
0
        while (*s && j < hyphword_size) {
1248
0
          hyphword[j++] = *s++;
1249
0
        }
1250
0
        i += (*cut)[i] - (*pos)[i];
1251
0
      } else {
1252
        /* standard */
1253
0
        hyphword[j++] = '=';
1254
0
      }
1255
0
    }
1256
0
  }
1257
0
  hyphword[j] = '\0';
1258
0
}
1259
1260
1261
/* no hyphenation next to a NOHYPHEN string within the first word_size bytes of the word */
1262
static void hnj_hyphen_nohyphen(HyphenDict *dict, const char *word, int word_size, char * hyphens)
1263
0
{
1264
0
  char * nh = dict->nohyphen;
1265
0
  int nhi;
1266
0
  for (nhi = 0; nhi <= dict->nohyphenl; nhi++) {
1267
0
    int nh_size = strlen(nh);
1268
0
    int k;
1269
0
    if (nh_size > 0) {
1270
0
      for (k = 0; k + nh_size <= word_size; k++) {
1271
0
        if (memcmp(word + k, nh, nh_size) == 0) {
1272
0
          hyphens[k + nh_size - 1] = '0';
1273
0
          if (k > 0) hyphens[k - 1] = '0';
1274
0
        }
1275
0
      }
1276
0
    }
1277
0
    nh = nh + nh_size + 1;
1278
0
  }
1279
0
}
1280
1281
/* rep, pos and cut point to the caller's arrays, or are NULL, and then the arrays are local */
1282
static int hnj_hyphen_hyphenate_hyphenmin (HyphenDict *dict,
1283
  const char *word, int word_size, char * hyphens,
1284
  char *hyphword, char *** rep, int ** pos, int ** cut,
1285
  int lhmin, int rhmin, int clhmin, int crhmin)
1286
0
{
1287
0
  char ** rep_local = NULL;
1288
0
  int * pos_local = NULL;
1289
0
  int * cut_local = NULL;
1290
0
  int ret = 0;
1291
0
  int i;
1292
  /* the code below reads the word up to a terminating NUL, which the caller need not give */
1293
0
  char * word_copy = (char *) hnj_malloc(word_size + 1);
1294
1295
0
  memcpy(word_copy, word, word_size);
1296
0
  word_copy[word_size] = '\0';
1297
0
  word = word_copy;
1298
1299
0
  if (!rep || !pos || !cut) {
1300
0
    rep = &rep_local;
1301
0
    pos = &pos_local;
1302
0
    cut = &cut_local;
1303
0
  }
1304
1305
0
  hnj_hyphen_hyph_(dict, word, word_size, hyphens, rep, pos, cut,
1306
0
    clhmin, crhmin, 1, 1);
1307
0
  hnj_hyphen_lhmin(dict->utf8, word, word_size, hyphens,
1308
0
    rep, pos, cut, (lhmin > 0 ? lhmin : 2));
1309
0
  hnj_hyphen_rhmin(dict->utf8, word, word_size, hyphens,
1310
0
    rep, pos, cut, (rhmin > 0 ? rhmin : 2));
1311
1312
  /* nohyphen */
1313
0
  if (dict->nohyphen) hnj_hyphen_nohyphen(dict, word, word_size, hyphens);
1314
1315
0
  if (hyphword) hnj_hyphen_hyphword(word, word_size, hyphens, hyphword, rep, pos, cut);
1316
0
  if (dict->utf8) ret = hnj_hyphen_norm(word, word_size, hyphens, rep, pos, cut);
1317
#ifdef VERBOSE
1318
  else printf ("nums: %s\n", hyphens);
1319
#endif
1320
1321
0
  if (rep_local) {
1322
0
    for (i = 0; i < word_size; i++) free(rep_local[i]);
1323
0
    free(rep_local);
1324
0
  }
1325
0
  free(pos_local);
1326
0
  free(cut_local);
1327
0
  hnj_free(word_copy);
1328
0
  return ret;
1329
0
}
1330
1331
/* main api function with default hyphenmin parameters */
1332
int hnj_hyphen_hyphenate2 (HyphenDict *dict,
1333
         const char *word, int word_size, char * hyphens,
1334
         char *hyphword, char *** rep, int ** pos, int ** cut)
1335
0
{
1336
0
  if (word_size < 1 || word_size > MAX_WORD_SIZE)
1337
0
    return 1;
1338
0
  return hnj_hyphen_hyphenate_hyphenmin(dict, word, word_size, hyphens, hyphword, rep, pos, cut,
1339
0
    dict->lhmin, dict->rhmin, dict->clhmin, dict->crhmin);
1340
0
}
1341
1342
/* previous main api function with hyphenmin parameters */
1343
int hnj_hyphen_hyphenate3 (HyphenDict *dict,
1344
  const char *word, int word_size, char * hyphens,
1345
  char *hyphword, char *** rep, int ** pos, int ** cut,
1346
  int lhmin, int rhmin, int clhmin, int crhmin)
1347
0
{
1348
0
  if (word_size < 1 || word_size > MAX_WORD_SIZE)
1349
0
    return 1;
1350
0
  lhmin = (lhmin > dict->lhmin) ? lhmin : dict->lhmin;
1351
0
  rhmin = (rhmin > dict->rhmin) ? rhmin : dict->rhmin;
1352
0
  clhmin = (clhmin > dict->clhmin) ? clhmin : dict->clhmin;
1353
0
  crhmin = (crhmin > dict->crhmin) ? crhmin : dict->crhmin;
1354
0
  return hnj_hyphen_hyphenate_hyphenmin(dict, word, word_size, hyphens, hyphword, rep, pos, cut,
1355
0
    lhmin, rhmin, clhmin, crhmin);
1356
0
}