Improve scrollback file handling

- Properly use filesystem encoding
- Validate utf8 when loading
- Use Gio
- Handle Windows line endings
- Remove dead code
- Add SCROLLBACK_MAX
This commit is contained in:
TingPing 2014-08-26 10:47:42 -04:00
parent d1c40196e3
commit fcd0554077
3 changed files with 106 additions and 126 deletions

View File

@ -473,7 +473,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;

View File

@ -22,6 +22,7 @@
#include <glib.h> #include <glib.h>
#include <glib/gstdio.h> #include <glib/gstdio.h>
#include <glib/gi18n.h> #include <glib/gi18n.h>
#include <gio/gio.h>
#include <time.h> /* need time_t */ #include <time.h> /* need time_t */
@ -391,7 +392,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 */

View File

@ -62,13 +62,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)
@ -85,53 +87,22 @@ scrollback_get_filename (session *sess)
buf = NULL; buf = NULL;
free (chan); 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) if (sess->scrollfile)
{ {
close (sess->scrollfd); g_object_unref (sess->scrollfile);
sess->scrollfd = -1; sess->scrollfile = NULL;
} }
} }
@ -140,29 +111,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;
@ -173,42 +128,39 @@ scrollback_shrink (session *sess)
p++; p++;
} }
fh = g_open (file, O_CREAT | O_TRUNC | O_APPEND | O_WRONLY, 0644); g_return_if_fail (lines >= max_lines);
g_free (file); offset = lines - max_lines;
if (fh == -1)
{
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,
free (buf); G_FILE_CREATE_PRIVATE, NULL, NULL, NULL))
sess->scrollwritten = lines;
g_free (buf);
} }
static void static void
scrollback_save (session *sess, char *text) scrollback_save (session *sess, char *text)
{ {
GOutputStream *ostream;
char *buf; char *buf;
time_t stamp; time_t stamp;
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;
@ -224,47 +176,55 @@ scrollback_save (session *sess, char *text)
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, 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;
stamp = time (0); stamp = time (0);
if (sizeof (stamp) == 4) /* gcc will optimize one of these out */ if (sizeof (stamp) == 4) /* gcc will optimize one of these out */
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)
{ {
@ -277,39 +237,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;
/* If nothing but funny trailing matter e.g. 0x0d or 0x0d0a, toss it */
if (n_bytes >= 1 && buf[0] == 0x0d)
{
g_free (buf);
continue;
}
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
@ -353,12 +311,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;
@ -390,16 +363,22 @@ 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; char *dirname, *dirname_fs;
dirname = g_path_get_dirname (filename); dirname = g_path_get_dirname (filename);
dirname_fs = g_filename_from_utf8 (dirname, -1, NULL, NULL, NULL);
g_mkdir_with_parents (dirname, 0700); g_mkdir_with_parents (dirname_fs, 0700);
g_free (dirname); g_free (dirname);
g_free (dirname_fs);
} }
static char * static char *