How to animate the command line? - command-line

I have always wondered how people update a previous line in a command line. a great example of this is when using the wget command in linux. It creates an ASCII loading bar of sorts that looks like this:
[======> ] 37%
and of course the loading bar moves and the percent changes, But it doesn't make a new line. I cannot figure out how to do this. Can someone point me in the right direction?

One way to do this is to repeatedly update the line of text with the current progress. For example:
def status(percent):
sys.stdout.write("%3d%%\r" % percent)
sys.stdout.flush()
Note that I used sys.stdout.write instead of print (this is Python) because print automatically prints "\r\n" (carriage-return new-line) at the end of each line. I just want the carriage-return which returns the cursor to the start of the line. Also, the flush() is necessary because by default, sys.stdout only flushes its output after a newline (or after its buffer gets full).

There are two ways I know of to do this:
Use the backspace escape character ('\b') to erase your line
Use the curses package, if your programming language of choice has bindings for it.
And a Google revealed ANSI Escape Codes, which appear to be a good way. For reference, here is a function in C++ to do this:
void DrawProgressBar(int len, double percent) {
cout << "\x1B[2K"; // Erase the entire current line.
cout << "\x1B[0E"; // Move to the beginning of the current line.
string progress;
for (int i = 0; i < len; ++i) {
if (i < static_cast<int>(len * percent)) {
progress += "=";
} else {
progress += " ";
}
}
cout << "[" << progress << "] " << (static_cast<int>(100 * percent)) << "%";
flush(cout); // Required.
}

The secret is to print only \r instead of \n or \r\n at the and of the line.
\r is called carriage return and it moves the cursor at the start of the line
\n is called line feed and it moves the cursor on the next line
In the console. If you only use \r you overwrite the previously written line.
So first write a line like the following:
[ ]
then add a sign for each tick
\r[= ]
\r[== ]
...
\r[==========]
and so on.
You can use 10 chars, each representing a 10%.
Also, if you want to display a message when finished, don't forget to also add enough white chars so that you overwrite the previously written equal signs like so:
\r[done ]

below is my answer,use the windows APIConsoles(Windows), coding of C.
/*
* file: ProgressBarConsole.cpp
* description: a console progress bar Demo
* author: lijian <hustlijian#gmail.com>
* version: 1.0
* date: 2012-12-06
*/
#include <stdio.h>
#include <windows.h>
HANDLE hOut;
CONSOLE_SCREEN_BUFFER_INFO bInfo;
char charProgress[80] =
{"================================================================"};
char spaceProgress = ' ';
/*
* show a progress in the [row] line
* row start from 0 to the end
*/
int ProgressBar(char *task, int row, int progress)
{
char str[100];
int len, barLen,progressLen;
COORD crStart, crCurr;
GetConsoleScreenBufferInfo(hOut, &bInfo);
crCurr = bInfo.dwCursorPosition; //the old position
len = bInfo.dwMaximumWindowSize.X;
barLen = len - 17;//minus the extra char
progressLen = (int)((progress/100.0)*barLen);
crStart.X = 0;
crStart.Y = row;
sprintf(str,"%-10s[%-.*s>%*c]%3d%%", task,progressLen,charProgress, barLen-progressLen,spaceProgress,50);
#if 0 //use stdand libary
SetConsoleCursorPosition(hOut, crStart);
printf("%s\n", str);
#else
WriteConsoleOutputCharacter(hOut, str, len,crStart,NULL);
#endif
SetConsoleCursorPosition(hOut, crCurr);
return 0;
}
int main(int argc, char* argv[])
{
int i;
hOut = GetStdHandle(STD_OUTPUT_HANDLE);
GetConsoleScreenBufferInfo(hOut, &bInfo);
for (i=0;i<100;i++)
{
ProgressBar("test", 0, i);
Sleep(50);
}
return 0;
}

PowerShell has a Write-Progress cmdlet that creates an in-console progress bar that you can update and modify as your script runs.

