1/* ----------------------------------------------------------------------------- 2 * fio.c 3 * 4 * This file implements a number of standard I/O operations included 5 * formatted output, readline, and splitting. 6 * 7 * Author(s) : David Beazley (beazley@cs.uchicago.edu) 8 * 9 * Copyright (C) 1999-2000. The University of Chicago 10 * See the file LICENSE for information on usage and redistribution. 11 * ----------------------------------------------------------------------------- */ 12 13char cvsroot_fio_c[] = "$Id: fio.c 9607 2006-12-05 22:11:40Z beazley $"; 14 15#include "dohint.h" 16 17#define OBUFLEN 512 18 19static DOH *encodings = 0; /* Encoding hash */ 20 21/* ----------------------------------------------------------------------------- 22 * Writen() 23 * 24 * Write's N characters of output and retries until all characters are 25 * written. This is useful should a write operation encounter a spurious signal. 26 * ----------------------------------------------------------------------------- */ 27 28static int Writen(DOH *out, void *buffer, int len) { 29 int nw = len, ret; 30 char *cb = (char *) buffer; 31 while (nw) { 32 ret = Write(out, cb, nw); 33 if (ret < 0) 34 return -1; 35 nw = nw - ret; 36 cb += ret; 37 } 38 return len; 39} 40 41/* ----------------------------------------------------------------------------- 42 * DohEncoding() 43 * 44 * Registers a new printf encoding method. An encoder function should accept 45 * two file-like objects and operate as a filter. 46 * ----------------------------------------------------------------------------- */ 47 48void DohEncoding(char *name, DOH *(*fn) (DOH *s)) { 49 if (!encodings) 50 encodings = NewHash(); 51 Setattr(encodings, (void *) name, NewVoid((void *) fn, 0)); 52} 53 54/* internal function for processing an encoding */ 55static DOH *encode(char *name, DOH *s) { 56 DOH *handle, *ns; 57 DOH *(*fn) (DOH *); 58 long pos; 59 char *cfmt = strchr(name, ':'); 60 DOH *tmp = 0; 61 if (cfmt) { 62 tmp = NewString(cfmt + 1); 63 Append(tmp, s); 64 Setfile(tmp, Getfile((DOH *) s)); 65 Setline(tmp, Getline((DOH *) s)); 66 *cfmt = '\0'; 67 } 68 if (!encodings || !(handle = Getattr(encodings, name))) { 69 return Copy(s); 70 } 71 if (tmp) 72 s = tmp; 73 pos = Tell(s); 74 Seek(s, 0, SEEK_SET); 75 fn = (DOH *(*)(DOH *)) Data(handle); 76 ns = (*fn) (s); 77 Seek(s, pos, SEEK_SET); 78 if (tmp) 79 Delete(tmp); 80 return ns; 81} 82 83/* ----------------------------------------------------------------------------- 84 * DohvPrintf() 85 * 86 * DOH implementation of printf. Output can be directed to any file-like object 87 * including bare FILE * objects. The same formatting codes as printf are 88 * recognized with two extensions: 89 * 90 * %s - Prints a "char *" or the string representation of any 91 * DOH object. This will implicitly result in a call to 92 * Str(obj). 93 * 94 * %(encoder)* - Filters the output through an encoding function registered 95 * with DohEncoder(). 96 * 97 * Note: This function is not particularly memory efficient with large strings. 98 * It's better to use Dump() or some other method instead. 99 * ----------------------------------------------------------------------------- */ 100 101int DohvPrintf(DOH *so, const char *format, va_list ap) { 102 static char *fmt_codes = "dioxXucsSfeEgGpn"; 103 int state = 0; 104 const char *p = format; 105 char newformat[256]; 106 char obuffer[OBUFLEN]; 107 char *fmt = 0; 108 char temp[64]; 109 int widthval = 0; 110 int precval = 0; 111 int maxwidth; 112 char *w = 0; 113 int ivalue; 114 double dvalue; 115 void *pvalue; 116 char *stemp; 117 int nbytes = 0; 118 char encoder[128], *ec = 0; 119 int plevel = 0; 120 121 memset(newformat, 0, sizeof(newformat)); 122 123 while (*p) { 124 switch (state) { 125 case 0: /* Ordinary text */ 126 if (*p != '%') { 127 Putc(*p, so); 128 nbytes++; 129 } else { 130 fmt = newformat; 131 widthval = 0; 132 precval = 0; 133 *(fmt++) = *p; 134 encoder[0] = 0; 135 state = 10; 136 } 137 break; 138 case 10: /* Look for a width and precision */ 139 if (isdigit((int) *p) && (*p != '0')) { 140 w = temp; 141 *(w++) = *p; 142 *(fmt++) = *p; 143 state = 20; 144 } else if (strchr(fmt_codes, *p)) { 145 /* Got one of the formatting codes */ 146 p--; 147 state = 100; 148 } else if (*p == '*') { 149 /* Width field is specified in the format list */ 150 widthval = va_arg(ap, int); 151 sprintf(temp, "%d", widthval); 152 for (w = temp; *w; w++) { 153 *(fmt++) = *w; 154 } 155 state = 30; 156 } else if (*p == '%') { 157 Putc(*p, so); 158 fmt = newformat; 159 nbytes++; 160 state = 0; 161 } else if (*p == '(') { 162 ++plevel; 163 ec = encoder; 164 state = 60; 165 } else { 166 *(fmt++) = *p; 167 } 168 break; 169 170 case 20: /* Hmmm. At the start of a width field */ 171 if (isdigit((int) *p)) { 172 *(w++) = *p; 173 *(fmt++) = *p; 174 } else if (strchr(fmt_codes, *p)) { 175 /* Got one of the formatting codes */ 176 /* Figure out width */ 177 *w = 0; 178 widthval = atoi(temp); 179 p--; 180 state = 100; 181 } else if (*p == '.') { 182 *w = 0; 183 widthval = atoi(temp); 184 w = temp; 185 *(fmt++) = *p; 186 state = 40; 187 } else { 188 /* ??? */ 189 *w = 0; 190 widthval = atoi(temp); 191 state = 50; 192 } 193 break; 194 195 case 30: /* Parsed a width from an argument. Look for a . */ 196 if (*p == '.') { 197 w = temp; 198 *(fmt++) = *p; 199 state = 40; 200 } else if (strchr(fmt_codes, *p)) { 201 /* Got one of the formatting codes */ 202 /* Figure out width */ 203 p--; 204 state = 100; 205 } else { 206 /* hmmm. Something else. */ 207 state = 50; 208 } 209 break; 210 211 case 40: 212 /* Start of precision expected */ 213 if (isdigit((int) *p) && (*p != '0')) { 214 *(fmt++) = *p; 215 *(w++) = *p; 216 state = 41; 217 } else if (*p == '*') { 218 /* Precision field is specified in the format list */ 219 precval = va_arg(ap, int); 220 sprintf(temp, "%d", precval); 221 for (w = temp; *w; w++) { 222 *(fmt++) = *w; 223 } 224 state = 50; 225 } else if (strchr(fmt_codes, *p)) { 226 p--; 227 state = 100; 228 } else { 229 *(fmt++) = *p; 230 state = 50; 231 } 232 break; 233 case 41: 234 if (isdigit((int) *p)) { 235 *(fmt++) = *p; 236 *(w++) = *p; 237 } else if (strchr(fmt_codes, *p)) { 238 /* Got one of the formatting codes */ 239 /* Figure out width */ 240 *w = 0; 241 precval = atoi(temp); 242 p--; 243 state = 100; 244 } else { 245 *w = 0; 246 precval = atoi(temp); 247 *(fmt++) = *p; 248 state = 50; 249 } 250 break; 251 /* Hang out, wait for format specifier */ 252 case 50: 253 if (strchr(fmt_codes, *p)) { 254 p--; 255 state = 100; 256 } else { 257 *(fmt++) = *p; 258 } 259 break; 260 261 /* Got an encoding header */ 262 case 60: 263 if (*p == '(') { 264 ++plevel; 265 *ec = *p; 266 ec++; 267 } else if (*p == ')') { 268 --plevel; 269 if (plevel <= 0) { 270 *ec = 0; 271 state = 10; 272 } else { 273 *ec = *p; 274 ec++; 275 } 276 } else { 277 *ec = *p; 278 ec++; 279 } 280 break; 281 case 100: 282 /* Got a formatting code */ 283 if (widthval < precval) 284 maxwidth = precval; 285 else 286 maxwidth = widthval; 287 if ((*p == 's') || (*p == 'S')) { /* Null-Terminated string */ 288 DOH *doh; 289 DOH *Sval; 290 DOH *enc = 0; 291 doh = va_arg(ap, DOH *); 292 if (DohCheck(doh)) { 293 /* Is a DOH object. */ 294 if (DohIsString(doh)) { 295 Sval = doh; 296 } else { 297 Sval = Str(doh); 298 } 299 if (strlen(encoder)) { 300 enc = encode(encoder, Sval); 301 maxwidth = maxwidth + strlen(newformat) + Len(enc); 302 } else { 303 maxwidth = maxwidth + strlen(newformat) + Len(Sval); 304 } 305 *(fmt++) = 's'; 306 *fmt = 0; 307 if ((maxwidth + 1) < OBUFLEN) { 308 stemp = obuffer; 309 } else { 310 stemp = (char *) DohMalloc(maxwidth + 1); 311 } 312 if (enc) { 313 nbytes += sprintf(stemp, newformat, Data(enc)); 314 } else { 315 nbytes += sprintf(stemp, newformat, Data(Sval)); 316 } 317 if (Writen(so, stemp, strlen(stemp)) < 0) 318 return -1; 319 if ((DOH *) Sval != doh) { 320 Delete(Sval); 321 } 322 if (enc) 323 Delete(enc); 324 if (*p == 'S') { 325 Delete(doh); 326 } 327 if (stemp != obuffer) { 328 DohFree(stemp); 329 } 330 } else { 331 if (!doh) 332 doh = (char *) ""; 333 334 if (strlen(encoder)) { 335 DOH *s = NewString(doh); 336 Seek(s, 0, SEEK_SET); 337 enc = encode(encoder, s); 338 Delete(s); 339 doh = Char(enc); 340 } else { 341 enc = 0; 342 } 343 maxwidth = maxwidth + strlen(newformat) + strlen((char *) doh); 344 *(fmt++) = 's'; 345 *fmt = 0; 346 if ((maxwidth + 1) < OBUFLEN) { 347 stemp = obuffer; 348 } else { 349 stemp = (char *) DohMalloc(maxwidth + 1); 350 } 351 nbytes += sprintf(stemp, newformat, doh); 352 if (Writen(so, stemp, strlen(stemp)) < 0) 353 return -1; 354 if (stemp != obuffer) { 355 DohFree(stemp); 356 } 357 if (enc) 358 Delete(enc); 359 } 360 } else { 361 *(fmt++) = *p; 362 *fmt = 0; 363 maxwidth = maxwidth + strlen(newformat) + 64; 364 365 /* Only allocate a buffer if it is too big to fit. Shouldn't have to do 366 this very often */ 367 368 if (maxwidth < OBUFLEN) 369 stemp = obuffer; 370 else 371 stemp = (char *) DohMalloc(maxwidth + 1); 372 switch (*p) { 373 case 'd': 374 case 'i': 375 case 'o': 376 case 'u': 377 case 'x': 378 case 'X': 379 case 'c': 380 ivalue = va_arg(ap, int); 381 nbytes += sprintf(stemp, newformat, ivalue); 382 break; 383 case 'f': 384 case 'g': 385 case 'e': 386 case 'E': 387 case 'G': 388 dvalue = va_arg(ap, double); 389 nbytes += sprintf(stemp, newformat, dvalue); 390 break; 391 case 'p': 392 pvalue = va_arg(ap, void *); 393 nbytes += sprintf(stemp, newformat, pvalue); 394 break; 395 default: 396 break; 397 } 398 if (Writen(so, stemp, strlen(stemp)) < 0) 399 return -1; 400 if (stemp != obuffer) 401 DohFree(stemp); 402 } 403 state = 0; 404 break; 405 } 406 p++; 407 } 408 if (state) { 409 int r; 410 *fmt = 0; 411 r = Writen(so, fmt, strlen(fmt)); 412 if (r < 0) 413 return -1; 414 nbytes += r; 415 } 416 return nbytes; 417} 418 419/* ----------------------------------------------------------------------------- 420 * DohPrintf() 421 * 422 * Variable length argument entry point to Printf 423 * ----------------------------------------------------------------------------- */ 424 425int DohPrintf(DOH *obj, const char *format, ...) { 426 va_list ap; 427 int ret; 428 va_start(ap, format); 429 ret = DohvPrintf(obj, format, ap); 430 va_end(ap); 431 return ret; 432} 433 434/* ----------------------------------------------------------------------------- 435 * DohPrintv() 436 * 437 * Print a null-terminated variable length list of DOH objects 438 * ----------------------------------------------------------------------------- */ 439 440int DohPrintv(DOHFile * f, ...) { 441 va_list ap; 442 int ret = 0; 443 DOH *obj; 444 va_start(ap, f); 445 while (1) { 446 obj = va_arg(ap, void *); 447 if ((!obj) || (obj == DohNone)) 448 break; 449 if (DohCheck(obj)) { 450 ret += DohDump(obj, f); 451 } else { 452 ret += DohWrite(f, obj, strlen((char *) obj)); 453 } 454 } 455 va_end(ap); 456 return ret; 457} 458 459/* ----------------------------------------------------------------------------- 460 * DohCopyto() 461 * 462 * Copies all of the input from an input stream to an output stream. Returns the 463 * number of bytes copied. 464 * ----------------------------------------------------------------------------- */ 465 466int DohCopyto(DOH *in, DOH *out) { 467 int nbytes = 0, ret; 468 int nwrite = 0, wret; 469 char *cw; 470 char buffer[16384]; 471 472 if ((!in) || (!out)) 473 return 0; 474 while (1) { 475 ret = Read(in, buffer, 16384); 476 if (ret > 0) { 477 nwrite = ret; 478 cw = buffer; 479 while (nwrite) { 480 wret = Write(out, cw, nwrite); 481 if (wret < 0) 482 return -1; 483 nwrite = nwrite - wret; 484 cw += wret; 485 } 486 nbytes += ret; 487 } else { 488 return nbytes; 489 } 490 } 491} 492 493 494/* ----------------------------------------------------------------------------- 495 * DohSplit() 496 * 497 * Split an input stream into a list of strings delimited by the specified 498 * character. Optionally accepts a maximum number of splits to perform. 499 * ----------------------------------------------------------------------------- */ 500 501DOH *DohSplit(DOH *in, char ch, int nsplits) { 502 DOH *list; 503 DOH *str; 504 int c; 505 506 list = NewList(); 507 508 if (DohIsString(in)) { 509 Seek(in, 0, SEEK_SET); 510 } 511 512 while (1) { 513 str = NewStringEmpty(); 514 do { 515 c = Getc(in); 516 } while ((c != EOF) && (c == ch)); 517 if (c != EOF) { 518 Putc(c, str); 519 while (1) { 520 c = Getc(in); 521 if ((c == EOF) || ((c == ch) && (nsplits != 0))) 522 break; 523 Putc(c, str); 524 } 525 nsplits--; 526 } 527 Append(list, str); 528 Delete(str); 529 if (c == EOF) 530 break; 531 } 532 return list; 533} 534 535/* ----------------------------------------------------------------------------- 536 * DohSplitLines() 537 * 538 * Split an input stream into a list of strings delimited by newline characters. 539 * ----------------------------------------------------------------------------- */ 540 541DOH *DohSplitLines(DOH *in) { 542 DOH *list; 543 DOH *str; 544 int c = 0; 545 546 list = NewList(); 547 548 if (DohIsString(in)) { 549 Seek(in, 0, SEEK_SET); 550 } 551 552 while (c != EOF) { 553 str = NewStringEmpty(); 554 while ((c = Getc(in)) != '\n' && c != EOF) { 555 Putc(c, str); 556 } 557 Append(list, str); 558 Delete(str); 559 } 560 return list; 561} 562 563 564/* ----------------------------------------------------------------------------- 565 * DohReadline() 566 * 567 * Read a single input line and return it as a string. 568 * ----------------------------------------------------------------------------- */ 569 570DOH *DohReadline(DOH *in) { 571 char c; 572 int n = 0; 573 DOH *s = NewStringEmpty(); 574 while (1) { 575 if (Read(in, &c, 1) < 0) { 576 if (n == 0) { 577 Delete(s); 578 return 0; 579 } 580 return s; 581 } 582 if (c == '\n') 583 return s; 584 if (c == '\r') 585 continue; 586 Putc(c, s); 587 n++; 588 } 589} 590