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  */
51 extern int pagesize;
52 
53 /*
54  * Implementation
55  */
56 int
LispGet(void)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
LispUnget(int ch)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
LispPushInput(LispObj * stream)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
LispPopInput(LispObj * stream)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
calculate_line(const void * data,int size)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
calculate_column(const void * data,int size,int column)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 *
LispFdopen(int descriptor,int mode)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 *
LispFopen(const char * path,int mode)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
LispFclose(LispFile * file)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
LispSetFileWrite(LispFile * file,io_write_fn new_write)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
LispFflush(LispFile * file)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
LispFungetc(LispFile * file,int ch)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
LispFgetc(LispFile * file)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
LispFputc(LispFile * file,int ch)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
LispSgetc(LispString * string)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
LispSputc(LispString * string,int ch)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 *
LispFgets(LispFile * file,char * string,int size)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
LispFputs(LispFile * file,const char * buffer)467 LispFputs(LispFile *file, const char *buffer)
468 {
469     return (LispFwrite(file, buffer, strlen(buffer)));
470 }
471 
472 int
LispSputs(LispString * string,const char * buffer)473 LispSputs(LispString *string, const char *buffer)
474 {
475     return (LispSwrite(string, buffer, strlen(buffer)));
476 }
477 
478 int
LispFread(LispFile * file,void * data,int size)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
LispFwrite(LispFile * file,const void * data,int size)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
LispSwrite(LispString * string,const void * data,int size)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 *
LispGetSstring(LispString * string,int * length)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
LispRename(const char * from,const char * to)707 LispRename(const char *from, const char *to)
708 {
709     return (rename(from, to));
710 }
711 
712 int
LispUnlink(const char * name)713 LispUnlink(const char *name)
714 {
715     return (unlink(name));
716 }
717