code: plan9front

Download patch

ref: 744475a503e3af4597a4a038c3686c25abb48ee0
parent: 1b02c81c18eb4be6d9372c8d668ad2314bae7872
author: Sigrid Solveig Haflínudóttir <sigrid@ftrv.se>
date: Sat Nov 5 19:41:43 EDT 2022

read: add -r to read runes instead of bytes (thanks umbraticus)

--- a/sys/man/1/cat
+++ b/sys/man/1/cat
@@ -10,12 +10,15 @@
 .B read
 [
 .B -m
-] [
+|
 .B -n
 .I nlines
-] [
+|
 .B -c
 .I nbytes
+|
+.B -r
+.I nrunes
 ] [
 .I file ...
 ]
@@ -59,20 +62,14 @@
 causes it to read no more than
 .I nlines
 lines.
-.PP
-With the
+The
 .B -c
-flag,
-.I read
-copies exactly
-.I nbytes
-of characters instead of lines. It is mutually exclusive with
-.B -n
 and
-.B -m
-flag.
+.B -r
+flags specify a number of bytes or runes to read instead of lines.
 .PP
-.I Read
+When reading lines,
+.I read
 always executes a single
 .B write
 for each line of input, which can be helpful when
--- a/sys/src/cmd/read.c
+++ b/sys/src/cmd/read.c
@@ -2,8 +2,7 @@
 #include <libc.h>
 
 int	multi;
-int	nlines;
-vlong	nchars;
+vlong	count;
 char	*status = nil;
 
 int
@@ -50,7 +49,7 @@
 	do{
 		if(line(fd, file) == 0)
 			break;
-	}while(multi || --nlines>0);
+	}while(multi || --count > 0);
 }
 
 void
@@ -60,10 +59,10 @@
 	vlong m;
 	int n;
 
-	for(m = 0; m < nchars; m += n){
+	for(m = 0; m < count; m += n){
 		n = sizeof(buf);
-		if(n > (nchars - m))
-			n = nchars - m;
+		if(n > (count - m))
+			n = count - m;
 		if((n = read(fd, buf, n)) < 0){
 			fprint(2, "read: error reading %s: %r\n", file);
 			exits("read error");
@@ -78,9 +77,45 @@
 }
 
 void
+runes(int fd, char *file)
+{
+	char buf[8*1024], *s, *e;
+	Rune r;
+
+	while(count > 0){
+		e = buf + read(fd, buf, count + UTFmax < sizeof buf ? count : sizeof buf - UTFmax);
+		if(e < buf){
+			fprint(2, "read: error reading %s: %r\n", file);
+			exits("read error");
+		}
+		if(e == buf)
+			break;
+		for(s = buf; s < e && fullrune(s, e - s); s += chartorune(&r, s))
+			count--;
+		if(s < e){
+			while(!fullrune(s, e - s))
+				switch(read(fd, e, 1)){
+				case -1:
+					fprint(2, "read: error reading %s: %r\n", file);
+					exits("read error");
+				case 0:
+					fprint(2, "warning: partial rune at end of %s: %r\n", file);
+					write(1, buf, e - buf);
+					return;
+				case 1:
+					e++;
+					break;
+				}
+			count--;
+		}
+		write(1, buf, e - buf);
+	}
+}
+
+void
 usage(void)
 {
-	fprint(2, "usage: read [-m] [-n nlines] [-c nbytes] [files...]\n");
+	fprint(2, "usage: read [ -m | -n nlines | -c nbytes | -r nrunes ] [ file ... ]\n");
 	exits("usage");
 }
 
@@ -93,11 +128,15 @@
 	proc = lines;
 	ARGBEGIN{
 	case 'c':
-		nchars = atoll(EARGF(usage()));
+		count = atoll(EARGF(usage()));
 		proc = chars;
 		break;
+	case 'r':
+		count = atoll(EARGF(usage()));
+		proc = runes;
+		break;
 	case 'n':
-		nlines = atoi(EARGF(usage()));
+		count = atoi(EARGF(usage()));
 		break;
 	case 'm':
 		multi = 1;