about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/complex/cacosh.c2
-rw-r--r--src/locale/iconv.c1
-rw-r--r--src/math/fma.c2
-rw-r--r--src/misc/initgroups.c26
-rw-r--r--src/stdio/vfprintf.c14
-rw-r--r--src/time/strptime.c66
-rw-r--r--src/unistd/pwrite.c11
-rw-r--r--src/unistd/pwritev.c10
8 files changed, 114 insertions, 18 deletions
diff --git a/src/complex/cacosh.c b/src/complex/cacosh.c
index 76127f75..55b857ce 100644
--- a/src/complex/cacosh.c
+++ b/src/complex/cacosh.c
@@ -1,6 +1,6 @@
 #include "complex_impl.h"
 
-/* acosh(z) = i acos(z) */
+/* acosh(z) = ±i acos(z) */
 
 double complex cacosh(double complex z)
 {
diff --git a/src/locale/iconv.c b/src/locale/iconv.c
index 4b7967a7..7fb2e1ef 100644
--- a/src/locale/iconv.c
+++ b/src/locale/iconv.c
@@ -340,6 +340,7 @@ size_t iconv(iconv_t cd, char **restrict in, size_t *restrict inb, char **restri
 				c++;
 				d -= 159;
 			}
+			if (c>=84) goto ilseq;
 			c = jis0208[c][d];
 			if (!c) goto ilseq;
 			break;
diff --git a/src/math/fma.c b/src/math/fma.c
index 0c6f90c9..adfadca8 100644
--- a/src/math/fma.c
+++ b/src/math/fma.c
@@ -53,7 +53,7 @@ double fma(double x, double y, double z)
 		return x*y + z;
 	if (nz.e >= ZEROINFNAN) {
 		if (nz.e > ZEROINFNAN) /* z==0 */
-			return x*y + z;
+			return x*y;
 		return z;
 	}
 
diff --git a/src/misc/initgroups.c b/src/misc/initgroups.c
index 922a9581..101f5c7b 100644
--- a/src/misc/initgroups.c
+++ b/src/misc/initgroups.c
@@ -1,11 +1,29 @@
 #define _GNU_SOURCE
 #include <grp.h>
 #include <limits.h>
+#include <stdlib.h>
 
 int initgroups(const char *user, gid_t gid)
 {
-	gid_t groups[NGROUPS_MAX];
-	int count = NGROUPS_MAX;
-	if (getgrouplist(user, gid, groups, &count) < 0) return -1;
-	return setgroups(count, groups);
+	gid_t buf[32], *groups = buf;
+	int count = sizeof buf / sizeof *buf, prev_count = count;
+	while (getgrouplist(user, gid, groups, &count) < 0) {
+		if (groups != buf) free(groups);
+
+		/* Return if failure isn't buffer size */
+		if (count <= prev_count)
+			return -1;
+
+		/* Always increase by at least 50% to limit to
+		 * logarithmically many retries on TOCTOU races. */
+		if (count < prev_count + (prev_count>>1))
+			count = prev_count + (prev_count>>1);
+
+		groups = calloc(count, sizeof *groups);
+		if (!groups) return -1;
+		prev_count = count;
+	}
+	int ret = setgroups(count, groups);
+	if (groups != buf) free(groups);
+	return ret;
 }
diff --git a/src/stdio/vfprintf.c b/src/stdio/vfprintf.c
index 497c5e19..360d723a 100644
--- a/src/stdio/vfprintf.c
+++ b/src/stdio/vfprintf.c
@@ -166,7 +166,8 @@ static char *fmt_u(uintmax_t x, char *s)
 {
 	unsigned long y;
 	for (   ; x>ULONG_MAX; x/=10) *--s = '0' + x%10;
-	for (y=x;           y; y/=10) *--s = '0' + y%10;
+	for (y=x;       y>=10; y/=10) *--s = '0' + y%10;
+	if (y) *--s = '0' + y;
 	return s;
 }
 
@@ -211,18 +212,11 @@ static int fmt_fp(FILE *f, long double y, int w, int p, int fl, int t)
 	if (y) e2--;
 
 	if ((t|32)=='a') {
-		long double round = 8.0;
-		int re;
-
 		if (t&32) prefix += 9;
 		pl += 2;
 
-		if (p<0 || p>=LDBL_MANT_DIG/4-1) re=0;
-		else re=LDBL_MANT_DIG/4-1-p;
-
-		if (re) {
-			round *= 1<<(LDBL_MANT_DIG%4);
-			while (re--) round*=16;
+		if (p>=0 && p<(LDBL_MANT_DIG-1+3)/4) {
+			double round = scalbn(1, LDBL_MANT_DIG-1-(p*4));
 			if (*prefix=='-') {
 				y=-y;
 				y-=round;
diff --git a/src/time/strptime.c b/src/time/strptime.c
index c54a0d8c..b1147242 100644
--- a/src/time/strptime.c
+++ b/src/time/strptime.c
@@ -59,6 +59,22 @@ char *strptime(const char *restrict s, const char *restrict f, struct tm *restri
 			s = strptime(s, "%m/%d/%y", tm);
 			if (!s) return 0;
 			break;
+		case 'F':
+			/* Use temp buffer to implement the odd requirement
+			 * that entire field be width-limited but the year
+			 * subfield not itself be limited. */
+			i = 0;
+			char tmp[20];
+			if (*s == '-' || *s == '+') tmp[i++] = *s++;
+			while (*s=='0' && isdigit(s[1])) s++;
+			for (; *s && i<(size_t)w && i+1<sizeof tmp; i++) {
+				tmp[i] = *s++;
+			}
+			tmp[i] = 0;
+			char *p = strptime(tmp, "%12Y-%m-%d", tm);
+			if (!p) return 0;
+			s -= tmp+i-p;
+			break;
 		case 'H':
 			dest = &tm->tm_hour;
 			min = 0;
@@ -114,6 +130,13 @@ char *strptime(const char *restrict s, const char *restrict f, struct tm *restri
 			s = strptime(s, "%H:%M", tm);
 			if (!s) return 0;
 			break;
+		case 's':
+			/* Parse only. Effect on tm is unspecified
+			 * and presently no effect is implemented.. */
+			if (*s == '-') s++;
+			if (!isdigit(*s)) return 0;
+			while (isdigit(*s)) s++;
+			break;
 		case 'S':
 			dest = &tm->tm_sec;
 			min = 0;
@@ -125,11 +148,30 @@ char *strptime(const char *restrict s, const char *restrict f, struct tm *restri
 			break;
 		case 'U':
 		case 'W':
-			/* Throw away result, for now. (FIXME?) */
+			/* Throw away result of %U, %V, %W, %g, and %G. Effect
+			 * is unspecified and there is no clear right choice. */
 			dest = &dummy;
 			min = 0;
 			range = 54;
 			goto numeric_range;
+		case 'V':
+			dest = &dummy;
+			min = 1;
+			range = 53;
+			goto numeric_range;
+		case 'g':
+			dest = &dummy;
+			w = 2;
+			goto numeric_digits;
+		case 'G':
+			dest = &dummy;
+			if (w<0) w=4;
+			goto numeric_digits;
+		case 'u':
+			dest = &tm->tm_wday;
+			min = 1;
+			range = 7;
+			goto numeric_range;
 		case 'w':
 			dest = &tm->tm_wday;
 			min = 0;
@@ -154,6 +196,28 @@ char *strptime(const char *restrict s, const char *restrict f, struct tm *restri
 			adj = 1900;
 			want_century = 0;
 			goto numeric_digits;
+		case 'z':
+			if (*s == '+') neg = 0;
+			else if (*s == '-') neg = 1;
+			else return 0;
+			for (i=0; i<4; i++) if (!isdigit(s[1+i])) return 0;
+			tm->__tm_gmtoff = (s[1]-'0')*36000+(s[2]-'0')*3600
+				+ (s[3]-'0')*600 + (s[4]-'0')*60;
+			if (neg) tm->__tm_gmtoff = -tm->__tm_gmtoff;
+			s += 5;
+			break;
+		case 'Z':
+			if (!strncmp(s, tzname[0], len = strlen(tzname[0]))) {
+				tm->tm_isdst = 0;
+				s += len;
+			} else if (!strncmp(s, tzname[1], len=strlen(tzname[1]))) {
+				tm->tm_isdst = 1;
+				s += len;
+			} else {
+				/* FIXME: is this supposed to be an error? */
+				while ((*s|32)-'a' <= 'z'-'a') s++;
+			}
+			break;
 		case '%':
 			if (*s++ != '%') return 0;
 			break;
diff --git a/src/unistd/pwrite.c b/src/unistd/pwrite.c
index 869b69f0..a008b3ec 100644
--- a/src/unistd/pwrite.c
+++ b/src/unistd/pwrite.c
@@ -1,7 +1,18 @@
+#define _GNU_SOURCE
 #include <unistd.h>
+#include <sys/uio.h>
+#include <fcntl.h>
 #include "syscall.h"
 
 ssize_t pwrite(int fd, const void *buf, size_t size, off_t ofs)
 {
+	if (ofs == -1) ofs--;
+	int r = __syscall_cp(SYS_pwritev2, fd,
+		(&(struct iovec){ .iov_base = (void *)buf, .iov_len = size }),
+		1, (long)(ofs), (long)(ofs>>32), RWF_NOAPPEND);
+	if (r != -EOPNOTSUPP && r != -ENOSYS)
+		return __syscall_ret(r);
+	if (fcntl(fd, F_GETFL) & O_APPEND)
+		return __syscall_ret(-EOPNOTSUPP);
 	return syscall_cp(SYS_pwrite, fd, buf, size, __SYSCALL_LL_PRW(ofs));
 }
diff --git a/src/unistd/pwritev.c b/src/unistd/pwritev.c
index becf9deb..44a53d85 100644
--- a/src/unistd/pwritev.c
+++ b/src/unistd/pwritev.c
@@ -1,10 +1,18 @@
-#define _BSD_SOURCE
+#define _GNU_SOURCE
 #include <sys/uio.h>
 #include <unistd.h>
+#include <fcntl.h>
 #include "syscall.h"
 
 ssize_t pwritev(int fd, const struct iovec *iov, int count, off_t ofs)
 {
+	if (ofs == -1) ofs--;
+	int r = __syscall_cp(SYS_pwritev2, fd, iov, count,
+		(long)(ofs), (long)(ofs>>32), RWF_NOAPPEND);
+	if (r != -EOPNOTSUPP && r != -ENOSYS)
+		return __syscall_ret(r);
+	if (fcntl(fd, F_GETFL) & O_APPEND)
+		return __syscall_ret(-EOPNOTSUPP);
 	return syscall_cp(SYS_pwritev, fd, iov, count,
 		(long)(ofs), (long)(ofs>>32));
 }