像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