├── .clang_complete
├── .github
└── workflows
│ └── makefile.yml
├── .gitignore
├── CHANGELOG
├── LICENSE
├── Makefile
├── README.md
├── android-build.sh
├── build.config
├── docs
└── FrameIntroduction.odp
├── frame-browser
├── FrameBrowser.ico
├── FrameBrowser.lpi
├── FrameBrowser.lpr
├── FrameBrowser.lps
├── FrameBrowser.res
├── framewrapper.pas
├── mainwin.lfm
└── mainwin.pas
├── frame.bash.autocomplete
├── src
├── ds_array.c
├── ds_array.h
├── ds_str.c
├── ds_str.h
├── frame.c
├── frm.c
└── frm.h
├── swig-input.swig
├── test.ps1
├── test.results.saved
└── test.sh
/.clang_complete:
--------------------------------------------------------------------------------
1 | -I.
2 | -I./include
3 | -std=gnu99
4 | -Dframe_version='"0.0.1"'
5 | -DBUILD_TIMESTAMP='"201909162104"'
6 |
7 |
--------------------------------------------------------------------------------
/.github/workflows/makefile.yml:
--------------------------------------------------------------------------------
1 | name: Makefile CI
2 |
3 | on:
4 | push:
5 | branches: [ "master" ]
6 | pull_request:
7 | branches: [ "master" ]
8 |
9 | jobs:
10 | build:
11 |
12 | runs-on: ubuntu-latest
13 |
14 | steps:
15 | - uses: actions/checkout@v3
16 |
17 | - name: build
18 | run: make debug
19 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Prerequisites
2 | *.d
3 |
4 | # Object files
5 | *.o
6 | *.ko
7 | *.obj
8 | *.elf
9 |
10 | # Linker output
11 | *.ilk
12 | *.map
13 | *.exp
14 |
15 | # Precompiled Headers
16 | *.gch
17 | *.pch
18 |
19 | # Libraries
20 | *.lib
21 | *.a
22 | *.la
23 | *.lo
24 |
25 | # Shared objects (inc. Windows DLLs)
26 | *.dll
27 | *.so
28 | *.so.*
29 | *.dylib
30 |
31 | # Executables
32 | *.exe
33 | *.out
34 | *.app
35 | *.i*86
36 | *.x86_64
37 | *.hex
38 |
39 | # Debug files
40 | *.dSYM/
41 | *.su
42 | *.idb
43 | *.pdb
44 |
45 | # Kernel Module Compile Results
46 | *.mod*
47 | *.cmd
48 | .tmp_versions/
49 | modules.order
50 | Module.symvers
51 | Mkfile.old
52 | dkms.conf
53 |
--------------------------------------------------------------------------------
/CHANGELOG:
--------------------------------------------------------------------------------
1 | v0.1.4 - Tue 18 Jul 2023 09:49:03 SAST
2 | 1, Adds Windows support, including powershell test script.
3 | 2. Lots of improvements to GUI, including proper Windows support and moving to
4 | a monospace font for the notes.
5 |
6 | v0.1.3 - Mon 12 Jun 2023 16:35:42 SAST
7 | 1. Adds some documentation.
8 | 2. Adds '--frame=' option, to execute the command in the context of a different
9 | frame.
10 | 3. Adds a gui program, FrameBrowser, for easy navigation and editing of
11 | frames.
12 | 4. Adds 'rename' command.
13 | 5. Adds lockfile to prevent different applications using the frm_*() functions
14 | at the same time.
15 |
16 | v0.1.2 - Fri 05 May 2023 22:02:07 SAST
17 |
18 | Features
19 | 1. Adds --force for pop command, to let user pop a non-empty frame.
20 | 2. With no command, a short message is printed and the command defaults to status.
21 | 3. Adds tree command and tree functions, to print an indented tree of frames] to
22 | strip the dates'.
23 |
24 | v0.1.1 - Wed 19 Apr 2023 18:00:27 SAST
25 |
26 | Features
27 | 1. Adds edit command.
28 | 2. Listing defaults to current node descendents only.
29 | 3. Adds --quiet mode for calling scripts.
30 | 4. Adds 'back' command for navigating through history.
31 | 5. Updates history to take count argument.
32 | 6. Prints out payload after every node change.
33 | 7. Default message in EDITOR contains the pathname being created.
34 | 8. Adds optional target for "list" command.
35 | 9. Switching works smartly, by "remembering" the most recent subnode in the node
36 | being switched to.
37 | 10. New "top" command to quickly go to the root of the tree.
38 | 11. Frame pop command does not allow popping if subframe exists.
39 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | # ######################################################################
2 | # We include the user-specified variables first
3 | include build.config
4 | ifndef PROJNAME
5 | $(error $$PROJNAME not defined. Is the 'build.config' file missing?)
6 | endif
7 |
8 | export DESCRIPTION
9 |
10 | VERSION?=0.0.0
11 | MAINTAINER?="No maintainer"
12 | HOMEPAGE?="No homepage"
13 | DESCRIPTION?="No description"
14 | SECTION?="custom"
15 | DEPENDS=
16 |
17 | export DESCRIPTION
18 |
19 | # ######################################################################
20 | # Set some colours for $(ECHO) to use
21 | NONE:=\e[0m
22 | INV:=\e[7m
23 | RED:=\e[31m
24 | GREEN:=\e[32m
25 | BLUE:=\e[34m
26 | CYAN:=\e[36m
27 | YELLOW:=\e[33m
28 |
29 |
30 | # ######################################################################
31 | # We record the start time, for determining how long the build took
32 | START_TIME:=$(shell date +"%s")
33 |
34 |
35 | # ######################################################################
36 | # Some housekeeping to determine if we are running on a POSIX
37 | # platform or on Windows
38 |
39 | MAKEPROGRAM_EXE=$(findstring exe,$(MAKE))
40 | MAKEPROGRAM_MINGW=$(findstring mingw,$(MAKE))
41 | GITSHELL=$(findstring Git,$(SHELL))
42 | GITSHELL+=$(findstring git,$(SHELL))
43 | MINGW_DETECTED=$(findstring mingw,$(GCC))
44 | BUILD_HOST=$(findstring Linux,$(shell uname -s))
45 |
46 |
47 | # TODO: Remember that freebsd might use not gmake/gnu-make; must add in
48 | # some diagnostics so that user gets a message to install gnu make.
49 |
50 | ifneq ($(MINGW_DETECTED),)
51 | ifeq ($(strip $(BUILD_HOST)),Linux)
52 | HOME=$(subst \,/,$(HOMEDRIVE)$(HOMEPATH))
53 | PLATFORM:=Windows
54 | EXE_EXT:=.exe
55 | LIB_EXT:=.dll
56 | PLATFORM_LDFLAGS:=-L$(HOME)/lib -lmingw32 -lmsvcrt -lgcc -liphlpapi -lws2_32
57 | PLATFORM_CFLAGS:= -D__USE_MINGW_ANSI_STDIO -DWINVER=0x0600 -D_WIN32_WINNT=0x0600
58 | ECHO:=echo
59 | endif
60 | endif
61 |
62 |
63 | ifneq ($(MAKEPROGRAM_EXE),)
64 | ifeq ($(strip $(GITSHELL)),)
65 | $(error On windows this must be executed from the Git bash shell)
66 | endif
67 | HOME=$(subst \,/,$(HOMEDRIVE)$(HOMEPATH))
68 | PLATFORM:=Windows
69 | EXE_EXT:=.exe
70 | LIB_EXT:=.dll
71 | PLATFORM_LDFLAGS:=-L$(HOME)/lib -lws2_32 -lmsvcrt -lgcc
72 | PLATFORM_CFLAGS:= -D__USE_MINGW_ANSI_STDIO
73 | ECHO:=echo -e
74 | endif
75 |
76 | ifneq ($(MAKEPROGRAM_MINGW),)
77 | ifeq ($(strip $(GITSHELL)),)
78 | $(error On windows this must be executed from the Git bash shell)
79 | endif
80 | HOME=$(subst \,/,$(HOMEDRIVE)$(HOMEPATH))
81 | PLATFORM:=Windows
82 | EXE_EXT:=.exe
83 | LIB_EXT:=.dll
84 | PLATFORM_LDFLAGS:=-L$(HOME)/lib -lmingw32 -lws2_32 -lmsvcrt -lgcc
85 | PLATFORM_CFLAGS:= -D__USE_MINGW_ANSI_STDIO
86 | ECHO:=echo -e
87 | endif
88 |
89 | # If neither of the above are true then we assume a working POSIX
90 | # platform
91 | ifeq ($(PLATFORM),)
92 | PLATFORM:=POSIX
93 | EXE_EXT:=.elf
94 | LIB_EXT:=.so
95 | PLATFORM_LDFLAGS:= -ldl
96 | ECHO:=echo
97 | REAL_SHOW:=real-show
98 | endif
99 |
100 |
101 |
102 | # ######################################################################
103 | # Set the output directories, output filenames
104 |
105 | OUTDIR=debug
106 | ifeq ($(INSTALL_PREFIX),)
107 | $(warning *********************************************************************)
108 | $(warning * $$INSTALL_PREFIX not defined! *)
109 | $(warning * If you get include path or link errors, set the $$INSTALL_PREFIX *)
110 | $(warning * and try again. *)
111 | $(warning *********************************************************************)
112 | $(warning Using '$(CURDIR)/..' as $$INSTALL_PREFIX)
113 | endif
114 |
115 | INSTALL_PREFIX?=$(CURDIR)/..
116 |
117 | ifneq (,$(findstring debug,$(MAKECMDGOALS)))
118 | OUTDIR=debug
119 | endif
120 |
121 | ifneq (,$(findstring release,$(MAKECMDGOALS)))
122 | OUTDIR=release
123 | endif
124 |
125 | TARGET:=$(shell $(GCC) -dumpmachine)
126 | T_ARCH=$(shell $(GCC) -dumpmachine | cut -f 1 -d - )
127 | OUTLIB:=$(OUTDIR)/lib/$(TARGET)
128 | OUTBIN:=$(OUTDIR)/bin/$(TARGET)
129 | OUTOBS:=$(OUTDIR)/obs/$(TARGET)
130 | OUTDIRS:=$(OUTLIB) $(OUTBIN) $(OUTOBS) include
131 |
132 | # ######################################################################
133 | # The architecture has to be manually mapped to a compatible one for
134 | # debian. As I test more arches, I'll put them in here. By default we
135 | # will use whatever gcc gave us.
136 | PACKAGE_ARCH=$(T_ARCH)
137 | ifeq ($(T_ARCH),x86_64)
138 | PACKAGE_ARCH=amd64
139 | endif
140 |
141 | # ######################################################################
142 | # Declare the final outputs
143 | BINPROGS:=\
144 | $(foreach fname,$(MAIN_PROGRAM_CSOURCEFILES),$(OUTBIN)/$(fname)$(EXE_EXT))\
145 | $(foreach fname,$(MAIN_PROGRAM_CPPSOURCEFILES),$(OUTBIN)/$(fname)$(EXE_EXT))
146 |
147 | DYNLIB:=$(OUTLIB)/lib$(PROJNAME)-$(VERSION)$(LIB_EXT)
148 | STCLIB:=$(OUTLIB)/lib$(PROJNAME)-$(VERSION).a
149 | DYNLNK_TARGET:=lib$(PROJNAME)-$(VERSION)$(LIB_EXT)
150 | STCLNK_TARGET:=lib$(PROJNAME)-$(VERSION).a
151 | DYNLNK_NAME:=$(OUTLIB)/lib$(PROJNAME)$(LIB_EXT)
152 | STCLNK_NAME:=$(OUTLIB)/lib$(PROJNAME).a
153 |
154 | ifneq ($(SWIG_WRAPPERS),)
155 | SWIG_OBJECTS:=swig_$(PROJNAME)
156 | endif
157 |
158 | # ######################################################################
159 | # Declare the intermediate outputs
160 | BIN_COBS:=\
161 | $(foreach fname,$(MAIN_PROGRAM_CSOURCEFILES),$(OUTOBS)/$(fname).o)
162 |
163 | BIN_CPPOBS:=\
164 | $(foreach fname,$(MAIN_PROGRAM_CPPSOURCEFILES),$(OUTOBS)/$(fname).o)
165 |
166 | BINOBS:=$(BIN_COBS) $(BIN_CPPOBS)
167 |
168 | COBS:=\
169 | $(foreach fname,$(LIBRARY_OBJECT_CSOURCEFILES) $(SWIG_OBJECTS),$(OUTOBS)/$(fname).o)
170 |
171 | CPPOBS:=\
172 | $(foreach fname,$(LIBRARY_OBJECT_CPPSOURCEFILES),$(OUTOBS)/$(fname).o)
173 |
174 | OBS=$(COBS) $(CPPOBS)
175 | ALL_OBS=$(OBS) $(BINOBS)
176 | DEPS:=\
177 | $(subst $(OUTOBS),src,$(subst .o,.d,$(ALL_OBS)))
178 |
179 | # ######################################################################
180 | # Find all the source files so that we can do dependencies properly
181 | SOURCES:=\
182 | $(shell find . | grep -E "\.(c|cpp)\$$")
183 |
184 | # ######################################################################
185 | # Declare the build programs
186 | ifndef GCC
187 | GCC=gcc
188 | endif
189 | ifndef GXX
190 | GXX=g++
191 | endif
192 | ifndef LD_PROG
193 | LD_PROG=gcc
194 | endif
195 | ifndef LD_LIB
196 | LD_LIB=gcc
197 | endif
198 |
199 | # ######################################################################
200 | # On android targets we must remove the -lpthread from the link flags
201 | #
202 | ifneq (,$(findstring android,$(LD_PROG)))
203 | REAL_EXTRA_LIB_LDFLAGS=$(subst -lpthread,,$(EXTRA_LIB_LDFLAGS))
204 | else
205 | REAL_EXTRA_LIB_LDFLAGS=$(EXTRA_LIB_LDFLAGS)
206 | endif
207 |
208 | ifneq (,$(findstring android,$(LD_LIB)))
209 | REAL_EXTRA_PROG_LDFLAGS=$(subst -lpthread,,$(EXTRA_PROG_LDFLAGS))
210 | else
211 | REAL_EXTRA_PROG_LDFLAGS=$(EXTRA_PROG_LDFLAGS)
212 | endif
213 |
214 | # ######################################################################
215 | # Declare all the flags we need to compile and link
216 | BUILD_TIMESTAMP:=$(shell date +"%Y%m%d%H%M%S")
217 | CC:=$(GCC)
218 | CXX:=$(GXX)
219 | PROG_LD=$(GCC_LD_PROG)
220 | LIB_LD=$(GCC_LD_LIB)
221 |
222 | INCLUDE_DIRS:= -I.\
223 | -I./src\
224 | -I$(INSTALL_PREFIX)/include/$(TARGET)\
225 | $(foreach ipath,$(INCLUDE_PATHS),-I$(ipath))
226 |
227 | LIBDIRS:=\
228 | $(foreach lpath,$(LIBRARY_PATHS),-L$(lpath))
229 |
230 | LIBFILES:=\
231 | $(foreach lfile,$(LIBRARY_FILES),-l$(lfile))
232 |
233 | COMMONFLAGS:=\
234 | $(EXTRA_COMPILER_FLAGS)\
235 | -W -Wall -c -fPIC \
236 | -DPLATFORM=$(PLATFORM) -DPLATFORM_$(PLATFORM) \
237 | -D$(PROJNAME)_version='"$(VERSION)"'\
238 | -DBUILD_TIMESTAMP='"$(BUILD_TIMESTAMP)"'\
239 | $(PLATFORM_CFLAGS)\
240 | $(INCLUDE_DIRS)
241 |
242 | CFLAGS:=$(COMMONFLAGS) $(EXTRA_CFLAGS)
243 | CXXFLAGS:=$(COMMONFLAGS) $(EXTRA_CXXFLAGS)
244 | LD:=$(GCC)
245 | LDFLAGS:= $(LIBDIRS) $(LIBFILES) -lm $(PLATFORM_LDFLAGS)
246 | AR:=ar
247 | ARFLAGS:= rcs
248 |
249 |
250 | .PHONY: help real-help show real-show debug release clean-all deps
251 |
252 | # ######################################################################
253 | # All the conditional targets
254 |
255 | help: real-help
256 |
257 | debug: CFLAGS+= -ggdb -DDEBUG
258 | debug: CXXFLAGS+= -ggdb -DDEBUG
259 | debug: $(SWIG_WRAPPERS)
260 | debug: all
261 |
262 | release: CFLAGS+= -O3
263 | release: CXXFLAGS+= -O3
264 | release: LDFLAGS+=
265 |
266 | debug: $(SWIG_WRAPPERS)
267 | release: all
268 |
269 | # ######################################################################
270 | # Finally, build the system
271 |
272 | real-help:
273 | @$(ECHO) "Possible targets:"
274 | @$(ECHO) "help: This message."
275 | @$(ECHO) "show: Display all the variable values that will be"
276 | @$(ECHO) " used during execution. Also 'show debug' or"
277 | @$(ECHO) " 'show release' works."
278 | @$(ECHO) "deps: Make the dependencies only."
279 | @$(ECHO) "debug: Build debug binaries."
280 | @$(ECHO) "release: Build release binaries."
281 | @$(ECHO) "clean-debug: Clean a debug build (release is ignored)."
282 | @$(ECHO) "clean-release: Clean a release build (debug is ignored)."
283 | @$(ECHO) "clean-all: Clean everything."
284 | @$(ECHO) ""
285 | @$(ECHO) "Variables that can be set in build.conf or the environment."
286 | @$(ECHO) "Defaults, if any, are displayed in parenthesis:"
287 | @$(ECHO) "GCC: The C compiler executable."
288 | @$(ECHO) "GXX: The C++ compiler executable."
289 | @$(ECHO) "LD_PROG: The program linker executable (default $$GCC)."
290 | @$(ECHO) "LD_LIB: The library linker executable (default $$GCC)."
291 | @$(ECHO) "INSTALL_PREFIX: The path to where the lib, include and bin dirs"
292 | @$(ECHO) " would be created (default ../)."
293 |
294 |
295 | real-all: $(OUTDIRS) $(DYNLIB) $(STCLIB) $(BINPROGS)
296 | @unlink ./recent || rm -rf ./recent
297 | @ln -s $(OUTDIR) ./recent
298 |
299 | all: $(SWIG_WRAPPERS) real-all
300 | @$(ECHO) "[$(CYAN)Soft linking$(NONE)] [$(STCLNK_TARGET)]"
301 | @ln -f -s $(STCLNK_TARGET) $(STCLNK_NAME)
302 | @$(ECHO) "[$(CYAN)Soft linking$(NONE)] [$(DYNLNK_TARGET)]"
303 | @ln -f -s $(DYNLNK_TARGET) $(DYNLNK_NAME)
304 | @$(ECHO) "[$(CYAN)Copying$(NONE) ] [ -> ./include/]"
305 | @cp -R $(HEADERS) include
306 | @mkdir -p $(INSTALL_PREFIX)/bin/$(TARGET)
307 | @mkdir -p $(INSTALL_PREFIX)/lib/$(TARGET)
308 | @mkdir -p $(INSTALL_PREFIX)/obs/$(TARGET)
309 | @mkdir -p $(INSTALL_PREFIX)/include/$(TARGET)
310 | @$(ECHO) "[$(CYAN)Copying$(NONE) ] [ $(OUTBIN) -> $(INSTALL_PREFIX)/bin/$(TARGET)]"
311 | @cp $(OUTBIN)/* $(INSTALL_PREFIX)/bin/$(TARGET)
312 | @$(ECHO) "[$(CYAN)Copying$(NONE) ] [ $(OUTLIB)-> $(INSTALL_PREFIX)/lib/$(TARGET)]"
313 | @cp $(OUTLIB)/* $(INSTALL_PREFIX)/lib/$(TARGET)
314 | @$(ECHO) "[$(CYAN)Copying$(NONE) ] [ $(OUTOBS) -> $(INSTALL_PREFIX)/obs/$(TARGET)]"
315 | @cp $(OUTOBS)/* $(INSTALL_PREFIX)/obs/$(TARGET)
316 | @$(ECHO) "[$(CYAN)Copying$(NONE) ] [ include -> $(INSTALL_PREFIX)/include/$(TARGET)/]"
317 | @cp -R include/* $(INSTALL_PREFIX)/include/$(TARGET)
318 | @$(ECHO) "$(INV)$(YELLOW)Build completed: `date`$(NONE)"
319 | @$(ECHO) "$(YELLOW)Total build time: $$((`date +"%s"` - $(START_TIME)))s"\
320 | "$(NONE)"
321 |
322 |
323 | real-show:
324 | @$(ECHO) "$(GREEN)PROJNAME$(NONE) $(PROJNAME)"
325 | @$(ECHO) "$(GREEN)VERSION$(NONE) $(VERSION)"
326 | @$(ECHO) "$(GREEN)MAINTAINER$(NONE) $(MAINTAINER)"
327 | @$(ECHO) "$(GREEN)HOMEPAGE$(NONE) $(HOMEPAGE)"
328 | @$(ECHO) "$(GREEN)INSTALL_PREFIX$(NONE) $(INSTALL_PREFIX)"
329 | @$(ECHO) "$(GREEN)DESCRIPTION$(NONE) $$DESCRIPTION"
330 | @$(ECHO) "$(GREEN)TARGET-ARCH$(NONE) $(T_ARCH)"
331 | @$(ECHO) "$(GREEN)HOME$(NONE) $(HOME)"
332 | @$(ECHO) "$(GREEN)SHELL$(NONE) $(SHELL)"
333 | @$(ECHO) "$(GREEN)EXE_EXT$(NONE) $(EXE_EXT)"
334 | @$(ECHO) "$(GREEN)LIB_EXT$(NONE) $(LIB_EXT)"
335 | @$(ECHO) "$(GREEN)DYNLIB$(NONE) $(DYNLIB)"
336 | @$(ECHO) "$(GREEN)STCLIB$(NONE) $(STCLIB)"
337 | @$(ECHO) "$(GREEN)CC$(NONE) $(CC)"
338 | @$(ECHO) "$(GREEN)CXX$(NONE) $(CXX)"
339 | @$(ECHO) "$(GREEN)CFLAGS$(NONE) $(CFLAGS)"
340 | @$(ECHO) "$(GREEN)CXXFLAGS$(NONE) $(CXXFLAGS)"
341 | @$(ECHO) "$(GREEN)LD_LIB$(NONE) $(LD_LIB)"
342 | @$(ECHO) "$(GREEN)LD_PROG$(NONE) $(LD_PROG)"
343 | @$(ECHO) "$(GREEN)LDFLAGS$(NONE) $(LDFLAGS)"
344 | @$(ECHO) "$(GREEN)LIBDIRS$(NONE) $(LIBDIRS)"
345 | @$(ECHO) "$(GREEN)AR$(NONE) $(AR)"
346 | @$(ECHO) "$(GREEN)ARFLAGS$(NONE) $(ARFLAGS)"
347 | @$(ECHO) "$(GREEN)"
348 | @$(ECHO) "$(GREEN)PLATFORM$(NONE) $(PLATFORM)"
349 | @$(ECHO) "$(GREEN)TARGET$(NONE) $(TARGET)"
350 | @$(ECHO) "$(GREEN)OUTBIN$(NONE) $(OUTBIN)"
351 | @$(ECHO) "$(GREEN)OUTLIB$(NONE) $(OUTLIB)"
352 | @$(ECHO) "$(GREEN)OUTOBS$(NONE) $(OUTOBS)"
353 | @$(ECHO) "$(GREEN)SWIG_OBJECTS$(NONE) $(SWIG_OBJECTS)"
354 | @$(ECHO) "$(GREEN)OUTDIRS$(NONE) "
355 | @for X in $(OUTDIRS); do $(ECHO) " $$X"; done
356 | @$(ECHO) "$(GREEN)DEPS$(NONE) "
357 | @for X in $(DEPS); do $(ECHO) " $$X"; done
358 | @$(ECHO) "$(GREEN)HEADERS$(NONE) "
359 | @for X in $(HEADERS); do $(ECHO) " $$X"; done
360 | @$(ECHO) "$(GREEN)COBS$(NONE) "
361 | @for X in $(COBS); do $(ECHO) " $$X"; done
362 | @$(ECHO) "$(GREEN)CPPOBS$(NONE) "
363 | @for X in $(CPPOBS); do $(ECHO) " $$X"; done
364 | @$(ECHO) "$(GREEN)OBS$(NONE) "
365 | @for X in $(OBS); do $(ECHO) " $$X"; done
366 | @$(ECHO) "$(GREEN)BIN_COBS$(NONE) "
367 | @for X in $(BIN_COBS); do $(ECHO) " $$X"; done
368 | @$(ECHO) "$(GREEN)BIN_CPPOBS$(NONE) "
369 | @for X in $(BIN_CPPOBS); do $(ECHO) " $$X"; done
370 | @$(ECHO) "$(GREEN)BINOBS$(NONE) "
371 | @for X in $(BINOBS); do $(ECHO) " $$X"; done
372 | @$(ECHO) "$(GREEN)BINPROGS$(NONE) "
373 | @for X in $(BINPROGS); do $(ECHO) " $$X"; done
374 | @$(ECHO) "$(GREEN)SOURCES$(NONE) "
375 | @for X in $(SOURCES); do $(ECHO) " $$X"; done
376 | @$(ECHO) "$(GREEN)PWD$(NONE) $(PWD)"
377 |
378 | show: real-show
379 | @$(ECHO) "Only target 'show' selected, ending now."
380 | @false
381 |
382 | $(DEPS): $(HEADERS)
383 |
384 | deps: $(DEPS)
385 |
386 | debian-package:
387 | @mkdir -p $(OUTDIR)/$(PROJNAME)/usr/lib
388 | @mkdir -p $(OUTDIR)/$(PROJNAME)/usr/bin
389 | @mkdir -p $(OUTDIR)/$(PROJNAME)/DEBIAN
390 | @cp -v $(OUTLIB)/*.so $(OUTDIR)/$(PROJNAME)/usr/lib
391 | @cp -v $(OUTLIB)/*.a $(OUTDIR)/$(PROJNAME)/usr/lib
392 | @cp -v $(OUTBIN)/* $(OUTDIR)/$(PROJNAME)/usr/bin
393 | @echo 'Package: $(PROJNAME)' > $(OUTDIR)/$(PROJNAME)/DEBIAN/control
394 | @echo 'Version: $(VERSION)' >> $(OUTDIR)/$(PROJNAME)/DEBIAN/control
395 | @echo 'Section: $(SECTION)' >> $(OUTDIR)/$(PROJNAME)/DEBIAN/control
396 | @echo 'Homepage: $(HOMEPAGE)' >> $(OUTDIR)/$(PROJNAME)/DEBIAN/control
397 | @echo 'Priority: optional' >> $(OUTDIR)/$(PROJNAME)/DEBIAN/control
398 | @echo 'Architecture: $(PACKAGE_ARCH)' >> $(OUTDIR)/$(PROJNAME)/DEBIAN/control
399 | @echo 'Depends: $(DEPENDS)' >> $(OUTDIR)/$(PROJNAME)/DEBIAN/control
400 | @echo 'Essential: no' >> $(OUTDIR)/$(PROJNAME)/DEBIAN/control
401 | @echo "Installed-size: `du -bs $(OUTDIR) | cut -f 1 `" \
402 | >> $(OUTDIR)/$(PROJNAME)/DEBIAN/control
403 | @echo 'Maintainer: $(MAINTAINER)' >> $(OUTDIR)/$(PROJNAME)/DEBIAN/control
404 | @echo "Description: $$DESCRIPTION" | sed "s/^$$/ ./g; s/^/ /g; s/^ D/D/g" \
405 | >> $(OUTDIR)/$(PROJNAME)/DEBIAN/control
406 | @dpkg-deb --build $(OUTDIR)/$(PROJNAME) $(OUTDIR)/$(PROJNAME)-$(PACKAGE_ARCH)-$(VERSION).deb
407 |
408 | debug-package: debug debian-package
409 | release-package: release debian-package
410 |
411 | swig_prep: swig-input.swig
412 | @$(ECHO) "[$(CYAN)SWIG$(NONE) ] [ <- ./src/"'*.h]'
413 | @mkdir -p wrappers
414 |
415 | $(SWIG_WRAPPERS): swig_prep
416 | @mkdir -p wrappers/`echo $@ | cut -f 2 -d -`/swig_$(PROJNAME)
417 | @swig -package swig_$(PROJNAME) \
418 | -o src/swig_$(PROJNAME).c \
419 | -`echo $@ | cut -f 2 -d -`\
420 | -outdir wrappers/`echo $@ | cut -f 2 -d -`/swig_$(PROJNAME)\
421 | swig-input.swig
422 |
423 | src/swig_$(PROJNAME).c: $(SWIG_WRAPPERS)
424 |
425 | src/%.d: src/%.c
426 | @$(ECHO) "[$(RED)Dependency$(NONE) ] [$@]"
427 | @$(CC) $(CFLAGS) -MM -MF $@ -MT $(OUTOBS)/$*.o $< ||\
428 | ($(ECHO) "$(INV)$(RED)[Depend failure ] [$@]$(NONE)" ; exit 127)
429 |
430 | src/%.d: src/%.cpp
431 | @$(ECHO) "[$(RED)Dependency$(NONE) ] [$@]"
432 | @$(CXX) $(CXXFLAGS) -MM -MF $@ $< ||\
433 | ($(ECHO) "$(INV)$(RED)[Depend failure ] [$@]$(NONE)" ; exit 127)
434 |
435 | $(BIN_COBS) $(COBS): $(OUTOBS)/%.o: src/%.c src/%.d
436 | @$(ECHO) "[$(BLUE)Building$(NONE) ] [$@]"
437 | @$(CC) $(CFLAGS) -o $@ $< ||\
438 | ($(ECHO) "$(INV)$(RED)[Compile failure] [$@]$(NONE)" ; exit 127)
439 |
440 | $(BIN_CPPOBS) $(CPPOBS): $(OUTOBS)/%.o: src/%.cpp src/%.d
441 | @$(ECHO) "[$(BLUE)Building$(NONE) ] [$@]"
442 | @$(CXX) $(CXXFLAGS) -o $@ $< ||\
443 | ($(ECHO) "$(INV)$(RED)[Compile failure] [$@]$(NONE)" ; exit 127)
444 |
445 | $(OUTBIN)/%.exe: $(OUTOBS)/%.o $(OBS)
446 | @$(ECHO) "[$(GREEN)Linking$(NONE) ] [$@]"
447 | @$(LD_PROG) $< $(OBS) -o $@ $(LDFLAGS) $(REAL_EXTRA_PROG_LDFLAGS) ||\
448 | ($(ECHO) "$(INV)$(RED)[Link failure] [$@]$(NONE)" ; exit 127)
449 |
450 | $(OUTBIN)/%.elf: $(OUTOBS)/%.o $(OBS)
451 | @$(ECHO) "[$(GREEN)Linking$(NONE) ] [$@]"
452 | @$(LD_PROG) $< $(OBS) -o $@ $(LDFLAGS) $(REAL_EXTRA_PROG_LDFLAGS) ||\
453 | ($(ECHO) "$(INV)$(RED)[Link failure] [$@]$(NONE)" ; exit 127)
454 |
455 | $(DYNLIB): $(OBS)
456 | @$(ECHO) "[$(GREEN)Linking$(NONE) ] [$@]"
457 | @$(LD_LIB) -shared $^ -o $@ $(LDFLAGS) $(REAL_EXTRA_LIB_LDFLAGS) ||\
458 | ($(ECHO) "$(INV)$(RED)[Link failure] [$@]$(NONE)" ; exit 127)
459 |
460 | $(STCLIB): $(OBS)
461 | @$(ECHO) "[$(GREEN)Linking$(NONE) ] [$@]"
462 | @$(AR) $(ARFLAGS) $@ $^ ||\
463 | ($(ECHO) "$(INV)$(RED)[Link failure] [$@]$(NONE)" ; exit 127)
464 |
465 | $(OUTDIRS):
466 | @$(ECHO) "[$(CYAN)Creating dir$(NONE)] [$@]"
467 | @mkdir -p $@ ||\
468 | ($(ECHO) "$(INV)$(RED)[mkdir failure] [$@]$(NONE)" ; exit 127)
469 |
470 | clean-release:
471 | @rm -rfv release wrappers
472 |
473 | clean-debug:
474 | @rm -rfv debug wrappers
475 |
476 | clean-all: clean-release clean-debug
477 | @unlink ./recent || rm -rf ./recent
478 | @rm -rfv include
479 | @rm -rfv `find . | grep "\.d$$"`
480 |
481 | clean:
482 | @$(ECHO) Choose either clean-release or clean-debug
483 |
484 | include $(DEPS)
485 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # FRAME
2 |
3 | Estimated time to read this document: 10m
4 |
5 | This is a small utility that I can use from the command-line to keep
6 | track of where in the bigger picture my current efforts fit.
7 |
8 | ## Problem
9 |
10 | Too often I find myself completing some small and intricate part of my
11 | hobby/personal project, and realise that I don't remember where I stopped
12 | last in the project, which I last looked at as an overview two weeks ago.
13 |
14 | I deep-dive into something tiny and complex that takes all of my
15 | attention, and once that is done and integrated, I'm not too sure what
16 | I had planned next.
17 |
18 | ## Solution
19 |
20 | I store context as a tree of nodes. I call each node in this tree a `frame`
21 | (as in *frame of context*, *frame of mind*, or *framing the problem*, etc). At any
22 | given time only a single frame can be active. To see all the notes I made
23 | while working during that frame of context I use `frame status`.
24 |
25 | When I start a task I first do `push frame 'title of task name here'` and
26 | enter a short message, and that new frame is the current frame. When I am done I
27 | do `frame pop` and the current frame switches to the parent of the popped
28 | frame. The popped frame is discarded.
29 |
30 | All frames descend from an initial frame called `root`[^1].
31 |
32 |
33 | ## Setup for terminal usage
34 | The first thing to do is to integrate the frame name into the shell's
35 | `PS1` variable. The command `frame current` prints the name of the current
36 | frame and the date it was changed, with the two fields separated by a colon.
37 |
38 | ```sh
39 | $ frame current
40 | root/projects/frame: Thu May 11 22:26:29 2023
41 | ```
42 |
43 | I have the following function which is called within my `PS1` variable:
44 |
45 | ```sh
46 | frame_ps1() {
47 | frame current --quiet | cut -f 1 -d :
48 | }
49 | ```
50 |
51 | Calling `frame_ps1` within the `PS1` variable prints out the current frame
52 | after each command, in every terminal I am logged into[^2].
53 |
54 | ```sh
55 | [root/projects/frame]$ echo "Hello World"
56 | Hello World
57 | [root/projects/frame]$
58 | ```
59 |
60 | ## Example Usage
61 | Continuing with the frame `root/projects/frame`, in this case I
62 | want to create a GUI browser for this project:
63 |
64 | ```sh
65 | [root/projects/frame]$ frame push 'create GUI' --message="
66 | Create a GUI for frame browsing
67 | "
68 | Frame 0.1.2, (© 2023 Lelanthran Manickum)
69 | Created new frame [root/projects/frame/create GUI]
70 | [root/projects/frame/create GUI]$
71 | ```
72 |
73 | > Had I neglected to provide a `--message` parameter then $EDITOR is opened to
74 | > allow me to enter a message, similar to how `git commit` works when no message
75 | > is specified. Each frame can store a single note of unlimited size; the
76 | > `--message` argument provides the notes when creating a new frame.
77 |
78 | The current frame's name is `create GUI`. It has a parent, `projects`, which
79 | itself has the parent `root`.
80 |
81 | ```mermaid
82 | graph TD
83 | A[root] --> B[projects];
84 | B --> C[frame];
85 | C --> D[create GUI];
86 | ```
87 | Some useful commands at this point:
88 |
89 | 1. `frame status` returns the notes (message) for this frame.
90 | 2. `frame edit` opens the editor to allow editing the notes for this frame.
91 |
92 | I have some idea of what needs to be done, namely, startup a new Lazarus
93 | project, create the GUI elements, include the `libframe.so` library, ensure
94 | that the build compiles, etc.
95 |
96 | ```sh
97 | [root/projects/frame/create GUI]$ frame append --message="
98 | 1. Create new lazarus project
99 | 2. Add `libframe.so` to the lazarus project
100 | 3. Create GUI elements
101 | "
102 | Frame 0.1.2, (© 2023 Lelanthran Manickum)
103 | root/frame/create GUI: Sat May 13 08:20:32 2023
104 | ```
105 |
106 | It takes a few minutes to create the Lazarus project, add the `libframe.so`
107 | file and ensure that the linker is finding and accepting the library.
108 |
109 | My next task is to create the GUI elements. This is much more involved, and
110 | so I create a new frame for it:
111 |
112 | ```sh
113 | [root/projects/frame]$ frame push 'GUI Elements' --message="
114 | Needs to display:
115 | 1. The history, with a filter
116 | 2. A treeview of all frames from root, with the current frame selected and
117 | expanded.
118 | 3. An editor box that displays the notes for the current frame, and allows the
119 | user to edit those notes.
120 | "
121 | Frame 0.1.2, (© 2023 Lelanthran Manickum)
122 | Created new frame [root/frame/create GUI/GUI Elements]
123 | [root/projects/frame/create GUI/GUI Elements]$
124 | ```
125 |
126 | I do the form design (ridiculously easy in Lazarus, takes about 10m). I
127 | attempt to populate the `history` GUI element, and realise I forgot to declare
128 | the `C` functions in the library file. My Lazarus code cannot call undeclared
129 | functions even if the library is linked correctly.
130 |
131 | ```sh
132 | [root/projects/frame/create GUI/GUI Elements]$ frame push 'decls needed" --message='
133 | Declare all the `C` functions in `libframe.so`.
134 | '
135 | Frame 0.1.2, (© 2023 Lelanthran Manickum)
136 | Created new frame [root/frame/create GUI/GUI Elements/decls needed]
137 | ```
138 |
139 | To make things easier, I paste the `C` header file into ChatGPT and ask for
140 | the Lazarus definitions. It provides the definitions, helpfully hallucinating
141 | a few types. and I copy the results into a Lazarus sourcefile and fix all the
142 | compilation errors.
143 |
144 | Finally, the needed declarations are in. To return to whatever I was
145 | previously doing (what was it again):
146 | ```sh
147 | [root/frame/create GUI/GUI Elements/decls needed] $ frame pop
148 | Frame 0.1.2, (© 2023 Lelanthran Manickum)
149 | [src/frm.c:55] Failed to switch to new dir [root/frame/create GUI/GUI Elements/decls needed]
150 | [src/frm.c:1440] Warning: using relative path [root/frame/create GUI/GUI Elements/decls needed] failed, trying absolute path
151 | Current frame
152 | root/frame/create GUI/GUI Elements
153 |
154 | Notes (Sat May 13 08:26:59 2023)
155 | Needs to display:
156 | 1. The history, with a filter
157 | 2. A treeview of all frames from root, with the current frame selected and
158 | expanded.
159 | 3. An editor box that displays the notes for the current frame, and allows the
160 | user to edit those notes.
161 | ```
162 |
163 | Right, I was on the first item. Maybe create a frame for that:
164 |
165 | ```sh
166 | [root/frame/create GUI/GUI Elements] $ frame push 'populate history element' --message='
167 | > create a populate_history() procedure
168 | > '
169 | Frame 0.1.2, (© 2023 Lelanthran Manickum)
170 | Created new frame [root/frame/create GUI/GUI Elements/populate history element]
171 | ```
172 |
173 | I create the named function, run it, watch in disbelief as it crashes, and
174 | then investigate the crash. Turns out there's a bug in the library. New frame
175 | time:
176 | ```sh
177 | [root/frame/create GUI/GUI Elements/populate history element] $ frame push 'debug frm_history' --message='
178 | > frm_history, when called multiple times in the same session, has a double-free
179 | > bug. This does not show up in the c/line `frame history` command because the `frame`
180 | > program runs the specified command and then exits.
181 | > '
182 | Frame 0.1.2, (© 2023 Lelanthran Manickum)
183 | Created new frame [root/frame/create GUI/GUI Elements/populate history element/debug frm_history]
184 | ```
185 |
186 | I switch to working on the frame library instead. I update the test script to
187 | reproduce the error, and using valgrind, and then some gdb, I come up with an
188 | appropriate fix.
189 |
190 | ```mermaid
191 | graph TD
192 | A[root] -->B[projects];
193 | B[projects] --> C[frame];
194 | C --> D[create GUI];
195 | D --> E[populate history element];
196 | E --> F[debug frm_history];
197 | F --> G[update tests to reproduce bugs];
198 | G --> H[fix bug];
199 | ```
200 |
201 | Now that the bug in frm_history has been fixed, I perform multiple `frame pop`s,
202 | until I reach a frame that is still incomplete: `populate history element'.
203 |
204 | After each `pop`, the current frame is **deleted**, never to be seen again.
205 | This helps keep the signal/noise ratio to an acceptable number.
206 |
207 | Once I am at `populate history element`, I relink and rerun the program. It
208 | populates the history just fine. I continue my development in this manner;
209 | `push`ing new frames when I start something even slightly long, `pop`ping
210 | frames once I am done with whatever I am currently working on.
211 |
212 | When I need to record more notes or thoughts related to the current frame, I
213 | use `frame edit` and record my notes in the editor that opens.
214 |
215 | When I come
216 | back to my computer after (for example) a good nights sleep, my terminal will
217 | display the name of the current frame. If that is not sufficient to remind me
218 | where I stopped last, then `frame status` displays all my notes.
219 |
220 | Sometimes I come back to a frame after multiple days; I create a frame, store
221 | my notes, then 'push' new frames diving ever deeper into whatever it is I am
222 | doing, and then two days later I have finally `pop`ped my way back to the
223 | original frame, and thankfully I can use `frame status` to remember what it
224 | was I wanted to do next.
225 |
226 | It's all very linear, and intentionally so. You can create new frames
227 | without immediately switching to the newly created frame by using `frame new`.
228 | I do this when I know in advance that I need to do $X, $Y and $Z: then it's
229 | simpler to just do `frame create $X`, `frame create $Y` and `frame create $Z`,
230 |
231 | ## Not only linear
232 | I create multiple frames off `root`; typically one for each project. The
233 | `frame switch` command accepts a target frame, and attempts to switch to the
234 | sub-branch of that frame that you were last working on.
235 |
236 | For example, having frames
237 |
238 | ```mermaid
239 | graph TD
240 | A[root] --> B[frame];
241 | A --> C[game];
242 | B --> D[create GUI];
243 | D --> E[populate history];
244 | E --> G[debug crash];
245 | B --> H[write docs];
246 | C --> I[Scrabble Clone];
247 | I --> J[implement basic user mgmt];
248 | J --> K[email verification on signup];
249 | I --> L[create board];
250 | L --> M[store game state];
251 | ```
252 | My current frame is *[root/frame/create GUI/populate history]*. The last time I
253 | was in a sub-branch of *[root/game]*, it was in the *[email verification on
254 | signup]* sub-branch.
255 |
256 | When I do `frame switch root/game`, it returns me to the branch
257 | *[root/game/Scrabble Clone/implement basic user mgmt/ email verification on
258 | signup]*.
259 |
260 | The next time I do `frame switch root/frame`, it returns me to
261 | *[root/frame/create GUI/populate history]*.
262 |
263 | ## Command reference
264 | You can get a list of commands using `frame --help`. The important commands
265 | are
266 |
267 | 1. `push ` - push a new child frame with specified title.
268 | 2. `pop` - discard current frame and make parent current.
269 | 3. `edit` - edit the context attached to the current frame.
270 | 4. `switch ` - (smart) switch to a new path.
271 |
272 |
273 | [^1]: I made a conscious decision at the start of this project that `root` will
274 | always be explicit and not implicit. This avoids errors where a typo results
275 | in addressing root when the user did not mean to. For example the following
276 | error is impossible when root is explicit: `rm -rf myhomedir/Downloads /lib`.
277 |
278 | [^2]: In reality, my `PS1` variable is a lot more complex than this, as it
279 | includes the git branch (if any) and other information (current directory,
280 | username, etc). My prompt uses a separate line for the `frame` name, as this
281 | name can get quite long.
282 |
--------------------------------------------------------------------------------
/android-build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Build for android targets
4 | #
5 |
6 | # Use pretty colors for output
7 | export FG_RESET="\033[39m"
8 | export BG_RESET="\033[49m"
9 | export RESET="${FG_RESET}${BG_RESET}"
10 | export FG_BLACK="\033[30m"
11 | export FG_RED="\033[31m"
12 | export FG_GREEN="\033[32m"
13 | export FG_YELLOW="\033[33m"
14 | export FG_BLUE="\033[34m"
15 | export FG_MAGENTA="\033[35m"
16 | export FG_CYAN="\033[36m"
17 | export FG_GREY90="\033[37m"
18 | export BG_BLACK="\033[40m"
19 | export BG_RED="\033[41m"
20 | export BG_GREEN="\033[42m"
21 | export BG_YELLOW="\033[43m"
22 | export BG_BLUE="\033[44m"
23 | export BG_MAGENTA="\033[45m"
24 | export BG_CYAN="\033[46m"
25 | export BG_GREY90="\033[47m"
26 | export BG_GREY50="\033[100m"
27 | export BG_LIGHTRED="\033[101m"
28 | export BG_LIGHTGREEN="\033[102m"
29 | export BG_LIGHTYELLOW="\033[103m"
30 | export BG_LIGHTBLUE="\033[104m"
31 | export BG_PURPLE="\033[105m"
32 | export BG_TEAL="\033[106m"
33 | export BG_WHITE="\033[107m"
34 |
35 | # Quick exit with a message
36 | function die () {
37 | printf "${FG_RED}$@${RESET}\n"
38 | printf "${BG_RED}${FG_BLACK}Aborting ... ${RESET}\n"
39 | exit 127
40 | }
41 |
42 | # Print the variables we are using to locate the GCC we want.
43 | function print_kv () {
44 | export size_name=`echo -ne $1 | wc -c`
45 | export nspaces=$((20 - $size_name))
46 | printf "${FG_BLUE}${1}${RESET}"
47 | printf "%${nspaces}s" " "
48 | printf "[${FG_GREEN}${2}${RESET}]\n"
49 | }
50 |
51 | # Read the user selections. Whatever is set on the command-line overrides the
52 | # environment variables of the same name.
53 | #
54 |
55 | function highlight () {
56 | echo -ne "${BG_GREY90}${FG_GREEN}${1}${RESET}"
57 | }
58 |
59 | function print_help_message () {
60 | export HELP_MSG="
61 | android-build.sh [--android-ndk-path=]
62 | [--target-machine=]
63 | [--android-level=]
64 |
65 |
66 | Build for a specified android target architecture. At least one make
67 | target must be specified. Multiple make targets separated by spaces can
68 | be specified.
69 |
70 | Options are set from files, the environment and the command-line. Any
71 | option set later overrides in options that were already set. Options are
72 | set in the following order:
73 |
74 | 1. From the system android-build.conf file ("`highlight /etc/android-build.conf`"
75 | or "`highlight /c/Windows/android-build.conf`" on Windows).
76 |
77 | 2. From the file "`highlight .android-build.conf`" file in the users home directory
78 | (even on Windows): "`highlight \\$HOME/.android-build.conf`"
79 |
80 | 3. From the file '.android-build.conf' in the current directory:
81 | "`highlight \\$PWD/.android-build.conf`"
82 |
83 | 4. From the environment variables (the description of each option below
84 | specifies which variable is used for each option).
85 |
86 | 5. From the command-line, as described below.
87 |
88 | Command-line options override the environment variables, which override
89 | options set in the "`highlight \\$PWD/.android-build.conf`", which overrides
90 | "`highlight \\$HOME/.android-build.conf`" which overrides "`highlight \\$SYSROOT/android-build.conf`".
91 |
92 | --android-ndk-path=
93 | Specify a path. A path must be specified. If no path is specified the
94 | environment variable "`highlight ANDROID_NDK_PATH`" is used.
95 |
96 | --target-machine=
97 | Specify the machine to build for. The name "`highlight all`" will build for all
98 | machines found in the Android NDK. At least one machine or the word 'all'
99 | must be specified. Multiple target machines may be specified using spaces
100 | to separate the machine names. If a target machine is not specified the
101 | environment variable "`highlight TARGET_MACHINE`" is used.
102 |
103 | --android-level=
104 | Specify the android level to build for. Multiple android levels can be
105 | specified, seperated with a space. If a level is not specified the
106 | environment variable "`highlight ANDROID_LEVEL`" is used.
107 | "
108 | printf "$HELP_MSG\n"
109 |
110 | exit 0
111 | }
112 |
113 | export HOST_MACHINE="`uname -m`"
114 | export HOST_OS="`uname -o`"
115 | export TARGET_OS=linux
116 | export SYSROOT_CONF=/etc/android-build.conf
117 |
118 | # TODO: Double-check this on Windows.
119 | if [ "$HOST_OS" == "msys" ]; then
120 | export SYSROOT_CONF=/c/Windows/android-build.conf
121 | fi
122 |
123 | function source_file () {
124 | if [ -f "$1" ]; then
125 | printf "Reading variables from ${FG_MAGENTA}$1${RESET}\n"
126 | . "$1"
127 | fi
128 | }
129 |
130 | source_file "$SYSROOT_CONF"
131 | source_file "$HOME/.android-build.conf"
132 | source_file "$PWD/.android-build.conf"
133 |
134 | function get_cline_value () {
135 | echo $1 | cut -f 2 -d =
136 | }
137 |
138 | while [ ! -z "$1" ]; do
139 | case $1 in
140 |
141 | --help*) print_help_message
142 | ;;
143 |
144 | --android-ndk-path=*) export ANDROID_NDK_PATH=`get_cline_value $1`
145 | ;;
146 |
147 | --target-machine=*) export TARGET_MACHINE=`get_cline_value "$1"`
148 |
149 | ;;
150 |
151 | --android-level=*) export ANDROID_LEVEL=`get_cline_value "$1"`
152 | ;;
153 |
154 | *) export MAKE_TARGETS="$MAKE_TARGETS $1"
155 | ;;
156 |
157 | esac
158 | shift 1
159 | done
160 |
161 | [ -z "${MAKE_TARGETS}" ] && die "No make targets specified."
162 | [ -z "${ANDROID_NDK_PATH}" ] && die 'Value $ANDROID_NDK_PATH not set. Try --help.'
163 | [ -z "${TARGET_MACHINE}" ] && die 'Value $TARGET_MACHINE not set. Try --help.'
164 | [ -z "${ANDROID_LEVEL}" ] && die 'Value $ANDROID_LEVEL not set. Try --help.'
165 |
166 | # TODO: Add in the one for Windows here
167 | case "$HOST_OS" in
168 |
169 | GNU/Linux) export HOST_OS=linux
170 | ;;
171 |
172 |
173 | *) die "Unknown OS - $HOST_OS"
174 | ;;
175 | esac
176 |
177 | export HOST_BUILD_TOOLS="$ANDROID_NDK_PATH/toolchains/llvm/prebuilt/$HOST_OS-$HOST_MACHINE"
178 |
179 | if [ "$TARGET_MACHINE" == "all" ]; then
180 | export INSTALLED_ARCHES="`ls -1 $HOST_BUILD_TOOLS/bin/*-android*-clang* | sed \"s:$HOST_BUILD_TOOLS/bin/::g\" | cut -f 1 -d - | sort -u`"
181 | echo "Found the following arches: $INSTALLED_ARCHS"
182 | export TARGET_MACHINE=$INSTALLED_ARCHES
183 | fi
184 |
185 | for X in $TARGET_MACHINE; do
186 | export TARGET_EABI=android
187 | if [ `echo $X | grep -c arm` -gt 0 ]; then
188 | export TARGET_EABI=androideabi
189 | echo "TARGET_EABI $TARGET_EABI"
190 | fi
191 |
192 | for Y in $ANDROID_LEVEL; do
193 | export ANDROID_GCC="$HOST_BUILD_TOOLS/bin/${X}-${TARGET_OS}-${TARGET_EABI}${Y}-clang"
194 | export ANDROID_GXX="$HOST_BUILD_TOOLS/bin/${X}-${TARGET_OS}-${TARGET_EABI}${Y}-clang++"
195 |
196 | print_kv MAKE_TARGETS ${MAKE_TARGETS}
197 | print_kv ANDROID_NDK_PATH ${ANDROID_NDK_PATH}
198 | print_kv ANDROID_GCC ${ANDROID_GCC}
199 | print_kv HOST_MACHINE ${HOST_MACHINE}
200 | print_kv HOST_OS ${HOST_OS}
201 | print_kv TARGET_MACHINE ${X}
202 | print_kv ANDROID_LEVEL ${Y}
203 |
204 | $ANDROID_GCC --version &> /dev/null || die "Error executing android compiler [${BG_RED}${FG_BLACK}$ANDROID_GCC${RESET}]"
205 |
206 | export GCC=$ANDROID_GCC
207 | export GXX=$ANDROID_GXX
208 | export LD_PROG=$ANDROID_GCC
209 | export LD_LIB=$ANDROID_GCC
210 |
211 | for Z in $MAKE_TARGETS; do
212 | make $Z -j 4
213 | done
214 | done
215 | done
216 |
--------------------------------------------------------------------------------
/build.config:
--------------------------------------------------------------------------------
1 | # ######################################################################
2 | # First, set your project name and version here.
3 | PROJNAME=frame
4 | VERSION=0.1.4
5 |
6 | # ######################################################################
7 | # These are used to create packages.
8 |
9 | # First, the maintainer and project details
10 | MAINTAINER=lelanthran
11 | HOMEPAGE=http://www.lelanthran.com
12 |
13 | # The section that this software belongs in
14 | SECTION=custom
15 |
16 | # The dependencies for this software to run (not build). The dependencies for
17 | # building are specified below in the EXTRA_LIBS section (and entries for build
18 | # dependencies have to be in the HEADERS field).
19 | #
20 | # The dependencies must be specified as a comma-seperated list of package names,
21 | # with an optional version of that package in parenthesis.
22 | DEPENDS=
23 |
24 | # Define a description that will go into the package.
25 | #
26 | define DESCRIPTION
27 | Frame: a context-switch manager for humans.
28 |
29 | Humans are notoriously poor at context-switching between multiple
30 | tasks. Tasks, which may recursively have subtasks, can easily get
31 | forgotten, misremembered or partially completed.
32 |
33 | Frame implements a tree of nodes, in which each node represents some
34 | context. The user can create new nodes to represent a new context, edit
35 | the content of that context, and eventually delete it when done.
36 |
37 | endef
38 |
39 | # ######################################################################
40 | # Set the main (executable) source files. These are all the source files
41 | # that have a 'main' function. Note that you must specify the filename
42 | # without the extension, so don't specify 'myfile.c', just specify
43 | # 'myfile'.
44 | #
45 | # Note that this list is only for C files.
46 | MAIN_PROGRAM_CSOURCEFILES=\
47 | frame
48 |
49 | # ######################################################################
50 | # Set the main (executable) source files. These are all the source files
51 | # that have a 'main' function. Note that you must specify the filename
52 | # without the extension, so don't specify 'myfile.c', just specify
53 | # 'myfile'.
54 | #
55 | # Note that this list is only for C++ files.
56 | MAIN_PROGRAM_CPPSOURCEFILES=\
57 |
58 |
59 | # ######################################################################
60 | # Set each of the source files that must be built. These are all those
61 | # source files (both .c and .cpp) that *DON'T* have a main function. All
62 | # of these files will be compiled into a single library (sorry, I do not
63 | # have plans to allow multiple library files to be built).
64 | #
65 | # Note once again that you must not specify the file extension.
66 | # Unfortunately you are not allowed to have two object files that have the
67 | # same name save for the extension. For example, you cannot have 'myfile.c'
68 | # and 'myfile.cpp' in the same project, although it is allowed
69 | # (encouraged even) to have either 'myfile.c' or 'myfile.cpp' together
70 | # with 'myfile.h'.
71 | #
72 | # Note that this list is only for C files.
73 | LIBRARY_OBJECT_CSOURCEFILES=\
74 | frm\
75 | ds_str\
76 | ds_array
77 |
78 | # ######################################################################
79 | # Set each of the source files that must be built. These are all those
80 | # source files (both .c and .cpp) that *DON'T* have a main function. All
81 | # of these files will be compiled into a single library (sorry, I do not
82 | # have plans to allow multiple library files to be built).
83 | #
84 | # Note once again that you must not specify the file extension.
85 | # Unfortunately you are not allowed to have two object files that have the
86 | # same name save for the extension. For example, you cannot have 'myfile.c'
87 | # and 'myfile.cpp' in the same project, although it is allowed
88 | # (encouraged even) to have either 'myfile.c' or 'myfile.cpp' together
89 | # with 'myfile.h'.
90 | #
91 | # Note that this list is only for C++ files.
92 | LIBRARY_OBJECT_CPPSOURCEFILES=\
93 |
94 |
95 |
96 | # ######################################################################
97 | # For now we set the headers manually. In the future I plan to use gcc to
98 | # generate the dependencies that can be included in this file. Simply name
99 | # all the header files you wrote for this project. Note that unlike the
100 | # previous settings, for this setting you must specify the path to the
101 | # headers (relative to this directory).
102 | HEADERS=\
103 | src/frm.h\
104 | src/ds_str.h\
105 | src/ds_array.h\
106 |
107 |
108 | # ######################################################################
109 | # Here you must set the list of include paths. Note that the variable
110 | # $(HOME) is available if you have include directories relative to your
111 | # home directory. $(HOME) works correctly on Windows as well.
112 | #
113 | # You can put as many paths in here as you want to. I've put one in as an
114 | # example (to my home directory include) and one to the openjdk on my system
115 | # for jni functionality.
116 | INCLUDE_PATHS=\
117 | $(HOME)/include\
118 | /usr/lib/jvm/java-11-openjdk-amd64/include\
119 | /usr/lib/jvm/java-11-openjdk-amd64/include/linux
120 |
121 | # ######################################################################
122 | # This is similar to the INCLUDE_PATHS you set above, except that it is
123 | # for the library search paths. $(HOME) is available if you have library
124 | # directories relative to your home directory. $(HOME) works correctly
125 | # on Windows as well.
126 | #
127 | # You can put as many paths in here as you want to. I've put one in as an
128 | # example. See the variable LIBRARY_FILES to set the actual libraries you
129 | # want to link in.
130 | LIBRARY_PATHS=\
131 | $(INSTALL_PREFIX)/lib/$(TARGET)
132 |
133 |
134 | # ######################################################################
135 | # This is for specifying extra libraries. Note that you must only specify
136 | # the library name, and neither the extension nor the prefix 'lib'.
137 | #
138 | # These files *MUST* be in the library search path specified with
139 | # LIBRARY_PATHS.
140 | #
141 | # I've put in an example here that is commented out, so that you can see
142 | # how the files are supposed to be specified but, because it is commented
143 | # out, it will not break the build process if this library is not
144 | # installed.
145 | #
146 | # (If it is not commented out, go ahead and comment it out when the build
147 | # fails)
148 | #LIBRARY_FILES=\
149 | # ds
150 |
151 |
152 | # ######################################################################
153 | # Here you set extra compiler flags that are common to both the C++ and
154 | # the C compiler. You can comment this line out with no ill-effects.
155 | #
156 | # Note that this does not override the existing flags, it only adds to
157 | # them
158 | EXTRA_COMPILER_FLAGS=\
159 | -W -Wall\
160 |
161 |
162 |
163 | # ######################################################################
164 | # Here you set extra compiler flags for the C compiler only. You can comment
165 | # this line out with no ill-effects.
166 | #
167 | # Note that this does not override the existing flags, it only adds to
168 | # them
169 | EXTRA_CFLAGS=\
170 | -std=gnu99
171 |
172 |
173 | # ######################################################################
174 | # Here you set extra compiler flags for the C++ compiler only. You can
175 | # comment this line out with no ill-effects.
176 | #
177 | # Note that this does not override the existing flags, it only adds to
178 | # them
179 | EXTRA_CXXFLAGS=\
180 | -std=c++11\
181 |
182 |
183 | # ######################################################################
184 | # You can add in extra flags to the linker here, for the library. This
185 | # does not override the existing flags, it adds to them.
186 | #
187 | EXTRA_LIB_LDFLAGS=\
188 |
189 |
190 |
191 | # ######################################################################
192 | # You can add in extra flags to the linker here, for the programs. This
193 | # does not override the existing flags, it adds to them.
194 | #
195 | EXTRA_PROG_LDFLAGS=\
196 |
197 |
198 | # ######################################################################
199 | # The default compilers are gcc and g++. If you want to specify something
200 | # different, this is the place to do it. This is useful if you want to
201 | # cross-compile, or use a different gcc/g++ than the one in your path, or
202 | # simply want to use clang instead.
203 | #
204 | # Note that only clang and gcc are supported (due to reliance on the
205 | # compiler command-line options).
206 | #
207 | # You can comment this out with no ill-effects.
208 | GCC?=gcc
209 | GXX?=g++
210 | LD_PROG?=gcc
211 | LD_LIB?=gcc
212 |
213 | # ######################################################################
214 | # If you need need to call your library from other languages, set the
215 | # SWIG targets here, using 'wrap-$language' as a target. The SWIG_WRAPPERS
216 | # can be left empty if this is not needed.
217 | #
218 | # Note that to use SWIG:
219 | # 1. It must be installed,
220 | # 2. The swig-input.swig file must be modified for your project.
221 | #
222 | # An example of basic SWIG usage can be found at:
223 | # http://swig.org/tutorial.html
224 | #
225 | #SWIG_WRAPPERS:=\
226 | # wrap-java
227 |
228 |
229 | # ######################################################################
230 | # TODO: Add in a way to override the default linker
231 | #
232 |
233 |
234 |
--------------------------------------------------------------------------------
/docs/FrameIntroduction.odp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lelanthran/frame/f1197928963dca50fc4c61b85091d9166b3385b9/docs/FrameIntroduction.odp
--------------------------------------------------------------------------------
/frame-browser/FrameBrowser.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lelanthran/frame/f1197928963dca50fc4c61b85091d9166b3385b9/frame-browser/FrameBrowser.ico
--------------------------------------------------------------------------------
/frame-browser/FrameBrowser.lpi:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | -
28 |
29 |
30 | -
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 | -
84 |
85 |
86 | -
87 |
88 |
89 | -
90 |
91 |
92 |
93 |
94 |
95 |
--------------------------------------------------------------------------------
/frame-browser/FrameBrowser.lpr:
--------------------------------------------------------------------------------
1 | program FrameBrowser;
2 |
3 | {$mode objfpc}{$H+}
4 |
5 | uses
6 | {$IFDEF UNIX}
7 | cthreads,
8 | {$ENDIF}
9 | {$IFDEF HASAMIGA}
10 | athreads,
11 | {$ENDIF}
12 | Interfaces, // this includes the LCL widgetset
13 | Forms, lazcontrols, mainWin, FrameWrapper;
14 |
15 | {$R *.res}
16 |
17 | begin
18 | RequireDerivedFormResource:=True;
19 | Application.Scaled:=True;
20 | Application.Initialize;
21 | Application.CreateForm(TfrmMain, frmMain);
22 | Application.Run;
23 | frm_close(frame_var);
24 | end.
25 |
26 |
--------------------------------------------------------------------------------
/frame-browser/FrameBrowser.lps:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 | -
223 |
224 |
225 |
226 | -
227 |
228 |
229 |
230 |
231 |
232 |
--------------------------------------------------------------------------------
/frame-browser/FrameBrowser.res:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lelanthran/frame/f1197928963dca50fc4c61b85091d9166b3385b9/frame-browser/FrameBrowser.res
--------------------------------------------------------------------------------
/frame-browser/framewrapper.pas:
--------------------------------------------------------------------------------
1 | unit FrameWrapper;
2 |
3 | {$mode ObjFPC}{$H+}
4 |
5 | interface
6 | uses
7 | Classes, SysUtils, Ctypes, ComCtrls, Types, StrUtils,
8 | StdCtrls;
9 |
10 | type
11 | va_list = pointer;
12 |
13 | frm_t = pointer;
14 | frm_node_t = Pointer;
15 |
16 | var frame_var: frm_t;
17 |
18 |
19 | function frm_create(dbpath: PAnsiChar): frm_t; cdecl; external 'frame';
20 | function frm_init(dbpath: PAnsiChar): frm_t; cdecl; external 'frame';
21 | procedure frm_close(frm: frm_t); cdecl; external 'frame';
22 |
23 | function frm_history(frm: frm_t; count: csize_t): PAnsiChar; cdecl; external 'frame';
24 | function frm_current(frm: frm_t): PAnsiChar; cdecl; external 'frame';
25 | function frm_payload: PAnsiChar; cdecl; external 'frame';
26 | function frm_date_epoch: UInt64; cdecl; external 'frame';
27 | function frm_date_str: PAnsiChar; cdecl; external 'frame';
28 | function frm_lastmsg(frm: frm_t): PAnsiChar; cdecl; external 'frame';
29 |
30 | function frm_new(frm: frm_t; name, message: PAnsiChar): LongBool; cdecl; external 'frame';
31 | function frm_push(frm: frm_t; name, message: PAnsiChar): LongBool; cdecl; external 'frame';
32 | function frm_payload_replace(message: PAnsiChar): LongBool; cdecl; external 'frame';
33 | function frm_payload_append(message: PAnsiChar): LongBool; cdecl; external 'frame';
34 | function frm_payload_fname: PAnsiChar; cdecl; external 'frame';
35 |
36 | function frm_top(frm: frm_t): LongBool; cdecl; external 'frame';
37 | function frm_up(frm: frm_t): LongBool; cdecl; external 'frame';
38 | function frm_down(frm: frm_t; target: PAnsiChar): LongBool; cdecl; external 'frame';
39 | function frm_switch(frm: frm_t; target: PAnsiChar): LongBool; cdecl; external 'frame';
40 | function frm_switch_direct(frm: frm_t; target: PAnsiChar): LongBool; cdecl; external 'frame';
41 | function frm_back(frm: frm_t; index: csize_t): LongBool; cdecl; external 'frame';
42 | function frm_delete(frm: frm_t; target: PAnsiChar): LongBool; cdecl; external 'frame';
43 | function frm_pop(frm: frm_t; force: LongBool): LongBool; cdecl; external 'frame';
44 | function frm_rename(frm: frm_t; newname: PAnsiChar): LongBool; cdecl; external 'frame';
45 |
46 | function frm_list(frm: frm_t; from: PAnsiChar): PPAnsiChar; cdecl; external 'frame';
47 | function frm_match(frm: frm_t; sterm: PAnsiChar; flags: LongWord): PPAnsiChar; cdecl; external 'frame';
48 | function frm_match_from_root(frm: frm_t; sterm: PAnsiChar; flags: LongWord): PPAnsiChar; cdecl; external 'frame';
49 |
50 | procedure frm_mem_free(ptr: Pointer); cdecl; external 'frame';
51 | procedure frm_strarray_free(array_: PPAnsiChar); cdecl; external 'frame';
52 |
53 | function frm_readfile(fname: PAnsiChar): PAnsiChar; cdecl; external 'frame';
54 | function frm_vwritefile(fname: PAnsiChar; data: PAnsiChar; ap: Pointer): LongBool; cdecl; external 'frame';
55 | function frm_writefile(fname: PAnsiChar; data: PAnsiChar; args: array of const): LongBool; cdecl; varargs; external 'frame';
56 | function frm_homepath: PAnsiChar; cdecl; external 'frame';
57 |
58 | function frm_node_create(frm: frm_t): frm_node_t; cdecl; external 'frame';
59 | procedure frm_node_free(rootnode: frm_node_t); cdecl; external 'frame';
60 |
61 | function frm_node_name(node: frm_node_t): PAnsiChar; cdecl; external 'frame';
62 | function frm_node_date(node: frm_node_t): cuint64; cdecl; external 'frame';
63 | function frm_node_fpath(node: frm_node_t): PChar; cdecl; external 'frame';
64 | function frm_node_nchildren(node: frm_node_t): csize_t; cdecl; external 'frame';
65 | function frm_node_child(node: frm_node_t; index: csize_t): frm_node_t; cdecl; external 'frame';
66 | function frm_node_parent(node: frm_node_t): frm_node_t; cdecl; external 'frame';
67 | function frm_node_root(node: frm_node_t): frm_node_t; cdecl; external 'frame';
68 | function frm_node_find(node: frm_node_t; fpath: PChar): frm_node_t; cdecl; external 'frame';
69 |
70 |
71 |
72 | procedure frame_history_populate(searchTerm: String; tlView: TListView);
73 | procedure frame_frames_populate(tv: TTreeView);
74 | procedure frame_current_populate(edt: TEdit);
75 | procedure frame_notes_populate(memo: TMemo);
76 | procedure frame_set_frames_selected(tv: TTreeView; edt: TEdit);
77 |
78 | implementation
79 |
80 | procedure frame_history_populate(searchTerm: String; tlView: TListView);
81 | var
82 | frame_history: PChar;
83 | i: csize_t;
84 | strItems: TStringDynArray;
85 | listItem: TListItem;
86 |
87 | begin
88 | frame_history := frm_history(frame_var, csize_t($ffffffffffffffff));
89 | strItems := SplitString(frame_history, #$0a);
90 |
91 | tlView.Items.Clear();
92 | for i:=0 to (Length(strItems) - 1) do
93 | begin
94 | if (Length(searchTerm) > 2) and (PosEx(searchTerm, strItems[i]) <= 0) then
95 | begin
96 | continue;
97 | end;
98 | listItem := tlView.Items.Add();
99 | listItem.Caption := Copy(strItems[i], 1, Length(strItems[i]));
100 | end;
101 | frm_mem_free(frame_history);
102 | end;
103 |
104 |
105 | procedure addChild (tv: TTreeView; parent: TTreeNode; frmNode: frm_node_t);
106 | var
107 | name: String;
108 | nchildren: csize_t;
109 | child: frm_node_t;
110 | i: csize_t;
111 | current: TTreeNode;
112 |
113 | begin
114 | name := frm_node_name(frmNode);
115 | current := tv.Items.AddChild(parent, name);
116 | // current.Data := frm_node_fpath(frmNode);
117 |
118 | nchildren := frm_node_nchildren(frmNode);
119 | i := 0;
120 |
121 | while i < nchildren do
122 | begin
123 | child := frm_node_child(frmNode, i);
124 | addChild (tv, current, child);
125 | Inc(i);
126 | end;
127 | end;
128 |
129 | procedure frame_frames_populate(tv: TTreeView);
130 | var
131 | root: pointer;
132 | begin
133 | tv.Items.Clear;
134 | root := frm_node_create(frame_var);
135 | addChild (tv, nil, root);
136 | frm_node_free(root);
137 | end;
138 |
139 | procedure frame_current_populate(edt: TEdit);
140 | begin
141 | edt.Caption := frm_current(frame_var);
142 | end;
143 |
144 | procedure frame_notes_populate(memo: TMemo);
145 | begin
146 | memo.Clear;
147 | memo.Append(frm_payload());
148 | end;
149 |
150 | procedure frame_set_frames_selected(tv: TTreeView; edt: TEdit);
151 | begin
152 | tv.Selected := tv.Items.FindNodeWithTextPath(edt.Caption);
153 | end;
154 | end.
155 |
156 |
--------------------------------------------------------------------------------
/frame-browser/mainwin.lfm:
--------------------------------------------------------------------------------
1 | object frmMain: TfrmMain
2 | Left = 472
3 | Height = 486
4 | Top = 234
5 | Width = 1278
6 | Caption = 'Frame Browser'
7 | ClientHeight = 486
8 | ClientWidth = 1278
9 | OnActivate = FormActivate
10 | OnCreate = FormCreate
11 | LCLVersion = '2.2.6.0'
12 | object Panel1: TPanel
13 | AnchorSideLeft.Control = Owner
14 | AnchorSideTop.Control = Owner
15 | AnchorSideRight.Control = Owner
16 | AnchorSideRight.Side = asrBottom
17 | Left = 0
18 | Height = 32
19 | Top = 0
20 | Width = 1278
21 | Anchors = [akTop, akLeft, akRight]
22 | ClientHeight = 32
23 | ClientWidth = 1278
24 | TabOrder = 0
25 | object bbtnQuit: TBitBtn
26 | AnchorSideLeft.Control = Panel1
27 | AnchorSideTop.Control = Panel1
28 | AnchorSideBottom.Control = Panel1
29 | AnchorSideBottom.Side = asrBottom
30 | Left = 1
31 | Height = 30
32 | Hint = 'Quit FrameBrowser'
33 | Top = 1
34 | Width = 75
35 | Anchors = [akTop, akLeft, akBottom]
36 | Caption = 'Quit'
37 | OnClick = bbtnQuitClick
38 | ParentShowHint = False
39 | ShowHint = True
40 | TabOrder = 0
41 | end
42 | object bbtnHelp: TBitBtn
43 | AnchorSideLeft.Control = bbtnQuit
44 | AnchorSideLeft.Side = asrBottom
45 | AnchorSideTop.Control = Panel1
46 | AnchorSideBottom.Control = Panel1
47 | AnchorSideBottom.Side = asrBottom
48 | Left = 76
49 | Height = 30
50 | Top = 1
51 | Width = 75
52 | Anchors = [akTop, akLeft, akBottom]
53 | Caption = 'Help'
54 | TabOrder = 1
55 | end
56 | end
57 | object Panel2: TScrollBox
58 | AnchorSideLeft.Control = Owner
59 | AnchorSideTop.Control = Panel1
60 | AnchorSideTop.Side = asrBottom
61 | AnchorSideRight.Control = Owner
62 | AnchorSideRight.Side = asrBottom
63 | AnchorSideBottom.Control = Owner
64 | AnchorSideBottom.Side = asrBottom
65 | Left = 0
66 | Height = 454
67 | Top = 32
68 | Width = 1278
69 | HorzScrollBar.Page = 946
70 | HorzScrollBar.Tracking = True
71 | VertScrollBar.Page = 195
72 | Anchors = [akTop, akLeft, akRight, akBottom]
73 | ClientHeight = 452
74 | ClientWidth = 1276
75 | TabOrder = 1
76 | object PairSplitter1: TPairSplitter
77 | AnchorSideLeft.Control = Panel2
78 | AnchorSideTop.Control = Panel2
79 | AnchorSideRight.Control = Panel2
80 | AnchorSideRight.Side = asrBottom
81 | AnchorSideBottom.Control = Panel2
82 | AnchorSideBottom.Side = asrBottom
83 | Left = 0
84 | Height = 452
85 | Top = 0
86 | Width = 1276
87 | Anchors = [akTop, akLeft, akRight, akBottom]
88 | Position = 408
89 | object PairSplitterSide1: TPairSplitterSide
90 | Cursor = crArrow
91 | Left = 0
92 | Height = 452
93 | Top = 0
94 | Width = 408
95 | ClientWidth = 408
96 | ClientHeight = 452
97 | object StaticText2: TStaticText
98 | AnchorSideLeft.Control = PairSplitterSide1
99 | AnchorSideTop.Control = PairSplitterSide1
100 | AnchorSideRight.Control = PairSplitterSide1
101 | AnchorSideRight.Side = asrBottom
102 | Left = 0
103 | Height = 17
104 | Top = 0
105 | Width = 408
106 | Anchors = [akTop, akLeft, akRight]
107 | Caption = 'History'
108 | TabOrder = 0
109 | end
110 | object edtSearchTerm: TEdit
111 | AnchorSideLeft.Control = PairSplitterSide1
112 | AnchorSideTop.Control = StaticText2
113 | AnchorSideTop.Side = asrBottom
114 | AnchorSideRight.Control = PairSplitterSide1
115 | AnchorSideRight.Side = asrBottom
116 | Left = 0
117 | Height = 28
118 | Top = 17
119 | Width = 408
120 | Anchors = [akTop, akLeft, akRight]
121 | OnChange = edtSearchTermChange
122 | TabOrder = 1
123 | end
124 | object lvHistory: TListView
125 | AnchorSideLeft.Control = PairSplitterSide1
126 | AnchorSideTop.Control = edtSearchTerm
127 | AnchorSideTop.Side = asrBottom
128 | AnchorSideRight.Control = PairSplitterSide1
129 | AnchorSideRight.Side = asrBottom
130 | AnchorSideBottom.Control = PairSplitterSide1
131 | AnchorSideBottom.Side = asrBottom
132 | Left = 0
133 | Height = 407
134 | Top = 45
135 | Width = 408
136 | Anchors = [akTop, akLeft, akRight, akBottom]
137 | Columns = <>
138 | Enabled = False
139 | TabOrder = 2
140 | OnSelectItem = lvHistorySelectItem
141 | end
142 | end
143 | object PairSplitterSide2: TPairSplitterSide
144 | Cursor = crArrow
145 | Left = 412
146 | Height = 452
147 | Top = 0
148 | Width = 864
149 | ClientWidth = 864
150 | ClientHeight = 452
151 | object PairSplitter2: TPairSplitter
152 | AnchorSideLeft.Control = PairSplitterSide2
153 | AnchorSideTop.Control = PairSplitterSide2
154 | AnchorSideRight.Control = PairSplitterSide2
155 | AnchorSideRight.Side = asrBottom
156 | AnchorSideBottom.Control = PairSplitterSide2
157 | AnchorSideBottom.Side = asrBottom
158 | Left = 0
159 | Height = 452
160 | Top = 0
161 | Width = 864
162 | Anchors = [akTop, akLeft, akRight, akBottom]
163 | Position = 336
164 | object PairSplitterSide3: TPairSplitterSide
165 | Cursor = crArrow
166 | Left = 0
167 | Height = 452
168 | Top = 0
169 | Width = 336
170 | ClientWidth = 336
171 | ClientHeight = 452
172 | object StaticText1: TStaticText
173 | AnchorSideLeft.Control = PairSplitterSide3
174 | AnchorSideTop.Control = PairSplitterSide3
175 | AnchorSideRight.Control = PairSplitterSide3
176 | AnchorSideRight.Side = asrBottom
177 | Left = 0
178 | Height = 17
179 | Top = 0
180 | Width = 336
181 | Anchors = [akTop, akLeft, akRight]
182 | Caption = 'Current Frame'
183 | TabOrder = 0
184 | end
185 | object tvFrames: TTreeView
186 | AnchorSideLeft.Control = PairSplitterSide3
187 | AnchorSideTop.Control = edtCurrentFrame
188 | AnchorSideTop.Side = asrBottom
189 | AnchorSideRight.Control = PairSplitterSide3
190 | AnchorSideRight.Side = asrBottom
191 | AnchorSideBottom.Control = PairSplitterSide3
192 | AnchorSideBottom.Side = asrBottom
193 | Left = 0
194 | Height = 407
195 | Top = 45
196 | Width = 336
197 | Anchors = [akTop, akLeft, akRight, akBottom]
198 | PopupMenu = ctxMenuCurrentFrame
199 | TabOrder = 2
200 | OnEditingEnd = tvFramesEditingEnd
201 | OnKeyDown = tvFramesKeyDown
202 | OnSelectionChanged = tvFramesSelectionChanged
203 | Items.Data = {
204 | F9FFFFFF020001000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF010000000000
205 | 000001050000004974656D30FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF01000000
206 | 0000000001050000004974656D31FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000
207 | 00000000000000050000004974656D32
208 | }
209 | end
210 | object edtCurrentFrame: TEdit
211 | AnchorSideLeft.Control = PairSplitterSide3
212 | AnchorSideTop.Control = StaticText1
213 | AnchorSideTop.Side = asrBottom
214 | AnchorSideRight.Control = PairSplitterSide3
215 | AnchorSideRight.Side = asrBottom
216 | AnchorSideBottom.Control = tvFrames
217 | Left = 0
218 | Height = 28
219 | Top = 17
220 | Width = 336
221 | Anchors = [akTop, akLeft, akRight]
222 | Enabled = False
223 | TabOrder = 1
224 | end
225 | end
226 | object PairSplitterSide4: TPairSplitterSide
227 | Cursor = crArrow
228 | Left = 340
229 | Height = 452
230 | Top = 0
231 | Width = 524
232 | ClientWidth = 524
233 | ClientHeight = 452
234 | object StaticText3: TStaticText
235 | AnchorSideLeft.Control = PairSplitterSide4
236 | AnchorSideTop.Control = PairSplitterSide4
237 | AnchorSideRight.Control = PairSplitterSide4
238 | AnchorSideRight.Side = asrBottom
239 | Left = 0
240 | Height = 17
241 | Top = 0
242 | Width = 58
243 | Caption = 'Notes'
244 | TabOrder = 0
245 | end
246 | object memoNotes: TMemo
247 | AnchorSideLeft.Control = PairSplitterSide4
248 | AnchorSideTop.Control = StaticText3
249 | AnchorSideTop.Side = asrBottom
250 | AnchorSideRight.Control = PairSplitterSide4
251 | AnchorSideRight.Side = asrBottom
252 | AnchorSideBottom.Control = PairSplitterSide4
253 | AnchorSideBottom.Side = asrBottom
254 | Left = 0
255 | Height = 435
256 | Top = 17
257 | Width = 524
258 | Anchors = [akTop, akLeft, akRight, akBottom]
259 | Font.Height = -13
260 | Font.Name = 'Monospace'
261 | Lines.Strings = (
262 | ''
263 | )
264 | OnChange = memoNotesChange
265 | OnEditingDone = memoNotesEditingDone
266 | ParentFont = False
267 | ScrollBars = ssBoth
268 | TabOrder = 1
269 | end
270 | object stxtStatusChanged: TStaticText
271 | AnchorSideTop.Control = PairSplitterSide4
272 | AnchorSideRight.Control = PairSplitterSide4
273 | AnchorSideRight.Side = asrBottom
274 | Left = 330
275 | Height = 17
276 | Top = 0
277 | Width = 194
278 | Anchors = [akTop, akRight]
279 | Caption = 'Notes Changed'
280 | Color = clDefault
281 | Font.Color = clRed
282 | ParentFont = False
283 | ParentColor = False
284 | TabOrder = 2
285 | end
286 | end
287 | end
288 | end
289 | end
290 | end
291 | object sbarStatus: TStatusBar
292 | Left = 0
293 | Height = 18
294 | Top = 468
295 | Width = 1278
296 | Panels = <>
297 | SimpleText = 'help'
298 | end
299 | object ctxMenuCurrentFrame: TPopupMenu
300 | Left = 208
301 | object MenuItem1: TMenuItem
302 | Caption = 'Push Child Frame'
303 | OnClick = MenuItem1Click
304 | end
305 | object MenuItem2: TMenuItem
306 | Caption = 'Pop This Frame'
307 | OnClick = MenuItem2Click
308 | end
309 | object MenuItem3: TMenuItem
310 | Caption = 'Rename This Frame'
311 | OnClick = MenuItem3Click
312 | end
313 | object Separator1: TMenuItem
314 | Caption = '-'
315 | end
316 | object MenuItem4: TMenuItem
317 | Caption = 'Pop Frame and All Children'
318 | OnClick = MenuItem4Click
319 | end
320 | end
321 | end
322 |
--------------------------------------------------------------------------------
/frame-browser/mainwin.pas:
--------------------------------------------------------------------------------
1 | unit mainWin;
2 |
3 | {$mode objfpc}{$H+}
4 |
5 | interface
6 |
7 | uses
8 | Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls, Buttons,
9 | StdCtrls, PairSplitter, Types, ComCtrls, TreeFilterEdit, CTypes, Cmem,
10 | LCLType, Menus,
11 | FrameWrapper;
12 |
13 | type
14 |
15 | { TfrmMain }
16 |
17 | TfrmMain = class(TForm)
18 | bbtnQuit: TBitBtn;
19 | bbtnHelp: TBitBtn;
20 | edtSearchTerm: TEdit;
21 | lvHistory: TListView;
22 | memoNotes: TMemo;
23 | MenuItem1: TMenuItem;
24 | MenuItem2: TMenuItem;
25 | MenuItem3: TMenuItem;
26 | MenuItem4: TMenuItem;
27 | PairSplitter1: TPairSplitter;
28 | PairSplitter2: TPairSplitter;
29 | PairSplitterSide1: TPairSplitterSide;
30 | PairSplitterSide2: TPairSplitterSide;
31 | PairSplitterSide3: TPairSplitterSide;
32 | PairSplitterSide4: TPairSplitterSide;
33 | Panel1: TPanel;
34 | Panel2: TScrollBox;
35 | ctxMenuCurrentFrame: TPopupMenu;
36 | Separator1: TMenuItem;
37 | StaticText1: TStaticText;
38 | StaticText2: TStaticText;
39 | StaticText3: TStaticText;
40 | edtCurrentFrame: TEdit;
41 | sbarStatus: TStatusBar;
42 | stxtStatusChanged: TStaticText;
43 | tvFrames: TTreeView;
44 | procedure bbtnQuitClick(Sender: TObject);
45 | procedure edtSearchTermChange(Sender: TObject);
46 | procedure FormActivate(Sender: TObject);
47 | procedure FormCreate(Sender: TObject);
48 | procedure lvHistorySelectItem(Sender: TObject; Item: TListItem;
49 | Selected: Boolean);
50 | procedure memoNotesChange(Sender: TObject);
51 | procedure memoNotesEditingDone(Sender: TObject);
52 | procedure MenuItem1Click(Sender: TObject);
53 | procedure MenuItem2Click(Sender: TObject);
54 | procedure MenuItem3Click(Sender: TObject);
55 | procedure MenuItem4Click(Sender: TObject);
56 | procedure tvFramesEditingEnd(Sender: TObject; Node: TTreeNode;
57 | Cancel: Boolean);
58 | procedure tvFramesKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState
59 | );
60 | procedure tvFramesSelectionChanged(Sender: TObject);
61 | private
62 | notesChanged: Boolean;
63 | public
64 |
65 | end;
66 |
67 | var
68 | frmMain: TfrmMain;
69 |
70 | implementation
71 |
72 | {$R *.lfm}
73 |
74 | { TfrmMain }
75 |
76 |
77 | procedure Alert(message: String);
78 | var
79 | tmp: String;
80 | begin
81 | ShowMessage(message);
82 | tmp := frm_lastmsg(frame_var);
83 | frmMain.sbarStatus.SimpleText:=frm_lastmsg(frame_var);
84 | end;
85 |
86 | procedure TfrmMain.bbtnQuitClick(Sender: TObject);
87 | begin
88 | frmMain.Close;
89 | end;
90 |
91 |
92 | procedure TfrmMain.edtSearchTermChange(Sender: TObject);
93 | var
94 | sterm: String;
95 | begin
96 | sterm := frmMain.edtSearchTerm.Caption;
97 | frame_history_populate (sterm, frmMain.lvHistory);
98 | end;
99 |
100 | function FrameInit (path: String): Boolean;
101 | begin
102 | frm_close(frame_var);
103 | frame_var := frm_init(Pchar(path));
104 | if frame_var = nil then
105 | begin
106 | Alert('Failed to open Frame Database at [' + path + ']'
107 | + sLineBreak
108 | + 'Aborting');
109 | Exit(false);
110 | end;
111 | Exit(true);
112 | end;
113 |
114 | procedure FrameReopen();
115 | begin
116 | FrameInit(frm_homepath() + '/.framedb');
117 | frame_history_populate('', frmMain.lvHistory);
118 | frame_current_populate(frmMain.edtCurrentFrame);
119 | frame_notes_populate(frmMain.memoNotes);
120 | frame_frames_populate(frmMain.tvFrames);
121 | frame_set_frames_selected(frmMain.tvFrames, frmMain.edtCurrentFrame);
122 | end;
123 |
124 | procedure TfrmMain.FormActivate(Sender: TObject);
125 | begin
126 | if FrameInit(frm_homepath() + '/.framedb') <> true then
127 | begin
128 | frmMain.Close;
129 | Exit;
130 | end;
131 | frame_history_populate('', frmMain.lvHistory);
132 | frame_current_populate(frmMain.edtCurrentFrame);
133 | frame_notes_populate(frmMain.memoNotes);
134 | frame_frames_populate(frmMain.tvFrames);
135 | frame_set_frames_selected(frmMain.tvFrames, frmMain.edtCurrentFrame);
136 | end;
137 |
138 |
139 | procedure TfrmMain.FormCreate(Sender: TObject);
140 | begin
141 |
142 | end;
143 |
144 | procedure TfrmMain.lvHistorySelectItem(Sender: TObject; Item: TListItem;
145 | Selected: Boolean);
146 | var
147 | fpath: String;
148 | ttnode: TListItem;
149 | begin
150 | ttnode := frmMain.lvHistory.Selected;
151 | if ttnode <> nil then
152 | begin
153 | fpath := ttnode.Caption;
154 | frm_switch_direct(frame_var, PChar(fpath));
155 | frame_current_populate(frmMain.edtCurrentFrame);
156 | frame_notes_populate(frmMain.memoNotes);
157 | frame_frames_populate(frmMain.tvFrames);
158 | frame_set_frames_selected(frmMain.tvFrames, frmMain.edtCurrentFrame);
159 | end;
160 | end;
161 |
162 | procedure TfrmMain.memoNotesChange(Sender: TObject);
163 | begin
164 | frmMain.notesChanged:=true;
165 | frmMain.stxtStatusChanged.Caption:='Frame notes changed';
166 | end;
167 |
168 |
169 | procedure TfrmMain.memoNotesEditingDone(Sender: TObject);
170 | var
171 | reply, boxStyle: Integer;
172 | begin
173 | if frmMain.notesChanged = false then
174 | Exit;
175 |
176 | boxStyle := MB_ICONQUESTION + MB_YESNO;
177 | reply := Application.MessageBox('Frame contents changed. Save Changes?', 'Contents Changed', BoxStyle);
178 | if reply = IDYES then
179 | begin
180 | frm_payload_replace(PChar(frmMain.memoNotes.Text));
181 | end;
182 | frmMain.notesChanged:=false;
183 | frmMain.stxtStatusChanged.Caption:='';
184 | end;
185 |
186 | procedure TfrmMain.MenuItem1Click(Sender: TObject);
187 | begin
188 | frmMain.sbarStatus.SimpleText:='Pushing new frame';
189 | if frm_push(frame_var, 'New Frame', 'Enter Contents for new frame') <> true then
190 | begin
191 | Alert('failed to push new frame:' + sLineBreak + frm_lastmsg(frame_var));
192 | end else
193 | begin
194 | FrameReopen();
195 | frmMain.tvFrames.Selected := frmMain.tvFrames.Items.FindNodeWithTextPath(frmMain.edtCurrentFrame.Caption);
196 | frmMain.tvFrames.Selected.EditText;
197 | end;
198 | end;
199 |
200 | procedure TfrmMain.MenuItem2Click(Sender: TObject);
201 | begin
202 | frmMain.sbarStatus.SimpleText:='Popping current frame';
203 | if frm_pop(frame_var, false) <> true then
204 | begin
205 | Alert('failed to pop frame:' + sLineBreak + frm_lastmsg(frame_var));
206 | end else
207 | begin
208 | FrameReopen();
209 | end;
210 | end;
211 |
212 | procedure TfrmMain.MenuItem3Click(Sender: TObject);
213 | var
214 | node: TTreeNode;
215 | begin
216 | frmMain.sbarStatus.SimpleText:='Renaming current frame';
217 | node := frmMain.tvFrames.Selected;
218 | node.EditText();
219 | end;
220 |
221 | procedure TfrmMain.MenuItem4Click(Sender: TObject);
222 | begin
223 | frmMain.sbarStatus.SimpleText:='Recursively popping current frame';
224 | if frm_pop(frame_var, true) <> true then
225 | begin
226 | Alert('failed to force pop frame:' + sLineBreak + frm_lastmsg(frame_var));
227 | end else
228 | begin
229 | FrameReopen();
230 | end;
231 | end;
232 |
233 | procedure TfrmMain.tvFramesEditingEnd(Sender: TObject; Node: TTreeNode;
234 | Cancel: Boolean);
235 | var
236 | newname: String;
237 | begin
238 | if Cancel = true then
239 | Exit();
240 |
241 | newname := Node.Text;
242 | if frm_rename(frame_var, PChar(newname)) <> true then
243 | begin
244 | Alert('Failed to rename node:' + sLineBreak + frm_lastmsg(frame_var));
245 | end else
246 | begin
247 | FrameReopen();
248 | end;
249 | end;
250 |
251 | procedure TfrmMain.tvFramesKeyDown(Sender: TObject; var Key: Word;
252 | Shift: TShiftState);
253 | begin
254 | if key = VK_F2 then
255 | begin
256 | frmMain.tvFrames.Selected.EditText;
257 | end;
258 |
259 | if key = VK_LCL_ALT then
260 | begin
261 | frmMain.ctxMenuCurrentFrame.PopUp;
262 | end;
263 | end;
264 |
265 |
266 | function TreeNodeFpath(node: TTreeNode): String;
267 | var
268 | parent: String;
269 | begin
270 | if node = nil then
271 | begin
272 | Exit('');
273 | end;
274 |
275 | parent := TreeNodeFpath(node.Parent);
276 | if Length(parent) > 1 then
277 | begin
278 | parent := parent + '/';
279 | end;
280 | Exit(parent + node.Text);
281 | end;
282 |
283 | procedure TfrmMain.tvFramesSelectionChanged(Sender: TObject);
284 | var
285 | fpath: String;
286 | ttnode: TTreeNode;
287 | begin
288 | ttnode := frmMain.tvFrames.Selected;
289 | if ttnode <> nil then
290 | begin
291 | fpath := TreeNodeFpath(frmMain.tvFrames.Selected);
292 | frm_switch_direct(frame_var, PChar(fpath));
293 | frame_history_populate('', frmMain.lvHistory);
294 | frame_current_populate(frmMain.edtCurrentFrame);
295 | frame_notes_populate(frmMain.memoNotes);
296 | frmMain.notesChanged:=false;
297 | frmMain.stxtStatusChanged.Caption:= '';
298 | end;
299 | end;
300 |
301 | end.
302 |
303 |
--------------------------------------------------------------------------------
/frame.bash.autocomplete:
--------------------------------------------------------------------------------
1 | # Source this file to have frame autocompletions in bash
2 |
3 | _frame_completions() {
4 | local cmd="$1" # Always 'frame'
5 | local cur="$2" # Current completion, can be blank
6 | local prev="$3" # Previous word, can be 'frame'
7 |
8 | local opts="--help"
9 | local commands=("status" "switch" "current" "down")
10 | local args
11 | local fpath="`frame current --quiet | cut -f 1 -d :`"
12 |
13 | declare -a COMPREPLY
14 |
15 | pushd $PWD &> /dev/null
16 |
17 | case "${prev}" in
18 | down)
19 | cd "$HOME/.framedb/$fpath"
20 | args="`for X in $cur*/; do printf "%s\n" "$X" | sed 's/ /|/g'; done`"
21 | local INDEX=0;
22 | echo "cur = [$cur]" > /tmp/t
23 | for X in `compgen -W "${args}" ${cur}`
24 | do
25 | COMPREPLY[$INDEX]=`echo -ne $X | sed 's/|/ /g'`
26 | echo $INDEX: ${COMPREPLY[$INDEX]} >> /tmp/t
27 | printf "in loop: %q\n" "${COMPREPLY[@]}" >> /tmp/t
28 | INDEX=$(($INDEX+1))
29 | done
30 | printf "out loop 1: %q\n" "${COMPREPLY[@]}" >> /tmp/t
31 | printf "out loop 2: %q\n" "${COMPREPLY[*]}" >> /tmp/t
32 | ;;
33 | *)
34 | ;;
35 | esac
36 |
37 | popd &> /dev/null
38 | return 0;
39 | }
40 |
41 | complete -F _frame_completions frame
42 |
--------------------------------------------------------------------------------
/src/ds_array.c:
--------------------------------------------------------------------------------
1 |
2 | /* ************************************************************************** *
3 | * Frame (©2023 Lelanthran Manickum) *
4 | * *
5 | * This program comes with ABSOLUTELY NO WARRANTY. This is free software *
6 | * and you are welcome to redistribute it under certain conditions; see *
7 | * the LICENSE file for details. *
8 | * ****************************************************************************/
9 |
10 |
11 | #include
12 | #include
13 | #include
14 | #include
15 |
16 | #include "ds_array.h"
17 |
18 | struct ds_array_t {
19 | size_t nitems;
20 | void **array;
21 | };
22 |
23 | ds_array_t *ds_array_new (void)
24 | {
25 | ds_array_t *ret = calloc (1, sizeof *ret);
26 | if (!(ret->array = calloc (1, sizeof *(ret->array)))) {
27 | free (ret);
28 | ret = NULL;
29 | }
30 | return ret;
31 | }
32 |
33 | void ds_array_del (ds_array_t *ll)
34 | {
35 | if (!ll)
36 | return;
37 |
38 | free (ll->array);
39 | free (ll);
40 | }
41 |
42 | ds_array_t *ds_array_copy (const ds_array_t *src, size_t from_index, size_t to_index)
43 | {
44 | size_t nitems;
45 | ds_array_t *ret = ds_array_new ();
46 | bool error = true;
47 |
48 | if (!src)
49 | return NULL;
50 |
51 | nitems = ds_array_length (src);
52 |
53 | for (size_t i=from_index; i>=from_index && iarray[i])))
55 | goto errorexit;
56 | }
57 |
58 | error = false;
59 |
60 | errorexit:
61 |
62 | if (error) {
63 | ds_array_del (ret);
64 | ret = NULL;
65 | }
66 |
67 | return ret;
68 | }
69 |
70 | size_t ds_array_length (const ds_array_t *ll)
71 | {
72 | if (!ll)
73 | return 0;
74 |
75 | return ll->nitems;
76 | }
77 |
78 | void *ds_array_get (const ds_array_t *ll, size_t i)
79 | {
80 | if (!ll)
81 | return NULL;
82 |
83 | if (i >= ll->nitems)
84 | return NULL;
85 |
86 | return ll->array[i];
87 | }
88 |
89 | void ds_array_iterate (const ds_array_t *ll,
90 | void (*fptr) (void *, void *), void *param)
91 | {
92 | if (!ll || !fptr)
93 | return;
94 |
95 | for (size_t i=0; ll->array[i]; i++) {
96 | fptr (ll->array[i], param);
97 | }
98 | }
99 |
100 | static bool ds_array_grow (ds_array_t *ll, size_t nelems)
101 | {
102 | if (!ll)
103 | return false;
104 |
105 | void **tmp = realloc (ll->array, (sizeof *ll->array) * (ll->nitems + 1 + nelems + 1));
106 | if (!tmp)
107 | return false;
108 |
109 | ll->array = tmp;
110 |
111 | memset (&ll->array[ll->nitems], 0, (sizeof *ll->array) * (nelems + 1));
112 | return true;
113 | }
114 |
115 | void ds_array_shrink_to_fit (ds_array_t *ll)
116 | {
117 | if (!ll)
118 | return;
119 |
120 | void **tmp = realloc (ll->array, (sizeof *ll->array) * (ll->nitems + 1));
121 | if (!tmp)
122 | return;
123 |
124 | ll->array = tmp;
125 | }
126 |
127 | void *ds_array_ins_tail (ds_array_t *ll, void *el)
128 | {
129 | if (!ll || !el)
130 | return NULL;
131 |
132 | if (!(ds_array_grow (ll, 1)))
133 | return NULL;
134 |
135 | ll->array[ll->nitems++] = el;
136 |
137 | return el;
138 | }
139 |
140 | void *ds_array_ins_head (ds_array_t *ll, void *el)
141 | {
142 | if (!ll || !el)
143 | return NULL;
144 |
145 | size_t endpos = ll->nitems;
146 |
147 | if (!(ds_array_grow (ll, 1)))
148 | return NULL;
149 |
150 | memmove (&ll->array[1], &ll->array[0], (sizeof *(ll->array)) * (endpos + 1));
151 |
152 | ll->array[0] = el;
153 | ll->nitems++;
154 |
155 | return el;
156 | }
157 |
158 | void *ds_array_rm_tail (ds_array_t *ll)
159 | {
160 | if(!ll || !ll->array[0] || ll->nitems == 0)
161 | return NULL;
162 |
163 | void *ret = ll->array[ll->nitems - 1];
164 |
165 | ll->nitems--;
166 | ll->array[ll->nitems] = NULL;
167 |
168 | return ret;
169 | }
170 |
171 | void *ds_array_rm_head (ds_array_t *ll)
172 | {
173 | return ds_array_rm (ll, 0);
174 | }
175 |
176 |
177 | void *ds_array_rm (ds_array_t *ll, size_t index)
178 | {
179 | if (!ll || ll->nitems == 0)
180 | return NULL;
181 |
182 | if (index >= ll->nitems)
183 | return NULL;
184 |
185 | void *ret = ll->array[index];
186 |
187 | memmove (&ll->array[index], &ll->array[index + 1],
188 | (sizeof (void *)) * (ll->nitems - index));
189 |
190 | return ret;
191 | }
192 |
--------------------------------------------------------------------------------
/src/ds_array.h:
--------------------------------------------------------------------------------
1 |
2 | /* ************************************************************************** *
3 | * Frame (©2023 Lelanthran Manickum) *
4 | * *
5 | * This program comes with ABSOLUTELY NO WARRANTY. This is free software *
6 | * and you are welcome to redistribute it under certain conditions; see *
7 | * the LICENSE file for details. *
8 | * ****************************************************************************/
9 |
10 |
11 | #ifndef H_DS_LL
12 | #define H_DS_LL
13 |
14 | #include
15 |
16 | typedef struct ds_array_t ds_array_t;
17 |
18 | // This array stores pointers to objects that must be allocated and freed by the caller.
19 | // Removing an entry from the array does not free the object stored by the caller. The caller
20 | // must free all objects that they have allocated.
21 | #ifdef __cplusplus
22 | extern "C" {
23 | #endif
24 |
25 | // TODO: Change 'remove' to 'rm'
26 | ds_array_t *ds_array_new (void);
27 | void ds_array_del (ds_array_t *ll);
28 | ds_array_t *ds_array_copy (const ds_array_t *src, size_t from_index, size_t to_index);
29 |
30 | size_t ds_array_length (const ds_array_t *ll);
31 | void *ds_array_get (const ds_array_t *ll, size_t i);
32 | void ds_array_iterate (const ds_array_t *ll,
33 | void (*fptr) (void *, void *), void *param);
34 |
35 | void *ds_array_ins_tail (ds_array_t *ll, void *el);
36 | void *ds_array_ins_head (ds_array_t *ll, void *el);
37 |
38 | void *ds_array_rm_tail (ds_array_t *ll);
39 | void *ds_array_rm_head (ds_array_t *ll);
40 |
41 | void *ds_array_rm (ds_array_t *ll, size_t index);
42 |
43 | void ds_array_shrink_to_fit (ds_array_t *ll);
44 |
45 | #ifdef __cplusplus
46 | };
47 | #endif
48 |
49 | #endif
50 |
51 |
52 |
--------------------------------------------------------------------------------
/src/ds_str.c:
--------------------------------------------------------------------------------
1 |
2 | /* ************************************************************************** *
3 | * Frame (©2023 Lelanthran Manickum) *
4 | * *
5 | * This program comes with ABSOLUTELY NO WARRANTY. This is free software *
6 | * and you are welcome to redistribute it under certain conditions; see *
7 | * the LICENSE file for details. *
8 | * ****************************************************************************/
9 |
10 |
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 |
18 | #include "ds_str.h"
19 |
20 | /* ******************************************************************** */
21 |
22 | char *ds_str_dup (const char *src)
23 | {
24 | if (!src)
25 | return NULL;
26 |
27 | size_t len = strlen (src) + 1;
28 |
29 | char *ret = malloc (len);
30 | if (!ret)
31 | return NULL;
32 |
33 | return strcpy (ret, src);
34 | }
35 |
36 | /* ******************************************************************** */
37 |
38 | char *ds_str_vcat (const char *src, va_list ap)
39 | {
40 | bool error = true;
41 | char *ret = NULL;
42 | const char *tmp = src;
43 | size_t nbytes = 0;
44 | va_list apc;
45 |
46 | va_copy (apc, ap);
47 |
48 | while (tmp) {
49 | nbytes += strlen (tmp);
50 | tmp = va_arg (apc, const char *);
51 | }
52 |
53 | va_end (apc);
54 |
55 | if (!(ret = malloc (nbytes + 1)))
56 | goto errorexit;
57 |
58 | *ret = 0;
59 |
60 | tmp = src;
61 | size_t idx = strlen (ret);
62 | while (tmp) {
63 | // strcat (ret, tmp);
64 | size_t tmplen = strlen (tmp);
65 | memcpy (&ret[idx], tmp, tmplen);
66 | idx += tmplen;
67 | tmp = va_arg (ap, const char *);
68 | }
69 | ret[idx] = 0;
70 |
71 | error = false;
72 |
73 | errorexit:
74 |
75 | if (error) {
76 | free (ret);
77 | ret = NULL;
78 | }
79 |
80 | return ret;
81 | }
82 |
83 | char *ds_str_cat (const char *src, ...)
84 | {
85 | va_list ap;
86 |
87 | va_start (ap, src);
88 | char *ret = ds_str_vcat (src, ap);
89 | va_end (ap);
90 |
91 | return ret;
92 | }
93 |
94 | char *ds_str_vappend (char **dst, const char *s1, va_list ap)
95 | {
96 | bool error = true;
97 | char *ret = NULL;
98 |
99 |
100 | if (!(*dst))
101 | (*dst) = ds_str_dup ("");
102 |
103 | if (!(*dst))
104 | return NULL;
105 |
106 | if (!(ret = ds_str_cat ((*dst), s1, NULL)))
107 | goto errorexit;
108 |
109 | size_t idx = strlen (ret);
110 | while ((s1 = va_arg (ap, char *))!=NULL) {
111 | // char *tmp = ds_str_cat (ret, s1, NULL);
112 | // if (!tmp)
113 | // goto errorexit;
114 |
115 | size_t s1_len = strlen (s1);
116 | size_t newlen = idx + s1_len;
117 | char *tmp = realloc (ret, newlen + 1);
118 | if (!tmp) {
119 | free (ret);
120 | return NULL;
121 | }
122 | ret = tmp;
123 | memcpy (&ret[idx], s1, s1_len);
124 | idx += s1_len;
125 | }
126 |
127 | ret[idx] = 0;
128 |
129 | free (*dst);
130 | (*dst) = ret;
131 |
132 | error = false;
133 |
134 | errorexit:
135 |
136 | if (error) {
137 | free (ret);
138 | ret = NULL;
139 | }
140 |
141 | return ret;
142 | }
143 |
144 | char *ds_str_append (char **dst, const char *s1, ...)
145 | {
146 | va_list ap;
147 |
148 | va_start (ap, s1);
149 | char *ret = ds_str_vappend (dst, s1, ap);
150 | va_end (ap);
151 |
152 | return ret;
153 | }
154 |
155 | /* ******************************************************************** */
156 |
157 | size_t ds_str_vprintf (char **dst, const char *fmt, va_list ap)
158 | {
159 | size_t ret = 0;
160 | size_t tmprc = 0;
161 | char *tmp = NULL;
162 | va_list ac;
163 |
164 | *dst = NULL;
165 |
166 | va_copy (ac, ap);
167 | int rc = vsnprintf (*dst, ret, fmt, ac);
168 | va_end (ac);
169 |
170 | ret = rc + 1;
171 |
172 | if (!(tmp = realloc (*dst, ret))) {
173 | return 0;
174 | }
175 |
176 | *dst = tmp;
177 | rc = vsnprintf (*dst, ret, fmt, ap);
178 | tmprc = rc;
179 | if (tmprc >= ret) {
180 | free (*dst);
181 | *dst = NULL;
182 | return 0;
183 | }
184 |
185 | return ret;
186 | }
187 |
188 | size_t ds_str_printf (char **dst, const char *fmt, ...)
189 | {
190 | va_list ap;
191 |
192 | va_start (ap, fmt);
193 | size_t ret = ds_str_vprintf (dst, fmt, ap);
194 | va_end (ap);
195 |
196 | return ret;
197 | }
198 |
199 | /* ******************************************************************** */
200 |
201 | char *ds_str_ltrim (char *src)
202 | {
203 | if (!src)
204 | return NULL;
205 |
206 | size_t begin = 0;
207 | size_t slen = strlen (src);
208 |
209 | if (!slen)
210 | return src;
211 |
212 | while ((src[begin]) && (isspace (src[begin]))) {
213 | begin++;
214 | }
215 | if (!src[begin]) {
216 | src[0] = 0;
217 | }
218 |
219 | slen++;
220 |
221 | memmove (&src[0], &src[begin], slen - begin);
222 | return src;
223 | }
224 |
225 | char *ds_str_rtrim (char *src)
226 | {
227 | if (!src)
228 | return NULL;
229 |
230 | size_t slen = strlen (src);
231 | size_t end = slen - 1;
232 |
233 | if (!slen)
234 | return src;
235 |
236 | while (end!=0 && isspace (src[end])) {
237 | src[end--] = 0;
238 | }
239 | if (end==0 && isspace (src[0])) {
240 | src[0] = 0;
241 | }
242 |
243 | return src;
244 | }
245 |
246 | char *ds_str_trim (char *src)
247 | {
248 | return ds_str_rtrim (ds_str_ltrim (src));
249 | }
250 |
251 | /* ******************************************************************** */
252 |
253 | char *ds_str_vchsubst (const char *src, int oldc, int newc, va_list ap)
254 | {
255 | char *ret = ds_str_dup (src);
256 | if (!ret)
257 | return NULL;
258 |
259 | while (oldc) {
260 | char *tmp = ret;
261 | while ((tmp = strchr (tmp, (char)oldc))) {
262 | *tmp++ = (char)newc;
263 | }
264 | oldc = va_arg (ap, int);
265 | if (oldc)
266 | newc = va_arg (ap, int);
267 | }
268 | return ret;
269 | }
270 |
271 | char *ds_str_chsubst (const char *src, int oldc, int newc, ...)
272 | {
273 | va_list ap;
274 |
275 | va_start (ap, newc);
276 | char *ret = ds_str_vchsubst (src, oldc, newc, ap);
277 | va_end (ap);
278 |
279 | return ret;
280 | }
281 |
282 | char *ds_str_strsubst (const char *src,
283 | const char *olds, const char *news, ...)
284 | {
285 | va_list ap;
286 |
287 | va_start (ap, news);
288 | char *ret = ds_str_vstrsubst (src, olds, news, ap);
289 | va_end (ap);
290 |
291 | return ret;
292 | }
293 |
294 | static char *ds_str_subst1 (char *src, const char *olds, const char *news)
295 | {
296 | bool error = true;
297 | char *ret = NULL;
298 |
299 | size_t olds_len = strlen (olds);
300 | char *tmp = NULL;
301 | char *startstr = src;
302 |
303 | while ((tmp = strstr (startstr, olds))) {
304 |
305 | *tmp++ = 0;
306 |
307 | if (!(ds_str_append (&ret, startstr, news, NULL))) {
308 | goto errorexit;
309 | }
310 |
311 | tmp += olds_len - 1;
312 | startstr = tmp;
313 | }
314 |
315 | if (!(ds_str_append (&ret, startstr, NULL))) {
316 | goto errorexit;
317 | }
318 |
319 | error = false;
320 |
321 | errorexit:
322 | if (error) {
323 | free (ret);
324 | ret = NULL;
325 | }
326 |
327 | return ret;
328 | }
329 |
330 | char *ds_str_vstrsubst (const char *src,
331 | const char *olds, const char *news, va_list ap)
332 | {
333 | bool error = true;
334 | char *ret = NULL;
335 | char *tmpsrc = NULL;
336 |
337 | error = false;
338 |
339 | if (!(ret = ds_str_dup (src)))
340 | goto errorexit;
341 |
342 | while (olds) {
343 |
344 | char *tmp = ds_str_subst1 (ret, olds, news);
345 | if (!tmp)
346 | goto errorexit;
347 |
348 | free (ret);
349 | ret = tmp;
350 |
351 | if ((olds = va_arg (ap, const char *))) {
352 | if (!(news = va_arg (ap, const char *))) {
353 | break;
354 | }
355 | }
356 | }
357 |
358 | error = false;
359 |
360 | errorexit:
361 |
362 | free (tmpsrc);
363 |
364 | if (error) {
365 | free (ret);
366 | ret = NULL;
367 | }
368 |
369 | return ret;
370 | }
371 |
372 | char *ds_str_substring (const char *src, size_t from_position, size_t nchars)
373 | {
374 | size_t srclen = strlen (src);
375 | size_t lastchar = nchars + from_position;
376 |
377 | if (lastchar > srclen)
378 | lastchar = srclen;
379 |
380 | char *ret = calloc ((lastchar - from_position) + 2, 1);
381 | if (!ret)
382 | return NULL;
383 |
384 | size_t dstidx = 0;
385 | for (size_t i=from_position; i
15 | #include
16 |
17 | #ifdef __cplusplus
18 | extern "C" {
19 | #endif
20 |
21 | // Make a copy of the src string, caller must free the result, NULL
22 | // returned on error.
23 | char *ds_str_dup (const char *src);
24 |
25 | // Concatenate all the strings given (ending the parameter list with a
26 | // NULL) into a single string that is returned which the caller must
27 | // free. NULL is returned on error
28 | char *ds_str_cat (const char *src, ...);
29 | char *ds_str_vcat (const char *src, va_list ap);
30 |
31 | // Append all the strings given in '...' (ending with a NULL) to
32 | // parameter '(*dst)'. Parameter '(*dst)' is reallocated as necessary
33 | // and therefore must be reallocatable (returned by malloc() or similar).
34 | // The reallocated '(*dst)' is also returned on success.
35 | //
36 | // NULL is returned on error.
37 | char *ds_str_append (char **dst, const char *s1, ...);
38 | char *ds_str_vappend (char **dst, const char *s1, va_list ap);
39 |
40 | // Perform a printf into a buffer allocated on demand. The parameter
41 | // '*dst' is allocated by this function and must be freed by the caller.
42 | // On success the length of '*dst' is returned, on failure zero is
43 | // returned.
44 | size_t ds_str_printf (char **dst, const char *fmt, ...);
45 | size_t ds_str_vprintf (char **dst, const char *fmt, va_list ap);
46 |
47 | // Trim the leading, the trailing or both the leading and the trailing
48 | // whitespace from the specified string. The operations are performed
49 | // in-place on the string provided.
50 | //
51 | // A pointer to the string is always returned - no errors are possible.
52 | // If the input is NULL then the return value is NULL as well.
53 | char *ds_str_ltrim (char *src);
54 | char *ds_str_rtrim (char *src);
55 | char *ds_str_trim (char *src);
56 |
57 | // Perform a character substitution on the source string. The caller
58 | // must free the returned value. NULL is returned on error.
59 | //
60 | // All occurrences of 'oldc' will be replaced with 'newc'. Thereafter
61 | // every two arguments will be interpreted as a new {oldc,newc} pair
62 | // until oldc is 0.
63 | //
64 | // Note that although oldc and newc are both of type int, they are cast
65 | // to char before usage.
66 | char *ds_str_chsubst (const char *src, int oldc, int newc, ...);
67 | char *ds_str_vchsubst (const char *src, int oldc, int newc, va_list ap);
68 |
69 |
70 | // Perform a string substitution on the source string. The caller must
71 | // free the returned value. NULL is returned on error.
72 | //
73 | // All occurrences of 'olds' will be replaced by 'news'. Thereafter,
74 | // every two arguments will be interpreted as a pair of {olds,news} and
75 | // substitutions continue until an instance of NULL is read for 'olds'.
76 | char *ds_str_strsubst (const char *src,
77 | const char *olds, const char *news, ...);
78 | char *ds_str_vstrsubst (const char *src,
79 | const char *olds, const char *news, va_list ap);
80 |
81 | // Copy nchars from the string src, starting at position from_position. The
82 | // caller is responsible for freeing the returned value. If the nchars results
83 | // in a out of bounds access (i.e. the caller specifies more character to copy
84 | // than exist in the src) the the copy is limited to the number of characters
85 | // available in the string.
86 | //
87 | // If the from_position is out of bounds, then an empty string is returned and
88 | // must still be freed by the caller.
89 | //
90 | // If there is no memory to allocate the return value then NULL is returned.
91 | char *ds_str_substring (const char *src, size_t from_position, size_t nchars);
92 |
93 | #ifdef __cplusplus
94 | };
95 | #endif
96 |
97 | #endif
98 |
99 |
--------------------------------------------------------------------------------
/src/frame.c:
--------------------------------------------------------------------------------
1 |
2 | /* ************************************************************************** *
3 | * Frame (©2023 Lelanthran Manickum) *
4 | * *
5 | * This program comes with ABSOLUTELY NO WARRANTY. This is free software *
6 | * and you are welcome to redistribute it under certain conditions; see *
7 | * the LICENSE file for details. *
8 | * ****************************************************************************/
9 |
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 |
17 | #include
18 |
19 | #include "ds_str.h"
20 | #include "frm.h"
21 |
22 | /* **********************************************************
23 | * Command line handling:
24 | * prog [options] [options] ...
25 | *
26 | * Essentially, options can come before commands, after commands
27 | * or both before and after commands, and multiple subcommands
28 | * may be present, with options once again appearing anywhere.
29 | */
30 |
31 | // All options, separated by 0x1e
32 | static char *g_options = NULL;
33 | static void cline_parse_options (int argc, char **argv)
34 | {
35 | for (int i=1; i [options] ",
208 | "",
209 | " Options are of the form '--name', '--name=' and '--name=value'. The first",
210 | "two forms are for boolean options which are either set or unset and do not",
211 | "require any value. The third form is for options that require a value.",
212 | "",
213 | " Commands and subcommands are of the form 'command'. Some commands require",
214 | "specific options and/or mandatory subcommands. Commands that require either",
215 | "options or subcommands will be decribed below.",
216 | "",
217 | " Commands must be one of help, create, history, status, push, replace,",
218 | "append, up, down, switch, pop, delete, list or match.",
219 | "",
220 | "Options:",
221 | "",
222 | " --help Print this page and exit.",
223 | "",
224 | " --force Force an action that against the program's wishes,",
225 | " for example popping a frame that is not empty.",
226 | "",
227 | " --frame= Execute in the context of the specified frame. Frame can",
228 | " be specified with a relative path (\"../..\") or an",
229 | " absolute path from root (\"root/frame\").",
230 | " For example, to create a sibling frame, use the command:",
231 | " frame --frame=../ new 'New Task Name'",
232 | "",
233 | " --dbpath= Specify the location of the database path. Defaults to",
234 | " '$HOME/.framedb' unless overridden by this option.",
235 | " The Windows default is '$HOMEDRIVE\\$HOMEPATH\\.framedb'",
236 | "",
237 | " --message= Provides the message for any command that requires a",
238 | " message (such as 'push', 'replace', etc). If this option",
239 | " is not present and the command requires a message, then",
240 | " the editor specified with $EDITOR is used. If $EDITOR",
241 | " is empty, then a prompt for a message is issued on the",
242 | " standard input.",
243 | "",
244 | " --from-root Specify that the match command must search for matches",
245 | " from the root frame. If this option is not present then",
246 | " matches are, by default, made only from the current frame",
247 | " onwards (down the tree).",
248 | "",
249 | " --invert Perform an inverted search when matching using a search",
250 | " term. By default the match command finds all nodes that",
251 | " match the search term provided. Using this flag causes",
252 | " the match command to find all nodes that *DON'T* match",
253 | " the search term.",
254 | "",
255 | " --quiet Suppress all non-functional stdout messages, such as",
256 | " the copyright notice.",
257 | "",
258 | "Commands:",
259 | "",
260 | "help",
261 | " Print this message and exit.",
262 | "",
263 | "create",
264 | " Create a new frame database. If --dbpath is specified then it is used as the",
265 | " location of the new database. If it is not then $HOME/.framdb is used instead.",
266 | "",
267 | "history [count]",
268 | " Display the history of all nodes visited, with a number that can be used",
269 | " in the 'back' command (see 'back' below). The [count] value specifies how",
270 | " many items to display. If [count] is omitted it defaults to 10. To list",
271 | " all items in the history (which may be very large) use '0' as the count.",
272 | "",
273 | "back [number]",
274 | " Jump to the nth item in the history, as specified by [number]. If [number]",
275 | " is omitted then '1' is used. The 'history' command helpfully lists a number",
276 | " next to each element that can be used to determine what number in the",
277 | " history to jump to. Specifying '0' is pointless.",
278 | "",
279 | "status",
280 | " Display the status of the current frame.",
281 | "",
282 | "push",
283 | " Create a new frame as the child of the current frame AND switches to it. If a",
284 | " message is specified with '--message=' then it will be used as the",
285 | " contents of the new frame. If no message is specified with '--message='",
286 | " then $EDITOR will be started to allow the user to enter a message. If $EDITOR",
287 | " is not set, the user will be prompted for a message.",
288 | "",
289 | "new",
290 | " Create a new frame as the child of the current frame WITHOUT switching to it. If",
291 | " a message is specified with '--message=' then it will be used as the",
292 | " contents of the new frame. If no message is specified with '--message='",
293 | " then $EDITOR will be started to allow the user to enter a message. If $EDITOR",
294 | " is not set, the user will be prompted for a message.",
295 | "",
296 | "replace",
297 | " Overwrite the content of the current frame with the provided message. See ",
298 | " option '--message='.",
299 | "",
300 | "append",
301 | " Appends the provided message (see option '--message' and command 'push') to",
302 | " the current frame.",
303 | "",
304 | "top",
305 | " Changes the current frame to root frame (i.e. top of the tree).",
306 | "",
307 | "up",
308 | " Changes the current frame to the parent of the current frame.",
309 | "",
310 | "down ",
311 | " Changes the current frame to the child frame named by 'name'.",
312 | "",
313 | "switch ",
314 | " Changes the current frame to the non-child frame named by .",
315 | "",
316 | "pop",
317 | " Deletes the current frame and set the current frame to the parent of the",
318 | " deleted frame.",
319 | "",
320 | "delete ",
321 | " Deletes the frame named by . The current frame is not changed.",
322 | "",
323 | "list",
324 | " Lists all descendents of the current frame.",
325 | "",
326 | "tree",
327 | " Display a tree of all the nodes starting at the root frame.",
328 | "",
329 | "rename ",
330 | " Rename the current node to .",
331 | "",
332 | "match [--from-root] [--invert]",
333 | " Lists the nodes that match the search term , starting at the current",
334 | " frame. If '--from-root' is specified then the search is performed from the",
335 | " root frame and not the current frame. If --invert is specified, then the search",
336 | " is performed for all those nodes *NOT MATCHING* the search term .",
337 | "",
338 | NULL,
339 | };
340 | for (size_t i=0; msg[i]; i++) {
341 | printf ("%s\n", msg[i]);
342 | }
343 | printf ("\n");
344 | }
345 |
346 | static void status (frm_t *frm)
347 | {
348 | char *current = frm_current (frm);
349 | char *payload = frm_payload ();
350 | char *mtime = frm_date_str ();
351 |
352 | printf ("Current frame\n %s\n", current);
353 | printf ("\nNotes (%s)\n", mtime);
354 | char *sptr = NULL;
355 | char *tok = strtok_r (payload, "\n", &sptr);
356 | do {
357 | printf (" %s\n", tok);
358 | } while ((tok = strtok_r (NULL, "\n", &sptr)));
359 | printf ("\n");
360 | free (current);
361 | free (mtime);
362 | free (payload);
363 | }
364 |
365 | static void current (frm_t *frm)
366 | {
367 | char *current = frm_current (frm);
368 | char *mtime = frm_date_str ();
369 |
370 | printf ("%s: %s\n", current, mtime);
371 | free (current);
372 | free (mtime);
373 | }
374 |
375 | #ifdef PLATFORM_Windows
376 | static void ctime_r (time_t *date, char *dst)
377 | {
378 | strcpy (dst, ctime (date));
379 | }
380 | #endif
381 |
382 | int print_tree (const frm_node_t *node, size_t level)
383 | {
384 | #define INDENT(x) for (size_t i=0; i t
169 | # if [ `wc -l t | cut -f 1 -d \ ` -ne 1 ]; then
170 | # die expected failed match
171 | # fi
172 |
173 | # Rename root/one/five
174 | Execute-Frame $PROG switch root/one/five
175 | Execute-Frame $PROG rename 'FIVE'
176 | # Current node is root/one/FIVE
177 | Execute-Frame $PROG status
178 |
179 | Execute-Frame $PROG match --from-root "one/ei"
180 |
181 | Execute-Frame $PROG tree
182 |
183 | echo 'Use [sed "s:(.\+)::g"] to strip the dates'
184 |
185 | exit 0
186 |
187 |
--------------------------------------------------------------------------------
/test.results.saved:
--------------------------------------------------------------------------------
1 | Executing 0: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet create
2 | Created framedb at [/tmp/frame/]
3 | Executing 1: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet status
4 | Current frame
5 | root
6 |
7 | Notes
8 | ENTER YOUR NOTES HERE
9 |
10 | Executing 2: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet history
11 | Frame history
12 | * 0: root
13 |
14 | Executing 3: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet replace --message=Root: replacement
15 | Executing 4: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet status
16 | Current frame
17 | root
18 |
19 | Notes
20 | Root:
21 |
22 | Executing 5: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet history
23 | Frame history
24 | * 0: root
25 |
26 | Executing 6: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet push one --message=One: message one
27 | Created new frame [root/one]
28 | Executing 7: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet status
29 | Current frame
30 | root/one
31 |
32 | Notes
33 | One:
34 |
35 | Executing 8: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet history
36 | Frame history
37 | * 0: root/one
38 | 1: root
39 |
40 | Executing 9: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet append --message=
41 | Message: 1
42 |
43 | root/one: Sat Jun 10 12:36:55 2023
44 | Executing 10: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet status
45 | Current frame
46 | root/one
47 |
48 | Notes
49 | One:
50 | \nMessage:
51 |
52 | Executing 11: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet history
53 | Frame history
54 | * 0: root/one
55 | 1: root
56 |
57 | Executing 12: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet up
58 | Current frame
59 | root
60 |
61 | Notes
62 | Root:
63 |
64 | Executing 13: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet status
65 | Current frame
66 | root
67 |
68 | Notes
69 | Root:
70 |
71 | Executing 14: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet history
72 | Frame history
73 | * 0: root
74 | 1: root/one
75 | 2: root
76 |
77 | Executing 15: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet push two --message=Two: message two
78 | Created new frame [root/two]
79 | Executing 16: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet status
80 | Current frame
81 | root/two
82 |
83 | Notes
84 | Two:
85 |
86 | Executing 17: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet history
87 | Frame history
88 | * 0: root/two
89 | 1: root
90 | 2: root/one
91 | 3: root
92 |
93 | Executing 18: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet top
94 | Current frame
95 | root
96 |
97 | Notes
98 | Root:
99 |
100 | Executing 19: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet down one
101 | Current frame
102 | root/one
103 |
104 | Notes
105 | One:
106 | \nMessage:
107 |
108 | Executing 20: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet status
109 | Current frame
110 | root/one
111 |
112 | Notes
113 | One:
114 | \nMessage:
115 |
116 | Executing 21: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet history
117 | Frame history
118 | * 0: root/one
119 | 1: root
120 | 2: root/two
121 | 3: root
122 | 4: root/one
123 | 5: root
124 |
125 | Executing 22: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet back 1
126 | Current frame
127 | root/one
128 |
129 | Notes
130 | One:
131 | \nMessage:
132 |
133 | Executing 23: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet status
134 | Current frame
135 | root/one
136 |
137 | Notes
138 | One:
139 | \nMessage:
140 |
141 | Executing 24: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet history
142 | Frame history
143 | * 0: root/one
144 | 1: root/one
145 | 2: root
146 | 3: root/two
147 | 4: root
148 | 5: root/one
149 | 6: root
150 |
151 | Executing 25: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet back 1
152 | Current frame
153 | root/one
154 |
155 | Notes
156 | One:
157 | \nMessage:
158 |
159 | Executing 26: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet status
160 | Current frame
161 | root/one
162 |
163 | Notes
164 | One:
165 | \nMessage:
166 |
167 | Executing 27: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet history
168 | Frame history
169 | * 0: root/one
170 | 1: root/one
171 | 2: root/one
172 | 3: root
173 | 4: root/two
174 | 5: root
175 | 6: root/one
176 | 7: root
177 |
178 | Executing 28: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet push three --message=three: three
179 | Created new frame [root/one/three]
180 | Executing 29: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet status
181 | Current frame
182 | root/one/three
183 |
184 | Notes
185 | three:
186 |
187 | Executing 30: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet history
188 | Frame history
189 | * 0: root/one/three
190 | 1: root/one
191 | 2: root/one
192 | 3: root/one
193 | 4: root
194 | 5: root/two
195 | 6: root
196 | 7: root/one
197 | 8: root
198 |
199 | Executing 31: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet pop
200 | Current frame
201 | root/one
202 |
203 | Notes
204 | One:
205 | \nMessage:
206 |
207 | Executing 32: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet status
208 | Current frame
209 | root/one
210 |
211 | Notes
212 | One:
213 | \nMessage:
214 |
215 | Executing 33: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet history
216 | Frame history
217 | * 0: root/one
218 | 1: root/one/three
219 | 2: root/one
220 | 3: root/one
221 | 4: root/one
222 | 5: root
223 | 6: root/two
224 | 7: root
225 | 8: root/one
226 | 9: root
227 |
228 | Executing 34: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet push four --message=four: four
229 | Created new frame [root/one/four]
230 | Executing 35: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet status
231 | Current frame
232 | root/one/four
233 |
234 | Notes
235 | four:
236 |
237 | Executing 36: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet history
238 | Frame history
239 | * 0: root/one/four
240 | 1: root/one
241 | 2: root/one/three
242 | 3: root/one
243 | 4: root/one
244 | 5: root/one
245 | 6: root
246 | 7: root/two
247 | 8: root
248 | 9: root/one
249 |
250 | Executing 37: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet delete root/two
251 | Current frame
252 | root/one/four
253 |
254 | Notes
255 | four:
256 |
257 | Executing 38: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet status
258 | Current frame
259 | root/one/four
260 |
261 | Notes
262 | four:
263 |
264 | Executing 39: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet history
265 | Frame history
266 | * 0: root/one/four
267 | 1: root/one
268 | 2: root/one/three
269 | 3: root/one
270 | 4: root/one
271 | 5: root/one
272 | 6: root
273 | 7: root/two
274 | 8: root
275 | 9: root/one
276 |
277 | Executing 40: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet top
278 | Current frame
279 | root
280 |
281 | Notes
282 | Root:
283 |
284 | Executing 41: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet status
285 | Current frame
286 | root
287 |
288 | Notes
289 | Root:
290 |
291 | Executing 42: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet history
292 | Frame history
293 | * 0: root
294 | 1: root/one/four
295 | 2: root/one
296 | 3: root/one/three
297 | 4: root/one
298 | 5: root/one
299 | 6: root/one
300 | 7: root
301 | 8: root/two
302 | 9: root
303 |
304 | Executing 43: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet push five --message=five: five
305 | Created new frame [root/five]
306 | Executing 44: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet up
307 | Current frame
308 | root
309 |
310 | Notes
311 | Root:
312 |
313 | Executing 45: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet push six --message=six: six
314 | Created new frame [root/six]
315 | Executing 46: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet up
316 | Current frame
317 | root
318 |
319 | Notes
320 | Root:
321 |
322 | Executing 47: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet push seven --message=seven: seven
323 | Created new frame [root/seven]
324 | Executing 48: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet up
325 | Current frame
326 | root
327 |
328 | Notes
329 | Root:
330 |
331 | Executing 49: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet push eight --message=eight: eight
332 | Created new frame [root/eight]
333 | Executing 50: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet up
334 | Current frame
335 | root
336 |
337 | Notes
338 | Root:
339 |
340 | Executing 51: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet push eighteen --message=eight: eight
341 | Created new frame [root/eighteen]
342 | Executing 52: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet up
343 | Current frame
344 | root
345 |
346 | Notes
347 | Root:
348 |
349 | Executing 53: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet push eighty --message=eight: eight
350 | Created new frame [root/eighty]
351 | Executing 54: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet up
352 | Current frame
353 | root
354 |
355 | Notes
356 | Root:
357 |
358 | Executing 55: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet down one
359 | Current frame
360 | root/one
361 |
362 | Notes
363 | One:
364 | \nMessage:
365 |
366 | Executing 56: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet push one --message=new one
367 | Created new frame [root/one/one]
368 | Executing 57: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet up
369 | Current frame
370 | root/one
371 |
372 | Notes
373 | One:
374 | \nMessage:
375 |
376 | Executing 58: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet push two --message=new one
377 | Created new frame [root/one/two]
378 | Executing 59: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet up
379 | Current frame
380 | root/one
381 |
382 | Notes
383 | One:
384 | \nMessage:
385 |
386 | Executing 60: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet push three --message=new one
387 | Created new frame [root/one/three]
388 | Executing 61: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet up
389 | Current frame
390 | root/one
391 |
392 | Notes
393 | One:
394 | \nMessage:
395 |
396 | Executing 62: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet push five --message=new one
397 | Created new frame [root/one/five]
398 | Executing 63: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet up
399 | Current frame
400 | root/one
401 |
402 | Notes
403 | One:
404 | \nMessage:
405 |
406 | Executing 64: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet push six --message=new one
407 | Created new frame [root/one/six]
408 | Executing 65: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet up
409 | Current frame
410 | root/one
411 |
412 | Notes
413 | One:
414 | \nMessage:
415 |
416 | Executing 66: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet push seven --message=new one
417 | Created new frame [root/one/seven]
418 | Executing 67: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet up
419 | Current frame
420 | root/one
421 |
422 | Notes
423 | One:
424 | \nMessage:
425 |
426 | Executing 68: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet push eight --message=new one
427 | Created new frame [root/one/eight]
428 | Executing 69: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet up
429 | Current frame
430 | root/one
431 |
432 | Notes
433 | One:
434 | \nMessage:
435 |
436 | Executing 70: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet push nine --message=new one
437 | Created new frame [root/one/nine]
438 | Executing 71: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet up
439 | Current frame
440 | root/one
441 |
442 | Notes
443 | One:
444 | \nMessage:
445 |
446 | Executing 72: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet push ten --message=new one
447 | Created new frame [root/one/ten]
448 | Executing 73: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet up
449 | Current frame
450 | root/one
451 |
452 | Notes
453 | One:
454 | \nMessage:
455 |
456 | Executing 74: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet status
457 | Current frame
458 | root/one
459 |
460 | Notes
461 | One:
462 | \nMessage:
463 |
464 | Executing 75: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet list
465 | root/one/eight
466 | root/one/five
467 | root/one/four
468 | root/one/nine
469 | root/one/one
470 | root/one/seven
471 | root/one/six
472 | root/one/ten
473 | root/one/three
474 | root/one/two
475 | Executing 76: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet match --from-root e
476 | root/eight
477 | root/eighteen
478 | root/eighty
479 | root/five
480 | root/one
481 | root/one/eight
482 | root/one/five
483 | root/one/four
484 | root/one/nine
485 | root/one/one
486 | root/one/seven
487 | root/one/six
488 | root/one/ten
489 | root/one/three
490 | root/one/two
491 | root/seven
492 | Executing 77: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet match --from-root ei
493 | root/eight
494 | root/eighteen
495 | root/eighty
496 | root/one/eight
497 | Executing 78: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet match --from-root eigh
498 | root/eight
499 | root/eighteen
500 | root/eighty
501 | root/one/eight
502 | Executing 79: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet match e
503 | root/one
504 | root/one/eight
505 | root/one/five
506 | root/one/four
507 | root/one/nine
508 | root/one/one
509 | root/one/seven
510 | root/one/six
511 | root/one/ten
512 | root/one/three
513 | root/one/two
514 | Executing 80: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet match ei
515 | root/one/eight
516 | Executing 81: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet match eigh
517 | root/one/eight
518 | Executing 82: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet match eigh --invert
519 | root/one
520 | root/one/five
521 | root/one/four
522 | root/one/nine
523 | root/one/one
524 | root/one/seven
525 | root/one/six
526 | root/one/ten
527 | root/one/three
528 | root/one/two
529 | Executing 83: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet status
530 | Current frame
531 | root/one
532 |
533 | Notes
534 | One:
535 | \nMessage:
536 |
537 | Executing 85: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet switch root/one/five
538 | Current frame
539 | root/one/five
540 |
541 | Notes
542 | new
543 |
544 | Executing 86: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet rename FIVE
545 | Current frame
546 | root/one/FIVE
547 |
548 | Notes
549 | new
550 |
551 | Executing 87: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet status
552 | Current frame
553 | root/one/FIVE
554 |
555 | Notes
556 | new
557 |
558 | Executing 88: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet match --from-root one/ei
559 | root/one/eight
560 | Executing 89: ./recent/bin/x86_64-linux-gnu/frame.elf --quiet tree
561 | root
562 | eighty
563 | eighteen
564 | five
565 | six
566 | eight
567 | seven
568 | one
569 | four
570 | FIVE
571 | two
572 | three
573 | six
574 | eight
575 | ten
576 | nine
577 | seven
578 | one
579 | Use [sed "s:::g"] to strip the dates
580 |
--------------------------------------------------------------------------------
/test.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # ########################################################################## #
4 | # Frame (©2023 Lelanthran Manickum) #
5 | # #
6 | # This program comes with ABSOLUTELY NO WARRANTY. This is free software #
7 | # and you are welcome to redistribute it under certain conditions; see #
8 | # the LICENSE file for details. #
9 | # ########################################################################## #
10 |
11 |
12 | export DBPATH=/tmp/frame/
13 | export PROG="./recent/bin/x86_64-linux-gnu/frame.elf --quiet"
14 |
15 | if [ ! -z "$VG" ]; then
16 | export VG="valgrind -q --leak-check=full --show-leak-kinds=all --error-exitcode=1"
17 | fi
18 |
19 | die () {
20 | echo testcommand failure: $@
21 | exit -1
22 | }
23 |
24 | export NC="\e[0m"
25 | export INV="\e[7m"
26 | export RED="\e[31m"
27 | export GREEN="\e[32m"
28 | export BLUE="\e[34m"
29 | export CYAN="\e[36m"
30 | export YELLOW="\e[33m"
31 | export HI_ON="${RED}${INV}"
32 |
33 | if [ ! -t 1 ]; then
34 | export NC=""
35 | export INV=""
36 | export RED=""
37 | export GREEN=""
38 | export BLUE=""
39 | export CYAN=""
40 | export YELLOW=""
41 | export HI_ON=""
42 | fi
43 |
44 | export STMT_NUM=0
45 |
46 | if [ -z "$DEBUG" ]; then
47 | export DEBUG=-1
48 | fi
49 |
50 | execute () {
51 | echo -ne "${RED}Executing${NC} ${GREEN}$STMT_NUM:${NC} "
52 | echo -e "${BLUE}$@${NC}"
53 | if [ ! -z "$VG" ]; then
54 | $VG $@ --dbpath=$DBPATH
55 | export RET=$?
56 | export STMT_NUM=$(($STMT_NUM + 1))
57 | return $RET
58 | fi
59 |
60 | if [ "$DEBUG" -eq "$STMT_NUM" ]; then
61 | gdb --args $@ --dbpath=$DBPATH
62 | export RET=$?
63 | export STMT_NUM=$(($STMT_NUM + 1))
64 | return $RET
65 | fi
66 |
67 | export STMT_NUM=$(($STMT_NUM + 1))
68 | $@ --dbpath=$DBPATH
69 | }
70 |
71 | die () {
72 | echo testcommand failure: $@
73 | execute $PROG status
74 | exit -1
75 | }
76 |
77 | rm -rf $DBPATH
78 | execute $PROG create || die failed to create
79 | execute $PROG status || die failed status
80 | execute $PROG history || die failed history
81 |
82 | execute $PROG replace --message="Root: replacement" || die failed replace
83 | execute $PROG status || die failed status
84 | execute $PROG history || die failed history
85 |
86 | execute $PROG push one --message="One: message one" || die failed push
87 | execute $PROG status || die failed status
88 | execute $PROG history || die failed history
89 |
90 | execute $PROG append --message="\nMessage: 1\n" || die failed append
91 | execute $PROG status || die failed status
92 | execute $PROG history || die failed history
93 |
94 | execute $PROG up || die failed uptree
95 | execute $PROG status || die failed status
96 | execute $PROG history || die failed history
97 |
98 | execute $PROG push two --message="Two: message two" || die failed push
99 | execute $PROG status || die failed status
100 | execute $PROG history || die failed history
101 |
102 | execute $PROG top || die failed top
103 | execute $PROG down one || die failed down one
104 | execute $PROG status || die failed status
105 | execute $PROG history || die failed history
106 |
107 | execute $PROG back 1 || die failed back
108 | execute $PROG status || die failed status
109 | execute $PROG history || die failed history
110 |
111 | execute $PROG back 1 || die failed back
112 | execute $PROG status || die failed status
113 | execute $PROG history || die failed history
114 |
115 | execute $PROG push three --message="three: three" || die failed push
116 | execute $PROG status || die failed status
117 | execute $PROG history || die failed history
118 |
119 | execute $PROG pop || die failed pop
120 | execute $PROG status || die failed status
121 | execute $PROG history || die failed history
122 |
123 | execute $PROG push four --message="four: four" || die failed push
124 | execute $PROG status || die failed status
125 | execute $PROG history || die failed history
126 |
127 | execute $PROG delete root/two || die failed delete
128 | execute $PROG status || die failed status
129 | execute $PROG history || die failed history
130 |
131 | execute $PROG top || die failed top
132 | execute $PROG status || die failed status
133 | execute $PROG history || die failed history
134 |
135 |
136 | execute $PROG push five --message="five: five" || die failed push
137 | execute $PROG up || die failed up
138 | execute $PROG push six --message="six: six" || die failed push
139 | execute $PROG up || die failed up
140 | execute $PROG push seven --message="seven: seven" || die failed push
141 | execute $PROG up || die failed up
142 | execute $PROG push eight --message="eight: eight" || die failed push
143 | execute $PROG up || die failed up
144 | execute $PROG push eighteen --message="eight: eight" || die failed push
145 | execute $PROG up || die failed up
146 | execute $PROG push eighty --message="eight: eight" || die failed push
147 | execute $PROG up || die failed up
148 | execute $PROG down one || die failed down
149 | execute $PROG push one --message="new one" || die failed push
150 | execute $PROG up || die failed up
151 | execute $PROG push two --message="new one" || die failed push
152 | execute $PROG up || die failed up
153 | execute $PROG push three --message="new one" || die failed push
154 | execute $PROG up || die failed up
155 | # execute $PROG push four --message="new one" || die failed push
156 | # execute $PROG up || die failed up
157 | execute $PROG push five --message="new one" || die failed push
158 | execute $PROG up || die failed up
159 | execute $PROG push six --message="new one" || die failed push
160 | execute $PROG up || die failed up
161 | execute $PROG push seven --message="new one" || die failed push
162 | execute $PROG up || die failed up
163 | execute $PROG push eight --message="new one" || die failed push
164 | execute $PROG up || die failed up
165 | execute $PROG push nine --message="new one" || die failed push
166 | execute $PROG up || die failed up
167 | execute $PROG push ten --message="new one" || die failed push
168 | execute $PROG up || die failed up
169 | execute $PROG status || die failed status
170 | execute $PROG list || die failed list
171 |
172 | # Current node is root/one
173 | execute $PROG match --from-root "e" || die failed match
174 | execute $PROG match --from-root "ei" || die failed match
175 | execute $PROG match --from-root "eigh" || die failed match
176 |
177 | execute $PROG match "e" || die failed match
178 | execute $PROG match "ei" || die failed match
179 | execute $PROG match "eigh" || die failed match
180 | execute $PROG match "eigh" --invert || die failed match
181 | execute $PROG status || die failed status
182 | execute $PROG match "one/egh" > t
183 | if [ `wc -l t | cut -f 1 -d \ ` -ne 1 ]; then
184 | die expected failed match
185 | fi
186 |
187 | # Rename root/one/five
188 | execute $PROG switch root/one/five
189 | execute $PROG rename 'FIVE' || die failed rename
190 | # Current node is root/one/FIVE
191 | execute $PROG status || die failed status
192 |
193 | execute $PROG match --from-root "one/ei" || die failed match
194 |
195 | execute $PROG tree || die failed tree
196 |
197 | echo 'Use [sed "s:(.\+)::g"] to strip the dates'
198 |
--------------------------------------------------------------------------------