Le problème est que tu modifies le type de stream de l'entrée standard, en l'occurrence un pipe au lieu d'un périphérique intératif style terminal. Hors la norme est très clair à ce sujet, l'entrée standard (son flux plus exactement) est fully buffered par défaut pour l'entrée standard à moins que le flux sous jacent ne soit un périphérique intéractif. Ici tu es sauver car cette implémentation de tr modifie de type de bufférisation en line buffered, c'est pour cela qu'un retour charriot valide l'entrée. Si demain tu prends une implémentation qui ne le fait pas, les données en entrées ne sont reçu (via les fonctions standard du C biensûr) qu'au moment ou le buffer est plein. Et c'est la que l'interblocage peut se produire, si tu écris un programme tiers censé manipulé ce wrapper, les données que tu vas lui envoyés ne déclencherons rien tant que le buffer ne sera pas plein, ce n'est pas ce que l'on veut, on veut valider l'entrée sur la saisie d'une ligne.
Je ne sais pas si j'ai réussi à me faire comprendre mais souvent une grosse source d'erreur dans les co-processus.