1 /* $Vendor-Id: out.c,v 1.11 2009/11/12 08:21:05 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 #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 #ifdef __linux__ 49 extern size_t strlcat(char *, const char *, size_t); 50 #endif 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, type, term, lim; 201 const char *wp, *sp; 202 203 *d = DECO_NONE; 204 wp = *word; 205 type = 1; 206 207 switch (*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 ('*'): 223 switch (*(++wp)) { 224 case ('\0'): 225 return(1); 226 227 case ('('): 228 if ('\0' == *(++wp)) 229 return(2); 230 if ('\0' == *(wp + 1)) 231 return(3); 232 233 *d = DECO_RESERVED; 234 *sz = 2; 235 *word = wp; 236 return(4); 237 238 case ('['): 239 type = 0; 240 break; 241 242 default: 243 *d = DECO_RESERVED; 244 *sz = 1; 245 *word = wp; 246 return(2); 247 } 248 break; 249 250 case ('s'): 251 sp = wp; 252 if ('\0' == *(++wp)) 253 return(1); 254 255 C2LIM(*wp, lim); 256 C2TERM(*wp, term); 257 258 if (term) 259 wp++; 260 261 *word = wp; 262 263 if (*wp == '+' || *wp == '-') 264 ++wp; 265 266 switch (*wp) { 267 case ('\''): 268 /* FALLTHROUGH */ 269 case ('['): 270 /* FALLTHROUGH */ 271 case ('('): 272 if (term) 273 return((int)(wp - sp)); 274 275 C2LIM(*wp, lim); 276 C2TERM(*wp, term); 277 wp++; 278 break; 279 default: 280 break; 281 } 282 283 if ( ! isdigit((u_char)*wp)) 284 return((int)(wp - sp)); 285 286 for (j = 0; isdigit((u_char)*wp); j++) { 287 if (lim && j >= lim) 288 break; 289 ++wp; 290 } 291 292 if (term && term < 3) { 293 if (1 == term && *wp != '\'') 294 return((int)(wp - sp)); 295 if (2 == term && *wp != ']') 296 return((int)(wp - sp)); 297 ++wp; 298 } 299 300 *d = DECO_SIZE; 301 return((int)(wp - sp)); 302 303 case ('f'): 304 switch (*(++wp)) { 305 case ('\0'): 306 return(1); 307 case ('3'): 308 /* FALLTHROUGH */ 309 case ('B'): 310 *d = DECO_BOLD; 311 break; 312 case ('2'): 313 /* FALLTHROUGH */ 314 case ('I'): 315 *d = DECO_ITALIC; 316 break; 317 case ('P'): 318 *d = DECO_PREVIOUS; 319 break; 320 case ('1'): 321 /* FALLTHROUGH */ 322 case ('R'): 323 *d = DECO_ROMAN; 324 break; 325 default: 326 break; 327 } 328 329 return(2); 330 331 case ('['): 332 break; 333 334 case ('c'): 335 *d = DECO_NOSPACE; 336 *sz = 1; 337 return(1); 338 339 default: 340 *d = DECO_SPECIAL; 341 *word = wp; 342 *sz = 1; 343 return(1); 344 } 345 346 *word = ++wp; 347 for (j = 0; *wp && ']' != *wp; wp++, j++) 348 /* Loop... */ ; 349 350 if ('\0' == *wp) 351 return(j + 1); 352 353 *d = type ? DECO_SPECIAL : DECO_RESERVED; 354 *sz = (size_t)j; 355 return (j + 2); 356 } 357