Simplify pathname building, in which a bug was noted by Larry Doolittle,
[oweals/busybox.git] / ls.c
diff --git a/ls.c b/ls.c
index 0e08f7683fa57b43ca2010b242957e2bb6cf1d1b..d24ba98663f12e2227d6ef4f0c6604a0a7f7b668 100644 (file)
--- a/ls.c
+++ b/ls.c
  * 1. requires lstat (BSD) - how do you do it without?
  */
 
-#define TERMINAL_WIDTH 80              /* use 79 if your terminal has linefold bug */
-#define COLUMN_WIDTH   14              /* default if AUTOWIDTH not defined */
-#define COLUMN_GAP     2                       /* includes the file type char, if present */
+enum {
+       TERMINAL_WIDTH = 80,            /* use 79 if terminal has linefold bug */
+       COLUMN_WIDTH = 14,                      /* default if AUTOWIDTH not defined */
+       COLUMN_GAP = 2,                         /* includes the file type char */
+};
+
 
 /************************************************************************/
 
-#include "internal.h"
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <stdio.h>
 #include <dirent.h>
 #include <errno.h>
 #include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+#include "busybox.h"
+
 #ifdef BB_FEATURE_LS_TIMESTAMPS
 #include <time.h>
 #endif
-#include <string.h>
 
-#ifndef NAJOR
+#ifndef MAJOR
 #define MAJOR(dev) (((dev)>>8)&0xff)
 #define MINOR(dev) ((dev)&0xff)
 #endif
 
 /* what is the overall style of the listing */
-#define STYLE_AUTO             0
-#define STYLE_LONG             1               /* one record per line, extended info */
-#define STYLE_SINGLE   2               /* one record per line */
-#define STYLE_COLUMNS  3               /* fill columns */
+enum {
+STYLE_AUTO = 0,
+STYLE_LONG = 1,                /* one record per line, extended info */
+STYLE_SINGLE = 2,              /* one record per line */
+STYLE_COLUMNS = 3              /* fill columns */
+};
 
 /* 51306 lrwxrwxrwx  1 root     root         2 May 11 01:43 /bin/view -> vi* */
 /* what file information will be listed */
 
 #ifdef BB_FEATURE_LS_SORTFILES
 /* how will the files be sorted */
-#define SORT_FORWARD    0              /* sort in reverse order */
-#define SORT_REVERSE    1              /* sort in reverse order */
-#define SORT_NAME              2               /* sort by file name */
-#define SORT_SIZE              3               /* sort by file size */
-#define SORT_ATIME             4               /* sort by last access time */
-#define SORT_CTIME             5               /* sort by last change time */
-#define SORT_MTIME             6               /* sort by last modification time */
-#define SORT_VERSION   7               /* sort by version */
-#define SORT_EXT               8               /* sort by file name extension */
-#define SORT_DIR               9               /* sort by file or directory */
+static const int SORT_FORWARD = 0;             /* sort in reverse order */
+static const int SORT_REVERSE = 1;             /* sort in reverse order */
+static const int SORT_NAME = 2;                /* sort by file name */
+static const int SORT_SIZE = 3;                /* sort by file size */
+static const int SORT_ATIME = 4;               /* sort by last access time */
+static const int SORT_CTIME = 5;               /* sort by last change time */
+static const int SORT_MTIME = 6;               /* sort by last modification time */
+static const int SORT_VERSION = 7;             /* sort by version */
+static const int SORT_EXT = 8;         /* sort by file name extension */
+static const int SORT_DIR = 9;         /* sort by file or directory */
 #endif
 
 #ifdef BB_FEATURE_LS_TIMESTAMPS
 /* which of the three times will be used */
-#define TIME_MOD    0
-#define TIME_CHANGE 1
-#define TIME_ACCESS 2
+static const int TIME_MOD = 0;
+static const int TIME_CHANGE = 1;
+static const int TIME_ACCESS = 2;
 #endif
 
 #define LIST_SHORT             (LIST_FILENAME)
                                                LIST_SYMLINK)
 #define LIST_ILONG             (LIST_INO | LIST_LONG)
 
