blob: 8164c601cb3eafd01b88b1235d2a7d923f245788 [file] [log] [blame]
/*
* Copyright (c) 1989 The Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/* 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
* - added Native Language Support
*/
#include <sys/types.h>
#include <sys/file.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include "hexdump.h"
#include "nls.h"
static void escape(char *p1);
static void badcnt(const char *s);
static void badsfmt(void);
static void badfmt(const char *fmt);
static void badconv(const char *ch);
FU *endfu; /* format at end-of-data */
void addfile(char *name)
{
char *p;
FILE *fp;
int ch;
char buf[2048 + 1];
if ((fp = fopen(name, "r")) == NULL) {
(void)fprintf(stderr, _("hexdump: can't read %s.\n"), name);
exit(1);
}
while (fgets(buf, sizeof(buf), fp)) {
if ((p = strchr(buf, '\n')) == NULL) {
(void)fprintf(stderr, _("hexdump: line too long.\n"));
while ((ch = getchar()) != '\n' && ch != EOF);
continue;
}
*p = '\0';
for (p = buf; *p && isspace((unsigned char)*p); ++p);
if (!*p || *p == '#')
continue;
add(p);
}
(void)fclose(fp);
}
void add(const char *fmt)
{
const char *p;
static FS **nextfs;
FS *tfs;
FU *tfu, **nextfu;
const char *savep;
/* Start new linked list of format units. */
tfs = emalloc(sizeof(FS));
if (!fshead)
fshead = tfs;
else
*nextfs = tfs;
nextfs = &tfs->nextfs;
nextfu = &tfs->nextfu;
/* Take the format string and break it up into format units. */
for (p = fmt;;) {
/* Skip leading white space. */
for (; isspace((unsigned char)*p); ++p);
if (!*p)
break;
/* Allocate a new format unit and link it in. */
tfu = emalloc(sizeof(FU));
*nextfu = tfu;
nextfu = &tfu->nextfu;
tfu->reps = 1;
/* If leading digit, repetition count. */
if (isdigit((unsigned char)*p)) {
for (savep = p; isdigit((unsigned char)*p); ++p);
if (!isspace((unsigned char)*p) && *p != '/')
badfmt(fmt);
/* may overwrite either white space or slash */
tfu->reps = atoi(savep);
tfu->flags = F_SETREP;
/* skip trailing white space */
for (++p; isspace((unsigned char)*p); ++p);
}
/* Skip slash and trailing white space. */
if (*p == '/')
while (isspace((unsigned char)*++p));
/* byte count */
if (isdigit((unsigned char)*p)) {
for (savep = p; isdigit((unsigned char)*p); ++p);
if (!isspace((unsigned char)*p))
badfmt(fmt);
tfu->bcnt = atoi(savep);
/* skip trailing white space */
for (++p; isspace((unsigned char)*p); ++p);
}
/* format */
if (*p != '"')
badfmt(fmt);
for (savep = ++p; *p != '"';)
if (*p++ == 0)
badfmt(fmt);
if (!(tfu->fmt = malloc(p - savep + 1)))
nomem();
(void) strncpy(tfu->fmt, savep, p - savep);
tfu->fmt[p - savep] = '\0';
escape(tfu->fmt);
p++;
}
}
static const char *spec = ".#-+ 0123456789";
int size(FS *fs)
{
FU *fu;
int bcnt, cursize;
char *fmt;
int prec;
/* figure out the data block size needed for each format unit */
for (cursize = 0, fu = fs->nextfu; fu; fu = fu->nextfu) {
if (fu->bcnt) {
cursize += fu->bcnt * fu->reps;
continue;
}
for (bcnt = prec = 0, fmt = fu->fmt; *fmt; ++fmt) {
if (*fmt != '%')
continue;
/*
* skip any special chars -- save precision in
* case it's a %s format.
*/
while (strchr(spec + 1, *++fmt));
if (*fmt == '.' && isdigit((unsigned char)*++fmt)) {
prec = atoi(fmt);
while (isdigit((unsigned char)*++fmt));
}
switch(*fmt) {
case 'c':
bcnt += 1;
break;
case 'd': case 'i': case 'o': case 'u':
case 'x': case 'X':
bcnt += 4;
break;
case 'e': case 'E': case 'f': case 'g': case 'G':
bcnt += 8;
break;
case 's':
bcnt += prec;
break;
case '_':
switch(*++fmt) {
case 'c': case 'p': case 'u':
bcnt += 1;
break;
}
}
}
cursize += bcnt * fu->reps;
}
return(cursize);
}
void rewrite(FS *fs)
{
enum { NOTOKAY, USEBCNT, USEPREC } sokay;
PR *pr, **nextpr;
FU *fu;
char *p1, *p2;
char savech, *fmtp, cs[3];
int nconv, prec;
nextpr = NULL;
prec = 0;
for (fu = fs->nextfu; fu; fu = fu->nextfu) {
/*
* Break each format unit into print units; each
* conversion character gets its own.
*/
for (nconv = 0, fmtp = fu->fmt; *fmtp; nextpr = &pr->nextpr) {
pr = emalloc(sizeof(PR));
if (!fu->nextpr)
fu->nextpr = pr;
else
*nextpr = pr;
/* Skip preceding text and up to the next % sign. */
for (p1 = fmtp; *p1 && *p1 != '%'; ++p1);
/* Only text in the string. */
if (!*p1) {
pr->fmt = fmtp;
pr->flags = F_TEXT;
break;
}
/*
* Get precision for %s -- if have a byte count, don't
* need it.
*/
if (fu->bcnt) {
sokay = USEBCNT;
/* skip to conversion character */
for (++p1; strchr(spec, *p1); ++p1);
} else {
/* skip any special chars, field width */
while (strchr(spec + 1, *++p1));
if (*p1 == '.' &&
isdigit((unsigned char)*++p1)) {
sokay = USEPREC;
prec = atoi(p1);
while (isdigit((unsigned char)*++p1));
} else
sokay = NOTOKAY;
}
p2 = p1 + 1; /* Set end pointer. */
cs[0] = *p1; /* Set conversion string. */
cs[1] = 0;
/*
* Figure out the byte count for each conversion;
* rewrite the format as necessary, set up blank-
* padding for end of data.
*/
switch(cs[0]) {
case 'c':
pr->flags = F_CHAR;
switch(fu->bcnt) {
case 0: case 1:
pr->bcnt = 1;
break;
default:
p1[1] = '\0';
badcnt(p1);
}
break;
case 'd': case 'i':
pr->flags = F_INT;
goto isint;
case 'o': case 'u': case 'x': case 'X':
pr->flags = F_UINT;
isint: cs[2] = '\0';
cs[1] = cs[0];
cs[0] = 'q';
switch(fu->bcnt) {
case 0: case 4:
pr->bcnt = 4;
break;
case 1:
pr->bcnt = 1;
break;
case 2:
pr->bcnt = 2;
break;
case 8:
pr->bcnt = 8;
break;
default:
p1[1] = '\0';
badcnt(p1);
}
break;
case 'e': case 'E': case 'f': case 'g': case 'G':
pr->flags = F_DBL;
switch(fu->bcnt) {
case 0: case 8:
pr->bcnt = 8;
break;
case 4:
pr->bcnt = 4;
break;
default:
p1[1] = '\0';
badcnt(p1);
}
break;
case 's':
pr->flags = F_STR;
switch(sokay) {
case NOTOKAY:
badsfmt();
case USEBCNT:
pr->bcnt = fu->bcnt;
break;
case USEPREC:
pr->bcnt = prec;
break;
}
break;
case '_':
++p2;
switch(p1[1]) {
case 'A':
endfu = fu;
fu->flags |= F_IGNORE;
/* FALLTHROUGH */
case 'a':
pr->flags = F_ADDRESS;
++p2;
switch(p1[2]) {
case 'd': case 'o': case'x':
cs[0] = 'q';
cs[1] = p1[2];
cs[2] = '\0';
break;
default:
p1[3] = '\0';
badconv(p1);
}
break;
case 'c':
pr->flags = F_C;
/* cs[0] = 'c'; set in conv_c */
goto isint2;
case 'p':
pr->flags = F_P;
cs[0] = 'c';
goto isint2;
case 'u':
pr->flags = F_U;
/* cs[0] = 'c'; set in conv_u */
isint2: switch(fu->bcnt) {
case 0: case 1:
pr->bcnt = 1;
break;
default:
p1[2] = '\0';
badcnt(p1);
}
break;
default:
p1[2] = '\0';
badconv(p1);
}
break;
default:
p1[1] = '\0';
badconv(p1);
}
/*
* Copy to PR format string, set conversion character
* pointer, update original.
*/
savech = *p2;
p1[0] = '\0';
pr->fmt = emalloc(strlen(fmtp) + strlen(cs) + 1);
(void)strcpy(pr->fmt, fmtp);
(void)strcat(pr->fmt, cs);
*p2 = savech;
pr->cchar = pr->fmt + (p1 - fmtp);
fmtp = p2;
/* Only one conversion character if byte count */
if (!(pr->flags&F_ADDRESS) && fu->bcnt && nconv++) {
(void)fprintf(stderr,
_("hexdump: byte count with multiple conversion characters.\n"));
exit(1);
}
}
/*
* If format unit byte count not specified, figure it out
* so can adjust rep count later.
*/
if (!fu->bcnt)
for (pr = fu->nextpr; pr; pr = pr->nextpr)
fu->bcnt += pr->bcnt;
}
/*
* If the format string interprets any data at all, and it's
* not the same as the blocksize, and its last format unit
* interprets any data at all, and has no iteration count,
* repeat it as necessary.
*
* If rep count is greater than 1, no trailing whitespace
* gets output from the last iteration of the format unit.
*/
for (fu = fs->nextfu; fu; fu = fu->nextfu) {
if (!fu->nextfu && fs->bcnt < blocksize &&
!(fu->flags&F_SETREP) && fu->bcnt)
fu->reps += (blocksize - fs->bcnt) / fu->bcnt;
if (fu->reps > 1) {
for (pr = fu->nextpr;; pr = pr->nextpr)
if (!pr->nextpr)
break;
for (p1 = pr->fmt, p2 = NULL; *p1; ++p1)
p2 = isspace((unsigned char)*p1) ? p1 : NULL;
if (p2)
pr->nospace = p2;
}
}
}
static void escape(char *p1)
{
char *p2;
/* alphabetic escape sequences have to be done in place */
for (p2 = p1;; ++p1, ++p2) {
if (!*p1) {
*p2 = *p1;
break;
}
if (*p1 == '\\')
switch(*++p1) {
case 'a':
/* *p2 = '\a'; */
*p2 = '\007';
break;
case 'b':
*p2 = '\b';
break;
case 'f':
*p2 = '\f';
break;
case 'n':
*p2 = '\n';
break;
case 'r':
*p2 = '\r';
break;
case 't':
*p2 = '\t';
break;
case 'v':
*p2 = '\v';
break;
default:
*p2 = *p1;
break;
}
}
}
static void badcnt(const char *s)
{
(void)fprintf(stderr,
_("hexdump: bad byte count for conversion character %s.\n"), s);
exit(1);
}
static void badsfmt(void)
{
(void)fprintf(stderr,
_("hexdump: %%s requires a precision or a byte count.\n"));
exit(1);
}
static void badfmt(const char *fmt)
{
(void)fprintf(stderr, _("hexdump: bad format {%s}\n"), fmt);
exit(1);
}
static void badconv(const char *ch)
{
(void)fprintf(stderr, _("hexdump: bad conversion character %%%s.\n"), ch);
exit(1);
}