summary refs log tree commit diff stats
path: root/src/common
diff options
context:
space:
mode:
authorPatrick Griffis <tingping@tingping.se>2016-01-31 15:13:18 -0500
committerPatrick Griffis <tingping@tingping.se>2016-01-31 15:31:15 -0500
commit650bddcfd1399c875156cec8be37e331835ce129 (patch)
tree4c614087565cda60f7dea50532e4f041310c0fd0 /src/common
parent089fe95a426dc59693618a2dd848fa726d7ccac4 (diff)
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
Diffstat (limited to 'src/common')
-rw-r--r--src/common/hexchat.c1
-rw-r--r--src/common/hexchat.h3
-rw-r--r--src/common/text.c223
3 files changed, 109 insertions, 118 deletions
diff --git a/src/common/hexchat.c b/src/common/hexchat.c
index a76db332..caa20eb7 100644
--- a/src/common/hexchat.c
+++ b/src/common/hexchat.c
@@ -447,7 +447,6 @@ session_new (server *serv, char *from, int type, int focus)
 
 	sess->server = serv;
 	sess->logfd = -1;
-	sess->scrollfd = -1;
 	sess->type = type;
 
 	sess->alert_beep = SET_DEFAULT;
diff --git a/src/common/hexchat.h b/src/common/hexchat.h
index 51c9b9aa..ec055939 100644
--- a/src/common/hexchat.h
+++ b/src/common/hexchat.h
@@ -377,7 +377,8 @@ typedef struct session
 	char channelkey[64];			  /* XXX correct max length? */
 	int limit;						  /* channel user limit */
 	int logfd;
-	int scrollfd;							/* scrollback filedes */
+
+	GFile *scrollfile;							/* scrollback file */
 	int scrollwritten;					/* number of lines written */
 
 	char lastnick[NICKLEN];			  /* last nick you /msg'ed */
diff --git a/src/common/text.c b/src/common/text.c
index 766d2dd9..c2db9254 100644
--- a/src/common/text.c
+++ b/src/common/text.c
@@ -65,13 +65,15 @@ struct pevt_stage1
 static ca_context *ca_con;
 #endif
 
+#define SCROLLBACK_MAX 32000
+
 static void mkdir_p (char *filename);
 static char *log_create_filename (char *channame);
 
 static char *
 scrollback_get_filename (session *sess)
 {
-	char *net, *chan, *buf;
+	char *net, *chan, *buf, *ret = NULL;
 
 	net = server_get_network (sess->server, FALSE);
 	if (!net)
@@ -88,54 +90,19 @@ scrollback_get_filename (session *sess)
 		buf = NULL;
 	g_free (chan);
 
-	return buf;
-}
-
-#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;
+	if (buf)
+	{
+		ret = g_filename_from_utf8 (buf, -1, NULL, NULL, NULL);
+		g_free (buf);
+	}
 
-	return TRUE;
+	return ret;
 }
 
-#endif
-
 void
 scrollback_close (session *sess)
 {
-	if (sess->scrollfd != -1)
-	{
-		close (sess->scrollfd);
-		sess->scrollfd = -1;
-	}
+	g_clear_object (&sess->scrollfile);
 }
 
 /* shrink the file to roughly prefs.hex_text_max_lines */
@@ -143,29 +110,13 @@ scrollback_close (session *sess)
 static void
 scrollback_shrink (session *sess)
 {
-	char *file;
-	char *buf;
-	int fh;
-	int lines;
-	int line;
+	char *buf, *p;
 	gsize len;
-	char *p;
-
-	scrollback_close (sess);
-	sess->scrollwritten = 0;
-	lines = 0;
+	gint offset, lines = 0;
+	const gint max_lines = MIN(prefs.hex_text_max_lines, SCROLLBACK_MAX);
 
-	if ((file = scrollback_get_filename (sess)) == NULL)
-	{
-		g_free (file);
+	if (!g_file_load_contents (sess->scrollfile, NULL, &buf, &len, NULL, NULL))
 		return;
-	}
-
-	if (!g_file_get_contents (file, &buf, &len, NULL))
-	{
-		g_free (file);
-		return;
-	}
 
 	/* count all lines */
 	p = buf;
@@ -176,41 +127,37 @@ scrollback_shrink (session *sess)
 		p++;
 	}
 
-	fh = g_open (file, O_CREAT | O_TRUNC | O_APPEND | O_WRONLY | OFLAGS, 0644);
-	g_free (file);
-	if (fh == -1)
-	{
-		g_free (buf);
-		return;
-	}
+	offset = lines - max_lines;
 
-	line = 0;
+	/* now just go back to where we want to start the file */
 	p = buf;
