'strict' import { file } from 'bun' import * as fs from 'fs' import * as path from 'path' let paths: string[] = [] let options = [] const defaultColors = { di: [1, 34], ln: [1, 36], ex: [1, 32] } for(const arg of process.argv.slice(3)){ if(arg.startsWith('--')){ options.push(arg) } else if(arg.startsWith('-')){ for(let option of arg.slice(1)){ options.push('-' + option); } } else{ paths.push(arg) } } if(paths.length == 0){ paths = [ process.cwd() ]; } try{ listFiles(paths[0], options) } catch(e){ switch(e.code){ case 'ENOENT': console.error(`path '${paths[0]}' does not exist`); break; default: console.error(`ls: ${e}`) } } function listFiles(directory: string, options: string[]){ if(!fs.existsSync(directory)) { console.error('Path not found') return; } let files = ['.', '..', ...fs.readdirSync(paths[0])] if(!options.includes('-a')) { files = files.filter(file => !file.startsWith('.') || file === '.' || file === '..') } let colorList: typeof defaultColors if(process.env.ls_color) { colorList = { ...defaultColors, ...JSON.parse(process.env.ls_color)} } else { colorList = defaultColors } if(options.includes('-l')){ console.log(`total ${file.length}`) for(let file of files){ let result = '' const stats = fs.lstatSync(path.join(directory, file)); let color = '' let defaultColor = '\x1b[0m' let fileType = '?' if(stats.isFile()){ fileType = '-'; if(stats.mode & 0o100){ //owner can execute color = colorList.ex ? `\x1b[${colorList.ex[0]}m\x1b[${colorList.ex[1]}m` : '\x1b[0m' } } else if (stats.isDirectory()) { fileType = 'd'; color = colorList.di ? `\x1b[${colorList.di[0]}m\x1b[${colorList.di[1]}m` : '\x1b[0m' } else if (stats.isSymbolicLink()){ fileType = 'l' color = colorList.ln ? `\x1b[${colorList.ln[0]}m\x1b[${colorList.ln[1]}m` : '\x1b[0m' } result += fileType; const permissions = 'xwr' for(let i=8;i>=0;i--){ result += parseInt(Math.pow(2,i).toString(2)) & stats.mode ? permissions[i%3] : '-' } if(0o1000 & stats.mode){ // check sticky bit color = '\x1b[40m\x1b[92m\x1b[7m' } const date = stats.mtime.toDateString().split(' '); result += ` ${usernameFromUID(stats.uid)}\t${groupnameFromGID(stats.gid)}\t${stats.size}\t${date[2]} ${date[1]}. ${options.includes('--color') ? color : ''}${file}${defaultColor}` console.log(result) } } else{ const defaultColor = '\x1b[0m'; if(options.includes('--color')){ let result = '' for(let file of files){ const stats = fs.lstatSync(path.join(directory, file)); let color = defaultColor if(0o1000 & stats.mode){ // check sticky bit color = '\x1b[40m\x1b[92m\x1b[7m' } else if(stats.isDirectory()){ color = colorList.di ? `\x1b[${colorList.di[1]}m\x1b[${colorList.di[0]}m` : defaultColor } else if (stats.isSymbolicLink()){ color = colorList.ln ? `\x1b[${colorList.ln[1]}m\x1b[${colorList.ln[0]}m` : defaultColor } result += color + file + ' ' + defaultColor; } console.log(result) }else{ console.log(files.join(' ')) } } } function usernameFromUID(uid: string | number){ if(uid == 0) return 'root' if(uid != 0) return 'unknown user' //TODO: add support for multiple users to the os return uid } function groupnameFromGID(uid: string | number){ if(uid == 0) return 'root' if(uid != 0) return 'unknown group' //TODO: add support for multiple groups to the os return uid }