Synchronet now requires the libarchive development package (e.g. libarchive-dev on Debian-based Linux distros, libarchive.org for more info) to build successfully.

ans2asc.c 7.94 KB
Newer Older
1
/* Convert ANSI messages to Synchronet Ctrl-A code format */
rswindell's avatar
rswindell committed
2 3 4 5 6

/****************************************************************************
 * @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
7
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
rswindell's avatar
rswindell committed
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 *																			*
 * 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>
deuce's avatar
deuce committed
24
#include <string.h>		/* strcmp */
25 26 27
#include "sauce.h"
#include "git_branch.h"
#include "git_hash.h"
rswindell's avatar
rswindell committed
28

29 30 31 32
#ifndef CTRL_Z
#define CTRL_Z 0x1a
#endif

33
static void print_usage(const char* prog)
rswindell's avatar
rswindell committed
34
{
35 36 37 38
	fprintf(stderr,"\nSynchronet ANSI-Terminal-Sequence to Ctrl-A-Code Conversion Utility %s/%s\n"
		,GIT_BRANCH
		,GIT_HASH
	);
39
	fprintf(stderr,"\nusage: %s infile.ans [outfile.asc | outfile.msg] [[option] [...]]\n",prog);
40
	fprintf(stderr,"\noptions:\n\n");
41 42 43 44 45 46 47 48
	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,"  -delay <int>    insert a 1/10th second delay code at output byte interval\n");
49 50 51 52 53 54 55 56 57
	fprintf(stderr,"                  (lower interval values result in more delays, slower display)\n");
}

