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