chiark / gitweb /
preload-hacks: Some patches to make it work.
[termux-packages] / packages / command-not-found / command-not-found.c
1 #include <stdio.h>
2 #include <string.h>
3 #include <stdbool.h>
4
5 #include "commands.h"
6
7 static inline int termux_min3(unsigned int a, unsigned int b, unsigned int c) {
8         return (a < b ? (a < c ? a : c) : (b < c ? b : c));
9 }
10
11 static int termux_levenshtein_distance(char const* restrict s1, char const* restrict s2) {
12         unsigned int s1len = strlen(s1);
13         unsigned int s2len = strlen(s2);
14         unsigned int matrix[s2len+1][s1len+1];
15         matrix[0][0] = 0;
16         for (unsigned x = 1; x <= s2len; x++)
17                 matrix[x][0] = matrix[x-1][0] + 1;
18         for (unsigned y = 1; y <= s1len; y++)
19                 matrix[0][y] = matrix[0][y-1] + 1;
20         for (unsigned x = 1; x <= s2len; x++)
21                 for (unsigned y = 1; y <= s1len; y++)
22                         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));
23         return matrix[s2len][s1len];
24 }
25
26 int main(int argc, char** argv) {
27         if (argc != 2) {
28                 fprintf(stderr, "usage: termux-command-not-found <command>\n");
29                 return 1;
30         }
31         char* command_not_found = argv[1];
32
33         int best_distance = -1;
34         int guesses_at_best_distance = 0;
35         char current_package[128];
36         char best_package_guess[128];
37         char best_command_guess[128];
38         const int num_commands = sizeof(commands) / sizeof(commands[0]);
39         for (int i = 0; i < num_commands; i++) {
40                 char const* current_line = commands[i];
41                 if (current_line[0] == ' ') { // Binary
42                         char const* binary_name = current_line + 1;
43                         int distance = termux_levenshtein_distance(command_not_found, binary_name);
44                         if (distance == 0 && strcmp(command_not_found, binary_name) == 0) {
45                                 if (best_distance == 0) {
46                                         fprintf(stderr, "or\n");
47                                 } else {
48                                         fprintf(stderr, "The program '%s' is not installed. Install it by executing:\n", binary_name);
49                                 }
50                                 fprintf(stderr, " pkg install %s\n", current_package);
51                                 best_distance = 0;
52                         } else if (best_distance == distance) {
53                                 guesses_at_best_distance++;
54                         } else if (best_distance == -1 || best_distance > distance) {
55                                 guesses_at_best_distance = 0;
56                                 best_distance = distance;
57                                 strncpy(best_command_guess, binary_name, sizeof(best_command_guess));
58                                 strncpy(best_package_guess, current_package, sizeof(best_package_guess));
59                         }
60                 } else { // Package
61                         strncpy(current_package, current_line, sizeof(current_package));
62                 }
63         }
64
65         if (best_distance == 0) return 127;
66
67         if (best_distance == -1 || best_distance > 3) {
68                 fprintf(stderr, "%s: command not found\n", command_not_found);
69         } else {
70                 fprintf(stderr, "No command '%s' found, did you mean:\n", command_not_found);
71                 if (guesses_at_best_distance == 1) {
72                         // Only one suggestion - show it:
73                         fprintf(stderr, " Command '%s' from package '%s'\n", best_command_guess, best_package_guess);
74                 } else {
75                         // Multiple suggestions at the same distance - show them all:
76                         for (int i = 0; i < num_commands; i++) {
77                                 char const* current_line = commands[i];
78                                 if (current_line[0] == ' ') { // Binary
79                                         char const* binary_name = current_line + 1;
80                                         int distance = termux_levenshtein_distance(command_not_found, binary_name);
81                                         if (best_distance == distance) {
82                                                 fprintf(stderr, " Command '%s' from package '%s'\n", binary_name, current_package);
83                                         }
84                                 } else { // Package
85                                         strncpy(current_package, current_line, sizeof(current_package));
86                                 }
87                         }
88                 }
89         }
90         return 127;
91 }
92