1/*small shar in C inspired by netbsd shar script but with some
2 * improvements, public domain :] */
3
4#include <sys/stat.h>
5#include <sys/types.h>
6
7#include <alloc.h>
8#include <buffer.h>
9#include <byte.h>
10#include <fmt.h>
11#include <open.h>
12#include <str.h>
13#include <strerr.h>
14
15#include <unistd.h>
16
17static void
18die_usage(void)
19{
20 buffer_puts(buffer_2, "usage: shar [file ...]\n");
21 buffer_flush(buffer_2);
22 _exit(1);
23}
24
25static void
26die_nomem(void)
27{
28 strerr_die2sys(111, "shar: ", "alloc");
29}
30
31static void
32warn_path(const char *path)
33{
34 strerr_warn4("shar: ", path, ": ", "", &strerr_sys);
35}
36
37static void
38out(const char *s)
39{
40 buffer_puts(buffer_1, s);
41}
42
43static void
44outn(const char *s, unsigned int n)
45{
46 buffer_put(buffer_1, s, n);
47}
48
49static void
50outc(char c)
51{
52 buffer_put(buffer_1, &c, 1);
53}
54
55static void
56out_ulong(unsigned long u)
57{
58 char buf[FMT_ULONG];
59 unsigned int n;
60
61 n = fmt_ulong(buf, u);
62 outn(buf, n);
63}
64
65static void
66out_mode(unsigned int mode)
67{
68 char buf[3];
69
70 buf[0] = '0' + ((mode >> 6) & 7);
71 buf[1] = '0' + ((mode >> 3) & 7);
72 buf[2] = '0' + (mode & 7);
73 outn(buf, sizeof(buf));
74}
75
76/* single quote path name */
77static void
78q(char *s)
79{
80 outc('\'');
81 for (; *s; s++)
82 if (*s == '\'')
83 out("'\\''");
84 else
85 outc(*s);
86 outc('\'');
87}
88
89static void
90qt(char *s, char *t)
91{
92 q(s);
93 out(t);
94}
95
96static void
97mkpar(char *s)
98{
99 char *p, *e;
100 size_t n;
101
102 e = s + str_rchr(s, '/');
103 if (*e == '\0' || e == s)
104 return;
105 n = (size_t)(e - s);
106 p = alloc((unsigned int)n + 1);
107 if (!p)
108 die_nomem();
109 byte_copy(p, n, s);
110 p[n] = 0;
111 out("mkdir -p ");
112 q(p);
113 out(" > /dev/null 2>&1\n");
114 alloc_free(p);
115}
116
117static int
118readfile(char *s)
119{
120 int fd, r, bol;
121 char buf[4096];
122 struct stat st;
123 unsigned int i;
124
125 fd = open_read(s);
126 if (fd == -1) {
127 warn_path(s);
128 return 1;
129 }
130 if (stat(s, &st) == -1) {
131 warn_path(s);
132 close(fd);
133 return 1;
134 }
135 mkpar(s);
136 out("echo x - ");
137 q(s);
138 out("\nsed 's/^X//' >");
139 q(s);
140 out(" << 'SHAR_EOF'\n");
141 bol = 1;
142 for (;;) {
143 r = read(fd, buf, sizeof(buf));
144 if (r == 0)
145 break;
146 if (r < 0) {
147 warn_path(s);
148 close(fd);
149 return 1;
150 }
151 for (i = 0; i < (unsigned int)r; ++i) {
152 if (bol)
153 outc('X');
154 outc(buf[i]);
155 bol = buf[i] == '\n';
156 }
157 }
158 close(fd);
159 /* netbsd shar glues the marker to non newline files but we fix and trim */
160 if (!bol)
161 outc('\n');
162 out("SHAR_EOF\n");
163 if (!bol) {
164 out("dd if=");
165 q(s);
166 out(" of=");
167 qt(s, ".$$");
168 out(" bs=1 count=");
169 out_ulong((unsigned long)st.st_size);
170 out(" > /dev/null 2>&1 && mv ");
171 qt(s, ".$$");
172 outc(' ');
173 q(s);
174 outc('\n');
175 }
176 out("chmod ");
177 out_mode((unsigned int)st.st_mode & 0777);
178 outc(' ');
179 q(s);
180 outc('\n');
181 return 0;
182}
183
184static void
185makedir(char *s, mode_t m)
186{
187 out("echo c - ");
188 q(s);
189 out("\nmkdir -p ");
190 q(s);
191 out(" > /dev/null 2>&1\n");
192 out("chmod ");
193 out_mode((unsigned int)m & 0777);
194 outc(' ');
195 q(s);
196 outc('\n');
197}
198
199static void
200head(int n, char **v)
201{
202 int i;
203
204 out("# This is a shell archive. Save it in a file, remove anything before\n"
205 "# this line, and then unpack it by entering \"sh file\". Note, it may\n"
206 "# create directories; files and directories will be owned by you and\n"
207 "# have their original permissions.\n"
208 "#\n"
209 "# This archive contains:\n"
210 "#\n");
211 for (i = 1; i < n; i++) {
212 out("#\t");
213 out(v[i]);
214 outc('\n');
215 }
216 out("#\n");
217}
218
219int
220main(int n, char **v)
221{
222 struct stat st;
223 int i;
224
225 if (n == 1)
226 die_usage();
227 head(n, v);
228 for (i = 1; i < n; i++)
229 if (stat(v[i], &st) == 0 && S_ISDIR(st.st_mode))
230 makedir(v[i], st.st_mode);
231 else if (readfile(v[i]))
232 return 1;
233 out("exit\n\n");
234 buffer_flush(buffer_1);
235 return 0;
236}