/* * This file contains static string helper functions. * * mICQ Copyright (C) © 2001-2007 Rüdiger Kuhlmann * * mICQ 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; version 2 dated June, 1991. * * mICQ 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. * * In addition, as a special exception permission is granted to link the * code of this release of mICQ with the OpenSSL project's "OpenSSL" * library, and distribute the linked executables. You must obey the GNU * General Public License in all respects for all of the code used other * than "OpenSSL". If you modify this file, you may extend this exception * to your version of the file, but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your version * of this file. * * You should have received a copy of the GNU General Public License * along with this package; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * $Id$ */ #include "micq.h" #include #if HAVE_NETDB_H #include #endif #if HAVE_NETINET_IN_H #include #endif #if HAVE_ARPA_INET_H #include #endif #if HAVE_WINSOCK2_H #include #endif #if HAVE_WCTYPE_H #include #endif #include #include #include "util_str.h" #include "conv.h" #include "contact.h" #include "preferences.h" /* * Initialize a dynamically allocated string. * * Returns NULL if not enough memory. */ str_t s_init (str_t str, const char *init, size_t add) { UDWORD initlen, size; initlen = strlen (init); size = ((initlen + add) | 0x7f) + 1; if (!str->txt || size > str->max) { if (str->txt && str->max) free (str->txt); str->txt = malloc (size); if (!str->txt) { str->max = str->len = 0; str->txt = "\0"; return NULL; } str->max = size; } str->len = initlen; memcpy (str->txt, init, initlen + 1); return str; } /* * Increase storage size of dynamically allocated string. * * Returns NULL if not enough memory. */ str_t s_blow (str_t str, size_t len) { int newlen; char *tmp; newlen = 1 + ((str->max + len - 1) | 0x7f); if (!str->txt || !str->max) tmp = malloc (newlen); else tmp = realloc (str->txt, newlen); if (!tmp) { if (!str->txt || !str->max) { str->max = str->len = 0; str->txt = "\0"; } return NULL; } str->txt = tmp; str->max = newlen; return str; } /* * Appends a byte to a dynamically allocated string. */ str_t s_catc (str_t str, char add) { if (str->len + 2 > str->max) if (!s_blow (str, str->len + 2 - str->max)) return NULL; str->txt[str->len++] = add; str->txt[str->len] = '\0'; return str; } /* * Appends bytes to a dynamically allocated string. */ str_t s_catn (str_t str, const char *add, size_t len) { if (str->len + len + 2 > str->max) if (!s_blow (str, str->len + len + 2 - str->max)) return NULL; memcpy (str->txt + str->len, add, len); str->len += len; str->txt[str->len] = '\0'; return str; } /* * Appends a string to a dynamically allocated string. */ str_t s_cat (str_t str, const char *add) { int addlen = strlen (add); if (str->len + addlen + 2 > str->max) if (!s_blow (str, str->len + addlen + 2 - str->max)) return NULL; memcpy (str->txt + str->len, add, addlen + 1); str->len += addlen; return str; } /* * Appends a formatted string to a dynamically allocated string. */ str_t s_catf (str_t str, const char *fmt, ...) { va_list args; size_t add = 128; int rc; if (str->max < add || str->len + 2 >= str->max) s_blow (str, add); while (1) { if (str->len + 2 >= str->max) return NULL; str->txt[str->max - 2] = '\0'; va_start (args, fmt); rc = vsnprintf (str->txt + str->len, str->max - str->len - 1, fmt, args); va_end (args); if (rc >= 0 && rc < str->max - str->len - 2 && !str->txt[str->max - 2]) { str->len += strlen (str->txt + str->len); return str; } add = (rc > 0 ? rc - str->max + str->len + 5 : str->max); if (!s_blow (str, add)) { str->txt[str->max - 2] = '\0'; str->len = str->max - 2; return str; } } return str; } /* * Insert a string into a dynamically allocated string. */ str_t s_insn (str_t str, size_t pos, const char *ins, size_t len) { if (pos > str->len || pos < 0) return NULL; if (str->len + len + 2 > str->max) if (!s_blow (str, str->len + len + 2 - str->max)) return NULL; memmove (str->txt + pos + len, str->txt + pos, str->len - pos + 1); memcpy (str->txt + pos, ins, len); str->len += len; return str; } /* * Insert a byte into a dynamically allocated string. */ str_t s_insc (str_t str, size_t pos, char ins) { if (pos > str->len || pos < 0) return NULL; if (str->len + 3 > str->max) if (!s_blow (str, str->len + 3 - str->max)) return NULL; memmove (str->txt + pos + 1, str->txt + pos, str->len - pos + 1); str->txt[pos] = ins; str->len++; return str; } /* * Delete a byte from a dynamically allocated string. */ str_t s_delc (str_t str, size_t pos) { if (pos > str->len || pos < 0) return NULL; memmove (str->txt + pos, str->txt + pos + 1, str->len - pos); str->len--; return str; } /* * Delete bytes from a dynamically allocated string. */ str_t s_deln (str_t str, size_t pos, size_t len) { if (pos + len > str->len) return NULL; memmove (str->txt + pos, str->txt + pos + len, str->len - pos); str->len -= len; return str; } /* * Frees a dynamically allocated string. */ void s_done (str_t str) { if (str->txt && str->max) free (str->txt); str->txt = "\0"; str->len = str->max = 0; } /* * Return a static formatted string. */ const char *s_sprintf (const char *fmt, ...) { static char *buf = NULL; static int size = 0; va_list args; char *nbuf; int rc, nsize; if (!buf) buf = calloc (1, size = 1024); while (1) { buf[size - 2] = '\0'; va_start (args, fmt); rc = vsnprintf (buf, size, fmt, args); va_end (args); if (rc >= 0 && rc < size && !buf[size - 2]) break; nsize = (rc > 0 ? rc + 5 : size * 2); nbuf = malloc (nsize); if (!nbuf) break; free (buf); buf = nbuf; size = nsize; } return buf; } /* * Return a static string consisting of the given IP. */ const char *s_ip (UDWORD ip) { struct sockaddr_in sin; sin.sin_addr.s_addr = htonl (ip); return s_sprintf ("%s", inet_ntoa (sin.sin_addr)); } /* * Return a static string describing the status. */ const char *s_status (status_t status, UDWORD nativestatus) { static char buf[200]; if (status == ims_offline) return i18n (1969, "offline"); if (ContactIsInv (status)) snprintf (buf, sizeof (buf), "%s-", i18n (1975, "invisible")); else buf[0] = '\0'; switch (ContactClearInv (status)) { case imr_dnd: snprintf (buf + strlen (buf), sizeof(buf) - strlen (buf), "%s", i18n (1971, "do not disturb")); break; case imr_occ: snprintf (buf + strlen (buf), sizeof(buf) - strlen (buf), "%s", i18n (1973, "occupied")); break; case imr_na: snprintf (buf + strlen (buf), sizeof(buf) - strlen (buf), "%s", i18n (1974, "not available")); break; case imr_away: snprintf (buf + strlen (buf), sizeof(buf) - strlen (buf), "%s", i18n (1972, "away")); break; case imr_ffc: snprintf (buf + strlen (buf), sizeof(buf) - strlen (buf), "%s", i18n (1976, "free for chat")); break; case imr_offline: assert (0); case imr_online: if (buf[0]) buf[strlen (buf) - 1] = '\0'; else snprintf (buf + strlen (buf), sizeof(buf) - strlen (buf), "%s", i18n (1970, "online")); } if (prG->verbose) snprintf (buf + strlen (buf), sizeof(buf) - strlen (buf), " %08lx", nativestatus); return buf; } /* * Return a static string short describing the status. */ const char *s_status_short (status_t status) { static char buf[20]; if (status == ims_offline) return i18n (2621, "off"); if (status == ims_inv) return i18n (2622, "inv"); if (ContactIsInv (status)) snprintf (buf, sizeof (buf), "%s-", i18n (2622, "inv")); else buf[0] = '\0'; switch (ContactClearInv (status)) { case imr_dnd: snprintf (buf + strlen (buf), sizeof (buf) - strlen (buf), "%s", i18n (2623, "dnd")); break; case imr_occ: snprintf (buf + strlen (buf), sizeof (buf) - strlen (buf), "%s", i18n (2624, "occ")); break; case imr_na: snprintf (buf + strlen (buf), sizeof (buf) - strlen (buf), "%s", i18n (2625, "na")); break; case imr_away: snprintf (buf + strlen (buf), sizeof (buf) - strlen (buf), "%s", i18n (2626, "away")); break; case imr_ffc: snprintf (buf + strlen (buf), sizeof (buf) - strlen (buf), "%s", i18n (2627, "ffc")); break; case imr_offline: assert (0); case imr_online: snprintf (buf + strlen (buf), sizeof (buf) - strlen (buf), "%s", i18n (2628, "online")); } return buf; } /* * Returns static string describing the given time. */ const char *s_time (time_t *stamp) { static str_s str = { NULL, 0, 0 }; struct timeval p = {0L, 0L}; struct tm now; struct tm *thetime; time_t nowsec; size_t rc = 0; if (stamp && *stamp != NOW) { nowsec = time (NULL); now = *localtime (&nowsec); thetime = localtime (stamp); } else { #ifdef HAVE_GETTIMEOFDAY if (gettimeofday (&p, NULL) == -1) #endif { p.tv_usec = 0L; nowsec = time (NULL); } #ifdef HAVE_GETTIMEOFDAY else nowsec = p.tv_sec; #endif thetime = &now; stamp = NULL; now = *localtime (&nowsec); } s_init (&str, "", 32); while (!rc) { rc = strftime (str.txt, str.max, thetime->tm_year == now.tm_year && thetime->tm_mon == now.tm_mon && thetime->tm_mday == now.tm_mday ? "%H:%M:%S" : "%a %b %d %H:%M:%S %Y", thetime); /*"*/ if (rc <= 0 || rc >= str.max) { rc = 0; s_blow (&str, 32); } else { str.txt[str.max - 1] = 0; str.len = strlen (str.txt); } } if (prG->verbose > 7) s_catf (&str, ".%.06ld", p.tv_usec); else if (prG->verbose > 1) s_catf (&str, ".%.03ld", p.tv_usec / 1000); return ConvFrom (&str, prG->enc_loc)->txt; } /* * Returns static string describing the given time in strftime format. */ const char *s_strftime (time_t *stamp, const char *fmt, char as_gmt) { static str_s str = { NULL, 0, 0 }; struct tm *thetime; size_t rc = 0; char *dotfmt; /* strftime()'s error reporting is incomplete, so make sure a correct result is never empty */ dotfmt = malloc (strlen (fmt) + 2); strcpy (dotfmt, fmt); strcat (dotfmt, "."); if (stamp && *stamp != NOW) thetime = as_gmt ? gmtime (stamp) : localtime (stamp); else { time_t nowsec = time (NULL); thetime = as_gmt ? gmtime (&nowsec) : localtime (&nowsec); } s_init (&str, "", 32); while (!rc) { rc = strftime (str.txt, str.max, dotfmt, thetime); if (rc <= 0 || rc >= str.max) { rc = 0; s_blow (&str, 32); } else { str.txt[str.max - 1] = 0; str.len = strlen (str.txt); if (str.len && str.txt [str.len - 1] == '.') { str.txt [str.len - 1] = 0; str.len--; } } } s_free (dotfmt); return ConvFrom (&str, prG->enc_loc)->txt; } /* * strtok()/strsep() replacement, use to separate message fields */ const char *s_msgtok (char *txt) { static char *str = NULL; char *p, *t; if (txt) str = txt; if (!str) return NULL; if (txt && !*str) return NULL; p = strchr (t = str, Conv0xFE); if (p) { *p = '\0'; str = p + 1; } else { str = NULL; } return t; } /* * Indent a string two characters. */ const char *s_ind (const char *str) { static str_s t; UDWORD cnt = 5; const char *p; char *q; if (!str || !*str) return ""; for (p = str; *p; p++, cnt++) if (*p == '\n') cnt += 2; s_init (&t, "", cnt); if (t.max < cnt) return ""; q = t.txt; *q++ = ' '; *q++ = ' '; for (p = str; *p; ) if ((*q++ = *p++) == '\n') if (*p) { *q++ = ' '; *q++ = ' '; } *q = '\0'; return t.txt; } /* * Count the string length in unicode characters. */ UDWORD s_strlen (const char *str) { UDWORD c; for (c = 0; *str; str++) if (!(*str & 0x80) || (*str & 0x40)) c++; return c; } /* * Count the string length in unicode characters. */ UDWORD s_strnlen (const char *str, UDWORD len) { UDWORD c; for (c = 0; *str && len; str++, len--) if (!(*str & 0x80) || (*str & 0x40)) c++; return c; } /* * Gives the byte offset of a character offset in UTF-8. */ UDWORD s_offset (const char *str, UDWORD offset) { int off; for (off = 0; offset && *str; offset--, off++) { if (*str & 0x80) { str++; while (*str && ((*str & 0xc0) == 0x80)) str++, off++; } else str++; } return off; } /* * Split off a certain amount of recoded bytes */ strc_t s_split (const char **input, UBYTE enc, int len) { static str_s str = { NULL, 0, 0 }; str_s in = { NULL, 0, 0 }; strc_t tr; int off, offt, offold, offnl, offin, offmax; UDWORD ucs; in.txt = (char *) *input; in.len = strlen (*input); off = offt = offnl = offin = offmax = 0; while (in.txt[off]) { offold = off; ucs = ConvGetUTF8 (&in, &off); tr = ConvToLen (in.txt + offold, enc, off - offold); if (ucs == '\r') offnl = offold; if (!(ucs & ~0xff) && isspace (ucs & 0xff)) offin = offold; if (tr->len > len) { off = offold; break; } if (ucs == '\n') offnl = off; if (!iswalnum (ucs)) offin = off; len -= tr->len; if (!in.txt[off]) offmax = off; } if (offmax) off = offmax; else if (offnl) off = offnl; else if (offin) off = offin; s_init (&str, "", off + 2); memcpy (str.txt, in.txt, off); str.len = off; str.txt[off] = 0; *input += off; return &str; } #define noctl(x) ((((x & 0x60) && x != 0x7f)) ? ConvUTF8 (x) : ".") /* * Hex dump to a string with ASCII. */ const char *s_dump (const UBYTE *data, UWORD len) { static str_s t; UDWORD i, off; const unsigned char *d = (const unsigned char *)data; s_init (&t, "", 100); while (len >= 16) { s_catf (&t, "%02x %02x %02x %02x %02x %02x %02x %02x " "%02x %02x %02x %02x %02x %02x %02x %02x ", d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]); s_catc (&t, '\"'); s_cat (&t, noctl (d[0])); s_cat (&t, noctl (d[1])); s_cat (&t, noctl (d[2])); s_cat (&t, noctl (d[3])); s_cat (&t, noctl (d[4])); s_cat (&t, noctl (d[5])); s_cat (&t, noctl (d[6])); s_cat (&t, noctl (d[7])); s_cat (&t, " "); s_cat (&t, noctl (d[8])); s_cat (&t, noctl (d[9])); s_cat (&t, noctl (d[10])); s_cat (&t, noctl (d[11])); s_cat (&t, noctl (d[12])); s_cat (&t, noctl (d[13])); s_cat (&t, noctl (d[14])); s_cat (&t, noctl (d[15])); s_catc (&t, '"'); s_catc (&t, '\n'); len -= 16; d += 16; } if (!len) return (t.txt); for (off = i = 0; i <= 16; i++) { if (len) { s_catf (&t, "%02x ", *d++); len--; off++; } else if (i != 16) s_catf (&t, " "); if (i == 7) s_catc (&t, ' '); } s_catc (&t, ' '); s_catc (&t, '\''); while (off) { s_cat (&t, noctl (*(d - off))); off--; } s_catf (&t, "'\n"); return t.txt; } /* * Hex dump to a string without ASCII. */ const char *s_dumpnd (const UBYTE *data, UWORD len) { static str_s t; UDWORD i; const unsigned char *d = (const unsigned char *)data; s_init (&t, "", 100); while (len > 16) { s_catf (&t, "%02x %02x %02x %02x %02x %02x %02x %02x " "%02x %02x %02x %02x %02x %02x %02x %02x\\\n", d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]); len -= 16; d += 16; } if (len > 10) { for (i = 0; i < len && i < 8; i++) { s_catf (&t, "%02x ", *d++); } s_catc (&t, '\n'); len -= i; } for (i = 0; i < 10; i++) { if (len) { s_catf (&t, "%02x ", *d++); len--; } else s_cat (&t, " "); if (i == 7) s_catc (&t, ' '); } return t.txt; } #define SUPERSAFE "%*+-_.:=@/0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" /* * Quote a string for use in a config file */ const char *s_quote (const char *input) { static str_s t; const char *tmp; if (!input || !*input) return "\"\""; for (tmp = input; *tmp; tmp++) if (!strchr (SUPERSAFE, *tmp)) break; if (!*tmp) return input; s_init (&t, "", 100); s_catc (&t, '\"'); for (tmp = input; *tmp; tmp++) { if (strchr ("\\\"", *tmp)) { s_catc (&t, '\\'); s_catc (&t, *tmp); } else if (*tmp & 0xe0) s_catc (&t, *tmp); else { s_catc (&t, '\\'); s_catc (&t, 'x'); s_catc (&t, (*tmp / 16) <= 9 ? ((UBYTE)*tmp / 16) + '0' : ((UBYTE)*tmp / 16) - 10 + 'a'); s_catc (&t, (*tmp & 15) <= 9 ? (*tmp & 15) + '0' : (*tmp & 15) - 10 + 'a'); } } s_catc (&t, '\"'); return t.txt; } /* * Quote a string for display. May use quotes to make sure text boundary is clear. */ const char *s_cquote (const char *input, const char *color) { static str_s t; const char *tmp; if (!input || !*input) return "\"\""; s_init (&t, "", 100); for (tmp = input; *tmp; tmp++) if (!strchr (SUPERSAFE, *tmp)) break; if (!*tmp) { s_cat (&t, color); s_cat (&t, input); s_cat (&t, COLNONE); return t.txt; } s_catc (&t, '\"'); s_cat (&t, color); for (tmp = input; *tmp; tmp++) { if (strchr ("\\\"", *tmp)) { s_catc (&t, '\\'); s_catc (&t, *tmp); } else if (*tmp & 0xe0) s_catc (&t, *tmp); else { s_catc (&t, '\\'); s_catc (&t, 'x'); s_catc (&t, (*tmp / 16) <= 9 ? ((UBYTE)*tmp / 16) + '0' : ((UBYTE)*tmp / 16) - 10 + 'a'); s_catc (&t, (*tmp & 15) <= 9 ? (*tmp & 15) + '0' : (*tmp & 15) - 10 + 'a'); } } s_cat (&t, COLNONE); s_catc (&t, '\"'); return t.txt; } /* * Quote a string for message display. Uses quotes only for the empty string. */ const char *s_mquote (const char *input, const char *color, BOOL allownl) { static str_s t; const char *tmp; if (!input || !*input) return "\"\""; s_init (&t, "", 100); for (tmp = input; *tmp; tmp++) if (!strchr (SUPERSAFE, *tmp)) break; if (!*tmp) { s_cat (&t, color); s_cat (&t, input); s_cat (&t, COLNONE); return t.txt; } s_cat (&t, color); for (tmp = input; *tmp; tmp++) { if (*tmp == '\n' && !tmp[1]) ; else if (*tmp & 0xe0 || (*tmp == '\n' && allownl)) s_catc (&t, *tmp); else if (*tmp != '\r' || !allownl || tmp[1] != '\n') { s_catf (&t, COLINVCHAR); s_catc (&t, *tmp - 1 + 'A'); s_catf (&t, color); } } s_cat (&t, COLNONE); return t.txt; }