blob: 6a2e7d983eea4abf2abd9f7aa039fd2a3fbda172 [file] [log] [blame]
landleycd9dfc32006-10-18 18:38:16 -04001/* vi: set sw=4 ts=4 :*/
Charlie Shepherd54524c92008-01-25 12:36:24 +00002/* lib.c - reusable stuff.
landley4f344e32006-10-05 16:18:03 -04003 *
landleycd9dfc32006-10-18 18:38:16 -04004 * Functions with the x prefix are wrappers for library functions. They either
5 * succeed or kill the program with an error message, but never return failure.
6 * They usually have the same arguments and return value as the function they
7 * wrap.
landley09ea7ac2006-10-30 01:38:00 -05008 *
9 * Copyright 2006 Rob Landley <rob@landley.net>
landley4f344e32006-10-05 16:18:03 -040010 */
11
12#include "toys.h"
13
Rob Landleye15850a2007-11-19 01:51:00 -060014// Strcpy with size checking: exit if there's not enough space for the string.
15void xstrcpy(char *dest, char *src, size_t size)
Rob Landley18d43ff2007-06-07 15:19:44 -040016{
Rob Landleye15850a2007-11-19 01:51:00 -060017 if (strlen(src)+1 > size) error_exit("xstrcpy");
18 strcpy(dest, src);
Rob Landley18d43ff2007-06-07 15:19:44 -040019}
Rob Landley18d43ff2007-06-07 15:19:44 -040020
landley09ea7ac2006-10-30 01:38:00 -050021void verror_msg(char *msg, int err, va_list va)
22{
Rob Landley12138e42008-01-27 15:26:08 -060023 char *s = ": %s";
24
landley09ea7ac2006-10-30 01:38:00 -050025 fprintf(stderr, "%s: ", toys.which->name);
Rob Landley12138e42008-01-27 15:26:08 -060026 if (msg) vfprintf(stderr, msg, va);
27 else s+=2;
28 if (err) fprintf(stderr, s, strerror(err));
landley09ea7ac2006-10-30 01:38:00 -050029 putc('\n', stderr);
30}
31
32void error_msg(char *msg, ...)
33{
34 va_list va;
35
36 va_start(va, msg);
37 verror_msg(msg, 0, va);
38 va_end(va);
39}
40
41void perror_msg(char *msg, ...)
42{
43 va_list va;
44
45 va_start(va, msg);
46 verror_msg(msg, errno, va);
47 va_end(va);
48}
49
landley4f344e32006-10-05 16:18:03 -040050// Die with an error message.
51void error_exit(char *msg, ...)
52{
landley09ea7ac2006-10-30 01:38:00 -050053 va_list va;
landley4f344e32006-10-05 16:18:03 -040054
Rob Landleyd06c58d2007-10-11 15:36:36 -050055 if (CFG_HELP && toys.exithelp) {
56 *toys.optargs=*toys.argv;
Rob Landley55928b12008-01-19 17:43:27 -060057 USE_HELP(help_main();) // dear gcc: shut up.
Rob Landleyd06c58d2007-10-11 15:36:36 -050058 fprintf(stderr,"\n");
59 }
60
landley09ea7ac2006-10-30 01:38:00 -050061 va_start(va, msg);
62 verror_msg(msg, 0, va);
63 va_end(va);
64
Rob Landleyaaffc072007-12-09 15:35:42 -060065 exit(!toys.exitval ? 1 : toys.exitval);
landley09ea7ac2006-10-30 01:38:00 -050066}
67
Rob Landley055cfcb2007-01-14 20:20:06 -050068
landley09ea7ac2006-10-30 01:38:00 -050069// Die with an error message and strerror(errno)
70void perror_exit(char *msg, ...)
71{
72 va_list va;
73
74 va_start(va, msg);
75 verror_msg(msg, errno, va);
76 va_end(va);
77
Rob Landleyaaffc072007-12-09 15:35:42 -060078 exit(!toys.exitval ? 1 : toys.exitval);
landley4f344e32006-10-05 16:18:03 -040079}
80
landley4f344e32006-10-05 16:18:03 -040081// Die unless we can allocate memory.
82void *xmalloc(size_t size)
83{
84 void *ret = malloc(size);
85 if (!ret) error_exit("xmalloc");
landleycd9dfc32006-10-18 18:38:16 -040086
87 return ret;
landley4f344e32006-10-05 16:18:03 -040088}
89
landleycd9dfc32006-10-18 18:38:16 -040090// Die unless we can allocate prezeroed memory.
91void *xzalloc(size_t size)
92{
93 void *ret = xmalloc(size);
Rob Landley2b54b1a2012-02-18 22:44:11 -060094 memset(ret, 0, size);
landleycd9dfc32006-10-18 18:38:16 -040095 return ret;
96}
97
98// Die unless we can change the size of an existing allocation, possibly
99// moving it. (Notice different arguments from libc function.)
Rob Landley0c93f6c2007-04-29 19:55:21 -0400100void *xrealloc(void *ptr, size_t size)
landleycd9dfc32006-10-18 18:38:16 -0400101{
Rob Landley0c93f6c2007-04-29 19:55:21 -0400102 ptr = realloc(ptr, size);
103 if (!ptr) error_exit("xrealloc");
104
105 return ptr;
landleycd9dfc32006-10-18 18:38:16 -0400106}
107
Rob Landleyfa98d012006-11-02 02:57:27 -0500108// Die unless we can allocate a copy of this many bytes of string.
Rob Landley1e01cd12010-01-05 10:48:32 -0600109char *xstrndup(char *s, size_t n)
landley4f344e32006-10-05 16:18:03 -0400110{
Rob Landley3fc4e0f2008-04-13 00:29:00 -0500111 char *ret = xmalloc(++n);
112 strncpy(ret, s, n);
113 ret[--n]=0;
Rob Landley2c226852007-11-15 18:30:30 -0600114
landley4f344e32006-10-05 16:18:03 -0400115 return ret;
116}
117
Rob Landleyfa98d012006-11-02 02:57:27 -0500118// Die unless we can allocate a copy of this string.
Rob Landley1e01cd12010-01-05 10:48:32 -0600119char *xstrdup(char *s)
Rob Landleyfa98d012006-11-02 02:57:27 -0500120{
Rob Landleyf6418542008-01-27 16:22:41 -0600121 return xstrndup(s, strlen(s));
Rob Landleyfa98d012006-11-02 02:57:27 -0500122}
123
landley00f87f12006-10-25 18:38:37 -0400124// Die unless we can allocate enough space to sprintf() into.
125char *xmsprintf(char *format, ...)
126{
Rob Landley0d8dfb22007-06-15 15:16:46 -0400127 va_list va, va2;
landley00f87f12006-10-25 18:38:37 -0400128 int len;
129 char *ret;
Rob Landley2c226852007-11-15 18:30:30 -0600130
landley00f87f12006-10-25 18:38:37 -0400131 va_start(va, format);
Rob Landley0d8dfb22007-06-15 15:16:46 -0400132 va_copy(va2, va);
133
134 // How long is it?
landley00f87f12006-10-25 18:38:37 -0400135 len = vsnprintf(0, 0, format, va);
136 len++;
137 va_end(va);
138
139 // Allocate and do the sprintf()
140 ret = xmalloc(len);
Rob Landley2c226852007-11-15 18:30:30 -0600141 vsnprintf(ret, len, format, va2);
Rob Landley0d8dfb22007-06-15 15:16:46 -0400142 va_end(va2);
landley00f87f12006-10-25 18:38:37 -0400143
144 return ret;
145}
146
Rob Landley24d1d452007-01-20 18:04:20 -0500147void xprintf(char *format, ...)
148{
149 va_list va;
150 va_start(va, format);
151
152 vprintf(format, va);
153 if (ferror(stdout)) perror_exit("write");
154}
155
Rob Landley5084fea2007-06-18 00:14:03 -0400156void xputs(char *s)
157{
158 if (EOF == puts(s)) perror_exit("write");
159}
160
Rob Landley24d1d452007-01-20 18:04:20 -0500161void xputc(char c)
162{
163 if (EOF == fputc(c, stdout)) perror_exit("write");
164}
165
166void xflush(void)
167{
168 if (fflush(stdout)) perror_exit("write");;
169}
170
landleycd9dfc32006-10-18 18:38:16 -0400171// Die unless we can exec argv[] (or run builtin command). Note that anything
172// with a path isn't a builtin, so /bin/sh won't match the builtin sh.
landley09ea7ac2006-10-30 01:38:00 -0500173void xexec(char **argv)
landley4f344e32006-10-05 16:18:03 -0400174{
landleycd9dfc32006-10-18 18:38:16 -0400175 toy_exec(argv);
landley4f344e32006-10-05 16:18:03 -0400176 execvp(argv[0], argv);
Rob Landley26e7b5e2012-02-02 07:27:35 -0600177
178 perror_exit("exec %s", argv[0]);
landley4f344e32006-10-05 16:18:03 -0400179}
180
Rob Landleyd3e9d642007-01-08 03:25:47 -0500181void xaccess(char *path, int flags)
182{
Charlie Shepherd94dd3e72008-01-25 12:54:31 +0000183 if (access(path, flags)) perror_exit("Can't access '%s'", path);
Rob Landleyd3e9d642007-01-08 03:25:47 -0500184}
185
Rob Landleye745d8e2007-12-20 06:30:19 -0600186// Die unless we can delete a file. (File must exist to be deleted.)
187void xunlink(char *path)
188{
189 if (unlink(path)) perror_exit("unlink '%s'", path);
190}
191
landley4f344e32006-10-05 16:18:03 -0400192// Die unless we can open/create a file, returning file descriptor.
Rob Landley1322beb2007-01-07 22:51:12 -0500193int xcreate(char *path, int flags, int mode)
landley4f344e32006-10-05 16:18:03 -0400194{
195 int fd = open(path, flags, mode);
Rob Landley961e1712007-11-04 15:31:06 -0600196 if (fd == -1) perror_exit("%s", path);
landley4f344e32006-10-05 16:18:03 -0400197 return fd;
198}
199
Rob Landley1322beb2007-01-07 22:51:12 -0500200// Die unless we can open a file, returning file descriptor.
201int xopen(char *path, int flags)
202{
203 return xcreate(path, flags, 0);
204}
205
Rob Landleybc078652007-12-15 21:47:25 -0600206void xclose(int fd)
207{
208 if (close(fd)) perror_exit("xclose");
209}
210
landley4f344e32006-10-05 16:18:03 -0400211// Die unless we can open/create a file, returning FILE *.
212FILE *xfopen(char *path, char *mode)
213{
214 FILE *f = fopen(path, mode);
Charlie Shepherd94dd3e72008-01-25 12:54:31 +0000215 if (!f) perror_exit("No file %s", path);
landley4f344e32006-10-05 16:18:03 -0400216 return f;
217}
landley00f87f12006-10-25 18:38:37 -0400218
landley64b2e232006-10-30 10:01:19 -0500219// Keep reading until full or EOF
Rob Landley90163772007-01-18 21:54:08 -0500220ssize_t readall(int fd, void *buf, size_t len)
landley64b2e232006-10-30 10:01:19 -0500221{
Rob Landley90163772007-01-18 21:54:08 -0500222 size_t count = 0;
Rob Landley15b23152008-07-18 04:15:59 -0500223
Rob Landley90163772007-01-18 21:54:08 -0500224 while (count<len) {
225 int i = read(fd, buf+count, len-count);
Rob Landley15b23152008-07-18 04:15:59 -0500226 if (!i) break;
landley64b2e232006-10-30 10:01:19 -0500227 if (i<0) return i;
228 count += i;
229 }
230
231 return count;
232}
233
Rob Landleyf3e452a2007-01-08 02:49:39 -0500234// Keep writing until done or EOF
Rob Landley90163772007-01-18 21:54:08 -0500235ssize_t writeall(int fd, void *buf, size_t len)
Rob Landleyf3e452a2007-01-08 02:49:39 -0500236{
Rob Landley90163772007-01-18 21:54:08 -0500237 size_t count = 0;
238 while (count<len) {
239 int i = write(fd, buf+count, len-count);
240 if (i<1) return i;
Rob Landleyf3e452a2007-01-08 02:49:39 -0500241 count += i;
242 }
243
244 return count;
245}
246
Rob Landley055cfcb2007-01-14 20:20:06 -0500247// Die if there's an error other than EOF.
Rob Landley90163772007-01-18 21:54:08 -0500248size_t xread(int fd, void *buf, size_t len)
landley64b2e232006-10-30 10:01:19 -0500249{
Rob Landleyf265d042011-12-28 13:01:12 -0600250 ssize_t ret = read(fd, buf, len);
251 if (ret < 0) perror_exit("xread");
Rob Landley055cfcb2007-01-14 20:20:06 -0500252
Rob Landleyf265d042011-12-28 13:01:12 -0600253 return ret;
Rob Landley055cfcb2007-01-14 20:20:06 -0500254}
255
Rob Landley90163772007-01-18 21:54:08 -0500256void xreadall(int fd, void *buf, size_t len)
Rob Landley055cfcb2007-01-14 20:20:06 -0500257{
Rob Landley90163772007-01-18 21:54:08 -0500258 if (len != readall(fd, buf, len)) perror_exit("xreadall");
Rob Landley055cfcb2007-01-14 20:20:06 -0500259}
landley00f87f12006-10-25 18:38:37 -0400260
Rob Landley90163772007-01-18 21:54:08 -0500261// There's no xwriteall(), just xwrite(). When we read, there may or may not
262// be more data waiting. When we write, there is data and it had better go
263// somewhere.
264
265void xwrite(int fd, void *buf, size_t len)
Rob Landleyf3e452a2007-01-08 02:49:39 -0500266{
Rob Landley90163772007-01-18 21:54:08 -0500267 if (len != writeall(fd, buf, len)) perror_exit("xwrite");
Rob Landleyf3e452a2007-01-08 02:49:39 -0500268}
269
Rob Landley52476712009-01-18 16:19:25 -0600270// Die if lseek fails, probably due to being called on a pipe.
271
272off_t xlseek(int fd, off_t offset, int whence)
273{
274 offset = lseek(fd, offset, whence);
275 if (offset<0) perror_exit("lseek");
276
277 return offset;
278}
279
landley00f87f12006-10-25 18:38:37 -0400280char *xgetcwd(void)
281{
282 char *buf = getcwd(NULL, 0);
Rob Landley24d1d452007-01-20 18:04:20 -0500283 if (!buf) perror_exit("xgetcwd");
landley09ea7ac2006-10-30 01:38:00 -0500284
285 return buf;
landley00f87f12006-10-25 18:38:37 -0400286}
287
Rob Landleyd25f7e42007-02-03 14:11:26 -0500288void xstat(char *path, struct stat *st)
289{
Rob Landleyf6418542008-01-27 16:22:41 -0600290 if(stat(path, st)) perror_exit("Can't stat %s", path);
Rob Landleyd25f7e42007-02-03 14:11:26 -0500291}
292
Rob Landleyfa98d012006-11-02 02:57:27 -0500293// Cannonicalizes path by removing ".", "..", and "//" elements. This is not
Rob Landleyc6f481c2006-12-30 22:01:47 -0500294// the same as realpath(), where "dir/.." could wind up somewhere else by
295// following symlinks.
Rob Landleyfa98d012006-11-02 02:57:27 -0500296char *xabspath(char *path)
landley00f87f12006-10-25 18:38:37 -0400297{
Rob Landleyfa98d012006-11-02 02:57:27 -0500298 char *from, *to;
landley00f87f12006-10-25 18:38:37 -0400299
Rob Landleyfa98d012006-11-02 02:57:27 -0500300 // If this isn't an absolute path, make it one with cwd.
301 if (path[0]!='/') {
302 char *cwd=xgetcwd();
Rob Landleyf6418542008-01-27 16:22:41 -0600303 path = xmsprintf("%s/%s", cwd, path);
Rob Landleyfa98d012006-11-02 02:57:27 -0500304 free(cwd);
305 } else path = xstrdup(path);
landley00f87f12006-10-25 18:38:37 -0400306
Rob Landleyfa98d012006-11-02 02:57:27 -0500307 // Loop through path elements
308 from = to = path;
309 while (*from) {
310
311 // Continue any current path component.
312 if (*from!='/') {
313 *(to++) = *(from++);
314 continue;
landley00f87f12006-10-25 18:38:37 -0400315 }
Rob Landleyfa98d012006-11-02 02:57:27 -0500316
317 // Skip duplicate slashes.
318 while (*from=='/') from++;
Rob Landley2c226852007-11-15 18:30:30 -0600319
Rob Landleyfa98d012006-11-02 02:57:27 -0500320 // Start of a new filename. Handle . and ..
321 while (*from=='.') {
322 // Skip .
323 if (from[1]=='/') from += 2;
324 else if (!from[1]) from++;
325 // Back up for ..
326 else if (from[1]=='.') {
327 if (from[2]=='/') from +=3;
328 else if(!from[2]) from+=2;
329 else break;
330 while (to>path && *(--to)!='/');
331 } else break;
332 }
333 // Add directory separator slash.
334 *(to++) = '/';
335 }
336 *to = 0;
337
338 return path;
339}
340
Rob Landley988abb32008-05-12 00:52:27 -0500341void xchdir(char *path)
342{
Rob Landleyddae5e92009-01-25 16:34:46 -0600343 if (chdir(path)) error_exit("chdir '%s'", path);
Rob Landley988abb32008-05-12 00:52:27 -0500344}
345
Rob Landley35483412007-12-27 21:36:33 -0600346// Ensure entire path exists.
347// If mode != -1 set permissions on newly created dirs.
348// Requires that path string be writable (for temporary null terminators).
349void xmkpath(char *path, int mode)
350{
351 char *p, old;
352 mode_t mask;
353 int rc;
354 struct stat st;
355
356 for (p = path; ; p++) {
357 if (!*p || *p == '/') {
358 old = *p;
359 *p = rc = 0;
360 if (stat(path, &st) || !S_ISDIR(st.st_mode)) {
361 if (mode != -1) {
362 mask=umask(0);
363 rc = mkdir(path, mode);
364 umask(mask);
365 } else rc = mkdir(path, 0777);
366 }
367 *p = old;
Rob Landleyf6418542008-01-27 16:22:41 -0600368 if(rc) perror_exit("mkpath '%s'", path);
Rob Landley35483412007-12-27 21:36:33 -0600369 }
370 if (!*p) break;
371 }
372}
Rob Landleye0377fb2010-01-05 12:17:05 -0600373
374// setuid() can fail (for example, too many processes belonging to that user),
375// which opens a security hole if the process continues as the original user.
376
377void xsetuid(uid_t uid)
378{
379 if (setuid(uid)) perror_exit("xsetuid");
380}
381
382
Rob Landley0a04b3e2006-11-03 00:05:52 -0500383// Find all file in a colon-separated path with access type "type" (generally
384// X_OK or R_OK). Returns a list of absolute paths to each file found, in
385// order.
386
387struct string_list *find_in_path(char *path, char *filename)
Rob Landleyfa98d012006-11-02 02:57:27 -0500388{
Rob Landley59f490c2008-05-17 17:52:51 -0500389 struct string_list *rlist = NULL, **prlist=&rlist;
Rob Landley0a04b3e2006-11-03 00:05:52 -0500390 char *cwd = xgetcwd();
Rob Landleyfa98d012006-11-02 02:57:27 -0500391
392 for (;;) {
Rob Landleyb7529b62012-03-08 20:14:55 -0600393 char *next = path ? strchr(path, ':') : NULL;
Rob Landleyfa98d012006-11-02 02:57:27 -0500394 int len = next ? next-path : strlen(path);
Rob Landley0a04b3e2006-11-03 00:05:52 -0500395 struct string_list *rnext;
396 struct stat st;
Rob Landleyfa98d012006-11-02 02:57:27 -0500397
Rob Landley0a04b3e2006-11-03 00:05:52 -0500398 rnext = xmalloc(sizeof(void *) + strlen(filename)
399 + (len ? len : strlen(cwd)) + 2);
400 if (!len) sprintf(rnext->str, "%s/%s", cwd, filename);
Rob Landleyfa98d012006-11-02 02:57:27 -0500401 else {
Rob Landley0a04b3e2006-11-03 00:05:52 -0500402 char *res = rnext->str;
Rob Landleyfa98d012006-11-02 02:57:27 -0500403 strncpy(res, path, len);
Rob Landley0a04b3e2006-11-03 00:05:52 -0500404 res += len;
405 *(res++) = '/';
406 strcpy(res, filename);
Rob Landleyfa98d012006-11-02 02:57:27 -0500407 }
408
Rob Landley0a04b3e2006-11-03 00:05:52 -0500409 // Confirm it's not a directory.
410 if (!stat(rnext->str, &st) && S_ISREG(st.st_mode)) {
Rob Landley59f490c2008-05-17 17:52:51 -0500411 *prlist = rnext;
412 rnext->next = NULL;
413 prlist = &(rnext->next);
Rob Landley0a04b3e2006-11-03 00:05:52 -0500414 } else free(rnext);
Rob Landleyfa98d012006-11-02 02:57:27 -0500415
Rob Landleyfa98d012006-11-02 02:57:27 -0500416 if (!next) break;
417 path += len;
418 path++;
landley00f87f12006-10-25 18:38:37 -0400419 }
420 free(cwd);
421
Rob Landley0a04b3e2006-11-03 00:05:52 -0500422 return rlist;
landley00f87f12006-10-25 18:38:37 -0400423}
landley09ea7ac2006-10-30 01:38:00 -0500424
425// Convert unsigned int to ascii, writing into supplied buffer. A truncated
426// result contains the first few digits of the result ala strncpy, and is
427// always null terminated (unless buflen is 0).
428void utoa_to_buf(unsigned n, char *buf, unsigned buflen)
429{
430 int i, out = 0;
431
432 if (buflen) {
433 for (i=1000000000; i; i/=10) {
434 int res = n/i;
435
436 if ((res || out || i == 1) && --buflen>0) {
437 out++;
438 n -= res*i;
439 *buf++ = '0' + res;
440 }
441 }
442 *buf = 0;
443 }
444}
445
446// Convert signed integer to ascii, using utoa_to_buf()
447void itoa_to_buf(int n, char *buf, unsigned buflen)
448{
449 if (buflen && n<0) {
450 n = -n;
451 *buf++ = '-';
452 buflen--;
453 }
454 utoa_to_buf((unsigned)n, buf, buflen);
455}
456
457// This static buffer is used by both utoa() and itoa(), calling either one a
458// second time will overwrite the previous results.
459//
460// The longest 32 bit integer is -2 billion plus a null terminator: 12 bytes.
461// Note that int is always 32 bits on any remotely unix-like system, see
462// http://www.unix.org/whitepapers/64bit.html for details.
463
464static char itoa_buf[12];
465
466// Convert unsigned integer to ascii, returning a static buffer.
467char *utoa(unsigned n)
468{
469 utoa_to_buf(n, itoa_buf, sizeof(itoa_buf));
470
471 return itoa_buf;
472}
473
474char *itoa(int n)
475{
476 itoa_to_buf(n, itoa_buf, sizeof(itoa_buf));
477
478 return itoa_buf;
479}
Rob Landley055cfcb2007-01-14 20:20:06 -0500480
Rob Landleyf5757162007-02-16 21:08:22 -0500481// atol() with the kilo/mega/giga/tera/peta/exa extensions.
482// (zetta and yotta don't fit in 64 bits.)
Rob Landley5e6dca62012-02-09 06:09:27 -0600483long atolx(char *numstr)
Rob Landleyf5757162007-02-16 21:08:22 -0500484{
Rob Landley5e6dca62012-02-09 06:09:27 -0600485 char *c, *suffixes="kmgtpe", *end;
486 long val = strtol(numstr, &c, 0);
Rob Landleyf5757162007-02-16 21:08:22 -0500487
Rob Landley6a6dee32007-11-04 15:32:59 -0600488 if (*c) {
489 end = strchr(suffixes, tolower(*c));
490 if (end) val *= 1024L<<((end-suffixes)*10);
Rob Landley5e6dca62012-02-09 06:09:27 -0600491 else {
492 while (isspace(*c)) c++;
493 if (*c) error_exit("not integer: %s", numstr);
494 }
Rob Landley6a6dee32007-11-04 15:32:59 -0600495 }
Rob Landleyad63f4b2011-12-12 15:19:52 -0600496
Rob Landleyf5757162007-02-16 21:08:22 -0500497 return val;
498}
499
Rob Landley055cfcb2007-01-14 20:20:06 -0500500// Return how long the file at fd is, if there's any way to determine it.
501off_t fdlength(int fd)
502{
Rob Landleyf15387d2008-07-18 05:43:44 -0500503 off_t bottom = 0, top = 0, pos, old;
Rob Landleye2580db2007-01-23 13:20:38 -0500504 int size;
Rob Landley055cfcb2007-01-14 20:20:06 -0500505
506 // If the ioctl works for this, return it.
507
Rob Landleye2580db2007-01-23 13:20:38 -0500508 if (ioctl(fd, BLKGETSIZE, &size) >= 0) return size*512L;
Rob Landley055cfcb2007-01-14 20:20:06 -0500509
510 // If not, do a binary search for the last location we can read. (Some
511 // block devices don't do BLKGETSIZE right.) This should probably have
512 // a CONFIG option...
513
Rob Landleyf15387d2008-07-18 05:43:44 -0500514 old = lseek(fd, 0, SEEK_CUR);
Rob Landley055cfcb2007-01-14 20:20:06 -0500515 do {
516 char temp;
517
518 pos = bottom + (top - bottom) / 2;
519
520 // If we can read from the current location, it's bigger.
521
Rob Landleyb3a33822007-01-25 16:10:37 -0500522 if (lseek(fd, pos, 0)>=0 && read(fd, &temp, 1)==1) {
Rob Landley055cfcb2007-01-14 20:20:06 -0500523 if (bottom == top) bottom = top = (top+1) * 2;
524 else bottom = pos;
525
526 // If we can't, it's smaller.
527
528 } else {
529 if (bottom == top) {
530 if (!top) return 0;
531 bottom = top/2;
532 } else top = pos;
533 }
534 } while (bottom + 1 != top);
535
Rob Landleyf15387d2008-07-18 05:43:44 -0500536 lseek(fd, old, SEEK_SET);
537
Rob Landley055cfcb2007-01-14 20:20:06 -0500538 return pos + 1;
539}
540
Rob Landley0c93f6c2007-04-29 19:55:21 -0400541// This can return null (meaning file not found). It just won't return null
542// for memory allocation reasons.
543char *xreadlink(char *name)
544{
545 int len, size = 0;
546 char *buf = 0;
547
548 // Grow by 64 byte chunks until it's big enough.
549 for(;;) {
550 size +=64;
551 buf = xrealloc(buf, size);
552 len = readlink(name, buf, size);
553
554 if (len<0) {
555 free(buf);
556 return 0;
557 }
558 if (len<size) {
559 buf[len]=0;
560 return buf;
561 }
562 }
563}
564
Rob Landleyb3a33822007-01-25 16:10:37 -0500565/*
566 This might be of use or might not. Unknown yet...
567
Rob Landley055cfcb2007-01-14 20:20:06 -0500568// Read contents of file as a single freshly allocated nul-terminated string.
569char *readfile(char *name)
570{
571 off_t len;
572 int fd;
573 char *buf;
574
Rob Landley2c226852007-11-15 18:30:30 -0600575 fd = open(name, O_RDONLY);
Rob Landley055cfcb2007-01-14 20:20:06 -0500576 if (fd == -1) return 0;
577 len = fdlength(fd);
578 buf = xmalloc(len+1);
Rob Landley8d43d912011-11-13 21:05:28 -0600579 buf[readall(fd, buf, len)] = 0;
Rob Landley055cfcb2007-01-14 20:20:06 -0500580
581 return buf;
582}
583
584char *xreadfile(char *name)
585{
586 char *buf = readfile(name);
Rob Landley24d1d452007-01-20 18:04:20 -0500587 if (!buf) perror_exit("xreadfile %s", name);
Rob Landley055cfcb2007-01-14 20:20:06 -0500588 return buf;
589}
590
591*/
592
593// Open a /var/run/NAME.pid file, dying if we can't write it or if it currently
594// exists and is this executable.
595void xpidfile(char *name)
596{
597 char pidfile[256], spid[32];
598 int i, fd;
599 pid_t pid;
600
601 sprintf(pidfile, "/var/run/%s.pid", name);
602 // Try three times to open the sucker.
603 for (i=0; i<3; i++) {
604 fd = open(pidfile, O_CREAT|O_EXCL, 0644);
605 if (fd != -1) break;
606
607 // If it already existed, read it. Loop for race condition.
608 fd = open(pidfile, O_RDONLY);
609 if (fd == -1) continue;
610
611 // Is the old program still there?
612 spid[xread(fd, spid, sizeof(spid)-1)] = 0;
613 close(fd);
614 pid = atoi(spid);
615 if (fd < 1 || kill(pid, 0) == ESRCH) unlink(pidfile);
616
617 // An else with more sanity checking might be nice here.
618 }
Rob Landley2c226852007-11-15 18:30:30 -0600619
Rob Landley055cfcb2007-01-14 20:20:06 -0500620 if (i == 3) error_exit("xpidfile %s", name);
621
622 xwrite(fd, spid, sprintf(spid, "%ld\n", (long)getpid()));
623 close(fd);
624}
Rob Landley7634b552007-11-29 17:49:50 -0600625
Rob Landley2bfaaf22008-07-03 19:19:00 -0500626// Iterate through an array of files, opening each one and calling a function
627// on that filehandle and name. The special filename "-" means stdin if
628// flags is O_RDONLY, stdout otherwise. An empty argument list calls
629// function() on just stdin/stdout.
630//
631// Note: read only filehandles are automatically closed when function()
632// returns, but writeable filehandles must be close by function()
Rob Landleyad63f4b2011-12-12 15:19:52 -0600633void loopfiles_rw(char **argv, int flags, int permissions, int failok,
634 void (*function)(int fd, char *name))
Rob Landley7634b552007-11-29 17:49:50 -0600635{
636 int fd;
637
638 // If no arguments, read from stdin.
Rob Landley2bfaaf22008-07-03 19:19:00 -0500639 if (!*argv) function(flags ? 1 : 0, "-");
Rob Landley7634b552007-11-29 17:49:50 -0600640 else do {
641 // Filename "-" means read from stdin.
642 // Inability to open a file prints a warning, but doesn't exit.
643
644 if (!strcmp(*argv,"-")) fd=0;
Rob Landleyad63f4b2011-12-12 15:19:52 -0600645 else if (0>(fd = open(*argv, flags, permissions)) && !failok) {
Rob Landleyf6418542008-01-27 16:22:41 -0600646 perror_msg("%s", *argv);
Rob Landley7634b552007-11-29 17:49:50 -0600647 toys.exitval = 1;
Rob Landley3632d5d2008-01-01 02:39:29 -0600648 continue;
Rob Landley7634b552007-11-29 17:49:50 -0600649 }
650 function(fd, *argv);
Rob Landley2bfaaf22008-07-03 19:19:00 -0500651 if (!flags) close(fd);
Rob Landley7634b552007-11-29 17:49:50 -0600652 } while (*++argv);
653}
Rob Landleybc078652007-12-15 21:47:25 -0600654
Rob Landleyad63f4b2011-12-12 15:19:52 -0600655// Call loopfiles_rw with O_RDONLY and !failok (common case).
Rob Landley2bfaaf22008-07-03 19:19:00 -0500656void loopfiles(char **argv, void (*function)(int fd, char *name))
657{
Rob Landleyad63f4b2011-12-12 15:19:52 -0600658 loopfiles_rw(argv, O_RDONLY, 0, 0, function);
Rob Landley2bfaaf22008-07-03 19:19:00 -0500659}
660
Rob Landleybc078652007-12-15 21:47:25 -0600661// Slow, but small.
662
Rob Landley3fc4e0f2008-04-13 00:29:00 -0500663char *get_rawline(int fd, long *plen, char end)
Rob Landleybc078652007-12-15 21:47:25 -0600664{
665 char c, *buf = NULL;
666 long len = 0;
667
668 for (;;) {
669 if (1>read(fd, &c, 1)) break;
Rob Landleyd6b26132009-04-16 17:03:38 -0500670 if (!(len & 63)) buf=xrealloc(buf, len+65);
Rob Landley3fc4e0f2008-04-13 00:29:00 -0500671 if ((buf[len++]=c) == end) break;
Rob Landleybc078652007-12-15 21:47:25 -0600672 }
673 if (buf) buf[len]=0;
674 if (plen) *plen = len;
675
676 return buf;
677}
678
679char *get_line(int fd)
680{
681 long len;
Rob Landley3fc4e0f2008-04-13 00:29:00 -0500682 char *buf = get_rawline(fd, &len, '\n');
Rob Landleybc078652007-12-15 21:47:25 -0600683
684 if (buf && buf[--len]=='\n') buf[len]=0;
685
686 return buf;
687}
688
689// Copy the rest of in to out and close both files.
690
691void xsendfile(int in, int out)
692{
693 long len;
Rob Landley42ecbab2007-12-18 02:02:21 -0600694 char buf[4096];
Rob Landleybc078652007-12-15 21:47:25 -0600695
696 if (in<0) return;
697 for (;;) {
Rob Landley42ecbab2007-12-18 02:02:21 -0600698 len = xread(in, buf, 4096);
Rob Landleybc078652007-12-15 21:47:25 -0600699 if (len<1) break;
Rob Landley42ecbab2007-12-18 02:02:21 -0600700 xwrite(out, buf, len);
Rob Landleybc078652007-12-15 21:47:25 -0600701 }
Rob Landley42ecbab2007-12-18 02:02:21 -0600702}
703
704// Open a temporary file to copy an existing file into.
705int copy_tempfile(int fdin, char *name, char **tempname)
706{
707 struct stat statbuf;
708 int fd;
709
710 *tempname = xstrndup(name, strlen(name)+6);
711 strcat(*tempname,"XXXXXX");
712 if(-1 == (fd = mkstemp(*tempname))) error_exit("no temp file");
713
714 // Set permissions of output file
715
716 fstat(fdin, &statbuf);
717 fchmod(fd, statbuf.st_mode);
718
719 return fd;
720}
721
722// Abort the copy and delete the temporary file.
723void delete_tempfile(int fdin, int fdout, char **tempname)
724{
725 close(fdin);
726 close(fdout);
727 unlink(*tempname);
728 free(*tempname);
729 *tempname = NULL;
730}
731
732// Copy the rest of the data and replace the original with the copy.
733void replace_tempfile(int fdin, int fdout, char **tempname)
734{
735 char *temp = xstrdup(*tempname);
736
737 temp[strlen(temp)-6]=0;
738 if (fdin != -1) {
739 xsendfile(fdin, fdout);
740 xclose(fdin);
741 }
742 xclose(fdout);
743 rename(*tempname, temp);
744 free(*tempname);
745 free(temp);
746 *tempname = NULL;
Rob Landleybc078652007-12-15 21:47:25 -0600747}
Rob Landley7e849c52009-01-03 18:15:18 -0600748
749// Create a 256 entry CRC32 lookup table.
750
Rob Landleyb15b8fa2009-01-05 01:05:43 -0600751void crc_init(unsigned int *crc_table, int little_endian)
Rob Landley7e849c52009-01-03 18:15:18 -0600752{
753 unsigned int i;
754
755 // Init the CRC32 table (big endian)
756 for (i=0; i<256; i++) {
Rob Landleyb15b8fa2009-01-05 01:05:43 -0600757 unsigned int j, c = little_endian ? i : i<<24;
Rob Landley7e849c52009-01-03 18:15:18 -0600758 for (j=8; j; j--)
Rob Landleyb15b8fa2009-01-05 01:05:43 -0600759 if (little_endian) c = (c&1) ? (c>>1)^0xEDB88320 : c>>1;
760 else c=c&0x80000000 ? (c<<1)^0x04c11db7 : (c<<1);
Rob Landley7e849c52009-01-03 18:15:18 -0600761 crc_table[i] = c;
762 }
763}
Rob Landley26e7b5e2012-02-02 07:27:35 -0600764
765// Quick and dirty query size of terminal, doesn't do ANSI probe fallback.
766// set *x=0 and *y=0 before calling to detect failure to set either, or
767// x=80 y=25 to provide defaults
768
769void terminal_size(unsigned *x, unsigned *y)
770{
771 struct winsize ws;
772 int i;
773
774 //memset(&ws, 0, sizeof(ws));
775 for (i=0; i<3; i++) {
776 if (ioctl(i, TIOCGWINSZ, &ws)) continue;
777 if (x) *x = ws.ws_col;
778 if (y) *y = ws.ws_row;
779 }
780 if (x) {
781 char *s = getenv("COLUMNS");
782
783 i = s ? atoi(s) : 0;
784 if (i>0) *x = i;
785 }
786 if (y) {
787 char *s = getenv("ROWS");
788
789 i = s ? atoi(s) : 0;
790 if (i>0) *y = i;
791 }
792}
793
794// This should use a raw tty, fixit later.
Rob Landleyf793d532012-02-27 21:56:49 -0600795int yesno(char *prompt, int def)
Rob Landley26e7b5e2012-02-02 07:27:35 -0600796{
Rob Landleyee00a7f2012-03-19 19:19:21 -0500797 FILE *fp = fopen("/dev/tty", "rw");
Rob Landleyf793d532012-02-27 21:56:49 -0600798 char buf;
Rob Landley26e7b5e2012-02-02 07:27:35 -0600799
Rob Landleyee00a7f2012-03-19 19:19:21 -0500800 if (!fp) return 1;
Rob Landley26e7b5e2012-02-02 07:27:35 -0600801
Rob Landleyee00a7f2012-03-19 19:19:21 -0500802 fprintf(fp, "%s (%c/%c):", prompt, def ? 'Y' : 'y', def ? 'n' : 'N');
803 while (fread(&buf, 1, 1, fp)) {
804 if (tolower(buf) == 'y') def = 1;
805 if (tolower(buf) == 'n') def = 0;
806 else if (!isspace(buf)) continue;
807
808 break;
Rob Landley26e7b5e2012-02-02 07:27:35 -0600809 }
Rob Landleyee00a7f2012-03-19 19:19:21 -0500810 fclose(fp);
811
Rob Landley26e7b5e2012-02-02 07:27:35 -0600812 return def;
813}
Rob Landleyff9ee8f2012-02-18 15:12:41 -0600814
815// Execute a callback for each PID that matches a process name from a list.
Rob Landleyf42e11b2012-02-18 18:09:14 -0600816void for_each_pid_with_name_in(char **names, void (*callback)(pid_t pid))
817{
Rob Landleyff9ee8f2012-02-18 15:12:41 -0600818 DIR *dp;
819 struct dirent *entry;
Rob Landleyf42e11b2012-02-18 18:09:14 -0600820 char cmd[PATH_MAX], path[64];
Rob Landleyff9ee8f2012-02-18 15:12:41 -0600821 char **curname;
822
Rob Landleyf42e11b2012-02-18 18:09:14 -0600823 if (!(dp = opendir("/proc"))) perror_exit("opendir");
Rob Landleyff9ee8f2012-02-18 15:12:41 -0600824
825 while ((entry = readdir(dp))) {
Rob Landleyf42e11b2012-02-18 18:09:14 -0600826 int fd;
Rob Landleyff9ee8f2012-02-18 15:12:41 -0600827
Rob Landleyf42e11b2012-02-18 18:09:14 -0600828 if (!isdigit(*entry->d_name)) continue;
Rob Landleyff9ee8f2012-02-18 15:12:41 -0600829
Rob Landleyf42e11b2012-02-18 18:09:14 -0600830 if (sizeof(path) <= snprintf(path, sizeof(path), "/proc/%s/cmdline",
831 entry->d_name)) continue;
Rob Landleyff9ee8f2012-02-18 15:12:41 -0600832
Rob Landleyf42e11b2012-02-18 18:09:14 -0600833 if (-1 != (fd=xopen(path, O_RDONLY))) {
834 int n = read(fd, cmd, sizeof(cmd));
Rob Landleyff9ee8f2012-02-18 15:12:41 -0600835
Rob Landleyf42e11b2012-02-18 18:09:14 -0600836 close(fd);
837 if (n<1) continue;
Rob Landleyff9ee8f2012-02-18 15:12:41 -0600838
Rob Landleyf42e11b2012-02-18 18:09:14 -0600839 for (curname = names; *curname; curname++)
840 if (!strcmp(basename(cmd), *curname))
841 callback(atol(entry->d_name));
Rob Landleyff9ee8f2012-02-18 15:12:41 -0600842 }
843 }
844
845 closedir(dp);
Rob Landleyff9ee8f2012-02-18 15:12:41 -0600846}
Rob Landley2dd50ad2012-02-26 13:48:00 -0600847
848struct signame {
849 int num;
850 char *name;
851};
852
853// Signals required by POSIX 2008:
854// http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/signal.h.html
855
856#define SIGNIFY(x) {SIG##x, #x}
857
858static struct signame signames[] = {
859 SIGNIFY(ABRT), SIGNIFY(ALRM), SIGNIFY(BUS), SIGNIFY(CHLD), SIGNIFY(CONT),
860 SIGNIFY(FPE), SIGNIFY(HUP), SIGNIFY(ILL), SIGNIFY(INT), SIGNIFY(KILL),
861 SIGNIFY(PIPE), SIGNIFY(QUIT), SIGNIFY(SEGV), SIGNIFY(STOP), SIGNIFY(TERM),
862 SIGNIFY(TSTP), SIGNIFY(TTIN), SIGNIFY(TTOU), SIGNIFY(USR1), SIGNIFY(USR2),
863 SIGNIFY(SYS), SIGNIFY(TRAP), SIGNIFY(URG), SIGNIFY(VTALRM), SIGNIFY(XCPU),
864 SIGNIFY(XFSZ)
865};
866
867// not in posix: SIGNIFY(STKFLT), SIGNIFY(WINCH), SIGNIFY(IO), SIGNIFY(PWR)
868// obsolete: SIGNIFY(PROF) SIGNIFY(POLL)
869
870// Convert name to signal number. If name == NULL print names.
871int sig_to_num(char *pidstr)
872{
873 int i;
874
875 if (pidstr) {
876 char *s;
877 i = strtol(pidstr, &s, 10);
878 if (!*s) return i;
879
880 if (!strncasecmp(pidstr, "sig", 3)) pidstr+=3;
881 }
882 for (i = 0; i < sizeof(signames)/sizeof(struct signame); i++)
883 if (!pidstr) xputs(signames[i].name);
884 else if (!strcasecmp(pidstr, signames[i].name))
885 return signames[i].num;
886
887 return -1;
888}
889
890char *num_to_sig(int sig)
891{
892 int i;
893
894 for (i=0; i<sizeof(signames)/sizeof(struct signame); i++)
895 if (signames[i].num == sig) return signames[i].name;
896 return NULL;
897}