diff --git a/README.md b/README.md index bd168a9..7eb4904 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,2 @@ -# cclock -#### Simply C program to display the time and temperature without too much fuss, made for home use. - -Current the only dependencies are: -``` -gcc -libncurses -libcurl -``` - -Build with ```gcc -Wall -Wextra -pedantic -std=c11 cclock.c -lncurses -lcurl -o cclock``` - -To effectively use this program, there is a toml config you must provide -$HOME/.conifg/cclock/config -``` -[weather] -api_key = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" -location = "Portland,US" -units = "imperial" # metric is another option -``` +# cclock diff --git a/cclock b/cclock deleted file mode 100755 index 1cf1aaa..0000000 Binary files a/cclock and /dev/null differ diff --git a/cclock.bkp.c b/cclock.bkp.c deleted file mode 100644 index 192f620..0000000 --- a/cclock.bkp.c +++ /dev/null @@ -1,319 +0,0 @@ -#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) { -bool twelve_hour = false; -bool human_date = false; -bool quiet = false; -bool hide_temp = false; -bool force_c = false; -bool force_f = false; - -/* Parse CLI flags FIRST */ - -for (int i = 1; i < argc; i++) { - if (strcmp(argv[i], "--help") == 0) { - goto show_help; - } - if (argv[i][0] != '-') continue; - - for (int j = 1; argv[i][j]; j++) { - switch (argv[i][j]) { - case 't': twelve_hour = true; break; - case 'H': human_date = true; break; - case 'q': quiet = true; break; - case 'T': hide_temp = true; break; - case 'C': force_c = true; break; - case 'F': force_f = true; break; - - case 'h': - goto show_help; - - show_help: - - printf("Usage: %s [-HqTtCF]\n\n", argv[0]); - printf("Options:\n"); - printf(" -t Use 12-hour time format\n"); - printf(" -H Human-readable date (\"Tuesday 16, Feb\")\n"); - printf(" -q Hide \"Press q to quit\" footer\n"); - printf(" -T Hide temperature display\n"); - printf(" -C Force Celsius\n"); - printf(" -F Force Fahrenheit\n"); - printf(" -h,--help Show this help\n"); - return 0; - - default: - printf("Unknown flag: -%c\n", argv[i][j]); - return 1; - } - } -} - -/* NOW load config */ - -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)) { - hide_temp = true; - printf("Failed to load config from %s\n", config_path); -} -/* WORK IN PROGRESS CUSTOM_CONFIG -else if (custom_config = true) { - snprintf(config_path, sizeof(config_path), "%s/.config/cclock/config", getenv("HOME")); -}*/ - - -/* NOW apply overrides */ - -if (force_c) { - strncpy(cfg.units, "metric", sizeof(cfg.units) - 1); - cfg.units[sizeof(cfg.units) - 1] = '\0'; -} -else if (force_f) { - strncpy(cfg.units, "imperial", sizeof(cfg.units) - 1); - cfg.units[sizeof(cfg.units) - 1] = '\0'; -} - -for (int i = 1; i < argc; i++) { - if (argv[i][0] != '-') continue; - - for (int j = 1; argv[i][j]; j++) { - switch (argv[i][j]) { - case 't': - twelve_hour = true; - break; - - case 'T': - hide_temp = true; - break; - - case 'C': - force_c = true; - break; - - case 'F': - force_f = true; - break; - - case 'H': - human_date = true; - break; - - case 'q': - quiet = true; - break; - - /*case 'c': - goto custom_config;*/ - - case 'h': - goto show_help; - - default: - printf("Unknown flag: -%c\n", argv[i][j]); - return 1; - } - } -} - - 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[20], 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); - - const char *date_fmt = human_date ? "%A %d, %b" : "%m/%d/%Y"; - strftime(date_buf, sizeof(date_buf), date_fmt, 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 -1, time_x, "%s", time_buf); - attroff(A_BOLD); - - int date_x = (cols - strlen(date_buf)) / 2; - mvprintw(y_mid, date_x, "%s", date_buf); - - if (!hide_temp) { - int temp_x = (cols - strlen(temp_buf)) / 2; - mvprintw(y_mid + 1, temp_x, "%s", temp_buf); - } - - if (!quiet) - 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; -} diff --git a/cclock.c b/cclock.c deleted file mode 100644 index 192f620..0000000 --- a/cclock.c +++ /dev/null @@ -1,319 +0,0 @@ -#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) { -bool twelve_hour = false; -bool human_date = false; -bool quiet = false; -bool hide_temp = false; -bool force_c = false; -bool force_f = false; - -/* Parse CLI flags FIRST */ - -for (int i = 1; i < argc; i++) { - if (strcmp(argv[i], "--help") == 0) { - goto show_help; - } - if (argv[i][0] != '-') continue; - - for (int j = 1; argv[i][j]; j++) { - switch (argv[i][j]) { - case 't': twelve_hour = true; break; - case 'H': human_date = true; break; - case 'q': quiet = true; break; - case 'T': hide_temp = true; break; - case 'C': force_c = true; break; - case 'F': force_f = true; break; - - case 'h': - goto show_help; - - show_help: - - printf("Usage: %s [-HqTtCF]\n\n", argv[0]); - printf("Options:\n"); - printf(" -t Use 12-hour time format\n"); - printf(" -H Human-readable date (\"Tuesday 16, Feb\")\n"); - printf(" -q Hide \"Press q to quit\" footer\n"); - printf(" -T Hide temperature display\n"); - printf(" -C Force Celsius\n"); - printf(" -F Force Fahrenheit\n"); - printf(" -h,--help Show this help\n"); - return 0; - - default: - printf("Unknown flag: -%c\n", argv[i][j]); - return 1; - } - } -} - -/* NOW load config */ - -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)) { - hide_temp = true; - printf("Failed to load config from %s\n", config_path); -} -/* WORK IN PROGRESS CUSTOM_CONFIG -else if (custom_config = true) { - snprintf(config_path, sizeof(config_path), "%s/.config/cclock/config", getenv("HOME")); -}*/ - - -/* NOW apply overrides */ - -if (force_c) { - strncpy(cfg.units, "metric", sizeof(cfg.units) - 1); - cfg.units[sizeof(cfg.units) - 1] = '\0'; -} -else if (force_f) { - strncpy(cfg.units, "imperial", sizeof(cfg.units) - 1); - cfg.units[sizeof(cfg.units) - 1] = '\0'; -} - -for (int i = 1; i < argc; i++) { - if (argv[i][0] != '-') continue; - - for (int j = 1; argv[i][j]; j++) { - switch (argv[i][j]) { - case 't': - twelve_hour = true; - break; - - case 'T': - hide_temp = true; - break; - - case 'C': - force_c = true; - break; - - case 'F': - force_f = true; - break; - - case 'H': - human_date = true; - break; - - case 'q': - quiet = true; - break; - - /*case 'c': - goto custom_config;*/ - - case 'h': - goto show_help; - - default: - printf("Unknown flag: -%c\n", argv[i][j]); - return 1; - } - } -} - - 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[20], 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); - - const char *date_fmt = human_date ? "%A %d, %b" : "%m/%d/%Y"; - strftime(date_buf, sizeof(date_buf), date_fmt, 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 -1, time_x, "%s", time_buf); - attroff(A_BOLD); - - int date_x = (cols - strlen(date_buf)) / 2; - mvprintw(y_mid, date_x, "%s", date_buf); - - if (!hide_temp) { - int temp_x = (cols - strlen(temp_buf)) / 2; - mvprintw(y_mid + 1, temp_x, "%s", temp_buf); - } - - if (!quiet) - 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; -} diff --git a/error b/error deleted file mode 100644 index c70d5e6..0000000 --- a/error +++ /dev/null @@ -1 +0,0 @@ -cannot evaluate a function that has an argument without a value ('lib') Nix attempted to evaluate a function as a top level expression; in this case it must have its arguments supplied either by default values, or passed explicitly with '--arg' or '--argstr'. diff --git a/toml-parse b/toml-parse deleted file mode 100755 index 09b80ac..0000000 Binary files a/toml-parse and /dev/null differ diff --git a/toml-parse.c b/toml-parse.c deleted file mode 100644 index 3408764..0000000 --- a/toml-parse.c +++ /dev/null @@ -1,26 +0,0 @@ -#include -#include - -typedef struct { - char api_key[128]; - char location[64]; - char units[16]; -} config_t; - -static bool load_config_toml(config_t *cfg, const char *path) { - -int main() { - 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); - } - - else if (load_config_toml(&cfg, config_path)) { - printf("Loaded config from %s\n", config_path); - } - - return 0; -}