parse.c revision 80290
11590Srgrimes/*
21590Srgrimes * Copyright (c) 1989, 1993
31590Srgrimes *	The Regents of the University of California.  All rights reserved.
41590Srgrimes *
51590Srgrimes * Redistribution and use in source and binary forms, with or without
61590Srgrimes * modification, are permitted provided that the following conditions
71590Srgrimes * are met:
81590Srgrimes * 1. Redistributions of source code must retain the above copyright
91590Srgrimes *    notice, this list of conditions and the following disclaimer.
101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111590Srgrimes *    notice, this list of conditions and the following disclaimer in the
121590Srgrimes *    documentation and/or other materials provided with the distribution.
131590Srgrimes * 3. All advertising materials mentioning features or use of this software
141590Srgrimes *    must display the following acknowledgement:
151590Srgrimes *	This product includes software developed by the University of
161590Srgrimes *	California, Berkeley and its contributors.
171590Srgrimes * 4. Neither the name of the University nor the names of its contributors
181590Srgrimes *    may be used to endorse or promote products derived from this software
191590Srgrimes *    without specific prior written permission.
201590Srgrimes *
211590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311590Srgrimes * SUCH DAMAGE.
321590Srgrimes */
331590Srgrimes
341590Srgrimes#ifndef lint
3527315Scharnier#if 0
361590Srgrimesstatic char sccsid[] = "@(#)parse.c	8.1 (Berkeley) 6/6/93";
3727315Scharnier#endif
3827315Scharnierstatic const char rcsid[] =
3950477Speter  "$FreeBSD: head/usr.bin/hexdump/parse.c 80290 2001-07-24 14:11:09Z obrien $";
401590Srgrimes#endif /* not lint */
411590Srgrimes
421590Srgrimes#include <sys/types.h>
431590Srgrimes
4427315Scharnier#include <err.h>
451590Srgrimes#include <fcntl.h>
461590Srgrimes#include <stdio.h>
471590Srgrimes#include <stdlib.h>
481590Srgrimes#include <ctype.h>
491590Srgrimes#include <string.h>
501590Srgrimes#include "hexdump.h"
511590Srgrimes
521590SrgrimesFU *endfu;					/* format at end-of-data */
531590Srgrimes
541590Srgrimesvoid
551590Srgrimesaddfile(name)
561590Srgrimes	char *name;
571590Srgrimes{
5830921Sache	register unsigned char *p;
591590Srgrimes	FILE *fp;
601590Srgrimes	int ch;
611590Srgrimes	char buf[2048 + 1];
621590Srgrimes
631590Srgrimes	if ((fp = fopen(name, "r")) == NULL)
6427315Scharnier		err(1, "%s", name);
651590Srgrimes	while (fgets(buf, sizeof(buf), fp)) {
661590Srgrimes		if (!(p = index(buf, '\n'))) {
6727315Scharnier			warnx("line too long");
681590Srgrimes			while ((ch = getchar()) != '\n' && ch != EOF);
691590Srgrimes			continue;
701590Srgrimes		}
711590Srgrimes		*p = '\0';
721590Srgrimes		for (p = buf; *p && isspace(*p); ++p);
731590Srgrimes		if (!*p || *p == '#')
741590Srgrimes			continue;
751590Srgrimes		add(p);
761590Srgrimes	}
771590Srgrimes	(void)fclose(fp);
781590Srgrimes}
791590Srgrimes
801590Srgrimesvoid
811590Srgrimesadd(fmt)
821590Srgrimes	char *fmt;
831590Srgrimes{
8430921Sache	unsigned char *p, *savep;
851590Srgrimes	static FS **nextfs;
861590Srgrimes	FS *tfs;
871590Srgrimes	FU *tfu, **nextfu;
881590Srgrimes
891590Srgrimes	/* start new linked list of format units */
9080290Sobrien	if ((tfs = calloc(1, sizeof(FS))) == NULL)
9180290Sobrien		err(1, NULL);
921590Srgrimes	if (!fshead)
931590Srgrimes		fshead = tfs;
941590Srgrimes	else
951590Srgrimes		*nextfs = tfs;
961590Srgrimes	nextfs = &tfs->nextfs;
971590Srgrimes	nextfu = &tfs->nextfu;
981590Srgrimes
991590Srgrimes	/* take the format string and break it up into format units */
1001590Srgrimes	for (p = fmt;;) {
1011590Srgrimes		/* skip leading white space */
1021590Srgrimes		for (; isspace(*p); ++p);
1031590Srgrimes		if (!*p)
1041590Srgrimes			break;
1051590Srgrimes
1061590Srgrimes		/* allocate a new format unit and link it in */
10780290Sobrien		if ((tfu = calloc(1, sizeof(FU))) == NULL)
10880290Sobrien			err(1, NULL);
1091590Srgrimes		*nextfu = tfu;
1101590Srgrimes		nextfu = &tfu->nextfu;
1111590Srgrimes		tfu->reps = 1;
1121590Srgrimes
1131590Srgrimes		/* if leading digit, repetition count */
1141590Srgrimes		if (isdigit(*p)) {
1151590Srgrimes			for (savep = p; isdigit(*p); ++p);
1161590Srgrimes			if (!isspace(*p) && *p != '/')
1171590Srgrimes				badfmt(fmt);
1181590Srgrimes			/* may overwrite either white space or slash */
1191590Srgrimes			tfu->reps = atoi(savep);
1201590Srgrimes			tfu->flags = F_SETREP;
1211590Srgrimes			/* skip trailing white space */
1221590Srgrimes			for (++p; isspace(*p); ++p);
1231590Srgrimes		}
1241590Srgrimes
1251590Srgrimes		/* skip slash and trailing white space */
1261590Srgrimes		if (*p == '/')
1271590Srgrimes			while (isspace(*++p));
1281590Srgrimes
1291590Srgrimes		/* byte count */
1301590Srgrimes		if (isdigit(*p)) {
1311590Srgrimes			for (savep = p; isdigit(*p); ++p);
1321590Srgrimes			if (!isspace(*p))
1331590Srgrimes				badfmt(fmt);
1341590Srgrimes			tfu->bcnt = atoi(savep);
1351590Srgrimes			/* skip trailing white space */
1361590Srgrimes			for (++p; isspace(*p); ++p);
1371590Srgrimes		}
1381590Srgrimes
1391590Srgrimes		/* format */
1401590Srgrimes		if (*p != '"')
1411590Srgrimes			badfmt(fmt);
1421590Srgrimes		for (savep = ++p; *p != '"';)
1431590Srgrimes			if (*p++ == 0)
1441590Srgrimes				badfmt(fmt);
1451590Srgrimes		if (!(tfu->fmt = malloc(p - savep + 1)))
14680290Sobrien			err(1, NULL);
1471590Srgrimes		(void) strncpy(tfu->fmt, savep, p - savep);
1481590Srgrimes		tfu->fmt[p - savep] = '\0';
1491590Srgrimes		escape(tfu->fmt);
1501590Srgrimes		p++;
1511590Srgrimes	}
1521590Srgrimes}
1531590Srgrimes
1541590Srgrimesstatic char *spec = ".#-+ 0123456789";
1551590Srgrimes
1561590Srgrimesint
1571590Srgrimessize(fs)
1581590Srgrimes	FS *fs;
1591590Srgrimes{
1601590Srgrimes	register FU *fu;
1611590Srgrimes	register int bcnt, cursize;
16230921Sache	register unsigned char *fmt;
1631590Srgrimes	int prec;
1641590Srgrimes
1651590Srgrimes	/* figure out the data block size needed for each format unit */
1661590Srgrimes	for (cursize = 0, fu = fs->nextfu; fu; fu = fu->nextfu) {
1671590Srgrimes		if (fu->bcnt) {
1681590Srgrimes			cursize += fu->bcnt * fu->reps;
1691590Srgrimes			continue;
1701590Srgrimes		}
1711590Srgrimes		for (bcnt = prec = 0, fmt = fu->fmt; *fmt; ++fmt) {
1721590Srgrimes			if (*fmt != '%')
1731590Srgrimes				continue;
1741590Srgrimes			/*
1751590Srgrimes			 * skip any special chars -- save precision in
1761590Srgrimes			 * case it's a %s format.
1771590Srgrimes			 */
1781590Srgrimes			while (index(spec + 1, *++fmt));
1791590Srgrimes			if (*fmt == '.' && isdigit(*++fmt)) {
1801590Srgrimes				prec = atoi(fmt);
1811590Srgrimes				while (isdigit(*++fmt));
1821590Srgrimes			}
1831590Srgrimes			switch(*fmt) {
1841590Srgrimes			case 'c':
1851590Srgrimes				bcnt += 1;
1861590Srgrimes				break;
1871590Srgrimes			case 'd': case 'i': case 'o': case 'u':
1881590Srgrimes			case 'x': case 'X':
1891590Srgrimes				bcnt += 4;
1901590Srgrimes				break;
1911590Srgrimes			case 'e': case 'E': case 'f': case 'g': case 'G':
1921590Srgrimes				bcnt += 8;
1931590Srgrimes				break;
1941590Srgrimes			case 's':
1951590Srgrimes				bcnt += prec;
1961590Srgrimes				break;
1971590Srgrimes			case '_':
1981590Srgrimes				switch(*++fmt) {
1991590Srgrimes				case 'c': case 'p': case 'u':
2001590Srgrimes					bcnt += 1;
2011590Srgrimes					break;
2021590Srgrimes				}
2031590Srgrimes			}
2041590Srgrimes		}
2051590Srgrimes		cursize += bcnt * fu->reps;
2061590Srgrimes	}
2071590Srgrimes	return (cursize);
2081590Srgrimes}
2091590Srgrimes
2101590Srgrimesvoid
2111590Srgrimesrewrite(fs)
2121590Srgrimes	FS *fs;
2131590Srgrimes{
2141590Srgrimes	enum { NOTOKAY, USEBCNT, USEPREC } sokay;
2151590Srgrimes	register PR *pr, **nextpr;
2161590Srgrimes	register FU *fu;
21730921Sache	unsigned char *p1, *p2, *fmtp;
21830921Sache	char savech, cs[3];
2191590Srgrimes	int nconv, prec;
2201590Srgrimes
2211590Srgrimes	for (fu = fs->nextfu; fu; fu = fu->nextfu) {
2221590Srgrimes		/*
2231590Srgrimes		 * Break each format unit into print units; each conversion
2241590Srgrimes		 * character gets its own.
2251590Srgrimes		 */
2261590Srgrimes		for (nconv = 0, fmtp = fu->fmt; *fmtp; nextpr = &pr->nextpr) {
22780290Sobrien			if ((pr = calloc(1, sizeof(PR))) == NULL)
22880290Sobrien				err(1, NULL);
2291590Srgrimes			if (!fu->nextpr)
2301590Srgrimes				fu->nextpr = pr;
2311590Srgrimes			else
2321590Srgrimes				*nextpr = pr;
2331590Srgrimes
2341590Srgrimes			/* Skip preceding text and up to the next % sign. */
2351590Srgrimes			for (p1 = fmtp; *p1 && *p1 != '%'; ++p1);
2361590Srgrimes
2371590Srgrimes			/* Only text in the string. */
2381590Srgrimes			if (!*p1) {
2391590Srgrimes				pr->fmt = fmtp;
2401590Srgrimes				pr->flags = F_TEXT;
2411590Srgrimes				break;
2421590Srgrimes			}
2431590Srgrimes
2441590Srgrimes			/*
2451590Srgrimes			 * Get precision for %s -- if have a byte count, don't
2461590Srgrimes			 * need it.
2471590Srgrimes			 */
2481590Srgrimes			if (fu->bcnt) {
2491590Srgrimes				sokay = USEBCNT;
2501590Srgrimes				/* Skip to conversion character. */
2511590Srgrimes				for (++p1; index(spec, *p1); ++p1);
2521590Srgrimes			} else {
2531590Srgrimes				/* Skip any special chars, field width. */
2541590Srgrimes				while (index(spec + 1, *++p1));
2551590Srgrimes				if (*p1 == '.' && isdigit(*++p1)) {
2561590Srgrimes					sokay = USEPREC;
2571590Srgrimes					prec = atoi(p1);
2581590Srgrimes					while (isdigit(*++p1));
2591590Srgrimes				} else
2601590Srgrimes					sokay = NOTOKAY;
2611590Srgrimes			}
2621590Srgrimes
2631590Srgrimes			p2 = p1 + 1;		/* Set end pointer. */
2641590Srgrimes			cs[0] = *p1;		/* Set conversion string. */
2651590Srgrimes			cs[1] = '\0';
2661590Srgrimes
2671590Srgrimes			/*
2681590Srgrimes			 * Figure out the byte count for each conversion;
2691590Srgrimes			 * rewrite the format as necessary, set up blank-
2701590Srgrimes			 * padding for end of data.
2711590Srgrimes			 */
2721590Srgrimes			switch(cs[0]) {
2731590Srgrimes			case 'c':
2741590Srgrimes				pr->flags = F_CHAR;
2751590Srgrimes				switch(fu->bcnt) {
2761590Srgrimes				case 0: case 1:
2771590Srgrimes					pr->bcnt = 1;
2781590Srgrimes					break;
2791590Srgrimes				default:
2801590Srgrimes					p1[1] = '\0';
2811590Srgrimes					badcnt(p1);
2821590Srgrimes				}
2831590Srgrimes				break;
2841590Srgrimes			case 'd': case 'i':
2851590Srgrimes				pr->flags = F_INT;
2861590Srgrimes				goto isint;
2871590Srgrimes			case 'o': case 'u': case 'x': case 'X':
2881590Srgrimes				pr->flags = F_UINT;
2891590Srgrimesisint:				cs[2] = '\0';
2901590Srgrimes				cs[1] = cs[0];
2911590Srgrimes				cs[0] = 'q';
2921590Srgrimes				switch(fu->bcnt) {
2931590Srgrimes				case 0: case 4:
2941590Srgrimes					pr->bcnt = 4;
2951590Srgrimes					break;
2961590Srgrimes				case 1:
2971590Srgrimes					pr->bcnt = 1;
2981590Srgrimes					break;
2991590Srgrimes				case 2:
3001590Srgrimes					pr->bcnt = 2;
3011590Srgrimes					break;
3021590Srgrimes				default:
3031590Srgrimes					p1[1] = '\0';
3041590Srgrimes					badcnt(p1);
3051590Srgrimes				}
3061590Srgrimes				break;
3071590Srgrimes			case 'e': case 'E': case 'f': case 'g': case 'G':
3081590Srgrimes				pr->flags = F_DBL;
3091590Srgrimes				switch(fu->bcnt) {
3101590Srgrimes				case 0: case 8:
3111590Srgrimes					pr->bcnt = 8;
3121590Srgrimes					break;
3131590Srgrimes				case 4:
3141590Srgrimes					pr->bcnt = 4;
3151590Srgrimes					break;
3161590Srgrimes				default:
3171590Srgrimes					p1[1] = '\0';
3181590Srgrimes					badcnt(p1);
3191590Srgrimes				}
3201590Srgrimes				break;
3211590Srgrimes			case 's':
3221590Srgrimes				pr->flags = F_STR;
3231590Srgrimes				switch(sokay) {
3241590Srgrimes				case NOTOKAY:
3251590Srgrimes					badsfmt();
3261590Srgrimes				case USEBCNT:
3271590Srgrimes					pr->bcnt = fu->bcnt;
3281590Srgrimes					break;
3291590Srgrimes				case USEPREC:
3301590Srgrimes					pr->bcnt = prec;
3311590Srgrimes					break;
3321590Srgrimes				}
3331590Srgrimes				break;
3341590Srgrimes			case '_':
3351590Srgrimes				++p2;
3361590Srgrimes				switch(p1[1]) {
3371590Srgrimes				case 'A':
3381590Srgrimes					endfu = fu;
3391590Srgrimes					fu->flags |= F_IGNORE;
3401590Srgrimes					/* FALLTHROUGH */
3411590Srgrimes				case 'a':
3421590Srgrimes					pr->flags = F_ADDRESS;
3431590Srgrimes					++p2;
3441590Srgrimes					switch(p1[2]) {
3451590Srgrimes					case 'd': case 'o': case'x':
3461590Srgrimes						cs[0] = 'q';
3471590Srgrimes						cs[1] = p1[2];
3481590Srgrimes						cs[2] = '\0';
3491590Srgrimes						break;
3501590Srgrimes					default:
3511590Srgrimes						p1[3] = '\0';
3521590Srgrimes						badconv(p1);
3531590Srgrimes					}
3541590Srgrimes					break;
3551590Srgrimes				case 'c':
3561590Srgrimes					pr->flags = F_C;
3571590Srgrimes					/* cs[0] = 'c';	set in conv_c */
3581590Srgrimes					goto isint2;
3591590Srgrimes				case 'p':
3601590Srgrimes					pr->flags = F_P;
3611590Srgrimes					cs[0] = 'c';
3621590Srgrimes					goto isint2;
3631590Srgrimes				case 'u':
3641590Srgrimes					pr->flags = F_U;
3651590Srgrimes					/* cs[0] = 'c';	set in conv_u */
3661590Srgrimesisint2:					switch(fu->bcnt) {
3671590Srgrimes					case 0: case 1:
3681590Srgrimes						pr->bcnt = 1;
3691590Srgrimes						break;
3701590Srgrimes					default:
3711590Srgrimes						p1[2] = '\0';
3721590Srgrimes						badcnt(p1);
3731590Srgrimes					}
3741590Srgrimes					break;
3751590Srgrimes				default:
3761590Srgrimes					p1[2] = '\0';
3771590Srgrimes					badconv(p1);
3781590Srgrimes				}
3791590Srgrimes				break;
3801590Srgrimes			default:
3811590Srgrimes				p1[1] = '\0';
3821590Srgrimes				badconv(p1);
3831590Srgrimes			}
3841590Srgrimes
3851590Srgrimes			/*
3861590Srgrimes			 * Copy to PR format string, set conversion character
3871590Srgrimes			 * pointer, update original.
3881590Srgrimes			 */
3891590Srgrimes			savech = *p2;
3901590Srgrimes			p1[0] = '\0';
39180290Sobrien			if ((pr->fmt = calloc(1, strlen(fmtp) + 2)) == NULL)
39280290Sobrien				err(1, NULL);
3931590Srgrimes			(void)strcpy(pr->fmt, fmtp);
3941590Srgrimes			(void)strcat(pr->fmt, cs);
3951590Srgrimes			*p2 = savech;
3961590Srgrimes			pr->cchar = pr->fmt + (p1 - fmtp);
3971590Srgrimes			fmtp = p2;
3981590Srgrimes
3991590Srgrimes			/* Only one conversion character if byte count. */
4001590Srgrimes			if (!(pr->flags&F_ADDRESS) && fu->bcnt && nconv++)
40127315Scharnier	    errx(1, "byte count with multiple conversion characters");
4021590Srgrimes		}
4031590Srgrimes		/*
4041590Srgrimes		 * If format unit byte count not specified, figure it out
4051590Srgrimes		 * so can adjust rep count later.
4061590Srgrimes		 */
4071590Srgrimes		if (!fu->bcnt)
4081590Srgrimes			for (pr = fu->nextpr; pr; pr = pr->nextpr)
4091590Srgrimes				fu->bcnt += pr->bcnt;
4101590Srgrimes	}
4111590Srgrimes	/*
4121590Srgrimes	 * If the format string interprets any data at all, and it's
4131590Srgrimes	 * not the same as the blocksize, and its last format unit
4141590Srgrimes	 * interprets any data at all, and has no iteration count,
4151590Srgrimes	 * repeat it as necessary.
4161590Srgrimes	 *
4171590Srgrimes	 * If, rep count is greater than 1, no trailing whitespace
4181590Srgrimes	 * gets output from the last iteration of the format unit.
4191590Srgrimes	 */
4201590Srgrimes	for (fu = fs->nextfu;; fu = fu->nextfu) {
4211590Srgrimes		if (!fu->nextfu && fs->bcnt < blocksize &&
4221590Srgrimes		    !(fu->flags&F_SETREP) && fu->bcnt)
4231590Srgrimes			fu->reps += (blocksize - fs->bcnt) / fu->bcnt;
4241590Srgrimes		if (fu->reps > 1) {
4251590Srgrimes			for (pr = fu->nextpr;; pr = pr->nextpr)
4261590Srgrimes				if (!pr->nextpr)
4271590Srgrimes					break;
4281590Srgrimes			for (p1 = pr->fmt, p2 = NULL; *p1; ++p1)
4291590Srgrimes				p2 = isspace(*p1) ? p1 : NULL;
4301590Srgrimes			if (p2)
4311590Srgrimes				pr->nospace = p2;
4321590Srgrimes		}
4331590Srgrimes		if (!fu->nextfu)
4341590Srgrimes			break;
4351590Srgrimes	}
4361590Srgrimes#ifdef DEBUG
4371590Srgrimes	for (fu = fs->nextfu; fu; fu = fu->nextfu) {
4381590Srgrimes		(void)printf("fmt:");
4391590Srgrimes		for (pr = fu->nextpr; pr; pr = pr->nextpr)
4401590Srgrimes			(void)printf(" {%s}", pr->fmt);
4411590Srgrimes		(void)printf("\n");
4421590Srgrimes	}
4431590Srgrimes#endif
4441590Srgrimes}
4451590Srgrimes
4461590Srgrimesvoid
4471590Srgrimesescape(p1)
4481590Srgrimes	register char *p1;
4491590Srgrimes{
4501590Srgrimes	register char *p2;
4511590Srgrimes
4521590Srgrimes	/* alphabetic escape sequences have to be done in place */
4531590Srgrimes	for (p2 = p1;; ++p1, ++p2) {
4541590Srgrimes		if (!*p1) {
4551590Srgrimes			*p2 = *p1;
4561590Srgrimes			break;
4571590Srgrimes		}
4581590Srgrimes		if (*p1 == '\\')
4591590Srgrimes			switch(*++p1) {
4601590Srgrimes			case 'a':
4611590Srgrimes			     /* *p2 = '\a'; */
4621590Srgrimes				*p2 = '\007';
4631590Srgrimes				break;
4641590Srgrimes			case 'b':
4651590Srgrimes				*p2 = '\b';
4661590Srgrimes				break;
4671590Srgrimes			case 'f':
4681590Srgrimes				*p2 = '\f';
4691590Srgrimes				break;
4701590Srgrimes			case 'n':
4711590Srgrimes				*p2 = '\n';
4721590Srgrimes				break;
4731590Srgrimes			case 'r':
4741590Srgrimes				*p2 = '\r';
4751590Srgrimes				break;
4761590Srgrimes			case 't':
4771590Srgrimes				*p2 = '\t';
4781590Srgrimes				break;
4791590Srgrimes			case 'v':
4801590Srgrimes				*p2 = '\v';
4811590Srgrimes				break;
4821590Srgrimes			default:
4831590Srgrimes				*p2 = *p1;
4841590Srgrimes				break;
4851590Srgrimes			}
4861590Srgrimes	}
4871590Srgrimes}
4881590Srgrimes
4891590Srgrimesvoid
4901590Srgrimesbadcnt(s)
4911590Srgrimes	char *s;
4921590Srgrimes{
49327315Scharnier	errx(1, "%s: bad byte count", s);
4941590Srgrimes}
4951590Srgrimes
4961590Srgrimesvoid
4971590Srgrimesbadsfmt()
4981590Srgrimes{
49927315Scharnier	errx(1, "%%s: requires a precision or a byte count");
5001590Srgrimes}
5011590Srgrimes
5021590Srgrimesvoid
5031590Srgrimesbadfmt(fmt)
5041590Srgrimes	char *fmt;
5051590Srgrimes{
50627315Scharnier	errx(1, "\"%s\": bad format", fmt);
5071590Srgrimes}
5081590Srgrimes
5091590Srgrimesvoid
5101590Srgrimesbadconv(ch)
5111590Srgrimes	char *ch;
5121590Srgrimes{
51327315Scharnier	errx(1, "%%%s: bad conversion character", ch);
5141590Srgrimes}
515