Created
January 26, 2026 18:46
-
-
Save nyteshade/93cf63e9e599aff0ff3662102f0bb889 to your computer and use it in GitHub Desktop.
Simple shell commands
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <ctype.h> | |
| /* ANSI SGR Escape Codes for Pretty Printing */ | |
| #define RED "\033[31m" | |
| #define GREEN "\033[32m" | |
| #define BLUE "\033[34m" | |
| #define RESET "\033[0m" | |
| #define BOLD "\033[1m" | |
| /** | |
| * Custom cross-platform basename implementation. | |
| * Returns a pointer to the filename within the path string. | |
| */ | |
| const char *get_basename(const char *path) { | |
| const char *base = path; | |
| const char *p; | |
| for (p = path; *p != '\0'; p++) { | |
| /* Check for both POSIX (/) and Windows (\) separators */ | |
| if (*p == '/' || *p == '\\') { | |
| base = p + 1; | |
| } | |
| } | |
| return base; | |
| } | |
| /** | |
| * C89-compatible case-insensitive substring search. | |
| */ | |
| char *strcasestr_c89(const char *haystack, const char *needle) { | |
| size_t i; | |
| size_t needle_len = strlen(needle); | |
| size_t haystack_len = strlen(haystack); | |
| if (needle_len == 0) return (char *)haystack; | |
| if (haystack_len < needle_len) return NULL; | |
| for (i = 0; i <= haystack_len - needle_len; i++) { | |
| size_t j; | |
| for (j = 0; j < needle_len; j++) { | |
| if (tolower((unsigned char)haystack[i + j]) != | |
| tolower((unsigned char)needle[j])) { | |
| break; | |
| } | |
| } | |
| if (j == needle_len) return (char *)(haystack + i); | |
| } | |
| return NULL; | |
| } | |
| void print_usage(const char *prog_name) { | |
| printf(BOLD "Usage:" RESET " %s [OPTIONS] <search_path>\n\n", prog_name); | |
| printf(BOLD "Options:\n" RESET); | |
| printf(" " BLUE "-c, --case-insensitive" RESET " Perform a case-insensitive search\n"); | |
| printf(" " BLUE "-v, --verbose" RESET " Print the matching entry found in PATH\n"); | |
| printf(" " BLUE "-h, --help" RESET " Show this help message\n\n"); | |
| printf(BOLD "Returns:\n" RESET); | |
| printf(" " GREEN "0" RESET " if the path is found, " RED "1" RESET " otherwise.\n"); | |
| } | |
| int main(int argc, char *argv[]) { | |
| char *path_env; | |
| char *search_str = NULL; | |
| char *path_copy; | |
| char *token; | |
| const char *prog_name; | |
| int case_insensitive = 0; | |
| int verbose = 0; | |
| int found = 0; | |
| int i; | |
| /* Extract basename for the usage string */ | |
| prog_name = get_basename(argv[0]); | |
| /* Manual argument parsing for maximum compatibility */ | |
| for (i = 1; i < argc; i++) { | |
| if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) { | |
| print_usage(prog_name); | |
| return 0; | |
| } else if (strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--case-insensitive") == 0) { | |
| case_insensitive = 1; | |
| } else if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) { | |
| verbose = 1; | |
| } else { | |
| /* Assume anything without a dash is the search string */ | |
| search_str = argv[i]; | |
| } | |
| } | |
| if (!search_str) { | |
| print_usage(prog_name); | |
| return 1; | |
| } | |
| path_env = getenv("PATH"); | |
| if (!path_env) { | |
| if (verbose) fprintf(stderr, RED "Error:" RESET " PATH variable not found.\n"); | |
| return 1; | |
| } | |
| /* Duplicate PATH; strtok is destructive */ | |
| path_copy = (char *)malloc(strlen(path_env) + 1); | |
| if (!path_copy) { | |
| fprintf(stderr, RED "Error:" RESET " Memory allocation failed.\n"); | |
| return 1; | |
| } | |
| strcpy(path_copy, path_env); | |
| /* POSIX path separator is ':' (Windows uses ';') */ | |
| token = strtok(path_copy, ":"); | |
| while (token != NULL) { | |
| int match = 0; | |
| if (case_insensitive) { | |
| if (strcasestr_c89(token, search_str) != NULL) match = 1; | |
| } else { | |
| if (strstr(token, search_str) != NULL) match = 1; | |
| } | |
| if (match) { | |
| if (verbose) printf(GREEN "Found in PATH:" RESET " %s\n", token); | |
| found = 1; | |
| break; | |
| } | |
| token = strtok(NULL, ":"); | |
| } | |
| free(path_copy); | |
| /* Return 0 if found, 1 if not */ | |
| return found ? 0 : 1; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment