commit d93243566cb258a8bde11530aa4629e704cd4e3e Author: Andrew Kloet Date: Thu Jul 10 10:12:18 2025 -0400 initial commit diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..9cf4486 --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2025 Andrew Kloet + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8984336 --- /dev/null +++ b/Makefile @@ -0,0 +1,32 @@ +CC = cc +CFLAGS = -Wall -Wextra -O3 +CURL_LIBS = -lcurl + +SOURCES = sb-cpu.c sb-date.c sb-memory.c sb-time.c sb-weather.c + +EXECUTABLES = $(SOURCES:.c=) + +all: $(EXECUTABLES) + +sb-weather: sb-weather.c + $(CC) $(CFLAGS) $< -o $@ $(CURL_LIBS) + +sb-cpu: sb-cpu.c + $(CC) $(CFLAGS) $< -o $@ + +sb-date: sb-date.c + $(CC) $(CFLAGS) $< -o $@ + +sb-memory: sb-memory.c + $(CC) $(CFLAGS) $< -o $@ + +sb-time: sb-time.c + $(CC) $(CFLAGS) $< -o $@ + +clean: + rm -f $(EXECUTABLES) + +install: all + cp $(EXECUTABLES) /usr/local/bin + +.PHONY: all clean diff --git a/README b/README new file mode 100644 index 0000000..2b0e839 --- /dev/null +++ b/README @@ -0,0 +1,15 @@ +Tiny programs for Linux which print useful info. Suited for piping into other programs which handle window manager statusbar, like Suckless' DWM. +Compile with `make`. Install with `make install`. + +``` +$ ./sb-cpu +CPU: 2% +$ ./sb-memory +Mem: 34% +$ ./sb-weather glasgow +27°C Overcast +$ ./sb-time +10:10am +$ ./sb-date +Jul 10 2025 +``` diff --git a/sb-cpu.c b/sb-cpu.c new file mode 100644 index 0000000..4d8ff1b --- /dev/null +++ b/sb-cpu.c @@ -0,0 +1,53 @@ +#include +#include + +typedef struct { + long long idle; + long long total; +} cpu_stats_t; + +static int read_cpu_stats(cpu_stats_t *stats) { + FILE *fp = fopen("/proc/stat", "r"); + if (!fp) + return 1; + + long long user, nice, system, idle, iowait, irq, softirq; + int ret = fscanf(fp, "cpu %lld %lld %lld %lld %lld %lld %lld", &user, &nice, + &system, &idle, &iowait, &irq, &softirq); + fclose(fp); + + if (ret != 7) + return 1; + + stats->idle = idle + iowait; + stats->total = user + nice + system + idle + iowait + irq + softirq; + return 0; +} + +static double calculate_usage(cpu_stats_t *prev, cpu_stats_t *curr) { + long long diff_total = curr->total - prev->total; + long long diff_idle = curr->idle - prev->idle; + + if (diff_total == 0) + return 0.0; + return 100.0 * (diff_total - diff_idle) / diff_total; +} + +int main() { + cpu_stats_t prev, curr; + + if (read_cpu_stats(&prev) < 0) { + fprintf(stderr, "Error reading CPU stats\n"); + return 1; + } + + sleep(1); + + if (read_cpu_stats(&curr) < 0) { + fprintf(stderr, "Error reading CPU stats\n"); + return 1; + } + + printf("CPU: %.0f%%\n", calculate_usage(&prev, &curr)); + return 0; +} diff --git a/sb-date.c b/sb-date.c new file mode 100644 index 0000000..6b5c83c --- /dev/null +++ b/sb-date.c @@ -0,0 +1,14 @@ +#include +#include + +int main() { + time_t t = time(NULL); + struct tm tm = *localtime(&t); + + const char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + + printf("%s %02d %d\n", months[tm.tm_mon], tm.tm_mday, tm.tm_year + 1900); + + return 0; +} diff --git a/sb-memory.c b/sb-memory.c new file mode 100644 index 0000000..3dc1085 --- /dev/null +++ b/sb-memory.c @@ -0,0 +1,18 @@ +#include + +int main() { + FILE *fp = fopen("/proc/meminfo", "r"); + if (!fp) + return 1; + + long long total = 0, available = 0; + char line[256]; + while (fgets(line, sizeof(line), fp) && (!total || !available)) { + sscanf(line, "MemTotal: %lld kB", &total); + sscanf(line, "MemAvailable: %lld kB", &available); + } + fclose(fp); + + printf("Mem: %.0f%%\n", (double)(total - available) / total * 100); + return 0; +} diff --git a/sb-time.c b/sb-time.c new file mode 100644 index 0000000..34c0006 --- /dev/null +++ b/sb-time.c @@ -0,0 +1,11 @@ +#include +#include + +int main() { + time_t t = time(NULL); + char time_str[10]; + + strftime(time_str, sizeof(time_str), "%I:%M%P", localtime(&t)); + printf("%s\n", time_str); + return 0; +} diff --git a/sb-weather.c b/sb-weather.c new file mode 100644 index 0000000..f80bc60 --- /dev/null +++ b/sb-weather.c @@ -0,0 +1,58 @@ +#include +#include +#include +#include + +#define WEATHER_FORMAT "?format=%f+%C" +#define MAX_SIZE 1024 + +size_t write_callback(void *ptr, size_t size, size_t nmemb, char *data) { + strncat(data, ptr, size * nmemb); + return size * nmemb; +} + +int main(int argc, char *argv[]) { + if (argc < 2) { + fprintf(stderr, "Usage: %s \n", argv[0]); + return 1; + } + + char url[MAX_SIZE], weather_data[MAX_SIZE] = {0}, cache_path[MAX_SIZE]; + const char *cache_dir = getenv("XDG_CACHE_HOME") ?: "/tmp"; + + snprintf(url, sizeof(url), "https://wttr.in/%s%s", argv[1], WEATHER_FORMAT); + snprintf(cache_path, sizeof(cache_path), "%s/weatherreport", cache_dir); + + curl_global_init(CURL_GLOBAL_DEFAULT); + CURL *curl = curl_easy_init(); + if (!curl) + return 1; + + curl_easy_setopt(curl, CURLOPT_URL, url); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, weather_data); + + if (curl_easy_perform(curl) != CURLE_OK) { + curl_easy_cleanup(curl); + curl_global_cleanup(); + return 1; + } + + curl_easy_cleanup(curl); + curl_global_cleanup(); + + // Remove '+' character + for (char *p = weather_data; *p; p++) { + if (*p == '+') + memmove(p, p + 1, strlen(p)); + } + + FILE *fp = fopen(cache_path, "w"); + if (fp) { + fputs(weather_data, fp); + fclose(fp); + } + + printf("%s\n", weather_data); + return 0; +}