main.c (5830B)
1 /* 2 * src/main.c from hirc 3 * 4 * Copyright (c) 2021-2022 hhvn <dev@hhvn.uk> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 * 18 */ 19 20 #include <stdio.h> 21 #include <wchar.h> 22 #include <errno.h> 23 #include <stdlib.h> 24 #include <libgen.h> 25 #include <limits.h> 26 #include <string.h> 27 #include <unistd.h> 28 #include <stdarg.h> 29 #include <signal.h> 30 #include <poll.h> 31 #include "hirc.h" 32 33 struct Server *servers = NULL; 34 struct HistInfo *main_buf; 35 36 void 37 die(int code, char *format, ...) { 38 static int dying = 0; 39 va_list ap; 40 41 /* prevent loop if a function in cleanup() calls die() */ 42 if (!dying) { 43 dying = 1; 44 cleanup("Client error"); 45 dying = 0; 46 47 fprintf(stderr, "Fatal: "); 48 va_start(ap, format); 49 vfprintf(stderr, format, ap); 50 va_end(ap); 51 #ifdef DIE_CORE 52 raise(SIGABRT); 53 #else 54 exit(code); 55 #endif /* DIE_CORE */ 56 } 57 } 58 59 void 60 cleanup(char *quitmsg) { 61 struct Server *sp, *prev; 62 63 for (sp = servers, prev = NULL; sp; sp = sp->next) { 64 if (prev) 65 serv_free(prev); 66 serv_disconnect(sp, 0, quitmsg); 67 prev = sp; 68 } 69 70 serv_free(prev); 71 ui_deinit(); 72 } 73 74 int 75 main(int argc, char *argv[]) { 76 struct Selected oldselected; 77 struct Server *sp; 78 int i, j, refreshed, inputrefreshed; 79 long pinginact, reconnectinterval, maxreconnectinterval; 80 81 if (argc > 2) { 82 fprintf(stderr, "usage: %s [configfile]\n", basename(argv[0])); 83 fprintf(stderr, " %s -d\n", basename(argv[0])); 84 return EXIT_FAILURE; 85 } 86 87 if (argc == 2 && strcmp(argv[1], "-d") == 0) { 88 printf(".Bl -tag\n"); 89 for (i=0; config[i].name; i++) { 90 printf(".It Ic %s\n", config[i].name); 91 printf(".Bd -literal -compact\n"); 92 printf("Default value: %s\n", config_get_pretty(&config[i], 1)); 93 for (j=0; config[i].description[j]; j++) 94 printf("%s\n", config[i].description[j]); 95 printf(".Ed\n"); 96 } 97 printf(".El\n"); 98 printf(".Sh COMMANDS\n"); 99 printf(".Bl -tag\n"); 100 for (i=0; commands[i].name && commands[i].func; i++) { 101 printf(".It Ic /%s\n", commands[i].name); 102 printf(".Bd -literal -compact\n"); 103 for (j=0; commands[i].description[j]; j++) 104 printf("%s\n", commands[i].description[j]); 105 printf(".Ed\n"); 106 } 107 printf(".El\n"); 108 return 0; 109 } 110 111 main_buf = emalloc(sizeof(struct HistInfo)); 112 main_buf->activity = Activity_none; 113 main_buf->unread = main_buf->ignored = 0; 114 main_buf->server = NULL; 115 main_buf->channel = NULL; 116 main_buf->history = NULL; 117 118 ui_init(); 119 120 if (argc == 2) 121 if (config_read(argv[1]) == -1) 122 die(1, "cannot read config file '%s': %s\n", argv[1], strerror(errno)); 123 124 for (;;) { 125 /* 25 seems fast enough not to cause any visual lag */ 126 if (serv_poll(&servers, 25) < 0) { 127 perror("serv_poll()"); 128 exit(EXIT_FAILURE); 129 } 130 131 pinginact = config_getl("misc.pingtime"); 132 reconnectinterval = config_getl("reconnect.interval"); 133 maxreconnectinterval = config_getl("reconnect.maxinterval"); 134 for (sp = servers; sp; sp = sp->next) { 135 if (sp->rpollfd->revents) { 136 /* received an event */ 137 sp->pingsent = 0; 138 sp->lastrecv = time(NULL); 139 sp->rpollfd->revents = 0; 140 serv_read(sp); 141 } else if (!sp->pingsent && sp->lastrecv && (time(NULL) - sp->lastrecv) >= pinginact) { 142 /* haven't heard from server in pinginact seconds, sending a ping */ 143 serv_write(sp, Sched_now, "PING :ground control to Major Tom\r\n"); 144 sp->pingsent = time(NULL); 145 } else if (sp->pingsent && (time(NULL) - sp->pingsent) >= pinginact) { 146 /* haven't gotten a response in pinginact seconds since 147 * sending ping, this connexion is probably dead now */ 148 serv_disconnect(sp, 1, NULL); 149 hist_format(sp->history, Activity_error, HIST_SHOW, 150 "SELF_CONNECTLOST %s %s %s :No ping reply in %d seconds", 151 sp->name, sp->host, sp->port, pinginact); 152 } else if (sp->status == ConnStatus_notconnected && sp->reconnect && 153 ((time(NULL) - sp->lastconnected) >= maxreconnectinterval || 154 (time(NULL) - sp->lastconnected) >= (sp->connectfail * reconnectinterval))) { 155 /* time since last connected is sufficient to initiate reconnect */ 156 serv_connect(sp); 157 } 158 } 159 160 if (oldselected.channel != selected.channel || oldselected.server != selected.server) { 161 if (windows[Win_nicklist].location) 162 windows[Win_nicklist].refresh = 1; 163 if (windows[Win_buflist].location) 164 windows[Win_buflist].refresh = 1; 165 } 166 167 if (oldselected.history != selected.history) 168 windows[Win_main].refresh = 1; 169 170 oldselected.channel = selected.channel; 171 oldselected.server = selected.server; 172 oldselected.history = selected.history; 173 oldselected.name = selected.name; 174 175 if (uineedredraw) { 176 uineedredraw = 0; 177 ui_redraw(); 178 for (i=0; i < Win_last; i++) 179 windows[i].refresh = 0; 180 continue; 181 } 182 183 refreshed = inputrefreshed = 0; 184 for (i=0; i < Win_last; i++) { 185 if (windows[i].refresh && windows[i].location) { 186 if (windows[i].handler) 187 windows[i].handler(); 188 wnoutrefresh(windows[i].window); 189 windows[i].refresh = 0; 190 refreshed = 1; 191 if (i == Win_input) 192 inputrefreshed = 1; 193 } 194 } 195 doupdate(); 196 197 /* refresh Win_input after any other window to 198 * force ncurses to place the cursor here. */ 199 if (refreshed && !inputrefreshed) 200 wrefresh(windows[Win_input].window); 201 202 ui_read(); 203 } 204 205 return 0; 206 }