Mercurial > octave
comparison src/file-io.cc @ 1:78fd87e624cb
[project @ 1993-08-08 01:13:40 by jwe]
Initial revision
author | jwe |
---|---|
date | Sun, 08 Aug 1993 01:13:40 +0000 |
parents | |
children | 74d6f5fe70a1 |
comparison
equal
deleted
inserted
replaced
0:22412e3a4641 | 1:78fd87e624cb |
---|---|
1 // file-io.cc -*- C++ -*- | |
2 /* | |
3 | |
4 Copyright (C) 1993 John W. Eaton | |
5 | |
6 This file is part of Octave. | |
7 | |
8 Octave is free software; you can redistribute it and/or modify it | |
9 under the terms of the GNU General Public License as published by the | |
10 Free Software Foundation; either version 2, or (at your option) any | |
11 later version. | |
12 | |
13 Octave is distributed in the hope that it will be useful, but WITHOUT | |
14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
16 for more details. | |
17 | |
18 You should have received a copy of the GNU General Public License | |
19 along with Octave; see the file COPYING. If not, write to the Free | |
20 Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | |
21 | |
22 */ | |
23 | |
24 // Written by John C. Campbell <jcc@che.utexas.edu>. | |
25 | |
26 #ifdef __GNUG__ | |
27 #pragma implementation | |
28 #endif | |
29 | |
30 #include <DLList.h> | |
31 #include <unistd.h> | |
32 #include <string.h> | |
33 #include <stdio.h> | |
34 #include <stdlib.h> | |
35 #include <strstream.h> | |
36 #include <ctype.h> | |
37 | |
38 #include "statdefs.h" | |
39 #include "file-io.h" | |
40 #include "input.h" | |
41 #include "octave-hist.h" | |
42 #include "tree-const.h" | |
43 #include "error.h" | |
44 #include "utils.h" | |
45 #include "pager.h" | |
46 | |
47 // keeps a count of how many files are open and in the file list | |
48 static int file_count = 0; | |
49 | |
50 // keeps a count of args sent to printf or scanf | |
51 static int fmt_arg_count = 0; | |
52 | |
53 class File_info | |
54 { | |
55 public: | |
56 File_info (void); | |
57 File_info (int num, char *nm, FILE *t, char *md); | |
58 File_info (const File_info& f); | |
59 | |
60 File_info& operator = (const File_info& f); | |
61 | |
62 ~File_info (void); | |
63 | |
64 int number (void) const; | |
65 char *name (void) const; | |
66 FILE *fptr (void) const; | |
67 char *mode (void) const; | |
68 | |
69 private: | |
70 int _number; | |
71 char *_name; | |
72 FILE *_fptr; | |
73 char *_mode; | |
74 }; | |
75 | |
76 File_info::File_info (void) | |
77 { | |
78 _number = -1; | |
79 _name = (char *) NULL; | |
80 _fptr = (FILE *) NULL; | |
81 _mode = (char *) NULL; | |
82 } | |
83 | |
84 File_info::File_info (const File_info& f) | |
85 { | |
86 _number = f._number; | |
87 _name = strsave (f._name); | |
88 _fptr = f._fptr; | |
89 _mode = strsave (f._mode); | |
90 } | |
91 | |
92 File_info& | |
93 File_info::operator = (const File_info& f) | |
94 { | |
95 _number = f._number; | |
96 _name = strsave (f._name); | |
97 _fptr = f._fptr; | |
98 _mode = strsave (f._mode); | |
99 | |
100 return *this; | |
101 } | |
102 | |
103 File_info::~File_info (void) | |
104 { | |
105 delete [] _name; | |
106 delete [] _mode; | |
107 } | |
108 | |
109 File_info::File_info (int n, char *nm, FILE *t, char *md) | |
110 { | |
111 _number = n; | |
112 _name = strsave (nm); | |
113 _fptr = t; | |
114 _mode = strsave (md); | |
115 } | |
116 | |
117 int | |
118 File_info::number (void) const | |
119 { | |
120 return _number; | |
121 } | |
122 | |
123 char * | |
124 File_info::name (void) const | |
125 { | |
126 return _name; | |
127 } | |
128 | |
129 FILE * | |
130 File_info::fptr (void) const | |
131 { | |
132 return _fptr; | |
133 } | |
134 | |
135 char * | |
136 File_info::mode (void) const | |
137 { | |
138 return _mode; | |
139 } | |
140 | |
141 | |
142 // double linked list containing relevant information about open files | |
143 static DLList <File_info> file_list; | |
144 | |
145 void | |
146 initialize_file_io () | |
147 { | |
148 File_info _stdin (0, "stdin", stdin, "r"); | |
149 File_info _stdout (1, "stdout", stdout, "w"); | |
150 File_info _stderr (2, "stderr", stderr, "w"); | |
151 | |
152 file_list.append (_stdin); | |
153 file_list.append (_stdout); | |
154 file_list.append (_stderr); | |
155 | |
156 file_count = 3; | |
157 } | |
158 | |
159 Pix | |
160 return_valid_file (tree_constant& arg) | |
161 { | |
162 if (arg.is_string_type ()) | |
163 { | |
164 Pix p = file_list.first (); | |
165 File_info file; | |
166 for (int i = 0; i < file_count; i++) | |
167 { | |
168 char *file_name = arg.string_value (); | |
169 file = file_list (p); | |
170 if (strcmp (file.name (), file_name) == 0) | |
171 return p; | |
172 file_list.next (p); | |
173 } | |
174 } | |
175 else if (arg.is_scalar_type ()) | |
176 { | |
177 double file_num = arg.double_value (); | |
178 if ((double) NINT (file_num) != file_num) | |
179 error ("file number not an integer value"); | |
180 else | |
181 { | |
182 Pix p = file_list.first (); | |
183 File_info file; | |
184 for (int i = 0; i < file_count; i++) | |
185 { | |
186 file = file_list (p); | |
187 if (file.number () == file_num) | |
188 return p; | |
189 file_list.next (p); | |
190 } | |
191 error ("no file with that number"); | |
192 } | |
193 } | |
194 else | |
195 error ("inapproriate file specifier"); | |
196 | |
197 return (Pix) NULL; | |
198 } | |
199 | |
200 static Pix | |
201 fopen_file_for_user (tree_constant& arg, char *mode) | |
202 { | |
203 char *file_name = arg.string_value (); | |
204 | |
205 FILE *file_ptr = fopen (file_name, mode); | |
206 if (file_ptr != (FILE *) NULL) | |
207 { | |
208 File_info file (++file_count, file_name, file_ptr, mode); | |
209 file_list.append (file); | |
210 | |
211 Pix p = file_list.first (); | |
212 File_info file_from_list; | |
213 | |
214 for (int i = 0; i < file_count; i++) | |
215 { | |
216 file_from_list = file_list (p); | |
217 if (strcmp (file_from_list.name (), file_name) == 0) | |
218 return p; | |
219 file_list.next (p); | |
220 } | |
221 } | |
222 | |
223 error ("problems automatically opening file for user"); | |
224 return (Pix) NULL; | |
225 } | |
226 | |
227 | |
228 tree_constant * | |
229 fclose_internal (tree_constant *args) | |
230 { | |
231 tree_constant *retval = NULL_TREE_CONST; | |
232 | |
233 Pix p = return_valid_file (args[1]); | |
234 | |
235 if (p == (Pix) NULL) | |
236 return retval; | |
237 | |
238 File_info file = file_list (p); | |
239 | |
240 if (file.number () < 3) | |
241 { | |
242 warning ("fclose: can't close stdin, stdout, or stderr!"); | |
243 return retval; | |
244 } | |
245 | |
246 int success = fclose (file.fptr ()); | |
247 file_list.del (p); | |
248 file_count--; | |
249 | |
250 retval = new tree_constant[2]; | |
251 if (success == 0) | |
252 retval[0] = tree_constant (1.0); // succeeded | |
253 else | |
254 { | |
255 error ("fclose: error on closing file"); | |
256 retval[0] = tree_constant (0.0); // failed | |
257 } | |
258 | |
259 return retval; | |
260 } | |
261 | |
262 tree_constant * | |
263 fflush_internal (tree_constant *args) | |
264 { | |
265 tree_constant *retval = NULL_TREE_CONST; | |
266 | |
267 Pix p = return_valid_file (args[1]); | |
268 | |
269 if (p == (Pix) NULL) | |
270 return retval; | |
271 | |
272 File_info file = file_list (p); | |
273 | |
274 if (strcmp (file.mode (), "r") == 0) | |
275 { | |
276 warning ("can't flush an input stream"); | |
277 return retval; | |
278 } | |
279 | |
280 int success = 0; | |
281 if (file.number () == 1) | |
282 flush_output_to_pager (); | |
283 else | |
284 success = fflush (file.fptr ()); | |
285 | |
286 retval = new tree_constant[2]; | |
287 if (success == 0) | |
288 retval[0] = tree_constant (1.0); // succeeded | |
289 else | |
290 { | |
291 error ("fflush: write error"); | |
292 retval[0] = tree_constant (0.0); // failed | |
293 } | |
294 | |
295 return retval; | |
296 } | |
297 | |
298 static int | |
299 valid_mode (char *mode) | |
300 { | |
301 if (mode != (char *) NULL) | |
302 { | |
303 char m = mode[0]; | |
304 if (m == 'r' || m == 'w' || m == 'a') | |
305 { | |
306 m = mode[1]; | |
307 return (m == '\0' || (m == '+' && mode[2] == '\0')); | |
308 } | |
309 } | |
310 return 0; | |
311 } | |
312 | |
313 tree_constant * | |
314 fgets_internal (tree_constant *args, int nargout) | |
315 { | |
316 tree_constant *retval = NULL_TREE_CONST; | |
317 | |
318 Pix p = return_valid_file (args[1]); | |
319 | |
320 if (p == (Pix) NULL) | |
321 { | |
322 if (args[1].is_string_type ()) | |
323 { | |
324 struct stat buffer; | |
325 char *name = args[1].string_value (); | |
326 if (stat (name, &buffer) == 0 | |
327 && (buffer.st_mode & S_IFREG) == S_IFREG) | |
328 { | |
329 p = fopen_file_for_user (args[1], "r"); | |
330 } | |
331 else | |
332 { | |
333 error ("fgets: file dosen't exist"); | |
334 return retval; | |
335 } | |
336 } | |
337 else | |
338 return retval; | |
339 } | |
340 | |
341 int length = 0; | |
342 | |
343 if (args[2].is_scalar_type ()) | |
344 { | |
345 length = (int) args[2].double_value (); | |
346 if ((double) NINT (length) != length) | |
347 { | |
348 error ("fgets: length not an integer value"); | |
349 return retval; | |
350 } | |
351 } | |
352 | |
353 char string[length+1]; | |
354 File_info file = file_list (p); | |
355 char *success = fgets (string, length+1, file.fptr ()); | |
356 | |
357 if (success == (char *) NULL) | |
358 { | |
359 retval = new tree_constant[2]; | |
360 retval[0] = tree_constant (-1.0); | |
361 return retval; | |
362 } | |
363 | |
364 if (nargout == 2) | |
365 { | |
366 retval = new tree_constant[3]; | |
367 retval[1] = tree_constant ((double) strlen (string)); | |
368 } | |
369 else | |
370 retval = new tree_constant[2]; | |
371 | |
372 retval[0] = tree_constant (string); | |
373 | |
374 return retval; | |
375 } | |
376 | |
377 tree_constant * | |
378 fopen_internal (tree_constant *args) | |
379 { | |
380 tree_constant *retval = NULL_TREE_CONST; | |
381 Pix p; | |
382 | |
383 if (! args[1].is_string_type ()) | |
384 { | |
385 error ("fopen: file name must be a string"); | |
386 return retval; | |
387 } | |
388 | |
389 p = return_valid_file (args[1]); | |
390 | |
391 if (p != (Pix) NULL) | |
392 { | |
393 File_info file = file_list (p); | |
394 | |
395 retval = new tree_constant[2]; | |
396 retval[0] = tree_constant ((double) file.number ()); | |
397 | |
398 return retval; | |
399 } | |
400 | |
401 if (! args[2].is_string_type ()) | |
402 { | |
403 error ("fopen: mode must be a string"); | |
404 return retval; | |
405 } | |
406 | |
407 char *name = args[1].string_value (); | |
408 char *mode = args[2].string_value (); | |
409 | |
410 if (! valid_mode (mode)) | |
411 { | |
412 error ("fopen: invalid mode"); | |
413 return retval; | |
414 } | |
415 | |
416 struct stat buffer; | |
417 if (stat (name, &buffer) == 0 && (buffer.st_mode & S_IFDIR) == S_IFDIR) | |
418 { | |
419 error ("fopen: can't open directory"); | |
420 return retval; | |
421 } | |
422 | |
423 FILE *file_ptr = fopen (name, mode); | |
424 | |
425 if (file_ptr == (FILE *) NULL) | |
426 { | |
427 error ("fopen: file does not exist"); | |
428 return retval; | |
429 } | |
430 | |
431 int number = file_count++; | |
432 | |
433 File_info file (number, name, file_ptr, mode); | |
434 file_list.append (file); | |
435 | |
436 retval = new tree_constant[2]; | |
437 retval[0] = tree_constant ((double) number); | |
438 | |
439 return retval; | |
440 } | |
441 | |
442 tree_constant * | |
443 freport_internal () | |
444 { | |
445 tree_constant *retval = NULL_TREE_CONST; | |
446 Pix p = file_list.first (); | |
447 | |
448 ostrstream output_buf; | |
449 | |
450 output_buf << "\n number mode name\n\n"; | |
451 for (int i = 0; i < file_count; i++) | |
452 { | |
453 File_info file = file_list (p); | |
454 output_buf.form ("%7d%6s %s\n", file.number (), file.mode (), | |
455 file.name ()); | |
456 file_list.next (p); | |
457 } | |
458 | |
459 output_buf << "\n" << ends; | |
460 maybe_page_output (output_buf); | |
461 | |
462 return retval; | |
463 } | |
464 | |
465 tree_constant * | |
466 frewind_internal (tree_constant *args) | |
467 { | |
468 tree_constant *retval = NULL_TREE_CONST; | |
469 | |
470 Pix p = return_valid_file (args[1]); | |
471 if (p == (Pix) NULL) | |
472 p = fopen_file_for_user (args[1], "a+"); | |
473 | |
474 File_info file = file_list (p); | |
475 rewind (file.fptr ()); | |
476 | |
477 return retval; | |
478 } | |
479 | |
480 tree_constant * | |
481 fseek_internal (tree_constant *args, int nargin) | |
482 { | |
483 tree_constant *retval = NULL_TREE_CONST; | |
484 | |
485 Pix p = return_valid_file (args[1]); | |
486 | |
487 if (p == (Pix) NULL) | |
488 p = fopen_file_for_user (args[1], "a+"); | |
489 | |
490 long origin = SEEK_SET; | |
491 long offset = 0; | |
492 if (args[2].is_scalar_type ()) | |
493 { | |
494 offset = (long) args[2].double_value (); | |
495 if ((double) NINT (offset) != offset) | |
496 { | |
497 error ("fseek: offset not an integer value"); | |
498 return retval; | |
499 } | |
500 } | |
501 | |
502 if (nargin == 4 && args[3].is_scalar_type ()) | |
503 { | |
504 origin = (long) args[3].double_value (); | |
505 if (origin == -1) | |
506 origin = SEEK_CUR; | |
507 else if (origin == -2) | |
508 origin = SEEK_END; | |
509 else | |
510 { | |
511 if ((double) NINT (origin) != origin) | |
512 { | |
513 error ("fseek: origin not an integer value"); | |
514 return retval; | |
515 } | |
516 } | |
517 } | |
518 | |
519 File_info file = file_list (p); | |
520 int success = fseek (file.fptr (), offset, origin); | |
521 retval = new tree_constant[2]; | |
522 | |
523 if (success == 0) | |
524 retval[0] = tree_constant (1.0); // succeeded | |
525 else | |
526 { | |
527 error ("fseek: file error"); | |
528 retval[0] = tree_constant (0.0); // failed | |
529 } | |
530 | |
531 return retval; | |
532 } | |
533 | |
534 tree_constant * | |
535 ftell_internal (tree_constant *args) | |
536 { | |
537 tree_constant *retval = NULL_TREE_CONST; | |
538 Pix p = return_valid_file (args[1]); | |
539 | |
540 if (p == (Pix) NULL) | |
541 p = fopen_file_for_user (args[1], "a+"); | |
542 | |
543 File_info file = file_list (p); | |
544 long offset = ftell (file.fptr ()); | |
545 retval = new tree_constant[2]; | |
546 retval[0] = tree_constant ((double) offset); | |
547 | |
548 if (offset == -1L) | |
549 error ("ftell: write error"); | |
550 | |
551 return retval; | |
552 } | |
553 | |
554 void | |
555 close_files () | |
556 { | |
557 Pix p = file_list.first (); | |
558 | |
559 for (int i = 0; i < file_count; i++) | |
560 { | |
561 File_info file = file_list (p); | |
562 if (i > 2) // do not close stdin, stdout, stderr! | |
563 { | |
564 int success = fclose (file.fptr ()); | |
565 if (success != 0) | |
566 error ("closing %s", file.name ()); | |
567 } | |
568 file_list.del (p); | |
569 } | |
570 } | |
571 | |
572 static int | |
573 process_printf_format (char *s, tree_constant *args, ostrstream& sb, | |
574 char *type, int nargin) | |
575 { | |
576 ostrstream fmt; | |
577 | |
578 fmt << "%"; // do_printf() already blew past this one... | |
579 | |
580 tree_constant_rep::constant_type arg_type; | |
581 | |
582 int chars_from_fmt_str = 0; | |
583 | |
584 again: | |
585 switch (*s) | |
586 { | |
587 case '+': case '-': case ' ': case '0': case '#': | |
588 chars_from_fmt_str++; | |
589 fmt << *s++; | |
590 goto again; | |
591 | |
592 case '\0': | |
593 goto invalid_format; | |
594 | |
595 default: | |
596 break; | |
597 } | |
598 | |
599 if (*s == '*') | |
600 { | |
601 if (fmt_arg_count >= nargin) | |
602 { | |
603 message (type, "not enough arguments"); | |
604 return -1; | |
605 } | |
606 | |
607 if (args[fmt_arg_count].const_type () | |
608 != tree_constant_rep::scalar_constant) | |
609 { | |
610 message (type, "`*' must be replaced by an integer"); | |
611 return -1; | |
612 } | |
613 | |
614 fmt << (int) (args[fmt_arg_count++].double_value ()); | |
615 s++; | |
616 chars_from_fmt_str++; | |
617 } | |
618 else | |
619 { | |
620 while (*s != '\0' && isdigit (*s)) | |
621 { | |
622 chars_from_fmt_str++; | |
623 fmt << *s++; | |
624 } | |
625 } | |
626 | |
627 if (*s == '\0') | |
628 goto invalid_format; | |
629 | |
630 if (*s == '.') | |
631 { | |
632 chars_from_fmt_str++; | |
633 fmt << *s++; | |
634 } | |
635 | |
636 if (*s == '*') | |
637 { | |
638 if (*(s-1) == '*') | |
639 goto invalid_format; | |
640 | |
641 if (fmt_arg_count >= nargin) | |
642 { | |
643 message (type, "not enough arguments"); | |
644 return -1; | |
645 } | |
646 | |
647 if (args[fmt_arg_count].const_type () | |
648 != tree_constant_rep::scalar_constant) | |
649 { | |
650 message (type, "`*' must be replaced by an integer"); | |
651 return -1; | |
652 } | |
653 | |
654 fmt << (int) (args[fmt_arg_count++].double_value ()); | |
655 s++; | |
656 chars_from_fmt_str++; | |
657 } | |
658 else | |
659 { | |
660 while (*s != '\0' && isdigit (*s)) | |
661 { | |
662 chars_from_fmt_str++; | |
663 fmt << *s++; | |
664 } | |
665 } | |
666 | |
667 if (*s == '\0') | |
668 goto invalid_format; | |
669 | |
670 if (*s != '\0' && (*s == 'h' || *s == 'l' || *s == 'L')) | |
671 { | |
672 chars_from_fmt_str++; | |
673 fmt << *s++; | |
674 } | |
675 | |
676 if (*s == '\0') | |
677 goto invalid_format; | |
678 | |
679 if (fmt_arg_count >= nargin) | |
680 { | |
681 message (type, "not enough arguments"); | |
682 return -1; | |
683 } | |
684 | |
685 arg_type = args[fmt_arg_count].const_type (); | |
686 | |
687 switch (*s) | |
688 { | |
689 case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': | |
690 | |
691 if (arg_type != tree_constant_rep::scalar_constant) | |
692 goto invalid_conversion; | |
693 else | |
694 { | |
695 chars_from_fmt_str++; | |
696 fmt << *s << ends; | |
697 double d = args[fmt_arg_count++].double_value (); | |
698 if ((int) d != d) | |
699 goto invalid_conversion; | |
700 else | |
701 { | |
702 char *s = fmt.str (); | |
703 sb.form (s, (int) d); | |
704 delete [] s; | |
705 return chars_from_fmt_str; | |
706 } | |
707 } | |
708 | |
709 case 'e': case 'E': case 'f': case 'g': case 'G': | |
710 | |
711 if (arg_type != tree_constant_rep::scalar_constant) | |
712 goto invalid_conversion; | |
713 else | |
714 { | |
715 chars_from_fmt_str++; | |
716 fmt << *s << ends; | |
717 char *s = fmt.str (); | |
718 sb.form (s, args[fmt_arg_count++].double_value ()); | |
719 delete [] s; | |
720 return chars_from_fmt_str; | |
721 } | |
722 | |
723 case 's': | |
724 | |
725 if (arg_type != tree_constant_rep::string_constant) | |
726 goto invalid_conversion; | |
727 else | |
728 { | |
729 chars_from_fmt_str++; | |
730 fmt << *s << ends; | |
731 char *s = fmt.str (); | |
732 sb.form (s, args[fmt_arg_count++].string_value ()); | |
733 delete [] s; | |
734 return chars_from_fmt_str; | |
735 } | |
736 | |
737 case 'c': | |
738 | |
739 if (arg_type != tree_constant_rep::string_constant) | |
740 goto invalid_conversion; | |
741 else | |
742 { | |
743 chars_from_fmt_str++; | |
744 fmt << *s << ends; | |
745 char *str = args[fmt_arg_count++].string_value (); | |
746 if (strlen (str) != 1) | |
747 goto invalid_conversion; | |
748 else | |
749 { | |
750 char *s = fmt.str (); | |
751 sb.form (s, *str); | |
752 delete [] s; | |
753 return chars_from_fmt_str; | |
754 } | |
755 } | |
756 | |
757 default: | |
758 goto invalid_format; | |
759 } | |
760 | |
761 invalid_conversion: | |
762 message (type, "invalid conversion"); | |
763 return -1; | |
764 | |
765 invalid_format: | |
766 message (type, "invalid format"); | |
767 return -1; | |
768 } | |
769 | |
770 | |
771 tree_constant * | |
772 do_printf (char *type, tree_constant *args, int nargin, int nargout) | |
773 { | |
774 tree_constant *retval = NULL_TREE_CONST; | |
775 fmt_arg_count = 1; | |
776 char *fmt; | |
777 File_info file; | |
778 | |
779 if (strcmp (type, "fprintf") == 0) | |
780 { | |
781 Pix p; | |
782 | |
783 if (args[2].is_string_type ()) | |
784 { | |
785 fmt = args[2].string_value (); | |
786 fmt_arg_count++; | |
787 } | |
788 else | |
789 { | |
790 error ("%s: format must be a string", type); | |
791 return retval; | |
792 } | |
793 | |
794 if (args[1].is_scalar_type ()) | |
795 { | |
796 p = return_valid_file (args[1]); | |
797 if (p == (Pix) NULL) | |
798 return retval; | |
799 } | |
800 else if (args[1].is_string_type ()) | |
801 { | |
802 p = return_valid_file (args[1]); | |
803 if (p == (Pix) NULL) | |
804 p = fopen_file_for_user (args[1], "a+"); | |
805 } | |
806 else | |
807 { | |
808 error ("%s: illegal file specifier", type); | |
809 return retval; | |
810 } | |
811 | |
812 file = file_list (p); | |
813 if (file.mode () == "r") | |
814 { | |
815 error ("%s: file is read only", type); | |
816 return retval; | |
817 } | |
818 fmt = args[2].string_value (); | |
819 fmt_arg_count++; | |
820 } | |
821 else if (args[1].is_string_type ()) | |
822 { | |
823 fmt = args[1].string_value (); | |
824 fmt_arg_count++; | |
825 } | |
826 else | |
827 { | |
828 error ("%s: invalid format string", type); | |
829 return retval; | |
830 } | |
831 | |
832 // Scan fmt for % escapes and print out the arguments. | |
833 | |
834 ostrstream output_buf; | |
835 | |
836 char *ptr = fmt; | |
837 | |
838 for (;;) | |
839 { | |
840 char c; | |
841 while ((c = *ptr++) != '\0' && c != '%') | |
842 output_buf << c; | |
843 | |
844 if (c == '\0') | |
845 break; | |
846 | |
847 if (*ptr == '%') | |
848 { | |
849 ptr++; | |
850 output_buf << c; | |
851 continue; | |
852 } | |
853 | |
854 // We must be looking at a format specifier. Extract it or fail. | |
855 | |
856 | |
857 int status = process_printf_format (ptr, args, output_buf, type, | |
858 nargin); | |
859 | |
860 if (status < 0) | |
861 return retval; | |
862 | |
863 ptr += status; | |
864 } | |
865 | |
866 output_buf << ends; | |
867 if (strcmp (type, "printf") == 0 | |
868 || (strcmp (type, "fprintf") == 0 && file.number () == 1)) | |
869 { | |
870 maybe_page_output (output_buf); | |
871 } | |
872 else if (strcmp (type, "fprintf") == 0) | |
873 { | |
874 char *msg = output_buf.str (); | |
875 int success = fputs (msg, file.fptr ()); | |
876 if (success == EOF) | |
877 error ("%s: writing to file", type); | |
878 delete [] msg; | |
879 } | |
880 else if (strcmp (type, "sprintf") == 0) | |
881 { | |
882 retval = new tree_constant [2]; | |
883 char *msg = output_buf.str (); | |
884 retval[0] = tree_constant (msg); | |
885 delete [] msg; | |
886 } | |
887 | |
888 return retval; | |
889 } | |
890 | |
891 static int | |
892 process_scanf_format (char *s, tree_constant *args, ostrstream& fmt, | |
893 char *type, int nargout, FILE* fptr, | |
894 tree_constant *values) | |
895 { | |
896 fmt << "%"; | |
897 | |
898 tree_constant_rep::constant_type arg_type; | |
899 | |
900 int chars_from_fmt_str = 0; | |
901 int store_value = 1; | |
902 int string_width = -1; | |
903 int success = 1; | |
904 | |
905 if (*s == '*') | |
906 { | |
907 store_value = 0; | |
908 s++; | |
909 chars_from_fmt_str++; | |
910 } | |
911 | |
912 if (isdigit (*s)) | |
913 { | |
914 ostrstream str_number; | |
915 while (*s != '\0' && isdigit (*s)) | |
916 { | |
917 chars_from_fmt_str++; | |
918 str_number << *s; | |
919 fmt << *s++; | |
920 } | |
921 str_number << ends; | |
922 char *number = str_number.str (); | |
923 string_width = atoi (number); | |
924 delete [] number; | |
925 } | |
926 | |
927 if (*s == '\0') | |
928 goto invalid_format; | |
929 | |
930 if (*s != '\0' && (*s == 'h' || *s == 'l' || *s == 'L')) | |
931 { | |
932 chars_from_fmt_str++; | |
933 s++; | |
934 } | |
935 | |
936 if (*s == '\0') | |
937 goto invalid_format; | |
938 | |
939 if (fmt_arg_count >= nargout && store_value) | |
940 { | |
941 message (type, "not enough arguments"); | |
942 return -1; | |
943 } | |
944 | |
945 arg_type = args[fmt_arg_count].const_type (); | |
946 | |
947 switch (*s) | |
948 { | |
949 case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': | |
950 { | |
951 chars_from_fmt_str++; | |
952 fmt << *s << ends; | |
953 int temp; | |
954 char *str = fmt.str (); | |
955 success = fscanf (fptr, str, &temp); | |
956 delete [] str; | |
957 if (success > 0 && store_value) | |
958 values[fmt_arg_count++] = tree_constant ((double) temp); | |
959 } | |
960 break; | |
961 case 'e': case 'E': case 'f': case 'g': case 'G': | |
962 { | |
963 chars_from_fmt_str++; | |
964 fmt << 'l' << *s << ends; | |
965 double temp; | |
966 char *str = fmt.str (); | |
967 success = fscanf (fptr, str, &temp); | |
968 delete [] str; | |
969 if (success > 0 && store_value) | |
970 values[fmt_arg_count++] = tree_constant (temp); | |
971 } | |
972 break; | |
973 case 's': | |
974 { | |
975 if (string_width < 1) | |
976 { | |
977 string_width = 0; | |
978 long original_position = ftell (fptr); | |
979 int c; | |
980 | |
981 while ((c = getc (fptr)) != EOF | |
982 && (c == ' ' || c == '\n' || c != '\t')) | |
983 ; // Don't count leading whitespace. | |
984 | |
985 if (c != EOF) | |
986 string_width++; | |
987 | |
988 for (;;) | |
989 { | |
990 c = getc (fptr); | |
991 if (c != EOF && c != ' ' && c != '\n' && c != '\t') | |
992 string_width++; | |
993 else | |
994 break; | |
995 } | |
996 | |
997 fseek (fptr, original_position, SEEK_SET); | |
998 } | |
999 chars_from_fmt_str++; | |
1000 char temp[string_width+1]; | |
1001 fmt << *s << ends; | |
1002 char *str = fmt.str (); | |
1003 success = fscanf (fptr, str, temp); | |
1004 delete [] str; | |
1005 if (success && store_value) | |
1006 values[fmt_arg_count++] = tree_constant (temp); | |
1007 } | |
1008 break; | |
1009 case 'c': | |
1010 { | |
1011 if (string_width < 1) | |
1012 string_width = 1; | |
1013 chars_from_fmt_str++; | |
1014 char temp[string_width+1]; | |
1015 memset (temp, '\0', string_width+1); | |
1016 fmt << *s << ends; | |
1017 char *str = fmt.str (); | |
1018 success = fscanf (fptr, str, temp); | |
1019 delete [] str; | |
1020 temp[string_width] = '\0'; | |
1021 if (success > 0 && store_value) | |
1022 values[fmt_arg_count++] = tree_constant (temp); | |
1023 } | |
1024 break; | |
1025 default: | |
1026 goto invalid_format; | |
1027 } | |
1028 | |
1029 if (success > 0 || (success == 0 && store_value == 0)) | |
1030 return chars_from_fmt_str; | |
1031 | |
1032 if (success == 0) | |
1033 message (type, "invalid conversion"); | |
1034 else if (success == EOF) | |
1035 { | |
1036 if (strcmp (type, "fscanf") == 0) | |
1037 message (type, "end of file reached before final conversion"); | |
1038 else if (strcmp (type, "sscanf") == 0) | |
1039 message (type, "end of string reached before final conversion"); | |
1040 else if (strcmp (type, "scanf") == 0) | |
1041 message (type, "end of input reached before final conversion"); | |
1042 } | |
1043 else | |
1044 { | |
1045 invalid_format: | |
1046 message (type, "invalid format"); | |
1047 } | |
1048 | |
1049 return -1; | |
1050 } | |
1051 | |
1052 tree_constant * | |
1053 do_scanf (char *type, tree_constant *args, int nargin, int nargout) | |
1054 { | |
1055 tree_constant *retval = NULL_TREE_CONST; | |
1056 char *scanf_fmt = (char *) NULL; | |
1057 char *tmp_file = (char *) NULL; | |
1058 int tmp_file_open = 0; | |
1059 FILE *fptr = (FILE *) NULL; | |
1060 File_info file; | |
1061 | |
1062 fmt_arg_count = 0; | |
1063 | |
1064 if (strcmp (type, "scanf") != 0) | |
1065 { | |
1066 if ( args[2].is_string_type ()) | |
1067 scanf_fmt = args[2].string_value (); | |
1068 else | |
1069 { | |
1070 error ("%s: format must be a string", type); | |
1071 return retval; | |
1072 } | |
1073 } | |
1074 | |
1075 int doing_fscanf = (strcmp (type, "fscanf") == 0); | |
1076 | |
1077 if (doing_fscanf) | |
1078 { | |
1079 Pix p; | |
1080 if (args[1].is_scalar_type () | |
1081 || args[1].is_string_type ()) | |
1082 { | |
1083 p = return_valid_file (args[1]); | |
1084 if (p == (Pix) NULL) | |
1085 return retval; | |
1086 } | |
1087 else | |
1088 { | |
1089 error ("%s: illegal file specifier", type); | |
1090 return retval; | |
1091 } | |
1092 | |
1093 file = file_list (p); | |
1094 | |
1095 if (strcmp (file.mode (), "w") == 0 || strcmp (file.mode (), "a") == 0) | |
1096 { | |
1097 error ("%s: this file is opened for writing only", type); | |
1098 return retval; | |
1099 } | |
1100 | |
1101 fptr = file.fptr (); | |
1102 } | |
1103 | |
1104 if (args[1].is_string_type () || (doing_fscanf && file.number () == 0)) | |
1105 { | |
1106 char *string; | |
1107 | |
1108 if (strcmp (type, "scanf") == 0) | |
1109 scanf_fmt = args[1].string_value (); | |
1110 | |
1111 if (strcmp (type, "scanf") == 0 | |
1112 || (doing_fscanf && file.number () == 0)) | |
1113 { | |
1114 string = gnu_readline (""); | |
1115 if (string && *string) | |
1116 maybe_save_history (string); | |
1117 } | |
1118 else | |
1119 string = args[1].string_value (); | |
1120 | |
1121 tmp_file = tmpnam ((char *) NULL); | |
1122 | |
1123 fptr = fopen (tmp_file, "w+"); | |
1124 if (fptr == (FILE *) NULL) | |
1125 { | |
1126 error ("%s: error opening temporary file", type); | |
1127 return retval; | |
1128 } | |
1129 tmp_file_open = 1; | |
1130 unlink (tmp_file); | |
1131 | |
1132 if (string == (char *) NULL) | |
1133 panic_impossible (); | |
1134 | |
1135 int success = fputs (string, fptr); | |
1136 fflush (fptr); | |
1137 rewind (fptr); | |
1138 | |
1139 if (success < 0) | |
1140 { | |
1141 error ("%s: trouble writing temporary file", type); | |
1142 fclose (fptr); | |
1143 return retval; | |
1144 } | |
1145 } | |
1146 else if (! doing_fscanf) | |
1147 { | |
1148 error ("%s: first argument must be a string", type); | |
1149 return retval; | |
1150 } | |
1151 | |
1152 // Scan scanf_fmt for % escapes and assign the arguments. | |
1153 | |
1154 retval = new tree_constant[nargout+1]; | |
1155 | |
1156 char *ptr = scanf_fmt; | |
1157 | |
1158 for (;;) | |
1159 { | |
1160 ostrstream fmt; | |
1161 char c; | |
1162 while ((c = *ptr++) != '\0' && c != '%') | |
1163 fmt << c; | |
1164 | |
1165 if (c == '\0') | |
1166 break; | |
1167 | |
1168 if (*ptr == '%') | |
1169 { | |
1170 ptr++; | |
1171 fmt << c; | |
1172 continue; | |
1173 } | |
1174 | |
1175 // We must be looking at a format specifier. Extract it or fail. | |
1176 | |
1177 int status = process_scanf_format (ptr, args, fmt, type, | |
1178 nargout, fptr, retval); | |
1179 | |
1180 if (status < 0) | |
1181 { | |
1182 if (fmt_arg_count == 0) | |
1183 { | |
1184 delete [] retval; | |
1185 retval = NULL_TREE_CONST; | |
1186 } | |
1187 break; | |
1188 } | |
1189 | |
1190 ptr += status; | |
1191 } | |
1192 | |
1193 if (tmp_file_open) | |
1194 fclose (fptr); | |
1195 | |
1196 return retval; | |
1197 } | |
1198 | |
1199 /* | |
1200 ;;; Local Variables: *** | |
1201 ;;; mode: C++ *** | |
1202 ;;; page-delimiter: "^/\\*" *** | |
1203 ;;; End: *** | |
1204 */ |