/*
 * Sample code showing how to use the reentrant ucpp API.
 * This file is public domain.
 */

/*
 * This is an example of how to use ucpp as a preprocessor and lexer
 * into another project. The steps are those described in ucpp README
 * file. To use this code, compile the ucpp source files with
 * STAND_ALONE not defined, and link them with this code. The resulting
 * binary will take a C source file as standard input, preprocess it,
 * and output each non-whitespace token on stdout, with its numerical
 * value (defined as an enum in cpp.h) and its contents. This code
 * defines no system include path.
 *
 * This code supposes that the ucpp files are compiled with PRAGMA_TOKENIZE
 * enabled (see the tune.h file).
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mem.h"
#include "cpp.h"

int main(int argc, char *argv[])
{
	int i, r;
	struct CPP *cpp;
	struct lexer_state ls;

	cpp = new_cpp();

	/* step 1 */
	init_cpp(cpp);

	/* step 2 */
	cpp->no_special_macros = 0;
	cpp->emit_defines = cpp->emit_assertions = 0;

	/* step 3 -- with assertions */
	init_tables(cpp, 1);

	/* step 4 -- no default include path */
	init_include_path(cpp, 0);

	/* step 5 -- no need to reset the two emit_* variables set in 2 */
	cpp->emit_dependencies = 0;

	/* step 6 -- we work with stdin, this is not a real filename */
	set_init_filename(cpp, "[stdin]", 0);

	/* step 7 -- we make sure that assertions are on, and pragma are
	   handled */
	init_lexer_state(&ls);
	init_lexer_mode(&ls);
	ls.flags |= HANDLE_ASSERTIONS | HANDLE_PRAGMA | LINE_NUM;

	/* step 8 -- input is from stdin */
	ls.input = stdin;

	/* step 9 -- we do not have any macro to define, but we add any
	   argument as an include path */
	for (i = 1; i < argc; i ++) add_incpath(cpp, argv[i]);

	/* step 10 -- we are a lexer and we want CONTEXT tokens */
	enter_file(cpp, &ls, ls.flags);

	/* read tokens until end-of-input is reached -- errors (non-zero
	   return values different from CPPERR_EOF) are ignored */
	while ((r = lex(cpp, &ls)) < CPPERR_EOF) {
		if (r) {
			/* error condition -- no token was retrieved */
			continue;
		}
		/* we print each token: its numerical value, and its
		   string content; if this is a PRAGMA token, the
		   string content is in fact a compressed token list,
		   that we uncompress and print. */
		if (ls.ctok->type == PRAGMA) {
			unsigned char *c = (unsigned char *)(ls.ctok->name);

			printf("line %ld: <#pragma>\n", ls.line);
			for (; *c; c ++) {
				int t = *c;

				if (STRING_TOKEN(t)) {
					printf("  <%2d>  ", t);
					for (c ++; *c != PRAGMA_TOKEN_END;
						c ++) putchar(*c);
					putchar('\n');
				} else {
					printf("  <%2d>  `%s'\n", t,
						operators_name[t]);
				}
			}
		} else if (ls.ctok->type == CONTEXT) {
			printf("new context: file '%s', line %ld\n",
				ls.ctok->name, ls.ctok->line);
		} else if (ls.ctok->type == NEWLINE) {
			printf("[newline]\n");
		} else {
			printf("line %ld: <%2d>  `%s'\n", ls.ctok->line,
				ls.ctok->type,
				STRING_TOKEN(ls.ctok->type) ? ls.ctok->name
				: operators_name[ls.ctok->type]);
		}
	}

	/* give back memory and exit */
	wipeout(cpp);
	free_lexer_state(&ls);
	del_cpp(cpp);
#ifdef MEM_DEBUG
	report_leaks();
#endif
	return 0;
}