Here is the answer for your question... (python)
def disp_status(timelapse, timeout):
if timelapse and timeout:
percent = 100 * (float(timelapse)/float(timeout))
sys.stdout.write("progress : ["+"*"*int(percent)+" "*(100-int(percent-1))+"]"+str(percent)+" %")
sys.stdout.flush()
stdout.write("\r \r")

As a follow up to Greg's answer, here is an extended version of his function that allows you to display multi-line messages; just pass in a list or tuple of the strings you want to display/refresh.
def status(msgs):
assert isinstance(msgs, (list, tuple))
sys.stdout.write(''.join(msg + '\n' for msg in msgs[:-1]) + msgs[-1] + ('\x1b[A' * (len(msgs) - 1)) + '\r')
sys.stdout.flush()
Note: I have only tested this using a linux terminal, so your mileage may vary on Windows-based systems.

If your using a scripting language you could use the "tput cup" command to get this done...
P.S. This is a Linux/Unix thing only as far as I know...

Related

Lex program to count number of Vowels and Consonants doesnot to terminate even after end of input

I have written a simple program to count number of vowels and consonants in LEX. But the program does not terminate after I enter input and enters an infinite loop. Here's the program
%{
#include<stdio.h>
int vow_count = 0;
int const_count = 0;
%}
%%
[aeiouAEIOU] {vow_count++;}
[a-zA-Z] {const_count++;}
%%
int yywrap(){return 1;}
int main()
{
printf("Enter a string : ");
yylex();
printf("\n No. of vowels = %d", vow_count);
printf("\n No. of consonants = %d\n", const_count);
return 0;
}
Here's the output
I have tried almost everything, I have tried with "%option noyywrap", without the yywrap() function.
Also the the program works fine when I take input from a file.
Use getch(); after the last printf statement in the main function. Also, after giving your input in the command prompt, press Ctrl+Z. I'm using Windows, it worked for me.
You have to press ctrl + d to terminate.
After pressing ctrl + d you will get the below output.

strncpy functions produces wrong file names

I am new in C and writing a code to help my data analysis. Part of it opens predetermined files.
This piece of code is giving me problems and I cannot understand why.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXLOGGERS 26
// Declare the input files
char inputfile[];
char inputfile_hum[MAXLOGGERS][8];
// Declare the output files
char newfile[];
char newfile_hum[MAXLOGGERS][8];
int main()
{
int n = 2;
while (n > MAXLOGGERS)
{
printf("n error, n must be < %d: ", MAXLOGGERS);
scanf("%d", &n);
}
// Initialize the input and output file names
strncpy(inputfile_hum[1], "Ahum.csv", 8);
strncpy(inputfile_hum[2], "Bhum.csv", 8);
strncpy(newfile_hum[1], "Ahum.txt", 8);
strncpy(newfile_hum[2], "Bhum.txt", 8);
for (int i = 1; i < n + 1; i++)
{
strncpy(inputfile, inputfile_hum[i], 8);
FILE* file1 = fopen(inputfile, "r");
// Safety check
while (file1 == NULL)
{
printf("\nError: %s == NULL\n", inputfile);
printf("\nPress enter to exit:");
getchar();
return 0;
}
strncpy(newfile, newfile_hum[i], 8);
FILE* file2 = fopen(newfile, "w");
// Safety check
if (file2 == NULL)
{
printf("Error: file2 == NULL\n");
getchar();
return 0;
}
for (int c = fgetc(file1); c != EOF; c = fgetc(file1))
{
fprintf(file2, "%c", c);
}
fclose(file1);
fclose(file2);
}
// system("Ahum.txt");
// system("Bhum.txt");
}
This code produces two files but instead of the names:
Ahum.txt
Bhum.txt
the files are named:
Ahum.txtv
Bhum.txtv
The reason I am using strncpy in the for loop is because n will actually be inputted by the user later.
I see at least three problems here.
The first problem is that your character array is too small for your strings.
"ahum.txt", etc. will need to take nine characters. Eight for the actual text plus one more for the null terminating character.
The second problem is that you have declared the character arrays "newfile" and "inputfile" as empty arrays. These also need to be a number able to contain the strings (at least 9).
You're lucky to have not had a crash from overwriting memory out the program space.
The third and final problem is your use of strcpy().
strncpy(dest, src, n) will copy n characters from src to dest, but it won't copy final null terminator character if n is equal or less than size of the src string.
From strncpy() manpage: https://linux.die.net/man/3/strncpy
The strncpy() function ... at most n bytes of src are copied.
Warning: If there is no null byte among the first n bytes of src,
the string placed in dest will not be null-terminated.
Normally what you would want to do is have "n" be the size of the destination buffer minus 1 to allow for the null character.
For example:
strncpy(dest, src, sizeof(dest) - 1); // assuming dest is char array
There are a couple of problems with your code.
inputfile_hum, newfile_hum, need to be to be one char bigger for the trailing '\0' on strings.
char inputfile_hum[MAXLOGGERS][9];
...
char newfile_hum[MAXLOGGERS][9];
strncpy expects the first argument to be a char * region big enough to hold the expected results, so inputfile[] and outputfile[] need to be declared:
char inputfile[9];
char outputfile[9];

