fi-1.36/0000755000175000017500000000000012302365027011737 5ustar folkertfolkertfi-1.36/config.h0000644000175000017500000000440312302365027013356 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 863 $ * (C) 2006-2014 by folkert@vanheusden.com */ #include "gen.h" #include "buffer.h" #include "channels.h" #include "grep_filter.h" typedef enum { CNF_BOOL, CNF_VALUE, CNF_STRING, CNF_COLOR } cnf_entry_t; typedef struct { const char *name; cnf_entry_t type; void *p; void (*dofunc)(void); } cnf_entry; extern cnf_entry cnf_pars[]; extern BOOL nick_color; extern BOOL colors_meta, colors_all, use_nonbasic_colors; extern BOOL inverse_window_heading; extern BOOL partial_highlight_match; extern BOOL auto_private_channel; extern const char *part_message, *server_exit_message; extern int max_channel_record_lines; extern int delay_before_reconnect; extern BOOL highlight, fuzzy_highlight; extern BOOL topic_scroll; extern BOOL notice_in_server_channel; extern BOOL allow_invite; extern BOOL store_config_on_exit; extern BOOL show_parts; extern BOOL show_mode_changes; extern BOOL show_nick_change; extern BOOL show_joins; extern BOOL auto_rejoin; extern BOOL mark_personal_messages; extern BOOL update_clock_at_data; extern BOOL irc_keepalive; extern BOOL auto_reconnect; extern BOOL space_after_start_marker; extern BOOL allow_userinfo; extern BOOL ignore_mouse; extern BOOL jumpy_navigation; extern BOOL user_column; extern BOOL mark_meta; extern BOOL full_user; extern BOOL show_headlines; extern BOOL auto_markerline; extern BOOL ignore_unknown_irc_protocol_msgs; extern BOOL only_one_markerline; extern BOOL keep_channels_sorted; extern BOOL create_channel_for_meta_requests; extern int user_column_width; extern const char *userinfo; extern int nick_sleep; extern favorite *favorite_channels; extern int n_favorite_channels, favorite_channels_index; extern char *conf_file; extern const char *log_dir; extern const char *notify_nick; extern const char *dcc_bind_to; extern grep_target *gp; /* grep filter */ extern grep_target *hlgp; /* headline grep filter */ extern const char *finger_str; /* ? */ extern int check_for_mail; int load_config(const char *file); void add_favorite(const char *serv, const char *chan); void free_favorites(void); BOOL save_config(BOOL save_channels, char **err_msg); int config_color_str_convert(const char *in, int linenr, const char *subj); int parse_color_spec(const char *par, int linenr, const char *subj); fi-1.36/colors.h0000644000175000017500000000060112302365027013406 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 671 $ * (C) 2006-2014 by folkert@vanheusden.com */ void free_colors(void); int get_color_mirc(int mfg, int mbg); int find_alloc_colorpair(int fg, int bg); int get_color_ncurses(int ncol_fg, int ncol_bg); int color_str_convert(const char *in); char *color_to_str(int nr); void emit_colorpair(FILE *fh, short pair); int get_n_cpairs(void); fi-1.36/nickcolor.h0000644000175000017500000000061412302365027014074 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 671 $ * (C) 2006-2014 by folkert@vanheusden.com */ #ifndef __NICKCOLOR_H__ #define __NICKCOLOR_H__ typedef enum { LRC, DJB2 } hash_types; typedef struct { int pair; BOOL bold; } nick_color_settings; extern int n_nick_pairs; void init_nick_coloring(hash_types hf); void find_nick_colorpair(const char *nick, nick_color_settings *pncs); #endif fi-1.36/dcc.h0000644000175000017500000000215012302365027012637 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 810 $ * (C) 2006-2014 by folkert@vanheusden.com */ typedef enum { DCC_RECEIVE_FILE, DCC_SEND_FILE, DCC_CHAT } dcc_type_t; typedef enum { DSTATE_NO_CONNECTION = 0, DSTATE_ERROR, DSTATE_TCP_CONNECT, DSTATE_DCC_CONNECTING, DSTATE_CONNECTED1, DSTATE_RUNNING, DSTATE_DISCONNECTED } dcc_conn_state_t; typedef struct { dcc_type_t mode; dcc_conn_state_t state; int fd_conn, ifd; int fd_file; char *filename; int server_nr, channel_nr; time_t last_update; resolve_info ri; } DCC_t; extern DCC_t *dcc_list; extern int n_dcc; extern char *dcc_path; void init_dcc(void); void free_dcc(void); void init_recv_dcc(const char *filename, const char *addr, int port, int server_index, int channel_index); int init_send_dcc(const char *filename, int server_index, int channel_index, const char *nick); void set_dcc_state(int index, dcc_conn_state_t state); dcc_conn_state_t get_dcc_state(int index); int dcc_send(DCC_t *pnt); int dcc_receive(DCC_t *pnt); void free_dcc(void); int register_dcc_events(struct pollfd **pfd, int *n_fd); void process_dcc_events(struct pollfd *pfd, int n_fd); fi-1.36/lf_buffer.c0000644000175000017500000000211012302365027014027 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 695 $ * (C) 2006-2014 by folkert@vanheusden.com */ #include #include #include "lf_buffer.h" #include "utils.h" void init_lf_buffer(lf_buffer_t *p) { p -> data = NULL; p -> size = 0; } void free_lf_buffer(lf_buffer_t *p) { free(p -> data); p -> data = NULL; p -> size = 0; } void add_lf_buffer(lf_buffer_t *p, const char *what, int what_size) { p -> data = (char *)realloc(p -> data, p -> size + what_size + 1); memcpy(&p -> data[p -> size], what, what_size); p -> data[p -> size + what_size] = 0x00; p -> size += what_size; } const char *get_line_lf_buffer(lf_buffer_t *p) { char *out = NULL, *end_in = NULL; int len = 0, left = 0; if (p -> data == NULL || p -> size == 0) return NULL; end_in = strstr(p -> data, "\r\n"); if (!end_in) return NULL; out = strdup(p -> data); len = (int)(end_in - p -> data) + 2; terminate_str(out, '\r'); terminate_str(out, '\n'); left = p -> size - len; if (left > 0) memmove(&p -> data[0], &p -> data[len], left + 1); p -> size = left; return out; } fi-1.36/tcp.c0000644000175000017500000001204612302365027012674 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 806 $ * (C) 2006-2014 by folkert@vanheusden.com */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include "error.h" #include "gen.h" #include "tcp.h" #include "utils.h" void set_ka(int fd) { int on = 1, interval = 31; setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof on); #ifdef __linux__ setsockopt(fd, SOL_TCP, TCP_KEEPIDLE, &interval, sizeof interval); setsockopt(fd, SOL_TCP, TCP_KEEPINTVL, &interval, sizeof interval); #endif } int set_no_delay(int fd) { int flag = 1; if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(int)) < 0) { LOG("could not set TCP_NODELAY on socket (%s)\n", strerror(errno)); return -1; } return 0; } int setup_nonblocking_socket(void) { /* create socket */ int handle = socket(AF_INET, SOCK_STREAM, 0); if (handle == -1) return -1; /* set fd to non-blocking */ if (fcntl(handle, F_SETFL, O_NONBLOCK) == -1) { int e = errno; close(handle); errno = e; return -1; } set_ka(handle); set_no_delay(handle); return handle; } char * get_endpoint_name(int fd) { char *result = NULL; struct sockaddr_storage addr; socklen_t addr_len = sizeof addr; if (getpeername(fd, (struct sockaddr *)&addr, &addr_len) == -1) asprintf(&result, "getpeername failed: %s (%d)", strerror(errno), errno); else { char peer_name[4096] = { 0 }; int port = -1, rc = 0; if (addr.ss_family == AF_INET) { struct sockaddr_in *s = (struct sockaddr_in *)&addr; port = ntohs(s->sin_port); rc = inet_ntop(AF_INET, &s->sin_addr, peer_name, sizeof peer_name) != NULL; } else /* ipv6 */ { struct sockaddr_in6 *s = (struct sockaddr_in6 *)&addr; port = ntohs(s->sin6_port); rc = inet_ntop(AF_INET6, &s->sin6_addr, peer_name, sizeof peer_name) != NULL; } if (rc) asprintf(&result, "[%s]:%d", peer_name, port); else result = strdup("inet_ntop failed"); } return result; } void free_resolve_info(resolve_info *ri) { free(ri -> alist); freeaddrinfo(ri -> result); } BOOL resolve(const char *host, int portnr, resolve_info *ri, char **message) { char portnr_str[8] = { 0 }; struct addrinfo hints, **alist = NULL, *result = NULL, *rp = NULL; int alist_n = 0, rc = 0; memset(ri, 0x00, sizeof(*ri)); free(*message); *message = NULL; memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; /* For wildcard IP address */ hints.ai_protocol = 0; /* Any protocol */ hints.ai_canonname = NULL; hints.ai_addr = NULL; hints.ai_next = NULL; snprintf(portnr_str, sizeof portnr_str, "%d", portnr); rc = getaddrinfo(host, portnr_str, &hints, &result); if (rc != 0) { asprintf(message, "problem resolving %s: %s", host, gai_strerror(rc)); return FALSE; } for(rp = result; rp != NULL; rp = rp->ai_next) { alist = (struct addrinfo **)realloc(alist, (alist_n + 1) * sizeof(struct addrinfo *)); alist[alist_n++] = rp; } if (alist_n == 0) { free(alist); freeaddrinfo(result); asprintf(message, "resolving %s returned nothing", host); return FALSE; } memset(ri, 0x00, sizeof(*ri)); ri -> result = result; ri -> alist = alist; ri -> alist_n = alist_n; return TRUE; } const char *get_ip(resolve_info *ri) { char peer_name[4096] = { 0 }; struct addrinfo *rp = ri -> alist[ri -> index]; if (getnameinfo(rp -> ai_addr, sizeof(*rp -> ai_addr), peer_name, sizeof(peer_name), NULL, 0, NI_NUMERICHOST) == -1) return strdup("(cannot resolve)"); return strdup(peer_name); } int connect_to(resolve_info *ri, char **message) { struct addrinfo *rp = ri -> alist[ri -> index]; int fd = socket(rp -> ai_family, rp -> ai_socktype, rp -> ai_protocol); if (fd == -1) { asprintf(message, "could not create socket: %s (%d)", strerror(errno), errno); ri -> index = (ri -> index + 1) % ri -> alist_n; return -1; } if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) { asprintf(message, "fcnt(O_NONBLOCK) failed: %s (%d)", strerror(errno), errno); close(fd); ri -> index = (ri -> index + 1) % ri -> alist_n; return -1; } set_ka(fd); set_no_delay(fd); /* connect to peer */ if (connect(fd, rp -> ai_addr, rp -> ai_addrlen) == 0) return fd; if (errno == EINPROGRESS) { /* not yet made but might be fine*/ return fd; } asprintf(message, "connect failed: %s (%d)", strerror(errno), errno); close(fd); ri -> index = (ri -> index + 1) % ri -> alist_n; return -1; } cstate_t check_connection_progress(int fd) { int optval = 0; socklen_t optvallen = sizeof optval; /* see if the connect succeeded or failed */ if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &optval, &optvallen) == -1) { LOG("getsockopt failed\n"); return TCS_ERROR; } /* no error? */ if (optval == 0) return TCS_CONNECTED; if (optval != EINPROGRESS) { errno = optval; return TCS_ERROR; } return TCS_IN_PROGRESS; } fi-1.36/servers.c0000644000175000017500000004444312302365027013605 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 863 $ * (C) 2006-2014 by folkert@vanheusden.com */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "error.h" #include "gen.h" #include "utils.h" #include "term.h" #include "user.h" #include "channels.h" #include "servers.h" #include "irc.h" #include "loop.h" #include "config.h" #include "tcp.h" #include "utils.h" #include "string_array.h" void free_server(int server_index) { server *pc = &server_list[server_index]; int loop; if (pc -> state >= STATE_IRC_CONNECTING) close(pc -> fd); myfree(pc -> server_host); myfree(pc -> server_real); myfree(pc -> nickname); myfree(pc -> nickname2); myfree(pc -> description); myfree(pc -> username); myfree(pc -> password); myfree(pc -> user_complete_name); myfree(pc -> prev_cmd); free_resolve_info(&pc -> ri); free_channel_list(server_index); for(loop=0; loop n_channels; loop++) free_channel(&pc -> pchannels[loop]); free_string_array(&pc -> send_after_login); free_string_array(&pc -> auto_join); myfree(pc -> pchannels); free_lf_buffer(&pc -> io_buffer); } void close_server(int server_index, BOOL leave_channel) { LOG("close server %d\n", server_list[server_index].fd); set_state(server_index, STATE_DISCONNECTED); irc_quit(server_list[server_index].fd, server_exit_message); if (leave_channel) { int loop; for(loop=server_list[server_index].n_channels - 1; loop>-1; loop--) close_channel(server_index, loop, leave_channel); } close(server_list[server_index].fd); server_list[server_index].fd = -1; } void toggle_server_minimized(int toggle_index) { server_list[vc_list -> server_index[toggle_index]].minimized = ! server_list[vc_list -> server_index[toggle_index]].minimized; } int find_server_index(const char *server_name) { int loop; for(loop=0; loop description && strcasecmp(p -> description, server_name) == 0) return loop; if (strcasecmp(p -> server_host, server_name) == 0) return loop; } return -1; } void find_server_channel_index(const char *server_name, const char *channel_name, int *s_i, int *c_i) { int si = -1; *s_i = *c_i = -1; for(si=0; si description && strcasecmp(p -> description, server_name) == 0) || strcasecmp(p -> server_host, server_name) == 0)) check = TRUE; if (check) { ci = find_channel_index(si, channel_name); if (ci != -1) { *s_i = si; *c_i = ci; break; } } } } int add_server(const char *host_and_port_in, const char *username, const char *password, const char *nickname, const char *complete_name, const char *description) { char *host_and_port = strdup(host_and_port_in); char *colon = strchr(host_and_port, ':'); /* grow & initialize list */ server_list = realloc(server_list, (n_servers + 1)* sizeof(server)); memset(&server_list[n_servers], 0x00, sizeof(server)); server_list[n_servers].fd = -1; /* host [+ port] */ if (colon) { *colon = 0x00; server_list[n_servers].server_port = atoi(colon + 1); } else { server_list[n_servers].server_port = DEFAULT_IRC_PORT; } server_list[n_servers].server_host = host_and_port; /* general info */ if (description) server_list[n_servers].description = strdup(description); server_list[n_servers].username = strdup(username ? username : ""); if (password) server_list[n_servers].password = strdup(password); server_list[n_servers].nickname = strdup(nickname); server_list[n_servers].user_complete_name = strdup(complete_name ? complete_name : ""); server_list[n_servers].channel_list_complete = FALSE; server_list[n_servers].channel_list = NULL; server_list[n_servers].channel_list_n = 0; init_string_array(&server_list[n_servers].auto_join); init_string_array(&server_list[n_servers].send_after_login); server_list[n_servers].reconnect_delay = DEFAULT_RECONNECT_DELAY; /* initialize server entry */ server_list[n_servers].state = STATE_NO_CONNECTION; /* allocate and init channel */ (void)add_channel(n_servers, "server messages"); n_servers++; return n_servers - 1; } const char *gen_random_nick() { long int some_value = lrand48(); char *nick = NULL; asprintf(&nick, "firc%lx", some_value); return nick; } void create_default_server(void) { if (n_servers == 0) { server *ps = NULL; const char *nick = gen_random_nick(); popup_notify(TRUE, "No server in config file (empty?): creating example"); add_server("irc.vanheusden.com:6667", nick, "bla123", nick, "unconfigured f-irc user", "example server"); myfree(nick); ps = &server_list[0]; add_to_string_array(&ps -> auto_join, "#f-irc"); store_config_on_exit= TRUE; popup_notify(TRUE, "Since this probably the first run: press F1 for help"); } } void set_state(int server_index, conn_state_t state) { LOG("set state for server %d to %d\n", server_index, state); server_list[server_index].state = state; server_list[server_index].state_since = time(NULL); update_channel_border(server_index); show_channel_names_list(); } conn_state_t get_state(int server_index) { return server_list[server_index].state; } long int get_state_age(int server_index) { long int age = time(NULL) - server_list[server_index].state_since; return age; } int get_server_color(int server_index) { conn_state_t state = get_state(server_index); BOOL error_state = state == STATE_NO_CONNECTION || state == STATE_ERROR || state == STATE_DISCONNECTED; BOOL busy_state = state != STATE_RUNNING && error_state == FALSE; if (error_state) return error_colorpair; if (busy_state) return temp_colorpair; return -1; } void restart_server(int sr) { LOG("restart server %d\n", server_list[sr].fd); close(server_list[sr].fd); server_list[sr].state = STATE_NO_CONNECTION; server_list[sr].state_since = 0; server_list[sr].channel_list_complete = FALSE; } void try_next_server(int sr) { server_list[sr].ri.index = (server_list[sr].ri.index + 1) % server_list[sr].ri.alist_n; server_list[sr].ri.attempt = 0; server_list[sr].reconnect_delay = DEFAULT_RECONNECT_DELAY; } int register_server_events(struct pollfd **pfd, int *n_fd) { int loop = 0, redraw_rc = 0; for(loop=0; loop= delay_before_reconnect) { char *message = NULL; update_statusline(loop, 0, "Resolving %s", server_list[loop].server_host); mydoupdate(); if (resolve(server_list[loop].server_host, server_list[loop].server_port, &server_list[loop].ri, &message) && server_list[loop].ri.alist_n > 0) set_state(loop, STATE_TCP_CONNECT); else { update_statusline(loop, 0, "Problem resolving %s: %s", server_list[loop].server_host, message); set_state(loop, STATE_ERROR); free(message); } redraw_rc = 1; } } else if (get_state(loop) == STATE_ERROR) { } else if (get_state(loop) == STATE_TCP_CONNECT) { char *message = NULL; const char *ip = get_ip(&server_list[loop].ri); if ((server_list[loop].fd = connect_to(&server_list[loop].ri, &message)) == -1) { set_state(loop, STATE_ERROR); close(server_list[loop].fd); update_statusline(loop, 0, "Cannot connect to %s:%d (%s), %s", server_list[loop].server_host, server_list[loop].server_port, message, ip); } else { set_state(loop, STATE_IRC_CONNECTING); assert(server_list[loop].fd != -1); server_list[loop].ifd = add_poll(pfd, n_fd, server_list[loop].fd, POLLOUT | POLLHUP); update_statusline(loop, 0, "Connecting to %s:%d (%s)", server_list[loop].server_host?server_list[loop].server_host : "?", server_list[loop].server_port, ip); LOG("server %d has fd %d\n", loop, server_list[loop].fd); } myfree(ip); free(message); redraw_rc = 1; } else if (get_state(loop) == STATE_IRC_CONNECTING) { assert(server_list[loop].fd != -1); server_list[loop].ifd = add_poll(pfd, n_fd, server_list[loop].fd, POLLOUT | POLLHUP); } else if (get_state(loop) == STATE_CONNECTED1) { free(server_list[loop].server_real); server_list[loop].server_real = get_endpoint_name(server_list[loop].fd); if (set_no_delay(server_list[loop].fd) == -1) { set_state(loop, STATE_DISCONNECTED); close(server_list[loop].fd); update_statusline(loop, 0, "Connection to %s:%d (%s) closed (1)", server_list[loop].server_host, server_list[loop].server_port, server_list[loop].server_real); try_next_server(loop); } /* login */ else if (irc_login1(&server_list[loop]) == -1) { set_state(loop, STATE_DISCONNECTED); close(server_list[loop].fd); if (++server_list[loop].ri.attempt > 3) try_next_server(loop); update_statusline(loop, 0, "Connection to %s:%d (%s) closed (2)", server_list[loop].server_host, server_list[loop].server_port, server_list[loop].server_real); } else { update_statusline(loop, 0, "Connected to %s", server_list[loop].server_real); update_statusline(loop, 0, "%s: send NICK, sleeping for %d seconds", server_list[loop].server_host, nick_sleep); } set_state(loop, STATE_CONNECTED2); assert(server_list[loop].fd != -1); server_list[loop].ifd = add_poll(pfd, n_fd, server_list[loop].fd, POLLIN | POLLHUP); redraw_rc = 1; } else if (get_state(loop) == STATE_CONNECTED2) { if (get_state_age(loop) >= nick_sleep) { if (irc_login2(&server_list[loop]) == -1) { set_state(loop, STATE_DISCONNECTED); close(server_list[loop].fd); update_statusline(loop, 0, "Connection to %s:%d (%s) closed (3)", server_list[loop].server_host, server_list[loop].server_port, server_list[loop].server_real); if (++server_list[loop].ri.attempt > 3) try_next_server(loop); redraw_rc = 1; } set_state(loop, STATE_LOGGING_IN); } } else if (get_state(loop) == STATE_LOGGING_IN) { assert(server_list[loop].fd != -1); server_list[loop].ifd = add_poll(pfd, n_fd, server_list[loop].fd, POLLIN | POLLHUP); server_list[loop].must_send_after_login = 1; } else if (get_state(loop) == STATE_RUNNING) { int rc = 0; assert(server_list[loop].fd != -1); server_list[loop].ifd = add_poll(pfd, n_fd, server_list[loop].fd, POLLIN | POLLHUP); if (server_list[loop].must_send_after_login == 1) { /* re-join any channels (when this run was because of a re-(!)connect) */ if (server_list[loop].n_channels && rc == 0) { int ch_index = 0; update_statusline(loop, 0, "re-join %d channels", server_list[loop].n_channels); for(ch_index=1; ch_index %s", ch); c_rc = irc_join(server_list[loop].fd, ch); rc |= c_rc; if (c_rc) { update_statusline(loop, 0, "...failed"); break; } } } } if ((string_array_get_n(&server_list[loop].send_after_login) || string_array_get_n(&server_list[loop].auto_join)) && rc == 0) { int sal_index, ch_index; update_statusline(loop, 0, "Doing %d auto commands (send_after_login)", string_array_get_n(&server_list[loop].send_after_login)); /* send list of things to do when connecting */ for(sal_index=0; sal_index %s", cmd); c_rc = do_send(server_list[loop].fd, "%s", cmd); rc |= c_rc; if (c_rc) { update_statusline(loop, 0, "...failed"); break; } } /* re-join any configured channels */ update_statusline(loop, 0, "join %d channels", string_array_get_n(&server_list[loop].auto_join)); for(ch_index=0; ch_index %s", ch); c_rc = irc_join(server_list[loop].fd, ch); rc |= c_rc; if (c_rc) { update_statusline(loop, 0, "...failed"); break; } } } show_channel_names_list(); /* update_statusline(loop, 0, "requesting list of channels"); rc |= irc_list(server_list[loop].fd); */ server_list[loop].must_send_after_login = 0; } if (rc) { LOG("failure close server %d\n", server_list[loop].fd); close(server_list[loop].fd); update_statusline(loop, 0, "Connection to %s:%d (%s) closed (4)", server_list[loop].server_host, server_list[loop].server_port, server_list[loop].server_real); set_state(loop, STATE_DISCONNECTED); close(server_list[loop].fd); if (++server_list[loop].ri.attempt > 3) try_next_server(loop); } redraw_rc = 1; } else if (get_state(loop) == STATE_DISCONNECTED) { /* do nothing */ /* if user writes something or does /reconnect, start reconnect */ if (server_list[loop].reconnect_delay < DEFAULT_MAX_RECONNECT_DELAY) { double progress = (double)server_list[loop].reconnect_delay / (double)DEFAULT_MAX_RECONNECT_DELAY; double factor = 2.0 - 0.9 * progress; int ifactor = (int)(factor * 1000.0); server_list[loop].reconnect_delay = (server_list[loop].reconnect_delay * ifactor) / 1000; } } else { update_statusline(loop, 0, "Server socket %d (%s:%d) in unknown state %d", server_list[loop].fd, server_list[loop].server_host, server_list[loop].server_port, server_list[loop].state); redraw_rc = 1; } } return redraw_rc; } int process_server(int cur_server) { server *ps = &server_list[cur_server]; char str_buffer[65536] = { 0 }; int n_read = read(ps -> fd, str_buffer, sizeof str_buffer); if (n_read > 0) { time_t now = time(NULL); int t_diff = now - ps -> ts_bytes; if (t_diff > 30) { ps -> prev_bps = ps -> bytes / t_diff; ps -> bytes = 0; ps -> ts_bytes = now; } ps -> bytes += n_read; ps -> ts_last_action = now; /* move data to buffer */ add_lf_buffer(&ps -> io_buffer, str_buffer, n_read); /* see if there's anything to process */ for(;;) { char *line = (char *)get_line_lf_buffer(&ps -> io_buffer); /* FIXME char cast */ if (!line) break; LOG("IN: %s\n", line); if (process_server_do_line(cur_server, line) == -1) { LOG("process_server_do_line returned -1\n"); return -1; } myfree(line); } } else if (n_read == 0) /* connection closed */ { LOG("read() returned 'connection closed'\n"); return -1; } else /* -1: error */ { if (errno != EINTR && errno != EAGAIN) { LOG("read returned error %d\n", errno); update_statusline(cur_server, 0, "read() for server %s:%d failed, reason: %s (%d)", ps -> server_host, ps -> server_port, strerror(errno), errno); return -1; } } return 0; } void process_server_events(struct pollfd *pfd, int n_fd) { int si = 0; for(si=0; si revents & POLLHUP) || (cpfd -> revents & POLLNVAL)) { update_statusline(si, 0, "Connection to %s:%d closed (9)", server_list[si].server_host, server_list[si].server_port); set_state(si, STATE_DISCONNECTED); close(server_list[si].fd); server_list[si].fd = -1; } else if (cpfd -> revents & POLLOUT) /* connected? */ { cstate_t cstate = check_connection_progress(server_list[si].fd); if (cstate == TCS_CONNECTED) { set_state(si, STATE_CONNECTED1); update_statusline(si, 0, "Connected to %s:%d (%s)", server_list[si].server_host, (int)server_list[si].server_port, str_or_nothing(server_list[si].description)); } else if (cstate == TCS_ERROR) { set_state(si, STATE_ERROR); update_statusline(si, 0, "Cannot connect to %s:%d (%s), reason: %s (%d)", server_list[si].server_host, server_list[si].server_port, str_or_nothing(server_list[si].description), strerror(errno), errno); close(server_list[si].fd); server_list[si].fd = -1; } } /* any traffic? */ if ((cpfd -> revents & POLLIN) && server_list[si].fd != -1) { if (process_server(si) == -1) { char prev_state = server_list[si].state; update_statusline(si, 0, "Connection to %s:%d closed (8)", server_list[si].server_host, server_list[si].server_port); if (prev_state == STATE_ERROR) set_state(si, STATE_ERROR); else set_state(si, STATE_DISCONNECTED); close(server_list[si].fd); } } } } int find_in_autojoin(int sr, const char *channel_name) { return find_str_in_string_array(&server_list[sr].auto_join, channel_name, TRUE); } void add_autojoin(int sr, char *channel_name) { add_to_string_array(&server_list[sr].auto_join, channel_name); } void remove_autojoin(int sr, int aj_nr) { del_nr_from_string_array(&server_list[sr].auto_join, aj_nr); } void server_set_additional_nick(int sr, const char *n2) { myfree(server_list[sr].nickname2); server_list[sr].nickname2 = strdup(n2); } int compare_channel_list_item(const void *a, const void *b) { channel_topic_t *pa = (channel_topic_t *)a; channel_topic_t *pb = (channel_topic_t *)b; return strcasecmp(pa -> channel, pb -> channel); } void free_channel_list(int sr) { server *ps = &server_list[sr]; int index = 0; for(index=0; index channel_list_n; index++) { myfree(ps -> channel_list[index].channel); myfree(ps -> channel_list[index].topic); } myfree(ps -> channel_list); ps -> channel_list_n = 0; ps -> channel_list = NULL; ps -> channel_list_complete = FALSE; } int compare_channel_for_sort(const void *a, const void *b) { channel *pa = (channel *)a; channel *pb = (channel *)b; return strcasecmp(pa -> channel_name, pb -> channel_name); } void sort_channels(int sr) { server *ps = &server_list[sr]; /* server channel must always be at position 0 */ if (ps -> n_channels > 1) qsort(&ps -> pchannels[1], ps -> n_channels - 1, sizeof(channel), compare_channel_for_sort); } server *gsr(int sr) { return &server_list[sr]; } fi-1.36/loop.c0000644000175000017500000005617512302365027013072 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 819 $ * (C) 2006-2014 by folkert@vanheusden.com */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gen.h" #include "error.h" #include "theme.h" #include "term.h" #include "buffer.h" #include "channels.h" #include "servers.h" #include "loop.h" #include "utils.h" #include "tcp.h" #include "irc.h" #include "dcc.h" #include "main.h" #include "names.h" #include "key_value.h" #include "wordcloud.h" #include "config.h" #include "colors.h" #include "ansi.h" #include "soundex.h" #include "user.h" #include "ctcp.h" #include "headlines.h" #include "help.h" NEWWIN *chat_window_border = NULL, *channel_window_border = NULL, *input_window_border = NULL; NEWWIN *chat_window = NULL, *input_window = NULL, *channel_window = NULL, *topic_line_window = NULL, *headline_window = NULL; visible_channels *vc_list = NULL; server *server_list = NULL; int n_servers = 0; layout_theme theme; int input_window_width = 40; BOOL notified_logging_error = FALSE; int topic_scroll_offset = 0; int current_server = 0, current_server_channel_nr = 0; double last_topic_show_ts = 0.0; void gen_display_markerline(NEWWIN *win, time_t ts) { char *ts_str = ctime(&ts); terminate_str(ts_str, '\n'); display_markerline(win, ts_str); } void add_markerline(int sr, int ch) { server *ps = &server_list[sr]; channel *pc = &ps -> pchannels[ch]; if (!latest_is_markerline(pc -> pbuffer)) { if (only_one_markerline) delete_type(pc -> pbuffer, BET_MARKERLINE); add_to_buffer(pc -> pbuffer, NULL, NULL, TRUE, sr, ch); if (sr == current_server && ch == current_server_channel_nr) { time_t now = time(NULL); gen_display_markerline(chat_window, now); } } } void add_markerline_to_all(void) { int sr = 0; for(sr=0; sr n_channels; ch++) add_markerline(sr, ch); } } int myisdigit(int c) { return c >= '0' && c <= '9'; } int get_color(const char *string, int *num) { char c1 = string[0]; if (myisdigit(c1)) { char c2 = string[1]; if (myisdigit(c2)) { *num = (c1 - '0') * 10 + (c2 - '0'); return 2; } *num = c1 - '0'; return 1; } return 0; } BOOL nick_hit(const char *nick, const char *str) { if (fuzzy_highlight && fuzzy_match(str, nick, NULL)) return TRUE; if (strcasestr(str, nick)) return TRUE; return FALSE; } BOOL find_matches(const char *haystack, const char *needle, char *bitmap) { int nlen = strlen(needle); const char *index = haystack; BOOL match = FALSE; for(;;) { int pos = -1; index = strcasestr(index, needle); if (!index) break; match = TRUE; pos = (int)(index - haystack); memset(&bitmap[pos], '1', nlen); index += nlen; } return match; } BOOL find_extra_highlights(const char *haystack, char *bitmap) { BOOL match = FALSE; int index = 0; for(index=0; index bold; if (bold) mywattron(win -> win, A_BOLD); else mywattroff(win -> win, A_BOLD); last_pair = pncs -> pair; color_on(win, last_pair); } else if (hl == TRUE && colors_meta) { if (!partial_highlight_match && !force_partial_highlight) { last_pair = highlight_colorpair; color_on(win, last_pair); } } else if (line_type == BET_META && colors_meta) { last_pair = meta_colorpair; color_on(win, last_pair); } for(outer_loop=0; outer_loop win); int start_word = 0, wordlen = 0; if (outer_loop == 0 && x > 0) wprintw(win -> win, "\n"); while(string[outer_loop] == ' ') { wprintw(win -> win, " "); outer_loop++; } x = getcurx(win -> win); start_word = outer_loop; while(string[outer_loop] != ' ' && string[outer_loop] != 0x00 && string[outer_loop] != '\n') outer_loop++; /* count size of word, might be utf8! */ /* FIXME do not count escapes (^B, ^C) */ for(loop=start_word; loop win -> ncols && wordlen < win -> ncols / 3) wprintw(win -> win, "\n"); for(loop=start_word; loop reset attributes */ { inverse = FALSE; underline = FALSE; bold = FALSE; bright = FALSE; reset_attributes(win); if (last_pair != -1) { color_off(win, last_pair); last_pair = -1; } loop++; } else if (string[loop] == 22) /* ^V -> inverse */ { inverse = !inverse; if (inverse) mywattron(win -> win, A_REVERSE); else mywattroff(win -> win, A_REVERSE); loop++; } else if (string[loop] == 31) /* ^_ -> underline */ { underline = !underline; if (underline) mywattron(win -> win, A_UNDERLINE); else mywattroff(win -> win, A_UNDERLINE); loop++; } else if (string[loop] == 2) /* ^B -> bold */ { bold = !bold; if (bold) mywattron(win -> win, A_BOLD); else mywattroff(win -> win, A_BOLD); loop++; } else if (string[loop] == 3) /* ^c */ { int fg = -1, bg = -1; if (last_pair != -1) { color_off(win, last_pair); last_pair = -1; if (bright) { mywattroff(win -> win, A_STANDOUT); bright = FALSE; } } loop++; /* skip ^c */ loop += get_color(&string[loop], &fg); /* skip over this color */ if (string[loop] == ',') { loop++; /* skip , */ loop += get_color(&string[loop], &bg); } last_pair = get_color_mirc(fg, bg); color_on(win, last_pair); hl = FALSE; } else { wchar_t dest = 0; const char *dummy = &string[loop], *dummy_start = dummy; int skip_n = 0; mbsrtowcs(&dest, &dummy, 1, NULL); waddnwstr(win -> win, &dest, 1); skip_n = (int)(dummy - dummy_start); if (skip_n <= 0) skip_n = 1; loop += skip_n; } } } if (last_pair != -1) color_off(win, last_pair); reset_attributes(win); color_on(win, default_colorpair); if (!fit) { mywattron(win -> win, A_REVERSE); waddch(win -> win, '>'); mywattroff(win -> win, A_REVERSE); } myfree(pm_bitmap); myfree(string); } int log_channel(int iserver, int channel_nr, const char *user, const char *string, BOOL meta_hl) { int rc = 0; char *str_buffer = NULL; time_t rnow = time(NULL); struct tm *ptm = localtime(&rnow); char *ts = NULL; server *ps = &server_list[iserver]; channel *pc = &ps -> pchannels[channel_nr]; BOOL output_to_terminal = iserver == current_server && channel_nr == current_server_channel_nr; char *headline = NULL; asprintf(&headline, "%s(%s) %s: %s\n", pc -> channel_name, ps -> description, str_or_nothing(user), string); if (log_to_file(iserver, channel_nr, user, string) == -1) { if (notified_logging_error == FALSE) { popup_notify(FALSE, "Problem logging to file:\n%s\n(this message is shown once)", strerror(errno)); notified_logging_error = TRUE; } } if (user != NULL && nick_hit(ps -> nickname, string) && strcasecmp(user, ps -> nickname) != 0) { if (notify_nick) { const char *pars[3] = { NULL }; pars[0] = user; pars[1] = string; pars[2] = NULL; run(notify_nick, pars); } add_headline(TRUE, headline); } else { check_headline_matches(string, headline); } if (pc -> last_entry) { pc -> t_event += rnow - pc -> last_entry; pc -> n_event++; if (pc -> n_event > 100) { pc -> t_event /= (double)pc -> n_event; pc -> t_event *= 5; pc -> n_event = 5; } } if (theme.show_time) asprintf(&ts, "%02d:%02d:%02d]", ptm -> tm_hour, ptm -> tm_min, ptm -> tm_sec); if (user) { BOOL me = FALSE; const char *temp = exec_and_strip_ctcp(iserver, channel_nr, user, string, &me); if (me) asprintf(&str_buffer, "%s %s", ts, temp); else if (full_user) asprintf(&str_buffer, "%s %s: %s", ts, user, temp); else { char *nick_only = strdup(user); terminate_str(nick_only, '!'); asprintf(&str_buffer, "%s %s: %s", ts, nick_only, temp); free(nick_only); } myfree(temp); } else { asprintf(&str_buffer, "%s > %s", ts, string); } if (user == NULL || is_ignored(iserver, channel_nr, user) == FALSE) { time_t last_entry = pc -> last_entry; if (rnow - last_entry >= 86400 && last_entry > 0) { char *meta_str_buffer = NULL; /* output date/time last msg */ char *time_str1 = strdup(ctime(&last_entry)), *time_str2 = NULL; if (time_str1 == NULL) time_str1 = strdup("?"); else { char *dummy = strchr(time_str1, '\n'); if (dummy) *dummy = 0x00; } /* display current date/time */ time_str2 = strdup(ctime(&rnow)); if (!time_str2) time_str2 = strdup("?"); else terminate_str(time_str2, '\n'); asprintf(&meta_str_buffer, "last message: %s (%ld), current time: %s (%ld)", time_str1, last_entry, time_str2, rnow); add_to_buffer(pc -> pbuffer, meta_str_buffer, NULL, TRUE, iserver, channel_nr); if (output_to_terminal) output_to_window(chat_window, meta_str_buffer, ps -> nickname, FALSE, NULL, FALSE, TRUE); myfree(meta_str_buffer); myfree(time_str2); myfree(time_str1); } add_to_buffer(pc -> pbuffer, str_buffer, user, meta_hl, iserver, channel_nr); pc -> last_entry = rnow; if (output_to_terminal) { nick_color_settings ncs; find_nick_colorpair(user, &ncs); output_to_window(chat_window, str_buffer, ps -> nickname, meta_hl, nick_color ? &ncs : NULL, FALSE, TRUE); } else { /* set mark on window */ if (nick_hit(ps -> nickname, string) || (is_channel(pc -> channel_name) == FALSE && channel_nr != 0 && mark_personal_messages)) pc -> new_entry = YOU; else { if (meta_hl == TRUE) { if (pc -> new_entry == NONE && mark_meta) pc -> new_entry = META; } else { if (pc -> new_entry == NONE || pc -> new_entry == META) pc -> new_entry = MISC; } } set_new_line_received(); } } free(headline); myfree(str_buffer); myfree(ts); return rc; } void update_statusline(int serv, int chan, const char *fmt, ...) { char *str_buffer = NULL; va_list ap; va_start(ap, fmt); (void)vasprintf(&str_buffer, fmt, ap); va_end(ap); if (serv == -1) serv = 0; log_channel(serv, chan, NULL, str_buffer, TRUE); LOG("%s\n", str_buffer); free(str_buffer); } void create_windows() { int ch_win_height = max_y - 2, ch_win_offset = 0; int all_win_offset = 1, lines_min = 2; bkgd(default_colorpair); /* while building the display, ignore SIGWINCH */ if (signal(SIGWINCH, SIG_IGN) == SIG_ERR) error_exit(TRUE, "signal (SIGWINCH/SIG_IGN) failed"); if (topic_line_window) delete_window(topic_line_window); topic_line_window = create_window_xy(0, 0, 1, max_x); if (headline_window) delete_window(headline_window); if (show_headlines) { headline_window = create_window_xy(1, 0, 1, max_x); all_win_offset++; lines_min++; ch_win_height--; } scrollok(topic_line_window -> win, FALSE); color_on(topic_line_window, default_colorpair); mywbkgd(topic_line_window, default_colorpair); if (chat_window_border) delete_window(chat_window_border); if (chat_window) delete_window(chat_window); if (theme.chat_window_border == TRUE && max_y > 2) { chat_window_border = create_window_xy(all_win_offset, 0, max_y - lines_min, max_x - theme.channellist_window_width); color_on(chat_window_border, default_colorpair); box(chat_window_border -> win, 0, 0); mywbkgd(chat_window_border, default_colorpair); chat_window = create_window_xy(all_win_offset + 1, 1, max_y - (lines_min + 2), max_x - (theme.channellist_window_width + 2)); } else { chat_window = create_window_xy(all_win_offset, 0, max_y - lines_min, max_x - theme.channellist_window_width); } mywbkgd(chat_window, default_colorpair); color_on(chat_window, default_colorpair); scrollok(chat_window -> win, TRUE); if (input_window_border) delete_window(input_window_border); if (input_window) delete_window(input_window); if (space_after_start_marker) input_window_width = max_x - 4; else input_window_width = max_x - 2; input_window_border = create_window_xy(max_y - 1, 0, 1, input_window_width + (space_after_start_marker ? 4 : 2)); input_window = create_window_xy(max_y - 1, (space_after_start_marker ? 2 : 1), 1, input_window_width); color_on(input_window, default_colorpair); mywbkgd(input_window, default_colorpair); if (channel_window_border) delete_window(channel_window_border); if (channel_window) delete_window(channel_window); if (word_cloud_n > 0 && word_cloud_win_height > 0) { ch_win_height -= word_cloud_win_height + 2/* =border size */; ch_win_offset = word_cloud_win_height + 2 + all_win_offset; wc_window_border = create_window_xy(all_win_offset, max_x - theme.channellist_window_width, word_cloud_win_height + 2, theme.channellist_window_width); wc_window = create_window_xy(all_win_offset + 1, max_x - (theme.channellist_window_width - 1), word_cloud_win_height, theme.channellist_window_width - 2); mywbkgd(wc_window_border, default_colorpair); mywbkgd(wc_window, default_colorpair); color_on(wc_window, default_colorpair); color_on(wc_window_border, default_colorpair); } else { ch_win_offset = all_win_offset; } if (theme.channellist_border == TRUE) { channel_window_border = create_window_xy(ch_win_offset, max_x - theme.channellist_window_width, ch_win_height, theme.channellist_window_width); color_on(channel_window_border, default_colorpair); channel_window = create_window_xy(1 + ch_win_offset, max_x - (theme.channellist_window_width - 1), ch_win_height - 2, theme.channellist_window_width - 2); mywbkgd(channel_window_border, default_colorpair); } else { channel_window = create_window_xy(ch_win_offset, max_x - theme.channellist_window_width, ch_win_height, theme.channellist_window_width); } color_on(channel_window, default_colorpair); mywbkgd(channel_window, default_colorpair); show_channel_names_list(); /* set signalhandler for terminal resize */ if (signal(SIGWINCH, do_resize) == SIG_ERR) error_exit(TRUE, "signal(SIGWINCH/SIG_IGN) failed"); } BOOL show_clock(time_t prev_ts, time_t now_ts, BOOL new_data) { if (theme.show_clock == TRUE) { BOOL update_clock = FALSE; int time_diff = now_ts - prev_ts; if (update_clock_at_data) update_clock = time_diff > 0 && new_data; else update_clock = time_diff > 0; if (update_clock) { struct tm *ptm = localtime(&now_ts); prev_ts = now_ts; mvwprintw(topic_line_window -> win, 0, topic_line_window -> ncols - 9, " %02d:%02d:%02d", ptm -> tm_hour, ptm -> tm_min, ptm -> tm_sec); return TRUE; } } return FALSE; } BOOL show_topic(int sr, int ch, double now_tsd) { channel *pch = NULL; BOOL rc = FALSE; if (sr != -1) pch = &server_list[sr].pchannels[ch]; if (pch == NULL) { } else if (topic_scroll && now_tsd - last_topic_show_ts >= 0.1) { char *cur_topic = strdup(str_or_nothing(pch -> topic)); int ct_len = strlen(cur_topic); int n_shown = 0, cur_offset = 0; if (topic_scroll_offset >= ct_len) topic_scroll_offset = 0; werase(topic_line_window -> win); cur_offset = topic_scroll_offset; while(n_shown < topic_line_window -> ncols) { mvwprintw(topic_line_window -> win, 0, n_shown, "%s ", &cur_topic[cur_offset]); n_shown += ct_len - cur_offset + 1/*space!*/; cur_offset = 0; } topic_scroll_offset++; myfree(cur_topic); rc = TRUE; last_topic_show_ts = now_tsd; } else if (pch -> topic_changed == TRUE) { werase(topic_line_window -> win); output_to_window(topic_line_window, str_or_nothing(pch -> topic), server_list[sr].nickname, FALSE, NULL, TRUE, TRUE); pch -> topic_changed = FALSE; rc = TRUE; } return rc; } void check_server_connections_alive(time_t now_ts, BOOL force) { if (irc_keepalive || force) { int ka_loop = 0; for(ka_loop=0; ka_loop ts_bytes; int d = difftime(now_ts, last_data); if ((d >= 30 || force) && ps -> state == STATE_RUNNING) { if (ps -> sent_time_req_ts < 1) { ps -> hide_time_req = TRUE; ps -> sent_time_req_ts = get_ts(); } if (irc_time(ps -> fd) == -1) { close(ps -> fd); set_state(ka_loop, STATE_NO_CONNECTION); update_statusline(ka_loop, 0, "Connection to %s:%d closed (5)", ps -> server_host, ps -> server_port); } ps -> ts_bytes = now_ts; } } } } void auto_reconnect_servers(time_t now_ts) { if (auto_reconnect) { int ar_loop = 0; for(ar_loop=0; ar_loop state_since; int d = difftime(now_ts, disc_since); if (d < ps -> reconnect_delay) continue; restart_server(ar_loop); update_statusline(ar_loop, 0, "Retry connecting to %s:%d", ps -> server_host, ps -> server_port); } } } } int wait_for_keypress(BOOL one_event) { int c = 0; time_t prev_ts = time(NULL); struct pollfd *pfd = NULL; int n_fd = 0; do { int pollrc = 0, do_refresh = 0, stdin_index = -1; free(pfd); pfd = NULL; n_fd = 0; /* wait for chars from stdin */ /* things will break if fd0 is not first in this array */ stdin_index = add_poll(&pfd, &n_fd, 0, POLLIN | POLLHUP); do_refresh |= register_dcc_events(&pfd, &n_fd); do_refresh |= register_server_events(&pfd, &n_fd); if (do_refresh) { mydoupdate(); do_refresh = 0; } for(;!terminal_changed;) { double now_tsd = get_ts(); double t_left = 0.1 - (now_tsd - last_topic_show_ts); int sleep_value = t_left * 1000.0; time_t now_ts = 0; if (sleep_value < 0) sleep_value = 0; if (!topic_scroll && sleep_value < 100) sleep_value = 100; pollrc = poll(pfd, n_fd, sleep_value); if (pollrc == -1) { if (errno == EINTR) continue; error_exit(TRUE, "poll() failed in main loop\n"); } now_tsd = get_ts(); now_ts = (time_t)now_tsd; if (update_headline(FALSE)) do_refresh = 1; /* update word cloud */ put_word_cloud(get_cursor_mode() == CM_WC, FALSE); if (show_clock(prev_ts, now_ts, pollrc > 0)) do_refresh = 1; if (show_topic(current_server, current_server_channel_nr, now_tsd)) do_refresh = 1; check_server_connections_alive(now_ts, FALSE); auto_reconnect_servers(now_ts); /* anything to redraw? */ if (pollrc > 0 && do_refresh == 0) do_refresh = 1; break; } process_dcc_events(pfd, n_fd); process_server_events(pfd, n_fd); if (do_refresh) mydoupdate(); /* terminal resize? */ if (terminal_changed) { c = -1; break; } /* key pressed? then break out of loop & process */ if (pfd[stdin_index].revents & POLLIN) { c = getch(); break; } } while(one_event == FALSE); free(pfd); return c; } void update_channel_border(int server_index) { if (chat_window_border && server_index == current_server) { const char f1_help[] = "[ Press F1 for help ]"; const char *tooltip = get_tooltip(); int f1_x = 0, f1_y = 0; int kr_x = 0, kr_y = -1; int f1_help_len = sizeof f1_help; int tooltip_len = strlen(tooltip); int server_color = get_server_color(server_index); if (server_color != -1) color_on(chat_window_border, server_color); /*color_on(chat_window_border, theme.chat_window_border_color);*/ wborder(chat_window_border -> win, theme.chat_window_border_left_side, theme.chat_window_border_right_side, theme.chat_window_border_top_side, theme.chat_window_border_bottom_side, theme.chat_window_border_top_left_hand_corner, theme.chat_window_border_top_right_hand_corner, theme.chat_window_border_bottom_left_hand_corner, theme.chat_window_border_bottom_right_hand_corner ); if (inverse_window_heading) { mywattron(chat_window_border -> win, A_STANDOUT); mvwprintw(chat_window_border -> win, 0, 2, "%s (%s)", str_or_nothing(cur_channel() -> channel_name), str_or_nothing(cur_server() -> description ? cur_server() -> description : cur_server() -> server_host)); mywattroff(chat_window_border -> win, A_STANDOUT); } else { mvwprintw(chat_window_border -> win, 0, 1, "[ %s (%s) ]", str_or_nothing(cur_channel() -> channel_name), str_or_nothing(cur_server() -> description ? cur_server() -> description : cur_server() -> server_host)); } if (time(NULL) - started_at > 30) { f1_y = chat_window_border -> nlines - 1; kr_y = -1; } else { f1_y = 0; kr_y = chat_window_border -> nlines - 1; } f1_x = chat_window_border -> ncols - (f1_help_len + 2); if (f1_x < 0) { f1_x = 0; f1_y = -1; } kr_x = chat_window_border -> ncols - (tooltip_len + 2); if (kr_x < 0) kr_y = -1; if (f1_y >= 0) mvwprintw(chat_window_border -> win, f1_y, f1_x, f1_help); if (kr_y >= 0) mvwprintw(chat_window_border -> win, kr_y, kr_x, tooltip); if (server_color != -1) color_off(chat_window_border, server_color); } } void reset_topic_scroll_offset(void) { topic_scroll_offset = 0; } int log_to_file(int sr, int ch, const char *nick, const char *msg) { int rc = 0; if (log_dir && strlen(log_dir) > 0) { server *ps = &server_list[sr]; channel *pc = &ps -> pchannels[ch]; char *path = NULL, *file = NULL; FILE *fh = NULL; asprintf(&path, "%s/%s/", log_dir, ps -> server_host); asprintf(&file, "%s/%s/%s.log", log_dir, ps -> server_host, pc -> channel_name); if (mkpath(path, 0755) == -1) rc = -1; else { fh = fopen(file, "a+"); if (!fh) rc = -1; else { time_t now = time(NULL); char *tstr = ctime(&now), *dummy = strchr(tstr, '\n'); int crc = -1; if (dummy) *dummy = 0x00; if (nick) crc = fprintf(fh, "%s <%s> %s\n", tstr, nick, msg); else crc = fprintf(fh, "%s + %s\n", tstr, msg); fclose(fh); if (crc == -1) rc = -1; } } free(file); free(path); } return rc; } fi-1.36/dictionary.c0000644000175000017500000000254512302365027014256 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 696 $ * (C) 2006-2014 by folkert@vanheusden.com */ #include #include #include #include "string_array.h" #include "utils.h" const char *dictionary_file = NULL; string_array_t dictionary; BOOL load_dictionary(void) { char buffer[4096] = { 0 }; FILE *fh = fopen(dictionary_file, "r"); if (!fh) return FALSE; free_string_array(&dictionary); while(!feof(fh)) { if (fgets(buffer, sizeof buffer, fh) == NULL) break; if (strlen(buffer) == 0) continue; terminate_str(buffer, '\r'); terminate_str(buffer, '\n'); add_to_string_array(&dictionary, buffer); } fclose(fh); sort_string_array(&dictionary); return TRUE; } BOOL save_dictionary(void) { int n = string_array_get_n(&dictionary), idx = 0, rc = 0; FILE *fh = fopen(dictionary_file, "w"); if (!fh) return FALSE; for(idx=0; idx #include #include #include #include #include #include #include #include #include #include #ifndef AIX #include /* needed on Solaris 8 */ #endif #include #include #include "gen.h" #include "error.h" #include "theme.h" #include "term.h" #include "utils.h" #include "config.h" #include "main.h" #include "colors.h" int max_x = 80, max_y = 24; int default_colorpair = 0, highlight_colorpair = 0, meta_colorpair = 0, error_colorpair = 0, temp_colorpair = 0, markerline_colorpair = 0; NEWWIN *override_cursor_win = NULL; int override_cursor_x = 0, override_cursor_y = 0; void wrong_key(void) { flash(); beep(); flushinp(); } void mywbkgd(NEWWIN *win, int pair) { if (pair < 0) error_exit(FALSE, "color pair %d < 0 by color_on", pair); if (pair >= get_n_cpairs()) error_exit(FALSE, "color pair %d >= %d by color_on", pair, get_n_cpairs()); if (colors_all == TRUE) wbkgd(win -> win, COLOR_PAIR(pair)); } void color_on(NEWWIN *win, int pair) { if (pair < 0) error_exit(FALSE, "color pair %d < 0 by color_on", pair); if (pair >= get_n_cpairs()) error_exit(FALSE, "color pair %d>= %d by color_on", pair, get_n_cpairs()); if (colors_all == TRUE) wattron(win -> win, COLOR_PAIR(pair)); } void color_off(NEWWIN *win, int pair) { if (pair < 0) error_exit(FALSE, "color pair %d < 0 by color_off", pair); if (pair >= get_n_cpairs()) error_exit(FALSE, "color pair %d >= %d by color_off", pair, get_n_cpairs()); if (colors_all == TRUE) wattroff(win -> win, COLOR_PAIR(pair)); } void delete_window(NEWWIN *mywin) { mydelwin(mywin); myfree(mywin); } void mydelwin(NEWWIN *win) { if (win) { if (win -> pwin && ERR == del_panel(win -> pwin)) error_exit(TRUE, "del_panel() failed\n"); if (win -> win && ERR == delwin(win -> win)) error_exit(TRUE, "delwin() failed\n"); } } extern NEWWIN *input_window; void mydoupdate() { update_panels(); if (override_cursor_win) { wmove(override_cursor_win -> win, override_cursor_y, override_cursor_x); setsyx(override_cursor_win -> y + override_cursor_y, override_cursor_win -> x + override_cursor_x); } else if (input_window) { wmove(input_window -> win, 0, ul_x); setsyx(input_window -> y + 0, input_window -> x + ul_x); } doupdate(); } WINDOW * mynewwin(int nlines, int ncols, int begin_y, int begin_x) { WINDOW *dummy = newwin(nlines, ncols, begin_y, begin_x); if (!dummy) error_exit(TRUE, "failed to create window (subwin) with dimensions %d-%d at offset %d,%d (terminal size: %d,%d)\n", ncols, nlines, begin_x, begin_y, max_x, max_y); keypad(dummy, TRUE); return dummy; } NEWWIN * create_window(int n_lines, int n_colls) { return create_window_xy((max_y/2) - (n_lines/2), (max_x/2) - (n_colls/2), n_lines, n_colls); } NEWWIN * create_window_xy(int y_offset, int x_offset, int n_lines, int n_colls) { NEWWIN *newwin = malloc(sizeof(NEWWIN)); /* create new window */ newwin -> win = mynewwin(n_lines, n_colls, y_offset, x_offset); newwin -> pwin = new_panel(newwin -> win); werase(newwin -> win); newwin -> ncols = n_colls; newwin -> nlines = n_lines; newwin -> x = x_offset; newwin -> y = y_offset; return newwin; } void limit_print(NEWWIN *win, int width, int y, int x, const char *format, ...) { va_list ap; int len = 0; char *buf = NULL; va_start(ap, format); len = vasprintf(&buf, format, ap); va_end(ap); if (len > width) buf[width] = 0x00; mvwprintw(win -> win, y, x, "%s", buf); free(buf); } void escape_print_xy(NEWWIN *win, int y, int x, const char *str) { int loop = 0, cursor_x = 0, len = strlen(str); BOOL inv = FALSE, underline = FALSE; for(loop=0; loop win, A_REVERSE); else mywattroff(win -> win, A_REVERSE); inv = 1 - inv; } else if (str[loop] == '_') { if (!underline) mywattron(win -> win, A_UNDERLINE); else mywattroff(win -> win, A_UNDERLINE); underline = 1 - underline; } else if (str[loop] == '\n') { cursor_x = 0; y++; } else { mvwprintw(win -> win, y, x + cursor_x++, "%c", str[loop]); } } if (inv) mywattroff(win -> win, A_REVERSE); if (underline) mywattroff(win -> win, A_UNDERLINE); } void escape_print(NEWWIN *win, const char *str, const char rev, const char un) { int loop, len = strlen(str); BOOL inv = FALSE, underline = FALSE; for(loop=0; loop win, A_REVERSE); else mywattroff(win -> win, A_REVERSE); inv = 1 - inv; } else if (str[loop] == un) { if (!underline) mywattron(win -> win, A_UNDERLINE); else mywattroff(win -> win, A_UNDERLINE); underline = 1 - underline; } else { waddch(win -> win, str[loop]); } } if (inv) mywattroff(win -> win, A_REVERSE); if (underline) mywattroff(win -> win, A_UNDERLINE); } void determine_terminal_size(void) { struct winsize size; max_x = max_y = 0; /* changed from 'STDIN_FILENO' as that is incorrect: we're * outputting to stdout! */ if (ioctl(1, TIOCGWINSZ, &size) == 0) { max_y = size.ws_row; max_x = size.ws_col; } if (!max_x || !max_y) { char *dummy = getenv("COLUMNS"); if (dummy) max_x = atoi(dummy); else max_x = 80; dummy = getenv("LINES"); if (dummy) max_x = atoi(dummy); else max_x = 24; } } void create_win_border(int width, int height, const char *title, NEWWIN **bwin, NEWWIN **win, BOOL f1) { const char f1_for_help [] = " F1 for help "; int x = max_x / 2 - (width + 2) / 2; int y = max_y / 2 - (height + 2) / 2; *bwin = create_window_xy(y + 0, x + 0, height + 2, width + 2); *win = create_window_xy(y + 1, x + 1, height + 0, width + 0); mywattron((*bwin) -> win, A_REVERSE); box((*bwin) -> win, 0, 0); mywattroff((*bwin) -> win, A_REVERSE); mywattron((*bwin) -> win, A_STANDOUT); if (inverse_window_heading) { mvwprintw((*bwin) -> win, 0, 1, "%s", title); if (f1) mvwprintw((*bwin) -> win, (*bwin) -> nlines - 1, 2, "%s", f1_for_help); } else { mvwprintw((*bwin) -> win, 0, 1, "[ %s ]", title); if (f1) mvwprintw((*bwin) -> win, (*bwin) -> nlines - 1, 2, "[ %s ]", f1_for_help); } mywattroff((*bwin) -> win, A_STANDOUT); } void init_colors(void) { default_colorpair = get_color_ncurses(-1, -1); highlight_colorpair = get_color_ncurses(COLOR_GREEN, -1); meta_colorpair = get_color_ncurses(COLOR_BLUE, -1); error_colorpair = get_color_ncurses(COLOR_YELLOW, -1); temp_colorpair = get_color_ncurses(COLOR_CYAN, -1); markerline_colorpair = default_colorpair; } void apply_mouse_setting(void) { if (ignore_mouse) mousemask(0, NULL); else mousemask(BUTTON1_CLICKED | BUTTON1_DOUBLE_CLICKED | BUTTON3_CLICKED, NULL); } void init_ncurses(BOOL ignore_mouse) { initscr(); start_color(); /* don't care if this one failes */ use_default_colors(); keypad(stdscr, TRUE); cbreak(); intrflush(stdscr, FALSE); noecho(); nonl(); refresh(); nodelay(stdscr, FALSE); meta(stdscr, TRUE); /* enable 8-bit input */ raw(); /* to be able to catch ctrl+c */ mywattron(stdscr, COLOR_PAIR(default_colorpair)); apply_mouse_setting(); init_colors(); max_y = LINES; max_x = COLS; } void mywattron(WINDOW *w, int a) { if (a != A_BLINK && a != A_BOLD && a != A_NORMAL && a != A_REVERSE && a != A_STANDOUT && a != A_UNDERLINE) error_exit(FALSE, "funny attributes: %d\n", a); wattron(w, a); } void mywattroff(WINDOW *w, int a) { if (a != A_BLINK && a != A_BOLD && a != A_NORMAL && a != A_REVERSE && a != A_STANDOUT && a != A_UNDERLINE) error_exit(FALSE, "funny attributes: %d\n", a); wattroff(w, a); } void reset_attributes(NEWWIN *win) { wattrset(win -> win, A_NORMAL | COLOR_PAIR(default_colorpair)); } BOOL is_in_window(NEWWIN *win, int x, int y) { return wenclose(win -> win, y, x); } BOOL right_mouse_button_clicked(void) { MEVENT event; return getmouse(&event) == OK && (event.bstate & BUTTON3_CLICKED); } void display_markerline(NEWWIN *win, const char *msg) { char *line = (char *)calloc(1, win -> ncols + 1), *msg_use = NULL; int len = 0, len_msg = 0; if (getcurx(win -> win)) waddch(win -> win, '\n'); color_on(win, markerline_colorpair); mywattron(win -> win, A_REVERSE); memset(line, '-', win -> ncols); len_msg = asprintf(&msg_use, "%s", msg); msg_use[min(len_msg, win -> ncols)] = 0x00; len = strlen(msg_use); memcpy(&line[win -> ncols / 2 - len / 2], msg_use, len); waddstr(win -> win, line); mywattroff(win -> win, A_REVERSE); free(line); free(msg_use); color_off(win, markerline_colorpair); color_on(win, default_colorpair); } void simple_marker(NEWWIN *win) { char *end_marker = (char *)calloc(1, win -> ncols + 1); memset(end_marker, '-', win -> ncols); if (getcurx(win -> win)) waddstr(win -> win, "\n"); waddstr(win -> win, end_marker); free(end_marker); } fi-1.36/dcc.c0000644000175000017500000003626412302365027012647 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 817 $ * (C) 2006-2014 by folkert@vanheusden.com */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "error.h" #include "gen.h" #include "term.h" #include "buffer.h" #include "channels.h" #include "servers.h" #include "loop.h" #include "utils.h" #include "tcp.h" #include "irc.h" #include "dcc.h" #include "config.h" DCC_t *dcc_list = NULL; int n_dcc = 0; char *dcc_path = NULL; #define BUFFER_SIZE 1024 /* for read/write: I've read that 1024 is standard for most(?) irc clients */ void init_dcc(void) { dcc_path = strdup(""); } void set_dcc_state(int index, dcc_conn_state_t state) { LOG("DCC %d state is now %d\n", index, state); dcc_list[index].state = state; } void free_dcc(void) { int loop = 0; for(loop=0; loop sin6_addr) == 1) ((struct sockaddr_in6 *)&sock_name) -> sin6_family = AF_INET6; else if (inet_pton(AF_INET, dcc_bind_to, &((struct sockaddr_in *)&sock_name) -> sin_addr) == 1) ((struct sockaddr_in *)&sock_name) -> sin_family = AF_INET; else update_statusline(server_index, channel_index, "DCC (send): cannot convert %s (not a valid IPv4 or IPv6 address): %s (%d)", dcc_bind_to, strerror(errno), errno); return 0; } else if (getsockname(server_list[server_index].fd, &sock_name, &sock_name_len)) { update_statusline(server_index, channel_index, "DCC (send): cannot retrieve local address: %s (%d)", strerror(errno), errno); return 0; /* not a irc-server socket problem */ } /* setup entry in DCC array */ n_dcc++; dcc_list = (DCC_t *)realloc(dcc_list, n_dcc * sizeof(DCC_t)); memset(&dcc_list[di], 0x00, sizeof(DCC_t)); dcc_list[di].mode = DCC_SEND_FILE; set_dcc_state(di, DSTATE_DCC_CONNECTING); dcc_list[di].server_nr = server_index; dcc_list[di].channel_nr = channel_index; dcc_list[di].last_update = 0; dcc_list[di].filename = strdup(filename); /* try to open the file */ dcc_list[di].fd_file = open(filename, O_RDONLY); if (dcc_list[di].fd_file == -1) { set_dcc_state(di, DSTATE_ERROR); update_statusline(server_index, channel_index, "DCC (send): failed to open the file %s: %s (%d)", filename, strerror(errno), errno); return 0; /* not a irc-server socket problem */ } /* create socket on which we will listen for a connection from the receiving end */ dcc_list[di].fd_conn = setup_nonblocking_socket(); if (dcc_list[di].fd_conn == -1) { set_dcc_state(di, DSTATE_ERROR); update_statusline(server_index, channel_index, "DCC: cannot create socket, reason: %s (%d)", strerror(errno), errno); return 0; /* not a irc-server socket problem */ } /* bind socket to port 0: let the OS pick a portnumber */ sock_name_in -> sin_port = 0; if (bind(dcc_list[di].fd_conn, &sock_name, sock_name_len)) { set_dcc_state(di, DSTATE_ERROR); update_statusline(server_index, channel_index, "DCC: cannot bind the socket to the adapter used by the outgoing IRC connection, reason: %s (%d)", strerror(errno), errno); return 0; /* not a irc-server socket problem */ } /* now find out what port was given to us */ if (getsockname(dcc_list[di].fd_conn, &sock_name, &sock_name_len)) { update_statusline(server_index, channel_index, "DCC (send): failed finding out local port: %s (%d)", strerror(errno), errno); return 0; /* not a irc-server socket problem */ } /* setup listen queue */ if (listen(dcc_list[di].fd_conn, 5) == -1) { set_dcc_state(di, DSTATE_ERROR); update_statusline(server_index, channel_index, "DCC: cannot setup socket (listen), reason: %s (%d)", strerror(errno), errno); return 0; /* not a irc-server socket problem */ } /* create irc command */ dummy = strrchr(filename, '/'); /* FIXME dotter ipv4/ipv6 */ asprintf(&msg_buffer, "\001DCC SEND %s %u %u\001", dummy?dummy+1:filename, ntohl(sock_name_in -> sin_addr.s_addr), ntohs(sock_name_in -> sin_port)); /* send irc command */ if (irc_privmsg(server_list[server_index].fd, nick, msg_buffer) != 0) { free(msg_buffer); return -1; } free(msg_buffer); /* now sit back and wait for the other end to connect; then start * start streaming */ /* start the listening & do a first probe: */ /* FIXME if other end does not connect then that is a DDOS */ new_conn_fd = accept(dcc_list[di].fd_conn, &nc_sa, &nc_sa_len); if (new_conn_fd != -1) { /* close listen socket */ close(dcc_list[di].fd_conn); /* and use the new socket (with the new client on it) as the xfer socket */ dcc_list[di].fd_conn = new_conn_fd; set_dcc_state(di, DSTATE_CONNECTED1); update_statusline(server_index, channel_index, "DCC: connected"); } return 0; } void init_recv_dcc(const char *filename, const char *addr_in, int port, int server_nr, int channel_nr) { int di = n_dcc; char *addr = NULL; char *real_name = NULL; char *local_filename = NULL; char *dummy = NULL; char *message = NULL; struct stat st; if (strcmp(addr_in, "0") == 0) /* irc bouncer most of the times */ addr = strdup(server_list[server_nr].server_host); else if (strchr(addr_in, '.')) /* dotted ipv4 */ addr = strdup(addr_in); else if (strchr(addr_in, ':')) /* dotted ipv6 */ addr = strdup(addr_in); else { int ip = atoi(addr_in); asprintf(&addr, "%d.%d.%d.%d", (ip >> 24) & 255, (ip >> 16) & 255, (ip >> 8) & 255, ip & 255); } update_statusline(server_nr, channel_nr, "DCC: IPv4 address is [%s]:%d", addr, port); n_dcc++; dcc_list = (DCC_t *)realloc(dcc_list, n_dcc * sizeof(DCC_t)); memset(&dcc_list[di], 0x00, sizeof(DCC_t)); dcc_list[di].server_nr = server_nr; dcc_list[di].channel_nr = channel_nr; dcc_list[di].last_update = 0; dcc_list[di].filename = strdup(filename); if (resolve(addr, port, &dcc_list[di].ri, &message) == FALSE) { update_statusline(server_nr, channel_nr, "Failed converting network address: %s", message); free(message); set_dcc_state(di, DSTATE_ERROR); } else { BOOL first_attempt = TRUE; const char *dcc_path_exploded = (dcc_path && strlen(dcc_path)) ? (char *)explode_path(dcc_path) : NULL; /* strip slashes from filename */ dummy = strrchr(filename, '/'); if (dummy) filename = dummy + 1; dummy = strrchr(filename, '\\'); if (dummy) filename = dummy + 1; for(;;) { local_filename = NULL; if (first_attempt) { if (dcc_path_exploded) asprintf(&local_filename, "%s/%s", dcc_path_exploded, filename); else local_filename = strdup(filename); first_attempt = FALSE; } else { if (dcc_path_exploded) asprintf(&local_filename, "%s/%s.%d", dcc_path_exploded, filename, rand()); else asprintf(&local_filename, "%s.%d", filename, rand()); } if (stat(local_filename, &st) == -1) break; LOG("DCC create file, tested %s: %s\n", local_filename, strerror(errno)); free(local_filename); } myfree(dcc_path_exploded); dcc_list[di].fd_file = open(local_filename, O_WRONLY | O_CREAT | O_EXCL, S_IRWXU | S_IRGRP | S_IROTH); if (dcc_list[di].fd_file == -1) { update_statusline(server_nr, channel_nr, "DCC: failed to create local file %s, reason: %s (%d)", local_filename, strerror(errno), errno); set_dcc_state(di, DSTATE_ERROR); } else { dcc_list[di].mode = DCC_RECEIVE_FILE; set_dcc_state(di, DSTATE_TCP_CONNECT); } myfree(local_filename); free(real_name); free(addr); } } int dcc_receive(DCC_t *pnt) { char io_buffer[BUFFER_SIZE] = { 0 }; int rc = -1; for(;;) { rc = read(pnt -> fd_conn, io_buffer, BUFFER_SIZE); if (rc > 0) break; if (rc == 0) { update_statusline(pnt -> server_nr, pnt -> channel_nr, "DCC: end of file? (%ld bytes received)", lseek(pnt -> fd_file, 0, SEEK_CUR)); pnt -> state = DSTATE_NO_CONNECTION; close(pnt -> fd_file); close(pnt -> fd_conn); break; } if (errno != EINTR && errno != EAGAIN) { update_statusline(pnt -> server_nr, pnt -> channel_nr, "DCC: read error from socket (%s)", strerror(errno)); pnt -> state = DSTATE_ERROR; close(pnt -> fd_file); close(pnt -> fd_conn); break; } } if (rc > 0) { if (write(pnt -> fd_file, io_buffer, rc) != rc) { update_statusline(pnt -> server_nr, pnt -> channel_nr, "DCC: error writing to disk (%s)", strerror(errno)); pnt -> state = DSTATE_NO_CONNECTION; close(pnt -> fd_file); close(pnt -> fd_conn); return 1; } else { time_t now = time(NULL); off_t f_offs = lseek(pnt -> fd_file, 0, SEEK_CUR); /* success reading a block and writing it to disk * transmit an offset */ uint32_t offset = htonl(f_offs); if (WRITE(pnt -> fd_conn, (char *)&offset, sizeof(offset)) != sizeof(offset)) { update_statusline(pnt -> server_nr, pnt -> channel_nr, "DCC: failed to ack file"); pnt -> state = DSTATE_ERROR; close(pnt -> fd_file); close(pnt -> fd_conn); return 1; } if (now - pnt -> last_update >= 5) { update_statusline(pnt -> server_nr, pnt -> channel_nr, "DCC: %d bytes received", f_offs); pnt -> last_update = now; } } return 0; /* connection still in progress, 1=eof */ } return 1; } int dcc_send(DCC_t *pnt) { char io_buffer[BUFFER_SIZE] = { 0 }; int rc; rc = read(pnt -> fd_file, io_buffer, BUFFER_SIZE); if (rc == -1) { update_statusline(pnt -> server_nr, pnt -> channel_nr, "DCC: error reading from file: %s (%d)", strerror(errno), errno); pnt -> state = DSTATE_ERROR; close(pnt -> fd_file); close(pnt -> fd_conn); return 1; } else if (rc == 0) { update_statusline(pnt -> server_nr, pnt -> channel_nr, "DCC: EOF"); close(pnt -> fd_file); close(pnt -> fd_conn); pnt -> state = DSTATE_NO_CONNECTION; return 1; } if (WRITE(pnt -> fd_conn, io_buffer, rc) != rc) { update_statusline(pnt -> server_nr, pnt -> channel_nr, "DCC: error sending to host: %s (%d)", strerror(errno), errno); pnt -> state = DSTATE_ERROR; close(pnt -> fd_file); close(pnt -> fd_conn); return 1; } return 0; } dcc_conn_state_t get_dcc_state(int index) { return dcc_list[index].state; } int register_dcc_events(struct pollfd **pfd, int *n_fd) { char *message = NULL; int loop = 0, redraw_rc = 0; for(loop=0; loop #include #include #include #include #include #include #include "gen.h" #include "utils.h" #include "key_value.h" #include "term.h" #include "user.h" #include "config.h" #include "main.h" #include "scrollback.h" /* size of word cloud window and number of entries to show in it */ int word_cloud_n = 0, word_cloud_win_height = 5, word_cloud_min_word_size = 4; /* interval for refreshing the window, in s */ int word_cloud_refresh = 5; time_t word_cloud_last_refresh = 0, word_cloud_prev_time_update = 0, word_cloud_5s_update = 0; NEWWIN *wc_window = NULL, *wc_window_border = NULL; int wc_offset = 0, wc_cursor = 0; key_value *global_wordcloud1 = NULL; key_value *global_wordcloud2 = NULL; char **wc_list = NULL; int wc_list_n = 0, *wc_counts = NULL; void init_wc(void) { global_wordcloud1 = allocate_kv(); global_wordcloud2 = allocate_kv(); } void uninit_wc(void) { free_kv(global_wordcloud1); free_kv(global_wordcloud2); } void add_to_wc_do(key_value *kv, const char *in) { int loop = 0; string_array_t words; init_string_array(&words); split_string(in, " ", TRUE, &words); for(loop=0; loop= word_cloud_min_word_size) { const int *count = get_from_kv(kv, temp); int *new_count = calloc(1, sizeof(int)); if (count) *new_count = *count + 1; else *new_count = 1; add_to_kv(kv, strdup(temp), new_count); } free(temp); } free_splitted_string(&words); } void add_to_wc(const char *in) { add_to_wc_do(global_wordcloud1, in); add_to_wc_do(global_wordcloud2, in); } int value_cmp_int(const void *pv1, const void *pv2) { int v1 = *(int *)pv1; int v2 = *(int *)pv2; return v1 - v2; } void get_top_n_keys_from_wc(key_value *kv, int get_n, char ***list, int *list_n, int **counts) { int kv_size = get_n_kv_from_kv(kv); int out_n = min(get_n, kv_size), loop = 0; if (out_n == 0) { *list = NULL; *list_n = 0; return; } sort_kv(kv, FALSE, FALSE, value_cmp_int); *list = (char **)malloc(out_n * sizeof(char *)); *counts = (int *)malloc(out_n * sizeof(int)); for(loop=0; loop win); for(wc_index=wc_offset; wc_index nlines, wc_list_n); wc_index++) { int y = wc_index - wc_offset; if (y == wc_cursor) mywattron(wc_window -> win, A_REVERSE); limit_print(wc_window, wc_window -> ncols, y, 0, "%s (%d)", wc_list[wc_index], wc_counts[wc_index]); if (y == wc_cursor) mywattroff(wc_window -> win, A_REVERSE); } mydoupdate(); } void put_word_cloud(BOOL selected, BOOL force_redraw) { if (word_cloud_n > 0 && wc_window_border) { time_t now = time(NULL); int time_passed = now - word_cloud_last_refresh; int time_left = word_cloud_refresh - time_passed; if (now - word_cloud_prev_time_update >= 1 || force_redraw) { if (inverse_window_heading) { mywattron(wc_window_border -> win, A_STANDOUT); mvwprintw(wc_window_border -> win, 0, 1, "Word cloud"); mvwprintw(wc_window_border -> win, wc_window_border -> nlines - 1, 1, "%03ds left", time_left); mywattroff(wc_window_border -> win, A_STANDOUT); } else { if (selected) mywattron(wc_window_border -> win, A_STANDOUT); mvwprintw(wc_window_border -> win, 0, 1, "[Word cloud]"); mvwprintw(wc_window_border -> win, wc_window_border -> nlines - 1, 1, "[ %03ds left ]", time_left); if (selected) mywattroff(wc_window_border -> win, A_STANDOUT); } word_cloud_prev_time_update = now; } if (time_left <= 0 || now - word_cloud_5s_update >= 5 || force_redraw) { char **new_list = NULL; int *new_counts = NULL; int new_list_n = 0; if (time_left <= 0) { get_top_n_keys_from_wc(global_wordcloud2, word_cloud_n, &new_list, &new_list_n, &new_counts); truncate_kv(global_wordcloud1); } else { get_top_n_keys_from_wc(global_wordcloud1, word_cloud_n, &new_list, &new_list_n, &new_counts); } if (new_list_n) { int loop = 0; for(loop=0; loop= wc_list_n) { wc_offset = 0; wc_cursor = 0; } } if (time_left <= 0) { key_value *temp = global_wordcloud1; global_wordcloud1 = global_wordcloud2; global_wordcloud2 = temp; } } } void search_in_wc(const char *what) { global_search(what, what); } void go_to_last_wc(void) { if (wc_list_n >= wc_window -> nlines) { wc_cursor = wc_window -> nlines - 1; wc_offset = wc_list_n - wc_window -> nlines; } else { wc_cursor = wc_list_n - 1; wc_offset = 0; } } void do_word_cloud_keypress(int c) { /* extern char **wc_list = NULL; */ /* extern int wc_list_n = 0, *wc_counts = NULL; */ if (c == KEY_UP) { if (wc_cursor > 0) wc_cursor--; else if (wc_offset > 0) wc_offset--; else go_to_last_wc(); } else if (c == KEY_END) { go_to_last_wc(); } else if (c == KEY_PPAGE) { if (wc_offset >= wc_window -> nlines) { wc_cursor = 0; wc_offset -= wc_window -> nlines; } else { wc_cursor = 0; wc_offset = 0; } } else if (c == KEY_DOWN) { if (wc_offset + wc_cursor < wc_list_n - 1) { if (wc_cursor < wc_window -> nlines - 1) wc_cursor++; else wc_offset++; } else { wc_cursor = 0; wc_offset = 0; } } else if (c == KEY_NPAGE) { if (wc_offset + wc_window -> nlines < wc_list_n) { wc_cursor = 0; wc_offset += wc_window -> nlines; } else { wc_cursor = 0; wc_offset = wc_list_n - 1; } } else if (c == KEY_HOME) { wc_cursor = 0; wc_offset = 0; } else if (c == KEY_RIGHT && wc_offset + wc_cursor < wc_list_n) { int ar_index = wc_offset + wc_cursor; char *temp = strdup(wc_list[ar_index]); search_in_wc(temp); free(temp); } refresh_wc_window(); } void wordcloud_mouse(mmask_t buttons, int x, int y) { if (get_cursor_mode() != CM_WC) set_cursor_mode(CM_WC); else if (buttons & BUTTON1_CLICKED) { if (wc_offset + y < wc_list_n) { int ar_index = wc_offset + y; char *temp = strdup(wc_list[ar_index]); search_in_wc(temp); free(temp); } else { wrong_key(); } } else { wrong_key(); } } void apply_show_wordcloud(void) { if (word_cloud_n == 0) { delete_window(wc_window); wc_window = NULL; delete_window(wc_window_border); wc_window_border = NULL; } } fi-1.36/headlines.h0000644000175000017500000000106612302365027014047 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 760 $ * (C) 2006-2014 by folkert@vanheusden.com */ #define MAX_HEADLINES_QUEUED 10 #define NEXT_HEADLINE_INTERVAL 5 #include "gen.h" #include "string_array.h" extern string_array_t matchers; void init_headlines(void); void free_headlines(void); void add_headline(BOOL prio, const char *what); BOOL update_headline(BOOL force); void add_headline_matcher(const char *str); BOOL dump_headline_matchers(FILE *fh); void check_headline_matches(const char *haystack, const char *headline); void apply_show_headlines(void); fi-1.36/config.c0000644000175000017500000006615212302365027013362 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 863 $ * (C) 2006-2014 by folkert@vanheusden.com */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gen.h" #include "error.h" #include "term.h" #include "buffer.h" #include "channels.h" #include "servers.h" #include "utils.h" #include "irc.h" #include "loop.h" #include "grep_filter.h" #include "main.h" #include "wordcloud.h" #include "dcc.h" #include "theme.h" #include "config.h" #include "colors.h" #include "ignores.h" #include "string_array.h" #include "dictionary.h" #include "headlines.h" BOOL topic_scroll = FALSE; BOOL partial_highlight_match = FALSE; const char *part_message = NULL, *server_exit_message = NULL; int check_for_mail = 0; int delay_before_reconnect = 31; int max_channel_record_lines; BOOL auto_private_channel; BOOL highlight = FALSE, fuzzy_highlight = FALSE; BOOL notice_in_server_channel = FALSE; BOOL colors_meta = TRUE, colors_all = TRUE; BOOL allow_invite = TRUE; BOOL store_config_on_exit = FALSE; BOOL show_parts = TRUE; BOOL show_mode_changes = TRUE; BOOL show_nick_change = TRUE; BOOL show_joins = TRUE; BOOL auto_rejoin = FALSE; BOOL nick_color = FALSE; BOOL use_nonbasic_colors = FALSE; char *conf_file = NULL; const char *finger_str = NULL; BOOL inverse_window_heading = TRUE; BOOL remember_channels = TRUE; BOOL mark_personal_messages = FALSE; BOOL update_clock_at_data = FALSE; BOOL irc_keepalive = TRUE; BOOL auto_reconnect = TRUE; BOOL space_after_start_marker = FALSE; BOOL allow_userinfo = TRUE; BOOL ignore_mouse = TRUE; BOOL jumpy_navigation = FALSE; BOOL user_column = FALSE; BOOL mark_meta = TRUE; BOOL full_user = FALSE; BOOL show_headlines = FALSE; BOOL auto_markerline = FALSE; BOOL ignore_unknown_irc_protocol_msgs = TRUE; BOOL only_one_markerline = TRUE; BOOL keep_channels_sorted = FALSE; BOOL create_channel_for_meta_requests = FALSE; int user_column_width = 8; int nick_sleep = 4; const char *userinfo = NULL; const char *log_dir = NULL; const char *notify_nick = NULL; const char *dcc_bind_to = NULL; grep_target *gp = NULL; grep_target *hlgp = NULL; favorite *favorite_channels = NULL; int n_favorite_channels = 0, favorite_channels_index = 0; cnf_entry cnf_pars[] = { { "enable colors", CNF_BOOL, &colors_all, NULL }, { "use non basic colors", CNF_BOOL, &use_nonbasic_colors, NULL }, { "nick color", CNF_BOOL, &nick_color, NULL }, { "meta-texts colors", CNF_BOOL, &colors_meta, NULL }, { "default colors", CNF_COLOR, &default_colorpair, NULL }, { "highlight colors", CNF_COLOR, &highlight_colorpair, NULL }, { "highlight", CNF_BOOL, &highlight, NULL }, { "fuzzy highlight match", CNF_BOOL, &fuzzy_highlight, NULL }, { "meta colors", CNF_COLOR, &meta_colorpair, NULL }, { "error colors", CNF_COLOR, &error_colorpair, NULL }, { "temp colors", CNF_COLOR, &temp_colorpair, NULL }, { "inverse window heading", CNF_BOOL, &inverse_window_heading, NULL }, { "partial highlight match", CNF_BOOL, &partial_highlight_match, NULL }, { "markerline colors", CNF_COLOR, &markerline_colorpair, NULL }, { "marker line when changing channel", CNF_BOOL, &auto_markerline, NULL }, { "show only the last marker line", CNF_BOOL, &only_one_markerline, NULL }, { "scroll topic", CNF_BOOL, &topic_scroll, NULL }, { "only update clock when data comes in", CNF_BOOL, &update_clock_at_data, NULL }, { "space between \">\" and input line", CNF_BOOL, &space_after_start_marker, NULL }, { "channel buffering", CNF_VALUE, &max_channel_record_lines, NULL }, { "keep channels sorted", CNF_BOOL, &keep_channels_sorted, NULL }, { "mark personal messages-channels", CNF_BOOL, &mark_personal_messages, NULL }, { "star meta messages", CNF_BOOL, &mark_meta, NULL }, { "align nicks in a column", CNF_BOOL, &user_column, NULL }, { "nick column width", CNF_VALUE, &user_column_width, NULL }, { "full user: include hostname", CNF_BOOL, &full_user, NULL }, { "show headlines", CNF_BOOL, &show_headlines, apply_show_headlines }, { "word cloud window height (=enable)", CNF_VALUE, &word_cloud_n, apply_show_wordcloud }, { "DCC path", CNF_STRING, &dcc_path, NULL }, { "DCC network interface to bind to", CNF_STRING, &dcc_bind_to, NULL }, { "ignores file", CNF_STRING, &ignore_file, NULL }, { "logging directory", CNF_STRING, &log_dir, NULL }, { "program to invoke when nick matches", CNF_STRING, ¬ify_nick, NULL }, { "store configuration on exit", CNF_BOOL, &store_config_on_exit, NULL }, { "IRC ping (detect off-line sooner)", CNF_BOOL, &irc_keepalive, NULL }, { "suppress unknown irc protocol msgs", CNF_BOOL, &ignore_unknown_irc_protocol_msgs, NULL }, { "delay before connect", CNF_VALUE, &delay_before_reconnect, NULL }, { "auto reconnect when connection lost", CNF_BOOL, &auto_reconnect, NULL }, { "allow USERINFO CTCP request", CNF_BOOL, &allow_userinfo, NULL }, { "USERINFO string, empty for default", CNF_STRING, &userinfo, NULL }, { "FINGER string, empty for default", CNF_STRING, &finger_str, NULL }, { "part message", CNF_STRING, &part_message, NULL }, { "leave a server message", CNF_STRING, &server_exit_message, NULL }, { "automatically create private channel", CNF_BOOL, &auto_private_channel, NULL }, { "notices in server window", CNF_BOOL, ¬ice_in_server_channel, NULL }, { "create channel for meta requests", CNF_BOOL, &create_channel_for_meta_requests, NULL }, { "show PARTs", CNF_BOOL, &show_parts, NULL }, { "show JOINs", CNF_BOOL, &show_joins, NULL }, { "show mode changes", CNF_BOOL, &show_mode_changes, NULL }, { "show nick changes", CNF_BOOL, &show_nick_change, NULL }, { "allow invites", CNF_BOOL, &allow_invite, NULL }, { "automatically re-join after kick", CNF_BOOL, &auto_rejoin, NULL }, { "^W/R/Z/X: jumpy navigation", CNF_BOOL, &jumpy_navigation, NULL }, { "ignore mouse clicks", CNF_BOOL, &ignore_mouse, apply_mouse_setting }, { NULL, 0, NULL } }; void add_favorite(const char *serv, const char *chan) { favorite_channels = realloc(favorite_channels, (n_favorite_channels + 1) * sizeof(favorite)); favorite_channels[n_favorite_channels].server = serv ? strdup(serv) : NULL; favorite_channels[n_favorite_channels].channel = strdup(chan); n_favorite_channels++; } void free_favorites(void) { int loop = 0; for(loop=0; loop server_host, ps -> server_port) == -1; failed |= fprintf(fh, "description=%s\n", ps -> description ? ps -> description : ps -> server_host) == -1; failed |= fprintf(fh, "name=%s\n", ps -> user_complete_name) == -1; failed |= fprintf(fh, "username=%s\n", ps -> username) == -1; failed |= fprintf(fh, "password=%s\n", ps -> password) == -1; failed |= fprintf(fh, "nickname=%s\n", ps -> nickname) == -1; for(achl=0; achl auto_join) && !failed; achl++) failed |= fprintf(fh, "auto_join=%s\n", string_array_get(&ps -> auto_join, achl)) == -1; for(sal=0; sal send_after_login) && !failed; sal++) failed |= fprintf(fh, "send_after_login=%s\n", string_array_get(&ps -> send_after_login, sal)) == -1; if (remember_channels) { int chl = 0; for(chl=0; chl n_channels && !failed; chl++) { BOOL is_auto_join = FALSE; const char *cur_channel = ps -> pchannels[chl].channel_name; if (!is_channel(cur_channel)) continue; for(achl=0; achl auto_join); achl++) { if (strcasecmp(string_array_get(&ps -> auto_join, achl), cur_channel) == 0) { is_auto_join = TRUE; break; } } if (!is_auto_join) failed |= fprintf(fh, "rejoin=%s\n", cur_channel) == -1; } } failed |= fprintf(fh, ";\n") == -1; } for(loop=0; loop server) failed |= fprintf(fh, "favorite=%s %s\n", pf -> server, pf -> channel) == 0; else failed |= fprintf(fh, "favorite=%s\n", pf -> channel) == 0; if (failed) break; } if (n_favorite_channels) failed |= fprintf(fh, ";\n") == -1; failed |= fprintf(fh, "all-colors=%s\n", true_false_str(colors_all)) == -1; failed |= fprintf(fh, "meta-colors=%s\n", true_false_str(colors_meta)) == -1; failed |= fprintf(fh, "nick-color=%s\n", true_false_str(nick_color)) == -1; failed |= fprintf(fh, "partial_highlight_match=%s\n", true_false_str(partial_highlight_match)) == -1; failed |= fprintf(fh, "notice_in_serverchannel=%s\n", true_false_str(notice_in_server_channel)) == -1; failed |= fprintf(fh, "highlight=%s\n", true_false_str(highlight)) == -1; failed |= fprintf(fh, "auto_private_channel=%s\n", true_false_str(auto_private_channel)) == -1; failed |= fprintf(fh, "update_clock_at_data=%s\n", true_false_str(update_clock_at_data)) == -1; failed |= fprintf(fh, "max_channel_record_lines=%d\n", max_channel_record_lines) == -1; failed |= fprintf(fh, "word_cloud_n=%d\n", word_cloud_n) == -1; failed |= fprintf(fh, "delay_before_reconnect=%d\n", delay_before_reconnect) == -1; failed |= fprintf(fh, "word_cloud_refresh=%d\n", word_cloud_refresh) == -1; failed |= fprintf(fh, "word_cloud_win_height=%d\n", word_cloud_win_height) == -1; failed |= fprintf(fh, "word_cloud_min_word_size=%d\n", word_cloud_min_word_size) == -1; failed |= fprintf(fh, "store_config_on_exit=%s\n", true_false_str(store_config_on_exit)) == -1; failed |= fprintf(fh, "topic_scroll=%s\n", true_false_str(topic_scroll)) == -1; failed |= fprintf(fh, "allow_invite=%s\n", true_false_str(allow_invite)) == -1; failed |= fprintf(fh, "show_parts=%s\n", true_false_str(show_parts)) == -1; failed |= fprintf(fh, "show_mode_changes=%s\n", true_false_str(show_mode_changes)) == -1; failed |= fprintf(fh, "show_nick_change=%s\n", true_false_str(show_nick_change)) == -1; failed |= fprintf(fh, "show_joins=%s\n", true_false_str(show_joins)) == -1; failed |= fprintf(fh, "auto_rejoin=%s\n", true_false_str(auto_rejoin)) == -1; failed |= fprintf(fh, "use_nonbasic_colors=%s\n", true_false_str(use_nonbasic_colors)) == -1; failed |= fprintf(fh, "inverse_window_heading=%s\n", true_false_str(inverse_window_heading)) == -1; failed |= fprintf(fh, "remember_channels=%s\n", true_false_str(remember_channels)) == -1; failed |= fprintf(fh, "mark_personal_messages=%s\n", true_false_str(mark_personal_messages)) == -1; failed |= fprintf(fh, "space_after_start_marker=%s\n", true_false_str(space_after_start_marker)) == -1; failed |= fprintf(fh, "fuzzy_highlight=%s\n", true_false_str(fuzzy_highlight)) == -1; failed |= fprintf(fh, "allow_userinfo=%s\n", true_false_str(allow_userinfo)) == -1; failed |= fprintf(fh, "ignore_mouse=%s\n", true_false_str(ignore_mouse)) == -1; failed |= fprintf(fh, "jumpy_navigation=%s\n", true_false_str(jumpy_navigation)) == -1; failed |= fprintf(fh, "user_column=%s\n", true_false_str(user_column)) == -1; failed |= fprintf(fh, "user_column_width=%d\n", user_column_width) == -1; failed |= fprintf(fh, "mark_meta=%s\n", true_false_str(mark_meta)) == -1; failed |= fprintf(fh, "full_user=%s\n", true_false_str(full_user)) == -1; failed |= fprintf(fh, "show_headlines=%s\n", true_false_str(show_headlines)) == -1; failed |= fprintf(fh, "irc_keepalive=%s\n", true_false_str(irc_keepalive)) == -1; failed |= fprintf(fh, "auto_markerline=%s\n", true_false_str(auto_markerline)) == -1; failed |= fprintf(fh, "ignore_unknown_irc_protocol_msgs=%s\n", true_false_str(ignore_unknown_irc_protocol_msgs)) == -1; failed |= fprintf(fh, "only_one_markerline=%s\n", true_false_str(only_one_markerline)) == -1; if (dcc_bind_to) failed |= fprintf(fh, "dcc_bind_to=%s\n", dcc_bind_to) == -1; if (log_dir) failed |= fprintf(fh, "log_dir=%s\n", log_dir) == -1; if (userinfo) failed |= fprintf(fh, "userinfo=%s\n", userinfo) == -1; if (notify_nick) failed |= fprintf(fh, "notify_nick=%s\n", notify_nick) == -1; if (dictionary_file) failed |= fprintf(fh, "dictionary_file=%s\n", dictionary_file) == -1; if (finger_str) failed |= fprintf(fh, "finger_str=%s\n", finger_str) == -1; if (dcc_path) failed |= fprintf(fh, "dcc_path=%s\n", dcc_path) == -1; if (ignore_file) failed |= fprintf(fh, "ignore_file=%s\n", ignore_file) == -1; if (theme_file) failed |= fprintf(fh, "theme=%s\n", theme_file) == -1; if (part_message) failed |= fprintf(fh, "part_message=%s\n", part_message) == -1; if (server_exit_message) failed |= fprintf(fh, "server_exit_message=%s\n", server_exit_message) == -1; failed |= dump_grep_filters(gp, "grep_filter", fh) == FALSE; failed |= dump_grep_filters(hlgp, "headline_filter", fh) == FALSE; failed |= dump_headline_matchers(fh) == FALSE; failed |= dump_string_array(&extra_highlights, "extra_highlights", fh) == FALSE; if (failed) { asprintf(err_msg, "Failed writing to %s: disk full?", conf_file); fclose(fh); return FALSE; } asprintf(err_msg, "All fine, written %ld bytes to %s", ftell(fh), conf_file); if (fclose(fh)) { asprintf(err_msg, "Problem finalizing %s", conf_file); return FALSE; } return TRUE; } int load_config(const char *file) { char *description = NULL, *server_host = NULL, *username = NULL, *password = NULL, *nickname = NULL, *user_complete_name = NULL; int server_index = -1; int linenr = 0; int fd = open(file, O_RDONLY); if (fd == -1) { if (errno == ENOENT) return -1; error_exit(TRUE, "Cannot open config file %s\n", file); } conf_file = strdup(file); for(;;) { char *line = read_line_fd(fd); char *cmd, *par; char *is; if (!line) break; linenr++; if (strlen(line) == 0) { myfree(line); continue; } if (line[0] == '#' || line[0] == ';') { myfree(line); continue; } is = strchr(line, '='); if (!is) error_exit(FALSE, "config: line %d is missing either command or parameter! (%s)", linenr, line); /* find parameter */ par = is + 1; while(*par == ' ') par++; /* remove spaces around command */ /* spaces at the start */ cmd = line; while(*cmd == ' ') cmd++; /* spaces at the end */ *is = 0x00; is--; while(*is == ' ') { *is = 0x00; is--; } if (strcmp(cmd, "server") == 0 || strcmp(cmd, "send_after_login") == 0 || strcmp(cmd, "auto_join") == 0 || strcmp(cmd, "channel") == 0 || strcmp(cmd, "rejoin") == 0) { /* all stuff already known? */ if (server_host) { if (nickname == NULL) error_exit(FALSE, "nickname must be set for %s", server_host); server_index = add_server(server_host, username, password, nickname, user_complete_name, description ? description : server_host); myfree(server_host); server_host = NULL; myfree(username); myfree(password); myfree(nickname); myfree(user_complete_name); myfree(description); username = password = nickname = user_complete_name = description = NULL; } } if (strcmp(cmd, "server") == 0) { /* new server */ server_host = strdup(par); } else if (strcmp(cmd, "favorite") == 0) { int n = -1; string_array_t parts; init_string_array(&parts); split_string(par, " ", TRUE, &parts); n = string_array_get_n(&parts); if (n != 1 && n != 2) error_exit(FALSE, "favorite needs either be in format \"server channel\" or \"channel\""); if (n == 2) add_favorite(string_array_get(&parts, 0), string_array_get(&parts, 1)); else add_favorite(NULL, string_array_get(&parts, 0)); free_splitted_string(&parts); } else if (strcmp(cmd, "username") == 0) username = strdup(par); else if (strcmp(cmd, "password") == 0) password = strdup(par); else if (strcmp(cmd, "nick") == 0 || strcmp(cmd, "nickname") == 0) nickname = strdup(par); else if (strcmp(cmd, "name") == 0) user_complete_name = strdup(par); else if (strcmp(cmd, "dictionary_file") == 0) { const char *filename = explode_path(par); if (!filename) error_exit(TRUE, "Path '%s' is not understood\n", par); dictionary_file = filename; if (load_dictionary() == FALSE) error_exit(TRUE, "Failure loading dictionary file %s (%s)", filename, par); } else if (strcmp(cmd, "description") == 0) description = strdup(par); else if (strcmp(cmd, "server_exit_message") == 0) server_exit_message = strdup(par); else if (strcmp(cmd, "log_dir") == 0) log_dir = strdup(par); else if (strcmp(cmd, "part_message") == 0) part_message = strdup(par); else if (strcmp(cmd, "notify_nick") == 0) notify_nick = strdup(par); else if (strcmp(cmd, "userinfo") == 0) userinfo = strdup(par); else if (strcmp(cmd, "finger_str") == 0) finger_str = strdup(par); else if (strcmp(cmd, "mark_personal_messages") == 0) mark_personal_messages = parse_false_true(par, cmd, linenr); else if (strcmp(cmd, "meta-colors") == 0) colors_meta = parse_false_true(par, cmd, linenr); else if (strcmp(cmd, "headline_matcher") == 0) add_headline_matcher(par); else if (strcmp(cmd, "all-colors") == 0) colors_all = parse_false_true(par, cmd, linenr); else if (strcmp(cmd, "dcc_bind_to") == 0) dcc_bind_to = strdup(par); else if (strcmp(cmd, "update_clock_at_data") == 0) update_clock_at_data = parse_false_true(par, cmd, linenr); else if (strcmp(cmd, "nick-color") == 0) nick_color = parse_false_true(par, cmd, linenr); else if (strcmp(cmd, "use_nonbasic_colors") == 0) use_nonbasic_colors = parse_false_true(par, cmd, linenr); else if (strcmp(cmd, "ignore_unknown_irc_protocol_msgs") == 0) ignore_unknown_irc_protocol_msgs = parse_false_true(par, cmd, linenr); else if (strcmp(cmd, "auto_markerline") == 0) auto_markerline = parse_false_true(par, cmd, linenr); else if (strcmp(cmd, "inverse_window_heading") == 0) inverse_window_heading = parse_false_true(par, cmd, linenr); else if (strcmp(cmd, "keep_channels_sorted") == 0) keep_channels_sorted = parse_false_true(par, cmd, linenr); else if (strcmp(cmd, "allow_invite") == 0) allow_invite = parse_false_true(par, cmd, linenr); else if (strcmp(cmd, "show_headlines") == 0) show_headlines = parse_false_true(par, cmd, linenr); else if (strcmp(cmd, "remember_channels") == 0) remember_channels = parse_false_true(par, cmd, linenr); else if (strcmp(cmd, "allow_userinfo") == 0) allow_userinfo = parse_false_true(par, cmd, linenr); else if (strcmp(cmd, "extra_highlights") == 0) add_to_string_array(&extra_highlights, par); else if (strcmp(cmd, "only_one_markerline") == 0) only_one_markerline = parse_false_true(par, cmd, linenr); else if (strcmp(cmd, "auto_rejoin") == 0) auto_rejoin = parse_false_true(par, cmd, linenr); else if (strcmp(cmd, "ignore_mouse") == 0) ignore_mouse = parse_false_true(par, cmd, linenr); else if (strcmp(cmd, "irc_keepalive") == 0) irc_keepalive = parse_false_true(par, cmd, linenr); else if (strcmp(cmd, "space_after_start_marker") == 0) space_after_start_marker = parse_false_true(par, cmd, linenr); else if (strcmp(cmd, "jumpy_navigation") == 0) jumpy_navigation = parse_false_true(par, cmd, linenr); else if (strcmp(cmd, "mark_meta") == 0) mark_meta = parse_false_true(par, cmd, linenr); else if (strcmp(cmd, "user_column") == 0) user_column = parse_false_true(par, cmd, linenr); else if (strcmp(cmd, "full_user") == 0) full_user = parse_false_true(par, cmd, linenr); else if (strcmp(cmd, "grep_filter") == 0) add_filter(gp, par, linenr); else if (strcmp(cmd, "headline_filter") == 0) add_filter(hlgp, par, linenr); else if (strcmp(cmd, "show_parts") == 0) show_parts = parse_false_true(par, cmd, linenr); else if (strcmp(cmd, "show_mode_changes") == 0) show_mode_changes = parse_false_true(par, cmd, linenr); else if (strcmp(cmd, "show_nick_change") == 0) show_nick_change = parse_false_true(par, cmd, linenr); else if (strcmp(cmd, "show_joins") == 0) show_joins = parse_false_true(par, cmd, linenr); else if (strcmp(cmd, "store_config_on_exit") == 0) store_config_on_exit = parse_false_true(par, cmd, linenr); else if (strcmp(cmd, "partial_highlight_match") == 0) partial_highlight_match = parse_false_true(par, cmd, linenr); else if (strcmp(cmd, "topic_scroll") == 0) topic_scroll = parse_false_true(par, cmd, linenr); else if (strcmp(cmd, "notice_in_serverchannel") == 0) notice_in_server_channel = parse_false_true(par, cmd, linenr); else if (strcmp(cmd, "highlight") == 0) highlight = parse_false_true(par, cmd, linenr); else if (strcmp(cmd, "fuzzy_highlight") == 0) fuzzy_highlight = parse_false_true(par, cmd, linenr); else if (strcmp(cmd, "theme") == 0) { struct stat status; const char *filename = explode_path(par); if (!filename) error_exit(TRUE, "Path '%s' is not understood\n", par); if (stat(filename, &status) == -1) /* file doesn't exist, look for it under SYSCONFDIR */ { int len = strlen(SYSCONFDIR) + strlen(par) + 2; char *theme_path = malloc(len * sizeof(char)); snprintf(theme_path, len, "%s/%s", SYSCONFDIR, par); load_theme(theme_path); theme_file = theme_path; } else { load_theme(filename); theme_file = strdup(par); } myfree(filename); } else if (strcmp(cmd, "ignore_file") == 0) { struct stat status; const char *filename = explode_path(par); if (!filename) error_exit(TRUE, "Path '%s' is not understood\n", par); if (load_ignore_list(par) == TRUE) { } else if (load_ignore_list(filename) == TRUE) { } else if (stat(filename, &status) == -1) /* file doesn't exist, look elsewhere */ { int len = strlen(SYSCONFDIR) + strlen(par) + 2; char *ignore_file = malloc(len * sizeof(char)); /* look for it under SYSCONFDIR */ snprintf(ignore_file, len, "%s/%s", SYSCONFDIR, par); /* look for it under ~/.firc location */ if (stat(ignore_file, &status) == -1) snprintf(ignore_file, len, "%s/%s", dirname(conf_file), par); load_ignore_list(ignore_file); myfree(ignore_file); } myfree(filename); } else if (strcmp(cmd, "send_after_login") == 0) { server *ps = &server_list[server_index]; if (server_index == -1) error_exit(FALSE, "send_after_login: you need to define a server first\n"); add_to_string_array(&ps -> send_after_login, par); } else if (strcmp(cmd, "auto_join") == 0 || strcmp(cmd, "channel") == 0) { if (server_index == -1) error_exit(FALSE, "auto_join: you need to define a server first\n"); add_autojoin(server_index, par); } else if (strcmp(cmd, "rejoin") == 0) { add_channel(server_index, par); if (keep_channels_sorted) sort_channels(server_index); } else if (strcmp(cmd, "auto_private_channel") == 0) auto_private_channel = parse_false_true(par, cmd, linenr); else if (strcmp(cmd, "dcc_path") == 0) dcc_path = strdup(par); else if (strcmp(cmd, "default_colorpair") == 0) default_colorpair = parse_color_spec(par, linenr, cmd); else if (strcmp(cmd, "markerline_colorpair") == 0) markerline_colorpair = parse_color_spec(par, linenr, cmd); else if (strcmp(cmd, "highlight_colorpair") == 0) highlight_colorpair = parse_color_spec(par, linenr, cmd); else if (strcmp(cmd, "meta_colorpair") == 0) meta_colorpair = parse_color_spec(par, linenr, cmd); else if (strcmp(cmd, "error_colorpair") == 0) error_colorpair = parse_color_spec(par, linenr, cmd); else if (strcmp(cmd, "temp_colorpair") == 0) temp_colorpair = parse_color_spec(par, linenr, cmd); else if (strcmp(cmd, "check_for_mail") == 0) check_for_mail = atoi(par); else if (strcmp(cmd, "user_column_width") == 0) user_column_width = atoi(par); else if (strcmp(cmd, "delay_before_reconnect") == 0) delay_before_reconnect = atoi(par); else if (strcmp(cmd, "word_cloud_n") == 0) word_cloud_n = atoi(par); else if (strcmp(cmd, "word_cloud_refresh") == 0) { word_cloud_refresh = atoi(par); word_cloud_last_refresh = time(NULL); } else if (strcmp(cmd, "word_cloud_win_height") == 0) word_cloud_win_height = atoi(par); else if (strcmp(cmd, "max_channel_record_lines") == 0) max_channel_record_lines = atoi(par); else if (strcmp(cmd, "word_cloud_min_word_size") == 0) word_cloud_min_word_size = atoi(par); else { error_exit(FALSE, "'%s=%s' is not understood\n", cmd, par); } myfree(line); } close(fd); if (server_host) { if (nickname == NULL) error_exit(FALSE, "nickname must be set for %s", server_host); add_server(server_host, username, password, nickname, user_complete_name, description); myfree(server_host); myfree(username); myfree(password); myfree(nickname); myfree(user_complete_name); myfree(description); } return 0; } int config_color_str_convert(const char *in, int linenr, const char *subj) { int nr = color_str_convert(in); if (nr >= -1) return nr; error_exit(FALSE, "Color %s for %s at line %d not recognized", in, subj, linenr); return -2; } int parse_color_spec(const char *par, int linenr, const char *subj) { char *temp = strdup(par); char *komma = strchr(temp, ','); const char *fgstr = temp, *bgstr = "default"; int fg = -1, bg = -1; int col = -1; if (komma) { *komma = 0x00; bgstr = komma + 1; } fg = config_color_str_convert(fgstr, linenr, subj); bg = config_color_str_convert(bgstr, linenr, subj); free(temp); if (fg == bg && fg != -1) fg = bg = -1; col = get_color_ncurses(fg, bg); return col; } fi-1.36/gen.h0000644000175000017500000000076112302365027012665 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 671 $ * (C) 2006-2014 by folkert@vanheusden.com */ #ifndef __GEN_H_ #define __GEN_H_ #define BOOL char #ifndef TRUE #define TRUE 1 #ifndef FALSE #define FALSE 0 #endif #endif #define min(x, y) ((x)<(y)?(x):(y)) #define max(x, y) ((x)>(y)?(x):(y)) #define DEFAULT_IRC_PORT 6667 /* length of the edit line */ #define LINE_LENGTH 460 #define USER_COLUMN_WIDTH 8 typedef enum { CM_EDIT, CM_CHANNELS, CM_NAMES, CM_WC } cursor_mode_t; #endif fi-1.36/readme.txt0000644000175000017500000000123412302365027013735 0ustar folkertfolkertcompiling --------- You need to have installed the libraries libncursesw5-dev and libssl-dev. It should contain the files ncursesw/panel.h ncursesw/ncurses.h and appropriate library files. Then invoke: make install This will create a program called 'f-irc'. Usage ----- Invoke: man f-irc for details how to configure and use it. F-IRC was written by folkert@vanheusden.com http://www.vanheusden.com/fi/ Please consider using PGP when sending e-mails to me. Key-ID: 1F28D8AE Public key can be retrieved from: http://www.vanheusden.com/key.txt Please support my opensource development: http://www.vanheusden.com/wishlist.php SVN revision: $Revision: 671 $ fi-1.36/lf_buffer.h0000644000175000017500000000053212302365027014042 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 687 $ * (C) 2006-2014 by folkert@vanheusden.com */ typedef struct { char *data; int size; } lf_buffer_t; void init_lf_buffer(lf_buffer_t *p); void free_lf_buffer(lf_buffer_t *p); void add_lf_buffer(lf_buffer_t *p, const char *what, int what_size); const char *get_line_lf_buffer(lf_buffer_t *p); fi-1.36/wordcloud.h0000644000175000017500000000157412302365027014121 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 744 $ * (C) 2006-2014 by folkert@vanheusden.com */ #ifndef __WORDCLOUD_H__ #define __WORDCLOUD_H__ #include "key_value.h" extern NEWWIN *wc_window, *wc_window_border; extern int word_cloud_n, word_cloud_win_height, word_cloud_refresh, word_cloud_min_word_size; extern time_t word_cloud_last_refresh; extern int wc_offset, wc_cursor; extern char **wc_list; extern int wc_list_n, *wc_counts; void init_wc(void); void uninit_wc(void); void add_to_wc(const char *in); void get_top_n_keys_from_wc(key_value *kv, int get_n, char ***list, int *list_n, int **counts); void put_word_cloud(BOOL win_selected, BOOL force_redraw); void refresh_wc_window(void); void search_in_wc(const char *what); void go_to_last_wc(void); void do_word_cloud_keypress(int c); void wordcloud_mouse(mmask_t buttons, int x, int y); void apply_show_wordcloud(void); #endif fi-1.36/key_value.c0000644000175000017500000000631012302365027014067 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 671 $ * (C) 2006-2014 by folkert@vanheusden.com */ #define _GNU_SOURCE #include #include #include #include "gen.h" #include "utils.h" #include "key_value.h" key_value * allocate_kv(void) { return (key_value *)calloc(1, sizeof(key_value)); } void free_kv(key_value *p) { truncate_kv(p); free(p); } void bin_search(const key_value *work, const char *key, int *found_at, int *insert_at) { int imin = 0, imax = work -> n - 1, found = -1; while(imax >= imin) { int imid = (imin + imax) / 2; int cmp = strcmp(work -> pairs[imid].key, key); if (cmp < 0) imin = imid + 1; else if (cmp > 0) imax = imid - 1; else { found = imid; break; } } if (found == -1) { *insert_at = imin; *found_at = -1; } else { *insert_at = -1; *found_at = found; } } int get_n_kv_from_kv(const key_value *in) { return in -> n; } const char *get_key_by_index(const key_value *in, int nr) { return in -> pairs[nr].key; } const void *get_value_by_index(const key_value *in, int nr) { return in -> pairs[nr].value; } void add_to_kv(key_value *work, const char *key, const void *value) { int found_at = -1, insert_at = -1; bin_search(work, key, &found_at, &insert_at); if (found_at != -1) { myfree(work -> pairs[found_at].value); work -> pairs[found_at].value = value; } else { int n_to_move = work -> n - insert_at; work -> pairs = realloc(work -> pairs, (work -> n + 1) * sizeof(key_value_pair)); if (n_to_move) memmove(&work -> pairs[insert_at + 1], &work -> pairs[insert_at], n_to_move * sizeof(key_value_pair)); work -> pairs[insert_at].key = key; work -> pairs[insert_at].value = value; work -> n++; } } const void * get_from_kv(const key_value *in, const char *key) { int found_at = -1, insert_at = -1; bin_search(in, key, &found_at, &insert_at); if (found_at == -1) return NULL; return in -> pairs[found_at].value; } BOOL update_kv(key_value *work, const char *key, const void *new_value) { int found_at = -1, insert_at = -1; bin_search(work, key, &found_at, &insert_at); if (found_at == -1) return FALSE; myfree(work -> pairs[found_at].value); work -> pairs[found_at].value = new_value; return TRUE; } void truncate_kv(key_value *work) { if (work) { int loop; for(loop=0; loop n; loop++) { myfree(work -> pairs[loop].key); myfree(work -> pairs[loop].value); } myfree(work -> pairs); work -> pairs = NULL; work -> n = 0; } } typedef struct { BOOL asc, key; int (*value_cmp)(const void *pv1, const void *pv2); } sort_pars; sort_pars sp; int qsort_cmp(const void *pv1, const void *pv2) { key_value_pair *v1 = (key_value_pair *)pv1; key_value_pair *v2 = (key_value_pair *)pv2; if (sp.key) { if (sp.asc) return strcmp(v1 -> key, v2 -> key); else return strcmp(v2 -> key, v1 -> key); } if (sp.asc) return sp.value_cmp(v1 -> value, v2 -> value); else return sp.value_cmp(v2 -> value, v1 -> value); } void sort_kv(key_value *work, const BOOL asc, const BOOL key, int (*value_cmp)(const void *pv1, const void *pv2)) { sp.asc = asc; sp.key = key; sp.value_cmp = value_cmp; qsort(work -> pairs, work -> n, sizeof(key_value_pair), qsort_cmp); } fi-1.36/names.c0000644000175000017500000003243412302365027013214 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 819 $ * (C) 2006-2014 by folkert@vanheusden.com */ #include #include #include #include #include #include "gen.h" #include "theme.h" #include "term.h" #include "buffer.h" #include "channels.h" #include "servers.h" #include "irc.h" #include "names.h" #include "utils.h" #include "loop.h" #include "config.h" #include "main.h" #include "user.h" #include "ignores.h" int names_offset = 0, names_cursor = 0; void free_person(person_t *p) { myfree(p -> nick); myfree(p -> complete_name); myfree(p -> user_host); } void delete_index(int server_index, int channel_index, int name_index) { channel *pc = &server_list[server_index].pchannels[channel_index]; int move_n = pc -> n_names - (name_index + 1); free_person(&pc -> persons[name_index]); memmove(&pc -> persons[name_index], &pc -> persons[name_index + 1], move_n * sizeof(person_t)); pc -> n_names--; } void change_nick(int server_index, int channel_index, const char *old_nick, const char *new_nick) { channel *pc = &server_list[server_index].pchannels[channel_index]; int insert_index = 0, nick_found_at = -1; const char *search_nick = has_nick_mode(old_nick) ? old_nick + 1 : old_nick; if (strcasecmp(search_nick, new_nick) != 0) { search_for_nick(pc, search_nick, &nick_found_at, &insert_index); if (nick_found_at != -1) { person_t *p = &pc -> persons[nick_found_at]; const char *complete_name = p -> complete_name ? strdup(p -> complete_name) : NULL; const char *user_host = p -> user_host ? strdup(p -> user_host) : NULL; /* this to keep the list sorted */ delete_index(server_index, channel_index, nick_found_at); add_nick(server_index, channel_index, new_nick, complete_name, user_host); myfree(user_host); myfree(complete_name); } } } void change_name(int server_index, int channel_index, const char *nick, const char *new_name) { channel *pc = &server_list[server_index].pchannels[channel_index]; int insert_index = 0, nick_found_at = -1; const char *search_nick = has_nick_mode(nick) ? nick + 1 : nick; search_for_nick(pc, search_nick, &nick_found_at, &insert_index); if (nick_found_at != -1) { person_t *p = &pc -> persons[nick_found_at]; myfree(p -> complete_name); p -> complete_name = strdup(new_name); LOG("DO_WHOIS %s %s %s\n", search_nick, nick, new_name); } } void change_user_host(int server_index, int channel_index, const char *nick, const char *new_user_host) { channel *pc = &server_list[server_index].pchannels[channel_index]; int insert_index = 0, nick_found_at = -1; const char *search_nick = has_nick_mode(nick) ? nick + 1 : nick; search_for_nick(pc, search_nick, &nick_found_at, &insert_index); if (nick_found_at != -1) { person_t *p = &pc -> persons[nick_found_at]; myfree(p -> user_host); p -> user_host = strdup(new_user_host); } } void search_for_nick(channel *cur_channel, const char *nick, int *found_at, int *insert_at) { int imin = 0, imax = cur_channel -> n_names - 1; const char *search_nick = has_nick_mode(nick) ? nick + 1 : nick; while(imax >= imin) { int imid = (imin + imax) / 2; int cmp = strcasecmp(cur_channel -> persons[imid].nick, search_nick); if (cmp < 0) imin = imid + 1; else if (cmp > 0) imax = imid - 1; else { *found_at = imid; *insert_at = -1; return; } } *insert_at = imin; *found_at = -1; } irc_user_mode_t text_to_nick_mode(const char *nick) { if (nick[0] == '@') return MODE_OPERATOR; if (nick[0] == '+') return MODE_CAN_SPEAK; return 0; } void add_nick(int server_index, int channel_index, const char *nick, const char *complete_name, const char *user_host) { int insert_index = 0, nick_found_at = -1; channel *cur_channel = &server_list[server_index].pchannels[channel_index]; const char *search_nick = has_nick_mode(nick) ? nick + 1 : nick; search_for_nick(cur_channel, search_nick, &nick_found_at, &insert_index); if (nick_found_at == -1) { int new_n = cur_channel -> n_names + 1, move_n = cur_channel -> n_names - insert_index; cur_channel -> persons = realloc(cur_channel -> persons, new_n * sizeof(person_t)); if (move_n > 0) memmove(&(cur_channel -> persons)[insert_index + 1], &(cur_channel -> persons)[insert_index], sizeof(person_t) * move_n); /* set */ (cur_channel -> persons)[insert_index].nick = strdup(search_nick); (cur_channel -> persons)[insert_index].complete_name = complete_name ? strdup(complete_name) : NULL; (cur_channel -> persons)[insert_index].user_host = user_host ? strdup(user_host) : NULL; (cur_channel -> persons)[insert_index].mode = text_to_nick_mode(nick); (cur_channel -> persons)[insert_index].ignored = check_ignore(cur_channel -> channel_name, nick, complete_name); cur_channel -> n_names++; } else { myfree((cur_channel -> persons)[nick_found_at].complete_name); (cur_channel -> persons)[nick_found_at].complete_name = complete_name ? strdup(complete_name) : NULL; myfree((cur_channel -> persons)[nick_found_at].user_host); (cur_channel -> persons)[nick_found_at].user_host = user_host ? strdup(user_host) : NULL; (cur_channel -> persons)[nick_found_at].ignored = check_ignore(cur_channel -> channel_name, nick, complete_name); } } BOOL has_nick(int sr, int ch, const char *nick) { int nick_found_at = -1, insert_index = -1; channel *cur_channel = &server_list[sr].pchannels[ch]; const char *search_nick = has_nick_mode(nick) ? nick + 1 : nick; search_for_nick(cur_channel, search_nick, &nick_found_at, &insert_index); return nick_found_at != -1; } void delete_from_channel_by_nick(int sr, int ch, const char *nick) { int insert_index = 0, nick_found_at = -1; channel *cur_channel = &server_list[sr].pchannels[ch]; const char *search_nick = has_nick_mode(nick) ? nick + 1 : nick; search_for_nick(cur_channel, search_nick, &nick_found_at, &insert_index); if (nick_found_at != -1) { int n_to_move = (cur_channel -> n_names - nick_found_at) - 1; free_person(&(cur_channel -> persons)[nick_found_at]); if (n_to_move > 0) memmove(&(cur_channel -> persons)[nick_found_at], &(cur_channel -> persons)[nick_found_at + 1], n_to_move * sizeof(person_t)); cur_channel -> n_names--; } } void delete_by_nick(int server_index, const char *nick) { const char *search_nick = has_nick_mode(nick) ? nick + 1 : nick; server *ps = &server_list[server_index]; int channel_index = 0; for(channel_index = 0; channel_index < ps -> n_channels; channel_index++) delete_from_channel_by_nick(server_index, channel_index, search_nick); } void update_user_host(int sr, const char *prefix) { server *ps = &server_list[sr]; int channel_index = 0; char *nick = strdup(prefix); terminate_str(nick, '!'); for(channel_index = 0; channel_index < ps -> n_channels; channel_index++) { int insert_index = 0, nick_found_at = -1; channel *cur_channel = &server_list[sr].pchannels[channel_index]; search_for_nick(cur_channel, nick, &nick_found_at, &insert_index); if (nick_found_at != -1) { myfree((cur_channel -> persons)[nick_found_at].user_host); (cur_channel -> persons)[nick_found_at].user_host = strdup(prefix); } } free(nick); } void replace_nick(int server_index, const char *old_nick, const char *new_nick) { server *ps = &server_list[server_index]; int channel_index = 0; const char *search_nick = has_nick_mode(old_nick) ? old_nick + 1 : old_nick; for(channel_index = 0; channel_index < ps -> n_channels; channel_index++) change_nick(server_index, channel_index, search_nick, new_nick); } irc_user_mode_t get_nick_mode(int server_index, int channel_index, const char *nick) { int insert_index = 0, nick_found_at = -1; channel *cur_channel = &server_list[server_index].pchannels[channel_index]; const char *search_nick = has_nick_mode(nick) ? nick + 1 : nick; search_for_nick(cur_channel, search_nick, &nick_found_at, &insert_index); if (nick_found_at != -1) return (cur_channel -> persons)[nick_found_at].mode; return 0; } void set_nick_mode(int server_index, int channel_index, const char *nick, irc_user_mode_t mode) { int insert_index = 0, nick_found_at = -1; channel *cur_channel = &server_list[server_index].pchannels[channel_index]; const char *search_nick = has_nick_mode(nick) ? nick + 1 : nick; search_for_nick(cur_channel, search_nick, &nick_found_at, &insert_index); if (nick_found_at != -1) (cur_channel -> persons)[nick_found_at].mode = mode; } void free_names_list(channel *pc) { int name_index; for(name_index=0; name_index n_names; name_index++) free_person(&pc -> persons[name_index]); myfree(pc -> persons); pc -> persons = NULL; pc -> n_names = 0; } char nick_mode_to_text(char nick_mode) { if (nick_mode & MODE_OPERATOR) return '@'; if (nick_mode & MODE_CAN_SPEAK) return '+'; return ' '; } void show_name_list(int server_index, int channel_index, NEWWIN *channel_window) { if (server_index == -1 || channel_index == -1) return; if (server_index < n_servers && server_list[server_index].n_channels > channel_index) { int name_index = 0; channel *cur_channel = &server_list[server_index].pchannels[channel_index]; werase(channel_window -> win); for(name_index=names_offset; name_index nlines, cur_channel -> n_names); name_index++) { char nick_mode = (cur_channel -> persons)[name_index].mode; BOOL ignored = (cur_channel -> persons)[name_index].ignored; char nick_char = ignored ? 'I' : nick_mode_to_text(nick_mode); if (name_index - names_offset == names_cursor) mywattron(channel_window -> win, A_REVERSE); limit_print(channel_window, channel_window -> ncols, name_index - names_offset, 0, "%c%s", nick_char, (cur_channel -> persons)[name_index].nick); if (name_index - names_offset == names_cursor) mywattroff(channel_window -> win, A_REVERSE); } } } BOOL has_nick_mode(const char *nick) { if (nick[0] == '@') return TRUE; if (nick[0] == '+') return TRUE; return FALSE; } void go_to_last_name(void) { int n_names = cur_channel() -> n_names; if (n_names >= channel_window -> nlines) { names_cursor = channel_window -> nlines - 1; names_offset = n_names - channel_window -> nlines; } else { names_cursor = n_names - 1; names_offset = 0; } } void do_names_keypress(int c) { if (c == KEY_UP) { if (names_cursor > 0) names_cursor--; else if (names_offset > 0) names_offset--; else go_to_last_name(); } else if (c == KEY_END) { go_to_last_name(); } else if (c == KEY_PPAGE) { if (names_offset >= channel_window -> nlines) { names_cursor = 0; names_offset -= channel_window -> nlines; } else { names_cursor = 0; names_offset = 0; } } else if (c == KEY_DOWN) { if (names_offset + names_cursor < cur_channel() -> n_names - 1) { if (names_cursor < channel_window -> nlines - 1) names_cursor++; else names_offset++; } else { names_cursor = 0; names_offset = 0; } } else if (c == KEY_NPAGE) { if (names_offset + channel_window -> nlines < cur_channel() -> n_names) { names_cursor = 0; names_offset += channel_window -> nlines; } else { names_cursor = 0; names_offset = cur_channel() -> n_names - 1; } } else if (c == KEY_HOME) { names_cursor = 0; names_offset = 0; } else if (c == KEY_LEFT) /* leave nameslist */ { set_cursor_mode(CM_CHANNELS); } else if (c == KEY_RIGHT && names_offset + names_cursor < cur_channel() -> n_names) /* enter user menu */ { if (user_menu(current_server, current_server_channel_nr, names_offset + names_cursor) == -1) { cur_server() -> state = STATE_DISCONNECTED; close(cur_server() -> fd); update_statusline(current_server, current_server_channel_nr, "Connection to %s:%d closed (7)", cur_server() -> server_host, cur_server() -> server_port); } } } BOOL is_ignored(int sr, int ch, const char *nick) { channel *pc = &server_list[sr].pchannels[ch]; char *search_nick = strdup(has_nick_mode(nick) ? nick + 1 : nick); int insert_index = 0, nick_found_at = -1; terminate_str(search_nick, '!'); search_for_nick(pc, search_nick, &nick_found_at, &insert_index); free(search_nick); if (nick_found_at == -1) { LOG("is_ignored: %s not found\n", nick); return FALSE; } return pc -> persons[nick_found_at].ignored; } BOOL ignore_nick(int sr, int ch, const char *nick, BOOL *was) { channel *pc = &server_list[sr].pchannels[ch]; char *search_nick = strdup(has_nick_mode(nick) ? nick + 1 : nick); int insert_index = 0, nick_found_at = -1; terminate_str(search_nick, '!'); search_for_nick(pc, search_nick, &nick_found_at, &insert_index); free(search_nick); if (nick_found_at == -1) return FALSE; *was = pc -> persons[nick_found_at].ignored; pc -> persons[nick_found_at].ignored = TRUE; return TRUE; } BOOL unignore_nick(int sr, int ch, const char *nick, BOOL *was) { channel *pc = &server_list[sr].pchannels[ch]; char *search_nick = strdup(has_nick_mode(nick) ? nick + 1 : nick); int insert_index = 0, nick_found_at = -1; terminate_str(search_nick, '!'); search_for_nick(pc, search_nick, &nick_found_at, &insert_index); free(search_nick); if (nick_found_at == -1) return FALSE; *was = pc -> persons[nick_found_at].ignored; pc -> persons[nick_found_at].ignored = FALSE; return TRUE; } fi-1.36/colors.c0000644000175000017500000001406712302365027013414 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 671 $ * (C) 2006-2014 by folkert@vanheusden.com */ #define _GNU_SOURCE #include #include #include #include #include #include "gen.h" #include "error.h" #include "utils.h" typedef struct { int r, g, b; const char *name; } rgb_t; rgb_t mirc_col[16] = { { 999, 999, 999, "white" }, /* 0 white */ { 0, 0, 0, "black" }, /* 1 black */ { 0, 0, 500, "blue" }, /* 2 navy blue */ { 0, 999, 0, "green" }, /* 3 green */ { 999, 0, 0, "red" }, /* 4 red */ { 482, 66, 74, "maroon" }, /* 5 maroon */ { 666, 0, 999, "purple" }, /* 6 purple ("paars") */ { 999, 647, 0, "orange" }, /* 7 orange */ { 999, 999, 0, "yellow" }, /* 8 yellow */ { 196, 803, 196, "limegreen" }, /* 9 lime green */ { 0, 999, 999, "cyan" }, /* 10 cyan */ { 878, 999, 999, "lightcyan" }, /* 11 light cyan */ { 678, 847, 901, "lightblue" }, /* 12 light blue */ { 999, 752, 796, "pink" }, /* 13 pink */ { 500, 500, 500, "grey" }, /* 14 grey */ { 750, 750, 750, "silvergrey" } /* 15 silver grey */ }; /* ncurses color (COLOR_...) to mirc color index */ int nc_to_mirc[8] = { 1, 4, 3, 8, 2, 12, 10, 0 }; int mirc_color_cache[16] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; typedef struct { BOOL fg, bg; } is_default_color; is_default_color *pidc = NULL; int n_pidc = 0; int cpairs = 1; void free_colors(void) { free(pidc); } void set_idc(int pair, BOOL fg, BOOL bg) { int new_n = pair + 1, index = 0; if (new_n > n_pidc) pidc = (is_default_color *)realloc(pidc, new_n * sizeof(is_default_color)); for(index=n_pidc; index n_pidc) n_pidc = new_n; } BOOL get_idc_fg(int pair) { if (pair < n_pidc) return pidc[pair].fg; return FALSE; } BOOL get_idc_bg(int pair) { if (pair < n_pidc) return pidc[pair].bg; return FALSE; } /* taken & adapted from http://www.compuphase.com/cmetric.htm */ double colour_distance(rgb_t *e1, rgb_t *e2) { double r_mean = ((double)e1 -> r + (double)e2 -> r) / 2.0; double rd = pow(e1 -> r - e2 -> r, 2.0); double gd = pow(e1 -> g - e2 -> g, 2.0); double bd = pow(e1 -> b - e2 -> b, 2.0); return sqrt((((512.0 + r_mean) * rd) / 256.0) + 4.0 * gd + (((767.0 - r_mean) * bd) / 256.0)); } /* find an RGB value in the COLORS RGB triplets as available by ncurses */ int find_rgb(rgb_t *col) { int index = 0, chosen = -1; double max = 2000000000.0; for(index=0; index= COLOR_PAIRS - 1) return 0; set_idc(cpairs, fg == -1, bg == -1); if (init_pair(cpairs++, fg, bg) != OK) error_exit(FALSE, "init_pair(%d: %d,%d) failed", curmaxpairs, fg, bg); return curmaxpairs; } void test_colors(void) { int id0 = find_alloc_colorpair(COLOR_RED, -1); int id1 = find_alloc_colorpair(COLOR_RED, -1); int id2 = find_alloc_colorpair(COLOR_GREEN, -1); int id3 = find_alloc_colorpair(COLOR_RED, -1); LOG("%d %d,%d\n", id0, get_idc_fg(id0), get_idc_bg(id0)); LOG("%d %d,%d\n", id1, get_idc_fg(id1), get_idc_bg(id1)); LOG("%d %d,%d\n", id2, get_idc_fg(id2), get_idc_bg(id2)); LOG("%d %d,%d\n", id3, get_idc_fg(id3), get_idc_bg(id3)); error_exit(FALSE, ""); } int get_color_mirc(int mfg, int mbg) { int pair = 0; int fg = -1, bg = -1; if (mfg == -1 && mbg == -1) set_idc(0, TRUE, TRUE); else if (mfg == mbg) { mbg++; mbg &= 0x0f; } if (mfg != -1 && mfg < 16) { if (mirc_color_cache[mfg] != -1) fg = mirc_color_cache[mfg]; else mirc_color_cache[mfg] = fg = find_rgb(&mirc_col[mfg]); } if (mbg != -1 && mbg < 16) { if (mirc_color_cache[mbg] != -1) bg = mirc_color_cache[mbg]; else mirc_color_cache[mbg] = bg = find_rgb(&mirc_col[mbg]); } pair = find_alloc_colorpair(fg, bg); return pair; } int get_color_ncurses(int ncol_fg, int ncol_bg) { int pair = 0; if (ncol_fg == -1 && ncol_bg == -1) set_idc(0, TRUE, TRUE); else if (ncol_fg == ncol_bg) { ncol_bg++; ncol_bg %= COLORS; } pair = find_alloc_colorpair(ncol_fg, ncol_bg); return pair; } /* -1: default color, * >= 0: color x * -2: error */ int color_str_convert(const char *in) { int index = 0; if (in[0] == 0x00) return -1; if (in[0] == '#') { rgb_t triplet; if (strlen(&in[1]) != 9) return -2; triplet.r = hextoint(&in[1], 3); triplet.g = hextoint(&in[4], 3); triplet.b = hextoint(&in[7], 3); return find_rgb(&triplet); } if (strcasecmp(in, "default") == 0) return -1; for(index=0; index<16; index++) { if (strcasecmp(mirc_col[index].name, in) == 0) return find_rgb(&mirc_col[index]); } return -2; } char *color_to_str(int color_nr) { int index = 0; short cr = 0, cg = 0, cb = 0; char *name = NULL; if (color_nr == -1) return strdup("default"); color_content(color_nr, &cr, &cg, &cb); for(index=0; index<16; index++) { if (mirc_col[index].r == cr && mirc_col[index].g == cg && mirc_col[index].b == cb) return strdup(mirc_col[index].name); } asprintf(&name, "#%03x%03x%03x", cr, cg, cb); return name; } void emit_colorpair(FILE *fh, short pair) { char *fg_str = NULL, *bg_str = NULL; short cfg = 0, cbg = 0; pair_content(pair, &cfg, &cbg); if (cfg == cbg || pair == 0) { fprintf(fh, "default,default"); return; } fg_str = get_idc_fg(pair) ? color_to_str(-1) : color_to_str(cfg); bg_str = get_idc_bg(pair) ? color_to_str(-1) : color_to_str(cbg); fprintf(fh, "%s,%s", fg_str, bg_str); free(bg_str); free(fg_str); } fi-1.36/headlines.c0000644000175000017500000000420012302365027014033 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 760 $ * (C) 2006-2014 by folkert@vanheusden.com */ #define _GNU_SOURCE #include #include #include #include "headlines.h" #include "term.h" #include "config.h" #include "loop.h" #include "utils.h" string_array_t matchers; static string_array_t headlines; static time_t last_headline_update = 0; static const char *current = NULL; void add_headline_matcher(const char *str) { add_to_string_array(&matchers, str); } BOOL dump_headline_matchers(FILE *fh) { return dump_string_array(&matchers, "headline_matcher", fh); } void init_headlines(void) { init_string_array(&headlines); } void free_headlines(void) { free_string_array(&headlines); myfree(current); current = NULL; } void add_headline(BOOL prio, const char *what) { if (prio && string_array_get_n(&headlines) > 0) { insert_into_string_array(&headlines, 0, what); last_headline_update = 0; } else { while(string_array_get_n(&headlines) >= MAX_HEADLINES_QUEUED) del_nr_from_string_array(&headlines, 0); add_to_string_array(&headlines, what); } } BOOL update_headline(BOOL force) { time_t now = time(NULL); if (headline_window) { if (now - last_headline_update >= NEXT_HEADLINE_INTERVAL && string_array_get_n(&headlines) > 0 && show_headlines) { const char *new = strdup(string_array_get(&headlines, 0)); wclear(headline_window -> win); mvwprintw(headline_window -> win, 0, 0, "%s", new); del_nr_from_string_array(&headlines, 0); myfree(current); current = new; last_headline_update = now; return TRUE; } else if (force && current) { wclear(headline_window -> win); mvwprintw(headline_window -> win, 0, 0, "%s", current); return TRUE; } } return FALSE; } void check_headline_matches(const char *haystack, const char *headline) { int index = 0; for(index=0; index #include #include #include #include #include #include #include "utils.h" #include "irc.h" #include "loop.h" #include "config.h" #include "dcc.h" char *exec_and_strip_ctcp(int sr, int ch, const char *nick_complete, const char *in, BOOL *me) { server *ps = &server_list[sr]; int len = strlen(in), rc = 0; char *out = (char *)calloc(len * 2 + 1, 1), *p = out; char *nick = strdup(nick_complete); memcpy(out, in, len + 1); terminate_str(nick, '!'); LOG("exec_and_strip_ctcp(%s, %s)\n", nick, out); *me = FALSE; for(;;) { BOOL skip = FALSE; int n_to_move = 0; char *marker = strchr(p, '\001'), *end_marker = NULL; if (!marker) break; end_marker = strchr(marker + 1, '\001'); if (!end_marker) LOG("no end-marker? '%s'\n", in); if (strncmp(marker, "\001ACTION ", 8) == 0) { char *work = strdup(&marker[8]); char *temp = NULL; terminate_str(work, '\001'); asprintf(&temp, "* \002%s\002 %s", nick, work); strcpy(out, temp); *me = TRUE; free(temp); free(work); break; } else if (strncmp(marker, "\001VERSION\001", 9) == 0) { const char *reply = "f-irc v" VERSION " by mail@vanheusden.com - http://www.vanheusden.com/f-irc/ (" __DATE__ " " __TIME__ ")"; update_statusline(sr, ch, "CTCP VERSION %s: %s", nick, reply); if (do_send(ps -> fd, "NOTICE %s :\001VERSION %s\001", nick, reply) == -1) { rc = -1; break; } } else if (strncmp(marker, "\001TIME\001", 6) == 0) { time_t now = time(NULL); char *ts = ctime(&now); terminate_str(ts, '\n'); update_statusline(sr, ch, "CTCP TIME %s: %s", nick, ts); if (do_send(ps -> fd, "NOTICE %s :\001TIME %s\001", nick, ts) == -1) { rc = -1; break; } } else if (strncmp(marker, "\001FINGER\001", 8) == 0) { const char *reply = finger_str ? finger_str : "http://vanheusden.com/f-irc/"; update_statusline(sr, ch, "CTCP FINGER %s: %s", nick, reply); if (do_send(ps -> fd, "NOTICE %s :\001FINGER %s\001", nick, reply) == -1) { rc = -1; break; } } else if (strncmp(marker, "\001PING", 5) == 0) { /* make copy and end at the ^A (which ends the ctcp * command) */ BOOL is_reply = FALSE; char *ping = strdup(marker + 1); char *space = strchr(ping, ' '); terminate_str(ping, '\001'); if (space && ps -> user_ping != NULL) { while(*space == ' ') space++; if (atoi(space) == ps -> user_ping_id) /* is it a reply? */ { char *str_buffer = NULL; asprintf(&str_buffer, "Ping to %s took aproximately %f seconds", ps -> user_ping, get_ts() - ps -> t_user_ping); if (log_channel(sr, ch, NULL, str_buffer, TRUE) == -1) rc = -1; free(str_buffer); myfree(ps -> user_ping); ps -> user_ping = NULL; is_reply = TRUE; } } if (is_reply == FALSE) { update_statusline(sr, ch, "CTCP PING %s: %s", nick, ping); if (do_send(ps -> fd, "NOTICE %s :\001%s\001", nick, ping) == -1) rc = -1; } myfree(ping); if (rc == -1) break; } else if (strncmp(marker, "\001CLIENTINFO\001", 12) == 0) { const char *reply = "PING DCC FINGER TIME ACTION USERINFO CLIENTINFO"; update_statusline(sr, ch, "CTCP CLIENTINFO %s: %s", nick, reply); if (do_send(ps -> fd, "NOTICE %s :\001CLIENTINFO %s\001", nick, reply) == -1) { rc = -1; break; } } else if (strncmp(marker, "\001USERINFO\001", 10) == 0 && allow_userinfo) { char *reply = NULL; if (userinfo && strlen(userinfo) > 0) reply = strdup(userinfo); else { struct passwd *p = NULL; setpwent(); while((p = getpwent()) != NULL) { if (p -> pw_uid == getuid()) break; } if (p) asprintf(&reply, "%s (%s)", p -> pw_gecos, p -> pw_name); } if (reply) { update_statusline(sr, ch, "CTCP USERINFO %s: %s", nick, reply); if (do_send(ps -> fd, "NOTICE %s :\001USERINFO %s\001", nick, reply) == -1) rc = -1; free(reply); } if (rc == -1) break; } else if (strncmp(marker, "\001DCC SEND ", 10) == 0) { char *fname = NULL, *pars = NULL; char *dcc = strdup(marker + 10); string_array_t parts; terminate_str(dcc, '\001'); pars = strchr(dcc, ' '); if (dcc[0] == '"') { char *end_fname = strchr(dcc + 1, '"'); if (!end_fname) end_fname = pars; else pars = end_fname; if (end_fname) { *end_fname = 0x00; fname = strdup(dcc + 1); } } else { if (pars) *pars = 0x00; fname = strdup(dcc); } /* skip over ' ' and '"' */ if (pars) pars++; /* seek to end of file name */ while(pars && *pars == ' ') pars++; init_string_array(&parts); split_string(pars, " ", TRUE, &parts); /* dcc: file ip port [size] */ /* ^ADCC SEND flok.txt 2a02:898:62:f6::ae 38113 4^A */ if (string_array_get_n(&parts) >= 3) { const char *ip = string_array_get(&parts, 0); int port = atoi(string_array_get(&parts, 1)); int size = atoi(string_array_get(&parts, 2)); update_statusline(sr, ch, "DCC: receive %s (%d bytes) from %s ([%s]:%d)", fname, size, nick, ip, port); init_recv_dcc(fname, ip, port, sr, ch); } free_splitted_string(&parts); myfree(fname); myfree(dcc); } else { LOG("CTCP request '%s' not known\n", marker); skip = TRUE; } if (skip) p = end_marker + 1; else { n_to_move = strlen(end_marker + 1) + 1; if (n_to_move) memmove(marker, end_marker + 1, n_to_move); p = marker; } } if (strlen(out) == 0) { free(out); out = NULL; asprintf(&out, "(incoming CTCP request)"); } free(nick); if (rc == -1) { free(out); return NULL; } return out; } fi-1.36/utils.h0000644000175000017500000000232512302365027013252 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 709 $ * (C) 2006-2014 by folkert@vanheusden.com */ #ifndef __POLL_H__ #define __POLL_H__ #include #include #include #include #include "string_array.h" void LOG(char *str, ...); int myrand(int max); /*void myfree(const void *p); */ #define myfree(p) free((void *)p); int resize(void **pnt, int n, int *len, int size); ssize_t READ(int fd, char *whereto, size_t len); ssize_t WRITE(int fd, const char *whereto, size_t len); char * read_line_fd(int fd); const char * str_or_nothing(const char *string); const char * skip_colon(const char *in); char * replace_string(const char *in, int pos_start, int pos_end, const char *with_what); unsigned long int myatoul(const char *str); double gettime(void); int count_char(const char *str, char what); double get_ts(void); int hextoint(const char *in, int n); int add_poll(struct pollfd **pfd, int *n_fd, int fd, int events); int mkpath(const char* file_path_in, mode_t mode); int strpos(const char *str, char what); const char *explode_path(const char *in); void terminate_str(char *str, char what); void terminate_str_r(char *str, char what); void run(const char *what, const char *pars[]); #endif fi-1.36/names.h0000644000175000017500000000330512302365027013214 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 819 $ * (C) 2006-2014 by folkert@vanheusden.com */ #ifndef __NAMES_H__ #define __NAMES_H__ extern int names_offset, names_cursor; void free_person(person_t *p); void delete_index(int server_index, int channel_index, int name_index); void change_nick(int server_index, int channel_index, const char *nick, const char *new_nick); void change_name(int server_index, int channel_index, const char *nick, const char *new_name); void change_user_host(int server_index, int channel_index, const char *nick, const char *new_user_host); void search_for_nick(channel *cur_channel, const char *nick, int *found_at, int *insert_at); void add_nick(int server_index, int channel_index, const char *nick, const char *complete_name, const char *user_host); BOOL has_nick(int sr, int ch, const char *nick); void delete_from_channel_by_nick(int sr, int ch, const char *nick); void delete_by_nick(int server_index, const char *nick); void update_user_host(int sr, const char *prefix); void replace_nick(int server_index, const char *old_nick, const char *new_nick); irc_user_mode_t get_nick_mode(int server_index, int channel_index, const char *nick); void set_nick_mode(int server_index, int channel_index, const char *nick, irc_user_mode_t mode); irc_user_mode_t text_to_nick_mode(const char *nick); void free_names_list(channel *pc); void show_name_list(int server_index, int channel_index, NEWWIN *channel_window); void go_to_last_name(void); void do_names_keypress(int c); BOOL has_nick_mode(const char *nick); BOOL is_ignored(int sr, int ch, const char *nick); BOOL unignore_nick(int sr, int ch, const char *nick, BOOL *was); BOOL ignore_nick(int sr, int ch, const char *nick, BOOL *was); #endif fi-1.36/ansi.h0000644000175000017500000000022212302365027013036 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 671 $ * (C) 2006-2014 by folkert@vanheusden.com */ const char *filter_ansi(const char *string_in); fi-1.36/ansi.c0000644000175000017500000000663412302365027013046 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 671 $ * (C) 2006-2014 by folkert@vanheusden.com */ #include #include #include #include #include "gen.h" #include "utils.h" int ansi_color_to_ncurses(int nr) { switch(nr) { case 0: /* black */ return 1; case 1: /* red */ return 4; case 2: /* green */ return 3; case 3: /* yellow */ return 8; case 4: /* blue */ return 2; case 5: /* magenta */ return 12; case 6: /* cyan */ return 11; case 7: /* white */ return 0; } return -1; } void convert_ansi(const char *ansi_in, char *out, int *out_index, int out_len, BOOL *has_color) { char *ansi = strdup(ansi_in); int len = strlen(ansi); int cmd = 0, index = 0; string_array_t parts; BOOL bold = FALSE, underline = FALSE, reverse = FALSE; int fgc = 0, bgc = 1; /* white on black is default */ const char *values = ansi + 1; /* skip ^[ */ BOOL new_has_color = FALSE; init_string_array(&parts); /* remove terminating 'm' */ if (len > 0) { cmd = ansi[len - 1]; ansi[len - 1] = 0x00; } if (cmd != 'm') LOG("unexpected ansi command %c!\n", cmd); if (values[0] == '[') values++; split_string(values, ";", TRUE, &parts); for(index=0; index= 30 && val <= 37) /* foreground color */ { fgc = ansi_color_to_ncurses(val - 30); new_has_color = TRUE; } else if (val >= 40 && val <= 47) /* background color */ { bgc = ansi_color_to_ncurses(val - 40); new_has_color = TRUE; } } if (*has_color) *out_index += snprintf(&out[*out_index], out_len - *out_index, "\x03"); /* ^c terminate previous sequence */ if (bold) *out_index += snprintf(&out[*out_index], out_len - *out_index, "\x02"); /* ^B */ if (underline) *out_index += snprintf(&out[*out_index], out_len - *out_index, "\x1f"); /* ^U */ if (reverse) *out_index += snprintf(&out[*out_index], out_len - *out_index, "\x16"); /* ^V */ *out_index += snprintf(&out[*out_index], out_len - *out_index, "\x03%d,%d", fgc, bgc); *has_color = new_has_color; free_splitted_string(&parts); free(ansi); } const char *filter_ansi(const char *string_in) { int len = strlen(string_in); int out_len = len * 8 + 1; char *out = (char *)calloc(1, out_len); char *ansi = (char *)calloc(1, len + 1); int index = 0, out_index = 0, ansi_index = 0; BOOL is_ansi = FALSE, has_color = FALSE; for(index=0; index 16) { if (c == 'm') /* color */ convert_ansi(ansi, out, &out_index, out_len - 1, &has_color); is_ansi = FALSE; ansi_index = 0; } } else if (c == 27) { is_ansi = TRUE; ansi[ansi_index++] = c; } else out[out_index++] = c; } if (has_color) out[out_index++] = 0x03; out[out_index] = 0x00; free(ansi); return out; } fi-1.36/grep_filter.c0000644000175000017500000000602312302365027014406 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 709 $ * (C) 2006-2014 by folkert@vanheusden.com */ #define _GNU_SOURCE #include #include #include #include #include "gen.h" #include "grep_filter.h" #include "utils.h" grep_target *alloc_grep_target(void) { return (grep_target *)calloc(sizeof(grep_target), 1); } BOOL add_grep_filter(grep_target *gp, const char *re, const char *sserver, const char *schannel, const char *file, char **err) { int rc = 0; gp -> gf_list = (grep_filter_t *)realloc(gp -> gf_list, (gp -> n_gf_list + 1)* sizeof(grep_filter_t)); memset(&gp -> gf_list[gp -> n_gf_list], 0x00, sizeof(grep_filter_t)); rc = regcomp(&gp -> gf_list[gp -> n_gf_list].filter, re, REG_NOSUB); if (rc) { char buffer[4096] = { 0 }; regerror(rc, &gp -> gf_list[gp -> n_gf_list].filter, buffer, sizeof buffer); *err = strdup(buffer); return FALSE; } gp -> gf_list[gp -> n_gf_list].re = strdup(re); gp -> gf_list[gp -> n_gf_list].sserver = strlen(sserver) ? strdup(sserver) : NULL; gp -> gf_list[gp -> n_gf_list].schannel = strlen(schannel) ? strdup(schannel) : NULL; gp -> gf_list[gp -> n_gf_list].file = strlen(file) ? strdup(file) : NULL; gp -> n_gf_list++; *err = NULL; return TRUE; } BOOL process_grep_filter(const grep_target *gp, const char *sserver, const char *schannel, const char *line, char **err, BOOL *match) { BOOL ok = TRUE; int loop = 0; *err = NULL; *match = FALSE; for(loop=0; loop n_gf_list && ok; loop++) { if ((gp -> gf_list[loop].sserver == NULL || strcasecmp(gp -> gf_list[loop].sserver, sserver) == 0) && (gp -> gf_list[loop].schannel == NULL || strcasecmp(gp -> gf_list[loop].schannel, schannel) == 0)) { if (regexec(&gp -> gf_list[loop].filter, line, 0, NULL, 0) == 0) { *match = TRUE; if (gp -> gf_list[loop].file) { FILE *fh = fopen(gp -> gf_list[loop].file, "a+"); if (!fh) { asprintf(err, "Cannot open %s: %s\n", gp -> gf_list[loop].file, strerror(errno)); ok = FALSE; } else { if (fprintf(fh, "%s\n", line) <= 0) { asprintf(err, "Cannot write to %s: %s\n", gp -> gf_list[loop].file, strerror(errno)); ok = FALSE; } fclose(fh); } } } } } return ok; } void free_grep_filters(grep_target *gp) { int loop = 0; for(loop=0; loop n_gf_list; loop++) { regfree(&gp -> gf_list[loop].filter); myfree(gp -> gf_list[loop].re); myfree(gp -> gf_list[loop].sserver); myfree(gp -> gf_list[loop].schannel); myfree(gp -> gf_list[loop].file); } free(gp -> gf_list); gp -> gf_list = NULL; gp -> n_gf_list = 0; } BOOL dump_grep_filters(const grep_target *gp, const char *target, FILE *fh) { int loop = 0; if (gp -> n_gf_list) fprintf(fh, ";\n"); for(loop=0; loop n_gf_list; loop++) { if (fprintf(fh, "%s=%s,%s,%s,%s\n", target, str_or_nothing(gp -> gf_list[loop].sserver), str_or_nothing(gp -> gf_list[loop].schannel), str_or_nothing(gp -> gf_list[loop].file), gp -> gf_list[loop].re) <= 0) return FALSE; } return TRUE; } fi-1.36/term.h0000644000175000017500000000312412302365027013057 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 789 $ * (C) 2006-2014 by folkert@vanheusden.com */ #ifndef __TERM_H__ #define __TERM_H__ #include #include typedef struct { WINDOW *win; PANEL *pwin; int nlines, ncols; int x, y; } NEWWIN; extern NEWWIN *override_cursor_win; extern int override_cursor_x, override_cursor_y; extern int default_colorpair, highlight_colorpair, meta_colorpair, error_colorpair, temp_colorpair, markerline_colorpair; extern int max_y, max_x; void wrong_key(void); void color_on(NEWWIN *win, int pair); void color_off(NEWWIN *win, int pair); void mywattron(WINDOW *w, int a); void mywattroff(WINDOW *w, int a); void mywbkgd(NEWWIN *win, int pair); void mydelwin(NEWWIN *win); void mydoupdate(); void delete_window(NEWWIN *mywin); NEWWIN * create_window(int n_lines, int n_colls); NEWWIN * create_window_xy(int y_offset, int x_offset, int n_lines, int n_colls); void limit_print(NEWWIN *win, int width, int y, int x, const char *format, ...); void escape_print_xy(NEWWIN *win, int y, int x, const char *str); void escape_print(NEWWIN *win, const char *str, const char reverse, const char underline); void determine_terminal_size(void); void create_win_border(int width, int height, const char *title, NEWWIN **bwin, NEWWIN **win, BOOL f1); void initcol(void); void apply_mouse_setting(void); void init_ncurses(BOOL ignore_mouse); void reset_attributes(NEWWIN *win); BOOL is_in_window(NEWWIN *win, int x, int y); BOOL right_mouse_button_clicked(void); void display_markerline(NEWWIN *win, const char *msg); void simple_marker(NEWWIN *win); #endif fi-1.36/scrollback.c0000644000175000017500000003416312302365027014231 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 861 $ * (C) 2006-2014 by folkert@vanheusden.com */ #define _GNU_SOURCE #include #include #include #include #include #include "gen.h" #include "buffer.h" #include "term.h" #include "user.h" #include "utils.h" #include "scrollback.h" #include "loop.h" #include "config.h" #include "main.h" #include "theme.h" #include "help.h" void search_in_buffer_popup(const buffer *pb, const char *highlight) { NEWWIN *bwin = NULL, *win = NULL; const char *what = edit_box(60, "^Search...^", NULL); char *title = NULL; buffer *search_results = NULL; if (!what) return; /* user selected cancel */ if (strlen(what) == 0) { myfree(what); return; /* user pressed enter without nothing entered */ } search_results = search_in_buffer_new(pb, what, FALSE); asprintf(&title, "Press left cursor key to exit | Searched %s", what); create_win_border(max_x - 4, max_y - 4, title, &bwin, &win, TRUE); scrollback(win, search_results, what, FALSE, TRUE); delete_window(win); delete_window(bwin); mydoupdate(); myfree(title); free_buffer(search_results); myfree(what); } void search_everywhere(void) { NEWWIN *bwin = NULL, *win = NULL; const char *what = edit_box(60, "^Search all ...^", NULL); char *title = NULL; int sr = 0; buffer *search_results = NULL; if (!what || strlen(what) == 0) { myfree(what); return; /* user selected cancel or enter with nothing entered */ } search_results = create_buffer(0); for(sr=0; sr n_channels; ch++) { channel *pc = &ps -> pchannels[ch]; search_in_buffer(pc -> pbuffer, search_results, what, TRUE); } } asprintf(&title, "Press left cursor key to exit | Searched for %s", what); create_win_border(max_x - 4, max_y - 4, title, &bwin, &win, TRUE); scrollback(win, search_results, what, FALSE, TRUE); delete_window(win); delete_window(bwin); mydoupdate(); myfree(title); free_buffer(search_results); myfree(what); } void generate_line(buffer_element_t *line, BOOL with_server, BOOL with_channel, int max_width, char **out, BOOL *did_fit) { if (with_channel) { const char *c_name = server_list[line -> sr].pchannels[line -> ch].channel_name; if (with_server) { const char *s_name = server_list[line -> sr].description; if (s_name == NULL) s_name = server_list[line -> sr].server_host; asprintf(out, "%s|%s] %s", s_name, c_name, line -> msg); } else { asprintf(out, "%s] %s", c_name, line -> msg); } } else { *out = strdup(line -> msg); } *did_fit = TRUE; if (max_width > 0 && strlen(*out) > max_width) { *did_fit = FALSE; (*out)[max_width - 1] = 0x00; } } void write_buffer_to_file(const buffer *pb, BOOL with_channel) { const char *file = edit_box(60, "^Select file to write to...^", NULL); if (file) { FILE *fh = fopen(file, "wb"); if (!fh) popup_notify(FALSE, "Failed to create file: %s", strerror(errno)); else { BOOL err = FALSE; int loop = 0; for(loop=0; loop 1, with_channel, -1, &msg, &fit); err |= fprintf(fh, "%s\n", msg) < 1; free(msg); } fclose(fh); if (err) popup_notify(FALSE, "An error occured writing to file %s", file); } myfree(file); } } void scrollback_displayline(NEWWIN *win, buffer_element_t *line, const int terminal_offset, const char *highlight, BOOL with_channel, int max_width, BOOL force_partial_highlight) { nick_color_settings ncs; char *out = NULL; BOOL fit = FALSE; generate_line(line, n_servers > 1, with_channel, max_width, &out, &fit); wmove(win -> win, terminal_offset, 0); if (line -> line_type == BET_MARKERLINE) gen_display_markerline(win, line -> when); else { find_nick_colorpair(line -> msg_from, &ncs); output_to_window(win, out, highlight, line -> line_type, nick_color ? &ncs : NULL, force_partial_highlight, fit); } myfree(out); } void global_search(const char *highlight, const char *search_what) { NEWWIN *bwin = NULL, *win = NULL; const char *what = search_what ? strdup(search_what) : edit_box(60, "^Global search...^", NULL); char *title = NULL; buffer *search_results = create_buffer(2500); int s = 0, c = 0; if (!what) { free_buffer(search_results); return; /* user selected cancel */ } if (strlen(what) == 0) { myfree(what); free_buffer(search_results); return; /* user pressed enter without nothing entered */ } asprintf(&title, "Press left cursor key to exit | Global search for %s", what); create_win_border(max_x - 4, max_y - 4, title, &bwin, &win, TRUE); for(s=0; s line_type == BET_MARKERLINE) return 1; return (strlen(pbe -> msg) + width) / width; } void replace_input_buffer(const char *selected_line) { if (utf8_strlen(cur_channel() -> input) > 0) { char *temp = utf8_get_utf8(cur_channel() -> input); add_to_buffer(cur_channel() -> input_buffer, temp, cur_server() -> nickname, FALSE, current_server, current_server_channel_nr); myfree(temp); } utf8_truncate(cur_channel() -> input, 0); utf8_strcat_ascii(cur_channel() -> input, selected_line); } void scrollback(NEWWIN *win, const buffer *pbuffer, const char *highlight, BOOL with_channel, BOOL force_partial_highlight) { int buffer_size = get_buffer_n_elements(pbuffer); int offset = buffer_size - 1, n_lines = 0; if (offset < 0) offset = 0; for(;;) { int cur_n_lines = -1; if (offset == 0) break; cur_n_lines = calc_line_height(get_from_buffer(pbuffer, offset - 1), win -> ncols); if (n_lines + cur_n_lines >= win -> nlines) break; n_lines += cur_n_lines; offset--; } for(;;) { int c = 0; int index = 0, lines_used = 0; werase(win -> win); for(;offset + index < get_buffer_n_elements(pbuffer);) { int prev_lines_used = lines_used, lines_left = win -> nlines - lines_used; buffer_element_t *pbe = get_from_buffer(pbuffer, offset + index); if (lines_used >= win -> nlines) break; lines_used += calc_line_height(pbe, win -> ncols); scrollback_displayline(win, pbe, prev_lines_used, highlight, with_channel, win -> ncols * lines_left, force_partial_highlight); index++; } if (lines_used < win -> nlines) simple_marker(win); mydoupdate(); c = wait_for_keypress(FALSE); if (toupper(c) == 'Q' || toupper(c) == 'X') break; else if (c == KEY_LEFT || (c == KEY_MOUSE && right_mouse_button_clicked())) break; else if (c == 2) /* ^B scrollback & select */ { char *selected_line = NULL; int sr = -1, ch = -1; NEWWIN *bwin = NULL, *win = NULL; create_win_border(max_x - 6, max_y - 4, "right cursor key: select, left: cancel", &bwin, &win, TRUE); if (scrollback_and_select(win, pbuffer, &selected_line, &sr, &ch, cur_server() -> nickname, with_channel, force_partial_highlight)) { replace_input_buffer(selected_line); free(selected_line); } delete_window(win); delete_window(bwin); break; } else if (c == KEY_F(1)) scrollback_help(); else if (c == 3) exit_fi(); else if (c == 'w') /* write to file */ write_buffer_to_file(pbuffer, with_channel); else if (c == '/') /* search */ search_in_buffer_popup(pbuffer, highlight); else if (c == KEY_RIGHT) wrong_key(); else if (c == KEY_UP && offset > 0) offset--; else if (c == KEY_DOWN && offset < get_buffer_n_elements(pbuffer) - 1) offset++; else if (c == KEY_NPAGE && offset < get_buffer_n_elements(pbuffer) - 1) { int dummy = 0; while(offset < get_buffer_n_elements(pbuffer) - 1) { offset++; dummy += calc_line_height(get_from_buffer(pbuffer, offset), win -> ncols); if (dummy > win -> nlines) { offset--; break; } } } else if (c == KEY_PPAGE && offset > 0) { int dummy = 0; while(offset > 0) { offset--; dummy += calc_line_height(get_from_buffer(pbuffer, offset), win -> ncols); if (dummy > win -> nlines) { offset++; break; } } } else if (c == KEY_HOME && offset > 0) offset = 0; else if (c == KEY_END && offset < get_buffer_n_elements(pbuffer) - 1) offset = get_buffer_n_elements(pbuffer) - 1; else if (c == 'm' && offset > 0) { BOOL found = false; int temp_offset = offset - 1; for(;;) { if (get_from_buffer(pbuffer, temp_offset) -> line_type == BET_MARKERLINE) { offset = temp_offset; found = TRUE; break; } if (temp_offset <= 0) break; temp_offset--; } if (!found) wrong_key(); } else { wrong_key(); } } } BOOL scrollback_and_select(NEWWIN *win, const buffer *pbuffer, char **sel_str, int *sr, int *ch, const char *needle, BOOL with_channel, BOOL force_partial_highlight) { int ppos = -1; int offset = 0, cursor = 0; const char *search_for = NULL; for(;;) { int c = 0; /* redraw */ if (offset + cursor != ppos) { int loop = 0, end_at = min(get_buffer_n_elements(pbuffer) - offset, win -> nlines); werase(win -> win); for(loop=0; loop < min(get_buffer_n_elements(pbuffer) - offset, win -> nlines); loop++) { int index = offset + loop; if (loop == cursor) mywattron(win -> win, A_REVERSE); if (index & 1) mywattron(win -> win, A_BOLD); scrollback_displayline(win, get_from_buffer(pbuffer, index), loop, needle, with_channel, win -> ncols, force_partial_highlight); if (index & 1) mywattroff(win -> win, A_BOLD); if (loop == cursor) mywattroff(win -> win, A_REVERSE); } if (end_at < win -> nlines) simple_marker(win); mydoupdate(); ppos = offset + cursor; } c = wait_for_keypress(FALSE); if (tolower(c) == 'q') return FALSE; else if (c == KEY_LEFT || (c == KEY_MOUSE && right_mouse_button_clicked())) return FALSE; else if (c == KEY_RIGHT || c == 13) { if (offset + cursor < get_buffer_n_elements(pbuffer)) { buffer_element_t *pbe = get_from_buffer(pbuffer, offset + cursor); *sel_str = strdup(pbe -> msg); *sr = pbe -> sr; *ch = pbe -> ch; return TRUE; } wrong_key(); } else if (c == KEY_F(1)) scrollback_and_select_help(); else if (c == 'w') /* write to file */ write_buffer_to_file(pbuffer, TRUE); else if (c == '/') /* search */ { const char *what = edit_box(60, "^Search...^", NULL); myfree(search_for); search_for = NULL; if (what) { int new_index = search_in_buffer_index(pbuffer, what, offset + cursor + 1); if (new_index != -1) { offset = new_index; cursor = 0; search_for = what; } else { wrong_key(); } } } else if (c == 'n') { if (search_for) { int new_index = search_in_buffer_index(pbuffer, search_for, offset + cursor + 1); if (new_index != -1) { offset = new_index; cursor = 0; } else { wrong_key(); } } else { wrong_key(); } } else if (c == KEY_HOME) { offset = cursor = 0; } else if (c == KEY_END) { int n_lines = get_buffer_n_elements(pbuffer); if (n_lines >= win -> nlines) { cursor = win -> nlines - 1; offset = n_lines - win -> nlines; } else { cursor = n_lines - 1; offset = 0; } } else if (c == KEY_UP) { if (cursor > 0) cursor--; else if (offset > 0) offset--; else wrong_key(); } else if (c == KEY_DOWN) { if (cursor + offset < get_buffer_n_elements(pbuffer) - 1) { if (cursor < win -> nlines - 1) cursor++; else offset++; } else { wrong_key(); } } else if (c == KEY_PPAGE) { if (cursor) cursor = 0; else if (offset >= win -> nlines -1) offset -= win -> nlines - 1; else wrong_key(); } else if (c == KEY_NPAGE) { if (cursor + offset < get_buffer_n_elements(pbuffer) - (win -> nlines - 1)) offset += win -> nlines - 1; else wrong_key(); } else if (c == 3) { exit_fi(); } else { wrong_key(); } } myfree(search_for); return FALSE; } void show_channel_history(void) { NEWWIN *bwin = NULL, *win = NULL; create_win_border(max_x - (theme.channellist_window_width + 2), max_y - 4, "Press left cursor key to exit", &bwin, &win, TRUE); scrollback(win, cur_channel() -> pbuffer, cur_server() -> nickname, FALSE, FALSE); delete_window(win); delete_window(bwin); } void select_own_history_line(BOOL search_in_all) { char *selected_line = NULL; int sr = -1, ch = -1; NEWWIN *bwin = NULL, *win = NULL; BOOL rc = FALSE; create_win_border(max_x - 6, max_y - 4, "right cursor key: select, left: cancel", &bwin, &win, TRUE); if (search_in_all) { buffer *temp = create_buffer(0); int sr=0; for(sr=0; sr n_channels; ch++) add_buffer_to_buffer(temp, ps -> pchannels[ch].input_buffer); } sort_buffer(temp, TRUE); rc = scrollback_and_select(win, temp, &selected_line, &sr, &ch, cur_server() -> nickname, TRUE, FALSE); free_buffer(temp); } else { rc = scrollback_and_select(win, cur_channel() -> input_buffer, &selected_line, &sr, &ch, cur_server() -> nickname, FALSE, FALSE); } if (rc) { replace_input_buffer(selected_line); myfree(selected_line); } delete_window(win); delete_window(bwin); } fi-1.36/utf8.c0000644000175000017500000001536412302365027013002 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 671 $ * (C) 2006-2014 by folkert@vanheusden.com */ #include #include #include #include #include "gen.h" #include "utf8.h" #include "utils.h" utf8_string *alloc_utf8_string() { return (utf8_string *)calloc(1, sizeof(utf8_string)); } utf8_string *utf8_strdup(const utf8_string *in) { utf8_string *out = alloc_utf8_string(); int n_bytes = in -> len * sizeof(utf8_byte); out -> string = (utf8_byte *)malloc(n_bytes); memcpy(out -> string, in -> string, n_bytes); out -> len = in -> len; return out; } void free_utf8_string(const utf8_string *in) { myfree(in -> string); myfree((void *)in); } void truncate_utf8_string(utf8_string *work) { free(work -> string); work -> string = NULL; work -> len = 0; work -> working = FALSE; work -> work_buffer_in = 0; memset(work -> work_buffer, 0x00, sizeof work -> work_buffer); } void char_to_utf8_byte(const int c, utf8_byte *out) { out -> ascii = c; memset(out -> utf8, 0x00, sizeof(out -> utf8)); } BOOL add_stream_to_utf8_string(utf8_string *out, const char cur_byte) { if (out -> working) { out -> work_buffer[out -> work_buffer_in++] = cur_byte; if (out -> work_buffer_in == count_utf_bytes(out -> work_buffer[0])) { utf8_byte ub; ub.ascii = (out -> work_buffer[0] & 0x3f) | ((out -> work_buffer[1] & 3) << 6); memcpy(ub.utf8, out -> work_buffer, sizeof ub.utf8); add_utf8_to_utf8_string(out, ub); out -> working = FALSE; out -> work_buffer_in = 0; memset(out -> work_buffer, 0x00, sizeof out -> work_buffer); return TRUE; } return FALSE; } else if (count_utf_bytes(cur_byte) > 1) { out -> working = TRUE; out -> work_buffer[out -> work_buffer_in++] = cur_byte; return FALSE; } else { utf8_byte ub; ub.ascii = cur_byte; ub.utf8[0] = 0x00; add_utf8_to_utf8_string(out, ub); return TRUE; } return FALSE; } void utf8_strcat_ascii(utf8_string *work, const char *in) { const int len = strlen(in); int loop; for(loop=0; loop string[loop]); } void add_utf8_to_utf8_string(utf8_string *out, const utf8_byte in) { assert(out -> len >= 0); out -> string = realloc(out -> string, (out -> len + 1) * sizeof(utf8_byte)); memcpy(&out -> string[out -> len], &in, sizeof in); out -> len++; } int utf8_strlen(const utf8_string *in) { assert(in -> len >= 0); return in -> len; } char *utf8_get_ascii(const utf8_string *in) { return utf8_get_ascii_pos(in, 0); } char *utf8_get_ascii_pos(const utf8_string *in, const int pos) { char *out = (char *)malloc(in -> len + 1); int loop; assert(pos >= 0); assert(pos < in -> len || (in -> len == 0 && pos == 0)); assert(in -> len >= 0); for(loop=pos; loop len; loop++) out[loop - pos] = in -> string[loop].ascii; out[loop - pos] = 0x00; return out; } char *utf8_get_utf8(const utf8_string *in) { return utf8_get_utf8_pos(in, 0); } char *utf8_get_utf8_pos(const utf8_string *in, const int pos) { int out_index = 0, loop; char *out = (char *)malloc(in -> len * 4 + 1); assert(pos >= 0); assert(pos < in -> len || (in -> len == 0 && pos == 0)); assert(in -> len >= 0); for(loop=pos; loop len; loop++) { if (in -> string[loop].utf8[0]) { int n = count_utf_bytes(in -> string[loop].utf8[0]); memcpy(&out[out_index], in -> string[loop].utf8, n); out_index += n; } else { out[out_index++] = in -> string[loop].ascii; } } out[out_index] = 0x00; return out; } wchar_t *utf8_get_wchar(const utf8_string *in) { return utf8_get_wchar_pos(in, 0); } wchar_t *utf8_get_wchar_pos(const utf8_string *in, int pos) { wchar_t *out = (wchar_t *)malloc((in -> len + 1) * sizeof(wchar_t)); char *temp = utf8_get_utf8_pos(in, pos); const char *dummy = temp; int n = 0; assert(pos >= 0); assert(pos < in -> len || (in -> len == 0 && pos == 0)); assert(in -> len >= 0); n = mbsrtowcs(out, &dummy, in -> len, NULL); if (n >= 0) out[n] = 0x00; myfree(temp); return out; } void utf8_del_pos(utf8_string *in, const int pos) { int n_to_move = (in -> len - pos) - 1; assert(pos >= 0); assert(pos < in -> len || (in -> len == 0 && pos == 0)); assert(in -> len >= 0); assert(n_to_move >= 0); if (n_to_move > 0) memmove(&in -> string[pos], &in -> string[pos + 1], n_to_move * sizeof(utf8_byte)); in -> len--; } void utf8_insert_pos_utf8(utf8_string *work, const int pos, const utf8_byte in) { assert(pos >= 0); assert(pos < work -> len || (work -> len == 0 && pos == 0)); assert(work -> len >= 0); work -> string = realloc(work -> string, (work -> len + 1) * sizeof(utf8_byte)); memmove(&work -> string[pos + 1], &work -> string[pos], (work -> len - pos) * sizeof(utf8_byte)); memcpy(&work -> string[pos], &in, sizeof in); work -> len++; } void utf8_insert_pos_ascii(utf8_string *work, const int pos, const int c) { utf8_byte ub; char_to_utf8_byte(c, &ub); utf8_insert_pos_utf8(work, pos, ub); } int utf8_find_nonblank_reverse(const utf8_string *work, const int start) { int cur_pos = start; assert(start >= 0); if (cur_pos == work -> len) cur_pos--; assert(cur_pos < work -> len); while(cur_pos > 0 && work -> string[cur_pos].ascii == ' ') cur_pos--; if (cur_pos == 0 && work -> string[cur_pos].ascii == ' ') return -1; return cur_pos; } int utf8_find_nonblank(const utf8_string *work, const int start) { int cur_pos = start; assert(start >= 0); assert(start < work -> len || (work -> len == 0 && start == 0)); while(cur_pos < work -> len && work -> string[cur_pos].ascii == ' ') cur_pos++; if (cur_pos == work -> len) return -1; return cur_pos; } int utf8_find_blank_reverse(const utf8_string *work, const int start) { int loop; assert(start >= 0); for(loop=start; loop > -1; loop--) { if (work -> string[loop].ascii == ' ') return loop; } return -1; } int utf8_ascii_get_at(const utf8_string *in, const int pos) { assert(pos >= 0); assert(pos < in -> len || (in -> len == 0 && pos == 0)); return in -> string[pos].ascii; } void utf8_ascii_set_at(utf8_string *work, const int pos, const char what) { assert(pos >= 0); assert(pos < work -> len || (work -> len == 0 && pos == 0)); work -> string[pos].ascii = what; memset(work -> string[pos].utf8, 0x00, sizeof(work -> string[pos].utf8)); work -> string[pos].utf8[0] = what; } void utf8_truncate(utf8_string *work, const int new_size) { assert(new_size >= 0 && new_size <= work -> len); work -> len = new_size; } int count_utf_bytes(int c) { if ((c & 0xe0) == 0xc0) return 2; else if ((c & 0xf0) == 0xe0) return 3; else if ((c & 0xf8) == 0xf0) return 4; return 1; } fi-1.36/scrollback.h0000644000175000017500000000144412302365027014232 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 762 $ * (C) 2006-2014 by folkert@vanheusden.com */ #include "gen.h" #include "term.h" void search_everywhere(void); void write_buffer_to_file(const buffer *pb, BOOL with_channel); void global_search(const char *highlight, const char *title_in); void scrollback_new(const buffer *pbuffer, const char *highlight, const char *title_in, BOOL with_channel, BOOL force_partial_highlight); void scrollback(NEWWIN *win, const buffer *pbuffer, const char *highlight, BOOL with_channel, BOOL force_partial_highlight); BOOL scrollback_and_select(NEWWIN *win, const buffer *pbuffer, char **value, int *sr, int *ch, const char *needle, BOOL with_channel, BOOL force_partial_highlight); void show_channel_history(void); void select_own_history_line(BOOL search_in_all); fi-1.36/tcp.h0000644000175000017500000000125412302365027012700 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 696 $ * (C) 2006-2014 by folkert@vanheusden.com */ #ifndef __TCP_H__ #define __TCP_H__ typedef enum { TCS_CONNECTED, TCS_IN_PROGRESS, TCS_ERROR } cstate_t; typedef struct { struct addrinfo *result; struct addrinfo **alist; int alist_n, index, attempt; } resolve_info; void free_resolve_info(resolve_info *ri); BOOL resolve(const char *host, int portnr, resolve_info *ri, char **message); int connect_to(resolve_info *ri, char **message); const char *get_ip(resolve_info *ri); int setup_nonblocking_socket(void); int set_no_delay(int fd); cstate_t check_connection_progress(const int fd); char * get_endpoint_name(int fd); #endif fi-1.36/changelog.gz0000644000175000017500000000010312302365027014222 0ustar folkertfolkert‹qš˜Rchangelog ÉÈÌK/VÈH,KUHÎHÌKOMÑQ(ÉH­THÌÉQHɇP‰•z\H¯…*fi-1.36/key_value.h0000644000175000017500000000153212302365027014075 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 671 $ * (C) 2006-2014 by folkert@vanheusden.com */ #ifndef __KEY_VALUE_H__ #define __KEY_VALUE_H__ typedef struct { const char *key; const void *value; } key_value_pair; typedef struct { key_value_pair *pairs; int n; } key_value; key_value * allocate_kv(void); void free_kv(key_value *p); void add_to_kv(key_value *work, const char *key, const void *value); int get_n_kv_from_kv(const key_value *in); const char *get_key_by_index(const key_value *in, int nr); const void *get_value_by_index(const key_value *in, int nr); const void * get_from_kv(const key_value *in, const char *key); BOOL update_kv(key_value *work, const char *key, const void *new_value); void truncate_kv(key_value *work); void sort_kv(key_value *work, BOOL asc, BOOL key, int (*value_cmp)(const void *pv1, const void *pv2)); #endif fi-1.36/chistory.c0000644000175000017500000000207112302365027013747 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 671 $ * (C) 2006-2014 by folkert@vanheusden.com */ #include #include #include "chistory.h" ch_t *channel_history = NULL; int channel_history_max_n = 2; void init_channel_history(int max_n) { int loop = 0; channel_history = calloc(sizeof(ch_t), max_n); channel_history_max_n = max_n; for(loop=0; loop #include #include #include #include #include "gen.h" #include "term.h" #include "channels.h" #include "loop.h" #include "colors.h" #include "main.h" #include "user.h" time_t prev_tooltip = 0; const int tooltip_interval = 5; int tooltip_nr = 0; const char *tooltips[] = { "Press right cursor key to select channel", "Press F8 to edit configuration", "Press F8 to add/edit a server", NULL }; const char *get_tooltip(void) { time_t now = time(NULL); if (now - prev_tooltip >= tooltip_interval) { int nr = rand() % 100, index = 0; while(nr > 0) { ++index; if (tooltips[index] == NULL) index = 0; nr--; } tooltip_nr = index; prev_tooltip = now; } return tooltips[tooltip_nr]; } void main_help(void) { const int help_win_width = 77, help_win_height = 17; NEWWIN *bwin = NULL, *win = NULL; int screen_nr = 0; create_win_border(help_win_width, help_win_height, "left cursor key: previous screen / exit help, right key: next screen", &bwin, &win, FALSE); for(;;) { int c = 0; werase(win -> win); if (screen_nr == 0) { escape_print(win, "\n", '|', '_'); escape_print(win, " Press |right cursor key| to:\n", '|', '_'); escape_print(win, " - enter a screen\n", '|', '_'); escape_print(win, " - unfold a server (=show channels)\n", '|', '_'); escape_print(win, " - show a server commands menu\n", '|', '_'); escape_print(win, " - when in a channel: show list of people (nicks)\n", '|', '_'); escape_print(win, " - when in a list of people: show a user menu\n", '|', '_'); escape_print(win, " - in the word cloud: do a search for the selected word\n", '|', '_'); escape_print(win, " \n", '|', '_'); escape_print(win, " Press |left cursor key| to:\n", '|', '_'); escape_print(win, " - leave a screen\n", '|', '_'); escape_print(win, " - fold a server (=hide channels)\n", '|', '_'); escape_print(win, " - go back from the people list to the channel list\n", '|', '_'); escape_print(win, " \n", '|', '_'); escape_print(win, " Press right cursor key to go to the next help-screen\n", '|', '_'); escape_print(win, " which lists the shortcut keys.\n", '|', '_'); } else if (screen_nr == 1) { escape_print(win, "\n", '|', '_'); escape_print(win, " |^A| moves the cursor to the left |^E| move right |^D| deletes current character\n", '|', '_'); escape_print(win, " |^U| clear line, enter twice to undo clear\n", '|', '_'); escape_print(win, " |^W| jump to the next channel with new text - ^R jump backwards\n", '|', '_'); escape_print(win, " |^Z| jump to the next channel with your nick name - ^X search backwards\n", '|', '_'); escape_print(win, " |^Q| jump to next favorite channel (see configuration file)\n", '|', '_'); escape_print(win, " |^O| remove all the *'s (forget what channels have unread text)\n", '|', '_'); escape_print(win, " |^T| go back to the previous selected server/channel\n", '|', '_'); escape_print(win, " |^F| scroll-back in what was written by others\n", '|', '_'); escape_print(win, " |^B| scroll-back in what you wrote, enter to select, |^S| search all\n", '|', '_'); escape_print(win, " |^V| Enter 1 character: used when entering ascii values < 32\n", '|', '_'); escape_print(win, " |TAB| auto-completion of commands and nick names\n", '|', '_'); escape_print(win, " |^G| close a channel (F9: undo close)\n", '|', '_'); escape_print(win, " |^C| terminate the program\n", '|', '_'); if (vc_list_data_only) escape_print(win, "*|^Y| toggle \"only show channels with new messages\"", '|', '_'); else escape_print(win, " |^Y| toggle \"only show channels with new messages\"", '|', '_'); escape_print(win, " |^P| add markerline\n", '|', '_'); } else if (screen_nr == 2) { escape_print(win, "\n", '|', '_'); escape_print(win, " |F1| this help\n", '|', '_'); escape_print(win, " |F2| store current configuration on disk\n", '|', '_'); /* escape_print(win, " |F3| add server\n", '|', '_'); */ escape_print(win, " |F4| switch to edit-line (shortcut for ^N)\n", '|', '_'); escape_print(win, " |F5|/|^L| redraw screen\n", '|', '_'); escape_print(win, " |F6| search in all windows for text\n", '|', '_'); escape_print(win, " |F7| close all channels with only \"NOTICE\" messages\n", '|', '_'); if (n_servers <= 1) escape_print(win, " |F8| _edit configuration_\n", '|', '_'); else escape_print(win, " |F8| edit configuration\n", '|', '_'); escape_print(win, " |F9| undo last channel close\n", '|', '_'); escape_print(win, " |F10|/|^N| toggle between channel-list, edit-line and word-cloud\n", '|', '_'); escape_print(win, " |@/|... sends /... to other end\n", '|', '_'); escape_print(win, " |@@|... sends @... to other end\n", '|', '_'); escape_print(win, " |@|... goes to the first channel with \"...\" in its name |^J| for the next\n", '|', '_'); } else if (screen_nr == 3) { const char pairs[] = " Defined pairs: "; int x = sizeof pairs, index = 0, n_cpairs = get_n_cpairs(); char *nc_vars = NULL; wchar_t block = 0; char *pblock = "\xe2\x96\x85"; char *res = NULL; mbsrtowcs(&block, (const char **)&pblock, 1, NULL); escape_print(win, "\n If you're on IRCNet, OFTC or FreeNet, say hi to |flok|/|flok99|/|flok42|!\n\n", '|', '_'); waddstr(win -> win, " Compiled on " __DATE__ " " __TIME__ "\n\n"); asprintf(&res, " Columns/rows: %dx%d\n\n", max_x, max_y); waddstr(win -> win, res); free(res); asprintf(&nc_vars, " Colors: %d, pairs: %d, pairs defined: %d, can change colors: %s,\n nick color pairs: %d\n", COLORS, COLOR_PAIRS, n_cpairs, can_change_color() ? "yes" : "no", n_nick_pairs); waddstr(win -> win, nc_vars); waddstr(win -> win, "\n"); waddstr(win -> win, pairs); for(index=0; index win, &block, 1); color_off(win, index); if (++x == help_win_width - 2) { waddstr(win -> win, "\n "); x = 1; } } free(nc_vars); } mydoupdate(); c = wait_for_keypress(FALSE); if (c == 'q' || c == 'Q' || c == -1) break; else if (c == 3) exit_fi(); else if (c == KEY_LEFT || (c == KEY_MOUSE && right_mouse_button_clicked())) { if (screen_nr == 0) break; screen_nr--; } else if (c == KEY_RIGHT) { if (screen_nr < 3) screen_nr++; else wrong_key(); } } delete_window(win); delete_window(bwin); mydoupdate(); } void configure_firc_help(void) { const int help_win_width = 77, help_win_height = 17; NEWWIN *bwin = NULL, *win = NULL; create_win_border(help_win_width, help_win_height, "Configure f-irc help | Press any key to exit this help", &bwin, &win, FALSE); werase(win -> win); escape_print(win, "\n", '|', '_'); escape_print(win, " Main configuration screen:\n", '|', '_'); escape_print(win, " Navigate through items list using the cursor keys.\n", '|', '_'); escape_print(win, " Press |/| to search for a configuration item.\n", '|', '_'); escape_print(win, " Press |enter| to change an item.\n", '|', '_'); escape_print(win, "\n", '|', '_'); escape_print(win, " Changing an item:\n", '|', '_'); escape_print(win, " ON/OFF can be toggled using the |space bar|.\n", '|', '_'); escape_print(win, " Select between OK and CANCEL using the |TAB| key.\n", '|', '_'); escape_print(win, " Press |enter| to select either OK or CANCEL.\n", '|', '_'); escape_print(win, "\n", '|', '_'); escape_print(win, " Editing text/number:\n", '|', '_'); escape_print(win, " Navigate with the cursor keys or |^A| and |^E|.\n", '|', '_'); escape_print(win, " |^U| to clear the line.\n", '|', '_'); escape_print(win, " Select between OK and CANCEL using the |TAB| key.\n", '|', '_'); escape_print(win, " Press |enter| to select either OK or CANCEL.\n", '|', '_'); mydoupdate(); (void)wait_for_keypress(FALSE); delete_window(win); delete_window(bwin); mydoupdate(); } void edit_box_help(void) { const int help_win_width = 77, help_win_height = 17; NEWWIN *bwin = NULL, *win = NULL; create_win_border(help_win_width, help_win_height, "Edit text help | Press any key to exit this help", &bwin, &win, FALSE); werase(win -> win); escape_print(win, "\n", '|', '_'); escape_print(win, " |^A| move cursor to start of line\n", '|', '_'); escape_print(win, " |^E| move cursor to end of line\n", '|', '_'); escape_print(win, " |^D| delete the character under the cursor\n", '|', '_'); escape_print(win, " |^U| erase line\n", '|', '_'); escape_print(win, " |^W| delete the word left from the cursor\n", '|', '_'); mydoupdate(); (void)wait_for_keypress(FALSE); delete_window(win); delete_window(bwin); mydoupdate(); } void commandline_help(void) { popup_notify(TRUE, "The only command line parameter accepted is the file name of the\nconfiguration file. Leave empty to create a default one. Servers\ncan be configured from within f-irc (press F8)."); } void scrollback_help(void) { const int help_win_width = 77, help_win_height = 17; NEWWIN *bwin = NULL, *win = NULL; create_win_border(help_win_width, help_win_height, "Scrollback help | Press any key to exit this help", &bwin, &win, FALSE); werase(win -> win); escape_print(win, "\n", '|', '_'); escape_print(win, " |w| write to file\n", '|', '_'); escape_print(win, " |c| copy to X clipboard (requires xclip)\n", '|', '_'); escape_print(win, " |/| search\n", '|', '_'); escape_print(win, " |m| jump to marker line\n", '|', '_'); escape_print(win, " |^B| select a line & copy to edit line\n", '|', '_'); mydoupdate(); (void)wait_for_keypress(FALSE); delete_window(win); delete_window(bwin); mydoupdate(); } void scrollback_and_select_help(void) { const int help_win_width = 77, help_win_height = 17; NEWWIN *bwin = NULL, *win = NULL; create_win_border(help_win_width, help_win_height, "Scrollback + select help | Press any key to exit this help", &bwin, &win, FALSE); werase(win -> win); escape_print(win, "\n", '|', '_'); escape_print(win, " |right cursor key| selection line\n", '|', '_'); escape_print(win, " |left cursor key| abort selection\n", '|', '_'); escape_print(win, " |w| write to file\n", '|', '_'); escape_print(win, " |/| search\n", '|', '_'); escape_print(win, " |n| search next\n", '|', '_'); mydoupdate(); (void)wait_for_keypress(FALSE); delete_window(win); delete_window(bwin); mydoupdate(); } void user_channel_menu_help(void) { const int help_win_width = 77, help_win_height = 17; NEWWIN *bwin = NULL, *win = NULL; create_win_border(help_win_width, help_win_height, "User menu | Press any key to exit this help", &bwin, &win, FALSE); werase(win -> win); escape_print(win, "\n", '|', '_'); escape_print(win, " Output of commands like \"version\" etc can be seen\n", '|', '_'); escape_print(win, " in the server channel.\n", '|', '_'); mydoupdate(); (void)wait_for_keypress(FALSE); delete_window(win); delete_window(bwin); mydoupdate(); } fi-1.36/checkmail.h0000644000175000017500000000017612302365027014034 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 671 $ * (C) 2006-2014 by folkert@vanheusden.com */ void init_check_mail(void); fi-1.36/faq.txt0000644000175000017500000000225112302365027013247 0ustar folkertfolkertQ: it crashes if I do this or that A: please please please send an e-mail to mail@vanheusden.com describing what you did when it crashed. I will do my best to fix it. Q: function keys (F1, F2, etc.) don't work in putty from within 'screen' A: go to change settings -> terminal -> keyboard and set function keys to 'xterm R6' Q: f-irc uses only +/- 15 colors! A: start it with TERM=xterm-256color e.g.: TERM=xterm-256color f-irc Q: if I request a list of occurences of a word in the word-cloud, I get far more results than what the number behind the word says A: the count behind the word is the number of occurences in the time-interval in which this word cloud was determined. so older lines are not counted but do appear in the search. Q: when just started, f-irc responds sluggish A: at start, f-irc asks all joined channels for a list of users. processing of this data takes some time. Q: pressing the ESC key wreaks havoc A: the data produced by the ESC key is part of what is returned by other "special" keys (like F1-F12). As it is only part of it, ncurses (yes, not f-irc) expects more data to come in and waits for that. That's why pressing ESC makes f-irc look like it hang. fi-1.36/channels.h0000644000175000017500000000475512302365027013716 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 863 $ * (C) 2006-2014 by folkert@vanheusden.com */ #ifndef __CHANNELS_H__ #define __CHANNELS_H__ #include "utf8.h" #include "term.h" #include "buffer.h" extern int channel_offset, channel_cursor; typedef struct { const char *server; const char *channel; } favorite; typedef enum { MODE_NONE = 0, MODE_OPERATOR = 1, MODE_CAN_SPEAK = 2 } irc_user_mode_t; typedef struct { char *nick, *complete_name, *user_host; irc_user_mode_t mode; BOOL ignored; } person_t; typedef enum { NONE=1, META=2, MISC=3, YOU=4 } new_entry_t; typedef struct { char *channel_name; char *topic; char *keeptopic; BOOL topic_changed; /* buffer with received messages */ buffer *pbuffer; /* messages/commands entered by user */ buffer *input_buffer; utf8_string *input; int input_buffer_cursor; BOOL input_buffer_changed; BOOL recvd_non_notice; new_entry_t new_entry; person_t *persons; int n_names; BOOL adding_names; /* set to FALSE when a 366 is received. * a (new) 353 will then first free the * current list */ time_t last_view; time_t last_entry; double t_event; int n_event; } channel; typedef struct { int *server_index; BOOL *is_server_channel; /* when set, server_host is shown instead of channelname */ int *channel_index; BOOL *is_1on1_channel; int n_channels; } visible_channels; #define N_CU 10 typedef struct { channel *data; const char *s_name; } cu_t; extern cu_t undo_channels[N_CU]; extern BOOL vc_list_data_only; void free_channel(channel *pc); int add_channel(int server_index, const char *channel_name); void close_channel(int server_index, int channel_index, BOOL leave_channel); void show_channel_from_list(NEWWIN *win, int vc_index, int y); void show_channel_list(NEWWIN *win); int find_channel_index(int cur_server, const char *channel_name); void find_server_channel_index(const char *server_name, const char *channel_name, int *s_i, int *c_i); void set_new_line_received(); BOOL change_channel(int server_index, int channel_index, BOOL reset_cursor, BOOL push_history); void show_channel_names_list(void); void go_to_last_channel(void); void do_channels_keypress(int c); BOOL redo_channel(void); void channelwindow_mouse(mmask_t buttons, int x, int y); channel *gch(int sr, int ch); void create_visible_channels_list(void); void free_visible_channels_list(void); int find_vc_list_entry(int server_index, int channel_index); int find_vc_list_entry_by_name(const char *name, int search_offset, BOOL match_server_channel); #endif fi-1.36/nickcolor.c0000644000175000017500000000375612302365027014101 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 671 $ * (C) 2006-2014 by folkert@vanheusden.com */ #include #include #include #include #include "gen.h" #include "nickcolor.h" #include "term.h" int *nick_pairs = NULL, n_nick_pairs = 0; hash_types hash_function = DJB2; int hash_nick(const char *nick) { int len = strlen(nick), loop = 0; int hash = 0; if (hash_function == DJB2) { /* DJB2 (http://stackoverflow.com/questions/14409466/simple-hash-functions): */ for(loop=0; loop> 16); /* FvH */ hash &= 0xffff; return hash; } void find_nick_colorpair(const char *nick, nick_color_settings *pncs) { if (nick) { int hash = hash_nick(nick); pncs -> bold = hash & 1; pncs -> pair = nick_pairs[(hash >> 1) % n_nick_pairs]; } else { pncs -> bold = FALSE; pncs -> pair = default_colorpair; } } void init_nick_colorpairs(void) { int loop = 0; short fg = 0, bg = 0; short fg_r = 0, fg_g = 0, fg_b = 0; short bg_r = 0, bg_g = 0, bg_b = 0; nick_pairs = (int *)calloc(1, COLOR_PAIRS * sizeof(int)); n_nick_pairs = 0; /* they're 0 zo hardcoded */ pair_content(0, &fg, &bg); color_content(fg, &fg_r, &fg_g, &fg_b); color_content(bg, &bg_r, &bg_g, &bg_b); fg_r = 255; fg_b = 255; fg_g = 255; bg_r = 0; bg_g = 0; bg_b = 0; for(loop=0; loop Distributed under the terms of the GNU General Public License. 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 2 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, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA On Debian GNU/Linux systems, the complete text of the GNU General Public License can be found in the /usr/share/common-licenses/GPL file. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. fi-1.36/irc.c0000644000175000017500000006057312302365027012673 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 863 $ * (C) 2006-2014 by folkert@vanheusden.com */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "gen.h" #include "error.h" #include "theme.h" #include "term.h" #include "buffer.h" #include "channels.h" #include "servers.h" #include "utils.h" #include "tcp.h" #include "loop.h" #include "irc.h" #include "names.h" #include "user.h" #include "key_value.h" #include "wordcloud.h" #include "grep_filter.h" #include "config.h" #include "headlines.h" BOOL notified_grep_filter_problem = FALSE; BOOL is_channel(const char *name) { return name[0] == '#' || name[0] == '&'; } int do_send(int fd, const char *format, ...) { /* not to way to do it but I can't find a way to "predict" how large * a string will be */ char *str_buffer = NULL; int len = 0, rc = -1; va_list ap; va_start(ap, format); len = vasprintf(&str_buffer, format, ap); va_end(ap); str_buffer = realloc(str_buffer, len + 3); str_buffer[len + 0] = '\r'; str_buffer[len + 1] = '\n'; str_buffer[len + 2] = 0x00; LOG("OUT %d: %s\n", fd, str_buffer); len += 2; /* CR/LF */ rc = WRITE(fd, str_buffer, len); if (rc != len) { LOG("send: %d, rc: %d\n", len, rc); myfree(str_buffer); return -1; } myfree(str_buffer); return 0; } int irc_nick(int fd, const char *nick) { if (do_send(fd, "NICK %s", nick)) return -1; return 0; } int irc_login1(server *pserver) { /* password required? */ if (pserver -> password && do_send(pserver -> fd, "PASS %s", pserver -> password) == -1) return -1; /* send nick */ if (irc_nick(pserver -> fd, pserver -> nickname) == -1) return -1; return 0; } int irc_login2(server *pserver) { /* logon */ if (do_send(pserver -> fd, "USER %s %s %s :%s", pserver -> username, "-", pserver -> server_host, pserver -> user_complete_name) == -1) return -1; return 0; } int create_channel(int server_index, const char *channel) { /* find channel */ int channel_index = find_channel_index(server_index, channel); if (channel_index == -1) channel_index = add_channel(server_index, channel); return channel_index; } void headline_channel_msg(server *ps, channel *pc, const char *msg) { char *headline = NULL; asprintf(&headline, "%s (%s): %s", pc -> channel_name, ps -> description ? ps -> description : ps -> server_host, msg); add_headline(FALSE, headline); free(headline); } void log_to_user_window(int sr, const char *nick, const char *what) { int ch = -1; if (create_channel_for_meta_requests) ch = create_channel(sr, nick); else ch = find_channel_index(sr, nick); if (ch != -1) log_channel(sr, ch, nick, what, FALSE); } int process_server_do_line(int sr, const char *string) { int rc = 0; time_t now = time(NULL); server *ps = &server_list[sr]; const char *prefix = NULL; const char *cmd = NULL; int cmd_nr = -1; const char *pars = NULL; char *pars_before_colon = NULL; const char *pars_after_colon = NULL; string_array_t before_parts; const char *parse_offset = string; init_string_array(&before_parts); /* prefix */ if (parse_offset[0] == ':') { char *space = NULL; prefix = strdup(parse_offset + 1); space = strchr(prefix, ' '); if (!space) { LOG("Line consists of only a prefix?\n"); return -1; } *space = 0x00; if (strlen(prefix) == 0) { myfree(prefix); prefix = NULL; } parse_offset = strchr(parse_offset, ' '); while(parse_offset && isspace(*parse_offset)) parse_offset++; } /* command */ if (parse_offset && strlen(parse_offset)) { cmd = strdup(parse_offset); cmd_nr = atoi(cmd); terminate_str((char *)cmd, ' '); parse_offset = strchr(parse_offset, ' '); while(parse_offset && isspace(*parse_offset)) parse_offset++; } /* get parameter(s) */ if (parse_offset && strlen(parse_offset)) { if (parse_offset[0] == ':') { pars_after_colon = strdup(parse_offset + 1); } else { char *colon_search_before = NULL; pars_before_colon = strdup(parse_offset); colon_search_before = strstr(pars_before_colon, " :"); if (colon_search_before) { const char *colon_search_after = strstr(parse_offset, " :"); *colon_search_before = 0x00; if (colon_search_after) pars_after_colon = strdup(colon_search_after + 2); } split_string(pars_before_colon, " ", TRUE, &before_parts); /* also keep track of nick used in reply-messages: it may be different * from what we think is our nick in case you use e.g. znc */ if (cmd_nr > 1 && string_array_get_n(&before_parts) >= 1) server_set_additional_nick(sr, string_array_get(&before_parts, 0)); } } LOG("%s %d> %s|%s|%s :%s\n", ps -> description ? ps -> description : ps -> server_host, ps -> fd, str_or_nothing(prefix), str_or_nothing(cmd), str_or_nothing(pars_before_colon), str_or_nothing(pars_after_colon)); /* do commands */ if (!cmd) { LOG("line without command from irc server\n"); } else if (strcmp(cmd, "PING") == 0) /* PING/PONG */ { if (do_send(ps -> fd, "PONG %s", pars_after_colon) == -1) rc = -1; } else if (strcmp(cmd, "TOPIC") == 0) { if (string_array_get_n(&before_parts) && pars_after_colon) { const char *topic = pars_after_colon; int channel_index = create_channel(sr, string_array_get(&before_parts, 0)); channel *pc = &ps -> pchannels[channel_index]; myfree(pc -> topic); pc -> topic = strdup(topic); if (pc -> keeptopic != NULL && strstr(topic, pc -> keeptopic) == NULL) { if (irc_topic(ps -> fd, pc -> channel_name, pc -> keeptopic) == -1) rc = -1; } pc -> topic_changed = TRUE; headline_channel_msg(ps, pc, topic); update_statusline(sr, channel_index, "topic changed on channel %s by %s to %s", pc -> channel_name, prefix, topic); } } else if (strcmp(cmd, "KICK") == 0) { if (string_array_get_n(&before_parts) == 2) { const char *channel_name = string_array_get(&before_parts, 0); int channel_index = create_channel(sr, channel_name); const char *victim = string_array_get(&before_parts, 1); const char *kicker = prefix ? prefix : "?"; const char *reason = pars_after_colon ? pars_after_colon : "?"; if (channel_index != -1) { char redraw = 0; if (sr == current_server && current_server_channel_nr == channel_index) redraw = 1; /* me? */ if (strcasecmp(victim, ps -> nickname) == 0) { update_statusline(sr, 0, "you were kicked from channel '%s' by %s: %s\n", victim, kicker, reason); cmd_LEAVE(sr, channel_index, channel_name); if (auto_rejoin) irc_join(ps -> fd, channel_name); else popup_notify(FALSE, "You were kicked from channel %s by\n%s, reason:\n%s", channel_name, kicker, reason); } else { delete_from_channel_by_nick(sr, channel_index, victim); update_statusline(sr, channel_index, "user '%s' kicked from channel '%s' by %s\n", victim, channel_name, kicker); } if (redraw) { show_channel_names_list(); } } else if (channel_index == -1) { update_statusline(sr, 0, "user '%s' kicked from unknown channel '%s'\n", victim, channel_name); } } if (prefix) update_user_host(sr, prefix); } else if (strcmp(cmd, "PART") == 0 && prefix) /* PART/QUIT (leave channel) */ { int idx = 0, n = string_array_get_n(&before_parts); char *nick = strdup(prefix); for(idx=0; idx n_channels; channel_index++) { if (has_nick(sr, channel_index, nick)) update_statusline(sr, channel_index, "user %s quits (%s)", nick, str_or_nothing(pars_after_colon)); } } delete_by_nick(sr, nick); free(nick); } else if (strcmp(cmd, "MODE") == 0 && string_array_get_n(&before_parts) >= 2) /* MODE */ { const char *channel_str = string_array_get(&before_parts, 0); int channel_index = create_channel(sr, channel_str); const char *mode = string_array_get(&before_parts, 1); const char *flag_pars = string_array_get_n(&before_parts) >= 3 ? string_array_get(&before_parts, 2) : NULL; if (is_channel(channel_str) && flag_pars != NULL) { char *nick = strdup(flag_pars); irc_user_mode_t u_mode = MODE_NONE; terminate_str(nick, ' '); if (mode[1] == 'o') u_mode = MODE_OPERATOR; else if (mode[1] == '+') /* FIXME */ u_mode = MODE_CAN_SPEAK; if (u_mode == MODE_OPERATOR) { channel *pc = &ps -> pchannels[channel_index]; char *headline = NULL; if (mode[0] == '+') asprintf(&headline, "%s became OP", nick); else asprintf(&headline, "%s is no longer OP", nick); headline_channel_msg(ps, pc, headline); free(headline); } if (mode[0] == '+') set_nick_mode(sr, channel_index, nick, text_to_nick_mode(nick) | u_mode); else if (mode[0] == '-') set_nick_mode(sr, channel_index, nick, text_to_nick_mode(nick) & (~u_mode)); else LOG("!! unknown mode thing '%s' in channel '%s'\n", mode, channel_str); show_channel_names_list(); if (show_mode_changes) update_statusline(sr, channel_index, "new mode %s by %s for %s in channel %s", mode, prefix, nick, channel_str); free(nick); } else if (show_mode_changes) { update_statusline(sr, channel_index, "new mode %s for %s", str_or_nothing(mode), str_or_nothing(channel_str)); } if (prefix) update_user_host(sr, prefix); } else if (strcmp(cmd, "NICK") == 0 && (pars_after_colon != NULL || pars_before_colon != NULL) && prefix) /* NICK */ { int channel_index = 0; const char *new_nick = (pars_after_colon && strlen(pars_after_colon)) ? pars_after_colon : pars_before_colon; char *old_nick = strdup(prefix); terminate_str(old_nick, '!'); for(channel_index = 0; channel_index < ps -> n_channels; channel_index++) { channel *cur_channel = &ps -> pchannels[channel_index]; /* change nick in each channel */ if (show_nick_change) { if (has_nick(sr, channel_index, old_nick)) update_statusline(sr, channel_index, "%s is now known as %s", old_nick, new_nick); } /* change channelnames */ if (strcasecmp(cur_channel -> channel_name, old_nick) == 0) { myfree(cur_channel -> channel_name); cur_channel -> channel_name = strdup(new_nick); } } replace_nick(sr, old_nick, new_nick); free(old_nick); } else if (strcmp(cmd, "JOIN") == 0) /* JOIN */ { int idx = 0; char *channels_str_before = pars_before_colon ? strdup(pars_before_colon) : NULL; char *channels_str_after = pars_after_colon ? strdup(pars_after_colon) : NULL; char *nick = strdup(prefix); BOOL me = strcasecmp(ps -> nickname, nick) == 0; string_array_t channels; init_string_array(&channels); terminate_str(nick, '!'); if (channels_str_before && strlen(channels_str_before)) { terminate_str(channels_str_before, ' '); split_string(channels_str_before, ",", TRUE, &channels); } if (channels_str_after && strlen(channels_str_after)) { terminate_str(channels_str_after, ' '); split_string(channels_str_after, ",", TRUE, &channels); } for(idx=0; idx fd, cur_channel) == -1) rc = -1; } show_channel_names_list(); free_string_array(&channels); free(channels_str_before); free(channels_str_after); if (keep_channels_sorted) sort_channels(sr); } else if (strcmp(cmd, "INVITE") == 0) /* INVITE */ { const char *channel_name = pars_after_colon; if (!channel_name) update_statusline(sr, 0, "Received garbled INVITE message (channel missing)"); else if (!allow_invite) update_statusline(sr, 0, "Ignoring invite for channel %s", channel_name); else rc = irc_join(ps -> fd, channel_name); } else if ((strcmp(cmd, "PRIVMSG") == 0 || strcmp(cmd, "NOTICE") == 0) && pars_before_colon) /* PRIVMSG / NOTICE */ { int idx = 0; const char *text = pars_after_colon; const char *sender = prefix ? prefix : "?"; char *channel_list = strdup(pars_before_colon); string_array_t targets; terminate_str(channel_list, ' '); init_string_array(&targets); split_string(channel_list, ",", TRUE, &targets); if (ps -> ts_last_msg) { ps -> t_event += now - ps -> ts_last_msg; ps -> n_event++; if (ps -> n_event > 100) { ps -> t_event /= (double)ps -> n_event; ps -> t_event *= 5; ps -> n_event = 5; } } ps -> ts_last_msg = now; if (text) add_to_wc(text); for(idx=0; idx nickname, channel) == 0 || (ps -> nickname2 && strcasecmp(ps -> nickname2, channel) == 0)) { free(channel); channel = strdup(sender); terminate_str_r(channel, '!'); } if (text) { BOOL dummy = FALSE, match = FALSE; if (process_grep_filter(gp, ps -> description, channel, text, &err, &dummy) == FALSE && notified_grep_filter_problem == FALSE) { popup_notify(FALSE, "%s", err); free(err); notified_grep_filter_problem = TRUE; } if (process_grep_filter(hlgp, ps -> description, channel, text, &err, &match) && show_headlines && match) { char *line = NULL; char *sender_short = strdup(sender); terminate_str(sender_short, '!'); asprintf(&line, "%s@%s (%s): %s", sender_short, channel, ps -> description, text); add_headline(TRUE, line); free(line); free(sender_short); } } channel_index = create_channel(sr, channel); myfree(channel); } if (text && log_channel(sr, channel_index, sender, text, FALSE) == -1) rc = -1; if (strcmp(cmd, "PRIVMSG") == 0) ps -> pchannels[channel_index].recvd_non_notice = TRUE; } free_string_array(&targets); free(channel_list); if (prefix) update_user_host(sr, prefix); } else if (cmd_nr == 001) { update_statusline(sr, 0, "Logged in to %s:%d (%s)", ps -> server_host, (int)ps -> server_port, str_or_nothing(ps -> description)); set_state(sr, STATE_RUNNING); } else if (cmd_nr == 317 && pars_before_colon) /* idle time and such */ { time_t signon = 0; /* flok Uhmmm 111 1110956321 :seconds idle, signon time */ char *space = strchr(pars_before_colon, ' '); if (space) { char *nick = NULL; while(*space == ' ') space++; /* space now points behind 'flok' */ nick = space; space = strchr(space, ' '); if (space) { int idle = 0; *space = 0x00; space++; while(*space == ' ') space++; /* space now points behind 'uhmmm' */ idle = atoi(space); space = strchr(space, ' '); if (space) { int lc_rc = -1; char *str_buffer = NULL; while(*space == ' ') space++; /* space now points behind 111 */ signon = (time_t)atol(space); asprintf(&str_buffer, "%s is %d seconds idle and on-line since %s", nick, idle, ctime(&signon)); lc_rc = log_channel(sr, 0, NULL, str_buffer, TRUE); myfree(str_buffer); if (lc_rc == -1) rc = -1; } } } } else if (cmd_nr == 332) /* set TOPIC */ { const char *channel_str = string_array_get(&before_parts, 1); const char *topic = pars_after_colon; int channel_index = create_channel(sr, channel_str); channel *pc = &ps -> pchannels[channel_index]; myfree(pc -> topic); pc -> topic = topic ? strdup(topic) : NULL; headline_channel_msg(ps, pc, topic); pc -> topic_changed = TRUE; } else if (cmd_nr == 409) /* ":No origin specified": PING/PONG fails! */ { update_statusline(sr, 0, "internal error while replying to PING"); rc = -1; } else if (cmd_nr >= 431 && cmd_nr <= 436)/* problem setting NICK */ { if (ps -> state != STATE_RUNNING) { char *msg = NULL; const char *nick = gen_random_nick(); rc = irc_nick(ps -> fd, nick); asprintf(&msg, "Your default nick was refused, using a temporary random one: %s", nick); update_statusline(sr, 0, msg); myfree(nick); myfree(msg); } else { const char msg [] = "Your new nick was refused"; update_statusline(sr, 0, msg); popup_notify(FALSE, msg); } } else if ((cmd_nr == 353 || cmd_nr == 366) && pars_before_colon) /* channels nick list */ { char *channel_start = strchr(pars_before_colon, '#'); if (channel_start == NULL) channel_start = strchr(pars_before_colon, '&'); if (channel_start && pars_after_colon) { int channel_index = 0; const char *pnames = pars_after_colon; channel *pc = NULL; terminate_str(channel_start, ' '); /* find channel index */ channel_index = create_channel(sr, channel_start); pc = &ps -> pchannels[channel_index]; if (cmd_nr == 353) { if (pc -> adding_names == FALSE) { free_names_list(pc); pc -> adding_names = TRUE; } for(; strlen(pnames) > 0;) { char *space = strchr(pnames, ' '); if (space) *space = 0x00; add_nick(sr, channel_index, pnames, NULL, NULL); if (!space) break; pnames = space + 1; } } else { pc -> adding_names = FALSE; } show_channel_names_list(); } } else if (cmd_nr >= 251 && cmd_nr <= 259) /* stats */ update_statusline(sr, 0, "%s %03d: %s :%s", str_or_nothing(prefix), cmd_nr, str_or_nothing(pars_before_colon), str_or_nothing(pars_after_colon)); else if (cmd_nr == 311) /* reply to whois */ { /* 311 RPL_WHOISUSER " * :" :irc.xs4all.nl 311 mynick hisnick hisuser hishost * :hisname */ if (string_array_get_n(&before_parts) >= 2 && pars_after_colon != NULL) { char *temp = NULL; const char *nick = string_array_get(&before_parts, 1); const char *full_name = pars_after_colon; int index = 0; for(index=0; index n_channels; index++) change_name(sr, index, nick, full_name); if (string_array_get_n(&before_parts) >= 4) { char *user_host = NULL; asprintf(&user_host, "%s@%s", string_array_get(&before_parts, 3), string_array_get(&before_parts, 4)); update_user_host(sr, user_host); free(user_host); } asprintf(&temp, "%s is %s", nick, full_name); update_statusline(sr, 0, "%s\n", temp); if (string_array_get_n(&before_parts) >= 2) log_to_user_window(sr, string_array_get(&before_parts, 1), temp); free(temp); } } else if ((cmd_nr >= 312 && cmd_nr <= 319) || cmd_nr == 330) /* more WHOIS info */ { if (string_array_get_n(&before_parts) >= 2) { char *temp = NULL; asprintf(&temp, "%s %s", str_or_nothing(pars_before_colon), str_or_nothing(pars_after_colon)); log_to_user_window(sr, string_array_get(&before_parts, 1), temp); free(temp); } } else if (cmd_nr == 321) /* LIST start */ { update_statusline(sr, 0, "Receiving list of channels/topics..."); free_channel_list(sr); } else if (cmd_nr == 322 && string_array_get_n(&before_parts) >= 2) /* LIST part */ { const char *channel_str = string_array_get(&before_parts, 1); const char *topic = str_or_nothing(pars_after_colon); ps -> channel_list = (channel_topic_t *)realloc(ps -> channel_list, sizeof(channel_topic_t) * (ps -> channel_list_n + 1)); ps -> channel_list[ps -> channel_list_n].channel = strdup(channel_str); ps -> channel_list[ps -> channel_list_n].topic = strdup(topic); ps -> channel_list_n++; } else if (cmd_nr == 323) /* LIST end */ { ps -> channel_list_complete = TRUE; qsort(ps -> channel_list, ps -> channel_list_n, sizeof(channel_topic_t), compare_channel_list_item); update_statusline(sr, 0, "List of channels/topics successfully received"); } else if (cmd_nr == 352) /* reply to 'WHO' */ { const char *channel_str = string_array_get(&before_parts, 1); const char *nick = string_array_get(&before_parts, 5); int channel_index = create_channel(sr, channel_str); const char *user = string_array_get(&before_parts, 2); const char *host = string_array_get(&before_parts, 3); char *user_host = NULL; /* irc.0x20.nl|352|flox3 #eenzaam folkert7 2001:888:13b3:64:349c:ff08:8d88:114f irc.0x20.nl flok H :0 Folkert van Heusden */ asprintf(&user_host, "%s@%s", user, host); if (!has_nick(sr, channel_index, nick)) add_nick(sr, channel_index, nick, NULL, user_host); else change_user_host(sr, channel_index, nick, user_host); free(user_host); } else if (cmd_nr == 372 || cmd_nr == 375 || cmd_nr == 376) /* MOTD */ update_statusline(sr, 0, "%s %03d: %s :%s", str_or_nothing(prefix), cmd_nr, str_or_nothing(pars_before_colon), str_or_nothing(pars_after_colon)); else if (cmd_nr == 391) /* TIME */ { double took = get_ts() - ps -> sent_time_req_ts; if (!ps -> hide_time_req && pars_after_colon && (ps -> prev_cmd == NULL || strcmp(ps -> prev_cmd, cmd) != 0)) log_channel(sr, 0, NULL, pars_after_colon, TRUE); ps -> sent_time_req_ts = 0; ps -> hide_time_req = FALSE; if (ps -> server_latency <= 0.0) ps -> server_latency = took; else ps -> server_latency = (took * 2 + ps -> server_latency) / 3; } else if (!ignore_unknown_irc_protocol_msgs) { update_statusline(sr, 0, "unknown command from irc-server: ':%s %s %s :%s'", str_or_nothing(prefix), str_or_nothing(cmd), str_or_nothing(pars_before_colon), str_or_nothing(pars_after_colon)); } if (strcmp(cmd, "PRIVMSG") != 0 && strcmp(cmd, "NOTICE") != 0 && strcmp(cmd, "PING") != 0) { free(server_list[sr].prev_cmd); ps -> prev_cmd = strdup(cmd); } free_string_array(&before_parts); myfree(pars_after_colon); myfree(pars_before_colon); myfree(pars); myfree(cmd); myfree(prefix); return rc; } int irc_privmsg(int fd, const char *channel, const char *msg) { return do_send(fd, "PRIVMSG %s :%s", channel, msg); } int irc_kick(int fd, const char *channel, const char *nick, const char *comment) { if (comment) return do_send(fd, "KICK %s %s :%s", channel, nick, comment); else return do_send(fd, "KICK %s %s", channel, nick); } int irc_ban(int fd, const char *channel, const char *nick) { return do_send(fd, "MODE %s +b %s", channel, nick); } int irc_list(int fd) { return do_send(fd, "LIST"); } int irc_op(int fd, const char *channel, const char *nick, BOOL op) { if (op) return do_send(fd, "MODE %s +o %s", channel, nick); else return do_send(fd, "MODE %s -o %s", channel, nick); } int irc_whois(int fd, const char *nick) { return do_send(fd, "WHOIS %s", nick); } int irc_allowspeak(int fd, const char *channel, const char *nick, BOOL speak) { if (speak) return do_send(fd, "MODE %s +v %s", channel, nick); else return do_send(fd, "MODE %s -v %s", channel, nick); } int irc_v3_cap_start(int fd) { return do_send(fd, "CAP REQ"); } int irc_v3_cap_end(int fd) { return do_send(fd, "CAP END"); } int irc_who(int fd, const char *channel) { return do_send(fd, "WHO %s", channel); } int irc_topic(int fd, const char *channel, const char *topic) { return do_send(fd, "TOPIC %s :%s", channel, topic); } int irc_ping(int fd, const char *nick, int ts) { char str_buffer[IRC_MAX_MSG_LEN] = { 0 }; snprintf(str_buffer, sizeof str_buffer, "\001PING %d\001", ts); return irc_privmsg(fd, nick, str_buffer); } int irc_version(int fd) { return do_send(fd, "VERSION"); } int irc_time(int fd) { return do_send(fd, "TIME"); } int irc_join(int fd, const char *channel) { return do_send(fd, "JOIN %s", channel); } int irc_part(int fd, const char *channel, const char *msg) { if (msg) return do_send(fd, "PART %s :%s", channel, msg); return do_send(fd, "PART %s", channel); } int irc_quit(int fd, const char *msg) { if (msg) return do_send(fd, "QUIT :%s", msg); return do_send(fd, "QUIT"); } fi-1.36/servers.h0000644000175000017500000000505512302365027013606 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 863 $ * (C) 2006-2014 by folkert@vanheusden.com */ #ifndef __SERVER_H__ #define __SERVER_H__ #include #include "tcp.h" #include "string_array.h" #include "lf_buffer.h" #include "channels.h" #define DEFAULT_RECONNECT_DELAY 44 #define DEFAULT_MAX_RECONNECT_DELAY 512 typedef enum { STATE_NO_CONNECTION = 0, STATE_ERROR, STATE_TCP_CONNECT, STATE_IRC_CONNECTING, STATE_CONNECTED1, STATE_CONNECTED2, STATE_LOGGING_IN, STATE_RUNNING, STATE_DISCONNECTED } conn_state_t; typedef struct { const char *channel, *topic; } channel_topic_t; int compare_channel_list_item(const void *a, const void *b); typedef struct { const char *server_host; char *server_real; int server_port; const char *description; resolve_info ri; int reconnect_delay; channel_topic_t *channel_list; int channel_list_n; BOOL channel_list_complete; string_array_t send_after_login; char must_send_after_login; string_array_t auto_join; char *user_complete_name; char *username; char *password; char *nickname, *nickname2; int fd; int ifd; lf_buffer_t io_buffer; char *user_ping; double t_user_ping; int user_ping_id; conn_state_t state; time_t state_since; double sent_time_req_ts, server_latency; BOOL hide_time_req; /* channel[0] = server channel */ channel *pchannels; int n_channels; /* wether the complete list of channels is shown or not */ BOOL minimized; time_t ts_last_action; time_t ts_last_msg; time_t last_view; double t_event; int n_event; int prev_bps; time_t ts_bytes; long int bytes; char *prev_cmd; } server; void free_server(int server_index); void toggle_server_minimized(int toggle_index); int find_server_index(const char *server_name); int add_server(const char *host_and_port, const char *username, const char *password, const char *nickname, const char *complete_name, const char *description); void close_server(int server_index, BOOL leave_channels); void create_default_server(void); void set_state(int server_index, conn_state_t state); conn_state_t get_state(int server_index); int get_server_color(int server_index); const char *gen_random_nick(); void restart_server(int sr); int register_server_events(struct pollfd **pfd, int *n_fd); void process_server_events(struct pollfd *pfd, int n_fd); int find_in_autojoin(int sr, const char *channel_name); void remove_autojoin(int sr, int aj_nr); void add_autojoin(int server_index, char *channel_name); void server_set_additional_nick(int sr, const char *n2); void free_channel_list(int sr); void sort_channels(int sr); server *gsr(int sr); #endif fi-1.36/firc.conf0000644000175000017500000000102512302365027013527 0ustar folkertfolkertignore_file=firc.ignore highlight=on notice_in_serverchannel=true partial_highlight_match=true ; server=irc.vanheusden.com:6667 description=vhc nick=notset username=notset name=notset auto_join=#f-irc ; example for how to authenticate against nickserv send_after_login=PRIVMSG NickServ :IDENTIFY my_password ; auto_private_channel=1 ; dcc_path=/home/folkert/dcc_in/ ; max_channel_record_lines=1000 ; word_cloud_win_height=5 word_cloud_refresh=300 ; following parameter can be for example 64 word_cloud_n=0 word_cloud_min_word_size=4 fi-1.36/buffer.c0000644000175000017500000001265012302365027013360 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 798 $ * (C) 2006-2014 by folkert@vanheusden.com */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "gen.h" #include "term.h" #include "buffer.h" #include "channels.h" #include "servers.h" #include "loop.h" #include "utils.h" #include "main.h" void truncate_buffer(buffer *pbuffer) { int loop; for(loop=0; loop n_records; loop++) { myfree((void *)pbuffer -> records[loop].msg); myfree((void *)pbuffer -> records[loop].msg_from); } myfree(pbuffer -> records); pbuffer -> records = NULL; pbuffer -> n_records = 0; } void free_buffer(buffer *pbuffer) { truncate_buffer(pbuffer); free(pbuffer); } buffer * create_buffer(const int max_channel_record_lines) { buffer *pbuffer = malloc(sizeof(buffer)); assert(max_channel_record_lines >= 0); memset(pbuffer, 0x00, sizeof(buffer)); pbuffer -> max_n_elements = max_channel_record_lines; return pbuffer; } int get_buffer_n_elements(const buffer *pb) { assert(pb -> n_records >= 0); return pb -> n_records; } buffer_element_t *get_from_buffer(const buffer *pb, int pos) { assert(pos < pb -> n_records); assert(pos >= 0); return &pb -> records[pos]; } BOOL is_markerline(buffer *pb, int pos) { assert(pos < pb -> n_records); assert(pos >= 0); return pb -> records[pos].line_type == BET_MARKERLINE; } BOOL latest_is_markerline(buffer *pb) { if (pb -> n_records == 0) return FALSE; return pb -> records[pb -> n_records - 1].line_type == BET_MARKERLINE; } void add_to_buffer(buffer *pbuffer, const char *what, const char *what_from, const BOOL is_meta, const int sr, const int ch) { be_type_t type = BET_REGULAR; if (is_meta) type = BET_META; if (!what) { what = ""; type = BET_MARKERLINE; } if (pbuffer -> n_records < pbuffer -> max_n_elements || pbuffer -> max_n_elements == 0) { pbuffer -> records = (buffer_element_t *)realloc(pbuffer -> records, sizeof(buffer_element_t) * (pbuffer -> n_records + 1)); pbuffer -> records[pbuffer -> n_records].msg = strdup(what); pbuffer -> records[pbuffer -> n_records].msg_from = what_from ? strdup(what_from) : NULL; pbuffer -> records[pbuffer -> n_records].line_type = type; pbuffer -> records[pbuffer -> n_records].when = time(NULL); pbuffer -> records[pbuffer -> n_records].sr = sr; pbuffer -> records[pbuffer -> n_records].ch = ch; pbuffer -> n_records++; } else { myfree(pbuffer -> records[0].msg); myfree(pbuffer -> records[0].msg_from); memmove(&pbuffer -> records[0], &pbuffer -> records[1], (pbuffer -> n_records - 1) * sizeof(buffer_element_t)); pbuffer -> records[pbuffer -> n_records - 1].msg = strdup(what); pbuffer -> records[pbuffer -> n_records - 1].msg_from = what_from ? strdup(what_from) : NULL; pbuffer -> records[pbuffer -> n_records - 1].line_type = type; pbuffer -> records[pbuffer -> n_records - 1].when = time(NULL); pbuffer -> records[pbuffer -> n_records - 1].sr = sr; pbuffer -> records[pbuffer -> n_records - 1].ch = ch; } if (pbuffer -> last_shown > 0) pbuffer -> last_shown--; } void search_in_buffer(const buffer *in, buffer *result, const char *search_what, BOOL fuzzy) { int index; for(index=0; index n_records; index++) { const char *msg = in -> records[index].msg; if (msg && strcasestr(msg, search_what)) { buffer_element_t *pbe = &in -> records[index]; add_to_buffer(result, msg, pbe -> msg_from, pbe -> line_type, pbe -> sr, pbe -> ch); } } } buffer * search_in_buffer_new(const buffer *in, const char *search_what, BOOL fuzzy) { buffer *result = create_buffer(1000); search_in_buffer(in, result, search_what, fuzzy); return result; } int buffer_sorter_asc(const void *el1_in, const void *el2_in) { buffer_element_t *el1 = (buffer_element_t *)el1_in; buffer_element_t *el2 = (buffer_element_t *)el2_in; return el1 -> when - el2 -> when; } int buffer_sorter_desc(const void *el1_in, const void *el2_in) { buffer_element_t *el1 = (buffer_element_t *)el1_in; buffer_element_t *el2 = (buffer_element_t *)el2_in; return el2 -> when - el1 -> when; } void sort_buffer(buffer *work, BOOL direction) { if (direction) qsort(work -> records, work -> n_records, sizeof(buffer_element_t), buffer_sorter_asc); else qsort(work -> records, work -> n_records, sizeof(buffer_element_t), buffer_sorter_desc); } void add_buffer_to_buffer(buffer *target, const buffer *source) { int index=0; for(index=0; index n_records; index++) { buffer_element_t *pbe = &source -> records[index]; add_to_buffer(target, pbe -> msg, pbe -> msg_from, pbe -> line_type == BET_META, pbe -> sr, pbe -> ch); } } void delete_type(buffer *pb, be_type_t type) { int index=0; for(index=0; index n_records;) { if (pb -> records[index].line_type == type) { int n_to_move = pb -> n_records - (index + 1); myfree(pb -> records[index].msg); myfree(pb -> records[index].msg_from); if (n_to_move > 0) memmove(&pb -> records[index], &pb -> records[index + 1], n_to_move * sizeof(buffer_element_t)); pb -> n_records--; } else { index++; } } } int search_in_buffer_index(const buffer *in, const char *what, int search_offset) { int index = 0; for(index=0; index n_records; index++) { int offset = (search_offset + index) % in -> n_records; if (strcasestr(in -> records[offset].msg, what)) return offset; } return -1; } fi-1.36/ctcp.h0000644000175000017500000000027212302365027013042 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 721 $ * (C) 2006-2014 by folkert@vanheusden.com */ char *exec_and_strip_ctcp(int sr, int ch, const char *nick, const char *in, BOOL *me); fi-1.36/soundex.c0000644000175000017500000000466712302365027013605 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 671 $ * (C) 2006-2014 by folkert@vanheusden.com */ #include #include #include #include "gen.h" #include "utils.h" #include "string_array.h" /* Returns the soundex equivalent to in * adapted from http://physics.nist.gov/cuu/Reference/soundex.html */ char *soundex(const char *in) { int index_in = 1, index_out = 1; char prev_dig, *out = (char *)malloc(5), cur_char; memset(out, '0', 4); out[0] = tolower(in[0]); prev_dig = out[0]; while(in[index_in] && index_out < 4) { switch(tolower(in[index_in++])) { case 'b' : cur_char = '1'; break; case 'p' : cur_char = '1'; break; case 'f' : cur_char = '1'; break; case 'v' : cur_char = '1'; break; case 'c' : cur_char = '2'; break; case 's' : cur_char = '2'; break; case 'k' : cur_char = '2'; break; case 'g' : cur_char = '2'; break; case 'j' : cur_char = '2'; break; case 'q' : cur_char = '2'; break; case 'x' : cur_char = '2'; break; case 'z' : cur_char = '2'; break; case 'd' : cur_char = '3'; break; case 't' : cur_char = '3'; break; case 'l' : cur_char = '4'; break; case 'm' : cur_char = '5'; break; case 'n' : cur_char = '5'; break; case 'r' : cur_char = '6'; break; default : cur_char = '*'; } if (cur_char != prev_dig && cur_char != '*') out[index_out++] = prev_dig = cur_char; } out[4] = 0x00; return out; } BOOL fuzzy_match(const char *haystackIn, const char *needle, char *bitmap) { const char *needleS = soundex(needle); char *haystack = strdup(haystackIn), *search_start = haystack; int loop = 0, len = strlen(haystackIn); BOOL match = FALSE; string_array_t hsS; init_string_array(&hsS); for(loop=0; loop= 0 && pos <= len - word_len) memset(&bitmap[pos], '1', word_len); if (!bitmap) { myfree(cur); break; } search_start = found_at + word_len; } myfree(cur); } free_splitted_string(&hsS); myfree(needleS); free(haystack); return match; } fi-1.36/utf8.h0000644000175000017500000000341112302365027012775 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 709 $ * (C) 2006-2014 by folkert@vanheusden.com */ #ifndef __UTF8__ #define __UTF8__ #include typedef struct { char ascii; char utf8[4]; } utf8_byte; typedef struct { utf8_byte *string; int len; BOOL working; char work_buffer[4]; int work_buffer_in; } utf8_string; utf8_string *alloc_utf8_string(); utf8_string *utf8_strdup(const utf8_string *in); void free_utf8_string(const utf8_string *in); void truncate_utf8_string(utf8_string *work); BOOL add_stream_to_utf8_string(utf8_string *out, const char cur_byte); void add_utf8_to_utf8_string(utf8_string *out, const utf8_byte in); void utf8_strcat_ascii(utf8_string *work, const char *in); void utf8_strcat_utf8_string(utf8_string *work, const utf8_string *in); int utf8_strlen(const utf8_string *in); char *utf8_get_ascii(const utf8_string *in); char *utf8_get_ascii_pos(const utf8_string *in, const int pos); char *utf8_get_utf8(const utf8_string *in); char *utf8_get_utf8_pos(const utf8_string *in, const int pos); wchar_t *utf8_get_wchar(const utf8_string *in); wchar_t *utf8_get_wchar_pos(const utf8_string *in, const int pos); void utf8_del_pos(utf8_string *in, const int pos); void utf8_insert_pos_ascii(utf8_string *work, const int pos, const int c); void utf8_insert_pos_utf8(utf8_string *work, const int pos, const utf8_byte in); int utf8_find_nonblank_reverse(const utf8_string *work, const int start); int utf8_find_nonblank(const utf8_string *work, const int start); int utf8_find_blank_reverse(const utf8_string *work, const int start); int utf8_ascii_get_at(const utf8_string *in, const int pos); void utf8_ascii_set_at(utf8_string *work, const int pos, const char what); void utf8_truncate(utf8_string *work, const int new_size); int count_utf_bytes(int c); #endif fi-1.36/main.c0000644000175000017500000006415612302365027013043 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 861 $ * (C) 2006-2014 by folkert@vanheusden.com */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #ifndef AIX #include /* needed on Solaris 8 */ #endif #include #include #include #include #include "gen.h" #include "error.h" #include "theme.h" #include "term.h" #include "buffer.h" #include "channels.h" #include "utils.h" #include "servers.h" #include "loop.h" #include "user.h" #include "config.h" #include "utf8.h" #include "key_value.h" #include "grep_filter.h" #include "wordcloud.h" #include "dcc.h" #include "nickcolor.h" #include "chistory.h" #include "autocomplete.h" #include "checkmail.h" #include "names.h" #include "ignores.h" #include "colors.h" #include "dictionary.h" #include "headlines.h" #include "help.h" #include "scrollback.h" char *jump_channel = NULL; /* editline */ unsigned int ul_str_pos = 0, ul_x = 0; BOOL editline_redraw = FALSE; /* channel window */ cursor_mode_t cursor_mode = CM_CHANNELS; time_t cursor_mode_since = 0; /* redraw terminal when resizing */ volatile BOOL terminal_changed = FALSE; string_array_t extra_highlights; utf8_string *input_line_undo = NULL; time_t started_at = 0; void set_cursor_mode(cursor_mode_t cm) { cursor_mode = cm; cursor_mode_since = time(NULL); } cursor_mode_t get_cursor_mode(void) { return cursor_mode; } time_t get_cursor_mode_since(void) { return cursor_mode_since; } server *cur_server(void) { return &server_list[current_server]; } channel *cur_channel(void) { return &server_list[current_server].pchannels[current_server_channel_nr]; } /* move to the end of an editline */ void reposition_editline_cursor(void) { char *dummy = utf8_get_ascii(cur_channel() -> input); if (current_server >= 0 && current_server_channel_nr >= 0 && n_servers > 0) { int str_len = utf8_strlen(cur_channel() -> input); ul_x = str_len % input_window_width; ul_str_pos = str_len - ul_x; /* have the line redrawn */ editline_redraw = TRUE; } else { ul_x = ul_str_pos = 0; } free(dummy); } void inputline_mouse(mmask_t buttons, int x, int y) { if ((buttons & BUTTON1_CLICKED) || (buttons & BUTTON1_DOUBLE_CLICKED)) { if (get_cursor_mode() == CM_EDIT) { int len = utf8_strlen(cur_channel() -> input); if (x + ul_str_pos < len) ul_x = x; else reposition_editline_cursor(); } else { set_cursor_mode(CM_EDIT); } } if (buttons & BUTTON3_CLICKED) { select_own_history_line(FALSE); reposition_editline_cursor(); } } void update_input_buffer() { if (cur_channel() -> input_buffer_changed) { const char *ul_asc = utf8_get_utf8(cur_channel() -> input); add_to_buffer(cur_channel() -> input_buffer, ul_asc, cur_server() -> nickname, FALSE, current_server, current_server_channel_nr); myfree((void *)ul_asc); } utf8_truncate(cur_channel() -> input, 0); if (cur_channel() -> input_buffer_cursor < get_buffer_n_elements(cur_channel() -> input_buffer)) utf8_strcat_ascii(cur_channel() -> input, get_from_buffer(cur_channel() -> input_buffer, cur_channel() -> input_buffer_cursor) -> msg); cur_channel() -> input_buffer_changed = FALSE; reposition_editline_cursor(); } void edit_line_keypress(c) { if (c == KEY_LEFT) { if (ul_x > 0) { ul_x--; } else if (ul_str_pos > 0) { ul_str_pos--; } else { wrong_key(); } } else if (c == KEY_RIGHT) { int len = utf8_strlen(cur_channel() -> input); if (ul_x + ul_str_pos < len) { if (ul_x < input_window_width - 1) ul_x++; else ul_str_pos++; } else { wrong_key(); } } else if (c == KEY_UP) { if (cur_channel() -> input_buffer_cursor) { cur_channel() -> input_buffer_cursor--; update_input_buffer(); editline_redraw = TRUE; } else { wrong_key(); } } else if (c == KEY_HOME) { if (cur_channel() -> input_buffer_cursor) { cur_channel() -> input_buffer_cursor = 0; update_input_buffer(); editline_redraw = TRUE; } else { wrong_key(); } } else if (c == KEY_DOWN) { if (cur_channel() -> input_buffer_cursor < get_buffer_n_elements(cur_channel() -> input_buffer)) { cur_channel() -> input_buffer_cursor++; update_input_buffer(); } else { wrong_key(); } editline_redraw = TRUE; } else if (c == KEY_END) { if (cur_channel() -> input_buffer_cursor != get_buffer_n_elements(cur_channel() -> input_buffer) - 1) { cur_channel() -> input_buffer_cursor = get_buffer_n_elements(cur_channel() -> input_buffer) - 1; update_input_buffer(); editline_redraw = TRUE; } else { wrong_key(); } } } void jump_next_favorite(void) { if (n_favorite_channels) { int attempts = 0; for(attempts=0; attempts server, pf -> channel, &s_i, &c_i); if (s_i != -1 && c_i != -1 && (s_i != current_server || c_i != current_server_channel_nr)) { change_channel(s_i, c_i, TRUE, TRUE); break; } if (c_i == -1) { LOG("channel %s %s not found\n", pf -> server, pf -> channel); wrong_key(); } favorite_channels_index = (favorite_channels_index + 1) % n_favorite_channels; } } else { LOG("no favorites defined\n"); wrong_key(); } } void redraw() { determine_terminal_size(); if (ERR == resizeterm(max_y, max_x)) error_exit(TRUE, "problem resizing terminal\n"); wresize(stdscr, max_y, max_x); endwin(); refresh(); wclear(stdscr); create_windows(); refresh_window_with_buffer(chat_window, max_y - 1, cur_channel() -> pbuffer, cur_server() -> nickname, FALSE); change_channel(current_server, current_server_channel_nr, TRUE, FALSE); create_visible_channels_list(); update_headline(TRUE); } void redraw_chat_window_only() { werase(chat_window -> win); refresh_window_with_buffer(chat_window, max_y - 1, cur_channel() -> pbuffer, cur_server() -> nickname, FALSE); } void reset_new_data(void) { int s; for(s=0; s n_channels; loop++) { int check_index = (prev_index + loop) % vc_list -> n_channels; int check_server = vc_list -> server_index[check_index]; int check_channel = vc_list -> channel_index[check_index]; new_entry_t ne = server_list[check_server].pchannels[check_channel].new_entry; if (ne == NONE/* || ne == META*/) continue; if (me_only == TRUE && ne != YOU) continue; found = check_index; break; } } else { int loop; for(loop=1; loop n_channels; loop++) { int check_index = prev_index - loop; int check_server = -1, check_channel = -1; new_entry_t ne = NONE; while(check_index < 0) check_index += vc_list -> n_channels; check_server = vc_list -> server_index[check_index]; check_channel = vc_list -> channel_index[check_index]; ne = server_list[check_server].pchannels[check_channel].new_entry; if (ne == NONE /*|| ne == META*/) continue; if (me_only == TRUE && ne != YOU) continue; found = check_index; break; } } if (found == -1) wrong_key(); else { int check_server = vc_list -> server_index[found]; int check_channel = vc_list -> channel_index[found]; set_cursor_mode(CM_CHANNELS); if (jumpy_navigation) change_channel(check_server, check_channel, TRUE, TRUE); else { int diff = found - prev_index; channel_cursor += diff; if (channel_cursor < 0) { channel_offset += channel_cursor; channel_cursor = 0; } else if (channel_cursor >= channel_window -> nlines) { channel_offset += channel_cursor - (channel_window -> nlines - 1); channel_cursor = channel_window -> nlines - 1; } change_channel(check_server, check_channel, FALSE, TRUE); } } } void add_char(int c) { unsigned int len = utf8_strlen(cur_channel() -> input); if (len >= LINE_LENGTH) { wrong_key(); } else { int cur_combined_pos = ul_str_pos + ul_x; BOOL added = TRUE; if (cur_combined_pos > len) error_exit(FALSE, "position too large %d[%d], cur: %d[%d], %d", cur_combined_pos, len, current_server, n_servers, current_server_channel_nr); /* cursor at end of cur_channel() -> input? */ if (cur_combined_pos == len) added = add_stream_to_utf8_string(cur_channel() -> input, c); else /* add character to somewhere IN the cur_channel() -> input */ utf8_insert_pos_ascii(cur_channel() -> input, cur_combined_pos, c); cur_channel() -> input_buffer_changed = TRUE; editline_redraw = TRUE; if (!added) /* UTF8 stream */ { } else if (cur_combined_pos < LINE_LENGTH) { if (ul_x < input_window_width - 1) ul_x++; else ul_str_pos++; } else { wrong_key(); } } } void add_escape_char() { int c = -1; NEWWIN *bwin = NULL, *win = NULL; create_win_border(21, 3, "^V", &bwin, &win, FALSE); mvwprintw(win -> win, 1, 1, "Press key to escape"); mydoupdate(); c = wait_for_keypress(FALSE); if (c == 21) /* ^U -> ^_ */ c = 31; delete_window(win); delete_window(bwin); if (c != 0x00) add_char(c); else wrong_key(); } void draw_editline() { werase(input_window -> win); if (utf8_strlen(cur_channel() -> input) > 0) { wchar_t *part = utf8_get_wchar_pos(cur_channel() -> input, ul_str_pos); int len = min(wcslen(part), input_window_width); int index = 0; for(index=0; index win, A_REVERSE); /* FIXME handle > 26 */ mvwaddch(input_window -> win, 0, index, temp[0] + 'A' - 1); mywattroff(input_window -> win, A_REVERSE); } else { mvwaddwstr(input_window -> win, 0, index, temp); } } myfree(part); } } void exit_fi(void) { char *cnf_save_msg = NULL; int loop = 0; if (input_line_undo) free_utf8_string(input_line_undo); if (store_config_on_exit) { char *err_msg = NULL; if (save_config(TRUE, &err_msg) == FALSE) asprintf(&cnf_save_msg, "\n\nProblem: %s\n\n\n", err_msg); else asprintf(&cnf_save_msg, "\n\nUpdated configuration file (%s)\n\n\n", err_msg); free(err_msg); } for(loop=0; loop= n_servers || current_server_channel_nr >= cur_server() -> n_channels || current_server_channel_nr < 0) error_exit(FALSE, "p: %d %d\n", current_server, current_server_channel_nr); if (c == 8 || c == KEY_F(1)) /* F1: Help */ { main_help(); } else if (c == KEY_MOUSE) { MEVENT event; int x = 0, y = 0; if (getmouse(&event) == OK) { x = event.x; y = event.y; if ((chat_window_border && is_in_window(chat_window_border, event.x, event.y)) || (chat_window && is_in_window(chat_window, event.x, event.y))) { show_channel_history(); } else if (input_window && is_in_window(input_window, event.x, event.y)) { wmouse_trafo(input_window -> win, &y, &x, FALSE); inputline_mouse(event.bstate, x, y); } else if (input_window_border && is_in_window(input_window_border, event.x, event.y)) set_cursor_mode(CM_EDIT); else if (channel_window && is_in_window(channel_window, event.x, event.y)) { wmouse_trafo(channel_window -> win, &y, &x, FALSE); channelwindow_mouse(event.bstate, x, y); } else if (channel_window_border && is_in_window(channel_window_border, event.x, event.y)) set_cursor_mode(CM_CHANNELS); else if (wc_window && is_in_window(wc_window, event.x, event.y)) { wmouse_trafo(wc_window -> win, &y, &x, FALSE); wordcloud_mouse(event.bstate, x, y); } else if (wc_window_border && is_in_window(wc_window_border, event.x, event.y)) set_cursor_mode(CM_WC); else { wrong_key(); } editline_redraw = TRUE; force_channel_win_redraw = TRUE; } } else if (c == KEY_F(2)) /* F2: save config */ { char *err_msg = NULL; if (!save_ignore_list()) popup_notify(FALSE, "Problem saving ignore-list file"); (void)save_config(TRUE, &err_msg); popup_notify(FALSE, "%s", err_msg); free(err_msg); } else if (c == KEY_F(4)) /* F4 */ { set_cursor_mode(CM_EDIT); } else if (c == -1 || c == KEY_F(5)) /* F5: terminal resize / redraw */ { redraw(); terminal_changed = FALSE; editline_redraw = TRUE; } else if (c == KEY_F(6)) /* F6: search in all channels */ { search_everywhere(); } else if (c == KEY_F(7)) /* F7: close notice channels */ { close_notice_channels(); } else if (c == KEY_F(8)) /* F8: edit configuration */ { if (configure_firc()) redraw(); } else if (c == KEY_F(9)) /* F9: undo channel close */ { if (!redo_channel()) wrong_key(); else force_channel_win_redraw = TRUE; } else if (c == 14 || c == KEY_F(10)) /* ^N / F10 */ { if (get_cursor_mode() == CM_CHANNELS) set_cursor_mode(CM_EDIT); else if (get_cursor_mode() == CM_NAMES) set_cursor_mode(CM_EDIT); else if (get_cursor_mode() == CM_EDIT && word_cloud_n > 0) set_cursor_mode(CM_WC); else set_cursor_mode(CM_CHANNELS); if (get_cursor_mode() == CM_EDIT) cur_channel() -> input_buffer_cursor = get_buffer_n_elements(cur_channel() -> input_buffer); force_channel_win_redraw = TRUE; } else if (c == KEY_F(12)) add_markerline_to_all(); else if (c == KEY_RESIZE) /* handled by signal */ { } else if (c == 23) /* ^W search next channel with data */ find_next_channel_with_data(FALSE, TRUE); else if (c == 7) /* ^G: close channel */ { yna_reply_t rc = ABORT; int nr = find_in_autojoin(current_server, server_list[current_server].pchannels[current_server_channel_nr].channel_name); if (nr != -1) { rc = yesno_box(FALSE, "Close channel", "Remove from auto-join list as well?", TRUE); if (rc == YES) remove_autojoin(current_server, nr); } else { rc = YES; } if (rc != ABORT) cmd_LEAVE(current_server, current_server_channel_nr, NULL); reposition_editline_cursor(); } else if (c == 3) /* ^C */ { if (yesno_box(FALSE, "Terminate f-irc", "Are you sure you want to terminate the program?", FALSE) == YES) break; } else if (c == 20) /* ^T */ { ch_t prev = pop_channel_history(); if (prev.s != -1 && prev.c != -1) change_channel(prev.s, prev.c, TRUE, FALSE); else wrong_key(); } else if (c == 17) /* ^Q */ jump_next_favorite(); else if (is_cursor_key && get_cursor_mode() == CM_WC) do_word_cloud_keypress(c); else if (is_cursor_key && get_cursor_mode() == CM_NAMES) { do_names_keypress(c); force_channel_win_redraw = TRUE; } else if (is_cursor_key && get_cursor_mode() == CM_CHANNELS) { do_channels_keypress(c); force_channel_win_redraw = TRUE; } else if (is_cursor_key && get_cursor_mode() == CM_EDIT) { edit_line_keypress(c); } else if (c == 4) /* ^D */ { int str_offset = ul_str_pos + ul_x; if (utf8_strlen(cur_channel() -> input) - str_offset > 0) { utf8_del_pos(cur_channel() -> input, str_offset); cur_channel() -> input_buffer_changed = TRUE; } else { wrong_key(); } editline_redraw = TRUE; } else if (c == KEY_BACKSPACE || c == 127) /* BACKSPACE/DEL */ { int str_offset = ul_str_pos + ul_x; if (str_offset > 0) { utf8_del_pos(cur_channel() -> input, str_offset - 1); if (ul_x > 0) ul_x--; else ul_str_pos--; editline_redraw = TRUE; cur_channel() -> input_buffer_changed = TRUE; } else { wrong_key(); } } else if (c == 9 && utf8_strlen(cur_channel() -> input) > 0) /* TAB */ { unsigned int x = ul_str_pos + ul_x; /* find first non-space before the cursor */ x = utf8_find_nonblank_reverse(cur_channel() -> input, x); if (x != -1) { int space_offset = -1, word_offset = -1; char *temp_str = NULL; const char *dummy = NULL; /* then find the last space (if any) before the cursor */ space_offset = utf8_find_blank_reverse(cur_channel() -> input, x); if (space_offset == -1) word_offset = 0; else word_offset = space_offset + 1; temp_str = utf8_get_ascii_pos(cur_channel() -> input, word_offset); if (temp_str[0] == '/') dummy = make_complete_command(temp_str); else { dummy = make_complete_nickorchannel(temp_str, word_offset == 0); if (!dummy && dictionary_file) { dummy = lookup_dictionary(temp_str); if (dummy) dummy = strdup(dummy); } } if (dummy) { utf8_truncate(cur_channel() -> input, word_offset); utf8_strcat_ascii(cur_channel() -> input, dummy); reposition_editline_cursor(); editline_redraw = TRUE; } else { wrong_key(); } myfree(dummy); myfree(temp_str); } } else if (c == 18) /* ^R search next channel with data backwares */ find_next_channel_with_data(FALSE, FALSE); else if (c == 26) /* ^Z personal msg */ find_next_channel_with_data(TRUE, TRUE); else if (c == 24) /* ^X personal msg, backwards */ find_next_channel_with_data(TRUE, FALSE); else if (c == 16) /* ^P */ { add_markerline(current_server, current_server_channel_nr); if (only_one_markerline) redraw_chat_window_only(); } else if (c == 10) /* ^J */ { if (jump_channel == NULL) wrong_key(); else { int vc_list_offset = channel_cursor + channel_offset; int vc_nr = find_vc_list_entry_by_name(jump_channel, vc_list_offset, FALSE); if (vc_nr == -1) vc_nr = find_vc_list_entry_by_name(jump_channel, vc_list_offset, TRUE); if (vc_nr == -1) wrong_key(); else { int s = vc_list -> server_index[vc_nr]; int c = vc_list -> channel_index[vc_nr]; change_channel(s, c, TRUE, TRUE); } } } else if (c == 13) /* RETURN */ { if (utf8_strlen(cur_channel() -> input) > 0) { BOOL do_line = TRUE, do_commands = TRUE; if (utf8_ascii_get_at(cur_channel() -> input, 0) == '@' && utf8_strlen(cur_channel() -> input) >= 2) { if (utf8_ascii_get_at(cur_channel() -> input, 1) == '/' || utf8_ascii_get_at(cur_channel() -> input, 1) == '@') { /* strlen without -1 to include the terminating 0x00 */ utf8_del_pos(cur_channel() -> input, 0); do_commands = FALSE; } else { const char *c_part = utf8_get_ascii_pos(cur_channel() -> input, 1); int vc_nr = find_vc_list_entry_by_name(c_part, channel_cursor + channel_offset, FALSE); if (vc_nr == -1) vc_nr = find_vc_list_entry_by_name(c_part, channel_cursor + channel_offset, TRUE); utf8_truncate(cur_channel() -> input, 0); ul_x = ul_str_pos = 0; do_line = FALSE; if (vc_nr == -1) wrong_key(); else { int s = vc_list -> server_index[vc_nr]; int c = vc_list -> channel_index[vc_nr]; myfree(jump_channel); jump_channel = strdup(c_part); change_channel(s, c, TRUE, TRUE); } myfree((void *)c_part); } } if (do_line) { const char *ul_asc = utf8_get_utf8(cur_channel() -> input); if (user_command(current_server, current_server_channel_nr, ul_asc, do_commands) == -1) { cur_server() -> state = STATE_DISCONNECTED; close(cur_server() -> fd); update_statusline(current_server, current_server_channel_nr, "Connection to %s:%d closed (6)", cur_server() -> server_host, cur_server() -> server_port); } else { add_to_buffer(cur_channel() -> input_buffer, ul_asc, cur_server() -> nickname, FALSE, current_server, current_server_channel_nr); } utf8_truncate(cur_channel() -> input, 0); ul_x = ul_str_pos = 0; myfree((void *)ul_asc); } editline_redraw = TRUE; werase(input_window -> win); } else { wrong_key(); } if (get_cursor_mode() != CM_CHANNELS) { set_cursor_mode(CM_CHANNELS); force_channel_win_redraw = TRUE; } } else if (c == 1 || c == KEY_HOME) /* ^A / HOME */ ul_str_pos = ul_x = 0; else if (c == 5 || c == KEY_END) /* ^E / END */ reposition_editline_cursor(); else if (c == 21) /* ^U */ { if (utf8_strlen(cur_channel() -> input) == 0) /* redo */ { if (!input_line_undo) wrong_key(); else { utf8_strcat_utf8_string(cur_channel() -> input, input_line_undo); free_utf8_string(input_line_undo); input_line_undo = NULL; reposition_editline_cursor(); } } else { if (input_line_undo != NULL) free_utf8_string(input_line_undo); input_line_undo = utf8_strdup(cur_channel() -> input); utf8_truncate(cur_channel() -> input, 0); ul_x = ul_str_pos = 0; } editline_redraw = TRUE; } else if (c == 15) /* ^O reset new data */ { reset_new_data(); force_channel_win_redraw = TRUE; } else if (c == 2 || c == 19) /* ^B scrollback inputlines, ^S in all */ { select_own_history_line(c == 19); reposition_editline_cursor(); editline_redraw = TRUE; } else if (c == 6) /* ^F scrollback channel */ { show_channel_history(); /* because of ^B functionality: */ reposition_editline_cursor(); editline_redraw = TRUE; } else if (c == 22) /* ^V escape char */ add_escape_char(); else if (c == 25) /* ^Y with-data only toggle */ { if (vc_list_data_only == FALSE) { if (yesno_box(FALSE, "Channels to show", "Show only channels with messages?", FALSE) == YES) vc_list_data_only = TRUE; } else { vc_list_data_only = FALSE; } show_channel_names_list(); } /* 255: because ncurses has values > 255 for certain keys */ else if (c >= 32 && c <= 255) { add_char(c); } else { LOG("invalid key: %d\n", c); } /* at this point the use may have changed channel/server so re-get the pointers */ /* redraw channel cursor (if moved) */ if (channel_cursor + channel_offset != prev_channel_pos || names_cursor + names_offset != prev_names_pos || force_channel_win_redraw) show_channel_names_list(); if (ul_str_pos != ul_prev_str_pos || editline_redraw) { draw_editline(); editline_redraw = FALSE; } mydoupdate(); } exit_fi(); return 0; } fi-1.36/checkmail.c0000644000175000017500000000137412302365027014030 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 671 $ * (C) 2006-2014 by folkert@vanheusden.com */ #include #include #include #include #include #include "gen.h" #include "config.h" char *mail_spool_file = NULL; struct stat msf_info; off_t msf_prev_size = 0; time_t msf_last_check = 0; void init_check_mail(void) { mail_spool_file = getenv("MAIL"); if (check_for_mail > 0 && mail_spool_file != NULL) { if (stat(mail_spool_file, &msf_info) == -1) check_for_mail = 0; else { msf_prev_size = msf_info.st_size; msf_last_check = time(NULL); } } } fi-1.36/ignores.h0000644000175000017500000000124512302365027013560 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 671 $ * (C) 2006-2014 by folkert@vanheusden.com */ #ifndef __IGNORES_H__ #define __IGNORES_H__ typedef struct { const char *nick, *name_complete; const char *channel; } ignore; extern ignore *ignores; extern char *ignore_file; extern int n_ignores; #define IGNORE_NOT_SET "NOT sEt bla invalid123" void add_ignore(const char *channel, const char *nick, const char *name_complete); void del_ignore(const char *channel, const char *nick); BOOL load_ignore_list(const char *file); void free_ignores(void); BOOL save_ignore_list(void); BOOL check_ignore(const char *channel, const char *nick, const char *name_complete); #endif fi-1.36/autocomplete.h0000644000175000017500000000040212302365027014605 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 671 $ * (C) 2006-2014 by folkert@vanheusden.com */ typedef struct { char *what; } tab_completion_t; char *make_complete_command(const char *in); char *make_complete_nickorchannel(char *in, int start_of_line); fi-1.36/firc.ignore0000644000175000017500000000002112302365027014060 0ustar folkertfolkert#bruenhild basze fi-1.36/string_array.c0000644000175000017500000000665212302365027014620 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 749 $ * (C) 2006-2014 by folkert@vanheusden.com */ #include #include #include "string_array.h" #include "utils.h" void init_string_array(string_array_t *p) { p -> data = NULL; p -> n = 0; } void free_string_array(string_array_t *p) { int i = 0; for(i=0; i < p -> n; i++) myfree(p -> data[i]); myfree(p -> data); p -> data = NULL; p -> n = 0; } void add_to_string_array(string_array_t *p, const char *what) { p -> data = (const char **)realloc(p -> data, (p -> n + 1) * sizeof(char *)); p -> data[p -> n] = strdup(what); p -> n++; } int find_str_in_string_array(const string_array_t *p, const char *what, BOOL ignore_case) { int i = 0; for(i=0; i

n; i++) { if (ignore_case && strcasecmp(p -> data[i], what) == 0) return i; if (!ignore_case && strcmp(p -> data[i], what) == 0) return i; } return -1; } void del_nr_from_string_array(string_array_t *p, int nr) { int n_to_move = (p -> n - nr) - 1; myfree(p -> data[nr]); if (n_to_move > 0) memmove(&p -> data[nr], &p -> data[nr + 1], n_to_move * sizeof(char *)); p -> n--; } void del_str_from_string_array(string_array_t *p, const char *what) { int idx = find_str_in_string_array(p, what, FALSE); if (idx != -1) del_nr_from_string_array(p, idx); } int string_array_get_n(const string_array_t *p) { return p -> n; } const char* string_array_get(const string_array_t *p, int idx) { return p -> data[idx]; } void insert_into_string_array(string_array_t *p, int idx, const char *what) { int n_to_move = p -> n - idx; p -> data = (const char **)realloc(p -> data, (p -> n + 1) * sizeof(char *)); if (n_to_move > 0) memmove(&p -> data[idx + 1], &p -> data[idx], sizeof(char *) * n_to_move); p -> data[idx] = strdup(what); p -> n++; } void replace_in_string_array(string_array_t *p, int idx, const char *what) { myfree(p -> data[idx]); p -> data[idx] = strdup(what); } void split_string(const char *in, const char *split, BOOL clean, string_array_t *parts) { char *copy = strdup(in), *pc = copy; int split_len = strlen(split); for(;;) { char *term = strstr(pc, split); if (term) *term = 0x00; add_to_string_array(parts, pc); if (!term) break; pc = term + split_len; if (clean) { while(strncmp(pc, split, split_len) == 0) pc += split_len; } } free(copy); } void free_splitted_string(string_array_t *parts) { free_string_array(parts); } static int cmp(const void *a, const void *b) { const char **ia = (const char **)a; const char **ib = (const char **)b; return strcmp(*ia, *ib); } void sort_string_array(string_array_t *p) { qsort(p -> data, p -> n, sizeof(char *), cmp); } int partial_match_search_string_array(const string_array_t *p, const char *what) { int imin = 0, imax = p -> n - 1, what_len = strlen(what); while(imax >= imin) { int imid = (imin + imax) / 2, cmp = 0; if (memcmp(p -> data[imid], what, what_len) == 0) return imid; cmp = strcasecmp(p -> data[imid], what); if (cmp < 0) imin = imid + 1; else /* if (cmp > 0) */ imax = imid - 1; } return -1; } BOOL dump_string_array(const string_array_t *p, const char *name, FILE *fh) { int index = 0; for(index=0; index

n; index++) { if (fprintf(fh, "%s=%s\n", name, p -> data[index]) == -1) return FALSE; } return TRUE; } fi-1.36/grep_filter.h0000644000175000017500000000146112302365027014414 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 709 $ * (C) 2006-2014 by folkert@vanheusden.com */ #ifndef __GREP_FILTER_H__ #define __GREP_FILTER_H__ #include #include typedef struct { const char *sserver, *schannel; const char *file; const char *re; regex_t filter; } grep_filter_t; typedef struct { grep_filter_t *gf_list; int n_gf_list; } grep_target; grep_target *alloc_grep_target(void); BOOL add_grep_filter(grep_target *gp, const char *re, const char *sserver, const char *schannel, const char *file, char **err); BOOL process_grep_filter(const grep_target *gp, const char *sserver, const char *schannel, const char *line, char **err, BOOL *match); void free_grep_filters(grep_target *gp); BOOL dump_grep_filters(const grep_target *gp, const char *target, FILE *fh); #endif fi-1.36/string_array.h0000644000175000017500000000232512302365027014616 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 749 $ * (C) 2006-2014 by folkert@vanheusden.com */ #ifndef __STRING_ARRAY_H__ #define __STRING_ARRAY_H__ #include #include "gen.h" typedef struct { const char **data; int n; } string_array_t; void init_string_array(string_array_t *p); void free_string_array(string_array_t *p); void add_to_string_array(string_array_t *p, const char *what); int find_str_in_string_array(const string_array_t *p, const char *what, BOOL ignore_case); void del_str_from_string_array(string_array_t *p, const char *what); void del_nr_from_string_array(string_array_t *p, int nr); int string_array_get_n(const string_array_t *p); const char* string_array_get(const string_array_t *p, int idx); void insert_into_string_array(string_array_t *p, int idx, const char *what); void replace_in_string_array(string_array_t *p, int idx, const char *what); void sort_string_array(string_array_t *p); int partial_match_search_string_array(const string_array_t *p, const char *what); void split_string(const char *in, const char *split, BOOL clean, string_array_t *parts); void free_splitted_string(string_array_t *prats); BOOL dump_string_array(const string_array_t *p, const char *name, FILE *fh); #endif fi-1.36/help.h0000644000175000017500000000052412302365027013041 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 861 $ * (C) 2006-2014 by folkert@vanheusden.com */ const char *get_tooltip(void); void main_help(void); void configure_firc_help(void); void edit_box_help(void); void commandline_help(void); void scrollback_help(void); void scrollback_and_select_help(void); void user_channel_menu_help(void); fi-1.36/user.c0000644000175000017500000015565612302365027013103 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 863 $ * (C) 2006-2014 by folkert@vanheusden.com */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "gen.h" #include "error.h" #include "theme.h" #include "term.h" #include "buffer.h" #include "channels.h" #include "servers.h" #include "utils.h" #include "irc.h" #include "loop.h" #include "main.h" #include "dcc.h" #include "names.h" #include "config.h" #include "user.h" #include "colors.h" #include "ignores.h" #include "dictionary.h" #include "headlines.h" #include "help.h" #include "scrollback.h" void keypress_visual_feedback(void) { NEWWIN *popup = create_window(5, 25); box(popup -> win, 0, 0); mywattron(popup -> win, A_BOLD); mvwprintw(popup -> win, 0, 2, "Executing command"); mywattroff(popup -> win, A_BOLD); escape_print_xy(popup, 2, 2, "Please wait..."); mydoupdate(); usleep(100000); delete_window(popup); mydoupdate(); } void popup_notify(BOOL use_getch, const char *format, ...) { va_list ap; char *msg = NULL; const char any_key[] = "Press any key..."; int width = -1, loop = 0; string_array_t lines; NEWWIN *popup = NULL; init_string_array(&lines); va_start(ap, format); vasprintf(&msg, format, ap); va_end(ap); split_string(msg, "\n", FALSE, &lines); for(loop=0; loop width) width = cur_width; } if (sizeof(any_key) > width) width = sizeof(any_key); popup = create_window(5 + string_array_get_n(&lines) - 1, width + 4); box(popup -> win, 0, 0); mywattron(popup -> win, A_BOLD); mvwprintw(popup -> win, 0, 2, any_key); mywattroff(popup -> win, A_BOLD); escape_print_xy(popup, 2, 2, msg); mydoupdate(); myfree(msg); if (use_getch) getch(); else (void)wait_for_keypress(FALSE); delete_window(popup); free_string_array(&lines); mydoupdate(); } BOOL onoff_box(const char *q, BOOL default_value) { NEWWIN *bwin = NULL, *win = NULL; const char *title = " Space to toggle, tab key select OK/CANCEL, enter exit "; int q_len = max(strlen(title), strlen(q)); BOOL cur_value = default_value; BOOL ret_ok = TRUE; create_win_border(q_len + 5, 5, title, &bwin, &win, FALSE); for(;;) { int c = -1; werase(win -> win); escape_print_xy(win, 1, 1, q); if (cur_value) escape_print_xy(win, 2, q_len / 2, "^ ON ^"); else escape_print_xy(win, 2, q_len / 2, " OFF"); if (ret_ok == TRUE) escape_print_xy(win, 3, 1, "^[ OK ]^ [ CANCEL ]"); else escape_print_xy(win, 3, 1, "[ OK ] ^[ CANCEL ]^"); mydoupdate(); c = wait_for_keypress(FALSE); if (c == ' ') cur_value = !cur_value; else if (c == 7) { ret_ok = false; cur_value = default_value; break; } else if (c == 9) ret_ok = !ret_ok; else if (c == 13) { if (!ret_ok) cur_value = default_value; break; } else if (c == 3) exit_fi(); else if (toupper(c) == 'q') break; else wrong_key(); } delete_window(win); delete_window(bwin); mydoupdate(); return cur_value; } void save_config_with_popup(void) { char *err_msg = NULL; (void)save_config(TRUE, &err_msg); popup_notify(FALSE, "%s", err_msg); free(err_msg); } const char *edit_box(int width, const char *title, const char *initial) { char *line = NULL; NEWWIN *bwin = NULL, *win = NULL; BOOL ret_ok = TRUE; int offset = 0, pos = 0; BOOL reposition = TRUE; char *undo_clear = NULL; width = min(max(width, 38), max_x); if (initial) line = strdup(initial); else { line = (char *)malloc(1); line[0] = 0x00; } create_win_border(width, 3, " TAB select OK/CANCEL, enter to exit ", &bwin, &win, TRUE); for(;;) { char *dummy = NULL; int c = -1, l = strlen(line); werase(win -> win); escape_print_xy(win, 0, 1, title); if (ret_ok == TRUE) escape_print_xy(win, 2, 1, "^[ OK ]^ [ CANCEL ]"); else escape_print_xy(win, 2, 1, "[ OK ] ^[ CANCEL ]^"); if (reposition) { if (l < width) pos = l; else pos = (width * 3) / 4; offset = l - pos; reposition = FALSE; } dummy = strdup(&line[offset]); if (strlen(dummy) >= width - 2) dummy[width - 2] = 0x00; mvwprintw(win -> win, 1, 1, "%s", dummy); free(dummy); override_cursor_win = win; override_cursor_x = pos + 1; override_cursor_y = 1; mydoupdate(); c = wait_for_keypress(FALSE); if (c == KEY_BACKSPACE || c == 127) /* backspace/del */ { if (pos > 0) pos--; else if (offset > 0) offset--; else wrong_key(); line[pos + offset] = 0x00; } else if (c == KEY_F(1)) /* F1 for help */ edit_box_help(); else if (c == 1) /* ^A */ pos = offset = 0; else if (c == 4) /* ^D */ { int x = pos + offset; if (x < l) memmove(&line[x], &line[x + 1], l - x); else wrong_key(); } else if (c == 5) /* ^E */ reposition = TRUE; else if (c == 7) { ret_ok = false; break; } else if (c == 9) ret_ok = !ret_ok; else if (c == 3) exit_fi(); else if (c == 13) /* enter */ break; else if (c == 21) /* ^U */ { if (l == 0) { free(line); line = undo_clear; undo_clear = NULL; } else { free(undo_clear); undo_clear = strdup(line); line[0] = 0x00; } reposition = TRUE; } else if (c == KEY_LEFT) { if (pos > 0) pos--; else if (offset > 0) offset--; else wrong_key(); } else if (c == KEY_RIGHT) { int l = strlen(line); if (pos + offset < l) { if (pos < width - 1) pos++; else offset++; } else { wrong_key(); } } else if (c == 23) /* ^W */ { int x = pos + offset; while(x > 0 && isalnum(line[x - 1])) { line[x - 1] = 0x00; x--; } } else if (c >= 32 && c < 127) { int l = strlen(line); int x = pos + offset; line = realloc(line, l + 1 + 1); if (x < l) memmove(&line[x + 1], &line[x], (l - x) + 1); line[x] = c; line[l + 1] = 0x00; if (pos < width - 1) pos++; else offset++; } } delete_window(win); delete_window(bwin); override_cursor_win = NULL; override_cursor_y = override_cursor_x = 0; mydoupdate(); if (!ret_ok) { myfree(line); line = NULL; } return line; } int ping_user(int sr, const char *nick) { popup_notify(FALSE, "Ping sent"); server_list[sr].user_ping = strdup(nick); server_list[sr].t_user_ping = get_ts(); server_list[sr].user_ping_id = rand(); if (irc_ping(server_list[sr].fd, nick, server_list[sr].user_ping_id) != 0) return -1; return log_channel(sr, 0, nick, "send ping", TRUE); } int dcc_send_user(int sr, int ch, const char *nick) { int rc = 0; const char *filename_in = edit_box(60, "^DCC send file^", NULL); if (filename_in) { char *log_msg = NULL; const char *filename = explode_path(filename_in); rc = init_send_dcc(filename, sr, ch, nick); asprintf(&log_msg, "DCC send %s", filename); if (rc == 0 && log_channel(sr, ch, nick, log_msg, TRUE) == -1) rc = -1; myfree(filename); myfree(log_msg); myfree(filename_in); } return rc; } int ctcp_user(int sr, const char *nick) { int rc = 0; const char *ctcp_command = edit_box(60, "^CTCP^", NULL); if (ctcp_command) { char *msg = NULL, *log_msg = NULL; asprintf(&msg, "\001%s\001", ctcp_command); rc = irc_privmsg(server_list[sr].fd, nick, msg); myfree(msg); asprintf(&log_msg, "CTCP: %s", ctcp_command); myfree(ctcp_command); if (rc == 0 && log_channel(sr, 0, nick, log_msg, TRUE) == -1) rc = -1; myfree(log_msg); } return rc; } void cmd_LEAVE(int server_nr, int server_channel_nr, const char *channel_name) { int ch = -1; /* see if there's a channel given */ if (channel_name && strlen(channel_name) > 0) { int loop; for(loop=0; loop= server_list[server_nr].n_channels) cur_channel = server_list[server_nr].n_channels - 1; if (cur_channel < 0) cur_channel = 0; change_channel(server_nr, cur_channel, TRUE, FALSE); dummy = find_vc_list_entry(server_nr, cur_channel); if (dummy == -1) dummy = 0; channel_cursor = dummy % channel_window -> nlines; channel_offset = dummy - channel_cursor; if (get_cursor_mode() == CM_NAMES) set_cursor_mode(CM_CHANNELS); } } show_channel_names_list(); } int user_command(int current_server, int current_server_channel_nr, const char *user_line, BOOL do_command) { int rc = 0; char me = strncasecmp(user_line, "/ME ", 4) == 0; if (user_line[0] == '/' && !me && do_command == TRUE) /* command for server? */ { char *command = strdup(user_line + 1); char *pars = strchr(command, ' '); char *parsc = NULL; /* find start of parameters */ if (pars) { *pars = 0x00; pars++; while(*pars == ' ') pars++; parsc = strdup(pars); } /* remove spaces at the end of the parameters */ if (pars) { int pars_len = strlen(pars); while(pars_len) { pars_len--; if (isspace(pars[pars_len])) pars[pars_len] = 0x00; else break; } if (strlen(pars) == 0) pars = NULL; } /* first determine if it is a command for the irc-client or for the server */ if (strcasecmp(command, "ADDSERVER") == 0) { if (!pars) popup_notify(FALSE, "/ADDSERVER: missing host(-name) to connect to"); else { int sr = add_server(pars, /* host[:port] */ server_list[current_server].username, /* username */ NULL, /* password */ server_list[current_server].nickname, /* nickname */ server_list[current_server].user_complete_name, /* complete name */ NULL /* description */ ); update_statusline(current_server, current_server_channel_nr, "Added server %s:%d", server_list[sr].server_host, server_list[sr].server_port); change_channel(sr, 0, TRUE, TRUE); } } else if (strcasecmp(command, "TIME") == 0) { server *ps = &server_list[current_server]; ps -> hide_time_req = FALSE; /* only set when not already set: a ping may already have been pending */ if (ps -> sent_time_req_ts < 1) { ps -> sent_time_req_ts = get_ts(); if (irc_time(ps -> fd) == -1) rc = -1; } } else if (strcasecmp(command, "PING") == 0) { char do_it = 1; if (server_list[current_server].user_ping) { if (get_ts() - server_list[current_server].t_user_ping > 10.0) { update_statusline(current_server, current_server_channel_nr, "PING to user %s timed out", server_list[current_server].user_ping); myfree(server_list[current_server].user_ping); server_list[current_server].user_ping = NULL; } else { update_statusline(current_server, current_server_channel_nr, "A PING to user %s is still in progress", server_list[current_server].user_ping); do_it = 0; } } if (!pars) popup_notify(FALSE, "/PING: you forgot to tell me what user to ping"); else if (do_it) { if (ping_user(current_server, pars) == -1) rc = -1; } } else if (strcasecmp(command, "IGNORE") == 0) { if (!pars) popup_notify(FALSE, "Ignore: nick name missing"); else { channel *pch = &server_list[current_server].pchannels[current_server_channel_nr]; BOOL prev_status = FALSE, ok = ignore_nick(current_server, current_server_channel_nr, pars, &prev_status); add_ignore(pch -> channel_name, pars, IGNORE_NOT_SET); if (!ok) popup_notify(FALSE, "IGNORE: user %s is not known", pars); else if (prev_status) popup_notify(FALSE, "IGNORE: already ignoring user %s!", pars); else { update_statusline(current_server, current_server_channel_nr, "ignoring user %s", pars); if (!save_ignore_list()) popup_notify(FALSE, "Problem saving ignore-list file"); } } } else if (strcasecmp(command, "UNIGNORE") == 0) { if (!pars) popup_notify(FALSE, "Unignore: nick name missing"); else { channel *pch = &server_list[current_server].pchannels[current_server_channel_nr]; BOOL prev_status = FALSE, ok = unignore_nick(current_server, current_server_channel_nr, pars, &prev_status); del_ignore(pch -> channel_name, pars); if (!ok) popup_notify(FALSE, "IGNORE: user %s is not known", pars); else if (!prev_status) popup_notify(FALSE, "IGNORE: was not ignoring user %s!", pars); else { update_statusline(current_server, current_server_channel_nr, "no longer ignoring user %s", pars); if (!save_ignore_list()) popup_notify(FALSE, "Problem saving ignore-list file"); } } } else if (strcasecmp(command, "DCCSEND") == 0) { char *end_of_nick = pars ? strchr(pars, ' ') : NULL; if (!end_of_nick) popup_notify(FALSE, "DCCSEND: either the nickname or the filename are missing"); else { *end_of_nick = 0x00; /* verify nick */ if (has_nick(current_server, current_server_channel_nr, pars) == FALSE) popup_notify(FALSE, "DCCSEND: nick '%s' is not known for this channel", pars); else { if (init_send_dcc(end_of_nick + 1, current_server, current_server_channel_nr, pars) != 0) popup_notify(FALSE, "DCCSEND: failed to start transmitting file"); else update_statusline(current_server, current_server_channel_nr, "DCCSEND: waiting for user '%s' to acknowledge file '%s'", pars, end_of_nick + 1); } } } else if (strcasecmp(command, "BAN") == 0) { if (!pars) { rc = do_send(server_list[current_server].fd, "MODE %s +b", server_list[current_server].pchannels[current_server_channel_nr].channel_name); } else { int loop = 0; server *ps = &server_list[current_server]; channel *pc = &ps -> pchannels[current_server_channel_nr]; person_t *p = NULL; for(loop=0; loop n_names; loop++) { if (strcasecmp((pc -> persons)[loop].nick, pars) == 0) { p = & (pc -> persons)[loop]; break; } } if (p) { if (irc_ban(ps -> fd, pc -> channel_name, p -> user_host) == -1) rc = -1; else update_statusline(current_server, current_server_channel_nr, "user %s (%s / %s) banned", pars, p -> complete_name, p -> user_host); } else { popup_notify(FALSE, "BAN: user %s is not known", pars); } } } else if (strcasecmp(command, "RAW") == 0 || strcasecmp(command, "QUOTE") == 0) { if (do_send(server_list[current_server].fd, "%s", pars) == -1) rc = -1; } else if (strcasecmp(command, "LEAVE") == 0 || strcasecmp(command, "PART") == 0) { cmd_LEAVE(current_server, current_server_channel_nr, pars); } else if (strcasecmp(command, "TOPIC") == 0) { channel *pc = &server_list[current_server].pchannels[current_server_channel_nr]; if (parsc == NULL || strlen(parsc) == 0) { char *topic = pc -> topic; update_statusline(current_server, current_server_channel_nr, "Topic for channel %s is: %s", pc -> channel_name, topic?topic: "not yet known."); } else if (irc_topic(server_list[current_server].fd, pc -> channel_name, parsc) == -1) { rc = -1; } } else if (strcasecmp(command, "KEEPTOPIC") == 0) { if (parsc == NULL || strlen(parsc) == 0) { myfree(server_list[current_server].pchannels[current_server_channel_nr].keeptopic); server_list[current_server].pchannels[current_server_channel_nr].keeptopic = NULL; update_statusline(current_server, current_server_channel_nr, "No longer maintaining topic on channel %s", server_list[current_server].pchannels[current_server_channel_nr].channel_name); } else { myfree(server_list[current_server].pchannels[current_server_channel_nr].keeptopic); server_list[current_server].pchannels[current_server_channel_nr].keeptopic = strdup(parsc); if (irc_topic(server_list[current_server].fd, server_list[current_server].pchannels[current_server_channel_nr].channel_name, parsc) == -1) { rc = -1; } } } else if (strcasecmp(command, "EXIT") == 0) /* leave program */ { exit_fi(); } else if (strcasecmp(command, "QUIT") == 0) /* leave server */ { int sr = -1; /* see if there's a channel given */ if (pars && strlen(pars) > 0) { int loop; /* find server into sr */ for(loop=0; loop pchannels[current_server_channel_nr]; int loop = 0; for(loop=0; loop n_names; loop++) { if (irc_privmsg(ps -> fd, pc -> persons[loop].nick, pars) == -1) rc = -1; } } else if (strcasecmp(command, "VERSIONSPAM") == 0) /* ask all CTCP version */ { int loop = 0; server *ps = &server_list[current_server]; channel *pc = &ps -> pchannels[current_server_channel_nr]; for(loop=0; loop n_names; loop++) { if (irc_privmsg(ps -> fd, pc -> persons[loop].nick, "\001VERSION\001") == -1) rc = -1; } } else if (strcasecmp(command, "MSG") == 0 || strcasecmp(command, "QUERY") == 0) /* private message */ { if (!pars) popup_notify(FALSE, "%s: missing nick/channel and message", command); else { char *end_of_nick = strchr(pars, ' '); if (!end_of_nick) popup_notify(FALSE, "%s: missing nick/channel or message", command); else { *end_of_nick = 0x00; if (auto_private_channel) { int channel_nr = create_channel(current_server, pars); if (channel_nr == -1) rc = -1; else if (log_channel(current_server, channel_nr, server_list[current_server].nickname, end_of_nick + 1, FALSE) == -1) rc = -1; } if (irc_privmsg(server_list[current_server].fd, pars, end_of_nick + 1) == -1) { rc = -1; } } } } else if (strcasecmp(command, "CTCP") == 0) /* CTCP message */ { char *end_of_nick = pars ? strchr(pars, ' ') : NULL; if (!end_of_nick) popup_notify(FALSE, "CTCP: either the nickname or the message is missing"); else { *end_of_nick = 0x00; /* verify nick */ if (has_nick(current_server, current_server_channel_nr, pars) == FALSE) popup_notify(FALSE, "CTCP: nick '%s' is not known for this channel", pars); else { char *msg = NULL; asprintf(&msg, "\001%s\001", end_of_nick + 1); if (irc_privmsg(server_list[current_server].fd, pars, msg) == -1) rc = -1; myfree(msg); } } } else if (strcasecmp(command, "SAVECONFIG") == 0) { save_config_with_popup(); } else if (strcasecmp(command, "SEARCHALL") == 0) global_search(pars, pars); else { if (do_send(server_list[current_server].fd, "%s", user_line + 1) == -1) rc = -1; } myfree(command); myfree(parsc); } else /* message to a certain channel */ { server *ps = &server_list[current_server]; char *line = NULL; if (me) asprintf(&line, "\001ACTION %s\001", &user_line[4]); else line = strdup(user_line); if (irc_privmsg(ps -> fd, ps -> pchannels[current_server_channel_nr].channel_name, line) == -1) rc = -1; if (log_channel(current_server, current_server_channel_nr, ps -> nickname, line, FALSE) == -1) rc = -1; myfree(line); } return rc; } int user_menu(int sr, int ch, int name_index) { int rc = 0; NEWWIN *bwin = NULL, *win = NULL; char *channel_name = gch(sr, ch) -> channel_name; const char *nick = NULL; char *title = NULL; if (name_index < 0 || name_index > gch(sr, ch) -> n_names) { delete_window(win); delete_window(bwin); mydoupdate(); return 0; } nick = gch(sr, ch) -> persons[name_index].nick; if (gch(sr, ch) -> persons[name_index].complete_name == NULL) (void)irc_whois(server_list[sr].fd, nick); asprintf(&title, "%s [%s] (%s)", nick, channel_name, server_list[sr].server_host); create_win_border(60, 18, title, &bwin, &win, FALSE); free(title); escape_print_xy(win, 3, 2, "^k^ kick user, ^K^ kick with message"); escape_print_xy(win, 4, 2, "^b^ ban nick, ^B^ ban+kick nick"); escape_print_xy(win, 5, 2, "^g^ ban user, ^G^ ban+kick user"); escape_print_xy(win, 6, 2, "^o^ op, ^O^ de-op"); escape_print_xy(win, 7, 2, "^w^ whois"); escape_print_xy(win, 8, 2, "^v^ version"); escape_print_xy(win, 9, 2, "^a^ allow speak"); escape_print_xy(win, 10, 2, "^A^ disallow speak"); escape_print_xy(win, 11, 2, "^p^ ping!"); escape_print_xy(win, 12, 2, "^i^ ignore, ^I^ un-ignore"); escape_print_xy(win, 13, 2, "^D^ send a file using DCC"); escape_print_xy(win, 14, 2, "^c^ CTCP"); escape_print_xy(win, 15, 2, "^q^/^LEFT cursor key^ exit this menu"); if (gch(sr, ch) -> persons[name_index].ignored) escape_print_xy(win, 16, 2, "currently ignoring user"); for(;;) { int c = -1; const char *name_complete = gch(sr, ch) -> persons[name_index].complete_name; const char *user_host = gch(sr, ch) -> persons[name_index].user_host; mywattron(win -> win, A_BOLD); mvwprintw(win -> win, 1, 2, "%-44s", name_complete ? name_complete : "(name not yet known)"); mvwprintw(win -> win, 2, 2, "%-44s", user_host ? user_host : "(user & host not yet known)"); mywattroff(win -> win, A_BOLD); mydoupdate(); c = wait_for_keypress(TRUE); if (c == KEY_LEFT || (c == KEY_MOUSE && right_mouse_button_clicked())) break; if (c <= 0) continue; if (c == 'q') break; else if (c == 7) break; else if (c == 3) exit_fi(); else if (c == 'i') { gch(sr, ch) -> persons[name_index].ignored = TRUE; add_ignore(gch(sr, ch) -> channel_name, gch(sr, ch) -> persons[name_index].nick, gch(sr, ch) -> persons[name_index].user_host); if (!save_ignore_list()) popup_notify(FALSE, "Problem saving ignore-list file"); if (rc == 0 && log_channel(sr, ch, nick, "ignore", TRUE) == -1) rc = -1; } else if (c == 'I') { gch(sr, ch) -> persons[name_index].ignored = FALSE; del_ignore(gch(sr, ch) -> channel_name, gch(sr, ch) -> persons[name_index].nick); if (!save_ignore_list()) popup_notify(FALSE, "Problem saving ignore-list file"); if (rc == 0 && log_channel(sr, ch, nick, "unignore", TRUE) == -1) rc = -1; } else if (c == 'k' || c == 'K') { const char *msg = NULL; BOOL dokick = TRUE; if (c == 'K') { msg = edit_box(60, "^Kick message^", NULL); if (!msg) dokick = FALSE; } if (dokick) { rc = irc_kick(server_list[sr].fd, channel_name, nick, msg); if (rc == 0 && log_channel(sr, ch, nick, "kick", TRUE) == -1) rc = -1; } myfree(msg); break; } else if (c == 'b' || c == 'g') { const char *what = c == 'b' ? nick : user_host; rc = irc_ban(server_list[sr].fd, channel_name, what); if (rc == 0 && log_channel(sr, ch, what, "BAN", TRUE) == -1) rc = -1; break; } else if (c == 'B' || c == 'G') { const char *msg = edit_box(60, "^Kick+ban message^", NULL); if (msg) { const char *what = c == 'B' ? nick : user_host; rc = irc_ban(server_list[sr].fd, channel_name, what); if (rc == -1) update_statusline(sr, ch, "BAN failed: also not kicked\n"); else { rc = irc_kick(server_list[sr].fd, channel_name, what, msg); if (rc == 0 && log_channel(sr, ch, what, "BAN + KICK", TRUE) == -1) rc = -1; } myfree(msg); } break; } else if (c == 'o') { rc = irc_op(server_list[sr].fd, channel_name, nick, TRUE); if (rc == 0 && log_channel(sr, ch, nick, "set OP", TRUE) == -1) rc = -1; } else if (c == 'O') { rc = irc_op(server_list[sr].fd, channel_name, nick, FALSE); if (rc == 0 && log_channel(sr, ch, nick, "remove OP", TRUE) == -1) rc = -1; } else if (c == 'w') { rc = irc_whois(server_list[sr].fd, nick); if (rc == 0 && log_channel(sr, 0, nick, "WHOIS", TRUE) == -1) rc = -1; } else if (c == 'v') { rc = irc_privmsg(server_list[sr].fd, nick, "\001VERSION\001"); if (rc == 0 && log_channel(sr, 0, nick, "CTCP VERSION", TRUE) == -1) rc = -1; } else if (c == 'a') { popup_notify(FALSE, "Speak allowed"); rc = irc_allowspeak(server_list[sr].fd, channel_name, nick, TRUE); if (rc == 0 && log_channel(sr, ch, nick, "allow speak", TRUE) == -1) rc = -1; } else if (c == 'A') { popup_notify(FALSE, "Speak disabled"); rc = irc_allowspeak(server_list[sr].fd, channel_name, nick, FALSE); if (rc == 0 && log_channel(sr, ch, nick, "disallow speak", TRUE) == -1) rc = -1; } else if (c == 'p') { if (ping_user(sr, nick) == -1) rc = -1; else popup_notify(FALSE, "Ping sent"); } else if (c == 'D') { if (dcc_send_user(sr, ch, nick) == -1) rc = -1; } else if (c == 'c') { if (ctcp_user(sr, nick) == -1) rc = -1; } else { wrong_key(); } keypress_visual_feedback(); } delete_window(win); delete_window(bwin); mydoupdate(); return rc; } void refresh_window_with_buffer(NEWWIN *where, const int window_height, buffer *pbuffer, const char *hl, BOOL force_partial_highlight) { int loop=0, n_elements = get_buffer_n_elements(pbuffer); werase(where -> win); for(loop=max(n_elements - window_height, 0); loop line_type == BET_MARKERLINE) gen_display_markerline(where, pel -> when); else { find_nick_colorpair(pel -> msg_from, &ncs); output_to_window(where, pel -> msg, hl, pel -> line_type, nick_color ? &ncs : NULL, force_partial_highlight, TRUE); } } pbuffer -> last_shown = n_elements; mydoupdate(); } int select_server(void) { NEWWIN *bwin = NULL, *win = NULL; char *title = NULL; int lines = 12, cols = 59; int offset = 0, woffset = 0, selection = -1; asprintf(&title, "Select server"); create_win_border(63, 16, title, &bwin, &win, FALSE); free(title); for(;;) { int c = -1, index = 0; werase(win -> win); for(index=0; index win, A_REVERSE); mvwprintw(win -> win, 1 + index, 2, buffer); if (index == woffset) mywattroff(win -> win, A_REVERSE); free(buffer); } mydoupdate(); c = wait_for_keypress(TRUE); if (c == 0) continue; if (c == 'q' || c == 'Q' || c == -1) break; else if (c == KEY_LEFT || (c == KEY_MOUSE && right_mouse_button_clicked())) break; else if (c == 7) break; if (c == KEY_UP) { if (woffset > 0) woffset--; else if (offset > 0) offset--; else wrong_key(); } else if (c == KEY_DOWN) { if (woffset < lines - 1 && woffset + offset < n_servers - 1) woffset++; else if (offset + woffset < n_servers - 1) offset++; else wrong_key(); } else if (c == 3) { if (yesno_box(FALSE, "Terminate f-irc", "Are you sure you want to terminate the program?", FALSE) == YES) exit_fi(); } else if (c == ' ' || c == KEY_RIGHT || c == 13) { selection = offset + woffset; break; } else { wrong_key(); } } delete_window(win); delete_window(bwin); mydoupdate(); return selection; } void edit_highlight_matchers() { if (edit_string_array(&extra_highlights, "EDIT \"highlight matchers\", left cursor to exit")) save_config_with_popup(); } void edit_headline_matchers() { if (edit_string_array(&matchers, "EDIT \"headline matchers\", left cursor to exit")) save_config_with_popup(); } void edit_send_after_connect(int sr) { char *title = NULL; asprintf(&title, "EDIT %s \"send after connect\", left cursor to exit", gsr(sr) -> server_host); edit_string_array(&gsr(sr) -> send_after_login, title); free(title); } void edit_auto_join(int sr) { char *title = NULL; asprintf(&title, "EDIT %s \"auto-join\", left cursor to exit", gsr(sr) -> server_host); edit_string_array(&gsr(sr) -> auto_join, title); free(title); } void edit_favorites(void) { int i = 0; string_array_t f; init_string_array(&f); for(i=0; i win); escape_print_xy(win, 1, 2, "^a^ add ^e^/^right cursor key^ edit ^i^ insert"); escape_print_xy(win, 2, 2, "^d^ del (the one under the cursor) ^/^ search"); for(index=0; index win, A_REVERSE); mvwprintw(win -> win, 4 + index, 2, buffer); if (index == woffset) mywattroff(win -> win, A_REVERSE); free(buffer); } mydoupdate(); c = wait_for_keypress(TRUE); if (c == 0) continue; if (c == 'q' || c == 'Q' || c == -1) break; else if (c == KEY_LEFT || (c == KEY_MOUSE && right_mouse_button_clicked())) break; else if (c == 7) break; if (c == KEY_UP) { if (woffset > 0) woffset--; else if (offset > 0) offset--; else wrong_key(); } else if (c == KEY_DOWN) { if (woffset < lines - 1 && woffset + offset < string_array_get_n(p) - 1) woffset++; else if (woffset + offset < string_array_get_n(p) - 1) offset++; else wrong_key(); } else if (c == KEY_PPAGE) { if (offset >= lines) offset -= lines; else if (woffset) woffset = 0; else wrong_key(); } else if (c == KEY_NPAGE) { if (woffset + offset + lines < string_array_get_n(p) - 1) offset += lines; else if (woffset + offset < string_array_get_n(p) - 1) { woffset = 0; offset = string_array_get_n(p) - 1; } else { wrong_key(); } } else if (c == 3) { if (yesno_box(FALSE, "Terminate f-irc", "Are you sure you want to terminate the program?", FALSE) == YES) exit_fi(); } else if (c == 'd') { if (yesno_box(FALSE, "Delete", "Are you sure you want to delete this entry?", FALSE) == YES) { del_nr_from_string_array(p, offset + woffset); changes = TRUE; woffset = offset = 0; } } else if (c == '/') { const char *what = edit_box(60, "^Search...^", NULL); if (what != NULL && strlen(what) != 0) { int n = string_array_get_n(p), idx = -1, found_at = -1; for(idx=0; idx server_host, gsr(sr) -> server_port); create_win_border(63, 16, title, &bwin, &win, FALSE); free(title); for(;;) { int c = -1; werase(win -> win); escape_print_xy(win, 1, 2, "^d^ description"); escape_print_xy(win, 2, 2, "^h^ host"); escape_print_xy(win, 3, 2, "^p^ port"); escape_print_xy(win, 4, 2, "^U^ complete name"); escape_print_xy(win, 5, 2, "^u^ username"); escape_print_xy(win, 6, 2, "^P^ password"); escape_print_xy(win, 7, 2, "^n^ nickname"); escape_print_xy(win, 8, 2, "^S^ edit \"send after connect\""); escape_print_xy(win, 9, 2, "^A^ edit \"auto-join\""); mydoupdate(); c = wait_for_keypress(TRUE); if (c == 0) continue; if (c == 'q' || c == 'Q' || c == -1) break; if (c == KEY_LEFT || (c == KEY_MOUSE && right_mouse_button_clicked())) break; else if (c == 7) break; if (c == 3) { if (yesno_box(FALSE, "Terminate f-irc", "Are you sure you want to terminate the program?", FALSE) == YES) exit_fi(); } else if (c == 'd') { const char *n = edit_box(60, "^Edit server description^", gsr(sr) -> description); if (n != NULL) { myfree(gsr(sr) -> description); gsr(sr) -> description = n; } } else if (c == 'h') { const char *n = edit_box(60, "^Edit server host^", gsr(sr) -> server_host); if (n != NULL) { myfree(gsr(sr) -> server_host); gsr(sr) -> server_host = n; } restart_required = TRUE; } else if (c == 'p') { char *port_str = NULL; const char *n = NULL; asprintf(&port_str, "%d", gsr(sr) -> server_port); n = edit_box(60, "^Edit server port^", port_str); free(port_str); if (n != NULL) gsr(sr) -> server_port = atoi(n); restart_required = TRUE; } else if (c == 'U') { char *n = (char *)edit_box(60, "^Edit \"complete name\"^", gsr(sr) -> user_complete_name); if (n != NULL) { free(gsr(sr) -> user_complete_name); gsr(sr) -> user_complete_name = n; } restart_required = TRUE; } else if (c == 'u') { char *n = (char *)edit_box(60, "^Edit user name^", gsr(sr) -> username); if (n != NULL) { free(gsr(sr) -> username); gsr(sr) -> username = n; } restart_required = TRUE; } else if (c == 'P') { char *n = (char *)edit_box(60, "^Edit password^", gsr(sr) -> password); if (n != NULL) { free(gsr(sr) -> password); gsr(sr) -> password = n; } restart_required = TRUE; } else if (c == 'n') { char *n = (char *)edit_box(60, "^Edit nick^", gsr(sr) -> nickname); if (n != NULL) { free(gsr(sr) -> nickname); gsr(sr) -> nickname = n; } restart_required = TRUE; } else if (c == 'S') edit_send_after_connect(sr); else if (c == 'A') edit_auto_join(sr); else { wrong_key(); } } if (restart_required) popup_notify(FALSE, "The changes you made require you to\n\"restart the irc-connection\".\nTo do so, press 'r' in the server menu."); delete_window(win); delete_window(bwin); mydoupdate(); } void server_menu(int sr) { NEWWIN *bwin = NULL, *win = NULL; char *title = NULL; asprintf(&title, "%s [%s]", gsr(sr) -> description, gsr(sr) -> server_host); create_win_border(63, 16, title, &bwin, &win, FALSE); free(title); for(;;) { char *ss = NULL; int c = -1; char *ch_list_compl = NULL; char *addr = NULL, *addr_real = NULL, *bps_str = NULL; time_t now = time(NULL); double bps = 0.0; int t_diff = now - gsr(sr) -> ts_bytes; werase(win -> win); asprintf(&addr, "address: ^[%s]:%d^", gsr(sr) -> server_host, gsr(sr) -> server_port); escape_print_xy(win, 3, 2, addr); free(addr); asprintf(&addr_real, "connected to: ^%s^", gsr(sr) -> server_real); escape_print_xy(win, 4, 2, addr_real); free(addr_real); bps = gsr(sr) -> prev_bps; if (t_diff) bps = (bps + gsr(sr) -> bytes / (double)t_diff) / 2; asprintf(&bps_str, "BPS: %.2f latency: %fs", bps, gsr(sr) -> server_latency); escape_print_xy(win, 5, 2, bps_str); free(bps_str); escape_print_xy(win, 7, 2, "^r^ reconnect"); escape_print_xy(win, 8, 2, "^p^ disconnect & remove"); escape_print_xy(win, 9, 2, "^g^ (re-)retrieve channel list (/LIST)"); escape_print_xy(win, 10, 2, "^j^ join a channel"); escape_print_xy(win, 11, 2, "^e^ edit server settings"); escape_print_xy(win, 14, 2, "^q^/^LEFT cursor key^ exit this menu"); if (gsr(sr) -> channel_list_complete == TRUE) asprintf(&ch_list_compl, "List of channels available (%d known)", gsr(sr) -> channel_list_n); else asprintf(&ch_list_compl, "List of channels NOT YET available (received: %d)", gsr(sr) -> channel_list_n); escape_print_xy(win, 1, 2, ch_list_compl); free(ch_list_compl); switch(gsr(sr) -> state) { case STATE_NO_CONNECTION: ss = "Server state: no connection, will connect"; break; case STATE_ERROR: ss = "Server state: connect error"; break; case STATE_TCP_CONNECT: ss = "Server state: connecting (TCP)"; break; case STATE_IRC_CONNECTING: ss = "Server state: connecting (IRC)"; break; case STATE_CONNECTED1: ss = "Server state: connecting (IRC handshake phase 1)"; break; case STATE_CONNECTED2: ss = "Server state: connecting (IRC handshake phase 2)"; break; case STATE_LOGGING_IN: ss = "Server state: connecting (logging in)"; break; case STATE_RUNNING: ss = "Server state: connected!"; break; case STATE_DISCONNECTED: ss = "Server state: disconnected"; break; default: ss = "Server state: UNKNOWN!"; break; } escape_print_xy(win, 2, 2, ss); mydoupdate(); c = wait_for_keypress(TRUE); if (c == 0) continue; if (c == 'q' || c == 'Q' || c == -1) break; else if (c == KEY_LEFT || (c == KEY_MOUSE && right_mouse_button_clicked())) break; else if (c == 7) break; if (c == 3) exit_fi(); else if (c == 'p') { if (n_servers > 1) { if (yesno_box(FALSE, "Remove server", "Are you sure?", FALSE) == YES) { close_server(sr, TRUE); free_server(sr); if (sr <= n_servers - 1) memmove(&server_list[sr], &server_list[sr + 1], sizeof(server) * (n_servers - (sr + 1))); n_servers--; show_channel_names_list(); change_channel(0, 0, TRUE, FALSE); popup_notify(FALSE, "Server removed"); } } else { popup_notify(FALSE, "Cannot remove server: need at least 1."); } break; } else if (c == 'r') { server_list[sr].reconnect_delay = DEFAULT_RECONNECT_DELAY; restart_server(sr); popup_notify(FALSE, "Restarting connection"); } else if (c == 'g') { if (irc_list(gsr(sr) -> fd) == -1) { popup_notify(FALSE, "Disconnected from server"); break; } else { popup_notify(FALSE, "Requested channel list"); } } else if (c == 'e') { edit_server(sr); } else if (c == 'j') { if (gsr(sr) -> channel_list_complete == FALSE) popup_notify(FALSE, "Please wait for the channel list to load"); else { buffer *temp = create_buffer(server_list[sr].channel_list_n); int loop, csr = -1, cch = -1; char *dummy = NULL; BOOL err = FALSE; NEWWIN *bwin = NULL, *win = NULL; create_win_border(max_x - 6, max_y - 4, "right cursor key: select, left: cancel", &bwin, &win, FALSE); for(loop=0; loop channel_list[loop].channel, gsr(sr) -> channel_list[loop].topic); add_to_buffer(temp, line, NULL, FALSE, sr, loop); free(line); } if (scrollback_and_select(win, temp, &dummy, &csr, &cch, server_list[sr].nickname, FALSE, FALSE)) { if (irc_join(server_list[sr].fd, gsr(sr) -> channel_list[cch].channel)) err = TRUE; } delete_window(win); delete_window(bwin); mydoupdate(); myfree(dummy); free_buffer(temp); if (err) break; } } } delete_window(win); delete_window(bwin); mydoupdate(); } void add_server_menu(void) { const char *nick = NULL, *s = NULL; s = edit_box(60, "^Add server: enter hostname:port^", NULL); if (s) nick = edit_box(60, "^Add server: enter nick^", NULL); if (nick) { add_server(s, nick, "notset", nick, nick, s); popup_notify(FALSE, "Server %s was added", s); } myfree(s); myfree(nick); } int user_channel_menu(int sr, const char *nick) { int rc = 0; NEWWIN *bwin = NULL, *win = NULL; char *title = NULL; asprintf(&title, "%s (%s)", nick, server_list[sr].server_host); create_win_border(40, 9, title, &bwin, &win, TRUE); free(title); escape_print_xy(win, 1, 2, "^w^ whois"); escape_print_xy(win, 2, 2, "^v^ version"); escape_print_xy(win, 3, 2, "^p^ ping!"); escape_print_xy(win, 4, 2, "^D^ send a file using DCC"); escape_print_xy(win, 5, 2, "^c^ CTCP"); escape_print_xy(win, 6, 2, "^q^/^LEFT cursor key^ exit this menu"); mydoupdate(); for(;;) { int c = wait_for_keypress(FALSE); if (c == KEY_LEFT || (c == KEY_MOUSE && right_mouse_button_clicked())) break; if (c == 'q') break; else if (c == 7) break; else if (c == 3) exit_fi(); else if (c == KEY_F(1)) user_channel_menu_help(); else if (c == 'w') { rc = irc_whois(server_list[sr].fd, nick); if (rc == 0 && log_channel(sr, 0, nick, "CTCP WHOIS", TRUE) == -1) rc = -1; break; } else if (c == 'v') { rc = irc_privmsg(server_list[sr].fd, nick, "\001VERSION\001"); if (rc == 0 && log_channel(sr, 0, nick, "CTCP VERSION", TRUE) == -1) rc = -1; break; } else if (c == 'p') { if (ping_user(sr, nick) == -1) rc = -1; else popup_notify(FALSE, "Ping sent"); } else if (c == 'D') { if (dcc_send_user(sr, 0, nick) == -1) rc = -1; } else if (c == 'c') { if (ctcp_user(sr, nick) == -1) rc = -1; } else wrong_key(); } delete_window(win); delete_window(bwin); mydoupdate(); return rc; } /* use_getch: used when no valid configuration was loaded */ yna_reply_t yesno_box(BOOL use_getch, const char *title, const char *q, BOOL allow_abort) { NEWWIN *bwin = NULL, *win = NULL; int q_len = max(strlen(title), strlen(q)); yna_reply_t rc = NO; create_win_border(q_len + 5, 4, title, &bwin, &win, FALSE); for(;;) { int c = -1; werase(win -> win); escape_print_xy(win, 1, 1, q); if (allow_abort) escape_print_xy(win, 2, 1, "^y^es / ^n^o / ^a^bort"); else escape_print_xy(win, 2, 1, "^y^es / ^n^o"); mydoupdate(); if (use_getch) c = getch(); else c = wait_for_keypress(FALSE); if (c == 3 || c == 'n' || c == 'N') { rc = NO; break; } if (c == 'y' || c == 'Y') { rc = YES; break; } if ((c == 7 || c == 'a' || c == 'A') && allow_abort) { rc = ABORT; break; } wrong_key(); } delete_window(win); delete_window(bwin); mydoupdate(); return rc; } void put_field(NEWWIN *win, const char *what, int y, int x, short fg, short bg) { short dummy = get_color_ncurses(fg, bg); color_on(win, dummy); mvwprintw(win -> win, y, x, what); color_off(win, dummy); } void put_colors(NEWWIN *win, int y, int x) { put_field(win, "BLACK ", y + 0, x, COLOR_WHITE, COLOR_BLACK); put_field(win, "RED ", y + 1, x, COLOR_BLACK, COLOR_RED); put_field(win, "GREEN ", y + 2, x, COLOR_BLACK, COLOR_GREEN); put_field(win, "YELLOW ", y + 3, x, COLOR_BLACK, COLOR_YELLOW); put_field(win, "BLUE ", y + 4, x, COLOR_BLACK, COLOR_BLUE); put_field(win, "MAGENTA", y + 5, x, COLOR_BLACK, COLOR_MAGENTA); put_field(win, "CYAN ", y + 6, x, COLOR_BLACK, COLOR_CYAN); put_field(win, "WHITE ", y + 7, x, COLOR_BLACK, COLOR_WHITE); put_field(win, "default", y + 8, x, -1, -1); } void put_cursor(NEWWIN *win, int y, int x, BOOL selected) { mywattron(win -> win, A_REVERSE); if (selected) mvwprintw(win -> win, y, x, ">"); else mvwprintw(win -> win, y, x, " "); mywattroff(win -> win, A_REVERSE); } int choose_colorpair(const int default_pair) { int color = default_pair; NEWWIN *bwin = NULL, *win = NULL; BOOL ret_ok = TRUE, update_buttons = TRUE; short fg = 0, bg = 0; int column = 0; pair_content(default_pair, &fg, &bg); if (fg == -1) fg = 8; if (bg == -1) bg = 8; create_win_border(50, 15, " Navigate with cursor keys, tab+enter to exit ", &bwin, &win, FALSE); werase(win -> win); mywattron(win -> win, A_UNDERLINE); mvwprintw(win -> win, 1, 10, "foreground"); mvwprintw(win -> win, 1, 26, "background"); mywattroff(win -> win, A_UNDERLINE); put_colors(win, 2, 10); put_colors(win, 2, 26); for(;;) { int c = -1, old_fg = fg, old_bg = bg; put_cursor(win, 2 + fg, 8, column == 0); put_cursor(win, 2 + bg, 24, column == 1); if (update_buttons) { if (ret_ok == TRUE) escape_print_xy(win, 13, 1, "^[ OK ]^ [ CANCEL ]"); else escape_print_xy(win, 13, 1, "[ OK ] ^[ CANCEL ]^"); update_buttons = FALSE; } c = wait_for_keypress(FALSE); if (c == 9) { ret_ok = !ret_ok; update_buttons = TRUE; } else if (c == 3 || c == 7) { color = default_pair; break; } else if (c == 13) { if (!ret_ok) { color = default_pair; break; } if (fg == 8) fg = -1; if (bg == 8) bg = -1; color = get_color_ncurses(fg, bg); if (fg == bg && fg != -1) { if (yesno_box(FALSE, "Use selected colors?", "Foreground and background colors are the same: are you sure?", FALSE) == YES) break; } else { break; } } else if (c == KEY_LEFT) { if (column > 0) column--; else wrong_key(); } else if (c == KEY_RIGHT) { if (column < 1) column++; else wrong_key(); } else if (c == KEY_UP) { if (column == 0) { if (fg) fg--; else wrong_key(); } else { if (bg) bg--; else wrong_key(); } } else if (c == KEY_DOWN) { if (column == 0) { if (fg < 8) fg++; else wrong_key(); } else { if (bg < 8) bg++; else wrong_key(); } } else { wrong_key(); } if (fg != old_fg) mvwprintw(win -> win, 2 + old_fg, 8, " "); if (bg != old_bg) mvwprintw(win -> win, 2 + old_bg, 24, " "); } delete_window(win); delete_window(bwin); mydoupdate(); return color; } BOOL configure_firc(void) { BOOL force_redraw = TRUE; NEWWIN *bwin = NULL, *win = NULL; int cursor = 0, offset = 0; BOOL redraw = TRUE; const int win_height = 21, win_width = 68, content_offset = 6, content_height = win_height - (content_offset + 1); int cnt = 0, cnf_len = 0, val_pos = 50; const char title[] = " Configure f-irc, key right to select, key left to exit "; const char *find = NULL; for(cnt=0; cnf_pars[cnt].p != NULL; cnt++) { int l = strlen(cnf_pars[cnt].name); if (l > cnf_len) cnf_len = l; } if (cnf_len + 2 < val_pos) val_pos = cnf_len + 2; create_win_border(win_width, win_height, title, &bwin, &win, TRUE); for(;;) { int prev_pos = offset + cursor; int c = -1; if (redraw) { int loop = 0, max_rows = cnt - offset; werase(win -> win); escape_print_xy(win, 1, 1, "^F2^: store configuration on disk"); escape_print_xy(win, 2, 1, "^a^: add server ^s^: edit server(s)"); escape_print_xy(win, 3, 1, "^l^ edit favorites list ^d^ edit dictionary"); escape_print_xy(win, 4, 1, "^m^: edit highlight matchers ^h^ edit headline matchers"); for(loop=0; loop win, A_REVERSE); mvwprintw(win -> win, content_offset + loop, 1, "%s", str_out); if (loop == cursor) mywattroff(win -> win, A_REVERSE); free(str_out); } mydoupdate(); redraw = FALSE; } c = wait_for_keypress(FALSE); if (c == KEY_LEFT || c == 3 || toupper(c) == 'Q') break; else if (c == KEY_LEFT || (c == KEY_MOUSE && right_mouse_button_clicked())) break; else if (c == 7) break; if (c == KEY_F(1)) configure_firc_help(); else if (c == KEY_F(2)) save_config_with_popup(); else if (c == 'a') add_server_menu(); else if (c == 's') { for(;;) { int sr = select_server(); if (sr == -1) break; edit_server(sr); } } else if (c == 'l') { char *err_msg = NULL; edit_favorites(); if (save_config(TRUE, &err_msg) == FALSE) { popup_notify(FALSE, "%s", err_msg); free(err_msg); } } else if (c == 'd') edit_dictionary(); else if (c == 'h') edit_headline_matchers(); else if (c == 'm') edit_highlight_matchers(); else if (c == KEY_RIGHT || c == 13 || c == ' ') { int pos = offset + cursor; BOOL *pb = NULL; int *pi = NULL; char *pi_str = NULL; char *pc = NULL; const char *new_str = NULL; char *q = NULL; asprintf(&q, "%s:", cnf_pars[pos].name); q[0] = toupper(q[0]); switch(cnf_pars[pos].type) { case CNF_BOOL: pb = (BOOL *)cnf_pars[pos].p; *pb = onoff_box(q, *pb); break; case CNF_VALUE: pi = (int *)cnf_pars[pos].p; asprintf(&pi_str, "%d", *pi); new_str = edit_box(val_pos, q, pi_str); if (new_str) { *pi= atoi(new_str); myfree(new_str); } free(pi_str); break; case CNF_STRING: pc = *(char **)cnf_pars[pos].p; new_str = edit_box(val_pos, q, pc); if (new_str) { free(pc); *(char **)cnf_pars[pos].p = (char *)new_str; } break; case CNF_COLOR: pi = (int *)cnf_pars[pos].p; *pi = choose_colorpair(*pi); break; } free(q); redraw = TRUE; if (cnf_pars[pos].dofunc) (cnf_pars[pos].dofunc)(); } else if (c == KEY_PPAGE) { if (offset >= content_height) offset -= content_height; else if (cursor) cursor = 0; else wrong_key(); } else if (c == KEY_NPAGE) { if (cursor + offset + content_height < cnt - 1) offset += content_height; else if (cursor + offset < cnt - 1) { cursor = 0; offset = cnt - 1; } else { wrong_key(); } } else if (c == KEY_UP) { if (cursor > 0) cursor--; else if (offset > 0) offset--; else wrong_key(); } else if (c == KEY_DOWN) { if (cursor < content_height - 1 && cursor + offset < cnt - 1) cursor++; else if (cursor + offset < cnt - 1) offset++; else wrong_key(); } else if (c == KEY_HOME) { cursor = offset = 0; } else if (c == KEY_END) { cursor = content_height - 1; offset = cnt - cursor - 1; } else if (c == '/' || c == 'n') { int pos = offset + cursor + 1, loop = 0, found = -1; if (c == '/') { const char *new_find = edit_box(40, "Search for...", find); if (new_find) { myfree(find); find = new_find; } } if (find) { for(loop=0; loop= cnt) pos -= cnt; if (strcasestr(cnf_pars[pos].name, find)) { found = pos; break; } pos++; } } if (found == -1) wrong_key(); else { cursor = pos % content_height; offset = pos - cursor; } } else { wrong_key(); } if (cursor + offset != prev_pos) redraw = TRUE; } delete_window(win); delete_window(bwin); mydoupdate(); return force_redraw; } void close_notice_channels(void) { if (yesno_box(FALSE, "Close all \"NOTICE\" channels", "Are you sure?", FALSE) == YES) { int sr = 0; NEWWIN *win = NULL, *bwin = NULL; create_win_border(30, 3, "Please wait", &bwin, &win, FALSE); mvwprintw(win -> win, 1, 1, "Closing \"NOTICE\" channels..."); mydoupdate(); for(sr = 0; sr < n_servers; sr++) { int ch = 0; server *ps = &server_list[sr]; BOOL left = FALSE; do { left = FALSE; for(ch=1; ch n_channels; ch++) { channel *pc = &ps -> pchannels[ch]; if (is_channel(pc -> channel_name)) continue; if (pc -> recvd_non_notice == FALSE) { cmd_LEAVE(sr, ch, NULL); left = TRUE; break; } } } while(left); } delete_window(win); delete_window(bwin); mydoupdate(); } } void edit_dictionary(void) { BOOL changes_pending = edit_string_array(&dictionary, "Edit dictionary"); if (changes_pending) { sort_string_array(&dictionary); if (!dictionary_file && string_array_get_n(&dictionary) > 0) dictionary_file = explode_path("~/.firc.dictionary"); if (dictionary_file) save_dictionary(); } } fi-1.36/channels.c0000644000175000017500000004502012302365027013677 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 863 $ * (C) 2006-2014 by folkert@vanheusden.com */ #define _GNU_SOURCE #include #include #include #include #include #include #include "gen.h" #include "theme.h" #include "term.h" #include "buffer.h" #include "channels.h" #include "servers.h" #include "utils.h" #include "irc.h" #include "loop.h" #include "names.h" #include "main.h" #include "user.h" #include "wordcloud.h" #include "channels.h" #include "config.h" #include "chistory.h" int channel_offset = 0, channel_cursor = 0; BOOL vc_list_data_only = FALSE; cu_t undo_channels[N_CU]; void free_channel(channel *pc) { myfree(pc -> channel_name); myfree(pc -> topic); free_buffer(pc -> pbuffer); free_buffer(pc -> input_buffer); free_names_list(pc); free_utf8_string(pc -> input); } void free_visible_channels_list(void) { if (vc_list) { myfree(vc_list -> server_index); myfree(vc_list -> is_server_channel); myfree(vc_list -> channel_index); myfree(vc_list -> is_1on1_channel); myfree(vc_list); vc_list = NULL; } } int add_channel(int server_index, const char *channel_name) { channel *pc = NULL; int channel_index = server_list[server_index].n_channels; server_list[server_index].n_channels++; server_list[server_index].pchannels = realloc(server_list[server_index].pchannels, server_list[server_index].n_channels * sizeof(channel)); pc = &server_list[server_index].pchannels[channel_index]; memset(pc, 0x00, sizeof(channel)); pc -> channel_name = strdup(channel_name); pc -> pbuffer = create_buffer(max_channel_record_lines); pc -> input_buffer = create_buffer(max_channel_record_lines); pc -> input_buffer_cursor = 0; pc -> input_buffer_changed = FALSE; pc -> adding_names = TRUE; pc -> input = alloc_utf8_string(); return channel_index; } BOOL redo_channel(void) { channel *pc = NULL; int si = -1; if (undo_channels[N_CU - 1].data == NULL) return FALSE; si = find_server_index(undo_channels[N_CU - 1].s_name); if (si == -1) return FALSE; if (find_channel_index(si, undo_channels[N_CU - 1].data -> channel_name) == -1) { server_list[si].n_channels++; server_list[si].pchannels = realloc(server_list[si].pchannels, server_list[si].n_channels * sizeof(channel)); pc = &server_list[si].pchannels[server_list[si].n_channels - 1]; memcpy(pc, undo_channels[N_CU - 1].data, sizeof(channel)); } free(undo_channels[N_CU - 1].data); myfree(undo_channels[N_CU - 1].s_name); memmove(&undo_channels[1], &undo_channels[0], (N_CU - 1) * sizeof(cu_t)); memset(&undo_channels[0], 0x00, sizeof(cu_t)); if (pc != NULL && is_channel(pc -> channel_name)) return irc_join(server_list[si].fd, pc -> channel_name) != -1; return TRUE; } void close_channel(int server_index, int channel_index, BOOL leave_channel) { channel *pc = NULL; /* if already membering an other channel, forget that one first */ if (undo_channels[0].data) { free_channel(undo_channels[0].data); free(undo_channels[0].data); myfree(undo_channels[0].s_name); } pc = &server_list[server_index].pchannels[channel_index]; /* rember this channel */ memmove(&undo_channels[0], &undo_channels[1], (N_CU - 1) * sizeof(cu_t)); undo_channels[N_CU - 1].data = (channel *)malloc(sizeof(channel)); memcpy(undo_channels[N_CU - 1].data, pc, sizeof(channel)); if (server_list[server_index].description) undo_channels[N_CU - 1].s_name = strdup(server_list[server_index].description); else undo_channels[N_CU - 1].s_name = strdup(server_list[server_index].server_host); /* remove channel from current channel-list */ if (is_channel(pc -> channel_name) && leave_channel == TRUE) irc_part(server_list[server_index].fd, pc -> channel_name, part_message); if (channel_index < server_list[server_index].n_channels - 1) { memmove(&server_list[server_index].pchannels[channel_index], &server_list[server_index].pchannels[channel_index + 1], sizeof(channel) * (server_list[server_index].n_channels - (channel_index + 1))); } server_list[server_index].n_channels--; if (server_list[server_index].n_channels == 0) { free(server_list[server_index].pchannels); server_list[server_index].pchannels = NULL; } } void create_visible_channels_list(void) { int sloop = 0; int old_server = -1, old_channel = -1; if (vc_list) { int offset = channel_offset + channel_cursor; if (offset >= vc_list -> n_channels && vc_list -> n_channels > 0) offset = vc_list -> n_channels - 1; old_server = vc_list -> server_index[offset]; old_channel = vc_list -> channel_index[offset]; } free_visible_channels_list(); vc_list = malloc(sizeof(visible_channels)); memset(vc_list, 0x00, sizeof(visible_channels)); for(sloop=0; sloop n_channels; /* add this server to the list */ for(cloop=0; cloop pchannels[cloop]; if ((vc_list_data_only && pc -> new_entry != NONE) || !vc_list_data_only || cloop == 0 || (cloop == old_channel && sloop == old_server) || (cloop == current_server_channel_nr && sloop == current_server)) { /* grow lists */ vc_list -> server_index = (int *)realloc(vc_list -> server_index, sizeof(int) * (vc_list -> n_channels + 1)); vc_list -> is_server_channel = (BOOL *)realloc(vc_list -> is_server_channel, sizeof(BOOL) * (vc_list -> n_channels + 1)); vc_list -> channel_index = (int *)realloc(vc_list -> channel_index, sizeof(int) * (vc_list -> n_channels + 1)); vc_list -> is_1on1_channel = (BOOL *)realloc(vc_list -> is_1on1_channel, sizeof(BOOL) * (vc_list -> n_channels + 1)); vc_list -> is_server_channel[vc_list -> n_channels] = cloop == 0; vc_list -> is_1on1_channel[vc_list -> n_channels] = !is_channel(pc -> channel_name); vc_list -> server_index[vc_list -> n_channels] = sloop; vc_list -> channel_index[vc_list -> n_channels] = cloop; vc_list -> n_channels++; } } } } void show_channel_from_list(NEWWIN *win, int vc_index, int y) { char mark = ' '; int loop; int textcolor = 0; int sc = -1; int server_nr = vc_list -> server_index[vc_index]; int channel_nr = vc_list -> channel_index[vc_index]; server *ps = &server_list[server_nr]; channel *pc = &ps -> pchannels[channel_nr]; if (theme.channellist_newlines_markchar != ' ') { mark = pc -> new_entry == NONE ? ' ' : theme.channellist_newlines_markchar; if (pc -> new_entry == YOU) textcolor = highlight_colorpair; else if (pc -> new_entry == META) textcolor = meta_colorpair; else textcolor = 0; } if (channel_nr == 0) { sc = get_server_color(server_nr); if (sc != -1) color_on(win, sc); } if (sc == -1 && textcolor && colors_meta) color_on(win, textcolor); for(loop=0; loop ncols; loop++) mvwprintw(win -> win, y, loop, " "); if (vc_list -> is_server_channel[vc_index]) { /*if (server_list[server_nr].minimized) color_on(win, theme.color_channellist_minimized_server); */ if (ps -> description) limit_print(win, win -> ncols - 0, y, 0, "%c%s", mark, ps -> description); else limit_print(win, win -> ncols - 0, y, 0, "%c%s", mark, ps -> server_host); /*if (server_list[server_nr].minimized) color_off(win, theme.color_channellist_minimized_server);*/ } else { limit_print(win, win -> ncols - 1, y, 0, "%c %s", mark, pc -> channel_name); } if (sc != -1) color_off(win, sc); if (sc == -1 && textcolor && colors_meta) color_off(win, textcolor); } void show_channel_list(NEWWIN *channel_window) { int channel_index = -1; create_visible_channels_list(); werase(channel_window -> win); for(channel_index=channel_offset; channel_index nlines, vc_list -> n_channels); channel_index++) { int y = channel_index - channel_offset; if (y == channel_cursor) mywattron(channel_window -> win, A_REVERSE); show_channel_from_list(channel_window, channel_index, y); if (y == channel_cursor) mywattroff(channel_window -> win, A_REVERSE); } } int find_channel_index(int cur_server, const char *channel_name) { server *ps = &server_list[cur_server]; int loop = 0; if (strcmp(channel_name, "AUTH") == 0) return 0; for(loop=0; loop n_channels; loop++) { if (strcasecmp(ps -> pchannels[loop].channel_name, channel_name) == 0) return loop; } return -1; /* new channel */ } void set_new_line_received() { if (get_cursor_mode() == CM_CHANNELS || get_cursor_mode() == CM_EDIT || get_cursor_mode() == CM_WC) show_channel_names_list(); } int find_vc_list_entry(int server_index, int channel_index) { int loop, channel_cursor = -1; for(loop=0; loop n_channels; loop++) { if (vc_list -> server_index[loop] == server_index && vc_list -> channel_index[loop] == channel_index) { channel_cursor = loop; break; } } return channel_cursor; } int find_vc_list_entry_by_name(const char *name, int search_offset, BOOL match_server_channel) { int loop; for(loop=0; loop n_channels; loop++) { int cur = (search_offset + loop + 1) % vc_list -> n_channels; int si = vc_list -> server_index[cur]; int ci = vc_list -> channel_index[cur]; server *ps = &server_list[si]; if (ci == 0 && match_server_channel == TRUE) { if (strcasestr(ps -> description, name)) return cur; if (strcasestr(ps -> server_host, name)) return cur; } else if (strcasestr(ps -> pchannels[ci].channel_name, name)) { return cur; } } return -1; } BOOL change_channel(int server_index, int channel_index, BOOL change_cursor, BOOL push_history) { server *ps = &server_list[server_index]; channel *pc = &ps -> pchannels[channel_index]; BOOL changed = FALSE; if (auto_markerline) add_markerline(current_server, current_server_channel_nr); /* remember last view timestamp */ if (current_server != -1) { time_t now = time(NULL); server_list[server_index].last_view = now; if (current_server_channel_nr != -1 && current_server_channel_nr < ps -> n_channels) pc -> last_view = now; } refresh_window_with_buffer(chat_window, max_y - 1, pc -> pbuffer, ps -> nickname, FALSE); werase(topic_line_window -> win); output_to_window(topic_line_window, str_or_nothing(pc -> topic), ps -> nickname, FALSE, NULL, TRUE, TRUE); pc -> topic_changed = FALSE; pc -> new_entry = NONE; names_offset = 0; names_cursor = 0; show_channel_names_list(); if (current_server != server_index || current_server_channel_nr != channel_index) changed = TRUE; if (push_history) push_channel_history(current_server, current_server_channel_nr); current_server = server_index; current_server_channel_nr = channel_index; reset_topic_scroll_offset(); update_channel_border(server_index); show_channel_names_list(); /* find new channel cursor */ if (change_cursor) { int dummy = find_vc_list_entry(server_index, channel_index); if (dummy != -1) { channel_cursor = dummy % channel_window -> nlines; channel_offset = dummy - channel_cursor; } } reposition_editline_cursor(); return changed; } void show_channel_names_list(void) { BOOL cm_duration = (time(NULL) - get_cursor_mode_since()) < 4; if (channel_window_border) { color_on(input_window_border, default_colorpair); if (get_cursor_mode() == CM_EDIT) mywattron(input_window_border -> win, A_REVERSE); else if (get_cursor_mode() == CM_NAMES || get_cursor_mode() == CM_CHANNELS) mywattron(channel_window_border -> win, A_REVERSE); if (cm_duration && get_cursor_mode() == CM_EDIT) { color_on(input_window_border, highlight_colorpair); mywattron(input_window_border -> win, A_BLINK); } mvwprintw(input_window_border -> win, 0, 0, ">"); mvwprintw(input_window_border -> win, 0, input_window_border -> ncols - 1, "<"); if (cm_duration && get_cursor_mode() == CM_EDIT) { color_off(input_window_border, highlight_colorpair); color_on(input_window_border, default_colorpair); mywattroff(input_window_border -> win, A_BLINK); } box(channel_window_border -> win, 0, 0); #if 0 color_on(channel_window_border, theme.channellist_border_color); wborder(channel_window_border -> win, theme.channellist_border_left_side, theme.channellist_border_right_side, theme.channellist_border_top_side, theme.channellist_border_bottom_side, theme.channellist_border_top_left_hand_corner, theme.channellist_border_top_right_hand_corner, theme.channellist_border_bottom_left_hand_corner, theme.channellist_border_bottom_right_hand_corner ); color_off(channel_window_border, theme.channellist_border_color); #endif if (get_cursor_mode() == CM_EDIT) mywattroff(input_window_border -> win, A_REVERSE); else if (get_cursor_mode() == CM_NAMES || get_cursor_mode() == CM_CHANNELS) mywattroff(channel_window_border -> win, A_REVERSE); if (inverse_window_heading) { mywattron(channel_window_border -> win, A_STANDOUT); mvwprintw(channel_window_border -> win, 0, 1, get_cursor_mode() == CM_NAMES ? "Names" : "Channels"); mywattroff(channel_window_border -> win, A_STANDOUT); } else { BOOL selected = get_cursor_mode() == CM_NAMES || get_cursor_mode() == CM_CHANNELS; if (selected) mywattron(channel_window_border -> win, A_STANDOUT); mvwprintw(channel_window_border -> win, 0, 1, get_cursor_mode() == CM_NAMES ? "[ Names ]" : "[Channels]"); if (selected) mywattroff(channel_window_border -> win, A_STANDOUT); } } if (word_cloud_n > 0 && word_cloud_win_height > 0 && wc_window_border) { if (get_cursor_mode() == CM_WC) mywattron(wc_window_border -> win, A_REVERSE); else mywattroff(wc_window_border -> win, A_REVERSE); box(wc_window_border -> win, 0, 0); if (get_cursor_mode() == CM_WC) mywattroff(wc_window_border -> win, A_REVERSE); } if (get_cursor_mode() == CM_NAMES) show_name_list(current_server, current_server_channel_nr, channel_window); else show_channel_list(channel_window); put_word_cloud(get_cursor_mode() == CM_WC, TRUE); mydoupdate(); } void go_to_last_channel(void) { if (vc_list -> n_channels >= channel_window -> nlines) { channel_cursor = channel_window -> nlines - 1; channel_offset = vc_list -> n_channels - channel_window -> nlines; } else { channel_cursor = vc_list -> n_channels - 1; channel_offset = 0; } } void do_channels_keypress(int c) { if (c == KEY_UP) { if (channel_cursor > 0) channel_cursor--; else if (channel_offset > 0) channel_offset--; else go_to_last_channel(); } else if (c == KEY_END) { int diff = vc_list -> n_channels - (channel_offset + channel_cursor); if (diff >= channel_window -> nlines) go_to_last_channel(); else channel_offset += diff; } else if (c == KEY_PPAGE) { if (channel_offset >= channel_window -> nlines) channel_offset -= channel_window -> nlines; else { channel_cursor = 0; channel_offset = 0; } } else if (c == KEY_DOWN) { if (channel_offset + channel_cursor < vc_list -> n_channels - 1) { if (channel_cursor < channel_window -> nlines - 1) channel_cursor++; else channel_offset++; } else { channel_cursor = 0; channel_offset = 0; } } else if (c == KEY_NPAGE) { if (channel_offset + channel_window -> nlines < vc_list -> n_channels - 1) channel_offset += channel_window -> nlines; else { channel_cursor = 0; channel_offset = vc_list -> n_channels - 1; } } else if (c == KEY_HOME) { channel_cursor = 0; channel_offset = 0; } else if (c == KEY_LEFT) { /* if channel_cursor is server, then minimized toggle else wrong_key(); */ if (vc_list -> is_server_channel[channel_cursor + channel_offset] == TRUE) { if (server_list[vc_list -> server_index[channel_cursor + channel_offset]].minimized == FALSE) { server_list[vc_list -> server_index[channel_cursor + channel_offset]].minimized = TRUE; if (channel_cursor + channel_offset > vc_list -> n_channels) channel_offset = channel_cursor = 0; } else { wrong_key(); } } else { wrong_key(); } } else if (c == KEY_RIGHT) { int vc_list_offset = channel_cursor + channel_offset; BOOL is_server = vc_list -> is_server_channel[vc_list_offset]; cur_channel() -> new_entry = NONE; if (current_server == vc_list -> server_index[vc_list_offset] && current_server_channel_nr == vc_list -> channel_index[vc_list_offset]) { if (is_server) { if (cur_server() -> minimized == TRUE) { cur_server() -> minimized = FALSE; show_channel_names_list(); } else { server_menu(current_server); create_visible_channels_list(); } } else if (vc_list -> is_1on1_channel[vc_list_offset]) user_channel_menu(current_server, server_list[current_server].pchannels[current_server_channel_nr].channel_name); else set_cursor_mode(CM_NAMES); } else { (void)change_channel(vc_list -> server_index[vc_list_offset], vc_list -> channel_index[vc_list_offset], FALSE, TRUE); } } } void channelwindow_mouse(mmask_t buttons, int x, int y) { static int prev_y_channels = -1; static int prev_y_names = -1; if (buttons & (BUTTON1_CLICKED | BUTTON1_DOUBLE_CLICKED)) { if (get_cursor_mode() == CM_CHANNELS) { if (channel_offset + y < vc_list -> n_channels) { if (y < channel_window -> nlines) { int vc_list_offset = y + channel_offset; (void)change_channel(vc_list -> server_index[vc_list_offset], vc_list -> channel_index[vc_list_offset], FALSE, TRUE); channel_cursor = y; } else { go_to_last_channel(); } if (prev_y_channels == y || (buttons & BUTTON1_DOUBLE_CLICKED)) { prev_y_names = -1; set_cursor_mode(CM_NAMES); } } prev_y_channels = y; } else if (get_cursor_mode() == CM_NAMES) { if (names_offset + y < cur_channel() -> n_names) { if (y < channel_window -> nlines) names_cursor = y; else go_to_last_name(); if (prev_y_names == y || (buttons & BUTTON1_DOUBLE_CLICKED)) { if (user_menu(current_server, current_server_channel_nr, names_offset + y) == -1) { cur_server() -> state = STATE_DISCONNECTED; close(cur_server() -> fd); update_statusline(current_server, current_server_channel_nr, "Connection to %s:%d closed (10)", cur_server() -> server_host, cur_server() -> server_port); } } } prev_y_names = y; } else { set_cursor_mode(CM_CHANNELS); prev_y_channels = -1; prev_y_names = -1; } } else if (buttons & BUTTON3_CLICKED) { set_cursor_mode(CM_CHANNELS); } } channel *gch(int sr, int ch) { server *ps = &server_list[sr]; return &ps -> pchannels[ch]; } fi-1.36/utils.c0000644000175000017500000001417112302365027013247 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 807 $ * (C) 2006-2014 by folkert@vanheusden.com */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gen.h" #include "error.h" #include "utils.h" int myrand(int max) { return (int)(((double)max * (double)rand()) / (double)RAND_MAX); } #if 0 void myfree(const void *p) { free((void *)p); } #endif int resize(void **pnt, int n, int *len, int size) { if (n == *len) { *len = (*len) ? (*len) * 2 : 4096; *pnt = realloc(*pnt, (*len) * size); } else if (n > *len || n<0 || *len<0) error_exit(TRUE, "resize: fatal memory corruption problem: n > len || n<0 || len<0!\n"); return 0; } ssize_t READ(int fd, char *whereto, size_t len) { ssize_t cnt=0; while(len>0) { ssize_t rc; rc = read(fd, whereto, len); if (rc == -1) return -1; else if (rc == 0) break; else { whereto += rc; len -= rc; cnt += rc; } } return cnt; } ssize_t WRITE(int fd, const char *whereto, size_t len) { ssize_t cnt=0; while(len > 0) { ssize_t rc; rc = write(fd, whereto, len); if (rc == -1) { if (errno == EAGAIN) continue; return -1; } else if (rc == 0) break; else { whereto += rc; len -= rc; cnt += rc; } } return cnt; } char * read_line_fd(int fd) { char *str = malloc(80); int n_in=0, size=80; for(;;) { char c; /* read one character */ if (READ(fd, &c, 1) != 1) { if (n_in == 0) { myfree(str); str = NULL; } return str; } /* resize input-buffer */ resize((void **)&str, n_in, &size, sizeof(char)); /* EOF or \n == end of line */ if (c == 10 || c == EOF) break; /* ignore CR */ if (c == 13) continue; /* add to string */ str[n_in++] = c; } /* terminate string */ resize((void **)&str, n_in, &size, sizeof(char)); str[n_in] = 0x00; return str; } const char * str_or_nothing(const char *string) { if (string) return string; return ""; } const char * skip_colon(const char *in) { if (in[0] == ':') return in + 1; return in; } char * replace_string(const char *in, int pos_start, int pos_end, const char *with_what) { int str_len_in = strlen(in); int str_len_ww = strlen(with_what); int n_remove = (pos_end - pos_start) + 1; /* +1 => including pos_end! */ int new_len = str_len_in + str_len_ww - n_remove; char *out = malloc(new_len + 1); /* +1 => 0x00 */ memmove(out, in, pos_start); memmove(&out[pos_start], with_what, str_len_ww); memmove(&out[pos_start + str_len_ww], &in[pos_end + 1], str_len_in - (pos_end + 1)); out[new_len] = 0x00; return out; } unsigned long int myatoul(const char *str) { unsigned long int dummy = 0; while(isspace(*str)) str++; while(*str >= '0' && *str <= '9') { dummy *= 10; dummy += (*str) - '0'; str++; } return dummy; } int count_char(const char *str, char what) { int len = strlen(str); int loop = 0, cnt = 0; for(loop=0; loop (4 * 1024 * 1024)) /* > 4MB? */ { unlink(LF ".9"); rename(LF ".8", LF ".9"); rename(LF ".7", LF ".8"); rename(LF ".6", LF ".7"); rename(LF ".5", LF ".6"); rename(LF ".4", LF ".5"); rename(LF ".3", LF ".4"); rename(LF ".2", LF ".3"); rename(LF ".1", LF ".2"); rename(LF ".0", LF ".1"); rename(LF, LF ".0"); } } errno = e; } #else void LOG(char *s, ...) { } #endif int hextoint(const char *in, int n) { int value = 0, index = 0; for(index=0; index= '0' && c <= '9') value += c - '0'; else value += c - 'A' + 10; } return value; } int add_poll(struct pollfd **pfd, int *n_fd, int fd, int events) { int out = -1; *pfd = (struct pollfd *)realloc(*pfd, (*n_fd + 1) * sizeof(struct pollfd)); memset(&(*pfd)[*n_fd], 0x00, sizeof(struct pollfd)); (*pfd)[*n_fd].fd = fd; out = *n_fd; (*pfd)[*n_fd].events = events; (*n_fd)++; return out; } int mkpath(const char* file_path_in, mode_t mode) { char *file_path = strdup(file_path_in); char *p = NULL; for(p=strchr(file_path + 1, '/'); p; p=strchr(p + 1, '/')) { *p = '\0'; if (mkdir(file_path, mode) == -1) { if (errno != EEXIST) { free(file_path); return -1; } } *p = '/'; } free(file_path); return 0; } int strpos(const char *str, char what) { const char *p = strchr(str, what); if (!p) return -1; return (int)(p - str); } const char *explode_path(const char *in) { const char *result = NULL; wordexp_t p; if (wordexp(in, &p, 0) != 0) return NULL; if (p.we_wordv[0]) result = strdup(p.we_wordv[0]); wordfree(&p); return result; } void terminate_str(char *str, char what) { char *dummy = strchr(str, what); if (dummy) *dummy = 0x00; } void terminate_str_r(char *str, char what) { char *dummy = strrchr(str, what); if (dummy) *dummy = 0x00; } void run(const char *what, const char *pars[]) { pid_t pid = fork(); if (pid == 0) { char * const *env = { NULL }; int fd = open("/dev/null", O_RDWR); close(0); dup(fd); close(1); dup(fd); close(2); dup(fd); execve(what, (char * const *)pars, env); exit(0); } } fi-1.36/Makefile0000644000175000017500000000450212302365027013400 0ustar folkertfolkert# GPLv2 applies # SVN revision: $Revision: 865 $ # (C) 2006-2014 by folkert@vanheusden.com VERSION=1.36 DESTDIR=/usr/local SYSCONFDIR=/etc DEBUG=-O3 -g LDFLAGS+=-lpanelw -lncursesw -pthread -lm -rdynamic CFLAGS+=-DVERSION=\"$(VERSION)\" -DSYSCONFDIR=\"$(DESTDIR)$(SYSCONFDIR)\" $(DEBUG) -Wall -pedantic FILES=*.c *.h firc.* f-irc.1 Makefile readme.txt license.txt faq.txt changelog.gz DOCS=firc.conf changelog.gz readme.txt faq.txt PACKAGE=fi-$(VERSION).tgz OBJS=string_array.o main.o theme.o buffer.o channels.o error.o utils.o loop.o term.o tcp.o irc.o user.o names.o config.o dcc.o utf8.o key_value.o wordcloud.o grep_filter.o nickcolor.o chistory.o autocomplete.o checkmail.o servers.o colors.o ansi.o soundex.o ignores.o dictionary.o lf_buffer.o ctcp.o headlines.o help.o scrollback.o all: f-irc f-irc: $(OBJS) $(CC) $(DEBUG) $(OBJS) $(LDFLAGS) -o f-irc install-docs-base: mkdir -p $(DESTDIR)$(PREFIX)/share/doc/f-irc cp -a $(DOCS) $(DESTDIR)$(PREFIX)/share/doc/f-irc install-docs-wlicense: install-docs-base cp -a license.txt $(DESTDIR)$(PREFIX)/share/doc/f-irc install-nodocs: f-irc install-gen install-debian: f-irc install-gen install-docs-base install: f-irc install-gen install-docs-wlicense install-gen: mkdir -p $(DESTDIR)$(PREFIX)/bin rm -f $(DESTDIR)$(PREFIX)/bin/fi cp -a f-irc $(DESTDIR)$(PREFIX)/bin/f-irc mkdir -p $(DESTDIR)$(PREFIX)/share/man/man1 cp f-irc.1 $(DESTDIR)$(PREFIX)/share/man/man1 deb: package rm -rf d mkdir -p d/tmp cp -a $(FILES) debian/ d/tmp cp license.txt d/tmp/debian/copyright sed -i 's/^DEBUG=.*/DEBUG=-O3 -g/g' d/tmp/Makefile make package mv $(PACKAGE) d/f-irc_$(VERSION).orig.tar.gz cd d/tmp ; debuild -k'Folkert van Heusden ' clean: rm -f $(OBJS) f-irc core log.log gmon.out *.da package: # source package rm -rf fi-$(VERSION)* mkdir fi-$(VERSION) cp $(FILES) fi-$(VERSION) sed -i 's/^DEBUG=.*/DEBUG=-O3 -g/g' fi-$(VERSION)/Makefile tar czf $(PACKAGE) fi-$(VERSION) rm -rf fi-$(VERSION) daily: package scp $(PACKAGE) folkert@vps001.vanheusden.com:www/daily_f-irc/`date +%Y-%m-%d`.tgz cppcheck: cppcheck -v --enable=all --std=c++11 --inconclusive -I. . 2> err.txt make clean scan-build make coverity: clean rm -rf cov-int cov-build --dir cov-int make clean all tar vczf ~/site/coverity/f-irc.tgz README cov-int/ putsite -q /home/folkert/.coverity-fi.sh fi-1.36/ignores.c0000644000175000017500000000713412302365027013556 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 727 $ * (C) 2006-2014 by folkert@vanheusden.com */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "error.h" #include "ignores.h" #include "config.h" #include "utils.h" #include "string_array.h" char *ignore_file = NULL; ignore *ignores = NULL; int n_ignores = 0; int find_ignore(const char *channel, const char *nick, const char *name_complete) { int loop; for(loop=0; loop 0) memmove(&ignores[found], &ignores[found + 1], sizeof(ignore) * n_move); n_ignores--; } } BOOL load_ignore_list(const char *file) { int fd = open(file, O_RDONLY); if (fd == -1) return FALSE; ignore_file = strdup(file); for(;;) { char *line = read_line_fd(fd); string_array_t parts; init_string_array(&parts); if (!line) break; if (strlen(line) == 0) { myfree(line); continue; } split_string(line, " ", TRUE, &parts); if (string_array_get_n(&parts) == 2) add_ignore(string_array_get(&parts, 0), string_array_get(&parts, 1), IGNORE_NOT_SET); else if (string_array_get_n(&parts) == 3) add_ignore(string_array_get(&parts, 0), string_array_get(&parts, 1), string_array_get(&parts, 2)); else error_exit(FALSE, "'ignore' (%s) missing parameter: must be 'channel nick [name_complete]'", line); free_splitted_string(&parts); myfree(line); } close(fd); return TRUE; } void free_ignores(void) { int loop = 0; for(loop=0; loop x ) #define THEME_VAR_TYPE_BOOL 1 #define THEME_VAR_TYPE_COLOR 2 #define THEME_VAR_TYPE_INT 3 #define THEME_VAR_TYPE_CHAR 4 typedef struct { char *keyword; char *description; int type; /* THEME_VAR_TYPE_... */ long int offset; } theme_parsing; extern layout_theme theme; extern const char *theme_file; void load_theme(const char *file); BOOL parse_false_true(const char *value_in, const char *subject, int line); fi-1.36/main.h0000644000175000017500000000107212302365027013034 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 746 $ * (C) 2006-2014 by folkert@vanheusden.com */ #ifndef __MAIN_H__ #define __MAIN_H__ #include "channels.h" #include "servers.h" extern unsigned int ul_x; extern volatile BOOL terminal_changed; extern time_t started_at; extern string_array_t extra_highlights; void reposition_editline_cursor(void); void exit_fi(void); void do_resize(int s); server *cur_server(void); channel *cur_channel(void); void set_cursor_mode(cursor_mode_t cm); cursor_mode_t get_cursor_mode(void); time_t get_cursor_mode_since(void); #endif fi-1.36/f-irc.10000644000175000017500000002704712302365027013033 0ustar folkertfolkert.\" Copyright Folkert van Heusden, 2006-2014 .\" .\" This file may be copied under the conditions described .\" in the GNU GENERAL PUBLIC LICENSE, Version 1, September 1998 .\" that should have been distributed together with this file. .\" .TH F-IRC 1 2014-11 "f-irc" .SH NAME f-irc - a user friendly irc client for a terminal/command-line/console .SH SYNOPSIS .BI "f-irc [" configurationfile.conf "] .sp options: .BI "configurationfile.conf" .sp .SH DESCRIPTION The program .B f-irc Is an irc client for a terminal/command-line/console. Its goal is to be as user friendly as possible with easy navigation and keyboard shortcuts for quick navigation. The learning curve should be as shallow as possible. .SH CONFIGURATION .TP Create .B ~/.firc or .B firc.conf. ~/.firc will be automatically found at start, else enter it on the command-line: f-irc my-fi-config.conf .TP .B server=host:port An IRC server to connect to. .TP .B auto_join=... Channel to automatically join. .TP .B send_after_login=... Command to send to the IRC server after connection setup. E.g. PRIVMSG #NickServ IDENTIFY my_password .TP .B username=... password=... Used for authentication against IRC server .TP .B nick=... name=... Identification settings. .TP .B description=... Will be shown instead of server name (if used). .TP .B part_message=... Message to send to the channel when leaving a channel. .TP .B server_exit_message=... Message to send to all channels when leaving a server. .TP .B allow_invite=true/false Should f-irc automatically open a channel when it receives an INVITE message? .TP .B ignore_mouse=true/false Wether to ignore mouse clicks(!). Default is true which makes f-irc ignore the mouse. .TP .B favorite=... ... This defines a favorite channel. First parameter is the server hostname or description, the second parameter the channel name (e.g. #linux.nl). Jump through your favorite channels using ^Q. .TP .B store_config_on_exit=true/false Wether to update the configuration-file at exit. Useful when adding channels and/or servers or changing nicks. .TP .B notice_in_serverchannel=true/false Most (all?) "NOTICE" messages are not personal messages but are from e.g. chanserv and nickserv and friends. If this parameter is set to true, no new channel-windows are created for them; they are put in the server-channel window instead. .TP .B highlight=on/off Wether lines with your nick name in them should be highlighted (green on black). .TP .B meta-colors=true/false Show server-messages and personal messages in a different color than generic messages. .TP .B all-colors=true/false Global color-switch. .TP .B nick-color=true/false When enabled, each line spoken will be colored with the color depending on who said it. .TP .B use_nonbasic_colors=true/false Use more than the 16 standard colors. Useful for "nick-color". .TP .B inverse_window_heading=true/false Show window-heading in reverse (true) or with ASCII-art (false). .TP .B show_parts=true/false Should f-irc show a message when someone leaves a channel? .TP .B show_mode_changes=true/false Should f-irc show a message when there's a mode change? .TP .B show_nick_change=true/false Should f-irc show a message when someone changes his/hers nick? .TP .B show_joins=true/false Should f-irc show a message when someone joins a channel? .TP .B auto_rejoin=true/false Allows you to automatically re-join a channel when you're kicked. .TP .B remember_channels=true/false Store any channels you were in when f-irc terminates. .TP .B grep_filter=server,channel,file,reg-exp If a line written in a server/channel with the specified regular expression, write that line to "file". server and or channel can be omitted but the comma needs to stay. For the server, f\-irc looks at the "description"\-field of a server. .TP .B ignore_file=... Points to a file with nicks to ignore: #channel nick etc. .TP .B auto_private_channel=on/off Automatically create a new channel window when using /MSG .TP .B dcc_path=... Path where to store files received via DCC. .TP .B max_channel_record_lines=... How many lines of text to store in the scroll-back buffer. .TP .B partial_highlight_match=yes/no Draw the whole line in green or only the parts that match the search term. .TP .B word_cloud_n=... How many items to put in the word-cloud window. Set to 0 to completely disable the word-cloud. .TP .B word_cloud_win_height=... Height of the word-cloud window. This excludes the 2 rows for the border. .TP .B word_cloud_refresh=... Interval in which to restart the word-cloud. Value is in seconds. .TP .B word_cloud_min_word_size=... Minimum size (in characters) of the words evaluated. A sensible value is 4. .TP .B default_colorpair=...,... Set the default color pair. Used for generic texts and borders. First is foreground, second is background. Note that you can either use a color name ("red", "green", etc.) or a hexadecimal color triplet with values 000...3e7 (that may sound strange but ncurses use the range 0...999) with a '#' in front of it. E.g. #1230fe2fa,white .TP .B highlight_colorpair=...,... Color pair used when your nick is mentioned. .TP .B meta_colorpair=...,... Color pair used when server messages are shown (joins, parts, etc) .TP .B error_colorpair=...,... Colors to use when there's an error to show .TP .B jumpy_navigation=true/false ^W/R/Z/X: jumpy navigation .TP .B temp_colorpair=...,... Temporary color; .TP .B dictionary_file=... Dictionary file to use for tab completion, see below. .TP .B log_dir= Path where to store chats logging. .TP .B mark_personal_messages=yes/no If someone send you a message in a personal channel ("query"), should f-irc then highlight that channel as if a personal message was sent (default is no). .SH COLORS Available colors: black, blue, cyan, default, green, magenta, red, white, yellow. .SH DICTIONARY F-irc can, apart from channel/nick/command also do tab-completion on a configurable list of words. With "dictionary_file=..." you can select a text-file with 2 or more columns. In the first column you put the word that f-irc needs to look for when pressing tab. Then when there's a match, that word will be replaced by what's in the 2nd (and 3d, etc.) column. .SH USAGE When F-IRC is started, you'll see a list of channels and servers on the right. On the left a large box with what is written by people. The line at the top is the topic of that channel and the line at the bottom is where you write the things you would like to appear in the channels. .TP If there's a reverse color border around the channel-list, then it is selected. That means that you can navigate through it with the cursor keys. Press right to enter a channel. Press right again on that channel to get a list of names in that channel. Press right once again to get a list of actions to perform on that user. Press left to exit the actions menu. Another left to return to the channel-list. Use up cursor/down cursor keys to navigate through the list. 'home' jumps to the top of the list, 'end' to the bottom. .TP Press left on a server to "fold it": this hides the channels. Press the right cursor key to unfold it. When a server is not folded and it is already selected, press "right" again to get a menu with server-options: reconnect a server for example and disconnect it. .TP In the "scroll back"/"search" windows you can press '/' to search in that window and 'w' to write the contents of a window to a file. .TP .B ^X means: press ctrl key and press x while still holding ctrl. .TP If the channel/name-list is selected, you can enter ^N: you can then move the cursor from right to left and back to navigate through the sentence you're typing. If the edit-line is selected, then the border around the names/channels is no longer reverse, also the '>' left from- and the '>' right from the edit line are in reverse video. .TP If the edit-line is selected, you can press up cursor key and down left cursor key to browse through older lines you wrote. Also "home" and "end" work like they should. .TP .B ^A moves the cursor to the left, .B ^E moves the cursor to the right .B ^D deletes the character under the cursor. This is like how the bash-shell behaves. .TP .B ^U Clear input line. Press second time to undo clear. .TP If there's something new in a channel, a '*' is put in front of the server or channel name. .TP .B ^Q jump to your next favorite channel (see the "favorite=... ..." configuration file parameter). .TP .B ^W lets you jump to the next channel in which new lines were written. .TP .B ^R like ^W but searching backwards .TP .B ^Z lets you jump to the next channel in which your (nick-)name was mentioned .TP .B ^X like ^Z but searching backwards .TP .B ^T jump back to the previously selected channel .TP .B ^O lets f-irc remove all the *'s .TP .B ^F lets you scroll-back in what was written by others .TP .B ^B lets you scroll-back in what you wrote. you can then select a line and press enter to copy it to your edit-line .TP .B ^Y Enable/disable "only show channels with new messages". .TP .B ^V Enter 1 character. Used when entering ascii values < 32, e.g. ^B for bold. .TP Press .B TAB for auto-completion of commands (e.g. /TOPIC, /TIME, etc.) and nick names .TP Press .B ^G to close a channel. .TP Press .B ^L to redraw the screen. .TP Press .B ^C to terminate the program. Or enter /EXIT (or /exit, commands are not case sensitive). .TP When you enter: .B @/... then /... is send .br .B @@... then @... is send .br .B @... then the program will look for a channel/server with '...' in its name and then change the channel to that one. you can enter .B ^J to let it jump to the next channel with that .TP .B ^P add a markerline to the current channel .TP .B F2 Store current configuration on disk .TP .B F3 Add a new server .TP .B F4 Switch to edit-line (shortcut for ^N) .TP .B F5 Redraw screen .TP .B F6 Search in all windows .TP .B F7 Close all channels with only "NOTICE" messages. Those are most oftehn the channels with messages that come from bots and servers. .TP .B F8 Configure f-irc .TP .B F9 Undo the close of the last closed channel .TP .B F10 ^N Toggle between channel-list, edit-line and word-cloud (if enabled) .TP .B F12 Add markerline to all channels on all servers .SH USAGE Not only messages but also commands can be entered in the line at the bottom. Above @... was mentioned and also the TAB-completion was mentioned. Apart from that also a couple of commands weere implemented: .B /ADDSERVER Adds a server .TP .B /AWAY Set yout away status. .TP .B /BAN Ban a user from the current channel. .TP .B /CTCP Send a CTCP command. E.g. /CTCP flok VERSION .TP .B /DCCSEND Send a file to a user using DCC. .TP .B /EXIT Terminate the program. .TP .B /IGNORE Ignore a user. .TP .B /INFO This asks info of an IRC server. .TP .B /INVITE Invite a user to a channel. .TP .B /JOIN Enter a channel. .TP .B /KICK Kick a user from a channel .TP .B /LEAVE Leave a channel. Short-cut is ^G. .TP .B /LINKS Lists servers to which this irc server is connected. .TP .B /LIST Ask server for a list of channels. .TP .B /MODE Sets a mode on a user. .TP .B /MSG Send a private message to a user. .TP .B /NICK Set your nick for that server. .TP .B /NOTICE Send a notice to a user. .TP .B /PING Ping a user. .TP .B /SAVECONFIG Store current configuration on disk. .TP .B /SEARCHALL Search for a text in all channels and servers. .TP .B /SPAM Send a private message to everybody in a channel. People don't like it and you will probably be banned. .TP .B /TIME Ask an IRC server for its notion of time. .TP .B /TOPIC Set the topic of a channel .TP .B /UNIGNORE Un-ignore a user. .TP .B /VERSION Ask parameters of a server. .TP .B /WHOIS x Asks the server who 'x' is. .SH SEE ALSO .BR http://www.vanheusden.com/fi/ .SH MISC F-IRC was written by folkert@vanheusden.com .PP Please be nice too other people. fi-1.36/autocomplete.c0000644000175000017500000001055412302365027014611 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 716 $ * (C) 2006-2014 by folkert@vanheusden.com */ #define _GNU_SOURCE #include #include #include #include "gen.h" #include "autocomplete.h" #include "utils.h" #include "term.h" #include "buffer.h" #include "channels.h" #include "servers.h" #include "loop.h" #include "main.h" tab_completion_t tab_completion[] = { { "/ADDSERVER" }, { "/ADMIN" }, { "/AWAY" }, { "/BAN" }, { "/CTCP" }, { "/CONNECT" }, { "/DCCSEND" }, { "/EXIT" }, { "/IGNORE" }, { "/INFO" }, { "/INVITE" }, { "/ISON" }, { "/JOIN" }, { "/KEEPTOPIC" }, { "/KICK" }, { "/KILL" }, { "/LEAVE" }, { "/LINKS" }, { "/LIST" }, { "/MODE" }, { "/MSG" }, { "/NAMES" }, { "/NICK" }, { "/NOTICE" }, { "/OPER" }, { "/PART" }, { "/PASS" }, { "/PING" }, { "/PRIVMSG" }, { "/QUIT" }, { "/QUOTE" }, { "/RAW" }, { "/REHASH" }, { "/RESTART" }, { "/SAVECONFIG" }, { "/SEARCHALL" }, { "/SPAM" }, { "/STATS" }, { "/SUMMON" }, { "/TIME" }, { "/TOPIC" }, { "/TRACE" }, { "/UNIGNORE" }, { "/USER" }, { "/USERHOST" }, { "/USERS" }, { "/VERSION" }, { "/WALLOPS" }, { "/WHO" }, { "/WHOIS" }, { "/WHOWAS" }, { NULL } }; char *make_complete_command(const char *in) { int n_elements_equal = 0; char *matching_str = NULL, *dummy = NULL; int matching_str_len = 32767; int index = 0; int in_len = strlen(in); int matching_index = -1; char str_buffer[4096] = { 0 }; unsigned int nb = 0; while(tab_completion[index].what) { if (strncasecmp(tab_completion[index].what, in, in_len) == 0) { if (matching_str) { int loop; int cur_len = strlen(tab_completion[index].what); for(loop=in_len; loop= sizeof str_buffer - 1) break; } index++; } if (n_elements_equal > 1) update_statusline(current_server, current_server_channel_nr, "Matching: %s", str_buffer); if (matching_index != -1) { asprintf(&dummy, "%s ", matching_str); myfree(matching_str); matching_str = dummy; } return matching_str; } char *make_complete_nickorchannel(char *in, int start_of_line) { int n_elements_equal = 0; char *matching_str = NULL, *dummy = NULL; int matching_str_len = 32767; int index = 0; int in_len = strlen(in); int matching_index = -1; int cur_n_channels = cur_server() -> n_channels; int cur_n_names = cur_channel() -> n_names; int matching_type = -1; char str_buffer[4096] = { 0 }; unsigned int nb = 0; for(index=0; index<(cur_n_channels + cur_n_names); index++) { char *compare_str; int type; if (index >= cur_n_channels) { char *dummy = cur_channel() -> persons[index - cur_n_channels].nick; if (dummy[0] == '@') compare_str = &dummy[1]; else compare_str = dummy; type = 1; /* nick */ } else { compare_str = cur_server() -> pchannels[index].channel_name; type = 0; /* channel */ } if (strncasecmp(compare_str, in, in_len) == 0) { if (matching_str) { int loop; int cur_len = strlen(compare_str); for(loop=in_len; loop= sizeof str_buffer - 1) break; } } if (n_elements_equal > 1) update_statusline(current_server, current_server_channel_nr, "Matching: %s", str_buffer); if (matching_index != -1) { if (matching_type == 1 && start_of_line) asprintf(&dummy, "%s: ", matching_str); else asprintf(&dummy, "%s ", matching_str); myfree(matching_str); matching_str = dummy; } return matching_str; } fi-1.36/theme.c0000644000175000017500000002374112302365027013214 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 671 $ * (C) 2006-2014 by folkert@vanheusden.com */ #include #include #include #include #include #include #include #include #include #include "gen.h" #include "error.h" #include "theme.h" #include "utils.h" layout_theme theme; const char *theme_file = NULL; theme_parsing theme_parser[] = { /* channel list */ { "color_channellist_default", "color for channellist box", THEME_VAR_TYPE_COLOR, THEME_STRUCT_OFFSET(color_channellist_default) }, { "color_channellist_minimized_server", "color for a minimized server", THEME_VAR_TYPE_COLOR, THEME_STRUCT_OFFSET(color_channellist_minimized_server) }, { "color_channellist_newlines", "color for a channel with new messages", THEME_VAR_TYPE_COLOR, THEME_STRUCT_OFFSET(color_channellist_newlines) }, { "channellist_newlines_markchar", "character to place before channels with new messages", THEME_VAR_TYPE_CHAR, THEME_STRUCT_OFFSET(channellist_newlines_markchar) }, { "channellist_window_width", "width of the channellist window", THEME_VAR_TYPE_INT, THEME_STRUCT_OFFSET(channellist_window_width) }, { "channellist_border", "wether to draw a border around the channellist or not", THEME_VAR_TYPE_BOOL, THEME_STRUCT_OFFSET(channellist_border) }, { "channellist_border_color", "color of the channellist border", THEME_VAR_TYPE_COLOR, THEME_STRUCT_OFFSET(channellist_border_color) }, { "channellist_border_left_side", "channellist window layout", THEME_VAR_TYPE_CHAR, THEME_STRUCT_OFFSET(channellist_border_left_side) }, { "channellist_border_right_side", "channellist window layout", THEME_VAR_TYPE_CHAR, THEME_STRUCT_OFFSET(channellist_border_right_side) }, { "channellist_border_top_side", "channellist window layout", THEME_VAR_TYPE_CHAR, THEME_STRUCT_OFFSET(channellist_border_top_side) }, { "channellist_border_bottom_side", "channellist window layout", THEME_VAR_TYPE_CHAR, THEME_STRUCT_OFFSET(channellist_border_bottom_side) }, { "channellist_border_top_left_hand_corner", "channellist window layout", THEME_VAR_TYPE_CHAR, THEME_STRUCT_OFFSET(channellist_border_top_left_hand_corner) }, { "channellist_border_top_right_hand_corner", "channellist window layout", THEME_VAR_TYPE_CHAR, THEME_STRUCT_OFFSET(channellist_border_top_right_hand_corner) }, { "channellist_border_bottom_left_hand_corner", "channellist window layout", THEME_VAR_TYPE_CHAR, THEME_STRUCT_OFFSET(channellist_border_bottom_left_hand_corner) }, { "channellist_border_bottom_right_hand_corner", "channellist window layout", THEME_VAR_TYPE_CHAR, THEME_STRUCT_OFFSET(channellist_border_bottom_right_hand_corner) }, { "start_in_channellist_window", "wether to start (with the cursor) in the channel window", THEME_VAR_TYPE_BOOL, THEME_STRUCT_OFFSET(start_in_channellist_window) }, /* chat window */ { "show_time", "show time when a message was received?", THEME_VAR_TYPE_BOOL, THEME_STRUCT_OFFSET(show_time) }, { "show_date_when_changed", "show date when it has changed since the last message when a message was received?", THEME_VAR_TYPE_BOOL, THEME_STRUCT_OFFSET(show_date_when_changed) }, { "chat_window_border", "wether to draw a border around the chatwindow or not", THEME_VAR_TYPE_BOOL, THEME_STRUCT_OFFSET(chat_window_border) }, { "chat_window_border_color", "color of the chatwindow border", THEME_VAR_TYPE_COLOR, THEME_STRUCT_OFFSET(chat_window_border_color) }, { "chat_window_border_left_side", "chat window layout", THEME_VAR_TYPE_CHAR, THEME_STRUCT_OFFSET(chat_window_border_left_side) }, { "chat_window_border_right_side", "chat window layout", THEME_VAR_TYPE_CHAR, THEME_STRUCT_OFFSET(chat_window_border_right_side) }, { "chat_window_border_top_side", "chat window layout", THEME_VAR_TYPE_CHAR, THEME_STRUCT_OFFSET(chat_window_border_top_side) }, { "chat_window_border_bottom_side", "chat window layout", THEME_VAR_TYPE_CHAR, THEME_STRUCT_OFFSET(chat_window_border_bottom_side) }, { "chat_window_border_top_left_hand_corner", "chat window layout", THEME_VAR_TYPE_CHAR, THEME_STRUCT_OFFSET(chat_window_border_top_left_hand_corner) }, { "chat_window_border_top_right_hand_corner", "chat window layout", THEME_VAR_TYPE_CHAR, THEME_STRUCT_OFFSET(chat_window_border_top_right_hand_corner) }, { "chat_window_border_bottom_left_hand_corner", "chat window layout", THEME_VAR_TYPE_CHAR, THEME_STRUCT_OFFSET(chat_window_border_bottom_left_hand_corner) }, { "chat_window_border_bottom_right_hand_corner", "chat window layout", THEME_VAR_TYPE_CHAR, THEME_STRUCT_OFFSET(chat_window_border_bottom_right_hand_corner) }, /* show clock? */ { "show_clock", "wether to show a clock in the topicline", THEME_VAR_TYPE_BOOL, THEME_STRUCT_OFFSET(show_clock) }, { NULL, NULL, 0, -1 } }; void load_theme(const char *file) { int self_defined = 0; int loop; color *clist[THEME_N_COLOR_STRUCTS]; int n_clist = 0; int linenr = 0; int fd = open(file, O_RDONLY); if (fd == -1) error_exit(TRUE, "Cannot open themefile %s", file); theme_file = strdup(file); for(;;) { int theme_index = 0; char *line = read_line_fd(fd); char *cmd = NULL, *par = NULL; char *is = NULL; if (!line) break; linenr++; if (strlen(line) == 0) { myfree(line); continue; } if (line[0] == '#' || line[0] == ';') { myfree(line); continue; } is = strchr(line, '='); if (!is) error_exit(FALSE, "themeconfig: line %d is missing either command or parameter! (%s)", linenr, line); /* find parameter */ par = is + 1; while(*par == ' ') par++; /* remove spaces around command */ /* spaces at the start */ cmd = line; while(*cmd == ' ') cmd++; /* spaces at the end */ *is = 0x00; is--; while(*is == ' ') { *is = 0x00; is--; } while(theme_parser[theme_index].keyword) { if (strcasecmp(theme_parser[theme_index].keyword, cmd) == 0) break; theme_index++; } if (theme_parser[theme_index].keyword) { void *dummy_theme_pnt = (void *)&(((char *)&theme)[theme_parser[theme_index].offset]); BOOL *boolpnt; color *colorpnt; int *intpnt; char *charpnt; char *komma; switch(theme_parser[theme_index].type) { case THEME_VAR_TYPE_BOOL: boolpnt = (BOOL *)dummy_theme_pnt; if (parse_false_true(par, cmd, linenr)) *boolpnt = TRUE; else *boolpnt = FALSE; break; case THEME_VAR_TYPE_COLOR: colorpnt = (color *)dummy_theme_pnt; memset(colorpnt, 0x00, sizeof(color)); komma = strchr(par, ','); if (!komma && isdigit(par[0])) { colorpnt -> mode = COLOR_MODE_INDEX; colorpnt -> index = atoi(par); } else if (!isdigit(par[0])) { colorpnt -> mode = COLOR_MODE_INDEX; if (strcasecmp(par, "BLACK") == 0) colorpnt -> index = COLOR_BLACK; else if (strcasecmp(par, "RED") == 0) colorpnt -> index = COLOR_RED; else if (strcasecmp(par, "GREEN") == 0) colorpnt -> index = COLOR_GREEN; else if (strcasecmp(par, "YELLOW") == 0) colorpnt -> index = COLOR_YELLOW; else if (strcasecmp(par, "BLUE") == 0) colorpnt -> index = COLOR_BLUE; else if (strcasecmp(par, "MAGENTA") == 0) colorpnt -> index = COLOR_MAGENTA; else if (strcasecmp(par, "CYAN") == 0) colorpnt -> index = COLOR_CYAN; else if (strcasecmp(par, "WHITE") == 0) colorpnt -> index = COLOR_WHITE; else error_exit(FALSE, "line %d: %s is an unknown color\n", linenr, par); } else { colorpnt -> mode = COLOR_MODE_RGB; colorpnt -> r = atoi(par); colorpnt -> g = atoi(komma + 1); komma = strchr(komma + 1, ','); if (!komma) error_exit(FALSE, "line %d: '%s' is an invalid color specification", linenr, theme_parser[theme_index].keyword); colorpnt -> b = atoi(komma + 1); clist[n_clist++] = colorpnt; } break; case THEME_VAR_TYPE_INT : intpnt = (int *)dummy_theme_pnt; *intpnt = atoi(par); break; case THEME_VAR_TYPE_CHAR: charpnt = (char *)dummy_theme_pnt; if (*par == 0x00) *charpnt = ' '; else *charpnt = *par; break; } } else { error_exit(FALSE, "line %d: '%s=%s' is not understood\n", linenr, cmd, par); } myfree(line); } close(fd); /* convert rgb values to indexes (if any) */ for(loop=0; loop mode = COLOR_MODE_INDEX; /* fist see if this color is one of the 8 predefined colors or the * ones just created */ for(cloop=0; cloop<(8 + self_defined); cloop++) { short r, g, b; int diff; color_content(cloop, &r, &g, &b); /* let's not use black */ if (r == 0 && g == 0 && b == 0) continue; diff = abs(clist[loop] -> r - r) + abs(clist[loop] -> g - g) + abs(clist[loop] -> b - b); if (diff < mindiff) { cindex = cloop; mindiff = diff; if (diff == 0) break; } } if (cindex == -1) error_exit(FALSE, "internal error: no colors found at all"); /* found? or no more available? or cannot define any colors at all? */ if (mindiff == 0 || (8 + self_defined) >= COLORS || can_change_color() == FALSE) { clist[loop] -> index = cindex; } else { init_color(8 + self_defined, clist[loop] -> r, clist[loop] -> g, clist[loop] -> b); clist[loop] -> index = 8 + self_defined; self_defined++; } } } BOOL parse_false_true(const char *value_in, const char *subject, int line) { if (strcasecmp(value_in, "YES") == 0 || strcasecmp(value_in, "JA") == 0 || strcasecmp(value_in, "TRUE") == 0 || strcasecmp(value_in, "ON") == 0 || strcasecmp(value_in, "1") == 0) { return TRUE; } else if (strcasecmp(value_in, "NO") == 0 || strcasecmp(value_in, "NEE") == 0 || strcasecmp(value_in, "FALSE") == 0 || strcasecmp(value_in, "OFF") == 0 || strcasecmp(value_in, "0") == 0) { return FALSE; } error_exit(FALSE, "%s requires either 'true' or 'false', not '%s' (line %d)", subject, value_in, line); return FALSE; } fi-1.36/error.c0000644000175000017500000000217312302365027013237 0ustar folkertfolkert/* GPLv2 applies * SVN revision: $Revision: 671 $ * (C) 2006-2014 by folkert@vanheusden.com */ #include #include #include #include #include #include #include #include #if defined(__GLIBC__) #include #endif #include #include "error.h" void error_exit(BOOL sys_err, char *format, ...) { int e = errno; va_list ap; #if defined(__GLIBC__) int index; void *trace[128]; int trace_size = backtrace(trace, 128); char **messages = backtrace_symbols(trace, trace_size); #endif (void)endwin(); va_start(ap, format); (void)vfprintf(stderr, format, ap); va_end(ap); if (sys_err == TRUE) fprintf(stderr, "error: %s (%d)\n", strerror(e), e); fflush(NULL); #if defined(__GLIBC__) fprintf(stderr, "Execution path:\n"); for(index=0; index