/*
 * Copyright (c) 1996, 1997, 1998, 1999, 2000, 2006
 *	Tama Communications Corporation
 *
 * This file is part of GNU GLOBAL.
 *
 * This program 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 3 of the License, or
 * (at your option) any later version.
 * 
 * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>

#include "die.h"
#include "tab.h"

/*
 * Puct and getc are very slow on some platforms including GNU/Linux.
 * Because GLOBAL does not have multi-threaded program,
 * they can be replaced with non thread safe version.
 */
#ifdef HAVE_PUTC_UNLOCKED
#undef putc
#define putc	putc_unlocked
#endif
#ifdef HAVE_GETC_UNLOCKED
#undef getc
#define getc	getc_unlocked
#endif

static int tabs = 8;

/**
 * settabs: set default tab stop
 *
 *	@param[in]	n	tab stop (1...32 only)
 */
void
settabs(int n)
{
	if (n < 1 || n > 32)
		return;
	tabs = n;
}
/**
 * size_t read_file_detabing(char *buf, size_t size, FILE *ip, int *dest_saved, int *spaces_saved)
 *
 * Read file converting tabs into spaces.
 *
 *	@param[out]	buf	
 *	@param[in]	size	size of buf
 *	@param[in]	ip	input file
 *	@param[out]	dest_saved	current column in buf
 *	@param[out]	spaces_saved	left spaces
 *	@return		size of data
 *
 * dest_saved and spaces_saved are control variables.
 * You must initialize them with 0 (zero) when the input file is opened.
 */
#define PUTSPACES \
	do {									\
		int n = (spaces < size) ? spaces : size;			\
		dest += n;							\
		size -= n;							\
		spaces -= n;							\
		do {								\
			*p++ = ' ';						\
		} while (--n);							\
	} while (0)
size_t
read_file_detabing(char *buf, size_t size, FILE *ip, int *dest_saved, int *spaces_saved)
{
	char *p;
	int c, dest, spaces;

	if (size == 0)
		return 0;
	p = buf;
	dest = *dest_saved;
	spaces = *spaces_saved;
	if (spaces > 0)
		PUTSPACES;
	while (size > 0) {
		c = getc(ip);
		if (c == EOF) {
			if (ferror(ip))
				die("read error.");
			break;
		}
		if (c == '\t') {
			spaces = tabs - dest % tabs;
			PUTSPACES;
		} else {
			*p++ = c;
			dest++;
			if (c == '\n')
				dest = 0;
			size--;
		}
	}
	*dest_saved = dest;
	*spaces_saved = spaces;
	return p - buf;
}
/**
 * detab_replacing: convert tabs into spaces and print with replacing.
 *
 *	@param[in]	op	FILE *
 *	@param[in]	buf	string including tabs
 *	@param[in]	replace	replacing function
 */
void
detab_replacing(FILE *op, const char *buf, const char *(*replace)(int c))
{
	int dst, spaces;
	int c;

	dst = 0;
	while ((c = *buf++) != '\0') {
		if (c == '\t') {
			spaces = tabs - dst % tabs;
			dst += spaces;
			do {
				putc(' ', op);
			} while (--spaces);
		} else {
			const char *s = replace(c);
			if (s)
				fputs(s, op);
			else
				putc(c, op);
			dst++;
		}
	}
	putc('\n', op);
}
