Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 02 Mar 2001 09:49:17 +0000
From:      Tony Finch <dot@dotat.at>
To:        FreeBSD-gnats-submit@freebsd.org
Subject:   ports/25490: [PATCH] fix various bugs in stat(1)
Message-ID:  <E14YmBZ-0000cI-00@hand.dotat.at>

next in thread | raw e-mail | index | archive | help

>Number:         25490
>Category:       ports
>Synopsis:       [PATCH] fix various bugs in stat(1)
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-ports
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Fri Mar 02 01:50:01 PST 2001
>Closed-Date:
>Last-Modified:
>Originator:     Tony Finch <dot@dotat.at>
>Release:        FreeBSD 4.2-STABLE i386
>Organization:
Covalent Technologies, Inc.
>Environment:

FreeBSD hand.dotat.at 4.2-STABLE FreeBSD 4.2-STABLE #4: Wed Feb 21 00:26:12 GMT 2001     fanf@hand.dotat.at:/FreeBSD/obj/FreeBSD/releng4/sys/DELL-Latitude-CSx  i386

stat-1.3            Print inode contents

>Description:

The program is chock-a-block with buffer overflows.
It prints the symbolic mode incorrectly in a few cases.
It handles backslashes in a bogus fashion.

>How-To-Repeat:

fanf@hand.dotat.at:/FreeBSD/ports/sysutils/stat
:; stat -f `perl -e 'print "."x1028'` .
  File: "."
....................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
Segmentation fault (core dumped)
fanf@hand.dotat.at:/FreeBSD/ports/sysutils/stat
:; touch it
fanf@hand.dotat.at:/FreeBSD/ports/sysutils/stat
:; chmod +s it
fanf@hand.dotat.at:/FreeBSD/ports/sysutils/stat
:; ll it
-rwSr-Sr--   1 fanf     fanf            0 Mar  2 09:31 it
fanf@hand.dotat.at:/FreeBSD/ports/sysutils/stat
:; stat it
  File: "it"
  Size: 0       Allocated Blocks: 0     Filetype: Regular File
  Mode: (6644/-rw-s-sr--)       Uid: (1014/fanf) Gid: (1014/fanf)
Device: 160771  Inode: 435037   Links: 1
Access: Fri Mar  2 09:31:21 2001
Modify: Fri Mar  2 09:31:21 2001
Change: Fri Mar  2 09:31:32 2001
fanf@hand.dotat.at:/FreeBSD/ports/sysutils/stat
:; stat -f '%f\n' .
  File: "."
Directoryn


>Fix:

In adittion to fixing the above bugs, this also extends the format
syntax so that you can specify in more detail how the filename,
symlink target, device major and minor numbers, and times are
printed.

--- stat.fmt.c.orig	Sun Feb 16 13:12:33 1997
+++ stat.fmt.c	Fri Mar  2 09:22:21 2001
@@ -1,7 +1,7 @@
 /* $Id$ */
 #ifndef lint
-static char *sccsid = "@(#)stat.c	1.3 (UKC) 17/10/85";
-#endif  lint
+static const char *sccsid = "@(#)stat.c	1.3 (UKC) 17/10/85";
+#endif /* lint */
 /***
 
 * program name:
@@ -11,28 +11,16 @@
 * switches:
 	-f fmt	Display the i-node according to the format characters
 		in "fmt".  Syntax is like that of printf(3s) with the
-		following formatting characters:-
-		%f		File type.
-		%d		Device inode lives on.
-		%i		Inode itself.
-		%p		Protection bits.
-		%l		# of links.
-		%u		The user id
-		%g		The group id.
-		%t		Type of device.
-		%s		Total size of file.
-		%a		Access time.
-		%m		Modified time.
-		%c		Status change time.
-		%b		# of blocks allocated.
+		formatting characters as encoded in FmtTbl below.
 * libraries used:
-	standard + getopt
+	libc
 * compile time parameters:
-	cc -O -o stat stat.c -lgetopt
+	cc -O -o stat stat.c
 * history:
 	pur-ee!pucc-j!rsk, rsk@purdue-asc.arpa
 	Original version by Hoch in the dark days of v6;
 	this one by Rsk for 4.2 BSD.
+	fix-ups by fanf in 2001
 
 ***/
 
