termux-packages/packages/command-not-found/command-not-found.c
Fredrik Fornwall 6ebd8dafa5 command-not-found: Instruct to use 'packages'
Running 'packages install' wraps apt to run 'apt update' before
running 'apt install', so it should avoid the initial confusion
with 'apt install' not working for any packages.
2017-03-20 20:22:57 +01:00

86 lines
3.0 KiB
C

#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include "commands.h"
static inline int termux_min3(unsigned int a, unsigned int b, unsigned int c) {
return (a < b ? (a < c ? a : c) : (b < c ? b : c));
}
static int termux_levenshtein_distance(char const* restrict s1, char const* restrict s2) {
unsigned int s1len = strlen(s1);
unsigned int s2len = strlen(s2);
unsigned int matrix[s2len+1][s1len+1];
matrix[0][0] = 0;
for (unsigned x = 1; x <= s2len; x++)
matrix[x][0] = matrix[x-1][0] + 1;
for (unsigned y = 1; y <= s1len; y++)
matrix[0][y] = matrix[0][y-1] + 1;
for (unsigned x = 1; x <= s2len; x++)
for (unsigned y = 1; y <= s1len; y++)
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));
return matrix[s2len][s1len];
}
int main(int argc, char** argv) {
if (argc != 2) {
fprintf(stderr, "usage: termux-command-not-found <command>\n");
return 1;
}
char* command_not_found = argv[1];
int best_distance = -1;
int guesses_at_best_distance = 0;
char current_package[128];
char best_package_guess[128];
char best_command_guess[128];
const int num_commands = sizeof(commands) / sizeof(commands[0]);
for (int i = 0; i < num_commands; i++) {
char const* current_line = commands[i];
if (current_line[0] == ' ') { // Binary
char const* binary_name = current_line + 1;
int distance = termux_levenshtein_distance(command_not_found, binary_name);
if (distance == 0 && strcmp(command_not_found, binary_name) == 0) {
printf("The program '%s' is not installed. Install it by executing:\n packages install %s\n", binary_name, current_package);
return 127;
} else if (best_distance == distance) {
guesses_at_best_distance++;
} else if (best_distance == -1 || best_distance > distance) {
guesses_at_best_distance = 0;
best_distance = distance;
strncpy(best_command_guess, binary_name, sizeof(best_command_guess));
strncpy(best_package_guess, current_package, sizeof(best_package_guess));
}
} else { // Package
strncpy(current_package, current_line, sizeof(current_package));
}
}
if (best_distance == -1 || best_distance > 3) {
printf("%s: command not found\n", command_not_found);
} else {
printf("No command '%s' found, did you mean:\n", command_not_found);
if (guesses_at_best_distance == 1) {
// Only one suggestion - show it:
printf(" Command '%s' from package '%s'\n", best_command_guess, best_package_guess);
} else {
// Multiple suggestions at the same distance - show them all:
for (int i = 0; i < num_commands; i++) {
char const* current_line = commands[i];
if (current_line[0] == ' ') { // Binary
char const* binary_name = current_line + 1;
int distance = termux_levenshtein_distance(command_not_found, binary_name);
if (best_distance == distance) {
printf(" Command '%s' from package '%s'\n", binary_name, current_package);
}
} else { // Package
strncpy(current_package, current_line, sizeof(current_package));
}
}
}
}
return 127;
}