Guest User

Untitled

a guest
Mar 3rd, 2016
32
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 23.02 KB | None | 0 0
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include "storage_mgr.h"
  4. #include "dberror.h"
  5. #include <sys/stat.h>
  6.  
  7. void initStorageManager (void) {
  8.  
  9. }
  10.  
  11. /************************************************************
  12. * manipulating page files *
  13. ************************************************************/
  14.  
  15.  
  16. /*******************************************************************
  17. * NAME : RC createPageFile (char *fileName)
  18. *
  19. * DESCRIPTION : Create a new page file fileName. The initial size is one page.
  20. * This method fills this single page with '\0' bytes.
  21. *
  22. * PARAMETERS:
  23. * char * fileName Name of the file to be created
  24. *
  25. * RETURN :
  26. * Type: RC Returned code:
  27. * Values: RC_OK file created successfully
  28. *
  29. * AUTHOR :
  30. * Adrian Tirados <atirados@hawk.iit.edu>
  31. *
  32. * HISTORY :
  33. * DATE WHO DETAIL
  34. * ----------- --------------------------------------- ---------------------------------
  35. * 2016-02-01 Adrian Tirados <atirados@hawk.iit.edu> Initialization
  36. * 2016-02-06 Adrian Tirados <atirados@hawk.iit.edu> Added comments and header comment
  37. *
  38. *******************************************************************/
  39. RC createPageFile (char *fileName) {
  40. // Open a new file for writing
  41. FILE *pageFile = fopen(fileName, "wb");
  42.  
  43. // Allocate one PAGE_SIZE space in memory and fill with \0 characters
  44. char *buffer = (char *) calloc(PAGE_SIZE, sizeof(char));
  45.  
  46. // Write the buffer of zeroes to into the file
  47. fwrite (buffer, sizeof(char), PAGE_SIZE, pageFile);
  48.  
  49. // Free memory and close
  50. free(buffer);
  51. fclose(pageFile);
  52.  
  53. return RC_OK;
  54. }
  55.  
  56. /*******************************************************************
  57. * NAME : RC openPageFile (char *fileName, SM_FileHandle *fHandle)
  58. *
  59. * DESCRIPTION : Opens an existing page file. If opening the file is successful,
  60. * then the fields of this file handle should be initialized with
  61. * the information about the opened file.
  62. *
  63. * PARAMETERS:
  64. * char * fileName Name of the file to be created
  65. * SM_Filehandle * fHandle An existing file handle
  66. *
  67. * RETURN :
  68. * Type: RC Returned code:
  69. * Values: RC_OK file created successfully
  70. * RC_FILE_NOT_FOUND file does not exist
  71. *
  72. * AUTHOR :
  73. * Adrian Tirados <atirados@hawk.iit.edu>
  74. *
  75. * HISTORY :
  76. * DATE WHO DETAIL
  77. * ----------- --------------------------------------- ---------------------------------
  78. * 2016-02-01 Adrian Tirados <atirados@hawk.iit.edu> Initialization
  79. * 2016-02-06 Adrian Tirados <atirados@hawk.iit.edu> Added comments and header comment
  80. *
  81. *******************************************************************/
  82. RC openPageFile (char *fileName, SM_FileHandle *fHandle) {
  83. // Open an existing file for both reading and writing
  84. FILE *pageFile = fopen(fileName, "rb+");
  85.  
  86. // Check if file exists
  87. if (pageFile == NULL) {
  88. return RC_FILE_NOT_FOUND;
  89. } else {
  90. // Calculate size of the file
  91. struct stat st;
  92. stat(fileName, &st);
  93. int size = st.st_size;
  94.  
  95. // Obtain number of pages
  96. int nPages = (int) size / PAGE_SIZE;
  97.  
  98. // Initialize fields of file handle
  99. fHandle->fileName = fileName;
  100. fHandle->totalNumPages = (int) size%PAGE_SIZE > 0? nPages+1:nPages;
  101. fHandle->curPagePos = 0;
  102. fHandle->mgmtInfo = pageFile;
  103. }
  104. return RC_OK;
  105. }
  106.  
  107. /*******************************************************************
  108. * NAME : closePageFile (SM_FileHandle *fHandle)
  109. *
  110. * DESCRIPTION : Close page file which already opened stored information in fHandle.
  111. * If closing the file is successful, then return RC_OK,
  112. * else return error.
  113. *
  114. * PARAMETERS
  115. * SM_Filehandle * fHandle An existing file handle
  116. *
  117. * RETURN :
  118. * Type: RC Returned code:
  119. * Values: RC_OK file created successfully
  120. * RC_FILE_NOT_FOUND file does not exist
  121. *
  122. * AUTHOR :
  123. * Patipat Duangchalomnin <pduangchalomnin@hawk.iit.edu>
  124. *
  125. * HISTORY :
  126. * DATE WHO DETAIL
  127. * ----------- ----------------------------------------------------- ---------------------------------
  128. * 2016-02-01 Patipat Duangchalomnin <pduangchalomnin@hawk.iit.edu> Initialization
  129. * 2016-02-06 Patipat Duangchalomnin <pduangchalomnin@hawk.iit.edu> Added comments and header comment
  130. *
  131. *******************************************************************/
  132. RC closePageFile (SM_FileHandle *fHandle) {
  133.  
  134. //Check and close file return error if any
  135. int close = fclose(fHandle->mgmtInfo);
  136. if (close == 0)
  137. {
  138. return RC_OK;
  139. }
  140. else
  141. {
  142. return RC_FILE_NOT_FOUND;
  143. }
  144. }
  145.  
  146. /*******************************************************************
  147. * NAME : RC destroyPageFile (char *fileName)
  148. *
  149. * DESCRIPTION : Destroys an existing page file.
  150. *
  151. * PARAMETERS:
  152. * char * fileName Name of the file to be created
  153. *
  154. * RETURN :
  155. * Type: RC Returned code:
  156. * Values: RC_OK file destroyed successfully
  157. * RC_FILE_NOT_FOUND file does not exist
  158. *
  159. * AUTHOR :
  160. * Adrian Tirados <atirados@hawk.iit.edu>
  161. *
  162. * HISTORY :
  163. * DATE WHO DETAIL
  164. * ----------- --------------------------------------- ---------------------------------
  165. * 2016-02-01 Adrian Tirados <atirados@hawk.iit.edu> Initialization
  166. * 2016-02-06 Adrian Tirados <atirados@hawk.iit.edu> Added comments and header comment
  167. *
  168. *******************************************************************/
  169. RC destroyPageFile (char *fileName) {
  170. // Check if file is removed successfully
  171. int removed = remove(fileName);
  172. if (removed == 0) {
  173. return RC_OK;
  174. } else {
  175. return RC_FILE_NOT_FOUND;
  176. }
  177. }
  178.  
  179. /*******************************************************************
  180. * NAME : readBlock (int pageNum, SM_FileHandle *fHandle, SM_PageHandle memPage)
  181. *
  182. * DESCRIPTION : Seek into specific page and perform read page in to memory if it valid
  183. *
  184. * PARAMETERS
  185. * int pageNum The pageNumth block needed to be read
  186. * SM_Filehandle * fHandle An existing file handle
  187. * SM_PageHandle memPage Memory which wanted to load data into
  188. *
  189. * RETURN :
  190. * Type: RC Returned code:
  191. * Values: RC_OK file created successfully
  192. * RC_READ_NON_EXISTING_PAGE page file does not exist
  193. *
  194. * AUTHOR :
  195. * Patipat Duangchalomnin <pduangchalomnin@hawk.iit.edu>
  196. *
  197. * HISTORY :
  198. * DATE WHO DETAIL
  199. * ----------- ----------------------------------------------------- ---------------------------------
  200. * 2016-02-01 Patipat Duangchalomnin <pduangchalomnin@hawk.iit.edu> Initialization
  201. * 2016-02-06 Patipat Duangchalomnin <pduangchalomnin@hawk.iit.edu> Add missing fseek
  202. * 2016-02-06 Patipat Duangchalomnin <pduangchalomnin@hawk.iit.edu> Added comments and header comment
  203. * 2016-02-08 Adrian Tirados <atirados@hawk.iit.edu> Added check for opened file
  204. *
  205. *******************************************************************/
  206. RC readBlock (int pageNum, SM_FileHandle *fHandle, SM_PageHandle memPage) {
  207. int pageFirstByte = pageNum * PAGE_SIZE * sizeof(char);
  208.  
  209. // Check if file is opened
  210. if (fHandle->mgmtInfo == NULL) {
  211. return RC_FILE_NOT_FOUND;
  212. }
  213. //Check if page does exist
  214. if (0 > pageNum || fHandle->totalNumPages < pageNum)
  215. {
  216. return RC_READ_NON_EXISTING_PAGE;
  217. }
  218. else
  219. {
  220. //Read and write block to memPage (Expected number of element read should be return)
  221. if(fseek(fHandle->mgmtInfo, pageFirstByte, SEEK_SET) != 0) {
  222. return RC_READ_NON_EXISTING_PAGE;
  223. }
  224.  
  225. unsigned long size = fread(memPage, sizeof(char), PAGE_SIZE, fHandle->mgmtInfo);
  226.  
  227. if (size == PAGE_SIZE)
  228. {
  229. //Set current page position
  230. fHandle->curPagePos = pageNum;
  231. return RC_OK;
  232. }
  233. else
  234. {
  235. return RC_READ_NON_EXISTING_PAGE;
  236. }
  237. }
  238. }
  239.  
  240. /*******************************************************************
  241. * NAME : int getBlockPos (SM_FileHandle *fHandle)
  242. *
  243. * DESCRIPTION : Return the current page position in a file.
  244. *
  245. * PARAMETERS:
  246. * SM_Filehandle * fHandle An existing file handle
  247. *
  248. * RETURN :
  249. * Type: int Current page position
  250. *
  251. * AUTHOR :
  252. * Adrian Tirados <atirados@hawk.iit.edu>
  253. *
  254. * HISTORY :
  255. * DATE WHO DETAIL
  256. * ----------- --------------------------------------- ---------------------------------
  257. * 2016-02-02 Adrian Tirados <atirados@hawk.iit.edu> Initialization
  258. * 2016-02-06 Adrian Tirados <atirados@hawk.iit.edu> Added comments and header comment
  259. *
  260. *******************************************************************/
  261. int getBlockPos (SM_FileHandle *fHandle) {
  262. // Get the current page from the file handle information
  263. return fHandle->curPagePos;
  264. }
  265.  
  266. /*******************************************************************
  267. * NAME : RC readFirstBlock (SM_FileHandle *fHandle, SM_PageHandle memPage)
  268. *
  269. * DESCRIPTION : Read the first page in a file.
  270. *
  271. * PARAMETERS:
  272. * SM_Filehandle * fHandle An existing file handle
  273. * SM_PageHandle memPage Pointer to an area in memory storing the data of a page
  274. *
  275. * RETURN :
  276. * Type: RC Returned code:
  277. * Values: RC_OK file read successfully
  278. * RC_READ_NON_EXISTING_PAGE page does not exist
  279. *
  280. * AUTHOR :
  281. * Adrian Tirados <atirados@hawk.iit.edu>
  282. *
  283. * HISTORY :
  284. * DATE WHO DETAIL
  285. * ----------- --------------------------------------- ---------------------------------
  286. * 2016-02-02 Adrian Tirados <atirados@hawk.iit.edu> Initialization
  287. * 2016-02-06 Adrian Tirados <atirados@hawk.iit.edu> Added comments and header comment
  288. *
  289. *******************************************************************/
  290. RC readFirstBlock (SM_FileHandle *fHandle, SM_PageHandle memPage) {
  291. // Call readBlock to read page '0' and return its RC
  292. return readBlock(0, fHandle, memPage);
  293. }
  294.  
  295. /*******************************************************************
  296. * NAME : RC readPreviousBlock (SM_FileHandle *fHandle, SM_PageHandle memPage)
  297. *
  298. * DESCRIPTION : Read the previous page to the current position in a file.
  299. *
  300. * PARAMETERS:
  301. * SM_Filehandle * fHandle An existing file handle
  302. * SM_PageHandle memPage Pointer to an area in memory storing the data of a page
  303. *
  304. * RETURN :
  305. * Type: RC Returned code:
  306. * Values: RC_OK file read successfully
  307. * RC_READ_NON_EXISTING_PAGE page does not exist
  308. *
  309. * AUTHOR :
  310. * Adrian Tirados <atirados@hawk.iit.edu>
  311. *
  312. * HISTORY :
  313. * DATE WHO DETAIL
  314. * ----------- --------------------------------------- ---------------------------------
  315. * 2016-02-02 Adrian Tirados <atirados@hawk.iit.edu> Initialization
  316. * 2016-02-06 Adrian Tirados <atirados@hawk.iit.edu> Added comments and header comment
  317. *
  318. *******************************************************************/
  319. RC readPreviousBlock (SM_FileHandle *fHandle, SM_PageHandle memPage) {
  320. // Calculate previous page
  321. int previousBlock = fHandle->curPagePos - 1;
  322.  
  323. // Call readBlock to read previousBlock and return its RC
  324. return readBlock(previousBlock, fHandle, memPage);
  325. }
  326.  
  327. /*******************************************************************
  328. * NAME : RC readCurrentBlock (SM_FileHandle *fHandle, SM_PageHandle memPage)
  329. *
  330. * DESCRIPTION : Read the current page in a file.
  331. *
  332. * PARAMETERS:
  333. * SM_Filehandle * fHandle An existing file handle
  334. * SM_PageHandle memPage Pointer to an area in memory storing the data of a page
  335. *
  336. * RETURN :
  337. * Type: RC Returned code:
  338. * Values: RC_OK file read successfully
  339. * RC_READ_NON_EXISTING_PAGE page does not exist
  340. *
  341. * AUTHOR :
  342. * Adrian Tirados <atirados@hawk.iit.edu>
  343. *
  344. * HISTORY :
  345. * DATE WHO DETAIL
  346. * ----------- --------------------------------------- ---------------------------------
  347. * 2016-02-02 Adrian Tirados <atirados@hawk.iit.edu> Initialization
  348. * 2016-02-06 Adrian Tirados <atirados@hawk.iit.edu> Added comments and header comment
  349. *
  350. *******************************************************************/
  351. RC readCurrentBlock (SM_FileHandle *fHandle, SM_PageHandle memPage) {
  352. // Call readBlock to read current page and return its RC
  353. return readBlock(fHandle->curPagePos, fHandle, memPage);
  354. }
  355.  
  356. /*******************************************************************
  357. * NAME : RC readNextBlock (SM_FileHandle *fHandle, SM_PageHandle memPage)
  358. *
  359. * DESCRIPTION : Read the next page to the current position page in a file.
  360. *
  361. * PARAMETERS:
  362. * SM_Filehandle * fHandle An existing file handle
  363. * SM_PageHandle memPage Pointer to an area in memory storing the data of a page
  364. *
  365. * RETURN :
  366. * Type: RC Returned code:
  367. * Values: RC_OK file read successfully
  368. * RC_READ_NON_EXISTING_PAGE page does not exist
  369. *
  370. * AUTHOR :
  371. * Adrian Tirados <atirados@hawk.iit.edu>
  372. *
  373. * HISTORY :
  374. * DATE WHO DETAIL
  375. * ----------- --------------------------------------- ---------------------------------
  376. * 2016-02-02 Adrian Tirados <atirados@hawk.iit.edu> Initialization
  377. * 2016-02-06 Adrian Tirados <atirados@hawk.iit.edu> Added comments and header comment
  378. *
  379. *******************************************************************/
  380. RC readNextBlock (SM_FileHandle *fHandle, SM_PageHandle memPage) {
  381. // Calculate next page
  382. int nextBlock = fHandle->curPagePos + 1;
  383.  
  384. // Call readBlock to read nextBlock and return its RC
  385. return readBlock(nextBlock, fHandle, memPage);
  386. }
  387.  
  388. /*******************************************************************
  389. * NAME : RC readLastBlock (SM_FileHandle *fHandle, SM_PageHandle memPage)
  390. *
  391. * DESCRIPTION : Read the last page in a file.
  392. *
  393. * PARAMETERS:
  394. * SM_Filehandle * fHandle An existing file handle
  395. * SM_PageHandle memPage Pointer to an area in memory storing the data of a page
  396. *
  397. * RETURN :
  398. * Type: RC Returned code:
  399. * Values: RC_OK file read successfully
  400. * RC_READ_NON_EXISTING_PAGE page does not exist
  401. *
  402. * AUTHOR :
  403. * Adrian Tirados <atirados@hawk.iit.edu>
  404. *
  405. * HISTORY :
  406. * DATE WHO DETAIL
  407. * ----------- --------------------------------------- ---------------------------------
  408. * 2016-02-02 Adrian Tirados <atirados@hawk.iit.edu> Initialization
  409. * 2016-02-06 Adrian Tirados <atirados@hawk.iit.edu> Added comments and header comment
  410. *
  411. *******************************************************************/
  412. RC readLastBlock (SM_FileHandle *fHandle, SM_PageHandle memPage) {
  413. // Call readBlock to read last page (totalNumPages) and return its RC
  414. int lastBlock = fHandle->totalNumPages-1;
  415. return readBlock(lastBlock, fHandle, memPage);
  416. }
  417.  
  418. /************************************************************
  419. * writing blocks to a page file *
  420. ************************************************************/
  421.  
  422. /*******************************************************************
  423. * NAME : RC writeBlock (int pageNum, SM_FileHandle *fHandle, SM_PageHandle memPage)
  424. *
  425. * DESCRIPTION : Write a page to disk using an absolute position.
  426. *
  427. * PARAMETERS:
  428. * int pageNum The pageNumth block that the method writes
  429. * SM_Filehandle * fHandle An existing file handle
  430. * SM_PageHandle memPage Pointer to an area in memory storing the data of a page
  431. *
  432. * RETURN :
  433. * Type: RC Returned code:
  434. * Values: RC_OK file write successfully
  435. * RC_WRITE_FAILED failed to write
  436. *
  437. * AUTHOR :
  438. * Yung Chi Shih <yshih2@hawk.iit.edu>
  439. *
  440. * HISTORY :
  441. * DATE WHO DETAIL
  442. * ----------- --------------------------------------- ---------------------------------
  443. * 2016-02-08 Yung Chi Shih <yshih2@hawk.iit.edu> Initialization
  444. * 2016-02-08 Yung Chi Shih <yshih2@hawk.iit.edu> Added comments and header comment
  445. * 2016-02-08 Adrian Tirados <atirados@hawk.iit.edu> Fixed return code after running test
  446. *
  447. *******************************************************************/
  448. RC writeBlock (int pageNum, SM_FileHandle *fHandle, SM_PageHandle memPage) {
  449.  
  450. //check if the pageNumth block that the method writes is not less than 0
  451. if (0 > pageNum || fHandle->totalNumPages < pageNum)
  452. return RC_WRITE_FAILED;
  453.  
  454. //sets the file pointer point to a structure that contains information about the file
  455. FILE *pageFile = fHandle->mgmtInfo;
  456.  
  457. //declares the number of bytes to offset
  458. long int offset = pageNum * PAGE_SIZE;
  459.  
  460. //sets the file position of the stream to beginning of file
  461. fseek(pageFile, offset, SEEK_SET);
  462.  
  463. //check if the write is successful
  464. if (fwrite(memPage, sizeof(char), PAGE_SIZE, pageFile) != PAGE_SIZE) {
  465. return RC_WRITE_FAILED;
  466. } else {
  467. fHandle->curPagePos = pageNum;
  468. return RC_OK;
  469. }
  470. }
  471.  
  472. /*******************************************************************
  473. * NAME : RC writeCurrentBlock (SM_FileHandle *fHandle, SM_PageHandle memPage)
  474. *
  475. * DESCRIPTION : Write the current page in a file.
  476. *
  477. * PARAMETERS:
  478. * SM_Filehandle * fHandle An existing file handle
  479. * SM_PageHandle memPage Pointer to an area in memory storing the data of a page
  480. *
  481. * RETURN :
  482. * Type: RC Returned code:
  483. * Values: RC_OK file write successfully
  484. * RC_WRITE_FAILED failed to write
  485. *
  486. * AUTHOR :
  487. * Yung Chi Shih <yshih2@hawk.iit.edu>
  488. *
  489. * HISTORY :
  490. * DATE WHO DETAIL
  491. * ----------- --------------------------------------- ---------------------------------
  492. * 2016-02-07 Yung Chi Shih <yshih2@hawk.iit.edu> Initialization
  493. * 2016-02-07 Yung Chi Shih <yshih2@hawk.iit.edu> Added comments and header comment
  494. *
  495. *******************************************************************/
  496. RC writeCurrentBlock (SM_FileHandle *fHandle, SM_PageHandle memPage) {
  497. // Call writeBlock to write current page and return its RC
  498. return writeBlock(fHandle->curPagePos, fHandle, memPage);
  499. }
  500.  
  501. /*******************************************************************
  502. * NAME : RC appendEmptyBlock (SM_FileHandle *fHandle, SM_PageHandle memPage)
  503. *
  504. * DESCRIPTION : Append a new page at the end of the file
  505. *
  506. * PARAMETERS:
  507. * SM_Filehandle * fHandle An existing file handle
  508. *
  509. * RETURN :
  510. * Type: RC Returned code:
  511. * Values: RC_WRITE_FAILED failed to write
  512. *
  513. *
  514. * AUTHOR :
  515. * Yung Chi Shih <yshih2@hawk.iit.edu>
  516. *
  517. * HISTORY :
  518. * DATE WHO DETAIL
  519. * ----------- --------------------------------------- ---------------------------------
  520. * 2016-02-02 Yung Chi Shih <yshih2@hawk.iit.edu> Initialization
  521. * 2016-02-07 Yung Chi Shih <yshih2@hawk.iit.edu> Added comments and header comment
  522. * 2016-02-08 Yung Chi Shih <yshih2@hawk.iit.edu> Added file pointer declaration
  523. *
  524. *******************************************************************/
  525. RC appendEmptyBlock (SM_FileHandle *fHandle) {
  526.  
  527. //sets the file pointer point to a structure that contains information about the file
  528. FILE *pageFile = fHandle->mgmtInfo;
  529.  
  530. char *buffer = (char *) calloc(PAGE_SIZE, sizeof(char));
  531.  
  532. int totalNumPages = fHandle->totalNumPages;
  533.  
  534. struct stat st;
  535. stat(fHandle->fileName, &st);
  536. int size = st.st_size;
  537.  
  538. fseek(pageFile, PAGE_SIZE * totalNumPages, SEEK_SET);
  539.  
  540. //writes data
  541. fwrite (buffer, sizeof(char), PAGE_SIZE, pageFile);
  542.  
  543. stat(fHandle->fileName, &st);
  544. int newSize = st.st_size;
  545.  
  546. //check if a new block of size PAGE_SIZE is appeneded
  547. if (size + PAGE_SIZE != newSize) {
  548. free(buffer);
  549. return RC_WRITE_FAILED;
  550. }
  551.  
  552. free(buffer);
  553.  
  554. //increments the total number of pages
  555. fHandle->totalNumPages++;
  556. fHandle->curPagePos++;
  557. return RC_OK;
  558. }
  559.  
  560. /*******************************************************************
  561. * NAME : RC ensureCapacity (int numberOfPages, SM_FileHandle *fHandle)
  562. *
  563. * DESCRIPTION : Increase page size of file to equal numberOfPages if it is less than numberOfPages.
  564. *
  565. * PARAMETERS:
  566. * SM_Filehandle * fHandle An existing file handle
  567. *
  568. * RETURN :
  569. * Type: RC Returned code:
  570. * Values: RC_OK file page size is equal to or more than numberOfPages
  571. *
  572. *
  573. * AUTHOR :
  574. * Arpita Rathore <arathore@hawk.iit.edu>
  575. *
  576. * HISTORY :
  577. * DATE WHO DETAIL
  578. * ----------- --------------------------------------- ---------------------------------
  579. * 2016-02-04 Arpita Rathore <arathore@hawk.iit.edu> Initialization
  580. * 2016-02-07 Arpita Rathore <arathore@hawk.iit.edu> Added comments and header comment
  581. *
  582. *******************************************************************/
  583. RC ensureCapacity (int numberOfPages, SM_FileHandle *fHandle) {
  584.  
  585. //check if the file page size is less than numberOfPages
  586. if (fHandle->totalNumPages < numberOfPages) {
  587. //call appendEmptyBlock to append an empty page, check again if size is still less than numberOfPages, continue appending a page until size is equal to numberOfPages
  588. while (fHandle->totalNumPages < numberOfPages){
  589. appendEmptyBlock(fHandle);
  590. }
  591.  
  592. //file page size is equal to or more than numberOfPages, appending is not needed
  593. }
  594. return RC_OK;
  595. }
Add Comment
Please, Sign In to add comment