nl.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 1995 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
#pragma ident "%Z%%M% %I% %E% SMI"
#include <locale.h>
#include <regexpr.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <wchar.h>
#include <wctype.h>
#include <limits.h>
#define EXPSIZ 512
#ifdef XPG4
#define USAGE "usage: nl [-p] [-b type] [-d delim] [ -f type] " \
"[-h type] [-i incr] [-l num] [-n format]\n" \
"[-s sep] [-v startnum] [-w width] [file]\n"
#else
#define USAGE "usage: nl [-p] [-btype] [-ddelim] [ -ftype] " \
"[-htype] [-iincr] [-lnum] [-nformat] [-ssep] " \
"[-vstartnum] [-wwidth] [file]\n"
#endif
#ifdef u370
int nbra, sed; /* u370 - not used in nl.c, but extern in regexp.h */
#endif
static int width = 6; /* Declare default width of number */
static char nbuf[100]; /* Declare bufsize used in convert/pad/cnt routines */
static char *bexpbuf; /* Declare the regexp buf */
static char *hexpbuf; /* Declare the regexp buf */
static char *fexpbuf; /* Declare the regexp buf */
static char delim1 = '\\';
static char delim2 = ':'; /* Default delimiters. */
static char pad = ' '; /* Declare the default pad for numbers */
static char *s; /* Declare the temp array for args */
static char s1[EXPSIZ]; /* Declare the conversion array */
static char format = 'n'; /* Declare the format of numbers to be rt just */
static int q = 2; /* Initialize arg pointer to drop 1st 2 chars */
static int k; /* Declare var for return of convert */
static int r; /* Declare the arg array ptr for string args */
#ifdef XPG4
static int convert(int, char *);
#else
static int convert(char *);
#endif
static void num(int, int);
static void npad(int, char *);
#ifdef XPG4
static void optmsg(int, char *);
#else
static void optmsg(char *);
#endif
static void pnum(int, char *);
static void regerr(int);
static void usage();
extern char *optarg; /* getopt support */
extern int optind;
int
main(argc, argv)
int argc;
char *argv[];
{
register int j;
register int i = 0;
register char *p;
register char header = 'n';
register char body = 't';
register char footer = 'n';
char line[LINE_MAX];
char tempchr; /* Temporary holding variable. */
char swtch = 'n';
char cntck = 'n';
char type;
int cnt; /* line counter */
int pass1 = 1; /* First pass flag. 1=pass1, 0=additional passes. */
char sep[EXPSIZ];
char pat[EXPSIZ];
int startcnt = 1;
int increment = 1;
int blank = 1;
int blankctr = 0;
int c;
int lnt;
char last;
FILE *iptr = stdin;
FILE *optr = stdout;
#ifndef XPG4
int option_end = 0;
#endif
sep[0] = '\t';
sep[1] = '\0';
(void) setlocale(LC_ALL, "");
#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
#define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
#endif
(void) textdomain(TEXT_DOMAIN);
#ifdef XPG4
/*
* XPG4: Allow either a space or no space between the
* options and their required arguments.
*/
while (argc > 0) {
while ((c = getopt(argc, argv,
"pb:d:f:h:i:l:n:s:v:w:")) != EOF) {
switch (c) {
case 'h':
switch (*optarg) {
case 'n':
header = 'n';
break;
case 't':
header = 't';
break;
case 'a':
header = 'a';
break;
case 'p':
(void) strcpy(pat, optarg+1);
header = 'h';
hexpbuf =
compile(pat, (char *)0, (char *)0);
if (regerrno)
regerr(regerrno);
break;
case '\0':
header = 'n';
break;
default:
optmsg(c, optarg);
}
break;
case 'b':
switch (*optarg) {
case 't':
body = 't';
break;
case 'a':
body = 'a';
break;
case 'n':
body = 'n';
break;
case 'p':
(void) strcpy(pat, optarg+1);
body = 'b';
bexpbuf =
compile(pat, (char *)0, (char *)0);
if (regerrno)
regerr(regerrno);
break;
case '\0':
body = 't';
break;
default:
optmsg(c, optarg);
}
break;
case 'f':
switch (*optarg) {
case 'n':
footer = 'n';
break;
case 't':
footer = 't';
break;
case 'a':
footer = 'a';
break;
case 'p':
(void) strcpy(pat, optarg+1);
footer = 'f';
fexpbuf =
compile(pat, (char *)0, (char *)0);
if (regerrno)
regerr(regerrno);
break;
case '\0':
footer = 'n';
break;
default:
optmsg(c, optarg);
}
break;
case 'p':
if (optarg == (char *)NULL)
cntck = 'y';
else
optmsg(c, optarg);
break;
case 'v':
if (*optarg == '\0')
startcnt = 1;
else
startcnt = convert(c, optarg);
break;
case 'i':
if (*optarg == '\0')
increment = 1;
else
increment = convert(c, optarg);
break;
case 'w':
if (*optarg == '\0')
width = 6;
else
width = convert(c, optarg);
break;
case 'l':
if (*optarg == '\0')
blank = 1;
else
blank = convert(c, optarg);
break;
case 'n':
switch (*optarg) {
case 'l':
if (*(optarg+1) == 'n')
format = 'l';
else
optmsg(c, optarg);
break;
case 'r':
if ((*(optarg+1) == 'n') ||
(*(optarg+1) == 'z'))
format = *(optarg+1);
else
optmsg(c, optarg);
break;
case '\0':
format = 'n';
break;
default:
optmsg(c, optarg);
break;
}
break;
case 's':
(void) strcpy(sep, optarg);
break;
case 'd':
delim1 = *optarg;
if (*(optarg+1) == '\0')
break;
delim2 = *(optarg+1);
if (*(optarg+2) != '\0')
optmsg(c, optarg);
break;
default:
optmsg(c, optarg);
} /* end switch char returned from getopt() */
} /* end while getopt */
argv += optind;
argc -= optind;
optind = 0;
if (argc > 0) {
if ((iptr = fopen(argv[0], "r")) == NULL) {
(void) fprintf(stderr, "nl: %s: ", argv[0]);
perror("");
return (1);
}
++argv;
--argc;
}
} /* end while argc > 0 */
/* end XPG4 version of argument parsing */
#else
/*
* Solaris: For backward compatibility, do not allow a space between the
* options and their arguments. Option arguments are optional,
* not required as in the XPG4 version of nl.
*/
for (j = 1; j < argc; j++) {
if (argv[j][i] == '-' && (c = argv[j][i + 1])) {
if (!option_end) {
switch (c) {
case 'h':
switch (argv[j][i + 2]) {
case 'n':
header = 'n';
break;
case 't':
header = 't';
break;
case 'a':
header = 'a';
break;
case 'p':
s = argv[j];
q = 3;
r = 0;
while (s[q] != '\0') {
pat[r] = s[q];
r++;
q++;
}
pat[r] = '\0';
header = 'h';
hexpbuf =
compile(pat, (char *)0, (char *)0);
if (regerrno)
regerr(regerrno);
break;
case '\0':
header = 'n';
break;
default:
optmsg(argv[j]);
}
break;
case 'b':
switch (argv[j][i + 2]) {
case 't':
body = 't';
break;
case 'a':
body = 'a';
break;
case 'n':
body = 'n';
break;
case 'p':
s = argv[j];
q = 3;
r = 0;
while (s[q] != '\0') {
pat[r] = s[q];
r++;
q++;
}
pat[r] = '\0';
body = 'b';
bexpbuf =
compile(pat, (char *)0, (char *)0);
if (regerrno)
regerr(regerrno);
break;
case '\0':
body = 't';
break;
default:
optmsg(argv[j]);
}
break;
case 'f':
switch (argv[j][i + 2]) {
case 'n':
footer = 'n';
break;
case 't':
footer = 't';
break;
case 'a':
footer = 'a';
break;
case 'p':
s = argv[j];
q = 3;
r = 0;
while (s[q] != '\0') {
pat[r] = s[q];
r++;
q++;
}
pat[r] = '\0';
footer = 'f';
fexpbuf =
compile(pat, (char *)0, (char *)0);
if (regerrno)
regerr(regerrno);
break;
case '\0':
footer = 'n';
break;
default:
optmsg(argv[j]);
}
break;
case 'p':
if (argv[j][i+2] == '\0')
cntck = 'y';
else
{
optmsg(argv[j]);
}
break;
case 'v':
if (argv[j][i+2] == '\0')
startcnt = 1;
else
startcnt = convert(argv[j]);
break;
case 'i':
if (argv[j][i+2] == '\0')
increment = 1;
else
increment = convert(argv[j]);
break;
case 'w':
if (argv[j][i+2] == '\0')
width = 6;
else
width = convert(argv[j]);
break;
case 'l':
if (argv[j][i+2] == '\0')
blank = 1;
else
blank = convert(argv[j]);
break;
case 'n':
switch (argv[j][i+2]) {
case 'l':
if (argv[j][i+3] == 'n')
format = 'l';
else
{
optmsg(argv[j]);
}
break;
case 'r':
if ((argv[j][i+3] == 'n') ||
(argv[j][i+3] == 'z'))
format = argv[j][i+3];
else
{
optmsg(argv[j]);
}
break;
case '\0':
format = 'n';
break;
default:
optmsg(argv[j]);
break;
}
break;
case 's':
if (argv[j][i + 2] != '\0') {
s = argv[j];
q = 2;
r = 0;
while (s[q] != '\0') {
sep[r] = s[q];
r++;
q++;
}
sep[r] = '\0';
}
/* else default sep is tab (set above) */
break;
case 'd':
tempchr = argv[j][i+2];
if (tempchr == '\0')break;
delim1 = tempchr;
tempchr = argv[j][i+3];
if (tempchr == '\0')break;
delim2 = tempchr;
if (argv[j][i+4] != '\0')optmsg(argv[j]);
break;
case '-':
if (argv[j][i + 2] == '\0') {
option_end = 1;
break;
}
default:
optmsg(argv[j]);
}
} else if ((iptr = fopen(argv[j], "r")) == NULL) {
/* end of options, filename starting with '-' */
(void) fprintf(stderr, "nl: %s: ", argv[j]);
perror("");
return (1);
}
} else if ((iptr = fopen(argv[j], "r")) == NULL) {
/* filename starting with char other than '-' */
(void) fprintf(stderr, "nl: %s: ", argv[j]);
perror("");
return (1);
}
} /* closing brace of for loop */
/* end Solaris version of argument parsing */
#endif
/* ON FIRST PASS ONLY, SET LINE COUNTER (cnt) = startcnt & */
/* SET DEFAULT BODY TYPE TO NUMBER ALL LINES. */
if (pass1) {
cnt = startcnt;
type = body;
last = 'b';
pass1 = 0;
}
/*
* DO WHILE THERE IS INPUT
* CHECK TO SEE IF LINE IS NUMBERED,
* IF SO, CALCULATE NUM, PRINT NUM,
* THEN OUTPUT SEPERATOR CHAR AND LINE
*/
while ((p = fgets(line, sizeof (line), iptr)) != NULL) {
if (p[0] == delim1 && p[1] == delim2) {
if (p[2] == delim1 &&
p[3] == delim2 &&
p[4] == delim1 &&
p[5] == delim2 &&
p[6] == '\n') {
if (cntck != 'y')
cnt = startcnt;
type = header;
last = 'h';
swtch = 'y';
} else {
if (p[2] == delim1 && p[3] == delim2 && p[4] == '\n') {
if (cntck != 'y' && last != 'h')
cnt = startcnt;
type = body;
last = 'b';
swtch = 'y';
} else {
if (p[0] == delim1 && p[1] == delim2 &&
p[2] == '\n') {
if (cntck != 'y' && last == 'f')
cnt = startcnt;
type = footer;
last = 'f';
swtch = 'y';
}
}
}
}
if (p[0] != '\n') {
lnt = strlen(p);
if (p[lnt-1] == '\n')
p[lnt-1] = NULL;
}
if (swtch == 'y') {
swtch = 'n';
(void) fprintf(optr, "\n");
} else {
switch (type) {
case 'n':
npad(width, sep);
break;
case 't':
/*
* XPG4: The wording of Spec 1170 is misleading;
* the official interpretation is to number all
* non-empty lines, ie: the Solaris code has not
* been changed.
*/
if (p[0] != '\n') {
pnum(cnt, sep);
cnt += increment;
} else {
npad(width, sep);
}
break;
case 'a':
if (p[0] == '\n') {
blankctr++;
if (blank == blankctr) {
blankctr = 0;
pnum(cnt, sep);
cnt += increment;
} else
npad(width, sep);
} else {
blankctr = 0;
pnum(cnt, sep);
cnt += increment;
}
break;
case 'b':
if (step(p, bexpbuf)) {
pnum(cnt, sep);
cnt += increment;
} else {
npad(width, sep);
}
break;
case 'h':
if (step(p, hexpbuf)) {
pnum(cnt, sep);
cnt += increment;
} else {
npad(width, sep);
}
break;
case 'f':
if (step(p, fexpbuf)) {
pnum(cnt, sep);
cnt += increment;
} else {
npad(width, sep);
}
break;
}
if (p[0] != '\n')
p[lnt-1] = '\n';
(void) fprintf(optr, "%s", line);
} /* Closing brace of "else" */
} /* Closing brace of "while". */
(void) fclose(iptr);
return (0);
}
/* REGEXP ERR ROUTINE */
static void
regerr(c)
int c;
{
(void) fprintf(stderr, gettext(
"nl: invalid regular expression: error code %d\n"), c);
exit(1);
}
/* CALCULATE NUMBER ROUTINE */
static void
pnum(n, sep)
int n;
char * sep;
{
register int i;
if (format == 'z') {
pad = '0';
}
for (i = 0; i < width; i++)
nbuf[i] = pad;
num(n, width - 1);
if (format == 'l') {
while (nbuf[0] == ' ') {
for (i = 0; i < width; i++)
nbuf[i] = nbuf[i+1];
nbuf[width-1] = ' ';
}
}
(void) printf("%s%s", nbuf, sep);
}
/* IF NUM > 10, THEN USE THIS CALCULATE ROUTINE */
static void
num(v, p)
int v, p;
{
if (v < 10)
nbuf[p] = v + '0';
else {
nbuf[p] = (v % 10) + '0';
if (p > 0)
num(v / 10, p - 1);
}
}
/* CONVERT ARG STRINGS TO STRING ARRAYS */
#ifdef XPG4
static int
convert(c, option_arg)
int c;
char *option_arg;
{
s = option_arg;
q = r = 0;
while (s[q] != '\0') {
if (s[q] >= '0' && s[q] <= '9') {
s1[r] = s[q];
r++;
q++;
} else
optmsg(c, option_arg);
}
s1[r] = '\0';
k = atoi(s1);
return (k);
}
#else
/* Solaris version */
static int
convert(argv)
char *argv;
{
s = (char *)argv;
q = 2;
r = 0;
while (s[q] != '\0') {
if (s[q] >= '0' && s[q] <= '9')
{
s1[r] = s[q];
r++;
q++;
}
else
{
optmsg(argv);
}
}
s1[r] = '\0';
k = atoi(s1);
return (k);
}
#endif
/* CALCULATE NUM/TEXT SEPRATOR */
static void
npad(width, sep)
int width;
char * sep;
{
register int i;
pad = ' ';
for (i = 0; i < width; i++)
nbuf[i] = pad;
(void) printf("%s", nbuf);
for (i = 0; i < (int) strlen(sep); i++)
(void) printf(" ");
}
#ifdef XPG4
static void
optmsg(option, option_arg)
int option;
char *option_arg;
{
if (option_arg != (char *)NULL) {
(void) fprintf(stderr, gettext(
"nl: invalid option (-%c %s)\n"), option, option_arg);
}
/* else getopt() will print illegal option message */
usage();
}
#else
/* Solaris version */
static void
optmsg(option)
char *option;
{
(void) fprintf(stderr, gettext(
"nl: invalid option (%s)\n"), option);
usage();
}
#endif
void
usage()
{
(void) fprintf(stderr, gettext(USAGE));
exit(1);
}