| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * Common code for providing a command line progress bar. | ||
| 3 | * Copyright © 2005-2026, albinoloverats ~ Software Development | ||
| 4 | * email: webmaster@albinoloverats.net | ||
| 5 | * | ||
| 6 | * This program is free software: you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License as published by | ||
| 8 | * the Free Software Foundation, either version 3 of the License, or | ||
| 9 | * (at your option) any later version. | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License | ||
| 17 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 18 | * | ||
| 19 | */ | ||
| 20 | |||
| 21 | #include <stdio.h> | ||
| 22 | #include <stdlib.h> | ||
| 23 | #include <unistd.h> | ||
| 24 | #include <time.h> | ||
| 25 | #include <stdarg.h> | ||
| 26 | |||
| 27 | #include <ctype.h> | ||
| 28 | #include <inttypes.h> | ||
| 29 | #include <string.h> | ||
| 30 | |||
| 31 | #include <time.h> | ||
| 32 | #include <signal.h> | ||
| 33 | |||
| 34 | #include <sys/stat.h> | ||
| 35 | #include <sys/time.h> | ||
| 36 | #include <math.h> | ||
| 37 | #include <locale.h> | ||
| 38 | |||
| 39 | #ifndef _WIN32 | ||
| 40 | #include <termios.h> | ||
| 41 | #include <sys/ioctl.h> | ||
| 42 | #endif | ||
| 43 | |||
| 44 | #include "common.h" | ||
| 45 | #include "cli.h" | ||
| 46 | #include "error.h" | ||
| 47 | #include "mem.h" | ||
| 48 | #include "dir.h" | ||
| 49 | #include "non-gnu.h" | ||
| 50 | |||
| 51 | //#define CLI_HELP_FORMAT_RIGHT_COLUMN 37 | ||
| 52 | |||
| 53 | #define CLI_SMALL 62 | ||
| 54 | #define CLI_DEFAULT 80 /* Expected default terminal width */ | ||
| 55 | #define CLI_LARGE 75 | ||
| 56 | |||
| 57 | static int cli_width = CLI_DEFAULT; | ||
| 58 | static int cli_size_width = 0; | ||
| 59 | |||
| 60 | static void cli_display_bars(cli_progress_s *, cli_progress_s *, cli_bps_s *); | ||
| 61 | #ifndef _WIN32 | ||
| 62 | static void cli_sigwinch(int); | ||
| 63 | #endif | ||
| 64 | |||
| 65 | #define CLI_DO_INIT if (cli_inited != 1) cli_init(); | ||
| 66 | |||
| 67 | extern void on_quit(int) __attribute__((noreturn)); | ||
| 68 | |||
| 69 | static void cli_init(void); | ||
| 70 | static int cli_inited = -1; // -1 (no), 0 (failed), 1 (okay) | ||
| 71 | |||
| 72 | static int cli_bps_sort(const void *, const void *); | ||
| 73 | static int cli_print(FILE *, const char *); | ||
| 74 | |||
| 75 | 1 | extern int cli_printf(const char * const restrict s, ...) | |
| 76 | { | ||
| 77 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | CLI_DO_INIT; |
| 78 | |||
| 79 | va_list ap; | ||
| 80 | 1 | va_start(ap, s); | |
| 81 | 1 | char *d = NULL; | |
| 82 | 1 | vasprintf(&d, s, ap); | |
| 83 | 1 | int x = cli_print(stdout, d); | |
| 84 | 1 | va_end(ap); | |
| 85 | 1 | free(d); | |
| 86 | 1 | return x; | |
| 87 | } | ||
| 88 | |||
| 89 | 94 | extern int cli_eprintf(const char * const restrict s, ...) | |
| 90 | { | ||
| 91 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 94 times.
|
94 | CLI_DO_INIT; |
| 92 | |||
| 93 | va_list ap; | ||
| 94 | 94 | va_start(ap, s); | |
| 95 | 94 | char *d = NULL; | |
| 96 | 94 | vasprintf(&d, s, ap); | |
| 97 | 94 | int x = cli_print(stderr, d); | |
| 98 | 94 | va_end(ap); | |
| 99 | 94 | free(d); | |
| 100 | 94 | return x; | |
| 101 | } | ||
| 102 | |||
| 103 | 1 | extern int logger(const char * const restrict s, ...) | |
| 104 | { | ||
| 105 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | CLI_DO_INIT; |
| 106 | |||
| 107 | 1 | int x = 0; | |
| 108 | #if 0 | ||
| 109 | time_t now = time(NULL); | ||
| 110 | char at[24]; | ||
| 111 | struct tm t; | ||
| 112 | localtime_r(&now, &t); | ||
| 113 | strftime(at, sizeof at, "%F %T : ", &t); | ||
| 114 | x += cli_print(stderr, at); | ||
| 115 | #endif | ||
| 116 | va_list ap; | ||
| 117 | 1 | va_start(ap, s); | |
| 118 | 1 | char *d = NULL; | |
| 119 | 1 | vasprintf(&d, s, ap); | |
| 120 | 1 | x += cli_print(stderr, d); | |
| 121 | 1 | va_end(ap); | |
| 122 | 1 | free(d); | |
| 123 | |||
| 124 | static const char nl[] = "\n"; | ||
| 125 | 1 | x += cli_print(stderr, nl); | |
| 126 | |||
| 127 | 1 | return x; | |
| 128 | } | ||
| 129 | |||
| 130 | 120 | extern int cli_fprintf(FILE *f, const char * const restrict s, ...) | |
| 131 | { | ||
| 132 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 119 times.
|
120 | CLI_DO_INIT; |
| 133 | |||
| 134 | va_list ap; | ||
| 135 | 120 | va_start(ap, s); | |
| 136 | 120 | char *d = NULL; | |
| 137 | 120 | vasprintf(&d, s, ap); | |
| 138 | 120 | int x = cli_print(f, d); | |
| 139 | 120 | va_end(ap); | |
| 140 | 120 | free(d); | |
| 141 | 120 | return x; | |
| 142 | } | ||
| 143 | |||
| 144 | 1 | extern int cli_printx(const uint8_t * const restrict x, size_t z) | |
| 145 | { | ||
| 146 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | CLI_DO_INIT; |
| 147 | |||
| 148 | 1 | return cli_fprintx(stdout, x, z); | |
| 149 | } | ||
| 150 | |||
| 151 | 1 | extern int cli_eprintx(const uint8_t * const restrict x, size_t z) | |
| 152 | { | ||
| 153 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | CLI_DO_INIT; |
| 154 | |||
| 155 | 1 | return cli_fprintx(stderr, x, z); | |
| 156 | } | ||
| 157 | |||
| 158 | 5 | extern int cli_fprintx(FILE *f, const uint8_t * const restrict x, size_t z) | |
| 159 | { | ||
| 160 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
|
5 | CLI_DO_INIT; |
| 161 | |||
| 162 | #define CLI_PRINTX_W 16 | ||
| 163 | // TODO allow variable width lines | ||
| 164 | 5 | int l = 0, o = 0; | |
| 165 | 5 | char r[CLI_PRINTX_W * 2 + CLI_PRINTX_W] = { 0x0 }; | |
| 166 |
2/2✓ Branch 0 taken 26 times.
✓ Branch 1 taken 5 times.
|
31 | for (size_t i = 0; i < z; i++) |
| 167 | { | ||
| 168 | 26 | char c[4] = { 0x0 }; | |
| 169 | 26 | sprintf(c, "%02x", x[i]); | |
| 170 | 26 | strcat(r, c); | |
| 171 |
2/2✓ Branch 0 taken 11 times.
✓ Branch 1 taken 15 times.
|
26 | if (i % 2 == 1) |
| 172 | 11 | strcat(r, " "); | |
| 173 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 25 times.
|
26 | if (i % CLI_PRINTX_W == (CLI_PRINTX_W - 1)) |
| 174 | { | ||
| 175 | 1 | l += cli_fprintf(f, "%08x: %s\n", o, r); | |
| 176 | 1 | memset(r, 0x00, sizeof r); | |
| 177 | 1 | o += CLI_PRINTX_W; | |
| 178 | } | ||
| 179 | } | ||
| 180 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
|
5 | if (strlen(r)) |
| 181 | 4 | l += cli_fprintf(f, "%08x: %s\n", o, r); | |
| 182 | 5 | return l; | |
| 183 | } | ||
| 184 | |||
| 185 | 1 | extern double cli_calc_bps(cli_bps_s *bps) | |
| 186 | { | ||
| 187 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | CLI_DO_INIT; |
| 188 | |||
| 189 | 1 | cli_bps_s *copy = m_calloc(BPS, sizeof( cli_bps_s )); | |
| 190 |
2/2✓ Branch 0 taken 128 times.
✓ Branch 1 taken 1 times.
|
129 | for (int i = 0; i < BPS; i++) |
| 191 | { | ||
| 192 | 128 | copy[i].time = bps[i].time; | |
| 193 | 128 | copy[i].bytes = bps[i].bytes; | |
| 194 | } | ||
| 195 | 1 | qsort(copy, BPS, sizeof( cli_bps_s ), cli_bps_sort); | |
| 196 | 1 | double avg[BPS - 1] = { 0.0f }; | |
| 197 |
2/2✓ Branch 0 taken 127 times.
✓ Branch 1 taken 1 times.
|
128 | for (int i = 0; i < BPS - 1; i++) |
| 198 | /* | ||
| 199 | * requires scale factor of MILLION as time is in microseconds | ||
| 200 | * not seconds (millions of bytes / micros of seconds, so to | ||
| 201 | * speak) | ||
| 202 | */ | ||
| 203 | 127 | avg[i] = MILLION * (double)(copy[i + 1].bytes - copy[i].bytes) / (double)(copy[i + 1].time - copy[i].time); | |
| 204 | 1 | double val = 0.0; | |
| 205 |
2/2✓ Branch 0 taken 127 times.
✓ Branch 1 taken 1 times.
|
128 | for (int i = 0; i < BPS - 1; i++) |
| 206 | 127 | val += avg[i]; | |
| 207 | 1 | free(copy); | |
| 208 | 1 | return val /= (BPS - 1); | |
| 209 | } | ||
| 210 | |||
| 211 | //#ifndef _WIN32 | ||
| 212 | ✗ | extern void cli_display(cli_s *p) | |
| 213 | { | ||
| 214 | ✗ | CLI_DO_INIT; | |
| 215 | |||
| 216 | #ifndef _WIN32 | ||
| 217 | ✗ | cli_sigwinch(SIGWINCH); | |
| 218 | #endif | ||
| 219 | ✗ | cli_size_width = 0; | |
| 220 | |||
| 221 | cli_bps_s bps[BPS]; | ||
| 222 | ✗ | memset(bps, 0x00, BPS * sizeof( cli_bps_s )); | |
| 223 | ✗ | int b = 0; | |
| 224 | |||
| 225 | ✗ | fprintf(stderr, "\e[?25l\n"); /* hide cursor */ | |
| 226 | ✗ | while (*p->status == CLI_INIT || *p->status == CLI_RUN) | |
| 227 | { | ||
| 228 | ✗ | struct timespec s = { 0, 10 * MILLION }; /* 10 milliseconds */ | |
| 229 | ✗ | nanosleep(&s, NULL); | |
| 230 | ✗ | if (*p->status == CLI_INIT) | |
| 231 | ✗ | continue; | |
| 232 | struct timeval tv; | ||
| 233 | ✗ | gettimeofday(&tv, NULL); | |
| 234 | ✗ | bps[b].time = tv.tv_sec * MILLION + tv.tv_usec; | |
| 235 | ✗ | bps[b].bytes = p->current->offset; | |
| 236 | ✗ | b++; | |
| 237 | ✗ | if (b >= BPS) | |
| 238 | ✗ | b = 0; | |
| 239 | ✗ | cli_display_bars(p->total, p->current, bps); | |
| 240 | } | ||
| 241 | ✗ | if (*p->status == CLI_DONE) | |
| 242 | { | ||
| 243 | ✗ | p->total->offset = p->total->size; | |
| 244 | ✗ | p->current->offset = p->current->size; | |
| 245 | ✗ | cli_display_bars(p->total, p->current, bps); | |
| 246 | } | ||
| 247 | ✗ | fprintf(stderr, "\e[?25h\n"); /* restore cursor */ | |
| 248 | |||
| 249 | ✗ | return; | |
| 250 | } | ||
| 251 | |||
| 252 | ✗ | static void cli_display_bar(char *name, cli_progress_s *p, double percent) | |
| 253 | { | ||
| 254 | ✗ | int name_width = CLI_TRUNCATED_DISPLAY_SHORT + strlen(CLI_TRUNCATED_ELLIPSE) + (cli_width > CLI_DEFAULT ? CLI_TRUNCATED_DISPLAY_SHORT : 0); | |
| 255 | ✗ | fprintf(stderr, "\r%-*s : ", name_width, name); | |
| 256 | ✗ | if (cli_width > CLI_SMALL) | |
| 257 | ✗ | fprintf(stderr, "%'13" PRIu64 "/%-'13" PRIu64 " (", p->offset, p->size); | |
| 258 | ✗ | fprintf(stderr, "%3.0f%%", isnan(percent) ? 0.0f : (percent > PERCENT ? PERCENT : percent)); | |
| 259 | ✗ | if (cli_width > CLI_SMALL) | |
| 260 | ✗ | fprintf(stderr, ")"); | |
| 261 | |||
| 262 | ✗ | if (cli_width > CLI_DEFAULT) | |
| 263 | { | ||
| 264 | ✗ | int progress_bar_width = cli_width - CLI_LARGE; | |
| 265 | ✗ | fprintf(stderr, " ["); | |
| 266 | /* | ||
| 267 | * display overall progress bar | ||
| 268 | */ | ||
| 269 | ✗ | for (int i = 0; i < progress_bar_width; i++) | |
| 270 | ✗ | fprintf(stderr, "%c", i < progress_bar_width * percent / PERCENT ? '=' : ' '); | |
| 271 | ✗ | fprintf(stderr, "]"); | |
| 272 | } | ||
| 273 | ✗ | return; | |
| 274 | } | ||
| 275 | |||
| 276 | ✗ | static void cli_display_bps(cli_bps_s *bps) | |
| 277 | { | ||
| 278 | /* | ||
| 279 | * calculate B/s | ||
| 280 | */ | ||
| 281 | ✗ | double val = cli_calc_bps(bps); | |
| 282 | ✗ | if (isnan(val) || val == 0.0f) | |
| 283 | ✗ | fprintf(stderr, " 0.0 B/s"); | |
| 284 | else | ||
| 285 | { | ||
| 286 | ✗ | if (val < THOUSAND) | |
| 287 | ✗ | fprintf(stderr, " %5.1f B/s", val); | |
| 288 | ✗ | else if (val < MILLION) | |
| 289 | ✗ | fprintf(stderr, " %5.1f KB/s", val / KILOBYTE); | |
| 290 | ✗ | else if (val < THOUSAND_MILLION) | |
| 291 | ✗ | fprintf(stderr, " %5.1f MB/s", val / MEGABYTE); | |
| 292 | ✗ | else if (val < BILLION) | |
| 293 | ✗ | fprintf(stderr, " %5.1f GB/s", val / GIGABYTE); | |
| 294 | #if 1 /* if you’re getting these kinds of speeds please, please can I have your machine ;-) */ | ||
| 295 | ✗ | else if (val < THOUSAND_BILLION) | |
| 296 | ✗ | fprintf(stderr, " %5.1f TB/s", val / TERABYTE); | |
| 297 | ✗ | else if (val < TRILLION) | |
| 298 | ✗ | fprintf(stderr, " %5.1f PB/s", val / PETABYTE); | |
| 299 | // else // if (val < THOUSAND_TRILLION) // even uint64_t is too small now! | ||
| 300 | // fprintf(progress_bar + strlen(progress_bar), " %5.1f EB/s", val / EXABYTE); | ||
| 301 | // else if (val < QUADRILLION) | ||
| 302 | // fprintf(progress_bar + strlen(progress_bar), " %5.1f ZB/s", val / ZETTABYTE); | ||
| 303 | // else | ||
| 304 | // fprintf(progress_bar + strlen(progress_bar), " %5.1f YB/s", val / YOTTABYTE); | ||
| 305 | #endif | ||
| 306 | else | ||
| 307 | ✗ | fprintf(stderr, " ---.- B/s"); | |
| 308 | } | ||
| 309 | ✗ | return; | |
| 310 | } | ||
| 311 | |||
| 312 | ✗ | static void cli_display_bars(cli_progress_s *t, cli_progress_s *c, cli_bps_s *bps) | |
| 313 | { | ||
| 314 | ✗ | double total = PERCENT * (t->offset + (1.0f * c->offset / c->size)) / t->size; | |
| 315 | ✗ | double current = PERCENT * c->offset / c->size; | |
| 316 | ✗ | bool single = t->size == 1; | |
| 317 | |||
| 318 | ✗ | if (!single) | |
| 319 | { | ||
| 320 | ✗ | char name[CLI_TRUNCATED_DISPLAY_LONG] = { 0x0 }; | |
| 321 | ✗ | char *nm = c->display ? /*dir_get_name(c->display, '.')*/ : CLI_UNKNOWN; | |
| 322 | ✗ | if (strlen(nm) < CLI_TRUNCATED_DISPLAY_LONG) | |
| 323 | ✗ | strcpy(name, nm); | |
| 324 | else | ||
| 325 | { | ||
| 326 | ✗ | strncpy(name, nm, CLI_TRUNCATED_DISPLAY_SHORT); | |
| 327 | ✗ | strcat(name, CLI_TRUNCATED_ELLIPSE); | |
| 328 | ✗ | if (cli_width > CLI_DEFAULT) | |
| 329 | ✗ | strcat(name, nm + (strlen(nm) - CLI_TRUNCATED_DISPLAY_SHORT)); | |
| 330 | } | ||
| 331 | ✗ | fprintf(stderr, "\x1B[1F"); // go up 1 line | |
| 332 | ✗ | cli_display_bar(name, c, current); | |
| 333 | ✗ | cli_display_bps(bps); | |
| 334 | ✗ | fprintf(stderr, "\n"); | |
| 335 | } | ||
| 336 | ✗ | cli_display_bar("Total", single ? c : t, total); | |
| 337 | |||
| 338 | ✗ | return; | |
| 339 | } | ||
| 340 | |||
| 341 | #ifndef _WIN32 | ||
| 342 | ✗ | static void cli_sigwinch(int s) | |
| 343 | { | ||
| 344 | struct winsize ws; | ||
| 345 | ✗ | ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws); | |
| 346 | ✗ | cli_width = ws.ws_col; | |
| 347 | ✗ | signal(SIGWINCH, cli_sigwinch); | |
| 348 | (void)s; | ||
| 349 | ✗ | } | |
| 350 | #endif | ||
| 351 | |||
| 352 | 448 | static int cli_bps_sort(const void *a, const void *b) | |
| 353 | { | ||
| 354 | 448 | const cli_bps_s *ba = (const cli_bps_s *)a; | |
| 355 | 448 | const cli_bps_s *bb = (const cli_bps_s *)b; | |
| 356 | 448 | return (ba->time > bb->time) - (ba->time < bb->time); | |
| 357 | } | ||
| 358 | |||
| 359 | 217 | static int cli_print(FILE *stream, const char *text) | |
| 360 | { | ||
| 361 | 217 | size_t l = strlen(text); | |
| 362 | 217 | char *copy = m_calloc(1, l + 1); | |
| 363 | #ifndef _WIN32 | ||
| 364 |
6/8✓ Branch 0 taken 2 times.
✓ Branch 1 taken 215 times.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 186 times.
✓ Branch 6 taken 31 times.
✓ Branch 8 taken 186 times.
✗ Branch 9 not taken.
|
217 | bool strip = !((stream == stdout && isatty(STDOUT_FILENO)) || (stream == stderr && isatty(STDERR_FILENO))); |
| 365 | #else | ||
| 366 | #ifdef __DEBUG__ | ||
| 367 | bool strip = cli_inited != 1; | ||
| 368 | #else | ||
| 369 | bool strip = false; | ||
| 370 | #endif | ||
| 371 | #endif | ||
| 372 |
1/2✓ Branch 0 taken 217 times.
✗ Branch 1 not taken.
|
217 | if (strip) |
| 373 | { | ||
| 374 | 217 | char *ptr = (char *)text; | |
| 375 |
1/2✓ Branch 0 taken 421 times.
✗ Branch 1 not taken.
|
421 | for (size_t i = 0, j = 0; i < l; i++) |
| 376 | { | ||
| 377 | 421 | char *e = strstr(ptr, "\x1b["); | |
| 378 |
2/2✓ Branch 0 taken 217 times.
✓ Branch 1 taken 204 times.
|
421 | if (!e) |
| 379 | 217 | break; | |
| 380 | 204 | memcpy(copy + j, ptr, e - ptr); | |
| 381 | 204 | j += e - ptr; | |
| 382 | 204 | ptr = strchr(e, 'm') + 1; | |
| 383 | } | ||
| 384 | 217 | strcat(copy, ptr); | |
| 385 | } | ||
| 386 | else | ||
| 387 | ✗ | strcpy(copy, text); | |
| 388 | 217 | int x = fprintf(stream, "%s", copy); | |
| 389 | //fprintf(stream, "\n"); | ||
| 390 | 217 | free(copy); | |
| 391 | 217 | return x; | |
| 392 | } | ||
| 393 | |||
| 394 | ✗ | extern void on_quit(int s) | |
| 395 | { | ||
| 396 | ✗ | fprintf(stderr, "\e[?25h\n"); /* restore cursor */ | |
| 397 | ✗ | signal(s, SIG_DFL); | |
| 398 | ✗ | raise(s); | |
| 399 | ✗ | _exit(EXIT_FAILURE); // this shouldn't happen as the raise above will handle things | |
| 400 | } | ||
| 401 | |||
| 402 | 1 | static void cli_init(void) | |
| 403 | { | ||
| 404 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (cli_inited >= 0) |
| 405 | ✗ | return; | |
| 406 | 1 | signal(SIGTERM, on_quit); | |
| 407 | 1 | signal(SIGINT, on_quit); | |
| 408 | 1 | signal(SIGQUIT, on_quit); | |
| 409 | |||
| 410 | 1 | setlocale(LC_NUMERIC, ""); | |
| 411 | #ifdef _WIN32 | ||
| 412 | // Set output mode to handle virtual terminal sequences | ||
| 413 | HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); | ||
| 414 | if (hOut == INVALID_HANDLE_VALUE) | ||
| 415 | { | ||
| 416 | cli_inited = 0; | ||
| 417 | return; | ||
| 418 | } | ||
| 419 | DWORD dwMode = 0; | ||
| 420 | if (!GetConsoleMode(hOut, &dwMode)) | ||
| 421 | { | ||
| 422 | cli_inited = 1; | ||
| 423 | return; // MSYS2 ends up here; so take a chance that everything's going to be okay | ||
| 424 | } | ||
| 425 | dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; | ||
| 426 | if (!SetConsoleMode(hOut, dwMode)) | ||
| 427 | { | ||
| 428 | cli_inited = 0; | ||
| 429 | return; // cmd on Win7 ends up here; looks like the feature is not available until Win10 | ||
| 430 | } | ||
| 431 | #endif | ||
| 432 | 1 | cli_inited = 1; | |
| 433 | 1 | return; | |
| 434 | } | ||
| 435 |