1/* 2 * Copyright (c) 2002 by The XFree86 Project, Inc. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included in 12 * all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 * THE XFREE86 PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 18 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF 19 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 * SOFTWARE. 21 * 22 * Except as contained in this notice, the name of the XFree86 Project shall 23 * not be used in advertising or otherwise to promote the sale, use or other 24 * dealings in this Software without prior written authorization from the 25 * XFree86 Project. 26 * 27 * Author: Paulo César Pereira de Andrade 28 */ 29 30/* $XFree86: xc/programs/xedit/lisp/io.c,v 1.16tsi Exp $ */ 31 32#include "lisp/io.h" 33#include <errno.h> 34#include <fcntl.h> 35#include <stdarg.h> 36#include <sys/types.h> 37#include <sys/stat.h> 38 39/* Match the FILE_XXX flags */ 40#define READ_BIT 0x01 41#define WRITE_BIT 0x02 42#define APPEND_BIT 0x04 43#define BUFFERED_BIT 0x08 44#define UNBUFFERED_BIT 0x10 45#define BINARY_BIT 0x20 46 47 48/* 49 * Initialization 50 */ 51extern int pagesize; 52 53/* 54 * Implementation 55 */ 56int 57LispGet(void) 58{ 59 int ch = EOF; 60 LispUngetInfo *unget = lisp__data.unget[lisp__data.iunget]; 61 62 if (unget->offset) 63 ch = ((unsigned char*)unget->buffer)[--unget->offset]; 64 else if (SINPUT->data.stream.readable) { 65 LispFile *file = NULL; 66 67 switch (SINPUT->data.stream.type) { 68 case LispStreamStandard: 69 case LispStreamFile: 70 file = FSTREAMP(SINPUT); 71 break; 72 case LispStreamPipe: 73 file = IPSTREAMP(SINPUT); 74 break; 75 case LispStreamString: 76 ch = LispSgetc(SSTREAMP(SINPUT)); 77 break; 78 default: 79 ch = EOF; 80 break; 81 } 82 if (file != NULL) { 83 if (file->nonblock) { 84 if (fcntl(file->descriptor, F_SETFL, 0) < 0) 85 LispDestroy("fcntl: %s", strerror(errno)); 86 file->nonblock = 0; 87 } 88 ch = LispFgetc(file); 89 } 90 } 91 else 92 LispDestroy("cannot read from *STANDARD-INPUT*"); 93 94 if (ch == EOF) 95 lisp__data.eof = 1; 96 97 return (ch); 98} 99 100int 101LispUnget(int ch) 102{ 103 LispUngetInfo *unget = lisp__data.unget[lisp__data.iunget]; 104 105 if ((ch & 0xff) == ch) { 106 if (unget->offset == sizeof(unget->buffer)) { 107 LispWarning("character %c lost at LispUnget()", unget->buffer[0]); 108 memmove(unget->buffer, unget->buffer + 1, unget->offset - 1); 109 unget->buffer[unget->offset - 1] = ch; 110 } 111 else 112 unget->buffer[unget->offset++] = ch; 113 } 114 115 return (ch); 116} 117 118void 119LispPushInput(LispObj *stream) 120{ 121 if (!STREAMP(stream) || !stream->data.stream.readable) 122 LispDestroy("bad stream at PUSH-INPUT"); 123 lisp__data.input_list = CONS(stream, lisp__data.input_list); 124 SINPUT = stream; 125 if (lisp__data.iunget + 1 == lisp__data.nunget) { 126 LispUngetInfo **info = 127 realloc(lisp__data.unget, 128 sizeof(LispUngetInfo) * (lisp__data.nunget + 1)); 129 130 if (!info || 131 (info[lisp__data.nunget] = 132 calloc(1, sizeof(LispUngetInfo))) == NULL) 133 LispDestroy("out of memory"); 134 lisp__data.unget = info; 135 ++lisp__data.nunget; 136 } 137 ++lisp__data.iunget; 138 memset(lisp__data.unget[lisp__data.iunget], '\0', sizeof(LispUngetInfo)); 139 lisp__data.eof = 0; 140} 141 142void 143LispPopInput(LispObj *stream) 144{ 145 if (!CONSP(lisp__data.input_list) || stream != CAR(lisp__data.input_list)) 146 LispDestroy("bad stream at POP-INPUT"); 147 lisp__data.input_list = CDR(lisp__data.input_list); 148 SINPUT = CONSP(lisp__data.input_list) ? 149 CAR(lisp__data.input_list) : lisp__data.input_list; 150 --lisp__data.iunget; 151 lisp__data.eof = 0; 152} 153 154/* 155 * Low level functions 156 */ 157static int 158calculate_line(const void *data, int size) 159{ 160 int line = 0; 161 const char *str, *ptr; 162 163 for (str = (const char *)data, ptr = (const char *)data + size; 164 str < ptr; str++) 165 if (*ptr == '\n') 166 ++line; 167 168 return (line); 169} 170 171static int 172calculate_column(const void *data, int size, int column) 173{ 174 const char *str, *ptr; 175 176 /* search for newline in data */ 177 for (str = (const char *)data, ptr = (const char *)data + size - 1; 178 ptr >= str; ptr--) 179 if (*ptr == '\n') 180 break; 181 182 /* newline found */ 183 if (ptr >= str) 184 return (size - (ptr - str) - 1); 185 186 /* newline not found */ 187 return (column + size); 188} 189 190LispFile * 191LispFdopen(int descriptor, int mode) 192{ 193 LispFile *file = calloc(1, sizeof(LispFile)); 194 195 if (file) { 196 struct stat st; 197 198 file->descriptor = descriptor; 199 file->readable = (mode & READ_BIT) != 0; 200 file->writable = (mode & WRITE_BIT) != 0; 201 202 if (fstat(descriptor, &st) == 0) 203 file->regular = S_ISREG(st.st_mode); 204 else 205 file->regular = 0; 206 207 file->buffered = (mode & BUFFERED_BIT) != 0; 208 if ((mode & UNBUFFERED_BIT) == 0) 209 file->buffered = file->regular; 210 211 if (file->buffered) { 212 file->buffer = malloc(pagesize); 213 if (file->buffer == NULL) 214 file->buffered = 0; 215 } 216 file->line = 1; 217 file->binary = (mode & BINARY_BIT) != 0; 218 file->io_write = write; 219 } 220 221 return (file); 222} 223 224LispFile * 225LispFopen(const char *path, int mode) 226{ 227 LispFile *file; 228 int descriptor; 229 int flags = O_NOCTTY; 230 231 /* check read/write attributes */ 232 if ((mode & (READ_BIT | WRITE_BIT)) == (READ_BIT | WRITE_BIT)) 233 flags |= O_RDWR; 234 else if (mode & READ_BIT) 235 flags |= O_RDONLY; 236 else if (mode & WRITE_BIT) 237 flags |= O_WRONLY; 238 239 /* create if does not exist */ 240 if (mode & WRITE_BIT) { 241 flags |= O_CREAT; 242 243 /* append if exists? */ 244 if (mode & APPEND_BIT) 245 flags |= O_APPEND; 246 else 247 flags |= O_TRUNC; 248 } 249 250 /* open file */ 251 descriptor = open(path, flags, 0666); 252 if (descriptor < 0) 253 return (NULL); 254 255 /* initialize LispFile structure */ 256 file = LispFdopen(descriptor, mode); 257 if (file == NULL) 258 close(descriptor); 259 260 return (file); 261} 262 263void 264LispFclose(LispFile *file) 265{ 266 /* flush any pending output */ 267 LispFflush(file); 268 /* cleanup */ 269 close(file->descriptor); 270 if (file->buffer) 271 free(file->buffer); 272 free(file); 273} 274 275io_write_fn 276LispSetFileWrite(LispFile *file, io_write_fn new_write) 277{ 278 io_write_fn old_write = file->io_write; 279 280 file->io_write = new_write; 281 282 return (old_write); 283} 284 285int 286LispFflush(LispFile *file) 287{ 288 if (file->writable && file->length) { 289 int length = (*file->io_write)(file->descriptor, 290 file->buffer, file->length); 291 292 if (length > 0) { 293 if (file->length > length) 294 memmove(file->buffer, file->buffer + length, 295 file->length - length); 296 file->length -= length; 297 } 298 return (length); 299 } 300 301 return (0); 302} 303 304int 305LispFungetc(LispFile *file, int ch) 306{ 307 if (file->readable) { 308 file->available = 1; 309 file->unget = ch; 310 /* this should never happen */ 311 if (ch == '\n' && !file->binary) 312 --file->line; 313 } 314 315 return (ch); 316} 317 318int 319LispFgetc(LispFile *file) 320{ 321 int ch; 322 323 if (file->readable) { 324 unsigned char c; 325 326 if (file->available) { 327 ch = file->unget; 328 file->available = 0; 329 } 330 else if (file->buffered) { 331 if (file->writable) { 332 LispFflush(file); 333 if (read(file->descriptor, &c, 1) == 1) 334 ch = c; 335 else 336 ch = EOF; 337 } 338 else { 339 if (file->offset < file->length) 340 ch = ((unsigned char*)file->buffer)[file->offset++]; 341 else { 342 int length = read(file->descriptor, 343 file->buffer, pagesize); 344 345 if (length >= 0) 346 file->length = length; 347 else 348 file->length = 0; 349 file->offset = 0; 350 if (file->length) 351 ch = ((unsigned char*)file->buffer)[file->offset++]; 352 else 353 ch = EOF; 354 } 355 } 356 } 357 else if (read(file->descriptor, &c, 1) == 1) 358 ch = c; 359 else 360 ch = EOF; 361 } 362 else 363 ch = EOF; 364 365 if (ch == '\n' && !file->binary) 366 ++file->line; 367 368 return (ch); 369} 370 371int 372LispFputc(LispFile *file, int ch) 373{ 374 if (file->writable) { 375 unsigned char c = ch; 376 377 if (file->buffered) { 378 if (file->length + 1 >= pagesize) 379 LispFflush(file); 380 file->buffer[file->length++] = c; 381 } 382 else if ((*file->io_write)(file->descriptor, &c, 1) != 1) 383 ch = EOF; 384 385 if (!file->binary) { 386 /* update column number */ 387 if (ch == '\n') 388 file->column = 0; 389 else 390 ++file->column; 391 } 392 } 393 394 return (ch); 395} 396 397int 398LispSgetc(LispString *string) 399{ 400 int ch; 401 402 if (string->input >= string->length) 403 return (EOF); /* EOF reading from string */ 404 405 ch = ((unsigned char*)string->string)[string->input++]; 406 if (ch == '\n' && !string->binary) 407 ++string->line; 408 409 return (ch); 410} 411 412int 413LispSputc(LispString *string, int ch) 414{ 415 if (string->output + 1 >= string->space) { 416 if (string->fixed) 417 return (EOF); 418 else { 419 char *tmp = realloc(string->string, string->space + pagesize); 420 421 if (tmp == NULL) 422 return (EOF); 423 string->string = tmp; 424 string->space += pagesize; 425 } 426 } 427 428 string->string[string->output++] = ch; 429 if (string->length < string->output) 430 string->length = string->output; 431 432 /* update column number */ 433 if (!string->binary) { 434 if (ch == '\n') 435 string->column = 0; 436 else 437 ++string->column; 438 } 439 440 return (ch); 441} 442 443char * 444LispFgets(LispFile *file, char *string, int size) 445{ 446 int ch, offset = 0; 447 448 if (size < 1) 449 return (string); 450 451 for (;;) { 452 if (offset + 1 >= size) 453 break; 454 if ((ch = LispFgetc(file)) == EOF) 455 break; 456 string[offset++] = ch; 457 /* line number is calculated in LispFgetc */ 458 if (ch == '\n') 459 break; 460 } 461 string[offset] = '\0'; 462 463 return (offset ? string : NULL); 464} 465 466int 467LispFputs(LispFile *file, const char *buffer) 468{ 469 return (LispFwrite(file, buffer, strlen(buffer))); 470} 471 472int 473LispSputs(LispString *string, const char *buffer) 474{ 475 return (LispSwrite(string, buffer, strlen(buffer))); 476} 477 478int 479LispFread(LispFile *file, void *data, int size) 480{ 481 int bytes, length; 482 char *buffer; 483 484 if (!file->readable) 485 return (EOF); 486 487 if (size <= 0) 488 return (size); 489 490 length = 0; 491 buffer = (char*)data; 492 493 /* check if there is an unget character */ 494 if (file->available) { 495 *buffer++ = file->unget; 496 file->available = 0; 497 if (--size == 0) { 498 if (file->unget == '\n' && !file->binary) 499 ++file->line; 500 501 return (1); 502 } 503 504 length = 1; 505 } 506 507 if (file->buffered) { 508 void *base_data = (char*)data - length; 509 510 if (file->writable) { 511 LispFflush(file); 512 bytes = read(file->descriptor, buffer, size); 513 if (bytes < 0) 514 bytes = 0; 515 if (!file->binary) 516 file->line += calculate_line(base_data, length + bytes); 517 518 return (length + bytes); 519 } 520 521 /* read anything that is in the buffer */ 522 if (file->offset < file->length) { 523 bytes = file->length - file->offset; 524 if (bytes > size) 525 bytes = size; 526 memcpy(buffer, file->buffer + file->offset, bytes); 527 buffer += bytes; 528 file->offset += bytes; 529 size -= bytes; 530 } 531 532 /* if there is still something to read */ 533 if (size) { 534 bytes = read(file->descriptor, buffer, size); 535 if (bytes < 0) 536 bytes = 0; 537 538 length += bytes; 539 } 540 541 if (!file->binary) 542 file->line += calculate_line(base_data, length); 543 544 return (length); 545 } 546 547 bytes = read(file->descriptor, buffer, size); 548 if (bytes < 0) 549 bytes = 0; 550 if (!file->binary) 551 file->line += calculate_line(buffer - length, length + bytes); 552 553 return (length + bytes); 554} 555 556int 557LispFwrite(LispFile *file, const void *data, int size) 558{ 559 if (!file->writable || size < 0) 560 return (EOF); 561 562 if (!file->binary) 563 file->column = calculate_column(data, size, file->column); 564 565 if (file->buffered) { 566 int length, bytes; 567 const char *buffer = (const char *)data; 568 569 length = 0; 570 if (size + file->length > pagesize) { 571 /* fill remaining space in buffer and flush */ 572 bytes = pagesize - file->length; 573 memcpy(file->buffer + file->length, buffer, bytes); 574 file->length += bytes; 575 LispFflush(file); 576 577 /* check if all data was written */ 578 if (file->length) 579 return (pagesize - file->length); 580 581 length = bytes; 582 buffer += bytes; 583 size -= bytes; 584 } 585 586 while (size > pagesize) { 587 /* write multiple of pagesize */ 588 bytes = (*file->io_write)(file->descriptor, buffer, 589 size - (size % pagesize)); 590 if (bytes <= 0) 591 return (length); 592 593 length += bytes; 594 buffer += bytes; 595 size -= bytes; 596 } 597 598 if (size) { 599 /* keep remaining data in buffer */ 600 switch (size) { 601 case 8: 602 file->buffer[file->length++] = *buffer++; 603 case 7: 604 file->buffer[file->length++] = *buffer++; 605 case 6: 606 file->buffer[file->length++] = *buffer++; 607 case 5: 608 file->buffer[file->length++] = *buffer++; 609 case 4: 610 file->buffer[file->length++] = *buffer++; 611 case 3: 612 file->buffer[file->length++] = *buffer++; 613 case 2: 614 file->buffer[file->length++] = *buffer++; 615 case 1: 616 file->buffer[file->length++] = *buffer++; 617 break; 618 default: 619 memcpy(file->buffer + file->length, buffer, size); 620 file->length += size; 621 break; 622 } 623 length += size; 624 } 625 626 return (length); 627 } 628 629 return ((*file->io_write)(file->descriptor, data, size)); 630} 631 632int 633LispSwrite(LispString *string, const void *data, int size) 634{ 635 int bytes; 636 637 if (size < 0) 638 return (EOF); 639 640 if (string->output + size >= string->space) { 641 if (string->fixed) { 642 /* leave space for a ending nul character */ 643 bytes = string->space - string->output - 1; 644 645 if (bytes < size) 646 size = bytes; 647 648 if (size <= 0) 649 return (-1); 650 } 651 else { 652 char *tmp; 653 654 bytes = string->space + size; 655 bytes += pagesize - (bytes % pagesize); 656 tmp = realloc(string->string, bytes); 657 658 if (tmp == NULL) 659 return (-1); 660 661 string->string = tmp; 662 string->space = bytes; 663 } 664 } 665 memcpy(string->string + string->output, data, size); 666 string->output += size; 667 if (string->length < string->output) 668 string->length = string->output; 669 670 if (!string->binary) 671 string->column = calculate_column(data, size, string->column); 672 673 return (size); 674} 675 676const char * 677LispGetSstring(LispString *string, int *length) 678{ 679 if (string->string == NULL || string->length <= 0) { 680 *length = 0; 681 682 return (""); 683 } 684 *length = string->length; 685 if (string->string[string->length -1] != '\0') { 686 if (string->length < string->space) 687 string->string[string->length] = '\0'; 688 else if (string->fixed && string->space) 689 string->string[string->space - 1] = '\0'; 690 else { 691 char *tmp = realloc(string->string, string->space + pagesize); 692 693 if (tmp == NULL) 694 string->string[string->space - 1] = '\0'; 695 else { 696 string->string = tmp; 697 string->space += pagesize; 698 string->string[string->length] = '\0'; 699 } 700 } 701 } 702 703 return (string->string); 704} 705 706int 707LispRename(const char *from, const char *to) 708{ 709 return (rename(from, to)); 710} 711 712int 713LispUnlink(const char *name) 714{ 715 return (unlink(name)); 716} 717