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