@@ -40,468 +28,531 @@
 		 *	System - wide header files
 		 */
 
-#include	<stdio.h>
-#include	<sys/time.h>
 #include	<sys/types.h>
-#include	<ctype.h>
 #include	<sys/stat.h>
-#include	<pwd.h>
+#include	<sys/time.h>
+
+#include	<ctype.h>
+#include	<errno.h>
 #include	<grp.h>
-#include        <stdlib.h>
-#include        <unistd.h>
+#include	<pwd.h>
+#include	<stdio.h>
+#include	<stdlib.h>
+#include	<string.h>
+#include	<unistd.h>
 
-			/*
-			 *	Global structures and definitions
-			 */
+		/*
+		 *	Global structures and definitions
+		 */
 
 
 #define	FAIL	-1		/* Failure return code from call */	
 #define OKAY	0		/* Success return code from call */
 
-char	*fmt_str;		/* Format string for displaying i-nodes */
 char	*progname;		/* Name we're called as */
 
-char *fmt_app __P((char *, char *));
-void FmtInode __P((char *, char*, struct stat *));
-void Usage __P((void));
 
+struct mystat {
+	char		*filename;
+	char		*linkname;
+	struct stat	st;
+};
 
-/*
-**  STAT_IT -- Display an i-node.
-**
-**	Print the contents of an i-node.
-**
-**	Parameters:
-**		ip    --  Reference to a stat structure.
-**
-**	Returns:
-**		None.
-**
-**	Notes:
-**		None.
-**
-*/
-
-int
-stat_it(filename)
-char	*filename;
-{
-	int		count;
-	char		buf [BUFSIZ];
-	struct 	stat	Sbuf;		/* for return values from stat() call */
-#define	LBUFSIZ		256	/* Length of symbolic link translation buffer */
-	char		Lbuf [LBUFSIZ];	/* Symbolic link translation buffer */
-
-	if( lstat(filename,&Sbuf) == FAIL) {
-		fprintf(stderr,"Can't lstat %s\n",filename); 
-		return(FAIL);
-	}
-
-	if( (Sbuf.st_mode & S_IFMT) == S_IFLNK) {
-		if( (count = readlink(filename, Lbuf, sizeof Lbuf)) == FAIL) {
-			fprintf(stderr, "Can't readlink %s\n", filename);
-			return(FAIL);
-		}
-		if( count < LBUFSIZ)
-			Lbuf[count] = '\0';
-		printf("  File: \"%s\" -> \"%s\"\n", filename, Lbuf);
-	} else
-		printf("  File: \"%s\"\n", filename);
+typedef size_t FmtFunc __P((char *, size_t, char *, struct mystat *));
 
+size_t FmtInode __P((char *, size_t, char *, struct mystat *));
 
-	FmtInode(buf, fmt_str, &Sbuf);
 
-	printf("%s\n", buf);
-
-	return(OKAY);
-}
-
-/*
-**  UTOS  -- Uid to string mapping
-**
-**	Return the login of the given
-**	uid.
-**
-**	Parameters:
-**		buf    --  Buffer to fill,
-**		ip     --  Reference to a stat structure.
-**
-**	Returns:
-**		None.
-**
-**	Notes:
-**		None.
-**
+
+/*
+** UidToS
 */
-
-void
-UtoS(buf, ip)
+size_t
+UidToS(buf, len, fmt, ip)
 char		buf [];
-struct stat	*ip;
+size_t		len;
+char		*fmt;
+struct mystat	*ip;
 {
 	struct passwd	*pwent;
-	extern struct passwd	*getpwuid();
 
 	(void) setpwent();
-	if ((pwent = getpwuid(ip -> st_uid)) == NULL) {
-		fprintf(stderr, "getpwuid() failed\n");
-		exit(1);
-	}
-	(void) sprintf(buf, "%lu/%s",
-		ip -> st_uid,
-		pwent->pw_name);
+	if( (pwent = getpwuid(ip -> st.st_uid)) == NULL)
+		return snprintf(buf, len, "%lu",
+			(unsigned long) ip -> st.st_uid);
+	else 
+		return snprintf(buf, len, "%lu/%s",
+			(unsigned long) ip -> st.st_gid,
+			pwent -> pw_name);
 }
