| 1 | /* | 
| 2 |  * This Source Code Form is subject to the terms of the Mozilla Public | 
| 3 |  * License, v. 2.0.  If a copy of the MPL was not distributed with this | 
| 4 |  * file, You can obtain one at http://mozilla.org/MPL/2.0/. | 
| 5 |  * | 
| 6 |  * Copyright 1997 - July 2008 CWI, August 2008 - 2019 MonetDB B.V. | 
| 7 |  */ | 
| 8 |  | 
| 9 | /* | 
| 10 |  * author N.J. Nes, M.L. Kersten | 
| 11 |  * 01/07/1996, 31/01/2002 | 
| 12 |  * | 
| 13 |  * Input/Output module | 
| 14 |  * The IO module provides simple @sc{ascii-io} rendering options. | 
| 15 |  * It is modeled after the tuple formats, but does not | 
| 16 |  * attempt to outline the results. Instead, it is geared at speed, | 
| 17 |  * which also means that some functionality regarding the built-in | 
| 18 |  * types is duplicated from the atoms definitions. | 
| 19 |  * | 
| 20 |  * A functional limited form of formatted printf is also provided. | 
| 21 |  * It accepts at most one variable. | 
| 22 |  * A more complete approach is the tablet module. | 
| 23 |  * | 
| 24 |  * The commands to load and save a BAT from/to an ASCII dump | 
| 25 |  * are efficient, but work only for binary tables. | 
| 26 |  */ | 
| 27 |  | 
| 28 | /* | 
| 29 |  * Printing | 
| 30 |  * The print commands are implemented as single instruction rules, | 
| 31 |  * because they need access to the calling context. | 
| 32 |  * At a later stage we can look into the issues related to | 
| 33 |  * parsing the format string as part of the initialization phase. | 
| 34 |  * The old method in V4 essentially causes a lot of overhead | 
| 35 |  * because you have to prepare for the worst (e.g. mismatch format | 
| 36 |  * identifier and argument value) | 
| 37 |  * Beware, the types of the objects to be printed should be | 
| 38 |  * obtained from the stack, because the symbol table may actually | 
| 39 |  * allow for any type to be assigned. | 
| 40 |  */ | 
| 41 | #include "monetdb_config.h" | 
| 42 | #include "mal_io.h" | 
| 43 |  | 
| 44 | #define MAXFORMAT 64*1024 | 
| 45 |  | 
| 46 | str | 
| 47 | io_stdin(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci) | 
| 48 | { | 
| 49 | 	bstream **ret= (bstream**) getArgReference(stk,pci,0); | 
| 50 | 	(void) mb; | 
| 51 | 	*ret = cntxt->fdin; | 
| 52 | 	return MAL_SUCCEED; | 
| 53 | } | 
| 54 |  | 
| 55 | str | 
| 56 | io_stdout(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci) | 
| 57 | { | 
| 58 | 	stream **ret= (stream**) getArgReference(stk,pci,0); | 
| 59 | 	(void) mb; | 
| 60 | 	*ret = cntxt->fdout; | 
| 61 | 	return MAL_SUCCEED; | 
| 62 | } | 
| 63 |  | 
| 64 | static str | 
| 65 | IOprintBoth(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci, int indx, str hd, str tl, int nobat) | 
| 66 | { | 
| 67 | 	int tpe = getArgType(mb, pci, indx); | 
| 68 | 	ptr val = getArgReference(stk, pci, indx); | 
| 69 | 	stream *fp = cntxt->fdout; | 
| 70 |  | 
| 71 | 	(void) mb; | 
| 72 |  | 
| 73 | 	if (tpe == TYPE_any) | 
| 74 | 		tpe = stk->stk[pci->argv[indx]].vtype; | 
| 75 | 	if (val == NULL || tpe == TYPE_void) { | 
| 76 | 		if (hd) | 
| 77 | 			mnstr_printf(fp, "%s" , hd); | 
| 78 | 		mnstr_printf(fp, "nil" ); | 
| 79 | 		if (tl) | 
| 80 | 			mnstr_printf(fp, "%s" , tl); | 
| 81 | 		return MAL_SUCCEED; | 
| 82 | 	} | 
| 83 | 	if (isaBatType(tpe) ) { | 
| 84 | 		BAT *b[2]; | 
| 85 |  | 
| 86 | 		if (is_bat_nil(*(bat *) val)) { | 
| 87 | 			if (hd) | 
| 88 | 				mnstr_printf(fp, "%s" , hd); | 
| 89 | 			mnstr_printf(fp,"nil" ); | 
| 90 | 			if (tl) | 
| 91 | 				mnstr_printf(fp, "%s" , tl); | 
| 92 | 			return MAL_SUCCEED; | 
| 93 | 		} | 
| 94 | 		b[1] = BATdescriptor(*(bat *) val); | 
| 95 | 		if (b[1] == NULL) { | 
| 96 | 			throw(MAL, "io.print" , SQLSTATE(HY002) RUNTIME_OBJECT_MISSING); | 
| 97 | 		} | 
| 98 | 		if (nobat) { | 
| 99 | 			if (hd) | 
| 100 | 				mnstr_printf(fp, "%s" , hd); | 
| 101 | 			mnstr_printf(fp, "<%s>" , BBPname(b[1]->batCacheid)); | 
| 102 | 			if (tl) | 
| 103 | 				mnstr_printf(fp, "%s" , tl); | 
| 104 | 		} else { | 
| 105 | 			b[0] = BATdense(b[1]->hseqbase, b[1]->hseqbase, BATcount(b[1])); | 
| 106 | 			if (b[0] == NULL) { | 
| 107 | 				BBPunfix(b[1]->batCacheid); | 
| 108 | 				throw(MAL, "io.print" , SQLSTATE(HY001) MAL_MALLOC_FAIL); | 
| 109 | 			} | 
| 110 | 			if (BATroles(b[0], "h" ) != GDK_SUCCEED) { | 
| 111 | 				BBPunfix(b[0]->batCacheid); | 
| 112 | 				BBPunfix(b[1]->batCacheid); | 
| 113 | 				throw(MAL, "io.print" , SQLSTATE(HY001) MAL_MALLOC_FAIL); | 
| 114 | 			} | 
| 115 | 			BATprintcolumns(cntxt->fdout, 2, b); | 
| 116 | 			BBPunfix(b[0]->batCacheid); | 
| 117 | 		} | 
| 118 | 		BBPunfix(b[1]->batCacheid); | 
| 119 | 		return MAL_SUCCEED; | 
| 120 | 	} | 
| 121 | 	if (hd) | 
| 122 | 		mnstr_printf(fp, "%s" , hd); | 
| 123 |  | 
| 124 | 	if (ATOMvarsized(tpe)) | 
| 125 | 		ATOMprint(tpe, *(str *) val, fp); | 
| 126 | 	else | 
| 127 | 		ATOMprint(tpe, val, fp); | 
| 128 |  | 
| 129 | 	if (tl) | 
| 130 | 		mnstr_printf(fp, "%s" , tl); | 
| 131 | 	return MAL_SUCCEED; | 
| 132 | } | 
| 133 |  | 
| 134 | str | 
| 135 | IOprint_val(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr p) | 
| 136 | { | 
| 137 | 	int i; | 
| 138 | 	str msg; | 
| 139 |  | 
| 140 | 	(void) cntxt; | 
| 141 | 	if (p->argc == 2) | 
| 142 | 		msg = IOprintBoth(cntxt, mb, stk, p, 1, "[ " , " ]\n" , 0); | 
| 143 | 	else { | 
| 144 | 		msg = IOprintBoth(cntxt, mb, stk, p, 1, "[ " , 0, 1); | 
| 145 | 		if (msg) | 
| 146 | 			return msg; | 
| 147 | 		for (i = 2; i < p->argc - 1; i++) | 
| 148 | 			if ((msg = IOprintBoth(cntxt,mb, stk, p, i, ", " , 0, 1)) != NULL) | 
| 149 | 				return msg; | 
| 150 | 		msg = IOprintBoth(cntxt,mb, stk, p, i, ", " , "]\n" , 1); | 
| 151 | 	} | 
| 152 | 	return msg; | 
| 153 |  | 
| 154 | } | 
| 155 |  | 
| 156 | /* | 
| 157 |  * The IOprintf_() gets a format str, and a sequence of (ptr,int) parameters | 
| 158 |  * containing values and their type numbers. The printf() proved to be a | 
| 159 |  * great risk; people formatting badly their "%s" format strings were crashing | 
| 160 |  * the kernel. This function will prevent you from doing so. | 
| 161 |  * | 
| 162 |  * New implementation that repeatedly invokes sprintf => hacking the va_alist | 
| 163 |  * for using vfsprintf proved to be too compiler-dependent (OLD approach). | 
| 164 |  */ | 
| 165 | #define writemem(X1)													\ | 
| 166 | 	do {																\ | 
| 167 | 		if (dst+X1 > buf+size) {										\ | 
| 168 | 			ptrdiff_t offset = dst - buf;								\ | 
| 169 | 			char *tmp;													\ | 
| 170 | 			do {														\ | 
| 171 | 				size *= 2;												\ | 
| 172 | 			} while (dst+X1 > buf+size);								\ | 
| 173 | 			tmp = GDKrealloc(buf, size);								\ | 
| 174 | 			if (tmp == NULL) {											\ | 
| 175 | 				va_end(ap);												\ | 
| 176 | 				GDKfree(buf);											\ | 
| 177 | 				GDKfree(add);											\ | 
| 178 | 				throw(MAL, "io.printf", SQLSTATE(HY001) MAL_MALLOC_FAIL); \ | 
| 179 | 			}															\ | 
| 180 | 			buf = tmp;													\ | 
| 181 | 			dst = buf + offset;											\ | 
| 182 | 		}																\ | 
| 183 | 	} while (0) | 
| 184 |  | 
| 185 | #define m5sprintf(X1)\ | 
| 186 | 	if (width > adds) {\ | 
| 187 | 		str newadd;\ | 
| 188 | 		newadd = GDKrealloc(add, width + 10);\ | 
| 189 | 		if (newadd != NULL) {\ | 
| 190 | 			adds = width + 10;\ | 
| 191 | 			add = newadd;\ | 
| 192 | 		}\ | 
| 193 | 	}\ | 
| 194 | 	n = snprintf(add, adds, meta, X1);\ | 
| 195 | 	while (n < 0 || (size_t) n >= adds) {\ | 
| 196 | 		size_t newadds;\ | 
| 197 | 		str newadd;\ | 
| 198 | \ | 
| 199 | 		if (n >= 0)     /* glibc 2.1 */\ | 
| 200 | 			newadds = n + 1;   /* precisely what is needed */\ | 
| 201 | 		else            /* glibc 2.0 */\ | 
| 202 | 			newadds = n * 2;     /* twice the old size */\ | 
| 203 | \ | 
| 204 | 		newadd = GDKrealloc(add, newadds);\ | 
| 205 | 		if (newadd == NULL)\ | 
| 206 | 			break;\ | 
| 207 | \ | 
| 208 | 		adds = newadds;\ | 
| 209 | 		add = newadd;\ | 
| 210 | 		n = snprintf(add, adds, meta, X1);\ | 
| 211 | 	} | 
| 212 |  | 
| 213 |  | 
| 214 | static char toofew_error[80] = OPERATION_FAILED " At least %d parameter(s) expected.\n" ; | 
| 215 | static char format_error[80] = OPERATION_FAILED " Error in format before param %d.\n" ; | 
| 216 | static char type_error[80] = OPERATION_FAILED " Illegal type in param %d.\n" ; | 
| 217 |  | 
| 218 | #define return_error(x)							\ | 
| 219 | 	do {										\ | 
| 220 | 		GDKfree(buf);							\ | 
| 221 | 		GDKfree(add);							\ | 
| 222 | 		throw(MAL,"io.printf", x,argc);			\ | 
| 223 | 	} while (0) | 
| 224 |  | 
| 225 | static char niltext[4] = "nil" ; | 
| 226 |  | 
| 227 | static str | 
| 228 | IOprintf_(str *res, str format, ...) | 
| 229 | { | 
| 230 | 	va_list ap; | 
| 231 | 	int n; | 
| 232 |  | 
| 233 | 	int prec = 0, dotseen = 0, escaped = 0, type, size, argc = 1; | 
| 234 | 	size_t adds = 100, width = 0; | 
| 235 | 	char *add, *dst, *buf, *cur, *paramseen = NULL; | 
| 236 | 	char *p; | 
| 237 |  | 
| 238 | 	if (format == NULL) { | 
| 239 | 		throw(MAL,"io.printf" , ILLEGAL_ARGUMENT " NULL pointer passed as format.\n" ); | 
| 240 | 	} else if (strchr(format, '%') == NULL) { | 
| 241 | 		*res = GDKstrdup(format); | 
| 242 | 		if (*res == NULL) | 
| 243 | 			throw(MAL,"io.printf" , SQLSTATE(HY001) MAL_MALLOC_FAIL); | 
| 244 | 		return MAL_SUCCEED; | 
| 245 | 	} | 
| 246 | 	buf = dst = (str) GDKmalloc(size = 80); | 
| 247 | 	if ( buf == NULL) | 
| 248 | 		throw(MAL,"io.printf" , SQLSTATE(HY001) MAL_MALLOC_FAIL); | 
| 249 | 	*res = NULL; | 
| 250 |  | 
| 251 | 	add = GDKmalloc(adds); | 
| 252 | 	if (add == NULL) { | 
| 253 | 		GDKfree(buf); | 
| 254 | 		throw(MAL,"io.printf" , SQLSTATE(HY001) MAL_MALLOC_FAIL); | 
| 255 | 	} | 
| 256 |  | 
| 257 | 	va_start(ap,format); | 
| 258 | 	for (cur = format; *cur; cur++) { | 
| 259 | 		if (paramseen) { | 
| 260 | 			char meta[100]; | 
| 261 | 			ptrdiff_t  = 0; | 
| 262 | 			ptrdiff_t len; | 
| 263 |  | 
| 264 | 			if (GDKisdigit(*cur)) { | 
| 265 | 				if (dotseen) { | 
| 266 | 					prec = 10 * prec + (*cur - '0'); | 
| 267 | 				} else { | 
| 268 | 					width = 10 * width + (*cur - '0'); | 
| 269 | 				} | 
| 270 | 				continue; | 
| 271 | 			} else if (dotseen == 0 && *cur == '.') { | 
| 272 | 				dotseen = 1; | 
| 273 | 				continue; | 
| 274 | 			} else if (cur == paramseen + 1 && (*cur == '+' || *cur == '-' || *cur == ' ')) { | 
| 275 | 				continue; | 
| 276 | 			} else if (*cur == 'l') { | 
| 277 | 				cur++; | 
| 278 | 				if (*cur == 'l') { | 
| 279 | 					cur++; | 
| 280 | 					/* start of ll */ | 
| 281 | 					extra = (cur - paramseen) - 2; | 
| 282 | 				} | 
| 283 | 			} | 
| 284 | 			if ((p = va_arg(ap, char *)) == NULL) { | 
| 285 | 				va_end(ap); | 
| 286 | 				return_error(toofew_error); | 
| 287 | 			} | 
| 288 | 			type = va_arg(ap, int); | 
| 289 | 			type = ATOMbasetype(type); | 
| 290 |  | 
| 291 | 			len = 1 + (cur - paramseen); | 
| 292 | 			memcpy(meta, paramseen, len); | 
| 293 | 			meta[len] = 0; | 
| 294 | 			if (ATOMcmp(type, ATOMnilptr(type), p) == 0) { | 
| 295 | 				/* value is nil; attempt to print formatted 'nil' | 
| 296 | 				   without generating %ls etc. */ | 
| 297 | 				char *csrc, *ctrg = meta; | 
| 298 |  | 
| 299 | 				for (csrc = paramseen; csrc < cur; csrc++) { | 
| 300 | 					if (*csrc == '.') | 
| 301 | 						break; | 
| 302 | 					if (GDKisdigit(*csrc) || *csrc == '-') | 
| 303 | 						*(++ctrg) = *csrc; | 
| 304 | 				} | 
| 305 | 				*(++ctrg) = 's'; | 
| 306 | 				*(++ctrg) = 0; | 
| 307 | 				m5sprintf(niltext); | 
| 308 | 			} else if (strchr("cdiouxX" , *cur) && !extra) { | 
| 309 | 				int ival; | 
| 310 |  | 
| 311 | 				if (dotseen) { | 
| 312 | 					va_end(ap); | 
| 313 | 					return_error(format_error); | 
| 314 | 				} else if (type == TYPE_bte) { | 
| 315 | 					ival = (int) *(bte *) p; | 
| 316 | 				} else if (type == TYPE_sht) { | 
| 317 | 					ival = (int) *(sht *) p; | 
| 318 | 				} else if (type == TYPE_flt) { | 
| 319 | 					ival = (int) *(flt *) p; | 
| 320 | 				} else if (type == TYPE_lng) { | 
| 321 | 					goto largetypes; | 
| 322 | #ifdef HAVE_HGE | 
| 323 | 				} else if (type == TYPE_hge) { | 
| 324 | 					/* Does this happen? | 
| 325 | 					 * If so, what do we have TODO ? */ | 
| 326 | 					va_end(ap); | 
| 327 | 					return_error(type_error); | 
| 328 | #endif | 
| 329 | 				} else if (type == TYPE_int) { | 
| 330 | 					ival = *(int *) p; | 
| 331 | 				} else { | 
| 332 | 					va_end(ap); | 
| 333 | 					return_error(type_error); | 
| 334 | 				} | 
| 335 | 				m5sprintf(ival); | 
| 336 | 			} else if (strchr("diouxX" , *cur)) { | 
| 337 | #ifdef NATIVE_WIN32 | 
| 338 | 				ptrdiff_t i; | 
| 339 | #endif | 
| 340 | 				lng lval; | 
| 341 |  | 
| 342 | 				if (dotseen) { | 
| 343 | 					va_end(ap); | 
| 344 | 					return_error(format_error); | 
| 345 | 				} | 
| 346 | 			largetypes: | 
| 347 | 				if (type == TYPE_bte) { | 
| 348 | 					lval = (lng) *(bte *) p; | 
| 349 | 				} else if (type == TYPE_sht) { | 
| 350 | 					lval = (lng) *(sht *) p; | 
| 351 | 				} else if (type == TYPE_int) { | 
| 352 | 					lval = (lng) *(int *) p; | 
| 353 | 				} else if (type == TYPE_flt) { | 
| 354 | 					lval = (lng) *(flt *) p; | 
| 355 | 				} else if (type == TYPE_dbl) { | 
| 356 | 					lval = (lng) *(dbl *) p; | 
| 357 | 				} else if (type == TYPE_lng) { | 
| 358 | 					lval = *(lng *) p; | 
| 359 | #ifdef HAVE_HGE | 
| 360 | 				} else if (type == TYPE_hge) { | 
| 361 | 					/* Does this happen? | 
| 362 | 					 * If so, what do we have TODO ? */ | 
| 363 | 					va_end(ap); | 
| 364 | 					return_error(type_error); | 
| 365 | #endif | 
| 366 | 				} else { | 
| 367 | 					va_end(ap); | 
| 368 | 					return_error(type_error); | 
| 369 | 				} | 
| 370 | 				if (!extra) { | 
| 371 | 					meta[len + 2] = meta[len]; | 
| 372 | 					meta[len + 1] = meta[len - 1]; | 
| 373 | 					meta[len] = 'l'; | 
| 374 | 					meta[len - 1] = 'l'; | 
| 375 | 					len += 2; | 
| 376 | 					extra = len - 3; | 
| 377 | 				} | 
| 378 | #ifdef NATIVE_WIN32 | 
| 379 | 				for (i = len; i >= (extra + 2); i--) { | 
| 380 | 					meta[i + 1] = meta[i]; | 
| 381 | 				} | 
| 382 | 				meta[extra] = 'I'; | 
| 383 | 				meta[extra + 1] = '6'; | 
| 384 | 				meta[extra + 2] = '4'; | 
| 385 | #endif | 
| 386 | 				m5sprintf(lval); | 
| 387 | 			} else if (strchr("feEgG" , *cur)) { | 
| 388 | 				dbl dval; | 
| 389 |  | 
| 390 | 				if (type == TYPE_flt) { | 
| 391 | 					dval = (dbl) *(flt *) p; | 
| 392 | 				} else if (type == TYPE_dbl) { | 
| 393 | 					dval = *(dbl *) p; | 
| 394 | 				} else { | 
| 395 | 					va_end(ap); | 
| 396 | 					return_error(type_error); | 
| 397 | 				} | 
| 398 | 				width += (1 + prec); | 
| 399 | 				m5sprintf(dval); | 
| 400 | 			} else if (*cur == 's') { | 
| 401 | 				size_t length; | 
| 402 |  | 
| 403 | 				if (extra) { | 
| 404 | 					va_end(ap); | 
| 405 | 					return_error(format_error); | 
| 406 | 				} else if (type != TYPE_str) { | 
| 407 | 					va_end(ap); | 
| 408 | 					return_error(type_error); | 
| 409 | 				} | 
| 410 | 				length = strLen(p); | 
| 411 | 				width++; | 
| 412 | 				prec++;	/* account for '\0' */ | 
| 413 | 				if (dotseen && (size_t) prec < length) | 
| 414 | 					length = (size_t) prec; | 
| 415 | 				if (length > width) | 
| 416 | 					width = length; | 
| 417 | 				m5sprintf(p); | 
| 418 | 			} else { | 
| 419 | 				va_end(ap); | 
| 420 | 				return_error(format_error); | 
| 421 | 			} | 
| 422 | 			width = strlen(add); | 
| 423 | 			writemem(width); | 
| 424 | 			memcpy(dst, add, width); | 
| 425 | 			dst += width; | 
| 426 | 			paramseen = NULL; | 
| 427 | 			argc++; | 
| 428 | 		} else if (!escaped) { | 
| 429 | 			if (*cur == '\\' || (*cur == '%' && cur[1] == '%')) { | 
| 430 | 				escaped = 1; | 
| 431 | 			} else if (*cur == '%') { | 
| 432 | 				paramseen = cur; | 
| 433 | 				dotseen = prec = 0; | 
| 434 | 				width = 0; | 
| 435 | 			} else { | 
| 436 | 				writemem(1); | 
| 437 | 				*dst++ = *cur; | 
| 438 | 			} | 
| 439 | 		} else { | 
| 440 | 			escaped = 0; | 
| 441 | 			writemem(1); | 
| 442 | 			*dst++ = *cur; | 
| 443 | 		} | 
| 444 | 	} | 
| 445 |  | 
| 446 | /* | 
| 447 | 	if ( va_arg(ap, char *) != NULL){ | 
| 448 | 		GDKfree(buf);  | 
| 449 | 		throw(MAL,"io.printf", "params %d and beyond ignored %s.\n",argc); | 
| 450 | 	} | 
| 451 | */ | 
| 452 |  | 
| 453 | 	writemem(1); | 
| 454 | 	va_end(ap); | 
| 455 | 	*dst = 0; | 
| 456 | 	*res = buf; | 
| 457 | 	GDKfree(add); | 
| 458 | 	return MAL_SUCCEED; | 
| 459 | } | 
| 460 |  | 
| 461 | #define getArgValue(s,p,k) VALptr(&(s)->stk[(p)->argv[k]]) | 
| 462 |  | 
| 463 | #define G(X) getArgValue(stk,pci,X), getArgType(mb,pci,X) | 
| 464 |  | 
| 465 | str | 
| 466 | IOprintf(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci) | 
| 467 | { | 
| 468 | 	str *fmt = getArgReference_str(stk,pci,1); | 
| 469 | 	str fmt2 = NULL; | 
| 470 | 	str msg= MAL_SUCCEED; | 
| 471 |  | 
| 472 | 	(void) cntxt; | 
| 473 | 	(void) mb; | 
| 474 | 	switch( pci->argc){ | 
| 475 | 	case 2: msg= IOprintf_(&fmt2,*fmt); | 
| 476 | 			break; | 
| 477 | 	case 3: msg= IOprintf_(&fmt2,*fmt,G(2)); | 
| 478 | 		break; | 
| 479 | 	case 4: msg= IOprintf_(&fmt2,*fmt,G(2),G(3)); | 
| 480 | 		break; | 
| 481 | 	case 5: msg= IOprintf_(&fmt2,*fmt,G(2),G(3),G(4)); | 
| 482 | 		break; | 
| 483 | 	case 6: msg= IOprintf_(&fmt2,*fmt,G(2),G(3),G(4),G(5)); | 
| 484 | 		break; | 
| 485 | 	case 7: msg= IOprintf_(&fmt2,*fmt,G(2),G(3),G(4),G(5),G(6)); | 
| 486 | 		break; | 
| 487 | 	case 8: msg= IOprintf_(&fmt2,*fmt,G(2),G(3),G(4),G(5),G(6),G(7)); | 
| 488 | 		break; | 
| 489 | 	case 9: msg= IOprintf_(&fmt2,*fmt,G(2),G(3),G(4),G(5),G(6),G(7),G(8)); | 
| 490 | 		break; | 
| 491 | 	case 10: msg= IOprintf_(&fmt2,*fmt,G(2),G(3),G(4),G(5),G(6),G(7),G(8),G(9)); | 
| 492 | 	} | 
| 493 | 	if (msg== MAL_SUCCEED) { | 
| 494 | 		mnstr_printf(cntxt->fdout,"%s" ,fmt2); | 
| 495 | 		GDKfree(fmt2); | 
| 496 | 	} | 
| 497 | 	return msg; | 
| 498 | } | 
| 499 | str | 
| 500 | IOprintfStream(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci){ | 
| 501 | 	str *fmt = getArgReference_str(stk,pci,2); | 
| 502 | 	str fmt2 = NULL; | 
| 503 | 	stream *f= (stream *) getArgReference(stk,pci,1); | 
| 504 | 	str msg= MAL_SUCCEED; | 
| 505 |  | 
| 506 | 	(void) cntxt; | 
| 507 | 	(void) mb; | 
| 508 | 	switch( pci->argc){ | 
| 509 | 	case 3: msg= IOprintf_(&fmt2,*fmt); | 
| 510 | 		break; | 
| 511 | 	case 4: msg= IOprintf_(&fmt2,*fmt,G(3)); | 
| 512 | 		break; | 
| 513 | 	case 5: msg= IOprintf_(&fmt2,*fmt,G(3),G(4)); | 
| 514 | 		break; | 
| 515 | 	case 6: msg= IOprintf_(&fmt2,*fmt,G(3),G(4),G(5)); | 
| 516 | 		break; | 
| 517 | 	case 7: msg= IOprintf_(&fmt2,*fmt,G(3),G(4),G(5),G(6)); | 
| 518 | 		break; | 
| 519 | 	case 8: msg= IOprintf_(&fmt2,*fmt,G(3),G(4),G(5),G(6),G(7)); | 
| 520 | 		break; | 
| 521 | 	case 9: msg= IOprintf_(&fmt2,*fmt,G(3),G(4),G(5),G(6),G(7),G(8)); | 
| 522 | 		break; | 
| 523 | 	case 10: msg= IOprintf_(&fmt2,*fmt,G(3),G(4),G(5),G(6),G(7),G(8),G(9)); | 
| 524 | 		break; | 
| 525 | 	case 11: msg= IOprintf_(&fmt2,*fmt,G(3),G(4),G(5),G(6),G(7),G(8),G(9),G(10)); | 
| 526 | 	} | 
| 527 | 	if (msg== MAL_SUCCEED){ | 
| 528 | 		mnstr_printf(f,"%s" ,fmt2); | 
| 529 | 		GDKfree(fmt2); | 
| 530 | 	} | 
| 531 | 	return msg; | 
| 532 | } | 
| 533 |  | 
| 534 | /* | 
| 535 |  * The table printing routine implementations. | 
| 536 |  * They merely differ in destination and order prerequisite | 
| 537 |  */ | 
| 538 | str | 
| 539 | IOtable(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci) | 
| 540 | { | 
| 541 | 	BAT *piv[MAXPARAMS]; | 
| 542 | 	int i; | 
| 543 | 	int tpe; | 
| 544 | 	ptr val; | 
| 545 |  | 
| 546 | 	(void) cntxt; | 
| 547 | 	if ( pci->retc != 1 || pci->argc < 2) | 
| 548 | 		throw(MAL, "io.table" , "INTERNAL ERROR"  " assertion error  retc %d  argc %d" , pci->retc, pci->argc); | 
| 549 |  | 
| 550 | 	memset(piv, 0, sizeof(BAT*) * MAXPARAMS); | 
| 551 | 	for (i = 1; i < pci->argc; i++) { | 
| 552 | 		tpe = getArgType(mb, pci, i); | 
| 553 | 		val = getArgReference(stk, pci, i); | 
| 554 | 		if (!isaBatType(tpe)) { | 
| 555 | 			while (--i >= 1) | 
| 556 | 				if (piv[i] != NULL) | 
| 557 | 					BBPunfix(piv[i]->batCacheid); | 
| 558 | 			throw(MAL, "io.table" , ILLEGAL_ARGUMENT " BAT expected" ); | 
| 559 | 		} | 
| 560 | 		if ((piv[i] = BATdescriptor(*(bat *) val)) == NULL) { | 
| 561 | 			while (--i >= 1) | 
| 562 | 				BBPunfix(piv[i]->batCacheid); | 
| 563 | 			throw(MAL, "io.table" , ILLEGAL_ARGUMENT " null BAT encountered" ); | 
| 564 | 		} | 
| 565 | 	} | 
| 566 | 	/* add materialized void column */ | 
| 567 | 	piv[0] = BATdense(piv[1]->hseqbase, 0, BATcount(piv[1])); | 
| 568 | 	if (piv[0] == NULL) { | 
| 569 | 		for (i = 1; i < pci->argc; i++) | 
| 570 | 			BBPunfix(piv[i]->batCacheid); | 
| 571 | 		throw(MAL, "io.table" , SQLSTATE(HY001) MAL_MALLOC_FAIL); | 
| 572 | 	} | 
| 573 | 	BATprintcolumns(cntxt->fdout, pci->argc, piv); | 
| 574 | 	for (i = 0; i < pci->argc; i++) | 
| 575 | 		BBPunfix(piv[i]->batCacheid); | 
| 576 | 	return MAL_SUCCEED; | 
| 577 | } | 
| 578 |  | 
| 579 |  | 
| 580 | /* | 
| 581 |  * Bulk export/loading | 
| 582 |  * To simplify conversion between versions and to interface with other | 
| 583 |  * applications, we use a simple import/export operation. | 
| 584 |  * | 
| 585 |  * The conversion routine assumes space in the buffer for storing the result. | 
| 586 |  */ | 
| 587 | /* | 
| 588 |  * A BAT can be saved in Monet format using the export command. | 
| 589 |  * It is of particular use in preparing an ASCII version for migration. | 
| 590 |  * The exported file is saved in the context of the directory | 
| 591 |  * where the server was started unless an absolute file name was | 
| 592 |  * presented. | 
| 593 |  */ | 
| 594 |  | 
| 595 | str | 
| 596 | IOexport(void *ret, bat *bid, str *fnme) | 
| 597 | { | 
| 598 | 	BAT *b; | 
| 599 | 	stream *s; | 
| 600 |  | 
| 601 | 	(void) ret; | 
| 602 | 	if ((b = BATdescriptor(*bid)) == NULL)  | 
| 603 | 		throw(MAL, "io.export" , SQLSTATE(HY002) RUNTIME_OBJECT_MISSING); | 
| 604 | 	 | 
| 605 | 	s = open_wastream(*fnme); | 
| 606 | 	if (s == NULL ){ | 
| 607 | 		BBPunfix(b->batCacheid); | 
| 608 | 		throw(MAL, "io.export" , RUNTIME_FILE_NOT_FOUND ":%s" , *fnme); | 
| 609 | 	} | 
| 610 | 	if (mnstr_errnr(s)) { | 
| 611 | 		mnstr_close(s); | 
| 612 | 		BBPunfix(b->batCacheid); | 
| 613 | 		throw(MAL, "io.export" , RUNTIME_FILE_NOT_FOUND ":%s" , *fnme); | 
| 614 | 	} | 
| 615 |     BATprintcolumns(s, 1, &b); | 
| 616 | 	close_stream(s); | 
| 617 | 	BBPunfix(b->batCacheid); | 
| 618 | 	return MAL_SUCCEED; | 
| 619 | } | 
| 620 |  | 
| 621 | /* | 
| 622 |  * The import command reads a single BAT from an ASCII file produced by export. | 
| 623 |  */ | 
| 624 | str | 
| 625 | IOimport(void *ret, bat *bid, str *fnme) | 
| 626 | { | 
| 627 | 	BAT *b; | 
| 628 | 	ssize_t (*tconvert) (const char *, size_t *, ptr *, bool); | 
| 629 | 	ssize_t n; | 
| 630 | 	size_t bufsize = 2048;	/* NIELS:tmp change used to be 1024 */ | 
| 631 | 	char *base, *cur, *end; | 
| 632 | 	char *buf; | 
| 633 | 	ptr t = 0; | 
| 634 | 	size_t lt = 0; | 
| 635 | 	FILE *fp = fopen(*fnme, "r" ); | 
| 636 | 	char msg[BUFSIZ]; | 
| 637 |  | 
| 638 | 	(void) ret; | 
| 639 | 	if ((b = BATdescriptor(*bid)) == NULL) { | 
| 640 | 		if (fp) | 
| 641 | 			fclose(fp); | 
| 642 | 		throw(MAL, "io.import" , SQLSTATE(HY002) RUNTIME_OBJECT_MISSING); | 
| 643 | 	} | 
| 644 |  | 
| 645 | 	tconvert = BATatoms[BATttype(b)].atomFromStr; | 
| 646 | 	/* | 
| 647 | 	 * Open the file. Memory map it to minimize buffering problems. | 
| 648 | 	 */ | 
| 649 | 	if (fp == NULL) { | 
| 650 | 		BBPunfix(b->batCacheid); | 
| 651 | 		throw(MAL, "io.import" , RUNTIME_FILE_NOT_FOUND ":%s" , *fnme); | 
| 652 | 	} else { | 
| 653 | 		int fn; | 
| 654 | 		struct stat st; | 
| 655 |  | 
| 656 | 		buf = (char *) GDKmalloc(bufsize); | 
| 657 | 		if ( buf == NULL) { | 
| 658 | 			BBPunfix(b->batCacheid); | 
| 659 | 			fclose(fp); | 
| 660 | 			throw(MAL,"io.import" , SQLSTATE(HY001) MAL_MALLOC_FAIL); | 
| 661 | 		} | 
| 662 |  | 
| 663 | 		if ((fn = fileno(fp)) <= 0) { | 
| 664 | 			BBPunfix(b->batCacheid); | 
| 665 | 			fclose(fp); | 
| 666 | 			GDKfree(buf); | 
| 667 | 			throw(MAL, "io.import" , OPERATION_FAILED " fileno()" ); | 
| 668 | 		} | 
| 669 | 		if (fstat(fn, &st) != 0) { | 
| 670 | 			BBPunfix(b->batCacheid); | 
| 671 | 			fclose(fp); | 
| 672 | 			GDKfree(buf); | 
| 673 | 			throw(MAL, "io.imports" , OPERATION_FAILED "fstat()" ); | 
| 674 | 		} | 
| 675 |  | 
| 676 | 		(void) fclose(fp); | 
| 677 | 		if (st.st_size <= 0) { | 
| 678 | 			BBPunfix(b->batCacheid); | 
| 679 | 			GDKfree(buf); | 
| 680 | 			throw(MAL, "io.imports" , OPERATION_FAILED "Empty file" ); | 
| 681 | 		} | 
| 682 | #if SIZEOF_SIZE_T == SIZEOF_INT | 
| 683 | 		if (st.st_size > ~ (size_t) 0) { | 
| 684 | 			BBPunfix(b->batCacheid); | 
| 685 | 			GDKfree(buf); | 
| 686 | 			throw(MAL, "io.imports" , OPERATION_FAILED "File too large" ); | 
| 687 | 		} | 
| 688 | #endif | 
| 689 | 		base = cur = (char *) MT_mmap(*fnme, MMAP_SEQUENTIAL, (size_t) st.st_size); | 
| 690 | 		if (cur == NULL) { | 
| 691 | 			BBPunfix(b->batCacheid); | 
| 692 | 			GDKfree(buf); | 
| 693 | 			throw(MAL, "io.mport" , OPERATION_FAILED "MT_mmap()" ); | 
| 694 | 		} | 
| 695 | 		end = cur + st.st_size; | 
| 696 |  | 
| 697 | 	} | 
| 698 | 	/* Parse a line. Copy it into a buffer. Concat broken lines with a slash.  */ | 
| 699 | 	while (cur < end) { | 
| 700 | 		str dst = buf, src = cur, p; | 
| 701 | 		size_t l; | 
| 702 |  | 
| 703 | 		/* like p = strchr(cur, '\n') but with extra bounds check */ | 
| 704 | 		for (p = cur; p < end && *p != '\n'; p++) | 
| 705 | 			; | 
| 706 | 		l = p - cur; | 
| 707 |  | 
| 708 | 		if (p < end) { | 
| 709 | 			while (src[l - 1] == '\\') { | 
| 710 | 				if (buf+bufsize < dst+l) { | 
| 711 | 					size_t len = dst - buf; | 
| 712 | 					size_t inc = (size_t) ((dst+l) - buf); | 
| 713 | 					char *tmp = GDKrealloc(buf, bufsize = MAX(inc,bufsize)*2); | 
| 714 | 					if (tmp == NULL) { | 
| 715 | 						BBPunfix(b->batCacheid); | 
| 716 | 						GDKfree(buf); | 
| 717 | 						GDKfree(t); | 
| 718 | 						throw(MAL, "io.imports" , SQLSTATE(HY001) MAL_MALLOC_FAIL); | 
| 719 | 					} | 
| 720 | 					buf = tmp; | 
| 721 | 					dst = buf + len; | 
| 722 | 				} | 
| 723 | 				memcpy(dst, src, l-1); | 
| 724 | 				dst += l - 1; | 
| 725 | 				src += l + 1; | 
| 726 | 				for (p = src; p < end && *p != '\n'; p++) | 
| 727 | 					; | 
| 728 | 				if (p == end) | 
| 729 | 					break; | 
| 730 | 				l = p - src; | 
| 731 | 			} | 
| 732 | 		} | 
| 733 |  | 
| 734 | 		if (buf+bufsize < dst+l) { | 
| 735 | 			size_t len = dst - buf; | 
| 736 | 			size_t inc = (size_t) ((dst+l) - buf); | 
| 737 | 			char *tmp = GDKrealloc(buf, bufsize = MAX(inc,bufsize)*2); | 
| 738 | 			if (tmp == NULL) { | 
| 739 | 				BBPunfix(b->batCacheid); | 
| 740 | 				GDKfree(buf); | 
| 741 | 				GDKfree(t); | 
| 742 | 				throw(MAL, "io.imports" , SQLSTATE(HY001) MAL_MALLOC_FAIL); | 
| 743 | 			} | 
| 744 | 			buf = tmp; | 
| 745 | 			dst = buf + len; | 
| 746 | 		} | 
| 747 | 		memcpy(dst, src, l); | 
| 748 | 		dst[l] = 0; | 
| 749 | 		cur = p + 1; | 
| 750 | 		/* Parse the line, and insert a BUN.  */ | 
| 751 | 		for (p = buf; *p && GDKisspace(*p); p++) | 
| 752 | 			; | 
| 753 | 		if (*p == '#') | 
| 754 | 			continue; | 
| 755 |  | 
| 756 | 		for (;*p && *p != '['; p++) | 
| 757 | 			; | 
| 758 | 		if (*p) | 
| 759 | 			for (p++; *p && GDKisspace(*p); p++) | 
| 760 | 				; | 
| 761 | 		if (*p == 0) { | 
| 762 | 			BBPunfix(b->batCacheid); | 
| 763 | 			snprintf(msg,sizeof(msg),"error in input %s" ,buf); | 
| 764 | 			GDKfree(buf); | 
| 765 | 			MT_munmap(base, end - base); | 
| 766 | 			GDKfree(t); | 
| 767 | 			throw(MAL, "io.import" , "%s" , msg); | 
| 768 | 		} | 
| 769 | 		n = tconvert(p, <, (ptr*)&t, true); | 
| 770 | 		if (n < 0) { | 
| 771 | 			BBPunfix(b->batCacheid); | 
| 772 | 			snprintf(msg,sizeof(msg),"error in input %s" ,buf); | 
| 773 | 			GDKfree(buf); | 
| 774 | 			MT_munmap(base, end - base); | 
| 775 | 			GDKfree(t); | 
| 776 | 			throw(MAL, "io.import" , "%s" , msg); | 
| 777 | 		} | 
| 778 | 		p += n; | 
| 779 | 		if (BUNappend(b, t, false) != GDK_SUCCEED) { | 
| 780 | 			BBPunfix(b->batCacheid); | 
| 781 | 			GDKfree(buf); | 
| 782 | 			GDKfree(t); | 
| 783 | 			MT_munmap(base, end - base); | 
| 784 | 			throw(MAL, "io.import" , "insert failed" ); | 
| 785 | 		} | 
| 786 |  | 
| 787 | #if 0							/* why do this? any measured effects? */ | 
| 788 | /* | 
| 789 |  * Unmap already parsed memory, to keep the memory usage low. | 
| 790 |  */ | 
| 791 | #ifndef WIN32 | 
| 792 | #define MAXBUF 40*MT_pagesize() | 
| 793 | 		if ((unsigned) (cur - base) > MAXBUF) { | 
| 794 | 			MT_munmap(base, MAXBUF); | 
| 795 | 			base += MAXBUF; | 
| 796 | 		} | 
| 797 | #endif | 
| 798 | #endif | 
| 799 | 	} | 
| 800 | 	/* Cleanup and exit. Return the filled BAT.  */ | 
| 801 | 	if (t) | 
| 802 | 		GDKfree(t); | 
| 803 | 	GDKfree(buf); | 
| 804 | 	MT_munmap(base, end - base); | 
| 805 | 	BBPunfix(b->batCacheid); | 
| 806 | 	return MAL_SUCCEED; | 
| 807 | } | 
| 808 |  | 
| 809 |  | 
| 810 |  | 
| 811 | str | 
| 812 | IOsetmallocsuccesscount(void *res, lng *count) { | 
| 813 | 	(void) res; | 
| 814 | 	GDKsetmallocsuccesscount(*count); | 
| 815 | 	return MAL_SUCCEED; | 
| 816 | } | 
| 817 |  |