/[osn-commons]/trunk/utils/tmpcat.c
ViewVC logotype

Contents of /trunk/utils/tmpcat.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 69 - (show annotations)
Sun Feb 16 17:51:44 2025 UTC (5 weeks, 5 days ago) by rakinar2
File MIME type: text/x-c
File size: 9520 byte(s)
feat(utils): tmpcat program

This program allows you to create a temporary file and open your editor
to edit it. Then once you exit out of the editor, it will echo the data
in the file to stdout, which can then be piped to another process or used
in other ways.

1 /*
2 * tmpcat.c -- A simple utility to write an input in your editor and cat it.
3 *
4 * This file is part of OSN Commons.
5 * Copyright (C) 2025 OSN Developers.
6 *
7 * OSN Commons is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * any later version.
11 *
12 * OSN Commons is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with OSN Commons. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include <errno.h>
22 #include <getopt.h>
23 #include <stdbool.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <sys/types.h>
28 #include <sys/wait.h>
29 #include <unistd.h>
30
31 #ifdef HAVE_CONFIG_H
32 # include "config.h"
33 #endif
34
35 #define PROG_CANONICAL_NAME "tmpcat"
36 #define PROG_AUTHORS "Ar Rakin <[email protected]>"
37
38 /* TODO: Add support for more file types, and
39 output statistics separately for each file type. */
40
41 static const char *prog_name = NULL;
42
43 static struct option const long_options[] = {
44 { "help", no_argument, 0, 'h' },
45 { "version", no_argument, 0, 'v' },
46 { 0, 0, 0, 0 }
47 };
48
49 static const char *short_options = "hv";
50
51 struct editor_data
52 {
53 unsigned char *buffer;
54 size_t size;
55 };
56
57 static void
58 usage (void)
59 {
60 fprintf (stdout, "Usage:\n");
61 fprintf (stdout, " %s [OPTIONS...] [EDITOR]\n", PROG_CANONICAL_NAME);
62 fprintf (
63 stdout,
64 "Allows you to write an input in your editor and echo it out.\n\n");
65 fprintf (stdout, "Options:\n");
66 fprintf (stdout, " -h, --help Show this help message and exit.\n");
67 fprintf (stdout, " -v, --version Show version info and exit.\n");
68 fprintf (stdout, "\n");
69 fprintf (stdout,
70 "For general help and bug reports, feel free to send your\n");
71 fprintf (stdout, "message to <[email protected]>.\n");
72 }
73
74 static void
75 show_version (void)
76 {
77 printf (PROG_CANONICAL_NAME " (" PACKAGE_FULLNAME ") v" PACKAGE_VERSION
78 "\n");
79 fputc ('\n', stdout);
80 printf ("License GPLv3+: GNU GPL version 3 or later "
81 "<http://gnu.org/licenses/gpl.html>.\n");
82 printf (
83 "This is free software: you are free to change and redistribute it.\n");
84 printf ("There is NO WARRANTY, to the extent permitted by law.\n");
85 fputc ('\n', stdout);
86 printf ("Written by " PROG_AUTHORS ".\n");
87 }
88
89 static char **
90 split_string (const char *str, const char delim)
91 {
92 char **splitted = NULL;
93 char *buf = NULL;
94 size_t size = 0;
95 size_t buf_size = 0;
96
97 while (true)
98 {
99 char c = *str;
100
101 buf = realloc (buf, ++buf_size);
102
103 if (c == delim || !c)
104 {
105 buf[buf_size - 1] = 0;
106 splitted = realloc (splitted, (++size) * (sizeof (char *)));
107 splitted[size - 1] = buf;
108 buf = NULL;
109 buf_size = 0;
110
111 if (!c)
112 break;
113 }
114 else
115 buf[buf_size - 1] = c;
116
117 str++;
118 }
119
120 splitted = realloc (splitted, (++size) * (sizeof (char *)));
121 splitted[size - 1] = NULL;
122
123 return splitted;
124 }
125
126 static void
127 free_str_r (char **strv)
128 {
129 char **base = strv;
130
131 while (*strv)
132 free (*strv++);
133
134 free (base);
135 }
136
137 static void *
138 path_concat (const char *s1, const char *s2)
139 {
140 size_t len1 = strlen (s1);
141 size_t len2 = strlen (s2);
142 size_t len = len1 + len2 + 1;
143 char *str = malloc (len + 1);
144
145 memcpy (str, s1, len1);
146 str[len1] = '/';
147 memcpy (str + len1 + 1, s2, len2);
148 str[len] = 0;
149
150 return str;
151 }
152
153 static char *
154 find_executable (const char *name)
155 {
156 if (strchr (name, '/') != NULL)
157 return strdup (name);
158
159 const char *path_env = getenv ("PATH");
160 path_env = path_env == NULL ? "" : path_env;
161
162 char **paths = split_string (path_env, ':');
163 char **base = paths;
164 char *result = NULL;
165
166 while (*paths)
167 {
168 char *path = path_concat (*paths, name);
169
170 if (access (path, R_OK) == 0)
171 {
172 result = path;
173 break;
174 }
175
176 free (path);
177 paths++;
178 }
179
180 free_str_r (base);
181 return result;
182 }
183
184 static struct editor_data *
185 read_editor_data (const char *editor_path, size_t editor_argc,
186 const char **editor_args)
187 {
188 char *executable = find_executable (editor_path);
189
190 if (executable == NULL)
191 {
192 fprintf (stderr, "%s: no executable '%s' found in $PATH\n",
193 prog_name, editor_path);
194 exit (EXIT_FAILURE);
195 }
196
197 char tmpfile_template[] = "/tmp/tmpXXXXXXXXX";
198 int tmpfile_fd = mkstemp (tmpfile_template);
199
200 pid_t pid = fork ();
201
202 if (pid == -1)
203 {
204 fprintf (stderr, "%s: fork failed: %s\n", prog_name,
205 strerror (errno));
206 exit (EXIT_FAILURE);
207 }
208
209 if (pid == 0)
210 {
211 char **args = malloc ((editor_argc + 3) * sizeof (char *));
212
213 if (args == NULL)
214 {
215 fprintf (stderr, "%s: malloc failed: %s\n", prog_name,
216 strerror (errno));
217 exit (EXIT_FAILURE);
218 }
219
220 args[0] = executable;
221 args[editor_argc + 1] = strdup (tmpfile_template);
222 args[editor_argc + 2] = NULL;
223
224 for (size_t i = 0; i < editor_argc; i++)
225 args[i + 1] = strdup (editor_args[i]);
226
227 execv (executable, args);
228 fprintf (stderr, "%s: failed to execute '%s': %s\n", prog_name,
229 executable, strerror (errno));
230 exit (EXIT_FAILURE);
231 }
232 else
233 {
234 int status;
235
236 if (waitpid (pid, &status, 0) == -1)
237 {
238 fprintf (stderr, "%s: could not wait for child: %s\n",
239 prog_name, strerror (errno));
240 exit (EXIT_FAILURE);
241 }
242
243 if (WIFEXITED (status) && WEXITSTATUS (status) != 0)
244 {
245 fprintf (stderr, "%s: '%s' failed with exit status %d\n",
246 prog_name, executable, WEXITSTATUS (status));
247 exit (EXIT_FAILURE);
248 }
249
250 if (WIFSIGNALED (status))
251 {
252 fprintf (stderr, "%s: '%s' terminated by signal %s\n",
253 prog_name, executable,
254 strsignal (WTERMSIG (status)));
255 exit (EXIT_FAILURE);
256 }
257
258 struct editor_data *data = malloc (sizeof (struct editor_data));
259 off_t size = lseek (tmpfile_fd, 0, SEEK_END);
260
261 if (size < 0)
262 {
263 fprintf (stderr, "%s: lseek failed: %s\n", prog_name,
264 strerror (errno));
265 exit (EXIT_FAILURE);
266 }
267
268 if (lseek (tmpfile_fd, 0, SEEK_SET) < 0)
269 {
270 fprintf (stderr, "%s: lseek failed: %s\n", prog_name,
271 strerror (errno));
272 exit (EXIT_FAILURE);
273 }
274
275 data->buffer = malloc (size + 1);
276 data->size = size;
277
278 if (read (tmpfile_fd, data->buffer, (size_t) size) == -1)
279 {
280 fprintf (stderr, "%s: cannot read temporary file: %s\n",
281 prog_name, strerror (errno));
282 exit (EXIT_FAILURE);
283 }
284
285 close (tmpfile_fd);
286 free (executable);
287 return data;
288 }
289
290 return NULL;
291 }
292
293 void
294 free_editor_data (struct editor_data *data)
295 {
296 free (data->buffer);
297 free (data);
298 }
299
300 int
301 main (int argc, char **argv)
302 {
303 prog_name = argv[0];
304
305 while (true)
306 {
307 int longind = 0;
308 int c = getopt_long (argc, argv, short_options, long_options,
309 &longind);
310
311 if (c == -1)
312 break;
313
314 switch (c)
315 {
316 case 'h':
317 usage ();
318 exit (EXIT_SUCCESS);
319 break;
320
321 case 'v':
322 show_version ();
323 exit (EXIT_SUCCESS);
324 break;
325
326 case '?':
327 exit (EXIT_FAILURE);
328 break;
329 }
330 }
331
332 const char *editor = getenv ("EDITOR");
333
334 if (argc > optind)
335 editor = argv[optind];
336
337 if (editor == NULL)
338 editor = "editor";
339
340 struct editor_data *data
341 = read_editor_data (editor, argc - 1 > optind ? argc - optind - 1 : 0,
342 (const char **) (argv + optind + 1));
343
344 if (write (STDOUT_FILENO, data->buffer, data->size) == -1)
345 {
346 fprintf (stderr, "%s: unable to write to stdout: %s\n", prog_name,
347 strerror (errno));
348 exit (EXIT_FAILURE);
349 }
350
351 free_editor_data (data);
352 return 0;
353 }

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26