timetracker-save.c (4832B)
1 #include <signal.h> 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <string.h> 5 #include <sys/stat.h> 6 #include <sys/types.h> 7 #include <time.h> 8 #include <unistd.h> 9 10 #define MAX_PROGRAM_NAME 30 /* longer program names get cut off */ 11 #define WRITE_INTERVAL 600 /* 10 minutes */ 12 13 #define FILENAME "/timetracker/data" /* relative to $XDG_DATA_HOME */ 14 #define FILENAME2 "/.local/share/timetracker/data" /* relative to $HOME */ 15 16 17 /* returns path to data file; should be free()d */ 18 char * filelocation() { 19 char *loc = NULL; 20 char *xdg_data_home = getenv("XDG_DATA_HOME"); 21 if (xdg_data_home) { 22 loc = malloc(strlen(xdg_data_home) + strlen(FILENAME) + 1); 23 strcpy(loc, xdg_data_home); 24 strcpy(loc + strlen(xdg_data_home), FILENAME); 25 } else { 26 char *xdg_home = getenv("HOME"); 27 loc = malloc(strlen(xdg_home) + strlen(FILENAME2) + 1); 28 strcpy(loc, xdg_home); 29 strcpy(loc + strlen(xdg_home), FILENAME2); 30 } 31 return loc; 32 } 33 34 35 /* creates .local/share/timetracker (or $XDG_DATA_HOME/timetracker) */ 36 /* assumes .local/share is already there */ 37 void createdatadir() { 38 char * loc = filelocation(); 39 for (int i = strlen(loc) - 1; i >= 0; i--) { 40 if (loc[i] == '/') { 41 loc[i] = '\0'; 42 break; 43 } 44 } 45 /* fails if directory exists already but that is no problem */ 46 mkdir(loc, 0777); 47 free(loc); 48 } 49 50 51 struct program { 52 char name[MAX_PROGRAM_NAME]; 53 int time; 54 }; 55 56 char lastline[MAX_PROGRAM_NAME]; 57 time_t lasttime = 0; 58 59 time_t writetime; 60 61 struct program *programs = NULL; 62 int programnum = 0; /* number of programs stored in the programs array */ 63 64 65 /* returns current time (this does not make sense but it is convenient) */ 66 /* this function was created to be able to add programs/write data file from 67 * inside and outside the while loop in main(); the current solution with many 68 * global variables is rather dirty but it works */ 69 time_t addprogram(_Bool forcewrite) { 70 time_t newtime = time(NULL); 71 72 if (lasttime) { 73 /* is the program already in the list? (and where?) */ 74 int index = -1; 75 for (int i = 0; i < programnum; i++) { 76 if (!strcmp(lastline, programs[i].name)) { 77 index = i; 78 } 79 } 80 if (index == -1) { 81 /* extend/create list if necessary */ 82 if (programnum % 10 == 0) { 83 programs = realloc(programs, sizeof(struct program) * 84 (10 + programnum)); 85 } 86 /* add new program to list */ 87 strncpy(programs[programnum].name, lastline, MAX_PROGRAM_NAME); 88 programs[programnum].time = newtime - lasttime; 89 programnum++; 90 } else { 91 /* update time for existing program */ 92 programs[index].time += newtime - lasttime; 93 } 94 95 if (writetime <= newtime || forcewrite) { 96 /* write to data file */ 97 char *loc = filelocation(); 98 FILE *dataFile = fopen(loc, "a");; 99 free(loc); 100 101 fprintf(dataFile, "%d,", newtime); 102 for (int i = 0; i < programnum - 1; i++) { 103 fprintf(dataFile, "%s:%d,", programs[i].name, programs[i].time); 104 } 105 fprintf(dataFile, "%s:%d\n", programs[programnum - 1].name, programs[programnum - 1].time); 106 fclose(dataFile); 107 108 /* reset everything */ 109 free(programs); 110 programs = NULL; 111 programnum = 0; 112 writetime = newtime + WRITE_INTERVAL; 113 } 114 } 115 return newtime; 116 } 117 118 119 /* cause getline() to throw error end return from main function */ 120 void exithandler(int dummy) { 121 fclose(stdin); 122 } 123 124 125 int main() { 126 createdatadir(); 127 128 signal(SIGHUP, exithandler); 129 signal(SIGINT, exithandler); 130 signal(SIGTERM, exithandler); 131 132 /* ensures that no two timestamps in output file are identical */ 133 sleep(1); 134 135 writetime = time(NULL) + WRITE_INTERVAL; 136 137 size_t size = 0; /* i do not need this value but getline does */ 138 char *line = NULL; /* current line / program name */ 139 while (getline(&line, &size, stdin) != -1) { 140 /* remove \n at the end of the line */ 141 line[strlen(line) - 1] = '\0'; 142 /* remove ":" and "," from program name because they would mess up 143 * the output file */ 144 for (int i = 0; i < strlen(line); i++) { 145 if (line[i] == ':' || line[i] == ',') line[i] = '-'; 146 } 147 148 /* for the next iteration */ 149 lasttime = addprogram(0); 150 strncpy(lastline, line, MAX_PROGRAM_NAME); 151 lastline[MAX_PROGRAM_NAME - 1] = '\0'; 152 } 153 free(line); 154 addprogram(1); 155 156 /* is this successful or unsuccessful? the program is meant to operate in 157 * an infinite loop and be quit by sigterm */ 158 return 0; 159 }