Annotation of src/usr.bin/mandoc/mandoc.c, Revision 1.87
1.87 ! schwarze 1: /* $OpenBSD: mandoc.c,v 1.86 2020/10/24 22:52:34 schwarze Exp $ */
1.1 schwarze 2: /*
1.58 schwarze 3: * Copyright (c) 2008-2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
1.87 ! schwarze 4: * Copyright (c) 2011-2015, 2017-2021 Ingo Schwarze <schwarze@openbsd.org>
1.1 schwarze 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: *
1.21 schwarze 10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
1.1 schwarze 11: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1.21 schwarze 12: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
1.1 schwarze 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: */
1.2 schwarze 18: #include <sys/types.h>
19:
1.1 schwarze 20: #include <assert.h>
21: #include <ctype.h>
1.26 schwarze 22: #include <errno.h>
23: #include <limits.h>
1.1 schwarze 24: #include <stdlib.h>
1.4 schwarze 25: #include <stdio.h>
26: #include <string.h>
1.5 schwarze 27: #include <time.h>
1.1 schwarze 28:
1.69 schwarze 29: #include "mandoc_aux.h"
1.14 schwarze 30: #include "mandoc.h"
1.69 schwarze 31: #include "roff.h"
1.1 schwarze 32: #include "libmandoc.h"
1.82 schwarze 33: #include "roff_int.h"
1.22 schwarze 34:
1.14 schwarze 35: static int a2time(time_t *, const char *, const char *);
1.22 schwarze 36: static char *time2a(time_t);
1.5 schwarze 37:
1.26 schwarze 38:
39: enum mandoc_esc
1.80 schwarze 40: mandoc_font(const char *cp, int sz)
41: {
42: switch (sz) {
43: case 0:
44: return ESCAPE_FONTPREV;
45: case 1:
46: switch (cp[0]) {
47: case 'B':
48: case '3':
49: return ESCAPE_FONTBOLD;
50: case 'I':
51: case '2':
52: return ESCAPE_FONTITALIC;
53: case 'P':
54: return ESCAPE_FONTPREV;
55: case 'R':
56: case '1':
57: return ESCAPE_FONTROMAN;
58: case '4':
59: return ESCAPE_FONTBI;
60: default:
61: return ESCAPE_ERROR;
62: }
63: case 2:
64: switch (cp[0]) {
65: case 'B':
66: switch (cp[1]) {
67: case 'I':
68: return ESCAPE_FONTBI;
69: default:
70: return ESCAPE_ERROR;
71: }
72: case 'C':
73: switch (cp[1]) {
74: case 'B':
1.87 ! schwarze 75: return ESCAPE_FONTCB;
1.80 schwarze 76: case 'I':
1.87 ! schwarze 77: return ESCAPE_FONTCI;
1.80 schwarze 78: case 'R':
79: case 'W':
1.87 ! schwarze 80: return ESCAPE_FONTCR;
1.80 schwarze 81: default:
82: return ESCAPE_ERROR;
83: }
84: default:
85: return ESCAPE_ERROR;
86: }
87: default:
88: return ESCAPE_ERROR;
89: }
90: }
91:
92: enum mandoc_esc
1.44 schwarze 93: mandoc_escape(const char **end, const char **start, int *sz)
1.26 schwarze 94: {
1.34 schwarze 95: const char *local_start;
1.73 schwarze 96: int local_sz, c, i;
1.34 schwarze 97: char term;
1.48 schwarze 98: enum mandoc_esc gly;
1.26 schwarze 99:
1.34 schwarze 100: /*
101: * When the caller doesn't provide return storage,
102: * use local storage.
103: */
104:
105: if (NULL == start)
106: start = &local_start;
107: if (NULL == sz)
108: sz = &local_sz;
109:
110: /*
1.79 schwarze 111: * Treat "\E" just like "\";
112: * it only makes a difference in copy mode.
113: */
114:
115: if (**end == 'E')
116: ++*end;
117:
118: /*
1.34 schwarze 119: * Beyond the backslash, at least one input character
120: * is part of the escape sequence. With one exception
121: * (see below), that character won't be returned.
122: */
123:
1.26 schwarze 124: gly = ESCAPE_ERROR;
1.34 schwarze 125: *start = ++*end;
126: *sz = 0;
1.33 schwarze 127: term = '\0';
1.26 schwarze 128:
1.34 schwarze 129: switch ((*start)[-1]) {
1.26 schwarze 130: /*
131: * First the glyphs. There are several different forms of
132: * these, but each eventually returns a substring of the glyph
133: * name.
134: */
1.48 schwarze 135: case '(':
1.26 schwarze 136: gly = ESCAPE_SPECIAL;
1.34 schwarze 137: *sz = 2;
1.26 schwarze 138: break;
1.48 schwarze 139: case '[':
1.79 schwarze 140: if (**start == ' ') {
141: ++*end;
142: return ESCAPE_ERROR;
143: }
1.26 schwarze 144: gly = ESCAPE_SPECIAL;
145: term = ']';
146: break;
1.48 schwarze 147: case 'C':
1.34 schwarze 148: if ('\'' != **start)
1.62 schwarze 149: return ESCAPE_ERROR;
1.34 schwarze 150: *start = ++*end;
1.54 schwarze 151: gly = ESCAPE_SPECIAL;
1.26 schwarze 152: term = '\'';
153: break;
1.41 schwarze 154:
155: /*
156: * Escapes taking no arguments at all.
157: */
1.79 schwarze 158: case '!':
159: case '?':
160: return ESCAPE_UNSUPP;
161: case '%':
162: case '&':
163: case ')':
164: case ',':
165: case '/':
166: case '^':
167: case 'a':
1.48 schwarze 168: case 'd':
1.79 schwarze 169: case 'r':
170: case 't':
1.48 schwarze 171: case 'u':
1.79 schwarze 172: case '{':
173: case '|':
174: case '}':
1.62 schwarze 175: return ESCAPE_IGNORE;
1.79 schwarze 176: case 'c':
177: return ESCAPE_NOSPACE;
1.70 schwarze 178: case 'p':
179: return ESCAPE_BREAK;
1.32 schwarze 180:
181: /*
182: * The \z escape is supposed to output the following
1.48 schwarze 183: * character without advancing the cursor position.
1.32 schwarze 184: * Since we are mostly dealing with terminal mode,
185: * let us just skip the next character.
186: */
1.48 schwarze 187: case 'z':
1.62 schwarze 188: return ESCAPE_SKIPCHAR;
1.1 schwarze 189:
1.26 schwarze 190: /*
191: * Handle all triggers matching \X(xy, \Xx, and \X[xxxx], where
192: * 'X' is the trigger. These have opaque sub-strings.
193: */
1.48 schwarze 194: case 'F':
1.79 schwarze 195: case 'f':
1.48 schwarze 196: case 'g':
197: case 'k':
198: case 'M':
199: case 'm':
200: case 'n':
1.79 schwarze 201: case 'O':
1.48 schwarze 202: case 'V':
203: case 'Y':
1.86 schwarze 204: case '*':
205: switch ((*start)[-1]) {
206: case 'f':
207: gly = ESCAPE_FONT;
208: break;
209: case '*':
210: gly = ESCAPE_DEVICE;
211: break;
212: default:
213: gly = ESCAPE_IGNORE;
214: break;
215: }
1.34 schwarze 216: switch (**start) {
1.48 schwarze 217: case '(':
1.79 schwarze 218: if ((*start)[-1] == 'O')
219: gly = ESCAPE_ERROR;
1.34 schwarze 220: *start = ++*end;
221: *sz = 2;
1.26 schwarze 222: break;
1.48 schwarze 223: case '[':
1.79 schwarze 224: if ((*start)[-1] == 'O')
225: gly = (*start)[1] == '5' ?
226: ESCAPE_UNSUPP : ESCAPE_ERROR;
1.34 schwarze 227: *start = ++*end;
1.26 schwarze 228: term = ']';
229: break;
230: default:
1.79 schwarze 231: if ((*start)[-1] == 'O') {
232: switch (**start) {
233: case '0':
234: gly = ESCAPE_UNSUPP;
235: break;
236: case '1':
237: case '2':
238: case '3':
239: case '4':
240: break;
241: default:
242: gly = ESCAPE_ERROR;
243: break;
244: }
245: }
1.34 schwarze 246: *sz = 1;
1.26 schwarze 247: break;
248: }
1.74 schwarze 249: break;
1.26 schwarze 250:
251: /*
252: * These escapes are of the form \X'Y', where 'X' is the trigger
253: * and 'Y' is any string. These have opaque sub-strings.
1.47 schwarze 254: * The \B and \w escapes are handled in roff.c, roff_res().
1.26 schwarze 255: */
1.48 schwarze 256: case 'A':
257: case 'b':
258: case 'D':
259: case 'R':
260: case 'X':
261: case 'Z':
1.59 schwarze 262: gly = ESCAPE_IGNORE;
263: /* FALLTHROUGH */
264: case 'o':
265: if (**start == '\0')
1.62 schwarze 266: return ESCAPE_ERROR;
1.59 schwarze 267: if (gly == ESCAPE_ERROR)
268: gly = ESCAPE_OVERSTRIKE;
1.46 schwarze 269: term = **start;
1.34 schwarze 270: *start = ++*end;
1.16 schwarze 271: break;
1.26 schwarze 272:
273: /*
274: * These escapes are of the form \X'N', where 'X' is the trigger
275: * and 'N' resolves to a numerical expression.
276: */
1.48 schwarze 277: case 'h':
278: case 'H':
279: case 'L':
280: case 'l':
281: case 'S':
282: case 'v':
283: case 'x':
1.51 schwarze 284: if (strchr(" %&()*+-./0123456789:<=>", **start)) {
1.53 schwarze 285: if ('\0' != **start)
286: ++*end;
1.62 schwarze 287: return ESCAPE_ERROR;
1.51 schwarze 288: }
1.68 schwarze 289: switch ((*start)[-1]) {
290: case 'h':
291: gly = ESCAPE_HORIZ;
292: break;
293: case 'l':
294: gly = ESCAPE_HLINE;
295: break;
296: default:
297: gly = ESCAPE_IGNORE;
298: break;
299: }
1.46 schwarze 300: term = **start;
1.34 schwarze 301: *start = ++*end;
1.26 schwarze 302: break;
1.29 schwarze 303:
304: /*
305: * Special handling for the numbered character escape.
306: * XXX Do any other escapes need similar handling?
307: */
1.48 schwarze 308: case 'N':
1.34 schwarze 309: if ('\0' == **start)
1.62 schwarze 310: return ESCAPE_ERROR;
1.34 schwarze 311: (*end)++;
312: if (isdigit((unsigned char)**start)) {
313: *sz = 1;
1.62 schwarze 314: return ESCAPE_IGNORE;
1.34 schwarze 315: }
316: (*start)++;
1.29 schwarze 317: while (isdigit((unsigned char)**end))
318: (*end)++;
1.34 schwarze 319: *sz = *end - *start;
1.29 schwarze 320: if ('\0' != **end)
321: (*end)++;
1.62 schwarze 322: return ESCAPE_NUMBERED;
1.26 schwarze 323:
1.48 schwarze 324: /*
1.26 schwarze 325: * Sizes get a special category of their own.
326: */
1.48 schwarze 327: case 's':
1.26 schwarze 328: gly = ESCAPE_IGNORE;
1.17 schwarze 329:
1.26 schwarze 330: /* See +/- counts as a sign. */
1.34 schwarze 331: if ('+' == **end || '-' == **end || ASCII_HYPH == **end)
1.58 schwarze 332: *start = ++*end;
1.6 schwarze 333:
1.34 schwarze 334: switch (**end) {
1.48 schwarze 335: case '(':
1.34 schwarze 336: *start = ++*end;
337: *sz = 2;
1.16 schwarze 338: break;
1.48 schwarze 339: case '[':
1.34 schwarze 340: *start = ++*end;
1.33 schwarze 341: term = ']';
1.16 schwarze 342: break;
1.48 schwarze 343: case '\'':
1.34 schwarze 344: *start = ++*end;
1.33 schwarze 345: term = '\'';
1.60 schwarze 346: break;
347: case '3':
348: case '2':
349: case '1':
350: *sz = (*end)[-1] == 's' &&
351: isdigit((unsigned char)(*end)[1]) ? 2 : 1;
1.16 schwarze 352: break;
353: default:
1.34 schwarze 354: *sz = 1;
1.16 schwarze 355: break;
1.6 schwarze 356: }
357:
1.26 schwarze 358: break;
359:
360: /*
1.79 schwarze 361: * Several special characters can be encoded as
362: * one-byte escape sequences without using \[].
1.26 schwarze 363: */
1.79 schwarze 364: case ' ':
365: case '\'':
366: case '-':
367: case '.':
368: case '0':
369: case ':':
370: case '_':
371: case '`':
372: case 'e':
373: case '~':
374: gly = ESCAPE_SPECIAL;
375: /* FALLTHROUGH */
1.26 schwarze 376: default:
1.79 schwarze 377: if (gly == ESCAPE_ERROR)
378: gly = ESCAPE_UNDEF;
1.34 schwarze 379: *start = --*end;
380: *sz = 1;
1.26 schwarze 381: break;
382: }
383:
384: /*
1.33 schwarze 385: * Read up to the terminating character,
386: * paying attention to nested escapes.
1.26 schwarze 387: */
388:
389: if ('\0' != term) {
1.33 schwarze 390: while (**end != term) {
391: switch (**end) {
1.48 schwarze 392: case '\0':
1.62 schwarze 393: return ESCAPE_ERROR;
1.48 schwarze 394: case '\\':
1.33 schwarze 395: (*end)++;
396: if (ESCAPE_ERROR ==
397: mandoc_escape(end, NULL, NULL))
1.62 schwarze 398: return ESCAPE_ERROR;
1.33 schwarze 399: break;
400: default:
401: (*end)++;
402: break;
403: }
404: }
1.34 schwarze 405: *sz = (*end)++ - *start;
1.79 schwarze 406:
407: /*
408: * The file chars.c only provides one common list
409: * of character names, but \[-] == \- is the only
410: * one of the characters with one-byte names that
411: * allows enclosing the name in brackets.
412: */
413: if (gly == ESCAPE_SPECIAL && *sz == 1 && **start != '-')
414: return ESCAPE_ERROR;
1.33 schwarze 415: } else {
1.34 schwarze 416: assert(*sz > 0);
417: if ((size_t)*sz > strlen(*start))
1.62 schwarze 418: return ESCAPE_ERROR;
1.34 schwarze 419: *end += *sz;
1.26 schwarze 420: }
1.19 schwarze 421:
1.26 schwarze 422: /* Run post-processors. */
1.19 schwarze 423:
1.26 schwarze 424: switch (gly) {
1.48 schwarze 425: case ESCAPE_FONT:
1.80 schwarze 426: gly = mandoc_font(*start, *sz);
1.16 schwarze 427: break;
1.48 schwarze 428: case ESCAPE_SPECIAL:
1.73 schwarze 429: if (**start == 'c') {
430: if (*sz < 6 || *sz > 7 ||
431: strncmp(*start, "char", 4) != 0 ||
432: (int)strspn(*start + 4, "0123456789") + 4 < *sz)
433: break;
434: c = 0;
435: for (i = 4; i < *sz; i++)
436: c = 10 * c + ((*start)[i] - '0');
437: if (c < 0x21 || (c > 0x7e && c < 0xa0) || c > 0xff)
438: break;
439: *start += 4;
440: *sz -= 4;
441: gly = ESCAPE_NUMBERED;
442: break;
443: }
444:
1.54 schwarze 445: /*
1.55 schwarze 446: * Unicode escapes are defined in groff as \[u0000]
1.54 schwarze 447: * to \[u10FFFF], where the contained value must be
448: * a valid Unicode codepoint. Here, however, only
1.55 schwarze 449: * check the length and range.
1.54 schwarze 450: */
1.55 schwarze 451: if (**start != 'u' || *sz < 5 || *sz > 7)
452: break;
453: if (*sz == 7 && ((*start)[1] != '1' || (*start)[2] != '0'))
454: break;
455: if (*sz == 6 && (*start)[1] == '0')
1.64 schwarze 456: break;
457: if (*sz == 5 && (*start)[1] == 'D' &&
458: strchr("89ABCDEF", (*start)[2]) != NULL)
1.55 schwarze 459: break;
460: if ((int)strspn(*start + 1, "0123456789ABCDEFabcdef")
1.54 schwarze 461: + 1 == *sz)
462: gly = ESCAPE_UNICODE;
1.86 schwarze 463: break;
464: case ESCAPE_DEVICE:
465: assert(*sz == 2 && (*start)[0] == '.' && (*start)[1] == 'T');
1.16 schwarze 466: break;
1.1 schwarze 467: default:
1.16 schwarze 468: break;
1.1 schwarze 469: }
470:
1.62 schwarze 471: return gly;
1.4 schwarze 472: }
1.5 schwarze 473:
474: static int
475: a2time(time_t *t, const char *fmt, const char *p)
476: {
477: struct tm tm;
478: char *pp;
479:
480: memset(&tm, 0, sizeof(struct tm));
481:
482: pp = strptime(p, fmt, &tm);
483: if (NULL != pp && '\0' == *pp) {
484: *t = mktime(&tm);
1.62 schwarze 485: return 1;
1.5 schwarze 486: }
487:
1.62 schwarze 488: return 0;
1.5 schwarze 489: }
490:
1.22 schwarze 491: static char *
492: time2a(time_t t)
493: {
1.28 schwarze 494: struct tm *tm;
1.23 schwarze 495: char *buf, *p;
496: size_t ssz;
1.22 schwarze 497: int isz;
498:
1.84 schwarze 499: buf = NULL;
1.28 schwarze 500: tm = localtime(&t);
1.57 schwarze 501: if (tm == NULL)
1.84 schwarze 502: goto fail;
1.22 schwarze 503:
1.23 schwarze 504: /*
505: * Reserve space:
506: * up to 9 characters for the month (September) + blank
507: * up to 2 characters for the day + comma + blank
508: * 4 characters for the year and a terminating '\0'
509: */
1.66 schwarze 510:
1.23 schwarze 511: p = buf = mandoc_malloc(10 + 4 + 4 + 1);
512:
1.66 schwarze 513: if ((ssz = strftime(p, 10 + 1, "%B ", tm)) == 0)
1.23 schwarze 514: goto fail;
515: p += (int)ssz;
1.22 schwarze 516:
1.66 schwarze 517: /*
518: * The output format is just "%d" here, not "%2d" or "%02d".
519: * That's also the reason why we can't just format the
520: * date as a whole with "%B %e, %Y" or "%B %d, %Y".
521: * Besides, the present approach is less prone to buffer
522: * overflows, in case anybody should ever introduce the bug
523: * of looking at LC_TIME.
524: */
525:
1.84 schwarze 526: isz = snprintf(p, 4 + 1, "%d, ", tm->tm_mday);
527: if (isz < 0 || isz > 4)
1.23 schwarze 528: goto fail;
1.22 schwarze 529: p += isz;
530:
1.66 schwarze 531: if (strftime(p, 4 + 1, "%Y", tm) == 0)
1.23 schwarze 532: goto fail;
1.62 schwarze 533: return buf;
1.23 schwarze 534:
535: fail:
536: free(buf);
1.84 schwarze 537: return mandoc_strdup("");
1.22 schwarze 538: }
539:
540: char *
1.85 schwarze 541: mandoc_normdate(struct roff_node *nch, struct roff_node *nbl)
1.5 schwarze 542: {
1.71 schwarze 543: char *cp;
1.5 schwarze 544: time_t t;
1.84 schwarze 545:
1.85 schwarze 546: /* No date specified. */
1.5 schwarze 547:
1.85 schwarze 548: if (nch == NULL) {
549: if (nbl == NULL)
550: mandoc_msg(MANDOCERR_DATE_MISSING, 0, 0, NULL);
551: else
552: mandoc_msg(MANDOCERR_DATE_MISSING, nbl->line,
553: nbl->pos, "%s", roff_name[nbl->tok]);
554: return mandoc_strdup("");
555: }
556: if (*nch->string == '\0') {
557: mandoc_msg(MANDOCERR_DATE_MISSING, nch->line,
558: nch->pos, "%s", roff_name[nbl->tok]);
559: return mandoc_strdup("");
560: }
561: if (strcmp(nch->string, "$" "Mdocdate$") == 0)
1.66 schwarze 562: return time2a(time(NULL));
563:
564: /* Valid mdoc(7) date format. */
565:
1.85 schwarze 566: if (a2time(&t, "$" "Mdocdate: %b %d %Y $", nch->string) ||
567: a2time(&t, "%b %d, %Y", nch->string)) {
1.71 schwarze 568: cp = time2a(t);
569: if (t > time(NULL) + 86400)
1.85 schwarze 570: mandoc_msg(MANDOCERR_DATE_FUTURE, nch->line,
571: nch->pos, "%s %s", roff_name[nbl->tok], cp);
572: else if (*nch->string != '$' &&
573: strcmp(nch->string, cp) != 0)
574: mandoc_msg(MANDOCERR_DATE_NORM, nch->line,
575: nch->pos, "%s %s", roff_name[nbl->tok], cp);
1.71 schwarze 576: return cp;
577: }
1.66 schwarze 578:
1.69 schwarze 579: /* In man(7), do not warn about the legacy format. */
1.66 schwarze 580:
1.85 schwarze 581: if (a2time(&t, "%Y-%m-%d", nch->string) == 0)
582: mandoc_msg(MANDOCERR_DATE_BAD, nch->line, nch->pos,
583: "%s %s", roff_name[nbl->tok], nch->string);
1.71 schwarze 584: else if (t > time(NULL) + 86400)
1.85 schwarze 585: mandoc_msg(MANDOCERR_DATE_FUTURE, nch->line, nch->pos,
586: "%s %s", roff_name[nbl->tok], nch->string);
587: else if (nbl->tok == MDOC_Dd)
588: mandoc_msg(MANDOCERR_DATE_LEGACY, nch->line, nch->pos,
589: "Dd %s", nch->string);
1.66 schwarze 590:
591: /* Use any non-mdoc(7) date verbatim. */
592:
1.85 schwarze 593: return mandoc_strdup(nch->string);
1.5 schwarze 594: }
595:
1.9 schwarze 596: int
1.43 schwarze 597: mandoc_eos(const char *p, size_t sz)
1.9 schwarze 598: {
1.43 schwarze 599: const char *q;
600: int enclosed, found;
1.9 schwarze 601:
1.10 schwarze 602: if (0 == sz)
1.62 schwarze 603: return 0;
1.9 schwarze 604:
1.11 schwarze 605: /*
606: * End-of-sentence recognition must include situations where
607: * some symbols, such as `)', allow prior EOS punctuation to
1.26 schwarze 608: * propagate outward.
1.11 schwarze 609: */
610:
1.43 schwarze 611: enclosed = found = 0;
1.16 schwarze 612: for (q = p + (int)sz - 1; q >= p; q--) {
1.15 schwarze 613: switch (*q) {
1.48 schwarze 614: case '\"':
615: case '\'':
616: case ']':
617: case ')':
1.15 schwarze 618: if (0 == found)
619: enclosed = 1;
1.11 schwarze 620: break;
1.48 schwarze 621: case '.':
622: case '!':
623: case '?':
1.15 schwarze 624: found = 1;
625: break;
1.11 schwarze 626: default:
1.62 schwarze 627: return found &&
628: (!enclosed || isalnum((unsigned char)*q));
1.11 schwarze 629: }
1.9 schwarze 630: }
631:
1.62 schwarze 632: return found && !enclosed;
1.9 schwarze 633: }
1.26 schwarze 634:
635: /*
636: * Convert a string to a long that may not be <0.
637: * If the string is invalid, or is less than 0, return -1.
638: */
639: int
1.27 schwarze 640: mandoc_strntoi(const char *p, size_t sz, int base)
1.26 schwarze 641: {
642: char buf[32];
643: char *ep;
644: long v;
645:
646: if (sz > 31)
1.62 schwarze 647: return -1;
1.26 schwarze 648:
649: memcpy(buf, p, sz);
650: buf[(int)sz] = '\0';
651:
652: errno = 0;
653: v = strtol(buf, &ep, base);
654:
655: if (buf[0] == '\0' || *ep != '\0')
1.62 schwarze 656: return -1;
1.26 schwarze 657:
1.27 schwarze 658: if (v > INT_MAX)
659: v = INT_MAX;
660: if (v < INT_MIN)
661: v = INT_MIN;
1.26 schwarze 662:
1.62 schwarze 663: return (int)v;
1.26 schwarze 664: }