rs/.linked/strtonum.c010064400000000000000000000036761363255147700120450ustar00/* $OpenBSD: strtonum.c,v 1.8 2015/09/13 08:31:48 guenther Exp $ */ /* * Copyright (c) 2004 Ted Unangst and Todd Miller * Copyright (c) 2005 mirabilos * All rights reserved. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #ifdef MBSDPORT_H #include MBSDPORT_H #endif __RCSID("$MirOS: src/usr.bin/rs/.linked/strtonum.c,v 1.4 2020/03/13 00:26:12 tg Exp $"); #define INVALID 1 #define TOOSMALL 2 #define TOOLARGE 3 long long strtonum(const char *numstr, long long minval, long long maxval, const char **errstrp) { long long ll = 0; int error = 0; char *ep; struct errval { const char *errstr; int err; } ev[4] = { { NULL, 0 }, { "invalid", EINVAL }, { "too small", ERANGE }, { "too large", ERANGE }, }; ev[0].err = errno; errno = 0; if (minval > maxval) { error = INVALID; } else { ll = strtoll(numstr, &ep, 0); if (numstr == ep || *ep != '\0') error = INVALID; else if ((ll == LLONG_MIN && errno == ERANGE) || ll < minval) error = TOOSMALL; else if ((ll == LLONG_MAX && errno == ERANGE) || ll > maxval) error = TOOLARGE; } if (errstrp != NULL) *errstrp = ev[error].errstr; errno = ev[error].err; if (error) ll = 0; return (ll); } #ifdef DEF_WEAK DEF_WEAK(strtonum); #endif rs/Makefile010064400000000000000000000005151363225417500101020ustar00# $MirOS: src/usr.bin/rs/Makefile,v 1.3 2020/03/11 21:30:10 tg Exp $ # $OpenBSD: Makefile,v 1.3 2015/12/03 12:23:15 schwarze Exp $ PROG= rs SRCS= rs.c utf8.c MKSHDIR?= ${BSDSRCDIR}/bin/mksh SRCDIR?= ${.CURDIR} regress: ${PROG} check.t perl ${MKSHDIR:Q}/check.pl -s ${SRCDIR:Q}/check.t -v -p ./${PROG:Q} .include rs/check.t010064400000000000000000000045731363255334700077170ustar00# $MirOS: src/usr.bin/rs/check.t,v 1.7 2020/03/13 00:41:48 tg Exp $ name: debian-650029-e description: Check basic output stdin: 1 a wide column 2 3 4 a another wide column b c d arguments: !-e!0!5! expected-stdout: 1 a wide column 2 3 4 a another wide column b c d --- name: debian-650029-ze description: Check output with -z stdin: 1 a wide column 2 3 4 a another wide column b c d arguments: !-ze!0!5! expected-stdout: 1 a wide column 2 3 4 a another wide column b c d --- name: debian-668306 description: Check it doesn’t immediately segfault stdin: 1 2 3 4 5 6 7 8 9 arguments: !3!3! expected-stdout: 1 2 3 4 5 6 7 8 9 --- name: debian-706717-r1.10 description: Handle empty fields stdin: 0 0 0 arguments: !-c!0!2! expected-stdout: 0 0 0 --- name: debian-706717-r1.11-1 description: Handle empty last fields stdin: 0 0 0 arguments: !-c!0!2! expected-stdout: 0 0 0 --- name: debian-706717-r1.11-2 description: Handle empty last fields stdin: 0 0 0 arguments: !-c!0!2! expected-stdout: 0 0 0 --- name: empty-last-line-1 description: Basis for the next test stdin: a b c d arguments: !0!2! expected-stdout: a b c d --- name: empty-last-line-2 description: There should be no empty line just due to a trailing space stdin: a b c d arguments: !0!2! expected-stdout: a b c d --- name: unicode-1 description: Fun with UTF-8 stdin: mäh miau a Kuh 猫 b ❣❧ マヌル c env-setup: !LC_ALL=C.UTF-8! arguments: !0!3! expected-stdout: mäh miau a Kuh 猫 b ❣❧ マヌル c --- name: unicode-2 description: Fun with UTF-8 stdin: mäh miau a Kuh 猫 b ❣❧ マヌル c env-setup: !LC_ALL=C.UTF-8! arguments: !-z!0!3! expected-stdout: mäh miau a Kuh 猫 b ❣❧ マヌル c --- name: unicode-3-utf8 description: Invalids category: !os:mirbsd stdin: b c env-setup: !LC_ALL=C.UTF-8! arguments: !-z!0!2! expected-stdout: ? b c ?? --- name: unicode-3-optu8 description: Invalids category: os:mirbsd stdin: b c env-setup: !LC_ALL=C.UTF-8! arguments: !-z!0!2! expected-stdout: b c --- name: unicode-4 description: ASCII category: !os:mirbsd stdin: ä b c d env-setup: !LC_ALL=C! arguments: !-z!0!2! expected-stdout: ä b c d --- rs/rs.1010064400000000000000000000262661363277660200071670ustar00.\" $MirOS: src/usr.bin/rs/rs.1,v 1.9 2020/03/13 21:38:47 tg Exp $ .\" $OpenBSD: rs.1,v 1.19 2016/10/24 13:46:58 schwarze Exp $ .\" $FreeBSD: src/usr.bin/rs/rs.1,v 1.4 1999/08/28 01:05:21 peter Exp $ .\"- .\" Copyright (c) 1993 .\" The Regents of the University of California. All rights reserved. .\" Copyright (c) 2008, 2009, 2010, 2012, 2016, 2018, 2020 .\" mirabilos .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" 3. Neither the name of the University nor the names of its contributors .\" may be used to endorse or promote products derived from this software .\" without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" @(#)rs.1 8.2 (Berkeley) 12/30/93 .\" .\"- .\" Try to make GNU groff and AT&T nroff more compatible .\" * ` generates ‘ in gnroff, so use \` .\" * ' generates ’ in gnroff, \' generates ´, so use \*(aq .\" * - generates ‐ in gnroff, \- generates −, so .tr it to - .\" thus use - for hyphens and \- for minus signs and option dashes .\" * ~ is size-reduced and placed atop in groff, so use \*(TI .\" * ^ is size-reduced and placed atop in groff, so use \*(ha .\" * \(en does not work in nroff, so use \*(en .\" * <>| are problematic, so redefine and use \*(Lt\*(Gt\*(Ba .\" Also make sure to use \& *before* a punctuation char that is to not .\" be interpreted as punctuation, and especially with two-letter words .\" but also (after) a period that does not end a sentence (“e.g.\&”). .\" The section after the "doc" macropackage has been loaded contains .\" additional code to convene between the UCB mdoc macropackage (and .\" its variant as BSD mdoc in groff) and the GNU mdoc macropackage. .\" .ie \n(.g \{\ . if \*[.T]ascii .tr \-\N'45' . if \*[.T]latin1 .tr \-\N'45' . if \*[.T]utf8 .tr \-\N'45' . ds <= \[<=] . ds >= \[>=] . ds Rq \[rq] . ds Lq \[lq] . ds sL \(aq . ds sR \(aq . if \*[.T]utf8 .ds sL ` . if \*[.T]ps .ds sL ` . if \*[.T]utf8 .ds sR ' . if \*[.T]ps .ds sR ' . ds aq \(aq . ds TI \(ti . ds ha \(ha . ds en \(en .\} .el \{\ . ds aq ' . ds TI ~ . ds ha ^ . ds en \(em .\} .\" .\" Implement .Dd with the Mdocdate RCS keyword .\" .rn Dd xD .de Dd .ie \\$1$Mdocdate: \{\ . xD \\$2 \\$3, \\$4 .\} .el .xD \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 .. .\" .\" .Dd must come before definition of .Mx, because when called .\" with -mandoc, it might implement .Mx itself, but we want to .\" use our own definition. And .Dd must come *first*, always. .\" .Dd $Mdocdate: March 13 2020 $ .\" .\" Check which macro package we use, and do other -mdoc setup. .\" .ie \n(.g \{\ . if \*[.T]utf8 .tr \[la]\*(Lt . if \*[.T]utf8 .tr \[ra]\*(Gt . ie d volume-ds-1 .ds tT gnu . el .ie d doc-volume-ds-1 .ds tT gnp . el .ds tT bsd .\} .el .ds tT ucb .\" .\" Implement .Mx (MirBSD) .\" .ie "\*(tT"gnu" \{\ . eo . de Mx . nr curr-font \n[.f] . nr curr-size \n[.ps] . ds str-Mx \f[\n[curr-font]]\s[\n[curr-size]u] . ds str-Mx1 \*[Tn-font-size]\%MirBSD\*[str-Mx] . if !\n[arg-limit] \ . if \n[.$] \{\ . ds macro-name Mx . parse-args \$@ . \} . if (\n[arg-limit] > \n[arg-ptr]) \{\ . nr arg-ptr +1 . ie (\n[type\n[arg-ptr]] == 2) \ . as str-Mx1 \~\*[arg\n[arg-ptr]] . el \ . nr arg-ptr -1 . \} . ds arg\n[arg-ptr] "\*[str-Mx1] . nr type\n[arg-ptr] 2 . ds space\n[arg-ptr] "\*[space] . nr num-args (\n[arg-limit] - \n[arg-ptr]) . nr arg-limit \n[arg-ptr] . if \n[num-args] \ . parse-space-vector . print-recursive .. . ec . ds sP \s0 . ds tN \*[Tn-font-size] .\} .el .ie "\*(tT"gnp" \{\ . eo . de Mx . nr doc-curr-font \n[.f] . nr doc-curr-size \n[.ps] . ds doc-str-Mx \f[\n[doc-curr-font]]\s[\n[doc-curr-size]u] . ds doc-str-Mx1 \*[doc-Tn-font-size]\%MirBSD\*[doc-str-Mx] . if !\n[doc-arg-limit] \ . if \n[.$] \{\ . ds doc-macro-name Mx . doc-parse-args \$@ . \} . if (\n[doc-arg-limit] > \n[doc-arg-ptr]) \{\ . nr doc-arg-ptr +1 . ie (\n[doc-type\n[doc-arg-ptr]] == 2) \ . as doc-str-Mx1 \~\*[doc-arg\n[doc-arg-ptr]] . el \ . nr doc-arg-ptr -1 . \} . ds doc-arg\n[doc-arg-ptr] "\*[doc-str-Mx1] . nr doc-type\n[doc-arg-ptr] 2 . ds doc-space\n[doc-arg-ptr] "\*[doc-space] . nr doc-num-args (\n[doc-arg-limit] - \n[doc-arg-ptr]) . nr doc-arg-limit \n[doc-arg-ptr] . if \n[doc-num-args] \ . doc-parse-space-vector . doc-print-recursive .. . ec . ds sP \s0 . ds tN \*[doc-Tn-font-size] .\} .el \{\ . de Mx . nr cF \\n(.f . nr cZ \\n(.s . ds aa \&\f\\n(cF\s\\n(cZ . if \\n(aC==0 \{\ . ie \\n(.$==0 \&MirBSD\\*(aa . el .aV \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9 . \} . if \\n(aC>\\n(aP \{\ . nr aP \\n(aP+1 . ie \\n(C\\n(aP==2 \{\ . as b1 \&MirBSD\ #\&\\*(A\\n(aP\\*(aa . ie \\n(aC>\\n(aP \{\ . nr aP \\n(aP+1 . nR . \} . el .aZ . \} . el \{\ . as b1 \&MirBSD\\*(aa . nR . \} . \} .. .\} .\"- .Dt RS 1 .Os .Sh NAME .Nm rs .Nd reshape a data array .Sh SYNOPSIS .Nm rs .Op Fl CcSs Ns Op Ar x .Op Fl GgKkw Ar N .Op Fl EeHhjmnTtyz .Op Ar rows Op Ar cols .Sh DESCRIPTION .Nm reads the standard input, interpreting each line as a row of blank-separated entries in an array, transforms the array according to the options, and writes it on the standard output. With no arguments .Bk -words .Pq Dv argc Li \*(Lt 2 .Ek it transforms stream input into a columnar format convenient for terminal viewing, i.e. if the length (in bytes!) of the first line is smaller than the display width, .Fl et is implied, .Fl t otherwise. .Pp The shape of the input array is deduced from the number of lines and the number of columns on the first line. If that shape is inconvenient, a more useful one might be obtained by skipping some of the input with the .Fl k option. Other options control interpretation of the input columns. .Pp The shape of the output array is influenced by the .Ar rows and .Ar cols specifications, which should be positive integers. If only one of them is a positive integer, .Nm computes a value for the other which will accommodate all of the data. When necessary, missing data are supplied in a manner specified by the options and surplus data are deleted. There are options to control presentation of the output columns, including transposition of the rows and columns. .Pp The options are as follows: .Bl -tag -width XXXXX .It Fl C Ns Op Ar x Output columns are delimited by the single character .Ar x . .br A missing .Ar x is taken to be .Ql \*(haI . .It Fl c Ns Op Ar x Input columns are delimited by the single character .Ar x . .br A missing .Ar x is taken to be .Ql \*(haI . .It Fl E Consider each character of input as an array entry. .It Fl e Consider each line of input as an array entry. .It Fl G Ns Ar N The gutter width (inter-column space) has .Ar N percent of the maximum column width added to it. .It Fl g Ns Ar N The gutter width, normally 2, is taken to be .Ar N . .It Fl H Like .Fl h , but also print the length of each line. .It Fl h Print the shape of the input array and do nothing else. The shape is just the number of lines and the number of entries on the first line. .It Fl j Right adjust entries within columns. .It Fl K Ns Ar N Like .Fl k , but print the ignored lines. .It Fl k Ns Ar N Ignore the first .Ar N lines of input. .It Fl m Do not trim excess delimiters from the ends of the output array. .It Fl n On lines having fewer entries than the first line, use null entries to pad out the line. Normally, missing entries are taken from the next line of input. .It Fl S Ns Op Ar x Like .Fl C , but padded strings of .Ar x are delimiters. .It Fl s Ns Op Ar x Like .Fl c , but maximal strings of .Ar x are delimiters. .It Fl T Print the pure transpose of the input, ignoring any .Ar rows or .Ar cols specification. .It Fl t Fill in the rows of the output array using the columns of the input array, that is, transpose the input while honoring any .Ar rows and .Ar cols specifications. .It Fl w Ns Ar N The width of the display, normally 80, is taken to be the positive integer .Ar N . .It Fl y If there are too few entries to make up the output dimensions, pad the output by recycling the input from the beginning. Normally, the output is padded with blanks. .It Fl z Shrink column widths to fit the largest entries appearing in them. .El .Pp With no arguments, .Nm transposes its input, and assumes one array entry per input line unless the first non-ignored line is longer than the display width. Option letters which take numerical arguments interpret a missing number as zero unless otherwise indicated. .Sh ENVIRONMENT .Bl -tag -width LC_CTYPE .It Ev LC_CTYPE The character encoding .Xr locale 1 . It decides which byte sequences form characters and what their display width is. If unset or set to .Qq Li C , .Qq Li POSIX or an unsupported value, each byte is treated as a character of display width 1. .El .Sh EXAMPLES .Nm can be used as a filter to convert the stream output of certain programs (e.g., .Xr spell 1 , .Xr du 1 , .Xr file 1 , .Xr look 1 , .Xr nm 1 , .Xr who 1 , and .Xr wc 1 ) into a convenient .Dq window format, as in .Bd -literal -offset indent $ who \*(Ba rs .Ed .Pp This function has been incorporated into the .Xr ls 1 program, though for most programs with similar output .Nm suffices. .Pp To convert stream input into vector output and back again, use .Bd -literal -offset indent $ rs 1 0 \*(Ba rs 0 1 .Ed .Pp A 10 by 10 array of random numbers from 1 to 100 and its transpose can be generated with .Bd -literal -offset indent $ jot \-r 100 \*(Ba rs 10 10 \*(Ba tee array \*(Ba rs \-T \*(Gttarray .Ed .Pp In the editor .Xr vi 1 , a file consisting of a multi-line vector with 9 elements per line can undergo insertions and deletions, and then be neatly reshaped into 9 columns with .Bd -literal -offset indent :1,$!rs 0 9 .Ed .Pp Finally, to sort a database by the first line of each 4-line field, try .Bd -literal -offset indent $ rs \-eC 0 4 \*(Ba sort \*(Ba rs \-c 0 1 .Ed .Sh SEE ALSO .Xr jot 1 , .Xr pr 1 , .Xr sort 1 , .Xr vi 1 .Sh HISTORY The .Nm utility first appeared in .Bx 4.2 . .Sh AUTHORS .An John A. Kunze .Sh BUGS Handles only two dimensional arrays. .Pp The algorithm currently reads the whole file into memory, so files that do not fit in memory will not be reshaped. .Pp Fields cannot be defined yet on character positions. .Pp Re-ordering of columns is not yet possible. .Pp There are too many options. rs/rs.c010064400000000000000000000245201363275420700072350ustar00/* $OpenBSD: rs.c,v 1.30 2015/12/03 12:23:15 schwarze Exp $ */ /*- * Copyright (c) 2014, 2020 * mirabilos * Copyright (c) 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * rs - reshape a data array * Author: John Kunze, Office of Comp. Affairs, UCB * BEWARE: lots of unfinished edges */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #define EXTERN #include "rs.h" __RCSID("$MirOS: src/usr.bin/rs/rs.c,v 1.16 2020/03/13 19:01:00 tg Exp $"); struct entry { int w; /* display width */ const char *s; /* multibyte string */ }; long flags; #define TRANSPOSE 000001 #define MTRANSPOSE 000002 #define ONEPERLINE 000004 #define ONEISEPONLY 000010 #define ONEOSEPONLY 000020 #define NOTRIMENDCOL 000040 #define SQUEEZE 000100 #define SHAPEONLY 000200 #define DETAILSHAPE 000400 #define RIGHTADJUST 001000 #define NULLPAD 002000 #define RECYCLE 004000 #define SKIPPRINT 010000 #define ONEPERCHAR 0100000 #define NOARGS 0200000 short *colwidths; int nelem; struct entry *elem; struct entry *endelem; char *curline; size_t allocsize = BUFSIZ; int irows, icols; int orows, ocols; int maxwidth; int skip; int propgutter; char isep = ' ', osep = ' '; int owidth = 80, gutter = 2; void usage(void) __dead; void getargs(int, char *[]); void getfile(void); int get_line(void); struct entry *getptrs(struct entry *); void prepfile(void); void prints(struct entry *, int); void putfile(void); #define INCR(ep) do { \ if (++ep >= endelem) \ ep = getptrs(ep); \ } while(0) int main(int argc, char *argv[]) { setlocale(LC_CTYPE, ""); #ifdef __OpenBSD__ if (pledge("stdio", NULL) == -1) err(1, "pledge"); #endif getargs(argc, argv); getfile(); if (flags & SHAPEONLY) { printf("%d %d\n", irows, icols); exit(0); } prepfile(); putfile(); exit(0); } void getfile(void) { const char delim[2] = { isep, '\0' }; char *p; struct entry *ep; int multisep = (flags & ONEISEPONLY ? 0 : 1); int nullpad = flags & NULLPAD; struct entry *padto; curline = NULL; while (skip--) { if (get_line() == EOF) return; if (flags & SKIPPRINT) puts(curline); } if (get_line() == EOF) return; if (flags & NOARGS && strlen(curline) < (size_t)owidth) flags |= ONEPERLINE; if (flags & ONEPERLINE) icols = 1; else /* count cols on first line */ for (p = curline; *p != '\0'; p++) { if (*p == isep && multisep) continue; icols++; while (*p && *p != isep) p++; } ep = getptrs(NULL); p = curline; do { if (flags & ONEPERLINE) { ep->w = mbsavis(&ep->s, curline); if (maxwidth < ep->w) maxwidth = ep->w; INCR(ep); /* prepare for next entry */ irows++; continue; } p = curline; while (p != NULL && *p != '\0') { if (*p == isep) { p++; if (multisep) continue; ep->s = ""; /* empty column */ ep->w = 0; } else ep->w = mbsavis(&ep->s, strsep(&p, delim)); if (maxwidth < ep->w) maxwidth = ep->w; INCR(ep); /* prepare for next entry */ } irows++; /* update row count */ if (nullpad) { /* pad missing entries */ padto = elem + irows * icols; while (ep < padto) { ep->s = ""; ep->w = 0; INCR(ep); } } } while (get_line() != EOF); nelem = ep - elem; } void putfile(void) { struct entry *ep; int i, j, n; ep = elem; if (flags & TRANSPOSE) { for (i = 0; i < orows; i++) { for (j = i; j < nelem; j += orows) prints(ep + j, (j - i) / orows); putchar('\n'); } } else { for (n = 0, i = 0; i < orows && n < nelem; i++) { for (j = 0; j < ocols; j++) { if (n++ >= nelem) break; prints(ep++, j); } putchar('\n'); } } } void prints(struct entry *ep, int col) { int n; n = (flags & ONEOSEPONLY ? 1 : colwidths[col] - ep->w); if (flags & RIGHTADJUST) while (n-- > 0) putchar(osep); fputs(ep->s, stdout); while (n-- > 0) putchar(osep); } void usage(void) { extern char *__progname; fprintf(stderr, "usage: %s [-CcSs[x]] [-GgKkw N] [-EeHhjmnTtyz] [rows [cols]]\n", __progname); exit(1); } void prepfile(void) { struct entry *ep; int i; int j; struct entry *lp; int colw; int max = 0; int n; if (!nelem) exit(0); gutter += maxwidth * propgutter / 100.0; colw = maxwidth + gutter; if (flags & MTRANSPOSE) { orows = icols; ocols = irows; } else if (orows == 0 && ocols == 0) { /* decide rows and cols */ ocols = owidth / colw; if (ocols == 0) { warnx("Display width %d is less than column width %d", owidth, colw); ocols = 1; } if (ocols > nelem) ocols = nelem; orows = nelem / ocols + (nelem % ocols ? 1 : 0); } else if (orows == 0) /* decide on rows */ orows = nelem / ocols + (nelem % ocols ? 1 : 0); else if (ocols == 0) /* decide on cols */ ocols = nelem / orows + (nelem % orows ? 1 : 0); while ((lp = elem + orows * ocols) > endelem) (void)getptrs(NULL); if (flags & RECYCLE) { for (ep = elem + nelem; ep < lp; ep++) memcpy(ep, ep - nelem, sizeof(*ep)); nelem = lp - elem; } if (!(colwidths = calloc(ocols, sizeof(short)))) errx(1, "malloc: No gutter space"); if (flags & SQUEEZE) { for (ep = elem, i = 0; i < ocols; i++) { max = 0; if (flags & TRANSPOSE) { for (j = 0; j < orows; j++, ep++) if (ep->w > max) max = ep->w; } else { for (j = i; j < nelem; j += ocols) if (ep[j].w > max) max = ep[j].w; } colwidths[i] = max + gutter; } } else { for (i = 0; i < ocols; i++) colwidths[i] = colw; } if (!(flags & NOTRIMENDCOL)) { if (flags & RIGHTADJUST) colwidths[0] -= gutter; else colwidths[ocols - 1] = 0; } n = orows * ocols; if (n > nelem && (flags & RECYCLE)) nelem = n; } int get_line(void) { static size_t cursz; static ssize_t curlen; if (irows > 0 && flags & DETAILSHAPE) printf(" %zd line %d\n", curlen, irows); if ((curlen = getline(&curline, &cursz, stdin)) == EOF) { if (ferror(stdin)) err(1, NULL); return EOF; } if (curlen > 0 && curline[curlen - 1] == '\n') curline[--curlen] = '\0'; return 0; } struct entry * getptrs(struct entry *sp) { struct entry *p; size_t newsize; newsize = allocsize * 2; p = reallocarray(elem, newsize, sizeof(*p)); if (p == NULL) err(1, "no memory"); allocsize = newsize; sp = sp == NULL ? p : p + (sp - elem); elem = p; endelem = elem + allocsize; return(sp); } void getargs(int ac, char *av[]) { int ch; const char *errstr; if (ac < 2) flags |= NOARGS | TRANSPOSE; while ((ch = getopt(ac, av, "c::C::s::S::k:K:g:G:w:tTeEnyjhHmz")) != -1) { switch (ch) { case 'T': flags |= MTRANSPOSE; /* FALLTHROUGH */ case 't': flags |= TRANSPOSE; break; case 'c': /* input col. separator */ flags |= ONEISEPONLY; /* FALLTHROUGH */ case 's': /* one or more allowed */ if (optarg == NULL) isep = '\t'; /* default is ^I */ else if (optarg[1] != '\0') usage(); /* single char only */ else isep = *optarg; break; case 'C': flags |= ONEOSEPONLY; /* FALLTHROUGH */ case 'S': if (optarg == NULL) osep = '\t'; /* default is ^I */ else if (optarg[1] != '\0') usage(); /* single char only */ else osep = *optarg; break; case 'w': /* window width, default 80 */ owidth = strtonum(optarg, 1, INT_MAX, &errstr); if (errstr) { warnx("width %s", errstr); usage(); } break; case 'K': /* skip N lines */ flags |= SKIPPRINT; /* FALLTHROUGH */ case 'k': /* skip, do not print */ skip = strtonum(optarg, 0, INT_MAX, &errstr); if (errstr) { warnx("skip value %s", errstr); usage(); } if (skip == 0) skip = 1; break; case 'm': flags |= NOTRIMENDCOL; break; case 'g': /* gutter width */ gutter = strtonum(optarg, 0, INT_MAX, &errstr); if (errstr) { warnx("gutter width %s", errstr); usage(); } break; case 'G': propgutter = strtonum(optarg, 0, INT_MAX, &errstr); if (errstr) { warnx("gutter proportion %s", errstr); usage(); } break; case 'e': /* each line is an entry */ flags |= ONEPERLINE; break; case 'E': flags |= ONEPERCHAR; break; case 'j': /* right adjust */ flags |= RIGHTADJUST; break; case 'n': /* null padding for missing values */ flags |= NULLPAD; break; case 'y': flags |= RECYCLE; break; case 'H': /* print shape only */ flags |= DETAILSHAPE; /* FALLTHROUGH */ case 'h': flags |= SHAPEONLY; break; case 'z': /* squeeze col width */ flags |= SQUEEZE; break; default: usage(); } } ac -= optind; av += optind; switch (ac) { case 2: ocols = strtonum(av[1], 0, INT_MAX, &errstr); if (errstr) { warnx("columns value %s", errstr); usage(); } /* FALLTHROUGH */ case 1: orows = strtonum(av[0], 0, INT_MAX, &errstr); if (errstr) { warnx("columns value %s", errstr); usage(); } /* FALLTHROUGH */ case 0: break; default: usage(); } } rs/rs.h010064400000000000000000000057531363255243000072430ustar00#ifndef RS_H #define RS_H "$MirOS: src/usr.bin/rs/rs.h,v 1.4 2020/03/13 00:34:05 tg Exp $" /*- * Copyright © 2004, 2006, 2009, 2010, 2020 * mirabilos * * Provided that these terms and disclaimer and all copyright notices * are retained or reproduced in an accompanying document, permission * is granted to deal in this work without restriction, including un‐ * limited rights to use, publicly perform, distribute, sell, modify, * merge, give away, or sublicence. * * This work is provided “AS IS” and WITHOUT WARRANTY of any kind, to * the utmost extent permitted by applicable law, neither express nor * implied; without malicious intent or gross negligence. In no event * may a licensor, author or contributor be held liable for indirect, * direct, other damage, loss, or other issues arising in any way out * of dealing in the work, even if advised of the possibility of such * damage or existence of a defect, except proven that it results out * of said person’s immediate fault when using the work as intended. */ /* really sys/cdefs.h but that’s less portable */ #include /* now for stuff from our cdefs */ #if defined(MirBSD) && (MirBSD >= 0x09A1) && \ defined(__ELF__) && defined(__GNUC__) && \ !defined(__llvm__) && !defined(__NWCC__) /* * We got usable __IDSTRING __COPYRIGHT __RCSID __SCCSID macros * which work for all cases; no need to redefine them using the * "portable" macros from below when we might have the "better" * gcc+ELF specific macros or other system dependent ones. */ #else #undef __IDSTRING #undef __IDSTRING_CONCAT #undef __IDSTRING_EXPAND #undef __COPYRIGHT #undef __RCSID #undef __SCCSID #define __IDSTRING_CONCAT(l,p) __LINTED__ ## l ## _ ## p #define __IDSTRING_EXPAND(l,p) __IDSTRING_CONCAT(l,p) #ifdef MKSH_DONT_EMIT_IDSTRING #define __IDSTRING(prefix, string) /* nothing */ #elif defined(__ELF__) && defined(__GNUC__) && \ !(defined(__GNUC__) && defined(__mips16) && (__GNUC__ >= 8)) && \ !defined(__llvm__) && !defined(__NWCC__) && !defined(NO_ASM) #define __IDSTRING(prefix, string) \ __asm__(".section .comment" \ "\n .ascii \"@(\"\"#)" #prefix ": \"" \ "\n .asciz \"" string "\"" \ "\n .previous") #else #define __IDSTRING(prefix, string) \ static const char __IDSTRING_EXPAND(__LINE__,prefix) [] \ __attribute__((__used__)) = "@(""#)" #prefix ": " string #endif #define __COPYRIGHT(x) __IDSTRING(copyright,x) #define __RCSID(x) __IDSTRING(rcsid,x) #define __SCCSID(x) __IDSTRING(sccsid,x) #endif #ifndef __BEGIN_DECLS #define __BEGIN_DECLS /* nothing */ #define __END_DECLS /* unless this is CFrustFrust */ #endif #ifndef __dead #define __dead __attribute__((__noreturn__)) #endif /* above was stuff from MirBSD */ #ifdef EXTERN __RCSID(RS_H); #endif __BEGIN_DECLS #ifdef NEED_STRTONUM /* .linked/strtonum.c */ extern long long strtonum(const char *, long long, long long, const char **); #endif /* utf8.c */ int mbsavis(const char **, const char *); __END_DECLS #endif rs/utf8.c010064400000000000000000000034511363255255500075010ustar00/* * Copyright (c) 2015 Ingo Schwarze * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #define _XOPEN_SOURCE #include #include #include #include #include "rs.h" __RCSID("$MirOS: src/usr.bin/rs/utf8.c,v 1.5 2020/03/13 00:35:30 tg Exp $"); int mbsavis(const char **outp, const char *mbs) { const char *src; /* Iterate mbs. */ char *dst; /* Iterate *outp. */ wchar_t wc; int total_width; /* Display width of the whole string. */ int width; /* Display width of a single Unicode char. */ int len; /* Length in bytes of UTF-8 encoded string. */ len = strlen(mbs); if ((*outp = dst = malloc(len + 1)) == NULL) err(1, NULL); if (MB_CUR_MAX == 1) { memcpy(dst, mbs, len + 1); return len; } src = mbs; total_width = 0; while (*src != '\0') { if ((len = mbtowc(&wc, src, MB_CUR_MAX)) == -1) { total_width++; *dst++ = '?'; src++; } else if ((width = wcwidth(wc)) == -1) { total_width++; *dst++ = '?'; src += len; } else { total_width += width; while (len-- > 0) *dst++ = *src++; } } *dst = '\0'; return total_width; }