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

Annotation of /trunk/utils/tmpcat.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 69 - (hide annotations)
Sun Feb 16 17:51:44 2025 UTC (6 weeks, 2 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 rakinar2 69 /*
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