-/*
-**  GTOS -- Group to string mapping
-**
-**	Given a numeric group-id, return
-**	the ASCII name of that group.
-**
-**	Parameters:
-**		buf    --  Buffer to fill,
-**		ip      --  Reference to a stat structure.
-**
-**	Returns:
-**		None.
-**
-**	Notes:
-**		None.
-**
+
+/*
+** GidToS
 */
-
-void
-GtoS(buf, ip)
+size_t
+GidToS(buf, len, fmt, ip)
 char		buf [];
-struct stat	*ip;
+size_t		len;
+char		*fmt;
+struct mystat	*ip;
 {
 	struct group	*grent;
-	extern struct group	*getgrid();
 
 	(void) setgrent();
-	if( (grent = getgrgid(ip -> st_gid)) == NULL) {
-		fprintf(stderr, "getgrgid(%lu) failed\n", ip -> st_gid);
-		exit(1);
-	}
-	(void) sprintf(buf, "%lu/%s",
-		ip -> st_gid,
-		grent -> gr_name);
+	if( (grent = getgrgid(ip -> st.st_gid)) == NULL)
+		return snprintf(buf, len, "%lu",
+			(unsigned long) ip -> st.st_gid);
+	else 
+		return snprintf(buf, len, "%lu/%s",
+			(unsigned long) ip -> st.st_gid,
+			grent -> gr_name);
 }
-
-/*
-** DEVTOS --  Device to string mapping
-**
-**	Return a string containing the type
-**	of the device, if the file is a device.
-**	Else return a null string.
-**
-**	Parameters:
-**		buf    --  Buffer to fill,
-**		ip      --  Reference to a stat structure.
-**
-**	Returns:
-**		None.
-**
-**	Notes:
-**		None.
-**
+
+/*
+** RDevToS
 */
-
-void
-DevToS(buf, ip)
+size_t
+RDevToS(buf, len, fmt, ip)
 char		buf [];
-struct stat	*ip;
+size_t		len;
+char		*fmt;
+struct mystat	*ip;
 {
-	if ((ip -> st_mode & S_IFMT) == S_IFCHR
-	|| (ip -> st_mode & S_IFMT) == S_IFBLK)
-		sprintf(buf, "Device Type: %lu", ip -> st_rdev);
+	if( !S_ISCHR(ip -> st.st_mode) && !S_ISBLK(ip -> st.st_mode))
+		return 0;
+	else if( fmt != NULL)
+		return FmtInode(buf, len, fmt, ip);
+	else if( minor(ip -> st.st_rdev) > 255
+		|| minor(ip -> st.st_rdev) < 0)
+		return snprintf(buf, len, "%3d,0x%08x",
+			major(ip -> st.st_rdev),
+			minor(ip -> st.st_rdev));
 	else
-		strcpy(buf, "");
+		return snprintf(buf, len, "%3d,%3d",
+			major(ip -> st.st_rdev),
+			minor(ip -> st.st_rdev));
 }
-/*
-**  MODETOS -- Mode word to string mapping
-**
-**	Display the contents of the mode field
-**	in a readable format.
-**
-**	Parameters:
-**		buf    --  Buffer to write on,
-**		mode   --  Mode field to interpret.
-**
-**	Returns:
-**		None.
-**
-**	Notes:
-**		None.
-**
+
+/*
+** ModeToS
 */
-
-void
-ModeToS(buf, ip)
+size_t
+ModeToS(buf, len, fmt, ip)
 char		buf [];
