/* vi:ts=4:sw=4
 *
 * JCAT	- Japanized CAT
 *
 * Code Contributions By:	Atsushi Nakamura		ann@mrit.mei.co.jp
 *
 */

#define EXTERN
#include "vim.h"
#ifdef JP
#include "jp.h"
#endif
#include "globals.h"
#include "proto.h"

#ifndef M_XENIX
# include <sys/types.h>
#endif

#include <fcntl.h>
#ifndef MSDOS
# ifdef SYSV_UNIX
#  if defined(M_XENIX) || defined(SCO) || defined(UNICOS) || defined(AIX31)
#   include <sys/select.h>
#  else
#   if !defined(AUX3)
#     include <poll.h>
#   endif
#  endif
# else	/* SYSV_UNIX */
#  include <sys/time.h>
# endif	/* !SYSV_UNIX */
#endif /* !MSDOS */

#if defined(FD_ZERO) && defined(SYSV_UNIX)
# undef FD_ZERO
#endif


#undef free

#ifdef IOSIZE
# undef IOSIZE
#endif

/*
#define IOSIZE 20
#define LINESIZE 10
*/
#define IOSIZE 4096
#define LINESIZE 4096


static void usage();

	void
emsg(msg)
	char *msg;
{
	fprintf(stderr, "%s\n", msg);
}

#ifndef MSDOS
# define T_FILE	2
static int text = FALSE;
#endif

static int o_lnum  = FALSE;		/* -n */
static int o_lnume = FALSE;		/* -b */
static int o_vis   = FALSE;		/* -v */
static int o_vist  = FALSE;		/* -t */
static int o_visl  = FALSE;		/* -e */
static int o_smel  = FALSE;		/* -s */
#ifndef MSDOS
static int o_text  = T_FILE;	/* -T, -B */
#endif
static int o_ubf   = FALSE;		/* -u */
static int o_serr  = FALSE;		/* -Q */
static int o_scode = FALSE;		/* -C */

	static void
usage()
{
#ifdef JP
	fprintf(stderr, "Jcat/%s by ann@mrit.mei.co.jp\n", JpVersion);
#else
	fprintf(stderr, "Non Japanized Jcat by ann@mrit.mei.co.jp\n");
#endif
	fprintf(stderr, "Usage: jcat [ -benstuv ] [filename ...]\n");
	fprintf(stderr, "	-n     number the lines.\n");
	fprintf(stderr, "	-b     number the lines omitting blank line.\n");
	fprintf(stderr, "	-v     display non printable characters.\n");
	fprintf(stderr, "	-t     display TAB as ^I and LF as ^L with -v.\n");
	fprintf(stderr, "	-e     display $ at the end of line as with -v.\n");
	fprintf(stderr, "	-s     suppress multiple blank lines.\n");
	fprintf(stderr, "	-u     unbuffered.\n");
	fprintf(stderr, "\n");
#ifndef MSDOS
	fprintf(stderr, "	-T     display NEWLINE as CR-LF(MS-DOS text mode).\n");
	fprintf(stderr, "	-B     display NEWLINE as LF(UNIX text or MS-DOS binary mode).\n");
#endif
	fprintf(stderr, "	-Q     suppress error messages.\n");
#ifdef JP
	fprintf(stderr, "	-C     print kanji code.\n");
	fprintf(stderr, "	-N -J -E -S -X	\n");
	fprintf(stderr, "	       kanji code for output.\n");
	fprintf(stderr, "	-. -,  kanji code for input.\n");
#endif
	fprintf(stderr, "	Use - as a filename to read from standard input.\n");
	fprintf(stderr, "\n");
#ifdef JP
	fprintf(stderr, "Environment JMASK specifies default Kanji code.\n");
#endif
	fprintf(stderr, "Do not concatenate binary files by this program.\n");
	exit(1);
}


char jread;
char jdisp;

	void
