mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 20:12:43 +00:00 
			
		
		
		
	Add TIOCGWINSZ ioctl so userland can determine terminal geometry.
(Don't) use this to implement short-form output in ls. I'm too tired to make a nice column formatting algorithm. I just wanted something concise when I type "ls".
This commit is contained in:
		
							parent
							
								
									f5a83c4d8a
								
							
						
					
					
						commit
						ac7a60225e
					
				
					 11 changed files with 208 additions and 63 deletions
				
			
		|  | @ -109,6 +109,7 @@ int TTY::ioctl(Process& process, unsigned request, unsigned arg) | |||
| { | ||||
|     pid_t pgid; | ||||
|     Unix::termios* tp; | ||||
|     Unix::winsize* ws; | ||||
| 
 | ||||
|     if (process.tty() != this) | ||||
|         return -ENOTTY; | ||||
|  | @ -136,7 +137,20 @@ int TTY::ioctl(Process& process, unsigned request, unsigned arg) | |||
|             return -EFAULT; | ||||
|         set_termios(*tp); | ||||
|         return 0; | ||||
|     case TIOCGWINSZ: | ||||
|         ws = reinterpret_cast<Unix::winsize*>(arg); | ||||
|         if (!process.validate_write(ws, sizeof(Unix::winsize))) | ||||
|             return -EFAULT; | ||||
|         ws->ws_row = m_rows; | ||||
|         ws->ws_col = m_columns; | ||||
|         return 0; | ||||
|     } | ||||
|     ASSERT_NOT_REACHED(); | ||||
|     return -EINVAL; | ||||
| } | ||||
| 
 | ||||
| void TTY::set_size(unsigned short columns, unsigned short rows) | ||||
| { | ||||
|     m_rows = rows; | ||||
|     m_columns = columns; | ||||
| } | ||||
|  |  | |||
|  | @ -39,6 +39,9 @@ public: | |||
| 
 | ||||
|     virtual String ttyName() const = 0; | ||||
| 
 | ||||
|     unsigned short rows() const { return m_rows; } | ||||
|     unsigned short columns() const { return m_columns; } | ||||
| 
 | ||||
|     void set_pgid(pid_t pgid) { m_pgid = pgid; } | ||||
|     pid_t pgid() const { return m_pgid; } | ||||
| 
 | ||||
|  | @ -50,6 +53,7 @@ public: | |||
| 
 | ||||
| protected: | ||||
|     virtual void onTTYWrite(const byte*, size_t) = 0; | ||||
|     void set_size(unsigned short columns, unsigned short rows); | ||||
| 
 | ||||
|     TTY(unsigned major, unsigned minor); | ||||
|     void emit(byte); | ||||
|  | @ -63,5 +67,7 @@ private: | |||
|     DoubleBuffer m_buffer; | ||||
|     pid_t m_pgid { 0 }; | ||||
|     Unix::termios m_termios; | ||||
|     unsigned short m_rows { 0 }; | ||||
|     unsigned short m_columns { 0 }; | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -17,13 +17,13 @@ void VirtualConsole::get_vga_cursor(byte& row, byte& column) | |||
|     value = IO::in8(0x3d5) << 8; | ||||
|     IO::out8(0x3d4, 0x0f); | ||||
|     value |= IO::in8(0x3d5); | ||||
|     row = value / 80; | ||||
|     column = value % 80; | ||||
|     row = value / columns(); | ||||
|     column = value % columns(); | ||||
| } | ||||
| 
 | ||||
