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