vis
a vi-like editor based on Plan 9's structural regular expressions
git clone https://9o.is/git/vis.git
vis-single.c
(3195B)
1 #include <sys/wait.h>
2 #include <errno.h>
3 #include <fcntl.h>
4 #include <ftw.h>
5 #include <limits.h>
6 #include <signal.h>
7 #include <stdint.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <unistd.h>
12
13 #include <lzma.h>
14 #include <libuntar.h>
15
16 #ifndef PATH_MAX
17 #define PATH_MAX 4096
18 #endif
19
20 #include "vis-single-payload.inc"
21
22 #ifndef VIS_TMP_DIR
23 #define VIS_TMP_DIR "/tmp"
24 #endif
25
26 #ifndef VIS_TMP
27 #define VIS_TMP ".vis-single-XXXXXX"
28 #endif
29
30 #ifndef VIS_TERMINFO
31 #define VIS_TERMINFO "/etc/terminfo:/lib/terminfo:/usr/share/terminfo:" \
32 "/usr/lib/terminfo:/usr/local/share/terminfo:/usr/local/lib/terminfo"
33 #endif
34
35 static lzma_stream strm = LZMA_STREAM_INIT;
36
37 static int libtar_xzopen(const char *pathname, int flags, ...) {
38 int ret = lzma_stream_decoder(&strm, UINT64_MAX, LZMA_TELL_UNSUPPORTED_CHECK | LZMA_CONCATENATED);
39 if (ret != LZMA_OK) {
40 fprintf(stderr, "lzma_stream_decoder error: %d\n", ret);
41 return ret;
42 }
43
44 strm.next_in = vis_single_payload;
45 strm.avail_in = sizeof(vis_single_payload);
46
47 return ret;
48 }
49
50 static int libtar_xzclose(int fd) {
51 lzma_end(&strm);
52 return 0;
53 }
54
55 static ssize_t libtar_xzread(int fd, void *buf, size_t count) {
56 strm.next_out = buf;
57 strm.avail_out = count;
58
59 int ret = lzma_code(&strm, LZMA_FINISH);
60 if (ret != LZMA_OK && ret != LZMA_STREAM_END) {
61 fprintf(stderr, "lzma_code error: %d\n", ret);
62 return -1;
63 }
64
65 return count - strm.avail_out;
66 }
67
68 tartype_t xztype = {
69 libtar_xzopen,
70 libtar_xzclose,
71 libtar_xzread,
72 };
73
74 int extract(char *directory) {
75 TAR *tar;
76
77 if (tar_open(&tar, NULL, &xztype, O_RDONLY, 0, 0) == -1) {
78 perror("tar_open");
79 return -1;
80 }
81
82 if (tar_extract_all(tar, directory) != 0) {
83 perror("tar_extract_all");
84 return -1;
85 }
86
87 if (tar_close(tar) != 0) {
88 perror("tar_close");
89 return -1;
90 }
91
92 return 0;
93 }
94
95 static int unlink_cb(const char *path, const struct stat *sb, int typeflag, struct FTW *ftwbuf) {
96 return remove(path);
97 }
98
99 int main(int argc, char **argv) {
100 int rc = EXIT_FAILURE;
101 char exe[256], path[PATH_MAX], tmp_dirname[PATH_MAX];
102
103 char *tmpdir = getenv("TMPDIR");
104 if (snprintf(tmp_dirname, sizeof(tmp_dirname), "%s/%s",
105 tmpdir ? tmpdir : VIS_TMP_DIR, VIS_TMP) < 0) {
106 perror("snprintf");
107 return rc;
108 }
109
110 if (!mkdtemp(tmp_dirname)) {
111 perror("mkdtemp");
112 return rc;
113 }
114
115 char *old_path = getenv("PATH");
116 if (snprintf(path, sizeof(path), "%s%s%s", tmp_dirname,
117 old_path ? ":" : "", old_path ? old_path : "") < 0) {
118 goto err;
119 }
120
121 if (setenv("PATH", path, 1) == -1 ||
122 setenv("TERMINFO_DIRS", VIS_TERMINFO, 0) == -1) {
123 perror("setenv");
124 goto err;
125 }
126
127 if (extract(tmp_dirname) != 0)
128 goto err;
129
130 if (snprintf(exe, sizeof(exe), "%s/vis", tmp_dirname) < 0)
131 goto err;
132
133 int child_pid = fork();
134 if (child_pid == -1) {
135 perror("fork");
136 goto err;
137 } else if (child_pid == 0) {
138 execv(exe, argv);
139 perror("execv");
140 return EXIT_FAILURE;
141 }
142
143 signal(SIGINT, SIG_IGN);
144
145 for (;;) {
146 int status;
147 int w = waitpid(child_pid, &status, 0);
148 if (w == -1) {
149 perror("waitpid");
150 break;
151 }
152 if (w == child_pid) {
153 rc = WEXITSTATUS(status);
154 break;
155 }
156 }
157
158 err:
159 nftw(tmp_dirname, unlink_cb, 64, FTW_DEPTH|FTW_PHYS|FTW_MOUNT);
160 return rc;
161 }