OSDN Git Service

Merge remote-tracking branch 'toybox/master' into HEAD
[android-x86/external-toybox.git] / lib / lib.c
index b0ac471..6f7ed30 100644 (file)
--- a/lib/lib.c
+++ b/lib/lib.c
@@ -273,16 +273,16 @@ struct string_list *find_in_path(char *path, char *filename)
   return rlist;
 }
 
-long estrtol(char *str, char **end, int base)
+long long estrtol(char *str, char **end, int base)
 {
   errno = 0;
 
-  return strtol(str, end, base);
+  return strtoll(str, end, base);
 }
 
-long xstrtol(char *str, char **end, int base)
+long long xstrtol(char *str, char **end, int base)
 {
-  long l = estrtol(str, end, base);
+  long long l = estrtol(str, end, base);
 
   if (errno) perror_exit_raw(str);
 
@@ -291,31 +291,32 @@ long xstrtol(char *str, char **end, int base)
 
 // atol() with the kilo/mega/giga/tera/peta/exa extensions.
 // (zetta and yotta don't fit in 64 bits.)
-long atolx(char *numstr)
+long long atolx(char *numstr)
 {
-  char *c, *suffixes="cbkmgtpe", *end;
-  long val;
+  char *c = numstr, *suffixes="cbkmgtpe", *end;
+  long long val;
 
   val = xstrtol(numstr, &c, 0);
-  if (*c) {
-    if (c != numstr && (end = strchr(suffixes, tolower(*c)))) {
-      int shift = end-suffixes-2;
-      if (shift >= 0) val *= 1024L<<(shift*10);
-    } else {
-      while (isspace(*c)) c++;
-      if (*c) error_exit("not integer: %s", numstr);
+  if (c != numstr && *c && (end = strchr(suffixes, tolower(*c)))) {
+    int shift = end-suffixes-2;
+
+    if (shift >= 0) {
+      if (toupper(*++c)=='d') do val *= 1000; while (shift--);
+      else val *= 1024LL<<(shift*10);
     }
   }
+  while (isspace(*c)) c++;
+  if (c==numstr || *c) error_exit("not integer: %s", numstr);
 
   return val;
 }
 
-long atolx_range(char *numstr, long low, long high)
+long long atolx_range(char *numstr, long long low, long long high)
 {
-  long val = atolx(numstr);
+  long long val = atolx(numstr);
 
-  if (val < low) error_exit("%ld < %ld", val, low);
-  if (val > high) error_exit("%ld > %ld", val, high);
+  if (val < low) error_exit("%lld < %lld", val, low);
+  if (val > high) error_exit("%lld > %lld", val, high);
 
   return val;
 }
@@ -444,7 +445,8 @@ off_t fdlength(int fd)
 
 // Read contents of file as a single nul-terminated string.
 // measure file size if !len, allocate buffer if !buf
-// note: for existing buffers use len = size-1, will set buf[len] = 0
+// Existing buffers need len in *plen
+// Returns amount of data read in *plen
 char *readfileat(int dirfd, char *name, char *ibuf, off_t *plen)
 {
   off_t len, rlen;
@@ -475,12 +477,12 @@ char *readfileat(int dirfd, char *name, char *ibuf, off_t *plen)
     rbuf = buf+rlen;
     len -= rlen;
   }
-  *plen = len = rlen+(buf-ibuf);
+  *plen = len = rlen+(rbuf-buf);
   close(fd);
 
   if (rlen<0) {
     if (ibuf != buf) free(buf);
-    buf =  0;
+    buf = 0;
   } else buf[len] = 0;
 
   return buf;
@@ -508,8 +510,7 @@ int64_t peek_le(void *ptr, unsigned size)
   char *c = ptr;
   int i;
 
-  for (i=0; i<size; i++) ret |= ((int64_t)c[i])<<i;
-
+  for (i=0; i<size; i++) ret |= ((int64_t)c[i])<<(i*8);
   return ret;
 }
 
@@ -517,9 +518,9 @@ int64_t peek_be(void *ptr, unsigned size)
 {
   int64_t ret = 0;
   char *c = ptr;
+  int i;
 
-  while (size--) ret = (ret<<8)|c[size];
-
+  for (i=0; i<size; i++) ret = (ret<<8)|(c[i]&0xff);
   return ret;
 }
 
@@ -546,16 +547,20 @@ void poke(void *ptr, uint64_t val, int size)
 }
 
 // Iterate through an array of files, opening each one and calling a function
-// on that filehandle and name.  The special filename "-" means stdin if
-// flags is O_RDONLY, stdout otherwise.  An empty argument list calls
+// on that filehandle and name. The special filename "-" means stdin if
+// flags is O_RDONLY, stdout otherwise. An empty argument list calls
 // function() on just stdin/stdout.
 //
 // Note: pass O_CLOEXEC to automatically close filehandles when function()
-// returns, otherwise filehandles must be closed by function()
-void loopfiles_rw(char **argv, int flags, int permissions, int failok,
+// returns, otherwise filehandles must be closed by function().
+// pass WARN_ONLY to produce warning messages about files it couldn't
+// open/create, and skip them. Otherwise function is called with fd -1.
+void loopfiles_rw(char **argv, int flags, int permissions,
   void (*function)(int fd, char *name))
 {
-  int fd;
+  int fd, failok = !(flags&WARN_ONLY);
+
+  flags &= ~WARN_ONLY;
 
   // If no arguments, read from stdin.
   if (!*argv) function((flags & O_ACCMODE) != O_RDONLY ? 1 : 0, "-");
@@ -563,20 +568,20 @@ void loopfiles_rw(char **argv, int flags, int permissions, int failok,
     // 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) {
+    if (!strcmp(*argv, "-")) fd = 0;
+    else if (0>(fd = notstdio(open(*argv, flags, permissions))) && !failok) {
       perror_msg_raw(*argv);
       continue;
     }
     function(fd, *argv);
-    if (flags & O_CLOEXEC) close(fd);
+    if ((flags & O_CLOEXEC) && fd) close(fd);
   } while (*++argv);
 }
 
-// Call loopfiles_rw with O_RDONLY|O_CLOEXEC and !failok (common case).
+// Call loopfiles_rw with O_RDONLY|O_CLOEXEC|WARN_ONLY (common case)
 void loopfiles(char **argv, void (*function)(int fd, char *name))
 {
-  loopfiles_rw(argv, O_RDONLY|O_CLOEXEC, 0, 0, function);
+  loopfiles_rw(argv, O_RDONLY|O_CLOEXEC|WARN_ONLY, 0, function);
 }
 
 // Slow, but small.
@@ -619,10 +624,9 @@ int wfchmodat(int fd, char *name, mode_t mode)
 }
 
 static char *tempfile2zap;
-static void tempfile_handler(int i)
+static void tempfile_handler(void)
 {
   if (1 < (long)tempfile2zap) unlink(tempfile2zap);
-  _exit(1);
 }
 
 // Open a temporary file to copy an existing file into.
@@ -636,11 +640,18 @@ int copy_tempfile(int fdin, char *name, char **tempname)
   if (!tempfile2zap) sigatexit(tempfile_handler);
   tempfile2zap = *tempname;
 
-  // Set permissions of output file
+  // Set permissions of output file (ignoring errors, usually due to nonroot)
 
   fstat(fdin, &statbuf);
   fchmod(fd, statbuf.st_mode);
 
+  // We chmod before chown, which strips the suid bit. Caller has to explicitly
+  // switch it back on if they want to keep suid.
+
+  // I said IGNORING ERRORS. Both gcc and clang clutch their pearls about this
+  // but it's _supposed_ to fail when we're not root.
+  if (fchown(fd, statbuf.st_uid, statbuf.st_gid)) fd = fd;
+
   return fd;
 }
 
