How do I program ls commands on Linux?

Keywords: Linux github Ubuntu shell

What is the 0x01 ls command?

The system environment I use is Ubuntu 18.04, and the ls command tells us what files are in the current directory:

The ls command also has common parameters:

  • -l: List file details
  • -a: List the files that contain the beginning of.
  • -s: file size is printed in blocks
  • -t: Sort output by time
  • -F: Display file type
  • -r: reverse order output
  • -R: Recursive output
  • -q: unsorted output

Introduction of 0x02 using functions

Because there are read and write file operations, the main operations here are

0x0201 Catalog Open

Header file required

#include <sys/types.h>
#include <dirent.h>

Format of function

DIR *opendir(const char *name);

name points to the directory we need to open and returns a DIR structure pointer (which can be interpreted as a Directory Descriptor).

Read operation of 0x0202 directory

Header file required

#include <dirent.h>

Format of function

struct dirent *readdir(DIR *dirp);

The dirp points to our directory descriptor and returns a dirent structure.

struct dirent {
    ino_t          d_ino;       /* Inode number */
    off_t          d_off;       /* Not an offset; see below */
    unsigned short d_reclen;    /* Length of this record */
    unsigned char  d_type;      /* Type of file; not supported
                                              by all filesystem types */
    char           d_name[256]; /* Null-terminated filename */
};

We can get the file information in the directory by d_name.

Close operation of 0x0203 directory

Required header file

#include <sys/types.h>
#include <dirent.h>

Function Prototype

int closedir(DIR *dirp);

dirp points to our directory descriptor and an error returns -1 during shutdown.

0x0204 Get File Information

Required header file

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

Function Prototype

int stat(const char *pathname, struct stat *statbuf);

The pathname points to the file we need to get, and the stat is a structure that contains the file information.

struct stat {
    dev_t     st_dev;         /* ID of device containing file */
    ino_t     st_ino;         /* Inode number */
    mode_t    st_mode;        /* File type and mode */
    nlink_t   st_nlink;       /* Number of hard links */
    uid_t     st_uid;         /* User ID of owner */
    gid_t     st_gid;         /* Group ID of owner */
    dev_t     st_rdev;        /* Device ID (if special file) */
    off_t     st_size;        /* Total size, in bytes */
    blksize_t st_blksize;     /* Block size for filesystem I/O */
    blkcnt_t  st_blocks;      /* Number of 512B blocks allocated */

    /* Since Linux 2.6, the kernel supports nanosecond
                  precision for the following timestamp fields.
                  For the details before Linux 2.6, see NOTES. */

    struct timespec st_atim;  /* Time of last access */
    struct timespec st_mtim;  /* Time of last modification */
    struct timespec st_ctim;  /* Time of last status change */

#define st_atime st_atim.tv_sec      /* Backward compatibility */
#define st_mtime st_mtim.tv_sec
#define st_ctime st_ctim.tv_sec
};

Return-1 if there is an error getting information.

0x0205 Gets the user's passwd information

Required header file

#include <sys/types.h>
#include <pwd.h>

Function Prototype

    struct passwd *getpwuid(uid_t uid);

uid is our user ID, returning a passwd structure pointer

struct passwd
{
  char *pw_name;                /* Username.  */
  char *pw_passwd;              /* Password.  */
  __uid_t pw_uid;               /* User ID.  */
  __gid_t pw_gid;               /* Group ID.  */
  char *pw_gecos;               /* Real name.  */
  char *pw_dir;                 /* Home directory.  */
  char *pw_shell;               /* Shell program.  */
};

0x0206 Get Group ID Information

Required header file

#include <sys/types.h>
#include <grp.h>

Function Prototype

struct group *getgrgid(gid_t gid);

The gid is our group ID and returns a group structure pointer

struct group
{
    char *gr_name;              /* Group name.  */
    char *gr_passwd;            /* Password.    */
    __gid_t gr_gid;             /* Group ID.    */
    char **gr_mem;              /* Member list. */
};

0x03 Write an ls

0x0301 First Edition

#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>

void do_ls(char []);

void do_ls(char dirname[])
{
    DIR* dir_ptr;
    struct dirent* direntp;
    if ((dir_ptr = opendir(dirname)) == NULL)
    {
        fprintf(stderr, "ls1: cannot open %s\n", dirname);
    }
    else
    {
        while ((direntp = readdir(dir_ptr)) != NULL)
        {
            printf("%s\n", direntp->d_name);
        }
        closedir(dir_ptr);
    }
}

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

Let's see the difference between our version and the actual version

The function is implemented well and the output format needs to be adjusted.Our output needs to be sorted.ls does not output. The file at the beginning needs to be in -a.We'l l also add a -l feature.

0x0302 2nd Edition

Let's first see what information ls-l will print

$ ls -l
total 24
-rwxr-xr-x 1 lly lly 8568 4 19/09:54 app
-rw-r–r-- 1 lly lly 589 4 19/09:52 ls.c
-rw-r–r-- 1 lly lly 2256 4 19/09:54 ls.o
-rw-r–r-- 1 lly lly 249 4 19/09:54 Makefile

