像linux ls命令一样优雅地打印

 

 

  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

 

 

posted @ 2021-06-18 21:18  LiuYanYGZ  阅读(105)  评论(0编辑  收藏  举报