aboutsummaryrefslogtreecommitdiff
path: root/lib/lib.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/lib.c')
-rw-r--r--lib/lib.c1217
1 files changed, 606 insertions, 611 deletions
diff --git a/lib/lib.c b/lib/lib.c
index 2c2ea63e..06828a40 100644
--- a/lib/lib.c
+++ b/lib/lib.c
@@ -1,4 +1,3 @@
-/* vi: set sw=4 ts=4 :*/
/* lib.c - reusable stuff.
*
* Functions with the x prefix are wrappers for library functions. They either
@@ -14,257 +13,257 @@
// Strcpy with size checking: exit if there's not enough space for the string.
void xstrcpy(char *dest, char *src, size_t size)
{
- if (strlen(src)+1 > size) error_exit("xstrcpy");
- strcpy(dest, src);
+ if (strlen(src)+1 > size) error_exit("xstrcpy");
+ strcpy(dest, src);
}
void verror_msg(char *msg, int err, va_list va)
{
- char *s = ": %s";
+ char *s = ": %s";
- fprintf(stderr, "%s: ", toys.which->name);
- if (msg) vfprintf(stderr, msg, va);
- else s+=2;
- if (err) fprintf(stderr, s, strerror(err));
- putc('\n', stderr);
+ fprintf(stderr, "%s: ", toys.which->name);
+ if (msg) vfprintf(stderr, msg, va);
+ else s+=2;
+ if (err) fprintf(stderr, s, strerror(err));
+ putc('\n', stderr);
}
void error_msg(char *msg, ...)
{
- va_list va;
+ va_list va;
- va_start(va, msg);
- verror_msg(msg, 0, va);
- va_end(va);
+ va_start(va, msg);
+ verror_msg(msg, 0, va);
+ va_end(va);
}
void perror_msg(char *msg, ...)
{
- va_list va;
+ va_list va;
- va_start(va, msg);
- verror_msg(msg, errno, va);
- va_end(va);
+ va_start(va, msg);
+ verror_msg(msg, errno, va);
+ va_end(va);
}
// Die with an error message.
void error_exit(char *msg, ...)
{
- va_list va;
+ va_list va;
- if (CFG_HELP && toys.exithelp) {
- *toys.optargs=*toys.argv;
- USE_HELP(help_main();) // dear gcc: shut up.
- fprintf(stderr,"\n");
- }
+ if (CFG_HELP && toys.exithelp) {
+ *toys.optargs=*toys.argv;
+ USE_HELP(help_main();) // dear gcc: shut up.
+ fprintf(stderr,"\n");
+ }
- va_start(va, msg);
- verror_msg(msg, 0, va);
- va_end(va);
+ va_start(va, msg);
+ verror_msg(msg, 0, va);
+ va_end(va);
- exit(!toys.exitval ? 1 : toys.exitval);
+ exit(!toys.exitval ? 1 : toys.exitval);
}
// Die with an error message and strerror(errno)
void perror_exit(char *msg, ...)
{
- va_list va;
+ va_list va;
- va_start(va, msg);
- verror_msg(msg, errno, va);
- va_end(va);
+ va_start(va, msg);
+ verror_msg(msg, errno, va);
+ va_end(va);
- exit(!toys.exitval ? 1 : toys.exitval);
+ exit(!toys.exitval ? 1 : toys.exitval);
}
// Die unless we can allocate memory.
void *xmalloc(size_t size)
{
- void *ret = malloc(size);
- if (!ret) error_exit("xmalloc");
+ void *ret = malloc(size);
+ if (!ret) error_exit("xmalloc");
- return ret;
+ return ret;
}
// Die unless we can allocate prezeroed memory.
void *xzalloc(size_t size)
{
- void *ret = xmalloc(size);
- memset(ret, 0, size);
- return ret;
+ void *ret = xmalloc(size);
+ memset(ret, 0, size);
+ return ret;
}
// Die unless we can change the size of an existing allocation, possibly
// moving it. (Notice different arguments from libc function.)
void *xrealloc(void *ptr, size_t size)
{
- ptr = realloc(ptr, size);
- if (!ptr) error_exit("xrealloc");
+ ptr = realloc(ptr, size);
+ if (!ptr) error_exit("xrealloc");
- return ptr;
+ return ptr;
}
// Die unless we can allocate a copy of this many bytes of string.
char *xstrndup(char *s, size_t n)
{
- char *ret = xmalloc(++n);
- strncpy(ret, s, n);
- ret[--n]=0;
+ char *ret = xmalloc(++n);
+ strncpy(ret, s, n);
+ ret[--n]=0;
- return ret;
+ return ret;
}
// Die unless we can allocate a copy of this string.
char *xstrdup(char *s)
{
- return xstrndup(s, strlen(s));
+ return xstrndup(s, strlen(s));
}
// Die unless we can allocate enough space to sprintf() into.
char *xmsprintf(char *format, ...)
{
- va_list va, va2;
- int len;
- char *ret;
+ va_list va, va2;
+ int len;
+ char *ret;
- va_start(va, format);
- va_copy(va2, va);
+ va_start(va, format);
+ va_copy(va2, va);
- // How long is it?
- len = vsnprintf(0, 0, format, va);
- len++;
- va_end(va);
+ // How long is it?
+ len = vsnprintf(0, 0, format, va);
+ len++;
+ va_end(va);
- // Allocate and do the sprintf()
- ret = xmalloc(len);
- vsnprintf(ret, len, format, va2);
- va_end(va2);
+ // Allocate and do the sprintf()
+ ret = xmalloc(len);
+ vsnprintf(ret, len, format, va2);
+ va_end(va2);
- return ret;
+ return ret;
}
void xprintf(char *format, ...)
{
- va_list va;
- va_start(va, format);
+ va_list va;
+ va_start(va, format);
- vprintf(format, va);
- if (ferror(stdout)) perror_exit("write");
+ vprintf(format, va);
+ if (ferror(stdout)) perror_exit("write");
}
void xputs(char *s)
{
- if (EOF == puts(s)) perror_exit("write");
+ if (EOF == puts(s)) perror_exit("write");
}
void xputc(char c)
{
- if (EOF == fputc(c, stdout) || fflush(stdout)) perror_exit("write");
+ if (EOF == fputc(c, stdout) || fflush(stdout)) perror_exit("write");
}
void xflush(void)
{
- if (fflush(stdout)) perror_exit("write");;
+ if (fflush(stdout)) perror_exit("write");;
}
// Die unless we can exec argv[] (or run builtin command). Note that anything
// with a path isn't a builtin, so /bin/sh won't match the builtin sh.
void xexec(char **argv)
{
- toy_exec(argv);
- execvp(argv[0], argv);
+ toy_exec(argv);
+ execvp(argv[0], argv);
- perror_exit("exec %s", argv[0]);
+ perror_exit("exec %s", argv[0]);
}
void xaccess(char *path, int flags)
{
- if (access(path, flags)) perror_exit("Can't access '%s'", path);
+ if (access(path, flags)) perror_exit("Can't access '%s'", path);
}
// Die unless we can delete a file. (File must exist to be deleted.)
void xunlink(char *path)
{
- if (unlink(path)) perror_exit("unlink '%s'", path);
+ if (unlink(path)) perror_exit("unlink '%s'", path);
}
// Die unless we can open/create a file, returning file descriptor.
int xcreate(char *path, int flags, int mode)
{
- int fd = open(path, flags, mode);
- if (fd == -1) perror_exit("%s", path);
- return fd;
+ int fd = open(path, flags, mode);
+ if (fd == -1) perror_exit("%s", path);
+ return fd;
}
// Die unless we can open a file, returning file descriptor.
int xopen(char *path, int flags)
{
- return xcreate(path, flags, 0);
+ return xcreate(path, flags, 0);
}
void xclose(int fd)
{
- if (close(fd)) perror_exit("xclose");
+ if (close(fd)) perror_exit("xclose");
}
int xdup(int fd)
{
- if (fd != -1) {
- fd = dup(fd);
- if (fd == -1) perror_exit("xdup");
- }
- return fd;
+ if (fd != -1) {
+ fd = dup(fd);
+ if (fd == -1) perror_exit("xdup");
+ }
+ return fd;
}
// Die unless we can open/create a file, returning FILE *.
FILE *xfopen(char *path, char *mode)
{
- FILE *f = fopen(path, mode);
- if (!f) perror_exit("No file %s", path);
- return f;
+ FILE *f = fopen(path, mode);
+ if (!f) perror_exit("No file %s", path);
+ return f;
}
// Keep reading until full or EOF
ssize_t readall(int fd, void *buf, size_t len)
{
- size_t count = 0;
+ size_t count = 0;
- while (count<len) {
- int i = read(fd, buf+count, len-count);
- if (!i) break;
- if (i<0) return i;
- count += i;
- }
+ while (count<len) {
+ int i = read(fd, buf+count, len-count);
+ if (!i) break;
+ if (i<0) return i;
+ count += i;
+ }
- return count;
+ return count;
}
// Keep writing until done or EOF
ssize_t writeall(int fd, void *buf, size_t len)
{
- size_t count = 0;
- while (count<len) {
- int i = write(fd, buf+count, len-count);
- if (i<1) return i;
- count += i;
- }
+ size_t count = 0;
+ while (count<len) {
+ int i = write(fd, buf+count, len-count);
+ if (i<1) return i;
+ count += i;
+ }
- return count;
+ return count;
}
// Die if there's an error other than EOF.
size_t xread(int fd, void *buf, size_t len)
{
- ssize_t ret = read(fd, buf, len);
- if (ret < 0) perror_exit("xread");
+ ssize_t ret = read(fd, buf, len);
+ if (ret < 0) perror_exit("xread");
- return ret;
+ return ret;
}
void xreadall(int fd, void *buf, size_t len)
{
- if (len != readall(fd, buf, len)) perror_exit("xreadall");
+ if (len != readall(fd, buf, len)) perror_exit("xreadall");
}
// There's no xwriteall(), just xwrite(). When we read, there may or may not
@@ -273,51 +272,51 @@ void xreadall(int fd, void *buf, size_t len)
void xwrite(int fd, void *buf, size_t len)
{
- if (len != writeall(fd, buf, len)) perror_exit("xwrite");
+ if (len != writeall(fd, buf, len)) perror_exit("xwrite");
}
// Die if lseek fails, probably due to being called on a pipe.
off_t xlseek(int fd, off_t offset, int whence)
{
- offset = lseek(fd, offset, whence);
- if (offset<0) perror_exit("lseek");
+ offset = lseek(fd, offset, whence);
+ if (offset<0) perror_exit("lseek");
- return offset;
+ return offset;
}
off_t lskip(int fd, off_t offset)
{
- off_t and = lseek(fd, offset, SEEK_CUR);
+ off_t and = lseek(fd, offset, SEEK_CUR);
- if (and != -1 && offset >= lseek(fd, offset, SEEK_END)
- && offset+and == lseek(fd, offset+and, SEEK_SET)) return 0;
- else {
- char buf[4096];
- while (offset>0) {
- int try = offset>sizeof(buf) ? sizeof(buf) : offset, or;
+ if (and != -1 && offset >= lseek(fd, offset, SEEK_END)
+ && offset+and == lseek(fd, offset+and, SEEK_SET)) return 0;
+ else {
+ char buf[4096];
+ while (offset>0) {
+ int try = offset>sizeof(buf) ? sizeof(buf) : offset, or;
- or = readall(fd, buf, try);
- if (or < 0) perror_msg("lskip to %lld", (long long)offset);
- else offset -= try;
- if (or < try) break;
- }
+ or = readall(fd, buf, try);
+ if (or < 0) perror_msg("lskip to %lld", (long long)offset);
+ else offset -= try;
+ if (or < try) break;
+ }
- return offset;
- }
+ return offset;
+ }
}
char *xgetcwd(void)
{
- char *buf = getcwd(NULL, 0);
- if (!buf) perror_exit("xgetcwd");
+ char *buf = getcwd(NULL, 0);
+ if (!buf) perror_exit("xgetcwd");
- return buf;
+ return buf;
}
void xstat(char *path, struct stat *st)
{
- if(stat(path, st)) perror_exit("Can't stat %s", path);
+ if(stat(path, st)) perror_exit("Can't stat %s", path);
}
// Cannonicalizes path by removing ".", "..", and "//" elements. This is not
@@ -325,60 +324,60 @@ void xstat(char *path, struct stat *st)
// following symlinks.
char *xabspath(char *path)
{
- char *from, *to;
-
- // If this isn't an absolute path, make it one with cwd.
- if (path[0]!='/') {
- char *cwd=xgetcwd();
- path = xmsprintf("%s/%s", cwd, path);
- free(cwd);
- } else path = xstrdup(path);
-
- // Loop through path elements
- from = to = path;
- while (*from) {
-
- // Continue any current path component.
- if (*from!='/') {
- *(to++) = *(from++);
- continue;
- }
-
- // Skip duplicate slashes.
- while (*from=='/') from++;
-
- // Start of a new filename. Handle . and ..
- while (*from=='.') {
- // Skip .
- if (from[1]=='/') from += 2;
- else if (!from[1]) from++;
- // Back up for ..
- else if (from[1]=='.') {
- if (from[2]=='/') from +=3;
- else if(!from[2]) from+=2;
- else break;
- while (to>path && *(--to)!='/');
- } else break;
- }
- // Add directory separator slash.
- *(to++) = '/';
- }
- *to = 0;
-
- return path;
+ char *from, *to;
+
+ // If this isn't an absolute path, make it one with cwd.
+ if (path[0]!='/') {
+ char *cwd=xgetcwd();
+ path = xmsprintf("%s/%s", cwd, path);
+ free(cwd);
+ } else path = xstrdup(path);
+
+ // Loop through path elements
+ from = to = path;
+ while (*from) {
+
+ // Continue any current path component.
+ if (*from!='/') {
+ *(to++) = *(from++);
+ continue;
+ }
+
+ // Skip duplicate slashes.
+ while (*from=='/') from++;
+
+ // Start of a new filename. Handle . and ..
+ while (*from=='.') {
+ // Skip .
+ if (from[1]=='/') from += 2;
+ else if (!from[1]) from++;
+ // Back up for ..
+ else if (from[1]=='.') {
+ if (from[2]=='/') from +=3;
+ else if(!from[2]) from+=2;
+ else break;
+ while (to>path && *(--to)!='/');
+ } else break;
+ }
+ // Add directory separator slash.
+ *(to++) = '/';
+ }
+ *to = 0;
+
+ return path;
}
// Resolve all symlinks, returning malloc() memory.
char *xrealpath(char *path)
{
- char *new = realpath(path, NULL);
- if (!new) perror_exit("realpath '%s'", path);
- return new;
+ char *new = realpath(path, NULL);
+ if (!new) perror_exit("realpath '%s'", path);
+ return new;
}
void xchdir(char *path)
{
- if (chdir(path)) error_exit("chdir '%s'", path);
+ if (chdir(path)) error_exit("chdir '%s'", path);
}
// Ensure entire path exists.
@@ -386,27 +385,27 @@ void xchdir(char *path)
// Requires that path string be writable (for temporary null terminators).
void xmkpath(char *path, int mode)
{
- char *p, old;
- mode_t mask;
- int rc;
- struct stat st;
-
- for (p = path; ; p++) {
- if (!*p || *p == '/') {
- old = *p;
- *p = rc = 0;
- if (stat(path, &st) || !S_ISDIR(st.st_mode)) {
- if (mode != -1) {
- mask=umask(0);
- rc = mkdir(path, mode);
- umask(mask);
- } else rc = mkdir(path, 0777);
- }
- *p = old;
- if(rc) perror_exit("mkpath '%s'", path);
- }
- if (!*p) break;
- }
+ char *p, old;
+ mode_t mask;
+ int rc;
+ struct stat st;
+
+ for (p = path; ; p++) {
+ if (!*p || *p == '/') {
+ old = *p;
+ *p = rc = 0;
+ if (stat(path, &st) || !S_ISDIR(st.st_mode)) {
+ if (mode != -1) {
+ mask=umask(0);
+ rc = mkdir(path, mode);
+ umask(mask);
+ } else rc = mkdir(path, 0777);
+ }
+ *p = old;
+ if(rc) perror_exit("mkpath '%s'", path);
+ }
+ if (!*p) break;
+ }
}
// setuid() can fail (for example, too many processes belonging to that user),
@@ -414,7 +413,7 @@ void xmkpath(char *path, int mode)
void xsetuid(uid_t uid)
{
- if (setuid(uid)) perror_exit("xsetuid");
+ if (setuid(uid)) perror_exit("xsetuid");
}
@@ -424,40 +423,40 @@ void xsetuid(uid_t uid)
struct string_list *find_in_path(char *path, char *filename)
{
- struct string_list *rlist = NULL, **prlist=&rlist;
- char *cwd = xgetcwd();
-
- for (;;) {
- char *next = path ? strchr(path, ':') : NULL;
- int len = next ? next-path : strlen(path);
- struct string_list *rnext;
- struct stat st;
-
- rnext = xmalloc(sizeof(void *) + strlen(filename)
- + (len ? len : strlen(cwd)) + 2);
- if (!len) sprintf(rnext->str, "%s/%s", cwd, filename);
- else {
- char *res = rnext->str;
- strncpy(res, path, len);
- res += len;
- *(res++) = '/';
- strcpy(res, filename);
- }
-
- // Confirm it's not a directory.
- if (!stat(rnext->str, &st) && S_ISREG(st.st_mode)) {
- *prlist = rnext;
- rnext->next = NULL;
- prlist = &(rnext->next);
- } else free(rnext);
-
- if (!next) break;
- path += len;
- path++;
- }
- free(cwd);
-
- return rlist;
+ struct string_list *rlist = NULL, **prlist=&rlist;
+ char *cwd = xgetcwd();
+
+ for (;;) {
+ char *next = path ? strchr(path, ':') : NULL;
+ int len = next ? next-path : strlen(path);
+ struct string_list *rnext;
+ struct stat st;
+
+ rnext = xmalloc(sizeof(void *) + strlen(filename)
+ + (len ? len : strlen(cwd)) + 2);
+ if (!len) sprintf(rnext->str, "%s/%s", cwd, filename);
+ else {
+ char *res = rnext->str;
+ strncpy(res, path, len);
+ res += len;
+ *(res++) = '/';
+ strcpy(res, filename);
+ }
+
+ // Confirm it's not a directory.
+ if (!stat(rnext->str, &st) && S_ISREG(st.st_mode)) {
+ *prlist = rnext;
+ rnext->next = NULL;
+ prlist = &(rnext->next);
+ } else free(rnext);
+
+ if (!next) break;
+ path += len;
+ path++;
+ }
+ free(cwd);
+
+ return rlist;
}
// Convert unsigned int to ascii, writing into supplied buffer. A truncated
@@ -465,31 +464,31 @@ struct string_list *find_in_path(char *path, char *filename)
// always null terminated (unless buflen is 0).
void utoa_to_buf(unsigned n, char *buf, unsigned buflen)
{
- int i, out = 0;
+ int i, out = 0;
- if (buflen) {
- for (i=1000000000; i; i/=10) {
- int res = n/i;
+ if (buflen) {
+ for (i=1000000000; i; i/=10) {
+ int res = n/i;
- if ((res || out || i == 1) && --buflen>0) {
- out++;
- n -= res*i;
- *buf++ = '0' + res;
- }
- }
- *buf = 0;
- }
+ if ((res || out || i == 1) && --buflen>0) {
+ out++;
+ n -= res*i;
+ *buf++ = '0' + res;
+ }
+ }
+ *buf = 0;
+ }
}
// Convert signed integer to ascii, using utoa_to_buf()
void itoa_to_buf(int n, char *buf, unsigned buflen)
{
- if (buflen && n<0) {
- n = -n;
- *buf++ = '-';
- buflen--;
- }
- utoa_to_buf((unsigned)n, buf, buflen);
+ if (buflen && n<0) {
+ n = -n;
+ *buf++ = '-';
+ buflen--;
+ }
+ utoa_to_buf((unsigned)n, buf, buflen);
}
// This static buffer is used by both utoa() and itoa(), calling either one a
@@ -504,122 +503,122 @@ static char itoa_buf[12];
// Convert unsigned integer to ascii, returning a static buffer.
char *utoa(unsigned n)
{
- utoa_to_buf(n, itoa_buf, sizeof(itoa_buf));
+ utoa_to_buf(n, itoa_buf, sizeof(itoa_buf));
- return itoa_buf;
+ return itoa_buf;
}
char *itoa(int n)
{
- itoa_to_buf(n, itoa_buf, sizeof(itoa_buf));
+ itoa_to_buf(n, itoa_buf, sizeof(itoa_buf));
- return itoa_buf;
+ return itoa_buf;
}
// atol() with the kilo/mega/giga/tera/peta/exa extensions.
// (zetta and yotta don't fit in 64 bits.)
long atolx(char *numstr)
{
- char *c, *suffixes="bkmgtpe", *end;
- long val = strtol(numstr, &c, 0);
+ char *c, *suffixes="bkmgtpe", *end;
+ long val = strtol(numstr, &c, 0);
- if (*c) {
- if (c != numstr && (end = strchr(suffixes, tolower(*c)))) {
- int shift = end-suffixes;
- if (shift--) val *= 1024L<<(shift*10);
- } else {
- while (isspace(*c)) c++;
- if (*c) error_exit("not integer: %s", numstr);
- }
- }
+ if (*c) {
+ if (c != numstr && (end = strchr(suffixes, tolower(*c)))) {
+ int shift = end-suffixes;
+ if (shift--) val *= 1024L<<(shift*10);
+ } else {
+ while (isspace(*c)) c++;
+ if (*c) error_exit("not integer: %s", numstr);
+ }
+ }
- return val;
+ return val;
}
int numlen(long l)
{
- int len = 0;
- while (l) {
- l /= 10;
- len++;
- }
- return len;
+ int len = 0;
+ while (l) {
+ l /= 10;
+ len++;
+ }
+ return len;
}
int stridx(char *haystack, char needle)
{
- char *off;
+ char *off;
- if (!needle) return -1;
- off = strchr(haystack, needle);
- if (!off) return -1;
+ if (!needle) return -1;
+ off = strchr(haystack, needle);
+ if (!off) return -1;
- return off-haystack;
+ return off-haystack;
}
// Return how long the file at fd is, if there's any way to determine it.
off_t fdlength(int fd)
{
- off_t bottom = 0, top = 0, pos, old;
- int size;
+ off_t bottom = 0, top = 0, pos, old;
+ int size;
- // If the ioctl works for this, return it.
+ // If the ioctl works for this, return it.
- if (ioctl(fd, BLKGETSIZE, &size) >= 0) return size*512L;
+ if (ioctl(fd, BLKGETSIZE, &size) >= 0) return size*512L;
- // If not, do a binary search for the last location we can read. (Some
- // block devices don't do BLKGETSIZE right.) This should probably have
- // a CONFIG option...
+ // If not, do a binary search for the last location we can read. (Some
+ // block devices don't do BLKGETSIZE right.) This should probably have
+ // a CONFIG option...
- old = lseek(fd, 0, SEEK_CUR);
- do {
- char temp;
+ old = lseek(fd, 0, SEEK_CUR);
+ do {
+ char temp;
- pos = bottom + (top - bottom) / 2;
+ pos = bottom + (top - bottom) / 2;
- // If we can read from the current location, it's bigger.
+ // If we can read from the current location, it's bigger.
- if (lseek(fd, pos, 0)>=0 && read(fd, &temp, 1)==1) {
- if (bottom == top) bottom = top = (top+1) * 2;
- else bottom = pos;
+ if (lseek(fd, pos, 0)>=0 && read(fd, &temp, 1)==1) {
+ if (bottom == top) bottom = top = (top+1) * 2;
+ else bottom = pos;
- // If we can't, it's smaller.
+ // If we can't, it's smaller.
- } else {
- if (bottom == top) {
- if (!top) return 0;
- bottom = top/2;
- } else top = pos;
- }
- } while (bottom + 1 != top);
+ } else {
+ if (bottom == top) {
+ if (!top) return 0;
+ bottom = top/2;
+ } else top = pos;
+ }
+ } while (bottom + 1 != top);
- lseek(fd, old, SEEK_SET);
+ lseek(fd, old, SEEK_SET);
- return pos + 1;
+ return pos + 1;
}
// This can return null (meaning file not found). It just won't return null
// for memory allocation reasons.
char *xreadlink(char *name)
{
- int len, size = 0;
- char *buf = 0;
+ int len, size = 0;
+ char *buf = 0;
- // Grow by 64 byte chunks until it's big enough.
- for(;;) {
- size +=64;
- buf = xrealloc(buf, size);
- len = readlink(name, buf, size);
+ // Grow by 64 byte chunks until it's big enough.
+ for(;;) {
+ size +=64;
+ buf = xrealloc(buf, size);
+ len = readlink(name, buf, size);
- if (len<0) {
- free(buf);
- return 0;
- }
- if (len<size) {
- buf[len]=0;
- return buf;
- }
- }
+ if (len<0) {
+ free(buf);
+ return 0;
+ }
+ if (len<size) {
+ buf[len]=0;
+ return buf;
+ }
+ }
}
/*
@@ -628,24 +627,24 @@ char *xreadlink(char *name)
// Read contents of file as a single freshly allocated nul-terminated string.
char *readfile(char *name)
{
- off_t len;
- int fd;
- char *buf;
+ off_t len;
+ int fd;
+ char *buf;
- fd = open(name, O_RDONLY);
- if (fd == -1) return 0;
- len = fdlength(fd);
- buf = xmalloc(len+1);
- buf[readall(fd, buf, len)] = 0;
+ fd = open(name, O_RDONLY);
+ if (fd == -1) return 0;
+ len = fdlength(fd);
+ buf = xmalloc(len+1);
+ buf[readall(fd, buf, len)] = 0;
- return buf;
+ return buf;
}
char *xreadfile(char *name)
{
- char *buf = readfile(name);
- if (!buf) perror_exit("xreadfile %s", name);
- return buf;
+ char *buf = readfile(name);
+ if (!buf) perror_exit("xreadfile %s", name);
+ return buf;
}
*/
@@ -654,33 +653,33 @@ char *xreadfile(char *name)
// exists and is this executable.
void xpidfile(char *name)
{
- char pidfile[256], spid[32];
- int i, fd;
- pid_t pid;
-
- sprintf(pidfile, "/var/run/%s.pid", name);
- // Try three times to open the sucker.
- for (i=0; i<3; i++) {
- fd = open(pidfile, O_CREAT|O_EXCL, 0644);
- if (fd != -1) break;
+ char pidfile[256], spid[32];
+ int i, fd;
+ pid_t pid;
+
+ sprintf(pidfile, "/var/run/%s.pid", name);
+ // Try three times to open the sucker.
+ for (i=0; i<3; i++) {
+ fd = open(pidfile, O_CREAT|O_EXCL, 0644);
+ if (fd != -1) break;
- // If it already existed, read it. Loop for race condition.
- fd = open(pidfile, O_RDONLY);
- if (fd == -1) continue;
+ // If it already existed, read it. Loop for race condition.
+ fd = open(pidfile, O_RDONLY);
+ if (fd == -1) continue;
- // Is the old program still there?
- spid[xread(fd, spid, sizeof(spid)-1)] = 0;
- close(fd);
- pid = atoi(spid);
- if (pid < 1 || kill(pid, 0) == ESRCH) unlink(pidfile);
+ // Is the old program still there?
+ spid[xread(fd, spid, sizeof(spid)-1)] = 0;
+ close(fd);
+ pid = atoi(spid);
+ if (pid < 1 || kill(pid, 0) == ESRCH) unlink(pidfile);
- // An else with more sanity checking might be nice here.
- }
+ // An else with more sanity checking might be nice here.
+ }
- if (i == 3) error_exit("xpidfile %s", name);
+ if (i == 3) error_exit("xpidfile %s", name);
- xwrite(fd, spid, sprintf(spid, "%ld\n", (long)getpid()));
- close(fd);
+ xwrite(fd, spid, sprintf(spid, "%ld\n", (long)getpid()));
+ close(fd);
}
// Iterate through an array of files, opening each one and calling a function
@@ -691,157 +690,157 @@ void xpidfile(char *name)
// Note: read only filehandles are automatically closed when function()
// returns, but writeable filehandles must be close by function()
void loopfiles_rw(char **argv, int flags, int permissions, int failok,
- void (*function)(int fd, char *name))
+ void (*function)(int fd, char *name))
{
- int fd;
+ int fd;
- // If no arguments, read from stdin.
- if (!*argv) function(flags ? 1 : 0, "-");
- else do {
- // Filename "-" means read from stdin.
- // Inability to open a file prints a warning, but doesn't exit.
+ // If no arguments, read from stdin.
+ if (!*argv) function(flags ? 1 : 0, "-");
+ else do {
+ // Filename "-" means read from stdin.
+ // Inability to open a file prints a warning, but doesn't exit.
- if (!strcmp(*argv,"-")) fd=0;
- else if (0>(fd = open(*argv, flags, permissions)) && !failok) {
- perror_msg("%s", *argv);
- toys.exitval = 1;
- continue;
- }
- function(fd, *argv);
- if (flags == O_RDONLY) close(fd);
- } while (*++argv);
+ if (!strcmp(*argv,"-")) fd=0;
+ else if (0>(fd = open(*argv, flags, permissions)) && !failok) {
+ perror_msg("%s", *argv);
+ toys.exitval = 1;
+ continue;
+ }
+ function(fd, *argv);
+ if (flags == O_RDONLY) close(fd);
+ } while (*++argv);
}
// Call loopfiles_rw with O_RDONLY and !failok (common case).
void loopfiles(char **argv, void (*function)(int fd, char *name))
{
- loopfiles_rw(argv, O_RDONLY, 0, 0, function);
+ loopfiles_rw(argv, O_RDONLY, 0, 0, function);
}
// Slow, but small.
char *get_rawline(int fd, long *plen, char end)
{
- char c, *buf = NULL;
- long len = 0;
+ char c, *buf = NULL;
+ long len = 0;
- for (;;) {
- if (1>read(fd, &c, 1)) break;
- if (!(len & 63)) buf=xrealloc(buf, len+65);
- if ((buf[len++]=c) == end) break;
- }
- if (buf) buf[len]=0;
- if (plen) *plen = len;
+ for (;;) {
+ if (1>read(fd, &c, 1)) break;
+ if (!(len & 63)) buf=xrealloc(buf, len+65);
+ if ((buf[len++]=c) == end) break;
+ }
+ if (buf) buf[len]=0;
+ if (plen) *plen = len;
- return buf;
+ return buf;
}
char *get_line(int fd)
{
- long len;
- char *buf = get_rawline(fd, &len, '\n');
+ long len;
+ char *buf = get_rawline(fd, &len, '\n');
- if (buf && buf[--len]=='\n') buf[len]=0;
+ if (buf && buf[--len]=='\n') buf[len]=0;
- return buf;
+ return buf;
}
// Copy the rest of in to out and close both files.
void xsendfile(int in, int out)
{
- long len;
- char buf[4096];
+ long len;
+ char buf[4096];
- if (in<0) return;
- for (;;) {
- len = xread(in, buf, 4096);
- if (len<1) break;
- xwrite(out, buf, len);
- }
+ if (in<0) return;
+ for (;;) {
+ len = xread(in, buf, 4096);
+ if (len<1) break;
+ xwrite(out, buf, len);
+ }
}
int wfchmodat(int fd, char *name, mode_t mode)
{
- int rc = fchmodat(fd, name, mode, 0);
+ int rc = fchmodat(fd, name, mode, 0);
- if (rc) {
- perror_msg("chmod '%s' to %04o", name, mode);
- toys.exitval=1;
- }
- return rc;
+ if (rc) {
+ perror_msg("chmod '%s' to %04o", name, mode);
+ toys.exitval=1;
+ }
+ return rc;
}
static char *tempfile2zap;
static void tempfile_handler(int i)
{
- if (1 < (long)tempfile2zap) unlink(tempfile2zap);
- _exit(1);
+ if (1 < (long)tempfile2zap) unlink(tempfile2zap);
+ _exit(1);
}
// Open a temporary file to copy an existing file into.
int copy_tempfile(int fdin, char *name, char **tempname)
{
- struct stat statbuf;
- int fd;
+ struct stat statbuf;
+ int fd;
- *tempname = xstrndup(name, strlen(name)+6);
- strcat(*tempname,"XXXXXX");
- if(-1 == (fd = mkstemp(*tempname))) error_exit("no temp file");
- if (!tempfile2zap) sigatexit(tempfile_handler);
- tempfile2zap = *tempname;
+ *tempname = xstrndup(name, strlen(name)+6);
+ strcat(*tempname,"XXXXXX");
+ if(-1 == (fd = mkstemp(*tempname))) error_exit("no temp file");
+ if (!tempfile2zap) sigatexit(tempfile_handler);
+ tempfile2zap = *tempname;
- // Set permissions of output file
+ // Set permissions of output file
- fstat(fdin, &statbuf);
- fchmod(fd, statbuf.st_mode);
+ fstat(fdin, &statbuf);
+ fchmod(fd, statbuf.st_mode);
- return fd;
+ return fd;
}
// Abort the copy and delete the temporary file.
void delete_tempfile(int fdin, int fdout, char **tempname)
{
- close(fdin);
- close(fdout);
- unlink(*tempname);
- tempfile2zap = (char *)1;
- free(*tempname);
- *tempname = NULL;
+ close(fdin);
+ close(fdout);
+ unlink(*tempname);
+ tempfile2zap = (char *)1;
+ free(*tempname);
+ *tempname = NULL;
}
// Copy the rest of the data and replace the original with the copy.
void replace_tempfile(int fdin, int fdout, char **tempname)
{
- char *temp = xstrdup(*tempname);
+ char *temp = xstrdup(*tempname);
- temp[strlen(temp)-6]=0;
- if (fdin != -1) {
- xsendfile(fdin, fdout);
- xclose(fdin);
- }
- xclose(fdout);
- rename(*tempname, temp);
- tempfile2zap = (char *)1;
- free(*tempname);
- free(temp);
- *tempname = NULL;
+ temp[strlen(temp)-6]=0;
+ if (fdin != -1) {
+ xsendfile(fdin, fdout);
+ xclose(fdin);
+ }
+ xclose(fdout);
+ rename(*tempname, temp);
+ tempfile2zap = (char *)1;
+ free(*tempname);
+ free(temp);
+ *tempname = NULL;
}
// Create a 256 entry CRC32 lookup table.
void crc_init(unsigned int *crc_table, int little_endian)
{
- unsigned int i;
+ unsigned int i;
- // Init the CRC32 table (big endian)
- for (i=0; i<256; i++) {
- unsigned int j, c = little_endian ? i : i<<24;
- for (j=8; j; j--)
- if (little_endian) c = (c&1) ? (c>>1)^0xEDB88320 : c>>1;
- else c=c&0x80000000 ? (c<<1)^0x04c11db7 : (c<<1);
- crc_table[i] = c;
- }
+ // Init the CRC32 table (big endian)
+ for (i=0; i<256; i++) {
+ unsigned int j, c = little_endian ? i : i<<24;
+ for (j=8; j; j--)
+ if (little_endian) c = (c&1) ? (c>>1)^0xEDB88320 : c>>1;
+ else c=c&0x80000000 ? (c<<1)^0x04c11db7 : (c<<1);
+ crc_table[i] = c;
+ }
}
// Quick and dirty query size of terminal, doesn't do ANSI probe fallback.
@@ -850,86 +849,85 @@ void crc_init(unsigned int *crc_table, int little_endian)
void terminal_size(unsigned *x, unsigned *y)
{
- struct winsize ws;
- int i;
+ struct winsize ws;
+ int i;
- //memset(&ws, 0, sizeof(ws));
- for (i=0; i<3; i++) {
- if (ioctl(i, TIOCGWINSZ, &ws)) continue;
- if (x) *x = ws.ws_col;
- if (y) *y = ws.ws_row;
- }
- if (x) {
- char *s = getenv("COLUMNS");
+ //memset(&ws, 0, sizeof(ws));
+ for (i=0; i<3; i++) {
+ if (ioctl(i, TIOCGWINSZ, &ws)) continue;
+ if (x) *x = ws.ws_col;
+ if (y) *y = ws.ws_row;
+ }
+ if (x) {
+ char *s = getenv("COLUMNS");
- i = s ? atoi(s) : 0;
- if (i>0) *x = i;
- }
- if (y) {
- char *s = getenv("ROWS");
+ i = s ? atoi(s) : 0;
+ if (i>0) *x = i;
+ }
+ if (y) {
+ char *s = getenv("ROWS");
- i = s ? atoi(s) : 0;
- if (i>0) *y = i;
- }
+ i = s ? atoi(s) : 0;
+ if (i>0) *y = i;
+ }
}
// This should use a raw tty, fixit later.
int yesno(char *prompt, int def)
{
- FILE *fps[] = {stdin, stdout, stderr};
- int i;
- char buf;
+ FILE *fps[] = {stdin, stdout, stderr};
+ int i;
+ char buf;
- for (i=0; i<3; i++) if (isatty(i)) break;
- if (i == 3) return 1;
+ for (i=0; i<3; i++) if (isatty(i)) break;
+ if (i == 3) return 1;
- fprintf(fps[i], "%s (%c/%c):", prompt, def ? 'Y' : 'y', def ? 'n' : 'N');
- fflush(fps[i]);
- while (fread(&buf, 1, 1, fps[i])) {
- if (tolower(buf) == 'y') def = 1;
- if (tolower(buf) == 'n') def = 0;
- else if (!isspace(buf)) continue;
+ fprintf(fps[i], "%s (%c/%c):", prompt, def ? 'Y' : 'y', def ? 'n' : 'N');
+ fflush(fps[i]);
+ while (fread(&buf, 1, 1, fps[i])) {
+ if (tolower(buf) == 'y') def = 1;
+ if (tolower(buf) == 'n') def = 0;
+ else if (!isspace(buf)) continue;
- break;
- }
+ break;
+ }
- return def;
+ return def;
}
// Execute a callback for each PID that matches a process name from a list.
void for_each_pid_with_name_in(char **names, void (*callback)(pid_t pid))
{
- DIR *dp;
- struct dirent *entry;
- char cmd[sizeof(toybuf)], path[64];
- char **curname;
+ DIR *dp;
+ struct dirent *entry;
+ char cmd[sizeof(toybuf)], path[64];
+ char **curname;
- if (!(dp = opendir("/proc"))) perror_exit("opendir");
+ if (!(dp = opendir("/proc"))) perror_exit("opendir");
- while ((entry = readdir(dp))) {
- int fd, n;
+ while ((entry = readdir(dp))) {
+ int fd, n;
- if (!isdigit(*entry->d_name)) continue;
+ if (!isdigit(*entry->d_name)) continue;
- if (sizeof(path) <= snprintf(path, sizeof(path), "/proc/%s/cmdline",
- entry->d_name)) continue;
+ if (sizeof(path) <= snprintf(path, sizeof(path), "/proc/%s/cmdline",
+ entry->d_name)) continue;
- if (-1 == (fd=open(path, O_RDONLY))) continue;
- n = read(fd, cmd, sizeof(cmd));
- close(fd);
- if (n<1) continue;
+ if (-1 == (fd=open(path, O_RDONLY))) continue;
+ n = read(fd, cmd, sizeof(cmd));
+ close(fd);
+ if (n<1) continue;
- for (curname = names; *curname; curname++)
- if (!strcmp(basename(cmd), *curname))
- callback(atol(entry->d_name));
- }
+ for (curname = names; *curname; curname++)
+ if (!strcmp(basename(cmd), *curname)) callback(atol(entry->d_name));
+ }
- closedir(dp);
+ closedir(dp);
}
struct signame {
- int num;
- char *name;
+ int num;
+ char *name;
};
// Signals required by POSIX 2008:
@@ -938,16 +936,16 @@ struct signame {
#define SIGNIFY(x) {SIG##x, #x}
static struct signame signames[] = {
- SIGNIFY(ABRT), SIGNIFY(ALRM), SIGNIFY(BUS),
- SIGNIFY(FPE), SIGNIFY(HUP), SIGNIFY(ILL), SIGNIFY(INT), SIGNIFY(KILL),
- SIGNIFY(PIPE), SIGNIFY(QUIT), SIGNIFY(SEGV), SIGNIFY(TERM),
- SIGNIFY(USR1), SIGNIFY(USR2), SIGNIFY(SYS), SIGNIFY(TRAP),
- SIGNIFY(VTALRM), SIGNIFY(XCPU), SIGNIFY(XFSZ),
+ SIGNIFY(ABRT), SIGNIFY(ALRM), SIGNIFY(BUS),
+ SIGNIFY(FPE), SIGNIFY(HUP), SIGNIFY(ILL), SIGNIFY(INT), SIGNIFY(KILL),
+ SIGNIFY(PIPE), SIGNIFY(QUIT), SIGNIFY(SEGV), SIGNIFY(TERM),
+ SIGNIFY(USR1), SIGNIFY(USR2), SIGNIFY(SYS), SIGNIFY(TRAP),
+ SIGNIFY(VTALRM), SIGNIFY(XCPU), SIGNIFY(XFSZ),
- // Start of non-terminal signals
+ // Start of non-terminal signals
- SIGNIFY(CHLD), SIGNIFY(CONT), SIGNIFY(STOP), SIGNIFY(TSTP),
- SIGNIFY(TTIN), SIGNIFY(TTOU), SIGNIFY(URG)
+ SIGNIFY(CHLD), SIGNIFY(CONT), SIGNIFY(STOP), SIGNIFY(TSTP),
+ SIGNIFY(TTIN), SIGNIFY(TTOU), SIGNIFY(URG)
};
// not in posix: SIGNIFY(STKFLT), SIGNIFY(WINCH), SIGNIFY(IO), SIGNIFY(PWR)
@@ -956,147 +954,144 @@ static struct signame signames[] = {
// Install the same handler on every signal that defaults to killing the process
void sigatexit(void *handler)
{
- int i;
- for (i=0; signames[i].num != SIGCHLD; i++)
- signal(signames[i].num, handler);
+ int i;
+ for (i=0; signames[i].num != SIGCHLD; i++) signal(signames[i].num, handler);
}
// Convert name to signal number. If name == NULL print names.
int sig_to_num(char *pidstr)
{
- int i;
+ int i;
- if (pidstr) {
- char *s;
- i = strtol(pidstr, &s, 10);
- if (!*s) return i;
+ if (pidstr) {
+ char *s;
+ i = strtol(pidstr, &s, 10);
+ if (!*s) return i;
- if (!strncasecmp(pidstr, "sig", 3)) pidstr+=3;
- }
- for (i = 0; i < sizeof(signames)/sizeof(struct signame); i++)
- if (!pidstr) xputs(signames[i].name);
- else if (!strcasecmp(pidstr, signames[i].name))
- return signames[i].num;
+ if (!strncasecmp(pidstr, "sig", 3)) pidstr+=3;
+ }
+ for (i = 0; i < sizeof(signames)/sizeof(struct signame); i++)
+ if (!pidstr) xputs(signames[i].name);
+ else if (!strcasecmp(pidstr, signames[i].name)) return signames[i].num;
- return -1;
+ return -1;
}
char *num_to_sig(int sig)
{
- int i;
+ int i;
- for (i=0; i<sizeof(signames)/sizeof(struct signame); i++)
- if (signames[i].num == sig) return signames[i].name;
- return NULL;
+ for (i=0; i<sizeof(signames)/sizeof(struct signame); i++)
+ if (signames[i].num == sig) return signames[i].name;
+ return NULL;
}
// premute mode bits based on posix mode strings.
mode_t string_to_mode(char *modestr, mode_t mode)
{
- char *whos = "ogua", *hows = "=+-", *whats = "xwrstX", *whys = "ogu";
- char *s, *str = modestr;
-
- // Handle octal mode
- if (isdigit(*str)) {
- mode = strtol(str, &s, 8);
- if (*s || (mode & ~(07777))) goto barf;
-
- return mode;
- }
-
- // Gaze into the bin of permission...
- for (;;) {
- int i, j, dowho, dohow, dowhat, amask;
-
- dowho = dohow = dowhat = amask = 0;
-
- // Find the who, how, and what stanzas, in that order
- while (*str && (s = strchr(whos, *str))) {
- dowho |= 1<<(s-whos);
- str++;
- }
- // If who isn't specified, like "a" but honoring umask.
- if (!dowho) {
- dowho = 8;
- umask(amask=umask(0));
- }
- if (!*str || !(s = strchr(hows, *str))) goto barf;
- dohow = *(str++);
-
- if (!dohow) goto barf;
- while (*str && (s = strchr(whats, *str))) {
- dowhat |= 1<<(s-whats);
- str++;
- }
-
- // Convert X to x for directory or if already executable somewhere
- if ((dowhat&32) && (S_ISDIR(mode) || (mode&0111))) dowhat |= 1;
-
- // Copy mode from another category?
- if (!dowhat && *str && (s = strchr(whys, *str))) {
- dowhat = (mode>>(3*(s-whys)))&7;
- str++;
- }
-
- // Are we ready to do a thing yet?
- if (*str && *(str++) != ',') goto barf;
-
- // Ok, apply the bits to the mode.
- for (i=0; i<4; i++) {
- for (j=0; j<3; j++) {
- mode_t bit = 0;
- int where = 1<<((3*i)+j);
-
- if (amask & where) continue;
-
- // Figure out new value at this location
- if (i == 3) {
- // suid/sticky bit.
- if (j) {
- if ((dowhat & 8) && (dowho&(8|(1<<i)))) bit++;
- } else if (dowhat & 16) bit++;
- } else {
- if (!(dowho&(8|(1<<i)))) continue;
- if (dowhat&(1<<j)) bit++;
- }
-
- // When selection active, modify bit
-
- if (dohow == '=' || (bit && dohow == '-'))
- mode &= ~where;
- if (bit && dohow != '-') mode |= where;
- }
- }
-
- if (!*str) break;
- }
- return mode;
+ char *whos = "ogua", *hows = "=+-", *whats = "xwrstX", *whys = "ogu";
+ char *s, *str = modestr;
+
+ // Handle octal mode
+ if (isdigit(*str)) {
+ mode = strtol(str, &s, 8);
+ if (*s || (mode & ~(07777))) goto barf;
+
+ return mode;
+ }
+
+ // Gaze into the bin of permission...
+ for (;;) {
+ int i, j, dowho, dohow, dowhat, amask;
+
+ dowho = dohow = dowhat = amask = 0;
+
+ // Find the who, how, and what stanzas, in that order
+ while (*str && (s = strchr(whos, *str))) {
+ dowho |= 1<<(s-whos);
+ str++;
+ }
+ // If who isn't specified, like "a" but honoring umask.
+ if (!dowho) {
+ dowho = 8;
+ umask(amask=umask(0));
+ }
+ if (!*str || !(s = strchr(hows, *str))) goto barf;
+ dohow = *(str++);
+
+ if (!dohow) goto barf;
+ while (*str && (s = strchr(whats, *str))) {
+ dowhat |= 1<<(s-whats);
+ str++;
+ }
+
+ // Convert X to x for directory or if already executable somewhere
+ if ((dowhat&32) && (S_ISDIR(mode) || (mode&0111))) dowhat |= 1;
+
+ // Copy mode from another category?
+ if (!dowhat && *str && (s = strchr(whys, *str))) {
+ dowhat = (mode>>(3*(s-whys)))&7;
+ str++;
+ }
+
+ // Are we ready to do a thing yet?
+ if (*str && *(str++) != ',') goto barf;
+
+ // Ok, apply the bits to the mode.
+ for (i=0; i<4; i++) {
+ for (j=0; j<3; j++) {
+ mode_t bit = 0;
+ int where = 1<<((3*i)+j);
+
+ if (amask & where) continue;
+
+ // Figure out new value at this location
+ if (i == 3) {
+ // suid/sticky bit.
+ if (j) {
+ if ((dowhat & 8) && (dowho&(8|(1<<i)))) bit++;
+ } else if (dowhat & 16) bit++;
+ } else {
+ if (!(dowho&(8|(1<<i)))) continue;
+ if (dowhat&(1<<j)) bit++;
+ }
+
+ // When selection active, modify bit
+
+ if (dohow == '=' || (bit && dohow == '-')) mode &= ~where;
+ if (bit && dohow != '-') mode |= where;
+ }
+ }
+
+ if (!*str) break;
+ }
+ return mode;
barf:
- error_exit("bad mode '%s'", modestr);
+ error_exit("bad mode '%s'", modestr);
}
char* make_human_readable(unsigned long long size, unsigned long unit)
{
- unsigned int frac = 0;
- if(unit) {
- size = (size/(unit)) + (size%(unit)?1:0);
- return xmsprintf("%llu", size);
+ unsigned int frac = 0;
+ if(unit) {
+ size = (size/(unit)) + (size%(unit)?1:0);
+ return xmsprintf("%llu", size);
+ }
+ else {
+ static char units[] = {'\0', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'};
+ int index = 0;
+ while(size >= 1024) {
+ frac = size%1024;
+ size /= 1024;
+ index++;
}
- else {
- static char units[] = {'\0', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'};
- int index = 0;
- while(size >= 1024) {
- frac = size%1024;
- size /= 1024;
- index++;
- }
- frac = (frac/102) + ((frac%102)?1:0);
- if(frac >= 10) {
- size += 1;
- frac = 0;
- }
- if(frac) return xmsprintf("%llu.%u%c", size, frac, units[index]);
- else return xmsprintf("%llu%c", size, units[index]);
+ frac = (frac/102) + ((frac%102)?1:0);
+ if(frac >= 10) {
+ size += 1;
+ frac = 0;
}
- return NULL; //not reached
+ if(frac) return xmsprintf("%llu.%u%c", size, frac, units[index]);
+ else return xmsprintf("%llu%c", size, units[index]);
+ }
+ return NULL; //not reached
}