10f66f451Sopenharmony_ci/* lib.c - various reusable stuff. 20f66f451Sopenharmony_ci * 30f66f451Sopenharmony_ci * Copyright 2006 Rob Landley <rob@landley.net> 40f66f451Sopenharmony_ci */ 50f66f451Sopenharmony_ci 60f66f451Sopenharmony_ci#define SYSLOG_NAMES 70f66f451Sopenharmony_ci#include "toys.h" 80f66f451Sopenharmony_ci 90f66f451Sopenharmony_civoid verror_msg(char *msg, int err, va_list va) 100f66f451Sopenharmony_ci{ 110f66f451Sopenharmony_ci char *s = ": %s"; 120f66f451Sopenharmony_ci 130f66f451Sopenharmony_ci fprintf(stderr, "%s: ", toys.which->name); 140f66f451Sopenharmony_ci if (msg) vfprintf(stderr, msg, va); 150f66f451Sopenharmony_ci else s+=2; 160f66f451Sopenharmony_ci if (err>0) fprintf(stderr, s, strerror(err)); 170f66f451Sopenharmony_ci if (err<0 && CFG_TOYBOX_HELP) 180f66f451Sopenharmony_ci fprintf(stderr, " (see \"%s --help\")", toys.which->name); 190f66f451Sopenharmony_ci if (msg || err) putc('\n', stderr); 200f66f451Sopenharmony_ci if (!toys.exitval) toys.exitval++; 210f66f451Sopenharmony_ci} 220f66f451Sopenharmony_ci 230f66f451Sopenharmony_ci// These functions don't collapse together because of the va_stuff. 240f66f451Sopenharmony_ci 250f66f451Sopenharmony_civoid error_msg(char *msg, ...) 260f66f451Sopenharmony_ci{ 270f66f451Sopenharmony_ci va_list va; 280f66f451Sopenharmony_ci 290f66f451Sopenharmony_ci va_start(va, msg); 300f66f451Sopenharmony_ci verror_msg(msg, 0, va); 310f66f451Sopenharmony_ci va_end(va); 320f66f451Sopenharmony_ci} 330f66f451Sopenharmony_ci 340f66f451Sopenharmony_civoid perror_msg(char *msg, ...) 350f66f451Sopenharmony_ci{ 360f66f451Sopenharmony_ci va_list va; 370f66f451Sopenharmony_ci 380f66f451Sopenharmony_ci va_start(va, msg); 390f66f451Sopenharmony_ci verror_msg(msg, errno, va); 400f66f451Sopenharmony_ci va_end(va); 410f66f451Sopenharmony_ci} 420f66f451Sopenharmony_ci 430f66f451Sopenharmony_ci// Die with an error message. 440f66f451Sopenharmony_civoid error_exit(char *msg, ...) 450f66f451Sopenharmony_ci{ 460f66f451Sopenharmony_ci va_list va; 470f66f451Sopenharmony_ci 480f66f451Sopenharmony_ci va_start(va, msg); 490f66f451Sopenharmony_ci verror_msg(msg, 0, va); 500f66f451Sopenharmony_ci va_end(va); 510f66f451Sopenharmony_ci 520f66f451Sopenharmony_ci xexit(); 530f66f451Sopenharmony_ci} 540f66f451Sopenharmony_ci 550f66f451Sopenharmony_ci// Die with an error message and strerror(errno) 560f66f451Sopenharmony_civoid perror_exit(char *msg, ...) 570f66f451Sopenharmony_ci{ 580f66f451Sopenharmony_ci // Die silently if our pipeline exited. 590f66f451Sopenharmony_ci if (errno != EPIPE) { 600f66f451Sopenharmony_ci va_list va; 610f66f451Sopenharmony_ci 620f66f451Sopenharmony_ci va_start(va, msg); 630f66f451Sopenharmony_ci verror_msg(msg, errno, va); 640f66f451Sopenharmony_ci va_end(va); 650f66f451Sopenharmony_ci } 660f66f451Sopenharmony_ci 670f66f451Sopenharmony_ci xexit(); 680f66f451Sopenharmony_ci} 690f66f451Sopenharmony_ci 700f66f451Sopenharmony_ci// Exit with an error message after showing help text. 710f66f451Sopenharmony_civoid help_exit(char *msg, ...) 720f66f451Sopenharmony_ci{ 730f66f451Sopenharmony_ci va_list va; 740f66f451Sopenharmony_ci 750f66f451Sopenharmony_ci if (!msg) show_help(stdout); 760f66f451Sopenharmony_ci else { 770f66f451Sopenharmony_ci va_start(va, msg); 780f66f451Sopenharmony_ci verror_msg(msg, -1, va); 790f66f451Sopenharmony_ci va_end(va); 800f66f451Sopenharmony_ci } 810f66f451Sopenharmony_ci 820f66f451Sopenharmony_ci xexit(); 830f66f451Sopenharmony_ci} 840f66f451Sopenharmony_ci 850f66f451Sopenharmony_ci// If you want to explicitly disable the printf() behavior (because you're 860f66f451Sopenharmony_ci// printing user-supplied data, or because android's static checker produces 870f66f451Sopenharmony_ci// false positives for 'char *s = x ? "blah1" : "blah2"; printf(s);' and it's 880f66f451Sopenharmony_ci// -Werror there for policy reasons). 890f66f451Sopenharmony_civoid error_msg_raw(char *msg) 900f66f451Sopenharmony_ci{ 910f66f451Sopenharmony_ci error_msg("%s", msg); 920f66f451Sopenharmony_ci} 930f66f451Sopenharmony_ci 940f66f451Sopenharmony_civoid perror_msg_raw(char *msg) 950f66f451Sopenharmony_ci{ 960f66f451Sopenharmony_ci perror_msg("%s", msg); 970f66f451Sopenharmony_ci} 980f66f451Sopenharmony_ci 990f66f451Sopenharmony_civoid error_exit_raw(char *msg) 1000f66f451Sopenharmony_ci{ 1010f66f451Sopenharmony_ci error_exit("%s", msg); 1020f66f451Sopenharmony_ci} 1030f66f451Sopenharmony_ci 1040f66f451Sopenharmony_civoid perror_exit_raw(char *msg) 1050f66f451Sopenharmony_ci{ 1060f66f451Sopenharmony_ci perror_exit("%s", msg); 1070f66f451Sopenharmony_ci} 1080f66f451Sopenharmony_ci 1090f66f451Sopenharmony_ci// Keep reading until full or EOF 1100f66f451Sopenharmony_cissize_t readall(int fd, void *buf, size_t len) 1110f66f451Sopenharmony_ci{ 1120f66f451Sopenharmony_ci size_t count = 0; 1130f66f451Sopenharmony_ci 1140f66f451Sopenharmony_ci while (count<len) { 1150f66f451Sopenharmony_ci int i = read(fd, (char *)buf+count, len-count); 1160f66f451Sopenharmony_ci if (!i) break; 1170f66f451Sopenharmony_ci if (i<0) return i; 1180f66f451Sopenharmony_ci count += i; 1190f66f451Sopenharmony_ci } 1200f66f451Sopenharmony_ci 1210f66f451Sopenharmony_ci return count; 1220f66f451Sopenharmony_ci} 1230f66f451Sopenharmony_ci 1240f66f451Sopenharmony_ci// Keep writing until done or EOF 1250f66f451Sopenharmony_cissize_t writeall(int fd, void *buf, size_t len) 1260f66f451Sopenharmony_ci{ 1270f66f451Sopenharmony_ci size_t count = 0; 1280f66f451Sopenharmony_ci 1290f66f451Sopenharmony_ci while (count<len) { 1300f66f451Sopenharmony_ci int i = write(fd, count+(char *)buf, len-count); 1310f66f451Sopenharmony_ci if (i<1) return i; 1320f66f451Sopenharmony_ci count += i; 1330f66f451Sopenharmony_ci } 1340f66f451Sopenharmony_ci 1350f66f451Sopenharmony_ci return count; 1360f66f451Sopenharmony_ci} 1370f66f451Sopenharmony_ci 1380f66f451Sopenharmony_ci// skip this many bytes of input. Return 0 for success, >0 means this much 1390f66f451Sopenharmony_ci// left after input skipped. 1400f66f451Sopenharmony_cioff_t lskip(int fd, off_t offset) 1410f66f451Sopenharmony_ci{ 1420f66f451Sopenharmony_ci off_t cur = lseek(fd, 0, SEEK_CUR); 1430f66f451Sopenharmony_ci 1440f66f451Sopenharmony_ci if (cur != -1) { 1450f66f451Sopenharmony_ci off_t end = lseek(fd, 0, SEEK_END) - cur; 1460f66f451Sopenharmony_ci 1470f66f451Sopenharmony_ci if (end > 0 && end < offset) return offset - end; 1480f66f451Sopenharmony_ci end = offset+cur; 1490f66f451Sopenharmony_ci if (end == lseek(fd, end, SEEK_SET)) return 0; 1500f66f451Sopenharmony_ci perror_exit("lseek"); 1510f66f451Sopenharmony_ci } 1520f66f451Sopenharmony_ci 1530f66f451Sopenharmony_ci while (offset>0) { 1540f66f451Sopenharmony_ci int try = offset>sizeof(libbuf) ? sizeof(libbuf) : offset, or; 1550f66f451Sopenharmony_ci 1560f66f451Sopenharmony_ci or = readall(fd, libbuf, try); 1570f66f451Sopenharmony_ci if (or < 0) perror_exit("lskip to %lld", (long long)offset); 1580f66f451Sopenharmony_ci else offset -= or; 1590f66f451Sopenharmony_ci if (or < try) break; 1600f66f451Sopenharmony_ci } 1610f66f451Sopenharmony_ci 1620f66f451Sopenharmony_ci return offset; 1630f66f451Sopenharmony_ci} 1640f66f451Sopenharmony_ci 1650f66f451Sopenharmony_ci// flags: 1=make last dir (with mode lastmode, otherwise skips last component) 1660f66f451Sopenharmony_ci// 2=make path (already exists is ok) 1670f66f451Sopenharmony_ci// 4=verbose 1680f66f451Sopenharmony_ci// returns 0 = path ok, 1 = error 1690f66f451Sopenharmony_ciint mkpathat(int atfd, char *dir, mode_t lastmode, int flags) 1700f66f451Sopenharmony_ci{ 1710f66f451Sopenharmony_ci struct stat buf; 1720f66f451Sopenharmony_ci char *s; 1730f66f451Sopenharmony_ci 1740f66f451Sopenharmony_ci // mkdir -p one/two/three is not an error if the path already exists, 1750f66f451Sopenharmony_ci // but is if "three" is a file. The others we dereference and catch 1760f66f451Sopenharmony_ci // not-a-directory along the way, but the last one we must explicitly 1770f66f451Sopenharmony_ci // test for. Might as well do it up front. 1780f66f451Sopenharmony_ci 1790f66f451Sopenharmony_ci if (!fstatat(atfd, dir, &buf, 0) && !S_ISDIR(buf.st_mode)) { 1800f66f451Sopenharmony_ci errno = EEXIST; 1810f66f451Sopenharmony_ci return 1; 1820f66f451Sopenharmony_ci } 1830f66f451Sopenharmony_ci 1840f66f451Sopenharmony_ci for (s = dir; ;s++) { 1850f66f451Sopenharmony_ci char save = 0; 1860f66f451Sopenharmony_ci mode_t mode = (0777&~toys.old_umask)|0300; 1870f66f451Sopenharmony_ci 1880f66f451Sopenharmony_ci // find next '/', but don't try to mkdir "" at start of absolute path 1890f66f451Sopenharmony_ci if (*s == '/' && (flags&2) && s != dir) { 1900f66f451Sopenharmony_ci save = *s; 1910f66f451Sopenharmony_ci *s = 0; 1920f66f451Sopenharmony_ci } else if (*s) continue; 1930f66f451Sopenharmony_ci 1940f66f451Sopenharmony_ci // Use the mode from the -m option only for the last directory. 1950f66f451Sopenharmony_ci if (!save) { 1960f66f451Sopenharmony_ci if (flags&1) mode = lastmode; 1970f66f451Sopenharmony_ci else break; 1980f66f451Sopenharmony_ci } 1990f66f451Sopenharmony_ci 2000f66f451Sopenharmony_ci if (mkdirat(atfd, dir, mode)) { 2010f66f451Sopenharmony_ci if (!(flags&2) || errno != EEXIST) return 1; 2020f66f451Sopenharmony_ci } else if (flags&4) 2030f66f451Sopenharmony_ci fprintf(stderr, "%s: created directory '%s'\n", toys.which->name, dir); 2040f66f451Sopenharmony_ci 2050f66f451Sopenharmony_ci if (!(*s = save)) break; 2060f66f451Sopenharmony_ci } 2070f66f451Sopenharmony_ci 2080f66f451Sopenharmony_ci return 0; 2090f66f451Sopenharmony_ci} 2100f66f451Sopenharmony_ci 2110f66f451Sopenharmony_ci// The common case 2120f66f451Sopenharmony_ciint mkpath(char *dir) 2130f66f451Sopenharmony_ci{ 2140f66f451Sopenharmony_ci return mkpathat(AT_FDCWD, dir, 0, MKPATHAT_MAKE); 2150f66f451Sopenharmony_ci} 2160f66f451Sopenharmony_ci 2170f66f451Sopenharmony_ci// Split a path into linked list of components, tracking head and tail of list. 2180f66f451Sopenharmony_ci// Filters out // entries with no contents. 2190f66f451Sopenharmony_cistruct string_list **splitpath(char *path, struct string_list **list) 2200f66f451Sopenharmony_ci{ 2210f66f451Sopenharmony_ci char *new = path; 2220f66f451Sopenharmony_ci 2230f66f451Sopenharmony_ci *list = 0; 2240f66f451Sopenharmony_ci do { 2250f66f451Sopenharmony_ci int len; 2260f66f451Sopenharmony_ci 2270f66f451Sopenharmony_ci if (*path && *path != '/') continue; 2280f66f451Sopenharmony_ci len = path-new; 2290f66f451Sopenharmony_ci if (len > 0) { 2300f66f451Sopenharmony_ci *list = xmalloc(sizeof(struct string_list) + len + 1); 2310f66f451Sopenharmony_ci (*list)->next = 0; 2320f66f451Sopenharmony_ci memcpy((*list)->str, new, len); 2330f66f451Sopenharmony_ci (*list)->str[len] = 0; 2340f66f451Sopenharmony_ci list = &(*list)->next; 2350f66f451Sopenharmony_ci } 2360f66f451Sopenharmony_ci new = path+1; 2370f66f451Sopenharmony_ci } while (*path++); 2380f66f451Sopenharmony_ci 2390f66f451Sopenharmony_ci return list; 2400f66f451Sopenharmony_ci} 2410f66f451Sopenharmony_ci 2420f66f451Sopenharmony_ci// Find all file in a colon-separated path with access type "type" (generally 2430f66f451Sopenharmony_ci// X_OK or R_OK). Returns a list of absolute paths to each file found, in 2440f66f451Sopenharmony_ci// order. 2450f66f451Sopenharmony_ci 2460f66f451Sopenharmony_cistruct string_list *find_in_path(char *path, char *filename) 2470f66f451Sopenharmony_ci{ 2480f66f451Sopenharmony_ci struct string_list *rlist = NULL, **prlist=&rlist; 2490f66f451Sopenharmony_ci char *cwd; 2500f66f451Sopenharmony_ci 2510f66f451Sopenharmony_ci if (!path) return 0; 2520f66f451Sopenharmony_ci 2530f66f451Sopenharmony_ci cwd = xgetcwd(); 2540f66f451Sopenharmony_ci for (;;) { 2550f66f451Sopenharmony_ci char *res, *next = strchr(path, ':'); 2560f66f451Sopenharmony_ci int len = next ? next-path : strlen(path); 2570f66f451Sopenharmony_ci struct string_list *rnext; 2580f66f451Sopenharmony_ci struct stat st; 2590f66f451Sopenharmony_ci 2600f66f451Sopenharmony_ci rnext = xmalloc(sizeof(void *) + strlen(filename) 2610f66f451Sopenharmony_ci + (len ? len : strlen(cwd)) + 2); 2620f66f451Sopenharmony_ci if (!len) sprintf(rnext->str, "%s/%s", cwd, filename); 2630f66f451Sopenharmony_ci else { 2640f66f451Sopenharmony_ci memcpy(res = rnext->str, path, len); 2650f66f451Sopenharmony_ci res += len; 2660f66f451Sopenharmony_ci *(res++) = '/'; 2670f66f451Sopenharmony_ci strcpy(res, filename); 2680f66f451Sopenharmony_ci } 2690f66f451Sopenharmony_ci 2700f66f451Sopenharmony_ci // Confirm it's not a directory. 2710f66f451Sopenharmony_ci if (!stat(rnext->str, &st) && S_ISREG(st.st_mode)) { 2720f66f451Sopenharmony_ci *prlist = rnext; 2730f66f451Sopenharmony_ci rnext->next = NULL; 2740f66f451Sopenharmony_ci prlist = &(rnext->next); 2750f66f451Sopenharmony_ci } else free(rnext); 2760f66f451Sopenharmony_ci 2770f66f451Sopenharmony_ci if (!next) break; 2780f66f451Sopenharmony_ci path += len; 2790f66f451Sopenharmony_ci path++; 2800f66f451Sopenharmony_ci } 2810f66f451Sopenharmony_ci free(cwd); 2820f66f451Sopenharmony_ci 2830f66f451Sopenharmony_ci return rlist; 2840f66f451Sopenharmony_ci} 2850f66f451Sopenharmony_ci 2860f66f451Sopenharmony_cilong long estrtol(char *str, char **end, int base) 2870f66f451Sopenharmony_ci{ 2880f66f451Sopenharmony_ci errno = 0; 2890f66f451Sopenharmony_ci 2900f66f451Sopenharmony_ci return strtoll(str, end, base); 2910f66f451Sopenharmony_ci} 2920f66f451Sopenharmony_ci 2930f66f451Sopenharmony_cilong long xstrtol(char *str, char **end, int base) 2940f66f451Sopenharmony_ci{ 2950f66f451Sopenharmony_ci long long l = estrtol(str, end, base); 2960f66f451Sopenharmony_ci 2970f66f451Sopenharmony_ci if (errno) perror_exit_raw(str); 2980f66f451Sopenharmony_ci 2990f66f451Sopenharmony_ci return l; 3000f66f451Sopenharmony_ci} 3010f66f451Sopenharmony_ci 3020f66f451Sopenharmony_ci// atol() with the kilo/mega/giga/tera/peta/exa extensions, plus word and block. 3030f66f451Sopenharmony_ci// (zetta and yotta don't fit in 64 bits.) 3040f66f451Sopenharmony_cilong long atolx(char *numstr) 3050f66f451Sopenharmony_ci{ 3060f66f451Sopenharmony_ci char *c = numstr, *suffixes="cwbkmgtpe", *end; 3070f66f451Sopenharmony_ci long long val; 3080f66f451Sopenharmony_ci 3090f66f451Sopenharmony_ci val = xstrtol(numstr, &c, 0); 3100f66f451Sopenharmony_ci if (c != numstr && *c && (end = strchr(suffixes, tolower(*c)))) { 3110f66f451Sopenharmony_ci int shift = end-suffixes-2; 3120f66f451Sopenharmony_ci ++c; 3130f66f451Sopenharmony_ci if (shift==-1) val *= 2; 3140f66f451Sopenharmony_ci else if (!shift) val *= 512; 3150f66f451Sopenharmony_ci else if (shift>0) { 3160f66f451Sopenharmony_ci if (*c && tolower(*c++)=='d') while (shift--) val *= 1000; 3170f66f451Sopenharmony_ci else val *= 1LL<<(shift*10); 3180f66f451Sopenharmony_ci } 3190f66f451Sopenharmony_ci } 3200f66f451Sopenharmony_ci while (isspace(*c)) c++; 3210f66f451Sopenharmony_ci if (c==numstr || *c) error_exit("not integer: %s", numstr); 3220f66f451Sopenharmony_ci 3230f66f451Sopenharmony_ci return val; 3240f66f451Sopenharmony_ci} 3250f66f451Sopenharmony_ci 3260f66f451Sopenharmony_cilong long atolx_range(char *numstr, long long low, long long high) 3270f66f451Sopenharmony_ci{ 3280f66f451Sopenharmony_ci long long val = atolx(numstr); 3290f66f451Sopenharmony_ci 3300f66f451Sopenharmony_ci if (val < low) error_exit("%lld < %lld", val, low); 3310f66f451Sopenharmony_ci if (val > high) error_exit("%lld > %lld", val, high); 3320f66f451Sopenharmony_ci 3330f66f451Sopenharmony_ci return val; 3340f66f451Sopenharmony_ci} 3350f66f451Sopenharmony_ci 3360f66f451Sopenharmony_ciint stridx(char *haystack, char needle) 3370f66f451Sopenharmony_ci{ 3380f66f451Sopenharmony_ci char *off; 3390f66f451Sopenharmony_ci 3400f66f451Sopenharmony_ci if (!needle) return -1; 3410f66f451Sopenharmony_ci off = strchr(haystack, needle); 3420f66f451Sopenharmony_ci if (!off) return -1; 3430f66f451Sopenharmony_ci 3440f66f451Sopenharmony_ci return off-haystack; 3450f66f451Sopenharmony_ci} 3460f66f451Sopenharmony_ci 3470f66f451Sopenharmony_ci// Convert wc to utf8, returning bytes written. Does not null terminate. 3480f66f451Sopenharmony_ciint wctoutf8(char *s, unsigned wc) 3490f66f451Sopenharmony_ci{ 3500f66f451Sopenharmony_ci int len = (wc>0x7ff)+(wc>0xffff), i; 3510f66f451Sopenharmony_ci 3520f66f451Sopenharmony_ci if (wc<128) { 3530f66f451Sopenharmony_ci *s = wc; 3540f66f451Sopenharmony_ci return 1; 3550f66f451Sopenharmony_ci } else { 3560f66f451Sopenharmony_ci i = len; 3570f66f451Sopenharmony_ci do { 3580f66f451Sopenharmony_ci s[1+i] = 0x80+(wc&0x3f); 3590f66f451Sopenharmony_ci wc >>= 6; 3600f66f451Sopenharmony_ci } while (i--); 3610f66f451Sopenharmony_ci *s = (((signed char) 0x80) >> (len+1)) | wc; 3620f66f451Sopenharmony_ci } 3630f66f451Sopenharmony_ci 3640f66f451Sopenharmony_ci return 2+len; 3650f66f451Sopenharmony_ci} 3660f66f451Sopenharmony_ci 3670f66f451Sopenharmony_ci// Convert utf8 sequence to a unicode wide character 3680f66f451Sopenharmony_ciint utf8towc(unsigned *wc, char *str, unsigned len) 3690f66f451Sopenharmony_ci{ 3700f66f451Sopenharmony_ci unsigned result, mask, first; 3710f66f451Sopenharmony_ci char *s, c; 3720f66f451Sopenharmony_ci 3730f66f451Sopenharmony_ci // fast path ASCII 3740f66f451Sopenharmony_ci if (len && *str<128) return !!(*wc = *str); 3750f66f451Sopenharmony_ci 3760f66f451Sopenharmony_ci result = first = *(s = str++); 3770f66f451Sopenharmony_ci if (result<0xc2 || result>0xf4) return -1; 3780f66f451Sopenharmony_ci for (mask = 6; (first&0xc0)==0xc0; mask += 5, first <<= 1) { 3790f66f451Sopenharmony_ci if (!--len) return -2; 3800f66f451Sopenharmony_ci if (((c = *(str++))&0xc0) != 0x80) return -1; 3810f66f451Sopenharmony_ci result = (result<<6)|(c&0x3f); 3820f66f451Sopenharmony_ci } 3830f66f451Sopenharmony_ci result &= (1<<mask)-1; 3840f66f451Sopenharmony_ci c = str-s; 3850f66f451Sopenharmony_ci 3860f66f451Sopenharmony_ci // Avoid overlong encodings 3870f66f451Sopenharmony_ci if (result<(unsigned []){0x80,0x800,0x10000}[c-2]) return -1; 3880f66f451Sopenharmony_ci 3890f66f451Sopenharmony_ci // Limit unicode so it can't encode anything UTF-16 can't. 3900f66f451Sopenharmony_ci if (result>0x10ffff || (result>=0xd800 && result<=0xdfff)) return -1; 3910f66f451Sopenharmony_ci *wc = result; 3920f66f451Sopenharmony_ci 3930f66f451Sopenharmony_ci return str-s; 3940f66f451Sopenharmony_ci} 3950f66f451Sopenharmony_ci 3960f66f451Sopenharmony_cichar *strlower(char *s) 3970f66f451Sopenharmony_ci{ 3980f66f451Sopenharmony_ci char *try, *new; 3990f66f451Sopenharmony_ci 4000f66f451Sopenharmony_ci if (!CFG_TOYBOX_I18N) { 4010f66f451Sopenharmony_ci try = new = xstrdup(s); 4020f66f451Sopenharmony_ci for (; *s; s++) *(new++) = tolower(*s); 4030f66f451Sopenharmony_ci } else { 4040f66f451Sopenharmony_ci // I can't guarantee the string _won't_ expand during reencoding, so...? 4050f66f451Sopenharmony_ci try = new = xmalloc(strlen(s)*2+1); 4060f66f451Sopenharmony_ci 4070f66f451Sopenharmony_ci while (*s) { 4080f66f451Sopenharmony_ci unsigned c; 4090f66f451Sopenharmony_ci int len = utf8towc(&c, s, MB_CUR_MAX); 4100f66f451Sopenharmony_ci 4110f66f451Sopenharmony_ci if (len < 1) *(new++) = *(s++); 4120f66f451Sopenharmony_ci else { 4130f66f451Sopenharmony_ci s += len; 4140f66f451Sopenharmony_ci // squash title case too 4150f66f451Sopenharmony_ci c = towlower(c); 4160f66f451Sopenharmony_ci 4170f66f451Sopenharmony_ci // if we had a valid utf8 sequence, convert it to lower case, and can't 4180f66f451Sopenharmony_ci // encode back to utf8, something is wrong with your libc. But just 4190f66f451Sopenharmony_ci // in case somebody finds an exploit... 4200f66f451Sopenharmony_ci len = wcrtomb(new, c, 0); 4210f66f451Sopenharmony_ci if (len < 1) error_exit("bad utf8 %x", (int)c); 4220f66f451Sopenharmony_ci new += len; 4230f66f451Sopenharmony_ci } 4240f66f451Sopenharmony_ci } 4250f66f451Sopenharmony_ci *new = 0; 4260f66f451Sopenharmony_ci } 4270f66f451Sopenharmony_ci 4280f66f451Sopenharmony_ci return try; 4290f66f451Sopenharmony_ci} 4300f66f451Sopenharmony_ci 4310f66f451Sopenharmony_ci// strstr but returns pointer after match 4320f66f451Sopenharmony_cichar *strafter(char *haystack, char *needle) 4330f66f451Sopenharmony_ci{ 4340f66f451Sopenharmony_ci char *s = strstr(haystack, needle); 4350f66f451Sopenharmony_ci 4360f66f451Sopenharmony_ci return s ? s+strlen(needle) : s; 4370f66f451Sopenharmony_ci} 4380f66f451Sopenharmony_ci 4390f66f451Sopenharmony_ci// Remove trailing \n 4400f66f451Sopenharmony_cichar *chomp(char *s) 4410f66f451Sopenharmony_ci{ 4420f66f451Sopenharmony_ci char *p = strrchr(s, '\n'); 4430f66f451Sopenharmony_ci 4440f66f451Sopenharmony_ci if (p && !p[1]) *p = 0; 4450f66f451Sopenharmony_ci return s; 4460f66f451Sopenharmony_ci} 4470f66f451Sopenharmony_ci 4480f66f451Sopenharmony_ciint unescape(char c) 4490f66f451Sopenharmony_ci{ 4500f66f451Sopenharmony_ci char *from = "\\abefnrtv", *to = "\\\a\b\033\f\n\r\t\v"; 4510f66f451Sopenharmony_ci int idx = stridx(from, c); 4520f66f451Sopenharmony_ci 4530f66f451Sopenharmony_ci return (idx == -1) ? 0 : to[idx]; 4540f66f451Sopenharmony_ci} 4550f66f451Sopenharmony_ci 4560f66f451Sopenharmony_ci// parse next character advancing pointer. echo requires leading 0 in octal esc 4570f66f451Sopenharmony_ciint unescape2(char **c, int echo) 4580f66f451Sopenharmony_ci{ 4590f66f451Sopenharmony_ci int idx = *((*c)++), i, off; 4600f66f451Sopenharmony_ci 4610f66f451Sopenharmony_ci if (idx != '\\' || !**c) return idx; 4620f66f451Sopenharmony_ci if (**c == 'c') return 31&*(++*c); 4630f66f451Sopenharmony_ci for (i = 0; i<4; i++) { 4640f66f451Sopenharmony_ci if (sscanf(*c, (char *[]){"0%3o%n"+!echo, "x%2x%n", "u%4x%n", "U%6x%n"}[i], 4650f66f451Sopenharmony_ci &idx, &off) > 0) 4660f66f451Sopenharmony_ci { 4670f66f451Sopenharmony_ci *c += off; 4680f66f451Sopenharmony_ci 4690f66f451Sopenharmony_ci return idx; 4700f66f451Sopenharmony_ci } 4710f66f451Sopenharmony_ci } 4720f66f451Sopenharmony_ci 4730f66f451Sopenharmony_ci if (-1 == (idx = stridx("\\abeEfnrtv'\"?0", **c))) return '\\'; 4740f66f451Sopenharmony_ci ++*c; 4750f66f451Sopenharmony_ci 4760f66f451Sopenharmony_ci return "\\\a\b\033\033\f\n\r\t\v'\"?"[idx]; 4770f66f451Sopenharmony_ci} 4780f66f451Sopenharmony_ci 4790f66f451Sopenharmony_ci// If string ends with suffix return pointer to start of suffix in string, 4800f66f451Sopenharmony_ci// else NULL 4810f66f451Sopenharmony_cichar *strend(char *str, char *suffix) 4820f66f451Sopenharmony_ci{ 4830f66f451Sopenharmony_ci long a = strlen(str), b = strlen(suffix); 4840f66f451Sopenharmony_ci 4850f66f451Sopenharmony_ci if (a>b && !strcmp(str += a-b, suffix)) return str; 4860f66f451Sopenharmony_ci 4870f66f451Sopenharmony_ci return 0; 4880f66f451Sopenharmony_ci} 4890f66f451Sopenharmony_ci 4900f66f451Sopenharmony_ci// If *a starts with b, advance *a past it and return 1, else return 0; 4910f66f451Sopenharmony_ciint strstart(char **a, char *b) 4920f66f451Sopenharmony_ci{ 4930f66f451Sopenharmony_ci char *c = *a; 4940f66f451Sopenharmony_ci 4950f66f451Sopenharmony_ci while (*b && *c == *b) b++, c++; 4960f66f451Sopenharmony_ci if (!*b) *a = c; 4970f66f451Sopenharmony_ci 4980f66f451Sopenharmony_ci return !*b; 4990f66f451Sopenharmony_ci} 5000f66f451Sopenharmony_ci 5010f66f451Sopenharmony_ci// If *a starts with b, advance *a past it and return 1, else return 0; 5020f66f451Sopenharmony_ciint strcasestart(char **a, char *b) 5030f66f451Sopenharmony_ci{ 5040f66f451Sopenharmony_ci int len = strlen(b), i = !strncasecmp(*a, b, len); 5050f66f451Sopenharmony_ci 5060f66f451Sopenharmony_ci if (i) *a += len; 5070f66f451Sopenharmony_ci 5080f66f451Sopenharmony_ci return i; 5090f66f451Sopenharmony_ci} 5100f66f451Sopenharmony_ci 5110f66f451Sopenharmony_ci// Return how long the file at fd is, if there's any way to determine it. 5120f66f451Sopenharmony_cioff_t fdlength(int fd) 5130f66f451Sopenharmony_ci{ 5140f66f451Sopenharmony_ci struct stat st; 5150f66f451Sopenharmony_ci off_t base = 0, range = 1, expand = 1, old; 5160f66f451Sopenharmony_ci 5170f66f451Sopenharmony_ci if (!fstat(fd, &st) && S_ISREG(st.st_mode)) return st.st_size; 5180f66f451Sopenharmony_ci 5190f66f451Sopenharmony_ci // If the ioctl works for this, return it. 5200f66f451Sopenharmony_ci // TODO: is blocksize still always 512, or do we stat for it? 5210f66f451Sopenharmony_ci // unsigned int size; 5220f66f451Sopenharmony_ci // if (ioctl(fd, BLKGETSIZE, &size) >= 0) return size*512L; 5230f66f451Sopenharmony_ci 5240f66f451Sopenharmony_ci // If not, do a binary search for the last location we can read. (Some 5250f66f451Sopenharmony_ci // block devices don't do BLKGETSIZE right.) This should probably have 5260f66f451Sopenharmony_ci // a CONFIG option... 5270f66f451Sopenharmony_ci 5280f66f451Sopenharmony_ci // If not, do a binary search for the last location we can read. 5290f66f451Sopenharmony_ci 5300f66f451Sopenharmony_ci old = lseek(fd, 0, SEEK_CUR); 5310f66f451Sopenharmony_ci do { 5320f66f451Sopenharmony_ci char temp; 5330f66f451Sopenharmony_ci off_t pos = base + range / 2; 5340f66f451Sopenharmony_ci 5350f66f451Sopenharmony_ci if (lseek(fd, pos, 0)>=0 && read(fd, &temp, 1)==1) { 5360f66f451Sopenharmony_ci off_t delta = (pos + 1) - base; 5370f66f451Sopenharmony_ci 5380f66f451Sopenharmony_ci base += delta; 5390f66f451Sopenharmony_ci if (expand) range = (expand <<= 1) - base; 5400f66f451Sopenharmony_ci else range -= delta; 5410f66f451Sopenharmony_ci } else { 5420f66f451Sopenharmony_ci expand = 0; 5430f66f451Sopenharmony_ci range = pos - base; 5440f66f451Sopenharmony_ci } 5450f66f451Sopenharmony_ci } while (range > 0); 5460f66f451Sopenharmony_ci 5470f66f451Sopenharmony_ci lseek(fd, old, SEEK_SET); 5480f66f451Sopenharmony_ci 5490f66f451Sopenharmony_ci return base; 5500f66f451Sopenharmony_ci} 5510f66f451Sopenharmony_ci 5520f66f451Sopenharmony_ci// Read contents of file as a single nul-terminated string. 5530f66f451Sopenharmony_ci// measure file size if !len, allocate buffer if !buf 5540f66f451Sopenharmony_ci// Existing buffers need len in *plen 5550f66f451Sopenharmony_ci// Returns amount of data read in *plen 5560f66f451Sopenharmony_cichar *readfileat(int dirfd, char *name, char *ibuf, off_t *plen) 5570f66f451Sopenharmony_ci{ 5580f66f451Sopenharmony_ci off_t len, rlen; 5590f66f451Sopenharmony_ci int fd; 5600f66f451Sopenharmony_ci char *buf, *rbuf; 5610f66f451Sopenharmony_ci 5620f66f451Sopenharmony_ci // Unsafe to probe for size with a supplied buffer, don't ever do that. 5630f66f451Sopenharmony_ci if (CFG_TOYBOX_DEBUG && (ibuf ? !*plen : *plen)) error_exit("bad readfileat"); 5640f66f451Sopenharmony_ci 5650f66f451Sopenharmony_ci if (-1 == (fd = openat(dirfd, name, O_RDONLY))) return 0; 5660f66f451Sopenharmony_ci 5670f66f451Sopenharmony_ci // If we dunno the length, probe it. If we can't probe, start with 1 page. 5680f66f451Sopenharmony_ci if (!*plen) { 5690f66f451Sopenharmony_ci if ((len = fdlength(fd))>0) *plen = len; 5700f66f451Sopenharmony_ci else len = 4096; 5710f66f451Sopenharmony_ci } else len = *plen-1; 5720f66f451Sopenharmony_ci 5730f66f451Sopenharmony_ci if (!ibuf) buf = xmalloc(len+1); 5740f66f451Sopenharmony_ci else buf = ibuf; 5750f66f451Sopenharmony_ci 5760f66f451Sopenharmony_ci for (rbuf = buf;;) { 5770f66f451Sopenharmony_ci rlen = readall(fd, rbuf, len); 5780f66f451Sopenharmony_ci if (*plen || rlen<len) break; 5790f66f451Sopenharmony_ci 5800f66f451Sopenharmony_ci // If reading unknown size, expand buffer by 1.5 each time we fill it up. 5810f66f451Sopenharmony_ci rlen += rbuf-buf; 5820f66f451Sopenharmony_ci buf = xrealloc(buf, len = (rlen*3)/2); 5830f66f451Sopenharmony_ci rbuf = buf+rlen; 5840f66f451Sopenharmony_ci len -= rlen; 5850f66f451Sopenharmony_ci } 5860f66f451Sopenharmony_ci *plen = len = rlen+(rbuf-buf); 5870f66f451Sopenharmony_ci close(fd); 5880f66f451Sopenharmony_ci 5890f66f451Sopenharmony_ci if (rlen<0) { 5900f66f451Sopenharmony_ci if (ibuf != buf) free(buf); 5910f66f451Sopenharmony_ci buf = 0; 5920f66f451Sopenharmony_ci } else buf[len] = 0; 5930f66f451Sopenharmony_ci 5940f66f451Sopenharmony_ci return buf; 5950f66f451Sopenharmony_ci} 5960f66f451Sopenharmony_ci 5970f66f451Sopenharmony_cichar *readfile(char *name, char *ibuf, off_t len) 5980f66f451Sopenharmony_ci{ 5990f66f451Sopenharmony_ci return readfileat(AT_FDCWD, name, ibuf, &len); 6000f66f451Sopenharmony_ci} 6010f66f451Sopenharmony_ci 6020f66f451Sopenharmony_ci// Sleep for this many thousandths of a second 6030f66f451Sopenharmony_civoid msleep(long milliseconds) 6040f66f451Sopenharmony_ci{ 6050f66f451Sopenharmony_ci struct timespec ts; 6060f66f451Sopenharmony_ci 6070f66f451Sopenharmony_ci ts.tv_sec = milliseconds/1000; 6080f66f451Sopenharmony_ci ts.tv_nsec = (milliseconds%1000)*1000000; 6090f66f451Sopenharmony_ci nanosleep(&ts, &ts); 6100f66f451Sopenharmony_ci} 6110f66f451Sopenharmony_ci 6120f66f451Sopenharmony_ci// Adjust timespec by nanosecond offset 6130f66f451Sopenharmony_civoid nanomove(struct timespec *ts, long long offset) 6140f66f451Sopenharmony_ci{ 6150f66f451Sopenharmony_ci long long nano = ts->tv_nsec + offset, secs = nano/1000000000; 6160f66f451Sopenharmony_ci 6170f66f451Sopenharmony_ci ts->tv_sec += secs; 6180f66f451Sopenharmony_ci nano %= 1000000000; 6190f66f451Sopenharmony_ci if (nano<0) { 6200f66f451Sopenharmony_ci ts->tv_sec--; 6210f66f451Sopenharmony_ci nano += 1000000000; 6220f66f451Sopenharmony_ci } 6230f66f451Sopenharmony_ci ts->tv_nsec = nano; 6240f66f451Sopenharmony_ci} 6250f66f451Sopenharmony_ci 6260f66f451Sopenharmony_ci// return difference between two timespecs in nanosecs 6270f66f451Sopenharmony_cilong long nanodiff(struct timespec *old, struct timespec *new) 6280f66f451Sopenharmony_ci{ 6290f66f451Sopenharmony_ci return (new->tv_sec - old->tv_sec)*1000000000LL+(new->tv_nsec - old->tv_nsec); 6300f66f451Sopenharmony_ci} 6310f66f451Sopenharmony_ci 6320f66f451Sopenharmony_ci// return 1<<x of highest bit set 6330f66f451Sopenharmony_ciint highest_bit(unsigned long l) 6340f66f451Sopenharmony_ci{ 6350f66f451Sopenharmony_ci int i; 6360f66f451Sopenharmony_ci 6370f66f451Sopenharmony_ci for (i = 0; l; i++) l >>= 1; 6380f66f451Sopenharmony_ci 6390f66f451Sopenharmony_ci return i-1; 6400f66f451Sopenharmony_ci} 6410f66f451Sopenharmony_ci 6420f66f451Sopenharmony_ci// Inefficient, but deals with unaligned access 6430f66f451Sopenharmony_ciint64_t peek_le(void *ptr, unsigned size) 6440f66f451Sopenharmony_ci{ 6450f66f451Sopenharmony_ci int64_t ret = 0; 6460f66f451Sopenharmony_ci char *c = ptr; 6470f66f451Sopenharmony_ci int i; 6480f66f451Sopenharmony_ci 6490f66f451Sopenharmony_ci for (i=0; i<size; i++) ret |= ((int64_t)c[i])<<(i*8); 6500f66f451Sopenharmony_ci return ret; 6510f66f451Sopenharmony_ci} 6520f66f451Sopenharmony_ci 6530f66f451Sopenharmony_ciint64_t peek_be(void *ptr, unsigned size) 6540f66f451Sopenharmony_ci{ 6550f66f451Sopenharmony_ci int64_t ret = 0; 6560f66f451Sopenharmony_ci char *c = ptr; 6570f66f451Sopenharmony_ci int i; 6580f66f451Sopenharmony_ci 6590f66f451Sopenharmony_ci for (i=0; i<size; i++) ret = (ret<<8)|(c[i]&0xff); 6600f66f451Sopenharmony_ci return ret; 6610f66f451Sopenharmony_ci} 6620f66f451Sopenharmony_ci 6630f66f451Sopenharmony_ciint64_t peek(void *ptr, unsigned size) 6640f66f451Sopenharmony_ci{ 6650f66f451Sopenharmony_ci return (IS_BIG_ENDIAN ? peek_be : peek_le)(ptr, size); 6660f66f451Sopenharmony_ci} 6670f66f451Sopenharmony_ci 6680f66f451Sopenharmony_civoid poke_le(void *ptr, long long val, unsigned size) 6690f66f451Sopenharmony_ci{ 6700f66f451Sopenharmony_ci char *c = ptr; 6710f66f451Sopenharmony_ci 6720f66f451Sopenharmony_ci while (size--) { 6730f66f451Sopenharmony_ci *c++ = val&255; 6740f66f451Sopenharmony_ci val >>= 8; 6750f66f451Sopenharmony_ci } 6760f66f451Sopenharmony_ci} 6770f66f451Sopenharmony_ci 6780f66f451Sopenharmony_civoid poke_be(void *ptr, long long val, unsigned size) 6790f66f451Sopenharmony_ci{ 6800f66f451Sopenharmony_ci char *c = ptr + size; 6810f66f451Sopenharmony_ci 6820f66f451Sopenharmony_ci while (size--) { 6830f66f451Sopenharmony_ci *--c = val&255; 6840f66f451Sopenharmony_ci val >>=8; 6850f66f451Sopenharmony_ci } 6860f66f451Sopenharmony_ci} 6870f66f451Sopenharmony_ci 6880f66f451Sopenharmony_civoid poke(void *ptr, long long val, unsigned size) 6890f66f451Sopenharmony_ci{ 6900f66f451Sopenharmony_ci (IS_BIG_ENDIAN ? poke_be : poke_le)(ptr, val, size); 6910f66f451Sopenharmony_ci} 6920f66f451Sopenharmony_ci 6930f66f451Sopenharmony_ci// Iterate through an array of files, opening each one and calling a function 6940f66f451Sopenharmony_ci// on that filehandle and name. The special filename "-" means stdin if 6950f66f451Sopenharmony_ci// flags is O_RDONLY, stdout otherwise. An empty argument list calls 6960f66f451Sopenharmony_ci// function() on just stdin/stdout. 6970f66f451Sopenharmony_ci// 6980f66f451Sopenharmony_ci// Note: pass O_CLOEXEC to automatically close filehandles when function() 6990f66f451Sopenharmony_ci// returns, otherwise filehandles must be closed by function(). 7000f66f451Sopenharmony_ci// pass WARN_ONLY to produce warning messages about files it couldn't 7010f66f451Sopenharmony_ci// open/create, and skip them. Otherwise function is called with fd -1. 7020f66f451Sopenharmony_civoid loopfiles_rw(char **argv, int flags, int permissions, 7030f66f451Sopenharmony_ci void (*function)(int fd, char *name)) 7040f66f451Sopenharmony_ci{ 7050f66f451Sopenharmony_ci int fd, failok = !(flags&WARN_ONLY); 7060f66f451Sopenharmony_ci 7070f66f451Sopenharmony_ci flags &= ~WARN_ONLY; 7080f66f451Sopenharmony_ci 7090f66f451Sopenharmony_ci // If no arguments, read from stdin. 7100f66f451Sopenharmony_ci if (!*argv) function((flags & O_ACCMODE) != O_RDONLY ? 1 : 0, "-"); 7110f66f451Sopenharmony_ci else do { 7120f66f451Sopenharmony_ci // Filename "-" means read from stdin. 7130f66f451Sopenharmony_ci // Inability to open a file prints a warning, but doesn't exit. 7140f66f451Sopenharmony_ci 7150f66f451Sopenharmony_ci if (!strcmp(*argv, "-")) fd = 0; 7160f66f451Sopenharmony_ci else if (0>(fd = notstdio(open(*argv, flags, permissions))) && !failok) { 7170f66f451Sopenharmony_ci perror_msg_raw(*argv); 7180f66f451Sopenharmony_ci continue; 7190f66f451Sopenharmony_ci } 7200f66f451Sopenharmony_ci function(fd, *argv); 7210f66f451Sopenharmony_ci if ((flags & O_CLOEXEC) && fd) close(fd); 7220f66f451Sopenharmony_ci } while (*++argv); 7230f66f451Sopenharmony_ci} 7240f66f451Sopenharmony_ci 7250f66f451Sopenharmony_ci// Call loopfiles_rw with O_RDONLY|O_CLOEXEC|WARN_ONLY (common case) 7260f66f451Sopenharmony_civoid loopfiles(char **argv, void (*function)(int fd, char *name)) 7270f66f451Sopenharmony_ci{ 7280f66f451Sopenharmony_ci loopfiles_rw(argv, O_RDONLY|O_CLOEXEC|WARN_ONLY, 0, function); 7290f66f451Sopenharmony_ci} 7300f66f451Sopenharmony_ci 7310f66f451Sopenharmony_ci// glue to call do_lines() from loopfiles 7320f66f451Sopenharmony_cistatic void (*do_lines_bridge)(char **pline, long len); 7330f66f451Sopenharmony_cistatic void loopfile_lines_bridge(int fd, char *name) 7340f66f451Sopenharmony_ci{ 7350f66f451Sopenharmony_ci do_lines(fd, '\n', do_lines_bridge); 7360f66f451Sopenharmony_ci} 7370f66f451Sopenharmony_ci 7380f66f451Sopenharmony_civoid loopfiles_lines(char **argv, void (*function)(char **pline, long len)) 7390f66f451Sopenharmony_ci{ 7400f66f451Sopenharmony_ci do_lines_bridge = function; 7410f66f451Sopenharmony_ci // No O_CLOEXEC because we need to call fclose. 7420f66f451Sopenharmony_ci loopfiles_rw(argv, O_RDONLY|WARN_ONLY, 0, loopfile_lines_bridge); 7430f66f451Sopenharmony_ci} 7440f66f451Sopenharmony_ci 7450f66f451Sopenharmony_ci// Slow, but small. 7460f66f451Sopenharmony_cichar *get_line(int fd) 7470f66f451Sopenharmony_ci{ 7480f66f451Sopenharmony_ci char c, *buf = NULL; 7490f66f451Sopenharmony_ci long len = 0; 7500f66f451Sopenharmony_ci 7510f66f451Sopenharmony_ci for (;;) { 7520f66f451Sopenharmony_ci if (1>read(fd, &c, 1)) break; 7530f66f451Sopenharmony_ci if (!(len & 63)) buf=xrealloc(buf, len+65); 7540f66f451Sopenharmony_ci if ((buf[len++]=c) == '\n') break; 7550f66f451Sopenharmony_ci } 7560f66f451Sopenharmony_ci if (buf) { 7570f66f451Sopenharmony_ci buf[len]=0; 7580f66f451Sopenharmony_ci if (buf[--len]=='\n') buf[len]=0; 7590f66f451Sopenharmony_ci } 7600f66f451Sopenharmony_ci 7610f66f451Sopenharmony_ci return buf; 7620f66f451Sopenharmony_ci} 7630f66f451Sopenharmony_ci 7640f66f451Sopenharmony_ciint wfchmodat(int fd, char *name, mode_t mode) 7650f66f451Sopenharmony_ci{ 7660f66f451Sopenharmony_ci int rc = fchmodat(fd, name, mode, 0); 7670f66f451Sopenharmony_ci 7680f66f451Sopenharmony_ci if (rc) { 7690f66f451Sopenharmony_ci perror_msg("chmod '%s' to %04o", name, mode); 7700f66f451Sopenharmony_ci toys.exitval=1; 7710f66f451Sopenharmony_ci } 7720f66f451Sopenharmony_ci return rc; 7730f66f451Sopenharmony_ci} 7740f66f451Sopenharmony_ci 7750f66f451Sopenharmony_cistatic char *tempfile2zap; 7760f66f451Sopenharmony_cistatic void tempfile_handler(void) 7770f66f451Sopenharmony_ci{ 7780f66f451Sopenharmony_ci if (1 < (long)tempfile2zap) unlink(tempfile2zap); 7790f66f451Sopenharmony_ci} 7800f66f451Sopenharmony_ci 7810f66f451Sopenharmony_ci// Open a temporary file to copy an existing file into. 7820f66f451Sopenharmony_ciint copy_tempfile(int fdin, char *name, char **tempname) 7830f66f451Sopenharmony_ci{ 7840f66f451Sopenharmony_ci struct stat statbuf; 7850f66f451Sopenharmony_ci int fd = xtempfile(name, tempname), ignored __attribute__((__unused__)); 7860f66f451Sopenharmony_ci 7870f66f451Sopenharmony_ci // Record tempfile for exit cleanup if interrupted 7880f66f451Sopenharmony_ci if (!tempfile2zap) sigatexit(tempfile_handler); 7890f66f451Sopenharmony_ci tempfile2zap = *tempname; 7900f66f451Sopenharmony_ci 7910f66f451Sopenharmony_ci // Set permissions of output file. 7920f66f451Sopenharmony_ci if (!fstat(fdin, &statbuf)) fchmod(fd, statbuf.st_mode); 7930f66f451Sopenharmony_ci 7940f66f451Sopenharmony_ci // We chmod before chown, which strips the suid bit. Caller has to explicitly 7950f66f451Sopenharmony_ci // switch it back on if they want to keep suid. 7960f66f451Sopenharmony_ci 7970f66f451Sopenharmony_ci // Suppress warn-unused-result. Both gcc and clang clutch their pearls about 7980f66f451Sopenharmony_ci // this but it's _supposed_ to fail when we're not root. 7990f66f451Sopenharmony_ci ignored = fchown(fd, statbuf.st_uid, statbuf.st_gid); 8000f66f451Sopenharmony_ci 8010f66f451Sopenharmony_ci return fd; 8020f66f451Sopenharmony_ci} 8030f66f451Sopenharmony_ci 8040f66f451Sopenharmony_ci// Abort the copy and delete the temporary file. 8050f66f451Sopenharmony_civoid delete_tempfile(int fdin, int fdout, char **tempname) 8060f66f451Sopenharmony_ci{ 8070f66f451Sopenharmony_ci close(fdin); 8080f66f451Sopenharmony_ci close(fdout); 8090f66f451Sopenharmony_ci if (*tempname) unlink(*tempname); 8100f66f451Sopenharmony_ci tempfile2zap = (char *)1; 8110f66f451Sopenharmony_ci free(*tempname); 8120f66f451Sopenharmony_ci *tempname = NULL; 8130f66f451Sopenharmony_ci} 8140f66f451Sopenharmony_ci 8150f66f451Sopenharmony_ci// Copy the rest of the data and replace the original with the copy. 8160f66f451Sopenharmony_civoid replace_tempfile(int fdin, int fdout, char **tempname) 8170f66f451Sopenharmony_ci{ 8180f66f451Sopenharmony_ci char *temp = xstrdup(*tempname); 8190f66f451Sopenharmony_ci 8200f66f451Sopenharmony_ci temp[strlen(temp)-6]=0; 8210f66f451Sopenharmony_ci if (fdin != -1) { 8220f66f451Sopenharmony_ci xsendfile(fdin, fdout); 8230f66f451Sopenharmony_ci xclose(fdin); 8240f66f451Sopenharmony_ci } 8250f66f451Sopenharmony_ci xclose(fdout); 8260f66f451Sopenharmony_ci xrename(*tempname, temp); 8270f66f451Sopenharmony_ci tempfile2zap = (char *)1; 8280f66f451Sopenharmony_ci free(*tempname); 8290f66f451Sopenharmony_ci free(temp); 8300f66f451Sopenharmony_ci *tempname = NULL; 8310f66f451Sopenharmony_ci} 8320f66f451Sopenharmony_ci 8330f66f451Sopenharmony_ci// Create a 256 entry CRC32 lookup table. 8340f66f451Sopenharmony_ci 8350f66f451Sopenharmony_civoid crc_init(unsigned int *crc_table, int little_endian) 8360f66f451Sopenharmony_ci{ 8370f66f451Sopenharmony_ci unsigned int i; 8380f66f451Sopenharmony_ci 8390f66f451Sopenharmony_ci // Init the CRC32 table (big endian) 8400f66f451Sopenharmony_ci for (i=0; i<256; i++) { 8410f66f451Sopenharmony_ci unsigned int j, c = little_endian ? i : i<<24; 8420f66f451Sopenharmony_ci for (j=8; j; j--) 8430f66f451Sopenharmony_ci if (little_endian) c = (c&1) ? (c>>1)^0xEDB88320 : c>>1; 8440f66f451Sopenharmony_ci else c=c&0x80000000 ? (c<<1)^0x04c11db7 : (c<<1); 8450f66f451Sopenharmony_ci crc_table[i] = c; 8460f66f451Sopenharmony_ci } 8470f66f451Sopenharmony_ci} 8480f66f451Sopenharmony_ci 8490f66f451Sopenharmony_ci// Init base64 table 8500f66f451Sopenharmony_ci 8510f66f451Sopenharmony_civoid base64_init(char *p) 8520f66f451Sopenharmony_ci{ 8530f66f451Sopenharmony_ci int i; 8540f66f451Sopenharmony_ci 8550f66f451Sopenharmony_ci for (i = 'A'; i != ':'; i++) { 8560f66f451Sopenharmony_ci if (i == 'Z'+1) i = 'a'; 8570f66f451Sopenharmony_ci if (i == 'z'+1) i = '0'; 8580f66f451Sopenharmony_ci *(p++) = i; 8590f66f451Sopenharmony_ci } 8600f66f451Sopenharmony_ci *(p++) = '+'; 8610f66f451Sopenharmony_ci *(p++) = '/'; 8620f66f451Sopenharmony_ci} 8630f66f451Sopenharmony_ci 8640f66f451Sopenharmony_ciint yesno(int def) 8650f66f451Sopenharmony_ci{ 8660f66f451Sopenharmony_ci return fyesno(stdin, def); 8670f66f451Sopenharmony_ci} 8680f66f451Sopenharmony_ci 8690f66f451Sopenharmony_ciint fyesno(FILE *in, int def) 8700f66f451Sopenharmony_ci{ 8710f66f451Sopenharmony_ci char buf; 8720f66f451Sopenharmony_ci 8730f66f451Sopenharmony_ci fprintf(stderr, " (%c/%c):", def ? 'Y' : 'y', def ? 'n' : 'N'); 8740f66f451Sopenharmony_ci fflush(stderr); 8750f66f451Sopenharmony_ci while (fread(&buf, 1, 1, in)) { 8760f66f451Sopenharmony_ci int new; 8770f66f451Sopenharmony_ci 8780f66f451Sopenharmony_ci // The letter changes the value, the newline (or space) returns it. 8790f66f451Sopenharmony_ci if (isspace(buf)) break; 8800f66f451Sopenharmony_ci if (-1 != (new = stridx("ny", tolower(buf)))) def = new; 8810f66f451Sopenharmony_ci } 8820f66f451Sopenharmony_ci 8830f66f451Sopenharmony_ci return def; 8840f66f451Sopenharmony_ci} 8850f66f451Sopenharmony_ci 8860f66f451Sopenharmony_ci// Handler that sets toys.signal, and writes to toys.signalfd if set 8870f66f451Sopenharmony_civoid generic_signal(int sig) 8880f66f451Sopenharmony_ci{ 8890f66f451Sopenharmony_ci if (toys.signalfd) { 8900f66f451Sopenharmony_ci char c = sig; 8910f66f451Sopenharmony_ci 8920f66f451Sopenharmony_ci writeall(toys.signalfd, &c, 1); 8930f66f451Sopenharmony_ci } 8940f66f451Sopenharmony_ci toys.signal = sig; 8950f66f451Sopenharmony_ci} 8960f66f451Sopenharmony_ci 8970f66f451Sopenharmony_civoid exit_signal(int sig) 8980f66f451Sopenharmony_ci{ 8990f66f451Sopenharmony_ci if (sig) toys.exitval = sig|128; 9000f66f451Sopenharmony_ci xexit(); 9010f66f451Sopenharmony_ci} 9020f66f451Sopenharmony_ci 9030f66f451Sopenharmony_ci// Install the same handler on every signal that defaults to killing the 9040f66f451Sopenharmony_ci// process, calling the handler on the way out. Calling multiple times 9050f66f451Sopenharmony_ci// adds the handlers to a list, to be called in order. 9060f66f451Sopenharmony_civoid sigatexit(void *handler) 9070f66f451Sopenharmony_ci{ 9080f66f451Sopenharmony_ci xsignal_all_killers(handler ? exit_signal : SIG_DFL); 9090f66f451Sopenharmony_ci 9100f66f451Sopenharmony_ci if (handler) { 9110f66f451Sopenharmony_ci struct arg_list *al = xmalloc(sizeof(struct arg_list)); 9120f66f451Sopenharmony_ci 9130f66f451Sopenharmony_ci al->next = toys.xexit; 9140f66f451Sopenharmony_ci al->arg = handler; 9150f66f451Sopenharmony_ci toys.xexit = al; 9160f66f451Sopenharmony_ci } else { 9170f66f451Sopenharmony_ci llist_traverse(toys.xexit, free); 9180f66f451Sopenharmony_ci toys.xexit = 0; 9190f66f451Sopenharmony_ci } 9200f66f451Sopenharmony_ci} 9210f66f451Sopenharmony_ci 9220f66f451Sopenharmony_ci// Output a nicely formatted 80-column table of all the signals. 9230f66f451Sopenharmony_civoid list_signals() 9240f66f451Sopenharmony_ci{ 9250f66f451Sopenharmony_ci int i = 0, count = 0; 9260f66f451Sopenharmony_ci char *name; 9270f66f451Sopenharmony_ci 9280f66f451Sopenharmony_ci for (; i<=NSIG; i++) { 9290f66f451Sopenharmony_ci if ((name = num_to_sig(i))) { 9300f66f451Sopenharmony_ci printf("%2d) SIG%-9s", i, name); 9310f66f451Sopenharmony_ci if (++count % 5 == 0) putchar('\n'); 9320f66f451Sopenharmony_ci } 9330f66f451Sopenharmony_ci } 9340f66f451Sopenharmony_ci putchar('\n'); 9350f66f451Sopenharmony_ci} 9360f66f451Sopenharmony_ci 9370f66f451Sopenharmony_ci// premute mode bits based on posix mode strings. 9380f66f451Sopenharmony_cimode_t string_to_mode(char *modestr, mode_t mode) 9390f66f451Sopenharmony_ci{ 9400f66f451Sopenharmony_ci char *whos = "ogua", *hows = "=+-", *whats = "xwrstX", *whys = "ogu", 9410f66f451Sopenharmony_ci *s, *str = modestr; 9420f66f451Sopenharmony_ci mode_t extrabits = mode & ~(07777); 9430f66f451Sopenharmony_ci 9440f66f451Sopenharmony_ci // Handle octal mode 9450f66f451Sopenharmony_ci if (isdigit(*str)) { 9460f66f451Sopenharmony_ci mode = estrtol(str, &s, 8); 9470f66f451Sopenharmony_ci if (errno || *s || (mode & ~(07777))) goto barf; 9480f66f451Sopenharmony_ci 9490f66f451Sopenharmony_ci return mode | extrabits; 9500f66f451Sopenharmony_ci } 9510f66f451Sopenharmony_ci 9520f66f451Sopenharmony_ci // Gaze into the bin of permission... 9530f66f451Sopenharmony_ci for (;;) { 9540f66f451Sopenharmony_ci int i, j, dowho, dohow, dowhat, amask; 9550f66f451Sopenharmony_ci 9560f66f451Sopenharmony_ci dowho = dohow = dowhat = amask = 0; 9570f66f451Sopenharmony_ci 9580f66f451Sopenharmony_ci // Find the who, how, and what stanzas, in that order 9590f66f451Sopenharmony_ci while (*str && (s = strchr(whos, *str))) { 9600f66f451Sopenharmony_ci dowho |= 1<<(s-whos); 9610f66f451Sopenharmony_ci str++; 9620f66f451Sopenharmony_ci } 9630f66f451Sopenharmony_ci // If who isn't specified, like "a" but honoring umask. 9640f66f451Sopenharmony_ci if (!dowho) { 9650f66f451Sopenharmony_ci dowho = 8; 9660f66f451Sopenharmony_ci umask(amask=umask(0)); 9670f66f451Sopenharmony_ci } 9680f66f451Sopenharmony_ci if (!*str || !(s = strchr(hows, *str))) goto barf; 9690f66f451Sopenharmony_ci dohow = *(str++); 9700f66f451Sopenharmony_ci 9710f66f451Sopenharmony_ci if (!dohow) goto barf; 9720f66f451Sopenharmony_ci while (*str && (s = strchr(whats, *str))) { 9730f66f451Sopenharmony_ci dowhat |= 1<<(s-whats); 9740f66f451Sopenharmony_ci str++; 9750f66f451Sopenharmony_ci } 9760f66f451Sopenharmony_ci 9770f66f451Sopenharmony_ci // Convert X to x for directory or if already executable somewhere 9780f66f451Sopenharmony_ci if ((dowhat&32) && (S_ISDIR(mode) || (mode&0111))) dowhat |= 1; 9790f66f451Sopenharmony_ci 9800f66f451Sopenharmony_ci // Copy mode from another category? 9810f66f451Sopenharmony_ci if (!dowhat && *str && (s = strchr(whys, *str))) { 9820f66f451Sopenharmony_ci dowhat = (mode>>(3*(s-whys)))&7; 9830f66f451Sopenharmony_ci str++; 9840f66f451Sopenharmony_ci } 9850f66f451Sopenharmony_ci 9860f66f451Sopenharmony_ci // Are we ready to do a thing yet? 9870f66f451Sopenharmony_ci if (*str && *(str++) != ',') goto barf; 9880f66f451Sopenharmony_ci 9890f66f451Sopenharmony_ci // Ok, apply the bits to the mode. 9900f66f451Sopenharmony_ci for (i=0; i<4; i++) { 9910f66f451Sopenharmony_ci for (j=0; j<3; j++) { 9920f66f451Sopenharmony_ci mode_t bit = 0; 9930f66f451Sopenharmony_ci int where = 1<<((3*i)+j); 9940f66f451Sopenharmony_ci 9950f66f451Sopenharmony_ci if (amask & where) continue; 9960f66f451Sopenharmony_ci 9970f66f451Sopenharmony_ci // Figure out new value at this location 9980f66f451Sopenharmony_ci if (i == 3) { 9990f66f451Sopenharmony_ci // suid/sticky bit. 10000f66f451Sopenharmony_ci if (j) { 10010f66f451Sopenharmony_ci if ((dowhat & 8) && (dowho&(8|(1<<i)))) bit++; 10020f66f451Sopenharmony_ci } else if (dowhat & 16) bit++; 10030f66f451Sopenharmony_ci } else { 10040f66f451Sopenharmony_ci if (!(dowho&(8|(1<<i)))) continue; 10050f66f451Sopenharmony_ci if (dowhat&(1<<j)) bit++; 10060f66f451Sopenharmony_ci } 10070f66f451Sopenharmony_ci 10080f66f451Sopenharmony_ci // When selection active, modify bit 10090f66f451Sopenharmony_ci 10100f66f451Sopenharmony_ci if (dohow == '=' || (bit && dohow == '-')) mode &= ~where; 10110f66f451Sopenharmony_ci if (bit && dohow != '-') mode |= where; 10120f66f451Sopenharmony_ci } 10130f66f451Sopenharmony_ci } 10140f66f451Sopenharmony_ci 10150f66f451Sopenharmony_ci if (!*str) break; 10160f66f451Sopenharmony_ci } 10170f66f451Sopenharmony_ci 10180f66f451Sopenharmony_ci return mode|extrabits; 10190f66f451Sopenharmony_cibarf: 10200f66f451Sopenharmony_ci error_exit("bad mode '%s'", modestr); 10210f66f451Sopenharmony_ci} 10220f66f451Sopenharmony_ci 10230f66f451Sopenharmony_ci// Format access mode into a drwxrwxrwx string 10240f66f451Sopenharmony_civoid mode_to_string(mode_t mode, char *buf) 10250f66f451Sopenharmony_ci{ 10260f66f451Sopenharmony_ci char c, d; 10270f66f451Sopenharmony_ci int i, bit; 10280f66f451Sopenharmony_ci 10290f66f451Sopenharmony_ci buf[10]=0; 10300f66f451Sopenharmony_ci for (i=0; i<9; i++) { 10310f66f451Sopenharmony_ci bit = mode & (1<<i); 10320f66f451Sopenharmony_ci c = i%3; 10330f66f451Sopenharmony_ci if (!c && (mode & (1<<((d=i/3)+9)))) { 10340f66f451Sopenharmony_ci c = "tss"[d]; 10350f66f451Sopenharmony_ci if (!bit) c &= ~0x20; 10360f66f451Sopenharmony_ci } else c = bit ? "xwr"[c] : '-'; 10370f66f451Sopenharmony_ci buf[9-i] = c; 10380f66f451Sopenharmony_ci } 10390f66f451Sopenharmony_ci 10400f66f451Sopenharmony_ci if (S_ISDIR(mode)) c = 'd'; 10410f66f451Sopenharmony_ci else if (S_ISBLK(mode)) c = 'b'; 10420f66f451Sopenharmony_ci else if (S_ISCHR(mode)) c = 'c'; 10430f66f451Sopenharmony_ci else if (S_ISLNK(mode)) c = 'l'; 10440f66f451Sopenharmony_ci else if (S_ISFIFO(mode)) c = 'p'; 10450f66f451Sopenharmony_ci else if (S_ISSOCK(mode)) c = 's'; 10460f66f451Sopenharmony_ci else c = '-'; 10470f66f451Sopenharmony_ci *buf = c; 10480f66f451Sopenharmony_ci} 10490f66f451Sopenharmony_ci 10500f66f451Sopenharmony_ci// dirname() can modify its argument or return a pointer to a constant string 10510f66f451Sopenharmony_ci// This always returns a malloc() copy of everyting before last (run of ) '/'. 10520f66f451Sopenharmony_cichar *getdirname(char *name) 10530f66f451Sopenharmony_ci{ 10540f66f451Sopenharmony_ci char *s = xstrdup(name), *ss = strrchr(s, '/'); 10550f66f451Sopenharmony_ci 10560f66f451Sopenharmony_ci while (ss && *ss && *ss == '/' && s != ss) *ss-- = 0; 10570f66f451Sopenharmony_ci 10580f66f451Sopenharmony_ci return s; 10590f66f451Sopenharmony_ci} 10600f66f451Sopenharmony_ci 10610f66f451Sopenharmony_ci// basename() can modify its argument or return a pointer to a constant string 10620f66f451Sopenharmony_ci// This just gives after the last '/' or the whole stirng if no / 10630f66f451Sopenharmony_cichar *getbasename(char *name) 10640f66f451Sopenharmony_ci{ 10650f66f451Sopenharmony_ci char *s = strrchr(name, '/'); 10660f66f451Sopenharmony_ci 10670f66f451Sopenharmony_ci if (s) return s+1; 10680f66f451Sopenharmony_ci 10690f66f451Sopenharmony_ci return name; 10700f66f451Sopenharmony_ci} 10710f66f451Sopenharmony_ci 10720f66f451Sopenharmony_ci// Return pointer to xabspath(file) if file is under dir, else 0 10730f66f451Sopenharmony_cichar *fileunderdir(char *file, char *dir) 10740f66f451Sopenharmony_ci{ 10750f66f451Sopenharmony_ci char *s1 = xabspath(dir, 1), *s2 = xabspath(file, -1), *ss = s2; 10760f66f451Sopenharmony_ci int rc = s1 && s2 && strstart(&ss, s1) && (!s1[1] || s2[strlen(s1)] == '/'); 10770f66f451Sopenharmony_ci 10780f66f451Sopenharmony_ci free(s1); 10790f66f451Sopenharmony_ci if (!rc) free(s2); 10800f66f451Sopenharmony_ci 10810f66f451Sopenharmony_ci return rc ? s2 : 0; 10820f66f451Sopenharmony_ci} 10830f66f451Sopenharmony_ci 10840f66f451Sopenharmony_ci// Execute a callback for each PID that matches a process name from a list. 10850f66f451Sopenharmony_civoid names_to_pid(char **names, int (*callback)(pid_t pid, char *name), 10860f66f451Sopenharmony_ci int scripts) 10870f66f451Sopenharmony_ci{ 10880f66f451Sopenharmony_ci DIR *dp; 10890f66f451Sopenharmony_ci struct dirent *entry; 10900f66f451Sopenharmony_ci 10910f66f451Sopenharmony_ci if (!(dp = opendir("/proc"))) perror_exit("no /proc"); 10920f66f451Sopenharmony_ci 10930f66f451Sopenharmony_ci while ((entry = readdir(dp))) { 10940f66f451Sopenharmony_ci unsigned u = atoi(entry->d_name); 10950f66f451Sopenharmony_ci char *cmd = 0, *comm = 0, **cur; 10960f66f451Sopenharmony_ci off_t len; 10970f66f451Sopenharmony_ci 10980f66f451Sopenharmony_ci if (!u) continue; 10990f66f451Sopenharmony_ci 11000f66f451Sopenharmony_ci // Comm is original name of executable (argv[0] could be #! interpreter) 11010f66f451Sopenharmony_ci // but it's limited to 15 characters 11020f66f451Sopenharmony_ci if (scripts) { 11030f66f451Sopenharmony_ci sprintf(libbuf, "/proc/%u/comm", u); 11040f66f451Sopenharmony_ci len = sizeof(libbuf); 11050f66f451Sopenharmony_ci if (!(comm = readfileat(AT_FDCWD, libbuf, libbuf, &len)) || !len) 11060f66f451Sopenharmony_ci continue; 11070f66f451Sopenharmony_ci if (libbuf[len-1] == '\n') libbuf[--len] = 0; 11080f66f451Sopenharmony_ci } 11090f66f451Sopenharmony_ci 11100f66f451Sopenharmony_ci for (cur = names; *cur; cur++) { 11110f66f451Sopenharmony_ci struct stat st1, st2; 11120f66f451Sopenharmony_ci char *bb = getbasename(*cur); 11130f66f451Sopenharmony_ci off_t len = strlen(bb); 11140f66f451Sopenharmony_ci 11150f66f451Sopenharmony_ci // Fast path: only matching a filename (no path) that fits in comm. 11160f66f451Sopenharmony_ci // `len` must be 14 or less because with a full 15 bytes we don't 11170f66f451Sopenharmony_ci // know whether the name fit or was truncated. 11180f66f451Sopenharmony_ci if (scripts && len<=14 && bb==*cur && !strcmp(comm, bb)) goto match; 11190f66f451Sopenharmony_ci 11200f66f451Sopenharmony_ci // If we have a path to existing file only match if same inode 11210f66f451Sopenharmony_ci if (bb!=*cur && !stat(*cur, &st1)) { 11220f66f451Sopenharmony_ci char buf[32]; 11230f66f451Sopenharmony_ci 11240f66f451Sopenharmony_ci sprintf(buf, "/proc/%u/exe", u); 11250f66f451Sopenharmony_ci if (stat(buf, &st2)) continue; 11260f66f451Sopenharmony_ci if (st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino) continue; 11270f66f451Sopenharmony_ci goto match; 11280f66f451Sopenharmony_ci } 11290f66f451Sopenharmony_ci 11300f66f451Sopenharmony_ci // Nope, gotta read command line to confirm 11310f66f451Sopenharmony_ci if (!cmd) { 11320f66f451Sopenharmony_ci sprintf(cmd = libbuf+16, "/proc/%u/cmdline", u); 11330f66f451Sopenharmony_ci len = sizeof(libbuf)-17; 11340f66f451Sopenharmony_ci if (!(cmd = readfileat(AT_FDCWD, cmd, cmd, &len))) continue; 11350f66f451Sopenharmony_ci // readfile only guarantees one null terminator and we need two 11360f66f451Sopenharmony_ci // (yes the kernel should do this for us, don't care) 11370f66f451Sopenharmony_ci cmd[len] = 0; 11380f66f451Sopenharmony_ci } 11390f66f451Sopenharmony_ci if (!strcmp(bb, getbasename(cmd))) goto match; 11400f66f451Sopenharmony_ci if (scripts && !strcmp(bb, getbasename(cmd+strlen(cmd)+1))) goto match; 11410f66f451Sopenharmony_ci continue; 11420f66f451Sopenharmony_cimatch: 11430f66f451Sopenharmony_ci if (callback(u, *cur)) break; 11440f66f451Sopenharmony_ci } 11450f66f451Sopenharmony_ci } 11460f66f451Sopenharmony_ci closedir(dp); 11470f66f451Sopenharmony_ci} 11480f66f451Sopenharmony_ci 11490f66f451Sopenharmony_ci// display first "dgt" many digits of number plus unit (kilo-exabytes) 11500f66f451Sopenharmony_ciint human_readable_long(char *buf, unsigned long long num, int dgt, int style) 11510f66f451Sopenharmony_ci{ 11520f66f451Sopenharmony_ci unsigned long long snap = 0; 11530f66f451Sopenharmony_ci int len, unit, divisor = (style&HR_1000) ? 1000 : 1024; 11540f66f451Sopenharmony_ci 11550f66f451Sopenharmony_ci // Divide rounding up until we have 3 or fewer digits. Since the part we 11560f66f451Sopenharmony_ci // print is decimal, the test is 999 even when we divide by 1024. 11570f66f451Sopenharmony_ci // We can't run out of units because 1<<64 is 18 exabytes. 11580f66f451Sopenharmony_ci for (unit = 0; snprintf(0, 0, "%llu", num)>dgt; unit++) 11590f66f451Sopenharmony_ci num = ((snap = num)+(divisor/2))/divisor; 11600f66f451Sopenharmony_ci len = sprintf(buf, "%llu", num); 11610f66f451Sopenharmony_ci if (unit && len == 1) { 11620f66f451Sopenharmony_ci // Redo rounding for 1.2M case, this works with and without HR_1000. 11630f66f451Sopenharmony_ci num = snap/divisor; 11640f66f451Sopenharmony_ci snap -= num*divisor; 11650f66f451Sopenharmony_ci snap = ((snap*100)+50)/divisor; 11660f66f451Sopenharmony_ci snap /= 10; 11670f66f451Sopenharmony_ci len = sprintf(buf, "%llu.%llu", num, snap); 11680f66f451Sopenharmony_ci } 11690f66f451Sopenharmony_ci if (style & HR_SPACE) buf[len++] = ' '; 11700f66f451Sopenharmony_ci if (unit) { 11710f66f451Sopenharmony_ci unit = " kMGTPE"[unit]; 11720f66f451Sopenharmony_ci 11730f66f451Sopenharmony_ci if (!(style&HR_1000)) unit = toupper(unit); 11740f66f451Sopenharmony_ci buf[len++] = unit; 11750f66f451Sopenharmony_ci } else if (style & HR_B) buf[len++] = 'B'; 11760f66f451Sopenharmony_ci buf[len] = 0; 11770f66f451Sopenharmony_ci 11780f66f451Sopenharmony_ci return len; 11790f66f451Sopenharmony_ci} 11800f66f451Sopenharmony_ci 11810f66f451Sopenharmony_ci// Give 3 digit estimate + units ala 999M or 1.7T 11820f66f451Sopenharmony_ciint human_readable(char *buf, unsigned long long num, int style) 11830f66f451Sopenharmony_ci{ 11840f66f451Sopenharmony_ci return human_readable_long(buf, num, 3, style); 11850f66f451Sopenharmony_ci} 11860f66f451Sopenharmony_ci 11870f66f451Sopenharmony_ci// The qsort man page says you can use alphasort, the posix committee 11880f66f451Sopenharmony_ci// disagreed, and doubled down: http://austingroupbugs.net/view.php?id=142 11890f66f451Sopenharmony_ci// So just do our own. (The const is entirely to humor the stupid compiler.) 11900f66f451Sopenharmony_ciint qstrcmp(const void *a, const void *b) 11910f66f451Sopenharmony_ci{ 11920f66f451Sopenharmony_ci return strcmp(*(char **)a, *(char **)b); 11930f66f451Sopenharmony_ci} 11940f66f451Sopenharmony_ci 11950f66f451Sopenharmony_ci// See https://tools.ietf.org/html/rfc4122, specifically section 4.4 11960f66f451Sopenharmony_ci// "Algorithms for Creating a UUID from Truly Random or Pseudo-Random 11970f66f451Sopenharmony_ci// Numbers". 11980f66f451Sopenharmony_civoid create_uuid(char *uuid) 11990f66f451Sopenharmony_ci{ 12000f66f451Sopenharmony_ci // "Set all the ... bits to randomly (or pseudo-randomly) chosen values". 12010f66f451Sopenharmony_ci xgetrandom(uuid, 16, 0); 12020f66f451Sopenharmony_ci 12030f66f451Sopenharmony_ci // "Set the four most significant bits ... of the time_hi_and_version 12040f66f451Sopenharmony_ci // field to the 4-bit version number [4]". 12050f66f451Sopenharmony_ci uuid[6] = (uuid[6] & 0x0F) | 0x40; 12060f66f451Sopenharmony_ci // "Set the two most significant bits (bits 6 and 7) of 12070f66f451Sopenharmony_ci // clock_seq_hi_and_reserved to zero and one, respectively". 12080f66f451Sopenharmony_ci uuid[8] = (uuid[8] & 0x3F) | 0x80; 12090f66f451Sopenharmony_ci} 12100f66f451Sopenharmony_ci 12110f66f451Sopenharmony_cichar *show_uuid(char *uuid) 12120f66f451Sopenharmony_ci{ 12130f66f451Sopenharmony_ci char *out = libbuf; 12140f66f451Sopenharmony_ci int i; 12150f66f451Sopenharmony_ci 12160f66f451Sopenharmony_ci for (i=0; i<16; i++) out+=sprintf(out, "-%02x"+!(0x550&(1<<i)), uuid[i]); 12170f66f451Sopenharmony_ci *out = 0; 12180f66f451Sopenharmony_ci 12190f66f451Sopenharmony_ci return libbuf; 12200f66f451Sopenharmony_ci} 12210f66f451Sopenharmony_ci 12220f66f451Sopenharmony_ci// Returns pointer to letter at end, 0 if none. *start = initial % 12230f66f451Sopenharmony_cichar *next_printf(char *s, char **start) 12240f66f451Sopenharmony_ci{ 12250f66f451Sopenharmony_ci for (; *s; s++) { 12260f66f451Sopenharmony_ci if (*s != '%') continue; 12270f66f451Sopenharmony_ci if (*++s == '%') continue; 12280f66f451Sopenharmony_ci if (start) *start = s-1; 12290f66f451Sopenharmony_ci while (0 <= stridx("0'#-+ ", *s)) s++; 12300f66f451Sopenharmony_ci while (isdigit(*s)) s++; 12310f66f451Sopenharmony_ci if (*s == '.') s++; 12320f66f451Sopenharmony_ci while (isdigit(*s)) s++; 12330f66f451Sopenharmony_ci 12340f66f451Sopenharmony_ci return s; 12350f66f451Sopenharmony_ci } 12360f66f451Sopenharmony_ci 12370f66f451Sopenharmony_ci return 0; 12380f66f451Sopenharmony_ci} 12390f66f451Sopenharmony_ci 12400f66f451Sopenharmony_ciint dev_minor(int dev) 12410f66f451Sopenharmony_ci{ 12420f66f451Sopenharmony_ci return ((dev&0xfff00000)>>12)|(dev&0xff); 12430f66f451Sopenharmony_ci} 12440f66f451Sopenharmony_ci 12450f66f451Sopenharmony_ciint dev_major(int dev) 12460f66f451Sopenharmony_ci{ 12470f66f451Sopenharmony_ci return (dev&0xfff00)>>8; 12480f66f451Sopenharmony_ci} 12490f66f451Sopenharmony_ci 12500f66f451Sopenharmony_ciint dev_makedev(int major, int minor) 12510f66f451Sopenharmony_ci{ 12520f66f451Sopenharmony_ci return (minor&0xff)|((major&0xfff)<<8)|((minor&0xfff00)<<12); 12530f66f451Sopenharmony_ci} 12540f66f451Sopenharmony_ci 12550f66f451Sopenharmony_ci// Return cached passwd entries. 12560f66f451Sopenharmony_cistruct passwd *bufgetpwuid(uid_t uid) 12570f66f451Sopenharmony_ci{ 12580f66f451Sopenharmony_ci struct pwuidbuf_list { 12590f66f451Sopenharmony_ci struct pwuidbuf_list *next; 12600f66f451Sopenharmony_ci struct passwd pw; 12610f66f451Sopenharmony_ci } *list = 0; 12620f66f451Sopenharmony_ci struct passwd *temp; 12630f66f451Sopenharmony_ci static struct pwuidbuf_list *pwuidbuf; 12640f66f451Sopenharmony_ci unsigned size = 256; 12650f66f451Sopenharmony_ci 12660f66f451Sopenharmony_ci // If we already have this one, return it. 12670f66f451Sopenharmony_ci for (list = pwuidbuf; list; list = list->next) 12680f66f451Sopenharmony_ci if (list->pw.pw_uid == uid) return &(list->pw); 12690f66f451Sopenharmony_ci 12700f66f451Sopenharmony_ci for (;;) { 12710f66f451Sopenharmony_ci list = xrealloc(list, size *= 2); 12720f66f451Sopenharmony_ci errno = getpwuid_r(uid, &list->pw, sizeof(*list)+(char *)list, 12730f66f451Sopenharmony_ci size-sizeof(*list), &temp); 12740f66f451Sopenharmony_ci if (errno != ERANGE) break; 12750f66f451Sopenharmony_ci } 12760f66f451Sopenharmony_ci 12770f66f451Sopenharmony_ci if (!temp) { 12780f66f451Sopenharmony_ci free(list); 12790f66f451Sopenharmony_ci 12800f66f451Sopenharmony_ci return 0; 12810f66f451Sopenharmony_ci } 12820f66f451Sopenharmony_ci list->next = pwuidbuf; 12830f66f451Sopenharmony_ci pwuidbuf = list; 12840f66f451Sopenharmony_ci 12850f66f451Sopenharmony_ci return &list->pw; 12860f66f451Sopenharmony_ci} 12870f66f451Sopenharmony_ci 12880f66f451Sopenharmony_ci// Return cached group entries. 12890f66f451Sopenharmony_cistruct group *bufgetgrgid(gid_t gid) 12900f66f451Sopenharmony_ci{ 12910f66f451Sopenharmony_ci struct grgidbuf_list { 12920f66f451Sopenharmony_ci struct grgidbuf_list *next; 12930f66f451Sopenharmony_ci struct group gr; 12940f66f451Sopenharmony_ci } *list = 0; 12950f66f451Sopenharmony_ci struct group *temp; 12960f66f451Sopenharmony_ci static struct grgidbuf_list *grgidbuf; 12970f66f451Sopenharmony_ci unsigned size = 256; 12980f66f451Sopenharmony_ci 12990f66f451Sopenharmony_ci for (list = grgidbuf; list; list = list->next) 13000f66f451Sopenharmony_ci if (list->gr.gr_gid == gid) return &(list->gr); 13010f66f451Sopenharmony_ci 13020f66f451Sopenharmony_ci for (;;) { 13030f66f451Sopenharmony_ci list = xrealloc(list, size *= 2); 13040f66f451Sopenharmony_ci errno = getgrgid_r(gid, &list->gr, sizeof(*list)+(char *)list, 13050f66f451Sopenharmony_ci size-sizeof(*list), &temp); 13060f66f451Sopenharmony_ci if (errno != ERANGE) break; 13070f66f451Sopenharmony_ci } 13080f66f451Sopenharmony_ci if (!temp) { 13090f66f451Sopenharmony_ci free(list); 13100f66f451Sopenharmony_ci 13110f66f451Sopenharmony_ci return 0; 13120f66f451Sopenharmony_ci } 13130f66f451Sopenharmony_ci list->next = grgidbuf; 13140f66f451Sopenharmony_ci grgidbuf = list; 13150f66f451Sopenharmony_ci 13160f66f451Sopenharmony_ci return &list->gr; 13170f66f451Sopenharmony_ci} 13180f66f451Sopenharmony_ci 13190f66f451Sopenharmony_ci// Always null terminates, returns 0 for failure, len for success 13200f66f451Sopenharmony_ciint readlinkat0(int dirfd, char *path, char *buf, int len) 13210f66f451Sopenharmony_ci{ 13220f66f451Sopenharmony_ci if (!len) return 0; 13230f66f451Sopenharmony_ci 13240f66f451Sopenharmony_ci len = readlinkat(dirfd, path, buf, len-1); 13250f66f451Sopenharmony_ci if (len<0) len = 0; 13260f66f451Sopenharmony_ci buf[len] = 0; 13270f66f451Sopenharmony_ci 13280f66f451Sopenharmony_ci return len; 13290f66f451Sopenharmony_ci} 13300f66f451Sopenharmony_ci 13310f66f451Sopenharmony_ciint readlink0(char *path, char *buf, int len) 13320f66f451Sopenharmony_ci{ 13330f66f451Sopenharmony_ci return readlinkat0(AT_FDCWD, path, buf, len); 13340f66f451Sopenharmony_ci} 13350f66f451Sopenharmony_ci 13360f66f451Sopenharmony_ci// Do regex matching with len argument to handle embedded NUL bytes in string 13370f66f451Sopenharmony_ciint regexec0(regex_t *preg, char *string, long len, int nmatch, 13380f66f451Sopenharmony_ci regmatch_t *pmatch, int eflags) 13390f66f451Sopenharmony_ci{ 13400f66f451Sopenharmony_ci regmatch_t backup; 13410f66f451Sopenharmony_ci 13420f66f451Sopenharmony_ci if (!nmatch) pmatch = &backup; 13430f66f451Sopenharmony_ci pmatch->rm_so = 0; 13440f66f451Sopenharmony_ci pmatch->rm_eo = len; 13450f66f451Sopenharmony_ci return regexec(preg, string, nmatch, pmatch, eflags|REG_STARTEND); 13460f66f451Sopenharmony_ci} 13470f66f451Sopenharmony_ci 13480f66f451Sopenharmony_ci// Return user name or string representation of number, returned buffer 13490f66f451Sopenharmony_ci// lasts until next call. 13500f66f451Sopenharmony_cichar *getusername(uid_t uid) 13510f66f451Sopenharmony_ci{ 13520f66f451Sopenharmony_ci struct passwd *pw = bufgetpwuid(uid); 13530f66f451Sopenharmony_ci static char unum[12]; 13540f66f451Sopenharmony_ci 13550f66f451Sopenharmony_ci sprintf(unum, "%u", (unsigned)uid); 13560f66f451Sopenharmony_ci return pw ? pw->pw_name : unum; 13570f66f451Sopenharmony_ci} 13580f66f451Sopenharmony_ci 13590f66f451Sopenharmony_ci// Return group name or string representation of number, returned buffer 13600f66f451Sopenharmony_ci// lasts until next call. 13610f66f451Sopenharmony_cichar *getgroupname(gid_t gid) 13620f66f451Sopenharmony_ci{ 13630f66f451Sopenharmony_ci struct group *gr = bufgetgrgid(gid); 13640f66f451Sopenharmony_ci static char gnum[12]; 13650f66f451Sopenharmony_ci 13660f66f451Sopenharmony_ci sprintf(gnum, "%u", (unsigned)gid); 13670f66f451Sopenharmony_ci return gr ? gr->gr_name : gnum; 13680f66f451Sopenharmony_ci} 13690f66f451Sopenharmony_ci 13700f66f451Sopenharmony_ci// Iterate over lines in file, calling function. Function can write 0 to 13710f66f451Sopenharmony_ci// the line pointer if they want to keep it, or 1 to terminate processing, 13720f66f451Sopenharmony_ci// otherwise line is freed. Passed file descriptor is closed at the end. 13730f66f451Sopenharmony_ci// At EOF calls function(0, 0) 13740f66f451Sopenharmony_civoid do_lines(int fd, char delim, void (*call)(char **pline, long len)) 13750f66f451Sopenharmony_ci{ 13760f66f451Sopenharmony_ci FILE *fp = fd ? xfdopen(fd, "r") : stdin; 13770f66f451Sopenharmony_ci 13780f66f451Sopenharmony_ci for (;;) { 13790f66f451Sopenharmony_ci char *line = 0; 13800f66f451Sopenharmony_ci ssize_t len; 13810f66f451Sopenharmony_ci 13820f66f451Sopenharmony_ci len = getdelim(&line, (void *)&len, delim, fp); 13830f66f451Sopenharmony_ci if (len > 0) { 13840f66f451Sopenharmony_ci call(&line, len); 13850f66f451Sopenharmony_ci if (line == (void *)1) break; 13860f66f451Sopenharmony_ci free(line); 13870f66f451Sopenharmony_ci } else break; 13880f66f451Sopenharmony_ci } 13890f66f451Sopenharmony_ci call(0, 0); 13900f66f451Sopenharmony_ci 13910f66f451Sopenharmony_ci if (fd) fclose(fp); 13920f66f451Sopenharmony_ci} 13930f66f451Sopenharmony_ci 13940f66f451Sopenharmony_ci// Return unix time in milliseconds 13950f66f451Sopenharmony_cilong long millitime(void) 13960f66f451Sopenharmony_ci{ 13970f66f451Sopenharmony_ci struct timespec ts; 13980f66f451Sopenharmony_ci 13990f66f451Sopenharmony_ci clock_gettime(CLOCK_MONOTONIC, &ts); 14000f66f451Sopenharmony_ci return ts.tv_sec*1000+ts.tv_nsec/1000000; 14010f66f451Sopenharmony_ci} 14020f66f451Sopenharmony_ci 14030f66f451Sopenharmony_ci// Formats `ts` in ISO format ("2018-06-28 15:08:58.846386216 -0700"). 14040f66f451Sopenharmony_cichar *format_iso_time(char *buf, size_t len, struct timespec *ts) 14050f66f451Sopenharmony_ci{ 14060f66f451Sopenharmony_ci char *s = buf; 14070f66f451Sopenharmony_ci 14080f66f451Sopenharmony_ci s += strftime(s, len, "%F %T", localtime(&(ts->tv_sec))); 14090f66f451Sopenharmony_ci s += sprintf(s, ".%09ld ", ts->tv_nsec); 14100f66f451Sopenharmony_ci s += strftime(s, len-strlen(buf), "%z", localtime(&(ts->tv_sec))); 14110f66f451Sopenharmony_ci 14120f66f451Sopenharmony_ci return buf; 14130f66f451Sopenharmony_ci} 14140f66f451Sopenharmony_ci 14150f66f451Sopenharmony_ci// Syslog with the openlog/closelog, autodetecting daemon status via no tty 14160f66f451Sopenharmony_ci 14170f66f451Sopenharmony_civoid loggit(int priority, char *format, ...) 14180f66f451Sopenharmony_ci{ 14190f66f451Sopenharmony_ci int i, facility = LOG_DAEMON; 14200f66f451Sopenharmony_ci va_list va; 14210f66f451Sopenharmony_ci 14220f66f451Sopenharmony_ci for (i = 0; i<3; i++) if (isatty(i)) facility = LOG_AUTH; 14230f66f451Sopenharmony_ci openlog(toys.which->name, LOG_PID, facility); 14240f66f451Sopenharmony_ci va_start(va, format); 14250f66f451Sopenharmony_ci vsyslog(priority, format, va); 14260f66f451Sopenharmony_ci va_end(va); 14270f66f451Sopenharmony_ci closelog(); 14280f66f451Sopenharmony_ci} 14290f66f451Sopenharmony_ci 14300f66f451Sopenharmony_ci// Calculate tar packet checksum, with cksum field treated as 8 spaces 14310f66f451Sopenharmony_ciunsigned tar_cksum(void *data) 14320f66f451Sopenharmony_ci{ 14330f66f451Sopenharmony_ci unsigned i, cksum = 8*' '; 14340f66f451Sopenharmony_ci 14350f66f451Sopenharmony_ci for (i = 0; i<500; i += (i==147) ? 9 : 1) cksum += ((char *)data)[i]; 14360f66f451Sopenharmony_ci 14370f66f451Sopenharmony_ci return cksum; 14380f66f451Sopenharmony_ci} 14390f66f451Sopenharmony_ci 14400f66f451Sopenharmony_ci// is this a valid tar header? 14410f66f451Sopenharmony_ciint is_tar_header(void *pkt) 14420f66f451Sopenharmony_ci{ 14430f66f451Sopenharmony_ci char *p = pkt; 14440f66f451Sopenharmony_ci int i = 0; 14450f66f451Sopenharmony_ci 14460f66f451Sopenharmony_ci if (p[257] && memcmp("ustar", p+257, 5)) return 0; 14470f66f451Sopenharmony_ci if (p[148] != '0' && p[148] != ' ') return 0; 14480f66f451Sopenharmony_ci sscanf(p+148, "%8o", &i); 14490f66f451Sopenharmony_ci 14500f66f451Sopenharmony_ci return i && tar_cksum(pkt) == i; 14510f66f451Sopenharmony_ci} 1452