xref: /titanic_44/usr/src/cmd/mandoc/preconv.c (revision 698f87a48e2e945bfe5493ce168e0d0ae1cedd5c)
1*698f87a4SGarrett D'Amore /*	$Id: preconv.c,v 1.6 2013/06/02 03:52:21 schwarze Exp $ */
295c635efSGarrett D'Amore /*
395c635efSGarrett D'Amore  * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
495c635efSGarrett D'Amore  *
595c635efSGarrett D'Amore  * Permission to use, copy, modify, and distribute this software for any
695c635efSGarrett D'Amore  * purpose with or without fee is hereby granted, provided that the above
795c635efSGarrett D'Amore  * copyright notice and this permission notice appear in all copies.
895c635efSGarrett D'Amore  *
995c635efSGarrett D'Amore  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1095c635efSGarrett D'Amore  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1195c635efSGarrett D'Amore  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1295c635efSGarrett D'Amore  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1395c635efSGarrett D'Amore  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1495c635efSGarrett D'Amore  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1595c635efSGarrett D'Amore  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1695c635efSGarrett D'Amore  */
1795c635efSGarrett D'Amore #ifdef HAVE_CONFIG_H
1895c635efSGarrett D'Amore #include "config.h"
1995c635efSGarrett D'Amore #endif
2095c635efSGarrett D'Amore 
2195c635efSGarrett D'Amore #ifdef HAVE_MMAP
2295c635efSGarrett D'Amore #include <sys/stat.h>
2395c635efSGarrett D'Amore #include <sys/mman.h>
2495c635efSGarrett D'Amore #endif
2595c635efSGarrett D'Amore 
2695c635efSGarrett D'Amore #include <assert.h>
2795c635efSGarrett D'Amore #include <fcntl.h>
2895c635efSGarrett D'Amore #include <stdio.h>
2995c635efSGarrett D'Amore #include <stdlib.h>
3095c635efSGarrett D'Amore #include <string.h>
3195c635efSGarrett D'Amore #include <unistd.h>
3295c635efSGarrett D'Amore 
3395c635efSGarrett D'Amore /*
3495c635efSGarrett D'Amore  * The read_whole_file() and resize_buf() functions are copied from
35*698f87a4SGarrett D'Amore  * read.c, including all dependency code.
3695c635efSGarrett D'Amore  */
3795c635efSGarrett D'Amore 
3895c635efSGarrett D'Amore enum	enc {
3995c635efSGarrett D'Amore 	ENC_UTF_8, /* UTF-8 */
4095c635efSGarrett D'Amore 	ENC_US_ASCII, /* US-ASCII */
4195c635efSGarrett D'Amore 	ENC_LATIN_1, /* Latin-1 */
4295c635efSGarrett D'Amore 	ENC__MAX
4395c635efSGarrett D'Amore };
4495c635efSGarrett D'Amore 
4595c635efSGarrett D'Amore struct	buf {
4695c635efSGarrett D'Amore 	char		 *buf; /* binary input buffer */
4795c635efSGarrett D'Amore 	size_t	 	  sz; /* size of binary buffer */
4895c635efSGarrett D'Amore 	size_t		  offs; /* starting buffer offset */
4995c635efSGarrett D'Amore };
5095c635efSGarrett D'Amore 
5195c635efSGarrett D'Amore struct	encode {
5295c635efSGarrett D'Amore 	const char	 *name;
5395c635efSGarrett D'Amore 	int		(*conv)(const struct buf *);
5495c635efSGarrett D'Amore };
5595c635efSGarrett D'Amore 
5695c635efSGarrett D'Amore static	int	 cue_enc(const struct buf *, size_t *, enum enc *);
5795c635efSGarrett D'Amore static	int	 conv_latin_1(const struct buf *);
5895c635efSGarrett D'Amore static	int	 conv_us_ascii(const struct buf *);
5995c635efSGarrett D'Amore static	int	 conv_utf_8(const struct buf *);
6095c635efSGarrett D'Amore static	int	 read_whole_file(const char *, int,
6195c635efSGarrett D'Amore 			struct buf *, int *);
6295c635efSGarrett D'Amore static	void	 resize_buf(struct buf *, size_t);
6395c635efSGarrett D'Amore static	void	 usage(void);
6495c635efSGarrett D'Amore 
6595c635efSGarrett D'Amore static	const struct encode encs[ENC__MAX] = {
6695c635efSGarrett D'Amore 	{ "utf-8", conv_utf_8 }, /* ENC_UTF_8 */
6795c635efSGarrett D'Amore 	{ "us-ascii", conv_us_ascii }, /* ENC_US_ASCII */
6895c635efSGarrett D'Amore 	{ "latin-1", conv_latin_1 }, /* ENC_LATIN_1 */
6995c635efSGarrett D'Amore };
7095c635efSGarrett D'Amore 
7195c635efSGarrett D'Amore static	const char	 *progname;
7295c635efSGarrett D'Amore 
7395c635efSGarrett D'Amore static void
usage(void)7495c635efSGarrett D'Amore usage(void)
7595c635efSGarrett D'Amore {
7695c635efSGarrett D'Amore 
7795c635efSGarrett D'Amore 	fprintf(stderr, "usage: %s "
7895c635efSGarrett D'Amore 			"[-D enc] "
7995c635efSGarrett D'Amore 			"[-e ENC] "
8095c635efSGarrett D'Amore 			"[file]\n", progname);
8195c635efSGarrett D'Amore }
8295c635efSGarrett D'Amore 
8395c635efSGarrett D'Amore static int
conv_latin_1(const struct buf * b)8495c635efSGarrett D'Amore conv_latin_1(const struct buf *b)
8595c635efSGarrett D'Amore {
8695c635efSGarrett D'Amore 	size_t		 i;
8795c635efSGarrett D'Amore 	unsigned char	 cu;
8895c635efSGarrett D'Amore 	const char	*cp;
8995c635efSGarrett D'Amore 
9095c635efSGarrett D'Amore 	cp = b->buf + (int)b->offs;
9195c635efSGarrett D'Amore 
9295c635efSGarrett D'Amore 	/*
9395c635efSGarrett D'Amore 	 * Latin-1 falls into the first 256 code-points of Unicode, so
9495c635efSGarrett D'Amore 	 * there's no need for any sort of translation.  Just make the
9595c635efSGarrett D'Amore 	 * 8-bit characters use the Unicode escape.
9695c635efSGarrett D'Amore 	 * Note that binary values 128 < v < 160 are passed through
9795c635efSGarrett D'Amore 	 * unmodified to mandoc.
9895c635efSGarrett D'Amore 	 */
9995c635efSGarrett D'Amore 
10095c635efSGarrett D'Amore 	for (i = b->offs; i < b->sz; i++) {
10195c635efSGarrett D'Amore 		cu = (unsigned char)*cp++;
10295c635efSGarrett D'Amore 		cu < 160U ? putchar(cu) : printf("\\[u%.4X]", cu);
10395c635efSGarrett D'Amore 	}
10495c635efSGarrett D'Amore 
10595c635efSGarrett D'Amore 	return(1);
10695c635efSGarrett D'Amore }
10795c635efSGarrett D'Amore 
10895c635efSGarrett D'Amore static int
conv_us_ascii(const struct buf * b)10995c635efSGarrett D'Amore conv_us_ascii(const struct buf *b)
11095c635efSGarrett D'Amore {
11195c635efSGarrett D'Amore 
11295c635efSGarrett D'Amore 	/*
11395c635efSGarrett D'Amore 	 * US-ASCII has no conversion since it falls into the first 128
11495c635efSGarrett D'Amore 	 * bytes of Unicode.
11595c635efSGarrett D'Amore 	 */
11695c635efSGarrett D'Amore 
11795c635efSGarrett D'Amore 	fwrite(b->buf, 1, b->sz, stdout);
11895c635efSGarrett D'Amore 	return(1);
11995c635efSGarrett D'Amore }
12095c635efSGarrett D'Amore 
12195c635efSGarrett D'Amore static int
conv_utf_8(const struct buf * b)12295c635efSGarrett D'Amore conv_utf_8(const struct buf *b)
12395c635efSGarrett D'Amore {
12495c635efSGarrett D'Amore 	int		 state, be;
12595c635efSGarrett D'Amore 	unsigned int	 accum;
12695c635efSGarrett D'Amore 	size_t		 i;
12795c635efSGarrett D'Amore 	unsigned char	 cu;
12895c635efSGarrett D'Amore 	const char	*cp;
12995c635efSGarrett D'Amore 	const long	 one = 1L;
13095c635efSGarrett D'Amore 
13195c635efSGarrett D'Amore 	cp = b->buf + (int)b->offs;
13295c635efSGarrett D'Amore 	state = 0;
13395c635efSGarrett D'Amore 	accum = 0U;
13495c635efSGarrett D'Amore 	be = 0;
13595c635efSGarrett D'Amore 
13695c635efSGarrett D'Amore 	/* Quick test for big-endian value. */
13795c635efSGarrett D'Amore 
13895c635efSGarrett D'Amore 	if ( ! (*((const char *)(&one))))
13995c635efSGarrett D'Amore 		be = 1;
14095c635efSGarrett D'Amore 
14195c635efSGarrett D'Amore 	for (i = b->offs; i < b->sz; i++) {
14295c635efSGarrett D'Amore 		cu = (unsigned char)*cp++;
14395c635efSGarrett D'Amore 		if (state) {
14495c635efSGarrett D'Amore 			if ( ! (cu & 128) || (cu & 64)) {
14595c635efSGarrett D'Amore 				/* Bad sequence header. */
14695c635efSGarrett D'Amore 				return(0);
14795c635efSGarrett D'Amore 			}
14895c635efSGarrett D'Amore 
14995c635efSGarrett D'Amore 			/* Accept only legitimate bit patterns. */
15095c635efSGarrett D'Amore 
15195c635efSGarrett D'Amore 			if (cu > 191 || cu < 128) {
15295c635efSGarrett D'Amore 				/* Bad in-sequence bits. */
15395c635efSGarrett D'Amore 				return(0);
15495c635efSGarrett D'Amore 			}
15595c635efSGarrett D'Amore 
15695c635efSGarrett D'Amore 			accum |= (cu & 63) << --state * 6;
15795c635efSGarrett D'Amore 
15895c635efSGarrett D'Amore 			/*
15995c635efSGarrett D'Amore 			 * Accum is held in little-endian order as
16095c635efSGarrett D'Amore 			 * stipulated by the UTF-8 sequence coding.  We
16195c635efSGarrett D'Amore 			 * need to convert to a native big-endian if our
16295c635efSGarrett D'Amore 			 * architecture requires it.
16395c635efSGarrett D'Amore 			 */
16495c635efSGarrett D'Amore 
16595c635efSGarrett D'Amore 			if (0 == state && be)
16695c635efSGarrett D'Amore 				accum = (accum >> 24) |
16795c635efSGarrett D'Amore 					((accum << 8) & 0x00FF0000) |
16895c635efSGarrett D'Amore 					((accum >> 8) & 0x0000FF00) |
16995c635efSGarrett D'Amore 					(accum << 24);
17095c635efSGarrett D'Amore 
17195c635efSGarrett D'Amore 			if (0 == state) {
17295c635efSGarrett D'Amore 				accum < 128U ? putchar(accum) :
17395c635efSGarrett D'Amore 					printf("\\[u%.4X]", accum);
17495c635efSGarrett D'Amore 				accum = 0U;
17595c635efSGarrett D'Amore 			}
17695c635efSGarrett D'Amore 		} else if (cu & (1 << 7)) {
17795c635efSGarrett D'Amore 			/*
17895c635efSGarrett D'Amore 			 * Entering a UTF-8 state:  if we encounter a
17995c635efSGarrett D'Amore 			 * UTF-8 bitmask, calculate the expected UTF-8
18095c635efSGarrett D'Amore 			 * state from it.
18195c635efSGarrett D'Amore 			 */
18295c635efSGarrett D'Amore 			for (state = 0; state < 7; state++)
18395c635efSGarrett D'Amore 				if ( ! (cu & (1 << (7 - state))))
18495c635efSGarrett D'Amore 					break;
18595c635efSGarrett D'Amore 
18695c635efSGarrett D'Amore 			/* Accept only legitimate bit patterns. */
18795c635efSGarrett D'Amore 
18895c635efSGarrett D'Amore 			switch (state) {
18995c635efSGarrett D'Amore 			case (4):
19095c635efSGarrett D'Amore 				if (cu <= 244 && cu >= 240) {
19195c635efSGarrett D'Amore 					accum = (cu & 7) << 18;
19295c635efSGarrett D'Amore 					break;
19395c635efSGarrett D'Amore 				}
19495c635efSGarrett D'Amore 				/* Bad 4-sequence start bits. */
19595c635efSGarrett D'Amore 				return(0);
19695c635efSGarrett D'Amore 			case (3):
19795c635efSGarrett D'Amore 				if (cu <= 239 && cu >= 224) {
19895c635efSGarrett D'Amore 					accum = (cu & 15) << 12;
19995c635efSGarrett D'Amore 					break;
20095c635efSGarrett D'Amore 				}
20195c635efSGarrett D'Amore 				/* Bad 3-sequence start bits. */
20295c635efSGarrett D'Amore 				return(0);
20395c635efSGarrett D'Amore 			case (2):
20495c635efSGarrett D'Amore 				if (cu <= 223 && cu >= 194) {
20595c635efSGarrett D'Amore 					accum = (cu & 31) << 6;
20695c635efSGarrett D'Amore 					break;
20795c635efSGarrett D'Amore 				}
20895c635efSGarrett D'Amore 				/* Bad 2-sequence start bits. */
20995c635efSGarrett D'Amore 				return(0);
21095c635efSGarrett D'Amore 			default:
21195c635efSGarrett D'Amore 				/* Bad sequence bit mask. */
21295c635efSGarrett D'Amore 				return(0);
21395c635efSGarrett D'Amore 			}
21495c635efSGarrett D'Amore 			state--;
21595c635efSGarrett D'Amore 		} else
21695c635efSGarrett D'Amore 			putchar(cu);
21795c635efSGarrett D'Amore 	}
21895c635efSGarrett D'Amore 
21995c635efSGarrett D'Amore 	if (0 != state) {
22095c635efSGarrett D'Amore 		/* Bad trailing bits. */
22195c635efSGarrett D'Amore 		return(0);
22295c635efSGarrett D'Amore 	}
22395c635efSGarrett D'Amore 
22495c635efSGarrett D'Amore 	return(1);
22595c635efSGarrett D'Amore }
22695c635efSGarrett D'Amore 
22795c635efSGarrett D'Amore static void
resize_buf(struct buf * buf,size_t initial)22895c635efSGarrett D'Amore resize_buf(struct buf *buf, size_t initial)
22995c635efSGarrett D'Amore {
23095c635efSGarrett D'Amore 
23195c635efSGarrett D'Amore 	buf->sz = buf->sz > initial / 2 ?
23295c635efSGarrett D'Amore 		2 * buf->sz : initial;
23395c635efSGarrett D'Amore 
23495c635efSGarrett D'Amore 	buf->buf = realloc(buf->buf, buf->sz);
23595c635efSGarrett D'Amore 	if (NULL == buf->buf) {
23695c635efSGarrett D'Amore 		perror(NULL);
23795c635efSGarrett D'Amore 		exit(EXIT_FAILURE);
23895c635efSGarrett D'Amore 	}
23995c635efSGarrett D'Amore }
24095c635efSGarrett D'Amore 
24195c635efSGarrett D'Amore static int
read_whole_file(const char * f,int fd,struct buf * fb,int * with_mmap)24295c635efSGarrett D'Amore read_whole_file(const char *f, int fd,
24395c635efSGarrett D'Amore 		struct buf *fb, int *with_mmap)
24495c635efSGarrett D'Amore {
24595c635efSGarrett D'Amore 	size_t		 off;
24695c635efSGarrett D'Amore 	ssize_t		 ssz;
24795c635efSGarrett D'Amore 
24895c635efSGarrett D'Amore #ifdef	HAVE_MMAP
24995c635efSGarrett D'Amore 	struct stat	 st;
25095c635efSGarrett D'Amore 	if (-1 == fstat(fd, &st)) {
25195c635efSGarrett D'Amore 		perror(f);
25295c635efSGarrett D'Amore 		return(0);
25395c635efSGarrett D'Amore 	}
25495c635efSGarrett D'Amore 
25595c635efSGarrett D'Amore 	/*
25695c635efSGarrett D'Amore 	 * If we're a regular file, try just reading in the whole entry
25795c635efSGarrett D'Amore 	 * via mmap().  This is faster than reading it into blocks, and
25895c635efSGarrett D'Amore 	 * since each file is only a few bytes to begin with, I'm not
25995c635efSGarrett D'Amore 	 * concerned that this is going to tank any machines.
26095c635efSGarrett D'Amore 	 */
26195c635efSGarrett D'Amore 
26295c635efSGarrett D'Amore 	if (S_ISREG(st.st_mode) && st.st_size >= (1U << 31)) {
26395c635efSGarrett D'Amore 		fprintf(stderr, "%s: input too large\n", f);
26495c635efSGarrett D'Amore 		return(0);
26595c635efSGarrett D'Amore 	}
26695c635efSGarrett D'Amore 
26795c635efSGarrett D'Amore 	if (S_ISREG(st.st_mode)) {
26895c635efSGarrett D'Amore 		*with_mmap = 1;
26995c635efSGarrett D'Amore 		fb->sz = (size_t)st.st_size;
270*698f87a4SGarrett D'Amore 		fb->buf = mmap(NULL, fb->sz, PROT_READ, MAP_SHARED, fd, 0);
27195c635efSGarrett D'Amore 		if (fb->buf != MAP_FAILED)
27295c635efSGarrett D'Amore 			return(1);
27395c635efSGarrett D'Amore 	}
27495c635efSGarrett D'Amore #endif
27595c635efSGarrett D'Amore 
27695c635efSGarrett D'Amore 	/*
27795c635efSGarrett D'Amore 	 * If this isn't a regular file (like, say, stdin), then we must
27895c635efSGarrett D'Amore 	 * go the old way and just read things in bit by bit.
27995c635efSGarrett D'Amore 	 */
28095c635efSGarrett D'Amore 
28195c635efSGarrett D'Amore 	*with_mmap = 0;
28295c635efSGarrett D'Amore 	off = 0;
28395c635efSGarrett D'Amore 	fb->sz = 0;
28495c635efSGarrett D'Amore 	fb->buf = NULL;
28595c635efSGarrett D'Amore 	for (;;) {
28695c635efSGarrett D'Amore 		if (off == fb->sz && fb->sz == (1U << 31)) {
28795c635efSGarrett D'Amore 			fprintf(stderr, "%s: input too large\n", f);
28895c635efSGarrett D'Amore 			break;
28995c635efSGarrett D'Amore 		}
29095c635efSGarrett D'Amore 
29195c635efSGarrett D'Amore 		if (off == fb->sz)
29295c635efSGarrett D'Amore 			resize_buf(fb, 65536);
29395c635efSGarrett D'Amore 
29495c635efSGarrett D'Amore 		ssz = read(fd, fb->buf + (int)off, fb->sz - off);
29595c635efSGarrett D'Amore 		if (ssz == 0) {
29695c635efSGarrett D'Amore 			fb->sz = off;
29795c635efSGarrett D'Amore 			return(1);
29895c635efSGarrett D'Amore 		}
29995c635efSGarrett D'Amore 		if (ssz == -1) {
30095c635efSGarrett D'Amore 			perror(f);
30195c635efSGarrett D'Amore 			break;
30295c635efSGarrett D'Amore 		}
30395c635efSGarrett D'Amore 		off += (size_t)ssz;
30495c635efSGarrett D'Amore 	}
30595c635efSGarrett D'Amore 
30695c635efSGarrett D'Amore 	free(fb->buf);
30795c635efSGarrett D'Amore 	fb->buf = NULL;
30895c635efSGarrett D'Amore 	return(0);
30995c635efSGarrett D'Amore }
31095c635efSGarrett D'Amore 
31195c635efSGarrett D'Amore static int
cue_enc(const struct buf * b,size_t * offs,enum enc * enc)31295c635efSGarrett D'Amore cue_enc(const struct buf *b, size_t *offs, enum enc *enc)
31395c635efSGarrett D'Amore {
31495c635efSGarrett D'Amore 	const char	*ln, *eoln, *eoph;
31595c635efSGarrett D'Amore 	size_t		 sz, phsz, nsz;
31695c635efSGarrett D'Amore 	int		 i;
31795c635efSGarrett D'Amore 
31895c635efSGarrett D'Amore 	ln = b->buf + (int)*offs;
31995c635efSGarrett D'Amore 	sz = b->sz - *offs;
32095c635efSGarrett D'Amore 
32195c635efSGarrett D'Amore 	/* Look for the end-of-line. */
32295c635efSGarrett D'Amore 
32395c635efSGarrett D'Amore 	if (NULL == (eoln = memchr(ln, '\n', sz)))
32495c635efSGarrett D'Amore 		return(-1);
32595c635efSGarrett D'Amore 
32695c635efSGarrett D'Amore 	/* Set next-line marker. */
32795c635efSGarrett D'Amore 
32895c635efSGarrett D'Amore 	*offs = (size_t)((eoln + 1) - b->buf);
32995c635efSGarrett D'Amore 
33095c635efSGarrett D'Amore 	/* Check if we have the correct header/trailer. */
33195c635efSGarrett D'Amore 
33295c635efSGarrett D'Amore 	if ((sz = (size_t)(eoln - ln)) < 10 ||
33395c635efSGarrett D'Amore 			memcmp(ln, ".\\\" -*-", 7) ||
33495c635efSGarrett D'Amore 			memcmp(eoln - 3, "-*-", 3))
33595c635efSGarrett D'Amore 		return(0);
33695c635efSGarrett D'Amore 
33795c635efSGarrett D'Amore 	/* Move after the header and adjust for the trailer. */
33895c635efSGarrett D'Amore 
33995c635efSGarrett D'Amore 	ln += 7;
34095c635efSGarrett D'Amore 	sz -= 10;
34195c635efSGarrett D'Amore 
34295c635efSGarrett D'Amore 	while (sz > 0) {
34395c635efSGarrett D'Amore 		while (sz > 0 && ' ' == *ln) {
34495c635efSGarrett D'Amore 			ln++;
34595c635efSGarrett D'Amore 			sz--;
34695c635efSGarrett D'Amore 		}
34795c635efSGarrett D'Amore 		if (0 == sz)
34895c635efSGarrett D'Amore 			break;
34995c635efSGarrett D'Amore 
35095c635efSGarrett D'Amore 		/* Find the end-of-phrase marker (or eoln). */
35195c635efSGarrett D'Amore 
35295c635efSGarrett D'Amore 		if (NULL == (eoph = memchr(ln, ';', sz)))
35395c635efSGarrett D'Amore 			eoph = eoln - 3;
35495c635efSGarrett D'Amore 		else
35595c635efSGarrett D'Amore 			eoph++;
35695c635efSGarrett D'Amore 
35795c635efSGarrett D'Amore 		/* Only account for the "coding" phrase. */
35895c635efSGarrett D'Amore 
35995c635efSGarrett D'Amore 		if ((phsz = (size_t)(eoph - ln)) < 7 ||
36095c635efSGarrett D'Amore 				strncasecmp(ln, "coding:", 7)) {
36195c635efSGarrett D'Amore 			sz -= phsz;
36295c635efSGarrett D'Amore 			ln += phsz;
36395c635efSGarrett D'Amore 			continue;
36495c635efSGarrett D'Amore 		}
36595c635efSGarrett D'Amore 
36695c635efSGarrett D'Amore 		sz -= 7;
36795c635efSGarrett D'Amore 		ln += 7;
36895c635efSGarrett D'Amore 
36995c635efSGarrett D'Amore 		while (sz > 0 && ' ' == *ln) {
37095c635efSGarrett D'Amore 			ln++;
37195c635efSGarrett D'Amore 			sz--;
37295c635efSGarrett D'Amore 		}
37395c635efSGarrett D'Amore 		if (0 == sz)
37495c635efSGarrett D'Amore 			break;
37595c635efSGarrett D'Amore 
37695c635efSGarrett D'Amore 		/* Check us against known encodings. */
37795c635efSGarrett D'Amore 
37895c635efSGarrett D'Amore 		for (i = 0; i < (int)ENC__MAX; i++) {
37995c635efSGarrett D'Amore 			nsz = strlen(encs[i].name);
38095c635efSGarrett D'Amore 			if (phsz < nsz)
38195c635efSGarrett D'Amore 				continue;
38295c635efSGarrett D'Amore 			if (strncasecmp(ln, encs[i].name, nsz))
38395c635efSGarrett D'Amore 				continue;
38495c635efSGarrett D'Amore 
38595c635efSGarrett D'Amore 			*enc = (enum enc)i;
38695c635efSGarrett D'Amore 			return(1);
38795c635efSGarrett D'Amore 		}
38895c635efSGarrett D'Amore 
38995c635efSGarrett D'Amore 		/* Unknown encoding. */
39095c635efSGarrett D'Amore 
39195c635efSGarrett D'Amore 		*enc = ENC__MAX;
39295c635efSGarrett D'Amore 		return(1);
39395c635efSGarrett D'Amore 	}
39495c635efSGarrett D'Amore 
39595c635efSGarrett D'Amore 	return(0);
39695c635efSGarrett D'Amore }
39795c635efSGarrett D'Amore 
39895c635efSGarrett D'Amore int
main(int argc,char * argv[])39995c635efSGarrett D'Amore main(int argc, char *argv[])
40095c635efSGarrett D'Amore {
40195c635efSGarrett D'Amore 	int	 	 i, ch, map, fd, rc;
40295c635efSGarrett D'Amore 	struct buf	 b;
40395c635efSGarrett D'Amore 	const char	*fn;
40495c635efSGarrett D'Amore 	enum enc	 enc, def;
40595c635efSGarrett D'Amore 	unsigned char 	 bom[3] = { 0xEF, 0xBB, 0xBF };
40695c635efSGarrett D'Amore 	size_t		 offs;
40795c635efSGarrett D'Amore 	extern int	 optind;
40895c635efSGarrett D'Amore 	extern char	*optarg;
40995c635efSGarrett D'Amore 
41095c635efSGarrett D'Amore 	progname = strrchr(argv[0], '/');
41195c635efSGarrett D'Amore 	if (progname == NULL)
41295c635efSGarrett D'Amore 		progname = argv[0];
41395c635efSGarrett D'Amore 	else
41495c635efSGarrett D'Amore 		++progname;
41595c635efSGarrett D'Amore 
41695c635efSGarrett D'Amore 	fn = "<stdin>";
41795c635efSGarrett D'Amore 	fd = STDIN_FILENO;
41895c635efSGarrett D'Amore 	rc = EXIT_FAILURE;
41995c635efSGarrett D'Amore 	enc = def = ENC__MAX;
42095c635efSGarrett D'Amore 	map = 0;
42195c635efSGarrett D'Amore 
42295c635efSGarrett D'Amore 	memset(&b, 0, sizeof(struct buf));
42395c635efSGarrett D'Amore 
42495c635efSGarrett D'Amore 	while (-1 != (ch = getopt(argc, argv, "D:e:rdvh")))
42595c635efSGarrett D'Amore 		switch (ch) {
42695c635efSGarrett D'Amore 		case ('D'):
42795c635efSGarrett D'Amore 			/* FALLTHROUGH */
42895c635efSGarrett D'Amore 		case ('e'):
42995c635efSGarrett D'Amore 			for (i = 0; i < (int)ENC__MAX; i++) {
43095c635efSGarrett D'Amore 				if (strcasecmp(optarg, encs[i].name))
43195c635efSGarrett D'Amore 					continue;
43295c635efSGarrett D'Amore 				break;
43395c635efSGarrett D'Amore 			}
43495c635efSGarrett D'Amore 			if (i < (int)ENC__MAX) {
43595c635efSGarrett D'Amore 				if ('D' == ch)
43695c635efSGarrett D'Amore 					def = (enum enc)i;
43795c635efSGarrett D'Amore 				else
43895c635efSGarrett D'Amore 					enc = (enum enc)i;
43995c635efSGarrett D'Amore 				break;
44095c635efSGarrett D'Amore 			}
44195c635efSGarrett D'Amore 
44295c635efSGarrett D'Amore 			fprintf(stderr, "%s: Bad encoding\n", optarg);
44395c635efSGarrett D'Amore 			return(EXIT_FAILURE);
44495c635efSGarrett D'Amore 		case ('r'):
44595c635efSGarrett D'Amore 			/* FALLTHROUGH */
44695c635efSGarrett D'Amore 		case ('d'):
44795c635efSGarrett D'Amore 			/* FALLTHROUGH */
44895c635efSGarrett D'Amore 		case ('v'):
44995c635efSGarrett D'Amore 			/* Compatibility with GNU preconv. */
45095c635efSGarrett D'Amore 			break;
45195c635efSGarrett D'Amore 		case ('h'):
45295c635efSGarrett D'Amore 			/* Compatibility with GNU preconv. */
45395c635efSGarrett D'Amore 			/* FALLTHROUGH */
45495c635efSGarrett D'Amore 		default:
45595c635efSGarrett D'Amore 			usage();
45695c635efSGarrett D'Amore 			return(EXIT_FAILURE);
45795c635efSGarrett D'Amore 		}
45895c635efSGarrett D'Amore 
45995c635efSGarrett D'Amore 	argc -= optind;
46095c635efSGarrett D'Amore 	argv += optind;
46195c635efSGarrett D'Amore 
46295c635efSGarrett D'Amore 	/*
46395c635efSGarrett D'Amore 	 * Open and read the first argument on the command-line.
46495c635efSGarrett D'Amore 	 * If we don't have one, we default to stdin.
46595c635efSGarrett D'Amore 	 */
46695c635efSGarrett D'Amore 
46795c635efSGarrett D'Amore 	if (argc > 0) {
46895c635efSGarrett D'Amore 		fn = *argv;
46995c635efSGarrett D'Amore 		fd = open(fn, O_RDONLY, 0);
47095c635efSGarrett D'Amore 		if (-1 == fd) {
47195c635efSGarrett D'Amore 			perror(fn);
47295c635efSGarrett D'Amore 			return(EXIT_FAILURE);
47395c635efSGarrett D'Amore 		}
47495c635efSGarrett D'Amore 	}
47595c635efSGarrett D'Amore 
47695c635efSGarrett D'Amore 	if ( ! read_whole_file(fn, fd, &b, &map))
47795c635efSGarrett D'Amore 		goto out;
47895c635efSGarrett D'Amore 
47995c635efSGarrett D'Amore 	/* Try to read the UTF-8 BOM. */
48095c635efSGarrett D'Amore 
48195c635efSGarrett D'Amore 	if (ENC__MAX == enc)
48295c635efSGarrett D'Amore 		if (b.sz > 3 && 0 == memcmp(b.buf, bom, 3)) {
48395c635efSGarrett D'Amore 			b.offs = 3;
48495c635efSGarrett D'Amore 			enc = ENC_UTF_8;
48595c635efSGarrett D'Amore 		}
48695c635efSGarrett D'Amore 
48795c635efSGarrett D'Amore 	/* Try reading from the "-*-" cue. */
48895c635efSGarrett D'Amore 
48995c635efSGarrett D'Amore 	if (ENC__MAX == enc) {
49095c635efSGarrett D'Amore 		offs = b.offs;
49195c635efSGarrett D'Amore 		ch = cue_enc(&b, &offs, &enc);
49295c635efSGarrett D'Amore 		if (0 == ch)
49395c635efSGarrett D'Amore 			ch = cue_enc(&b, &offs, &enc);
49495c635efSGarrett D'Amore 	}
49595c635efSGarrett D'Amore 
49695c635efSGarrett D'Amore 	/*
49795c635efSGarrett D'Amore 	 * No encoding has been detected.
49895c635efSGarrett D'Amore 	 * Thus, we either fall into our default encoder, if specified,
49995c635efSGarrett D'Amore 	 * or use Latin-1 if all else fails.
50095c635efSGarrett D'Amore 	 */
50195c635efSGarrett D'Amore 
50295c635efSGarrett D'Amore 	if (ENC__MAX == enc)
50395c635efSGarrett D'Amore 		enc = ENC__MAX == def ? ENC_LATIN_1 : def;
50495c635efSGarrett D'Amore 
50595c635efSGarrett D'Amore 	if ( ! (*encs[(int)enc].conv)(&b)) {
50695c635efSGarrett D'Amore 		fprintf(stderr, "%s: Bad encoding\n", fn);
50795c635efSGarrett D'Amore 		goto out;
50895c635efSGarrett D'Amore 	}
50995c635efSGarrett D'Amore 
51095c635efSGarrett D'Amore 	rc = EXIT_SUCCESS;
51195c635efSGarrett D'Amore out:
51295c635efSGarrett D'Amore #ifdef	HAVE_MMAP
51395c635efSGarrett D'Amore 	if (map)
51495c635efSGarrett D'Amore 		munmap(b.buf, b.sz);
51595c635efSGarrett D'Amore 	else
51695c635efSGarrett D'Amore #endif
51795c635efSGarrett D'Amore 		free(b.buf);
51895c635efSGarrett D'Amore 
51995c635efSGarrett D'Amore 	if (fd > STDIN_FILENO)
52095c635efSGarrett D'Amore 		close(fd);
52195c635efSGarrett D'Amore 
52295c635efSGarrett D'Amore 	return(rc);
52395c635efSGarrett D'Amore }
524