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