Improve scrollback file handling
- Properly use filesystem encoding - Validate utf8 when loading (hopefully fixing crashes) - Use Gio - Handle Windows line endings - Remove dead code - Fix respecting max length of scrollback files
This commit is contained in:
parent
089fe95a42
commit
650bddcfd1
|
@ -447,7 +447,6 @@ session_new (server *serv, char *from, int type, int focus)
|
||||||
|
|
||||||
sess->server = serv;
|
sess->server = serv;
|
||||||
sess->logfd = -1;
|
sess->logfd = -1;
|
||||||
sess->scrollfd = -1;
|
|
||||||
sess->type = type;
|
sess->type = type;
|
||||||
|
|
||||||
sess->alert_beep = SET_DEFAULT;
|
sess->alert_beep = SET_DEFAULT;
|
||||||
|
|
|
@ -377,7 +377,8 @@ typedef struct session
|
||||||
char channelkey[64]; /* XXX correct max length? */
|
char channelkey[64]; /* XXX correct max length? */
|
||||||
int limit; /* channel user limit */
|
int limit; /* channel user limit */
|
||||||
int logfd;
|
int logfd;
|
||||||
int scrollfd; /* scrollback filedes */
|
|
||||||
|
GFile *scrollfile; /* scrollback file */
|
||||||
int scrollwritten; /* number of lines written */
|
int scrollwritten; /* number of lines written */
|
||||||
|
|
||||||
char lastnick[NICKLEN]; /* last nick you /msg'ed */
|
char lastnick[NICKLEN]; /* last nick you /msg'ed */
|
||||||
|
|
|
@ -65,13 +65,15 @@ struct pevt_stage1
|
||||||
static ca_context *ca_con;
|
static ca_context *ca_con;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define SCROLLBACK_MAX 32000
|
||||||
|
|
||||||
static void mkdir_p (char *filename);
|
static void mkdir_p (char *filename);
|
||||||
static char *log_create_filename (char *channame);
|
static char *log_create_filename (char *channame);
|
||||||
|
|
||||||
static char *
|
static char *
|
||||||
scrollback_get_filename (session *sess)
|
scrollback_get_filename (session *sess)
|
||||||
{
|
{
|
||||||
char *net, *chan, *buf;
|
char *net, *chan, *buf, *ret = NULL;
|
||||||
|
|
||||||
net = server_get_network (sess->server, FALSE);
|
net = server_get_network (sess->server, FALSE);
|
||||||
if (!net)
|
if (!net)
|
||||||
|
@ -88,54 +90,19 @@ scrollback_get_filename (session *sess)
|
||||||
buf = NULL;
|
buf = NULL;
|
||||||
g_free (chan);
|
g_free (chan);
|
||||||
|
|
||||||
return buf;
|
if (buf)
|
||||||
|
{
|
||||||
|
ret = g_filename_from_utf8 (buf, -1, NULL, NULL, NULL);
|
||||||
|
g_free (buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
|
||||||
|
|
||||||
static void
|
|
||||||
scrollback_unlock (session *sess)
|
|
||||||
{
|
|
||||||
char buf[1024];
|
|
||||||
|
|
||||||
if (scrollback_get_filename (sess, buf, sizeof (buf) - 6) == NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
strcat (buf, ".lock");
|
|
||||||
unlink (buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
scrollback_lock (session *sess)
|
|
||||||
{
|
|
||||||
char buf[1024];
|
|
||||||
int fh;
|
|
||||||
|
|
||||||
if (scrollback_get_filename (sess, buf, sizeof (buf) - 6) == NULL)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
strcat (buf, ".lock");
|
|
||||||
|
|
||||||
if (access (buf, F_OK) == 0)
|
|
||||||
return FALSE; /* can't get lock */
|
|
||||||
|
|
||||||
fh = open (buf, O_CREAT | O_TRUNC | O_APPEND | O_WRONLY, 0644);
|
|
||||||
if (fh == -1)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void
|
void
|
||||||
scrollback_close (session *sess)
|
scrollback_close (session *sess)
|
||||||
{
|
{
|
||||||
if (sess->scrollfd != -1)
|
g_clear_object (&sess->scrollfile);
|
||||||
{
|
|
||||||
close (sess->scrollfd);
|
|
||||||
sess->scrollfd = -1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* shrink the file to roughly prefs.hex_text_max_lines */
|
/* shrink the file to roughly prefs.hex_text_max_lines */
|
||||||
|
@ -143,29 +110,13 @@ scrollback_close (session *sess)
|
||||||
static void
|
static void
|
||||||
scrollback_shrink (session *sess)
|
scrollback_shrink (session *sess)
|
||||||
{
|
{
|
||||||
char *file;
|
char *buf, *p;
|
||||||
char *buf;
|
|
||||||
int fh;
|
|
||||||
int lines;
|
|
||||||
int line;
|
|
||||||
gsize len;
|
gsize len;
|
||||||
char *p;
|
gint offset, lines = 0;
|
||||||
|
const gint max_lines = MIN(prefs.hex_text_max_lines, SCROLLBACK_MAX);
|
||||||
|
|
||||||
scrollback_close (sess);
|
if (!g_file_load_contents (sess->scrollfile, NULL, &buf, &len, NULL, NULL))
|
||||||
sess->scrollwritten = 0;
|
|
||||||
lines = 0;
|
|
||||||
|
|
||||||
if ((file = scrollback_get_filename (sess)) == NULL)
|
|
||||||
{
|
|
||||||
g_free (file);
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
if (!g_file_get_contents (file, &buf, &len, NULL))
|
|
||||||
{
|
|
||||||
g_free (file);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* count all lines */
|
/* count all lines */
|
||||||
p = buf;
|
p = buf;
|
||||||
|
@ -176,41 +127,37 @@ scrollback_shrink (session *sess)
|
||||||
p++;
|
p++;
|
||||||
}
|
}
|
||||||
|
|
||||||
fh = g_open (file, O_CREAT | O_TRUNC | O_APPEND | O_WRONLY | OFLAGS, 0644);
|
offset = lines - max_lines;
|
||||||
g_free (file);
|
|
||||||
if (fh == -1)
|
|
||||||
{
|
|
||||||
g_free (buf);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
line = 0;
|
/* now just go back to where we want to start the file */
|
||||||
p = buf;
|
p = buf;
|
||||||
|
lines = 0;
|
||||||
while (p != buf + len)
|
while (p != buf + len)
|
||||||
{
|
{
|
||||||
if (*p == '\n')
|
if (*p == '\n')
|
||||||
{
|
{
|
||||||
line++;
|
lines++;
|
||||||
if (line >= lines - prefs.hex_text_max_lines &&
|
if (lines == offset)
|
||||||
p + 1 != buf + len)
|
|
||||||
{
|
{
|
||||||
p++;
|
p++;
|
||||||
write (fh, p, len - (p - buf));
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p++;
|
p++;
|
||||||
}
|
}
|
||||||
|
|
||||||
close (fh);
|
if (g_file_replace_contents (sess->scrollfile, p, strlen(p), NULL, FALSE,
|
||||||
|
G_FILE_CREATE_PRIVATE, NULL, NULL, NULL))
|
||||||
|
sess->scrollwritten = lines;
|
||||||
|
|
||||||
g_free (buf);
|
g_free (buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
scrollback_save (session *sess, char *text, time_t stamp)
|
scrollback_save (session *sess, char *text, time_t stamp)
|
||||||
{
|
{
|
||||||
|
GOutputStream *ostream;
|
||||||
char *buf;
|
char *buf;
|
||||||
int len;
|
|
||||||
|
|
||||||
if (sess->type == SESS_SERVER && prefs.hex_gui_tab_server == 1)
|
if (sess->type == SESS_SERVER && prefs.hex_gui_tab_server == 1)
|
||||||
return;
|
return;
|
||||||
|
@ -226,16 +173,25 @@ scrollback_save (session *sess, char *text, time_t stamp)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sess->scrollfd == -1)
|
if (!sess->scrollfile)
|
||||||
{
|
{
|
||||||
if ((buf = scrollback_get_filename (sess)) == NULL)
|
if ((buf = scrollback_get_filename (sess)) == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
sess->scrollfd = g_open (buf, O_CREAT | O_APPEND | O_WRONLY | OFLAGS, 0644);
|
sess->scrollfile = g_file_new_for_path (buf);
|
||||||
g_free (buf);
|
g_free (buf);
|
||||||
if (sess->scrollfd == -1)
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Users can delete the folder after it's created... */
|
||||||
|
GFile *parent = g_file_get_parent (sess->scrollfile);
|
||||||
|
g_file_make_directory_with_parents (parent, NULL, NULL);
|
||||||
|
g_object_unref (parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
ostream = G_OUTPUT_STREAM(g_file_append_to (sess->scrollfile, G_FILE_CREATE_PRIVATE, NULL, NULL));
|
||||||
|
if (!ostream)
|
||||||
|
return;
|
||||||
|
|
||||||
if (!stamp)
|
if (!stamp)
|
||||||
stamp = time(0);
|
stamp = time(0);
|
||||||
|
@ -243,31 +199,30 @@ scrollback_save (session *sess, char *text, time_t stamp)
|
||||||
buf = g_strdup_printf ("T %d ", (int) stamp);
|
buf = g_strdup_printf ("T %d ", (int) stamp);
|
||||||
else
|
else
|
||||||
buf = g_strdup_printf ("T %" G_GINT64_FORMAT " ", (gint64)stamp);
|
buf = g_strdup_printf ("T %" G_GINT64_FORMAT " ", (gint64)stamp);
|
||||||
write (sess->scrollfd, buf, strlen (buf));
|
|
||||||
g_free (buf);
|
|
||||||
|
|
||||||
len = strlen (text);
|
g_output_stream_write (ostream, buf, strlen (buf), NULL, NULL);
|
||||||
write (sess->scrollfd, text, len);
|
g_output_stream_write (ostream, text, strlen (text), NULL, NULL);
|
||||||
if (len && text[len - 1] != '\n')
|
if (!g_str_has_suffix (text, "\n"))
|
||||||
write (sess->scrollfd, "\n", 1);
|
g_output_stream_write (ostream, "\n", 1, NULL, NULL);
|
||||||
|
|
||||||
|
g_free (buf);
|
||||||
|
g_object_unref (ostream);
|
||||||
|
|
||||||
sess->scrollwritten++;
|
sess->scrollwritten++;
|
||||||
|
|
||||||
if ((sess->scrollwritten * 2 > prefs.hex_text_max_lines && prefs.hex_text_max_lines > 0) ||
|
if ((sess->scrollwritten > prefs.hex_text_max_lines && prefs.hex_text_max_lines > 0) ||
|
||||||
sess->scrollwritten > 32000)
|
sess->scrollwritten > SCROLLBACK_MAX)
|
||||||
scrollback_shrink (sess);
|
scrollback_shrink (sess);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
scrollback_load (session *sess)
|
scrollback_load (session *sess)
|
||||||
{
|
{
|
||||||
char *buf;
|
GInputStream *stream;
|
||||||
char *text;
|
GDataInputStream *istream;
|
||||||
|
gchar *buf, *text;
|
||||||
|
gint lines = 0;
|
||||||
time_t stamp;
|
time_t stamp;
|
||||||
int lines;
|
|
||||||
GIOChannel *io;
|
|
||||||
GError *file_error = NULL;
|
|
||||||
GError *io_err = NULL;
|
|
||||||
|
|
||||||
if (sess->text_scrollback == SET_DEFAULT)
|
if (sess->text_scrollback == SET_DEFAULT)
|
||||||
{
|
{
|
||||||
|
@ -280,32 +235,37 @@ scrollback_load (session *sess)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((buf = scrollback_get_filename (sess)) == NULL)
|
if (!sess->scrollfile)
|
||||||
|
{
|
||||||
|
if ((buf = scrollback_get_filename (sess)) == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
sess->scrollfile = g_file_new_for_path (buf);
|
||||||
|
g_free (buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
stream = G_INPUT_STREAM(g_file_read (sess->scrollfile, NULL, NULL));
|
||||||
|
if (!stream)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
io = g_io_channel_new_file (buf, "r", &file_error);
|
istream = g_data_input_stream_new (stream);
|
||||||
g_free (buf);
|
/*
|
||||||
if (!io)
|
* This is to avoid any issues moving between windows/unix
|
||||||
return;
|
* but the docs mention an invalid \r without a following \n
|
||||||
|
* can lock up the program... (Our write() always adds \n)
|
||||||
lines = 0;
|
*/
|
||||||
|
g_data_input_stream_set_newline_type (istream, G_DATA_STREAM_NEWLINE_TYPE_ANY);
|
||||||
|
g_object_unref (stream);
|
||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
|
GError *err = NULL;
|
||||||
gsize n_bytes;
|
gsize n_bytes;
|
||||||
GIOStatus io_status;
|
|
||||||
|
|
||||||
io_status = g_io_channel_read_line (io, &buf, &n_bytes, NULL, &io_err);
|
buf = g_data_input_stream_read_line_utf8 (istream, &n_bytes, NULL, &err);
|
||||||
|
|
||||||
if (io_status == G_IO_STATUS_NORMAL)
|
if (!err && buf)
|
||||||
{
|
{
|
||||||
char *buf_tmp;
|
|
||||||
|
|
||||||
n_bytes--;
|
|
||||||
buf_tmp = buf;
|
|
||||||
buf = g_strndup (buf_tmp, n_bytes);
|
|
||||||
g_free (buf_tmp);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Some scrollback lines have three blanks after the timestamp and a newline
|
* Some scrollback lines have three blanks after the timestamp and a newline
|
||||||
* Some have only one blank and a newline
|
* Some have only one blank and a newline
|
||||||
|
@ -349,12 +309,27 @@ scrollback_load (session *sess)
|
||||||
|
|
||||||
g_free (buf);
|
g_free (buf);
|
||||||
}
|
}
|
||||||
|
else if (err)
|
||||||
|
{
|
||||||
|
/* If its only an encoding error it may be specific to the line */
|
||||||
|
if (g_error_matches (err, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
|
||||||
|
{
|
||||||
|
g_warning ("Invalid utf8 in scrollback file\n");
|
||||||
|
g_clear_error (&err);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
else
|
/* For general errors just give up */
|
||||||
|
g_clear_error (&err);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
else /* No new line */
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
g_io_channel_unref (io);
|
g_object_unref (istream);
|
||||||
|
|
||||||
sess->scrollwritten = lines;
|
sess->scrollwritten = lines;
|
||||||
|
|
||||||
|
@ -386,14 +361,30 @@ log_close (session *sess)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* filename should be in utf8 encoding and will be
|
||||||
|
* converted to filesystem encoding automatically.
|
||||||
|
*/
|
||||||
static void
|
static void
|
||||||
mkdir_p (char *filename)
|
mkdir_p (char *filename)
|
||||||
{
|
{
|
||||||
char *dirname = g_path_get_dirname (filename);
|
char *dirname, *dirname_fs;
|
||||||
|
GError *err = NULL;
|
||||||
|
|
||||||
g_mkdir_with_parents (dirname, 0700);
|
dirname = g_path_get_dirname (filename);
|
||||||
|
dirname_fs = g_filename_from_utf8 (dirname, -1, NULL, NULL, &err);
|
||||||
|
if (!dirname_fs)
|
||||||
|
{
|
||||||
|
g_warning ("%s", err->message);
|
||||||
|
g_error_free (err);
|
||||||
|
g_free (dirname);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_mkdir_with_parents (dirname_fs, 0700);
|
||||||
|
|
||||||
g_free (dirname);
|
g_free (dirname);
|
||||||
|
g_free (dirname_fs);
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *
|
static char *
|
||||||
|
|
Loading…
Reference in New Issue