Quicksaving before I add the -f option.

Added ability to log the battery level continuously.
Added compile-time option #define USE_LIBNOTIFY to post a low-battery warning with libnotify.
This commit is contained in:
Sandy Mossgrave 2023-06-06 15:05:52 +00:00
parent 4a942d92f7
commit 15a20da1aa
6 changed files with 297 additions and 63 deletions

7
.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
# Generated directories
build/
objects/
# Autosave files
*~
*#

View File

@ -1,9 +1,17 @@
#NEWPROJECT requires refactor.sh main.c
# Taken from https://stackoverflow.com/questions/30573481/how-to-write-a-makefile-with-separate-source-and-header-directories
# Always build targets and unit tests together. This avoids the scenario wherein the targets are built, then the source files are edited, then the unit tests are built and, accidentally, outdated targets are packaged and shipped. I say this knowing full well that I never ship my targets to anyone ever.
MAKEFILE_VERSION := Makefile from battery log v1.0
PREFIX :=
SUFFIX :=
SRCDIR := sources
MAINDIR := $(SRCDIR)/main
INCDIR := include
INCDIR := $(abspath $(SRCDIR)/include)
OBJDIR := objects
MAINOD := $(OBJDIR)/main
BINDIR := build
@ -12,34 +20,57 @@ INSTDIR := /usr/local/bin
REFACTOR := refactor.h
TESTCFLAGS:= $(shell pkg-config --cflags criterion)
TESTLIBS := $(shell pkg-config --libs criterion)
TESTDIR := $(SRCDIR)/test
TESTOD := $(OBJDIR)/test
HEADERS := $(wildcard $(INCDIR)/*.h)
MAINS := $(wildcard $(MAINDIR)/*.c)
MAINOBJS := $(patsubst $(MAINDIR)/%.c, $(MAINOD)/%.o, $(MAINS))
TESTS := $(wildcard $(TESTDIR)/*.c)
TESTOBJS := $(patsubst $(TESTDIR)/%.c, $(TESTOD)/%.o, $(TESTS))
UNITTESTS := $(patsubst $(TESTDIR)/%.c, $(BINDIR)/%, $(TESTS))
TARGETS := $(patsubst $(MAINDIR)/%.c, $(BINDIR)/%, $(MAINS))
SOURCES := $(wildcard $(SRCDIR)/*.c)
OBJECTS := $(patsubst $(SRCDIR)/%.c, $(OBJDIR)/%.o, $(SOURCES))
CPPFLAGS := -Iinclude -MMD -MP
CFLAGS := -Wall -Werror -Wpedantic
CPPFLAGS := -I$(INCDIR) -MMD -MP -DUSE_LIBNOTIFY
CFLAGS := -Wall -Werror -Wpedantic $(shell pkg-config --cflags libnotify)
LDFLAGS := #-Lmath or whatever
LDLIBS := #-lm or whatever
LDLIBS := $(shell pkg-config --libs libnotify) #-lm or whatever
.PHONY: all clean
define MANGLE =
$(dir $(1))$(PREFIX)$(notdir $(1))$(SUFFIX)
endef
all: $(TARGETS)
@echo $(TARGETS)
.PHONY: all clean version
all: | $(TARGETS) $(UNITTESTS)
version:
@echo $(MAKEFILE_VERSION)
refactor:
./refactor.sh
$(TARGETS): $(OBJECTS) $(MAINOBJS) | $(BINDIR) $(OBJDIR) $(MAINOD)
$(CC) $(LDFLAGS) $(OBJECTS) $(patsubst $(BINDIR)/%, $(MAINOD)/%.o, $@) $(LDLIBS) -o $@
$(UNITTESTS): $(OBJECTS) $(TESTOBJS) | $(BINDIR) $(OBJDIR) $(TESTOD)
@echo "Linking unit tests..."
$(CC) $(LDFLAGS) $(OBJECTS) $(patsubst $(BINDIR)/%, $(TESTOD)/%.o, $@) $(LDLIBS) $(TESTLIBS) -o $(dir $@)$(PREFIX)$(notdir $@)$(SUFFIX)
@echo "...Done linking unit tests"
$(BINDIR) $(OBJDIR) $(MAINOD):
$(TARGETS): $(OBJECTS) $(MAINOBJS) | $(BINDIR) $(OBJDIR) $(MAINOD)
@echo "Linking targets..."
$(CC) $(LDFLAGS) $(OBJECTS) $(patsubst $(BINDIR)/%, $(MAINOD)/%.o, $@) $(LDLIBS) -o $(dir $@)$(PREFIX)$(notdir $@)$(SUFFIX)
@echo "...Done linking targets"
$(BINDIR) $(OBJDIR) $(MAINOD) $(TESTOD):
mkdir --parents $@
$(OBJDIR)/%.o: $(SRCDIR)/%.c $(HEADERS) | $(OBJDIR)
@ -48,6 +79,9 @@ $(OBJDIR)/%.o: $(SRCDIR)/%.c $(HEADERS) | $(OBJDIR)
$(MAINOD)/%.o: $(MAINDIR)/%.c $(HEADERS) | $(MAINOD)
$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
$(TESTOD)/%.o: $(TESTDIR)/%.c $(HEADERS) | $(TESTOD)
$(CC) $(CPPFLAGS) $(CFLAGS) $(TESTCFLAGS) $(TESTLIBS) -c $< -o $@
# $| evaluates to the order-only prerequisites, in this case: $(TARGETS)
# The $(foreach ...) function will evaluate to a semicolon-separated list of 'cp <target> $instdir'
# Putting this $(foreach ...) into the recipe means that its result
@ -59,15 +93,35 @@ $(MAINOD)/%.o: $(MAINDIR)/%.c $(HEADERS) | $(MAINOD)
install: | $(TARGETS)
@echo "Installing executables to $(INSTDIR)"
@$(foreach target, $|, cp -v $(target) $(INSTDIR);)
@$(foreach target, $|, cp -v $(call MANGLE, $(target)) $(INSTDIR);)
uninstall: | $(TARGETS)
@echo "Removing executables from $(INSTDIR)"
$(foreach target, $(notdir $|), rm -v $(INSTDIR)/$(target);)
$(foreach target, $(notdir $(call MANGLE, $|)), rm -v $(INSTDIR)/$(target);)
clean:
$(RM) -rv $(BINDIR) $(OBJDIR)
# Lazy kludge to run the first executable specified in $(TARGETS)
# Could also be accomplished by running $< but this is easier to read
run: all
$(eval MANGLEDNAME=$(call MANGLE, $(word 1,$(TARGETS))))
@echo "Running " $(MANGLEDNAME)
@$(MANGLEDNAME)
# @$(dir $(word 1,$(TARGETS)))$(PREFIX)$(notdir $(word 1,$(TARGETS)))$(SUFFIX)
# unit testing!
test: $(TARGETS) $(UNITTESTS)
@echo "Testing " $(word 1,$(UNITTESTS))
@$(dir $(word 1,$(UNITTESTS)))$(PREFIX)$(notdir $(word 1,$(UNITTESTS)))$(SUFFIX)
.PHONY: variable
variable: all
$(eval mangle2=$(dir $(word 1,$(UNITTESTS)))$(PREFIX)$(notdir $(word 1,$(UNITTESTS)))$(SUFFIX))
$(eval mangle3=$(call MANGLE, $(UNITTESTS)))
@echo "variable declared within a recipe: "$(mangle2)
@echo "variable declared within a recipe using a canned recipe: "$(mangle3)
# Honestly still not sure what this directive does.
# It's an include directive, which processes all .d files that correspond to members of $(OBJ),
# with the hyphen in front meaning that it suppresses/ignores errors (from nonexistent .d files)

1
sources/include/c.h Normal file
View File

@ -0,0 +1 @@
#define A 4

26
sources/include/main.h Normal file
View File

@ -0,0 +1,26 @@
/**
* Main.h: contains definitions for battery.c
*/
#define DEFAULT_LOG_INTERVAL 15
#define DEFAULT_LOG_INTERVAL_STRING "15"
#define CHARGE_NOW_PATH "/sys/class/power_supply/BAT0/charge_now"
#define CHARGE_FULL_PATH "/sys/class/power_supply/BAT0/charge_full"
#define VERSION "v0.2"
#define TITLE "Battery Percentage, by Catluck Kettlemerry"
#define PROGNAME "battery"
#define USAGE0 "Usage:"
#define USAGE1 "[OPTION...]"
#define OPTIONS0 "Options:"
#define OPTIONS1 "\t-v run verbosely"
#define OPTIONS2 "\t-h show help and exit"
#define OPTIONS3 "\t-t provide terse output"
#define OPTIONS4 "\t-l Continuously log battery level every " DEFAULT_LOG_INTERVAL_STRING " seconds"
#define OPTIONS5 "\t-i <n> Set log interval to poll every <n> seconds"
#define OPTIONS6 "\t (This argument automatically activates continuous logging)"
#define OPTIONS7 "\t-u (If the interval is less than 15 seconds, " PROGNAME " will refuse to run."
#define OPTIONS8 "\t Specify -u to run anyway.)"
#define OPTIONS9 "\t-w <n> Warn the user if the battery charge drops below <n> percent"
#define OPTIONS10 "\t-p Print the effects of the given options for this invocation"
#define MESGLEN 25

View File

@ -1,54 +1,44 @@
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#define CHARGE_NOW_PATH "/sys/class/power_supply/BAT0/charge_now"
#define CHARGE_FULL_PATH "/sys/class/power_supply/BAT0/charge_full"
#define VERSION "v0.0"
#define TITLE "Battery Percentage, by Catluck Kettlemerry"
#define PROGNAME "battery_percentage"
#define USAGE0 "Usage: "
#define USAGE1 "[option]"
#define OPTIONS0 "Options:"
#define OPTIONS1 "\t-v run verbosely"
#define OPTIONS2 "\t-h show help and exit"
#define OPTIONS3 "\t-t provide terse output"
#ifdef USE_SYSLOG
#include <syslog.h>
#endif
#include "main.h"
#ifdef USE_LIBNOTIFY
#include <libnotify/notify.h>
#endif
int run_verbose = 0;
int run_terse = 0;
int run_continuously = 0;
int interval = DEFAULT_LOG_INTERVAL;
int run_unsafe = 0;
int force_print_parameters = 0;
int warn_level = 10;
int warned = 0; /* Set if already warned */
const char * delimiter = "\n";
void print_help(void){
printf("%s %s\n", TITLE, VERSION);
printf("%s %s %s\n", USAGE0, PROGNAME, USAGE1);
printf("%s\n%s\n%s\n%s\n", OPTIONS0, OPTIONS1, OPTIONS2, OPTIONS3);
printf("%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
OPTIONS0, OPTIONS1, OPTIONS2, OPTIONS3, OPTIONS4,
OPTIONS5, OPTIONS6, OPTIONS7, OPTIONS8, OPTIONS9,
OPTIONS10);
}
int main(int argc, char *const argv[]){
int opt;
while((opt = getopt(argc, argv, "vht")) != -1) {
switch(opt){
case 'v':
run_verbose = 1;
break;
case 'h':
print_help();
return 0;
case 't':
run_terse = 1;
break;
default:
printf("Literally how could you pass an unspecified argument to this program");
}
}
FILE * fd_now = fopen(CHARGE_NOW_PATH, "r");
FILE * fd_full = fopen(CHARGE_FULL_PATH, "r");
int c_now;
int c_full;
int vals_read = fscanf(fd_now, "%d", &c_now);
int get_percentage
(FILE * fd_now, FILE * fd_full, int * c_now, int * c_full, float * frac, int *pct)
{
int vals_read = fscanf(fd_now, "%d", c_now);
if(vals_read != 1){
if(errno != 0){
perror("Error reading full battery charge: ");
@ -59,7 +49,7 @@ int main(int argc, char *const argv[]){
}
return 1;
}
vals_read = fscanf(fd_full, "%d", &c_full);
vals_read = fscanf(fd_full, "%d", c_full);
if(vals_read != 1){
if(errno != 0){
perror("Error reading full battery charge: ");
@ -70,17 +60,159 @@ int main(int argc, char *const argv[]){
}
return 1;
}
(*frac) = ((float)(*c_now)) / ((float)(*c_full));
(*pct) = (int)((*frac) * 100);
return 0;
}
void warn(int pct){
if(!warned){
printf("Battery Low\n");
#ifdef USE_SYSLOG
syslog(LOG_WARNING, "Battery Low");
#endif
#ifdef USE_LIBNOTIFY
char warnmesg[MESGLEN];
snprintf(warnmesg, MESGLEN, "Battery is at %d percent", pct);
notify_init(PROGNAME);
NotifyNotification * levelwarn = notify_notification_new("Battery Low", warnmesg, "dialog-information");
notify_notification_show(levelwarn, NULL);
g_object_unref(G_OBJECT(levelwarn));
notify_uninit();
#endif
}
warned = 1;
}
int main(int argc, char *const argv[]){
int opt;
while((opt = getopt(argc, argv, "vhtli:uw:p")) != -1) {
switch(opt){
case 'v':
run_verbose = 1;
break;
case 'h':
print_help();
return 0;
case 't':
run_terse = 1;
break;
case 'l':
run_continuously = 1;
break;
case 'i':
interval = atoi(optarg);
run_continuously = 1;
break;
case 'u':
run_unsafe = 1;
break;
case 'w':
warn_level = atoi(optarg);
break;
case 'p':
force_print_parameters = 1;
break;
default:
printf("Literally how could you pass an unspecified argument to this program");
}
}
/**
Print run parameters at startup
*/
if(force_print_parameters) {
if(run_terse && run_verbose){
printf(PROGNAME " called with arguments to run both tersely and verbosely, the author legitimately cannot tell what you want her to do\n");
} else if(run_terse) {
printf(PROGNAME " will run tersely\n");
} else if(run_verbose) {
printf(PROGNAME " will run verbosely\n");
}
printf(PROGNAME " will poll every %d seconds\n", interval);
if(!run_terse){
printf(PROGNAME " will warn at %d%% battery\n", warn_level);
}
}
if(warn_level <= 0 && !run_terse){
fprintf(stderr, "Warn level set to %d. " PROGNAME " will not send warning on low battery.\n", warn_level);
}
if(interval < 15 && !run_unsafe){
fprintf(stderr, "Update interval less than 15 seconds, refusing to run. Specify -u to override this check.\n");
return 1;
}
#ifdef USE_SYSLOG
openlog(PROGNAME, 0, LOG_USER);
#endif
FILE * fd_now = fopen(CHARGE_NOW_PATH, "r");
FILE * fd_full = fopen(CHARGE_FULL_PATH, "r");
int c_now;
int c_full;
float frac;
int pct;
do{
int rval = get_percentage(fd_now, fd_full, &c_now, &c_full, &frac, &pct);
if (rval){
printf("Um, problem\n");
return 1;
}
if(run_verbose){
printf("Current charge: %d\n microampere-hours", c_now);
printf("Full charge: %d\n microampere-hours", c_full);
printf("Current charge: %d\n microampere-hours\n", c_now);
printf("Full charge: %d\n microampere-hours\n", c_full);
}
if(!run_terse) {
printf("Battery percentage: ");
}
float frac = ((float)c_now) / ((float)c_full);
int pct = (int)(frac * 100);
printf("%d\n", pct);
if(pct > warn_level){
warned = 0;
}
if(pct <= warn_level && !warned){
warn(pct);
}
printf("%d%s", pct, delimiter);
#ifdef USE_SYSLOG
syslog(LOG_INFO, "%d%%%s", pct, delimiter);
#endif
if(run_continuously){
sleep(interval);/*
errno = 0;
rewind(fd_now);
if(errno != 0){
perror("Error rewinding " CHARGE_NOW_PATH);
return 1;
}
rewind(fd_full); //just for the hecc of it
if(errno != 0){
perror("Error rewinding " CHARGE_FULL_PATH);
return 1;
}*/
fclose(fd_now);
fclose(fd_full);
fd_now = fopen(CHARGE_NOW_PATH, "r");
fd_full = fopen(CHARGE_FULL_PATH, "r");
}
} while (run_continuously);
#ifdef USE_SYSLOG
closelog();
#endif
return 0;
}

14
sources/test/test.c Normal file
View File

@ -0,0 +1,14 @@
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <criterion/criterion.h>
#ifdef USE_LIBNOTIFY
#include <libnotify/notify.h>
#endif
Test(misc, passing){
cr_assert(1);
}