/* ********************************************************************** */ /* * * */ /* * myls.c * */ /* * * */ /* ********************************************************************** */ /* * * */ /* * Homework #1 - Problem #1 * */ /* * * */ /* * This program is designed to implement a scaled down version of * */ /* * "ls" command. Specifically, this program implements a version * */ /* * of the "-s" and "-l" option, and allows you to specify a list of * */ /* * files and directories an arguments. * */ /* * * */ /* ********************************************************************** */ /* * * */ /* * 06/08/2009 - Program Created * */ /* * * */ /* ********************************************************************** */ #include #include #include #include #include #include #include #include #include #include #define TRUE 1 #define FALSE 0 #define MAX_FCOUNT 8192 #define SEC_PER_YEAR 60 * 60 * 24 * 365 /* Global array of structures to hold output to be sorted */ struct filename { char name[384]; /* Filename */ struct stat buf; /* Inode for the file */ }; typedef struct filename FILENAME; /* Give us a shortcut for struct */ /* ********************************************************************** */ /* * Function: cmp_name * */ /* * * */ /* * Compare two filename entries based on name using same return * */ /* * values as strcmp to show less than, equal to, or greater than. * */ /* ********************************************************************** */ int cmp_name(FILENAME **a, FILENAME **b) { FILENAME *one, *two; /* Temp structures */ one = *a; two = *b; return(strcmp(one->name, two->name)); } /* ********************************************************************** */ /* * Function: get_perm * */ /* * * */ /* * Generate the permission string for the "-l" output format. * */ /* ********************************************************************** */ int get_perm(char *perm, mode_t m) { char ftype; /* File type -, d, p, c, b, l */ /* Determine the file's type */ if (S_ISREG(m)) ftype = '-'; else if (S_ISDIR(m)) ftype = 'd'; else if (S_ISLNK(m)) ftype = 'l'; else if (S_ISFIFO(m)) ftype = 'p'; else if (S_ISBLK(m)) ftype = 'b'; else if (S_ISCHR(m)) ftype = 'c'; else if (S_ISSOCK(m)) ftype = 's'; /* Decode the permission string */ sprintf(perm, "%c%c%c%c%c%c%c%c%c%c", ftype, (m & S_IRUSR) ? 'r' : '-', (m & S_IWUSR) ? 'w' : '-', (m & S_IXUSR) ? 'x' : '-', (m & S_IRGRP) ? 'r' : '-', (m & S_IWGRP) ? 'w' : '-', (m & S_IXGRP) ? 'x' : '-', (m & S_IROTH) ? 'r' : '-', (m & S_IWOTH) ? 'w' : '-', (m & S_IXOTH) ? 'x' : '-'); /* Make corrections for SUID, SGID, and sticky bit */ if (m & S_ISUID) perm[3] = (m & S_IXUSR) ? 's' : 'S'; if (m & S_ISGID) perm[6] = (m & S_IXGRP) ? 's' : 'S'; if (m & S_ISVTX) perm[9] = (m & S_IXOTH) ? 't' : 'T'; return TRUE; } /* ********************************************************************** */ /* * Function: print_long * */ /* * * */ /* * Output a filename entry in the "-l" format (i.e. ls -l). * */ /* ********************************************************************** */ int print_long(char *name, char *dir, struct stat buf) { int num; /* Number of bytes in readlink */ char perm[16]; /* Permission string (-rwxrwxrwx) */ char temp[256]; /* Temporary filename */ char link[256]; /* Target of symbolic link */ char owner[32]; /* Name of file's owner */ char group[32]; /* Name of file's group owner */ struct passwd *pw; /* Temporary passwd structure */ struct group *gr; /* Temporary group structure */ char mtime[32]; /* Formatted modification time */ time_t now; /* Current timestamp */ struct tm *tm, *tnow; /* Temporary time structures */ /* If we have a symlink, then lookup the target filename */ if (S_ISLNK(buf.st_mode)) { if (dir != NULL) sprintf(temp, "%s/%s", dir, name); else strcpy(temp, name); if ((num = readlink(temp, link, 256)) >= 0) { link[num] = 0; sprintf(name, "%s -> %s", name, link); } } /* Decode the permission string */ get_perm(perm, buf.st_mode); /* Use the uid to get the owner's name */ if ((pw = getpwuid(buf.st_uid)) == NULL) sprintf(owner, "%d", buf.st_uid); else strcpy(owner, pw->pw_name); /* Use the gid to get the group owner's name */ if ((gr = getgrgid(buf.st_gid)) == NULL) sprintf(group, "%d", buf.st_gid); else strcpy(group, gr->gr_name); /* Format the modification time */ now = time(0); tnow = localtime(&now); tm = localtime(&(buf.st_mtime)); if (buf.st_mtime < now - SEC_PER_YEAR) strftime(mtime, 32, "%b %d %Y", tm); else strftime(mtime, 32, "%b %d %H:%M", tm); /* Print the output */ if (S_ISCHR(buf.st_mode) || S_ISBLK(buf.st_mode)) { printf("%s %d %-8s %-9s %3d,%3d %s %s\n", perm, buf.st_nlink, owner, group, major(buf.st_rdev), minor(buf.st_rdev), mtime, name); } else { printf("%s %d %-8s %-9s %7d %s %s\n", perm, buf.st_nlink, owner, group, buf.st_size, mtime, name); } } /* ********************************************************************** */ /* * Function: print_file * */ /* * * */ /* * Print ls style output for one file. * */ /* ********************************************************************** */ int print_file(char *file, char *dir, struct stat buf, int showsize, int showlong) { if (showsize) printf("%4d ", buf.st_blocks); if (showlong) print_long(file, dir, buf); else printf("%s\n", file); } /* ********************************************************************** */ /* * Function: myls * */ /* * * */ /* * Load file info into a data structure and generate "ls" style * */ /* * output. * */ /* ********************************************************************** */ int myls(char *dir, int showsize, int showlong) { DIR *dp; /* Directory pointer */ struct dirent *dirp; /* Directory structure */ struct stat sbuf; /* Stat structure */ FILENAME *f; /* temporary filename ptr */ FILENAME **files; /* filename array */ int fcount; /* counter for files array */ int i; /* loop variable */ int total = 0; /* total blocks in dir */ char fbuf[256]; /* filename buffer */ /* Check the file's type */ if (lstat(dir, &sbuf) < 0) { perror("stat call failed"); _exit(1); } /* If not a directory, just print its info */ if (!S_ISDIR(sbuf.st_mode)) { print_file(dir, NULL, sbuf, showsize, showlong); return(TRUE); } /* Allocate the filename array */ files = (FILENAME **) malloc(sizeof(FILENAME *) * MAX_FCOUNT + 1); fcount = 0; if (files == NULL) { fprintf(stderr, "Cannot allocate filename array\n"); return(FALSE); } /* Open the directory file */ if ((dp = opendir(dir)) == NULL) { fprintf(stderr, "Cannot open directory %s\n", dir); return(FALSE); } /* Read each directory entry and print results */ while ((dirp = readdir(dp)) != NULL) { if (dirp->d_name[0] == '.') continue; sprintf(fbuf, "%s/%s", dir, dirp->d_name); if (lstat(fbuf, &sbuf) < 0) { perror("stat call failed"); return(FALSE); } if ((f = (FILENAME *) malloc(sizeof(FILENAME))) == NULL) { fprintf(stderr, "Cannot malloc filename %s\n", dirp->d_name); return(FALSE); } strcpy(f->name, dirp->d_name); f->buf = sbuf; files[fcount++] = f; total += sbuf.st_blocks; if (fcount > MAX_FCOUNT) { fprintf(stderr, "MAX_FCOUNT of %d exceeeded\n", MAX_FCOUNT); return(FALSE); } } closedir(dp); qsort(files, fcount, sizeof(FILENAME *), cmp_name); if (strcmp(dir, ".") != 0) printf("%s: \n", dir); if (showlong) printf("total %d\n", total); for (i = 0; i < fcount; i++) { print_file(files[i]->name, dir, files[i]->buf, showsize, showlong); free(files[i]); } free(files); } /* ********************************************************************** */ /* * Function: main * */ /* ********************************************************************** */ int main(int argc, char **argv) { int showsize = FALSE; /* "-s" flag */ int showlong = FALSE; /* "-l" flag */ int i; /* loop variable */ char c; /* Current option */ /* Process command line args */ while ((c = getopt(argc, argv, ":sl")) != -1) { switch (c) { case 's': showsize = TRUE; break; case 'l': showlong = TRUE; break; case '?': fprintf(stderr, "Invalid option -%c\n", optopt); return(1); break; } } /* If there are no more args, then use "." */ if (argc == optind) myls(".", showsize, showlong); else { for (i = optind; i < argc; i++) { printf("\n"); /* Call the myls function to do the actual work */ myls(argv[i], showsize, showlong); } } /* Cleanup */ return(0); }