Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does fgets on OS X output a BELL (^G, ascii: 07) character when input exceeds expected length?

Tags:

c

xcode

macos

Test-case program:

#include <stdio.h>
#define SIZE 1024
int main(int args,char *argv[]){
  char buf[SIZE];
  fgets(buf, SIZE, stdin);
  return 0;
}

Example usage (i.e. entering 1024 x characters):

bash-3.2$ gcc read.c -o read
bash-3.2$ ./read
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
^G

On OS X, this beeps the terminal and waits. I can hit RET again and it beeps again. If I run it in a pipe or from Emacs's shell, I see the ^G character being outputted.

With the same exact invocation on my Ubuntu VM, the program completes as expected (i.e. read 1023 bytes into buf and null-terminate it).

This is interrupting a program I'm trying to write (in another language, but uses the C library underneath) that takes in lines of JSON as part of a command interface. So the question is: how do I disable this behaviour in OS X? Is there an API call to turn it off? Perhaps an environment variable? It's so bizzare.

like image 301
Christopher Done Avatar asked Nov 10 '22 04:11

Christopher Done


1 Answers

You can prevent this behavior by adding this to your ~/.bash_profile file:

stty -imaxbel

This command is documented in this Apple manpage for the stty program.

imaxbel (-imaxbel) The system imposes a limit of MAX_INPUT (currently 255) characters in the input queue. If imaxbel is set and the input queue limit has been reached, subsequent input causes the system to send an ASCII BEL character to the output queue (the terminal beeps at you). Otherwise, if imaxbel is unset and the input queue is full, the next input character causes the entire input and output queues to be discarded.

You also have the choice of manually issuing stty -imaxbel in your current shell. Without adding it to a startup file (like ~/.bash_profile) the setting will revert to its default the next time you log in.

If you require line buffering to be disabled and every character passed to the program as it is typed then one way is to use stty cbreak . One of the primary things it changes is to disable icanon mode which controls whether you are in buffered line mode or not. This article has a reasonable discussion in layman terms of cbreak and icanon:

ICANON - Perhaps the most important bit in c_lflag is the ICANON bit. Enabling it enables "canonical" mode – also known as "line editing" mode. When ICANON is set, the terminal buffers a line at a time, and enables line editing. Without ICANON, input is made available to programs immediately (this is also known as "cbreak" mode).

You may be able to get the results you are looking for by a command like:

stty cbreak -imaxbel

This goes into character by character mode and ensures the BEL is disabled.

like image 110
Michael Petch Avatar answered Nov 14 '22 21:11

Michael Petch