add simplestatus to utils

This commit is contained in:
randomuser 2022-01-07 02:29:04 -06:00
parent 2bde763903
commit c798d4ddb3
4 changed files with 418 additions and 0 deletions

1
.gitignore vendored
View File

@ -5,3 +5,4 @@ c/timer
c/boid
c/a.out
c/anaconda
c/simplestatus

View File

@ -29,10 +29,12 @@ mkc:
cc c/timer.c -o c/timer
cc c/boid.c -o c/boid -lm -lX11
cc c/anaconda.c -o c/anaconda -lm -lX11
cc c/simplestatus.c -o c/simplestatus
c:
cp -f c/scream $(DESTDIR)$(PREFIX)/bin
cp -f c/timer $(DESTDIR)$(PREFIX)/bin
cp -f c/boid $(DESTDIR)$(PREFIX)/bin
cp -f c/anaconda $(DESTDIR)$(PREFIX)/bin
cp -f c/simplestatus $(DESTDIR)$(PREFIX)/bin
clean:
rm c/scream

328
c/simplestatus.c Normal file
View File

@ -0,0 +1,328 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#define NAME "simplestatus"
typedef struct module {
char *mod_name;
int refresh;
int signal;
struct module *next;
} module;
typedef struct cache {
char *mod_name;
char *data;
struct cache *next;
} cache;
const int alloc_inc = 30;
const char module_text[] = "module";
const char order_text[] = "order";
const char format_text[] = "format";
char *format_string = NULL;
module *table = NULL;
cache *mcache = NULL;
void llprintf(char *fmt) {
int length = strlen(fmt);
cache *ptr = mcache;
ptr = mcache;
for(int i = 0; i < length; i++) {
if(fmt[i] == '&') {
fprintf(stdout, "%s", ptr->data);
ptr = ptr->next;
} else {
fprintf(stdout, "%c", fmt[i]);
}
}
fprintf(stdout, "\n");
fflush(stdout);
}
char *get_conf_dir(void) {
/* check if $XDG_CONFIG_DIR is set */
char *rootdir, *append;
if((rootdir = getenv("XDG_CONFIG_DIR"))) {
append = "/"NAME"/";
} else if((rootdir = getenv("HOME"))) {
append = "/.config/"NAME"/";
} else {
return NULL;
}
char *buffer = malloc(strlen(rootdir) + strlen(append) + 1);
strcpy(buffer, rootdir);
strcat(buffer, append);
return buffer;
}
char *execstdout(char *file, char *arg[], char *env[]) {
int pfds[2];
if(pipe(pfds) == -1) {
perror("PIPE");
fprintf(stderr, "pipe failed! aborting.\n");
abort();
}
int pid = fork();
if(pid == -1) {
fprintf(stderr, "fork failed! aborting.\n");
abort();
} else if(pid == 0) {
if(close(1) == -1) exit(0);
if(dup(pfds[1]) == -1) exit(0);
if(close(pfds[0])) exit(0);
execlp(file, file, NULL);
} else {
wait(NULL);
int bufsize = alloc_inc, total = 0;
char *buf = malloc(bufsize);
if(!buf) {
fprintf(stderr, "malloc failed! aborting.\n");
abort();
}
for(;;) {
int size = read(pfds[0], buf + total, alloc_inc);
if(size == -1) {
if(errno == EINTR) {
continue;
} else {
free(buf);
return NULL;
}
}
total += size;
if(size == alloc_inc) { /* we need to reallocate */
buf = realloc(buf, (bufsize *= 2));
if(!buf) {
fprintf(stderr, "realloc failed! aborting.\n");
abort();
}
continue;
}
buf[total] = '\0';
buf = realloc(buf, strlen(buf));
return buf;
}
close(pfds[0]);
}
}
char *read_line(FILE *fp) {
int size = 30;
int filled = 0;
int c;
char *buf = malloc(size);
while(c = fgetc(fp)) {
if(c == EOF) {
if(filled == 0) {
free(buf);
return NULL;
} else {
break;
}
}
if(c == '\n') break;
if(size == filled) {
buf = realloc(buf, (size *= 2));
}
buf[filled] = c;
filled++;
}
buf[filled] = '\0';
return buf;
}
module *parse_file(char *file) {
FILE *fp = fopen(file, "r");
char *cline;
module *current = NULL, *head = NULL;
int linenumber = 0;
while((cline = read_line(fp)) != NULL) {
linenumber++;
/* free the string up here */
if(strlen(cline) == 0) {
free(cline);
continue;
}
if(cline[0] == '#') {
free(cline);
continue;
}
char *command = strdup(strtok(cline, " "));
if(!memcmp(command, module_text, sizeof module_text)) {
if(current == NULL) {
current = malloc(sizeof *current);
head = current;
current->next = NULL;
} else {
current->next = malloc(sizeof *current);
current = current->next;
current->next = NULL;
}
/* get the module name */
char *token = strtok(NULL, " ");
if(!token) {
fprintf(stderr, "syntax error at line %i: specify a module name!\n", linenumber);
return NULL;
}
current->mod_name = strdup(token);
/* get the signal number */
token = strtok(NULL, " ");
if(!token) {
fprintf(stderr, "syntax error at line %i: specify the signal number!\n", linenumber);
return NULL;
}
current->signal = atoi(token);
/* get the refresh duration */
token = strtok(NULL, " ");
if(!token) {
fprintf(stderr, "syntax error at line %i: specify the refresh duration!\n", linenumber);
return NULL;
}
current->refresh = atoi(token);
free(cline);
continue;
} else if(!memcmp(command, order_text, sizeof order_text)) {
if(mcache) {
fprintf(stderr, "syntax error at line %i: you can't issue the order command twice\n", linenumber);
return NULL;
}
char *token;
cache *ptr = mcache;
while(token = strtok(NULL, " ")) {
if(mcache) {
ptr->next = malloc(sizeof *mcache);
ptr = ptr->next;
} else {
mcache = malloc(sizeof mcache);
ptr = mcache;
}
ptr->mod_name = token;
ptr->data = NULL;
ptr->next = NULL;
}
} else if(!memcmp(command, format_text, sizeof format_text)) {
char *pattern = cline + sizeof format_text;
format_string = strdup(pattern);
}
}
return head;
}
int is_dir(char *dir) {
struct stat sb;
if(stat(dir, &sb) == 0 && S_ISDIR(sb.st_mode)) {
return 1;
} else {
return 0;
}
}
void sighandler(int sig) {
module *ptr = table;
char *mod = NULL;
while(ptr) {
if(ptr->signal == sig) {
mod = ptr->mod_name;
char *path = malloc(strlen(mod) + 3);
strcpy(path, "./");
strcat(path, mod);
char *output = execstdout(path, NULL, NULL);
free(path);
cache *ptr2 = mcache;
while(ptr2) {
if(!memcmp(mod, ptr2->mod_name, strlen(mod))) {
ptr2->data = output;
break;
}
ptr2 = ptr2->next;
}
}
ptr = ptr->next;
}
}
void sighandler_w(int sig) {
sighandler(sig);
llprintf(format_string);
}
void create_sighandle_from_table(void) {
module *ptr = table;
struct sigaction *action = malloc(sizeof *action);
action->sa_handler = sighandler_w;
sigemptyset(&(action->sa_mask));
action->sa_flags = 0;
while(ptr) {
sigaction(ptr->signal, action, NULL);
sighandler(ptr->signal); /* force an update */
ptr = ptr->next;
}
}
int main(void) {
char *conf_dir = get_conf_dir();
if(!is_dir(conf_dir)) {
fprintf(stderr, "create configuration at %s and try again\n", conf_dir);
exit(1);
}
chdir(conf_dir);
table = parse_file("config");
create_sighandle_from_table();
llprintf(format_string);
int counter = 0;
for(;;) {
module *ptr = table;
counter++;
sleep(1);
while(ptr) {
if(counter % ptr->refresh == 0) {
sighandler_w(ptr->signal);
}
ptr = ptr->next;
}
}
return 0;
}

