|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T l
Length: 30892 (0x78ac) Types: TextFile Names: »ls.c«
└─⟦a05ed705a⟧ Bits:30007078 DKUUG GNU 2/12/89 └─⟦this⟧ »./ls.c«
/* `ls' directory listing program for GNU. Copyright (C) 1985, 1988 Free Software Foundation, Inc. Modified by Jay Fenlason, Jan 1988 so that ls -Ra doesn't loop on ././././. ... Modified by Roland McGrath, May 1988 so that -g does something. Also made it so that -B -A will not display .*~ Also cleaned up massive ugliness. NO WARRANTY BECAUSE THIS PROGRAM IS LICENSED FREE OF CHARGE, WE PROVIDE ABSOLUTELY NO WARRANTY, TO THE EXTENT PERMITTED BY APPLICABLE STATE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING, FREE SOFTWARE FOUNDATION, INC, RICHARD M. STALLMAN AND/OR OTHER PARTIES PROVIDE THIS PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL RICHARD M. STALLMAN, THE FREE SOFTWARE FOUNDATION, INC., AND/OR ANY OTHER PARTY WHO MAY MODIFY AND REDISTRIBUTE THIS PROGRAM AS PERMITTED BELOW, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY LOST PROFITS, LOST MONIES, OR OTHER SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS) THIS PROGRAM, EVEN IF YOU HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES, OR FOR ANY CLAIM BY ANY OTHER PARTY. GENERAL PUBLIC LICENSE TO COPY 1. You may copy and distribute verbatim copies of this source file as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy a valid copyright notice "Copyright (C) 1985 Free Software Foundation, Inc.", and include following the copyright notice a verbatim copy of the above disclaimer of warranty and of this License. 2. You may modify your copy or copies of this source file or any portion of it, and copy and distribute such modifications under the terms of Paragraph 1 above, provided that you also do the following: a) cause the modified files to carry prominent notices stating that you changed the files and the date of any change; and b) cause the whole of any work that you distribute or publish, that in whole or in part contains or is a derivative of this program or any part thereof, to be licensed at no charge to all third parties on terms identical to those contained in this License Agreement (except that you may choose to grant more extensive warranty protection to third parties, at your option). c) You may charge a distribution fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 3. You may copy and distribute this program or any portion of it in compiled, executable or object code form under the terms of Paragraphs 1 and 2 above provided that you do the following: a) cause each such copy to be accompanied by the corresponding machine-readable source code, which must be distributed under the terms of Paragraphs 1 and 2 above; or, b) cause each such copy to be accompanied by a written offer, with no time limit, to give any third party free (except for a nominal shipping charge) a machine readable copy of the corresponding source code, to be distributed under the terms of Paragraphs 1 and 2 above; or, c) in the case of a recipient of this program in compiled, executable or object code form (without the corresponding source code) you shall cause copies you distribute to be accompanied by a copy of the written offer of source code which you received along with the copy you received. 4. You may not copy, sublicense, distribute or transfer this program except as expressly provided under this License Agreement. Any attempt otherwise to copy, sublicense, distribute or transfer this program is void and your rights to use the program under this License agreement shall be automatically terminated. However, parties who have received computer software programs from you with this License Agreement will not have their licenses terminated so long as such parties remain in full compliance. 5. If you wish to incorporate parts of this program into other free programs whose distribution conditions are different, write to the Free Software Foundation at 675 Mass Ave, Cambridge, MA 02139. We have not yet worked out a simple rule that can be stated here, but we will often permit this. We will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software. In other words, you are welcome to use, share and improve this program. You are forbidden to forbid anyone else to use, share and improve what you give them. Help stamp out software-hoarding! */ \f #include <sys/types.h> #include <sys/stat.h> #include <sys/dir.h> #include <sys/file.h> #include <sys/ioctl.h> #include <sgtty.h> #include <stdio.h> #include <strings.h> #include <grp.h> #include <pwd.h> #ifdef sparc #include "alloca.h" #endif /* If the macro MULTI_COL is defined, then multi-column format is the default regardless of the type of output device, if the macro LONG_FORMAT is defined, then long format is the default regardless of the type of output device. These setting will be used for GNU's preferred directory listing programs, but not for GNU's ls, which will exist for compatibility. */ char *copystring (); int compare_atime (), compare_mtime (), compare_ctime (), compare_names (); char *getuser (), *getgroup (); extern int errno; extern int sys_nerr; extern char *sys_errlist[]; \f /* The table of files in the current directory: `files' points to a vector of `struct file', one per file. `nfiles' is the number of elements space has been allocated for. `files_index' is the number actually in use. */ enum filetype { normal, directory, symbolic_link }; struct file { char *name; /* The file name */ struct stat stat; char *linkname; /* name linked to, for symbolic link, otherwise zero */ enum filetype filetype; /* directory, file or symlink */ }; /* Address of block containing the files that are described. */ struct file *files; /* Length of block that `files' points to, measured in files. */ int nfiles; /* Index of first unused in `files'. */ int files_index; \f /* Record of one pending directory waiting to be listed. */ struct pending { char *name; struct pending *next; }; struct pending *pending_dirs; /* Option flags */ /* 0 for lots of info, one per line. 1 for just names, one per line. 2 for just names, many per line. 3 for many per line, horizontally. 4 for many per line, separated by commas, no tabs. */ /* -l, -1, -C, -x and -m control this parameter. */ enum format { long_format, one_per_line, many_per_line, horizontal, with_commas, } format; /* type of time to print or sort by. Controlled by -c and -u. */ enum time_type { time_mtime, /* default */ time_ctime, /* -c */ time_atime, /* -u */ } time_type; /* Current time (seconds since 1970). When we are printing a file's time, include the year if it is more than 10 months before this time. */ long current_time; /* Nonzero means sort by time (-t). Zero, sort by name. */ int sort_by_time; /* Direction of sort. 0 means highest first if numeric, lowest first if alphabetic; these happen to be the defaults. 1 means the opposite order in each case. -r */ int sort_reverse; /* mention the group of each file. -g */ int print_groups; /* mention size in blocks of each file. -s */ int print_block_size; /* Number of digits to use for block sizes. 4, or more if needed for bigger numbers. */ int block_size_size; /* 1 means mention type of file. -F */ /* Also -p sets this to -1, which means do so except for executables. */ int print_filetype; /* mention inode number of each file. -i */ int print_inode; /* When a symbolic link is found, display info on the file linked to. -L */ int trace_links; /* When a directory is found, display info on its contents. -R */ int trace_dirs; /* When an argument is a directory name, display info on it itself. -d */ int immediate_dirs; /* Don't omit files whose names suggest they are uninteresting. -A */ int all_files; /* Don't omit files "." and ".." -a. This flag implies all_files */ int really_all_files; /* Make backup files appear uninteresting. Better might be options to: 1) list numbered backups in csh syntax "foo{.~12~,~13~}", or 2) ignoring all files matching a regexp that the user specifies. */ int ignore_backup_files; #define BACKUP_SUFFIX '~' /* used for detecting backup files */ /* Quote nongraphic chars in file names. -b */ int quote_funny_chars; /* Output each file name using C syntax for a string. Always accompanied by quote_funny_chars. This mode, together with -x or -C or -m, and without such frills as -F or -s, is guaranteed to make it possible for a program receiving the output to tell exactly what file names are present. -Q */ int quote_as_string; /* Nonzero means we are listing working directory because no args given */ int dir_defaulted; /* Line length to use for breaking lines in many-per-line format. Can be set with -w. */ int line_length; /* A version string, 'just 'cuz its a good idea */ /* JF: made this version string */ /* 1.00: added -v (version) and -h (help) options */ char *version_string = "GNU ls version 1.00\n"; /* A help msg. Help is always a good idea (?) */ char *help_msg = "\ This program is used to list the files and subdirectories contained\n\ in a directory. For more information, try the command 'info ls'.\n"; \f int main (argc, argv) int argc; char **argv; { register int i; register char *thisdir; register struct pending *thispend; dir_defaulted = 1; pending_dirs = 0; current_time = time ((long *) 0); i = decode_switches (argc, argv); /* Note that decode_switches can set elements of argv to zero to prevent them from being taken as file names. */ nfiles = 100; files = (struct file *) xmalloc (sizeof (struct file) * nfiles); files_index = 0; clear_files (); for (; i < argc; i++) if (argv[i] != 0) { dir_defaulted = 0; gobble_file (argv[i], 1, ""); } if (dir_defaulted) if (immediate_dirs) gobble_file (".", 0, ""); else queue_directory ("."); if (files_index) { sort_files (); if (!immediate_dirs) extract_dirs_from_files ("", 0); /* files_index may be zero now. */ } if (files_index) print_current_files (); while (pending_dirs) { thisdir = pending_dirs->name; thispend = pending_dirs; pending_dirs = pending_dirs->next; free (thispend); print_dir (thisdir); } return 0; } \f /* Set all the option flags according to the switches specified. Also set to zero any elements of argv that are switches or args to switches. Returns the index of the first non-option argument. */ decode_switches (argc, argv) int argc; char **argv; { extern char *getenv (); register int i; register char *p; struct sgttyb garbage; extern char *optarg; extern int optind; int c; /* initialize all switches to default settings */ #ifdef MULTI_COL format = many_per_line; #else #ifdef LONG_FORMAT format = long_format; #else if (ioctl (1, TIOCGETP, &garbage) == 0) format = many_per_line; else format = one_per_line; #endif #endif time_type = time_mtime; sort_by_time = 0; sort_reverse = 0; print_groups = 0; print_block_size = 0; print_filetype = 0; print_inode = 0; trace_links = 0; trace_dirs = 0; immediate_dirs = 0; all_files = 0; really_all_files = 0; ignore_backup_files = 0; quote_funny_chars = 0; quote_as_string = 0; p = getenv ("COLUMNS"); line_length = (p ? atoi (p) : 80) - 1; while ((c = getopt (argc, argv, "abcdhgilmprstuvw:xABCFLQR1")) != EOF) switch (c) { case 'a': all_files = 1; really_all_files = 1; break; case 'b': quote_funny_chars = 1; break; case 'c': time_type = time_ctime; break; case 'd': immediate_dirs = 1; break; case 'g': print_groups = 1; break; case 'h': fprintf(stderr,help_msg); exit(0); case 'i': print_inode = 1; break; case 'l': format = long_format; break; case 'm': format = with_commas; break; case 'p': print_filetype = -1; break; case 'r': sort_reverse = 1; break; case 's': print_block_size = 1; break; case 't': sort_by_time = 1; break; case 'u': time_type = time_atime; break; case 'v': printf("%s",version_string); break; case 'w': line_length = atoi (optarg); break; case 'x': format = horizontal; break; case 'A': all_files = 1; break; case 'B': ignore_backup_files = 1; break; case 'C': format = many_per_line; break; case 'F': print_filetype = 1; break; case 'L': trace_links = 1; break; case 'Q': quote_as_string = 1; quote_funny_chars = 1; break; case 'R': trace_dirs = 1; break; case '1': format = one_per_line; break; } return optind; } \f /* Request that the directory named `name' have its contents listed later. */ queue_directory (name) char *name; { struct pending *new = (struct pending *) xmalloc (sizeof (struct pending)); new->next = pending_dirs; pending_dirs = new; new->name = copystring (name); } /* Read directory `name', and list the files in it. */ print_dir (name) char *name; { register DIR *reading = opendir (name); register struct direct *next; register int total_blocks = 0; register int blocks; if (!reading) { perror_with_name (name); return; } /* Read the directory entries, and insert the subfiles into the `files' table. */ clear_files (); while (next = readdir (reading)) if (file_interesting_p (next)) total_blocks += gobble_file (copystring (next->d_name), 0, name); closedir (reading); /* Sort the directory contents. */ sort_files (); /* If any member files are subdirectories, perhaps they should have their contents listed rather than being mentioned here as files. */ if (trace_dirs) extract_dirs_from_files (name, 1); /* If we queued any subdirectories to be listed, print each listed directory's name, including this one's. */ if (pending_dirs) dir_defaulted = 0; if (!dir_defaulted) printf ("%s:\n", name); if (format == long_format || print_block_size) printf ("total %d\n", total_blocks); if (files_index) print_current_files (); if (pending_dirs) putchar ('\n'); free (name); } /* return nonzero if file should be listed. */ int file_interesting_p (next) register struct direct *next; { if (really_all_files) return 1; if (ignore_backup_files && next->d_name[next->d_namlen - 1] == BACKUP_SUFFIX) return 0; if (next->d_name[0] != '.' || (all_files && next->d_name[1] != '\0' && (next->d_name[1] != '.' || next->d_name[2] != '\0'))) return 1; return 0; } \f /* Enter and remove entries in the table `files'. */ /* Empty the table of files */ clear_files () { register int i; for (i = 0; i < files_index; i++) { free (files[i].name); free (files[i].linkname); } files_index = 0; block_size_size = 4; } /* Add a file to the current table of files. check nonzero means verify that the file exists, and print an error message if it does not. Returns the number of blocks that the file occupies. */ gobble_file (name, explicit_arg, dirname) char *name; int explicit_arg; char *dirname; { register int val; register char *buf; register int bufsiz = 100; register int blocks; register char *concat; if (files_index == nfiles) files = (struct file *) xrealloc (files, sizeof (struct file) * (nfiles *= 2)); files[files_index].name = name; files[files_index].linkname = 0; if (explicit_arg || sort_by_time || format == long_format || trace_links || trace_dirs || print_filetype || print_block_size || print_inode) { /* `concat' gets the absolute pathname of this file. */ if (name[0] == '/' || dirname[0] == 0) concat = name; else { concat = (char *) alloca (strlen (name) + strlen (dirname) + 2); strcpy (concat, dirname); strcat (concat, "/"); strcat (concat, name); } /* Read the file's properties. */ if (trace_links) val = stat (concat, &files[files_index].stat); else val = lstat (concat, &files[files_index].stat); if (val < 0) { perror_with_name (concat); return 0; } blocks = files[files_index].stat.st_blocks; if (blocks >= 10000 && block_size_size < 5) block_size_size = 5; if (blocks >= 100000 && block_size_size < 6) block_size_size = 6; if (blocks >= 1000000 && block_size_size < 7) block_size_size = 7; /* Fill in auxiliary fields about the file. */ files[files_index].filetype = normal; if ((files[files_index].stat.st_mode & S_IFMT) == S_IFLNK) files[files_index].filetype = symbolic_link; else if ((files[files_index].stat.st_mode & S_IFMT) == S_IFDIR) files[files_index].filetype = directory; if (format == long_format && files[files_index].filetype == symbolic_link) { while (1) { buf = (char *) xmalloc (bufsiz); buf[bufsiz-1] = 0; val = readlink (concat, buf, bufsiz); if (val < 0 || buf[bufsiz-1] == 0) break; free (buf); bufsiz *= 2; } if (val < 0) { perror_with_name (concat); files[files_index].linkname = 0; } else { buf[val] = 0; files[files_index].linkname = buf; } } } files_index++; return blocks; } /* Remove any entries from `files' that are for directories, and queue them to be listed as directories instead. DIRNAME is the prefix to prepend to each dirname to make it correct relative to ls's working dir. RECURSIVE is nonzero if we should not treat `.' and `..' as dirs. This is desirable when processing dirs recursively. */ extract_dirs_from_files (dirname, recursive) char *dirname; int recursive; { register int i, j = 0; register char *concat; /* Queue the directories last one first, because queueing reverses the order. */ for (i = files_index - 1; i >= 0; i--) if (files[i].filetype == directory && (! recursive || is_not_dot_or_dotdot (files[i].name))) { if (files[i].name[0] == '/' || dirname[0] == 0) concat = files[i].name; else { concat = (char *) alloca (strlen (files[i].name) + strlen (dirname) + 2); sprintf (concat, "%s/%s", dirname, files[i].name); } queue_directory (concat); free (files[i].name); } /* Now delete the directories from the table, compacting all the remaining entries. */ for (i = 0; i < files_index; i++) if (files[i].filetype != directory) files[j++] = files[i]; files_index = j; } \f /* Returns non-zero if the filename doesn't end in . or .. */ /* This is so we don't try to recurse on ././././. ... */ is_not_dot_or_dotdot (name) char *name; { char *t; t = rindex (name,'/'); if (t) name = t + 1; if (name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'))) return 0; return 1; } \f /* Sort the files now in the table. */ sort_files () { int (*func) (); if (sort_by_time) switch (time_type) { case time_ctime: func = compare_ctime; break; case time_mtime: func = compare_mtime; break; case time_atime: func = compare_atime; break; } else func = compare_names; qsort (files, files_index, sizeof (struct file), func); if (sort_reverse) reverse_files (); } /* Auxiliary routines for sorting the files */ compare_ctime (file1, file2) struct file *file1, *file2; { return file2->stat.st_ctime - file1->stat.st_ctime; } compare_mtime (file1, file2) struct file *file1, *file2; { return file2->stat.st_mtime - file1->stat.st_mtime; } compare_atime (file1, file2) struct file *file1, *file2; { return file2->stat.st_atime - file1->stat.st_atime; } compare_names (file1, file2) struct file *file1, *file2; { return strcmp (file1->name, file2->name); } reverse_files () { register int i, j; struct file temp; for (i = 0, j = files_index - 1; i < j; i++, j--) { temp = files[i]; files[i] = files[j]; files[j] = temp; } } \f /* List all the files now in the table. */ print_current_files () { register int i; switch (format) { case one_per_line: for (i = 0; i < files_index; i++) { print_file_name_and_frills (files + i); putchar ('\n'); } break; case many_per_line: print_many_per_line (); break; case horizontal: print_horizontal (); break; case with_commas: print_with_commas (); break; case long_format: for (i = 0; i < files_index; i++) { print_long_format (files + i); putchar ('\n'); } break; } } print_long_format (f) struct file *f; { char mbuf[20]; char tbuf[40]; long time; filemodestring (&f->stat, mbuf); if (f->filetype == symbolic_link) mbuf[0] = 'l'; mbuf[10] = 0; switch (time_type) { case time_ctime: time = f->stat.st_ctime; break; case time_mtime: time = f->stat.st_mtime; break; case time_atime: time = f->stat.st_atime; break; } strcpy (tbuf, ctime (&time)); if (current_time - time > 300 * 24 * 60 * 60) { /* File is at least 300 days old. Show its year instead of the time of day. */ strcpy (tbuf + 11, tbuf + 19); } tbuf[16] = 0; if (print_inode) printf ("%5d ", f->stat.st_ino); if (print_block_size) printf ("%*d ", block_size_size, f->stat.st_blocks); printf ("%s%3d %-9s %-9s", mbuf, f->stat.st_nlink, getuser (f->stat.st_uid), getgroup (f->stat.st_gid)); if ((f->stat.st_mode & S_IFMT) == S_IFCHR || (f->stat.st_mode & S_IFMT) == S_IFBLK) printf ("%3d, %3d %s ", major (f->stat.st_rdev), minor (f->stat.st_rdev), tbuf + 4); else printf ("%8d %s ", f->stat.st_size, tbuf + 4); print_name_with_quoting (f->name); if (f->filetype == symbolic_link && f->linkname) { fputs (" -> ", stdout); print_name_with_quoting (f->linkname); } } \f print_name_with_quoting (p) register char *p; { register char c; if (quote_as_string) putchar ('"'); while (c = *p++) { if (quote_funny_chars) { switch (c) { case '\\': printf ("\\\\"); break; case '\n': printf ("\\n"); break; case '\b': printf ("\\b"); break; case '\r': printf ("\\r"); break; case '\t': printf ("\\t"); break; case '\f': printf ("\\f"); break; case ' ': printf ("\\ "); break; case '"': printf ("\\\""); break; default: if (c > 040 && c < 0177) putchar (c); else printf ("\\%03o", c); } } else { if (c >= 040 && c < 0177) putchar (c); else putchar ('?'); } } if (quote_as_string) putchar ('"'); } \f /* Print a file name with appropriate quoting. Also print file size, inode number, and filetype indicator character, as requested by switches. */ print_file_name_and_frills (f) struct file *f; { if (print_inode) printf ("%5d ", f->stat.st_ino); if (print_block_size) printf ("%*d ", block_size_size, f->stat.st_blocks); print_name_with_quoting (f->name); if (print_filetype) switch (f->filetype) { case directory: putchar ('/'); break; case symbolic_link: putchar ('@'); break; default: if (print_filetype > 0 && (f->stat.st_mode & (S_IEXEC|S_IEXEC>>3|S_IEXEC>>6))) putchar ('*'); /* else JF because it makes -Fm look funny putchar (' '); */ } } length_of_file_name_and_frills (f) struct file *f; { register char *p = f->name; register char c; register int len = 0; if (print_inode) len += 6; if (print_block_size) len += 1 + block_size_size; if (quote_as_string) len += 2; while (c = *p++) { if (quote_funny_chars) { switch (c) { case '\\': case '\n': case '\b': case '\r': case '\t': case '\f': case ' ': len += 2; break; case '"': if (quote_as_string) len += 2; else len += 1; break; default: if (c >= 040 && c < 0177) len += 1; else len += 4; } } else len += 1; } if (print_filetype) { if (f->filetype!=normal) len += 1; else if (print_filetype > 0 && (f->stat.st_mode & (S_IEXEC|S_IEXEC>>3|S_IEXEC>>6))) len += 1; } return len; } \f print_many_per_line () { int i; int i1; int max; int tem; int pos; int perline; int nlines; int longcols; int start_shortcol; /* Compute the maximum file name length. */ max = 0; for (i = 0; i < files_index; i++) { tem = length_of_file_name_and_frills (files + i); if (tem > max) max = tem; } /* Allow at least two spaces between names. */ max += 2; perline = line_length / max; nlines = (files_index + perline - 1) / perline; longcols = files_index % perline; if (!longcols) longcols = perline; start_shortcol = longcols * nlines; for (i1 = 0; i1 < nlines; i1++) { i = i1; pos = 0; /* Print the next line of output. */ while (1) { print_file_name_and_frills (files + i); tem = length_of_file_name_and_frills (files + i); if (i >= start_shortcol) i--; i += nlines; if (i >= files_index) break; if (i1 == nlines - 1 && i >= start_shortcol) break; indent (pos + tem, pos + max); pos += max; } putchar ('\n'); } } \f print_horizontal () { int i; int max; int tem; int perline; int pos; /* Compute the maximum file name length. */ max = 0; for (i = 0; i < files_index; i++) { tem = length_of_file_name_and_frills (files + i); if (tem > max) max = tem; } /* Allow two spaces between names. */ max += 2; perline = line_length / max; pos = 0; tem = 0; for (i = 0; i < files_index; i++) { if (i != 0) { if (i % perline == 0) { putchar ('\n'); pos = 0; } else { indent (pos + tem, pos + max); pos += max; } } print_file_name_and_frills (files + i); tem = length_of_file_name_and_frills (files + i); } putchar ('\n'); } \f print_with_commas () { int i; int col, ocol; col = 0; for (i = 0; i < files_index; i++) { ocol = col; col += length_of_file_name_and_frills (files + i); if (i + 1 < files_index) col += 2; /* For the comma and space */ if (ocol != 0 && col > line_length) { putchar ('\n'); col -= ocol; } print_file_name_and_frills (files + i); if (i + 1 < files_index) { putchar (','); putchar (' '); } } putchar ('\n'); } \f /* Translate userid to login name, with cache. */ struct userid { int uid; char *name; struct userid *next; }; struct userid *user_alist; char * getuser (uid) int uid; { register struct userid *tail; struct passwd *pwent; char usernum_string[20]; for (tail = user_alist; tail; tail = tail->next) if (tail->uid == uid) return tail->name; pwent = getpwuid (uid); tail = (struct userid *) xmalloc (sizeof (struct userid)); tail->uid = uid; tail->next = user_alist; if (pwent == 0) { sprintf (usernum_string, "%d", uid); tail->name = copystring (usernum_string); } else tail->name = copystring (pwent->pw_name); user_alist = tail; return tail->name; } /* Translate gid to group name, with cache. */ /* We use the same struct as for userids. */ struct userid *group_alist; char * getgroup (uid) int uid; { register struct userid *tail; struct group *grent; char usernum_string[20]; for (tail = group_alist; tail; tail = tail->next) if (tail->uid == uid) return tail->name; grent = getgrgid (uid); tail = (struct userid *) xmalloc (sizeof (struct userid)); tail->uid = uid; tail->next = user_alist; if (grent == 0) { sprintf (usernum_string, "%d", uid); tail->name = copystring (usernum_string); } else tail->name = copystring (grent->gr_name); group_alist = tail; return tail->name; } /* Assuming cursor is at position FROM, indent up to position TO. */ indent (from, to) int from, to; { while (from < to) { if ((to / 8) > (from / 8)) { putchar ('\t'); from += 8 - from % 8; } else { putchar (' '); from++; } } } /* Low level subroutines of general use, not specifically related to the task of listing a directory. */ xrealloc (obj, size) int obj, size; { int val = realloc (obj, size); if (!val) fatal ("memory exhausted", 0, 0); return val; } xmalloc (size) int size; { int val = malloc (size); if (!val) fatal ("memory exhausted", 0, 0); return val; } fatal (string, arg, arg2) char *string; int arg, arg2; { error (string, arg, arg2); exit (1); } error (string, arg, arg2) char *string; int arg, arg2; { fprintf (stderr, "ls: "); fprintf (stderr, string, arg, arg2); fprintf (stderr, "\n"); } perror_with_name (name) char *name; { int err = errno; if (err <= sys_nerr) error ("%s: %s", name, sys_errlist[err]); else error ("%s: %s", name, "unknown system error"); } char * copystring (string) char *string; { int len = strlen (string); char *new = (char *) xmalloc (len + 1); strcpy (new, string); new[len] = 0; return new; } /* Emacs C-mode formatting variables: Local Variables: c-argdecl-indent: 5 c-brace-imaginary-offset: 0 c-brace-offset: 0 c-continued-brace-offset: 0 c-continued-statement-offset: 2 c-indent-level: 2 c-label-offset: -2 End: */