Skip to content

Instantly share code, notes, and snippets.

@nyteshade
Created January 26, 2026 18:46
Show Gist options
  • Select an option

  • Save nyteshade/93cf63e9e599aff0ff3662102f0bb889 to your computer and use it in GitHub Desktop.

Select an option

Save nyteshade/93cf63e9e599aff0ff3662102f0bb889 to your computer and use it in GitHub Desktop.
Simple shell commands
#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