Emulate ls -l output

The program ls2 displays information about files in the standard ls -l format. The main program uses a command argument for the directory name (or ".") as the default and calls the routine do_ls, which scans the directory and calls dostat to output the file information via a call to show_file_info. This routine prints out the information from struct stat, using mode_to_letters to generate a string describing the file permissions (in the style of ls), uid_to_name to look up the user name and gid_to_name to look up the group name.

Sample Results

> ./ls2
-rwxr-xr-x   1 loomis   users        9649 Sep 27 12:06 ls2
-rwxr-xr-x   1 loomis   users         153 Sep 23 17:34 Makefile
-rwxr-xr-x   1 loomis   users        3604 Sep 27 08:52 ls2.c
-rwxr-xr-x   1 loomis   users        1450 Sep 27 12:08 printdir.c~
drwxr-xr-x   3 loomis   users          72 Sep 27 12:04 subDir1
-rwxr-xr-x   1 loomis   users        8465 Sep 27 12:12 printdir
-rwxr-xr-x   1 loomis   users          55 Sep 27 12:02 prog.sh~
-rw-r--r--   1 loomis   users           0 Sep 27 12:51 prog.txt
-rwxr-xr-x   1 loomis   users        1471 Sep 27 12:12 printdir.c
-rwxr-xr-x   1 loomis   users          54 Sep 27 12:08 prog.sh
> ls -l
total 48
-rwxr-xr-x  1 loomis users 9649 2006-09-27 12:06 ls2
-rwxr-xr-x  1 loomis users 3604 2006-09-27 08:52 ls2.c
-rwxr-xr-x  1 loomis users  153 2006-09-23 17:34 Makefile
-rwxr-xr-x  1 loomis users 8465 2006-09-27 12:12 printdir
-rwxr-xr-x  1 loomis users 1471 2006-09-27 12:12 printdir.c
-rwxr-xr-x  1 loomis users 1450 2006-09-27 12:08 printdir.c~
-rwxr-xr-x  1 loomis users   54 2006-09-27 12:08 prog.sh
-rwxr-xr-x  1 loomis users   55 2006-09-27 12:02 prog.sh~
-rw-r--r--  1 loomis users    0 2006-09-27 12:51 prog.txt
drwxr-xr-x  3 loomis users   72 2006-09-27 12:04 subDir1

Work Required

The real version of ls prints a total line at the top. What does that mean? Furthermore, our version does not sort filenames and assumes the arguments must be directories. Unlike ls without the -a option, our code always shows hidden files.

Finally, there is a more serious bug: it does not list the information about files in other directories correctly:

$ls /usr
bin    i586-suse-linux  lib    sbin   src  X11
games  include          local  share  tmp  X11R6
$./ls2 /usr
/usr:
X11: No such file or directory
bin: No such file or directory
lib: No such file or directory
src: No such file or directory
tmp: No such file or directory
sbin: No such file or directory
X11R6: No such file or directory
games: No such file or directory
local: No such file or directory
share: No such file or directory
i586-suse-linux: No such file or directory
include: No such file or directory

Correcting this bug is left as an exercise.

Reference

Bruce Molay, Understanding Unix/Linux Programming, Pearson Prentice-Hall, 2003. ISBN 0-13-008396-8. Chapter 3, p 91-95

Source code: ls2.c

/* ls2.c
 *	purpose  list contents of directory or directories
 *	action   if no args, use .  else list files in args
 *	note     uses stat and pwd.h and grp.h 
 *	BUG: try ls2 /tmp
 *
 *	Molay, chapter 3, p 91-95
 */
#include	<stdio.h>
#include	<sys/types.h>
#include	<dirent.h>
#include	<sys/stat.h>
#include	<string.h>

void do_ls(char[]);
void dostat(char *);
void show_file_info( char *, struct stat *);
void mode_to_letters( int , char [] );
char *uid_to_name( uid_t );
char *gid_to_name( gid_t );


