[
  {
    "path": ".github/workflows/python-publish.yml",
    "content": "# This workflows will upload a Python Package using Twine when a release is created\n# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries\n\nname: Upload Python Package\n\non:\n  release:\n    types: [created]\n\njobs:\n  deploy:\n\n    runs-on: ubuntu-latest\n\n    steps:\n    - uses: actions/checkout@v2\n    - name: Set up Python\n      uses: actions/setup-python@v2\n      with:\n        python-version: '3.7.x'\n    - name: Install dependencies\n      run: |\n        python -m pip install --upgrade pip\n        pip install setuptools wheel twine\n    - name: Build and publish\n      env:\n        TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}\n        TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}\n      run: |\n        python setup.py sdist bdist_wheel\n        twine upload dist/*\n"
  },
  {
    "path": ".gitignore",
    "content": ".python-version\n__pychache__\n.idea\n"
  },
  {
    "path": "README.md",
    "content": "# Shirah\n[![Downloads](https://pepy.tech/badge/shirah-reader)](https://pepy.tech/project/shirah-reader)\n\nA curses based terminal RSVP speed reader.\n\n![Alt text](/assets/shirah.gif \"Optional Title\")\n\nNote: Poor colour representation in gif, looks way better irl.\n\n### What is an RSVP reader?\nRSVP stands for [Rapid Serial Visual presentiation](https://en.wikipedia.org/wiki/Rapid_serial_visual_presentation).\nIts a controversial method to enable speedreading. I have been using this on my devices, and it has given me great results. I wanted to have an option to do this in the terminal too.\n\n\n![Alt text](/assets/speedread.gif \"Optional Title\")\n\nIgnore the haters, and try it for yourself.\nAlso, I don't like that the best options on a computer are paid softwares.\n\n### Usage\nTo install, use either \n\n```pip install shirah-reader```\n\nor\n\n```pip3 install shirah-reader```\n\n\nTo read a book, and set a speed, enter\n\n```shirah /path/to/book```\n\nHere are the shortcuts:\n```\nUsage:\n    shirah             read last epub\n    shirah EPUBFILE    read EPUBFILE\n    shirah STRINGS     read matched STRINGS from history\n    shirah NUMBER      read file from history\n                       with associated NUMBER\n                       \nOptions:\n    -r              print reading history\n    -d              dump epub\n    -h, --help      print short, long help\n```\n\n### How to speed read\nOnce in a chapter, press `r` to get into the rsvp mode. Press ^C to change the speed. Press ^C and then enter q to get out of the rsvp mode.\n\n### Navigation\nPress `tab` to see table of contents, and navigate.\n\n\n### Keybindings\n```\nKey Binding:\n    Help            : ?\n    Quit            : q\n    Scroll down     : DOWN      j\n    Scroll up       : UP        k\n    Half screen up  : C-u\n    Half screen dn  \" C-d\n    Page down       : PGDN      RIGHT   SPC\n    Page up         : PGUP      LEFT\n    Next chapter    : n\n    Prev chapter    : p\n    Beginning of ch : HOME      g\n    End of ch       : END       G\n    Open image      : o\n    Search          : /\n    Next Occurence  : n\n    Prev Occurence  : N\n    Toggle width    : =\n    Set width       : [count]=\n    Shrink          : -\n    Enlarge         : +\n    ToC             : TAB       t\n    Metadata        : m\n    Mark pos to n   : b[n]\n    Jump to pos n   : `[n]\n    Switch colorsch : [default=0, dark=1, light=2]c\n```\n\n### Main Goals\n- [x] Read txt\n- [x] Read epubs\n- [x] Pause\n- [x] Save progress\n- [x] Progress bar.\n- [x] Chapter-wise Navigation\n\n## Name\nNamed after the marathi name for the sweet dish [Kesari Bhat](https://en.wikipedia.org/wiki/Kesari_bat)\n\n## Credits\n- ebooklib by Aleksandar Erkalović (https://github.com/aerkalov/ebooklib)\n- epy by Benawi Adha (https://github.com/wustho/epy) (This guy did ALL the heavy lifting.)\n\n### License\n```\n This program is free software; you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation; either version 2 of the License, or\n (at your option) any later version.\n\n This program is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with this program; if not, write to the Free Software\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA\n ```\n"
  },
  {
    "path": "assets/alice.txt",
    "content": "Alice's Adventures in Wonderland\n\n                ALICE'S ADVENTURES IN WONDERLAND\n\n                          Lewis Carroll\n\n               THE MILLENNIUM FULCRUM EDITION 3.0\n\n\n\n\n                            CHAPTER I\n\n                      Down the Rabbit-Hole\n\n\n  Alice was beginning to get very tired of sitting by her sister\non the bank, and of having nothing to do:  once or twice she had\npeeped into the book her sister was reading, but it had no\npictures or conversations in it, `and what is the use of a book,'\nthought Alice `without pictures or conversation?'\n\n  So she was considering in her own mind (as well as she could,\nfor the hot day made her feel very sleepy and stupid), whether\nthe pleasure of making a daisy-chain would be worth the trouble\nof getting up and picking the daisies, when suddenly a White\nRabbit with pink eyes ran close by her.\n\n  There was nothing so VERY remarkable in that; nor did Alice\nthink it so VERY much out of the way to hear the Rabbit say to\nitself, `Oh dear!  Oh dear!  I shall be late!'  (when she thought\nit over afterwards, it occurred to her that she ought to have\nwondered at this, but at the time it all seemed quite natural);\nbut when the Rabbit actually TOOK A WATCH OUT OF ITS WAISTCOAT-\nPOCKET, and looked at it, and then hurried on, Alice started to\nher feet, for it flashed across her mind that she had never\nbefore seen a rabbit with either a waistcoat-pocket, or a watch to\ntake out of it, and burning with curiosity, she ran across the\nfield after it, and fortunately was just in time to see it pop\ndown a large rabbit-hole under the hedge.\n\n  In another moment down went Alice after it, never once\nconsidering how in the world she was to get out again.\n\n  The rabbit-hole went straight on like a tunnel for some way,\nand then dipped suddenly down, so suddenly that Alice had not a\nmoment to think about stopping herself before she found herself\nfalling down a very deep well.\n\n  Either the well was very deep, or she fell very slowly, for she\nhad plenty of time as she went down to look about her and to\nwonder what was going to happen next.  First, she tried to look\ndown and make out what she was coming to, but it was too dark to\nsee anything; then she looked at the sides of the well, and\nnoticed that they were filled with cupboards and book-shelves;\nhere and there she saw maps and pictures hung upon pegs.  She\ntook down a jar from one of the shelves as she passed; it was\nlabelled `ORANGE MARMALADE', but to her great disappointment it\nwas empty:  she did not like to drop the jar for fear of killing\nsomebody, so managed to put it into one of the cupboards as she\nfell past it.\n\n  `Well!' thought Alice to herself, `after such a fall as this, I\nshall think nothing of tumbling down stairs!  How brave they'll\nall think me at home!  Why, I wouldn't say anything about it,\neven if I fell off the top of the house!' (Which was very likely\ntrue.)\n\n  Down, down, down.  Would the fall NEVER come to an end!  `I\nwonder how many miles I've fallen by this time?' she said aloud.\n`I must be getting somewhere near the centre of the earth.  Let\nme see:  that would be four thousand miles down, I think--' (for,\nyou see, Alice had learnt several things of this sort in her\nlessons in the schoolroom, and though this was not a VERY good\nopportunity for showing off her knowledge, as there was no one to\nlisten to her, still it was good practice to say it over) `--yes,\nthat's about the right distance--but then I wonder what Latitude\nor Longitude I've got to?'  (Alice had no idea what Latitude was,\nor Longitude either, but thought they were nice grand words to\nsay.)\n\n  Presently she began again.  `I wonder if I shall fall right\nTHROUGH the earth!  How funny it'll seem to come out among the\npeople that walk with their heads downward!  The Antipathies, I\nthink--' (she was rather glad there WAS no one listening, this\ntime, as it didn't sound at all the right word) `--but I shall\nhave to ask them what the name of the country is, you know.\nPlease, Ma'am, is this New Zealand or Australia?' (and she tried\nto curtsey as she spoke--fancy CURTSEYING as you're falling\nthrough the air!  Do you think you could manage it?)  `And what\nan ignorant little girl she'll think me for asking!  No, it'll\nnever do to ask:  perhaps I shall see it written up somewhere.'\n\n  Down, down, down.  There was nothing else to do, so Alice soon\nbegan talking again.  `Dinah'll miss me very much to-night, I\nshould think!'  (Dinah was the cat.)  `I hope they'll remember\nher saucer of milk at tea-time.  Dinah my dear!  I wish you were\ndown here with me!  There are no mice in the air, I'm afraid, but\nyou might catch a bat, and that's very like a mouse, you know.\nBut do cats eat bats, I wonder?'  And here Alice began to get\nrather sleepy, and went on saying to herself, in a dreamy sort of\nway, `Do cats eat bats?  Do cats eat bats?' and sometimes, `Do\nbats eat cats?' for, you see, as she couldn't answer either\nquestion, it didn't much matter which way she put it.  She felt\nthat she was dozing off, and had just begun to dream that she\nwas walking hand in hand with Dinah, and saying to her very\nearnestly, `Now, Dinah, tell me the truth:  did you ever eat a\nbat?' when suddenly, thump! thump! down she came upon a heap of\nsticks and dry leaves, and the fall was over.\n\n  Alice was not a bit hurt, and she jumped up on to her feet in a\nmoment:  she looked up, but it was all dark overhead; before her\nwas another long passage, and the White Rabbit was still in\nsight, hurrying down it.  There was not a moment to be lost:\naway went Alice like the wind, and was just in time to hear it\nsay, as it turned a corner, `Oh my ears and whiskers, how late\nit's getting!'  She was close behind it when she turned the\ncorner, but the Rabbit was no longer to be seen:  she found\nherself in a long, low hall, which was lit up by a row of lamps\nhanging from the roof.\n\n  There were doors all round the hall, but they were all locked;\nand when Alice had been all the way down one side and up the\nother, trying every door, she walked sadly down the middle,\nwondering how she was ever to get out again.\n\n  Suddenly she came upon a little three-legged table, all made of\nsolid glass; there was nothing on it except a tiny golden key,\nand Alice's first thought was that it might belong to one of the\ndoors of the hall; but, alas! either the locks were too large, or\nthe key was too small, but at any rate it would not open any of\nthem.  However, on the second time round, she came upon a low\ncurtain she had not noticed before, and behind it was a little\ndoor about fifteen inches high:  she tried the little golden key\nin the lock, and to her great delight it fitted!\n\n  Alice opened the door and found that it led into a small\npassage, not much larger than a rat-hole:  she knelt down and\nlooked along the passage into the loveliest garden you ever saw.\nHow she longed to get out of that dark hall, and wander about\namong those beds of bright flowers and those cool fountains, but\nshe could not even get her head through the doorway; `and even if\nmy head would go through,' thought poor Alice, `it would be of\nvery little use without my shoulders.  Oh, how I wish\nI could shut up like a telescope!  I think I could, if I only\nknow how to begin.'  For, you see, so many out-of-the-way things\nhad happened lately, that Alice had begun to think that very few\nthings indeed were really impossible.\n\n  There seemed to be no use in waiting by the little door, so she\nwent back to the table, half hoping she might find another key on\nit, or at any rate a book of rules for shutting people up like\ntelescopes:  this time she found a little bottle on it, (`which\ncertainly was not here before,' said Alice,) and round the neck\nof the bottle was a paper label, with the words `DRINK ME'\nbeautifully printed on it in large letters.\n\n  It was all very well to say `Drink me,' but the wise little\nAlice was not going to do THAT in a hurry.  `No, I'll look\nfirst,' she said, `and see whether it's marked \"poison\" or not';\nfor she had read several nice little histories about children who\nhad got burnt, and eaten up by wild beasts and other unpleasant\nthings, all because they WOULD not remember the simple rules\ntheir friends had taught them:  such as, that a red-hot poker\nwill burn you if you hold it too long; and that if you cut your\nfinger VERY deeply with a knife, it usually bleeds; and she had\nnever forgotten that, if you drink much from a bottle marked\n`poison,' it is almost certain to disagree with you, sooner or\nlater.\n\n  However, this bottle was NOT marked `poison,' so Alice ventured\nto taste it, and finding it very nice, (it had, in fact, a sort\nof mixed flavour of cherry-tart, custard, pine-apple, roast\nturkey, toffee, and hot buttered toast,) she very soon finished\nit off.\n\n     *       *       *       *       *       *       *\n\n         *       *       *       *       *       *\n\n     *       *       *       *       *       *       *\n\n  `What a curious feeling!' said Alice; `I must be shutting up\nlike a telescope.'\n\n  And so it was indeed:  she was now only ten inches high, and\nher face brightened up at the thought that she was now the right\nsize for going through the little door into that lovely garden.\nFirst, however, she waited for a few minutes to see if she was\ngoing to shrink any further:  she felt a little nervous about\nthis; `for it might end, you know,' said Alice to herself, `in my\ngoing out altogether, like a candle.  I wonder what I should be\nlike then?'  And she tried to fancy what the flame of a candle is\nlike after the candle is blown out, for she could not remember\never having seen such a thing.\n\n  After a while, finding that nothing more happened, she decided\non going into the garden at once; but, alas for poor Alice!\nwhen she got to the door, she found she had forgotten the\nlittle golden key, and when she went back to the table for it,\nshe found she could not possibly reach it:  she could see it\nquite plainly through the glass, and she tried her best to climb\nup one of the legs of the table, but it was too slippery;\nand when she had tired herself out with trying,\nthe poor little thing sat down and cried.\n\n  `Come, there's no use in crying like that!' said Alice to\nherself, rather sharply; `I advise you to leave off this minute!'\nShe generally gave herself very good advice, (though she very\nseldom followed it), and sometimes she scolded herself so\nseverely as to bring tears into her eyes; and once she remembered\ntrying to box her own ears for having cheated herself in a game\nof croquet she was playing against herself, for this curious\nchild was very fond of pretending to be two people.  `But it's no\nuse now,' thought poor Alice, `to pretend to be two people!  Why,\nthere's hardly enough of me left to make ONE respectable\nperson!'\n\n  Soon her eye fell on a little glass box that was lying under\nthe table:  she opened it, and found in it a very small cake, on\nwhich the words `EAT ME' were beautifully marked in currants.\n`Well, I'll eat it,' said Alice, `and if it makes me grow larger,\nI can reach the key; and if it makes me grow smaller, I can creep\nunder the door; so either way I'll get into the garden, and I\ndon't care which happens!'\n\n  She ate a little bit, and said anxiously to herself, `Which\nway?  Which way?', holding her hand on the top of her head to\nfeel which way it was growing, and she was quite surprised to\nfind that she remained the same size:  to be sure, this generally\nhappens when one eats cake, but Alice had got so much into the\nway of expecting nothing but out-of-the-way things to happen,\nthat it seemed quite dull and stupid for life to go on in the\ncommon way.\n\n  So she set to work, and very soon finished off the cake.\n\n     *       *       *       *       *       *       *\n\n         *       *       *       *       *       *\n\n     *       *       *       *       *       *       *\n\n\n\n\n                           CHAPTER II\n\n                        The Pool of Tears\n\n\n  `Curiouser and curiouser!' cried Alice (she was so much\nsurprised, that for the moment she quite forgot how to speak good\nEnglish); `now I'm opening out like the largest telescope that\never was!  Good-bye, feet!' (for when she looked down at her\nfeet, they seemed to be almost out of sight, they were getting so\nfar off).  `Oh, my poor little feet, I wonder who will put on\nyour shoes and stockings for you now, dears?  I'm sure _I_ shan't\nbe able!  I shall be a great deal too far off to trouble myself\nabout you:  you must manage the best way you can; --but I must be\nkind to them,' thought Alice, `or perhaps they won't walk the\nway I want to go!  Let me see:  I'll give them a new pair of\nboots every Christmas.'\n\n  And she went on planning to herself how she would manage it.\n`They must go by the carrier,' she thought; `and how funny it'll\nseem, sending presents to one's own feet!  And how odd the\ndirections will look!\n\n            ALICE'S RIGHT FOOT, ESQ.\n                HEARTHRUG,\n                    NEAR THE FENDER,\n                        (WITH ALICE'S LOVE).\n\nOh dear, what nonsense I'm talking!'\n\n  Just then her head struck against the roof of the hall:  in\nfact she was now more than nine feet high, and she at once took\nup the little golden key and hurried off to the garden door.\n\n  Poor Alice!  It was as much as she could do, lying down on one\nside, to look through into the garden with one eye; but to get\nthrough was more hopeless than ever:  she sat down and began to\ncry again.\n\n  `You ought to be ashamed of yourself,' said Alice, `a great\ngirl like you,' (she might well say this), `to go on crying in\nthis way!  Stop this moment, I tell you!'  But she went on all\nthe same, shedding gallons of tears, until there was a large pool\nall round her, about four inches deep and reaching half down the\nhall.\n\n  After a time she heard a little pattering of feet in the\ndistance, and she hastily dried her eyes to see what was coming.\nIt was the White Rabbit returning, splendidly dressed, with a\npair of white kid gloves in one hand and a large fan in the\nother:  he came trotting along in a great hurry, muttering to\nhimself as he came, `Oh! the Duchess, the Duchess! Oh! won't she\nbe savage if I've kept her waiting!'  Alice felt so desperate\nthat she was ready to ask help of any one; so, when the Rabbit\ncame near her, she began, in a low, timid voice, `If you please,\nsir--'  The Rabbit started violently, dropped the white kid\ngloves and the fan, and skurried away into the darkness as hard\nas he could go.\n\n  Alice took up the fan and gloves, and, as the hall was very\nhot, she kept fanning herself all the time she went on talking:\n`Dear, dear!  How queer everything is to-day!  And yesterday\nthings went on just as usual.  I wonder if I've been changed in\nthe night?  Let me think:  was I the same when I got up this\nmorning?  I almost think I can remember feeling a little\ndifferent.  But if I'm not the same, the next question is, Who in\nthe world am I?  Ah, THAT'S the great puzzle!'  And she began\nthinking over all the children she knew that were of the same age\nas herself, to see if she could have been changed for any of\nthem.\n\n  `I'm sure I'm not Ada,' she said, `for her hair goes in such\nlong ringlets, and mine doesn't go in ringlets at all; and I'm\nsure I can't be Mabel, for I know all sorts of things, and she,\noh! she knows such a very little!  Besides, SHE'S she, and I'm I,\nand--oh dear, how puzzling it all is!  I'll try if I know all the\nthings I used to know.  Let me see:  four times five is twelve,\nand four times six is thirteen, and four times seven is--oh dear!\nI shall never get to twenty at that rate!  However, the\nMultiplication Table doesn't signify:  let's try Geography.\nLondon is the capital of Paris, and Paris is the capital of Rome,\nand Rome--no, THAT'S all wrong, I'm certain!  I must have been\nchanged for Mabel!  I'll try and say \"How doth the little--\"'\nand she crossed her hands on her lap as if she were saying lessons,\nand began to repeat it, but her voice sounded hoarse and\nstrange, and the words did not come the same as they used to do:--\n\n            `How doth the little crocodile\n              Improve his shining tail,\n            And pour the waters of the Nile\n              On every golden scale!\n\n            `How cheerfully he seems to grin,\n              How neatly spread his claws,\n            And welcome little fishes in\n              With gently smiling jaws!'\n\n  `I'm sure those are not the right words,' said poor Alice, and\nher eyes filled with tears again as she went on, `I must be Mabel\nafter all, and I shall have to go and live in that poky little\nhouse, and have next to no toys to play with, and oh! ever so\nmany lessons to learn!  No, I've made up my mind about it; if I'm\nMabel, I'll stay down here!  It'll be no use their putting their\nheads down and saying \"Come up again, dear!\"  I shall only look\nup and say \"Who am I then?  Tell me that first, and then, if I\nlike being that person, I'll come up:  if not, I'll stay down\nhere till I'm somebody else\"--but, oh dear!' cried Alice, with a\nsudden burst of tears, `I do wish they WOULD put their heads\ndown!  I am so VERY tired of being all alone here!'\n\n  As she said this she looked down at her hands, and was\nsurprised to see that she had put on one of the Rabbit's little\nwhite kid gloves while she was talking.  `How CAN I have done\nthat?' she thought.  `I must be growing small again.'  She got up\nand went to the table to measure herself by it, and found that,\nas nearly as she could guess, she was now about two feet high,\nand was going on shrinking rapidly:  she soon found out that the\ncause of this was the fan she was holding, and she dropped it\nhastily, just in time to avoid shrinking away altogether.\n\n`That WAS a narrow escape!' said Alice, a good deal frightened at\nthe sudden change, but very glad to find herself still in\nexistence; `and now for the garden!' and she ran with all speed\nback to the little door:  but, alas! the little door was shut\nagain, and the little golden key was lying on the glass table as\nbefore, `and things are worse than ever,' thought the poor child,\n`for I never was so small as this before, never!  And I declare\nit's too bad, that it is!'\n\n  As she said these words her foot slipped, and in another\nmoment, splash! she was up to her chin in salt water.  Her first\nidea was that she had somehow fallen into the sea, `and in that\ncase I can go back by railway,' she said to herself.  (Alice had\nbeen to the seaside once in her life, and had come to the general\nconclusion, that wherever you go to on the English coast you find\na number of bathing machines in the sea, some children digging in\nthe sand with wooden spades, then a row of lodging houses, and\nbehind them a railway station.)  However, she soon made out that\nshe was in the pool of tears which she had wept when she was nine\nfeet high.\n\n  `I wish I hadn't cried so much!' said Alice, as she swam about,\ntrying to find her way out.  `I shall be punished for it now, I\nsuppose, by being drowned in my own tears!  That WILL be a queer\nthing, to be sure!  However, everything is queer to-day.'\n\n  Just then she heard something splashing about in the pool a\nlittle way off, and she swam nearer to make out what it was:  at\nfirst she thought it must be a walrus or hippopotamus, but then\nshe remembered how small she was now, and she soon made out that\nit was only a mouse that had slipped in like herself.\n\n  `Would it be of any use, now,' thought Alice, `to speak to this\nmouse?  Everything is so out-of-the-way down here, that I should\nthink very likely it can talk:  at any rate, there's no harm in\ntrying.'  So she began:  `O Mouse, do you know the way out of\nthis pool?  I am very tired of swimming about here, O Mouse!'\n(Alice thought this must be the right way of speaking to a mouse:\nshe had never done such a thing before, but she remembered having\nseen in her brother's Latin Grammar, `A mouse--of a mouse--to a\nmouse--a mouse--O mouse!')  The Mouse looked at her rather\ninquisitively, and seemed to her to wink with one of its little\neyes, but it said nothing.\n\n  `Perhaps it doesn't understand English,' thought Alice; `I\ndaresay it's a French mouse, come over with William the\nConqueror.'  (For, with all her knowledge of history, Alice had\nno very clear notion how long ago anything had happened.)  So she\nbegan again:  `Ou est ma chatte?' which was the first sentence in\nher French lesson-book.  The Mouse gave a sudden leap out of the\nwater, and seemed to quiver all over with fright.  `Oh, I beg\nyour pardon!' cried Alice hastily, afraid that she had hurt the\npoor animal's feelings.  `I quite forgot you didn't like cats.'\n\n  `Not like cats!' cried the Mouse, in a shrill, passionate\nvoice.  `Would YOU like cats if you were me?'\n\n  `Well, perhaps not,' said Alice in a soothing tone:  `don't be\nangry about it.  And yet I wish I could show you our cat Dinah:\nI think you'd take a fancy to cats if you could only see her.\nShe is such a dear quiet thing,' Alice went on, half to herself,\nas she swam lazily about in the pool, `and she sits purring so\nnicely by the fire, licking her paws and washing her face--and\nshe is such a nice soft thing to nurse--and she's such a capital\none for catching mice--oh, I beg your pardon!' cried Alice again,\nfor this time the Mouse was bristling all over, and she felt\ncertain it must be really offended.  `We won't talk about her any\nmore if you'd rather not.'\n\n  `We indeed!' cried the Mouse, who was trembling down to the end\nof his tail.  `As if I would talk on such a subject!  Our family\nalways HATED cats:  nasty, low, vulgar things!  Don't let me hear\nthe name again!'\n\n  `I won't indeed!' said Alice, in a great hurry to change the\nsubject of conversation.  `Are you--are you fond--of--of dogs?'\nThe Mouse did not answer, so Alice went on eagerly:  `There is\nsuch a nice little dog near our house I should like to show you!\nA little bright-eyed terrier, you know, with oh, such long curly\nbrown hair!  And it'll fetch things when you throw them, and\nit'll sit up and beg for its dinner, and all sorts of things--I\ncan't remember half of them--and it belongs to a farmer, you\nknow, and he says it's so useful, it's worth a hundred pounds!\nHe says it kills all the rats and--oh dear!' cried Alice in a\nsorrowful tone, `I'm afraid I've offended it again!'  For the\nMouse was swimming away from her as hard as it could go, and\nmaking quite a commotion in the pool as it went.\n\n  So she called softly after it, `Mouse dear!  Do come back\nagain, and we won't talk about cats or dogs either, if you don't\nlike them!'  When the Mouse heard this, it turned round and swam\nslowly back to her:  its face was quite pale (with passion, Alice\nthought), and it said in a low trembling voice, `Let us get to\nthe shore, and then I'll tell you my history, and you'll\nunderstand why it is I hate cats and dogs.'\n\n  It was high time to go, for the pool was getting quite crowded\nwith the birds and animals that had fallen into it:  there were a\nDuck and a Dodo, a Lory and an Eaglet, and several other curious\ncreatures.  Alice led the way, and the whole party swam to the\nshore.\n\n\n\n                           CHAPTER III\n\n                  A Caucus-Race and a Long Tale\n\n\n  They were indeed a queer-looking party that assembled on the\nbank--the birds with draggled feathers, the animals with their\nfur clinging close to them, and all dripping wet, cross, and\nuncomfortable.\n\n  The first question of course was, how to get dry again:  they\nhad a consultation about this, and after a few minutes it seemed\nquite natural to Alice to find herself talking familiarly with\nthem, as if she had known them all her life.  Indeed, she had\nquite a long argument with the Lory, who at last turned sulky,\nand would only say, `I am older than you, and must know better';\nand this Alice would not allow without knowing how old it was,\nand, as the Lory positively refused to tell its age, there was no\nmore to be said.\n\n  At last the Mouse, who seemed to be a person of authority among\nthem, called out, `Sit down, all of you, and listen to me!  I'LL\nsoon make you dry enough!'  They all sat down at once, in a large\nring, with the Mouse in the middle.  Alice kept her eyes\nanxiously fixed on it, for she felt sure she would catch a bad\ncold if she did not get dry very soon.\n\n  `Ahem!' said the Mouse with an important air, `are you all ready?\nThis is the driest thing I know.  Silence all round, if you please!\n\"William the Conqueror, whose cause was favoured by the pope, was\nsoon submitted to by the English, who wanted leaders, and had been\nof late much accustomed to usurpation and conquest.  Edwin and\nMorcar, the earls of Mercia and Northumbria--\"'\n\n  `Ugh!' said the Lory, with a shiver.\n\n  `I beg your pardon!' said the Mouse, frowning, but very\npolitely:  `Did you speak?'\n\n  `Not I!' said the Lory hastily.\n\n  `I thought you did,' said the Mouse.  `--I proceed.  \"Edwin and\nMorcar, the earls of Mercia and Northumbria, declared for him:\nand even Stigand, the patriotic archbishop of Canterbury, found\nit advisable--\"'\n\n  `Found WHAT?' said the Duck.\n\n  `Found IT,' the Mouse replied rather crossly:  `of course you\nknow what \"it\" means.'\n\n  `I know what \"it\" means well enough, when I find a thing,' said\nthe Duck:  `it's generally a frog or a worm.  The question is,\nwhat did the archbishop find?'\n\n  The Mouse did not notice this question, but hurriedly went on,\n`\"--found it advisable to go with Edgar Atheling to meet William\nand offer him the crown.  William's conduct at first was\nmoderate.  But the insolence of his Normans--\"  How are you\ngetting on now, my dear?' it continued, turning to Alice as it\nspoke.\n\n  `As wet as ever,' said Alice in a melancholy tone:  `it doesn't\nseem to dry me at all.'\n\n  `In that case,' said the Dodo solemnly, rising to its feet, `I\nmove that the meeting adjourn, for the immediate adoption of more\nenergetic remedies--'\n\n  `Speak English!' said the Eaglet.  `I don't know the meaning of\nhalf those long words, and, what's more, I don't believe you do\neither!'  And the Eaglet bent down its head to hide a smile:\nsome of the other birds tittered audibly.\n\n  `What I was going to say,' said the Dodo in an offended tone,\n`was, that the best thing to get us dry would be a Caucus-race.'\n\n  `What IS a Caucus-race?' said Alice; not that she wanted much\nto know, but the Dodo had paused as if it thought that SOMEBODY\nought to speak, and no one else seemed inclined to say anything.\n\n  `Why,' said the Dodo, `the best way to explain it is to do it.'\n(And, as you might like to try the thing yourself, some winter\nday, I will tell you how the Dodo managed it.)\n\n  First it marked out a race-course, in a sort of circle, (`the\nexact shape doesn't matter,' it said,) and then all the party\nwere placed along the course, here and there.  There was no `One,\ntwo, three, and away,' but they began running when they liked,\nand left off when they liked, so that it was not easy to know\nwhen the race was over.  However, when they had been running half\nan hour or so, and were quite dry again, the Dodo suddenly called\nout `The race is over!' and they all crowded round it, panting,\nand asking, `But who has won?'\n\n  This question the Dodo could not answer without a great deal of\nthought, and it sat for a long time with one finger pressed upon\nits forehead (the position in which you usually see Shakespeare,\nin the pictures of him), while the rest waited in silence.  At\nlast the Dodo said, `EVERYBODY has won, and all must have\nprizes.'\n\n  `But who is to give the prizes?' quite a chorus of voices\nasked.\n\n  `Why, SHE, of course,' said the Dodo, pointing to Alice with\none finger; and the whole party at once crowded round her,\ncalling out in a confused way, `Prizes! Prizes!'\n\n  Alice had no idea what to do, and in despair she put her hand\nin her pocket, and pulled out a box of comfits, (luckily the salt\nwater had not got into it), and handed them round as prizes.\nThere was exactly one a-piece all round.\n\n  `But she must have a prize herself, you know,' said the Mouse.\n\n  `Of course,' the Dodo replied very gravely.  `What else have\nyou got in your pocket?' he went on, turning to Alice.\n\n  `Only a thimble,' said Alice sadly.\n\n  `Hand it over here,' said the Dodo.\n\n  Then they all crowded round her once more, while the Dodo\nsolemnly presented the thimble, saying `We beg your acceptance of\nthis elegant thimble'; and, when it had finished this short\nspeech, they all cheered.\n\n  Alice thought the whole thing very absurd, but they all looked\nso grave that she did not dare to laugh; and, as she could not\nthink of anything to say, she simply bowed, and took the thimble,\nlooking as solemn as she could.\n\n  The next thing was to eat the comfits:  this caused some noise\nand confusion, as the large birds complained that they could not\ntaste theirs, and the small ones choked and had to be patted on\nthe back.  However, it was over at last, and they sat down again\nin a ring, and begged the Mouse to tell them something more.\n\n  `You promised to tell me your history, you know,' said Alice,\n`and why it is you hate--C and D,' she added in a whisper, half\nafraid that it would be offended again.\n\n  `Mine is a long and a sad tale!' said the Mouse, turning to\nAlice, and sighing.\n\n  `It IS a long tail, certainly,' said Alice, looking down with\nwonder at the Mouse's tail; `but why do you call it sad?'  And\nshe kept on puzzling about it while the Mouse was speaking, so\nthat her idea of the tale was something like this:--\n\n                    `Fury said to a\n                   mouse, That he\n                 met in the\n               house,\n            \"Let us\n              both go to\n                law:  I will\n                  prosecute\n                    YOU.  --Come,\n                       I'll take no\n                        denial; We\n                     must have a\n                 trial:  For\n              really this\n           morning I've\n          nothing\n         to do.\"\n           Said the\n             mouse to the\n               cur, \"Such\n                 a trial,\n                   dear Sir,\n                         With\n                     no jury\n                  or judge,\n                would be\n              wasting\n             our\n              breath.\"\n               \"I'll be\n                 judge, I'll\n                   be jury,\"\n                         Said\n                    cunning\n                      old Fury:\n                     \"I'll\n                      try the\n                         whole\n                          cause,\n                             and\n                        condemn\n                       you\n                      to\n                       death.\"'\n\n\n  `You are not attending!' said the Mouse to Alice severely.\n`What are you thinking of?'\n\n  `I beg your pardon,' said Alice very humbly:  `you had got to\nthe fifth bend, I think?'\n\n  `I had NOT!' cried the Mouse, sharply and very angrily.\n\n  `A knot!' said Alice, always ready to make herself useful, and\nlooking anxiously about her.  `Oh, do let me help to undo it!'\n\n  `I shall do nothing of the sort,' said the Mouse, getting up\nand walking away.  `You insult me by talking such nonsense!'\n\n  `I didn't mean it!' pleaded poor Alice.  `But you're so easily\noffended, you know!'\n\n  The Mouse only growled in reply.\n\n  `Please come back and finish your story!' Alice called after\nit; and the others all joined in chorus, `Yes, please do!' but\nthe Mouse only shook its head impatiently, and walked a little\nquicker.\n\n  `What a pity it wouldn't stay!' sighed the Lory, as soon as it\nwas quite out of sight; and an old Crab took the opportunity of\nsaying to her daughter `Ah, my dear!  Let this be a lesson to you\nnever to lose YOUR temper!'  `Hold your tongue, Ma!' said the\nyoung Crab, a little snappishly.  `You're enough to try the\npatience of an oyster!'\n\n  `I wish I had our Dinah here, I know I do!' said Alice aloud,\naddressing nobody in particular.  `She'd soon fetch it back!'\n\n  `And who is Dinah, if I might venture to ask the question?'\nsaid the Lory.\n\n  Alice replied eagerly, for she was always ready to talk about\nher pet:  `Dinah's our cat.  And she's such a capital one for\ncatching mice you can't think!  And oh, I wish you could see her\nafter the birds!  Why, she'll eat a little bird as soon as look\nat it!'\n\n  This speech caused a remarkable sensation among the party.\nSome of the birds hurried off at once:  one old Magpie began\nwrapping itself up very carefully, remarking, `I really must be\ngetting home; the night-air doesn't suit my throat!' and a Canary\ncalled out in a trembling voice to its children, `Come away, my\ndears!  It's high time you were all in bed!'  On various pretexts\nthey all moved off, and Alice was soon left alone.\n\n  `I wish I hadn't mentioned Dinah!' she said to herself in a\nmelancholy tone.  `Nobody seems to like her, down here, and I'm\nsure she's the best cat in the world!  Oh, my dear Dinah!  I\nwonder if I shall ever see you any more!'  And here poor Alice\nbegan to cry again, for she felt very lonely and low-spirited.\nIn a little while, however, she again heard a little pattering of\nfootsteps in the distance, and she looked up eagerly, half hoping\nthat the Mouse had changed his mind, and was coming back to\nfinish his story.\n\n\n\n                           CHAPTER IV\n\n                The Rabbit Sends in a Little Bill\n\n\n  It was the White Rabbit, trotting slowly back again, and\nlooking anxiously about as it went, as if it had lost something;\nand she heard it muttering to itself `The Duchess!  The Duchess!\nOh my dear paws!  Oh my fur and whiskers!  She'll get me\nexecuted, as sure as ferrets are ferrets!  Where CAN I have\ndropped them, I wonder?'  Alice guessed in a moment that it was\nlooking for the fan and the pair of white kid gloves, and she\nvery good-naturedly began hunting about for them, but they were\nnowhere to be seen--everything seemed to have changed since her\nswim in the pool, and the great hall, with the glass table and\nthe little door, had vanished completely.\n\n  Very soon the Rabbit noticed Alice, as she went hunting about,\nand called out to her in an angry tone, `Why, Mary Ann, what ARE\nyou doing out here?  Run home this moment, and fetch me a pair of\ngloves and a fan!  Quick, now!'  And Alice was so much frightened\nthat she ran off at once in the direction it pointed to, without\ntrying to explain the mistake it had made.\n\n  `He took me for his housemaid,' she said to herself as she ran.\n`How surprised he'll be when he finds out who I am!  But I'd\nbetter take him his fan and gloves--that is, if I can find them.'\nAs she said this, she came upon a neat little house, on the door\nof which was a bright brass plate with the name `W. RABBIT'\nengraved upon it.  She went in without knocking, and hurried\nupstairs, in great fear lest she should meet the real Mary Ann,\nand be turned out of the house before she had found the fan and\ngloves.\n\n  `How queer it seems,' Alice said to herself, `to be going\nmessages for a rabbit!  I suppose Dinah'll be sending me on\nmessages next!'  And she began fancying the sort of thing that\nwould happen:  `\"Miss Alice!  Come here directly, and get ready\nfor your walk!\" \"Coming in a minute, nurse!  But I've got to see\nthat the mouse doesn't get out.\"  Only I don't think,' Alice went\non, `that they'd let Dinah stop in the house if it began ordering\npeople about like that!'\n\n  By this time she had found her way into a tidy little room with\na table in the window, and on it (as she had hoped) a fan and two\nor three pairs of tiny white kid gloves:  she took up the fan and\na pair of the gloves, and was just going to leave the room, when\nher eye fell upon a little bottle that stood near the looking-\nglass.  There was no label this time with the words `DRINK ME,'\nbut nevertheless she uncorked it and put it to her lips.  `I know\nSOMETHING interesting is sure to happen,' she said to herself,\n`whenever I eat or drink anything; so I'll just see what this\nbottle does.  I do hope it'll make me grow large again, for\nreally I'm quite tired of being such a tiny little thing!'\n\n  It did so indeed, and much sooner than she had expected:\nbefore she had drunk half the bottle, she found her head pressing\nagainst the ceiling, and had to stoop to save her neck from being\nbroken.  She hastily put down the bottle, saying to herself\n`That's quite enough--I hope I shan't grow any more--As it is, I\ncan't get out at the door--I do wish I hadn't drunk quite so\nmuch!'\n\n  Alas! it was too late to wish that!  She went on growing, and\ngrowing, and very soon had to kneel down on the floor:  in\nanother minute there was not even room for this, and she tried\nthe effect of lying down with one elbow against the door, and the\nother arm curled round her head.  Still she went on growing, and,\nas a last resource, she put one arm out of the window, and one\nfoot up the chimney, and said to herself `Now I can do no more,\nwhatever happens.  What WILL become of me?'\n\n  Luckily for Alice, the little magic bottle had now had its full\neffect, and she grew no larger:  still it was very uncomfortable,\nand, as there seemed to be no sort of chance of her ever getting\nout of the room again, no wonder she felt unhappy.\n\n  `It was much pleasanter at home,' thought poor Alice, `when one\nwasn't always growing larger and smaller, and being ordered about\nby mice and rabbits.  I almost wish I hadn't gone down that\nrabbit-hole--and yet--and yet--it's rather curious, you know,\nthis sort of life!  I do wonder what CAN have happened to me!\nWhen I used to read fairy-tales, I fancied that kind of thing\nnever happened, and now here I am in the middle of one!  There\nought to be a book written about me, that there ought!  And when\nI grow up, I'll write one--but I'm grown up now,' she added in a\nsorrowful tone; `at least there's no room to grow up any more\nHERE.'\n\n  `But then,' thought Alice, `shall I NEVER get any older than I\nam now?  That'll be a comfort, one way--never to be an old woman--\nbut then--always to have lessons to learn!  Oh, I shouldn't like THAT!'\n\n  `Oh, you foolish Alice!' she answered herself.  `How can you\nlearn lessons in here?  Why, there's hardly room for YOU, and no\nroom at all for any lesson-books!'\n\n  And so she went on, taking first one side and then the other,\nand making quite a conversation of it altogether; but after a few\nminutes she heard a voice outside, and stopped to listen.\n\n  `Mary Ann!  Mary Ann!' said the voice.  `Fetch me my gloves\nthis moment!'  Then came a little pattering of feet on the\nstairs.  Alice knew it was the Rabbit coming to look for her, and\nshe trembled till she shook the house, quite forgetting that she\nwas now about a thousand times as large as the Rabbit, and had no\nreason to be afraid of it.\n\n  Presently the Rabbit came up to the door, and tried to open it;\nbut, as the door opened inwards, and Alice's elbow was pressed\nhard against it, that attempt proved a failure.  Alice heard it\nsay to itself `Then I'll go round and get in at the window.'\n\n  `THAT you won't' thought Alice, and, after waiting till she\nfancied she heard the Rabbit just under the window, she suddenly\nspread out her hand, and made a snatch in the air.  She did not\nget hold of anything, but she heard a little shriek and a fall,\nand a crash of broken glass, from which she concluded that it was\njust possible it had fallen into a cucumber-frame, or something\nof the sort.\n\n  Next came an angry voice--the Rabbit's--`Pat! Pat!  Where are\nyou?'  And then a voice she had never heard before, `Sure then\nI'm here!  Digging for apples, yer honour!'\n\n  `Digging for apples, indeed!' said the Rabbit angrily.  `Here!\nCome and help me out of THIS!'  (Sounds of more broken glass.)\n\n  `Now tell me, Pat, what's that in the window?'\n\n  `Sure, it's an arm, yer honour!'  (He pronounced it `arrum.')\n\n  `An arm, you goose!   Who ever saw one that size?  Why, it\nfills the whole window!'\n\n  `Sure, it does, yer honour:  but it's an arm for all that.'\n\n  `Well, it's got no business there, at any rate:  go and take it\naway!'\n\n  There was a long silence after this, and Alice could only hear\nwhispers now and then; such as, `Sure, I don't like it, yer\nhonour, at all, at all!'  `Do as I tell you, you coward!' and at\nlast she spread out her hand again, and made another snatch in\nthe air.  This time there were TWO little shrieks, and more\nsounds of broken glass.  `What a number of cucumber-frames there\nmust be!' thought Alice.  `I wonder what they'll do next!  As for\npulling me out of the window, I only wish they COULD!  I'm sure I\ndon't want to stay in here any longer!'\n\n  She waited for some time without hearing anything more:  at\nlast came a rumbling of little cartwheels, and the sound of a\ngood many voices all talking together:  she made out the words:\n`Where's the other ladder?--Why, I hadn't to bring but one;\nBill's got the other--Bill! fetch it here, lad!--Here, put 'em up\nat this corner--No, tie 'em together first--they don't reach half\nhigh enough yet--Oh! they'll do well enough; don't be particular--\nHere, Bill! catch hold of this rope--Will the roof bear?--Mind\nthat loose slate--Oh, it's coming down!  Heads below!' (a loud\ncrash)--`Now, who did that?--It was Bill, I fancy--Who's to go\ndown the chimney?--Nay, I shan't! YOU do it!--That I won't,\nthen!--Bill's to go down--Here, Bill! the master says you're to\ngo down the chimney!'\n\n  `Oh! So Bill's got to come down the chimney, has he?' said\nAlice to herself.  `Shy, they seem to put everything upon Bill!\nI wouldn't be in Bill's place for a good deal:  this fireplace is\nnarrow, to be sure; but I THINK I can kick a little!'\n\n  She drew her foot as far down the chimney as she could, and\nwaited till she heard a little animal (she couldn't guess of what\nsort it was) scratching and scrambling about in the chimney close\nabove her:  then, saying to herself `This is Bill,' she gave one\nsharp kick, and waited to see what would happen next.\n\n  The first thing she heard was a general chorus of `There goes\nBill!' then the Rabbit's voice along--`Catch him, you by the\nhedge!' then silence, and then another confusion of voices--`Hold\nup his head--Brandy now--Don't choke him--How was it, old fellow?\nWhat happened to you?  Tell us all about it!'\n\n  Last came a little feeble, squeaking voice, (`That's Bill,'\nthought Alice,) `Well, I hardly know--No more, thank ye; I'm\nbetter now--but I'm a deal too flustered to tell you--all I know\nis, something comes at me like a Jack-in-the-box, and up I goes\nlike a sky-rocket!'\n\n  `So you did, old fellow!' said the others.\n\n  `We must burn the house down!' said the Rabbit's voice; and\nAlice called out as loud as she could, `If you do.  I'll set\nDinah at you!'\n\n  There was a dead silence instantly, and Alice thought to\nherself, `I wonder what they WILL do next!  If they had any\nsense, they'd take the roof off.'  After a minute or two, they\nbegan moving about again, and Alice heard the Rabbit say, `A\nbarrowful will do, to begin with.'\n\n  `A barrowful of WHAT?' thought Alice; but she had not long to\ndoubt, for the next moment a shower of little pebbles came\nrattling in at the window, and some of them hit her in the face.\n`I'll put a stop to this,' she said to herself, and shouted out,\n`You'd better not do that again!' which produced another dead\nsilence.\n\n  Alice noticed with some surprise that the pebbles were all\nturning into little cakes as they lay on the floor, and a bright\nidea came into her head.  `If I eat one of these cakes,' she\nthought, `it's sure to make SOME change in my size; and as it\ncan't possibly make me larger, it must make me smaller, I\nsuppose.'\n\n  So she swallowed one of the cakes, and was delighted to find\nthat she began shrinking directly.  As soon as she was small\nenough to get through the door, she ran out of the house, and\nfound quite a crowd of little animals and birds waiting outside.\nThe poor little Lizard, Bill, was in the middle, being held up by\ntwo guinea-pigs, who were giving it something out of a bottle.\nThey all made a rush at Alice the moment she appeared; but she\nran off as hard as she could, and soon found herself safe in a\nthick wood.\n\n  `The first thing I've got to do,' said Alice to herself, as she\nwandered about in the wood, `is to grow to my right size again;\nand the second thing is to find my way into that lovely garden.\nI think that will be the best plan.'\n\n  It sounded an excellent plan, no doubt, and very neatly and\nsimply arranged; the only difficulty was, that she had not the\nsmallest idea how to set about it; and while she was peering\nabout anxiously among the trees, a little sharp bark just over\nher head made her look up in a great hurry.\n\n  An enormous puppy was looking down at her with large round\neyes, and feebly stretching out one paw, trying to touch her.\n`Poor little thing!' said Alice, in a coaxing tone, and she tried\nhard to whistle to it; but she was terribly frightened all the\ntime at the thought that it might be hungry, in which case it\nwould be very likely to eat her up in spite of all her coaxing.\n\n  Hardly knowing what she did, she picked up a little bit of\nstick, and held it out to the puppy; whereupon the puppy jumped\ninto the air off all its feet at once, with a yelp of delight,\nand rushed at the stick, and made believe to worry it; then Alice\ndodged behind a great thistle, to keep herself from being run\nover; and the moment she appeared on the other side, the puppy\nmade another rush at the stick, and tumbled head over heels in\nits hurry to get hold of it; then Alice, thinking it was very\nlike having a game of play with a cart-horse, and expecting every\nmoment to be trampled under its feet, ran round the thistle\nagain; then the puppy began a series of short charges at the\nstick, running a very little way forwards each time and a long\nway back, and barking hoarsely all the while, till at last it sat\ndown a good way off, panting, with its tongue hanging out of its\nmouth, and its great eyes half shut.\n\n  This seemed to Alice a good opportunity for making her escape;\nso she set off at once, and ran till she was quite tired and out\nof breath, and till the puppy's bark sounded quite faint in the\ndistance.\n\n  `And yet what a dear little puppy it was!' said Alice, as she\nleant against a buttercup to rest herself, and fanned herself\nwith one of the leaves:  `I should have liked teaching it tricks\nvery much, if--if I'd only been the right size to do it!  Oh\ndear!  I'd nearly forgotten that I've got to grow up again!  Let\nme see--how IS it to be managed?  I suppose I ought to eat or\ndrink something or other; but the great question is, what?'\n\n  The great question certainly was, what?  Alice looked all round\nher at the flowers and the blades of grass, but she did not see\nanything that looked like the right thing to eat or drink under\nthe circumstances.  There was a large mushroom growing near her,\nabout the same height as herself; and when she had looked under\nit, and on both sides of it, and behind it, it occurred to her\nthat she might as well look and see what was on the top of it.\n\n  She stretched herself up on tiptoe, and peeped over the edge of\nthe mushroom, and her eyes immediately met those of a large\ncaterpillar, that was sitting on the top with its arms folded,\nquietly smoking a long hookah, and taking not the smallest notice\nof her or of anything else.\n\n\n\n                            CHAPTER V\n\n                    Advice from a Caterpillar\n\n\n  The Caterpillar and Alice looked at each other for some time in\nsilence:  at last the Caterpillar took the hookah out of its\nmouth, and addressed her in a languid, sleepy voice.\n\n  `Who are YOU?' said the Caterpillar.\n\n  This was not an encouraging opening for a conversation.  Alice\nreplied, rather shyly, `I--I hardly know, sir, just at present--\nat least I know who I WAS when I got up this morning, but I think\nI must have been changed several times since then.'\n\n  `What do you mean by that?' said the Caterpillar sternly.\n`Explain yourself!'\n\n  `I can't explain MYSELF, I'm afraid, sir' said Alice, `because\nI'm not myself, you see.'\n\n  `I don't see,' said the Caterpillar.\n\n  `I'm afraid I can't put it more clearly,' Alice replied very\npolitely, `for I can't understand it myself to begin with; and\nbeing so many different sizes in a day is very confusing.'\n\n  `It isn't,' said the Caterpillar.\n\n  `Well, perhaps you haven't found it so yet,' said Alice; `but\nwhen you have to turn into a chrysalis--you will some day, you\nknow--and then after that into a butterfly, I should think you'll\nfeel it a little queer, won't you?'\n\n  `Not a bit,' said the Caterpillar.\n\n  `Well, perhaps your feelings may be different,' said Alice;\n`all I know is, it would feel very queer to ME.'\n\n  `You!' said the Caterpillar contemptuously.  `Who are YOU?'\n\n  Which brought them back again to the beginning of the\nconversation.  Alice felt a little irritated at the Caterpillar's\nmaking such VERY short remarks, and she drew herself up and said,\nvery gravely, `I think, you ought to tell me who YOU are, first.'\n\n  `Why?' said the Caterpillar.\n\n  Here was another puzzling question; and as Alice could not\nthink of any good reason, and as the Caterpillar seemed to be in\na VERY unpleasant state of mind, she turned away.\n\n  `Come back!' the Caterpillar called after her.  `I've something\nimportant to say!'\n\n  This sounded promising, certainly:  Alice turned and came back\nagain.\n\n  `Keep your temper,' said the Caterpillar.\n\n  `Is that all?' said Alice, swallowing down her anger as well as\nshe could.\n\n  `No,' said the Caterpillar.\n\n  Alice thought she might as well wait, as she had nothing else\nto do, and perhaps after all it might tell her something worth\nhearing.  For some minutes it puffed away without speaking, but\nat last it unfolded its arms, took the hookah out of its mouth\nagain, and said, `So you think you're changed, do you?'\n\n  `I'm afraid I am, sir,' said Alice; `I can't remember things as\nI used--and I don't keep the same size for ten minutes together!'\n\n  `Can't remember WHAT things?' said the Caterpillar.\n\n  `Well, I've tried to say \"HOW DOTH THE LITTLE BUSY BEE,\" but it\nall came different!' Alice replied in a very melancholy voice.\n\n  `Repeat, \"YOU ARE OLD, FATHER WILLIAM,\"' said the Caterpillar.\n\n  Alice folded her hands, and began:--\n\n    `You are old, Father William,' the young man said,\n      `And your hair has become very white;\n    And yet you incessantly stand on your head--\n      Do you think, at your age, it is right?'\n\n    `In my youth,' Father William replied to his son,\n      `I feared it might injure the brain;\n    But, now that I'm perfectly sure I have none,\n      Why, I do it again and again.'\n\n    `You are old,' said the youth, `as I mentioned before,\n      And have grown most uncommonly fat;\n    Yet you turned a back-somersault in at the door--\n      Pray, what is the reason of that?'\n\n    `In my youth,' said the sage, as he shook his grey locks,\n      `I kept all my limbs very supple\n    By the use of this ointment--one shilling the box--\n      Allow me to sell you a couple?'\n\n    `You are old,' said the youth, `and your jaws are too weak\n      For anything tougher than suet;\n    Yet you finished the goose, with the bones and the beak--\n      Pray how did you manage to do it?'\n\n    `In my youth,' said his father, `I took to the law,\n      And argued each case with my wife;\n    And the muscular strength, which it gave to my jaw,\n      Has lasted the rest of my life.'\n\n    `You are old,' said the youth, `one would hardly suppose\n      That your eye was as steady as ever;\n    Yet you balanced an eel on the end of your nose--\n      What made you so awfully clever?'\n\n    `I have answered three questions, and that is enough,'\n      Said his father; `don't give yourself airs!\n    Do you think I can listen all day to such stuff?\n      Be off, or I'll kick you down stairs!'\n\n\n  `That is not said right,' said the Caterpillar.\n\n  `Not QUITE right, I'm afraid,' said Alice, timidly; `some of the\nwords have got altered.'\n\n  `It is wrong from beginning to end,' said the Caterpillar\ndecidedly, and there was silence for some minutes.\n\n  The Caterpillar was the first to speak.\n\n  `What size do you want to be?' it asked.\n\n  `Oh, I'm not particular as to size,' Alice hastily replied;\n`only one doesn't like changing so often, you know.'\n\n  `I DON'T know,' said the Caterpillar.\n\n  Alice said nothing:  she had never been so much contradicted in\nher life before, and she felt that she was losing her temper.\n\n  `Are you content now?' said the Caterpillar.\n\n  `Well, I should like to be a LITTLE larger, sir, if you\nwouldn't mind,' said Alice:  `three inches is such a wretched\nheight to be.'\n\n  `It is a very good height indeed!' said the Caterpillar\nangrily, rearing itself upright as it spoke (it was exactly three\ninches high).\n\n  `But I'm not used to it!' pleaded poor Alice in a piteous tone.\nAnd she thought of herself, `I wish the creatures wouldn't be so\neasily offended!'\n\n  `You'll get used to it in time,' said the Caterpillar; and it\nput the hookah into its mouth and began smoking again.\n\n  This time Alice waited patiently until it chose to speak again.\nIn a minute or two the Caterpillar took the hookah out of its\nmouth and yawned once or twice, and shook itself.  Then it got\ndown off the mushroom, and crawled away in the grass, merely\nremarking as it went, `One side will make you grow taller, and\nthe other side will make you grow shorter.'\n\n  `One side of WHAT?  The other side of WHAT?' thought Alice to\nherself.\n\n  `Of the mushroom,' said the Caterpillar, just as if she had\nasked it aloud; and in another moment it was out of sight.\n\n  Alice remained looking thoughtfully at the mushroom for a\nminute, trying to make out which were the two sides of it; and as\nit was perfectly round, she found this a very difficult question.\nHowever, at last she stretched her arms round it as far as they\nwould go, and broke off a bit of the edge with each hand.\n\n  `And now which is which?' she said to herself, and nibbled a\nlittle of the right-hand bit to try the effect:  the next moment\nshe felt a violent blow underneath her chin:  it had struck her\nfoot!\n\n  She was a good deal frightened by this very sudden change, but\nshe felt that there was no time to be lost, as she was shrinking\nrapidly; so she set to work at once to eat some of the other bit.\nHer chin was pressed so closely against her foot, that there was\nhardly room to open her mouth; but she did it at last, and\nmanaged to swallow a morsel of the lefthand bit.\n\n\n     *       *       *       *       *       *       *\n\n         *       *       *       *       *       *\n\n     *       *       *       *       *       *       *\n\n  `Come, my head's free at last!' said Alice in a tone of\ndelight, which changed into alarm in another moment, when she\nfound that her shoulders were nowhere to be found:  all she could\nsee, when she looked down, was an immense length of neck, which\nseemed to rise like a stalk out of a sea of green leaves that lay\nfar below her.\n\n  `What CAN all that green stuff be?' said Alice.  `And where\nHAVE my shoulders got to?  And oh, my poor hands, how is it I\ncan't see you?'  She was moving them about as she spoke, but no\nresult seemed to follow, except a little shaking among the\ndistant green leaves.\n\n  As there seemed to be no chance of getting her hands up to her\nhead, she tried to get her head down to them, and was delighted\nto find that her neck would bend about easily in any direction,\nlike a serpent.  She had just succeeded in curving it down into a\ngraceful zigzag, and was going to dive in among the leaves, which\nshe found to be nothing but the tops of the trees under which she\nhad been wandering, when a sharp hiss made her draw back in a\nhurry:  a large pigeon had flown into her face, and was beating\nher violently with its wings.\n\n  `Serpent!' screamed the Pigeon.\n\n  `I'm NOT a serpent!' said Alice indignantly.  `Let me alone!'\n\n  `Serpent, I say again!' repeated the Pigeon, but in a more\nsubdued tone, and added with a kind of sob, `I've tried every\nway, and nothing seems to suit them!'\n\n  `I haven't the least idea what you're talking about,' said\nAlice.\n\n  `I've tried the roots of trees, and I've tried banks, and I've\ntried hedges,' the Pigeon went on, without attending to her; `but\nthose serpents!  There's no pleasing them!'\n\n  Alice was more and more puzzled, but she thought there was no\nuse in saying anything more till the Pigeon had finished.\n\n  `As if it wasn't trouble enough hatching the eggs,' said the\nPigeon; `but I must be on the look-out for serpents night and\nday!  Why, I haven't had a wink of sleep these three weeks!'\n\n  `I'm very sorry you've been annoyed,' said Alice, who was\nbeginning to see its meaning.\n\n  `And just as I'd taken the highest tree in the wood,' continued\nthe Pigeon, raising its voice to a shriek, `and just as I was\nthinking I should be free of them at last, they must needs come\nwriggling down from the sky!  Ugh, Serpent!'\n\n  `But I'm NOT a serpent, I tell you!' said Alice.  `I'm a--I'm\na--'\n\n  `Well!  WHAT are you?' said the Pigeon.  `I can see you're\ntrying to invent something!'\n\n  `I--I'm a little girl,' said Alice, rather doubtfully, as she\nremembered the number of changes she had gone through that day.\n\n  `A likely story indeed!' said the Pigeon in a tone of the\ndeepest contempt.  `I've seen a good many little girls in my\ntime, but never ONE with such a neck as that!  No, no!  You're a\nserpent; and there's no use denying it.  I suppose you'll be\ntelling me next that you never tasted an egg!'\n\n  `I HAVE tasted eggs, certainly,' said Alice, who was a very\ntruthful child; `but little girls eat eggs quite as much as\nserpents do, you know.'\n\n  `I don't believe it,' said the Pigeon; `but if they do, why\nthen they're a kind of serpent, that's all I can say.'\n\n  This was such a new idea to Alice, that she was quite silent\nfor a minute or two, which gave the Pigeon the opportunity of\nadding, `You're looking for eggs, I know THAT well enough; and\nwhat does it matter to me whether you're a little girl or a\nserpent?'\n\n  `It matters a good deal to ME,' said Alice hastily; `but I'm\nnot looking for eggs, as it happens; and if I was, I shouldn't\nwant YOURS:  I don't like them raw.'\n\n  `Well, be off, then!' said the Pigeon in a sulky tone, as it\nsettled down again into its nest.  Alice crouched down among the\ntrees as well as she could, for her neck kept getting entangled\namong the branches, and every now and then she had to stop and\nuntwist it.  After a while she remembered that she still held the\npieces of mushroom in her hands, and she set to work very\ncarefully, nibbling first at one and then at the other, and\ngrowing sometimes taller and sometimes shorter, until she had\nsucceeded in bringing herself down to her usual height.\n\n  It was so long since she had been anything near the right size,\nthat it felt quite strange at first; but she got used to it in a\nfew minutes, and began talking to herself, as usual.  `Come,\nthere's half my plan done now!  How puzzling all these changes\nare!  I'm never sure what I'm going to be, from one minute to\nanother!  However, I've got back to my right size:  the next\nthing is, to get into that beautiful garden--how IS that to be\ndone, I wonder?'  As she said this, she came suddenly upon an\nopen place, with a little house in it about four feet high.\n`Whoever lives there,' thought Alice, `it'll never do to come\nupon them THIS size:  why, I should frighten them out of their\nwits!'  So she began nibbling at the righthand bit again, and did\nnot venture to go near the house till she had brought herself\ndown to nine inches high.\n\n\n\n                           CHAPTER VI\n\n                         Pig and Pepper\n\n\n  For a minute or two she stood looking at the house, and\nwondering what to do next, when suddenly a footman in livery came\nrunning out of the wood--(she considered him to be a footman\nbecause he was in livery:  otherwise, judging by his face only,\nshe would have called him a fish)--and rapped loudly at the door\nwith his knuckles.  It was opened by another footman in livery,\nwith a round face, and large eyes like a frog; and both footmen,\nAlice noticed, had powdered hair that curled all over their\nheads.  She felt very curious to know what it was all about, and\ncrept a little way out of the wood to listen.\n\n  The Fish-Footman began by producing from under his arm a great\nletter, nearly as large as himself, and this he handed over to\nthe other, saying, in a solemn tone, `For the Duchess.  An\ninvitation from the Queen to play croquet.'  The Frog-Footman\nrepeated, in the same solemn tone, only changing the order of the\nwords a little, `From the Queen.  An invitation for the Duchess\nto play croquet.'\n\n  Then they both bowed low, and their curls got entangled\ntogether.\n\n  Alice laughed so much at this, that she had to run back into\nthe wood for fear of their hearing her; and when she next peeped\nout the Fish-Footman was gone, and the other was sitting on the\nground near the door, staring stupidly up into the sky.\n\n  Alice went timidly up to the door, and knocked.\n\n  `There's no sort of use in knocking,' said the Footman, `and\nthat for two reasons.  First, because I'm on the same side of the\ndoor as you are; secondly, because they're making such a noise\ninside, no one could possibly hear you.'  And certainly there was\na most extraordinary noise going on within--a constant howling\nand sneezing, and every now and then a great crash, as if a dish\nor kettle had been broken to pieces.\n\n  `Please, then,' said Alice, `how am I to get in?'\n\n  `There might be some sense in your knocking,' the Footman went\non without attending to her, `if we had the door between us.  For\ninstance, if you were INSIDE, you might knock, and I could let\nyou out, you know.'  He was looking up into the sky all the time\nhe was speaking, and this Alice thought decidedly uncivil.  `But\nperhaps he can't help it,' she said to herself; `his eyes are so\nVERY nearly at the top of his head.  But at any rate he might\nanswer questions.--How am I to get in?' she repeated, aloud.\n\n  `I shall sit here,' the Footman remarked, `till tomorrow--'\n\n  At this moment the door of the house opened, and a large plate\ncame skimming out, straight at the Footman's head:  it just\ngrazed his nose, and broke to pieces against one of the trees\nbehind him.\n\n  `--or next day, maybe,' the Footman continued in the same tone,\nexactly as if nothing had happened.\n\n  `How am I to get in?' asked Alice again, in a louder tone.\n\n  `ARE you to get in at all?' said the Footman.  `That's the\nfirst question, you know.'\n\n  It was, no doubt:  only Alice did not like to be told so.\n`It's really dreadful,' she muttered to herself, `the way all the\ncreatures argue.  It's enough to drive one crazy!'\n\n  The Footman seemed to think this a good opportunity for\nrepeating his remark, with variations.  `I shall sit here,' he\nsaid, `on and off, for days and days.'\n\n  `But what am I to do?' said Alice.\n\n  `Anything you like,' said the Footman, and began whistling.\n\n  `Oh, there's no use in talking to him,' said Alice desperately:\n`he's perfectly idiotic!'  And she opened the door and went in.\n\n  The door led right into a large kitchen, which was full of\nsmoke from one end to the other:  the Duchess was sitting on a\nthree-legged stool in the middle, nursing a baby; the cook was\nleaning over the fire, stirring a large cauldron which seemed to\nbe full of soup.\n\n  `There's certainly too much pepper in that soup!' Alice said to\nherself, as well as she could for sneezing.\n\n  There was certainly too much of it in the air.  Even the\nDuchess sneezed occasionally; and as for the baby, it was\nsneezing and howling alternately without a moment's pause.  The\nonly things in the kitchen that did not sneeze, were the cook,\nand a large cat which was sitting on the hearth and grinning from\near to ear.\n\n  `Please would you tell me,' said Alice, a little timidly, for\nshe was not quite sure whether it was good manners for her to\nspeak first, `why your cat grins like that?'\n\n  `It's a Cheshire cat,' said the Duchess, `and that's why.  Pig!'\n\n  She said the last word with such sudden violence that Alice\nquite jumped; but she saw in another moment that it was addressed\nto the baby, and not to her, so she took courage, and went on\nagain:--\n\n  `I didn't know that Cheshire cats always grinned; in fact, I\ndidn't know that cats COULD grin.'\n\n  `They all can,' said the Duchess; `and most of 'em do.'\n\n  `I don't know of any that do,' Alice said very politely,\nfeeling quite pleased to have got into a conversation.\n\n  `You don't know much,' said the Duchess; `and that's a fact.'\n\n  Alice did not at all like the tone of this remark, and thought\nit would be as well to introduce some other subject of\nconversation.  While she was trying to fix on one, the cook took\nthe cauldron of soup off the fire, and at once set to work\nthrowing everything within her reach at the Duchess and the baby\n--the fire-irons came first; then followed a shower of saucepans,\nplates, and dishes.  The Duchess took no notice of them even when\nthey hit her; and the baby was howling so much already, that it\nwas quite impossible to say whether the blows hurt it or not.\n\n  `Oh, PLEASE mind what you're doing!' cried Alice, jumping up\nand down in an agony of terror.  `Oh, there goes his PRECIOUS\nnose'; as an unusually large saucepan flew close by it, and very\nnearly carried it off.\n\n  `If everybody minded their own business,' the Duchess said in a\nhoarse growl, `the world would go round a deal faster than it\ndoes.'\n\n  `Which would NOT be an advantage,' said Alice, who felt very\nglad to get an opportunity of showing off a little of her\nknowledge.  `Just think of what work it would make with the day\nand night!  You see the earth takes twenty-four hours to turn\nround on its axis--'\n\n  `Talking of axes,' said the Duchess, `chop off her head!'\n\n  Alice glanced rather anxiously at the cook, to see if she meant\nto take the hint; but the cook was busily stirring the soup, and\nseemed not to be listening, so she went on again:  `Twenty-four\nhours, I THINK; or is it twelve?  I--'\n\n  `Oh, don't bother ME,' said the Duchess; `I never could abide\nfigures!'  And with that she began nursing her child again,\nsinging a sort of lullaby to it as she did so, and giving it a\nviolent shake at the end of every line:\n\n        `Speak roughly to your little boy,\n          And beat him when he sneezes:\n        He only does it to annoy,\n          Because he knows it teases.'\n\n                    CHORUS.\n\n    (In which the cook and the baby joined):--\n\n                `Wow! wow! wow!'\n\n  While the Duchess sang the second verse of the song, she kept\ntossing the baby violently up and down, and the poor little thing\nhowled so, that Alice could hardly hear the words:--\n\n        `I speak severely to my boy,\n          I beat him when he sneezes;\n        For he can thoroughly enjoy\n          The pepper when he pleases!'\n\n                    CHORUS.\n\n                `Wow! wow! wow!'\n\n  `Here! you may nurse it a bit, if you like!' the Duchess said\nto Alice, flinging the baby at her as she spoke.  `I must go and\nget ready to play croquet with the Queen,' and she hurried out of\nthe room.  The cook threw a frying-pan after her as she went out,\nbut it just missed her.\n\n  Alice caught the baby with some difficulty, as it was a queer-\nshaped little creature, and held out its arms and legs in all\ndirections, `just like a star-fish,' thought Alice.  The poor\nlittle thing was snorting like a steam-engine when she caught it,\nand kept doubling itself up and straightening itself out again,\nso that altogether, for the first minute or two, it was as much\nas she could do to hold it.\n\n  As soon as she had made out the proper way of nursing it,\n(which was to twist it up into a sort of knot, and then keep\ntight hold of its right ear and left foot, so as to prevent its\nundoing itself,) she carried it out into the open air.  `IF I\ndon't take this child away with me,' thought Alice, `they're sure\nto kill it in a day or two:  wouldn't it be murder to leave it\nbehind?'  She said the last words out loud, and the little thing\ngrunted in reply (it had left off sneezing by this time).  `Don't\ngrunt,' said Alice; `that's not at all a proper way of expressing\nyourself.'\n\n  The baby grunted again, and Alice looked very anxiously into\nits face to see what was the matter with it.  There could be no\ndoubt that it had a VERY turn-up nose, much more like a snout\nthan a real nose; also its eyes were getting extremely small for\na baby:  altogether Alice did not like the look of the thing at\nall.  `But perhaps it was only sobbing,' she thought, and looked\ninto its eyes again, to see if there were any tears.\n\n  No, there were no tears.  `If you're going to turn into a pig,\nmy dear,' said Alice, seriously, `I'll have nothing more to do\nwith you.  Mind now!'  The poor little thing sobbed again (or\ngrunted, it was impossible to say which), and they went on for\nsome while in silence.\n\n  Alice was just beginning to think to herself, `Now, what am I\nto do with this creature when I get it home?' when it grunted\nagain, so violently, that she looked down into its face in some\nalarm.  This time there could be NO mistake about it:  it was\nneither more nor less than a pig, and she felt that it would be\nquite absurd for her to carry it further.\n\n  So she set the little creature down, and felt quite relieved to\nsee it trot away quietly into the wood.  `If it had grown up,'\nshe said to herself, `it would have made a dreadfully ugly child:\nbut it makes rather a handsome pig, I think.'  And she began\nthinking over other children she knew, who might do very well as\npigs, and was just saying to herself, `if one only knew the right\nway to change them--' when she was a little startled by seeing\nthe Cheshire Cat sitting on a bough of a tree a few yards off.\n\n  The Cat only grinned when it saw Alice.  It looked good-\nnatured, she thought:  still it had VERY long claws and a great\nmany teeth, so she felt that it ought to be treated with respect.\n\n  `Cheshire Puss,' she began, rather timidly, as she did not at\nall know whether it would like the name:  however, it only\ngrinned a little wider.  `Come, it's pleased so far,' thought\nAlice, and she went on.  `Would you tell me, please, which way I\nought to go from here?'\n\n  `That depends a good deal on where you want to get to,' said\nthe Cat.\n\n  `I don't much care where--' said Alice.\n\n  `Then it doesn't matter which way you go,' said the Cat.\n\n  `--so long as I get SOMEWHERE,' Alice added as an explanation.\n\n  `Oh, you're sure to do that,' said the Cat, `if you only walk\nlong enough.'\n\n  Alice felt that this could not be denied, so she tried another\nquestion.  `What sort of people live about here?'\n\n  `In THAT direction,' the Cat said, waving its right paw round,\n`lives a Hatter:  and in THAT direction,' waving the other paw,\n`lives a March Hare.  Visit either you like:  they're both mad.'\n\n  `But I don't want to go among mad people,' Alice remarked.\n\n  `Oh, you can't help that,' said the Cat:  `we're all mad here.\nI'm mad.  You're mad.'\n\n  `How do you know I'm mad?' said Alice.\n\n  `You must be,' said the Cat, `or you wouldn't have come here.'\n\n  Alice didn't think that proved it at all; however, she went on\n`And how do you know that you're mad?'\n\n  `To begin with,' said the Cat, `a dog's not mad.  You grant\nthat?'\n\n  `I suppose so,' said Alice.\n\n  `Well, then,' the Cat went on, `you see, a dog growls when it's\nangry, and wags its tail when it's pleased.  Now I growl when I'm\npleased, and wag my tail when I'm angry.  Therefore I'm mad.'\n\n  `I call it purring, not growling,' said Alice.\n\n  `Call it what you like,' said the Cat.  `Do you play croquet\nwith the Queen to-day?'\n\n  `I should like it very much,' said Alice, `but I haven't been\ninvited yet.'\n\n  `You'll see me there,' said the Cat, and vanished.\n\n  Alice was not much surprised at this, she was getting so used\nto queer things happening.  While she was looking at the place\nwhere it had been, it suddenly appeared again.\n\n  `By-the-bye, what became of the baby?' said the Cat.  `I'd\nnearly forgotten to ask.'\n\n  `It turned into a pig,' Alice quietly said, just as if it had\ncome back in a natural way.\n\n  `I thought it would,' said the Cat, and vanished again.\n\n  Alice waited a little, half expecting to see it again, but it\ndid not appear, and after a minute or two she walked on in the\ndirection in which the March Hare was said to live.  `I've seen\nhatters before,' she said to herself; `the March Hare will be\nmuch the most interesting, and perhaps as this is May it won't be\nraving mad--at least not so mad as it was in March.'  As she said\nthis, she looked up, and there was the Cat again, sitting on a\nbranch of a tree.\n\n  `Did you say pig, or fig?' said the Cat.\n\n  `I said pig,' replied Alice; `and I wish you wouldn't keep\nappearing and vanishing so suddenly:  you make one quite giddy.'\n\n  `All right,' said the Cat; and this time it vanished quite slowly,\nbeginning with the end of the tail, and ending with the grin,\nwhich remained some time after the rest of it had gone.\n\n  `Well!  I've often seen a cat without a grin,' thought Alice;\n`but a grin without a cat!  It's the most curious thing I ever\nsaw in my life!'\n\n  She had not gone much farther before she came in sight of the\nhouse of the March Hare:  she thought it must be the right house,\nbecause the chimneys were shaped like ears and the roof was\nthatched with fur.  It was so large a house, that she did not\nlike to go nearer till she had nibbled some more of the lefthand\nbit of mushroom, and raised herself to about two feet high:  even\nthen she walked up towards it rather timidly, saying to herself\n`Suppose it should be raving mad after all!  I almost wish I'd\ngone to see the Hatter instead!'\n\n\n\n                           CHAPTER VII\n\n                         A Mad Tea-Party\n\n\n  There was a table set out under a tree in front of the house,\nand the March Hare and the Hatter were having tea at it:  a\nDormouse was sitting between them, fast asleep, and the other two\nwere using it as a cushion, resting their elbows on it, and talking\nover its head.  `Very uncomfortable for the Dormouse,' thought Alice;\n`only, as it's asleep, I suppose it doesn't mind.'\n\n  The table was a large one, but the three were all crowded\ntogether at one corner of it:  `No room!  No room!' they cried\nout when they saw Alice coming.  `There's PLENTY of room!' said\nAlice indignantly, and she sat down in a large arm-chair at one\nend of the table.\n\n  `Have some wine,' the March Hare said in an encouraging tone.\n\n  Alice looked all round the table, but there was nothing on it\nbut tea.  `I don't see any wine,' she remarked.\n\n  `There isn't any,' said the March Hare.\n\n  `Then it wasn't very civil of you to offer it,' said Alice\nangrily.\n\n  `It wasn't very civil of you to sit down without being\ninvited,' said the March Hare.\n\n  `I didn't know it was YOUR table,' said Alice; `it's laid for a\ngreat many more than three.'\n\n  `Your hair wants cutting,' said the Hatter.  He had been\nlooking at Alice for some time with great curiosity, and this was\nhis first speech.\n\n  `You should learn not to make personal remarks,' Alice said\nwith some severity; `it's very rude.'\n\n  The Hatter opened his eyes very wide on hearing this; but all\nhe SAID was, `Why is a raven like a writing-desk?'\n\n  `Come, we shall have some fun now!' thought Alice.  `I'm glad\nthey've begun asking riddles.--I believe I can guess that,' she\nadded aloud.\n\n  `Do you mean that you think you can find out the answer to it?'\nsaid the March Hare.\n\n  `Exactly so,' said Alice.\n\n  `Then you should say what you mean,' the March Hare went on.\n\n  `I do,' Alice hastily replied; `at least--at least I mean what\nI say--that's the same thing, you know.'\n\n  `Not the same thing a bit!' said the Hatter.  `You might just\nas well say that \"I see what I eat\" is the same thing as \"I eat\nwhat I see\"!'\n\n  `You might just as well say,' added the March Hare, `that \"I\nlike what I get\" is the same thing as \"I get what I like\"!'\n\n  `You might just as well say,' added the Dormouse, who seemed to\nbe talking in his sleep, `that \"I breathe when I sleep\" is the\nsame thing as \"I sleep when I breathe\"!'\n\n  `It IS the same thing with you,' said the Hatter, and here the\nconversation dropped, and the party sat silent for a minute,\nwhile Alice thought over all she could remember about ravens and\nwriting-desks, which wasn't much.\n\n  The Hatter was the first to break the silence.  `What day of\nthe month is it?' he said, turning to Alice:  he had taken his\nwatch out of his pocket, and was looking at it uneasily, shaking\nit every now and then, and holding it to his ear.\n\n  Alice considered a little, and then said `The fourth.'\n\n  `Two days wrong!' sighed the Hatter.  `I told you butter\nwouldn't suit the works!' he added looking angrily at the March\nHare.\n\n  `It was the BEST butter,' the March Hare meekly replied.\n\n  `Yes, but some crumbs must have got in as well,' the Hatter\ngrumbled:  `you shouldn't have put it in with the bread-knife.'\n\n  The March Hare took the watch and looked at it gloomily:  then\nhe dipped it into his cup of tea, and looked at it again:  but he\ncould think of nothing better to say than his first remark, `It\nwas the BEST butter, you know.'\n\n  Alice had been looking over his shoulder with some curiosity.\n`What a funny watch!' she remarked.  `It tells the day of the\nmonth, and doesn't tell what o'clock it is!'\n\n  `Why should it?' muttered the Hatter.  `Does YOUR watch tell\nyou what year it is?'\n\n  `Of course not,' Alice replied very readily:  `but that's\nbecause it stays the same year for such a long time together.'\n\n  `Which is just the case with MINE,' said the Hatter.\n\n  Alice felt dreadfully puzzled.  The Hatter's remark seemed to\nhave no sort of meaning in it, and yet it was certainly English.\n`I don't quite understand you,' she said, as politely as she\ncould.\n\n  `The Dormouse is asleep again,' said the Hatter, and he poured\na little hot tea upon its nose.\n\n  The Dormouse shook its head impatiently, and said, without\nopening its eyes, `Of course, of course; just what I was going to\nremark myself.'\n\n  `Have you guessed the riddle yet?' the Hatter said, turning to\nAlice again.\n\n  `No, I give it up,' Alice replied:  `what's the answer?'\n\n  `I haven't the slightest idea,' said the Hatter.\n\n  `Nor I,' said the March Hare.\n\n  Alice sighed wearily.  `I think you might do something better\nwith the time,' she said, `than waste it in asking riddles that\nhave no answers.'\n\n  `If you knew Time as well as I do,' said the Hatter, `you\nwouldn't talk about wasting IT.  It's HIM.'\n\n  `I don't know what you mean,' said Alice.\n\n  `Of course you don't!' the Hatter said, tossing his head\ncontemptuously.  `I dare say you never even spoke to Time!'\n\n  `Perhaps not,' Alice cautiously replied:  `but I know I have to\nbeat time when I learn music.'\n\n  `Ah! that accounts for it,' said the Hatter.  `He won't stand\nbeating.  Now, if you only kept on good terms with him, he'd do\nalmost anything you liked with the clock.  For instance, suppose\nit were nine o'clock in the morning, just time to begin lessons:\nyou'd only have to whisper a hint to Time, and round goes the\nclock in a twinkling!  Half-past one, time for dinner!'\n\n  (`I only wish it was,' the March Hare said to itself in a\nwhisper.)\n\n  `That would be grand, certainly,' said Alice thoughtfully:\n`but then--I shouldn't be hungry for it, you know.'\n\n  `Not at first, perhaps,' said the Hatter:  `but you could keep\nit to half-past one as long as you liked.'\n\n  `Is that the way YOU manage?' Alice asked.\n\n  The Hatter shook his head mournfully.  `Not I!' he replied.\n`We quarrelled last March--just before HE went mad, you know--'\n(pointing with his tea spoon at the March Hare,) `--it was at the\ngreat concert given by the Queen of Hearts, and I had to sing\n\n            \"Twinkle, twinkle, little bat!\n            How I wonder what you're at!\"\n\nYou know the song, perhaps?'\n\n  `I've heard something like it,' said Alice.\n\n  `It goes on, you know,' the Hatter continued, `in this way:--\n\n            \"Up above the world you fly,\n            Like a tea-tray in the sky.\n                    Twinkle, twinkle--\"'\n\nHere the Dormouse shook itself, and began singing in its sleep\n`Twinkle, twinkle, twinkle, twinkle--' and went on so long that\nthey had to pinch it to make it stop.\n\n  `Well, I'd hardly finished the first verse,' said the Hatter,\n`when the Queen jumped up and bawled out, \"He's murdering the\ntime!  Off with his head!\"'\n\n  `How dreadfully savage!' exclaimed Alice.\n\n  `And ever since that,' the Hatter went on in a mournful tone,\n`he won't do a thing I ask!  It's always six o'clock now.'\n\n  A bright idea came into Alice's head.  `Is that the reason so\nmany tea-things are put out here?' she asked.\n\n  `Yes, that's it,' said the Hatter with a sigh:  `it's always\ntea-time, and we've no time to wash the things between whiles.'\n\n  `Then you keep moving round, I suppose?' said Alice.\n\n  `Exactly so,' said the Hatter:  `as the things get used up.'\n\n  `But what happens when you come to the beginning again?' Alice\nventured to ask.\n\n  `Suppose we change the subject,' the March Hare interrupted,\nyawning.  `I'm getting tired of this.  I vote the young lady\ntells us a story.'\n\n  `I'm afraid I don't know one,' said Alice, rather alarmed at\nthe proposal.\n\n  `Then the Dormouse shall!' they both cried.  `Wake up,\nDormouse!'  And they pinched it on both sides at once.\n\n  The Dormouse slowly opened his eyes.  `I wasn't asleep,' he\nsaid in a hoarse, feeble voice:  `I heard every word you fellows\nwere saying.'\n\n  `Tell us a story!' said the March Hare.\n\n  `Yes, please do!' pleaded Alice.\n\n  `And be quick about it,' added the Hatter, `or you'll be asleep\nagain before it's done.'\n\n  `Once upon a time there were three little sisters,' the\nDormouse began in a great hurry; `and their names were Elsie,\nLacie, and Tillie; and they lived at the bottom of a well--'\n\n  `What did they live on?' said Alice, who always took a great\ninterest in questions of eating and drinking.\n\n  `They lived on treacle,' said the Dormouse, after thinking a\nminute or two.\n\n  `They couldn't have done that, you know,' Alice gently\nremarked; `they'd have been ill.'\n\n  `So they were,' said the Dormouse; `VERY ill.'\n\n  Alice tried to fancy to herself what such an extraordinary ways\nof living would be like, but it puzzled her too much, so she went\non:  `But why did they live at the bottom of a well?'\n\n  `Take some more tea,' the March Hare said to Alice, very\nearnestly.\n\n  `I've had nothing yet,' Alice replied in an offended tone, `so\nI can't take more.'\n\n  `You mean you can't take LESS,' said the Hatter:  `it's very\neasy to take MORE than nothing.'\n\n  `Nobody asked YOUR opinion,' said Alice.\n\n  `Who's making personal remarks now?' the Hatter asked\ntriumphantly.\n\n  Alice did not quite know what to say to this:  so she helped\nherself to some tea and bread-and-butter, and then turned to the\nDormouse, and repeated her question.  `Why did they live at the\nbottom of a well?'\n\n  The Dormouse again took a minute or two to think about it, and\nthen said, `It was a treacle-well.'\n\n  `There's no such thing!'  Alice was beginning very angrily, but\nthe Hatter and the March Hare went `Sh! sh!' and the Dormouse\nsulkily remarked, `If you can't be civil, you'd better finish the\nstory for yourself.'\n\n  `No, please go on!' Alice said very humbly; `I won't interrupt\nagain.  I dare say there may be ONE.'\n\n  `One, indeed!' said the Dormouse indignantly.  However, he\nconsented to go on.  `And so these three little sisters--they\nwere learning to draw, you know--'\n\n  `What did they draw?' said Alice, quite forgetting her promise.\n\n  `Treacle,' said the Dormouse, without considering at all this\ntime.\n\n  `I want a clean cup,' interrupted the Hatter:  `let's all move\none place on.'\n\n  He moved on as he spoke, and the Dormouse followed him:  the\nMarch Hare moved into the Dormouse's place, and Alice rather\nunwillingly took the place of the March Hare.  The Hatter was the\nonly one who got any advantage from the change:  and Alice was a\ngood deal worse off than before, as the March Hare had just upset\nthe milk-jug into his plate.\n\n  Alice did not wish to offend the Dormouse again, so she began\nvery cautiously:  `But I don't understand.  Where did they draw\nthe treacle from?'\n\n  `You can draw water out of a water-well,' said the Hatter; `so\nI should think you could draw treacle out of a treacle-well--eh,\nstupid?'\n\n  `But they were IN the well,' Alice said to the Dormouse, not\nchoosing to notice this last remark.\n\n  `Of course they were', said the Dormouse; `--well in.'\n\n  This answer so confused poor Alice, that she let the Dormouse\ngo on for some time without interrupting it.\n\n  `They were learning to draw,' the Dormouse went on, yawning and\nrubbing its eyes, for it was getting very sleepy; `and they drew\nall manner of things--everything that begins with an M--'\n\n  `Why with an M?' said Alice.\n\n  `Why not?' said the March Hare.\n\n  Alice was silent.\n\n  The Dormouse had closed its eyes by this time, and was going\noff into a doze; but, on being pinched by the Hatter, it woke up\nagain with a little shriek, and went on:  `--that begins with an\nM, such as mouse-traps, and the moon, and memory, and muchness--\nyou know you say things are \"much of a muchness\"--did you ever\nsee such a thing as a drawing of a muchness?'\n\n  `Really, now you ask me,' said Alice, very much confused, `I\ndon't think--'\n\n  `Then you shouldn't talk,' said the Hatter.\n\n  This piece of rudeness was more than Alice could bear:  she got\nup in great disgust, and walked off; the Dormouse fell asleep\ninstantly, and neither of the others took the least notice of her\ngoing, though she looked back once or twice, half hoping that\nthey would call after her:  the last time she saw them, they were\ntrying to put the Dormouse into the teapot.\n\n  `At any rate I'll never go THERE again!' said Alice as she\npicked her way through the wood.  `It's the stupidest tea-party I\never was at in all my life!'\n\n  Just as she said this, she noticed that one of the trees had a\ndoor leading right into it.  `That's very curious!' she thought.\n`But everything's curious today.  I think I may as well go in at once.'\nAnd in she went.\n\n  Once more she found herself in the long hall, and close to the\nlittle glass table.  `Now, I'll manage better this time,'\nshe said to herself, and began by taking the little golden key,\nand unlocking the door that led into the garden.  Then she went\nto work nibbling at the mushroom (she had kept a piece of it\nin her pocket) till she was about a foot high:  then she walked down\nthe little passage:  and THEN--she found herself at last in the\nbeautiful garden, among the bright flower-beds and the cool fountains.\n\n\n\n                          CHAPTER VIII\n\n                   The Queen's Croquet-Ground\n\n\n  A large rose-tree stood near the entrance of the garden:  the\nroses growing on it were white, but there were three gardeners at\nit, busily painting them red.  Alice thought this a very curious\nthing, and she went nearer to watch them, and just as she came up\nto them she heard one of them say, `Look out now, Five!  Don't go\nsplashing paint over me like that!'\n\n  `I couldn't help it,' said Five, in a sulky tone; `Seven jogged\nmy elbow.'\n\n  On which Seven looked up and said, `That's right, Five!  Always\nlay the blame on others!'\n\n  `YOU'D better not talk!' said Five.  `I heard the Queen say only\nyesterday you deserved to be beheaded!'\n\n  `What for?' said the one who had spoken first.\n\n  `That's none of YOUR business, Two!' said Seven.\n\n  `Yes, it IS his business!' said Five, `and I'll tell him--it\nwas for bringing the cook tulip-roots instead of onions.'\n\n  Seven flung down his brush, and had just begun `Well, of all\nthe unjust things--' when his eye chanced to fall upon Alice, as\nshe stood watching them, and he checked himself suddenly:  the\nothers looked round also, and all of them bowed low.\n\n  `Would you tell me,' said Alice, a little timidly, `why you are\npainting those roses?'\n\n  Five and Seven said nothing, but looked at Two.  Two began in a\nlow voice, `Why the fact is, you see, Miss, this here ought to\nhave been a RED rose-tree, and we put a white one in by mistake;\nand if the Queen was to find it out, we should all have our heads\ncut off, you know.  So you see, Miss, we're doing our best, afore\nshe comes, to--'  At this moment Five, who had been anxiously\nlooking across the garden, called out `The Queen!  The Queen!'\nand the three gardeners instantly threw themselves flat upon\ntheir faces.  There was a sound of many footsteps, and Alice\nlooked round, eager to see the Queen.\n\n  First came ten soldiers carrying clubs; these were all shaped\nlike the three gardeners, oblong and flat, with their hands and\nfeet at the corners:  next the ten courtiers; these were\nornamented all over with diamonds, and walked two and two, as the\nsoldiers did.  After these came the royal children; there were\nten of them, and the little dears came jumping merrily along hand\nin hand, in couples:  they were all ornamented with hearts.  Next\ncame the guests, mostly Kings and Queens, and among them Alice\nrecognised the White Rabbit:  it was talking in a hurried nervous\nmanner, smiling at everything that was said, and went by without\nnoticing her.  Then followed the Knave of Hearts, carrying the\nKing's crown on a crimson velvet cushion; and, last of all this\ngrand procession, came THE KING AND QUEEN OF HEARTS.\n\n  Alice was rather doubtful whether she ought not to lie down on\nher face like the three gardeners, but she could not remember\never having heard of such a rule at processions; `and besides,\nwhat would be the use of a procession,' thought she, `if people\nhad all to lie down upon their faces, so that they couldn't see it?'\nSo she stood still where she was, and waited.\n\n  When the procession came opposite to Alice, they all stopped\nand looked at her, and the Queen said severely `Who is this?'\nShe said it to the Knave of Hearts, who only bowed and smiled in reply.\n\n  `Idiot!' said the Queen, tossing her head impatiently; and,\nturning to Alice, she went on, `What's your name, child?'\n\n  `My name is Alice, so please your Majesty,' said Alice very\npolitely; but she added, to herself, `Why, they're only a pack of\ncards, after all.  I needn't be afraid of them!'\n\n  `And who are THESE?' said the Queen, pointing to the three\ngardeners who were lying round the rosetree; for, you see, as\nthey were lying on their faces, and the pattern on their backs\nwas the same as the rest of the pack, she could not tell whether\nthey were gardeners, or soldiers, or courtiers, or three of her\nown children.\n\n  `How should I know?' said Alice, surprised at her own courage.\n`It's no business of MINE.'\n\n  The Queen turned crimson with fury, and, after glaring at her\nfor a moment like a wild beast, screamed `Off with her head!\nOff--'\n\n  `Nonsense!' said Alice, very loudly and decidedly, and the\nQueen was silent.\n\n  The King laid his hand upon her arm, and timidly said\n`Consider, my dear:  she is only a child!'\n\n  The Queen turned angrily away from him, and said to the Knave\n`Turn them over!'\n\n  The Knave did so, very carefully, with one foot.\n\n  `Get up!' said the Queen, in a shrill, loud voice, and the\nthree gardeners instantly jumped up, and began bowing to the\nKing, the Queen, the royal children, and everybody else.\n\n  `Leave off that!' screamed the Queen.  `You make me giddy.'\nAnd then, turning to the rose-tree, she went on, `What HAVE you\nbeen doing here?'\n\n  `May it please your Majesty,' said Two, in a very humble tone,\ngoing down on one knee as he spoke, `we were trying--'\n\n  `I see!' said the Queen, who had meanwhile been examining the\nroses.  `Off with their heads!' and the procession moved on,\nthree of the soldiers remaining behind to execute the unfortunate\ngardeners, who ran to Alice for protection.\n\n  `You shan't be beheaded!' said Alice, and she put them into a\nlarge flower-pot that stood near.  The three soldiers wandered\nabout for a minute or two, looking for them, and then quietly\nmarched off after the others.\n\n  `Are their heads off?' shouted the Queen.\n\n  `Their heads are gone, if it please your Majesty!' the soldiers\nshouted in reply.\n\n  `That's right!' shouted the Queen.  `Can you play croquet?'\n\n  The soldiers were silent, and looked at Alice, as the question\nwas evidently meant for her.\n\n  `Yes!' shouted Alice.\n\n  `Come on, then!' roared the Queen, and Alice joined the\nprocession, wondering very much what would happen next.\n\n  `It's--it's a very fine day!' said a timid voice at her side.\nShe was walking by the White Rabbit, who was peeping anxiously\ninto her face.\n\n  `Very,' said Alice:  `--where's the Duchess?'\n\n  `Hush!  Hush!' said the Rabbit in a low, hurried tone.  He\nlooked anxiously over his shoulder as he spoke, and then raised\nhimself upon tiptoe, put his mouth close to her ear, and\nwhispered `She's under sentence of execution.'\n\n  `What for?' said Alice.\n\n  `Did you say \"What a pity!\"?' the Rabbit asked.\n\n  `No, I didn't,' said Alice:  `I don't think it's at all a pity.\nI said \"What for?\"'\n\n  `She boxed the Queen's ears--' the Rabbit began.  Alice gave a\nlittle scream of laughter.  `Oh, hush!' the Rabbit whispered in a\nfrightened tone.  `The Queen will hear you!  You see, she came\nrather late, and the Queen said--'\n\n  `Get to your places!' shouted the Queen in a voice of thunder,\nand people began running about in all directions, tumbling up\nagainst each other; however, they got settled down in a minute or\ntwo, and the game began.  Alice thought she had never seen such a\ncurious croquet-ground in her life; it was all ridges and\nfurrows; the balls were live hedgehogs, the mallets live\nflamingoes, and the soldiers had to double themselves up and to\nstand on their hands and feet, to make the arches.\n\n  The chief difficulty Alice found at first was in managing her\nflamingo:  she succeeded in getting its body tucked away,\ncomfortably enough, under her arm, with its legs hanging down,\nbut generally, just as she had got its neck nicely straightened\nout, and was going to give the hedgehog a blow with its head, it\nWOULD twist itself round and look up in her face, with such a\npuzzled expression that she could not help bursting out laughing:\nand when she had got its head down, and was going to begin again,\nit was very provoking to find that the hedgehog had unrolled\nitself, and was in the act of crawling away:  besides all this,\nthere was generally a ridge or furrow in the way wherever she\nwanted to send the hedgehog to, and, as the doubled-up soldiers\nwere always getting up and walking off to other parts of the\nground, Alice soon came to the conclusion that it was a very\ndifficult game indeed.\n\n  The players all played at once without waiting for turns,\nquarrelling all the while, and fighting for the hedgehogs; and in\na very short time the Queen was in a furious passion, and went\nstamping about, and shouting `Off with his head!' or `Off with\nher head!' about once in a minute.\n\n  Alice began to feel very uneasy:  to be sure, she had not as\nyet had any dispute with the Queen, but she knew that it might\nhappen any minute, `and then,' thought she, `what would become of\nme?  They're dreadfully fond of beheading people here; the great\nwonder is, that there's any one left alive!'\n\n  She was looking about for some way of escape, and wondering\nwhether she could get away without being seen, when she noticed a\ncurious appearance in the air:  it puzzled her very much at\nfirst, but, after watching it a minute or two, she made it out to\nbe a grin, and she said to herself `It's the Cheshire Cat:  now I\nshall have somebody to talk to.'\n\n  `How are you getting on?' said the Cat, as soon as there was\nmouth enough for it to speak with.\n\n  Alice waited till the eyes appeared, and then nodded.  `It's no\nuse speaking to it,' she thought, `till its ears have come, or at\nleast one of them.'  In another minute the whole head appeared,\nand then Alice put down her flamingo, and began an account of the\ngame, feeling very glad she had someone to listen to her.  The\nCat seemed to think that there was enough of it now in sight, and\nno more of it appeared.\n\n  `I don't think they play at all fairly,' Alice began, in rather\na complaining tone, `and they all quarrel so dreadfully one can't\nhear oneself speak--and they don't seem to have any rules in\nparticular; at least, if there are, nobody attends to them--and\nyou've no idea how confusing it is all the things being alive;\nfor instance, there's the arch I've got to go through next\nwalking about at the other end of the ground--and I should have\ncroqueted the Queen's hedgehog just now, only it ran away when it\nsaw mine coming!'\n\n  `How do you like the Queen?' said the Cat in a low voice.\n\n  `Not at all,' said Alice:  `she's so extremely--'  Just then\nshe noticed that the Queen was close behind her, listening:  so\nshe went on, `--likely to win, that it's hardly worth while\nfinishing the game.'\n\n  The Queen smiled and passed on.\n\n  `Who ARE you talking to?' said the King, going up to Alice, and\nlooking at the Cat's head with great curiosity.\n\n  `It's a friend of mine--a Cheshire Cat,' said Alice:  `allow me\nto introduce it.'\n\n  `I don't like the look of it at all,' said the King:\n`however, it may kiss my hand if it likes.'\n\n  `I'd rather not,' the Cat remarked.\n\n  `Don't be impertinent,' said the King, `and don't look at me\nlike that!'  He got behind Alice as he spoke.\n\n  `A cat may look at a king,' said Alice.  `I've read that in\nsome book, but I don't remember where.'\n\n  `Well, it must be removed,' said the King very decidedly, and\nhe called the Queen, who was passing at the moment, `My dear!  I\nwish you would have this cat removed!'\n\n  The Queen had only one way of settling all difficulties, great\nor small.  `Off with his head!' she said, without even looking\nround.\n\n  `I'll fetch the executioner myself,' said the King eagerly, and\nhe hurried off.\n\n  Alice thought she might as well go back, and see how the game\nwas going on, as she heard the Queen's voice in the distance,\nscreaming with passion.  She had already heard her sentence three\nof the players to be executed for having missed their turns, and\nshe did not like the look of things at all, as the game was in\nsuch confusion that she never knew whether it was her turn or\nnot.  So she went in search of her hedgehog.\n\n  The hedgehog was engaged in a fight with another hedgehog,\nwhich seemed to Alice an excellent opportunity for croqueting one\nof them with the other:  the only difficulty was, that her\nflamingo was gone across to the other side of the garden, where\nAlice could see it trying in a helpless sort of way to fly up\ninto a tree.\n\n  By the time she had caught the flamingo and brought it back,\nthe fight was over, and both the hedgehogs were out of sight:\n`but it doesn't matter much,' thought Alice, `as all the arches\nare gone from this side of the ground.'  So she tucked it away\nunder her arm, that it might not escape again, and went back for\na little more conversation with her friend.\n\n  When she got back to the Cheshire Cat, she was surprised to\nfind quite a large crowd collected round it:  there was a dispute\ngoing on between the executioner, the King, and the Queen, who\nwere all talking at once, while all the rest were quite silent,\nand looked very uncomfortable.\n\n  The moment Alice appeared, she was appealed to by all three to\nsettle the question, and they repeated their arguments to her,\nthough, as they all spoke at once, she found it very hard indeed\nto make out exactly what they said.\n\n  The executioner's argument was, that you couldn't cut off a\nhead unless there was a body to cut it off from:  that he had\nnever had to do such a thing before, and he wasn't going to begin\nat HIS time of life.\n\n  The King's argument was, that anything that had a head could be\nbeheaded, and that you weren't to talk nonsense.\n\n  The Queen's argument was, that if something wasn't done about\nit in less than no time she'd have everybody executed, all round.\n(It was this last remark that had made the whole party look so\ngrave and anxious.)\n\n  Alice could think of nothing else to say but `It belongs to the\nDuchess:  you'd better ask HER about it.'\n\n  `She's in prison,' the Queen said to the executioner:  `fetch\nher here.'  And the executioner went off like an arrow.\n\n   The Cat's head began fading away the moment he was gone, and,\nby the time he had come back with the Duchess, it had entirely\ndisappeared; so the King and the executioner ran wildly up and down\nlooking for it, while the rest of the party went back to the game.\n\n\n\n                           CHAPTER IX\n\n                     The Mock Turtle's Story\n\n\n  `You can't think how glad I am to see you again, you dear old\nthing!' said the Duchess, as she tucked her arm affectionately\ninto Alice's, and they walked off together.\n\n  Alice was very glad to find her in such a pleasant temper, and\nthought to herself that perhaps it was only the pepper that had\nmade her so savage when they met in the kitchen.\n\n  `When I'M a Duchess,' she said to herself, (not in a very\nhopeful tone though), `I won't have any pepper in my kitchen AT\nALL.  Soup does very well without--Maybe it's always pepper that\nmakes people hot-tempered,' she went on, very much pleased at\nhaving found out a new kind of rule, `and vinegar that makes them\nsour--and camomile that makes them bitter--and--and barley-sugar\nand such things that make children sweet-tempered.  I only wish\npeople knew that:  then they wouldn't be so stingy about it, you\nknow--'\n\n  She had quite forgotten the Duchess by this time, and was a\nlittle startled when she heard her voice close to her ear.\n`You're thinking about something, my dear, and that makes you\nforget to talk.  I can't tell you just now what the moral of that\nis, but I shall remember it in a bit.'\n\n  `Perhaps it hasn't one,' Alice ventured to remark.\n\n  `Tut, tut, child!' said the Duchess.  `Everything's got a\nmoral, if only you can find it.'  And she squeezed herself up\ncloser to Alice's side as she spoke.\n\n  Alice did not much like keeping so close to her:  first,\nbecause the Duchess was VERY ugly; and secondly, because she was\nexactly the right height to rest her chin upon Alice's shoulder,\nand it was an uncomfortably sharp chin.  However, she did not\nlike to be rude, so she bore it as well as she could.\n\n  `The game's going on rather better now,' she said, by way of\nkeeping up the conversation a little.\n\n  `'Tis so,' said the Duchess:  `and the moral of that is--\"Oh,\n'tis love, 'tis love, that makes the world go round!\"'\n\n  `Somebody said,' Alice whispered, `that it's done by everybody\nminding their own business!'\n\n  `Ah, well!  It means much the same thing,' said the Duchess,\ndigging her sharp little chin into Alice's shoulder as she added,\n`and the moral of THAT is--\"Take care of the sense, and the\nsounds will take care of themselves.\"'\n\n  `How fond she is of finding morals in things!' Alice thought to\nherself.\n\n  `I dare say you're wondering why I don't put my arm round your\nwaist,' the Duchess said after a pause:  `the reason is, that I'm\ndoubtful about the temper of your flamingo.  Shall I try the\nexperiment?'\n\n  `HE might bite,' Alice cautiously replied, not feeling at all\nanxious to have the experiment tried.\n\n  `Very true,' said the Duchess:  `flamingoes and mustard both\nbite.  And the moral of that is--\"Birds of a feather flock\ntogether.\"'\n\n  `Only mustard isn't a bird,' Alice remarked.\n\n  `Right, as usual,' said the Duchess:  `what a clear way you\nhave of putting things!'\n\n  `It's a mineral, I THINK,' said Alice.\n\n  `Of course it is,' said the Duchess, who seemed ready to agree\nto everything that Alice said; `there's a large mustard-mine near\nhere.  And the moral of that is--\"The more there is of mine, the\nless there is of yours.\"'\n\n  `Oh, I know!' exclaimed Alice, who had not attended to this\nlast remark, `it's a vegetable.  It doesn't look like one, but it\nis.'\n\n  `I quite agree with you,' said the Duchess; `and the moral of\nthat is--\"Be what you would seem to be\"--or if you'd like it put\nmore simply--\"Never imagine yourself not to be otherwise than\nwhat it might appear to others that what you were or might have\nbeen was not otherwise than what you had been would have appeared\nto them to be otherwise.\"'\n\n  `I think I should understand that better,' Alice said very\npolitely, `if I had it written down:  but I can't quite follow it\nas you say it.'\n\n  `That's nothing to what I could say if I chose,' the Duchess\nreplied, in a pleased tone.\n\n  `Pray don't trouble yourself to say it any longer than that,'\nsaid Alice.\n\n  `Oh, don't talk about trouble!' said the Duchess.  `I make you\na present of everything I've said as yet.'\n\n  `A cheap sort of present!' thought Alice.  `I'm glad they don't\ngive birthday presents like that!'  But she did not venture to\nsay it out loud.\n\n  `Thinking again?' the Duchess asked, with another dig of her\nsharp little chin.\n\n  `I've a right to think,' said Alice sharply, for she was\nbeginning to feel a little worried.\n\n  `Just about as much right,' said the Duchess, `as pigs have to fly;\nand the m--'\n\n  But here, to Alice's great surprise, the Duchess's voice died\naway, even in the middle of her favourite word `moral,' and the\narm that was linked into hers began to tremble.  Alice looked up,\nand there stood the Queen in front of them, with her arms folded,\nfrowning like a thunderstorm.\n\n  `A fine day, your Majesty!' the Duchess began in a low, weak\nvoice.\n\n  `Now, I give you fair warning,' shouted the Queen, stamping on\nthe ground as she spoke; `either you or your head must be off,\nand that in about half no time!  Take your choice!'\n\n  The Duchess took her choice, and was gone in a moment.\n\n  `Let's go on with the game,' the Queen said to Alice; and Alice\nwas too much frightened to say a word, but slowly followed her\nback to the croquet-ground.\n\n  The other guests had taken advantage of the Queen's absence,\nand were resting in the shade:  however, the moment they saw her,\nthey hurried back to the game, the Queen merely remarking that a\nmoment's delay would cost them their lives.\n\n  All the time they were playing the Queen never left off\nquarrelling with the other players, and shouting `Off with his\nhead!' or `Off with her head!'  Those whom she sentenced were\ntaken into custody by the soldiers, who of course had to leave\noff being arches to do this, so that by the end of half an hour\nor so there were no arches left, and all the players, except the\nKing, the Queen, and Alice, were in custody and under sentence of\nexecution.\n\n  Then the Queen left off, quite out of breath, and said to\nAlice, `Have you seen the Mock Turtle yet?'\n\n  `No,' said Alice.  `I don't even know what a Mock Turtle is.'\n\n  `It's the thing Mock Turtle Soup is made from,' said the Queen.\n\n  `I never saw one, or heard of one,' said Alice.\n\n  `Come on, then,' said the Queen, `and he shall tell you his\nhistory,'\n\n  As they walked off together, Alice heard the King say in a low\nvoice, to the company generally, `You are all pardoned.'  `Come,\nTHAT'S a good thing!' she said to herself, for she had felt quite\nunhappy at the number of executions the Queen had ordered.\n\n  They very soon came upon a Gryphon, lying fast asleep in the\nsun.  (IF you don't know what a Gryphon is, look at the picture.)\n`Up, lazy thing!' said the Queen, `and take this young lady to\nsee the Mock Turtle, and to hear his history.  I must go back and\nsee after some executions I have ordered'; and she walked off,\nleaving Alice alone with the Gryphon.  Alice did not quite like\nthe look of the creature, but on the whole she thought it would\nbe quite as safe to stay with it as to go after that savage\nQueen:  so she waited.\n\n  The Gryphon sat up and rubbed its eyes:  then it watched the\nQueen till she was out of sight:  then it chuckled.  `What fun!'\nsaid the Gryphon, half to itself, half to Alice.\n\n  `What IS the fun?' said Alice.\n\n  `Why, SHE,' said the Gryphon.  `It's all her fancy, that:  they\nnever executes nobody, you know.  Come on!'\n\n  `Everybody says \"come on!\" here,' thought Alice, as she went\nslowly after it:  `I never was so ordered about in all my life,\nnever!'\n\n  They had not gone far before they saw the Mock Turtle in the\ndistance, sitting sad and lonely on a little ledge of rock, and,\nas they came nearer, Alice could hear him sighing as if his heart\nwould break.  She pitied him deeply.  `What is his sorrow?' she\nasked the Gryphon, and the Gryphon answered, very nearly in the\nsame words as before, `It's all his fancy, that:  he hasn't got\nno sorrow, you know.  Come on!'\n\n  So they went up to the Mock Turtle, who looked at them with\nlarge eyes full of tears, but said nothing.\n\n  `This here young lady,' said the Gryphon, `she wants for to\nknow your history, she do.'\n\n  `I'll tell it her,' said the Mock Turtle in a deep, hollow\ntone:  `sit down, both of you, and don't speak a word till I've\nfinished.'\n\n  So they sat down, and nobody spoke for some minutes.  Alice\nthought to herself, `I don't see how he can EVEN finish, if he\ndoesn't begin.'  But she waited patiently.\n\n  `Once,' said the Mock Turtle at last, with a deep sigh, `I was\na real Turtle.'\n\n  These words were followed by a very long silence, broken only\nby an occasional exclamation of `Hjckrrh!' from the Gryphon, and\nthe constant heavy sobbing of the Mock Turtle.  Alice was very\nnearly getting up and saying, `Thank you, sir, for your\ninteresting story,' but she could not help thinking there MUST be\nmore to come, so she sat still and said nothing.\n\n  `When we were little,' the Mock Turtle went on at last, more\ncalmly, though still sobbing a little now and then, `we went to\nschool in the sea.  The master was an old Turtle--we used to call\nhim Tortoise--'\n\n  `Why did you call him Tortoise, if he wasn't one?' Alice asked.\n\n  `We called him Tortoise because he taught us,' said the Mock\nTurtle angrily:  `really you are very dull!'\n\n  `You ought to be ashamed of yourself for asking such a simple\nquestion,' added the Gryphon; and then they both sat silent and\nlooked at poor Alice, who felt ready to sink into the earth.  At\nlast the Gryphon said to the Mock Turtle, `Drive on, old fellow!\nDon't be all day about it!' and he went on in these words:\n\n  `Yes, we went to school in the sea, though you mayn't believe\nit--'\n\n  `I never said I didn't!' interrupted Alice.\n\n  `You did,' said the Mock Turtle.\n\n  `Hold your tongue!' added the Gryphon, before Alice could speak\nagain.  The Mock Turtle went on.\n\n  `We had the best of educations--in fact, we went to school\nevery day--'\n\n  `I'VE been to a day-school, too,' said Alice; `you needn't be\nso proud as all that.'\n\n  `With extras?' asked the Mock Turtle a little anxiously.\n\n  `Yes,' said Alice, `we learned French and music.'\n\n  `And washing?' said the Mock Turtle.\n\n  `Certainly not!' said Alice indignantly.\n\n  `Ah! then yours wasn't a really good school,' said the Mock\nTurtle in a tone of great relief.  `Now at OURS they had at the\nend of the bill, \"French, music, AND WASHING--extra.\"'\n\n  `You couldn't have wanted it much,' said Alice; `living at the\nbottom of the sea.'\n\n  `I couldn't afford to learn it.' said the Mock Turtle with a\nsigh.  `I only took the regular course.'\n\n  `What was that?' inquired Alice.\n\n  `Reeling and Writhing, of course, to begin with,' the Mock\nTurtle replied; `and then the different branches of Arithmetic--\nAmbition, Distraction, Uglification, and Derision.'\n\n  `I never heard of \"Uglification,\"' Alice ventured to say.  `What is it?'\n\n  The Gryphon lifted up both its paws in surprise.  `What!  Never\nheard of uglifying!' it exclaimed.  `You know what to beautify is,\nI suppose?'\n\n  `Yes,' said Alice doubtfully:  `it means--to--make--anything--prettier.'\n\n  `Well, then,' the Gryphon went on, `if you don't know what to\nuglify is, you ARE a simpleton.'\n\n  Alice did not feel encouraged to ask any more questions about\nit, so she turned to the Mock Turtle, and said `What else had you\nto learn?'\n\n  `Well, there was Mystery,' the Mock Turtle replied, counting\noff the subjects on his flappers, `--Mystery, ancient and modern,\nwith Seaography:  then Drawling--the Drawling-master was an old\nconger-eel, that used to come once a week:  HE taught us\nDrawling, Stretching, and Fainting in Coils.'\n\n  `What was THAT like?' said Alice.\n\n  `Well, I can't show it you myself,' the Mock Turtle said:  `I'm\ntoo stiff.  And the Gryphon never learnt it.'\n\n  `Hadn't time,' said the Gryphon:  `I went to the Classics\nmaster, though.  He was an old crab, HE was.'\n\n  `I never went to him,' the Mock Turtle said with a sigh:  `he\ntaught Laughing and Grief, they used to say.'\n\n  `So he did, so he did,' said the Gryphon, sighing in his turn;\nand both creatures hid their faces in their paws.\n\n  `And how many hours a day did you do lessons?' said Alice, in a\nhurry to change the subject.\n\n  `Ten hours the first day,' said the Mock Turtle: `nine the\nnext, and so on.'\n\n  `What a curious plan!' exclaimed Alice.\n\n  `That's the reason they're called lessons,' the Gryphon\nremarked:  `because they lessen from day to day.'\n\n  This was quite a new idea to Alice, and she thought it over a\nlittle before she made her next remark.  `Then the eleventh day\nmust have been a holiday?'\n\n  `Of course it was,' said the Mock Turtle.\n\n  `And how did you manage on the twelfth?' Alice went on eagerly.\n\n  `That's enough about lessons,' the Gryphon interrupted in a\nvery decided tone:  `tell her something about the games now.'\n\n\n\n                            CHAPTER X\n\n                      The Lobster Quadrille\n\n\n  The Mock Turtle sighed deeply, and drew the back of one flapper\nacross his eyes.  He looked at Alice, and tried to speak, but for\na minute or two sobs choked his voice.  `Same as if he had a bone\nin his throat,' said the Gryphon:  and it set to work shaking him\nand punching him in the back.  At last the Mock Turtle recovered\nhis voice, and, with tears running down his cheeks, he went on\nagain:--\n\n  `You may not have lived much under the sea--' (`I haven't,' said Alice)--\n`and perhaps you were never even introduced to a lobster--'\n(Alice began to say `I once tasted--' but checked herself hastily,\nand said `No, never') `--so you can have no idea what a delightful\nthing a Lobster Quadrille is!'\n\n  `No, indeed,' said Alice.  `What sort of a dance is it?'\n\n  `Why,' said the Gryphon, `you first form into a line along the sea-shore--'\n\n  `Two lines!' cried the Mock Turtle.  `Seals, turtles, salmon, and so on;\nthen, when you've cleared all the jelly-fish out of the way--'\n\n  `THAT generally takes some time,' interrupted the Gryphon.\n\n  `--you advance twice--'\n\n  `Each with a lobster as a partner!' cried the Gryphon.\n\n  `Of course,' the Mock Turtle said:  `advance twice, set to\npartners--'\n\n  `--change lobsters, and retire in same order,' continued the\nGryphon.\n\n  `Then, you know,' the Mock Turtle went on, `you throw the--'\n\n  `The lobsters!' shouted the Gryphon, with a bound into the air.\n\n  `--as far out to sea as you can--'\n\n  `Swim after them!' screamed the Gryphon.\n\n  `Turn a somersault in the sea!' cried the Mock Turtle,\ncapering wildly about.\n\n  `Change lobsters again!' yelled the Gryphon at the top of its voice.\n\n  `Back to land again, and that's all the first figure,' said the\nMock Turtle, suddenly dropping his voice; and the two creatures,\nwho had been jumping about like mad things all this time, sat\ndown again very sadly and quietly, and looked at Alice.\n\n  `It must be a very pretty dance,' said Alice timidly.\n\n  `Would you like to see a little of it?' said the Mock Turtle.\n\n  `Very much indeed,' said Alice.\n\n  `Come, let's try the first figure!' said the Mock Turtle to the\nGryphon.  `We can do without lobsters, you know.  Which shall\nsing?'\n\n  `Oh, YOU sing,' said the Gryphon.  `I've forgotten the words.'\n\n  So they began solemnly dancing round and round Alice, every now\nand then treading on her toes when they passed too close, and\nwaving their forepaws to mark the time, while the Mock Turtle\nsang this, very slowly and sadly:--\n\n\n`\"Will you walk a little faster?\" said a whiting to a snail.\n\"There's a porpoise close behind us, and he's treading on my\n tail.\nSee how eagerly the lobsters and the turtles all advance!\nThey are waiting on the shingle--will you come and join the\ndance?\n\nWill you, won't you, will you, won't you, will you join the\ndance?\nWill you, won't you, will you, won't you, won't you join the\ndance?\n\n\n\"You can really have no notion how delightful it will be\nWhen they take us up and throw us, with the lobsters, out to\n                                                      sea!\"\nBut the snail replied \"Too far, too far!\" and gave a look\n                                                       askance--\nSaid he thanked the whiting kindly, but he would not join the\n   dance.\n    Would not, could not, would not, could not, would not join\n        the dance.\n    Would not, could not, would not, could not, could not join\n        the dance.\n\n`\"What matters it how far we go?\" his scaly friend replied.\n\"There is another shore, you know, upon the other side.\nThe further off from England the nearer is to France--\nThen turn not pale, beloved snail, but come and join the dance.\n\n    Will you, won't you, will you, won't you, will you join the\n         dance?\n    Will you, won't you, will you, won't you, won't you join the\n         dance?\"'\n\n\n\n  `Thank you, it's a very interesting dance to watch,' said\nAlice, feeling very glad that it was over at last:  `and I do so\nlike that curious song about the whiting!'\n\n  `Oh, as to the whiting,' said the Mock Turtle, `they--you've\nseen them, of course?'\n\n  `Yes,' said Alice, `I've often seen them at dinn--' she\nchecked herself hastily.\n\n  `I don't know where Dinn may be,' said the Mock Turtle, `but\nif you've seen them so often, of course you know what they're\nlike.'\n\n  `I believe so,' Alice replied thoughtfully.  `They have their\ntails in their mouths--and they're all over crumbs.'\n\n  `You're wrong about the crumbs,' said the Mock Turtle:\n`crumbs would all wash off in the sea.  But they HAVE their tails\nin their mouths; and the reason is--' here the Mock Turtle\nyawned and shut his eyes.--`Tell her about the reason and all\nthat,' he said to the Gryphon.\n\n  `The reason is,' said the Gryphon, `that they WOULD go with\nthe lobsters to the dance.  So they got thrown out to sea.  So\nthey had to fall a long way.  So they got their tails fast in\ntheir mouths.  So they couldn't get them out again.  That's all.'\n\n  `Thank you,' said Alice, `it's very interesting.  I never knew\nso much about a whiting before.'\n\n  `I can tell you more than that, if you like,' said the\nGryphon.  `Do you know why it's called a whiting?'\n\n  `I never thought about it,' said Alice.  `Why?'\n\n  `IT DOES THE BOOTS AND SHOES.' the Gryphon replied very\nsolemnly.\n\n  Alice was thoroughly puzzled.  `Does the boots and shoes!' she\nrepeated in a wondering tone.\n\n  `Why, what are YOUR shoes done with?' said the Gryphon.  `I\nmean, what makes them so shiny?'\n\n  Alice looked down at them, and considered a little before she\ngave her answer.  `They're done with blacking, I believe.'\n\n  `Boots and shoes under the sea,' the Gryphon went on in a deep\nvoice, `are done with a whiting.  Now you know.'\n\n  `And what are they made of?' Alice asked in a tone of great\ncuriosity.\n\n  `Soles and eels, of course,' the Gryphon replied rather\nimpatiently:  `any shrimp could have told you that.'\n\n  `If I'd been the whiting,' said Alice, whose thoughts were\nstill running on the song, `I'd have said to the porpoise, \"Keep\nback, please:  we don't want YOU with us!\"'\n\n  `They were obliged to have him with them,' the Mock Turtle\nsaid:  `no wise fish would go anywhere without a porpoise.'\n\n  `Wouldn't it really?' said Alice in a tone of great surprise.\n\n  `Of course not,' said the Mock Turtle:  `why, if a fish came\nto ME, and told me he was going a journey, I should say \"With\nwhat porpoise?\"'\n\n  `Don't you mean \"purpose\"?' said Alice.\n\n  `I mean what I say,' the Mock Turtle replied in an offended\ntone.  And the Gryphon added `Come, let's hear some of YOUR\nadventures.'\n\n  `I could tell you my adventures--beginning from this morning,'\nsaid Alice a little timidly:  `but it's no use going back to\nyesterday, because I was a different person then.'\n\n  `Explain all that,' said the Mock Turtle.\n\n  `No, no!  The adventures first,' said the Gryphon in an\nimpatient tone:  `explanations take such a dreadful time.'\n\n  So Alice began telling them her adventures from the time when\nshe first saw the White Rabbit.  She was a little nervous about\nit just at first, the two creatures got so close to her, one on\neach side, and opened their eyes and mouths so VERY wide, but she\ngained courage as she went on.  Her listeners were perfectly\nquiet till she got to the part about her repeating `YOU ARE OLD,\nFATHER WILLIAM,' to the Caterpillar, and the words all coming\ndifferent, and then the Mock Turtle drew a long breath, and said\n`That's very curious.'\n\n  `It's all about as curious as it can be,' said the Gryphon.\n\n  `It all came different!' the Mock Turtle repeated\nthoughtfully.  `I should like to hear her try and repeat\nsomething now.  Tell her to begin.'  He looked at the Gryphon as\nif he thought it had some kind of authority over Alice.\n\n  `Stand up and repeat \"'TIS THE VOICE OF THE SLUGGARD,\"' said\nthe Gryphon.\n\n  `How the creatures order one about, and make one repeat\nlessons!' thought Alice; `I might as well be at school at once.'\nHowever, she got up, and began to repeat it, but her head was so\nfull of the Lobster Quadrille, that she hardly knew what she was\nsaying, and the words came very queer indeed:--\n\n    `'Tis the voice of the Lobster; I heard him declare,\n    \"You have baked me too brown, I must sugar my hair.\"\n    As a duck with its eyelids, so he with his nose\n    Trims his belt and his buttons, and turns out his toes.'\n\n              [later editions continued as follows\n    When the sands are all dry, he is gay as a lark,\n    And will talk in contemptuous tones of the Shark,\n    But, when the tide rises and sharks are around,\n    His voice has a timid and tremulous sound.]\n\n  `That's different from what I used to say when I was a child,'\nsaid the Gryphon.\n\n  `Well, I never heard it before,' said the Mock Turtle; `but it\nsounds uncommon nonsense.'\n\n  Alice said nothing; she had sat down with her face in her\nhands, wondering if anything would EVER happen in a natural way\nagain.\n\n  `I should like to have it explained,' said the Mock Turtle.\n\n  `She can't explain it,' said the Gryphon hastily.  `Go on with\nthe next verse.'\n\n  `But about his toes?' the Mock Turtle persisted.  `How COULD\nhe turn them out with his nose, you know?'\n\n  `It's the first position in dancing.' Alice said; but was\ndreadfully puzzled by the whole thing, and longed to change the\nsubject.\n\n  `Go on with the next verse,' the Gryphon repeated impatiently:\n`it begins \"I passed by his garden.\"'\n\n  Alice did not dare to disobey, though she felt sure it would\nall come wrong, and she went on in a trembling voice:--\n\n    `I passed by his garden, and marked, with one eye,\n    How the Owl and the Panther were sharing a pie--'\n\n        [later editions continued as follows\n    The Panther took pie-crust, and gravy, and meat,\n    While the Owl had the dish as its share of the treat.\n    When the pie was all finished, the Owl, as a boon,\n    Was kindly permitted to pocket the spoon:\n    While the Panther received knife and fork with a growl,\n    And concluded the banquet--]\n\n  `What IS the use of repeating all that stuff,' the Mock Turtle\ninterrupted, `if you don't explain it as you go on?  It's by far\nthe most confusing thing I ever heard!'\n\n  `Yes, I think you'd better leave off,' said the Gryphon:  and\nAlice was only too glad to do so.\n\n  `Shall we try another figure of the Lobster Quadrille?' the\nGryphon went on.  `Or would you like the Mock Turtle to sing you\na song?'\n\n  `Oh, a song, please, if the Mock Turtle would be so kind,'\nAlice replied, so eagerly that the Gryphon said, in a rather\noffended tone, `Hm!  No accounting for tastes!  Sing her\n\"Turtle Soup,\" will you, old fellow?'\n\n  The Mock Turtle sighed deeply, and began, in a voice sometimes\nchoked with sobs, to sing this:--\n\n\n    `Beautiful Soup, so rich and green,\n    Waiting in a hot tureen!\n    Who for such dainties would not stoop?\n    Soup of the evening, beautiful Soup!\n    Soup of the evening, beautiful Soup!\n        Beau--ootiful Soo--oop!\n        Beau--ootiful Soo--oop!\n    Soo--oop of the e--e--evening,\n        Beautiful, beautiful Soup!\n\n    `Beautiful Soup!  Who cares for fish,\n    Game, or any other dish?\n    Who would not give all else for two\n    Pennyworth only of beautiful Soup?\n    Pennyworth only of beautiful Soup?\n        Beau--ootiful Soo--oop!\n        Beau--ootiful Soo--oop!\n    Soo--oop of the e--e--evening,\n        Beautiful, beauti--FUL SOUP!'\n\n  `Chorus again!' cried the Gryphon, and the Mock Turtle had\njust begun to repeat it, when a cry of `The trial's beginning!'\nwas heard in the distance.\n\n  `Come on!' cried the Gryphon, and, taking Alice by the hand,\nit hurried off, without waiting for the end of the song.\n\n  `What trial is it?' Alice panted as she ran; but the Gryphon\nonly answered `Come on!' and ran the faster, while more and more\nfaintly came, carried on the breeze that followed them, the\nmelancholy words:--\n\n    `Soo--oop of the e--e--evening,\n        Beautiful, beautiful Soup!'\n\n\n\n                           CHAPTER XI\n\n                      Who Stole the Tarts?\n\n\n  The King and Queen of Hearts were seated on their throne when\nthey arrived, with a great crowd assembled about them--all sorts\nof little birds and beasts, as well as the whole pack of cards:\nthe Knave was standing before them, in chains, with a soldier on\neach side to guard him; and near the King was the White Rabbit,\nwith a trumpet in one hand, and a scroll of parchment in the\nother.  In the very middle of the court was a table, with a large\ndish of tarts upon it:  they looked so good, that it made Alice\nquite hungry to look at them--`I wish they'd get the trial done,'\nshe thought, `and hand round the refreshments!'  But there seemed\nto be no chance of this, so she began looking at everything about\nher, to pass away the time.\n\n  Alice had never been in a court of justice before, but she had\nread about them in books, and she was quite pleased to find that\nshe knew the name of nearly everything there.  `That's the\njudge,' she said to herself, `because of his great wig.'\n\n  The judge, by the way, was the King; and as he wore his crown\nover the wig, (look at the frontispiece if you want to see how he\ndid it,) he did not look at all comfortable, and it was certainly\nnot becoming.\n\n  `And that's the jury-box,' thought Alice, `and those twelve\ncreatures,' (she was obliged to say `creatures,' you see, because\nsome of them were animals, and some were birds,) `I suppose they\nare the jurors.'  She said this last word two or three times over\nto herself, being rather proud of it:  for she thought, and\nrightly too, that very few little girls of her age knew the\nmeaning of it at all.  However, `jury-men' would have done just\nas well.\n\n  The twelve jurors were all writing very busily on slates.\n`What are they doing?'  Alice whispered to the Gryphon.  `They\ncan't have anything to put down yet, before the trial's begun.'\n\n  `They're putting down their names,' the Gryphon whispered in\nreply, `for fear they should forget them before the end of the\ntrial.'\n\n  `Stupid things!' Alice began in a loud, indignant voice, but\nshe stopped hastily, for the White Rabbit cried out, `Silence in\nthe court!' and the King put on his spectacles and looked\nanxiously round, to make out who was talking.\n\n  Alice could see, as well as if she were looking over their\nshoulders, that all the jurors were writing down `stupid things!'\non their slates, and she could even make out that one of them\ndidn't know how to spell `stupid,' and that he had to ask his\nneighbour to tell him.  `A nice muddle their slates'll be in\nbefore the trial's over!' thought Alice.\n\n  One of the jurors had a pencil that squeaked.  This of course,\nAlice could not stand, and she went round the court and got\nbehind him, and very soon found an opportunity of taking it\naway.  She did it so quickly that the poor little juror (it was\nBill, the Lizard) could not make out at all what had become of\nit; so, after hunting all about for it, he was obliged to write\nwith one finger for the rest of the day; and this was of very\nlittle use, as it left no mark on the slate.\n\n  `Herald, read the accusation!' said the King.\n\n  On this the White Rabbit blew three blasts on the trumpet, and\nthen unrolled the parchment scroll, and read as follows:--\n\n    `The Queen of Hearts, she made some tarts,\n          All on a summer day:\n      The Knave of Hearts, he stole those tarts,\n          And took them quite away!'\n\n  `Consider your verdict,' the King said to the jury.\n\n  `Not yet, not yet!' the Rabbit hastily interrupted.  `There's\na great deal to come before that!'\n\n  `Call the first witness,' said the King; and the White Rabbit\nblew three blasts on the trumpet, and called out, `First\nwitness!'\n\n  The first witness was the Hatter.  He came in with a teacup in\none hand and a piece of bread-and-butter in the other.  `I beg\npardon, your Majesty,' he began, `for bringing these in:  but I\nhadn't quite finished my tea when I was sent for.'\n\n  `You ought to have finished,' said the King.  `When did you\nbegin?'\n\n  The Hatter looked at the March Hare, who had followed him into\nthe court, arm-in-arm with the Dormouse.  `Fourteenth of March, I\nthink it was,' he said.\n\n  `Fifteenth,' said the March Hare.\n\n  `Sixteenth,' added the Dormouse.\n\n  `Write that down,' the King said to the jury, and the jury\neagerly wrote down all three dates on their slates, and then\nadded them up, and reduced the answer to shillings and pence.\n\n  `Take off your hat,' the King said to the Hatter.\n\n  `It isn't mine,' said the Hatter.\n\n  `Stolen!' the King exclaimed, turning to the jury, who\ninstantly made a memorandum of the fact.\n\n  `I keep them to sell,' the Hatter added as an explanation;\n`I've none of my own.  I'm a hatter.'\n\n  Here the Queen put on her spectacles, and began staring at the\nHatter, who turned pale and fidgeted.\n\n  `Give your evidence,' said the King; `and don't be nervous, or\nI'll have you executed on the spot.'\n\n  This did not seem to encourage the witness at all:  he kept\nshifting from one foot to the other, looking uneasily at the\nQueen, and in his confusion he bit a large piece out of his\nteacup instead of the bread-and-butter.\n\n  Just at this moment Alice felt a very curious sensation, which\npuzzled her a good deal until she made out what it was:  she was\nbeginning to grow larger again, and she thought at first she\nwould get up and leave the court; but on second thoughts she\ndecided to remain where she was as long as there was room for\nher.\n\n  `I wish you wouldn't squeeze so.' said the Dormouse, who was\nsitting next to her.  `I can hardly breathe.'\n\n  `I can't help it,' said Alice very meekly:  `I'm growing.'\n\n  `You've no right to grow here,' said the Dormouse.\n\n  `Don't talk nonsense,' said Alice more boldly:  `you know\nyou're growing too.'\n\n  `Yes, but I grow at a reasonable pace,' said the Dormouse:\n`not in that ridiculous fashion.'  And he got up very sulkily\nand crossed over to the other side of the court.\n\n  All this time the Queen had never left off staring at the\nHatter, and, just as the Dormouse crossed the court, she said to\none of the officers of the court, `Bring me the list of the\nsingers in the last concert!' on which the wretched Hatter\ntrembled so, that he shook both his shoes off.\n\n  `Give your evidence,' the King repeated angrily, `or I'll have\nyou executed, whether you're nervous or not.'\n\n  `I'm a poor man, your Majesty,' the Hatter began, in a\ntrembling voice, `--and I hadn't begun my tea--not above a week\nor so--and what with the bread-and-butter getting so thin--and\nthe twinkling of the tea--'\n\n  `The twinkling of the what?' said the King.\n\n  `It began with the tea,' the Hatter replied.\n\n  `Of course twinkling begins with a T!' said the King sharply.\n`Do you take me for a dunce?  Go on!'\n\n  `I'm a poor man,' the Hatter went on, `and most things\ntwinkled after that--only the March Hare said--'\n\n  `I didn't!' the March Hare interrupted in a great hurry.\n\n  `You did!' said the Hatter.\n\n  `I deny it!' said the March Hare.\n\n  `He denies it,' said the King:  `leave out that part.'\n\n  `Well, at any rate, the Dormouse said--' the Hatter went on,\nlooking anxiously round to see if he would deny it too:  but the\nDormouse denied nothing, being fast asleep.\n\n  `After that,' continued the Hatter, `I cut some more bread-\nand-butter--'\n\n  `But what did the Dormouse say?' one of the jury asked.\n\n  `That I can't remember,' said the Hatter.\n\n  `You MUST remember,' remarked the King, `or I'll have you\nexecuted.'\n\n  The miserable Hatter dropped his teacup and bread-and-butter,\nand went down on one knee.  `I'm a poor man, your Majesty,' he\nbegan.\n\n  `You're a very poor speaker,' said the King.\n\n  Here one of the guinea-pigs cheered, and was immediately\nsuppressed by the officers of the court.  (As that is rather a\nhard word, I will just explain to you how it was done.  They had\na large canvas bag, which tied up at the mouth with strings:\ninto this they slipped the guinea-pig, head first, and then sat\nupon it.)\n\n  `I'm glad I've seen that done,' thought Alice.  `I've so often\nread in the newspapers, at the end of trials, \"There was some\nattempts at applause, which was immediately suppressed by the\nofficers of the court,\" and I never understood what it meant\ntill now.'\n\n  `If that's all you know about it, you may stand down,'\ncontinued the King.\n\n  `I can't go no lower,' said the Hatter:  `I'm on the floor, as\nit is.'\n\n  `Then you may SIT down,' the King replied.\n\n  Here the other guinea-pig cheered, and was suppressed.\n\n  `Come, that finished the guinea-pigs!' thought Alice.  `Now we\nshall get on better.'\n\n  `I'd rather finish my tea,' said the Hatter, with an anxious\nlook at the Queen, who was reading the list of singers.\n\n  `You may go,' said the King, and the Hatter hurriedly left the\ncourt, without even waiting to put his shoes on.\n\n  `--and just take his head off outside,' the Queen added to one\nof the officers:  but the Hatter was out of sight before the\nofficer could get to the door.\n\n  `Call the next witness!' said the King.\n\n  The next witness was the Duchess's cook.  She carried the\npepper-box in her hand, and Alice guessed who it was, even before\nshe got into the court, by the way the people near the door began\nsneezing all at once.\n\n  `Give your evidence,' said the King.\n\n  `Shan't,' said the cook.\n\n  The King looked anxiously at the White Rabbit, who said in a\nlow voice, `Your Majesty must cross-examine THIS witness.'\n\n  `Well, if I must, I must,' the King said, with a melancholy\nair, and, after folding his arms and frowning at the cook till\nhis eyes were nearly out of sight, he said in a deep voice, `What\nare tarts made of?'\n\n  `Pepper, mostly,' said the cook.\n\n  `Treacle,' said a sleepy voice behind her.\n\n  `Collar that Dormouse,' the Queen shrieked out.  `Behead that\nDormouse!  Turn that Dormouse out of court!  Suppress him!  Pinch\nhim!  Off with his whiskers!'\n\n  For some minutes the whole court was in confusion, getting the\nDormouse turned out, and, by the time they had settled down\nagain, the cook had disappeared.\n\n  `Never mind!' said the King, with an air of great relief.\n`Call the next witness.'  And he added in an undertone to the\nQueen, `Really, my dear, YOU must cross-examine the next witness.\nIt quite makes my forehead ache!'\n\n  Alice watched the White Rabbit as he fumbled over the list,\nfeeling very curious to see what the next witness would be like,\n`--for they haven't got much evidence YET,' she said to herself.\nImagine her surprise, when the White Rabbit read out, at the top\nof his shrill little voice, the name `Alice!'\n\n\n\n                           CHAPTER XII\n\n                        Alice's Evidence\n\n\n  `Here!' cried Alice, quite forgetting in the flurry of the\nmoment how large she had grown in the last few minutes, and she\njumped up in such a hurry that she tipped over the jury-box with\nthe edge of her skirt, upsetting all the jurymen on to the heads\nof the crowd below, and there they lay sprawling about, reminding\nher very much of a globe of goldfish she had accidentally upset\nthe week before.\n\n  `Oh, I BEG your pardon!' she exclaimed in a tone of great\ndismay, and began picking them up again as quickly as she could,\nfor the accident of the goldfish kept running in her head, and\nshe had a vague sort of idea that they must be collected at once\nand put back into the jury-box, or they would die.\n\n  `The trial cannot proceed,' said the King in a very grave\nvoice, `until all the jurymen are back in their proper places--\nALL,' he repeated with great emphasis, looking hard at Alice as\nhe said do.\n\n  Alice looked at the jury-box, and saw that, in her haste, she\nhad put the Lizard in head downwards, and the poor little thing\nwas waving its tail about in a melancholy way, being quite unable\nto move.  She soon got it out again, and put it right; `not that\nit signifies much,' she said to herself; `I should think it\nwould be QUITE as much use in the trial one way up as the other.'\n\n  As soon as the jury had a little recovered from the shock of\nbeing upset, and their slates and pencils had been found and\nhanded back to them, they set to work very diligently to write\nout a history of the accident, all except the Lizard, who seemed\ntoo much overcome to do anything but sit with its mouth open,\ngazing up into the roof of the court.\n\n  `What do you know about this business?' the King said to\nAlice.\n\n  `Nothing,' said Alice.\n\n  `Nothing WHATEVER?' persisted the King.\n\n  `Nothing whatever,' said Alice.\n\n  `That's very important,' the King said, turning to the jury.\nThey were just beginning to write this down on their slates, when\nthe White Rabbit interrupted:  `UNimportant, your Majesty means,\nof course,' he said in a very respectful tone, but frowning and\nmaking faces at him as he spoke.\n\n  `UNimportant, of course, I meant,' the King hastily said, and\nwent on to himself in an undertone, `important--unimportant--\nunimportant--important--' as if he were trying which word\nsounded best.\n\n  Some of the jury wrote it down `important,' and some\n`unimportant.'  Alice could see this, as she was near enough to\nlook over their slates; `but it doesn't matter a bit,' she\nthought to herself.\n\n  At this moment the King, who had been for some time busily\nwriting in his note-book, cackled out `Silence!' and read out\nfrom his book, `Rule Forty-two.  ALL PERSONS MORE THAN A MILE\nHIGH TO LEAVE THE COURT.'\n\n  Everybody looked at Alice.\n\n  `I'M not a mile high,' said Alice.\n\n  `You are,' said the King.\n\n  `Nearly two miles high,' added the Queen.\n\n  `Well, I shan't go, at any rate,' said Alice:  `besides,\nthat's not a regular rule:  you invented it just now.'\n\n  `It's the oldest rule in the book,' said the King.\n\n  `Then it ought to be Number One,' said Alice.\n\n  The King turned pale, and shut his note-book hastily.\n`Consider your verdict,' he said to the jury, in a low, trembling\nvoice.\n\n  `There's more evidence to come yet, please your Majesty,' said\nthe White Rabbit, jumping up in a great hurry; `this paper has\njust been picked up.'\n\n  `What's in it?' said the Queen.\n\n  `I haven't opened it yet,' said the White Rabbit, `but it seems\nto be a letter, written by the prisoner to--to somebody.'\n\n  `It must have been that,' said the King, `unless it was\nwritten to nobody, which isn't usual, you know.'\n\n  `Who is it directed to?' said one of the jurymen.\n\n  `It isn't directed at all,' said the White Rabbit; `in fact,\nthere's nothing written on the OUTSIDE.'  He unfolded the paper\nas he spoke, and added `It isn't a letter, after all:  it's a set\nof verses.'\n\n  `Are they in the prisoner's handwriting?' asked another of\nthe jurymen.\n\n  `No, they're not,' said the White Rabbit, `and that's the\nqueerest thing about it.'  (The jury all looked puzzled.)\n\n  `He must have imitated somebody else's hand,' said the King.\n(The jury all brightened up again.)\n\n  `Please your Majesty,' said the Knave, `I didn't write it, and\nthey can't prove I did:  there's no name signed at the end.'\n\n  `If you didn't sign it,' said the King, `that only makes the\nmatter worse.  You MUST have meant some mischief, or else you'd\nhave signed your name like an honest man.'\n\n  There was a general clapping of hands at this:  it was the\nfirst really clever thing the King had said that day.\n\n  `That PROVES his guilt,' said the Queen.\n\n  `It proves nothing of the sort!' said Alice.  `Why, you don't\neven know what they're about!'\n\n  `Read them,' said the King.\n\n  The White Rabbit put on his spectacles.  `Where shall I begin,\nplease your Majesty?' he asked.\n\n  `Begin at the beginning,' the King said gravely, `and go on\ntill you come to the end:  then stop.'\n\n  These were the verses the White Rabbit read:--\n\n        `They told me you had been to her,\n          And mentioned me to him:\n        She gave me a good character,\n          But said I could not swim.\n\n        He sent them word I had not gone\n          (We know it to be true):\n        If she should push the matter on,\n          What would become of you?\n\n        I gave her one, they gave him two,\n          You gave us three or more;\n        They all returned from him to you,\n          Though they were mine before.\n\n        If I or she should chance to be\n          Involved in this affair,\n        He trusts to you to set them free,\n          Exactly as we were.\n\n        My notion was that you had been\n          (Before she had this fit)\n        An obstacle that came between\n          Him, and ourselves, and it.\n\n        Don't let him know she liked them best,\n          For this must ever be\n        A secret, kept from all the rest,\n          Between yourself and me.'\n\n  `That's the most important piece of evidence we've heard yet,'\nsaid the King, rubbing his hands; `so now let the jury--'\n\n  `If any one of them can explain it,' said Alice, (she had\ngrown so large in the last few minutes that she wasn't a bit\nafraid of interrupting him,) `I'll give him sixpence.  _I_ don't\nbelieve there's an atom of meaning in it.'\n\n  The jury all wrote down on their slates, `SHE doesn't believe\nthere's an atom of meaning in it,' but none of them attempted to\nexplain the paper.\n\n  `If there's no meaning in it,' said the King, `that saves a\nworld of trouble, you know, as we needn't try to find any.  And\nyet I don't know,' he went on, spreading out the verses on his\nknee, and looking at them with one eye; `I seem to see some\nmeaning in them, after all.  \"--SAID I COULD NOT SWIM--\" you\ncan't swim, can you?' he added, turning to the Knave.\n\n  The Knave shook his head sadly.  `Do I look like it?' he said.\n(Which he certainly did NOT, being made entirely of cardboard.)\n\n  `All right, so far,' said the King, and he went on muttering\nover the verses to himself:  `\"WE KNOW IT TO BE TRUE--\" that's\nthe jury, of course-- \"I GAVE HER ONE, THEY GAVE HIM TWO--\" why,\nthat must be what he did with the tarts, you know--'\n\n  `But, it goes on \"THEY ALL RETURNED FROM HIM TO YOU,\"' said\nAlice.\n\n  `Why, there they are!' said the King triumphantly, pointing to\nthe tarts on the table.  `Nothing can be clearer than THAT.\nThen again--\"BEFORE SHE HAD THIS FIT--\"  you never had fits, my\ndear, I think?' he said to the Queen.\n\n  `Never!' said the Queen furiously, throwing an inkstand at the\nLizard as she spoke.  (The unfortunate little Bill had left off\nwriting on his slate with one finger, as he found it made no\nmark; but he now hastily began again, using the ink, that was\ntrickling down his face, as long as it lasted.)\n\n  `Then the words don't FIT you,' said the King, looking round\nthe court with a smile.  There was a dead silence.\n\n  `It's a pun!' the King added in an offended tone, and\neverybody laughed, `Let the jury consider their verdict,' the\nKing said, for about the twentieth time that day.\n\n  `No, no!' said the Queen.  `Sentence first--verdict afterwards.'\n\n  `Stuff and nonsense!' said Alice loudly.  `The idea of having\nthe sentence first!'\n\n  `Hold your tongue!' said the Queen, turning purple.\n\n  `I won't!' said Alice.\n\n  `Off with her head!' the Queen shouted at the top of her voice.\nNobody moved.\n\n  `Who cares for you?' said Alice, (she had grown to her full\nsize by this time.)  `You're nothing but a pack of cards!'\n\n  At this the whole pack rose up into the air, and came flying\ndown upon her:  she gave a little scream, half of fright and half\nof anger, and tried to beat them off, and found herself lying on\nthe bank, with her head in the lap of her sister, who was gently\nbrushing away some dead leaves that had fluttered down from the\ntrees upon her face.\n\n  `Wake up, Alice dear!' said her sister; `Why, what a long\nsleep you've had!'\n\n  `Oh, I've had such a curious dream!' said Alice, and she told\nher sister, as well as she could remember them, all these strange\nAdventures of hers that you have just been reading about; and\nwhen she had finished, her sister kissed her, and said, `It WAS a\ncurious dream, dear, certainly:  but now run in to your tea; it's\ngetting late.'  So Alice got up and ran off, thinking while she\nran, as well she might, what a wonderful dream it had been.\n\n  But her sister sat still just as she left her, leaning her\nhead on her hand, watching the setting sun, and thinking of\nlittle Alice and all her wonderful Adventures, till she too began\ndreaming after a fashion, and this was her dream:--\n\n  First, she dreamed of little Alice herself, and once again the\ntiny hands were clasped upon her knee, and the bright eager eyes\nwere looking up into hers--she could hear the very tones of her\nvoice, and see that queer little toss of her head to keep back\nthe wandering hair that WOULD always get into her eyes--and\nstill as she listened, or seemed to listen, the whole place\naround her became alive the strange creatures of her little\nsister's dream.\n\n  The long grass rustled at her feet as the White Rabbit hurried\nby--the frightened Mouse splashed his way through the\nneighbouring pool--she could hear the rattle of the teacups as\nthe March Hare and his friends shared their never-ending meal,\nand the shrill voice of the Queen ordering off her unfortunate\nguests to execution--once more the pig-baby was sneezing on the\nDuchess's knee, while plates and dishes crashed around it--once\nmore the shriek of the Gryphon, the squeaking of the Lizard's\nslate-pencil, and the choking of the suppressed guinea-pigs,\nfilled the air, mixed up with the distant sobs of the miserable\nMock Turtle.\n\n  So she sat on, with closed eyes, and half believed herself in\nWonderland, though she knew she had but to open them again, and\nall would change to dull reality--the grass would be only\nrustling in the wind, and the pool rippling to the waving of the\nreeds--the rattling teacups would change to tinkling sheep-\nbells, and the Queen's shrill cries to the voice of the shepherd\nboy--and the sneeze of the baby, the shriek of the Gryphon, and\nall the other queer noises, would change (she knew) to the\nconfused clamour of the busy farm-yard--while the lowing of the\ncattle in the distance would take the place of the Mock Turtle's\nheavy sobs.\n\n  Lastly, she pictured to herself how this same little sister of\nhers would, in the after-time, be herself a grown woman; and how\nshe would keep, through all her riper years, the simple and\nloving heart of her childhood:  and how she would gather about\nher other little children, and make THEIR eyes bright and eager\nwith many a strange tale, perhaps even with the dream of\nWonderland of long ago:  and how she would feel with all their\nsimple sorrows, and find a pleasure in all their simple joys,\nremembering her own child-life, and the happy summer days.\n\n                             THE END"
  },
  {
    "path": "build/lib/shirah_reader/__init__.py",
    "content": "# __init__.py\n\n# Version of the shirah-reader package\n__version__ = \"1.0.0\"\n"
  },
  {
    "path": "build/lib/shirah_reader/__main__.py",
    "content": "#!/usr/bin/env python3\n\n\"\"\"\n Copyright (C) 2020\n - Original author Benawi Adha\n - Modified by Advait Raykar (github.com/hallicopter)\n\n This program is free software; you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation; either version 2 of the License, or\n (at your option) any later version.\n\n This program is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with this program; if not, write to the Free Software\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA\n\nUsages:\n    shirah             read last epub\n    shirah EPUBFILE    read EPUBFILE\n    shirah STRINGS     read matched STRINGS from history\n    shirah NUMBER      read file from history\n                       with associated NUMBER\n\nOptions:\n    -r              print reading history\n    -d              dump epub\n    -h, --help      print short, long help\n\"\"\"\n\n\nimport base64\nimport curses\nimport zipfile\nimport sys\nimport re\nimport os\nimport textwrap\nimport json\nimport tempfile\nimport shutil\nimport subprocess\nimport multiprocessing\nimport xml.etree.ElementTree as ET\nfrom urllib.parse import unquote\nfrom html import unescape\nfrom html.parser import HTMLParser\nfrom difflib import SequenceMatcher as SM\nfrom functools import wraps\nimport time\nimport syllables\n\ntry:\n    import mobi\n\n    MOBISUPPORT = True\nexcept ModuleNotFoundError:\n    MOBISUPPORT = False\n\n# -1 is default terminal fg/bg colors\nCFG = {\n    \"DefaultViewer\": \"auto\",\n    \"DictionaryClient\": \"auto\",\n    \"ShowProgressIndicator\": True,\n    \"DarkColorFG\": 252,\n    \"DarkColorBG\": 235,\n    \"LightColorFG\": 238,\n    \"LightColorBG\": 253,\n    \"Keys\": {\n        \"ScrollUp\": \"k\",\n        \"ScrollDown\": \"j\",\n        \"PageUp\": \"h\",\n        \"PageDown\": \"l\",\n        \"HalfScreenUp\": \"^u\",\n        \"HalfScreenDown\": \"C-d\",\n        \"NextChapter\": \"n\",\n        \"PrevChapter\": \"p\",\n        \"BeginningOfCh\": \"g\",\n        \"EndOfCh\": \"G\",\n        \"Shrink\": \"-\",\n        \"Enlarge\": \"+\",\n        \"SetWidth\": \"=\",\n        \"Metadata\": \"M\",\n        \"DefineWord\": \"d\",\n        \"TableOfContents\": \"t\",\n        \"Follow\": \"f\",\n        \"OpenImage\": \"o\",\n        \"RegexSearch\": \"/\",\n        \"ShowHideProgress\": \"s\",\n        \"MarkPosition\": \"m\",\n        \"JumpToPosition\": \"`\",\n        \"AddBookmark\": \"b\",\n        \"ShowBookmarks\": \"B\",\n        \"Quit\": \"q\",\n        \"Help\": \"?\",\n        \"SwitchColor\": \"c\",\n    },\n}\nSTATE = {\"LastRead\": \"\", \"States\": {}}\n# default keys\nK = {\n    \"ScrollUp\": {curses.KEY_UP},\n    \"ScrollDown\": {curses.KEY_DOWN},\n    \"PageUp\": {curses.KEY_PPAGE, curses.KEY_LEFT},\n    \"PageDown\": {curses.KEY_NPAGE, ord(\" \"), curses.KEY_RIGHT},\n    \"BeginningOfCh\": {curses.KEY_HOME},\n    \"EndOfCh\": {curses.KEY_END},\n    \"TableOfContents\": {9, ord(\"\\t\")},\n    \"Follow\": {10},\n    \"Quit\": {3, 27, 304},\n    \"RSVP\": {ord(\"r\")},\n}\nWINKEYS = set()\nCFGFILE = \"\"\nSTATEFILE = \"\"\nCOLORSUPPORT = False\nLINEPRSRV = 0  # 2\nSEARCHPATTERN = None\nVWR = None\nDICT = None\nSCREEN = None\nJUMPLIST = {}\nSHOWPROGRESS = CFG[\"ShowProgressIndicator\"]\nMULTIPROC = False if multiprocessing.cpu_count() == 1 else True\nALLPREVLETTERS = []\nSUMALLLETTERS = 0\nPROC_COUNTLETTERS = None\nwpm = 300\n\n\nclass Epub:\n    NS = {\n        \"DAISY\": \"http://www.daisy.org/z3986/2005/ncx/\",\n        \"OPF\": \"http://www.idpf.org/2007/opf\",\n        \"CONT\": \"urn:oasis:names:tc:opendocument:xmlns:container\",\n        \"XHTML\": \"http://www.w3.org/1999/xhtml\",\n        \"EPUB\": \"http://www.idpf.org/2007/ops\",\n    }\n\n    def __init__(self, fileepub):\n        self.path = os.path.abspath(fileepub)\n        self.file = zipfile.ZipFile(fileepub, \"r\")\n\n    def get_meta(self):\n        meta = []\n        # why self.file.read(self.rootfile) problematic\n        cont = ET.fromstring(self.file.open(self.rootfile).read())\n        for i in cont.findall(\"OPF:metadata/*\", self.NS):\n            if i.text is not None:\n                meta.append([re.sub(\"{.*?}\", \"\", i.tag), i.text])\n        return meta\n\n    def initialize(self):\n        cont = ET.parse(self.file.open(\"META-INF/container.xml\"))\n        self.rootfile = cont.find(\"CONT:rootfiles/CONT:rootfile\", self.NS).attrib[\n            \"full-path\"\n        ]\n        self.rootdir = (\n            os.path.dirname(self.rootfile) + \"/\"\n            if os.path.dirname(self.rootfile) != \"\"\n            else \"\"\n        )\n        cont = ET.parse(self.file.open(self.rootfile))\n        # EPUB3\n        self.version = cont.getroot().get(\"version\")\n        if self.version == \"2.0\":\n            # \"OPF:manifest/*[@id='ncx']\"\n            self.toc = self.rootdir + cont.find(\n                \"OPF:manifest/*[@media-type='application/x-dtbncx+xml']\", self.NS\n            ).get(\"href\")\n        elif self.version == \"3.0\":\n            self.toc = self.rootdir + cont.find(\n                \"OPF:manifest/*[@properties='nav']\", self.NS\n            ).get(\"href\")\n\n        self.contents = []\n        self.toc_entries = [[], [], []]\n\n        # cont = ET.parse(self.file.open(self.rootfile)).getroot()\n        manifest = []\n        for i in cont.findall(\"OPF:manifest/*\", self.NS):\n            # EPUB3\n            # if i.get(\"id\") != \"ncx\" and i.get(\"properties\") != \"nav\":\n            if (\n                i.get(\"media-type\") != \"application/x-dtbncx+xml\"\n                and i.get(\"properties\") != \"nav\"\n            ):\n                manifest.append([i.get(\"id\"), i.get(\"href\")])\n\n        spine, contents = [], []\n        for i in cont.findall(\"OPF:spine/*\", self.NS):\n            spine.append(i.get(\"idref\"))\n        for i in spine:\n            for j in manifest:\n                if i == j[0]:\n                    self.contents.append(self.rootdir + unquote(j[1]))\n                    contents.append(unquote(j[1]))\n                    manifest.remove(j)\n                    # TODO: test is break necessary\n                    break\n\n        toc = ET.parse(self.file.open(self.toc)).getroot()\n        # EPUB3\n        if self.version == \"2.0\":\n            navPoints = toc.findall(\"DAISY:navMap//DAISY:navPoint\", self.NS)\n        elif self.version == \"3.0\":\n            navPoints = toc.findall(\n                \"XHTML:body//XHTML:nav[@EPUB:type='toc']//XHTML:a\", self.NS\n            )\n        for i in navPoints:\n            if self.version == \"2.0\":\n                src = i.find(\"DAISY:content\", self.NS).get(\"src\")\n                name = i.find(\"DAISY:navLabel/DAISY:text\", self.NS).text\n            elif self.version == \"3.0\":\n                src = i.get(\"href\")\n                name = \"\".join(list(i.itertext()))\n            src = src.split(\"#\")\n            try:\n                idx = contents.index(unquote(src[0]))\n            except ValueError:\n                continue\n            self.toc_entries[0].append(name)\n            self.toc_entries[1].append(idx)\n            if len(src) == 2:\n                self.toc_entries[2].append(src[1])\n            elif len(src) == 1:\n                self.toc_entries[2].append(\"\")\n\n    def get_raw_text(self, chpath):\n        # using try-except block to catch\n        # zlib.error: Error -3 while decompressing data: invalid distance too far back\n        # caused by forking PROC_COUNTLETTERS\n        while True:\n            try:\n                content = self.file.open(chpath).read()\n                break\n            except:\n                continue\n        return content.decode(\"utf-8\")\n\n    def get_img_bytestr(self, impath):\n        return impath, self.file.read(impath)\n\n    def cleanup(self):\n        return\n\n\nclass Mobi(Epub):\n    def __init__(self, filemobi):\n        self.path = os.path.abspath(filemobi)\n        self.file, _ = mobi.extract(filemobi)\n\n    def get_meta(self):\n        meta = []\n        # why self.file.read(self.rootfile) problematic\n        with open(os.path.join(self.rootdir, \"content.opf\")) as f:\n            cont = ET.parse(f).getroot()\n        for i in cont.findall(\"OPF:metadata/*\", self.NS):\n            if i.text is not None:\n                meta.append([re.sub(\"{.*?}\", \"\", i.tag), i.text])\n        return meta\n\n    def initialize(self):\n        self.rootdir = os.path.join(self.file, \"mobi7\")\n        self.toc = os.path.join(self.rootdir, \"toc.ncx\")\n        self.version = \"2.0\"\n\n        self.contents = []\n        self.toc_entries = [[], [], []]\n\n        with open(os.path.join(self.rootdir, \"content.opf\")) as f:\n            cont = ET.parse(f).getroot()\n        manifest = []\n        for i in cont.findall(\"OPF:manifest/*\", self.NS):\n            # EPUB3\n            # if i.get(\"id\") != \"ncx\" and i.get(\"properties\") != \"nav\":\n            if (\n                i.get(\"media-type\") != \"application/x-dtbncx+xml\"\n                and i.get(\"properties\") != \"nav\"\n            ):\n                manifest.append([i.get(\"id\"), i.get(\"href\")])\n\n        spine, contents = [], []\n        for i in cont.findall(\"OPF:spine/*\", self.NS):\n            spine.append(i.get(\"idref\"))\n        for i in spine:\n            for j in manifest:\n                if i == j[0]:\n                    self.contents.append(os.path.join(self.rootdir, unquote(j[1])))\n                    contents.append(unquote(j[1]))\n                    manifest.remove(j)\n                    # TODO: test is break necessary\n                    break\n\n        with open(self.toc) as f:\n            toc = ET.parse(f).getroot()\n        # EPUB3\n        if self.version == \"2.0\":\n            navPoints = toc.findall(\"DAISY:navMap//DAISY:navPoint\", self.NS)\n        elif self.version == \"3.0\":\n            navPoints = toc.findall(\n                \"XHTML:body//XHTML:nav[@EPUB:type='toc']//XHTML:a\", self.NS\n            )\n        for i in navPoints:\n            if self.version == \"2.0\":\n                src = i.find(\"DAISY:content\", self.NS).get(\"src\")\n                name = i.find(\"DAISY:navLabel/DAISY:text\", self.NS).text\n            elif self.version == \"3.0\":\n                src = i.get(\"href\")\n                name = \"\".join(list(i.itertext()))\n            src = src.split(\"#\")\n            try:\n                idx = contents.index(unquote(src[0]))\n            except ValueError:\n                continue\n            self.toc_entries[0].append(name)\n            self.toc_entries[1].append(idx)\n            if len(src) == 2:\n                self.toc_entries[2].append(src[1])\n            elif len(src) == 1:\n                self.toc_entries[2].append(\"\")\n\n    def get_raw_text(self, chpath):\n        # using try-except block to catch\n        # zlib.error: Error -3 while decompressing data: invalid distance too far back\n        # caused by forking PROC_COUNTLETTERS\n        while True:\n            try:\n                with open(chpath) as f:\n                    content = f.read()\n                break\n            except:\n                continue\n        # return content.decode(\"utf-8\")\n        return content\n\n    def cleanup(self):\n        shutil.rmtree(self.file)\n        return\n\n\nclass Azw3(Epub):\n    def __init__(self, fileepub):\n        self.path = os.path.abspath(fileepub)\n        self.tmpdir, self.tmpepub = mobi.extract(fileepub)\n        self.file = zipfile.ZipFile(self.tmpepub, \"r\")\n\n    def cleanup(self):\n        shutil.rmtree(self.tmpdir)\n        return\n\n\nclass FictionBook:\n    NS = {\"FB2\": \"http://www.gribuser.ru/xml/fictionbook/2.0\"}\n\n    def __init__(self, filefb):\n        self.path = os.path.abspath(filefb)\n        self.file = filefb\n\n    def get_meta(self):\n        desc = self.root.find(\"FB2:description\", self.NS)\n        alltags = desc.findall(\"*/*\")\n        return [[re.sub(\"{.*?}\", \"\", i.tag), \" \".join(i.itertext())] for i in alltags]\n\n    def initialize(self):\n        cont = ET.parse(self.file)\n        self.root = cont.getroot()\n\n        self.contents = []\n        self.toc_entries = [[], [], []]\n\n        self.contents = self.root.findall(\"FB2:body/*\", self.NS)\n        # TODO\n        for n, i in enumerate(self.contents):\n            title = i.find(\"FB2:title\", self.NS)\n            if title is not None:\n                self.toc_entries[0].append(\"\".join(title.itertext()))\n                self.toc_entries[1].append(n)\n                self.toc_entries[2].append(\"\")\n\n    def get_raw_text(self, node):\n        ET.register_namespace(\"\", \"http://www.gribuser.ru/xml/fictionbook/2.0\")\n        # sys.exit(ET.tostring(node, encoding=\"utf8\", method=\"html\").decode(\"utf-8\").replace(\"ns1:\",\"\"))\n        return (\n            ET.tostring(node, encoding=\"utf8\", method=\"html\")\n            .decode(\"utf-8\")\n            .replace(\"ns1:\", \"\")\n        )\n\n    def get_img_bytestr(self, imgid):\n        imgid = imgid.replace(\"#\", \"\")\n        img = self.root.find(\"*[@id='{}']\".format(imgid))\n        imgtype = img.get(\"content-type\").split(\"/\")[1]\n        return imgid + \".\" + imgtype, base64.b64decode(img.text)\n\n    def cleanup(self):\n        return\n\n\nclass HTMLtoLines(HTMLParser):\n    para = {\"p\", \"div\"}\n    inde = {\"q\", \"dt\", \"dd\", \"blockquote\"}\n    pref = {\"pre\"}\n    bull = {\"li\"}\n    hide = {\"script\", \"style\", \"head\"}\n\n    # hide = {\"script\", \"style\", \"head\", \", \"sub}\n\n    def __init__(self, sects={\"\"}):\n        HTMLParser.__init__(self)\n        self.text = [\"\"]\n        self.imgs = []\n        self.ishead = False\n        self.isinde = False\n        self.isbull = False\n        self.ispref = False\n        self.ishidden = False\n        self.idhead = set()\n        self.idinde = set()\n        self.idbull = set()\n        self.idpref = set()\n        self.sects = sects\n\n    def handle_starttag(self, tag, attrs):\n        if re.match(\"h[1-6]\", tag) is not None:\n            self.ishead = True\n        elif tag in self.inde:\n            self.isinde = True\n        elif tag in self.pref:\n            self.ispref = True\n        elif tag in self.bull:\n            self.isbull = True\n        elif tag in self.hide:\n            self.ishidden = True\n        elif tag == \"sup\":\n            self.text[-1] += \"^{\"\n        elif tag == \"sub\":\n            self.text[-1] += \"_{\"\n        elif tag == \"image\":\n            for i in attrs:\n                # if i[0] == \"xlink:href\":\n                if i[0].endswith(\"href\"):\n                    self.text.append(\"[IMG:{}]\".format(len(self.imgs)))\n                    self.imgs.append(unquote(i[1]))\n        if self.sects != {\"\"}:\n            for i in attrs:\n                if i[0] == \"id\" and i[1] in self.sects:\n                    self.text[-1] += \" (#\" + i[1] + \") \"\n\n    def handle_startendtag(self, tag, attrs):\n        if tag == \"br\":\n            self.text += [\"\"]\n        elif tag in {\"img\", \"image\"}:\n            for i in attrs:\n                if (tag == \"img\" and i[0] == \"src\") or (\n                    tag == \"image\" and i[0] == \"xlink:href\"\n                ):\n                    self.text.append(\"[IMG:{}]\".format(len(self.imgs)))\n                    self.imgs.append(unquote(i[1]))\n                    self.text.append(\"\")\n        # sometimes attribute \"id\" is inside \"startendtag\"\n        # especially html from mobi module (kindleunpack fork)\n        if self.sects != {\"\"}:\n            for i in attrs:\n                if i[0] == \"id\" and i[1] in self.sects:\n                    self.text[-1] += \" (#\" + i[1] + \") \"\n\n    def handle_endtag(self, tag):\n        if re.match(\"h[1-6]\", tag) is not None:\n            self.text.append(\"\")\n            self.text.append(\"\")\n            self.ishead = False\n        elif tag in self.para:\n            self.text.append(\"\")\n        elif tag in self.hide:\n            self.ishidden = False\n        elif tag in self.inde:\n            if self.text[-1] != \"\":\n                self.text.append(\"\")\n            self.isinde = False\n        elif tag in self.pref:\n            if self.text[-1] != \"\":\n                self.text.append(\"\")\n            self.ispref = False\n        elif tag in self.bull:\n            if self.text[-1] != \"\":\n                self.text.append(\"\")\n            self.isbull = False\n        elif tag in {\"sub\", \"sup\"}:\n            self.text[-1] += \"}\"\n        elif tag == \"image\":\n            self.text.append(\"\")\n\n    def handle_data(self, raw):\n        if raw and not self.ishidden:\n            if self.text[-1] == \"\":\n                tmp = raw.lstrip()\n            else:\n                tmp = raw\n            if self.ispref:\n                line = unescape(tmp)\n            else:\n                line = unescape(re.sub(r\"\\s+\", \" \", tmp))\n            self.text[-1] += line\n            if self.ishead:\n                self.idhead.add(len(self.text) - 1)\n            elif self.isbull:\n                self.idbull.add(len(self.text) - 1)\n            elif self.isinde:\n                self.idinde.add(len(self.text) - 1)\n            elif self.ispref:\n                self.idpref.add(len(self.text) - 1)\n\n    def get_lines(self, width=0):\n        text, sect = [], {}\n        if width == 0:\n            return self.text\n        for n, i in enumerate(self.text):\n            findsect = re.search(r\"(?<= \\(#).*?(?=\\) )\", i)\n            if findsect is not None and findsect.group() in self.sects:\n                i = i.replace(\" (#\" + findsect.group() + \") \", \"\")\n                sect[findsect.group()] = len(text)\n            if n in self.idhead:\n                text += [i.rjust(width // 2 + len(i) // 2)] + [\"\"]\n            elif n in self.idinde:\n                text += [\"   \" + j for j in textwrap.wrap(i, width - 3)] + [\"\"]\n            elif n in self.idbull:\n                tmp = textwrap.wrap(i, width - 3)\n                text += [\" - \" + j if j == tmp[0] else \"   \" + j for j in tmp] + [\"\"]\n            elif n in self.idpref:\n                tmp = i.splitlines()\n                wraptmp = []\n                for line in tmp:\n                    wraptmp += [j for j in textwrap.wrap(line, width - 6)]\n                text += [\"   \" + j for j in wraptmp] + [\"\"]\n            else:\n                text += textwrap.wrap(i, width) + [\"\"]\n        return text, self.imgs, sect\n\n\nclass Board:\n    MAXCHUNKS = 32000  # lines\n\n    def __init__(self, totlines, width):\n        self.chunks = [\n            self.MAXCHUNKS * (i + 1) - 1 for i in range(totlines // self.MAXCHUNKS)\n        ]\n        self.chunks += (\n            []\n            if totlines % self.MAXCHUNKS == 0\n            else [\n                totlines % self.MAXCHUNKS\n                + (0 if self.chunks == [] else self.chunks[-1])\n            ]\n        )  # -1\n        self.pad = curses.newpad(min([self.MAXCHUNKS, totlines]), width)\n        self.pad.keypad(True)\n        # self.current_chunk = 0\n        self.y = 0\n        self.width = width\n\n    def feed(self, textlist):\n        self.text = textlist\n\n    def getch(self):\n        return self.pad.getch()\n\n    def bkgd(self, bg):\n        self.pad.bkgd(SCREEN.getbkgd())\n\n    def find_chunkidx(self, y):\n        for n, i in enumerate(self.chunks):\n            if y <= i:\n                return n\n\n    def paint_text(self, chunkidx=0):\n        self.pad.clear()\n        start_chunk = 0 if chunkidx == 0 else self.chunks[chunkidx - 1] + 1\n        end_chunk = self.chunks[chunkidx]\n        for n, i in enumerate(self.text[start_chunk : end_chunk + 1]):\n            if re.search(\"\\\\[IMG:[0-9]+\\\\]\", i):\n                self.pad.addstr(\n                    n, self.width // 2 - len(i) // 2 + 1, i, curses.A_REVERSE\n                )\n            else:\n                self.pad.addstr(n, 0, i)\n        # chapter suffix\n        ch_suffix = \"***\"  # \"\\u3064\\u3065\\u304f\" つづく\n        try:\n            self.pad.addstr(n + 1, (self.width - len(ch_suffix)) // 2 + 1, ch_suffix)\n        except curses.error:\n            pass\n\n    def chgat(self, y, x, n, attr):\n        chunkidx = self.find_chunkidx(y)\n        start_chunk = 0 if chunkidx == 0 else self.chunks[chunkidx - 1] + 1\n        end_chunk = self.chunks[chunkidx]\n        if y in range(start_chunk, end_chunk + 1):\n            self.pad.chgat(y % self.MAXCHUNKS, x, n, attr)\n\n    def getbkgd(self):\n        return self.pad.getbkgd()\n\n    def refresh(self, y, b, c, d, e, f):\n        chunkidx = self.find_chunkidx(y)\n        if chunkidx != self.find_chunkidx(self.y):\n            self.paint_text(chunkidx)\n        # TODO: not modulo by self.MAXCHUNKS but self.pad.height\n        self.pad.refresh(y % self.MAXCHUNKS, b, c, d, e, f)\n        self.y = y\n\n\ndef text_win(textfunc):\n    @wraps(textfunc)\n    def wrapper(*args, **kwargs):\n        rows, cols = SCREEN.getmaxyx()\n        hi, wi = rows - 4, cols - 4\n        Y, X = 2, 2\n        textw = curses.newwin(hi, wi, Y, X)\n        if COLORSUPPORT:\n            textw.bkgd(SCREEN.getbkgd())\n\n        title, raw_texts, key = textfunc(*args, **kwargs)\n\n        if len(title) > cols - 8:\n            title = title[: cols - 8]\n\n        texts = []\n        for i in raw_texts.splitlines():\n            texts += textwrap.wrap(i, wi - 6, drop_whitespace=False)\n\n        textw.box()\n        textw.keypad(True)\n        textw.addstr(1, 2, title)\n        textw.addstr(2, 2, \"-\" * len(title))\n        key_textw = 0\n\n        totlines = len(texts)\n\n        pad = curses.newpad(totlines, wi - 2)\n        if COLORSUPPORT:\n            pad.bkgd(SCREEN.getbkgd())\n\n        pad.keypad(True)\n        for n, i in enumerate(texts):\n            pad.addstr(n, 0, i)\n        y = 0\n        textw.refresh()\n        pad.refresh(y, 0, Y + 4, X + 4, rows - 5, cols - 6)\n        padhi = rows - 8 - Y\n\n        while key_textw not in K[\"Quit\"] | key:\n            if key_textw in K[\"ScrollUp\"] and y > 0:\n                y -= 1\n            elif key_textw in K[\"ScrollDown\"] and y < totlines - hi + 6:\n                y += 1\n            elif key_textw in K[\"PageUp\"]:\n                y = pgup(y, padhi)\n            elif key_textw in K[\"PageDown\"]:\n                y = pgdn(y, totlines, padhi)\n            elif key_textw in K[\"BeginningOfCh\"]:\n                y = 0\n            elif key_textw in K[\"EndOfCh\"]:\n                y = pgend(totlines, padhi)\n            elif key_textw in WINKEYS - key:\n                textw.clear()\n                textw.refresh()\n                return key_textw\n            pad.refresh(y, 0, 6, 5, rows - 5, cols - 5)\n            key_textw = textw.getch()\n\n        textw.clear()\n        textw.refresh()\n        return\n\n    return wrapper\n\n\ndef choice_win(allowdel=False):\n    def inner_f(listgen):\n        @wraps(listgen)\n        def wrapper(*args, **kwargs):\n            rows, cols = SCREEN.getmaxyx()\n            hi, wi = rows - 4, cols - 4\n            Y, X = 2, 2\n            chwin = curses.newwin(hi, wi, Y, X)\n            if COLORSUPPORT:\n                chwin.bkgd(SCREEN.getbkgd())\n\n            title, ch_list, index, key = listgen(*args, **kwargs)\n\n            if len(title) > cols - 8:\n                title = title[: cols - 8]\n\n            chwin.box()\n            chwin.keypad(True)\n            chwin.addstr(1, 2, title)\n            chwin.addstr(2, 2, \"-\" * len(title))\n            if allowdel:\n                chwin.addstr(3, 2, \"HINT: Press 'd' to delete.\")\n            key_chwin = 0\n\n            totlines = len(ch_list)\n            chwin.refresh()\n            pad = curses.newpad(totlines, wi - 2)\n            if COLORSUPPORT:\n                pad.bkgd(SCREEN.getbkgd())\n\n            pad.keypad(True)\n\n            padhi = rows - 5 - Y - 4 + 1 - (1 if allowdel else 0)\n            # padhi = rows - 5 - Y - 4 + 1 - 1\n            y = 0\n            if index in range(padhi // 2, totlines - padhi // 2):\n                y = index - padhi // 2 + 1\n            span = []\n\n            for n, i in enumerate(ch_list):\n                # strs = \"  \" + str(n+1).rjust(d) + \" \" + i[0]\n                strs = \"  \" + i\n                strs = strs[0 : wi - 3]\n                pad.addstr(n, 0, strs)\n                span.append(len(strs))\n\n            countstring = \"\"\n            while key_chwin not in K[\"Quit\"] | key:\n                if countstring == \"\":\n                    count = 1\n                else:\n                    count = int(countstring)\n                if key_chwin in range(48, 58):  # i.e., k is a numeral\n                    countstring = countstring + chr(key_chwin)\n                else:\n                    if key_chwin in K[\"ScrollUp\"] or key_chwin in K[\"PageUp\"]:\n                        index -= count\n                        if index < 0:\n                            index = 0\n                    elif key_chwin in K[\"ScrollDown\"] or key_chwin in K[\"PageDown\"]:\n                        index += count\n                        if index + 1 >= totlines:\n                            index = totlines - 1\n                    elif key_chwin in K[\"Follow\"]:\n                        chwin.clear()\n                        chwin.refresh()\n                        return None, index, None\n                    # elif key_chwin in K[\"PageUp\"]:\n                    #     index -= 3\n                    #     if index < 0:\n                    #         index = 0\n                    # elif key_chwin in K[\"PageDown\"]:\n                    #     index += 3\n                    #     if index >= totlines:\n                    #         index = totlines - 1\n                    elif key_chwin in K[\"BeginningOfCh\"]:\n                        index = 0\n                    elif key_chwin in K[\"EndOfCh\"]:\n                        index = totlines - 1\n                    elif key_chwin == ord(\"D\") and allowdel:\n                        return None, (0 if index == 0 else index - 1), index\n                        # chwin.redrawwin()\n                        # chwin.refresh()\n                    elif key_chwin == ord(\"d\") and allowdel:\n                        resk, resp, _ = choice_win()(\n                            lambda: (\n                                \"Delete '{}'?\".format(ch_list[index]),\n                                [\"(Y)es\", \"(N)o\"],\n                                0,\n                                {ord(\"n\")},\n                            )\n                        )()\n                        if resk is not None:\n                            key_chwin = resk\n                            continue\n                        elif resp == 0:\n                            return None, (0 if index == 0 else index - 1), index\n                        chwin.redrawwin()\n                        chwin.refresh()\n                    elif key_chwin in {\n                        ord(i) for i in [\"Y\", \"y\", \"N\", \"n\"]\n                    } and ch_list == [\"(Y)es\", \"(N)o\"]:\n                        if key_chwin in {ord(\"Y\"), ord(\"y\")}:\n                            return None, 0, None\n                        else:\n                            return None, 1, None\n                    elif key_chwin in WINKEYS - key:\n                        chwin.clear()\n                        chwin.refresh()\n                        return key_chwin, index, None\n                    countstring = \"\"\n\n                while index not in range(y, y + padhi):\n                    if index < y:\n                        y -= 1\n                    else:\n                        y += 1\n\n                for n in range(totlines):\n                    att = curses.A_REVERSE if index == n else curses.A_NORMAL\n                    pre = \">>\" if index == n else \"  \"\n                    pad.addstr(n, 0, pre)\n                    pad.chgat(n, 0, span[n], pad.getbkgd() | att)\n\n                pad.refresh(\n                    y, 0, Y + 4 + (1 if allowdel else 0), X + 4, rows - 5, cols - 6\n                )\n                # pad.refresh(y, 0, Y+5, X+4, rows - 5, cols - 6)\n                key_chwin = chwin.getch()\n\n            chwin.clear()\n            chwin.refresh()\n            return None, None, None\n\n        return wrapper\n\n    return inner_f\n\n\ndef show_loader(scr):\n    scr.clear()\n    rows, cols = scr.getmaxyx()\n    scr.addstr((rows - 1) // 2, (cols - 1) // 2, \"\\u231B\")\n    # scr.addstr(((rows-2)//2)+1, (cols-len(msg))//2, msg)\n    scr.refresh()\n\n\ndef loadstate():\n    global CFG, STATE, CFGFILE, STATEFILE\n    prefix = \"\"\n    if os.getenv(\"HOME\") is not None:\n        homedir = os.getenv(\"HOME\")\n        if os.path.isdir(os.path.join(homedir, \".config\")):\n            prefix = os.path.join(homedir, \".config\", \"epy\")\n        else:\n            prefix = os.path.join(homedir, \".epy\")\n    elif os.getenv(\"USERPROFILE\") is not None:\n        prefix = os.path.join(os.getenv(\"USERPROFILE\"), \".epy\")\n    else:\n        CFGFILE = os.devnull\n        STATEFILE = os.devnull\n    os.makedirs(prefix, exist_ok=True)\n    CFGFILE = os.path.join(prefix, \"config.json\")\n    STATEFILE = os.path.join(prefix, \"state.json\")\n\n    try:\n        with open(CFGFILE) as f:\n            CFG = json.load(f)\n        with open(STATEFILE) as f:\n            STATE = json.load(f)\n    except FileNotFoundError:\n        pass\n\n\ndef parse_keys():\n    global WINKEYS\n    for i in CFG[\"Keys\"].keys():\n        parsedk = CFG[\"Keys\"][i]\n        if len(parsedk) == 1:\n            parsedk = ord(parsedk)\n        elif parsedk[:-1] in {\"^\", \"C-\"}:\n            parsedk = ord(parsedk[-1]) - 96  # Reference: ASCII chars\n        else:\n            sys.exit(\"ERROR: Keybindings {}\".format(i))\n\n        try:\n            K[i].add(parsedk)\n        except KeyError:\n            K[i] = {parsedk}\n    WINKEYS = (\n        {curses.KEY_RESIZE}\n        | K[\"Metadata\"]\n        | K[\"Help\"]\n        | K[\"TableOfContents\"]\n        | K[\"ShowBookmarks\"]\n    )\n\n\ndef savestate(file, index, width, pos, pctg):\n    with open(CFGFILE, \"w\") as f:\n        json.dump(CFG, f, indent=2)\n    STATE[\"LastRead\"] = file\n    STATE[\"States\"][file][\"index\"] = index\n    STATE[\"States\"][file][\"width\"] = width\n    STATE[\"States\"][file][\"pos\"] = pos\n    STATE[\"States\"][file][\"pctg\"] = pctg\n    with open(STATEFILE, \"w\") as f:\n        json.dump(STATE, f, indent=4)\n\n    if MULTIPROC:\n        # PROC_COUNTLETTERS.terminate()\n        # PROC_COUNTLETTERS.kill()\n        # PROC_COUNTLETTERS.join()\n        try:\n            PROC_COUNTLETTERS.kill()\n        except AttributeError:\n            PROC_COUNTLETTERS.terminate()\n\n\ndef pgup(pos, winhi, preservedline=0, c=1):\n    if pos >= (winhi - preservedline) * c:\n        return pos - (winhi + preservedline) * c\n    else:\n        return 0\n\n\ndef pgdn(pos, tot, winhi, preservedline=0, c=1):\n    if pos + (winhi * c) <= tot - winhi:\n        return pos + (winhi * c)\n    else:\n        pos = tot - winhi\n        if pos < 0:\n            return 0\n        return pos\n\n\ndef pgend(tot, winhi):\n    if tot - winhi >= 0:\n        return tot - winhi\n    else:\n        return 0\n\n\n@choice_win()\ndef toc(src, index):\n    return \"Table of Contents\", src, index, K[\"TableOfContents\"]\n\n\ndef rsvp(content, y):\n    global wpm\n    curses.start_color()\n    curses.use_default_colors()\n    for i in range(0, curses.COLORS):\n        curses.init_pair(i + 1, i, -1)\n    rows, cols = SCREEN.getmaxyx()\n    hi, wi = rows - 4, cols - 4\n    Y, X = 2, 2\n    chwin = curses.newwin(hi, wi, Y, X)\n    if COLORSUPPORT:\n        chwin.bkgd(SCREEN.getbkgd())\n\n    for i, text in enumerate(content[y:]):\n        try:\n            time.sleep(0.1)\n            for j, word in enumerate(text.strip().split()):\n                wps = wpm / 60.0\n                chwin.clear()\n                word_len = len(word)\n                t_wait_sec = (1 / wps) * (1 + syllables.estimate(word) ** 2 / 100)\n                highlight_letter_index = min(4, round(word_len / 2))\n                spaces = abs(4 - highlight_letter_index) * \" \"\n                wi_cn = round(wi / 2) - 5\n                chwin.addstr(\n                    round(hi / 2), wi_cn + len(spaces), word[:highlight_letter_index]\n                )\n                chwin.addstr(\n                    round(hi / 2),\n                    wi_cn + len(word[:highlight_letter_index]) + len(spaces),\n                    word[highlight_letter_index],\n                    curses.color_pair(197),\n                )\n                chwin.addstr(\n                    round(hi / 2),\n                    wi_cn + len(word[:highlight_letter_index]) + 1 + len(spaces),\n                    word[highlight_letter_index + 1 :],\n                )\n                if \".\" in word:\n                    time.sleep(0.2)\n                if (\n                    \",\" in word\n                    or \"'\" in word\n                    or '\"' in word\n                    or \"`\" in word\n                    or \"-\" in word\n                    or \"(\" in word\n                    or \")\" in word\n                    or \":\" in word\n                ):\n                    time.sleep(0.15)\n                time.sleep(t_wait_sec)\n                chwin.refresh()\n        except KeyboardInterrupt as e:\n            keybinding_msg = \"Current wpm: \" + str(wpm) + \" Enter new wpm: \"\n            try:\n                option = input_prompt(keybinding_msg)\n                wpm = int(option)\n            except ValueError as e:\n                return y + i\n            except KeyboardInterrupt as e:\n                return y + i\n\n    return None, None, None\n\n\n@text_win\ndef meta(ebook):\n    mdata = \"[File Info]\\nPATH: {}\\nSIZE: {} MB\\n \\n[Book Info]\\n\".format(\n        ebook.path, round(os.path.getsize(ebook.path) / 1024 ** 2, 2)\n    )\n    for i in ebook.get_meta():\n        data = re.sub(\"<[^>]*>\", \"\", i[1])\n        mdata += i[0].upper() + \": \" + data + \"\\n\"\n        data = re.sub(\"\\t\", \"\", data)\n        # mdata += textwrap.wrap(i[0].upper() + \": \" + data, wi - 6)\n    return \"Metadata\", mdata, K[\"Metadata\"]\n\n\n@text_win\ndef help():\n    src = \"Key Bindings:\\n\"\n    dig = max([len(i) for i in CFG[\"Keys\"].values()]) + 2\n    for i in CFG[\"Keys\"].keys():\n        src += \"{}  {}\\n\".format(\n            CFG[\"Keys\"][i].rjust(dig), \" \".join(re.findall(\"[A-Z][^A-Z]*\", i))\n        )\n    return \"Help\", src, K[\"Help\"]\n\n\n@text_win\ndef errmsg(title, msg, key):\n    return title, msg, key\n\n\ndef bookmarks(ebookpath):\n    idx = 0\n    while True:\n        bmarkslist = [i[0] for i in STATE[\"States\"][ebookpath][\"bmarks\"]]\n        if bmarkslist == []:\n            return list(K[\"ShowBookmarks\"])[0], None\n        retk, idx, todel = choice_win(True)(\n            lambda: (\"Bookmarks\", bmarkslist, idx, {ord(\"B\")})\n        )()\n        if todel is not None:\n            del STATE[\"States\"][ebookpath][\"bmarks\"][todel]\n        else:\n            return retk, idx\n\n\ndef truncate(teks, subte, maxlen, startsub=0):\n    if startsub > maxlen:\n        raise ValueError(\"Var startsub cannot be bigger than maxlen.\")\n    elif len(teks) <= maxlen:\n        return teks\n    else:\n        lensu = len(subte)\n        beg = teks[:startsub]\n        mid = subte if lensu <= maxlen - startsub else subte[: maxlen - startsub]\n        end = teks[startsub + lensu - maxlen :] if lensu < maxlen - startsub else \"\"\n        return beg + mid + end\n\n\ndef input_prompt(prompt):\n    # prevent pad hole when prompting for input while\n    # other window is active\n    # pad.refresh(y, 0, 0, x, rows-2, x+width)\n    rows, cols = SCREEN.getmaxyx()\n    stat = curses.newwin(1, cols, rows - 1, 0)\n    if COLORSUPPORT:\n        stat.bkgd(SCREEN.getbkgd())\n    stat.keypad(True)\n    curses.echo(1)\n    curses.curs_set(1)\n\n    init_text = \"\"\n\n    stat.addstr(0, 0, prompt, curses.A_REVERSE)\n    stat.addstr(0, len(prompt), init_text)\n    stat.refresh()\n\n    try:\n        while True:\n            ipt = stat.getch()\n            if ipt == 27:\n                stat.clear()\n                stat.refresh()\n                curses.echo(0)\n                curses.curs_set(0)\n                return\n            elif ipt == 10:\n                stat.clear()\n                stat.refresh()\n                curses.echo(0)\n                curses.curs_set(0)\n                return init_text\n            elif ipt in {8, curses.KEY_BACKSPACE}:\n                init_text = init_text[:-1]\n            elif ipt == curses.KEY_RESIZE:\n                stat.clear()\n                stat.refresh()\n                curses.echo(0)\n                curses.curs_set(0)\n                return curses.KEY_RESIZE\n            # elif len(init_text) <= maxlen:\n            else:\n                init_text += chr(ipt)\n\n            stat.clear()\n            stat.addstr(0, 0, prompt, curses.A_REVERSE)\n            stat.addstr(\n                0,\n                len(prompt),\n                init_text\n                if len(prompt + init_text) < cols\n                else \"...\" + init_text[len(prompt) - cols + 4 :],\n            )\n            stat.refresh()\n    except KeyboardInterrupt:\n        stat.clear()\n        stat.refresh()\n        curses.echo(0)\n        curses.curs_set(0)\n        return\n\n\ndef det_ebook_cls(file):\n    filext = os.path.splitext(file)[1]\n    if filext == \".epub\":\n        return Epub(file)\n    elif filext == \".fb2\":\n        return FictionBook(file)\n    elif MOBISUPPORT and filext == \".mobi\":\n        return Mobi(file)\n    elif MOBISUPPORT and filext == \".azw3\":\n        return Azw3(file)\n    elif not MOBISUPPORT and filext in {\".mobi\", \".azw3\"}:\n        sys.exit(\n            \"\"\"ERROR: Format not supported. (Supported: epub, fb2).\nTo get mobi and azw3 support, install mobi module from pip.\n   $ pip install mobi\"\"\"\n        )\n    else:\n        sys.exit(\"ERROR: Format not supported. (Supported: epub, fb2)\")\n\n\ndef dots_path(curr, tofi):\n    candir = curr.split(\"/\")\n    tofi = tofi.split(\"/\")\n    alld = tofi.count(\"..\")\n    t = len(candir)\n    candir = candir[0 : t - alld - 1]\n    try:\n        while True:\n            tofi.remove(\"..\")\n    except ValueError:\n        pass\n    return \"/\".join(candir + tofi)\n\n\ndef find_dict_client():\n    global DICT\n    if shutil.which(CFG[\"DictionaryClient\"].split()[0]) is not None:\n        DICT = CFG[\"DictionaryClient\"]\n    else:\n        DICT_LIST = [\"sdcv\", \"dict\"]\n        for i in DICT_LIST:\n            if shutil.which(i) is not None:\n                DICT = i\n                break\n        if DICT in {\"sdcv\"}:\n            DICT += \" -n\"\n\n\ndef find_media_viewer():\n    global VWR\n    if shutil.which(CFG[\"DefaultViewer\"].split()[0]) is not None:\n        VWR = CFG[\"DefaultViewer\"]\n    elif sys.platform == \"win32\":\n        VWR = \"start\"\n    elif sys.platform == \"darwin\":\n        VWR = \"open\"\n    else:\n        VWR_LIST = [\n            \"feh\",\n            \"gio\",\n            \"gnome-open\",\n            \"gvfs-open\",\n            \"xdg-open\",\n            \"kde-open\",\n            \"firefox\",\n        ]\n        for i in VWR_LIST:\n            if shutil.which(i) is not None:\n                VWR = i\n                break\n\n    if VWR in {\"gio\"}:\n        VWR += \" open\"\n\n\ndef open_media(scr, name, bstr):\n    sfx = os.path.splitext(name)[1]\n    fd, path = tempfile.mkstemp(suffix=sfx)\n    try:\n        with os.fdopen(fd, \"wb\") as tmp:\n            # tmp.write(epub.file.read(src))\n            tmp.write(bstr)\n        # run(VWR + \" \" + path, shell=True)\n        subprocess.call(\n            VWR + \" \" + path,\n            shell=True,\n            stdout=subprocess.DEVNULL,\n            stderr=subprocess.DEVNULL,\n        )\n        k = scr.getch()\n    finally:\n        os.remove(path)\n    return k\n\n\n@text_win\ndef define_word(word):\n    rows, cols = SCREEN.getmaxyx()\n    hi, wi = 5, 16\n    Y, X = (rows - hi) // 2, (cols - wi) // 2\n\n    p = subprocess.Popen(\n        \"{} {}\".format(DICT, word),\n        stdout=subprocess.PIPE,\n        stderr=subprocess.PIPE,\n        shell=True,\n    )\n\n    dictwin = curses.newwin(hi, wi, Y, X)\n    dictwin.box()\n    dictwin.addstr((hi - 1) // 2, (wi - 10) // 2, \"Loading...\")\n    dictwin.refresh()\n\n    out, err = p.communicate()\n\n    dictwin.clear()\n    dictwin.refresh()\n\n    if err == b\"\":\n        return \"Definition: \" + word.upper(), out.decode(), K[\"DefineWord\"]\n    else:\n        return \"Error: \" + DICT, err.decode(), K[\"DefineWord\"]\n\n\ndef searching(pad, src, width, y, ch, tot):\n    global SEARCHPATTERN\n    rows, cols = SCREEN.getmaxyx()\n    x = (cols - width) // 2\n    if SEARCHPATTERN is None:\n        candtext = input_prompt(\" Regex:\")\n        if candtext is None:\n            return y\n        elif isinstance(candtext, str):\n            SEARCHPATTERN = \"/\" + candtext\n        elif candtext == curses.KEY_RESIZE:\n            return candtext\n\n    if SEARCHPATTERN in {\"?\", \"/\"}:\n        SEARCHPATTERN = None\n        return y\n\n    found = []\n    try:\n        pattern = re.compile(SEARCHPATTERN[1:], re.IGNORECASE)\n    except re.error as reerrmsg:\n        SEARCHPATTERN = None\n        tmpk = errmsg(\"!Regex Error\", str(reerrmsg), set())\n        return tmpk\n\n    for n, i in enumerate(src):\n        for j in pattern.finditer(i):\n            found.append([n, j.span()[0], j.span()[1] - j.span()[0]])\n\n    if found == []:\n        if SEARCHPATTERN[0] == \"/\" and ch + 1 < tot:\n            return 1\n        elif SEARCHPATTERN[0] == \"?\" and ch > 0:\n            return -1\n        else:\n            s = 0\n            while True:\n                if s in K[\"Quit\"]:\n                    SEARCHPATTERN = None\n                    SCREEN.clear()\n                    SCREEN.refresh()\n                    return y\n                elif s == ord(\"n\") and ch == 0:\n                    SEARCHPATTERN = \"/\" + SEARCHPATTERN[1:]\n                    return 1\n                elif s == ord(\"N\") and ch + 1 == tot:\n                    SEARCHPATTERN = \"?\" + SEARCHPATTERN[1:]\n                    return -1\n\n                SCREEN.clear()\n                SCREEN.addstr(\n                    rows - 1,\n                    0,\n                    \" Finished searching: \" + SEARCHPATTERN[1 : cols - 22] + \" \",\n                    curses.A_REVERSE,\n                )\n                SCREEN.refresh()\n                pad.refresh(y, 0, 0, x, rows - 2, x + width)\n                s = pad.getch()\n\n    sidx = len(found) - 1\n    if SEARCHPATTERN[0] == \"/\":\n        if y > found[-1][0]:\n            return 1\n        for n, i in enumerate(found):\n            if i[0] >= y:\n                sidx = n\n                break\n\n    s = 0\n    msg = (\n        \" Searching: \"\n        + SEARCHPATTERN[1:]\n        + \" --- Res {}/{} Ch {}/{} \".format(sidx + 1, len(found), ch + 1, tot)\n    )\n    while True:\n        if s in K[\"Quit\"]:\n            SEARCHPATTERN = None\n            for i in found:\n                pad.chgat(i[0], i[1], i[2], pad.getbkgd())\n            SCREEN.clear()\n            SCREEN.refresh()\n            return y\n        elif s == ord(\"n\"):\n            SEARCHPATTERN = \"/\" + SEARCHPATTERN[1:]\n            if sidx == len(found) - 1:\n                if ch + 1 < tot:\n                    return 1\n                else:\n                    s = 0\n                    msg = \" Finished searching: \" + SEARCHPATTERN[1:] + \" \"\n                    continue\n            else:\n                sidx += 1\n                msg = (\n                    \" Searching: \"\n                    + SEARCHPATTERN[1:]\n                    + \" --- Res {}/{} Ch {}/{} \".format(\n                        sidx + 1, len(found), ch + 1, tot\n                    )\n                )\n        elif s == ord(\"N\"):\n            SEARCHPATTERN = \"?\" + SEARCHPATTERN[1:]\n            if sidx == 0:\n                if ch > 0:\n                    return -1\n                else:\n                    s = 0\n                    msg = \" Finished searching: \" + SEARCHPATTERN[1:] + \" \"\n                    continue\n            else:\n                sidx -= 1\n                msg = (\n                    \" Searching: \"\n                    + SEARCHPATTERN[1:]\n                    + \" --- Res {}/{} Ch {}/{} \".format(\n                        sidx + 1, len(found), ch + 1, tot\n                    )\n                )\n        elif s == curses.KEY_RESIZE:\n            return s\n\n        # TODO\n        if y + rows - 1 > pad.chunks[pad.find_chunkidx(y)]:\n            y = pad.chunks[pad.find_chunkidx(y)] + 1\n\n        while found[sidx][0] not in list(range(y, y + rows - 1)):\n            if found[sidx][0] > y:\n                y += rows - 1\n            else:\n                y -= rows - 1\n                if y < 0:\n                    y = 0\n\n        for n, i in enumerate(found):\n            attr = curses.A_REVERSE if n == sidx else curses.A_NORMAL\n            pad.chgat(i[0], i[1], i[2], pad.getbkgd() | attr)\n\n        SCREEN.clear()\n        SCREEN.addstr(rows - 1, 0, msg, curses.A_REVERSE)\n        SCREEN.refresh()\n        pad.refresh(y, 0, 0, x, rows - 2, x + width)\n        s = pad.getch()\n\n\ndef find_curr_toc_id(toc_idx, toc_sect, toc_secid, index, y):\n    ntoc = 0\n    for n, (i, j) in enumerate(zip(toc_idx, toc_sect)):\n        if i <= index:\n            if y >= toc_secid.get(j, 0):\n                ntoc = n\n        else:\n            break\n    return ntoc\n\n\ndef count_pct_async(ebook, allprev, sumlet):\n    perch = []\n    for n, i in enumerate(ebook.contents):\n        content = ebook.get_raw_text(i)\n        parser = HTMLtoLines()\n        # try:\n        parser.feed(content)\n        parser.close()\n        # except:\n        #     pass\n        src_lines = parser.get_lines()\n        allprev[n] = sum(perch)\n        perch.append(sum([len(re.sub(\"\\s\", \"\", j)) for j in src_lines]))\n    sumlet.value = sum(perch)\n\n\ndef count_pct(ebook):\n    perch = []\n    allprev = []\n    for i in ebook.contents:\n        content = ebook.get_raw_text(i)\n        parser = HTMLtoLines()\n        # try:\n        parser.feed(content)\n        parser.close()\n        # except:\n        #     pass\n        src_lines = parser.get_lines()\n        allprev.append(sum(perch))\n        perch.append(sum([len(re.sub(\"\\s\", \"\", j)) for j in src_lines]))\n    sumlet = sum(perch)\n    return allprev, sumlet\n\n\ndef count_max_reading_pg(ebook):\n    global ALLPREVLETTERS, SUMALLLETTERS, PROC_COUNTLETTERS, MULTIPROC\n\n    if MULTIPROC:\n        try:\n            ALLPREVLETTERS = multiprocessing.Array(\"i\", len(ebook.contents))\n            SUMALLLETTERS = multiprocessing.Value(\"i\", 0)\n            PROC_COUNTLETTERS = multiprocessing.Process(\n                target=count_pct_async, args=(ebook, ALLPREVLETTERS, SUMALLLETTERS)\n            )\n            # forking PROC_COUNTLETTERS will raise\n            # zlib.error: Error -3 while decompressing data: invalid distance too far back\n            PROC_COUNTLETTERS.start()\n        except:\n            MULTIPROC = False\n    if not MULTIPROC:\n        ALLPREVLETTERS, SUMALLLETTERS = count_pct(ebook)\n\n\ndef reader(ebook, index, width, y, pctg, sect):\n    global SHOWPROGRESS\n\n    k = 0 if SEARCHPATTERN is None else ord(\"/\")\n    rows, cols = SCREEN.getmaxyx()\n    x = (cols - width) // 2\n\n    contents = ebook.contents\n    toc_name = ebook.toc_entries[0]\n    toc_idx = ebook.toc_entries[1]\n    toc_sect = ebook.toc_entries[2]\n    toc_secid = {}\n    chpath = contents[index]\n    content = ebook.get_raw_text(chpath)\n    parser = HTMLtoLines(set(toc_sect))\n    # parser = HTMLtoLines()\n    # try:\n    parser.feed(content)\n    parser.close()\n    # except:\n    #     pass\n\n    src_lines, imgs, toc_secid = parser.get_lines(width)\n    totlines = len(src_lines) + 1  # 1 extra line for suffix\n\n    if y < 0 and totlines <= rows:\n        y = 0\n    elif pctg is not None:\n        y = round(pctg * totlines)\n    else:\n        y = y % totlines\n\n    pad = Board(totlines, width)\n    pad.feed(src_lines)\n\n    # this make curses.A_REVERSE not working\n    # put before paint_text\n    if COLORSUPPORT:\n        pad.bkgd(SCREEN.getbkgd())\n\n    pad.paint_text(0)\n\n    LOCALPCTG = []\n    for i in src_lines:\n        LOCALPCTG.append(len(re.sub(\"\\s\", \"\", i)))\n\n    SCREEN.clear()\n    SCREEN.refresh()\n    # try except to be more flexible on terminal resize\n    try:\n        pad.refresh(y, 0, 0, x, rows - 1, x + width)\n    except curses.error:\n        pass\n\n    if sect != \"\":\n        y = toc_secid.get(sect, 0)\n\n    countstring = \"\"\n    svline = \"dontsave\"\n    try:\n        while True:\n            if countstring == \"\":\n                count = 1\n            else:\n                count = int(countstring)\n            if k in range(48, 58):  # i.e., k is a numeral\n                countstring = countstring + chr(k)\n            else:\n                if k in K[\"Quit\"]:\n                    if k == 27 and countstring != \"\":\n                        countstring = \"\"\n                    else:\n                        savestate(ebook.path, index, width, y, y / totlines)\n                        sys.exit()\n                elif k in K[\"ScrollUp\"]:\n                    if count > 1:\n                        svline = y - 1\n                    if y >= count:\n                        y -= count\n                    elif y == 0 and index != 0:\n                        return -1, width, -rows, None, \"\"\n                    else:\n                        y = 0\n                elif k in K[\"PageUp\"]:\n                    if y == 0 and index != 0:\n                        return -1, width, -rows, None, \"\"\n                    else:\n                        y = pgup(y, rows, LINEPRSRV, count)\n                elif k in K[\"ScrollDown\"]:\n                    if count > 1:\n                        svline = y + rows - 1\n                    if y + count <= totlines - rows:\n                        y += count\n                    elif y == totlines - rows and index != len(contents) - 1:\n                        return 1, width, 0, None, \"\"\n                    else:\n                        y = totlines - rows\n                elif k in K[\"PageDown\"]:\n                    if totlines - y - LINEPRSRV > rows:\n                        if y + rows > pad.chunks[pad.find_chunkidx(y)]:\n                            y = pad.chunks[pad.find_chunkidx(y)] + 1\n                        else:\n                            y += rows - LINEPRSRV\n                        # SCREEN.clear()\n                        # SCREEN.refresh()\n                    elif index != len(contents) - 1:\n                        return 1, width, 0, None, \"\"\n                elif k in K[\"HalfScreenUp\"] | K[\"HalfScreenDown\"]:\n                    countstring = str(rows // 2)\n                    k = list(K[\"ScrollUp\" if k in K[\"HalfScreenUp\"] else \"ScrollDown\"])[\n                        0\n                    ]\n                    continue\n                elif k in K[\"NextChapter\"]:\n                    ntoc = find_curr_toc_id(toc_idx, toc_sect, toc_secid, index, y)\n                    if ntoc < len(toc_idx) - 1:\n                        if index == toc_idx[ntoc + 1]:\n                            try:\n                                y = toc_secid[toc_sect[ntoc + 1]]\n                            except KeyError:\n                                pass\n                        else:\n                            return (\n                                toc_idx[ntoc + 1] - index,\n                                width,\n                                0,\n                                None,\n                                toc_sect[ntoc + 1],\n                            )\n                elif k in K[\"PrevChapter\"]:\n                    ntoc = find_curr_toc_id(toc_idx, toc_sect, toc_secid, index, y)\n                    if ntoc > 0:\n                        if index == toc_idx[ntoc - 1]:\n                            y = toc_secid.get(toc_sect[ntoc - 1], 0)\n                        else:\n                            return (\n                                toc_idx[ntoc - 1] - index,\n                                width,\n                                0,\n                                None,\n                                toc_sect[ntoc - 1],\n                            )\n                elif k in K[\"BeginningOfCh\"]:\n                    ntoc = find_curr_toc_id(toc_idx, toc_sect, toc_secid, index, y)\n                    try:\n                        y = toc_secid[toc_sect[ntoc]]\n                    except (KeyError, IndexError):\n                        y = 0\n                elif k in K[\"EndOfCh\"]:\n                    ntoc = find_curr_toc_id(toc_idx, toc_sect, toc_secid, index, y)\n                    try:\n                        if toc_secid[toc_sect[ntoc + 1]] - rows >= 0:\n                            y = toc_secid[toc_sect[ntoc + 1]] - rows\n                        else:\n                            y = toc_secid[toc_sect[ntoc]]\n                    except (KeyError, IndexError):\n                        y = pgend(totlines, rows)\n                elif k in K[\"TableOfContents\"]:\n                    if ebook.toc_entries == [[], [], []]:\n                        k = errmsg(\n                            \"Table of Contents\",\n                            \"N/A: TableOfContents is unavailable for this book.\",\n                            K[\"TableOfContents\"],\n                        )\n                        continue\n                    ntoc = find_curr_toc_id(toc_idx, toc_sect, toc_secid, index, y)\n                    rettock, fllwd, _ = toc(toc_name, ntoc)\n                    if rettock is not None:  # and rettock in WINKEYS:\n                        k = rettock\n                        continue\n                    elif fllwd is not None:\n                        if index == toc_idx[fllwd]:\n                            try:\n                                y = toc_secid[toc_sect[fllwd]]\n                            except KeyError:\n                                y = 0\n                        else:\n                            return (\n                                toc_idx[fllwd] - index,\n                                width,\n                                0,\n                                None,\n                                toc_sect[fllwd],\n                            )\n                elif k in K[\"Metadata\"]:\n                    k = meta(ebook)\n                    if k in WINKEYS:\n                        continue\n                elif k in K[\"Help\"]:\n                    k = help()\n                    if k in WINKEYS:\n                        continue\n                elif k in K[\"Enlarge\"] and (width + count) < cols - 4:\n                    width += count\n                    return 0, width, 0, y / totlines, \"\"\n                elif k in K[\"Shrink\"] and width >= 22:\n                    width -= count\n                    return 0, width, 0, y / totlines, \"\"\n                elif k in K[\"SetWidth\"]:\n                    if countstring == \"\":\n                        # if called without a count, toggle between 80 cols and full width\n                        if width != 80 and cols - 4 >= 80:\n                            return 0, 80, 0, y / totlines, \"\"\n                        else:\n                            return 0, cols - 4, 0, y / totlines, \"\"\n                    else:\n                        width = count\n                    if width < 20:\n                        width = 20\n                    elif width >= cols - 4:\n                        width = cols - 4\n                    return 0, width, 0, y / totlines, \"\"\n                # elif k == ord(\"0\"):\n                #     if width != 80 and cols - 2 >= 80:\n                #         return 0, 80, 0, y/totlines, \"\"\n                #     else:\n                #         return 0, cols - 2, 0, y/totlines, \"\"\n                elif k in K[\"RegexSearch\"]:\n                    fs = searching(pad, src_lines, width, y, index, len(contents))\n                    if fs in WINKEYS or fs is None:\n                        k = fs\n                        continue\n                    elif SEARCHPATTERN is not None:\n                        return fs, width, 0, None, \"\"\n                    else:\n                        y = fs\n                elif k in K[\"OpenImage\"] and VWR is not None:\n                    gambar, idx = [], []\n                    for n, i in enumerate(src_lines[y : y + rows]):\n                        img = re.search(\"(?<=\\\\[IMG:)[0-9]+(?=\\\\])\", i)\n                        if img is not None:\n                            gambar.append(img.group())\n                            idx.append(n)\n\n                    impath = \"\"\n                    if len(gambar) == 1:\n                        impath = imgs[int(gambar[0])]\n                    elif len(gambar) > 1:\n                        p, i = 0, 0\n                        while p not in K[\"Quit\"] and p not in K[\"Follow\"]:\n                            SCREEN.move(idx[i], x + width // 2 + len(gambar[i]) + 1)\n                            SCREEN.refresh()\n                            curses.curs_set(1)\n                            p = pad.getch()\n                            if p in K[\"ScrollDown\"]:\n                                i += 1\n                            elif p in K[\"ScrollUp\"]:\n                                i -= 1\n                            i = i % len(gambar)\n\n                        curses.curs_set(0)\n                        if p in K[\"Follow\"]:\n                            impath = imgs[int(gambar[i])]\n\n                    if impath != \"\":\n                        if ebook.__class__.__name__ in {\"Epub\", \"Azw3\"}:\n                            impath = dots_path(chpath, impath)\n                        imgnm, imgbstr = ebook.get_img_bytestr(impath)\n                        k = open_media(pad, imgnm, imgbstr)\n                        continue\n                elif (\n                    k in K[\"SwitchColor\"]\n                    and COLORSUPPORT\n                    and countstring in {\"\", \"0\", \"1\", \"2\"}\n                ):\n                    if countstring == \"\":\n                        count_color = curses.pair_number(SCREEN.getbkgd())\n                        if count_color not in {2, 3}:\n                            count_color = 1\n                        count_color = count_color % 3\n                    else:\n                        count_color = count\n                    SCREEN.bkgd(curses.color_pair(count_color + 1))\n                    return 0, width, y, None, \"\"\n                elif k in K[\"AddBookmark\"]:\n                    defbmname_suffix = 1\n                    defbmname = \"Bookmark \" + str(defbmname_suffix)\n                    occupiedbmnames = [\n                        i[0] for i in STATE[\"States\"][ebook.path][\"bmarks\"]\n                    ]\n                    while defbmname in occupiedbmnames:\n                        defbmname_suffix += 1\n                        defbmname = \"Bookmark \" + str(defbmname_suffix)\n                    bmname = input_prompt(\" Add bookmark ({}):\".format(defbmname))\n                    if bmname is not None:\n                        if bmname.strip() == \"\":\n                            bmname = defbmname\n                        STATE[\"States\"][ebook.path][\"bmarks\"].append(\n                            [bmname, index, y, y / totlines]\n                        )\n                elif k in K[\"ShowBookmarks\"]:\n                    if STATE[\"States\"][ebook.path][\"bmarks\"] == []:\n                        k = text_win(\n                            lambda: (\n                                \"Bookmarks\",\n                                \"N/A: Bookmarks are not found in this book.\",\n                                {ord(\"B\")},\n                            )\n                        )()\n                        continue\n                    else:\n                        retk, idxchoice = bookmarks(ebook.path)\n                        if retk is not None:\n                            k = retk\n                            continue\n                        elif idxchoice is not None:\n                            bmtojump = STATE[\"States\"][ebook.path][\"bmarks\"][idxchoice]\n                            return (\n                                bmtojump[1] - index,\n                                width,\n                                bmtojump[2],\n                                bmtojump[3],\n                                \"\",\n                            )\n                elif k in K[\"DefineWord\"] and DICT is not None:\n                    word = input_prompt(\" Define:\")\n                    if word == curses.KEY_RESIZE:\n                        k = word\n                        continue\n                    elif word is not None:\n                        defin = define_word(word)\n                        if defin in WINKEYS:\n                            k = defin\n                            continue\n                elif k in K[\"MarkPosition\"]:\n                    jumnum = pad.getch()\n                    if jumnum in range(49, 58):\n                        JUMPLIST[chr(jumnum)] = [index, width, y, y / totlines]\n                    else:\n                        k = jumnum\n                        continue\n                elif k in K[\"JumpToPosition\"]:\n                    jumnum = pad.getch()\n                    if jumnum in range(49, 58) and chr(jumnum) in JUMPLIST.keys():\n                        tojumpidxdiff = JUMPLIST[chr(jumnum)][0] - index\n                        tojumpy = JUMPLIST[chr(jumnum)][2]\n                        tojumpctg = (\n                            None\n                            if JUMPLIST[chr(jumnum)][1] == width\n                            else JUMPLIST[chr(jumnum)][3]\n                        )\n                        return tojumpidxdiff, width, tojumpy, tojumpctg, \"\"\n                    else:\n                        k = jumnum\n                        continue\n                elif k in K[\"ShowHideProgress\"]:\n                    SHOWPROGRESS = not SHOWPROGRESS\n                elif k == curses.KEY_RESIZE:\n                    savestate(ebook.path, index, width, y, y / totlines)\n                    # stated in pypi windows-curses page:\n                    # to call resize_term right after KEY_RESIZE\n                    if sys.platform == \"win32\":\n                        curses.resize_term(rows, cols)\n                        rows, cols = SCREEN.getmaxyx()\n                    else:\n                        rows, cols = SCREEN.getmaxyx()\n                        curses.resize_term(rows, cols)\n                    if cols < 22 or rows < 12:\n                        sys.exit(\"ERROR: Screen was too small (min 22cols x 12rows).\")\n                    if cols <= width + 4:\n                        return 0, cols - 4, 0, y / totlines, \"\"\n                    else:\n                        return 0, width, y, None, \"\"\n                elif k in K[\"RSVP\"]:\n                    y = rsvp(src_lines, y)\n                countstring = \"\"\n\n            if svline != \"dontsave\":\n                pad.chgat(svline, 0, width, curses.A_UNDERLINE)\n\n            try:\n                SCREEN.clear()\n                SCREEN.addstr(0, 0, countstring)\n                LOCALSUMALLL = SUMALLLETTERS.value if MULTIPROC else SUMALLLETTERS\n                if SHOWPROGRESS and (cols - width - 2) // 2 > 3 and LOCALSUMALLL != 0:\n                    PROGRESS = (\n                        ALLPREVLETTERS[index] + sum(LOCALPCTG[: y + rows - 1])\n                    ) / LOCALSUMALLL\n                    PROGRESSTR = \"{}%\".format(int(PROGRESS * 100))\n                    SCREEN.addstr(0, cols - len(PROGRESSTR), PROGRESSTR)\n                SCREEN.refresh()\n                if totlines - y < rows:\n                    pad.refresh(y, 0, 0, x, totlines - y, x + width)\n                else:\n                    pad.refresh(y, 0, 0, x, rows - 1, x + width)\n            except curses.error:\n                pass\n            k = pad.getch()\n\n            if svline != \"dontsave\":\n                pad.chgat(svline, 0, width, curses.A_NORMAL)\n                svline = \"dontsave\"\n    except KeyboardInterrupt:\n        savestate(ebook.path, index, width, y, y / totlines)\n        sys.exit()\n\n\ndef preread(stdscr, file):\n    global COLORSUPPORT, SHOWPROGRESS, SCREEN\n\n    curses.use_default_colors()\n    try:\n        curses.init_pair(1, -1, -1)\n        curses.init_pair(2, CFG[\"DarkColorFG\"], CFG[\"DarkColorBG\"])\n        curses.init_pair(3, CFG[\"LightColorFG\"], CFG[\"LightColorBG\"])\n        COLORSUPPORT = True\n    except:\n        COLORSUPPORT = False\n\n    SCREEN = stdscr\n\n    SCREEN.keypad(True)\n    curses.curs_set(0)\n    SCREEN.clear()\n    rows, cols = SCREEN.getmaxyx()\n    show_loader(SCREEN)\n\n    ebook = det_ebook_cls(file)\n\n    try:\n        if ebook.path in STATE[\"States\"]:\n            idx = STATE[\"States\"][ebook.path][\"index\"]\n            width = STATE[\"States\"][ebook.path][\"width\"]\n            y = STATE[\"States\"][ebook.path][\"pos\"]\n        else:\n            STATE[\"States\"][ebook.path] = {}\n            STATE[\"States\"][ebook.path][\"bmarks\"] = []\n            idx = 0\n            y = 0\n            width = 80\n        pctg = None\n\n        if cols <= width + 4:\n            width = cols - 4\n            pctg = STATE[\"States\"][ebook.path].get(\"pctg\", None)\n\n        try:\n            ebook.initialize()\n        except Exception as e:\n            sys.exit(\"ERROR: Badly-structured ebook.\\n\" + str(e))\n        find_media_viewer()\n        find_dict_client()\n        parse_keys()\n        SHOWPROGRESS = CFG[\"ShowProgressIndicator\"]\n        count_max_reading_pg(ebook)\n\n        sec = \"\"\n        while True:\n            incr, width, y, pctg, sec = reader(ebook, idx, width, y, pctg, sec)\n            idx += incr\n            show_loader(SCREEN)\n    finally:\n        ebook.cleanup()\n\n\ndef main():\n    termc, termr = shutil.get_terminal_size()\n\n    args = []\n    if sys.argv[1:] != []:\n        args += sys.argv[1:]\n\n    if len({\"-h\", \"--help\"} & set(args)) != 0:\n        print(__doc__.rstrip())\n        sys.exit()\n\n    loadstate()\n\n    if len({\"-v\", \"--version\", \"-V\"} & set(args)) != 0:\n        print(\"Startup file loaded:\")\n        print(CFGFILE)\n        print(STATEFILE)\n        print()\n        print(\"v\" + __version__)\n        print(__license__, \"License\")\n        print(\"Copyright (c) 2019\", __author__)\n        print(__url__)\n        sys.exit()\n\n    if len({\"-d\"} & set(args)) != 0:\n        args.remove(\"-d\")\n        dump = True\n    else:\n        dump = False\n\n    if args == []:\n        file = STATE[\"LastRead\"]\n        if not os.path.isfile(file):\n            # print(__doc__)\n            sys.exit(\"ERROR: Found no last read file.\")\n\n    elif os.path.isfile(args[0]):\n        file = args[0]\n\n    else:\n        file = None\n        todel = []\n        xitmsg = 0\n\n        val = 0\n        for i in STATE[\"States\"].keys():\n            if not os.path.exists(i):\n                todel.append(i)\n            else:\n                match_val = sum(\n                    [\n                        j.size\n                        for j in SM(\n                            None, i.lower(), \" \".join(args).lower()\n                        ).get_matching_blocks()\n                    ]\n                )\n                if match_val >= val:\n                    val = match_val\n                    file = i\n        if val == 0:\n            xitmsg = \"\\nERROR: No matching file found in history.\"\n\n        for i in todel:\n            del STATE[\"States\"][i]\n        with open(STATEFILE, \"w\") as f:\n            json.dump(STATE, f, indent=4)\n\n        if len(args) == 1 and re.match(r\"[0-9]+\", args[0]) is not None:\n            try:\n                file = list(STATE[\"States\"].keys())[int(args[0]) - 1]\n                xitmsg = 0\n            except IndexError:\n                xitmsg = \"ERROR: No matching file found in history.\"\n\n        if xitmsg != 0 or \"-r\" in args:\n            print(\"Reading history:\")\n            dig = len(str(len(STATE[\"States\"].keys()) + 1))\n            tcols = termc - dig - 2\n            for n, i in enumerate(STATE[\"States\"].keys()):\n                p = i.replace(os.getenv(\"HOME\"), \"~\")\n                print(\n                    \"{}{} {}\".format(\n                        str(n + 1).rjust(dig),\n                        \"*\" if i == STATE[\"LastRead\"] else \" \",\n                        truncate(p, \"...\", tcols, 7),\n                    )\n                )\n            sys.exit(xitmsg)\n\n    if dump:\n        ebook = det_ebook_cls(file)\n        try:\n            try:\n                ebook.initialize()\n            except Exception as e:\n                sys.exit(\"ERROR: Badly-structured ebook.\\n\" + str(e))\n            for i in ebook.contents:\n                content = ebook.get_raw_text(i)\n                parser = HTMLtoLines()\n                # try:\n                parser.feed(content)\n                parser.close()\n                # except:\n                #     pass\n                src_lines = parser.get_lines()\n                # sys.stdout.reconfigure(encoding=\"utf-8\")  # Python>=3.7\n                for j in src_lines:\n                    sys.stdout.buffer.write((j + \"\\n\\n\").encode(\"utf-8\"))\n        finally:\n            ebook.cleanup()\n        sys.exit()\n\n    else:\n        if termc < 22 or termr < 12:\n            sys.exit(\"ERROR: Screen was too small (min 22cols x 12rows).\")\n        curses.wrapper(preread, file)\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "setup.py",
    "content": "import pathlib\nfrom setuptools import setup, find_packages\n\n# The directory containing this file\nHERE = pathlib.Path(__file__).parent\n\n# The text of the README file\nREADME = (HERE / \"README.md\").read_text()\n\nsetup(\n    name=\"shirah_reader\",\n    version=\"1.0.0\",\n    long_description=README,\n    long_description_content_type=\"text/markdown\",\n    url=\"https://github.com/hallicopter/shirah-reader\",\n    author=\"Hallicopter\",\n    author_email=\"advait.raykar@gmail.com\",\n    license=\"MIT\",\n    classifiers=[\n        \"Programming Language :: Python :: 3\",\n        \"Programming Language :: Python :: 3.7\",\n    ],\n    packages=find_packages(),\n    include_package_data=True,\n    install_requires=[\"EbookLib\", \"beautifulsoup4\", \"syllables\", \"termcolor\"],\n    entry_points={\n        \"console_scripts\": [\n            \"shirah = shirah_reader.__main__:main\",\n        ]\n    },\n)\n"
  },
  {
    "path": "shirah_reader/__init__.py",
    "content": "# __init__.py\n\n# Version of the shirah-reader package\n__version__ = \"1.0.0\"\n"
  },
  {
    "path": "shirah_reader/__main__.py",
    "content": "#!/usr/bin/env python3\n\n\"\"\"\n Copyright (C) 2020\n - Original author Benawi Adha\n - Modified by Advait Raykar (github.com/hallicopter)\n\n This program is free software; you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation; either version 2 of the License, or\n (at your option) any later version.\n\n This program is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with this program; if not, write to the Free Software\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA\n\nUsages:\n    shirah             read last epub\n    shirah EPUBFILE    read EPUBFILE\n    shirah STRINGS     read matched STRINGS from history\n    shirah NUMBER      read file from history\n                       with associated NUMBER\n\nOptions:\n    -r              print reading history\n    -d              dump epub\n    -h, --help      print short, long help\n\"\"\"\n\n\nimport base64\nimport curses\nimport zipfile\nimport sys\nimport re\nimport os\nimport textwrap\nimport json\nimport tempfile\nimport shutil\nimport subprocess\nimport multiprocessing\nimport xml.etree.ElementTree as ET\nfrom urllib.parse import unquote\nfrom html import unescape\nfrom html.parser import HTMLParser\nfrom difflib import SequenceMatcher as SM\nfrom functools import wraps\nimport time\nimport syllables\n\ntry:\n    import mobi\n\n    MOBISUPPORT = True\nexcept ModuleNotFoundError:\n    MOBISUPPORT = False\n\n# -1 is default terminal fg/bg colors\nCFG = {\n    \"DefaultViewer\": \"auto\",\n    \"DictionaryClient\": \"auto\",\n    \"ShowProgressIndicator\": True,\n    \"DarkColorFG\": 252,\n    \"DarkColorBG\": 235,\n    \"LightColorFG\": 238,\n    \"LightColorBG\": 253,\n    \"Keys\": {\n        \"ScrollUp\": \"k\",\n        \"ScrollDown\": \"j\",\n        \"PageUp\": \"h\",\n        \"PageDown\": \"l\",\n        \"HalfScreenUp\": \"^u\",\n        \"HalfScreenDown\": \"C-d\",\n        \"NextChapter\": \"n\",\n        \"PrevChapter\": \"p\",\n        \"BeginningOfCh\": \"g\",\n        \"EndOfCh\": \"G\",\n        \"Shrink\": \"-\",\n        \"Enlarge\": \"+\",\n        \"SetWidth\": \"=\",\n        \"Metadata\": \"M\",\n        \"DefineWord\": \"d\",\n        \"TableOfContents\": \"t\",\n        \"Follow\": \"f\",\n        \"OpenImage\": \"o\",\n        \"RegexSearch\": \"/\",\n        \"ShowHideProgress\": \"s\",\n        \"MarkPosition\": \"m\",\n        \"JumpToPosition\": \"`\",\n        \"AddBookmark\": \"b\",\n        \"ShowBookmarks\": \"B\",\n        \"Quit\": \"q\",\n        \"Help\": \"?\",\n        \"SwitchColor\": \"c\",\n    },\n}\nSTATE = {\"LastRead\": \"\", \"States\": {}}\n# default keys\nK = {\n    \"ScrollUp\": {curses.KEY_UP},\n    \"ScrollDown\": {curses.KEY_DOWN},\n    \"PageUp\": {curses.KEY_PPAGE, curses.KEY_LEFT},\n    \"PageDown\": {curses.KEY_NPAGE, ord(\" \"), curses.KEY_RIGHT},\n    \"BeginningOfCh\": {curses.KEY_HOME},\n    \"EndOfCh\": {curses.KEY_END},\n    \"TableOfContents\": {9, ord(\"\\t\")},\n    \"Follow\": {10},\n    \"Quit\": {3, 27, 304},\n    \"RSVP\": {ord(\"r\")},\n}\nWINKEYS = set()\nCFGFILE = \"\"\nSTATEFILE = \"\"\nCOLORSUPPORT = False\nLINEPRSRV = 0  # 2\nSEARCHPATTERN = None\nVWR = None\nDICT = None\nSCREEN = None\nJUMPLIST = {}\nSHOWPROGRESS = CFG[\"ShowProgressIndicator\"]\nMULTIPROC = False if multiprocessing.cpu_count() == 1 else True\nALLPREVLETTERS = []\nSUMALLLETTERS = 0\nPROC_COUNTLETTERS = None\nwpm = 300\n\n\nclass Epub:\n    NS = {\n        \"DAISY\": \"http://www.daisy.org/z3986/2005/ncx/\",\n        \"OPF\": \"http://www.idpf.org/2007/opf\",\n        \"CONT\": \"urn:oasis:names:tc:opendocument:xmlns:container\",\n        \"XHTML\": \"http://www.w3.org/1999/xhtml\",\n        \"EPUB\": \"http://www.idpf.org/2007/ops\",\n    }\n\n    def __init__(self, fileepub):\n        self.path = os.path.abspath(fileepub)\n        self.file = zipfile.ZipFile(fileepub, \"r\")\n\n    def get_meta(self):\n        meta = []\n        # why self.file.read(self.rootfile) problematic\n        cont = ET.fromstring(self.file.open(self.rootfile).read())\n        for i in cont.findall(\"OPF:metadata/*\", self.NS):\n            if i.text is not None:\n                meta.append([re.sub(\"{.*?}\", \"\", i.tag), i.text])\n        return meta\n\n    def initialize(self):\n        cont = ET.parse(self.file.open(\"META-INF/container.xml\"))\n        self.rootfile = cont.find(\"CONT:rootfiles/CONT:rootfile\", self.NS).attrib[\n            \"full-path\"\n        ]\n        self.rootdir = (\n            os.path.dirname(self.rootfile) + \"/\"\n            if os.path.dirname(self.rootfile) != \"\"\n            else \"\"\n        )\n        cont = ET.parse(self.file.open(self.rootfile))\n        # EPUB3\n        self.version = cont.getroot().get(\"version\")\n        if self.version == \"2.0\":\n            # \"OPF:manifest/*[@id='ncx']\"\n            self.toc = self.rootdir + cont.find(\n                \"OPF:manifest/*[@media-type='application/x-dtbncx+xml']\", self.NS\n            ).get(\"href\")\n        elif self.version == \"3.0\":\n            self.toc = self.rootdir + cont.find(\n                \"OPF:manifest/*[@properties='nav']\", self.NS\n            ).get(\"href\")\n\n        self.contents = []\n        self.toc_entries = [[], [], []]\n\n        # cont = ET.parse(self.file.open(self.rootfile)).getroot()\n        manifest = []\n        for i in cont.findall(\"OPF:manifest/*\", self.NS):\n            # EPUB3\n            # if i.get(\"id\") != \"ncx\" and i.get(\"properties\") != \"nav\":\n            if (\n                i.get(\"media-type\") != \"application/x-dtbncx+xml\"\n                and i.get(\"properties\") != \"nav\"\n            ):\n                manifest.append([i.get(\"id\"), i.get(\"href\")])\n\n        spine, contents = [], []\n        for i in cont.findall(\"OPF:spine/*\", self.NS):\n            spine.append(i.get(\"idref\"))\n        for i in spine:\n            for j in manifest:\n                if i == j[0]:\n                    self.contents.append(self.rootdir + unquote(j[1]))\n                    contents.append(unquote(j[1]))\n                    manifest.remove(j)\n                    # TODO: test is break necessary\n                    break\n\n        toc = ET.parse(self.file.open(self.toc)).getroot()\n        # EPUB3\n        if self.version == \"2.0\":\n            navPoints = toc.findall(\"DAISY:navMap//DAISY:navPoint\", self.NS)\n        elif self.version == \"3.0\":\n            navPoints = toc.findall(\n                \"XHTML:body//XHTML:nav[@EPUB:type='toc']//XHTML:a\", self.NS\n            )\n        for i in navPoints:\n            if self.version == \"2.0\":\n                src = i.find(\"DAISY:content\", self.NS).get(\"src\")\n                name = i.find(\"DAISY:navLabel/DAISY:text\", self.NS).text\n            elif self.version == \"3.0\":\n                src = i.get(\"href\")\n                name = \"\".join(list(i.itertext()))\n            src = src.split(\"#\")\n            try:\n                idx = contents.index(unquote(src[0]))\n            except ValueError:\n                continue\n            self.toc_entries[0].append(name)\n            self.toc_entries[1].append(idx)\n            if len(src) == 2:\n                self.toc_entries[2].append(src[1])\n            elif len(src) == 1:\n                self.toc_entries[2].append(\"\")\n\n    def get_raw_text(self, chpath):\n        # using try-except block to catch\n        # zlib.error: Error -3 while decompressing data: invalid distance too far back\n        # caused by forking PROC_COUNTLETTERS\n        while True:\n            try:\n                content = self.file.open(chpath).read()\n                break\n            except:\n                continue\n        return content.decode(\"utf-8\")\n\n    def get_img_bytestr(self, impath):\n        return impath, self.file.read(impath)\n\n    def cleanup(self):\n        return\n\n\nclass Mobi(Epub):\n    def __init__(self, filemobi):\n        self.path = os.path.abspath(filemobi)\n        self.file, _ = mobi.extract(filemobi)\n\n    def get_meta(self):\n        meta = []\n        # why self.file.read(self.rootfile) problematic\n        with open(os.path.join(self.rootdir, \"content.opf\")) as f:\n            cont = ET.parse(f).getroot()\n        for i in cont.findall(\"OPF:metadata/*\", self.NS):\n            if i.text is not None:\n                meta.append([re.sub(\"{.*?}\", \"\", i.tag), i.text])\n        return meta\n\n    def initialize(self):\n        self.rootdir = os.path.join(self.file, \"mobi7\")\n        self.toc = os.path.join(self.rootdir, \"toc.ncx\")\n        self.version = \"2.0\"\n\n        self.contents = []\n        self.toc_entries = [[], [], []]\n\n        with open(os.path.join(self.rootdir, \"content.opf\")) as f:\n            cont = ET.parse(f).getroot()\n        manifest = []\n        for i in cont.findall(\"OPF:manifest/*\", self.NS):\n            # EPUB3\n            # if i.get(\"id\") != \"ncx\" and i.get(\"properties\") != \"nav\":\n            if (\n                i.get(\"media-type\") != \"application/x-dtbncx+xml\"\n                and i.get(\"properties\") != \"nav\"\n            ):\n                manifest.append([i.get(\"id\"), i.get(\"href\")])\n\n        spine, contents = [], []\n        for i in cont.findall(\"OPF:spine/*\", self.NS):\n            spine.append(i.get(\"idref\"))\n        for i in spine:\n            for j in manifest:\n                if i == j[0]:\n                    self.contents.append(os.path.join(self.rootdir, unquote(j[1])))\n                    contents.append(unquote(j[1]))\n                    manifest.remove(j)\n                    # TODO: test is break necessary\n                    break\n\n        with open(self.toc) as f:\n            toc = ET.parse(f).getroot()\n        # EPUB3\n        if self.version == \"2.0\":\n            navPoints = toc.findall(\"DAISY:navMap//DAISY:navPoint\", self.NS)\n        elif self.version == \"3.0\":\n            navPoints = toc.findall(\n                \"XHTML:body//XHTML:nav[@EPUB:type='toc']//XHTML:a\", self.NS\n            )\n        for i in navPoints:\n            if self.version == \"2.0\":\n                src = i.find(\"DAISY:content\", self.NS).get(\"src\")\n                name = i.find(\"DAISY:navLabel/DAISY:text\", self.NS).text\n            elif self.version == \"3.0\":\n                src = i.get(\"href\")\n                name = \"\".join(list(i.itertext()))\n            src = src.split(\"#\")\n            try:\n                idx = contents.index(unquote(src[0]))\n            except ValueError:\n                continue\n            self.toc_entries[0].append(name)\n            self.toc_entries[1].append(idx)\n            if len(src) == 2:\n                self.toc_entries[2].append(src[1])\n            elif len(src) == 1:\n                self.toc_entries[2].append(\"\")\n\n    def get_raw_text(self, chpath):\n        # using try-except block to catch\n        # zlib.error: Error -3 while decompressing data: invalid distance too far back\n        # caused by forking PROC_COUNTLETTERS\n        while True:\n            try:\n                with open(chpath) as f:\n                    content = f.read()\n                break\n            except:\n                continue\n        # return content.decode(\"utf-8\")\n        return content\n\n    def cleanup(self):\n        shutil.rmtree(self.file)\n        return\n\n\nclass Azw3(Epub):\n    def __init__(self, fileepub):\n        self.path = os.path.abspath(fileepub)\n        self.tmpdir, self.tmpepub = mobi.extract(fileepub)\n        self.file = zipfile.ZipFile(self.tmpepub, \"r\")\n\n    def cleanup(self):\n        shutil.rmtree(self.tmpdir)\n        return\n\n\nclass FictionBook:\n    NS = {\"FB2\": \"http://www.gribuser.ru/xml/fictionbook/2.0\"}\n\n    def __init__(self, filefb):\n        self.path = os.path.abspath(filefb)\n        self.file = filefb\n\n    def get_meta(self):\n        desc = self.root.find(\"FB2:description\", self.NS)\n        alltags = desc.findall(\"*/*\")\n        return [[re.sub(\"{.*?}\", \"\", i.tag), \" \".join(i.itertext())] for i in alltags]\n\n    def initialize(self):\n        cont = ET.parse(self.file)\n        self.root = cont.getroot()\n\n        self.contents = []\n        self.toc_entries = [[], [], []]\n\n        self.contents = self.root.findall(\"FB2:body/*\", self.NS)\n        # TODO\n        for n, i in enumerate(self.contents):\n            title = i.find(\"FB2:title\", self.NS)\n            if title is not None:\n                self.toc_entries[0].append(\"\".join(title.itertext()))\n                self.toc_entries[1].append(n)\n                self.toc_entries[2].append(\"\")\n\n    def get_raw_text(self, node):\n        ET.register_namespace(\"\", \"http://www.gribuser.ru/xml/fictionbook/2.0\")\n        # sys.exit(ET.tostring(node, encoding=\"utf8\", method=\"html\").decode(\"utf-8\").replace(\"ns1:\",\"\"))\n        return (\n            ET.tostring(node, encoding=\"utf8\", method=\"html\")\n            .decode(\"utf-8\")\n            .replace(\"ns1:\", \"\")\n        )\n\n    def get_img_bytestr(self, imgid):\n        imgid = imgid.replace(\"#\", \"\")\n        img = self.root.find(\"*[@id='{}']\".format(imgid))\n        imgtype = img.get(\"content-type\").split(\"/\")[1]\n        return imgid + \".\" + imgtype, base64.b64decode(img.text)\n\n    def cleanup(self):\n        return\n\n\nclass HTMLtoLines(HTMLParser):\n    para = {\"p\", \"div\"}\n    inde = {\"q\", \"dt\", \"dd\", \"blockquote\"}\n    pref = {\"pre\"}\n    bull = {\"li\"}\n    hide = {\"script\", \"style\", \"head\"}\n\n    # hide = {\"script\", \"style\", \"head\", \", \"sub}\n\n    def __init__(self, sects={\"\"}):\n        HTMLParser.__init__(self)\n        self.text = [\"\"]\n        self.imgs = []\n        self.ishead = False\n        self.isinde = False\n        self.isbull = False\n        self.ispref = False\n        self.ishidden = False\n        self.idhead = set()\n        self.idinde = set()\n        self.idbull = set()\n        self.idpref = set()\n        self.sects = sects\n\n    def handle_starttag(self, tag, attrs):\n        if re.match(\"h[1-6]\", tag) is not None:\n            self.ishead = True\n        elif tag in self.inde:\n            self.isinde = True\n        elif tag in self.pref:\n            self.ispref = True\n        elif tag in self.bull:\n            self.isbull = True\n        elif tag in self.hide:\n            self.ishidden = True\n        elif tag == \"sup\":\n            self.text[-1] += \"^{\"\n        elif tag == \"sub\":\n            self.text[-1] += \"_{\"\n        elif tag == \"image\":\n            for i in attrs:\n                # if i[0] == \"xlink:href\":\n                if i[0].endswith(\"href\"):\n                    self.text.append(\"[IMG:{}]\".format(len(self.imgs)))\n                    self.imgs.append(unquote(i[1]))\n        if self.sects != {\"\"}:\n            for i in attrs:\n                if i[0] == \"id\" and i[1] in self.sects:\n                    self.text[-1] += \" (#\" + i[1] + \") \"\n\n    def handle_startendtag(self, tag, attrs):\n        if tag == \"br\":\n            self.text += [\"\"]\n        elif tag in {\"img\", \"image\"}:\n            for i in attrs:\n                if (tag == \"img\" and i[0] == \"src\") or (\n                    tag == \"image\" and i[0] == \"xlink:href\"\n                ):\n                    self.text.append(\"[IMG:{}]\".format(len(self.imgs)))\n                    self.imgs.append(unquote(i[1]))\n                    self.text.append(\"\")\n        # sometimes attribute \"id\" is inside \"startendtag\"\n        # especially html from mobi module (kindleunpack fork)\n        if self.sects != {\"\"}:\n            for i in attrs:\n                if i[0] == \"id\" and i[1] in self.sects:\n                    self.text[-1] += \" (#\" + i[1] + \") \"\n\n    def handle_endtag(self, tag):\n        if re.match(\"h[1-6]\", tag) is not None:\n            self.text.append(\"\")\n            self.text.append(\"\")\n            self.ishead = False\n        elif tag in self.para:\n            self.text.append(\"\")\n        elif tag in self.hide:\n            self.ishidden = False\n        elif tag in self.inde:\n            if self.text[-1] != \"\":\n                self.text.append(\"\")\n            self.isinde = False\n        elif tag in self.pref:\n            if self.text[-1] != \"\":\n                self.text.append(\"\")\n            self.ispref = False\n        elif tag in self.bull:\n            if self.text[-1] != \"\":\n                self.text.append(\"\")\n            self.isbull = False\n        elif tag in {\"sub\", \"sup\"}:\n            self.text[-1] += \"}\"\n        elif tag == \"image\":\n            self.text.append(\"\")\n\n    def handle_data(self, raw):\n        if raw and not self.ishidden:\n            if self.text[-1] == \"\":\n                tmp = raw.lstrip()\n            else:\n                tmp = raw\n            if self.ispref:\n                line = unescape(tmp)\n            else:\n                line = unescape(re.sub(r\"\\s+\", \" \", tmp))\n            self.text[-1] += line\n            if self.ishead:\n                self.idhead.add(len(self.text) - 1)\n            elif self.isbull:\n                self.idbull.add(len(self.text) - 1)\n            elif self.isinde:\n                self.idinde.add(len(self.text) - 1)\n            elif self.ispref:\n                self.idpref.add(len(self.text) - 1)\n\n    def get_lines(self, width=0):\n        text, sect = [], {}\n        if width == 0:\n            return self.text\n        for n, i in enumerate(self.text):\n            findsect = re.search(r\"(?<= \\(#).*?(?=\\) )\", i)\n            if findsect is not None and findsect.group() in self.sects:\n                i = i.replace(\" (#\" + findsect.group() + \") \", \"\")\n                sect[findsect.group()] = len(text)\n            if n in self.idhead:\n                text += [i.rjust(width // 2 + len(i) // 2)] + [\"\"]\n            elif n in self.idinde:\n                text += [\"   \" + j for j in textwrap.wrap(i, width - 3)] + [\"\"]\n            elif n in self.idbull:\n                tmp = textwrap.wrap(i, width - 3)\n                text += [\" - \" + j if j == tmp[0] else \"   \" + j for j in tmp] + [\"\"]\n            elif n in self.idpref:\n                tmp = i.splitlines()\n                wraptmp = []\n                for line in tmp:\n                    wraptmp += [j for j in textwrap.wrap(line, width - 6)]\n                text += [\"   \" + j for j in wraptmp] + [\"\"]\n            else:\n                text += textwrap.wrap(i, width) + [\"\"]\n        return text, self.imgs, sect\n\n\nclass Board:\n    MAXCHUNKS = 32000  # lines\n\n    def __init__(self, totlines, width):\n        self.chunks = [\n            self.MAXCHUNKS * (i + 1) - 1 for i in range(totlines // self.MAXCHUNKS)\n        ]\n        self.chunks += (\n            []\n            if totlines % self.MAXCHUNKS == 0\n            else [\n                totlines % self.MAXCHUNKS\n                + (0 if self.chunks == [] else self.chunks[-1])\n            ]\n        )  # -1\n        self.pad = curses.newpad(min([self.MAXCHUNKS, totlines]), width)\n        self.pad.keypad(True)\n        # self.current_chunk = 0\n        self.y = 0\n        self.width = width\n\n    def feed(self, textlist):\n        self.text = textlist\n\n    def getch(self):\n        return self.pad.getch()\n\n    def bkgd(self, bg):\n        self.pad.bkgd(SCREEN.getbkgd())\n\n    def find_chunkidx(self, y):\n        for n, i in enumerate(self.chunks):\n            if y <= i:\n                return n\n\n    def paint_text(self, chunkidx=0):\n        self.pad.clear()\n        start_chunk = 0 if chunkidx == 0 else self.chunks[chunkidx - 1] + 1\n        end_chunk = self.chunks[chunkidx]\n        for n, i in enumerate(self.text[start_chunk : end_chunk + 1]):\n            if re.search(\"\\\\[IMG:[0-9]+\\\\]\", i):\n                self.pad.addstr(\n                    n, self.width // 2 - len(i) // 2 + 1, i, curses.A_REVERSE\n                )\n            else:\n                self.pad.addstr(n, 0, i)\n        # chapter suffix\n        ch_suffix = \"***\"  # \"\\u3064\\u3065\\u304f\" つづく\n        try:\n            self.pad.addstr(n + 1, (self.width - len(ch_suffix)) // 2 + 1, ch_suffix)\n        except curses.error:\n            pass\n\n    def chgat(self, y, x, n, attr):\n        chunkidx = self.find_chunkidx(y)\n        start_chunk = 0 if chunkidx == 0 else self.chunks[chunkidx - 1] + 1\n        end_chunk = self.chunks[chunkidx]\n        if y in range(start_chunk, end_chunk + 1):\n            self.pad.chgat(y % self.MAXCHUNKS, x, n, attr)\n\n    def getbkgd(self):\n        return self.pad.getbkgd()\n\n    def refresh(self, y, b, c, d, e, f):\n        chunkidx = self.find_chunkidx(y)\n        if chunkidx != self.find_chunkidx(self.y):\n            self.paint_text(chunkidx)\n        # TODO: not modulo by self.MAXCHUNKS but self.pad.height\n        self.pad.refresh(y % self.MAXCHUNKS, b, c, d, e, f)\n        self.y = y\n\n\ndef text_win(textfunc):\n    @wraps(textfunc)\n    def wrapper(*args, **kwargs):\n        rows, cols = SCREEN.getmaxyx()\n        hi, wi = rows - 4, cols - 4\n        Y, X = 2, 2\n        textw = curses.newwin(hi, wi, Y, X)\n        if COLORSUPPORT:\n            textw.bkgd(SCREEN.getbkgd())\n\n        title, raw_texts, key = textfunc(*args, **kwargs)\n\n        if len(title) > cols - 8:\n            title = title[: cols - 8]\n\n        texts = []\n        for i in raw_texts.splitlines():\n            texts += textwrap.wrap(i, wi - 6, drop_whitespace=False)\n\n        textw.box()\n        textw.keypad(True)\n        textw.addstr(1, 2, title)\n        textw.addstr(2, 2, \"-\" * len(title))\n        key_textw = 0\n\n        totlines = len(texts)\n\n        pad = curses.newpad(totlines, wi - 2)\n        if COLORSUPPORT:\n            pad.bkgd(SCREEN.getbkgd())\n\n        pad.keypad(True)\n        for n, i in enumerate(texts):\n            pad.addstr(n, 0, i)\n        y = 0\n        textw.refresh()\n        pad.refresh(y, 0, Y + 4, X + 4, rows - 5, cols - 6)\n        padhi = rows - 8 - Y\n\n        while key_textw not in K[\"Quit\"] | key:\n            if key_textw in K[\"ScrollUp\"] and y > 0:\n                y -= 1\n            elif key_textw in K[\"ScrollDown\"] and y < totlines - hi + 6:\n                y += 1\n            elif key_textw in K[\"PageUp\"]:\n                y = pgup(y, padhi)\n            elif key_textw in K[\"PageDown\"]:\n                y = pgdn(y, totlines, padhi)\n            elif key_textw in K[\"BeginningOfCh\"]:\n                y = 0\n            elif key_textw in K[\"EndOfCh\"]:\n                y = pgend(totlines, padhi)\n            elif key_textw in WINKEYS - key:\n                textw.clear()\n                textw.refresh()\n                return key_textw\n            pad.refresh(y, 0, 6, 5, rows - 5, cols - 5)\n            key_textw = textw.getch()\n\n        textw.clear()\n        textw.refresh()\n        return\n\n    return wrapper\n\n\ndef choice_win(allowdel=False):\n    def inner_f(listgen):\n        @wraps(listgen)\n        def wrapper(*args, **kwargs):\n            rows, cols = SCREEN.getmaxyx()\n            hi, wi = rows - 4, cols - 4\n            Y, X = 2, 2\n            chwin = curses.newwin(hi, wi, Y, X)\n            if COLORSUPPORT:\n                chwin.bkgd(SCREEN.getbkgd())\n\n            title, ch_list, index, key = listgen(*args, **kwargs)\n\n            if len(title) > cols - 8:\n                title = title[: cols - 8]\n\n            chwin.box()\n            chwin.keypad(True)\n            chwin.addstr(1, 2, title)\n            chwin.addstr(2, 2, \"-\" * len(title))\n            if allowdel:\n                chwin.addstr(3, 2, \"HINT: Press 'd' to delete.\")\n            key_chwin = 0\n\n            totlines = len(ch_list)\n            chwin.refresh()\n            pad = curses.newpad(totlines, wi - 2)\n            if COLORSUPPORT:\n                pad.bkgd(SCREEN.getbkgd())\n\n            pad.keypad(True)\n\n            padhi = rows - 5 - Y - 4 + 1 - (1 if allowdel else 0)\n            # padhi = rows - 5 - Y - 4 + 1 - 1\n            y = 0\n            if index in range(padhi // 2, totlines - padhi // 2):\n                y = index - padhi // 2 + 1\n            span = []\n\n            for n, i in enumerate(ch_list):\n                # strs = \"  \" + str(n+1).rjust(d) + \" \" + i[0]\n                strs = \"  \" + i\n                strs = strs[0 : wi - 3]\n                pad.addstr(n, 0, strs)\n                span.append(len(strs))\n\n            countstring = \"\"\n            while key_chwin not in K[\"Quit\"] | key:\n                if countstring == \"\":\n                    count = 1\n                else:\n                    count = int(countstring)\n                if key_chwin in range(48, 58):  # i.e., k is a numeral\n                    countstring = countstring + chr(key_chwin)\n                else:\n                    if key_chwin in K[\"ScrollUp\"] or key_chwin in K[\"PageUp\"]:\n                        index -= count\n                        if index < 0:\n                            index = 0\n                    elif key_chwin in K[\"ScrollDown\"] or key_chwin in K[\"PageDown\"]:\n                        index += count\n                        if index + 1 >= totlines:\n                            index = totlines - 1\n                    elif key_chwin in K[\"Follow\"]:\n                        chwin.clear()\n                        chwin.refresh()\n                        return None, index, None\n                    # elif key_chwin in K[\"PageUp\"]:\n                    #     index -= 3\n                    #     if index < 0:\n                    #         index = 0\n                    # elif key_chwin in K[\"PageDown\"]:\n                    #     index += 3\n                    #     if index >= totlines:\n                    #         index = totlines - 1\n                    elif key_chwin in K[\"BeginningOfCh\"]:\n                        index = 0\n                    elif key_chwin in K[\"EndOfCh\"]:\n                        index = totlines - 1\n                    elif key_chwin == ord(\"D\") and allowdel:\n                        return None, (0 if index == 0 else index - 1), index\n                        # chwin.redrawwin()\n                        # chwin.refresh()\n                    elif key_chwin == ord(\"d\") and allowdel:\n                        resk, resp, _ = choice_win()(\n                            lambda: (\n                                \"Delete '{}'?\".format(ch_list[index]),\n                                [\"(Y)es\", \"(N)o\"],\n                                0,\n                                {ord(\"n\")},\n                            )\n                        )()\n                        if resk is not None:\n                            key_chwin = resk\n                            continue\n                        elif resp == 0:\n                            return None, (0 if index == 0 else index - 1), index\n                        chwin.redrawwin()\n                        chwin.refresh()\n                    elif key_chwin in {\n                        ord(i) for i in [\"Y\", \"y\", \"N\", \"n\"]\n                    } and ch_list == [\"(Y)es\", \"(N)o\"]:\n                        if key_chwin in {ord(\"Y\"), ord(\"y\")}:\n                            return None, 0, None\n                        else:\n                            return None, 1, None\n                    elif key_chwin in WINKEYS - key:\n                        chwin.clear()\n                        chwin.refresh()\n                        return key_chwin, index, None\n                    countstring = \"\"\n\n                while index not in range(y, y + padhi):\n                    if index < y:\n                        y -= 1\n                    else:\n                        y += 1\n\n                for n in range(totlines):\n                    att = curses.A_REVERSE if index == n else curses.A_NORMAL\n                    pre = \">>\" if index == n else \"  \"\n                    pad.addstr(n, 0, pre)\n                    pad.chgat(n, 0, span[n], pad.getbkgd() | att)\n\n                pad.refresh(\n                    y, 0, Y + 4 + (1 if allowdel else 0), X + 4, rows - 5, cols - 6\n                )\n                # pad.refresh(y, 0, Y+5, X+4, rows - 5, cols - 6)\n                key_chwin = chwin.getch()\n\n            chwin.clear()\n            chwin.refresh()\n            return None, None, None\n\n        return wrapper\n\n    return inner_f\n\n\ndef show_loader(scr):\n    scr.clear()\n    rows, cols = scr.getmaxyx()\n    scr.addstr((rows - 1) // 2, (cols - 1) // 2, \"\\u231B\")\n    # scr.addstr(((rows-2)//2)+1, (cols-len(msg))//2, msg)\n    scr.refresh()\n\n\ndef loadstate():\n    global CFG, STATE, CFGFILE, STATEFILE\n    prefix = \"\"\n    if os.getenv(\"HOME\") is not None:\n        homedir = os.getenv(\"HOME\")\n        if os.path.isdir(os.path.join(homedir, \".config\")):\n            prefix = os.path.join(homedir, \".config\", \"shirah\")\n        else:\n            prefix = os.path.join(homedir, \".shirah\")\n    elif os.getenv(\"USERPROFILE\") is not None:\n        prefix = os.path.join(os.getenv(\"USERPROFILE\"), \".shirah\")\n    else:\n        CFGFILE = os.devnull\n        STATEFILE = os.devnull\n    os.makedirs(prefix, exist_ok=True)\n    CFGFILE = os.path.join(prefix, \"config.json\")\n    STATEFILE = os.path.join(prefix, \"state.json\")\n\n    try:\n        with open(CFGFILE) as f:\n            CFG = json.load(f)\n        with open(STATEFILE) as f:\n            STATE = json.load(f)\n    except FileNotFoundError:\n        pass\n\n\ndef parse_keys():\n    global WINKEYS\n    for i in CFG[\"Keys\"].keys():\n        parsedk = CFG[\"Keys\"][i]\n        if len(parsedk) == 1:\n            parsedk = ord(parsedk)\n        elif parsedk[:-1] in {\"^\", \"C-\"}:\n            parsedk = ord(parsedk[-1]) - 96  # Reference: ASCII chars\n        else:\n            sys.exit(\"ERROR: Keybindings {}\".format(i))\n\n        try:\n            K[i].add(parsedk)\n        except KeyError:\n            K[i] = {parsedk}\n    WINKEYS = (\n        {curses.KEY_RESIZE}\n        | K[\"Metadata\"]\n        | K[\"Help\"]\n        | K[\"TableOfContents\"]\n        | K[\"ShowBookmarks\"]\n    )\n\n\ndef savestate(file, index, width, pos, pctg):\n    with open(CFGFILE, \"w\") as f:\n        json.dump(CFG, f, indent=2)\n    STATE[\"LastRead\"] = file\n    STATE[\"States\"][file][\"index\"] = index\n    STATE[\"States\"][file][\"width\"] = width\n    STATE[\"States\"][file][\"pos\"] = pos\n    STATE[\"States\"][file][\"pctg\"] = pctg\n    with open(STATEFILE, \"w\") as f:\n        json.dump(STATE, f, indent=4)\n\n    if MULTIPROC:\n        # PROC_COUNTLETTERS.terminate()\n        # PROC_COUNTLETTERS.kill()\n        # PROC_COUNTLETTERS.join()\n        try:\n            PROC_COUNTLETTERS.kill()\n        except AttributeError:\n            PROC_COUNTLETTERS.terminate()\n\n\ndef pgup(pos, winhi, preservedline=0, c=1):\n    if pos >= (winhi - preservedline) * c:\n        return pos - (winhi + preservedline) * c\n    else:\n        return 0\n\n\ndef pgdn(pos, tot, winhi, preservedline=0, c=1):\n    if pos + (winhi * c) <= tot - winhi:\n        return pos + (winhi * c)\n    else:\n        pos = tot - winhi\n        if pos < 0:\n            return 0\n        return pos\n\n\ndef pgend(tot, winhi):\n    if tot - winhi >= 0:\n        return tot - winhi\n    else:\n        return 0\n\n\n@choice_win()\ndef toc(src, index):\n    return \"Table of Contents\", src, index, K[\"TableOfContents\"]\n\n\ndef rsvp(content, y):\n    global wpm\n    curses.start_color()\n    curses.use_default_colors()\n    for i in range(0, curses.COLORS):\n        curses.init_pair(i + 1, i, -1)\n    rows, cols = SCREEN.getmaxyx()\n    hi, wi = rows - 4, cols - 4\n    Y, X = 2, 2\n    chwin = curses.newwin(hi, wi, Y, X)\n    if COLORSUPPORT:\n        chwin.bkgd(SCREEN.getbkgd())\n\n    for i, text in enumerate(content[y:]):\n        try:\n            time.sleep(0.1)\n            for j, word in enumerate(text.strip().split()):\n                wps = wpm / 60.0\n                chwin.clear()\n                word_len = len(word)\n                t_wait_sec = (1 / wps) * (1 + syllables.estimate(word) ** 2 / 100)\n                highlight_letter_index = min(4, round(word_len / 2))\n                spaces = abs(4 - highlight_letter_index) * \" \"\n                wi_cn = round(wi / 2) - 5\n                chwin.addstr(\n                    round(hi / 2), wi_cn + len(spaces), word[:highlight_letter_index]\n                )\n                chwin.addstr(\n                    round(hi / 2),\n                    wi_cn + len(word[:highlight_letter_index]) + len(spaces),\n                    word[highlight_letter_index],\n                    curses.color_pair(197),\n                )\n                chwin.addstr(\n                    round(hi / 2),\n                    wi_cn + len(word[:highlight_letter_index]) + 1 + len(spaces),\n                    word[highlight_letter_index + 1 :],\n                )\n                if \".\" in word:\n                    time.sleep(0.2)\n                if (\n                    \",\" in word\n                    or \"'\" in word\n                    or '\"' in word\n                    or \"`\" in word\n                    or \"-\" in word\n                    or \"(\" in word\n                    or \")\" in word\n                    or \":\" in word\n                ):\n                    time.sleep(0.15)\n                time.sleep(t_wait_sec)\n                chwin.refresh()\n        except KeyboardInterrupt as e:\n            keybinding_msg = \"Current wpm: \" + str(wpm) + \" Enter new wpm: \"\n            try:\n                option = input_prompt(keybinding_msg)\n                wpm = int(option)\n            except ValueError as e:\n                return y + i\n            except KeyboardInterrupt as e:\n                return y + i\n\n    return None, None, None\n\n\n@text_win\ndef meta(ebook):\n    mdata = \"[File Info]\\nPATH: {}\\nSIZE: {} MB\\n \\n[Book Info]\\n\".format(\n        ebook.path, round(os.path.getsize(ebook.path) / 1024 ** 2, 2)\n    )\n    for i in ebook.get_meta():\n        data = re.sub(\"<[^>]*>\", \"\", i[1])\n        mdata += i[0].upper() + \": \" + data + \"\\n\"\n        data = re.sub(\"\\t\", \"\", data)\n        # mdata += textwrap.wrap(i[0].upper() + \": \" + data, wi - 6)\n    return \"Metadata\", mdata, K[\"Metadata\"]\n\n\n@text_win\ndef help():\n    src = \"Key Bindings:\\n\"\n    dig = max([len(i) for i in CFG[\"Keys\"].values()]) + 2\n    for i in CFG[\"Keys\"].keys():\n        src += \"{}  {}\\n\".format(\n            CFG[\"Keys\"][i].rjust(dig), \" \".join(re.findall(\"[A-Z][^A-Z]*\", i))\n        )\n    return \"Help\", src, K[\"Help\"]\n\n\n@text_win\ndef errmsg(title, msg, key):\n    return title, msg, key\n\n\ndef bookmarks(ebookpath):\n    idx = 0\n    while True:\n        bmarkslist = [i[0] for i in STATE[\"States\"][ebookpath][\"bmarks\"]]\n        if bmarkslist == []:\n            return list(K[\"ShowBookmarks\"])[0], None\n        retk, idx, todel = choice_win(True)(\n            lambda: (\"Bookmarks\", bmarkslist, idx, {ord(\"B\")})\n        )()\n        if todel is not None:\n            del STATE[\"States\"][ebookpath][\"bmarks\"][todel]\n        else:\n            return retk, idx\n\n\ndef truncate(teks, subte, maxlen, startsub=0):\n    if startsub > maxlen:\n        raise ValueError(\"Var startsub cannot be bigger than maxlen.\")\n    elif len(teks) <= maxlen:\n        return teks\n    else:\n        lensu = len(subte)\n        beg = teks[:startsub]\n        mid = subte if lensu <= maxlen - startsub else subte[: maxlen - startsub]\n        end = teks[startsub + lensu - maxlen :] if lensu < maxlen - startsub else \"\"\n        return beg + mid + end\n\n\ndef input_prompt(prompt):\n    # prevent pad hole when prompting for input while\n    # other window is active\n    # pad.refresh(y, 0, 0, x, rows-2, x+width)\n    rows, cols = SCREEN.getmaxyx()\n    stat = curses.newwin(1, cols, rows - 1, 0)\n    if COLORSUPPORT:\n        stat.bkgd(SCREEN.getbkgd())\n    stat.keypad(True)\n    curses.echo(1)\n    curses.curs_set(1)\n\n    init_text = \"\"\n\n    stat.addstr(0, 0, prompt, curses.A_REVERSE)\n    stat.addstr(0, len(prompt), init_text)\n    stat.refresh()\n\n    try:\n        while True:\n            ipt = stat.getch()\n            if ipt == 27:\n                stat.clear()\n                stat.refresh()\n                curses.echo(0)\n                curses.curs_set(0)\n                return\n            elif ipt == 10:\n                stat.clear()\n                stat.refresh()\n                curses.echo(0)\n                curses.curs_set(0)\n                return init_text\n            elif ipt in {8, curses.KEY_BACKSPACE}:\n                init_text = init_text[:-1]\n            elif ipt == curses.KEY_RESIZE:\n                stat.clear()\n                stat.refresh()\n                curses.echo(0)\n                curses.curs_set(0)\n                return curses.KEY_RESIZE\n            # elif len(init_text) <= maxlen:\n            else:\n                init_text += chr(ipt)\n\n            stat.clear()\n            stat.addstr(0, 0, prompt, curses.A_REVERSE)\n            stat.addstr(\n                0,\n                len(prompt),\n                init_text\n                if len(prompt + init_text) < cols\n                else \"...\" + init_text[len(prompt) - cols + 4 :],\n            )\n            stat.refresh()\n    except KeyboardInterrupt:\n        stat.clear()\n        stat.refresh()\n        curses.echo(0)\n        curses.curs_set(0)\n        return\n\n\ndef det_ebook_cls(file):\n    filext = os.path.splitext(file)[1]\n    if filext == \".epub\":\n        return Epub(file)\n    elif filext == \".fb2\":\n        return FictionBook(file)\n    elif MOBISUPPORT and filext == \".mobi\":\n        return Mobi(file)\n    elif MOBISUPPORT and filext == \".azw3\":\n        return Azw3(file)\n    elif not MOBISUPPORT and filext in {\".mobi\", \".azw3\"}:\n        sys.exit(\n            \"\"\"ERROR: Format not supported. (Supported: epub, fb2).\nTo get mobi and azw3 support, install mobi module from pip.\n   $ pip install mobi\"\"\"\n        )\n    else:\n        sys.exit(\"ERROR: Format not supported. (Supported: epub, fb2)\")\n\n\ndef dots_path(curr, tofi):\n    candir = curr.split(\"/\")\n    tofi = tofi.split(\"/\")\n    alld = tofi.count(\"..\")\n    t = len(candir)\n    candir = candir[0 : t - alld - 1]\n    try:\n        while True:\n            tofi.remove(\"..\")\n    except ValueError:\n        pass\n    return \"/\".join(candir + tofi)\n\n\ndef find_dict_client():\n    global DICT\n    if shutil.which(CFG[\"DictionaryClient\"].split()[0]) is not None:\n        DICT = CFG[\"DictionaryClient\"]\n    else:\n        DICT_LIST = [\"sdcv\", \"dict\"]\n        for i in DICT_LIST:\n            if shutil.which(i) is not None:\n                DICT = i\n                break\n        if DICT in {\"sdcv\"}:\n            DICT += \" -n\"\n\n\ndef find_media_viewer():\n    global VWR\n    if shutil.which(CFG[\"DefaultViewer\"].split()[0]) is not None:\n        VWR = CFG[\"DefaultViewer\"]\n    elif sys.platform == \"win32\":\n        VWR = \"start\"\n    elif sys.platform == \"darwin\":\n        VWR = \"open\"\n    else:\n        VWR_LIST = [\n            \"feh\",\n            \"gio\",\n            \"gnome-open\",\n            \"gvfs-open\",\n            \"xdg-open\",\n            \"kde-open\",\n            \"firefox\",\n        ]\n        for i in VWR_LIST:\n            if shutil.which(i) is not None:\n                VWR = i\n                break\n\n    if VWR in {\"gio\"}:\n        VWR += \" open\"\n\n\ndef open_media(scr, name, bstr):\n    sfx = os.path.splitext(name)[1]\n    fd, path = tempfile.mkstemp(suffix=sfx)\n    try:\n        with os.fdopen(fd, \"wb\") as tmp:\n            # tmp.write(epub.file.read(src))\n            tmp.write(bstr)\n        # run(VWR + \" \" + path, shell=True)\n        subprocess.call(\n            VWR + \" \" + path,\n            shell=True,\n            stdout=subprocess.DEVNULL,\n            stderr=subprocess.DEVNULL,\n        )\n        k = scr.getch()\n    finally:\n        os.remove(path)\n    return k\n\n\n@text_win\ndef define_word(word):\n    rows, cols = SCREEN.getmaxyx()\n    hi, wi = 5, 16\n    Y, X = (rows - hi) // 2, (cols - wi) // 2\n\n    p = subprocess.Popen(\n        \"{} {}\".format(DICT, word),\n        stdout=subprocess.PIPE,\n        stderr=subprocess.PIPE,\n        shell=True,\n    )\n\n    dictwin = curses.newwin(hi, wi, Y, X)\n    dictwin.box()\n    dictwin.addstr((hi - 1) // 2, (wi - 10) // 2, \"Loading...\")\n    dictwin.refresh()\n\n    out, err = p.communicate()\n\n    dictwin.clear()\n    dictwin.refresh()\n\n    if err == b\"\":\n        return \"Definition: \" + word.upper(), out.decode(), K[\"DefineWord\"]\n    else:\n        return \"Error: \" + DICT, err.decode(), K[\"DefineWord\"]\n\n\ndef searching(pad, src, width, y, ch, tot):\n    global SEARCHPATTERN\n    rows, cols = SCREEN.getmaxyx()\n    x = (cols - width) // 2\n    if SEARCHPATTERN is None:\n        candtext = input_prompt(\" Regex:\")\n        if candtext is None:\n            return y\n        elif isinstance(candtext, str):\n            SEARCHPATTERN = \"/\" + candtext\n        elif candtext == curses.KEY_RESIZE:\n            return candtext\n\n    if SEARCHPATTERN in {\"?\", \"/\"}:\n        SEARCHPATTERN = None\n        return y\n\n    found = []\n    try:\n        pattern = re.compile(SEARCHPATTERN[1:], re.IGNORECASE)\n    except re.error as reerrmsg:\n        SEARCHPATTERN = None\n        tmpk = errmsg(\"!Regex Error\", str(reerrmsg), set())\n        return tmpk\n\n    for n, i in enumerate(src):\n        for j in pattern.finditer(i):\n            found.append([n, j.span()[0], j.span()[1] - j.span()[0]])\n\n    if found == []:\n        if SEARCHPATTERN[0] == \"/\" and ch + 1 < tot:\n            return 1\n        elif SEARCHPATTERN[0] == \"?\" and ch > 0:\n            return -1\n        else:\n            s = 0\n            while True:\n                if s in K[\"Quit\"]:\n                    SEARCHPATTERN = None\n                    SCREEN.clear()\n                    SCREEN.refresh()\n                    return y\n                elif s == ord(\"n\") and ch == 0:\n                    SEARCHPATTERN = \"/\" + SEARCHPATTERN[1:]\n                    return 1\n                elif s == ord(\"N\") and ch + 1 == tot:\n                    SEARCHPATTERN = \"?\" + SEARCHPATTERN[1:]\n                    return -1\n\n                SCREEN.clear()\n                SCREEN.addstr(\n                    rows - 1,\n                    0,\n                    \" Finished searching: \" + SEARCHPATTERN[1 : cols - 22] + \" \",\n                    curses.A_REVERSE,\n                )\n                SCREEN.refresh()\n                pad.refresh(y, 0, 0, x, rows - 2, x + width)\n                s = pad.getch()\n\n    sidx = len(found) - 1\n    if SEARCHPATTERN[0] == \"/\":\n        if y > found[-1][0]:\n            return 1\n        for n, i in enumerate(found):\n            if i[0] >= y:\n                sidx = n\n                break\n\n    s = 0\n    msg = (\n        \" Searching: \"\n        + SEARCHPATTERN[1:]\n        + \" --- Res {}/{} Ch {}/{} \".format(sidx + 1, len(found), ch + 1, tot)\n    )\n    while True:\n        if s in K[\"Quit\"]:\n            SEARCHPATTERN = None\n            for i in found:\n                pad.chgat(i[0], i[1], i[2], pad.getbkgd())\n            SCREEN.clear()\n            SCREEN.refresh()\n            return y\n        elif s == ord(\"n\"):\n            SEARCHPATTERN = \"/\" + SEARCHPATTERN[1:]\n            if sidx == len(found) - 1:\n                if ch + 1 < tot:\n                    return 1\n                else:\n                    s = 0\n                    msg = \" Finished searching: \" + SEARCHPATTERN[1:] + \" \"\n                    continue\n            else:\n                sidx += 1\n                msg = (\n                    \" Searching: \"\n                    + SEARCHPATTERN[1:]\n                    + \" --- Res {}/{} Ch {}/{} \".format(\n                        sidx + 1, len(found), ch + 1, tot\n                    )\n                )\n        elif s == ord(\"N\"):\n            SEARCHPATTERN = \"?\" + SEARCHPATTERN[1:]\n            if sidx == 0:\n                if ch > 0:\n                    return -1\n                else:\n                    s = 0\n                    msg = \" Finished searching: \" + SEARCHPATTERN[1:] + \" \"\n                    continue\n            else:\n                sidx -= 1\n                msg = (\n                    \" Searching: \"\n                    + SEARCHPATTERN[1:]\n                    + \" --- Res {}/{} Ch {}/{} \".format(\n                        sidx + 1, len(found), ch + 1, tot\n                    )\n                )\n        elif s == curses.KEY_RESIZE:\n            return s\n\n        # TODO\n        if y + rows - 1 > pad.chunks[pad.find_chunkidx(y)]:\n            y = pad.chunks[pad.find_chunkidx(y)] + 1\n\n        while found[sidx][0] not in list(range(y, y + rows - 1)):\n            if found[sidx][0] > y:\n                y += rows - 1\n            else:\n                y -= rows - 1\n                if y < 0:\n                    y = 0\n\n        for n, i in enumerate(found):\n            attr = curses.A_REVERSE if n == sidx else curses.A_NORMAL\n            pad.chgat(i[0], i[1], i[2], pad.getbkgd() | attr)\n\n        SCREEN.clear()\n        SCREEN.addstr(rows - 1, 0, msg, curses.A_REVERSE)\n        SCREEN.refresh()\n        pad.refresh(y, 0, 0, x, rows - 2, x + width)\n        s = pad.getch()\n\n\ndef find_curr_toc_id(toc_idx, toc_sect, toc_secid, index, y):\n    ntoc = 0\n    for n, (i, j) in enumerate(zip(toc_idx, toc_sect)):\n        if i <= index:\n            if y >= toc_secid.get(j, 0):\n                ntoc = n\n        else:\n            break\n    return ntoc\n\n\ndef count_pct_async(ebook, allprev, sumlet):\n    perch = []\n    for n, i in enumerate(ebook.contents):\n        content = ebook.get_raw_text(i)\n        parser = HTMLtoLines()\n        # try:\n        parser.feed(content)\n        parser.close()\n        # except:\n        #     pass\n        src_lines = parser.get_lines()\n        allprev[n] = sum(perch)\n        perch.append(sum([len(re.sub(\"\\s\", \"\", j)) for j in src_lines]))\n    sumlet.value = sum(perch)\n\n\ndef count_pct(ebook):\n    perch = []\n    allprev = []\n    for i in ebook.contents:\n        content = ebook.get_raw_text(i)\n        parser = HTMLtoLines()\n        # try:\n        parser.feed(content)\n        parser.close()\n        # except:\n        #     pass\n        src_lines = parser.get_lines()\n        allprev.append(sum(perch))\n        perch.append(sum([len(re.sub(\"\\s\", \"\", j)) for j in src_lines]))\n    sumlet = sum(perch)\n    return allprev, sumlet\n\n\ndef count_max_reading_pg(ebook):\n    global ALLPREVLETTERS, SUMALLLETTERS, PROC_COUNTLETTERS, MULTIPROC\n\n    if MULTIPROC:\n        try:\n            ALLPREVLETTERS = multiprocessing.Array(\"i\", len(ebook.contents))\n            SUMALLLETTERS = multiprocessing.Value(\"i\", 0)\n            PROC_COUNTLETTERS = multiprocessing.Process(\n                target=count_pct_async, args=(ebook, ALLPREVLETTERS, SUMALLLETTERS)\n            )\n            # forking PROC_COUNTLETTERS will raise\n            # zlib.error: Error -3 while decompressing data: invalid distance too far back\n            PROC_COUNTLETTERS.start()\n        except:\n            MULTIPROC = False\n    if not MULTIPROC:\n        ALLPREVLETTERS, SUMALLLETTERS = count_pct(ebook)\n\n\ndef reader(ebook, index, width, y, pctg, sect):\n    global SHOWPROGRESS\n\n    k = 0 if SEARCHPATTERN is None else ord(\"/\")\n    rows, cols = SCREEN.getmaxyx()\n    x = (cols - width) // 2\n\n    contents = ebook.contents\n    toc_name = ebook.toc_entries[0]\n    toc_idx = ebook.toc_entries[1]\n    toc_sect = ebook.toc_entries[2]\n    toc_secid = {}\n    chpath = contents[index]\n    content = ebook.get_raw_text(chpath)\n    parser = HTMLtoLines(set(toc_sect))\n    # parser = HTMLtoLines()\n    # try:\n    parser.feed(content)\n    parser.close()\n    # except:\n    #     pass\n\n    src_lines, imgs, toc_secid = parser.get_lines(width)\n    totlines = len(src_lines) + 1  # 1 extra line for suffix\n\n    if y < 0 and totlines <= rows:\n        y = 0\n    elif pctg is not None:\n        y = round(pctg * totlines)\n    else:\n        y = y % totlines\n\n    pad = Board(totlines, width)\n    pad.feed(src_lines)\n\n    # this make curses.A_REVERSE not working\n    # put before paint_text\n    if COLORSUPPORT:\n        pad.bkgd(SCREEN.getbkgd())\n\n    pad.paint_text(0)\n\n    LOCALPCTG = []\n    for i in src_lines:\n        LOCALPCTG.append(len(re.sub(\"\\s\", \"\", i)))\n\n    SCREEN.clear()\n    SCREEN.refresh()\n    # try except to be more flexible on terminal resize\n    try:\n        pad.refresh(y, 0, 0, x, rows - 1, x + width)\n    except curses.error:\n        pass\n\n    if sect != \"\":\n        y = toc_secid.get(sect, 0)\n\n    countstring = \"\"\n    svline = \"dontsave\"\n    try:\n        while True:\n            if countstring == \"\":\n                count = 1\n            else:\n                count = int(countstring)\n            if k in range(48, 58):  # i.e., k is a numeral\n                countstring = countstring + chr(k)\n            else:\n                if k in K[\"Quit\"]:\n                    if k == 27 and countstring != \"\":\n                        countstring = \"\"\n                    else:\n                        savestate(ebook.path, index, width, y, y / totlines)\n                        sys.exit()\n                elif k in K[\"ScrollUp\"]:\n                    if count > 1:\n                        svline = y - 1\n                    if y >= count:\n                        y -= count\n                    elif y == 0 and index != 0:\n                        return -1, width, -rows, None, \"\"\n                    else:\n                        y = 0\n                elif k in K[\"PageUp\"]:\n                    if y == 0 and index != 0:\n                        return -1, width, -rows, None, \"\"\n                    else:\n                        y = pgup(y, rows, LINEPRSRV, count)\n                elif k in K[\"ScrollDown\"]:\n                    if count > 1:\n                        svline = y + rows - 1\n                    if y + count <= totlines - rows:\n                        y += count\n                    elif y == totlines - rows and index != len(contents) - 1:\n                        return 1, width, 0, None, \"\"\n                    else:\n                        y = totlines - rows\n                elif k in K[\"PageDown\"]:\n                    if totlines - y - LINEPRSRV > rows:\n                        if y + rows > pad.chunks[pad.find_chunkidx(y)]:\n                            y = pad.chunks[pad.find_chunkidx(y)] + 1\n                        else:\n                            y += rows - LINEPRSRV\n                        # SCREEN.clear()\n                        # SCREEN.refresh()\n                    elif index != len(contents) - 1:\n                        return 1, width, 0, None, \"\"\n                elif k in K[\"HalfScreenUp\"] | K[\"HalfScreenDown\"]:\n                    countstring = str(rows // 2)\n                    k = list(K[\"ScrollUp\" if k in K[\"HalfScreenUp\"] else \"ScrollDown\"])[\n                        0\n                    ]\n                    continue\n                elif k in K[\"NextChapter\"]:\n                    ntoc = find_curr_toc_id(toc_idx, toc_sect, toc_secid, index, y)\n                    if ntoc < len(toc_idx) - 1:\n                        if index == toc_idx[ntoc + 1]:\n                            try:\n                                y = toc_secid[toc_sect[ntoc + 1]]\n                            except KeyError:\n                                pass\n                        else:\n                            return (\n                                toc_idx[ntoc + 1] - index,\n                                width,\n                                0,\n                                None,\n                                toc_sect[ntoc + 1],\n                            )\n                elif k in K[\"PrevChapter\"]:\n                    ntoc = find_curr_toc_id(toc_idx, toc_sect, toc_secid, index, y)\n                    if ntoc > 0:\n                        if index == toc_idx[ntoc - 1]:\n                            y = toc_secid.get(toc_sect[ntoc - 1], 0)\n                        else:\n                            return (\n                                toc_idx[ntoc - 1] - index,\n                                width,\n                                0,\n                                None,\n                                toc_sect[ntoc - 1],\n                            )\n                elif k in K[\"BeginningOfCh\"]:\n                    ntoc = find_curr_toc_id(toc_idx, toc_sect, toc_secid, index, y)\n                    try:\n                        y = toc_secid[toc_sect[ntoc]]\n                    except (KeyError, IndexError):\n                        y = 0\n                elif k in K[\"EndOfCh\"]:\n                    ntoc = find_curr_toc_id(toc_idx, toc_sect, toc_secid, index, y)\n                    try:\n                        if toc_secid[toc_sect[ntoc + 1]] - rows >= 0:\n                            y = toc_secid[toc_sect[ntoc + 1]] - rows\n                        else:\n                            y = toc_secid[toc_sect[ntoc]]\n                    except (KeyError, IndexError):\n                        y = pgend(totlines, rows)\n                elif k in K[\"TableOfContents\"]:\n                    if ebook.toc_entries == [[], [], []]:\n                        k = errmsg(\n                            \"Table of Contents\",\n                            \"N/A: TableOfContents is unavailable for this book.\",\n                            K[\"TableOfContents\"],\n                        )\n                        continue\n                    ntoc = find_curr_toc_id(toc_idx, toc_sect, toc_secid, index, y)\n                    rettock, fllwd, _ = toc(toc_name, ntoc)\n                    if rettock is not None:  # and rettock in WINKEYS:\n                        k = rettock\n                        continue\n                    elif fllwd is not None:\n                        if index == toc_idx[fllwd]:\n                            try:\n                                y = toc_secid[toc_sect[fllwd]]\n                            except KeyError:\n                                y = 0\n                        else:\n                            return (\n                                toc_idx[fllwd] - index,\n                                width,\n                                0,\n                                None,\n                                toc_sect[fllwd],\n                            )\n                elif k in K[\"Metadata\"]:\n                    k = meta(ebook)\n                    if k in WINKEYS:\n                        continue\n                elif k in K[\"Help\"]:\n                    k = help()\n                    if k in WINKEYS:\n                        continue\n                elif k in K[\"Enlarge\"] and (width + count) < cols - 4:\n                    width += count\n                    return 0, width, 0, y / totlines, \"\"\n                elif k in K[\"Shrink\"] and width >= 22:\n                    width -= count\n                    return 0, width, 0, y / totlines, \"\"\n                elif k in K[\"SetWidth\"]:\n                    if countstring == \"\":\n                        # if called without a count, toggle between 80 cols and full width\n                        if width != 80 and cols - 4 >= 80:\n                            return 0, 80, 0, y / totlines, \"\"\n                        else:\n                            return 0, cols - 4, 0, y / totlines, \"\"\n                    else:\n                        width = count\n                    if width < 20:\n                        width = 20\n                    elif width >= cols - 4:\n                        width = cols - 4\n                    return 0, width, 0, y / totlines, \"\"\n                # elif k == ord(\"0\"):\n                #     if width != 80 and cols - 2 >= 80:\n                #         return 0, 80, 0, y/totlines, \"\"\n                #     else:\n                #         return 0, cols - 2, 0, y/totlines, \"\"\n                elif k in K[\"RegexSearch\"]:\n                    fs = searching(pad, src_lines, width, y, index, len(contents))\n                    if fs in WINKEYS or fs is None:\n                        k = fs\n                        continue\n                    elif SEARCHPATTERN is not None:\n                        return fs, width, 0, None, \"\"\n                    else:\n                        y = fs\n                elif k in K[\"OpenImage\"] and VWR is not None:\n                    gambar, idx = [], []\n                    for n, i in enumerate(src_lines[y : y + rows]):\n                        img = re.search(\"(?<=\\\\[IMG:)[0-9]+(?=\\\\])\", i)\n                        if img is not None:\n                            gambar.append(img.group())\n                            idx.append(n)\n\n                    impath = \"\"\n                    if len(gambar) == 1:\n                        impath = imgs[int(gambar[0])]\n                    elif len(gambar) > 1:\n                        p, i = 0, 0\n                        while p not in K[\"Quit\"] and p not in K[\"Follow\"]:\n                            SCREEN.move(idx[i], x + width // 2 + len(gambar[i]) + 1)\n                            SCREEN.refresh()\n                            curses.curs_set(1)\n                            p = pad.getch()\n                            if p in K[\"ScrollDown\"]:\n                                i += 1\n                            elif p in K[\"ScrollUp\"]:\n                                i -= 1\n                            i = i % len(gambar)\n\n                        curses.curs_set(0)\n                        if p in K[\"Follow\"]:\n                            impath = imgs[int(gambar[i])]\n\n                    if impath != \"\":\n                        if ebook.__class__.__name__ in {\"Epub\", \"Azw3\"}:\n                            impath = dots_path(chpath, impath)\n                        imgnm, imgbstr = ebook.get_img_bytestr(impath)\n                        k = open_media(pad, imgnm, imgbstr)\n                        continue\n                elif (\n                    k in K[\"SwitchColor\"]\n                    and COLORSUPPORT\n                    and countstring in {\"\", \"0\", \"1\", \"2\"}\n                ):\n                    if countstring == \"\":\n                        count_color = curses.pair_number(SCREEN.getbkgd())\n                        if count_color not in {2, 3}:\n                            count_color = 1\n                        count_color = count_color % 3\n                    else:\n                        count_color = count\n                    SCREEN.bkgd(curses.color_pair(count_color + 1))\n                    return 0, width, y, None, \"\"\n                elif k in K[\"AddBookmark\"]:\n                    defbmname_suffix = 1\n                    defbmname = \"Bookmark \" + str(defbmname_suffix)\n                    occupiedbmnames = [\n                        i[0] for i in STATE[\"States\"][ebook.path][\"bmarks\"]\n                    ]\n                    while defbmname in occupiedbmnames:\n                        defbmname_suffix += 1\n                        defbmname = \"Bookmark \" + str(defbmname_suffix)\n                    bmname = input_prompt(\" Add bookmark ({}):\".format(defbmname))\n                    if bmname is not None:\n                        if bmname.strip() == \"\":\n                            bmname = defbmname\n                        STATE[\"States\"][ebook.path][\"bmarks\"].append(\n                            [bmname, index, y, y / totlines]\n                        )\n                elif k in K[\"ShowBookmarks\"]:\n                    if STATE[\"States\"][ebook.path][\"bmarks\"] == []:\n                        k = text_win(\n                            lambda: (\n                                \"Bookmarks\",\n                                \"N/A: Bookmarks are not found in this book.\",\n                                {ord(\"B\")},\n                            )\n                        )()\n                        continue\n                    else:\n                        retk, idxchoice = bookmarks(ebook.path)\n                        if retk is not None:\n                            k = retk\n                            continue\n                        elif idxchoice is not None:\n                            bmtojump = STATE[\"States\"][ebook.path][\"bmarks\"][idxchoice]\n                            return (\n                                bmtojump[1] - index,\n                                width,\n                                bmtojump[2],\n                                bmtojump[3],\n                                \"\",\n                            )\n                elif k in K[\"DefineWord\"] and DICT is not None:\n                    word = input_prompt(\" Define:\")\n                    if word == curses.KEY_RESIZE:\n                        k = word\n                        continue\n                    elif word is not None:\n                        defin = define_word(word)\n                        if defin in WINKEYS:\n                            k = defin\n                            continue\n                elif k in K[\"MarkPosition\"]:\n                    jumnum = pad.getch()\n                    if jumnum in range(49, 58):\n                        JUMPLIST[chr(jumnum)] = [index, width, y, y / totlines]\n                    else:\n                        k = jumnum\n                        continue\n                elif k in K[\"JumpToPosition\"]:\n                    jumnum = pad.getch()\n                    if jumnum in range(49, 58) and chr(jumnum) in JUMPLIST.keys():\n                        tojumpidxdiff = JUMPLIST[chr(jumnum)][0] - index\n                        tojumpy = JUMPLIST[chr(jumnum)][2]\n                        tojumpctg = (\n                            None\n                            if JUMPLIST[chr(jumnum)][1] == width\n                            else JUMPLIST[chr(jumnum)][3]\n                        )\n                        return tojumpidxdiff, width, tojumpy, tojumpctg, \"\"\n                    else:\n                        k = jumnum\n                        continue\n                elif k in K[\"ShowHideProgress\"]:\n                    SHOWPROGRESS = not SHOWPROGRESS\n                elif k == curses.KEY_RESIZE:\n                    savestate(ebook.path, index, width, y, y / totlines)\n                    # stated in pypi windows-curses page:\n                    # to call resize_term right after KEY_RESIZE\n                    if sys.platform == \"win32\":\n                        curses.resize_term(rows, cols)\n                        rows, cols = SCREEN.getmaxyx()\n                    else:\n                        rows, cols = SCREEN.getmaxyx()\n                        curses.resize_term(rows, cols)\n                    if cols < 22 or rows < 12:\n                        sys.exit(\"ERROR: Screen was too small (min 22cols x 12rows).\")\n                    if cols <= width + 4:\n                        return 0, cols - 4, 0, y / totlines, \"\"\n                    else:\n                        return 0, width, y, None, \"\"\n                elif k in K[\"RSVP\"]:\n                    y = rsvp(src_lines, y)\n                countstring = \"\"\n\n            if svline != \"dontsave\":\n                pad.chgat(svline, 0, width, curses.A_UNDERLINE)\n\n            try:\n                SCREEN.clear()\n                SCREEN.addstr(0, 0, countstring)\n                LOCALSUMALLL = SUMALLLETTERS.value if MULTIPROC else SUMALLLETTERS\n                if SHOWPROGRESS and (cols - width - 2) // 2 > 3 and LOCALSUMALLL != 0:\n                    PROGRESS = (\n                        ALLPREVLETTERS[index] + sum(LOCALPCTG[: y + rows - 1])\n                    ) / LOCALSUMALLL\n                    PROGRESSTR = \"{}%\".format(int(PROGRESS * 100))\n                    SCREEN.addstr(0, cols - len(PROGRESSTR), PROGRESSTR)\n                SCREEN.refresh()\n                if totlines - y < rows:\n                    pad.refresh(y, 0, 0, x, totlines - y, x + width)\n                else:\n                    pad.refresh(y, 0, 0, x, rows - 1, x + width)\n            except curses.error:\n                pass\n            k = pad.getch()\n\n            if svline != \"dontsave\":\n                pad.chgat(svline, 0, width, curses.A_NORMAL)\n                svline = \"dontsave\"\n    except KeyboardInterrupt:\n        savestate(ebook.path, index, width, y, y / totlines)\n        sys.exit()\n\n\ndef preread(stdscr, file):\n    global COLORSUPPORT, SHOWPROGRESS, SCREEN\n\n    curses.use_default_colors()\n    try:\n        curses.init_pair(1, -1, -1)\n        curses.init_pair(2, CFG[\"DarkColorFG\"], CFG[\"DarkColorBG\"])\n        curses.init_pair(3, CFG[\"LightColorFG\"], CFG[\"LightColorBG\"])\n        COLORSUPPORT = True\n    except:\n        COLORSUPPORT = False\n\n    SCREEN = stdscr\n\n    SCREEN.keypad(True)\n    curses.curs_set(0)\n    SCREEN.clear()\n    rows, cols = SCREEN.getmaxyx()\n    show_loader(SCREEN)\n\n    ebook = det_ebook_cls(file)\n\n    try:\n        if ebook.path in STATE[\"States\"]:\n            idx = STATE[\"States\"][ebook.path][\"index\"]\n            width = STATE[\"States\"][ebook.path][\"width\"]\n            y = STATE[\"States\"][ebook.path][\"pos\"]\n        else:\n            STATE[\"States\"][ebook.path] = {}\n            STATE[\"States\"][ebook.path][\"bmarks\"] = []\n            idx = 0\n            y = 0\n            width = 80\n        pctg = None\n\n        if cols <= width + 4:\n            width = cols - 4\n            pctg = STATE[\"States\"][ebook.path].get(\"pctg\", None)\n\n        try:\n            ebook.initialize()\n        except Exception as e:\n            sys.exit(\"ERROR: Badly-structured ebook.\\n\" + str(e))\n        find_media_viewer()\n        find_dict_client()\n        parse_keys()\n        SHOWPROGRESS = CFG[\"ShowProgressIndicator\"]\n        count_max_reading_pg(ebook)\n\n        sec = \"\"\n        while True:\n            incr, width, y, pctg, sec = reader(ebook, idx, width, y, pctg, sec)\n            idx += incr\n            show_loader(SCREEN)\n    finally:\n        ebook.cleanup()\n\n\ndef main():\n    termc, termr = shutil.get_terminal_size()\n\n    args = []\n    if sys.argv[1:] != []:\n        args += sys.argv[1:]\n\n    if len({\"-h\", \"--help\"} & set(args)) != 0:\n        print(__doc__.rstrip())\n        sys.exit()\n\n    loadstate()\n\n    if len({\"-v\", \"--version\", \"-V\"} & set(args)) != 0:\n        print(\"Startup file loaded:\")\n        print(CFGFILE)\n        print(STATEFILE)\n        print()\n        print(\"v\" + __version__)\n        print(__license__, \"License\")\n        print(\"Copyright (c) 2019\", __author__)\n        print(__url__)\n        sys.exit()\n\n    if len({\"-d\"} & set(args)) != 0:\n        args.remove(\"-d\")\n        dump = True\n    else:\n        dump = False\n\n    if args == []:\n        file = STATE[\"LastRead\"]\n        if not os.path.isfile(file):\n            # print(__doc__)\n            sys.exit(\"ERROR: Found no last read file.\")\n\n    elif os.path.isfile(args[0]):\n        file = args[0]\n\n    else:\n        file = None\n        todel = []\n        xitmsg = 0\n\n        val = 0\n        for i in STATE[\"States\"].keys():\n            if not os.path.exists(i):\n                todel.append(i)\n            else:\n                match_val = sum(\n                    [\n                        j.size\n                        for j in SM(\n                            None, i.lower(), \" \".join(args).lower()\n                        ).get_matching_blocks()\n                    ]\n                )\n                if match_val >= val:\n                    val = match_val\n                    file = i\n        if val == 0:\n            xitmsg = \"\\nERROR: No matching file found in history.\"\n\n        for i in todel:\n            del STATE[\"States\"][i]\n        with open(STATEFILE, \"w\") as f:\n            json.dump(STATE, f, indent=4)\n\n        if len(args) == 1 and re.match(r\"[0-9]+\", args[0]) is not None:\n            try:\n                file = list(STATE[\"States\"].keys())[int(args[0]) - 1]\n                xitmsg = 0\n            except IndexError:\n                xitmsg = \"ERROR: No matching file found in history.\"\n\n        if xitmsg != 0 or \"-r\" in args:\n            print(\"Reading history:\")\n            dig = len(str(len(STATE[\"States\"].keys()) + 1))\n            tcols = termc - dig - 2\n            for n, i in enumerate(STATE[\"States\"].keys()):\n                p = i.replace(os.getenv(\"HOME\"), \"~\")\n                print(\n                    \"{}{} {}\".format(\n                        str(n + 1).rjust(dig),\n                        \"*\" if i == STATE[\"LastRead\"] else \" \",\n                        truncate(p, \"...\", tcols, 7),\n                    )\n                )\n            sys.exit(xitmsg)\n\n    if dump:\n        ebook = det_ebook_cls(file)\n        try:\n            try:\n                ebook.initialize()\n            except Exception as e:\n                sys.exit(\"ERROR: Badly-structured ebook.\\n\" + str(e))\n            for i in ebook.contents:\n                content = ebook.get_raw_text(i)\n                parser = HTMLtoLines()\n                # try:\n                parser.feed(content)\n                parser.close()\n                # except:\n                #     pass\n                src_lines = parser.get_lines()\n                # sys.stdout.reconfigure(encoding=\"utf-8\")  # Python>=3.7\n                for j in src_lines:\n                    sys.stdout.buffer.write((j + \"\\n\\n\").encode(\"utf-8\"))\n        finally:\n            ebook.cleanup()\n        sys.exit()\n\n    else:\n        if termc < 22 or termr < 12:\n            sys.exit(\"ERROR: Screen was too small (min 22cols x 12rows).\")\n        curses.wrapper(preread, file)\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "shirah_reader.egg-info/PKG-INFO",
    "content": "Metadata-Version: 2.1\nName: shirah-reader\nVersion: 1.0.0\nSummary: UNKNOWN\nHome-page: https://github.com/hallicopter/shirah-reader\nAuthor: Hallicopter\nAuthor-email: advait.raykar@gmail.com\nLicense: MIT\nDescription: # Shirah\n        A terminal RSVP speed reader.\n        \n        ![Alt text](/assets/shirah.gif \"Optional Title\")\n        \n        Caution, early stage software.\n        \n        ### What is an RSVP reader?\n        RSVP stands for [Rapid Serial Visual presentiation](https://en.wikipedia.org/wiki/Rapid_serial_visual_presentation).\n        Its a controversial method to enable speedreading. I have been using this on my devices, and it has given me great results. I wanted to have an option to do this in the terminal too.\n        Ignore the haters, and try it for yourself.\n        Also, I don't like that the best options on a computer are paid softwares.\n        \n        ### Usage\n        To install, use either \n        \n        ```pip install shirah-reader==0.1.1```\n        \n        or\n        \n        ```pip3 install shirah-reader==0.1.1```\n        \n        To run a test, just enter `shirah`.\n        \n        To read a book, and set a speed, enter\n        \n        ```shirah -f <path/to/book> -s <speed>```\n        \n        eg: `shirah -f alice.epub -s 600`\n        \n        ### Goals\n        - [x] Read txt\n        - [x] Read epubs\n        - [x] Pause\n        - [ ] Save progress\n        - [ ] Progress bar.\n        - [ ] Chapter-wise Navigation\n        \nPlatform: UNKNOWN\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.7\nDescription-Content-Type: text/markdown\n"
  },
  {
    "path": "shirah_reader.egg-info/SOURCES.txt",
    "content": "README.md\nsetup.py\nshirah_reader/__init__.py\nshirah_reader/__main__.py\nshirah_reader.egg-info/PKG-INFO\nshirah_reader.egg-info/SOURCES.txt\nshirah_reader.egg-info/dependency_links.txt\nshirah_reader.egg-info/entry_points.txt\nshirah_reader.egg-info/requires.txt\nshirah_reader.egg-info/top_level.txt"
  },
  {
    "path": "shirah_reader.egg-info/dependency_links.txt",
    "content": "\n"
  },
  {
    "path": "shirah_reader.egg-info/entry_points.txt",
    "content": "[console_scripts]\nshirah = shirah_reader.__main__:main\n\n"
  },
  {
    "path": "shirah_reader.egg-info/requires.txt",
    "content": "EbookLib\nbeautifulsoup4\nsyllables\ntermcolor\n"
  },
  {
    "path": "shirah_reader.egg-info/top_level.txt",
    "content": "shirah_reader\n"
  }
]