]> git.sur5r.net Git - i3/i3status/commitdiff
Added function to print content from file (#331) master
authorDennis Vesterlund <dennisvesterlund@gmail.com>
Thu, 21 Feb 2019 14:34:32 +0000 (15:34 +0100)
committerMichael Stapelberg <stapelberg@users.noreply.github.com>
Thu, 21 Feb 2019 14:34:32 +0000 (15:34 +0100)
Added a function to print file contents to status bar without newlines.
Added tests for print file contents function
Added manpage entry for file contents

Makefile.am
i3status.c
include/i3status.h
man/i3status.man
src/print_file_contents.c [new file with mode: 0644]
testcases/025-file-contents/expected_output.txt [new file with mode: 0644]
testcases/025-file-contents/i3status.conf [new file with mode: 0644]
testcases/025-file-contents/short.txt [new file with mode: 0644]

index 7d8b5a9dee64b1355be2f622897edc5d27d9d395..bb251f0498503fc10e990b4214ad76af58e356d5 100644 (file)
@@ -67,6 +67,7 @@ i3status_SOURCES = \
        src/print_time.c \
        src/print_volume.c \
        src/print_wireless_info.c \
+       src/print_file_contents.c \
        src/process_runs.c \
        src/pulse.c
 
index 90660bbc0e48add8f022a2b3e9f7b8686f34e12b..0898da312eee08656cd782fea766383cd6b2b256 100644 (file)
@@ -491,6 +491,18 @@ int main(int argc, char *argv[]) {
         CFG_CUSTOM_SEP_BLOCK_WIDTH_OPT,
         CFG_END()};
 
+    cfg_opt_t read_opts[] = {
+        CFG_STR("format", "%content", CFGF_NONE),
+        CFG_STR("format_bad", "%title - %errno: %error", CFGF_NONE),
+        CFG_STR("path", NULL, CFGF_NONE),
+        CFG_INT("max_characters", 255, CFGF_NONE),
+        CFG_CUSTOM_ALIGN_OPT,
+        CFG_CUSTOM_COLOR_OPTS,
+        CFG_CUSTOM_MIN_WIDTH_OPT,
+        CFG_CUSTOM_SEPARATOR_OPT,
+        CFG_CUSTOM_SEP_BLOCK_WIDTH_OPT,
+        CFG_END()};
+
     cfg_opt_t opts[] = {
         CFG_STR_LIST("order", "{}", CFGF_NONE),
         CFG_SEC("general", general_opts, CFGF_NONE),
@@ -509,6 +521,7 @@ int main(int argc, char *argv[]) {
         CFG_SEC("load", load_opts, CFGF_NONE),
         CFG_SEC("memory", memory_opts, CFGF_NONE),
         CFG_SEC("cpu_usage", usage_opts, CFGF_NONE),
+        CFG_SEC("read_file", read_opts, CFGF_TITLE | CFGF_MULTI),
         CFG_END()};
 
     char *configfile = NULL;
@@ -787,6 +800,12 @@ int main(int argc, char *argv[]) {
                 print_cpu_usage(json_gen, buffer, cfg_getstr(sec, "format"), cfg_getstr(sec, "format_above_threshold"), cfg_getstr(sec, "format_above_degraded_threshold"), cfg_getstr(sec, "path"), cfg_getfloat(sec, "max_threshold"), cfg_getfloat(sec, "degraded_threshold"));
                 SEC_CLOSE_MAP;
             }
+
+            CASE_SEC_TITLE("read_file") {
+                SEC_OPEN_MAP("read_file");
+                print_file_contents(json_gen, buffer, title, cfg_getstr(sec, "path"), cfg_getstr(sec, "format"), cfg_getstr(sec, "format_bad"), cfg_getint(sec, "max_characters"));
+                SEC_CLOSE_MAP;
+            }
         }
         if (output_format == O_I3BAR) {
             yajl_gen_array_close(json_gen);
index f3a894126c649cd73f2bc137f05f7cdf59f71531..217376a1a6d1667b19085c4972a80ed9ad0d0dbc 100644 (file)
@@ -231,6 +231,7 @@ bool process_runs(const char *path);
 int volume_pulseaudio(uint32_t sink_idx, const char *sink_name);
 bool description_pulseaudio(uint32_t sink_idx, const char *sink_name, char buffer[MAX_SINK_DESCRIPTION_LEN]);
 bool pulse_initialize(void);
+void print_file_contents(yajl_gen json_gen, char *buffer, const char *title, const char *path, const char *format, const char *format_bad, const int max_chars);
 
 /* socket file descriptor for general purposes */
 extern int general_socket;
index 4bc7d0aecb5817778838483bf6fc98356445c2ae..70ba71a90938bca805833ee70be14dd90a1ad320 100644 (file)
@@ -123,6 +123,11 @@ memory {
 disk "/" {
        format = "%free"
 }
+
+read_file uptime {
+       path = "/proc/uptime"
+}
+
 -------------------------------------------------------------
 
 === General
@@ -611,6 +616,25 @@ volume master {
 }
 -------------------------------------------------------------
 
+=== File Contents
+
+Outputs the contents of the specified file. You can use this to check contents
+of files on your system, for example /proc/uptime. By default the function only
+reads the first 254 characters of the file, if you want to override this set 
+the Max_characters option. It will never read beyond the first 4095 characters.
+If the file is not found "no file" will be printed, if the file can't be read
+"error read" will be printed.
+
+*Example order*: read_file UPTIME
+
+*Example format*: "%title: %content"
+
+*Example format_bad*: "%title - %errno: %error"
+
+*Example path*: "/proc/uptime"
+
+*Example Max_characters*: 255
+
 == Universal module options
 
 When using the i3bar output format, there are a few additional options that
diff --git a/src/print_file_contents.c b/src/print_file_contents.c
new file mode 100644 (file)
index 0000000..afbe3e3
--- /dev/null
@@ -0,0 +1,73 @@
+// vim:ts=4:sw=4:expandtab
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <yajl/yajl_gen.h>
+#include <yajl/yajl_version.h>
+#include <sys/types.h>
+
+#include <sys/fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include "i3status.h"
+
+static void *scalloc(size_t size) {
+    void *result = calloc(size, 1);
+    if (result == NULL) {
+        die("Error: out of memory (calloc(%zu))\n", size);
+    }
+    return result;
+}
+
+void print_file_contents(yajl_gen json_gen, char *buffer, const char *title, const char *path, const char *format, const char *format_bad, const int max_chars) {
+    const char *walk = format;
+    char *outwalk = buffer;
+    char *buf = scalloc(max_chars * sizeof(char));
+
+    int n = -1;
+    int fd = open(path, O_RDONLY);
+
+    INSTANCE(path);
+
+    if (fd > -1) {
+        n = read(fd, buf, max_chars);
+        if (n != -1) {
+            buf[n] = '\0';
+        }
+        (void)close(fd);
+        START_COLOR("color_good");
+    } else if (errno != 0) {
+        walk = format_bad;
+        START_COLOR("color_bad");
+    }
+
+    for (; *walk != '\0'; walk++) {
+        if (*walk != '%') {
+            *(outwalk++) = *walk;
+        } else if (BEGINS_WITH(walk + 1, "title")) {
+            outwalk += sprintf(outwalk, "%s", title);
+            walk += strlen("title");
+        } else if (BEGINS_WITH(walk + 1, "content")) {
+            for (char *s = buf; *s != '\0' && n > 0; s++, n--) {
+                if (*s != '\n') {
+                    *(outwalk++) = *s;
+                }
+            }
+            walk += strlen("content");
+        } else if (BEGINS_WITH(walk + 1, "errno")) {
+            outwalk += sprintf(outwalk, "%d", errno);
+            walk += strlen("errno");
+        } else if (BEGINS_WITH(walk + 1, "error")) {
+            outwalk += sprintf(outwalk, "%s", strerror(errno));
+            walk += strlen("error");
+        } else {
+            *(outwalk++) = '%';
+        }
+    }
+
+    free(buf);
+
+    END_COLOR;
+    OUTPUT_FULL_TEXT(buffer);
+}
diff --git a/testcases/025-file-contents/expected_output.txt b/testcases/025-file-contents/expected_output.txt
new file mode 100644 (file)
index 0000000..5c56020
--- /dev/null
@@ -0,0 +1 @@
+contents | NONEXISTANT - 2: No such file or directory
diff --git a/testcases/025-file-contents/i3status.conf b/testcases/025-file-contents/i3status.conf
new file mode 100644 (file)
index 0000000..26646cd
--- /dev/null
@@ -0,0 +1,14 @@
+general {
+        output_format = "none"
+}
+
+order += "read_file EXISTING"
+order += "read_file NONEXISTANT"
+
+read_file EXISTING {
+        path = "testcases/025-file-contents/short.txt"
+}
+
+read_file NONEXISTANT {
+        path = "testcases/025-file-contents/nonexistant"
+}
diff --git a/testcases/025-file-contents/short.txt b/testcases/025-file-contents/short.txt
new file mode 100644 (file)
index 0000000..12f00e9
--- /dev/null
@@ -0,0 +1 @@
+contents