1/* mpc_inp_str -- Input a complex number from a given stream.
2
3Copyright (C) 2009, 2010, 2011 INRIA
4
5This file is part of GNU MPC.
6
7GNU MPC is free software; you can redistribute it and/or modify it under
8the terms of the GNU Lesser General Public License as published by the
9Free Software Foundation; either version 3 of the License, or (at your
10option) any later version.
11
12GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY
13WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
15more details.
16
17You should have received a copy of the GNU Lesser General Public License
18along with this program. If not, see http://www.gnu.org/licenses/ .
19*/
20
21#include <stdio.h> /* for FILE */
22#include <ctype.h>
23#include <string.h>
24#include "mpc-impl.h"
25
26static size_t
27skip_whitespace (FILE *stream)
28{
29   int c = getc (stream);
30   size_t size = 0;
31   while (c != EOF && isspace ((unsigned char) c)) {
32      c = getc (stream);
33      size++;
34   }
35   if (c != EOF)
36      ungetc (c, stream);
37   return size;
38}
39
40/* Extract from stream the longest string made up of alphanumeric char and
41   '_' (i.e. n-char-sequence).
42   The user must free the returned string. */
43static char *
44extract_suffix (FILE *stream)
45{
46  int c;
47  size_t nread = 0;
48  size_t strsize = 100;
49  char *str = mpc_alloc_str (strsize);
50
51  c = getc (stream);
52  while (isalnum ((unsigned char) c) || c == '_') {
53    str [nread] = (char) c;
54    nread++;
55    if (nread == strsize) {
56      str = mpc_realloc_str (str, strsize, 2 * strsize);
57      strsize *= 2;
58         }
59    c = getc (stream);
60  }
61
62  str = mpc_realloc_str (str, strsize, nread + 1);
63  strsize = nread + 1;
64  str [nread] = '\0';
65
66  if (c != EOF)
67    ungetc (c, stream);
68  return str;
69}
70
71
72/* Extract from the stream the longest string of characters which are neither
73   whitespace nor brackets (except for an optional bracketed n-char_sequence
74   directly following nan or @nan@ independently of case).
75   The user must free the returned string.                                    */
76static char *
77extract_string (FILE *stream)
78{
79  int c;
80  size_t nread = 0;
81  size_t strsize = 100;
82  char *str = mpc_alloc_str (strsize);
83  size_t lenstr;
84
85  c = getc (stream);
86  while (c != EOF && c != '\n'
87         && !isspace ((unsigned char) c)
88         && c != '(' && c != ')') {
89    str [nread] = (char) c;
90    nread++;
91    if (nread == strsize) {
92      str = mpc_realloc_str (str, strsize, 2 * strsize);
93      strsize *= 2;
94    }
95    c = getc (stream);
96  }
97
98  str = mpc_realloc_str (str, strsize, nread + 1);
99  strsize = nread + 1;
100  str [nread] = '\0';
101
102  if (nread == 0)
103    return str;
104
105  lenstr = nread;
106
107  if (c == '(') {
108    size_t n;
109    char *suffix;
110    int ret;
111
112    /* (n-char-sequence) only after a NaN */
113    if ((nread != 3
114         || tolower ((unsigned char) (str[0])) != 'n'
115         || tolower ((unsigned char) (str[1])) != 'a'
116         || tolower ((unsigned char) (str[2])) != 'n')
117        && (nread != 5
118            || str[0] != '@'
119            || tolower ((unsigned char) (str[1])) != 'n'
120            || tolower ((unsigned char) (str[2])) != 'a'
121            || tolower ((unsigned char) (str[3])) != 'n'
122            || str[4] != '@')) {
123      ungetc (c, stream);
124      return str;
125    }
126
127    suffix = extract_suffix (stream);
128    nread += strlen (suffix) + 1;
129    if (nread >= strsize) {
130      str = mpc_realloc_str (str, strsize, nread + 1);
131      strsize = nread + 1;
132    }
133
134    /* Warning: the sprintf does not allow overlap between arguments. */
135    ret = sprintf (str + lenstr, "(%s", suffix);
136    MPC_ASSERT (ret >= 0);
137    n = lenstr + (size_t) ret;
138    MPC_ASSERT (n == nread);
139
140    c = getc (stream);
141    if (c == ')') {
142      str = mpc_realloc_str (str, strsize, nread + 2);
143      strsize = nread + 2;
144      str [nread] = (char) c;
145      str [nread+1] = '\0';
146      nread++;
147    }
148    else if (c != EOF)
149      ungetc (c, stream);
150
151    mpc_free_str (suffix);
152  }
153  else if (c != EOF)
154    ungetc (c, stream);
155
156  return str;
157}
158
159
160int
161mpc_inp_str (mpc_ptr rop, FILE *stream, size_t *read, int base,
162mpc_rnd_t rnd_mode)
163{
164   size_t white, nread = 0;
165   int inex = -1;
166   int c;
167   char *str;
168
169   if (stream == NULL)
170      stream = stdin;
171
172   white = skip_whitespace (stream);
173   c = getc (stream);
174   if (c != EOF) {
175     if (c == '(') {
176       char *real_str;
177       char *imag_str;
178       size_t n;
179       int ret;
180
181       nread++; /* the opening parenthesis */
182       white = skip_whitespace (stream);
183       real_str = extract_string (stream);
184       nread += strlen(real_str);
185
186       c = getc (stream);
187       if (!isspace ((unsigned int) c)) {
188         if (c != EOF)
189           ungetc (c, stream);
190         mpc_free_str (real_str);
191         goto error;
192       }
193       else
194         ungetc (c, stream);
195
196       white += skip_whitespace (stream);
197       imag_str = extract_string (stream);
198       nread += strlen (imag_str);
199
200       str = mpc_alloc_str (nread + 2);
201       ret = sprintf (str, "(%s %s", real_str, imag_str);
202       MPC_ASSERT (ret >= 0);
203       n = (size_t) ret;
204       MPC_ASSERT (n == nread + 1);
205       mpc_free_str (real_str);
206       mpc_free_str (imag_str);
207
208       white += skip_whitespace (stream);
209       c = getc (stream);
210       if (c == ')') {
211         str = mpc_realloc_str (str, nread +2, nread + 3);
212         str [nread+1] = (char) c;
213         str [nread+2] = '\0';
214         nread++;
215       }
216       else if (c != EOF)
217         ungetc (c, stream);
218     }
219     else {
220       if (c != EOF)
221         ungetc (c, stream);
222       str = extract_string (stream);
223       nread += strlen (str);
224     }
225
226     inex = mpc_set_str (rop, str, base, rnd_mode);
227
228     mpc_free_str (str);
229   }
230
231error:
232   if (inex == -1) {
233      mpfr_set_nan (mpc_realref(rop));
234      mpfr_set_nan (mpc_imagref(rop));
235   }
236   if (read != NULL)
237     *read = white + nread;
238   return inex;
239}
240