main shrub/shrubtools / shar / shar.c
  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}