commit 3f2a1b22e59fce24b2a8e19e4eddee9c3def910e
Author: awy <awy@awy.one>
Date: Tue, 16 Dec 2025 22:00:44 +0300
init
Diffstat:
| A | .gitignore | | | 5 | +++++ |
| A | LICENSE | | | 674 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | Makefile | | | 50 | ++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | lib/cjson/cJSON.c | | | 2980 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | lib/cjson/cJSON.h | | | 396 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | lib/util.c | | | 144 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | lib/util.h | | | 31 | +++++++++++++++++++++++++++++++ |
| A | src/main.c | | | 56 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | src/stavg.c | | | 40 | ++++++++++++++++++++++++++++++++++++++++ |
| A | src/stbar.h | | | 32 | ++++++++++++++++++++++++++++++++ |
| A | src/stclock.c | | | 76 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | src/stmail.c | | | 134 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | src/stmemory.c | | | 81 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | src/stmpdup.c | | | 79 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | src/stmusic.c | | | 169 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | src/sttorrent.c | | | 234 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | src/stweath.c | | | 201 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
17 files changed, 5382 insertions(+), 0 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1,5 @@
+st*
+!*.c
+!*.h
+*.out
+*.o
diff --git a/LICENSE b/LICENSE
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<https://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<https://www.gnu.org/licenses/why-not-lgpl.html>.
diff --git a/Makefile b/Makefile
@@ -0,0 +1,50 @@
+CC := gcc
+CFLAGS := -O3 -march=native -pipe -Wall -Wextra -std=c99
+
+SRCDIR := src
+LIBDIR := lib
+BINDIR := $(HOME)/.local/bin/statusbar
+
+TARGET := $(BINDIR)/stbar
+
+SRCS := \
+ $(wildcard $(SRCDIR)/*.c) \
+ $(LIBDIR)/util.c \
+ $(LIBDIR)/cjson/cJSON.c
+
+NOTIFY_CFLAGS := $(shell pkg-config --cflags libnotify)
+NOTIFY_LIBS := $(shell pkg-config --libs libnotify)
+
+LIBS := \
+ $(NOTIFY_LIBS) \
+ -lcurl \
+ -lmpdclient
+
+# polymorphic names
+LINKS := \
+ stavg \
+ stclock \
+ stmail \
+ stmemory \
+ stmpdup \
+ stmusic \
+ sttorrent \
+ stweath
+
+.PHONY: all clean install-links
+
+all: $(TARGET) install-links
+
+$(BINDIR):
+ mkdir -p $(BINDIR)
+
+$(TARGET): $(SRCS) | $(BINDIR)
+ $(CC) $(CFLAGS) $(NOTIFY_CFLAGS) -o $@ $(SRCS) $(LIBS)
+
+install-links:
+ @for name in $(LINKS); do \
+ ln -sf stbar $(BINDIR)/$$name; \
+ done
+
+clean:
+ rm -f $(TARGET) $(addprefix $(BINDIR)/,$(LINKS))
diff --git a/lib/cjson/cJSON.c b/lib/cjson/cJSON.c
@@ -0,0 +1,2980 @@
+/*
+ Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+
+/* cJSON */
+/* JSON parser in C. */
+
+/* disable warnings about old C89 functions in MSVC */
+#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER)
+#define _CRT_SECURE_NO_DEPRECATE
+#endif
+
+#ifdef __GNUC__
+#pragma GCC visibility push(default)
+#endif
+#if defined(_MSC_VER)
+#pragma warning(push)
+/* disable warning about single line comments in system headers */
+#pragma warning(disable : 4001)
+#endif
+
+#include <ctype.h>
+#include <float.h>
+#include <limits.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef ENABLE_LOCALES
+#include <locale.h>
+#endif
+
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+#ifdef __GNUC__
+#pragma GCC visibility pop
+#endif
+
+#include "cJSON.h"
+
+/* define our own boolean type */
+#ifdef true
+#undef true
+#endif
+#define true ((cJSON_bool)1)
+
+#ifdef false
+#undef false
+#endif
+#define false ((cJSON_bool)0)
+
+/* define isnan and isinf for ANSI C, if in C99 or above, isnan and isinf has
+ * been defined in math.h */
+#ifndef isinf
+#define isinf(d) (isnan((d - d)) && !isnan(d))
+#endif
+#ifndef isnan
+#define isnan(d) (d != d)
+#endif
+
+#ifndef NAN
+#ifdef _WIN32
+#define NAN sqrt(-1.0)
+#else
+#define NAN 0.0 / 0.0
+#endif
+#endif
+
+typedef struct {
+ const unsigned char *json;
+ size_t position;
+} error;
+static error global_error = {NULL, 0};
+
+CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void)
+{
+ return (const char *)(global_error.json + global_error.position);
+}
+
+CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON *const item)
+{
+ if (!cJSON_IsString(item)) {
+ return NULL;
+ }
+
+ return item->valuestring;
+}
+
+CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON *const item)
+{
+ if (!cJSON_IsNumber(item)) {
+ return (double)NAN;
+ }
+
+ return item->valuedouble;
+}
+
+/* This is a safeguard to prevent copy-pasters from using incompatible C and
+ * header files */
+#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || \
+ (CJSON_VERSION_PATCH != 18)
+#error cJSON.h and cJSON.c have different versions. Make sure that both have the same.
+#endif
+
+CJSON_PUBLIC(const char *) cJSON_Version(void)
+{
+ static char version[15];
+ sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR,
+ CJSON_VERSION_PATCH);
+
+ return version;
+}
+
+/* Case insensitive string comparison, doesn't consider two NULL pointers equal
+ * though */
+static int case_insensitive_strcmp(const unsigned char *string1,
+ const unsigned char *string2)
+{
+ if ((string1 == NULL) || (string2 == NULL)) {
+ return 1;
+ }
+
+ if (string1 == string2) {
+ return 0;
+ }
+
+ for (; tolower(*string1) == tolower(*string2); (void)string1++, string2++) {
+ if (*string1 == '\0') {
+ return 0;
+ }
+ }
+
+ return tolower(*string1) - tolower(*string2);
+}
+
+typedef struct internal_hooks {
+ void *(CJSON_CDECL *allocate)(size_t size);
+ void(CJSON_CDECL *deallocate)(void *pointer);
+ void *(CJSON_CDECL *reallocate)(void *pointer, size_t size);
+} internal_hooks;
+
+#if defined(_MSC_VER)
+/* work around MSVC error C2322: '...' address of dllimport '...' is not static
+ */
+static void *CJSON_CDECL internal_malloc(size_t size) { return malloc(size); }
+static void CJSON_CDECL internal_free(void *pointer) { free(pointer); }
+static void *CJSON_CDECL internal_realloc(void *pointer, size_t size)
+{
+ return realloc(pointer, size);
+}
+#else
+#define internal_malloc malloc
+#define internal_free free
+#define internal_realloc realloc
+#endif
+
+/* strlen of character literals resolved at compile time */
+#define static_strlen(string_literal) (sizeof(string_literal) - sizeof(""))
+
+static internal_hooks global_hooks = {internal_malloc, internal_free,
+ internal_realloc};
+
+static unsigned char *cJSON_strdup(const unsigned char *string,
+ const internal_hooks *const hooks)
+{
+ size_t length = 0;
+ unsigned char *copy = NULL;
+
+ if (string == NULL) {
+ return NULL;
+ }
+
+ length = strlen((const char *)string) + sizeof("");
+ copy = (unsigned char *)hooks->allocate(length);
+ if (copy == NULL) {
+ return NULL;
+ }
+ memcpy(copy, string, length);
+
+ return copy;
+}
+
+CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks *hooks)
+{
+ if (hooks == NULL) {
+ /* Reset hooks */
+ global_hooks.allocate = malloc;
+ global_hooks.deallocate = free;
+ global_hooks.reallocate = realloc;
+ return;
+ }
+
+ global_hooks.allocate = malloc;
+ if (hooks->malloc_fn != NULL) {
+ global_hooks.allocate = hooks->malloc_fn;
+ }
+
+ global_hooks.deallocate = free;
+ if (hooks->free_fn != NULL) {
+ global_hooks.deallocate = hooks->free_fn;
+ }
+
+ /* use realloc only if both free and malloc are used */
+ global_hooks.reallocate = NULL;
+ if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free)) {
+ global_hooks.reallocate = realloc;
+ }
+}
+
+/* Internal constructor. */
+static cJSON *cJSON_New_Item(const internal_hooks *const hooks)
+{
+ cJSON *node = (cJSON *)hooks->allocate(sizeof(cJSON));
+ if (node) {
+ memset(node, '\0', sizeof(cJSON));
+ }
+
+ return node;
+}
+
+/* Delete a cJSON structure. */
+CJSON_PUBLIC(void) cJSON_Delete(cJSON *item)
+{
+ cJSON *next = NULL;
+ while (item != NULL) {
+ next = item->next;
+ if (!(item->type & cJSON_IsReference) && (item->child != NULL)) {
+ cJSON_Delete(item->child);
+ }
+ if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) {
+ global_hooks.deallocate(item->valuestring);
+ item->valuestring = NULL;
+ }
+ if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) {
+ global_hooks.deallocate(item->string);
+ item->string = NULL;
+ }
+ global_hooks.deallocate(item);
+ item = next;
+ }
+}
+
+/* get the decimal point character of the current locale */
+static unsigned char get_decimal_point(void)
+{
+#ifdef ENABLE_LOCALES
+ struct lconv *lconv = localeconv();
+ return (unsigned char)lconv->decimal_point[0];
+#else
+ return '.';
+#endif
+}
+
+typedef struct {
+ const unsigned char *content;
+ size_t length;
+ size_t offset;
+ size_t depth; /* How deeply nested (in arrays/objects) is the input at
+ the current offset. */
+ internal_hooks hooks;
+} parse_buffer;
+
+/* check if the given size is left to read in a given parse buffer (starting
+ * with 1) */
+#define can_read(buffer, size) \
+ ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length))
+/* check if the buffer can be accessed at the given index (starting with 0) */
+#define can_access_at_index(buffer, index) \
+ ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length))
+#define cannot_access_at_index(buffer, index) \
+ (!can_access_at_index(buffer, index))
+/* get a pointer to the buffer at the position */
+#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset)
+
+/* Parse the input text to generate a number, and populate the result into item.
+ */
+static cJSON_bool parse_number(cJSON *const item,
+ parse_buffer *const input_buffer)
+{
+ double number = 0;
+ unsigned char *after_end = NULL;
+ unsigned char *number_c_string;
+ unsigned char decimal_point = get_decimal_point();
+ size_t i = 0;
+ size_t number_string_length = 0;
+ cJSON_bool has_decimal_point = false;
+
+ if ((input_buffer == NULL) || (input_buffer->content == NULL)) {
+ return false;
+ }
+
+ /* copy the number into a temporary buffer and replace '.' with the
+ * decimal point of the current locale (for strtod) This also takes care
+ * of '\0' not necessarily being available for marking the end of the
+ * input */
+ for (i = 0; can_access_at_index(input_buffer, i); i++) {
+ switch (buffer_at_offset(input_buffer)[i]) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case '+':
+ case '-':
+ case 'e':
+ case 'E':
+ number_string_length++;
+ break;
+
+ case '.':
+ number_string_length++;
+ has_decimal_point = true;
+ break;
+
+ default:
+ goto loop_end;
+ }
+ }
+loop_end:
+ /* malloc for temporary buffer, add 1 for '\0' */
+ number_c_string =
+ (unsigned char *)input_buffer->hooks.allocate(number_string_length + 1);
+ if (number_c_string == NULL) {
+ return false; /* allocation failure */
+ }
+
+ memcpy(number_c_string, buffer_at_offset(input_buffer), number_string_length);
+ number_c_string[number_string_length] = '\0';
+
+ if (has_decimal_point) {
+ for (i = 0; i < number_string_length; i++) {
+ if (number_c_string[i] == '.') {
+ /* replace '.' with the decimal point of the
+ * current locale (for strtod) */
+ number_c_string[i] = decimal_point;
+ }
+ }
+ }
+
+ number = strtod((const char *)number_c_string, (char **)&after_end);
+ if (number_c_string == after_end) {
+ /* free the temporary buffer */
+ input_buffer->hooks.deallocate(number_c_string);
+ return false; /* parse_error */
+ }
+
+ item->valuedouble = number;
+
+ /* use saturation in case of overflow */
+ if (number >= INT_MAX) {
+ item->valueint = INT_MAX;
+ } else if (number <= (double)INT_MIN) {
+ item->valueint = INT_MIN;
+ } else {
+ item->valueint = (int)number;
+ }
+
+ item->type = cJSON_Number;
+
+ input_buffer->offset += (size_t)(after_end - number_c_string);
+ /* free the temporary buffer */
+ input_buffer->hooks.deallocate(number_c_string);
+ return true;
+}
+
+/* don't ask me, but the original cJSON_SetNumberValue returns an integer or
+ * double */
+CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number)
+{
+ if (number >= INT_MAX) {
+ object->valueint = INT_MAX;
+ } else if (number <= (double)INT_MIN) {
+ object->valueint = INT_MIN;
+ } else {
+ object->valueint = (int)number;
+ }
+
+ return object->valuedouble = number;
+}
+
+/* Note: when passing a NULL valuestring, cJSON_SetValuestring treats this as an
+ * error and return NULL */
+CJSON_PUBLIC(char *)
+cJSON_SetValuestring(cJSON *object, const char *valuestring)
+{
+ char *copy = NULL;
+ size_t v1_len;
+ size_t v2_len;
+ /* if object's type is not cJSON_String or is cJSON_IsReference, it
+ * should not set valuestring */
+ if ((object == NULL) || !(object->type & cJSON_String) ||
+ (object->type & cJSON_IsReference)) {
+ return NULL;
+ }
+ /* return NULL if the object is corrupted or valuestring is NULL */
+ if (object->valuestring == NULL || valuestring == NULL) {
+ return NULL;
+ }
+
+ v1_len = strlen(valuestring);
+ v2_len = strlen(object->valuestring);
+
+ if (v1_len <= v2_len) {
+ /* strcpy does not handle overlapping string: [X1, X2] [Y1, Y2]
+ * => X2 < Y1 or Y2 < X1 */
+ if (!(valuestring + v1_len < object->valuestring ||
+ object->valuestring + v2_len < valuestring)) {
+ return NULL;
+ }
+ strcpy(object->valuestring, valuestring);
+ return object->valuestring;
+ }
+ copy =
+ (char *)cJSON_strdup((const unsigned char *)valuestring, &global_hooks);
+ if (copy == NULL) {
+ return NULL;
+ }
+ if (object->valuestring != NULL) {
+ cJSON_free(object->valuestring);
+ }
+ object->valuestring = copy;
+
+ return copy;
+}
+
+typedef struct {
+ unsigned char *buffer;
+ size_t length;
+ size_t offset;
+ size_t depth; /* current nesting depth (for formatted printing) */
+ cJSON_bool noalloc;
+ cJSON_bool format; /* is this print a formatted print */
+ internal_hooks hooks;
+} printbuffer;
+
+/* realloc printbuffer if necessary to have at least "needed" bytes more */
+static unsigned char *ensure(printbuffer *const p, size_t needed)
+{
+ unsigned char *newbuffer = NULL;
+ size_t newsize = 0;
+
+ if ((p == NULL) || (p->buffer == NULL)) {
+ return NULL;
+ }
+
+ if ((p->length > 0) && (p->offset >= p->length)) {
+ /* make sure that offset is valid */
+ return NULL;
+ }
+
+ if (needed > INT_MAX) {
+ /* sizes bigger than INT_MAX are currently not supported */
+ return NULL;
+ }
+
+ needed += p->offset + 1;
+ if (needed <= p->length) {
+ return p->buffer + p->offset;
+ }
+
+ if (p->noalloc) {
+ return NULL;
+ }
+
+ /* calculate new buffer size */
+ if (needed > (INT_MAX / 2)) {
+ /* overflow of int, use INT_MAX if possible */
+ if (needed <= INT_MAX) {
+ newsize = INT_MAX;
+ } else {
+ return NULL;
+ }
+ } else {
+ newsize = needed * 2;
+ }
+
+ if (p->hooks.reallocate != NULL) {
+ /* reallocate with realloc if available */
+ newbuffer = (unsigned char *)p->hooks.reallocate(p->buffer, newsize);
+ if (newbuffer == NULL) {
+ p->hooks.deallocate(p->buffer);
+ p->length = 0;
+ p->buffer = NULL;
+
+ return NULL;
+ }
+ } else {
+ /* otherwise reallocate manually */
+ newbuffer = (unsigned char *)p->hooks.allocate(newsize);
+ if (!newbuffer) {
+ p->hooks.deallocate(p->buffer);
+ p->length = 0;
+ p->buffer = NULL;
+
+ return NULL;
+ }
+
+ memcpy(newbuffer, p->buffer, p->offset + 1);
+ p->hooks.deallocate(p->buffer);
+ }
+ p->length = newsize;
+ p->buffer = newbuffer;
+
+ return newbuffer + p->offset;
+}
+
+/* calculate the new length of the string in a printbuffer and update the offset
+ */
+static void update_offset(printbuffer *const buffer)
+{
+ const unsigned char *buffer_pointer = NULL;
+ if ((buffer == NULL) || (buffer->buffer == NULL)) {
+ return;
+ }
+ buffer_pointer = buffer->buffer + buffer->offset;
+
+ buffer->offset += strlen((const char *)buffer_pointer);
+}
+
+/* securely comparison of floating-point variables */
+static cJSON_bool compare_double(double a, double b)
+{
+ double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b);
+ return (fabs(a - b) <= maxVal * DBL_EPSILON);
+}
+
+/* Render the number nicely from the given item into a string. */
+static cJSON_bool print_number(const cJSON *const item,
+ printbuffer *const output_buffer)
+{
+ unsigned char *output_pointer = NULL;
+ double d = item->valuedouble;
+ int length = 0;
+ size_t i = 0;
+ unsigned char number_buffer[26] = {
+ 0}; /* temporary buffer to print the number into */
+ unsigned char decimal_point = get_decimal_point();
+ double test = 0.0;
+
+ if (output_buffer == NULL) {
+ return false;
+ }
+
+ /* This checks for NaN and Infinity */
+ if (isnan(d) || isinf(d)) {
+ length = sprintf((char *)number_buffer, "null");
+ } else if (d == (double)item->valueint) {
+ length = sprintf((char *)number_buffer, "%d", item->valueint);
+ } else {
+ /* Try 15 decimal places of precision to avoid nonsignificant
+ * nonzero digits */
+ length = sprintf((char *)number_buffer, "%1.15g", d);
+
+ /* Check whether the original double can be recovered */
+ if ((sscanf((char *)number_buffer, "%lg", &test) != 1) ||
+ !compare_double((double)test, d)) {
+ /* If not, print with 17 decimal places of precision */
+ length = sprintf((char *)number_buffer, "%1.17g", d);
+ }
+ }
+
+ /* sprintf failed or buffer overrun occurred */
+ if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) {
+ return false;
+ }
+
+ /* reserve appropriate space in the output */
+ output_pointer = ensure(output_buffer, (size_t)length + sizeof(""));
+ if (output_pointer == NULL) {
+ return false;
+ }
+
+ /* copy the printed number to the output and replace locale
+ * dependent decimal point with '.' */
+ for (i = 0; i < ((size_t)length); i++) {
+ if (number_buffer[i] == decimal_point) {
+ output_pointer[i] = '.';
+ continue;
+ }
+
+ output_pointer[i] = number_buffer[i];
+ }
+ output_pointer[i] = '\0';
+
+ output_buffer->offset += (size_t)length;
+
+ return true;
+}
+
+/* parse 4 digit hexadecimal number */
+static unsigned parse_hex4(const unsigned char *const input)
+{
+ unsigned int h = 0;
+ size_t i = 0;
+
+ for (i = 0; i < 4; i++) {
+ /* parse digit */
+ if ((input[i] >= '0') && (input[i] <= '9')) {
+ h += (unsigned int)input[i] - '0';
+ } else if ((input[i] >= 'A') && (input[i] <= 'F')) {
+ h += (unsigned int)10 + input[i] - 'A';
+ } else if ((input[i] >= 'a') && (input[i] <= 'f')) {
+ h += (unsigned int)10 + input[i] - 'a';
+ } else /* invalid */
+ {
+ return 0;
+ }
+
+ if (i < 3) {
+ /* shift left to make place for the next nibble */
+ h = h << 4;
+ }
+ }
+
+ return h;
+}
+
+/* converts a UTF-16 literal to UTF-8
+ * A literal can be one or two sequences of the form \uXXXX */
+static unsigned char
+utf16_literal_to_utf8(const unsigned char *const input_pointer,
+ const unsigned char *const input_end,
+ unsigned char **output_pointer)
+{
+ long unsigned int codepoint = 0;
+ unsigned int first_code = 0;
+ const unsigned char *first_sequence = input_pointer;
+ unsigned char utf8_length = 0;
+ unsigned char utf8_position = 0;
+ unsigned char sequence_length = 0;
+ unsigned char first_byte_mark = 0;
+
+ if ((input_end - first_sequence) < 6) {
+ /* input ends unexpectedly */
+ goto fail;
+ }
+
+ /* get the first utf16 sequence */
+ first_code = parse_hex4(first_sequence + 2);
+
+ /* check that the code is valid */
+ if (((first_code >= 0xDC00) && (first_code <= 0xDFFF))) {
+ goto fail;
+ }
+
+ /* UTF16 surrogate pair */
+ if ((first_code >= 0xD800) && (first_code <= 0xDBFF)) {
+ const unsigned char *second_sequence = first_sequence + 6;
+ unsigned int second_code = 0;
+ sequence_length = 12; /* \uXXXX\uXXXX */
+
+ if ((input_end - second_sequence) < 6) {
+ /* input ends unexpectedly */
+ goto fail;
+ }
+
+ if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u')) {
+ /* missing second half of the surrogate pair */
+ goto fail;
+ }
+
+ /* get the second utf16 sequence */
+ second_code = parse_hex4(second_sequence + 2);
+ /* check that the code is valid */
+ if ((second_code < 0xDC00) || (second_code > 0xDFFF)) {
+ /* invalid second half of the surrogate pair */
+ goto fail;
+ }
+
+ /* calculate the unicode codepoint from the surrogate pair */
+ codepoint =
+ 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF));
+ } else {
+ sequence_length = 6; /* \uXXXX */
+ codepoint = first_code;
+ }
+
+ /* encode as UTF-8
+ * takes at maximum 4 bytes to encode:
+ * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
+ if (codepoint < 0x80) {
+ /* normal ascii, encoding 0xxxxxxx */
+ utf8_length = 1;
+ } else if (codepoint < 0x800) {
+ /* two bytes, encoding 110xxxxx 10xxxxxx */
+ utf8_length = 2;
+ first_byte_mark = 0xC0; /* 11000000 */
+ } else if (codepoint < 0x10000) {
+ /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */
+ utf8_length = 3;
+ first_byte_mark = 0xE0; /* 11100000 */
+ } else if (codepoint <= 0x10FFFF) {
+ /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */
+ utf8_length = 4;
+ first_byte_mark = 0xF0; /* 11110000 */
+ } else {
+ /* invalid unicode codepoint */
+ goto fail;
+ }
+
+ /* encode as utf8 */
+ for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0;
+ utf8_position--) {
+ /* 10xxxxxx */
+ (*output_pointer)[utf8_position] =
+ (unsigned char)((codepoint | 0x80) & 0xBF);
+ codepoint >>= 6;
+ }
+ /* encode first byte */
+ if (utf8_length > 1) {
+ (*output_pointer)[0] =
+ (unsigned char)((codepoint | first_byte_mark) & 0xFF);
+ } else {
+ (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F);
+ }
+
+ *output_pointer += utf8_length;
+
+ return sequence_length;
+
+fail:
+ return 0;
+}
+
+/* Parse the input text into an unescaped cinput, and populate item. */
+static cJSON_bool parse_string(cJSON *const item,
+ parse_buffer *const input_buffer)
+{
+ const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1;
+ const unsigned char *input_end = buffer_at_offset(input_buffer) + 1;
+ unsigned char *output_pointer = NULL;
+ unsigned char *output = NULL;
+
+ /* not a string */
+ if (buffer_at_offset(input_buffer)[0] != '\"') {
+ goto fail;
+ }
+
+ {
+ /* calculate approximate size of the output (overestimate) */
+ size_t allocation_length = 0;
+ size_t skipped_bytes = 0;
+ while (
+ ((size_t)(input_end - input_buffer->content) < input_buffer->length) &&
+ (*input_end != '\"')) {
+ /* is escape sequence */
+ if (input_end[0] == '\\') {
+ if ((size_t)(input_end + 1 - input_buffer->content) >=
+ input_buffer->length) {
+ /* prevent buffer overflow when last
+ * input character is a backslash */
+ goto fail;
+ }
+ skipped_bytes++;
+ input_end++;
+ }
+ input_end++;
+ }
+ if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) ||
+ (*input_end != '\"')) {
+ goto fail; /* string ended unexpectedly */
+ }
+
+ /* This is at most how much we need for the output */
+ allocation_length =
+ (size_t)(input_end - buffer_at_offset(input_buffer)) - skipped_bytes;
+ output = (unsigned char *)input_buffer->hooks.allocate(allocation_length +
+ sizeof(""));
+ if (output == NULL) {
+ goto fail; /* allocation failure */
+ }
+ }
+
+ output_pointer = output;
+ /* loop through the string literal */
+ while (input_pointer < input_end) {
+ if (*input_pointer != '\\') {
+ *output_pointer++ = *input_pointer++;
+ }
+ /* escape sequence */
+ else {
+ unsigned char sequence_length = 2;
+ if ((input_end - input_pointer) < 1) {
+ goto fail;
+ }
+
+ switch (input_pointer[1]) {
+ case 'b':
+ *output_pointer++ = '\b';
+ break;
+ case 'f':
+ *output_pointer++ = '\f';
+ break;
+ case 'n':
+ *output_pointer++ = '\n';
+ break;
+ case 'r':
+ *output_pointer++ = '\r';
+ break;
+ case 't':
+ *output_pointer++ = '\t';
+ break;
+ case '\"':
+ case '\\':
+ case '/':
+ *output_pointer++ = input_pointer[1];
+ break;
+
+ /* UTF-16 literal */
+ case 'u':
+ sequence_length =
+ utf16_literal_to_utf8(input_pointer, input_end, &output_pointer);
+ if (sequence_length == 0) {
+ /* failed to convert UTF16-literal to
+ * UTF-8 */
+ goto fail;
+ }
+ break;
+
+ default:
+ goto fail;
+ }
+ input_pointer += sequence_length;
+ }
+ }
+
+ /* zero terminate the output */
+ *output_pointer = '\0';
+
+ item->type = cJSON_String;
+ item->valuestring = (char *)output;
+
+ input_buffer->offset = (size_t)(input_end - input_buffer->content);
+ input_buffer->offset++;
+
+ return true;
+
+fail:
+ if (output != NULL) {
+ input_buffer->hooks.deallocate(output);
+ output = NULL;
+ }
+
+ if (input_pointer != NULL) {
+ input_buffer->offset = (size_t)(input_pointer - input_buffer->content);
+ }
+
+ return false;
+}
+
+/* Render the cstring provided to an escaped version that can be printed. */
+static cJSON_bool print_string_ptr(const unsigned char *const input,
+ printbuffer *const output_buffer)
+{
+ const unsigned char *input_pointer = NULL;
+ unsigned char *output = NULL;
+ unsigned char *output_pointer = NULL;
+ size_t output_length = 0;
+ /* numbers of additional characters needed for escaping */
+ size_t escape_characters = 0;
+
+ if (output_buffer == NULL) {
+ return false;
+ }
+
+ /* empty string */
+ if (input == NULL) {
+ output = ensure(output_buffer, sizeof("\"\""));
+ if (output == NULL) {
+ return false;
+ }
+ strcpy((char *)output, "\"\"");
+
+ return true;
+ }
+
+ /* set "flag" to 1 if something needs to be escaped */
+ for (input_pointer = input; *input_pointer; input_pointer++) {
+ switch (*input_pointer) {
+ case '\"':
+ case '\\':
+ case '\b':
+ case '\f':
+ case '\n':
+ case '\r':
+ case '\t':
+ /* one character escape sequence */
+ escape_characters++;
+ break;
+ default:
+ if (*input_pointer < 32) {
+ /* UTF-16 escape sequence uXXXX */
+ escape_characters += 5;
+ }
+ break;
+ }
+ }
+ output_length = (size_t)(input_pointer - input) + escape_characters;
+
+ output = ensure(output_buffer, output_length + sizeof("\"\""));
+ if (output == NULL) {
+ return false;
+ }
+
+ /* no characters have to be escaped */
+ if (escape_characters == 0) {
+ output[0] = '\"';
+ memcpy(output + 1, input, output_length);
+ output[output_length + 1] = '\"';
+ output[output_length + 2] = '\0';
+
+ return true;
+ }
+
+ output[0] = '\"';
+ output_pointer = output + 1;
+ /* copy the string */
+ for (input_pointer = input; *input_pointer != '\0';
+ (void)input_pointer++, output_pointer++) {
+ if ((*input_pointer > 31) && (*input_pointer != '\"') &&
+ (*input_pointer != '\\')) {
+ /* normal character, copy */
+ *output_pointer = *input_pointer;
+ } else {
+ /* character needs to be escaped */
+ *output_pointer++ = '\\';
+ switch (*input_pointer) {
+ case '\\':
+ *output_pointer = '\\';
+ break;
+ case '\"':
+ *output_pointer = '\"';
+ break;
+ case '\b':
+ *output_pointer = 'b';
+ break;
+ case '\f':
+ *output_pointer = 'f';
+ break;
+ case '\n':
+ *output_pointer = 'n';
+ break;
+ case '\r':
+ *output_pointer = 'r';
+ break;
+ case '\t':
+ *output_pointer = 't';
+ break;
+ default:
+ /* escape and print as unicode codepoint */
+ sprintf((char *)output_pointer, "u%04x", *input_pointer);
+ output_pointer += 4;
+ break;
+ }
+ }
+ }
+ output[output_length + 1] = '\"';
+ output[output_length + 2] = '\0';
+
+ return true;
+}
+
+/* Invoke print_string_ptr (which is useful) on an item. */
+static cJSON_bool print_string(const cJSON *const item, printbuffer *const p)
+{
+ return print_string_ptr((unsigned char *)item->valuestring, p);
+}
+
+/* Predeclare these prototypes. */
+static cJSON_bool parse_value(cJSON *const item,
+ parse_buffer *const input_buffer);
+static cJSON_bool print_value(const cJSON *const item,
+ printbuffer *const output_buffer);
+static cJSON_bool parse_array(cJSON *const item,
+ parse_buffer *const input_buffer);
+static cJSON_bool print_array(const cJSON *const item,
+ printbuffer *const output_buffer);
+static cJSON_bool parse_object(cJSON *const item,
+ parse_buffer *const input_buffer);
+static cJSON_bool print_object(const cJSON *const item,
+ printbuffer *const output_buffer);
+
+/* Utility to jump whitespace and cr/lf */
+static parse_buffer *buffer_skip_whitespace(parse_buffer *const buffer)
+{
+ if ((buffer == NULL) || (buffer->content == NULL)) {
+ return NULL;
+ }
+
+ if (cannot_access_at_index(buffer, 0)) {
+ return buffer;
+ }
+
+ while (can_access_at_index(buffer, 0) &&
+ (buffer_at_offset(buffer)[0] <= 32)) {
+ buffer->offset++;
+ }
+
+ if (buffer->offset == buffer->length) {
+ buffer->offset--;
+ }
+
+ return buffer;
+}
+
+/* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */
+static parse_buffer *skip_utf8_bom(parse_buffer *const buffer)
+{
+ if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0)) {
+ return NULL;
+ }
+
+ if (can_access_at_index(buffer, 4) &&
+ (strncmp((const char *)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) ==
+ 0)) {
+ buffer->offset += 3;
+ }
+
+ return buffer;
+}
+
+CJSON_PUBLIC(cJSON *)
+cJSON_ParseWithOpts(const char *value, const char **return_parse_end,
+ cJSON_bool require_null_terminated)
+{
+ size_t buffer_length;
+
+ if (NULL == value) {
+ return NULL;
+ }
+
+ /* Adding null character size due to require_null_terminated. */
+ buffer_length = strlen(value) + sizeof("");
+
+ return cJSON_ParseWithLengthOpts(value, buffer_length, return_parse_end,
+ require_null_terminated);
+}
+
+/* Parse an object - create a new root, and populate. */
+CJSON_PUBLIC(cJSON *)
+cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length,
+ const char **return_parse_end,
+ cJSON_bool require_null_terminated)
+{
+ parse_buffer buffer = {0, 0, 0, 0, {0, 0, 0}};
+ cJSON *item = NULL;
+
+ /* reset error position */
+ global_error.json = NULL;
+ global_error.position = 0;
+
+ if (value == NULL || 0 == buffer_length) {
+ goto fail;
+ }
+
+ buffer.content = (const unsigned char *)value;
+ buffer.length = buffer_length;
+ buffer.offset = 0;
+ buffer.hooks = global_hooks;
+
+ item = cJSON_New_Item(&global_hooks);
+ if (item == NULL) /* memory fail */
+ {
+ goto fail;
+ }
+
+ if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer)))) {
+ /* parse failure. ep is set. */
+ goto fail;
+ }
+
+ /* if we require null-terminated JSON without appended garbage, skip and
+ * then check for a null terminator */
+ if (require_null_terminated) {
+ buffer_skip_whitespace(&buffer);
+ if ((buffer.offset >= buffer.length) ||
+ buffer_at_offset(&buffer)[0] != '\0') {
+ goto fail;
+ }
+ }
+ if (return_parse_end) {
+ *return_parse_end = (const char *)buffer_at_offset(&buffer);
+ }
+
+ return item;
+
+fail:
+ if (item != NULL) {
+ cJSON_Delete(item);
+ }
+
+ if (value != NULL) {
+ error local_error;
+ local_error.json = (const unsigned char *)value;
+ local_error.position = 0;
+
+ if (buffer.offset < buffer.length) {
+ local_error.position = buffer.offset;
+ } else if (buffer.length > 0) {
+ local_error.position = buffer.length - 1;
+ }
+
+ if (return_parse_end != NULL) {
+ *return_parse_end = (const char *)local_error.json + local_error.position;
+ }
+
+ global_error = local_error;
+ }
+
+ return NULL;
+}
+
+/* Default options for cJSON_Parse */
+CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value)
+{
+ return cJSON_ParseWithOpts(value, 0, 0);
+}
+
+CJSON_PUBLIC(cJSON *)
+cJSON_ParseWithLength(const char *value, size_t buffer_length)
+{
+ return cJSON_ParseWithLengthOpts(value, buffer_length, 0, 0);
+}
+
+#define cjson_min(a, b) (((a) < (b)) ? (a) : (b))
+
+static unsigned char *print(const cJSON *const item, cJSON_bool format,
+ const internal_hooks *const hooks)
+{
+ static const size_t default_buffer_size = 256;
+ printbuffer buffer[1];
+ unsigned char *printed = NULL;
+
+ memset(buffer, 0, sizeof(buffer));
+
+ /* create buffer */
+ buffer->buffer = (unsigned char *)hooks->allocate(default_buffer_size);
+ buffer->length = default_buffer_size;
+ buffer->format = format;
+ buffer->hooks = *hooks;
+ if (buffer->buffer == NULL) {
+ goto fail;
+ }
+
+ /* print the value */
+ if (!print_value(item, buffer)) {
+ goto fail;
+ }
+ update_offset(buffer);
+
+ /* check if reallocate is available */
+ if (hooks->reallocate != NULL) {
+ printed =
+ (unsigned char *)hooks->reallocate(buffer->buffer, buffer->offset + 1);
+ if (printed == NULL) {
+ goto fail;
+ }
+ buffer->buffer = NULL;
+ } else /* otherwise copy the JSON over to a new buffer */
+ {
+ printed = (unsigned char *)hooks->allocate(buffer->offset + 1);
+ if (printed == NULL) {
+ goto fail;
+ }
+ memcpy(printed, buffer->buffer,
+ cjson_min(buffer->length, buffer->offset + 1));
+ printed[buffer->offset] = '\0'; /* just to be sure */
+
+ /* free the buffer */
+ hooks->deallocate(buffer->buffer);
+ buffer->buffer = NULL;
+ }
+
+ return printed;
+
+fail:
+ if (buffer->buffer != NULL) {
+ hooks->deallocate(buffer->buffer);
+ buffer->buffer = NULL;
+ }
+
+ if (printed != NULL) {
+ hooks->deallocate(printed);
+ printed = NULL;
+ }
+
+ return NULL;
+}
+
+/* Render a cJSON item/entity/structure to text. */
+CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item)
+{
+ return (char *)print(item, true, &global_hooks);
+}
+
+CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item)
+{
+ return (char *)print(item, false, &global_hooks);
+}
+
+CJSON_PUBLIC(char *)
+cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt)
+{
+ printbuffer p = {0, 0, 0, 0, 0, 0, {0, 0, 0}};
+
+ if (prebuffer < 0) {
+ return NULL;
+ }
+
+ p.buffer = (unsigned char *)global_hooks.allocate((size_t)prebuffer);
+ if (!p.buffer) {
+ return NULL;
+ }
+
+ p.length = (size_t)prebuffer;
+ p.offset = 0;
+ p.noalloc = false;
+ p.format = fmt;
+ p.hooks = global_hooks;
+
+ if (!print_value(item, &p)) {
+ global_hooks.deallocate(p.buffer);
+ p.buffer = NULL;
+ return NULL;
+ }
+
+ return (char *)p.buffer;
+}
+
+CJSON_PUBLIC(cJSON_bool)
+cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length,
+ const cJSON_bool format)
+{
+ printbuffer p = {0, 0, 0, 0, 0, 0, {0, 0, 0}};
+
+ if ((length < 0) || (buffer == NULL)) {
+ return false;
+ }
+
+ p.buffer = (unsigned char *)buffer;
+ p.length = (size_t)length;
+ p.offset = 0;
+ p.noalloc = true;
+ p.format = format;
+ p.hooks = global_hooks;
+
+ return print_value(item, &p);
+}
+
+/* Parser core - when encountering text, process appropriately. */
+static cJSON_bool parse_value(cJSON *const item,
+ parse_buffer *const input_buffer)
+{
+ if ((input_buffer == NULL) || (input_buffer->content == NULL)) {
+ return false; /* no input */
+ }
+
+ /* parse the different types of values */
+ /* null */
+ if (can_read(input_buffer, 4) &&
+ (strncmp((const char *)buffer_at_offset(input_buffer), "null", 4) == 0)) {
+ item->type = cJSON_NULL;
+ input_buffer->offset += 4;
+ return true;
+ }
+ /* false */
+ if (can_read(input_buffer, 5) &&
+ (strncmp((const char *)buffer_at_offset(input_buffer), "false", 5) ==
+ 0)) {
+ item->type = cJSON_False;
+ input_buffer->offset += 5;
+ return true;
+ }
+ /* true */
+ if (can_read(input_buffer, 4) &&
+ (strncmp((const char *)buffer_at_offset(input_buffer), "true", 4) == 0)) {
+ item->type = cJSON_True;
+ item->valueint = 1;
+ input_buffer->offset += 4;
+ return true;
+ }
+ /* string */
+ if (can_access_at_index(input_buffer, 0) &&
+ (buffer_at_offset(input_buffer)[0] == '\"')) {
+ return parse_string(item, input_buffer);
+ }
+ /* number */
+ if (can_access_at_index(input_buffer, 0) &&
+ ((buffer_at_offset(input_buffer)[0] == '-') ||
+ ((buffer_at_offset(input_buffer)[0] >= '0') &&
+ (buffer_at_offset(input_buffer)[0] <= '9')))) {
+ return parse_number(item, input_buffer);
+ }
+ /* array */
+ if (can_access_at_index(input_buffer, 0) &&
+ (buffer_at_offset(input_buffer)[0] == '[')) {
+ return parse_array(item, input_buffer);
+ }
+ /* object */
+ if (can_access_at_index(input_buffer, 0) &&
+ (buffer_at_offset(input_buffer)[0] == '{')) {
+ return parse_object(item, input_buffer);
+ }
+
+ return false;
+}
+
+/* Render a value to text. */
+static cJSON_bool print_value(const cJSON *const item,
+ printbuffer *const output_buffer)
+{
+ unsigned char *output = NULL;
+
+ if ((item == NULL) || (output_buffer == NULL)) {
+ return false;
+ }
+
+ switch ((item->type) & 0xFF) {
+ case cJSON_NULL:
+ output = ensure(output_buffer, 5);
+ if (output == NULL) {
+ return false;
+ }
+ strcpy((char *)output, "null");
+ return true;
+
+ case cJSON_False:
+ output = ensure(output_buffer, 6);
+ if (output == NULL) {
+ return false;
+ }
+ strcpy((char *)output, "false");
+ return true;
+
+ case cJSON_True:
+ output = ensure(output_buffer, 5);
+ if (output == NULL) {
+ return false;
+ }
+ strcpy((char *)output, "true");
+ return true;
+
+ case cJSON_Number:
+ return print_number(item, output_buffer);
+
+ case cJSON_Raw: {
+ size_t raw_length = 0;
+ if (item->valuestring == NULL) {
+ return false;
+ }
+
+ raw_length = strlen(item->valuestring) + sizeof("");
+ output = ensure(output_buffer, raw_length);
+ if (output == NULL) {
+ return false;
+ }
+ memcpy(output, item->valuestring, raw_length);
+ return true;
+ }
+
+ case cJSON_String:
+ return print_string(item, output_buffer);
+
+ case cJSON_Array:
+ return print_array(item, output_buffer);
+
+ case cJSON_Object:
+ return print_object(item, output_buffer);
+
+ default:
+ return false;
+ }
+}
+
+/* Build an array from input text. */
+static cJSON_bool parse_array(cJSON *const item,
+ parse_buffer *const input_buffer)
+{
+ cJSON *head = NULL; /* head of the linked list */
+ cJSON *current_item = NULL;
+
+ if (input_buffer->depth >= CJSON_NESTING_LIMIT) {
+ return false; /* to deeply nested */
+ }
+ input_buffer->depth++;
+
+ if (buffer_at_offset(input_buffer)[0] != '[') {
+ /* not an array */
+ goto fail;
+ }
+
+ input_buffer->offset++;
+ buffer_skip_whitespace(input_buffer);
+ if (can_access_at_index(input_buffer, 0) &&
+ (buffer_at_offset(input_buffer)[0] == ']')) {
+ /* empty array */
+ goto success;
+ }
+
+ /* check if we skipped to the end of the buffer */
+ if (cannot_access_at_index(input_buffer, 0)) {
+ input_buffer->offset--;
+ goto fail;
+ }
+
+ /* step back to character in front of the first element */
+ input_buffer->offset--;
+ /* loop through the comma separated array elements */
+ do {
+ /* allocate next item */
+ cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks));
+ if (new_item == NULL) {
+ goto fail; /* allocation failure */
+ }
+
+ /* attach next item to list */
+ if (head == NULL) {
+ /* start the linked list */
+ current_item = head = new_item;
+ } else {
+ /* add to the end and advance */
+ current_item->next = new_item;
+ new_item->prev = current_item;
+ current_item = new_item;
+ }
+
+ /* parse next value */
+ input_buffer->offset++;
+ buffer_skip_whitespace(input_buffer);
+ if (!parse_value(current_item, input_buffer)) {
+ goto fail; /* failed to parse value */
+ }
+ buffer_skip_whitespace(input_buffer);
+ } while (can_access_at_index(input_buffer, 0) &&
+ (buffer_at_offset(input_buffer)[0] == ','));
+
+ if (cannot_access_at_index(input_buffer, 0) ||
+ buffer_at_offset(input_buffer)[0] != ']') {
+ goto fail; /* expected end of array */
+ }
+
+success:
+ input_buffer->depth--;
+
+ if (head != NULL) {
+ head->prev = current_item;
+ }
+
+ item->type = cJSON_Array;
+ item->child = head;
+
+ input_buffer->offset++;
+
+ return true;
+
+fail:
+ if (head != NULL) {
+ cJSON_Delete(head);
+ }
+
+ return false;
+}
+
+/* Render an array to text */
+static cJSON_bool print_array(const cJSON *const item,
+ printbuffer *const output_buffer)
+{
+ unsigned char *output_pointer = NULL;
+ size_t length = 0;
+ cJSON *current_element = item->child;
+
+ if (output_buffer == NULL) {
+ return false;
+ }
+
+ /* Compose the output array. */
+ /* opening square bracket */
+ output_pointer = ensure(output_buffer, 1);
+ if (output_pointer == NULL) {
+ return false;
+ }
+
+ *output_pointer = '[';
+ output_buffer->offset++;
+ output_buffer->depth++;
+
+ while (current_element != NULL) {
+ if (!print_value(current_element, output_buffer)) {
+ return false;
+ }
+ update_offset(output_buffer);
+ if (current_element->next) {
+ length = (size_t)(output_buffer->format ? 2 : 1);
+ output_pointer = ensure(output_buffer, length + 1);
+ if (output_pointer == NULL) {
+ return false;
+ }
+ *output_pointer++ = ',';
+ if (output_buffer->format) {
+ *output_pointer++ = ' ';
+ }
+ *output_pointer = '\0';
+ output_buffer->offset += length;
+ }
+ current_element = current_element->next;
+ }
+
+ output_pointer = ensure(output_buffer, 2);
+ if (output_pointer == NULL) {
+ return false;
+ }
+ *output_pointer++ = ']';
+ *output_pointer = '\0';
+ output_buffer->depth--;
+
+ return true;
+}
+
+/* Build an object from the text. */
+static cJSON_bool parse_object(cJSON *const item,
+ parse_buffer *const input_buffer)
+{
+ cJSON *head = NULL; /* linked list head */
+ cJSON *current_item = NULL;
+
+ if (input_buffer->depth >= CJSON_NESTING_LIMIT) {
+ return false; /* to deeply nested */
+ }
+ input_buffer->depth++;
+
+ if (cannot_access_at_index(input_buffer, 0) ||
+ (buffer_at_offset(input_buffer)[0] != '{')) {
+ goto fail; /* not an object */
+ }
+
+ input_buffer->offset++;
+ buffer_skip_whitespace(input_buffer);
+ if (can_access_at_index(input_buffer, 0) &&
+ (buffer_at_offset(input_buffer)[0] == '}')) {
+ goto success; /* empty object */
+ }
+
+ /* check if we skipped to the end of the buffer */
+ if (cannot_access_at_index(input_buffer, 0)) {
+ input_buffer->offset--;
+ goto fail;
+ }
+
+ /* step back to character in front of the first element */
+ input_buffer->offset--;
+ /* loop through the comma separated array elements */
+ do {
+ /* allocate next item */
+ cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks));
+ if (new_item == NULL) {
+ goto fail; /* allocation failure */
+ }
+
+ /* attach next item to list */
+ if (head == NULL) {
+ /* start the linked list */
+ current_item = head = new_item;
+ } else {
+ /* add to the end and advance */
+ current_item->next = new_item;
+ new_item->prev = current_item;
+ current_item = new_item;
+ }
+
+ if (cannot_access_at_index(input_buffer, 1)) {
+ goto fail; /* nothing comes after the comma */
+ }
+
+ /* parse the name of the child */
+ input_buffer->offset++;
+ buffer_skip_whitespace(input_buffer);
+ if (!parse_string(current_item, input_buffer)) {
+ goto fail; /* failed to parse name */
+ }
+ buffer_skip_whitespace(input_buffer);
+
+ /* swap valuestring and string, because we parsed the name */
+ current_item->string = current_item->valuestring;
+ current_item->valuestring = NULL;
+
+ if (cannot_access_at_index(input_buffer, 0) ||
+ (buffer_at_offset(input_buffer)[0] != ':')) {
+ goto fail; /* invalid object */
+ }
+
+ /* parse the value */
+ input_buffer->offset++;
+ buffer_skip_whitespace(input_buffer);
+ if (!parse_value(current_item, input_buffer)) {
+ goto fail; /* failed to parse value */
+ }
+ buffer_skip_whitespace(input_buffer);
+ } while (can_access_at_index(input_buffer, 0) &&
+ (buffer_at_offset(input_buffer)[0] == ','));
+
+ if (cannot_access_at_index(input_buffer, 0) ||
+ (buffer_at_offset(input_buffer)[0] != '}')) {
+ goto fail; /* expected end of object */
+ }
+
+success:
+ input_buffer->depth--;
+
+ if (head != NULL) {
+ head->prev = current_item;
+ }
+
+ item->type = cJSON_Object;
+ item->child = head;
+
+ input_buffer->offset++;
+ return true;
+
+fail:
+ if (head != NULL) {
+ cJSON_Delete(head);
+ }
+
+ return false;
+}
+
+/* Render an object to text. */
+static cJSON_bool print_object(const cJSON *const item,
+ printbuffer *const output_buffer)
+{
+ unsigned char *output_pointer = NULL;
+ size_t length = 0;
+ cJSON *current_item = item->child;
+
+ if (output_buffer == NULL) {
+ return false;
+ }
+
+ /* Compose the output: */
+ length = (size_t)(output_buffer->format ? 2 : 1); /* fmt: {\n */
+ output_pointer = ensure(output_buffer, length + 1);
+ if (output_pointer == NULL) {
+ return false;
+ }
+
+ *output_pointer++ = '{';
+ output_buffer->depth++;
+ if (output_buffer->format) {
+ *output_pointer++ = '\n';
+ }
+ output_buffer->offset += length;
+
+ while (current_item) {
+ if (output_buffer->format) {
+ size_t i;
+ output_pointer = ensure(output_buffer, output_buffer->depth);
+ if (output_pointer == NULL) {
+ return false;
+ }
+ for (i = 0; i < output_buffer->depth; i++) {
+ *output_pointer++ = '\t';
+ }
+ output_buffer->offset += output_buffer->depth;
+ }
+
+ /* print key */
+ if (!print_string_ptr((unsigned char *)current_item->string,
+ output_buffer)) {
+ return false;
+ }
+ update_offset(output_buffer);
+
+ length = (size_t)(output_buffer->format ? 2 : 1);
+ output_pointer = ensure(output_buffer, length);
+ if (output_pointer == NULL) {
+ return false;
+ }
+ *output_pointer++ = ':';
+ if (output_buffer->format) {
+ *output_pointer++ = '\t';
+ }
+ output_buffer->offset += length;
+
+ /* print value */
+ if (!print_value(current_item, output_buffer)) {
+ return false;
+ }
+ update_offset(output_buffer);
+
+ /* print comma if not last */
+ length = ((size_t)(output_buffer->format ? 1 : 0) +
+ (size_t)(current_item->next ? 1 : 0));
+ output_pointer = ensure(output_buffer, length + 1);
+ if (output_pointer == NULL) {
+ return false;
+ }
+ if (current_item->next) {
+ *output_pointer++ = ',';
+ }
+
+ if (output_buffer->format) {
+ *output_pointer++ = '\n';
+ }
+ *output_pointer = '\0';
+ output_buffer->offset += length;
+
+ current_item = current_item->next;
+ }
+
+ output_pointer = ensure(
+ output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2);
+ if (output_pointer == NULL) {
+ return false;
+ }
+ if (output_buffer->format) {
+ size_t i;
+ for (i = 0; i < (output_buffer->depth - 1); i++) {
+ *output_pointer++ = '\t';
+ }
+ }
+ *output_pointer++ = '}';
+ *output_pointer = '\0';
+ output_buffer->depth--;
+
+ return true;
+}
+
+/* Get Array size/item / object item. */
+CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array)
+{
+ cJSON *child = NULL;
+ size_t size = 0;
+
+ if (array == NULL) {
+ return 0;
+ }
+
+ child = array->child;
+
+ while (child != NULL) {
+ size++;
+ child = child->next;
+ }
+
+ /* FIXME: Can overflow here. Cannot be fixed without breaking the API */
+
+ return (int)size;
+}
+
+static cJSON *get_array_item(const cJSON *array, size_t index)
+{
+ cJSON *current_child = NULL;
+
+ if (array == NULL) {
+ return NULL;
+ }
+
+ current_child = array->child;
+ while ((current_child != NULL) && (index > 0)) {
+ index--;
+ current_child = current_child->next;
+ }
+
+ return current_child;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index)
+{
+ if (index < 0) {
+ return NULL;
+ }
+
+ return get_array_item(array, (size_t)index);
+}
+
+static cJSON *get_object_item(const cJSON *const object, const char *const name,
+ const cJSON_bool case_sensitive)
+{
+ cJSON *current_element = NULL;
+
+ if ((object == NULL) || (name == NULL)) {
+ return NULL;
+ }
+
+ current_element = object->child;
+ if (case_sensitive) {
+ while ((current_element != NULL) && (current_element->string != NULL) &&
+ (strcmp(name, current_element->string) != 0)) {
+ current_element = current_element->next;
+ }
+ } else {
+ while ((current_element != NULL) &&
+ (case_insensitive_strcmp(
+ (const unsigned char *)name,
+ (const unsigned char *)(current_element->string)) != 0)) {
+ current_element = current_element->next;
+ }
+ }
+
+ if ((current_element == NULL) || (current_element->string == NULL)) {
+ return NULL;
+ }
+
+ return current_element;
+}
+
+CJSON_PUBLIC(cJSON *)
+cJSON_GetObjectItem(const cJSON *const object, const char *const string)
+{
+ return get_object_item(object, string, false);
+}
+
+CJSON_PUBLIC(cJSON *)
+cJSON_GetObjectItemCaseSensitive(const cJSON *const object,
+ const char *const string)
+{
+ return get_object_item(object, string, true);
+}
+
+CJSON_PUBLIC(cJSON_bool)
+cJSON_HasObjectItem(const cJSON *object, const char *string)
+{
+ return cJSON_GetObjectItem(object, string) ? 1 : 0;
+}
+
+/* Utility for array list handling. */
+static void suffix_object(cJSON *prev, cJSON *item)
+{
+ prev->next = item;
+ item->prev = prev;
+}
+
+/* Utility for handling references. */
+static cJSON *create_reference(const cJSON *item,
+ const internal_hooks *const hooks)
+{
+ cJSON *reference = NULL;
+ if (item == NULL) {
+ return NULL;
+ }
+
+ reference = cJSON_New_Item(hooks);
+ if (reference == NULL) {
+ return NULL;
+ }
+
+ memcpy(reference, item, sizeof(cJSON));
+ reference->string = NULL;
+ reference->type |= cJSON_IsReference;
+ reference->next = reference->prev = NULL;
+ return reference;
+}
+
+static cJSON_bool add_item_to_array(cJSON *array, cJSON *item)
+{
+ cJSON *child = NULL;
+
+ if ((item == NULL) || (array == NULL) || (array == item)) {
+ return false;
+ }
+
+ child = array->child;
+ /*
+ * To find the last item in array quickly, we use prev in array
+ */
+ if (child == NULL) {
+ /* list is empty, start new one */
+ array->child = item;
+ item->prev = item;
+ item->next = NULL;
+ } else {
+ /* append to the end */
+ if (child->prev) {
+ suffix_object(child->prev, item);
+ array->child->prev = item;
+ }
+ }
+
+ return true;
+}
+
+/* Add item to array/object. */
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item)
+{
+ return add_item_to_array(array, item);
+}
+
+#if defined(__clang__) || \
+ (defined(__GNUC__) && \
+ ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5))))
+#pragma GCC diagnostic push
+#endif
+#ifdef __GNUC__
+#pragma GCC diagnostic ignored "-Wcast-qual"
+#endif
+/* helper function to cast away const */
+static void *cast_away_const(const void *string) { return (void *)string; }
+#if defined(__clang__) || \
+ (defined(__GNUC__) && \
+ ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5))))
+#pragma GCC diagnostic pop
+#endif
+
+static cJSON_bool add_item_to_object(cJSON *const object,
+ const char *const string,
+ cJSON *const item,
+ const internal_hooks *const hooks,
+ const cJSON_bool constant_key)
+{
+ char *new_key = NULL;
+ int new_type = cJSON_Invalid;
+
+ if ((object == NULL) || (string == NULL) || (item == NULL) ||
+ (object == item)) {
+ return false;
+ }
+
+ if (constant_key) {
+ new_key = (char *)cast_away_const(string);
+ new_type = item->type | cJSON_StringIsConst;
+ } else {
+ new_key = (char *)cJSON_strdup((const unsigned char *)string, hooks);
+ if (new_key == NULL) {
+ return false;
+ }
+
+ new_type = item->type & ~cJSON_StringIsConst;
+ }
+
+ if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) {
+ hooks->deallocate(item->string);
+ }
+
+ item->string = new_key;
+ item->type = new_type;
+
+ return add_item_to_array(object, item);
+}
+
+CJSON_PUBLIC(cJSON_bool)
+cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item)
+{
+ return add_item_to_object(object, string, item, &global_hooks, false);
+}
+
+/* Add an item to an object with constant string as key */
+CJSON_PUBLIC(cJSON_bool)
+cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item)
+{
+ return add_item_to_object(object, string, item, &global_hooks, true);
+}
+
+CJSON_PUBLIC(cJSON_bool)
+cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item)
+{
+ if (array == NULL) {
+ return false;
+ }
+
+ return add_item_to_array(array, create_reference(item, &global_hooks));
+}
+
+CJSON_PUBLIC(cJSON_bool)
+cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item)
+{
+ if ((object == NULL) || (string == NULL)) {
+ return false;
+ }
+
+ return add_item_to_object(object, string,
+ create_reference(item, &global_hooks),
+ &global_hooks, false);
+}
+
+CJSON_PUBLIC(cJSON *)
+cJSON_AddNullToObject(cJSON *const object, const char *const name)
+{
+ cJSON *null = cJSON_CreateNull();
+ if (add_item_to_object(object, name, null, &global_hooks, false)) {
+ return null;
+ }
+
+ cJSON_Delete(null);
+ return NULL;
+}
+
+CJSON_PUBLIC(cJSON *)
+cJSON_AddTrueToObject(cJSON *const object, const char *const name)
+{
+ cJSON *true_item = cJSON_CreateTrue();
+ if (add_item_to_object(object, name, true_item, &global_hooks, false)) {
+ return true_item;
+ }
+
+ cJSON_Delete(true_item);
+ return NULL;
+}
+
+CJSON_PUBLIC(cJSON *)
+cJSON_AddFalseToObject(cJSON *const object, const char *const name)
+{
+ cJSON *false_item = cJSON_CreateFalse();
+ if (add_item_to_object(object, name, false_item, &global_hooks, false)) {
+ return false_item;
+ }
+
+ cJSON_Delete(false_item);
+ return NULL;
+}
+
+CJSON_PUBLIC(cJSON *)
+cJSON_AddBoolToObject(cJSON *const object, const char *const name,
+ const cJSON_bool boolean)
+{
+ cJSON *bool_item = cJSON_CreateBool(boolean);
+ if (add_item_to_object(object, name, bool_item, &global_hooks, false)) {
+ return bool_item;
+ }
+
+ cJSON_Delete(bool_item);
+ return NULL;
+}
+
+CJSON_PUBLIC(cJSON *)
+cJSON_AddNumberToObject(cJSON *const object, const char *const name,
+ const double number)
+{
+ cJSON *number_item = cJSON_CreateNumber(number);
+ if (add_item_to_object(object, name, number_item, &global_hooks, false)) {
+ return number_item;
+ }
+
+ cJSON_Delete(number_item);
+ return NULL;
+}
+
+CJSON_PUBLIC(cJSON *)
+cJSON_AddStringToObject(cJSON *const object, const char *const name,
+ const char *const string)
+{
+ cJSON *string_item = cJSON_CreateString(string);
+ if (add_item_to_object(object, name, string_item, &global_hooks, false)) {
+ return string_item;
+ }
+
+ cJSON_Delete(string_item);
+ return NULL;
+}
+
+CJSON_PUBLIC(cJSON *)
+cJSON_AddRawToObject(cJSON *const object, const char *const name,
+ const char *const raw)
+{
+ cJSON *raw_item = cJSON_CreateRaw(raw);
+ if (add_item_to_object(object, name, raw_item, &global_hooks, false)) {
+ return raw_item;
+ }
+
+ cJSON_Delete(raw_item);
+ return NULL;
+}
+
+CJSON_PUBLIC(cJSON *)
+cJSON_AddObjectToObject(cJSON *const object, const char *const name)
+{
+ cJSON *object_item = cJSON_CreateObject();
+ if (add_item_to_object(object, name, object_item, &global_hooks, false)) {
+ return object_item;
+ }
+
+ cJSON_Delete(object_item);
+ return NULL;
+}
+
+CJSON_PUBLIC(cJSON *)
+cJSON_AddArrayToObject(cJSON *const object, const char *const name)
+{
+ cJSON *array = cJSON_CreateArray();
+ if (add_item_to_object(object, name, array, &global_hooks, false)) {
+ return array;
+ }
+
+ cJSON_Delete(array);
+ return NULL;
+}
+
+CJSON_PUBLIC(cJSON *)
+cJSON_DetachItemViaPointer(cJSON *parent, cJSON *const item)
+{
+ if ((parent == NULL) || (item == NULL) ||
+ (item != parent->child && item->prev == NULL)) {
+ return NULL;
+ }
+
+ if (item != parent->child) {
+ /* not the first element */
+ item->prev->next = item->next;
+ }
+ if (item->next != NULL) {
+ /* not the last element */
+ item->next->prev = item->prev;
+ }
+
+ if (item == parent->child) {
+ /* first element */
+ parent->child = item->next;
+ } else if (item->next == NULL) {
+ /* last element */
+ parent->child->prev = item->prev;
+ }
+
+ /* make sure the detached item doesn't point anywhere anymore */
+ item->prev = NULL;
+ item->next = NULL;
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which)
+{
+ if (which < 0) {
+ return NULL;
+ }
+
+ return cJSON_DetachItemViaPointer(array,
+ get_array_item(array, (size_t)which));
+}
+
+CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which)
+{
+ cJSON_Delete(cJSON_DetachItemFromArray(array, which));
+}
+
+CJSON_PUBLIC(cJSON *)
+cJSON_DetachItemFromObject(cJSON *object, const char *string)
+{
+ cJSON *to_detach = cJSON_GetObjectItem(object, string);
+
+ return cJSON_DetachItemViaPointer(object, to_detach);
+}
+
+CJSON_PUBLIC(cJSON *)
+cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string)
+{
+ cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string);
+
+ return cJSON_DetachItemViaPointer(object, to_detach);
+}
+
+CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string)
+{
+ cJSON_Delete(cJSON_DetachItemFromObject(object, string));
+}
+
+CJSON_PUBLIC(void)
+cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string)
+{
+ cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string));
+}
+
+/* Replace array/object items with new ones. */
+CJSON_PUBLIC(cJSON_bool)
+cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem)
+{
+ cJSON *after_inserted = NULL;
+
+ if (which < 0 || newitem == NULL) {
+ return false;
+ }
+
+ after_inserted = get_array_item(array, (size_t)which);
+ if (after_inserted == NULL) {
+ return add_item_to_array(array, newitem);
+ }
+
+ if (after_inserted != array->child && after_inserted->prev == NULL) {
+ /* return false if after_inserted is a corrupted array item */
+ return false;
+ }
+
+ newitem->next = after_inserted;
+ newitem->prev = after_inserted->prev;
+ after_inserted->prev = newitem;
+ if (after_inserted == array->child) {
+ array->child = newitem;
+ } else {
+ newitem->prev->next = newitem;
+ }
+ return true;
+}
+
+CJSON_PUBLIC(cJSON_bool)
+cJSON_ReplaceItemViaPointer(cJSON *const parent, cJSON *const item,
+ cJSON *replacement)
+{
+ if ((parent == NULL) || (parent->child == NULL) || (replacement == NULL) ||
+ (item == NULL)) {
+ return false;
+ }
+
+ if (replacement == item) {
+ return true;
+ }
+
+ replacement->next = item->next;
+ replacement->prev = item->prev;
+
+ if (replacement->next != NULL) {
+ replacement->next->prev = replacement;
+ }
+ if (parent->child == item) {
+ if (parent->child->prev == parent->child) {
+ replacement->prev = replacement;
+ }
+ parent->child = replacement;
+ } else { /*
+ * To find the last item in array quickly, we use prev in
+ * array. We can't modify the last item's next pointer where
+ * this item was the parent's child
+ */
+ if (replacement->prev != NULL) {
+ replacement->prev->next = replacement;
+ }
+ if (replacement->next == NULL) {
+ parent->child->prev = replacement;
+ }
+ }
+
+ item->next = NULL;
+ item->prev = NULL;
+ cJSON_Delete(item);
+
+ return true;
+}
+
+CJSON_PUBLIC(cJSON_bool)
+cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem)
+{
+ if (which < 0) {
+ return false;
+ }
+
+ return cJSON_ReplaceItemViaPointer(
+ array, get_array_item(array, (size_t)which), newitem);
+}
+
+static cJSON_bool replace_item_in_object(cJSON *object, const char *string,
+ cJSON *replacement,
+ cJSON_bool case_sensitive)
+{
+ if ((replacement == NULL) || (string == NULL)) {
+ return false;
+ }
+
+ /* replace the name in the replacement */
+ if (!(replacement->type & cJSON_StringIsConst) &&
+ (replacement->string != NULL)) {
+ cJSON_free(replacement->string);
+ }
+ replacement->string =
+ (char *)cJSON_strdup((const unsigned char *)string, &global_hooks);
+ if (replacement->string == NULL) {
+ return false;
+ }
+
+ replacement->type &= ~cJSON_StringIsConst;
+
+ return cJSON_ReplaceItemViaPointer(
+ object, get_object_item(object, string, case_sensitive), replacement);
+}
+
+CJSON_PUBLIC(cJSON_bool)
+cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem)
+{
+ return replace_item_in_object(object, string, newitem, false);
+}
+
+CJSON_PUBLIC(cJSON_bool)
+cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string,
+ cJSON *newitem)
+{
+ return replace_item_in_object(object, string, newitem, true);
+}
+
+/* Create basic types: */
+CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if (item) {
+ item->type = cJSON_NULL;
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if (item) {
+ item->type = cJSON_True;
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if (item) {
+ item->type = cJSON_False;
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if (item) {
+ item->type = boolean ? cJSON_True : cJSON_False;
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if (item) {
+ item->type = cJSON_Number;
+ item->valuedouble = num;
+
+ /* use saturation in case of overflow */
+ if (num >= INT_MAX) {
+ item->valueint = INT_MAX;
+ } else if (num <= (double)INT_MIN) {
+ item->valueint = INT_MIN;
+ } else {
+ item->valueint = (int)num;
+ }
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if (item) {
+ item->type = cJSON_String;
+ item->valuestring =
+ (char *)cJSON_strdup((const unsigned char *)string, &global_hooks);
+ if (!item->valuestring) {
+ cJSON_Delete(item);
+ return NULL;
+ }
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if (item != NULL) {
+ item->type = cJSON_String | cJSON_IsReference;
+ item->valuestring = (char *)cast_away_const(string);
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if (item != NULL) {
+ item->type = cJSON_Object | cJSON_IsReference;
+ item->child = (cJSON *)cast_away_const(child);
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if (item != NULL) {
+ item->type = cJSON_Array | cJSON_IsReference;
+ item->child = (cJSON *)cast_away_const(child);
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if (item) {
+ item->type = cJSON_Raw;
+ item->valuestring =
+ (char *)cJSON_strdup((const unsigned char *)raw, &global_hooks);
+ if (!item->valuestring) {
+ cJSON_Delete(item);
+ return NULL;
+ }
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if (item) {
+ item->type = cJSON_Array;
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if (item) {
+ item->type = cJSON_Object;
+ }
+
+ return item;
+}
+
+/* Create Arrays: */
+CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count)
+{
+ size_t i = 0;
+ cJSON *n = NULL;
+ cJSON *p = NULL;
+ cJSON *a = NULL;
+
+ if ((count < 0) || (numbers == NULL)) {
+ return NULL;
+ }
+
+ a = cJSON_CreateArray();
+
+ for (i = 0; a && (i < (size_t)count); i++) {
+ n = cJSON_CreateNumber(numbers[i]);
+ if (!n) {
+ cJSON_Delete(a);
+ return NULL;
+ }
+ if (!i) {
+ a->child = n;
+ } else {
+ suffix_object(p, n);
+ }
+ p = n;
+ }
+
+ if (a && a->child) {
+ a->child->prev = n;
+ }
+
+ return a;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count)
+{
+ size_t i = 0;
+ cJSON *n = NULL;
+ cJSON *p = NULL;
+ cJSON *a = NULL;
+
+ if ((count < 0) || (numbers == NULL)) {
+ return NULL;
+ }
+
+ a = cJSON_CreateArray();
+
+ for (i = 0; a && (i < (size_t)count); i++) {
+ n = cJSON_CreateNumber((double)numbers[i]);
+ if (!n) {
+ cJSON_Delete(a);
+ return NULL;
+ }
+ if (!i) {
+ a->child = n;
+ } else {
+ suffix_object(p, n);
+ }
+ p = n;
+ }
+
+ if (a && a->child) {
+ a->child->prev = n;
+ }
+
+ return a;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count)
+{
+ size_t i = 0;
+ cJSON *n = NULL;
+ cJSON *p = NULL;
+ cJSON *a = NULL;
+
+ if ((count < 0) || (numbers == NULL)) {
+ return NULL;
+ }
+
+ a = cJSON_CreateArray();
+
+ for (i = 0; a && (i < (size_t)count); i++) {
+ n = cJSON_CreateNumber(numbers[i]);
+ if (!n) {
+ cJSON_Delete(a);
+ return NULL;
+ }
+ if (!i) {
+ a->child = n;
+ } else {
+ suffix_object(p, n);
+ }
+ p = n;
+ }
+
+ if (a && a->child) {
+ a->child->prev = n;
+ }
+
+ return a;
+}
+
+CJSON_PUBLIC(cJSON *)
+cJSON_CreateStringArray(const char *const *strings, int count)
+{
+ size_t i = 0;
+ cJSON *n = NULL;
+ cJSON *p = NULL;
+ cJSON *a = NULL;
+
+ if ((count < 0) || (strings == NULL)) {
+ return NULL;
+ }
+
+ a = cJSON_CreateArray();
+
+ for (i = 0; a && (i < (size_t)count); i++) {
+ n = cJSON_CreateString(strings[i]);
+ if (!n) {
+ cJSON_Delete(a);
+ return NULL;
+ }
+ if (!i) {
+ a->child = n;
+ } else {
+ suffix_object(p, n);
+ }
+ p = n;
+ }
+
+ if (a && a->child) {
+ a->child->prev = n;
+ }
+
+ return a;
+}
+
+/* Duplication */
+cJSON *cJSON_Duplicate_rec(const cJSON *item, size_t depth, cJSON_bool recurse);
+
+CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse)
+{
+ return cJSON_Duplicate_rec(item, 0, recurse);
+}
+
+cJSON *cJSON_Duplicate_rec(const cJSON *item, size_t depth, cJSON_bool recurse)
+{
+ cJSON *newitem = NULL;
+ cJSON *child = NULL;
+ cJSON *next = NULL;
+ cJSON *newchild = NULL;
+
+ /* Bail on bad ptr */
+ if (!item) {
+ goto fail;
+ }
+ /* Create new item */
+ newitem = cJSON_New_Item(&global_hooks);
+ if (!newitem) {
+ goto fail;
+ }
+ /* Copy over all vars */
+ newitem->type = item->type & (~cJSON_IsReference);
+ newitem->valueint = item->valueint;
+ newitem->valuedouble = item->valuedouble;
+ if (item->valuestring) {
+ newitem->valuestring =
+ (char *)cJSON_strdup((unsigned char *)item->valuestring, &global_hooks);
+ if (!newitem->valuestring) {
+ goto fail;
+ }
+ }
+ if (item->string) {
+ newitem->string = (item->type & cJSON_StringIsConst)
+ ? item->string
+ : (char *)cJSON_strdup((unsigned char *)item->string,
+ &global_hooks);
+ if (!newitem->string) {
+ goto fail;
+ }
+ }
+ /* If non-recursive, then we're done! */
+ if (!recurse) {
+ return newitem;
+ }
+ /* Walk the ->next chain for the child. */
+ child = item->child;
+ while (child != NULL) {
+ if (depth >= CJSON_CIRCULAR_LIMIT) {
+ goto fail;
+ }
+ newchild = cJSON_Duplicate_rec(child, depth + 1,
+ true); /* Duplicate (with recurse) each item
+ in the ->next chain */
+ if (!newchild) {
+ goto fail;
+ }
+ if (next != NULL) {
+ /* If newitem->child already set, then crosswire ->prev
+ * and ->next and move on */
+ next->next = newchild;
+ newchild->prev = next;
+ next = newchild;
+ } else {
+ /* Set newitem->child and move to it */
+ newitem->child = newchild;
+ next = newchild;
+ }
+ child = child->next;
+ }
+ if (newitem && newitem->child) {
+ newitem->child->prev = newchild;
+ }
+
+ return newitem;
+
+fail:
+ if (newitem != NULL) {
+ cJSON_Delete(newitem);
+ }
+
+ return NULL;
+}
+
+static void skip_oneline_comment(char **input)
+{
+ *input += static_strlen("//");
+
+ for (; (*input)[0] != '\0'; ++(*input)) {
+ if ((*input)[0] == '\n') {
+ *input += static_strlen("\n");
+ return;
+ }
+ }
+}
+
+static void skip_multiline_comment(char **input)
+{
+ *input += static_strlen("/*");
+
+ for (; (*input)[0] != '\0'; ++(*input)) {
+ if (((*input)[0] == '*') && ((*input)[1] == '/')) {
+ *input += static_strlen("*/");
+ return;
+ }
+ }
+}
+
+static void minify_string(char **input, char **output)
+{
+ (*output)[0] = (*input)[0];
+ *input += static_strlen("\"");
+ *output += static_strlen("\"");
+
+ for (; (*input)[0] != '\0'; (void)++(*input), ++(*output)) {
+ (*output)[0] = (*input)[0];
+
+ if ((*input)[0] == '\"') {
+ (*output)[0] = '\"';
+ *input += static_strlen("\"");
+ *output += static_strlen("\"");
+ return;
+ } else if (((*input)[0] == '\\') && ((*input)[1] == '\"')) {
+ (*output)[1] = (*input)[1];
+ *input += static_strlen("\"");
+ *output += static_strlen("\"");
+ }
+ }
+}
+
+CJSON_PUBLIC(void) cJSON_Minify(char *json)
+{
+ char *into = json;
+
+ if (json == NULL) {
+ return;
+ }
+
+ while (json[0] != '\0') {
+ switch (json[0]) {
+ case ' ':
+ case '\t':
+ case '\r':
+ case '\n':
+ json++;
+ break;
+
+ case '/':
+ if (json[1] == '/') {
+ skip_oneline_comment(&json);
+ } else if (json[1] == '*') {
+ skip_multiline_comment(&json);
+ } else {
+ json++;
+ }
+ break;
+
+ case '\"':
+ minify_string(&json, (char **)&into);
+ break;
+
+ default:
+ into[0] = json[0];
+ json++;
+ into++;
+ }
+ }
+
+ /* and null-terminate. */
+ *into = '\0';
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON *const item)
+{
+ if (item == NULL) {
+ return false;
+ }
+
+ return (item->type & 0xFF) == cJSON_Invalid;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON *const item)
+{
+ if (item == NULL) {
+ return false;
+ }
+
+ return (item->type & 0xFF) == cJSON_False;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON *const item)
+{
+ if (item == NULL) {
+ return false;
+ }
+
+ return (item->type & 0xff) == cJSON_True;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON *const item)
+{
+ if (item == NULL) {
+ return false;
+ }
+
+ return (item->type & (cJSON_True | cJSON_False)) != 0;
+}
+CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON *const item)
+{
+ if (item == NULL) {
+ return false;
+ }
+
+ return (item->type & 0xFF) == cJSON_NULL;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON *const item)
+{
+ if (item == NULL) {
+ return false;
+ }
+
+ return (item->type & 0xFF) == cJSON_Number;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON *const item)
+{
+ if (item == NULL) {
+ return false;
+ }
+
+ return (item->type & 0xFF) == cJSON_String;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON *const item)
+{
+ if (item == NULL) {
+ return false;
+ }
+
+ return (item->type & 0xFF) == cJSON_Array;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON *const item)
+{
+ if (item == NULL) {
+ return false;
+ }
+
+ return (item->type & 0xFF) == cJSON_Object;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON *const item)
+{
+ if (item == NULL) {
+ return false;
+ }
+
+ return (item->type & 0xFF) == cJSON_Raw;
+}
+
+CJSON_PUBLIC(cJSON_bool)
+cJSON_Compare(const cJSON *const a, const cJSON *const b,
+ const cJSON_bool case_sensitive)
+{
+ if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF))) {
+ return false;
+ }
+
+ /* check if type is valid */
+ switch (a->type & 0xFF) {
+ case cJSON_False:
+ case cJSON_True:
+ case cJSON_NULL:
+ case cJSON_Number:
+ case cJSON_String:
+ case cJSON_Raw:
+ case cJSON_Array:
+ case cJSON_Object:
+ break;
+
+ default:
+ return false;
+ }
+
+ /* identical objects are equal */
+ if (a == b) {
+ return true;
+ }
+
+ switch (a->type & 0xFF) {
+ /* in these cases and equal type is enough */
+ case cJSON_False:
+ case cJSON_True:
+ case cJSON_NULL:
+ return true;
+
+ case cJSON_Number:
+ if (compare_double(a->valuedouble, b->valuedouble)) {
+ return true;
+ }
+ return false;
+
+ case cJSON_String:
+ case cJSON_Raw:
+ if ((a->valuestring == NULL) || (b->valuestring == NULL)) {
+ return false;
+ }
+ if (strcmp(a->valuestring, b->valuestring) == 0) {
+ return true;
+ }
+
+ return false;
+
+ case cJSON_Array: {
+ cJSON *a_element = a->child;
+ cJSON *b_element = b->child;
+
+ for (; (a_element != NULL) && (b_element != NULL);) {
+ if (!cJSON_Compare(a_element, b_element, case_sensitive)) {
+ return false;
+ }
+
+ a_element = a_element->next;
+ b_element = b_element->next;
+ }
+
+ /* one of the arrays is longer than the other */
+ if (a_element != b_element) {
+ return false;
+ }
+
+ return true;
+ }
+
+ case cJSON_Object: {
+ cJSON *a_element = NULL;
+ cJSON *b_element = NULL;
+ cJSON_ArrayForEach(a_element, a)
+ {
+ /* TODO This has O(n^2) runtime, which is horrible! */
+ b_element = get_object_item(b, a_element->string, case_sensitive);
+ if (b_element == NULL) {
+ return false;
+ }
+
+ if (!cJSON_Compare(a_element, b_element, case_sensitive)) {
+ return false;
+ }
+ }
+
+ /* doing this twice, once on a and b to prevent true comparison
+ * if a subset of b
+ * TODO: Do this the proper way, this is just a fix for now */
+ cJSON_ArrayForEach(b_element, b)
+ {
+ a_element = get_object_item(a, b_element->string, case_sensitive);
+ if (a_element == NULL) {
+ return false;
+ }
+
+ if (!cJSON_Compare(b_element, a_element, case_sensitive)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ default:
+ return false;
+ }
+}
+
+CJSON_PUBLIC(void *) cJSON_malloc(size_t size)
+{
+ return global_hooks.allocate(size);
+}
+
+CJSON_PUBLIC(void) cJSON_free(void *object)
+{
+ global_hooks.deallocate(object);
+ object = NULL;
+}
diff --git a/lib/cjson/cJSON.h b/lib/cjson/cJSON.h
@@ -0,0 +1,396 @@
+/*
+ Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+
+#ifndef cJSON__h
+#define cJSON__h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if !defined(__WINDOWS__) && \
+ (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32))
+#define __WINDOWS__
+#endif
+
+#ifdef __WINDOWS__
+
+/* When compiling for windows, we specify a specific calling convention to avoid
+issues where we are being called from a project with a different default calling
+convention. For windows you have 3 define options:
+
+CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever
+dllexport symbols CJSON_EXPORT_SYMBOLS - Define this on library build when you
+want to dllexport symbols (default) CJSON_IMPORT_SYMBOLS - Define this if you
+want to dllimport symbol
+
+For *nix builds that support visibility attribute, you can define similar
+behavior by
+
+setting default visibility to hidden by adding
+-fvisibility=hidden (for gcc)
+or
+-xldscope=hidden (for sun cc)
+to CFLAGS
+
+then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way
+CJSON_EXPORT_SYMBOLS does
+
+*/
+
+#define CJSON_CDECL __cdecl
+#define CJSON_STDCALL __stdcall
+
+/* export symbols by default, this is necessary for copy pasting the C and
+ * header file */
+#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && \
+ !defined(CJSON_EXPORT_SYMBOLS)
+#define CJSON_EXPORT_SYMBOLS
+#endif
+
+#if defined(CJSON_HIDE_SYMBOLS)
+#define CJSON_PUBLIC(type) type CJSON_STDCALL
+#elif defined(CJSON_EXPORT_SYMBOLS)
+#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL
+#elif defined(CJSON_IMPORT_SYMBOLS)
+#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL
+#endif
+#else /* !__WINDOWS__ */
+#define CJSON_CDECL
+#define CJSON_STDCALL
+
+#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined(__SUNPRO_C)) && \
+ defined(CJSON_API_VISIBILITY)
+#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type
+#else
+#define CJSON_PUBLIC(type) type
+#endif
+#endif
+
+/* project version */
+#define CJSON_VERSION_MAJOR 1
+#define CJSON_VERSION_MINOR 7
+#define CJSON_VERSION_PATCH 18
+
+#include <stddef.h>
+
+/* cJSON Types: */
+#define cJSON_Invalid (0)
+#define cJSON_False (1 << 0)
+#define cJSON_True (1 << 1)
+#define cJSON_NULL (1 << 2)
+#define cJSON_Number (1 << 3)
+#define cJSON_String (1 << 4)
+#define cJSON_Array (1 << 5)
+#define cJSON_Object (1 << 6)
+#define cJSON_Raw (1 << 7) /* raw json */
+
+#define cJSON_IsReference 256
+#define cJSON_StringIsConst 512
+
+/* The cJSON structure: */
+typedef struct cJSON {
+ /* next/prev allow you to walk array/object chains. Alternatively, use
+ * GetArraySize/GetArrayItem/GetObjectItem */
+ struct cJSON *next;
+ struct cJSON *prev;
+ /* An array or object item will have a child pointer pointing to a chain
+ * of the items in the array/object. */
+ struct cJSON *child;
+
+ /* The type of the item, as above. */
+ int type;
+
+ /* The item's string, if type==cJSON_String and type == cJSON_Raw */
+ char *valuestring;
+ /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead
+ */
+ int valueint;
+ /* The item's number, if type==cJSON_Number */
+ double valuedouble;
+
+ /* The item's name string, if this item is the child of, or is in the
+ * list of subitems of an object. */
+ char *string;
+} cJSON;
+
+typedef struct cJSON_Hooks {
+ /* malloc/free are CDECL on Windows regardless of the default calling
+ * convention of the compiler, so ensure the hooks allow passing those
+ * functions directly. */
+ void *(CJSON_CDECL *malloc_fn)(size_t sz);
+ void(CJSON_CDECL *free_fn)(void *ptr);
+} cJSON_Hooks;
+
+typedef int cJSON_bool;
+
+/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse
+ * them. This is to prevent stack overflows. */
+#ifndef CJSON_NESTING_LIMIT
+#define CJSON_NESTING_LIMIT 1000
+#endif
+
+/* Limits the length of circular references can be before cJSON rejects to parse
+ * them. This is to prevent stack overflows. */
+#ifndef CJSON_CIRCULAR_LIMIT
+#define CJSON_CIRCULAR_LIMIT 10000
+#endif
+
+/* returns the version of cJSON as a string */
+CJSON_PUBLIC(const char *) cJSON_Version(void);
+
+/* Supply malloc, realloc and free functions to cJSON */
+CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks *hooks);
+
+/* Memory Management: the caller is always responsible to free the results from
+ * all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib
+ * free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is
+ * cJSON_PrintPreallocated, where the caller has full responsibility of the
+ * buffer. */
+/* Supply a block of JSON, and this returns a cJSON object you can interrogate.
+ */
+CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);
+CJSON_PUBLIC(cJSON *)
+cJSON_ParseWithLength(const char *value, size_t buffer_length);
+/* ParseWithOpts allows you to require (and check) that the JSON is null
+ * terminated, and to retrieve the pointer to the final byte parsed. */
+/* If you supply a ptr in return_parse_end and parsing fails, then
+ * return_parse_end will contain a pointer to the error so will match
+ * cJSON_GetErrorPtr(). */
+CJSON_PUBLIC(cJSON *)
+cJSON_ParseWithOpts(const char *value, const char **return_parse_end,
+ cJSON_bool require_null_terminated);
+CJSON_PUBLIC(cJSON *)
+cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length,
+ const char **return_parse_end,
+ cJSON_bool require_null_terminated);
+
+/* Render a cJSON entity to text for transfer/storage. */
+CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);
+/* Render a cJSON entity to text for transfer/storage without any formatting. */
+CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item);
+/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess
+ * at the final size. guessing well reduces reallocation. fmt=0 gives
+ * unformatted, =1 gives formatted */
+CJSON_PUBLIC(char *)
+cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt);
+/* Render a cJSON entity to text using a buffer already allocated in memory with
+ * given length. Returns 1 on success and 0 on failure. */
+/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will
+ * use, so to be safe allocate 5 bytes more than you actually need */
+CJSON_PUBLIC(cJSON_bool)
+cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length,
+ const cJSON_bool format);
+/* Delete a cJSON entity and all subentities. */
+CJSON_PUBLIC(void) cJSON_Delete(cJSON *item);
+
+/* Returns the number of items in an array (or object). */
+CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array);
+/* Retrieve item number "index" from array "array". Returns NULL if
+ * unsuccessful. */
+CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);
+/* Get item "string" from object. Case insensitive. */
+CJSON_PUBLIC(cJSON *)
+cJSON_GetObjectItem(const cJSON *const object, const char *const string);
+CJSON_PUBLIC(cJSON *)
+cJSON_GetObjectItemCaseSensitive(const cJSON *const object,
+ const char *const string);
+CJSON_PUBLIC(cJSON_bool)
+cJSON_HasObjectItem(const cJSON *object, const char *string);
+/* For analysing failed parses. This returns a pointer to the parse error.
+ * You'll probably need to look a few chars back to make sense of it. Defined
+ * when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
+CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void);
+
+/* Check item type and return its value */
+CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON *const item);
+CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON *const item);
+
+/* These functions check the type of an item */
+CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON *const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON *const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON *const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON *const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON *const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON *const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON *const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON *const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON *const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON *const item);
+
+/* These calls create a cJSON item of the appropriate type. */
+CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void);
+CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void);
+CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void);
+CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean);
+CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num);
+CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string);
+/* raw json */
+CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw);
+CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void);
+CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void);
+
+/* Create a string where valuestring references a string so
+ * it will not be freed by cJSON_Delete */
+CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string);
+/* Create an object/array that only references it's elements so
+ * they will not be freed by cJSON_Delete */
+CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child);
+CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child);
+
+/* These utilities create an Array of count items.
+ * The parameter count cannot be greater than the number of elements in the
+ * number array, otherwise array access will be out of bounds.*/
+CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count);
+CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count);
+CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count);
+CJSON_PUBLIC(cJSON *)
+cJSON_CreateStringArray(const char *const *strings, int count);
+
+/* Append item to the specified array/object. */
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item);
+CJSON_PUBLIC(cJSON_bool)
+cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
+/* Use this when string is definitely const (i.e. a literal, or as good as), and
+ * will definitely survive the cJSON object. WARNING: When this function was
+ * used, make sure to always check that (item->type & cJSON_StringIsConst) is
+ * zero before writing to `item->string` */
+CJSON_PUBLIC(cJSON_bool)
+cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item);
+/* Append reference to item to the specified array/object. Use this when you
+ * want to add an existing cJSON to a new cJSON, but don't want to corrupt your
+ * existing cJSON. */
+CJSON_PUBLIC(cJSON_bool)
+cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
+CJSON_PUBLIC(cJSON_bool)
+cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item);
+
+/* Remove/Detach items from Arrays/Objects. */
+CJSON_PUBLIC(cJSON *)
+cJSON_DetachItemViaPointer(cJSON *parent, cJSON *const item);
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which);
+CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which);
+CJSON_PUBLIC(cJSON *)
+cJSON_DetachItemFromObject(cJSON *object, const char *string);
+CJSON_PUBLIC(cJSON *)
+cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string);
+CJSON_PUBLIC(void)
+cJSON_DeleteItemFromObject(cJSON *object, const char *string);
+CJSON_PUBLIC(void)
+cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string);
+
+/* Update array items. */
+CJSON_PUBLIC(cJSON_bool)
+cJSON_InsertItemInArray(
+ cJSON *array, int which,
+ cJSON *newitem); /* Shifts pre-existing items to the right. */
+CJSON_PUBLIC(cJSON_bool)
+cJSON_ReplaceItemViaPointer(cJSON *const parent, cJSON *const item,
+ cJSON *replacement);
+CJSON_PUBLIC(cJSON_bool)
+cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
+CJSON_PUBLIC(cJSON_bool)
+cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem);
+CJSON_PUBLIC(cJSON_bool)
+cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string,
+ cJSON *newitem);
+
+/* Duplicate a cJSON item */
+CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse);
+/* Duplicate will create a new, identical cJSON item to the one you pass, in new
+ * memory that will need to be released. With recurse!=0, it will duplicate any
+ * children connected to the item. The item->next and ->prev pointers are always
+ * zero on return from Duplicate. */
+/* Recursively compare two cJSON items for equality. If either a or b is NULL or
+ * invalid, they will be considered unequal. case_sensitive determines if object
+ * keys are treated case sensitive (1) or case insensitive (0) */
+CJSON_PUBLIC(cJSON_bool)
+cJSON_Compare(const cJSON *const a, const cJSON *const b,
+ const cJSON_bool case_sensitive);
+
+/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from
+ * strings. The input pointer json cannot point to a read-only address area,
+ * such as a string constant, but should point to a readable and writable
+ * address area. */
+CJSON_PUBLIC(void) cJSON_Minify(char *json);
+
+/* Helper functions for creating and adding items to an object at the same time.
+ * They return the added item or NULL on failure. */
+CJSON_PUBLIC(cJSON *)
+cJSON_AddNullToObject(cJSON *const object, const char *const name);
+CJSON_PUBLIC(cJSON *)
+cJSON_AddTrueToObject(cJSON *const object, const char *const name);
+CJSON_PUBLIC(cJSON *)
+cJSON_AddFalseToObject(cJSON *const object, const char *const name);
+CJSON_PUBLIC(cJSON *)
+cJSON_AddBoolToObject(cJSON *const object, const char *const name,
+ const cJSON_bool boolean);
+CJSON_PUBLIC(cJSON *)
+cJSON_AddNumberToObject(cJSON *const object, const char *const name,
+ const double number);
+CJSON_PUBLIC(cJSON *)
+cJSON_AddStringToObject(cJSON *const object, const char *const name,
+ const char *const string);
+CJSON_PUBLIC(cJSON *)
+cJSON_AddRawToObject(cJSON *const object, const char *const name,
+ const char *const raw);
+CJSON_PUBLIC(cJSON *)
+cJSON_AddObjectToObject(cJSON *const object, const char *const name);
+CJSON_PUBLIC(cJSON *)
+cJSON_AddArrayToObject(cJSON *const object, const char *const name);
+
+/* When assigning an integer value, it needs to be propagated to valuedouble
+ * too. */
+#define cJSON_SetIntValue(object, number) \
+ ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number))
+/* helper for the cJSON_SetNumberValue macro */
+CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number);
+#define cJSON_SetNumberValue(object, number) \
+ ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number))
+/* Change the valuestring of a cJSON_String object, only takes effect when type
+ * of object is cJSON_String */
+CJSON_PUBLIC(char *)
+cJSON_SetValuestring(cJSON *object, const char *valuestring);
+
+/* If the object is not a boolean type this does nothing and returns
+ * cJSON_Invalid else it returns the new type*/
+#define cJSON_SetBoolValue(object, boolValue) \
+ ((object != NULL && ((object)->type & (cJSON_False | cJSON_True))) \
+ ? (object)->type = ((object)->type & (~(cJSON_False | cJSON_True))) | \
+ ((boolValue) ? cJSON_True : cJSON_False) \
+ : cJSON_Invalid)
+
+/* Macro for iterating over an array or object */
+#define cJSON_ArrayForEach(element, array) \
+ for (element = (array != NULL) ? (array)->child : NULL; element != NULL; \
+ element = element->next)
+
+/* malloc/free objects using the malloc/free functions that have been set with
+ * cJSON_InitHooks */
+CJSON_PUBLIC(void *) cJSON_malloc(size_t size);
+CJSON_PUBLIC(void) cJSON_free(void *object);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/lib/util.c b/lib/util.c
@@ -0,0 +1,144 @@
+/* Copyright (C) 2025 awy <awy@awy.one>
+
+ This file is part of stbar.
+
+ stbar is free software: you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation,
+ either version 3 of the License, or (at your option) any later version.
+
+ stbar is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with stbar. If not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <ctype.h>
+#include <libnotify/notify.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "util.h"
+
+void die(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+
+ if (fmt[0] && fmt[strlen(fmt) - 1] == ':') {
+ fputc(' ', stderr);
+ perror(NULL);
+ } else {
+ fputc('\n', stderr);
+ }
+
+ exit(1);
+}
+
+void sendnotif(const char *appname, const char *title, const char *body)
+{
+ notify_init(appname);
+
+ NotifyNotification *n;
+ n = notify_notification_new(title, body, NULL);
+
+ notify_notification_show(n, NULL);
+
+ g_object_unref(G_OBJECT(n));
+ notify_uninit();
+}
+
+int getbtnint(const char *blkbtn)
+{
+ char *endptr;
+ int errno;
+ long val;
+
+ errno = 0;
+
+ if (blkbtn == NULL)
+ return 0;
+
+ val = strtol(blkbtn, &endptr, 10);
+
+ if (errno == 0 && *endptr == '\0' && val >= 1 && val <= 8)
+ return (int)val;
+
+ return 0;
+}
+
+pid_t spawn(const char *const argv[])
+{
+ pid_t pid = fork();
+
+ if (pid == 0) {
+ dup2(STDERR_FILENO, STDOUT_FILENO);
+ setsid();
+ execvp(argv[0], (char *const *)argv);
+ die("spawn %s failed:", (argv)[0]);
+ }
+ return pid;
+}
+
+int find_pid_by_name(const char *process_name)
+{
+ DIR *dir = opendir("/proc");
+ if (!dir)
+ return -1;
+
+ struct dirent *entry;
+ while ((entry = readdir(dir)) != NULL) {
+ if (!isdigit(entry->d_name[0]))
+ continue;
+
+ char path[256], name[256];
+ snprintf(path, sizeof(path), "/proc/%s/comm", entry->d_name);
+
+ FILE *fp = fopen(path, "r");
+ if (!fp)
+ continue;
+
+ if (fgets(name, sizeof(name), fp)) {
+ name[strcspn(name, "\n")] = 0;
+ if (strcmp(name, process_name) == 0) {
+ fclose(fp);
+ closedir(dir);
+ return atoi(entry->d_name);
+ }
+ }
+ fclose(fp);
+ }
+ closedir(dir);
+ return -1;
+}
+
+int updatebar(const int signal)
+{
+ int pid_to_kill;
+ const int wbar = find_pid_by_name("waybar");
+ const int sblk = find_pid_by_name("someblocks");
+ const int i3blk = find_pid_by_name("i3blocks");
+
+ if (wbar == -1 && sblk == -1 && i3blk == -1)
+ die("no bar running");
+
+ if (wbar != -1)
+ pid_to_kill = wbar;
+ else if (sblk != -1)
+ pid_to_kill = sblk;
+ else
+ pid_to_kill = i3blk;
+
+ kill(pid_to_kill, SIGRTMIN + signal);
+ return 0;
+}
diff --git a/lib/util.h b/lib/util.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2025 awy <awy@awy.one>
+
+ This file is part of stbar.
+
+ stbar is free software: you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation,
+ either version 3 of the License, or (at your option) any later version.
+
+ stbar is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with stbar. If not, see
+ <https://www.gnu.org/licenses/>. */
+
+#pragma once
+#ifndef UTIL_H
+#define UTIL_H
+#include <sys/types.h>
+
+void die(const char *, ...);
+void sendnotif(const char *, const char *, const char *);
+int getbtnint(const char *blkbtn);
+pid_t spawn(const char *const argv[]);
+int find_pid_by_name(const char *process_name);
+int updatebar(const int signal);
+
+#endif
diff --git a/src/main.c b/src/main.c
@@ -0,0 +1,56 @@
+/* Copyright (C) 2025 awy <awy@awy.one>
+
+ This file is part of stbar.
+
+ stbar is free software: you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation,
+ either version 3 of the License, or (at your option) any later version.
+
+ stbar is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with stbar. If not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "stbar.h"
+
+void usage(void)
+{
+ fputs("usage:", stderr);
+}
+
+int main(int argc, char *argv[])
+{
+ const char *name = argv[0];
+
+ if (strcmp(name, "stavg") == 0) {
+ stavg();
+ } else if (strcmp(name, "stclock") == 0) {
+ stclock();
+ } else if (strcmp(name, "stmail") == 0) {
+ stmail();
+ } else if (strcmp(name, "stmemory") == 0) {
+ stmemory();
+ } else if (strcmp(name, "stmpdup") == 0) {
+ stmpdup();
+ } else if (strcmp(name, "stmusic") == 0) {
+ stmusic();
+ } else if (strcmp(name, "sttorrent") == 0) {
+ sttorrent();
+ } else if (strcmp(name, "stweath") == 0) {
+ stweath();
+ }
+ else {
+ usage();
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/src/stavg.c b/src/stavg.c
@@ -0,0 +1,40 @@
+/* Copyright (C) 2025 awy <awy@awy.one>
+
+ This file is part of stbar.
+
+ stbar is free software: you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation,
+ either version 3 of the License, or (at your option) any later version.
+
+ stbar is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with stbar. If not, see
+ <https://www.gnu.org/licenses/>. */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "../lib/util.h"
+
+int stavg(void)
+{
+ double avgs[3];
+ const float nproc = sysconf(_SC_NPROCESSORS_ONLN);
+
+ if (getloadavg(avgs, 3) < 0)
+ die("getloadavg: Failed to obtain load average");
+
+ if (avgs[0] > (nproc / 2))
+ printf("<span color='#FF0000'>%.2f</span>\n", avgs[0]);
+ else
+ printf("%.2f\n", avgs[0]);
+
+ return 0;
+}
diff --git a/src/stbar.h b/src/stbar.h
@@ -0,0 +1,32 @@
+/* Copyright (C) 2025 awy <awy@awy.one>
+
+ This file is part of stbar.
+
+ stbar is free software: you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation,
+ either version 3 of the License, or (at your option) any later version.
+
+ stbar is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with stbar. If not, see
+ <https://www.gnu.org/licenses/>. */
+
+#pragma once
+#ifndef STBAR_H
+#define STBAR_H
+
+int stavg(void);
+int stclock(void);
+int stmail(void);
+int stmemory(void);
+int stmpdup(void);
+int stmusic(void);
+int sttorrent(void);
+int stweath(void);
+
+#endif
diff --git a/src/stclock.c b/src/stclock.c
@@ -0,0 +1,76 @@
+/* Copyright (C) 2025 awy <awy@awy.one>
+
+ This file is part of stbar.
+
+ stbar is free software: you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation,
+ either version 3 of the License, or (at your option) any later version.
+
+ stbar is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with stbar. If not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include "../lib/util.h"
+
+static void buttonhandler(void)
+{
+ char *term;
+ char *env;
+ int button;
+
+ button = 0;
+ if ((env = getenv("BLOCK_BUTTON")))
+ button = getbtnint(env);
+
+ if (!(term = getenv("TERMINAL")))
+ term = "footclient";
+
+ const char *termcmd[] = {term, "-e", "calcurse", NULL};
+
+ switch (button) {
+ /* case 1:
+ sendnotif ("stclock", "This Month",
+ "there should be calendar but you know how we doing");
+ break; */
+ case 2:
+ spawn(termcmd);
+ break;
+ /* case 3:
+ sendnotif ("stclock", " Time/date module",
+ "- Left click to show upcoming \
+appointments for the next three days\
+via `calcurse -d3` and show the month via `cal`\n\
+- Middle click opens calcurse if installed");
+ break; */
+ default:
+ break;
+ }
+}
+
+int stclock(void)
+{
+ time_t t;
+ char buf[64];
+
+ buttonhandler();
+
+ t = time(NULL);
+ /* if (!strftime (buf, sizeof (buf), "%a %b %d %H:%M", localtime (&t)))
+ */
+ /* if (!strftime (buf, sizeof (buf), "%F %T", localtime (&t))) */
+ if (!strftime(buf, sizeof(buf), "%F %T", localtime(&t)))
+ die("strftime: Result string exceeds buffer size");
+
+ printf("%s\n", buf);
+ return 0;
+}
diff --git a/src/stmail.c b/src/stmail.c
@@ -0,0 +1,134 @@
+/* Copyright (C) 2025 awy <awy@awy.one>
+
+ This file is part of stbar.
+
+ stbar is free software: you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation,
+ either version 3 of the License, or (at your option) any later version.
+
+ stbar is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with stbar. If not, see
+ <https://www.gnu.org/licenses/>. */
+
+#define _DEFAULT_SOURCE
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <dirent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "../lib/util.h"
+
+static void buttonhandler(void)
+{
+ char *term;
+ char *env;
+ int button;
+ pid_t pid;
+
+ button = 0;
+ if ((env = getenv("BLOCK_BUTTON")))
+ button = getbtnint(env);
+
+ if (!(term = getenv("TERMINAL")))
+ term = "footclient";
+
+ const char *neomutt[] = {"sh", "-c", "setsid -w -f \"$TERMINAL\" -e neomutt",
+ NULL};
+ const char *mailup[] = {"mailup", NULL};
+
+ switch (button) {
+ case 1:
+ pid = spawn(neomutt);
+ int status;
+ waitpid(pid, &status, 0);
+ updatebar(12);
+ break;
+ case 2:
+ spawn(mailup);
+ break;
+ /* case 3:
+ sendnotif ("stmail", " Mail module",
+ "- Shows unread mail\n- Shows if syncing mail\n\
+- Left click opens neomutt\n\
+- Middle click to mute.");
+ break; */
+ default:
+ break;
+ }
+}
+
+int newmsg(char path[1024])
+{
+ DIR *dir;
+ int count;
+ struct dirent *entry;
+
+ if (!(dir = opendir(path)))
+ die("failed to open dir");
+
+ count = 0;
+ while ((entry = readdir(dir)) != NULL) {
+ if (entry->d_type == DT_REG) {
+ if (strcmp(entry->d_name, ".") && strcmp(entry->d_name, "..")) {
+ count++;
+ }
+ }
+ }
+ return count;
+}
+
+int stmail(void)
+{
+ DIR *dir;
+ int count;
+ char *env;
+ char buf[1024];
+ char path[1024];
+ struct dirent *entry;
+
+ buttonhandler();
+
+ if (!(env = getenv("XDG_DATA_HOME")))
+ die("XDG_DATA_HOME is not set");
+
+ snprintf(buf, sizeof(buf), "%s/mail/", env);
+
+ if (!(dir = opendir(buf)))
+ die("failed to open dir");
+
+ count = 0;
+ while ((entry = readdir(dir)) != NULL) {
+ if (entry->d_type == DT_DIR) {
+ if (strcmp(entry->d_name, ".") && strcmp(entry->d_name, "..") &&
+ strcmp(entry->d_name, ".notmuch")) {
+ strcpy(path, buf);
+ strcat(path, entry->d_name);
+ strcat(path, "/INBOX/new/");
+ count += newmsg(path);
+ }
+ }
+ }
+
+ closedir(dir);
+
+ if (access("/tmp/mailupdate", F_OK) != -1) {
+ printf("<span color='#7F7F7F'>[updating]</span>\n");
+ return 0;
+ }
+
+ if (count == 0)
+ return 0;
+
+ printf("%d\n", count);
+ return 0;
+}
diff --git a/src/stmemory.c b/src/stmemory.c
@@ -0,0 +1,81 @@
+/* Copyright (C) 2025 awy <awy@awy.one>
+
+ This file is part of stbar.
+
+ stbar is free software: you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation,
+ either version 3 of the License, or (at your option) any later version.
+
+ stbar is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with stbar. If not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "../lib/util.h"
+
+static void buttonhandler(void)
+{
+ char *term;
+ char *env;
+ int button;
+
+ button = 0;
+ if ((env = getenv("BLOCK_BUTTON")))
+ button = getbtnint(env);
+
+ if (!(term = getenv("TERMINAL")))
+ term = "footclient";
+
+ const char *termcmd[] = {term, "-e", "btop", NULL};
+
+ switch (button) {
+ case 1:
+ break;
+ case 2:
+ spawn(termcmd);
+ break;
+ case 3:
+ sendnotif("stmemory", " Memory module",
+ "- Shows Memory Used.\n- Click to show memory hogs.\n\
+- Middle click to open btop.");
+ break;
+ default:
+ break;
+ }
+}
+
+int stmemory(void)
+{
+ FILE *fp;
+ char line[256];
+ long total, free;
+ double totalf, freef;
+
+ buttonhandler();
+
+ if (!(fp = fopen("/proc/meminfo", "r")))
+ die("failed to open: /proc/meminfo");
+
+ fgets(line, sizeof(line), fp);
+ sscanf(line + 9, "%ld", &total);
+
+ fgets(line, sizeof(line), fp);
+ fgets(line, sizeof(line), fp);
+ sscanf(line + 13, "%ld", &free);
+
+ totalf = total / (1024.0 * 1024.0);
+ freef = free / (1024.0 * 1024.0);
+
+ printf("%.2fGiB\n", (totalf - freef));
+ fclose(fp);
+
+ return 0;
+}
diff --git a/src/stmpdup.c b/src/stmpdup.c
@@ -0,0 +1,79 @@
+/* Copyright (C) 2025 awy <awy@awy.one>
+
+ This file is part of stbar.
+
+ stbar is free software: you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation,
+ either version 3 of the License, or (at your option) any later version.
+
+ stbar is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with stbar. If not, see
+ <https://www.gnu.org/licenses/>. */
+
+/* cmd_idle and cmd_idleloop functions are taken from the
+ MPC <https://github.com/MusicPlayerDaemon/mpc> */
+
+#define _POSIX_C_SOURCE 200112L
+#include <dirent.h>
+#include <mpd/client.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "../lib/util.h"
+
+int cmd_idle(struct mpd_connection *connection)
+{
+ enum mpd_idle idle = 0;
+ pid_t wbarpid;
+
+ idle = idle == 0 ? mpd_run_idle(connection)
+ : mpd_run_idle_mask(connection, idle);
+ if (idle == 0 && mpd_connection_get_error(connection) != MPD_ERROR_SUCCESS)
+ printf("error");
+
+ for (unsigned j = 0;; ++j) {
+ enum mpd_idle i = 1 << j;
+ const char *name = mpd_idle_name(i);
+
+ if (name == NULL)
+ break;
+
+ if (idle & i) {
+ updatebar(11);
+ }
+ }
+
+ return 0;
+}
+
+int cmd_idleloop(struct mpd_connection *connection)
+{
+ while (true) {
+ int ret = cmd_idle(connection);
+ fflush(stdout);
+ if (ret != 0)
+ return ret;
+ }
+}
+
+int stmpdup(void)
+{
+ struct mpd_connection *conn;
+
+ conn = mpd_connection_new(NULL, 0, 30000);
+ if (mpd_connection_get_error(conn) != MPD_ERROR_SUCCESS) {
+ fprintf(stderr, "MPD connection error: %s\n",
+ mpd_connection_get_error_message(conn));
+ return 1;
+ }
+
+ cmd_idleloop(conn);
+ return 0;
+}
diff --git a/src/stmusic.c b/src/stmusic.c
@@ -0,0 +1,169 @@
+/* Copyright (C) 2025 awy <awy@awy.one>
+
+ This file is part of stbar.
+
+ stbar is free software: you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation,
+ either version 3 of the License, or (at your option) any later version.
+
+ stbar is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with stbar. If not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <mpd/client.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "../lib/util.h"
+
+static void buttonhandler(void)
+{
+ char *term;
+ char *env;
+ int button;
+
+ button = 0;
+ if ((env = getenv("BLOCK_BUTTON")))
+ button = getbtnint(env);
+
+ if (!(term = getenv("TERMINAL")))
+ term = "footclient";
+
+ const char *rmpc[] = {term, "-e", "rmpc", NULL};
+ const char *toggle[] = {"rmpc", "togglepause", NULL};
+ const char *prev[] = {"rmpc", "prev", NULL};
+ const char *next[] = {"rmpc", "next", NULL};
+
+ switch (button) {
+ case 1:
+ spawn(rmpc);
+ break;
+ case 2:
+ spawn(toggle);
+ break;
+ case 3:
+ sendnotif("stmusic", " Music module", "- Shows mpd song playing.\n\
+- paused.\n\
+- repeat mode.\n\
+- shuffle mode.\n\
+- consume mode.\n\
+- consume oneshot mode.\n\
+- single mode.\n\
+- single oneshot mode.\n\
+- Left click opens rmpc.\n\
+- Middle click pauses.\n\
+- Scroll changes track.");
+ break;
+ case 4:
+ spawn(prev);
+ break;
+ case 5:
+ spawn(next);
+ break;
+ default:
+ break;
+ }
+}
+
+int stmusic(void)
+{
+ struct mpd_connection *conn;
+ struct mpd_status *status;
+ struct mpd_song *song;
+
+ buttonhandler();
+
+ /* Connect to MPD (default: localhost:6600) */
+ conn = mpd_connection_new(NULL, 0, 30000);
+ if (mpd_connection_get_error(conn) != MPD_ERROR_SUCCESS) {
+ fprintf(stderr, "MPD connection error: %s\n",
+ mpd_connection_get_error_message(conn));
+ return 1;
+ }
+
+ /* Get MPD status */
+ status = mpd_run_status(conn);
+ if (!status) {
+ fprintf(stderr, "Failed to get status: %s\n",
+ mpd_connection_get_error_message(conn));
+ mpd_connection_free(conn);
+ return 1;
+ }
+
+ enum mpd_state st = mpd_status_get_state(status);
+
+ /* Don't print anything if mpd is stopped */
+ if (st == MPD_STATE_STOP) {
+ return 0;
+ }
+
+ if (st == MPD_STATE_PAUSE) {
+ printf(" ");
+ };
+
+ /* Get current song */
+ song = mpd_run_current_song(conn);
+
+ if (song) {
+ const char *artist;
+ const char *title = mpd_song_get_tag(song, MPD_TAG_TITLE, 0);
+
+ // loop through all artists
+ for (unsigned i = 0; (artist = mpd_song_get_tag(song, MPD_TAG_ARTIST, i));
+ ++i) {
+ if (i > 0)
+ printf(", "); // separator between artists
+ printf("%s", artist);
+ }
+
+ printf(" - %s", title ? title : "unknown");
+
+ mpd_song_free(song);
+ }
+
+ if (mpd_status_get_repeat(status)) {
+ printf(" ");
+ };
+ if (mpd_status_get_random(status)) {
+ printf(" ");
+ };
+
+ enum mpd_consume_state consumest = mpd_status_get_consume_state(status);
+ switch (consumest) {
+ case MPD_CONSUME_ONESHOT:
+ printf(" ");
+ break;
+ case MPD_CONSUME_ON:
+ printf(" ");
+ break;
+ case MPD_CONSUME_UNKNOWN:
+ break;
+ case MPD_CONSUME_OFF:
+ break;
+ }
+
+ enum mpd_single_state singlest = mpd_status_get_single_state(status);
+ switch (singlest) {
+ case MPD_SINGLE_ONESHOT:
+ printf(" ");
+ break;
+ case MPD_SINGLE_ON:
+ printf(" ");
+ break;
+ case MPD_CONSUME_UNKNOWN:
+ break;
+ case MPD_CONSUME_OFF:
+ break;
+ }
+
+ puts("");
+ mpd_status_free(status);
+ mpd_connection_free(conn);
+ return 0;
+}
diff --git a/src/sttorrent.c b/src/sttorrent.c
@@ -0,0 +1,234 @@
+/* Copyright (C) 2025 awy <awy@awy.one>
+
+ This file is part of stbar.
+
+ stbar is free software: you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation,
+ either version 3 of the License, or (at your option) any later version.
+
+ stbar is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with stbar. If not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <curl/curl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../lib/cjson/cJSON.h"
+#include "../lib/util.h"
+
+static void buttonhandler(void)
+{
+ char *term;
+ char *env;
+ int button;
+
+ button = 0;
+ if ((env = getenv("BLOCK_BUTTON")))
+ button = getbtnint(env);
+
+ if (!(term = getenv("TERMINAL")))
+ term = "footclient";
+
+ const char *stig[] = {term, "-e", "stig", NULL};
+ const char *toggle[] = {"td-toggle", NULL};
+
+ switch (button) {
+ case 1:
+ spawn(stig);
+ break;
+ case 2:
+ spawn(toggle);
+ break;
+ case 3:
+ sendnotif("sttorrent", " Torrent module", "- Left click to open stig.\n\
+- Middle click to toggle transmission.");
+ break;
+ default:
+ break;
+ }
+}
+
+struct MemoryStruct {
+ char *memory;
+ size_t size;
+};
+
+static size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb,
+ void *userp)
+{
+ size_t realsize = size * nmemb;
+ struct MemoryStruct *mem = (struct MemoryStruct *)userp;
+
+ mem->memory = realloc(mem->memory, mem->size + realsize + 1);
+ if (mem->memory == NULL) {
+ /* out of memory! */
+ puts("not enough memory (realloc returned NULL)");
+ return 0;
+ }
+
+ memcpy(&(mem->memory[mem->size]), contents, realsize);
+ mem->size += realsize;
+ mem->memory[mem->size] = 0;
+
+ return realsize;
+}
+
+int get_session_id(char id[75])
+{
+ CURL *handle;
+ CURLcode res;
+ struct MemoryStruct chunk;
+
+ chunk.memory = malloc(1);
+ chunk.size = 0;
+
+ handle = curl_easy_init();
+
+ if (!handle) {
+ return 1;
+ }
+
+ curl_easy_setopt(handle, CURLOPT_URL,
+ "http://localhost:9091/transmission/rpc");
+ curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
+ curl_easy_setopt(handle, CURLOPT_WRITEDATA, (void *)&chunk);
+ curl_easy_setopt(handle, CURLOPT_HEADERDATA, (void *)&chunk);
+
+ res = curl_easy_perform(handle);
+
+ if (res != CURLE_OK) {
+ return 1;
+ }
+ int nl = 0;
+ int idx = 0;
+ char buff[128] = {'\0'};
+
+ for (size_t i = 0; i < chunk.size; i++) {
+ if (nl > 3) {
+ break;
+ }
+ if (chunk.memory[i] == '\n') {
+ nl++;
+ }
+ if (nl == 3) {
+ buff[idx] = chunk.memory[i + 1];
+ idx++;
+ }
+ }
+ /* cleanup \r\n garbage at the end of the buff*/
+ buff[strcspn(buff, "\r\n")] = 0;
+ strcpy(id, buff);
+ curl_easy_cleanup(handle);
+ if (chunk.memory)
+ free(chunk.memory);
+ return 0;
+}
+
+int sttorrent(void)
+{
+ char id[75];
+ CURL *handle;
+ CURLcode res;
+ cJSON *root, *arguments, *torrents;
+ int status_count[7] = {0};
+ struct MemoryStruct chunk;
+ struct curl_slist *headers = NULL;
+ const char *json = "{\"method\":\"torrent-get\",\"arguments\":{\"fields\":["
+ "\"status\", \"percentDone\"]}}";
+
+ buttonhandler();
+
+ curl_global_init(CURL_GLOBAL_ALL);
+ /* To get status from transmission we need to send
+ two CURL requests, first one to obtain session id. */
+ get_session_id(id);
+
+ chunk.memory = malloc(1);
+ chunk.size = 0;
+
+ handle = curl_easy_init();
+
+ if (!handle) {
+ return 1;
+ }
+
+ curl_easy_setopt(handle, CURLOPT_URL, "localhost:9091/transmission/rpc");
+ curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
+ curl_easy_setopt(handle, CURLOPT_WRITEDATA, (void *)&chunk);
+ headers = curl_slist_append(headers, "Content-Type: application/json");
+ headers = curl_slist_append(headers, "Accept: application/json");
+ headers = curl_slist_append(headers, id);
+ curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
+ curl_easy_setopt(handle, CURLOPT_POST, 1L);
+ curl_easy_setopt(handle, CURLOPT_POSTFIELDS, json);
+
+ res = curl_easy_perform(handle);
+
+ if (res != CURLE_OK) {
+ return 1;
+ }
+
+ /* JSON parsing */
+ root = cJSON_Parse(chunk.memory);
+
+ /* Cleanup */
+ curl_easy_cleanup(handle);
+ if (chunk.memory)
+ free(chunk.memory);
+ curl_global_cleanup();
+
+ arguments = cJSON_GetObjectItem(root, "arguments");
+ torrents = cJSON_GetObjectItem(arguments, "torrents");
+
+ if (cJSON_GetArraySize(torrents) == 0) {
+ cJSON_Delete(root);
+ return 0;
+ }
+
+ /* Counting statuses */
+ for (int i = 0; i < cJSON_GetArraySize(torrents); i++) {
+ cJSON *val = cJSON_GetArrayItem(torrents, i);
+ cJSON *status = cJSON_GetObjectItem(val, "status");
+ if (status) {
+ int s = status->valueint;
+ status_count[s]++;
+ }
+ }
+
+ for (int i = 0; i < 7; i++) {
+ if (status_count[i] == 0) {
+ continue;
+ }
+ switch (i) {
+ case 0:
+ printf(" ");
+ break;
+ case 3:
+ printf(" ");
+ break;
+ case 4:
+ printf(" ");
+ break;
+ case 5:
+ printf(" ");
+ break;
+ case 6:
+ printf(" ");
+ break;
+ default:
+ break;
+ }
+ printf("%d", status_count[i]);
+ }
+
+ cJSON_Delete(root);
+ return 0;
+}
diff --git a/src/stweath.c b/src/stweath.c
@@ -0,0 +1,201 @@
+/* Copyright (C) 2025 awy <awy@awy.one>
+
+ This file is part of stbar.
+
+ stbar is free software: you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation,
+ either version 3 of the License, or (at your option) any later version.
+
+ stbar is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with stbar. If not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <sys/stat.h>
+
+#include <curl/curl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "../lib/cjson/cJSON.h"
+#include "../lib/util.h"
+
+typedef struct {
+ int code;
+ const char *icon;
+} WeatherIcon;
+
+const char *geticon(int code)
+{
+ static const WeatherIcon icons[] = {
+ {113, ""}, {116, ""}, {119, ""}, {122, ""}, {143, ""},
+ {248, ""}, {260, ""}, {176, ""}, {179, ""}, {182, ""},
+ {185, ""}, {263, ""}, {266, ""}, {281, ""}, {284, ""},
+ {293, ""}, {296, ""}, {299, ""}, {302, ""}, {305, ""},
+ {308, ""}, {311, ""}, {314, ""}, {317, ""}, {350, ""},
+ {353, ""}, {356, ""}, {359, ""}, {362, ""}, {365, ""},
+ {368, ""}, {392, ""}, {200, ""}, {227, ""}, {230, ""},
+ {320, ""}, {323, ""}, {326, ""}, {374, ""}, {377, ""},
+ {386, ""}, {389, ""}, {329, ""}, {332, ""}, {335, ""},
+ {338, ""}, {371, ""}, {395, ""}};
+ size_t n = sizeof(icons) / sizeof(icons[0]);
+ for (size_t i = 0; i < n; i++) {
+ if (icons[i].code == code)
+ return icons[i].icon;
+ }
+ return "unknown";
+}
+
+int getforecast(const char *path)
+{
+ FILE *fp;
+ CURL *handle;
+ CURLcode res;
+
+ if (!(fp = fopen(path, "w")))
+ die("failed to open report:");
+
+ handle = curl_easy_init();
+
+ if (!handle) {
+ fclose(fp);
+ die("failed to create curl handle");
+ }
+
+ curl_easy_setopt(handle, CURLOPT_URL, "https://wttr.in/?format=j1");
+ curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, NULL);
+ curl_easy_setopt(handle, CURLOPT_WRITEDATA, fp);
+
+ res = curl_easy_perform(handle);
+
+ if (res != CURLE_OK) {
+ fclose(fp);
+ die("curl failed");
+ }
+
+ curl_easy_cleanup(handle);
+
+ fclose(fp);
+ return 0;
+}
+
+int showforecast(const char *path)
+{
+ FILE *fp;
+ long size;
+ char *data;
+ const char *icon;
+ cJSON *root, *temp, *wcode, *rain;
+
+ if (!(fp = fopen(path, "rb")))
+ die("fopen:");
+
+ fseek(fp, 0, SEEK_END);
+ size = ftell(fp);
+ fseek(fp, 0, SEEK_SET);
+
+ data = malloc(size + 1);
+ if (!(data = malloc(size + 1))) {
+ fclose(fp);
+ die("failed to allocate memory");
+ }
+
+ fread(data, 1, size, fp);
+ data[size] = '\0';
+ fclose(fp);
+
+ if (!(root = cJSON_Parse(data))) {
+ free(data);
+ die("failed to parse json");
+ }
+
+ rain = cJSON_GetArrayItem(cJSON_GetObjectItem(root, "weather"), 0);
+ rain = cJSON_GetArrayItem(cJSON_GetObjectItem(rain, "hourly"), 0);
+ rain = cJSON_GetObjectItem(rain, "chanceofrain");
+ temp = cJSON_GetObjectItem(root, "current_condition");
+ temp = cJSON_GetArrayItem(temp, 0);
+ wcode = cJSON_GetObjectItem(temp, "weatherCode");
+ temp = cJSON_GetObjectItem(temp, "temp_C");
+
+ icon = geticon(atoi(wcode->valuestring));
+
+ if (atoi(rain->valuestring) != 0)
+ printf(" %s%% ", rain->valuestring);
+
+ printf("%s %s°C", icon, temp->valuestring);
+
+ cJSON_Delete(root);
+ return 0;
+}
+
+int checkforecast(const char *path)
+{
+ time_t now;
+ char *buff;
+ struct stat st;
+ int tries, force;
+
+ /* check if there is WEATHERFORCEUPDATE
+ environment variable */
+ force = 0;
+ buff = getenv("WEATHERFORCEUPDATE");
+ if (buff && buff[0] == '1') {
+ force = 1;
+ }
+
+ if (access(path, F_OK) != -1) {
+ if (stat(path, &st) != 0)
+ die("failed to get modification time");
+ } else {
+ force = 1;
+ }
+
+ now = time(NULL);
+ if (now == ((time_t)-1)) {
+ return 1;
+ }
+
+ /* check if 30 mins passed
+ then get new forecast */
+ if (((now - st.st_mtime) >= 1800) || force) {
+ tries = 0;
+ /* try 100 fast retries */
+ while (tries < 100) {
+ if (getforecast(path) == 0) {
+ break;
+ }
+ tries++;
+ sleep(1);
+ }
+ }
+
+ showforecast(path);
+ return 0;
+}
+
+int stweath(void)
+{
+ char *env;
+ char path[1024];
+
+ if (!(env = getenv("XDG_CACHE_HOME")))
+ die("XDG_CACHE_HOME is not set");
+
+ strcpy(path, env);
+ strcat(path, "/weatherreport.json");
+
+ curl_global_init(CURL_GLOBAL_ALL);
+
+ checkforecast(path);
+
+ curl_global_cleanup();
+ return 0;
+}