Module Name: src Committed By: reinoud Date: Sun Dec 27 21:13:18 UTC 2020
Modified Files: src/distrib/sets/lists/base: mi src/distrib/sets/lists/man: mi src/distrib/sets/lists/xbase: mi src/doc: CHANGES src/external/mit/xorg/bin/xterm: Makefile Added Files: src/usr.bin/resize: Makefile resize.1 resize.c xstrings.c xstrings.h Log Message: Import Xterm's resize(1) for querying (x)terminal sizes in base for headless clients To generate a diff of this commit: cvs rdiff -u -r1.1272 -r1.1273 src/distrib/sets/lists/base/mi cvs rdiff -u -r1.1711 -r1.1712 src/distrib/sets/lists/man/mi cvs rdiff -u -r1.163 -r1.164 src/distrib/sets/lists/xbase/mi cvs rdiff -u -r1.2770 -r1.2771 src/doc/CHANGES cvs rdiff -u -r1.18 -r1.19 src/external/mit/xorg/bin/xterm/Makefile cvs rdiff -u -r0 -r1.1 src/usr.bin/resize/Makefile \ src/usr.bin/resize/resize.1 src/usr.bin/resize/resize.c \ src/usr.bin/resize/xstrings.c src/usr.bin/resize/xstrings.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/distrib/sets/lists/base/mi diff -u src/distrib/sets/lists/base/mi:1.1272 src/distrib/sets/lists/base/mi:1.1273 --- src/distrib/sets/lists/base/mi:1.1272 Mon Nov 23 12:41:47 2020 +++ src/distrib/sets/lists/base/mi Sun Dec 27 21:13:18 2020 @@ -1,4 +1,4 @@ -# $NetBSD: mi,v 1.1272 2020/11/23 12:41:47 martin Exp $ +# $NetBSD: mi,v 1.1273 2020/12/27 21:13:18 reinoud Exp $ # # Note: Don't delete entries from here - mark them as "obsolete" instead, # unless otherwise stated below. @@ -951,6 +951,7 @@ ./usr/bin/renice base-util-bin ./usr/bin/reset base-util-bin ./usr/bin/rev base-util-bin +./usr/bin/resize base-util-bin ./usr/bin/revoke base-util-bin ./usr/bin/rfcomm_sppd base-util-bin ./usr/bin/rlog base-rcs-bin Index: src/distrib/sets/lists/man/mi diff -u src/distrib/sets/lists/man/mi:1.1711 src/distrib/sets/lists/man/mi:1.1712 --- src/distrib/sets/lists/man/mi:1.1711 Tue Nov 10 21:47:41 2020 +++ src/distrib/sets/lists/man/mi Sun Dec 27 21:13:18 2020 @@ -1,4 +1,4 @@ -# $NetBSD: mi,v 1.1711 2020/11/10 21:47:41 kamil Exp $ +# $NetBSD: mi,v 1.1712 2020/12/27 21:13:18 reinoud Exp $ # # Note: don't delete entries from here - mark them as "obsolete" instead. # @@ -490,6 +490,7 @@ ./usr/share/man/cat1/rehash.0 man-util-catman .cat ./usr/share/man/cat1/repeat.0 man-util-catman .cat ./usr/share/man/cat1/reset.0 man-util-catman .cat +./usr/share/man/cat1/resize.0 man-util-catman .cat ./usr/share/man/cat1/rev.0 man-util-catman .cat ./usr/share/man/cat1/rfcomm_sppd.0 man-util-catman .cat ./usr/share/man/cat1/rlog.0 man-rcs-catman .cat @@ -3774,6 +3775,7 @@ ./usr/share/man/html1/rehash.html man-util-htmlman html ./usr/share/man/html1/repeat.html man-util-htmlman html ./usr/share/man/html1/reset.html man-util-htmlman html +./usr/share/man/html1/resize.html man-util-htmlman html ./usr/share/man/html1/rev.html man-util-htmlman html ./usr/share/man/html1/rfcomm_sppd.html man-util-htmlman html ./usr/share/man/html1/rlog.html man-rcs-htmlman html @@ -6708,6 +6710,7 @@ ./usr/share/man/man1/rehash.1 man-util-man .man ./usr/share/man/man1/repeat.1 man-util-man .man ./usr/share/man/man1/reset.1 man-util-man .man +./usr/share/man/man1/resize.1 man-util-man .man ./usr/share/man/man1/rev.1 man-util-man .man ./usr/share/man/man1/rfcomm_sppd.1 man-util-man .man ./usr/share/man/man1/rlog.1 man-rcs-man .man Index: src/distrib/sets/lists/xbase/mi diff -u src/distrib/sets/lists/xbase/mi:1.163 src/distrib/sets/lists/xbase/mi:1.164 --- src/distrib/sets/lists/xbase/mi:1.163 Tue Nov 10 21:47:41 2020 +++ src/distrib/sets/lists/xbase/mi Sun Dec 27 21:13:18 2020 @@ -1,4 +1,4 @@ -# $NetBSD: mi,v 1.163 2020/11/10 21:47:41 kamil Exp $ +# $NetBSD: mi,v 1.164 2020/12/27 21:13:18 reinoud Exp $ # # Note: don't delete entries from here - mark them as "obsolete" instead. # @@ -53,7 +53,7 @@ ./usr/X11R7/bin/perfboth xbase-x11perf-bin xorg ./usr/X11R7/bin/perfratio xbase-x11perf-bin xorg ./usr/X11R7/bin/proxymngr xbase-proxymngr-bin xorg -./usr/X11R7/bin/resize xbase-xterm-bin xorg +./usr/X11R7/bin/resize xbase-obsolete obsolete ./usr/X11R7/bin/revpath xbase-revpath-bin xorg ./usr/X11R7/bin/sessreg xbase-sessreg-bin xorg ./usr/X11R7/bin/setxkbmap xbase-setxkbmap-bin xorg @@ -1309,7 +1309,7 @@ ./usr/X11R7/man/cat1/mkhtmlindex.0 xbase-mkhtmlindex-catman .cat,xorg ./usr/X11R7/man/cat1/oclock.0 xbase-oclock-catman .cat,xorg ./usr/X11R7/man/cat1/proxymngr.0 xbase-proxymngr-catman .cat,xorg -./usr/X11R7/man/cat1/resize.0 xbase-xterm-catman .cat,xorg +./usr/X11R7/man/cat1/resize.0 xbase-obsolete obsolete ./usr/X11R7/man/cat1/revpath.0 xbase-revpath-catman .cat,xorg ./usr/X11R7/man/cat1/sessreg.0 xbase-sessreg-catman .cat,xorg ./usr/X11R7/man/cat1/setxkbmap.0 xbase-setxkbmap-catman .cat,xorg @@ -1457,7 +1457,7 @@ ./usr/X11R7/man/html1/mkhtmlindex.html xbase-mkhtmlindex-htmlman html,xorg ./usr/X11R7/man/html1/oclock.html xbase-oclock-htmlman html,xorg ./usr/X11R7/man/html1/proxymngr.html xbase-proxymngr-htmlman html,xorg -./usr/X11R7/man/html1/resize.html xbase-resize-htmlman html,xorg +./usr/X11R7/man/html1/resize.html xbase-obsolete obsolete ./usr/X11R7/man/html1/revpath.html xbase-revpath-htmlman html,xorg ./usr/X11R7/man/html1/sessreg.html xbase-sessreg-htmlman html,xorg ./usr/X11R7/man/html1/setxkbmap.html xbase-setxkbmap-htmlman html,xorg @@ -1606,7 +1606,7 @@ ./usr/X11R7/man/man1/mkhtmlindex.1 xbase-mkhtmlindex-man .man,xorg ./usr/X11R7/man/man1/oclock.1 xbase-oclock-man .man,xorg ./usr/X11R7/man/man1/proxymngr.1 xbase-proxymngr-man .man,xorg -./usr/X11R7/man/man1/resize.1 xbase-xterm-man .man,xorg +./usr/X11R7/man/man1/resize.1 xbase-obsolete obsolete ./usr/X11R7/man/man1/revpath.1 xbase-revpath-man .man,xorg ./usr/X11R7/man/man1/sessreg.1 xbase-sessreg-man .man,xorg ./usr/X11R7/man/man1/setxkbmap.1 xbase-setxkbmap-man .man,xorg Index: src/doc/CHANGES diff -u src/doc/CHANGES:1.2770 src/doc/CHANGES:1.2771 --- src/doc/CHANGES:1.2770 Sun Dec 27 20:56:14 2020 +++ src/doc/CHANGES Sun Dec 27 21:13:17 2020 @@ -1,4 +1,4 @@ -# LIST OF CHANGES FROM LAST RELEASE: <$Revision: 1.2770 $> +# LIST OF CHANGES FROM LAST RELEASE: <$Revision: 1.2771 $> # # # [Note: This file does not mention every change made to the NetBSD source tree. @@ -322,4 +322,6 @@ Changes from NetBSD 9.0 to NetBSD 10.0: framebuffer for HP9000/425t. [tsutsui 20201223] openresolv: Update to version 3.12.0 [roy 20201227] nvmm: implement support for trapping REP CMPS [reinoud 20201227] + resize: Import Xterm's resize(1) for querying (x)terminal sizes in + base for headless clients [reinoud 20201227] Index: src/external/mit/xorg/bin/xterm/Makefile diff -u src/external/mit/xorg/bin/xterm/Makefile:1.18 src/external/mit/xorg/bin/xterm/Makefile:1.19 --- src/external/mit/xorg/bin/xterm/Makefile:1.18 Fri Oct 2 13:08:07 2020 +++ src/external/mit/xorg/bin/xterm/Makefile Sun Dec 27 21:13:18 2020 @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.18 2020/10/02 13:08:07 nia Exp $ +# $NetBSD: Makefile,v 1.19 2020/12/27 21:13:18 reinoud Exp $ .include <bsd.own.mk> @@ -38,7 +38,7 @@ DPADD+= ${LIBXAW} ${LIBXMU} ${LIBXT} ${L LDADD+= -lXpm -lXext -lX11 -lcurses -lterminfo -lutil DPADD+= ${LIBXPM} ${LIBXEXT} ${LIBX11} ${LIBCURSES} ${LIBTERMINFO} ${LIBUTIL} -SUBDIR= resize uxterm +SUBDIR= uxterm .PATH: ${X11SRCDIR.${PROG}} Added files: Index: src/usr.bin/resize/Makefile diff -u /dev/null src/usr.bin/resize/Makefile:1.1 --- /dev/null Sun Dec 27 21:13:18 2020 +++ src/usr.bin/resize/Makefile Sun Dec 27 21:13:18 2020 @@ -0,0 +1,7 @@ +# $NetBSD: Makefile,v 1.1 2020/12/27 21:13:18 reinoud Exp $ + +WARNS= 3 +PROG= resize +SRCS= resize.c xstrings.c + +.include <bsd.prog.mk> Index: src/usr.bin/resize/resize.1 diff -u /dev/null src/usr.bin/resize/resize.1:1.1 --- /dev/null Sun Dec 27 21:13:18 2020 +++ src/usr.bin/resize/resize.1 Sun Dec 27 21:13:18 2020 @@ -0,0 +1,219 @@ +.\" $XTermId: resize.man,v 1.32 2016/09/24 11:14:15 tom Exp $ +.\" +.\" Copyright 1998-2013,2016 by Thomas E. Dickey +.\" +.\" All Rights Reserved +.\" +.\" Permission is hereby granted, free of charge, to any person obtaining a +.\" copy of this software and associated documentation files (the +.\" "Software"), to deal in the Software without restriction, including +.\" without limitation the rights to use, copy, modify, merge, publish, +.\" distribute, sublicense, and/or sell copies of the Software, and to +.\" permit persons to whom the Software is furnished to do so, subject to +.\" the following conditions: +.\" +.\" The above copyright notice and this permission notice shall be included +.\" in all copies or substantial portions of the Software. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +.\" OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +.\" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +.\" IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY +.\" CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +.\" TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +.\" SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +.\" +.\" Except as contained in this notice, the name(s) of the above copyright +.\" holders shall not be used in advertising or otherwise to promote the +.\" sale, use or other dealings in this Software without prior written +.\" authorization. +.\" +.\" updated by Thomas E. Dickey for XFree86, 1998-2006. +.\" +.ds N Resize +.ds n resize +.\" +.\" Bulleted paragraph +.de bP +.IP \(bu 4 +.. +.\" Escape single quotes in literal strings from groff's Unicode transform. +.ie \n(.g .ds AQ \(aq +.el .ds AQ ' +.ie \n(.g .ds `` \(lq +.el .ds `` `` +.ie \n(.g .ds '' \(rq +.el .ds '' '' +.TH RESIZE 1 "__app_date__" "__app_version__" "X Window System" +.SH NAME +resize \- set environment and terminal settings to current xterm window size +.SH SYNOPSIS +.B \*n +[ \fB\-v\fP | \fB\-u\fP | \fB\-c\fP ] [ \fB\-s\fP [ \fIrow col\fP ] ] +.SH DESCRIPTION +.I \*N +prints a shell command for setting the appropriate environment variables +to indicate the current size of \fIxterm\fP window from which the command +is run. +.PP +.I \*N +determines the command through several steps: +.bP +first, it finds the name of the user's shell program. +It uses the \fBSHELL\fP variable if set, +otherwise it uses the user's data from /etc/passwd. +.bP +then it decides whether to use Bourne shell syntax or C-Shell syntax. +It uses a built-in table of known shells, +which can be overridden by the \fB\-u\fP and \fB\-c\fP options. +.bP +then \fI\*n\fP asks the operating system for the terminal settings. +This is the same information which can be manipulated using \fIstty\fP. +.bP +then \fI\*n\fP asks the terminal for its size in characters. +Depending on whether the "\fB\-s\fP option is given, +\fI\*n\fP uses a different escape sequence to ask for this information. +.bP +at this point, \fI\*n\fP attempts to update the terminal settings +to reflect the terminal window's size in pixels: +.RS +.bP +if the \fB\-s\fP option is used, +\fI\*n\fP then asks the terminal for its size in pixels. +.bP +otherwise, +\fI\*n\fP asks the operating system for the information +and updates that after ensuring that the window's dimensions are +a multiple of the character height and width. +.bP +in either case, the updated terminal settings are done +using a different system call than used for \fIstty\fP. +.RE +.bP +then \fI\*n\fP updates the terminal settings to reflect any altered +values such as its size in rows or columns. +This affects the values shown by \fIstty\fP. +.bP +finally, \fI\*n\fP writes the shell command for setting the +environment variables to the standard output. +.SH EXAMPLES +For \*n's output to take effect, +\fI\*n\fP must either be evaluated +as part of the command line (usually done with a shell alias or function) or +else redirected to a file which can then be read in. +From the C shell (usually +known as \fI/bin/csh\fP), the following alias could be defined in the +user's \fI.cshrc\fP: +.sp +.nf + % alias rs \*(AQset noglob; eval \fC\`\fP\*n\fC\`\fP\*(AQ +.fi +.sp +After resizing the window, the user would type: +.sp +.nf + % rs +.fi +.sp +Users of versions of the Bourne shell (usually known as \fI/bin/sh\fP) that +don't have command +functions will need to send the output to a temporary file and then read it back +in with the \*(``.\*('' command: +.sp +.nf + $ \*n > /tmp/out + $ .\0/tmp/out +.fi +.SH OPTIONS +The following options may be used with \fI\*n\fP: +.TP 8 +.B \-c +This option indicates that C shell commands should be generated even if the +user's current shell is not \fI/bin/csh\fP. +.TP 8 +.B \-s \fR[\fIrows columns\fP] +This option indicates that Sun console escape sequences will be used +instead of the VT100-style \fIxterm\fP escape codes. +If \fIrows\fP and +\fIcolumns\fP are given, +\fI\*n\fP will ask the \fIxterm\fP to resize itself using those values. +.IP +Both of the escape sequences used for this option +(first to obtain the window size and +second to modify it) +are subject to \fIxterm\fP's \fBallowWindowOps\fP resource setting. +The window manager may also choose to disallow the change. +.IP +The VT100-style escape sequence used to determine the +screen size always works for VT100-compatible terminals. +VT100s have no corresponding way to modify the screensize. +.TP 8 +.B \-u +This option indicates that Bourne shell commands should be generated even if +the user's current shell is not \fI/bin/sh\fP. +.TP 8 +.B \-v +This causes \fI\*n\fP to print a version number to the standard output, +and then exit. +.PP +Note that the Sun console escape sequences are recognized +by XFree86 \fIxterm\fP and +by \fIdtterm\fP. +The \fI\*n\fP program may be installed as \fIsunsize\fP, +which causes makes it assume the \fB\-s\fP option. +.PP +The \fIrows\fP and +\fIcolumns\fP arguments must appear last; though they are normally +associated with the \fB\-s\fP option, they are parsed separately. +.SH FILES +.TP 15 +/etc/termcap +for the base termcap entry to modify. +.TP 15 +~/.cshrc +user's alias for the command. +.SH ENVIRONMENT +.TP 15 +SHELL +.I \*N +determines the user's current shell by first checking if \fB$SHELL\fP +is set, and using that. +Otherwise it determines the user's shell by looking in the password file +(/etc/passwd). +Generally Bourne-shell variants (including \fIksh\fP) +do not modify \fB$SHELL\fP, +so it is possible for \fI\*n\fP to be confused if one runs +\fI\*n\fP from a Bourne shell spawned from a C shell. +.TP 15 +TERM +.I \*N +sets this to "__default_termname__" if not already set. +.TP 15 +TERMCAP +.I \*N +sets this variable on systems using termcap, +e.g., when \*n is linked with the \fItermcap\fP library +rather than a \fIterminfo\fP library. +The latter does not provide the complete text for a termcap entry. +.TP 15 +COLUMNS, LINES +.I \*N +sets these variables on systems using terminfo. +Many applications (including the curses library) +use those variables when set to override their screensize. +.SH "SEE ALSO" +use_env(3x) +.br +csh(1), stty(1), tset(1) +.br +xterm(__mansuffix__) +.SH AUTHORS +Mark Vandevoorde (MIT-Athena), Edward Moy (Berkeley) +.br +Thomas Dickey (invisible-island.net). +.br +Copyright (c) 1984, 1985 by X Consortium +.br +See +.IR X (__miscmansuffix__) +for a complete copyright notice. Index: src/usr.bin/resize/resize.c diff -u /dev/null src/usr.bin/resize/resize.c:1.1 --- /dev/null Sun Dec 27 21:13:18 2020 +++ src/usr.bin/resize/resize.c Sun Dec 27 21:13:18 2020 @@ -0,0 +1,388 @@ +/* $NetBSD: resize.c,v 1.1 2020/12/27 21:13:18 reinoud Exp $ */ +/* $XTermId: resize.c,v 1.139 2017/05/31 08:58:56 tom Exp $ */ + +/* + * Copyright 2003-2015,2017 by Thomas E. Dickey + * + * All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the name(s) of the above copyright + * holders shall not be used in advertising or otherwise to promote the + * sale, use or other dealings in this Software without prior written + * authorization. + * + * + * Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. + * + * All Rights Reserved + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of Digital Equipment + * Corporation not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior permission. + * + * + * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING + * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL + * DIGITAL BE LIABLE FOR ANY SPECIAL, 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. + */ + +/* + * Extracted version from Xterm tailored for NetBSD + */ +/* resize.c */ + +#include <stdio.h> +#include <ctype.h> +#include <stdlib.h> +#include <sys/ioctl.h> +#include <sys/time.h> +#include <errno.h> +#include <unistd.h> +#include <strings.h> +#include <libgen.h> +#include <termios.h> +#include "xstrings.h" + + +/* imported from origional <xterm.h> */ +#define DFT_TERMTYPE "xterm" +#define UIntClr(dst,bits) dst = dst & (unsigned) ~(bits) + +#include <signal.h> +#include <pwd.h> + +#define ESCAPE(string) "\033" string + +#define EMULATIONS 2 +#define SUN 1 +#define VT100 0 + +#define TIMEOUT 10 + +#define SHELL_UNKNOWN 0 +#define SHELL_C 1 +#define SHELL_BOURNE 2 +/* *INDENT-OFF* */ +static struct { + const char *name; + int type; +} shell_list[] = { + { "csh", SHELL_C }, /* vanilla cshell */ + { "jcsh", SHELL_C }, + { "tcsh", SHELL_C }, + { "sh", SHELL_BOURNE }, /* vanilla Bourne shell */ + { "ash", SHELL_BOURNE }, + { "bash", SHELL_BOURNE }, /* GNU Bourne again shell */ + { "dash", SHELL_BOURNE }, + { "jsh", SHELL_BOURNE }, + { "ksh", SHELL_BOURNE }, /* Korn shell (from AT&T toolchest) */ + { "ksh-i", SHELL_BOURNE }, /* another name for Korn shell */ + { "ksh93", SHELL_BOURNE }, /* Korn shell */ + { "mksh", SHELL_BOURNE }, + { "pdksh", SHELL_BOURNE }, + { "zsh", SHELL_BOURNE }, + { NULL, SHELL_BOURNE } /* default (same as xterm's) */ +}; +/* *INDENT-ON* */ + +static const char *const emuname[EMULATIONS] = +{ + "VT100", + "Sun", +}; +static char *myname; +static int shell_type = SHELL_UNKNOWN; +static const char *const getsize[EMULATIONS] = +{ + ESCAPE("7") ESCAPE("[r") ESCAPE("[9999;9999H") ESCAPE("[6n"), + ESCAPE("[18t"), +}; +static const char *const restore[EMULATIONS] = +{ + ESCAPE("8"), + 0, +}; +static const char *const setsize[EMULATIONS] = +{ + 0, + ESCAPE("[8;%s;%st"), +}; + +static struct termios tioorig; + +static const char *const size[EMULATIONS] = +{ + ESCAPE("[%d;%dR"), + ESCAPE("[8;%d;%dt"), +}; +static const char sunname[] = "sunsize"; +static int tty; +static FILE *ttyfp; + + +static void +failed(const char *s) +{ + int save = errno; + IGNORE_RC(write(2, myname, strlen(myname))); + IGNORE_RC(write(2, ": ", (size_t) 2)); + errno = save; + perror(s); + exit(EXIT_FAILURE); +} + +/* ARGSUSED */ +static void +onintr(int sig GCC_UNUSED) +{ + (void) tcsetattr(tty, TCSADRAIN, &tioorig); + exit(EXIT_FAILURE); +} + +static void +resize_timeout(int sig) +{ + fprintf(stderr, "\n%s: Time out occurred\r\n", myname); + onintr(sig); +} + +static void +Usage(void) +{ + fprintf(stderr, strcmp(myname, sunname) == 0 ? + "Usage: %s [rows cols]\n" : + "Usage: %s [-v] [-u] [-c] [-s [rows cols]]\n", myname); + exit(EXIT_FAILURE); +} + + +static int +checkdigits(char *str) +{ + while (*str) { + if (!isdigit(CharOf(*str))) + return (0); + str++; + } + return (1); +} + +static void +readstring(FILE *fp, char *buf, const char *str) +{ + int last, c; + struct itimerval it; + + signal(SIGALRM, resize_timeout); + memset((char *) &it, 0, sizeof(struct itimerval)); + it.it_value.tv_sec = TIMEOUT; + setitimer(ITIMER_REAL, &it, (struct itimerval *) NULL); + if ((c = getc(fp)) == 0233) { /* meta-escape, CSI */ + c = ESCAPE("")[0]; + *buf++ = (char) c; + *buf++ = '['; + } else { + *buf++ = (char) c; + } + if (c != *str) { + fprintf(stderr, "%s: unknown character, exiting.\r\n", myname); + onintr(0); + } + last = str[strlen(str) - 1]; + while ((*buf++ = (char) getc(fp)) != last) { + ; + } + memset((char *) &it, 0, sizeof(struct itimerval)); + setitimer(ITIMER_REAL, &it, (struct itimerval *) NULL); + *buf = 0; +} + +/* + resets termcap string to reflect current screen size + */ +int +main(int argc, char **argv ENVP_ARG) +{ + char *ptr; + int emu = VT100; + char *shell; + int i; + int rc; + int rows, cols; + struct termios tio; + char buf[BUFSIZ]; + char *name_of_tty; + const char *setname = ""; + + myname = x_basename(argv[0]); + if (strcmp(myname, sunname) == 0) + emu = SUN; + for (argv++, argc--; argc > 0 && **argv == '-'; argv++, argc--) { + switch ((*argv)[1]) { + case 's': /* Sun emulation */ + if (emu == SUN) + Usage(); /* Never returns */ + emu = SUN; + break; + case 'u': /* Bourne (Unix) shell */ + shell_type = SHELL_BOURNE; + break; + case 'c': /* C shell */ + shell_type = SHELL_C; + break; + case 'v': + printf("Xterm(330)\n"); + exit(EXIT_SUCCESS); + default: + Usage(); /* Never returns */ + } + } + + if (SHELL_UNKNOWN == shell_type) { + /* Find out what kind of shell this user is running. + * This is the same algorithm that xterm uses. + */ + if ((ptr = x_getenv("SHELL")) == NULL) { + uid_t uid = getuid(); + struct passwd pw; + + if (x_getpwuid(uid, &pw)) { + (void) x_getlogin(uid, &pw); + } + if (!OkPasswd(&pw) + || *(ptr = pw.pw_shell) == 0) { + /* this is the same default that xterm uses */ + ptr = x_strdup("/bin/sh"); + } + } + + shell = x_basename(ptr); + + /* now that we know, what kind is it? */ + for (i = 0; shell_list[i].name; i++) { + if (!strcmp(shell_list[i].name, shell)) { + break; + } + } + shell_type = shell_list[i].type; + } + + if (argc == 2) { + if (!setsize[emu]) { + fprintf(stderr, + "%s: Can't set window size under %s emulation\n", + myname, emuname[emu]); + exit(EXIT_FAILURE); + } + if (!checkdigits(argv[0]) || !checkdigits(argv[1])) { + Usage(); /* Never returns */ + } + } else if (argc != 0) { + Usage(); /* Never returns */ + } + name_of_tty = x_strdup("/dev/tty"); + + if ((ttyfp = fopen(name_of_tty, "r+")) == NULL) { + fprintf(stderr, "%s: can't open terminal %s\n", + myname, name_of_tty); + exit(EXIT_FAILURE); + } + tty = fileno(ttyfp); + if (x_getenv("TERM") == 0) { + if (SHELL_BOURNE == shell_type) { + setname = "TERM=" DFT_TERMTYPE ";\nexport TERM;\n"; + } else { + setname = "setenv TERM " DFT_TERMTYPE ";\n"; + } + } + + rc = tcgetattr(tty, &tioorig); + tio = tioorig; + UIntClr(tio.c_iflag, ICRNL); + UIntClr(tio.c_lflag, (ICANON | ECHO)); + tio.c_cflag |= CS8; + tio.c_cc[VMIN] = 6; + tio.c_cc[VTIME] = 1; + if (rc != 0) + failed("get tty settings"); + + signal(SIGINT, onintr); + signal(SIGQUIT, onintr); + signal(SIGTERM, onintr); + + rc = tcsetattr(tty, TCSADRAIN, &tio); + if (rc != 0) + failed("set tty settings"); + + if (argc == 2) { /* look for optional parameters of "-s" */ + char *tmpbuf = TypeMallocN(char, + strlen(setsize[emu]) + + strlen(argv[0]) + + strlen(argv[1]) + + 1); + if (tmpbuf == 0) { + fprintf(stderr, "%s: Cannot query size\n", myname); + onintr(0); + } else { + sprintf(tmpbuf, setsize[emu], argv[0], argv[1]); + IGNORE_RC(write(tty, tmpbuf, strlen(tmpbuf))); + free(tmpbuf); + } + } + IGNORE_RC(write(tty, getsize[emu], strlen(getsize[emu]))); + readstring(ttyfp, buf, size[emu]); + if (sscanf(buf, size[emu], &rows, &cols) != 2) { + fprintf(stderr, "%s: Can't get rows and columns\r\n", myname); + onintr(0); + } + if (restore[emu]) + IGNORE_RC(write(tty, restore[emu], strlen(restore[emu]))); + + rc = tcsetattr(tty, TCSADRAIN, &tioorig); + if (rc != 0) + failed("set tty settings"); + + signal(SIGINT, SIG_DFL); + signal(SIGQUIT, SIG_DFL); + signal(SIGTERM, SIG_DFL); + + + if (SHELL_BOURNE == shell_type) { + + printf("%sCOLUMNS=%d;\nLINES=%d;\nexport COLUMNS LINES;\n", + setname, cols, rows); + + } else { /* not Bourne shell */ + + printf("set noglob;\n%ssetenv COLUMNS '%d';\nsetenv LINES '%d';\nunset noglob;\n", + setname, cols, rows); + } + exit(EXIT_SUCCESS); +} Index: src/usr.bin/resize/xstrings.c diff -u /dev/null src/usr.bin/resize/xstrings.c:1.1 --- /dev/null Sun Dec 27 21:13:18 2020 +++ src/usr.bin/resize/xstrings.c Sun Dec 27 21:13:18 2020 @@ -0,0 +1,580 @@ +/* $NetBSD: xstrings.c,v 1.1 2020/12/27 21:13:18 reinoud Exp $ */ +/* $XTermId: xstrings.c,v 1.70 2017/06/11 21:20:37 tom Exp $ */ + +/* + * Copyright 2000-2016,2017 by Thomas E. Dickey + * + * All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the name(s) of the above copyright + * holders shall not be used in advertising or otherwise to promote the + * sale, use or other dealings in this Software without prior written + * authorization. + */ + + +#include <sys/types.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> + +#include <stdlib.h> +#include <unistd.h> +#include "xstrings.h" + +static void +alloc_pw(struct passwd *target, struct passwd *source) +{ + *target = *source; + /* we care only about these strings */ + target->pw_dir = x_strdup(source->pw_dir); + target->pw_name = x_strdup(source->pw_name); + target->pw_shell = x_strdup(source->pw_shell); +} + +static void +free_pw(struct passwd *source) +{ + free(source->pw_dir); + free(source->pw_name); + free(source->pw_shell); +} + +void +x_appendargv(char **target, char **source) +{ + if (target && source) { + target += x_countargv(target); + while ((*target++ = *source++) != 0) ; + } +} + +char * +x_basename(char *name) +{ + char *cp; + + cp = strrchr(name, '/'); + return (cp ? cp + 1 : name); +} + +unsigned +x_countargv(char **argv) +{ + unsigned result = 0; + if (argv) { + while (*argv++) { + ++result; + } + } + return result; +} + +/* + * Decode a hexadecimal string, returning the decoded string. + * On return, 'next' points to the first character not part of the input. + * The caller must free the result. + */ +char * +x_decode_hex(const char *source, const char **next) +{ + char *result = 0; + int pass; + size_t j, k; + + for (pass = 0; pass < 2; ++pass) { + for (j = k = 0; isxdigit(CharOf(source[j])); ++j) { + if ((pass != 0) && (j & 1) != 0) { + result[k++] = (char) ((x_hex2int(source[j - 1]) << 4) + | x_hex2int(source[j])); + } + } + *next = (source + j); + if ((j & 1) == 0) { + if (pass) { + result[k] = '\0'; + } else { + result = malloc(++j); + if (result == 0) + break; /* not enough memory */ + } + } else { + break; /* must have an even number of digits */ + } + } + return result; +} + +/* + * Encode a string into hexadecimal, returning the encoded string. + * The caller must free the result. + */ +char * +x_encode_hex(const char *source) +{ + size_t need = (strlen(source) * 2) + 1; + char *result = malloc(need); + + if (result != 0) { + unsigned j, k; + for (j = k = 0; source[j] != '\0'; ++j) { + sprintf(result + k, "%02X", CharOf(source[j])); + k += 2; + } + } + return result; +} + +char * +x_getenv(const char *name) +{ + char *result; + result = x_strdup(x_nonempty(getenv(name))); + TRACE2(("getenv(%s) %s\n", name, result)); + return result; +} + +static char * +login_alias(char *login_name, uid_t uid, struct passwd *in_out) +{ + /* + * If the logon-name differs from the value we get by looking in the + * password file, check if it does correspond to the same uid. If so, + * allow that as an alias for the uid. + */ + if (!IsEmpty(login_name) + && strcmp(login_name, in_out->pw_name)) { + struct passwd pw2; + Boolean ok2; + + if ((ok2 = x_getpwnam(login_name, &pw2))) { + uid_t uid2 = pw2.pw_uid; + struct passwd pw3; + Boolean ok3; + + if ((ok3 = x_getpwuid(uid, &pw3)) + && ((uid_t) pw3.pw_uid == uid2)) { + /* use the other passwd-data including shell */ + alloc_pw(in_out, &pw2); + } else { + free(login_name); + login_name = NULL; + } + if (ok2) + free_pw(&pw2); + if (ok3) + free_pw(&pw3); + } + } + return login_name; +} + +/* + * Call this with in_out pointing to data filled in by x_getpwnam() or by + * x_getpwnam(). It finds the user's logon name, if possible. As a side + * effect, it updates in_out to fill in possibly more-relevant data, i.e., + * in case there is more than one alias for the same uid. + */ +char * +x_getlogin(uid_t uid, struct passwd *in_out) +{ + char *login_name = NULL; + + login_name = login_alias(x_getenv("LOGNAME"), uid, in_out); + if (IsEmpty(login_name)) { + free(login_name); + login_name = login_alias(x_getenv("USER"), uid, in_out); + } +#ifdef HAVE_GETLOGIN + /* + * Of course getlogin() will fail if we're started from a window-manager, + * since there's no controlling terminal to fuss with. For that reason, we + * tried first to get something useful from the user's $LOGNAME or $USER + * environment variables. + */ + if (IsEmpty(login_name)) { + TRACE2(("...try getlogin\n")); + free(login_name); + login_name = login_alias(x_strdup(getlogin()), uid, in_out); + } +#endif + + if (IsEmpty(login_name)) { + free(login_name); + login_name = x_strdup(in_out->pw_name); + } + + TRACE2(("x_getloginid ->%s\n", NonNull(login_name))); + return login_name; +} + +/* + * Simpler than getpwnam_r, retrieves the passwd result by name and stores the + * result via the given pointer. On failure, wipes the data to prevent use. + */ +Boolean +x_getpwnam(const char *name, struct passwd *result) +{ + struct passwd *ptr = getpwnam(name); + Boolean code; + + if (ptr != 0 && OkPasswd(ptr)) { + code = True; + alloc_pw(result, ptr); + } else { + code = False; + memset(result, 0, sizeof(*result)); + } + return code; +} + +/* + * Simpler than getpwuid_r, retrieves the passwd result by uid and stores the + * result via the given pointer. On failure, wipes the data to prevent use. + */ +Boolean +x_getpwuid(uid_t uid, struct passwd *result) +{ + struct passwd *ptr = getpwuid((uid_t) uid); + Boolean code; + + if (ptr != 0 && OkPasswd(ptr)) { + code = True; + alloc_pw(result, ptr); + } else { + code = False; + memset(result, 0, sizeof(*result)); + } + TRACE2(("x_getpwuid(%d) %d\n", (int) uid, (int) code)); + return code; +} + +/* + * Decode a single hex "nibble", returning the nibble as 0-15, or -1 on error. + */ +int +x_hex2int(int c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + return -1; +} + +/* + * Check if the given string is nonnull/nonempty. If so, return a pointer + * to the beginning of its content, otherwise return null. + */ +String +x_nonempty(String s) +{ + if (s != 0) { + if (*s == '\0') { + s = 0; + } else { + s = x_skip_blanks(s); + if (*s == '\0') + s = 0; + } + } + return s; +} + +String +x_skip_blanks(String s) +{ + while (IsSpace(CharOf(*s))) + ++s; + return s; +} + +String +x_skip_nonblanks(String s) +{ + while (*s != '\0' && !IsSpace(CharOf(*s))) + ++s; + return s; +} + +static const char * +skip_blanks(const char *s) +{ + while (IsSpace(CharOf(*s))) + ++s; + return s; +} + +/* + * Split a command-string into an argv[]-style array. + */ +char ** +x_splitargs(const char *command) +{ + char **result = 0; + + if (command != 0) { + const char *first = skip_blanks(command); + char *blob = x_strdup(first); + + if (blob != 0) { + int pass; + + for (pass = 0; pass < 2; ++pass) { + int state; + size_t count; + size_t n; + + for (n = count = 0, state = 0; first[n] != '\0'; ++n) { + + switch (state) { + case 0: + if (!IsSpace(CharOf(first[n]))) { + state = 1; + if (pass) + result[count] = blob + n; + ++count; + } else { + blob[n] = '\0'; + } + break; + case 1: + if (IsSpace(CharOf(first[n]))) { + blob[n] = '\0'; + state = 0; + } + break; + } + } + if (!pass) { + result = TypeCallocN(char *, count + 1); + if (!result) { + free(blob); + break; + } + } + } + } + } else { + result = TypeCalloc(char *); + } + return result; +} + +/* + * Free storage allocated by x_splitargs(). + */ +void +x_freeargs(char **argv) +{ + if (argv != 0) { + if (*argv != 0) + free(*argv); + free(argv); + } +} + +int +x_strcasecmp(const char *s1, const char *s2) +{ + size_t len = strlen(s1); + + if (len != strlen(s2)) + return 1; + + return x_strncasecmp(s1, s2, (unsigned) len); +} + +int +x_strncasecmp(const char *s1, const char *s2, unsigned n) +{ + while (n-- != 0) { + char c1 = x_toupper(*s1); + char c2 = x_toupper(*s2); + if (c1 != c2) + return 1; + if (c1 == 0) + break; + s1++, s2++; + } + + return 0; +} + +/* + * Allocates a copy of a string + */ +char * +x_strdup(const char *s) +{ + char *result = 0; + + if (s != 0) { + char *t = TextAlloc(4 + strlen(s)); + if (t != 0) { + strcpy(t, s); + } + result = t; + } + return result; +} + +/* + * Returns a pointer to the first occurrence of s2 in s1, + * or NULL if there are none. + */ +char * +x_strindex(char *s1, const char *s2) +{ + char *s3; + size_t s2len = strlen(s2); + + while ((s3 = (strchr) (s1, *s2)) != NULL) { + if (strncmp(s3, s2, s2len) == 0) + return (s3); + s1 = ++s3; + } + return (NULL); +} + +/* + * Trims leading/trailing spaces from a copy of the string. + */ +char * +x_strtrim(const char *source) +{ + char *result; + + if (source != 0 && *source != '\0') { + char *t = x_strdup(source); + if (t != 0) { + char *s = t; + char *d = s; + while (IsSpace(CharOf(*s))) + ++s; + while ((*d++ = *s++) != '\0') { + ; + } + if (*t != '\0') { + s = t + strlen(t); + while (s != t && IsSpace(CharOf(s[-1]))) { + *--s = '\0'; + } + } + } + result = t; + } else { + result = x_strdup(""); + } + return result; +} + +/* + * Trims trailing whitespace from a copy of the string. + */ +char * +x_strrtrim(const char *source) +{ + char *result; + + if (source != 0 && *source != '\0') { + char *t = x_strdup(source); + if (t != 0) { + if (*t != '\0') { + char *s = t + strlen(t); + while (s != t && IsSpace(CharOf(s[-1]))) { + *--s = '\0'; + } + } + } + result = t; + } else { + result = x_strdup(""); + } + return result; +} + +/* + * Avoid using system locale for upper/lowercase conversion, since there are + * a few locales where toupper(tolower(c)) != c. + */ +char +x_toupper(int ch) +{ + static char table[256]; + char result = table[CharOf(ch)]; + + if (result == '\0') { + unsigned n; + static const char s[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + + for (n = 0; n < sizeof(table); ++n) { + table[n] = (char) n; + } + for (n = 0; s[n] != '\0'; ++n) { + table[CharOf(s[n])] = s[n % 26]; + } + result = table[CharOf(ch)]; + } + + return result; +} + +/* + * Match strings ignoring case and allowing glob-like '*' and '?' + */ +int +x_wildstrcmp(const char *pattern, const char *actual) +{ + int result = 0; + + while (*pattern && *actual) { + char c1 = x_toupper(*pattern); + char c2 = x_toupper(*actual); + + if (c1 == '*') { + Boolean found = False; + pattern++; + while (*actual != '\0') { + if (!x_wildstrcmp(pattern, actual++)) { + found = True; + break; + } + } + if (!found) { + result = 1; + break; + } + } else if (c1 == '?') { + ++pattern; + ++actual; + } else if ((result = (c1 != c2)) == 0) { + ++pattern; + ++actual; + } else { + break; + } + } + return result; +} Index: src/usr.bin/resize/xstrings.h diff -u /dev/null src/usr.bin/resize/xstrings.h:1.1 --- /dev/null Sun Dec 27 21:13:18 2020 +++ src/usr.bin/resize/xstrings.h Sun Dec 27 21:13:18 2020 @@ -0,0 +1,87 @@ +/* $NetBSD: xstrings.h,v 1.1 2020/12/27 21:13:18 reinoud Exp $ */ +/* $XTermId: xstrings.h,v 1.30 2016/12/22 23:48:38 tom Exp $ */ + +/* + * Copyright 2000-2015,2016 by Thomas E. Dickey + * + * All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the name(s) of the above copyright + * holders shall not be used in advertising or otherwise to promote the + * sale, use or other dealings in this Software without prior written + * authorization. + */ + +#ifndef included_xstrings_h +#define included_xstrings_h 1 +/* *INDENT-OFF* */ + +#include <pwd.h> + +#include <stdbool.h> +#define Boolean bool +#define True true +#define False false + +#define String char * +#define ENVP_ARG +#define GCC_UNUSED +#define IGNORE_RC(a) (void) a +#define TRACE2(a) + +#define TextAlloc(l) (char *) malloc((l)) +#define CharOf(a) ((int)(a)) +#define IsSpace(a) isspace((a)) +#define IsEmpty(s) ((s) == 0 || *(s) == '\0') +#define TypeCallocN(type,n) (type *)calloc((size_t) (n), sizeof(type)) +#define TypeCalloc(type) TypeCallocN(type, 1) +#define TypeMallocN(type,n) TypeCallocN(type, n) + +#define OkPasswd(p) ((p)->pw_name != 0 && (p)->pw_name[0] != 0) + +extern Boolean x_getpwnam(const char * /* name */, struct passwd * /* result */); +extern Boolean x_getpwuid(uid_t /* uid */, struct passwd * /* result */); +extern String x_nonempty(String /* s */); +extern String x_skip_blanks(String /* s */); +extern String x_skip_nonblanks(String /* s */); +extern char **x_splitargs(const char * /* command */); +extern char *x_basename(char * /* name */); +extern char *x_decode_hex(const char * /* source */, const char ** /* next */); +extern char *x_encode_hex(const char * /* source */); +extern char *x_getenv(const char * /* name */); +extern char *x_getlogin(uid_t /* uid */, struct passwd * /* in_out */); +extern char *x_strdup(const char * /* s */); +extern char *x_strindex(char * /* s1 */, const char * /* s2 */); +extern char *x_strtrim(const char * /* s */); +extern char *x_strrtrim(const char * /* s */); +extern char x_toupper(int /* ch */); +extern int x_hex2int(int /* ch */); +extern int x_strcasecmp(const char * /* s1 */, const char * /* s2 */); +extern int x_strncasecmp(const char * /* s1 */, const char * /* s2 */, unsigned /* n */); +extern int x_wildstrcmp(const char * /* pattern */, const char * /* actual */); +extern unsigned x_countargv(char ** /* argv */); +extern void x_appendargv(char ** /* target */, char ** /* source */); +extern void x_freeargs(char ** /* argv */); + +/* *INDENT-ON* */ + +#endif /* included_xstrings_h */