/* Copyright (C) 2025 awy stweath is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. stweath is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with stweath. If not, see . */ #include #include #include #include #include #include #include #include "cjson/cJSON.h" int getforecast(char *path) { FILE *report; CURL* handle; CURLcode res; report = fopen(path, "w"); if (!report) { return 1; } handle = curl_easy_init(); if (!handle) { fclose(report); return 1; } curl_easy_setopt(handle, CURLOPT_URL, "https://wttr.in/?format=j1"); curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, NULL); curl_easy_setopt(handle, CURLOPT_WRITEDATA, report); res = curl_easy_perform(handle); if (res != CURLE_OK) { fclose(report); return 1; } curl_easy_cleanup(handle); fclose(report); return 0; } int showforecast(char *path) { long size; char *data; char *icon; cJSON *root, *temp, *wcode, *rain; FILE *report; report = fopen(path, "rb"); if (!report) { return 1; } fseek(report, 0, SEEK_END); size = ftell(report); fseek(report, 0, SEEK_SET); data = malloc(size + 1); if (!data) { perror("malloc"); fclose(report); return 1; } fread(data, 1, size, report); data[size] = '\0'; fclose(report); root = cJSON_Parse(data); free(data); if (!root) { return 1; } rain = cJSON_GetArrayItem(cJSON_GetObjectItem(root, "weather"), 0); rain = cJSON_GetArrayItem(cJSON_GetObjectItem(rain, "hourly"), 0); rain = cJSON_GetObjectItem(rain, "chanceofrain"); temp = cJSON_GetObjectItem(root, "current_condition"); temp = cJSON_GetArrayItem(temp, 0); wcode = cJSON_GetObjectItem(temp, "weatherCode"); temp = cJSON_GetObjectItem(temp, "temp_C"); switch (atoi(wcode->valuestring)) { case 113: icon = ""; break; case 116: icon = ""; break; case 119: case 122: case 143: case 248: case 260: icon = ""; break; case 176: case 179: case 182: case 185: case 263: case 266: case 281: case 284: case 293: case 296: case 299: case 302: case 305: case 308: case 311: case 314: case 317: case 350: case 353: case 356: case 359: case 362: case 365: case 368: case 392: icon = ""; break; case 200: icon = ""; break; case 227: case 230: case 320: case 323: case 326: case 374: case 377: case 386: case 389: icon = ""; break; case 329: case 332: case 335: case 338: case 371: case 395: icon = ""; break; default: icon = "❓"; break; } if (atoi(rain->valuestring) != 0) { printf(" %s%% ", rain->valuestring); } printf("%s %s°C", icon, temp->valuestring); cJSON_Delete(root); return 0; } int checkforecast(char *path) { time_t now; char *buff; struct stat st; int tries, force; /* Check if there is WEATHERFORCEUPDATE environment variable */ force = 0; buff = getenv("WEATHERFORCEUPDATE"); if (buff && buff[0] == '1') { force = 1; } if (stat(path, &st) != 0) { return 1; } now = time(NULL); if (now == ((time_t)-1)) { return 1; } /* Check if 30 mins passed then get new forecast */ if (((now - st.st_mtime) >= 1800) || force) { tries = 0; /* Try 100 fast retries */ while (tries < 100) { if(getforecast(path) == 0) { break; } tries++; sleep(1); } } showforecast(path); return 0; } int main(void) { char *path; path = getenv("XDG_CACHE_HOME"); if (!path) { puts("XDG_CACHE_HOME is not set"); return 1; } strcat(path, "/weatherreport.json"); curl_global_init(CURL_GLOBAL_ALL); checkforecast(path); curl_global_cleanup(); return 0; }