home *** CD-ROM | disk | FTP | other *** search
/ PC Plus SuperCD (UK) 1999 May / pcp151c.iso / misc / src / install / ls.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-09-25  |  11.5 KB  |  500 lines

  1. #include <errno.h>
  2. #include <dirent.h>
  3. #include <fcntl.h>
  4. #include <glob.h>
  5. #include <stdio.h>
  6. #include <stdlib.h>
  7. #include <string.h>
  8. #include <sys/stat.h>
  9. #include <sys/sysmacros.h>
  10. #include <sys/time.h>
  11. #include <sys/types.h>
  12. #include <unistd.h>
  13.  
  14. #include "idmap.h"
  15. #include "ls.h"
  16.  
  17. struct fileInfo {
  18.     char * name;
  19.     struct stat sb;
  20. };
  21.  
  22. static void permsString(int mode, char * perms);
  23. static int statFile(char * dir, char * fn, int flags, struct stat * sbp);
  24. static int implicitListFile(int sock, char * path, 
  25.                 char * fn, struct stat * sbp, int flags);
  26. static int nameCmp(const void * a, const void * b);
  27. static int sizeCmp(const void * a, const void * b);
  28. static int mtimeCmp(const void * a, const void * b);
  29. static void multicolumnListing(int sock, struct fileInfo * files, 
  30.                 int filesCount, int flags);
  31. static int sendDirContents(int sock, char * path, 
  32.             char * fn, int flags);
  33.  
  34. static void permsString(int mode, char * perms) {
  35.     strcpy(perms, "----------");
  36.  
  37.     if (mode & S_ISVTX) perms[9] = 't';
  38.  
  39.     if (mode & S_IRUSR) perms[1] = 'r';
  40.     if (mode & S_IWUSR) perms[2] = 'w';
  41.     if (mode & S_IXUSR) perms[3] = 'x';
  42.  
  43.     if (mode & S_IRGRP) perms[4] = 'r';
  44.     if (mode & S_IWGRP) perms[5] = 'w';
  45.     if (mode & S_IXGRP) perms[6] = 'x';
  46.  
  47.     if (mode & S_IROTH) perms[7] = 'r';
  48.     if (mode & S_IWOTH) perms[8] = 'w';
  49.     if (mode & S_IXOTH) perms[9] = 'x';
  50.  
  51.     if (mode & S_ISUID) {
  52.         if (mode & S_IXUSR)
  53.             perms[3] = 's';
  54.         else
  55.             perms[3] = 'S';
  56.     }
  57.  
  58.     if (mode & S_ISGID) {
  59.         if (mode & S_IXGRP)
  60.             perms[6] = 's';
  61.         else
  62.             perms[6] = 'S';
  63.     }
  64.  
  65.     if (S_ISDIR(mode))
  66.         perms[0] = 'd';
  67.     else if (S_ISLNK(mode)) {
  68.         perms[0] = 'l';
  69.     }
  70.     else if (S_ISFIFO(mode))
  71.         perms[0] = 'p';
  72.     else if (S_ISSOCK(mode))
  73.         perms[0] = 'l';
  74.     else if (S_ISCHR(mode)) {
  75.         perms[0] = 'c';
  76.     } else if (S_ISBLK(mode)) {
  77.         perms[0] = 'b';
  78.     }
  79. }
  80.  
  81. static int statFile(char * dir, char * fn, int flags, struct stat * sbp) {
  82.     char * filename;
  83.  
  84.     if (dir) {
  85.     filename = alloca(strlen(dir) + strlen(fn) + 2);
  86.     sprintf(filename, "%s/%s", dir, fn);
  87.     } else
  88.     filename = fn;
  89.  
  90.     if (!(flags & SENDDIR_FOLLOWLINKS) || stat(filename, sbp)) {
  91.     if (lstat(filename, sbp)) {
  92.         return 1;
  93.     }
  94.     }
  95.  
  96.     return 0;
  97. }
  98.  
  99. char * fileStatStr(char * dir, char * fn, struct stat * sbp, int flags) {
  100.     char * info;
  101.     char perms[12];
  102.     char sizefield[15];
  103.     char ownerfield[9], groupfield[9];
  104.     char timefield[20] = "";
  105.     char * linkto;
  106.     char * namefield = fn;
  107.     time_t themtime;
  108.     time_t currenttime;
  109.     char * name;
  110.     int thisYear = 0;
  111.     int thisMonth = 0;
  112.     struct tm * tstruct;
  113.     int i;
  114.     char * filename;
  115.  
  116.     if (!sbp) {
  117.     sbp = alloca(sizeof(*sbp));
  118.     if (statFile(dir, fn, flags, sbp))
  119.         return NULL;
  120.     }
  121.  
  122.     permsString(sbp->st_mode, perms);
  123.  
  124.     currenttime = time(NULL);
  125.     tstruct = localtime(¤ttime);
  126.     thisYear = tstruct->tm_year;
  127.     thisMonth = tstruct->tm_mon;
  128.  
  129.     name = idSearchByUid(sbp->st_uid);
  130.     if (name)
  131.     sprintf(ownerfield, "%-8s", name);
  132.     else
  133.     sprintf(ownerfield, "%-8d", (int) sbp->st_uid);
  134.  
  135.     name = idSearchByGid(sbp->st_gid);
  136.     if (name)
  137.     sprintf(groupfield, "%-8s", name);
  138.     else
  139.     sprintf(groupfield, "%-8d", (int) sbp->st_gid);
  140.  
  141.     if (S_ISLNK(sbp->st_mode)) {
  142.     /* they don't reall want to see "opt -> /usr/opt@" */ 
  143.  
  144.     linkto = alloca(1024);
  145.     strcpy(linkto, "(link)");
  146.  
  147.     filename = alloca(strlen(dir) + strlen(fn) + 2);
  148.     sprintf(filename, "%s/%s", dir, fn);
  149.  
  150.     i = readlink(filename, linkto, 1023);
  151.     if (i < 1) 
  152.         strcpy(linkto, "(cannot read symlink)");
  153.     else
  154.         linkto[i] = 0;
  155.  
  156.         namefield = alloca(strlen(fn) + strlen(linkto) + 10);
  157.         sprintf(namefield, "%s -> %s", fn, linkto);
  158.  
  159.     sprintf(sizefield, "%d", i);
  160.     } else if (S_ISCHR(sbp->st_mode)) {
  161.         perms[0] = 'c';
  162.         sprintf(sizefield, "%3d, %3d", major(sbp->st_rdev), 
  163.         minor(sbp->st_rdev));
  164.     } else if (S_ISBLK(sbp->st_mode)) {
  165.         perms[0] = 'b';
  166.         sprintf(sizefield, "%3d, %3d", major(sbp->st_rdev), 
  167.         minor(sbp->st_rdev));
  168.     } else  {
  169.     sprintf(sizefield, "%8ld", sbp->st_size);
  170.     }
  171.  
  172.     /* this is important if sizeof(int_32) ! sizeof(time_t) */
  173.     themtime = sbp->st_mtime;
  174.     tstruct = localtime(&themtime);
  175.  
  176.     if (tstruct->tm_year == thisYear ||
  177.         ((tstruct->tm_year + 1) == thisYear && tstruct->tm_mon > thisMonth))
  178.         strftime(timefield, sizeof(timefield) - 1, "%b %d %H:%M", tstruct);
  179.     else
  180.         strftime(timefield, sizeof(timefield) - 1, "%b %d  %Y", tstruct);
  181.  
  182.     info = malloc(strlen(namefield) + strlen(timefield) + 85);
  183.  
  184.     sprintf(info, "%s %3d %8s %8s %8s %s %s", perms, (int) sbp->st_nlink, 
  185.         ownerfield, groupfield, sizefield, timefield, namefield);
  186.  
  187.     return info;
  188. }
  189.  
  190. /* Like listFiles(), but don't explode directories or wildcards */
  191. static int implicitListFile(int sock, char * path, 
  192.                 char * fn, struct stat * sbp, int flags) {
  193.     char * info;
  194.     char fileType;
  195.  
  196.     if (flags & SENDDIR_LONG) {
  197.     info = fileStatStr(path, fn, sbp, flags);
  198.     if (info) {
  199.         write(sock, info, strlen(info));
  200.         free(info);
  201.     }
  202.     } else {
  203.     write(sock, fn, strlen(fn));
  204.     }
  205.  
  206.     if (flags & SENDDIR_FILETYPE) {
  207.     if (S_ISSOCK(sbp->st_mode)) {
  208.         fileType = '=';
  209.     } else if (S_ISFIFO(sbp->st_mode)) {
  210.         fileType = '|';
  211.     } else if (S_ISDIR(sbp->st_mode)) {
  212.         fileType = '/';
  213.     } else if (S_IRWXO & sbp->st_mode) {
  214.         fileType = '*';
  215.     } else {
  216.         fileType = '\0';
  217.      }
  218.  
  219.     if (fileType) write(sock, &fileType, 1);
  220.     }
  221.  
  222.     write(sock, "\n", 1);
  223.  
  224.     return 0;
  225. }
  226.  
  227. static int nameCmp(const void * a, const void * b) {
  228.     const struct fileInfo * one = a;
  229.     const struct fileInfo * two = b;
  230.  
  231.     return (strcmp(one->name, two->name));
  232. }
  233.  
  234. static int sizeCmp(const void * a, const void * b) {
  235.     const struct fileInfo * one = a;
  236.     const struct fileInfo * two = b;
  237.  
  238.     /* list newer files first */
  239.  
  240.     if (one->sb.st_size < two->sb.st_size)
  241.     return 1;
  242.     else if (one->sb.st_size > two->sb.st_size)
  243.     return -1;
  244.  
  245.     return 0;
  246. }
  247.  
  248. static int mtimeCmp(const void * a, const void * b) {
  249.     const struct fileInfo * one = a;
  250.     const struct fileInfo * two = b;
  251.  
  252.     if (one->sb.st_mtime < two->sb.st_mtime)
  253.     return -1;
  254.     else if (one->sb.st_mtime > two->sb.st_mtime)
  255.     return 1;
  256.  
  257.     return 0;
  258. }
  259.  
  260. static void multicolumnListing(int sock, struct fileInfo * files, 
  261.                 int filesCount, int flags) {
  262.     int i, j, k;
  263.     int maxWidth = 0;
  264.     char format[20];
  265.     char * fileType = " ";
  266.     char * buf, * name = NULL;
  267.     int rows, columns;
  268.  
  269.     if (!filesCount) return;
  270.  
  271.     for (i = 0; i < filesCount; i++) {
  272.     j = strlen(files[i].name);
  273.     if (j > maxWidth) maxWidth = j;
  274.     }
  275.  
  276.     maxWidth += 3;
  277.     buf = alloca(maxWidth + 1);
  278.  
  279.     if (flags & SENDDIR_FILETYPE)
  280.     name = alloca(maxWidth);
  281.  
  282.     columns = 80 / maxWidth;
  283.     if (columns == 0) columns = 1;
  284.  
  285.     sprintf(format, "%%-%ds", 80 / columns);
  286.     
  287.     rows = filesCount / columns;
  288.     if (filesCount % columns) rows++;
  289.  
  290.     for (i = 0; i < rows; i++) {
  291.     j = i;
  292.     while (j < filesCount) {
  293.         if (flags & SENDDIR_FILETYPE) {
  294.         if (S_ISDIR(files[j].sb.st_mode))
  295.             fileType = "/";
  296.         else if (S_ISSOCK(files[j].sb.st_mode))
  297.             fileType = "=";
  298.         else if (S_ISFIFO(files[j].sb.st_mode))
  299.             fileType = "|";
  300.         else if (S_ISLNK(files[j].sb.st_mode))
  301.             fileType = "@";
  302.         else
  303.             fileType = " ";
  304.  
  305.         strcpy(name, files[j].name);
  306.         strcat(name, fileType);
  307.         } else
  308.         name = files[j].name;
  309.  
  310.         if ((j + rows) < filesCount)
  311.         k = sprintf(buf, format, name);
  312.         else
  313.         k = sprintf(buf, "%s", name);
  314.  
  315.         j += rows;
  316.  
  317.         write(sock, buf, k);
  318.     }
  319.  
  320.     write(sock, "\n", 1);
  321.     }
  322. }
  323.  
  324. static int sendDirContents(int sock, char * path, char * fn, int flags) {
  325.     struct dirent * ent;
  326.     int start, direction;
  327.     DIR * dir;
  328.     int filesAlloced, filesCount, i;
  329.     struct fileInfo * files, * newfiles;
  330.     int failed = 0;
  331.     int total = 0;
  332.     char buf[20];
  333.     char * fullpath;
  334.     char * subdir;
  335.  
  336.     filesAlloced = 15;
  337.     filesCount = 0;
  338.     files = malloc(sizeof(*files) * filesAlloced);
  339.  
  340.     if (fn) {
  341.     fullpath = alloca(strlen(path) + strlen(fn) + 2);
  342.     sprintf(fullpath, "%s/%s", path, fn);
  343.     } else
  344.     fullpath = path;
  345.  
  346.     dir = opendir(fullpath);
  347.  
  348.     do {
  349.     errno = 0;
  350.     ent = readdir(dir);
  351.     if (errno) {
  352.         fprintf(stderr, "Error reading directory entry: %s",
  353.             strerror(errno));
  354.         failed = 1;
  355.     } else if (ent && (*ent->d_name != '.' || (flags & SENDDIR_ALL))) {
  356.         if (filesCount == filesAlloced) {
  357.         filesAlloced += 15;
  358.         newfiles = realloc(files, sizeof(*files) * filesAlloced);
  359.         files = newfiles;
  360.         }
  361.  
  362.         if (!failed) {
  363.         files[filesCount].name = strdup(ent->d_name);
  364.  
  365.         if (statFile(fullpath, files[filesCount].name, flags,
  366.                  &files[filesCount].sb)) {
  367.             fprintf(stderr, "stat of %s failed: %s" , 
  368.                 files[filesCount].name, strerror(errno));
  369.             failed = 1;
  370.         } else {
  371.             total += files[filesCount].sb.st_size /
  372.                  1024;
  373.         }
  374.  
  375.         filesCount++;
  376.         }
  377.     }
  378.     } while (ent && !failed);
  379.  
  380.     closedir(dir);
  381.  
  382.     if (!failed) {
  383.     if (flags & SENDDIR_SORTMTIME) {
  384.         qsort(files, filesCount, sizeof(*files), mtimeCmp);
  385.     } else if (flags & SENDDIR_SORTSIZE) {
  386.         qsort(files, filesCount, sizeof(*files), sizeCmp);
  387.     } else if (!(flags & SENDDIR_SORTNONE)) {
  388.         qsort(files, filesCount, sizeof(*files), nameCmp);
  389.     }
  390.  
  391.     if (flags & SENDDIR_SORTREVERSE) {
  392.         direction = -1;
  393.         start = filesCount - 1;
  394.     } else {
  395.         direction = 1;
  396.         start = 0;
  397.     }
  398.  
  399.     if (fn) {
  400.         write(sock, fn, strlen(fn));
  401.         write(sock, ":\n", 2);
  402.     }
  403.     
  404.     if (flags & SENDDIR_MULTICOLUMN) {
  405.         multicolumnListing(sock, files, filesCount, flags);
  406.     } else {
  407.         if (flags & SENDDIR_LONG) {
  408.         i = sprintf(buf, "total %d\n", total);
  409.         write(sock, buf, i);
  410.         }
  411.  
  412.         for (i = start; i >= 0  && i < filesCount; i += direction) {
  413.         implicitListFile(sock, fullpath, files[i].name, 
  414.                  &files[i].sb, flags);
  415.         }
  416.     }
  417.  
  418.     if (flags & SENDDIR_RECURSE) {
  419.         for (i = start; i >= 0  && i < filesCount && !failed; 
  420.          i += direction) {
  421.         if (S_ISDIR(files[i].sb.st_mode) && 
  422.             strcmp(files[i].name, ".") && 
  423.             strcmp(files[i].name, "..")) {
  424.             write(sock, "\n", 1);
  425.  
  426.             if (fn)  {
  427.             subdir = malloc(strlen(fn) + strlen(files[i].name) + 2);
  428.             sprintf(subdir, "%s/%s", fn, files[i].name);
  429.             } else {
  430.             subdir = files[i].name;
  431.             }
  432.  
  433.             failed = sendDirContents(sock, path, subdir, flags);
  434.  
  435.             if (fn) free(subdir);
  436.         }
  437.         }
  438.     }
  439.     }
  440.  
  441.     for (i = 0; i < filesCount; i++) {
  442.     free(files[i].name);
  443.     }
  444.     free(files);
  445.  
  446.     return failed;
  447. }
  448.  
  449. /* implements 'ls' */
  450. void listFiles(char * path, char * fn, int flags) {
  451.     struct stat sb;
  452.     int i, rc;
  453.     char * filename, * this;
  454.     int isExplicit = 1;
  455.     glob_t matches;
  456.     int failed = 0;
  457.  
  458.     if (!fn) {
  459.     fn = ".";
  460.     isExplicit = 0;
  461.     }
  462.  
  463.     filename = malloc(strlen(fn) + strlen(path) + 2);
  464.     sprintf(filename, "%s/%s", path, fn);
  465.     
  466.     rc = glob(filename, GLOB_NOSORT, NULL, &matches);
  467.     if (rc == GLOB_NOMATCH) {
  468.     fprintf(stderr, "File not found.");
  469.     return;
  470.     } 
  471.     free(filename);
  472.  
  473.     for (i = 0; i < matches.gl_pathc && !failed; i++) {
  474.     this = matches.gl_pathv[i] + strlen(path);
  475.     if (*this) 
  476.         this++;
  477.     else
  478.         this = ".";
  479.  
  480.     if (!statFile(path, this, flags, &sb)) {
  481.         if (S_ISDIR(sb.st_mode) && !(flags & SENDDIR_SIMPLEDIRS)) {
  482.         filename = malloc(strlen(path) + strlen(this) + 2);
  483.         sprintf(filename, "%s/%s", path, this);
  484.  
  485.         failed = sendDirContents(1, filename, NULL, flags);
  486.         free(filename);
  487.         } else {
  488.         implicitListFile(1, path, this, &sb, flags);
  489.         }
  490.     } else {
  491.         write(1, matches.gl_pathv[i], strlen(matches.gl_pathv[i]));
  492.         write(1, ": file not found.\n", 18);
  493.     }
  494.     }
  495.  
  496.     globfree(&matches);
  497.  
  498.     close(1);
  499. }
  500.