scanf hangs when copy and paste many line of inputs at a time

This may be a simple question, but I'm new to C, and yet couldn't find any answer. My program is simple, it takes 21 lines of string input in a for loop, and print them after that. The number could be less or greater.
int t = 21;
char *lines[t];
for (i = 0; i < t; i++) {
lines[i] = malloc(100);
scanf("%s", lines[i]);
}
for (int i = 0; i < t; i++) {
printf("%s\n", lines[i]);
free(lines[i]);
}
...
So when I copy & paste the inputs at a time, my program hangs, no error, no crash. It's fine if there's only 20 lines or below. And if I enter by hand line by line, it works normally regardless of number of inputs.
I'm using XCode 5 in Mac OS X 10.10, but I don't think this is the issue.
Update:
I tried to debug it when the program hangs, it stopped when i == 20 at the line below:
0x7fff9209430a: jae 0x7fff92094314 ; __read_nocancel + 20
The issue may be related to scanf, but it's so confused, why the number 20? May be I'm using it the wrong way, great thanks to any help.
Update:
I have tried to compile the program using the CLI gcc. It works just fine. So, it is the issue of XCode eventually. Somehow it prevents user from pasting multiple inputs.
Use fgets when you want to read a string in C , and see this documentation about that function:
[FGETS Function]
So you should use it like this :
fgets (lines[i],100,stdin);
So it'll get the string from the input of the user and you can have a look on these two posts as well about reading strings in C:
Post1
Post2
I hope that this'll help you with your problem.
Edit :
#include <stdio.h>
void main(){
int t = 21;
int i;
char *lines[t];
for (i = 0; i < t; i++) {
lines[i] = malloc(100);
fgets(lines[i],255,stdin);
}
for (i = 0; i < t; i++) {
printf("String %d : %s\n",i, lines[i]);
free(lines[i]);
}
}
This code gives :
As you can see , I got the 21 strings that I entered (From 0 to 20, that's why it stops when i==20).
I tried with your input ,here's the results :
I wrote the same code and ran. It works.
It might contain more than 99 characters (include line feed) per line...
Or it might contain spaces and tabs.
scanf(3)
When one or more whitespace characters (space, horizontal tab \t, vertical tab \v, form feed \f, carriage return \r, newline or linefeed \n) occur in the format string, input data up to the first non-whitespace character is read, or until no more data remains. If no whitespace characters are found in the input data, the scanning is complete, and the function returns.
To avoid this, try
scanf ("%[^\n]%*c", lines[i]);
The whole code is:
#include <stdio.h>
int main() {
const int T = 5;
char lines[T][100]; // length: 99 (null terminated string)
// if the length per line is fixed, you don't need to use malloc.
printf("input -------\n");
for (int i = 0; i < T; i++) {
scanf ("%[^\n]%*c", lines[i]);
}
printf("result -------\n");
for (int i = 0; i < T; i++) {
printf("%s\n", lines[i]);
}
return 0;
}
If you still continue to face the problem, show us the input data and more details. Best regards.

How to use fgets() to safely handle user input more than once

I'm sorry if I duplicate, but I have tried EVERYTHING, and I can't figure out why this code keeps breaking. The highest-priority goal was to make this code handle input safely, or just anything that the user can type into the console, without it breaking. However, I also need it to be able to run more than once. fgets() won't let me do that since it keeps reading '\n' somewhere and preventing me from entering input more than once when it hits the end of the do/while loop. I have tried fflushing stdin, I have tried scanf("%d *[^\n]"); and just regular scanf("%d *[^\n]");, but none of those work, and, in fact, they break the code! I used this website to try to get the "Safely handling input" code to work, but I don't completely understand what they're doing. I tried to jerry-rig (spelling?) it as best I could, but I'm not sure if I did it right. Did I miss something? I didn't think a problem this seemingly simple could be so much of a headache! >_<
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
using namespace std;
#define BUF_LEN 100
#define SPACE 32
#define SPCL_CHAR1F 33
#define SPCL_CHAR1L 47
#define SPCL_CHAR2F 58
#define SPCL_CHAR2L 64
#define SPCL_CHAR3F 91
#define SPCL_CHAR3L 96
#define NUMF 48
#define NUML 57
#define UC_CHARF 65
#define UC_CHARL 90
#define LC_CHARF 97
#define LC_CHARL 122
void main ()
{
char* buffer;
int SpcCounter=0, SpclCounter=0, NumCounter=0,LcCounter=0, UcCounter=0;
char line[BUF_LEN],response[4];
char*input="";
bool repeat=false;
do
{
for(int i=0;i<BUF_LEN;i++)
{
line[i]=NULL;
}
buffer=NULL;
printf("Enter your mess of characters.\n");
buffer=fgets(line,BUF_LEN,stdin);
//To handle going over the buffer limit: BROKEN
if(buffer!=NULL)
{
size_t last=strlen(line)-1;
if(line[last]=='\n')
line[last]='\0';
else
{
fscanf(stdin,"%c *[^\n]");
}
}
for(int i=0;i<BUF_LEN;i++)
{
char temp=buffer[i];
if(temp==SPACE||temp==255)
SpcCounter++;
else if((temp >= SPCL_CHAR1F && temp <= SPCL_CHAR1L)||/*Special characters*/
(temp >= SPCL_CHAR2F && temp <= SPCL_CHAR2L)||
(temp >= SPCL_CHAR3F && temp <= SPCL_CHAR3L))
SpclCounter++;
else if (temp >=NUMF && temp <= NUML)/*Numbers*/
NumCounter++;
else if (temp >= UC_CHARF && temp <= UC_CHARL)/*Uppercase letters*/
UcCounter++;
else if (temp >= LC_CHARF && temp <= LC_CHARL)/*Lowercase letters*/
LcCounter++;
}
printf("There were %i space%s, %i special character%s, %i number%s, and %i letter%s,\n"
"consisting of %i uppercase letter%s and %i lowercase.\n",
SpcCounter,(SpcCounter==1?"":"s"),SpclCounter,(SpclCounter==1?"":"s"), NumCounter,(NumCounter==1?"":"s"),UcCounter+LcCounter,
(UcCounter+LcCounter==1?"":"s"), UcCounter,(UcCounter==1?"":"s"), LcCounter);
printf("Would you like to do this again? (yes/no)");
input=fgets(response,4,stdin);
/*
ALL BROKEN
if(input!=NULL)
{
size_t last=strlen(response)-1;
if(response[last]=='\n')
response[last]='\0';
else
{
fscanf(stdin,"%*[^\n]");
fscanf(stdin,"%c");
}
}
*/
//To capitalize the letters
for(int i=0;i<4;i++)
{
char* temp=&response[i];
if (*temp >= LC_CHARF && *temp <= LC_CHARL)
*temp=toupper(*temp);//Capitalize it
}
//To set repeat: WORKS, BUT WEIRD
repeat=!strncmp(input,"YES",4);
}
while(repeat);
}
For safe, secure user input in C (and in C++ if I'm using C-style strings), I usually revert to an old favorite of mine, the getLine function:
// Use stdio.h and string.h for C.
#include <cstdio>
#include <cstring>
#define OK 0
#define NO_INPUT 1
#define TOO_LONG 2
static int getLine (char *prmpt, char *buff, size_t sz) {
int ch, extra;
// Output prompt then get line with buffer overrun protection.
if (prmpt != NULL) {
printf ("%s", prmpt);
fflush (stdout);
}
if (fgets (buff, sz, stdin) == NULL)
return NO_INPUT;
// If it was too long, there'll be no newline. In that case, we flush
// to end of line so that excess doesn't affect the next call.
if (buff[strlen(buff)-1] != '\n') {
extra = 0;
while (((ch = getchar()) != '\n') && (ch != EOF))
extra = 1;
return (extra == 1) ? TOO_LONG : OK;
}
// Otherwise remove newline and give string back to caller.
buff[strlen(buff)-1] = '\0';
return OK;
}
This function:
can output a prompt if desired.
uses fgets in a way that avoids buffer overflow.
detects end-of-file during the input.
detects if the line was too long, by detecting lack of newline at the end.
removes the newline if there.
"eats" characters until the next newline to ensure that they're not left in the input stream for the next call to this function.
It's a fairly solid piece of code that's been tested over many years and is a good solution to the problem of user input.
In terms of how you call it for the purposes in your question, I would add something very similar to what you have, but using the getLine function instead of directly calling fgets and fiddling with the results. First some headers and the same definitions:
#include <iostream>
#include <cstdlib>
#include <cctype>
#define BUF_LEN 100
#define SPACE 32
#define SPCL_CHAR1F 33
#define SPCL_CHAR1L 47
#define SPCL_CHAR2F 58
#define SPCL_CHAR2L 64
#define SPCL_CHAR3F 91
#define SPCL_CHAR3L 96
#define NUMF 48
#define NUML 57
#define UC_CHARF 65
#define UC_CHARL 90
#define LC_CHARF 97
#define LC_CHARL 122
Then the first part of main gathering a valid line (using the function) to be evaluated:
int main () {
int SpcCounter, SpclCounter, NumCounter, LcCounter, UcCounter;
char line[BUF_LEN], response[4];
bool repeat = false;
do {
SpcCounter = SpclCounter = NumCounter = LcCounter = UcCounter = 0;
// Get a line until valid.
int stat = getLine ("\nEnter a line: ", line, BUF_LEN);
while (stat != OK) {
// End of file means no more data possible.
if (stat == NO_INPUT) {
cout << "\nEnd of file reached.\n";
return 1;
}
// Only other possibility is "Too much data on line", try again.
stat = getLine ("Input too long.\nEnter a line: ", line, BUF_LEN);
}
Note that I've changed where the counters are set to zero. Your method had them accumulating values every time through the loop rather than resetting them to zero for each input line. This is followed by your own code which assigns each character to a class:
for (int i = 0; i < strlen (line); i++) {
char temp=line[i];
if(temp==SPACE||temp==255)
SpcCounter++;
else if((temp >= SPCL_CHAR1F && temp <= SPCL_CHAR1L)||
(temp >= SPCL_CHAR2F && temp <= SPCL_CHAR2L)||
(temp >= SPCL_CHAR3F && temp <= SPCL_CHAR3L))
SpclCounter++;
else if (temp >=NUMF && temp <= NUML)
NumCounter++;
else if (temp >= UC_CHARF && temp <= UC_CHARL)
UcCounter++;
else if (temp >= LC_CHARF && temp <= LC_CHARL)
LcCounter++;
}
printf("There were %i space%s, %i special character%s, "
"%i number%s, and %i letter%s,\n"
"consisting of %i uppercase letter%s and "
"%i lowercase.\n",
SpcCounter, (SpcCounter==1?"":"s"),
SpclCounter, (SpclCounter==1?"":"s"),
NumCounter, (NumCounter==1?"":"s"),
UcCounter+LcCounter, (UcCounter+LcCounter==1?"":"s"),
UcCounter, (UcCounter==1?"":"s"),
LcCounter);
Then finally, a similar way as above for asking whether user wants to continue.
// Get a line until valid yes/no, force entry initially.
*line = 'x';
while ((*line != 'y') && (*line != 'n')) {
stat = getLine ("Try another line (yes/no): ", line, BUF_LEN);
// End of file means no more data possible.
if (stat == NO_INPUT) {
cout << "\nEnd of file reached, assuming no.\n";
strcpy (line, "no");
}
// "Too much data on line" means try again.
if (stat == TOO_LONG) {
cout << "Line too long.\n";
*line = 'x';
continue;
}
// Must be okay: first char not 'y' or 'n', try again.
*line = tolower (*line);
if ((*line != 'y') && (*line != 'n'))
cout << "Line doesn't start with y/n.\n";
}
} while (*line == 'y');
}
That way, you build up your program logic based on a solid input routine (which hopefully you'll understand as a separate unit).
You could further improve the code by removing the explicit range checks and using proper character classes with cctype(), like isalpha() or isspace(). That would make it more portable (to non-ASCII systems) but I'll leave that as an exercise for later.
A sample run of the program is:
Enter a line: Hello, my name is Pax and I am 927 years old!
There were 10 spaces, 2 special characters, 3 numbers, and 30 letters,
consisting of 3 uppercase letters and 27 lowercase.
Try another line (yes/no): yes
Enter a line: Bye for now
There were 2 spaces, 0 special characters, 0 numbers, and 9 letters,
consisting of 1 uppercase letter and 8 lowercase.
Try another line (yes/no): no

How do I change the name of Emacs auto-recovery file?

At the moment it saves the file with format:
.#main.c -> sara#sara.home.com.27017:1231918415
This makes it problematic since it ends with ".c".
I need it to be .#main.c#
Update: I have emacs 22.1
That's not the auto-recovery file, that's the link used as a locking token for the file.
update
If I tell you, will you introduce me to Summer Glau?
It's probably not going to be easy to change that; I just dug a bit and it looks like it's set in the C code. But let's ask the next question: why do you want to? I'm guessing you're hitting a regular expression for .c files that you don't want to match these. If so, note that all these lockfile links start with .# -- invariably, that's hardcoded -- so you could always exclude files with names that match "^.#" (depending on which regex syntax you use.)
If you really want to hack at it, it's in filelock.c at about line 320 in EMACS 22. Here's the code:
/* Write the name of the lock file for FN into LFNAME. Length will be
that of FN plus two more for the leading `.#' plus 1 for the
trailing period plus one for the digit after it plus one for the
null. */
#define MAKE_LOCK_NAME(lock, file) \
(lock = (char *) alloca (SBYTES (file) + 2 + 1 + 1 + 1), \
fill_in_lock_file_name (lock, (file)))
static void
fill_in_lock_file_name (lockfile, fn)
register char *lockfile;
register Lisp_Object fn;
{
register char *p;
struct stat st;
int count = 0;
strcpy (lockfile, SDATA (fn));
/* Shift the nondirectory part of the file name (including the null)
right two characters. Here is one of the places where we'd have to
do something to support 14-character-max file names. */
for (p = lockfile + strlen (lockfile); p != lockfile && *p != '/'; p--)
p[2] = *p;
/* Insert the `.#'. */
p[1] = '.';
p[2] = '#';
p = p + strlen (p);
while (lstat (lockfile, &st) == 0 && !S_ISLNK (st.st_mode))
{
if (count > 9)
{
*p = '\0';
return;
}
sprintf (p, ".%d", count++);
}
}
You can upgrade to emacs 24.3 and add to your .emacs file the following line:
(setq create-lockfiles nil)
That's strange, the default should have # at each end..
You can customize the name by redefining the auto-save-file-name-p and make-auto-save-file-name functions. In GNU emacs you would add the elisp to your .emacs file.