|
| 1 | +#include <argparse.h> |
| 2 | +#include <stdio.h> |
| 3 | +#include <stdint.h> |
| 4 | +#include <string.h> |
| 5 | +#include <stdlib.h> |
| 6 | + |
| 7 | +/* This is a very simple implementation of a calculator |
| 8 | + * usage: |
| 9 | + * example -o[+, -, /, *, %] op1 op2 |
| 10 | + * or |
| 11 | + * example --operator [+, -, /, *, %] op1 op2 */ |
| 12 | + |
| 13 | +/* define a very simple list of operators */ |
| 14 | +enum operator { |
| 15 | + plus, |
| 16 | + minus, |
| 17 | + multiply, |
| 18 | + divide, |
| 19 | + modulo, |
| 20 | + undef |
| 21 | +}; |
| 22 | + |
| 23 | +/* convinient macro */ |
| 24 | +#define STREQ(a, b) (strcmp (a, b) == 0) |
| 25 | + |
| 26 | +/* Usage info */ |
| 27 | +#define USAGE \ |
| 28 | + "Usage:\n" \ |
| 29 | + "\texample -o[+, -, /, *, %] op1 op2\n" \ |
| 30 | + "or\n" \ |
| 31 | + "\texample --operator [+, -, /, *, %] op1 op2" |
| 32 | + |
| 33 | +static char op = undef; |
| 34 | + |
| 35 | +/* the custom handler for our enumerator |
| 36 | + * |
| 37 | + * handlers modify the value of the argument |
| 38 | + * to write it in retval the right way */ |
| 39 | +arg_return custom_enum_handler (void * data, size_t size, void * retval) { |
| 40 | + if (STREQ (data, "+")) { |
| 41 | + op = plus; |
| 42 | + } |
| 43 | + else if (STREQ (data, "-")) { |
| 44 | + op = minus; |
| 45 | + } |
| 46 | + else if (STREQ (data, "/")) { |
| 47 | + op = divide; |
| 48 | + } |
| 49 | + else if (STREQ (data, "*")) { |
| 50 | + op = multiply; |
| 51 | + } |
| 52 | + else if (STREQ (data, "%")) { |
| 53 | + op = modulo; |
| 54 | + } |
| 55 | + else { |
| 56 | + /* The value is not an operator, so we return ARG_NMATCH */ |
| 57 | + return ARG_NMATCH; |
| 58 | + } |
| 59 | + /* Everything is good, return ARG_SUCCESS */ |
| 60 | + return ARG_SUCCESS; |
| 61 | +} |
| 62 | + |
| 63 | +int main (int argc, char ** argv) { |
| 64 | + if (argc == 1) { |
| 65 | + puts ("You haven't specified any arguments"); |
| 66 | + return 0; |
| 67 | + } |
| 68 | + /* A description of the parser rules |
| 69 | + * a full argument rule would look like this: |
| 70 | + * { 'o', "operator", custom_enum_handler, &op, ARG_FLAG_DEFAULT } |
| 71 | + * |
| 72 | + * Where: |
| 73 | + * 'o' is the short key |
| 74 | + * "operator" is the long key |
| 75 | + * custom_enum_handler is the handler |
| 76 | + * &op is the return value for the handler |
| 77 | + * ARG_FLAG_DEFAULT is flag, defining the parser's behaviour, when the key is met */ |
| 78 | + arg_list list = { |
| 79 | + { 'o', "operator", custom_enum_handler, &op }, /* C allows to drop ARG_FLAGS_DEFAULT */ |
| 80 | + /* if the handler == NULL, the parser will consider the argument a flag and write |
| 81 | + * 1 into the return value (unless ARG_FLAG_UNSET is passed, which will write 0 in the ret. val). |
| 82 | + * If the return value passed == 0, nothing will happen. |
| 83 | + * Here, because `flags` is set to ARG_FLAG_HALT, the parser will stop, when the key is met. */ |
| 84 | + { 'h', "help", NULL, NULL, ARG_FLAG_HALT }, |
| 85 | + { 0 } /* any argument list should end with 0, so the parser could count the number of rules */ |
| 86 | + }; |
| 87 | + |
| 88 | + /* this variable will store the faulty argument (if there are any) */ |
| 89 | + char * err_arg; |
| 90 | + /* the return code of the parser (should be ARG_SUCCESS <0>) */ |
| 91 | + arg_return code; |
| 92 | + |
| 93 | + /* the buffer for the *not-a-key* value (value, not preceeded by a key) */ |
| 94 | + char * nk_buf[2]; |
| 95 | + /* size of the written buffer */ |
| 96 | + size_t nk_siz; |
| 97 | + |
| 98 | + /* call the parser |
| 99 | + * if an error occured, the parser will stop and return the faulty argument and will write the error |
| 100 | + * code to `code`. On success it will return NULL, and the return code will be ARG_SUCCESS. |
| 101 | + * |
| 102 | + * here: |
| 103 | + * &argc is a pointer to the argc variable |
| 104 | + * &argv is a pointer to the argv variable |
| 105 | + * list is the argument list |
| 106 | + * nk_buf is the buffer for storing *not-a-key* values |
| 107 | + * &nk_siz is the variable, that is going to store the size of the nk_buf |
| 108 | + * ARG_PARSE_MERGED is a flag, that tells the parser, that we allow merged key-value arguments (i.e -o+) |
| 109 | + * &code is a variable that is going to store the return code of the parser */ |
| 110 | + if ((err_arg = arg_parse (&argc, &argv, list, nk_buf, &nk_siz, ARG_PARSE_MERGED, &code)) != NULL) { |
| 111 | + /* if --help or -h is met, the return code will be set to ARG_HALT as defined in the parser rules*/ |
| 112 | + if (code == ARG_HALT) { |
| 113 | + puts (USAGE); |
| 114 | + return 0; |
| 115 | + } |
| 116 | + /* if an error occured */ |
| 117 | + printf ("Could not parse argument: %s\n", err_arg); |
| 118 | + return 1; |
| 119 | + } |
| 120 | + |
| 121 | + if (op == undef) { |
| 122 | + puts ("Specify an operator with -o [+, -, /, *, %]"); |
| 123 | + return 1; |
| 124 | + } |
| 125 | + |
| 126 | + /* Check, if user passed 2 arguments */ |
| 127 | + if (nk_siz != 2) { |
| 128 | + puts ("You have to specify two operands"); |
| 129 | + return 2; |
| 130 | + } |
| 131 | + |
| 132 | + int64_t a, b; |
| 133 | + /* write them into a and b */ |
| 134 | + a = atol (nk_buf[0]); |
| 135 | + b = atol (nk_buf[1]); |
| 136 | + |
| 137 | + switch (op) { |
| 138 | + case plus: |
| 139 | + a += b; |
| 140 | + break; |
| 141 | + case minus: |
| 142 | + a -= b; |
| 143 | + break; |
| 144 | + case divide: |
| 145 | + a /= b; |
| 146 | + break; |
| 147 | + case multiply: |
| 148 | + a *= b; |
| 149 | + break; |
| 150 | + case modulo: |
| 151 | + a %= b; |
| 152 | + break; |
| 153 | + } |
| 154 | + |
| 155 | + printf ("The result is %li\n", a); |
| 156 | + |
| 157 | + return 0; |
| 158 | +} |
0 commit comments