1 /*
2     Qalculate
3 
4     Copyright (C) 2003-2007, 2008, 2016-2019  Hanna Knutsson (hanna.knutsson@protonmail.com)
5 
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10 */
11 
12 #include "support.h"
13 
14 #include "Calculator.h"
15 #include "util.h"
16 #include "MathStructure.h"
17 
18 #include <locale.h>
19 #include <unistd.h>
20 #include <time.h>
21 #include <utime.h>
22 #include <errno.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <dirent.h>
26 #include <vector>
27 
28 using std::string;
29 using std::vector;
30 using std::cout;
31 using std::endl;
32 
PlotParameters()33 PlotParameters::PlotParameters() {
34 	auto_y_min = true;
35 	auto_x_min = true;
36 	auto_y_max = true;
37 	auto_x_max = true;
38 	y_log = false;
39 	x_log = false;
40 	y_log_base = 10;
41 	x_log_base = 10;
42 	grid = false;
43 	color = true;
44 	linewidth = -1;
45 	show_all_borders = false;
46 	legend_placement = PLOT_LEGEND_TOP_RIGHT;
47 }
PlotDataParameters()48 PlotDataParameters::PlotDataParameters() {
49 	yaxis2 = false;
50 	xaxis2 = false;
51 	style = PLOT_STYLE_LINES;
52 	smoothing = PLOT_SMOOTHING_NONE;
53 	test_continuous = false;
54 }
55 
canPlot()56 bool Calculator::canPlot() {
57 #ifdef HAVE_GNUPLOT_CALL
58 #	ifdef _WIN32
59 	LPSTR lpFilePart;
60 	char filename[MAX_PATH];
61 	return SearchPath(NULL, "gnuplot", ".exe", MAX_PATH, filename, &lpFilePart);
62 #	else
63 	FILE *pipe = popen("gnuplot - 2>/dev/null", "w");
64 	if(!pipe) return false;
65 	return pclose(pipe) == 0;
66 #	endif
67 #else
68 	return false;
69 #endif
70 }
71 
parse_and_precalculate_plot(string & expression,MathStructure & mstruct,const ParseOptions & po,EvaluationOptions & eo)72 void parse_and_precalculate_plot(string &expression, MathStructure &mstruct, const ParseOptions &po, EvaluationOptions &eo) {
73 	eo.approximation = APPROXIMATION_APPROXIMATE;
74 	ParseOptions po2 = po;
75 	po2.read_precision = DONT_READ_PRECISION;
76 	eo.parse_options = po2;
77 	eo.interval_calculation = INTERVAL_CALCULATION_NONE;
78 	mstruct = CALCULATOR->parse(expression, po2);
79 	MathStructure mbak(mstruct);
80 	eo.calculate_functions = false;
81 	eo.expand = false;
82 	CALCULATOR->beginTemporaryStopMessages();
83 	mstruct.eval(eo);
84 	int im = 0;
85 	if(CALCULATOR->endTemporaryStopMessages(NULL, &im) > 0 || im > 0) mstruct = mbak;
86 	eo.calculate_functions = true;
87 	eo.expand = true;
88 }
89 
expressionToPlotVector(string expression,const MathStructure & min,const MathStructure & max,int steps,MathStructure * x_vector,string x_var,const ParseOptions & po,int msecs)90 MathStructure Calculator::expressionToPlotVector(string expression, const MathStructure &min, const MathStructure &max, int steps, MathStructure *x_vector, string x_var, const ParseOptions &po, int msecs) {
91 	Variable *v = getActiveVariable(x_var);
92 	MathStructure x_mstruct;
93 	if(v) x_mstruct = v;
94 	else x_mstruct = x_var;
95 	EvaluationOptions eo;
96 	MathStructure mparse;
97 	if(msecs > 0) startControl(msecs);
98 	beginTemporaryStopIntervalArithmetic();
99 	parse_and_precalculate_plot(expression, mparse, po, eo);
100 	beginTemporaryStopMessages();
101 	MathStructure y_vector(mparse.generateVector(x_mstruct, min, max, steps, x_vector, eo));
102 	endTemporaryStopMessages();
103 	endTemporaryStopIntervalArithmetic();
104 	if(msecs > 0) {
105 		if(aborted()) error(true, _("It took too long to generate the plot data."), NULL);
106 		stopControl();
107 	}
108 	if(y_vector.size() == 0) {
109 		error(true, _("Unable to generate plot data with current min, max and sampling rate."), NULL);
110 	}
111 	return y_vector;
112 }
expressionToPlotVector(string expression,float min,float max,int steps,MathStructure * x_vector,string x_var,const ParseOptions & po,int msecs)113 MathStructure Calculator::expressionToPlotVector(string expression, float min, float max, int steps, MathStructure *x_vector, string x_var, const ParseOptions &po, int msecs) {
114 	MathStructure min_mstruct(min), max_mstruct(max);
115 	ParseOptions po2 = po;
116 	po2.read_precision = DONT_READ_PRECISION;
117 	MathStructure y_vector(expressionToPlotVector(expression, min_mstruct, max_mstruct, steps, x_vector, x_var, po2, msecs));
118 	return y_vector;
119 }
expressionToPlotVector(string expression,const MathStructure & min,const MathStructure & max,const MathStructure & step,MathStructure * x_vector,string x_var,const ParseOptions & po,int msecs)120 MathStructure Calculator::expressionToPlotVector(string expression, const MathStructure &min, const MathStructure &max, const MathStructure &step, MathStructure *x_vector, string x_var, const ParseOptions &po, int msecs) {
121 	Variable *v = getActiveVariable(x_var);
122 	MathStructure x_mstruct;
123 	if(v) x_mstruct = v;
124 	else x_mstruct = x_var;
125 	EvaluationOptions eo;
126 	MathStructure mparse;
127 	if(msecs > 0) startControl(msecs);
128 	beginTemporaryStopIntervalArithmetic();
129 	parse_and_precalculate_plot(expression, mparse, po, eo);
130 	beginTemporaryStopMessages();
131 	MathStructure y_vector(mparse.generateVector(x_mstruct, min, max, step, x_vector, eo));
132 	endTemporaryStopMessages();
133 	endTemporaryStopIntervalArithmetic();
134 	if(msecs > 0) {
135 		if(aborted()) error(true, _("It took too long to generate the plot data."), NULL);
136 		stopControl();
137 	}
138 	if(y_vector.size() == 0) {
139 		error(true, _("Unable to generate plot data with current min, max and step size."), NULL);
140 	}
141 	return y_vector;
142 }
expressionToPlotVector(string expression,float min,float max,float step,MathStructure * x_vector,string x_var,const ParseOptions & po,int msecs)143 MathStructure Calculator::expressionToPlotVector(string expression, float min, float max, float step, MathStructure *x_vector, string x_var, const ParseOptions &po, int msecs) {
144 	MathStructure min_mstruct(min), max_mstruct(max), step_mstruct(step);
145 	ParseOptions po2 = po;
146 	po2.read_precision = DONT_READ_PRECISION;
147 	MathStructure y_vector(expressionToPlotVector(expression, min_mstruct, max_mstruct, step_mstruct, x_vector, x_var, po2, msecs));
148 	return y_vector;
149 }
expressionToPlotVector(string expression,const MathStructure & x_vector,string x_var,const ParseOptions & po,int msecs)150 MathStructure Calculator::expressionToPlotVector(string expression, const MathStructure &x_vector, string x_var, const ParseOptions &po, int msecs) {
151 	Variable *v = getActiveVariable(x_var);
152 	MathStructure x_mstruct;
153 	if(v) x_mstruct = v;
154 	else x_mstruct = x_var;
155 	EvaluationOptions eo;
156 	MathStructure mparse;
157 	if(msecs > 0) startControl(msecs);
158 	beginTemporaryStopIntervalArithmetic();
159 	parse_and_precalculate_plot(expression, mparse, po, eo);
160 	beginTemporaryStopMessages();
161 	MathStructure y_vector(mparse.generateVector(x_mstruct, x_vector, eo).eval(eo));
162 	endTemporaryStopMessages();
163 	endTemporaryStopIntervalArithmetic();
164 	if(msecs > 0) {
165 		if(aborted()) error(true, _("It took too long to generate the plot data."), NULL);
166 		stopControl();
167 	}
168 	return y_vector;
169 }
170 
plotVectors(PlotParameters * param,const vector<MathStructure> & y_vectors,const vector<MathStructure> & x_vectors,vector<PlotDataParameters * > & pdps,bool persistent,int msecs)171 bool Calculator::plotVectors(PlotParameters *param, const vector<MathStructure> &y_vectors, const vector<MathStructure> &x_vectors, vector<PlotDataParameters*> &pdps, bool persistent, int msecs) {
172 
173 	string homedir = getLocalTmpDir();
174 	recursiveMakeDir(homedir);
175 
176 	string commandline_extra;
177 	string title;
178 
179 	if(!param) {
180 		PlotParameters pp;
181 		param = &pp;
182 	}
183 
184 	string plot;
185 
186 	if(param->filename.empty()) {
187 		if(!param->color) {
188 			commandline_extra += " -mono";
189 		}
190 		plot += "set terminal pop\n";
191 	} else {
192 		persistent = true;
193 		if(param->filetype == PLOT_FILETYPE_AUTO) {
194 			size_t i = param->filename.rfind(".");
195 			if(i == string::npos) {
196 				param->filetype = PLOT_FILETYPE_PNG;
197 				error(false, _("No extension in file name. Saving as PNG image."), NULL);
198 			} else {
199 				string ext = param->filename.substr(i + 1, param->filename.length() - (i + 1));
200 				if(ext == "png") {
201 					param->filetype = PLOT_FILETYPE_PNG;
202 				} else if(ext == "ps") {
203 					param->filetype = PLOT_FILETYPE_PS;
204 				} else if(ext == "pdf") {
205 					param->filetype = PLOT_FILETYPE_PDF;
206 				} else if(ext == "eps") {
207 					param->filetype = PLOT_FILETYPE_EPS;
208 				} else if(ext == "svg") {
209 					param->filetype = PLOT_FILETYPE_SVG;
210 				} else if(ext == "fig") {
211 					param->filetype = PLOT_FILETYPE_FIG;
212 				} else if(ext == "tex") {
213 					param->filetype = PLOT_FILETYPE_LATEX;
214 				} else {
215 					param->filetype = PLOT_FILETYPE_PNG;
216 					error(false, _("Unknown extension in file name. Saving as PNG image."), NULL);
217 				}
218 			}
219 		}
220 		plot += "set terminal ";
221 		switch(param->filetype) {
222 			case PLOT_FILETYPE_FIG: {
223 				plot += "fig ";
224 				if(param->color) {
225 					plot += "color";
226 				} else {
227 					plot += "monochrome";
228 				}
229 				break;
230 			}
231 			case PLOT_FILETYPE_SVG: {
232 				plot += "svg";
233 				break;
234 			}
235 			case PLOT_FILETYPE_LATEX: {
236 				plot += "latex ";
237 				break;
238 			}
239 			case PLOT_FILETYPE_PS: {
240 				plot += "postscript ";
241 				if(param->color) {
242 					plot += "color";
243 				} else {
244 					plot += "monochrome";
245 				}
246 				plot += " \"Times\"";
247 				break;
248 			}
249 			case PLOT_FILETYPE_PDF: {
250 				plot += "pdf ";
251 				if(param->color) {
252 					plot += "color";
253 				} else {
254 					plot += "monochrome";
255 				}
256 				break;
257 			}
258 			case PLOT_FILETYPE_EPS: {
259 				plot += "postscript eps ";
260 				if(param->color) {
261 					plot += "color";
262 				} else {
263 					plot += "monochrome";
264 				}
265 				plot += " \"Times\"";
266 				break;
267 			}
268 			default: {
269 				plot += "png ";
270 				break;
271 			}
272 
273 		}
274 		plot += "\nset output \"";
275 		plot += param->filename;
276 		plot += "\"\n";
277 	}
278 
279 	plot += "set termoption noenhanced\n";
280 
281 	switch(param->legend_placement) {
282 		case PLOT_LEGEND_NONE: {plot += "set nokey\n"; break;}
283 		case PLOT_LEGEND_TOP_LEFT: {plot += "set key top left\n"; break;}
284 		case PLOT_LEGEND_TOP_RIGHT: {plot += "set key top right\n"; break;}
285 		case PLOT_LEGEND_BOTTOM_LEFT: {plot += "set key bottom left\n"; break;}
286 		case PLOT_LEGEND_BOTTOM_RIGHT: {plot += "set key bottom right\n"; break;}
287 		case PLOT_LEGEND_BELOW: {plot += "set key below\n"; break;}
288 		case PLOT_LEGEND_OUTSIDE: {plot += "set key outside\n"; break;}
289 	}
290 	if(!param->x_label.empty()) {
291 		title = param->x_label;
292 		gsub("\"", "\\\"", title);
293 		plot += "set xlabel \"";
294 		plot += title;
295 		plot += "\"\n";
296 	}
297 	if(!param->y_label.empty()) {
298 		string title = param->y_label;
299 		gsub("\"", "\\\"", title);
300 		plot += "set ylabel \"";
301 		plot += title;
302 		plot += "\"\n";
303 	}
304 	if(!param->title.empty()) {
305 		title = param->title;
306 		gsub("\"", "\\\"", title);
307 		plot += "set title \"";
308 		plot += title;
309 		plot += "\"\n";
310 	}
311 	if(param->grid) {
312 		plot += "set grid\n";
313 
314 	}
315 	if(!param->auto_y_min || !param->auto_y_max) {
316 		plot += "set yrange [";
317 		if(!param->auto_y_min) plot += d2s(param->y_min);
318 		plot += ":";
319 		if(!param->auto_y_max) plot += d2s(param->y_max);
320 		plot += "]";
321 		plot += "\n";
322 	}
323 	if(param->x_log) {
324 		plot += "set logscale x ";
325 		plot += i2s(param->x_log_base);
326 		plot += "\n";
327 	}
328 	if(param->y_log) {
329 		plot += "set logscale y ";
330 		plot += i2s(param->y_log_base);
331 		plot += "\n";
332 	}
333 	if(param->show_all_borders) {
334 		plot += "set border 15\n";
335 	} else {
336 		bool xaxis2 = false, yaxis2 = false;
337 		for(size_t i = 0; i < pdps.size(); i++) {
338 			if(pdps[i] && pdps[i]->xaxis2) {
339 				xaxis2 = true;
340 			}
341 			if(pdps[i] && pdps[i]->yaxis2) {
342 				yaxis2 = true;
343 			}
344 		}
345 		if(xaxis2 && yaxis2) {
346 			plot += "set border 15\nset x2tics\nset y2tics\n";
347 		} else if(xaxis2) {
348 			plot += "set border 7\nset x2tics\n";
349 		} else if(yaxis2) {
350 			plot += "set border 11\nset y2tics\n";
351 		} else {
352 			plot += "set border 3\n";
353 		}
354 		plot += "set xtics nomirror\nset ytics nomirror\n";
355 	}
356 	size_t samples = 1000;
357 	for(size_t i = 0; i < y_vectors.size(); i++) {
358 		if(!y_vectors[i].isUndefined()) {
359 			if(y_vectors[i].size() > 3000) {
360 				samples = 6000;
361 				break;
362 			}
363 			if(y_vectors[i].size() * 2 > samples) samples = y_vectors[i].size() * 2;
364 		}
365 	}
366 	plot += "set samples ";
367 	plot += i2s(samples);
368 	plot += "\n";
369 	plot += "plot ";
370 	for(size_t i = 0; i < y_vectors.size(); i++) {
371 		if(!y_vectors[i].isUndefined()) {
372 			if(i != 0) {
373 				plot += ",";
374 			}
375 			string filename = "gnuplot_data";
376 			filename += i2s(i + 1);
377 			filename = buildPath(homedir, filename);
378 #ifdef _WIN32
379 			gsub("\\", "\\\\", filename);
380 #endif
381 			plot += "\"";
382 			plot += filename;
383 			plot += "\"";
384 			if(i < pdps.size()) {
385 				switch(pdps[i]->smoothing) {
386 					case PLOT_SMOOTHING_UNIQUE: {plot += " smooth unique"; break;}
387 					case PLOT_SMOOTHING_CSPLINES: {plot += " smooth csplines"; break;}
388 					case PLOT_SMOOTHING_BEZIER: {plot += " smooth bezier"; break;}
389 					case PLOT_SMOOTHING_SBEZIER: {plot += " smooth sbezier"; break;}
390 					default: {}
391 				}
392 				if(pdps[i]->xaxis2 && pdps[i]->yaxis2) {
393 					plot += " axis x2y2";
394 				} else if(pdps[i]->xaxis2) {
395 					plot += " axis x2y1";
396 				} else if(pdps[i]->yaxis2) {
397 					plot += " axis x1y2";
398 				}
399 				if(!pdps[i]->title.empty()) {
400 					title = pdps[i]->title;
401 					gsub("\"", "\\\"", title);
402 					plot += " title \"";
403 					plot += title;
404 					plot += "\"";
405 				}
406 				switch(pdps[i]->style) {
407 					case PLOT_STYLE_LINES: {plot += " with lines"; break;}
408 					case PLOT_STYLE_POINTS: {plot += " with points"; break;}
409 					case PLOT_STYLE_POINTS_LINES: {plot += " with linespoints"; break;}
410 					case PLOT_STYLE_BOXES: {plot += " with boxes"; break;}
411 					case PLOT_STYLE_HISTOGRAM: {plot += " with histeps"; break;}
412 					case PLOT_STYLE_STEPS: {plot += " with steps"; break;}
413 					case PLOT_STYLE_CANDLESTICKS: {plot += " with candlesticks"; break;}
414 					case PLOT_STYLE_DOTS: {plot += " with dots"; break;}
415 				}
416 				if(param->linewidth < 1) {
417 					plot += " lw 2";
418 				} else {
419 					plot += " lw ";
420 					plot += i2s(param->linewidth);
421 				}
422 			}
423 		}
424 	}
425 	plot += "\n";
426 
427 	string plot_data;
428 	PrintOptions po;
429 	po.number_fraction_format = FRACTION_DECIMAL;
430 	po.interval_display = INTERVAL_DISPLAY_MIDPOINT;
431 	po.decimalpoint_sign = ".";
432 	po.comma_sign = ",";
433 	for(size_t serie = 0; serie < y_vectors.size(); serie++) {
434 		if(!y_vectors[serie].isUndefined()) {
435 			string filename = "gnuplot_data";
436 			filename += i2s(serie + 1);
437 			string filepath = buildPath(homedir, filename);
438 			FILE *fdata = fopen(filepath.c_str(), "w+");
439 			if(!fdata) {
440 				error(true, _("Could not create temporary file %s"), filepath.c_str(), NULL);
441 				return false;
442 			}
443 			plot_data = "";
444 			int non_numerical = 0, non_real = 0;
445 			//string str = "";
446 			if(msecs > 0) startControl(msecs);
447 			ComparisonResult ct1 = COMPARISON_RESULT_EQUAL, ct2 = COMPARISON_RESULT_EQUAL;
448 			size_t last_index = string::npos, last_index2 = string::npos;
449 			bool check_continuous = pdps[serie]->test_continuous && (pdps[serie]->style == PLOT_STYLE_LINES || pdps[serie]->style == PLOT_STYLE_POINTS_LINES);
450 			bool prev_failed = false;
451 			for(size_t i = 1; i <= y_vectors[serie].countChildren(); i++) {
452 				ComparisonResult ct = COMPARISON_RESULT_UNKNOWN;
453 				bool invalid_nr = false, b_imagzero_x = false, b_imagzero_y = false;
454 				if(!y_vectors[serie].getChild(i)->isNumber()) {
455 					invalid_nr = true;
456 					non_numerical++;
457 					//if(non_numerical == 1) str = y_vectors[serie].getChild(i)->print(po);
458 				} else if(!y_vectors[serie].getChild(i)->number().isReal()) {
459 					b_imagzero_y = testComplexZero(&y_vectors[serie].getChild(i)->number(), y_vectors[serie].getChild(i)->number().internalImaginary());
460 					if(!b_imagzero_y) {
461 						invalid_nr = true;
462 						non_real++;
463 						//if(non_numerical + non_real == 1) str = y_vectors[serie].getChild(i)->print(po);
464 					}
465 				}
466 				if(serie < x_vectors.size() && !x_vectors[serie].isUndefined() && x_vectors[serie].countChildren() == y_vectors[serie].countChildren()) {
467 					if(!x_vectors[serie].getChild(i)->isNumber()) {
468 						invalid_nr = true;
469 						non_numerical++;
470 						//if(non_numerical == 1) str = x_vectors[serie].getChild(i)->print(po);
471 					} else if(!x_vectors[serie].getChild(i)->number().isReal()) {
472 						b_imagzero_x = testComplexZero(&x_vectors[serie].getChild(i)->number(), x_vectors[serie].getChild(i)->number().internalImaginary());
473 						if(!b_imagzero_x) {
474 							invalid_nr = true;
475 							non_real++;
476 							//if(non_numerical + non_real == 1) str = x_vectors[serie].getChild(i)->print(po);
477 						}
478 					}
479 					if(!invalid_nr) {
480 						if(b_imagzero_y) plot_data += x_vectors[serie].getChild(i)->number().realPart().print(po);
481 						else plot_data += x_vectors[serie].getChild(i)->print(po);
482 						plot_data += " ";
483 					}
484 				}
485 				if(!invalid_nr) {
486 					if(check_continuous && !prev_failed) {
487 						if(i == 1 || ct2 == COMPARISON_RESULT_UNKNOWN) ct = COMPARISON_RESULT_EQUAL;
488 						else ct = y_vectors[serie].getChild(i - 1)->number().compare(y_vectors[serie].getChild(i)->number());
489 						if((ct == COMPARISON_RESULT_GREATER || ct == COMPARISON_RESULT_LESS) && (ct1 == COMPARISON_RESULT_GREATER || ct1 == COMPARISON_RESULT_LESS) && (ct2 == COMPARISON_RESULT_GREATER || ct2 == COMPARISON_RESULT_LESS) && ct1 != ct2 && ct != ct2) {
490 							if(last_index2 != string::npos) plot_data.insert(last_index2 + 1, "  \n");
491 						}
492 					}
493 					if(b_imagzero_x) plot_data += y_vectors[serie].getChild(i)->number().realPart().print(po);
494 					else plot_data += y_vectors[serie].getChild(i)->print(po);
495 					plot_data += "\n";
496 					prev_failed = false;
497 				} else if(!prev_failed) {
498 					ct = COMPARISON_RESULT_UNKNOWN;
499 					plot_data += "  \n";
500 					prev_failed = true;
501 				}
502 				last_index2 = last_index;
503 				last_index = plot_data.length() - 1;
504 				ct1 = ct2;
505 				ct2 = ct;
506 				if(aborted()) {
507 					fclose(fdata);
508 					if(msecs > 0) {
509 						error(true, _("It took too long to generate the plot data."), NULL);
510 						stopControl();
511 					}
512 					return false;
513 				}
514 			}
515 			if(msecs > 0) stopControl();
516 			/*if(non_numerical > 0 || non_real > 0) {
517 				string stitle;
518 				if(serie < pdps.size() && !pdps[serie]->title.empty()) {
519 					stitle = pdps[serie]->title.c_str();
520 				} else {
521 					stitle = i2s(serie).c_str();
522 				}
523 				if(non_numerical > 0) {
524 					error(true, _("Series %s contains non-numerical data (\"%s\" first of %s) which can not be properly plotted."), stitle.c_str(), str.c_str(), i2s(non_numerical).c_str(), NULL);
525 				} else {
526 					error(true, _("Series %s contains non-real data (\"%s\" first of %s) which can not be properly plotted."), stitle.c_str(), str.c_str(), i2s(non_real).c_str(), NULL);
527 				}
528 			}*/
529 			fputs(plot_data.c_str(), fdata);
530 			fflush(fdata);
531 			fclose(fdata);
532 		}
533 	}
534 
535 	return invokeGnuplot(plot, commandline_extra, persistent);
536 }
537 #ifdef HAVE_GNUPLOT_CALL
invokeGnuplot(string commands,string commandline_extra,bool persistent)538 bool Calculator::invokeGnuplot(string commands, string commandline_extra, bool persistent) {
539 	FILE *pipe = NULL;
540 	if(!b_gnuplot_open || !gnuplot_pipe || persistent || commandline_extra != gnuplot_cmdline) {
541 		if(!persistent) {
542 			closeGnuplot();
543 		}
544 		string commandline = "gnuplot";
545 		if(persistent) {
546 			commandline += " -persist";
547 		}
548 		commandline += commandline_extra;
549 #ifdef _WIN32
550 		commandline += " - 2>nul";
551 		pipe = _popen(commandline.c_str(), "w");
552 #else
553 		commandline += " - 2>/dev/null";
554 		pipe = popen(commandline.c_str(), "w");
555 #endif
556 		if(!pipe) {
557 			error(true, _("Failed to invoke gnuplot. Make sure that you have gnuplot installed in your path."), NULL);
558 			return false;
559 		}
560 		if(!persistent && pipe) {
561 			gnuplot_pipe = pipe;
562 			b_gnuplot_open = true;
563 			gnuplot_cmdline = commandline_extra;
564 		}
565 	} else {
566 		pipe = gnuplot_pipe;
567 	}
568 	if(!pipe) {
569 		return false;
570 	}
571 	if(!persistent) {
572 		fputs("clear\n", pipe);
573 		fputs("reset\n", pipe);
574 	}
575 	fputs(commands.c_str(), pipe);
576 	fflush(pipe);
577 	if(persistent) {
578 		return pclose(pipe) == 0;
579 	}
580 	return true;
581 }
582 #else
invokeGnuplot(string,string,bool)583 bool Calculator::invokeGnuplot(string, string, bool) {
584 	return false;
585 }
586 #endif
closeGnuplot()587 bool Calculator::closeGnuplot() {
588 #ifdef HAVE_GNUPLOT_CALL
589 	if(gnuplot_pipe) {
590 #	ifdef _WIN32
591 		int rv = _pclose(gnuplot_pipe);
592 #	else
593 		int rv = pclose(gnuplot_pipe);
594 #	endif
595 		gnuplot_pipe = NULL;
596 		b_gnuplot_open = false;
597 		return rv == 0;
598 	}
599 	gnuplot_pipe = NULL;
600 	b_gnuplot_open = false;
601 	return true;
602 #else
603 	return false;
604 #endif
605 }
gnuplotOpen()606 bool Calculator::gnuplotOpen() {
607 	return b_gnuplot_open && gnuplot_pipe;
608 }
609 
610