1 /* $Vendor-Id: out.c,v 1.15 2010/04/07 11:29:55 kristaps Exp $ */ 2 /* 3 * Copyright (c) 2009 Kristaps Dzonsons <kristaps@kth.se> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 #ifdef HAVE_CONFIG_H 18 #include "config.h" 19 #endif 20 21 #include <sys/types.h> 22 23 #include <assert.h> 24 #include <ctype.h> 25 #include <stdio.h> 26 #include <stdlib.h> 27 #include <string.h> 28 #include <time.h> 29 30 #include "out.h" 31 32 /* See a2roffdeco(). */ 33 #define C2LIM(c, l) do { \ 34 (l) = 1; \ 35 if ('[' == (c) || '\'' == (c)) \ 36 (l) = 0; \ 37 else if ('(' == (c)) \ 38 (l) = 2; } \ 39 while (/* CONSTCOND */ 0) 40 41 /* See a2roffdeco(). */ 42 #define C2TERM(c, t) do { \ 43 (t) = 0; \ 44 if ('\'' == (c)) \ 45 (t) = 1; \ 46 else if ('[' == (c)) \ 47 (t) = 2; \ 48 else if ('(' == (c)) \ 49 (t) = 3; } \ 50 while (/* CONSTCOND */ 0) 51 52 /* 53 * Convert a `scaling unit' to a consistent form, or fail. Scaling 54 * units are documented in groff.7, mdoc.7, man.7. 55 */ 56 int 57 a2roffsu(const char *src, struct roffsu *dst, enum roffscale def) 58 { 59 char buf[BUFSIZ], hasd; 60 int i; 61 enum roffscale unit; 62 63 if ('\0' == *src) 64 return(0); 65 66 i = hasd = 0; 67 68 switch (*src) { 69 case ('+'): 70 src++; 71 break; 72 case ('-'): 73 buf[i++] = *src++; 74 break; 75 default: 76 break; 77 } 78 79 if ('\0' == *src) 80 return(0); 81 82 while (i < BUFSIZ) { 83 if ( ! isdigit((u_char)*src)) { 84 if ('.' != *src) 85 break; 86 else if (hasd) 87 break; 88 else 89 hasd = 1; 90 } 91 buf[i++] = *src++; 92 } 93 94 if (BUFSIZ == i || (*src && *(src + 1))) 95 return(0); 96 97 buf[i] = '\0'; 98 99 switch (*src) { 100 case ('c'): 101 unit = SCALE_CM; 102 break; 103 case ('i'): 104 unit = SCALE_IN; 105 break; 106 case ('P'): 107 unit = SCALE_PC; 108 break; 109 case ('p'): 110 unit = SCALE_PT; 111 break; 112 case ('f'): 113 unit = SCALE_FS; 114 break; 115 case ('v'): 116 unit = SCALE_VS; 117 break; 118 case ('m'): 119 unit = SCALE_EM; 120 break; 121 case ('\0'): 122 if (SCALE_MAX == def) 123 return(0); 124 unit = SCALE_BU; 125 break; 126 case ('u'): 127 unit = SCALE_BU; 128 break; 129 case ('M'): 130 unit = SCALE_MM; 131 break; 132 case ('n'): 133 unit = SCALE_EN; 134 break; 135 default: 136 return(0); 137 } 138 139 if ((dst->scale = atof(buf)) < 0) 140 dst->scale = 0; 141 dst->unit = unit; 142 dst->pt = hasd; 143 144 return(1); 145 } 146 147 148 /* 149 * Correctly writes the time in nroff form, which differs from standard 150 * form in that a space isn't printed in lieu of the extra %e field for 151 * single-digit dates. 152 */ 153 void 154 time2a(time_t t, char *dst, size_t sz) 155 { 156 struct tm tm; 157 char buf[5]; 158 char *p; 159 size_t nsz; 160 161 assert(sz > 1); 162 localtime_r(&t, &tm); 163 164 p = dst; 165 nsz = 0; 166 167 dst[0] = '\0'; 168 169 if (0 == (nsz = strftime(p, sz, "%B ", &tm))) 170 return; 171 172 p += (int)nsz; 173 sz -= nsz; 174 175 if (0 == strftime(buf, sizeof(buf), "%e, ", &tm)) 176 return; 177 178 nsz = strlcat(p, buf + (' ' == buf[0] ? 1 : 0), sz); 179 180 if (nsz >= sz) 181 return; 182 183 p += (int)nsz; 184 sz -= nsz; 185 186 (void)strftime(p, sz, "%Y", &tm); 187 } 188 189 190 /* 191 * Returns length of parsed string (the leading "\" should NOT be 192 * included). This can be zero if the current character is the nil 193 * terminator. "d" is set to the type of parsed decorator, which may 194 * have an adjoining "word" of size "sz" (e.g., "(ab" -> "ab", 2). 195 */ 196 int 197 a2roffdeco(enum roffdeco *d, 198 const char **word, size_t *sz) 199 { 200 int j, term, lim; 201 char set; 202 const char *wp, *sp; 203 204 *d = DECO_NONE; 205 wp = *word; 206 207 switch ((set = *wp)) { 208 case ('\0'): 209 return(0); 210 211 case ('('): 212 if ('\0' == *(++wp)) 213 return(1); 214 if ('\0' == *(wp + 1)) 215 return(2); 216 217 *d = DECO_SPECIAL; 218 *sz = 2; 219 *word = wp; 220 return(3); 221 222 case ('F'): 223 /* FALLTHROUGH */ 224 case ('f'): 225 /* 226 * FIXME: this needs work and consolidation (it should 227 * follow the sequence that special characters do, for 228 * one), but isn't a priority at the moment. Note, for 229 * one, that in reality \fB != \FB, although here we let 230 * these slip by. 231 */ 232 switch (*(++wp)) { 233 case ('\0'): 234 return(1); 235 case ('3'): 236 /* FALLTHROUGH */ 237 case ('B'): 238 *d = DECO_BOLD; 239 return(2); 240 case ('2'): 241 /* FALLTHROUGH */ 242 case ('I'): 243 *d = DECO_ITALIC; 244 return(2); 245 case ('P'): 246 *d = DECO_PREVIOUS; 247 return(2); 248 case ('1'): 249 /* FALLTHROUGH */ 250 case ('R'): 251 *d = DECO_ROMAN; 252 return(2); 253 case ('('): 254 if ('\0' == *(++wp)) 255 return(2); 256 if ('\0' == *(wp + 1)) 257 return(3); 258 259 *d = 'F' == set ? DECO_FFONT : DECO_FONT; 260 *sz = 2; 261 *word = wp; 262 return(4); 263 case ('['): 264 *word = ++wp; 265 for (j = 0; *wp && ']' != *wp; wp++, j++) 266 /* Loop... */ ; 267 268 if ('\0' == *wp) 269 return(j + 2); 270 271 *d = 'F' == set ? DECO_FFONT : DECO_FONT; 272 *sz = (size_t)j; 273 return(j + 3); 274 default: 275 break; 276 } 277 278 *d = 'F' == set ? DECO_FFONT : DECO_FONT; 279 *sz = 1; 280 *word = wp; 281 return(2); 282 283 case ('*'): 284 switch (*(++wp)) { 285 case ('\0'): 286 return(1); 287 288 case ('('): 289 if ('\0' == *(++wp)) 290 return(2); 291 if ('\0' == *(wp + 1)) 292 return(3); 293 294 *d = DECO_RESERVED; 295 *sz = 2; 296 *word = wp; 297 return(4); 298 299 case ('['): 300 *word = ++wp; 301 for (j = 0; *wp && ']' != *wp; wp++, j++) 302 /* Loop... */ ; 303 304 if ('\0' == *wp) 305 return(j + 2); 306 307 *d = DECO_RESERVED; 308 *sz = (size_t)j; 309 return(j + 3); 310 311 default: 312 break; 313 } 314 315 *d = DECO_RESERVED; 316 *sz = 1; 317 *word = wp; 318 return(2); 319 320 case ('s'): 321 sp = wp; 322 if ('\0' == *(++wp)) 323 return(1); 324 325 C2LIM(*wp, lim); 326 C2TERM(*wp, term); 327 328 if (term) 329 wp++; 330 331 *word = wp; 332 333 if (*wp == '+' || *wp == '-') 334 ++wp; 335 336 switch (*wp) { 337 case ('\''): 338 /* FALLTHROUGH */ 339 case ('['): 340 /* FALLTHROUGH */ 341 case ('('): 342 if (term) 343 return((int)(wp - sp)); 344 345 C2LIM(*wp, lim); 346 C2TERM(*wp, term); 347 wp++; 348 break; 349 default: 350 break; 351 } 352 353 if ( ! isdigit((u_char)*wp)) 354 return((int)(wp - sp)); 355 356 for (j = 0; isdigit((u_char)*wp); j++) { 357 if (lim && j >= lim) 358 break; 359 ++wp; 360 } 361 362 if (term && term < 3) { 363 if (1 == term && *wp != '\'') 364 return((int)(wp - sp)); 365 if (2 == term && *wp != ']') 366 return((int)(wp - sp)); 367 ++wp; 368 } 369 370 *d = DECO_SIZE; 371 return((int)(wp - sp)); 372 373 case ('['): 374 *word = ++wp; 375 376 for (j = 0; *wp && ']' != *wp; wp++, j++) 377 /* Loop... */ ; 378 379 if ('\0' == *wp) 380 return(j + 1); 381 382 *d = DECO_SPECIAL; 383 *sz = (size_t)j; 384 return(j + 2); 385 386 case ('c'): 387 *d = DECO_NOSPACE; 388 *sz = 1; 389 return(1); 390 391 default: 392 break; 393 } 394 395 *d = DECO_SPECIAL; 396 *word = wp; 397 *sz = 1; 398 return(1); 399 } 400