wcalc  2.5
About: Wcalc is a natural-expression command-line calculator.
  Fossies Dox: wcalc-2.5.tar.gz  ("inofficial" and yet experimental doxygen-generated source code documentation)  

main.c
Go to the documentation of this file.
1 /* Based (very loosely) on Ostermann's headstart for the shell project
2  * Modified by Kyle Wheeler
3  */
4 
5 #ifdef HAVE_CONFIG_H
6 # include "config.h"
7 #endif
8 #include <stdlib.h>
9 #include <stdio.h>
10 #include <string.h>
11 #include <strings.h> /* for strncasecmp(), per POSIX 2001 */
12 #include <unistd.h>
13 #include <sys/types.h>
14 #include <sys/stat.h> /* for stat() */
15 #include <ctype.h> /* for isdigit and isalpha */
16 #include <errno.h>
17 #include <fcntl.h> /* for open() */
18 #include <limits.h> /* for stroul() */
19 #include <sys/mman.h> /* for mmap() */
20 #include <assert.h> /* for assert() */
21 #include <stdarg.h> /* for va_start() */
22 #include "number.h"
23 
24 #ifdef HAVE_LIBREADLINE
25 # if defined(HAVE_READLINE_READLINE_H)
26 # include <readline/readline.h>
27 # elif defined(HAVE_READLINE_H)
28 # include <readline.h>
29 # else /* !defined(HAVE_READLINE_H) */
30 extern char *readline();
31 # endif /* !defined(HAVE_READLINE_H) */
32 /*@null@*/
33 char *cmdline = NULL;
34 #else /* !defined(HAVE_READLINE_READLINE_H) */
35 /* no readline */
36 #endif /* HAVE_LIBREADLINE */
37 
38 #ifdef HAVE_READLINE_HISTORY
39 # if defined(HAVE_READLINE_HISTORY_H)
40 # include <readline/history.h>
41 # elif defined(HAVE_HISTORY_H)
42 # include <history.h>
43 # else /* ! defined(HAVE_HISTORY_H) */
44 extern void add_history();
45 extern int write_history();
46 extern int read_history();
47 extern int history_truncate_file(char *,
48  int);
49 # endif /* defined(HAVE_READLINE_HISTORY_H) */
50 /* no history */
51 #endif /* HAVE_READLINE_HISTORY */
52 
53 #include "calculator.h"
54 #include "conversion.h"
55 #ifdef HAVE_CONFIG_H /* auto-tool build */
56 # include "parser.h"
57 #else
58 # include "y.tab.h"
59 #endif
60 #include "variables.h"
61 #include "files.h"
62 #include "historyManager.h"
63 #include "list.h"
64 #include "iscmd.h"
65 #include "isconst.h"
66 #include "isfunc.h"
67 #include "output.h"
68 #include "evalvar.h"
69 
70 #define TRUEFALSE (!strcmp(value, "yes") || !strcmp(value, "true") || !strcmp(value, "1"))
71 #define BIG_STRING 4096
72 #define VERSION PACKAGE_VERSION
73 #define EXIT_EARLY(code) do { \
74  clearHistory(); \
75  cleanupvar(); \
76  num_free(last_answer); \
77  lists_cleanup(); \
78  fflush(NULL); \
79  exit(code); \
80 } while (0)
81 
82 static int read_prefs(void);
83 static int read_preload(void);
84 static void display_and_clear_errstring(void);
85 
86 /*
87  * These are declared here because they're not in any header files.
88  * yyparse() is declared with an empty argument list so that it is
89  * compatible with the generated C code from yacc/bison.
90  * This part is taken from http://www.bgw.org/tutorials/programming/c/lex_yacc/main.c
91  */
92 extern int yyparse(void);
93 extern int yy_scan_string(const char *);
94 
95 static int exit_on_err = 0;
96 
97 #ifdef HAVE_LIBREADLINE
98 /*@null@*/
99 static List tc_options = NULL;
100 
101 /*@null@*/
102 static char *
103 tc_generator(const char *text,
104  int state)
105 { /*{{{ */
106  char *ret = getHeadOfList(tc_options);
107 
108  if (ret) {
109  return ret;
110  } else {
111  return NULL;
112  }
113 } /*}}} */
114 
115 /*@null@*/
116 static char *
117 tc_rounding(const char *text,
118  int state)
119 { /*{{{ */
120  static unsigned int i = 0;
121  char *rounding[] = { "none", "simple", "sig_fig", 0 };
122 
123  if (state == 0) {
124  i = 0;
125  }
126  while (rounding[i] != NULL) {
127  if (strncmp(text, rounding[i], strlen(text)) == 0) {
128  return strdup(rounding[i++]);
129  }
130  i++;
131  }
132  return NULL;
133 } /*}}} */
134 
135 /*@null@*/
136 static char *
137 tc_engineering(const char *text,
138  int state)
139 { /*{{{ */
140  static unsigned int i = 0;
141  char *engineering[] = { "always", "never", "auto", "automatic", 0 };
142 
143  if (state == 0) {
144  i = 0;
145  }
146  while (engineering[i] != NULL) {
147  if (strncmp(text, engineering[i], strlen(text)) == 0) {
148  return strdup(engineering[i++]);
149  }
150  i++;
151  }
152  return NULL;
153 } /*}}} */
154 
155 char **qcommands = NULL;
156 
157 # define COMPLETE(strs) do { \
158  int compareword = 0; \
159  int comparechar = 0; \
160  int textcurs = 0; \
161  while (strs[compareword] != NULL) { \
162  if (text[textcurs] == 0) { /* add to the list of possibilities */ \
163  addToList(&tc_options, strdup(strs[compareword])); \
164  textcurs = 0; \
165  comparechar = 0; \
166  compareword++; \
167  } else if (text[textcurs] == strs[compareword][comparechar]) { \
168  textcurs++; \
169  comparechar++; \
170  } else { /* not a possibility: next! */ \
171  textcurs = 0; \
172  compareword++; \
173  comparechar = 0; \
174  } \
175  } \
176 } while (0)
177 # define COMPLETE2(strs) do { \
178  const unsigned textlen = strlen(text); \
179  for (unsigned _c_i = 0; strs[_c_i].explanation; _c_i++) { \
180  const char *const *const _c_names = strs[_c_i].names; \
181  for (unsigned _c_j = 0; _c_names[_c_j]; _c_j++) { \
182  if (!strncmp(text, _c_names[_c_j], textlen)) { \
183  /* add to the list of possibilities */ \
184  addToList(&tc_options, strdup(_c_names[_c_j])); \
185  } \
186  } \
187  } \
188 } while (0)
189 
190 static void
191 build_qcommands(void)
192 {
193  unsigned c_count = 0;
194 
195  for (unsigned c = 0; commands[c].explanation; c++) {
196  for (unsigned n = 0; commands[c].names[n]; n++) {
197  c_count++;
198  }
199  }
200  c_count++;
201  qcommands = malloc(sizeof(char *) * c_count);
202  c_count = 0;
203  for (unsigned c = 0; commands[c].explanation; c++) {
204  for (unsigned n = 0; commands[c].names[n]; n++) {
205  qcommands[c_count] = malloc(strlen(commands[c].names[n]) + 2);
206  sprintf(qcommands[c_count], "\\%s", commands[c].names[n]);
207  c_count++;
208  }
209  }
210  qcommands[c_count] = NULL;
211 }
212 
213 static char **
214 wcalc_completion(const char *text,
215  int start,
216  int end)
217 { /*{{{ */
218  /*extern const char *commands[];*/
219  char **variables;
220  char **retvals = NULL;
221 
222  // printf("\ncompleting: %s\n", text);
223  if ('\\' == rl_line_buffer[0]) {
224  if ((NULL == strchr(rl_line_buffer, ' ')) &&
225  (NULL == strchr(rl_line_buffer, '\t'))) {
226  COMPLETE(qcommands);
227  } else if (strncmp("\\explain", rl_line_buffer, 8) == 0) {
228  int i = 8;
229 
230  while (isspace(rl_line_buffer[i])) ++i;
231  if (i == start) {
232  COMPLETE(qcommands);
233  COMPLETE2(consts);
234  COMPLETE2(funcs);
235  variables = listvarnames();
236  COMPLETE(variables);
237  free(variables);
238  }
239  } else if ((strncmp("\\open", rl_line_buffer, 5) == 0) ||
240  (strncmp("\\save", rl_line_buffer, 5) == 0)) {
241  int i = 5;
242 
243  while (isspace(rl_line_buffer[i])) ++i;
244  if (i == start) {
245  retvals =
246  rl_completion_matches(text,
247  rl_filename_completion_function);
248  return retvals;
249  }
250  } else if (((strncmp("\\rou", rl_line_buffer, 4) == 0) &&
251  isspace(rl_line_buffer[4])) ||
252  ((strncmp("\\round", rl_line_buffer, 6) == 0) &&
253  isspace(rl_line_buffer[6])) ||
254  ((strncmp("\\rounding", rl_line_buffer, 9) == 0) &&
255  isspace(rl_line_buffer[9]))) {
256  int i = 4;
257 
258  while (!isspace(rl_line_buffer[i])) ++i;
259  while (isspace(rl_line_buffer[i])) ++i;
260  if (i == start) {
261  retvals = rl_completion_matches(text, tc_rounding);
262  return retvals;
263  }
264  } else if (((strncmp("\\e", rl_line_buffer, 2) == 0) &&
265  isspace(rl_line_buffer[2])) ||
266  ((strncmp("\\eng", rl_line_buffer, 4) == 0) &&
267  isspace(rl_line_buffer[4])) ||
268  ((strncmp("\\engineering", rl_line_buffer, 12) == 0) &&
269  isspace(rl_line_buffer[12]))) {
270  int i = 2;
271 
272  while (!isspace(rl_line_buffer[i])) ++i;
273  while (isspace(rl_line_buffer[i])) ++i;
274  if (i == start) {
275  retvals = rl_completion_matches(text, tc_engineering);
276  return retvals;
277  }
278  } else if (((strncmp("\\c", rl_line_buffer, 2) == 0) &&
279  isspace(rl_line_buffer[2])) ||
280  ((strncmp("\\conv", rl_line_buffer, 5) == 0) &&
281  isspace(rl_line_buffer[5])) ||
282  ((strncmp("\\convert", rl_line_buffer, 8) == 0) &&
283  isspace(rl_line_buffer[8]))) {
284  int i = 2;
285  /*extern const struct conversion lengths[], areas[], volumes[],
286  * masses[], speeds[], powers[], forces[], accelerations[];*/
287  extern const struct conversion *conversions[];
288 
289  while (!isspace(rl_line_buffer[i])) ++i;
290  while (isspace(rl_line_buffer[i])) ++i;
291  if (i == start) {
292  /* complete on ALL units */
293  size_t unit, conversion;
294 
296  for (unit = 0; conversions[conversion][unit].name; unit++) {
297  COMPLETE(conversions[conversion][unit].aka);
298  }
299  }
300  } else {
301  /* seek past the previous unit... */
302  char *unit1 = rl_line_buffer + i;
303  char saved_char;
304  ssize_t unit_cat;
305 
306  while (!isspace(rl_line_buffer[i])) ++i;
307  saved_char = rl_line_buffer[i];
308  rl_line_buffer[i] = 0;
309  unit_cat = identify_unit(unit1);
310  rl_line_buffer[i] = saved_char;
311  if (unit_cat != -1) {
312  while (isspace(rl_line_buffer[i])) ++i;
313  if (i == start) {
314  size_t unit;
315 
316  /* complete on COMPATABLE units */
317  for (unit = 0; conversions[unit_cat][unit].name;
318  unit++) {
319  COMPLETE(conversions[unit_cat][unit].aka);
320  }
321  addToList(&tc_options, strdup("to"));
322  } else if ((strncmp(rl_line_buffer + i, "to", 2) == 0) &&
323  isspace(rl_line_buffer[i + 2])) {
324  i += 2;
325  while (isspace(rl_line_buffer[i])) ++i;
326  if (i == start) {
327  size_t unit;
328 
329  /* complete on COMPATABLE units */
330  for (unit = 0; conversions[unit_cat][unit].name;
331  unit++) {
332  COMPLETE(conversions[unit_cat][unit].aka);
333  }
334  }
335  }
336  }
337  }
338  }
339  } else {
340  COMPLETE(qcommands);
341  COMPLETE2(consts);
342  COMPLETE2(funcs);
343  variables = listvarnames();
344  COMPLETE(variables);
345  free(variables);
346  }
347  rl_attempted_completion_over = 1; // do not use standard file completion
348  rl_completion_entry_function = tc_generator;
349  retvals = rl_completion_matches(text, tc_generator);
350  return retvals;
351 } /*}}} */
352 #endif /* ifdef HAVE_LIBREADLINE */
353 
354 enum ui_colors {
373 };
374 const char *const colors[] = {
375  "", // NONE
376  "\033[0m", // RESET
377  "\033[30m",
378  "\033[31m",
379  "\033[32m",
380  "\033[33m",
381  "\033[34m",
382  "\033[35m",
383  "\033[36m",
384  "\033[37m",
385  "\033[1m\033[30m",
386  "\033[1m\033[31m",
387  "\033[1m\033[32m",
388  "\033[1m\033[33m",
389  "\033[1m\033[34m",
390  "\033[1m\033[35m",
391  "\033[1m\033[36m",
392  "\033[1m\033[37m",
393 };
394 
395 enum ui_pieces {
412 };
414  NONE, // PROMPT
415  NONE, // CONV_CAT
416  NONE, // CONV_UNIT
417  NONE, // APPROX_ANSWER
418  NONE, // EXACT_ANSWER
419  NONE, // ERR_LOCATION
420  NONE, // ERR_TEXT
421  NONE, // PREF_NAME
422  NONE, // PREF_VAL
423  NONE, // PREF_CMD
424  NONE, // STATUS
425  NONE, // VAR_NAME
426  NONE, // VAR_DESC
427  NONE, // SUBVAR_NAME
428  NONE, // EXPLANATION
429  NONE, // UNCOLOR
430 };
431 int color_ui[] = {
432  BLUE, // PROMPT
433  BOLDYELLOW, // CONV_CAT
434  CYAN, // CONV_UNIT
435  YELLOW, // APPROX_ANSWER
436  GREEN, // EXACT_ANSWER
437  BOLDMAGENTA, // ERR_LOCATION
438  BOLDRED, // ERR_TEXT
439  YELLOW, // PREF_NAME
440  CYAN, // PREF_VAL
441  BLUE, // PREF_CMD
442  BOLDGREEN, // STATUS
443  BOLDCYAN, // VAR_NAME
444  CYAN, // VAR_DESC
445  CYAN, // SUBVAR_NAME
446  NONE, // EXPLANATION
447  RESET, // UNCOLOR
448 };
450 
451 static void
453 {
454  printf("\n%s%s%s\n", colors[uiselect[CONV_CAT]], conversion_names[nCategory], colors[uiselect[UNCOLOR]]);
455  size_t unit, nAka;
456 
457  for (unit = 0; conversions[nCategory][unit].name; ++unit) {
458  printf("\n%s%s%s: ", colors[uiselect[CONV_UNIT]], conversions[nCategory][unit].name, colors[uiselect[UNCOLOR]]);
459  for (nAka = 0; nAka < 9; ++nAka) {
460  if (conversions[nCategory][unit].aka[nAka] != NULL) {
461  if (nAka > 0) { printf(", "); }
462  printf("%s", conversions[nCategory][unit].aka[nAka]);
463  }
464  }
465  }
466  printf("\n\n");
467 }
468 
469 void
470 show_answer(char *err,
471  int uncertain,
472  char *answer)
473 { /*{{{*/
474  if (err && strlen(err)) {
476  }
477  if (uncertain) {
478  printf("%s%s %s%s\n", colors[uiselect[APPROX_ANSWER]], conf.print_equal ? "~=" : " ", colors[uiselect[UNCOLOR]], answer);
479  } else {
480  printf("%s%s %s%s\n", colors[uiselect[EXACT_ANSWER]], conf.print_equal ? " =" : " ", colors[uiselect[UNCOLOR]], answer);
481  }
482 } /*}}}*/
483 
484 static void
486 { /*{{{ */
487  extern int scanerror;
488  extern char *errstring;
489  extern int errloc;
490 
491  if (errstring && errstring[0]) {
492  if (errloc != -1) {
493  int i;
494  extern int show_line_numbers;
495  extern char *last_input;
496 
497  if (show_line_numbers && last_input) {
498  fprintf(stderr, "-> %s\n", last_input);
499  }
500  fprintf(stderr, " ");
501  for (i = 0; i < errloc; i++) {
502  fprintf(stderr, " ");
503  }
504  fprintf(stderr, "%s^%s\n", colors[uiselect[ERR_LOCATION]], colors[uiselect[UNCOLOR]]);
505  errloc = -1;
506  }
507  fprintf(stderr, "%s%s%s", colors[uiselect[ERR_TEXT]], errstring, colors[uiselect[UNCOLOR]]);
508  if (errstring[strlen(errstring) - 1] != '\n') {
509  fprintf(stderr, "\n");
510  }
511  free(errstring);
512  errstring = NULL;
513  scanerror = 0;
514  if (exit_on_err) {
515  EXIT_EARLY(EXIT_FAILURE);
516  }
517  }
518 } /*}}} */
519 
520 int
521 main(int argc,
522  char *argv[])
523 { /*{{{ */
524  extern int yydebug;
525  extern int lines;
526 
527 #ifdef HAVE_LIBREADLINE
528  char *readme = NULL;
529 #else
530  char readme[BIG_STRING];
531 #endif
532 #ifdef HAVE_READLINE_HISTORY
533  char *historyfile = "/.wcalc_history";
534 #endif
535  int tty, i;
536  short cmdline_input = 0;
537 
538  // yydebug = 1; /* turn on ugly YACC debugging */
539  yydebug = 0; /* turn off ugly YACC debugging */
540 
541  initvar();
542  lists_init();
543 
544  /* initialize the preferences */
545  conf.precision = -1;
547  standard_output = 1;
548  conf.picky_variables = 1;
549  conf.print_prefixes = 1;
550  conf.precision_guard = 1;
553  conf.thou_delimiter = ',';
554  conf.dec_delimiter = '.';
555  conf.print_equal = 1;
556  conf.print_ints = 0;
557  conf.print_commas = 0;
558  conf.verbose = 0;
559  conf.c_style_mod = 1;
561 
562  init_numbers();
564 
565  /* load the preferences */
566  {
567  int foundflag = 0;
568 
569  /* quick check the commandline for a --defaults argument */
570  for (i = 1; i < argc; ++i) {
571  if (!strcmp(argv[i], "--defaults")) {
572  foundflag = 1;
573  break;
574  }
575  }
576 
577  if (foundflag == 0) {
578  if (read_prefs()) {
579  read_preload();
580  } else {
581  perror("Error reading preferences file (~/.wcalcrc)");
582  }
583  }
584  }
585 
586  /* Parse commandline options */
587  for (i = 1; i < argc; ++i) {
588  if (!strncmp(argv[i], "-P", 2)) {
589  long int argnum;
590  char *endptr;
591  char *valstr;
592 
593  if ((strlen(&argv[i][2]) == 0) && (i + 1 < argc)) {
594  // pull the arg from i+1
595  valstr = argv[++i];
596  } else {
597  valstr = &argv[i][2];
598  }
599  argnum = strtol(valstr, &endptr, 0);
600  if ((*endptr != '\0') || (endptr == valstr)) {
601  if (strlen(valstr)) {
602  fprintf(stderr,
603  "-P option requires a valid integer (found '%s' instead).\n", valstr);
604  } else {
605  fprintf(stderr,
606  "-P option requires a valid integer.\n");
607  }
608  EXIT_EARLY(EXIT_FAILURE);
609  } else {
610  conf.precision = (int)argnum;
611  }
612  } else if (!strcmp(argv[i], "-E") ||
613  !strcmp(argv[i], "--engineering")) {
615  } else if (!strcmp(argv[i], "-EE")) {
617  } else if (!strcmp(argv[i], "-H") || !strcmp(argv[i], "--help")) {
619  EXIT_EARLY(EXIT_SUCCESS);
620  } else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) {
621  printf("wcalc "VERSION "\n");
622  EXIT_EARLY(EXIT_SUCCESS);
623  } else if (!strcmp(argv[i], "-u") || !strcmp(argv[i], "--units")) {
624  if ((i + 1 >= argc) || !strcasecmp(argv[i + 1], "all")) {
625  int nCategory;
626  for (nCategory = 0; nCategory <= MAX_TYPE; ++nCategory) {
627  PrintConversionUnitCategory(nCategory);
628  }
629  } else if (strlen(argv[i + 1]) == 1) {
630  fprintf(stderr, "The %s option's (optional) argument must be two or more letters of one\nof the following (case insensitive):\n", argv[i]);
631  int nCategory;
632  for (nCategory = 0; nCategory <= MAX_TYPE; ++nCategory) {
633  printf("\t%s\n", conversion_names[nCategory]);
634  }
635  EXIT_EARLY(EXIT_FAILURE);
636  } else {
637  const size_t len = strlen(argv[i + 1]);
638  int printed = 0;
639  int nCategory;
640  for (nCategory = 0; nCategory <= MAX_TYPE; ++nCategory) {
641  if (!strncasecmp(argv[i + 1], conversion_names[nCategory], len)) {
642  printed = 1;
643  PrintConversionUnitCategory(nCategory);
644  break;
645  }
646  }
647  if (printed == 0) {
648  fprintf(stderr, "The %s option's (optional) argument must be two or more letters of one\nof the following (case insensitive):\n", argv[i]);
649  for (nCategory = 0; nCategory <= MAX_TYPE; ++nCategory) {
650  printf("\t%s\n", conversion_names[nCategory]);
651  }
652  EXIT_EARLY(EXIT_FAILURE);
653  }
654  }
655  EXIT_EARLY(EXIT_SUCCESS);
656  } else if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--decimal") ||
657  !strcmp(argv[i], "-dec") || !strcmp(argv[i], "--dec")) {
659  } else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--hexadecimal") ||
660  !strcmp(argv[i], "-hex") || !strcmp(argv[i], "--hex")) {
662  } else if (!strcmp(argv[i], "-o") || !strcmp(argv[i], "--octal") ||
663  !strcmp(argv[i], "-oct") || !strcmp(argv[i], "--oct")) {
665  } else if (!strcmp(argv[i], "-b") || !strcmp(argv[i], "--binary") ||
666  !strcmp(argv[i], "-bin") || !strcmp(argv[i], "--bin")) {
668  } else if (!strcmp(argv[i], "-p") || !strcmp(argv[i], "--prefixes")) {
670  /*} else if (!strcmp(argv[i], "-l") || !strcmp(argv[i], "--lenient")) {
671  * conf.picky_variables = 0; */
672  } else if (!strcmp(argv[i], "-r") || !strcmp(argv[i], "--radians")) {
674  } else if (!strcmp(argv[i], "-q") || !strcmp(argv[i], "--quiet")) {
676  } else if (!strcmp(argv[i], "-c") ||
677  !strcmp(argv[i], "--conservative")) {
679  } else if (!strcmp(argv[i], "-R") || !strcmp(argv[i], "--remember")) {
681  } else if (!strncmp(argv[i], "--round=", 8)) {
682  if (!strcmp(&(argv[i][8]), "no") ||
683  !strcmp(&(argv[i][8]), "none")) {
685  } else if (!strcmp(&(argv[i][8]), "simple")) {
687  } else if (!strcmp(&(argv[i][8]), "sig_fig")) {
689  }
690  } else if (!strcmp(argv[i], "--round")) {
691  fprintf(stderr,
692  "--round requires an argument (none|simple|sig_fig)\n");
693  EXIT_EARLY(EXIT_FAILURE);
694  } else if (!strncmp(argv[i], "--idsep=", 7)) {
695  if ((strlen(argv[i]) > 8) || (strlen(argv[i]) == 7)) {
696  fprintf(stderr, "--idsep= must have an argument\n");
697  EXIT_EARLY(EXIT_FAILURE);
698  }
699  if ((conf.in_thou_delimiter != argv[i][7]) && ((conf.in_thou_delimiter != 0) || (conf.thou_delimiter != argv[i][7]))) {
700  conf.in_dec_delimiter = argv[i][7];
701  } else {
702  fprintf(stderr,
703  "%c cannot be the decimal separator. It is the thousands separator.\n",
704  argv[i][7]);
705  EXIT_EARLY(EXIT_FAILURE);
706  }
707  } else if (!strncmp(argv[i], "--dsep=", 7)) {
708  if ((strlen(argv[i]) > 8) || (strlen(argv[i]) == 7)) {
709  fprintf(stderr, "--dsep= must have an argument\n");
710  EXIT_EARLY(EXIT_FAILURE);
711  }
712  if (conf.thou_delimiter != argv[i][7]) {
713  conf.dec_delimiter = argv[i][7];
714  } else {
715  fprintf(stderr,
716  "%c cannot be the decimal separator. It is the thousands separator.\n",
717  argv[i][7]);
718  EXIT_EARLY(EXIT_FAILURE);
719  }
720  } else if (!strncmp(argv[i], "--itsep=", 7)) {
721  if ((strlen(argv[i]) > 8) || (strlen(argv[i]) == 7)) {
722  fprintf(stderr, "--itsep= must have an argument\n");
723  EXIT_EARLY(EXIT_FAILURE);
724  }
725  if ((conf.in_dec_delimiter != argv[i][7]) && ((conf.in_dec_delimiter != 0) || (conf.dec_delimiter != argv[i][7]))) {
726  conf.in_thou_delimiter = argv[i][7];
727  } else {
728  fprintf(stderr,
729  "%c cannot be the thousands separator. It is the decimal separator.\n",
730  argv[i][7]);
731  EXIT_EARLY(EXIT_FAILURE);
732  }
733  } else if (!strncmp(argv[i], "--tsep=", 7)) {
734  if ((strlen(argv[i]) > 8) || (strlen(argv[i]) == 7)) {
735  fprintf(stderr, "--tsep= must have an argument\n");
736  EXIT_EARLY(EXIT_FAILURE);
737  }
738  if (conf.dec_delimiter != argv[i][7]) {
739  conf.thou_delimiter = argv[i][7];
740  } else {
741  fprintf(stderr,
742  "%c cannot be the thousands separator. It is the decimal separator.\n",
743  argv[i][7]);
744  EXIT_EARLY(EXIT_FAILURE);
745  }
746  } else if (!strncmp(argv[i], "--bits", 6)) {
747  unsigned long int argnum;
748  char *endptr;
749 
750  argnum = strtoul(&(argv[i][6]), &endptr, 0);
751  if ((endptr != NULL) && (strlen(endptr) > 0)) {
752  fprintf(stderr,
753  "--bits option requires a valid integer without spaces.\n");
754  EXIT_EARLY(EXIT_FAILURE);
755  } else {
756  if (argnum < NUM_PREC_MIN) {
757  fprintf(stderr, "Minimum precision is %lu.\n", (unsigned long)NUM_PREC_MIN);
758  } else if (argnum > NUM_PREC_MAX) {
759  fprintf(stderr, "Maximum precision is %lu.\n", (unsigned long)NUM_PREC_MAX);
760  } else {
761  num_set_default_prec(argnum);
762  }
763  }
764  } else if (!strcmp(argv[i], "--ints")) {
766  } else if (!strcmp(argv[i], "--delim")) {
768  } else if (!strcmp(argv[i], "--verbose")) {
770  } else if (!strcmp(argv[i], "--yydebug")) {
771  yydebug = 1;
772  } else if (!strcmp(argv[i], "-n")) {
773  conf.print_equal = 0;
774  } else if (!strcmp(argv[i], "-C") || !strcmp(argv[i], "--color")) {
776  if (conf.color_ui) {
777  uiselect = color_ui;
778  } else {
780  }
781  } else if (!strcmp(argv[i], "--defaults")) {
782  /* ignore this argument */
783  } else {
784  extern char *errstring;
785 
786  if (isatty(1) == 0) { /* Find out where stdout is going */
788  }
789 
790  if (!cmdline_input) {
791 #ifdef HAVE_READLINE_HISTORY
792  char *filename;
793  const char *const home = getenv("HOME");
794 
795  using_history();
796  filename = malloc(strlen(home) + strlen(historyfile) + 2);
797  snprintf(filename,
798  strlen(home) + strlen(historyfile) + 1,
799  "%s%s",
800  home, historyfile);
801  if (read_history(filename)) {
802  if (errno != ENOENT) {
803  perror("Reading History");
804  }
805  }
806  free(filename);
807 #endif /* ifdef HAVE_READLINE_HISTORY */
808  cmdline_input = 1;
809  }
810  if (conf.verbose) {
811  printf("-> %s\n", argv[i]);
812  }
813  parseme(argv[i]);
814  if (!errstring || (errstring && !strlen(errstring)) ||
816  addToHistory(argv[i], last_answer);
817  }
818  if (errstring && strlen(errstring)) {
819  fprintf(stderr, "%s", errstring);
820  if (errstring[strlen(errstring) - 1] != '\n') {
821  fprintf(stderr, "\n");
822  }
823  free(errstring);
824  errstring = NULL;
825  }
826  if (putval("a", last_answer, "previous answer") != 0) {
827  fprintf(stderr, "error storing last answer\n");
828  }
829  }
830  }
831 
832  if (!cmdline_input) {
833  extern char *errstring;
834  char *envinput = getenv("wcalc_input");
835  if (envinput) {
836 #ifdef HAVE_READLINE_HISTORY
837  char *filename;
838  const char *const home = getenv("HOME");
839 
840  using_history();
841  filename = malloc(strlen(home) + strlen(historyfile) + 2);
842  strcpy(filename, home);
843  strcat(filename, historyfile);
844  if (read_history(filename)) {
845  if (errno != ENOENT) {
846  perror("Reading History");
847  }
848  }
849  free(filename);
850 #endif /* ifdef HAVE_READLINE_HISTORY */
851  cmdline_input = 1;
852  if (conf.verbose) {
853  printf("-> %s\n", envinput);
854  }
855  parseme(envinput);
856  if (!errstring || (errstring && !strlen(errstring)) ||
858  addToHistory(envinput, last_answer);
859  }
860  if (errstring && strlen(errstring)) {
861  fprintf(stderr, "%s", errstring);
862  if (errstring[strlen(errstring) - 1] != '\n') {
863  fprintf(stderr, "\n");
864  }
865  free(errstring);
866  errstring = NULL;
867  }
868  if (putval("a", last_answer, "previous answer") != 0) {
869  fprintf(stderr, "error storing last answer\n");
870  }
871  }
872  }
873 
874  if (cmdline_input) {
875 #ifdef HAVE_READLINE_HISTORY
876  char *filename;
877  char *home = getenv("HOME");
878 
879  filename = malloc(strlen(home) + strlen(historyfile) + 2);
880  snprintf(filename,
881  strlen(home) + strlen(historyfile) + 1,
882  "%s%s",
883  home, historyfile);
884  if (write_history(filename)) {
885  perror("Saving History 1");
886  }
887  if (conf.history_limit) {
888  if (history_truncate_file(filename, conf.history_limit_len)) {
889  perror("Truncating History");
890  }
891  }
892  free(filename);
893 #endif /* ifdef HAVE_READLINE_HISTORY */
894  EXIT_EARLY(EXIT_SUCCESS);
895  }
896 
897  tty = isatty(0); /* Find out where stdin is coming from... */
898  if (tty > 0) {
899  /* if stdin is a keyboard or terminal, then use readline and prompts */
900 #ifdef HAVE_READLINE_HISTORY
901  const char *const home = getenv("HOME");
902  char *filename;
903 
904  using_history();
905  filename = malloc(strlen(home) + strlen(historyfile) + 2);
906  snprintf(filename,
907  strlen(home) + strlen(historyfile) + 1,
908  "%s%s",
909  home, historyfile);
910  if (read_history(filename)) {
911  if (errno != ENOENT) {
912  perror("Reading History");
913  }
914  }
915 #endif /* ifdef HAVE_READLINE_HISTORY */
916 #ifdef HAVE_LIBREADLINE
917  build_qcommands();
918  rl_attempted_completion_function = wcalc_completion;
919  rl_basic_word_break_characters = " \t\n\"\'+-*/[{()}]=<>!|~&^%";
920 #endif
921  exit_on_err = 0;
922  printf("Enter an expression to evaluate, q to quit, or ? for help:\n");
923  while (1) {
924  lines = 1;
925  fflush(NULL);
926 #ifdef HAVE_LIBREADLINE
927  {
928  char prompt[30] = "";
929  snprintf(prompt, 30, "\1%s\2->\1%s\2 ", colors[uiselect[PROMPT]], colors[uiselect[UNCOLOR]]);
930  readme = readline(prompt);
931  }
932  if (!readme) {
933  /* from the readline manpage:
934  * readline returns the text of the line read. A blank
935  * line returns the empty string. If EOF is encountered
936  * while reading a line, and the line is empty, NULL is
937  * returned. If an eof is read with a non-empty line, it
938  * is treated as a newline.
939  * This means: readme == NULL is a perfectly reasonable
940  * response from readline(), AND it means something
941  * significant. DO NOT DO errno PARSING HERE!!!!
942  */
943  printf("\n");
944  break;
945  }
946 #else /* ifdef HAVE_LIBREADLINE */
947  {
948  char c;
949  unsigned int i = 0;
950 
951  memset(readme, 0, BIG_STRING);
952  printf("%s->%s ", colors[uiselect[PROMPT]], colors[uiselect[UNCOLOR]]);
953  fflush(stdout);
954  c = fgetc(stdin);
955  while (c != '\n' && i < BIG_STRING && !feof(stdin) &&
956  !ferror(stdin)) {
957  readme[i] = c;
958  c = fgetc(stdin);
959  ++i;
960  }
961  if (feof(stdin) || ferror(stdin)) {
962  printf("\n");
963  break;
964  }
965  }
966 #endif /* ifdef HAVE_LIBREADLINE */
967  /* if there is a line */
968  if (strlen(readme)) {
969  if (!strcmp(readme, "q") || !strcmp(readme, "quit") ||
970  !strcmp(readme, "\\q")) {
971  break;
972  } else if (!strncmp(readme, "\\yydebug", 8)) {
973  // addToHistory(readme, 0);
974  yydebug = !yydebug;
975  printf("Debug Mode %s\n", yydebug ? "On" : "Off");
976  } else if (!strncmp(readme, "\\color", 6)) {
978  if (conf.color_ui) {
979  uiselect = color_ui;
980  } else {
982  }
983  } else {
984  if (conf.verbose) {
985  printf("-> %s\n", readme);
986  }
987  parseme(readme);
988  {
989  extern char *errstring;
990 
991  if (!errstring || (errstring && !strlen(errstring)) ||
993  addToHistory(readme, last_answer);
994  }
995  }
996  }
997  if (putval("a", last_answer, "previous answer") != 0) {
998  fprintf(stderr, "error storing last answer\n");
999  }
1000 
1001  {
1002  extern char *errstring;
1003 
1004  if (errstring && strlen(errstring)) {
1006  /*rl_stuff_char('f');*/
1007  }
1008  }
1009  }
1010 #ifdef HAVE_LIBREADLINE
1011  free(readme);
1012 #endif
1013  }
1014 #ifdef HAVE_READLINE_HISTORY
1015  if (write_history(filename)) {
1016  fprintf(stderr, "Cannot save history to %s: %s\n", filename, strerror(errno));
1017  fflush(stderr);
1018  }
1019  if (conf.history_limit) {
1020  if (history_truncate_file(filename, conf.history_limit_len)) {
1021  perror("Truncating History");
1022  }
1023  }
1024  free(filename);
1025 #endif /* ifdef HAVE_READLINE_HISTORY */
1026  } else if (tty < 0) {
1027  fprintf(stderr, "Could not determine terminal type.\n");
1028  } else {
1029  /* if stdin is ANYTHING ELSE (a pipe, a file, etc), don't prompt */
1030  unsigned int linelen = 0, maxlinelen = BIG_STRING;
1031  extern int show_line_numbers;
1032 
1033  exit_on_err = 1;
1034  show_line_numbers = 1;
1035  while (1) {
1036  char *line = calloc(maxlinelen, sizeof(char));
1037  char gotten = fgetc(stdin);
1038  while (gotten != '\n' && !feof(stdin) && !ferror(stdin)) {
1039  line[linelen] = gotten;
1040  gotten = fgetc(stdin);
1041  linelen++;
1042  if (linelen > maxlinelen) {
1043  char *temp;
1044  temp = realloc(line, (maxlinelen + BIG_STRING) * sizeof(char));
1045  if (!temp) {
1046  free(line);
1047  fprintf(stderr,
1048  "Ran out of memory. Line too long.\n");
1049  EXIT_EARLY(EXIT_FAILURE);
1050  }
1051  memset(temp + maxlinelen, 0, BIG_STRING);
1052  maxlinelen += BIG_STRING;
1053  line = temp;
1054  }
1055  }
1056  if (ferror(stdin) || (feof(stdin) && (linelen == 0))) {
1057  free(line);
1058  break;
1059  }
1060  if (conf.verbose) {
1061  printf("-> %s\n", line);
1062  }
1063  parseme(line);
1064  if (putval("a", last_answer, "previous answer") != 0) {
1065  fprintf(stderr, "error storing last answer\n");
1066  }
1067  {
1068  extern int errloc;
1069 
1070  if (errloc != -1) {
1072  /*fprintf(stderr, "%s", errstring);
1073  * if (errstring[strlen(errstring) - 1] != '\n')
1074  * fprintf(stderr, "\n");
1075  * free(errstring);
1076  * errstring = NULL;*/
1077  }
1078  }
1079  free(line);
1080  linelen = 0;
1081  }
1082  }
1083 
1084  if (pretty_answer) {
1085  extern char *pa;
1086 
1088  if (pa) {
1089  free(pa);
1090  }
1091  }
1092  EXIT_EARLY(EXIT_SUCCESS);
1093 } /*}}} */
1094 
1095 static int
1097 { /*{{{ */
1098  int fd = openDotFile("wcalc_preload", O_RDONLY);
1099 
1100  switch (fd) {
1101  case -1:
1102  fprintf(stderr, "HOME environment variable unset. Cannot read preload file.\n");
1103  return 0;
1104 
1105  case -2:
1106  fprintf(stderr, "HOME environment variable is too long. Cannot read preload file.\n");
1107  return 0;
1108  }
1109  if (fd < 0) {
1110  if (errno == ENOENT) {
1111  /* The prefs file does not exist. This is okay, just means we can't read it. */
1112  return 1;
1113  }
1114  perror("Could not open preload file (~/.wcalc_preload)");
1115  return 0;
1116  }
1117  if (loadStateFD(fd, 0) < 0) {
1118  perror("Error reading preload file (~/.wcalc_preload)");
1119  }
1120  if (close(fd) < 0) {
1121  perror("Could not close preload file.");
1122  }
1123  return 1;
1124 } /*}}} */
1125 
1126 #define CHECK_COLOR(x) else if (!strcasecmp(str, # x)) { return x; }
1127 
1128 static enum ui_colors
1129 str2color(const char *str)
1130 {
1131  if (!strcasecmp(str, "NONE")) {
1132  return NONE;
1133  }
1135  CHECK_COLOR(RED)
1138  CHECK_COLOR(BLUE)
1140  CHECK_COLOR(CYAN)
1150  else {
1151  fprintf(stderr, "Unrecognized color: '%s'\n", str);
1152  return RESET;
1153  }
1154 }
1155 
1156 #define HANDLECOLOR(x) else if (!strcasecmp(key, # x "]")) { uiselect[x] = str2color(value); }
1157 static int
1158 assign_color_prefs(const char *key,
1159  const char *value)
1160 {
1161  assert(key);
1162  assert(value);
1163  if (uiselect != color_ui) {
1164  fprintf(stderr, "Colors not enabled!\n");
1165  return 0;
1166  }
1167  if (!strcasecmp(key, "conversion_category]")) {
1168  uiselect[CONV_CAT] = str2color(value);
1169  } else if (!strcasecmp(key, "conversion_unit]")) {
1170  uiselect[CONV_UNIT] = str2color(value);
1171  }
1185  else {
1186  char *res = strdup(key);
1187  *strchr(res, ']') = 0;
1188  fprintf(stderr, "Unrecognized colorable resource: '%s'\n", res);
1189  free(res);
1190  return 0;
1191  }
1192  return 1;
1193 }
1194 
1195 static int
1196 set_pref(const char *key,
1197  const char *value)
1198 {
1199  if (!strcmp(key, "precision")) {
1200  conf.precision = atoi(value);
1201  } else if (!strcmp(key, "show_equals")) {
1203  } else if (!strcmp(key, "flag_undeclared")) {
1205  } else if (!strcmp(key, "use_radians")) {
1207  } else if (!strcmp(key, "print_prefixes")) {
1209  } else if (!strcmp(key, "save_errors")) {
1211  } else if (!strcmp(key, "remember_errors")) {
1213  } else if (!strcmp(key, "precision_guard")) {
1215  } else if (!strcmp(key, "print_integers")) {
1217  } else if (!strcmp(key, "print_delimiters")) {
1219  } else if (!strcmp(key, "input_thousands_delimiter")) {
1220  conf.in_thou_delimiter = value[0];
1221  } else if (!strcmp(key, "input_decimal_delimiter")) {
1222  conf.in_dec_delimiter = value[0];
1223  } else if (!strcmp(key, "thousands_delimiter")) {
1224  conf.thou_delimiter = value[0];
1225  } else if (!strcmp(key, "decimal_delimiter")) {
1226  conf.dec_delimiter = value[0];
1227  } else if (!strcmp(key, "history_limit")) {
1228  if (!strcmp(value, "no")) {
1230  } else {
1231  conf.history_limit = 1;
1232  conf.history_limit_len = strtoul(value, NULL, 0);
1233  }
1234  } else if (!strcmp(key, "output_format")) {
1235  if (!strcmp(value, "decimal")) {
1237  } else if (!strcmp(value, "octal")) {
1239  } else if (!strcmp(value, "binary")) {
1241  } else if (!strcmp(value, "hex") || !strcmp(value, "hexadecimal")) {
1243  } else {
1244  fprintf(stderr, "Unrecognized output_format in wcalcrc.\n\tSupported formats are decimal, octal, binary, hex.\n");
1245  return 0;
1246  }
1247  } else if (!strcmp(key, "rounding_indication")) {
1248  if (!strcmp(value, "no") || !strcmp(value, "none")) {
1250  } else if (!strcmp(value, "simple")) {
1252  } else if (!strcmp(value, "sig_fig")) {
1254  } else {
1255  fprintf(stderr, "Unrecognized rounding_indication in wcalcrc.\n\tSupported indication types are none, simple, sig_fig.\n");
1256  return 0;
1257  }
1258  } else if (!strcmp(key, "engineering")) {
1259  if (!strcmp(value, "auto") || !strcmp(value, "automatic") || !strcmp(value, "yes") || !strcmp(value, "true") || !strcmp(value, "1")) {
1261  } else if (!strcmp(value, "always")) {
1263  } else {
1265  }
1266  } else if (!strcmp(key, "c_style_mod")) {
1268  } else if (!strcmp(key, "color_ui") || !strcmp(key, "color")) {
1270  if (conf.color_ui) {
1271  uiselect = color_ui;
1272  } else {
1274  }
1275  } else if (!strncmp(key, "colors[", 7)) {
1276  return assign_color_prefs(key + 7, value);
1277  } else {
1278  fprintf(stderr, "Unrecognized key in wcalcrc: %s\n", key);
1279  return 0;
1280  }
1281  return 1;
1282 }
1283 
1284 static void
1285 config_error(const char *format,
1286  ...)
1287 { /*{{{*/
1288  va_list args;
1289 
1290  fprintf(stderr, "Wcalc: Config: ");
1291  va_start(args, format);
1292  vfprintf(stderr, format, args);
1293  va_end(args);
1294 } /*}}}*/
1295 
1296 static size_t
1297 copy_string(char *d,
1298  const char *s,
1299  size_t dmax,
1300  size_t smax)
1301 { /*{{{*/
1302  size_t dcurs = 0, scurs = 0;
1303  const char quoted = (s[0] == '\'');
1304 
1305  if (quoted) {
1306  scurs = 1;
1307  while (dcurs < dmax && scurs < smax && s[scurs] != '\'') {
1308  d[dcurs++] = s[scurs++];
1309  }
1310  scurs++; // skip the terminating quote
1311  } else {
1312  do {
1313  d[dcurs++] = s[scurs++];
1314  } while (dcurs < dmax && scurs < smax &&
1315  (isalnum(s[scurs]) || s[scurs] == '[' || s[scurs] == ']' || s[scurs] == '.' || s[scurs] == '_' || s[scurs] == ' '));
1316  }
1317  if (scurs > smax) { return 0; }
1318  if (dcurs > dmax) { return 0; }
1319  d[dcurs] = 0;
1320  while (dcurs > 0 && isspace(d[dcurs - 1])) {
1321  dcurs--;
1322  d[dcurs] = 0;
1323  }
1324  return scurs;
1325 } /*}}}*/
1326 
1327 static int
1329 { /*{{{ */
1330  int fd = openDotFile("wcalcrc", O_RDONLY);
1331  char key[BIG_STRING], value[BIG_STRING];
1332  size_t curs = 0;
1333  size_t curs_max;
1334  char *f_mutable;
1335  const char *f;
1336 
1337  switch (fd) {
1338  case -1:
1339  fprintf(stderr, "HOME environment variable unset. Cannot read preferences.\n");
1340  return 0;
1341 
1342  case -2:
1343  fprintf(stderr, "HOME environment variable is too long. Cannot read preferences.\n");
1344  return 0;
1345  }
1346  if (fd < 0) {
1347  if (errno == ENOENT) {
1348  /* The prefs file does not exist. This is okay, just means we can't read it. */
1349  return 1;
1350  }
1351  perror("Could not open preferences file (~/.wcalcrc)");
1352  return 0;
1353  }
1354  {
1355  struct stat info;
1356  if (fstat(fd, &info)) {
1357  perror("Could not determine the size of the preference file");
1358  close(fd);
1359  return 0;
1360  }
1361  curs_max = info.st_size - 1;
1362  f_mutable = mmap(NULL, info.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
1363  if (f_mutable == MAP_FAILED) {
1364  perror("Could not read the preference file");
1365  close(fd);
1366  return 0;
1367  }
1368  f = f_mutable;
1369  }
1370  assert(curs_max > curs);
1371  do {
1372  memset(value, 0, BIG_STRING);
1373  memset(key, 0, BIG_STRING);
1374  // read until we find a non-comment
1375  while (curs < curs_max && (isspace(f[curs]) || f[curs] == '#')) {
1376  if (f[curs] == '#') { // skip to the next line
1377  do {
1378  curs++;
1379  } while (curs < curs_max && f[curs] != '\n');
1380  }
1381  curs++;
1382  }
1383  if (curs >= curs_max) { break; } // not an error!
1384  // read in the key
1385  {
1386  size_t skip = copy_string(key, f + curs, BIG_STRING, curs_max - curs);
1387  if (!skip) {
1388  config_error("Incomplete key (%s)! (probably an unterminated quote)\n", key);
1389  goto err_exit;
1390  }
1391  curs += skip;
1392  }
1393  // eat the equals sign (and any surrounding space)
1394  while (curs < curs_max && isspace(f[curs])) curs++;
1395  if ((curs == curs_max) || (f[curs] != '=')) {
1396  config_error("Expected equals (=) after key (%s)!\n", key);
1397  goto err_exit;
1398  }
1399  do {
1400  curs++;
1401  } while (curs < curs_max && isspace(f[curs]));
1402  if (curs == curs_max) {
1403  config_error("Key (%s) has no value!\n", key);
1404  goto err_exit;
1405  }
1406  // read in the value
1407  {
1408  size_t skip = copy_string(value, f + curs, BIG_STRING, curs_max - curs);
1409  if (!skip) {
1410  config_error("Incomplete value (%s) for key (%s)! (probably an unterminated quote)\n", value, key);
1411  goto err_exit;
1412  }
1413  curs += skip;
1414  }
1415 
1416  if (!set_pref(key, value)) { goto err_exit; }
1417 
1418  // eat the rest of the line
1419  while (curs < curs_max && f[curs] != '\n') curs++;
1420  if (curs < curs_max) { curs++; }
1421  } while (curs < curs_max);
1422  if (munmap(f_mutable, curs_max + 1)) {
1423  perror("Unmapping the config file");
1424  }
1425  if (close(fd)) {
1426  perror("Closing the config file");
1427  }
1428  return 1;
1429 
1430 err_exit:
1431  config_error("Corrupt config file. Cannot continue.\n");
1432  exit(EXIT_FAILURE);
1433 } /*}}} */
1434 
1435 static void
1436 prefline(const char *name,
1437  const char *val,
1438  const char *cmd)
1439 {
1440  if (name && val && cmd) {
1441  printf("%s%27s:%s %s%-24s%s -> ",
1444  if (strchr(cmd, ',')) {
1445  unsigned offset = 0;
1446  while (strchr(cmd + offset, ',')) {
1447  unsigned cmdlen = strchr(cmd + offset, ',') - (cmd + offset);
1448  printf("%s%.*s%s, ",
1449  colors[uiselect[PREF_CMD]], cmdlen, cmd + offset, colors[uiselect[UNCOLOR]]);
1450  offset += cmdlen + 1;
1451  }
1452  printf("%s%s%s\n",
1453  colors[uiselect[PREF_CMD]], cmd + offset, colors[uiselect[UNCOLOR]]);
1454  } else {
1455  printf("%s%s%s\n",
1457  }
1458  } else if (name && val) {
1459  printf("%s%27s:%s %s%-24s%s\n",
1462  }
1463 }
1464 
1465 #define DP_YESNO(x) ((x) ? "yes" : "no")
1466 void
1468 {
1469  if (standard_output) {
1470  char tmp[50];
1471  sprintf(tmp, "%-3i %s", conf.precision, ((conf.precision == -1) ? "(auto)" : " "));
1472  prefline("Display Precision", tmp, "\\p");
1473  sprintf(tmp, "%-24lu", (unsigned long)num_get_default_prec());
1474  prefline("Internal Precision", tmp, "\\bits");
1475  prefline("Engineering Output",
1476  (conf.engineering == always) ? "always" : (conf.engineering == never) ? "never " : "auto ",
1477  "\\e");
1478  prefline("Output Format", output_string(conf.output_format), "\\b,\\d,\\h,\\o");
1479  prefline("Use Radians", DP_YESNO(conf.use_radians), "\\r");
1480  prefline("Print Prefixes", DP_YESNO(conf.print_prefixes), "\\pre,\\prefixes");
1481  prefline("Avoid Abbreviating Integers", DP_YESNO(conf.print_ints), "\\ints");
1482  prefline("Rounding Indication",
1483  conf.rounding_indication == SIMPLE_ROUNDING_INDICATION ? "yes (simple) " : (conf.rounding_indication == SIG_FIG_ROUNDING_INDICATION ? "yes (sig_fig)" : "no "),
1484  "\\round");
1485  prefline("Save Errors in History", DP_YESNO(conf.remember_errors), "\\re");
1486  sprintf(tmp, "'%c'", conf.thou_delimiter);
1487  prefline("Thousands Delimiter", tmp, "\\tsep");
1488  sprintf(tmp, "'%c'", conf.dec_delimiter);
1489  prefline("Decimal Delimiter", tmp, "\\dsep");
1490  prefline("Precision Guard", DP_YESNO(conf.precision_guard), "\\cons");
1491  prefline("History Limit", DP_YESNO(conf.history_limit), "\\hlimit");
1492  if (conf.history_limit) {
1493  sprintf(tmp, "%lu", conf.history_limit_len);
1494  prefline("History Limited To", tmp, NULL);
1495  }
1496  prefline("Verbose", DP_YESNO(conf.verbose), "\\verbose");
1497  prefline("Display Delimiters", DP_YESNO(conf.print_commas), "\\delim");
1498  prefline("Modulo Operator", (conf.c_style_mod ? "C-style " : "not C-style"), "\\cmod");
1499  }
1500 }
1501 
1502 void
1503 display_status(const char *format,
1504  ...)
1505 {
1506  if (standard_output) {
1507  va_list args;
1508 
1509  printf("%s", colors[uiselect[STATUS]]);
1510  va_start(args, format);
1511  vprintf(format, args);
1512  va_end(args);
1513  printf("%s\n", colors[uiselect[UNCOLOR]]);
1514  }
1515 }
1516 
1517 void
1519 {
1520  if (standard_output) {
1521  switch (format) {
1522  case HEXADECIMAL_FORMAT:
1523  display_status("Hexadecimal Formatted Output");
1524  break;
1525  case OCTAL_FORMAT:
1526  display_status("Octal Formatted Output");
1527  break;
1528  case DECIMAL_FORMAT:
1529  display_status("Decimal Formatted Output");
1530  break;
1531  case BINARY_FORMAT:
1532  display_status("Binary Formatted Output");
1533  break;
1534  }
1535  }
1536 }
1537 
1538 void
1539 display_val(const char *name)
1540 {
1541  if (standard_output) {
1542  answer_t val;
1543  char approx = 0;
1544  char *err;
1545  printf("display_val\n");
1547  printf("%s%s%s", colors[uiselect[VAR_NAME]], name, colors[uiselect[UNCOLOR]]);
1548  val = getvar_full(name);
1549  if (val.exp) {
1550  printf(" %s=%s %s\n", colors[uiselect[EXACT_ANSWER]], colors[uiselect[UNCOLOR]], val.exp);
1551  } else {
1552  char *p = print_this_result(val.val, 0, &approx, &err);
1553  printf("display_val\n");
1554  show_answer(err, approx, p);
1555  }
1556  if (val.desc) {
1557  printf(":: %s%s%s\n", colors[uiselect[VAR_DESC]], val.desc, colors[uiselect[UNCOLOR]]);
1558  }
1559  }
1560 }
1561 
1562 void
1564  unsigned count,
1565  unsigned digits)
1566 {
1567  printf("%*u. %s%s%s", digits, count,
1569  if (v->exp) {
1570  printf(" %s=%s %s\n",
1572  v->expression);
1573  } else {
1574  char approx = 0;
1575  char *err;
1576  char *p = print_this_result(v->value, 0, &approx, &err);
1577  printf("display_var\n");
1578  show_answer(err, approx, p);
1579  }
1580  if (v->description) {
1581  printf("%*s %s%s%s\n", digits + 4, "::",
1583  }
1584 }
1585 
1586 void
1588  const char *exp,
1589  List subvars,
1590  const char *desc)
1591 {
1592  printf("%s%s%s is the expression: '%s'\n", colors[uiselect[VAR_NAME]], str,
1593  colors[uiselect[UNCOLOR]], exp);
1594  if (desc) {
1595  printf("Description: %s%s%s\n", colors[uiselect[VAR_DESC]], desc, colors[uiselect[UNCOLOR]]);
1596  }
1597  if (listLen(subvars) > 0) {
1598  unsigned maxnamelen = 0;
1599  {
1600  /* First, find the longest variable name... */
1601  ListIterator si = getListIterator(subvars);
1602  char *cursor = (char *)nextListElement(si);
1603  if (cursor != NULL) {
1604  maxnamelen = strlen(cursor);
1605  while ((cursor = (char *)nextListElement(si)) != NULL) {
1606  unsigned len = strlen(cursor);
1607  if (maxnamelen < len) { maxnamelen = len; }
1608  }
1609  }
1610  freeListIterator(si);
1611  }
1612  printf("%s%s%s uses the following variables:\n",
1614  while (listLen(subvars) > 0) {
1615  char *curstr = (char *)getHeadOfList(subvars);
1616  char *value = evalvar_noparse(curstr);
1617 
1618  printf("\t%s%*s%s\t(currently: %s)\n", colors[uiselect[SUBVAR_NAME]], -maxnamelen, curstr, colors[uiselect[UNCOLOR]],
1619  value ? value : "undefined");
1620  if (curstr) {
1621  free(curstr);
1622  }
1623  if (value) {
1624  free(value);
1625  }
1626  }
1627  }
1628 }
1629 
1630 void
1632  Number *val,
1633  const char *desc)
1634 {
1635  printf("%s%s%s is a variable with the value: %s\n",
1637  print_this_result(*val, 0, NULL, NULL));
1638  if (desc) {
1639  printf("Description: %s%s%s\n", colors[uiselect[VAR_DESC]], desc, colors[uiselect[UNCOLOR]]);
1640  }
1641 }
1642 
1643 void
1644 display_explanation(const char *exp,
1645  ...)
1646 {
1647  if (standard_output) {
1648  va_list args;
1649 
1650  printf("%s", colors[uiselect[EXPLANATION]]);
1651  va_start(args, exp);
1652  vprintf(exp, args);
1653  va_end(args);
1654  printf("%s\n", colors[uiselect[UNCOLOR]]);
1655  }
1656 }
1657 
1658 void
1659 display_stateline(const char *buf)
1660 {
1661  printf("-> %s\n", buf);
1662 }
1663 
1664 void
1666 {
1667  size_t linelen = 0;
1668 
1669  for (size_t i = 0; consts[i].explanation; i++) {
1670  const char *const *const names = consts[i].names;
1671  for (size_t j = 0; names[j]; j++) {
1672  if (linelen + strlen(names[j]) + 2 > 70) {
1673  printf(",\n");
1674  linelen = 0;
1675  }
1676  if (linelen == 0) {
1677  printf("%s", names[j]);
1678  linelen = strlen(names[j]);
1679  } else {
1680  printf(", %s", names[j]);
1681  linelen += strlen(names[j]) + 2;
1682  }
1683  }
1684  }
1685  printf("\n");
1686 }
1687 
1688 /* vim:set expandtab: */
_conf::history_limit_len
unsigned long history_limit_len
Definition: calculator.h:123
name_with_exp::explanation
const char * explanation
Definition: explain.h:6
CONV_CAT
Definition: main.c:397
_conf::picky_variables
unsigned int picky_variables
Definition: calculator.h:130
_conf::precision_guard
unsigned int precision_guard
Definition: calculator.h:136
CONV_UNIT
Definition: main.c:398
GREEN
Definition: main.c:359
_conf::engineering
enum engineering_modes engineering
Definition: calculator.h:129
BOLDMAGENTA
Definition: main.c:370
isatty
int isatty(int)
_conf::c_style_mod
unsigned int c_style_mod
Definition: calculator.h:144
show_answer
void show_answer(char *err, int uncertain, char *answer)
Definition: main.c:470
config_error
static void config_error(const char *format,...)
Definition: main.c:1285
answer::desc
char * desc
Definition: variables.h:32
OCTAL_FORMAT
#define OCTAL_FORMAT
Definition: calculator.h:164
NUM_PREC_MAX
#define NUM_PREC_MAX
Definition: number.h:25
BOLDWHITE
Definition: main.c:372
_list_iterator
Definition: list.c:27
EXACT_ANSWER
Definition: main.c:400
name_with_exp::names
const char * names[10]
Definition: explain.h:5
_conf::print_equal
unsigned int print_equal
Definition: calculator.h:138
display_var
void display_var(variable_t *v, unsigned count, unsigned digits)
Definition: main.c:1563
strchr
#define strchr
Definition: variables.h:11
display_output_format
void display_output_format(int format)
Definition: main.c:1518
conf
struct _conf conf
Definition: calculator.c:111
initvar
void initvar(void)
Definition: variables.c:29
display_expvar_explanation
void display_expvar_explanation(const char *str, const char *exp, List subvars, const char *desc)
Definition: main.c:1587
conversions
const struct conversion * conversions[]
Definition: conversion.c:923
black_and_white_ui
int black_and_white_ui[]
Definition: main.c:413
BINARY_FORMAT
#define BINARY_FORMAT
Definition: calculator.h:166
listvarnames
char ** listvarnames(void)
Definition: variables.c:91
BOLDBLACK
Definition: main.c:365
_conf::color_ui
unsigned int color_ui
Definition: calculator.h:145
PREF_NAME
Definition: main.c:403
calculator.h
HANDLECOLOR
#define HANDLECOLOR(x)
Definition: main.c:1156
isfunc.h
_conf::use_radians
unsigned int use_radians
Definition: calculator.h:131
pa
char * pa
Definition: calculator.c:108
display_stateline
void display_stateline(const char *buf)
Definition: main.c:1659
EXIT_EARLY
#define EXIT_EARLY(code)
Definition: main.c:73
output_string
char * output_string(const unsigned int o)
Definition: calculator.c:1380
_conf::history_limit
unsigned int history_limit
Definition: calculator.h:137
_conf::print_prefixes
unsigned int print_prefixes
Definition: calculator.h:133
_conf::print_ints
unsigned int print_ints
Definition: calculator.h:139
free
void free(void *)
assign_color_prefs
static int assign_color_prefs(const char *key, const char *value)
Definition: main.c:1158
ui_colors
ui_colors
Definition: main.c:354
display_prefs
void display_prefs(void)
Definition: main.c:1467
isconst.h
BOLDBLUE
Definition: main.c:369
set_pref
static int set_pref(const char *key, const char *value)
Definition: main.c:1196
parseme
void parseme(const char *pthis)
Definition: calculator.c:130
variables.h
standard_output
char standard_output
Definition: calculator.c:106
getHeadOfList
void * getHeadOfList(List)
Definition: list.c:264
num_init_set_ui
void num_init_set_ui(Number n, const unsigned int ui)
Definition: number.c:89
listLen
size_t listLen(List)
Definition: list.c:352
RED
Definition: main.c:358
PREF_VAL
Definition: main.c:404
variable::exp
unsigned int exp
Definition: variables.h:25
display_command_help
void display_command_help(void)
Definition: help.c:68
errstring
char * errstring
Definition: parser.c:111
APPROX_ANSWER
Definition: main.c:399
_conf::in_dec_delimiter
char in_dec_delimiter
Definition: calculator.h:128
PROMPT
Definition: main.c:396
_list
Definition: list.c:19
conversion::name
char * name
Definition: conversion.h:30
VAR_NAME
Definition: main.c:407
display_val
void display_val(const char *name)
Definition: main.c:1539
addToList
void addToList(List *, void *)
Definition: list.c:204
MAX_TYPE
#define MAX_TYPE
Definition: conversion.h:40
yydebug
int yydebug
Definition: parser.c:1281
uiselect
int * uiselect
Definition: main.c:449
variable::description
char * description
Definition: variables.h:23
answer
Definition: variables.h:29
yy_scan_string
int yy_scan_string(const char *)
WHITE
Definition: main.c:364
_conf::print_commas
unsigned int print_commas
Definition: calculator.h:142
TRUEFALSE
#define TRUEFALSE
Definition: main.c:70
EXPLANATION
Definition: main.c:410
_conf::rounding_indication
unsigned int rounding_indication
Definition: calculator.h:134
exit_on_err
static int exit_on_err
Definition: main.c:95
display_consts
void display_consts(void)
Definition: main.c:1665
commands
commands
Definition: calculator.h:100
last_input
char * last_input
Definition: calculator.c:109
prefline
static void prefline(const char *name, const char *val, const char *cmd)
Definition: main.c:1436
init_numbers
void init_numbers(void)
Definition: number.c:26
ui_pieces
ui_pieces
Definition: main.c:395
display_explanation
void display_explanation(const char *exp,...)
Definition: main.c:1644
Number
#define Number
Definition: number.h:23
evalvar.h
_conf::dec_delimiter
char dec_delimiter
Definition: calculator.h:126
VAR_DESC
Definition: main.c:408
_conf::in_thou_delimiter
char in_thou_delimiter
Definition: calculator.h:127
yyparse
int yyparse(void)
Definition: parser.c:1563
CHECK_COLOR
#define CHECK_COLOR(x)
Definition: main.c:1126
getvar_full
struct answer getvar_full(const char *key)
Definition: variables.c:160
BLUE
Definition: main.c:361
read_prefs
static int read_prefs(void)
Definition: main.c:1328
BOLDYELLOW
Definition: main.c:368
openDotFile
int openDotFile(const char *dotFileName, int flags)
Definition: files.c:63
display_and_clear_errstring
static void display_and_clear_errstring(void)
Definition: main.c:485
malloc
void * malloc(size_t)
answer::exp
char * exp
Definition: variables.h:31
consts
const struct name_with_exp consts[]
Definition: isconst.c:13
display_valvar_explanation
void display_valvar_explanation(const char *str, Number *val, const char *desc)
Definition: main.c:1631
BLACK
Definition: main.c:357
last_answer
Number last_answer
Definition: calculator.c:98
identify_unit
ssize_t identify_unit(const char *unit)
Definition: conversion.c:940
evalvar_noparse
char * evalvar_noparse(const char *varname)
Definition: evalvar.c:38
display_status
void display_status(const char *format,...)
Definition: main.c:1503
conversion.h
ERR_LOCATION
Definition: main.c:401
scanerror
short scanerror
Definition: parser.c:110
iscmd.h
PREF_CMD
Definition: main.c:405
DP_YESNO
#define DP_YESNO(x)
Definition: main.c:1465
color_ui
int color_ui[]
Definition: main.c:431
lines
int lines
Definition: parser.c:108
colors
const char *const colors[]
Definition: main.c:374
main
int main(int argc, char *argv[])
Definition: main.c:521
getListIterator
ListIterator getListIterator(List)
Definition: list.c:362
DECIMAL_FORMAT
#define DECIMAL_FORMAT
Definition: calculator.h:163
CYAN
Definition: main.c:363
RESET
Definition: main.c:356
never
Definition: calculator.h:102
copy_string
static size_t copy_string(char *d, const char *s, size_t dmax, size_t smax)
Definition: main.c:1297
MAGENTA
Definition: main.c:362
nextListElement
void * nextListElement(ListIterator)
Definition: list.c:385
BOLDRED
Definition: main.c:366
pretty_answer
char * pretty_answer
Definition: calculator.c:99
UNCOLOR
Definition: main.c:411
HEXADECIMAL_FORMAT
#define HEXADECIMAL_FORMAT
Definition: calculator.h:165
print_this_result
char * print_this_result(const Number result, int output, char *nad, char **es)
Definition: calculator.c:926
parser.h
NUM_PREC_MIN
#define NUM_PREC_MIN
Definition: number.h:24
conversion::aka
char * aka[9]
Definition: conversion.h:31
variable::expression
char * expression
Definition: variables.h:22
BOLDGREEN
Definition: main.c:367
_conf::verbose
unsigned int verbose
Definition: calculator.h:141
show_line_numbers
int show_line_numbers
Definition: parser.c:113
automatic
Definition: calculator.h:102
SUBVAR_NAME
Definition: main.c:409
num_get_default_prec
num_prec_t num_get_default_prec()
Definition: number.c:479
variable::value
mpfr_t value
Definition: variables.h:24
freeListIterator
void freeListIterator(ListIterator)
Definition: list.c:406
addToHistory
void addToHistory(char *expression, Number answer)
Definition: historyManager.c:71
_conf::precision
int precision
Definition: calculator.h:124
variable
Definition: variables.h:20
answer::val
mpfr_t val
Definition: variables.h:30
num_set_default_prec
#define num_set_default_prec(prec)
Definition: number.h:133
lists_init
void lists_init(void)
Definition: list.c:52
loadStateFD
int loadStateFD(int fd, const int into_history)
Definition: files.c:194
NONE
Definition: main.c:355
PrintConversionUnitCategory
static void PrintConversionUnitCategory(int nCategory)
Definition: main.c:452
SIG_FIG_ROUNDING_INDICATION
#define SIG_FIG_ROUNDING_INDICATION
Definition: calculator.h:170
_conf::thou_delimiter
char thou_delimiter
Definition: calculator.h:125
files.h
_conf::output_format
unsigned int output_format
Definition: calculator.h:132
SIMPLE_ROUNDING_INDICATION
#define SIMPLE_ROUNDING_INDICATION
Definition: calculator.h:169
STATUS
Definition: main.c:406
list.h
historyManager.h
BIG_STRING
#define BIG_STRING
Definition: main.c:71
YELLOW
Definition: main.c:360
NO_ROUNDING_INDICATION
#define NO_ROUNDING_INDICATION
Definition: calculator.h:168
read_preload
static int read_preload(void)
Definition: main.c:1096
number.h
errloc
int errloc
Definition: parser.c:112
ERR_TEXT
Definition: main.c:402
funcs
const struct name_with_exp funcs[]
Definition: isfunc.c:11
conversion_names
const char * conversion_names[]
Definition: conversion.c:20
putval
int putval(const char *key, const mpfr_t value, const char *desc)
always
Definition: calculator.h:102
BOLDCYAN
Definition: main.c:371
_conf::remember_errors
unsigned int remember_errors
Definition: calculator.h:135
output.h
VERSION
#define VERSION
Definition: main.c:72
str2color
static enum ui_colors str2color(const char *str)
Definition: main.c:1129
conversion
Definition: conversion.h:28
variable::key
char * key
Definition: variables.h:21