+ * Reads a string from the data input stream, up to the first
+ * occurrence of any of the stop characters.
+ *
+ * Note that, in contrast to g_data_input_stream_read_until_async(),
+ * this function consumes the stop character that it finds.
+ *
+ * Don't use this function in new code. Its functionality is
+ * inconsistent with g_data_input_stream_read_until_async(). Both
+ * functions will be marked as deprecated in a future release. Use
+ * g_data_input_stream_read_upto() instead, but note that that function
+ * does not consume the stop character.
+ *
+ * Returns: (transfer full): a string with the data that was read
+ * before encountering any of the stop characters. Set @length to
+ * a #gsize to get the length of the string. This function will
+ * return %NULL on an error.
+ */
+char *
+g_data_input_stream_read_until (GDataInputStream *stream,
+ const gchar *stop_chars,
+ gsize *length,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GBufferedInputStream *bstream;
+ gchar *result;
+
+ bstream = G_BUFFERED_INPUT_STREAM (stream);
+
+ result = g_data_input_stream_read_upto (stream, stop_chars, -1,
+ length, cancellable, error);
+
+ /* If we're not at end of stream then we have a stop_char to consume. */
+ if (result != NULL && g_buffered_input_stream_get_available (bstream) > 0)
+ {
+ gsize res;
+ gchar b;
+
+ res = g_input_stream_read (G_INPUT_STREAM (stream), &b, 1, NULL, NULL);
+ g_assert (res == 1);
+ }
+
+ return result;
+}
+
+typedef struct
+{
+ gboolean last_saw_cr;
+ gsize checked;
+
+ gchar *stop_chars;
+ gssize stop_chars_len;
+ gsize length;
+} GDataInputStreamReadData;
+
+static void
+g_data_input_stream_read_complete (GTask *task,
+ gsize read_length,
+ gsize skip_length)
+{
+ GDataInputStreamReadData *data = g_task_get_task_data (task);
+ GInputStream *stream = g_task_get_source_object (task);
+ char *line = NULL;
+
+ if (read_length || skip_length)
+ {
+ gssize bytes;
+
+ data->length = read_length;
+ line = g_malloc (read_length + 1);
+ line[read_length] = '\0';
+
+ /* we already checked the buffer. this shouldn't fail. */
+ bytes = g_input_stream_read (stream, line, read_length, NULL, NULL);
+ g_assert_cmpint (bytes, ==, read_length);
+
+ bytes = g_input_stream_skip (stream, skip_length, NULL, NULL);
+ g_assert_cmpint (bytes, ==, skip_length);
+ }
+
+ g_task_return_pointer (task, line, g_free);
+ g_object_unref (task);
+}
+
+static void
+g_data_input_stream_read_line_ready (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GTask *task = user_data;
+ GDataInputStreamReadData *data = g_task_get_task_data (task);
+ GBufferedInputStream *buffer = g_task_get_source_object (task);
+ gssize found_pos;
+ gint newline_len;
+
+ if (result)
+ /* this is a callback. finish the async call. */
+ {
+ GError *error = NULL;
+ gssize bytes;
+
+ bytes = g_buffered_input_stream_fill_finish (buffer, result, &error);
+
+ if (bytes <= 0)
+ {
+ if (bytes < 0)
+ /* stream error. */
+ {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ g_data_input_stream_read_complete (task, data->checked, 0);
+ return;
+ }
+
+ /* only proceed if we got more bytes... */
+ }
+
+ if (data->stop_chars)
+ {
+ found_pos = scan_for_chars (G_DATA_INPUT_STREAM (buffer),
+ &data->checked,
+ data->stop_chars,
+ data->stop_chars_len);
+ newline_len = 0;
+ }
+ else
+ found_pos = scan_for_newline (G_DATA_INPUT_STREAM (buffer), &data->checked,
+ &data->last_saw_cr, &newline_len);
+
+ if (found_pos == -1)
+ /* didn't find a full line; need to buffer some more bytes */
+ {
+ gsize size;
+
+ size = g_buffered_input_stream_get_buffer_size (buffer);
+
+ if (g_buffered_input_stream_get_available (buffer) == size)
+ /* need to grow the buffer */
+ g_buffered_input_stream_set_buffer_size (buffer, size * 2);
+
+ /* try again */
+ g_buffered_input_stream_fill_async (buffer, -1,
+ g_task_get_priority (task),
+ g_task_get_cancellable (task),
+ g_data_input_stream_read_line_ready,
+ user_data);
+ }
+ else
+ {
+ /* read the line and the EOL. no error is possible. */
+ g_data_input_stream_read_complete (task, found_pos, newline_len);
+ }
+}
+
+static void
+g_data_input_stream_read_data_free (gpointer user_data)
+{
+ GDataInputStreamReadData *data = user_data;
+
+ g_free (data->stop_chars);
+ g_slice_free (GDataInputStreamReadData, data);
+}
+
+static void
+g_data_input_stream_read_async (GDataInputStream *stream,
+ const gchar *stop_chars,
+ gssize stop_chars_len,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GDataInputStreamReadData *data;
+ GTask *task;
+
+ data = g_slice_new0 (GDataInputStreamReadData);
+ if (stop_chars_len == -1)
+ stop_chars_len = strlen (stop_chars);
+ data->stop_chars = g_memdup (stop_chars, stop_chars_len);
+ data->stop_chars_len = stop_chars_len;
+ data->last_saw_cr = FALSE;
+
+ task = g_task_new (stream, cancellable, callback, user_data);
+ g_task_set_task_data (task, data, g_data_input_stream_read_data_free);
+ g_task_set_priority (task, io_priority);
+
+ g_data_input_stream_read_line_ready (NULL, NULL, task);
+}
+
+static gchar *
+g_data_input_stream_read_finish (GDataInputStream *stream,
+ GAsyncResult *result,
+ gsize *length,
+ GError **error)
+{
+ GTask *task = G_TASK (result);
+ gchar *line;
+
+ line = g_task_propagate_pointer (task, error);
+
+ if (length && line)
+ {
+ GDataInputStreamReadData *data = g_task_get_task_data (task);
+
+ *length = data->length;
+ }
+
+ return line;
+}
+
+/**
+ * g_data_input_stream_read_line_async:
+ * @stream: a given #GDataInputStream.
+ * @io_priority: the [I/O priority][io-priority] of the request
+ * @cancellable: (allow-none): optional #GCancellable object, %NULL to ignore.
+ * @callback: (scope async): callback to call when the request is satisfied.
+ * @user_data: (closure): the data to pass to callback function.
+ *
+ * The asynchronous version of g_data_input_stream_read_line(). It is
+ * an error to have two outstanding calls to this function.
+ *
+ * When the operation is finished, @callback will be called. You
+ * can then call g_data_input_stream_read_line_finish() to get
+ * the result of the operation.
+ *
+ * Since: 2.20
+ */
+void
+g_data_input_stream_read_line_async (GDataInputStream *stream,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (G_IS_DATA_INPUT_STREAM (stream));
+ g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+
+ g_data_input_stream_read_async (stream, NULL, 0, io_priority,
+ cancellable, callback, user_data);
+}
+
+/**
+ * g_data_input_stream_read_until_async:
+ * @stream: a given #GDataInputStream.
+ * @stop_chars: characters to terminate the read.
+ * @io_priority: the [I/O priority][io-priority] of the request
+ * @cancellable: (allow-none): optional #GCancellable object, %NULL to ignore.
+ * @callback: (scope async): callback to call when the request is satisfied.
+ * @user_data: (closure): the data to pass to callback function.
+ *
+ * The asynchronous version of g_data_input_stream_read_until().
+ * It is an error to have two outstanding calls to this function.
+ *
+ * Note that, in contrast to g_data_input_stream_read_until(),
+ * this function does not consume the stop character that it finds. You
+ * must read it for yourself.
+ *
+ * When the operation is finished, @callback will be called. You
+ * can then call g_data_input_stream_read_until_finish() to get
+ * the result of the operation.
+ *
+ * Don't use this function in new code. Its functionality is
+ * inconsistent with g_data_input_stream_read_until(). Both functions
+ * will be marked as deprecated in a future release. Use
+ * g_data_input_stream_read_upto_async() instead.
+ *
+ * Since: 2.20
+ */
+void
+g_data_input_stream_read_until_async (GDataInputStream *stream,
+ const gchar *stop_chars,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (G_IS_DATA_INPUT_STREAM (stream));
+ g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+ g_return_if_fail (stop_chars != NULL);
+
+ g_data_input_stream_read_async (stream, stop_chars, -1, io_priority,
+ cancellable, callback, user_data);
+}
+
+/**
+ * g_data_input_stream_read_line_finish:
+ * @stream: a given #GDataInputStream.
+ * @result: the #GAsyncResult that was provided to the callback.
+ * @length: (out): a #gsize to get the length of the data read in.
+ * @error: #GError for error reporting.
+ *
+ * Finish an asynchronous call started by
+ * g_data_input_stream_read_line_async(). Note the warning about
+ * string encoding in g_data_input_stream_read_line() applies here as
+ * well.
+ *
+ * Returns: (nullable) (transfer full) (array zero-terminated=1) (element-type guint8):
+ * a NUL-terminated byte array with the line that was read in
+ * (without the newlines). Set @length to a #gsize to get the length
+ * of the read line. On an error, it will return %NULL and @error
+ * will be set. If there's no content to read, it will still return
+ * %NULL, but @error won't be set.
+ *
+ * Since: 2.20
+ */
+gchar *
+g_data_input_stream_read_line_finish (GDataInputStream *stream,
+ GAsyncResult *result,
+ gsize *length,
+ GError **error)
+{
+ g_return_val_if_fail (g_task_is_valid (result, stream), NULL);
+
+ return g_data_input_stream_read_finish (stream, result, length, error);
+}
+
+/**
+ * g_data_input_stream_read_line_finish_utf8:
+ * @stream: a given #GDataInputStream.
+ * @result: the #GAsyncResult that was provided to the callback.
+ * @length: (out): a #gsize to get the length of the data read in.
+ * @error: #GError for error reporting.
+ *
+ * Finish an asynchronous call started by
+ * g_data_input_stream_read_line_async().
+ *
+ * Returns: (nullable) (transfer full): a string with the line that
+ * was read in (without the newlines). Set @length to a #gsize to
+ * get the length of the read line. On an error, it will return
+ * %NULL and @error will be set. For UTF-8 conversion errors, the set
+ * error domain is %G_CONVERT_ERROR. If there's no content to read,
+ * it will still return %NULL, but @error won't be set.
+ *
+ * Since: 2.30
+ */
+gchar *
+g_data_input_stream_read_line_finish_utf8 (GDataInputStream *stream,
+ GAsyncResult *result,
+ gsize *length,
+ GError **error)
+{
+ gchar *res;
+
+ res = g_data_input_stream_read_line_finish (stream, result, length, error);
+ if (!res)
+ return NULL;
+
+ if (!g_utf8_validate (res, -1, NULL))
+ {
+ g_set_error_literal (error, G_CONVERT_ERROR,
+ G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
+ _("Invalid byte sequence in conversion input"));
+ g_free (res);
+ return NULL;
+ }
+ return res;
+}
+
+/**
+ * g_data_input_stream_read_until_finish:
+ * @stream: a given #GDataInputStream.
+ * @result: the #GAsyncResult that was provided to the callback.
+ * @length: (out): a #gsize to get the length of the data read in.
+ * @error: #GError for error reporting.
+ *
+ * Finish an asynchronous call started by
+ * g_data_input_stream_read_until_async().
+ *
+ * Since: 2.20
+ *
+ * Returns: (transfer full): a string with the data that was read
+ * before encountering any of the stop characters. Set @length to
+ * a #gsize to get the length of the string. This function will
+ * return %NULL on an error.
+ */
+gchar *
+g_data_input_stream_read_until_finish (GDataInputStream *stream,
+ GAsyncResult *result,
+ gsize *length,
+ GError **error)
+{
+ g_return_val_if_fail (g_task_is_valid (result, stream), NULL);
+
+ return g_data_input_stream_read_finish (stream, result, length, error);
+}
+
+/**
+ * g_data_input_stream_read_upto:
+ * @stream: a #GDataInputStream
+ * @stop_chars: characters to terminate the read
+ * @stop_chars_len: length of @stop_chars. May be -1 if @stop_chars is
+ * nul-terminated
+ * @length: (out): a #gsize to get the length of the data read in
+ * @cancellable: (allow-none): optional #GCancellable object, %NULL to ignore
+ * @error: #GError for error reporting
+ *
+ * Reads a string from the data input stream, up to the first
+ * occurrence of any of the stop characters.
+ *
+ * In contrast to g_data_input_stream_read_until(), this function
+ * does not consume the stop character. You have to use
+ * g_data_input_stream_read_byte() to get it before calling
+ * g_data_input_stream_read_upto() again.
+ *
+ * Note that @stop_chars may contain '\0' if @stop_chars_len is
+ * specified.
+ *
+ * Returns: (transfer full): a string with the data that was read
+ * before encountering any of the stop characters. Set @length to
+ * a #gsize to get the length of the string. This function will
+ * return %NULL on an error
+ *
+ * Since: 2.26
+ */