Print this page
5051 import mdocml-1.12.3
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
Approved by: TBD

Split Close
Expand all
Collapse all
          --- old/usr/src/cmd/mandoc/mandoc.c
          +++ new/usr/src/cmd/mandoc/mandoc.c
   1      -/*      $Id: mandoc.c,v 1.62 2011/12/03 16:08:51 schwarze Exp $ */
        1 +/*      $Id: mandoc.c,v 1.74 2013/12/30 18:30:32 schwarze Exp $ */
   2    2  /*
   3    3   * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
   4      - * Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org>
        4 + * Copyright (c) 2011, 2012, 2013 Ingo Schwarze <schwarze@openbsd.org>
   5    5   *
   6    6   * Permission to use, copy, modify, and distribute this software for any
   7    7   * purpose with or without fee is hereby granted, provided that the above
   8    8   * copyright notice and this permission notice appear in all copies.
   9    9   *
  10   10   * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
  11   11   * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  12   12   * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
  13   13   * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  14   14   * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
↓ open down ↓ 15 lines elided ↑ open up ↑
  30   30  #include <string.h>
  31   31  #include <time.h>
  32   32  
  33   33  #include "mandoc.h"
  34   34  #include "libmandoc.h"
  35   35  
  36   36  #define DATESIZE 32
  37   37  
  38   38  static  int      a2time(time_t *, const char *, const char *);
  39   39  static  char    *time2a(time_t);
  40      -static  int      numescape(const char *);
  41   40  
  42      -/*
  43      - * Pass over recursive numerical expressions.  This context of this
  44      - * function is important: it's only called within character-terminating
  45      - * escapes (e.g., \s[xxxyyy]), so all we need to do is handle initial
  46      - * recursion: we don't care about what's in these blocks. 
  47      - * This returns the number of characters skipped or -1 if an error
  48      - * occurs (the caller should bail).
  49      - */
  50      -static int
  51      -numescape(const char *start)
       41 +
       42 +enum mandoc_esc
       43 +mandoc_escape(const char **end, const char **start, int *sz)
  52   44  {
  53      -        int              i;
  54      -        size_t           sz;
  55      -        const char      *cp;
       45 +        const char      *local_start;
       46 +        int              local_sz;
       47 +        char             term;
       48 +        enum mandoc_esc  gly; 
  56   49  
  57      -        i = 0;
       50 +        /*
       51 +         * When the caller doesn't provide return storage,
       52 +         * use local storage.
       53 +         */
  58   54  
  59      -        /* The expression consists of a subexpression. */
       55 +        if (NULL == start)
       56 +                start = &local_start;
       57 +        if (NULL == sz)
       58 +                sz = &local_sz;
  60   59  
  61      -        if ('\\' == start[i]) {
  62      -                cp = &start[++i];
  63      -                /*
  64      -                 * Read past the end of the subexpression.
  65      -                 * Bail immediately on errors.
  66      -                 */
  67      -                if (ESCAPE_ERROR == mandoc_escape(&cp, NULL, NULL))
  68      -                        return(-1);
  69      -                return(i + cp - &start[i]);
  70      -        } 
  71      -
  72      -        if ('(' != start[i++])
  73      -                return(0);
  74      -
  75   60          /*
  76      -         * A parenthesised subexpression.  Read until the closing
  77      -         * parenthesis, making sure to handle any nested subexpressions
  78      -         * that might ruin our parse.
       61 +         * Beyond the backslash, at least one input character
       62 +         * is part of the escape sequence.  With one exception
       63 +         * (see below), that character won't be returned.
  79   64           */
  80   65  
  81      -        while (')' != start[i]) {
  82      -                sz = strcspn(&start[i], ")\\");
  83      -                i += (int)sz;
  84      -
  85      -                if ('\0' == start[i])
  86      -                        return(-1);
  87      -                else if ('\\' != start[i])
  88      -                        continue;
  89      -
  90      -                cp = &start[++i];
  91      -                if (ESCAPE_ERROR == mandoc_escape(&cp, NULL, NULL))
  92      -                        return(-1);
  93      -                i += cp - &start[i];
  94      -        }
  95      -
  96      -        /* Read past the terminating ')'. */
  97      -        return(++i);
  98      -}
  99      -
 100      -enum mandoc_esc
 101      -mandoc_escape(const char **end, const char **start, int *sz)
 102      -{
 103      -        char             c, term, numeric;
 104      -        int              i, lim, ssz, rlim;
 105      -        const char      *cp, *rstart;
 106      -        enum mandoc_esc  gly; 
 107      -
 108      -        cp = *end;
 109      -        rstart = cp;
 110      -        if (start)
 111      -                *start = rstart;
 112      -        i = lim = 0;
 113   66          gly = ESCAPE_ERROR;
 114      -        term = numeric = '\0';
       67 +        *start = ++*end;
       68 +        *sz = 0;
       69 +        term = '\0';
 115   70  
 116      -        switch ((c = cp[i++])) {
       71 +        switch ((*start)[-1]) {
 117   72          /*
 118   73           * First the glyphs.  There are several different forms of
 119   74           * these, but each eventually returns a substring of the glyph
 120   75           * name.
 121   76           */
 122   77          case ('('):
 123   78                  gly = ESCAPE_SPECIAL;
 124      -                lim = 2;
       79 +                *sz = 2;
 125   80                  break;
 126   81          case ('['):
 127   82                  gly = ESCAPE_SPECIAL;
 128   83                  /*
 129   84                   * Unicode escapes are defined in groff as \[uXXXX] to
 130   85                   * \[u10FFFF], where the contained value must be a valid
 131   86                   * Unicode codepoint.  Here, however, only check whether
 132   87                   * it's not a zero-width escape.
 133   88                   */
 134      -                if ('u' == cp[i] && ']' != cp[i + 1])
       89 +                if ('u' == (*start)[0] && ']' != (*start)[1])
 135   90                          gly = ESCAPE_UNICODE;
 136   91                  term = ']';
 137   92                  break;
 138   93          case ('C'):
 139      -                if ('\'' != cp[i])
       94 +                if ('\'' != **start)
 140   95                          return(ESCAPE_ERROR);
 141      -                gly = ESCAPE_SPECIAL;
       96 +                *start = ++*end;
       97 +                if ('u' == (*start)[0] && '\'' != (*start)[1])
       98 +                        gly = ESCAPE_UNICODE;
       99 +                else
      100 +                        gly = ESCAPE_SPECIAL;
 142  101                  term = '\'';
 143  102                  break;
 144  103  
 145  104          /*
      105 +         * Escapes taking no arguments at all.
      106 +         */
      107 +        case ('d'):
      108 +                /* FALLTHROUGH */
      109 +        case ('u'):
      110 +                return(ESCAPE_IGNORE);
      111 +
      112 +        /*
      113 +         * The \z escape is supposed to output the following
      114 +         * character without advancing the cursor position.  
      115 +         * Since we are mostly dealing with terminal mode,
      116 +         * let us just skip the next character.
      117 +         */
      118 +        case ('z'):
      119 +                return(ESCAPE_SKIPCHAR);
      120 +
      121 +        /*
 146  122           * Handle all triggers matching \X(xy, \Xx, and \X[xxxx], where
 147  123           * 'X' is the trigger.  These have opaque sub-strings.
 148  124           */
 149  125          case ('F'):
 150  126                  /* FALLTHROUGH */
 151  127          case ('g'):
 152  128                  /* FALLTHROUGH */
 153  129          case ('k'):
 154  130                  /* FALLTHROUGH */
 155  131          case ('M'):
↓ open down ↓ 3 lines elided ↑ open up ↑
 159  135          case ('n'):
 160  136                  /* FALLTHROUGH */
 161  137          case ('V'):
 162  138                  /* FALLTHROUGH */
 163  139          case ('Y'):
 164  140                  gly = ESCAPE_IGNORE;
 165  141                  /* FALLTHROUGH */
 166  142          case ('f'):
 167  143                  if (ESCAPE_ERROR == gly)
 168  144                          gly = ESCAPE_FONT;
 169      -
 170      -                rstart= &cp[i];
 171      -                if (start) 
 172      -                        *start = rstart;
 173      -
 174      -                switch (cp[i++]) {
      145 +                switch (**start) {
 175  146                  case ('('):
 176      -                        lim = 2;
      147 +                        *start = ++*end;
      148 +                        *sz = 2;
 177  149                          break;
 178  150                  case ('['):
      151 +                        *start = ++*end;
 179  152                          term = ']';
 180  153                          break;
 181  154                  default:
 182      -                        lim = 1;
 183      -                        i--;
      155 +                        *sz = 1;
 184  156                          break;
 185  157                  }
 186  158                  break;
 187  159  
 188  160          /*
 189  161           * These escapes are of the form \X'Y', where 'X' is the trigger
 190  162           * and 'Y' is any string.  These have opaque sub-strings.
 191  163           */
 192  164          case ('A'):
 193  165                  /* FALLTHROUGH */
 194  166          case ('b'):
 195  167                  /* FALLTHROUGH */
      168 +        case ('B'):
      169 +                /* FALLTHROUGH */
 196  170          case ('D'):
 197  171                  /* FALLTHROUGH */
 198  172          case ('o'):
 199  173                  /* FALLTHROUGH */
 200  174          case ('R'):
 201  175                  /* FALLTHROUGH */
      176 +        case ('w'):
      177 +                /* FALLTHROUGH */
 202  178          case ('X'):
 203  179                  /* FALLTHROUGH */
 204  180          case ('Z'):
 205      -                if ('\'' != cp[i++])
      181 +                if ('\'' != **start)
 206  182                          return(ESCAPE_ERROR);
 207  183                  gly = ESCAPE_IGNORE;
      184 +                *start = ++*end;
 208  185                  term = '\'';
 209  186                  break;
 210  187  
 211  188          /*
 212  189           * These escapes are of the form \X'N', where 'X' is the trigger
 213  190           * and 'N' resolves to a numerical expression.
 214  191           */
 215      -        case ('B'):
 216      -                /* FALLTHROUGH */
 217  192          case ('h'):
 218  193                  /* FALLTHROUGH */
 219  194          case ('H'):
 220  195                  /* FALLTHROUGH */
 221  196          case ('L'):
 222  197                  /* FALLTHROUGH */
 223  198          case ('l'):
 224      -                gly = ESCAPE_NUMBERED;
 225  199                  /* FALLTHROUGH */
 226  200          case ('S'):
 227  201                  /* FALLTHROUGH */
 228  202          case ('v'):
 229  203                  /* FALLTHROUGH */
 230      -        case ('w'):
 231      -                /* FALLTHROUGH */
 232  204          case ('x'):
 233      -                if (ESCAPE_ERROR == gly)
 234      -                        gly = ESCAPE_IGNORE;
 235      -                if ('\'' != cp[i++])
      205 +                if ('\'' != **start)
 236  206                          return(ESCAPE_ERROR);
 237      -                term = numeric = '\'';
      207 +                gly = ESCAPE_IGNORE;
      208 +                *start = ++*end;
      209 +                term = '\'';
 238  210                  break;
 239  211  
 240  212          /*
 241  213           * Special handling for the numbered character escape.
 242  214           * XXX Do any other escapes need similar handling?
 243  215           */
 244  216          case ('N'):
 245      -                if ('\0' == cp[i])
      217 +                if ('\0' == **start)
 246  218                          return(ESCAPE_ERROR);
 247      -                *end = &cp[++i];
 248      -                if (isdigit((unsigned char)cp[i-1]))
      219 +                (*end)++;
      220 +                if (isdigit((unsigned char)**start)) {
      221 +                        *sz = 1;
 249  222                          return(ESCAPE_IGNORE);
      223 +                }
      224 +                (*start)++;
 250  225                  while (isdigit((unsigned char)**end))
 251  226                          (*end)++;
 252      -                if (start)
 253      -                        *start = &cp[i];
 254      -                if (sz)
 255      -                        *sz = *end - &cp[i];
      227 +                *sz = *end - *start;
 256  228                  if ('\0' != **end)
 257  229                          (*end)++;
 258  230                  return(ESCAPE_NUMBERED);
 259  231  
 260  232          /* 
 261  233           * Sizes get a special category of their own.
 262  234           */
 263  235          case ('s'):
 264  236                  gly = ESCAPE_IGNORE;
 265  237  
 266      -                rstart = &cp[i];
 267      -                if (start) 
 268      -                        *start = rstart;
 269      -
 270  238                  /* See +/- counts as a sign. */
 271      -                c = cp[i];
 272      -                if ('+' == c || '-' == c || ASCII_HYPH == c)
 273      -                        ++i;
      239 +                if ('+' == **end || '-' == **end || ASCII_HYPH == **end)
      240 +                        (*end)++;
 274  241  
 275      -                switch (cp[i++]) {
      242 +                switch (**end) {
 276  243                  case ('('):
 277      -                        lim = 2;
      244 +                        *start = ++*end;
      245 +                        *sz = 2;
 278  246                          break;
 279  247                  case ('['):
 280      -                        term = numeric = ']';
      248 +                        *start = ++*end;
      249 +                        term = ']';
 281  250                          break;
 282  251                  case ('\''):
 283      -                        term = numeric = '\'';
      252 +                        *start = ++*end;
      253 +                        term = '\'';
 284  254                          break;
 285  255                  default:
 286      -                        lim = 1;
 287      -                        i--;
      256 +                        *sz = 1;
 288  257                          break;
 289  258                  }
 290  259  
 291      -                /* See +/- counts as a sign. */
 292      -                c = cp[i];
 293      -                if ('+' == c || '-' == c || ASCII_HYPH == c)
 294      -                        ++i;
 295      -
 296  260                  break;
 297  261  
 298  262          /*
 299  263           * Anything else is assumed to be a glyph.
      264 +         * In this case, pass back the character after the backslash.
 300  265           */
 301  266          default:
 302  267                  gly = ESCAPE_SPECIAL;
 303      -                lim = 1;
 304      -                i--;
      268 +                *start = --*end;
      269 +                *sz = 1;
 305  270                  break;
 306  271          }
 307  272  
 308  273          assert(ESCAPE_ERROR != gly);
 309  274  
 310      -        rstart = &cp[i];
 311      -        if (start)
 312      -                *start = rstart;
 313      -
 314  275          /*
 315      -         * If a terminating block has been specified, we need to
 316      -         * handle the case of recursion, which could have their
 317      -         * own terminating blocks that mess up our parse.  This, by the
 318      -         * way, means that the "start" and "size" values will be
 319      -         * effectively meaningless.
      276 +         * Read up to the terminating character,
      277 +         * paying attention to nested escapes.
 320  278           */
 321  279  
 322      -        ssz = 0;
 323      -        if (numeric && -1 == (ssz = numescape(&cp[i])))
 324      -                return(ESCAPE_ERROR);
 325      -
 326      -        i += ssz;
 327      -        rlim = -1;
 328      -
 329      -        /*
 330      -         * We have a character terminator.  Try to read up to that
 331      -         * character.  If we can't (i.e., we hit the nil), then return
 332      -         * an error; if we can, calculate our length, read past the
 333      -         * terminating character, and exit.
 334      -         */
 335      -
 336  280          if ('\0' != term) {
 337      -                *end = strchr(&cp[i], term);
 338      -                if ('\0' == *end)
      281 +                while (**end != term) {
      282 +                        switch (**end) {
      283 +                        case ('\0'):
      284 +                                return(ESCAPE_ERROR);
      285 +                        case ('\\'):
      286 +                                (*end)++;
      287 +                                if (ESCAPE_ERROR ==
      288 +                                    mandoc_escape(end, NULL, NULL))
      289 +                                        return(ESCAPE_ERROR);
      290 +                                break;
      291 +                        default:
      292 +                                (*end)++;
      293 +                                break;
      294 +                        }
      295 +                }
      296 +                *sz = (*end)++ - *start;
      297 +        } else {
      298 +                assert(*sz > 0);
      299 +                if ((size_t)*sz > strlen(*start))
 339  300                          return(ESCAPE_ERROR);
 340      -
 341      -                rlim = *end - &cp[i];
 342      -                if (sz)
 343      -                        *sz = rlim;
 344      -                (*end)++;
 345      -                goto out;
      301 +                *end += *sz;
 346  302          }
 347  303  
 348      -        assert(lim > 0);
 349      -
 350      -        /*
 351      -         * We have a numeric limit.  If the string is shorter than that,
 352      -         * stop and return an error.  Else adjust our endpoint, length,
 353      -         * and return the current glyph.
 354      -         */
 355      -
 356      -        if ((size_t)lim > strlen(&cp[i]))
 357      -                return(ESCAPE_ERROR);
 358      -
 359      -        rlim = lim;
 360      -        if (sz)
 361      -                *sz = rlim;
 362      -
 363      -        *end = &cp[i] + lim;
 364      -
 365      -out:
 366      -        assert(rlim >= 0 && rstart);
 367      -
 368  304          /* Run post-processors. */
 369  305  
 370  306          switch (gly) {
 371  307          case (ESCAPE_FONT):
 372      -                /*
 373      -                 * Pretend that the constant-width font modes are the
 374      -                 * same as the regular font modes.
 375      -                 */
 376      -                if (2 == rlim && 'C' == *rstart)
 377      -                        rstart++;
 378      -                else if (1 != rlim)
      308 +                if (2 == *sz) {
      309 +                        if ('C' == **start) {
      310 +                                /*
      311 +                                 * Treat constant-width font modes
      312 +                                 * just like regular font modes.
      313 +                                 */
      314 +                                (*start)++;
      315 +                                (*sz)--;
      316 +                        } else {
      317 +                                if ('B' == (*start)[0] && 'I' == (*start)[1])
      318 +                                        gly = ESCAPE_FONTBI;
      319 +                                break;
      320 +                        }
      321 +                } else if (1 != *sz)
 379  322                          break;
 380  323  
 381      -                switch (*rstart) {
      324 +                switch (**start) {
 382  325                  case ('3'):
 383  326                          /* FALLTHROUGH */
 384  327                  case ('B'):
 385  328                          gly = ESCAPE_FONTBOLD;
 386  329                          break;
 387  330                  case ('2'):
 388  331                          /* FALLTHROUGH */
 389  332                  case ('I'):
 390  333                          gly = ESCAPE_FONTITALIC;
 391  334                          break;
↓ open down ↓ 1 lines elided ↑ open up ↑
 393  336                          gly = ESCAPE_FONTPREV;
 394  337                          break;
 395  338                  case ('1'):
 396  339                          /* FALLTHROUGH */
 397  340                  case ('R'):
 398  341                          gly = ESCAPE_FONTROMAN;
 399  342                          break;
 400  343                  }
 401  344                  break;
 402  345          case (ESCAPE_SPECIAL):
 403      -                if (1 != rlim)
 404      -                        break;
 405      -                if ('c' == *rstart)
      346 +                if (1 == *sz && 'c' == **start)
 406  347                          gly = ESCAPE_NOSPACE;
 407  348                  break;
 408  349          default:
 409  350                  break;
 410  351          }
 411  352  
 412  353          return(gly);
 413  354  }
 414  355  
 415  356  void *
↓ open down ↓ 61 lines elided ↑ open up ↑
 477  418                  exit((int)MANDOCLEVEL_SYSERR);
 478  419          }
 479  420  
 480  421          return(p);
 481  422  }
 482  423  
 483  424  /*
 484  425   * Parse a quoted or unquoted roff-style request or macro argument.
 485  426   * Return a pointer to the parsed argument, which is either the original
 486  427   * pointer or advanced by one byte in case the argument is quoted.
 487      - * Null-terminate the argument in place.
      428 + * NUL-terminate the argument in place.
 488  429   * Collapse pairs of quotes inside quoted arguments.
 489  430   * Advance the argument pointer to the next argument,
 490      - * or to the null byte terminating the argument line.
      431 + * or to the NUL byte terminating the argument line.
 491  432   */
 492  433  char *
 493  434  mandoc_getarg(struct mparse *parse, char **cpp, int ln, int *pos)
 494  435  {
 495  436          char     *start, *cp;
 496  437          int       quoted, pairs, white;
 497  438  
 498  439          /* Quoting can only start with a new word. */
 499  440          start = *cpp;
 500  441          quoted = 0;
 501  442          if ('"' == *start) {
 502  443                  quoted = 1;
 503  444                  start++;
 504  445          } 
 505  446  
 506  447          pairs = 0;
 507  448          white = 0;
 508  449          for (cp = start; '\0' != *cp; cp++) {
 509      -                /* Move left after quoted quotes and escaped backslashes. */
      450 +
      451 +                /*
      452 +                 * Move the following text left
      453 +                 * after quoted quotes and after "\\" and "\t".
      454 +                 */
 510  455                  if (pairs)
 511  456                          cp[-pairs] = cp[0];
      457 +
 512  458                  if ('\\' == cp[0]) {
 513      -                        if ('\\' == cp[1]) {
 514      -                                /* Poor man's copy mode. */
      459 +                        /*
      460 +                         * In copy mode, translate double to single
      461 +                         * backslashes and backslash-t to literal tabs.
      462 +                         */
      463 +                        switch (cp[1]) {
      464 +                        case ('t'):
      465 +                                cp[0] = '\t';
      466 +                                /* FALLTHROUGH */
      467 +                        case ('\\'):
 515  468                                  pairs++;
 516  469                                  cp++;
 517      -                        } else if (0 == quoted && ' ' == cp[1])
      470 +                                break;
      471 +                        case (' '):
 518  472                                  /* Skip escaped blanks. */
 519      -                                cp++;
      473 +                                if (0 == quoted)
      474 +                                        cp++;
      475 +                                break;
      476 +                        default:
      477 +                                break;
      478 +                        }
 520  479                  } else if (0 == quoted) {
 521  480                          if (' ' == cp[0]) {
 522  481                                  /* Unescaped blanks end unquoted args. */
 523  482                                  white = 1;
 524  483                                  break;
 525  484                          }
 526  485                  } else if ('"' == cp[0]) {
 527  486                          if ('"' == cp[1]) {
 528  487                                  /* Quoted quotes collapse. */
 529  488                                  pairs++;
↓ open down ↓ 3 lines elided ↑ open up ↑
 533  492                                  quoted = 2;
 534  493                                  break;
 535  494                          }
 536  495                  }
 537  496          }
 538  497  
 539  498          /* Quoted argument without a closing quote. */
 540  499          if (1 == quoted)
 541  500                  mandoc_msg(MANDOCERR_BADQUOTE, parse, ln, *pos, NULL);
 542  501  
 543      -        /* Null-terminate this argument and move to the next one. */
      502 +        /* NUL-terminate this argument and move to the next one. */
 544  503          if (pairs)
 545  504                  cp[-pairs] = '\0';
 546  505          if ('\0' != *cp) {
 547  506                  *cp++ = '\0';
 548  507                  while (' ' == *cp)
 549  508                          cp++;
 550  509          }
 551  510          *pos += (int)(cp - start) + (quoted ? 1 : 0);
 552  511          *cpp = cp;
 553  512  
↓ open down ↓ 116 lines elided ↑ open up ↑
 670  629                          found = 1;
 671  630                          break;
 672  631                  default:
 673  632                          return(found && (!enclosed || isalnum((unsigned char)*q)));
 674  633                  }
 675  634          }
 676  635  
 677  636          return(found && !enclosed);
 678  637  }
 679  638  
 680      -/*
 681      - * Find out whether a line is a macro line or not.  If it is, adjust the
 682      - * current position and return one; if it isn't, return zero and don't
 683      - * change the current position.
 684      - */
 685      -int
 686      -mandoc_getcontrol(const char *cp, int *ppos)
 687      -{
 688      -        int             pos;
 689      -
 690      -        pos = *ppos;
 691      -
 692      -        if ('\\' == cp[pos] && '.' == cp[pos + 1])
 693      -                pos += 2;
 694      -        else if ('.' == cp[pos] || '\'' == cp[pos])
 695      -                pos++;
 696      -        else
 697      -                return(0);
 698      -
 699      -        while (' ' == cp[pos] || '\t' == cp[pos])
 700      -                pos++;
 701      -
 702      -        *ppos = pos;
 703      -        return(1);
 704      -}
 705      -
 706  639  /*
 707  640   * Convert a string to a long that may not be <0.
 708  641   * If the string is invalid, or is less than 0, return -1.
 709  642   */
 710  643  int
 711  644  mandoc_strntoi(const char *p, size_t sz, int base)
 712  645  {
 713  646          char             buf[32];
 714  647          char            *ep;
 715  648          long             v;
↓ open down ↓ 20 lines elided ↑ open up ↑
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX