Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
fcfb3bb4a1 | |||
|
15a20da1aa |
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
# Generated directories
|
||||
build/
|
||||
objects/
|
||||
|
||||
# Autosave files
|
||||
*~
|
||||
*#
|
78
Makefile
78
Makefile
@ -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) -g
|
||||
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
1
sources/include/c.h
Normal file
@ -0,0 +1 @@
|
||||
#define A 4
|
30
sources/include/main.h
Normal file
30
sources/include/main.h
Normal file
@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Main.h: contains definitions for battery.c
|
||||
*/
|
||||
|
||||
#define DEFAULT_LOG_INTERVAL 15
|
||||
#define DEFAULT_LOG_INTERVAL_STRING "15"
|
||||
|
||||
#define BAT_PATH "/sys/class/power_supply/BAT1/"
|
||||
|
||||
#define CHARGE_NOW_PATH "charge_now"
|
||||
#define CHARGE_FULL_PATH "charge_full"
|
||||
#define ENERGY_NOW_PATH "energy_now"
|
||||
#define ENERGY_FULL_PATH "energy_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
|
@ -1,54 +1,79 @@
|
||||
#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 get_fds(FILE ** fd_now, FILE ** fd_full, const char * bat_path, const char * unit_now, const char * unit_full)
|
||||
{
|
||||
size_t bat_path_len = strlen(bat_path);
|
||||
size_t unit_now_len = strlen(unit_now);
|
||||
size_t unit_full_len = strlen(unit_full);
|
||||
char * now_path = (char*)malloc(bat_path_len + unit_now_len + 1);
|
||||
char * full_path = (char*)malloc(bat_path_len + unit_full_len + 1);
|
||||
|
||||
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");
|
||||
}
|
||||
strncpy(now_path, bat_path, bat_path_len + 1);
|
||||
strncpy(full_path, bat_path, bat_path_len + 1);
|
||||
|
||||
strncat(now_path, unit_now, unit_now_len);
|
||||
strncat(full_path, unit_full, unit_full_len);
|
||||
|
||||
printf("Attempting to open:\n\t%s\n\t%s\n", now_path, full_path);
|
||||
|
||||
(*fd_now) = fopen(now_path, "r");
|
||||
if(*fd_now == NULL){
|
||||
perror("Error opening file for current charge level");
|
||||
// if exiting here, no need to close fd_now because it is null
|
||||
return 1;
|
||||
}
|
||||
(*fd_full) = fopen(full_path, "r");
|
||||
if(*fd_full == NULL){
|
||||
perror("Error opening file for maximum charge level");
|
||||
// it just makes me feel safer, but this should be redundant
|
||||
if((*fd_now) != NULL){
|
||||
fclose(*fd_now);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
printf("Successfully found files at %p and %p\n", (void*)fd_now, (void*)fd_full);
|
||||
return 0;
|
||||
}
|
||||
|
||||
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, double * c_now, double * c_full, double * frac, int *pct)
|
||||
{
|
||||
int vals_read = fscanf(fd_now, "%lf", c_now);
|
||||
if(vals_read != 1){
|
||||
if(errno != 0){
|
||||
perror("Error reading full battery charge: ");
|
||||
@ -59,7 +84,7 @@ int main(int argc, char *const argv[]){
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
vals_read = fscanf(fd_full, "%d", &c_full);
|
||||
vals_read = fscanf(fd_full, "%lf", c_full);
|
||||
if(vals_read != 1){
|
||||
if(errno != 0){
|
||||
perror("Error reading full battery charge: ");
|
||||
@ -70,17 +95,170 @@ int main(int argc, char *const argv[]){
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(run_verbose){
|
||||
printf("Current charge: %d\n microampere-hours", c_now);
|
||||
printf("Full charge: %d\n microampere-hours", 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);
|
||||
(*frac) = *c_now / *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;
|
||||
FILE * fd_full;
|
||||
|
||||
|
||||
if(get_fds(&fd_now, &fd_full, BAT_PATH, CHARGE_NOW_PATH, CHARGE_FULL_PATH)){
|
||||
printf("Charge unavailable, attempting to use energy\n");
|
||||
if(get_fds(&fd_now, &fd_full, BAT_PATH, ENERGY_NOW_PATH, ENERGY_FULL_PATH)){
|
||||
printf("Could not find a charge unit that works.\n");
|
||||
exit(1);
|
||||
} else {
|
||||
printf("Unit is energy");
|
||||
}
|
||||
}
|
||||
fflush(stdout);
|
||||
double c_now;
|
||||
double c_full;
|
||||
double 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: %lf\n microampere-hours\n", c_now);
|
||||
printf("Full charge: %lf\n microampere-hours\n", c_full);
|
||||
}
|
||||
|
||||
if(!run_terse) {
|
||||
printf("Battery percentage: ");
|
||||
}
|
||||
|
||||
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
14
sources/test/test.c
Normal 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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user