Для того чтобы расположить файлы в столбцах, вам необходимо выяснить текущую ширину окна терминала. Во многих Unix-подобных системах (включая Linux и OS X) вы действительно можете использовать ioctl
для получения этой информации, используя селектор TIOCGWINSZ
.
Это именно то, что делает ls
(в системах, которые поддерживают запрос ioctl
), когда он определил, что стандартный вывод является терминалом (если только одностолбцовый формат не принудительно установлен с флагом -1
). Если он не может определить ширину терминала, он использует 80.
Вот краткий пример того, как получить информацию. (В системах Linux вы можете найти подробности, набрав man tty_ioctl
).
Для простоты следующий код предполагает, что stdout
является файловым дескриптором 1. В ретроспективе, 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 ls
; его (несколько более осторожный) вызов ioctl
будет замечен здесь