main(argc, argv)
	int				argc;
	char		  **argv;
{
#ifdef JP
	static char jmask[4] = JP;
#endif
	int	 i;
	char *cp;

	++argv;
	/*
	 * Process the command line arguments
	 */

#ifdef JP
	if ((cp = (char *)getenv("JMASK")) != NULL)
		strncpy(jmask, cp, 4);

	if (jmask[0] && !strchr("NJESX,.", jmask[0]))
	{
		fprintf(stderr, "Unknown Kanji code %c for reading.\n", jmask[0]);
		return;
	}

	if (jmask[1] && !strchr("NJESX", jmask[1]))
	{
		fprintf(stderr, "Unknown Kanji code %c for writing.\n", jmask[1]);
		return;
	}

	jread = jmask[0];
	jdisp = jmask[1];
#else
	jread = jdisp = JP_NONE;
#endif

	if (argc > 1 && argv[0][0] == '-' && argv[0][1] !=NUL)
	{
		char *cp;
		for(cp = argv[0] + 1; *cp; cp++)
		{
			switch(*cp)
			{
			case 'n':
				o_lnum = TRUE;
				break;

			case 'b':
				o_lnume = TRUE;
				break;

			case 'v':
				o_vis = TRUE;
				break;

			case 't':
				o_vist = TRUE;
				break;

			case 'e':
				o_visl = TRUE;
				break;

			case 's':
				o_smel = TRUE;
				break;

			case 'u':
				o_ubf = TRUE;
				break;

			case 'Q':
				o_serr = TRUE;
				break;

#ifndef MSDOS
			case 'T':
				o_text = TRUE;
				break;

			case 'B':
				o_text = FALSE;
				break;
#endif
#ifdef JP
			case 'C':
				o_scode = TRUE;
				break;

			case 'N':
			case 'J':
			case 'E':
			case 'S':
			case 'X':
				jdisp = *cp;
				break;

			case 'x':
				if (* ++cp == NUL)
				{
					cp --;
					fprintf(stderr, "Next of 'x' should be one of NJESX., \n");
					usage();
					break;
				}

			case ',':
			case '.':
				jread = *cp;
				break;
#endif

			default:
				fprintf(stderr, "Unknown option '%c'\n", *cp);
				usage();
				break;
			}
		}
		++ argv;
		-- argc;
	}

#ifndef MSDOS
	if (o_text != T_FILE)
		text = o_text;
#endif

#if !defined(UNIX) && !defined(MSDOS)
	ExpandWildCards(argc - 1, argv, &numfiles, &files, TRUE, TRUE);
	if (numfiles != 0)
		files_exp = TRUE;
#else
	files = argv;
	numfiles = argc - 1;
#endif

/*
 * execute cat for each file
 */
 	{	int do_cat();
		int lnum;

		lnum = 0;

		if (numfiles == 0)
			do_cat(lnum, 0, NULL);
		else
			for(i = 0; i < numfiles; i++)
			{
				int fd;

#ifdef AMIGA
				fname_case(files[i]);		/* set correct case for file name */
#endif

				if (!strcmp(files[i], "-"))
					fd = 0;
#ifdef UNIX
				else if (o_ubf)
# ifdef O_NDELAY
					fd = open(files[i], O_RDONLY | O_NDELAY);
# else
					fd = open(files[i], O_RDONLY | O_NONBLOCK);
# endif
#endif
				else
					fd = open(files[i], O_RDONLY);

				if (fd >= 0)
				{
					lnum = do_cat(lnum, fd, fd == 0 ? NULL: files[i]);
					close(fd);
				}
				else if (!o_serr)
					fprintf(stderr, "jcat: %s cannot open\n", files[i]);
			}
	}
}


static char round[5], *rp;
static char Rbuf[IOSIZE * 2], *Rp, *Rend;
static int kanji;
static int reof;

	static void
j_init()
{
	reof = kanji = FALSE;
	rp = round;
#ifdef JP
	reset_jcount();
#endif
	Rp = Rend = Rbuf;
}

/*
 *	j_readln(line, size) returns the number of char. transferred into 'line'
 *		note: line is not NUL terminated.
 *		return -1:	EOF reached
 *		return -2:	Error
 */

	static int
j_readln(fd, line, size)
	int fd;
	char *line;
	int size;
{
	int  len;
	char c;

	len = 0;

retry:
	while (Rp != Rend)
	{
		c = *line ++ = *Rp ++;
		len ++;

#ifdef JP
		if (IsKanji(c))
		{
			*line ++ = *Rp ++;
			len ++;
		}
		else 
#endif
		if (c == '\r' && *Rp == '\n')
		{
#ifndef MSDOS
			if (o_text == T_FILE)
				o_text = text = TRUE;
#endif
			/* suppress CR for MS-DOS text */
			c = *(line - 1) = *Rp ++;
		}
#ifndef MSDOS
		else if (c == '\n' && o_text == T_FILE)
			o_text = text = FALSE;
#endif

		if (c == '\n' || len >= size - 1)
			return len;
	}

	if (o_ubf && len)
		return len;

	if (reof)
	{
		if (rp != round)
		{
			memmove(Rbuf, round, rp - round);
			rp = round;
			Rp = Rbuf;
			Rend = Rbuf + (rp - round);
			goto retry;
		}
		else if (len)
			return len;
		else
			return -1;
	}

#ifdef UNIX
	/*
	 *	wait until read data is ready if unbufferd file is opend.
	 */
	if (o_ubf)
# ifndef FD_ZERO
	{
			struct pollfd fds[1];

			fds[0].fd = fd;
			fds[0].events = POLLIN;

			if (poll(fds, 1, -1) == -1)
				return -2;
	}
# else
	{
		extern int select();
		fd_set fds;

		FD_ZERO(&fds);
		FD_SET(fd, &fds);

		if (select(32, &fds, NULL, NULL, NULL) <= 0)
			return -2;
		if (!FD_ISSET(fd, &fds))
			return -2;
	}
# endif
#endif
	/* reload Rbuf */
	{
		static char cbuf[IOSIZE];
		int  clen;
		int  ofst;

		if (rp != round)	/* flush round buffer */
		{
			memmove(cbuf, round, ofst = rp - round);
			rp = round;
		}
		else
			ofst = 0;

		clen = read(fd, cbuf + ofst, IOSIZE - ofst);
		if (clen < 0)
			return -2;

		reof = (clen == 0);
		clen += ofst;

		Rend = Rp = Rbuf;
#ifdef JP
		Rend += kanjiconvsfrom(cbuf, clen, Rbuf, IOSIZE * 2, round,
															jread, &kanji);
#endif
		rp = round + strlen(round);

		goto retry;
	}
}

	int
