Commit | Line | Data |
---|---|---|
284604c7 FF |
1 | #include <stdio.h> |
2 | #include <string.h> | |
3 | #include <stdbool.h> | |
4 | ||
5 | #define STRINGIFY(x) #x | |
6 | #define TOSTRING(x) STRINGIFY(x) | |
7 | ||
8 | inline int termux_min3(unsigned int a, unsigned int b, unsigned int c) { | |
9 | return (a < b ? (a < c ? a : c) : (b < c ? b : c)); | |
10 | } | |
11 | ||
12 | int termux_levenshtein_distance(char const* restrict s1, char const* restrict s2) { | |
13 | unsigned int s1len = strlen(s1); | |
14 | unsigned int s2len = strlen(s2); | |
15 | unsigned int matrix[s2len+1][s1len+1]; | |
16 | matrix[0][0] = 0; | |
17 | for (unsigned x = 1; x <= s2len; x++) | |
18 | matrix[x][0] = matrix[x-1][0] + 1; | |
19 | for (unsigned y = 1; y <= s1len; y++) | |
20 | matrix[0][y] = matrix[0][y-1] + 1; | |
21 | for (unsigned x = 1; x <= s2len; x++) | |
22 | for (unsigned y = 1; y <= s1len; y++) | |
23 | matrix[x][y] = termux_min3(matrix[x-1][y] + 1, matrix[x][y-1] + 1, matrix[x-1][y-1] + (s1[y-1] == s2[x-1] ? 0 : 1)); | |
24 | return matrix[s2len][s1len]; | |
25 | } | |
26 | ||
27 | int main(int argc, char** argv) { | |
28 | if (argc != 2) { | |
29 | fprintf(stderr, "usage: termux-command-not-found <command>\n"); | |
30 | return 1; | |
31 | } | |
32 | char* command_not_found = argv[1]; | |
33 | ||
34 | FILE* commands_file = fopen(TOSTRING(TERMUX_COMMANDS_LISTING), "r"); | |
35 | if (commands_file == NULL) { | |
36 | perror(TOSTRING(TERMUX_COMMANDS_LISTING)); | |
37 | return 1; | |
38 | } | |
39 | ||
40 | int best_distance = -1; | |
41 | int guesses_at_best_distance = 0; | |
42 | char current_package[128]; | |
43 | char best_package_guess[128]; | |
44 | char best_command_guess[128]; | |
45 | char* current_line = NULL; | |
46 | while (true) { | |
47 | size_t buffer_length = sizeof(current_line); | |
48 | ssize_t read_bytes = getline(¤t_line, &buffer_length, commands_file); | |
49 | if (read_bytes <= 1) break; | |
50 | size_t line_length = strlen(current_line); | |
51 | current_line[line_length-1] = 0; | |
52 | if (current_line[0] == ' ') { // Binary | |
53 | char* binary_name = current_line + 1; | |
54 | int distance = termux_levenshtein_distance(command_not_found, binary_name); | |
55 | if (distance == 0 && strcmp(command_not_found, binary_name) == 0) { | |
56 | printf("The program '%s' is currently not installed. You can install it by executing:\n apt install %s\n", binary_name, current_package); | |
57 | return 0; | |
58 | } else if (best_distance == distance) { | |
59 | guesses_at_best_distance++; | |
60 | } else if (best_distance == -1 || best_distance > distance) { | |
61 | guesses_at_best_distance = 0; | |
62 | best_distance = distance; | |
63 | strncpy(best_command_guess, binary_name, sizeof(best_command_guess)); | |
64 | strncpy(best_package_guess, current_package, sizeof(best_package_guess)); | |
65 | } | |
66 | } else { // Package | |
67 | strncpy(current_package, current_line, sizeof(current_package)); | |
68 | } | |
69 | } | |
70 | ||
71 | if (best_distance == -1 || best_distance > 3) { | |
72 | printf("%s: command not found\n", command_not_found); | |
73 | } else { | |
74 | printf("No command '%s' found, did you mean:\n", command_not_found); | |
6ee3449e FF |
75 | if (guesses_at_best_distance == 1) { |
76 | // Only one suggestion - show it: | |
77 | printf(" Command '%s' from package '%s'\n", best_command_guess, best_package_guess); | |
78 | } else { | |
79 | // Multiple suggestions at the same distance - show them all: | |
80 | rewind(commands_file); | |
81 | while (true) { | |
82 | size_t buffer_length = sizeof(current_line); | |
83 | ssize_t read_bytes = getline(¤t_line, &buffer_length, commands_file); | |
84 | if (read_bytes <= 1) break; | |
85 | size_t line_length = strlen(current_line); | |
86 | current_line[line_length-1] = 0; | |
87 | if (current_line[0] == ' ') { // Binary | |
88 | char* binary_name = current_line + 1; | |
89 | int distance = termux_levenshtein_distance(command_not_found, binary_name); | |
90 | if (best_distance == distance) { | |
91 | printf(" Command '%s' from package '%s'\n", binary_name, current_package); | |
92 | } | |
93 | } else { // Package | |
94 | strncpy(current_package, current_line, sizeof(current_package)); | |
95 | } | |
96 | } | |
97 | } | |
284604c7 FF |
98 | } |
99 | } | |
100 |