001package org.unix4j.unix.ls;
002
003import org.unix4j.line.SimpleLine;
004import org.unix4j.processor.LineProcessor;
005import org.unix4j.util.FileUtil;
006import org.unix4j.util.StringUtil;
007
008import java.io.File;
009import java.io.IOException;
010import java.nio.file.Files;
011import java.nio.file.attribute.PosixFileAttributeView;
012import java.nio.file.attribute.PosixFilePermission;
013import java.util.Calendar;
014import java.util.List;
015import java.util.Locale;
016import java.util.Set;
017
018/**
019 * Formatter for long file output including permissions, owner, size etc. An
020 * example output with long format looks like this:
021 *
022 * <pre>
023 * -rw-r--r--@  1 myself  geeks   1.6K May  8 23:20 EFMTool Test Case.zip
024 * -rw-r--r--   1 myself  myself  2.6M May  5  2011 EM_FX_RANK_2011-1.pages
025 * -rw-r--r--   1 myself  geeks   466K May  5  2011 EM_FX_RANK_2011-1.pdf
026 * -rw-r--r--   1 myself  geeks   1.3M May  5  2011 EM_FX_RANK_2011.pdf
027 * -rw-r--r--@  1 myself  geeks   118M Apr 11  2011 PS_AIO_07_B110_USW_Full_Mac_WW_11.dmg
028 * drwxrwxrwx   5 myself  geeks   170B Mar  6  2011 Private Tax
029 * -rw-r--r--   1 myself  myself   37M Apr 26  2011 Scanned Image 111160000.pdf
030 * -rw-r--r--   1 myself  mysel    37M Apr 26  2011 Scanned Image 111160008.pdf
031 * drwxrwxrwx   9 myself  geeks   306B Apr 19  2011 aevum
032 * drwxr-xr-x   3 myself  geeks   102B Dec 21  2011 baby
033 * drwxr-xr-x   4 myself  geeks   136B May 18  2011 bills
034 * drwxrwxrwx  17 myself  geeks   578B Jun 15 11:21 cv
035 * </pre>
036 */
037final class LsFormatterLong implements LsFormatter {
038
039        private final ThreadLocal<Calendar> calendar = ThreadLocal.withInitial(Calendar::getInstance);
040        private final ThreadLocal<Integer> maxSizeStringLength = ThreadLocal.withInitial(() -> Integer.valueOf(0));
041
042        @Override
043        public boolean writeFormatted(File relativeTo, File file, LsArguments args, LineProcessor output) {
044                return output.processLine(
045                                new SimpleLine(
046                                                getFilePermissions(file, args) + ' ' +
047                                                                getOwner(file, args) + ' ' +
048                                                                getGroup(file, args) + ' ' +
049                                                                getSize(file, args) + ' ' +
050                                                                getDateTime(file, args) + ' ' +
051                                                                getName(relativeTo, file, args)
052                                )
053                );
054        }
055
056        LsFormatterLong(List<File> directoryFiles, LsArguments args) {
057                maxSizeStringLength.set(calculateMaxSizeStringLength(directoryFiles, args));
058        }
059
060        private String getOwner(File file, LsArguments args) {
061                try {
062                        final String owner = Files.getOwner(file.toPath()).getName();
063                        return StringUtil.fixSizeString(7, true, owner);
064                } catch (IOException e) {
065                        return StringUtil.fixSizeString(7, true, "???");
066                }
067        }
068
069        private String getGroup(File file, LsArguments args) {
070                try {
071                        final PosixFileAttributeView view = Files.getFileAttributeView(file.toPath(), PosixFileAttributeView.class);
072                        final String group = view.readAttributes().group().getName();
073                        return StringUtil.fixSizeString(7, true, group);
074                } catch (Exception e) {
075                        return StringUtil.fixSizeString(7, true, "???");
076                }
077        }
078
079        private String getFilePermissions(File file, LsArguments args) {
080                try {
081                        final Set<PosixFilePermission> perms = Files.getPosixFilePermissions(file.toPath());
082                        return (file.isDirectory() ? "d" : "-") +
083                                (perms.contains(PosixFilePermission.OWNER_READ) ? 'r' : '-') +
084                                (perms.contains(PosixFilePermission.OWNER_WRITE) ? 'w' : '-') +
085                                (perms.contains(PosixFilePermission.OWNER_EXECUTE) ? 'x' : '-') +
086                                (perms.contains(PosixFilePermission.GROUP_READ) ? 'r' : '-') +
087                                (perms.contains(PosixFilePermission.GROUP_WRITE) ? 'w' : '-') +
088                                (perms.contains(PosixFilePermission.GROUP_EXECUTE) ? 'x' : '-') +
089                                (perms.contains(PosixFilePermission.OTHERS_READ) ? 'r' : '-') +
090                                (perms.contains(PosixFilePermission.OTHERS_WRITE) ? 'w' : '-') +
091                                (perms.contains(PosixFilePermission.OTHERS_EXECUTE) ? 'x' : '-');
092                } catch (Exception e) {
093                        final boolean r = file.canRead();
094                        final boolean w = file.canWrite();
095                        final boolean x = file.canExecute();
096                        return (file.isDirectory() ? "d" : "-") +
097                                        //owner
098                                        (r ? 'r' : '-') +
099                                        (w ? 'w' : '-') +
100                                        (x ? 'x' : '-') +
101                                        //group
102                                        (r ? '.' : '-') +
103                                        (w ? '.' : '-') +
104                                        (x ? '.' : '-') +
105                                        //other
106                                        (r ? '.' : '-') +
107                                        (w ? '.' : '-') +
108                                        (x ? '.' : '-');
109                }
110        }
111
112        private long getLastModifiedMS(File file, LsArguments args) {
113                try {
114                        return Files.getLastModifiedTime(file.toPath()).toMillis();
115                } catch (Exception e) {
116                        return file.lastModified();
117                }
118        }
119
120        private String getDateTime(File file, LsArguments args) {
121                final Calendar cal = calendar.get();
122                cal.setTimeInMillis(System.currentTimeMillis());//set current date to get current year below
123                final int curYear = cal.get(Calendar.YEAR);
124                cal.setTimeInMillis(getLastModifiedMS(file, args));
125                final int fileYear = cal.get(Calendar.YEAR);
126                final String month = cal.getDisplayName(Calendar.MONTH, Calendar.SHORT, Locale.getDefault());
127
128                final String sM = StringUtil.fixSizeString(3, true, month);
129                final String sD = StringUtil.fixSizeString(2, false, ' ', cal.get(Calendar.DAY_OF_MONTH));
130                if (curYear == fileYear) {
131                        final String sHou = StringUtil.fixSizeString(2, false, '0', cal.get(Calendar.HOUR_OF_DAY));
132                        final String sMin = StringUtil.fixSizeString(2, false, '0', cal.get(Calendar.MINUTE));
133                        return sM + ' ' + sD + ' ' + sHou + ':' + sMin;
134                } else {
135                        final String sY = StringUtil.fixSizeString(5, false, ' ', fileYear);
136                        return sM + ' ' + sD + ' ' + sY;
137                }
138        }
139
140        protected String getName(File relativeTo, File file, LsArguments args) {
141                return FileUtil.getRelativePath(relativeTo, file);
142        }
143
144        private String getSize(File file, LsArguments args) {
145                long size;
146                try {
147                        size = Files.size(file.toPath());
148                } catch (Exception e) {
149                        size = file.length();
150                }
151                final String sizeString = LsCommand.getSizeString(args, size);
152                return StringUtil.fixSizeString(maxSizeStringLength.get(), false, sizeString);
153        }
154
155        private static int calculateMaxSizeStringLength(List<File> directoryFiles, LsArguments args) {
156                int maxSizeStringLength = 0;
157                for (final File f : directoryFiles) {
158                        if (f.isFile()) {
159                                if (f.isFile()) {
160                                        final String sizeString = LsCommand.getSizeString(args, f.length());
161                                        maxSizeStringLength = Math.max(maxSizeStringLength, sizeString.length());
162                                }
163                        }
164                }
165                return maxSizeStringLength;
166        }
167}