1 /*      $Id: chars.c,v 1.52 2011/11/08 00:15:23 kristaps Exp $ */
   2 /*
   3  * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
   4  * Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org>
   5  *
   6  * Permission to use, copy, modify, and distribute this software for any
   7  * purpose with or without fee is hereby granted, provided that the above
   8  * copyright notice and this permission notice appear in all copies.
   9  *
  10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  17  */
  18 #ifdef HAVE_CONFIG_H
  19 #include "config.h"
  20 #endif
  21 
  22 #include <assert.h>
  23 #include <ctype.h>
  24 #include <stdlib.h>
  25 #include <string.h>
  26 
  27 #include "mandoc.h"
  28 #include "libmandoc.h"
  29 
  30 #define PRINT_HI         126
  31 #define PRINT_LO         32
  32 
  33 struct  ln {
  34         struct ln        *next;
  35         const char       *code;
  36         const char       *ascii;
  37         int               unicode;
  38 };
  39 
  40 #define LINES_MAX         328
  41 
  42 #define CHAR(in, ch, code) \
  43         { NULL, (in), (ch), (code) },
  44 
  45 #define CHAR_TBL_START    static struct ln lines[LINES_MAX] = {
  46 #define CHAR_TBL_END      };
  47 
  48 #include "chars.in"
  49 
  50 struct  mchars {
  51         struct ln       **htab;
  52 };
  53 
  54 static  const struct ln  *find(const struct mchars *, 
  55                                 const char *, size_t);
  56 
  57 void
  58 mchars_free(struct mchars *arg)
  59 {
  60 
  61         free(arg->htab);
  62         free(arg);
  63 }
  64 
  65 struct mchars *
  66 mchars_alloc(void)
  67 {
  68         struct mchars    *tab;
  69         struct ln       **htab;
  70         struct ln        *pp;
  71         int               i, hash;
  72 
  73         /*
  74          * Constructs a very basic chaining hashtable.  The hash routine
  75          * is simply the integral value of the first character.
  76          * Subsequent entries are chained in the order they're processed.
  77          */
  78 
  79         tab = mandoc_malloc(sizeof(struct mchars));
  80         htab = mandoc_calloc(PRINT_HI - PRINT_LO + 1, sizeof(struct ln **));
  81 
  82         for (i = 0; i < LINES_MAX; i++) {
  83                 hash = (int)lines[i].code[0] - PRINT_LO;
  84 
  85                 if (NULL == (pp = htab[hash])) {
  86                         htab[hash] = &lines[i];
  87                         continue;
  88                 }
  89 
  90                 for ( ; pp->next; pp = pp->next)
  91                         /* Scan ahead. */ ;
  92                 pp->next = &lines[i];
  93         }
  94 
  95         tab->htab = htab;
  96         return(tab);
  97 }
  98 
  99 int
 100 mchars_spec2cp(const struct mchars *arg, const char *p, size_t sz)
 101 {
 102         const struct ln *ln;
 103 
 104         ln = find(arg, p, sz);
 105         if (NULL == ln)
 106                 return(-1);
 107         return(ln->unicode);
 108 }
 109 
 110 char
 111 mchars_num2char(const char *p, size_t sz)
 112 {
 113         int               i;
 114 
 115         if ((i = mandoc_strntoi(p, sz, 10)) < 0)
 116                 return('\0');
 117         return(i > 0 && i < 256 && isprint(i) ? 
 118                         /* LINTED */ i : '\0');
 119 }
 120 
 121 int
 122 mchars_num2uc(const char *p, size_t sz)
 123 {
 124         int               i;
 125 
 126         if ((i = mandoc_strntoi(p, sz, 16)) < 0)
 127                 return('\0');
 128         /* FIXME: make sure we're not in a bogus range. */
 129         return(i > 0x80 && i <= 0x10FFFF ? i : '\0');
 130 }
 131 
 132 const char *
 133 mchars_spec2str(const struct mchars *arg, 
 134                 const char *p, size_t sz, size_t *rsz)
 135 {
 136         const struct ln *ln;
 137 
 138         ln = find(arg, p, sz);
 139         if (NULL == ln) {
 140                 *rsz = 1;
 141                 return(NULL);
 142         }
 143 
 144         *rsz = strlen(ln->ascii);
 145         return(ln->ascii);
 146 }
 147 
 148 static const struct ln *
 149 find(const struct mchars *tab, const char *p, size_t sz)
 150 {
 151         const struct ln  *pp;
 152         int               hash;
 153 
 154         assert(p);
 155 
 156         if (0 == sz || p[0] < PRINT_LO || p[0] > PRINT_HI)
 157                 return(NULL);
 158 
 159         hash = (int)p[0] - PRINT_LO;
 160 
 161         for (pp = tab->htab[hash]; pp; pp = pp->next)
 162                 if (0 == strncmp(pp->code, p, sz) && 
 163                                 '\0' == pp->code[(int)sz])
 164                         return(pp);
 165 
 166         return(NULL);
 167 }