Add logging functions
The mkdir dance is a bit awkward...
This commit is contained in:
		
							parent
							
								
									4f40ace9d4
								
							
						
					
					
						commit
						d99f20c0ff
					
				
							
								
								
									
										1
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								Makefile
									
									
									
									
									
								
							| @ -13,6 +13,7 @@ OBJS += config.o | ||||
| OBJS += edit.o | ||||
| OBJS += handle.o | ||||
| OBJS += irc.o | ||||
| OBJS += log.o | ||||
| OBJS += ui.o | ||||
| OBJS += url.o | ||||
| OBJS += xdg.o | ||||
|  | ||||
							
								
								
									
										4
									
								
								README.7
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								README.7
									
									
									
									
									
								
							| @ -1,4 +1,4 @@ | ||||
| .Dd February 12, 2020 | ||||
| .Dd March 25, 2020 | ||||
| .Dt README 7 | ||||
| .Os "Causal Agency" | ||||
| . | ||||
| @ -132,6 +132,8 @@ line editing | ||||
| tab complete | ||||
| .It Pa url.c | ||||
| URL detection | ||||
| .It Pa log.c | ||||
| chat logging | ||||
| .It Pa config.c | ||||
| configuration parsing | ||||
| .It Pa xdg.c | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| .Dd March 23, 2020 | ||||
| .Dd March 25, 2020 | ||||
| .Dt CATGIRL 1 | ||||
| .Os | ||||
| . | ||||
| @ -8,7 +8,7 @@ | ||||
| . | ||||
| .Sh SYNOPSIS | ||||
| .Nm | ||||
| .Op Fl Rev | ||||
| .Op Fl Relv | ||||
| .Op Fl C Ar copy | ||||
| .Op Fl H Ar hash | ||||
| .Op Fl N Ar send | ||||
| @ -155,6 +155,10 @@ Join the comma-separated list of channels | ||||
| Load the TLS client private key from | ||||
| .Ar path . | ||||
| . | ||||
| .It Fl l , Cm log | ||||
| Log chat events to files in paths | ||||
| .Pa $XDG_DATA_HOME/catgirl/log/network/channel/YYYY-MM-DD.log . | ||||
| . | ||||
| .It Fl n Ar nick , Cm nick = Ar nick | ||||
| Set nickname to | ||||
| .Ar nick . | ||||
|  | ||||
							
								
								
									
										5
									
								
								chat.c
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								chat.c
									
									
									
									
									
								
							| @ -129,7 +129,7 @@ int main(int argc, char *argv[]) { | ||||
| 	const char *user = NULL; | ||||
| 	const char *real = NULL; | ||||
| 
 | ||||
| 	const char *Opts = "!C:H:N:O:RS:a:c:eg:h:j:k:n:p:r:s:u:vw:"; | ||||
| 	const char *Opts = "!C:H:N:O:RS:a:c:eg:h:j:k:ln:p:r:s:u:vw:"; | ||||
| 	const struct option LongOpts[] = { | ||||
| 		{ "insecure", no_argument, NULL, '!' }, | ||||
| 		{ "copy", required_argument, NULL, 'C' }, | ||||
| @ -144,6 +144,7 @@ int main(int argc, char *argv[]) { | ||||
| 		{ "host", required_argument, NULL, 'h' }, | ||||
| 		{ "join", required_argument, NULL, 'j' }, | ||||
| 		{ "priv", required_argument, NULL, 'k' }, | ||||
| 		{ "log", no_argument, NULL, 'l' }, | ||||
| 		{ "nick", required_argument, NULL, 'n' }, | ||||
| 		{ "port", required_argument, NULL, 'p' }, | ||||
| 		{ "real", required_argument, NULL, 'r' }, | ||||
| @ -171,6 +172,7 @@ int main(int argc, char *argv[]) { | ||||
| 			break; case 'h': host = optarg; | ||||
| 			break; case 'j': self.join = optarg; | ||||
| 			break; case 'k': priv = optarg; | ||||
| 			break; case 'l': logEnable = true; | ||||
| 			break; case 'n': nick = optarg; | ||||
| 			break; case 'p': port = optarg; | ||||
| 			break; case 'r': real = optarg; | ||||
| @ -327,5 +329,6 @@ int main(int argc, char *argv[]) { | ||||
| 	handle(msg); | ||||
| 
 | ||||
| 	ircClose(); | ||||
| 	logClose(); | ||||
| 	uiHide(); | ||||
| } | ||||
|  | ||||
							
								
								
									
										6
									
								
								chat.h
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								chat.h
									
									
									
									
									
								
							| @ -259,8 +259,14 @@ void urlOpenCount(uint id, uint count); | ||||
| void urlOpenMatch(uint id, const char *str); | ||||
| void urlCopyMatch(uint id, const char *str); | ||||
| 
 | ||||
| extern bool logEnable; | ||||
| void logFormat(uint id, const time_t *time, const char *format, ...) | ||||
| 	__attribute__((format(printf, 3, 4))); | ||||
| void logClose(void); | ||||
| 
 | ||||
| FILE *configOpen(const char *path, const char *mode); | ||||
| FILE *dataOpen(const char *path, const char *mode); | ||||
| void dataMkdir(const char *path); | ||||
| 
 | ||||
| int getopt_config( | ||||
| 	int argc, char *const *argv, | ||||
|  | ||||
							
								
								
									
										112
									
								
								log.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								log.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,112 @@ | ||||
| /* Copyright (C) 2020  C. McEnroe <june@causal.agency>
 | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | ||||
|  */ | ||||
| 
 | ||||
| #include <assert.h> | ||||
| #include <err.h> | ||||
| #include <limits.h> | ||||
| #include <stdarg.h> | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <sysexits.h> | ||||
| #include <time.h> | ||||
| 
 | ||||
| #include "chat.h" | ||||
| 
 | ||||
| bool logEnable; | ||||
| 
 | ||||
| static struct { | ||||
| 	int year; | ||||
| 	int month; | ||||
| 	int day; | ||||
| 	FILE *file; | ||||
| } logs[IDCap]; | ||||
| 
 | ||||
| static FILE *logFile(uint id, const struct tm *tm) { | ||||
| 	if ( | ||||
| 		logs[id].file && | ||||
| 		logs[id].year == tm->tm_year && | ||||
| 		logs[id].month == tm->tm_mon && | ||||
| 		logs[id].day == tm->tm_mday | ||||
| 	) return logs[id].file; | ||||
| 
 | ||||
| 	if (logs[id].file) { | ||||
| 		int error = fclose(logs[id].file); | ||||
| 		if (error) err(EX_IOERR, "%s", idNames[id]); | ||||
| 	} | ||||
| 
 | ||||
| 	logs[id].year = tm->tm_year; | ||||
| 	logs[id].month = tm->tm_mon; | ||||
| 	logs[id].day = tm->tm_mday; | ||||
| 
 | ||||
| 	char path[PATH_MAX] = "log"; | ||||
| 	size_t len = strlen(path); | ||||
| 	dataMkdir(""); | ||||
| 	dataMkdir(path); | ||||
| 
 | ||||
| 	path[len++] = '/'; | ||||
| 	for (const char *ch = network.name; *ch; ++ch) { | ||||
| 		path[len++] = (*ch == '/' ? '_' : *ch); | ||||
| 	} | ||||
| 	path[len] = '\0'; | ||||
| 	dataMkdir(path); | ||||
| 
 | ||||
| 	path[len++] = '/'; | ||||
| 	for (const char *ch = idNames[id]; *ch; ++ch) { | ||||
| 		path[len++] = (*ch == '/' ? '_' : *ch); | ||||
| 	} | ||||
| 	path[len] = '\0'; | ||||
| 	dataMkdir(path); | ||||
| 
 | ||||
| 	strftime(&path[len], sizeof(path) - len, "/%F.log", tm); | ||||
| 	logs[id].file = dataOpen(path, "a"); | ||||
| 	if (!logs[id].file) exit(EX_CANTCREAT); | ||||
| 
 | ||||
| 	setlinebuf(logs[id].file); | ||||
| 	return logs[id].file; | ||||
| } | ||||
| 
 | ||||
| void logClose(void) { | ||||
| 	if (!logEnable) return; | ||||
| 	for (uint id = 0; id < IDCap; ++id) { | ||||
| 		if (!logs[id].file) continue; | ||||
| 		int error = fclose(logs[id].file); | ||||
| 		if (error) err(EX_IOERR, "%s", idNames[id]); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void logFormat(uint id, const time_t *src, const char *format, ...) { | ||||
| 	if (!logEnable) return; | ||||
| 
 | ||||
| 	time_t ts = (src ? *src : time(NULL)); | ||||
| 	struct tm *tm = localtime(&ts); | ||||
| 	if (!tm) err(EX_OSERR, "localtime"); | ||||
| 
 | ||||
| 	FILE *file = logFile(id, tm); | ||||
| 
 | ||||
| 	char buf[sizeof("0000-00-00T00:00:00+0000")]; | ||||
| 	strftime(buf, sizeof(buf), "%FT%T%z", tm); | ||||
| 	fprintf(file, "[%s] ", buf); | ||||
| 	if (ferror(file)) err(EX_IOERR, "%s", idNames[id]); | ||||
| 
 | ||||
| 	va_list ap; | ||||
| 	va_start(ap, format); | ||||
| 	vfprintf(file, format, ap); | ||||
| 	va_end(ap); | ||||
| 	if (ferror(file)) err(EX_IOERR, "%s", idNames[id]); | ||||
| 
 | ||||
| 	fprintf(file, "\n"); | ||||
| 	if (ferror(file)) err(EX_IOERR, "%s", idNames[id]); | ||||
| } | ||||
							
								
								
									
										22
									
								
								xdg.c
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								xdg.c
									
									
									
									
									
								
							| @ -134,3 +134,25 @@ local: | ||||
| 	if (!file) warn("%s", path); | ||||
| 	return file; | ||||
| } | ||||
| 
 | ||||
| void dataMkdir(const char *path) { | ||||
| 	const char *home = getenv("HOME"); | ||||
| 	const char *dataHome = getenv("XDG_DATA_HOME"); | ||||
| 
 | ||||
| 	char homePath[PATH_MAX]; | ||||
| 	if (dataHome) { | ||||
| 		snprintf( | ||||
| 			homePath, sizeof(homePath), | ||||
| 			"%s/" SUBDIR "/%s", dataHome, path | ||||
| 		); | ||||
| 	} else { | ||||
| 		if (!home) return; | ||||
| 		snprintf( | ||||
| 			homePath, sizeof(homePath), | ||||
| 			"%s/.local/share/" SUBDIR "/%s", home, path | ||||
| 		); | ||||
| 	} | ||||
| 
 | ||||
| 	int error = mkdir(homePath, S_IRWXU); | ||||
| 	if (error && errno != EEXIST) warn("%s", homePath); | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user