Add base64 decode for translation and right-align clock in status bar

This commit is contained in:
2026-05-02 00:00:03 +02:00
parent 8ca2281a1c
commit b198855ae5
+101 -2
View File
@@ -218,6 +218,77 @@ static void config_save(void)
fclose(f); fclose(f);
} }
/* Base64 decode: returns decoded length, or -1 if not valid base64 */
static int base64_decode(const char *in, size_t in_len, char *out, size_t out_size)
{
static const int T[256] = {
['A']=0,['B']=1,['C']=2,['D']=3,['E']=4,['F']=5,['G']=6,['H']=7,
['I']=8,['J']=9,['K']=10,['L']=11,['M']=12,['N']=13,['O']=14,['P']=15,
['Q']=16,['R']=17,['S']=18,['T']=19,['U']=20,['V']=21,['W']=22,['X']=23,
['Y']=24,['Z']=25,['a']=26,['b']=27,['c']=28,['d']=29,['e']=30,['f']=31,
['g']=32,['h']=33,['i']=34,['j']=35,['k']=36,['l']=37,['m']=38,['n']=39,
['o']=40,['p']=41,['q']=42,['r']=43,['s']=44,['t']=45,['u']=46,['v']=47,
['w']=48,['x']=49,['y']=50,['z']=51,['0']=52,['1']=53,['2']=54,['3']=55,
['4']=56,['5']=57,['6']=58,['7']=59,['8']=60,['9']=61,['+']=62,['/']=63,
};
/* Strip trailing padding for length calc */
size_t pad = 0;
while (in_len > 0 && in[in_len - 1] == '=') { pad++; in_len--; }
if (in_len < 4) return -1;
size_t out_len = in_len * 3 / 4;
if (out_len >= out_size) return -1;
size_t o = 0;
for (size_t i = 0; i < in_len; i += 4) {
int n = (int)(in_len - i);
if (n < 2) break;
unsigned int a = T[(unsigned char)in[i]];
unsigned int b = T[(unsigned char)in[i+1]];
out[o++] = (a << 2) | (b >> 4);
if (n > 2 && i + 2 < in_len + pad) {
unsigned int c = (i+2 < in_len) ? T[(unsigned char)in[i+2]] : 0;
out[o++] = (b << 4) | (c >> 2);
if (n > 3 && i + 3 < in_len + pad) {
unsigned int d = (i+3 < in_len) ? T[(unsigned char)in[i+3]] : 0;
out[o++] = (c << 6) | d;
}
}
}
/* Adjust for padding */
if (pad == 1 && o > 0) o--;
if (pad == 2 && o > 0) o--;
out[o] = '\0';
return (int)o;
}
/* Check if text looks like base64 and decode it. Returns 1 if decoded. */
static int try_base64_decode(const char *text, char *out, size_t out_size)
{
size_t len = strlen(text);
if (len < 8 || len > 1000) return 0;
/* Must be valid base64 chars only */
size_t i;
for (i = 0; i < len; i++) {
char c = text[i];
if (!((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
(c >= '0' && c <= '9') || c == '+' || c == '/' || c == '='))
return 0;
}
/* Length must be multiple of 4 */
if (len % 4 != 0) return 0;
int n = base64_decode(text, len, out, out_size);
if (n < 4) return 0;
/* Check that result looks like text (mostly printable) */
int printable = 0;
for (int j = 0; j < n; j++) {
unsigned char c = (unsigned char)out[j];
if ((c >= 0x20 && c <= 0x7e) || c == '\n' || c == '\r' || c == '\t' || c >= 0x80)
printable++;
}
if (printable * 100 / n < 80) return 0;
return 1;
}
static int needs_translation(const char *text) static int needs_translation(const char *text)
{ {
if (!ai_cfg.enabled || !translate_enabled) return 0; if (!ai_cfg.enabled || !translate_enabled) return 0;
@@ -461,6 +532,22 @@ static void draw_statusbar(void)
size_t blen = strlen(bar); size_t blen = strlen(bar);
snprintf(bar + blen, sizeof(bar) - blen, "%s", act); snprintf(bar + blen, sizeof(bar) - blen, "%s", act);
/* Right-align timestamp */
time_t now = time(NULL);
struct tm *tm = localtime(&now);
char rhs[64];
snprintf(rhs, sizeof(rhs), "[Holck Mirk - %02d%02d %02d:%02d]",
tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min);
size_t bar_len = strlen(bar);
size_t rhs_len = strlen(rhs);
if (bar_len + rhs_len < (size_t)term_cols) {
size_t pad = term_cols - rhs_len;
while (bar_len < pad)
bar[bar_len++] = ' ';
memcpy(bar + pad, rhs, rhs_len);
bar[term_cols] = '\0';
}
/* Status bar on second-to-last row */ /* Status bar on second-to-last row */
printf("\033[%d;1H\033[7m%-*.*s\033[0m", printf("\033[%d;1H\033[7m%-*.*s\033[0m",
term_rows - 1, term_cols, term_cols, bar); term_rows - 1, term_cols, term_cols, bar);
@@ -995,12 +1082,24 @@ static void handle_line(char *line)
} else if (strcasecmp(target, nick) == 0) { } else if (strcasecmp(target, nick) == 0) {
wprintf(WL_MSG, "<%s> %s\n", sender, text); wprintf(WL_MSG, "<%s> %s\n", sender, text);
pm_nick_add(sender); pm_nick_add(sender);
if (needs_translation(text)) char b64dec[1024];
if (try_base64_decode(text, b64dec, sizeof(b64dec))) {
if (needs_translation(b64dec))
translate_async(b64dec, WL_MSG, NULL);
else
wprintf(WL_MSG, " \033[3m[b64: %s]\033[0m\n", b64dec);
} else if (needs_translation(text))
translate_async(text, WL_MSG, NULL); translate_async(text, WL_MSG, NULL);
} else { } else {
int lvl = chan_to_level(target); int lvl = chan_to_level(target);
wprintf(lvl, "<%s> %s\n", sender, text); wprintf(lvl, "<%s> %s\n", sender, text);
if (needs_translation(text)) char b64dec2[1024];
if (try_base64_decode(text, b64dec2, sizeof(b64dec2))) {
if (needs_translation(b64dec2))
translate_async(b64dec2, lvl, translate_public ? target : NULL);
else
wprintf(lvl, " \033[3m[b64: %s]\033[0m\n", b64dec2);
} else if (needs_translation(text))
translate_async(text, lvl, translate_public ? target : NULL); translate_async(text, lvl, translate_public ? target : NULL);
} }
} }