InputDevices.cpp 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. /*
  2. * The contents of this file are subject to the Initial
  3. * Developer's Public License Version 1.0 (the "License");
  4. * you may not use this file except in compliance with the
  5. * License. You may obtain a copy of the License at
  6. * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl.
  7. *
  8. * Software distributed under the License is distributed AS IS,
  9. * WITHOUT WARRANTY OF ANY KIND, either express or implied.
  10. * See the License for the specific language governing rights
  11. * and limitations under the License.
  12. *
  13. * The Original Code was created by Claudio Valderrama on 5-Oct-2007
  14. * for the Firebird Open Source RDBMS project.
  15. *
  16. * Copyright (c) 2007 Claudio Valderrama
  17. * and all contributors signed below.
  18. *
  19. * All Rights Reserved.
  20. * Contributor(s): ______________________________________.
  21. *
  22. */
  23. #include "firebird.h"
  24. #if defined(DARWIN) && !defined(IOS)
  25. #if defined(i386) || defined(__x86_64__)
  26. #include <architecture/i386/io.h>
  27. #else
  28. #include <io.h>
  29. #endif
  30. #endif
  31. #ifdef HAVE_UNISTD_H
  32. #include <unistd.h>
  33. #endif
  34. #include "../common/utils_proto.h"
  35. #include "InputDevices.h"
  36. using Firebird::PathName;
  37. InputDevices::indev::indev()
  38. : indev_fpointer(0), indev_line(0), indev_aux(0), indev_next(0),
  39. indev_fn(*getDefaultMemoryPool()), indev_fn_display(*getDefaultMemoryPool())
  40. {
  41. makeFullFileName();
  42. }
  43. InputDevices::indev::indev(FILE* fp, const char* fn, const char* fn_display)
  44. : indev_fpointer(fp), indev_line(0), indev_aux(0), indev_next(0),
  45. indev_fn(*getDefaultMemoryPool()), indev_fn_display(*getDefaultMemoryPool())
  46. {
  47. indev_fn = fn;
  48. indev_fn_display = fn_display;
  49. makeFullFileName();
  50. }
  51. // Performs the same task that one of the constructors, but called manually.
  52. // File handle and file name are assumed to be valid and the rest of the data
  53. // members aren't copied but reset.
  54. void InputDevices::indev::init(FILE* fp, const char* fn, const char* fn_display)
  55. {
  56. indev_fpointer = fp;
  57. indev_line = 0;
  58. indev_aux = 0;
  59. indev_fn = fn;
  60. indev_fn_display = fn_display;
  61. indev_next = 0;
  62. makeFullFileName();
  63. }
  64. // Copies only the file handle and file name from one indev to another.
  65. // File handle and file name are assumed to be valid.
  66. void InputDevices::indev::init(const indev& src)
  67. {
  68. indev_fpointer = src.indev_fpointer;
  69. indev_line = 0;
  70. indev_aux = 0;
  71. indev_fn = src.indev_fn;
  72. indev_fn_display = src.indev_fn_display;
  73. indev_next = 0;
  74. }
  75. //InputDevices::indev::~indev()
  76. //{
  77. // Nothing for now. Let the owner or caller close the file pointer if needed.
  78. //}
  79. // Initializes one indev with another. Useful for method of the owning class
  80. // that needs to copy from the input indev to the top of the stack and vice-versa.
  81. void InputDevices::indev::copy_from(const indev* src)
  82. {
  83. fb_assert(src);
  84. indev_fpointer = src->indev_fpointer;
  85. indev_line = src->indev_line;
  86. indev_aux = src->indev_aux;
  87. indev_fn = src->indev_fn;
  88. indev_fn_display = src->indev_fn_display;
  89. // indev_next not copied.
  90. }
  91. // Drop a file associated with an indev.
  92. void InputDevices::indev::drop()
  93. {
  94. fb_assert(indev_fpointer != stdin);
  95. fb_assert(!indev_fn.isEmpty()); // Some name should exist.
  96. fclose(indev_fpointer);
  97. unlink(indev_fn.c_str());
  98. }
  99. // Save the reading position in the parameter.
  100. void InputDevices::indev::getPos(fpos_t* out) const
  101. {
  102. fb_assert(out);
  103. fb_assert(indev_fpointer);
  104. fb_io::fgetpos(indev_fpointer, out);
  105. }
  106. // Restore a previously stored reading position held in the parameter.
  107. void InputDevices::indev::setPos(const fpos_t* in)
  108. {
  109. fb_assert(in);
  110. fb_assert(indev_fpointer);
  111. #ifdef SFIO
  112. // hack to fix bad sfio header
  113. fb_io::fsetpos(indev_fpointer, const_cast<fpos_t*>(in));
  114. #else
  115. fb_io::fsetpos(indev_fpointer, in);
  116. #endif
  117. }
  118. void InputDevices::indev::makeFullFileName()
  119. {
  120. if (!indev_fn.isEmpty() && PathUtils::isRelative(indev_fn))
  121. {
  122. PathName name = indev_fn;
  123. PathName path;
  124. fb_utils::getCwd(path);
  125. PathUtils::concatPath(indev_fn, path, name);
  126. }
  127. }
  128. // Clear the chain of indev, closing file handles.
  129. void InputDevices::clear(FILE* fpointer)
  130. {
  131. while (m_head)
  132. {
  133. FILE* const p = m_head->indev_fpointer;
  134. if (fpointer && p == fpointer)
  135. break;
  136. if (p != stdin && p != m_ofp.indev_fpointer)
  137. fclose(p);
  138. indev* const flist = m_head->indev_next;
  139. delete m_head;
  140. m_head = flist;
  141. --m_count;
  142. }
  143. if (m_ifp.indev_fpointer && // In case we called clear(NULL) manually before.
  144. m_ifp.indev_fpointer != stdin && m_ifp.indev_fpointer != m_ofp.indev_fpointer)
  145. {
  146. fclose(m_ifp.indev_fpointer);
  147. m_ifp.indev_fpointer = 0;
  148. }
  149. fb_assert(m_count == 0 || fpointer != 0);
  150. if (!fpointer)
  151. {
  152. m_head = 0;
  153. m_count = 0;
  154. }
  155. }
  156. // Insert an indev in the chain, always in LIFO way.
  157. bool InputDevices::insert(FILE* fp, const char* name, const char* display)
  158. {
  159. if (!m_head)
  160. {
  161. fb_assert(m_count == 0);
  162. m_head = FB_NEW indev(fp, name, display);
  163. }
  164. else
  165. {
  166. fb_assert(m_count > 0);
  167. indev* p = m_head;
  168. m_head = FB_NEW indev(fp, name, display);
  169. m_head->indev_next = p;
  170. }
  171. ++m_count;
  172. return true;
  173. }
  174. // Shortcut for inserting the currently input file in the indev chain.
  175. bool InputDevices::insertIfp()
  176. {
  177. if (insert(NULL, "", ""))
  178. {
  179. m_head->copy_from(&m_ifp);
  180. return true;
  181. }
  182. return false;
  183. }
  184. // Remove the top (last inserted) indev in the chain.
  185. bool InputDevices::remove()
  186. {
  187. if (m_head)
  188. {
  189. fb_assert(m_count > 0);
  190. indev* p = m_head;
  191. m_head = m_head->indev_next;
  192. delete p;
  193. --m_count;
  194. return true;
  195. }
  196. return false;
  197. }
  198. // Shortcut for moving the top indev in the chain to the current input file.
  199. void InputDevices::removeIntoIfp()
  200. {
  201. fb_assert(m_head && m_count > 0);
  202. m_ifp.copy_from(m_head);
  203. // When we come back from inout(), we continue inside the get_statement loop.
  204. // If we are inside do_isql() due to Ctrl-C, it doesn't cause any harm.
  205. m_ifp.indev_line = m_ifp.indev_aux;
  206. remove();
  207. }
  208. // Shortcut for testing whether current input and output handles are the same.
  209. // This may happen if we are reading from the command history file that's filled
  210. // automatically with every interactive SQL command (isql commands aren't logged).
  211. bool InputDevices::sameInputAndOutput() const
  212. {
  213. return m_ifp.indev_fpointer && (m_ifp.indev_fpointer == m_ofp.indev_fpointer);
  214. }
  215. // Save SQL command (not isql's own commands) to a history file only if we
  216. // are in interactive mode.
  217. void InputDevices::saveCommand(const char* statement, const char* term)
  218. {
  219. if (m_ifp.indev_fpointer == stdin)
  220. {
  221. FILE* f = m_ofp.indev_fpointer;
  222. if (f)
  223. {
  224. fputs(statement, f);
  225. fputs(term, f);
  226. // Add newline to make the file more readable.
  227. fputc('\n', f);
  228. }
  229. else
  230. {
  231. Command* command = FB_NEW Command(statement, term);
  232. commands.add(command);
  233. }
  234. }
  235. }
  236. // Are we reading from stdin now or from a file?
  237. bool InputDevices::readingStdin() const
  238. {
  239. fb_assert(m_ifp.indev_fpointer);
  240. return m_ifp.indev_fpointer == stdin;
  241. }
  242. // Go to end of file if we are reading from a file (not stdin).
  243. void InputDevices::gotoEof()
  244. {
  245. fb_assert(m_ifp.indev_fpointer);
  246. if (m_ifp.indev_fpointer != stdin)
  247. fseek(m_ifp.indev_fpointer, 0, SEEK_END);
  248. }
  249. InputDevices::Command::Command(const char* statement, const char* term)
  250. : m_statement(getPool())
  251. {
  252. m_statement = statement;
  253. m_statement += term;
  254. }
  255. void InputDevices::Command::toFile(FILE* f)
  256. {
  257. fputs(m_statement.c_str(), f);
  258. // Add newline to make the file more readable.
  259. fputc('\n', f);
  260. }
  261. void InputDevices::commandsToFile(FILE* f)
  262. {
  263. for (unsigned n = 0; n < commands.getCount(); ++n)
  264. {
  265. commands[n]->toFile(f);
  266. delete commands[n];
  267. }
  268. commands.clear();
  269. }