Numerus  v2.0.0
Roman numerals conversion and manipulation C library.
numerus_cli.c
Go to the documentation of this file.
1 
19 #include <stdio.h> /* For `printf()` */
20 #include <stdlib.h> /* For `malloc()`, `free()`, `strtod()` */
21 #include <ctype.h> /* For `isspace()`, `tolower()` */
22 #include <string.h> /* For `strcmp()` */
23 #include "numerus.h"
24 
29 #define NUMERUS_PROMPT_AGAIN 1
30 
31 
36 #define NUMERUS_STOP_REPL 0
37 
38 static const char *PROMPT_TEXT = "numerus> ";
39 static const char *WELCOME_TEXT = ""
40 "+-----------------+\n"
41 "| N V M E R V S |\n"
42 "+-----------------+\n";
43 static const char *INFO_TEXT = ""
44 "Numerus, C library for conversion and manipulation of roman numerals.\n"
45 "Version 2.0.0, Command Line Interface\n"
46 "Copyright (c) 2015-2016 Matjaž Guštin <dev@matjaz.it> http://matjaz.it\n"
47 "This software is subject to the terms of the BSD 3-Clause license.\n"
48 "Project page and source code: https://github.com/TheMatjaz/Numerus\n";
49 static const char *MOO_TEXT = "This is not an easter egg. Try `ascii`.\n";
50 static const char *PING_TEXT = "Pong.\n";
51 static const char *AVE_TEXT = "Ave tibi!\n";
52 static const char *HELP_TEXT = ""
53 "For ANY information about the library or the syntax of roman numeral, \n"
54 "check the documentation available on https://thematjaz.github.io/Numerus/\n\n"
55 ""
56 "To convert an (arabic) integer to a roman numeral or vice-versa,\n"
57 "just type it in the shell and press enter.\n"
58 "Other Numerus commands are:\n\n"
59 ""
60 "pretty switches on/off the pretty printing of long roman numerals\n"
61 " (with overlined notation instead of underscore notation)\n"
62 " and the pretty printing of values as integer and fractional part\n"
63 "?, help shows this help text\n"
64 "info, about shows version, credits, licence, repository of Numerus\n"
65 "exit, quit ends this shell\n\n"
66 ""
67 "We also have: moo, ping, ave.\n";
68 static const char *QUIT_TEXT = "Vale!\n";
69 static const char *ASCII_TEXT = ""
70 " ____ _____ ____ ____ ____ ____ _________ _______ ____ ____ _______ \n"
71 "|_ \\|_ _| |_ _| |_ _| |_ \\ / _| |_ ___ | |_ __ \\ |_ _| |_ _| / ___ |\n"
72 " | \\ | | \\ \\ / / | \\/ | | |_ \\_| | |__) | \\ \\ / / | (__ \\_|\n"
73 " | |\\ \\| | \\ \\ / / | |\\ /| | | _| _ | __ / \\ \\ / / '.___`-. \n"
74 " _| |_\\ |_ \\ ' / _| |_\\/_| |_ _| |___/ | _| | \\ \\_ \\ ' / |`\\____) |\n"
75 "|_____|\\____| \\_/ |_____||_____| |_________| |____| |___| \\_/ |_______.'\n";
76 static const char *UNKNOWN_COMMAND_TEXT = "Unknown command or wrong roman numeral syntax:\n";
77 static const char *PRETTY_ON_TEXT = "Pretty printing is enabled.\n";
78 static const char *PRETTY_OFF_TEXT = "Pretty printing is disabled.\n";
79 static int pretty_printing = 0;
80 
81 
95 static char *_num_get_first_word_trimmed_lowercased(char *string) {
96  while(isspace(*string)) {
97  string++;
98  }
99  if(*string == '\0') {
100  /* The string was full of whitespaces */
101  return string;
102  }
103  char *first_word_start = string;
104  while (*string != '\0' && !isspace(*string)) {
105  *string = (char) tolower(*string);
106  string++;
107  }
108  *string = '\0';
109  return first_word_start;
110 }
111 
112 
121 static short _num_string_is_double_zero(char *zero_as_string) {
122  if (*zero_as_string == '-') {
123  zero_as_string++;
124  }
125  while (*zero_as_string == '0') {
126  zero_as_string++;
127  }
128  if (*zero_as_string == '.' || *zero_as_string == ',') {
129  zero_as_string++;
130  if (*zero_as_string == '0') {
131  while (*zero_as_string == '0') {
132  zero_as_string++;
133  }
134  } else {
135  return 0;
136  }
137  }
138  if (*zero_as_string == '\0') {
139  return 1;
140  } else {
141  return 0;
142  }
143 }
144 
145 
157 void _num_convert_to_other_form_and_print(char *string) {
158  double value;
159  char *roman;
160  int errcode;
161  /* This check is necessary because strtod() returns 0 in case of errors
162  * AND in case finds an actual zero. Duh! */
163  if (_num_string_is_double_zero(string)) {
164  printf("%s\n", roman = numerus_double_to_roman(0, NULL));
165  free(roman);
166  return;
167  }
168  value = strtod(string, NULL);
169  if (value != 0) {
170  /* The string is a double */
171  roman = numerus_double_to_roman(value, &errcode);
172  if (errcode != NUMERUS_OK) {
173  printf("%s\n", numerus_explain_error(errcode));
174  if (errcode != NUMERUS_ERROR_MALLOC_FAIL) {
175  free(roman);
176  }
177  return;
178  }
179  /* Successful conversion */
180  if (pretty_printing == 1) {
181  /* Enabled pretty printing */
182  char *roman_pretty = numerus_overline_long_numerals(roman, &errcode);
183  if (errcode != NUMERUS_OK) {
184  printf("%s\n", numerus_explain_error(errcode));
185  } else {
186  /* Successful transformed into pretty format */
187  printf("%s\n", roman_pretty);
188  free(roman_pretty);
189  }
190  } else {
191  /* Disabled pretty printing, just print the roman numeral */
192  printf("%s\n", roman);
193  }
194  free(roman);
195  return;
196  }
197  /* The string is not a double, trying as a roman numeral */
198  value = numerus_roman_to_double(string, &errcode);
199  if (errcode == NUMERUS_OK) {
200  /* The string is a roman numeral */
201  if (pretty_printing == 1) {
202  /* Pretty printing enabled */
203  char *pretty_value = numerus_create_pretty_value_as_double(value);
204  if (pretty_value == NULL) {
206  } else {
207  /* Successful transformed into pretty format */
208  printf("%s\n", pretty_value);
209  free(pretty_value);
210  }
211  } else {
212  /* Pretty printing disabled, just print the value */
213  printf("%f\n", value);
214  }
215  } else {
216  /* The command is not a value/a roman numeral or it has wrong syntax */
217  printf("%s -> %s\n", UNKNOWN_COMMAND_TEXT, numerus_explain_error(errcode));
218  }
219 }
220 
221 
227 static int _num_parse_command(char *command) {
228  if (strcmp(command, "?") == 0 || strcmp(command, "help") == 0) {
229  printf("%s", HELP_TEXT);
230  return NUMERUS_PROMPT_AGAIN;
231  } else if (strcmp(command, "moo") == 0) {
232  printf("%s", MOO_TEXT);
233  return NUMERUS_PROMPT_AGAIN;
234  } else if (strcmp(command, "ascii") == 0) {
235  printf("%s", ASCII_TEXT);
236  return NUMERUS_PROMPT_AGAIN;
237  } else if (strcmp(command, "info") == 0 || strcmp(command, "about") == 0) {
238  printf("%s", INFO_TEXT);
239  return NUMERUS_PROMPT_AGAIN;
240  } else if (strcmp(command, "ave") == 0) {
241  printf("%s", AVE_TEXT);
242  return NUMERUS_PROMPT_AGAIN;
243  } else if (strcmp(command, "pretty") == 0) {
244  /* Toggles pretty printing */
245  if (pretty_printing == 1) {
246  pretty_printing = 0;
247  printf("%s", PRETTY_OFF_TEXT);
248  return NUMERUS_PROMPT_AGAIN;
249  } else {
250  pretty_printing = 1;
251  printf("%s", PRETTY_ON_TEXT);
252  return NUMERUS_PROMPT_AGAIN;
253  }
254  } else if (strcmp(command, "ping") == 0) {
255  printf("%s", PING_TEXT);
256  return NUMERUS_PROMPT_AGAIN;
257  } else if (strcmp(command, "exit") == 0 || strcmp(command, "quit") == 0) {
258  printf("%s", QUIT_TEXT);
259  return NUMERUS_STOP_REPL;
260  } else if (*command == '\0') {
261  /* No inserted command, just an <enter> */
262  return NUMERUS_PROMPT_AGAIN;
263  } else {
264  _num_convert_to_other_form_and_print(command);
265  return NUMERUS_PROMPT_AGAIN;
266  }
267 }
268 
269 
285 int numerus_cli(int argc, char **args) {
286  char *command;
287  /* line_buffer_size = 50 enough for every command,
288  * gets reallocated by getline() if not enough */
289  size_t line_buffer_size = 50;
290  char *line = malloc(line_buffer_size);
291  if (line == NULL) {
294  }
295  if (argc > 1) {
296  /* Parse main arguments and exit */
297  args++;
298  pretty_printing = 0;
299  while (argc > 1) {
300  command = _num_get_first_word_trimmed_lowercased(*args);
301  _num_parse_command(command);
302  args++;
303  argc--;
304  }
305  } else {
306  /* Enter command line interface */
307  pretty_printing = 1;
308  printf("%s", WELCOME_TEXT);
309  int command_result = NUMERUS_PROMPT_AGAIN;
310  while (command_result == NUMERUS_PROMPT_AGAIN) {
311  printf("%s", PROMPT_TEXT);
312  if (getline(&line, &line_buffer_size, stdin) == -1) {
313  break;
314  } else {
315  command = _num_get_first_word_trimmed_lowercased(line);
316  command_result = _num_parse_command(command);
317  }
318  }
319  }
320  free(line);
321  return 0;
322 }
Numerus roman numerals library header.
char * numerus_overline_long_numerals(char *roman, int *errcode)
Allocates a string with a prettier representation of a long roman numeral with actual overlining...
const char * numerus_explain_error(int error_code)
Returns a pointer to the human-friendly text description of any NUMERUS_ERROR_* error code...
#define NUMERUS_OK
Everything went all right.
int numerus_error_code
The global error code variable to store any errors during conversions.
Definition: numerus_core.c:105
#define NUMERUS_ERROR_MALLOC_FAIL
Heap memory allocation failure.
int numerus_cli(int argc, char **args)
Starts a command line interface that converts any typed value to a roman numeral or vice-versa...
Definition: numerus_cli.c:285
char * numerus_double_to_roman(double double_value, int *errcode)
Converts a double value to a roman numeral with its value.
Definition: numerus_core.c:726
char * numerus_create_pretty_value_as_double(double double_value)
Allocates a string with a prettier representation of a double value of a roman numeral as integer par...
double numerus_roman_to_double(char *roman, int *errcode)
Converts a roman numeral to its value expressed as a double.
Definition: numerus_core.c:474