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:
parent
9d28a661a7
commit
9b5f6ae5f4
@ -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
|
||||
|
||||
|
19
i3status.c
19
i3status.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);
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
73
src/print_file_contents.c
Normal file
73
src/print_file_contents.c
Normal 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);
|
||||
}
|
1
testcases/025-file-contents/expected_output.txt
Normal file
1
testcases/025-file-contents/expected_output.txt
Normal file
@ -0,0 +1 @@
|
||||
contents | NONEXISTANT - 2: No such file or directory
|
14
testcases/025-file-contents/i3status.conf
Normal file
14
testcases/025-file-contents/i3status.conf
Normal 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"
|
||||
}
|
1
testcases/025-file-contents/short.txt
Normal file
1
testcases/025-file-contents/short.txt
Normal file
@ -0,0 +1 @@
|
||||
contents
|
Loading…
Reference in New Issue
Block a user