From: <suc...@us...> - 2006-08-05 22:50:39
|
Revision: 179 Author: sucknblow Date: 2006-08-05 15:50:29 -0700 (Sat, 05 Aug 2006) ViewCVS: http://svn.sourceforge.net/pmplib/?rev=179&view=rev Log Message: ----------- * Detect width of the terminal. * Avoid printing many blank lines when displaying progress on terminals of width < 80. * Truncate long track names to the width of the terminal. * Clear the line properly for files with very long names. * Introduces easypmp_progress_num_str, for progress messages with both a number and a string. * Dump the whole line to the output if the output is not associated with a tty. * Use stdout rather than stderr for a few more things. Modified Paths: -------------- trunk/pmplib/configure.in trunk/pmplib/frontend/easypmp/cui/main.c trunk/pmplib/frontend/easypmp/cui/util.c trunk/pmplib/frontend/easypmp/cui/util.h Modified: trunk/pmplib/configure.in =================================================================== --- trunk/pmplib/configure.in 2006-08-02 21:33:07 UTC (rev 178) +++ trunk/pmplib/configure.in 2006-08-05 22:50:29 UTC (rev 179) @@ -65,7 +65,8 @@ dnl Checks for header files. dnl ------------------------------------------------------------------ AC_HEADER_STDC -AC_CHECK_HEADERS(fcntl.h limits.h malloc.h strings.h unistd.h stdint.h getopt.h) +AC_CHECK_HEADERS(fcntl.h limits.h malloc.h strings.h unistd.h \ + stdint.h getopt.h signal.h sys/ioctl.h) dnl ------------------------------------------------------------------ @@ -186,6 +187,26 @@ dnl Check for libsmjs (SpiderMonkey JavaScript engine) AC_PATH_SPIDERMONKEY +dnl ------------------------------------------------------------------ +dnl Check whether we can detect the terminal window size +dnl (Already checked for signal.h, sys/ioctl.h) +dnl ------------------------------------------------------------------ +if test "$ac_cv_header_signal_h" = "yes" -a \ + "$ac_cv_header_sys_ioctl_h" = "yes" ; then +AC_MSG_CHECKING([whether terminal window size can be detected]) +AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[ +#include <signal.h> +#include <sys/ioctl.h>]],[[ +struct winsize wsize; +signal(SIGWINCH, 0); +ioctl(0, TIOCGWINSZ, &wsize);]]) + ], + [AC_DEFINE([CAN_GET_WIN_SIZE],1,[Define to 1 if you can set a signal + handler for SIGWINCH, and use the TIOCGWINSZ ioctl.]) + AC_MSG_RESULT([yes])], + [AC_MSG_RESULT([no, assuming width of 80.])]) +fi dnl ------------------------------------------------------------------ dnl Export variables Modified: trunk/pmplib/frontend/easypmp/cui/main.c =================================================================== --- trunk/pmplib/frontend/easypmp/cui/main.c 2006-08-02 21:33:07 UTC (rev 178) +++ trunk/pmplib/frontend/easypmp/cui/main.c 2006-08-05 22:50:29 UTC (rev 179) @@ -59,22 +59,25 @@ void device_show_information(pmp_t* pmp, FILE *fpe); void device_enumerate(pmphelp_t* pmphelp); +int +easypmp_progress_num_str( + FILE *fp, + size_t n, + const ucs2char_t* msg); + static int easypmp_enumerate_progress( void *instance, const ucs2char_t* path, const ucs2char_t* file, - size_t n - ) + size_t n) { - FILE *fpe = stderr; - - clear_line(fpe); - fprintf(fpe, " %d: ", n); - fprints(fpe, "%s\r", file); + FILE *fpe = stdout; + easypmp_progress_num_str(fpe, n, file); return 0; } + static int easypmp_progress( void *instance, @@ -91,39 +94,37 @@ case EASYPMPDBP_START: break; case EASYPMPDBP_READ|EASYPMPSP_START: - fprintf(fpe, "Reading database\n"); + fprintf(fpo, "Reading database\n"); break; case EASYPMPDBP_READ|EASYPMPSP_PROGRESS: break; case EASYPMPDBP_READ|EASYPMPSP_END: - fprintf(fpe, "\n"); + fprintf(fpo, "\n"); break; case EASYPMPDBP_GMI|EASYPMPSP_START: - fprintf(fpe, "Obtaining media information from %d files\n", param_int); + fprintf(fpo, "Obtaining media information from %d files\n", param_int); break; case EASYPMPDBP_GMI|EASYPMPSP_PROGRESS: - clear_line(fpe); - fprintf(fpe, " %d: ", param_int+1); - fprints(fpe, "%s\r", filepath_skippath(param_str)); + easypmp_progress_num_str(fpo, param_int+1, filepath_skippath(param_str)); break; case EASYPMPDBP_GMI|EASYPMPSP_END: - clear_line(fpe); - fprintf(fpe, " %d files were imported\n", param_int); - fprintf(fpe, "\n"); + clear_line(fpo); + fprintf(fpo, " %d files were imported\n", param_int); + fprintf(fpo, "\n"); break; case EASYPMPDBP_UPDATE|EASYPMPSP_START: - fprintf(fpe, "Updating database\n"); + fprintf(fpo, "Updating database\n"); break; case EASYPMPDBP_UPDATE|EASYPMPSP_END: - fprintf(fpe, "\n"); + fprintf(fpo, "\n"); break; case EASYPMPDBP_WRITE|EASYPMPSP_START: - fprintf(fpe, "Writing database\n"); + fprintf(fpo, "Writing database\n"); break; case EASYPMPDBP_WRITE|EASYPMPSP_PROGRESS: break; case EASYPMPDBP_WRITE|EASYPMPSP_END: - fprintf(fpe, "\n"); + fprintf(fpo, "\n"); break; case EASYPMPDBP_END: break; @@ -131,12 +132,10 @@ case EASYPMPPLP_START: break; case EASYPMPPLP_CONVERT|EASYPMPSP_START: - fprintf(fpe, "Converting playlists\n"); + fprintf(fpo, "Converting playlists\n"); break; case EASYPMPPLP_CONVERT|EASYPMPSP_PROGRESS: - clear_line(fpe); - fprintf(fpe, " %d: ", param_int+1); - fprints(fpe, "%s\r", filepath_skippath(param_str)); + easypmp_progress_num_str(fpo, param_int+1, filepath_skippath(param_str)); break; case EASYPMPPLP_CONVERT|EASYPMPSP_WARN_PLAYLIST: fprintf(fpe, "\n"); @@ -150,6 +149,7 @@ fprints(fpe, " %s\n", param_str); break; case EASYPMPPLP_CONVERT|EASYPMPSP_JSPL_ERROR: + fprintf(fpe, "\n"); fprints(fpe, " JSPL(ERROR): %s\n", param_str); break; case EASYPMPPLP_CONVERT|EASYPMPSP_JSPL_ERROR_POS: @@ -162,7 +162,7 @@ fprints(fpo, " JSPL: %s\n", param_str); break; case EASYPMPPLP_CONVERT|EASYPMPSP_END: - fprintf(fpe, "\n"); + fprintf(fpo, "\n"); break; case EASYPMPPLP_END: break; @@ -215,8 +215,8 @@ option_init(&opt); // Show copyright information. - fprintf(fpe, APPLICATION_S " " VERSION_S " " COPYRIGHT_S "\n"); - fprintf(fpe, "\n"); + fprintf(fpo, APPLICATION_S " " VERSION_S " " COPYRIGHT_S "\n"); + fprintf(fpo, "\n"); // Parse the command-line arguments. used_args = option_parse(&opt, argc, argv, fpe); @@ -320,25 +320,26 @@ } // Show player information. - device_show_information(pmp, fpe); - fprintf(fpe, "\n"); + device_show_information(pmp, fpo); + fprintf(fpo, "\n"); memset(&musics, 0, sizeof(musics)); + display_init(); if ((opt.verb & MODE_DATABASE) || ((opt.verb & MODE_PLAYLIST) && (opt.verb & MODE_PLAYLIST_FIND))) { - fprintf(fpe, "Enumerating music files\n"); + fprintf(fpo, "Enumerating music files\n"); easypmp_enumerate_music(&musics, pmp, &opt, easypmp_enumerate_progress, NULL); - clear_line(fpe); - fprintf(fpe, " %d music files were found\n", musics.num_elements); - fprintf(fpe, "\n"); + clear_line(fpo); + fprintf(fpo, " %d music files were found\n", musics.num_elements); + fprintf(fpo, "\n"); } memset(&playlists, 0, sizeof(playlists)); if (opt.verb & MODE_PLAYLIST) { fprintf(fpe, "Enumerating playlist files\n"); easypmp_enumerate_playlist(&playlists, pmp, &opt, easypmp_enumerate_progress, NULL); - clear_line(fpe); - fprintf(fpe, " %d music files were found\n", playlists.num_elements); - fprintf(fpe, "\n"); + clear_line(fpo); + fprintf(fpo, " %d music files were found\n", playlists.num_elements); + fprintf(fpo, "\n"); } // Execute jobs. Modified: trunk/pmplib/frontend/easypmp/cui/util.c =================================================================== --- trunk/pmplib/frontend/easypmp/cui/util.c 2006-08-02 21:33:07 UTC (rev 178) +++ trunk/pmplib/frontend/easypmp/cui/util.c 2006-08-05 22:50:29 UTC (rev 179) @@ -30,17 +30,189 @@ #endif/*HAVE_STRING_H*/ #include <os.h> +#include <systems.h> #include <stdio.h> #include <stdlib.h> #include <ucs2char.h> +#if CAN_GET_WIN_SIZE +#include <sys/ioctl.h> +#include <signal.h> +#endif/*CAN_GET_WIN_SIZE*/ + #include "util.h" + +#if CAN_GET_WIN_SIZE +/* + The number of characters that can be printed on a single line, + without causing a line wrap. Since the right-most column is + required for the cursor, this is one less than the actual terminal + width. + + Defaults to 79 on systems where we can't tell the width of the + terminal. +*/ +static volatile unsigned short int window_width; +#else +static const unsigned short int window_width = 79; +#endif + +/* + The minimum width of the terminal we're willing to entertain. If the + terminal gets narrower than this width, we treat it as this width. + Note that it must be at least 2 to allow for one character and the + cursor. +*/ +static const int min_term_width = 6; + + +/* + Flags to indicate whether stdin, stdout, and stderr are attached to a + terminal. These are used to determine whether we should check the + width of some progress lines before printing them. Initialised in + display_init. + */ +#define POSSIBLE_TTYS 2 +static int fd_is_tty[POSSIBLE_TTYS+1]; + +#if CAN_GET_WIN_SIZE +/* + Hander for the "terminal window changed size" signal. +*/ +void window_size_changed(int unused) +{ + static struct winsize wsize; + if (ioctl(1, TIOCGWINSZ, &wsize) != -1) { + if (wsize.ws_col > min_term_width) { + window_width = wsize.ws_col - 1; + } else { + window_width = min_term_width; + } + } +} + +#endif/*CAN_GET_WIN_SIZE*/ + +void display_init() +{ +#if CAN_GET_WIN_SIZE + int i; + for(i = 0; i <= 2; ++i) + fd_is_tty[i] = isatty(i); + + signal(SIGWINCH,window_size_changed); + window_size_changed(0); +#endif/*CAN_GET_WIN_SIZE*/ +} + +/* + Delete all text on the current line by overwriting it with spaces, + and write a \r to return the cursor to the start of the line. +*/ void clear_line(FILE *fp) { - fprintf(fp, "%-79.79s\r", ""); + /* fmt needs 4 chars (%, -, s, \r) + + room for window_width as string (max. 65535) + + null terminator */ + char fmt[10]; + sprintf(fmt, "%%-%us\r", window_width); + fprintf(fp, fmt, ""); } + +/* + Display as much of a UCS-2 encoded string as will fit on a single + line in the terminal, and returning the cursor to the start of the + line. If the terminal is less that the given minimum width, display + that minimum number of characters anyway, even if it means the text + will wrap onto the next line. + + If fp isn't associated with a terminal, just print the whole line. + + fp - FILE* to print on + line - the UCS-2 encoded string to display + min-width - minimum number of characters to print + */ +void display_line(FILE *fp, const ucs2char_t* const line, unsigned int min_width) +{ + int writing_to_tty; + /* Check if writing to a terminal. If so, clear the current line. */ + int fd = fileno(fp); + if (0 < fd && fd <= POSSIBLE_TTYS && fd_is_tty[fd]) { + clear_line(fp); + writing_to_tty = TRUE; + } else { + writing_to_tty = FALSE; + } + + unsigned int length = ucs2len(line); + const ucs2char_t* line_to_print; + + if(!writing_to_tty) { + /* Write the whole line to the file, add \n. */ + fprints(fp, "%s\r\n", line); + } else if (length <= window_width) { + /* There's enough room to show the whole line. */ + fprints(fp, "%s\r", line); + } + else { + /* Length of the longest string we might display: */ + const int max_trunc_length = MAX(window_width,min_width); + + /* Length of string we actually will display: */ + const size_t trunc_length = MIN(max_trunc_length, length); + + ucs2char_t truncated_line[trunc_length+1]; + truncated_line[trunc_length]=0; + ucs2ncpy(truncated_line, line, trunc_length); + fprints(fp, "%s\r", truncated_line); + } +} + +/* + Generic display method for progress messages consisting of a number + and a string. + + n - number to be shown in the numeric part + msg - message + */ +int +easypmp_progress_num_str( + FILE *fp, + size_t n, + const ucs2char_t* msg + ) +{ + /* + A terminal must be of a certain width in order to usefully + show progress when enumerating/reading media files. + Specifically, it needs to be wide enough to display the + number of files read. + + display_line is used to trucate the line in this way. + */ + + // Numeric part, plus associated punctuation + const int prefix_length = 16; + char fmt[prefix_length + 1]; + + ucs2char_t line[ucs2len(msg) + prefix_length + 1]; + ucs2char_t *fmt_ucs2; + + // Build the numeric part... + sprintf(fmt, " %u: ", n); + fmt_ucs2 = mbsdupucs2(fmt); + if (fmt_ucs2) { + ucs2cpy(line, fmt_ucs2); + // ... and append the message. + ucs2cat(line, msg); + display_line(fp, line, strlen(fmt)); + ucs2free(fmt_ucs2); + } + return 0; +} + void fprints(FILE *fp, const char *format, const ucs2char_t* value) { fprints_fixed(fp, format, value, ucs2len(value)); @@ -58,3 +230,11 @@ ucs2free(mbs); } } + +/* + * Local Variables: + * indent-tabs-mode: t + * tab-width: 8 + * c-basic-offset: 8 + * End: + */ Modified: trunk/pmplib/frontend/easypmp/cui/util.h =================================================================== --- trunk/pmplib/frontend/easypmp/cui/util.h 2006-08-02 21:33:07 UTC (rev 178) +++ trunk/pmplib/frontend/easypmp/cui/util.h 2006-08-05 22:50:29 UTC (rev 179) @@ -25,6 +25,8 @@ #ifndef __UTIL_H__ #define __UTIL_H__ +void display_init(); + void clear_line(FILE *fp); void fprints(FILE *fp, const char *format, const ucs2char_t* value); void fprints_fixed(FILE *fp, const char *format, const ucs2char_t* value, size_t length); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |