[net.sources] C strings library

ok@edai.UUCP (Richard O'Keefe) (06/25/84)

What is this first-line bug anyway?

This second posting of my strings library includes versions
of the V5 mem* functions, and also the strx* functions altered
slightly to be more compatible with the standard functions.
A couple more functions have Vax assembly code versions.  And
there are some functions for reversing things.

I haven't received any bug reports, now I *can't* be that good...

This library is in shar format.  A copy of my version of "shar"
is included.  Tear at the dotted lines and feed the result into
sh in a new directory.

........................................................................
#!/bin/sh
cat >Makefile <<'------ EOF ------'
#   File   : strings.d/Makefile
#   Author : Richard A. O'Keefe.
#   Updated: 2 June 1984.
#   Purpose: UNIX make(1)file for the strings library.

#   If you are not using a Vax, or if your strings might be 2^16
#   characters long or longer, use
#   CFLAGS=-O
#   On the Vax we can use the string instructions some but not all the time.
CFLAGS=-O -DVaxAsm

#   The SIII functions are the ones described in the System III
#   string(3) manual page, and also in ctype(3), atoi(3).

SIII=strcat.o strncat.o strcmp.o strncmp.o strcpy.o strncpy.o strlen.o\
	strchr.o strrchr.o strpbrk.o strspn.o strcspn.o strtok.o\
	_c2type.o str2int.o getopt.o

#   The Sys5 functions are the ones described in the System V
#   memory(3C) manual page.  mem{mov,rchr,rev} are in "mine".

Sys5=memccpy.o memchr.o memcmp.o memcpy.o memmov.o memrchr.o memset.o

#   The BSD2 functions are the ones described in the 4.2bsd
#   bstring(3) manual page, plus a couple of my additions.
#   All except ffs have VAX-specific machine code versions.

BSD2=bcmp.o bcopy.o bfill.o bmove.o bzero.o ffs.o

#   The "xstr" functions are Tony Hansen's "xstring(3c)" package with
#   some additions of mine.  All the code is mine, the names are his.
#   It is not clear whether his strxncpy pads with NULs as strncpy does.
#   In this package str[x]n{cpy,mov} all pad to exactly len chars with NUL.

xstr=strxcat.o strxcpy.o strxmov.o strxncat.o strxncpy.o strxnmov.o

#   The "mine" functions are the ones which are entirely my own
#   invention, though they are supposed to fit into the SIII conventions.

mine=strmov.o strnmov.o strrpt.o strnrpt.o strend.o strnlen.o strcpbrk.o\
	strpack.o strcpack.o strtrans.o strntrans.o strpref.o strsuff.o\
	strtrim.o strctrim.o strfield.o strkey.o int2str.o substr.o\
	strnend.o strconc.o strrev.o strnrev.o _str2map.o _str2set.o\
	memmov.o memrchr.o memrev.o

#   The "find" functions are my code, but they are based on published
#   work by Boyer, Moore, and Hospool.  (See _str2pat.c.)

find=strfind.o strrepl.o

strings.a: ${SIII} ${Sys5} ${BSD2} ${xstr} ${mine} ${find}
	rm strings.a; ar rc strings.a *.o; ranlib strings.a

scan=strpbrk.o strcprbk.o strspn.o strcspn.o strpack.o strcpack.o \
	strtrim.o strctrim.o strtok.o

${scan} _str2set.o: _str2set.h

tran=strtrans.o strntrans.o

${tran} _str2map.o: _str2map.h

${find}: _str2pat.h

str2int.o: ctypes.h

${SIII} ${Sys5} ${BSD2} ${mine} ${xstr} ${find}: strings.h

clean:
	-rm *.o

#   The compilations should be done with the sources and headers in the
#   same directory.  However, users should find everything in the proper
#   places: /usr/include/{strings,memory}.h and /usr/lib/strings.a
#   /usr/local/lib would be ok.  Why is there no /usr/local/include?

install:
	cp memory.h strings.h /usr/include
	mv strings.a /usr/lib
------ EOF ------
ls -l Makefile
cat >READ-ME <<'------ EOF ------'
File   : READ-ME
Author : Richard A. O'Keefe.
Updated: 1 June 1984.
Purpose: Explain the new strings package.

    The UNIX string libraries (described in the string(3) manual page)
differ from UNIX to UNIX (e.g. strtok is not in V7 or 4.1bsd).  Worse,
the sources are not in the public domain, so that if there is a string
routine which is nearly what you want but not quite you can't  take  a
copy  and  modify it.  And of course C programmers on non-UNIX systems
are at the mercy of their supplier.

    This package was designed to let me do reasonable things with  C's
strings  whatever UNIX (V7, PaNiX, UX63, 4.1bsd) I happen to be using.
Everything in the System III manual is here and does just what the  S3
manual  says  it does.  There are also lots of new goodies.  I'm sorry
about the names, but the routines do have to work  on  asphyxiated-at-
birth  systems  which  truncate identifiers.  The convention is that a
routine is called
	str [n] [c] <operation>
If there is an "n", it means that the function takes an (int) "length"
argument, which bounds the number of characters to be moved or  looked
at.  If the function has a "set" argument, a "c" in the name indicates
that  the complement of the set is used.  Functions or variables whose
names start with _ are support routines which aren't really meant  for
general  use.  I don't know what the "p" is doing in "strpbrk", but it
is there in the S3 manual so it's here too.  "istrtok" does not follow
this rule, but with 7 letters what can you do?

    I have included new versions of atoi(3) and atol(3) as well.  They
use a new primitive str2int, which takes a pair of bounds and a radix,
and does much more thorough checking than the normal atoi and atol do.
The result returned by atoi & atol is valid if and only if errno == 0.
There is also an output conversion routine int2str, with itoa and ltoa
as interface macros.  Only after writing int2str did I notice that the
str2int routine has no provision for unsigned numbers.  On reflection,
I don't greatly care.   I'm afraid that int2str may depend on your "C"
compiler in unexpected ways.  Do check the code with -S.

    Several of these routines have "asm" inclusions conditional on the
VaxAsm option.  These insertions can make the routines which have them
quite a bit faster, but there is a snag.  The VAX architects, for some
reason best known to themselves and their therapists, decided that all
"strings" were shorter than 2^16 bytes.  Even when the length operands
are in 32-bit registers, only 16 bits count.  So the "asm" versions do
not work for long strings.  If you can guarantee that all your strings
will be short, define VaxAsm in the makefile, but in general, and when
using other machines, do not define it.

    Thanks to someone on the net who saw the first posting of strings,
and sent me a formatted copy of the System V memory(3C) manual page, I
have been able to include versions of these routines.   The convention
is that they are called
	mem{operation}([dst,] ... , len)
where operation is cpy, cmp, chr, and so on, and len is how many bytes
to move or test.  Note that this is different from the strn functions,
	str{operation}	-- stop when you find a NUL character
	strn{operation}	-- stop when len is exhausted or you find NUL
	mem{operation}	-- stop when len is exhausted
	b{operation}	-- stop when len is exhausted
but the b family has different argument orders or different results or
both.  In particular, note that my implementation of bcmp does conform
to the letter of the 4.2bsd manual page, but I decided to make it give
a value I have often wanted, which is not like the value of strcmp. As
the System V manual page is more explicit about the return code memcmp
DOES return a value like strcmp, so you may prefer to use it.  BEWARE:
the "c" in the name mem-c-cpy doesn't mean what it does in the System3
names, it's more like mem-chr-cpy.

    To use this library, you need the "strings.a" library file and the
"strings.h" header file.  The other header files are for compiling the
library itself, though if you are hacking extensions you may find them
useful.  General users really shouldn't see them.  I've defined a  few
macros  I find useful in "strings.h"; if you have no need for "index",
"rindex", "streql", and "beql", just edit them  out.   On  the  4.1bsd
system  I  am using, having all these functions 'extern' does not mean
that they will all be loaded; only the ones you call are.  When  using
lesser  systems you may find it necessary to break strings.h up or you
could get by with just adding "extern" declarations for  functions  as
you  need  them.   Note  that  as  many  of these functions have names
matching "standard C library" names (by design, this is  after  all  a
replacement/reimplementation  of part of that library) you may have to
talk the loader into loading this library first.  Again, I've found no
problems on 4.1bsd.

    A note on character comparison.  The various UNIX manuals come out
and say explicitly that the *cmp and *chr routines use the computer's
"native" character comparison.  That is, on a PDP-11, VAX-11, and some
other machines, signed character comparison is used, and the byte 0377
will never be located (use -1).   On IBM 370s and many other machines,
unsigned character comparison is used, and the byte -1 can't be found.
(Use 0377.)  If you have occasion to use 8-bit byte values in calls to
*chr functions, it would be nice if the package looked after making it
work portably.  I thought about that, and decided not to do it, as you
might *want* to write VAX code that didn't find 128, and might rely on
the current effect. However, you should be able to use 8-bit values in
a portable fashion if you ask, and that the package DOES do for you.
There is a macro
	int2char(c)
which takes the bottom 8 bits of c on a machine with unsigned character
comparison or sign-extends them on a machine with signed comparison. It
is up to you to use this macro in appropriate places.  It is up to who-
ever installs the package to make sure that the right definition is put
in and the wrong one commented out.

    You may wonder at my failure to provide manual pages for this  code.
For  the things in V7, 4.?, or SIII, you should be able to use whichever
manual page came with that system, and anything I might write  would  be
so  like it as to raise suspicions of violating AT&T copyrights.  In the
sources you will find comments which provide far more documentation  for
these  routines  than AT&T ever provided for their strings stuff, I just
don't happen to have put it in nroff -man form.   Had I done so, the *.3
files would have outbulked the .c files!

    There is a manual page for the strx family of routines.   It was the
work of Tony Hansen, of AT&T Information Systems Lincroft NJ.  It is not
clear whether I should distribute this manual page or not,  but as these
functions are not likely to documented anywhere else I decided to risk
it.  There is no risk in the *code* however.  His posting to net.sources
arrived at Edinburgh with just the reason for reposting, and the manual
page.  The code is my own work based on his manual page.  Indeed, I had
already written strx[n]mov, using different names.

    These files are in the public domain.  This includes getopt.c, which
is the work of Henry Spencer, University of Toronto Zoology, who says of
it "None of this software is derived from Bell software. I had no access
to the source for Bell's versions at the time I wrote it.  This software
is hereby explicitly placed in the public domain.  It may  be  used  for
any purpose on any machine by anyone." I would greatly prefer it if *my*
material received no military use.

------ EOF ------
ls -l READ-ME
cat >_c2type.c <<'------ EOF ------'
/*  File   : _c2type.c
    Author : Richard A. O'Keefe.
    Updated: 23 April 1984
    Purpose: Map character codes to types

    The mapping used here is such that we can use it for converting
    numbers expressed in a variety of radices to binary as well as for
    classifying characters.
*/

char _c2type[129] =
    {	37,			/* EOF == -1 */
	37, 37, 37, 37, 37, 37, 37, 37, 37, 38, 39, 39, 39, 39, 37, 37,
	37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
	38, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,
	00, 01, 02, 03, 04, 05, 06, 07,  8,  9, 36, 36, 36, 36, 36, 36,
	36, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
	25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 36, 36, 36, 36,
	36, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
	25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 36, 36, 36, 36
    };

------ EOF ------
ls -l _c2type.c
cat >_str2map.c <<'------ EOF ------'
/*  File   : _str2map.c
    Author : Richard A. O'Keefe.
    Updated: 20 April 1984
    Defines: _map_vec[], _str2map().

    _str2map(option, from, to) constructs a translation table.  If  from
    or to is NullS, the same string is used as last time, so if you want
    to translate a whole lot of strings using the same mapping you don't
    have to reconstruct it each time.  The options are

	0: initialise the map to the identity function,
	   then map each from[i] to the corresponding to[i].
	   If to[] is shorter than from[], its last character is
	   repeated as often as needed.

	1: as 0, but don't initialise the map.

	2: initialise the map to send every character to to[0],
	   then map each from[i] to itself.

    For example, to build a map which forces letters to lower case but
    sends everything else to blank, call

	_str2map(2, "abcdefghijklmnopqrstuvwxyz", " ");
	_str2map(1, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz");

    Only strtrans() and strntrans() in this package  call  _str2map;  if
    you  want  to  build your own maps this way you can "fool" them into
    using it, as when the two strings are NullS they  don't  change  the
    map.   As an extra-special dubious *hack*, _map_vec has an extra NUL
    character at the end, so after calling _str2map(0, "", ""), you  can
    use  _map_vec+1 as a string of the 127 non-NUL characters (or if the
    _AlphabetSize is 256, of the 255 non-NUL characters).
*/

#include "strings.h"
#include "_str2map.h"

static	_char_	*oldFrom = "?";
static	char	*oldTo   = "?";

char	_map_vec[_AlphabetSize+1];

void _str2map(option, from, to)
    int option;
    register _char_ *from;
    register char *to;
    {
	register int i, c;

	if (from == NullS && to == NullS) return;
	if (from == NullS) from = oldFrom; else oldFrom = from;
	if (to   == NullS) to   = oldTo;   else oldTo   = to;
	switch (option) {
	    case 0:
		for (i = _AlphabetSize; --i >= 0; _map_vec[i] = i) ;
	    case 1:
		while (i = *from++) {
		    _map_vec[i] = *to++;
		    if (!*to) {
			c = *--to;
			while (i = *from++) _map_vec[i] = c;
			return;
		    }
		}
		return;
	    case 2:
		c = *to;
		for (i = _AlphabetSize; --i >= 0; _map_vec[i] = c) ;
		while (c = *from++) _map_vec[c] = c;
		return;
	}
    }

------ EOF ------
ls -l _str2map.c
cat >_str2map.h <<'------ EOF ------'
/*  File   : _str2map.h
    Author : Richard A. O'Keefe.
    Updated: 11 April 1984
    Purpose: Definitions from _str2map.c
*/

extern	char	_map_vec[_AlphabetSize+1];
extern	void	_str2map(/*int,_char_^,char^*/);

------ EOF ------
ls -l _str2map.h
cat >_str2pat.c <<'------ EOF ------'
/*  File   : _str2pat.c
    Author : Richard A. O'Keefe.
    Updated: 2 June 1984
    Defines: _pat_lim, _pat_vec[], _str2pat()

    Searching in this package is done by an algorithm due  to  R.  Nigel
    Hospool,  described  in  Software  Practice & Experience 1980, p505.
    Elsewhere I have a version of it which does  exact  case  or  either
    case  match,  word  more or literal mode, forwards or backwards, and
    will look for the Nth instance.  For most applications that  is  too
    much  and  a  simple  exact  case forward search will do.  Hospool's
    algorithm is a simplification of  the  Boyer-Moore  algorithm  which
    doesn't  guarantee linear time, but in practice is very good indeed.

    _str2pat(pat) builds a search table for the string pat.  As usual in
    this pacakge, if pat == NullS, the table is not changed and the last
    search string is re-used.  To support  this,  _str2pat  returns  the
    actual search string.
*/

#include "strings.h"
#include "_str2pat.h"

int	_pat_lim;
int	_pat_vec[_AlphabetSize];
static	_char_	*oldPat = "";


_char_ *_str2pat(pat)
    register _char_ *pat;
    {
	register int L, i;

	if (pat == NullS) pat = oldPat; else oldPat = pat;
	for (L = 0; *pat++; L++) ;
	for (i = _AlphabetSize; --i >= 0; _pat_vec[i] = L) ;
	for (pat = oldPat, i = L; --i > 0; _pat_vec[*pat++] = i) ;
	_pat_lim = --L;
	return oldPat;
    }

------ EOF ------
ls -l _str2pat.c
cat >_str2pat.h <<'------ EOF ------'
/*  File   : _str2pat.h
    Author : Richard A. O'Keefe.
    Updated: 20 April 1984
    Purpose: Definitions from _str2pat.c
*/

extern	int	_pat_lim;
extern	int	_pat_vec[];
extern	_char_	*_str2pat(/*_char_^*/);

------ EOF ------
ls -l _str2pat.h
cat >_str2set.c <<'------ EOF ------'
/*  File   : _str2set.c
    Author : Richard A. O'Keefe.
    Updated: 20 April 1984
    Defines: _set_ctr, _set_vec[], _str2set().
    Purpose: Convert a character string to a set.
*/

/*  The obvious way of representing a set of characters  is  as  a
    vector  of 0s and 1s.  The snag with that is that to convert a
    string to such a vector, we have to clear all the elements  to
    0,  and  then  set the elements corresponding to characters in
    the string to 1, so the cost is  O(|alphabet|+|string|).  This
    package  uses another method, where there is a vector of small
    numbers and a counter.  A character is in the current  set  if
    and  only  if the corresponding element of the vector is equal
    to the current value of  the  counter.   Every  so  often  the
    vector  elements  would  overflow  and  we  have  to clear the
    vector, but the cost is reduced to O(|string|+1).

    Note that NUL ('\0') will never be in any set built by str2set.

    While this method reduces the cost of building a set, it would
    be useful to avoid it entirely.  So when the "set" argument is
    NullS the set is not changed.  Use NullS to mean "the same set
    as before."  MaxPosChar is the largest integer value which can
    be stored in a "char".  Although we might get a slightly wider
    range by using "unsigned char", "char" may be cheaper (as on a
    PDP-11).  By all means change the number from 127 if your C is
    one of those that treats char as unsigned, but don't change it
    just because _AlphabetSize is 256, the two are unrelated.  And
    don't dare change it on a VAX: it is built into the asm code!
*/

#include "strings.h"
#include "_str2set.h"

#if	CharsAreSigned
#define	MaxPosChar	127
#else  ~CharsAreSigned
#define MaxPosChar	255
#endif	CharsAreSigned

int  _set_ctr = MaxPosChar;
char _set_vec[_AlphabetSize];


void _str2set(set)
    register char *set;
    {
	if (set == NullS) return;
	if (++_set_ctr == MaxPosChar+1) {
#if	VaxAsm
	    asm("movc5 $0,4(ap),$0,$128,__set_vec");
#else  ~VaxAsm
	    register char *w = &_set_vec[_AlphabetSize];
	    do *--w = NUL; while (w != &_set_vec[0]);
#endif	VaxAsm
	    _set_ctr = 1;
	}
	while (*set) _set_vec[*set++] = _set_ctr;
    }

------ EOF ------
ls -l _str2set.c
cat >_str2set.h <<'------ EOF ------'
/*  File   : _str2set.h
    Updated: 10 April 1984
    Purpose: External declarations for strprbk, strspn, strcspn &c
    Copyright (C) 1984 Richard A. O'Keefe.
*/

extern	int	_set_ctr;
extern	char	_set_vec[];
extern	void	_str2set(/*char^*/);

------ EOF ------
ls -l _str2set.h
cat >ascii.h <<'------ EOF ------'
/*  File   : strings.d/ascii.h
    Author : Richard A. O'Keefe
    Updated: 28 April 1984
    Purpose: Define Ascii mnemonics.

    This file defines the ASCII control characters.  Note that these
    names refer to their use in communication; it is an ERROR to use
    these names to talk about keyboard commands.  For example, DO NOT
    use EOT when you mean "end of file", as many people prefer ^Z (if
    the Ascii code were taken seriously, EOT would log you off and
    hang up the line as well).  Similarly, DO NOT use DEL when you
    mean "interrupt", many people prefer ^C.  When writing a screen
    editor, you should speak of tocntrl('C') rather than ETX (see the
    header file "ctypes.h").
*/

#define NUL	'\000'	/* null character */
#define SOH	'\001'	/* Start Of Heading, start of message */
#define STX	'\002'	/* Start Of Text, end of address */
#define	ETX	'\003'	/* End of TeXt, end of message */
#define EOT	'\004'	/* End Of Transmission */
#define	ENQ	'\005'	/* ENQuiry "who are you" */
#define ACK	'\006'	/* (positive) ACKnowledge */
#define	BEL	'\007'	/* ring the BELl */
#define	BS	'\010'	/* BackSpace */
#define	HT	'\011'	/* Horizontal Tab */
#define	TAB	'\011'	/* an unofficial name for HT */
#define	LF	'\012'	/* Line Feed (does not imply cr) */
#define	NL	'\012'	/* unix unofficial name for LF: new line */
#define	VT	'\013'	/* Vertical Tab */
#define	FF	'\014'	/* Form Feed (new page starts AFTER this) */
#define	CR	'\015'	/* Carriage Return */
#define	SO	'\016'	/* Shift Out; select alternate character set */
#define	SI	'\017'	/* Shift In; select ASCII again */
#define	DLE	'\020'	/* Data Link Escape */
#define	DC1	'\021'	/* Device Control 1 */
#define XON	'\021'	/* transmitter on, resume output */
#define	DC2	'\022'	/* Device Control 2 (auxiliary on) */
#define	DC3	'\023'	/* Device Control 3 */
#define	XOFF	'\023'	/* transmitter off, suspend output */
#define	DC4	'\024'	/* Device Control 4 (auxiliary off) */
#define	NAK	'\025'	/* Negative AcKnowledge (signal error) */
#define	SYN	'\026'	/* SYNchronous idle */
#define	ETB	'\027'	/* End of Transmission Block, logical end of medium */
#define	CAN	'\030'	/* CANcel */
#define	EM	'\031'	/* End of Medium */
#define	SUB	'\032'	/* SUBstitute */
#define	ESC	'\033'	/* ESCape */
#define	FS	'\034'	/* File Separator */
#define	GS	'\035'	/* Group Separator */
#define	RS	'\036'	/* Record Separator */
#define	US	'\037'	/* Unit Separator */
#define	SP	'\040'	/* SPace */
#define	DEL	'\177'	/* DELete, rubout */

------ EOF ------
ls -l ascii.h
cat >bcmp.c <<'------ EOF ------'
/*  File   : bcmp.c
    Author : Richard A. O'Keefe.
    Updated: 23 April 1984
    Defines: bcmp()

    bcmp(s1, s2, len) returns 0 if the "len" bytes starting at "s1" are
    identical to the "len" bytes starting at "s2", non-zero if they are
    different.   The 4.2bsd manual page doesn't say what non-zero value
    is returned, though the BUGS note says that it takes its parameters
    backwards from strcmp.  This suggests that it is something like
	for (; --len >= 0; s1++, s2++)
	    if (*s1 != *s2) return *s2-*s1;
	return 0;
    There, I've told you how to do it.  As the manual page doesn't come
    out and *say* that this is the result, I tried to figure out what a
    useful result might be.   (I'd forgotten than strncmp stops when it
    hits a NUL, which the above does not do.)  What I came up with was:
    the result is the number of bytes in the differing tails.  That is,
    after you've skipped the equal parts, how many characters are left?
    To put it another way, N-bcmp(s1,s2,N) is the number of equal bytes
    (the size of the common prefix).  After deciding on this definition
    I discovered that the CMPC3 instruction does exactly what I wanted.
    The code assumes that N is non-negative.

    Note: the "b" routines are there to exploit certain VAX order codes,
    but the CMPC3 instruction will only test 65535 characters.   The asm
    code is presented for your interest and amusement.
*/

#include "strings.h"

#if	VaxAsm

int bcmp(s1, s2, len)
    char *s1, *s2;
    int len;
    {
	asm("cmpc3 12(ap),*4(ap),*8(ap)");
    }

#else  ~VaxAsm

int bcmp(s1, s2, len)
    register char *s1, *s2;
    register int len;
    {
	while (--len >= 0 && *s1++ == *s2++) ;
	return len+1;
    }

#endif	VaxAsm

------ EOF ------
ls -l bcmp.c
cat >bcopy.c <<'------ EOF ------'
/*  File   : bcopy.c
    Author : Richard A. O'Keefe.
    Updated: 23 April 1984
    Defines: bcopy()

    bcopy(src, dst, len) moves exactly "len" bytes from the source "src"
    to the destination "dst".  It does not check for NUL characters as
    strncpy() and strnmov() do.  Thus if your C compiler doesn't support
    structure assignment, you can simulate it with
	bcopy(&from, &to, sizeof from);
    BEWARE: the first two arguments are the other way around from almost
    everything else.   I'm sorry about that, but that's the way it is in
    the 4.2bsd manual, though they list it as a bug.  For a version with
    the arguments the right way around, use bmove().
    No value is returned.

    Note: the "b" routines are there to exploit certain VAX order codes,
    but the MOVC3 instruction will only move 65535 characters.   The asm
    code is presented for your interest and amusement.
*/

#include "strings.h"

#if	VaxAsm

void bcopy(src, dst, len)
    char *src, *dst;
    int len;
    {
	asm("movc3 12(ap),*4(ap),*8(ap)");
    }

#else  ~VaxAsm

void bcopy(src, dst, len)
    register char *src, *dst;
    register int len;
    {
	while (--len >= 0) *dst++ = *src++;
    }

#endif	VaxAsm

------ EOF ------
ls -l bcopy.c
cat >bfill.c <<'------ EOF ------'
/*  File   : bfill.c
    Author : Richard A. O'Keefe.
    Updated: 23 April 1984
    Defines: bfill()

    bfill(dst, len, fill) moves "len" fill characters to "dst".
    Thus to set a buffer to 80 spaces, do bfill(buff, 80, ' ').

    Note: the "b" routines are there to exploit certain VAX order codes,
    but the MOVC5 instruction will only move 65535 characters.   The asm
    code is presented for your interest and amusement.
*/

#include "strings.h"

#if	VaxAsm

void bfill(dst, len, fill)
    char *dst;
    int len;
    int fill;	/* actually char */
    {
	asm("movc5 $0,*4(ap),12(ap),8(ap),*4(ap)");
    }

#else  ~VaxAsm

void bfill(dst, len, fill)
    register char *dst;
    register int len;
    register int fill;	/* char */
    {
	while (--len >= 0) *dst++ = fill;

    }

#endif	VaxAsm

------ EOF ------
ls -l bfill.c
cat >bmove.c <<'------ EOF ------'
/*  File   : bmove.c
    Author : Richard A. O'Keefe.
    Updated: 23 April 1984
    Defines: bmove()

    bmove(dst, src, len) moves exactly "len" bytes from the source "src"
    to the destination "dst".  It does not check for NUL characters as
    strncpy() and strnmov() do.  Thus if your C compiler doesn't support
    structure assignment, you can simulate it with
	bmove(&to, &from, sizeof from);
    The standard 4.2bsd routine for this purpose is bcopy.  But as bcopy
    has its first two arguments the other way around you may find this a
    bit easier to get right.
    No value is returned.

    Note: the "b" routines are there to exploit certain VAX order codes,
    but the MOVC3 instruction will only move 65535 characters.   The asm
    code is presented for your interest and amusement.
*/

#include "strings.h"

#if	VaxAsm

void bmove(dst, src, len)
    char *dst, *src;
    int len;
    {
	asm("movc3 12(ap),*8(ap),*4(ap)");
    }

#else  ~VaxAsm

void bmove(dst, src, len)
    register char *dst, *src;
    register int len;
    {
	while (--len >= 0) *dst++ = *src++;
    }

#endif	VaxAsm

------ EOF ------
ls -l bmove.c
cat >bzero.c <<'------ EOF ------'
/*  File   : bzero.c
    Author : Richard A. O'Keefe.
    Updated: 23 April 1984
    Defines: bzero()

    bzero(dst, len) moves "len" 0 bytes to "dst".
    Thus to clear a disc buffer to 0s do bzero(buffer, BUFSIZ).

    Note: the "b" routines are there to exploit certain VAX order codes,
    but the MOVC5 instruction will only move 65535 characters.   The asm
    code is presented for your interest and amusement.
*/

#include "strings.h"

#if	VaxAsm

void bzero(dst, len)
    char *dst;
    int len;
    {
	asm("movc5 $0,*4(ap),$0,8(ap),*4(ap)");
    }

#else  ~VaxAsm

void bzero(dst, len)
    register char *dst;
    register int len;
    {
	while (--len >= 0) *dst++ = 0;
    }

#endif	VaxAsm

------ EOF ------
ls -l bzero.c
cat >ctypes.demo <<'------ EOF ------'
EOF .   .   .   .   .   .   .   .   .   #   .   .

ch  DD? OD? XD? AN? AF? LC? UC? PT? PR? CT? SP? EL?
^@  .   .   .   .   .   .   .   .   .   #   .   .
^A  .   .   .   .   .   .   .   .   .   #   .   .
^B  .   .   .   .   .   .   .   .   .   #   .   .
^C  .   .   .   .   .   .   .   .   .   #   .   .
^D  .   .   .   .   .   .   .   .   .   #   .   .
^E  .   .   .   .   .   .   .   .   .   #   .   .
^F  .   .   .   .   .   .   .   .   .   #   .   .
^G  .   .   .   .   .   .   .   .   .   #   .   .
^H  .   .   .   .   .   .   .   .   .   #   .   .
^I  .   .   .   .   .   .   .   .   .   #   #   .
^J  .   .   .   .   .   .   .   .   .   #   #   #
^K  .   .   .   .   .   .   .   .   .   #   #   #
^L  .   .   .   .   .   .   .   .   .   #   #   #
^M  .   .   .   .   .   .   .   .   .   #   #   #
^N  .   .   .   .   .   .   .   .   .   #   .   .
^O  .   .   .   .   .   .   .   .   .   #   .   .

ch  DD? OD? XD? AN? AF? LC? UC? PT? PR? CT? SP? EL?
^P  .   .   .   .   .   .   .   .   .   #   .   .
^Q  .   .   .   .   .   .   .   .   .   #   .   .
^R  .   .   .   .   .   .   .   .   .   #   .   .
^S  .   .   .   .   .   .   .   .   .   #   .   .
^T  .   .   .   .   .   .   .   .   .   #   .   .
^U  .   .   .   .   .   .   .   .   .   #   .   .
^V  .   .   .   .   .   .   .   .   .   #   .   .
^W  .   .   .   .   .   .   .   .   .   #   .   .
^X  .   .   .   .   .   .   .   .   .   #   .   .
^Y  .   .   .   .   .   .   .   .   .   #   .   .
^Z  .   .   .   .   .   .   .   .   .   #   .   .
^[  .   .   .   .   .   .   .   .   .   #   .   .
^\  .   .   .   .   .   .   .   .   .   #   .   .
^]  .   .   .   .   .   .   .   .   .   #   .   .
^^  .   .   .   .   .   .   .   .   .   #   .   .
^_  .   .   .   .   .   .   .   .   .   #   .   .

ch  DD? OD? XD? AN? AF? LC? UC? PT? PR? CT? SP? EL?
    .   .   .   .   .   .   .   .   #   .   #   .
!   .   .   .   .   .   .   .   #   #   .   .   .
"   .   .   .   .   .   .   .   #   #   .   .   .
#   .   .   .   .   .   .   .   #   #   .   .   .
$   .   .   .   .   .   .   .   #   #   .   .   .
%   .   .   .   .   .   .   .   #   #   .   .   .
&   .   .   .   .   .   .   .   #   #   .   .   .
'   .   .   .   .   .   .   .   #   #   .   .   .
(   .   .   .   .   .   .   .   #   #   .   .   .
)   .   .   .   .   .   .   .   #   #   .   .   .
*   .   .   .   .   .   .   .   #   #   .   .   .
+   .   .   .   .   .   .   .   #   #   .   .   .
,   .   .   .   .   .   .   .   #   #   .   .   .
-   .   .   .   .   .   .   .   #   #   .   .   .
.   .   .   .   .   .   .   .   #   #   .   .   .
/   .   .   .   .   .   .   .   #   #   .   .   .

ch  DD? OD? XD? AN? AF? LC? UC? PT? PR? CT? SP? EL?
0   #   #   #   #   .   .   .   .   #   .   .   .
1   #   #   #   #   .   .   .   .   #   .   .   .
2   #   #   #   #   .   .   .   .   #   .   .   .
3   #   #   #   #   .   .   .   .   #   .   .   .
4   #   #   #   #   .   .   .   .   #   .   .   .
5   #   #   #   #   .   .   .   .   #   .   .   .
6   #   #   #   #   .   .   .   .   #   .   .   .
7   #   #   #   #   .   .   .   .   #   .   .   .
8   #   .   #   #   .   .   .   .   #   .   .   .
9   #   .   #   #   .   .   .   .   #   .   .   .
:   .   .   .   .   .   .   .   #   #   .   .   .
;   .   .   .   .   .   .   .   #   #   .   .   .
<   .   .   .   .   .   .   .   #   #   .   .   .
=   .   .   .   .   .   .   .   #   #   .   .   .
>   .   .   .   .   .   .   .   #   #   .   .   .
?   .   .   .   .   .   .   .   #   #   .   .   .

ch  DD? OD? XD? AN? AF? LC? UC? PT? PR? CT? SP? EL?
@   .   .   .   .   .   .   .   #   #   .   .   .
A   .   .   #   #   #   .   #   .   #   .   .   .
B   .   .   #   #   #   .   #   .   #   .   .   .
C   .   .   #   #   #   .   #   .   #   .   .   .
D   .   .   #   #   #   .   #   .   #   .   .   .
E   .   .   #   #   #   .   #   .   #   .   .   .
F   .   .   #   #   #   .   #   .   #   .   .   .
G   .   .   .   #   #   .   #   .   #   .   .   .
H   .   .   .   #   #   .   #   .   #   .   .   .
I   .   .   .   #   #   .   #   .   #   .   .   .
J   .   .   .   #   #   .   #   .   #   .   .   .
K   .   .   .   #   #   .   #   .   #   .   .   .
L   .   .   .   #   #   .   #   .   #   .   .   .
M   .   .   .   #   #   .   #   .   #   .   .   .
N   .   .   .   #   #   .   #   .   #   .   .   .
O   .   .   .   #   #   .   #   .   #   .   .   .

ch  DD? OD? XD? AN? AF? LC? UC? PT? PR? CT? SP? EL?
P   .   .   .   #   #   .   #   .   #   .   .   .
Q   .   .   .   #   #   .   #   .   #   .   .   .
R   .   .   .   #   #   .   #   .   #   .   .   .
S   .   .   .   #   #   .   #   .   #   .   .   .
T   .   .   .   #   #   .   #   .   #   .   .   .
U   .   .   .   #   #   .   #   .   #   .   .   .
V   .   .   .   #   #   .   #   .   #   .   .   .
W   .   .   .   #   #   .   #   .   #   .   .   .
X   .   .   .   #   #   .   #   .   #   .   .   .
Y   .   .   .   #   #   .   #   .   #   .   .   .
Z   .   .   .   #   #   .   #   .   #   .   .   .
[   .   .   .   .   .   .   .   #   #   .   .   .
\   .   .   .   .   .   .   .   #   #   .   .   .
]   .   .   .   .   .   .   .   #   #   .   .   .
^   .   .   .   .   .   .   .   #   #   .   .   .
_   .   .   .   .   .   .   .   #   #   .   .   .

ch  DD? OD? XD? AN? AF? LC? UC? PT? PR? CT? SP? EL?
`   .   .   .   .   .   .   .   #   #   .   .   .
a   .   .   #   #   #   #   .   .   #   .   .   .
b   .   .   #   #   #   #   .   .   #   .   .   .
c   .   .   #   #   #   #   .   .   #   .   .   .
d   .   .   #   #   #   #   .   .   #   .   .   .
e   .   .   #   #   #   #   .   .   #   .   .   .
f   .   .   #   #   #   #   .   .   #   .   .   .
g   .   .   .   #   #   #   .   .   #   .   .   .
h   .   .   .   #   #   #   .   .   #   .   .   .
i   .   .   .   #   #   #   .   .   #   .   .   .
j   .   .   .   #   #   #   .   .   #   .   .   .
k   .   .   .   #   #   #   .   .   #   .   .   .
l   .   .   .   #   #   #   .   .   #   .   .   .
m   .   .   .   #   #   #   .   .   #   .   .   .
n   .   .   .   #   #   #   .   .   #   .   .   .
o   .   .   .   #   #   #   .   .   #   .   .   .

ch  DD? OD? XD? AN? AF? LC? UC? PT? PR? CT? SP? EL?
p   .   .   .   #   #   #   .   .   #   .   .   .
q   .   .   .   #   #   #   .   .   #   .   .   .
r   .   .   .   #   #   #   .   .   #   .   .   .
s   .   .   .   #   #   #   .   .   #   .   .   .
t   .   .   .   #   #   #   .   .   #   .   .   .
u   .   .   .   #   #   #   .   .   #   .   .   .
v   .   .   .   #   #   #   .   .   #   .   .   .
w   .   .   .   #   #   #   .   .   #   .   .   .
x   .   .   .   #   #   #   .   .   #   .   .   .
y   .   .   .   #   #   #   .   .   #   .   .   .
z   .   .   .   #   #   #   .   .   #   .   .   .
{   .   .   .   .   .   .   .   #   #   .   .   .
|   .   .   .   .   .   .   .   #   #   .   .   .
}   .   .   .   .   .   .   .   #   #   .   .   .
~   .   .   .   .   .   .   .   #   #   .   .   .
DEL .   .   .   .   .   .   .   #   .   #   .   .
------ EOF ------
ls -l ctypes.demo
cat >ctypes.h <<'------ EOF ------'
/*  File   : ctypes.h
    Author : Richard A. O'Keefe.
    Updated: 26 April 1984
    Purpose: Reimplement the UNIX ctype(3) library.

    isaneol(c) means that c is a line terminating character.
    isalnum, ispunct, isspace, and isaneol are defined on the
    range -1..127, i.e. on ASCII U {EOF}, while all the other
    macros are defined for any integer.

    isodigit(c) checks for Octal digits.
    isxdigit(c) checkx for heXadecimal digits.
*/

#define isdigit(c)	((unsigned)((c)-'0') < 10)
#define islower(c)	((unsigned)((c)-'a') < 26)
#define isupper(c)	((unsigned)((c)-'A') < 26)
#define isprint(c)	((unsigned)((c)-' ') < 95)
#define iscntrl(c)	((unsigned)((c)-' ') >= 95)
#define isascii(c)	((unsigned)(c) < 128)
#define isalpha(c)	((unsigned)(((c)|32)-'a') < 26)

extern	char	_c2type[];

#define isalnum(c)	(_c2type[(c)+1] < 36)
#define ispunct(c)	(_c2type[(c)+1] == 36)
#define isspace(c)	(_c2type[(c)+1] > 37)
#define isaneol(c)	(_c2type[(c)+1] > 38)

#define	isxdigit(c)	(_c2type[(c)+1] < 16)
#define isodigit(c)	((unsigned)((c)-'0') < 8)

/*  The following "conversion" macros have been in some versions of UNIX
    but are not in all.  tocntrl is new.  The original motivation for ^?
    being a name for DEL was that (x)^64 mapped A..Z to ^A..^Z and also
    ? to DEL.  The trouble is that this trick doesn't work for lower case
    letters.  The version given here is not mine.  I wish it was.  It has
    the nice property that DEL is mapped to itself (so does EOF).
    tolower(c) and toupper(c) are only defined when isalpha(c).
*/
#define	tolower(c)	((c)|32)
#define toupper(c)	((c)&~32)
#define tocntrl(c)	(((((c)+1)&~96)-1)&127)
#define	toascii(c)	((c)&127)

------ EOF ------
ls -l ctypes.h
cat >ffs.c <<'------ EOF ------'
/*  File   : ffs.c
    Author : Richard A. O'Keefe.
    Updated: 20 April 1984
    Defines: ffs(), ffc()

    ffs(i) returns the index of the least significant 1 bit in i,
	   where 1 means the least significant bit and 32 means the
	   most significant bit, or returns -1 if i is 0.

    ffc(i) returns the index of the least significant 0 bit in i,
	   where 1 means the least significant bit and 32 means the
	   most significant bit, or returns -1 if i is ~0.

    These functions mimic the VAX FFS and FFC instructions, except that
    the latter return much more sensible values.  This file only exists
    to make it easier to move 4.2bsd programs to System III (which is
    rather like moving up from a Rolls Royce to a model T Ford), and so
    I haven't bother with assembly code versions.
*/

#include "strings.h"

int ffs(i)
    register int i;
    {
	register int N;

	for (N = 8*sizeof(int); --N >= 0; i >>= 1)
	    if (i&1) return 8*sizeof(int)-N;
	return -1;
    }

int ffc(i)
    register int i;
    {
	register int N;

	for (N = 8*sizeof(int); --N >= 0; i >>= 1)
	    if (!(i&1)) return 8*sizeof(int)-N;
	return -1;
    }


------ EOF ------
ls -l ffs.c
cat >getopt.3 <<'------ EOF ------'
.TH GETOPT 3 local
.DA 25 March 1982
.SH NAME
getopt \- get option letter from argv
.SH SYNOPSIS
.ft B
int getopt(argc, argv, optstring)
.br
int argc;
.br
char **argv;
.br
char *optstring;
.sp
extern char *optarg;
.br
extern int optind;
.ft
.SH DESCRIPTION
.I Getopt
returns the next option letter in
.I argv
that matches a letter in
.IR optstring .
.I Optstring
is a string of recognized option letters;
if a letter is followed by a colon, the option is expected to have
an argument that may or may not be separated from it by white space.
.I Optarg
is set to point to the start of the option argument on return from
.IR getopt .
.PP
.I Getopt
places in
.I optind
the
.I argv
index of the next argument to be processed.
Because
.I optind
is external, it is normally initialized to zero automatically
before the first call to 
.IR getopt .
.PP
When all options have been processed (i.e., up to the first
non-option argument),
.I getopt
returns
.BR EOF .
The special option
.B \-\-
may be used to delimit the end of the options;
.B EOF
will be returned, and
.B \-\-
will be skipped.
.SH SEE ALSO
getopt(1)
.SH DIAGNOSTICS
.I Getopt
prints an error message on
.I stderr
and returns a question mark
.RB ( ? )
when it encounters an option letter not included in
.IR optstring .
.SH EXAMPLE
The following code fragment shows how one might process the arguments
for a command that can take the mutually exclusive options
.B a
and
.BR b ,
and the options
.B f
and
.BR o ,
both of which require arguments:
.PP
.RS
.nf
main(argc, argv)
    int argc;
    char **argv;
    {
        int c;
        extern int optind;
        extern char *optarg;
        \&.
        \&.
        \&.
        while ((c = getopt(argc, argv, "abf:o:")) != EOF) {
            switch (c) {
                case 'a':
                    if (bflg) errflg++; else aflg++;
                    break;
                case 'b':
                    if (aflg) errflg++; else bflg++;
                    break;
                case 'f':
                    ifile = optarg;
                    break;
                case 'o':
                    ofile = optarg;
                    break;
                case '?':
                default:
                    errflg++;
                    break;
            }
        }
        if (errflg) {
            fprintf(stderr, "Usage: ...");
            exit(2);
        }
        for (; optind < argc; optind++) {
            \&.
            \&.
            \&.
        }
        \&.
        \&.
        \&.
    }
.RE
.PP
A template similar to this can be found in
.IR /usr/pub/template.c .
.SH HISTORY
Written by Henry Spencer, working from a Bell Labs manual page.
Behavior believed identical to the Bell version.
.SH BUGS
It is not obvious how
`\-'
standing alone should be treated; this version treats it as
a non-option argument, which is not always right.
.PP
Option arguments are allowed to begin with `\-';
this is reasonable but reduces the amount of error checking possible.
.PP
.I Getopt
is quite flexible but the obvious price must be paid: there is much
it could do that it doesn't, like
checking mutually exclusive options, checking type of
option arguments, etc.
------ EOF ------
ls -l getopt.3
cat >getopt.c <<'------ EOF ------'
/*  File   : getopt.c
    Author : Henry Spencer, University of Toronto
    Updated: 28 April 1984
    Purpose: get option letter from argv.
*/

#include <stdio.h>
#include "strings.h"

char	*optarg;	/* Global argument pointer. */
int	optind = 0;	/* Global argv index. */

int getopt(argc, argv, optstring)
    int argc;
    char *argv[];
    char *optstring;
    {
	register int c;
	register char *place;
	static char *scan = NullS;	/* Private scan pointer. */

	optarg = NullS;

	if (scan == NullS || *scan == '\0') {
	    if (optind == 0) optind++;
	    if (optind >= argc) return EOF;
	    place = argv[optind];
	    if (place[0] != '-' || place[1] == '\0') return EOF;
	    optind++;
	    if (place[1] == '-' && place[2] == '\0') return EOF;
	    scan = place+1;
	}

	c = *scan++;
	place = index(optstring, c);
	if (place == NullS || c == ':') {
	    fprintf(stderr, "%s: unknown option %c\n", argv[0], c);
	    return '?';
	}
	if (*++place == ':') {
	    if (*scan != '\0') {
		optarg = scan, scan = NullS;
	    } else {
		optarg = argv[optind], optind++;
	    }
	}
	return c;
    }

------ EOF ------
ls -l getopt.c
cat >int2str.c <<'------ EOF ------'
/*  File   : int2str.c
    Author : Richard A. O'Keefe
    Updated: 30 April 1984
    Defines: int2str(), itoa(), ltoa()

    int2str(dst, radix, val)
    converts the (long) integer "val" to character form and moves it to
    the destination string "dst" followed by a terminating NUL.  The
    result is normally a pointer to this NUL character, but if the radix
    is dud the result will be NullS and nothing will be changed.

    If radix is -2..-36, val is taken to be SIGNED.
    If radix is  2.. 36, val is taken to be UNSIGNED.
    That is, val is signed if and only if radix is.  You will normally
    use radix -10 only through itoa and ltoa, for radix 2, 8, or 16
    unsigned is what you generally want.

    _dig_vec is public just in case someone has a use for it.
    The definitions of itoa and ltoa are actually macros in strings.h,
    but this is where the code is.
*/

#include "strings.h"

char _dig_vec[] =
    "0123456789abcdefghijklmnopqrstuvwxyz";


char *int2str(dst, radix, val)
    register char *dst;
    register int radix;
    register long val;
    {
	char buffer[33];
	register char *p;

	if (radix < 0) {
	    if (radix < -36 || radix > -2) return NullS;
	    if (val < 0) {
		*dst++ = '-';
		val = -val;
	    }
	    radix = -radix;
	} else {
	    if (radix > 36 || radix < 2) return NullS;
	}
	/*  The slightly contorted code which follows is due to the
	    fact that few machines directly support unsigned long / and %.
	    Certainly the VAX C compiler generates a subroutine call.  In
	    the interests of efficiency (hollow laugh) I let this happen
	    for the first digit only; after that "val" will be in range so
	    that signed integer division will do.  Sorry 'bout that.
	    CHECK THE CODE PRODUCED BY YOUR C COMPILER.  The first % and /
	    should be unsigned, the second % and / signed, but C compilers
	    tend to be extraordinarily sensitive to minor details of style.
	    This works on a VAX, that's all I claim for it.
	*/
	p = &buffer[32];
	*p = '\0';
	*--p = _dig_vec[(unsigned long)val%(unsigned long)radix];
	val = (unsigned long)val/(unsigned long)radix;
	while (val != 0) *--p = _dig_vec[val%radix], val /= radix;
	while (*dst++ = *p++) ;
	return dst-1;
    }

------ EOF ------
ls -l int2str.c
cat >memccpy.c <<'------ EOF ------'
/*  File   : memccpy.c
    Author : Richard A. O'Keefe.
    Updated: 25 May 1984
    Defines: memccpy()

    memccpy(dst, src, chr, len)
    copies bytes from src to dst until either len bytes have been moved
    or a byte equal to chr has been moved.  In the former case it gives
    NullS as the value, in the latter a pointer to just after the place
    where "chr" was moved to in dst.  Note that copying stops after the
    first instance of "chr", and that remaining characters in "dst" are
    not changed in any way, no NUL being inserted or anything.

    See the "Character Comparison" section in the READ-ME file.
*/

#include "strings.h"

char *memccpy(dst, src, chr, len)
    register char *dst, *src;
    register int chr;		/* should be char */
    register int len;
    {
	while (--len >= 0)
	    if ((*dst++ = *src++) == chr) return dst;
	return NullS;
    }

------ EOF ------
ls -l memccpy.c
cat >memchr.c <<'------ EOF ------'
/*  File   : memchr.c
    Author : Richard A. O'Keefe.
    Updated: 25 May 1984
    Defines: memchr()

    memchr(src, chr, len)
    searches the memory area pointed to by src extending for len bytes,
    looking for an occurrence of the byte value chr.  It returns NullS
    if there is no such occurrence.  Otherwise it returns a pointer to
    the FIRST such occurrence.

    See the "Character Comparison" section in the READ-ME file.
*/

#include "strings.h"

#if	VaxAsm

char *memchr(src, chr, len)
    char *src;
    char chr;
    int len;
    {
	asm("locc 8(ap),12(ap),*4(ap)");
	asm("bneq L1");
	asm("movl r1,r0");
	asm("L1: ret");
    }

#else  ~VaxAsm

char *memchr(src, chr, len)
    register char *src;
    register int chr;		/* should be char */
    register int len;
    {
	while (--len >= 0)
	    if (*src++ == chr) return src-1;
	return NullS;
    }

#endif	VaxAsm

------ EOF ------
ls -l memchr.c
cat >memcmp.c <<'------ EOF ------'
/*  File   : memcmp.c
    Author : Richard A. O'Keefe.
    Updated: 25 May 1984
    Defines: memcmp()

    memcmp(lhs, rhs, len)
    compares the two memory areas lhs[0..len-1]  ??  rhs[0..len-1].   It
    returns  an integer less than, equal to, or greater than 0 according
    as lhs[-] is lexicographically less than, equal to, or greater  than
    rhs[-].  Note  that this is not at all the same as bcmp, which tells
    you *where* the difference is but not what.

    Note:  suppose we have int x, y;  then memcmp(&x, &y, sizeof x) need
    not bear any relation to x-y.  This is because byte order is machine
    dependent, and also, some machines have integer representations that
    are shorter than a machine word and two equal  integers  might  have
    different  values  in the spare bits.  On a ones complement machine,
    -0 == 0, but the bit patterns are different.

    This could have a Vax assembly code version, but as the return value
    is not the value left behind by  the  cmpc3  instruction  I  haven't
    bothered.
*/

int memcmp(lhs, rhs, len)
    register char *lhs, *rhs;
    register int len;
    {
	while (--len >= 0)
	    if (*lhs++ != *rhs++) return lhs[-1]-rhs[-1];
	return 0;
    }

------ EOF ------
ls -l memcmp.c
cat >memcpy.c <<'------ EOF ------'
/*  File   : memcpy.c
    Author : Richard A. O'Keefe.
    Updated: 25 May 1984
    Defines: memcpy()

    memcpy(dst, src, len)
    moves len bytes from src to dst.  The result is dst.  This is not
    the same as strncpy or strnmov, while move a maximum of len bytes
    and stop early if they hit a NUL character.  This moves len bytes
    exactly, no more, no less.  See also bcopy() and bmove() which do
    not return a value but otherwise do the same job.

    Note: the VAX assembly code version can only handle 0 <= len < 2^16.
    It is presented for your interest and amusement.
*/

#include "strings.h"

#if	VaxAsm

char *memcpy(dst, src, len)
    char *dst, *src;
    int len;
    {
	asm("movc3 12(ap),*8(ap),*4(ap)");
	return dst;
    }

#else  ~VaxAsm

char *memcpy(dst, src, len)
    char *dst;
    register char *src;
    register int len;
    {
	register char *d;

	for (d = dst; --len >= 0; *d++ = *src++) ;
	return dst;
    }

#endif	VaxAsm

------ EOF ------
ls -l memcpy.c
cat >memmov.c <<'------ EOF ------'
/*  File   : memmov.c
    Author : Richard A. O'Keefe.
    Updated: 25 May 1984
    Defines: memmov()

    memmov(dst, src, len)
    moves len bytes from src to dst.  The result is dst+len.
    This is to memcpy as str[n]mov is to str[n]cpy, that is, it moves
    exactly the same bytes but returns a pointer to just after the
    the last changed byte.  You can concatenate blocks pa for la,
    pb for lb, pc for lc into area pd by doing
	memmov(memmov(memmov(pd, pa, la), pb, lb), pc, lc);
    Unlike strnmov, memmov does not stop when it hits a NUL byte.

    Note: the VAX assembly code version can only handle 0 <= len < 2^16.
    It is presented for your interest and amusement.
*/

#include "strings.h"

#if	VaxAsm

char *memmov(dst, src, len)
    char *dst, *src;
    int len;
    {
	asm("movc3 12(ap),*8(ap),*4(ap)");
	return dst+len;
    }

#else  ~VaxAsm

char *memmov(dst, src, len)
    register char *dst, *src;
    register int len;
    {
	while (--len >= 0) *dst++ = *src++;
	return dst;
    }

#endif	VaxAsm

------ EOF ------
ls -l memmov.c
cat >memory.h <<'------ EOF ------'
/*  File   : memory.h
    Author : Richard A. O'Keefe.
    Updated: 1 June 1984
    Purpose: Header file for the System V "memory(3C)" package.

    All the functions in this package are the original work  of  Richard
    A. O'Keefe.   Any resemblance between them and any functions in AT&T
    or other licensed software is due entirely to my use of the System V
    memory(3C) manual page as a specification.  See the READ-ME to  find
    the conditions under which this material may be used and copied.

    The System V manual says that the mem* functions are declared in the
    <memory.h> file.  This file is also included in the <strings.h> file,
    but it does no harm to #include both in either order.
*/

#ifndef	memeql

#define memeql	!memcmp
extern	int	memcmp(/*char^,char^,int*/);
extern	char	*memcpy(/*char^,char^,int*/);
extern	char	*memccpy(/*char^,char^,char,int*/);
extern	char	*memset(/*char^,char,int*/);
extern	char	*memchr(/*char^,char,int*/);
extern	char	*memrchr(/*char^,char,int*/);
extern	char	*memmov(/*char^,char^,int*/);
extern	void	memrev(/*char^,char^,int*/);

#endif	memeql

------ EOF ------
ls -l memory.h
cat >memrchr.c <<'------ EOF ------'
/*  File   : memrchr.c
    Author : Richard A. O'Keefe.
    Updated: 25 May 1984
    Defines: memrchr()

    memrchr(src, chr, len)
    searches the memory area pointed to by src extending for len bytes,
    looking for an occurrence of the byte value chr.  It returns NullS
    if there is no such occurrence.  Otherwise it returns a pointer to
    the LAST such occurrence.

    See the "Character Comparison" section in the READ-ME file.
*/

#include "strings.h"

char *memrchr(src, chr, len)
    register char *src;
    register int chr;		/* should be char */
    register int len;
    {
	register char *ans;
	for (ans = NullS; --len >= 0; src++)
	    if (*src == chr) ans = src;
	return ans;
    }

------ EOF ------
ls -l memrchr.c
cat >memrev.c <<'------ EOF ------'
/*  File   : memrev.c
    Author : Richard A. O'Keefe.
    Updated: 1 June 1984
    Defines: memrev()

    memrev(dst, src, len)
    moves len bytes from src to dst, in REVERSE order.  NUL characters
    receive no special treatment, they are moved like the rest.  It is
    to strrev as memcpy is to strcpy.

    Note: this function is perfectly happy to reverse a block into the
    same place, memrev(x, x, L) will work.
    It will not work for partially overlapping source and destination.
*/

#include "strings.h"

void memrev(dsta, srca, len)
    register char *dsta, *srca;
    int len;
    {
	register char *dstz, *srcz;
	register int t;

	if (len <= 0) return;
	srcz = srca+len;
	dstz = dsta+len;
	while (srcz > srca) {
	    t = *--srcz;
	    *--dstz = *srca++;
	    *dsta++ = t;
	}
    }

------ EOF ------
ls -l memrev.c
cat >memset.c <<'------ EOF ------'
/*  File   : memset.c
    Author : Richard A. O'Keefe.
    Updated: 25 May 1984
    Defines: memset()

    memset(dst, chr, len)
    fills the memory area dst[0..len-1] with len bytes all equal to chr.
    The result is dst.  See also bfill(), which has no return value and
    puts the last two arguments the other way around.

    Note: the VAX assembly code version can only handle 0 <= len < 2^16.
    It is presented for your interest and amusement.
*/

#include "strings.h"

#if	VaxAsm

char *memset(dst, chr, len)
    char *dst;
    int chr;			/* should be char */
    int len;
    {
	asm("movc5 $0,*4(ap),8(ap),12(ap),*4(ap)");
	return dst;
    }

#else  ~VaxAsm

char *memset(dst, chr, len)
    char *dst;
    register int chr;		/* should be char */
    register int len;
    {
	register char *d;

	for (d = dst; --len >= 0; *d++ = chr) ;
	return dst;
    }

#endif	VaxAsm

------ EOF ------
ls -l memset.c
cat >memtrans.c <<'------ EOF ------'
/*  File   : memtrans.c
    Author : Richard A. O'Keefe.
    Updated: 2 June 1984
    Defines: memtrans()

    memtrans(dst, src, from, to, len)
    copies exactly len characters from src[] to dst[], translating chars
    in from[] to corresponding characters in to[].   From[] and to[] are
    handled by _str2map. BEWARE: _str2map normally expects characters in
    the range 0..127.  The Vax MOVTC instruction thinks its table is 256
    bytes long; if you want to translate arbitrary bytes you'd better be
    sure that the _map_vec array is 256 bytes long.  As distributed, the
    memtrans function is only for translating ASCII (to 8-bit codes).

    The VaxAsm code can only handle 0 <= len < 2^16, and is presented as
    usual for your interest and amusement.  Why *do* designers of 32-bit
    machines put 16-bit limits on strings?  (Dec aren't the only ones.)
*/

#include "strings.h"
#include "_str2map.h"

#if	VaxAsm

void memtrans(dst, src, from, to, len)
    _char_ *dst, *src, *from, *to;
    int len;
    {
	_str2map(0, from, to);
	asm("movtc 20(ap),*8(ap),$0,__map_vec,20(ap),*4(ap)");
    }

#else  ~VaxAsm

void memtrans(dst, src, from, to, len)
    register _char_ *dst, *src;
    _char_ *from, *to;
    register int len;
    {
	_str2map(0, from, to);
	while (--len >= 0) *dst++ = _map_vec[*src++];
    }

#endif	VaxAsm

------ EOF ------
ls -l memtrans.c
cat >shar <<'------ EOF ------'
#!/bin/sh
append=0
if test $1 = -a
    then
	append=1
	shift
    fi
verbose=0
if test $1 = -v
    then
	verbose=1
	shift
    fi
sharchive=sharchive
if test $1 = -o
    then
	sharchive=$2
	shift; shift
    fi
if test $# = 0
    then
	echo "Usage: shar [-a] [-v] [-o sharchive] file..."
	exit
    fi
if test $append = 0
    then
	echo "#!/bin/sh" >$sharchive
    fi
for file in $*
    do
	if test "$file" != "$sharchive"
	    then
		if test $verbose = 1
		    then
			echo archiving $file
		    fi
		echo "cat >$file <<'------ EOF ------'" >>$sharchive
		cat $file >>$sharchive
		echo "------ EOF ------" >>$sharchive
		echo "ls -l $file" >>$sharchive
	    fi
    done
chmod +x $sharchive
------ EOF ------
ls -l shar
cat >str2int.c <<'------ EOF ------'
/*  File   : str2int.c
    Author : Richard A. O'Keefe
    Updated: 27 April 1984
    Defines: str2int(), atoi(), atol()

    str2int(src, radix, lower, upper, &val)
    converts the string pointed to by src to an integer and stores it in
    val.  It skips leading spaces and tabs (but not newlines, formfeeds,
    backspaces), then it accepts an optional sign and a sequence of digits
    in the specified radix.  The result should satisfy lower <= *val <= upper.
    The result is a pointer to the first character after the number;
    trailing spaces will NOT be skipped.

    If an error is detected, the result will be NullS, the value put
    in val will be 0, and errno will be set to
	EDOM	if there are no digits
	ERANGE	if the result would overflow or otherwise fail to lie
		within the specified bounds.
    Check that the bounds are right for your machine.
    This looks amazingly complicated for what you probably thought was an
    easy task.  Coping with integer overflow and the asymmetric range of
    twos complement machines is anything but easy.

    So that users of atoi and atol can check whether an error occured,
    I have taken a wholly unprecedented step: errno is CLEARED if this
    call has no problems.
*/

#include "strings.h"
#include "ctypes.h"
#include <errno.h>
extern int errno;

/*	CHECK THESE CONSTANTS FOR YOUR MACHINE!!!	*/

#if	pdp11
#   define	MaxInt      0x7fffL	/* int  = 16 bits */
#   define	MinInt      0x8000L
#   define	MaxLong 0x7fffffffL	/* long = 32 bits */
#   define	MinLong 0x80000000L
#else  !pdp11
#   define	MaxInt  0x7fffffffL	/* int  = 32 bits */
#   define	MinInt  0x80000000L
#   define	MaxLong 0x7fffffffL	/* long = 32 bits */
#   define	MinLong 0x80000000L
#endif	pdp11


char *str2int(src, radix, lower, upper, val)
    register char *src;
    register int radix;
    long lower, upper, *val;
    {
	int sign;		/* is number negative (+1) or positive (-1) */
	int n;			/* number of digits yet to be converted */
	long limit;		/* "largest" possible valid input */
	long scale;		/* the amount to multiply next digit by */
	long sofar;		/* the running value */
	register int d;		/* (negative of) next digit */
	char *answer;		

	/*  Make sure *val is sensible in case of error  */

	*val = 0;

	/*  Check that the radix is in the range 2..36  */

	if (radix < 2 || radix > 36) {
	    errno = EDOM;
	    return NullS;
	}

	/*  The basic problem is: how do we handle the conversion of
	    a number without resorting to machine-specific code to
	    check for overflow?  Obviously, we have to ensure that
	    no calculation can overflow.  We are guaranteed that the
	    "lower" and "upper" arguments are valid machine integers.
	    On sign-and-magnitude, twos-complement, and ones-complement
	    machines all, if +|n| is representable, so is -|n|, but on
	    twos complement machines the converse is not true.  So the
	    "maximum" representable number has a negative representative.
	    Limit is set to min(-|lower|,-|upper|); this is the "largest"
	    number we are concerned with.	*/

	/*  Calculate Limit using Scale as a scratch variable  */

	if ((limit = lower) > 0) limit = -limit;
	if ((scale = upper) > 0) scale = -scale;
	if (scale < limit) limit = scale;

	/*  Skip leading spaces and check for a sign.
	    Note: because on a 2s complement machine MinLong is a valid
	    integer but |MinLong| is not, we have to keep the current
	    converted value (and the scale!) as *negative* numbers,
	    so the sign is the opposite of what you might expect.
	    Should the test in the loop be isspace(*src)?
	*/
	while (*src == ' ' || *src == '\t') src++;
	sign = -1;
	if (*src == '+') src++; else
	if (*src == '-') src++, sign = 1;

	/*  Check that there is at least one digit  */

	if (_c2type[1+ *src] >= radix) {
	    errno = EDOM;
	    return NullS;
	}

	/*  Skip leading zeros so that we never compute a power of radix
	    in scale that we won't have a need for.  Otherwise sticking
	    enough 0s in front of a number could cause the multiplication
	    to overflow when it neededn't.
	*/
	while (*src == '0') src++;

	/*  Move over the remaining digits.  We have to convert from left
	    to left in order to avoid overflow.  Answer is after last digit.
	*/
	for (n = 0; _c2type[1+ *src++] < radix; n++) ;
	answer = --src;

	/*  The invariant we want to maintain is that src is just
	    to the right of n digits, we've converted k digits to
	    sofar, scale = -radix**k, and scale < sofar < 0.  Now
	    if the final number is to be within the original
	    Limit, we must have (to the left)*scale+sofar >= Limit,
	    or (to the left)*scale >= Limit-sofar, i.e. the digits
	    to the left of src must form an integer <= (Limit-sofar)/(scale).
	    In particular, this is true of the next digit.  In our
	    incremental calculation of Limit,

		IT IS VITAL that (-|N|)/(-|D|) = |N|/|D|
	*/
	
	for (sofar = 0, scale = -1; --n >= 0; ) {
	    d = _c2type[1+ *--src];
	    if (-d < limit) {
		errno = ERANGE;
		return NullS;
	    }
	    limit = (limit+d)/radix, sofar += d*scale;
	    if (n != 0) scale *= radix;	/* watch out for overflow!!! */
	}    
	/*  Now it might still happen that sofar = -32768 or its equivalent,
	    so we can't just multiply by the sign and check that the result
	    is in the range lower..upper.  All of this caution is a right
	    pain in the neck.  If only there were a standard routine which
	    says generate thus and such a signal on integer overflow...
	    But not enough machines can do it *SIGH*.
	*/
	if (sign < 0 && sofar < -MaxLong /* twos-complement problem */
	||  (sofar*=sign) < lower || sofar > upper) {
	    errno = ERANGE;
	    return NullS;
	}
	*val = sofar;
	errno = 0;		/* indicate that all went well */
	return answer;
    }


int atoi(src)
    char *src;
    {
	long val;
	str2int(src, 10, MinInt, MaxInt, &val);
	return (int)val;
    }


long atol(src)
    char *src;
    {
	long val;
	str2int(src, 10, MinLong, MaxLong, &val);
	return val;
    }

------ EOF ------
ls -l str2int.c
cat >strcat.c <<'------ EOF ------'
/*  File   : strcat.c
    Author : Richard A. O'Keefe.
    Updated: 10 April 1984
    Defines: strcat()

    strcat(s, t) concatenates t on the end of s.  There  had  better  be
    enough  room  in  the  space s points to; strcat has no way to tell.
    Note that strcat has to search for the end of s, so if you are doing
    a lot of concatenating it may be better to use strmov, e.g.
	strmov(strmov(strmov(strmov(s,a),b),c),d)
    rather than
	strcat(strcat(strcat(strcpy(s,a),b),c),d).
    strcat returns the old value of s.
*/

#include "strings.h"

char *strcat(s, t)
    register char *s, *t;
    {
	char *save;

	for (save = s; *s++; ) ;
	for (--s; *s++ = *t++; ) ;
	return save;
    }

------ EOF ------
ls -l strcat.c
cat >strchr.c <<'------ EOF ------'
/*  File   : strchr.c
    Author : Richard A. O'Keefe.
    Updated: 20 April 1984
    Defines: strchr(), index()

    strchr(s, c) returns a pointer to the  first  place  in  s  where  c
    occurs,  or  NullS if c does not occur in s. This function is called
    index in V7 and 4.?bsd systems; while not ideal the name is  clearer
    than  strchr,  so index remains in strings.h as a macro.  NB: strchr
    looks for single characters,  not for sets or strings.   To find the
    NUL character which closes s, use strchr(s, '\0') or strend(s).  The
    parameter 'c' is declared 'int' so it will go in a register; if your
    C compiler is happy with register _char_ change it to that.
*/

#include "strings.h"

char *strchr(s, c)
    register _char_ *s;
    register int c;
    {
	for (;;) {
	    if (*s == c) return s;
	    if (!*s++) return NullS;
	}
    }

------ EOF ------
ls -l strchr.c
cat >strcmp.c <<'------ EOF ------'
/*  File   : strcmp.c
    Author : Richard A. O'Keefe.
    Updated: 10 April 1984
    Defines: strcmp()

    strcmp(s, t) returns > 0, = 0,  or < 0  when s > t, s = t,  or s < t
    according  to  the  ordinary  lexicographical  order.   To  test for
    equality, the macro streql(s,t) is clearer than  !strcmp(s,t).  Note
    that  if the string contains characters outside the range 0..127 the
    result is machine-dependent; PDP-11s and  VAXen  use  signed  bytes,
    some other machines use unsigned bytes.
*/

#include "strings.h"

int strcmp(s, t)
    register char *s, *t;
    {
	while (*s == *t++) if (!*s++) return 0;
	return s[0]-t[-1];
    }

------ EOF ------
ls -l strcmp.c
cat >strcpack.c <<'------ EOF ------'
/*  File   : strcpack.c
    Author : Richard A. O'Keefe.
    Updated: 20 April 1984
    Defines: strcpack()

    strcpack(dst, src, set, c)
    copies characters from src to dst, stopping when it finds a NUL.  If
    c is NUL, characters not in the set are not copied to dst.  If c  is
    not  NUL,  sequences  of  characters  not in the set are copied as a
    single c. strcpack is to strpack as strcspn is to strspn.  If your C
    compiler is happy with register _char_, change the declaration of c.
    The result is the address of the NUL byte that now terminates "dst".
    Note that dst may safely be the same as src.
*/

#include "strings.h"
#include "_str2set.h"

char *strcpack(dst, src, set, c)
    register _char_ *dst, *src;
    char *set;
    register int c;
    {
	register int chr;

	_str2set(set);
	while (chr = *src++) {
	    if (_set_vec[chr] != _set_ctr) {
		while ((chr = *src++) && _set_vec[chr] != _set_ctr) ;
		if (c) *dst++ = c;	/* 1. If you don't want trailing */
		if (!chr) break;	/* 2. things turned into "c", swap */
	    }				/* lines 1 and 2. */
	    *dst++ = chr;
	}
	*dst = 0;
	return dst;
    }

------ EOF ------
ls -l strcpack.c
cat >strcpbrk.c <<'------ EOF ------'
/*  File   : strcpbrk.c
    Author : Richard A. O'Keefe.
    Updated: 20 April 1984
    Defines: strcpbrk()

    strcpbrk(s1, s2) returns a pointer to the first character of s1 which
    does not occur in s2.  It is to strpbrk as strcspn is to strspn.   It
    relies on NUL never being in a set.
*/

#include "strings.h"
#include "_str2set.h"

char *strcpbrk(str, set)
    register _char_ *str;
    char *set;
    {
	_str2set(set);
	while (_set_vec[*str++] == _set_ctr);
	return *--str ? str : NullS;
    }

------ EOF ------
ls -l strcpbrk.c
cat >strcpy.c <<'------ EOF ------'
/*  File   : strcpy.c
    Author : Richard A. O'Keefe.
    Updated: 20 April 1984
    Defines: strcpy()

    strcpy(dst, src) copies all the characters  of  src  (including  the
    closing  NUL)  to dst, and returns the old value of dst.  Maybe this
    is useful for doing i = strlen(strcpy(dst, src)); I've always  found
    strmov handier.
*/

#include "strings.h"

char *strcpy(dst, src)
    register char *dst, *src;
    {
	char *save;

	for (save = dst; *dst++ = *src++; ) ;
	return save;
    }

------ EOF ------
ls -l strcpy.c
cat >strcspn.c <<'------ EOF ------'
/*  File   : strcspn.c
    Author : Richard A. O'Keefe.
    Updated: 11 April 1984
    Defines: strspn()

    strcspn(s1, s2) returns the length  of  the  longest  prefix  of  s1
    consisting  entirely  of  characters  which  are  NOT  in s2 ("c" is
    "complement").  NUL is considered to be part  of  s2.   As  _str2set
    will never include NUL in a set, we have to check for it explicitly.
*/

#include "strings.h"
#include "_str2set.h"

int strcspn(str, set)
    register _char_ *str;
    char *set;
    {
	register int L;

	_str2set(set);
	for (L = 0; *str && _set_vec[*str++] != _set_ctr; L++) ;
	return L;
    }

------ EOF ------
ls -l strcspn.c
cat >strctrim.c <<'------ EOF ------'
/*  File   : strctrim.c
    Author : Richard A. O'Keefe.
    Updated: 20 April 1984
    Defines: strctrim()

    strctrim(dst, src, set, ends)
    copies src to dst, but will skip leading characters not in set if
    ends <= 0 and will skip trailing characters not in set if ends >= 0.
    Thus there are three cases:
	ends < 0 :	trim a prefix
	ends = 0 :	trim a prefix and a suffix both
	ends > 0 :	trim a suffix
    This is to strtrim as strcspn is to strspn.
*/

#include "strings.h"
#include "_str2set.h"

char *strctrim(dst, src, set, ends)
    register char *dst, *src;
    char *set;
    int ends;
    {
	_str2set(set);
	if (ends <= 0) {
	    register int chr;
	    while ((chr = *src++) && _set_vec[chr] != _set_ctr) ;
	    --src;
	}
	if (ends >= 0) {
	    register int chr;
	    register char *save = dst;
	    while (chr = *src++) {
		*dst++ = chr;
		if (_set_vec[chr] == _set_ctr) save = dst;
	    }
	    dst = save, *dst = NUL;
	} else {
	    while (*dst++ = *src++) ;
	    --dst;
	}
	return dst;
    }

------ EOF ------
ls -l strctrim.c
cat >strend.c <<'------ EOF ------'
/*  File   : strend.c
    Author : Richard A. O'Keefe.
    Updated: 23 April 1984
    Defines: strend()

    strend(s) returns a character pointer to the NUL which ends s.  That
    is,  strend(s)-s  ==  strlen(s). This is useful for adding things at
    the end of strings.  It is redundant, because  strchr(s,'\0')  could
    be used instead, but this is clearer and faster.
    Beware: the asm version works only if strlen(s) < 65535.
*/

#include "strings.h"

#if	VaxAsm

char *strend(s)
    char *s;
    {
	asm("locc $0,$65535,*4(ap)");
	asm("movl r1,r0");
    }

#else  ~VaxAsm 

char *strend(s)
    register char *s;
    {
	while (*s++);
	return s-1;
    }

#endif	VaxAsm

------ EOF ------
ls -l strend.c
cat >strfield.c <<'------ EOF ------'
/*  File   : strfield.c
    Author : Richard A. O'Keefe.
    Updated: 21 April 1984
    Defines: strfield()

    strfield(src, fields, chars, blanks, tabch)
	is based on the key specifications of the sort(1) command.

	tabch corresponds to 'x' in -t'x'.  If it is NUL, a field
	is leading layout (spaces, tabs &c) followed by at least
	one non-layout character, and is terminated by the next
	layout character or NUL.  If it is not NUL, a field is
	terminated by tabch or NUL.

	fields is the number of fields to skip over.  It corresponds
	to m in -m.n or +m.n .  There must be at least this many
	fields, and only the last may be terminated by NUL.

	chars is the number of characters to skip after the fields
	have been skipped.  At least this many non-NUL characters
	must remain after the fields have been skipped.  Note that
	it is entirely possible for this skip to cross one or more
	field boundaries.  This corresponds to n in +m.n or -m.n .

	Finally, if blanks is not 0, any layout characters will be
	skipped.  There need not be any.  This corresponds to the
	letter b in +2.0b or -0.4b .

	The result is NullS if the source ran out of fields or ran
	out of chars.  Otherwise it is a pointer to the first
	character of src which was not skipped.  It is quite possible
	for this character to be the terminating NUL.

    Example:
	to skip to the user-id field of /etc/passwd:
	    user_id = strfield(line, 2, 0, 0, ':');

	to check whether "line" is at least 27 characters long:
	    if (strfield(line, 0, 27, 0, 0)) then-it-is;

	to select the third blank-delimited field in a line:
	    head = strfield(line, 2, 0, 1, 0);
	    tail = strfield(head, 1, 0, 0, 0);
	    (* the field is the tail-head characters starting at head *)

    It's not a bug, it's a feature: "layout" means any ASCII character
	in the range '\1' .. ' ', including '\n', '\f' and so on.
*/

#include "strings.h"

char *strfield(src, fields, chars, blanks, tabch)
    register char *src;
    int fields, chars, blanks, tabch;
    {
	if (tabch <= 0) {
	    while (--fields >= 0) {
		while (*src <= ' ') if (!*src++) return NullS;
		while (*++src > ' ') ;
	    }
	} else
	if (fields > 0) {
	    do if (!*src) return NullS;
	    while (*src++ != tabch || --fields > 0);
	}
	while (--chars >= 0) if (!*src++) return NullS;
	if (blanks) while (*src && *src++ <= ' ') ;
	return src;
    }
------ EOF ------
ls -l strfield.c
cat >strfind.c <<'------ EOF ------'
/*  File   : strfind.c
    Author : Richard A. O'Keefe.
    Updated: 23 April 1984
    Defines: strfind()

    strfind(src, pat) looks for an instance of pat in src.  pat is not a
    regex(3) pattern, it is a literal string which must be matched exactly.
    As a special hack to prevent infinite loops, the empty string will be
    found just once, at the far end of src.  This is hard to justify.  The
    result is a pointer to the first character AFTER the located instance,
    or NullS if pat does not occur in src.  The reason for returning the
    place after the instance is so that you can count the number of instances
    by writing
	_str2pat(ToBeFound);
	for (p = src, n = 0; p = strfind(p, NullS); n++) ;
    If you want a pointer to the first character of the instance, it is up
    to you to subtract strlen(pat).

    If there were a strnfind it wouldn't have to look at all the characters
    of src, this version does otherwise it could miss the closing NUL.
*/

#include "strings.h"
#include "_str2pat.h"

char *strfind(src, pat)
    char *src, *pat;
    {
	register char *s, *p;
	register int c, lastch;

	pat = _str2pat(pat);
	if (_pat_lim < 0) {
	    for (s = src; *s++; ) ;
	    return s-1;
	}
	/*  The pattern is non-empty  */
	for (c = _pat_lim, lastch = pat[c]; ; c = _pat_vec[c]) {
	    for (s = src; --c >= 0; )
		if (!*s++) return NullS;
	    c = *s, src = s;
	    if (c == lastch) {
		for (s -= _pat_lim, p = pat; *p; )
		    if (*s++ != *p++) goto not_yet;
		return s;
not_yet:;   }
	}
    }

------ EOF ------
ls -l strfind.c
cat >strings.h <<'------ EOF ------'
/*  File   : strings.h
    Author : Richard A. O'Keefe.
    Updated: 1 June 1984
    Purpose: Header file for the "string(3C)" package.

    All  the  routines  in  this  package  are  the  original  work   of
    R.A.O'Keefe.   Any  resemblance  between  them  and  any routines in
    licensed software is due entirely  to  these  routines  having  been
    written  using the "man 3 string" UNIX manual page, or in some cases
    the "man 1 sort" manual page as a specification.  See the READ-ME to
    find the conditions under which these routines may be used & copied.
*/

#ifndef	NullS
#define	NullS	(char*)0
#define NUL	'\0'

/*  MAKE SURE THE RIGHT VERSION OF THE FOLLOWING MACRO IS INSTALLED!  */

#if	vax | pdp11 | m68000 | perq
#define CharsAreSigned 1			/* default is unsigned */
#endif	vax | pdp11 | m68000 | perq

#if	CharsAreSigned
#define	int2char(i)	(((i)<<((sizeof (int) -1)*8))>>((sizeof (int) -1)*8))
#else  !CharsAreSigned
#define	int2char(i)	((i)&255)
#endif	CharsAreSigned
/*  If characters are signed, but the above doesn't work,
    try	((127-(255&~(i)))^(-128))
*/

#ifndef	_AlphabetSize
#define	_AlphabetSize	128
#endif

#if	_AlphabetSize == 128
typedef	char _char_;
#endif
#if	_AlphabetSize == 256
typedef	unsigned char _char_;
#endif

/*  NullS is the "nil" character  pointer.   NULL  would  work  in  most
    cases,  but  in  some  C  compilers  pointers and integers may be of
    different sizes, so it is handy to have a nil pointer that  one  can
    pass to a function as well as compare pointers against.

    NUL is the ASCII name for the character with code 0.  Its use to end
    strings is a convention of the C programming language.  There are in
    fact three different end of string conventions supported by routines
    in this package:
	str<opn>	: end at the first NUL character
	strn<opn>	: end at the first NUL character, or when the
			  extra "len" parameter runs out.
	mem<opn>,b<opn>	: length determined solely by "len" parameter.
    Unfortunately, the VAX hardware only supports the last convention, a
    pity really.  Fortran 77 users BEWARE: Fortran 77's convention is an
    entirely different one, and there are NO routines in this package as
    yet which support it.  (But see section 3F of the 4.2 BSD manual.)

    The routines which move characters around don't  care  whether  they
    are  signed or unsigned.  But the routines which compare a character
    in a string with an argument, or use a character from a string as an
    index into an array, do care.  I have assumed that
	_AlphabetSize = 128 => only 0..127 appear in strings
	_AlphabetSize = 256 => only 0..255 appear in strings
    The files _str2set.c and _str2map.c declare character vectors  using
    this  size.  If you don't have unsigned char, your machine may treat
    char as unsigned anyway.

    Some string operations (*cmp, *chr) are explicitly defined in various
    UNIX manuals to use "native" comparison, so I have not used _char_ in
    them.  This package is meant to be compatible, not rational!
*/

extern	char	*strcat(/*char^,char^*/);
extern	char	*strncat(/*char^,char^,int*/);

extern	int	strcmp(/*char^,char^*/);
extern	int	strncmp(/*char^,char^,int*/);

#define streql	!strcmp
#define strneql	!strncmp	/* (str-N)-eql not str-(neq-l)! */

extern	char	*strcpy(/*char^,char^*/);
extern	char	*strncpy(/*char^,char^,int*/);

extern	int	strlen(/*char^*/);
extern	int	strnlen(/*char^,int*/);

extern	char	*strchr(/*char^,_char_*/);
extern	char	*strrchr(/*char^,_char_*/);
#define	index	strchr
#define	rindex	strrchr

extern	char	*strmov(/*char^,char^*/);
extern	char	*strnmov(/*char^,char^,int*/);

extern	void	strrev(/*char^,char^*/);
extern	void	strnrev(/*char^,char^,int*/);

extern	char	*strend(/*char^*/);
extern	char	*strnend(/*char^*/);

extern	char	*strpbrk(/*char^,char^*/);
extern	char	*strcpbrk(/*char^,char^*/);

extern	int	strspn(/*char^,char^*/);
extern	int	strcspn(/*char^,char^*/);

extern	char	*strtok(/*char^,char^*/);
extern	void	istrtok(/*char^,char^*/);

extern	char	*strpack(/*_char_^,_char_^,char^,int*/);
extern	char	*strcpack(/*_char_^,_char_^,char^,int*/);

extern	int	strrpt(/*char^,char^,int*/);
extern	int	strnrpt(/*char^,int,char^,int*/);

extern	void	strtrans(/*_char_^,_char_^,_char_^,_char_^*/);
extern	void	strntrans(/*_char_^,_char_^,int,_char_^,_char_^*/);

extern	char	*strtrim(/*char^,char^,char^,int*/);
extern	char	*strctrim(/*char^,char^,char^,int*/);

extern	char	*strfield(/*char^,int,int,int,int*/);
extern	char	*strkey(/*char^,char^,char^,char^*/);

extern	char	*strfind(/*char^,char^*/);
extern	char	*strrepl(/*char^,char^,char^,char^*/);

extern	void	bcopy(/*char^,char^,int*/);
extern	void	bmove(/*char^,char^,int*/);

extern	void	bfill(/*char^,int,char*/);
extern	void	bzero(/*char^,int*/);

extern	int	bcmp(/*char^,char^,int*/);
#define	beql	!bcmp

extern	int	ffs(/*int*/);
extern	int	ffc(/*int*/);

extern	char	*substr(/*char^,char^,int,int*/);

extern	char	*strxcat(/*VARARGS*/);
extern	char	*strxcpy(/*VARARGS*/);
extern	char	*strxmov(/*VARARGS*/);

extern	char	*strxncat(/*VARARGS*/);
extern	char	*strxncpy(/*VARARGS*/);
extern	char	*strxnmov(/*VARARGS*/);

#endif	NullS

#ifndef	memeql
#include "memory.h"
#endif	memeql

------ EOF ------
ls -l strings.h
cat >strkey.c <<'------ EOF ------'
/*  File   : strkey.c
    Author : Richard A. O'Keefe.
    Updated: 20 April 1984
    Defines: strkey()

    strkey(dst, head, tail, options)
	copies tail-head characters from head to dst according to the
	options.  If tail is NullS, it copies up to the terminating
	NUL of head.  This function is meant for doing comparisons as
	by sort(1).  The options are thus a string of characters
	taken from "bdfin".  In case the options came from somewhere
	else other letters are ignored.

	-b: leading layout characters are not copied.

	-d: only letters, digits, and blanks are copied.
	-i: only graphic characters (32..126) are copied.
	-n: a numeric string is copied.
	    These options are incompatible, and the last is taken.

	-f: upper case letters are copied as lower case.

    The question of what to do with a numeric string is  an  interesting
    one,  and  I  don't claim that this is a brilliant answer.  However,
    the solution used here does mean that the  caller  can  compare  two
    strings as strings without needing to know that they are numeric.  A
    number  is  copied  as  <sign><9  digits>.<remaining  digits>, where
    <sign> is '-' for a negative number and '0' for a  positive  number.
    The magic number 9 is defined to be DigitMagic.

    The idea is that to compare two lines using the keys
	-tx +m1.n1<flags> -m2.n2
    you do
	h1 = strfield(line1, m1, n1, 0, 'x');
	t1 = strfield(h1, 1, 0, 0, 'x');
	strkey(buff1, h1, t1, "flags");
	h2 = strfield(line2, m2, n2, 0, 'x');
	t2 = strfield(h2, 1, 0, 0, 'x');
	strkey(buff2, h2, t2, "flags");
	... strcmp(buff1, buff2) ...

    The point of all this, of course, is to make it easier to write new
    utilities which are compatible with sort(1) than ones which are not.
*/

#include "strings.h"

#define	DigitMagic 9

char *strkey(dst, head, tail, flags)
    register char *dst, *head, *tail;
    char *flags;
    {
	register int c;
	int b = 0;	/* b option? */
	int f = 0;	/* f option? */
	int k = 0;	/* 3->n, 2->d, 1->i, 0->none of them */

	while (*flags) switch (*flags++|32) {
	    case 'b':	b++;	break;
	    case 'f':	f++;	break;
	    case 'i':	k = 1;	break;
	    case 'd':	k = 2;	break;
	    case 'n':	k = 3;	break;
	    default : /*ignore*/break;
	}
	flags = dst;	/* save return value */

	if (tail == NullS) for (tail = head; *tail; tail++) ;

	if (b) while (head != tail && *head <= ' ') head++;

	switch (k) {
	    case 0:
		if (f) {
		    while (head != tail) {
			c = *head++;
			if (c >= 'A' && c <= 'Z') c |= 32;
			*dst++ = c;
		    }
		} else {
		    while (head != tail) *dst++ = *head++;
		}
		break;
	    case 1:
		if (f) {
		    while (head != tail) {
			c = *head++;
			if (c >= 32 && c <= 126) {
			    if (c >= 'A' && c <= 'Z') c |= 32;
			    *dst++ = c;
			}
		    }
		} else {
		    while (head != tail) {
			c = *head++;
			if (c >= 32 && c <= 126) *dst++ = c;
		    }
		}
		break;
	    case 2:
		if (f) f = 32;
		while (head != tail) {
		    c = *head++;
		    if (c >= '0' && c <= '9' ||  c >= 'a' && c <= 'z' || c == ' ') { 
			*dst++ = c;
		    } else
		    if (c >= 'A' && c <= 'Z') {
			*dst++ = c|f;
		    }
		}
		break;
	    case 3:
		if (*head == '-' && head != tail) {
		    *dst++ = *head++;
		    head++;
		} else {
		    *dst++ = '0';
		}
		b = 0;
		while (head != tail) {
		    c = *head;
		    if (c < '0' || c > '9') break;
		    b++, head++;
		}
		f = DigitMagic-b;
		while (--f >= 0) *dst++ = '0';
		head -= b;
		while (--b >= 0) *dst++ = *head++;
		if (*head == '.' && head != tail) {
		    *dst++ = *head++;
		    while (head != tail) {
			c = *head++;
			if (c < '0' || c > '9') break;
			*dst++ = c;
		    }
		    /* now remove trailing 0s and possibly the '.' as well */
		    while (*--dst == '0') ;
		    if (*dst != '.') dst++;
		}
		break;
	}
	*dst = NUL;
	return flags;	/* saved initial value of dst */
    }

------ EOF ------
ls -l strkey.c
cat >strlen.c <<'------ EOF ------'
/*  File   : strlen.c
    Author : Richard A. O'Keefe.
    Updated: 23 April 1984
    Defines: strlen()

    strlen(s) returns the number of characters in s, that is, the number
    of non-NUL characters found before the closing NULEosCh.  Note: some
    non-standard C compilers for 32-bit machines take int to be 16 bits,
    either put up with short strings or change int  to  long  throughout
    this package.  Better yet, BOYCOTT such shoddy compilers.
    Beware: the asm version works only if strlen(s) < 65536.
*/

#include "strings.h"

#if	VaxAsm

int strlen(s)
    char *s;
    {
	asm("locc  $0,$65535,*4(ap)");
	asm("subl3 r0,$65535,r0");
    }

#else  ~VaxAsm

int strlen(s)
    register char *s;
    {
	register int L;
	
	for (L = 0; *s++; L++) ;
	return L;
    }

#endif	VaxAsm

------ EOF ------
ls -l strlen.c
cat >strmov.c <<'------ EOF ------'
/*  File   : strmov.c
    Author : Richard A. O'Keefe.
    Updated: 20 April 1984
    Defines: strmov()

    strmov(dst, src) moves all the  characters  of  src  (including  the
    closing NUL) to dst, and returns a pointer to the new closing NUL in
    dst.   The similar UNIX routine strcpy returns the old value of dst,
    which I have never found useful.  strmov(strmov(dst,a),b) moves a//b
    into dst, which seems useful.
*/

#include "strings.h"

char *strmov(dst, src)
    register char *dst, *src;
    {
	while (*dst++ = *src++) ;
	return dst-1;
    }

------ EOF ------
ls -l strmov.c
cat >strncat.c <<'------ EOF ------'
/*  File   : strncat.c
    Author : Richard A. O'Keefe.
    Updated: 20 April 1984
    Defines: strncat()

    strncat(dst, src, n)  copies up to n characters of src to the end of
    dst.   As with strcat, it has to search for the end of dst.  Even if
    it abandons src early because n runs out it  will  still  close  dst
    with a NUL.  See also strnmov.
*/

#include "strings.h"

char *strncat(dst, src, n)
    register char *dst, *src;
    register int n;
    {
	char *save;

	for (save = dst; *dst++; ) ;
	for (--dst; --n >= 0; )
	    if (!(*dst++ = *src++)) return save;
	*dst = NUL;
	return save;
    }

------ EOF ------
ls -l strncat.c
cat >strncmp.c <<'------ EOF ------'
/*  File   : strncmp.c
    Author : Richard A. O'Keefe.
    Updated: 10 April 1984
    Defines: strncmp()

    strncmp(s, t, n) compares the first n characters of s and t.
    If they are the same in the first n characters it returns 0,
    otherwise it returns the same value as strcmp(s, t) would.
*/

#include "strings.h"

int strncmp(s, t, n)
    register char *s, *t;
    register int n;
    {
	while (--n >= 0) {
	    if (*s != *t++) return s[0]-t[-1];
	    if (!*s++) return 0;
	}
	return 0;
     }

------ EOF ------
ls -l strncmp.c
cat >strncpy.c <<'------ EOF ------'
/*  File   : strncpy.c
    Author : Richard A. O'Keefe.
    Updated: 20 April 1984
    Defines: strncpy()

    strncpy(dst, src, n) copies up to n characters of src  to  dst.   It
    will  pad  dst  on the right with NUL or truncate it as necessary to
    ensure that n characters exactly are transferred.   It  returns  the
    old value of dst as strcpy does.
*/

#include "strings.h"

char *strncpy(dst, src, n)
    register char *dst, *src;
    register int n;
    {
	char *save;

	for (save = dst;  --n >= 0; ) {
	    if (!(*dst++ = *src++)) {
		while (--n >= 0) *dst++ = NUL;
		return save;
	    }
	}
	return save;
    }

------ EOF ------
ls -l strncpy.c
cat >strnend.c <<'------ EOF ------'
/*  File   : strnend.c
    Author : Richard A. O'Keefe.
    Updated: 1 June 1984
    Defines: strnend()

    strnend(src, len)
    returns a pointer to just after the end of the string src, which is
    terminated by a NUL character, or by exhaustion of the length bound
    len.  That is, strnend(s,L)-s = strnlen(s,L).  s+strnlen(s,L) could
    of course be used instead, but this is sometimes clearer.
    Beware: the asm version works only if 0 <= len < 65535.
*/

#include "strings.h"

#if	VaxAsm

char *strnend(src, len)
    char *src;
    int len;
    {
	asm("locc $0,8(ap),*4(ap)");
	asm("movl r1,r0");
    }

#else  ~VaxAsm 

char *strnend(src, len)
    register char *src;
    register int len;
    {
	while (--len >= 0 && *src) src++;
	return src;
    }

#endif	VaxAsm

------ EOF ------
ls -l strnend.c
cat >strnlen.c <<'------ EOF ------'
/*  File   : strnlen.c
    Author : Richard A. O'Keefe.
    Updated: 10 April 1984
    Defines: strnlen()

    strnlen(src, len)
    returns the number of characters up to the first NUL in src, or len,
    whichever is smaller.  This is the same as strnend(src,len)-src.

    Beware: the VaxAsm version only works for 0 <= len < 65535.
*/

#include "strings.h"

#if	VaxAsm

int strnlen(src, len)
    char *src;
    int len;
    {
	asm("locc $0,8(ap),*4(ap)");
	asm("subl3 4(ap),r1,r0");
    }

#else  ~VaxAsm

int strnlen(s, n)
    register char *s;
    register int n;
    {
	register int L;
	
	for (L = 0; --n >= 0 && *s++; L++) ;
	return L;
    }

#endif	VaxAsm

------ EOF ------
ls -l strnlen.c
cat >strnmov.c <<'------ EOF ------'
/*  File   : strnmov.c
    Author : Richard A. O'Keefe.
    Updated: 20 April 1984
    Defines: strnmov()

    strnmov(dst, src, n) moves up to n characters of  src  to  dst.   It
    always  moves  exactly n characters to dst; if src is shorter than n
    characters dst will be extended on the right with NULs, while if src
    is longer than n characters dst will be a truncated version  of  src
    and  will  not  have  a closing NUL.  The result is a pointer to the
    first NUL in dst, or is dst+n if dst was truncated.
*/

#include "strings.h"

char *strnmov(dst, src, n)
    register char *dst, *src;
    register int n;
    {
	while (--n >= 0) {
	    if (!(*dst++ = *src++)) {
		src = dst-1;
		while (--n >= 0) *dst++ = NUL;
		return src;
	    }
	}
	return dst;
    }

------ EOF ------
ls -l strnmov.c
cat >strnrev.c <<'------ EOF ------'
/*  File   : strnrev.c
    Author : Richard A. O'Keefe.
    Updated: 1 June 1984
    Defines: strnrev()

    strnrev(dst, src, len)
    copies all the characters of src to dst, in REVERSE order.  If src
    was terminated by a NUL character, so will dst be, otherwise dst &
    src are both exactly len non-NUL characters long.  This returns no
    result.  It is to strrev as strncpy is to strcpy.

    Note: this function is perfectly happy to reverse a string into the
    same place, strnrev(x, x, L) will work.
    It will not work for partially overlapping source and destination.
*/

#include "strings.h"

void strnrev(dsta, srca, len)
    register char *dsta, *srca;
    register int len;
    {
	register char *dstz, *srcz;
	register int t;
	/*  On a machine which doesn't supply 6 register variables,
	    you could #define t len, as the two variables don't overlap.
	*/

	for (srcz = srca; --len >= 0 && *srcz; srcz++) ;
	dstz = dsta+(srcz-srca);
	/*  If srcz was stopped by len running out, it points just after
	    the last character of the source string, and it and dstz are
	    just right.  Otherwise, it was stopped by hitting NUL, and is
	    in the right place, but dstz should get a NUL as well.
	*/
	if (len >= 0) *dstz = NUL;
	/*  That was the very last use of len  */
	while (srcz > srca) {
	    t = *--srcz;
	    *--dstz = *srca++;
	    *dsta++ = t;
	}
    }

------ EOF ------
ls -l strnrev.c
cat >strnrpt.c <<'------ EOF ------'
/*  File   : strnrpt.c
    Author : Richard A. O'Keefe.
    Updated: 20 April 1984
    Defines: strnrpt()

    strnrpt(dst, n, src, k) "RePeaTs" the string src into dst  k  times,
    but  will  truncate  the  result at n characters if necessary.  E.g.
    strnrpt(dst, 7, "hack ", 2) will move "hack ha" to dst  WITHOUT  the
    closing  NUL.   The  result  is  the number of characters moved, not
    counting the closing NUL.  Equivalent to strrpt-ing into an infinite
    buffer and then strnmov-ing the result.
*/

#include "strings.h"

int strnrpt(dst, n, src, k)
    register char *dst;
    register int n;
    char *src;
    int k;
    {
	char *save;

	for (save = dst; --k >= 0; dst--) {
	    register char *p;
	    for (p = src; ; ) {
		if (--n < 0) return dst-save;
		if (!(*dst++ = *p++)) break;
	    }
	}
	return dst-save;
    }

------ EOF ------
ls -l strnrpt.c
cat >strntrans.c <<'------ EOF ------'
/*  File   : strntrans.c
    Author : Richard A. O'Keefe.
    Updated: 2 June 1984
    Defines: strntrans()

    strntrans(dst, src, len, from, to)
    Moves characters from src to dst, translating characters in from[]
    to the corresponding characters in to[], until either len characters
    have been moved or a NUL has been moved.  If fewer than len characters
    are moved, the remainder of dst will be filled with NULs, much like
    strncpy and family.  No value is returned.

    Apology: in the previous distribution of this package, strntrans was
    defined the way memtrans is now defined.  This is more consistent with
    the general naming conventions.
*/

#include "strings.h"
#include "_str2map.h"

#if	VaxAsm

void strntrans(dst, src, len, from, to)
    _char_ *dst, *src, *from, *to;
    int len;
    {
	_str2map(0, from, to);
	asm("movtuc 20(ap),*8(ap),$0,__map_vec,20(ap),*4(ap)");
	/* now pad the destination out with NUL characters */
	asm("movc5 $0,*8(ap),$0,r4,(r5)");
    }

#else  ~VaxAsm

void strntrans(dst, src, len, from, to)
    register _char_ *dst, *src;
    register int len;
    _char_ *from, *to;
    {
	_str2map(0, from, to);
	while (--len >= 0 && (*dst++ = _map_vec[*src++])) ;
	while (--len >= 0) *dst++ = NUL;
    }

#endif	VaxAsm

------ EOF ------
ls -l strntrans.c
cat >strpack.c <<'------ EOF ------'
/*  File   : strpack.c
    Author : Richard A. O'Keefe.
    Updated: 20 April 1984
    Defines: strpack()

    strpack(dst, src, set, c)
    copies characters from src to dst, stopping when it finds a NUL.  If
    c is NUL, characters in set are not copied to dst.  If c is not NUL,
    sequences of characters from set are copied as a single c.
    strpack(d, s, " \t", ' ') can be used to compress white space,
    strpack(d, s, " \t", NUL) to eliminate it.  To translate  characters
    in  set to c without compressing runs, see strtrans(). The result is
    the address of the NUL byte now terminating dst.  Note that dst  may
    safely be the same as src.
*/

#include "strings.h"
#include "_str2set.h"

char *strpack(dst, src, set, c)
    register _char_ *dst, *src;
    char *set;
    register int c;
    {
	register int chr;

	_str2set(set);
	while (chr = *src++) {
	    if (_set_vec[chr] == _set_ctr) {
		while ((chr = *src++) && _set_vec[chr] == _set_ctr) ;
		if (c) *dst++ = c;	/* 1. If you don't want trailing */
		if (!chr) break;	/* 2. things turned into "c", swap */
	    }				/* lines 1 and 2. */
	    *dst++ = chr;
	}
	*dst = 0;
	return dst;
    }

------ EOF ------
ls -l strpack.c
cat >strpbrk.c <<'------ EOF ------'
/*  File   : strpbrk.c
    Author : Richard A. O'Keefe.
    Updated: 11 April 1984
    Defines: strpbrk()

    strpbrk(s1, s2) returns NullS if no character of s2 occurs in s1, or
    a pointer to the first character of s1 which occurs in s2  if  there
    is one.  It generalises strchr (v7=index).  It wouldn't be useful to
    consider NUL as part of s2, as that would occur in every s1.
*/

#include "strings.h"
#include "_str2set.h"

char *strpbrk(str, set)
    register _char_ *str;
    char *set;
    {
	_str2set(set);
	while (_set_vec[*str] != _set_ctr)
	    if (!*str++) return NullS;
	return str;
    }

------ EOF ------
ls -l strpbrk.c
cat >strpref.c <<'------ EOF ------'
/*  File   : strpref.c
    Author : Richard A. O'Keefe.
    Updated: 11 April 1984
    Defines: strpref()

    strpref(src, prefix)
    checks whether prefix is a prefix of src.  If it is not, the  result
    is  NullS.  If it is, the result is a pointer to the first character
    of src after the prefix (src+strlen(prefix)).  You can use this in a
    conditional as a test: if (strpref(....)), but this is only portable
    provided you remember to declare strpref() properly or use strings.h
    as if (...) tests numbers against 0 and pointers against a suitable
    cast of 0; there is no guarantee that (char*)0 is represented by the
    same bit pattern as (int)0.
*/

#include "strings.h"

char *strpref(src, prefix)
    register char *src, *prefix;
    {
	while (*prefix) if (*src++ != *prefix++) return NullS;
	return src;
    }

------ EOF ------
ls -l strpref.c
cat >strrchr.c <<'------ EOF ------'
/*  File   : strrchr.c
    Author : Richard A. O'Keefe.
    Updated: 10 April 1984
    Defines: strrchr(), rindex()

    strrchr(s, c) returns a pointer to the  last  place  in  s  where  c
    occurs,  or  NullS if c does not occur in s. This function is called
    rindex in V7 and 4.?bsd systems; while not ideal the name is clearer
    than strrchr, so rindex  remains  in  strings.h  as  a  macro.   NB:
    strrchr  looks  for single characters, not for sets or strings.  The
    parameter 'c' is declared 'int' so it will go in a register; if your
    C compiler is happy with register char change it to that.
*/

#include "strings.h"

char *strrchr(s, c)
    register _char_ *s;
    register int c;
    {
	register char *t;

	t = NullS;
	do if (*s == c) t = s; while (*s++);
	return t;
    }

------ EOF ------
ls -l strrchr.c
cat >strrepl.c <<'------ EOF ------'
/*  File   : strrepl.c
    Author : Richard A. O'Keefe.
    Updated: 23 April 1984
    Defines: strrepl()

    strrepl(dst, src, pat, rep, times) copies src to dst, replacing  the
    first "times" non-overlapping instances of pat by rep.  pat is not a
    regex(3) pattern, it is a  literal  string  which  must  be  matched
    exactly.   As  a  special hack, since strfind claims to find "" just
    once at the end of the src string, strrepl does a strcat when pat is
    an empty string "".  If times <= 0, it is just strmov.

    The result is a pointer to the NUL which now terminates dst.

    BEWARE: even when rep is shorter than pat it is NOT necessarily safe
    for dst to be the same as src.  ALWAYS make sure dst and src do not/
    will not overlap.  You have been warned.

    There really ought to be a strnrepl with a bound for the size of the
    destination string, but there isn't.
*/

#include "strings.h"
#include "_str2pat.h"

char *strrepl(dst, src, pat, rep, times)
    char *dst, *src, *pat, *rep;
    int times;
    {
	register char *s, *p;
	register int c, lastch;

	pat = _str2pat(pat);
	if (times <= 0) {
	    for (p = dst, s = src; *p++ = *s++; ) ;
	    return p-1;
	}
	if (_pat_lim < 0) {
	    for (p = dst, s = src; *p++ = *s++; ) ;
	    for (--p, s = rep; *p++ = *s++; ) ;
	    return p-1;
 	}
	/*  The pattern is non-empty and times is positive  */
	c = _pat_lim, lastch = pat[c];
	for (;;) {
	    for (s = src, p = dst; --c >= 0; )
		if (!(*p++ = *s++)) return p-1;
	    c = *s, src = s, dst = p;
	    if (c == lastch) {
		for (s -= _pat_lim, p = pat; *p; )
		    if (*s++ != *p++) goto not_yet;
		for (p = dst-_pat_lim, s = rep; *p++ = *s++; ) ;
		--p;
		if (--times == 0) {
		    for (s = src; *p++ = *++s; ) ;
		    return p-1;
		}
		dst = p, src++, c = _pat_lim;
	    } else {
not_yet:	c = _pat_vec[c];
	    }
	}
    }
------ EOF ------
ls -l strrepl.c
cat >strrev.c <<'------ EOF ------'
/*  File   : strrev.c
    Author : Richard A. O'Keefe.
    Updated: 1 June 1984
    Defines: strrev()

    strrev(dst, src)
    copies all the characters of src to dst, in REVERSE order.   Dst is
    properly terminated with a NUL character.  There is no result.
    Example: strrev(x, "able was I er") moves "re I saw elba" to x.

    Note: this function is perfectly happy to reverse a string into the
    same place, strrev(x, x) will work.  That is why it looks so hairy.
    It will not work for partially overlapping source and destination.
*/

#include "strings.h"

void strrev(dsta, srca)
    register char *dsta, *srca;
    {
	register char *dstz, *srcz;
	register int t;			/* should be char */

	for (srcz = srca; *srcz++; ) ;
	srcz--;
	dstz = dsta+(srcz-srca);
	/*  Now srcz points to the NUL terminating src,
	    and dstz points to where the terminating NUL for dst belongs.
	*/
	*dstz = NUL;
	while (srcz > srca) {
	    /*  This is guaranteed safe by K&R, since srcz and srca
		point "into the same array".
	    */
	    t = *--srcz;
	    *--dstz = *srca++;
	    *dsta++ = t;
	}
    }

------ EOF ------
ls -l strrev.c
cat >strrpt.c <<'------ EOF ------'
/*  File   : strrpt.c
    Author : Richard A. O'Keefe.
    Updated: 20 April 1984
    Defines: strrpt()

    strrpt(dst, src, k) "RePeaTs" the string src into dst k times.  E.g.
    strrpt(dst, "hack ", 2) will move "hack hack" to dst.  If k <= 0 it
    does nothing.  The result is the number of characters moved, except
    for the closing NUL.  src may be "" but may not of course be NullS.
*/

#include "strings.h"

int strrpt(dst, src, k)
    register char *dst;
    char *src;
    int k;
    {
	char *save;

	for (save = dst; --k >= 0; --dst) {
	    register char *p;
	    for (p = src; *dst++ = *p++; ) ;
	}
	return dst-save;
    }

------ EOF ------
ls -l strrpt.c
cat >strspn.c <<'------ EOF ------'
/*  File   : strspn.c
    Author : Richard A. O'Keefe.
    Updated: 11 April 1984
    Defines: strspn()

    strspn(s1, s2) returns the  length  of  the  longest  prefix  of  s1
    consisting  entirely  of characters in s2.  NUL is not considered to
    be in s2, and _str2set will not include it in the set.
*/

#include "strings.h"
#include "_str2set.h"

int strspn(str, set)
    register _char_ *str;
    char *set;
    {
	register int L;

	_str2set(set);
	for (L = 0; _set_vec[*str++] == _set_ctr; L++) ;
	return L;
    }

------ EOF ------
ls -l strspn.c
cat >strsuff.c <<'------ EOF ------'
/*  File   : strsuff.c
    Author : Richard A. O'Keefe.
    Updated: 11 April 1984
    Defines: strsuff()

    strsuff(src, suffix)
    checks whether suffix is a suffix of src.  If it is not, the  result
    is NullS.  If it is, the result is a pointer to the character of src
    where suffix starts (which is the same as src+strlen(src)-strlen(prefix) ).
    See strpref.c for a comment about using if (strsuff(...)) in C.
*/

#include "strings.h"

char *strsuff(src, suffix)
    register char *src, *suffix;
    {
	register int L;	/* length of suffix */

	for (L = 0; *suffix++; L++)
	    if (!*src++) return NullS;
	while (*src++) ;
	for (--src, --suffix; --L >= 0; )
	    if (*--src != *--suffix) return NullS;
	return src;
    }

------ EOF ------
ls -l strsuff.c
cat >strtok.c <<'------ EOF ------'
/*  File   : strtok.c
    Author : Richard A. O'Keefe.
    Updated: 11 April 1984
    Defines: istrtok(), strtok()

    strtok(src, set)
	skips over initial characters of src[] which occur in set[].
	The result is a pointer to the first character of src[]
	which does not occur in set[].  It then skips until it finds
	a character which does occur in set[], and changes it to NUL.
	If src is NullS, it is as if you had specified the place
	just after the last NUL was written.  If src[] contains no
	characters which are not in set[] (e.g. if src == "") the
	result is NullS.

	To read a sequence of words separated by spaces you might write
	p = strtok(sequence, " ");
	while (p) {process_word(p); p = strtok(NullS, " ");}
	This is unpleasant, so there is also a function

    istrtok(src, set)
	which builds the set and notes the source string for future
	reference.  With this function, you can write

	for (istrtok(wordlist, " \t"); p = strtok(NullS, NullS); )
	    process_word(p);
*/

#include "strings.h"
#include "_str2set.h"

static	char	*oldSrc = "";

void istrtok(src, set)
    char *src, *set;
    {
	_str2set(set);
	if (src != NullS) oldSrc = src;
    }


char *strtok(src, set)
    register char *src;
    char *set;
    {
	char *save;

	_str2set(set);
	if (src == NullS) src = oldSrc;
	while (_set_vec[*src] == _set_ctr) src++;
	if (!*src) return NullS;
	save = src;
	while (_set_vec[*++src] != _set_ctr) ;
	*src++ = NUL;
	oldSrc = src;
	return save;
    }

------ EOF ------
ls -l strtok.c
cat >strtrans.c <<'------ EOF ------'
/*  File   : strtrans.c
    Author : Richard A. O'Keefe.
    Updated: 2 June 1984
    Defines: strtrans()

    strtrans(dst, src, from, to)
    copies characters from src[] to dst[], stopping when dst gets a  NUL
    character,  translating  characters  in  from[] to the corresponding
    characters in to[]. Courtesy of _str2map, if from or to is null  its
    previous  value  will be used, and if both are NullS the table won't
    be rebuilt.  Note that copying stops when a NUL is put  into  dst[],
    which  can  normally  happen  only  when a NUL has been fetched from
    src[], but if you have built your own translation table  it  may  be
    earlier  (if  some  character  is mapped to NUL) or later (if NUL is
    mapped to something else).  No value is returned.

    The VaxAsm version only works from strlen(src) < 2^16.
*/

#include "strings.h"
#include "_str2map.h"

#if	VaxAsm

void strtrans(dst, src, from, to)
    _char_ *dst, *src, *from, *to;
    {
	_str2map(0, from, to);
	asm("movtuc $65535,*8(ap),$0,__map_vec,$65535,*4(ap)");
	/*  That stops when the "escape" is found, and we want to move it */
	asm("movb $0,(r5)");
    }

#else  ~VaxAsm

void strtrans(dst, src, from, to)
    register _char_ *dst, *src;
    _char_ *from, *to;
    {
	_str2map(0, from, to);
	while (*dst++ = _map_vec[*src++]) ;
    }

#endif	VaxAsm

------ EOF ------
ls -l strtrans.c
cat >strtrim.c <<'------ EOF ------'
/*  File   : strtrim.c
    Author : Richard A. O'Keefe.
    Updated: 20 April 1984
    Defines: strtrim()

    strtrim(dst, src, set, ends)
    copies src to dst, but will skip leading characters in set if "ends"
    is <= 0, and will skip trailing characters in set if ends is >= 0.
    Thus there are three cases:
	ends < 0 :	trim a prefix
	ends = 0 :	trim a prefix and a suffix both
	ends > 0 :	trim a suffix
    To compress internal runs, see strpack.  The normal use of this is
    strtrim(buffer, buffer, " \t", 0);  The result is the address of the
    NUL which now terminates dst.
*/

#include "strings.h"
#include "_str2set.h"

char *strtrim(dst, src, set, ends)
    register char *dst, *src;
    char *set;
    int ends;
    {
	_str2set(set);
	if (ends <= 0) {
	    while (_set_vec[*src] == _set_ctr) src++;
	}
	if (ends >= 0) {
	    register int chr;
	    register char *save = dst;
	    while (chr = *src++) {
		*dst++ = chr;
		if (_set_vec[chr] != _set_ctr) save = dst;
	    }
	    dst = save, *dst = NUL;
	} else {
	    while (*dst++ = *src++) ;
	    --dst;
	}
	return dst;
    }

------ EOF ------
ls -l strtrim.c
cat >strxcat.c <<'------ EOF ------'
/*  File   : strxcat.c
    Author : Richard A. O'Keefe.
    Updated: 25 may 1984
    Defines: strxcat()

    strxcat(dst, src1, ..., srcn, NullS)
    moves the concatenation of dst,src1,...,srcn to dst, terminates it
    with a NUL character, and returns the original value of dst.
    It is just like strcat except that it concatenates multiple sources.
    Equivalence: strxcat(d, s1, ..., sn) <=> strxcpy(d, d, s1, ..., sn).
    Beware: the last argument should be the null character pointer.
    Take VERY great care not to omit it!  Also be careful to use NullS
    and NOT to use 0, as on some machines 0 is not the same size as a
    character pointer, or not the same bit pattern as NullS.
*/

#include "strings.h"
#include <varargs.h>

/*VARARGS*/
char *strxcat(va_alist)
    va_dcl
    {
	va_list pvar;
	register char *dst, *src;
	char *bogus;

	va_start(pvar);
	dst = va_arg(pvar, char *);
	bogus = dst;
	while (*dst) dst++;
	src = va_arg(pvar, char *);
	while (src != NullS) {
	    while (*dst++ = *src++) ;
	    dst--;
	    src = va_arg(pvar, char *);
	}
	return bogus;
    }

------ EOF ------
ls -l strxcat.c
cat >strxcpy.c <<'------ EOF ------'
/*  File   : strxcpy.c
    Author : Richard A. O'Keefe.
    Updated: 25 may 1984
    Defines: strxcpy()

    strxcpy(dst, src1, ..., srcn, NullS)
    moves the concatenation of src1,...,srcn to dst, terminates it
    with a NUL character, and returns the original value of dst.
    It is just like strcpy except that it concatenates multiple sources.
    Beware: the last argument should be the null character pointer.
    Take VERY great care not to omit it!  Also be careful to use NullS
    and NOT to use 0, as on some machines 0 is not the same size as a
    character pointer, or not the same bit pattern as NullS.
*/

#include "strings.h"
#include <varargs.h>

/*VARARGS*/
char *strxcpy(va_alist)
    va_dcl
    {
	va_list pvar;
	register char *dst, *src;
	char *bogus;

	va_start(pvar);
	dst = va_arg(pvar, char *);
	bogus = dst;
	src = va_arg(pvar, char *);
	while (src != NullS) {
	    while (*dst++ = *src++) ;
	    dst--;
	    src = va_arg(pvar, char *);
	}
	*dst = NUL;	/* there might have been no sources! */
	return bogus;
    }

------ EOF ------
ls -l strxcpy.c
cat >strxmov.c <<'------ EOF ------'
/*  File   : strxmov.c
    Author : Richard A. O'Keefe.
    Updated: 25 may 1984
    Defines: strxmov()

    strxmov(dst, src1, ..., srcn, NullS)
    moves the concatenation of src1,...,srcn to dst, terminates it
    with a NUL character, and returns a pointer to the terminating NUL.
    It is just like strmov except that it concatenates multiple sources.
    Beware: the last argument should be the null character pointer.
    Take VERY great care not to omit it!  Also be careful to use NullS
    and NOT to use 0, as on some machines 0 is not the same size as a
    character pointer, or not the same bit pattern as NullS.
*/

#include "strings.h"
#include <varargs.h>

/*VARARGS*/
char *strxmov(va_alist)
    va_dcl
    {
	va_list pvar;
	register char *dst, *src;

	va_start(pvar);
	dst = va_arg(pvar, char *);
	src = va_arg(pvar, char *);
	while (src != NullS) {
	    while (*dst++ = *src++) ;
	    dst--;
	    src = va_arg(pvar, char *);
	}
	*dst = NUL;	/* there might have been no sources! */
	return dst;
    }

------ EOF ------
ls -l strxmov.c
cat >strxncat.c <<'------ EOF ------'
/*  File   : strxncat.c
    Author : Richard A. O'Keefe.
    Updated: 2 June 1984
    Defines: strxncat()

    strxncat(dst, len, src1, ..., srcn, NullS)
    moves the first len bytes of the concatenation of dst,src1,...,srcn
    to dst, terminating it with a NUL character unless len runs out, and
    returns the original value of dst.
    It is just like strcat except that it concatenates multiple sources.
    Roughly, strxncat(d, L, s1, ..., sn) <=> strxncpy(d, L, d, s1, ..., sn).
    Beware: the last argument should be the null character pointer.
    Take VERY great care not to omit it!  Also be careful to use NullS
    and NOT to use 0, as on some machines 0 is not the same size as a
    character pointer, or not the same bit pattern as NullS.

    Note: strxncat is like strncat in that it will add at most one NUL,
    and may in consequence move fewer than len characters.  No so the
    strxncpy and strxnmov routines, which resemble strncpy and strnmov.
*/

#include "strings.h"
#include <varargs.h>

/*VARARGS*/
char *strxncat(va_alist)
    va_dcl
    {
	va_list pvar;
	register char *dst, *src;
	register int len;
	char *bogus;

	va_start(pvar);
	dst = va_arg(pvar, char *);
	bogus = dst;
	len = va_arg(pvar, int);
	while (*dst)
	    if (--len < 0) return bogus;
	    else dst++;
	src = va_arg(pvar, char *);
	while (src != NullS) {
	    do if (--len < 0) return bogus;
	    while (*dst++ = *src++);
	    dst--;
	    src = va_arg(pvar, char *);
	}
	return bogus;
    }

------ EOF ------
ls -l strxncat.c
cat >strxncpy.c <<'------ EOF ------'
/*  File   : strxncpy.c
    Author : Richard A. O'Keefe.
    Updated: 25 may 1984
    Defines: strxncpy()

    strxncpy(dst, len, src1, ..., srcn, NullS)
    moves the first len characters of the concatenation of src1,...,srcn
    to dst.  If there aren't that many characters, a NUL character will
    be added to the end of dst to terminate it properly.  This gives the
    same effect as calling strxcpy(buff, src1, ..., srcn, NullS) with a
    large enough buffer, and then calling strncpy(dst, buff, len).
    It is just like strncpy except that it concatenates multiple sources.
    Beware: the last argument should be the null character pointer.
    Take VERY great care not to omit it!  Also be careful to use NullS
    and NOT to use 0, as on some machines 0 is not the same size as a
    character pointer, or not the same bit pattern as NullS.

    Note: strxncpy is like strncpy in that it always moves EXACTLY len
    characters; dst will be padded on the right with NUL characters as
    needed.  strxnmov does the same.  strxncat, like strncat, does NOT.
*/

#include "strings.h"
#include <varargs.h>

/*VARARGS*/
char *strxncpy(va_alist)
    va_dcl
    {
	va_list pvar;
	register char *dst, *src;
	register int len;
	char *bogus;

	va_start(pvar);
	dst = va_arg(pvar, char *);
	bogus = dst;
	len = va_arg(pvar, int);
	src = va_arg(pvar, char *);
	while (src != NullS) {
	    do if (--len < 0) return bogus;
	    while (*dst++ = *src++);
	    dst--;
	    src = va_arg(pvar, char *);
	}
	for (src = dst; --len >= 0; *dst++ = NUL) ;
	return bogus;
    }

------ EOF ------
ls -l strxncpy.c
cat >strxnmov.c <<'------ EOF ------'
/*  File   : strxnmov.c
    Author : Richard A. O'Keefe.
    Updated: 2 June 1984
    Defines: strxnmov()

    strxnmov(dst, len, src1, ..., srcn, NullS)
    moves the first len characters of the concatenation of src1,...,srcn
    to dst.  If there aren't that many characters, a NUL character will
    be added to the end of dst to terminate it properly.  This gives the
    same effect as calling strxcpy(buff, src1, ..., srcn, NullS) with a
    large enough buffer, and then calling strnmov(dst, buff, len).
    It is just like strnmov except that it concatenates multiple sources.
    Beware: the last argument should be the null character pointer.
    Take VERY great care not to omit it!  Also be careful to use NullS
    and NOT to use 0, as on some machines 0 is not the same size as a
    character pointer, or not the same bit pattern as NullS.

    Note: strxnmov is like strnmov in that it always moves EXACTLY len
    characters; dst will be padded on the right with NUL characters as
    needed.  strxncpy does the same.  strxncat, like strncat, does NOT.
*/

#include "strings.h"
#include <varargs.h>

/*VARARGS*/
char *strxnmov(va_alist)
    va_dcl
    {
	va_list pvar;
	register char *dst, *src;
	register int len;

	va_start(pvar);
	dst = va_arg(pvar, char *);
	len = va_arg(pvar, int);
	src = va_arg(pvar, char *);
	while (src != NullS) {
	    do if (--len < 0) return dst;
	    while (*dst++ = *src++);
	    dst--;
	    src = va_arg(pvar, char *);
	}
	for (src = dst; --len >= 0; *dst++ = NUL) ;
	return src;
    }

------ EOF ------
ls -l strxnmov.c
cat >substr.c <<'------ EOF ------'
/*  File   : substr.c
    Author : Richard A. O'Keefe.
    Updated: 25 May 1984
    Defines: substr()

    substr(destination, source, offset, length)
    copies length bytes from source+offset to destination, stopping
    early if a NUL is encountered.  The difference between this and
    strncpy(destination, source+offset, length)
    is that if the offset is negative, it has the same effect as 0,
    and if it exceeds strlen(source), it has the same effect as
    strlen(source).  If either of these boundaries is hit, or if
    a NUL is encountered before length bytes have been moved, the
    value of errno will be EDOM, otherwise it is guaranteed to be 0.
    That is:
	errno == 0 <=> (0 <= offset <= strlen(source)
			&& 0 <= length && <= strlen(source)-offset)
    You may accept the sensible result produced when these boundary
    conditions are violated, or you may treat it as an error, as you
    will.  There is an algebra of sequences in which this treatment
    of boundary conditions makes sense.

    After the substring of source is moved to destination, a NUL byte
    is moved to terminate the string, and the result is a pointer to
    this NUL byte, ready to have new stuff stuck on the end.

    I suppose this should be called strsub, but I can't stick it.
*/

#include "strings.h"
#include <errno.h>

extern	int	errno;			/* why isn't this in errno.h?? */


char *substr(dst, src, off, len)
    register char *dst, *src;
    register int off, len;
    {
	errno = off < 0 || len < 0 ? EDOM : 0;

	while (--off >= 0)
	    if (!*src++) {		/* We've hit the end */
		errno = EDOM;		/* report boundary violation */
		*dst = NUL;		/* return empty string */
		return dst;
	    }
	while (--len >= 0)
	    if (!(*dst++ = *src++)) {	/* We've hit the end */
		errno = EDOM;
		return dst-1;		/* dst is already terminated */
	    }
	*dst = NUL;			/* terminate dst with NUL */
	return dst;
    }

------ EOF ------
ls -l substr.c
cat >xstring.3c <<'------ EOF ------'
.TH XSTRING 3C local
.SH NAME
strxcat, strxncat, strxcpy, strxncpy, strxmov, strxnmov \- string operations with variable number of arguments
.SH SYNOPSIS
.nf
.PP
.B "#include <strings.h>"
.PP
.B "char \(**strxcat(dst, src1, src2, ..., NullS)"
.B "    char \(**dst, \(**src1, \(**src2, ...;"
.PP
.B "char \(**strxncat(dst, len, src1, src2, ..., NullS)"
.B "    char \(**dst, \(**src1, \(**src2, ...;"
.B "    int len;"
.PP
.B "char \(**strxcpy(dst, src1, src2, ..., NullS)"
.B "    char \(**dst, \(**src1, \(**src2, ...;"
.PP
.B "char \(**strxncpy(dst, len, src1, src2, ..., NullS)"
.B "    char \(**dst, \(**src1, \(**src2, ...;"
.B "    int len;"
.PP
.B "char \(**strxmov(dst, src1, src2, ..., NullS)"
.B "    char \(**dst, \(**src1, \(**src2, ...;"
.PP
.B "char \(**strxnmov(dst, len, src1, src2, ..., NullS)"
.B "    char \(**dst, \(**src1, \(**src2, ...;"
.B "    int len;"
.SH DESCRIPTION
These functions operate on null-terminated strings.
They are equivalent to the corresponding functions
.IR strcat (3c),
.IR strncat (3c),
.IR strmov (3c),
.IR strnmov (3c),
.IR strcpy (3c),
and
.IR strncpy (3c),
except that they allow more than one source string to be supplied.
.IR Strxcat ,
.IR strxncat ,
.IR strxcpy ,
and
.I strxncpy
return their first argument (the destination pointer).
.I Strxmov
and
.I strxnmov
return a pointer to just after the last non-NUL character
moved to the destination.  This is the same convention that
is used throughout the strings package.
Except as implied by the length parameter
.IR len ,
they do not check for overflow of any receiving string.
.PP
.I Strxcat
appends a copy of the strings
.IR src1 ,
.IR src2 ,
and so on, to
.IR dst .
The resulting string will always be NUL-terminated.
.I Strxncat
copies at most
.I len
characters.
The resulting string will be NUL-terminated if fewer than
.I len
characters were moved.  At most one NUL is added.
.PP
.I Strxcpy
copies the strings
.IR src1 ,
.IR src2 ,
and so on, into
.IR dst .
.I Strxncpy
copies at most
.I len
characters.
The resulting string will not be null-terminated if
.I len
or more characters were in the source strings.
By analogy with
.IR strncpy ,
.I dst
will be padded on the right with NUL characters to exactly
.I len
bytes.
.PP
Apart from their return value,
.I strxmov
and
.I strxnmov
have the same effect as
.I strxcpy
and
.IR strxncpy .
.SH CAVEATS
The placement for the
.I len
variable is different from the placement in the functions
.IR strncat (3c),
.IR strncpy (3c),
and
.IR strnmov (3c).
------ EOF ------
ls -l xstring.3c
........................................................................

Share and enjoy.
	Sirius Cybernetics Corporation.