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