1/* This is the Assembler Pre-Processor
2   Copyright (C) 1987 Free Software Foundation, Inc.
3
4This file is part of GAS, the GNU Assembler.
5
6GAS is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation; either version 1, or (at your option)
9any later version.
10
11GAS is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with GAS; see the file COPYING.  If not, write to
18the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
19
20/* App, the assembler pre-processor.  This pre-processor strips out excess
21   spaces, turns single-quoted characters into a decimal constant, and turns
22   # <number> <filename> <garbage> into a .line <number>;.file <filename> pair.
23   This needs better error-handling.
24 */
25#include <stdio.h>
26#include <string.h>
27#include "as.h"
28#include "md.h"
29#include "app.h"
30#include "messages.h"
31
32FILE *scrub_file = NULL;
33char *scrub_string = NULL;
34char *scrub_last_string = NULL;
35
36#ifdef NeXT_MOD	/* .include feature */
37/* These are moved out of do_scrub() so save_scrub_context() can save them */
38static int state;
39#ifdef I386
40static int substate = 0;
41#endif
42static int old_state;
43static char *out_string;
44static char out_buf[20];
45static int add_newlines = 0;
46#endif /* NeXT_MOD .include feature */
47
48static char	lex [256];
49static char	symbol_chars[] =
50	"$._ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
51
52#define LEX_IS_SYMBOL_COMPONENT		(1)
53#define LEX_IS_WHITESPACE		(2)
54#define LEX_IS_LINE_SEPERATOR		(4)
55#define LEX_IS_COMMENT_START		(8)	/* JF added these two */
56#define LEX_IS_LINE_COMMENT_START	(16)
57#define IS_SYMBOL_COMPONENT(c)		(lex [c] & LEX_IS_SYMBOL_COMPONENT)
58#define IS_WHITESPACE(c)		(lex [c] & LEX_IS_WHITESPACE)
59#define IS_LINE_SEPERATOR(c)		(lex [c] & LEX_IS_LINE_SEPERATOR)
60#define IS_COMMENT(c)			(lex [c] & LEX_IS_COMMENT_START)
61#define IS_LINE_COMMENT(c)		(lex [c] & LEX_IS_LINE_COMMENT_START)
62
63void
64do_scrub_begin(
65void)
66{
67    char *p;
68    const char *q;
69
70	memset(lex, '\0', sizeof(lex));		/* Trust NOBODY! */
71	lex [' ']		|= LEX_IS_WHITESPACE;
72	lex ['\t']		|= LEX_IS_WHITESPACE;
73	lex ['\r']		|= LEX_IS_WHITESPACE;
74	for (p =symbol_chars;*p;++p)
75		lex [(int)*p] |= LEX_IS_SYMBOL_COMPONENT;
76	lex ['\n']		|= LEX_IS_LINE_SEPERATOR;
77#ifdef PPC
78	if(flagseen[(int)'p'] == TRUE)
79	    lex ['\r']		|= LEX_IS_LINE_SEPERATOR;
80#endif /* PPC */
81#ifndef DONTDEF
82#ifdef NeXT_MOD
83	/*
84	 * This DOES not cause ':' to be a LINE SEPERATOR but does make the
85	 * second if logic after flushchar: in do_scrub_next_char() to handle
86	 * "foo :" and strip the blanks.  This is the way has always been and
87	 * must be this way to work.
88	 */
89#endif /* NeXT_MOD */
90	lex [':']		|= LEX_IS_LINE_SEPERATOR;
91#endif /* !defined(DONTDEF) */
92
93#if defined(M88K) || defined(PPC) || defined(HPPA)
94#ifdef PPC
95	if(flagseen[(int)'p'] == FALSE)
96#endif /* PPC */
97	    lex ['@']		|= LEX_IS_LINE_SEPERATOR;
98#else
99	lex [';']		|= LEX_IS_LINE_SEPERATOR;
100#endif
101	for (q=md_comment_chars;*q;q++)
102		lex[(int)*q] |= LEX_IS_COMMENT_START;
103	for (q=md_line_comment_chars;*q;q++)
104		lex[(int)*q] |= LEX_IS_LINE_COMMENT_START;
105}
106
107static inline int
108scrub_from_string(
109void)
110{
111	return scrub_string == scrub_last_string ? EOF : *scrub_string++;
112}
113
114static inline void
115scrub_to_string(
116int ch)
117{
118	*--scrub_string = ch;
119}
120
121int
122do_scrub_next_char(
123FILE *fp)
124{
125	/* State 0: beginning of normal line
126		1: After first whitespace on normal line (flush more white)
127		2: After first non-white on normal line (keep 1white)
128		3: after second white on normal line (flush white)
129		4: after putting out a .line, put out digits
130		5: parsing a string, then go to old-state
131		6: putting out \ escape in a "d string.
132		7: After putting out a .file, put out string.
133		8: After putting out a .file string, flush until newline.
134	        FROM line 358
135		9: After seeing symbol char in state 3 (keep 1white after symchar)
136	       10: After seeing whitespace in state 9 (keep white before symchar)
137		-1: output string in out_string and go to the state in old_state
138		-2: flush text until a '*' '/' is seen, then go to state old_state
139	*/
140
141
142  /* FROM line 379 */
143  /* I added states 9 and 10 because the MIPS ECOFF assembler uses
144     constructs like ``.loc 1 20''.  This was turning into ``.loc
145     120''.  States 9 and 10 ensure that a space is never dropped in
146     between characters which could appear in an identifier.  Ian
147     Taylor, ian@cygnus.com.  */
148
149#ifndef NeXT_MOD	/* .include feature */
150	static state;
151	static old_state;
152	static char *out_string;
153	static char out_buf[20];
154	static add_newlines = 0;
155#endif /* NeXT_MOD .include feature */
156	int ch;
157
158	if(state==-1) {
159		ch= *out_string++;
160		if(*out_string==0) {
161			state=old_state;
162			old_state=3;
163		}
164		return ch;
165	}
166	if(state==-2) {
167		for(;;) {
168			do ch=getc_unlocked(fp);
169			while(ch!=EOF && ch!='\n' && ch!='*');
170			if(ch=='\n' || ch==EOF)
171				return ch;
172			 ch=getc_unlocked(fp);
173			 if(ch==EOF || ch=='/')
174			 	break;
175			ungetc(ch, fp);
176		}
177		state=old_state;
178		return ' ';
179	}
180	if(state==4) {
181		ch=getc_unlocked(fp);
182		if(ch==EOF || (ch>='0' && ch<='9'))
183			return ch;
184		else {
185			while(ch!=EOF && IS_WHITESPACE(ch))
186				ch=getc_unlocked(fp);
187			if(ch=='"') {
188				ungetc(ch, fp);
189#if defined(M88K) || defined(PPC) || defined(HPPA)
190				out_string="@ .file ";
191#else
192				out_string="; .file ";
193#endif
194				old_state=7;
195				state= -1;
196				return *out_string++;
197			} else {
198				while(ch!=EOF && ch!='\n')
199					ch=getc_unlocked(fp);
200#ifdef NeXT_MOD
201				/* bug fix for bug #8918, which was when
202				 * a full line comment line this:
203				 * # 40 MP1 = M + 1
204				 * got confused with a cpp output like:
205				 * # 1 "hello.c" 1
206				 */
207				state = 0;
208#endif /* NeXT_MOD */
209				return ch;
210			}
211		}
212	}
213	if(state==5) {
214		ch=getc_unlocked(fp);
215#ifdef PPC
216		if(flagseen[(int)'p'] == TRUE && ch=='\'') {
217			state=old_state;
218			return '\'';
219		} else
220#endif /* PPC */
221		if(ch=='"') {
222			state=old_state;
223			return '"';
224		} else if(ch=='\\') {
225			state=6;
226			return ch;
227		} else if(ch==EOF) {
228 			state=old_state;
229			ungetc('\n', fp);
230#ifdef PPC
231			if(flagseen[(int)'p'] == TRUE){
232			    as_warn("End of file in string: inserted '\''");
233			    return '\'';
234			}
235#endif /* PPC */
236			as_warn("End of file in string: inserted '\"'");
237			return '"';
238		} else {
239			return ch;
240		}
241	}
242	if(state==6) {
243		state=5;
244		ch=getc_unlocked(fp);
245		switch(ch) {
246			/* This is neet.  Turn "string
247			   more string" into "string\n  more string"
248			 */
249		case '\n':
250			ungetc('n', fp);
251			add_newlines++;
252			return '\\';
253
254		case '\'':
255		case '"':
256		case '\\':
257		case 'b':
258		case 'f':
259		case 'n':
260		case 'r':
261		case 't':
262		case '0':
263		case '1':
264		case '2':
265		case '3':
266		case '4':
267		case '5':
268		case '6':
269		case '7':
270			break;
271		default:
272			as_warn("Unknown escape '\\%c' in string: Ignored",ch);
273			break;
274
275		case EOF:
276			as_warn("End of file in string: '\"' inserted");
277			return '"';
278		}
279		return ch;
280	}
281
282	if(state==7) {
283		ch=getc_unlocked(fp);
284		state=5;
285		old_state=8;
286		return ch;
287	}
288
289	if(state==8) {
290		do ch= getc_unlocked(fp);
291		while(ch!='\n');
292		state=0;
293#ifdef I386
294		substate = 0;
295#endif
296		return ch;
297	}
298
299 flushchar:
300	ch=getc_unlocked(fp);
301	switch(ch) {
302	case ' ':
303	case '\t':
304		do ch=getc_unlocked(fp);
305		while(ch!=EOF && IS_WHITESPACE(ch));
306		if(ch==EOF)
307			return ch;
308		if(IS_COMMENT(ch) || (state==0 && IS_LINE_COMMENT(ch)) || ch=='/' || IS_LINE_SEPERATOR(ch)) {
309			ungetc(ch, fp);
310			goto flushchar;
311		}
312		ungetc(ch, fp);
313		if(state==0 || state==2) {
314#ifdef I386
315			if(state == 2){
316			    if(substate == 0){
317				/* if in state 2 don't change to state 3
318				   the first time, and leave white space
319				   after the first two tokens */
320				substate = 1;
321				return ' ';
322			    }
323			    substate = 0;
324			}
325#endif
326			state++;
327			return ' ';
328		}
329#ifdef PPC
330		if(flagseen[(int)'p'] == TRUE && state == 3){
331			return ' ';
332		}
333#endif
334		/* FROM line 900 */
335		if (state == 9)
336		  state = 10;
337		goto flushchar;
338
339	case '/':
340		ch=getc_unlocked(fp);
341		if(ch=='*') {
342			for(;;) {
343				do {
344					ch=getc_unlocked(fp);
345					if(ch=='\n')
346						add_newlines++;
347				} while(ch!=EOF && ch!='*');
348				ch=getc_unlocked(fp);
349				if(ch==EOF || ch=='/')
350					break;
351				ungetc(ch, fp);
352			}
353			if(ch==EOF)
354				as_warn("End of file in '/' '*' string: */ inserted");
355
356			ungetc(' ', fp);
357			goto flushchar;
358		} else {
359#if defined(I860) || defined(M88K) || defined(PPC) || defined(I386) || \
360    defined(HPPA) || defined (SPARC)
361		  if (ch == '/') {
362		    do {
363		      ch=getc_unlocked(fp);
364		    } while (ch != EOF && (ch != '\n'));
365		    if (ch == EOF)
366		      as_warn("End of file before newline in // comment");
367		    if ( ch == '\n' )	/* Push NL back so we can complete state */
368		    	ungetc(ch, fp);
369		    goto flushchar;
370		  }
371#endif
372			if(IS_COMMENT('/') || (state==0 && IS_LINE_COMMENT('/'))) {
373				ungetc(ch, fp);
374				ch='/';
375				goto deal_misc;
376			}
377			if(ch!=EOF)
378				ungetc(ch, fp);
379			return '/';
380		}
381		break;
382
383	case '"':
384		old_state=state;
385		state=5;
386		return '"';
387		break;
388
389	case '\'':
390#ifdef PPC
391		if(flagseen[(int)'p'] == TRUE){
392			old_state=state;
393			state=5;
394			return '\'';
395			break;
396		}
397#endif
398		ch=getc_unlocked(fp);
399		if(ch==EOF) {
400			as_warn("End-of-file after a ': \\000 inserted");
401			ch=0;
402		}
403		sprintf(out_buf,"(%d)",ch&0xff);
404		old_state=state;
405		state= -1;
406		out_string=out_buf;
407		return *out_string++;
408
409	case ':':
410		if(state!=3) {
411			state=0;
412#ifdef I386
413			substate = 0;
414#endif
415		}
416		return ch;
417
418	case '\n':
419		if(add_newlines) {
420			--add_newlines;
421			ungetc(ch, fp);
422		}
423	/* Fall through.  */
424#if defined(M88K) || defined(PPC) || defined(HPPA)
425	case '@':
426#else
427	case ';':
428#endif
429		state=0;
430#ifdef I386
431		substate = 0;
432#endif
433		return ch;
434
435	default:
436	deal_misc:
437		/* FROM line 1234 */
438		if (IS_SYMBOL_COMPONENT (ch))
439		  {
440		    if (state == 10)
441		      {
442			/* This is a symbol character following another symbol
443			   character, with whitespace in between.  We skipped
444			   the whitespace earlier, so output it now.  */
445			ungetc(ch, fp);
446			state = 3;
447			ch = ' ';
448			return ch;
449		      }
450
451		    if (state == 3)
452		      state = 9;
453		  }
454		/* FROM line 1321 */
455		else if (state == 9)
456		  {
457		    /* FROM line 1324 */
458		    state = 3;
459		  }
460		else if (state == 10)
461		  /* FROM line 1345 */
462		  state = 3;
463
464		if(state==0 && IS_LINE_COMMENT(ch)) {
465			do ch=getc_unlocked(fp);
466			while(ch!=EOF && IS_WHITESPACE(ch));
467			if(ch==EOF) {
468				as_warn("EOF in comment:  Newline inserted");
469				return '\n';
470			}
471			if(ch<'0' || ch>'9') {
472				if(ch!='\n'){
473					do ch=getc_unlocked(fp);
474					while(ch!=EOF && ch!='\n');
475				}
476				if(ch==EOF)
477					as_warn("EOF in Comment: Newline inserted");
478				state=0;
479#ifdef I386
480				substate = 0;
481#endif
482				return '\n';
483			}
484			ungetc(ch, fp);
485			old_state=4;
486			state= -1;
487			out_string=".line ";
488			return *out_string++;
489
490		} else if(IS_COMMENT(ch)) {
491			do ch=getc_unlocked(fp);
492			while(ch!=EOF && ch!='\n');
493			if(ch==EOF)
494				as_warn("EOF in comment:  Newline inserted");
495			state=0;
496#ifdef I386
497			substate = 0;
498#endif
499			return '\n';
500
501		} else if(state==0) {
502			state=2;
503			return ch;
504		} else if(state==1) {
505			state=2;
506			return ch;
507		} else {
508			return ch;
509
510		}
511	case EOF:
512		if(state==0)
513			return ch;
514		as_warn("End-of-File not at end of a line");
515	}
516	return -1;
517}
518
519int
520do_scrub_next_char_from_string()
521{
522	/* State 0: beginning of normal line
523		1: After first whitespace on normal line (flush more white)
524		2: After first non-white on normal line (keep 1white)
525		3: after second white on normal line (flush white)
526		4: after putting out a .line, put out digits
527		5: parsing a string, then go to old-state
528		6: putting out \ escape in a "d string.
529		7: After putting out a .file, put out string.
530		8: After putting out a .file string, flush until newline.
531		-1: output string in out_string and go to the state in old_state
532		-2: flush text until a '*' '/' is seen, then go to state old_state
533	*/
534
535#ifndef NeXT_MOD	/* .include feature */
536	static state;
537	static old_state;
538	static char *out_string;
539	static char out_buf[20];
540	static add_newlines = 0;
541#endif /* NeXT_MOD .include feature */
542	int ch;
543
544	if(state==-1) {
545		ch= *out_string++;
546		if(*out_string==0) {
547			state=old_state;
548			old_state=3;
549		}
550		return ch;
551	}
552	if(state==-2) {
553		for(;;) {
554			do ch=scrub_from_string();
555			while(ch!=EOF && ch!='\n' && ch!='*');
556			if(ch=='\n' || ch==EOF)
557				return ch;
558			 ch=scrub_from_string();
559			 if(ch==EOF || ch=='/')
560			 	break;
561			scrub_to_string(ch);
562		}
563		state=old_state;
564		return ' ';
565	}
566	if(state==4) {
567		ch=scrub_from_string();
568		if(ch==EOF || (ch>='0' && ch<='9'))
569			return ch;
570		else {
571			while(ch!=EOF && IS_WHITESPACE(ch))
572				ch=scrub_from_string();
573			if(ch=='"') {
574				scrub_to_string(ch);
575#if defined(M88K) || defined(PPC) || defined(HPPA)
576				out_string="@ .file ";
577#else
578				out_string="; .file ";
579#endif
580				old_state=7;
581				state= -1;
582				return *out_string++;
583			} else {
584				while(ch!=EOF && ch!='\n')
585					ch=scrub_from_string();
586#ifdef NeXT_MOD
587				/* bug fix for bug #8918, which was when
588				 * a full line comment line this:
589				 * # 40 MP1 = M + 1
590				 * got confused with a cpp output like:
591				 * # 1 "hello.c" 1
592				 */
593				state = 0;
594#endif /* NeXT_MOD */
595				return ch;
596			}
597		}
598	}
599	if(state==5) {
600		ch=scrub_from_string();
601#ifdef PPC
602		if(flagseen[(int)'p'] == TRUE && ch=='\'') {
603			state=old_state;
604			return '\'';
605		} else
606#endif /* PPC */
607		if(ch=='"') {
608			state=old_state;
609			return '"';
610		} else if(ch=='\\') {
611			state=6;
612			return ch;
613		} else if(ch==EOF) {
614 			state=old_state;
615			scrub_to_string('\n');
616#ifdef PPC
617			if(flagseen[(int)'p'] == TRUE){
618			    as_warn("End of file in string: inserted '\''");
619			    return '\'';
620			}
621#endif /* PPC */
622			as_warn("End of file in string: inserted '\"'");
623			return '"';
624		} else {
625			return ch;
626		}
627	}
628	if(state==6) {
629		state=5;
630		ch=scrub_from_string();
631		switch(ch) {
632			/* This is neet.  Turn "string
633			   more string" into "string\n  more string"
634			 */
635		case '\n':
636			scrub_to_string('n');
637			add_newlines++;
638			return '\\';
639
640		case '"':
641		case '\\':
642		case 'b':
643		case 'f':
644		case 'n':
645		case 'r':
646		case 't':
647		case '0':
648		case '1':
649		case '2':
650		case '3':
651		case '4':
652		case '5':
653		case '6':
654		case '7':
655			break;
656		default:
657			as_warn("Unknown escape '\\%c' in string: Ignored",ch);
658			break;
659
660		case EOF:
661			as_warn("End of file in string: '\"' inserted");
662			return '"';
663		}
664		return ch;
665	}
666
667	if(state==7) {
668		ch=scrub_from_string();
669		state=5;
670		old_state=8;
671		return ch;
672	}
673
674	if(state==8) {
675		do ch= scrub_from_string();
676		while(ch!='\n');
677		state=0;
678#ifdef I386
679		substate = 0;
680#endif
681		return ch;
682	}
683
684 flushchar:
685	ch=scrub_from_string();
686	switch(ch) {
687	case ' ':
688	case '\t':
689		do ch=scrub_from_string();
690		while(ch!=EOF && IS_WHITESPACE(ch));
691		if(ch==EOF)
692			return ch;
693		if(IS_COMMENT(ch) || (state==0 && IS_LINE_COMMENT(ch)) || ch=='/' || IS_LINE_SEPERATOR(ch)) {
694			scrub_to_string(ch);
695			goto flushchar;
696		}
697		scrub_to_string(ch);
698		if(state==0 || state==2) {
699			state++;
700			return ' ';
701		}
702#ifdef PPC
703		if(flagseen[(int)'p'] == TRUE && state == 3){
704			return ' ';
705		}
706#endif
707		else goto flushchar;
708
709	case '/':
710		ch=scrub_from_string();
711		if(ch=='*') {
712			for(;;) {
713				do {
714					ch=scrub_from_string();
715					if(ch=='\n')
716						add_newlines++;
717				} while(ch!=EOF && ch!='*');
718				ch=scrub_from_string();
719				if(ch==EOF || ch=='/')
720					break;
721				scrub_to_string(ch);
722			}
723			if(ch==EOF)
724				as_warn("End of file in '/' '*' string: */ inserted");
725
726			scrub_to_string(' ');
727			goto flushchar;
728		} else {
729#if defined(I860) || defined(M88K) || defined(PPC) || defined(I386) || \
730    defined(HPPA) || defined (SPARC)
731		  if (ch == '/') {
732		    do {
733		      ch=scrub_from_string();
734		    } while (ch != EOF && (ch != '\n'));
735		    if (ch == EOF)
736		      as_warn("End of file before newline in // comment");
737		    if ( ch == '\n' )	/* Push NL back so we can complete state */
738		    	scrub_to_string(ch);
739		    goto flushchar;
740		  }
741#endif
742			if(IS_COMMENT('/') || (state==0 && IS_LINE_COMMENT('/'))) {
743				scrub_to_string(ch);
744				ch='/';
745				goto deal_misc;
746			}
747			if(ch!=EOF)
748				scrub_to_string(ch);
749			return '/';
750		}
751		break;
752
753	case '"':
754		old_state=state;
755		state=5;
756		return '"';
757		break;
758
759	case '\'':
760#ifdef PPC
761		if(flagseen[(int)'p'] == TRUE){
762			old_state=state;
763			state=5;
764			return '\'';
765			break;
766		}
767#endif
768		ch=scrub_from_string();
769		if(ch==EOF) {
770			as_warn("End-of-file after a ': \\000 inserted");
771			ch=0;
772		}
773		sprintf(out_buf,"(%d)",ch&0xff);
774		old_state=state;
775		state= -1;
776		out_string=out_buf;
777		return *out_string++;
778
779	case ':':
780		if(state!=3) {
781			state=0;
782#ifdef I386
783			substate = 0;
784#endif
785		}
786		return ch;
787
788	case '\n':
789		if(add_newlines) {
790			--add_newlines;
791			scrub_to_string(ch);
792		}
793	/* Fall through.  */
794#if defined(M88K) || defined(PPC) || defined(HPPA)
795	case '@':
796#else
797	case ';':
798#endif
799		state=0;
800#ifdef I386
801		substate = 0;
802#endif
803		return ch;
804
805	default:
806	deal_misc:
807		if(state==0 && IS_LINE_COMMENT(ch)) {
808			do ch=scrub_from_string();
809			while(ch!=EOF && IS_WHITESPACE(ch));
810			if(ch==EOF) {
811				as_warn("EOF in comment:  Newline inserted");
812				return '\n';
813			}
814			if(ch<'0' || ch>'9') {
815				if(ch!='\n'){
816					do ch=scrub_from_string();
817					while(ch!=EOF && ch!='\n');
818				}
819				if(ch==EOF)
820					as_warn("EOF in Comment: Newline inserted");
821				state=0;
822#ifdef I386
823				substate = 0;
824#endif
825				return '\n';
826			}
827			scrub_to_string(ch);
828			old_state=4;
829			state= -1;
830			out_string=".line ";
831			return *out_string++;
832
833		} else if(IS_COMMENT(ch)) {
834			do ch=scrub_from_string();
835			while(ch!=EOF && ch!='\n');
836			if(ch==EOF)
837				as_warn("EOF in comment:  Newline inserted");
838			state=0;
839#ifdef I386
840	    		substate = 0;
841#endif
842			return '\n';
843
844		} else if(state==0) {
845			state=2;
846			return ch;
847		} else if(state==1) {
848			state=2;
849			return ch;
850		} else {
851			return ch;
852
853		}
854	case EOF:
855		if(state==0)
856			return ch;
857		as_warn("End-of-File not at end of a line");
858	}
859	return -1;
860}
861
862
863#ifdef NeXT_MOD	/* .include feature */
864void
865save_scrub_context(
866scrub_context_data *save_buffer_ptr)
867{
868	save_buffer_ptr->last_scrub_file = scrub_file;
869	save_buffer_ptr->last_state = state;
870	save_buffer_ptr->last_old_state = old_state;
871	save_buffer_ptr->last_out_string = out_string;
872	memcpy(save_buffer_ptr->last_out_buf, out_buf, sizeof(out_buf));
873	save_buffer_ptr->last_add_newlines = add_newlines;
874
875	state = 0;
876	old_state = 0;
877	out_string = NULL;
878	memset(out_buf, '\0', sizeof(out_buf));
879	add_newlines = 0;
880}
881
882void
883restore_scrub_context(
884scrub_context_data *save_buffer_ptr)
885{
886	scrub_file = save_buffer_ptr->last_scrub_file;
887	state = save_buffer_ptr->last_state;
888	old_state = save_buffer_ptr->last_old_state;
889	out_string = save_buffer_ptr->last_out_string;
890	memcpy(out_buf, save_buffer_ptr->last_out_buf, sizeof(out_buf));
891	add_newlines = save_buffer_ptr->last_add_newlines;
892}
893#endif /* NeXT_MOD .include feature */
894
895#ifdef TEST
896#include <stdarg.h>
897
898const char md_comment_chars[] = "|";
899const char md_line_comment_chars[] = "#";
900
901#ifdef PPC
902/* ['x'] TRUE if "-x" seen. */
903char flagseen[128] = { 0 };
904#endif
905
906int
907main(
908int argc,
909char *argv[],
910char *envp[])
911{
912    int ch;
913#ifdef PPC
914	if(argc > 1 && strncmp(argv[1], "-p", 2) == 0)
915	    flagseen[(int)'p'] = TRUE;
916#endif
917
918	while((ch = do_scrub_next_char(stdin)) != EOF)
919	    putc(ch, stdout);
920	return(0);
921}
922
923void
924as_warn(
925const char *format,
926...)
927{
928    va_list ap;
929
930	va_start(ap, format);
931	vfprintf(stderr, format, ap);
932	fprintf(stderr, "\n");
933	va_end(ap);
934}
935#endif /* TEST */
936