@@ -758,11 +769,25 @@ void generic_signal(int sig)
   toys.signal = sig;
 }
 
-// Install the same handler on every signal that defaults to killing the process
+void exit_signal(int sig)
+{
+  if (sig) toys.exitval = sig|128;
+  xexit();
+}
+
+// Install the same handler on every signal that defaults to killing the
+// process, calling the handler on the way out. Calling multiple times
+// adds the handlers to a list, to be called in order.
 void sigatexit(void *handler)
 {
+  struct arg_list *al = xmalloc(sizeof(struct arg_list));
   int i;
-  for (i=0; signames[i].num != SIGCHLD; i++) signal(signames[i].num, handler);
+
+  for (i=0; signames[i].num != SIGCHLD; i++)
+    signal(signames[i].num, exit_signal);
+  al->next = toys.xexit;
+  al->arg = handler;
+  toys.xexit = al;
 }
 
 // Convert name to signal number.  If name == NULL print names.
@@ -907,11 +932,14 @@ void mode_to_string(mode_t mode, char *buf)
   *buf = c;
 }
 
-char *basename_r(char *name)
+// basename() can modify its argument or return a pointer to a constant string
+// This just gives after the last '/' or the whole stirng if no /
+char *getbasename(char *name)
 {
   char *s = strrchr(name, '/');
 
   if (s) return s+1;
+
   return name;
 }
 