-struct stat	*ip;
+size_t		len;
+char		*fmt;
+struct mystat	*ip;
 {
-	sprintf(buf, "%04o/----------", ip -> st_mode & 07777);
+	char	mode[12];
 
-	if(ip -> st_mode & 0000001)	/* Other execute */
-		buf[5 + 9] = 'x';
-	if(ip -> st_mode & 0000002)	/* Other write */
-		buf[5 + 8] = 'w';
-	if(ip -> st_mode & 0000004)	/* Other read */
-		buf[5 + 7] = 'r';
-	if(ip -> st_mode & 0000010)	/* Group execute */
-		buf[5 + 6] = 'x';
-	if(ip -> st_mode & 0000020)	/* Group write */
-		buf[5 + 5] = 'w';
-	if(ip -> st_mode & 0000040)	/* Group read */
-		buf[5 + 4] = 'r';
-	if(ip -> st_mode & 0000100)	/* User execute */
-		buf[5 + 3] = 'x';
-	if(ip -> st_mode & 0000200)	/* User write */
-		buf[5 + 2] = 'w';
-	if(ip -> st_mode & 0000400)	/* User read */
-		buf[5 + 1] = 'r';
-	if(ip -> st_mode & 0001000)	/* Sticky bit */
-		buf[5 + 9] = 't';
-	if(ip -> st_mode & 0002000)	/* Set group id */
-		buf[5 + 6] = 's';
-	if(ip -> st_mode & 0004000)	/* Set user id */
-		buf[5 + 4] = 's';
-	switch( ip -> st_mode & S_IFMT) {
-		case	S_IFDIR:	buf[5 + 0] = 'd';
-					break;
-		case	S_IFCHR:	buf[5 + 0] = 'c';
-					break;
-		case	S_IFBLK:	buf[5 + 0] = 'b';
-					break;
-		case	S_IFREG:	buf[5 + 0] = '-';
-					break;
-		case	S_IFLNK:	buf[5 + 0] = 'l';
-					break;
-		case	S_IFSOCK:	buf[5 + 0] = 's';
-					break;
-		default		:	buf[5 + 0] = '?';
-	}
+	strmode(ip -> st.st_mode, mode);
+	if( mode[10] == ' ')
+		mode[10] = '\0';
+	return snprintf(buf, len, "%04o/%s",
+		(int)(ip -> st.st_mode & ~S_IFMT), mode);
 }
-/*
-**  TYPETOS -- Filetype to string mapping
-**
-**	Given a filetype, return a string
-**	representation of that type.
-**
-**	Parameters:
-**		buf    --  Buffer to fill,
-**		ip     --  Reference to a stat structure.
-**
-**	Returns:
-**		None.
-**
-**	Notes:
-**		None.
-**
+
+/*
+** TypeToS
 */
-
-void
-TypeToS(buf, ip)
+size_t
+TypeToS(buf, len, fmt, ip)
 char		buf [];
