1 /*
2 * Copyright 2015 Red Hat, Inc.
3 *
4 * SPDX-License-Identifier: LGPL-2.1-or-later
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18 *
19 * Author: Matthias Clasen <mclasen@redhat.com>
20 */
21
22 #include "config.h"
23
24 #include <gio/gio.h>
25 #include <gi18n.h>
26 #include <errno.h>
27
28 #ifdef G_OS_WIN32
29 #include <io.h>
30 #endif
31
32 #ifndef STDOUT_FILENO
33 #define STDOUT_FILENO 1
34 #endif
35
36 #ifdef HAVE_UNISTD_H
37 #include <unistd.h>
38 #endif
39
40 #include "gio-tool.h"
41
42
43 static const GOptionEntry entries[] = {
44 G_OPTION_ENTRY_NULL
45 };
46
47 /* 256k minus malloc overhead */
48 #define STREAM_BUFFER_SIZE (1024*256 - 2*sizeof(gpointer))
49
50 static gboolean
51 cat (GFile *file)
52 {
53 GInputStream *in;
54 char *buffer;
55 char *p;
56 gssize res;
57 gboolean close_res;
58 GError *error;
59 gboolean success;
60
61 error = NULL;
62 in = (GInputStream *) g_file_read (file, NULL, &error);
63 if (in == NULL)
64 {
65 print_file_error (file, error->message);
66 g_error_free (error);
67 return FALSE;
68 }
69
70 buffer = g_malloc (STREAM_BUFFER_SIZE);
71 success = TRUE;
72 while (1)
73 {
74 res = g_input_stream_read (in, buffer, STREAM_BUFFER_SIZE, NULL, &error);
75 if (res > 0)
76 {
77 gssize written;
78
79 p = buffer;
80 while (res > 0)
81 {
82 int errsv;
83
84 written = write (STDOUT_FILENO, p, res);
85 errsv = errno;
86
87 if (written == -1 && errsv != EINTR)
88 {
89 print_error ("%s", _("Error writing to stdout"));
90 success = FALSE;
91 goto out;
92 }
93 res -= written;
94 p += written;
95 }
96 }
97 else if (res < 0)
98 {
99 print_file_error (file, error->message);
100 g_error_free (error);
101 error = NULL;
102 success = FALSE;
103 break;
104 }
105 else if (res == 0)
106 break;
107 }
108
109 out:
110 close_res = g_input_stream_close (in, NULL, &error);
111 if (!close_res)
112 {
113 print_file_error (file, error->message);
114 g_error_free (error);
115 success = FALSE;
116 }
117
118 g_free (buffer);
119
120 return success;
121 }
122
123 int
124 handle_cat (int argc, char *argv[], gboolean do_help)
125 {
126 GOptionContext *context;
127 gchar *param;
128 GError *error = NULL;
129 int i;
130 gboolean res;
131 GFile *file;
132
133 g_set_prgname ("gio cat");
134 /* Translators: commandline placeholder */
135 param = g_strdup_printf ("%s…", _("LOCATION"));
136 context = g_option_context_new (param);
137 g_free (param);
138 g_option_context_set_help_enabled (context, FALSE);
139 g_option_context_set_summary (context,
140 _("Concatenate files and print to standard output."));
141 g_option_context_set_description (context,
142 _("gio cat works just like the traditional cat utility, but using GIO\n"
143 "locations instead of local files: for example, you can use something\n"
144 "like smb://server/resource/file.txt as location."));
145 g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
146
147 if (do_help)
148 {
149 show_help (context, NULL);
150 g_option_context_free (context);
151 return 0;
152 }
153
154 if (!g_option_context_parse (context, &argc, &argv, &error))
155 {
156 show_help (context, error->message);
157 g_error_free (error);
158 g_option_context_free (context);
159 return 1;
160 }
161
162 if (argc < 2)
163 {
164 show_help (context, _("No locations given"));
165 g_option_context_free (context);
166 return 1;
167 }
168
169 g_option_context_free (context);
170
171 res = TRUE;
172 for (i = 1; i < argc; i++)
173 {
174 file = g_file_new_for_commandline_arg (argv[i]);
175 res &= cat (file);
176 g_object_unref (file);
177 }
178
179 return res ? 0 : 2;
180 }