Skip to content
Snippets Groups Projects
ans2asc.c 9.09 KiB
Newer Older
/* Convert ANSI messages to Synchronet Ctrl-A code format */

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
rswindell's avatar
rswindell committed
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
 *																			*
 * 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 2			*
 * of the License, or (at your option) any later version.					*
 * See the GNU General Public License for more details: gpl.txt or			*
 * http://www.fsf.org/copyleft/gpl.html										*
 *																			*
 * For Synchronet coding style and modification guidelines, see				*
 * http://www.synchro.net/source.html										*
 *																			*
 * Note: If this box doesn't appear square, then you need to fix your tabs.	*
 ****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>     /* strcmp */
#include "git_branch.h"
#include "git_hash.h"
static void print_usage(const char* prog)
	fprintf(stderr, "\nSynchronet ANSI-Terminal-Sequence to Ctrl-A-Code Conversion Utility %s/%s\n"
	        , GIT_BRANCH
	        , GIT_HASH
	        );
	fprintf(stderr, "\nusage: %s infile.ans [outfile.asc | outfile.msg] [[option] [...]]\n", prog);
	fprintf(stderr, "\noptions:\n\n");
	fprintf(stderr, "  -ice            treat blink as bright-background (iCE colors)\n");
	fprintf(stderr, "  -<columns>      insert conditional-newlines to force wrap (e.g. -80)\n");
	fprintf(stderr, "  -normal         use 'save/normal/restore' attributes for conditional new-lines\n");
	fprintf(stderr, "  -newline        append a newline (CRLF) sequence to output file\n");
	fprintf(stderr, "  -clear          insert a clear screen code at beginning of output file\n");
	fprintf(stderr, "  -pause          append a pause (hit a key) code to end of output file\n");
	fprintf(stderr, "  -space          use space characters for cursor-right movement/alignment\n");
	fprintf(stderr, "  -esc            use C-style string literal escaping of control and CP437 chars\n");
	fprintf(stderr, "  -delay <int>    insert a 1/10th second delay code at output byte interval\n");
	fprintf(stderr, "                  (lower interval values result in more delays, slower display)\n");
	unsigned char esc, n[25];
	int           i, ch, ni;
	FILE *        in = stdin;
	FILE *        out = stdout;
	bool          ice = false;
	bool          normal = false;
	bool          encode = false;
	int           cols = 0;
	int           column = 0;
	int           delay = 0;
	int           clear = 0;
	int           pause = 0;
	int           space = 0;
	int           newline = 0;
	char*         ctrl_a = "\1";
	for (i = 1; i < argc; i++)  {
		if (argv[i][0] == '-') {
			if (strcmp(argv[i], "-delay") == 0) {
				if (i + 1 == argc) {
				if ((delay = atoi(argv[++i])) < 1) {
			else if (strcmp(argv[i], "-ice") == 0)
			else if (strcmp(argv[i], "-clear") == 0)
			else if (strcmp(argv[i], "-pause") == 0)
			else if (strcmp(argv[i], "-space") == 0)
			else if (strcmp(argv[i], "-normal") == 0)
			else if (strcmp(argv[i], "-newline") == 0)
rswindell's avatar
rswindell committed
				newline++;
			else if (strcmp(argv[i], "-esc") == 0) {
			else if (IS_DIGIT(argv[i][1]))
rswindell's avatar
rswindell committed
				cols = atoi(argv[i] + 1);
		} else if (in == stdin) {
			if ((in = fopen(argv[i], "rb")) == NULL) {
		} else if (out == stdout) {
			if ((out = fopen(argv[i], "wb")) == NULL) {
	if (in != stdin && cols < 1) {
		if (sauce_fread_charinfo(in, /* type: */ NULL, &info)) {
			cols = info.width;
			ice = info.ice_color;
		}
	}

	const char* cond_newline = normal ? "\1+\1N\1/\1-" : "\1/";

	if (clear)
		fprintf(out, "%sN%sL", ctrl_a, ctrl_a);
	esc = 0;
	while ((ch = fgetc(in)) != EOF && ch != CTRL_Z) {
		if (ch == '[' && esc) {    /* ANSI escape sequence */
			if (cols && column >= cols) {
				fprintf(out, "%s", cond_newline);   // Conditional-newline
			ni = 0;               /* zero number index */
			memset(n, 1, sizeof(n));
			while ((ch = fgetc(in)) != EOF) {
				if (IS_DIGIT(ch)) {          /* 1 digit */
					n[ni] = ch & 0xf;
					ch = fgetc(in);
					if (ch == EOF)
						break;
					if (IS_DIGIT(ch)) {      /* 2 digits */
						n[ni] *= 10;
						n[ni] += ch & 0xf;
						ch = fgetc(in);
						if (ch == EOF)
							break;
						if (IS_DIGIT(ch)) {  /* 3 digits */
							n[ni] *= 10;
							n[ni] += ch & 0xf;
							ch = fgetc(in);
					continue;
					case '=':
					case '?':
						ch = fgetc(in);      /* First digit */
						if (IS_DIGIT(ch))
							ch = fgetc(in);               /* l or h ? */
						if (IS_DIGIT(ch))
							ch = fgetc(in);
						if (IS_DIGIT(ch))
							fgetc(in);
						break;
						if (n[0] <= 1 && n[1] <= 1)          /* home cursor */
rswindell's avatar
rswindell committed
						column = 0;
					case 'J':
						if (n[0] == 2)                     /* clear screen */
						else if (n[0] == 0)                /* clear to EOS */
rswindell's avatar
rswindell committed
						column = 0;
						break;
					case 'K':
						fprintf(out, "%s>", ctrl_a);    /* clear to eol */
rswindell's avatar
rswindell committed
						column = cols ? (cols - 1) : 79;
						break;
					case 'm':
						for (i = 0; i < ni; i++) {
							fprintf(out, "%s", ctrl_a);     /* ctrl-ax */
							switch (n[i]) {
								case 0:
								case 2:                 /* no attribute */
									fputc('N', out);
									break;
								case 1:                 /* high intensity */
									fputc('H', out);
									break;
								case 3:
								case 4:
								case 5:                 /* blink */
								case 6:
								case 7:
									fputc(ice ? 'E': 'I', out);
									break;
								case 30:
									break;
								case 31:
									break;
								case 32:
									break;
								case 33:
									break;
								case 34:
									break;
								case 35:
									break;
								case 36:
									break;
								case 37:
									break;
								case 40:
									break;
								case 41:
									break;
								case 42:
									break;
								case 43:
									break;
								case 44:
									break;
								case 45:
									break;
								case 46:
									break;
								case 47:
									fprintf(stderr, "Unsupported ANSI color code: %u\n", (unsigned int)n[i]);
						break;
					case 'B':   /* cursor down */
						while (n[0]) {
							fprintf(out, "%s]", ctrl_a); /* linefeed */
					case 'C':   /* cursor right */
						if (space)
							fprintf(out, "%*s", n[0], " ");
								fprintf(out, "%s\\x%02X", ctrl_a, 0x7f + n[0]);
							else
								fprintf(out, "%s%c", ctrl_a, 0x7f + n[0]);
rswindell's avatar
rswindell committed
						column += n[0];
						break;
					case 'D':   /* cursor left */
rswindell's avatar
rswindell committed
						column -= n[0];
						if (n[0] >= column)
							fprintf(out, "%s[", ctrl_a);
							while (n[0]) {
								fprintf(out, "%s<", ctrl_a);
					default:
						fprintf(stderr, "Unsupported ANSI code '%c' (0x%02X)\r\n", ch, ch);
			}   /* end of while */
			esc = 0;
		}   /* end of ANSI expansion */
		if (ch == '\x1b')
			esc = 1;
		else {
			esc = 0;
			if (encode) {
				if (ch < ' ' || ch >= 0x7f) {
						fprintf(out, "\\x%02X", ch);
					else
						fprintf(out, "%s", p);
				}
				else
					fputc(ch, out);
			} else {
rswindell's avatar
rswindell committed
						column = 0;
						if (cols && column >= cols) {
							fprintf(out, "%s", cond_newline);   // Conditional-newline
rswindell's avatar
rswindell committed
			}
			if (delay && (ftell(out) % delay) == 0)
				fprintf(out, "%s,", ctrl_a);
rswindell's avatar
rswindell committed
			column = 0;
	while (newline--)
		fprintf(out, "\r\n");
	if (pause)
		fprintf(out, "%sp", ctrl_a);