mfterm.c 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. /**
  2. * Copyright (C) 2011-2013 Anders Sundman <anders@4zm.org>
  3. *
  4. * This file is part of mfterm.
  5. *
  6. * mfterm is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * mfterm 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
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with mfterm. If not, see <http://www.gnu.org/licenses/>.
  18. *
  19. * Parts of code used in this file are from the GNU readline library file
  20. * fileman.c (GPLv3). Copyright (C) 1987-2009 Free Software Foundation, Inc
  21. */
  22. #include <stdlib.h>
  23. #include <stdio.h>
  24. #include <stddef.h>
  25. #include <string.h>
  26. #include <readline/readline.h>
  27. #include <readline/history.h>
  28. #include <getopt.h>
  29. #include "term_cmd.h"
  30. #include "mfterm.h"
  31. #include "util.h"
  32. #include "spec_syntax.h"
  33. #include "config.h"
  34. int stop_input_loop_ = 0;
  35. void stop_input_loop() {
  36. stop_input_loop_ = 1;
  37. }
  38. char* completion_cmd_generator(const char* text, int state);
  39. char* completion_sub_cmd_generator(const char* text, int state);
  40. char* completion_spec_generator(const char* text, int state);
  41. int perform_filename_completion();
  42. char** mft_completion(char* text, int start, int end);
  43. int execute_line(char* line);
  44. void initialize_readline();
  45. void parse_cmdline(int argc, char** argv);
  46. void print_help();
  47. void print_version();
  48. typedef char** rl_completion_func_t(const char*, int, int);
  49. int main(int argc, char** argv) {
  50. parse_cmdline(argc, argv);
  51. initialize_readline();
  52. input_loop();
  53. return 0;
  54. }
  55. void parse_cmdline(int argc, char** argv) {
  56. static struct option long_options[] = {
  57. {"help", no_argument, 0, 'h' },
  58. {"version", no_argument, 0, 'v' },
  59. {"tag", required_argument, 0, 't' },
  60. {"keys", required_argument, 0, 'k' },
  61. {"dict", required_argument, 0, 'd' },
  62. {0, 0, 0, 0 }
  63. };
  64. char* tag_file = NULL;
  65. char* keys_file = NULL;
  66. char* dict_file = NULL;
  67. int opt = 0;
  68. int long_index = 0;
  69. while ((opt = getopt_long(argc, argv,"hvt:k:d:",
  70. long_options, &long_index )) != -1) {
  71. switch (opt) {
  72. case 'h' :
  73. print_help();
  74. exit(0);
  75. case 'v' :
  76. print_version();
  77. exit(0);
  78. case 't' : tag_file = optarg;
  79. break;
  80. case 'k' : keys_file = optarg;
  81. break;
  82. case 'd' : dict_file = optarg;
  83. break;
  84. default :
  85. exit(1);
  86. }
  87. }
  88. // If a tag file was specified, load it
  89. if (tag_file != NULL && com_load_tag(tag_file))
  90. exit(0);
  91. // If a keys file was specified, load it
  92. if (keys_file != NULL && com_keys_load(keys_file))
  93. exit(0);
  94. // If a dictionary file was specified, load it
  95. if (dict_file != NULL && com_dict_load(dict_file))
  96. exit(0);
  97. // Default is to do nothing, just enter the terminal
  98. }
  99. // Request user input until stop_intput_loop_ == 0
  100. void input_loop() {
  101. char *line, *s;
  102. while (stop_input_loop_ == 0) {
  103. line = readline ("$ ");
  104. if (!line)
  105. break;
  106. s = trim(line);
  107. if (*s) {
  108. add_history(s);
  109. execute_line(s);
  110. }
  111. free (line);
  112. }
  113. }
  114. /* Execute a command line. */
  115. int execute_line (char* line) {
  116. if (strncmp(line, ".", 1) == 0)
  117. return exec_path_command(line);
  118. command_t* command = find_command(line);
  119. if (!command) {
  120. fprintf (stderr, "%s: No such command.\n", line);
  121. return -1;
  122. }
  123. // Skip past command and ws to the arguments
  124. line += strlen(command->name);
  125. line = trim(line);
  126. return (*(command->func))(line);
  127. }
  128. void initialize_readline()
  129. {
  130. rl_readline_name = "MFT";
  131. rl_attempted_completion_function = (rl_completion_func_t*)mft_completion;
  132. }
  133. /* Attempt to complete on the contents of TEXT. START and END bound the
  134. region of rl_line_buffer that contains the word to complete. TEXT is
  135. the word to complete. We can use the entire contents of rl_line_buffer
  136. in case we want to do some simple parsing. Return the array of matches,
  137. or NULL if there aren't any. */
  138. char** mft_completion(char* text, int start, int end) {
  139. char **matches = (char **)NULL;
  140. // Don't complete on files for most cases
  141. rl_attempted_completion_over = 1;
  142. // Add the trailing space unless told otherwise
  143. rl_completion_suppress_append = 0;
  144. // Complete strings starting with '.' as specification paths
  145. if (text[0] == '.' && instance_root) {
  146. rl_completion_suppress_append = 1; // no trailing space on paths
  147. return rl_completion_matches(text, completion_spec_generator);
  148. }
  149. // Commands start at 0
  150. if (start == 0)
  151. return rl_completion_matches(text, completion_cmd_generator);
  152. // else: Sub commands and file arguments start at > 0
  153. // Try to match sub commands
  154. matches = rl_completion_matches(text, completion_sub_cmd_generator);
  155. if (matches)
  156. return matches;
  157. if (perform_filename_completion()) {
  158. // Do complete on filenames
  159. rl_attempted_completion_over = 0;
  160. return matches;
  161. }
  162. return matches;
  163. }
  164. int perform_filename_completion() {
  165. for (int i = 0; commands[i].name; ++i) {
  166. if (commands[i].fn_arg &&
  167. strncmp(rl_line_buffer, commands[i].name,
  168. strlen(commands[i].name)) == 0) {
  169. return 1;
  170. }
  171. }
  172. return 0;
  173. }
  174. /**
  175. Called to generate completion suggestions.
  176. state == 0 on first call.
  177. */
  178. char* completion_cmd_generator(const char* text, int state) {
  179. static int cmd_index;
  180. static size_t len;
  181. // First call?
  182. if (!state) {
  183. cmd_index = 0;
  184. len = strlen(text); // Cached for performance
  185. }
  186. // Return next suggestion
  187. char *name;
  188. while ((name = commands[cmd_index].name)) {
  189. ++cmd_index;
  190. // Check if the command is applicable
  191. if (strncmp(name, text, len) == 0) {
  192. char* r = malloc(strlen(name) + 1);
  193. strcpy(r, name);
  194. return r;
  195. }
  196. }
  197. // No (more) matches
  198. return (char*) NULL;
  199. }
  200. char* completion_sub_cmd_generator(const char* text, int state) {
  201. static int cmd_index;
  202. static size_t len, full_len;
  203. // First call?
  204. if (!state) {
  205. cmd_index = 0;
  206. len = strlen(text); // Cached for performance
  207. full_len = strlen(rl_line_buffer);
  208. }
  209. // Return next suggestion
  210. char* name;
  211. while ((name = commands[cmd_index].name)) {
  212. ++cmd_index;
  213. // Extract command and sub-command
  214. char buff[128];
  215. strncpy(buff, name, sizeof(buff));
  216. char* cmd = strtok(buff, " ");
  217. char* sub = strtok(NULL, " ");
  218. // Make sure the command *has* a sub command
  219. // and that we have the right command.
  220. if (cmd && sub && strncmp(rl_line_buffer, name, full_len) == 0) {
  221. // Check if the sub command is applicable
  222. if (strncmp(sub, text, len) == 0) {
  223. char* r = malloc(strlen(sub) + 1);
  224. strcpy(r, sub);
  225. return r;
  226. }
  227. }
  228. }
  229. // No (more) matches
  230. return (char*) NULL;
  231. }
  232. /**
  233. * Called to generate completion suggestions.
  234. * state == 0 on first call.
  235. */
  236. char* completion_spec_generator(const char* text, int state) {
  237. // Parent context is initialized on the first call
  238. static instance_t* parent_inst;
  239. static const char* parent_end;
  240. static size_t parent_end_len;
  241. // Instace iter is advanced on each repeated call
  242. static instance_list_t* inst_iter;
  243. // First call?
  244. if (!state) {
  245. // Set the parent context
  246. if (parse_partial_spec_path(text, &parent_end, &parent_inst) != 0)
  247. return NULL; // on error
  248. parent_end_len = strlen(parent_end);
  249. // The instance iter points to the first fields
  250. inst_iter = parent_inst->fields;
  251. }
  252. while (inst_iter) {
  253. instance_t* inst = inst_iter->instance;
  254. inst_iter = inst_iter->next_;
  255. // Anonymous filler field
  256. if (inst->field->name == NULL)
  257. continue;
  258. char* fname = inst->field->name;
  259. size_t fname_len = strlen(fname);
  260. // Check if the field is applicable - right prefix
  261. if (fname_len >= parent_end_len &&
  262. strncmp(fname, parent_end, parent_end_len) == 0) {
  263. if (parent_end - text <= 0)
  264. return NULL;
  265. size_t parent_len = (size_t)(parent_end - text);
  266. char* str = malloc(parent_len + fname_len + 1);
  267. // The parent part ending with '.'
  268. strncpy(str, text, parent_len);
  269. // The field
  270. strncpy(str + parent_len, fname, fname_len);
  271. // Null termination
  272. *(str + parent_len + fname_len) = '\0';
  273. return str;
  274. }
  275. }
  276. // No (more) matches
  277. return NULL;
  278. }
  279. void print_help() {
  280. printf("A terminal interface for working with Mifare Classic tags.\n");
  281. printf("Usage: mfterm [-v] [-h] [-k keyfile]\n");
  282. printf("\n");
  283. printf("Options: \n");
  284. printf(" --help (-h) Show this help message.\n");
  285. printf(" --version (-v) Display version information.\n");
  286. printf(" --tag=tagfile (-t) Load a tag from the specified file.\n");
  287. printf(" --keys=keyfile (-k) Load keys from the specified file.\n");
  288. printf(" --dict=dictfile (-d) Load dictionary from the specified file.\n");
  289. printf("\n");
  290. printf("Report bugs to: anders@4zm.org\n");
  291. printf(PACKAGE_NAME); printf(" home page: <https://github.com/4zm/mfterm>\n");
  292. }
  293. void print_version() {
  294. printf(PACKAGE_STRING); printf("\n");
  295. printf("Copyright (C) 2011-2013 Anders Sundman <anders@4zm.org>\n");
  296. printf("License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n");
  297. printf("This is free software: you are free to change and redistribute it.\n");
  298. printf("There is NO WARRANTY, to the extent permitted by law.\n");
  299. }