Each row contains seven fields, namely, mode, number of links, file owner, all groups of files, file size, and last modification time and file name, which can be obtained from the stat.

Let's write a function, show_stat_info, to see what information we can get.

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>

void show_stat_info(char *fname, struct stat* buf);

void show_stat_info(char *fname, struct stat* buf)
{
    printf("mode:%o\n", buf->st_mode);
    printf("links:%d\n", buf->st_nlink);
    printf("user:%d\n", buf->st_uid);
    printf("group:%d\n", buf->st_gid);
    printf("size:%d\n", buf->st_size);
    printf("modtime:%d\n", buf->st_mtime);
    printf("name:%s\n", fname);
}
int main(int argc, char *argv[])
{
    struct stat info;
    if (argc > 1)
    {
        if (stat(argv[1], &info) != -1)
        {
            show_stat_info(argv[1], &info);
            return 0;
        }
        else perror(argv[1]);
    }
    return 1;
}

See how the information we're getting now differs from that obtained by ls-l

We need to convert the mode l into a character form, how?Find the following information in/usr/include/bits/stat.h

#define __S_IFDIR       0040000 /* Directory.  */
#define __S_IFCHR       0020000 /* Character device.  */
#define __S_IFBLK       0060000 /* Block device.  */
#define __S_IFREG       0100000 /* Regular file.  */
#define __S_IFIFO       0010000 /* FIFO.  */
#define __S_IFLNK       0120000 /* Symbolic link.  */
#define __S_IFSOCK      0140000 /* Socket.  */

Find some of the following macro functions at / usr/include/sys/stat.h

#define S_ISDIR(mode)    __S_ISTYPE((mode), __S_IFDIR)
#define S_ISCHR(mode)    __S_ISTYPE((mode), __S_IFCHR)
#define S_ISBLK(mode)    __S_ISTYPE((mode), __S_IFBLK)
#define S_ISREG(mode)    __S_ISTYPE((mode), __S_IFREG)
#ifdef __S_IFIFO
# define S_ISFIFO(mode)  __S_ISTYPE((mode), __S_IFIFO)
#endif
#ifdef __S_IFLNK
# define S_ISLNK(mode)   __S_ISTYPE((mode), __S_IFLNK)
#endif

So now we can write a mode_to_letters function

void mode_to_letters(int mode, char str[])
{
    strcpy(str, "----------");
    if (S_ISDIR(mode)) str[0] = 'd';
    if (S_ISCHR(mode)) str[0] = 'c';
    if (S_ISBLK(mode)) str[0] = 'b';

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

    if (mode & S_IRGRP) str[4] = 'r';
    if (mode & S_IWGRP) str[5] = 'w';
    if (mode & S_IXGRP) str[6] = 'x';

    if (mode & S_IROTH) str[7] = 'r';
    if (mode & S_IWOTH) str[8] = 'w';
    if (mode & S_IXOTH) str[9] = 'x';
}

ok, now our mode l problem is solved.Next, let's look at the differences in user and group and bash output.

We know that the user's information file is in/etc/passwd, and we can get the information from it through getpwuid.For user group information, we can get it through getgrgid.We can quickly write two functions to get information

char *uid_to_name(uid_t uid)
{
    struct passwd* pw_ptr;
    static char numstr[10];

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

char *gid_to_name(gid_t gid)
{
    struct group* grp_ptr;
    static char numstr[10];

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

Okay, so far the basic functionality has been implemented.I put the code in**My GitHub Linux ** Up.

However, one problem with our code is that it does not show the total number of records, does not sort by file name, and does not support option-a.Existing functions cannot display special file formats suid, sgid, and sticky.How to add -R function?How to add -r function?How to add -q function?

0x0303 3rd Edition

Start with a simple problem by adding special file formats suid, sgid, and sticky.

void mode_to_letters(int mode, char str[])
{
    strcpy(str, "----------");
    if (S_ISDIR(mode)) str[0] = 'd';
    if (S_ISCHR(mode)) str[0] = 'c';
    if (S_ISBLK(mode)) str[0] = 'b';
    if (S_ISREG(mode)) str[0] = 'r';
    if (S_IFIFO(mode)) str[0] = 'p';
    if (S_IFLNK(mode)) str[0] = 'l';
    if (S_IFSOCK(mode)) str[0] = 's';

    if (mode & S_IRUSR) str[1] = 'r';
    if (mode & S_IWUSR) str[2] = 'w';
    if (mode & S_IXUSR) str[3] = 'x';
    if (mode & S_ISUID) str[3] = 's';

    if (mode & S_IRGRP) str[4] = 'r';
    if (mode & S_IWGRP) str[5] = 'w';
    if (mode & S_IXGRP) str[6] = 'x';
    if (mode & S_ISGID) str[6] = 's';

    if (mode & S_IROTH) str[7] = 'r';
    if (mode & S_IWOTH) str[8] = 'w';
    if (mode & S_IXOTH) str[9] = 'x';
    if (mode & S_ISVTX) str[9] = 't';
}

I added a different language version of the problem to my GitHub Linux

If there are any questions, I hope you can point out!!!

Posted by computerzworld on Sun, 05 May 2019 07:40:38 -0700