Added function to print content from file (#331)

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
This commit is contained in:
Dennis Vesterlund 2019-02-21 15:34:32 +01:00 committed by Michael Stapelberg
parent 9d28a661a7
commit 9b5f6ae5f4
8 changed files with 134 additions and 0 deletions

View File

@ -67,6 +67,7 @@ i3status_SOURCES = \
src/print_time.c \ src/print_time.c \
src/print_volume.c \ src/print_volume.c \
src/print_wireless_info.c \ src/print_wireless_info.c \
src/print_file_contents.c \
src/process_runs.c \ src/process_runs.c \
src/pulse.c src/pulse.c

View File

@ -491,6 +491,18 @@ int main(int argc, char *argv[]) {
CFG_CUSTOM_SEP_BLOCK_WIDTH_OPT, CFG_CUSTOM_SEP_BLOCK_WIDTH_OPT,
CFG_END()}; 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_opt_t opts[] = {
CFG_STR_LIST("order", "{}", CFGF_NONE), CFG_STR_LIST("order", "{}", CFGF_NONE),
CFG_SEC("general", general_opts, 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("load", load_opts, CFGF_NONE),
CFG_SEC("memory", memory_opts, CFGF_NONE), CFG_SEC("memory", memory_opts, CFGF_NONE),
CFG_SEC("cpu_usage", usage_opts, CFGF_NONE), CFG_SEC("cpu_usage", usage_opts, CFGF_NONE),
CFG_SEC("read_file", read_opts, CFGF_TITLE | CFGF_MULTI),
CFG_END()}; CFG_END()};
char *configfile = NULL; 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")); 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; 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) { if (output_format == O_I3BAR) {
yajl_gen_array_close(json_gen); yajl_gen_array_close(json_gen);

View File

@ -231,6 +231,7 @@ bool process_runs(const char *path);
int volume_pulseaudio(uint32_t sink_idx, const char *sink_name); 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 description_pulseaudio(uint32_t sink_idx, const char *sink_name, char buffer[MAX_SINK_DESCRIPTION_LEN]);
bool pulse_initialize(void); 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 */ /* socket file descriptor for general purposes */
extern int general_socket; extern int general_socket;

View File

@ -123,6 +123,11 @@ memory {
disk "/" { disk "/" {
format = "%free" format = "%free"
} }
read_file uptime {
path = "/proc/uptime"
}
------------------------------------------------------------- -------------------------------------------------------------
=== General === 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 == Universal module options
When using the i3bar output format, there are a few additional options that When using the i3bar output format, there are a few additional options that

73
src/print_file_contents.c Normal file
View File

@ -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);
}

View File

@ -0,0 +1 @@
contents | NONEXISTANT - 2: No such file or directory

View File

@ -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"
}

View File

@ -0,0 +1 @@
contents