do_cat(lnum, fd, fname)
	int lnum;
	int fd;
	char *fname;
{
	static char Lbuf[LINESIZE];
	static char Cbuf[LINESIZE * 2 + 2];
	char *Lp, *Cp;

	int len;
	int linetop;
	int lastempty;

	char *prline;
	char jcode;

#ifdef JP
	j_init();
#endif
	linetop = TRUE;
	lastempty = FALSE;
	lnum = 0;

	while((len = j_readln(fd, Lbuf, LINESIZE)) != -1)
	{
		int isempty;
		int tailnl;
		char *cp;

		if (len == -2)
		{
			fprintf(stderr, "jcat: read error(%s).",
										fname ? fname: "standard input");
			exit(-1);
		}

		isempty = linetop && Lbuf[0] == '\n';

		if (linetop)
		{
			lnum++;
			if (isempty && o_lnume)
				lnum--;
			else if (lastempty && isempty && o_smel)
				continue;
			else if (o_lnum || o_lnume)
			{
				char linehead[20];
				sprintf(linehead, "%6d  ", lnum);
				write(1, linehead, 6 + 2);
			}
		}

		lastempty = isempty;
		linetop = FALSE;

		/*
		 *	Generate print string.
		 */
		tailnl = FALSE;
		for(Lp = Lbuf, Cp = Cbuf; len; Lp++, Cp++, len--)
		{
			char c;

			c = *Lp;
#ifdef JP
			if (IsKanji(c))
			{
				if (o_vis && c == '\377')
				{
					*Cp ++ = '~';
					*Cp = '?';
				}
				else if (o_vis && (c & 0xe0) == 0)
				{
					*Cp ++ = '~';
					*Cp = (c & 0x1f) + '@';
				}
				else
				{
					*Cp ++ = c;
					*Cp = * ++Lp;
					len --;
				}
			}
			else
#endif
			{
				if (c == '\n')
				{
					if (o_vis && o_visl)
						*Cp ++ = '$';
					*Cp = c;
					linetop = TRUE;
					tailnl = TRUE;
				}
				else if (!o_vis)
					*Cp = (c == NUL) ? '\n' : c;
				else if (!o_vist && (c == TAB || c == Ctrl('L')))
					*Cp = c;
				else if ((c & 0xe0) == 0)
				{
					*Cp ++ = '^';
					*Cp = c + '@';
				}
				else if (c == '\177')
				{
					*Cp ++ = '^';
					*Cp = '?';
				}
				else
					*Cp = c;
			}
		}
		*Cp = NUL;

#ifdef JP
		prline  = kanjiconvsto(Cbuf, jdisp);
#else
		prline  = Cbuf;
#endif
		{
			int crlf;

			crlf = FALSE;
			for(cp = prline, len = 0; *cp; cp++, len++)
				if (*cp == '\n')
				{
					if (tailnl && *(cp + 1) == NUL)
					{
#ifndef MSDOS
						if (!text)
							continue;
#endif
						crlf = TRUE;
						break;
					}
					*cp = NUL;
				}
			if (write(1, prline, len) < 0 )
				exit(-1);

			if (crlf)
				if (write(1, "\r\n", 2) < 0 )
					exit(-1);
		}

		if (prline != Cbuf)
			free(prline);
	}

#ifdef JP
	jcode = judge_jcode(jread);

	if (!o_serr)
	{
		int  jisx0201r;
		extern int  num_jis0201r();

		if (jread == JP_ANY  && jcode == JP_SJIS)
		{
			if (fname)
				fprintf(stderr, "%s: ", fname);
			fprintf(stderr, "EUC SJIS conflict: ");
			fprintf(stderr, "Use ',' option to read SJIS file.\n");
		}
		if (jread == JP_SANY && jcode == JP_EUC)
		{
			if (fname)
				fprintf(stderr, "%s: ", fname);
			fprintf(stderr, "EUC SJIS conflict: ");
			fprintf(stderr, "Use '.' option to read EUC file.\n");
		}

		jisx0201r = num_jis0201r();
		if (jisx0201r)
		{
			if (fname)
				fprintf(stderr, "%s: ", fname);
			fprintf(stderr, "%d Hankaku Kana -> Zenkaku Katakana\n", jisx0201r);
		}
	}

	if (o_scode)
	{
		if (fname)
			printf("%s: ", fname);
		printf("%c\n", jcode);
	}
#endif

	return lnum;
}

	void
getout(r)
	int	r;
{
	fprintf(stderr, "\n");
	exit(r);
}



syntax highlighted by Code2HTML, v. 0.9.1