int main(int argc, char **argv)
{
	unsigned char esc,n[25];
	int i,ch,ni;
	FILE *in=stdin;
	FILE *out=stdout;
58 59
	bool ice=false;
	bool normal=false;
rswindell's avatar
rswindell committed
60 61
	int cols=0;
	int column=0;
62 63 64
	int delay=0;
	int clear=0;
	int pause=0;
65
	int space=0;
rswindell's avatar
rswindell committed
66
	int newline=0;
67

deuce's avatar
deuce committed
68
	if(argc<2) {
69
		print_usage(argv[0]);
rswindell's avatar
rswindell committed
70 71 72
		return(0); 
	}

73 74
	for(i=1; i<argc; i++)  {
		if(argv[i][0]=='-') {
75
			if(strcmp(argv[i], "-delay") == 0) {
76 77 78 79 80 81 82 83 84
				if(i+1 == argc) {
					print_usage(argv[0]);
					return -1;
				}
				if((delay=atoi(argv[++i])) < 1) {
					print_usage(argv[0]);
					return -1;
				}
			}
85
			else if(strcmp(argv[i], "-ice") == 0)
86
				ice = true;
87
			else if(strcmp(argv[i], "-clear") == 0)
88
				clear = 1;
89
			else if(strcmp(argv[i], "-pause") == 0)
90
				pause = 1;
91 92
			else if(strcmp(argv[i], "-space") == 0)
				space = 1;
93 94
			else if(strcmp(argv[i], "-normal") == 0)
				normal = true;
rswindell's avatar
rswindell committed
95 96
			else if(strcmp(argv[i], "-newline") == 0)
				newline++;
97
			else if(IS_DIGIT(argv[i][1]))
rswindell's avatar
rswindell committed
98
				cols = atoi(argv[i] + 1);
99 100 101 102 103 104 105 106 107 108 109 110 111 112
			else {
				print_usage(argv[0]);
				return 0;
			}
		} else if(in==stdin) {
			if((in=fopen(argv[i],"rb"))==NULL) {
				perror(argv[i]);
				return(1); 
			}
		} else if(out==stdout) {
			if((out=fopen(argv[i],"wb"))==NULL) {
				perror(argv[i]);
				return(1);
			}
deuce's avatar
deuce committed
113
		}
rswindell's avatar
rswindell committed
114 115
	}

116 117 118 119 120 121 122 123 124 125
	if(in != stdin && cols < 1) {
		struct sauce_charinfo info;
		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/";

126
	if(clear)
rswindell's avatar
rswindell committed
127
		fprintf(out,"\1N\1L");
rswindell's avatar
rswindell committed
128
	esc=0;
129
	while((ch=fgetc(in))!=EOF && ch != CTRL_Z) {
rswindell's avatar
rswindell committed
130
		if(ch=='[' && esc) {    /* ANSI escape sequence */
131
			if(cols && column >= cols) {
Rob Swindell's avatar
Rob Swindell committed
132
				fprintf(out, "%s", cond_newline);	// Conditional-newline
133 134
				column = 0;
			}
rswindell's avatar
rswindell committed
135
			ni=0;				/* zero number index */
136
			memset(n,1,sizeof(n));
rswindell's avatar
rswindell committed
137
			while((ch=fgetc(in))!=EOF) {
138
				if(IS_DIGIT(ch)) {			/* 1 digit */
rswindell's avatar
rswindell committed
139 140 141 142
					n[ni]=ch&0xf;
					ch=fgetc(in);
					if(ch==EOF)
						break;
143
					if(IS_DIGIT(ch)) {		/* 2 digits */
rswindell's avatar
rswindell committed
144 145 146 147 148
						n[ni]*=10;
						n[ni]+=ch&0xf;
						ch=fgetc(in);
						if(ch==EOF)
							break;
149
						if(IS_DIGIT(ch)) {	/* 3 digits */
rswindell's avatar
rswindell committed
150 151 152 153 154 155 156 157 158 159 160 161 162
							n[ni]*=10;
							n[ni]+=ch&0xf;
							ch=fgetc(in); 
						} 
					}
					ni++; 
				}
				if(ch==';')
					continue;
				switch(ch) {
					case '=':
					case '?':
						ch=fgetc(in);	   /* First digit */
163 164 165
						if(IS_DIGIT(ch)) ch=fgetc(in);	/* l or h ? */
						if(IS_DIGIT(ch)) ch=fgetc(in);
						if(IS_DIGIT(ch)) fgetc(in);
rswindell's avatar
rswindell committed
166
						break;
167 168 169
					case 'f':
					case 'H':
						if(n[0]<=1 && n[1]<=1)			/* home cursor */
170
							fputs("\1'", out);
rswindell's avatar
rswindell committed
171
						column = 0;
172
						break;
rswindell's avatar
rswindell committed
173 174
					case 'J':
						if(n[0]==2) 					/* clear screen */
175 176 177
							fputs("\1L",out);           /* ctrl-aL */
						else if(n[0]==0)				/* clear to EOS */
							fputs("\1J",out);           /* ctrl-aJ */
rswindell's avatar
rswindell committed
178
						column = 0;
rswindell's avatar
rswindell committed
179 180 181
						break;
					case 'K':
						fputs("\1>",out);               /* clear to eol */
rswindell's avatar
rswindell committed
182
						column = cols ? (cols - 1) : 79;
rswindell's avatar
rswindell committed
183 184 185 186 187 188 189
						break;
					case 'm':
						for(i=0;i<ni;i++) {
							fputc(1,out);				/* ctrl-ax */
							switch(n[i]) {
								case 0:
								case 2: 				/* no attribute */
190
									fputc('N',out);
rswindell's avatar
rswindell committed
191 192
									break;
								case 1: 				/* high intensity */
193
									fputc('H',out);
rswindell's avatar
rswindell committed
194 195 196 197 198 199
									break;
								case 3:
								case 4:
								case 5: 				/* blink */
								case 6:
								case 7:
200
									fputc(ice ? 'E': 'I',out);
rswindell's avatar
rswindell committed
201 202
									break;
								case 30:
203
									fputc('K',out);
rswindell's avatar
rswindell committed
204 205
									break;
								case 31:
206
									fputc('R',out);
rswindell's avatar
rswindell committed
207 208
									break;
								case 32:
209
									fputc('G',out);
rswindell's avatar
rswindell committed
210 211
									break;
								case 33:
212
									fputc('Y',out);
rswindell's avatar
rswindell committed
213 214
									break;
								case 34:
215
									fputc('B',out);
rswindell's avatar
rswindell committed
216 217
									break;
								case 35:
218
									fputc('M',out);
rswindell's avatar
rswindell committed
219 220
									break;
								case 36:
221
									fputc('C',out);
rswindell's avatar
rswindell committed
222 223
									break;
								case 37:
224
									fputc('W',out);
rswindell's avatar
rswindell committed
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248
									break;
								case 40:
									fputc('0',out);
									break;
								case 41:
									fputc('1',out);
									break;
								case 42:
									fputc('2',out);
									break;
								case 43:
									fputc('3',out);
									break;
								case 44:
									fputc('4',out);
									break;
								case 45:
									fputc('5',out);
									break;
								case 46:
									fputc('6',out);
									break;
								case 47:
									fputc('7',out);
249 250 251 252 253 254
									break; 
								default:
									fprintf(stderr,"Unsupported ANSI color code: %u\n", (unsigned int)n[i]);
									break;
							} 
						}
rswindell's avatar
rswindell committed
255
						break;
256 257
					case 'B':	/* cursor down */
						while(n[0]) {
258
							fprintf(out,"\1]");	/* linefeed */
259 260 261 262
							n[0]--;
						}
						break;
					case 'C':	/* cursor right */
263 264 265 266
						if(space)
							fprintf(out, "%*s", n[0], " ");
						else
							fprintf(out,"\1%c",0x7f+n[0]);
rswindell's avatar
rswindell committed
267
						column += n[0];
rswindell's avatar
rswindell committed
268
						break;
269
					case 'D':	/* cursor left */
rswindell's avatar
rswindell committed
270 271
						column -= n[0];
						if(n[0] >= column)
272
							fprintf(out,"\1[");
273 274
						else
							while(n[0]) {
275
								fprintf(out,"\1<");
276 277
								n[0]--;
							}
278
						break;
rswindell's avatar
rswindell committed
279 280 281 282 283 284 285 286 287 288 289 290 291
					default:
						fprintf(stderr,"Unsupported ANSI code '%c' (0x%02X)\r\n",ch,ch);
						break; 
				}
				break; 
			}	/* end of while */
			esc=0;
			continue; 
		} 	/* end of ANSI expansion */
		if(ch=='\x1b')
			esc=1;
		else {
			esc=0;
rswindell's avatar
rswindell committed
292 293 294 295 296 297 298 299
			switch(ch) {
				case '\r':
				case '\n':
					fputc(ch,out);
					column = 0;
					break;
				default:
					if(cols && column >= cols) {
Rob Swindell's avatar
Rob Swindell committed
300
						fprintf(out, "%s", cond_newline);	// Conditional-newline
rswindell's avatar
rswindell committed
301 302 303 304 305 306 307 308
						column = 0;
					}
					fputc(ch,out);
					column++;
					break;
			}
			if(delay && (ftell(out)%delay)==0)
				fprintf(out,"\1,");
rswindell's avatar
rswindell committed
309
		} 
rswindell's avatar
rswindell committed
310 311
		if(column < 0)
			column = 0;
rswindell's avatar
rswindell committed
312
	}
rswindell's avatar
rswindell committed
313 314
	while(newline--)
		fprintf(out,"\r\n");
315 316
	if(pause)
		fprintf(out,"\1p");
rswindell's avatar
rswindell committed
317 318 319
	return(0);
}