1
0
mirror of https://github.com/openbsd/src.git synced 2025-01-10 06:47:55 -08:00

finally make our m4 SusV3-compliant.

- changecom and changequote have a simple definition (that matches gnu-m4,
coincidentally, so we no longer need two distinct modes for these)

- off-by-one bug in -s, this finally works.

- reorder main parser loop, so that we can use alphabetic constructs in
quotes/comments.

- rename putback to pushback, this matches comments, and makes more sense.

- more uniform (and updated) description of changequote/changecom.

- new, systematic regression tests of comments/quotes.

- framework to test -s: one perl script to reconstitute `full' files with
all line numbers, so that we can verify the output without needing a
complete match.

okay otto@, fries@
This commit is contained in:
espie 2005-09-06 15:33:21 +00:00
parent ff117408ee
commit 739ce1244b
14 changed files with 399 additions and 207 deletions

View File

@ -1,4 +1,4 @@
# $OpenBSD: Makefile,v 1.21 2005/05/17 20:37:11 espie Exp $
# $OpenBSD: Makefile,v 1.22 2005/09/06 15:33:21 espie Exp $
FIBOMAX=25
M4=m4
@ -10,7 +10,7 @@ REGRESS_TARGETS= test-ff_after_dnl test-m4wrap test-m4wrap2 \
test-m4wrap3 test-gm4wrap3 test-fibo \
test-patterns trip test-strangequotes test-redef test-quotes \
test-weird test-args test-esyscmd test-eval test-gnupatterns \
test-gnupatterns2
test-gnupatterns2 test-comments test-synch1 test-synch1bis
test-ff_after_dnl: ff_after_dnl.m4
${M4} ff_after_dnl.m4 | diff - ${.CURDIR}/ff_after_dnl.out
@ -52,8 +52,9 @@ test-quotes:
${M4} ${.CURDIR}/quotes.m4 2>&1| \
sed -e 's,\( *\).*/quotes.m4,\1quotes.m4,' | \
diff - ${.CURDIR}/quotes.out
${M4} -g ${.CURDIR}/quotes.m4 2>&1|diff - ${.CURDIR}/quotes.gnu.out
test-comments:
${M4} ${.CURDIR}/comments.m4 | diff - ${.CURDIR}/comments.out
test-strangequotes: strangequotes.m4
${M4} strangequotes.m4| diff - ${.CURDIR}/strangequotes.out
@ -72,6 +73,14 @@ test-esyscmd:
test-eval:
${M4} ${.CURDIR}/eval.m4 | diff -u - ${.CURDIR}/eval.out
test-synch1:
${M4} -s ${.CURDIR}/synch1.m4|perl ${.CURDIR}/reconstitute|\
grep MARK| diff - ${.CURDIR}/synch1.out
test-synch1bis:
${M4} -s <${.CURDIR}/synch1.m4|perl ${.CURDIR}/reconstitute|\
grep MARK| diff - ${.CURDIR}/synch1bis.out
.PHONY: ${REGRESS_TARGETS}
.include <bsd.regress.mk>

View File

@ -0,0 +1,58 @@
dnl $OpenBSD: comments.m4,v 1.1 2005/09/06 15:33:21 espie Exp $
dnl checking the way changecom works.
1: normal
define(`comment', `COMMENT')dnl
define(`p', 'XXX')dnl
# this is a comment
>> this is a comment
p this is a comment
p this is a comment q comment too
2: `changecom(>>)dnl'
changecom(>>)dnl
# this is a comment
>> this is a comment
p this is a comment
p this is a comment q comment too
3: `changecom dnl'
changecom dnl
# this is a comment
>> this is a comment
p this is a comment
p this is a comment q comment too
4: `changecom()dnl'
changecom()dnl
# this is a comment
>> this is a comment
p this is a comment
p this is a comment q comment too
5: `changecom(,)dnl'
changecom(,)dnl
# this is a comment
>> this is a comment
p this is a comment
p this is a comment q comment too
6: `changecom(`p',q)dnl'
changecom(`p',q)dnl
# this is a comment
>> this is a comment
p this is a comment
p this is a comment q comment too
7: `changecom(`p')dnl'
changecom(`p')dnl
# this is a comment
>> this is a comment
p this is a comment
p this is a comment q comment too
8: `changecom(#)dnl'
changecom(#)dnl
# this is a comment
>> this is a comment
p this is a comment
p this is a comment q comment too

View File

@ -0,0 +1,47 @@
1: normal
# this is a comment
>> this is a COMMENT
'XXX' this is a COMMENT
'XXX' this is a COMMENT q COMMENT too
2: changecom(>>)dnl
# this is a COMMENT
>> this is a comment
'XXX' this is a COMMENT
'XXX' this is a COMMENT q COMMENT too
3: changecom dnl
# this is a COMMENT
>> this is a COMMENT
'XXX' this is a COMMENT
'XXX' this is a COMMENT q COMMENT too
4: changecom()dnl
# this is a COMMENT
>> this is a COMMENT
'XXX' this is a COMMENT
'XXX' this is a COMMENT q COMMENT too
5: changecom(,)dnl
# this is a COMMENT
>> this is a COMMENT
'XXX' this is a COMMENT
'XXX' this is a COMMENT q COMMENT too
6: changecom(`p',q)dnl
# this is a COMMENT
>> this is a COMMENT
p this is a comment
p this is a comment q COMMENT too
7: changecom(`p')dnl
# this is a COMMENT
>> this is a COMMENT
p this is a comment
p this is a comment q comment too
8: changecom(#)dnl
# this is a comment
>> this is a COMMENT
'XXX' this is a COMMENT
'XXX' this is a COMMENT q COMMENT too

View File

@ -1,21 +1,57 @@
dnl $OpenBSD: quotes.m4,v 1.1 2001/09/29 15:49:18 espie Exp $
dnl $OpenBSD: quotes.m4,v 1.2 2005/09/06 15:33:21 espie Exp $
dnl Checking the way changequote() is supposed to work
dnl in both normal and gnu emulation mode
define(`string',`STRING')dnl
1: normal
`quoted string'
[quoted string]
normal string
`half quoted string
going up to that string'
2: kill quotes
changequote()dnl
`No quotes left'
changequote([,])dnl
[Quotes]changequote
`No quotes left'
`quoted string'
[quoted string]
normal string
`half quoted string
going up to that string'
3: normal changed quote
changequote([,])dnl
`quoted string'
[quoted string]
normal string
`half quoted string
going up to that string'
4: empty quotes, kill them too
changequote(,)dnl
`No quotes left'
changequote([,])dnl
dnl same thing with comments, so first:
define([comment], [COMMENT])dnl
# this is a comment
changecom(>>)dnl
# this is a comment
changecom
# this is a comment
changecom(,)dnl
# this is a comment
`quoted string'
[quoted string]
normal string
`half quoted string
going up to that string'
5: start quote only
changequote(`)dnl
`quoted string'
[quoted string]
normal string
`half quoted string
going up to that string'
6: normal quotes are back
changequote
`quoted string'
[quoted string]
normal string
`half quoted string
going up to that string'
7: start quote+empty end quote
changequote([,)dnl
`quoted string'
[quoted string]
normal string
`half quoted string
going up to that string'

View File

@ -1,9 +1,49 @@
No quotes left
Quotes
No quotes left
No quotes left'
changequote([,)# this is a comment
# this is a comment
m4: unclosed quote:
quotes.m4 at line 54
1: normal
quoted string
[quoted STRING]
normal STRING
half quoted string
going up to that string
# this is a comment
# this is a comment
2: kill quotes
`quoted STRING'
[quoted STRING]
normal STRING
`half quoted STRING
going up to that STRING'
3: normal changed quote
`quoted STRING'
quoted string
normal STRING
`half quoted STRING
going up to that STRING'
4: empty quotes, kill them too
`quoted STRING'
[quoted STRING]
normal STRING
`half quoted STRING
going up to that STRING'
5: start quote only
quoted string'[quoted STRING]
normal STRING
half quoted stringgoing up to that STRING'
6: normal quotes are back
quoted string
[quoted STRING]
normal STRING
half quoted string
going up to that string
7: start quote+empty end quote
`quoted STRING'
quoted string]
normal string
`half quoted string
going up to that string'

View File

@ -0,0 +1,24 @@
#! /usr/bin/perl
# $OpenBSD: reconstitute,v 1.1 2005/09/06 15:33:21 espie Exp $
# Written by Marc Espie, 2005
# Public domain
# This simple perl script puts back line numbers everywhere.
# This is suitable for testing synchronization, as we don't really
# care how many synchronization marks we emit, as long as the line
# numbers match
use File::Basename;
my ($lineno, $file) = (-1, "<unknown>");
while (<>) {
if (m/^#line\s+(\d+)\s+\"(.*)\"/) {
($lineno, $file) = ($1, $2);
$file=basename($file);
} else {
print "$file:$lineno:$_";
$lineno++;
}
}

View File

@ -0,0 +1,13 @@
This is a test for multi MARK1
line synchronization stuff MARK2
# Let's include comments to see MARK3
dnl
dnl
`quoted string' MARK4
define(`A',`big string
with a newline embedded')dnl
Return to normal MARK5
Let's try A
And let's see where we are now. MARK6
Okay, some more input.
MARK7

View File

@ -0,0 +1,7 @@
synch1.m4:1:This is a test for multi MARK1
synch1.m4:2:line synchronization stuff MARK2
synch1.m4:3:# Let's include comments to see MARK3
synch1.m4:4:quoted string MARK4
synch1.m4:7:Return to normal MARK5
synch1.m4:11:And let's see where we are now. MARK6
synch1.m4:13:MARK7

View File

@ -0,0 +1,7 @@
stdin:1:This is a test for multi MARK1
stdin:2:line synchronization stuff MARK2
stdin:3:# Let's include comments to see MARK3
stdin:4:quoted string MARK4
stdin:7:Return to normal MARK5
stdin:11:And let's see where we are now. MARK6
stdin:13:MARK7

View File

@ -1,4 +1,4 @@
/* $OpenBSD: eval.c,v 1.57 2005/08/06 16:22:26 espie Exp $ */
/* $OpenBSD: eval.c,v 1.58 2005/09/06 15:33:21 espie Exp $ */
/* $NetBSD: eval.c,v 1.7 1996/11/10 21:21:29 pk Exp $ */
/*
@ -61,9 +61,7 @@ static void dotrace(const char *[], int, int);
static void doifelse(const char *[], int);
static int doincl(const char *);
static int dopaste(const char *);
static void gnu_dochq(const char *[], int);
static void dochq(const char *[], int);
static void gnu_dochc(const char *[], int);
static void dochc(const char *[], int);
static void dom4wrap(const char *);
static void dodiv(int);
@ -136,6 +134,9 @@ expand_builtin(const char *argv[], int argc, int td)
* if argc == 3 and argv[2] is null, then we
* have macro-or-builtin() type call. We adjust
* argc to avoid further checking..
*/
/* we keep the initial value for those built-ins that differentiate
* between builtin() and builtin.
*/
ac = argc;
@ -286,17 +287,11 @@ expand_builtin(const char *argv[], int argc, int td)
break;
#endif
case CHNQTYPE:
if (mimic_gnu)
gnu_dochq(argv, ac);
else
dochq(argv, argc);
dochq(argv, ac);
break;
case CHNCTYPE:
if (mimic_gnu)
gnu_dochc(argv, ac);
else
dochc(argv, argc);
dochc(argv, argc);
break;
case SUBSTYPE:
@ -319,7 +314,7 @@ expand_builtin(const char *argv[], int argc, int td)
pbstr(rquote);
pbstr(argv[n]);
pbstr(lquote);
putback(COMMA);
pushback(COMMA);
}
pbstr(rquote);
pbstr(argv[3]);
@ -518,7 +513,7 @@ expand_macro(const char *argv[], int argc)
p--; /* last character of defn */
while (p > t) {
if (*(p - 1) != ARGFLAG)
PUTBACK(*p);
PUSHBACK(*p);
else {
switch (*p) {
@ -542,7 +537,7 @@ expand_macro(const char *argv[], int argc)
if (argc > 2) {
for (n = argc - 1; n > 2; n--) {
pbstr(argv[n]);
putback(COMMA);
pushback(COMMA);
}
pbstr(argv[2]);
}
@ -553,7 +548,7 @@ expand_macro(const char *argv[], int argc)
pbstr(rquote);
pbstr(argv[n]);
pbstr(lquote);
putback(COMMA);
pushback(COMMA);
}
pbstr(rquote);
pbstr(argv[2]);
@ -561,8 +556,8 @@ expand_macro(const char *argv[], int argc)
}
break;
default:
PUTBACK(*p);
PUTBACK('$');
PUSHBACK(*p);
PUSHBACK('$');
break;
}
p--;
@ -570,7 +565,7 @@ expand_macro(const char *argv[], int argc)
p--;
}
if (p == t) /* do last character */
PUTBACK(*p);
PUSHBACK(*p);
}
@ -736,85 +731,44 @@ dopaste(const char *pfile)
}
#endif
static void
gnu_dochq(const char *argv[], int ac)
{
/* In gnu-m4 mode, the only way to restore quotes is to have no
* arguments at all. */
if (ac == 2) {
lquote[0] = LQUOTE, lquote[1] = EOS;
rquote[0] = RQUOTE, rquote[1] = EOS;
} else {
strlcpy(lquote, argv[2], sizeof(lquote));
if(ac > 3)
strlcpy(rquote, argv[3], sizeof(rquote));
else
rquote[0] = EOS;
}
}
/*
* dochq - change quote characters
*/
static void
dochq(const char *argv[], int argc)
dochq(const char *argv[], int ac)
{
if (argc > 2) {
if (*argv[2])
strlcpy(lquote, argv[2], sizeof(lquote));
else {
lquote[0] = LQUOTE;
lquote[1] = EOS;
}
if (argc > 3) {
if (*argv[3])
strlcpy(rquote, argv[3], sizeof(rquote));
} else
strlcpy(rquote, lquote, sizeof(rquote));
if (ac == 2) {
lquote[0] = LQUOTE; lquote[1] = EOS;
rquote[0] = RQUOTE; rquote[1] = EOS;
} else {
lquote[0] = LQUOTE, lquote[1] = EOS;
rquote[0] = RQUOTE, rquote[1] = EOS;
strlcpy(lquote, argv[2], sizeof(lquote));
if (ac > 3) {
strlcpy(rquote, argv[3], sizeof(rquote));
} else {
rquote[0] = ECOMMT; rquote[1] = EOS;
}
}
}
static void
gnu_dochc(const char *argv[], int ac)
{
/* In gnu-m4 mode, no arguments mean no comment
* arguments at all. */
if (ac == 2) {
scommt[0] = EOS;
ecommt[0] = EOS;
} else {
if (*argv[2])
strlcpy(scommt, argv[2], sizeof(scommt));
else
scommt[0] = SCOMMT, scommt[1] = EOS;
if(ac > 3 && *argv[3])
strlcpy(ecommt, argv[3], sizeof(ecommt));
else
ecommt[0] = ECOMMT, ecommt[1] = EOS;
}
}
/*
* dochc - change comment characters
*/
static void
dochc(const char *argv[], int argc)
{
if (argc > 2) {
if (*argv[2])
strlcpy(scommt, argv[2], sizeof(scommt));
if (argc > 3) {
if (*argv[3])
strlcpy(ecommt, argv[3], sizeof(ecommt));
/* XXX Note that there is no difference between no argument and a single
* empty argument.
*/
if (argc == 2) {
scommt[0] = EOS;
ecommt[0] = EOS;
} else {
strlcpy(scommt, argv[2], sizeof(scommt));
if (argc == 3) {
ecommt[0] = ECOMMT; ecommt[1] = EOS;
} else {
strlcpy(ecommt, argv[3], sizeof(ecommt));
}
else
ecommt[0] = ECOMMT, ecommt[1] = EOS;
}
else {
scommt[0] = SCOMMT, scommt[1] = EOS;
ecommt[0] = ECOMMT, ecommt[1] = EOS;
}
}
@ -918,7 +872,7 @@ dosub(const char *argv[], int argc)
#endif
if (fc >= ap && fc < ap + strlen(ap))
for (k = fc + nc - 1; k >= fc; k--)
putback(*k);
pushback(*k);
}
/*

View File

@ -1,4 +1,4 @@
/* $OpenBSD: extern.h,v 1.41 2005/05/29 18:44:36 espie Exp $ */
/* $OpenBSD: extern.h,v 1.42 2005/09/06 15:33:21 espie Exp $ */
/* $NetBSD: extern.h,v 1.3 1996/01/13 23:25:24 pk Exp $ */
/*-
@ -100,7 +100,7 @@ extern void pbnum(int);
extern void pbnumbase(int, int, int);
extern void pbunsigned(unsigned long);
extern void pbstr(const char *);
extern void putback(int);
extern void pushback(int);
extern void *xalloc(size_t, const char *fmt, ...);
extern void *xrealloc(void *, size_t, const char *fmt, ...);
extern char *xstrdup(const char *);
@ -113,8 +113,8 @@ extern int obtain_char(struct input_file *);
extern void set_input(struct input_file *, FILE *, const char *);
extern void release_input(struct input_file *);
/* speeded-up versions of chrsave/putback */
#define PUTBACK(c) \
/* speeded-up versions of chrsave/pushback */
#define PUSHBACK(c) \
do { \
if (bp >= endpbb) \
enlarge_bufspace(); \

View File

@ -1,4 +1,4 @@
.\" @(#) $OpenBSD: m4.1,v 1.38 2005/03/02 10:12:15 espie Exp $
.\" @(#) $OpenBSD: m4.1,v 1.39 2005/09/06 15:33:21 espie Exp $
.\"
.\" Copyright (c) 1989, 1993
.\" The Regents of the University of California. All rights reserved.
@ -155,17 +155,29 @@ Calls a built-in by its
.Fa name ,
overriding possible redefinitions.
.It Fn changecom startcomment endcomment
Change the start and end comment sequences.
The default is the pound sign
.Pq Sq #
Changes the start comment and end comment sequences.
Comment sequences may be up to five characters long.
The default values are the pound sign
and the newline character.
With no arguments comments are turned off.
The maximum length for a comment marker is five characters.
.Bd -literal -offset indent
# This is a comment
.Ed
.Pp
With no arguments, comments are turned off.
With one single argument, the end comment sequence is set
to the newline character.
.It Fn changequote beginquote endquote
Defines the quote symbols to be the first and second arguments.
The symbols may be up to five characters long.
If no arguments are
given it restores the default open and close single quotes.
Defines the open quote and close quote sequences.
Quote sequences may be up to five characters long.
The default values are the backquote character and the quote
character.
.Bd -literal -offset indent
`Here is a quoted string'
.Ed
.Pp
With no arguments, the default quotes are restored.
With one single argument, the close quote sequence is set
to the newline character.
.It Fn decr arg
Decrements the argument
.Fa arg
@ -386,18 +398,10 @@ Returns the current file's name.
.El
.Sh STANDARDS
.Nm
follows the Single Unix 2 specification, along with a few extensions taken
follows the Single Unix 3 specification, along with a few extensions taken
from
.Nm gnu-m4 .
.Pp
The
.Fl s
option
.Po
.Xr cpp 1 's
#line directives
.Pc
is currently not supported.
Flags
.Fl I ,
.Fl d ,

View File

@ -1,4 +1,4 @@
/* $OpenBSD: main.c,v 1.67 2005/08/06 16:22:26 espie Exp $ */
/* $OpenBSD: main.c,v 1.68 2005/09/06 15:33:21 espie Exp $ */
/* $NetBSD: main.c,v 1.12 1997/02/08 23:54:49 cgd Exp $ */
/*-
@ -296,9 +296,9 @@ do_look_ahead(int t, const char *token)
for (i = 1; *++token; i++) {
t = gpbc();
if (t == EOF || (unsigned char)t != (unsigned char)*token) {
putback(t);
pushback(t);
while (--i)
putback(*--token);
pushback(*--token);
return 0;
}
}
@ -322,10 +322,57 @@ macro(void)
cycle {
t = gpbc();
if (t == '_' || isalpha(t)) {
if (LOOK_AHEAD(t,lquote)) { /* strip quotes */
nlpar = 0;
record(quotes, nlpar++);
/*
* Opening quote: scan forward until matching
* closing quote has been found.
*/
do {
l = gpbc();
if (LOOK_AHEAD(l,rquote)) {
if (--nlpar > 0)
outputstr(rquote);
} else if (LOOK_AHEAD(l,lquote)) {
record(quotes, nlpar++);
outputstr(lquote);
} else if (l == EOF) {
if (nlpar == 1)
warnx("unclosed quote:");
else
warnx("%d unclosed quotes:", nlpar);
dump_stack(quotes, nlpar);
exit(1);
} else {
if (nlpar > 0) {
if (sp < 0)
reallyputchar(l);
else
CHRSAVE(l);
}
}
}
while (nlpar != 0);
} else if (sp < 0 && LOOK_AHEAD(t, scommt)) {
reallyoutputstr(scommt);
for(;;) {
t = gpbc();
if (LOOK_AHEAD(t, ecommt)) {
reallyoutputstr(ecommt);
break;
}
if (t == EOF)
break;
reallyputchar(t);
}
} else if (t == '_' || isalpha(t)) {
p = inspect(t, token);
if (p != NULL)
putback(l = gpbc());
pushback(l = gpbc());
if (p == NULL || (l != LPAREN &&
(macro_getdef(p)->type & NEEDARGS) != 0))
outputstr(token);
@ -371,62 +418,7 @@ macro(void)
emit_synchline();
bufbase = bbase[ilevel];
continue;
}
/*
* non-alpha token possibly seen..
* [the order of else if .. stmts is important.]
*/
else if (LOOK_AHEAD(t,lquote)) { /* strip quotes */
nlpar = 0;
record(quotes, nlpar++);
/*
* Opening quote: scan forward until matching
* closing quote has been found.
*/
do {
l = gpbc();
if (LOOK_AHEAD(l,rquote)) {
if (--nlpar > 0)
outputstr(rquote);
} else if (LOOK_AHEAD(l,lquote)) {
record(quotes, nlpar++);
outputstr(lquote);
} else if (l == EOF) {
if (nlpar == 1)
warnx("unclosed quote:");
else
warnx("%d unclosed quotes:", nlpar);
dump_stack(quotes, nlpar);
exit(1);
} else {
if (nlpar > 0) {
if (sp < 0)
reallyputchar(l);
else
CHRSAVE(l);
}
}
}
while (nlpar != 0);
}
else if (sp < 0 && LOOK_AHEAD(t, scommt)) {
reallyoutputstr(scommt);
for(;;) {
t = gpbc();
if (LOOK_AHEAD(t, ecommt)) {
reallyoutputstr(ecommt);
break;
}
if (t == EOF)
break;
reallyputchar(t);
}
}
else if (sp < 0) { /* not in a macro at all */
} else if (sp < 0) { /* not in a macro at all */
reallyputchar(t); /* output directly.. */
}
@ -437,7 +429,7 @@ macro(void)
chrsave(t);
while (isspace(l = gpbc()))
; /* skip blank, tab, nl.. */
putback(l);
pushback(l);
record(paren, PARLEV++);
break;
@ -464,7 +456,7 @@ macro(void)
chrsave(EOS); /* new argument */
while (isspace(l = gpbc()))
;
putback(l);
pushback(l);
pushs(ep);
} else
chrsave(t);
@ -550,7 +542,7 @@ inspect(int c, char *tp)
while ((isalnum(c = gpbc()) || c == '_') && tp < etp)
*tp++ = c;
if (c != EOF)
PUTBACK(c);
PUSHBACK(c);
*tp = EOS;
/* token is too long, it won't match anything, but it can still
* be output. */

View File

@ -1,4 +1,4 @@
/* $OpenBSD: misc.c,v 1.32 2005/08/06 16:22:26 espie Exp $ */
/* $OpenBSD: misc.c,v 1.33 2005/09/06 15:33:21 espie Exp $ */
/* $NetBSD: misc.c,v 1.6 1995/09/28 05:37:41 tls Exp $ */
/*
@ -76,10 +76,10 @@ indx(const char *s1, const char *s2)
return (t - s1);
}
/*
* putback - push character back onto input
* pushback - push character back onto input
*/
void
putback(int c)
pushback(int c)
{
if (c == EOF)
return;
@ -90,7 +90,7 @@ putback(int c)
/*
* pbstr - push string back onto input
* putback is replicated to improve
* pushback is replicated to improve
* performance.
*/
void
@ -129,7 +129,7 @@ pbnumbase(int n, int base, int d)
num = (n < 0) ? -n : n;
do {
putback(digits[num % base]);
pushback(digits[num % base]);
printed++;
}
while ((num /= base) > 0);
@ -137,10 +137,10 @@ pbnumbase(int n, int base, int d)
if (n < 0)
printed++;
while (printed++ < d)
putback('0');
pushback('0');
if (n < 0)
putback('-');
pushback('-');
}
/*
@ -150,7 +150,7 @@ void
pbunsigned(unsigned long n)
{
do {
putback(n % 10 + '0');
pushback(n % 10 + '0');
}
while ((n /= 10) > 0);
}
@ -332,10 +332,11 @@ obtain_char(struct input_file *f)
{
if (f->c == EOF)
return EOF;
else if (f->c == '\n')
f->lineno++;
f->c = fgetc(f->file);
if (f->c == '\n')
f->lineno++;
return f->c;
}