-#define SPLIT_DIR              0
-#define SPLIT_FILE             1
+static const int SPLIT_DIR = 0;
+static const int SPLIT_FILE = 1;
+static const int SPLIT_SUBDIR = 2;
 
 #define TYPEINDEX(mode) (((mode) >> 12) & 0x0f)
 #define TYPECHAR(mode)  ("0pcCd?bB-?l?s???" [TYPEINDEX(mode)])
@@ -145,19 +156,19 @@ struct dnode {                            /* the basic node */
 };
 typedef struct dnode dnode_t;
 
-struct dnode **list_dir(char *);
-struct dnode **dnalloc(int);
-int list_single(struct dnode *);
+static struct dnode **list_dir(char *);
+static struct dnode **dnalloc(int);
+static int list_single(struct dnode *);
 
-static unsigned int disp_opts= DISP_NORMAL;
-static unsigned int style_fmt= STYLE_AUTO ;
-static unsigned int list_fmt=  LIST_SHORT ;
+static unsigned int disp_opts;
+static unsigned int style_fmt;
+static unsigned int list_fmt;
 #ifdef BB_FEATURE_LS_SORTFILES
-static unsigned int sort_opts= SORT_FORWARD;
-static unsigned int sort_order=        SORT_FORWARD;
+static unsigned int sort_opts;
+static unsigned int sort_order;
 #endif
 #ifdef BB_FEATURE_LS_TIMESTAMPS
-static unsigned int time_fmt=  TIME_MOD;
+static unsigned int time_fmt;
 #endif
 #ifdef BB_FEATURE_LS_FOLLOWLINKS
 static unsigned int follow_links=FALSE;
@@ -167,16 +178,44 @@ static unsigned short column = 0;
 #ifdef BB_FEATURE_AUTOWIDTH
 static unsigned short terminal_width = TERMINAL_WIDTH;
 static unsigned short column_width = COLUMN_WIDTH;
-static unsigned short tabstops = 8;
+static unsigned short tabstops = COLUMN_GAP;
 #else
-#define terminal_width  TERMINAL_WIDTH
-#define column_width    COLUMN_WIDTH
+static unsigned short column_width = COLUMN_WIDTH;
 #endif
 
+static int status = EXIT_SUCCESS;
+
+#ifdef BB_FEATURE_HUMAN_READABLE
+static unsigned long ls_disp_hr = 0;
+#endif
+
+static int my_stat(struct dnode *cur)
+{
+#ifdef BB_FEATURE_LS_FOLLOWLINKS
+       if (follow_links == TRUE) {
+               if (stat(cur->fullname, &cur->dstat)) {
+                       perror_msg("%s", cur->fullname);
+                       status = EXIT_FAILURE;
+                       free(cur->fullname);
+                       free(cur);
+                       return -1;
+               }
+       } else
+#endif
+       if (lstat(cur->fullname, &cur->dstat)) {
+               perror_msg("%s", cur->fullname);
+               status = EXIT_FAILURE;
+               free(cur->fullname);
+               free(cur);
+               return -1;
+       }
+       return 0;
+}
+
 static void newline(void)
 {
     if (column > 0) {
-        fprintf(stdout, "\n");
+        putchar('\n');
         column = 0;
     }
 }
@@ -203,19 +242,24 @@ static void nexttabstop( void )
                n= nexttab - column;
                if (n < 1) n= 1;
                while (n--) {
-                       fprintf(stdout, " ");
+                       putchar(' ');
                        column++;
                }
        }
-       nexttab= column + column_width + COLUMN_GAP ;
+       nexttab= column + column_width + COLUMN_GAP
 }
 
 /*----------------------------------------------------------------------*/
