Another copy of my dotfiles. Because I don't completely trust GitHub.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

756 lines
22 KiB

4 years ago
  1. #pragma GCC diagnostic ignored "-Wunused-function"
  2. #include <unistd.h>
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <stdbool.h>
  6. #include <string.h>
  7. #include <dirent.h>
  8. #include <errno.h>
  9. #include <sys/utsname.h>
  10. #include <sys/sysinfo.h>
  11. #include <sys/statvfs.h>
  12. #include <pci/pci.h>
  13. #include <X11/Xlib.h>
  14. #include <X11/Xatom.h>
  15. #include "paleofetch.h"
  16. #include "config.h"
  17. #define BUF_SIZE 150
  18. #define COUNT(x) (int)(sizeof x / sizeof *x)
  19. #define halt_and_catch_fire(fmt, ...) \
  20. do { \
  21. if(status != 0) { \
  22. fprintf(stderr, "paleofetch: " fmt "\n", ##__VA_ARGS__); \
  23. exit(status); \
  24. } \
  25. } while(0)
  26. struct conf {
  27. char *label, *(*function)();
  28. bool cached;
  29. } config[] = CONFIG;
  30. struct {
  31. char *substring;
  32. char *repl_str;
  33. size_t length;
  34. size_t repl_len;
  35. } cpu_config[] = CPU_CONFIG, gpu_config[] = GPU_CONFIG;
  36. Display *display;
  37. struct statvfs file_stats;
  38. struct utsname uname_info;
  39. struct sysinfo my_sysinfo;
  40. int title_length, status;
  41. /*
  42. * Replaces the first newline character with null terminator
  43. */
  44. void remove_newline(char *s) {
  45. while (*s != '\0' && *s != '\n')
  46. s++;
  47. *s = '\0';
  48. }
  49. /*
  50. * Replaces the first newline character with null terminator
  51. * and returns the length of the string
  52. */
  53. int remove_newline_get_length(char *s) {
  54. int i;
  55. for (i = 0; *s != '\0' && *s != '\n'; s++, i++);
  56. *s = '\0';
  57. return i;
  58. }
  59. /*
  60. * Cleans up repeated spaces in a string
  61. * Trim spaces at the front of a string
  62. */
  63. void truncate_spaces(char *str) {
  64. int src = 0, dst = 0;
  65. while(*(str + dst) == ' ') dst++;
  66. while(*(str + dst) != '\0') {
  67. *(str + src) = *(str + dst);
  68. if(*(str + (dst++)) == ' ')
  69. while(*(str + dst) == ' ') dst++;
  70. src++;
  71. }
  72. *(str + src) = '\0';
  73. }
  74. /*
  75. * Removes the first len characters of substring from str
  76. * Assumes that strlen(substring) >= len
  77. * Returns index where substring was found, or -1 if substring isn't found
  78. */
  79. void remove_substring(char *str, const char* substring, size_t len) {
  80. /* shift over the rest of the string to remove substring */
  81. char *sub = strstr(str, substring);
  82. if(sub == NULL) return;
  83. int i = 0;
  84. do *(sub+i) = *(sub+i+len);
  85. while(*(sub+(++i)) != '\0');
  86. }
  87. /*
  88. * Replaces the first sub_len characters of sub_str from str
  89. * with the first repl_len characters of repl_str
  90. */
  91. void replace_substring(char *str, const char *sub_str, const char *repl_str, size_t sub_len, size_t repl_len) {
  92. char buffer[BUF_SIZE / 2];
  93. char *start = strstr(str, sub_str);
  94. if (start == NULL) return; // substring not found
  95. /* check if we have enough space for new substring */
  96. if (strlen(str) - sub_len + repl_len >= BUF_SIZE / 2) {
  97. status = -1;
  98. halt_and_catch_fire("new substring too long to replace");
  99. }
  100. strcpy(buffer, start + sub_len);
  101. strncpy(start, repl_str, repl_len);
  102. strcpy(start + repl_len, buffer);
  103. }
  104. static char *get_title() {
  105. // reduce the maximum size for these, so that we don't over-fill the title string
  106. char hostname[BUF_SIZE / 3];
  107. status = gethostname(hostname, BUF_SIZE / 3);
  108. halt_and_catch_fire("unable to retrieve host name");
  109. char username[BUF_SIZE / 3];
  110. status = getlogin_r(username, BUF_SIZE / 3);
  111. halt_and_catch_fire("unable to retrieve login name");
  112. title_length = strlen(hostname) + strlen(username) + 1;
  113. char *title = malloc(BUF_SIZE);
  114. snprintf(title, BUF_SIZE, COLOR"%s\e[0m@"COLOR"%s", username, hostname);
  115. return title;
  116. }
  117. static char *get_bar() {
  118. char *bar = malloc(BUF_SIZE);
  119. char *s = bar;
  120. for(int i = 0; i < title_length; i++) *(s++) = '-';
  121. *s = '\0';
  122. return bar;
  123. }
  124. static char *get_os() {
  125. char *os = malloc(BUF_SIZE),
  126. *name = malloc(BUF_SIZE),
  127. *line = NULL;
  128. size_t len;
  129. FILE *os_release = fopen("/etc/os-release", "r");
  130. if(os_release == NULL) {
  131. status = -1;
  132. halt_and_catch_fire("unable to open /etc/os-release");
  133. }
  134. while (getline(&line, &len, os_release) != -1) {
  135. if (sscanf(line, "NAME=\"%[^\"]+", name) > 0) break;
  136. }
  137. free(line);
  138. fclose(os_release);
  139. snprintf(os, BUF_SIZE, "%s %s", name, uname_info.machine);
  140. free(name);
  141. return os;
  142. }
  143. static char *get_kernel() {
  144. char *kernel = malloc(BUF_SIZE);
  145. strncpy(kernel, uname_info.release, BUF_SIZE);
  146. return kernel;
  147. }
  148. static char *get_host() {
  149. char *host = malloc(BUF_SIZE), buffer[BUF_SIZE/2];
  150. FILE *product_name, *product_version, *model;
  151. if((product_name = fopen("/sys/devices/virtual/dmi/id/product_name", "r")) != NULL) {
  152. if((product_version = fopen("/sys/devices/virtual/dmi/id/product_version", "r")) != NULL) {
  153. fread(host, 1, BUF_SIZE/2, product_name);
  154. remove_newline(host);
  155. strcat(host, " ");
  156. fread(buffer, 1, BUF_SIZE/2, product_version);
  157. remove_newline(buffer);
  158. strcat(host, buffer);
  159. fclose(product_version);
  160. } else {
  161. fclose(product_name);
  162. goto model_fallback;
  163. }
  164. fclose(product_name);
  165. return host;
  166. }
  167. model_fallback:
  168. if((model = fopen("/sys/firmware/devicetree/base/model", "r")) != NULL) {
  169. fread(host, 1, BUF_SIZE, model);
  170. remove_newline(host);
  171. return host;
  172. }
  173. status = -1;
  174. halt_and_catch_fire("unable to get host");
  175. return NULL;
  176. }
  177. static char *get_uptime() {
  178. long seconds = my_sysinfo.uptime;
  179. struct { char *name; int secs; } units[] = {
  180. { "day", 60 * 60 * 24 },
  181. { "hour", 60 * 60 },
  182. { "min", 60 },
  183. };
  184. int n, len = 0;
  185. char *uptime = malloc(BUF_SIZE);
  186. for (int i = 0; i < 3; ++i ) {
  187. if ((n = seconds / units[i].secs) || i == 2) /* always print minutes */
  188. len += snprintf(uptime + len, BUF_SIZE - len,
  189. "%d %s%s, ", n, units[i].name, n != 1 ? "s": "");
  190. seconds %= units[i].secs;
  191. }
  192. // null-terminate at the trailing comma
  193. uptime[len - 2] = '\0';
  194. return uptime;
  195. }
  196. // returns "<Battery Percentage>% [<Charging | Discharging | Unknown>]"
  197. // Credit: allisio - https://gist.github.com/allisio/1e850b93c81150124c2634716fbc4815
  198. static char *get_battery_percentage() {
  199. int battery_capacity;
  200. FILE *capacity_file, *status_file;
  201. char battery_status[12] = "Unknown";
  202. if ((capacity_file = fopen(BATTERY_DIRECTORY "/capacity", "r")) == NULL) {
  203. status = ENOENT;
  204. halt_and_catch_fire("Unable to get battery information");
  205. }
  206. fscanf(capacity_file, "%d", &battery_capacity);
  207. fclose(capacity_file);
  208. if ((status_file = fopen(BATTERY_DIRECTORY "/status", "r")) != NULL) {
  209. fscanf(status_file, "%s", battery_status);
  210. fclose(status_file);
  211. }
  212. // max length of resulting string is 19
  213. // one byte for padding incase there is a newline
  214. // 100% [Discharging]
  215. // 1234567890123456789
  216. char *battery = malloc(20);
  217. snprintf(battery, 20, "%d%% [%s]", battery_capacity, battery_status);
  218. return battery;
  219. }
  220. static char *get_packages(const char* dirname, const char* pacname, int num_extraneous) {
  221. int num_packages = 0;
  222. DIR * dirp;
  223. struct dirent *entry;
  224. dirp = opendir(dirname);
  225. if(dirp == NULL) {
  226. status = -1;
  227. halt_and_catch_fire("You may not have %s installed", dirname);
  228. }
  229. while((entry = readdir(dirp)) != NULL) {
  230. if(entry->d_type == DT_DIR) num_packages++;
  231. }
  232. num_packages -= (2 + num_extraneous); // accounting for . and ..
  233. status = closedir(dirp);
  234. char *packages = malloc(BUF_SIZE);
  235. snprintf(packages, BUF_SIZE, "%d (%s)", num_packages, pacname);
  236. return packages;
  237. }
  238. static char *get_packages_pacman() {
  239. return get_packages("/var/lib/pacman/local", "pacman", 0);
  240. }
  241. static char *get_shell() {
  242. char *shell = malloc(BUF_SIZE);
  243. char *shell_path = getenv("SHELL");
  244. char *shell_name = strrchr(getenv("SHELL"), '/');
  245. if(shell_name == NULL) /* if $SHELL doesn't have a '/' */
  246. strncpy(shell, shell_path, BUF_SIZE); /* copy the whole thing over */
  247. else
  248. strncpy(shell, shell_name + 1, BUF_SIZE); /* o/w copy past the last '/' */
  249. return shell;
  250. }
  251. static char *get_resolution() {
  252. int screen, width, height;
  253. char *resolution = malloc(BUF_SIZE);
  254. if (display != NULL) {
  255. screen = DefaultScreen(display);
  256. width = DisplayWidth(display, screen);
  257. height = DisplayHeight(display, screen);
  258. snprintf(resolution, BUF_SIZE, "%dx%d", width, height);
  259. } else {
  260. DIR *dir;
  261. struct dirent *entry;
  262. char dir_name[] = "/sys/class/drm";
  263. char modes_file_name[BUF_SIZE * 2];
  264. FILE *modes;
  265. char *line = NULL;
  266. size_t len;
  267. /* preload resolution with empty string, in case we cant find a resolution through parsing */
  268. strncpy(resolution, "", BUF_SIZE);
  269. dir = opendir(dir_name);
  270. if (dir == NULL) {
  271. status = -1;
  272. halt_and_catch_fire("Could not open /sys/class/drm to determine resolution in tty mode.");
  273. }
  274. /* parse through all directories and look for a non empty modes file */
  275. while ((entry = readdir(dir)) != NULL) {
  276. if (entry->d_type == DT_LNK) {
  277. snprintf(modes_file_name, BUF_SIZE * 2, "%s/%s/modes", dir_name, entry->d_name);
  278. modes = fopen(modes_file_name, "r");
  279. if (modes != NULL) {
  280. if (getline(&line, &len, modes) != -1) {
  281. strncpy(resolution, line, BUF_SIZE);
  282. remove_newline(resolution);
  283. free(line);
  284. fclose(modes);
  285. break;
  286. }
  287. fclose(modes);
  288. }
  289. }
  290. }
  291. closedir(dir);
  292. }
  293. return resolution;
  294. }
  295. static char *get_terminal() {
  296. unsigned char *prop;
  297. char *terminal = malloc(BUF_SIZE);
  298. /* check if xserver is running or if we are running in a straight tty */
  299. if (display != NULL) {
  300. unsigned long _, // not unused, but we don't need the results
  301. window = RootWindow(display, XDefaultScreen(display));
  302. Atom a,
  303. active = XInternAtom(display, "_NET_ACTIVE_WINDOW", True),
  304. class = XInternAtom(display, "WM_CLASS", True);
  305. #define GetProp(property) \
  306. XGetWindowProperty(display, window, property, 0, 64, 0, 0, &a, (int *)&_, &_, &_, &prop);
  307. GetProp(active);
  308. window = (prop[3] << 24) + (prop[2] << 16) + (prop[1] << 8) + prop[0];
  309. free(prop);
  310. if(!window) goto terminal_fallback;
  311. GetProp(class);
  312. #undef GetProp
  313. snprintf(terminal, BUF_SIZE, "%s", prop);
  314. free(prop);
  315. } else {
  316. terminal_fallback:
  317. strncpy(terminal, getenv("TERM"), BUF_SIZE); /* fallback to old method */
  318. /* in tty, $TERM is simply returned as "linux"; in this case get actual tty name */
  319. if (strcmp(terminal, "linux") == 0) {
  320. strncpy(terminal, ttyname(STDIN_FILENO), BUF_SIZE);
  321. }
  322. }
  323. return terminal;
  324. }
  325. static char *get_cpu() {
  326. FILE *cpuinfo = fopen("/proc/cpuinfo", "r"); /* read from cpu info */
  327. if(cpuinfo == NULL) {
  328. status = -1;
  329. halt_and_catch_fire("Unable to open cpuinfo");
  330. }
  331. char *cpu_model = malloc(BUF_SIZE / 2);
  332. char *line = NULL;
  333. size_t len; /* unused */
  334. int num_cores = 0, cpu_freq, prec = 3;
  335. double freq;
  336. char freq_unit[] = "GHz";
  337. /* read the model name into cpu_model, and increment num_cores every time model name is found */
  338. while(getline(&line, &len, cpuinfo) != -1) {
  339. num_cores += sscanf(line, "model name : %[^\n@]", cpu_model);
  340. }
  341. free(line);
  342. fclose(cpuinfo);
  343. FILE *cpufreq = fopen("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq", "r");
  344. line = NULL;
  345. if (cpufreq != NULL) {
  346. if (getline(&line, &len, cpufreq) != -1) {
  347. sscanf(line, "%d", &cpu_freq);
  348. cpu_freq /= 1000; // convert kHz to MHz
  349. } else {
  350. fclose(cpufreq);
  351. free(line);
  352. goto cpufreq_fallback;
  353. }
  354. } else {
  355. cpufreq_fallback:
  356. cpufreq = fopen("/proc/cpuinfo", "r"); /* read from cpu info */
  357. if (cpufreq == NULL) {
  358. status = -1;
  359. halt_and_catch_fire("Unable to open cpuinfo");
  360. }
  361. while (getline(&line, &len, cpufreq) != -1) {
  362. if (sscanf(line, "cpu MHz : %lf", &freq) > 0) break;
  363. }
  364. cpu_freq = (int) freq;
  365. }
  366. free(line);
  367. fclose(cpufreq);
  368. if (cpu_freq < 1000) {
  369. freq = (double) cpu_freq;
  370. freq_unit[0] = 'M'; // make MHz from GHz
  371. prec = 0; // show frequency as integer value
  372. } else {
  373. freq = cpu_freq / 1000.0; // convert MHz to GHz and cast to double
  374. while (cpu_freq % 10 == 0) {
  375. --prec;
  376. cpu_freq /= 10;
  377. }
  378. if (prec == 0) prec = 1; // we don't want zero decimal places
  379. }
  380. /* remove unneeded information */
  381. for (int i = 0; i < COUNT(cpu_config); ++i) {
  382. if (cpu_config[i].repl_str == NULL) {
  383. remove_substring(cpu_model, cpu_config[i].substring, cpu_config[i].length);
  384. } else {
  385. replace_substring(cpu_model, cpu_config[i].substring, cpu_config[i].repl_str, cpu_config[i].length, cpu_config[i].repl_len);
  386. }
  387. }
  388. char *cpu = malloc(BUF_SIZE);
  389. snprintf(cpu, BUF_SIZE, "%s (%d) @ %.*f%s", cpu_model, num_cores, prec, freq, freq_unit);
  390. free(cpu_model);
  391. truncate_spaces(cpu);
  392. if(num_cores == 0)
  393. *cpu = '\0';
  394. return cpu;
  395. }
  396. static char *find_gpu(int index) {
  397. // inspired by https://github.com/pciutils/pciutils/edit/master/example.c
  398. /* it seems that pci_lookup_name needs to be given a buffer, but I can't for the life of my figure out what its for */
  399. char buffer[BUF_SIZE], *device_class, *gpu = malloc(BUF_SIZE);
  400. struct pci_access *pacc;
  401. struct pci_dev *dev;
  402. int gpu_index = 0;
  403. bool found = false;
  404. pacc = pci_alloc();
  405. pci_init(pacc);
  406. pci_scan_bus(pacc);
  407. dev = pacc->devices;
  408. while(dev != NULL) {
  409. pci_fill_info(dev, PCI_FILL_IDENT);
  410. device_class = pci_lookup_name(pacc, buffer, sizeof(buffer), PCI_LOOKUP_CLASS, dev->device_class);
  411. if(strcmp("VGA compatible controller", device_class) == 0 || strcmp("3D controller", device_class) == 0) {
  412. strncpy(gpu, pci_lookup_name(pacc, buffer, sizeof(buffer), PCI_LOOKUP_DEVICE | PCI_LOOKUP_VENDOR, dev->vendor_id, dev->device_id), BUF_SIZE);
  413. if(gpu_index == index) {
  414. found = true;
  415. break;
  416. } else {
  417. gpu_index++;
  418. }
  419. }
  420. dev = dev->next;
  421. }
  422. if (found == false) *gpu = '\0'; // empty string, so it will not be printed
  423. pci_cleanup(pacc);
  424. /* remove unneeded information */
  425. for (int i = 0; i < COUNT(gpu_config); ++i) {
  426. if (gpu_config[i].repl_str == NULL) {
  427. remove_substring(gpu, gpu_config[i].substring, gpu_config[i].length);
  428. } else {
  429. replace_substring(gpu, gpu_config[i].substring, gpu_config[i].repl_str, gpu_config[i].length, gpu_config[i].repl_len);
  430. }
  431. }
  432. truncate_spaces(gpu);
  433. return gpu;
  434. }
  435. static char *get_gpu1() {
  436. return find_gpu(0);
  437. }
  438. static char *get_gpu2() {
  439. return find_gpu(1);
  440. }
  441. static char *get_memory() {
  442. int total_memory, used_memory;
  443. int total, shared, memfree, buffers, cached, reclaimable;
  444. FILE *meminfo = fopen("/proc/meminfo", "r"); /* get infomation from meminfo */
  445. if(meminfo == NULL) {
  446. status = -1;
  447. halt_and_catch_fire("Unable to open meminfo");
  448. }
  449. /* We parse through all lines of meminfo and scan for the information we need */
  450. char *line = NULL; // allocation handled automatically by getline()
  451. size_t len; /* unused */
  452. /* parse until EOF */
  453. while (getline(&line, &len, meminfo) != -1) {
  454. /* if sscanf doesn't find a match, pointer is untouched */
  455. sscanf(line, "MemTotal: %d", &total);
  456. sscanf(line, "Shmem: %d", &shared);
  457. sscanf(line, "MemFree: %d", &memfree);
  458. sscanf(line, "Buffers: %d", &buffers);
  459. sscanf(line, "Cached: %d", &cached);
  460. sscanf(line, "SReclaimable: %d", &reclaimable);
  461. }
  462. free(line);
  463. fclose(meminfo);
  464. /* use same calculation as neofetch */
  465. used_memory = (total + shared - memfree - buffers - cached - reclaimable) / 1024;
  466. total_memory = total / 1024;
  467. int percentage = (int) (100 * (used_memory / (double) total_memory));
  468. char *memory = malloc(BUF_SIZE);
  469. snprintf(memory, BUF_SIZE, "%dMiB / %dMiB (%d%%)", used_memory, total_memory, percentage);
  470. return memory;
  471. }
  472. static char *get_disk_usage(const char *folder) {
  473. char *disk_usage = malloc(BUF_SIZE);
  474. long total, used, free;
  475. int percentage;
  476. status = statvfs(folder, &file_stats);
  477. halt_and_catch_fire("Error getting disk usage for %s", folder);
  478. total = file_stats.f_blocks * file_stats.f_frsize;
  479. free = file_stats.f_bfree * file_stats.f_frsize;
  480. used = total - free;
  481. percentage = (used / (double) total) * 100;
  482. #define TO_GB(A) ((A) / (1024.0 * 1024 * 1024))
  483. snprintf(disk_usage, BUF_SIZE, "%.1fGiB / %.1fGiB (%d%%)", TO_GB(used), TO_GB(total), percentage);
  484. #undef TO_GB
  485. return disk_usage;
  486. }
  487. static char *get_disk_usage_root() {
  488. return get_disk_usage("/");
  489. }
  490. static char *get_disk_usage_home() {
  491. return get_disk_usage("/home");
  492. }
  493. static char *get_colors1() {
  494. char *colors1 = malloc(BUF_SIZE);
  495. char *s = colors1;
  496. for(int i = 0; i < 8; i++) {
  497. sprintf(s, "\e[4%dm ", i);
  498. s += 8;
  499. }
  500. snprintf(s, 5, "\e[0m");
  501. return colors1;
  502. }
  503. static char *get_colors2() {
  504. char *colors2 = malloc(BUF_SIZE);
  505. char *s = colors2;
  506. for(int i = 8; i < 16; i++) {
  507. sprintf(s, "\e[48;5;%dm ", i);
  508. s += 12 + (i >= 10 ? 1 : 0);
  509. }
  510. snprintf(s, 5, "\e[0m");
  511. return colors2;
  512. }
  513. static char *spacer() {
  514. return calloc(1, 1); // freeable, null-terminated string of length 1
  515. }
  516. char *get_cache_file() {
  517. char *cache_file = malloc(BUF_SIZE);
  518. char *env = getenv("XDG_CACHE_HOME");
  519. if(env == NULL)
  520. snprintf(cache_file, BUF_SIZE, "%s/.cache/paleofetch", getenv("HOME"));
  521. else
  522. snprintf(cache_file, BUF_SIZE, "%s/paleofetch", env);
  523. return cache_file;
  524. }
  525. /* This isn't especially robust, but as long as we're the only one writing
  526. * to our cache file, the format is simple, effective, and fast. One way
  527. * we might get in trouble would be if the user decided not to have any
  528. * sort of sigil (like ':') after their labels. */
  529. char *search_cache(char *cache_data, char *label) {
  530. char *start = strstr(cache_data, label);
  531. if(start == NULL) {
  532. status = ENODATA;
  533. halt_and_catch_fire("cache miss on key '%s'; need to --recache?", label);
  534. }
  535. start += strlen(label);
  536. char *end = strchr(start, ';');
  537. char *buf = calloc(1, BUF_SIZE);
  538. // skip past the '=' and stop just before the ';'
  539. strncpy(buf, start + 1, end - start - 1);
  540. return buf;
  541. }
  542. char *get_value(struct conf c, int read_cache, char *cache_data) {
  543. char *value;
  544. // If the user's config specifies that this value should be cached
  545. if(c.cached && read_cache) // and we have a cache to read from
  546. value = search_cache(cache_data, c.label); // grab it from the cache
  547. else {
  548. // Otherwise, call the associated function to get the value
  549. value = c.function();
  550. if(c.cached) { // and append it to our cache data if appropriate
  551. char *buf = malloc(BUF_SIZE);
  552. sprintf(buf, "%s=%s;", c.label, value);
  553. strcat(cache_data, buf);
  554. free(buf);
  555. }
  556. }
  557. return value;
  558. }
  559. int main(int argc, char *argv[]) {
  560. char *cache, *cache_data = NULL;
  561. FILE *cache_file;
  562. int read_cache;
  563. status = uname(&uname_info);
  564. halt_and_catch_fire("uname failed");
  565. status = sysinfo(&my_sysinfo);
  566. halt_and_catch_fire("sysinfo failed");
  567. display = XOpenDisplay(NULL);
  568. cache = get_cache_file();
  569. if(argc == 2 && strcmp(argv[1], "--recache") == 0)
  570. read_cache = 0;
  571. else {
  572. cache_file = fopen(cache, "r");
  573. read_cache = cache_file != NULL;
  574. }
  575. if(!read_cache)
  576. cache_data = calloc(4, BUF_SIZE); // should be enough
  577. else {
  578. size_t len; /* unused */
  579. getline(&cache_data, &len, cache_file);
  580. fclose(cache_file); // We just need the first (and only) line.
  581. }
  582. int offset = 0;
  583. for (int i = 0; i < COUNT(LOGO); i++) {
  584. // If we've run out of information to show...
  585. if(i >= COUNT(config) - offset) // just print the next line of the logo
  586. printf(COLOR"%s\n", LOGO[i]);
  587. else {
  588. // Otherwise, we've got a bit of work to do.
  589. char *label = config[i+offset].label,
  590. *value = get_value(config[i+offset], read_cache, cache_data);
  591. if (strcmp(value, "") != 0) { // check if value is an empty string
  592. printf(COLOR"%s%s\e[0m%s\n", LOGO[i], label, value); // just print if not empty
  593. } else {
  594. if (strcmp(label, "") != 0) { // check if label is empty, otherwise it's a spacer
  595. ++offset; // print next line of information
  596. free(value); // free memory allocated for empty value
  597. label = config[i+offset].label; // read new label and value
  598. value = get_value(config[i+offset], read_cache, cache_data);
  599. }
  600. printf(COLOR"%s%s\e[0m%s\n", LOGO[i], label, value);
  601. }
  602. free(value);
  603. }
  604. }
  605. puts("\e[0m");
  606. /* Write out our cache data (if we have any). */
  607. if(!read_cache && *cache_data) {
  608. cache_file = fopen(cache, "w");
  609. fprintf(cache_file, "%s", cache_data);
  610. fclose(cache_file);
  611. }
  612. free(cache);
  613. free(cache_data);
  614. if(display != NULL) {
  615. XCloseDisplay(display);
  616. }
  617. return 0;
  618. }