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