mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-25 11:37:56 +00:00
858 lines
20 KiB
C
858 lines
20 KiB
C
/*
|
|
* RCS file input
|
|
*/
|
|
/*********************************************************************************
|
|
* Syntax Analysis.
|
|
* Keyword table
|
|
* Testprogram: define SYNTEST
|
|
* Compatibility with Release 2: define COMPAT2=1
|
|
*********************************************************************************
|
|
*/
|
|
|
|
/* Copyright (C) 1982, 1988, 1989 Walter Tichy
|
|
Copyright 1990, 1991 by Paul Eggert
|
|
Distributed under license by the Free Software Foundation, Inc.
|
|
|
|
This file is part of RCS.
|
|
|
|
RCS is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2, or (at your option)
|
|
any later version.
|
|
|
|
RCS is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with RCS; see the file COPYING. If not, write to
|
|
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
Report problems and direct all questions to:
|
|
|
|
rcs-bugs@cs.purdue.edu
|
|
|
|
*/
|
|
|
|
|
|
/* $Log: rcssyn.c,v $
|
|
* Revision 5.8 1991/08/19 03:13:55 eggert
|
|
* Tune.
|
|
*
|
|
* Revision 5.7 1991/04/21 11:58:29 eggert
|
|
* Disambiguate names on shortname hosts.
|
|
* Fix errno bug. Add MS-DOS support.
|
|
*
|
|
* Revision 5.6 1991/02/28 19:18:51 eggert
|
|
* Fix null termination bug in reporting keyword expansion.
|
|
*
|
|
* Revision 5.5 1991/02/25 07:12:44 eggert
|
|
* Check diff output more carefully; avoid overflow.
|
|
*
|
|
* Revision 5.4 1990/11/01 05:28:48 eggert
|
|
* When ignoring unknown phrases, copy them to the output RCS file.
|
|
* Permit arbitrary data in logs and comment leaders.
|
|
* Don't check for nontext on initial checkin.
|
|
*
|
|
* Revision 5.3 1990/09/20 07:58:32 eggert
|
|
* Remove the test for non-text bytes; it caused more pain than it cured.
|
|
*
|
|
* Revision 5.2 1990/09/04 08:02:30 eggert
|
|
* Parse RCS files with no revisions.
|
|
* Don't strip leading white space from diff commands. Count RCS lines better.
|
|
*
|
|
* Revision 5.1 1990/08/29 07:14:06 eggert
|
|
* Add -kkvl. Clean old log messages too.
|
|
*
|
|
* Revision 5.0 1990/08/22 08:13:44 eggert
|
|
* Try to parse future RCS formats without barfing.
|
|
* Add -k. Don't require final newline.
|
|
* Remove compile-time limits; use malloc instead.
|
|
* Don't output branch keyword if there's no default branch,
|
|
* because RCS version 3 doesn't understand it.
|
|
* Tune. Remove lint.
|
|
* Add support for ISO 8859. Ansify and Posixate.
|
|
* Check that a newly checked-in file is acceptable as input to 'diff'.
|
|
* Check diff's output.
|
|
*
|
|
* Revision 4.6 89/05/01 15:13:32 narten
|
|
* changed copyright header to reflect current distribution rules
|
|
*
|
|
* Revision 4.5 88/08/09 19:13:21 eggert
|
|
* Allow cc -R; remove lint.
|
|
*
|
|
* Revision 4.4 87/12/18 11:46:16 narten
|
|
* more lint cleanups (Guy Harris)
|
|
*
|
|
* Revision 4.3 87/10/18 10:39:36 narten
|
|
* Updating version numbers. Changes relative to 1.1 actually relative to
|
|
* 4.1
|
|
*
|
|
* Revision 1.3 87/09/24 14:00:49 narten
|
|
* Sources now pass through lint (if you ignore printf/sprintf/fprintf
|
|
* warnings)
|
|
*
|
|
* Revision 1.2 87/03/27 14:22:40 jenkins
|
|
* Port to suns
|
|
*
|
|
* Revision 4.1 83/03/28 11:38:49 wft
|
|
* Added parsing and printing of default branch.
|
|
*
|
|
* Revision 3.6 83/01/15 17:46:50 wft
|
|
* Changed readdelta() to initialize selector and log-pointer.
|
|
* Changed puttree to check for selector==DELETE; putdtext() uses DELNUMFORM.
|
|
*
|
|
* Revision 3.5 82/12/08 21:58:58 wft
|
|
* renamed Commentleader to Commleader.
|
|
*
|
|
* Revision 3.4 82/12/04 13:24:40 wft
|
|
* Added routine gettree(), which updates keeplock after reading the
|
|
* delta tree.
|
|
*
|
|
* Revision 3.3 82/11/28 21:30:11 wft
|
|
* Reading and printing of Suffix removed; version COMPAT2 skips the
|
|
* Suffix for files of release 2 format. Fixed problems with printing nil.
|
|
*
|
|
* Revision 3.2 82/10/18 21:18:25 wft
|
|
* renamed putdeltatext to putdtext.
|
|
*
|
|
* Revision 3.1 82/10/11 19:45:11 wft
|
|
* made sure getc() returns into an integer.
|
|
*/
|
|
|
|
|
|
|
|
/* version COMPAT2 reads files of the format of release 2 and 3, but
|
|
* generates files of release 3 format. Need not be defined if no
|
|
* old RCS files generated with release 2 exist.
|
|
*/
|
|
/* version SYNTEST inputs a RCS file and then prints out its internal
|
|
* data structures.
|
|
*/
|
|
|
|
#include "rcsbase.h"
|
|
|
|
libId(synId, "$Id: rcssyn.c,v 5.8 1991/08/19 03:13:55 eggert Exp $")
|
|
|
|
/* forward */
|
|
static char const *getkeyval P((char const*,enum tokens,int));
|
|
static int strn2expmode P((char const*,size_t));
|
|
|
|
/* keyword table */
|
|
|
|
char const
|
|
Kdesc[] = "desc",
|
|
Klog[] = "log",
|
|
Ktext[] = "text";
|
|
|
|
static char const
|
|
Kaccess[] = "access",
|
|
Kauthor[] = "author",
|
|
Kbranch[] = "branch",
|
|
K_branches[]= "branches",
|
|
Kcomment[] = "comment",
|
|
Kdate[] = "date",
|
|
Kexpand[] = "expand",
|
|
Khead[] = "head",
|
|
Klocks[] = "locks",
|
|
Knext[] = "next",
|
|
Kstate[] = "state",
|
|
Kstrict[] = "strict",
|
|
#if COMPAT2
|
|
Ksuffix[] = "suffix",
|
|
#endif
|
|
Ksymbols[] = "symbols";
|
|
|
|
static struct buf Commleader;
|
|
static struct cbuf Ignored;
|
|
struct cbuf Comment;
|
|
struct access * AccessList;
|
|
struct assoc * Symbols;
|
|
struct lock * Locks;
|
|
int Expand;
|
|
int StrictLocks;
|
|
struct hshentry * Head;
|
|
char const * Dbranch;
|
|
unsigned TotalDeltas;
|
|
|
|
|
|
static void
|
|
getsemi(key)
|
|
char const *key;
|
|
/* Get a semicolon to finish off a phrase started by KEY. */
|
|
{
|
|
if (!getlex(SEMI))
|
|
fatserror("missing ';' after '%s'", key);
|
|
}
|
|
|
|
static struct hshentry *
|
|
getdnum()
|
|
/* Get a delta number. */
|
|
{
|
|
register struct hshentry *delta = getnum();
|
|
if (delta && countnumflds(delta->num)&1)
|
|
fatserror("%s isn't a delta number", delta->num);
|
|
return delta;
|
|
}
|
|
|
|
|
|
void
|
|
getadmin()
|
|
/* Read an <admin> and initialize the appropriate global variables. */
|
|
{
|
|
register char const *id;
|
|
struct access * newaccess;
|
|
struct assoc * newassoc;
|
|
struct lock * newlock;
|
|
struct hshentry * delta;
|
|
struct access **LastAccess;
|
|
struct assoc **LastSymbol;
|
|
struct lock **LastLock;
|
|
struct buf b;
|
|
struct cbuf cb;
|
|
|
|
TotalDeltas=0;
|
|
|
|
getkey(Khead);
|
|
Head = getdnum();
|
|
getsemi(Khead);
|
|
|
|
Dbranch = nil;
|
|
if (getkeyopt(Kbranch)) {
|
|
if ((delta = getnum()))
|
|
Dbranch = delta->num;
|
|
getsemi(Kbranch);
|
|
}
|
|
|
|
|
|
#if COMPAT2
|
|
/* read suffix. Only in release 2 format */
|
|
if (getkeyopt(Ksuffix)) {
|
|
if (nexttok==STRING) {
|
|
readstring(); nextlex(); /* Throw away the suffix. */
|
|
} else if (nexttok==ID) {
|
|
nextlex();
|
|
}
|
|
getsemi(Ksuffix);
|
|
}
|
|
#endif
|
|
|
|
getkey(Kaccess);
|
|
LastAccess = &AccessList;
|
|
while (id=getid()) {
|
|
newaccess = ftalloc(struct access);
|
|
newaccess->login = id;
|
|
*LastAccess = newaccess;
|
|
LastAccess = &newaccess->nextaccess;
|
|
}
|
|
*LastAccess = nil;
|
|
getsemi(Kaccess);
|
|
|
|
getkey(Ksymbols);
|
|
LastSymbol = &Symbols;
|
|
while (id = getid()) {
|
|
if (!getlex(COLON))
|
|
fatserror("missing ':' in symbolic name definition");
|
|
if (!(delta=getnum())) {
|
|
fatserror("missing number in symbolic name definition");
|
|
} else { /*add new pair to association list*/
|
|
newassoc = ftalloc(struct assoc);
|
|
newassoc->symbol=id;
|
|
newassoc->num = delta->num;
|
|
*LastSymbol = newassoc;
|
|
LastSymbol = &newassoc->nextassoc;
|
|
}
|
|
}
|
|
*LastSymbol = nil;
|
|
getsemi(Ksymbols);
|
|
|
|
getkey(Klocks);
|
|
LastLock = &Locks;
|
|
while (id = getid()) {
|
|
if (!getlex(COLON))
|
|
fatserror("missing ':' in lock");
|
|
if (!(delta=getdnum())) {
|
|
fatserror("missing number in lock");
|
|
} else { /*add new pair to lock list*/
|
|
newlock = ftalloc(struct lock);
|
|
newlock->login=id;
|
|
newlock->delta=delta;
|
|
*LastLock = newlock;
|
|
LastLock = &newlock->nextlock;
|
|
}
|
|
}
|
|
*LastLock = nil;
|
|
getsemi(Klocks);
|
|
|
|
if ((StrictLocks = getkeyopt(Kstrict)))
|
|
getsemi(Kstrict);
|
|
|
|
Comment.size = 0;
|
|
if (getkeyopt(Kcomment)) {
|
|
if (nexttok==STRING) {
|
|
Comment = savestring(&Commleader);
|
|
nextlex();
|
|
}
|
|
getsemi(Kcomment);
|
|
}
|
|
|
|
Expand = KEYVAL_EXPAND;
|
|
if (getkeyopt(Kexpand)) {
|
|
if (nexttok==STRING) {
|
|
bufautobegin(&b);
|
|
cb = savestring(&b);
|
|
if ((Expand = strn2expmode(cb.string,cb.size)) < 0)
|
|
fatserror("unknown expand mode %.*s",
|
|
(int)cb.size, cb.string
|
|
);
|
|
bufautoend(&b);
|
|
nextlex();
|
|
}
|
|
getsemi(Kexpand);
|
|
}
|
|
Ignored = getphrases(Kdesc);
|
|
}
|
|
|
|
char const *const expand_names[] = {
|
|
/* These must agree with *_EXPAND in rcsbase.h. */
|
|
"kv","kvl","k","v","o",
|
|
0
|
|
};
|
|
|
|
int
|
|
str2expmode(s)
|
|
char const *s;
|
|
/* Yield expand mode corresponding to S, or -1 if bad. */
|
|
{
|
|
return strn2expmode(s, strlen(s));
|
|
}
|
|
|
|
static int
|
|
strn2expmode(s, n)
|
|
char const *s;
|
|
size_t n;
|
|
{
|
|
char const *const *p;
|
|
|
|
for (p = expand_names; *p; ++p)
|
|
if (memcmp(*p,s,n) == 0 && !(*p)[n])
|
|
return p - expand_names;
|
|
return -1;
|
|
}
|
|
|
|
|
|
void
|
|
ignorephrase()
|
|
/* Ignore a phrase introduced by a later version of RCS. */
|
|
{
|
|
warnignore();
|
|
hshenter=false;
|
|
for (;;) {
|
|
switch (nexttok) {
|
|
case SEMI: hshenter=true; nextlex(); return;
|
|
case ID:
|
|
case NUM: ffree1(NextString); break;
|
|
case STRING: readstring(); break;
|
|
default: break;
|
|
}
|
|
nextlex();
|
|
}
|
|
}
|
|
|
|
|
|
static int
|
|
getdelta()
|
|
/* Function: reads a delta block.
|
|
* returns false if the current block does not start with a number.
|
|
*/
|
|
{
|
|
register struct hshentry * Delta, * num;
|
|
struct branchhead **LastBranch, *NewBranch;
|
|
|
|
if (!(Delta = getdnum()))
|
|
return false;
|
|
|
|
hshenter = false; /*Don't enter dates into hashtable*/
|
|
Delta->date = getkeyval(Kdate, NUM, false);
|
|
hshenter=true; /*reset hshenter for revision numbers.*/
|
|
|
|
Delta->author = getkeyval(Kauthor, ID, false);
|
|
|
|
Delta->state = getkeyval(Kstate, ID, true);
|
|
|
|
getkey(K_branches);
|
|
LastBranch = &Delta->branches;
|
|
while ((num = getdnum())) {
|
|
NewBranch = ftalloc(struct branchhead);
|
|
NewBranch->hsh = num;
|
|
*LastBranch = NewBranch;
|
|
LastBranch = &NewBranch->nextbranch;
|
|
}
|
|
*LastBranch = nil;
|
|
getsemi(K_branches);
|
|
|
|
getkey(Knext);
|
|
Delta->next = num = getdnum();
|
|
getsemi(Knext);
|
|
Delta->lockedby = nil;
|
|
Delta->log.string = 0;
|
|
Delta->selector = true;
|
|
Delta->ig = getphrases(Kdesc);
|
|
TotalDeltas++;
|
|
return (true);
|
|
}
|
|
|
|
|
|
void
|
|
gettree()
|
|
/* Function: Reads in the delta tree with getdelta(), then
|
|
* updates the lockedby fields.
|
|
*/
|
|
{
|
|
struct lock const *currlock;
|
|
|
|
while (getdelta());
|
|
currlock=Locks;
|
|
while (currlock) {
|
|
currlock->delta->lockedby = currlock->login;
|
|
currlock = currlock->nextlock;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
getdesc(prdesc)
|
|
int prdesc;
|
|
/* Function: read in descriptive text
|
|
* nexttok is not advanced afterwards.
|
|
* If prdesc is set, the text is printed to stdout.
|
|
*/
|
|
{
|
|
|
|
getkeystring(Kdesc);
|
|
if (prdesc)
|
|
printstring(); /*echo string*/
|
|
else readstring(); /*skip string*/
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static char const *
|
|
getkeyval(keyword, token, optional)
|
|
char const *keyword;
|
|
enum tokens token;
|
|
int optional;
|
|
/* reads a pair of the form
|
|
* <keyword> <token> ;
|
|
* where token is one of <id> or <num>. optional indicates whether
|
|
* <token> is optional. A pointer to
|
|
* the actual character string of <id> or <num> is returned.
|
|
*/
|
|
{
|
|
register char const *val = nil;
|
|
|
|
getkey(keyword);
|
|
if (nexttok==token) {
|
|
val = NextString;
|
|
nextlex();
|
|
} else {
|
|
if (!optional)
|
|
fatserror("missing %s", keyword);
|
|
}
|
|
getsemi(keyword);
|
|
return(val);
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
putadmin(fout)
|
|
register FILE * fout;
|
|
/* Function: Print the <admin> node read with getadmin() to file fout.
|
|
* Assumption: Variables AccessList, Symbols, Locks, StrictLocks,
|
|
* and Head have been set.
|
|
*/
|
|
{
|
|
struct assoc const *curassoc;
|
|
struct lock const *curlock;
|
|
struct access const *curaccess;
|
|
|
|
aprintf(fout, "%s\t%s;\n", Khead, Head?Head->num:"");
|
|
if (Dbranch && VERSION(4)<=RCSversion)
|
|
aprintf(fout, "%s\t%s;\n", Kbranch, Dbranch);
|
|
|
|
aputs(Kaccess, fout);
|
|
curaccess = AccessList;
|
|
while (curaccess) {
|
|
aprintf(fout, "\n\t%s", curaccess->login);
|
|
curaccess = curaccess->nextaccess;
|
|
}
|
|
aprintf(fout, ";\n%s", Ksymbols);
|
|
curassoc = Symbols;
|
|
while (curassoc) {
|
|
aprintf(fout, "\n\t%s:%s", curassoc->symbol, curassoc->num);
|
|
curassoc = curassoc->nextassoc;
|
|
}
|
|
aprintf(fout, ";\n%s", Klocks);
|
|
curlock = Locks;
|
|
while (curlock) {
|
|
aprintf(fout, "\n\t%s:%s", curlock->login, curlock->delta->num);
|
|
curlock = curlock->nextlock;
|
|
}
|
|
if (StrictLocks) aprintf(fout, "; %s", Kstrict);
|
|
aprintf(fout, ";\n");
|
|
if (Comment.size) {
|
|
aprintf(fout, "%s\t", Kcomment);
|
|
putstring(fout, true, Comment, false);
|
|
aprintf(fout, ";\n");
|
|
}
|
|
if (Expand != KEYVAL_EXPAND)
|
|
aprintf(fout, "%s\t%c%s%c;\n",
|
|
Kexpand, SDELIM, expand_names[Expand], SDELIM
|
|
);
|
|
awrite(Ignored.string, Ignored.size, fout);
|
|
aputc('\n', fout);
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
putdelta(node,fout)
|
|
register struct hshentry const *node;
|
|
register FILE * fout;
|
|
/* Function: prints a <delta> node to fout;
|
|
*/
|
|
{
|
|
struct branchhead const *nextbranch;
|
|
|
|
if (node == nil) return;
|
|
|
|
aprintf(fout, "\n%s\n%s\t%s;\t%s %s;\t%s %s;\nbranches",
|
|
node->num,
|
|
Kdate, node->date,
|
|
Kauthor, node->author,
|
|
Kstate, node->state?node->state:""
|
|
);
|
|
nextbranch = node->branches;
|
|
while (nextbranch) {
|
|
aprintf(fout, "\n\t%s", nextbranch->hsh->num);
|
|
nextbranch = nextbranch->nextbranch;
|
|
}
|
|
|
|
aprintf(fout, ";\n%s\t%s;\n", Knext, node->next?node->next->num:"");
|
|
awrite(node->ig.string, node->ig.size, fout);
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
puttree(root,fout)
|
|
struct hshentry const *root;
|
|
register FILE * fout;
|
|
/* Function: prints the delta tree in preorder to fout, starting with root.
|
|
*/
|
|
{
|
|
struct branchhead const *nextbranch;
|
|
|
|
if (root==nil) return;
|
|
|
|
if (root->selector)
|
|
putdelta(root,fout);
|
|
|
|
puttree(root->next,fout);
|
|
|
|
nextbranch = root->branches;
|
|
while (nextbranch) {
|
|
puttree(nextbranch->hsh,fout);
|
|
nextbranch = nextbranch->nextbranch;
|
|
}
|
|
}
|
|
|
|
|
|
static exiting void
|
|
unexpected_EOF()
|
|
{
|
|
faterror("unexpected EOF in diff output");
|
|
}
|
|
|
|
int putdtext(num,log,srcfilename,fout,diffmt)
|
|
char const *num, *srcfilename;
|
|
struct cbuf log;
|
|
FILE *fout;
|
|
int diffmt;
|
|
/* Function: write a deltatext-node to fout.
|
|
* num points to the deltanumber, log to the logmessage, and
|
|
* sourcefile contains the text. Doubles up all SDELIMs in both the
|
|
* log and the text; Makes sure the log message ends in \n.
|
|
* returns false on error.
|
|
* If diffmt is true, also checks that text is valid diff -n output.
|
|
*/
|
|
{
|
|
RILE *fin;
|
|
int result;
|
|
if (!(fin = Iopen(srcfilename, "r", (struct stat*)0))) {
|
|
eerror(srcfilename);
|
|
return false;
|
|
}
|
|
result = putdftext(num,log,fin,fout,diffmt);
|
|
Ifclose(fin);
|
|
return result;
|
|
}
|
|
|
|
void
|
|
putstring(out, delim, s, log)
|
|
register FILE *out;
|
|
struct cbuf s;
|
|
int delim, log;
|
|
/*
|
|
* Output to OUT one SDELIM if DELIM, then the string S with SDELIMs doubled.
|
|
* If LOG is set then S is a log string; append a newline if S is nonempty.
|
|
*/
|
|
{
|
|
register char const *sp;
|
|
register size_t ss;
|
|
|
|
if (delim)
|
|
aputc(SDELIM, out);
|
|
sp = s.string;
|
|
for (ss = s.size; ss; --ss) {
|
|
if (*sp == SDELIM)
|
|
aputc(SDELIM, out);
|
|
aputc(*sp++, out);
|
|
}
|
|
if (s.size && log)
|
|
aputc('\n', out);
|
|
aputc(SDELIM, out);
|
|
}
|
|
|
|
int
|
|
putdftext(num,log,finfile,foutfile,diffmt)
|
|
char const *num;
|
|
struct cbuf log;
|
|
RILE *finfile;
|
|
FILE *foutfile;
|
|
int diffmt;
|
|
/* like putdtext(), except the source file is already open */
|
|
{
|
|
declarecache;
|
|
register FILE *fout;
|
|
register int c;
|
|
register RILE *fin;
|
|
int ed;
|
|
struct diffcmd dc;
|
|
|
|
fout = foutfile;
|
|
aprintf(fout,DELNUMFORM,num,Klog);
|
|
/* put log */
|
|
putstring(fout, true, log, true);
|
|
/* put text */
|
|
aprintf(fout, "\n%s\n%c", Ktext, SDELIM);
|
|
fin = finfile;
|
|
setupcache(fin);
|
|
if (!diffmt) {
|
|
/* Copy the file */
|
|
cache(fin);
|
|
for (;;) {
|
|
cachegeteof(c, break;);
|
|
if (c==SDELIM) aputc(SDELIM,fout); /*double up SDELIM*/
|
|
aputc(c,fout);
|
|
}
|
|
} else {
|
|
initdiffcmd(&dc);
|
|
while (0 <= (ed = getdiffcmd(fin,false,fout,&dc)))
|
|
if (ed) {
|
|
cache(fin);
|
|
while (dc.nlines--)
|
|
do {
|
|
cachegeteof(c, { if (!dc.nlines) goto OK_EOF; unexpected_EOF(); });
|
|
if (c == SDELIM)
|
|
aputc(SDELIM,fout);
|
|
aputc(c,fout);
|
|
} while (c != '\n');
|
|
uncache(fin);
|
|
}
|
|
}
|
|
OK_EOF:
|
|
aprintf(fout, "%c\n", SDELIM);
|
|
return true;
|
|
}
|
|
|
|
void
|
|
initdiffcmd(dc)
|
|
register struct diffcmd *dc;
|
|
/* Initialize *dc suitably for getdiffcmd(). */
|
|
{
|
|
dc->adprev = 0;
|
|
dc->dafter = 0;
|
|
}
|
|
|
|
static exiting void
|
|
badDiffOutput(buf)
|
|
char const *buf;
|
|
{
|
|
faterror("bad diff output line: %s", buf);
|
|
}
|
|
|
|
static exiting void
|
|
diffLineNumberTooLarge(buf)
|
|
char const *buf;
|
|
{
|
|
faterror("diff line number too large: %s", buf);
|
|
}
|
|
|
|
int
|
|
getdiffcmd(finfile, delimiter, foutfile, dc)
|
|
RILE *finfile;
|
|
FILE *foutfile;
|
|
int delimiter;
|
|
struct diffcmd *dc;
|
|
/* Get a editing command output by 'diff -n' from fin.
|
|
* The input is delimited by SDELIM if delimiter is set, EOF otherwise.
|
|
* Copy a clean version of the command to fout (if nonnull).
|
|
* Yield 0 for 'd', 1 for 'a', and -1 for EOF.
|
|
* Store the command's line number and length into dc->line1 and dc->nlines.
|
|
* Keep dc->adprev and dc->dafter up to date.
|
|
*/
|
|
{
|
|
register int c;
|
|
declarecache;
|
|
register FILE *fout;
|
|
register char *p;
|
|
register RILE *fin;
|
|
unsigned long line1, nlines, t;
|
|
char buf[BUFSIZ];
|
|
|
|
fin = finfile;
|
|
fout = foutfile;
|
|
setupcache(fin); cache(fin);
|
|
cachegeteof(c, { if (delimiter) unexpected_EOF(); return -1; } );
|
|
if (delimiter) {
|
|
if (c==SDELIM) {
|
|
cacheget(c);
|
|
if (c==SDELIM) {
|
|
buf[0] = c;
|
|
buf[1] = 0;
|
|
badDiffOutput(buf);
|
|
}
|
|
uncache(fin);
|
|
nextc = c;
|
|
if (fout)
|
|
aprintf(fout, "%c%c", SDELIM, c);
|
|
return -1;
|
|
}
|
|
}
|
|
p = buf;
|
|
do {
|
|
if (buf+BUFSIZ-2 <= p) {
|
|
faterror("diff output command line too long");
|
|
}
|
|
*p++ = c;
|
|
cachegeteof(c, unexpected_EOF();) ;
|
|
} while (c != '\n');
|
|
uncache(fin);
|
|
if (delimiter)
|
|
++rcsline;
|
|
*p = '\0';
|
|
for (p = buf+1; (c = *p++) == ' '; )
|
|
;
|
|
line1 = 0;
|
|
while (isdigit(c)) {
|
|
t = line1 * 10;
|
|
if (
|
|
ULONG_MAX/10 < line1 ||
|
|
(line1 = t + (c - '0')) < t
|
|
)
|
|
diffLineNumberTooLarge(buf);
|
|
c = *p++;
|
|
}
|
|
while (c == ' ')
|
|
c = *p++;
|
|
nlines = 0;
|
|
while (isdigit(c)) {
|
|
t = nlines * 10;
|
|
if (
|
|
ULONG_MAX/10 < nlines ||
|
|
(nlines = t + (c - '0')) < t
|
|
)
|
|
diffLineNumberTooLarge(buf);
|
|
c = *p++;
|
|
}
|
|
if (c || !nlines) {
|
|
badDiffOutput(buf);
|
|
}
|
|
if (line1+nlines < line1)
|
|
diffLineNumberTooLarge(buf);
|
|
switch (buf[0]) {
|
|
case 'a':
|
|
if (line1 < dc->adprev) {
|
|
faterror("backward insertion in diff output: %s", buf);
|
|
}
|
|
dc->adprev = line1 + 1;
|
|
break;
|
|
case 'd':
|
|
if (line1 < dc->adprev || line1 < dc->dafter) {
|
|
faterror("backward deletion in diff output: %s", buf);
|
|
}
|
|
dc->adprev = line1;
|
|
dc->dafter = line1 + nlines;
|
|
break;
|
|
default:
|
|
badDiffOutput(buf);
|
|
}
|
|
if (fout) {
|
|
aprintf(fout, "%s\n", buf);
|
|
}
|
|
dc->line1 = line1;
|
|
dc->nlines = nlines;
|
|
return buf[0] == 'a';
|
|
}
|
|
|
|
|
|
|
|
#ifdef SYNTEST
|
|
|
|
char const cmdid[] = "syntest";
|
|
|
|
int
|
|
main(argc,argv)
|
|
int argc; char * argv[];
|
|
{
|
|
|
|
if (argc<2) {
|
|
aputs("No input file\n",stderr);
|
|
exitmain(EXIT_FAILURE);
|
|
}
|
|
if (!(finptr = Iopen(argv[1], FOPEN_R, (struct stat*)0))) {
|
|
faterror("can't open input file %s", argv[1]);
|
|
}
|
|
Lexinit();
|
|
getadmin();
|
|
putadmin(stdout);
|
|
|
|
gettree();
|
|
puttree(Head,stdout);
|
|
|
|
getdesc(true);
|
|
|
|
nextlex();
|
|
|
|
if (!eoflex()) {
|
|
fatserror("expecting EOF");
|
|
}
|
|
exitmain(EXIT_SUCCESS);
|
|
}
|
|
|
|
|
|
exiting void exiterr() { _exit(EXIT_FAILURE); }
|
|
|
|
|
|
#endif
|
|
|