-int countdirs(struct dnode **dn, int nfiles)
+static int is_subdir(struct dnode *dn)
+{
+       return (S_ISDIR(dn->dstat.st_mode) && strcmp(dn->name, ".") != 0 &&
+                       strcmp(dn->name, "..") != 0);
+}
+
+static int countdirs(struct dnode **dn, int nfiles)
 {
        int i, dirs;
 
-       /* count how many dirs and regular files there are */
        if (dn==NULL || nfiles < 1) return(0);
        dirs= 0;
        for (i=0; i<nfiles; i++) {
@@ -224,7 +268,19 @@ int countdirs(struct dnode **dn, int nfiles)
        return(dirs);
 }
 
-int countfiles(struct dnode **dnp)
+static int countsubdirs(struct dnode **dn, int nfiles)
+{
+       int i, subdirs;
+
+       if (dn == NULL || nfiles < 1) return 0;
+       subdirs = 0;
+       for (i = 0; i < nfiles; i++)
+               if (is_subdir(dn[i]))
+                       subdirs++;
+       return subdirs;
+}
+
+static int countfiles(struct dnode **dnp)
 {
        int nfiles;
        struct dnode *cur;
@@ -237,7 +293,7 @@ int countfiles(struct dnode **dnp)
 }
 
 /* get memory to hold an array of pointers */
-struct dnode **dnalloc(int num)
+static struct dnode **dnalloc(int num)
 {
        struct dnode **p;
 
@@ -247,7 +303,7 @@ struct dnode **dnalloc(int num)
        return(p);
 }
 
-void dfree(struct dnode **dnp)
+static void dfree(struct dnode **dnp)
 {
        struct dnode *cur, *next;
 
@@ -263,7 +319,7 @@ void dfree(struct dnode **dnp)
        free(dnp);      /* free the array holding the dnode pointers */
 }
 
-struct dnode **splitdnarray(struct dnode **dn, int nfiles, int which)
+static struct dnode **splitdnarray(struct dnode **dn, int nfiles, int which)
 {
        int dncnt, i, d;
        struct dnode **dnp;
@@ -271,9 +327,13 @@ struct dnode **splitdnarray(struct dnode **dn, int nfiles, int which)
        if (dn==NULL || nfiles < 1) return(NULL);
 
        /* count how many dirs and regular files there are */
-       dncnt= countdirs(dn, nfiles); /* assume we are looking for dirs */
-       if (which != SPLIT_DIR)
-               dncnt= nfiles - dncnt;  /* looking for files */
+       if (which == SPLIT_SUBDIR)
+               dncnt = countsubdirs(dn, nfiles);
+       else {
+               dncnt= countdirs(dn, nfiles); /* assume we are looking for dirs */
+               if (which == SPLIT_FILE)
+                       dncnt= nfiles - dncnt;  /* looking for files */
+       }
 
        /* allocate a file array and a dir array */
        dnp= dnalloc(dncnt);
@@ -284,6 +344,10 @@ struct dnode **splitdnarray(struct dnode **dn, int nfiles, int which)
                        if (S_ISDIR(dn[i]->dstat.st_mode)) {
                                dnp[d++]= dn[i];
                        }  /* else skip the file */
+               } else if (which == SPLIT_SUBDIR) {
+                       if (is_subdir(dn[i])) {
+                               dnp[d++]= dn[i];
+                       }  /* else skip the file or dir */
                } else {
                        if (!(S_ISDIR(dn[i]->dstat.st_mode))) {
                                dnp[d++]= dn[i];
@@ -295,7 +359,7 @@ struct dnode **splitdnarray(struct dnode **dn, int nfiles, int which)
 
 /*----------------------------------------------------------------------*/
 #ifdef BB_FEATURE_LS_SORTFILES
-int sortcmp(struct dnode *d1, struct dnode *d2)
+static int sortcmp(struct dnode *d1, struct dnode *d2)
 {
        int cmp, dif;
 
@@ -332,7 +396,7 @@ int sortcmp(struct dnode *d1, struct dnode *d2)
 }
 
 /*----------------------------------------------------------------------*/
-void shellsort(struct dnode **dn, int size)
+static void shellsort(struct dnode **dn, int size)
 {
        struct dnode *temp;
        int gap, i, j;
@@ -356,7 +420,7 @@ void shellsort(struct dnode **dn, int size)
 #endif
 
 /*----------------------------------------------------------------------*/
-void showfiles(struct dnode **dn, int nfiles)
+static void showfiles(struct dnode **dn, int nfiles)
 {
        int i, ncols, nrows, row, nc;
 #ifdef BB_FEATURE_AUTOWIDTH
@@ -373,10 +437,18 @@ void showfiles(struct dnode **dn, int nfiles)
                        ((list_fmt & LIST_INO) ? 8 : 0) +
                        ((list_fmt & LIST_BLOCKS) ? 5 : 0)
                        ;
-               if (column_width < len) column_width= len;
+               if (column_width < len) 
+                       column_width= len;
+       }
+       if (column_width >= 6)
+               ncols = (int)(terminal_width / (column_width + COLUMN_GAP));
+       else {
+               ncols = 1;
+               column_width = COLUMN_WIDTH;
        }
+#else
+       ncols= TERMINAL_WIDTH;
 #endif
-       ncols= (int)(terminal_width / (column_width + COLUMN_GAP));
        switch (style_fmt) {
                case STYLE_LONG:        /* one record per line, extended info */
                case STYLE_SINGLE:      /* one record per line */
@@ -384,7 +456,12 @@ void showfiles(struct dnode **dn, int nfiles)
                        break;
        }
 
-       nrows= nfiles / ncols;
+       if (ncols > 1) {
+               nrows = nfiles / ncols;
+       } else {
+               nrows = nfiles;
+               ncols = 1;
+       }
        if ((nrows * ncols) < nfiles) nrows++; /* round up fractionals */
 
        if (nrows > nfiles) nrows= nfiles;
@@ -404,11 +481,11 @@ void showfiles(struct dnode **dn, int nfiles)
 }
 
 /*----------------------------------------------------------------------*/
-void showdirs(struct dnode **dn, int ndirs)
+static void showdirs(struct dnode **dn, int ndirs)
 {
        int i, nfiles;
        struct dnode **subdnp;
-#ifdef BB_FEATURE_LS_SORTFILES
+#ifdef BB_FEATURE_LS_RECURSIVE
        int dndirs;
        struct dnode **dnd;
 #endif
@@ -417,7 +494,7 @@ void showdirs(struct dnode **dn, int ndirs)
 
        for (i=0; i<ndirs; i++) {
                if (disp_opts & (DISP_DIRNAME | DISP_RECURSIVE)) {
-                       fprintf(stdout, "\n%s:\n", dn[i]->fullname);
+                       printf("\n%s:\n", dn[i]->fullname);
                }
                subdnp= list_dir(dn[i]->fullname);
                nfiles= countfiles(subdnp);
@@ -430,10 +507,12 @@ void showdirs(struct dnode **dn, int ndirs)
 #ifdef BB_FEATURE_LS_RECURSIVE
                        if (disp_opts & DISP_RECURSIVE) {
                                /* recursive- list the sub-dirs */
-                               dnd= splitdnarray(subdnp, nfiles, SPLIT_DIR);
-                               dndirs= countdirs(subdnp, nfiles);
+                               dnd= splitdnarray(subdnp, nfiles, SPLIT_SUBDIR);
+                               dndirs= countsubdirs(subdnp, nfiles);
                                if (dndirs > 0) {
+#ifdef BB_FEATURE_LS_SORTFILES
                                        shellsort(dnd, dndirs);
+#endif
                                        showdirs(dnd, dndirs);
                                        free(dnd);  /* free the array of dnode pointers to the dirs */
                                }
@@ -445,54 +524,37 @@ void showdirs(struct dnode **dn, int ndirs)
 }
 
 /*----------------------------------------------------------------------*/
-struct dnode **list_dir(char *path)
+static struct dnode **list_dir(char *path)
 {
        struct dnode *dn, *cur, **dnp;
        struct dirent *entry;
        DIR *dir;
-       char *fnend, fullname[BUFSIZ+1] ;
        int i, nfiles;
 
        if (path==NULL) return(NULL);
-       strcpy(fullname, path);
-       fnend = fullname + strlen(fullname);
-       if (fnend[-1] != '/') {
-               strcat(fullname, "/");
-               fnend++;
-       }
 
        dn= NULL;
        nfiles= 0;
-       dir = opendir(fullname);
+       dir = opendir(path);
        if (dir == NULL) {
-               errorMsg("%s: %s\n", fullname, strerror(errno));
+               perror_msg("%s", path);
+               status = EXIT_FAILURE;
                return(NULL);   /* could not open the dir */
        }
        while ((entry = readdir(dir)) != NULL) {
                /* are we going to list the file- it may be . or .. or a hidden file */
-               strcpy(fnend, entry->d_name);
-               if ((strcmp(fnend, ".")==0) && !(disp_opts & DISP_DOT)) continue;
-               if ((strcmp(fnend, "..")==0) && !(disp_opts & DISP_DOT)) continue;
-               if ((fnend[0] ==  '.') && !(disp_opts & DISP_HIDDEN)) continue;
+               if ((strcmp(entry->d_name, ".")==0) && !(disp_opts & DISP_DOT))
+                       continue;
+               if ((strcmp(entry->d_name, "..")==0) && !(disp_opts & DISP_DOT))
+                       continue;
+               if ((entry->d_name[0] ==  '.') && !(disp_opts & DISP_HIDDEN))
+                       continue;
                cur= (struct dnode *)xmalloc(sizeof(struct dnode));
-               cur->fullname= xstrdup(fullname);
-               cur->name= cur->fullname + (int)(fnend - fullname) ;
-#ifdef BB_FEATURE_LS_FOLLOWLINKS
-               if (follow_links == TRUE) {
-                       if (stat(fullname, &cur->dstat)) {
-                               errorMsg("%s: %s\n", fullname, strerror(errno));
-                               free(cur->fullname);
-                               free(cur);
-                               continue;
-                       }
-               } else
-#endif
-               if (lstat(fullname, &cur->dstat)) {   /* get file stat info into node */
-                       errorMsg("%s: %s\n", fullname, strerror(errno));
-                       free(cur->fullname);
-                       free(cur);
+               cur->fullname = concat_path_file(path, entry->d_name);
+               cur->name = cur->fullname +
+                               (strlen(cur->fullname) - strlen(entry->d_name));
+               if (my_stat(cur))
                        continue;
-               }
                cur->next= dn;
                dn= cur;
                nfiles++;
@@ -513,7 +575,7 @@ struct dnode **list_dir(char *path)
 }
 
 /*----------------------------------------------------------------------*/
-int list_single(struct dnode *dn)
+static int list_single(struct dnode *dn)
 {
        int i, len;
        char scratch[BUFSIZ + 1];
@@ -521,8 +583,10 @@ int list_single(struct dnode *dn)
        char *filetime;
        time_t ttime, age;
 #endif
-#ifdef BB_FEATURE_LS_FILETYPES
+#if defined (BB_FEATURE_LS_FILETYPES)
        struct stat info;
+#endif
+#ifdef BB_FEATURE_LS_FILETYPES
        char append;
 #endif
 
@@ -541,65 +605,61 @@ int list_single(struct dnode *dn)
        for (i=0; i<=31; i++) {
                switch (list_fmt & (1<<i)) {
                        case LIST_INO:
-                               fprintf(stdout, "%7ld ", dn->dstat.st_ino);
+                               printf("%7ld ", dn->dstat.st_ino);
                                column += 8;
                                break;
                        case LIST_BLOCKS:
+#ifdef BB_FEATURE_HUMAN_READABLE
+                               fprintf(stdout, "%5s ", make_human_readable_str(dn->dstat.st_blocks>>1,
+                                                       (ls_disp_hr==TRUE)? 0: 1));
+#else
 #if _FILE_OFFSET_BITS == 64
-                               fprintf(stdout, "%4lld ", dn->dstat.st_blocks>>1);
+                               printf("%4lld ", dn->dstat.st_blocks>>1);
 #else
-                               fprintf(stdout, "%4ld ", dn->dstat.st_blocks>>1);
+                               printf("%4ld ", dn->dstat.st_blocks>>1);
+#endif
 #endif
                                column += 5;
                                break;
                        case LIST_MODEBITS:
-                               fprintf(stdout, "%10s", (char *)modeString(dn->dstat.st_mode));
+                               printf("%10s", (char *)mode_string(dn->dstat.st_mode));
                                column += 10;
                                break;
                        case LIST_NLINKS:
-                               fprintf(stdout, "%4d ", dn->dstat.st_nlink);
+                               printf("%4ld ", (long)dn->dstat.st_nlink);
                                column += 10;
                                break;
                        case LIST_ID_NAME:
 #ifdef BB_FEATURE_LS_USERNAME
-                               {
-                                       memset(&info, 0, sizeof(struct stat));
-                                       memset(scratch, 0, sizeof(scratch));
-                                       if (!stat(dn->fullname, &info)) {
-                                               my_getpwuid(scratch, info.st_uid);
-                                       }
-                                       if (*scratch) {
-                                               fprintf(stdout, "%-8.8s ", scratch);
-                                       } else {
-                                               fprintf(stdout, "%-8d ", dn->dstat.st_uid);
-                                       }
-                                       memset(scratch, 0, sizeof(scratch));
-                                       if (info.st_ctime != 0) {
-                                               my_getgrgid(scratch, info.st_gid);
-                                       }
-                                       if (*scratch) {
-                                               fprintf(stdout, "%-8.8s", scratch);
-                                       } else {
-                                               fprintf(stdout, "%-8d", dn->dstat.st_gid);
-                                       }
+                               my_getpwuid(scratch, dn->dstat.st_uid);
+                               printf("%-8.8s ", scratch);
+                               my_getgrgid(scratch, dn->dstat.st_gid);
+                               printf("%-8.8s", scratch);
                                column += 17;
-                               }
                                break;
 #endif
                        case LIST_ID_NUMERIC:
-                               fprintf(stdout, "%-8d %-8d", dn->dstat.st_uid, dn->dstat.st_gid);
+                               printf("%-8d %-8d", dn->dstat.st_uid, dn->dstat.st_gid);
                                column += 17;
                                break;
                        case LIST_SIZE:
                        case LIST_DEV:
                                if (S_ISBLK(dn->dstat.st_mode) || S_ISCHR(dn->dstat.st_mode)) {
-                                       fprintf(stdout, "%4d, %3d ", (int)MAJOR(dn->dstat.st_rdev), (int)MINOR(dn->dstat.st_rdev));
+                                       printf("%4d, %3d ", (int)MAJOR(dn->dstat.st_rdev), (int)MINOR(dn->dstat.st_rdev));
                                } else {
+#ifdef BB_FEATURE_HUMAN_READABLE
+                                       if (ls_disp_hr==TRUE) {
+                                               fprintf(stdout, "%9s ", make_human_readable_str(
+                                                                       dn->dstat.st_size>>10, 0));
+                                       } else 
+#endif 
+                                       {
 #if _FILE_OFFSET_BITS == 64
-                                       fprintf(stdout, "%9lld ", dn->dstat.st_size);
+                                               printf("%9lld ", (long long)dn->dstat.st_size);
 #else
-                                       fprintf(stdout, "%9ld ", dn->dstat.st_size);
+                                               printf("%9ld ", dn->dstat.st_size);
 #endif
+                                       }
                                }
                                column += 10;
                                break;
@@ -607,23 +667,23 @@ int list_single(struct dnode *dn)
                        case LIST_FULLTIME:
                        case LIST_DATE_TIME:
                                if (list_fmt & LIST_FULLTIME) {
-                                       fprintf(stdout, "%24.24s ", filetime);
+                                       printf("%24.24s ", filetime);
                                        column += 25;
                                        break;
                                }
                                age = time(NULL) - ttime;
-                               fprintf(stdout, "%6.6s ", filetime+4);
+                               printf("%6.6s ", filetime+4);
                                if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
                                        /* hh:mm if less than 6 months old */
-                                       fprintf(stdout, "%5.5s ", filetime+11);
+                                       printf("%5.5s ", filetime+11);
                                } else {
-                                       fprintf(stdout, " %4.4s ", filetime+20);
+                                       printf(" %4.4s ", filetime+20);
                                }
                                column += 13;
                                break;
 #endif
                        case LIST_FILENAME:
-                               fprintf(stdout, "%s", dn->name);
+                               printf("%s", dn->name);
                                column += strlen(dn->name);
                                break;
                        case LIST_SYMLINK:
@@ -631,7 +691,7 @@ int list_single(struct dnode *dn)
                                        len= readlink(dn->fullname, scratch, (sizeof scratch)-1);
                                        if (len > 0) {
                                                scratch[len]= '\0';
-                                               fprintf(stdout, " -> %s", scratch);
+                                               printf(" -> %s", scratch);
 #ifdef BB_FEATURE_LS_FILETYPES
                                                if (!stat(dn->fullname, &info)) {
                                                        append = append_char(info.st_mode);
@@ -644,7 +704,7 @@ int list_single(struct dnode *dn)
 #ifdef BB_FEATURE_LS_FILETYPES
                        case LIST_FILETYPE:
                                if (append != '\0') {
-                                       fprintf(stdout, "%1c", append);
+                                       printf("%1c", append);
                                        column++;
                                }
                                break;
@@ -665,19 +725,29 @@ extern int ls_main(int argc, char **argv)
        int opt;
        int oi, ac;
        char **av;
+#ifdef BB_FEATURE_AUTOWIDTH
+       struct winsize win = { 0, 0, 0, 0 };
+#endif
 
        disp_opts= DISP_NORMAL;
        style_fmt= STYLE_AUTO;
        list_fmt=  LIST_SHORT;
 #ifdef BB_FEATURE_LS_SORTFILES
        sort_opts= SORT_NAME;
+       sort_order=     SORT_FORWARD;
 #endif
 #ifdef BB_FEATURE_LS_TIMESTAMPS
        time_fmt= TIME_MOD;
+#endif
+#ifdef BB_FEATURE_AUTOWIDTH
+       ioctl(fileno(stdout), TIOCGWINSZ, &win);
+       if (win.ws_row > 4)
+               column_width = win.ws_row - 2;
+       if (win.ws_col > 0)
+               terminal_width = win.ws_col - 1;
 #endif
        nfiles=0;
 
-       applet_name= argv[0];
        /* process options */
        while ((opt = getopt(argc, argv, "1AaCdgilnsx"
 #ifdef BB_FEATURE_AUTOWIDTH
@@ -698,17 +768,25 @@ extern int ls_main(int argc, char **argv)
 #ifdef BB_FEATURE_LS_FOLLOWLINKS
 "L"
 #endif
-       )) > 0) {
+#ifdef BB_FEATURE_HUMAN_READABLE
+"h"
+#endif
+"k")) > 0) {
                switch (opt) {
                        case '1': style_fmt = STYLE_SINGLE; break;
                        case 'A': disp_opts |= DISP_HIDDEN; break;
                        case 'a': disp_opts |= DISP_HIDDEN | DISP_DOT; break;
                        case 'C': style_fmt = STYLE_COLUMNS; break;
                        case 'd': disp_opts |= DISP_NOLIST; break;
-                       case 'e': list_fmt |= LIST_FULLTIME; break;
                        case 'g': /* ignore -- for ftp servers */ break;
                        case 'i': list_fmt |= LIST_INO; break;
-                       case 'l': style_fmt = STYLE_LONG; list_fmt |= LIST_LONG; break;
+                       case 'l':
+                               style_fmt = STYLE_LONG;
+                               list_fmt |= LIST_LONG;
+#ifdef BB_FEATURE_HUMAN_READABLE
+                               ls_disp_hr = FALSE;
+#endif
+                       break;
                        case 'n': list_fmt |= LIST_ID_NUMERIC; break;
                        case 's': list_fmt |= LIST_BLOCKS; break;
                        case 'x': disp_opts = DISP_ROWS; break;
@@ -726,9 +804,24 @@ extern int ls_main(int argc, char **argv)
                        case 'X': sort_opts= SORT_EXT; break;
 #endif
 #ifdef BB_FEATURE_LS_TIMESTAMPS
-                       case 'c': time_fmt = TIME_CHANGE; sort_opts= SORT_CTIME; break;
-                       case 't': sort_opts= SORT_MTIME; break;
-                       case 'u': time_fmt = TIME_ACCESS; sort_opts= SORT_ATIME; break;
+                       case 'e': list_fmt |= LIST_FULLTIME; break;
+                       case 'c':
+                               time_fmt = TIME_CHANGE;
+#ifdef BB_FEATURE_LS_SORTFILES
+                               sort_opts= SORT_CTIME;
+#endif
+                               break;
+                       case 'u':
+                               time_fmt = TIME_ACCESS;
+#ifdef BB_FEATURE_LS_SORTFILES
+                               sort_opts= SORT_ATIME;
+#endif
+                               break;
+                       case 't':
+#ifdef BB_FEATURE_LS_SORTFILES
+                               sort_opts= SORT_MTIME;
+#endif
+                               break;
 #endif
 #ifdef BB_FEATURE_LS_FOLLOWLINKS
                        case 'L': follow_links= TRUE; break;
@@ -737,6 +830,10 @@ extern int ls_main(int argc, char **argv)
                        case 'T': tabstops= atoi(optarg); break;
                        case 'w': terminal_width= atoi(optarg); break;
 #endif
+#ifdef BB_FEATURE_HUMAN_READABLE
+                       case 'h': ls_disp_hr = TRUE; break;
+#endif
+                       case 'k': break;
                        default:
                                goto print_usage_message;
                }
@@ -747,7 +844,7 @@ extern int ls_main(int argc, char **argv)
        if (disp_opts & DISP_NOLIST)
                disp_opts &= ~DISP_RECURSIVE;   /* no recurse if listing only dir */
 #endif
-#ifdef BB_FEATURE_LS_TIMESTAMPS
+#if defined (BB_FEATURE_LS_TIMESTAMPS) && defined (BB_FEATURE_LS_SORTFILES)
        if (time_fmt & TIME_CHANGE) sort_opts= SORT_CTIME;
        if (time_fmt & TIME_ACCESS) sort_opts= SORT_ATIME;
 #endif
@@ -790,13 +887,9 @@ extern int ls_main(int argc, char **argv)
        for (oi=0 ; oi < ac; oi++) {
                cur= (struct dnode *)xmalloc(sizeof(struct dnode));
                cur->fullname= xstrdup(av[oi]);
-               cur->name= cur->fullname ;
-               if (lstat(av[oi], &cur->dstat)) {  /* get file info into node */
-                       errorMsg("%s: %s\n", av[oi], strerror(errno));
-                       free(cur->fullname);
-                       free(cur);
+               cur->name= cur->fullname;
+               if (my_stat(cur))
                        continue;
-               }
                cur->next= dn;
                dn= cur;
                nfiles++;
@@ -835,10 +928,8 @@ extern int ls_main(int argc, char **argv)
                        showdirs(dnd, dndirs);
                }
        }
-
-       return(0);
+       return(status);
 
   print_usage_message:
-       usage(ls_usage);
-       return(FALSE);
+       show_usage();
 }