├── .gdbinit
├── .github
└── workflows
│ └── build.yml
├── .gitignore
├── AUTHORS
├── COPYING
├── CREDITS
├── Makefile
├── README.md
├── include
├── bitops.h
├── common.h
├── config.h
├── cstack.h
├── cstring.h
├── main.h
├── markdown.h
├── parser.h
├── url.h
└── viewer.h
├── mdp.1
├── mdp.cygport
├── mdp.sublime-project
├── sample.md
└── src
├── Makefile
├── cstack.c
├── cstring.c
├── main.c
├── markdown.c
├── parser.c
├── url.c
└── viewer.c
/.gdbinit:
--------------------------------------------------------------------------------
1 | set args -d sample.md
2 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | ---
2 | name: Build CI
3 |
4 | on: [push, pull_request]
5 |
6 | jobs:
7 | build:
8 | runs-on: ubuntu-latest
9 | strategy:
10 | matrix:
11 | compiler: [gcc-12, gcc-13, clang-17, clang-18]
12 | env:
13 | PREFIX: /usr/local/bin
14 | CC: ${{ matrix.compiler }}
15 |
16 | steps:
17 | - uses: actions/checkout@v3
18 | - name: Install Dependencies
19 | run: |
20 | sudo apt-get update -qq
21 | sudo apt-get install -y \
22 | build-essential \
23 | clang-17 \
24 | clang-18 \
25 | gcc-12 \
26 | gcc-13
27 | - name: CC ver
28 | run: ${CC} -v
29 | - name: make
30 | run: make
31 | - name: make install
32 | run: sudo make install
33 | - name: ldd
34 | run: ldd $PREFIX/mdp
35 | - name: version
36 | run: $PREFIX/mdp -v
37 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # temporary compile results
2 | *.o
3 |
4 | # Binary excutions
5 | mdp
6 | mdp.exe
7 |
8 | # Other temporary files
9 | .DS_Store
10 | .idea
11 | *~
12 | *.swp
13 | *.sublime-workspace
14 | *.out
15 | tags
16 |
--------------------------------------------------------------------------------
/AUTHORS:
--------------------------------------------------------------------------------
1 | Author: visit1985
2 |
3 | Contributors:
4 | Pyrohh
5 | FreeBirdLjj
6 | ungureanuvladvictor
7 | lukaslueg
8 | mattn
9 | fredjean
10 | hspak
11 | dopsi
12 | alx741
13 | mnalt
14 | guobin2312
15 | lukebond
16 | namhyung
17 | ethanherbertson
18 | MrPicklePinosaur
19 |
20 |
--------------------------------------------------------------------------------
/COPYING:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 |
635 | Copyright (C)
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | Copyright (C)
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------
/CREDITS:
--------------------------------------------------------------------------------
1 | Credits go to
2 |
3 | Joshua M. Clulow for his terminal-based presentation tool in node.js
4 | https://github.com/jclulow/vtmc
5 |
6 | David Parsons for his inspiring Markdown implementation in C
7 | https://github.com/Orc/discount
8 |
9 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | #
2 | # Makefile
3 | # Copyright (C) 2018 Michael Goehler
4 | #
5 | # This file is part of mdp.
6 | #
7 | # This program is free software: you can redistribute it and/or modify
8 | # it under the terms of the GNU General Public License as published by
9 | # the Free Software Foundation, either version 3 of the License, or
10 | # (at your option) any later version.
11 | #
12 | # This program is distributed in the hope that it will be useful,
13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | # GNU General Public License for more details.
16 | #
17 | # You should have received a copy of the GNU General Public License
18 | # along with this program. If not, see .
19 | #
20 |
21 | UNAME_S := $(shell uname -s 2>/dev/null || echo not)
22 |
23 | SOURCES = $(sort $(wildcard src/*.c))
24 | OBJECTS = $(SOURCES:.c=.o)
25 | TARGET = mdp
26 | DESTDIR =
27 | PREFIX ?= /usr/local
28 | BINDIR ?= ${PREFIX}/bin
29 | MANDIR ?= ${PREFIX}/share/man
30 |
31 | CURSES = ncursesw
32 | LDFLAGS ?= -s
33 | CFLAGS ?= -O3
34 | CFLAGS += -Wall
35 |
36 | ifeq (Windows_NT,$(OS))
37 | ifeq (,$(findstring CYGWIN,$(UNAME_S)))
38 | CURSES := pdcurses
39 | endif
40 | endif
41 |
42 | ifeq ($(UNAME_S),Darwin)
43 | CURSES := ncurses
44 | LDFLAGS :=
45 | endif
46 |
47 | ifeq ($(DEBUG),1)
48 | CFLAGS := -O0 -Wall -g
49 | LDFLAGS :=
50 | endif
51 |
52 | LDLIBS = -l$(CURSES)
53 |
54 | all: $(TARGET)
55 |
56 | $(TARGET): src
57 | $(CC) $(OBJECTS) $(LDLIBS) $(CFLAGS) $(LDFLAGS) -o $(TARGET)
58 |
59 | src:
60 | $(MAKE) $(MFLAGS) -C src
61 |
62 | clean:
63 | $(MAKE) -C src clean
64 | $(RM) $(TARGET)
65 |
66 | install:
67 | install -d $(DESTDIR)$(BINDIR)
68 | install -m 755 $(TARGET) $(DESTDIR)$(BINDIR)/$(TARGET)
69 | install -d $(DESTDIR)$(MANDIR)/man1
70 | install -m 644 mdp.1 $(DESTDIR)$(MANDIR)/man1/$(TARGET).1
71 |
72 | uninstall:
73 | $(RM) $(DESTDIR)$(BINDIR)/$(TARGET)
74 | $(RM) $(DESTDIR)$(MANDIR)/man1/$(TARGET).1
75 |
76 | .PHONY: all clean install src uninstall
77 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | ## mdp - A command-line based markdown presentation tool.
3 |
4 | 
5 |
6 | ---
7 |
8 | ***How to get started:***
9 |
10 | mdp needs the ncursesw headers to compile.
11 | So make sure you have them installed:
12 |
13 | - on Raspbian (Raspberry Pi) you need `libncurses5-dev` and `libncursesw5-dev`
14 | - on Fedora you need `ncurses-devel` and `ncurses-c++-libs`
15 |
16 | Now download and install mdp:
17 |
18 | $ git clone https://github.com/visit1985/mdp.git
19 | $ cd mdp
20 | $ make
21 | $ make install
22 | $ mdp sample.md
23 |
24 | - On Arch Linux, you can use the existing [package](https://www.archlinux.org/packages/extra/x86_64/mdp/).
25 | - on Cygwin you can use the existing [package](https://cygwin.com/cgi-bin2/package-grep.cgi?grep=mdp.exe) from the setup program.
26 | - On Debian, you can use the existing [DEB package](https://tracker.debian.org/pkg/mdp-src), or run `apt-get install mdp`.
27 | - On FreeBSD, you can use the port [misc/mdp](http://www.freshports.org/misc/mdp).
28 | - On MacOS, use either the [Homebrew Formula](http://brewformulas.org/Mdp) by running `brew install mdp` or install with [MacPorts](https://ports.macports.org/port/mdp/) with `sudo port install mdp`.
29 | - On Slackware, grab the SlackBuild here: (http://slackbuilds.org/apps/mdp/), or run `sbopkg -i mdp`.
30 | - On Ubuntu, you can use the existing [DEB package](https://launchpad.net/ubuntu/+source/mdp-src), or run `apt-get install mdp`.
31 |
32 | Most terminals support 256 colors only if the TERM variable is
33 | set correctly. To enjoy mdp's color fading feature:
34 |
35 | $ export TERM=xterm-256color
36 |
37 | ---
38 |
39 | ***How to use it:***
40 |
41 | Horizontal rulers are used as slide separator.
42 |
43 | Supports basic markdown formatting:
44 |
45 | - line wide markup
46 | - headlines
47 | - code
48 | - quotes
49 | - unordered list
50 |
51 | - in-line markup
52 | - bold text
53 | - underlined text
54 | - code
55 |
56 | Supports headers prefixed by @ symbol.
57 |
58 | - first two header lines are displayed as title and author
59 | in top and bottom bar
60 |
61 | Review sample.md for more details.
62 |
63 | ---
64 |
65 | ***Default controls:***
66 |
67 | - h, j, k, l, Arrow keys,
68 | Space, Enter, Backspace,
69 | Page Up, Page Down - next/previous slide
70 | - Home, g - go to first slide
71 | - End, G - go to last slide
72 | - 1-9 - go to slide n
73 | - r - reload input file
74 | - q - exit
75 |
76 | ---
77 |
78 | ***Configuration***:
79 |
80 | A `config.h` configuration file is available in `include/`, change the settings you want and recompile.
81 | Colors, keybindings and list types are configurable as of now. Note that configuring colors only works in 8 color mode.
82 |
83 | ---
84 |
85 | ***How to debug it:***
86 |
87 | To make a debug version of `mdp`, just type:
88 |
89 | $ make DEBUG=1
90 |
91 | ***Convert to PDF:***
92 |
93 | - Install `md2pdf` by obtaining the [release](https://github.com/mandolyte/mdtopdf/releases) for your arch and OS or, if
94 | you have `go` installed, invoke:
95 |
96 | ```sh
97 | $ go install github.com/mandolyte/mdtopdf/cmd/md2pdf@latest
98 | ```
99 | - If you require syntax highlighting, download the [gohighlight lexers](https://github.com/jessp01/gohighlight/tree/master/syntax_files)
100 |
101 | `md2pdf` supports all major `mdp` features and accepts local files, remote HTTP(s) URL and `STDIN` inputs.
102 | The below command will convert an `mdp` compatible markdown file to a PDF with a dark theme,
103 | syntax highlighting (you'll need to provide the language hint, of course), page/slide separation and a footer:
104 |
105 | ```sh
106 | md2pdf -i https://github.com/jessp01/crash-course-in/raw/main/courses/apt_dpkg_deb/apt_dpkg_deb.md \
107 | -o apt_dpkg_deb.pdf \
108 | -s ~/.config/zaje/syntax_files \
109 | --theme dark \
110 | --new-page-on-hr \
111 | --with-footer \
112 | --author "Jesse Portnoy " \
113 | --title "A crash course on handling deb packages"
114 | ```
115 |
116 | Since `markdown` does not support the centering escape sequences (i.e: `->` and `<-`), you will want to remove these before converting, for example:
117 |
118 | ```sh
119 | $ sed 's@^->\s*\(#.*\)\s*<-@\1@g' sample.md | ~/go/bin/md2pdf -o mdp.pdf \
120 | --theme dark --new-page-on-hr
121 | ```
122 |
123 |
--------------------------------------------------------------------------------
/include/bitops.h:
--------------------------------------------------------------------------------
1 | #if !defined( BITOPS_H )
2 | #define BITOPS_H
3 |
4 | /*
5 | * Macros to do bit operations on integer variables.
6 | * Copyright (C) 2018 Michael Goehler
7 | *
8 | * This file is part of mdp.
9 | *
10 | * This program is free software: you can redistribute it and/or modify
11 | * it under the terms of the GNU General Public License as published by
12 | * the Free Software Foundation, either version 3 of the License, or
13 | * (at your option) any later version.
14 | *
15 | * This program is distributed in the hope that it will be useful,
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 | * GNU General Public License for more details.
19 | *
20 | * You should have received a copy of the GNU General Public License
21 | * along with this program. If not, see .
22 | *
23 | *
24 | * macro: SET_BIT to set bit at pos of var to 1
25 | * macro: CLEAR_BIT to set bit at pos of var to 0
26 | * macro: TOGGLE_BIT to toggle bit at pos of var from 1 to 0 and vice versa
27 | * macro: CHECK_BIT returns > 0 if bit at pos of var is set to 1
28 | *
29 | * Example:
30 | * int i = 0;
31 | * i = SET_BIT(i, 2);
32 | * if(CHECK_BIT(i, 2))
33 | * printf("bit 2 is set\n");
34 | * printf("value of i is %d\n", i);
35 | *
36 | */
37 |
38 | #define SET_BIT(var, pos) ((var) |= (1<<(pos)))
39 | #define CLEAR_BIT(var, pos) ((var) &= (~(1<<(pos))))
40 | #define TOGGLE_BIT(var, pos) ((var) ^= (1<<(pos)))
41 | #define CHECK_BIT(var, pos) ((var) & (1<<(pos)))
42 |
43 | #endif // !defined( BITOPS_H )
44 |
--------------------------------------------------------------------------------
/include/common.h:
--------------------------------------------------------------------------------
1 | #if !defined( COMMON_H )
2 | #define COMMON_H
3 |
4 | /*
5 | * Common macros.
6 | * Copyright (C) 2018 Michael Goehler
7 | *
8 | * This file is part of mdp.
9 | *
10 | * This program is free software: you can redistribute it and/or modify
11 | * it under the terms of the GNU General Public License as published by
12 | * the Free Software Foundation, either version 3 of the License, or
13 | * (at your option) any later version.
14 | *
15 | * This program is distributed in the hope that it will be useful,
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 | * GNU General Public License for more details.
19 | *
20 | * You should have received a copy of the GNU General Public License
21 | * along with this program. If not, see .
22 | *
23 | *
24 | * type: bool variable stores only true or false.
25 | *
26 | * macro: MAX returns the higher one of two input variables
27 | * macro: MIN returns the lower one of two input variables
28 | *
29 | */
30 |
31 | #if defined( __STDC__ ) // for standard C compiler
32 | #if __STDC_VERSION__ >= 199901L // for C99 and later
33 | #include
34 | #else // __STDC_VERSION__ >= 199901L
35 | #if !defined( bool )
36 | typedef enum {
37 | false = 0,
38 | true = 1
39 | } bool;
40 | #endif // !defined( bool )
41 | #endif // __STDC_VERSION__ >= 199901L
42 | #else // defined( __STDC__ )
43 | #define bool int
44 | #define true 1
45 | #define false 0
46 | #endif // defined( __STDC__ )
47 |
48 | #define MAX(a, b) ({ typeof(a) _a = a; typeof(b) _b = b; _a > _b? _a : _b; })
49 | #define MIN(a, b) ({ typeof(a) _a = a; typeof(b) _b = b; _a < _b? _a : _b; })
50 |
51 | #endif // !defined( COMMON_H )
52 |
--------------------------------------------------------------------------------
/include/config.h:
--------------------------------------------------------------------------------
1 | #if !defined( CONFIG_H )
2 | #define CONFIG_H
3 |
4 | /*
5 | * User configuration file
6 | * Copyright (C) 2018 Michael Goehler
7 | *
8 | * This file is part of mdp.
9 | *
10 | * This program is free software: you can redistribute it and/or modify
11 | * it under the terms of the GNU General Public License as published by
12 | * the Free Software Foundation, either version 3 of the License, or
13 | * (at your option) any later version.
14 | *
15 | * This program is distributed in the hope that it will be useful,
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 | * GNU General Public License for more details.
19 | *
20 | * You should have received a copy of the GNU General Public License
21 | * along with this program. If not, see .
22 | *
23 | */
24 |
25 | // unordered list characters
26 | //
27 | // you can also override via env vars:
28 | // export MDP_LIST_OPEN1=" " MDP_LIST_OPEN2=" " MDP_LIST_OPEN3=" "
29 | // export MDP_LIST_HEAD1=" ■ " MDP_LIST_HEAD2=" ● " MDP_LIST_HEAD3=" ▫ "
30 | // or export MDP_LIST_OPEN to override all MDP_LIST_OPENx variables
31 | // and similarly for MDP_LIST_HEAD
32 | static const char *list_open1 = " | ";
33 | static const char *list_open2 = " | ";
34 | static const char *list_open3 = " | ";
35 | static const char *list_head1 = " +- ";
36 | static const char *list_head2 = " +- ";
37 | static const char *list_head3 = " +- ";
38 |
39 | #define FADE_DELAY 15000 // micro seconds
40 | #define GOTO_SLIDE_DELAY 5 // tenths of seconds
41 |
42 | // colors - you can only set in 8-bit color mode
43 | //
44 | /* Use the ncurses defined colors, here's a list of them:
45 | * COLOR_BLACK
46 | * COLOR_RED
47 | * COLOR_GREEN
48 | * COLOR_YELLOW
49 | * COLOR_BLUE
50 | * COLOR_MAGENTA
51 | * COLOR_CYAN
52 | * COLOR_WHITE
53 | */
54 | #define FG_COLOR COLOR_WHITE
55 | #define BG_COLOR COLOR_BLACK
56 | #define TITLE_COLOR COLOR_YELLOW
57 | #define HEADER_COLOR COLOR_BLUE
58 | #define BOLD_COLOR COLOR_RED
59 |
60 | // color ramp for fading from black to color
61 | static short white_ramp[24] = { 16, 232, 233, 234, 235, 236,
62 | 237, 238, 239, 240, 241, 242,
63 | 244, 245, 246, 247, 248, 249,
64 | 250, 251, 252, 253, 254, 255 };
65 |
66 | static short blue_ramp[24] = { 16, 17, 17, 18, 18, 19,
67 | 19, 20, 20, 21, 27, 33,
68 | 32, 39, 38, 45, 44, 44,
69 | 81, 81, 51, 51, 123, 123 };
70 |
71 | static short red_ramp[24] = { 16, 52, 52, 53, 53, 89,
72 | 89, 90, 90, 126, 127, 127,
73 | 163, 163, 164, 164, 200, 200,
74 | 201, 201, 207, 207, 213, 213 };
75 |
76 | // color ramp for fading from white to color
77 | static short white_ramp_invert[24] = { 15, 255, 254, 254, 252, 251,
78 | 250, 249, 248, 247, 246, 245,
79 | 243, 242, 241, 240, 239, 238,
80 | 237, 236, 235, 234, 233, 232 };
81 |
82 | static short blue_ramp_invert[24] = { 15, 231, 231, 195, 195, 159,
83 | 159, 123, 123, 87, 51, 44,
84 | 45, 38, 39, 32, 33, 33,
85 | 26, 26, 27, 27, 21, 21 };
86 |
87 | static short red_ramp_invert[24] = { 15, 231, 231, 224, 224, 225,
88 | 225, 218, 218, 219, 212, 213,
89 | 206, 207, 201, 200, 199, 199,
90 | 198, 198, 197, 197, 196, 196 };
91 |
92 | // keybindings
93 | static const int prev_slide_binding[] = {
94 | KEY_UP,
95 | KEY_LEFT,
96 | KEY_PPAGE,
97 | 8, // BACKSPACE (ascii)
98 | 127, // BACKSPACE (xterm)
99 | 263, // BACKSPACE (getty)
100 | 'h',
101 | 'k',
102 | 0
103 | };
104 | static const int next_slide_binding[] = {
105 | KEY_DOWN,
106 | KEY_RIGHT,
107 | KEY_NPAGE,
108 | '\n', // ENTER
109 | ' ', // SPACE
110 | 'j',
111 | 'l',
112 | 0
113 | };
114 | static const int first_slide_binding[] = {
115 | 'g',
116 | KEY_HOME,
117 | 0
118 | };
119 | static const int last_slide_binding[] = {
120 | 'G',
121 | KEY_END,
122 | 0
123 | };
124 | static const int reload_binding[] = {
125 | 'r',
126 | 0
127 | };
128 | static const int quit_binding[] = {
129 | 'q',
130 | 0
131 | };
132 |
133 | #endif // !defined( CONFIG_H )
134 |
--------------------------------------------------------------------------------
/include/cstack.h:
--------------------------------------------------------------------------------
1 | #if !defined( CSTACK_H )
2 | #define CSTACK_H
3 |
4 | /*
5 | * An implementation of a char stack in heap memory.
6 | * Copyright (C) 2018 Michael Goehler
7 | *
8 | * This file is part of mdp.
9 | *
10 | * This program is free software: you can redistribute it and/or modify
11 | * it under the terms of the GNU General Public License as published by
12 | * the Free Software Foundation, either version 3 of the License, or
13 | * (at your option) any later version.
14 | *
15 | * This program is distributed in the hope that it will be useful,
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 | * GNU General Public License for more details.
19 | *
20 | * You should have received a copy of the GNU General Public License
21 | * along with this program. If not, see .
22 | *
23 | *
24 | * struct: cstack_t which defines char stack type in heap memory
25 | *
26 | * function: cstack_init to initialize struct of type cstack_t
27 | * function: cstack_t->push to add one char on top of the stack
28 | * function: cstack_t->pop to remove the top char from the stack
29 | * function: cstack_t->top to test if the top char is a given char
30 | * function: cstack_t->empty to test if the stack is empty
31 | * function: cstack_t->delete to free the allocated memory
32 | *
33 | * Example:
34 | * cstack_t *p = cstack_init();
35 | * (p->push)(p, 'X');
36 | * printf("%c\n", (p->pop)(p));
37 | * (p->delete)(p);
38 | *
39 | */
40 |
41 | #include "common.h"
42 |
43 | typedef struct _cstack_t {
44 | wchar_t *content;
45 | size_t alloc;
46 | size_t size;
47 | int head;
48 | void (*push)(struct _cstack_t *self, wchar_t c);
49 | wchar_t (*pop)(struct _cstack_t *self);
50 | bool (*top)(struct _cstack_t *self, wchar_t c);
51 | bool (*empty)(struct _cstack_t *self);
52 | void (*delete)(struct _cstack_t *self);
53 | } cstack_t;
54 |
55 | cstack_t *cstack_init();
56 | void cstack_push(cstack_t *self, wchar_t c);
57 | wchar_t cstack_pop(cstack_t *self);
58 | bool cstack_top(cstack_t *self, wchar_t c);
59 | bool cstack_empty(cstack_t *self);
60 | void cstack_delete(cstack_t *self);
61 |
62 | #endif // !defined( CSTACK_H )
63 |
--------------------------------------------------------------------------------
/include/cstring.h:
--------------------------------------------------------------------------------
1 | #if !defined( CSTRING_H )
2 | #define CSTRING_H
3 |
4 | /*
5 | * An implementation of expandable c strings in heap memory.
6 | * Copyright (C) 2018 Michael Goehler
7 | *
8 | * This file is part of mdp.
9 | *
10 | * This program is free software: you can redistribute it and/or modify
11 | * it under the terms of the GNU General Public License as published by
12 | * the Free Software Foundation, either version 3 of the License, or
13 | * (at your option) any later version.
14 | *
15 | * This program is distributed in the hope that it will be useful,
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 | * GNU General Public License for more details.
19 | *
20 | * You should have received a copy of the GNU General Public License
21 | * along with this program. If not, see .
22 | *
23 | *
24 | * struct: cstring_t which defines a expandable c string type in heap memory
25 | *
26 | * function: cstring_init to initialize struct of type cstring_t
27 | * function: cstring_t->expand to add one character to the struct
28 | * function: cstring_t->expand_arr to add a string to the struct
29 | * function: cstring_t->strip to remove a substring
30 | * function: cstring_t->reset to clear and reuse the struct
31 | * function: cstring_t->delete to free the allocated memory
32 | *
33 | * Example:
34 | * cstring_t *p = cstring_init();
35 | * (p->expand)(p, 'X');
36 | * (p->delete)(p);
37 | *
38 | */
39 |
40 | // The amount of memory allocated from heap when string expansion hits the
41 | // allocated memory limit
42 | #define REALLOC_ADD 10
43 |
44 | typedef struct _cstring_t {
45 | wchar_t *value;
46 | size_t size;
47 | size_t alloc;
48 | void (*expand)(struct _cstring_t *self, wchar_t x);
49 | void (*expand_arr)(struct _cstring_t *self, wchar_t *x);
50 | void (*strip)(struct _cstring_t *self, int pos, int len);
51 | void (*reset)(struct _cstring_t *self);
52 | void (*delete)(struct _cstring_t *self);
53 | } cstring_t;
54 |
55 | cstring_t *cstring_init();
56 | void cstring_expand(cstring_t *self, wchar_t x);
57 | void cstring_expand_arr(cstring_t *self, wchar_t *x);
58 | void cstring_strip(cstring_t *self, int pos, int len);
59 | void cstring_reset(cstring_t *self);
60 | void cstring_delete(cstring_t *self);
61 |
62 | #endif // !defined( CSTRING_H )
63 |
--------------------------------------------------------------------------------
/include/main.h:
--------------------------------------------------------------------------------
1 | #if !defined( MAIN_H )
2 | #define MAIN_H
3 |
4 | /*
5 | * mdp -- A command-line based markdown presentation tool.
6 | * Copyright (C) 2018 Michael Goehler
7 | *
8 | * This program is free software: you can redistribute it and/or modify
9 | * it under the terms of the GNU General Public License as published by
10 | * the Free Software Foundation, either version 3 of the License, or
11 | * (at your option) any later version.
12 | *
13 | * This program is distributed in the hope that it will be useful,
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | * GNU General Public License for more details.
17 | *
18 | * You should have received a copy of the GNU General Public License
19 | * along with this program. If not, see .
20 | *
21 | */
22 |
23 | #include "parser.h"
24 | #include "viewer.h"
25 |
26 | #define MDP_VER_MAJOR 1
27 | #define MDP_VER_MINOR 0
28 | #define MDP_VER_REVISION 15
29 |
30 | #endif // !defined( MAIN_H )
31 |
--------------------------------------------------------------------------------
/include/markdown.h:
--------------------------------------------------------------------------------
1 | #if !defined( MARKDOWN_H )
2 | #define MARKDOWN_H
3 |
4 | /*
5 | * An implementation of markdown objects.
6 | * Copyright (C) 2018 Michael Goehler
7 | *
8 | * This file is part of mdp.
9 | *
10 | * This program is free software: you can redistribute it and/or modify
11 | * it under the terms of the GNU General Public License as published by
12 | * the Free Software Foundation, either version 3 of the License, or
13 | * (at your option) any later version.
14 | *
15 | * This program is distributed in the hope that it will be useful,
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 | * GNU General Public License for more details.
19 | *
20 | * You should have received a copy of the GNU General Public License
21 | * along with this program. If not, see .
22 | *
23 | *
24 | * enum: line_bitmask which enumerates markdown formatting bits
25 | *
26 | * struct: deck_t the root object representing a deck of slides
27 | * struct: slide_t a linked list element of type slide contained in a deck
28 | * struct: line_t a linked list element of type line contained in a slide
29 | *
30 | * function: new_deck to initialize a new deck
31 | * function: new_slide to initialize a new linked list of type slide
32 | * function: next_slide to extend a linked list of type slide by one element
33 | * function: new_line to initialize a new linked list of type line
34 | * function: next_line to extend a linked list of type line by one element
35 | * function: free_line to free a line elements memory
36 | * function: free_deck to free a deck's memory
37 | *
38 | */
39 |
40 | #include "cstring.h"
41 | #include "bitops.h"
42 |
43 | enum line_bitmask {
44 | IS_H1,
45 | IS_H1_ATX,
46 | IS_H2,
47 | IS_H2_ATX,
48 | IS_QUOTE,
49 | IS_CODE,
50 | IS_TILDE_CODE,
51 | IS_GFM_CODE,
52 | IS_HR,
53 | IS_UNORDERED_LIST_1,
54 | IS_UNORDERED_LIST_2,
55 | IS_UNORDERED_LIST_3,
56 | IS_UNORDERED_LIST_EXT,
57 | IS_CENTER,
58 | IS_STOP,
59 | IS_EMPTY
60 | };
61 |
62 | typedef struct _line_t {
63 | cstring_t *text;
64 | struct _line_t *prev;
65 | struct _line_t *next;
66 | int bits;
67 | int length;
68 | int offset;
69 | } line_t;
70 |
71 | typedef struct _slide_t {
72 | line_t *line;
73 | struct _slide_t *prev;
74 | struct _slide_t *next;
75 | int lines;
76 | int stop;
77 | int lines_consumed;
78 | } slide_t;
79 |
80 | typedef struct _deck_t {
81 | line_t *header;
82 | slide_t *slide;
83 | int slides;
84 | int headers;
85 | } deck_t;
86 |
87 | line_t *new_line();
88 | line_t *next_line(line_t *prev);
89 | slide_t *new_slide();
90 | slide_t *next_slide(slide_t *prev);
91 | deck_t *new_deck();
92 | void free_line(line_t *l);
93 | void free_deck(deck_t *);
94 |
95 | #endif // !defined( MARKDOWN_H )
96 |
--------------------------------------------------------------------------------
/include/parser.h:
--------------------------------------------------------------------------------
1 | #if !defined( PARSER_H )
2 | #define PARSER_H
3 |
4 | /*
5 | * Functions necessary to parse a file and transform its content into
6 | * a deck of slides containing lines. All based on markdown formating
7 | * rules.
8 | * Copyright (C) 2018 Michael Goehler
9 | *
10 | * This file is part of mdp.
11 | *
12 | * This program is free software: you can redistribute it and/or modify
13 | * it under the terms of the GNU General Public License as published by
14 | * the Free Software Foundation, either version 3 of the License, or
15 | * (at your option) any later version.
16 | *
17 | * This program is distributed in the hope that it will be useful,
18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 | * GNU General Public License for more details.
21 | *
22 | * You should have received a copy of the GNU General Public License
23 | * along with this program. If not, see .
24 | *
25 | *
26 | * function: markdown_load is the main function which reads a file handle,
27 | * and initializes deck, slides and lines
28 | * function: markdown_analyse which is used to identify line wide formatting
29 | * rules in given line
30 | * function: markdown_debug to print a report of the generated data structure
31 | * function: adjust_line_length to calculate line length excluding markup
32 | * function: is_utf8 detects multi-byte char
33 | * function: length_utf8 calculates the amount of bytes used for a multi-byte
34 | * char
35 | * function: next_nonblank, next_blank, next_word to calculate string offset's
36 | *
37 | */
38 |
39 | #include "common.h"
40 | #include "markdown.h"
41 | #include "cstack.h"
42 |
43 | #if defined( CYGWIN )
44 | #undef WEOF
45 | #define WEOF (0xffff)
46 | #endif // defined( CYGWIN )
47 |
48 | #define EXPAND_TABS 4
49 | #define CODE_INDENT 4
50 | #define UNORDERED_LIST_MAX_LEVEL 3
51 |
52 | deck_t *markdown_load(FILE *input, int noexpand);
53 | int markdown_analyse(cstring_t *text, int prev);
54 | void markdown_debug(deck_t *deck, int debug);
55 | void expand_character_entities(line_t *line);
56 | void adjust_line_length(line_t *line);
57 | int next_nonblank(cstring_t *text, int i);
58 | int prev_blank(cstring_t *text, int i);
59 | int next_blank(cstring_t *text, int i);
60 | int next_word(cstring_t *text, int i);
61 | int next_nontilde(cstring_t *text, int i);
62 | int next_nonbacktick(cstring_t *text, int i);
63 |
64 | #endif // !defined( PARSER_H )
65 |
--------------------------------------------------------------------------------
/include/url.h:
--------------------------------------------------------------------------------
1 | #if !defined( URL_H )
2 | #define URL_H
3 |
4 | /*
5 | * An object to store all urls of a slide.
6 | * Copyright (C) 2018 Michael Goehler
7 | *
8 | * This file is part of mdp.
9 | *
10 | * This program is free software: you can redistribute it and/or modify
11 | * it under the terms of the GNU General Public License as published by
12 | * the Free Software Foundation, either version 3 of the License, or
13 | * (at your option) any later version.
14 | *
15 | * This program is distributed in the hope that it will be useful,
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 | * GNU General Public License for more details.
19 | *
20 | * You should have received a copy of the GNU General Public License
21 | * along with this program. If not, see .
22 | *
23 | * function: url_init to initialize a new url object
24 | */
25 |
26 | typedef struct _url_t {
27 | wchar_t *link_name;
28 | wchar_t *target;
29 | int x;
30 | int y;
31 | struct _url_t *next;
32 | } url_t;
33 |
34 | void url_init(void);
35 | int url_add(const wchar_t *link_name, int link_name_length, const wchar_t *target, int target_length, int x, int y);
36 | wchar_t* url_get_target(int index);
37 | wchar_t* url_get_name(int index);
38 | int url_get_amount(void);
39 | void url_purge(void);
40 | void url_dump(void);
41 | int url_count_inline(const wchar_t *line);
42 | int url_len_inline(const wchar_t *value);
43 |
44 | #endif // !defined( URL_H )
45 |
--------------------------------------------------------------------------------
/include/viewer.h:
--------------------------------------------------------------------------------
1 | #if !defined( VIEWER_H )
2 | #define VIEWER_H
3 |
4 | /*
5 | * Functions necessary to display a deck of slides in different color modes
6 | * using ncurses. Only white, red, and blue are supported, as they can be
7 | * faded in 256 color mode.
8 | * Copyright (C) 2018 Michael Goehler
9 | *
10 | * This file is part of mdp.
11 | *
12 | * This program is free software: you can redistribute it and/or modify
13 | * it under the terms of the GNU General Public License as published by
14 | * the Free Software Foundation, either version 3 of the License, or
15 | * (at your option) any later version.
16 | *
17 | * This program is distributed in the hope that it will be useful,
18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 | * GNU General Public License for more details.
21 | *
22 | * You should have received a copy of the GNU General Public License
23 | * along with this program. If not, see .
24 | *
25 | *
26 | * function: ncurses_display initializes ncurses, defines colors, calculates
27 | * window geometry and handles key strokes
28 | * function: add_line detects inline markdown formatting and prints line char
29 | * by char
30 | * function: fade_in, fade_out implementing color fading in 256 color mode
31 | * function: int_length to calculate decimal length of slide count
32 | *
33 | */
34 |
35 | #define _GNU_SOURCE // enable ncurses wchar support
36 | #define _XOPEN_SOURCE_EXTENDED 1 // enable ncurses wchar support
37 |
38 | #if defined( WIN32 )
39 | #include
40 | #else
41 | #include
42 | #endif
43 |
44 | #include "common.h"
45 | #include "parser.h"
46 | #include "cstack.h"
47 | #include "url.h"
48 |
49 | #define CP_WHITE 1 // 255
50 | #define CP_BLUE 2 // 123
51 | #define CP_RED 3 // 213
52 | #define CP_YELLOW 4 // 208
53 | #define CP_BLACK 5 // CP_WHITE with foreground and background swapped
54 |
55 | int ncurses_display(deck_t *deck, int notrans, int nofade, int invert, int reload, int noreload, int slidenum, int nocodebg);
56 | void add_line(WINDOW *window, int y, int x, line_t *line, int max_cols, int colors, int nocodebg);
57 | void inline_display(WINDOW *window, const wchar_t *c, const int colors, int nocodebg);
58 | void fade_out(WINDOW *window, int trans, int colors, int invert);
59 | void fade_in(WINDOW *window, int trans, int colors, int invert);
60 | int int_length (int val);
61 | int get_slide_number(char init);
62 | void setup_list_strings(void);
63 | bool evaluate_binding(const int bindings[], char c);
64 |
65 | #endif // !defined( VIEWER_H )
66 |
--------------------------------------------------------------------------------
/mdp.1:
--------------------------------------------------------------------------------
1 | .\" This is the groff documentation source for MDP
2 | .\"
3 | .\" Preview with: groff -man -Tascii mdp.1
4 | .\" or: man -l mdp.1
5 | .\"
6 | .
7 | .
8 | .TH MDP 1 "2016-04-02" "User Commands"
9 | .SH NAME
10 | mdp \- A command-line based
11 | markdown presentation tool
12 | .SH SYNOPSIS
13 | .B mdp
14 | .RI [ OPTION ].\|.\|.\|
15 | .RI [ FILE ]
16 | .
17 | .SH DESCRIPTION
18 | .B mdp
19 | is a command-line program that allows you to make elegant presentations from
20 | .B markdown formatted
21 | .IR FILE s.
22 | .PP
23 | It is as easy as write your presentation content in the text editor of your
24 | preference and launch the presentation from the command-line.
25 | .
26 | .SH OPTIONS
27 | .SS "Input Control"
28 | .TP
29 | .IR FILE
30 | The input file from which the presentation is read. If no file is specified,
31 | or if the file name is
32 | .BR \- ","
33 | the presentation is read from standard input.
34 | .SS "Output Control"
35 | .TP
36 | .BR \-c ", " \-\^\-nocodebg
37 | Don't change the background color of code blocks.
38 | .TP
39 | .BR \-e ", " \-\^\-expand
40 | Enable character entity expansion (e.g. '>' becomes '>').
41 | .TP
42 | .BR \-f ", " \-\^\-nofade
43 | Disable color fading in 256 color mode.
44 | .TP
45 | .BR \-i ", " \-\^\-invert
46 | Swap black and white color.
47 | .TP
48 | .BR \-s ", " \-\^\-noslidenum
49 | Do not show slide number at the bottom.
50 | .TP
51 | .BR \-t ", " \-\^\-notrans
52 | Disable transparency in transparent terminal.
53 | .TP
54 | .BR \-x ", " \-\^\-noslidemax
55 | Show slide number, but not total number of slides.
56 | .
57 | .SS "Miscellaneous Options"
58 | .TP
59 | .BR \-d ", " \-\^\-debug
60 | Enable debug messages on STDERR. Add multiple times to increases debug level.
61 | .TP
62 | .BR \-h ", " \-\^\-help
63 | Display usage message and exit.
64 | .TP
65 | .BR \-v ", " \-\^\-version
66 | Display version and license information.
67 | .
68 | .SH ENVIRONMENT VARIABLES
69 | .SS "Output Control"
70 | .TP
71 | .BR MDP_LIST_HEAD[1-3],\ MDP_LIST_OPEN[1-3]
72 | Controls the list characters of unordered lists.
73 |
74 | The default is equivalent to:
75 | .br
76 | MDP_LIST_OPEN1=' | '
77 | .br
78 | MDP_LIST_OPEN2=' | '
79 | .br
80 | MDP_LIST_OPEN3=' | '
81 | .br
82 | MDP_LIST_HEAD1=' +- '
83 | .br
84 | MDP_LIST_HEAD2=' +- '
85 | .br
86 | MDP_LIST_HEAD3=' +- '
87 | .
88 | .SH MARKDOWN FORMATTING
89 | For a complete list of supported markups, refer the sample presentation
90 | (sample.md) provided alongside
91 | .BR mdp ,\|
92 | or online available at
93 | .IR https://github.com/visit1985/mdp .
94 | .SS "Slides"
95 | The input
96 | .IR FILE
97 | is split into multiple slides by horizontal rules. Each consisting of at least
98 | 3
99 | .B \-\-\-
100 | or
101 | .B ***
102 | characters on a single line.
103 | .B This line must be prefixed by an completely empty line.
104 | It can also contain spaces but no other characters.
105 | .PP
106 | If any slide is too large to fit into your current screen, an error message
107 | will be displayed at the moment the presentation is launched.
108 | .
109 | .SS "Line-by-Line Mode"
110 | .SS "Block-by-Block Mode"
111 | A single
112 | .BR "
" ", " "
" " or " "^"
113 | on an otherwise empty line signals
114 | .B mdp
115 | to stop output of the current slide (stop point) and wait for a key-press by
116 | the user.
117 | .PP
118 | This enables the user to display bullet points or list items one by one
119 | (line by line) or block by block.
120 | .
121 | .SS "Headers"
122 | .B mdp
123 | supports header lines in the format of
124 | .BR @ "[DESCRIPTION] " [VALUE]
125 | The first two header lines are displayed as title and author in top and
126 | bottom bar.
127 | .PP
128 | Headers are only recognized at the top of the input
129 | .IR FILE .
130 | .
131 | .SS "Line spanning markup"
132 | Supported are headlines, code blocks, quotes and unordered lists.
133 | .
134 | .SS "In-line markup"
135 | As well as bold text, underlined text and in-line code.
136 | .
137 | .SH COLOR SUPPORT
138 | Most terminals are able to display 256 colors these days. But some of them
139 | enable only 16 colors by default. To enjoy
140 | .BR mdp "'s"
141 | full capabilities, these terminals need to be signaled to enable 256 color
142 | mode. This is usually done by setting the TERM environment variable.
143 | .PP
144 | .BR "export TERM=xterm-256color"
145 | .
146 | .SH KEYBOARD CONTROLS
147 | .TP
148 | .BR "h, j, k, l, Arrow keys, Space, Enter, Backspace, Page Up, Page Down"
149 | Display next/previous slide or continue after a stop point.
150 | .TP
151 | .BR "g, Home"
152 | Jump to first slide.
153 | .TP
154 | .BR "G, End"
155 | Jump to last slide.
156 | .TP
157 | .BR "1..N"
158 | Jump to
159 | .BR N "th"
160 | slide.
161 | .TP
162 | .BR "r"
163 | Reload the input
164 | .IR FILE .\|
165 | This key is disabled if input was read from standard input.
166 | .TP
167 | .BR "q"
168 | Exit
169 | .BR mdp "."
170 | .
171 | .SH CUSTOMIZATION
172 | .B mdp
173 | can be configured by modifying config.h and recompiling.
174 | .SH AUTHOR
175 | Written by Michael Goehler and others, see
176 | .IR https://github.com/visit1985/mdp/blob/master/AUTHORS "."
177 | .SH COPYRIGHT
178 | Copyright (C) 2018 Michael Goehler
179 | .PP
180 | This is free software; see the source for copying conditions. There is NO
181 | warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
182 |
--------------------------------------------------------------------------------
/mdp.cygport:
--------------------------------------------------------------------------------
1 | # package name
2 | NAME="mdp"
3 | VERSION=1.0.9
4 | RELEASE=1
5 |
6 | # .hint generation
7 | CATEGORY="Utils"
8 | SUMMARY="A command-line based markdown presentation tool"
9 | DESCRIPTION="A ncurses-based command-line presentation tool, which makes
10 | it easy to create slides using the popular markdown format."
11 |
12 | # source and patch files
13 | SRC_URI="https://github.com/visit1985/mdp/archive/${VERSION}.tar.gz"
14 | DOCS="sample.md"
15 |
16 | # Build dependencies only
17 | DEPEND="gcc-core libncurses-devel make"
18 | # runtime deps to go in setup.hint
19 | #REQUIRES="libncursesw10"
20 |
21 | # custom src_compile, src_install and src_test
22 |
23 | src_compile() {
24 | cd ${S}
25 | cygmake
26 | }
27 |
28 | src_install() {
29 | cd ${S}
30 | PREFIX=/usr cyginstall
31 | }
32 |
33 | src_test() { :; }
34 |
35 |
--------------------------------------------------------------------------------
/mdp.sublime-project:
--------------------------------------------------------------------------------
1 | {
2 | "settings":
3 | {
4 | "tab_size": 4,
5 | "translate_tabs_to_spaces": true,
6 | "use_tab_stops": false
7 | }
8 | ,"folders":
9 | [
10 | {
11 | "follow_symlinks": true,
12 | "path": ".",
13 | "file_exclude_patterns": [".*", "mdp", "mdp.sublime-project", "mdp.sublime-workspace"]
14 | }
15 | ]
16 | ,"build_systems":
17 | [
18 | {
19 | "name": "mdp",
20 | "env": {
21 | "DEBUG": "1"
22 | },
23 | "cmd": ["make"],
24 | "working_dir": "${project_path:${folder}}",
25 |
26 | "variants":
27 | [
28 | {
29 | "name": "Clean",
30 | "cmd": ["make", "clean"]
31 | }
32 | ]
33 | }
34 | ]
35 | }
36 |
--------------------------------------------------------------------------------
/sample.md:
--------------------------------------------------------------------------------
1 | %title: mdp - Sample Presentation
2 | %author: visit1985
3 | %date: 2016-02-07
4 |
5 | -> mdp <-
6 | =========
7 |
8 | -> A command-line based markdown presentation tool. <-
9 |
10 | _Basic controls:_
11 |
12 | next slide *Enter*, *Space*, *Page Down*, *j*, *l*,
13 | *Down Arrow*, *Right Arrow*
14 |
15 | previous slide *Backspace*, *Page Up*, *h*, *k*,
16 | *Up Arrow*, *Left Arrow*
17 |
18 | quit *q*
19 | reload *r*
20 | slide N *1..9*
21 | first slide *Home*, *g*
22 | last slide *End*, *G*
23 |
24 | -------------------------------------------------
25 |
26 | -> # Supported markdown formatting <-
27 |
28 | The input file is split into multiple slides by
29 | horizontal rules (hr). A hr consisting of at
30 | least 3 *\** or *-*. It can also contain spaces but
31 | no other characters.
32 |
33 | Each of these represents the start of a new slide.
34 |
35 | \* \* \*
36 | \---
37 | \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
38 | \- - -
39 |
40 | -------------------------------------------------
41 |
42 | -> # Supported markdown formatting <-
43 |
44 | First-level headers can be prefixed by single *#*
45 | or underlined by *===*.
46 |
47 | \# first-level
48 |
49 | becomes
50 |
51 | # first-level
52 |
53 | -------------------------------------------------
54 |
55 | -> # Supported markdown formatting <-
56 |
57 | Second-level headers can be prefixed by *##* or
58 | underlined by *---*.
59 |
60 | second-level
61 | \------------
62 |
63 | becomes
64 |
65 | second-level
66 | ------------
67 |
68 |
69 | -------------------------------------------------
70 |
71 | -> # Supported markdown formatting's <-
72 |
73 | Inline codes are surrounded with backticks.
74 |
75 | C program starts with \`main()\`.
76 |
77 | becomes
78 |
79 | C program starts with `main()`.
80 |
81 | -------------------------------------------------
82 |
83 | -> # Supported markdown formatting <-
84 |
85 | Code blocks are automatically detected by 4 spaces
86 | at the beginning of a line.
87 |
88 | Tabs are automatically expanded to 4 spaces while
89 | parsing the input.
90 |
91 | \ int main(int argc, char \*argv[]) {
92 | \ printf("%s\\n", "Hello world!");
93 | \ }
94 |
95 | becomes
96 |
97 | int main(int argc, char *argv[]) {
98 | printf("%s\n", "Hello world!");
99 | }
100 |
101 | -------------------------------------------------
102 |
103 | -> # Supported markdown formatting <-
104 |
105 | You can also use [pandoc](https://pandoc.org/MANUAL.html#fenced-code-blocks)'s fenced code block
106 | extension. Use at least three ~ chars to open and
107 | at least as many or more ~ for closing.
108 |
109 | \~~~ {.numberLines}
110 | \int main(int argc, char \*argv[]) {
111 | \ printf("%s\\n", "Hello world!");
112 | \}
113 | \~~~~~~~~~~~~~~~~~~
114 |
115 | becomes
116 |
117 | ~~~ {.numberLines}
118 | int main(int argc, char *argv[]) {
119 | printf("%s\n", "Hello world!");
120 | }
121 | ~~~~~~~~~~~~~~~~~~
122 |
123 | Pandoc attributes (like ".numberlines" etc.)
124 | will be ignored
125 |
126 | -------------------------------------------------
127 |
128 | -> # Supported markdown formatting <-
129 |
130 | You can also use [github](https://guides.github.com/features/mastering-markdown/#GitHub-flavored-markdown) flavored markdown's
131 | code block. Use at least three backticks to open
132 | and at least as many or more backticks for closing.
133 |
134 | \```
135 | \int main(int argc, char \*argv[]) {
136 | \ printf("%s\\n", "Hello world!");
137 | \}
138 | \```
139 |
140 | becomes
141 |
142 | ```
143 | int main(int argc, char *argv[]) {
144 | printf("%s\n", "Hello world!");
145 | }
146 | ```
147 |
148 | Language hint will be ignored
149 |
150 | -------------------------------------------------
151 |
152 | -> # Supported markdown formatting <-
153 |
154 | Quotes are auto-detected by preceding *>*.
155 |
156 | Multiple *>* are interpreted as nested quotes.
157 |
158 | \> quote
159 | \>> nested quote 1
160 | \> > nested quote 2
161 |
162 | becomes
163 |
164 | > quote
165 | >> nested quote 1
166 | > > nested quote 2
167 |
168 | -------------------------------------------------
169 |
170 | -> # Supported markdown formatting <-
171 |
172 | Inline highlighting is supported as followed:
173 |
174 | \- *\** colors text as red
175 | \- *\_* underlines text
176 |
177 | \_some\_ \*highlighted\* \_\*text\*\_
178 |
179 | becomes
180 |
181 | _some_ *highlighted* _*text*_
182 |
183 | -------------------------------------------------
184 |
185 | -> # Supported markdown formatting <-
186 |
187 | Backslashes force special markdown characters
188 | like *\**, *\_*, *#* and *>* to be printed as
189 | normal characters.
190 |
191 | \\\*special\\\*
192 |
193 | becomes
194 |
195 | \*special\*
196 |
197 | -------------------------------------------------
198 |
199 | -> # Supported markdown formatting <-
200 |
201 | Leading *\** or *-* indicate lists.
202 |
203 | list
204 | \* major
205 | \ - minor
206 | \ - \*important\*
207 | \ detail
208 | \ - minor
209 |
210 | becomes
211 |
212 | list
213 | * major
214 | - minor
215 | - *important*
216 | detail
217 | - minor
218 |
219 | -------------------------------------------------
220 |
221 | -> # Supported markdown formatting <-
222 |
223 | A single *\
* or *^* in a line indicates mdp
224 | to stop the output on that position.
225 |
226 | This can be used to show bullet points
227 | line by line.
228 |
229 | *\
* is also not displayed in HTML converted
230 | output.
231 |
232 | Agenda
233 |
234 | * major
235 |
236 | * minor
237 |
238 | * major
239 | ^
240 | * minor
241 | ^
242 | * detail
243 |
244 | -------------------------------------------------
245 |
246 | -> # Supported markdown formatting <-
247 |
248 | Leading *->* indicates centering.
249 |
250 | \-> # test <-
251 | \-> ## test <-
252 | \-> test
253 | \-> \_\*test\*\_ <-
254 |
255 | becomes
256 |
257 | -> # test <-
258 | -> ## test <-
259 | -> test
260 | -> _*test*_ <-
261 |
262 | -------------------------------------------------
263 |
264 | -> # Supported markdown formatting <-
265 |
266 | URL in pandoc style are supported:
267 |
268 | \[Google](http://www.google.com/)
269 |
270 | becomes
271 |
272 | [Google](http://www.google.com/)
273 |
274 | -------------------------------------------------
275 |
276 | -> ## More information about markdown <-
277 |
278 | can be found in the [markdown documentation](http://daringfireball.net/projects/markdown/).
279 |
280 | -------------------------------------------------
281 |
282 | -> # Support for UTF-8 special characters <-
283 |
284 | Here are some examples.
285 |
286 | ae = ä, oe = ö, ue = ü, ss = ß
287 | upsilon = Ʊ, phi = ɸ
288 |
289 | ▛▀▀▀▀▀▀▀▀▀▜
290 | ▌rectangle▐
291 | ▙▄▄▄▄▄▄▄▄▄▟
292 |
293 |
294 | -------------------------------------------------
295 |
296 | -> # Suspend your presentation for hands-on examples <-
297 |
298 | Use *Ctrl + z* to suspend the presentation.
299 |
300 | Use *fg* to resume it.
301 |
302 | -------------------------------------------------
303 |
304 | -> # Convert your presentation to PDF <-
305 |
306 | To publish your presentation later on, you may
307 | want to convert it to PDF.
308 |
309 | This can be achieved by two additional tools:
310 |
311 | \- *markdown* to convert to HTML
312 | \- *wkhtmltopdf* to convert from HTML to PDF
313 |
314 | After installing them, you can simply type:
315 |
316 | $ markdown sample.md | wkhtmltopdf - sample.pdf
317 |
318 | -------------------------------------------------
319 |
320 | -> ## Last words <-
321 |
322 | I hope you like *mdp*.
323 |
324 | If you observe strange behavior, feel free to
325 | open an issue on [GitHub](https://github.com/visit1985/mdp).
326 |
--------------------------------------------------------------------------------
/src/Makefile:
--------------------------------------------------------------------------------
1 | #
2 | # Makefile
3 | # Copyright (C) 2018 Michael Goehler
4 | #
5 | # This file is part of mdp.
6 | #
7 | # This program is free software: you can redistribute it and/or modify
8 | # it under the terms of the GNU General Public License as published by
9 | # the Free Software Foundation, either version 3 of the License, or
10 | # (at your option) any later version.
11 | #
12 | # This program is distributed in the hope that it will be useful,
13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | # GNU General Public License for more details.
16 | #
17 | # You should have received a copy of the GNU General Public License
18 | # along with this program. If not, see .
19 | #
20 |
21 | UNAME_S := $(shell uname -s 2>/dev/null || echo not)
22 |
23 | SOURCES = $(wildcard *.c)
24 | OBJECTS = $(SOURCES:.c=.o)
25 | CFLAGS ?= -O3
26 | CFLAGS += -Wall
27 | CPPFLAGS += -I../include
28 |
29 | ifeq ($(DEBUG),1)
30 | CFLAGS := -O0 -Wall -g
31 | endif
32 |
33 | ifeq ($(OS),Windows_NT)
34 | ifeq (,$(findstring CYGWIN,$(UNAME_S)))
35 | CPPFLAGS += -DWIN32
36 | else
37 | CPPFLAGS += -DCYGWIN
38 | endif
39 | endif
40 |
41 | ifeq ($(UNAME_S),Linux)
42 | LSB_RELEASE := $(shell lsb_release -si 2>/dev/null || echo not)
43 | ifneq ($(filter $(LSB_RELEASE),Debian Ubuntu LinuxMint CrunchBang),)
44 | CPPFLAGS += -I/usr/include/ncursesw
45 | endif
46 | endif
47 |
48 | all: $(OBJECTS)
49 |
50 | clean:
51 | $(RM) $(OBJECTS)
52 |
53 |
--------------------------------------------------------------------------------
/src/cstack.c:
--------------------------------------------------------------------------------
1 | /*
2 | * An implementation of a char stack in heap memory.
3 | * Copyright (C) 2018 Michael Goehler
4 | *
5 | * This file is part of mdp.
6 | *
7 | * This program is free software: you can redistribute it and/or modify
8 | * it under the terms of the GNU General Public License as published by
9 | * the Free Software Foundation, either version 3 of the License, or
10 | * (at your option) any later version.
11 | *
12 | * This program is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | * GNU General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU General Public License
18 | * along with this program. If not, see .
19 | *
20 | */
21 |
22 | #include
23 | #include // fprintf
24 | #include // malloc, realloc
25 |
26 | #include "cstack.h"
27 |
28 | cstack_t *cstack_init() {
29 | cstack_t *stack = NULL;
30 | if((stack = malloc(sizeof(cstack_t))) != NULL) {
31 | stack->content = NULL;
32 | stack->alloc = stack->size = 0;
33 | stack->head = -1;
34 | stack->push = cstack_push;
35 | stack->pop = cstack_pop;
36 | stack->top = cstack_top;
37 | stack->empty = cstack_empty;
38 | stack->delete = cstack_delete;
39 | } else {
40 | fprintf(stderr, "%s\n", "cstack_init() failed to allocate memory.");
41 | exit(EXIT_FAILURE);
42 | }
43 | return stack;
44 | }
45 |
46 | void cstack_push(cstack_t *self, wchar_t c) {
47 | if(self->size + sizeof(c) > self->alloc) {
48 | self->alloc += (sizeof(wchar_t));
49 | if((self->content = realloc(self->content, self->alloc)) == NULL) {
50 | fprintf(stderr, "%s\n", "cstack_push() failed to reallocate memory.");
51 | exit(EXIT_FAILURE);
52 | }
53 | }
54 | self->content[++self->head] = c;
55 | self->size += (sizeof(wchar_t));
56 | }
57 |
58 | wchar_t cstack_pop(cstack_t *self) {
59 | self->size -= (sizeof(wchar_t));
60 | return self->content[self->head--];
61 | }
62 |
63 | bool cstack_top(cstack_t *self, wchar_t c) {
64 | return self->head >= 0 && self->content[self->head] == c;
65 | }
66 |
67 | bool cstack_empty(cstack_t *self) {
68 | return self->head == -1;
69 | }
70 |
71 | void cstack_delete(cstack_t *self) {
72 | free(self->content);
73 | free(self);
74 | }
75 |
--------------------------------------------------------------------------------
/src/cstring.c:
--------------------------------------------------------------------------------
1 | /*
2 | * An implementation of expandable c strings in heap memory.
3 | * Copyright (C) 2018 Michael Goehler
4 | *
5 | * This file is part of mdp.
6 | *
7 | * This program is free software: you can redistribute it and/or modify
8 | * it under the terms of the GNU General Public License as published by
9 | * the Free Software Foundation, either version 3 of the License, or
10 | * (at your option) any later version.
11 | *
12 | * This program is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | * GNU General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU General Public License
18 | * along with this program. If not, see .
19 | *
20 | */
21 |
22 | #include // wcslen, wcscat, wmemmove
23 | #include // fprintf
24 | #include // malloc, realloc
25 |
26 | #include "cstring.h"
27 |
28 | cstring_t *cstring_init() {
29 | cstring_t *x = NULL;
30 | if((x = malloc(sizeof(cstring_t))) != NULL) {
31 | x->value = NULL;
32 | x->size = x->alloc = 0;
33 | x->expand = cstring_expand;
34 | x->expand_arr = cstring_expand_arr;
35 | x->strip = cstring_strip;
36 | x->reset = cstring_reset;
37 | x->delete = cstring_delete;
38 | } else {
39 | fprintf(stderr, "%s\n", "cstring_init() failed to allocate memory.");
40 | exit(EXIT_FAILURE);
41 | }
42 | return x;
43 | }
44 |
45 | void cstring_expand(cstring_t *self, wchar_t x) {
46 | if((self->size + 2) * sizeof(wchar_t) > self->alloc) {
47 | self->alloc += (REALLOC_ADD * sizeof(wchar_t));
48 | if((self->value = realloc(self->value, self->alloc)) == NULL) {
49 | fprintf(stderr, "%s\n", "cstring_expand() failed to reallocate memory.");
50 | exit(EXIT_FAILURE);
51 | }
52 | }
53 | self->value[self->size] = x;
54 | self->value[self->size+1] = L'\0';
55 | self->size = wcslen(self->value);
56 | }
57 |
58 | void cstring_expand_arr(cstring_t *self, wchar_t *x) {
59 | if((self->size + wcslen(x) + 1) * sizeof(wchar_t) > self->alloc) {
60 | self->alloc = ((self->size + wcslen(x) + 1) * sizeof(wchar_t));
61 | if((self->value = realloc(self->value, self->alloc)) == NULL) {
62 | fprintf(stderr, "%s\n", "cstring_expand_arr() failed to reallocate memory.");
63 | exit(EXIT_FAILURE);
64 | }
65 | }
66 | self->value = wcscat(self->value, x);
67 | self->size = wcslen(self->value);
68 | self->value[self->size+1] = L'\0';
69 | }
70 |
71 | void cstring_strip(cstring_t *self, int pos, int len) {
72 | if(pos + len >= self->size) {
73 | if(pos <= self->size) {
74 | self->value[pos] = L'\0';
75 | self->size = pos;
76 | }
77 | return;
78 | }
79 | wmemmove(&self->value[pos], &self->value[pos+len], self->size - pos - len+1);
80 | self->size -= len;
81 | }
82 |
83 | void cstring_reset(cstring_t *self) {
84 | free(self->value);
85 | self->value = NULL;
86 | self->size = self->alloc = 0;
87 | }
88 |
89 | void cstring_delete(cstring_t *self) {
90 | free(self->value);
91 | free(self);
92 | }
93 |
--------------------------------------------------------------------------------
/src/main.c:
--------------------------------------------------------------------------------
1 | /*
2 | * mdp -- A command-line based markdown presentation tool.
3 | * Copyright (C) 2018 Michael Goehler
4 | *
5 | * This program is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License as published by
7 | * the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program. If not, see .
17 | *
18 | */
19 |
20 | #include
21 | #include
22 | #include // setlocale
23 | #include
24 | #include
25 | #include
26 |
27 | #include "main.h"
28 |
29 | void usage() {
30 | fprintf(stderr, "%s", "Usage: mdp [OPTION]... [FILE]\n");
31 | fprintf(stderr, "%s", "A command-line based markdown presentation tool.\n\n");
32 | fprintf(stderr, "%s", " -d, --debug enable debug messages on STDERR\n");
33 | fprintf(stderr, "%s", " add it multiple times to increases debug level\n");
34 | fprintf(stderr, "%s", " -e, --expand enable character entity expansion\n");
35 | fprintf(stderr, "%s", " -f, --nofade disable color fading in 256 color mode\n");
36 | fprintf(stderr, "%s", " -h, --help display this help and exit\n");
37 | fprintf(stderr, "%s", " -i, --invert swap black and white color\n");
38 | fprintf(stderr, "%s", " -t, --notrans disable transparency in transparent terminal\n");
39 | fprintf(stderr, "%s", " -s, --noslidenum do not show slide number at the bottom\n");
40 | fprintf(stderr, "%s", " -v, --version display the version number and license\n");
41 | fprintf(stderr, "%s", " -x, --noslidemax show slide number, but not total number of slides\n");
42 | fprintf(stderr, "%s", " -c, --nocodebg don't change the background color of code blocks\n");
43 | fprintf(stderr, "%s", "\nWith no FILE, or when FILE is -, read standard input.\n\n");
44 | exit(EXIT_FAILURE);
45 | }
46 |
47 | void version() {
48 | printf("mdp %d.%d.%d\n", MDP_VER_MAJOR, MDP_VER_MINOR, MDP_VER_REVISION);
49 | printf("Copyright (C) 2018 Michael Goehler\n");
50 | printf("License GPLv3+: GNU GPL version 3 or later .\n");
51 | printf("This is free software: you are free to change and redistribute it.\n");
52 | printf("There is NO WARRANTY, to the extent permitted by law.\n");
53 | printf("\nWritten by Michael Goehler and others, see .\n");
54 | exit(EXIT_SUCCESS);
55 | }
56 |
57 | int main(int argc, char *argv[]) {
58 | int notrans = 0; // disable transparency
59 | int nofade = 0; // disable fading
60 | int invert = 0; // invert color (black on white)
61 | int noexpand = 1; // disable character entity expansion
62 | int reload = 0; // reload page N (0 means no reload)
63 | int noreload = 1; // reload disabled until we know input is a file
64 | int slidenum = 2; // 0:don't show; 1:show #; 2:show #/#
65 | int nocodebg = 0; // 0:show code bg as inverted; 1: don't invert code bg
66 |
67 | // define command-line options
68 | struct option longopts[] = {
69 | { "debug", no_argument, 0, 'd' },
70 | { "expand", no_argument, 0, 'e' },
71 | { "nofade", no_argument, 0, 'f' },
72 | { "help", no_argument, 0, 'h' },
73 | { "invert", no_argument, 0, 'i' },
74 | { "notrans", no_argument, 0, 't' },
75 | { "version", no_argument, 0, 'v' },
76 | { "noslidenum", no_argument, 0, 's' },
77 | { "noslidemax", no_argument, 0, 'x' },
78 | { "nocodebg", no_argument, 0, 'c' },
79 | { 0, 0, 0, 0 }
80 | };
81 |
82 | // parse command-line options
83 | int opt, debug = 0;
84 | while ((opt = getopt_long(argc, argv, ":defhitvsxc", longopts, NULL)) != -1) {
85 | switch(opt) {
86 | case 'd': debug += 1; break;
87 | case 'e': noexpand = 0; break;
88 | case 'f': nofade = 1; break;
89 | case 'h': usage(); break;
90 | case 'i': invert = 1; break;
91 | case 't': notrans = 1; break;
92 | case 'v': version(); break;
93 | case 's': slidenum = 0; break;
94 | case 'x': slidenum = 1; break;
95 | case 'c': nocodebg = 1; break;
96 | case ':': fprintf(stderr, "%s: '%c' requires an argument\n", argv[0], optopt); usage(); break;
97 | case '?':
98 | default : fprintf(stderr, "%s: option '%c' is invalid\n", argv[0], optopt); usage(); break;
99 | }
100 | }
101 |
102 | // set locale to that of the environment, so that ncurses properly renders
103 | // UTF-8 characters if the system supports it
104 | setlocale(LC_CTYPE, "");
105 |
106 | // setup list string
107 | setup_list_strings();
108 |
109 | // open file or set input to STDIN
110 | char *file = NULL;
111 | FILE *input;
112 | if (optind < argc) {
113 | do {
114 | file = argv[optind];
115 | } while(++optind < argc);
116 |
117 | if(!strcmp(file, "-")) {
118 | input = stdin;
119 | } else {
120 | input = fopen(file,"r");
121 | if(!input) {
122 | fprintf(stderr, "%s: %s: %s\n", argv[0], file, strerror(errno));
123 | exit(EXIT_FAILURE);
124 | }
125 | // enable reload because input is a file
126 | noreload = 0;
127 | }
128 | } else {
129 | input = stdin;
130 | }
131 |
132 | // reload loop
133 | do {
134 |
135 | // reopen input file on reload
136 | if(noreload == 0 && reload > 0) {
137 | if(file) {
138 | input = fopen(file,"r");
139 | if(!input) {
140 | fprintf(stderr, "%s: %s: %s\n", argv[0], file, strerror(errno));
141 | exit(EXIT_FAILURE);
142 | }
143 | } else {
144 | fprintf(stderr, "%s: %s\n", argv[0], "no input file");
145 | exit(EXIT_FAILURE);
146 | }
147 | }
148 |
149 | // load deck object from input
150 | deck_t *deck;
151 | deck = markdown_load(input, noexpand);
152 |
153 | // close file
154 | fclose(input);
155 |
156 | // replace stdin with current tty if input was a pipe
157 | // if input was a pipe reload is disabled, so we simply check that
158 | if(noreload == 1) {
159 | input = freopen("/dev/tty", "rw", stdin);
160 | if(!input) {
161 | fprintf(stderr, "%s: %s: %s\n", argv[0], "/dev/tty", strerror(errno));
162 | exit(EXIT_FAILURE);
163 | }
164 | }
165 |
166 | if(debug > 0) {
167 | markdown_debug(deck, debug);
168 | }
169 |
170 | reload = ncurses_display(deck, notrans, nofade, invert, reload, noreload, slidenum, nocodebg);
171 |
172 | free_deck(deck);
173 |
174 | // reload if supported and requested
175 | } while(noreload == 0 && reload > 0);
176 |
177 | return EXIT_SUCCESS;
178 | }
179 |
--------------------------------------------------------------------------------
/src/markdown.c:
--------------------------------------------------------------------------------
1 | /*
2 | * An implementation of markdown objects.
3 | * Copyright (C) 2018 Michael Goehler
4 | *
5 | * This file is part of mdp.
6 | *
7 | * This program is free software: you can redistribute it and/or modify
8 | * it under the terms of the GNU General Public License as published by
9 | * the Free Software Foundation, either version 3 of the License, or
10 | * (at your option) any later version.
11 | *
12 | * This program is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | * GNU General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU General Public License
18 | * along with this program. If not, see .
19 | *
20 | */
21 |
22 | #include
23 | #include
24 |
25 | #include "markdown.h"
26 |
27 | line_t *new_line() {
28 | line_t *x = malloc(sizeof(line_t));
29 | x->text = NULL;
30 | x->prev = x->next = NULL;
31 | x->bits = x->length = x->offset = 0;
32 | return x;
33 | }
34 |
35 | line_t *next_line(line_t *prev) {
36 | line_t *x = new_line();
37 | x->prev = prev;
38 | prev->next = x;
39 | return x;
40 | }
41 |
42 | slide_t *new_slide() {
43 | slide_t *x = malloc(sizeof(slide_t));
44 | x->line = NULL;
45 | x->prev = x->next = NULL;
46 | x->lines = x->stop = 0;
47 | return x;
48 | }
49 |
50 | slide_t *next_slide(slide_t *prev) {
51 | slide_t *x = new_slide();
52 | x->prev = prev;
53 | prev->next = x;
54 | return x;
55 | }
56 |
57 | deck_t *new_deck() {
58 | deck_t *x = malloc(sizeof(deck_t));
59 | x->header = NULL;
60 | x->slide = new_slide();
61 | x->slides = x->headers = 0;
62 | return x;
63 | }
64 |
65 | void free_line(line_t *line) {
66 | line_t *next;
67 | while (line) {
68 | next = line->next;
69 | if(line->text)
70 | (line->text->delete)(line->text);
71 | free(line);
72 | line = next;
73 | }
74 | }
75 |
76 | void free_deck(deck_t *deck) {
77 | slide_t *slide, *next;
78 | if (deck == NULL)
79 | return;
80 | slide = deck->slide;
81 | while (slide) {
82 | free_line(slide->line);
83 | next = slide->next;
84 | free(slide);
85 | slide = next;
86 | }
87 | free_line(deck->header);
88 | free(deck);
89 | }
90 |
--------------------------------------------------------------------------------
/src/parser.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Functions necessary to parse a file and transform its content into
3 | * a deck of slides containing lines. All based on markdown formating
4 | * rules.
5 | * Copyright (C) 2018 Michael Goehler
6 | *
7 | * This file is part of mdp.
8 | *
9 | * This program is free software: you can redistribute it and/or modify
10 | * it under the terms of the GNU General Public License as published by
11 | * the Free Software Foundation, either version 3 of the License, or
12 | * (at your option) any later version.
13 | *
14 | * This program is distributed in the hope that it will be useful,
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | * GNU General Public License for more details.
18 | *
19 | * You should have received a copy of the GNU General Public License
20 | * along with this program. If not, see .
21 | *
22 | */
23 |
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include
31 |
32 | #include "parser.h"
33 |
34 | // char entry translation table
35 | static struct named_character_entity {
36 | wchar_t ucs;
37 | const wchar_t *name;
38 | } named_character_entities[] = {
39 | { L'\x0022', L"quot" },
40 | { L'\x0026', L"amp" },
41 | { L'\x0027', L"apos" },
42 | { L'\x003C', L"lt" },
43 | { L'\x003E', L"gt" },
44 | { L'\x00A2', L"cent" },
45 | { L'\x00A3', L"pound" },
46 | { L'\x00A5', L"yen" },
47 | { L'\x00A7', L"sect" },
48 | { L'\x00A9', L"copy" },
49 | { L'\x00AA', L"laquo" },
50 | { L'\x00AE', L"reg" },
51 | { L'\x00B0', L"deg" },
52 | { L'\x00B1', L"plusmn" },
53 | { L'\x00B2', L"sup2" },
54 | { L'\x00B3', L"sup3" },
55 | { L'\x00B6', L"para" },
56 | { L'\x00B9', L"sup1" },
57 | { L'\x00BB', L"raquo" },
58 | { L'\x00BC', L"frac14" },
59 | { L'\x00BD', L"frac12" },
60 | { L'\x00BE', L"frac34" },
61 | { L'\x00D7', L"times" },
62 | { L'\x00F7', L"divide" },
63 | { L'\x2018', L"lsquo" },
64 | { L'\x2019', L"rsquo" },
65 | { L'\x201C', L"ldquo" },
66 | { L'\x201D', L"rdquo" },
67 | { L'\x2020', L"dagger" },
68 | { L'\x2021', L"Dagger" },
69 | { L'\x2022', L"bull" },
70 | { L'\x2026', L"hellip" },
71 | { L'\x2030', L"permil" },
72 | { L'\x2032', L"prime" },
73 | { L'\x2033', L"Prime" },
74 | { L'\x2039', L"lsaquo" },
75 | { L'\x203A', L"rsaquo" },
76 | { L'\x20AC', L"euro" },
77 | { L'\x2122', L"trade" },
78 | { L'\x2190', L"larr" },
79 | { L'\x2191', L"uarr" },
80 | { L'\x2192', L"rarr" },
81 | { L'\x2193', L"darr" },
82 | { L'\x2194', L"harr" },
83 | { L'\x21B5', L"crarr" },
84 | { L'\x21D0', L"lArr" },
85 | { L'\x21D1', L"uArr" },
86 | { L'\x21D2', L"rArr" },
87 | { L'\x21D3', L"dArr" },
88 | { L'\x21D4', L"hArr" },
89 | { L'\x221E', L"infin" },
90 | { L'\x2261', L"equiv" },
91 | { L'\x2308', L"lceil" },
92 | { L'\x2309', L"rceil" },
93 | { L'\x230A', L"lfloor" },
94 | { L'\x230B', L"rfloor" },
95 | { L'\x25CA', L"loz" },
96 | { L'\x2660', L"spades" },
97 | { L'\x2663', L"clubs" },
98 | { L'\x2665', L"hearts" },
99 | { L'\x2666', L"diams" },
100 | { L'\0', NULL },
101 | };
102 |
103 | deck_t *markdown_load(FILE *input, int noexpand) {
104 |
105 | wchar_t c = L'\0'; // char
106 | int i = 0; // increment
107 | int hc = 0; // header count
108 | int lc = 0; // line count
109 | int sc = 1; // slide count
110 | int bits = 0; // markdown bits
111 | int prev = 0; // markdown bits of previous line
112 |
113 | deck_t *deck = new_deck();
114 | slide_t *slide = deck->slide;
115 | line_t *line = NULL;
116 | line_t *tmp = NULL;
117 | cstring_t *text = cstring_init();
118 |
119 | // initialize bits as empty line
120 | SET_BIT(bits, IS_EMPTY);
121 |
122 | while ((c = fgetwc(input)) != WEOF) {
123 | if (ferror(input)) {
124 | fprintf(stderr, "markdown_load() failed to read input: %s\n", strerror(errno));
125 | exit(EXIT_FAILURE);
126 | }
127 |
128 | if(c == L'\n') {
129 |
130 | // markdown analyse
131 | prev = bits;
132 | bits = markdown_analyse(text, prev);
133 |
134 | // if first line in file is markdown hr
135 | if(!line && CHECK_BIT(bits, IS_HR)) {
136 |
137 | // clear text
138 | (text->reset)(text);
139 |
140 | } else if(line && CHECK_BIT(bits, IS_STOP)) {
141 |
142 | // set stop bit on last line
143 | SET_BIT(line->bits, IS_STOP);
144 |
145 | // clear text
146 | (text->reset)(text);
147 |
148 | // if text is markdown hr
149 | } else if(CHECK_BIT(bits, IS_HR) &&
150 | CHECK_BIT(line->bits, IS_EMPTY)) {
151 |
152 | slide->lines = lc;
153 |
154 | // clear text
155 | (text->reset)(text);
156 |
157 | // create next slide
158 | slide = next_slide(slide);
159 | sc++;
160 |
161 | } else if((CHECK_BIT(bits, IS_TILDE_CODE) ||
162 | CHECK_BIT(bits, IS_GFM_CODE)) &&
163 | CHECK_BIT(bits, IS_EMPTY)) {
164 | // remove tilde code markers
165 | (text->reset)(text);
166 |
167 | } else {
168 |
169 | // if slide ! has line
170 | if(!slide->line || !line) {
171 |
172 | // create new line
173 | line = new_line();
174 | slide->line = line;
175 | lc = 1;
176 |
177 | } else {
178 |
179 | // create next line
180 | line = next_line(line);
181 | lc++;
182 |
183 | }
184 |
185 | // add text to line
186 | line->text = text;
187 |
188 | // add bits to line
189 | line->bits = bits;
190 |
191 | // calc offset
192 | line->offset = next_nonblank(text, 0);
193 |
194 | // expand character entities if enabled
195 | if(line->text->value &&
196 | !noexpand &&
197 | !CHECK_BIT(line->bits, IS_CODE))
198 | expand_character_entities(line);
199 |
200 | // adjust line length dynamicaly - excluding markup
201 | if(line->text->value)
202 | adjust_line_length(line);
203 |
204 | // new text
205 | text = cstring_init();
206 | }
207 |
208 | } else if(c == L'\t') {
209 |
210 | // expand tab to spaces
211 | for (i = 0; i < EXPAND_TABS; i++) {
212 | (text->expand)(text, L' ');
213 | }
214 |
215 | } else if(c == L'\\') {
216 |
217 | // add char to line
218 | (text->expand)(text, c);
219 |
220 | // if !IS_CODE add next char to line
221 | // and do not increase line count
222 | if(next_nonblank(text, 0) < CODE_INDENT) {
223 |
224 | c = fgetwc(input);
225 | (text->expand)(text, c);
226 | }
227 |
228 | } else if(iswprint(c) || iswspace(c)) {
229 |
230 | // add char to line
231 | (text->expand)(text, c);
232 | }
233 | }
234 | (text->delete)(text);
235 |
236 | slide->lines = lc;
237 | deck->slides = sc;
238 |
239 | // detect header
240 | line = deck->slide->line;
241 | if(line && line->text->size > 0 && line->text->value[0] == L'%') {
242 |
243 | // assign header to deck
244 | deck->header = line;
245 |
246 | // find first non-header line
247 | while(line && line->text->size > 0 && line->text->value[0] == L'%') {
248 | hc++;
249 | line = line->next;
250 | }
251 |
252 | // only split header if any non-header line is found
253 | if(line) {
254 |
255 | // split linked list
256 | line->prev->next = NULL;
257 | line->prev = NULL;
258 |
259 | // remove header lines from slide
260 | deck->slide->line = line;
261 |
262 | // adjust counts
263 | deck->headers += hc;
264 | deck->slide->lines -= hc;
265 | } else {
266 |
267 | // remove header from deck
268 | deck->header = NULL;
269 | }
270 | }
271 |
272 | slide = deck->slide;
273 | while(slide) {
274 | line = slide->line;
275 |
276 | // ignore mdpress format attributes
277 | if(line &&
278 | slide->lines > 1 &&
279 | !CHECK_BIT(line->bits, IS_EMPTY) &&
280 | line->text->value[line->offset] == L'=' &&
281 | line->text->value[line->offset + 1] == L' ') {
282 |
283 | // remove line from linked list
284 | slide->line = line->next;
285 | line->next->prev = NULL;
286 |
287 | // maintain loop condition
288 | tmp = line;
289 | line = line->next;
290 |
291 | // adjust line count
292 | slide->lines -= 1;
293 |
294 | // delete line
295 | (tmp->text->delete)(tmp->text);
296 | free(tmp);
297 | }
298 |
299 | while(line) {
300 | // combine underlined H1/H2 in single line
301 | if((CHECK_BIT(line->bits, IS_H1) ||
302 | CHECK_BIT(line->bits, IS_H2)) &&
303 | CHECK_BIT(line->bits, IS_EMPTY) &&
304 | line->prev &&
305 | !CHECK_BIT(line->prev->bits, IS_EMPTY)) {
306 |
307 |
308 | // remove line from linked list
309 | line->prev->next = line->next;
310 | if(line->next)
311 | line->next->prev = line->prev;
312 |
313 | // set bits on previous line
314 | if(CHECK_BIT(line->bits, IS_H1)) {
315 | SET_BIT(line->prev->bits, IS_H1);
316 | } else {
317 | SET_BIT(line->prev->bits, IS_H2);
318 | }
319 |
320 | // adjust line count
321 | slide->lines -= 1;
322 |
323 | // maintain loop condition
324 | tmp = line;
325 | line = line->prev;
326 |
327 | // delete line
328 | (tmp->text->delete)(tmp->text);
329 | free(tmp);
330 |
331 | // pass enclosing flag IS_UNORDERED_LIST_3
332 | // to nested levels for unordered lists
333 | } else if(CHECK_BIT(line->bits, IS_UNORDERED_LIST_3)) {
334 | tmp = line->next;
335 | line_t *list_last_level_3 = line;
336 |
337 | while(tmp &&
338 | CHECK_BIT(tmp->bits, IS_UNORDERED_LIST_3)) {
339 | if(CHECK_BIT(tmp->bits, IS_UNORDERED_LIST_3)) {
340 | list_last_level_3 = tmp;
341 | }
342 | tmp = tmp->next;
343 | }
344 |
345 | for(tmp = line; tmp != list_last_level_3; tmp = tmp->next) {
346 | SET_BIT(tmp->bits, IS_UNORDERED_LIST_3);
347 | }
348 |
349 | // pass enclosing flag IS_UNORDERED_LIST_2
350 | // to nested levels for unordered lists
351 | } else if(CHECK_BIT(line->bits, IS_UNORDERED_LIST_2)) {
352 | tmp = line->next;
353 | line_t *list_last_level_2 = line;
354 |
355 | while(tmp &&
356 | (CHECK_BIT(tmp->bits, IS_UNORDERED_LIST_2) ||
357 | CHECK_BIT(tmp->bits, IS_UNORDERED_LIST_3))) {
358 | if(CHECK_BIT(tmp->bits, IS_UNORDERED_LIST_2)) {
359 | list_last_level_2 = tmp;
360 | }
361 | tmp = tmp->next;
362 | }
363 |
364 | for(tmp = line; tmp != list_last_level_2; tmp = tmp->next) {
365 | SET_BIT(tmp->bits, IS_UNORDERED_LIST_2);
366 | }
367 |
368 | // pass enclosing flag IS_UNORDERED_LIST_1
369 | // to nested levels for unordered lists
370 | } else if(CHECK_BIT(line->bits, IS_UNORDERED_LIST_1)) {
371 | tmp = line->next;
372 | line_t *list_last_level_1 = line;
373 |
374 | while(tmp &&
375 | (CHECK_BIT(tmp->bits, IS_UNORDERED_LIST_1) ||
376 | CHECK_BIT(tmp->bits, IS_UNORDERED_LIST_2) ||
377 | CHECK_BIT(tmp->bits, IS_UNORDERED_LIST_3))) {
378 | if(CHECK_BIT(tmp->bits, IS_UNORDERED_LIST_1)) {
379 | list_last_level_1 = tmp;
380 | }
381 | tmp = tmp->next;
382 | }
383 |
384 | for(tmp = line; tmp != list_last_level_1; tmp = tmp->next) {
385 | SET_BIT(tmp->bits, IS_UNORDERED_LIST_1);
386 | }
387 | }
388 |
389 | line = line->next;
390 | }
391 | slide = slide->next;
392 | }
393 |
394 | return deck;
395 | }
396 |
397 | int markdown_analyse(cstring_t *text, int prev) {
398 |
399 | // static variables can not be redeclaired, but changed outside of a declaration
400 | // the program remembers their value on every function calls
401 | static int unordered_list_level = 0;
402 | static int unordered_list_level_offset[] = {-1, -1, -1, -1};
403 | static int num_tilde_characters = 0;
404 | static int num_backticks = 0;
405 |
406 | int i = 0; // increment
407 | int bits = 0; // markdown bits
408 | int offset = 0; // text offset
409 | int eol = 0; // end of line
410 |
411 | int equals = 0, hashes = 0,
412 | stars = 0, minus = 0,
413 | spaces = 0, other = 0; // special character counts
414 |
415 | const int unordered_list_offset = unordered_list_level_offset[unordered_list_level];
416 |
417 | // return IS_EMPTY on null pointers
418 | if(!text || !text->value) {
419 | SET_BIT(bits, IS_EMPTY);
420 |
421 | // continue fenced code blocks across empty lines
422 | if(num_tilde_characters > 0)
423 | SET_BIT(bits, IS_CODE);
424 |
425 | return bits;
426 | }
427 |
428 | // count leading spaces
429 | offset = next_nonblank(text, 0);
430 |
431 | // IS_TILDE_CODE
432 | if (wcsncmp(text->value, L"~~~", 3) == 0) {
433 | int tildes_in_line = next_nontilde(text, 0);
434 | if (tildes_in_line >= num_tilde_characters) {
435 | if (num_tilde_characters > 0) {
436 | num_tilde_characters = 0;
437 | } else {
438 | num_tilde_characters = tildes_in_line;
439 | }
440 | SET_BIT(bits, IS_EMPTY);
441 | SET_BIT(bits, IS_TILDE_CODE);
442 | return bits;
443 | }
444 | }
445 |
446 | if (num_tilde_characters > 0) {
447 | SET_BIT(bits, IS_CODE);
448 | SET_BIT(bits, IS_TILDE_CODE);
449 | return bits;
450 | }
451 |
452 | // IS_GFM_CODE
453 | if (wcsncmp(text->value, L"```", 3) == 0) {
454 | int backticks_in_line = next_nonbacktick(text, 0);
455 | if (backticks_in_line >= num_backticks) {
456 | if (num_backticks > 0) {
457 | num_backticks = 0;
458 | } else {
459 | num_backticks = backticks_in_line;
460 | }
461 | SET_BIT(bits, IS_EMPTY);
462 | SET_BIT(bits, IS_GFM_CODE);
463 | return bits;
464 | }
465 | }
466 |
467 | if (num_backticks > 0) {
468 | SET_BIT(bits, IS_CODE);
469 | SET_BIT(bits, IS_GFM_CODE);
470 | return bits;
471 | }
472 |
473 | // IS_STOP
474 | if((offset < CODE_INDENT || !CHECK_BIT(prev, IS_CODE)) &&
475 | (!wcsncmp(&text->value[offset], L"
", 4) ||
476 | !wcsncmp(&text->value[offset], L"
", 4) ||
477 | !wcsncmp(&text->value[offset], L"^", 1))) {
478 | SET_BIT(bits, IS_STOP);
479 | return bits;
480 | }
481 |
482 | // strip trailing spaces
483 | for(eol = text->size; eol > offset && iswspace(text->value[eol - 1]); eol--);
484 | text->size = eol;
485 |
486 | // IS_UNORDERED_LIST_#
487 | if(text->size >= offset + 2 &&
488 | (text->value[offset] == L'*' || text->value[offset] == L'-') &&
489 | iswspace(text->value[offset + 1])) {
490 |
491 | // if different from last lines offset
492 | if(offset != unordered_list_offset) {
493 |
494 | // test if offset matches a lower indent level
495 | for(i = unordered_list_level; i >= 0; i--) {
496 | if(unordered_list_level_offset[i] == offset) {
497 | unordered_list_level = i;
498 | break;
499 | }
500 | }
501 | // if offset doesn't match any previously stored indent level
502 | if(i != unordered_list_level) {
503 | unordered_list_level = MIN(unordered_list_level + 1, UNORDERED_LIST_MAX_LEVEL);
504 | // memorize the offset as next bigger indent level
505 | unordered_list_level_offset[unordered_list_level] = offset;
506 | }
507 | }
508 |
509 | // if no previous indent level matches, this must be the first line of the list
510 | if(unordered_list_level == 0) {
511 | unordered_list_level = 1;
512 | unordered_list_level_offset[1] = offset;
513 | }
514 |
515 | switch(unordered_list_level) {
516 | case 1: SET_BIT(bits, IS_UNORDERED_LIST_1); break;
517 | case 2: SET_BIT(bits, IS_UNORDERED_LIST_2); break;
518 | case 3: SET_BIT(bits, IS_UNORDERED_LIST_3); break;
519 | default: break;
520 | }
521 | }
522 |
523 | if(!CHECK_BIT(bits, IS_UNORDERED_LIST_1) &&
524 | !CHECK_BIT(bits, IS_UNORDERED_LIST_2) &&
525 | !CHECK_BIT(bits, IS_UNORDERED_LIST_3)) {
526 |
527 | // continue list if indent level is still the same as in previous line
528 | if ((CHECK_BIT(prev, IS_UNORDERED_LIST_1) ||
529 | CHECK_BIT(prev, IS_UNORDERED_LIST_2) ||
530 | CHECK_BIT(prev, IS_UNORDERED_LIST_3)) &&
531 | offset >= unordered_list_offset) {
532 |
533 | switch(unordered_list_level) {
534 | case 1: SET_BIT(bits, IS_UNORDERED_LIST_1); break;
535 | case 2: SET_BIT(bits, IS_UNORDERED_LIST_2); break;
536 | case 3: SET_BIT(bits, IS_UNORDERED_LIST_3); break;
537 | default: break;
538 | }
539 |
540 | // this line extends the previous list item
541 | SET_BIT(bits, IS_UNORDERED_LIST_EXT);
542 |
543 | // or reset indent level
544 | } else {
545 | unordered_list_level = 0;
546 | }
547 | }
548 |
549 | if(!CHECK_BIT(bits, IS_UNORDERED_LIST_1) &&
550 | !CHECK_BIT(bits, IS_UNORDERED_LIST_2) &&
551 | !CHECK_BIT(bits, IS_UNORDERED_LIST_3)) {
552 |
553 | // IS_CODE
554 | if(offset >= CODE_INDENT &&
555 | (CHECK_BIT(prev, IS_EMPTY) ||
556 | CHECK_BIT(prev, IS_CODE) ||
557 | CHECK_BIT(prev, IS_STOP))) {
558 | SET_BIT(bits, IS_CODE);
559 |
560 | } else {
561 |
562 | // IS_QUOTE
563 | if(text->value[offset] == L'>') {
564 | SET_BIT(bits, IS_QUOTE);
565 | }
566 |
567 | // IS_CENTER
568 | if(text->size >= offset + 3 &&
569 | text->value[offset] == L'-' &&
570 | text->value[offset + 1] == L'>' &&
571 | iswspace(text->value[offset + 2])) {
572 | SET_BIT(bits, IS_CENTER);
573 |
574 | // remove start tag
575 | (text->strip)(text, offset, 3);
576 | eol -= 3;
577 |
578 | if(text->size >= offset + 3 &&
579 | text->value[eol - 1] == L'-' &&
580 | text->value[eol - 2] == L'<' &&
581 | iswspace(text->value[eol - 3])) {
582 |
583 | // remove end tags
584 | (text->strip)(text, eol - 3, 3);
585 |
586 | // adjust end of line
587 | for(eol = text->size; eol > offset && iswspace(text->value[eol - 1]); eol--);
588 |
589 | }
590 | }
591 |
592 | for(i = offset; i < eol; i++) {
593 |
594 | if(iswspace(text->value[i])) {
595 | spaces++;
596 |
597 | } else {
598 | switch(text->value[i]) {
599 | case L'=': equals++; break;
600 | case L'#': hashes++; break;
601 | case L'*': stars++; break;
602 | case L'-': minus++; break;
603 | case L'\\': other++; i++; break;
604 | default: other++; break;
605 | }
606 | }
607 | }
608 |
609 | // IS_H1
610 | if(equals > 0 &&
611 | hashes + stars + minus + spaces + other == 0) {
612 | SET_BIT(bits, IS_H1);
613 | }
614 | if(text->value[offset] == L'#' &&
615 | iswspace(text->value[offset+1])) {
616 | SET_BIT(bits, IS_H1);
617 | SET_BIT(bits, IS_H1_ATX);
618 | }
619 |
620 | // IS_H2
621 | if(minus > 0 &&
622 | equals + hashes + stars + spaces + other == 0) {
623 | SET_BIT(bits, IS_H2);
624 | }
625 | if(text->value[offset] == L'#' &&
626 | text->value[offset+1] == L'#' &&
627 | iswspace(text->value[offset+2])) {
628 | SET_BIT(bits, IS_H2);
629 | SET_BIT(bits, IS_H2_ATX);
630 | }
631 |
632 | // IS_HR
633 | if((minus >= 3 && equals + hashes + stars + other == 0) ||
634 | (stars >= 3 && equals + hashes + minus + other == 0)) {
635 |
636 | SET_BIT(bits, IS_HR);
637 | }
638 |
639 | // IS_EMPTY
640 | if(other == 0) {
641 | SET_BIT(bits, IS_EMPTY);
642 | }
643 | }
644 | }
645 |
646 | return bits;
647 | }
648 |
649 | void markdown_debug(deck_t *deck, int debug) {
650 |
651 | int sc = 0; // slide count
652 | int lc = 0; // line count
653 |
654 | int offset;
655 | line_t *header;
656 |
657 | if(debug == 1) {
658 | fwprintf(stderr, L"headers: %i\nslides: %i\n", deck->headers, deck->slides);
659 |
660 | } else if(debug > 1) {
661 |
662 | // print header to STDERR
663 | if(deck->header) {
664 | header = deck->header;
665 | while(header &&
666 | header->length > 0 &&
667 | header->text->value[0] == L'%') {
668 |
669 | // skip descriptor word (e.g. %title:)
670 | offset = next_blank(header->text, 0) + 1;
671 |
672 | fwprintf(stderr, L"header: %S\n", &header->text->value[offset]);
673 | header = header->next;
674 | }
675 | }
676 | }
677 |
678 | slide_t *slide = deck->slide;
679 | line_t *line;
680 |
681 | // print slide/line count to STDERR
682 | while(slide) {
683 | sc++;
684 |
685 | if(debug == 1) {
686 | fwprintf(stderr, L" slide %i: %i lines\n", sc, slide->lines);
687 |
688 | } else if(debug > 1) {
689 |
690 | // also print bits and line length
691 | fwprintf(stderr, L" slide %i:\n", sc);
692 | line = slide->line;
693 | lc = 0;
694 | while(line) {
695 | lc++;
696 | fwprintf(stderr, L" line %i: bits = %i, length = %i\n", lc, line->bits, line->length);
697 | line = line->next;
698 | }
699 | }
700 |
701 | slide = slide->next;
702 | }
703 | }
704 |
705 | void expand_character_entities(line_t *line)
706 | {
707 | wchar_t *ampersand;
708 | wchar_t *prev, *curr;
709 |
710 | ampersand = NULL;
711 | curr = &line->text->value[0];
712 |
713 | // for each char in line
714 | for(prev = NULL; *curr; prev = curr++) {
715 | if (*curr == L'&' && (prev == NULL || *prev != L'\\')) {
716 | ampersand = curr;
717 | continue;
718 | }
719 | if (ampersand == NULL) {
720 | continue;
721 | }
722 | if (*curr == L'#') {
723 | if (prev == ampersand)
724 | continue;
725 | ampersand = NULL;
726 | continue;
727 | }
728 | if (iswalpha(*curr) || iswxdigit(*curr)) {
729 | continue;
730 | }
731 | if (*curr == L';') {
732 | int cnt;
733 | wchar_t ucs = L'\0';
734 | if (ampersand + 1 >= curr || ampersand + 16 < curr) { // what is a good limit?
735 | ampersand = NULL;
736 | continue;
737 | }
738 | if (ampersand[1] == L'#') { // nnnn; or hhhh;
739 | if (ampersand + 2 >= curr) {
740 | ampersand = NULL;
741 | continue;
742 | }
743 | if (ampersand[2] != L'x') { // nnnn;
744 | cnt = wcsspn(&ersand[2], L"0123456789");
745 | if (ampersand + 2 + cnt != curr) {
746 | ampersand = NULL;
747 | continue;
748 | }
749 | ucs = wcstoul(&ersand[2], NULL, 10);
750 | } else { // hhhh;
751 | if (ampersand + 3 >= curr) {
752 | ampersand = NULL;
753 | continue;
754 | }
755 | cnt = wcsspn(&ersand[3], L"0123456789abcdefABCDEF");
756 | if (ampersand + 3 + cnt != curr) {
757 | ampersand = NULL;
758 | continue;
759 | }
760 | ucs = wcstoul(&ersand[3], NULL, 16);
761 | }
762 | } else { // &name;
763 | for (cnt = 0; cnt < sizeof(named_character_entities)/sizeof(named_character_entities[0]); ++cnt) {
764 | if (wcsncmp(named_character_entities[cnt].name, &ersand[1], curr - ampersand - 1))
765 | continue;
766 | ucs = named_character_entities[cnt].ucs;
767 | break;
768 | }
769 | if (ucs == L'\0') {
770 | ampersand = NULL;
771 | continue;
772 | }
773 | }
774 | *ampersand = ucs;
775 | cstring_strip(line->text, ampersand + 1 - &line->text->value[0], curr - ampersand);
776 | curr = ampersand;
777 | ampersand = NULL;
778 | }
779 | }
780 | }
781 |
782 | void adjust_line_length(line_t *line) {
783 | int l = 0;
784 | const static wchar_t *special = L"\\*_`"; // list of interpreted chars
785 | const wchar_t *c = &line->text->value[0];
786 | cstack_t *stack = cstack_init();
787 |
788 | // for each char in line
789 | for(; *c; c++) {
790 | // if char is in special char list
791 | if(wcschr(special, *c)) {
792 |
793 | // closing special char (or second backslash)
794 | if((stack->top)(stack, *c)) {
795 | if(*c == L'\\') l++;
796 | (stack->pop)(stack);
797 |
798 | // treat special as regular char
799 | } else if((stack->top)(stack, L'\\')) {
800 | l++;
801 | (stack->pop)(stack);
802 |
803 | // opening special char
804 | } else {
805 | (stack->push)(stack, *c);
806 | }
807 |
808 | } else {
809 | // remove backslash from stack
810 | if((stack->top)(stack, L'\\'))
811 | (stack->pop)(stack);
812 | l++;
813 | }
814 | }
815 |
816 | if(CHECK_BIT(line->bits, IS_H1_ATX))
817 | l -= 2;
818 | if(CHECK_BIT(line->bits, IS_H2_ATX))
819 | l -= 3;
820 |
821 | line->length = l;
822 |
823 | (stack->delete)(stack);
824 | }
825 |
826 | int next_nonblank(cstring_t *text, int i) {
827 | while ((i < text->size) && iswspace((text->value)[i]))
828 | i++;
829 |
830 | return i;
831 | }
832 |
833 | int prev_blank(cstring_t *text, int i) {
834 | while ((i > 0) && !iswspace((text->value)[i]))
835 | i--;
836 |
837 | return i;
838 | }
839 |
840 | int next_blank(cstring_t *text, int i) {
841 | while ((i < text->size) && !iswspace((text->value)[i]))
842 | i++;
843 |
844 | return i;
845 | }
846 |
847 | int next_word(cstring_t *text, int i) {
848 | return next_nonblank(text, next_blank(text, i));
849 | }
850 |
851 | int next_nontilde(cstring_t *text, int i) {
852 | while ((i < text->size) && text->value[i] == L'~')
853 | i++;
854 |
855 | return i;
856 | }
857 |
858 | int next_nonbacktick(cstring_t *text, int i) {
859 | while ((i < text->size) && text->value[i] == L'`')
860 | i++;
861 |
862 | return i;
863 | }
864 |
865 |
--------------------------------------------------------------------------------
/src/url.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Functions necessary to handle pandoc URLs.
3 | * Copyright (C) 2018 Michael Goehler
4 | *
5 | * This file is part of mdp.
6 | *
7 | * This program is free software: you can redistribute it and/or modify
8 | * it under the terms of the GNU General Public License as published by
9 | * the Free Software Foundation, either version 3 of the License, or
10 | * (at your option) any later version.
11 | *
12 | * This program is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | * GNU General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU General Public License
18 | * along with this program. If not, see .
19 | *
20 | */
21 |
22 | #include
23 | #include
24 | #include
25 | #include
26 |
27 | #include "url.h"
28 |
29 | static void url_del_elem(url_t *elem);
30 | static void url_print(url_t *u);
31 |
32 | static url_t *list;
33 | static int index_max;
34 | static int init_ok;
35 |
36 | void url_init(void) {
37 | list = NULL;
38 | index_max = 0;
39 | init_ok = 1;
40 | }
41 |
42 | int url_add(const wchar_t *link_name, int link_name_length, const wchar_t *target, int target_length, int x, int y) {
43 | if (!init_ok) return -1;
44 |
45 | url_t *tmp = NULL;
46 | int i = 0;
47 |
48 | if (list) {
49 | tmp = list;
50 | while (tmp->next) {
51 | tmp = tmp->next;
52 | i++;
53 | }
54 | tmp->next = malloc(sizeof(url_t));
55 | assert(tmp->next);
56 | tmp = tmp->next;
57 | } else {
58 | list = malloc(sizeof(url_t));
59 | tmp = list;
60 | assert(tmp);
61 | }
62 |
63 | tmp -> link_name = calloc(link_name_length+1, sizeof(wchar_t));
64 | assert(tmp->link_name);
65 | wcsncpy(tmp->link_name, link_name, link_name_length);
66 | tmp->link_name[link_name_length] = '\0';
67 |
68 | tmp->target = calloc(target_length+1, sizeof(wchar_t));
69 | assert(tmp->target);
70 | wcsncpy(tmp->target, target, target_length);
71 | tmp->target[target_length] = '\0';
72 |
73 | tmp->x = x;
74 | tmp->y = y;
75 | tmp->next = NULL;
76 |
77 | index_max++;
78 |
79 | return index_max-1;
80 | }
81 |
82 | wchar_t * url_get_target(int index) {
83 | if (!init_ok) return NULL;
84 |
85 | url_t *tmp = list;
86 |
87 | if (!tmp) return NULL;
88 |
89 | while (index > 0 && tmp && tmp->next) {
90 | tmp = tmp->next;
91 | index--;
92 | }
93 |
94 | if (!index) {
95 | return tmp->target;
96 | } else return NULL;
97 | }
98 |
99 | wchar_t * url_get_name(int index) {
100 | url_t *tmp = list;
101 |
102 | while (index > 0 && tmp && tmp->next) {
103 | tmp = tmp->next;
104 | index --;
105 | }
106 |
107 | if (!index) {
108 | return tmp->link_name;
109 | } else return NULL;
110 | }
111 |
112 | void url_purge() {
113 | url_del_elem(list);
114 | list = NULL;
115 | index_max = 0;
116 | init_ok = 0;
117 | }
118 |
119 | static void url_del_elem(url_t *elem) {
120 | if (!elem) return;
121 |
122 | if (elem->next) {
123 | url_del_elem(elem->next);
124 | elem->next = NULL;
125 | }
126 |
127 | if (elem->target) {
128 | free(elem->target);
129 | elem->target = NULL;
130 | }
131 |
132 | if (elem->link_name) {
133 | free(elem->link_name);
134 | elem->link_name = NULL;
135 | }
136 |
137 | free(elem);
138 | }
139 |
140 | void url_dump(void) {
141 | if (!list) return;
142 |
143 | url_t *tmp = list;
144 |
145 | while (tmp) {
146 | url_print(tmp);
147 | if (tmp->next)
148 | tmp = tmp->next;
149 | else break;
150 | }
151 | }
152 |
153 | static void url_print(url_t *u) {
154 | printf("url_t @ %p\n", u);
155 | }
156 |
157 | int url_get_amount(void) {
158 | return index_max;
159 | }
160 |
161 | int url_count_inline(const wchar_t *line) {
162 | int count = 0;
163 | const wchar_t *i = line;
164 |
165 | for (; *i; i++) {
166 | if (*i == '\\') {
167 | i++;
168 | } else if ( *i == '[' && *(i+1) && *(i+1) != ']') {
169 | while (*i && *i != ']') i++;
170 | i++;
171 | if (*i == '(' && wcschr(i, ')')) {
172 | count ++;
173 | i = wcschr(i, ')');
174 | }
175 | }
176 | }
177 |
178 | return count;
179 | }
180 |
181 | int url_len_inline(const wchar_t *value) {
182 | int count = 0;
183 | const wchar_t *i = value;
184 |
185 | for (; *i; i++) {
186 | if (*i == '\\') {
187 | i++;
188 | } else if ( *i == '[' && *(i+1) && *(i+1) != ']') {
189 | while (*i && *i != ']') i++;
190 | i++;
191 | if (*i == '(' && wcschr(i, ')')) {
192 | while (*i && *i != ')') {
193 | count++;
194 | i++;
195 | }
196 | }
197 | }
198 | }
199 |
200 | return count;
201 | }
202 |
--------------------------------------------------------------------------------
/src/viewer.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Functions necessary to display a deck of slides in different color modes
3 | * using ncurses. Only white, red, and blue are supported, as they can be
4 | * faded in 256 color mode.
5 | * Copyright (C) 2018 Michael Goehler
6 | *
7 | * This file is part of mdp.
8 | *
9 | * This program is free software: you can redistribute it and/or modify
10 | * it under the terms of the GNU General Public License as published by
11 | * the Free Software Foundation, either version 3 of the License, or
12 | * (at your option) any later version.
13 | *
14 | * This program is distributed in the hope that it will be useful,
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | * GNU General Public License for more details.
18 | *
19 | * You should have received a copy of the GNU General Public License
20 | * along with this program. If not, see .
21 | *
22 | */
23 |
24 | #include // isalnum
25 | #include // wcschr
26 | #include // iswalnum
27 | #include // strcpy
28 | #include // usleep
29 | #include // getenv
30 | #include "viewer.h"
31 | #include "config.h"
32 |
33 | int ncurses_display(deck_t *deck, int notrans, int nofade, int invert, int reload, int noreload, int slidenum, int nocodebg) {
34 |
35 | int c = 0; // char
36 | int i = 0; // iterate
37 | int l = 0; // line number
38 | int lc = 0; // line count
39 | int sc = 1; // slide count
40 | int colors = 0; // amount of colors supported
41 | int fade = 0; // disable color fading by default
42 | int trans = -1; // enable transparency if term supports it
43 | int max_lines = 0; // max lines per slide
44 | int max_lines_slide = -1; // the slide that has the most lines
45 | int max_cols = 0; // max columns per line
46 | int offset; // text offset
47 | int stop = 0; // passed stop bits per slide
48 |
49 | // header line 1 is displayed at the top
50 | int bar_top = (deck->headers > 0) ? 1 : 0;
51 | // header line 2 is displayed at the bottom
52 | // anyway we display the slide number at the bottom
53 | int bar_bottom = (slidenum || deck->headers > 1)? 1 : 0;
54 |
55 | slide_t *slide = deck->slide;
56 | line_t *line;
57 |
58 | // init ncurses
59 | initscr();
60 |
61 | while(slide) {
62 | lc = 0;
63 | line = slide->line;
64 |
65 | while(line && line->text) {
66 |
67 | if (line->text->value) {
68 | lc += url_count_inline(line->text->value);
69 | line->length -= url_len_inline(line->text->value);
70 | }
71 |
72 | if(line->length > COLS) {
73 | i = line->length;
74 | offset = 0;
75 | while(i > COLS) {
76 |
77 | i = prev_blank(line->text, offset + COLS) - offset;
78 |
79 | // single word is > COLS
80 | if(!i) {
81 | // calculate min_width
82 | i = next_blank(line->text, offset + COLS) - offset;
83 |
84 | // disable ncurses
85 | endwin();
86 |
87 | // print error
88 | fwprintf(stderr, L"Error: Terminal width (%i columns) too small. Need at least %i columns.\n", COLS, i);
89 | fwprintf(stderr, L"You may need to shorten some lines by inserting line breaks.\n");
90 |
91 | // no reload
92 | return 0;
93 | }
94 |
95 | // set max_cols
96 | max_cols = MAX(i, max_cols);
97 |
98 | // iterate to next line
99 | offset = prev_blank(line->text, offset + COLS);
100 | i = line->length - offset;
101 | lc++;
102 | }
103 | // set max_cols one last time
104 | max_cols = MAX(i, max_cols);
105 | } else {
106 | // set max_cols
107 | max_cols = MAX(line->length, max_cols);
108 | }
109 | lc++;
110 | line = line->next;
111 | }
112 |
113 | max_lines = MAX(lc, max_lines);
114 | if (lc == max_lines) {
115 | max_lines_slide = sc;
116 | }
117 |
118 | slide->lines_consumed = lc;
119 | slide = slide->next;
120 | ++sc;
121 | }
122 |
123 | // not enough lines
124 | if(max_lines + bar_top + bar_bottom > LINES) {
125 |
126 | // disable ncurses
127 | endwin();
128 |
129 | // print error
130 | fwprintf(stderr, L"Error: Terminal height (%i lines) too small. Need at least %i lines for slide #%i.\n", LINES, max_lines + bar_top + bar_bottom, max_lines_slide);
131 | fwprintf(stderr, L"You may need to add additional horizontal rules (---) to split your file in shorter slides.\n");
132 |
133 | // no reload
134 | return 0;
135 | }
136 |
137 | // disable cursor
138 | curs_set(0);
139 |
140 | // disable output of keyboard typing
141 | noecho();
142 |
143 | // make getch() process one char at a time
144 | cbreak();
145 |
146 | // enable arrow keys
147 | keypad(stdscr,TRUE);
148 |
149 | // set colors
150 | if(has_colors() == TRUE) {
151 | start_color();
152 | use_default_colors();
153 |
154 | // 256 color mode
155 | if(COLORS == 256) {
156 |
157 | if(notrans) {
158 | if(invert) {
159 | trans = 15; // white in 256 color mode
160 | } else {
161 | trans = 16; // black in 256 color mode
162 | }
163 | }
164 |
165 | if(invert) {
166 | init_pair(CP_WHITE, 232, trans);
167 | init_pair(CP_BLUE, 21, trans);
168 | init_pair(CP_RED, 196, trans);
169 | init_pair(CP_BLACK, 15, 232);
170 | } else {
171 | init_pair(CP_WHITE, 255, trans);
172 | init_pair(CP_BLUE, 123, trans);
173 | init_pair(CP_RED, 213, trans);
174 | init_pair(CP_BLACK, 16, 255);
175 | }
176 | init_pair(CP_YELLOW, 208, trans);
177 |
178 | // enable color fading
179 | if(!nofade)
180 | fade = true;
181 |
182 | // 8 color mode
183 | } else {
184 |
185 | if(notrans) {
186 | if(invert) {
187 | trans = FG_COLOR; // white in 8 color mode
188 | } else {
189 | trans = BG_COLOR; // black in 8 color mode
190 | }
191 | }
192 |
193 | if(invert) {
194 | init_pair(CP_WHITE, BG_COLOR, trans);
195 | init_pair(CP_BLACK, FG_COLOR, BG_COLOR);
196 | } else {
197 | init_pair(CP_WHITE, FG_COLOR, trans);
198 | init_pair(CP_BLACK, BG_COLOR, FG_COLOR);
199 | }
200 | init_pair(CP_BLUE, HEADER_COLOR, trans);
201 | init_pair(CP_RED, BOLD_COLOR, trans);
202 | init_pair(CP_YELLOW, TITLE_COLOR, trans);
203 | }
204 |
205 | colors = 1;
206 | }
207 |
208 | // set background color for main window
209 | if(colors)
210 | wbkgd(stdscr, COLOR_PAIR(CP_WHITE));
211 |
212 | // setup content window
213 | WINDOW *content = newwin(LINES - bar_top - bar_bottom, COLS, 0 + bar_top, 0);
214 |
215 | // set background color of content window
216 | if(colors)
217 | wbkgd(content, COLOR_PAIR(CP_WHITE));
218 |
219 | slide = deck->slide;
220 |
221 | // find slide to reload
222 | sc = 1;
223 | while(reload > 1 && reload <= deck->slides) {
224 | slide = slide->next;
225 | sc++;
226 | reload--;
227 | }
228 |
229 | // reset reload indicator
230 | reload = 0;
231 |
232 | while(slide) {
233 |
234 | url_init();
235 |
236 | // clear windows
237 | werase(content);
238 | werase(stdscr);
239 |
240 | // always resize window in case terminal geometry has changed
241 | wresize(content, LINES - bar_top - bar_bottom, COLS);
242 |
243 | // set main window text color
244 | if(colors)
245 | wattron(stdscr, COLOR_PAIR(CP_YELLOW));
246 |
247 | // setup header
248 | if(bar_top) {
249 | line = deck->header;
250 | offset = next_blank(line->text, 0) + 1;
251 | // add text to header
252 | mvwaddwstr(stdscr,
253 | 0, (COLS - line->length + offset) / 2,
254 | &line->text->value[offset]);
255 | }
256 |
257 | // setup footer
258 | if(deck->headers > 1) {
259 | line = deck->header->next;
260 | offset = next_blank(line->text, 0) + 1;
261 | switch(slidenum) {
262 | case 0: // add text to center footer
263 | mvwaddwstr(stdscr,
264 | LINES - 1, (COLS - line->length + offset) / 2,
265 | &line->text->value[offset]);
266 | break;
267 | case 1:
268 | case 2: // add text to left footer
269 | mvwaddwstr(stdscr,
270 | LINES - 1, 3,
271 | &line->text->value[offset]);
272 | break;
273 | }
274 | }
275 |
276 | // add slide number to right footer
277 | switch(slidenum) {
278 | case 1: // show slide number only
279 | mvwprintw(stdscr,
280 | LINES - 1, COLS - int_length(sc) - 3,
281 | "%d", sc);
282 | break;
283 | case 2: // show current slide & number of slides
284 | mvwprintw(stdscr,
285 | LINES - 1, COLS - int_length(deck->slides) - int_length(sc) - 6,
286 | "%d / %d", sc, deck->slides);
287 | break;
288 | }
289 |
290 | // copy changed lines in main window to virtual screen
291 | wnoutrefresh(stdscr);
292 |
293 | line = slide->line;
294 | l = stop = 0;
295 |
296 | // print lines
297 | while(line) {
298 | add_line(content, l + ((LINES - slide->lines_consumed - bar_top - bar_bottom) / 2),
299 | (COLS - max_cols) / 2, line, max_cols, colors, nocodebg);
300 |
301 | // raise stop counter if we pass a line having a stop bit
302 | if(CHECK_BIT(line->bits, IS_STOP))
303 | stop++;
304 |
305 | l += (line->length / COLS) + 1;
306 | line = line->next;
307 |
308 | // only stop here if we didn't stop here recently
309 | if(stop > slide->stop)
310 | break;
311 | }
312 |
313 | // print pandoc URL references
314 | // only if we already printed all lines of the current slide (or output is stopped)
315 | if(!line ||
316 | stop > slide->stop) {
317 | int i, ymax;
318 | getmaxyx( content, ymax, i );
319 | for (i = 0; i < url_get_amount(); i++) {
320 | mvwprintw(content, ymax - url_get_amount() - 1 + i, 3,
321 | "[%d] ", i);
322 | waddwstr(content, url_get_target(i));
323 | }
324 | }
325 |
326 | // copy changed lines in content window to virtual screen
327 | wnoutrefresh(content);
328 |
329 | // compare virtual screen to physical screen and does the actual updates
330 | doupdate();
331 |
332 | // fade in
333 | if(fade)
334 | fade_in(content, trans, colors, invert);
335 |
336 | // re-enable fading after any undefined key press
337 | if(COLORS == 256 && !nofade)
338 | fade = true;
339 |
340 | // wait for user input
341 | c = getch();
342 |
343 | // evaluate user input
344 | i = 0;
345 |
346 | if (evaluate_binding(prev_slide_binding, c)) {
347 | // show previous slide or stop bit
348 | if(stop > 1 || (stop == 1 && !line)) {
349 | // show current slide again
350 | // but stop one stop bit earlier
351 | slide->stop--;
352 | fade = false;
353 | } else {
354 | if(slide->prev) {
355 | // show previous slide
356 | slide = slide->prev;
357 | sc--;
358 | //stop on first bullet point always
359 | if(slide->stop > 0)
360 | slide->stop = 0;
361 | } else {
362 | // do nothing
363 | fade = false;
364 | }
365 | }
366 | } else if (evaluate_binding(next_slide_binding, c)) {
367 | // show next slide or stop bit
368 | if(stop && line) {
369 | // show current slide again
370 | // but stop one stop bit later (or at end of slide)
371 | slide->stop++;
372 | fade = false;
373 | } else {
374 | if(slide->next) {
375 | // show next slide
376 | slide = slide->next;
377 | sc++;
378 | } else {
379 | // do nothing
380 | fade = false;
381 | }
382 | }
383 | } else if (isdigit(c) && c != '0') {
384 | // show slide n
385 | i = get_slide_number(c);
386 | if(i > 0 && i <= deck->slides) {
387 | while(sc != i) {
388 | // search forward
389 | if(sc < i) {
390 | if(slide->next) {
391 | slide = slide->next;
392 | sc++;
393 | }
394 | // search backward
395 | } else {
396 | if(slide->prev) {
397 | slide = slide->prev;
398 | sc--;
399 | }
400 | }
401 | }
402 | } else {
403 | // disable fading if slide n doesn't exist
404 | fade = false;
405 | }
406 | } else if (evaluate_binding(first_slide_binding, c)) {
407 | // show first slide
408 | slide = deck->slide;
409 | sc = 1;
410 | } else if (evaluate_binding(last_slide_binding, c)) {
411 | // show last slide
412 | for(i = sc; i <= deck->slides; i++) {
413 | if(slide->next) {
414 | slide = slide->next;
415 | sc++;
416 | }
417 | }
418 | } else if (evaluate_binding(reload_binding, c)) {
419 | // reload
420 | if(noreload == 0) {
421 | // reload slide N
422 | reload = sc;
423 | slide = NULL;
424 | } else {
425 | // disable fading if reload is not possible
426 | fade = false;
427 | }
428 | } else if (evaluate_binding(quit_binding, c)) {
429 | // quit
430 | // do not fade out on exit
431 | fade = false;
432 | // do not reload
433 | reload = 0;
434 | slide = NULL;
435 | } else {
436 | // disable fading on undefined key press
437 | fade = false;
438 | }
439 |
440 | // fade out
441 | if(fade)
442 | fade_out(content, trans, colors, invert);
443 |
444 | url_purge();
445 | }
446 |
447 | // disable ncurses
448 | endwin();
449 |
450 | // free ncurses memory
451 | delwin(content);
452 | if(reload == 0)
453 | delwin(stdscr);
454 |
455 | // return reload indicator (0 means no reload)
456 | return reload;
457 | }
458 |
459 | void setup_list_strings(void)
460 | {
461 | const char *str;
462 |
463 | /* utf8 can require 6 bytes */
464 | if ((str = getenv("MDP_LIST_OPEN")) != NULL && strlen(str) <= 4*6) {
465 | list_open1 = list_open2 = list_open3 = str;
466 | } else {
467 | if ((str = getenv("MDP_LIST_OPEN1")) != NULL && strlen(str) <= 4*6)
468 | list_open1 = str;
469 | if ((str = getenv("MDP_LIST_OPEN2")) != NULL && strlen(str) <= 4*6)
470 | list_open2 = str;
471 | if ((str = getenv("MDP_LIST_OPEN3")) != NULL && strlen(str) <= 4*6)
472 | list_open3 = str;
473 | }
474 | if ((str = getenv("MDP_LIST_HEAD")) != NULL && strlen(str) <= 4*6) {
475 | list_head1 = list_head2 = list_head3 = str;
476 | } else {
477 | if ((str = getenv("MDP_LIST_HEAD1")) != NULL && strlen(str) <= 4*6)
478 | list_head1 = str;
479 | if ((str = getenv("MDP_LIST_HEAD2")) != NULL && strlen(str) <= 4*6)
480 | list_head2 = str;
481 | if ((str = getenv("MDP_LIST_HEAD3")) != NULL && strlen(str) <= 4*6)
482 | list_head3 = str;
483 | }
484 | }
485 |
486 | void add_line(WINDOW *window, int y, int x, line_t *line, int max_cols, int colors, int nocodebg) {
487 |
488 | int i; // increment
489 | int offset = 0; // text offset
490 |
491 | // move the cursor in position
492 | wmove(window, y, x);
493 |
494 | if(!line->text->value) {
495 |
496 | // fill rest off line with spaces if we are in a code block
497 | if(CHECK_BIT(line->bits, IS_CODE) && colors) {
498 | if(colors && !nocodebg)
499 | wattron(window, COLOR_PAIR(CP_BLACK));
500 | for(i = getcurx(window) - x; i < max_cols; i++)
501 | wprintw(window, "%s", " ");
502 | }
503 |
504 | // do nothing
505 | return;
506 | }
507 |
508 | // IS_UNORDERED_LIST_3
509 | if(CHECK_BIT(line->bits, IS_UNORDERED_LIST_3)) {
510 | offset = next_nonblank(line->text, 0);
511 | char prompt[13 * 6];
512 | int pos = 0, len, cnt;
513 | len = sizeof(prompt) - pos;
514 | cnt = snprintf(&prompt[pos], len, "%s", CHECK_BIT(line->bits, IS_UNORDERED_LIST_1)? list_open1 : " ");
515 | pos += (cnt > len - 1 ? len - 1 : cnt);
516 | len = sizeof(prompt) - pos;
517 | cnt = snprintf(&prompt[pos], len, "%s", CHECK_BIT(line->bits, IS_UNORDERED_LIST_2)? list_open2 : " ");
518 | pos += (cnt > len - 1 ? len - 1 : cnt);
519 | len = sizeof(prompt) - pos;
520 |
521 | if(CHECK_BIT(line->bits, IS_UNORDERED_LIST_EXT)) {
522 | snprintf(&prompt[pos], len, "%s", line->next && CHECK_BIT(line->next->bits, IS_UNORDERED_LIST_3)? list_open3 : " ");
523 | } else {
524 | snprintf(&prompt[pos], len, "%s", list_head3);
525 | offset += 2;
526 | }
527 |
528 | wprintw(window,
529 | "%s", prompt);
530 |
531 | if(!CHECK_BIT(line->bits, IS_CODE))
532 | inline_display(window, &line->text->value[offset], colors, nocodebg);
533 |
534 | // IS_UNORDERED_LIST_2
535 | } else if(CHECK_BIT(line->bits, IS_UNORDERED_LIST_2)) {
536 | offset = next_nonblank(line->text, 0);
537 | char prompt[9 * 6];
538 | int pos = 0, len, cnt;
539 | len = sizeof(prompt) - pos;
540 | cnt = snprintf(&prompt[pos], len, "%s", CHECK_BIT(line->bits, IS_UNORDERED_LIST_1)? list_open1 : " ");
541 | pos += (cnt > len - 1 ? len - 1 : cnt);
542 | len = sizeof(prompt) - pos;
543 |
544 | if(CHECK_BIT(line->bits, IS_UNORDERED_LIST_EXT)) {
545 | snprintf(&prompt[pos], len, "%s", line->next && CHECK_BIT(line->next->bits, IS_UNORDERED_LIST_2)? list_open2 : " ");
546 | } else {
547 | snprintf(&prompt[pos], len, "%s", list_head2);
548 | offset += 2;
549 | }
550 |
551 | wprintw(window,
552 | "%s", prompt);
553 |
554 | if(!CHECK_BIT(line->bits, IS_CODE))
555 | inline_display(window, &line->text->value[offset], colors, nocodebg);
556 |
557 | // IS_UNORDERED_LIST_1
558 | } else if(CHECK_BIT(line->bits, IS_UNORDERED_LIST_1)) {
559 | offset = next_nonblank(line->text, 0);
560 | char prompt[5 * 6];
561 |
562 | if(CHECK_BIT(line->bits, IS_UNORDERED_LIST_EXT)) {
563 | strcpy(&prompt[0], line->next && CHECK_BIT(line->next->bits, IS_UNORDERED_LIST_1)? list_open1 : " ");
564 | } else {
565 | strcpy(&prompt[0], list_head1);
566 | offset += 2;
567 | }
568 |
569 | wprintw(window,
570 | "%s", prompt);
571 |
572 | if(!CHECK_BIT(line->bits, IS_CODE))
573 | inline_display(window, &line->text->value[offset], colors, nocodebg);
574 | }
575 |
576 | // IS_CODE
577 | if(CHECK_BIT(line->bits, IS_CODE)) {
578 |
579 | if (!CHECK_BIT(line->bits, IS_TILDE_CODE) &&
580 | !CHECK_BIT(line->bits, IS_GFM_CODE)) {
581 | // set static offset for code
582 | offset = CODE_INDENT;
583 | }
584 |
585 | // reverse color for code blocks
586 | if(colors && !nocodebg)
587 | wattron(window, COLOR_PAIR(CP_BLACK));
588 |
589 | // print whole lines
590 | waddwstr(window, &line->text->value[offset]);
591 | }
592 |
593 | if(!CHECK_BIT(line->bits, IS_UNORDERED_LIST_1) &&
594 | !CHECK_BIT(line->bits, IS_UNORDERED_LIST_2) &&
595 | !CHECK_BIT(line->bits, IS_UNORDERED_LIST_3) &&
596 | !CHECK_BIT(line->bits, IS_CODE)) {
597 |
598 | // IS_QUOTE
599 | if(CHECK_BIT(line->bits, IS_QUOTE)) {
600 | while(line->text->value[offset] == '>') {
601 | // print a reverse color block
602 | if(colors) {
603 | wattron(window, COLOR_PAIR(CP_BLACK));
604 | wprintw(window, "%s", " ");
605 | wattron(window, COLOR_PAIR(CP_WHITE));
606 | wprintw(window, "%s", " ");
607 | } else {
608 | wprintw(window, "%s", ">");
609 | }
610 |
611 | // find next quote or break
612 | offset++;
613 | if(line->text->value[offset] == ' ')
614 | offset = next_word(line->text, offset);
615 | }
616 |
617 | inline_display(window, &line->text->value[offset], colors, nocodebg);
618 | } else {
619 |
620 | // IS_CENTER
621 | if(CHECK_BIT(line->bits, IS_CENTER)) {
622 | if(line->length < max_cols) {
623 | wmove(window, y, x + ((max_cols - line->length) / 2));
624 | }
625 | }
626 |
627 | // IS_H1 || IS_H2
628 | if(CHECK_BIT(line->bits, IS_H1) || CHECK_BIT(line->bits, IS_H2)) {
629 |
630 | // set headline color
631 | if(colors)
632 | wattron(window, COLOR_PAIR(CP_BLUE));
633 |
634 | // enable underline for H1
635 | if(CHECK_BIT(line->bits, IS_H1))
636 | wattron(window, A_UNDERLINE);
637 |
638 | // skip hashes
639 | while(line->text->value[offset] == '#')
640 | offset = next_word(line->text, offset);
641 |
642 | // print whole lines
643 | waddwstr(window, &line->text->value[offset]);
644 |
645 | wattroff(window, A_UNDERLINE);
646 |
647 | // no line-wide markdown
648 | } else {
649 |
650 | inline_display(window, &line->text->value[offset], colors, nocodebg);
651 | }
652 | }
653 | }
654 |
655 | // fill rest off line with spaces
656 | // we only need this if the color is inverted (e.g. code-blocks),
657 | // to ensure the background fades too
658 | if(CHECK_BIT(line->bits, IS_CODE))
659 | for(i = getcurx(window) - x; i < max_cols; i++)
660 | wprintw(window, "%s", " ");
661 |
662 | // reset to default color
663 | if(colors)
664 | wattron(window, COLOR_PAIR(CP_WHITE));
665 | wattroff(window, A_UNDERLINE);
666 | }
667 |
668 | void inline_display(WINDOW *window, const wchar_t *c, const int colors, int nocodebg) {
669 | const static wchar_t *special = L"\\*_`!["; // list of interpreted chars
670 | const wchar_t *i = c; // iterator
671 | const wchar_t *start_link_name, *start_url;
672 | int length_link_name, url_num;
673 | cstack_t *stack = cstack_init();
674 |
675 |
676 | // for each char in line
677 | for(; *i; i++) {
678 |
679 | // if char is in special char list
680 | if(wcschr(special, *i)) {
681 |
682 | // closing special char (or second backslash)
683 | // only if not followed by :alnum:
684 | if((stack->top)(stack, *i) &&
685 | (!iswalnum(i[1]) || *(i + 1) == L'\0' || *i == L'\\')) {
686 |
687 | switch(*i) {
688 | // print escaped backslash
689 | case L'\\':
690 | waddnwstr(window, i, 1);
691 | break;
692 | // disable highlight
693 | case L'*':
694 | if(colors)
695 | wattron(window, COLOR_PAIR(CP_WHITE));
696 | break;
697 | // disable underline
698 | case L'_':
699 | wattroff(window, A_UNDERLINE);
700 | break;
701 | // disable inline code
702 | case L'`':
703 | if(colors)
704 | wattron(window, COLOR_PAIR(CP_WHITE));
705 | break;
706 | }
707 |
708 | // remove top special char from stack
709 | (stack->pop)(stack);
710 |
711 | // treat special as regular char
712 | } else if((stack->top)(stack, L'\\')) {
713 | waddnwstr(window, i, 1);
714 |
715 | // remove backslash from stack
716 | (stack->pop)(stack);
717 |
718 | // opening special char
719 | } else {
720 |
721 | // emphasis or code span can start after new-line or space only
722 | // and of cause after another emphasis markup
723 | //TODO this condition looks ugly
724 | if(i == c ||
725 | iswspace(*(i - 1)) ||
726 | ((iswspace(*(i - 1)) || *(i - 1) == L'*' || *(i - 1) == L'_') &&
727 | ((i - 1) == c || iswspace(*(i - 2)))) ||
728 | *i == L'\\') {
729 |
730 | // url in pandoc style
731 | if ((*i == L'[' && wcschr(i, L']')) ||
732 | (*i == L'!' && *(i + 1) == L'[' && wcschr(i, L']'))) {
733 |
734 | if (*i == L'!') i++;
735 |
736 | if (wcschr(i, L']')[1] == L'(' && wcschr(i, L')')) {
737 | i++;
738 |
739 | // turn higlighting and underlining on
740 | if (colors)
741 | wattron(window, COLOR_PAIR(CP_BLUE));
742 | wattron(window, A_UNDERLINE);
743 |
744 | start_link_name = i;
745 |
746 | // print the content of the label
747 | // the label is printed as is
748 | do {
749 | waddnwstr(window, i, 1);
750 | i++;
751 | } while (*i != L']');
752 |
753 | length_link_name = i - 1 - start_link_name;
754 |
755 | i++;
756 | i++;
757 |
758 | start_url = i;
759 |
760 | while (*i != L')') i++;
761 |
762 | url_num = url_add(start_link_name, length_link_name, start_url, i - start_url, 0, 0);
763 |
764 | wprintw(window, " [%d]", url_num);
765 |
766 | // turn highlighting and underlining off
767 | wattroff(window, A_UNDERLINE);
768 | wattron(window, COLOR_PAIR(CP_WHITE));
769 |
770 | } else {
771 | wprintw(window, "[");
772 | }
773 |
774 | } else switch(*i) {
775 | // enable highlight
776 | case L'*':
777 | if(colors)
778 | wattron(window, COLOR_PAIR(CP_RED));
779 | break;
780 | // enable underline
781 | case L'_':
782 | wattron(window, A_UNDERLINE);
783 | break;
784 | // enable inline code
785 | case L'`':
786 | if(colors && !nocodebg)
787 | wattron(window, COLOR_PAIR(CP_BLACK));
788 | break;
789 | // do nothing for backslashes
790 | }
791 |
792 | // push special char to stack
793 | (stack->push)(stack, *i);
794 |
795 | } else {
796 | waddnwstr(window, i, 1);
797 | }
798 | }
799 |
800 | } else {
801 | // remove backslash from stack
802 | if((stack->top)(stack, L'\\'))
803 | (stack->pop)(stack);
804 |
805 | // print regular char
806 | waddnwstr(window, i, 1);
807 | }
808 | }
809 |
810 | // pop stack until empty to prevent formated trailing spaces
811 | while(!(stack->empty)(stack)) {
812 | switch((stack->pop)(stack)) {
813 | // disable highlight
814 | case L'*':
815 | if(colors)
816 | wattron(window, COLOR_PAIR(CP_WHITE));
817 | break;
818 | // disable underline
819 | case L'_':
820 | wattroff(window, A_UNDERLINE);
821 | break;
822 | // disable inline code
823 | case L'`':
824 | if(colors)
825 | wattron(window, COLOR_PAIR(CP_WHITE));
826 | break;
827 | // do nothing for backslashes
828 | }
829 | }
830 |
831 | (stack->delete)(stack);
832 | }
833 |
834 | void fade_out(WINDOW *window, int trans, int colors, int invert) {
835 | int i; // increment
836 | if(colors && COLORS == 256) {
837 | for(i = 22; i >= 0; i--) {
838 |
839 | // dim color pairs
840 | if(invert) {
841 | init_pair(CP_WHITE, white_ramp_invert[i], trans);
842 | init_pair(CP_BLUE, blue_ramp_invert[i], trans);
843 | init_pair(CP_RED, red_ramp_invert[i], trans);
844 | init_pair(CP_BLACK, 15, white_ramp_invert[i]);
845 | } else {
846 | init_pair(CP_WHITE, white_ramp[i], trans);
847 | init_pair(CP_BLUE, blue_ramp[i], trans);
848 | init_pair(CP_RED, red_ramp[i], trans);
849 | init_pair(CP_BLACK, 16, white_ramp[i]);
850 | }
851 |
852 | // refresh virtual screen with new color
853 | wnoutrefresh(window);
854 |
855 | // compare virtual screen to physical screen and does the actual updates
856 | doupdate();
857 |
858 | // delay for our eyes to recognize the change
859 | usleep(FADE_DELAY);
860 | }
861 | }
862 | }
863 |
864 | void fade_in(WINDOW *window, int trans, int colors, int invert) {
865 | int i; // increment
866 | if(colors && COLORS == 256) {
867 | for(i = 0; i <= 23; i++) {
868 |
869 | // brighten color pairs
870 | if(invert) {
871 | init_pair(CP_WHITE, white_ramp_invert[i], trans);
872 | init_pair(CP_BLUE, blue_ramp_invert[i], trans);
873 | init_pair(CP_RED, red_ramp_invert[i], trans);
874 | init_pair(CP_BLACK, 15, white_ramp_invert[i]);
875 | } else {
876 | init_pair(CP_WHITE, white_ramp[i], trans);
877 | init_pair(CP_BLUE, blue_ramp[i], trans);
878 | init_pair(CP_RED, red_ramp[i], trans);
879 | init_pair(CP_BLACK, 16, white_ramp[i]);
880 | }
881 |
882 | // refresh virtual screen with new color
883 | wnoutrefresh(window);
884 |
885 | // compare virtual screen to physical screen and does the actual updates
886 | doupdate();
887 |
888 | // delay for our eyes to recognize the change
889 | usleep(FADE_DELAY);
890 | }
891 | }
892 | }
893 |
894 | int int_length (int val) {
895 | int l = 1;
896 | while(val > 9) {
897 | l++;
898 | val /= 10;
899 | }
900 | return l;
901 | }
902 |
903 | int get_slide_number(char init) {
904 | int retval = init - '0';
905 | int c;
906 | // block for tenths of a second when using getch, ERR if no input
907 | halfdelay(GOTO_SLIDE_DELAY);
908 | while((c = getch()) != ERR) {
909 | if (c < '0' || c > '9') {
910 | retval = -1;
911 | break;
912 | }
913 | retval = (retval * 10) + (c - '0');
914 | }
915 | nocbreak(); // cancel half delay mode
916 | cbreak(); // go back to cbreak
917 | return retval;
918 | }
919 |
920 | bool evaluate_binding(const int bindings[], char c) {
921 | int binding;
922 | int ind = 0;
923 | while((binding = bindings[ind]) != 0) {
924 | if (c == binding) return true;
925 | ind++;
926 | }
927 | return false;
928 | }
929 |
930 |
--------------------------------------------------------------------------------