87
man/simplestatus.1 Normal file
View File

@ -0,0 +1,87 @@
'\" t
.TH SIMPLESTATUS 1 simplestatus
.SH NAME
simplestatus \- simple, lightweight status bar content generator
.SH SYNOPSIS
.B simplestatus
.SH DESCRIPTION
simplestatus is a simple status bar content generator for statusbars which accept their input via standard input (such as lemonbar), or can be used in conjunction with other utilities to set their statusbars (e.g. dwm's statusbar). simplestatus is configured via a configuration file.
simplestatus works on the premise of each component in the statusbar being a module; in this case, simplestatus represents them in shell scripts residing in the configuration directory. When a module needs to be updated, the cooresponding shell script is executed and its stdout read as the output of the module. Modules can be updated via signals send to the process or via timers, all specified in the configuration file.
.SH CONFIGURATION
The configuration file resides in the configuration directory. The configuration file location is determined via checking the following places:
.IP
o $XDG_CONFIG_DIR/simplestatus/config
.IP
o $HOME/.config/simplestatus/config
.P
If after these two steps the configuration cannot be found, simplestatus exits.
Once the configuration file location is determined, the configuration file is read and parsed. There are three main directives in the configuration file:
.IP
o module
.IP
o order
.IP
o format
.SS module
This directive takes three arguments: the module name, the signal trigger number, and the timer trigger. For example
.IP
.B module power 7 30
.P
defines a module named ``power'', which is updated when signal 7 is recieved (SIGBUS) or every thirty seconds. ``power'' must be an executable placed in the configuration directory (e.g. the directory in which the configuration file resides) and must have the executable flag set for the current user. Note that entering the signals' symbolic name (SIGBUS) instead of the number (7) is not supported at this time.
.SS order
The ``order'' directive specifies in what order the modules will be placed in the format string, defined by the directive ``format''. For example,
.IP
.B order power time date wm
.P
specifies the order in which the modules will be inserted in the format string, starting with ``power'' and ending with ``wm''.
.SS format
``format'' specifies the format string that the output of the modules will be inserted into. Below is an example command:
.IP
.B format %{l}&%{c}&%{r}&
.SH CONFIGURATION EXAMPLE
Assume we have the following modules, who output the following values:
.TS
tab(@) allbox;
l|l .
MODULE@EVALUATED VALUE
foo@bar
ping@pong
a@b
.TE
.P
and this configuration:
.IP
module foo 7 30
module ping 10 30
module a 12 120
order foo ping a
format output of foo: &; output of ping: &; output of a: &
.P
we will get the output:
.IP
.B output of foo: bar; output of ping: pong; output of a: b
.P
Should you wish to make comments, prefix lines with ``#''. If you wish to make spaces between instructions, make an empty line.
.SH COMMON PITFALLS
.IP
o Don't assign SIGINT (2) to a module unless you want it to update when you ^C!
.SH PLANNED FEATURES
.IP
o If the configuration file is marked +x, execute and read contents from the script's stdout to be parsed.
o free(...) memory when finished.
o Add ability to export environment variables to scripts via configuration file.
o Add signal to force re-parsing of configuration file.
.SH KNOWN BUGS
o Lack of use of free(...), causing potential for memory leaks.
.SH SEE ALSO
.BR lemonbar (1),
.BR slstatus (1)
.SH AUTHOR
randomuser <randomuser at tilde dot club>