diff options
Diffstat (limited to 'src/core')
-rw-r--r-- | src/core/Makefile.am | 3 | ||||
-rw-r--r-- | src/core/channels.c | 31 | ||||
-rw-r--r-- | src/core/channels.h | 1 | ||||
-rw-r--r-- | src/core/chat-commands.c | 11 | ||||
-rw-r--r-- | src/core/commands.h | 3 | ||||
-rw-r--r-- | src/core/ignore.c | 2 | ||||
-rw-r--r-- | src/core/log.c | 2 | ||||
-rw-r--r-- | src/core/misc.c | 143 | ||||
-rw-r--r-- | src/core/misc.h | 14 | ||||
-rw-r--r-- | src/core/network.c | 124 | ||||
-rw-r--r-- | src/core/network.h | 12 | ||||
-rw-r--r-- | src/core/rawlog.c | 7 | ||||
-rw-r--r-- | src/core/servers-reconnect.c | 1 | ||||
-rw-r--r-- | src/core/servers-setup.c | 4 | ||||
-rw-r--r-- | src/core/servers.c | 16 | ||||
-rw-r--r-- | src/core/settings.c | 95 | ||||
-rw-r--r-- | src/core/settings.h | 14 | ||||
-rw-r--r-- | src/core/special-vars.c | 23 | ||||
-rw-r--r-- | src/core/special-vars.h | 7 | ||||
-rw-r--r-- | src/core/utf8.c | 135 | ||||
-rw-r--r-- | src/core/utf8.h | 56 | ||||
-rw-r--r-- | src/core/wcwidth.c | 220 |
22 files changed, 627 insertions, 297 deletions
diff --git a/src/core/Makefile.am b/src/core/Makefile.am index fc32e17e..af323234 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -44,6 +44,8 @@ libcore_a_SOURCES = \ settings.c \ signals.c \ special-vars.c \ + utf8.c \ + wcwidth.c \ write-buffer.c structure_headers = \ @@ -93,6 +95,7 @@ pkginc_core_HEADERS = \ settings.h \ signals.h \ special-vars.h \ + utf8.h \ window-item-def.h \ write-buffer.h \ $(structure_headers) diff --git a/src/core/channels.c b/src/core/channels.c index 9af8b844..9c3b92ba 100644 --- a/src/core/channels.c +++ b/src/core/channels.c @@ -233,23 +233,46 @@ static int match_nick_flags(SERVER_REC *server, NICK_REC *nick, char flag) void channel_send_autocommands(CHANNEL_REC *channel) { CHANNEL_SETUP_REC *rec; - NICK_REC *nick; - char **bots, **bot; g_return_if_fail(IS_CHANNEL(channel)); if (channel->session_rejoin) - return; + return; rec = channel_setup_find(channel->name, channel->server->connrec->chatnet); if (rec == NULL || rec->autosendcmd == NULL || !*rec->autosendcmd) return; + /* if the autosendcmd alone (with no -bots parameter) has been + * specified then send it right after joining the channel, when + * the WHO list hasn't been yet retrieved. + * Depending on the value of the 'channel_max_who_sync' option + * the WHO list might not be retrieved after the join event. */ + if (rec->botmasks == NULL || !*rec->botmasks) { /* just send the command. */ eval_special_string(rec->autosendcmd, "", channel->server, channel); - return; } +} + +void channel_send_botcommands(CHANNEL_REC *channel) +{ + CHANNEL_SETUP_REC *rec; + NICK_REC *nick; + char **bots, **bot; + + g_return_if_fail(IS_CHANNEL(channel)); + + if (channel->session_rejoin) + return; + + rec = channel_setup_find(channel->name, channel->server->connrec->chatnet); + if (rec == NULL || rec->autosendcmd == NULL || !*rec->autosendcmd) + return; + + /* this case has already been handled by channel_send_autocommands */ + if (rec->botmasks == NULL || !*rec->botmasks) + return; /* find first available bot.. */ bots = g_strsplit(rec->botmasks, " ", -1); diff --git a/src/core/channels.h b/src/core/channels.h index 0839d69b..bd136fe2 100644 --- a/src/core/channels.h +++ b/src/core/channels.h @@ -31,6 +31,7 @@ void channel_change_visible_name(CHANNEL_REC *channel, const char *name); /* Send the auto send command to channel */ void channel_send_autocommands(CHANNEL_REC *channel); +void channel_send_botcommands(CHANNEL_REC *channel); void channels_init(void); void channels_deinit(void); diff --git a/src/core/chat-commands.c b/src/core/chat-commands.c index 8e881679..a9404fa3 100644 --- a/src/core/chat-commands.c +++ b/src/core/chat-commands.c @@ -73,6 +73,13 @@ static SERVER_CONNECT_REC *get_server_connect(const char *data, int *plus_addr, conn = server_create_conn(proto != NULL ? proto->id : -1, addr, atoi(portstr), chatnet, password, nick); + if (conn == NULL) { + signal_emit("error command", 1, + GINT_TO_POINTER(CMDERR_NO_SERVER_DEFINED)); + cmd_params_free(free_arg); + return NULL; + } + if (proto == NULL) proto = chat_protocol_find_id(conn->chat_type); @@ -117,8 +124,8 @@ static SERVER_CONNECT_REC *get_server_connect(const char *data, int *plus_addr, if (g_hash_table_lookup(optlist, "!") != NULL) conn->no_autojoin_channels = TRUE; - if (g_hash_table_lookup(optlist, "noautosendcmd") != NULL) - conn->no_autosendcmd = TRUE; + if (g_hash_table_lookup(optlist, "noautosendcmd") != NULL) + conn->no_autosendcmd = TRUE; if (g_hash_table_lookup(optlist, "noproxy") != NULL) g_free_and_null(conn->proxy); diff --git a/src/core/commands.h b/src/core/commands.h index 72105ad3..93d6276b 100644 --- a/src/core/commands.h +++ b/src/core/commands.h @@ -41,7 +41,8 @@ enum { CMDERR_INVALID_TIME, /* invalid time specification */ CMDERR_INVALID_CHARSET, /* invalid charset specification */ CMDERR_EVAL_MAX_RECURSE, /* eval hit recursion limit */ - CMDERR_PROGRAM_NOT_FOUND /* program not found */ + CMDERR_PROGRAM_NOT_FOUND, /* program not found */ + CMDERR_NO_SERVER_DEFINED, /* no server has been defined for a given chatnet */ }; /* Return the full command for `alias' */ diff --git a/src/core/ignore.c b/src/core/ignore.c index 8d5a27c2..2047dc9d 100644 --- a/src/core/ignore.c +++ b/src/core/ignore.c @@ -241,7 +241,7 @@ IGNORE_REC *ignore_find_full(const char *servertag, const char *mask, const char if (channels == NULL || rec->channels == NULL) continue; /* other doesn't have channels */ - if (strarray_length(channels) != strarray_length(rec->channels)) + if (g_strv_length(channels) != g_strv_length(rec->channels)) continue; /* different amount of channels */ /* check that channels match */ diff --git a/src/core/log.c b/src/core/log.c index 8306d2df..6af1effc 100644 --- a/src/core/log.c +++ b/src/core/log.c @@ -114,7 +114,7 @@ int log_start_logging(LOG_REC *log) /* path may contain variables (%time, $vars), make sure the directory is created */ dir = g_path_get_dirname(log->real_fname); - mkpath(dir, log_dir_create_mode); + g_mkdir_with_parents(dir, log_dir_create_mode); g_free(dir); } diff --git a/src/core/misc.c b/src/core/misc.c index aaa470f5..bc9f504e 100644 --- a/src/core/misc.c +++ b/src/core/misc.c @@ -20,6 +20,7 @@ #include "module.h" #include "misc.h" +#include "commands.h" #ifdef HAVE_REGEX_H # include <regex.h> @@ -150,27 +151,13 @@ int find_substr(const char *list, const char *item) return FALSE; } -int strarray_length(char **array) -{ - int len; - - g_return_val_if_fail(array != NULL, 0); - - len = 0; - while (*array) { - len++; - array++; - } - return len; -} - int strarray_find(char **array, const char *item) { char **tmp; int index; - g_return_val_if_fail(array != NULL, 0); - g_return_val_if_fail(item != NULL, 0); + g_return_val_if_fail(array != NULL, -1); + g_return_val_if_fail(item != NULL, -1); index = 0; for (tmp = array; *tmp != NULL; tmp++, index++) { @@ -279,14 +266,23 @@ void hash_save_key(char *key, void *value, GSList **list) *list = g_slist_append(*list, key); } -/* save all keys in hash table to linked list - you shouldn't remove any - items while using this list, use g_slist_free() after you're done with it */ -GSList *hashtable_get_keys(GHashTable *hash) +/* remove all the options from the optlist hash table that are valid for the + * command cmd */ +GList *optlist_remove_known(const char *cmd, GHashTable *optlist) { - GSList *list; + GList *list, *tmp, *next; + + list = g_hash_table_get_keys(optlist); + if (cmd != NULL && list != NULL) { + for (tmp = list; tmp != NULL; tmp = next) { + char *option = tmp->data; + next = tmp->next; + + if (command_have_option(cmd, option)) + list = g_list_remove(list, option); + } + } - list = NULL; - g_hash_table_foreach(hash, (GHFunc) hash_save_key, &list); return list; } @@ -389,62 +385,6 @@ char *stristr_full(const char *data, const char *key) return strstr_full_case(data, key, TRUE); } -int regexp_match(const char *str, const char *regexp) -{ -#ifdef HAVE_REGEX_H - regex_t preg; - int ret; - - if (regcomp(&preg, regexp, REG_EXTENDED|REG_ICASE|REG_NOSUB) != 0) - return 0; - - ret = regexec(&preg, str, 0, NULL, 0); - regfree(&preg); - - return ret == 0; -#else - return FALSE; -#endif -} - -/* Create the directory and all it's parent directories */ -int mkpath(const char *path, int mode) -{ - struct stat statbuf; - const char *p; - char *dir; - - g_return_val_if_fail(path != NULL, -1); - - p = g_path_skip_root((char *) path); - if (p == NULL) { - /* not a full path, maybe not what we wanted - but continue anyway.. */ - p = path; - } - for (;;) { - if (*p != G_DIR_SEPARATOR && *p != '\0') { - p++; - continue; - } - - dir = g_strndup(path, (int) (p-path)); - if (stat(dir, &statbuf) != 0) { - if (mkdir(dir, mode) == -1) - { - g_free(dir); - return -1; - } - } - g_free(dir); - - if (*p++ == '\0') - break; - } - - return 0; -} - /* convert ~/ to $HOME */ char *convert_home(const char *path) { @@ -471,22 +411,15 @@ int g_istr_cmp(gconstpointer v, gconstpointer v2) return g_ascii_strcasecmp((const char *) v, (const char *) v2); } -/* a char* hash function from ASU */ -unsigned int g_istr_hash(gconstpointer v) +guint g_istr_hash(gconstpointer v) { - const char *s = (const char *) v; - unsigned int h = 0, g; + const signed char *p; + guint32 h = 5381; - while (*s != '\0') { - h = (h << 4) + i_toupper(*s); - if ((g = h & 0xf0000000UL)) { - h = h ^ (g >> 24); - h = h ^ g; - } - s++; - } + for (p = v; *p != '\0'; p++) + h = (h << 5) + h + g_ascii_toupper(*p); - return h /* % M */; + return h; } /* Find `mask' from `data', you can use * and ? wildcards. */ @@ -592,15 +525,11 @@ int dec2octal(int decimal) /* string -> uoff_t */ uoff_t str_to_uofft(const char *str) { - uoff_t ret; - - ret = 0; - while (*str != '\0') { - ret = ret*10 + (*str - '0'); - str++; - } - - return ret; +#ifdef UOFF_T_LONG_LONG + return (uoff_t)strtoull(str, NULL, 10); +#else + return (uoff_t)strtoul(str, NULL, 10); +#endif } /* convert all low-ascii (<32) to ^<A..> combinations */ @@ -811,20 +740,6 @@ char *escape_string(const char *str) return ret; } -int strocpy(char *dest, const char *src, size_t dstsize) -{ - if (dstsize == 0) - return -1; - - while (*src != '\0' && dstsize > 1) { - *dest++ = *src++; - dstsize--; - } - - *dest++ = '\0'; - return *src == '\0' ? 0 : -1; -} - int nearest_power(int num) { int n = 1; diff --git a/src/core/misc.h b/src/core/misc.h index 7e78d3b9..c095e131 100644 --- a/src/core/misc.h +++ b/src/core/misc.h @@ -32,15 +32,8 @@ char *gslistptr_to_string(GSList *list, int offset, const char *delimiter); /* `list' contains char* */ char *gslist_to_string(GSList *list, const char *delimiter); -/* save all keys in hash table to linked list - you shouldn't remove any - items while using this list, use g_slist_free() after you're done with it */ -GSList *hashtable_get_keys(GHashTable *hash); +GList *optlist_remove_known(const char *cmd, GHashTable *optlist); -/* easy way to check if regexp matches */ -int regexp_match(const char *str, const char *regexp); - -/* Create the directory and all it's parent directories */ -int mkpath(const char *path, int mode); /* convert ~/ to $HOME */ char *convert_home(const char *path); @@ -85,9 +78,6 @@ int parse_size(const char *size, int *bytes); Stop when `end_char' is found from string. */ int is_numeric(const char *str, char end_char); -/* Like strlcpy(), but return -1 if buffer was overflown, 0 if not. */ -int strocpy(char *dest, const char *src, size_t dstsize); - /* strstr() with case-ignoring */ char *stristr(const char *data, const char *key); @@ -107,8 +97,6 @@ char *show_lowascii(const char *str); /* replace all `from' chars in string to `to' chars. returns `str' */ char *replace_chars(char *str, char from, char to); -/* return how many items `array' has */ -int strarray_length(char **array); /* return index of `item' in `array' or -1 if not found */ int strarray_find(char **array, const char *item); diff --git a/src/core/network.c b/src/core/network.c index c3ad4e23..3e1b7c70 100644 --- a/src/core/network.c +++ b/src/core/network.c @@ -30,17 +30,11 @@ union sockaddr_union { struct sockaddr sa; struct sockaddr_in sin; -#ifdef HAVE_IPV6 struct sockaddr_in6 sin6; -#endif }; -#ifdef HAVE_IPV6 -# define SIZEOF_SOCKADDR(so) ((so).sa.sa_family == AF_INET6 ? \ +#define SIZEOF_SOCKADDR(so) ((so).sa.sa_family == AF_INET6 ? \ sizeof(so.sin6) : sizeof(so.sin)) -#else -# define SIZEOF_SOCKADDR(so) (sizeof(so.sin)) -#endif GIOChannel *g_io_channel_new(int handle) { @@ -56,7 +50,7 @@ GIOChannel *g_io_channel_new(int handle) IPADDR ip4_any = { AF_INET, -#if defined(HAVE_IPV6) && defined(IN6ADDR_ANY_INIT) +#if defined(IN6ADDR_ANY_INIT) IN6ADDR_ANY_INIT #else { INADDR_ANY } @@ -68,10 +62,8 @@ int net_ip_compare(IPADDR *ip1, IPADDR *ip2) if (ip1->family != ip2->family) return 0; -#ifdef HAVE_IPV6 if (ip1->family == AF_INET6) return memcmp(&ip1->ip, &ip2->ip, sizeof(ip1->ip)) == 0; -#endif return memcmp(&ip1->ip, &ip2->ip, 4) == 0; } @@ -80,22 +72,16 @@ int net_ip_compare(IPADDR *ip1, IPADDR *ip2) static void sin_set_ip(union sockaddr_union *so, const IPADDR *ip) { if (ip == NULL) { -#ifdef HAVE_IPV6 so->sin6.sin6_family = AF_INET6; so->sin6.sin6_addr = in6addr_any; -#else - so->sin.sin_family = AF_INET; - so->sin.sin_addr.s_addr = INADDR_ANY; -#endif return; } so->sin.sin_family = ip->family; -#ifdef HAVE_IPV6 + if (ip->family == AF_INET6) memcpy(&so->sin6.sin6_addr, &ip->ip, sizeof(ip->ip)); else -#endif memcpy(&so->sin.sin_addr, &ip->ip, 4); } @@ -103,31 +89,25 @@ void sin_get_ip(const union sockaddr_union *so, IPADDR *ip) { ip->family = so->sin.sin_family; -#ifdef HAVE_IPV6 if (ip->family == AF_INET6) memcpy(&ip->ip, &so->sin6.sin6_addr, sizeof(ip->ip)); else -#endif memcpy(&ip->ip, &so->sin.sin_addr, 4); } static void sin_set_port(union sockaddr_union *so, int port) { -#ifdef HAVE_IPV6 if (so->sin.sin_family == AF_INET6) so->sin6.sin6_port = htons((unsigned short)port); else -#endif so->sin.sin_port = htons((unsigned short)port); } static int sin_get_port(union sockaddr_union *so) { -#ifdef HAVE_IPV6 - if (so->sin.sin_family == AF_INET6) - return ntohs(so->sin6.sin6_port); -#endif - return ntohs(so->sin.sin_port); + return ntohs((so->sin.sin_family == AF_INET6) ? + so->sin6.sin6_port : + so->sin.sin_port); } /* Connect to socket */ @@ -272,7 +252,7 @@ GIOChannel *net_listen(IPADDR *my_ip, int *port) /* create the socket */ handle = socket(so.sin.sin_family, SOCK_STREAM, 0); -#ifdef HAVE_IPV6 + if (handle == -1 && (errno == EINVAL || errno == EAFNOSUPPORT)) { /* IPv6 is not supported by OS */ so.sin.sin_family = AF_INET; @@ -280,7 +260,7 @@ GIOChannel *net_listen(IPADDR *my_ip, int *port) handle = socket(AF_INET, SOCK_STREAM, 0); } -#endif + if (handle == -1) return NULL; @@ -399,23 +379,18 @@ int net_getsockname(GIOChannel *handle, IPADDR *addr, int *port) Returns 0 = ok, others = error code for net_gethosterror() */ int net_gethostbyname(const char *addr, IPADDR *ip4, IPADDR *ip6) { -#ifdef HAVE_IPV6 union sockaddr_union *so; struct addrinfo hints, *ai, *ailist; int ret, count_v4, count_v6, use_v4, use_v6; -#else - struct hostent *hp; - int count; -#endif g_return_val_if_fail(addr != NULL, -1); memset(ip4, 0, sizeof(IPADDR)); memset(ip6, 0, sizeof(IPADDR)); -#ifdef HAVE_IPV6 memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_ADDRCONFIG; /* save error to host_error for later use */ ret = getaddrinfo(addr, NULL, &hints, &ailist); @@ -454,85 +429,40 @@ int net_gethostbyname(const char *addr, IPADDR *ip4, IPADDR *ip6) } freeaddrinfo(ailist); return 0; -#else - hp = gethostbyname(addr); - if (hp == NULL) - return h_errno; - - /* count IPs */ - count = 0; - while (hp->h_addr_list[count] != NULL) - count++; - - if (count == 0) - return HOST_NOT_FOUND; /* shouldn't happen? */ - - /* if there are multiple addresses, return random one */ - ip4->family = AF_INET; - memcpy(&ip4->ip, hp->h_addr_list[rand() % count], 4); - - return 0; -#endif } /* Get name for host, *name should be g_free()'d unless it's NULL. Return values are the same as with net_gethostbyname() */ int net_gethostbyaddr(IPADDR *ip, char **name) { -#ifdef HAVE_IPV6 union sockaddr_union so; int host_error; char hostname[NI_MAXHOST]; -#else - struct hostent *hp; -#endif g_return_val_if_fail(ip != NULL, -1); g_return_val_if_fail(name != NULL, -1); *name = NULL; -#ifdef HAVE_IPV6 + memset(&so, 0, sizeof(so)); sin_set_ip(&so, ip); /* save error to host_error for later use */ - host_error = getnameinfo((struct sockaddr *) &so, sizeof(so), - hostname, sizeof(hostname), NULL, 0, 0); + host_error = getnameinfo((struct sockaddr *)&so, sizeof(so), + hostname, sizeof(hostname), + NULL, 0, + NI_NAMEREQD); if (host_error != 0) return host_error; *name = g_strdup(hostname); -#else - if (ip->family != AF_INET) return -1; - hp = gethostbyaddr((const char *) &ip->ip, 4, AF_INET); - if (hp == NULL) return -1; - - *name = g_strdup(hp->h_name); -#endif return 0; } int net_ip2host(IPADDR *ip, char *host) { -#ifdef HAVE_IPV6 - if (!inet_ntop(ip->family, &ip->ip, host, MAX_IP_LEN)) - return -1; -#else - unsigned long ip4; - - if (ip->family != AF_INET) { - strcpy(host, "0.0.0.0"); - } else { - ip4 = ntohl(ip->ip.s_addr); - g_snprintf(host, MAX_IP_LEN, "%lu.%lu.%lu.%lu", - (ip4 & 0xff000000UL) >> 24, - (ip4 & 0x00ff0000) >> 16, - (ip4 & 0x0000ff00) >> 8, - (ip4 & 0x000000ff)); - } -#endif - return 0; + return inet_ntop(ip->family, &ip->ip, host, MAX_IP_LEN) ? 0 : -1; } int net_host2ip(const char *host, IPADDR *ip) @@ -542,12 +472,8 @@ int net_host2ip(const char *host, IPADDR *ip) if (strchr(host, ':') != NULL) { /* IPv6 */ ip->family = AF_INET6; -#ifdef HAVE_IPV6 if (inet_pton(AF_INET6, host, &ip->ip) == 0) return -1; -#else - ip->ip.s_addr = 0; -#endif } else { /* IPv4 */ ip->family = AF_INET; @@ -582,7 +508,6 @@ int net_geterror(GIOChannel *handle) /* get error of net_gethostname() */ const char *net_gethosterror(int error) { -#ifdef HAVE_IPV6 g_return_val_if_fail(error != 0, NULL); if (error == EAI_SYSTEM) { @@ -590,36 +515,17 @@ const char *net_gethosterror(int error) } else { return gai_strerror(error); } -#else - switch (error) { - case HOST_NOT_FOUND: - return "Host not found"; - case NO_ADDRESS: - return "No IP address found for name"; - case NO_RECOVERY: - return "A non-recovable name server error occurred"; - case TRY_AGAIN: - return "A temporary error on an authoritative name server"; - } - - /* unknown error */ - return NULL; -#endif } /* return TRUE if host lookup failed because it didn't exist (ie. not some error with name server) */ int net_hosterror_notfound(int error) { -#ifdef HAVE_IPV6 #ifdef EAI_NODATA /* NODATA is deprecated */ return error != 1 && (error == EAI_NONAME || error == EAI_NODATA); #else return error != 1 && (error == EAI_NONAME); #endif -#else - return error == HOST_NOT_FOUND || error == NO_ADDRESS; -#endif } /* Get name of TCP service */ diff --git a/src/core/network.h b/src/core/network.h index 73ac8dee..03f3b813 100644 --- a/src/core/network.h +++ b/src/core/network.h @@ -21,19 +21,11 @@ struct _IPADDR { unsigned short family; -#ifdef HAVE_IPV6 struct in6_addr ip; -#else - struct in_addr ip; -#endif }; /* maxmimum string length of IP address */ -#ifdef HAVE_IPV6 -# define MAX_IP_LEN INET6_ADDRSTRLEN -#else -# define MAX_IP_LEN 20 -#endif +#define MAX_IP_LEN INET6_ADDRSTRLEN #define IPADDR_IS_V6(ip) ((ip)->family != AF_INET) @@ -45,7 +37,7 @@ GIOChannel *g_io_channel_new(int handle); int net_ip_compare(IPADDR *ip1, IPADDR *ip2); /* Connect to socket */ -GIOChannel *net_connect(const char *addr, int port, IPADDR *my_ip); +GIOChannel *net_connect(const char *addr, int port, IPADDR *my_ip) G_GNUC_DEPRECATED; /* Connect to socket with ip address and SSL*/ GIOChannel *net_connect_ip_ssl(IPADDR *ip, int port, IPADDR *my_ip, SERVER_REC *server); int irssi_ssl_handshake(GIOChannel *handle); diff --git a/src/core/rawlog.c b/src/core/rawlog.c index 875c0ef2..5927e730 100644 --- a/src/core/rawlog.c +++ b/src/core/rawlog.c @@ -150,13 +150,18 @@ void rawlog_save(RAWLOG_REC *rawlog, const char *fname) int f; dir = g_path_get_dirname(fname); - mkpath(dir, log_dir_create_mode); + g_mkdir_with_parents(dir, log_dir_create_mode); g_free(dir); path = convert_home(fname); f = open(path, O_WRONLY | O_APPEND | O_CREAT, log_file_create_mode); g_free(path); + if (f < 0) { + g_warning("rawlog open() failed: %s", strerror(errno)); + return; + } + rawlog_dump(rawlog, f); close(f); } diff --git a/src/core/servers-reconnect.c b/src/core/servers-reconnect.c index f419035b..58c9dd09 100644 --- a/src/core/servers-reconnect.c +++ b/src/core/servers-reconnect.c @@ -190,6 +190,7 @@ server_connect_copy_skeleton(SERVER_CONNECT_REC *src, int connect_info) dest->away_reason = g_strdup(src->away_reason); dest->no_autojoin_channels = src->no_autojoin_channels; dest->no_autosendcmd = src->no_autosendcmd; + dest->unix_socket = src->unix_socket; dest->use_ssl = src->use_ssl; dest->ssl_cert = g_strdup(src->ssl_cert); diff --git a/src/core/servers-setup.c b/src/core/servers-setup.c index 4a048282..0cecfece 100644 --- a/src/core/servers-setup.c +++ b/src/core/servers-setup.c @@ -321,8 +321,8 @@ server_create_conn(int chat_type, const char *dest, int port, chatrec = chatnet_find(dest); if (chatrec != NULL) { rec = create_chatnet_conn(chatrec->name, port, password, nick); - if (rec != NULL) - return rec; + /* If rec is NULL the chatnet has no url to connect to */ + return rec; } chatrec = chatnet == NULL ? NULL : chatnet_find(chatnet); diff --git a/src/core/servers.c b/src/core/servers.c index 3342304e..d6297c4d 100644 --- a/src/core/servers.c +++ b/src/core/servers.c @@ -684,21 +684,11 @@ SERVER_REC *cmd_options_get_server(const char *cmd, SERVER_REC *defserver) { SERVER_REC *server; - GSList *list, *tmp, *next; + GList *list; /* get all the options, then remove the known ones. there should be only one left - the server tag. */ - list = hashtable_get_keys(optlist); - if (cmd != NULL) { - for (tmp = list; tmp != NULL; tmp = next) { - char *option = tmp->data; - next = tmp->next; - - if (command_have_option(cmd, option)) - list = g_slist_remove(list, option); - } - } - + list = optlist_remove_known(cmd, optlist); if (list == NULL) return defserver; @@ -713,7 +703,7 @@ SERVER_REC *cmd_options_get_server(const char *cmd, server = NULL; } - g_slist_free(list); + g_list_free(list); return server; } diff --git a/src/core/settings.c b/src/core/settings.c index 8e493124..e65ceb2c 100644 --- a/src/core/settings.c +++ b/src/core/settings.c @@ -148,11 +148,36 @@ int settings_get_size(const char *key) return str == NULL ? 0 : bytes; } +int settings_get_choice(const char *key) +{ + SETTINGS_REC *rec; + CONFIG_NODE *node; + char *str; + int index; + + rec = settings_get(key, SETTING_TYPE_CHOICE); + if (rec == NULL) return -1; + + node = iconfig_node_traverse("settings", FALSE); + node = node == NULL ? NULL : iconfig_node_section(node, rec->module, -1); + + str = node == NULL ? rec->default_value.v_string : + config_node_get_str(node, key, rec->default_value.v_string); + + if (str == NULL || (index = strarray_find(rec->choices, str)) < 0) + return rec->default_value.v_int; + + return index; +} + char *settings_get_print(SETTINGS_REC *rec) { char *value = NULL; switch(rec->type) { + case SETTING_TYPE_CHOICE: + value = g_strdup(rec->choices[settings_get_choice(rec->key)]); + break; case SETTING_TYPE_BOOLEAN: value = g_strdup(settings_get_bool(rec->key) ? "ON" : "OFF"); break; @@ -172,13 +197,31 @@ char *settings_get_print(SETTINGS_REC *rec) static void settings_add(const char *module, const char *section, const char *key, SettingType type, - const SettingValue *default_value) + const SettingValue *default_value, + const char *choices) { SETTINGS_REC *rec; + char **choices_vec = NULL; g_return_if_fail(key != NULL); g_return_if_fail(section != NULL); + if (type == SETTING_TYPE_CHOICE) { + if (choices == NULL) { + g_warning("Trying to add setting '%s' with no choices.", key); + return; + } + + choices_vec = g_strsplit(choices, ";", -1); + + /* validate the default value */ + if (default_value->v_int < 0 || default_value->v_int >= g_strv_length(choices_vec)) { + g_warning("Trying to add setting '%s' with an invalid default value.", key); + g_strfreev(choices_vec); + return; + } + } + rec = g_hash_table_lookup(settings, key); if (rec != NULL) { /* Already exists, make sure it's correct type */ @@ -197,6 +240,7 @@ static void settings_add(const char *module, const char *section, rec->type = type; rec->default_value = *default_value; + rec->choices = choices_vec; g_hash_table_insert(settings, rec->key, rec); } } @@ -208,7 +252,17 @@ void settings_add_str_module(const char *module, const char *section, memset(&default_value, 0, sizeof(default_value)); default_value.v_string = g_strdup(def); - settings_add(module, section, key, SETTING_TYPE_STRING, &default_value); + settings_add(module, section, key, SETTING_TYPE_STRING, &default_value, NULL); +} + +void settings_add_choice_module(const char *module, const char *section, + const char *key, int def, const char *choices) +{ + SettingValue default_value; + + memset(&default_value, 0, sizeof(default_value)); + default_value.v_int = def; + settings_add(module, section, key, SETTING_TYPE_CHOICE, &default_value, choices); } void settings_add_int_module(const char *module, const char *section, @@ -218,7 +272,7 @@ void settings_add_int_module(const char *module, const char *section, memset(&default_value, 0, sizeof(default_value)); default_value.v_int = def; - settings_add(module, section, key, SETTING_TYPE_INT, &default_value); + settings_add(module, section, key, SETTING_TYPE_INT, &default_value, NULL); } void settings_add_bool_module(const char *module, const char *section, @@ -228,8 +282,7 @@ void settings_add_bool_module(const char *module, const char *section, memset(&default_value, 0, sizeof(default_value)); default_value.v_bool = def; - settings_add(module, section, key, SETTING_TYPE_BOOLEAN, - &default_value); + settings_add(module, section, key, SETTING_TYPE_BOOLEAN, &default_value, NULL); } void settings_add_time_module(const char *module, const char *section, @@ -239,7 +292,7 @@ void settings_add_time_module(const char *module, const char *section, memset(&default_value, 0, sizeof(default_value)); default_value.v_string = g_strdup(def); - settings_add(module, section, key, SETTING_TYPE_TIME, &default_value); + settings_add(module, section, key, SETTING_TYPE_TIME, &default_value, NULL); } void settings_add_level_module(const char *module, const char *section, @@ -249,7 +302,7 @@ void settings_add_level_module(const char *module, const char *section, memset(&default_value, 0, sizeof(default_value)); default_value.v_string = g_strdup(def); - settings_add(module, section, key, SETTING_TYPE_LEVEL, &default_value); + settings_add(module, section, key, SETTING_TYPE_LEVEL, &default_value, NULL); } void settings_add_size_module(const char *module, const char *section, @@ -259,14 +312,16 @@ void settings_add_size_module(const char *module, const char *section, memset(&default_value, 0, sizeof(default_value)); default_value.v_string = g_strdup(def); - settings_add(module, section, key, SETTING_TYPE_SIZE, &default_value); + settings_add(module, section, key, SETTING_TYPE_SIZE, &default_value, NULL); } static void settings_destroy(SETTINGS_REC *rec) { if (rec->type != SETTING_TYPE_INT && - rec->type != SETTING_TYPE_BOOLEAN) + rec->type != SETTING_TYPE_BOOLEAN && + rec->type != SETTING_TYPE_CHOICE) g_free(rec->default_value.v_string); + g_strfreev(rec->choices); g_free(rec->module); g_free(rec->section); g_free(rec->key); @@ -328,6 +383,20 @@ static CONFIG_NODE *settings_get_node(const char *key) return iconfig_node_section(node, rec->module, NODE_TYPE_BLOCK); } +gboolean settings_set_choice(const char *key, const char *value) +{ + SETTINGS_REC *rec; + + rec = settings_get_record(key); + + if (rec != NULL && strarray_find(rec->choices, value) < 0) + return FALSE; + + settings_set_str(key, value); + + return TRUE; +} + void settings_set_str(const char *key, const char *value) { iconfig_node_set_str(settings_get_node(key), key, value); @@ -343,7 +412,7 @@ void settings_set_bool(const char *key, int value) iconfig_node_set_bool(settings_get_node(key), key, value); } -int settings_set_time(const char *key, const char *value) +gboolean settings_set_time(const char *key, const char *value) { int msecs; @@ -354,7 +423,7 @@ int settings_set_time(const char *key, const char *value) return TRUE; } -int settings_set_level(const char *key, const char *value) +gboolean settings_set_level(const char *key, const char *value) { int iserror; @@ -366,7 +435,7 @@ int settings_set_level(const char *key, const char *value) return TRUE; } -int settings_set_size(const char *key, const char *value) +gboolean settings_set_size(const char *key, const char *value) { int size; @@ -683,7 +752,7 @@ static void init_configfile(void) if (stat(get_irssi_dir(), &statbuf) != 0) { /* ~/.irssi not found, create it. */ - if (mkpath(get_irssi_dir(), 0700) != 0) { + if (g_mkdir_with_parents(get_irssi_dir(), 0700) != 0) { g_error("Couldn't create %s directory", get_irssi_dir()); } } else if (!S_ISDIR(statbuf.st_mode)) { diff --git a/src/core/settings.h b/src/core/settings.h index 6f2cf129..d174f250 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -8,6 +8,7 @@ typedef enum { SETTING_TYPE_TIME, SETTING_TYPE_LEVEL, SETTING_TYPE_SIZE, + SETTING_TYPE_CHOICE, SETTING_TYPE_ANY } SettingType; @@ -26,6 +27,7 @@ typedef struct { SettingType type; SettingValue default_value; + char **choices; } SETTINGS_REC; /* macros for handling the default Irssi configuration */ @@ -58,6 +60,7 @@ int settings_get_bool(const char *key); int settings_get_time(const char *key); /* as milliseconds */ int settings_get_level(const char *key); int settings_get_size(const char *key); /* as bytes */ +int settings_get_choice(const char *key); char *settings_get_print(SETTINGS_REC *rec); /* Functions to add/remove settings */ @@ -73,6 +76,8 @@ void settings_add_level_module(const char *module, const char *section, const char *key, const char *def); void settings_add_size_module(const char *module, const char *section, const char *key, const char *def); +void settings_add_choice_module(const char *module, const char *section, + const char *key, int def, const char *choices); void settings_remove(const char *key); void settings_remove_module(const char *module); @@ -88,13 +93,16 @@ void settings_remove_module(const char *module); settings_add_level_module(MODULE_NAME, section, key, def) #define settings_add_size(section, key, def) \ settings_add_size_module(MODULE_NAME, section, key, def) +#define settings_add_choice(section, key, def, choices) \ + settings_add_choice_module(MODULE_NAME, section, key, def, choices) void settings_set_str(const char *key, const char *value); void settings_set_int(const char *key, int value); void settings_set_bool(const char *key, int value); -int settings_set_time(const char *key, const char *value); -int settings_set_level(const char *key, const char *value); -int settings_set_size(const char *key, const char *value); +gboolean settings_set_time(const char *key, const char *value); +gboolean settings_set_level(const char *key, const char *value); +gboolean settings_set_size(const char *key, const char *value); +gboolean settings_set_choice(const char *key, const char *value); /* Get the type (SETTING_TYPE_xxx) of `key' */ SettingType settings_get_type(const char *key); diff --git a/src/core/special-vars.c b/src/core/special-vars.c index 4dcc3d2f..6ca080fc 100644 --- a/src/core/special-vars.c +++ b/src/core/special-vars.c @@ -25,10 +25,7 @@ #include "settings.h" #include "servers.h" #include "misc.h" - -#define ALIGN_RIGHT 0x01 -#define ALIGN_CUT 0x02 -#define ALIGN_PAD 0x04 +#include "utf8.h" #define isvarchar(c) \ (i_isalnum(c) || (c) == '_') @@ -47,7 +44,7 @@ static char *get_argument(char **cmd, char **arglist) arg = 0; max = -1; - argcount = arglist == NULL ? 0 : strarray_length(arglist); + argcount = arglist == NULL ? 0 : g_strv_length(arglist); if (**cmd == '*') { /* get all arguments */ @@ -316,22 +313,28 @@ static int get_alignment_args(char **data, int *align, int *flags, char *pad) } /* return the aligned text */ -static char *get_alignment(const char *text, int align, int flags, char pad) +char *get_alignment(const char *text, int align, int flags, char pad) { GString *str; char *ret; + int policy; + unsigned int cut_bytes; g_return_val_if_fail(text != NULL, NULL); + policy = string_policy(text); + str = g_string_new(text); /* cut */ - if ((flags & ALIGN_CUT) && align > 0 && str->len > align) - g_string_truncate(str, align); + if ((flags & ALIGN_CUT) && align > 0 && string_width(text, policy) > align) { + string_chars_for_width(text, policy, align, &cut_bytes); + g_string_truncate(str, cut_bytes); + } /* add pad characters */ if (flags & ALIGN_PAD) { - while (str->len < align) { + while (string_width(str->str, policy) < align) { if (flags & ALIGN_RIGHT) g_string_prepend_c(str, pad); else @@ -340,7 +343,7 @@ static char *get_alignment(const char *text, int align, int flags, char pad) } ret = str->str; - g_string_free(str, FALSE); + g_string_free(str, FALSE); return ret; } diff --git a/src/core/special-vars.h b/src/core/special-vars.h index 11262dad..300dae0e 100644 --- a/src/core/special-vars.h +++ b/src/core/special-vars.h @@ -9,9 +9,16 @@ #define PARSE_FLAG_ESCAPE_THEME 0x08 /* if any arguments/variables contain { or } chars, escape them with % */ #define PARSE_FLAG_ONLY_ARGS 0x10 /* expand only arguments ($0 $1 etc.) but no other $variables */ +#define ALIGN_RIGHT 0x01 +#define ALIGN_CUT 0x02 +#define ALIGN_PAD 0x04 + typedef char* (*SPECIAL_HISTORY_FUNC) (const char *text, void *item, int *free_ret); +/* Cut and/or pad text so it takes exactly "align" characters on the screen */ +char *get_alignment(const char *text, int align, int flags, char pad); + /* Parse and expand text after '$' character. return value has to be g_free()'d if `free_ret' is TRUE. */ char *parse_special(char **cmd, SERVER_REC *server, void *item, diff --git a/src/core/utf8.c b/src/core/utf8.c new file mode 100644 index 00000000..c53d8816 --- /dev/null +++ b/src/core/utf8.c @@ -0,0 +1,135 @@ +/* utf8.c - Operations on UTF-8 strings. + * + * Copyright (C) 2002 Timo Sirainen + * + * Based on GLib code by + * + * Copyright (C) 1999 Tom Tromey + * Copyright (C) 2000 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser 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 Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "utf8.h" +#include "module.h" + +/* Provide is_utf8(): */ +#include "recode.h" + +int string_advance(char const **str, int policy) +{ + if (policy == TREAT_STRING_AS_UTF8) { + gunichar c; + + c = g_utf8_get_char(*str); + *str = g_utf8_next_char(*str); + + return unichar_isprint(c) ? mk_wcwidth(c) : 1; + } else { + /* Assume TREAT_STRING_AS_BYTES: */ + *str += 1; + + return 1; + } +} + +int string_policy(const char *str) +{ + if (is_utf8()) { + if (str == NULL || g_utf8_validate(str, -1, NULL)) { + /* No string provided or valid UTF-8 string: treat as UTF-8: */ + return TREAT_STRING_AS_UTF8; + } + } + return TREAT_STRING_AS_BYTES; +} + +int string_length(const char *str, int policy) +{ + g_return_val_if_fail(str != NULL, 0); + + if (policy == -1) { + policy = string_policy(str); + } + + if (policy == TREAT_STRING_AS_UTF8) { + return g_utf8_strlen(str, -1); + } + else { + /* Assume TREAT_STRING_AS_BYTES: */ + return strlen(str); + } +} + +int string_width(const char *str, int policy) +{ + int len; + + g_return_val_if_fail(str != NULL, 0); + + if (policy == -1) { + policy = string_policy(str); + } + + len = 0; + while (*str != '\0') { + len += string_advance(&str, policy); + } + return len; +} + +int string_chars_for_width(const char *str, int policy, unsigned int n, unsigned int *bytes) +{ + const char *c, *previous_c; + int str_width, char_width, char_count; + + g_return_val_if_fail(str != NULL, -1); + + /* Handle the dummy case where n is 0: */ + if (n == 0) { + if (bytes != NULL) { + *bytes = 0; + } + return 0; + } + + if (policy == -1) { + policy = string_policy(str); + } + + /* Iterate over characters until we reach n: */ + char_count = 0; + str_width = 0; + c = str; + while (*c != '\0') { + previous_c = c; + char_width = string_advance(&c, policy); + if (str_width + char_width > n) { + /* We stepped beyond n, get one step back and stop there: */ + c = previous_c; + break; + } + ++ char_count; + str_width += char_width; + } + /* At this point, we know that char_count characters reach str_width + * columns, which is less than or equal to n. */ + + /* Optionally provide the equivalent amount of bytes: */ + if (bytes != NULL) { + *bytes = c - str; + } + return char_count; +} diff --git a/src/core/utf8.h b/src/core/utf8.h new file mode 100644 index 00000000..5bb53193 --- /dev/null +++ b/src/core/utf8.h @@ -0,0 +1,56 @@ +#ifndef __UTF8_H +#define __UTF8_H + +/* XXX I didn't check the encoding range of big5+. This is standard big5. */ +#define is_big5_los(lo) (0x40 <= (lo) && (lo) <= 0x7E) /* standard */ +#define is_big5_lox(lo) (0x80 <= (lo) && (lo) <= 0xFE) /* extended */ +#define is_big5_lo(lo) ((is_big5_los(lo) || is_big5_lox(lo))) +#define is_big5_hi(hi) (0x81 <= (hi) && (hi) <= 0xFE) +#define is_big5(hi,lo) (is_big5_hi(hi) && is_big5_lo(lo)) + +#include <glib.h> +typedef guint32 unichar; + +/* Returns width for character (0-2). */ +int mk_wcwidth(unichar c); + +/* Advance the str pointer one character further; return the number of columns + * occupied by the skipped character. + */ +int string_advance(char const **str, int policy); + +/* TREAT_STRING_AS_BYTES means strings are to be treated using strncpy, + * strnlen, etc. + * TREAT_STRING_AS_UTF8 means strings are to be treated using g_utf8_* + * functions. + */ +enum str_policy { + TREAT_STRING_AS_BYTES, + TREAT_STRING_AS_UTF8 +}; + +/* Return how the str string ought to be treated: TREAT_STRING_AS_UTF8 if the + * terminal handles UTF-8 and if the string appears to be a valid UTF-8 string; + * TREAT_STRING_AS_BYTES otherwise. + */ +int string_policy(const char *str); + +/* Return the length of the str string according to the given policy; if policy + * is -1, this function will call string_policy(). + */ +int string_length(const char *str, int policy); +/* Return the screen width of the str string according to the given policy; if + * policy is -1, this function will call string_policy(). + */ +int string_width(const char *str, int policy); + +/* Return the amount of characters from str it takes to reach n columns, or -1 if + * str is NULL. Optionally return the equivalent amount of bytes. + * If policy is -1, this function will call string_policy(). + */ +int string_chars_for_width(const char *str, int policy, unsigned int n, unsigned int *bytes); + +#define unichar_isprint(c) (((c) & ~0x80) >= 32) +#define is_utf8_leading(c) (((c) & 0xc0) != 0x80) + +#endif diff --git a/src/core/wcwidth.c b/src/core/wcwidth.c new file mode 100644 index 00000000..711c4646 --- /dev/null +++ b/src/core/wcwidth.c @@ -0,0 +1,220 @@ +/* + * This is an implementation of wcwidth() and wcswidth() (defined in + * IEEE Std 1002.1-2001) for Unicode. + * + * http://www.opengroup.org/onlinepubs/007904975/functions/wcwidth.html + * http://www.opengroup.org/onlinepubs/007904975/functions/wcswidth.html + * + * In fixed-width output devices, Latin characters all occupy a single + * "cell" position of equal width, whereas ideographic CJK characters + * occupy two such cells. Interoperability between terminal-line + * applications and (teletype-style) character terminals using the + * UTF-8 encoding requires agreement on which character should advance + * the cursor by how many cell positions. No established formal + * standards exist at present on which Unicode character shall occupy + * how many cell positions on character terminals. These routines are + * a first attempt of defining such behavior based on simple rules + * applied to data provided by the Unicode Consortium. + * + * For some graphical characters, the Unicode standard explicitly + * defines a character-cell width via the definition of the East Asian + * FullWidth (F), Wide (W), Half-width (H), and Narrow (Na) classes. + * In all these cases, there is no ambiguity about which width a + * terminal shall use. For characters in the East Asian Ambiguous (A) + * class, the width choice depends purely on a preference of backward + * compatibility with either historic CJK or Western practice. + * Choosing single-width for these characters is easy to justify as + * the appropriate long-term solution, as the CJK practice of + * displaying these characters as double-width comes from historic + * implementation simplicity (8-bit encoded characters were displayed + * single-width and 16-bit ones double-width, even for Greek, + * Cyrillic, etc.) and not any typographic considerations. + * + * Much less clear is the choice of width for the Not East Asian + * (Neutral) class. Existing practice does not dictate a width for any + * of these characters. It would nevertheless make sense + * typographically to allocate two character cells to characters such + * as for instance EM SPACE or VOLUME INTEGRAL, which cannot be + * represented adequately with a single-width glyph. The following + * routines at present merely assign a single-cell width to all + * neutral characters, in the interest of simplicity. This is not + * entirely satisfactory and should be reconsidered before + * establishing a formal standard in this area. At the moment, the + * decision which Not East Asian (Neutral) characters should be + * represented by double-width glyphs cannot yet be answered by + * applying a simple rule from the Unicode database content. Setting + * up a proper standard for the behavior of UTF-8 character terminals + * will require a careful analysis not only of each Unicode character, + * but also of each presentation form, something the author of these + * routines has avoided to do so far. + * + * http://www.unicode.org/unicode/reports/tr11/ + * + * Markus Kuhn -- 2007-05-25 (Unicode 5.0) + * + * Permission to use, copy, modify, and distribute this software + * for any purpose and without fee is hereby granted. The author + * disclaims all warranties with regard to this software. + * + * Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c + */ + +#include "utf8.h" + +struct interval { + int first; + int last; +}; + +/* auxiliary function for binary search in interval table */ +static int bisearch(unichar ucs, const struct interval *table, int max) { + int min = 0; + int mid; + + if (ucs < table[0].first || ucs > table[max].last) + return 0; + while (max >= min) { + mid = (min + max) / 2; + if (ucs > table[mid].last) + min = mid + 1; + else if (ucs < table[mid].first) + max = mid - 1; + else + return 1; + } + + return 0; +} + + +/* The following two functions define the column width of an ISO 10646 + * character as follows: + * + * - The null character (U+0000) has a column width of 0. + * + * - Other C0/C1 control characters and DEL will lead to a return + * value of -1. + * + * - Non-spacing and enclosing combining characters (general + * category code Mn or Me in the Unicode database) have a + * column width of 0. + * + * - SOFT HYPHEN (U+00AD) has a column width of 1. + * + * - Other format characters (general category code Cf in the Unicode + * database) and ZERO WIDTH SPACE (U+200B) have a column width of 0. + * + * - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF) + * have a column width of 0. + * + * - Spacing characters in the East Asian Wide (W) or East Asian + * Full-width (F) category as defined in Unicode Technical + * Report #11 have a column width of 2. + * + * - All remaining characters (including all printable + * ISO 8859-1 and WGL4 characters, Unicode control characters, + * etc.) have a column width of 1. + * + * This implementation assumes that wchar_t characters are encoded + * in ISO 10646. + */ + +int mk_wcwidth(unichar ucs) +{ + /* sorted list of non-overlapping intervals of non-spacing characters */ + /* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */ + static const struct interval combining[] = { + { 0x0300, 0x036F }, { 0x0483, 0x0486 }, { 0x0488, 0x0489 }, + { 0x0591, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 }, + { 0x05C4, 0x05C5 }, { 0x05C7, 0x05C7 }, { 0x0600, 0x0603 }, + { 0x0610, 0x0615 }, { 0x064B, 0x065E }, { 0x0670, 0x0670 }, + { 0x06D6, 0x06E4 }, { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED }, + { 0x070F, 0x070F }, { 0x0711, 0x0711 }, { 0x0730, 0x074A }, + { 0x07A6, 0x07B0 }, { 0x07EB, 0x07F3 }, { 0x0901, 0x0902 }, + { 0x093C, 0x093C }, { 0x0941, 0x0948 }, { 0x094D, 0x094D }, + { 0x0951, 0x0954 }, { 0x0962, 0x0963 }, { 0x0981, 0x0981 }, + { 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 }, { 0x09CD, 0x09CD }, + { 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 }, { 0x0A3C, 0x0A3C }, + { 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 }, { 0x0A4B, 0x0A4D }, + { 0x0A70, 0x0A71 }, { 0x0A81, 0x0A82 }, { 0x0ABC, 0x0ABC }, + { 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD }, + { 0x0AE2, 0x0AE3 }, { 0x0B01, 0x0B01 }, { 0x0B3C, 0x0B3C }, + { 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 }, { 0x0B4D, 0x0B4D }, + { 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 }, { 0x0BC0, 0x0BC0 }, + { 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 }, { 0x0C46, 0x0C48 }, + { 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, { 0x0CBC, 0x0CBC }, + { 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 }, { 0x0CCC, 0x0CCD }, + { 0x0CE2, 0x0CE3 }, { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D }, + { 0x0DCA, 0x0DCA }, { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 }, + { 0x0E31, 0x0E31 }, { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E }, + { 0x0EB1, 0x0EB1 }, { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC }, + { 0x0EC8, 0x0ECD }, { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 }, + { 0x0F37, 0x0F37 }, { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E }, + { 0x0F80, 0x0F84 }, { 0x0F86, 0x0F87 }, { 0x0F90, 0x0F97 }, + { 0x0F99, 0x0FBC }, { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 }, + { 0x1032, 0x1032 }, { 0x1036, 0x1037 }, { 0x1039, 0x1039 }, + { 0x1058, 0x1059 }, { 0x1160, 0x11FF }, { 0x135F, 0x135F }, + { 0x1712, 0x1714 }, { 0x1732, 0x1734 }, { 0x1752, 0x1753 }, + { 0x1772, 0x1773 }, { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD }, + { 0x17C6, 0x17C6 }, { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD }, + { 0x180B, 0x180D }, { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 }, + { 0x1927, 0x1928 }, { 0x1932, 0x1932 }, { 0x1939, 0x193B }, + { 0x1A17, 0x1A18 }, { 0x1B00, 0x1B03 }, { 0x1B34, 0x1B34 }, + { 0x1B36, 0x1B3A }, { 0x1B3C, 0x1B3C }, { 0x1B42, 0x1B42 }, + { 0x1B6B, 0x1B73 }, { 0x1DC0, 0x1DCA }, { 0x1DFE, 0x1DFF }, + { 0x200B, 0x200F }, { 0x202A, 0x202E }, { 0x2060, 0x2063 }, + { 0x206A, 0x206F }, { 0x20D0, 0x20EF }, { 0x302A, 0x302F }, + { 0x3099, 0x309A }, { 0xA806, 0xA806 }, { 0xA80B, 0xA80B }, + { 0xA825, 0xA826 }, { 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F }, + { 0xFE20, 0xFE23 }, { 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB }, + { 0x10A01, 0x10A03 }, { 0x10A05, 0x10A06 }, { 0x10A0C, 0x10A0F }, + { 0x10A38, 0x10A3A }, { 0x10A3F, 0x10A3F }, { 0x1D167, 0x1D169 }, + { 0x1D173, 0x1D182 }, { 0x1D185, 0x1D18B }, { 0x1D1AA, 0x1D1AD }, + { 0x1D242, 0x1D244 }, { 0xE0001, 0xE0001 }, { 0xE0020, 0xE007F }, + { 0xE0100, 0xE01EF } + }; + + /* test for 8-bit control characters */ + if (ucs == 0) + return 0; + if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0)) + return -1; + + /* binary search in table of non-spacing characters */ + if (bisearch(ucs, combining, + sizeof(combining) / sizeof(struct interval) - 1)) + return 0; + + /* if we arrive here, ucs is not a combining or C0/C1 control character */ + + return 1 + + (ucs >= 0x1100 && + (ucs <= 0x115f || /* Hangul Jamo init. consonants */ + ucs == 0x2329 || ucs == 0x232a || + (ucs >= 0x2e80 && ucs <= 0xa4cf && + ucs != 0x303f) || /* CJK ... Yi */ + (ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */ + (ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */ + (ucs >= 0xfe10 && ucs <= 0xfe19) || /* Vertical forms */ + (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */ + (ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */ + (ucs >= 0xffe0 && ucs <= 0xffe6) || + (ucs >= 0x20000 && ucs <= 0x2fffd) || + (ucs >= 0x30000 && ucs <= 0x3fffd))); +} + + +#if 0 +int mk_wcswidth(const unichar *pwcs, size_t n) +{ + int w, width = 0; + + for (;*pwcs && n-- > 0; pwcs++) + if ((w = mk_wcwidth(*pwcs)) < 0) + return -1; + else + width += w; + + return width; +} +#endif |