aaio1.c 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  1. /*
  2. * AAIO Advanced I/O
  3. * -----------------
  4. *
  5. * Many people moving from Windows programming to UNIX program have problems
  6. * with the missing non-blocking getch() and getche() functions provided by
  7. * conio.h. This library provides the functionality of getch(), getche() and
  8. * kbhit(). It does not require an initialization (like curses) and does
  9. * not abuse the terminal (i.e. whenever the mode of the terminal is
  10. * changed its state is stored so it can be restored). For increased
  11. * efficiency there exists funcionality to allow abuse of the terminal as well
  12. * as functions to restore or reset the terminal when the application exits.
  13. */
  14. /*
  15. -----BEGIN PGP SIGNED MESSAGE-----
  16. Hash: SHA1
  17. * Copyright (c) 2004-2005 by Daniel Aarno - <[email protected]>
  18. * M. Sc. Electrical Engineering * http://www.nada.kth.se/~bishop
  19. *
  20. * All Rights Reserved
  21. * ATTRIBUTION ASSURANCE LICENSE (adapted from the original BSD license)
  22. * Redistribution and use in source and binary forms, with or without
  23. * modification, are permitted provided that the conditions below are met.
  24. * These conditions require a modest attribution to (the
  25. * "Author"), who hopes that its promotional value may help justify the
  26. * thousands of dollars in otherwise billable time invested in writing
  27. * this and other freely available, open-source software.
  28. *
  29. * 1. Redistributions of source code, in whole or part and with or without
  30. * modification (the "Code"), must prominently display this GPG-signed
  31. * text in verifiable form.
  32. * 2. Redistributions of the Code in binary form must be accompanied by
  33. * this GPG-signed text in any documentation and, each time the resulting
  34. * executable program or a program dependent thereon is launched, a
  35. * prominent display (e.g., splash screen or banner text) of the Author's
  36. * attribution information, which includes:
  37. * (a) Name ("Daniel Aarno"),
  38. * (b) Professional identification ("M. Sc. Electrical Engineering"), and
  39. * (c) URL ("http://www.nada.kth.se/~bishop").
  40. * 3. Neither the name nor any trademark of the Author may be used to
  41. * endorse or promote products derived from this software without specific
  42. * prior written permission.
  43. * 4. Users are entirely responsible, to the exclusion of the Author and
  44. * any other persons, for compliance with (1) regulations set by owners or
  45. * administrators of employed equipment, (2) licensing terms of any other
  46. * software, and (3) local regulations regarding use, including those
  47. * regarding import, export, and use of encryption software.
  48. *
  49. * THIS FREE SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND
  50. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  51. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  52. * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
  53. * EVENT SHALL THE AUTHOR OR ANY CONTRIBUTOR BE LIABLE FOR
  54. * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  55. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  56. * EFFECTS OF UNAUTHORIZED OR MALICIOUS NETWORK ACCESS;
  57. * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  58. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  59. * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  60. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  61. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
  62. * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  63. -----BEGIN PGP SIGNATURE-----
  64. Version: GnuPG v1.2.4 (GNU/Linux)
  65. iD8DBQFCHuXui6ECThHTSIkRAk9qAKCVs7kMSUtv5YhljeQsAA52EcjTFgCeNflz
  66. w0lAUG3zeHQcJ+7t6tpce4s=
  67. =qlVs
  68. -----END PGP SIGNATURE-----
  69. */
  70. #include <stdlib.h>
  71. #include <stdio.h>
  72. #include <string.h>
  73. #include <unistd.h>
  74. #include <errno.h>
  75. #include <sys/types.h>
  76. #include <sys/wait.h>
  77. #include <sys/ioctl.h>
  78. #ifdef HAVE_SYS_FILIO_H
  79. //needed on solaris to get FIONREAD
  80. #include <sys/filio.h>
  81. #endif
  82. ///////////////////////////////////////////////////////////////////////////////
  83. #ifndef RESET_PATH
  84. #define RESET_PATH "/usr/bin/reset"
  85. #endif
  86. #ifndef STTY_PATH
  87. #define STTY_PATH "/bin/stty"
  88. #endif
  89. #define STR_SIZE 256
  90. #define FREE(p) free(p); (p) = NULL;
  91. ///////////////////////////////////////////////////////////////////////////////
  92. static char g_stty_lock = 0;
  93. static char g_echo = -1;
  94. static char *g_stty_setting = NULL;
  95. //////////////////////////// HELPER FUNCTIONS /////////////////////////////////
  96. static int
  97. get_stty_setting(char* buf, int nmemb)
  98. {
  99. int p[2]; //pipe
  100. int stat;
  101. int n;
  102. pid_t pid;
  103. errno = 0;
  104. pipe(p); //Create a pipe
  105. if((pid = fork()) == 0) { //Child process
  106. char *const argv[] = {"stty", "-g", NULL};
  107. dup2(p[1], STDOUT_FILENO); //Connect the write end of the pipe to stdout
  108. close(p[0]); //close read end of pipe
  109. close(p[1]); //close read end of pipe (now connected to stdout)
  110. execv(STTY_PATH, argv); //Start stty process
  111. exit(-1); //Should never get here
  112. }
  113. if(pid <= 0) { //Fork failed
  114. return -1;
  115. }
  116. close(p[1]); //Close write end of pipe
  117. n = read(p[0], buf, nmemb - 1); //Read the data
  118. close(p[0]); //Close read end of pipe
  119. if(n >= 0) { //If we got some data, zero terminate the string
  120. buf[n] = '\0';
  121. }
  122. if(n == nmemb - 1) { //Buffer to small
  123. errno = ENOMEM;
  124. return -1;
  125. }
  126. //Wait for child to finish
  127. wait(&stat);
  128. if(n < 0) //If read failed, return error
  129. return -1;
  130. if(!WIFEXITED(stat)) { //Child did not exit OK
  131. errno = ECHILD;
  132. return -1;
  133. }
  134. //Child exit OK, we read some data and all is well here, return exit
  135. //status of child
  136. return WEXITSTATUS(stat);
  137. }
  138. static int
  139. set_stty_setting(const char* s)
  140. {
  141. char* buf;
  142. int l;
  143. l = strlen(STTY_PATH);
  144. buf = (char*)malloc(strlen(s) + l + 1);
  145. (void)memcpy(buf, STTY_PATH, l);
  146. buf[l] = ' ';
  147. buf[l + 1] = '\0';
  148. (void)strcat(buf, s);
  149. l = system(buf);
  150. FREE(buf);
  151. return l;
  152. }
  153. static int
  154. getch_2(void)
  155. {
  156. char buf[STR_SIZE];
  157. int i;
  158. if(0 != get_stty_setting(buf, STR_SIZE)) {
  159. return -1;
  160. }
  161. //set stty to raw mode, no echo
  162. if(0 != set_stty_setting("raw -echo")) {
  163. //if not able to set stty raw mode
  164. return -1;
  165. }
  166. i = getchar();
  167. if(0 != set_stty_setting(buf)) {
  168. //Reset stty to original mode
  169. return -1;
  170. }
  171. system("stty -g > /dev/null");
  172. if(i == EOF)
  173. return -1;
  174. return i;
  175. }
  176. static int
  177. getche_2(void)
  178. {
  179. char buf[STR_SIZE];
  180. int i;
  181. if(0 != get_stty_setting(buf, STR_SIZE)) {
  182. return -1;
  183. }
  184. //set stty to raw mode, no echo
  185. if(0 != set_stty_setting("raw echo")) {
  186. //if not able to set stty raw mode
  187. return -1;
  188. }
  189. i = getchar();
  190. if(0 != set_stty_setting(buf)) {
  191. //Reset stty to original mode
  192. return -1;
  193. }
  194. if(i == EOF)
  195. return -1;
  196. return i;
  197. }
  198. static int
  199. kbhit_2(void)
  200. {
  201. char buf[STR_SIZE];
  202. int i;
  203. if(0 != get_stty_setting(buf, STR_SIZE)) {
  204. return -1;
  205. }
  206. //set stty to raw mode, no echo
  207. if(0 != set_stty_setting("raw -echo")) {
  208. //if not able to set stty raw mode
  209. return -1;
  210. }
  211. if(-1 == ioctl(STDIN_FILENO, FIONREAD, &i))
  212. return -1;
  213. if(0 != set_stty_setting(buf)) {
  214. //Reset stty to original mode
  215. return -1;
  216. }
  217. return i;
  218. }
  219. ///////////////////////////////////////////////////////////////////////////////
  220. ///////////////////////////////////////////////////////////////////////////////
  221. // EXPORTED LIBRARY ROUTINES //
  222. ///////////////////////////////////////////////////////////////////////////////
  223. ///////////////////////////////////////////////////////////////////////////////
  224. int
  225. aaio_grant_tty_lock(void)
  226. {
  227. if(g_stty_lock) {
  228. errno = EPERM;
  229. return -1;
  230. }
  231. if(g_stty_setting == NULL) {
  232. g_stty_setting = (char*)malloc(STR_SIZE);
  233. if(g_stty_setting == NULL)
  234. return -1;
  235. if(0 != get_stty_setting(g_stty_setting, STR_SIZE)) {
  236. FREE(g_stty_setting);
  237. return -1;
  238. }
  239. }
  240. if(set_stty_setting("raw -echo") < 0) {
  241. FREE(g_stty_setting);
  242. return -1;
  243. }
  244. g_stty_lock = 1;
  245. g_echo = 0;
  246. return 0;
  247. }
  248. int
  249. aaio_reset(void)
  250. {
  251. if(set_stty_setting(g_stty_setting))
  252. return -1;
  253. FREE(g_stty_setting);
  254. g_stty_lock = 0;
  255. return 0;
  256. }
  257. int
  258. aaio_hard_reset(void)
  259. {
  260. if(system(RESET_PATH))
  261. return -1;
  262. FREE(g_stty_setting);
  263. g_stty_lock = 0;
  264. return 0;
  265. }
  266. int
  267. getch(void)
  268. {
  269. if(!g_stty_lock)
  270. return getch_2(); //Switch modes back and forth == slower
  271. //We got the lock here, abuse the stty any way we like
  272. if(g_echo) { //We don't want echo
  273. g_echo = 0;
  274. if(0 != system("/bin/stty -echo")) //Disable echo
  275. return -1;
  276. }
  277. return getchar();
  278. }
  279. int
  280. getche(void)
  281. {
  282. if(!g_stty_lock)
  283. return getche_2(); //Switch modes back and forth == slower
  284. //We got the lock here, abuse the stty any way we like
  285. if(!g_echo) { //We want echo
  286. g_echo = 1;
  287. if(0 != system("/bin/stty echo")) //Enable echo
  288. return -1;
  289. }
  290. return getchar();
  291. }
  292. int
  293. kbhit(void)
  294. {
  295. int i;
  296. if(!g_stty_lock)
  297. return kbhit_2();
  298. //We got the lock here, abuse the stty any way we like
  299. if(g_echo) { //If we have echo, disable it
  300. g_echo = 0;
  301. if(0 != set_stty_setting("raw -echo")) {
  302. //if not able to set stty raw, no echo mode
  303. return -1;
  304. }
  305. }
  306. if(-1 == ioctl(STDIN_FILENO, FIONREAD, &i))
  307. return -1;
  308. return i;
  309. }
  310. int aaio_flush(void)
  311. {
  312. int n = kbhit();
  313. int i;
  314. for(i = 0; i < n; i++)
  315. (void)getch();
  316. return n;
  317. }