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 * 4. Neither the name of the University nor the names of its contributors
141590Srgrimes *    may be used to endorse or promote products derived from this software
151590Srgrimes *    without specific prior written permission.
161590Srgrimes *
171590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
181590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
191590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
201590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
211590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
221590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
231590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
241590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
251590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
261590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271590Srgrimes * SUCH DAMAGE.
281590Srgrimes */
291590Srgrimes
301590Srgrimes#ifndef lint
3127315Scharnier#if 0
321590Srgrimesstatic char sccsid[] = "@(#)parse.c	8.1 (Berkeley) 6/6/93";
3327315Scharnier#endif
341590Srgrimes#endif /* not lint */
3599112Sobrien#include <sys/cdefs.h>
3699112Sobrien__FBSDID("$FreeBSD$");
371590Srgrimes
381590Srgrimes#include <sys/types.h>
391590Srgrimes
4027315Scharnier#include <err.h>
411590Srgrimes#include <fcntl.h>
421590Srgrimes#include <stdio.h>
431590Srgrimes#include <stdlib.h>
441590Srgrimes#include <ctype.h>
451590Srgrimes#include <string.h>
461590Srgrimes#include "hexdump.h"
471590Srgrimes
481590SrgrimesFU *endfu;					/* format at end-of-data */
491590Srgrimes
501590Srgrimesvoid
51102944Sdwmaloneaddfile(char *name)
521590Srgrimes{
53102944Sdwmalone	unsigned char *p;
541590Srgrimes	FILE *fp;
551590Srgrimes	int ch;
561590Srgrimes	char buf[2048 + 1];
571590Srgrimes
581590Srgrimes	if ((fp = fopen(name, "r")) == NULL)
5927315Scharnier		err(1, "%s", name);
601590Srgrimes	while (fgets(buf, sizeof(buf), fp)) {
611590Srgrimes		if (!(p = index(buf, '\n'))) {
6227315Scharnier			warnx("line too long");
631590Srgrimes			while ((ch = getchar()) != '\n' && ch != EOF);
641590Srgrimes			continue;
651590Srgrimes		}
661590Srgrimes		*p = '\0';
671590Srgrimes		for (p = buf; *p && isspace(*p); ++p);
681590Srgrimes		if (!*p || *p == '#')
691590Srgrimes			continue;
701590Srgrimes		add(p);
711590Srgrimes	}
721590Srgrimes	(void)fclose(fp);
731590Srgrimes}
741590Srgrimes
751590Srgrimesvoid
76102944Sdwmaloneadd(const char *fmt)
771590Srgrimes{
7887203Smarkm	unsigned const char *p, *savep;
791590Srgrimes	static FS **nextfs;
801590Srgrimes	FS *tfs;
811590Srgrimes	FU *tfu, **nextfu;
821590Srgrimes
831590Srgrimes	/* start new linked list of format units */
8480290Sobrien	if ((tfs = calloc(1, sizeof(FS))) == NULL)
8580290Sobrien		err(1, NULL);
861590Srgrimes	if (!fshead)
871590Srgrimes		fshead = tfs;
881590Srgrimes	else
891590Srgrimes		*nextfs = tfs;
901590Srgrimes	nextfs = &tfs->nextfs;
911590Srgrimes	nextfu = &tfs->nextfu;
921590Srgrimes
931590Srgrimes	/* take the format string and break it up into format units */
941590Srgrimes	for (p = fmt;;) {
951590Srgrimes		/* skip leading white space */
961590Srgrimes		for (; isspace(*p); ++p);
971590Srgrimes		if (!*p)
981590Srgrimes			break;
991590Srgrimes
1001590Srgrimes		/* allocate a new format unit and link it in */
10180290Sobrien		if ((tfu = calloc(1, sizeof(FU))) == NULL)
10280290Sobrien			err(1, NULL);
1031590Srgrimes		*nextfu = tfu;
1041590Srgrimes		nextfu = &tfu->nextfu;
1051590Srgrimes		tfu->reps = 1;
1061590Srgrimes
1071590Srgrimes		/* if leading digit, repetition count */
1081590Srgrimes		if (isdigit(*p)) {
1091590Srgrimes			for (savep = p; isdigit(*p); ++p);
1101590Srgrimes			if (!isspace(*p) && *p != '/')
1111590Srgrimes				badfmt(fmt);
1121590Srgrimes			/* may overwrite either white space or slash */
1131590Srgrimes			tfu->reps = atoi(savep);
1141590Srgrimes			tfu->flags = F_SETREP;
1151590Srgrimes			/* skip trailing white space */
1161590Srgrimes			for (++p; isspace(*p); ++p);
1171590Srgrimes		}
1181590Srgrimes
1191590Srgrimes		/* skip slash and trailing white space */
1201590Srgrimes		if (*p == '/')
1211590Srgrimes			while (isspace(*++p));
1221590Srgrimes
1231590Srgrimes		/* byte count */
1241590Srgrimes		if (isdigit(*p)) {
1251590Srgrimes			for (savep = p; isdigit(*p); ++p);
1261590Srgrimes			if (!isspace(*p))
1271590Srgrimes				badfmt(fmt);
1281590Srgrimes			tfu->bcnt = atoi(savep);
1291590Srgrimes			/* skip trailing white space */
1301590Srgrimes			for (++p; isspace(*p); ++p);
1311590Srgrimes		}
1321590Srgrimes
1331590Srgrimes		/* format */
1341590Srgrimes		if (*p != '"')
1351590Srgrimes			badfmt(fmt);
1361590Srgrimes		for (savep = ++p; *p != '"';)
1371590Srgrimes			if (*p++ == 0)
1381590Srgrimes				badfmt(fmt);
1391590Srgrimes		if (!(tfu->fmt = malloc(p - savep + 1)))
14080290Sobrien			err(1, NULL);
141194796Sdelphij		(void) strlcpy(tfu->fmt, savep, p - savep + 1);
1421590Srgrimes		escape(tfu->fmt);
1431590Srgrimes		p++;
1441590Srgrimes	}
1451590Srgrimes}
1461590Srgrimes
14791840Sobrienstatic const char *spec = ".#-+ 0123456789";
1481590Srgrimes
1491590Srgrimesint
150102944Sdwmalonesize(FS *fs)
1511590Srgrimes{
152102944Sdwmalone	FU *fu;
153102944Sdwmalone	int bcnt, cursize;
154102944Sdwmalone	unsigned char *fmt;
1551590Srgrimes	int prec;
1561590Srgrimes
1571590Srgrimes	/* figure out the data block size needed for each format unit */
1581590Srgrimes	for (cursize = 0, fu = fs->nextfu; fu; fu = fu->nextfu) {
1591590Srgrimes		if (fu->bcnt) {
1601590Srgrimes			cursize += fu->bcnt * fu->reps;
1611590Srgrimes			continue;
1621590Srgrimes		}
1631590Srgrimes		for (bcnt = prec = 0, fmt = fu->fmt; *fmt; ++fmt) {
1641590Srgrimes			if (*fmt != '%')
1651590Srgrimes				continue;
1661590Srgrimes			/*
1671590Srgrimes			 * skip any special chars -- save precision in
1681590Srgrimes			 * case it's a %s format.
1691590Srgrimes			 */
1701590Srgrimes			while (index(spec + 1, *++fmt));
1711590Srgrimes			if (*fmt == '.' && isdigit(*++fmt)) {
1721590Srgrimes				prec = atoi(fmt);
1731590Srgrimes				while (isdigit(*++fmt));
1741590Srgrimes			}
1751590Srgrimes			switch(*fmt) {
1761590Srgrimes			case 'c':
1771590Srgrimes				bcnt += 1;
1781590Srgrimes				break;
1791590Srgrimes			case 'd': case 'i': case 'o': case 'u':
1801590Srgrimes			case 'x': case 'X':
1811590Srgrimes				bcnt += 4;
1821590Srgrimes				break;
1831590Srgrimes			case 'e': case 'E': case 'f': case 'g': case 'G':
1841590Srgrimes				bcnt += 8;
1851590Srgrimes				break;
1861590Srgrimes			case 's':
1871590Srgrimes				bcnt += prec;
1881590Srgrimes				break;
1891590Srgrimes			case '_':
1901590Srgrimes				switch(*++fmt) {
1911590Srgrimes				case 'c': case 'p': case 'u':
1921590Srgrimes					bcnt += 1;
1931590Srgrimes					break;
1941590Srgrimes				}
1951590Srgrimes			}
1961590Srgrimes		}
1971590Srgrimes		cursize += bcnt * fu->reps;
1981590Srgrimes	}
1991590Srgrimes	return (cursize);
2001590Srgrimes}
2011590Srgrimes
2021590Srgrimesvoid
203102944Sdwmalonerewrite(FS *fs)
2041590Srgrimes{
2051590Srgrimes	enum { NOTOKAY, USEBCNT, USEPREC } sokay;
206102944Sdwmalone	PR *pr, **nextpr;
207102944Sdwmalone	FU *fu;
20830921Sache	unsigned char *p1, *p2, *fmtp;
20930921Sache	char savech, cs[3];
2101590Srgrimes	int nconv, prec;
211161132Smaxim	size_t len;
2121590Srgrimes
213132541Sjohan	prec = 0;
214132541Sjohan
2151590Srgrimes	for (fu = fs->nextfu; fu; fu = fu->nextfu) {
2161590Srgrimes		/*
2171590Srgrimes		 * Break each format unit into print units; each conversion
2181590Srgrimes		 * character gets its own.
2191590Srgrimes		 */
220262927Sbrueffer		nextpr = &fu->nextpr;
2211590Srgrimes		for (nconv = 0, fmtp = fu->fmt; *fmtp; nextpr = &pr->nextpr) {
22280290Sobrien			if ((pr = calloc(1, sizeof(PR))) == NULL)
22380290Sobrien				err(1, NULL);
224262927Sbrueffer			*nextpr = pr;
2251590Srgrimes
2261590Srgrimes			/* Skip preceding text and up to the next % sign. */
2271590Srgrimes			for (p1 = fmtp; *p1 && *p1 != '%'; ++p1);
2281590Srgrimes
2291590Srgrimes			/* Only text in the string. */
2301590Srgrimes			if (!*p1) {
2311590Srgrimes				pr->fmt = fmtp;
2321590Srgrimes				pr->flags = F_TEXT;
2331590Srgrimes				break;
2341590Srgrimes			}
2351590Srgrimes
2361590Srgrimes			/*
2371590Srgrimes			 * Get precision for %s -- if have a byte count, don't
2381590Srgrimes			 * need it.
2391590Srgrimes			 */
2401590Srgrimes			if (fu->bcnt) {
2411590Srgrimes				sokay = USEBCNT;
2421590Srgrimes				/* Skip to conversion character. */
2431590Srgrimes				for (++p1; index(spec, *p1); ++p1);
2441590Srgrimes			} else {
2451590Srgrimes				/* Skip any special chars, field width. */
2461590Srgrimes				while (index(spec + 1, *++p1));
2471590Srgrimes				if (*p1 == '.' && isdigit(*++p1)) {
2481590Srgrimes					sokay = USEPREC;
2491590Srgrimes					prec = atoi(p1);
2501590Srgrimes					while (isdigit(*++p1));
2511590Srgrimes				} else
2521590Srgrimes					sokay = NOTOKAY;
2531590Srgrimes			}
2541590Srgrimes
255231578Stijl			p2 = *p1 ? p1 + 1 : p1;	/* Set end pointer -- make sure
256231578Stijl						 * that it's non-NUL/-NULL first
257231578Stijl						 * though. */
2581590Srgrimes			cs[0] = *p1;		/* Set conversion string. */
2591590Srgrimes			cs[1] = '\0';
2601590Srgrimes
2611590Srgrimes			/*
2621590Srgrimes			 * Figure out the byte count for each conversion;
2631590Srgrimes			 * rewrite the format as necessary, set up blank-
2641590Srgrimes			 * padding for end of data.
2651590Srgrimes			 */
2661590Srgrimes			switch(cs[0]) {
2671590Srgrimes			case 'c':
2681590Srgrimes				pr->flags = F_CHAR;
2691590Srgrimes				switch(fu->bcnt) {
2701590Srgrimes				case 0: case 1:
2711590Srgrimes					pr->bcnt = 1;
2721590Srgrimes					break;
2731590Srgrimes				default:
2741590Srgrimes					p1[1] = '\0';
2751590Srgrimes					badcnt(p1);
2761590Srgrimes				}
2771590Srgrimes				break;
2781590Srgrimes			case 'd': case 'i':
2791590Srgrimes				pr->flags = F_INT;
2801590Srgrimes				goto isint;
2811590Srgrimes			case 'o': case 'u': case 'x': case 'X':
2821590Srgrimes				pr->flags = F_UINT;
2831590Srgrimesisint:				cs[2] = '\0';
2841590Srgrimes				cs[1] = cs[0];
2851590Srgrimes				cs[0] = 'q';
2861590Srgrimes				switch(fu->bcnt) {
2871590Srgrimes				case 0: case 4:
2881590Srgrimes					pr->bcnt = 4;
2891590Srgrimes					break;
2901590Srgrimes				case 1:
2911590Srgrimes					pr->bcnt = 1;
2921590Srgrimes					break;
2931590Srgrimes				case 2:
2941590Srgrimes					pr->bcnt = 2;
2951590Srgrimes					break;
2961590Srgrimes				default:
2971590Srgrimes					p1[1] = '\0';
2981590Srgrimes					badcnt(p1);
2991590Srgrimes				}
3001590Srgrimes				break;
3011590Srgrimes			case 'e': case 'E': case 'f': case 'g': case 'G':
3021590Srgrimes				pr->flags = F_DBL;
3031590Srgrimes				switch(fu->bcnt) {
3041590Srgrimes				case 0: case 8:
3051590Srgrimes					pr->bcnt = 8;
3061590Srgrimes					break;
3071590Srgrimes				case 4:
3081590Srgrimes					pr->bcnt = 4;
3091590Srgrimes					break;
3101590Srgrimes				default:
31196795Stjr					if (fu->bcnt == sizeof(long double)) {
31296795Stjr						cs[2] = '\0';
31396795Stjr						cs[1] = cs[0];
31496795Stjr						cs[0] = 'L';
31596795Stjr						pr->bcnt = sizeof(long double);
31696795Stjr					} else {
31796795Stjr						p1[1] = '\0';
31896795Stjr						badcnt(p1);
31996795Stjr					}
3201590Srgrimes				}
3211590Srgrimes				break;
3221590Srgrimes			case 's':
3231590Srgrimes				pr->flags = F_STR;
3241590Srgrimes				switch(sokay) {
3251590Srgrimes				case NOTOKAY:
3261590Srgrimes					badsfmt();
3271590Srgrimes				case USEBCNT:
3281590Srgrimes					pr->bcnt = fu->bcnt;
3291590Srgrimes					break;
3301590Srgrimes				case USEPREC:
3311590Srgrimes					pr->bcnt = prec;
3321590Srgrimes					break;
3331590Srgrimes				}
3341590Srgrimes				break;
3351590Srgrimes			case '_':
3361590Srgrimes				++p2;
3371590Srgrimes				switch(p1[1]) {
3381590Srgrimes				case 'A':
3391590Srgrimes					endfu = fu;
3401590Srgrimes					fu->flags |= F_IGNORE;
3411590Srgrimes					/* FALLTHROUGH */
3421590Srgrimes				case 'a':
3431590Srgrimes					pr->flags = F_ADDRESS;
3441590Srgrimes					++p2;
3451590Srgrimes					switch(p1[2]) {
3461590Srgrimes					case 'd': case 'o': case'x':
3471590Srgrimes						cs[0] = 'q';
3481590Srgrimes						cs[1] = p1[2];
3491590Srgrimes						cs[2] = '\0';
3501590Srgrimes						break;
3511590Srgrimes					default:
3521590Srgrimes						p1[3] = '\0';
3531590Srgrimes						badconv(p1);
3541590Srgrimes					}
3551590Srgrimes					break;
3561590Srgrimes				case 'c':
3571590Srgrimes					pr->flags = F_C;
3581590Srgrimes					/* cs[0] = 'c';	set in conv_c */
3591590Srgrimes					goto isint2;
3601590Srgrimes				case 'p':
3611590Srgrimes					pr->flags = F_P;
3621590Srgrimes					cs[0] = 'c';
3631590Srgrimes					goto isint2;
3641590Srgrimes				case 'u':
3651590Srgrimes					pr->flags = F_U;
3661590Srgrimes					/* cs[0] = 'c';	set in conv_u */
3671590Srgrimesisint2:					switch(fu->bcnt) {
3681590Srgrimes					case 0: case 1:
3691590Srgrimes						pr->bcnt = 1;
3701590Srgrimes						break;
3711590Srgrimes					default:
3721590Srgrimes						p1[2] = '\0';
3731590Srgrimes						badcnt(p1);
3741590Srgrimes					}
3751590Srgrimes					break;
3761590Srgrimes				default:
3771590Srgrimes					p1[2] = '\0';
3781590Srgrimes					badconv(p1);
3791590Srgrimes				}
3801590Srgrimes				break;
3811590Srgrimes			default:
3821590Srgrimes				p1[1] = '\0';
3831590Srgrimes				badconv(p1);
3841590Srgrimes			}
3851590Srgrimes
3861590Srgrimes			/*
3871590Srgrimes			 * Copy to PR format string, set conversion character
3881590Srgrimes			 * pointer, update original.
3891590Srgrimes			 */
3901590Srgrimes			savech = *p2;
3911590Srgrimes			p1[0] = '\0';
392161132Smaxim			len = strlen(fmtp) + strlen(cs) + 1;
393161132Smaxim			if ((pr->fmt = calloc(1, len)) == NULL)
39480290Sobrien				err(1, NULL);
395161132Smaxim			snprintf(pr->fmt, len, "%s%s", fmtp, cs);
3961590Srgrimes			*p2 = savech;
3971590Srgrimes			pr->cchar = pr->fmt + (p1 - fmtp);
3981590Srgrimes			fmtp = p2;
3991590Srgrimes
4001590Srgrimes			/* Only one conversion character if byte count. */
4011590Srgrimes			if (!(pr->flags&F_ADDRESS) && fu->bcnt && nconv++)
40227315Scharnier	    errx(1, "byte count with multiple conversion characters");
4031590Srgrimes		}
4041590Srgrimes		/*
4051590Srgrimes		 * If format unit byte count not specified, figure it out
4061590Srgrimes		 * so can adjust rep count later.
4071590Srgrimes		 */
4081590Srgrimes		if (!fu->bcnt)
4091590Srgrimes			for (pr = fu->nextpr; pr; pr = pr->nextpr)
4101590Srgrimes				fu->bcnt += pr->bcnt;
4111590Srgrimes	}
4121590Srgrimes	/*
4131590Srgrimes	 * If the format string interprets any data at all, and it's
4141590Srgrimes	 * not the same as the blocksize, and its last format unit
4151590Srgrimes	 * interprets any data at all, and has no iteration count,
4161590Srgrimes	 * repeat it as necessary.
4171590Srgrimes	 *
4181590Srgrimes	 * If, rep count is greater than 1, no trailing whitespace
4191590Srgrimes	 * gets output from the last iteration of the format unit.
4201590Srgrimes	 */
42197329Stjr	for (fu = fs->nextfu; fu; fu = fu->nextfu) {
4221590Srgrimes		if (!fu->nextfu && fs->bcnt < blocksize &&
4231590Srgrimes		    !(fu->flags&F_SETREP) && fu->bcnt)
4241590Srgrimes			fu->reps += (blocksize - fs->bcnt) / fu->bcnt;
4251590Srgrimes		if (fu->reps > 1) {
4261590Srgrimes			for (pr = fu->nextpr;; pr = pr->nextpr)
4271590Srgrimes				if (!pr->nextpr)
4281590Srgrimes					break;
4291590Srgrimes			for (p1 = pr->fmt, p2 = NULL; *p1; ++p1)
4301590Srgrimes				p2 = isspace(*p1) ? p1 : NULL;
4311590Srgrimes			if (p2)
4321590Srgrimes				pr->nospace = p2;
4331590Srgrimes		}
4341590Srgrimes	}
4351590Srgrimes#ifdef DEBUG
4361590Srgrimes	for (fu = fs->nextfu; fu; fu = fu->nextfu) {
4371590Srgrimes		(void)printf("fmt:");
4381590Srgrimes		for (pr = fu->nextpr; pr; pr = pr->nextpr)
4391590Srgrimes			(void)printf(" {%s}", pr->fmt);
4401590Srgrimes		(void)printf("\n");
4411590Srgrimes	}
4421590Srgrimes#endif
4431590Srgrimes}
4441590Srgrimes
4451590Srgrimesvoid
446102944Sdwmaloneescape(char *p1)
4471590Srgrimes{
448102944Sdwmalone	char *p2;
4491590Srgrimes
4501590Srgrimes	/* alphabetic escape sequences have to be done in place */
451231578Stijl	for (p2 = p1;; p1++, p2++) {
452231578Stijl		if (*p1 == '\\') {
453231578Stijl			p1++;
454231578Stijl			switch(*p1) {
455231578Stijl			case '\0':
456231578Stijl				*p2 = '\\';
457231578Stijl				*++p2 = '\0';
458231578Stijl				return;
4591590Srgrimes			case 'a':
4601590Srgrimes			     /* *p2 = '\a'; */
4611590Srgrimes				*p2 = '\007';
4621590Srgrimes				break;
4631590Srgrimes			case 'b':
4641590Srgrimes				*p2 = '\b';
4651590Srgrimes				break;
4661590Srgrimes			case 'f':
4671590Srgrimes				*p2 = '\f';
4681590Srgrimes				break;
4691590Srgrimes			case 'n':
4701590Srgrimes				*p2 = '\n';
4711590Srgrimes				break;
4721590Srgrimes			case 'r':
4731590Srgrimes				*p2 = '\r';
4741590Srgrimes				break;
4751590Srgrimes			case 't':
4761590Srgrimes				*p2 = '\t';
4771590Srgrimes				break;
4781590Srgrimes			case 'v':
4791590Srgrimes				*p2 = '\v';
4801590Srgrimes				break;
4811590Srgrimes			default:
4821590Srgrimes				*p2 = *p1;
4831590Srgrimes				break;
4841590Srgrimes			}
485231578Stijl		} else {
486231578Stijl			*p2 = *p1;
487231578Stijl			if (*p1 == '\0')
488231578Stijl				return;
489231578Stijl		}
4901590Srgrimes	}
4911590Srgrimes}
4921590Srgrimes
4931590Srgrimesvoid
494102944Sdwmalonebadcnt(char *s)
4951590Srgrimes{
49627315Scharnier	errx(1, "%s: bad byte count", s);
4971590Srgrimes}
4981590Srgrimes
4991590Srgrimesvoid
500102944Sdwmalonebadsfmt(void)
5011590Srgrimes{
50227315Scharnier	errx(1, "%%s: requires a precision or a byte count");
5031590Srgrimes}
5041590Srgrimes
5051590Srgrimesvoid
506102944Sdwmalonebadfmt(const char *fmt)
5071590Srgrimes{
50827315Scharnier	errx(1, "\"%s\": bad format", fmt);
5091590Srgrimes}
5101590Srgrimes
5111590Srgrimesvoid
512102944Sdwmalonebadconv(char *ch)
5131590Srgrimes{
51427315Scharnier	errx(1, "%%%s: bad conversion character", ch);
5151590Srgrimes}
516