@@ -933,7 +961,7 @@ void names_to_pid(char **names, int (*callback)(pid_t pid, char *name))
 
     for (curname = names; *curname; curname++)
       if (**curname == '/' ? !strcmp(cmd, *curname)
-          : !strcmp(basename_r(cmd), basename_r(*curname)))
+          : !strcmp(getbasename(cmd), getbasename(*curname)))
         if (callback(u, *curname)) break;
     if (*curname) break;
   }
@@ -991,7 +1019,7 @@ int qstrcmp(const void *a, const void *b)
 void create_uuid(char *uuid)
 {
   // Read 128 random bits
-  int fd = xopen("/dev/urandom", O_RDONLY);
+  int fd = xopenro("/dev/urandom");
   xreadall(fd, uuid, 16);
   close(fd);
 
@@ -1015,3 +1043,199 @@ char *show_uuid(char *uuid)
 
   return libbuf;
 }
+
+// Returns pointer to letter at end, 0 if none. *start = initial %
+char *next_printf(char *s, char **start)
+{
+  for (; *s; s++) {
+    if (*s != '%') continue;
+    if (*++s == '%') continue;
+    if (start) *start = s-1;
+    while (0 <= stridx("0'#-+ ", *s)) s++;
+    while (isdigit(*s)) s++;
+    if (*s == '.') s++;
+    while (isdigit(*s)) s++;
+
+    return s;
+  }
+
+  return 0;
+}
+
+// Posix inexplicably hasn't got this, so find str in line.
+char *strnstr(char *line, char *str)
+{
+  long len = strlen(str);
+  char *s;
+
+  for (s = line; *s; s++) if (!strncasecmp(s, str, len)) break;
+
+  return *s ? s : 0;
+}
+
+int dev_minor(int dev)
+{
+  return ((dev&0xfff00000)>>12)|(dev&0xff);
+}
+
+int dev_major(int dev)
+{
+  return (dev&0xfff00)>>8;
+}
+
+int dev_makedev(int major, int minor)
+{
+  return (minor&0xff)|((major&0xfff)<<8)|((minor&0xfff00)<<12);
+}
+
+// Return cached passwd entries.
+struct passwd *bufgetpwuid(uid_t uid)
+{
+  struct pwuidbuf_list {
+    struct pwuidbuf_list *next;
+    struct passwd pw;
+  } *list;
+  struct passwd *temp;
+  static struct pwuidbuf_list *pwuidbuf;
+
+  for (list = pwuidbuf; list; list = list->next)
+    if (list->pw.pw_uid == uid) return &(list->pw);
+
+  list = xmalloc(512);
+  list->next = pwuidbuf;
+
+  errno = getpwuid_r(uid, &list->pw, sizeof(*list)+(char *)list,
+    512-sizeof(*list), &temp);
+  if (!temp) {
+    free(list);
+
+    return 0;
+  }
+  pwuidbuf = list;
+
+  return &list->pw;
+}
+
+// Return cached passwd entries.
+struct group *bufgetgrgid(gid_t gid)
+{
+  struct grgidbuf_list {
+    struct grgidbuf_list *next;
+    struct group gr;
+  } *list;
+  struct group *temp;
+  static struct grgidbuf_list *grgidbuf;
+
+  for (list = grgidbuf; list; list = list->next)
+    if (list->gr.gr_gid == gid) return &(list->gr);
+
+  list = xmalloc(512);
+  list->next = grgidbuf;
+
+  errno = getgrgid_r(gid, &list->gr, sizeof(*list)+(char *)list,
+    512-sizeof(*list), &temp);
+  if (!temp) {
+    free(list);
+
+    return 0;
+  }
+  grgidbuf = list;
+
+  return &list->gr;
+}
+
+// Always null terminates, returns 0 for failure, len for success
+int readlinkat0(int dirfd, char *path, char *buf, int len)
+{
+  if (!len) return 0;
+
+  len = readlinkat(dirfd, path, buf, len-1);
+  if (len<1) return 0;
+  buf[len] = 0;
+
+  return len;
+}
+
+int readlink0(char *path, char *buf, int len)
+{
+  return readlinkat0(AT_FDCWD, path, buf, len);
+}
+
+// Do regex matching handling embedded NUL bytes in string (hence extra len
+// argument). Note that neither the pattern nor the match can currently include
+// NUL bytes (even with wildcards) and string must be null terminated at
+// string[len]. But this can find a match after the first NUL.
+int regexec0(regex_t *preg, char *string, long len, int nmatch,
+  regmatch_t pmatch[], int eflags)
+{
+  char *s = string;
+
+  for (;;) {
+    long ll = 0;
+    int rc;
+
+    while (len && !*s) {
+      s++;
+      len--;
+    }
+    while (s[ll] && ll<len) ll++;
+
+    rc = regexec(preg, s, nmatch, pmatch, eflags);
+    if (!rc) {
+      for (rc = 0; rc<nmatch && pmatch[rc].rm_so!=-1; rc++) {
+        pmatch[rc].rm_so += s-string;
+        pmatch[rc].rm_eo += s-string;
+      }
+
+      return 0;
+    }
+    if (ll==len) return rc;
+
+    s += ll;
+    len -= ll;
+  }
+}
+
+// Return user name or string representation of number, returned buffer
+// lasts until next call.
+char *getusername(uid_t uid)
+{
+  struct passwd *pw = bufgetpwuid(uid);
+  static char unum[12];
+
+  sprintf(unum, "%u", (unsigned)uid);
+  return pw ? pw->pw_name : unum;
+}
+
+// Return group name or string representation of number, returned buffer
+// lasts until next call.
+char *getgroupname(gid_t gid)
+{
+  struct group *gr = bufgetgrgid(gid);
+  static char gnum[12];
+
+  sprintf(gnum, "%u", (unsigned)gid);
+  return gr ? gr->gr_name : gnum;
+}
+
+// Iterate over lines in file, calling function. Function can write 0 to
+// the line pointer if they want to keep it, or 1 to terminate processing,
+// otherwise line is freed. Passed file descriptor is closed at the end.
+void do_lines(int fd, void (*call)(char **pline, long len))
+{
+  FILE *fp = fd ? xfdopen(fd, "r") : stdin;
+
+  for (;;) {
+    char *line = 0;
+    ssize_t len;
+
+    len = getline(&line, (void *)&len, fp);
+    if (len > 0) {
+      call(&line, len);
+      if (line == (void *)1) break;
+      free(line);
+    } else break;
+  }
+
+  if (fd) fclose(fp);
+}