1da2e3ebdSchin /***********************************************************************
2da2e3ebdSchin *                                                                      *
3da2e3ebdSchin *               This software is part of the ast package               *
4*b30d1939SAndy Fiddaman *          Copyright (c) 2000-2011 AT&T Intellectual Property          *
5da2e3ebdSchin *                      and is licensed under the                       *
6*b30d1939SAndy Fiddaman *                 Eclipse Public License, Version 1.0                  *
77c2fbfb3SApril Chin *                    by AT&T Intellectual Property                     *
8da2e3ebdSchin *                                                                      *
9da2e3ebdSchin *                A copy of the License is available at                 *
10*b30d1939SAndy Fiddaman *          http://www.eclipse.org/org/documents/epl-v10.html           *
11*b30d1939SAndy Fiddaman *         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
12da2e3ebdSchin *                                                                      *
13da2e3ebdSchin *              Information and Software Systems Research               *
14da2e3ebdSchin *                            AT&T Research                             *
15da2e3ebdSchin *                           Florham Park NJ                            *
16da2e3ebdSchin *                                                                      *
17da2e3ebdSchin *                 Glenn Fowler <gsf@research.att.com>                  *
18da2e3ebdSchin *                                                                      *
19da2e3ebdSchin ***********************************************************************/
20da2e3ebdSchin #pragma prototyped
21da2e3ebdSchin /*
22da2e3ebdSchin  * Glenn Fowler
23da2e3ebdSchin  * AT&T Research
24da2e3ebdSchin  */
25da2e3ebdSchin 
26da2e3ebdSchin static const char usage[] =
27da2e3ebdSchin "[-?\n@(#)$Id: msgcvt (AT&T Research) 2000-05-01 $\n]"
28da2e3ebdSchin USAGE_LICENSE
29da2e3ebdSchin "[+NAME?msgcvt - convert message file to/from html]"
30da2e3ebdSchin "[+DESCRIPTION?\bmsgcvt\b reads a \bgencat\b(1) format file on the standard"
31da2e3ebdSchin "	input and converts it to \bhtml\b on the standard output. The input"
32da2e3ebdSchin "	file must contain the control statement \b$quote \"\b and use the \""
33da2e3ebdSchin "	character to quote message text. The output is in a form suitable for"
34da2e3ebdSchin "	automatic translation by web sites like"
35da2e3ebdSchin "	\bhttp://babelfish.altavista.com/\b or filters like"
36da2e3ebdSchin "	\btranslate\b(1).]"
37da2e3ebdSchin "[h:html?Generate \bhtml\b from \bgencat\b(1) input. This is the default.]"
38da2e3ebdSchin "[m:msg?Generate a \bgencat\b(1) message file from (presumably translated)"
39da2e3ebdSchin "	\bhtml\b. Wide characters are UTF-8 encoded.]"
40da2e3ebdSchin "[r:raw?The message file is raw message text, one message per line, with no"
41da2e3ebdSchin "	quoting or line numbering.]"
42da2e3ebdSchin "[+SEE ALSO?\bgencat\b(1), \bmsgcc\b(1), \bmsggen\b(1), \btranslate\b(1)]"
43da2e3ebdSchin ;
44da2e3ebdSchin 
45da2e3ebdSchin #include <ast.h>
46da2e3ebdSchin #include <ctype.h>
47da2e3ebdSchin #include <error.h>
48da2e3ebdSchin 
49da2e3ebdSchin #define MSG_RAW		(1<<0)
50da2e3ebdSchin #define MSG_SPLICE	(1<<1)
51da2e3ebdSchin 
52da2e3ebdSchin #define SPACE(s)	(isspace(*s)&&(s+=1)||*s=='\\'&&(*(s+1)=='n'||*(s+1)=='t')&&(s+=2))
53da2e3ebdSchin 
54da2e3ebdSchin typedef void (*Convert_f)(Sfio_t*, Sfio_t*, int);
55da2e3ebdSchin 
56da2e3ebdSchin typedef struct
57da2e3ebdSchin {
58da2e3ebdSchin 	const char*	name;
59da2e3ebdSchin 	int		code;
60da2e3ebdSchin } Code_t;
61da2e3ebdSchin 
62da2e3ebdSchin static const Code_t	codes[] =
63da2e3ebdSchin {
64da2e3ebdSchin 	"aacute",	225,
65da2e3ebdSchin 	"Aacute",	193,
66da2e3ebdSchin 	"acirc",	226,
67da2e3ebdSchin 	"Acirc",	194,
68da2e3ebdSchin 	"aelig",	230,
69da2e3ebdSchin 	"AElig",	198,
70da2e3ebdSchin 	"agrave",	224,
71da2e3ebdSchin 	"Agrave",	192,
72da2e3ebdSchin 	"amp",		'&',
73da2e3ebdSchin 	"aring",	229,
74da2e3ebdSchin 	"Aring",	197,
75da2e3ebdSchin 	"atilde",	227,
76da2e3ebdSchin 	"Atilde",	195,
77da2e3ebdSchin 	"auml",		228,
78da2e3ebdSchin 	"Auml",		196,
79da2e3ebdSchin 	"ccedil",	231,
80da2e3ebdSchin 	"Ccedil",	199,
81da2e3ebdSchin 	"copy",		169,
82da2e3ebdSchin 	"eacute",	233,
83da2e3ebdSchin 	"Eacute",	201,
84da2e3ebdSchin 	"ecirc",	234,
85da2e3ebdSchin 	"Ecirc",	202,
86da2e3ebdSchin 	"egrave",	232,
87da2e3ebdSchin 	"Egrave",	200,
88da2e3ebdSchin 	"euml",		235,
89da2e3ebdSchin 	"Euml",		203,
90da2e3ebdSchin 	"gt",		'>',
91da2e3ebdSchin 	"iacute",	237,
92da2e3ebdSchin 	"Iacute",	205,
93da2e3ebdSchin 	"icirc",	238,
94da2e3ebdSchin 	"Icirc",	206,
95da2e3ebdSchin 	"igrave",	236,
96da2e3ebdSchin 	"Igrave",	204,
97da2e3ebdSchin 	"iuml",		239,
98da2e3ebdSchin 	"Iuml",		207,
99da2e3ebdSchin 	"lt",		'<',
100da2e3ebdSchin 	"nbsp",		' ',
101da2e3ebdSchin 	"ntilde",	241,
102da2e3ebdSchin 	"Ntilde",	209,
103da2e3ebdSchin 	"oacute",	243,
104da2e3ebdSchin 	"Oacute",	211,
105da2e3ebdSchin 	"ocirc",	244,
106da2e3ebdSchin 	"Ocirc",	212,
107da2e3ebdSchin 	"ograve",	242,
108da2e3ebdSchin 	"Ograve",	210,
109da2e3ebdSchin 	"oslash",	248,
110da2e3ebdSchin 	"Oslash",	216,
111da2e3ebdSchin 	"otilde",	245,
112da2e3ebdSchin 	"Otilde",	213,
113da2e3ebdSchin 	"ouml",		246,
114da2e3ebdSchin 	"Ouml",		214,
115da2e3ebdSchin 	"quot",		'"',
116da2e3ebdSchin 	"reg",		174,
117da2e3ebdSchin 	"szlig",	223,
118da2e3ebdSchin 	"uacute",	250,
119da2e3ebdSchin 	"Uacute",	218,
120da2e3ebdSchin 	"ucirc",	251,
121da2e3ebdSchin 	"Ucirc",	219,
122da2e3ebdSchin 	"ugrave",	249,
123da2e3ebdSchin 	"Ugrave",	217,
124da2e3ebdSchin 	"uuml",		252,
125da2e3ebdSchin 	"Uuml",		220,
126da2e3ebdSchin 	"yuml",		255,
127da2e3ebdSchin };
128da2e3ebdSchin 
129da2e3ebdSchin static int
decode(Sfio_t * ip)130da2e3ebdSchin decode(Sfio_t* ip)
131da2e3ebdSchin {
132da2e3ebdSchin 	register int	c;
133da2e3ebdSchin 	register int	i;
134da2e3ebdSchin 	char		name[32];
135da2e3ebdSchin 
136da2e3ebdSchin 	if ((c = sfgetc(ip)) == EOF)
137da2e3ebdSchin 		return '&';
138da2e3ebdSchin 	name[0] = c;
139da2e3ebdSchin 	i = 1;
140da2e3ebdSchin 	if (c != '#' && !isalpha(c))
141da2e3ebdSchin 		goto bad;
142da2e3ebdSchin 	while ((c = sfgetc(ip)) != EOF && c != ';')
143da2e3ebdSchin 	{
144da2e3ebdSchin 		if (c == '&')
145da2e3ebdSchin 			i = 0;
146da2e3ebdSchin 		else
147da2e3ebdSchin 		{
148da2e3ebdSchin 			name[i++] = c;
149da2e3ebdSchin 			if (!isalnum(c) && (i > 1 || c != '#') || i >= (elementsof(name) - 1))
150da2e3ebdSchin 				goto bad;
151da2e3ebdSchin 		}
152da2e3ebdSchin 	}
153da2e3ebdSchin 	name[i] = 0;
154da2e3ebdSchin 	if (name[0] == '#')
155da2e3ebdSchin 	{
156da2e3ebdSchin 		switch (c = strtol(name + 1, NiL, 10))
157da2e3ebdSchin 		{
158da2e3ebdSchin 		case 91:
159da2e3ebdSchin 			c = '[';
160da2e3ebdSchin 			break;
161da2e3ebdSchin 		case 93:
162da2e3ebdSchin 			c = ']';
163da2e3ebdSchin 			break;
164da2e3ebdSchin 		}
165da2e3ebdSchin 	}
166da2e3ebdSchin 	else
167da2e3ebdSchin 	{
168da2e3ebdSchin 		for (i = 0; i < elementsof(codes); i++)
169da2e3ebdSchin 			if (streq(codes[i].name, name))
170da2e3ebdSchin 			{
171da2e3ebdSchin 				c = codes[i].code;
172da2e3ebdSchin 				break;
173da2e3ebdSchin 			}
174da2e3ebdSchin 		if (i >= elementsof(codes))
175da2e3ebdSchin 			goto bad;
176da2e3ebdSchin 	}
177da2e3ebdSchin 	return c;
178da2e3ebdSchin  bad:
179da2e3ebdSchin 	name[i] = 0;
180da2e3ebdSchin 	if (c == ';')
181da2e3ebdSchin 		error(1, "&%s: unknown HTML special character -- & assumed", name);
182da2e3ebdSchin 	else
183da2e3ebdSchin 		error(1, "&%s: invalid HTML special character -- & assumed", name);
184da2e3ebdSchin 	while (i--)
185da2e3ebdSchin 		sfungetc(ip, name[i]);
186da2e3ebdSchin 	return '&';
187da2e3ebdSchin }
188da2e3ebdSchin 
189da2e3ebdSchin static int
sfpututf(Sfio_t * op,register int w)190da2e3ebdSchin sfpututf(Sfio_t* op, register int w)
191da2e3ebdSchin {
192da2e3ebdSchin 	if (!(w & ~0x7F))
193da2e3ebdSchin 		return sfputc(op, w);
194da2e3ebdSchin 	else if (!(w & ~0x7FF))
195da2e3ebdSchin 		sfputc(op, 0xC0 + (w >> 6));
196da2e3ebdSchin 	else if (!(w & ~0xFFFF))
197da2e3ebdSchin 	{
198da2e3ebdSchin 		sfputc(op, 0xE0 + (w >> 12));
199da2e3ebdSchin 		sfputc(op, 0x80 + (w >> 6 ) & 0x3F);
200da2e3ebdSchin 	}
201da2e3ebdSchin 	else
202da2e3ebdSchin 		return sfputc(op, '?');
203da2e3ebdSchin 	return sfputc(op, 0x80 + (w & 0x3F));
204da2e3ebdSchin }
205da2e3ebdSchin 
206da2e3ebdSchin static int
sfnext(Sfio_t * ip)207da2e3ebdSchin sfnext(Sfio_t* ip)
208da2e3ebdSchin {
209da2e3ebdSchin 	register int	c;
210da2e3ebdSchin 
211da2e3ebdSchin 	while (isspace(c = sfgetc(ip)));
212da2e3ebdSchin 	return c;
213da2e3ebdSchin }
214da2e3ebdSchin 
215da2e3ebdSchin static void
html2msg(register Sfio_t * ip,register Sfio_t * op,int flags)216da2e3ebdSchin html2msg(register Sfio_t* ip, register Sfio_t* op, int flags)
217da2e3ebdSchin {
218da2e3ebdSchin 	register int	c;
219da2e3ebdSchin 	register int	q;
220da2e3ebdSchin 
221da2e3ebdSchin  again:
222da2e3ebdSchin 	while ((c = sfgetc(ip)) != EOF)
223da2e3ebdSchin 		if (c == '<')
224da2e3ebdSchin 		{
225da2e3ebdSchin 			if ((c = sfnext(ip)) == 'O' &&
226da2e3ebdSchin 			    (c = sfnext(ip)) == 'L' &&
227da2e3ebdSchin 			    isspace(c = sfgetc(ip)) &&
228da2e3ebdSchin 			    (c = sfnext(ip)) == 'S' &&
229da2e3ebdSchin 			    (c = sfnext(ip)) == 'T' &&
230da2e3ebdSchin 			    (c = sfnext(ip)) == 'A' &&
231da2e3ebdSchin 			    (c = sfnext(ip)) == 'R' &&
232da2e3ebdSchin 			    (c = sfnext(ip)) == 'T' &&
233da2e3ebdSchin 			    (c = sfnext(ip)) == '=' &&
234da2e3ebdSchin 			    (c = sfnext(ip)) == '"' &&
235da2e3ebdSchin 			    (c = sfnext(ip)) == '5' &&
236da2e3ebdSchin 			    (c = sfnext(ip)) == '5' &&
237da2e3ebdSchin 			    (c = sfnext(ip)) == '0' &&
238da2e3ebdSchin 			    (c = sfnext(ip)) == '7' &&
239da2e3ebdSchin 			    (c = sfnext(ip)) == '1' &&
240da2e3ebdSchin 			    (c = sfnext(ip)) == '7' &&
241da2e3ebdSchin 			    (c = sfnext(ip)) == '"' &&
242da2e3ebdSchin 			    (c = sfnext(ip)) == '>')
243da2e3ebdSchin 				break;
244da2e3ebdSchin 			while (c != EOF && c != '>')
245da2e3ebdSchin 				c = sfgetc(ip);
246da2e3ebdSchin 		}
247da2e3ebdSchin 	if ((c = sfnext(ip)) != EOF)
248da2e3ebdSchin 		sfungetc(ip, c);
249da2e3ebdSchin 	q = 0;
250da2e3ebdSchin 	for (;;)
251da2e3ebdSchin 	{
252da2e3ebdSchin 		switch (c = sfgetc(ip))
253da2e3ebdSchin 		{
254da2e3ebdSchin 		case EOF:
255da2e3ebdSchin 			break;
256da2e3ebdSchin 		case '&':
257da2e3ebdSchin 			c = decode(ip);
258da2e3ebdSchin 			sfpututf(op, c);
259da2e3ebdSchin 			if (isspace(c))
260da2e3ebdSchin 			{
261da2e3ebdSchin 				while (isspace(c = sfgetc(ip)));
262da2e3ebdSchin 				if (c == EOF)
263da2e3ebdSchin 					break;
264da2e3ebdSchin 				sfungetc(ip, c);
265da2e3ebdSchin 			}
266da2e3ebdSchin 			continue;
267da2e3ebdSchin 		case '<':
268da2e3ebdSchin 			switch (c = sfnext(ip))
269da2e3ebdSchin 			{
270da2e3ebdSchin 			case '/':
271da2e3ebdSchin 				if ((c = sfnext(ip)) == 'O' &&
272da2e3ebdSchin 				    (c = sfgetc(ip)) == 'L' &&
273da2e3ebdSchin 				    (c = sfnext(ip)) == '>')
274da2e3ebdSchin 				{
275da2e3ebdSchin 					if (q)
276da2e3ebdSchin 					{
277da2e3ebdSchin 						sfputc(op, q);
278da2e3ebdSchin 						q = '"';
279da2e3ebdSchin 					}
280da2e3ebdSchin 					goto again;
281da2e3ebdSchin 				}
282da2e3ebdSchin 				break;
283da2e3ebdSchin 			case 'B':
284da2e3ebdSchin 				if ((c = sfgetc(ip)) == 'R' &&
285da2e3ebdSchin 				    (c = sfnext(ip)) == '>')
286da2e3ebdSchin 					sfputc(op, ' ');
287da2e3ebdSchin 				break;
288da2e3ebdSchin 			case 'L':
289da2e3ebdSchin 				if ((c = sfgetc(ip)) == 'I' &&
290da2e3ebdSchin 				    (c = sfnext(ip)) == '>' &&
291da2e3ebdSchin 				     isdigit(c = sfnext(ip)))
292da2e3ebdSchin 				{
293da2e3ebdSchin 					if (q)
294da2e3ebdSchin 						sfputc(op, q);
295da2e3ebdSchin 					else
296da2e3ebdSchin 						q = '"';
297da2e3ebdSchin 					sfputc(op, '\n');
298da2e3ebdSchin 					do
299da2e3ebdSchin 					{
300da2e3ebdSchin 						sfputc(op, c);
301da2e3ebdSchin 					} while (isdigit(c = sfgetc(ip)));
302da2e3ebdSchin 					if (c == EOF)
303da2e3ebdSchin 						break;
304da2e3ebdSchin 					sfputc(op, ' ');
305da2e3ebdSchin 					sfputc(op, '"');
306da2e3ebdSchin 					if (isspace(c))
307da2e3ebdSchin 						c = sfnext(ip);
308da2e3ebdSchin 					if (c == '<' &&
309da2e3ebdSchin 					    (c = sfnext(ip)) == 'L' &&
310da2e3ebdSchin 					    (c = sfgetc(ip)) == 'I' &&
311da2e3ebdSchin 					    (c = sfnext(ip)) == '>')
312da2e3ebdSchin 						/* great */;
313da2e3ebdSchin 					continue;
314da2e3ebdSchin 				}
315da2e3ebdSchin 				break;
316da2e3ebdSchin 			case 'P':
317da2e3ebdSchin 				if ((c = sfnext(ip)) == '>')
318da2e3ebdSchin 					sfputc(op, '\n');
319da2e3ebdSchin 				else if (c == 'C' &&
320da2e3ebdSchin 					 (c = sfgetc(ip)) == 'L' &&
321da2e3ebdSchin 					 (c = sfgetc(ip)) == 'A' &&
322da2e3ebdSchin 					 (c = sfgetc(ip)) == 'S' &&
323da2e3ebdSchin 					 (c = sfgetc(ip)) == 'S' &&
324da2e3ebdSchin 					 (c = sfnext(ip)) == '=' &&
325da2e3ebdSchin 					 (c = sfnext(ip)) == '"')
326da2e3ebdSchin 					for (;;)
327da2e3ebdSchin 					{
328da2e3ebdSchin 						switch (c = sfgetc(ip))
329da2e3ebdSchin 						{
330da2e3ebdSchin 						case EOF:
331da2e3ebdSchin 						case '"':
332da2e3ebdSchin 							break;
333da2e3ebdSchin 						case '&':
334da2e3ebdSchin 							c = decode(ip);
335da2e3ebdSchin 							sfpututf(op, c);
336da2e3ebdSchin 							continue;
337da2e3ebdSchin 						default:
338da2e3ebdSchin 							sfpututf(op, c);
339da2e3ebdSchin 							continue;
340da2e3ebdSchin 						}
341da2e3ebdSchin 						break;
342da2e3ebdSchin 					}
343da2e3ebdSchin 				break;
344da2e3ebdSchin 			}
345da2e3ebdSchin 			while (c != EOF && c != '>')
346da2e3ebdSchin 				c = sfgetc(ip);
347da2e3ebdSchin 			if (c == EOF || (c = sfgetc(ip)) == EOF)
348da2e3ebdSchin 				break;
349da2e3ebdSchin 			sfungetc(ip, c);
350da2e3ebdSchin 			continue;
351da2e3ebdSchin 		case '"':
352da2e3ebdSchin 			if (!flags)
353da2e3ebdSchin 				sfputc(op, '\\');
354da2e3ebdSchin 			sfputc(op, c);
355da2e3ebdSchin 			continue;
356da2e3ebdSchin 		case '\n':
357da2e3ebdSchin 			if (flags)
358da2e3ebdSchin 			{
359da2e3ebdSchin 				sfputc(op, c);
360da2e3ebdSchin 				continue;
361da2e3ebdSchin 			}
362da2e3ebdSchin 			/*FALLTHROUGH*/
363da2e3ebdSchin 		case ' ':
364da2e3ebdSchin 		case '\t':
365da2e3ebdSchin 			while ((c = sfgetc(ip)) != EOF)
366da2e3ebdSchin 				if (c == '&')
367da2e3ebdSchin 				{
368da2e3ebdSchin 					c = decode(ip);
369da2e3ebdSchin 					if (!isspace(c))
370da2e3ebdSchin 						sfputc(op, ' ');
371da2e3ebdSchin 					sfpututf(op, c);
372da2e3ebdSchin 					break;
373da2e3ebdSchin 				}
374da2e3ebdSchin 				else if (!isspace(c))
375da2e3ebdSchin 				{
376da2e3ebdSchin 					if (c == '<')
377da2e3ebdSchin 					{
378da2e3ebdSchin 						c = sfgetc(ip);
379da2e3ebdSchin 						if (c == EOF)
380da2e3ebdSchin 							break;
381da2e3ebdSchin 						sfungetc(ip, c);
382da2e3ebdSchin 						sfungetc(ip, '<');
383da2e3ebdSchin 						if (c != 'L' && c != '/')
384da2e3ebdSchin 							sfputc(op, ' ');
385da2e3ebdSchin 					}
386da2e3ebdSchin 					else
387da2e3ebdSchin 					{
388da2e3ebdSchin 						if (c != EOF)
389da2e3ebdSchin 							sfungetc(ip, c);
390da2e3ebdSchin 						sfputc(op, ' ');
391da2e3ebdSchin 					}
392da2e3ebdSchin 					break;
393da2e3ebdSchin 				}
394da2e3ebdSchin 			continue;
395da2e3ebdSchin 		case '\r':
396da2e3ebdSchin 		case '[':
397da2e3ebdSchin 		case ']':
398da2e3ebdSchin 			continue;
399da2e3ebdSchin 		default:
400da2e3ebdSchin 			sfpututf(op, c);
401da2e3ebdSchin 			continue;
402da2e3ebdSchin 		}
403da2e3ebdSchin 		break;
404da2e3ebdSchin 	}
405da2e3ebdSchin 	if (q)
406da2e3ebdSchin 		sfputc(op, q);
407da2e3ebdSchin 	sfputc(op, '\n');
408da2e3ebdSchin }
409da2e3ebdSchin 
410da2e3ebdSchin static void
encode(Sfio_t * op,register int c)411da2e3ebdSchin encode(Sfio_t* op, register int c)
412da2e3ebdSchin {
413da2e3ebdSchin 	if (c == '<')
414da2e3ebdSchin 		sfprintf(op, "&lt;");
415da2e3ebdSchin 	else if (c == '>')
416da2e3ebdSchin 		sfprintf(op, "&gt;");
417da2e3ebdSchin 	else if (c == '"')
418da2e3ebdSchin 		sfprintf(op, "&quot;");
419da2e3ebdSchin 	else if (c == '&')
420da2e3ebdSchin 		sfprintf(op, "&amp;");
421da2e3ebdSchin 	else if (c == '[')
422da2e3ebdSchin 		sfprintf(op, "&#091;");
423da2e3ebdSchin 	else if (c == ']')
424da2e3ebdSchin 		sfprintf(op, "&#093;");
425da2e3ebdSchin 	else
426da2e3ebdSchin 		sfputc(op, c);
427da2e3ebdSchin }
428da2e3ebdSchin 
429da2e3ebdSchin static void
msg2html(register Sfio_t * ip,register Sfio_t * op,register int flags)430da2e3ebdSchin msg2html(register Sfio_t* ip, register Sfio_t* op, register int flags)
431da2e3ebdSchin {
432da2e3ebdSchin 	register char*	s;
433da2e3ebdSchin 	register int	c;
434da2e3ebdSchin 	register int	q;
435da2e3ebdSchin 	register int	p;
436da2e3ebdSchin 
437da2e3ebdSchin 	sfprintf(op, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\"><HTML><HEAD><!-- text massaged for external translation --></HEAD><BODY>\n");
438da2e3ebdSchin 	sfprintf(op, "<OL START=\"550717\">\n");
439da2e3ebdSchin 	p = q = 0;
440da2e3ebdSchin 	while (s = sfgetr(ip, '\n', 1))
441da2e3ebdSchin 	{
442da2e3ebdSchin 		error_info.line++;
443da2e3ebdSchin 		if (flags)
444da2e3ebdSchin 			sfprintf(op, "<P>");
445da2e3ebdSchin 		else
446da2e3ebdSchin 		{
447da2e3ebdSchin 			if (*s == '$')
448da2e3ebdSchin 			{
449da2e3ebdSchin 				if (p)
450da2e3ebdSchin 					sfprintf(op, "<P>");
451da2e3ebdSchin 				else
452da2e3ebdSchin 					p = 1;
453da2e3ebdSchin 				sfprintf(op, "<P CLASS=\"", s);
454da2e3ebdSchin 				while (c = *s++)
455da2e3ebdSchin 					encode(op, c);
456da2e3ebdSchin 				sfprintf(op, "\">\n");
457da2e3ebdSchin 				continue;
458da2e3ebdSchin 			}
459da2e3ebdSchin 			p = 0;
460da2e3ebdSchin 			if (!isdigit(*s))
461da2e3ebdSchin 				continue;
462da2e3ebdSchin 			sfprintf(op, "<LI>");
463da2e3ebdSchin 			while (isdigit(c = *s++))
464da2e3ebdSchin 				sfputc(op, c);
465da2e3ebdSchin 			sfprintf(op, "<LI>");
466da2e3ebdSchin 			while (c && c != '"')
467da2e3ebdSchin 				c = *s++;
468da2e3ebdSchin 			if (!c)
469da2e3ebdSchin 				s--;
470da2e3ebdSchin 			else if (isspace(*s))
471da2e3ebdSchin 			{
472da2e3ebdSchin 				s++;
473da2e3ebdSchin 				sfprintf(op, "<BR>");
474da2e3ebdSchin 			}
475da2e3ebdSchin 		}
476da2e3ebdSchin 		for (;;)
477da2e3ebdSchin 		{
478da2e3ebdSchin 			switch (c = *s++)
479da2e3ebdSchin 			{
480da2e3ebdSchin 			case 0:
481da2e3ebdSchin 				flags &= ~MSG_SPLICE;
482da2e3ebdSchin 				if (q)
483da2e3ebdSchin 				{
484da2e3ebdSchin 					q = 0;
485da2e3ebdSchin 					sfprintf(op, "\">");
486da2e3ebdSchin 				}
487da2e3ebdSchin 				sfputc(op, '\n');
488da2e3ebdSchin 				break;
489da2e3ebdSchin 			case '<':
490da2e3ebdSchin 				sfprintf(op, "&lt;");
491da2e3ebdSchin 				continue;
492da2e3ebdSchin 			case '>':
493da2e3ebdSchin 				sfprintf(op, "&gt;");
494da2e3ebdSchin 				continue;
495da2e3ebdSchin 			case '&':
496da2e3ebdSchin 				sfprintf(op, "&amp;");
497da2e3ebdSchin 				continue;
498da2e3ebdSchin 			case '[':
499da2e3ebdSchin 				sfprintf(op, "&#091;");
500da2e3ebdSchin 				continue;
501da2e3ebdSchin 			case ']':
502da2e3ebdSchin 				sfprintf(op, "&#093;");
503da2e3ebdSchin 				continue;
504da2e3ebdSchin 			case '$':
505da2e3ebdSchin 				if (!q)
506da2e3ebdSchin 				{
507da2e3ebdSchin 					q = 1;
508da2e3ebdSchin 					sfprintf(op, "<P CLASS=\"");
509da2e3ebdSchin 				}
510da2e3ebdSchin 				sfputc(op, c);
511da2e3ebdSchin 				while (isalnum(c = *s++))
512da2e3ebdSchin 					sfputc(op, c);
513da2e3ebdSchin 				s--;
514da2e3ebdSchin 				continue;
515da2e3ebdSchin 			case '%':
516da2e3ebdSchin 				if (!q)
517da2e3ebdSchin 				{
518da2e3ebdSchin 					q = 1;
519da2e3ebdSchin 					sfprintf(op, "<P CLASS=\"");
520da2e3ebdSchin 				}
521da2e3ebdSchin 				sfputc(op, c);
522da2e3ebdSchin 				if (*s == '%')
523da2e3ebdSchin 					sfputc(op, *s++);
524da2e3ebdSchin 				else
525da2e3ebdSchin 					do
526da2e3ebdSchin 					{
527da2e3ebdSchin 						if (!(c = *s++) || c == '"')
528da2e3ebdSchin 						{
529da2e3ebdSchin 							s--;
530da2e3ebdSchin 							break;
531da2e3ebdSchin 						}
532da2e3ebdSchin 						encode(op, c);
533da2e3ebdSchin 					} while (!isalpha(c) || (!islower(c) || c == 'h' || c == 'l') && isalpha(*s));
534da2e3ebdSchin 				if (SPACE(s))
535da2e3ebdSchin 					sfprintf(op, "&nbsp;");
536da2e3ebdSchin 				continue;
537da2e3ebdSchin 			case '"':
538da2e3ebdSchin 				if (!(flags & MSG_RAW))
539da2e3ebdSchin 				{
540da2e3ebdSchin 					s = "";
541da2e3ebdSchin 					continue;
542da2e3ebdSchin 				}
543da2e3ebdSchin 				/*FALLTHROUGH*/
544da2e3ebdSchin 			case '\'':
545da2e3ebdSchin 			case ':':
546da2e3ebdSchin 			case '/':
547da2e3ebdSchin 			case '+':
548da2e3ebdSchin 			case '@':
549da2e3ebdSchin 				if (!q)
550da2e3ebdSchin 				{
551da2e3ebdSchin 					q = 1;
552da2e3ebdSchin 					sfprintf(op, "<P CLASS=\"");
553da2e3ebdSchin 				}
554da2e3ebdSchin 				/*FALLTHROUGH*/
555da2e3ebdSchin 			case '.':
556da2e3ebdSchin 			case ',':
557da2e3ebdSchin 				sfputc(op, c);
558da2e3ebdSchin 				if (SPACE(s))
559da2e3ebdSchin 					sfprintf(op, "&nbsp;");
560da2e3ebdSchin 				continue;
561da2e3ebdSchin 			case '\\':
562da2e3ebdSchin 				if (!(c = *s++))
563da2e3ebdSchin 				{
564da2e3ebdSchin 					flags |= MSG_SPLICE;
565da2e3ebdSchin 					break;
566da2e3ebdSchin 				}
567da2e3ebdSchin 				if (c != 'n' && c != 't')
568da2e3ebdSchin 				{
569da2e3ebdSchin 					if (!q)
570da2e3ebdSchin 					{
571da2e3ebdSchin 						q = 1;
572da2e3ebdSchin 						sfprintf(op, "<P CLASS=\"");
573da2e3ebdSchin 					}
574da2e3ebdSchin 					sfputc(op, '\\');
575da2e3ebdSchin 					encode(op, c);
576da2e3ebdSchin 					if (c == 'b')
577da2e3ebdSchin 					{
578da2e3ebdSchin 						for (;;)
579da2e3ebdSchin 						{
580da2e3ebdSchin 							if (!(c = *s++) || c == '"')
581da2e3ebdSchin 							{
582da2e3ebdSchin 								s--;
583da2e3ebdSchin 								break;
584da2e3ebdSchin 							}
585da2e3ebdSchin 							if (c == '?')
586da2e3ebdSchin 							{
587da2e3ebdSchin 								if (*s != '?')
588da2e3ebdSchin 								{
589da2e3ebdSchin 									s--;
590da2e3ebdSchin 									break;
591da2e3ebdSchin 								}
592da2e3ebdSchin 								sfputc(op, c);
593da2e3ebdSchin 								sfputc(op, *s++);
594da2e3ebdSchin 								continue;
595da2e3ebdSchin 							}
596da2e3ebdSchin 							if (c == '\\')
597da2e3ebdSchin 							{
598da2e3ebdSchin 								if (!*s)
599da2e3ebdSchin 									break;
600da2e3ebdSchin 								sfputc(op, c);
601da2e3ebdSchin 								if (*s == 'a' || *s == 'b' || *s == '0')
602da2e3ebdSchin 								{
603da2e3ebdSchin 									sfputc(op, *s++);
604da2e3ebdSchin 									break;
605da2e3ebdSchin 								}
606da2e3ebdSchin 								c = *s++;
607da2e3ebdSchin 							}
608da2e3ebdSchin 							encode(op, c);
609da2e3ebdSchin 						}
610da2e3ebdSchin 					}
611da2e3ebdSchin 					else if (isdigit(c) && isdigit(*s))
612da2e3ebdSchin 					{
613da2e3ebdSchin 						sfputc(op, *s++);
614da2e3ebdSchin 						if (isdigit(*s))
615da2e3ebdSchin 							sfputc(op, *s++);
616da2e3ebdSchin 					}
617da2e3ebdSchin 					if (SPACE(s))
618da2e3ebdSchin 						sfprintf(op, "&nbsp;");
619da2e3ebdSchin 					continue;
620da2e3ebdSchin 				}
621da2e3ebdSchin 				/*FALLTHROUGH*/
622da2e3ebdSchin 			case ' ':
623da2e3ebdSchin 			case '\t':
624da2e3ebdSchin 				while (isspace(*s) || *s == '\\' && (*(s + 1) == 'n' || *(s + 1) == 't') && s++)
625da2e3ebdSchin 					s++;
626da2e3ebdSchin 				if (*s == '"')
627da2e3ebdSchin 				{
628da2e3ebdSchin 					if (q)
629da2e3ebdSchin 					{
630da2e3ebdSchin 						q = 0;
631da2e3ebdSchin 						sfprintf(op, " \">");
632da2e3ebdSchin 					}
633da2e3ebdSchin 					else
634da2e3ebdSchin 						sfprintf(op, "<BR>");
635da2e3ebdSchin 					continue;
636da2e3ebdSchin 				}
637da2e3ebdSchin 				c = ' ';
638da2e3ebdSchin 				/*FALLTHROUGH*/
639da2e3ebdSchin 			default:
640da2e3ebdSchin 				if (q)
641da2e3ebdSchin 				{
642da2e3ebdSchin 					q = 0;
643da2e3ebdSchin 					sfprintf(op, "\">");
644da2e3ebdSchin 				}
645da2e3ebdSchin 				sfputc(op, c);
646da2e3ebdSchin 				continue;
647da2e3ebdSchin 			}
648da2e3ebdSchin 			break;
649da2e3ebdSchin 		}
650da2e3ebdSchin 	}
651da2e3ebdSchin 	sfprintf(op, "</OL>\n");
652da2e3ebdSchin 	sfprintf(op, "</BODY></HTML>\n");
653da2e3ebdSchin 	error_info.line = 0;
654da2e3ebdSchin }
655da2e3ebdSchin 
656da2e3ebdSchin int
main(int argc,char ** argv)657da2e3ebdSchin main(int argc, char** argv)
658da2e3ebdSchin {
659da2e3ebdSchin 	int		flags = 0;
660da2e3ebdSchin 	Convert_f	convert = msg2html;
661da2e3ebdSchin 
662da2e3ebdSchin 	NoP(argc);
663da2e3ebdSchin 	error_info.id = "msgcvt";
664da2e3ebdSchin 	for (;;)
665da2e3ebdSchin 	{
666da2e3ebdSchin 		switch (optget(argv, usage))
667da2e3ebdSchin 		{
668da2e3ebdSchin 		case 'h':
669da2e3ebdSchin 			convert = msg2html;
670da2e3ebdSchin 			continue;
671da2e3ebdSchin 		case 'm':
672da2e3ebdSchin 			convert = html2msg;
673da2e3ebdSchin 			continue;
674da2e3ebdSchin 		case 'r':
675da2e3ebdSchin 			flags |= MSG_RAW;
676da2e3ebdSchin 			continue;
677da2e3ebdSchin 		case '?':
678da2e3ebdSchin 			error(ERROR_USAGE|4, "%s", opt_info.arg);
679da2e3ebdSchin 			continue;
680da2e3ebdSchin 		case ':':
681da2e3ebdSchin 			error(2, "%s", opt_info.arg);
682da2e3ebdSchin 			continue;
683da2e3ebdSchin 		}
684da2e3ebdSchin 		break;
685da2e3ebdSchin 	}
686da2e3ebdSchin 	argv += opt_info.index;
687da2e3ebdSchin 	if (error_info.errors)
688da2e3ebdSchin 		error(ERROR_USAGE|4, "%s", optusage(NiL));
689da2e3ebdSchin 	(*convert)(sfstdin, sfstdout, flags);
690da2e3ebdSchin 	return error_info.errors != 0;
691da2e3ebdSchin }
692