Home | History | Annotate | Line # | Download | only in lisp
      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 Csar 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  */
     51 extern int pagesize;
     52 
     53 /*
     54  * Implementation
     55  */
     56 int
     57 LispGet(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 
    100 int
    101 LispUnget(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 
    118 void
    119 LispPushInput(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 
    142 void
    143 LispPopInput(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  */
    157 static int
    158 calculate_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 
    171 static int
    172 calculate_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 
    190 LispFile *
    191 LispFdopen(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 
    224 LispFile *
    225 LispFopen(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 
    263 void
    264 LispFclose(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 
    275 io_write_fn
    276 LispSetFileWrite(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 
    285 int
    286 LispFflush(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 
    304 int
    305 LispFungetc(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 
    318 int
    319 LispFgetc(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 
    371 int
    372 LispFputc(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 
    397 int
    398 LispSgetc(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 
    412 int
    413 LispSputc(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 
    443 char *
    444 LispFgets(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 
    466 int
    467 LispFputs(LispFile *file, const char *buffer)
    468 {
    469     return (LispFwrite(file, buffer, strlen(buffer)));
    470 }
    471 
    472 int
    473 LispSputs(LispString *string, const char *buffer)
    474 {
    475     return (LispSwrite(string, buffer, strlen(buffer)));
    476 }
    477 
    478 int
    479 LispFread(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 
    556 int
    557 LispFwrite(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 
    632 int
    633 LispSwrite(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 
    676 const char *
    677 LispGetSstring(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 
    706 int
    707 LispRename(const char *from, const char *to)
    708 {
    709     return (rename(from, to));
    710 }
    711 
    712 int
    713 LispUnlink(const char *name)
    714 {
    715     return (unlink(name));
    716 }
    717