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