1 /********************************************
2 cast.c
3 copyright 2009-2014,2016, Thomas E. Dickey
4 copyright 1991-1995,1996, Michael D. Brennan
5 
6 This is a source file for mawk, an implementation of
7 the AWK programming language.
8 
9 Mawk is distributed without warranty under the terms of
10 the GNU General Public License, version 2, 1991.
11 ********************************************/
12 
13 /*
14  * $MawkId: cast.c,v 1.21 2016/09/30 11:51:20 tom Exp $
15  */
16 
17 /*  cast.c  */
18 
19 #include "mawk.h"
20 #include "field.h"
21 #include "memory.h"
22 #include "scan.h"
23 #include "repl.h"
24 
25 const int mpow2[NUM_CELL_TYPES] =
26 {1, 2, 4, 8, 16, 32, 64, 128, 256, 512};
27 
28 void
cast1_to_d(CELL * cp)29 cast1_to_d(CELL *cp)
30 {
31     switch (cp->type) {
32     case C_NOINIT:
33 	cp->dval = 0.0;
34 	break;
35 
36     case C_DOUBLE:
37 	return;
38 
39     case C_MBSTRN:
40     case C_STRING:
41 	{
42 	    register STRING *s = (STRING *) cp->ptr;
43 
44 	    errno = 0;
45 #ifdef FPE_TRAPS_ON		/* look for overflow error */
46 	    cp->dval = strtod(s->str, (char **) 0);
47 	    if (errno && cp->dval != 0.0)	/* ignore underflow */
48 		rt_error("overflow converting %s to double", s->str);
49 #else
50 	    cp->dval = strtod(s->str, (char **) 0);
51 #endif
52 	    free_STRING(s);
53 	}
54 	break;
55 
56     case C_STRNUM:
57 	/* don't need to convert, but do need to free the STRING part */
58 	free_STRING(string(cp));
59 	break;
60 
61     default:
62 	bozo("cast on bad type");
63     }
64     cp->type = C_DOUBLE;
65 }
66 
67 void
cast2_to_d(CELL * cp)68 cast2_to_d(CELL *cp)
69 {
70     register STRING *s;
71 
72     switch (cp->type) {
73     case C_NOINIT:
74 	cp->dval = 0.0;
75 	break;
76 
77     case C_DOUBLE:
78 	goto two;
79     case C_STRNUM:
80 	free_STRING(string(cp));
81 	break;
82 
83     case C_MBSTRN:
84     case C_STRING:
85 	s = (STRING *) cp->ptr;
86 
87 	errno = 0;
88 #ifdef FPE_TRAPS_ON		/* look for overflow error */
89 	cp->dval = strtod(s->str, (char **) 0);
90 	if (errno && cp->dval != 0.0)	/* ignore underflow */
91 	    rt_error("overflow converting %s to double", s->str);
92 #else
93 	cp->dval = strtod(s->str, (char **) 0);
94 #endif
95 	free_STRING(s);
96 	break;
97 
98     default:
99 	bozo("cast on bad type");
100     }
101     cp->type = C_DOUBLE;
102 
103   two:
104     cp++;
105 
106     switch (cp->type) {
107     case C_NOINIT:
108 	cp->dval = 0.0;
109 	break;
110 
111     case C_DOUBLE:
112 	return;
113     case C_STRNUM:
114 	free_STRING(string(cp));
115 	break;
116 
117     case C_MBSTRN:
118     case C_STRING:
119 	s = (STRING *) cp->ptr;
120 
121 	errno = 0;
122 #ifdef FPE_TRAPS_ON		/* look for overflow error */
123 	cp->dval = strtod(s->str, (char **) 0);
124 	if (errno && cp->dval != 0.0)	/* ignore underflow */
125 	    rt_error("overflow converting %s to double", s->str);
126 #else
127 	cp->dval = strtod(s->str, (char **) 0);
128 #endif
129 	free_STRING(s);
130 	break;
131 
132     default:
133 	bozo("cast on bad type");
134     }
135     cp->type = C_DOUBLE;
136 }
137 
138 void
cast1_to_s(CELL * cp)139 cast1_to_s(CELL *cp)
140 {
141     register Int lval;
142     char xbuff[260];
143 
144     switch (cp->type) {
145     case C_NOINIT:
146 	null_str.ref_cnt++;
147 	cp->ptr = (PTR) & null_str;
148 	break;
149 
150     case C_DOUBLE:
151 
152 	lval = d_to_I(cp->dval);
153 	if (lval == cp->dval)
154 	    sprintf(xbuff, INT_FMT, lval);
155 	else
156 	    sprintf(xbuff, string(CONVFMT)->str, cp->dval);
157 
158 	cp->ptr = (PTR) new_STRING(xbuff);
159 	break;
160 
161     case C_STRING:
162 	return;
163 
164     case C_MBSTRN:
165     case C_STRNUM:
166 	break;
167 
168     default:
169 	bozo("bad type on cast");
170     }
171     cp->type = C_STRING;
172 }
173 
174 void
cast2_to_s(CELL * cp)175 cast2_to_s(CELL *cp)
176 {
177     register Int lval;
178     char xbuff[260];
179 
180     switch (cp->type) {
181     case C_NOINIT:
182 	null_str.ref_cnt++;
183 	cp->ptr = (PTR) & null_str;
184 	break;
185 
186     case C_DOUBLE:
187 
188 	lval = d_to_I(cp->dval);
189 	if (lval == cp->dval)
190 	    sprintf(xbuff, INT_FMT, lval);
191 	else
192 	    sprintf(xbuff, string(CONVFMT)->str, cp->dval);
193 
194 	cp->ptr = (PTR) new_STRING(xbuff);
195 	break;
196 
197     case C_STRING:
198 	goto two;
199 
200     case C_MBSTRN:
201     case C_STRNUM:
202 	break;
203 
204     default:
205 	bozo("bad type on cast");
206     }
207     cp->type = C_STRING;
208 
209   two:
210     cp++;
211 
212     switch (cp->type) {
213     case C_NOINIT:
214 	null_str.ref_cnt++;
215 	cp->ptr = (PTR) & null_str;
216 	break;
217 
218     case C_DOUBLE:
219 
220 	lval = d_to_I(cp->dval);
221 	if (lval == cp->dval)
222 	    sprintf(xbuff, INT_FMT, lval);
223 	else
224 	    sprintf(xbuff, string(CONVFMT)->str, cp->dval);
225 
226 	cp->ptr = (PTR) new_STRING(xbuff);
227 	break;
228 
229     case C_STRING:
230 	return;
231 
232     case C_MBSTRN:
233     case C_STRNUM:
234 	break;
235 
236     default:
237 	bozo("bad type on cast");
238     }
239     cp->type = C_STRING;
240 }
241 
242 void
cast_to_RE(CELL * cp)243 cast_to_RE(CELL *cp)
244 {
245     register PTR p;
246 
247     if (cp->type < C_STRING)
248 	cast1_to_s(cp);
249 
250     p = re_compile(string(cp));
251     no_leaks_re_ptr(p);
252     free_STRING(string(cp));
253     cp->type = C_RE;
254     cp->ptr = p;
255 
256 }
257 
258 void
cast_for_split(CELL * cp)259 cast_for_split(CELL *cp)
260 {
261     static const char meta[] = "^$.*+?|[]()";
262     static char xbuff[] = "\\X";
263     int c;
264     size_t len;
265 
266     if (cp->type < C_STRING)
267 	cast1_to_s(cp);
268 
269     if ((len = string(cp)->len) == 1) {
270 	if ((c = string(cp)->str[0]) == ' ') {
271 	    free_STRING(string(cp));
272 	    cp->type = C_SPACE;
273 	    return;
274 	} else if (c == 0) {
275 #ifdef LOCAL_REGEXP
276 	    char temp[1];
277 	    temp[0] = (char) c;
278 	    free_STRING(string(cp));
279 	    cp->ptr = (PTR) new_STRING1(temp, (size_t) 1);
280 #else
281 	    /*
282 	     * A null is not a meta character, but strchr will match it anyway.
283 	     * For now, there's no reason to compile a null as a regular
284 	     * expression - just return a string containing the single
285 	     * character.  That is used in a special case in set_rs_shadow().
286 	     */
287 	    char temp[2];
288 	    temp[0] = (char) c;
289 	    free_STRING(string(cp));
290 	    cp->ptr = (PTR) new_STRING1(temp, (size_t) 1);
291 	    return;
292 #endif
293 	} else if ((strchr) (meta, c)) {
294 	    xbuff[1] = (char) c;
295 	    free_STRING(string(cp));
296 	    cp->ptr = (PTR) new_STRING(xbuff);
297 	}
298     } else if (len == 0) {
299 	free_STRING(string(cp));
300 	cp->type = C_SNULL;
301 	return;
302     }
303 
304     cast_to_RE(cp);
305 }
306 
307 /* input: cp-> a CELL of type C_MBSTRN (maybe strnum)
308    test it -- casting it to the appropriate type
309    which is C_STRING or C_STRNUM
310 */
311 
312 void
check_strnum(CELL * cp)313 check_strnum(CELL *cp)
314 {
315     char *temp;
316     register unsigned char *s, *q;
317 
318     cp->type = C_STRING;	/* assume not C_STRNUM */
319     s = (unsigned char *) string(cp)->str;
320     q = s + string(cp)->len;
321     while (scan_code[*s] == SC_SPACE)
322 	s++;
323     if (s == q)
324 	return;
325 
326     while (scan_code[q[-1]] == SC_SPACE)
327 	q--;
328     if (scan_code[q[-1]] != SC_DIGIT &&
329 	q[-1] != '.')
330 	return;
331 
332     switch (scan_code[*s]) {
333     case SC_DIGIT:
334     case SC_PLUS:
335     case SC_MINUS:
336     case SC_DOT:
337 
338 	errno = 0;
339 #ifdef FPE_TRAPS_ON
340 	cp->dval = strtod((char *) s, &temp);
341 	/* make overflow pure string */
342 	if (errno && cp->dval != 0.0)
343 	    break;
344 #else
345 	cp->dval = strtod((char *) s, &temp);
346 #endif
347 #ifdef ERANGE
348 	if (errno == ERANGE)
349 	    break;
350 #endif
351 
352 	if ((char *) q <= temp) {
353 	    cp->type = C_STRNUM;
354 	    /*  <= instead of == , for some buggy strtod
355 	       e.g. Apple Unix */
356 	}
357 	break;
358     }
359 }
360 
361 /* cast a CELL to a replacement cell */
362 
363 void
cast_to_REPL(CELL * cp)364 cast_to_REPL(CELL *cp)
365 {
366     register STRING *sval;
367 
368     if (cp->type < C_STRING)
369 	cast1_to_s(cp);
370     sval = (STRING *) cp->ptr;
371 
372     cellcpy(cp, repl_compile(sval));
373     free_STRING(sval);
374 }
375 
376 /* convert a double to Int (this is not as simple as a
377    cast because the results are undefined if it won't fit).
378    Truncate large values to +Max_Int or -Max_Int
379    Send nans to -Max_Int
380 */
381 
382 Int
d_to_I(double d)383 d_to_I(double d)
384 {
385     if (d >= Max_Int)
386 	return Max_Int;
387     if (d > -Max_Int)
388 	return (Int) d;
389     return -Max_Int;
390 }
391 
392 /* convert a double to UInt (this is not as simple as a
393    cast because the results are undefined if it won't fit).
394    Truncate large values to Max_UInt or 0
395    Send nans to 0
396 */
397 
398 UInt
d_to_U(double d)399 d_to_U(double d)
400 {
401     if (d >= Max_UInt)
402 	return Max_UInt;
403     if (d > 0)
404 	return (UInt) d;
405     return 0;
406 }
407 
408 #ifdef NO_LEAKS
409 typedef struct _all_cells {
410     struct _all_cells *next;
411     char ptr;
412     CELL *cp;
413 } ALL_CELLS;
414 
415 static ALL_CELLS *all_cells;
416 /*
417  * Some regular expressions are parsed, and the pointer stored in the byte-code
418  * where we cannot distinguish it from other constants.  Keep a list here, to
419  * free on exit for auditing.
420  */
421 void
no_leaks_cell(CELL * cp)422 no_leaks_cell(CELL *cp)
423 {
424     ALL_CELLS *p = calloc(1, sizeof(ALL_CELLS));
425     p->next = all_cells;
426     p->cp = cp;
427     p->ptr = 0;
428     all_cells = p;
429 }
430 
431 void
no_leaks_cell_ptr(CELL * cp)432 no_leaks_cell_ptr(CELL *cp)
433 {
434     ALL_CELLS *p = calloc(1, sizeof(ALL_CELLS));
435     p->next = all_cells;
436     p->cp = cp;
437     p->ptr = 1;
438     all_cells = p;
439 }
440 
441 void
cell_leaks(void)442 cell_leaks(void)
443 {
444     while (all_cells != 0) {
445 	ALL_CELLS *next = all_cells->next;
446 	if (all_cells->ptr) {
447 	    zfree(all_cells->cp, sizeof(CELL));
448 	} else {
449 	    free_cell_data(all_cells->cp);
450 	}
451 	free(all_cells);
452 	all_cells = next;
453     }
454 }
455 #endif
456