1 /*
2  * Mathomatic commands that don't belong anywhere else.
3  *
4  * Copyright (C) 1987-2012 George Gesslein II.
5 
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10 
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 Lesser General Public License for more details.
15 
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19 
20 The chief copyright holder can be contacted at gesslein@mathomatic.org, or
21 George Gesslein II, P.O. Box 224, Lansing, NY  14882-0224  USA.
22 
23  */
24 
25 #include "includes.h"
26 
27 #define	OPT_MIN_SIZE	7	/* Minimum size (in tokens) of repeated expressions to find in optimize command. */
28 
29 enum spf_function {
30 	SUM_COMMAND,
31 	PRODUCT_COMMAND,
32 	FOR_COMMAND
33 };
34 
35 static int sum_product(char *cp, enum spf_function current_function);
36 static int complex_func(char *cp, int imag_flag);
37 static int elim_sub(int i, long v);
38 
39 /* Global variables for the optimize command. */
40 static int	opt_en[N_EQUATIONS+1];
41 static int	last_temp_var = 0;
42 
43 #if	SHELL_OUT
44 /*
45  * The plot command.
46  *
47  * All command functions like this return true if successful, or false for failure.
48  */
49 int
plot_cmd(cp)50 plot_cmd(cp)
51 char	*cp;	/* the command-line argument */
52 {
53 #define	APPEND(str)	{ if (strlen(str) + cl1_len < sizeof(cl1)) { strcpy(&cl1[cl1_len], str); cl1_len += strlen(str); } else warning(_("Expression too large to plot; omitted.")); }
54 
55 	int		i1, i2;
56 	int		start, stop;
57 	int		first_time = true;
58 	int		cl1_len = 0, cl2_len = 0, len;
59 	char		*cp1, cl[16384+MAX_CMD_LEN], cl1[16384], cl2[MAX_CMD_LEN], *exp_str;
60 	long		v, vx;		/* Mathomatic variables */
61 	token_type	*equation;
62 	int		*np;
63 	int		ev;		/* exit value */
64 	int		cur_equation_flag = false;
65 
66 	if (security_level > 0) {
67 		show_usage = false;
68 		error(_("Command disabled by security level."));
69 		return false;
70 	}
71 	if (!parse_var(&vx, "x")) {
72 		return false;
73 	}
74 	do {
75 		cp1 = cp;
76 		if (!get_range(&cp, &start, &stop)) {
77 			reset_error();
78 			break;
79 		}
80 		if (cp != cp1 || first_time) {
81 			if (cp == cp1)
82 				cur_equation_flag = (!empty_equation_space(cur_equation));
83 			for (i1 = start; i1 <= stop; i1++) {
84 				if (i1 != cur_equation) {
85 					cur_equation_flag = false;
86 				}
87 				if (n_lhs[i1] > 0) {
88 					v = 0;
89 					if (n_rhs[i1]) {
90 						equation = rhs[i1];
91 						np = &n_rhs[i1];
92 					} else {
93 						equation = lhs[i1];
94 						np = &n_lhs[i1];
95 					}
96 					if (!no_vars(equation, *np, &v) && v) {
97 						if (strcmp(var_name(v), "x") && strcmp(var_name(v), "t")) {
98 							list_var(v, 0);
99 							fprintf(gfp, _("#%d: Renaming variable %s to x for gnuplot.\n"), i1 + 1, var_str);
100 							rename_var_in_es(i1, v, vx);
101 						}
102 					}
103 					if (n_rhs[i1] && !solved_equation(i1)) {
104 						warning(_("Not a normally solved equation, plotting the RHS only."));
105 					}
106 					for (i2 = 0; i2 < *np; i2 += 2) {
107 						if (equation[i2].kind == VARIABLE && (equation[i2].token.variable & VAR_MASK) == SIGN) {
108 							warning(_("Plot expression contains sign variables; try \"simplify sign\" before plotting."));
109 							break;
110 						}
111 					}
112 					exp_str = list_expression(equation, *np, 3);
113 					if (exp_str == NULL)
114 						return false;
115 					if (cl1_len) {
116 						APPEND(", ");
117 					}
118 					APPEND(exp_str);
119 					free(exp_str);
120 				}
121 			}
122 		}
123 		first_time = false;
124 	} while (*cp && cp != cp1);
125 	cl1[cl1_len] = '\0';
126 	if (cl1_len == 0 && *cp == '\0') {
127 		error(_("No plot expression specified."));
128 		return false;
129 	}
130 	for (cl2_len = 0, i2 = 0; cp[i2]; i2++) {
131 		if ((cl2_len + 2) >= sizeof(cl2)) {
132 			error(_("Command-line too long."));
133 			return false;
134 		}
135 		switch (cp[i2]) {
136 		case '^':
137 			cl2[cl2_len] = '*';
138 			cl2_len++;
139 			cl2[cl2_len] = '*';
140 			cl2_len++;
141 			break;
142 		default:
143 			cl2[cl2_len] = cp[i2];
144 			cl2_len++;
145 			break;
146 		}
147 	}
148 	cl2[cl2_len] = '\0';
149 	if (cl1_len && cl2_len && cl2[cl2_len-1] != ',') {
150 		if (cur_equation_flag) {
151 			snprintf(prompt_str, sizeof(prompt_str), _("Do you want to plot the current equation, too (y/n)? "));
152 			if (!get_yes_no()) {
153 				cl1_len = 0;
154 				cl1[cl1_len] = '\0';
155 			}
156 		}
157 		if (cl1_len) {
158 			snprintf(prompt_str, sizeof(prompt_str), _("Does the plot command-line consist of any expressions (y/n)? "));
159 			if (get_yes_no()) {
160 				fprintf(gfp, _("Appending a comma to the command-line.\n"));
161 				if ((cl2_len + 2) >= sizeof(cl2)) {
162 					error(_("Command-line too long."));
163 					return false;
164 				}
165 				cl2[cl2_len++] = ',';
166 				cl2[cl2_len] = '\0';
167 			}
168 		}
169 	}
170 	if (strchr(cl2, 'y') || strchr(cl1, 'y')) {
171 		fprintf(gfp, _("Performing 3D surface plot...\n"));
172 #if	MINGW
173 		len = snprintf(cl, sizeof(cl), "gnuplot -persist -e \"%s; splot %s %s\"", plot_prefix, cl2, cl1);
174 #else
175 		len = snprintf(cl, sizeof(cl), "echo '%s; splot %s %s'|gnuplot -persist", plot_prefix, cl2, cl1);
176 #endif
177 	} else {
178 		fprintf(gfp, _("Performing 2D plot...\n"));
179 #if	MINGW
180 		len = snprintf(cl, sizeof(cl), "gnuplot -persist -e \"%s; plot %s %s\"", plot_prefix, cl2, cl1);
181 #else
182 		len = snprintf(cl, sizeof(cl), "echo '%s; plot %s %s'|gnuplot -persist", plot_prefix, cl2, cl1);
183 #endif
184 	}
185 	if (len >= sizeof(cl)) {
186 		error(_("gnuplot command-line too long."));
187 		return false;
188 	}
189 	ev = shell_out(cl);
190 	if (ev) {
191 		error(_("Possible error running gnuplot."));
192 		printf(_("Decimal exit value = %d\n"), ev);
193 		if (ev == 256 || ev == 1) {
194 			printf(_("Try separating each expression with a comma.\n"));
195 		}
196 		printf(_("Shell command-line = %s\n"), cl);
197 	}
198 	return true;
199 }
200 #endif
201 
202 /*
203  * The version command.
204  *
205  * All commands return true if successful.
206  */
207 int
version_cmd(cp)208 version_cmd(cp)
209 char	*cp;	/* the command-line argument */
210 {
211 	int	rv = true;		/* return value */
212 	int	status_flag = false;
213 
214 	if (strncasecmp(cp, "status", 4) == 0) {
215 		status_flag = true;
216 		cp = skip_param(cp);
217 	}
218 	if (extra_characters(cp))	/* Make sure nothing else is on the command-line. */
219 		return false;
220 #if	LIBRARY
221 	free_result_str();
222 	result_str = strdup(VERSION);
223 #endif
224 	if (status_flag) {
225 		rv = version_report();
226 	} else {
227 #if	!SILENT || !LIBRARY
228 		fprintf(gfp, _("Mathomatic version %s\n"), VERSION);
229 #endif
230 	}
231 
232 	if (status_flag) {
233 		debug_string(0, "\nMathomatic is GNU LGPL version 2.1 licensed software,\n"
234 				"meaning it is free software that comes with no warranty.\n"
235 				"Type \"help license\" for the copyright and license.");
236 
237 		EP(_("\nFor all new stuff, visit the Mathomatic website: www.mathomatic.org"));
238 	}
239 #if	LIBRARY
240 	return(rv && result_str != NULL);
241 #else
242 	return rv;
243 #endif
244 }
245 
246 /*
247  * Return the maximum amount of memory (in bytes) that this program will use.
248  */
249 long
max_memory_usage(void)250 max_memory_usage(void)
251 {
252 	return((long) (N_EQUATIONS + 3L) * (long) n_tokens * sizeof(token_type) * 2L);
253 }
254 
255 /*
256  * Try the function getrusage(2).
257  *
258  * Return true if successful.
259  */
260 int
show_status(ofp)261 show_status(ofp)
262 FILE	*ofp;
263 {
264 #if	SHOW_RESOURCES
265 	struct rusage	usage_local;
266 
267 	if (getrusage(RUSAGE_SELF, &usage_local) == 0) {
268 		fprintf(ofp, _("Total CPU usage, user time: %g seconds, system time: %g seconds.\n"),
269 		    (double) usage_local.ru_utime.tv_sec + ((double) usage_local.ru_utime.tv_usec / 1000000.0),
270                     (double) usage_local.ru_stime.tv_sec + ((double) usage_local.ru_stime.tv_usec / 1000000.0));
271 		if (usage_local.ru_ixrss == 0 && usage_local.ru_idrss == 0 && usage_local.ru_isrss == 0) {
272 			if (usage_local.ru_maxrss)
273 				fprintf(ofp, _("Total RSS size: %ld kilobytes.\n"), usage_local.ru_maxrss);
274 		} else {
275 			fprintf(ofp, _("Total RSS size: %ld kbytes; shared text memory size: %ld kbytes*ticks;\n"), usage_local.ru_maxrss, usage_local.ru_ixrss);
276 			fprintf(ofp, _("Unshared data size: %ld kbytes*ticks; unshared stack size: %ld kbytes*ticks.\n"), usage_local.ru_idrss, usage_local.ru_isrss);
277 			fprintf(ofp, _("Number of times Mathomatic was swapped out: %ld; signals received: %ld.\n"), usage_local.ru_nswap, usage_local.ru_nsignals);
278 		}
279 		return true;
280 	}
281 #endif
282 	return false;
283 }
284 
285 /*
286  * Display version and status info.
287  */
288 int
version_report(void)289 version_report(void)
290 {
291 	long	l;
292 
293 	fprintf(gfp, _("Mathomatic version %s\n"), VERSION);
294 	fprintf(gfp, _("The last main prompt return value is %d (meaning "), previous_return_value);
295 	switch (previous_return_value) {
296 	case 0:
297 		fprintf(gfp, _("failure).\n"));
298 		break;
299 	default:
300 		fprintf(gfp, _("success).\n"));
301 		break;
302 	}
303 	show_status(gfp);
304 	fprintf(gfp, _("\nCompile-time defines used: "));
305 #if	linux
306 	fprintf(gfp, "linux ");
307 #endif
308 #if	sun
309 	fprintf(gfp, "sun ");
310 #endif
311 #if	UNIX
312 	fprintf(gfp, "UNIX ");
313 #endif
314 #if	CYGWIN
315 	fprintf(gfp, "CYGWIN ");
316 #endif
317 #if	MINGW
318 	fprintf(gfp, "MINGW ");
319 #endif
320 #if	HANDHELD
321 	fprintf(gfp, "HANDHELD ");
322 #endif
323 #if	EDITLINE
324 	fprintf(gfp, "EDITLINE ");
325 #endif
326 #if	READLINE
327 	fprintf(gfp, "READLINE ");
328 #endif
329 #if	SILENT
330 	fprintf(gfp, "SILENT ");
331 #endif
332 #if	LIBRARY
333 	fprintf(gfp, "LIBRARY ");
334 #endif
335 #if	SECURE
336 	fprintf(gfp, "SECURE ");
337 #endif
338 #if	TIMEOUT_SECONDS
339 	fprintf(gfp, "TIMEOUT_SECONDS=%d ", TIMEOUT_SECONDS);
340 #endif
341 #if	I18N
342 	fprintf(gfp, "I18N ");
343 #endif
344 #if	NO_COLOR
345 	fprintf(gfp, "NO_COLOR ");
346 #endif
347 #if	BOLD_COLOR
348 	fprintf(gfp, "BOLD_COLOR ");
349 #endif
350 #if	WIN32_CONSOLE_COLORS
351 	fprintf(gfp, "WIN32_CONSOLE_COLORS ");
352 #endif
353 #if	NOGAMMA
354 	fprintf(gfp, "NOGAMMA ");
355 #endif
356 #if	USE_TGAMMA
357 	fprintf(gfp, "USE_TGAMMA ");
358 #endif
359 #if	DEBUG
360 	fprintf(gfp, "DEBUG ");
361 #endif
362 #if	VALGRIND
363 	fprintf(gfp, "VALGRIND ");
364 #endif
365 #if     SHOW_RESOURCES
366 	fprintf(gfp, "SHOW_RESOURCES ");
367 #endif
368 
369 	fprintf(gfp, "\nsizeof(int) = %u bytes, sizeof(long) = %u bytes.\n", (unsigned) sizeof(int), (unsigned) sizeof(long));
370 	fprintf(gfp, "sizeof(double) = %u bytes, maximum double precision = %d decimal digits.\n", (unsigned) sizeof(double), DBL_DIG);
371 #ifdef	__VERSION__
372 #ifdef	__GNUC__
373 	fprintf(gfp, "GNU ");
374 #endif
375 	fprintf(gfp, _("C Compiler version: %s\n"), __VERSION__);
376 #endif
377 
378 	fprintf(gfp, _("\n%d equation spaces currently allocated.\n"), n_equations);
379 	fprintf(gfp, _("The current expression array size is %d tokens,\n"), n_tokens);
380 	l = max_memory_usage() / 1000L;
381 	if (l >= 10000L) {
382 		fprintf(gfp, _("making the maximum memory usage approximately %ld megabytes.\n"), l / 1000L);
383 	} else {
384 		fprintf(gfp, _("making the maximum memory usage approximately %ld kilobytes.\n"), l);
385 	}
386 #if	SECURE
387 	fprintf(gfp, _("Compiled for maximum security.\n"));
388 #else
389 	fprintf(gfp, _("The current security level is %d"), security_level);
390 	switch (security_level) {
391 	case -1:
392 		fprintf(gfp, _(", meaning you are running m4 Mathomatic.\n"));
393 		break;
394 	case 0:
395 		fprintf(gfp, _(", no security, meaning users are unrestricted.\n"));
396 		break;
397 	case 1:
398 	case 2:
399 		fprintf(gfp, _(", some security.\n"));
400 		break;
401 	case 3:
402 		fprintf(gfp, _(", high security.\n"));
403 		break;
404 	case 4:
405 		fprintf(gfp, _(", maximum security.\n"));
406 		break;
407 	default:
408 		fprintf(gfp, _(", unknown meaning.\n"));
409 		break;
410 	}
411 #endif
412 #if	READLINE || EDITLINE
413 #if	READLINE
414 	fprintf(gfp, _("\nreadline is compiled in and "));
415 #else
416 	fprintf(gfp, _("\neditline is compiled in and "));
417 #endif
418 	if (readline_enabled) {
419 		fprintf(gfp, _("activated.\n"));
420 	} else {
421 		fprintf(gfp, _("deactivated.\n"));
422 	}
423 #elif	!LIBRARY && !HANDHELD
424 #if	MINGW
425 	SP(_("\nreadline is not compiled in, however some of its functionality"));
426 	SP(_("already exists in the Windows console for any"));
427 	EP(_("Windows console program (like Mathomatic)."));
428 #else
429 	SP(_("\nreadline is not compiled in."));
430 	SP(_("Please notify the package maintainer that readline"));
431 	EP(_("should be compiled into Mathomatic, with \"make READLINE=1\"."));
432 #endif
433 #endif
434 	return true;
435 }
436 
437 /*
438  * The solve command.
439  *
440  * Return 0 on solve failure for any equation which a solve was requested, or something was not verifiable,
441  * with the "solve verifiable" option.
442  * Return 1 on total success (all requested solves completed successfully),
443  * or 2 if partial success (all solved, but some solutions didn't verify when doing "solve verify",
444  * or the result contains infinity or NaN).
445  */
446 int
solve_cmd(cp)447 solve_cmd(cp)
448 char	*cp;
449 {
450 	int		i, j, k;
451 	int		start, stop;
452 	char		buf[MAX_CMD_LEN];
453 	int		diff_sign;
454 	int		verify_flag = 0, plural_flag, once_through, contains_infinity, did_something = false, last_solve_successful = false;
455 	char		*cp1, *cp_start;
456 	token_type	want;
457 	int		rv = 1;
458 	long		pre_v;		/* Mathomatic variable */
459 
460 	cp_start = cp;
461 	if (strcmp_tospace(cp, "verify") == 0) {
462 		verify_flag = 1;
463 		cp = skip_param(cp);
464 	} else if (strcmp_tospace(cp, "verifiable") == 0) {
465 		verify_flag = 2;
466 		cp = skip_param(cp);
467 	}
468 	if (!get_range(&cp, &start, &stop)) {
469 		warning(_("No equations to solve."));
470 		return false;
471 	}
472 	i = next_espace();
473 	repeat_flag = true;
474 	if (strcmp_tospace(cp, "verify") == 0) {
475 		verify_flag = 1;
476 		cp = skip_param(cp);
477 	} else if (strcmp_tospace(cp, "verifiable") == 0) {
478 		verify_flag = 2;
479 		cp = skip_param(cp);
480 	}
481 	if (strcmp_tospace(cp, "for") == 0) {
482 		cp1 = skip_param(cp);
483 		if (*cp1) {
484 			cp = cp1;
485 		}
486 	}
487 	if (*cp == '\0') {
488 		my_strlcpy(prompt_str, _("Enter variable to solve for: "), sizeof(prompt_str));
489 		if ((cp = get_string(buf, sizeof(buf))) == NULL) {
490 			return false;
491 		}
492 		cp_start = cp;
493 	}
494 	input_column += (cp - cp_start);
495 	if ((cp = parse_equation(i, cp)) == NULL) {
496 		return false;
497 	}
498 	if (verify_flag) {
499 		if (n_lhs[i] != 1 || n_rhs[i] != 0 || (lhs[i][0].kind != VARIABLE
500 		    && (lhs[i][0].kind != CONSTANT || lhs[i][0].token.constant != 0.0))) {
501 			error(_("Can only verify for a single solve variable or identities after solving for 0."));
502 			goto fail;
503 		}
504 		want = lhs[i][0];
505 	}
506 	show_usage = false;
507 	for (k = start; k <= stop; k++) {
508 		if (k == i || n_lhs[k] <= 0 || n_rhs[k] <= 0) {
509 			continue;
510 		}
511 		last_solve_successful = false;
512 		cur_equation = k;
513 
514 		did_something = true;
515 		if (verify_flag) {
516 			pre_v = 0;
517 			fprintf(gfp, _("Solving equation #%d for "), cur_equation + 1);
518 			list_proc(&want, 1, false);
519 			if (verify_flag == 2) {
520 				fprintf(gfp, " with required ");
521 			} else {
522 				fprintf(gfp, " with ");
523 			}
524 			if (want.kind == VARIABLE) {
525 				fprintf(gfp, "verification...\n");
526 				if (solved_equation(cur_equation)) {
527 					pre_v = lhs[cur_equation][0].token.variable;
528 				}
529 			} else {
530 				fprintf(gfp, "identity verification...\n");
531 			}
532 			copy_espace(cur_equation, i);
533 			if (solve_sub(&want, 1, lhs[cur_equation], &n_lhs[cur_equation], rhs[cur_equation], &n_rhs[cur_equation]) > 0) {
534 				simpa_repeat(cur_equation, true, false);	/* Solve result should be quick simplified. */
535 				last_solve_successful = true;
536 				debug_string(0, _("Solve and \"repeat simplify quick\" successful:"));
537 				if (!return_result(cur_equation)) {		/* Display the simplified solve result. */
538 					goto fail;
539 				}
540 				if (want.kind == VARIABLE) {
541 					if (!solved_equation(cur_equation) || lhs[cur_equation][0].token.variable != want.token.variable) {
542 						error(_("Result not a properly solved equation, so cannot verify."));
543 						continue;
544 					}
545 					if (pre_v && pre_v == want.token.variable) {
546 						warning(_("Equation was already solved, so no need to verify solutions."));
547 						continue;
548 					}
549 				} else {
550 					copy_espace(cur_equation, i);
551 				}
552 				plural_flag = false;
553 				for (j = 0; j < n_rhs[cur_equation]; j += 2) {
554 					if (rhs[cur_equation][j].kind == VARIABLE && (rhs[cur_equation][j].token.variable & VAR_MASK) == SIGN) {
555 						plural_flag = true;
556 						break;
557 					}
558 				}
559 				if (want.kind == VARIABLE) {
560 					subst_var_with_exp(lhs[i], &n_lhs[i], rhs[cur_equation], n_rhs[cur_equation], want.token.variable);
561 					subst_var_with_exp(rhs[i], &n_rhs[i], rhs[cur_equation], n_rhs[cur_equation], want.token.variable);
562 				}
563 				once_through = false;
564 				calc_simp(lhs[i], &n_lhs[i]);
565 				calc_simp(rhs[i], &n_rhs[i]);
566 check_result:
567 				contains_infinity = (exp_contains_infinity(lhs[i], n_lhs[i])
568 				    || exp_contains_infinity(rhs[i], n_rhs[i]));
569 	                        if (se_compare(lhs[i], n_lhs[i], rhs[i], n_rhs[i], &diff_sign) && (want.kind != VARIABLE || !diff_sign)) {
570 					if (want.kind != VARIABLE) {
571 						fprintf(gfp, _("This equation is an identity.\n"));
572 					} else if (plural_flag)
573 						fprintf(gfp, _("All solutions verified.\n"));
574 					else
575 						fprintf(gfp, _("Solution verified.\n"));
576 					if (contains_infinity) {
577 						error(_("Solution might be incorrect because it contains infinity or NaN."));
578 						if (rv)
579 							rv = 2;
580 					}
581 				} else {
582 					if (!contains_infinity && once_through < 2) {
583 						symb_flag = symblify;
584 						simpa_repeat(i, once_through ? false : true, once_through ? true : false);	/* Simplify to compare equation sides. */
585 						symb_flag = false;
586 						once_through++;
587 						goto check_result;
588 					}
589 					if (contains_infinity) {
590 						error(_("Solution might be incorrect because it contains infinity or NaN."));
591 					} else {
592 						if (want.kind != VARIABLE) {
593 							error(_("This equation is NOT an identity."));
594 						} else if (plural_flag)
595 							error(_("Unable to verify all solutions."));
596 						else
597 							error(_("Unable to verify solution."));
598 					}
599 					if (verify_flag == 2)
600 						rv = 0;
601 					else if (rv)
602 						rv = 2;
603 				}
604 			} else {
605 				printf(_("Solve failed for equation space #%d.\n"), cur_equation + 1);
606 				rv = 0;
607 			}
608 		} else {
609 			if (solve_espace(i, cur_equation)) {
610 				last_solve_successful = true;
611 				if (!return_result(cur_equation)) {
612 					goto fail;
613 				}
614 			} else {
615 				rv = 0;
616 			}
617 		}
618 	}
619 	if (did_something) {
620 		if (last_solve_successful && verify_flag) {
621 			debug_string(1, "Verification identity:");
622 			list_esdebug(1, i);
623 		}
624 	} else {
625 		printf(_("No work done.\n"));
626 	}
627 	n_lhs[i] = 0;
628 	n_rhs[i] = 0;
629 	return rv;
630 fail:
631 	n_lhs[i] = 0;
632 	n_rhs[i] = 0;
633 	return 0;
634 }
635 
636 /*
637  * The sum command.
638  */
639 int
sum_cmd(cp)640 sum_cmd(cp)
641 char	*cp;
642 {
643 	return sum_product(cp, SUM_COMMAND);
644 }
645 
646 /*
647  * The product command.
648  */
649 int
product_cmd(cp)650 product_cmd(cp)
651 char	*cp;
652 {
653 	return sum_product(cp, PRODUCT_COMMAND);
654 }
655 
656 /*
657  * The for command.
658  */
659 int
for_cmd(cp)660 for_cmd(cp)
661 char	*cp;
662 {
663 	return sum_product(cp, FOR_COMMAND);
664 }
665 
666 /*
667  * Common function for the sum and product commands.
668  */
669 static int
sum_product(cp,current_function)670 sum_product(cp, current_function)
671 char			*cp;		/* the command-line */
672 enum spf_function	current_function;
673 {
674 	int		i;
675 	long		v = 0;			/* Mathomatic variable */
676 	double		start, end, step = 1.0;
677 	int		result_equation;
678 	int		n, ns;
679 	token_type	*dest, *source;
680 	int		count_down;		/* if true, count down, otherwise count up */
681 	char		*cp1, buf[MAX_CMD_LEN];
682 
683 	if (current_not_defined()) {
684 		return false;
685 	}
686 	result_equation = next_espace();
687 	if (n_rhs[cur_equation]) {
688 		ns = n_rhs[cur_equation];
689 		source = rhs[cur_equation];
690 		dest = rhs[result_equation];
691 	} else {
692 		ns = n_lhs[cur_equation];
693 		source = lhs[cur_equation];
694 		dest = lhs[result_equation];
695 	}
696 	if (*cp) {
697 		cp = parse_var2(&v, cp);
698 		if (cp == NULL) {
699 			return false;
700 		}
701 	}
702 	if (no_vars(source, ns, &v)) {
703 		error(_("Current expression contains no variables."));
704 		return false;
705 	}
706 	if (v == 0) {
707 		if (!prompt_var(&v)) {
708 			return false;
709 		}
710 	}
711 	if (!found_var(source, ns, v)) {
712 		error(_("Specified variable not found."));
713 		return false;
714 	}
715 	if (*cp) {
716 		if (*cp == '=') {
717 			cp++;
718 		}
719 		cp1 = cp;
720 	} else {
721 		list_var(v, 0);
722 		snprintf(prompt_str, sizeof(prompt_str), "%s = ", var_str);
723 		if ((cp1 = get_string(buf, sizeof(buf))) == NULL)
724 			return false;
725 	}
726 	start = strtod(cp1, &cp);
727 	if (cp1 == cp || !isfinite(start)) {
728 		error(_("Number expected."));
729 		return false;
730 	}
731 	if (fabs(start) >= MAX_K_INTEGER) {
732 		error(_("Number too large."));
733 		return false;
734 	}
735 	cp = skip_comma_space(cp);
736 	if (strcmp_tospace(cp, "to") == 0) {
737 		cp = skip_param(cp);
738 	}
739 	if (*cp) {
740 		cp1 = cp;
741 	} else {
742 		my_strlcpy(prompt_str, _("To: "), sizeof(prompt_str));
743 		if ((cp1 = get_string(buf, sizeof(buf))) == NULL)
744 			return false;
745 	}
746 	end = strtod(cp1, &cp);
747 	if (cp1 == cp || !isfinite(end)) {
748 		error(_("Number expected."));
749 		return false;
750 	}
751 	if (fabs(end) >= MAX_K_INTEGER) {
752 		error(_("Number too large."));
753 		return false;
754 	}
755 	cp = skip_comma_space(cp);
756 	if (strcmp_tospace(cp, "step") == 0) {
757 		cp = skip_param(cp);
758 	}
759 	if (*cp) {
760 		cp1 = cp;
761 		step = fabs(strtod(cp1, &cp));
762 		if (cp1 == cp || !isfinite(step) || step <= 0.0 || step >= MAX_K_INTEGER) {
763 			error(_("Invalid step."));
764 			return false;
765 		}
766 	}
767 	if (extra_characters(cp))
768 		return false;
769 	count_down = (end < start);
770 	if (fmod(fabs(start - end) / step, 1.0) != 0.0) {
771 		warning(_("End value not reached."));
772 	}
773 	if (current_function == PRODUCT_COMMAND) {
774 		dest[0] = one_token;
775 	} else {
776 		dest[0] = zero_token;
777 	}
778 	n = 1;
779 	for (; count_down ? (start >= end) : (start <= end); count_down ? (start -= step) : (start += step)) {
780 		if (n + 1 + ns > n_tokens) {
781 			error_huge();
782 		}
783 		blt(tlhs, source, ns * sizeof(token_type));
784 		n_tlhs = ns;
785 		for (i = 0; i < n_tlhs; i += 2) {
786 			if (tlhs[i].kind == VARIABLE && tlhs[i].token.variable == v) {
787 				tlhs[i].kind = CONSTANT;
788 				tlhs[i].token.constant = start;
789 			}
790 		}
791 		if (current_function != FOR_COMMAND) {
792 			for (i = 0; i < n_tlhs; i++) {
793 				tlhs[i].level++;
794 			}
795 			for (i = 0; i < n; i++) {
796 				dest[i].level++;
797 			}
798 			dest[n].kind = OPERATOR;
799 			dest[n].level = 1;
800 		}
801 		switch (current_function) {
802 		case PRODUCT_COMMAND:
803 			dest[n].token.operatr = TIMES;
804 			n++;
805 			break;
806 		case SUM_COMMAND:
807 			dest[n].token.operatr = PLUS;
808 			n++;
809 			break;
810 		case FOR_COMMAND:
811 			n = 0;
812 			break;
813 		}
814 		blt(&dest[n], tlhs, n_tlhs * sizeof(token_type));
815 		n += n_tlhs;
816 		calc_simp(dest, &n);
817 		if (current_function == FOR_COMMAND) {
818 			list_var(v, 0);
819 			fprintf(gfp, "%s = %.*g: ", var_str, precision, start);
820 			list_factor(dest, &n, false);
821 			fprintf(gfp, "\n");
822 		} else {
823 			side_debug(1, dest, n);
824 		}
825 	}
826 	if (current_function == FOR_COMMAND) {
827 		return true;
828 	} else {
829 		if (n_rhs[cur_equation]) {
830 			n_rhs[result_equation] = n;
831 			blt(lhs[result_equation], lhs[cur_equation], n_lhs[cur_equation] * sizeof(token_type));
832 			n_lhs[result_equation] = n_lhs[cur_equation];
833 		} else {
834 			n_lhs[result_equation] = n;
835 		}
836 		return return_result(result_equation);
837 	}
838 }
839 
840 /*
841  * This function is for the "optimize" command.
842  * It finds and substitutes all occurrences of the RHS of "en" in "equation".
843  * It should be called repeatedly until it returns false.
844  */
845 static int
find_more(equation,np,en)846 find_more(equation, np, en)
847 token_type	*equation;	/* expression to search */
848 int		*np;		/* pointer to length of expression */
849 int		en;		/* equation space number */
850 {
851 	int	i, j, k;
852 	int	level;
853 	int	diff_sign;
854 	int	found_se;	/* found sub-expression flag */
855 
856 	if (*np <= 0 || !solved_equation(en)) {
857 		return false;
858 	}
859 	for (level = 1, found_se = true; found_se; level++) {
860 		for (i = 1, found_se = false; i < *np; i = j + 2) {
861 			for (j = i; j < *np && equation[j].level >= level; j += 2)
862 				;
863 			if (j == i) {
864 				continue;
865 			}
866 			found_se = true;
867 			k = i - 1;
868 			if (se_compare(&equation[k], j - k, rhs[en], n_rhs[en], &diff_sign)) {
869 				if (diff_sign) {
870 					blt(&equation[i+2], &equation[j], (*np - j) * sizeof(token_type));
871 					*np -= (j - (i + 2));
872 					level++;
873 					equation[k].level = level;
874 					equation[k].kind = CONSTANT;
875 					equation[k].token.constant = -1.0;
876 					k++;
877 					equation[k].level = level;
878 					equation[k].kind = OPERATOR;
879 					equation[k].token.operatr = TIMES;
880 					k++;
881 				} else {
882 					blt(&equation[i], &equation[j], (*np - j) * sizeof(token_type));
883 					*np -= (j - i);
884 				}
885 				equation[k].level = level;
886 				equation[k].kind = VARIABLE;
887 				equation[k].token.variable = lhs[en][0].token.variable;
888 				return true;
889 			}
890 		}
891 	}
892 	return false;
893 }
894 
895 /*
896  * This function is for the "optimize" command.
897  * It finds and replaces all repeated expressions in "equation" with temporary variables.
898  * It also creates a new equation for each temporary variable.
899  * It should be called repeatedly until it returns false.
900  */
901 static int
opt_es(equation,np)902 opt_es(equation, np)
903 token_type	*equation;
904 int		*np;
905 {
906 	int	i, j, k, i1, i2, jj1, k1;
907 	int	level, level1;
908 	int	diff_sign;
909 	int	found_se, found_se1;	/* found sub-expression flags */
910 	long	v;			/* Mathomatic variable */
911 	char	var_name_buf[MAX_VAR_LEN];
912 
913 	if (*np <= 0) {
914 		return false;
915 	}
916 	for (level = 1, found_se = true; found_se; level++) {
917 		for (i = 1, found_se = false; i < *np; i = j + 2) {
918 			for (j = i; j < *np && equation[j].level > level; j += 2)
919 				;
920 			if (j == i) {
921 				continue;
922 			}
923 			found_se = true;
924 			k = i - 1;
925 			if ((j - k) < OPT_MIN_SIZE) {
926 				continue;
927 			}
928 			found_se1 = true;
929 			for (level1 = 1; found_se1; level1++) {
930 				for (i1 = 1, found_se1 = false; i1 < *np; i1 = jj1 + 2) {
931 					for (jj1 = i1; jj1 < *np && equation[jj1].level > level1; jj1 += 2) {
932 					}
933 					if (jj1 == i1) {
934 						continue;
935 					}
936 					found_se1 = true;
937 					if (i1 <= j)
938 						continue;
939 					k1 = i1 - 1;
940 					if ((jj1 - k1) >= OPT_MIN_SIZE
941 					    && se_compare(&equation[k], j - k, &equation[k1], jj1 - k1, &diff_sign)) {
942 						snprintf(var_name_buf, sizeof(var_name_buf), "temp%.0d", last_temp_var);
943 						if (parse_var(&v, var_name_buf) == NULL) {
944 							return false;	/* can't create "temp" variable */
945 						}
946 						last_temp_var++;
947 						if (last_temp_var < 0) {
948 							last_temp_var = 0;
949 						}
950 						i2 = next_espace();
951 						lhs[i2][0].level = 1;
952 						lhs[i2][0].kind = VARIABLE;
953 						lhs[i2][0].token.variable = v;
954 						n_lhs[i2] = 1;
955 						blt(rhs[i2], &equation[k], (j - k) * sizeof(token_type));
956 						n_rhs[i2] = j - k;
957 						if (diff_sign) {
958 							blt(&equation[i1+2], &equation[jj1], (*np - jj1) * sizeof(token_type));
959 							*np -= (jj1 - (i1 + 2));
960 							level1++;
961 							equation[k1].level = level1;
962 							equation[k1].kind = CONSTANT;
963 							equation[k1].token.constant = -1.0;
964 							k1++;
965 							equation[k1].level = level1;
966 							equation[k1].kind = OPERATOR;
967 							equation[k1].token.operatr = TIMES;
968 							k1++;
969 						} else {
970 							blt(&equation[i1], &equation[jj1], (*np - jj1) * sizeof(token_type));
971 							*np -= (jj1 - i1);
972 						}
973 						equation[k1].level = level1;
974 						equation[k1].kind = VARIABLE;
975 						equation[k1].token.variable = v;
976 						blt(&equation[i], &equation[j], (*np - j) * sizeof(token_type));
977 						*np -= j - i;
978 						equation[k].level = level;
979 						equation[k].kind = VARIABLE;
980 						equation[k].token.variable = v;
981 						while (find_more(equation, np, i2))
982 							;
983 						simp_loop(rhs[i2], &n_rhs[i2]);
984 						simp_loop(equation, np);
985 						for (i = 0;; i++) {
986 							if (i >= N_EQUATIONS) {
987 								error_bug("Too many optimized equations.");
988 							}
989 							if (opt_en[i] < 0)
990 								break;
991 						}
992 						opt_en[i] = i2;
993 						opt_en[i+1] = -1;
994 						return true;
995 					}
996 				}
997 			}
998 		}
999 	}
1000 	return false;
1001 }
1002 
1003 /*
1004  * The optimize command.
1005  */
1006 int
optimize_cmd(cp)1007 optimize_cmd(cp)
1008 char	*cp;
1009 {
1010 	int	i, j, k, i1;
1011 	int	start, stop;
1012 	int	rv = false, flag, skip_flag;
1013 	int	start_en;
1014 	int	diff_sign;
1015 
1016 	if (!get_range_eol(&cp, &start, &stop)) {
1017 		return false;
1018 	}
1019 	opt_en[0] = -1;
1020 	start_en = 0;
1021 	for (j = i = start; i <= stop; i++) {
1022 		if (n_lhs[i]) {
1023 			j = i;
1024 			simp_equation(i);
1025 		}
1026 	}
1027 	stop = j;
1028 	do {
1029 		flag = false;
1030 		for (i = start; i <= stop; i++) {
1031 			for (j = start; j <= stop; j++) {
1032 				if (i != j) {
1033 					while (find_more(rhs[i], &n_rhs[i], j)) {
1034 						flag = true;
1035 						rv = true;
1036 					}
1037 				}
1038 			}
1039 		}
1040 	} while (flag);
1041 	for (i = start; i <= stop; i++) {
1042 		if (n_lhs[i] == 0)
1043 			continue;
1044 		do {
1045 			flag = false;
1046 			simp_equation(i);
1047 			for (j = 0; opt_en[j] >= 0; j++) {
1048 				if (i != opt_en[j]) {
1049 					simp_equation(opt_en[j]);
1050 					while (find_more(lhs[i], &n_lhs[i], opt_en[j]))
1051 						flag = true;
1052 					while (find_more(rhs[i], &n_rhs[i], opt_en[j]))
1053 						flag = true;
1054 				}
1055 			}
1056 		} while (flag);
1057 		while (opt_es(lhs[i], &n_lhs[i])) {
1058 			rv = true;
1059 		}
1060 		while (opt_es(rhs[i], &n_rhs[i])) {
1061 			rv = true;
1062 		}
1063 		if (rv) {
1064 			for (i1 = start_en; opt_en[i1] >= 0; i1++) {
1065 				for (j = start_en; opt_en[j] >= 0; j++) {
1066 					for (k = j + 1; opt_en[k] >= 0; k++) {
1067 						while (find_more(rhs[opt_en[k]], &n_rhs[opt_en[k]], opt_en[j]))
1068 							;
1069 						while (find_more(rhs[opt_en[j]], &n_rhs[opt_en[j]], opt_en[k]))
1070 							;
1071 					}
1072 				}
1073 				while (opt_es(rhs[opt_en[i1]], &n_rhs[opt_en[i1]]))
1074 					;
1075 			}
1076 			/* Remove equation if identity, otherwise display. */
1077 			for (; opt_en[start_en] >= 0; start_en++) {
1078 				k = opt_en[start_en];
1079 				if (se_compare(lhs[k], n_lhs[k], rhs[k], n_rhs[k], &diff_sign) && !diff_sign) {
1080 					n_lhs[k] = 0;
1081 					n_rhs[k] = 0;
1082 				} else
1083 					list_sub(k);
1084 			}
1085 			if (se_compare(lhs[i], n_lhs[i], rhs[i], n_rhs[i], &diff_sign) && !diff_sign) {
1086 				n_lhs[i] = 0;
1087 				n_rhs[i] = 0;
1088 			}
1089 		}
1090 	}
1091 	if (rv) {
1092 		for (i = start; i <= stop; i++) {
1093 			if (n_lhs[i] == 0)
1094 				continue;
1095 			skip_flag = false;
1096 			do {
1097 				flag = false;
1098 				simp_equation(i);
1099 				for (j = 0; opt_en[j] >= 0; j++) {
1100 					if (i != opt_en[j]) {
1101 						simp_equation(opt_en[j]);
1102 						while (find_more(lhs[i], &n_lhs[i], opt_en[j]))
1103 							flag = true;
1104 						while (find_more(rhs[i], &n_rhs[i], opt_en[j]))
1105 							flag = true;
1106 					} else
1107 						skip_flag = true;
1108 				}
1109 			} while (flag);
1110 			if (!skip_flag)
1111 				list_sub(i);
1112 		}
1113 	}
1114 	if (!rv) {
1115 		error(_("Unable to find any repeated expressions."));
1116 	}
1117 	return rv;
1118 }
1119 
1120 #if	READLINE || EDITLINE
1121 /*
1122  * The push command.
1123  */
1124 int
push_cmd(cp)1125 push_cmd(cp)
1126 char	*cp;
1127 {
1128 	int	start, stop;
1129 	int	k;
1130 	char	*cp1, *cp_start;
1131 
1132 	cp_start = cp;
1133 	if (!readline_enabled) {
1134 		error(_("Readline is currently turned off."));
1135 		return false;
1136 	}
1137 	do {
1138 		cp1 = cp;
1139 		if (!get_range(&cp, &start, &stop)) {
1140 			if (*cp_start) {
1141 				reset_error();
1142 			}
1143 			goto push_text;
1144 		}
1145 		if (*cp && cp == cp1) {
1146 			goto push_text;
1147 		}
1148 		for (k = start; k <= stop; k++) {
1149 			if (n_lhs[k]) {
1150 				if (push_en(k)) {
1151 					debug_string(0, _("Expression pushed.  Press the UP key to access."));
1152 				} else {
1153 					error(_("Expression push failed."));
1154 					return false;
1155 				}
1156 			}
1157 		}
1158 	} while (*cp);
1159 	return true;
1160 
1161 push_text:
1162 	if (*cp_start) {
1163 		add_history(cp_start);
1164 		last_history_string = NULL;
1165 		debug_string(0, _("Text string pushed.  Press the UP key to access."));
1166 		return true;
1167 	}
1168 	return false;
1169 }
1170 
1171 /*
1172  * Push an equation space into the readline history.
1173  *
1174  * Return true if successful.
1175  */
1176 int
push_en(en)1177 push_en(en)
1178 int	en;	/* equation space number to push */
1179 {
1180 	char	*cp;
1181 
1182 	if (!readline_enabled)
1183 		return false;
1184 	high_prec = true;
1185 	cp = list_equation(en, false);
1186 	high_prec = false;
1187 	if (cp == NULL)
1188 		return false;
1189 	add_history(cp);
1190 	last_history_string = cp;
1191 	return true;
1192 }
1193 #endif
1194 
1195 /*
1196  * Output the current working directory.
1197  *
1198  * Return true if successful.
1199  */
1200 int
output_current_directory(ofp)1201 output_current_directory(ofp)
1202 FILE	*ofp;	/* output file pointer */
1203 {
1204 #if	!SECURE
1205 	char	buf[MAX_CMD_LEN];
1206 
1207 	if (security_level < 3 && ofp) {
1208 		if (getcwd(buf, sizeof(buf))) {
1209 			fprintf(ofp, "directory %s\n", buf);
1210 			return true;
1211 		} else {
1212 			perror(NULL);
1213 		}
1214 	}
1215 #endif
1216 	return false;
1217 }
1218 
1219 int
fprintf_escaped(ofp,cp)1220 fprintf_escaped(ofp, cp)
1221 FILE	*ofp;
1222 char	*cp;
1223 {
1224 	int	len = 0;
1225 
1226 	while (*cp) {
1227 		if (*cp == ';') {
1228 			len += fprintf(ofp, "\\");
1229 		}
1230 		len += fprintf(ofp, "%c", *cp);
1231 		cp++;
1232 	}
1233 	return len;
1234 }
1235 
1236 /*
1237  * Output the current set options in a format suitable for reading back in.
1238  * If all_set_options, include options you don't want to save.
1239  */
1240 void
output_options(ofp,all_set_options)1241 output_options(ofp, all_set_options)
1242 FILE	*ofp;	/* output file pointer */
1243 int	all_set_options;
1244 {
1245 	if (ofp == NULL)
1246 		return;
1247 
1248 	fprintf(ofp, "precision = %d digits\n", precision);
1249 
1250 	if (!autosolve) {
1251 		fprintf(ofp, "no ");
1252 	}
1253 	fprintf(ofp, "autosolve\n");
1254 
1255 	if (!autocalc) {
1256 		fprintf(ofp, "no ");
1257 	}
1258 	fprintf(ofp, "autocalc\n");
1259 
1260 	if (!autodelete) {
1261 		fprintf(ofp, "no ");
1262 	}
1263 	fprintf(ofp, "autodelete\n");
1264 
1265 	if (!autoselect) {
1266 		fprintf(ofp, "no ");
1267 	}
1268 	fprintf(ofp, "autoselect\n");
1269 
1270 #if	!SILENT
1271 	fprintf(ofp, "debug_level = %d\n", debug_level);
1272 #endif
1273 
1274 	if (!case_sensitive_flag) {
1275 		fprintf(ofp, "no ");
1276 	}
1277 	fprintf(ofp, "case_sensitive\n");
1278 
1279 	if (all_set_options && html_flag) {
1280 		if (html_flag == 2) {
1281 			fprintf(ofp, "all html ");
1282 		} else {
1283 			fprintf(ofp, "html ");
1284 		}
1285 	}
1286 	if (color_flag == 2) {
1287 		fprintf(ofp, "alternative ");
1288 	}
1289 	if (bold_colors && color_flag) {
1290 		fprintf(ofp, "bold color");
1291 	} else {
1292 		if (!color_flag) {
1293 			fprintf(ofp, "no color");
1294 		} else {
1295 			fprintf(ofp, "no bold color");
1296 		}
1297 	}
1298 	if (text_color >= 0) {
1299 		fprintf(ofp, " %d", text_color);
1300 	}
1301 	fprintf(ofp, "\n");
1302 
1303 	if (!display2d) {
1304 		fprintf(ofp, "no ");
1305 	}
1306 	fprintf(ofp, "display2d\n");
1307 
1308 	if (all_set_options) {
1309 		fprintf(ofp, "columns = %d, ", screen_columns);
1310 		fprintf(ofp, "rows = %d\n", screen_rows);
1311 	}
1312 
1313 	fprintf(ofp, "fractions_display_mode = ");
1314 	switch (fractions_display) {
1315 	case 0:
1316 		fprintf(ofp, "none\n");
1317 		break;
1318 	case 2:
1319 		fprintf(ofp, "mixed\n");
1320 		break;
1321 	default:
1322 		fprintf(ofp, "simple\n");
1323 		break;
1324 	}
1325 
1326 	if (quiet_mode) {
1327 		fprintf(ofp, "no ");
1328 	}
1329 	fprintf(ofp, "prompt\n");
1330 
1331 #if	0
1332 	if (!preserve_surds) {
1333 		fprintf(ofp, "no ");
1334 	}
1335 	fprintf(ofp, "preserve_surds\n");
1336 #endif
1337 
1338 	if (!rationalize_denominators) {
1339 		fprintf(ofp, "no ");
1340 	}
1341 	fprintf(ofp, "rationalize_denominators\n");
1342 
1343 	fprintf(ofp, "modulus_mode = ");
1344 	switch (modulus_mode) {
1345 	case 0:
1346 		fprintf(ofp, "C\n");
1347 		break;
1348 	case 1:
1349 		fprintf(ofp, "Python\n");
1350 		break;
1351 	case 2:
1352 		fprintf(ofp, "normal\n");
1353 		break;
1354 	default:
1355 		fprintf(ofp, "unknown\n");
1356 		break;
1357 	}
1358 
1359 	if (finance_option < 0) {
1360 		fprintf(ofp, "no fixed_point\n");
1361 	} else {
1362 		fprintf(ofp, "fixed_point = %d\n", finance_option);
1363 	}
1364 
1365 	if (!factor_int_flag) {
1366 		fprintf(ofp, "no ");
1367 	}
1368 	fprintf(ofp, "factor_integers\n");
1369 
1370 	if (right_associative_power) {	/* option is hardly ever used */
1371 		fprintf(ofp, "right_associative_power\n");
1372 	}
1373 
1374 #if	SHELL_OUT
1375 	fprintf(ofp, "plot_prefix = ");
1376 	fprintf_escaped(ofp, plot_prefix);
1377 	fprintf(ofp, "\n");
1378 #endif
1379 
1380 	fprintf(ofp, "special_variable_characters = %s\n", special_variable_characters);
1381 }
1382 
1383 /*
1384  * Skip over a yes/no indicator and return true if *cpp pointed to a negative word.
1385  */
1386 int
skip_no(cpp)1387 skip_no(cpp)
1388 char	**cpp;
1389 {
1390 	if (strcmp_tospace(*cpp, "no") == 0
1391 	    || strcmp_tospace(*cpp, "not") == 0
1392 	    || strcmp_tospace(*cpp, "off") == 0
1393 	    || strcmp_tospace(*cpp, "false") == 0) {
1394 		*cpp = skip_param(*cpp);
1395 		return true;
1396 	}
1397 	if (strcmp_tospace(*cpp, "yes") == 0
1398 	    || strcmp_tospace(*cpp, "on") == 0
1399 	    || strcmp_tospace(*cpp, "true") == 0) {
1400 		*cpp = skip_param(*cpp);
1401 	}
1402 	return false;
1403 }
1404 
1405 #if	!SECURE
1406 /*
1407  * Save set options in the startup file, displaying a confirmation message.
1408  * If a string is passed, then just save the string.
1409  *
1410  * Return true if successful.
1411  */
1412 int
save_set_options(cp)1413 save_set_options(cp)
1414 char	*cp;
1415 {
1416 	FILE	*fp;
1417 	int	pre_existing;
1418 
1419 	if (rc_file[0] == '\0') {
1420 		error(_("Set options startup file name not set; contact the developer."));
1421 		return false;
1422 	}
1423 	pre_existing = (access(rc_file, F_OK) == 0);
1424 	if ((fp = fopen(rc_file, "w")) == NULL) {
1425 		perror(rc_file);
1426 		error(_("Unable to write to set options startup file."));
1427 		return false;
1428 	}
1429 	fprintf(fp, "; Mathomatic set options loaded at startup,\n");
1430 	fprintf(fp, "; created by the \"set save\" command.\n");
1431 	fprintf(fp, "; This file can be edited or deleted.\n\n");
1432 	if (cp && *cp) {
1433 		fprintf(fp, "%s\n", cp);
1434 	} else {
1435 		output_options(fp, false);
1436 	}
1437 	if (fclose(fp) == 0) {
1438 		if (pre_existing)
1439 			printf(_("Startup file \"%s\" overwritten with set options.\n"), rc_file);
1440 		else
1441 			printf(_("Set options saved in startup file \"%s\".\n"), rc_file);
1442 	} else {
1443 		perror(rc_file);
1444 		error(_("Error saving set options."));
1445 		return false;
1446 	}
1447 	return true;
1448 }
1449 #endif
1450 
1451 /*
1452  * Handle parsing of options for the set command.
1453  *
1454  * Return false if error.
1455  */
1456 int
set_options(cp,loading_startup_file)1457 set_options(cp, loading_startup_file)
1458 char	*cp;
1459 int	loading_startup_file;
1460 {
1461 	int	i;
1462 	int	negate;
1463 	char	*cp1 = NULL, *option_string;
1464 
1465 	show_usage = false;	/* this command gives enough usage information */
1466 try_next_param:
1467 	cp = skip_comma_space(cp);
1468 	if (*cp == '\0') {
1469 		return true;
1470 	}
1471 	if (strncasecmp(cp, "directory", 3) == 0) {
1472 		cp = skip_param(cp);
1473 #if	!SECURE
1474 		if (security_level < 3) {
1475 	 		if (*cp == '\0') {
1476 				cp1 = getenv("HOME");
1477 				if (cp1 == NULL) {
1478 					error(_("HOME environment variable not set."));
1479 					return false;
1480 				}
1481 				cp = cp1;
1482 			}
1483 			if (chdir(cp)) {
1484 				perror(cp);
1485 				error(_("Error changing directory."));
1486 				return false;
1487 			}
1488 			printf(_("Current working directory changed to "));
1489 			return output_current_directory(stdout);
1490 		}
1491 #endif
1492 		error(_("Option disabled by security level."));
1493 		return false;
1494 	}
1495 	negate = skip_no(&cp);
1496 	option_string = cp;
1497 	cp = skip_param(cp);
1498 #if	!SILENT
1499 	if (strncasecmp(option_string, "debug", 5) == 0) {
1500 		if (negate) {
1501 			debug_level = 0;
1502 		} else {
1503 			i = decstrtol(cp, &cp1);
1504 			if (cp1 == NULL || cp == cp1) {
1505 				error(_("Please specify the debug level number from -2 to 6."));
1506 				return false;
1507 			}
1508 			cp = cp1;
1509 			debug_level = i;
1510 		}
1511 		goto try_next_param;
1512 	}
1513 #endif
1514 	if (strncasecmp(option_string, "special", 7) == 0) {
1515 		if (negate) {
1516 			special_variable_characters[0] = '\0';
1517 		} else {
1518 			for (i = 0; cp[i]; i++) {
1519 				if (is_mathomatic_operator(cp[i])) {
1520 					error(_("Invalid character in list, character is a Mathomatic operator."));
1521 					return false;
1522 				}
1523 			}
1524 			my_strlcpy(special_variable_characters, cp, sizeof(special_variable_characters));
1525 		}
1526 		return true;
1527 	}
1528 #if	SHELL_OUT
1529 	if (strncasecmp(option_string, "plot_prefix", 4) == 0) {
1530 		if (negate) {
1531 			plot_prefix[0] = '\0';
1532 		} else {
1533 			my_strlcpy(plot_prefix, cp, sizeof(plot_prefix));
1534 		}
1535 		return true;
1536 	}
1537 #endif
1538 	if (strncasecmp(option_string, "rows", 3) == 0) {
1539 		if (negate) {
1540 			screen_rows = 0;
1541 		} else {
1542 			if (*cp == '\0') {
1543 				printf(_("Current screen rows is %d.\n"), screen_rows);
1544 				goto check_return;
1545 			}
1546 			i = decstrtol(cp, &cp1);
1547 			if (i < 0 || cp1 == NULL || cp == cp1) {
1548 				error(_("Please specify how tall the screen is; 0 = no pagination."));
1549 				return false;
1550 			}
1551 			cp = cp1;
1552 			screen_rows = i;
1553 		}
1554 		goto try_next_param;
1555 	}
1556 	if (strncasecmp(option_string, "columns", 6) == 0) {
1557 		if (negate) {
1558 			screen_columns = 0;
1559 		} else {
1560 			if (*cp == '\0') {
1561 				if (!get_screen_size()) {
1562 					error(_("OS failed to return screen size."));
1563 					return false;
1564 				}
1565 				goto check_return;
1566 			}
1567 			i = decstrtol(cp, &cp1);
1568 			if (i < 0 || cp1 == NULL || cp == cp1) {
1569 				error(_("Please specify how wide the screen is; 0 = no limit."));
1570 				return false;
1571 			}
1572 			cp = cp1;
1573 			screen_columns = i;
1574 		}
1575 		goto try_next_param;
1576 	}
1577 	if (strncasecmp(option_string, "wide", 4) == 0) {
1578 		if (negate) {
1579 			if (!get_screen_size() || screen_columns == 0) {
1580 				error(_("OS failed to return screen size."));
1581 				return false;
1582 			}
1583 		} else {
1584 			screen_columns = 0;
1585 			screen_rows = 0;
1586 		}
1587 		goto try_next_param;
1588 	}
1589 	if (strncasecmp(option_string, "precision", 4) == 0) {
1590 		i = decstrtol(cp, &cp1);
1591 		if (i < 0 || i > 15 || cp1 == NULL || cp == cp1) {
1592 			error(_("Please specify a display precision between 0 and 15 digits."));
1593 			return false;
1594 		}
1595 		precision = i;
1596 		return true;
1597 	}
1598 	if (strcmp_tospace(option_string, "auto") == 0) {
1599 		autosolve = autocalc = autoselect = !negate;
1600 		goto try_next_param;
1601 	}
1602 	if (strncasecmp(option_string, "autosolve", 9) == 0) {
1603 		autosolve = !negate;
1604 		goto try_next_param;
1605 	}
1606 	if (strncasecmp(option_string, "autocalc", 8) == 0) {
1607 		autocalc = !negate;
1608 		goto try_next_param;
1609 	}
1610 	if (strncasecmp(option_string, "autodelete", 7) == 0) {
1611 		autodelete = !negate;
1612 		goto try_next_param;
1613 	}
1614 	if (strncasecmp(option_string, "autoselect", 10) == 0) {
1615 		autoselect = !negate;
1616 		goto try_next_param;
1617 	}
1618 	if (strncasecmp(option_string, "case", 4) == 0) {
1619 		case_sensitive_flag = !negate;
1620 		goto try_next_param;
1621 	}
1622 	if (strncasecmp(option_string, "display2d", 7) == 0) {
1623 		display2d = !negate;
1624 		goto try_next_param;
1625 	}
1626 	if (strncasecmp(option_string, "fractions", 4) == 0) {
1627 		if (negate) {
1628 			fractions_display = 0;
1629 		} else {
1630 			i = decstrtol(cp, &cp1);
1631 			if (cp == cp1) {
1632 				if (strcmp_tospace(cp, "none") == 0) {
1633 					cp1 = skip_param(cp);
1634 					i = 0;
1635 				} else if (strcmp_tospace(cp, "simple") == 0) {
1636 					cp1 = skip_param(cp);
1637 					i = 1;
1638 				} else if (strcmp_tospace(cp, "mixed") == 0) {
1639 					cp1 = skip_param(cp);
1640 					i = 2;
1641 				}
1642 			}
1643 			if (cp1 == NULL || cp == cp1 || i < 0 || i > 2) {
1644 				error(_("Please specify the fractions display mode number (0, 1, or 2)."));
1645 				printf(_("0 means do not display any constants as fractions,\n"));
1646 				printf(_("1 means display some constants as \"simple\" fractions,\n"));
1647                 		printf(_("2 means display some constants as \"mixed\" or simple fractions.\n"));
1648 				printf(_("Current value is %d.\n"), fractions_display);
1649 				return false;
1650 			}
1651 			cp = cp1;
1652 			fractions_display = i;
1653 		}
1654 		goto try_next_param;
1655 	}
1656 	if (strncasecmp(option_string, "prompt", 6) == 0) {
1657 		quiet_mode = negate;
1658 		goto try_next_param;
1659 	}
1660 	if (strncasecmp(option_string, "demo", 4) == 0) {
1661 		demo_mode = !negate;
1662 		goto try_next_param;
1663 	}
1664 	if (strncasecmp(option_string, "html", 4) == 0) {
1665 #if	!SECURE
1666 		if (security_level > 0) {
1667 #endif
1668 			error(_("Option disabled by security level."));
1669 			return false;
1670 #if	!SECURE
1671 		}
1672 #endif
1673 		reset_attr();
1674 		if (is_all(cp)) {
1675 			cp = skip_param(cp);
1676 			if (negate)
1677 				html_flag = 0;
1678 			else
1679 				html_flag = 2;
1680 		} else {
1681 			html_flag = !negate;
1682 		}
1683 		goto try_next_param;
1684 	}
1685 	if (strncasecmp(option_string, "preserve_surds", 13) == 0) {
1686 		preserve_surds = !negate;
1687 		goto try_next_param;
1688 	}
1689 	if (strncasecmp(option_string, "rationalize", 11) == 0) {
1690 		rationalize_denominators = !negate;
1691 		goto try_next_param;
1692 	}
1693 	if (strncasecmp(option_string, "modulus_mode", 3) == 0) {
1694 		if (negate) {
1695 			error(_("Modulus mode cannot be turned off."));
1696 			return false;
1697 		} else {
1698 			i = decstrtol(cp, &cp1);
1699 			if (cp == cp1) {
1700 				if (strcmp_tospace(cp, "C") == 0 || strcmp_tospace(cp, "java") == 0) {
1701 					cp1 = skip_param(cp);
1702 					i = 0;
1703 				} else if (strcmp_tospace(cp, "python") == 0) {
1704 					cp1 = skip_param(cp);
1705 					i = 1;
1706 				} else if (strcmp_tospace(cp, "positive") == 0 || strcmp_tospace(cp, "normal") == 0) {
1707 					cp1 = skip_param(cp);
1708 					i = 2;
1709 				}
1710 			}
1711 			if (cp1 == NULL || cp == cp1 || i < 0 || i > 2) {
1712 				error(_("Please specify the modulus mode number (0, 1, or 2)."));
1713 				printf(_("* \"C\" and \"Java\" programming language mode 0:\n"));
1714 				printf(_("  0 means modulus operator (dividend %% divisor) result has same sign as dividend;\n"));
1715 				printf(_("* \"Python\" programming language mode 1:\n"));
1716 				printf(_("  1 means computed result always has same sign as the divisor;\n"));
1717 				printf(_("* Mathematically correct mode 2 for perfect simplification:\n"));
1718                 		printf(_("  2 means the result is always \"positive\" or zero (\"normal\" mode).\n\n"));
1719 				printf(_("The current value is %d ("), modulus_mode);
1720 				switch (modulus_mode) {
1721 				case 0:
1722 					printf("C");
1723 					break;
1724 				case 1:
1725 					printf("Python");
1726 					break;
1727 				case 2:
1728 					printf("normal");
1729 					break;
1730 				default:
1731 					printf("unknown");
1732 					break;
1733 				}
1734 				printf(_(" mode).\n"));
1735 				return false;
1736 			}
1737 			cp = cp1;
1738 			modulus_mode = i;
1739 		}
1740 		goto try_next_param;
1741 	}
1742 	if (strncasecmp(option_string, "color", 5) == 0) {
1743 		reset_attr();
1744 		if (color_flag != 2 || negate) {
1745 			color_flag = !negate;
1746 		}
1747 		i = decstrtol(cp, &cp1);
1748 		if (cp1 && cp != cp1) {
1749 			text_color = i;
1750 			cp = cp1;
1751 		} else {
1752 			text_color = -1;
1753 		}
1754 		goto try_next_param;
1755 	}
1756 	if (strncasecmp(option_string, "alternative", 3) == 0) {
1757 		reset_attr();
1758 		color_flag = (!negate) + 1;
1759 		goto try_next_param;
1760 	}
1761 	if (strncasecmp(option_string, "bold", 4) == 0) {
1762 		reset_attr();
1763 		bold_colors = !negate;
1764 		goto try_next_param;
1765 	}
1766 	if (strncasecmp(option_string, "fixed", 3) == 0) {
1767 		if (negate) {
1768 			finance_option = -1;
1769 		} else {
1770 			i = decstrtol(cp, &cp1);
1771 			if (cp1 == NULL) {
1772 				return false;
1773 			}
1774 			if (cp == cp1) {
1775 				if (*cp1 == '\0') {
1776 					i = 2;
1777 				} else {
1778 					error(_("Please specify the number of digits to display after the decimal point."));
1779 					return false;
1780 				}
1781 			}
1782 			if (i < -1 || i > 100) {
1783 				error(_("Range is -1 to 100; Sets rounded display with fixed number of trailing digits."));
1784 				return false;
1785 			}
1786 			if (i == 0) {
1787 				warning("Setting rounded, integer-only display.");
1788 			}
1789 			cp = cp1;
1790 			finance_option = i;
1791 		}
1792 		goto try_next_param;
1793 	}
1794 	if (strncasecmp(option_string, "factor_integers", 6) == 0) {
1795 		factor_int_flag = !negate;
1796 		goto try_next_param;
1797 	}
1798 	if (strncasecmp(option_string, "right_associative_power", 5) == 0) {
1799 		right_associative_power = !negate;
1800 		goto try_next_param;
1801 	}
1802 	if (strcmp_tospace(option_string, "load") == 0) {
1803 #if	!SECURE
1804 		if (negate) {
1805 			printf(_("Doing nothing.\n"));
1806 			return true;
1807 		}
1808 		if (loading_startup_file) {
1809 			printf(_("Ignoring recursive \"set load\".\n"));
1810 			return true;
1811 		}
1812 		if (extra_characters(cp))
1813 			return false;
1814 		if (security_level <= 3) {
1815 			if (load_rc(false, gfp)) {
1816 				fprintf(gfp, _("\nEnd of file.\n"));
1817 				return true;
1818 			} else {
1819 				error(_("Error loading startup set options."));
1820 				return false;
1821 			}
1822 		}
1823 #endif
1824 		error(_("Option disabled by security level."));
1825 		return false;
1826 	}
1827 	if (strcmp_tospace(option_string, "save") == 0) {
1828 #if	!SECURE
1829 		if (security_level < 2) {
1830 			if (rc_file[0] == '\0') {
1831 				error(_("Set options startup file name not set; contact the developer."));
1832 				return false;
1833 			}
1834 			if (loading_startup_file) {
1835 				printf(_("Got \"set save\" while loading startup options, quitting.\n"));
1836 				return false;
1837 			}
1838 			if (negate) {
1839 				if (extra_characters(cp))
1840 					return false;
1841 				if (unlink(rc_file) == 0) {
1842 					printf(_("Set options startup file \"%s\" removed.\n"), rc_file);
1843 					printf(_("Factory default options will be used on next startup of Mathomatic.\n"));
1844 					return true;
1845 				} else {
1846 					perror(rc_file);
1847 					error(_("Set options startup file cannot be removed."));
1848 					return false;
1849 				}
1850 			} else {
1851 				if (save_set_options(cp)) {
1852 					if (load_rc(false, gfp)) {
1853 						fprintf(gfp, _("\nNew startup set options loaded.\n"));
1854 						return true;
1855 					} else {
1856 						error(_("Error loading new startup set options."));
1857 						fprintf(gfp, _("Correct or type \"set no save\" to remove.\n"));
1858 					}
1859 				}
1860 				return false;
1861 			}
1862 		}
1863 #endif
1864 		error(_("Option disabled by security level."));
1865 		return false;
1866 	}
1867 	if (strcmp_tospace(option_string, "set") == 0) {
1868 		if (!negate)
1869 			goto try_next_param;
1870 	}
1871 	printf(_("\nCannot process set string \"%s\".\n"), option_string);
1872 	error(_("Unknown set option."));
1873 	return false;
1874 
1875 check_return:
1876 	extra_characters(cp);
1877 	return true;
1878 }
1879 
1880 /*
1881  * The set command.
1882  */
1883 int
set_cmd(cp)1884 set_cmd(cp)
1885 char	*cp;
1886 {
1887 	int	rv;
1888 
1889 	if (*cp == '\0') {
1890 		fprintf(gfp, _("Options are set as follows:\n\n"));
1891 
1892 		output_options(gfp, true);
1893 
1894 		output_current_directory(gfp);
1895 		return true;
1896 	}
1897 	rv = set_options(cp, false);
1898 	if (rv) {
1899 		debug_string(0, _("Success."));
1900 	}
1901 	return rv;
1902 }
1903 
1904 /*
1905  * The echo command.
1906  */
1907 int
echo_cmd(cp)1908 echo_cmd(cp)
1909 char	*cp;
1910 {
1911 	int	i;
1912 	int	len = 0;
1913 	int	width, height;
1914 
1915 	if (repeat_flag) {
1916 		if (*cp) {
1917 			if (screen_columns)
1918 				width = screen_columns;
1919 			else
1920 				width = TEXT_COLUMNS;
1921 			while ((len + strlen(cp)) < width) {
1922 				fprintf(gfp, "%s", cp);
1923 				len += strlen(cp);
1924 			}
1925 			fprintf(gfp, "\n");
1926 		} else {
1927 			if (screen_rows)
1928 				height = screen_rows;
1929 			else
1930 				height = TEXT_ROWS;
1931 			for (i = 0; i < height; i++) {
1932 				fprintf(gfp, "\n");
1933 			}
1934 		}
1935 	} else {
1936 		fprintf(gfp, "%s\n", cp);
1937 	}
1938 	return true;
1939 }
1940 
1941 /*
1942  * The pause command.
1943  */
1944 int
pause_cmd(cp)1945 pause_cmd(cp)
1946 char	*cp;
1947 {
1948 #if	LIBRARY
1949 	return true;
1950 #else
1951 	char	*cp1;
1952 	char	buf[MAX_CMD_LEN];
1953 
1954 	if (test_mode || demo_mode) {
1955 		return true;
1956 	}
1957 	show_usage = false;
1958 	if (*cp == '\0') {
1959 		cp = _("Please press the Enter key");
1960 	}
1961 	set_color(3);	/* make color blue, to show that this is not part of the surrounding text */
1962 	snprintf(prompt_str, sizeof(prompt_str), "==== %s: ", cp);
1963 	cp1 = get_string(buf, sizeof(buf));
1964 	default_color(false);
1965 	if (cp1 == NULL) {
1966 		return false;
1967 	}
1968 	if (strncasecmp(cp1, "quit", 4) == 0) {
1969 		return false;
1970 	}
1971 	if (strncasecmp(cp1, "exit", 4) == 0) {
1972 		return false;
1973 	}
1974 	return true;
1975 #endif
1976 }
1977 
1978 /*
1979  * The copy command.
1980  */
1981 int
copy_cmd(cp)1982 copy_cmd(cp)
1983 char	*cp;
1984 {
1985 	int	i, j, k;
1986 	int	i1;
1987 	char	exists[N_EQUATIONS];
1988 	char	*cp1;
1989 	int	select_flag = false;	/* select the first equation space copied to */
1990 
1991 	CLEAR_ARRAY(exists);
1992 	for (i1 = 0; i1 < n_equations; i1++) {
1993 		if (n_lhs[i1] > 0) {
1994 			exists[i1] = true;
1995 		}
1996 	}
1997 	if (strncasecmp(cp, "select", 3) == 0) {
1998 		select_flag = true;
1999 		cp = skip_param(cp);
2000 	}
2001 	do {
2002 		cp1 = cp;
2003 		if (!get_range(&cp, &i, &j)) {
2004 			return false;
2005 		}
2006 		if (*cp && cp == cp1) {
2007 			error(_("Invalid equation number range."));
2008 			return false;
2009 		}
2010 		for (i1 = i; i1 <= j; i1++) {
2011 			if (exists[i1]) {
2012 				k = next_espace();
2013 				copy_espace(i1, k);
2014 				if (!return_result(k)) {
2015 					return false;
2016 				}
2017 				if (select_flag) {
2018 					cur_equation = k;
2019 					select_flag = false;
2020 				}
2021 			}
2022 		}
2023 	} while (*cp);
2024 	return true;
2025 }
2026 
2027 /*
2028  * Common function for the imaginary and real commands.
2029  */
2030 static int
complex_func(cp,imag_flag)2031 complex_func(cp, imag_flag)
2032 char	*cp;		/* the command-line */
2033 int	imag_flag;	/* if true, copy the imaginary part, otherwise copy the real part */
2034 {
2035 	int		i, j, k;
2036 	int		beg;
2037 	int		found_imag, has_imag, has_real, solved;
2038 	token_type	*source, *dest;
2039 	int		n1, *nps, *np;
2040 	long		v = IMAGINARY;			/* separation variable */
2041 
2042 	if (current_not_defined()) {
2043 		return false;
2044 	}
2045 	solved = solved_equation(cur_equation);
2046 	i = cur_equation;
2047 	j = next_espace();
2048 	if (n_rhs[i]) {
2049 		source = rhs[i];
2050 		nps = &n_rhs[i];
2051 		dest = rhs[j];
2052 		np = &n_rhs[j];
2053 	} else {
2054 		source = lhs[i];
2055 		nps = &n_lhs[i];
2056 		dest = lhs[j];
2057 		np = &n_lhs[j];
2058 	}
2059 	if (*cp) {
2060 		cp = parse_var2(&v, cp);
2061 		if (cp == NULL) {
2062 			return false;
2063 		}
2064 		if (extra_characters(cp))
2065 			return false;
2066 	}
2067 	simp_loop(source, nps);
2068 	uf_simp(source, nps);
2069 	factorv(source, nps, v);
2070 	partial_flag = false;
2071 	uf_simp(source, nps);
2072 	partial_flag = true;
2073 	n1 = 1;
2074 	dest[0] = zero_token;
2075 	has_imag = has_real = false;
2076 	for (beg = k = 0; beg < *nps; beg = k, k++) {
2077 		for (found_imag = false; k < *nps; k++) {
2078 			if (source[k].level == 1 && source[k].kind == OPERATOR
2079 			    && (source[k].token.operatr == PLUS || source[k].token.operatr == MINUS)) {
2080 				break;
2081 			}
2082 			if (source[k].kind == VARIABLE && source[k].token.variable == v) {
2083 				found_imag = true;
2084 			}
2085 		}
2086 		if (found_imag)
2087 			has_imag = true;
2088 		else
2089 			has_real = true;
2090 		if (found_imag == imag_flag) {
2091 			if (beg == 0) {
2092 				n1 = 0;
2093 			}
2094 			blt(&dest[n1], &source[beg], (k - beg) * sizeof(token_type));
2095 			n1 += (k - beg);
2096 		}
2097 	}
2098 	if (!has_imag || !has_real) {
2099 		warning(_("Expression was not a mix."));
2100 	}
2101 	do {
2102 		simp_loop(dest, &n1);
2103 	} while (factor_plus(dest, &n1, v, 0.0));
2104 	simp_divide(dest, &n1);
2105 	if (n_rhs[i]) {
2106 		blt(lhs[j], lhs[i], n_lhs[i] * sizeof(token_type));
2107 		n_lhs[j] = n_lhs[i];
2108 		if (solved) {
2109 			if (list_var(lhs[j][0].token.variable, 0) < (MAX_VAR_LEN - 5)) {
2110 				if (imag_flag)
2111 					strcat(var_str, "_imag");
2112 				else
2113 					strcat(var_str, "_real");
2114 				parse_var(&lhs[j][0].token.variable, var_str);
2115 			}
2116 		}
2117 	}
2118 	*np = n1;
2119 	cur_equation = j;
2120 	return return_result(cur_equation);
2121 }
2122 
2123 /*
2124  * The real command.
2125  */
2126 int
real_cmd(cp)2127 real_cmd(cp)
2128 char	*cp;
2129 {
2130 	return complex_func(cp, false);
2131 }
2132 
2133 /*
2134  * The imaginary command.
2135  */
2136 int
imaginary_cmd(cp)2137 imaginary_cmd(cp)
2138 char	*cp;
2139 {
2140 	return complex_func(cp, true);
2141 }
2142 
2143 #if	!LIBRARY
2144 /*
2145  * The tally command.
2146  */
2147 int
tally_cmd(cp)2148 tally_cmd(cp)
2149 char	*cp;
2150 {
2151 	int	i, k, first, last;
2152 	double	count = 0.0;
2153 	int	arithmetic_mean = false;
2154 	long	v;
2155 	char	*cp1;
2156 
2157 	if (!parse_var(&v, "total")) {
2158 		return false;
2159 	}
2160 	if (strcmp_tospace(cp, "average") == 0) {
2161 		arithmetic_mean = true;
2162 		cp = skip_param(cp);
2163 	}
2164 	trhs[0] = zero_token;
2165 	n_trhs = 1;
2166 	if (*cp) {
2167 		do {
2168 			cp1 = cp;
2169 			if (!get_range(&cp, &first, &last)) {
2170 				return false;
2171 			}
2172 			if (*cp && cp == cp1) {
2173 				error(_("Invalid argument.  Expecting equation number or range."));
2174 				return false;
2175 			}
2176 			for (k = first; k <= last; k++) {
2177 				if (n_lhs[k] <= 0)
2178 					continue;
2179 				if (n_rhs[k] > 0) {
2180 					if ((n_trhs + 1 + n_rhs[k]) > n_tokens) {
2181 						error_huge();
2182 					}
2183 					for (i = 0; i < n_trhs; i++) {
2184 						trhs[i].level++;
2185 					}
2186 					trhs[n_trhs].kind = OPERATOR;
2187 					trhs[n_trhs].level = 1;
2188 					trhs[n_trhs].token.operatr = PLUS;
2189 					n_trhs++;
2190 					blt(&trhs[n_trhs], rhs[k], n_rhs[k] * sizeof(token_type));
2191 					n_trhs += n_rhs[k];
2192 				} else {
2193 					if ((n_trhs + 1 + n_lhs[k]) > n_tokens) {
2194 						error_huge();
2195 					}
2196 					for (i = 0; i < n_trhs; i++) {
2197 						trhs[i].level++;
2198 					}
2199 					trhs[n_trhs].kind = OPERATOR;
2200 					trhs[n_trhs].level = 1;
2201 					trhs[n_trhs].token.operatr = PLUS;
2202 					n_trhs++;
2203 					blt(&trhs[n_trhs], lhs[k], n_lhs[k] * sizeof(token_type));
2204 					n_trhs += n_lhs[k];
2205 				}
2206 				for (i++; i < n_trhs; i++) {
2207 					trhs[i].level++;
2208 				}
2209 				calc_simp(trhs, &n_trhs);
2210 				count++;
2211 			}
2212 		} while (*cp);
2213 	}
2214 	if (extra_characters(cp)) {
2215 		return false;
2216 	}
2217 	for (;; count++) {
2218 		fprintf(gfp, _("total = "));
2219 		list_proc(trhs, n_trhs, false);
2220 		fprintf(gfp, "\n");
2221 		if (count > 0) {
2222 			if (arithmetic_mean) {
2223 				/* calculate and display the average */
2224 				blt(tlhs, trhs, n_trhs * sizeof(token_type));
2225 				n_tlhs = n_trhs;
2226 				if ((n_tlhs + 2) > n_tokens) {
2227 					error_huge();
2228 				}
2229 				for (i = 0; i < n_tlhs; i++) {
2230 					tlhs[i].level++;
2231 				}
2232 				tlhs[n_tlhs].kind = OPERATOR;
2233 				tlhs[n_tlhs].level = 1;
2234 				tlhs[n_tlhs].token.operatr = DIVIDE;
2235 				n_tlhs++;
2236 				tlhs[n_tlhs].kind = CONSTANT;
2237 				tlhs[n_tlhs].level = 1;
2238 				tlhs[n_tlhs].token.constant = count;
2239 				n_tlhs++;
2240 				calc_simp(tlhs, &n_tlhs);
2241 				fprintf(gfp, _("count = %.0f, average = "), count);
2242 				list_proc(tlhs, n_tlhs, false);
2243 				fprintf(gfp, "\n");
2244 			}
2245 		}
2246 		fprintf(gfp, "\n");
2247 		my_strlcpy(prompt_str, _("Enter value to add: "), sizeof(prompt_str));
2248 		if (!get_expr(tlhs, &n_tlhs)) {
2249 			break;
2250 		}
2251 		if ((n_trhs + 1 + n_tlhs) > n_tokens) {
2252 			error_huge();
2253 		}
2254 		for (i = 0; i < n_tlhs; i++) {
2255 			tlhs[i].level++;
2256 		}
2257 		for (i = 0; i < n_trhs; i++) {
2258 			trhs[i].level++;
2259 		}
2260 		trhs[n_trhs].kind = OPERATOR;
2261 		trhs[n_trhs].level = 1;
2262 		trhs[n_trhs].token.operatr = PLUS;
2263 		n_trhs++;
2264 		blt(&trhs[n_trhs], tlhs, n_tlhs * sizeof(token_type));
2265 		n_trhs += n_tlhs;
2266 		calc_simp(trhs, &n_trhs);
2267 	}
2268 	fprintf(gfp, _("End.\n"));
2269 	if (count > 0) {
2270 		i = next_espace();
2271 		lhs[i][0].level = 1;
2272 		lhs[i][0].kind = VARIABLE;
2273 		lhs[i][0].token.variable = v;
2274 		n_lhs[i] = 1;
2275 		blt(rhs[i], trhs, n_trhs * sizeof(token_type));
2276 		n_rhs[i] = n_trhs;
2277 		cur_equation = i;
2278 		return return_result(cur_equation);
2279 	}
2280 	return true;
2281 }
2282 #endif
2283 
2284 #if	!LIBRARY
2285 /*
2286  * The calculate command.
2287  */
2288 int
calculate_cmd(cp)2289 calculate_cmd(cp)
2290 char	*cp;
2291 {
2292 	int		i, k1, k;
2293 	int		first, last;
2294 	long		v, last_v, it_v = 0;	/* Mathomatic variables */
2295 	long		counter, counter_max;
2296 	sign_array_type	sa_mark, sa_value;
2297 	long		l, iterations = 1;
2298 	token_type	*source;
2299 	int		n;
2300 	int		diff_sign;
2301 	char		buf[MAX_CMD_LEN];
2302 	int		factor_flag = false, value_entered;
2303 
2304 	for (;; cp = skip_param(cp)) {
2305 		if (strcmp_tospace(cp, "factor") == 0) {
2306 			factor_flag = true;
2307 			continue;
2308 		}
2309 		break;
2310 	}
2311 	if (!get_range(&cp, &first, &last)) {
2312 		return false;
2313 	}
2314 	if (*cp) {
2315 		cp = parse_var2(&it_v, cp);
2316 		if (cp == NULL) {
2317 			return false;
2318 		}
2319 		if (*cp == '\0') {
2320 			my_strlcpy(prompt_str, _("Enter maximum number of iterations: "), sizeof(prompt_str));
2321 			if ((cp = get_string(buf, sizeof(buf))) == NULL)
2322 				return false;
2323 		}
2324 		iterations = decstrtol(cp, &cp);
2325 		if (*cp || iterations < 0) {
2326 			error(_("Positive integer required."));
2327 			return false;
2328 		}
2329 		if (iterations == 0) {
2330 			warning(_("Feedback calculation will be iterated until convergence."));
2331 			iterations = LONG_MAX - 1L;
2332 		}
2333 	}
2334 	if (extra_characters(cp)) {
2335 		return false;
2336 	}
2337 calc_again:
2338 	value_entered = false;
2339 	for (i = first; i <= last; i++) {
2340 		if (n_rhs[i]) {
2341 			source = rhs[i];
2342 			n = n_rhs[i];
2343 		} else {
2344 			source = lhs[i];
2345 			n = n_lhs[i];
2346 		}
2347 		if (it_v) {
2348 			if (!found_var(source, n, it_v)) {
2349 				debug_string((first == last) ? 0 : 1, _("Specified feedback variable not found."));
2350 				continue;
2351 			}
2352 		}
2353 		n_trhs = n;
2354 		blt(trhs, source, n_trhs * sizeof(token_type));
2355 		last_v = 0;
2356 		for (;;) {
2357 			v = -1;
2358 			for (k1 = 0; k1 < n; k1 += 2) {
2359 				if (source[k1].kind == VARIABLE) {
2360 					if (source[k1].token.variable > last_v
2361 					    && (v == -1 || source[k1].token.variable < v))
2362 						v = source[k1].token.variable;
2363 				}
2364 			}
2365 			if (v == -1)
2366 				break;
2367 			last_v = v;
2368 			if ((v & VAR_MASK) <= SIGN || v == it_v) {
2369 				continue;
2370 			}
2371 			if (test_mode || demo_mode)
2372 				continue;
2373 			list_var(v, 0);
2374 			snprintf(prompt_str, sizeof(prompt_str), _("Enter %s: "), var_str);
2375 			if (!get_expr(tlhs, &n_tlhs)) {
2376 				continue;
2377 			}
2378 			value_entered = true;
2379 			/* Disguise all variables in the entered expression by making them negative. */
2380 			/* That way they won't be improperly substituted in the future. */
2381 			for (k1 = 0; k1 < n_tlhs; k1 += 2)
2382 				if (tlhs[k1].kind == VARIABLE)
2383 					tlhs[k1].token.variable = -tlhs[k1].token.variable;
2384 			subst_var_with_exp(trhs, &n_trhs, tlhs, n_tlhs, v);
2385 		}
2386 		/* Restore disguised variables: */
2387 		for (k1 = 0; k1 < n_trhs; k1 += 2)
2388 			if (trhs[k1].kind == VARIABLE && trhs[k1].token.variable < 0)
2389 				trhs[k1].token.variable = -trhs[k1].token.variable;
2390 		if (it_v) {
2391 			/* Handle the iteration option, where the simplified result is repeatedly plugged into variable it_v. */
2392 			list_var(it_v, 0);
2393 			snprintf(prompt_str, sizeof(prompt_str), _("Enter initial %s: "), var_str);
2394 			while (!get_expr(tes, &n_tes)) {
2395 				printf("Aborted.\n");
2396 				return repeat_flag;
2397 			}
2398 			value_entered = true;
2399 			calc_simp(tes, &n_tes);
2400 			blt(tlhs, trhs, n_trhs * sizeof(token_type));
2401 			n_tlhs = n_trhs;
2402 			for (l = 0;; l++) {
2403 				if (l >= iterations) {
2404 					fprintf(gfp, _("%ld feedback iterations performed.\n"), l);
2405 					break;
2406 				}
2407 				side_debug(1, tes, n_tes);
2408 				blt(trhs, tlhs, n_tlhs * sizeof(token_type));
2409 				n_trhs = n_tlhs;
2410 				subst_var_with_exp(trhs, &n_trhs, tes, n_tes, it_v);
2411 				calc_simp(trhs, &n_trhs);
2412 				if (se_compare(trhs, n_trhs, tes, n_tes, &diff_sign) && !diff_sign) {
2413 					fprintf(gfp, _("Convergence reached after %ld iterations.\n"), l + 1);
2414 					break;
2415 				}
2416 				blt(tes, trhs, n_trhs * sizeof(token_type));
2417 				n_tes = n_trhs;
2418 			}
2419 		}
2420 		calc_simp(trhs, &n_trhs);
2421 
2422 		/* Now substitute all sign variables with +1 and -1. */
2423 		CLEAR_ARRAY(sa_mark);
2424 		for (k1 = 0; k1 < n_trhs; k1 += 2) {
2425 			if (trhs[k1].kind == VARIABLE && (trhs[k1].token.variable & VAR_MASK) == SIGN) {
2426 				sa_mark[(trhs[k1].token.variable >> VAR_SHIFT) & SUBSCRIPT_MASK] = true;
2427 			}
2428 		}
2429 		for (k1 = 0, k = 0; k1 < ARR_CNT(sa_mark); k1++) {
2430 			if (sa_mark[k1]) {
2431 				k++;
2432 			}
2433 		}
2434 		counter_max = (1L << k) - 1L;
2435 		if (counter_max) {
2436 			fprintf(gfp, _("There are %ld solutions.\n"), counter_max + 1);
2437 		}
2438 		for (counter = 0; counter <= counter_max; counter++) {
2439 			blt(tlhs, trhs, n_trhs * sizeof(token_type));
2440 			n_tlhs = n_trhs;
2441 			for (k1 = 0, k = 0; k1 < ARR_CNT(sa_mark); k1++) {
2442 				if (sa_mark[k1]) {
2443 					sa_value[k1] = (((1L << k) & counter) != 0);
2444 					k++;
2445 				}
2446 			}
2447 			for (k1 = 0; k1 < n_tlhs; k1 += 2) {
2448 				if (tlhs[k1].kind == VARIABLE && (tlhs[k1].token.variable & VAR_MASK) == SIGN) {
2449 					if (sa_value[(tlhs[k1].token.variable >> VAR_SHIFT) & SUBSCRIPT_MASK]) {
2450 						tlhs[k1].kind = CONSTANT;
2451 						tlhs[k1].token.constant = -1.0;
2452 					} else {
2453 						tlhs[k1].kind = CONSTANT;
2454 						tlhs[k1].token.constant = 1.0;
2455 					}
2456 				}
2457 			}
2458 			for (k1 = 0, k = false; k1 < ARR_CNT(sa_mark); k1++) {
2459 				if (sa_mark[k1]) {
2460 					if (k) {
2461 						fprintf(gfp, ", ");
2462 					} else {
2463 						fprintf(gfp, _("\nSolution number %ld with "), counter + 1);
2464 					}
2465 					list_var((long) SIGN + (((long) k1) << VAR_SHIFT), 0);
2466 					fprintf(gfp, "%s = ", var_str);
2467 					if (sa_value[k1]) {
2468 						fprintf(gfp, "-1");
2469 					} else {
2470 						fprintf(gfp, "1");
2471 					}
2472 					k = true;
2473 				}
2474 			}
2475 			if (k)
2476 				fprintf(gfp, ":\n");
2477 			calc_simp(tlhs, &n_tlhs);
2478 			if (factor_flag) {
2479 				mid_simp_side(tlhs, &n_tlhs);
2480 			}
2481 			fprintf(gfp, " ");
2482 			if (n_rhs[i]) {
2483 				list_proc(lhs[i], n_lhs[i], false);
2484 				fprintf(gfp, " = ");
2485 			}
2486 			list_factor(tlhs, &n_tlhs, factor_flag);
2487 			if (fractions_display && n_tlhs <= 9 && make_fractions(tlhs, &n_tlhs)) {
2488 				group_proc(tlhs, &n_tlhs);
2489 				fprintf(gfp, ", with fractions it is: ");
2490 				list_factor(tlhs, &n_tlhs, factor_flag);
2491 			}
2492 			fprintf(gfp, "\n");
2493 		}
2494 	}
2495 	if (value_entered && repeat_flag) {
2496 		fprintf(gfp, "Repeating:\n");
2497 		goto calc_again;
2498 	}
2499 	return true;
2500 }
2501 #endif
2502 
2503 /*
2504  * The clear command.
2505  */
2506 int
clear_cmd(cp)2507 clear_cmd(cp)
2508 char	*cp;
2509 {
2510 	int	i, j;
2511 	char	*cp1;
2512 
2513 	do {
2514 		cp1 = cp;
2515 		if (is_all(cp)) {
2516 			clear_all();
2517 			return true;
2518 		} else {
2519 			if (!get_range(&cp, &i, &j)) {
2520 				return false;
2521 			}
2522 			if (*cp && cp == cp1) {
2523 				error(_("Invalid argument.  Expecting equation number or range."));
2524 				return false;
2525 			}
2526 			for (; i <= j; i++) {
2527 				n_lhs[i] = 0;
2528 				n_rhs[i] = 0;
2529 			}
2530 		}
2531 	} while (*cp);
2532 	return true;
2533 }
2534 
2535 /*
2536  * Compare the Right Hand Sides of two equation spaces.
2537  */
2538 static int
compare_rhs(i,j,diff_signp)2539 compare_rhs(i, j, diff_signp)
2540 int	i, j;
2541 int	*diff_signp;
2542 {
2543 	int	rv;
2544 
2545 /* First, test the compare function by comparing with self: */
2546 	rv = se_compare(rhs[i], n_rhs[i], rhs[i], n_rhs[i], diff_signp);
2547 	if (!rv || *diff_signp) {
2548 		error(_("Too many terms to compare."));
2549 		return false;
2550 	}
2551 /* Now do the requested compare: */
2552 	sign_cmp_flag = true;
2553 	rv = se_compare(rhs[i], n_rhs[i], rhs[j], n_rhs[j], diff_signp);
2554 	sign_cmp_flag = false;
2555 	return rv;
2556 }
2557 
2558 /*
2559  * Compare two equation spaces.
2560  *
2561  * Return 0 if they differ.
2562  * Return 1 if identical.  Return -1 if they are expressions that differ only in sign.
2563  */
2564 int
compare_es(i,j)2565 compare_es(i, j)
2566 int	i, j;	/* equation space numbers */
2567 {
2568 	int	rv;
2569 	int	diff_sign_lhs, diff_sign_rhs;
2570 
2571 	if (n_lhs[i] == 0 || n_lhs[j] == 0)
2572 		return false;	/* empty equation space */
2573 	if ((n_rhs[i] == 0) != (n_rhs[j] == 0))
2574 		return false;	/* mix of expression and equation */
2575 /* Compare the two left hand sides: */
2576 	sign_cmp_flag = true;
2577 	rv = se_compare(lhs[i], n_lhs[i], lhs[j], n_lhs[j], &diff_sign_lhs);
2578 	sign_cmp_flag = false;
2579 	if (!rv)
2580 		return false;
2581 	if (n_rhs[i] == 0 && n_rhs[j] == 0) {
2582 		/* two expressions, not equations */
2583 		if (diff_sign_lhs)
2584 			return -1;
2585 		else
2586 			return 1;
2587 	}
2588 /* They are equations, so compare the two right hand sides: */
2589 	sign_cmp_flag = true;
2590 	rv = se_compare(rhs[i], n_rhs[i], rhs[j], n_rhs[j], &diff_sign_rhs);
2591 	sign_cmp_flag = false;
2592 	if (!rv)
2593 		return false;
2594 	return(diff_sign_lhs == diff_sign_rhs);
2595 }
2596 
2597 /*
2598  * The compare command.
2599  */
2600 int
compare_cmd(cp)2601 compare_cmd(cp)
2602 char	*cp;
2603 {
2604 	int		i, j;
2605 	int		diff_sign;
2606 	int		symb = false, approx = false;
2607 
2608 	for (;; cp = skip_param(cp)) {
2609 		if (strncasecmp(cp, "symbolic", 4) == 0) {
2610 			symb = true;
2611 			continue;
2612 		}
2613 		if (strncasecmp(cp, "approximate", 4) == 0) {
2614 			approx = true;
2615 			continue;
2616 		}
2617 		break;
2618 	}
2619 	if (strcmp_tospace(cp, "with") == 0) {
2620 		cp = skip_param(cp);
2621 	}
2622 	i = decstrtol(cp, &cp) - 1;
2623 	if (not_defined(i)) {
2624 		return false;
2625 	}
2626 	if (strcmp_tospace(cp, "with") == 0) {
2627 		cp = skip_param(cp);
2628 	}
2629 	if ((j = get_default_en(cp)) < 0) {
2630 		return false;
2631 	}
2632 	if (i == j) {
2633 		error(_("Cannot compare an expression with itself."));
2634 		return false;
2635 	}
2636 	show_usage = false;
2637 	fprintf(gfp, _("Comparing #%d with #%d...\n"), i + 1, j + 1);
2638 	simp_equation(i);
2639 	simp_equation(j);
2640 	if (n_rhs[i] == 0 || n_rhs[j] == 0) {
2641 		if (n_rhs[i] == 0 && n_rhs[j] == 0) {
2642 			switch (compare_es(i, j)) {
2643 			case 1:
2644 				fprintf(gfp, _("Expressions are identical.\n"));
2645 				return true;
2646 			case -1:
2647 				error(_("Expressions differ only in sign (times -1)."));
2648 				return false;
2649 			}
2650 			if (approx) {
2651 				debug_string(0, _("Approximating both expressions..."));
2652 				approximate(lhs[i], &n_lhs[i]);
2653 				approximate(lhs[j], &n_lhs[j]);
2654 				switch (compare_es(i, j)) {
2655 				case 1:
2656 					fprintf(gfp, _("Expressions are identical.\n"));
2657 					return true;
2658 				case -1:
2659 					error(_("Expressions differ only in sign (times -1)."));
2660 					return false;
2661 				}
2662 			}
2663 			debug_string(0, _("Simplifying both expressions..."));
2664 			symb_flag = symb;
2665 			simpa_repeat_side(lhs[i], &n_lhs[i], false, true);
2666 			simpa_repeat_side(lhs[j], &n_lhs[j], false, true);
2667 			symb_flag = false;
2668 			if (approx) {
2669 				approximate(lhs[i], &n_lhs[i]);
2670 				approximate(lhs[j], &n_lhs[j]);
2671 			}
2672 			switch (compare_es(i, j)) {
2673 			case 1:
2674 				fprintf(gfp, _("Expressions are identical.\n"));
2675 				return true;
2676 			case -1:
2677 				error(_("Expressions differ only in sign (times -1)."));
2678 				return false;
2679 			}
2680 #if	!SILENT
2681 			if (debug_level >= 0) {
2682 				list_sub(i);
2683 				list_sub(j);
2684 			}
2685 #endif
2686 			uf_simp(lhs[i], &n_lhs[i]);
2687 			uf_simp(lhs[j], &n_lhs[j]);
2688 			if (approx) {
2689 				approximate(lhs[i], &n_lhs[i]);
2690 				approximate(lhs[j], &n_lhs[j]);
2691 			}
2692 			switch (compare_es(i, j)) {
2693 			case 1:
2694 				fprintf(gfp, _("Expressions are identical.\n"));
2695 				return true;
2696 			case -1:
2697 				error(_("Expressions differ only in sign (times -1)."));
2698 				return false;
2699 			}
2700 			fprintf(gfp, _("Expressions differ.\n"));
2701 			return false;
2702 		}
2703 		error(_("Cannot compare an equation with a non-equation."));
2704 		return false;
2705 	}
2706 	if (compare_es(i, j) > 0) {
2707 		fprintf(gfp, _("Equations are identical.\n"));
2708 		return true;
2709 	}
2710 	if (solved_equation(i) && solved_equation(j)) {
2711 		if (compare_rhs(i, j, &diff_sign)) {
2712 			goto times_neg1;
2713 		}
2714 		if (approx) {
2715 			debug_string(0, _("Approximating both equations..."));
2716 			approximate(rhs[i], &n_rhs[i]);
2717 			approximate(rhs[j], &n_rhs[j]);
2718 			if (compare_rhs(i, j, &diff_sign)) {
2719 				goto times_neg1;
2720 			}
2721 		}
2722 		debug_string(0, _("Simplifying both equations..."));
2723 		symb_flag = symb;
2724 		simpa_repeat_side(rhs[i], &n_rhs[i], false, true);
2725 		simpa_repeat_side(rhs[j], &n_rhs[j], false, true);
2726 		symb_flag = false;
2727 		if (approx) {
2728 			approximate(rhs[i], &n_rhs[i]);
2729 			approximate(rhs[j], &n_rhs[j]);
2730 		}
2731 		if (compare_rhs(i, j, &diff_sign)) {
2732 			goto times_neg1;
2733 		}
2734 #if	!SILENT
2735 		if (debug_level >= 0) {
2736 			list_sub(i);
2737 			list_sub(j);
2738 		}
2739 #endif
2740 		uf_simp(rhs[i], &n_rhs[i]);
2741 		uf_simp(rhs[j], &n_rhs[j]);
2742 		if (approx) {
2743 			approximate(rhs[i], &n_rhs[i]);
2744 			approximate(rhs[j], &n_rhs[j]);
2745 		}
2746 		if (compare_rhs(i, j, &diff_sign)) {
2747 			goto times_neg1;
2748 		}
2749 	}
2750 	debug_string(0, _("Solving both equations for zero and expanding..."));
2751 	if (solve_sub(&zero_token, 1, lhs[i], &n_lhs[i], rhs[i], &n_rhs[i]) <= 0)
2752 		return false;
2753 	if (solve_sub(&zero_token, 1, lhs[j], &n_lhs[j], rhs[j], &n_rhs[j]) <= 0)
2754 		return false;
2755 	if (compare_rhs(i, j, &diff_sign)) {
2756 		fprintf(gfp, _("Equations are identical.\n"));
2757 		return true;
2758 	}
2759 	uf_simp(rhs[i], &n_rhs[i]);
2760 	uf_simp(rhs[j], &n_rhs[j]);
2761 	if (compare_rhs(i, j, &diff_sign)) {
2762 		fprintf(gfp, _("Equations are identical.\n"));
2763 		return true;
2764 	}
2765 	if (approx) {
2766 		debug_string(0, _("Approximating both equations..."));
2767 		approximate(rhs[i], &n_rhs[i]);
2768 		approximate(rhs[j], &n_rhs[j]);
2769 		if (compare_rhs(i, j, &diff_sign)) {
2770 			fprintf(gfp, _("Equations are identical.\n"));
2771 			return true;
2772 		}
2773 	}
2774 	debug_string(0, _("Simplifying both equations..."));
2775 	symb_flag = symb;
2776 	simpa_repeat_side(rhs[i], &n_rhs[i], false, false);
2777 	simpa_repeat_side(rhs[j], &n_rhs[j], false, false);
2778 	symb_flag = false;
2779 	if (approx) {
2780 		approximate(rhs[i], &n_rhs[i]);
2781 		approximate(rhs[j], &n_rhs[j]);
2782 	}
2783 	if (compare_rhs(i, j, &diff_sign)) {
2784 		fprintf(gfp, _("Equations are identical.\n"));
2785 		return true;
2786 	}
2787 	if (solve_sub(&zero_token, 1, lhs[i], &n_lhs[i], rhs[i], &n_rhs[i]) <= 0)
2788 		return false;
2789 	if (solve_sub(&zero_token, 1, lhs[j], &n_lhs[j], rhs[j], &n_rhs[j]) <= 0)
2790 		return false;
2791 	uf_simp(rhs[i], &n_rhs[i]);
2792 	uf_simp(rhs[j], &n_rhs[j]);
2793 	if (approx) {
2794 		approximate(rhs[i], &n_rhs[i]);
2795 		approximate(rhs[j], &n_rhs[j]);
2796 	}
2797 	if (compare_rhs(i, j, &diff_sign)) {
2798 		fprintf(gfp, _("Equations are identical.\n"));
2799 		return true;
2800 	}
2801 	fprintf(gfp, _("Equations differ.\n"));
2802 	return false;
2803 times_neg1:
2804 	if (!diff_sign && lhs[i][0].token.variable == lhs[j][0].token.variable) {
2805 		fprintf(gfp, _("Equations are identical.\n"));
2806 		return true;
2807 	}
2808 	fprintf(gfp, _("Variable "));
2809 	list_proc(lhs[i], n_lhs[i], false);
2810 	fprintf(gfp, _(" in the first equation\nis equal to "));
2811 	if (diff_sign) {
2812 		fprintf(gfp, "-");
2813 	}
2814 	list_proc(lhs[j], n_lhs[j], false);
2815 	fprintf(gfp, _(" in the second equation.\n"));
2816 #if	LIBRARY
2817 	if (diff_sign)
2818 		error(_("RHS appears negated."));
2819 	else
2820 		error(_("Different LHS variable name, otherwise the same."));
2821 	return false;
2822 #else
2823 	return 2;
2824 #endif
2825 }
2826 
2827 /*
2828  * Display the specified floating point value.
2829  * If it is equal to a simple fraction, display that too.
2830  *
2831  * Return true if a fraction was displayed.
2832  */
2833 int
display_fraction(value)2834 display_fraction(value)
2835 double	value;
2836 {
2837 	double	d4, d5;
2838 	int	rv = false;
2839 
2840 	f_to_fraction(value, &d4, &d5);
2841 	fprintf(gfp, "%.*g", precision, value);
2842 	if (d5 != 1.0) {
2843 		fprintf(gfp, " = %.*g/%.*g", precision, d4, precision, d5);
2844 		rv = true;
2845 	}
2846 	fprintf(gfp, "\n");
2847 	return rv;
2848 }
2849 
2850 /*
2851  * The divide command.
2852  */
2853 int
divide_cmd(cp)2854 divide_cmd(cp)
2855 char	*cp;
2856 {
2857 	long		v = 0, v_tmp;		/* Mathomatic variables */
2858 	int		i, j;
2859 	int		nleft = 0, nright = 0;
2860 	double		lcm, d1, d2, d3, d4, d5;
2861 	complexs	c1, c2, c3;
2862 	char		*cp_start;
2863 
2864 	cp_start = cp;
2865 	pull_number = -1;	/* Operands are last two entered expressions when using library. */
2866 	if (*cp && isvarchar(*cp)) {
2867 		cp = parse_var(&v, cp);
2868 		if (cp == NULL || (*cp && !isspace(*cp) && *cp != ',')) {
2869 			if (cp == NULL) {
2870 				reset_error();
2871 			}
2872 			cp = cp_start;
2873 			v = 0;
2874 		} else {
2875 			cp = skip_comma_space(cp);
2876 			SP(_("You have entered a base variable."));
2877 			EP(_("Polynomial division will be based on this variable."));
2878 			point_flag = false;
2879 		}
2880 	}
2881 	i = next_espace();
2882 	if (*cp) {
2883 		input_column += (cp - cp_start);
2884 		cp = parse_expr(rhs[i], &nright, cp, false);
2885 		if (cp == NULL || nright <= 0) {
2886 			return false;
2887 		}
2888 	}
2889 	if (*cp) {
2890 		cp_start = cp;
2891 		cp = skip_comma_space(cp);
2892 		input_column += (cp - cp_start);
2893 		cp = parse_expr(lhs[i], &nleft, cp, false);
2894 		if (cp == NULL || extra_characters(cp) || nleft <= 0) {
2895 			return false;
2896 		}
2897 	}
2898 do_repeat_prompt:
2899 /* prompt for the two operands */
2900 	my_strlcpy(prompt_str, _("Enter dividend: "), sizeof(prompt_str));
2901 	if (nright == 0 && !get_expr(rhs[i], &nright)) {
2902 		return repeat_flag;
2903 	}
2904 	my_strlcpy(prompt_str, _("Enter divisor: "), sizeof(prompt_str));
2905 	if (nleft == 0 && !get_expr(lhs[i], &nleft)) {
2906 		return repeat_flag;
2907 	}
2908 	fprintf(gfp, "\n");
2909 /* simplify and expand the operand expressions */
2910 #if	1
2911 	simp_loop(rhs[i], &nright);
2912 	uf_simp(rhs[i], &nright);
2913 	simp_loop(lhs[i], &nleft);
2914 	uf_simp(lhs[i], &nleft);
2915 #else
2916 /* approximates, too */
2917 	calc_simp(rhs[i], &nright);
2918 	calc_simp(lhs[i], &nleft);
2919 #endif
2920 /* if division by zero, display a warning */
2921 	if (get_constant(lhs[i], nleft, &d2)) {
2922 		check_divide_by_zero(d2);
2923 	}
2924 /* Do constant division if 2 normal numbers were entered */
2925 	if (get_constant(rhs[i], nright, &d1) && get_constant(lhs[i], nleft, &d2)) {
2926 		fprintf(gfp, _("Result of numerical division:\n"));
2927 		d3 = gcd_verified(d1, d2);
2928 		d4 = modf(d1 / d2, &d5);
2929 		fprintf(gfp, "%.*g/%.*g = %.*g", precision, d1, precision, d2, precision, d1 / d2);
2930 		if (d3 != 0.0 && d3 != 1.0 && (d2 / d3) != 1.0) {
2931 			if ((d1 / d2) < 0) {
2932 				fprintf(gfp, " = -%.*g/%.*g", precision, fabs(d1 / d3), precision, fabs(d2 / d3));
2933 			} else {
2934 				fprintf(gfp, " = %.*g/%.*g", precision, fabs(d1 / d3), precision, fabs(d2 / d3));
2935 			}
2936 		}
2937 		if (d3 != 0 && d4 != 0 && d5 != 0) {
2938 			if ((d1 / d2) < 0) {
2939 				fprintf(gfp, " = -(%.*g + (%.*g/%.*g))", precision, fabs(d5), precision, fabs(d4 * (d2 / d3)), precision, fabs(d2 / d3));
2940 			} else {
2941 				fprintf(gfp, " = %.*g + (%.*g/%.*g)", precision, fabs(d5), precision, fabs(d4 * (d2 / d3)), precision, fabs(d2 / d3));
2942 			}
2943 		}
2944 		fprintf(gfp, _("\nQuotient: %.*g, Remainder: %.*g\n"), precision, d5, precision, d4 * d2);
2945 		d1 = fabs(d1);
2946 		d2 = fabs(d2);
2947 		if (d3 == 0.0) {
2948 			fprintf(gfp, _("No GCD found.\n"));
2949 			if (repeat_flag)
2950 				goto do_repeat;
2951 			return true;
2952 		}
2953 		fprintf(gfp, "GCD = ");
2954 		if (d3 >= 4.0 && factor_one(d3) && !is_prime()) {
2955 			display_unique();
2956 		} else {
2957 			display_fraction(d3);
2958 		}
2959 		lcm = (d1 * d2) / d3;
2960 		fprintf(gfp, "LCM = ");
2961 		if (lcm >= 4.0 && factor_one(lcm) && !is_prime()) {
2962 			display_unique();
2963 		} else {
2964 			display_fraction(lcm);
2965 		}
2966 		if (repeat_flag)
2967 			goto do_repeat;
2968 		return true;
2969 	}
2970 /* else do complex number division if 2 complex numbers were entered */
2971 	if (parse_complex(rhs[i], nright, &c1) && parse_complex(lhs[i], nleft, &c2)) {
2972 		fprintf(gfp, _("Result of complex number division:\n"));
2973 		c3 = complex_div(c1, c2);
2974 		fprintf(gfp, "%.*g %+.*g*i\n\n", precision, c3.re, precision, c3.im);
2975 		if (repeat_flag)
2976 			goto do_repeat;
2977 		return true;
2978 	}
2979 /* else do polynomial division and univariate GCD display */
2980 	v_tmp = v;
2981 	if (poly_div(rhs[i], nright, lhs[i], nleft, &v_tmp)) {
2982 		simp_divide(tlhs, &n_tlhs);
2983 		simp_divide(trhs, &n_trhs);
2984 		list_var(v_tmp, 0);
2985 		fprintf(gfp, _("Polynomial division successful using base variable %s.\n"), var_str);
2986 		fprintf(gfp, _("The quotient is:\n"));
2987 		fractions_and_group(tlhs, &n_tlhs);
2988 		list_factor(tlhs, &n_tlhs, false);
2989 		fprintf(gfp, _("\n\nThe remainder is:\n"));
2990 		fractions_and_group(trhs, &n_trhs);
2991 		list_factor(trhs, &n_trhs, false);
2992 		fprintf(gfp, "\n");
2993 	} else {
2994 		SP(_("Polynomial division failed,"));
2995 		SP(_("because the given polynomials cannot be divided in the given order,"));
2996 		EP(_("according to the rules of polynomial division."));
2997 	}
2998 	fprintf(gfp, "\n");
2999 	j = poly_gcd(rhs[i], nright, lhs[i], nleft, v);
3000 	if (j == 0) {
3001 		j = poly_gcd(lhs[i], nleft, rhs[i], nright, v);
3002 	}
3003 	if (j > 0) {
3004 		simp_divide(trhs, &n_trhs);
3005 		fprintf(gfp, _("Polynomial GCD (after %d Euclidean algorithm iterations):\n"), j);
3006 		fractions_and_group(trhs, &n_trhs);
3007 		list_factor(trhs, &n_trhs, false);
3008 		fprintf(gfp, "\n");
3009 		blt(tes, trhs, n_trhs * sizeof(token_type));
3010 		n_tes = n_trhs;
3011 		if (poly_factor(tes, &n_tes, true)) {
3012 			simp_loop(tes, &n_tes);
3013 			fprintf(gfp, _("Polynomial GCD (after quick polynomial factoring):\n"));
3014 			fractions_and_group(tes, &n_tes);
3015 			list_factor(tes, &n_tes, false);
3016 			fprintf(gfp, "\n");
3017 		}
3018 	} else {
3019 		SP(_("No additive univariate polynomial GCD found."));
3020 		SP(_("This does not mean there is no GCD; it could be multivariate,"));
3021 		EP(_("or contain too much floating point round-off error."));
3022 	}
3023 	if (repeat_flag)
3024 		goto do_repeat;
3025 	return true;
3026 
3027 do_repeat:
3028 	nright = 0;
3029 	nleft = 0;
3030 	goto do_repeat_prompt;
3031 }
3032 
3033 /*
3034  * The eliminate command.
3035  */
3036 int
eliminate_cmd(cp)3037 eliminate_cmd(cp)
3038 char	*cp;
3039 {
3040 	long	v, last_v, v1, va[MAX_VARS];		/* Mathomatic variables */
3041 	int	vc = 0;					/* variable count */
3042 	int	i = 0, n;
3043 	int	success_flag = false, did_something = false, using_flag;
3044 	char	used[N_EQUATIONS];
3045 	char	*cp_start;
3046 	char	buf[MAX_CMD_LEN];
3047 
3048 	CLEAR_ARRAY(used);
3049 	if (current_not_defined()) {
3050 		return false;
3051 	}
3052 	if (*cp == '\0') {
3053 		my_strlcpy(prompt_str, _("Enter variables to eliminate: "), sizeof(prompt_str));
3054 		cp = get_string(buf, sizeof(buf));
3055 		if (cp == NULL || *cp == '\0') {
3056 			return false;
3057 		}
3058 	}
3059 	cp_start = cp;
3060 next_var:
3061 	if (vc) {
3062 		v = va[--vc];
3063 	} else if (*cp) {
3064 		if (is_all(cp)) {
3065 			cp = skip_param(cp);
3066 			vc = 0;
3067 			last_v = 0;
3068 			for (;;) {
3069 				v1 = -1;
3070 				for (i = 0; i < n_lhs[cur_equation]; i += 2) {
3071 					if (lhs[cur_equation][i].kind == VARIABLE
3072 					    && lhs[cur_equation][i].token.variable > last_v) {
3073 						if (v1 == -1 || lhs[cur_equation][i].token.variable < v1) {
3074 							v1 = lhs[cur_equation][i].token.variable;
3075 						}
3076 					}
3077 				}
3078 				for (i = 0; i < n_rhs[cur_equation]; i += 2) {
3079 					if (rhs[cur_equation][i].kind == VARIABLE
3080 					    && rhs[cur_equation][i].token.variable > last_v) {
3081 						if (v1 == -1 || rhs[cur_equation][i].token.variable < v1) {
3082 							v1 = rhs[cur_equation][i].token.variable;
3083 						}
3084 					}
3085 				}
3086 				if (v1 == -1)
3087 					break;
3088 				last_v = v1;
3089 				if ((v1 & VAR_MASK) > SIGN) {
3090 					if (vc >= ARR_CNT(va)) {
3091 						break;
3092 					}
3093 					va[vc++] = v1;
3094 				}
3095 			}
3096 			goto next_var;
3097 		}
3098 		cp = parse_var2(&v, cp);
3099 		if (cp == NULL) {
3100 			return false;
3101 		}
3102 	} else {
3103 		if (repeat_flag) {
3104 			if (success_flag) {
3105 				success_flag = false;
3106 				cp = cp_start;
3107 				goto next_var;	/* repeat until failure to substitute anything */
3108 			}
3109 		}
3110 		if (did_something) {
3111 			did_something = return_result(cur_equation);
3112 		} else {
3113 			error(_("No substitutions made."));
3114 		}
3115 		return did_something;
3116 	}
3117 	using_flag = (strcmp_tospace(cp, "using") == 0);
3118 	if (using_flag) {
3119 		cp = skip_param(cp);
3120 		if (*cp == '#')
3121 			cp++;
3122 		i = decstrtol(cp, &cp) - 1;
3123 		if (not_defined(i)) {
3124 			return false;
3125 		}
3126 	}
3127 	if (!var_in_equation(cur_equation, v)) {
3128 #if	!SILENT
3129 		if (!repeat_flag) {
3130 			list_var(v, 0);
3131 			printf(_("Variable %s not found in current equation.\n"), var_str);
3132 		}
3133 #endif
3134 		goto next_var;
3135 	}
3136 	if (using_flag) {
3137 		if (!elim_sub(i, v))
3138 			goto next_var;
3139 	} else {
3140 		n = 1;
3141 		i = cur_equation;
3142 		for (;; n++) {
3143 			if (n >= n_equations) {
3144 				goto next_var;
3145 			}
3146 			if (i <= 0)
3147 				i = n_equations - 1;
3148 			else
3149 				i--;
3150 			if (used[i])
3151 				continue;
3152 			if (n_lhs[i] && n_rhs[i] && var_in_equation(i, v)) {
3153 				if (elim_sub(i, v))
3154 					break;
3155 			}
3156 		}
3157 	}
3158 	success_flag = true;
3159 	did_something = true;
3160 	used[i] = true;
3161 	goto next_var;
3162 }
3163 
3164 /*
3165  * Solve equation number i for v and substitute the RHS
3166  * into all occurrences of v in the current equation, then simplify.
3167  */
3168 static int
elim_sub(i,v)3169 elim_sub(i, v)
3170 int	i;	/* equation number */
3171 long	v;	/* Mathomatic variable */
3172 {
3173 	token_type	want;
3174 	int		solved;
3175 
3176 	if (i == cur_equation) {
3177 		error(_("Error: source and destination are the same."));
3178 		return false;
3179 	}
3180 	solved = (solved_equation(i) && lhs[i][0].token.variable == v);
3181 #if	!SILENT
3182 	list_var(v, 0);
3183 	if (solved) {
3184 		fprintf(gfp, _("Eliminating variable %s using solved equation #%d...\n"), var_str, i + 1);
3185 	} else {
3186 		fprintf(gfp, _("Solving equation #%d for %s and substituting into the current equation...\n"), i + 1, var_str);
3187 	}
3188 #endif
3189 	if (!solved) {
3190 		want.level = 1;
3191 		want.kind = VARIABLE;
3192 		want.token.variable = v;
3193 		if (solve_sub(&want, 1, lhs[i], &n_lhs[i], rhs[i], &n_rhs[i]) <= 0) {
3194 			error(_("Solve failed."));
3195 			return false;
3196 		}
3197 	}
3198 	subst_var_with_exp(rhs[cur_equation], &n_rhs[cur_equation], rhs[i], n_rhs[i], v);
3199 	subst_var_with_exp(lhs[cur_equation], &n_lhs[cur_equation], rhs[i], n_rhs[i], v);
3200 
3201 	simp_equation(cur_equation);
3202 	return true;
3203 }
3204 
3205 /*
3206  * The display command.
3207  *
3208  * Displays equations in multi-line fraction format.
3209  *
3210  * Return number of expressions displayed.
3211  */
3212 int
display_cmd(cp)3213 display_cmd(cp)
3214 char	*cp;
3215 {
3216 	int	i, j;
3217 	char	*cp1;
3218 	jmp_buf	save_save;
3219 	int	factor_flag = false, displayed = 0;
3220 	int	orig_fractions_display_mode, new_fractions_display_mode;
3221 
3222 	new_fractions_display_mode = orig_fractions_display_mode = fractions_display;
3223 	for (;; cp = skip_param(cp)) {
3224 		if (strncasecmp(cp, "factor", 4) == 0) {
3225 			factor_flag = true;
3226 			continue;
3227 		}
3228 		if (strncasecmp(cp, "simple", 4) == 0) {
3229 			new_fractions_display_mode = 1;
3230 			continue;
3231 		}
3232 		if (strncasecmp(cp, "mixed", 3) == 0) {
3233 			new_fractions_display_mode = 2;
3234 			continue;
3235 		}
3236 		break;
3237 	}
3238 	do {
3239 		cp1 = cp;
3240 		if (!get_range(&cp, &i, &j)) {
3241 			return false;
3242 		}
3243 		if (*cp && cp == cp1) {
3244 			error(_("Invalid argument.  Expecting equation number or range."));
3245 			return false;
3246 		}
3247 		for (; i <= j; i++) {
3248 			if (n_lhs[i] > 0) {
3249 				blt(save_save, jmp_save, sizeof(jmp_save));
3250 				if (setjmp(jmp_save) != 0) {	/* trap errors */
3251 					fractions_display = orig_fractions_display_mode;
3252 					blt(jmp_save, save_save, sizeof(jmp_save));
3253 					printf("Skipping equation number %d.\n", i + 1);
3254 					continue;
3255 				}
3256 				fractions_display = new_fractions_display_mode;
3257 				make_fractions_and_group(i);
3258 				fractions_display = orig_fractions_display_mode;
3259 				if (factor_flag || factor_int_flag) {
3260 					factor_int_equation(i);
3261 				}
3262 				blt(jmp_save, save_save, sizeof(jmp_save));
3263 #if     LIBRARY
3264 				free_result_str();
3265 				result_str = flist_equation_string(i);
3266 				if (result_str == NULL)
3267 					result_str = list_equation(i, false);
3268 				if (result_str)
3269 					result_en = i;
3270 				if (gfp != stdout) {
3271 					if (flist_equation(i) > 0) {
3272 						displayed++;
3273 					}
3274 				}
3275 #else
3276 				if (flist_equation(i) > 0) {
3277 					displayed++;
3278 				}
3279 #endif
3280 			}
3281 		}
3282 	} while (*cp);
3283 #if	LIBRARY
3284 	return(result_str != NULL);
3285 #else
3286 	return(displayed);
3287 #endif
3288 }
3289 
3290 /*
3291  * The list command.
3292  */
3293 int
list_cmd(cp)3294 list_cmd(cp)
3295 char	*cp;
3296 {
3297 	int	k;
3298 	int	first, last;
3299 	char	*cp1;
3300 	int	export_flag = 0;
3301 #if     SHELL_OUT
3302 	char	cl[MAX_CMD_LEN];
3303 	int	primes_flag = false;
3304 	int	ev;	/* exit value */
3305 #endif
3306 
3307 	if (strncasecmp(cp, "gnuplot", 3) == 0) {
3308 		export_flag = 3;
3309 		cp = skip_param(cp);
3310 	} else if (strncasecmp(cp, "export", 3) == 0) {
3311 		export_flag = 2;
3312 		cp = skip_param(cp);
3313 	} else if (strncasecmp(cp, "maxima", 3) == 0) {
3314 		export_flag = 1;
3315 		cp = skip_param(cp);
3316 	} else if (strncasecmp(cp, "hexadecimal", 3) == 0) {
3317 		export_flag = 4;
3318 		cp = skip_param(cp);
3319 #if     SHELL_OUT
3320 	} else if (strncasecmp(cp, "primes", 5) == 0) {
3321 		primes_flag = true;
3322 		cp = skip_param(cp);
3323 #endif
3324 	}
3325 #if     SHELL_OUT
3326 	if (primes_flag) {
3327 		if (gfp && gfp_filename && gfp_filename[0]) {
3328 			if (snprintf(cl, sizeof(cl), "matho-primes -u %s >%s%s", cp, gfp_append_flag ? ">" : "", gfp_filename) >= sizeof(cl)) {
3329 				error(_("Command-line too long."));
3330 				return false;
3331 			}
3332 			clean_up();	/* end any redirection */
3333 		} else {
3334 			if (snprintf(cl, sizeof(cl), "matho-primes -u %s", cp) >= sizeof(cl)) {
3335 				error(_("Command-line too long."));
3336 				return false;
3337 			}
3338 		}
3339 		if ((ev = shell_out(cl))) {
3340 			error(_("Abnormal termination of matho-primes."));
3341 			printf(_("Decimal exit value = %d, shell command-line = %s\n"), ev, cl);
3342 			return false;
3343 		}
3344 		return true;
3345 	}
3346 #endif
3347 	do {
3348 		cp1 = cp;
3349 		if (!get_range(&cp, &first, &last)) {
3350 			return false;
3351 		}
3352 		if (*cp && cp == cp1) {
3353 			error(_("Invalid argument.  Expecting equation number or range."));
3354 			return false;
3355 		}
3356 		for (k = first; k <= last; k++) {
3357 			if (n_lhs[k] <= 0)
3358 				continue;
3359 #if	LIBRARY
3360 			free_result_str();
3361 			result_str = list_equation(k, export_flag);
3362 			if (result_str)
3363 				result_en = k;
3364 			else
3365 				return false;
3366 			if (gfp == stdout) {
3367 				continue;
3368 			}
3369 #endif
3370 			list1_sub(k, export_flag);
3371 		}
3372 	} while (*cp);
3373 	return true;
3374 }
3375 
3376 /*
3377  * The code command.
3378  */
3379 int
code_cmd(cp)3380 code_cmd(cp)
3381 char	*cp;
3382 {
3383 	int			i, j, k;
3384 	int			li, ri;
3385 	enum language_list	language = C;
3386 	int			int_flag = false, displayed = false;
3387 	char			*cp1;
3388 
3389 	for (;; cp = skip_param(cp)) {
3390 		if (strcmp_tospace(cp, "c") == 0 || strcmp_tospace(cp, "c++") == 0) {
3391 			language = C;
3392 			continue;
3393 		}
3394 		if (strcmp_tospace(cp, "java") == 0) {
3395 			language = JAVA;
3396 			continue;
3397 		}
3398 		if (strcmp_tospace(cp, "python") == 0) {
3399 			language = PYTHON;
3400 			continue;
3401 		}
3402 		if (strncasecmp(cp, "integer", 3) == 0) {
3403 			int_flag = true;
3404 			continue;
3405 		}
3406 		break;
3407 	}
3408 	do {
3409 		cp1 = cp;
3410 		if (!get_range(&cp, &i, &j)) {
3411 			return false;
3412 		}
3413 		if (*cp && cp == cp1) {
3414 			error(_("Invalid argument.  Expecting equation number or range."));
3415 			return false;
3416 		}
3417 		for (k = i; k <= j; k++) {
3418 			if (n_lhs[k] <= 0)
3419 				continue;
3420 			if (n_rhs[k] == 0 || n_lhs[k] != 1 || lhs[k][0].kind != VARIABLE) {
3421 				warning(_("Can't make assignment statement because this is not an equation."));
3422 			} else if (!solved_equation(k)) {
3423 				warning(_("Equation is not solved for a normal variable."));
3424 			}
3425 			simp_i(lhs[k], &n_lhs[k]);
3426 			if (int_flag) {
3427 				/* factor_constants() for more accurate integer results. */
3428 				do {
3429 					simp_loop(lhs[k], &n_lhs[k]);
3430 				} while (factor_constants(lhs[k], &n_lhs[k], 6));
3431 				/* Turn the power operator into the multiply operator, if raised to the power of a constant. */
3432 				uf_repeat_always(lhs[k], &n_lhs[k]);
3433 			}
3434 			if (n_rhs[k] > 0) {
3435 				simp_i(rhs[k], &n_rhs[k]);
3436 				if (int_flag) {
3437 					/* factor_constants() for more accurate integer results. */
3438 					do {
3439 						simp_loop(rhs[k], &n_rhs[k]);
3440 					} while (factor_constants(rhs[k], &n_rhs[k], 6));
3441 					/* Turn the power operator into the multiply operator, if raised to the power of a constant. */
3442 					uf_repeat_always(rhs[k], &n_rhs[k]);
3443 				}
3444 			}
3445 			make_fractions_and_group(k);
3446 			if (int_flag) {
3447 				if ((!(li = int_expr(lhs[k], n_lhs[k])) || !(ri = int_expr(rhs[k], n_rhs[k])))) {
3448 					warning(_("Not an integer expression, but this rounded code may possibly work:"));
3449 				} else if (li < 0 || ri < 0) {
3450 					warning(_("This integer expression contains non-integer divides:"));
3451 				}
3452 			}
3453 #if	LIBRARY
3454 			free_result_str();
3455 			result_str = string_code_equation(k, language, int_flag);
3456 			if (result_str)
3457 				result_en = k;
3458 			else
3459 				return false;
3460 			if (gfp == stdout) {
3461 				displayed = true;
3462 				continue;
3463 			}
3464 #endif
3465 			if (list_code_equation(k, language, int_flag) > 0) {
3466 				displayed = true;
3467 			}
3468 		}
3469 	} while (*cp);
3470 	return displayed;
3471 }
3472 
3473 /*
3474  * Compare function for qsort(3).
3475  */
3476 static int
vcmp(p1,p2)3477 vcmp(p1, p2)
3478 sort_type	*p1, *p2;
3479 {
3480 	if (p2->count == p1->count) {
3481 		if (p1->v < p2->v)
3482 			return -1;
3483 		if (p1->v == p2->v)
3484 			return 0;
3485 		return 1;
3486 	}
3487 	return(p2->count - p1->count);
3488 }
3489 
3490 /*
3491  * The variables command.
3492  */
3493 int
variables_cmd(cp)3494 variables_cmd(cp)
3495 char	*cp;
3496 {
3497 	int			start, stop;
3498 	int			k;
3499 	int			i1;
3500 	long			v1, last_v;		/* Mathomatic variables */
3501 	int			vc, cnt;		/* variable counts */
3502 	sort_type		va[MAX_VARS];		/* variable array */
3503 	token_type		*p1;
3504 	int			n1;
3505 	enum language_list	lang_code = 0;		/* default to no programming language */
3506 	int			int_flag = false, imag_flag = false, count_flag = false, not_complex = false;
3507 	char			imag_array[N_EQUATIONS];
3508 	char			*range_start, *cp1;
3509 	int			array_element_flag = false;
3510 	int			rv = false;
3511 	int			n_tabs = 0;
3512 
3513 	CLEAR_ARRAY(imag_array);
3514 	if (strncasecmp(cp, "counts", 5) == 0) {
3515 		cp = skip_param(cp);
3516 		count_flag = true;
3517 	}
3518 	if (strcmp_tospace(cp, "c") == 0 || strcmp_tospace(cp, "c++") == 0) {
3519 		cp = skip_param(cp);
3520 		lang_code = C;
3521 	} else if (strcmp_tospace(cp, "java") == 0) {
3522 		cp = skip_param(cp);
3523 		lang_code = JAVA;
3524 	} else if (strncasecmp(cp, "integer", 3) == 0) {
3525 		cp = skip_param(cp);
3526 		lang_code = C;
3527 		int_flag = true;
3528 	}
3529 	if (strncasecmp(cp, "counts", 5) == 0) {
3530 		cp = skip_param(cp);
3531 		count_flag = true;
3532 	}
3533 	range_start = cp;
3534 	do {
3535 		cp1 = cp;
3536 		if (!get_range(&cp, &start, &stop)) {
3537 			return false;
3538 		}
3539 		if (*cp && cp == cp1) {
3540 			error(_("Invalid argument.  Expecting equation number or range."));
3541 			return false;
3542 		}
3543 		for (k = start; k <= stop; k++) {
3544 			if (n_lhs[k] <= 0)
3545 				continue;
3546 			if (n_rhs[k] > 0) {
3547 				p1 = rhs[k];
3548 				n1 = n_rhs[k];
3549 			} else {
3550 				p1 = lhs[k];
3551 				n1 = n_lhs[k];
3552 			}
3553 			for (i1 = 0; i1 < n1; i1 += 2) {
3554 				if (p1[i1].kind == VARIABLE && p1[i1].token.variable == IMAGINARY) {
3555 					imag_flag = true;
3556 					imag_array[k] = true;
3557 					break;
3558 				}
3559 			}
3560 		}
3561 	} while (*cp);
3562 	show_usage = false;
3563 	last_v = 0;
3564 	for (vc = 0;;) {
3565 		if (vc >= ARR_CNT(va)) {
3566 			error(_("Too many variables to list."));
3567 			return false;
3568 		}
3569 		cnt = 0;
3570 		v1 = -1;
3571 		cp = range_start;
3572 		do {
3573 			cp1 = cp;
3574 			if (!get_range(&cp, &start, &stop)) {
3575 				return false;
3576 			}
3577 #if	DEBUG
3578 			if (*cp && cp == cp1) {
3579 				error_bug("Bug in variables command.");
3580 			}
3581 #endif
3582 			for (k = start; k <= stop; k++) {
3583 				if (n_lhs[k] <= 0)
3584 					continue;
3585 				p1 = lhs[k];
3586 				n1 = n_lhs[k];
3587 				for (i1 = 0; i1 < n1; i1 += 2) {
3588 					if (p1[i1].kind == VARIABLE && p1[i1].token.variable > last_v) {
3589 						if (v1 == -1 || p1[i1].token.variable < v1) {
3590 							v1 = p1[i1].token.variable;
3591 							cnt = 1;
3592 						} else if (p1[i1].token.variable == v1) {
3593 							cnt++;
3594 						}
3595 					}
3596 				}
3597 				p1 = rhs[k];
3598 				n1 = n_rhs[k];
3599 				for (i1 = 0; i1 < n1; i1 += 2) {
3600 					if (p1[i1].kind == VARIABLE && p1[i1].token.variable > last_v) {
3601 						if (v1 == -1 || p1[i1].token.variable < v1) {
3602 							v1 = p1[i1].token.variable;
3603 							cnt = 1;
3604 						} else if (p1[i1].token.variable == v1) {
3605 							cnt++;
3606 						}
3607 					}
3608 				}
3609 			}
3610 		} while (*cp);
3611 		if (v1 == -1)
3612 			break;
3613 		last_v = v1;
3614 		va[vc].v = v1;
3615 		va[vc].count = cnt;
3616 		vc++;
3617 	}
3618 	if (vc <= 0) {
3619 		if (lang_code == 0) {
3620 			error(_("Expression is numeric.  No normal variables found."));
3621 			return false;
3622 		} else {
3623 			return true;
3624 		}
3625 	}
3626 	qsort((char *) va, vc, sizeof(*va), vcmp);
3627 	for (i1 = 0; i1 < vc; i1++) {
3628 		if (lang_code && va[i1].v < SIGN) {
3629 			continue;
3630 		}
3631 		if ((va[i1].v & VAR_MASK) >= SIGN) {
3632 			rv = true;
3633 		}
3634 		n_tabs = list_var(va[i1].v, lang_code ? lang_code : -5);
3635 		if (lang_code) {
3636 			if (strpbrk(var_str, "[]()"))
3637 				array_element_flag = true;
3638 			if (imag_flag) {
3639 				for (k = 0;; k++) {
3640 					if (k >= n_equations) {
3641 						not_complex = true;
3642 						break;
3643 					}
3644 					if (imag_array[k] && n_lhs[k] == 1
3645 					    && lhs[k][0].kind == VARIABLE && lhs[k][0].token.variable == va[i1].v) {
3646 						fprintf(gfp, "_Complex ");
3647 						n_tabs += 8;
3648 						break;
3649 					}
3650 				}
3651 			}
3652 			if (int_flag || is_integer_var(va[i1].v) || (va[i1].v & VAR_MASK) == SIGN) {
3653 				fprintf(gfp, "int%s%s;", (n_tabs + 1)/8 ? "\t" : "\t\t", var_str);
3654 			} else {
3655 				fprintf(gfp, "double%s%s;", (n_tabs + 1)/8 ? "\t" : "\t\t", var_str);
3656 			}
3657 			if (n_tabs >= 7)
3658 				n_tabs -= 7;
3659 		} else {
3660 			fprintf(gfp, "%s", var_str);
3661 		}
3662 		if (count_flag) {
3663 			if ((n_tabs / 8) == 0) {
3664 				fprintf(gfp, "\t");
3665 			}
3666 			fprintf(gfp, _("\t/* count = %d */\n"), va[i1].count);
3667 		} else {
3668 			fprintf(gfp, "\n");
3669 		}
3670 	}
3671 	if (lang_code && imag_flag && not_complex && rv) {
3672 		printf("\n");
3673 		warning(_("Some variables might need to be of the complex number type."));
3674 		printf(_("Manual adjustments may be necessary\n"));
3675 		printf(_("because of the appearance of the imaginary unit (i).\n"));
3676 	}
3677 	if (!rv) {
3678 		error(_("Expressions are all numeric.  No variables found."));
3679 	}
3680 	if (array_element_flag) {
3681 		warning(_("Some defined variables were array elements or functions, requiring manual definition."));
3682 		rv = false;
3683 	}
3684 	return rv;
3685 }
3686 
3687 /*
3688  * The approximate command.
3689  */
3690 int
approximate_cmd(cp)3691 approximate_cmd(cp)
3692 char	*cp;
3693 {
3694 	int	start, stop;
3695 	int	k;
3696 	char	*cp1;
3697 
3698 	do {
3699 		cp1 = cp;
3700 		if (!get_range(&cp, &start, &stop)) {
3701 			return false;
3702 		}
3703 		if (*cp && cp == cp1) {
3704 			error(_("Invalid argument.  Expecting equation number or range."));
3705 			return false;
3706 		}
3707 		for (k = start; k <= stop; k++) {
3708 			if (n_lhs[k]) {
3709 				approximate(lhs[k], &n_lhs[k]);
3710 				if (n_rhs[k]) {
3711 					approximate(rhs[k], &n_rhs[k]);
3712 				}
3713 				if (!return_result(k)) {
3714 					return false;
3715 				}
3716 			}
3717 		}
3718 	} while (*cp);
3719 	return true;
3720 }
3721 
3722 /*
3723  * The replace command.
3724  */
3725 int
replace_cmd(cp)3726 replace_cmd(cp)
3727 char	*cp;
3728 {
3729 	int	i, j;
3730 	long	last_v, v, va[MAX_VARS];	/* Mathomatic variables */
3731 	int	vc;				/* variable count */
3732 	char	*cp_start, *cp1;
3733 	int	found, value_entered;
3734 	int	diff_sign;
3735 
3736 	cp_start = cp;
3737 	if (current_not_defined()) {
3738 		return false;
3739 	}
3740 	i = cur_equation;
3741 	for (vc = 0; *cp; vc++) {
3742 		if (strcmp_tospace(cp, "with") == 0) {
3743 			if (vc) {
3744 				repeat_flag = false;
3745 				break;
3746 			}
3747 		}
3748 		if (vc >= ARR_CNT(va)) {
3749 			error(_("Too many variables specified."));
3750 			return false;
3751 		}
3752 		cp = parse_var2(&va[vc], cp);
3753 		if (cp == NULL) {
3754 			return false;
3755 		}
3756 		if (!var_in_equation(i, va[vc])) {
3757 			error(_("Variable not found."));
3758 			return false;
3759 		}
3760 	}
3761 replace_again:
3762 	n_tlhs = n_lhs[i];
3763 	blt(tlhs, lhs[i], n_tlhs * sizeof(token_type));
3764 	n_trhs = n_rhs[i];
3765 	blt(trhs, rhs[i], n_trhs * sizeof(token_type));
3766 	value_entered = false;
3767 	last_v = 0;
3768 	for (;;) {
3769 		v = -1;
3770 		for (j = 0; j < n_lhs[i]; j += 2) {
3771 			if (lhs[i][j].kind == VARIABLE) {
3772 				if (lhs[i][j].token.variable > last_v
3773 				    && (v == -1 || lhs[i][j].token.variable < v))
3774 					v = lhs[i][j].token.variable;
3775 			}
3776 		}
3777 		for (j = 0; j < n_rhs[i]; j += 2) {
3778 			if (rhs[i][j].kind == VARIABLE) {
3779 				if (rhs[i][j].token.variable > last_v
3780 				    && (v == -1 || rhs[i][j].token.variable < v))
3781 					v = rhs[i][j].token.variable;
3782 			}
3783 		}
3784 		if (v == -1) {
3785 			break;
3786 		}
3787 		last_v = v;
3788 		if (vc) {
3789 			found = false;
3790 			for (j = 0; j < vc; j++) {
3791 				if (v == va[j])
3792 					found = true;
3793 			}
3794 			if (!found)
3795 				continue;
3796 			if (*cp) {
3797 				if (strcmp_tospace(cp, "with") != 0) {
3798 					return false;
3799 				}
3800 				cp1 = skip_param(cp);
3801 				input_column += (cp1 - cp_start);
3802 				if ((cp1 = parse_expr(tes, &n_tes, cp1, true)) == NULL || n_tes <= 0) {
3803 					return false;
3804 				}
3805 				goto do_this;
3806 			}
3807 		}
3808 		list_var(v, 0);
3809 		snprintf(prompt_str, sizeof(prompt_str), _("Enter %s: "), var_str);
3810 		if (!get_expr(tes, &n_tes)) {
3811 			continue;
3812 		}
3813 		value_entered = true;
3814 do_this:
3815 		/* Disguise all variables in the entered expression by making them negative; */
3816 		/* That way they won't be improperly substituted later, allowing variable interchange. */
3817 		for (j = 0; j < n_tes; j += 2) {
3818 			if (tes[j].kind == VARIABLE) {
3819 				tes[j].token.variable = -tes[j].token.variable;
3820 			}
3821 		}
3822 		subst_var_with_exp(tlhs, &n_tlhs, tes, n_tes, v);
3823 		subst_var_with_exp(trhs, &n_trhs, tes, n_tes, v);
3824 	}
3825 	/* Restore disguised variables: */
3826 	for (j = 0; j < n_tlhs; j += 2)
3827 		if (tlhs[j].kind == VARIABLE && tlhs[j].token.variable < 0)
3828 			tlhs[j].token.variable = -tlhs[j].token.variable;
3829 	for (j = 0; j < n_trhs; j += 2)
3830 		if (trhs[j].kind == VARIABLE && trhs[j].token.variable < 0)
3831 			trhs[j].token.variable = -trhs[j].token.variable;
3832 	if (repeat_flag) {
3833 		calc_simp(tlhs, &n_tlhs);
3834 		if (n_trhs) {
3835 			calc_simp(trhs, &n_trhs);
3836 			if (se_compare(tlhs, n_tlhs, trhs, n_trhs, &diff_sign) && !diff_sign) {
3837 				fprintf(gfp, _("The result is an identity:\n"));
3838 			}
3839 		}
3840 		list_tdebug(-10);
3841 		if (value_entered) {
3842 			fprintf(gfp, "Repeating:\n");
3843 			goto replace_again;
3844 		} else {
3845 			return true;
3846 		}
3847 	}
3848 	n_lhs[i] = n_tlhs;
3849 	blt(lhs[i], tlhs, n_tlhs * sizeof(token_type));
3850 	n_rhs[i] = n_trhs;
3851 	blt(rhs[i], trhs, n_trhs * sizeof(token_type));
3852 	simp_equation(i);
3853 	return return_result(i);
3854 }
3855 
3856 /*
3857  * The simplify command.
3858  *
3859  * Returns number of expressions simplified.
3860  */
3861 int
simplify_cmd(cp)3862 simplify_cmd(cp)
3863 char	*cp;
3864 {
3865 	int		i, i1;
3866 	int		first, last;
3867 	int		k, k1, total_number_of_solutions, number_simplified = 0;
3868 	long		counter, counter_max, previous_solution_number[N_EQUATIONS];
3869 	sign_array_type	sa_mark, sa_value;
3870 	int		sign_flag = false, quick_flag = false, quickest_flag = false, symb = false, frac_flag = false;
3871 	char		*cp1;
3872 
3873 	for (;; cp = skip_param(cp)) {
3874 		if (strncasecmp(cp, "sign", 4) == 0) {
3875 			sign_flag = true;
3876 			continue;
3877 		}
3878 		if (strncasecmp(cp, "symbolic", 4) == 0) {
3879 			symb = true;
3880 			continue;
3881 		}
3882 		if (strcmp_tospace(cp, "quickest") == 0) {
3883 			quickest_flag = true;
3884 			continue;
3885 		}
3886 		if (strcmp_tospace(cp, "quick") == 0) {
3887 			quick_flag = true;
3888 			continue;
3889 		}
3890 		if (strncasecmp(cp, "fraction", 4) == 0) {
3891 			frac_flag = true;
3892 			continue;
3893 		}
3894 		break;
3895 	}
3896 	do {
3897 		cp1 = cp;
3898 		if (!get_range(&cp, &first, &last)) {
3899 			return false;
3900 		}
3901 		if (*cp && cp == cp1) {
3902 			error(_("Invalid argument.  Expecting equation number or range."));
3903 			return false;
3904 		}
3905 		for (i = first; i <= last; i++) {
3906 			if (n_lhs[i] <= 0)
3907 				continue;
3908 			number_simplified++;
3909 			symb_flag = symb;
3910 			if (quickest_flag) {
3911 				simp_equation(i);
3912 			} else {
3913 				simpa_repeat(i, quick_flag, frac_flag);
3914 			}
3915 			symb_flag = false;
3916 			if (!return_result(i)) {
3917 				return false;
3918 			}
3919 			if (!sign_flag)
3920 				continue;
3921 			/* Now substitute all sign variables with +1 and -1. */
3922 			CLEAR_ARRAY(previous_solution_number);
3923 			CLEAR_ARRAY(sa_mark);
3924 			for (k1 = 0; k1 < n_lhs[i]; k1 += 2) {
3925 				if (lhs[i][k1].kind == VARIABLE && (lhs[i][k1].token.variable & VAR_MASK) == SIGN) {
3926 					sa_mark[(lhs[i][k1].token.variable >> VAR_SHIFT) & SUBSCRIPT_MASK] = true;
3927 				}
3928 			}
3929 			for (k1 = 0; k1 < n_rhs[i]; k1 += 2) {
3930 				if (rhs[i][k1].kind == VARIABLE && (rhs[i][k1].token.variable & VAR_MASK) == SIGN) {
3931 					sa_mark[(rhs[i][k1].token.variable >> VAR_SHIFT) & SUBSCRIPT_MASK] = true;
3932 				}
3933 			}
3934 			for (k1 = 0, k = 0; k1 < ARR_CNT(sa_mark); k1++) {
3935 				if (sa_mark[k1]) {
3936 					k++;
3937 				}
3938 			}
3939 			if (k == 0)
3940 				continue;
3941 			counter_max = (1L << k) - 1L;
3942 			if (counter_max) {
3943 				fprintf(gfp, _("There are %ld possible solutions.\n"), counter_max + 1);
3944 			}
3945 			for (counter = 0; counter <= counter_max; counter++) {
3946 				i1 = next_espace();
3947 				copy_espace(i, i1);
3948 				for (k1 = 0, k = 0; k1 < ARR_CNT(sa_mark); k1++) {
3949 					if (sa_mark[k1]) {
3950 						sa_value[k1] = (((1L << k) & counter) != 0);
3951 						k++;
3952 					}
3953 				}
3954 				for (k1 = 0; k1 < n_lhs[i1]; k1 += 2) {
3955 					if (lhs[i1][k1].kind == VARIABLE && (lhs[i1][k1].token.variable & VAR_MASK) == SIGN) {
3956 						if (sa_value[(lhs[i1][k1].token.variable >> VAR_SHIFT) & SUBSCRIPT_MASK]) {
3957 							lhs[i1][k1].kind = CONSTANT;
3958 							lhs[i1][k1].token.constant = -1.0;
3959 						} else {
3960 							lhs[i1][k1].kind = CONSTANT;
3961 							lhs[i1][k1].token.constant = 1.0;
3962 						}
3963 					}
3964 				}
3965 				for (k1 = 0; k1 < n_rhs[i1]; k1 += 2) {
3966 					if (rhs[i1][k1].kind == VARIABLE && (rhs[i1][k1].token.variable & VAR_MASK) == SIGN) {
3967 						if (sa_value[(rhs[i1][k1].token.variable >> VAR_SHIFT) & SUBSCRIPT_MASK]) {
3968 							rhs[i1][k1].kind = CONSTANT;
3969 							rhs[i1][k1].token.constant = -1.0;
3970 						} else {
3971 							rhs[i1][k1].kind = CONSTANT;
3972 							rhs[i1][k1].token.constant = 1.0;
3973 						}
3974 					}
3975 				}
3976 				for (k1 = 0, k = false; k1 < ARR_CNT(sa_mark); k1++) {
3977 					if (sa_mark[k1]) {
3978 						if (k) {
3979 							fprintf(gfp, ", ");
3980 						} else {
3981 							fprintf(gfp, _("Solution number %ld with "), counter + 1);
3982 						}
3983 						list_var((long) SIGN + (((long) k1) << VAR_SHIFT), 0);
3984 						fprintf(gfp, "%s = ", var_str);
3985 						if (sa_value[k1]) {
3986 							fprintf(gfp, "-1");
3987 						} else {
3988 							fprintf(gfp, "1");
3989 						}
3990 						k = true;
3991 					}
3992 				}
3993 				if (k)
3994 					fprintf(gfp, ":\n");
3995 				symb_flag = symb;
3996 				if (quickest_flag) {
3997 					simp_equation(i1);
3998 				} else {
3999 					simpa_repeat(i1, quick_flag, frac_flag);
4000 				}
4001 				symb_flag = false;
4002 				for (k1 = 0; k1 < ARR_CNT(previous_solution_number); k1++) {
4003 					if (previous_solution_number[k1]) {
4004 						if (compare_es(k1, i1) > 0) {
4005 							n_lhs[i1] = 0;
4006 							n_rhs[i1] = 0;
4007 							fprintf(gfp, _("is identical to solution number %ld.\n"), previous_solution_number[k1]);
4008 							break;
4009 						}
4010 					}
4011 				}
4012 				if (n_lhs[i1]) {
4013 					list_sub(i1);
4014 					previous_solution_number[i1] = counter + 1;
4015 				}
4016 			}
4017 			total_number_of_solutions = 0;
4018 			for (k1 = 0; k1 < ARR_CNT(previous_solution_number); k1++) {
4019 				if (previous_solution_number[k1]) {
4020 					total_number_of_solutions++;
4021 				}
4022 			}
4023 			if (total_number_of_solutions > 0) {
4024 				number_simplified += total_number_of_solutions;
4025 				fprintf(gfp, _("%d unique solutions stored in equation spaces for this expression (#%d).\n"), total_number_of_solutions, i + 1);
4026 			}
4027 		}
4028 	} while (*cp);
4029 	return number_simplified;
4030 }
4031 
4032 /*
4033  * The factor command.
4034  */
4035 int
factor_cmd(cp)4036 factor_cmd(cp)
4037 char	*cp;
4038 {
4039 	int	first, last;
4040 	int	i1;
4041 	int	found, rv = true;
4042 	long	v;				/* Mathomatic variable */
4043 	int	valid_range = false, power_flag = false;
4044 	char	*cp_start;
4045 	int	count_down;
4046 	char	*cp1, *cp2;
4047 	double	d, ed;
4048 #if	!LIBRARY
4049 	char	buf[MAX_CMD_LEN];
4050 #endif
4051 
4052 	cp_start = cp;
4053 	if (strcmp_tospace(cp, "number") == 0) {
4054 		cp = skip_param(cp);
4055 	} else if (strcmp_tospace(cp, "numbers") == 0) {
4056 		repeat_flag = true;
4057 		cp = skip_param(cp);
4058 	} else {
4059 		if (strcmp_tospace(cp, "power") == 0) {
4060 			power_flag = true;
4061 			cp = skip_param(cp);
4062 		}
4063 		valid_range = get_range(&cp, &first, &last);
4064 		if (!valid_range) {
4065 #if	LIBRARY	/* be consistent */
4066 			return false;
4067 #else		/* be helpful */
4068 			if (*cp == '-' || isdigit(*cp)) {
4069 				reset_error();
4070 				printf(_("Factoring integers on command-line instead:\n"));
4071 				point_flag = false;
4072 			} else {
4073 				extra_characters(cp);
4074 				return false;
4075 			}
4076 #endif
4077 		}
4078 	}
4079 	if (!valid_range) {
4080 #if	LIBRARY
4081 		repeat_flag = false;
4082 #endif
4083 		do {
4084 			if (*cp == '\0') {
4085 retry:
4086 #if	LIBRARY
4087 				return false;
4088 #else
4089 				my_strlcpy(prompt_str, _("Enter integers to factor: "), sizeof(prompt_str));
4090 				cp = get_string(buf, sizeof(buf));
4091 				if (cp == NULL)
4092 					return false;
4093 				cp_start = cp;
4094 #endif
4095 			}
4096 			if (*cp == '\0')
4097 				return true;
4098 			rv = true;
4099 			for (; *cp; ) {
4100 				cp1 = cp = skip_space(cp);
4101 				errno = 0;
4102 				ed = d = strtod(cp, &cp);
4103 				if (cp == cp1 || errno || !isfinite(d)) {
4104 					goto try_parsing;
4105 				}
4106 				cp = skip_space(cp);
4107 				if (*cp && !isdigit(*cp)) {
4108 					if (*cp == '-') {
4109 						cp2 = cp = skip_space(++cp);
4110 						errno = 0;
4111 						ed = strtod(cp, &cp);
4112 						if (cp == cp2 || errno || !isfinite(ed) || (*cp && *cp != ',' && !isspace(*cp))) {
4113 							goto try_parsing;
4114 						}
4115 					} else {
4116 try_parsing:
4117 						input_column += (cp1 - cp_start);
4118 						cp = parse_expr(tes, &n_tes, cp1, false);
4119 						if (cp == NULL)
4120 							goto retry;
4121 						cp_start = cp;
4122 						if (n_tes <= 0)
4123 							return rv;
4124 						calc_simp(tes, &n_tes);
4125 						if (n_tes != 1 || tes[0].kind != CONSTANT || !isfinite(tes[0].token.constant)) {
4126 							error(_("Integer expected."));
4127 							goto retry;
4128 						}
4129 						ed = d = tes[0].token.constant;
4130 					}
4131 				}
4132 				cp = skip_comma_space(cp);
4133 				count_down = (ed < d);
4134 				for (; count_down ? (d >= ed) : (d <= ed); count_down ? (d -= 1.0) : (d += 1.0)) {
4135 					if (d == 0) {
4136 						fprintf(gfp, _("0 can be evenly divided by any number.\n"));
4137 						continue;
4138 					}
4139 					if (!factor_one(d)) {
4140 						error(_("Number too large to factor or not an integer."));
4141 						rv = false;
4142 						break;
4143 					}
4144 #if	!SILENT
4145 					if (is_prime() && debug_level >= 0) {
4146 						fprintf(gfp, _("Prime number: "));
4147 					}
4148 #endif
4149 					if (!display_unique())
4150 						rv = false;
4151 				}
4152 			}
4153 		} while (repeat_flag);
4154 		return rv;
4155 	}
4156 	if (power_flag) {
4157 		if (extra_characters(cp))
4158 			return false;
4159 		for (i1 = first; i1 <= last; i1++) {
4160 			if (n_lhs[i1]) {
4161 /*				factor_power(lhs[i1], &n_lhs[i1]); */
4162 				do {
4163 					simp_loop(lhs[i1], &n_lhs[i1]);
4164 				} while (factor_power(lhs[i1], &n_lhs[i1]));
4165 				if (n_rhs[i1]) {
4166 /*					factor_power(rhs[i1], &n_rhs[i1]); */
4167 					do {
4168 						simp_loop(rhs[i1], &n_rhs[i1]);
4169 					} while (factor_power(rhs[i1], &n_rhs[i1]));
4170 				}
4171 				if (!return_result(i1))
4172 					return false;
4173 			}
4174 		}
4175 	} else {
4176 		do {
4177 			v = 0;
4178 			if (*cp) {
4179 				if ((cp = parse_var2(&v, cp)) == NULL) {
4180 					return false;
4181 				}
4182 			}
4183 			if (v) {
4184 				found = false;
4185 				for (i1 = first; i1 <= last; i1++) {
4186 					if (var_in_equation(i1, v)) {
4187 						found = true;
4188 						break;
4189 					}
4190 				}
4191 				if (!found) {
4192 					warning(_("Specified variable not found."));
4193 				}
4194 			}
4195 			for (i1 = first; i1 <= last; i1++) {
4196 #if	0
4197 				if (v == 0) {
4198 					if (n_lhs[i1]) {
4199 						simp_loop(lhs[i1], &n_lhs[i1]);
4200 						poly_factor(lhs[i1], &n_lhs[i1], true);
4201 						if (n_rhs[i1]) {
4202 							simp_loop(rhs[i1], &n_rhs[i1]);
4203 							poly_factor(rhs[i1], &n_rhs[i1], true);
4204 						}
4205 					}
4206 				}
4207 #endif
4208 				simpv_equation(i1, v);
4209 			}
4210 		} while (*cp);
4211 		for (i1 = first; i1 <= last; i1++) {
4212 			if (n_lhs[i1]) {
4213 				if (!return_result(i1))
4214 					return false;
4215 			}
4216 		}
4217 	}
4218 	return true;
4219 }
4220 
4221 /*
4222  * Display the number of additive terms in the specified equation space.
4223  *
4224  * Return the total number of terms.
4225  */
4226 int
display_term_count(en)4227 display_term_count(en)
4228 int	en;	/* equation space number */
4229 {
4230 	int	left_count = 0, right_count = 0;
4231 
4232 	if (empty_equation_space(en))
4233 		return 0;
4234 	left_count = level1_plus_count(lhs[en], n_lhs[en]) + 1;
4235 	if (n_rhs[en]) {
4236 		right_count = level1_plus_count(rhs[en], n_rhs[en]) + 1;
4237 		fprintf(gfp, "#%d: LHS consists of %d term%s; ", en + 1, left_count, (left_count == 1) ? "" : "s");
4238 		fprintf(gfp, "RHS consists of %d term%s.\n", right_count, (right_count == 1) ? "" : "s");
4239 	} else {
4240 		fprintf(gfp, "#%d: ", en + 1);
4241 	}
4242 	fprintf(gfp, "Expression consists of a total of %d term%s.\n", left_count + right_count, ((left_count + right_count) == 1) ? "" : "s");
4243 	return(left_count + right_count);
4244 }
4245 
4246 /*
4247  * The unfactor command.
4248  */
4249 int
unfactor_cmd(cp)4250 unfactor_cmd(cp)
4251 char	*cp;
4252 {
4253 	int	first, last;
4254 	int	k;
4255 	int	quick_flag = false, fraction_flag = false, power_flag = false, count_flag = false;
4256 
4257 	for (;; cp = skip_param(cp)) {
4258 		if (strncasecmp(cp, "quick", 4) == 0) {
4259 			quick_flag = true;
4260 			continue;
4261 		}
4262 		if (strncasecmp(cp, "fraction", 4) == 0 || strncasecmp(cp, "fully", 4) == 0) {
4263 			fraction_flag = true;
4264 			continue;
4265 		}
4266 		if (strncasecmp(cp, "power", 4) == 0) {
4267 			power_flag = true;
4268 			continue;
4269 		}
4270 		if (strncasecmp(cp, "count", 4) == 0) {
4271 			count_flag = true;
4272 			continue;
4273 		}
4274 		break;
4275 	}
4276 	if (!get_range_eol(&cp, &first, &last)) {
4277 		return false;
4278 	}
4279 	partial_flag = !fraction_flag;
4280 	if (power_flag) {
4281 		for (k = first; k <= last; k++) {
4282 			if (n_lhs[k] <= 0)
4283 				continue;
4284 			if (quick_flag) {
4285 				uf_power(lhs[k], &n_lhs[k]);
4286 			} else {
4287 				uf_allpower(lhs[k], &n_lhs[k]);
4288 			}
4289 			elim_loop(lhs[k], &n_lhs[k]);
4290 			if (n_rhs[k]) {
4291 				if (quick_flag) {
4292 					uf_power(rhs[k], &n_rhs[k]);
4293 				} else {
4294 					uf_allpower(rhs[k], &n_rhs[k]);
4295 				}
4296 				elim_loop(rhs[k], &n_rhs[k]);
4297 			}
4298 			if (!return_result(k)) {
4299 				partial_flag = true;
4300 				return false;
4301 			}
4302 			if (count_flag) {
4303 				display_term_count(k);
4304 			}
4305 		}
4306 	} else {
4307 		for (k = first; k <= last; k++) {
4308 			if (n_lhs[k] <= 0)
4309 				continue;
4310 			if (quick_flag) {
4311 				uf_tsimp(lhs[k], &n_lhs[k]);
4312 				if (n_rhs[k]) {
4313 					uf_tsimp(rhs[k], &n_rhs[k]);
4314 				}
4315 			} else {
4316 				uf_simp(lhs[k], &n_lhs[k]);
4317 				if (n_rhs[k]) {
4318 					uf_simp(rhs[k], &n_rhs[k]);
4319 				}
4320 			}
4321 			if (!return_result(k)) {
4322 				partial_flag = true;
4323 				return false;
4324 			}
4325 			if (count_flag) {
4326 				display_term_count(k);
4327 			}
4328 		}
4329 	}
4330 	partial_flag = true;
4331 	return true;
4332 }
4333 
4334 int
div_loc_find(expression,n)4335 div_loc_find(expression, n)
4336 token_type	*expression;
4337 int		n;
4338 {
4339 	int	k, div_loc;
4340 	int	level;
4341 
4342 	level = min_level(expression, n);
4343 	for (k = 1, div_loc = -1; k < n; k += 2) {
4344 		if (expression[k].level == level && expression[k].token.operatr == DIVIDE) {
4345 			if (div_loc >= 0) {
4346 				error_bug("Expression not grouped.");
4347 			}
4348 			div_loc = k;
4349 		}
4350 	}
4351 	return div_loc;
4352 }
4353 
4354 /*
4355  * The fraction command.
4356  */
4357 int
fraction_cmd(cp)4358 fraction_cmd(cp)
4359 char	*cp;
4360 {
4361 	int	i, div_loc;
4362 	int	first, last;
4363 	int	num_flag = false, den_flag = false, was_fraction;
4364 
4365 	for (;; cp = skip_param(cp)) {
4366 		if (strncasecmp(cp, "numerator", 3) == 0) {
4367 			num_flag = true;
4368 			continue;
4369 		}
4370 		if (strncasecmp(cp, "denominator", 3) == 0) {
4371 			den_flag = true;
4372 			continue;
4373 		}
4374 		break;
4375 	}
4376 	if (!get_range_eol(&cp, &first, &last)) {
4377 		return false;
4378 	}
4379 	show_usage = false;
4380 	for (i = first; i <= last; i++) {
4381 		if (n_lhs[i]) {
4382 			was_fraction = false;
4383 			simple_frac_repeat_side(lhs[i], &n_lhs[i]);
4384 			div_loc = div_loc_find(lhs[i], n_lhs[i]);
4385 			if (div_loc > 0) {
4386 				was_fraction = true;
4387 				if (num_flag && !den_flag) {
4388 					n_lhs[i] = div_loc;
4389 				} else if (den_flag && !num_flag) {
4390 					blt(&lhs[i][0], &lhs[i][div_loc+1], (n_lhs[i] - (div_loc + 1)) * sizeof(token_type));
4391 					n_lhs[i] -= (div_loc + 1);
4392 				}
4393 			}
4394 			if (n_rhs[i]) {
4395 				simple_frac_repeat_side(rhs[i], &n_rhs[i]);
4396 				div_loc = div_loc_find(rhs[i], n_rhs[i]);
4397 				if (div_loc > 0) {
4398 					was_fraction = true;
4399 					if (num_flag && !den_flag) {
4400 						n_rhs[i] = div_loc;
4401 					} else if (den_flag && !num_flag) {
4402 						blt(&rhs[i][0], &rhs[i][div_loc+1], (n_rhs[i] - (div_loc + 1)) * sizeof(token_type));
4403 						n_rhs[i] -= (div_loc + 1);
4404 					}
4405 				}
4406 			}
4407 			if ((num_flag || den_flag) && !was_fraction) {
4408 				warning(_("Expression is not an algebraic fraction."));
4409 				if (den_flag) {
4410 					error(_("Could not extract denominator."));
4411 					return false;
4412 				}
4413 			}
4414 			if (!return_result(i))
4415 				return false;
4416 		}
4417 	}
4418 	return true;
4419 }
4420 
4421 #if	!LIBRARY
4422 /*
4423  * The quit command.
4424  */
4425 int
quit_cmd(cp)4426 quit_cmd(cp)
4427 char	*cp;
4428 {
4429 	int	ev = 0;
4430 
4431 	if (*cp) {
4432 		ev = decstrtol(cp, &cp);
4433 		if (extra_characters(cp))
4434 			return false;
4435 	}
4436 	exit_program(ev);
4437 	return false;		/* to placate the C compiler */
4438 }
4439 #endif
4440 
4441 #if	!SECURE
4442 /*
4443  * The read command.
4444  */
4445 int
read_cmd(cp)4446 read_cmd(cp)
4447 char	*cp;
4448 {
4449 	int	rv;
4450 
4451 	if (security_level >= 3) {
4452 		show_usage = false;
4453 		error(_("Command disabled by security level."));
4454 		return false;
4455 	}
4456 	if (!repeat_flag || *cp == '\0') {
4457 		return read_file(cp);
4458 	}
4459 	do {
4460 		rv = read_file(cp);
4461 	} while (rv);
4462 	return rv;
4463 }
4464 
4465 /*
4466  * Main read command code.
4467  *
4468  * Return true if successful.
4469  */
4470 int
read_file(cp)4471 read_file(cp)
4472 char	*cp;
4473 {
4474 	int	rv;
4475 	FILE	*fp;
4476 	char	buf[MAX_CMD_LEN];
4477 #if	SHELL_OUT
4478 	char	cl[MAX_CMD_LEN];
4479 	int	ev;	/* exit value */
4480 	char	*lister;
4481 #endif
4482 
4483 	if (*cp == '\0') {
4484 #if	SHELL_OUT
4485 #if	MINGW
4486 		lister = "dir /W/P";
4487 #else
4488 		lister = "ls -C";
4489 #endif
4490 		if (gfp && gfp_filename && gfp_filename[0]) {
4491 			if (snprintf(cl, sizeof(cl), "%s >%s%s", lister, gfp_append_flag ? ">" : "", gfp_filename) >= sizeof(cl)) {
4492 				error(_("Command-line too long."));
4493 				return false;
4494 			}
4495 			clean_up();	/* end any redirection */
4496 		} else {
4497 			if (snprintf(cl, sizeof(cl), "%s", lister) >= sizeof(cl)) {
4498 				error(_("Command-line too long."));
4499 				return false;
4500 			}
4501 		}
4502 #if	!MINGW
4503 		printf(_("Listing contents of "));
4504 		output_current_directory(stdout);
4505 		printf("\n");
4506 #endif
4507 		if ((ev = shell_out(cl))) {
4508 			error(_("Error executing directory lister."));
4509 			printf(_("Decimal exit value = %d, shell command-line = %s\n"), ev, cl);
4510 			return false;
4511 		}
4512 		return true;
4513 #else
4514 		error(_("No file name specified."));
4515 		return false;
4516 #endif
4517 	}
4518 	if (snprintf(buf, sizeof(buf), "%s.in", cp) >= sizeof(buf)) {
4519 		error(_("File name too long."));
4520 		return false;
4521 	}
4522 	fp = fopen(buf, "r");
4523 	if (fp == NULL) {
4524 		buf[strlen(cp)] = '\0';
4525 		fp = fopen(buf, "r");
4526 		if (fp == NULL) {
4527 			if (chdir(buf)) {
4528 				error(_("Can't open requested file to read or change directory to."));
4529 				return false;
4530 			} else {
4531 				printf(_("Current working directory changed to "));
4532 				return output_current_directory(stdout);
4533 			}
4534 		}
4535 	}
4536 	rv = read_sub(fp, buf);
4537 	show_usage = false;
4538 	if (fclose(fp)) {
4539 		perror(buf);
4540 		rv = 1;
4541 	}
4542 	if (rv == 100)
4543 		return(true);
4544 #if	!SILENT
4545 	if (!quiet_mode) {
4546 		if (rv) {
4547 			if (!demo_mode) {
4548 				printf(_("Reading of script file \"%s\" aborted due to failure return status\n"), buf);
4549 				printf(_("of a command or expression parsing, or some other error listed above.\n"));
4550 			}
4551 		} else if (debug_level >= 0) {
4552 			printf(_("Successfully finished reading script file \"%s\".\n"), buf);
4553 		}
4554 	}
4555 #endif
4556 	return(!rv);
4557 }
4558 
4559 /*
4560  * Read and process Mathomatic input from a file pointer.
4561  *
4562  * Return zero if no error, non-zero if read aborted.
4563  */
4564 int
read_sub(fp,filename)4565 read_sub(fp, filename)
4566 FILE	*fp;		/* open Mathomatic input file */
4567 char	*filename;	/* filename of fp */
4568 {
4569 	int	rv;
4570 	jmp_buf	save_save;
4571 	char	*cp;
4572 	int	something_there = false;
4573 
4574 	if (fp == NULL) {
4575 		return -1;
4576 	}
4577 	blt(save_save, jmp_save, sizeof(jmp_save));
4578 	if ((rv = setjmp(jmp_save)) != 0) {	/* trap errors */
4579 		clean_up();
4580 		if (rv == 14) {
4581 			error(_("Expression too large."));
4582 		}
4583 		previous_return_value = 0;
4584 	} else {
4585 		while ((cp = fgets((char *) tlhs, n_tokens * sizeof(token_type), fp)) != NULL) {
4586 			if (*cp) {
4587 				something_there = true;
4588 			}
4589 			if (!display_process(cp)) {
4590 				longjmp(jmp_save, 3);	/* jump to the above error trap */
4591 			}
4592 		}
4593 		if (!something_there) {
4594 			if (chdir(filename)) {
4595 				error(_("Empty file (no script to read)."));
4596 				rv = 1;
4597 			} else {
4598 				printf(_("Current directory changed to "));
4599 				output_current_directory(stdout);
4600 				rv = 100;
4601 			}
4602 		}
4603 	}
4604 	blt(jmp_save, save_save, sizeof(jmp_save));
4605 	return rv;
4606 }
4607 #endif
4608 
4609 #if	SHELL_OUT
4610 static int
edit_sub(cp)4611 edit_sub(cp)
4612 char	*cp;
4613 {
4614 	char	cl[MAX_CMD_LEN];	/* command-line */
4615 	char	*cp1;
4616 	int	ev;	/* exit value */
4617 
4618 edit_again:
4619 	cp1 = getenv("EDITOR");
4620 	if (cp1 == NULL) {
4621 #if	CYGWIN || MINGW
4622 		cp1 = "notepad";
4623 #else
4624 		cp1 = "nano";
4625 #endif
4626 		warning("EDITOR environment variable not set; using default text editor.");
4627 	}
4628 	if (snprintf(cl, sizeof(cl), "%s %s", cp1, cp) >= sizeof(cl)) {
4629 		error(_("Editor command-line too long."));
4630 		return false;
4631 	}
4632 	if ((ev = shell_out(cl))) {
4633 		error("Error executing editor, check EDITOR environment variable.");
4634 		printf(_("Decimal exit value = %d, shell command-line = %s\n"), ev, cl);
4635 		return false;
4636 	}
4637 	clear_all();
4638 	if (!read_cmd(cp)) {
4639 		if (pause_cmd(_("Prepare to rerun the editor, or type \"quit\""))) {
4640 			goto edit_again;
4641 		}
4642 	}
4643 	return true;
4644 }
4645 
4646 /*
4647  * The edit command.
4648  */
4649 int
edit_cmd(cp)4650 edit_cmd(cp)
4651 char	*cp;
4652 {
4653 	FILE	*fp;
4654 #if	!MINGW
4655 	int	fd;
4656 #endif
4657 	int	rv;
4658 	char	tmp_file[MAX_CMD_LEN];
4659 
4660 	show_usage = false;
4661 	if (security_level) {
4662 		if (security_level < 0) {
4663 			error(_("Running the editor is not possible with m4."));
4664 		} else {
4665 			error(_("Command disabled by security level."));
4666 		}
4667 		return false;
4668 	}
4669 	clean_up();	/* end any redirection */
4670 	if (*cp == '\0') {
4671 #if	MINGW
4672 		my_strlcpy(tmp_file, "mathomatic.tmp", sizeof(tmp_file));
4673 		fp = fopen(tmp_file, "w+");
4674 		if (fp == NULL) {
4675 			perror(tmp_file);
4676 			error(_("Can't create temporary file."));
4677 			return false;
4678 		}
4679 #else
4680 		my_strlcpy(tmp_file, TMP_FILE, sizeof(tmp_file));
4681 		fd = mkstemp(tmp_file);
4682 		if (fd < 0 || (fp = fdopen(fd, "w+")) == NULL) {
4683 			perror(tmp_file);
4684 			error(_("Can't create temporary file."));
4685 			return false;
4686 		}
4687 #endif
4688 		gfp = fp;
4689 		high_prec = true;
4690 		list_cmd("all");
4691 		high_prec = false;
4692 		gfp = default_out;
4693 		rv = !ferror(fp);
4694 		if (fclose(fp) || !rv) {
4695 			rv = false;
4696 			perror(tmp_file);
4697 			error(_("Writing temporary file failed."));
4698 		} else {
4699 			rv = edit_sub(tmp_file);
4700 		}
4701 		if (unlink(tmp_file)) {
4702 			perror(tmp_file);
4703 		}
4704 		return rv;
4705 	} else {
4706 		show_usage = true;
4707 		if (access(cp, R_OK | W_OK)) {
4708 			perror(cp);
4709 			error(_("You can only edit existing/writable files or all equation spaces."));
4710 			return false;
4711 		}
4712 		return edit_sub(cp);
4713 	}
4714 }
4715 #endif
4716 
4717 #if	!SECURE
4718 /*
4719  * The save command.
4720  */
4721 int
save_cmd(cp)4722 save_cmd(cp)
4723 char	*cp;
4724 {
4725 	FILE	*fp;
4726 	int	rv, space_flag = false, error_flag;
4727 	char	*cp1;
4728 
4729 	if (security_level >= 2) {
4730 		show_usage = false;
4731 		error(_("Command disabled by security level."));
4732 		return false;
4733 	}
4734 	clean_up();	/* end any redirection */
4735 	if (*cp == '\0') {
4736 		error(_("No file name specified; nothing was saved."));
4737 		return false;
4738 	}
4739 	for (cp1 = cp; *cp1; cp1++) {
4740 		if (isspace(*cp1)) {
4741 			space_flag = true;
4742 		}
4743 	}
4744 #if	!SILENT
4745 	if (access(cp, F_OK) == 0) {
4746 		if (access(cp, W_OK)) {
4747 			perror(cp);
4748 			error(_("Specified save file is not writable; choose a different file name."));
4749 			return false;
4750 		}
4751 		snprintf(prompt_str, sizeof(prompt_str), _("File \"%s\" exists, overwrite (y/n)? "), cp);
4752 		if (!get_yes_no()) {
4753 			error(_("File not overwritten; nothing was saved."));
4754 			return false;
4755 		}
4756 	} else if (space_flag) {
4757 		snprintf(prompt_str, sizeof(prompt_str), _("File name \"%s\" contains space characters, create anyways (y/n)? "), cp);
4758 		if (!get_yes_no()) {
4759 			error(_("Save command aborted; nothing was saved."));
4760 			return false;
4761 		}
4762 	}
4763 #endif
4764 	fp = fopen(cp, "w");
4765 	if (fp == NULL) {
4766 		perror(cp);
4767 		error(_("Cannot create specified save file; nothing was saved."));
4768 		return false;
4769 	}
4770 	gfp = fp;
4771 	high_prec = true;
4772 	rv = list_cmd("all");
4773 	high_prec = false;
4774 	gfp = default_out;
4775 	error_flag = ferror(fp);
4776 	if (fclose(fp) || error_flag) {
4777 		rv = false;
4778 		perror(cp);
4779 	}
4780 	if (rv) {
4781 #if	!SILENT
4782 		printf(_("All expressions saved in file \"%s\".\n"), cp);
4783 #endif
4784 	} else {
4785 		error(_("Error encountered while saving expressions."));
4786 	}
4787 	return rv;
4788 }
4789 #endif
4790