1 #define _GNU_SOURCE
2
3 #include <stdio.h>
4 #include <string.h>
5 #include <stdlib.h>
6 #include <errno.h>
7 #include <unistd.h>
8
9 #define TEST_THIS_FILE (1)
10
11 #if TEST_THIS_FILE
12 #include <unistd.h>
13 #define MY_MAX(a, b) ((a > b) ? (a) : (b))
14 #define MY_CEIL(a, b) ((0 == (a % b)) ? (a / b) : ((a / b) + 1))
15 #define my_free(a) do{if(a) {free(a); (a) = NULL;}} while(0)
16 #define STR_SAFE(str) ((NULL == (str)) ? "" : (str))
17 // #define my_print_ln(fmt, arg...) do { printf("(%s|%d)"fmt"\r\n", __func__, __LINE__, ##arg); } while(0)
18 #define my_print_ln(fmt, arg...) do { ; } while(0)
19 #else
20 #include "my_platform_common.h"
21 #endif
22
23
24
25
26 #define SPAN_LEN (2) // 列间空格个数
27
28 typedef struct _last_max_len_of_line_t {
29 unsigned int len;
30 unsigned int col_total;
31 }last_max_len_of_line_t;
32
33 /****************************************
34 计算第几列宽度
35
36 参数 :
37 names : 待打印的字符串数组
38 names_count : 待打印的字符串数组长度
39 col_total : 打印列数
40 index_col : 第几列
41
42 return : 非NULL 成功
43 NULL 失败
44
45 ****************************************/
46 static unsigned int col_len_max(char **names, unsigned int names_count, unsigned int col_total, int index_col)
47 {
48 unsigned int curr_col_len_max = 0;
49 unsigned int index_name = 0;
50 int row_count = 0;
51
52 if (NULL == names || 0 >= names_count || 0 >= col_total || 0 > index_col) {
53 return -1;
54 }
55
56 row_count = MY_CEIL(names_count, col_total);
57
58 // 输出每一行
59 for (index_name = 0; index_name < MY_CEIL(names_count, col_total); index_name++)
60 {
61 if (names_count - 1 < index_name + index_col * row_count) {
62 break;
63 }
64
65 curr_col_len_max = MY_MAX(curr_col_len_max, strlen(names[index_name + index_col * row_count]));
66 }
67
68 if (0 < curr_col_len_max) {
69 curr_col_len_max += SPAN_LEN;
70 }
71
72 //my_print_ln("ret=%d", curr_col_len_max);
73
74 return curr_col_len_max;
75 }
76
77 /****************************************
78 获取第几行字符串
79
80 参数 :
81 names : 待打印的字符串数组
82 names_count : 待打印的字符串数组长度
83 col_total : 打印列数
84 index_row : 第几行
85 width_each : 每列宽度数组
86
87 return : 非NULL 成功
88 NULL 失败
89
90 ****************************************/
91 static char *make_buf_of_row(char **names, unsigned int names_count, unsigned int col_total, int index_row, int *width_each)
92 {
93 char *buf_row = NULL;
94 char *buf_row_tmp = NULL;
95 char *format = NULL;
96 int row_count = 0;
97 unsigned int i = 0;
98
99 if (NULL == names || 0 >= names_count || 0 >= col_total || 0 > index_row || NULL == width_each ) {
100 return NULL;
101 }
102
103 row_count = MY_CEIL(names_count, col_total);
104
105 for (i = 0; i < col_total; i++, buf_row_tmp = buf_row) {
106 if (names_count - 1 < index_row + i * row_count) {
107 break;
108 }
109
110 asprintf(&format, "%%s%%-%ds", width_each[i]);
111 if (NULL == format) {
112 my_free(buf_row);
113 my_free(buf_row_tmp);
114 return NULL;
115 }
116 asprintf(&buf_row, format, STR_SAFE(buf_row_tmp), names[index_row + i * row_count]);
117 if (NULL == buf_row) {
118 my_free(format);
119 my_free(buf_row_tmp);
120 return NULL;
121 }
122
123 my_free(format);
124 my_free(buf_row_tmp);
125 }
126
127 // my_print_ln("ret=%s", buf_row);
128 return buf_row;
129 }
130
131 static ssize_t _write(int fd, const void *buf, size_t count) {
132 size_t written = 0;
133 ssize_t thisTime = 0;
134 while (count != written) {
135 thisTime = write(fd, (char *)buf + written, count - written);
136 if (thisTime == -1) {
137 if (errno == EINTR)
138 continue;
139 else
140 return -1;
141 }
142 written += thisTime;
143 }
144 return written;
145 }
146
147 /****************************************
148 按n列打印字符串数组
149
150 参数 :
151 sockfd : 打印输出到文件描述符
152 names : 待打印的字符串数组
153 names_count : 待打印的字符串数组长度
154 col_total : 打印列数
155
156 return : 0 成功
157 -1 失败
158
159 ****************************************/
160 static int print_list(int sockfd, char **names, unsigned int names_count, unsigned int col_total)
161 {
162 unsigned int index_col = 0;
163 unsigned int index_row = 0;
164 int *width_each_col = NULL;
165
166 if (NULL == names || 0 >= names_count || 0 >= col_total) {
167 return -1;
168 }
169
170 width_each_col = calloc(col_total, sizeof(int));
171 if (NULL == width_each_col) {
172 return -1;
173 }
174
175 // 计算每列的宽度
176 for (index_col = 0; index_col < col_total; index_col++)
177 {
178 width_each_col[index_col] = col_len_max(names, names_count, col_total, index_col);
179 }
180
181 // 输出每一行
182 for (index_row = 0; index_row < MY_CEIL(names_count, col_total); index_row++)
183 {
184 char *buf_each_line = NULL;
185
186 buf_each_line = make_buf_of_row(names, names_count, col_total, index_row, width_each_col);
187 if (NULL == buf_each_line) {
188 continue;
189 }
190
191 _write(sockfd, buf_each_line, strlen(buf_each_line));
192 _write(sockfd, "\r\n", 2);
193
194 my_free(buf_each_line);
195 }
196
197 my_free(width_each_col);
198
199 return 0;
200 }
201
202 /****************************************
203 指定假设打印n列, 计算单行会输出的长度
204
205 参数 :
206 names : 待打印的字符串数组
207 names_count : 待打印的字符串数组长度
208 col_total : 打印列数
209
210 return : >=0 单行输出的长度
211
212 ****************************************/
213 static unsigned int cols_count_sum(char **names, unsigned int names_count, unsigned int col_total)
214 {
215 unsigned int chars_count_per_line = 0;
216 unsigned int index_col = 0;
217 unsigned int width_each_col = 0;
218
219 if (NULL == names || 0 >= names_count || 0 >= col_total) {
220 return 0;
221 }
222
223 // 计算每列的宽度
224 for (index_col = 0; index_col < col_total; index_col++)
225 {
226 width_each_col = col_len_max(names, names_count, col_total, index_col);
227 chars_count_per_line += width_each_col;
228 // my_print_ln("[col_total=%d]sum=%d,[%d]=%d", col_total, chars_count_per_line, index_col, width_each_col);
229 if (0 >= width_each_col) {
230 break;
231 }
232 }
233
234 return chars_count_per_line;
235 }
236
237 /****************************************
238 像ls命令那样优雅打印
239
240 参数 :
241 sockfd : 打印输出到文件描述符
242 names : 待打印的字符串数组
243 names_count : 待打印的字符串数组长度
244
245 return : 成功返回0
246 失败返回-1
247
248 ****************************************/
249 int print_better(int sockfd, char **names, unsigned int names_count)
250 {
251 last_max_len_of_line_t last = {0, 0};
252 unsigned int chars_count_per_line = 0;
253 int perfect_col_total = 0; // 10 11 NOt-OK
254 unsigned int col_total = 0;
255 unsigned int width = 0;
256
257 if (NULL == names || 0 >= names_count) {
258 return -1;
259 }
260
261 // 0. 获取命令行宽度
262
263 width = 205;
264 // 1. 计算出最合适的列数
265 for (col_total = 1; col_total < width; col_total++)
266 {
267 chars_count_per_line = cols_count_sum(names, names_count, col_total);
268 if (width < chars_count_per_line) {
269 break;
270 }
271
272 if (chars_count_per_line > last.len) {
273 last.len = chars_count_per_line;
274 last.col_total = col_total;
275 }
276
277 // my_print_ln("==========");
278 }
279
280 perfect_col_total = (0 >= last.col_total ) ? 1 : last.col_total;
281
282 // my_print_ln("perfect_col_total=%d", perfect_col_total);
283
284 // 2. 打印
285 print_list(sockfd, names, names_count, perfect_col_total);
286
287 return 0;
288 }
289
290 #if TEST_THIS_FILE
291 char *names[] = {
292 ".",
293 "..",
294 "AUTHORS",
295 "autom4te.cache",
296 "bootstrap",
297 "bootstrap.conf",
298 "build-aux",
299 "cfg.mk",
300 "configure.ac",
301 "COPYING",
302 "dist-check.mk",
303 "doc",
304 ".git",
305 ".gitattributes",
306 ".github",
307 ".gitignore",
308 ".gitmodules",
309 "gl",
310 "gnulib",
311 "gnulib-tests",
312 "HACKING",
313 "init.cfg",
314 "lib",
315 "m4",
316
317 /*
318 ".mailmap",
319 "Makefile.am",
320 "man",
321 "NEWS",
322 "po",
323 ".prev-version",
324 "README",
325 "README-hacking",
326 "README-package-renamed-to-coreutils",
327 "README-prereq",
328 "README-release",
329 "README-valgrind",
330 "scripts",
331 "src",
332 "tests",
333 "thanks-gen",
334 "THANKS.in",
335 "THANKStt.in",
336 "TODO",
337 ".vg-suppressions",
338 ".x-update-copyright",
339 */
340
341 };
342
343 int main(int argc, char const *argv[])
344 {
345 print_better(STDIN_FILENO, names, sizeof(names) / sizeof(char *));
346
347 return 0;
348 }
349 #endif
rm ./a.out -f ; gcc print_better.c ; ./a.out