From 56412a8e8b3ee508d560c502bc76a60c617af5dd Mon Sep 17 00:00:00 2001 From: anihilis Date: Thu, 12 Feb 2026 10:35:18 -0800 Subject: [PATCH] initial commit -- upload --- README.md | 6 +- clock | Bin 0 -> 25984 bytes clock.c | 211 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 215 insertions(+), 2 deletions(-) create mode 100755 clock create mode 100644 clock.c diff --git a/README.md b/README.md index 7eb4904..89f9b7b 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,4 @@ -# cclock - +# cclock +#### Simply C program to display the time and temperature without too much fuss, made for home use. + +Build with ```gcc -Wall -Wextra -pedantic -std=c11 clock.c -lncurses -o clock``` diff --git a/clock b/clock new file mode 100755 index 0000000000000000000000000000000000000000..7c438daa5cb10c741c9af9ebe9f34e8b6eb847f0 GIT binary patch literal 25984 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 AYXATM literal 0 HcmV?d00001 diff --git a/clock.c b/clock.c new file mode 100644 index 0000000..3e682df --- /dev/null +++ b/clock.c @@ -0,0 +1,211 @@ +#define _POSIX_C_SOURCE 200809L + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static volatile sig_atomic_t running = 1; +static volatile sig_atomic_t resized = 0; + +static void handle_sigint(int sig) { (void)sig; running = 0; } +static void handle_winch(int sig) { (void)sig; resized = 1; } + +/* ---------------- Config ---------------- */ +typedef struct { + char api_key[128]; + char location[64]; + char units[16]; +} config_t; + +/* Simple TOML parser for [weather] section */ +static bool load_config_toml(config_t *cfg, const char *path) { + FILE *f = fopen(path, "r"); + if (!f) return false; + + bool in_weather_section = false; + while (!feof(f)) { + char line[256]; + if (!fgets(line, sizeof(line), f)) break; + + /* Remove comments */ + char *hash = strchr(line, '#'); + if (hash) *hash = '\0'; + + /* Strip leading/trailing whitespace */ + char *start = line; + while (*start && (*start == ' ' || *start == '\t')) start++; + char *end = start + strlen(start) - 1; + while (end > start && (*end == ' ' || *end == '\t' || *end == '\n' || *end == '\r')) *end-- = '\0'; + + if (strlen(start) == 0) continue; + + if (start[0] == '[' && start[strlen(start)-1] == ']') { + if (strncmp(start+1, "weather", 7) == 0) in_weather_section = true; + else in_weather_section = false; + continue; + } + + if (!in_weather_section) continue; + + char key[32], val[128]; + if (sscanf(start, "%31[^=]= \"%127[^\"]\"", key, val) == 2) { + char *k_end = key + strlen(key) - 1; + while (k_end > key && (*k_end == ' ' || *k_end == '\t')) *k_end-- = '\0'; + + if (strcmp(key, "api_key") == 0) strncpy(cfg->api_key, val, sizeof(cfg->api_key)-1); + else if (strcmp(key, "location") == 0) strncpy(cfg->location, val, sizeof(cfg->location)-1); + else if (strcmp(key, "units") == 0) strncpy(cfg->units, val, sizeof(cfg->units)-1); + } + } + + fclose(f); + return cfg->api_key[0] && cfg->location[0]; +} + +/* ---------------- libcurl callback ---------------- */ +struct mem { + char *data; + size_t size; +}; + +static size_t write_cb(void *ptr, size_t size, size_t nmemb, void *userdata) { + struct mem *m = (struct mem *)userdata; + size_t total = size * nmemb; + + if (m->size + total < 10240) { + memcpy(m->data + m->size, ptr, total); + m->size += total; + m->data[m->size] = '\0'; + } + return total; +} + +/* ---------------- Fetch temperature ---------------- */ +bool fetch_temp_owm(const config_t *cfg, char *out, size_t out_size) { + if (!cfg || !out) return false; + + CURL *curl = curl_easy_init(); + if (!curl) return false; + + char url[512]; + snprintf(url, sizeof(url), + "https://api.openweathermap.org/data/2.5/weather?q=%s&units=%s&appid=%s", + cfg->location, cfg->units[0] ? cfg->units : "metric", cfg->api_key); + + char buffer[10240] = {0}; + struct mem m = { buffer, 0 }; + + curl_easy_setopt(curl, CURLOPT_URL, url); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &m); + curl_easy_setopt(curl, CURLOPT_USERAGENT, "tty-clock-weather/1.0"); + + CURLcode res = curl_easy_perform(curl); + curl_easy_cleanup(curl); + + if (res != CURLE_OK) return false; + + char *p = strstr(buffer, "\"temp\":"); + if (!p) return false; + p += 7; + + double temp = atof(p); + char unit = (cfg->units[0] && strcmp(cfg->units, "imperial") == 0) ? 'F' : 'C'; + snprintf(out, out_size, "%.0f°%c %s", temp, unit, cfg->location); + + return true; +} + +/* ---------------- Main ---------------- */ +int main(int argc, char **argv) { + config_t cfg = {0}; + char config_path[512]; + snprintf(config_path, sizeof(config_path), "%s/.config/cclock/config", getenv("HOME")); + + if (!load_config_toml(&cfg, config_path)) { + printf("Failed to load config from %s\n", config_path); + return 1; + } + + bool twelve_hour = false; + for (int i = 1; i < argc; i++) + if (strcmp(argv[i], "-t") == 0) twelve_hour = true; + + signal(SIGINT, handle_sigint); + signal(SIGWINCH, handle_winch); + + initscr(); + cbreak(); + noecho(); + curs_set(0); + keypad(stdscr, TRUE); + nodelay(stdscr, TRUE); + + curl_global_init(CURL_GLOBAL_DEFAULT); + + char time_buf[16], date_buf[11], temp_buf[64]; + time_t last_fetch = 0; + const int refresh_interval = 600; /* 10 minutes */ + + if (!fetch_temp_owm(&cfg, temp_buf, sizeof(temp_buf))) + strncpy(temp_buf, "N/A", sizeof(temp_buf)); + + while (running) { + if (resized) { + endwin(); + refresh(); + clear(); + resized = 0; + } + + clear(); + + time_t now = time(NULL); + struct tm *tm_now = localtime(&now); + if (!tm_now) break; + + const char *time_fmt = twelve_hour ? "%I:%M %p" : "%H:%M"; + strftime(time_buf, sizeof(time_buf), time_fmt, tm_now); + strftime(date_buf, sizeof(date_buf), "%m/%d/%Y", tm_now); + + if (difftime(now, last_fetch) >= refresh_interval) { + if (!fetch_temp_owm(&cfg, temp_buf, sizeof(temp_buf))) + strncpy(temp_buf, "N/A", sizeof(temp_buf)); + last_fetch = now; + } + + int rows, cols; + getmaxyx(stdscr, rows, cols); + + int time_x = (cols - strlen(time_buf)) / 2; + int y_mid = rows / 2; + attron(A_BOLD); + mvprintw(y_mid, time_x, "%s", time_buf); + attroff(A_BOLD); + + int date_x = (cols - strlen(date_buf)) / 2; + mvprintw(y_mid + 1, date_x, "%s", date_buf); + + int temp_x = (cols - strlen(temp_buf)) / 2; + mvprintw(y_mid + 2, temp_x, "%s", temp_buf); + + mvprintw(rows - 1, 0, "Press 'q' to quit"); + + refresh(); + + int ch = getch(); + if (ch == 'q' || ch == 'Q') break; + + sleep(1); + } + + curl_global_cleanup(); + endwin(); + return 0; +}