MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
uca-dump.c
1 /* Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved.
2 
3  This program is free software; you can redistribute it and/or modify
4  it under the terms of the GNU General Public License as published by
5  the Free Software Foundation; version 2 of the License.
6 
7  This program is distributed in the hope that it will be useful,
8  but WITHOUT ANY WARRANTY; without even the implied warranty of
9  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  GNU General Public License for more details.
11 
12  You should have received a copy of the GNU General Public License
13  along with this program; if not, write to the Free Software
14  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
15 
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 
20 typedef unsigned char uchar;
21 typedef unsigned short uint16;
22 typedef unsigned int uint;
23 
24 #define MY_UCA_MAXWEIGHT_TO_PARSE 64
25 #define MY_UCA_MAXWEIGHT_TO_DUMP 8
26 #define MY_UCA_MAXLEVEL 4
27 #define MY_UCA_VERSION_SIZE 32
28 #define MY_UCA_MAX_CONTRACTION 6
29 
30 #define MY_UCA_NCONTRACTIONS 1024
31 #define MY_UCA_MAXCHAR (0x10FFFF+1)
32 #define MY_UCA_NCHARS 256
33 #define MY_UCA_CMASK 255
34 #define MY_UCA_PSHIFT 8
35 #define MY_UCA_NPAGES MY_UCA_MAXCHAR/MY_UCA_NCHARS
36 typedef struct uca_item_st
37 {
38  uchar num; /* Number of weights */
39  uint16 weight[MY_UCA_MAXLEVEL+1][MY_UCA_MAXWEIGHT_TO_DUMP];
40  /* +1 for trailing zero */
41 } MY_UCA_ITEM;
42 
43 
44 typedef struct uca_contraction_st
45 {
46  uint ch[MY_UCA_MAX_CONTRACTION];
47  MY_UCA_ITEM item;
49 
50 typedef struct uca_info_st
51 {
52  char version[MY_UCA_VERSION_SIZE];
53  MY_UCA_ITEM item[MY_UCA_MAXCHAR];
54  size_t ncontractions;
55  MY_UCA_CONTRACTION contraction[MY_UCA_NCONTRACTIONS];
56  int optimize_contractions;
57  int debug;
58 } MY_UCA;
59 
60 
61 
62 
63 
64 static int load_uca_file(MY_UCA *uca,
65  size_t maxchar, int *pageloaded)
66 {
67  char str[512];
68  size_t lineno, out_of_range_chars= 0;
69  char *weights[MY_UCA_MAXWEIGHT_TO_PARSE];
70 
71  for (lineno= 0; fgets(str, sizeof(str), stdin); lineno++)
72  {
73  char *comment;
74  char *weight;
75  char *s, *ch[MY_UCA_MAX_CONTRACTION];
76  size_t codenum, i, code;
77  MY_UCA_ITEM *item= NULL;
78 
79 
80  /* Skip comment lines */
81  if (*str== '\r' || *str == '\n' || *str == '#')
82  continue;
83 
84  /* Detect version */
85  if (*str == '@' && !strncmp(str, "@version ", 9))
86  {
87  const char *value;
88  if (strtok(str, " \r\n\t") && (value= strtok(NULL, " \r\n\t")))
89  snprintf(uca->version, MY_UCA_VERSION_SIZE, value);
90  continue;
91  }
92 
93  /* Skip big characters */
94  if ((code= strtol(str,NULL,16)) > maxchar)
95  {
96  out_of_range_chars++;
97  continue;
98  }
99 
100 
101  if ((comment= strchr(str,'#')))
102  {
103  *comment++= '\0';
104  for ( ; *comment == ' '; comment++);
105  }
106  else
107  {
108  fprintf(stderr, "Warning: could not parse line #%d:\n'%s'\n",
109  lineno, str);
110  continue;
111  }
112 
113 
114  if ((weight= strchr(str,';')))
115  {
116  *weight++= '\0';
117  for ( ; *weight==' '; weight++);
118  }
119  else
120  {
121  fprintf(stderr, "Warning: could not parse line #%d:\n%s\n", lineno, str);
122  continue;
123  }
124 
125  for (codenum= 0, s= strtok(str, " \t"); s;
126  codenum++, s= strtok(NULL, " \t"))
127  {
128  if (codenum == MY_UCA_MAX_CONTRACTION)
129  {
130  fprintf(stderr, "Contraction length is too long (%d) line #%d",
131  codenum, lineno);
132  exit(1);
133  }
134  ch[codenum]= s;
135  ch[codenum + 1]= 0;
136  }
137 
138  if (codenum > 1)
139  {
140  MY_UCA_CONTRACTION *c= &uca->contraction[uca->ncontractions++];
141  size_t i;
142  /* Multi-character weight (contraction) - not supported yet. */
143 
144  if (uca->ncontractions >= MY_UCA_NCONTRACTIONS)
145  {
146  fprintf(stderr,
147  "Too many contractions (%d) at line #%d\n"
148  "Rebuild with a bigger MY_UCA_MAXCONTRACTIONS value\n",
149  uca->ncontractions, lineno);
150  exit(1);
151  }
152  /* Copy codepoints of the contraction parts */
153  for (i= 0; i < MY_UCA_MAX_CONTRACTION; i++)
154  {
155  c->ch[i]= (i < codenum) ? (uint) strtol(ch[i], NULL, 16) : 0;
156  }
157 
158  if (uca->debug)
159  fprintf(stderr, "Contraction: %04X-%04X-%04X\n",
160  c->ch[0], c->ch[1], c->ch[2]);
161  item= &c->item;
162  }
163  else
164  {
165  item= &uca->item[code];
166  }
167 
168  /*
169  Split weight string into separate weights
170 
171  "[p1.s1.t1.q1][p2.s2.t2.q2][p3.s3.t3.q3]" ->
172 
173  "p1.s1.t1.q1" "p2.s2.t2.q2" "p3.s3.t3.q3"
174  */
175  item->num= 0;
176  s= strtok(weight, " []");
177  while (s)
178  {
179  if (item->num >= MY_UCA_MAXWEIGHT_TO_PARSE)
180  {
181  fprintf(stderr, "Line #%d has more than %d weights\n",
182  lineno, MY_UCA_MAXWEIGHT_TO_PARSE);
183  fprintf(stderr, "Can't continue.\n");
184  exit(1);
185  }
186  weights[item->num]= s;
187  s= strtok(NULL, " []");
188  item->num++;
189  }
190 
191  for (i= 0; i < item->num; i++)
192  {
193  size_t level= 0;
194 
195  if (i >= MY_UCA_MAXWEIGHT_TO_DUMP)
196  {
197  fprintf(stderr,
198  "Warning: at line %d: character %04X has"
199  " more than %d many weights (%d). "
200  "Skipping the extra weights.\n",
201  lineno, code, MY_UCA_MAXWEIGHT_TO_DUMP, item->num);
202  item->num= MY_UCA_MAXWEIGHT_TO_DUMP;
203  break;
204  }
205 
206  for (s= weights[i]; *s; )
207  {
208  char *endptr;
209  size_t part= strtol(s + 1, &endptr, 16);
210  if (i < MY_UCA_MAXWEIGHT_TO_DUMP)
211  {
212  item->weight[level][i]= part;
213  }
214  else
215  {
216  fprintf(stderr, "Too many weights (%d) at line %d\n", i, lineno);
217  exit(1);
218  }
219  s= endptr;
220  level++;
221  }
222  }
223  /* Mark that a character from this page was loaded */
224  pageloaded[code >> MY_UCA_PSHIFT]++;
225  }
226 
227  if (out_of_range_chars)
228  fprintf(stderr, "%d out-of-range characters skipped\n", out_of_range_chars);
229 
230  return 0;
231 }
232 
233 
234 /*
235  We need to initialize implicit weights because
236  some pages have both implicit and explicit weights:
237  0x4D??, 0x9F??
238 */
239 static void
240 set_implicit_weights(MY_UCA *uca, size_t maxchar)
241 {
242  size_t code;
243  /* Now set implicit weights */
244  for (code= 0; code < maxchar; code++)
245  {
246  size_t base, aaaa, bbbb;
247  MY_UCA_ITEM *item= &uca->item[code];
248 
249  if (item->num)
250  continue;
251 
252  /*
253  3400;<CJK Ideograph Extension A, First>
254  4DB5;<CJK Ideograph Extension A, Last>
255  4E00;<CJK Ideograph, First>
256  9FA5;<CJK Ideograph, Last>
257  */
258 
259  if (code >= 0x3400 && code <= 0x4DB5)
260  base= 0xFB80;
261  else if (code >= 0x4E00 && code <= 0x9FA5)
262  base= 0xFB40;
263  else
264  base= 0xFBC0;
265 
266  aaaa= base + (code >> 15);
267  bbbb= (code & 0x7FFF) | 0x8000;
268  item->weight[0][0]= aaaa;
269  item->weight[0][1]= bbbb;
270 
271  item->weight[1][0]= 0x0020;
272  item->weight[1][1]= 0x0000;
273 
274  item->weight[2][0]= 0x0002;
275  item->weight[2][1]= 0x0000;
276 
277  item->weight[3][0]= 0x0001;
278  item->weight[3][2]= 0x0000;
279 
280  item->num= 2;
281  }
282 }
283 
284 
285 static void
286 get_page_statistics(MY_UCA *uca, size_t page, size_t level,
287  size_t *maxnum, size_t *ndefs)
288 {
289  size_t offs;
290 
291  for (offs= 0; offs < MY_UCA_NCHARS; offs++)
292  {
293  size_t i, num;
294  MY_UCA_ITEM *item= &uca->item[page * MY_UCA_NCHARS + offs];
295 
296  /* Calculate only non-zero weights */
297  for (num= 0, i= 0; i < item->num; i++)
298  {
299  if (item->weight[level][i])
300  num++;
301  }
302  *maxnum= *maxnum < num ? num : *maxnum;
303 
304  /* Check if default weight */
305  if (level == 1 && num == 1)
306  {
307  /* 0020 0000 ... */
308  if (item->weight[level][0] == 0x0020)
309  (*ndefs)++;
310  }
311  else if (level == 2 && num == 1)
312  {
313  /* 0002 0000 ... */
314  if (item->weight[level][0] == 0x0002)
315  (*ndefs)++;
316  }
317  }
318 }
319 
320 
321 static const char *pname[]= {"", "_s", "_t", "_q"};
322 static const char *lname[]= {"primary", "secondary", "tertiary", "quaternary"};
323 
324 static char *
325 prefix_name(MY_UCA *uca)
326 {
327  static char prefix[MY_UCA_VERSION_SIZE];
328  char *s, *d;
329  strcpy(prefix, "uca");
330  for (s= uca->version, d= prefix + strlen(prefix); *s; s++)
331  {
332  if ((*s >= '0' && *s <= '9') || (*s >= 'a' && *s <= 'z'))
333  *d++= *s;
334  }
335  *d= '\0';
336  return prefix;
337 }
338 
339 
340 static char *
341 page_name(MY_UCA *uca, size_t page, size_t level)
342 {
343  static char page_name_buf[120];
344 
345  snprintf(page_name_buf, sizeof(page_name_buf),
346  "%s_p%03X%s",
347  prefix_name(uca),
348  page, pname[level]);
349  return page_name_buf;
350 }
351 
352 
353 /*
354  "weight" must be [MY_UCA_MAXWEIGHT_TO_DUMP+1] elements long
355 */
356 static size_t
357 normalize_weight(MY_UCA_ITEM *item, size_t level,
358  uint16 *weight, size_t weight_elements)
359 {
360  size_t num, i;
361 
362  memset(weight, 0, weight_elements * sizeof(*weight));
363 
364  /*
365  Copy non-zero weights only. For example:
366 
367  [.17A6.0020.0004.00DF][.0000.015F.0004.00DF][.17A6.0020.001F.00DF]
368 
369  makes [17A6][0000][17A6] on the primary level
370 
371  pack it to [17A6][17A7]
372  */
373 
374  for (num= 0, i= 0; i < item->num && i < MY_UCA_MAXWEIGHT_TO_DUMP; i++)
375  {
376  if (item->weight[level][i])
377  {
378  weight[num]= item->weight[level][i];
379 #ifdef INVERT_TERTIARY_WEIGHTS
380  if (level == 2)
381  {
382  /*
383  Invert tertiary weights to sort upper case letters
384  before their lower case counterparts.
385  */
386  if (weight[num] >= 0x20)
387  fprintf(stderr, "Tertiary weight is too big: %02X\n", weight[num]);
388  weight[num]= (uint) (0x20) - weight[num];
389  }
390 #endif
391  num++;
392  }
393  }
394  return num;
395 }
396 
397 
398 static void
399 print_one_page(MY_UCA *uca, size_t level,
400  size_t page, size_t maxnum)
401 {
402  size_t offs, mchars, nchars= 0, chars_per_line;
403 
404  printf("uint16 %s[]= { /* %04X (%d weights per char) */\n",
405  page_name(uca, page, level), page * MY_UCA_NCHARS, maxnum);
406 
407  /* Calculate how many wights to print per line */
408  switch (maxnum)
409  {
410  case 0: mchars= 8; chars_per_line= 8; break;
411  case 1: mchars= 8; chars_per_line= 8; break;
412  case 2: mchars= 8; chars_per_line= 4; break;
413  case 3: mchars= 9; chars_per_line= 3; break;
414  case 4: mchars= 8; chars_per_line= 2; break;
415  default:
416  mchars= uca->item[page * MY_UCA_NCHARS + offs].num;
417  chars_per_line= 1;
418  }
419 
420 
421  /* Print the page */
422  for (offs=0; offs < MY_UCA_NCHARS; offs++)
423  {
424  uint16 weight[MY_UCA_MAXWEIGHT_TO_DUMP + 1];
425  size_t num, i, code= page * MY_UCA_NCHARS + offs;
426  MY_UCA_ITEM *item= &uca->item[code];
427 
428  normalize_weight(item, level, weight, sizeof(weight)/sizeof(weight[0]));
429 
430  /* Print weights */
431  for (i= 0; i < maxnum; i++)
432  {
433  int tmp= weight[i];
434 
435  printf("0x%04X", tmp);
436 
437  if (tmp > 0xFFFF || tmp < 0)
438  {
439  fprintf(stderr,
440  "Error: Too big weight for code point %04X level %d: %08X\n",
441  code, level, tmp);
442  exit(1);
443  }
444 
445  if ((offs + 1 != MY_UCA_NCHARS) || (i + 1 != maxnum))
446  printf(",");
447  else
448  printf(" ");
449  nchars++;
450  }
451 
452  if (nchars >=mchars)
453  {
454  /* Print "\n" with a comment telling the first code on this line. */
455  printf(" /* %04X */\n", (code + 1) - chars_per_line);
456  nchars= 0;
457  }
458  else
459  {
460  printf(" ");
461  }
462  }
463  printf("};\n\n");
464 }
465 
466 
467 /*
468  Compare two weight strings.
469  Return 1 if weight string differ
470  Return 0 if weigh string are equal
471 */
472 static int
473 weight_cmp(uint16 *w1, uint16 *w2, size_t len)
474 {
475  size_t i;
476  for (i= 0; i < len; i++)
477  {
478  if (w1[i] != w2[i])
479  return 1;
480  }
481  return 0;
482 }
483 
484 
485 static void
486 print_contraction(MY_UCA *uca, MY_UCA_CONTRACTION *c, size_t level)
487 {
488  size_t ch;
489  uint16 weight[MY_UCA_MAXWEIGHT_TO_DUMP + 1];
490  int optimize= 0;
491 
492  if (c)
493  {
494  normalize_weight(&c->item, level,
495  weight, sizeof(weight)/sizeof(weight[0]));
496 
497  if (uca->optimize_contractions)
498  {
499  /*
500  Some contraction can be optimized away on certain levels.
501  For example, in Unicode-6.0:
502 
503  0E40 0E01 ; [.2395.0020.0002.0E01][.23CF.0020.001F.0E40] # <THAI CHARACTER SARA E, THAI CHARACTER KO KAI>
504 
505  Its part weights are:
506 
507  0E40 ; [.23CF.0020.0002.0E40] # THAI CHARACTER SARA E
508  0E01 ; [.2395.0020.0002.0E01] # THAI CHARACTER KO KAI
509 
510  On the secondary level weights for the contraction
511  and for the two characters in a sequence are: 0020-0020.
512 
513  So "0E40 0E01" can be optimized away of the secondary level.
514 
515  This optimization is OFF by default, as it's better to optimize
516  this at collation initialization time rather than at dump time,
517  to preserve all available DUCET data.
518 
519  Also, this does not seem to ever happen on the primary level,
520  so this optimization will not bring any serious performance
521  improvement.
522  */
523  size_t i;
524  uint16 sweight[MY_UCA_MAXWEIGHT_TO_DUMP*MY_UCA_MAX_CONTRACTION + 1], *sw;
525 
526  /* Concatenate weight arrays for the contraction parts */
527  for (sw= sweight, i= 0; c->ch[i]; i++)
528  {
529  MY_UCA_ITEM *item= &uca->item[c->ch[i]];
530  sw+= normalize_weight(item, level, sw, MY_UCA_MAXWEIGHT_TO_DUMP);
531  }
532 
533  if (sw - sweight < MY_UCA_MAXWEIGHT_TO_DUMP &&
534  !weight_cmp(sweight, weight, MY_UCA_MAXWEIGHT_TO_DUMP))
535  {
536  if (uca->debug)
537  fprintf(stderr, "Equal[%d]: %04X [%04X-%04X-%04X] == {%04X,%04X,%04X} [%04X-%04X-%04X]\n",
538  level,
539  c->ch[0], sweight[0], sweight[1], sweight[2],
540  c->ch[0], c->ch[1], c->ch[2],
541  weight[0], weight[1], weight[2]);
542  optimize= 1;
543  }
544  }
545  }
546 
547  printf("%s{", optimize ? "/* " : "");
548  for (ch= 0; ch < MY_UCA_MAX_CONTRACTION; ch++)
549  {
550  uint codepoint= c ? c->ch[ch] : 0; /* Real character or terminator line */
551  printf("%s", ch > 0 ? "," : "");
552  if (codepoint)
553  printf("0x%04X", codepoint);
554  else
555  printf("0");
556  }
557  printf("},");
558  printf("{");
559  for (ch= 0; ch < MY_UCA_MAXWEIGHT_TO_DUMP; ch++)
560  {
561  uint w= c ? weight[ch] : 0; /* Real chr or terminator */
562  printf("%s", ch > 0 ? "," : "");
563  if (w)
564  printf("0x%04X", w);
565  else
566  printf("0");
567  }
568  printf("}");
569  printf(",0");
570  printf("},%s\n", optimize ? " */" : "");
571 }
572 
573 
574 static void
575 print_contractions(MY_UCA *uca, size_t level)
576 {
577  size_t i;
578 
579  printf("\n\n");
580  printf("/* Contractions, %s level */\n", lname[level]);
581  printf("MY_CONTRACTION %s_default_contraction%s[]= {\n",
582  prefix_name(uca), pname[level]);
583  for (i= 0; i < uca->ncontractions; i++)
584  {
585  MY_UCA_CONTRACTION *c= &uca->contraction[i];
586  print_contraction(uca, c, level);
587  }
588  print_contraction(uca, NULL, level);
589  printf("};\n");
590 }
591 
592 
593 static int contractions= 0;
594 static int nlevels= 1;
595 
596 static void
597 usage(FILE *file, int rc)
598 {
599  fprintf(file, "Usage:\n");
600  fprintf(file, "uca-dump [options...] < /path/to/allkeys.txt\n");
601  fprintf(file, "\n");
602  fprintf(file, "Options:\n");
603  fprintf(file, "--levels=NUM How many levels to dump, 1-4, default 1.\n");
604  fprintf(file, "--contractions=NUM Whether to dump comtractions, 0-1, default 0.\n");
605  fprintf(file, "--optimize-contractions=NUM Whether to optimize contractions, 0-1, default 0.\n");
606  fprintf(file, "--debug=NUM Print debug information, 0-1, default 0.\n");
607  fprintf(file, "\n\n");
608  exit(rc);
609 }
610 
611 
612 static int
613 get_int_option(const char *str, const char *name, int *num)
614 {
615  size_t namelen= strlen(name);
616  if (!strncmp(str, name, namelen))
617  {
618  *num= atoi(str + namelen);
619  if (*num == 0 && str[namelen] !='0')
620  {
621  fprintf(stderr, "\nBad numeric option value: %s\n\n", str);
622  usage(stderr, 1);
623  }
624  return 1;
625  }
626  return 0;
627 }
628 
629 
630 static void
631 process_options(int ac, char **av, MY_UCA *uca)
632 {
633  size_t i;
634  for (i= 1; i < ac ; i++)
635  {
636  /*printf("[%d]=%s\n", i, av[i]);*/
637  if (!get_int_option(av[i], "--levels=", &nlevels) &&
638  !get_int_option(av[i], "--contractions=", &contractions) &&
639  !get_int_option(av[i], "--debug=", &uca->debug) &&
640  !get_int_option(av[i], "--optimize-contractions=", &uca->optimize_contractions))
641  {
642  fprintf(stderr, "\nUnknown option: %s\n\n", av[i]);
643  usage(stderr, 1);
644  }
645  }
646 }
647 
648 
649 int
650 main(int ac, char **av)
651 {
652  static MY_UCA uca;
653  size_t level, maxchar= MY_UCA_MAXCHAR;
654  static int pageloaded[MY_UCA_NPAGES];
655 
656  memset(&uca, 0, sizeof(uca));
657 
658  process_options(ac, av, &uca);
659 
660  memset(pageloaded, 0, sizeof(pageloaded));
661 
662  load_uca_file(&uca, maxchar, pageloaded);
663 
664  /* Now set implicit weights */
665  set_implicit_weights(&uca, maxchar);
666 
667 
668  printf("#include \"my_uca.h\"\n");
669  printf("\n\n");
670  printf("#define MY_UCA_NPAGES %d\n", MY_UCA_NPAGES);
671  printf("#define MY_UCA_NCHARS %d\n", MY_UCA_NCHARS);
672  printf("#define MY_UCA_CMASK %d\n", MY_UCA_CMASK);
673  printf("#define MY_UCA_PSHIFT %d\n", MY_UCA_PSHIFT);
674  printf("\n\n");
675  printf("/* Created from allkeys.txt. Unicode version '%s'. */\n\n", uca.version);
676 
677  for (level= 0; level < nlevels; level++)
678  {
679  size_t page;
680  int pagemaxlen[MY_UCA_NPAGES];
681 
682  for (page=0; page < MY_UCA_NPAGES; page++)
683  {
684  size_t maxnum= 0;
685  size_t ndefs= 0;
686 
687  pagemaxlen[page]= 0;
688 
689  /* Skip this page if no weights were loaded */
690  if (!pageloaded[page])
691  continue;
692 
693  /*
694  Calculate number of weights per character
695  and number of default weights.
696  */
697  get_page_statistics(&uca, page, level, &maxnum, &ndefs);
698 
699 
700  maxnum++; /* For zero terminator */
701 
702 
703  /*
704  If the page have only default weights
705  then no needs to dump it, skip.
706  */
707  if (ndefs == MY_UCA_NCHARS)
708  continue;
709 
710  pagemaxlen[page]= maxnum;
711 
712 
713  /* Now print this page */
714  print_one_page(&uca, level, page, maxnum);
715  }
716 
717 
718  /* Print page lengths */
719  printf("uchar %s_length%s[%d]={\n",
720  prefix_name(&uca), pname[level], MY_UCA_NPAGES);
721  for (page=0; page < MY_UCA_NPAGES; page++)
722  {
723  printf("%d%s%s",
724  pagemaxlen[page],
725  page < MY_UCA_NPAGES - 1 ? "," : "" ,
726  (page + 1) % 16 ? "" : "\n");
727  }
728  printf("};\n");
729 
730 
731  /* Print page index */
732  printf("uint16 *%s_weight%s[%d]={\n",
733  prefix_name(&uca), pname[level], MY_UCA_NPAGES);
734  for (page=0; page < MY_UCA_NPAGES; page++)
735  {
736  const char *comma= page < MY_UCA_NPAGES - 1 ? "," : "";
737  const char *nline= (page + 1) % 4 ? "" : "\n";
738  if (!pagemaxlen[page])
739  printf("NULL %s%s%s", level ? " ": "", comma , nline);
740  else
741  printf("%s%s%s", page_name(&uca, page, level), comma, nline);
742  }
743  printf("};\n");
744 
745  /* Print contractions */
746  if (contractions)
747  print_contractions(&uca, level);
748  }
749 
750 
751  printf("int main(void){ return 0;};\n");
752  return 0;
753 }