| void VirtualConsole::flush_vga_cursor() | ||||
| { | ||||
|     word value = m_current_vga_start_address + (m_cursor_row * 80 + m_cursor_column); | ||||
|     word value = m_current_vga_start_address + (m_cursor_row * columns() + m_cursor_column); | ||||
|     IO::out8(0x3d4, 0x0e); | ||||
|     IO::out8(0x3d5, MSB(value)); | ||||
|     IO::out8(0x3d4, 0x0f); | ||||
|  | @ -41,14 +41,15 @@ VirtualConsole::VirtualConsole(unsigned index, InitialContents initial_contents) | |||
|     : TTY(4, index) | ||||
|     , m_index(index) | ||||
| { | ||||
|     set_size(80, 25); | ||||
|     s_consoles[index] = this; | ||||
|     m_buffer = (byte*)kmalloc_eternal(80 * 25 * 2); | ||||
|     m_buffer = (byte*)kmalloc_eternal(rows() * columns() * 2); | ||||
|     if (initial_contents == AdoptCurrentVGABuffer) { | ||||
|         memcpy(m_buffer, s_vga_buffer, 80 * 25 * 2); | ||||
|         memcpy(m_buffer, s_vga_buffer, rows() * columns() * 2); | ||||
|         get_vga_cursor(m_cursor_row, m_cursor_column); | ||||
|     } else { | ||||
|         word* line_mem = reinterpret_cast<word*>(m_buffer); | ||||
|         for (word i = 0; i < 80 * 25; ++i) | ||||
|         for (word i = 0; i < rows() * columns(); ++i) | ||||
|             line_mem[i] = 0x0720; | ||||
|     } | ||||
| } | ||||
|  | @ -60,7 +61,7 @@ VirtualConsole::~VirtualConsole() | |||
| void VirtualConsole::clear() | ||||
| { | ||||
|     word* linemem = m_active ? (word*)s_vga_buffer : (word*)m_buffer; | ||||
|     for (word i = 0; i < 80 * 25; ++i) | ||||
|     for (word i = 0; i < rows() * columns(); ++i) | ||||
|         linemem[i] = 0x0720; | ||||
|     if (m_active) | ||||
|         set_vga_start_row(0); | ||||
|  | @ -91,11 +92,11 @@ void VirtualConsole::set_active(bool b) | |||
| 
 | ||||
|     m_active = b; | ||||
|     if (!m_active) { | ||||
|         memcpy(m_buffer, m_current_vga_window, 80 * 25 * 2); | ||||
|         memcpy(m_buffer, m_current_vga_window, rows() * columns() * 2); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     memcpy(s_vga_buffer, m_buffer, 80 * 25 * 2); | ||||
|     memcpy(s_vga_buffer, m_buffer, rows() * columns() * 2); | ||||
|     set_vga_start_row(0); | ||||
|     flush_vga_cursor(); | ||||
| 
 | ||||
|  | @ -325,16 +326,16 @@ void VirtualConsole::execute_escape_sequence(byte final) | |||
| void VirtualConsole::clear_vga_row(word row) | ||||
| { | ||||
|     word* linemem = (word*)&m_current_vga_window[row * 160]; | ||||
|     for (word i = 0; i < 80; ++i) | ||||
|     for (word i = 0; i < columns(); ++i) | ||||
|         linemem[i] = 0x0720; | ||||
| } | ||||
| 
 | ||||
| void VirtualConsole::scroll_up() | ||||
| { | ||||
|     if (m_cursor_row == (m_rows - 1)) { | ||||
|     if (m_cursor_row == (rows() - 1)) { | ||||
|         if (m_active) { | ||||
|             if (m_vga_start_row >= 160) { | ||||
|                 memcpy(s_vga_buffer, m_current_vga_window + 160, 80 * 24 * 2); | ||||
|                 memcpy(s_vga_buffer, m_current_vga_window + 160, (rows() - 1) * columns() * 2); | ||||
|                 set_vga_start_row(0); | ||||
|                 clear_vga_row(24); | ||||
|             } else { | ||||
|  | @ -344,7 +345,7 @@ void VirtualConsole::scroll_up() | |||
|         } else { | ||||
|             memcpy(m_buffer, m_buffer + 160, 160 * 24); | ||||
|             word* linemem = (word*)&m_buffer[24 * 160]; | ||||
|             for (word i = 0; i < 80; ++i) | ||||
|             for (word i = 0; i < columns(); ++i) | ||||
|                 linemem[i] = 0x0720; | ||||
|         } | ||||
|     } else { | ||||
|  | @ -355,8 +356,8 @@ void VirtualConsole::scroll_up() | |||
| 
 | ||||
| void VirtualConsole::set_cursor(unsigned row, unsigned column) | ||||
| { | ||||
|     ASSERT(row < m_rows); | ||||
|     ASSERT(column < m_columns); | ||||
|     ASSERT(row < rows()); | ||||
|     ASSERT(column < columns()); | ||||
|     m_cursor_row = row; | ||||
|     m_cursor_column = column; | ||||
|     if (m_active) | ||||
|  | @ -365,8 +366,8 @@ void VirtualConsole::set_cursor(unsigned row, unsigned column) | |||
| 
 | ||||
| void VirtualConsole::put_character_at(unsigned row, unsigned column, byte ch) | ||||
| { | ||||
|     ASSERT(row < m_rows); | ||||
|     ASSERT(column < m_columns); | ||||
|     ASSERT(row < rows()); | ||||
|     ASSERT(column < columns()); | ||||
|     word cur = (row * 160) + (column * 2); | ||||
|     if (m_active) { | ||||
|         word cur = (row * 160) + (column * 2); | ||||
|  | @ -435,7 +436,7 @@ void VirtualConsole::on_char(byte ch) | |||
|     put_character_at(m_cursor_row, m_cursor_column, ch); | ||||
| 
 | ||||
|     ++m_cursor_column; | ||||
|     if (m_cursor_column >= m_columns) | ||||
|     if (m_cursor_column >= columns()) | ||||
|         scroll_up(); | ||||
|     set_cursor(m_cursor_row, m_cursor_column); | ||||
| } | ||||
|  | @ -480,7 +481,7 @@ String VirtualConsole::ttyName() const | |||
| void VirtualConsole::set_vga_start_row(word row) | ||||
| { | ||||
|     m_vga_start_row = row; | ||||
|     m_current_vga_start_address = row * 80; | ||||
|     m_current_vga_start_address = row * columns(); | ||||
|     m_current_vga_window = s_vga_buffer + row * 160; | ||||
|     IO::out8(0x3d4, 0x0c); | ||||
|     IO::out8(0x3d5, MSB(m_current_vga_start_address)); | ||||
|  |  | |||
|  | @ -49,8 +49,6 @@ private: | |||
| 
 | ||||
|     void clear(); | ||||
| 
 | ||||
|     const byte m_rows { 25 }; | ||||
|     const byte m_columns { 80 }; | ||||
|     byte m_cursor_row { 0 }; | ||||
|     byte m_cursor_column { 0 }; | ||||
|     byte m_saved_cursor_row { 0 }; | ||||
|  |  | |||
|  | @ -1,8 +1,8 @@ | |||
| #!/bin/sh | ||||
| 
 | ||||
| if [ "$1" = "b" ]; then | ||||
|     bochs -q -f .bochsrc | ||||
| else | ||||
| if [ "$1" = "q" ]; then | ||||
|     qemu-system-i386 -s -m 32 -drive format=raw,file=.floppy-image,if=floppy -drive format=raw,file=_fs_contents #$@ | ||||
| else | ||||
|     bochs -q -f .bochsrc | ||||
| fi | ||||
| 
 | ||||
|  |  | |||
|  | @ -32,5 +32,7 @@ cp -v ../Userland/strsignal mnt/bin/strsignal | |||
| cp -v ../Userland/mkdir mnt/bin/mkdir | ||||
| sh sync-local.sh | ||||
| cp -v kernel.map mnt/ | ||||
| ln -s dir_a mnt/dir_cur | ||||
| ln -s nowhere mnt/bad_link | ||||
| umount mnt | ||||
| sync | ||||
|  |  | |||
|  | @ -5,6 +5,11 @@ | |||
| 
 | ||||
| __BEGIN_DECLS | ||||
| 
 | ||||
| struct winsize { | ||||
|     unsigned short ws_row; | ||||
|     unsigned short ws_col; | ||||
| }; | ||||
| 
 | ||||
| int ioctl(int fd, unsigned request, ...); | ||||
| 
 | ||||
| __END_DECLS | ||||
|  |  | |||
|  | @ -7,5 +7,6 @@ enum IOCtlNumber { | |||
|     TCSETS, | ||||
|     TCSETSW, | ||||
|     TCSETSF, | ||||
|     TIOCGWINSZ, | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										184
									
								
								Userland/ls.cpp
									
										
									
									
									
								
							
							
						
						
									
										184
									
								
								Userland/ls.cpp
									
										
									
									
									
								
							|  | @ -1,17 +1,97 @@ | |||
| #include <LibC/stdio.h> | ||||
| #include <LibC/unistd.h> | ||||
| #include <LibC/dirent.h> | ||||
| #include <LibC/errno.h> | ||||
| #include <LibC/string.h> | ||||
| #include <stdio.h> | ||||
| #include <unistd.h> | ||||
| #include <dirent.h> | ||||
| #include <errno.h> | ||||
| #include <string.h> | ||||
| #include <getopt.h> | ||||
| #include <sys/ioctl.h> | ||||
| #include <AK/String.h> | ||||
| #include <AK/Vector.h> | ||||
| 
 | ||||
| static int do_dir(const char* path); | ||||
| static int do_dir_short(const char* path); | ||||
| 
 | ||||
| static bool flag_colorize = true; | ||||
| static bool flag_long = false; | ||||
| static bool flag_show_dotfiles = false; | ||||
| 
 | ||||
| int main(int argc, char** argv) | ||||
| { | ||||
|     if (argc == 2) { | ||||
|         return do_dir(argv[1]); | ||||
|     int opt; | ||||
|     while ((opt = getopt(argc, argv, "laG")) != -1) { | ||||
|         switch (opt) { | ||||
|         case 'a': | ||||
|             flag_show_dotfiles = true; | ||||
|             break; | ||||
|         case 'l': | ||||
|             flag_long = true; | ||||
|             break; | ||||
|         case 'G': | ||||
|             flag_colorize = false; | ||||
|             break; | ||||
|         default: | ||||
|             fprintf(stderr, "usage: ls [-lvG] [path]\n"); | ||||
|             return 1; | ||||
|         } | ||||
|     } | ||||
|     return do_dir("."); | ||||
| 
 | ||||
|     const char* path; | ||||
| 
 | ||||
|     if (optind >= argc) | ||||
|         path = "."; | ||||
|     else | ||||
|         path = argv[optind]; | ||||
| 
 | ||||
|     if (flag_long) | ||||
|         return do_dir(path); | ||||
|     return do_dir_short(path); | ||||
| } | ||||
| 
 | ||||
| void get_geometry(unsigned& rows, unsigned& columns) | ||||
| { | ||||
|     struct winsize ws; | ||||
|     ioctl(0, TIOCGWINSZ, &ws); | ||||
|     rows = ws.ws_row; | ||||
|     columns = ws.ws_col; | ||||
| } | ||||
| 
 | ||||
| int print_name(struct stat& st, const char* name, const char* path_for_link_resolution = nullptr) | ||||
| { | ||||
|     int nprinted = strlen(name); | ||||
|     if (!flag_colorize) { | ||||
|         printf("%s", name); | ||||
|     } else { | ||||
|         const char* begin_color = ""; | ||||
|         const char* end_color = "\033[0m"; | ||||
| 
 | ||||
|         if (S_ISLNK(st.st_mode)) | ||||
|             begin_color = "\033[36;1m"; | ||||
|         else if (S_ISDIR(st.st_mode)) | ||||
|             begin_color = "\033[34;1m"; | ||||
|         else if (st.st_mode & 0111) | ||||
|             begin_color = "\033[32;1m"; | ||||
|         else if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) | ||||
|             begin_color = "\033[33;1m"; | ||||
|         printf("%s%s%s", begin_color, name, end_color); | ||||
|     } | ||||
|     if (S_ISLNK(st.st_mode)) { | ||||
|         if (path_for_link_resolution) { | ||||
|             char linkbuf[256]; | ||||
|             ssize_t nread = readlink(path_for_link_resolution, linkbuf, sizeof(linkbuf)); | ||||
|             if (nread < 0) { | ||||
|                 perror("readlink failed"); | ||||
|             } else { | ||||
|                 nprinted += printf(" -> %s", linkbuf); | ||||
|             } | ||||
|         } else { | ||||
|             nprinted += printf("@"); | ||||
|         } | ||||
|     } else if (S_ISDIR(st.st_mode)) { | ||||
|         nprinted += printf("/"); | ||||
|     } else if (st.st_mode & 0111) { | ||||
|         nprinted += printf("*"); | ||||
|     } | ||||
|     return nprinted; | ||||
| } | ||||
| 
 | ||||
| int do_dir(const char* path) | ||||
|  | @ -21,9 +101,11 @@ int do_dir(const char* path) | |||
|         perror("opendir"); | ||||
|         return 1; | ||||
|     } | ||||
|     bool colorize = true; | ||||
|     char pathbuf[256]; | ||||
| 
 | ||||
|     while (auto* de = readdir(dirp)) { | ||||
|         if (de->d_name[0] == '.' && !flag_show_dotfiles) | ||||
|             continue; | ||||
|         sprintf(pathbuf, "%s/%s", path, de->d_name); | ||||
| 
 | ||||
|         struct stat st; | ||||
|  | @ -70,37 +152,63 @@ int do_dir(const char* path) | |||
| 
 | ||||
|         printf(" %10u  ", st.st_size); | ||||
| 
 | ||||
|         const char* beginColor = ""; | ||||
|         const char* endColor = ""; | ||||
|         print_name(st, de->d_name, pathbuf); | ||||
| 
 | ||||
|         if (colorize) { | ||||
|             if (S_ISLNK(st.st_mode)) | ||||
|                 beginColor = "\033[36;1m"; | ||||
|             else if (S_ISDIR(st.st_mode)) | ||||
|                 beginColor = "\033[34;1m"; | ||||
|             else if (st.st_mode & 0111) | ||||
|                 beginColor = "\033[32;1m"; | ||||
|             else if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) | ||||
|                 beginColor = "\033[33;1m"; | ||||
|             endColor = "\033[0m"; | ||||
|         } | ||||
| 
 | ||||
|         printf("%s%s%s", beginColor, de->d_name, endColor); | ||||
| 
 | ||||
|         if (S_ISLNK(st.st_mode)) { | ||||
|             char linkbuf[256]; | ||||
|             ssize_t nread = readlink(pathbuf, linkbuf, sizeof(linkbuf)); | ||||
|             if (nread < 0) { | ||||
|                 perror("readlink failed"); | ||||
|             } else { | ||||
|                 printf(" -> %s", linkbuf); | ||||
|             } | ||||
|         } else if (S_ISDIR(st.st_mode)) { | ||||
|             printf("/"); | ||||
|         } else if (st.st_mode & 0111) { | ||||
|             printf("*"); | ||||
|         } | ||||
|         printf("\n"); | ||||
|     } | ||||
|     closedir(dirp); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| int do_dir_short(const char* path) | ||||
| { | ||||
|     unsigned rows; | ||||
|     unsigned columns; | ||||
|     get_geometry(rows, columns); | ||||
| 
 | ||||
|     DIR* dirp = opendir(path); | ||||
|     if (!dirp) { | ||||
|         perror("opendir"); | ||||
|         return 1; | ||||
|     } | ||||
| 
 | ||||
|     Vector<String> names; | ||||
|     size_t longest_name = 0; | ||||
|     while (auto* de = readdir(dirp)) { | ||||
|         if (de->d_name[0] == '.' && !flag_show_dotfiles) | ||||
|             continue; | ||||
|         names.append(de->d_name); | ||||
|         if (names.last().length() > longest_name) | ||||
|             longest_name = names.last().length(); | ||||
|     } | ||||
|     closedir(dirp); | ||||
| 
 | ||||
|     int printed_on_row = 0; | ||||
| 
 | ||||
|     for (size_t i = 0; i < names.size(); ++i) { | ||||
|         auto& name = names[i]; | ||||
|         struct stat st; | ||||
|         char pathbuf[256]; | ||||
|         sprintf(pathbuf, "%s/%s", path, name.characters()); | ||||
|         int rc = lstat(pathbuf, &st); | ||||
|         if (rc == -1) { | ||||
|             printf("lstat(%s) failed: %s\n", pathbuf, strerror(errno)); | ||||
|             return 2; | ||||
|         } | ||||
| 
 | ||||
|         unsigned nprinted = print_name(st, name.characters()); | ||||
|         unsigned column_width = 14; | ||||
|         printed_on_row += column_width; | ||||
| 
 | ||||
|         for (unsigned i = nprinted; i < column_width; ++i) | ||||
|             printf(" "); | ||||
|         if ((printed_on_row + column_width) >= columns) { | ||||
|             if (i != names.size() - 1) | ||||
|                 printf("\n"); | ||||
|             printed_on_row = 0; | ||||
|         } | ||||
|     } | ||||
|     printf("\n"); | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
|  |  | |||
|  | @ -1,9 +1,15 @@ | |||
| #include <LibC/stdio.h> | ||||
| #include <stdio.h> | ||||
| #include <sys/ioctl.h> | ||||
| 
 | ||||
| int main(int argc, char** argv) | ||||
| { | ||||
|     (void) argc; | ||||
|     (void) argv; | ||||
| 
 | ||||
|     struct winsize ws; | ||||
|     ioctl(0, TIOCGWINSZ, &ws); | ||||
|     printf("Terminal is %ux%u\n", ws.ws_col, ws.ws_row); | ||||
| 
 | ||||
|     printf("Counting to 100000: \033[s"); | ||||
|     for (unsigned i = 0; i <= 100000; ++i) { | ||||
|         printf("\033[u\033[s%u", i); | ||||
|  |  | |||
|  | @ -208,6 +208,10 @@ namespace Unix { | |||
| #define	TCSADRAIN	1 | ||||
| #define	TCSAFLUSH	2 | ||||
| 
 | ||||
| struct winsize { | ||||
|     unsigned short ws_row; | ||||
|     unsigned short ws_col; | ||||
| }; | ||||
| 
 | ||||
| typedef dword dev_t; | ||||
| typedef dword ino_t; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Andreas Kling
						Andreas Kling