我正在尝试编写ls命令。我有以下功能可以打印每个文件名:
int ft_list(const char *filename)
{
DIR *dirp;
struct dirent *dir;
if (!(dirp = opendir(filename)))
return (-1);
while ((dir = readdir(dirp)))
{
if (dir->d_name[0] != '.')
ft_putendl(dir->d_name);
}
closedir(dirp);
return (0);
}
ls命令将文件组织成适合屏幕宽度的列。我已经读过它,并且我认为它使用了ioctl
标准库函数,但是我找不到任何细节。我到底该怎么做?
答案 0 :(得分:2)
为了将文件排列成列,您需要确定终端窗口的当前宽度。在许多类似Unix的系统(包括Linux和OS X)上,实际上可以使用ioctl
选择器来使用TIOCGWINSZ
来获取该信息。
这正是ls
所做的(在支持ioctl
请求的系统上),一旦确定标准输出是终端(除非使用{{1 }}标志)。如果无法确定端子宽度,则使用80。
这是如何获取信息的快速示例。 (在Linux系统上,您可能可以通过键入-1
来找到详细信息。)
为简单起见,以下代码假定man tty_ioctl
是文件描述符1。回想起来,stdout
会更好。如果要检查任意打开的文件,则需要使用FILE_STDOUT
来获取fileno
的fd编号。
FILE*
自编写上述示例以来,我一直跟踪/* This must come before any include, in order to see the
* declarations of Posix functions which are not in standard C
*/
#define _XOPEN_SOURCE 700
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
/* If stdout is a terminal and it is possible to find out how many
* columns its window has, return that number. Otherwise, return -1
*/
int window_get_columns(void) {
struct winsize sizes;
int cols = -1;
if (isatty(1)) {
/* Only try this if stdout is a terminal */
int status = ioctl(1, TIOCGWINSZ, &sizes);
if (status == 0) {
cols = sizes.ws_col;
}
}
return cols;
}
/* Example usage */
/* Print a line consisting of 'len' copies of the character 'ch' */
void print_row(int len, int ch) {
for (int i = 0; i < len; ++i) putchar(ch);
putchar('\n');
}
int main(int argc, char* argv[]) {
/* Print the first argument centred in the terminal window,
* if standard output is a terminal
*/
if (argc <= 1) return 1; /* No argument, nothing to do */
int width = window_get_columns();
/* If we can't figure out the width of the screen, just use the
* width of the string
*/
int arglen = strlen(argv[1]);
if (width < 0) width = arglen;
int indent = (width - arglen) / 2;
print_row(width - 1, '-');
printf("%*s\n", indent + arglen, argv[1]);
print_row(width - 1, '-');
return 0;
}
的Gnu实现的来源; here
ls
的调用