EDIT
The solution to the problem was understanding what Ctrl-D was actually doing.
On a new empty line, a single Ctrl-D will signal EOF.
But if there are characters already in the line, the first Ctrl-D causes the contents of the line to be echoed to the screen (but not written to STDOUT
). With characters already in the buffer, a second Ctrl-D must be issued to signal EOF
, thus writing the buffer to STDOUT
.
This can be demonstrated by redirecting output to a file.
EDIT
I'm using fgetc() to read input from stdin
. I loop until I receive an EOF. In the loop I build a string based on the characters typed before Ctrl-D was pressed. But I can't figure out a way of exiting the loop since the buffer ch = fgetc() reads from does not contain the EOF. (The EOF only triggers the fgetc() to return its first value.)
ungetc() does not allow pushing an EOF into the buffer, pushing any other char runs the risk of confusion with real data, I'm stuck!! I've read a LOT of answers but they don't address this issue or don't apply to the use-case I'm trying to implement.
I would like to be able to count, peek, etc on the stdin buffer.
I don't really want to read a whole line (or X chars at a time) because I'm processing each character as it arrives (edit) from fgetc().
Any suggestions on how to overcome this dilemma? (Without using NCurses)
I'm using Ubuntu. EOF = Ctrl-D Here is some code I'm working with:
This works, and does the same as Jonathan's simple example, but not what I want:
int main(int argc, char **argv) {
int inputChr;
do {
inputChr = fgetc(stdin);
if (inputChr != EOF) {
fputc( inputChr, stdout);
}
if (feof(stdin)) {
if (ferror(stdin)) {
perror(NULL);
return errno;
}
}
} while (inputChr != EOF);
return EXIT_SUCCESS;
}
HOWEVER, this is getting stuck but is trying to do what I want (edit) but requires Ctrl-D a second time:
char *buildLine (FILE *inputSource, char *currLine, int showTabs, int showNonPrint, int *haveLF) {
int inputChr;
char *thisLine = malloc(1);
int inputSize;
*haveLF = FALSE;
while ( (inputChr = fgetc(inputSource)) != EOF ) {
if (ferror(inputSource)) {
perror(NULL);
} else {
if (inputChr == LF) {
*haveLF = TRUE;
} else {
thisLine = strconcat(thisLine,(char *)&inputChr);
}
}
}
return thisLine;
}
Some more code that's been asked about:
char * strconcat ( char *str1, char * str2) {
char *newStr = malloc(strlen(str1)+strlen(str2)+1);
if (newStr == NULL) {
return NULL;
}
strcpy(newStr,str1);
strcat(newStr,str2);
return newStr;
}
THIS VERSION BELOW processes the input character by character and works just like cat
. But I decided I would process each character into a line first, before applying some extra transforms I need to implement. This simplified the state-machine design, but maybe trying to build lines wasn't good option (without using NCurses). :(
int echoInput( FILE *inputSource, FILE *outputDestination, int numbers, int showEnds) {
int haveNewLine = TRUE;
int lineNo = 1;
int inputChr;
do {
inputChr = fgetc(inputSource);
if (inputChr != EOF) {
if (numbers && haveNewLine) {
long lineNoSize = (long) log10(lineNo)+1; // effectively floor(log10(lineNo)+1) = number digits
char *lineNoStr = (lineNoSize<6)?malloc(8):malloc(lineNoSize+2); // If less than 6 digits, allow for minimum 6 plus tab. Also +1 for terminator.
if (lineNoStr == NULL) {
printf ("Error::Out of Memory");
return ENOMEM;
}
sprintf(lineNoStr,"%6d\t",lineNo); // format lineNo string
fputs(lineNoStr, outputDestination); // send string to output
lineNo++;
haveNewLine = FALSE;
}
if (inputChr == LF) {
if (showEnds) {
fputc('$', outputDestination); // send char to output
}
haveNewLine = TRUE;
}
fputc( inputChr, outputDestination);
}
if (feof(inputSource)) {
if (ferror(inputSource)) {
perror(NULL);
return errno;
}
}
if (ferror(outputDestination)) {
perror(NULL);
return errno;
}
} while (inputChr != EOF);
return EXIT_SUCCESS;
}