int main(int ac, char *av[])
{
	if ( ac == 1 )
		do_ls( "." );
	else
		while ( --ac ){
			printf("%s:\n", *++av );
			do_ls( *av );
		}
	return 0;
}


void do_ls( char dirname[] )
/*
 *	list files in directory called dirname
 */
{
	DIR		*dir_ptr;		/* the directory */
	struct dirent	*direntp;		/* each entry	 */

	if ( ( dir_ptr = opendir( dirname ) ) == NULL )
		fprintf(stderr,"ls2: cannot open %s\n", dirname);
	else
	{
		while ( ( direntp = readdir( dir_ptr ) ) != NULL ) {
			if (strcmp(direntp->d_name,".")==0 ||
				strcmp(direntp->d_name,"..")==0) continue;
			dostat( direntp->d_name );
		}
		closedir(dir_ptr);
	}
}


void dostat( char *filename )
{
	struct stat info;

	if ( stat(filename, &info) == -1 )		/* cannot stat	 */
		perror( filename );			/* say why	 */
	else					/* else show info	 */
		show_file_info( filename, &info );
}


void show_file_info( char *filename, struct stat *info_p )
/*
 * display the info about 'filename'.  The info is stored in struct at *info_p
 */
{
	char	*uid_to_name(), *ctime(), *gid_to_name(), *filemode();
	void	mode_to_letters();
        char    modestr[11];

	mode_to_letters( info_p->st_mode, modestr );

	printf( "%s"    , modestr );
	printf( "%4d "  , (int) info_p->st_nlink);	
	printf( "%-8s " , uid_to_name(info_p->st_uid) );
	printf( "%-8s " , gid_to_name(info_p->st_gid) );
	printf( "%8ld " , (long)info_p->st_size);
	printf( "%.12s ", 4+ctime(&info_p->st_mtime));
	printf( "%s\n"  , filename );

}

/*
 * utility functions
 */

/*
 * This function takes a mode value and a char array
 * and puts into the char array the file type and the 
 * nine letters that correspond to the bits in mode.
 * NOTE: It does not code setuid, setgid, and sticky
 * codes
 */

void mode_to_letters( int mode, char str[] )
{
    strcpy( str, "----------" );           /* default=no perms */

    if ( S_ISDIR(mode) )  str[0] = 'd';    /* directory?       */
    if ( S_ISCHR(mode) )  str[0] = 'c';    /* char devices     */
    if ( S_ISBLK(mode) )  str[0] = 'b';    /* block device     */

    if ( mode & S_IRUSR ) str[1] = 'r';    /* 3 bits for user  */
    if ( mode & S_IWUSR ) str[2] = 'w';
    if ( mode & S_IXUSR ) str[3] = 'x';

    if ( mode & S_IRGRP ) str[4] = 'r';    /* 3 bits for group */
    if ( mode & S_IWGRP ) str[5] = 'w';
    if ( mode & S_IXGRP ) str[6] = 'x';

    if ( mode & S_IROTH ) str[7] = 'r';    /* 3 bits for other */
    if ( mode & S_IWOTH ) str[8] = 'w';
    if ( mode & S_IXOTH ) str[9] = 'x';
}

#include	<pwd.h>

char *uid_to_name( uid_t uid )
/* 
 *	returns pointer to username associated with uid, uses getpw()
 */	
{
	struct	passwd *getpwuid(), *pw_ptr;
	static  char numstr[10];

	if ( ( pw_ptr = getpwuid( uid ) ) == NULL ){
		sprintf(numstr,"%d", uid);
		return numstr;
	}
	else
		return pw_ptr->pw_name ;
}

#include	<grp.h>

char *gid_to_name( gid_t gid )
/*
 *	returns pointer to group number gid. used getgrgid(3)
 */
{
	struct group *getgrgid(), *grp_ptr;
	static  char numstr[10];

	if ( ( grp_ptr = getgrgid(gid) ) == NULL ){
		sprintf(numstr,"%d", gid);
		return numstr;
	}
	else
		return grp_ptr->gr_name;
}


Maintained by John Loomis, last updated 27 September 2006