+	lines = 0;
 	while (p != buf + len)
 	{
 		if (*p == '\n')
 		{
-			line++;
-			if (line >= lines - prefs.hex_text_max_lines &&
-				 p + 1 != buf + len)
+			lines++;
+			if (lines == offset)
 			{
 				p++;
-				write (fh, p, len - (p - buf));
 				break;
 			}
 		}
 		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);
 }
 
 static void
 scrollback_save (session *sess, char *text, time_t stamp)
 {
+	GOutputStream *ostream;
 	char *buf;
-	int len;
 
 	if (sess->type == SESS_SERVER && prefs.hex_gui_tab_server == 1)
 		return;
@@ -226,16 +173,25 @@ scrollback_save (session *sess, char *text, time_t stamp)
 			return;
 	}
 
-	if (sess->scrollfd == -1)
+	if (!sess->scrollfile)
 	{
 		if ((buf = scrollback_get_filename (sess)) == NULL)
 			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);
-		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)
 		stamp = time(0);
@@ -243,31 +199,30 @@ scrollback_save (session *sess, char *text, time_t stamp)
 		buf = g_strdup_printf ("T %d ", (int) stamp);
 	else
 		buf = g_strdup_printf ("T %" G_GINT64_FORMAT " ", (gint64)stamp);
-	write (sess->scrollfd, buf, strlen (buf));
-	g_free (buf);
 
-	len = strlen (text);
-	write (sess->scrollfd, text, len);
-	if (len && text[len - 1] != '\n')
-		write (sess->scrollfd, "\n", 1);
+	g_output_stream_write (ostream, buf, strlen (buf), NULL, NULL);
+	g_output_stream_write (ostream, text, strlen (text), NULL, NULL);
+	if (!g_str_has_suffix (text, "\n"))
+		g_output_stream_write (ostream, "\n", 1, NULL, NULL);
+
+	g_free (buf);
+	g_object_unref (ostream);
 
 	sess->scrollwritten++;
 
-	if ((sess->scrollwritten * 2 > prefs.hex_text_max_lines && prefs.hex_text_max_lines > 0) ||
-       sess->scrollwritten > 32000)
+	if ((sess->scrollwritten > prefs.hex_text_max_lines && prefs.hex_text_max_lines > 0) ||
+       sess->scrollwritten > SCROLLBACK_MAX)
 		scrollback_shrink (sess);
 }
 
 void
 scrollback_load (session *sess)
 {
-	char *buf;
-	char *text;
+	GInputStream *stream;
+	GDataInputStream *istream;
+	gchar *buf, *text;
+	gint lines = 0;
 	time_t stamp;
-	int lines;
-	GIOChannel *io;
-	GError *file_error = NULL;
-	GError *io_err = NULL;
 
 	if (sess->text_scrollback == SET_DEFAULT)
 	{
@@ -280,32 +235,37 @@ scrollback_load (session *sess)
 			return;
 	}
 
-	if ((buf = scrollback_get_filename (sess)) == NULL)
-		return;
+	if (!sess->scrollfile)
+	{
+		if ((buf = scrollback_get_filename (sess)) == NULL)
+			return;
 
-	io = g_io_channel_new_file (buf, "r", &file_error);
-	g_free (buf);
-	if (!io)
+		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;
 
-	lines = 0;
+	istream = g_data_input_stream_new (stream);
+	/*
+	 * This is to avoid any issues moving between windows/unix
+	 * but the docs mention an invalid \r without a following \n
+	 * can lock up the program... (Our write() always adds \n)
+	 */
+	g_data_input_stream_set_newline_type (istream, G_DATA_STREAM_NEWLINE_TYPE_ANY);
+	g_object_unref (stream);
 
 	while (1)
 	{
+		GError *err = NULL;
 		gsize n_bytes;
-		GIOStatus io_status;
-
-		io_status = g_io_channel_read_line (io, &buf, &n_bytes, NULL, &io_err);
-		
-		if (io_status == G_IO_STATUS_NORMAL)
-		{
-			char *buf_tmp;
 
-			n_bytes--;
-			buf_tmp = buf;
-			buf = g_strndup (buf_tmp, n_bytes);
-			g_free (buf_tmp);
+		buf = g_data_input_stream_read_line_utf8 (istream, &n_bytes, NULL, &err);
 
+		if (!err && buf)
+		{
 			/*
 			 * Some scrollback lines have three blanks after the timestamp and a newline
 			 * Some have only one blank and a newline
@@ -349,12 +309,27 @@ scrollback_load (session *sess)
 
 			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;
+		}
+		else /* No new line */
+		{
+			break;
+		}
 	}
 
-	g_io_channel_unref (io);
+	g_object_unref (istream);
 
 	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
 mkdir_p (char *filename)
 {
-	char *dirname = g_path_get_dirname (filename);
+	char *dirname, *dirname_fs;
+	GError *err = NULL;
+	
+	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, 0700);
+	g_mkdir_with_parents (dirname_fs, 0700);
 
 	g_free (dirname);
+	g_free (dirname_fs);
 }
 
 static char *