diff options
| -rw-r--r-- | Makefile.am | 1 | ||||
| -rw-r--r-- | i3status.c | 19 | ||||
| -rw-r--r-- | include/i3status.h | 1 | ||||
| -rw-r--r-- | man/i3status.man | 24 | ||||
| -rw-r--r-- | src/print_file_contents.c | 73 | ||||
| -rw-r--r-- | testcases/025-file-contents/expected_output.txt | 1 | ||||
| -rw-r--r-- | testcases/025-file-contents/i3status.conf | 14 | ||||
| -rw-r--r-- | testcases/025-file-contents/short.txt | 1 | 
8 files changed, 134 insertions, 0 deletions
| diff --git a/Makefile.am b/Makefile.am index 7d8b5a9..bb251f0 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 @@ -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); diff --git a/include/i3status.h b/include/i3status.h index f3a8941..217376a 100644 --- a/include/i3status.h +++ b/include/i3status.h @@ -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; diff --git a/man/i3status.man b/man/i3status.man index 4bc7d0a..70ba71a 100644 --- a/man/i3status.man +++ b/man/i3status.man @@ -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 index 0000000..afbe3e3 --- /dev/null +++ b/src/print_file_contents.c @@ -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 index 0000000..5c56020 --- /dev/null +++ b/testcases/025-file-contents/expected_output.txt @@ -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 index 0000000..26646cd --- /dev/null +++ b/testcases/025-file-contents/i3status.conf @@ -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 index 0000000..12f00e9 --- /dev/null +++ b/testcases/025-file-contents/short.txt @@ -0,0 +1 @@ +contents | 