-struct stat	*ip;
+size_t		len;
+char		*fmt;
+struct mystat	*ip;
 {
-	switch( ip -> st_mode & S_IFMT ) {
+	switch( ip -> st.st_mode & S_IFMT ) {
 		case	S_IFDIR:
-			strcpy(buf, "Directory");
-			break;
+			return strlcpy(buf, "Directory", len);
 
 		case	S_IFCHR:
-			strcpy(buf, "Character Device");
-			break;
+			return strlcpy(buf, "Character Device", len);
 
 		case	S_IFBLK:
-			strcpy(buf, "Block Device");
-			break;
+			return strlcpy(buf, "Block Device", len);
 
 		case	S_IFREG:
-			strcpy(buf, "Regular File");
-			break;
+			return strlcpy(buf, "Regular File", len);
 
 		case	S_IFLNK:
-			strcpy(buf, "Symbolic Link");
-			break;
+			return strlcpy(buf, "Symbolic Link", len);
 
 		case	S_IFSOCK:
-			strcpy(buf, "Socket");
-			break;
+			return strlcpy(buf, "Socket", len);
 
 		default:
-			strcpy(buf, "Unknown");
-			break;
+			return strlcpy(buf, "Unknown", len);
 	}
 }
-/*
-**  FMT_APP -- Append a format string
-**
-**	Copy the first string argument
-**	to the second, returning a pointer
-**	to the next free byte.
-**
-**	Parameters:
-**		src  --  Source string,
-**		dest --  Destination string.
-**
-**	Returns:
-**		Reference to the next free byte
-**		in the destination string.
-**
-**	Notes:
-**		This routine is auxiliary to
-**		FmtInode() below.
-**
+
+/*
+** FileToS
 */
-
-char	*
-fmt_app(src, dest)
-register char	*src,
-		*dest;
+size_t
+FileToS(buf, len, fmt, ip)
+char		buf [];
+size_t		len;
+char		*fmt;
+struct mystat	*ip;
 {
-
-	while ((*dest++ = *src++) != '\0')
-		;	/* NULL BODY */
-
-	return dest - 1;		/* Back over the NUL byte */
+	return strlcpy(buf, ip -> filename, len);
 }
+
+/*
+** LinkToS
+*/
+size_t
+LinkToS(buf, len, fmt, ip)
+char		buf [];
+size_t		len;
+char		*fmt;
+struct mystat	*ip;
+{
+	if( ip -> linkname == NULL)
+		return 0;
+	else if( fmt != NULL)
+		return FmtInode(buf, len, fmt, ip);
+	else
+ 		return strlcpy(buf, ip -> linkname, len);
+}
+
+/*
+** QtoS
+*/
+size_t
+QtoS(buf, len, q)
+char		buf [];
+size_t		len;
+quad_t		q;
+{
+	return snprintf(buf, len, "%qd", q);
+}
+
+/*
+** InoToS
+*/
+size_t
+InoToS(buf, len, fmt, ip)
+char		buf [];
+size_t		len;
+char		*fmt;
+struct mystat	*ip;
+{
+	return QtoS(buf, len, (quad_t) ip -> st.st_ino);
+}
+
+/*
+** DevToS
+*/
+size_t
+DevToS(buf, len, fmt, ip)
+char		buf [];
+size_t		len;
+char		*fmt;
+struct mystat	*ip;
+{
+	return QtoS(buf, len, (quad_t) ip -> st.st_dev);
+}
+
+/*
+** NLinkToS
+*/
+size_t
+NLinkToS(buf, len, fmt, ip)
+char		buf [];
+size_t		len;
+char		*fmt;
+struct mystat	*ip;
+{
+	return QtoS(buf, len, (quad_t) ip -> st.st_nlink);
+}
+
+/*
+** SizeToS
+*/
+size_t
+SizeToS(buf, len, fmt, ip)
+char		buf [];
+size_t		len;
+char		*fmt;
+struct mystat	*ip;
+{
+	return QtoS(buf, len, (quad_t) ip -> st.st_size);
+}
+
+/*
+** BlocksToS
+*/
+size_t
+BlocksToS(buf, len, fmt, ip)
+char		buf [];
+size_t		len;
+char		*fmt;
+struct mystat	*ip;
+{
+	return QtoS(buf, len, (quad_t) ip -> st.st_blocks);
+}
+
+/*
+** TimeToS
+*/
+size_t
+TimeToS(buf, len, fmt, t)
+char		buf [];
+size_t		len;
+char		*fmt;
+time_t		t;
+{
+	return strftime(buf, len, fmt ? fmt : "%c", localtime(&t));
+}
+
+/*
+** ATimeToS
+*/
+size_t
+ATimeToS(buf, len, fmt, ip)
+char		buf [];
+size_t		len;
+char		*fmt;
+struct mystat	*ip;
+{
+	return TimeToS(buf, len, fmt, ip -> st.st_atime);
+}
+
+/*
+** CTimeToS
+*/
+size_t
+CTimeToS(buf, len, fmt, ip)
+char		buf [];
+size_t		len;
+char		*fmt;
+struct mystat	*ip;
+{
+	return TimeToS(buf, len, fmt, ip -> st.st_ctime);
+}
+
+/*
+** MTimeToS
+*/
+size_t
+MTimeToS(buf, len, fmt, ip)
+char		buf [];
+size_t		len;
+char		*fmt;
+struct mystat	*ip;
+{
+	return TimeToS(buf, len, fmt, ip -> st.st_mtime);
+}
+
+/*
+** Formatting table for the above functions
+*/
+FmtFunc *FmtTbl[] = {
+	/* a */ ATimeToS,
+	/* b */ BlocksToS,
+	/* c */ CTimeToS,
+	/* d */ DevToS,
+	/* e */ NULL,
+	/* f */ TypeToS,
+	/* g */ GidToS,
+	/* h */ NULL,
+	/* i */ InoToS,
+	/* j */ NULL,
+	/* k */ NULL,
+	/* l */ NLinkToS,
+	/* m */ MTimeToS,
+	/* n */ FileToS,
+	/* o */ NULL,
+	/* p */ ModeToS,
+	/* q */ NULL,
+	/* r */ LinkToS,
+	/* s */ SizeToS,
+	/* t */ RDevToS,
+	/* u */ UidToS,
+	/* v */ NULL,
+	/* w */ NULL,
+	/* x */ NULL,
+	/* y */ NULL,
+	/* z */ NULL,
+};
+
+/*
+**  Backslash
+*/
+int
+Backslash(buf, fmt)
+char	*buf;
+char	*fmt;
+{
+	int c, n;
 
-
-/*
-**  FMTINODE -- Format an Inode
+	switch (*fmt) {
+		case '0':
+		case '1':
+		case '2':
+		case '3':
+		case '4':
+		case '5':
+		case '6':
+		case '7':
+			for(n = c = 0; *fmt >= 0 && *fmt <= 7; fmt++, n++)
+				c = c * 8 + *fmt - '0';
+			*buf = c;
+			return n;
+		case 'a':
+			*buf++ = '\a';
+			return 1;
+		case 'b':
+			*buf++ = '\b';
+			return 1;
+		case 'e':
+			*buf++ = '\033';
+			return 1;
+		case 'f':
+			*buf++ = '\f';
+			return 1;
+		case 'n':
+			*buf++ = '\n';
+			return 1;
+		case 'r':
+			*buf++ = '\r';
+			return 1;
+		case 't':
+			*buf++ = '\t';
+			return 1;
+		case 'v':
+			*buf++ = '\v';
+			return 1;
+		default:
+			*buf++ = *fmt;
+			return 1;
+	}
+}
+
+/*
+**  FmtInode
+*/
+size_t
+FmtInode(buf, len, fmt, ip)
+char		*buf;
+size_t		len;
+char		*fmt;
+struct mystat	*ip;
+{
+	FmtFunc		*f;
+	char		*p;
+	size_t 		count, n;
+
+	count = 0;
+	for( n = strcspn(fmt, "\\%"); fmt[n]; n = strcspn(fmt, "\\%")) {
+		strlcpy(buf, fmt, n < len ? n+1 : len);
+		count += n;
+		buf += n;
+		if( n < len)
+			len -= n;
+		else
+			len = 0;
+		p = fmt += n + 1;
+		if( p[-1] == '\\') {
+			if( len > 0)
+				fmt += Backslash(buf, fmt);
+			 n = 1;
+		} else if( islower(*p)) {
+			f = FmtTbl[*p - 'a'];
+			if( f == NULL)
+				continue;
+			n = f(buf, len, NULL, ip);
+		} else if( *p == '{') {
+			p = strchr(p, '}');
+			if( p == NULL)
+				continue;
+			if( !islower(*++p))
+				continue;
+			f = FmtTbl[*p - 'a'];
+			if( f == NULL)
+				continue;
+			p[-1] = '\0';
+			n = f(buf, len, fmt+1, ip);
+			p[-1] = '}';
+		} else {
+			continue;
+		}
+		count += n;
+		buf += n;
+		if( n < len)
+			len -= n;
+		else
+			len = 0;
+		fmt = p + 1;
+	}
+	count += strlcpy(buf, fmt, len);
+	return count;
+}
+
+/*
+**  STAT_IT -- Display an i-node.
 **
-**	Build a textual representation of
-**	the Inode into the specified buffer
-**	according to the printf-like format
-**	string.
+**	Print the contents of an i-node.
 **
 **	Parameters:
-**		buf  --  Buffer to fill,
-**		fmt  --  Printf-like format string.
-**		rp   --  Reference to Inode cell,
+**		filename  --  Name of the file.
 **
 **	Returns:
 **		None.
 **
-**
 **	Notes:
-**		The format characters recognised here are:
-**
-**	%f		File type.
-**	%d		Device inode lives on.
-**	%i		Inode itself.
-**	%p		Protection bits.
-**	%l		# of links.
-**	%u		The user id
-**	%g		The group id.
-**	%t		Type of device.
-**	%s		Total size of file.
-**	%a		Access time.
-**	%m		Modified time.
-**	%c		Status change time.
-**	%b		# of blocks allocated.
-**
-**	Use a backslash to 'hide' any special character, e.g. '%'
-**
-**	The default format string is:
+**		None.
 **
-**		"%r,,%u,%t,%o"
 */
 
-void
-FmtInode(buf, fmt, ip)
-char		*buf,
-		*fmt;
-struct stat	*ip;
-{
-	char		auxbuf [64];
-	register char	*bufp;
-	register int	c;
-	extern char	*ctime();
-
-	for (bufp = buf; (c = *fmt++) != '\0'; ) {
-		if (c == '\\')
-			c = *fmt++;
-		
-		if (c != '%') {
-			*bufp++ = c;
-		} else {
-			switch (c = *fmt++) {
-				case 'f':	/* Filetype */
-					TypeToS(auxbuf, ip);
-					bufp = fmt_app(auxbuf, bufp);
-					break;
-
-				case 'i':	/* The actual Inode	*/
-					sprintf(auxbuf, "%ld", ip -> st_ino);
-					bufp = fmt_app(auxbuf, bufp);
-					break;
-
-				case 'd':	/* The Device	*/
-					sprintf(auxbuf, "%ld", ip -> st_dev);
-					bufp = fmt_app(auxbuf, bufp);
-					break;
-
-				case 't':	/* Device Type */
-					DevToS(auxbuf, ip);
-					bufp = fmt_app(auxbuf, bufp);
-					break;
-
-				case 'p':	/* Protection bits */
-					ModeToS(auxbuf, ip);
-					bufp = fmt_app(auxbuf, bufp);
-					break;
-
-				case 'l':	/* # of links */
-					sprintf(auxbuf, "%d", ip -> st_nlink);
-					bufp = fmt_app(auxbuf, bufp);
-					break;
-
-				case 'u':	/* User id */
-					UtoS(auxbuf, ip);
-					bufp = fmt_app(auxbuf, bufp);
-					break;
-
-				case 'g':	/* Group id */
-					GtoS(auxbuf, ip);
-					bufp = fmt_app(auxbuf, bufp);
-					break;
-
-				case 's':	/* Total size of file */
-					sprintf(auxbuf, "%qd", ip -> st_size);
-					bufp = fmt_app(auxbuf, bufp);
-					break;
-
-				case 'a':	/* Access time */
-					sprintf(auxbuf, "%24.24s", ctime(&ip -> st_atime));
-					bufp = fmt_app(auxbuf, bufp);
-					break;
-
-				case 'm':	/* Modified time */
-					sprintf(auxbuf, "%24.24s", ctime(&ip -> st_mtime));
-					bufp = fmt_app(auxbuf, bufp);
-					break;
-
-				case 'c':	/* Status change time */
-					sprintf(auxbuf, "%24.24s", ctime(&ip -> st_ctime));
-					bufp = fmt_app(auxbuf, bufp);
-					break;
-
-				case 'b':	/* # of blocks allocated */
-					sprintf(auxbuf, "%qd",ip -> st_blocks);
-					bufp = fmt_app(auxbuf, bufp);
-					break;
-
-				default:	/* Just copy it over */
-					*bufp++ = c;
-					break;
-			}
+int
+stat_it(format, filename)
+char	*format;
+char	*filename;
+{
+	int		count;
+	char		buf [BUFSIZ];
+	struct 	mystat	Sbuf;		/* for return values from stat() call */
+#define	LBUFSIZ		256	/* Length of symbolic link translation buffer */
+	char		Lbuf [LBUFSIZ];	/* Symbolic link translation buffer */
+	size_t		n;
+
+	if( lstat(filename,&Sbuf.st) == FAIL) {
+		fprintf(stderr,"lstat %s: %s\n",
+			filename, strerror(errno)); 
+		return(FAIL);
+	}
+
+	if( (Sbuf.st.st_mode & S_IFMT) == S_IFLNK) {
+		if( (count = readlink(filename, Lbuf, sizeof Lbuf)) == FAIL) {
+			fprintf(stderr, "readlink %s: %s\n",
+				filename, strerror(errno));
+			return(FAIL);
 		}
+		if( count >= sizeof Lbuf) {
+			fprintf(stderr, "readlink %s: Link name too long\n",
+				filename);
+			return(FAIL);
+		}
+		Lbuf[count] = '\0';
+		Sbuf.linkname = Lbuf;
+	} else {
+		Sbuf.linkname = NULL;
+	}
+	Sbuf.filename = filename;		
+	n = FmtInode(buf, sizeof buf, format, &Sbuf);
+	if( n >= sizeof(buf)) {
+		fprintf(stderr, "Formatted string too long for %s\n", filename);
+		return(FAIL);
 	}
+		
+	printf("%s", buf);
+	return(OKAY);
 }
-/*
+
+/*
 **  USAGE -- Display a reminder
 **
 **	Print a reminder of the correct usage
@@ -529,8 +580,7 @@
 int argc;
 char *argv[];
 {
-	extern int	optind;
-	extern char	*optarg;
+	char		*format;
 	int		c;
 
 
@@ -540,16 +590,11 @@
 
 	progname = argv [0];
 
-	if (argc == 1) {
-		Usage();
-		exit(1);
-	}
-
 	/*
 	 *	Set some defaults ...
 	 */
 
-	fmt_str = "  Size: %s\tAllocated Blocks: %b\tFiletype: %f\n  Mode: (%p)\tUid: (%u) Gid: (%g)\nDevice: %d\tInode: %i\tLinks: %l\t%t\nAccess: %a\nModify: %m\nChange: %c\n";
+	format = strdup("  File: \"%n\"%{ -> \"%r\"}r\n  Size: %s\tAllocated Blocks: %b\tFiletype: %f\n  Mode: (%p)\tUid: (%u) Gid: (%g)\nDevice: %d\tInode: %i\tLinks: %l%{\tDevice Type: %t}t\nAccess: %a\nModify: %m\nChange: %c\n\n");
 
 	/*
 	 *	Check for flag arguments ...
@@ -562,16 +607,23 @@
 				exit(1);
 
 			case 'f':		/* Format specifier */
-				fmt_str = optarg;
+				format = optarg;
 				break;
 		}
+	argc -= optind;
+	argv += optind;
 
 	/*
 	 *	Ok, now for the file arguments ...
 	 */
 
-	for (c = 0; optind < argc; ++optind) {
-		c += stat_it(argv [optind]);
+	if (argc == 0) {
+		Usage();
+		exit(1);
+	}
+
+	while (argc--) {
+		c += stat_it(format, *argv++);
 	}
 
 	/* check for errors, e.g. file does not exists */
--- stat.1~	Sun Feb 16 13:16:52 1997
+++ stat.1	Fri Mar  2 09:46:16 2001
@@ -18,35 +18,47 @@
   Size: 1024         Allocated Blocks: 2            Filetype: Directory
   Mode: (0755/drwxr-xr-x)         Uid: (0/root)  Gid: (0/system)
 Device:  0,0   Inode: 2         Links: 20   
-Access: Wed Jan  8 12:40:16 1986(00000.00:00:01)
-Modify: Wed Dec 18 09:32:09 1985(00021.03:08:08)
-Change: Wed Dec 18 09:32:09 1985(00021.03:08:08)
+Access: Wed Jan  8 12:40:16 1986
+Modify: Wed Dec 18 09:32:09 1985
+Change: Wed Dec 18 09:32:09 1985
 .fi
 .PP
 .SH OPTIONS
 .TP
 .B \-f format
 Display the i-node according to the format characters
-in "format".  Syntax is like that of printf(3s) with the
+in "format".
+Syntax is like that of printf(3s) with the
 following formatting characters:
 .nf
 
-	%f		File type.
+	%a		Access time.
+	%b		Number of blocks allocated.
+	%c		Status change time.
 	%d		Device inode lives on.
+	%f		File type.
+	%g		The group id.
 	%i		Inode itself.
+	%l		Number of links.
+	%m		Modified time.
+	%n		File name.
 	%p		Protection bits.
-	%l		# of links.
 	%u		The user id
-	%g		The group id.
-	%t		Type of device.
+	%r		Symlink name.
 	%s		Total size of file.
-	%a		Access time.
-	%m		Modified time.
-	%c		Status change time.
-	%b		# of blocks allocated.
+	%t		Type of device.
+
 .fi
+You can also specify additional formatting information inside curly
+braces between the percent and the formatting character.
+For the time options %{}a, %{}c, %{}m, this format is passed to
+\fIstrftime(3)\fR.
+For %{}r the format between the braces is interpreted by \fIstat\fR
+only if the argument is a symbolic link.
+For %{}t the format between the braces is interpreted by \fIstat\fR
+only if the argument is a special file.
 .SH "SEE ALSO"
-stat(2), ls(1)
+stat(2), ls(1), strftime(3).
 .SH DIAGNOSTICS
 "Can't stat file" or "Can't lstat file" usually means that it doesn't exist.
 "Can't readlink file" implies something is amiss with a symbolic link.
>Release-Note:
>Audit-Trail:
>Unformatted:

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-ports" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?E14YmBZ-0000cI-00>