diff --git a/LICENSES/CC-BY-4.0.txt b/LICENSES/CC-BY-4.0.txt new file mode 100644 index 000000000..13ca539f3 --- /dev/null +++ b/LICENSES/CC-BY-4.0.txt @@ -0,0 +1,156 @@ +Creative Commons Attribution 4.0 International + + Creative Commons Corporation (“Creative Commons”) is not a law firm and does not provide legal services or legal advice. Distribution of Creative Commons public licenses does not create a lawyer-client or other relationship. Creative Commons makes its licenses and related information available on an “as-is” basis. Creative Commons gives no warranties regarding its licenses, any material licensed under their terms and conditions, or any related information. Creative Commons disclaims all liability for damages resulting from their use to the fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and conditions that creators and other rights holders may use to share original works of authorship and other material subject to copyright and certain other rights specified in the public license below. The following considerations are for informational purposes only, are not exhaustive, and do not form part of our licenses. + +Considerations for licensors: Our public licenses are intended for use by those authorized to give the public permission to use material in ways otherwise restricted by copyright and certain other rights. Our licenses are irrevocable. Licensors should read and understand the terms and conditions of the license they choose before applying it. Licensors should also secure all rights necessary before applying our licenses so that the public can reuse the material as expected. Licensors should clearly mark any material not subject to the license. This includes other CC-licensed material, or material used under an exception or limitation to copyright. More considerations for licensors. + +Considerations for the public: By using one of our public licenses, a licensor grants the public permission to use the licensed material under specified terms and conditions. If the licensor’s permission is not necessary for any reason–for example, because of any applicable exception or limitation to copyright–then that use is not regulated by the license. Our licenses grant only permissions under copyright and certain other rights that a licensor has authority to grant. Use of the licensed material may still be restricted for other reasons, including because others have copyright or other rights in the material. A licensor may make special requests, such as asking that all changes be marked or described. Although not required by our licenses, you are encouraged to respect those requests where reasonable. More considerations for the public. + +Creative Commons Attribution 4.0 International Public License + +By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions. + +Section 1 – Definitions. + + a. Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image. + + b. Adapter's License means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License. + + c. Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. + + d. Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements. + + e. Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material. + + f. Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License. + + g. Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license. + + h. Licensor means the individual(s) or entity(ies) granting rights under this Public License. + + i. Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them. + + j. Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world. + + k. You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning. + +Section 2 – Scope. + + a. License grant. + + 1. Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to: + + A. reproduce and Share the Licensed Material, in whole or in part; and + + B. produce, reproduce, and Share Adapted Material. + + 2. Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions. + + 3. Term. The term of this Public License is specified in Section 6(a). + + 4. Media and formats; technical modifications allowed. The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a)(4) never produces Adapted Material. + + 5. Downstream recipients. + + A. Offer from the Licensor – Licensed Material. Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License. + + B. No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material. + + 6. No endorsement. Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i). + +b. Other rights. + + 1. Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise. + + 2. Patent and trademark rights are not licensed under this Public License. + + 3. To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties. + +Section 3 – License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the following conditions. + + a. Attribution. + + 1. If You Share the Licensed Material (including in modified form), You must: + + A. retain the following if it is supplied by the Licensor with the Licensed Material: + + i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of warranties; + + v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable; + + B. indicate if You modified the Licensed Material and retain an indication of any previous modifications; and + + C. indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License. + + 2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information. + + 3. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable. + + 4. If You Share Adapted Material You produce, the Adapter's License You apply must not prevent recipients of the Adapted Material from complying with this Public License. + +Section 4 – Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material: + + a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database; + + b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material; and + + c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database. +For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights. + +Section 5 – Disclaimer of Warranties and Limitation of Liability. + + a. Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this disclaimer may not apply to You. + + b. To the extent possible, in no event will the Licensor be liable to You on any legal theory (including, without limitation, negligence) or otherwise for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising out of this Public License or use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. Where a limitation of liability is not allowed in full or in part, this limitation may not apply to You. + + c. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability. + +Section 6 – Term and Termination. + + a. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically. + + b. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates: + + 1. automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or + + 2. upon express reinstatement by the Licensor. + + c. For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License. + + d. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License. + + e. Sections 1, 5, 6, 7, and 8 survive termination of this Public License. + +Section 7 – Other Terms and Conditions. + + a. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed. + + b. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License. + +Section 8 – Interpretation. + + a. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License. + + b. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions. + + c. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor. + + d. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority. + +Creative Commons is not a party to its public licenses. Notwithstanding, Creative Commons may elect to apply one of its public licenses to material it publishes and in those instances will be considered the “Licensor.” Except for the limited purpose of indicating that material is shared under a Creative Commons public license or as otherwise permitted by the Creative Commons policies published at creativecommons.org/policies, Creative Commons does not authorize the use of the trademark “Creative Commons” or any other trademark or logo of Creative Commons without its prior written consent including, without limitation, in connection with any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or agreements concerning use of licensed material. For the avoidance of doubt, this paragraph does not form part of the public licenses. + +Creative Commons may be contacted at creativecommons.org. diff --git a/LICENSES/EUPL-1.2.txt b/LICENSES/EUPL-1.2.txt new file mode 100644 index 000000000..4153cd377 --- /dev/null +++ b/LICENSES/EUPL-1.2.txt @@ -0,0 +1,287 @@ + EUROPEAN UNION PUBLIC LICENCE v. 1.2 + EUPL © the European Union 2007, 2016 + +This European Union Public Licence (the ‘EUPL’) applies to the Work (as defined +below) which is provided under the terms of this Licence. Any use of the Work, +other than as authorised under this Licence is prohibited (to the extent such +use is covered by a right of the copyright holder of the Work). + +The Work is provided under the terms of this Licence when the Licensor (as +defined below) has placed the following notice immediately following the +copyright notice for the Work: + + Licensed under the EUPL + +or has expressed by any other means his willingness to license under the EUPL. + +1. Definitions + +In this Licence, the following terms have the following meaning: + +- ‘The Licence’: this Licence. + +- ‘The Original Work’: the work or software distributed or communicated by the + Licensor under this Licence, available as Source Code and also as Executable + Code as the case may be. + +- ‘Derivative Works’: the works or software that could be created by the + Licensee, based upon the Original Work or modifications thereof. This Licence + does not define the extent of modification or dependence on the Original Work + required in order to classify a work as a Derivative Work; this extent is + determined by copyright law applicable in the country mentioned in Article 15. + +- ‘The Work’: the Original Work or its Derivative Works. + +- ‘The Source Code’: the human-readable form of the Work which is the most + convenient for people to study and modify. + +- ‘The Executable Code’: any code which has generally been compiled and which is + meant to be interpreted by a computer as a program. + +- ‘The Licensor’: the natural or legal person that distributes or communicates + the Work under the Licence. + +- ‘Contributor(s)’: any natural or legal person who modifies the Work under the + Licence, or otherwise contributes to the creation of a Derivative Work. + +- ‘The Licensee’ or ‘You’: any natural or legal person who makes any usage of + the Work under the terms of the Licence. + +- ‘Distribution’ or ‘Communication’: any act of selling, giving, lending, + renting, distributing, communicating, transmitting, or otherwise making + available, online or offline, copies of the Work or providing access to its + essential functionalities at the disposal of any other natural or legal + person. + +2. Scope of the rights granted by the Licence + +The Licensor hereby grants You a worldwide, royalty-free, non-exclusive, +sublicensable licence to do the following, for the duration of copyright vested +in the Original Work: + +- use the Work in any circumstance and for all usage, +- reproduce the Work, +- modify the Work, and make Derivative Works based upon the Work, +- communicate to the public, including the right to make available or display + the Work or copies thereof to the public and perform publicly, as the case may + be, the Work, +- distribute the Work or copies thereof, +- lend and rent the Work or copies thereof, +- sublicense rights in the Work or copies thereof. + +Those rights can be exercised on any media, supports and formats, whether now +known or later invented, as far as the applicable law permits so. + +In the countries where moral rights apply, the Licensor waives his right to +exercise his moral right to the extent allowed by law in order to make effective +the licence of the economic rights here above listed. + +The Licensor grants to the Licensee royalty-free, non-exclusive usage rights to +any patents held by the Licensor, to the extent necessary to make use of the +rights granted on the Work under this Licence. + +3. Communication of the Source Code + +The Licensor may provide the Work either in its Source Code form, or as +Executable Code. If the Work is provided as Executable Code, the Licensor +provides in addition a machine-readable copy of the Source Code of the Work +along with each copy of the Work that the Licensor distributes or indicates, in +a notice following the copyright notice attached to the Work, a repository where +the Source Code is easily and freely accessible for as long as the Licensor +continues to distribute or communicate the Work. + +4. Limitations on copyright + +Nothing in this Licence is intended to deprive the Licensee of the benefits from +any exception or limitation to the exclusive rights of the rights owners in the +Work, of the exhaustion of those rights or of other applicable limitations +thereto. + +5. Obligations of the Licensee + +The grant of the rights mentioned above is subject to some restrictions and +obligations imposed on the Licensee. Those obligations are the following: + +Attribution right: The Licensee shall keep intact all copyright, patent or +trademarks notices and all notices that refer to the Licence and to the +disclaimer of warranties. The Licensee must include a copy of such notices and a +copy of the Licence with every copy of the Work he/she distributes or +communicates. The Licensee must cause any Derivative Work to carry prominent +notices stating that the Work has been modified and the date of modification. + +Copyleft clause: If the Licensee distributes or communicates copies of the +Original Works or Derivative Works, this Distribution or Communication will be +done under the terms of this Licence or of a later version of this Licence +unless the Original Work is expressly distributed only under this version of the +Licence — for example by communicating ‘EUPL v. 1.2 only’. The Licensee +(becoming Licensor) cannot offer or impose any additional terms or conditions on +the Work or Derivative Work that alter or restrict the terms of the Licence. + +Compatibility clause: If the Licensee Distributes or Communicates Derivative +Works or copies thereof based upon both the Work and another work licensed under +a Compatible Licence, this Distribution or Communication can be done under the +terms of this Compatible Licence. For the sake of this clause, ‘Compatible +Licence’ refers to the licences listed in the appendix attached to this Licence. +Should the Licensee's obligations under the Compatible Licence conflict with +his/her obligations under this Licence, the obligations of the Compatible +Licence shall prevail. + +Provision of Source Code: When distributing or communicating copies of the Work, +the Licensee will provide a machine-readable copy of the Source Code or indicate +a repository where this Source will be easily and freely available for as long +as the Licensee continues to distribute or communicate the Work. + +Legal Protection: This Licence does not grant permission to use the trade names, +trademarks, service marks, or names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the copyright notice. + +6. Chain of Authorship + +The original Licensor warrants that the copyright in the Original Work granted +hereunder is owned by him/her or licensed to him/her and that he/she has the +power and authority to grant the Licence. + +Each Contributor warrants that the copyright in the modifications he/she brings +to the Work are owned by him/her or licensed to him/her and that he/she has the +power and authority to grant the Licence. + +Each time You accept the Licence, the original Licensor and subsequent +Contributors grant You a licence to their contributions to the Work, under the +terms of this Licence. + +7. Disclaimer of Warranty + +The Work is a work in progress, which is continuously improved by numerous +Contributors. It is not a finished work and may therefore contain defects or +‘bugs’ inherent to this type of development. + +For the above reason, the Work is provided under the Licence on an ‘as is’ basis +and without warranties of any kind concerning the Work, including without +limitation merchantability, fitness for a particular purpose, absence of defects +or errors, accuracy, non-infringement of intellectual property rights other than +copyright as stated in Article 6 of this Licence. + +This disclaimer of warranty is an essential part of the Licence and a condition +for the grant of any rights to the Work. + +8. Disclaimer of Liability + +Except in the cases of wilful misconduct or damages directly caused to natural +persons, the Licensor will in no event be liable for any direct or indirect, +material or moral, damages of any kind, arising out of the Licence or of the use +of the Work, including without limitation, damages for loss of goodwill, work +stoppage, computer failure or malfunction, loss of data or any commercial +damage, even if the Licensor has been advised of the possibility of such damage. +However, the Licensor will be liable under statutory product liability laws as +far such laws apply to the Work. + +9. Additional agreements + +While distributing the Work, You may choose to conclude an additional agreement, +defining obligations or services consistent with this Licence. However, if +accepting obligations, You may act only on your own behalf and on your sole +responsibility, not on behalf of the original Licensor or any other Contributor, +and only if You agree to indemnify, defend, and hold each Contributor harmless +for any liability incurred by, or claims asserted against such Contributor by +the fact You have accepted any warranty or additional liability. + +10. Acceptance of the Licence + +The provisions of this Licence can be accepted by clicking on an icon ‘I agree’ +placed under the bottom of a window displaying the text of this Licence or by +affirming consent in any other similar way, in accordance with the rules of +applicable law. Clicking on that icon indicates your clear and irrevocable +acceptance of this Licence and all of its terms and conditions. + +Similarly, you irrevocably accept this Licence and all of its terms and +conditions by exercising any rights granted to You by Article 2 of this Licence, +such as the use of the Work, the creation by You of a Derivative Work or the +Distribution or Communication by You of the Work or copies thereof. + +11. Information to the public + +In case of any Distribution or Communication of the Work by means of electronic +communication by You (for example, by offering to download the Work from a +remote location) the distribution channel or media (for example, a website) must +at least provide to the public the information requested by the applicable law +regarding the Licensor, the Licence and the way it may be accessible, concluded, +stored and reproduced by the Licensee. + +12. Termination of the Licence + +The Licence and the rights granted hereunder will terminate automatically upon +any breach by the Licensee of the terms of the Licence. + +Such a termination will not terminate the licences of any person who has +received the Work from the Licensee under the Licence, provided such persons +remain in full compliance with the Licence. + +13. Miscellaneous + +Without prejudice of Article 9 above, the Licence represents the complete +agreement between the Parties as to the Work. + +If any provision of the Licence is invalid or unenforceable under applicable +law, this will not affect the validity or enforceability of the Licence as a +whole. Such provision will be construed or reformed so as necessary to make it +valid and enforceable. + +The European Commission may publish other linguistic versions or new versions of +this Licence or updated versions of the Appendix, so far this is required and +reasonable, without reducing the scope of the rights granted by the Licence. New +versions of the Licence will be published with a unique version number. + +All linguistic versions of this Licence, approved by the European Commission, +have identical value. Parties can take advantage of the linguistic version of +their choice. + +14. Jurisdiction + +Without prejudice to specific agreement between parties, + +- any litigation resulting from the interpretation of this License, arising + between the European Union institutions, bodies, offices or agencies, as a + Licensor, and any Licensee, will be subject to the jurisdiction of the Court + of Justice of the European Union, as laid down in article 272 of the Treaty on + the Functioning of the European Union, + +- any litigation arising between other parties and resulting from the + interpretation of this License, will be subject to the exclusive jurisdiction + of the competent court where the Licensor resides or conducts its primary + business. + +15. Applicable Law + +Without prejudice to specific agreement between parties, + +- this Licence shall be governed by the law of the European Union Member State + where the Licensor has his seat, resides or has his registered office, + +- this licence shall be governed by Belgian law if the Licensor has no seat, + residence or registered office inside a European Union Member State. + +Appendix + +‘Compatible Licences’ according to Article 5 EUPL are: + +- GNU General Public License (GPL) v. 2, v. 3 +- GNU Affero General Public License (AGPL) v. 3 +- Open Software License (OSL) v. 2.1, v. 3.0 +- Eclipse Public License (EPL) v. 1.0 +- CeCILL v. 2.0, v. 2.1 +- Mozilla Public Licence (MPL) v. 2 +- GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3 +- Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) for + works other than software +- European Union Public Licence (EUPL) v. 1.1, v. 1.2 +- Québec Free and Open-Source Licence — Reciprocity (LiLiQ-R) or Strong + Reciprocity (LiLiQ-R+). + +The European Commission may update this Appendix to later versions of the above +licences without producing a new version of the EUPL, as long as they provide +the rights granted in Article 2 of this Licence and protect the covered Source +Code from exclusive appropriation. + +All other changes or additions to this Appendix require the production of a new +EUPL version. diff --git a/collector/compile-benchmarks/README.md b/collector/compile-benchmarks/README.md index 52654bf90..358383654 100644 --- a/collector/compile-benchmarks/README.md +++ b/collector/compile-benchmarks/README.md @@ -27,8 +27,8 @@ They mostly consist of real-world crates. - **cranelift-codegen-0.119.0**: The largest crate from a code generator. Used by wasmtime. Stresses obligation processing. - **diesel-1.4.8**: A type-safe SQL query builder. Utilizes the type system to ensure a lot of invariants. Stresses anything related to resolving trait bounds, by having a lot of trait impls for a large number of different types. - **diesel-2.2:10**: A type-safe SQL query builder. Utilizes the type system to ensure a lot of invariants. Stresses anything related to resolving trait bounds, by having a lot of trait impls for a large number of different types. -- **exa-0.10.1**: An `ls` replacement. A widely-used utility, and a binary - crate. +- **exa-0.10.1**: An `ls` replacement. A widely-used utility, and a binary crate. +- **eza-0.21.2**: An `ls` replacement. A widely-used utility, and a binary crate. Fork of `exa`. - **helloworld**: A trivial program. Gives a lower bound on compile time. - **html5ever-0.31.0**: An HTML parser. Stresses macro parsing code. - **hyper-0.14.18**: A fairly large crate. Utilizes async/await, and used by diff --git a/collector/compile-benchmarks/REUSE.toml b/collector/compile-benchmarks/REUSE.toml index fd2549c72..c8b9332ee 100644 --- a/collector/compile-benchmarks/REUSE.toml +++ b/collector/compile-benchmarks/REUSE.toml @@ -92,6 +92,11 @@ path = "exa-0.10.1/**" SPDX-FileCopyrightText = "exa contributors" SPDX-License-Identifier = "MIT" +[[annotations]] +path = "eza-0.21.2/**" +SPDX-FileCopyrightText = "Christina Sørensen, eza contributors" +SPDX-License-Identifier = "EUPL-1.2" + [[annotations]] path = "externs/**" SPDX-FileCopyrightText = "The Rust Project Developers (see https://thanks.rust-lang.org)" diff --git a/collector/compile-benchmarks/eza-0.21.2/.cargo_vcs_info.json b/collector/compile-benchmarks/eza-0.21.2/.cargo_vcs_info.json new file mode 100644 index 000000000..ef08a187a --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/.cargo_vcs_info.json @@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "ac2ed4d13fabc17919ad18e78651e3013636bf82" + }, + "path_in_vcs": "" +} \ No newline at end of file diff --git a/collector/compile-benchmarks/eza-0.21.2/0-printlns.patch b/collector/compile-benchmarks/eza-0.21.2/0-printlns.patch new file mode 100644 index 000000000..1c0399e1f --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/0-printlns.patch @@ -0,0 +1,36 @@ +diff --git a/src/fs/file.rs b/src/fs/file.rs +index 864ea074..87cccbcb 100644 +--- a/src/fs/file.rs ++++ b/src/fs/file.rs +@@ -131,6 +131,7 @@ impl<'dir> File<'dir> { + PD: Into>, + FN: Into>, + { ++ println!("testing 3"); + let parent_dir = parent_dir.into(); + let name = filename.into().unwrap_or_else(|| File::filename(&path)); + let ext = File::ext(&path); +diff --git a/src/logger.rs b/src/logger.rs +index 351890ef..517eee09 100644 +--- a/src/logger.rs ++++ b/src/logger.rs +@@ -39,6 +39,7 @@ const GLOBAL_LOGGER: &Logger = &Logger; + + impl log::Log for Logger { + fn enabled(&self, _: &log::Metadata<'_>) -> bool { ++ println!("testing 1"); + true // no need to filter after using ‘set_max_level’. + } + +diff --git a/src/theme/ui_styles.rs b/src/theme/ui_styles.rs +index 5f2b047a..33f1c422 100644 +--- a/src/theme/ui_styles.rs ++++ b/src/theme/ui_styles.rs +@@ -508,6 +508,7 @@ impl UiStyles { + /// by the `LS_COLORS` environment variable. Invalid keys set nothing, but + /// return false. + pub fn set_ls(&mut self, pair: &Pair<'_>) -> bool { ++ println!("testing 2"); + #[rustfmt::skip] + match pair.key { + "di" => self.filekinds().directory = Some(pair.to_style()), // DIR diff --git a/collector/compile-benchmarks/eza-0.21.2/CHANGELOG.md b/collector/compile-benchmarks/eza-0.21.2/CHANGELOG.md new file mode 100644 index 000000000..7901ede71 --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/CHANGELOG.md @@ -0,0 +1,2613 @@ + +# Changelog + +## [0.21.2] - 2025-04-25 + +### Bug Fixes + +- Make clippy work again + +### Documentation + +- Update Fedora install instructions for Fedora 42 +- Specify perf improvements + +### Features + +- Remove dependency on once_cell + +### Miscellaneous Tasks + +- Add icon for '.stowrc' files + +### Performance + +- Use a hashmap when possible for file extension matching + +### Styling + +- Clean up glob matching code + +### Build + +- Bump uutils_term_grid from 0.6.0 to 0.7.0 + +## [0.21.1] - 2025-04-19 + +### Bug Fixes + +- Don’t truncate branch name +- Hi extension icon wasnt working as it was in the wrong aray + +### Documentation + +- Update README.md +- Add crates.io link for README.md badge + +### Miscellaneous Tasks + +- Add MS DOS icon for *.com +- Add ruby icon for config.ru, Gemfile, Gemfile.lock, procfile, rake, rakefile and change ruby icon +- Add python icon for *.pxd and *.pyx +- Add markdown icon for *.mdx +- Add fsharp icon for *.f# and *.fsscript +- Add clojure icon for *.cljc and *.edn +- Eza v0.21.1 changelogs, version bump + +### Build + +- Flake bump 2025-04-19 +- Cargo bump 2025-04-19 + +## [0.21.0] - 2025-03-31 + +### Bug Fixes + +- Flake bump 2025-03-20 +- Remove unnescesarry unsafe blocks for libc major/minor device id +- Unwrap -> expect on libc deviceid calls +- Formatting issue +- Fix unused PermissionsPlus fields + +### Miscellaneous Tasks + +- Eza v0.21.0 changelogs, version bump + +### Build + +- Cargo deps 2025-03-20 +- [**breaking**] Change MSRV 1.78.0 -> 1.81.0 +- Bump flake deps 2025-03-30 +- Bump cargo deps 2025-03-30 + +## [0.20.24] - 2025-03-13 + +### Bug Fixes + +- Make temp files visible on white background + +### Documentation + +- More precise temp files color description + +### Features + +- Add `.exercism` folder icon +- Add `.ocamlinit` icon +- Add `.opam` folder icon + +### Miscellaneous Tasks + +- Add gcloud icon for .gcloudignore +- Add vim icon for .gvimrc, _vimrc and _gvimrc +- Add fennel icon for ~/.fennelrc and ~/.config/fennel/fennelrc +- Eza v0.20.24 changelogs, version bump + +### Build + +- Bump once_cell from 1.20.3 to 1.21.0 +- Bump terminal_size from 0.4.1 to 0.4.2 +- Bump serde from 1.0.218 to 1.0.219 +- Bump chrono from 0.4.39 to 0.4.40 + +## [0.20.23] - 2025-02-27 + +### Bug Fixes + +- Add Pixi installation instructions + +### Miscellaneous Tasks + +- Eza v0.20.23 changelogs, version bump + +### Build + +- Bump libc from 0.2.169 to 0.2.170 +- Bump serde from 1.0.217 to 1.0.218 +- Bump log from 0.4.25 to 0.4.26 +- Bump trycmd from 0.15.8 to 0.15.9 + +### Ci + +- Remove magic nix cache + +## [0.20.22] - 2025-02-20 + +### Features + +- Add prettier icon for *.prettierignore +- Add icon for *.hrl +- Add photoshop icon for *.psb +- Add eslint icon for .eslintignore +- Add renovate icon for renovate.json +- Add elixir icon for *.eex, *.leex and mix.lock + +### Miscellaneous Tasks + +- Eza v0.20.22 changelogs, version bump + +### Build + +- Bump once_cell from 1.20.2 to 1.20.3 + +## [0.20.21] - 2025-02-13 + +### Bug Fixes + +- Start publishing libgit arm builds + +### Miscellaneous Tasks + +- Eza v0.20.21 changelogs, version bump + +### Build + +- Add libgit alternative for arm builds + +## [0.20.20] - 2025-02-07 + +### Bug Fixes + +- Make `flake.lock` icon the nix logo + +### Miscellaneous Tasks + +- Eza v0.20.20 changelogs, version bump + +## [0.20.19] - 2025-01-30 + +### Bug Fixes + +- Update MSRV to 1.78 to solve rust malfunction +- Rustc false dead code positives +- Rustc false positives in tests +- Regression in theme config location, simplify path +- Wrong file name for Brewfile + +### Documentation + +- Add note regarding ci msrv update + +### Features + +- Add `.norg` icon + +### Miscellaneous Tasks + +- Eza v0.20.19 changelogs, version bump + +### Build + +- Bump dirs from 5.0.1 to 6.0.0 +- Bump NexusPHP/no-merge-commits from 2.1.0 to 2.2.1 +- Bump flake +- Bump cargo + +### Ci + +- Fix and unify msrv and add matrix to free/netbsd + +## [0.20.18] - 2025-01-23 + +### Bug Fixes + +- Support additional yaml file extension, clippy + +### Miscellaneous Tasks + +- Eza v0.20.18 changelogs, version bump + +### Build + +- Cargo bump 2025-01-23 +- Flake bump 2025-01-23 + +### Ci + +- Use rust 1.74 instead of latest via rustup + +## [0.20.17] - 2025-01-16 + +### Features + +- Add editorconfig icon for .editorconfig + +### Miscellaneous Tasks + +- Eza v0.20.17 changelogs, version bump + +### Build + +- Cargo bump 2025-01-16 +- Flake bump 2025-01-16 + +## [0.20.16] - 2025-01-09 + +### Features + +- Add brew icon for brewfile and brewfile.lock.json + +### Miscellaneous Tasks + +- Eza v0.20.16 changelogs, version bump + +### Build + +- Update flake inputs 2025-01-08 +- Update cargo inputs 2025-01-08 +- Bump git2 from 0.19.0 to 0.20.0 + +## [0.20.15] - 2025-01-02 + +### Features + +- Add icons from nerd fonts 3.3.0 release & more +- Add new icons, extensive list + +### Miscellaneous Tasks + +- Eza v0.20.15 changelogs, version bump + +### Build + +- We switch to our own fork of natord + +### Ci + +- Bump FreeBSD / NetBSD versions. + +## [0.20.14] - 2024-12-26 + +### Bug Fixes + +- ...those pesky workflow targets +- Remove separate bsd tests +- Remove audit workflow + +### Features + +- Audit checks in main CI + +### Miscellaneous Tasks + +- Eza v0.20.14 changelogs, version bump + +### Refactor + +- Move eza, trycmd packages +- Move BSD unit tests to main flow + +### Styling + +- Format workflows +- Ci checks formatted + +### Build + +- Cargo 2024-12-25 + +### Ci + +- Simplify +- Let's just always run em' +- Only run big checks on PRs to main +- Faster flake checks... maybe? +- Flakes on latest ubuntu +- Only do no-merge-commits on PR + +## [0.20.13] - 2024-12-18 + +### Bug Fixes + +- Pre-commit-hooks.nix trying to be too clever +- Remove stray description (originally from `--decay-mode`) + +### Miscellaneous Tasks + +- Eza v0.20.13 changelogs, version bump + +### Build + +- Update crate deps Mon Dec 16 +- Update flake deps Mon Dec 16 + +### Ci + +- Update to PRESENT DAY, PRESENT TIME + +## [0.20.12] - 2024-12-11 + +### Bug Fixes + +- Add unicode-3.0 license +- Use safe terminal_size_of +- Use terminal_size_of with borrowed raw handle + +### Features + +- Move MSRV to 1.74 and deep bump cargo deps +- Add Gleam lang icon + +### Miscellaneous Tasks + +- Eza v0.20.12 changelogs, version bump + +### Testing + +- Regen for 1.74 + +### Build + +- Bump terminal_size from 0.3.0 to 0.4.1 + +### Ci + +- Openbsd 7.4 -> 7.6 + +## [0.20.11] - 2024-12-05 + +### Bug Fixes + +- Bump libc from 0.2.165 to 0.2.167 + +### Miscellaneous Tasks + +- Eza v0.20.11 changelogs, version bump + +## [0.20.10] - 2024-11-28 + +### Bug Fixes + +- People dislike the phrasing "maintained" on hackernews + +### Miscellaneous Tasks + +- Eza v0.20.10 changelogs, version bump + +### Build + +- Bump libc from 0.2.164 to 0.2.165 + +## [0.20.9] - 2024-11-21 + +### Bug Fixes + +- Remove newline after doc comment of `regen` recipe + +### Miscellaneous Tasks + +- Eza v0.20.9 changelogs, version bump + +### Refactor + +- List all recipes by default +- Group related recipes + +### Build + +- Bump libc from 0.2.162 to 0.2.164 +- Bump DeterminateSystems/nix-installer-action from 15 to 16 + +## [0.20.8] - 2024-11-14 + +### Bug Fixes + +- Cross-compiling by updating to libz-sys to 1.1.20 + +### Miscellaneous Tasks + +- Eza v0.20.8 changelogs, version bump + +### Build + +- Bump palette from 0.7.5 to 0.7.6 +- Bump libc from 0.2.161 to 0.2.162 +- Bump serde from 1.0.214 to 1.0.215 + +## [0.20.7] - 2024-11-07 + +### Bug Fixes + +- Palette v0.7.6 -> v0.7.5 + +### Miscellaneous Tasks + +- Update package.exclude list in Cargo.toml +- Eza v0.20.7 changelogs, version bump + +### Build + +- Bump DeterminateSystems/nix-installer-action from 14 to 15 +- Bump serde_norway from 0.9.38 to 0.9.39 +- Bump trycmd from 0.15.7 to 0.15.8 + +## [0.20.6] - 2024-10-31 + +### Bug Fixes + +- Changelog spelling + +### Documentation + +- Fix typo `--get-repos-no-status` to `--git-repos-no-status` + +### Miscellaneous Tasks + +- Eza v0.20.6 changelogs, version bump + +### Build + +- Bump serde from 1.0.210 to 1.0.214 + +## [0.20.5] - 2024-10-25 + +### Bug Fixes + +- Ensure nested tree parts align under item name +- Remove depricated `chrono` `from_timestamp_opt` + +### Miscellaneous Tasks + +- Update generated test files +- Eza v0.20.5 changelogs, version bump + +### Build + +- Bump libc from 0.2.159 to 0.2.161 +- Chrono v0.4.34 -> v0.4.38 + +## [0.20.4] - 2024-10-18 + +### Bug Fixes + +- Filetype, coloring for executables and folder + +### Miscellaneous Tasks + +- Eza v0.20.4 changelogs, version bump + +## [0.20.3] - 2024-10-17 + +### Bug Fixes + +- Git cliff docs issue + +### Miscellaneous Tasks + +- Eza v0.20.3 changelogs, version bump + +### Performance + +- Reuse filetype from DirEntry + +## [0.20.2] - 2024-10-09 + +### Bug Fixes + +- Colors in old ms command prompt +- Bring help text in line with available flags +- Do not print parent filename with --absolute=on + +### Miscellaneous Tasks + +- Add fox installation option +- Eza v0.20.2 changelogs, version bump + +### Build + +- Bump once_cell from 1.20.1 to 1.20.2 + +## [0.20.1] - 2024-10-03 + +### Bug Fixes + +- Release recipe +- Support passing multiple options for generate-trycmd-test.sh +- Move options into flags +- Rustfmt errors + +### Documentation + +- Fix cross-references +- Update file type colors +- Document that exit 13 == permission denied + +### Features + +- Update just, add more formats +- Recursively walk symlinks pointing at dirs +- Add --follow-symlinks option +- Add autocomplete for --follow-symlinks +- Show directories last + +### Miscellaneous Tasks + +- Eza v0.20.1 changelogs, version bump + +### Testing + +- Add cases for -T and --follow-symlinks +- Regenerate tests broken by line number changes + +### Build + +- Fix manual version +- Bump once_cell from 1.19.0 to 1.20.1 + +### Ci + +- Remove flakehub, flakestry publish + +## [0.20.0] - 2024-09-26 + +### Bug Fixes + +- Flake trycmd bug +- Pre-commit-hook taplo bug + +### Documentation + +- Add link to eza-themes repository in readme +- Cargo install dir inaccurate +- Add x-cmd method to install eza +- Adding a testing infos file to guide everyone through tests + +### Features + +- Add `opml` file extension +- Add a regen rule +- [**breaking**] Relicensed to EUPL-1.2 + +### Miscellaneous Tasks + +- Eza v0.20.0 changelogs, version bump + +### Refactor + +- Move some files to `.config` +- Release scripts use `.config` +- Relicense to EUPL-1.2 + +### Styling + +- Switch to nixfmt rfc style, format tree +- Remove blank line + +### Testing + +- Regenerate integration tests +- Regenerate tests + +### Build + +- Darwin devShell resuse eza deps +- Ensure flake inputs aren't duplicated' +- Remove semnix deps +- Bump flake lock 2024-09-26 +- Removed unused flake follows +- Add cargo to devShell +- Add clippy to devShell +- Use toolchain in devShell +- Bump libc from 0.2.158 to 0.2.159 +- Bump unicode-width from 0.1.13 to 0.2.0 + +### Ci + +- Full nix3 command output in logs +- Allow EUPL-1.2 +- Unblock windows + +## [0.19.4] - 2024-09-18 + +### Bug Fixes + +- Remove non_alpha from percent encoding to fix hyprlinks + +### Features + +- Pass from serde_yaml to serde_norway + +### Miscellaneous Tasks + +- Eza v0.19.4 changelogs, version bump + +## [0.19.3] - 2024-09-12 + +### Bug Fixes + +- Convert empty space to %20 when render hyperlinks +- Split commit workflows and run no-merge-commits only on PRs +- Correct naming of commit related workflows + +### Documentation + +- Better version bump commit summary + +### Features + +- Add no-merge-commits job to commits workflow + +### Miscellaneous Tasks + +- Rename justfile +- Eza v0.19.3 changelogs, version bump + +### Refactor + +- Rename conventional-commits workflow to commits + +### Build + +- Bump DeterminateSystems/nix-installer-action from 13 to 14 +- Bump DeterminateSystems/flake-checker-action from 8 to 9 +- Bump actions/checkout from 3 to 4 +- Bump libc from 0.2.155 to 0.2.158 +- Bump nu-ansi-term from 0.50.0 to 0.50.1 + +## [0.19.2] - 2024-09-05 + +### Bug Fixes + +- Remove unnecessary map and make clippy happy +- Adjust grid details for CI tests +- Imports and merge conflicts +- Rustfmt issues +- Clippy issues +- Revise UiStyles::plain to have no style at all +- Pr reviews fixes for theme file +- Selectively filter files when recursing #1101 +- Fix typo in FromOverride impl +- Add serde(default) to StyleOverride.foreground/background fields + +### Documentation + +- Add Flox to INSTALL.md +- Add ic for icon color to colors man page +- Add further documentation about theme file + +### Features + +- Add c++ module interfaces as source file types +- Add icon field to UiStyles +- Add ic key for UiStyles icon in set_exa +- Add None as icon value in UiStyles.default_theme +- Add icon function to FileNameColours trait +- Implement FileNameColours.icon for Theme +- Adjust FileName.paint to consider possible icon color +- Begin implementation of config file +- Allow writing default theme.yml file for eventual config file implementation +- Theme file configuration base +- Add IconOverrides struct and UiStyles.icon_overrides +- Add icon_override function to FileNameColours trait +- Implement FileNameColours.icon_override for Theme +- Handle icon overrides in FileName.paint +- Add example config for icon_overrides +- Rename UiStyles.icon_override to icons and add Style field +- Add shorthand aliases to StyleOverride variables +- Add custom deserialize_color and use in StyleOverride +- Outsource color_from_str function to make it testable + +### Miscellaneous Tasks + +- Release eza v0.19.2 + +### Refactor + +- Simplify icon style setting in FileName.paint +- Make every setting optional with override layer +- Simplify sample theme.yml +- Formatting for rustfmt macro + +### Styling + +- Fix clippy issue in FileName.paint +- Apply rustfmt +- Simplify from_str_radix calls to please clippy + +### Testing + +- Add unit tests for color_from_str function + +### Build + +- Bump windows-sys from 0.52.0 to 0.59.0 + +### Ci + +- Allow MPL-2.0 + +## [0.19.1] - 2024-08-28 + +### Bug Fixes + +- FreeBSD build. +- Typo + +### Miscellaneous Tasks + +- Release eza v0.19.1 + +### Build + +- Bump uzers from 0.12.0 to 0.12.1 + +## [0.19.0] - 2024-08-08 + +### Bug Fixes + +- [**breaking**] Implement `EZA_GRID_ROWS` grid details view minimum rows threshold + +### Miscellaneous Tasks + +- Release eza v0.19.0 + +## [0.18.24] - 2024-08-03 + +### Bug Fixes + +- 1.80 breakage from time crate + +### Miscellaneous Tasks + +- Release eza v0.18.24 + +### Build + +- Bump time dependency + +## [0.18.23] - 2024-07-25 + +### Bug Fixes + +- Disable broken freebsd tests + +### Documentation + +- Clear up confusion around ls + +### Miscellaneous Tasks + +- Release eza v0.18.23 + +### Build + +- Bump log from 0.4.21 to 0.4.22 +- Bump DeterminateSystems/nix-installer-action from 12 to 13 +- Bump plist from 1.6.1 to 1.7.0 + +## [0.18.22] - 2024-07-18 + +### Bug Fixes + +- Use NaiveDateTime::from_timestamp_opt instead of panicky From impl + +### Features + +- Add non-nix pre-commit rustfmt and clippy hooks + +### Miscellaneous Tasks + +- Release eza v0.18.22 + +### Ci + +- Bump FreeBSD version. + +## [0.18.21] - 2024-07-01 + +### Bug Fixes + +- Fix missing line breaks in _eza + +### Miscellaneous Tasks + +- Release eza v0.18.21 + +## [0.18.20] - 2024-06-27 + +### Features + +- Add --no-|show-symlinks flags for filtering output + +### Miscellaneous Tasks + +- Release eza v0.18.20 + +## [0.18.19] - 2024-06-20 + +### Bug Fixes + +- Ship release binaries + +### Miscellaneous Tasks + +- Release eza v0.18.19 + +### Build + +- Bump git2 from 0.18.3 to 0.19.0 + +## [0.18.18] - 2024-06-13 + +### Features + +- Extend deny check in audit workflow to all +- Add deny.toml and workflow file to audit workflow paths +- Run on all features by default +- Ask for shell and terminal in bug report template + +### Miscellaneous Tasks + +- Release eza v0.18.18 + +### Build + +- Bump unicode-width from 0.1.12 to 0.1.13 +- Bump DeterminateSystems/flake-checker-action from 7 to 8 +- Bump DeterminateSystems/nix-installer-action from 11 to 12 + +## [0.18.17] - 2024-06-05 + +### Features + +- Add icon for Nushell extension + +### Miscellaneous Tasks + +- Release eza v0.18.17 + +### Build + +- Bump trycmd from 0.15.1 to 0.15.2 +- Bump libc from 0.2.154 to 0.2.155 + +## [0.18.16] - 2024-05-16 + +### Bug Fixes + +- Change windows-only imports to be windows-only + +### Documentation + +- Replace decay with color-scale +- Update INSTALL.md +- Fix typo in `INSTALL.md` +- Use 3 columns for packaging status badge + +### Miscellaneous Tasks + +- Release eza v0.18.16 + +### Build + +- Bump DeterminateSystems/flake-checker-action from 5 to 7 +- Bump DeterminateSystems/nix-installer-action from 10 to 11 + +## [0.18.15] - 2024-05-09 + +### Bug Fixes + +- Correct command for latest tag in deb-package.sh + +### Documentation + +- Add how to find man pages in terminal and online. Partly fixes #967 +- Correct heading levels in markdown +- Move heading out of collapsed section +- Add some keywords for benefit of ctrl-f + +### Features + +- Return to original commit at the end of deb-package.sh +- Add optional tag argument to deb-package.sh + +### Miscellaneous Tasks + +- Release eza v0.18.15 + +## [0.18.14] - 2024-05-02 + +### Features + +- Add icon for "cron.minutely" directory + +### Miscellaneous Tasks + +- Release eza v0.18.14 + +### Build + +- Bump uzers from 0.11.3 to 0.12.0 +- Bump libc from 0.2.153 to 0.2.154 +- Bump unicode-width from 0.1.11 to 0.1.12 +- Bump palette from 0.7.5 to 0.7.6 + +## [0.18.13] - 2024-04-25 + +### Bug Fixes + +- Allow unused imports for freebsd +- Checking for deref flag in file_name + +### Features + +- Add scheme filetype and icons +- Generate completion/manpage tarballs on release + +### Miscellaneous Tasks + +- Release eza v0.18.13 + +## [0.18.11] - 2024-04-19 + +### Bug Fixes + +- Fix clippy lints +- Enable the rule only for NetBSD. +- Build aarch64, arm without libgit2 + +### Miscellaneous Tasks + +- Release eza v0.18.11 + +### Ci + +- Bump NetBSD version to 10.0 + +## [0.18.10] - 2024-04-11 + +### Bug Fixes + +- Bump trycmd from 0.15.0 to 0.15.1 + +### Miscellaneous Tasks + +- Release eza v0.18.10 + +### Build + +- Bump nu-ansi-term from 0.49.0 to 0.50.0 + +## [0.18.9] - 2024-03-27 + +### Features + +- Switch out ansiterm crate for nu_ansi_term + +### Miscellaneous Tasks + +- Release eza v0.18.9 + +### Build + +- Bump DeterminateSystems/nix-installer-action from 9 to 10 +- Bump plist from 1.6.0 to 1.6.1 +- Bump rayon from 1.9.0 to 1.10.0 +- Bump git2 from 0.18.2 to 0.18.3 + +## [0.18.8] - 2024-03-21 + +### Bug Fixes + +- Avoid deprecation warnings +- Rustfmt issues + +### Features + +- Add fennel lang icon and associations + +### Miscellaneous Tasks + +- Release eza v0.18.8 + +## [0.18.7] - 2024-03-14 + +### Bug Fixes + +- Bugfix to resolve absolute paths that are not symlinks + +### Features + +- Add filetype and icon for .hh extension + +### Miscellaneous Tasks + +- Release eza v0.18.7 + +## [0.18.6] - 2024-03-06 + +### Bug Fixes + +- NetBSD did not have fflagstostr and as such did not build properly +- Fix total-size option +- Add fortran to source filetypes +- Fix absolute_path() for broken symlinks +- Update line numbers in panic messages in tests + +### Features + +- Add filetype and icon for age +- Adding icons for graphql extensions +- Add nim icons +- Use fsharp icon for fsproj files (similar to cs/csproj) +- Add new icons, diverse selection +- Adding more haskell related icons +- Adding more icons for docker specific files +- Adding more dockerfile icons +- Add --absolute flag +- Add shell completions for --absolute flag + +### Miscellaneous Tasks + +- Cleaning dirs +- Release eza v0.18.6 + +### Refactor + +- Port grid and grid-details to new uutils-term-grid + +### Testing + +- Add integration tests and powertests for --absolute flag +- Add directory symlink to tests/itest/ + +### Build + +- Bump log from 0.4.20 to 0.4.21 +- Bump rayon from 1.8.1 to 1.9.0 + +### Ci + +- Add NetBSD to CI. +- Fix warnings. +- Add FreeBSD to CI. +- Add OpenBSD to CI. + +## [0.18.5] - 2024-02-29 + +### Bug Fixes + +- Bump palette from 0.7.4 to 0.7.5 + +### Miscellaneous Tasks + +- Release eza v0.18.5 + +## [0.18.4] - 2024-02-22 + +### Bug Fixes + +- Classification width should be taken into account with -F=auto + +### Miscellaneous Tasks + +- Release eza v0.18.4 + +### Build + +- Bump libc from 0.2.152 to 0.2.153 +- Bump chrono from 0.4.33 to 0.4.34 +- Bump trycmd from 0.14.20 to 0.15.0 + +## [0.18.3] - 2024-02-15 + +### Bug Fixes + +- Duplicates in shell completions + +### Documentation + +- Add target arch to deb PPA installation for strict apt environments + +### Miscellaneous Tasks + +- Release eza v0.18.3 + +### Performance + +- Do not pre-compute MountInfo to reduce readlink calls + +### Refactor + +- Use #[default] attribute instead of custom impl for enums + +## [0.18.2] - 2024-02-08 + +### Bug Fixes + +- Update libgit2 to 1.7.2 + +### Miscellaneous Tasks + +- Release eza v0.18.2 + +## [0.18.1] - 2024-02-08 + +### Bug Fixes + +- Change shasum for main commit + +### Documentation + +- Add manual installation section + +### Miscellaneous Tasks + +- Release eza v0.18.1 + +### Refactor + +- Replace scoped_threadpool with rayon + +### Build + +- Add empty rustfmt to ensure project specific settings +- Bump libc from 0.2.151 to 0.2.152 +- Bump nick-fields/retry from 2 to 3 +- Bump palette from 0.7.3 to 0.7.4 +- Bump webiny/action-conventional-commits from 1.2.0 to 1.3.0 + +## [0.18.0] - 2024-02-01 + +### Features + +- [**breaking**] Add --classify=always,auto,never + +### Miscellaneous Tasks + +- Remove rustfmt config file that has a nightly only option in favor of rustfmt skip directive which is already in place +- Fix small typo in pull request template +- Release eza v0.18.0 + +### Refactor + +- Change cast to coertion, remove rustfmt skip and clippy lint ignore directives + +### Testing + +- Regenerate classification related tests + +### Build + +- Change flake inputs + +## [0.17.3] - 2024-01-25 + +### Bug Fixes + +- Remove version testing + +### Miscellaneous Tasks + +- Avoid `unwrap()` by changing filter-then-map to `filter_map` +- Release eza v0.17.3 + +### Build + +- Bump shlex from 1.2.0 to 1.3.0 +- Bump chrono from 0.4.31 to 0.4.33 +- Bump trycmd from 0.14.19 to 0.14.20 + +## [0.17.2] - 2024-01-20 + +### Bug Fixes + +- Crash using --git-repos on unreadable dir +- Crash using --git-repos on unreadable dir + +### Miscellaneous Tasks + +- Release eza v0.17.2 + +### Build + +- Add cargo-bump for releasing + +## [0.17.1] - 2024-01-11 + +### Bug Fixes + +- Offset widths in grid mode with utf8 filenames +- Format the code +- Unformat the code where needed +- Format the code correctly this time +- Redo everything from scratch +- Stack overflow when '-laaR' are used +- Stack overflow when '-laaR' is used + +### Features + +- Add Fortran icons + +### Miscellaneous Tasks + +- Adding blake3 to checksums +- Release eza v0.17.1 + +### Testing + +- Regenerate version tests... and others +- Updated tests to fit new features + +### Build + +- Add b3sum to devshell deps + +## [0.17.0] - 2023-12-13 + +### Bug Fixes + +- Add color scale mode to the bash completions +- Add color scale mode to the fish completions +- Quote symbolic links correctly when their destinations contain spaces + +### Documentation + +- Modify documentation about custom time style + +### Features + +- Add BSD file flags +- Add Windows file attributes +- [**breaking**] Support different custom time style for non-recent/recent files + +### Miscellaneous Tasks + +- Release eza v0.17.0 + +### Testing + +- Regen powertests +- Regenerate +- Add tests for non-recent/recent custom time style +- Update powertest expected help message output + +### Build + +- Update `flake.lock` +- Bump DeterminateSystems/nix-installer-action from 8 to 9 +- Bump once_cell from 1.18.0 to 1.19.0 +- Bump libc from 0.2.150 to 0.2.151 + +### Ci + +- Remove labeler + +## [0.16.3] - 2023-12-07 + +### Bug Fixes + +- Add bare git_repos fn if feature git is disabled +- Fixing color of size unit +- Color-scale broked size for colors + +### Miscellaneous Tasks + +- Release eza v0.16.3 + +### Testing + +- Fix powertests post-release + +### Build + +- Bump percent-encoding from 2.3.0 to 2.3.1 +- Bump actions/labeler from 4 to 5 + +## [0.16.2] - 2023-11-30 + +### Bug Fixes + +- Calculate width correctly when using grid icons & classify +- Fix the windows build + +### Miscellaneous Tasks + +- Release eza v0.16.2 + +### Testing + +- Fix version tests + +### Build + +- Bump webiny/action-conventional-commits from 1.1.0 to 1.2.0 +- Bump DeterminateSystems/nix-installer-action from 7 to 8 +- Bump windows-sys from 0.48.0 to 0.52.0 + +## [0.16.1] - 2023-11-23 + +### Bug Fixes + +- Don't panic with todo!() on inaccessible dir +- Don't panic if the btime of a file is Err +- Lifetime annotations and manpage/shell completion nits +- Reflow help + +### Features + +- Handle formatting and display of binary extended attributes. +- Add netbsd and freebsd support for extended attributes + +### Miscellaneous Tasks + +- Update flake inputs +- Release eza v0.16.1 + +### Testing + +- Vars mocking +- Display and meta options +- Filtering and sorting +- Long view options +- Regenerate `--help` tests + +### Build + +- Sign release tags + +## [0.16.0] - 2023-11-16 + +### Bug Fixes + +- Fix cross compilation +- Snap requires a base +- Move `--smart-group` to long view options +- Colo[u]r-scale didn't have a base value +- Fix snapcraft.yaml formatting + +### Documentation + +- Add comments for bzip variants +- Added the fact that total-size is unix only + +### Features + +- Add some file extensions +- Abort on panic (saving 0.1 M) +- Add powertest + +### Miscellaneous Tasks + +- Ignore blame from #644 +- Stabilize powertest version +- Release eza v0.16.0 + +### Testing + +- Implements tests using the generated directory +- Powertests using generated testdirs +- Add hashed versions of powertests + +## [0.15.3] - 2023-11-09 + +### Bug Fixes + +- Changed quote in --almost-all completion +- [**breaking**] Remove Repo column when using --git-repos when no git repo +- Reformat `help.rs` +- Allow unused macro rule arms + +### Documentation + +- Improve CONTRIBUTING.md, README.md +- Improve README.md +- Introduce INSTALL.md + +### Features + +- Create EZA_ICONS_AUTO environment variable +- Create EZA_ICONS_AUTO environment variable +- Demo gif and gif generation recipe +- Add ocaml icon filetypes +- Add PRQL +- Add `--color-scale` + +### Miscellaneous Tasks + +- Add to CODEOWNERS file to make sure I get ping'd on files being touched +- Add myself to codeowners to watch modifications on parsing +- Improve the PR template +- Release eza v0.15.3 + +### Refactor + +- Remove commented out test code +- Finalize `decay` -> `color_scale` + +### Build + +- Refactor flake +- Bump libc from 0.2.149 to 0.2.150 +- Bump DeterminateSystems/nix-installer-action from 4 to 7 +- Bump rustix from 0.38.13 to 0.38.21 + +### Ci + +- Refactor pre-commit-hooks +- Refactor publish workflow + +## [0.15.2] - 2023-11-02 + +### Bug Fixes + +- Correct width when --no-quotes is used +- Clippy lint and add option to grid-details +- --smart-group only works for current user + +### Features + +- Add Typst to the recognized files + +### Miscellaneous Tasks + +- Release eza v0.15.2 + +### Refactor + +- Replace `lazy_static` with `once_cell` +- Replace plain values with TextColours + +### Testing + +- Added more content to the dir generator +- Changed size of one of the files + +## [0.15.1] - 2023-10-26 + +### Bug Fixes + +- Only store top-level recursive dir size +- Changed windows methods +- Underscored unused windows variables +- Added device for filesystem to hashmap +- Don’t display target’s size if we’re not dereferencing +- Display offset for filenames with spaces +- Fix clippy warnings +- Fix doc-tests on RecursiveSize +- Fix dead_code warnings on Windows + +### Documentation + +- Fix doc-tests formatting and address other documentation review requests + +### Features + +- Add a new filetype for source code files +- Add a new icons for source code files and other files +- Support for displaying blocksize on directories + +### Miscellaneous Tasks + +- Release eza v0.15.1 + +### Refactor + +- Move total-size calculations to File +- Add RecursiveSize type to simplify total-size calculation + +## [0.15.0] - 2023-10-19 + +### Bug Fixes + +- Reenable debug symbols in debug builds +- Fmt, windows, and nix fixes +- Reverted autofmt changes +- Updated match indents +- Changed flag name +- Clippy lint +- Merge conflict with main + +### Documentation + +- Correct color option spellings +- Added flag to readme +- Added flag to man + +### Features + +- Add option --smart-group +- Add completions, man for --smart-group +- Added recursive directory parser +- Added flag to completions +- Add icons=always,auto,never. dont display icons in a tty|piped +- Fix auto value for colors and icons + documentation +- [**breaking**] Remove --no-icons in favor of --icons=always,auto,never. default is auto + +### Miscellaneous Tasks + +- Upgrade to uutils_term_grid from unmaintained term_grid +- Release eza v0.15.0 + +### Build + +- Bump DeterminateSystems/nix-installer-action from 5 to 6 + +### Ci + +- Remove stalebot, is super annoying +- Adjust test case to icons=auto (no icons should show due to tty) + +## [0.14.2] - 2023-10-12 + +### Bug Fixes + +- Comment out redundant static musl build +- Refactor sed command to build manpages +- Update additional completions for help, almost-all, dereference +- Fix zsh completions + +### Documentation + +- Add missing options to man page and CLI --help info + +### Features + +- Add missing nu shell completions +- Adding the EZA_OVERRIDE_GIT env var + +### Miscellaneous Tasks + +- Release eza v0.14.2 + +### Refactor + +- Use musl target for amd64 deb package +- Directly use one "big" awk command + +### Styling + +- Remove trailing spaces and trailing line + +### Build + +- Bump libc from 0.2.148 to 0.2.149 +- Bump DeterminateSystems/nix-installer-action from 4 to 5 + +## [0.14.1] - 2023-10-08 + +### Bug Fixes + +- Replace left-over exa in fish completion +- Diabling static linked binaries due to segfault +- Make os error 13 fail loud +- Root group not painted as expected when eza used by root +- Adjust change width calculations for hyperlink and classify + +### Documentation + +- Correct CONTRIBUTING.md on commit message type +- Fix typos +- Add zsh with homebrew part to completions section +- Installation on fedora updated + +### Features + +- Add basic nushell completion file +- Add codeowner for nu completions +- Readded musl static bin as it works + +### Miscellaneous Tasks + +- Release eza v0.14.1 + +### Refactor + +- Align completions +- Do not match for numbers and remove single-use fn +- Consistent argument order + +### Testing + +- Classify-hyperlink test case for width 50 +- Move classify tests to local + +### Build + +- Bump trycmd from 0.14.17 to 0.14.19 +- Make checksums easier to copy-paste +- Improve release automation +- Fix version bump +- Fix double echo +- Automate gh release +- Add `codegen-units = 1` and remove `opt-level = 3` +- Add back `opt-level = 3` + +### Ci + +- Treat warnings as errors + +## [0.14.0] - 2023-10-02 + +### Bug Fixes + +- Ignore refs for blame +- Avoid unstable inner attributes +- Merge conflict with main +- Merge conflict with main +- Fix manpage generation of default package +- Changed dll icon +- Changed readme and Added README icon +- New R lang icon +- README is sorted and formatted +- Fix large_enum_variant warning with explanation +- Query stdout terminal size to see if the output gose to a tty. +- Use windows-specific API for terminal size query on windows +- Add `windows-sys` dependency for targeting windows +- Use `std::io::IsTerminal` to eliminate compatibility issue +- Terminal size query should only check `stdout` +- Prefix unused binding name with underscore + +### Documentation + +- Add completions + manpage for --no-quotes flag +- Leave nix install instructions open-ended +- Adding termux section +- Leave nix install instructions open-ended +- Added the new colors option to the man +- Documenting custom time-style +- Time-format supporting custom formats +- Updated man to add new colors +- Description of `--color` in README, manpage, and completions +- Change `color` to `colo[u]r` in the option description. + +### Features + +- Add rustfmt.toml file to prevent flags.rs fmt on save +- Add quotations around filenames with spaces. exa pr#1165 +- Replace hardcoded version by version variable +- Add header to colors-explanation page +- Revise man rule to use for loop and insert version +- Adding the possibility to change git-repos colors +- [**breaking**] Separated root from other users +- New Rust icon +- Added bdf,psf icons +- Added lib icon +- Added Contacts,Favorites icons +- Added home icon +- Added fdmdownload icon +- Added statically linked binaries + +### Miscellaneous Tasks + +- Release 0.14.0 + +### Refactor + +- Ignore options/flags.rs +- Renamed and reintended some code +- Reformatted a line + +### Styling + +- Format some parts correctly + +### Build + +- Bump unicode-width from 0.1.10 to 0.1.11 +- Bump git2 from 0.18.0 to 0.18.1 +- Temporarily disable aarch64-unknown-linux-gnu +- Name static binaries + +## [0.13.1] - 2023-09-25 + +### Bug Fixes + +- Typo `this` -> `that` +- Don’t show color when color is disabled +- Respect spec on Windows and make it for with Konsole +- Major and minor device on MacOS +- Linux uses u32 for major/minor device numbers +- Error for missed semicolon +- More than 3 bools in a struct +- Enable rustfmt by removing .rustfmt.toml which disables it +- Replace rustfmt::skip on expressions because experimental +- Remove unnecessary rustfmt::skip's in windows code +- Add src/options/flags.rs to rustfmt.excludes +- Left-over merge conflict in src/output/table + +### Documentation + +- Update README.md +- Update --mounts option to include MacOS +- Documenting --only-files + +### Features + +- Add EXA_COLOR bindings for un-themed items +- Add EZA_ environment variables with fallback to EXA_ +- Listing files only using '--only-files' flag +- Add rustfmt check to unit-tests workflow + +### Miscellaneous Tasks + +- Add completion for --only-fies (zsh,fish) +- Release 0.13.1 + +### Refactor + +- Fix rustfmt issues and place skips where needed +- Reorder unit-tests to fmt, clippy and tests + +### Styling + +- Formatted using treefmt +- Fix clippy warning after rustfmt +- Fix treefmt issues in options module +- Reapply rustfmt after rebase from main + +### Testing + +- Add unit tests for new style abbreviations +- Regen git_repos_no_status +- Test for listing files only + +### Build + +- Bump actions/checkout from 2 to 4 +- Bump chrono from 0.4.30 to 0.4.31 +- Bump timeago from 0.4.1 to 0.4.2 +- Bump libc from 0.2.147 to 0.2.148 +- Bump terminal_size from 0.2.6 to 0.3.0 + +### Ci + +- Added formatters to treefmt +- Make various improvements +- Only apply labels when opening a PR + +## [0.13.0] - 2023-09-18 + +### Bug Fixes + +- Crate can't contain broken symlink +- Remove executable flag from fish completion file +- Use proc_mounts only on linux +- Hotfix harmful documentation +- Fix hyperlinks on Windows +- Needless_borrow +- Nix flake check also builds the package +- [**breaking**] Change number_huge and unit_huge to match the man page short codes + +### Documentation + +- Added instructions to install completions of eza to the readme +- Added cafkafk suggestions +- Fix codeblocks in zsh completions +- Update README.md +- Add Winget install info +- Link directly to space +- Document new file type two letter codes in man page +- Document filetypes theme and rename trait +- Update deb instructions to use keyring +- Fix chmod in deb installation instructions +- Add potential gpg install to deb installation instructions +- Add install instructions for Void Linux +- Document dimmed and italic style codes +- Document character style pairs in the code and match with man page +- Documentation of 'sn' and 'sb' conflicted with later docs + +### Features + +- Add completion files in deb packaging script +- Adds filtering for Windows hidden files +- Make file types themeable +- Lazy loading of a files extended attributes and absolute path + +### Miscellaneous Tasks + +- Augment gitter size in README +- Release 0.13.0 + +### Performance + +- Add criterion for benchmarking + +### Refactor + +- Refactor just in crossfile +- DRY up justfile +- Ignore missing MSVC docker image +- Removed unused imports, mark mods as allow unused +- Format code +- Move ALL_MOUNTS to fs::mounts +- Migrate ALL_MOUNTS from lazy_static to OnceLock +- Rename FileType::Immediate to more obvious FileType::Build + +### Testing + +- Autogenerate testing dir +- Stabalised unit-tests.yml +- Autogenerate test dirs +- Generate device files +- Add unit tests that test both exa and ls style codes together +- Address variable names + +### Build + +- Set optlevel to 3 +- Add musl binary for linux +- Fix checksums +- Add TODOs to targets + +### Ci + +- Add Winget Releaser workflow +- Add nix Flake check to flake.yml +- Removed nix build in favor of nix flake check +- Include bash completion script in treefmt and fixed shellcheck formatting in completion script +- Fix spelling attemps -> attempts + +## [0.12.0] - 2023-09-14 + +### Bug Fixes + +- RUSTSEC-2020-0071 +- Expand `--all` help +- Generalize gitignore to ignore all eza deb packages +- Change trycmd config to use test/itest folder for testing +- Revert to old apt install command suggestion and add hint +- Canonicalize errors when the destination of a symbolic link is bad +- Handle other canonicalize errors in hyperlinks and git +- Fix windows build when canonicalize returns an error +- Is_some_and is an unstable Rust feature until 1.70 +- Remove stray backslashes +- Exit 13 on os error 13 +- Rewrite comment +- Improve trace strings +- Tracing typo +- Revert "Support for Windows Hidden Files" +- Shellcheck warnings +- Revert "Support for Windows Hidden Files" +- Shellcheck warnings + +### Documentation + +- Expand `--all` documentation +- Add gentoo +- Fix gentoo install +- Add MacPorts install info +- Add pthorpe92 gist +- Add docs for --git-repos & --git-repos-no-status +- Fix gpg armor flag for deb release in readme +- Add scoop install info +- Add Mac support for the --mount option in the man page +- Add SAFETY comments to unsafe code blocks +- Remove license from developemnt section +- Update rust badge +- Add better explanation of git repos + no status +- Remove color specifications. change unknown git repo status to `~` +- Fix missing color specification from man page +- Add missing man page for debian release + +### Features + +- Add audit workflow +- Add trycmd as dev-dependency +- Add minimal trycmd binary +- Add a few trycmd tests as example +- Add apt installation workflow +- Support --mount option on Mac +- Support --mount option on Mac +- Adds filtering on Windows hidden files +- Document and change output for --git-repos +- Add PERMISSION_DENIED exit code +- Adds filtering on Windows hidden files +- Adds filtering on Windows hidden files +- Added shellcheck to treefmt +- Adds filtering on Windows hidden files + +### Miscellaneous Tasks + +- Bump uzers to v0.11.3 +- Bump chrono from 0.4.27 to 0.4.30 +- Removal of xtests +- Removal of vagrant +- Remove deprecated devtools +- [**breaking**] MSRV 1.70 +- Run spellcheck +- Release 0.12.0 + +### Refactor + +- Over-engineer deb-package.sh +- Hide xtests folder +- Split trycmd into tests for all, unix and windows +- Limit unit-tests run on workflow change to unit-tests itself +- Add tracing to various code parts +- Make std::process::exit global +- Moved generateTest.sh to devtools/ +- Renamed the file + +### Revert + +- "Support for Windows Hidden Files" + +### Styling + +- Remove TODO message on the absolute_path property +- Fix shellcheck issues in deb-package.sh +- Fix shellcheck issues in deb-package.sh +- Fix shellcheck issues in deb-package.sh + +### Testing + +- Remove vhs from flake +- Remove vhs-runner files +- Dump trycmd from nix sandbox +- Fix name of trydump +- Add trycmd +- Add nix feature +- Add example long tests for sandbox +- Set itests files to unix epoch +- Set itest files to unix epoch +- Refactor setting unix epoch +- Auto discard old definitions +- Fix test reference +- Add long_all_nix.toml +- Add long_blocksize_nix.toml +- Add long_extended_nix.toml +- Add long_git_nix.toml +- Add long_git_repos_nix.toml +- Add long_git_repos_no_status_nix.toml +- Add long_grid_nix.toml +- Add long_header_nix.toml +- Add long_icons_nix.toml +- Add long_octal_nix.toml +- Add long_time_style_relative_nix.toml +- Freeze nix tests +- Fix trydump when no files to delete +- Adding more content to test +- Modified unix and all tests +- Regenerate nix tests +- Convert windows tests with new itest dir +- Fixed windows tests being wrong +- Added a test generator +- Add more unix_tests +- Fixed unix tests to remove any distro specific +- Removed git test breaking on nix +- Remove non-deterministic test + +### Build + +- Add compression, checksum gen for bin +- Add deny.toml +- Update flake.lock, cargo.lock +- Remove org warnings +- Remove itest +- Update flake.lock +- Add itest, idump +- Make trycmd part of checks + +### Ci + +- Don't use nix feature on ci +- Fix windows build +- 1.65 -> 1.70 +- Enforce conventional commits +- Enforce conventional commits + +### Doc + +- Remove xtests section from readme +- Add deprecation warning to xtests/readme +- Add deprecation warning to just xtest commands +- Add deprecation warning to vagrantfile +- Add guidelines for commit messages + +## [0.11.1] - 2023-09-11 + +### Bug Fixes + +- Add vendored-libgit2 feature to git2 dependency +- Filename escaping (last character lost sometimes, no hyperlink) +- Build for Windows with chrono + +### Documentation + +- Explain vendored-libgit2 +- Add homebrew, misc fixes +- Fix code of conduct link +- Update archlinux +- Remove broken dependabot link +- Add informaton about lazy_static +- Add star history +- Add bright color options in man pages +- Add bright color support in readme changelog + +### Features + +- Add highlighting of mounted directories (Linux only) +- Mark `.git` as ignored, which hides it when using `--git-ignore` +- Expose git2 feature vendored-libgit2 +- Add build commands to deb-package.sh +- Add bright colour options, change punctuation default +- Support the MSRV of Rust (1.65.0) +- Use chrono crate to handle datetime-related features + +### Miscellaneous Tasks + +- Bump actions/checkout from 3 to 4 +- Release 0.11.1 + +### Testing + +- Stabilize testing without sandbox +- Disable gif rendering + +### Build + +- Add release binaries +- Fix binary gen +- Add armhf binary + +### Deps + +- Change ansi_term to ansiterm from rustadopt + +## [0.11.0] - 2023-09-04 + +### Bug Fixes + +- Add windows implementation of is_empty_dir +- Re-align `--git-ignore` in help message +- Avoid direnv error if nix isn't installed + +### Documentation + +- Empty dir functions +- Document is_empty_dir functions +- Add function documentation for get_file_type and icon_for_file. + +### Features + +- Optimize checking for empty directories when a directory has subdirectories +- Use perfect hash tables for file types and icons +- Add backlog of icons from various exa pull requests and others +- Add backlog of icons from various exa issues + +### Miscellaneous Tasks + +- Bump git2 from 0.17.2 to 0.18.0 +- Bump uzers from 0.11.1 to 0.11.2 +- Bump DeterminateSystems/flake-checker-action from 4 to 5 +- Bump DeterminateSystems/nix-installer-action from 3 to 4 +- Bump glob from 0.3.0 to 0.3.1 +- Bump actions/stale from 5 to 8 +- Bump terminal_size from 0.1.16 to 0.2.6 +- Bump timeago from 0.3.1 to 0.4.1 +- Release 0.11.0 + +### Refactor + +- Use phf macros instead of codegen to create icon and filetype tables +- Add constants for most of the commonly used icons +- Add constants for the rest of icons used multiple times +- Rename class FileExtension to FileTypeClassifier to better reflect the purpose +- Move get_file_type to FileType enum + +### Styling + +- Is_empty_dir() was put between the unix size() and windows size() + +### Build + +- Use rust stable +- Add unstable package +- Disable clippy check 'unreadable_literal' in generated files + +## [0.10.9] - 2023-08-28 + +### Bug Fixes + +- Respect git-repos flags + +### Documentation + +- Add badge for eza gitter/matrix room +- Fix matrix link +- Add ignored flags to readme +- Add ignored flags to manual +- Add ignored flags to help +- Add ignored flags to xtest + +### Features + +- `--no-git` option + +### Miscellaneous Tasks + +- Add funding.yml +- Release 0.10.9 + +### Tree-wide + +- Fix Windows build + +### Build + +- Add convco to dev + +### Ci + +- Create flakehub-publish-tagged.yml +- Add workflow_dispatch to flakehub-pub +- Edit workflow_dispath +- Refactor workflow_dispath +- Refactor workflow_dispath +- Remove broken dispatch +- Add flakehub-backfill +- Add codeowners +- Add gierens as .deb codeowner +- Add windows to CI + +## [0.10.8] - 2023-08-22 + +### Bug Fixes + +- TextCell building of detailed grid view for hyperlink and icon options +- Block's Colours trait as for file sizes +- --blocksize completion, new description +- Option.views unit tests use --blocksize +- Add missing colon before -w/--width +- Replace exa by eza in help string +- Change exa to eza in invalid option error +- Add missing name section to eza_colors-explanation manpage +- Replace exa by eza in .gitignore + +### Documentation + +- Update issue templates +- Cafkafk -> eza-community + +### Features + +- Add git-ignored color/style option +- Add `just` and `pandoc` to devShell bc they are necessary for man +- Add `.envrc` so direnv automatically opens the nix dev environment +- Match folder icon to reflect contents +- Match folder icon to reflect contents +- --blocksize completion, new description +- Add script deb-package.sh + +### Miscellaneous Tasks + +- Bump git2 from 0.16.1 to 0.17.2 +- Bump unicode-width from 0.1.8 to 0.1.10 +- Bump libc from 0.2.93 to 0.2.147 +- Bump num_cpus from 1.13.0 to 1.16.0 +- Release 0.10.8 + +### Refactor + +- Fs::fields::Blocks +- File::blocks() name, revise calculation +- Rendering Blocksize like file sizes +- Rename Blocks column to Blocksize +- Use -S/--blocksize and, var BLOCKSIZE +- Unit tests for output.render.blocks +- Flip if (as suggested/demanded by clippy) +- Migrate to uzers lib + +### Build + +- Add charm to nix develop +- Add tests/tmp to gitignore +- Add initial tape +- Add test runner sketch +- Add test runner to justfile +- Add out.gif to .gitignore +- Add run_tests NAME arg +- Add reference main.txt +- Add gen_test +- Fix typo +- Handle arbitrary NAMES +- Remove commented out code +- Fix code formatting +- Add vhs-runner main function +- Gen_test support automatic gen +- Automatic tape detection +- Add print_msg with ansi color +- Slight documentation/refactor +- Use ansi output on all output +- Disable vhs publish ad +- Add better tracing +- Remove defective sed +- Add color variables +- Add eza-long test +- Add itest testing dir +- Add parallel runner + +### Ci + +- Help text in xtests +- Nix flake check +- Add labeler for flake +- Add flake description + +### Deps + +- Change users depedency to uzers + +### Doc + +- Add git-ignore style/color information to manpage +- --blocksize, new description +- --blocksize, new description +- --blocksize, new description +- Add gpg public key for the deb repository +- Add section about debian and ubuntu installation + +### Git + +- Add deb package to .gitignore + +## [0.10.7] - 2023-08-13 + +### Bug Fixes + +- Respect GIT_CEILING_DIRECTORIES +- MacOS flake support +- Broken zsh completion syntax + +### Features + +- Add gitlab-ci.yml +- Improve icon for Earthfile +- Better.ps1, add .psd1, .psm1 icons +- Replace .bat icon by windows cli icon +- Use TeX icons and add .bib, .bst icon +- Use Ocaml logo, add .mli, .mll, .mly +- Add many more icons +- Add -w/--width to help string +- Add -w/--width to README +- Add -w/--width to flags +- Add -w/--width to manpage +- Fish -w/--width +- Zsh -w/--width + +### Miscellaneous Tasks + +- Add PR template +- Bump log from 0.4.14 to 0.4.20 +- Release 0.10.7 + +### Refactor + +- GIT_DIR handling +- Turn unused var into value +- Fix borrowed trait implements required +- Simplify format strings +- Consistent style +- Clippy::explicit_auto_deref +- Clippy::explicit_auto_deref +- Clippy::redundant_else +- Clippy::manual_map +- Clippy::semicolon_if_nothing_returned +- Clippy::extra_unused_lifetimes +- Allow clippy::wildcard_in_or_patterns +- Clippy::uninlined_format_args +- Allow Colours::new call with self +- Clippy::explicit_iter_loop +- Clippy::uninlined_format_args +- Clippy::needless_late_init +- Clippy::useless_conversion +- Clippy::implicit_clone +- Clippy::uninlined_format_args +- Clippy::into-iter-on-ref +- Clippy::semicolon_if_nothing_returned +- Clippy::into_iter_on_ref +- Clippy::needless_lifetimes +- Clippy::uninlined_format_args +- Trivial clippy lints +- Clippy::semicolon_if_nothing_returned +- Clippy::semicolon_if_nothing_returned +- Clippy::manual_let_else +- Clippy::semicolon_if_nothing_returned +- Clippy::semicolon_if_nothing_returned +- Clippy::uninlined_format_args +- Clippy::manual_let_else +- Clippy::manual_let_else +- Clippy::manual_let_else +- Clippy::manual_let_else +- Clippy::manual_let_else +- Fix trivial cast +- Clippy::needless-borrow +- TerminalWidth::deduce to -w/--width + +### Ci + +- Create pull_request_template.md +- Add clippy check +- Add dependabot updater + +### Doc + +- Create SECURITY.md +- Create CONTRIBUTING.md + +## [0.10.6] - 2023-08-07 + +### Bug Fixes + +- Rename eza-colors-explanation +- Exa -> eza in manpage + +### Documentation + +- Adding --git-repos to help. + +### Features + +- Use GIT_DIR env var to find the repo +- Add color explanations + +### Miscellaneous Tasks + +- Release 0.10.6 + +### Doc + +- Add aur, nixpkgs installation + +### Git + +- Use GIT_DIR env var to find the repo +- Use open_from_env before discover + +## [0.10.5] - 2023-08-03 + +### Bug Fixes + +- Output wraps in terminal +- Respect icon spacing + +### Miscellaneous Tasks + +- Release 0.10.5 + +## [0.10.4] - 2023-08-02 + +### Bug Fixes + +- Syntax error + +### Features + +- Added ".out" files for latex +- Add changelog generation + +### Miscellaneous Tasks + +- Release 0.10.4 + +## [0.10.3] - 2023-07-31 + +### Bug Fixes + +- More JPG extensions +- Add compression icon to .tXX files #930 +- Dereferencing linksfile size. +- Dereferencing links users. +- Dereferencing links groups. +- Dereferencing links permissions. +- Dereferencing links timestamps. +- Add Svelte icon +- Fish completion for -i/--inode option +- Typo +- Use eprintln instead +- Use stderr on no timezone info +- Bump openssl-src from 111.15.0+1.1.1k to 111.26.0+1.1.1u +- Bump openssl-src from 111.15.0+1.1.1k to 111.26.0+1.1.1u +- Changed bin name via cargo.toml +- Change man pages to reffer to new binary name +- Change completions to new binary name +- Change completion file names +- Change name to eza +- Bump git2 from 0.13.20 to 0.16.1 +- Fixed grid bug +- Fixed grid bug +- Bump rust to 1.71.0 +- Take -a and -A equally serious +- Changed default folder icon +- Add clippy as part of the toolchain +- Change license icon +- Change gpg icons to keys +- Add icon for ocaml (.ml extension) +- Better license icon +- Replace obsolete icons +- Add Emacs icon for .el and org-mode for .org +- Added icons for .rmeta +- Add icon support for .mjs, .cjs, .mts, .cts files +- Add webpack.config.cjs to immediate files list +- .ipynb icon comment +- Removed result +- Update --version info +- Sort is_immediate +- Add flake, autoconf, cargo lock +- Added trailing commas +- Update snapscraft.yaml +- Remove accidentally commited test files + +### Feat + +- Add JPF to image filetype + +### Features + +- Add support Typescript and ReasonML projects +- New Icons and CLI argument to suppress icons +- Add sty file +- Add julia file extension icon +- Add symlink dereferencing flag +- Add -X/--dereference completions +- Add -X/--dereference completions +- Symlinks report their target's valid size +- Update Cargo.toml to optimise binaries for size +- Add status for git repos +- Add selinux contexts support +- Add -o shorcut to --octal-permissions +- Hyperlink flag +- Update Cargo.toml to optimise binaries for size +- Add git-status-.* completions +- Zsh add git-status-.* completions +- Add git-status-.* completions +- Add Zig module icons +- Add icon for Vagrantfile +- Add git icon to .gitignore_global file +- Left align relative time +- Add support for --time-style=relative +- Add vim icon +- Add justfile +- Add pxm +- Add compressed types +- Add compressed icons + +### Fixup + +- Split prefix tests by property + +### Improve + +- Vim icon + +### Makefile + +- Be compatible with BSD and OS X + +### Miscellaneous Tasks + +- Update zoneinfo_compiled, datetime to 0.5 +- Update users to 0.10 +- PR feedback +- Bump to v0.10.2 +- Bump to v0.10.3 +- Update cargo lock + +### Refactor + +- Use shorthand fields +- Removed commented code +- Sorted file types, color table + +### StatResult + +- :Path -> Dir + +### Styling + +- Add icon for reStructuredText (src) files + +### Testing + +- Change to /usr/bin/env bash + +### ToStr + +- :to_str -> ToString::to_string + +### Add + +- Mp2 audio format icon + +### Build + +- Use binary name only + +### Ci + +- Remove unused .github files +- Remove unused .github files +- Create unit-tests.yml +- Create unit-tests.yml +- Add trivial nix flake +- Add treefmt, rust-toolchain, nixfmt +- Add .#test, .#clippy, .#check +- Add nix flake +- Change branch +- Bump rust to 1.71.0 +- Automatically mark issues/PRs stale +- Run tests when building with nix +- Moving actions to dtolnay's version +- Update Cargo.toml +- Create labeler.yml +- Add snap to labeler.yml +- Add filetype.rs autolabel + +### Details + +- `filter` is only used when recursing + +### Doc + +- Add -X/--dereference flag +- Change name in README.md +- Add `nix run` to readme +- Fix flow issue +- Fix typos +- Add mandatory snowflake emoji +- Document nix flake development +- Document nix flakew +- Update README.md +- Update README.md +- Update README.md +- Update README.md +- Update README.md +- Readme change screenshot to eza +- Add CoC badge to readme +- Add CODE_OF_CONDUCT.md +- Add crates.io badge, license badge +- Fix links +- Update README.md +- Update README.md + +### Documentation + +- Add hint how to install exa on Android / Termux + +### Git-feature + +- Display if a file is updated but unmerged (conflicted) + +### Icons + +- Add Gentoo for .ebuild + +### Io + +- :Result -> IOResult + +### Src/main.rs + +- Remove clippy::unnested_or_patterns + +### Vagrant + +- Update apt before installing + + diff --git a/collector/compile-benchmarks/eza-0.21.2/CODE_OF_CONDUCT.md b/collector/compile-benchmarks/eza-0.21.2/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..d056b0921 --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/CODE_OF_CONDUCT.md @@ -0,0 +1,140 @@ + +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall + community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or advances of + any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email address, + without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at: + +matrix: @cafkafk:nixos.dev + +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of +actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available at +[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at +[https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[Mozilla CoC]: https://github.com/mozilla/diversity +[FAQ]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations + diff --git a/collector/compile-benchmarks/eza-0.21.2/CONTRIBUTING.md b/collector/compile-benchmarks/eza-0.21.2/CONTRIBUTING.md new file mode 100644 index 000000000..330561840 --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/CONTRIBUTING.md @@ -0,0 +1,195 @@ + +# Contributing to eza + +If you'd like to contribute to eza, there are several things you should make +sure to familiarize yourself with first. + +- Code of conduct [Contributor Covenant Code of Conduct](CODE_OF_CONDUCT.md) +- Requirement of conformance to [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) +- Requirement of conformance to [Semantic Versioning](https://semver.org/) +- The [Security Policy](SECURITY.md) +- [Free and Open Source (FOSS) software](https://www.gnu.org/philosophy/free-sw.en.html) + +## Hacking on eza + +It is strongly recommended that you install Nix for hacking on eza. We leverage +nix as a way to easily test and distribute eza to many users, and it allows us +to provide multiple tools easily for developers. Instead of having to install +each dependency manually and setting up a development environment, Nix allows +you to use the same environment as the devs use. + +Therefore, it is useful that you have a version of Nix installed with the +"experimental" feature flakes enabled. Further, to make hacking on eza as easy +as possible for yourself, you'd do yourself a favor to install +[direnv](https://direnv.net/). + +When you enter the eza repository, if you have `direnv` installed, you'll be +prompted to allow it with `direnv allow`. Doing this will save you from having +to manually enter the development environment each time you open the folder. If +you don't have direnv installed however, you can run `nix develop` in a pinch, +to enter the direnv. + +The development environment includes basic checks of conformance to conventional +commits, cargo clippy lints, and much more. + +It also contains a pre-commit-hook making it a lot easier not to make potential +mistakes that will unnecessarily delay getting your PRs accepted. Most +importantly, it ensures your commits are conforming to conventional commits. + +Some useful commands include: +- `nix flake check`: checks everything is correct. +- `nix build`: build eza. +- `nix build .#test`: runs eza's cargo tests +- `nix build .#clippy`: runs clippy on eza +- `nix fmt`: automatically formats your code as required by flake cheks and + pre-commit-hooks.nix +- `just itest`: runs integration tests + +**For non-nix users,** +There are traditional `pre-commit` hooks, which you can install with your system package manager or +`brew|pip install pre-commit`, and run `pre-commit install -c .pre-commit-config-non-nix.yaml` in the root of the repository. +Then these hooks will run automatically when you commit. + +The [just](https://github.com/casey/just) command runner can be used to run some +helpful development commands, in a manner similar to `make`. Run `just --list` +to get an overview of what’s available. + +To compile the manual pages, you will need [pandoc](https://pandoc.org/), which +the nix flake should have installed for you. The `just man` command will +compile the Markdown into manual pages, which it will place in the `target/man` +directory. + +eza depends on [libgit2](https://github.com/rust-lang/git2-rs) for certain +features. If you’re unable to compile libgit2, you can opt out of Git support +by running `cargo build --no-default-features`. Again, the nix flake should +have taken care of this for you, if not, please file an issue. + +If you intend to compile for musl, you will need to use the flag +`vendored-openssl` if you want to get the Git feature working. The full command +is `cargo build --release --target=x86_64-unknown-linux-musl --features +vendored-openssl,git`. + +If you want more information on the tests please read: +[testing on eza](https://github.com/eza-community/eza/blob/main/TESTING.md) + +## Creating a PR + +First, use the pull request template. + +Please make sure that the thing you worked on... actually works. Make sure to +also add how you ensured this in the PR description. Further, it's expected +that you do your best to check for regressions. + +If your PR introduces a flag, you MUST: +- Add completions for bash, zsh, fish, nushell +- Add documentation to the man page +- Add your option to the help flag +- Add your option to the README.md + +Before submitting, you SHOULD have run `nix flake check` and ensured that all +issues are addressed. For formatting issues, `nix fmt` will format the code for +you. Most clippy issues can be resolved with `cargo clippy --fix` (although it +might be educational to fix them yourself). If you have reuse issues, you can +run the following command to annotate your code: + +Here are the absolute basics: +- your commit summary MUST follow conventional commits. +- your commits SHOULD be separated into small, logical chunks. +- reviewers may ask you to rebase your commits into more sensible chunks. +- your PR will need to pass CI and local `cargo test`. +- you may be asked to refactor parts of your code by reviewers. + +Remember that no one here is an employee, and treat everyone with respect, as +the code of conduct specifies. Also remember to be patient if it takes a while +to get a response on your PR. Usually it doesn't, but there's only so many +hours in a day, and if possible, there would be no delay. The delay alone is +evidence of it's own necessity. + +## Commit Messages +A common commit message contains at least a summary and reference with +closing action to the corresponding issue if any, and may also include a +description and signature. + +### Summary +For you commit messages, please use the first line for a brief summary what +the commit changes. Try to stay within the 72 char limit and prepend what type +of change. See the following list for some guidance: +- feat: adds a new feature to eza +- feat(zsh): adds something to zsh completion +- refactor: revises parts of the code +- docs(readme): revise the README +- docs(man): revision of the man pages +- fix: bugfix in the code base +- fix(ci): bugfix in the continuous integration +- ... + +Note that this list is not complete and there may be cases where a commit +could be characterized by different types, so just try to make your best +guess. This spares the maintainers a lot of work when merging your PR. + +### Description +If you commit warrants it due to complexity or external information required +to follow it, you should add a more detailed description of the changes, +reasoning and also link external documentation if necessary. This description +should go two lines below the summary and except for links stay in the 80 char +limit. + +### Issue Reference +If the commit resolves an issue add: `Resolves: #abc` where `abc` is the issue +number. In case of a bugfix you can also use `Fixes: #abc`. + +### Signature +You may add a signature at the end two lines below the description or +issue reference. + +### Example +Here is an example of a commit message for a breaking change that follows these rules: + +``` +fix(hyperlinks)!: TextCell building of detailed grid view, hyperlink, icon options + +The hyperlink option adds an escape sequence which in the normal TextCell +creation also becomes part of the length calculation. This patch applies +the same logic the normal grid already did, by using the filenames bare +width when a hyperlink is embedded. It also respects the ShowIcons +option just like the normal grid view. + +BREAKING CHANGE: The style codes for huge file and units where +documented to be `nt` and `ut` but the code was using `nh` and `uh`. +The code has been updated to match the documented style codes. +EXA_COLORS using style codes `nh` and `uh` will need to be updated to +use `nt` and `ut`. + +Resolves: #129 +Ref: #473, #319 + +Co-authored-by: 9glenda +Signed-off-by: Christina Sørensen +``` + +### Additional Examples + +- feat: add column selection +- fix(output): fix width issue with columns +- test(fs): add tests for filesystem metadata +- feat!: breaking change / feat(config)!: implement config file +- chore(deps): update dependencies + +### Commit types + +- build: Changes that affect the build system or external dependencies (example libgit2) +- ci: Changes to CI configuration files and scripts (example scopes: Nix, Vagrant, Docker) +- chore: Changes which do not change source code or tests (example: changes to the build process, auxiliary tools, libraries) +- docs: Documentation, README, completions, manpage only +- feat: A new feature +- fix: A bug fix +- perf: A code change that improves or addresses a performance issue +- refactor: A code change that neither fixes a bug nor adds a feature +- revert: Revert something +- style: Changes that do not affect the meaning of the code (example: clippy) +- test: Adding missing tests or correcting existing tests diff --git a/collector/compile-benchmarks/eza-0.21.2/Cargo.lock b/collector/compile-benchmarks/eza-0.21.2/Cargo.lock new file mode 100644 index 000000000..cf0a93fe6 --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/Cargo.lock @@ -0,0 +1,2066 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "ansi-width" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "219e3ce6f2611d83b51ec2098a12702112c29e57203a6b0a0929b2cddb486608" +dependencies = [ + "unicode-width 0.1.14", +] + +[[package]] +name = "anstream" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" +dependencies = [ + "anstyle", + "once_cell", + "windows-sys 0.59.0", +] + +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "automod" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebb4bd301db2e2ca1f5be131c24eb8ebf2d9559bc3744419e93baf8ddea7e670" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets", +] + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bitflags" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" + +[[package]] +name = "bumpalo" +version = "3.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" + +[[package]] +name = "by_address" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64fa3c856b712db6612c019f14756e64e4bcea13337a6b33b696333a9eaa2d06" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cc" +version = "1.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e3a13707ac958681c13b39b458c073d0d9bc8a22cb1b2f4c8e55eb72c13f362" +dependencies = [ + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "windows-link", +] + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "clap" +version = "4.5.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.5.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2" +dependencies = [ + "anstyle", + "clap_lex", +] + +[[package]] +name = "clap_lex" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" + +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + +[[package]] +name = "content_inspector" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7bda66e858c683005a53a9a60c69a4aca7eeaa45d124526e389f7aec8e62f38" +dependencies = [ + "memchr", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "criterion" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "is-terminal", + "itertools", + "num-traits", + "once_cell", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" + +[[package]] +name = "datetime" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c3f7a77f3e57fedf80e09136f2d8777ebf621207306f6d96d610af048354bc" +dependencies = [ + "libc", + "redox_syscall 0.1.57", + "winapi", +] + +[[package]] +name = "deranged" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "dirs" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.59.0", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "eza" +version = "0.21.2" +dependencies = [ + "ansi-width", + "backtrace", + "chrono", + "criterion", + "dirs", + "git2", + "glob", + "libc", + "locale", + "log", + "natord-plus-plus", + "nu-ansi-term", + "number_prefix", + "palette", + "path-clean", + "percent-encoding", + "phf", + "plist", + "proc-mounts", + "rayon", + "serde", + "serde_norway", + "terminal_size", + "timeago", + "trycmd", + "unicode-width 0.2.0", + "uutils_term_grid", + "uzers", + "windows-sys 0.59.0", + "zoneinfo_compiled", +] + +[[package]] +name = "fast-srgb8" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd2e7510819d6fbf51a5545c8f922716ecfb14df168a3242f7d33e0239efe6a1" + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "filetime" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" +dependencies = [ + "cfg-if", + "libc", + "libredox", + "windows-sys 0.59.0", +] + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "git2" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5220b8ba44c68a9a7f7a7659e864dd73692e417ef0211bea133c7b74e031eeb9" +dependencies = [ + "bitflags", + "libc", + "libgit2-sys", + "log", + "openssl-sys", + "url", +] + +[[package]] +name = "glob" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" + +[[package]] +name = "half" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" +dependencies = [ + "cfg-if", + "crunchy", +] + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" + +[[package]] +name = "hermit-abi" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbd780fe5cc30f81464441920d82ac8740e2e46b29a6fad543ddd075229ce37e" + +[[package]] +name = "humantime" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b112acc8b3adf4b107a8ec20977da0273a8c386765a3ec0229bd500a1443f9f" + +[[package]] +name = "humantime-serde" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57a3db5ea5923d99402c94e9feb261dc5ee9b4efa158b0315f788cf549cc200c" +dependencies = [ + "humantime", + "serde", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "is-terminal" +version = "0.4.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "jobserver" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" +dependencies = [ + "getrandom 0.3.2", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.172" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" + +[[package]] +name = "libgit2-sys" +version = "0.18.1+1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1dcb20f84ffcdd825c7a311ae347cce604a6f084a767dec4a4929829645290e" +dependencies = [ + "cc", + "libc", + "libz-sys", + "openssl-sys", + "pkg-config", +] + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags", + "libc", + "redox_syscall 0.5.11", +] + +[[package]] +name = "libz-sys" +version = "1.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b70e7a7df205e92a1a4cd9aaae7898dac0aa555503cc0a649494d0d60e7651d" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "linux-raw-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" + +[[package]] +name = "litemap" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" + +[[package]] +name = "locale" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fdbe492a9c0238da900a1165c42fc5067161ce292678a6fe80921f30fe307fd" +dependencies = [ + "libc", +] + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "miniz_oxide" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" +dependencies = [ + "adler2", +] + +[[package]] +name = "natord-plus-plus" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66f8ac0a79782d13e4ec2f955113676d786539965b3764e06fb4e107dcc3da64" + +[[package]] +name = "normalize-line-endings" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" + +[[package]] +name = "nu-ansi-term" +version = "0.50.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399" +dependencies = [ + "serde", + "windows-sys 0.52.0", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "number_prefix" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" + +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "oorandom" +version = "11.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" + +[[package]] +name = "openssl-src" +version = "300.5.0+3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8ce546f549326b0e6052b649198487d91320875da901e7bd11a06d1ee3f9c2f" +dependencies = [ + "cc", +] + +[[package]] +name = "openssl-sys" +version = "0.9.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8288979acd84749c744a9014b4382d42b8f7b2592847b5afb2ed29e5d16ede07" +dependencies = [ + "cc", + "libc", + "openssl-src", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "os_pipe" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ffd2b0a5634335b135d5728d84c5e0fd726954b87111f7506a61c502280d982" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "palette" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cbf71184cc5ecc2e4e1baccdb21026c20e5fc3dcf63028a086131b3ab00b6e6" +dependencies = [ + "approx", + "fast-srgb8", + "palette_derive", +] + +[[package]] +name = "palette_derive" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5030daf005bface118c096f510ffb781fc28f9ab6a32ab224d8631be6851d30" +dependencies = [ + "by_address", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "partition-identity" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa925f9becb532d758b0014b472c576869910929cf4c3f8054b386f19ab9e21" +dependencies = [ + "thiserror 1.0.69", +] + +[[package]] +name = "path-clean" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17359afc20d7ab31fdb42bb844c8b3bb1dabd7dcf7e68428492da7f16966fcef" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_macros", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +dependencies = [ + "phf_shared", + "rand", +] + +[[package]] +name = "phf_macros" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "plist" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac26e981c03a6e53e0aee43c113e3202f5581d5360dae7bd2c70e800dd0451d" +dependencies = [ + "base64", + "indexmap", + "quick-xml", + "time", +] + +[[package]] +name = "plotters" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" + +[[package]] +name = "plotters-svg" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proc-mounts" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d652f8435d0ab70bf4f3590a6a851d59604831a458086541b95238cc51ffcf2" +dependencies = [ + "partition-identity", +] + +[[package]] +name = "quick-xml" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d3a6e5838b60e0e8fa7a43f22ade549a37d61f8bdbe636d0d7816191de969c2" +dependencies = [ + "memchr", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "redox_syscall" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" + +[[package]] +name = "redox_syscall" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_users" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" +dependencies = [ + "getrandom 0.2.15", + "libredox", + "thiserror 2.0.12", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustix" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustversion" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_norway" +version = "0.9.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e408f29489b5fd500fab51ff1484fc859bb655f32c671f307dcd733b72e8168c" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml-norway", +] + +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "similar" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" + +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + +[[package]] +name = "smallvec" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" + +[[package]] +name = "snapbox" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96dcfc4581e3355d70ac2ee14cfdf81dce3d85c85f1ed9e2c1d3013f53b3436b" +dependencies = [ + "anstream", + "anstyle", + "content_inspector", + "dunce", + "filetime", + "libc", + "normalize-line-endings", + "os_pipe", + "similar", + "snapbox-macros", + "tempfile", + "wait-timeout", + "walkdir", + "windows-sys 0.59.0", +] + +[[package]] +name = "snapbox-macros" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16569f53ca23a41bb6f62e0a5084aa1661f4814a67fa33696a79073e03a664af" +dependencies = [ + "anstream", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "syn" +version = "2.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tempfile" +version = "3.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" +dependencies = [ + "fastrand", + "getrandom 0.3.2", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "terminal_size" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45c6481c4829e4cc63825e62c49186a34538b7b2750b73b266581ffb612fb5ed" +dependencies = [ + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl 2.0.12", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.3.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" + +[[package]] +name = "time-macros" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "timeago" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1710e589de0a76aaf295cd47a6699f6405737dbfd3cf2b75c92d000b548d0e6" + +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "trycmd" +version = "0.15.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8b5cf29388862aac065d6597ac9c8e842d1cc827cb50f7c32f11d29442eaae4" +dependencies = [ + "anstream", + "automod", + "glob", + "humantime", + "humantime-serde", + "rayon", + "serde", + "shlex", + "snapbox", + "toml_edit", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "unicode-width" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" + +[[package]] +name = "unsafe-libyaml-norway" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39abd59bf32521c7f2301b52d05a6a2c975b6003521cbd0c6dc1582f0a22104" + +[[package]] +name = "url" +version = "2.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "uutils_term_grid" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcba141ce511bad08e80b43f02976571072e1ff4286f7d628943efbd277c6361" +dependencies = [ + "ansi-width", +] + +[[package]] +name = "uzers" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4df81ff504e7d82ad53e95ed1ad5b72103c11253f39238bcc0235b90768a97dd" +dependencies = [ + "libc", + "log", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "wait-timeout" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" +dependencies = [ + "libc", +] + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-core" +version = "0.61.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" + +[[package]] +name = "windows-result" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63d3fcd9bba44b03821e7d699eeee959f3126dcc4aa8e4ae18ec617c2a5cea10" +dependencies = [ + "memchr", +] + +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags", +] + +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zoneinfo_compiled" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64fbebe65e899530f43bd760b23fda8f141118f4db49952b02998cbd0907a5de" +dependencies = [ + "byteorder", + "datetime", +] diff --git a/collector/compile-benchmarks/eza-0.21.2/Cargo.toml b/collector/compile-benchmarks/eza-0.21.2/Cargo.toml new file mode 100644 index 000000000..e92786d16 --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/Cargo.toml @@ -0,0 +1,241 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2021" +rust-version = "1.81.0" +name = "eza" +version = "0.21.2" +authors = ["Christina Sørensen "] +build = "build.rs" +exclude = [ + "/docs/", + "/devtools/", + "/snap/", + "/tests/", + "/.config/", + "/.github/", + "/deb.asc", + "/deny.toml", + "/flake.*", + "/justfile", + "/powertest.yaml", + "/rust-toolchain.toml", + "/.envrc", + "/.gitignore", + "/.git-blame-ignore-revs", + "/.pre-commit-config-non-nix.yaml", +] +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = "A modern replacement for ls" +homepage = "https://github.com/eza-community/eza" +readme = "README.md" +categories = ["command-line-utilities"] +license = "EUPL-1.2" +repository = "https://github.com/eza-community/eza" + +[package.metadata.deb] +assets = [ + [ + "target/release/eza", + "/usr/bin/eza", + "0755", +], + [ + "target/release/../man/eza.1", + "/usr/share/man/man1/eza.1", + "0644", +], + [ + "target/release/../man/eza_colors.5", + "/usr/share/man/man5/eza_colors.5", + "0644", +], + [ + "target/release/../man/eza_colors-explanation.5", + "/usr/share/man/man5/eza_colors-explanation.5", + "0644", +], + [ + "completions/bash/eza", + "/usr/share/bash-completion/completions/eza", + "0644", +], + [ + "completions/zsh/_eza", + "/usr/share/zsh/site-functions/_eza", + "0644", +], + [ + "completions/fish/eza.fish", + "/usr/share/fish/vendor_completions.d/eza.fish", + "0644", +], +] +depends = "$auto" +extended-description = """ +eza is a modern, maintained replacement for ls +""" +license-file = [ + "LICENCE", + "4", +] +priority = "optional" +section = "utils" + +[profile.release] +opt-level = 3 +lto = true +codegen-units = 1 +panic = "abort" +strip = true + +#[lib] +#name = "eza" +#path = "src/lib.rs" + +[[bin]] +name = "eza" +path = "src/main.rs" + +[[bench]] +name = "my_benchmark" +path = "benches/my_benchmark.rs" +harness = false + +[dependencies.ansi-width] +version = "0.1.0" + +[dependencies.backtrace] +version = "0.3" + +[dependencies.chrono] +version = "0.4.40" +features = ["clock"] +default-features = false + +[dependencies.dirs] +version = "6.0.0" + +[dependencies.git2] +version = "0.20" +optional = true +default-features = false + +[dependencies.glob] +version = "0.3" + +[dependencies.libc] +version = "0.2" + +[dependencies.locale] +version = "0.2" + +[dependencies.log] +version = "0.4" + +[dependencies.natord-plus-plus] +version = "2.0" + +[dependencies.nu-ansi-term] +version = "0.50.1" +features = [ + "serde", + "derive_serde_style", +] + +[dependencies.number_prefix] +version = "0.4" + +[dependencies.palette] +version = "0.7.6" +features = ["std"] +default-features = false + +[dependencies.path-clean] +version = "1.0.1" + +[dependencies.percent-encoding] +version = "2.3.1" + +[dependencies.phf] +version = "0.11.2" +features = ["macros"] + +[dependencies.plist] +version = "1.7.0" +default-features = false + +[dependencies.rayon] +version = "1.10.0" + +[dependencies.serde] +version = "1.0.219" +features = ["derive"] + +[dependencies.serde_norway] +version = "0.9" + +[dependencies.terminal_size] +version = "0.4.2" + +[dependencies.timeago] +version = "0.4.2" +default-features = false + +[dependencies.unicode-width] +version = "0.2" + +[dependencies.uutils_term_grid] +version = "0.7.0" + +[dependencies.zoneinfo_compiled] +version = "0.5.1" + +[dev-dependencies.criterion] +version = "0.5.1" +features = ["html_reports"] + +[dev-dependencies.trycmd] +version = "0.15" + +[build-dependencies.chrono] +version = "0.4.40" +features = ["clock"] +default-features = false + +[features] +default = ["git"] +git = ["git2"] +nix = [] +nix-generated = [] +nix-local = [] +powertest = [] +vendored-libgit2 = ["git2/vendored-libgit2"] +vendored-openssl = ["git2/vendored-openssl"] + +[target.'cfg(target_os = "linux")'.dependencies.proc-mounts] +version = "0.3" + +[target.'cfg(target_os = "windows")'.dependencies.windows-sys] +version = "0.59.0" +features = [ + "Win32_System_Console", + "Win32_Foundation", +] + +[target."cfg(unix)".dependencies.uzers] +version = "0.12.1" + +[workspace] diff --git a/collector/compile-benchmarks/eza-0.21.2/Cargo.toml.orig b/collector/compile-benchmarks/eza-0.21.2/Cargo.toml.orig new file mode 100644 index 000000000..a934df461 --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/Cargo.toml.orig @@ -0,0 +1,163 @@ +# SPDX-FileCopyrightText: 2024 Christina Sørensen +# SPDX-License-Identifier: EUPL-1.2 +[package] +name = "eza" +description = "A modern replacement for ls" +authors = ["Christina Sørensen "] +categories = ["command-line-utilities"] +edition = "2021" +rust-version = "1.81.0" +exclude = [ + "/docs/", + "/devtools/", + "/snap/", + "/tests/", + "/.config/", + "/.github/", + "/deb.asc", + "/deny.toml", + "/flake.*", + "/justfile", + "/powertest.yaml", + "/rust-toolchain.toml", + "/.envrc", + "/.gitignore", + "/.git-blame-ignore-revs", + "/.pre-commit-config-non-nix.yaml", +] +readme = "README.md" +homepage = "https://github.com/eza-community/eza" +license = "EUPL-1.2" +repository = "https://github.com/eza-community/eza" +version = "0.21.2" + + +[package.metadata.deb] +license-file = ["LICENCE", "4"] +depends = "$auto" +extended-description = """ +eza is a modern, maintained replacement for ls +""" +section = "utils" +priority = "optional" +assets = [ + [ + "target/release/eza", + "/usr/bin/eza", + "0755", + ], + [ + "target/release/../man/eza.1", + "/usr/share/man/man1/eza.1", + "0644", + ], + [ + "target/release/../man/eza_colors.5", + "/usr/share/man/man5/eza_colors.5", + "0644", + ], + [ + "target/release/../man/eza_colors-explanation.5", + "/usr/share/man/man5/eza_colors-explanation.5", + "0644", + ], + [ + "completions/bash/eza", + "/usr/share/bash-completion/completions/eza", + "0644", + ], + [ + "completions/zsh/_eza", + "/usr/share/zsh/site-functions/_eza", + "0644", + ], + [ + "completions/fish/eza.fish", + "/usr/share/fish/vendor_completions.d/eza.fish", + "0644", + ], +] + + +[[bin]] +name = "eza" + + +[dependencies] +rayon = "1.10.0" +chrono = { version = "0.4.40", default-features = false, features = ["clock"] } +nu-ansi-term = { version = "0.50.1", features = [ + "serde", + "derive_serde_style", +] } +glob = "0.3" +libc = "0.2" +locale = "0.2" +log = "0.4" +natord-plus-plus = "2.0" +path-clean = "1.0.1" +number_prefix = "0.4" +palette = { version = "0.7.6", default-features = false, features = ["std"] } +percent-encoding = "2.3.1" +phf = { version = "0.11.2", features = ["macros"] } +plist = { version = "1.7.0", default-features = false } +uutils_term_grid = "0.7.0" +terminal_size = "0.4.2" +timeago = { version = "0.4.2", default-features = false } +unicode-width = "0.2" +zoneinfo_compiled = "0.5.1" +ansi-width = "0.1.0" +serde = { version = "1.0.219", features = ["derive"] } +dirs = "6.0.0" +serde_norway = "0.9" +backtrace = "0.3" + +[dependencies.git2] +version = "0.20" +optional = true +default-features = false + +[target.'cfg(target_os = "linux")'.dependencies] +proc-mounts = "0.3" + +[target.'cfg(unix)'.dependencies] +uzers = "0.12.1" + +[target.'cfg(target_os = "windows")'.dependencies] +windows-sys = { version = "0.59.0", features = [ + "Win32_System_Console", + "Win32_Foundation", +] } + +[build-dependencies] +chrono = { version = "0.4.40", default-features = false, features = ["clock"] } + +[dev-dependencies] +criterion = { version = "0.5.1", features = ["html_reports"] } +trycmd = "0.15" + +[features] +default = ["git"] +git = ["git2"] +vendored-openssl = ["git2/vendored-openssl"] +vendored-libgit2 = ["git2/vendored-libgit2"] +# Should only be used inside of flake.nix +nix = [] +# Should only be used inside of flake.nix locally (not on CI) +nix-local = [] +# Should only be used inside of flake.nix +# Shouldn't ever be used in CI (slow!) +powertest = [] +nix-generated = [] + +# use LTO for smaller binaries (that take longer to build) +[profile.release] +lto = true +strip = true +opt-level = 3 +codegen-units = 1 +panic = 'abort' + +[[bench]] +name = "my_benchmark" +harness = false diff --git a/collector/compile-benchmarks/eza-0.21.2/INSTALL.md b/collector/compile-benchmarks/eza-0.21.2/INSTALL.md new file mode 100644 index 000000000..bff14c05d --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/INSTALL.md @@ -0,0 +1,303 @@ + +# Installation + +eza is available for Windows, macOS and Linux. + +### Cargo (crates.io) + +![Crates.io](https://img.shields.io/crates/v/eza?link=https%3A%2F%2Fcrates.io%2Fcrates%2Feza) + +If you already have a Rust environment set up, you can use the `cargo install` command: + + cargo install eza + +Cargo will build the `eza` binary and place it in your `CARGO_INSTALL_ROOT`. +For more details on installation location see [the cargo +book](https://doc.rust-lang.org/cargo/commands/cargo-install.html#description). + +### Cargo (git) + +If you already have a Rust environment set up, you can use the `cargo install` command in your local clone of the repo: + + git clone https://github.com/eza-community/eza.git + cd eza + cargo install --path . + +Cargo will build the `eza` binary and place it in `$HOME/.cargo`. + +### Arch Linux + +[![Arch Linux package](https://repology.org/badge/version-for-repo/arch/eza.svg)](https://repology.org/project/eza/versions) + +Eza is available in the [\[extra\]](https://archlinux.org/packages/extra/x86_64/eza/) repository of Arch Linux. + +```bash +pacman -S eza +``` + +### Debian and Ubuntu + +Eza is available from [deb.gierens.de](http://deb.gierens.de). The GPG public +key is in this repo under [deb.asc](/deb.asc). + +First make sure you have the `gpg` command, and otherwise install it via: + +```bash +sudo apt update +sudo apt install -y gpg +``` + +Then install eza via: + +```bash +sudo mkdir -p /etc/apt/keyrings +wget -qO- https://raw.githubusercontent.com/eza-community/eza/main/deb.asc | sudo gpg --dearmor -o /etc/apt/keyrings/gierens.gpg +echo "deb [signed-by=/etc/apt/keyrings/gierens.gpg] http://deb.gierens.de stable main" | sudo tee /etc/apt/sources.list.d/gierens.list +sudo chmod 644 /etc/apt/keyrings/gierens.gpg /etc/apt/sources.list.d/gierens.list +sudo apt update +sudo apt install -y eza +``` +_Note_: In strict apt environments, you may need to add the target: `echo "deb [arch=amd64 signed-by=...` + +### Nix (Linux, MacOS) + +[![nixpkgs unstable package](https://repology.org/badge/version-for-repo/nix_unstable/eza.svg)](https://repology.org/project/eza/versions) + +> **Note** +> Installing packages imperatively isn't idiomatic Nix, as this can lead to [many issues](https://stop-using-nix-env.privatevoid.net/). + +Eza is available from [Nixpkgs](https://github.com/NixOS/nixpkgs) and from the +flake in this repository. + +For `nix profile` users: + +```shell +nix profile install nixpkgs#eza +``` + +For `nix-env` users: + +```shell +nix-env -i eza +``` + +**Declarative Nix Installations** + +- Simple NixOS installation: [rfaulhaber/dotfiles](https://github.com/rfaulhaber/dotfiles/blob/a8d084d178efd0592b7ac02d34a450fb58913aca/nix/modules/programs/eza/default.nix#L15) +- Using the flake via NixOS: [hallettj/home.nix](https://github.com/hallettj/home.nix/blob/a8388483e5d78e110be73c5af0e7f0e3ca8f8aa3/flake.nix#L19) +- Using home-manager on NixOS: [Misterio77/nix-config](https://github.com/Misterio77/nix-config/blob/6867d66a2fe7899c608b9c8e5a8f9aee279d188b/home/misterio/features/cli/fish.nix#L6) + +### Gentoo + +[![Gentoo package](https://repology.org/badge/version-for-repo/gentoo/eza.svg)](https://repology.org/project/eza/versions) + +On Gentoo, eza is available as a package [`sys-apps/eza`](https://packages.gentoo.org/packages/sys-apps/eza): + +```bash +emerge --ask sys-apps/eza +``` + +### openSUSE + +Eza is available at [openSUSE:Factory/eza](https://build.opensuse.org/package/show/openSUSE:Factory/eza): + +```bash +zypper ar https://download.opensuse.org/tumbleweed/repo/oss/ factory-oss +zypper in eza +``` + +The preceding repository also contains the Bash, Fish, and Zsh completions. + +### Fedora + +[![Fedora package](https://repology.org/badge/version-for-repo/fedora_39/rust:eza.svg)](https://repology.org/project/eza/versions) + +> ⚠️ **Note:** As of **Fedora 42**, `eza` is **no longer available** in the official Fedora repositories due to the absence of an active maintainer. +> +> If you're using Fedora 42 or newer, consider one of these options: +> +> - **Use a pre-built binary** from the [Releases](https://github.com/eza-community/eza/releases) page +> - **Build from source** by following the [Cargo (git)](#cargo-git) instructions above +> +> 💬 Interested in helping? [Become a Fedora package maintainer](https://docs.fedoraproject.org/en-US/package-maintainers/) or reach out via [Matrix](https://matrix.to/#/#eza-community:gitter.im). + +For Fedora versions **prior to 42**, `eza` is available in the official repository: + +```bash +sudo dnf install eza +``` + +### Void Linux + +[![Void Linux package](https://repology.org/badge/version-for-repo/void_x86_64/eza.svg)](https://repology.org/project/eza/versions) + +Eza is available as the [eza](https://github.com/void-linux/void-packages/tree/master/srcpkgs/eza) package in the official Void Linux repository. + +```bash +sudo xbps-install eza +``` + +### Termux + +Eza is available as the [eza](https://github.com/termux/termux-packages/tree/master/packages/eza) package in the official Termux repository. + +```bash +pkg install eza +``` + +### Manual (Linux) + +Example is for x86_64 GNU, replaces the file names if downloading for a different arch. + +```shell +wget -c https://github.com/eza-community/eza/releases/latest/download/eza_x86_64-unknown-linux-gnu.tar.gz -O - | tar xz +sudo chmod +x eza +sudo chown root:root eza +sudo mv eza /usr/local/bin/eza +``` + +If `exa` was installed before, replace it with `eza`: +```shell +sudo rm -f /usr/local/bin/exa +sudo ln -s /usr/local/bin/eza /usr/local/bin/exa +``` + +### Pixi (Linux, MacOS, and Windows) + +[![conda-forge package](https://img.shields.io/conda/vn/conda-forge/eza)](https://prefix.dev/channels/conda-forge/packages/eza) + +Eza is available in the conda-forge repository and can be installed using [Pixi](https://pixi.sh/latest/): + +```shell +pixi global install eza +``` + +### Brew (MacOS) + +[![Homebrew package](https://repology.org/badge/version-for-repo/homebrew/eza.svg)](https://repology.org/project/eza/versions) + +Eza is available from [Homebrew](https://formulae.brew.sh/formula/eza#default). + +To install eza, run: + +```shell +brew install eza +``` + +### MacPorts (macOS) + +[![MacPorts port](https://repology.org/badge/version-for-repo/macports/eza.svg)](https://repology.org/project/eza/versions) + +On macOS, eza is also available via [MacPorts](https://ports.macports.org/port/eza/). + +To install eza, run: + +```shell +sudo port install eza +``` + +### Winget (Windows) + +[![Windows package](https://repology.org/badge/version-for-repo/winget/eza.svg)](https://repology.org/project/eza/versions) + +Eza is available on Winget. + +To install eza, run: + +```shell +winget install eza-community.eza +``` + +### Scoop (Windows) + +[![Windows package](https://repology.org/badge/version-for-repo/scoop/eza.svg)](https://repology.org/project/eza/versions) + +Eza is available from [Scoop](https://scoop.sh/#/apps?q=eza&id=a52070d25f94bbcc884f80bef53eb47ed1268198). + +To install eza, run: + +```shell +scoop install eza +``` + +### Flox (Linux, macOS, Windows WSL) + +Eza is available from [Flox](https://flox.dev). + +To install eza, run: + +```shell +flox install eza +``` + +### X-CMD (Linux, macOS, Windows WSL, Windows GitBash) + +Eza is available from [x-cmd](https://www.x-cmd.com). + +To install eza, run: + +```shell +x env use eza +# or +x eza +``` + +### fox (Linux, macOS) + +Eza is available from [fox](https://www.getfox.sh/). + +To install eza, run: + +```shell +fox install eza +``` + +### Completions + +#### For zsh: + +> **Note** +> Change `~/.zshrc` to your preferred zsh config file. + +##### Clone the repository: + +```sh +git clone https://github.com/eza-community/eza.git +``` + +##### Add the completion path to your zsh configuration: + +Replace `` with the actual path where you cloned the `eza` repository. + +```sh +echo 'export FPATH="/completions/zsh:$FPATH"' >> ~/.zshrc +``` + +##### Reload your zsh configuration: + +```sh +source ~/.zshrc +``` + + +#### For zsh with homebrew: + +In case zsh completions don't work out of the box with homebrew, add the +following to your `~/.zshrc`: + +```bash +if type brew &>/dev/null; then + FPATH="$(brew --prefix)/share/zsh/site-functions:${FPATH}" + autoload -Uz compinit + compinit +fi +``` + +For reference: +- https://docs.brew.sh/Shell-Completion#configuring-completions-in-zsh +- https://github.com/Homebrew/brew/issues/8984 diff --git a/collector/compile-benchmarks/eza-0.21.2/LICENSE.txt b/collector/compile-benchmarks/eza-0.21.2/LICENSE.txt new file mode 100644 index 000000000..6d8cea430 --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/LICENSE.txt @@ -0,0 +1,190 @@ +EUROPEAN UNION PUBLIC LICENCE v. 1.2 +EUPL © the European Union 2007, 2016 + +This European Union Public Licence (the ‘EUPL’) applies to the Work (as defined below) which is provided under the +terms of this Licence. Any use of the Work, other than as authorised under this Licence is prohibited (to the extent such +use is covered by a right of the copyright holder of the Work). +The Work is provided under the terms of this Licence when the Licensor (as defined below) has placed the following +notice immediately following the copyright notice for the Work: + Licensed under the EUPL +or has expressed by any other means his willingness to license under the EUPL. + +1.Definitions +In this Licence, the following terms have the following meaning: +— ‘The Licence’:this Licence. +— ‘The Original Work’:the work or software distributed or communicated by the Licensor under this Licence, available +as Source Code and also as Executable Code as the case may be. +— ‘Derivative Works’:the works or software that could be created by the Licensee, based upon the Original Work or +modifications thereof. This Licence does not define the extent of modification or dependence on the Original Work +required in order to classify a work as a Derivative Work; this extent is determined by copyright law applicable in +the country mentioned in Article 15. +— ‘The Work’:the Original Work or its Derivative Works. +— ‘The Source Code’:the human-readable form of the Work which is the most convenient for people to study and +modify. +— ‘The Executable Code’:any code which has generally been compiled and which is meant to be interpreted by +a computer as a program. +— ‘The Licensor’:the natural or legal person that distributes or communicates the Work under the Licence. +— ‘Contributor(s)’:any natural or legal person who modifies the Work under the Licence, or otherwise contributes to +the creation of a Derivative Work. +— ‘The Licensee’ or ‘You’:any natural or legal person who makes any usage of the Work under the terms of the +Licence. +— ‘Distribution’ or ‘Communication’:any act of selling, giving, lending, renting, distributing, communicating, +transmitting, or otherwise making available, online or offline, copies of the Work or providing access to its essential +functionalities at the disposal of any other natural or legal person. + +2.Scope of the rights granted by the Licence +The Licensor hereby grants You a worldwide, royalty-free, non-exclusive, sublicensable licence to do the following, for +the duration of copyright vested in the Original Work: +— use the Work in any circumstance and for all usage, +— reproduce the Work, +— modify the Work, and make Derivative Works based upon the Work, +— communicate to the public, including the right to make available or display the Work or copies thereof to the public +and perform publicly, as the case may be, the Work, +— distribute the Work or copies thereof, +— lend and rent the Work or copies thereof, +— sublicense rights in the Work or copies thereof. +Those rights can be exercised on any media, supports and formats, whether now known or later invented, as far as the +applicable law permits so. +In the countries where moral rights apply, the Licensor waives his right to exercise his moral right to the extent allowed +by law in order to make effective the licence of the economic rights here above listed. +The Licensor grants to the Licensee royalty-free, non-exclusive usage rights to any patents held by the Licensor, to the +extent necessary to make use of the rights granted on the Work under this Licence. + +3.Communication of the Source Code +The Licensor may provide the Work either in its Source Code form, or as Executable Code. If the Work is provided as +Executable Code, the Licensor provides in addition a machine-readable copy of the Source Code of the Work along with +each copy of the Work that the Licensor distributes or indicates, in a notice following the copyright notice attached to +the Work, a repository where the Source Code is easily and freely accessible for as long as the Licensor continues to +distribute or communicate the Work. + +4.Limitations on copyright +Nothing in this Licence is intended to deprive the Licensee of the benefits from any exception or limitation to the +exclusive rights of the rights owners in the Work, of the exhaustion of those rights or of other applicable limitations +thereto. + +5.Obligations of the Licensee +The grant of the rights mentioned above is subject to some restrictions and obligations imposed on the Licensee. Those +obligations are the following: + +Attribution right: The Licensee shall keep intact all copyright, patent or trademarks notices and all notices that refer to +the Licence and to the disclaimer of warranties. The Licensee must include a copy of such notices and a copy of the +Licence with every copy of the Work he/she distributes or communicates. The Licensee must cause any Derivative Work +to carry prominent notices stating that the Work has been modified and the date of modification. + +Copyleft clause: If the Licensee distributes or communicates copies of the Original Works or Derivative Works, this +Distribution or Communication will be done under the terms of this Licence or of a later version of this Licence unless +the Original Work is expressly distributed only under this version of the Licence — for example by communicating +‘EUPL v. 1.2 only’. The Licensee (becoming Licensor) cannot offer or impose any additional terms or conditions on the +Work or Derivative Work that alter or restrict the terms of the Licence. + +Compatibility clause: If the Licensee Distributes or Communicates Derivative Works or copies thereof based upon both +the Work and another work licensed under a Compatible Licence, this Distribution or Communication can be done +under the terms of this Compatible Licence. For the sake of this clause, ‘Compatible Licence’ refers to the licences listed +in the appendix attached to this Licence. Should the Licensee's obligations under the Compatible Licence conflict with +his/her obligations under this Licence, the obligations of the Compatible Licence shall prevail. + +Provision of Source Code: When distributing or communicating copies of the Work, the Licensee will provide +a machine-readable copy of the Source Code or indicate a repository where this Source will be easily and freely available +for as long as the Licensee continues to distribute or communicate the Work. +Legal Protection: This Licence does not grant permission to use the trade names, trademarks, service marks, or names +of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and +reproducing the content of the copyright notice. + +6.Chain of Authorship +The original Licensor warrants that the copyright in the Original Work granted hereunder is owned by him/her or +licensed to him/her and that he/she has the power and authority to grant the Licence. +Each Contributor warrants that the copyright in the modifications he/she brings to the Work are owned by him/her or +licensed to him/her and that he/she has the power and authority to grant the Licence. +Each time You accept the Licence, the original Licensor and subsequent Contributors grant You a licence to their contributions +to the Work, under the terms of this Licence. + +7.Disclaimer of Warranty +The Work is a work in progress, which is continuously improved by numerous Contributors. It is not a finished work +and may therefore contain defects or ‘bugs’ inherent to this type of development. +For the above reason, the Work is provided under the Licence on an ‘as is’ basis and without warranties of any kind +concerning the Work, including without limitation merchantability, fitness for a particular purpose, absence of defects or +errors, accuracy, non-infringement of intellectual property rights other than copyright as stated in Article 6 of this +Licence. +This disclaimer of warranty is an essential part of the Licence and a condition for the grant of any rights to the Work. + +8.Disclaimer of Liability +Except in the cases of wilful misconduct or damages directly caused to natural persons, the Licensor will in no event be +liable for any direct or indirect, material or moral, damages of any kind, arising out of the Licence or of the use of the +Work, including without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, loss +of data or any commercial damage, even if the Licensor has been advised of the possibility of such damage. However, +the Licensor will be liable under statutory product liability laws as far such laws apply to the Work. + +9.Additional agreements +While distributing the Work, You may choose to conclude an additional agreement, defining obligations or services +consistent with this Licence. However, if accepting obligations, You may act only on your own behalf and on your sole +responsibility, not on behalf of the original Licensor or any other Contributor, and only if You agree to indemnify, +defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against such Contributor by +the fact You have accepted any warranty or additional liability. + +10.Acceptance of the Licence +The provisions of this Licence can be accepted by clicking on an icon ‘I agree’ placed under the bottom of a window +displaying the text of this Licence or by affirming consent in any other similar way, in accordance with the rules of +applicable law. Clicking on that icon indicates your clear and irrevocable acceptance of this Licence and all of its terms +and conditions. +Similarly, you irrevocably accept this Licence and all of its terms and conditions by exercising any rights granted to You +by Article 2 of this Licence, such as the use of the Work, the creation by You of a Derivative Work or the Distribution +or Communication by You of the Work or copies thereof. + +11.Information to the public +In case of any Distribution or Communication of the Work by means of electronic communication by You (for example, +by offering to download the Work from a remote location) the distribution channel or media (for example, a website) +must at least provide to the public the information requested by the applicable law regarding the Licensor, the Licence +and the way it may be accessible, concluded, stored and reproduced by the Licensee. + +12.Termination of the Licence +The Licence and the rights granted hereunder will terminate automatically upon any breach by the Licensee of the terms +of the Licence. +Such a termination will not terminate the licences of any person who has received the Work from the Licensee under +the Licence, provided such persons remain in full compliance with the Licence. + +13.Miscellaneous +Without prejudice of Article 9 above, the Licence represents the complete agreement between the Parties as to the +Work. +If any provision of the Licence is invalid or unenforceable under applicable law, this will not affect the validity or +enforceability of the Licence as a whole. Such provision will be construed or reformed so as necessary to make it valid +and enforceable. +The European Commission may publish other linguistic versions or new versions of this Licence or updated versions of +the Appendix, so far this is required and reasonable, without reducing the scope of the rights granted by the Licence. +New versions of the Licence will be published with a unique version number. +All linguistic versions of this Licence, approved by the European Commission, have identical value. Parties can take +advantage of the linguistic version of their choice. + +14.Jurisdiction +Without prejudice to specific agreement between parties, +— any litigation resulting from the interpretation of this License, arising between the European Union institutions, +bodies, offices or agencies, as a Licensor, and any Licensee, will be subject to the jurisdiction of the Court of Justice +of the European Union, as laid down in article 272 of the Treaty on the Functioning of the European Union, +— any litigation arising between other parties and resulting from the interpretation of this License, will be subject to +the exclusive jurisdiction of the competent court where the Licensor resides or conducts its primary business. + +15.Applicable Law +Without prejudice to specific agreement between parties, +— this Licence shall be governed by the law of the European Union Member State where the Licensor has his seat, +resides or has his registered office, +— this licence shall be governed by Belgian law if the Licensor has no seat, residence or registered office inside +a European Union Member State. + + + Appendix + +‘Compatible Licences’ according to Article 5 EUPL are: +— GNU General Public License (GPL) v. 2, v. 3 +— GNU Affero General Public License (AGPL) v. 3 +— Open Software License (OSL) v. 2.1, v. 3.0 +— Eclipse Public License (EPL) v. 1.0 +— CeCILL v. 2.0, v. 2.1 +— Mozilla Public Licence (MPL) v. 2 +— GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3 +— Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) for works other than software +— European Union Public Licence (EUPL) v. 1.1, v. 1.2 +— Québec Free and Open-Source Licence — Reciprocity (LiLiQ-R) or Strong Reciprocity (LiLiQ-R+). + +The European Commission may update this Appendix to later versions of the above licences without producing +a new version of the EUPL, as long as they provide the rights granted in Article 2 of this Licence and protect the +covered Source Code from exclusive appropriation. +All other changes or additions to this Appendix require the production of a new EUPL version. diff --git a/collector/compile-benchmarks/eza-0.21.2/LICENSES/CC-BY-4.0.txt b/collector/compile-benchmarks/eza-0.21.2/LICENSES/CC-BY-4.0.txt new file mode 100644 index 000000000..13ca539f3 --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/LICENSES/CC-BY-4.0.txt @@ -0,0 +1,156 @@ +Creative Commons Attribution 4.0 International + + Creative Commons Corporation (“Creative Commons”) is not a law firm and does not provide legal services or legal advice. Distribution of Creative Commons public licenses does not create a lawyer-client or other relationship. Creative Commons makes its licenses and related information available on an “as-is” basis. Creative Commons gives no warranties regarding its licenses, any material licensed under their terms and conditions, or any related information. Creative Commons disclaims all liability for damages resulting from their use to the fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and conditions that creators and other rights holders may use to share original works of authorship and other material subject to copyright and certain other rights specified in the public license below. The following considerations are for informational purposes only, are not exhaustive, and do not form part of our licenses. + +Considerations for licensors: Our public licenses are intended for use by those authorized to give the public permission to use material in ways otherwise restricted by copyright and certain other rights. Our licenses are irrevocable. Licensors should read and understand the terms and conditions of the license they choose before applying it. Licensors should also secure all rights necessary before applying our licenses so that the public can reuse the material as expected. Licensors should clearly mark any material not subject to the license. This includes other CC-licensed material, or material used under an exception or limitation to copyright. More considerations for licensors. + +Considerations for the public: By using one of our public licenses, a licensor grants the public permission to use the licensed material under specified terms and conditions. If the licensor’s permission is not necessary for any reason–for example, because of any applicable exception or limitation to copyright–then that use is not regulated by the license. Our licenses grant only permissions under copyright and certain other rights that a licensor has authority to grant. Use of the licensed material may still be restricted for other reasons, including because others have copyright or other rights in the material. A licensor may make special requests, such as asking that all changes be marked or described. Although not required by our licenses, you are encouraged to respect those requests where reasonable. More considerations for the public. + +Creative Commons Attribution 4.0 International Public License + +By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions. + +Section 1 – Definitions. + + a. Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image. + + b. Adapter's License means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License. + + c. Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. + + d. Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements. + + e. Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material. + + f. Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License. + + g. Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license. + + h. Licensor means the individual(s) or entity(ies) granting rights under this Public License. + + i. Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them. + + j. Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world. + + k. You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning. + +Section 2 – Scope. + + a. License grant. + + 1. Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to: + + A. reproduce and Share the Licensed Material, in whole or in part; and + + B. produce, reproduce, and Share Adapted Material. + + 2. Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions. + + 3. Term. The term of this Public License is specified in Section 6(a). + + 4. Media and formats; technical modifications allowed. The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a)(4) never produces Adapted Material. + + 5. Downstream recipients. + + A. Offer from the Licensor – Licensed Material. Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License. + + B. No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material. + + 6. No endorsement. Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i). + +b. Other rights. + + 1. Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise. + + 2. Patent and trademark rights are not licensed under this Public License. + + 3. To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties. + +Section 3 – License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the following conditions. + + a. Attribution. + + 1. If You Share the Licensed Material (including in modified form), You must: + + A. retain the following if it is supplied by the Licensor with the Licensed Material: + + i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of warranties; + + v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable; + + B. indicate if You modified the Licensed Material and retain an indication of any previous modifications; and + + C. indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License. + + 2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information. + + 3. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable. + + 4. If You Share Adapted Material You produce, the Adapter's License You apply must not prevent recipients of the Adapted Material from complying with this Public License. + +Section 4 – Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material: + + a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database; + + b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material; and + + c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database. +For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights. + +Section 5 – Disclaimer of Warranties and Limitation of Liability. + + a. Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this disclaimer may not apply to You. + + b. To the extent possible, in no event will the Licensor be liable to You on any legal theory (including, without limitation, negligence) or otherwise for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising out of this Public License or use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. Where a limitation of liability is not allowed in full or in part, this limitation may not apply to You. + + c. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability. + +Section 6 – Term and Termination. + + a. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically. + + b. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates: + + 1. automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or + + 2. upon express reinstatement by the Licensor. + + c. For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License. + + d. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License. + + e. Sections 1, 5, 6, 7, and 8 survive termination of this Public License. + +Section 7 – Other Terms and Conditions. + + a. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed. + + b. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License. + +Section 8 – Interpretation. + + a. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License. + + b. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions. + + c. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor. + + d. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority. + +Creative Commons is not a party to its public licenses. Notwithstanding, Creative Commons may elect to apply one of its public licenses to material it publishes and in those instances will be considered the “Licensor.” Except for the limited purpose of indicating that material is shared under a Creative Commons public license or as otherwise permitted by the Creative Commons policies published at creativecommons.org/policies, Creative Commons does not authorize the use of the trademark “Creative Commons” or any other trademark or logo of Creative Commons without its prior written consent including, without limitation, in connection with any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or agreements concerning use of licensed material. For the avoidance of doubt, this paragraph does not form part of the public licenses. + +Creative Commons may be contacted at creativecommons.org. diff --git a/collector/compile-benchmarks/eza-0.21.2/LICENSES/EUPL-1.2.txt b/collector/compile-benchmarks/eza-0.21.2/LICENSES/EUPL-1.2.txt new file mode 100644 index 000000000..6d8cea430 --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/LICENSES/EUPL-1.2.txt @@ -0,0 +1,190 @@ +EUROPEAN UNION PUBLIC LICENCE v. 1.2 +EUPL © the European Union 2007, 2016 + +This European Union Public Licence (the ‘EUPL’) applies to the Work (as defined below) which is provided under the +terms of this Licence. Any use of the Work, other than as authorised under this Licence is prohibited (to the extent such +use is covered by a right of the copyright holder of the Work). +The Work is provided under the terms of this Licence when the Licensor (as defined below) has placed the following +notice immediately following the copyright notice for the Work: + Licensed under the EUPL +or has expressed by any other means his willingness to license under the EUPL. + +1.Definitions +In this Licence, the following terms have the following meaning: +— ‘The Licence’:this Licence. +— ‘The Original Work’:the work or software distributed or communicated by the Licensor under this Licence, available +as Source Code and also as Executable Code as the case may be. +— ‘Derivative Works’:the works or software that could be created by the Licensee, based upon the Original Work or +modifications thereof. This Licence does not define the extent of modification or dependence on the Original Work +required in order to classify a work as a Derivative Work; this extent is determined by copyright law applicable in +the country mentioned in Article 15. +— ‘The Work’:the Original Work or its Derivative Works. +— ‘The Source Code’:the human-readable form of the Work which is the most convenient for people to study and +modify. +— ‘The Executable Code’:any code which has generally been compiled and which is meant to be interpreted by +a computer as a program. +— ‘The Licensor’:the natural or legal person that distributes or communicates the Work under the Licence. +— ‘Contributor(s)’:any natural or legal person who modifies the Work under the Licence, or otherwise contributes to +the creation of a Derivative Work. +— ‘The Licensee’ or ‘You’:any natural or legal person who makes any usage of the Work under the terms of the +Licence. +— ‘Distribution’ or ‘Communication’:any act of selling, giving, lending, renting, distributing, communicating, +transmitting, or otherwise making available, online or offline, copies of the Work or providing access to its essential +functionalities at the disposal of any other natural or legal person. + +2.Scope of the rights granted by the Licence +The Licensor hereby grants You a worldwide, royalty-free, non-exclusive, sublicensable licence to do the following, for +the duration of copyright vested in the Original Work: +— use the Work in any circumstance and for all usage, +— reproduce the Work, +— modify the Work, and make Derivative Works based upon the Work, +— communicate to the public, including the right to make available or display the Work or copies thereof to the public +and perform publicly, as the case may be, the Work, +— distribute the Work or copies thereof, +— lend and rent the Work or copies thereof, +— sublicense rights in the Work or copies thereof. +Those rights can be exercised on any media, supports and formats, whether now known or later invented, as far as the +applicable law permits so. +In the countries where moral rights apply, the Licensor waives his right to exercise his moral right to the extent allowed +by law in order to make effective the licence of the economic rights here above listed. +The Licensor grants to the Licensee royalty-free, non-exclusive usage rights to any patents held by the Licensor, to the +extent necessary to make use of the rights granted on the Work under this Licence. + +3.Communication of the Source Code +The Licensor may provide the Work either in its Source Code form, or as Executable Code. If the Work is provided as +Executable Code, the Licensor provides in addition a machine-readable copy of the Source Code of the Work along with +each copy of the Work that the Licensor distributes or indicates, in a notice following the copyright notice attached to +the Work, a repository where the Source Code is easily and freely accessible for as long as the Licensor continues to +distribute or communicate the Work. + +4.Limitations on copyright +Nothing in this Licence is intended to deprive the Licensee of the benefits from any exception or limitation to the +exclusive rights of the rights owners in the Work, of the exhaustion of those rights or of other applicable limitations +thereto. + +5.Obligations of the Licensee +The grant of the rights mentioned above is subject to some restrictions and obligations imposed on the Licensee. Those +obligations are the following: + +Attribution right: The Licensee shall keep intact all copyright, patent or trademarks notices and all notices that refer to +the Licence and to the disclaimer of warranties. The Licensee must include a copy of such notices and a copy of the +Licence with every copy of the Work he/she distributes or communicates. The Licensee must cause any Derivative Work +to carry prominent notices stating that the Work has been modified and the date of modification. + +Copyleft clause: If the Licensee distributes or communicates copies of the Original Works or Derivative Works, this +Distribution or Communication will be done under the terms of this Licence or of a later version of this Licence unless +the Original Work is expressly distributed only under this version of the Licence — for example by communicating +‘EUPL v. 1.2 only’. The Licensee (becoming Licensor) cannot offer or impose any additional terms or conditions on the +Work or Derivative Work that alter or restrict the terms of the Licence. + +Compatibility clause: If the Licensee Distributes or Communicates Derivative Works or copies thereof based upon both +the Work and another work licensed under a Compatible Licence, this Distribution or Communication can be done +under the terms of this Compatible Licence. For the sake of this clause, ‘Compatible Licence’ refers to the licences listed +in the appendix attached to this Licence. Should the Licensee's obligations under the Compatible Licence conflict with +his/her obligations under this Licence, the obligations of the Compatible Licence shall prevail. + +Provision of Source Code: When distributing or communicating copies of the Work, the Licensee will provide +a machine-readable copy of the Source Code or indicate a repository where this Source will be easily and freely available +for as long as the Licensee continues to distribute or communicate the Work. +Legal Protection: This Licence does not grant permission to use the trade names, trademarks, service marks, or names +of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and +reproducing the content of the copyright notice. + +6.Chain of Authorship +The original Licensor warrants that the copyright in the Original Work granted hereunder is owned by him/her or +licensed to him/her and that he/she has the power and authority to grant the Licence. +Each Contributor warrants that the copyright in the modifications he/she brings to the Work are owned by him/her or +licensed to him/her and that he/she has the power and authority to grant the Licence. +Each time You accept the Licence, the original Licensor and subsequent Contributors grant You a licence to their contributions +to the Work, under the terms of this Licence. + +7.Disclaimer of Warranty +The Work is a work in progress, which is continuously improved by numerous Contributors. It is not a finished work +and may therefore contain defects or ‘bugs’ inherent to this type of development. +For the above reason, the Work is provided under the Licence on an ‘as is’ basis and without warranties of any kind +concerning the Work, including without limitation merchantability, fitness for a particular purpose, absence of defects or +errors, accuracy, non-infringement of intellectual property rights other than copyright as stated in Article 6 of this +Licence. +This disclaimer of warranty is an essential part of the Licence and a condition for the grant of any rights to the Work. + +8.Disclaimer of Liability +Except in the cases of wilful misconduct or damages directly caused to natural persons, the Licensor will in no event be +liable for any direct or indirect, material or moral, damages of any kind, arising out of the Licence or of the use of the +Work, including without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, loss +of data or any commercial damage, even if the Licensor has been advised of the possibility of such damage. However, +the Licensor will be liable under statutory product liability laws as far such laws apply to the Work. + +9.Additional agreements +While distributing the Work, You may choose to conclude an additional agreement, defining obligations or services +consistent with this Licence. However, if accepting obligations, You may act only on your own behalf and on your sole +responsibility, not on behalf of the original Licensor or any other Contributor, and only if You agree to indemnify, +defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against such Contributor by +the fact You have accepted any warranty or additional liability. + +10.Acceptance of the Licence +The provisions of this Licence can be accepted by clicking on an icon ‘I agree’ placed under the bottom of a window +displaying the text of this Licence or by affirming consent in any other similar way, in accordance with the rules of +applicable law. Clicking on that icon indicates your clear and irrevocable acceptance of this Licence and all of its terms +and conditions. +Similarly, you irrevocably accept this Licence and all of its terms and conditions by exercising any rights granted to You +by Article 2 of this Licence, such as the use of the Work, the creation by You of a Derivative Work or the Distribution +or Communication by You of the Work or copies thereof. + +11.Information to the public +In case of any Distribution or Communication of the Work by means of electronic communication by You (for example, +by offering to download the Work from a remote location) the distribution channel or media (for example, a website) +must at least provide to the public the information requested by the applicable law regarding the Licensor, the Licence +and the way it may be accessible, concluded, stored and reproduced by the Licensee. + +12.Termination of the Licence +The Licence and the rights granted hereunder will terminate automatically upon any breach by the Licensee of the terms +of the Licence. +Such a termination will not terminate the licences of any person who has received the Work from the Licensee under +the Licence, provided such persons remain in full compliance with the Licence. + +13.Miscellaneous +Without prejudice of Article 9 above, the Licence represents the complete agreement between the Parties as to the +Work. +If any provision of the Licence is invalid or unenforceable under applicable law, this will not affect the validity or +enforceability of the Licence as a whole. Such provision will be construed or reformed so as necessary to make it valid +and enforceable. +The European Commission may publish other linguistic versions or new versions of this Licence or updated versions of +the Appendix, so far this is required and reasonable, without reducing the scope of the rights granted by the Licence. +New versions of the Licence will be published with a unique version number. +All linguistic versions of this Licence, approved by the European Commission, have identical value. Parties can take +advantage of the linguistic version of their choice. + +14.Jurisdiction +Without prejudice to specific agreement between parties, +— any litigation resulting from the interpretation of this License, arising between the European Union institutions, +bodies, offices or agencies, as a Licensor, and any Licensee, will be subject to the jurisdiction of the Court of Justice +of the European Union, as laid down in article 272 of the Treaty on the Functioning of the European Union, +— any litigation arising between other parties and resulting from the interpretation of this License, will be subject to +the exclusive jurisdiction of the competent court where the Licensor resides or conducts its primary business. + +15.Applicable Law +Without prejudice to specific agreement between parties, +— this Licence shall be governed by the law of the European Union Member State where the Licensor has his seat, +resides or has his registered office, +— this licence shall be governed by Belgian law if the Licensor has no seat, residence or registered office inside +a European Union Member State. + + + Appendix + +‘Compatible Licences’ according to Article 5 EUPL are: +— GNU General Public License (GPL) v. 2, v. 3 +— GNU Affero General Public License (AGPL) v. 3 +— Open Software License (OSL) v. 2.1, v. 3.0 +— Eclipse Public License (EPL) v. 1.0 +— CeCILL v. 2.0, v. 2.1 +— Mozilla Public Licence (MPL) v. 2 +— GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3 +— Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) for works other than software +— European Union Public Licence (EUPL) v. 1.1, v. 1.2 +— Québec Free and Open-Source Licence — Reciprocity (LiLiQ-R) or Strong Reciprocity (LiLiQ-R+). + +The European Commission may update this Appendix to later versions of the above licences without producing +a new version of the EUPL, as long as they provide the rights granted in Article 2 of this Licence and protect the +covered Source Code from exclusive appropriation. +All other changes or additions to this Appendix require the production of a new EUPL version. diff --git a/collector/compile-benchmarks/eza-0.21.2/LICENSES/MIT.txt b/collector/compile-benchmarks/eza-0.21.2/LICENSES/MIT.txt new file mode 100644 index 000000000..2071b23b0 --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/LICENSES/MIT.txt @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) + +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. diff --git a/collector/compile-benchmarks/eza-0.21.2/README.md b/collector/compile-benchmarks/eza-0.21.2/README.md new file mode 100644 index 000000000..2ac1f6190 --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/README.md @@ -0,0 +1,221 @@ + + +
+
+ Special thanks to: +
+
+ + Warp sponsorship + + +### [Warp, the AI terminal for developers](https://www.warp.dev/eza) +[Available for MacOS, Linux, & Windows](https://www.warp.dev/eza)
+ +
+ +# eza + +A modern replacement for ls. + +Gitter + +[![Built with Nix](https://img.shields.io/badge/Built_With-Nix-5277C3.svg?logo=nixos&labelColor=73C3D5)](https://nixos.org) +[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](CODE_OF_CONDUCT.md) + +[![Unit tests](https://github.com/eza-community/eza/actions/workflows/unit-tests.yml/badge.svg)](https://github.com/eza-community/eza/actions/workflows/unit-tests.yml) +[![Crates.io](https://img.shields.io/crates/v/eza?link=https%3A%2F%2Fcrates.io%2Fcrates%2Feza)](https://crates.io/crates/eza) +![Crates.io](https://img.shields.io/crates/l/eza?link=https%3A%2F%2Fgithub.com%2Feza-community%2Feza%2Fblob%2Fmain%2FLICENCE) + +
+ +![eza demo gif](docs/images/screenshots.png) + +--- + +**eza** is a modern alternative for the venerable file-listing command-line program `ls` that ships with Unix and Linux operating systems, giving it more features and better defaults. +It uses colours to distinguish file types and metadata. +It knows about symlinks, extended attributes, and Git. +And it’s **small**, **fast**, and just **one single binary**. + +By deliberately making some decisions differently, eza attempts to be a more featureful, more user-friendly version of `ls`. + +--- + +**eza** features not in exa (non-exhaustive): + +- Fixes [“The Grid Bug”](https://github.com/eza-community/eza/issues/66#issuecomment-1656758327) introduced in exa 2021. +- Hyperlink support. +- Mount point details. +- Selinux context output. +- Git repo status output. +- Human readable relative dates. +- Several security fixes. +- Support for `bright` terminal colours. +- Many smaller bug fixes/changes! +- Configuration `theme.yml` file for customization of colors and icons. + +...and like, so much more that it became exhausting to update this all the time. +Like seriously, we have a lot of good stuff. + +--- + + +

Try it!

+
+ +### Nix ❄️ + +If you already have Nix setup with flake support, you can try out eza with the `nix run` command: + + nix run github:eza-community/eza + +Nix will build eza and run it. + +If you want to pass arguments this way, use e.g. `nix run github:eza-community/eza -- -ol`. + +# Installation + +eza is available for Windows, macOS and Linux. Platform and distribution +specific installation instructions can be found in [INSTALL.md](INSTALL.md). + +[![Packaging status](https://repology.org/badge/vertical-allrepos/eza.svg?columns=3)](https://repology.org/project/eza/versions) + +--- + + +

Command-line options

+
+ +eza’s options are almost, but not quite, entirely unlike `ls`’s. Quick overview: + +## Display options + +
+Click to expand + +- **-1**, **--oneline**: display one entry per line +- **-G**, **--grid**: display entries as a grid (default) +- **-l**, **--long**: display extended details and attributes +- **-R**, **--recurse**: recurse into directories +- **-T**, **--tree**: recurse into directories as a tree +- **-x**, **--across**: sort the grid across, rather than downwards +- **-F**, **--classify=(when)**: display type indicator by file names (always, auto, never) +- **--colo[u]r=(when)**: when to use terminal colours (always, auto, never) +- **--colo[u]r-scale=(field)**: highlight levels of `field` distinctly(all, age, size) +- **--color-scale-mode=(mode)**: use gradient or fixed colors in --color-scale. valid options are `fixed` or `gradient` +- **--icons=(when)**: when to display icons (always, auto, never) +- **--hyperlink**: display entries as hyperlinks +- **--absolute=(mode)**: display entries with their absolute path (on, follow, off) +- **-w**, **--width=(columns)**: set screen width in columns + +
+ +## Filtering options + +
+Click to expand + +- **-a**, **--all**: show hidden and 'dot' files +- **-d**, **--list-dirs**: list directories like regular files +- **-L**, **--level=(depth)**: limit the depth of recursion +- **-r**, **--reverse**: reverse the sort order +- **-s**, **--sort=(field)**: which field to sort by +- **--group-directories-first**: list directories before other files +- **--group-directories-last**: list directories after other files +- **-D**, **--only-dirs**: list only directories +- **-f**, **--only-files**: list only files +- **--no-symlinks**: don't show symbolic links +- **--show-symlinks**: explicitly show links (with `--only-dirs`, `--only-files`, to show symlinks that match the filter) +- **--git-ignore**: ignore files mentioned in `.gitignore` +- **-I**, **--ignore-glob=(globs)**: glob patterns (pipe-separated) of files to ignore + +Pass the `--all` option twice to also show the `.` and `..` directories. + +
+ +## Long view options + +
+Click to expand + +These options are available when running with `--long` (`-l`): + +- **-b**, **--binary**: list file sizes with binary prefixes +- **-B**, **--bytes**: list file sizes in bytes, without any prefixes +- **-g**, **--group**: list each file’s group +- **-h**, **--header**: add a header row to each column +- **-H**, **--links**: list each file’s number of hard links +- **-i**, **--inode**: list each file’s inode number +- **-m**, **--modified**: use the modified timestamp field +- **-M**, **--mounts**: Show mount details (Linux and MacOS only). +- **-S**, **--blocksize**: show size of allocated file system blocks +- **-t**, **--time=(field)**: which timestamp field to use +- **-u**, **--accessed**: use the accessed timestamp field +- **-U**, **--created**: use the created timestamp field +- **-X**, **--dereference**: dereference symlinks for file information +- **-Z**, **--context**: list each file’s security context +- **-@**, **--extended**: list each file’s extended attributes and sizes +- **--changed**: use the changed timestamp field +- **--git**: list each file’s Git status, if tracked or ignored +- **--git-repos**: list each directory’s Git status, if tracked +- **--git-repos-no-status**: list whether a directory is a Git repository, but not its status (faster) +- **--no-git**: suppress Git status (always overrides `--git`, `--git-repos`, `--git-repos-no-status`) +- **--time-style**: how to format timestamps. valid timestamp styles are ‘`default`’, ‘`iso`’, ‘`long-iso`’, ‘`full-iso`’, ‘`relative`’, or a custom style ‘`+`’ (E.g., ‘`+%Y-%m-%d %H:%M`’ => ‘`2023-09-30 13:00`’. For more specifications on the format string, see the _`eza(1)` manual page_ and [chrono documentation](https://docs.rs/chrono/latest/chrono/format/strftime/index.html).). +- **--total-size**: show recursive directory size +- **--no-permissions**: suppress the permissions field +- **-o**, **--octal-permissions**: list each file's permission in octal format +- **--no-filesize**: suppress the filesize field +- **--no-user**: suppress the user field +- **--no-time**: suppress the time field +- **--stdin**: read file names from stdin + +Some of the options accept parameters: + +- Valid **--colo\[u\]r** options are **always**, **automatic** (or **auto** for short), and **never**. +- Valid sort fields are **accessed**, **changed**, **created**, **extension**, **Extension**, **inode**, **modified**, **name**, **Name**, **size**, **type**, and **none**. Fields starting with a capital letter sort uppercase before lowercase. The modified field has the aliases **date**, **time**, and **newest**, while its reverse has the aliases **age** and **oldest**. +- Valid time fields are **modified**, **changed**, **accessed**, and **created**. +- Valid time styles are **default**, **iso**, **long-iso**, **full-iso**, and **relative**. + + + +See the `man` pages for further documentation of usage. They are available +- online [in the repo](https://github.com/eza-community/eza/tree/main/man) +- in your terminal via `man eza`, as of version [`[0.18.13] - 2024-04-25`](https://github.com/eza-community/eza/blob/main/CHANGELOG.md#01813---2024-04-25) +
+ + +## Custom Themes +
+Click to expand + +**Eza** has recently added support for a `theme.yml` file, where you can specify all of the existing theme-ing options +available for the `LS_COLORS` and `EXA_COLORS` environment variables, as well as the option to specify different icons +for different file types and extensions. Any existing environment variables set will continue to work and will take +precedence for backwards compatibility. + +#### **New** Pre-made themes +Check out the themes available in the official [eza-themes](https://github.com/eza-community/eza-themes) repository, or contribute your own. + +An example theme file is available in `docs/theme.yml`, and needs to either be placed in a directory specified by the +environment variable `EZA_CONFIG_DIR`, or will looked for by default in `$XDG_CONFIG_HOME/eza`. + +Full details are available on the [man page](https://github.com/eza-community/eza/tree/main/man/eza_colors-explanation.5.md) and an example theme file is included [here](https://github.com/eza-community/eza/tree/main/docs/theme.yml) + +
+ + +# Hacking on eza + +If you wanna contribute to eza, firstly, you're expected to follow our +[code of conduct](https://github.com/eza-community/eza/blob/main/CODE_OF_CONDUCT.md). +After having understood the code of conduct, you can have a look at our +[CONTRIBUTING.md](https://github.com/eza-community/eza/blob/main/CONTRIBUTING.md) +for more info about actual hacking. + +[![Star History Chart](https://api.star-history.com/svg?repos=eza-community/eza&type=Date)](https://star-history.com/#eza-community/eza&Date) diff --git a/collector/compile-benchmarks/eza-0.21.2/REUSE.toml b/collector/compile-benchmarks/eza-0.21.2/REUSE.toml new file mode 100644 index 000000000..894252bc2 --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/REUSE.toml @@ -0,0 +1,80 @@ +# SPDX-FileCopyrightText: 2024 Christina Sørensen +# +# SPDX-License-Identifier: EUPL-1.2 + +version = 1 +SPDX-PackageName = "eza" +SPDX-PackageSupplier = "Christina Sørensen " + +[[annotations]] +path = "flake.lock" +precedence = "aggregate" +SPDX-FileCopyrightText = "2024 Christina Sørensen" +SPDX-License-Identifier = "EUPL-1.2" + +[[annotations]] +path = "Cargo.lock" +precedence = "aggregate" +SPDX-FileCopyrightText = "2024 Christina Sørensen" +SPDX-License-Identifier = "EUPL-1.2" + +[[annotations]] +path = ".envrc" +precedence = "aggregate" +SPDX-FileCopyrightText = "2024 Christina Sørensen" +SPDX-License-Identifier = "EUPL-1.2" + +[[annotations]] +path = "tests/**" +SPDX-FileCopyrightText = "2024 Christina Sørensen" +SPDX-License-Identifier = "EUPL-1.2" + +[[annotations]] +path = "completions/**" +SPDX-FileCopyrightText = "2024 Christina Sørensen" +SPDX-License-Identifier = "EUPL-1.2" + +[[annotations]] +path = "man/**" +SPDX-FileCopyrightText = "2024 Christina Sørensen" +SPDX-License-Identifier = "EUPL-1.2" + +[[annotations]] +path = "deb.asc" +SPDX-FileCopyrightText = "2024 Christina Sørensen" +SPDX-License-Identifier = "EUPL-1.2" + +[[annotations]] +path = "devtools/**" +SPDX-FileCopyrightText = "2024 Christina Sørensen" +SPDX-License-Identifier = "EUPL-1.2" + +[[annotations]] +path = "docs/**" +SPDX-FileCopyrightText = "2024 Christina Sørensen" +SPDX-License-Identifier = "EUPL-1.2" + +[[annotations]] +path = ".github/**.md" +SPDX-FileCopyrightText = "2024 Christina Sørensen" +SPDX-License-Identifier = "EUPL-1.2" + +# pre-commit-hooks.nix cause these to appear in commit check:( + +[[annotations]] +path = ".cache/pre-commit/README" +precedence = "aggregate" +SPDX-FileCopyrightText = "2024 Christina Sørensen" +SPDX-License-Identifier = "EUPL-1.2" + +[[annotations]] +path = ".cache/pre-commit/db.db" +precedence = "aggregate" +SPDX-FileCopyrightText = "2024 Christina Sørensen" +SPDX-License-Identifier = "EUPL-1.2" + +[[annotations]] +path = ".gitconfig" +precedence = "aggregate" +SPDX-FileCopyrightText = "2024 Christina Sørensen" +SPDX-License-Identifier = "EUPL-1.2" diff --git a/collector/compile-benchmarks/eza-0.21.2/SECURITY.md b/collector/compile-benchmarks/eza-0.21.2/SECURITY.md new file mode 100644 index 000000000..c968bccdd --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/SECURITY.md @@ -0,0 +1,21 @@ + +# Security Policy + +## Supported Versions + +This section shows which versions of eza are +currently being supported with security updates. + +| Version | Supported | +| ------- | ------------------ | +| latest | :white_check_mark: | +| < 0.10.6 | :x: | + +## Reporting a Vulnerability + +Please email all vulnerabilities to christina@cafkafk.com, with PGP encryption and signature, and ideally send along plaintext public key or instructions on where to find public key (keyserver etc.). diff --git a/collector/compile-benchmarks/eza-0.21.2/TESTING.md b/collector/compile-benchmarks/eza-0.21.2/TESTING.md new file mode 100644 index 000000000..db0faa5af --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/TESTING.md @@ -0,0 +1,34 @@ + +# Testing eza + +## Running tests + +In order to run the tests in eza you need: +- [just](https://github.com/casey/just) +- [nix](https://nixos.org) + +then either run: +- `just itest` +- `nix build -L trycmd-local` + +## Modifying tests + +In order to test your changes on eza, you will need to do one or multiple things in different cases. +You will need the additional tool +- [powertest](https://github.com/eza-community/powertest) + +You will also need to modify the `devtools/dir-generator.sh` file if you want to add some test cases + +### You added/modified an option + +Please run `just regen` to regenerate powertesting. Then look into `tests/gen` or `tests/cmd` for any tests not passing + +### You changed the output of eza + +Please run `nix build -L trydump` or `just idump` +And lookout for any test no longer passing diff --git a/collector/compile-benchmarks/eza-0.21.2/benches/my_benchmark.rs b/collector/compile-benchmarks/eza-0.21.2/benches/my_benchmark.rs new file mode 100644 index 000000000..e8cbbc0d9 --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/benches/my_benchmark.rs @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2024 Christina Sørensen +// SPDX-License-Identifier: EUPL-1.2 +// +// SPDX-FileCopyrightText: 2023-2024 Christina Sørensen, eza contributors +// SPDX-FileCopyrightText: 2014 Benjamin Sago +// SPDX-License-Identifier: MIT +use criterion::{black_box, criterion_group, criterion_main, Criterion}; + +pub fn criterion_benchmark(c: &mut Criterion) { + c.bench_function("logger", |b| { + b.iter(|| { + eza::logger::configure(black_box(std::env::var_os(eza::options::vars::EZA_DEBUG))) + }) + }); +} + +criterion_group!(benches, criterion_benchmark); +criterion_main!(benches); diff --git a/collector/compile-benchmarks/eza-0.21.2/build.rs b/collector/compile-benchmarks/eza-0.21.2/build.rs new file mode 100644 index 000000000..201d495f0 --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/build.rs @@ -0,0 +1,141 @@ +// SPDX-FileCopyrightText: 2024 Christina Sørensen +// SPDX-License-Identifier: EUPL-1.2 +// +// SPDX-FileCopyrightText: 2023-2024 Christina Sørensen, eza contributors +// SPDX-FileCopyrightText: 2014 Benjamin Sago +// SPDX-License-Identifier: MIT +/// The version string isn’t the simplest: we want to show the version, +/// current Git hash, and compilation date when building *debug* versions, but +/// just the version for *release* versions so the builds are reproducible. +/// +/// This script generates the string from the environment variables that Cargo +/// adds (http://doc.crates.io/environment-variables.html) and runs `git` to +/// get the SHA1 hash. It then writes the string into a file, which exa then +/// includes at build-time. +/// +/// - https://stackoverflow.com/q/43753491/3484614 +/// - https://crates.io/crates/vergen +use std::env; +use std::fs::File; +use std::io::{self, Write}; +use std::path::PathBuf; + +use chrono::prelude::*; + +/// The build script entry point. +fn main() -> io::Result<()> { + #![allow(clippy::write_with_newline)] + + let tagline = "eza - A modern, maintained replacement for ls"; + let url = "https://github.com/eza-community/eza"; + + let ver = if is_debug_build() { + format!( + "{}\nv{} \\1;31m(pre-release debug build!)\\0m\n\\1;4;34m{}\\0m", + tagline, + version_string(), + url + ) + } else if is_development_version() { + format!( + "{}\nv{} [{}] built on {} \\1;31m(pre-release!)\\0m\n\\1;4;34m{}\\0m", + tagline, + version_string(), + git_hash(), + build_date(), + url + ) + } else { + format!("{}\nv{}\n\\1;4;34m{}\\0m", tagline, version_string(), url) + }; + + // We need to create these files in the Cargo output directory. + let out = PathBuf::from(env::var("OUT_DIR").unwrap()); + let path = &out.join("version_string.txt"); + + // Bland version text + let mut f = + File::create(path).unwrap_or_else(|_| panic!("{}", path.to_string_lossy().to_string())); + writeln!(f, "{}", strip_codes(&ver))?; + + Ok(()) +} + +/// Removes escape codes from a string. +fn strip_codes(input: &str) -> String { + input + .replace("\\0m", "") + .replace("\\1;31m", "") + .replace("\\1;4;34m", "") +} + +/// Retrieve the project’s current Git hash, as a string. +fn git_hash() -> String { + use std::process::Command; + + String::from_utf8_lossy( + &Command::new("git") + .args(["rev-parse", "--short", "HEAD"]) + .output() + .unwrap() + .stdout, + ) + .trim() + .to_string() +} + +/// Whether we should show pre-release info in the version string. +/// +/// Both weekly releases and actual releases are --release releases, +/// but actual releases will have a proper version number. +fn is_development_version() -> bool { + cargo_version().ends_with("-pre") || env::var("PROFILE").unwrap() == "debug" +} + +/// Whether we are building in debug mode. +fn is_debug_build() -> bool { + env::var("PROFILE").unwrap() == "debug" +} + +/// Retrieves the [package] version in Cargo.toml as a string. +fn cargo_version() -> String { + env::var("CARGO_PKG_VERSION").unwrap() +} + +/// Returns the version and build parameters string. +fn version_string() -> String { + let mut ver = cargo_version(); + + let feats = nonstandard_features_string(); + if !feats.is_empty() { + ver.push_str(&format!(" [{}]", &feats)); + } + + ver +} + +/// Finds whether a feature is enabled by examining the Cargo variable. +fn feature_enabled(name: &str) -> bool { + env::var(format!("CARGO_FEATURE_{}", name)) + .map(|e| !e.is_empty()) + .unwrap_or(false) +} + +/// A comma-separated list of non-standard feature choices. +fn nonstandard_features_string() -> String { + let mut s = Vec::new(); + + if feature_enabled("GIT") { + s.push("+git"); + } else { + s.push("-git"); + } + + s.join(", ") +} + +/// Formats the current date as an ISO 8601 string. +fn build_date() -> String { + let now = Local::now(); + now.date_naive().format("%Y-%m-%d").to_string() +} diff --git a/collector/compile-benchmarks/eza-0.21.2/completions/bash/eza b/collector/compile-benchmarks/eza-0.21.2/completions/bash/eza new file mode 100644 index 000000000..dbecd8a91 --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/completions/bash/eza @@ -0,0 +1,76 @@ +# shellcheck shell=bash +_eza() { + cur=${COMP_WORDS[COMP_CWORD]} + prev=${COMP_WORDS[COMP_CWORD-1]} + + case "$prev" in + --help|-v|--version|--smart-group) + return + ;; + + --colour) + mapfile -t COMPREPLY < <(compgen -W 'always automatic auto never' -- "$cur") + return + ;; + + --icons) + mapfile -t COMPREPLY < <(compgen -W 'always automatic auto never' -- "$cur") + return + ;; + + -L|--level) + mapfile -t COMPREPLY < <(compgen -W '{0..9}' -- "$cur") + return + ;; + + -s|--sort) + mapfile -t COMPREPLY < <(compgen -W 'name filename Name Filename size filesize extension Extension date time modified changed accessed created type inode oldest newest age none --' -- "$cur") + return + ;; + + -t|--time) + mapfile -t COMPREPLY < <(compgen -W 'modified changed accessed created --' -- "$cur") + return + ;; + + --time-style) + mapfile -t COMPREPLY < <(compgen -W 'default iso long-iso full-iso relative +FORMAT --' -- "$cur") + return + ;; + + --color-scale) + mapfile -t COMPREPLY < <(compgen -W 'all age size --' -- "$cur") + return + ;; + + --color-scale-mode) + mapfile -t COMPREPLY < <(compgen -W 'fixed gradient --' -- "$cur") + return + ;; + + --absolute) + mapfile -t COMPREPLY < <(compgen -W 'on follow off --' -- "$cur") + return + ;; + esac + + case "$cur" in + # _parse_help doesn’t pick up short options when they are on the same line than long options + --*) + # colo[u]r isn’t parsed correctly so we filter these options out and add them by hand + parse_help=$(eza --help | grep -oE ' (--[[:alnum:]@-]+)' | tr -d ' ' | grep -v '\--colo') + completions=$(echo '--color --colour --color-scale --colour-scale --color-scale-mode --colour-scale-mode' "$parse_help") + mapfile -t COMPREPLY < <(compgen -W "$completions" -- "$cur") + ;; + + -*) + completions=$(eza --help | grep -oE ' (-[[:alnum:]@])' | tr -d ' ') + mapfile -t COMPREPLY < <(compgen -W "$completions" -- "$cur") + ;; + + *) + _filedir + ;; + esac +} && +complete -o filenames -o bashdefault -F _eza eza diff --git a/collector/compile-benchmarks/eza-0.21.2/completions/fish/eza.fish b/collector/compile-benchmarks/eza-0.21.2/completions/fish/eza.fish new file mode 100644 index 000000000..7589c5d8e --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/completions/fish/eza.fish @@ -0,0 +1,131 @@ +# Meta-stuff +complete -c eza -s v -l version -d "Show version of eza" +complete -c eza -l help -d "Show list of command-line options" + +# Display options +complete -c eza -s 1 -l oneline -d "Display one entry per line" +complete -c eza -s l -l long -d "Display extended file metadata as a table" +complete -c eza -s G -l grid -d "Display entries in a grid" +complete -c eza -s x -l across -d "Sort the grid across, rather than downwards" +complete -c eza -s R -l recurse -d "Recurse into directories" +complete -c eza -s T -l tree -d "Recurse into directories as a tree" +complete -c eza -s X -l dereference -d "Dereference symbolic links when displaying file information" +complete -c eza -s F -l classify -d "Display type indicator by file names" +complete -c eza -l color \ + -l colour -d "When to use terminal colours" -x -a " + always\t'Always use colour' + auto\t'Use colour if standard output is a terminal' + automatic\t'Use colour if standard output is a terminal' + never\t'Never use colour' +" +complete -c eza -l color-scale \ + -l colour-scale -d "Highlight levels 'field' distinctly" -x -a " + all\t'' + age\t'' + size\t'' +" +complete -c eza -l color-scale-mode \ + -l colour-scale-mode \ + -d "Use gradient or fixed colors in --color-scale" -x -a " + fixed\t'Highlight based on fixed colors' + gradient\t'Highlight based \'field\' in relation to other files' +" +complete -c eza -l icons -d "When to display icons" -x -a " + always\t'Always display icons' + auto\t'Display icons if standard output is a terminal' + automatic\t'Display icons if standard output is a terminal' + never\t'Never display icons' +" +complete -c eza -l no-quotes -d "Don't quote file names with spaces" +complete -c eza -l hyperlink -d "Display entries as hyperlinks" +complete -c eza -l follow-symlinks -d "Drill down into symbolic links that point to directories" +complete -c eza -l absolute -d "Display entries with their absolute path" -x -a " + on\t'Show absolute path for listed entries' + follow\t'Show absolute path with followed symlinks' + off\t'Do not show the absolute path' +" +complete -c eza -l smart-group -d "Only show group if it has a different name from owner" + +# Filtering and sorting options +complete -c eza -l group-directories-first -d "Sort directories before other files" +complete -c eza -l group-directories-last -d "Sort directories after other files" +complete -c eza -l git-ignore -d "Ignore files mentioned in '.gitignore'" +complete -c eza -s a -l all -d "Show hidden and 'dot' files. Use this twice to also show the '.' and '..' directories" +complete -c eza -s A -l almost-all -d "Equivalent to --all; included for compatibility with `ls -A`" +complete -c eza -s d -l list-dirs -d "List directories like regular files" +complete -c eza -s L -l level -d "Limit the depth of recursion" -x -a "1 2 3 4 5 6 7 8 9" +complete -c eza -s w -l width -d "Limits column output of grid, 0 implies auto-width" +complete -c eza -s r -l reverse -d "Reverse the sort order" +complete -c eza -s s -l sort -d "Which field to sort by" -x -a " + accessed\t'Sort by file accessed time' + age\t'Sort by file modified time (newest first)' + changed\t'Sort by changed time' + created\t'Sort by file modified time' + date\t'Sort by file modified time' + ext\t'Sort by file extension' + Ext\t'Sort by file extension (uppercase first)' + extension\t'Sort by file extension' + Extension\t'Sort by file extension (uppercase first)' + filename\t'Sort by filename' + Filename\t'Sort by filename (uppercase first)' + inode\t'Sort by file inode' + modified\t'Sort by file modified time' + name\t'Sort by filename' + Name\t'Sort by filename (uppercase first)' + newest\t'Sort by file modified time (newest first)' + none\t'Do not sort files at all' + oldest\t'Sort by file modified time' + size\t'Sort by file size' + time\t'Sort by file modified time' + type\t'Sort by file type' +" + +complete -c eza -s I -l ignore-glob -d "Ignore files that match these glob patterns" -r +complete -c eza -s D -l only-dirs -d "List only directories" +complete -c eza -s f -l only-files -d "List only files" +complete -c eza -l show-symlinks -d "Explicitly show symbolic links (For use with --only-dirs | --only-files)" +complete -c eza -l no-symlinks -d "Do not show symbolic links" + +# Long view options +complete -c eza -s b -l binary -d "List file sizes with binary prefixes" +complete -c eza -s B -l bytes -d "List file sizes in bytes, without any prefixes" +complete -c eza -s g -l group -d "List each file's group" +complete -c eza -s h -l header -d "Add a header row to each column" +complete -c eza -s H -l links -d "List each file's number of hard links" +complete -c eza -s i -l inode -d "List each file's inode number" +complete -c eza -s S -l blocksize -d "List each file's size of allocated file system blocks" +complete -c eza -s t -l time -d "Which timestamp field to list" -x -a " + modified\t'Display modified time' + changed\t'Display changed time' + accessed\t'Display accessed time' + created\t'Display created time' +" +complete -c eza -s m -l modified -d "Use the modified timestamp field" +complete -c eza -s n -l numeric -d "List numeric user and group IDs." +complete -c eza -l changed -d "Use the changed timestamp field" +complete -c eza -s u -l accessed -d "Use the accessed timestamp field" +complete -c eza -s U -l created -d "Use the created timestamp field" +complete -c eza -l time-style -d "How to format timestamps" -x -a " + default\t'Use the default time style' + iso\t'Display brief ISO timestamps' + long-iso\t'Display longer ISO timestamps, up to the minute' + full-iso\t'Display full ISO timestamps, up to the nanosecond' + relative\t'Display relative timestamps' + +FORMAT\t'Use custom time style' +" +complete -c eza -l total-size -d "Show recursive directory size (unix only)" +complete -c eza -l no-permissions -d "Suppress the permissions field" +complete -c eza -s o -l octal-permissions -d "List each file's permission in octal format" +complete -c eza -l no-filesize -d "Suppress the filesize field" +complete -c eza -l no-user -d "Suppress the user field" +complete -c eza -l no-time -d "Suppress the time field" +complete -c eza -s M -l mounts -d "Show mount details" +complete -c eza -l stdin -d "When piping to eza. Read file names from stdin" + +# Optional extras +complete -c eza -l git -d "List each file's Git status, if tracked" +complete -c eza -l no-git -d "Suppress Git status" +complete -c eza -l git-repos -d "List each git-repos status and branch name" +complete -c eza -l git-repos-no-status -d "List each git-repos branch name (much faster)" +complete -c eza -s '@' -l extended -d "List each file's extended attributes and sizes" +complete -c eza -s Z -l context -d "List each file's security context" diff --git a/collector/compile-benchmarks/eza-0.21.2/completions/nush/eza.nu b/collector/compile-benchmarks/eza-0.21.2/completions/nush/eza.nu new file mode 100644 index 000000000..4cc434355 --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/completions/nush/eza.nu @@ -0,0 +1,66 @@ +export extern "eza" [ + --version(-v) # Show version of eza + --help # Show list of command-line options + --oneline(-1) # Display one entry per line + --long(-l) # Display extended file metadata as a table + --grid(-G) # Display entries in a grid + --across(-x) # Sort the grid across, rather than downwards + --recurse(-R) # Recurse into directories + --tree(-T) # Recurse into directories as a tree + --dereference(-X) # Dereference symbolic links when displaying file information + --classify(-F) # Display type indicator by file names + --color # When to use terminal colours + --colour # When to use terminal colours + --color-scale # Highlight levels of file sizes distinctly + --colour-scale # Highlight levels of file sizes distinctly + --color-scale-mode # Use gradient or fixed colors in --color-scale + --colour-scale-mode # Use gradient or fixed colors in --colour-scale + --icons # When to display icons + --no-quotes # Don't quote file names with spaces + --hyperlink # Display entries as hyperlinks + --absolute # Display entries with their absolute path + --follow-symlinks # Drill down into symbolic links that point to directories + --group-directories-first # Sort directories before other files + --group-directories-last # Sort directories after other files + --git-ignore # Ignore files mentioned in '.gitignore' + --all(-a) # Show hidden and 'dot' files. Use this twice to also show the '.' and '..' directories + --almost-all(-A) # Equivalent to --all; included for compatibility with `ls -A` + --list-dirs(-d) # List directories like regular files + --level(-L): string # Limit the depth of recursion + --width(-w) # Limits column output of grid, 0 implies auto-width + --reverse(-r) # Reverse the sort order + --sort(-s) # Which field to sort by + --only-dirs(-D) # List only directories + --only-files(-f) # List only files + --show-symlinks # Explicitly show symbolic links (for use with --only-dirs | --only-files) + --no-symlinks # Do not show symbolic links + --binary(-b) # List file sizes with binary prefixes + --bytes(-B) # List file sizes in bytes, without any prefixes + --group(-g) # List each file's group + --header(-h) # Add a header row to each column + --links(-H) # List each file's number of hard links + --inode(-i) # List each file's inode number + --blocksize(-S) # List each file's size of allocated file system blocks + --time(-t) -d # Which timestamp field to list + --modified(-m) # Use the modified timestamp field + --numeric(-n) # List numeric user and group IDs. + --changed # Use the changed timestamp field + --accessed(-u) # Use the accessed timestamp field + --created(-U) # Use the created timestamp field + --time-style # How to format timestamps + --total-size # Show recursive directory size (unix only) + --no-permissions # Suppress the permissions field + --octal-permissions(-o) # List each file's permission in octal format + --no-filesize # Suppress the filesize field + --no-user # Suppress the user field + --no-time # Suppress the time field + --mounts(-M) # Show mount details + --git # List each file's Git status, if tracked + --no-git # Suppress Git status + --git-repos # List each git-repos status and branch name + --git-repos-no-status # List each git-repos branch name (much faster) + --extended(-@) # List each file's extended attributes and sizes + --context(-Z) # List each file's security context + --smart-group # Only show group if it has a different name from owner + --stdin # When piping to eza. Read file paths from stdin +] diff --git a/collector/compile-benchmarks/eza-0.21.2/completions/zsh/_eza b/collector/compile-benchmarks/eza-0.21.2/completions/zsh/_eza new file mode 100644 index 000000000..6f6a4b817 --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/completions/zsh/_eza @@ -0,0 +1,78 @@ +#compdef eza + +# Save this file as _eza in /usr/local/share/zsh/site-functions or in any +# other folder in $fpath. E.g. save it in a folder called ~/.zfunc and add a +# line containing `fpath=(~/.zfunc $fpath)` somewhere before `compinit` in your +# ~/.zshrc. + +__eza() { + # Give completions using the `_arguments` utility function with + # `-s` for option stacking like `eza -ab` for `eza -a -b` and + # `-S` for delimiting options with `--` like in `eza -- -a`. + _arguments -s -S \ + "(- *)"{-v,--version}"[Show version of eza]" \ + "(- *)"--help"[Show list of command-line options]" \ + {-1,--oneline}"[Display one entry per line]" \ + {-l,--long}"[Display extended file metadata as a table]" \ + {-G,--grid}"[Display entries as a grid]" \ + {-x,--across}"[Sort the grid across, rather than downwards]" \ + {-R,--recurse}"[Recurse into directories]" \ + {-T,--tree}"[Recurse into directories as a tree]" \ + {-X,--dereference}"[Dereference symbolic links when displaying file information]" \ + {-F,--classify}"[Display type indicator by file names]:(when):(always auto automatic never)" \ + --colo{,u}r="[When to use terminal colours]:(when):(always auto automatic never)" \ + --colo{,u}r-scale"[highlight levels of 'field' distinctly]:(fields):(all age size)" \ + --colo{,u}r-scale-mode"[Use gradient or fixed colors in --color-scale]:(mode):(fixed gradient)" \ + --icons="[When to display icons]:(when):(always auto automatic never)" \ + --no-quotes"[Don't quote filenames with spaces]" \ + --hyperlink"[Display entries as hyperlinks]" \ + --absolute"[Display entries with their absolute path]:(mode):(on follow off)" \ + --follow-symlinks"[Drill down into symbolic links that point to directories]" \ + --group-directories-first"[Sort directories before other files]" \ + --group-directories-last"[Sort directories after other files]" \ + --git-ignore"[Ignore files mentioned in '.gitignore']" \ + {-a,--all}"[Show hidden and 'dot' files. Use this twice to also show the '.' and '..' directories]" \ + {-A,--almost-all}"[Equivalent to --all; included for compatibility with \'ls -A\']" \ + {-d,--list-dirs}"[List directories like regular files]" \ + {-D,--only-dirs}"[List only directories]" \ + --no-symlinks"[Do not show symbolic links]" \ + --show-symlinks"[Explictly show symbolic links: for use with '--only-dirs'| '--only-files']" \ + {-f,--only-files}"[List only files]" \ + {-L,--level}"+[Limit the depth of recursion]" \ + {-w,--width}"+[Limits column output of grid, 0 implies auto-width]" \ + {-r,--reverse}"[Reverse the sort order]" \ + {-s,--sort}="[Which field to sort by]:(sort field):(accessed age changed created date extension Extension filename Filename inode modified oldest name Name newest none size time type)" \ + {-I,--ignore-glob}"[Ignore files that match these glob patterns]" \ + {-b,--binary}"[List file sizes with binary prefixes]" \ + {-B,--bytes}"[List file sizes in bytes, without any prefixes]" \ + --changed"[Use the changed timestamp field]" \ + {-g,--group}"[List each file's group]" \ + {-h,--header}"[Add a header row to each column]" \ + {-H,--links}"[List each file's number of hard links]" \ + {-i,--inode}"[List each file's inode number]" \ + {-m,--modified}"[Use the modified timestamp field]" \ + {-n,--numeric}"[List numeric user and group IDs.]" \ + {-S,--blocksize}"[List each file's size of allocated file system blocks.]" \ + {-t,--time}="[Which time field to show]:(time field):(accessed changed created modified)" \ + --time-style="[How to format timestamps]:(time style):(default iso long-iso full-iso relative +FORMAT)" \ + --total-size"[Show recursive directory size (unix only)]" \ + --no-permissions"[Suppress the permissions field]" \ + {-o,--octal-permissions}"[List each file's permission in octal format]" \ + --no-filesize"[Suppress the filesize field]" \ + --no-user"[Suppress the user field]" \ + --no-time"[Suppress the time field]" \ + {-u,--accessed}"[Use the accessed timestamp field]" \ + {-U,--created}"[Use the created timestamp field]" \ + --git"[List each file's Git status, if tracked]" \ + --no-git"[Suppress Git status]" \ + --git-repos"[List each git-repos status and branch name]" \ + --git-repos-no-status"[List each git-repos branch name (much faster)]" \ + {-@,--extended}"[List each file's extended attributes and sizes]" \ + {-Z,--context}"[List each file's security context]" \ + {-M,--mounts}"[Show mount details (long mode only)]" \ + '*:filename:_files' \ + --smart-group"[Only show group if it has a different name from owner]" \ + --stdin"[When piping to eza. Read file names from stdin]" +} + +__eza diff --git a/collector/compile-benchmarks/eza-0.21.2/man/eza.1.md b/collector/compile-benchmarks/eza-0.21.2/man/eza.1.md new file mode 100644 index 000000000..71b3de20d --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/man/eza.1.md @@ -0,0 +1,383 @@ +% eza(1) $version + + + + + + +NAME +==== + +eza — a modern replacement for ls + + +SYNOPSIS +======== + +`eza [options] [files...]` + +**eza** is a modern replacement for `ls`. +It uses colours for information by default, helping you distinguish between many types of files, such as whether you are the owner, or in the owning group. + +It also has extra features not present in the original `ls`, such as viewing the Git status for a directory, or recursing into directories with a tree view. + + +EXAMPLES +======== + +`eza` +: Lists the contents of the current directory in a grid. + +`eza --oneline --reverse --sort=size` +: Displays a list of files with the largest at the top. + +`eza --long --header --inode --git` +: Displays a table of files with a header, showing each file’s metadata, inode, and Git status. + +`eza --long --tree --level=3` +: Displays a tree of files, three levels deep, as well as each file’s metadata. + + +META OPTIONS +=============== + +`--help` +: Show list of command-line options. + +`-v`, `--version` +: Show version of eza. + +DISPLAY OPTIONS +=============== + +`-1`, `--oneline` +: Display one entry per line. + +`-F`, `--classify=WHEN` +: Display file kind indicators next to file names. + +Valid settings are ‘`always`’, ‘`automatic`’ (or ‘`auto`’ for short), and ‘`never`’. +The default value is ‘`automatic`’. + +The default behavior (`automatic` or `auto`) will display file kind indicators only when the standard output is connected to a real terminal. If `eza` is ran while in a `tty`, or the output of `eza` is either redirected to a file or piped into another program, file kind indicators will not be used. Setting this option to ‘`always`’ causes `eza` to always display file kind indicators, while ‘`never`’ disables the use of file kind indicators. + +`-G`, `--grid` +: Display entries as a grid (default). + +`-l`, `--long` +: Display extended file metadata as a table. + +`-R`, `--recurse` +: Recurse into directories. + +`-T`, `--tree` +: Recurse into directories as a tree. + +`--follow-symlinks` +: Drill down into symbolic links that point to directories. + +`-X`, `--dereference` +: Dereference symbolic links when displaying information. + +`-x`, `--across` +: Sort the grid across, rather than downwards. + +`--color=WHEN`, `--colour=WHEN` +: When to use terminal colours (using ANSI escape code to colorize the output). + +Valid settings are ‘`always`’, ‘`automatic`’ (or ‘`auto`’ for short), and ‘`never`’. +The default value is ‘`automatic`’. + +The default behavior (‘`automatic`’ or ‘`auto`’) is to colorize the output only when the standard output is connected to a real terminal. If the output of `eza` is redirected to a file or piped into another program, terminal colors will not be used. Setting this option to ‘`always`’ causes `eza` to always output terminal color, while ‘`never`’ disables the use of terminal color. + +Manually setting this option overrides `NO_COLOR` environment. + +`--color-scale`, `--colour-scale` +: highlight levels of `field` distinctly. +Use comma(,) separated list of all, age, size + +`--color-scale-mode`, `--colour-scale-mode` +: Use gradient or fixed colors in `--color-scale`. + +Valid options are `fixed` or `gradient`. +The default value is `gradient`. + +`--icons=WHEN` +: Display icons next to file names. + +Valid settings are ‘`always`’, ‘`automatic`’ (‘`auto`’ for short), and ‘`never`’. +The default value is ‘`automatic`’. + +`automatic` or `auto` will display icons only when the standard output is connected to a real terminal. If `eza` is ran while in a `tty`, or the output of `eza` is either redirected to a file or piped into another program, icons will not be used. Setting this option to ‘`always`’ causes `eza` to always display icons, while ‘`never`’ disables the use of icons. + +`--no-quotes` +: Don't quote file names with spaces. + +`--hyperlink` +: Display entries as hyperlinks + +`-w`, `--width=COLS` +: Set screen width in columns. + +FILTERING AND SORTING OPTIONS +============================= + +`-a`, `--all` +: Show hidden and “dot” files. +Use this twice to also show the ‘`.`’ and ‘`..`’ directories. + +`-A`, `--almost-all` +: Equivalent to --all; included for compatibility with `ls -A`. + +`-d`, `--list-dirs` +: List directories as regular files, rather than recursing and listing their contents. + +`-L`, `--level=DEPTH` +: Limit the depth of recursion. + +`-r`, `--reverse` +: Reverse the sort order. + +`-s`, `--sort=SORT_FIELD` +: Which field to sort by. + +Valid sort fields are ‘`name`’, ‘`Name`’, ‘`extension`’, ‘`Extension`’, ‘`size`’, ‘`modified`’, ‘`changed`’, ‘`accessed`’, ‘`created`’, ‘`inode`’, ‘`type`’, and ‘`none`’. + +The `modified` sort field has the aliases ‘`date`’, ‘`time`’, and ‘`newest`’, and its reverse order has the aliases ‘`age`’ and ‘`oldest`’. + +Sort fields starting with a capital letter will sort uppercase before lowercase: ‘A’ then ‘B’ then ‘a’ then ‘b’. Fields starting with a lowercase letter will mix them: ‘A’ then ‘a’ then ‘B’ then ‘b’. + +`-I`, `--ignore-glob=GLOBS` +: Glob patterns, pipe-separated, of files to ignore. + +`--git-ignore` [if eza was built with git support] +: Do not list files that are ignored by Git. + +`--group-directories-first` +: List directories before other files. + +`--group-directories-last` +: List directories after other files. + +`-D`, `--only-dirs` +: List only directories, not files. + +`-f`, `--only-files` +: List only files, not directories. + +`--show-symlinks` +: Explicitly show symbolic links (when used with `--only-files` | `--only-dirs`) + +`--no-symlinks` +: Do not show symbolic links + +LONG VIEW OPTIONS +================= + +These options are available when running with `--long` (`-l`): + +`-b`, `--binary` +: List file sizes with binary prefixes. + +`-B`, `--bytes` +: List file sizes in bytes, without any prefixes. + +`--changed` +: Use the changed timestamp field. + +`-g`, `--group` +: List each file’s group. + +`--smart-group` +: Only show group if it has a different name from owner + +`-h`, `--header` +: Add a header row to each column. + +`-H`, `--links` +: List each file’s number of hard links. + +`-i`, `--inode` +: List each file’s inode number. + +`-m`, `--modified` +: Use the modified timestamp field. + +`-M`, `--mounts` +: Show mount details (Linux and Mac only) + +`-n`, `--numeric` +: List numeric user and group IDs. + +`-O`, `--flags` +: List file flags on Mac and BSD systems and file attributes on Windows systems. By default, Windows attributes are displayed in a long form. To display in attributes as single character set the environment variable `EZA_WINDOWS_ATTRIBUTES=short`. On BSD systems see chflags(1) for a list of file flags and their meanings. + +`-S`, `--blocksize` +: List each file’s size of allocated file system blocks. + +`-t`, `--time=WORD` +: Which timestamp field to list. + +: Valid timestamp fields are ‘`modified`’, ‘`changed`’, ‘`accessed`’, and ‘`created`’. + +`--time-style=STYLE` +: How to format timestamps. + +: Valid timestamp styles are ‘`default`’, ‘`iso`’, ‘`long-iso`’, ‘`full-iso`’, ‘`relative`’, or a custom style ‘`+`’ (e.g., ‘`+%Y-%m-%d %H:%M`’ => ‘`2023-09-30 13:00`’). + +`` should be a chrono format string. For details on the chrono format syntax, please read: https://docs.rs/chrono/latest/chrono/format/strftime/index.html . + +Alternatively, `` can be a two line string, the first line will be used for non-recent files and the second for recent files. E.g., if `` is "`%Y-%m-%d %H--%m-%d %H:%M`", non-recent files => "`2022-12-30 13`", recent files => "`--09-30 13:34`". + +`--total-size` +: Show recursive directory size (unix only). + +`-u`, `--accessed` +: Use the accessed timestamp field. + +`-U`, `--created` +: Use the created timestamp field. + +`--no-permissions` +: Suppress the permissions field. + +`-o`, `--octal-permissions` +: List each file's permissions in octal format. + +`--no-filesize` +: Suppress the file size field. + +`--no-user` +: Suppress the user field. + +`--no-time` +: Suppress the time field. + +`--stdin` +: When you wish to pipe directories to eza/read from stdin. Separate one per line or define custom separation char in `EZA_STDIN_SEPARATOR` env variable. + +`-@`, `--extended` +: List each file’s extended attributes and sizes. + +`-Z`, `--context` +: List each file's security context. + +`--git` [if eza was built with git support] +: List each file’s Git status, if tracked. +This adds a two-character column indicating the staged and unstaged statuses respectively. The status character can be ‘`-`’ for not modified, ‘`M`’ for a modified file, ‘`N`’ for a new file, ‘`D`’ for deleted, ‘`R`’ for renamed, ‘`T`’ for type-change, ‘`I`’ for ignored, and ‘`U`’ for conflicted. Directories will be shown to have the status of their contents, which is how ‘deleted’ is possible if a directory contains a file that has a certain status, it will be shown to have that status. + +`--git-repos` [if eza was built with git support] +: List each directory’s Git status, if tracked. +Symbols shown are `|`= clean, `+`= dirty, and `~`= for unknown. + +`--git-repos-no-status` [if eza was built with git support] +: List if a directory is a Git repository, but not its status. +All Git repository directories will be shown as (themed) `-` without status indicated. + + +`--no-git` +: Don't show Git status (always overrides `--git`, `--git-repos`, `--git-repos-no-status`) + + +ENVIRONMENT VARIABLES +===================== + +If an environment variable prefixed with `EZA_` is not set, for backward compatibility, it will default to its counterpart starting with `EXA_`. + +eza responds to the following environment variables: + +## `COLUMNS` + +Overrides the width of the terminal, in characters, however, `-w` takes precedence. + +For example, ‘`COLUMNS=80 eza`’ will show a grid view with a maximum width of 80 characters. + +This option won’t do anything when eza’s output doesn’t wrap, such as when using the `--long` view. + +## `EZA_STRICT` + +Enables _strict mode_, which will make eza error when two command-line options are incompatible. + +Usually, options can override each other going right-to-left on the command line, so that eza can be given aliases: creating an alias ‘`eza=eza --sort=ext`’ then running ‘`eza --sort=size`’ with that alias will run ‘`eza --sort=ext --sort=size`’, and the sorting specified by the user will override the sorting specified by the alias. + +In strict mode, the two options will not co-operate, and eza will error. + +This option is intended for use with automated scripts and other situations where you want to be certain you’re typing in the right command. + +## `EZA_GRID_ROWS` + +Limits the grid-details view (‘`eza --grid --long`’) so it’s only activated when at least the given number of rows of output would be generated. + +With widescreen displays, it’s possible for the grid to look very wide and sparse, on just one or two lines with none of the columns lining up. +By specifying a minimum number of rows, you can only use the view if it’s going to be worth using. + +## `EZA_ICON_SPACING` + +Specifies the number of spaces to print between an icon (see the ‘`--icons`’ option) and its file name. + +Different terminals display icons differently, as they usually take up more than one character width on screen, so there’s no “standard” number of spaces that eza can use to separate an icon from text. One space may place the icon too close to the text, and two spaces may place it too far away. So the choice is left up to the user to configure depending on their terminal emulator. + +## `NO_COLOR` + +Disables colours in the output (regardless of its value). Can be overridden by `--color` option. + +See `https://no-color.org/` for details. + +## `LS_COLORS`, `EZA_COLORS` + +Specifies the colour scheme used to highlight files based on their name and kind, as well as highlighting metadata and parts of the UI. + +For more information on the format of these environment variables, see the [eza_colors.5.md](eza_colors.5.md) manual page. + +## `EZA_OVERRIDE_GIT` + +Overrides any `--git` or `--git-repos` argument + +## `EZA_MIN_LUMINANCE` +Specifies the minimum luminance to use when color-scale is active. It's value can be between -100 to 100. + +## `EZA_ICONS_AUTO` + +If set, automates the same behavior as using `--icons` or `--icons=auto`. Useful for if you always want to have icons enabled. + +Any explicit use of the `--icons=WHEN` flag overrides this behavior. + +## `EZA_STDIN_SEPARATOR` + +Specifies the separator to use when file names are piped from stdin. Defaults to newline. + +## `EZA_CONFIG_DIR` + +Specifies the directory where eza will look for its configuration and theme files. Defaults to `$XDG_CONFIG_HOME/eza` or `$HOME/.config/eza` if `XDG_CONFIG_HOME` is not set. + +EXIT STATUSES +============= + +0 +: If everything goes OK. + +1 +: If there was an I/O error during operation. + +3 +: If there was a problem with the command-line arguments. + +13 +: If permission is denied to access a path. + + +AUTHOR +====== + +eza is maintained by Christina Sørensen and many other contributors. + +**Source code:** `https://github.com/eza-community/eza` \ +**Contributors:** `https://github.com/eza-community/eza/graphs/contributors` + +Our infinite thanks to Benjamin ‘ogham’ Sago and all the other contributors of exa, from which eza was forked. + +SEE ALSO +======== + +- [**eza_colors**(5)](eza_colors.5.md) +- [**eza_colors-explanation**(5)](eza_colors-explanation.5.md) diff --git a/collector/compile-benchmarks/eza-0.21.2/man/eza_colors-explanation.5.md b/collector/compile-benchmarks/eza-0.21.2/man/eza_colors-explanation.5.md new file mode 100644 index 000000000..f67514f95 --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/man/eza_colors-explanation.5.md @@ -0,0 +1,232 @@ +% eza_colors-explanation(5) $version + + + + + +# Name + +eza_colors-explanation — more details on customizing eza colors + +# Eza Color Explanation + +eza provides its own built\-in set of file extension mappings that cover a large range of common file extensions, including documents, archives, media, and temporary files. +Any mappings in the environment variables will override this default set: running eza with `LS_COLORS="*.zip=32"` will turn zip files green but leave the colours of other compressed files alone. + +You can also disable this built\-in set entirely by including a +`reset` entry at the beginning of `EZA_COLORS`. +So setting `EZA_COLORS="reset:*.txt=31"` will highlight only text +files; setting `EZA_COLORS="reset"` will highlight nothing. + +## Examples + +- Disable the "current user" highlighting: `EZA_COLORS="uu=0:gu=0"` +- Turn the date column green: `EZA_COLORS="da=32"` +- Highlight Vagrantfiles: `EZA_COLORS="Vagrantfile=1;4;33"` +- Override the existing zip colour: `EZA_COLORS="*.zip=38;5;125"` +- Markdown files a shade of green, log files a shade of grey: +`EZA_COLORS="*.md=38;5;121:*.log=38;5;248"` + +## BUILT\-IN EXTENSIONS + +- eza now supports bright colours! As supported by most modern 256\-colour terminals, you can now choose from `bright` colour codes when selecting your custom colours in your `#EZA_COLORS` environment variable. + +- Build (Makefile, Cargo.toml, package.json) are yellow and underlined. +- Images (png, jpeg, gif) are purple. +- Videos (mp4, ogv, m2ts) are a slightly purpler purple. +- Music (mp3, m4a, ogg) is a faint blue. +- Lossless music (flac, alac, wav) is a less faint blue. +- Cryptographic files (asc, enc, p12) are bright green. +- Documents (pdf, doc, dvi) are a fainter green. +- Compressed files (zip, tgz, Z) are red. +- Temporary files (tmp, swp, ~) are dimmed default foreground color. +- Compiled files (class, o, pyc) are yellow. A file is also counted as compiled if it uses a common extension and is +in the same directory as one of its source files: styles.css will count as compiled when next to styles.less or styles.sass, and scripts.js when next to scripts.ts or scripts.coffee. +- Source files (cpp, js, java) are bright yellow. + + +## Theme Configuration file + +Now you can specify these options and more in a `theme.yml` file with convenient syntax for defining your styles. + +Set `EZA_CONFIG_DIR` to specify which directory you would like eza to look for your `theme.yml` file, +otherwise eza will look for `$XDG_CONFIG_HOME/eza/theme.yml`. + + +These are the available options: + +LIST OF THEME OPTIONS +===================== + +```yaml +filekinds: + normal + directory + symlink + pipe + block_device + char_device + socket + special + executable + mount_point + +perms: + user_read + user_write + user_executable_file + user_execute_other + group_read + group_write + group_execute + other_read + other_write + other_execute + special_user_file + special_other + attribute + +size: + major + minor + number_byte + number_kilo + number_mega + number_giga + number_huge + unit_byte + unit_kilo + unit_mega + unit_giga + unit_huge + +users: + user_you + user_root + user_other + group_yours + group_other + group_root + +links: + normal + multi_link_file + +git: + new + modified + deleted + renamed + ignored + conflicted + +git_repo: + branch_main + branch_other + git_clean + git_dirty + +security_context: + none: + selinux: + colon + user + role + typ + range + +file_type: + image + video + music + crypto + document + compressed + temp + compiled + build + source + +punctuation: + +date: + +inode: + +blocks: + +header: + +octal: + +flags: + +control_char: + +broken_symlink: + +broken_path_overlay: + +``` + +Each of those fields/sub fields can have the following styling properties defined beneath it + +```yaml + foreground: Blue + background: null + is_bold: false + is_dimmed: false + is_italic: false + is_underline: false + is_blink: false + is_reverse: false + is_hidden: false + is_strikethrough: true + prefix_with_reset: false +``` + +Example: + +```yaml + +file_type: + image: + foreground: Blue + is_italic: true +date: + foreground: White + +security_context: + selinux: + role: + is_hidden: true +``` + +Icons can now be customized as well in the `filenames` and `extensions` fields + +```yaml + +filenames: + # Just change the icon glyph + Cargo.toml: {icon: {glyph: 🦀}} + Cargo.lock: {icon: {glyph: 🦀}} + +extensions: + rs: { filename: {foreground: Red}, icon: {glyph: 🦀}} + +``` + +**NOTES:** + +Not all glyphs support changing colors. + +If your theme is not working properly, double check the syntax in the config file, as +a syntax issue can cause multiple properties to not be applied. + +You must name the file `theme.yml`, no matter the directory you specify. + + +## See also + +- [**eza**(1)](eza.1.md) +- [**eza_colors**(5)](eza_colors.5.md) diff --git a/collector/compile-benchmarks/eza-0.21.2/man/eza_colors.5.md b/collector/compile-benchmarks/eza-0.21.2/man/eza_colors.5.md new file mode 100644 index 000000000..d8e54ec90 --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/man/eza_colors.5.md @@ -0,0 +1,403 @@ +% eza_colors(5) $version + + + + + + +NAME +==== + +eza_colors — customising the file and UI colours of eza + + +SYNOPSIS +======== + +The `EZA_COLORS` environment variable can be used to customise the colours that `eza` uses to highlight file names, file metadata, and parts of the UI. + +You can use the `dircolors` program to generate a script that sets the variable from an input file, or if you don’t mind editing long strings of text, you can just type it out directly. These variables have the following structure: + +- A list of key-value pairs separated by ‘`=`’, such as ‘`*.txt=32`’. +- Multiple ANSI formatting codes are separated by ‘`;`’, such as ‘`*.txt=32;1;4`’. +- Finally, multiple pairs are separated by ‘`:`’, such as ‘`*.txt=32:*.mp3=1;35`’. + +The key half of the pair can either be a two-letter code or a file glob, and anything that’s not a valid code will be treated as a glob, including keys that happen to be two letters long. + +For backwards compatibility `EXA_COLORS` environment variables is checked if `EZA_COLORS` is unset. + + +EXAMPLES +======== + +`EZA_COLORS="uu=0:gu=0"` +: Disable the “current user” highlighting + +`EZA_COLORS="da=32"` +: Turn the date column green + +`EZA_COLORS="Vagrantfile=1;4;33"` +: Highlight Vagrantfiles + +`EZA_COLORS="*.zip=38;5;125"` +: Override the existing zip colour + +`EZA_COLORS="*.md=38;5;121:*.log=38;5;248"` +: Markdown files a shade of green, log files a shade of grey + + +LIST OF CODES +============= + +`LS_COLORS` can use these ten codes: + +`di` +: directories + +`ex` +: executable files + +`fi` +: regular files + +`pi` +: named pipes + +`so` +: sockets + +`bd` +: block devices + +`cd` +: character devices + +`ln` +: symlinks + +`or` +: symlinks with no target + + +`EZA_COLORS` can use many more: + +`oc` +: the permissions displayed as octal + +`ur` +: the user-read permission bit + +`uw` +: the user-write permission bit + +`ux` +: the user-execute permission bit for regular files + +`ue` +: the user-execute for other file kinds + +`gr` +: the group-read permission bit + +`gw` +: the group-write permission bit + +`gx` +: the group-execute permission bit + +`tr` +: the others-read permission bit + +`tw` +: the others-write permission bit + +`tx` +: the others-execute permission bit + +`su` +: setuid, setgid, and sticky permission bits for files + +`sf` +: setuid, setgid, and sticky for other file kinds + +`xa` +: the extended attribute indicator + +`sn` +: the numbers of a file’s size (sets `nb`, `nk`, `nm`, `ng` and `nt`) + +`nb` +: the numbers of a file’s size if it is lower than 1 KB/Kib + +`nk` +: the numbers of a file’s size if it is between 1 KB/KiB and 1 MB/MiB + +`nm` +: the numbers of a file’s size if it is between 1 MB/MiB and 1 GB/GiB + +`ng` +: the numbers of a file’s size if it is between 1 GB/GiB and 1 TB/TiB + +`nt` +: the numbers of a file’s size if it is 1 TB/TiB or higher + +`sb` +: the units of a file’s size (sets `ub`, `uk`, `um`, `ug` and `ut`) + +`ub` +: the units of a file’s size if it is lower than 1 KB/Kib + +`uk` +: the units of a file’s size if it is between 1 KB/KiB and 1 MB/MiB + +`um` +: the units of a file’s size if it is between 1 MB/MiB and 1 GB/GiB + +`ug` +: the units of a file’s size if it is between 1 GB/GiB and 1 TB/TiB + +`ut` +: the units of a file’s size if it is 1 TB/TiB or higher + +`df` +: a device’s major ID + +`ds` +: a device’s minor ID + +`uu` +: a user that’s you + +`uR` +: a user that's root + +`un` +: a user that’s someone else + +`gu` +: a group that you belong to + +`gR` +: a group related to root + +`gn` +: a group you aren’t a member of + +`lc` +: a number of hard links + +`lm` +: a number of hard links for a regular file with at least two + +`ga` +: a new flag in Git + +`gm` +: a modified flag in Git + +`gd` +: a deleted flag in Git + +`gv` +: a renamed flag in Git + +`gt` +: a modified metadata flag in Git + +`gi` +: an ignored flag in Git + +`gc` +: a conflicted flag in Git + +`Gm` +: main branch of repo + +`Go` +: other branch of repo + +`Gc` +: clean branch of repo + +`Gd` +: dirty branch of repo + +`xx` +: “punctuation”, including many background UI elements + +`da` +: a file’s date + +`in` +: a file’s inode number + +`bl` +: a file’s number of blocks + +`hd` +: the header row of a table + +`lp` +: the path of a symlink + +`cc` +: an escaped character in a filename + +`bO` +: the overlay style for broken symlink paths + +`sp` +: special (not file, dir, mount, exec, pipe, socket, block device, char device, or link) + +`mp` +: a mount point + +`im` +: a regular file that is an image + +`vi` +: a regular file that is a video + +`mu` +: a regular file that is lossy music + +`lo` +: a regular file that is lossless music + +`cr` +: a regular file that is related to cryptography (ex: key or certificate) + +`do` +: a regular file that is a document (ex: office suite document or PDF) + +`co` +: a regular file that is compressed + +`tm` +: a regular file that is temporary (ex: a text editor's backup file) + +`cm` +: a regular file that is a compilation artifact (ex: Java class file) + +`bu` +: a regular file that is used to build a project (ex: Makefile) + +`sc` +: a regular file that is source code + +`ic` +: the icon (this is optional, if not set the icon color matches the file name's) + +`Sn` +: No security context on a file + +`Su` +: SELinux user + +`Sr` +: SELinux role + +`St` +: SELinux type + +`Sl` +: SELinux level + +`ff` +: BSD file flags + +Values in `EXA_COLORS` override those given in `LS_COLORS`, so you don’t need to re-write an existing `LS_COLORS` variable with proprietary extensions. + + +LIST OF STYLES +============== + +Unlike some versions of `ls`, the given ANSI values must be valid colour codes: eza won’t just print out whichever characters are given. + +The codes accepted by eza are: + +`1` +: for bold + +`2` +: for dimmed + +`3` +: for italic + +`4` +: for underline + +`31` +: for red text + +`32` +: for green text + +`33` +: for yellow text + +`34` +: for blue text + +`35` +: for purple text + +`36` +: for cyan text + +`37` +: for white text + +`90` +: for dark gray text + +`91` +: for bright red text + +`92` +: for bright green text + +`93` +: for bright yellow text + +`94` +: for bright blue text + +`95` +: for bright purple text + +`96` +: for bright cyan text + +`97` +: for bright text + +`38;5;nnn` +: for a colour from 0 to 255 (replace the `nnn` part) + +Many terminals will treat bolded text as a different colour, or at least provide the option to. + +eza provides its own built-in set of file extension mappings that cover a large range of common file extensions, including documents, archives, media, and temporary files. +Any mappings in the environment variables will override this default set: running eza with `LS_COLORS="*.zip=32"` will turn zip files green but leave the colours of other compressed files alone. + +You can also disable this built-in set entirely by including a `reset` entry at the beginning of `EZA_COLORS`. +So setting `EZA_COLORS="reset:*.txt=31"` will highlight only text files; setting `EZA_COLORS="reset"` will highlight nothing. + + +AUTHOR +====== + +eza is maintained by Christina Sørensen and many other contributors. + +**Source code:** `https://github.com/eza-community/eza` \ +**Contributors:** `https://github.com/eza-community/eza/graphs/contributors` + +Our infinite thanks to Benjamin ‘ogham’ Sago and all the other contributors of exa, from which eza was forked. + + +SEE ALSO +======== + +- [**eza**(1)](eza.1.md) +- [**eza_colors-explanation**(5)](eza_colors-explanation.5.md) diff --git a/collector/compile-benchmarks/eza-0.21.2/nix/eza.nix b/collector/compile-benchmarks/eza-0.21.2/nix/eza.nix new file mode 100644 index 000000000..4fa0d447b --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/nix/eza.nix @@ -0,0 +1,55 @@ +# SPDX-FileCopyrightText: 2024 Christina Sørensen +# SPDX-License-Identifier: EUPL-1.2 +{ + pkgs, + naersk', + buildInputs, + ... +}: + +naersk'.buildPackage rec { + pname = "eza"; + version = "git"; + + src = ../.; + doCheck = true; + + inherit buildInputs; + nativeBuildInputs = with pkgs; [ + cmake + pkg-config + installShellFiles + pandoc + ]; + + buildNoDefaultFeatures = true; + buildFeatures = "git"; + + postInstall = '' + for page in eza.1 eza_colors.5 eza_colors-explanation.5; do + sed "s/\$version/${version}/g" "man/$page.md" | + pandoc --standalone -f markdown -t man >"man/$page" + done + installManPage man/eza.1 man/eza_colors.5 man/eza_colors-explanation.5 + installShellCompletion \ + --bash completions/bash/eza \ + --fish completions/fish/eza.fish \ + --zsh completions/zsh/_eza + ''; + + meta = with pkgs.lib; { + description = "A modern, maintained replacement for ls"; + longDescription = '' + eza is a modern replacement for ls. It uses colours for information by + default, helping you distinguish between many types of files, such as + whether you are the owner, or in the owning group. It also has extra + features not present in the original ls, such as viewing the Git status + for a directory, or recursing into directories with a tree view. eza is + written in Rust, so it’s small, fast, and portable. + ''; + homepage = "https://github.com/eza-community/eza"; + license = licenses.mit; + mainProgram = "eza"; + maintainers = with maintainers; [ cafkafk ]; + }; +} diff --git a/collector/compile-benchmarks/eza-0.21.2/nix/trycmd.nix b/collector/compile-benchmarks/eza-0.21.2/nix/trycmd.nix new file mode 100644 index 000000000..50a3a1287 --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/nix/trycmd.nix @@ -0,0 +1,102 @@ +# SPDX-FileCopyrightText: 2024 Christina Sørensen +# SPDX-License-Identifier: EUPL-1.2 +{ + pkgs, + naersk', + buildInputs, + ... +}: + +{ + trycmd = naersk'.buildPackage { + src = ../.; + mode = "test"; + doCheck = true; + # No reason to wait for release build + release = false; + # buildPhase files differ between dep and main phase + singleStep = true; + # generate testing files + buildPhase = '' + bash devtools/dir-generator.sh tests/test_dir && echo "Dir generated" + bash devtools/generate-timestamp-test-dir.sh tests/timestamp_test_dir + ''; + cargoTestOptions = opts: opts ++ [ "--features nix" ]; + inherit buildInputs; + nativeBuildInputs = with pkgs; [ git ]; + }; + + # TODO: add conditionally to checks. + # Run `nix build .#trycmd` to run integration tests + trycmd-local = naersk'.buildPackage { + src = ../.; + mode = "test"; + doCheck = true; + # No reason to wait for release build + release = false; + # buildPhase files differ between dep and main phase + singleStep = true; + # set itests files creation date to unix epoch + buildPhase = '' + bash devtools/dir-generator.sh tests/test_dir + bash devtools/generate-timestamp-test-dir.sh tests/timestamp_test_dir + touch --date=@0 tests/itest/* + touch --date=@0 tests/ptests/*; + fd -e stdout -e stderr -H -t file -X sed -i 's/[CWD]\//\/build\/source\//g' + ''; + cargoTestOptions = + opts: + opts + ++ [ + "--features nix" + "--features nix-local" + "--features powertest" + ]; + inherit buildInputs; + nativeBuildInputs = with pkgs; [ git ]; + }; + + # Run `nix build .#trydump` to dump testing files + trydump = naersk'.buildPackage { + src = ../.; + mode = "test"; + doCheck = true; + # No reason to wait for release build + release = false; + # buildPhase files differ between dep and main phase + singleStep = true; + # set itests files creation date to unix epoch + buildPhase = '' + bash devtools/dir-generator.sh tests/test_dir + bash devtools/generate-timestamp-test-dir.sh tests/timestamp_test_dir + touch --date=@0 tests/itest/*; + rm tests/cmd/*.stdout || echo; + rm tests/cmd/*.stderr || echo; + + touch --date=@0 tests/ptests/*; + rm tests/ptests/*.stdout || echo; + rm tests/ptests/*.stderr || echo; + ''; + cargoTestOptions = + opts: + opts + ++ [ + "--features nix" + "--features nix-local" + "--features powertest" + #"-F trycmd/debug" + ]; + TRYCMD = "dump"; + postInstall = '' + fd -e stdout -e stderr -H -t file -X sed -i 's/\/build\/source\//[CWD]\//g' + + cp dump $out -r + ''; + inherit buildInputs; + nativeBuildInputs = with pkgs; [ + fd + gnused + git + ]; + }; +} diff --git a/collector/compile-benchmarks/eza-0.21.2/perf-config.json b/collector/compile-benchmarks/eza-0.21.2/perf-config.json new file mode 100644 index 000000000..3166b496a --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/perf-config.json @@ -0,0 +1,4 @@ +{ + "artifact": "binary", + "category": "primary" +} diff --git a/collector/compile-benchmarks/eza-0.21.2/src/fs/dir.rs b/collector/compile-benchmarks/eza-0.21.2/src/fs/dir.rs new file mode 100644 index 000000000..98dafa33e --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/src/fs/dir.rs @@ -0,0 +1,241 @@ +// SPDX-FileCopyrightText: 2024 Christina Sørensen +// SPDX-License-Identifier: EUPL-1.2 +// +// SPDX-FileCopyrightText: 2023-2024 Christina Sørensen, eza contributors +// SPDX-FileCopyrightText: 2014 Benjamin Sago +// SPDX-License-Identifier: MIT +use crate::fs::feature::git::GitCache; +use crate::fs::fields::GitStatus; +use std::fs; +use std::fs::DirEntry; +use std::io; +use std::path::{Path, PathBuf}; +use std::slice::Iter as SliceIter; + +use log::*; + +use crate::fs::File; + +/// A **Dir** provides a cached list of the file paths in a directory that’s +/// being listed. +/// +/// This object gets passed to the Files themselves, in order for them to +/// check the existence of surrounding files, then highlight themselves +/// accordingly. (See `File#get_source_files`) +pub struct Dir { + /// A vector of the files that have been read from this directory. + contents: Vec, + + /// The path that was read. + pub path: PathBuf, +} + +impl Dir { + /// Create a new Dir object filled with all the files in the directory + /// pointed to by the given path. Fails if the directory can’t be read, or + /// isn’t actually a directory, or if there’s an IO error that occurs at + /// any point. + /// + /// The `read_dir` iterator doesn’t actually yield the `.` and `..` + /// entries, so if the user wants to see them, we’ll have to add them + /// ourselves after the files have been read. + pub fn read_dir(path: PathBuf) -> io::Result { + info!("Reading directory {:?}", &path); + + let contents = fs::read_dir(&path)?.collect::, _>>()?; + + info!("Read directory success {:?}", &path); + Ok(Self { contents, path }) + } + + /// Produce an iterator of IO results of trying to read all the files in + /// this directory. + pub fn files<'dir, 'ig>( + &'dir self, + dots: DotFilter, + git: Option<&'ig GitCache>, + git_ignoring: bool, + deref_links: bool, + total_size: bool, + ) -> Files<'dir, 'ig> { + Files { + inner: self.contents.iter(), + dir: self, + dotfiles: dots.shows_dotfiles(), + dots: dots.dots(), + git, + git_ignoring, + deref_links, + total_size, + } + } + + /// Whether this directory contains a file with the given path. + pub fn contains(&self, path: &Path) -> bool { + self.contents.iter().any(|p| p.path().as_path() == path) + } + + /// Append a path onto the path specified by this directory. + pub fn join(&self, child: &Path) -> PathBuf { + self.path.join(child) + } +} + +/// Iterator over reading the contents of a directory as `File` objects. +#[allow(clippy::struct_excessive_bools)] +pub struct Files<'dir, 'ig> { + /// The internal iterator over the paths that have been read already. + inner: SliceIter<'dir, DirEntry>, + + /// The directory that begat those paths. + dir: &'dir Dir, + + /// Whether to include dotfiles in the list. + dotfiles: bool, + + /// Whether the `.` or `..` directories should be produced first, before + /// any files have been listed. + dots: DotsNext, + + git: Option<&'ig GitCache>, + + git_ignoring: bool, + + /// Whether symbolic links should be dereferenced when querying information. + deref_links: bool, + + /// Whether to calculate the directory size recursively + total_size: bool, +} + +impl<'dir, 'ig> Files<'dir, 'ig> { + fn parent(&self) -> PathBuf { + // We can’t use `Path#parent` here because all it does is remove the + // last path component, which is no good for us if the path is + // relative. For example, while the parent of `/testcases/files` is + // `/testcases`, the parent of `.` is an empty path. Adding `..` on + // the end is the only way to get to the *actual* parent directory. + self.dir.path.join("..") + } + + /// Go through the directory until we encounter a file we can list (which + /// varies depending on the dotfile visibility flag) + fn next_visible_file(&mut self) -> Option> { + loop { + if let Some(entry) = self.inner.next() { + let path = entry.path(); + let filename = File::filename(&path); + if !self.dotfiles && filename.starts_with('.') { + continue; + } + + // Also hide _prefix files on Windows because it's used by old applications + // as an alternative to dot-prefix files. + #[cfg(windows)] + if !self.dotfiles && filename.starts_with('_') { + continue; + } + + if self.git_ignoring { + let git_status = self.git.map(|g| g.get(&path, false)).unwrap_or_default(); + if git_status.unstaged == GitStatus::Ignored { + continue; + } + } + + let file = File::from_args( + path, + self.dir, + filename, + self.deref_links, + self.total_size, + entry.file_type().ok(), + ); + + // Windows has its own concept of hidden files, when dotfiles are + // hidden Windows hidden files should also be filtered out + #[cfg(windows)] + if !self.dotfiles && file.attributes().map_or(false, |a| a.hidden) { + continue; + } + + return Some(file); + } + + return None; + } + } +} + +/// The dot directories that need to be listed before actual files, if any. +/// If these aren’t being printed, then `FilesNext` is used to skip them. +enum DotsNext { + /// List the `.` directory next. + Dot, + + /// List the `..` directory next. + DotDot, + + /// Forget about the dot directories and just list files. + Files, +} + +impl<'dir, 'ig> Iterator for Files<'dir, 'ig> { + type Item = File<'dir>; + + fn next(&mut self) -> Option { + match self.dots { + DotsNext::Dot => { + self.dots = DotsNext::DotDot; + Some(File::new_aa_current(self.dir, self.total_size)) + } + + DotsNext::DotDot => { + self.dots = DotsNext::Files; + Some(File::new_aa_parent( + self.parent(), + self.dir, + self.total_size, + )) + } + + DotsNext::Files => self.next_visible_file(), + } + } +} + +/// Usually files in Unix use a leading dot to be hidden or visible, but two +/// entries in particular are “extra-hidden”: `.` and `..`, which only become +/// visible after an extra `-a` option. +#[derive(PartialEq, Eq, Debug, Default, Copy, Clone)] +pub enum DotFilter { + /// Shows files, dotfiles, and `.` and `..`. + DotfilesAndDots, + + /// Show files and dotfiles, but hide `.` and `..`. + Dotfiles, + + /// Just show files, hiding anything beginning with a dot. + #[default] + JustFiles, +} + +impl DotFilter { + /// Whether this filter should show dotfiles in a listing. + fn shows_dotfiles(self) -> bool { + match self { + Self::JustFiles => false, + Self::Dotfiles => true, + Self::DotfilesAndDots => true, + } + } + + /// Whether this filter should add dot directories to a listing. + fn dots(self) -> DotsNext { + match self { + Self::JustFiles => DotsNext::Files, + Self::Dotfiles => DotsNext::Files, + Self::DotfilesAndDots => DotsNext::Dot, + } + } +} diff --git a/collector/compile-benchmarks/eza-0.21.2/src/fs/dir_action.rs b/collector/compile-benchmarks/eza-0.21.2/src/fs/dir_action.rs new file mode 100644 index 000000000..3e27896e1 --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/src/fs/dir_action.rs @@ -0,0 +1,83 @@ +// SPDX-FileCopyrightText: 2024 Christina Sørensen +// SPDX-License-Identifier: EUPL-1.2 +// +// SPDX-FileCopyrightText: 2023-2024 Christina Sørensen, eza contributors +// SPDX-FileCopyrightText: 2014 Benjamin Sago +// SPDX-License-Identifier: MIT +//! What to do when encountering a directory? + +/// The action to take when trying to list a file that turns out to be a +/// directory. +/// +/// By default, exa will display the information about files passed in as +/// command-line arguments, with one file per entry. However, if a directory +/// is passed in, exa assumes that the user wants to see its contents, rather +/// than the directory itself. +/// +/// This can get annoying sometimes: if a user does `exa ~/Downloads/img-*` +/// to see the details of every file starting with `img-`, any directories +/// that happen to start with the same will be listed after the files at +/// the end in a separate block. By listing directories as files, their +/// directory status will be ignored, and both will be listed side-by-side. +/// +/// These two modes have recursive analogues in the “recurse” and “tree” +/// modes. Here, instead of just listing the directories, exa will descend +/// into them and print out their contents. The recurse mode does this by +/// having extra output blocks at the end, while the tree mode will show +/// directories inline, with their contents immediately underneath. +#[derive(PartialEq, Eq, Debug, Copy, Clone)] +pub enum DirAction { + /// This directory should be listed along with the regular files, instead + /// of having its contents queried. + AsFile, + + /// This directory should not be listed, and should instead be opened and + /// *its* files listed separately. This is the default behaviour. + List, + + /// This directory should be listed along with the regular files, and then + /// its contents should be listed afterward. The recursive contents of + /// *those* contents are dictated by the options argument. + Recurse(RecurseOptions), +} + +impl DirAction { + /// Gets the recurse options, if this dir action has any. + pub fn recurse_options(self) -> Option { + match self { + Self::Recurse(o) => Some(o), + _ => None, + } + } + + /// Whether to treat directories as regular files or not. + pub fn treat_dirs_as_files(self) -> bool { + match self { + Self::AsFile => true, + Self::Recurse(o) => o.tree, + Self::List => false, + } + } +} + +/// The options that determine how to recurse into a directory. +#[derive(PartialEq, Eq, Debug, Copy, Clone)] +pub struct RecurseOptions { + /// Whether recursion should be done as a tree or as multiple individual + /// views of files. + pub tree: bool, + + /// The maximum number of times that recursion should descend to, if one + /// is specified. + pub max_depth: Option, +} + +impl RecurseOptions { + /// Returns whether a directory of the given depth would be too deep. + pub fn is_too_deep(self, depth: usize) -> bool { + match self.max_depth { + None => false, + Some(d) => d <= depth, + } + } +} diff --git a/collector/compile-benchmarks/eza-0.21.2/src/fs/feature/git.rs b/collector/compile-benchmarks/eza-0.21.2/src/fs/feature/git.rs new file mode 100644 index 000000000..e4743f22d --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/src/fs/feature/git.rs @@ -0,0 +1,441 @@ +// SPDX-FileCopyrightText: 2024 Christina Sørensen +// SPDX-License-Identifier: EUPL-1.2 +// +// SPDX-FileCopyrightText: 2023-2024 Christina Sørensen, eza contributors +// SPDX-FileCopyrightText: 2014 Benjamin Sago +// SPDX-License-Identifier: MIT +//! Getting the Git status of files and directories. + +use std::env; +use std::ffi::OsStr; +#[cfg(target_family = "unix")] +use std::os::unix::ffi::OsStrExt; +use std::path::{Path, PathBuf}; +use std::sync::Mutex; + +use log::*; + +use crate::fs::fields as f; + +/// A **Git cache** is assembled based on the user’s input arguments. +/// +/// This uses vectors to avoid the overhead of hashing: it’s not worth it when the +/// expected number of Git repositories per exa invocation is 0 or 1... +pub struct GitCache { + /// A list of discovered Git repositories and their paths. + repos: Vec, + + /// Paths that we’ve confirmed do not have Git repositories underneath them. + misses: Vec, +} + +impl GitCache { + pub fn has_anything_for(&self, index: &Path) -> bool { + self.repos.iter().any(|e| e.has_path(index)) + } + + pub fn get(&self, index: &Path, prefix_lookup: bool) -> f::Git { + self.repos + .iter() + .find(|repo| repo.has_path(index)) + .map(|repo| repo.search(index, prefix_lookup)) + .unwrap_or_default() + } +} + +use std::iter::FromIterator; +impl FromIterator for GitCache { + fn from_iter(iter: I) -> Self + where + I: IntoIterator, + { + let iter = iter.into_iter(); + let mut git = Self { + repos: Vec::with_capacity(iter.size_hint().0), + misses: Vec::new(), + }; + + if let Ok(path) = env::var("GIT_DIR") { + // These flags are consistent with how `git` uses GIT_DIR: + let flags = git2::RepositoryOpenFlags::NO_SEARCH | git2::RepositoryOpenFlags::NO_DOTGIT; + match GitRepo::discover(path.into(), flags) { + Ok(repo) => { + debug!("Opened GIT_DIR repo"); + git.repos.push(repo); + } + Err(miss) => { + git.misses.push(miss); + } + } + } + + for path in iter { + if git.misses.contains(&path) { + debug!("Skipping {:?} because it already came back Gitless", path); + } else if git.repos.iter().any(|e| e.has_path(&path)) { + debug!("Skipping {:?} because we already queried it", path); + } else { + let flags = git2::RepositoryOpenFlags::FROM_ENV; + match GitRepo::discover(path, flags) { + Ok(r) => { + if let Some(r2) = git.repos.iter_mut().find(|e| e.has_workdir(&r.workdir)) { + debug!( + "Adding to existing repo (workdir matches with {:?})", + r2.workdir + ); + r2.extra_paths.push(r.original_path); + continue; + } + + debug!("Discovered new Git repo"); + git.repos.push(r); + } + Err(miss) => { + git.misses.push(miss); + } + } + } + } + + git + } +} + +/// A **Git repository** is one we’ve discovered somewhere on the filesystem. +pub struct GitRepo { + /// The queryable contents of the repository: either a `git2` repo, or the + /// cached results from when we queried it last time. + contents: Mutex, + + /// The working directory of this repository. + /// This is used to check whether two repositories are the same. + workdir: PathBuf, + + /// The path that was originally checked to discover this repository. + /// This is as important as the `extra_paths` (it gets checked first), but + /// is separate to avoid having to deal with a non-empty Vec. + original_path: PathBuf, + + /// Any other paths that were checked only to result in this same + /// repository. + extra_paths: Vec, +} + +/// A repository’s queried state. +enum GitContents { + /// All the interesting Git stuff goes through this. + Before { repo: git2::Repository }, + + /// Temporary value used in `repo_to_statuses` so we can move the + /// repository out of the `Before` variant. + Processing, + + /// The data we’ve extracted from the repository, but only after we’ve + /// actually done so. + After { statuses: Git }, +} + +impl GitRepo { + /// Searches through this repository for a path (to a file or directory, + /// depending on the prefix-lookup flag) and returns its Git status. + /// + /// Actually querying the `git2` repository for the mapping of paths to + /// Git statuses is only done once, and gets cached so we don’t need to + /// re-query the entire repository the times after that. + /// + /// The temporary `Processing` enum variant is used after the `git2` + /// repository is moved out, but before the results have been moved in! + /// See + fn search(&self, index: &Path, prefix_lookup: bool) -> f::Git { + use std::mem::replace; + + let mut contents = self.contents.lock().unwrap(); + if let GitContents::After { ref statuses } = *contents { + debug!("Git repo {:?} has been found in cache", &self.workdir); + return statuses.status(index, prefix_lookup); + } + + debug!("Querying Git repo {:?} for the first time", &self.workdir); + let repo = replace(&mut *contents, GitContents::Processing).inner_repo(); + let statuses = repo_to_statuses(&repo, &self.workdir); + let result = statuses.status(index, prefix_lookup); + let _processing = replace(&mut *contents, GitContents::After { statuses }); + result + } + + /// Whether this repository has the given working directory. + fn has_workdir(&self, path: &Path) -> bool { + self.workdir == path + } + + /// Whether this repository cares about the given path at all. + fn has_path(&self, path: &Path) -> bool { + path.starts_with(&self.original_path) + || self.extra_paths.iter().any(|e| path.starts_with(e)) + } + + /// Open a Git repository. Depending on the flags, the path is either + /// the repository's "gitdir" (or a "gitlink" to the gitdir), or the + /// path is the start of a rootwards search for the repository. + fn discover(path: PathBuf, flags: git2::RepositoryOpenFlags) -> Result { + info!("Opening Git repository for {:?} ({:?})", path, flags); + let unused: [&OsStr; 0] = []; + let repo = match git2::Repository::open_ext(&path, flags, unused) { + Ok(r) => r, + Err(e) => { + error!("Error opening Git repository for {path:?}: {e:?}"); + return Err(path); + } + }; + + if let Some(workdir) = repo.workdir() { + let workdir = workdir.to_path_buf(); + let contents = Mutex::new(GitContents::Before { repo }); + Ok(Self { + contents, + workdir, + original_path: path, + extra_paths: Vec::new(), + }) + } else { + warn!("Repository has no workdir?"); + Err(path) + } + } +} + +impl GitContents { + /// Assumes that the repository hasn’t been queried, and extracts it + /// (consuming the value) if it has. This is needed because the entire + /// enum variant gets replaced when a repo is queried (see above). + fn inner_repo(self) -> git2::Repository { + if let Self::Before { repo } = self { + repo + } else { + unreachable!("Tried to extract a non-Repository") + } + } +} + +/// Iterates through a repository’s statuses, consuming it and returning the +/// mapping of files to their Git status. +/// We will have already used the working directory at this point, so it gets +/// passed in rather than deriving it from the `Repository` again. +fn repo_to_statuses(repo: &git2::Repository, workdir: &Path) -> Git { + let mut statuses = Vec::new(); + + info!("Getting Git statuses for repo with workdir {:?}", workdir); + match repo.statuses(None) { + Ok(es) => { + for e in es.iter() { + #[cfg(target_family = "unix")] + let path = workdir.join(Path::new(OsStr::from_bytes(e.path_bytes()))); + // TODO: handle non Unix systems better: + // https://github.com/ogham/exa/issues/698 + #[cfg(not(target_family = "unix"))] + let path = workdir.join(Path::new(e.path().unwrap())); + let elem = (path, e.status()); + statuses.push(elem); + } + // We manually add the `.git` at the root of the repo as ignored, since it is in practice. + // Also we want to avoid `eza --tree --all --git-ignore` to display files inside `.git`. + statuses.push((workdir.join(".git"), git2::Status::IGNORED)); + } + Err(e) => { + error!("Error looking up Git statuses: {:?}", e); + } + } + + Git { statuses } +} + +// The `repo.statuses` call above takes a long time. exa debug output: +// +// 20.311276 INFO:exa::fs::feature::git: Getting Git statuses for repo with workdir "/vagrant/" +// 20.799610 DEBUG:exa::output::table: Getting Git status for file "./Cargo.toml" +// +// Even inserting another logging line immediately afterwards doesn’t make it +// look any faster. + +/// Container of Git statuses for all the files in this folder’s Git repository. +struct Git { + statuses: Vec<(PathBuf, git2::Status)>, +} + +impl Git { + /// Get either the file or directory status for the given path. + /// “Prefix lookup” means that it should report an aggregate status of all + /// paths starting with the given prefix (in other words, a directory). + fn status(&self, index: &Path, prefix_lookup: bool) -> f::Git { + if prefix_lookup { + self.dir_status(index) + } else { + self.file_status(index) + } + } + + /// Get the user-facing status of a file. + /// We check the statuses directly applying to a file, and for the ignored + /// status we check if any of its parents directories is ignored by git. + fn file_status(&self, file: &Path) -> f::Git { + let path = reorient(file); + + let s = self + .statuses + .iter() + .filter(|p| { + if p.1 == git2::Status::IGNORED { + path.starts_with(&p.0) + } else { + p.0 == path + } + }) + .fold(git2::Status::empty(), |a, b| a | b.1); + + let staged = index_status(s); + let unstaged = working_tree_status(s); + f::Git { staged, unstaged } + } + + /// Get the combined, user-facing status of a directory. + /// Statuses are aggregating (for example, a directory is considered + /// modified if any file under it has the status modified), except for + /// ignored status which applies to files under (for example, a directory + /// is considered ignored if one of its parent directories is ignored). + fn dir_status(&self, dir: &Path) -> f::Git { + let path = reorient(dir); + + let s = self + .statuses + .iter() + .filter(|p| { + if p.1 == git2::Status::IGNORED { + path.starts_with(&p.0) + } else { + p.0.starts_with(&path) + } + }) + .fold(git2::Status::empty(), |a, b| a | b.1); + + let staged = index_status(s); + let unstaged = working_tree_status(s); + f::Git { staged, unstaged } + } +} + +/// Converts a path to an absolute path based on the current directory. +/// Paths need to be absolute for them to be compared properly, otherwise +/// you’d ask a repo about “./README.md” but it only knows about +/// “/vagrant/README.md”, prefixed by the workdir. +#[cfg(unix)] +fn reorient(path: &Path) -> PathBuf { + use std::env::current_dir; + + // TODO: I’m not 100% on this func tbh + let path = match current_dir() { + Err(_) => Path::new(".").join(path), + Ok(dir) => dir.join(path), + }; + + path.canonicalize().unwrap_or(path) +} + +#[cfg(windows)] +fn reorient(path: &Path) -> PathBuf { + let unc_path = path.canonicalize().unwrap_or_else(|_| path.to_path_buf()); + // On Windows UNC path is returned. We need to strip the prefix for it to work. + let normal_path = unc_path + .as_os_str() + .to_str() + .unwrap() + .trim_start_matches("\\\\?\\"); + PathBuf::from(normal_path) +} + +/// The character to display if the file has been modified, but not staged. +fn working_tree_status(status: git2::Status) -> f::GitStatus { + #[rustfmt::skip] + return match status { + s if s.contains(git2::Status::WT_NEW) => f::GitStatus::New, + s if s.contains(git2::Status::WT_MODIFIED) => f::GitStatus::Modified, + s if s.contains(git2::Status::WT_DELETED) => f::GitStatus::Deleted, + s if s.contains(git2::Status::WT_RENAMED) => f::GitStatus::Renamed, + s if s.contains(git2::Status::WT_TYPECHANGE) => f::GitStatus::TypeChange, + s if s.contains(git2::Status::IGNORED) => f::GitStatus::Ignored, + s if s.contains(git2::Status::CONFLICTED) => f::GitStatus::Conflicted, + _ => f::GitStatus::NotModified, + }; +} + +/// The character to display if the file has been modified and the change +/// has been staged. +fn index_status(status: git2::Status) -> f::GitStatus { + #[rustfmt::skip] + return match status { + s if s.contains(git2::Status::INDEX_NEW) => f::GitStatus::New, + s if s.contains(git2::Status::INDEX_MODIFIED) => f::GitStatus::Modified, + s if s.contains(git2::Status::INDEX_DELETED) => f::GitStatus::Deleted, + s if s.contains(git2::Status::INDEX_RENAMED) => f::GitStatus::Renamed, + s if s.contains(git2::Status::INDEX_TYPECHANGE) => f::GitStatus::TypeChange, + _ => f::GitStatus::NotModified, + }; +} + +fn current_branch(repo: &git2::Repository) -> Option { + let head = match repo.head() { + Ok(head) => Some(head), + Err(ref e) + if e.code() == git2::ErrorCode::UnbornBranch + || e.code() == git2::ErrorCode::NotFound => + { + return None + } + Err(e) => { + error!("Error looking up Git branch: {:?}", e); + return None; + } + }; + + head.and_then(|h| h.shorthand().map(std::string::ToString::to_string)) +} + +impl f::SubdirGitRepo { + pub fn from_path(dir: &Path, status: bool) -> Self { + let path = &reorient(dir); + + if let Ok(repo) = git2::Repository::open(path) { + let branch = current_branch(&repo); + if !status { + return Self { + status: None, + branch, + }; + } + match repo.statuses(None) { + Ok(es) => { + if es.iter().any(|s| s.status() != git2::Status::IGNORED) { + return Self { + status: Some(f::SubdirGitRepoStatus::GitDirty), + branch, + }; + } + return Self { + status: Some(f::SubdirGitRepoStatus::GitClean), + branch, + }; + } + Err(e) => { + error!("Error looking up Git statuses: {e:?}"); + } + } + } + f::SubdirGitRepo { + status: if status { + Some(f::SubdirGitRepoStatus::NoRepo) + } else { + None + }, + branch: None, + } + } +} diff --git a/collector/compile-benchmarks/eza-0.21.2/src/fs/feature/mod.rs b/collector/compile-benchmarks/eza-0.21.2/src/fs/feature/mod.rs new file mode 100644 index 000000000..7f63cdba1 --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/src/fs/feature/mod.rs @@ -0,0 +1,45 @@ +// SPDX-FileCopyrightText: 2024 Christina Sørensen +// SPDX-License-Identifier: EUPL-1.2 +// +// SPDX-FileCopyrightText: 2023-2024 Christina Sørensen, eza contributors +// SPDX-FileCopyrightText: 2014 Benjamin Sago +// SPDX-License-Identifier: MIT +pub mod xattr; + +#[cfg(feature = "git")] +pub mod git; + +#[cfg(not(feature = "git"))] +pub mod git { + use std::iter::FromIterator; + use std::path::{Path, PathBuf}; + + use crate::fs::fields as f; + + pub struct GitCache; + + impl FromIterator for GitCache { + fn from_iter(_iter: I) -> Self + where + I: IntoIterator, + { + Self + } + } + + impl GitCache { + pub fn has_anything_for(&self, _index: &Path) -> bool { + false + } + + pub fn get(&self, _index: &Path, _prefix_lookup: bool) -> f::Git { + unreachable!(); + } + } + + impl f::SubdirGitRepo { + pub fn from_path(_dir: &Path, _status: bool) -> Self { + panic!("Tried to get subdir Git status, but Git support is disabled") + } + } +} diff --git a/collector/compile-benchmarks/eza-0.21.2/src/fs/feature/xattr.rs b/collector/compile-benchmarks/eza-0.21.2/src/fs/feature/xattr.rs new file mode 100644 index 000000000..f05bfaf72 --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/src/fs/feature/xattr.rs @@ -0,0 +1,714 @@ +// SPDX-FileCopyrightText: 2024 Christina Sørensen +// SPDX-License-Identifier: EUPL-1.2 +// +// SPDX-FileCopyrightText: 2023-2024 Christina Sørensen, eza contributors +// SPDX-FileCopyrightText: 2014 Benjamin Sago +// SPDX-License-Identifier: MIT +//! Extended attribute support for `NetBSD`, `Darwin`, and `Linux` systems. + +#![allow(trivial_casts)] // for ARM + +use std::fmt::{Display, Formatter}; +use std::io; +use std::path::Path; +use std::str; + +pub const ENABLED: bool = cfg!(any( + target_os = "macos", + target_os = "linux", + target_os = "netbsd", + target_os = "freebsd" +)); + +#[derive(Debug)] +pub struct Attribute { + pub name: String, + pub value: Option>, +} + +pub trait FileAttributes { + fn attributes(&self) -> io::Result>; + fn symlink_attributes(&self) -> io::Result>; +} + +#[cfg(any( + target_os = "macos", + target_os = "linux", + target_os = "netbsd", + target_os = "freebsd" +))] +impl FileAttributes for Path { + fn attributes(&self) -> io::Result> { + extended_attrs::attributes(self, true) + } + + fn symlink_attributes(&self) -> io::Result> { + extended_attrs::attributes(self, false) + } +} + +#[cfg(not(any( + target_os = "macos", + target_os = "linux", + target_os = "netbsd", + target_os = "freebsd" +)))] +impl FileAttributes for Path { + fn attributes(&self) -> io::Result> { + Ok(Vec::new()) + } + + fn symlink_attributes(&self) -> io::Result> { + Ok(Vec::new()) + } +} + +#[cfg(any( + target_os = "macos", + target_os = "linux", + target_os = "netbsd", + target_os = "freebsd" +))] +mod extended_attrs { + use super::Attribute; + use libc::{c_char, c_void, size_t, ssize_t, ERANGE}; + use std::ffi::{CStr, CString, OsStr, OsString}; + use std::io; + use std::os::unix::ffi::OsStrExt; + use std::path::Path; + use std::ptr::null_mut; + + #[cfg(target_os = "macos")] + mod os { + use libc::{ + c_char, c_int, c_void, getxattr, listxattr, size_t, ssize_t, XATTR_NOFOLLOW, + XATTR_SHOWCOMPRESSION, + }; + + // Options to use for MacOS versions of getxattr and listxattr + fn get_options(follow_symlinks: bool) -> c_int { + if follow_symlinks { + XATTR_SHOWCOMPRESSION + } else { + XATTR_NOFOLLOW | XATTR_SHOWCOMPRESSION + } + } + + // Wrapper around listxattr that handles symbolic links + pub(super) fn list_xattr( + follow_symlinks: bool, + path: *const c_char, + namebuf: *mut c_char, + size: size_t, + ) -> ssize_t { + // SAFETY: Calling C function + unsafe { listxattr(path, namebuf, size, get_options(follow_symlinks)) } + } + + // Wrapper around getxattr that handles symbolic links + pub(super) fn get_xattr( + follow_symlinks: bool, + path: *const c_char, + name: *const c_char, + value: *mut c_void, + size: size_t, + ) -> ssize_t { + // SAFETY: Calling C function + unsafe { getxattr(path, name, value, size, 0, get_options(follow_symlinks)) } + } + } + + #[cfg(target_os = "linux")] + mod os { + use libc::{c_char, c_void, size_t, ssize_t}; + + use libc::{getxattr, lgetxattr, listxattr, llistxattr}; + + // Wrapper around listxattr and llistattr for handling symbolic links + pub(super) fn list_xattr( + follow_symlinks: bool, + path: *const c_char, + namebuf: *mut c_char, + size: size_t, + ) -> ssize_t { + if follow_symlinks { + // SAFETY: Calling C function + unsafe { listxattr(path, namebuf, size) } + } else { + // SAFETY: Calling C function + unsafe { llistxattr(path, namebuf, size) } + } + } + + // Wrapper around getxattr and lgetxattr for handling symbolic links + pub(super) fn get_xattr( + follow_symlinks: bool, + path: *const c_char, + name: *const c_char, + value: *mut c_void, + size: size_t, + ) -> ssize_t { + if follow_symlinks { + // SAFETY: Calling C function + unsafe { getxattr(path, name, value, size) } + } else { + // SAFETY: Calling C function + unsafe { lgetxattr(path, name, value, size) } + } + } + } + + #[cfg(any(target_os = "netbsd", target_os = "freebsd"))] + mod os { + use libc::{ + c_char, c_int, c_void, extattr_get_file, extattr_get_link, extattr_list_file, + extattr_list_link, size_t, ssize_t, EXTATTR_NAMESPACE_SYSTEM, EXTATTR_NAMESPACE_USER, + }; + + // Wrapper around listxattr that handles symbolic links + fn list_xattr( + follow_symlinks: bool, + path: *const c_char, + namespace: c_int, + value: *mut c_void, + size: size_t, + ) -> ssize_t { + if follow_symlinks { + // SAFETY: Calling C function + unsafe { extattr_list_file(path, namespace, value, size) } + } else { + // SAFETY: Calling C function + unsafe { extattr_list_link(path, namespace, value, size) } + } + } + + fn get_xattr( + follow_symlinks: bool, + path: *const c_char, + namespace: c_int, + name: *const c_char, + value: *mut c_void, + size: size_t, + ) -> ssize_t { + if follow_symlinks { + // SAFETY: Calling C function + unsafe { extattr_get_file(path, namespace, name, value, size) } + } else { + // SAFETY: Calling C function + unsafe { extattr_get_link(path, namespace, name, value, size) } + } + } + + pub(super) fn list_system_xattr( + follow_symlinks: bool, + path: *const c_char, + namebuf: *mut c_char, + size: size_t, + ) -> ssize_t { + list_xattr( + follow_symlinks, + path, + EXTATTR_NAMESPACE_SYSTEM, + namebuf.cast(), + size, + ) + } + + pub(super) fn list_user_xattr( + follow_symlinks: bool, + path: *const c_char, + namebuf: *mut c_char, + size: size_t, + ) -> ssize_t { + list_xattr( + follow_symlinks, + path, + EXTATTR_NAMESPACE_USER, + namebuf.cast(), + size, + ) + } + + pub(super) fn get_system_xattr( + follow_symlinks: bool, + path: *const c_char, + name: *const c_char, + value: *mut c_void, + size: size_t, + ) -> ssize_t { + get_xattr( + follow_symlinks, + path, + EXTATTR_NAMESPACE_SYSTEM, + name, + value.cast(), + size, + ) + } + + pub(super) fn get_user_xattr( + follow_symlinks: bool, + path: *const c_char, + name: *const c_char, + value: *mut c_void, + size: size_t, + ) -> ssize_t { + get_xattr( + follow_symlinks, + path, + EXTATTR_NAMESPACE_USER, + name, + value.cast(), + size, + ) + } + } + + // Split attribute name list. Each attribute name is null terminated in the + // list. + #[cfg(any(target_os = "macos", target_os = "linux"))] + fn split_attribute_list(buffer: &[u8]) -> Vec { + buffer[..buffer.len() - 1] // Skip trailing null + .split(|&c| c == 0) + .filter(|&s| !s.is_empty()) + .map(OsStr::from_bytes) + .map(std::borrow::ToOwned::to_owned) + .collect() + } + + // Split attribute name list. Each attribute is a one byte name length + // followed by the name. + #[cfg(any(target_os = "netbsd", target_os = "freebsd"))] + fn split_attribute_list(buffer: &[u8]) -> Vec { + let mut result = Vec::new(); + let mut index = 0; + let length = buffer.len(); + + while index < length { + let item_length = buffer[index] as usize; + let start = index + 1; + let end = start + item_length; + if end <= length { + result.push(OsStr::from_bytes(&buffer[start..end]).to_owned()); + } + index = end; + } + + result + } + + // Calling getxattr and listxattr is a two part process. The first call + // a null ptr for buffer and a zero buffer size is passed and the function + // returns the needed buffer size. The second call the buffer ptr and the + // buffer size is passed and the buffer is filled. Care must be taken if + // the buffer size changes between the first and second call. + fn get_loop ssize_t>(f: F) -> io::Result>> { + let mut buffer: Vec = Vec::new(); + loop { + let buffer_size = match f(null_mut(), 0) { + -1 => return Err(io::Error::last_os_error()), + 0 => return Ok(None), + size => size as size_t, + }; + + buffer.resize(buffer_size, 0); + + return match f(buffer.as_mut_ptr(), buffer_size) { + -1 => { + let last_os_error = io::Error::last_os_error(); + if last_os_error.raw_os_error() == Some(ERANGE) { + // Passed buffer was to small so retry again. + continue; + } + Err(last_os_error) + } + 0 => Ok(None), + len => { + // Just in case the size shrunk + buffer.truncate(len as usize); + Ok(Some(buffer)) + } + }; + } + } + + // Get a list of all attribute names on `path` + fn list_attributes( + path: &CStr, + follow_symlinks: bool, + lister: fn( + follow_symlinks: bool, + path: *const c_char, + namebuf: *mut c_char, + size: size_t, + ) -> ssize_t, + ) -> io::Result> { + Ok( + get_loop(|buf, size| lister(follow_symlinks, path.as_ptr(), buf.cast(), size))? + .map_or_else(Vec::new, |buffer| split_attribute_list(&buffer)), + ) + } + + // Get the attribute value `name` on `path` + #[cfg(any(target_os = "macos", target_os = "linux"))] + fn get_attribute( + path: &CStr, + name: &CStr, + follow_symlinks: bool, + getter: fn( + follow_symlinks: bool, + path: *const c_char, + name: *const c_char, + value: *mut c_void, + size: size_t, + ) -> ssize_t, + ) -> io::Result>> { + use libc::ENODATA; + + get_loop(|buf, size| { + getter( + follow_symlinks, + path.as_ptr(), + name.as_ptr(), + buf.cast(), + size, + ) + }) + .or_else(|err| { + if err.raw_os_error() == Some(ENODATA) { + // This handles the case when the named attribute is not on the + // path. This is for mainly handling the special case for the + // security.selinux attribute mentioned below. This can + // also happen when an attribute is deleted between listing + // the attributes and getting its value. + Ok(None) + } else { + Err(err) + } + }) + } + + #[cfg(any(target_os = "netbsd", target_os = "freebsd"))] + fn get_attribute( + path: &CStr, + name: &CStr, + follow_symlinks: bool, + getter: fn( + follow_symlinks: bool, + path: *const c_char, + name: *const c_char, + value: *mut c_void, + size: size_t, + ) -> ssize_t, + ) -> io::Result>> { + get_loop(|buf, size| { + getter( + follow_symlinks, + path.as_ptr(), + name.as_ptr(), + buf.cast(), + size, + ) + }) + } + + // Specially handle security.linux for filesystem that do not list attributes. + #[cfg(target_os = "linux")] + fn get_selinux_attribute(path: &CStr, follow_symlinks: bool) -> io::Result> { + const SELINUX_XATTR_NAME: &str = "security.selinux"; + let name = CString::new(SELINUX_XATTR_NAME).unwrap(); + + get_attribute(path, &name, follow_symlinks, os::get_xattr).map(|value| { + if value.is_some() { + vec![Attribute { + name: String::from(SELINUX_XATTR_NAME), + value, + }] + } else { + Vec::new() + } + }) + } + + // Get a vector of all attribute names and values on `path` + #[cfg(any(target_os = "macos", target_os = "linux"))] + pub fn attributes(path: &Path, follow_symlinks: bool) -> io::Result> { + let path = CString::new(path.as_os_str().as_bytes()) + .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + let attr_names = list_attributes(&path, follow_symlinks, os::list_xattr)?; + + #[cfg(target_os = "linux")] + if attr_names.is_empty() { + // Some filesystems, like sysfs, return nothing on listxattr, even though the security + // attribute is set. + return get_selinux_attribute(&path, follow_symlinks); + } + + let mut attrs = Vec::with_capacity(attr_names.len()); + for attr_name in attr_names { + if let Some(name) = attr_name.to_str() { + let attr_name = + CString::new(name).map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + let value = get_attribute(&path, &attr_name, follow_symlinks, os::get_xattr)?; + attrs.push(Attribute { + name: name.to_string(), + value, + }); + } + } + + Ok(attrs) + } + + #[cfg(any(target_os = "netbsd", target_os = "freebsd"))] + fn get_namespace_attributes( + path: &CStr, + follow_symlinks: bool, + attr_names: Vec, + namespace: &str, + getter: fn( + follow_symlinks: bool, + path: *const c_char, + name: *const c_char, + value: *mut c_void, + size: size_t, + ) -> ssize_t, + attrs: &mut Vec, + ) -> io::Result<()> { + for attr_name in attr_names { + if let Some(name) = attr_name.to_str() { + let attr_name = + CString::new(name).map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + let value = get_attribute(path, &attr_name, follow_symlinks, getter)?; + attrs.push(Attribute { + name: format!("{namespace}::{name}"), + value, + }); + } + } + Ok(()) + } + + #[cfg(any(target_os = "netbsd", target_os = "freebsd"))] + pub fn attributes(path: &Path, follow_symlinks: bool) -> io::Result> { + use libc::EPERM; + + let path = CString::new(path.as_os_str().as_bytes()) + .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + let attr_names_system = list_attributes(&path, follow_symlinks, os::list_system_xattr) + .or_else(|err| { + // Reading of attributes in the system namespace is only supported for root + if err.raw_os_error() == Some(EPERM) { + Ok(Vec::new()) + } else { + Err(err) + } + })?; + let attr_names_user = list_attributes(&path, follow_symlinks, os::list_user_xattr)?; + + let mut attrs = Vec::with_capacity(attr_names_system.len() + attr_names_user.len()); + + get_namespace_attributes( + &path, + follow_symlinks, + attr_names_system, + "system", + os::get_system_xattr, + &mut attrs, + )?; + get_namespace_attributes( + &path, + follow_symlinks, + attr_names_user, + "user", + os::get_user_xattr, + &mut attrs, + )?; + + Ok(attrs) + } +} + +const ATTRIBUTE_VALUE_MAX_HEX_LENGTH: usize = 16; + +// Display for an attribute. Attribute values that have a custom display are +// enclosed in curley brackets. +impl Display for Attribute { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_fmt(format_args!("{}: ", self.name))?; + if let Some(value) = custom_attr_display(self) { + f.write_fmt(format_args!("<{value}>")) + } else { + match &self.value { + None => f.write_str(""), + Some(value) => { + if let Some(val) = custom_value_display(value) { + f.write_fmt(format_args!("<{val}>")) + } else if let Ok(v) = str::from_utf8(value) { + f.write_fmt(format_args!("{:?}", v.trim_end_matches(char::from(0)))) + } else if value.len() <= ATTRIBUTE_VALUE_MAX_HEX_LENGTH { + f.write_fmt(format_args!("{value:02x?}")) + } else { + f.write_fmt(format_args!("", value.len())) + } + } + } + } + } +} + +struct AttributeDisplay { + pub attribute: &'static str, + pub display: fn(&Attribute) -> Option, +} + +// Check for a custom display by attribute name and call the display function +fn custom_attr_display(attribute: &Attribute) -> Option { + let name = attribute.name.as_str(); + // Strip off MacOS Metadata Persistence Flags + // See https://eclecticlight.co/2020/11/02/controlling-metadata-tricks-with-persistence/ + #[cfg(target_os = "macos")] + let name = name.rsplit_once('#').map_or(name, |n| n.0); + + ATTRIBUTE_DISPLAYS + .iter() + .find(|c| c.attribute == name) + .and_then(|c| (c.display)(attribute)) +} + +#[cfg(target_os = "macos")] +const ATTRIBUTE_DISPLAYS: &[AttributeDisplay] = &[ + AttributeDisplay { + attribute: "com.apple.lastuseddate", + display: display_lastuseddate, + }, + AttributeDisplay { + attribute: "com.apple.macl", + display: display_macl, + }, +]; + +#[cfg(not(target_os = "macos"))] +const ATTRIBUTE_DISPLAYS: &[AttributeDisplay] = &[]; + +// com.apple.lastuseddate is two 64-bit values representing the seconds and nano seconds +// from January 1, 1970 +#[cfg(target_os = "macos")] +fn display_lastuseddate(attribute: &Attribute) -> Option { + use chrono::{Local, SecondsFormat, TimeZone}; + + attribute + .value + .as_ref() + .filter(|value| value.len() == 16) + .and_then(|value| { + let sec = i64::from_le_bytes(value[0..8].try_into().unwrap()); + let n_sec = i64::from_le_bytes(value[8..].try_into().unwrap()); + Local + .timestamp_opt(sec, n_sec as u32) + .map(|dt| dt.to_rfc3339_opts(SecondsFormat::Nanos, true)) + .single() + }) +} + +// com.apple.macl is a two byte flag followed by a uuid for the application +#[cfg(target_os = "macos")] +fn format_macl(value: &[u8]) -> String { + const HEX: [u8; 16] = [ + b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'a', b'b', b'c', b'd', b'e', + b'f', + ]; + const GROUPS: [(usize, usize, u8); 6] = [ + (0, 4, b';'), + (5, 13, b'-'), + (14, 18, b'-'), + (19, 23, b'-'), + (24, 28, b'-'), + (29, 41, 0), + ]; + + let mut dst = [0; 41]; + let mut i = 0; + + for (start, end, sep) in GROUPS { + for j in (start..end).step_by(2) { + let x = value[i]; + i += 1; + dst[j] = HEX[(x >> 4) as usize]; + dst[j + 1] = HEX[(x & 0x0f) as usize]; + } + if sep != 0 { + dst[end] = sep; + } + } + + // SAFETY: Vector generated above with only ASCII characters. + unsafe { String::from_utf8_unchecked(dst.to_vec()) } +} + +// See https://book.hacktricks.xyz/macos-hardening/macos-security-and-privilege-escalation/macos-security-protections/macos-tcc +#[cfg(target_os = "macos")] +fn display_macl(attribute: &Attribute) -> Option { + attribute + .value + .as_ref() + .filter(|v| v.len() % 18 == 0) + .map(|v| { + let macls = v + .as_slice() + .chunks(18) + .filter(|c| c[0] != 0 || c[1] != 0) + .map(format_macl) + .collect::>() + .join(", "); + format!("[{macls}]") + }) +} + +// plist::XmlWriter takes the writer instead of borrowing it. This is a +// wrapper around a borrowed vector that just forwards the Write trait +// calls to the borrowed vector. +struct BorrowedWriter<'a> { + pub buffer: &'a mut Vec, +} + +impl<'a> io::Write for BorrowedWriter<'a> { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.buffer.write(buf) + } + + fn flush(&mut self) -> io::Result<()> { + self.buffer.flush() + } + + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + self.buffer.write_all(buf) + } +} + +fn custom_value_display(value: &[u8]) -> Option { + if value.starts_with(b"bplist") { + plist_value_display(value) + } else { + None + } +} + +// Convert a binary plist to a XML plist. +fn plist_value_display(value: &[u8]) -> Option { + let reader = io::Cursor::new(value); + plist::Value::from_reader(reader).ok().and_then(|v| { + let mut buffer = Vec::new(); + v.to_writer_xml_with_options( + BorrowedWriter { + buffer: &mut buffer, + }, + &plist::XmlWriteOptions::default() + .indent(b' ', 0) + .root_element(false), + ) + .ok() + .and_then(|()| str::from_utf8(&buffer).ok()) + .map(|s| format!("{}", s.replace('\n', ""))) + }) +} diff --git a/collector/compile-benchmarks/eza-0.21.2/src/fs/fields.rs b/collector/compile-benchmarks/eza-0.21.2/src/fs/fields.rs new file mode 100644 index 000000000..4fbe4f668 --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/src/fs/fields.rs @@ -0,0 +1,299 @@ +// SPDX-FileCopyrightText: 2024 Christina Sørensen +// SPDX-License-Identifier: EUPL-1.2 +// +// SPDX-FileCopyrightText: 2023-2024 Christina Sørensen, eza contributors +// SPDX-FileCopyrightText: 2014 Benjamin Sago +// SPDX-License-Identifier: MIT +//! Wrapper types for the values returned from `File`s. +//! +//! The methods of `File` that return information about the entry on the +//! filesystem -- size, modification date, block count, or Git status -- used +//! to just return these as formatted strings, but this became inflexible once +//! customisable output styles landed. +//! +//! Instead, they will return a wrapper type from this module, which tags the +//! type with what field it is while containing the actual raw value. +//! +//! The `output::details` module, among others, uses these types to render and +//! display the information as formatted strings. + +#![allow(non_camel_case_types)] +#![allow(clippy::struct_excessive_bools)] + +/// The type of a file’s group ID. +pub type gid_t = u32; + +/// The type of a file’s inode. +#[allow(unused)] +pub type ino_t = u64; + +/// The type of a file’s number of links. +#[allow(unused)] +pub type nlink_t = u64; + +/// The type of a file’s timestamp (creation, modification, access, etc). +pub type time_t = i64; + +/// The type of a file’s user ID. +#[allow(unused)] +pub type uid_t = u32; + +/// The type of user file flags +pub type flag_t = u32; + +/// The file’s base type, which gets displayed in the very first column of the +/// details output. +/// +/// This type is set entirely by the filesystem, rather than relying on a +/// file’s contents. So “link” is a type, but “image” is just a type of +/// regular file. (See the `filetype` module for those checks.) +/// +/// Its ordering is used when sorting by type. +#[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone)] +pub enum Type { + Directory, + File, + Link, + Pipe, + Socket, + CharDevice, + BlockDevice, + Special, +} + +impl Type { + pub fn is_regular_file(self) -> bool { + matches!(self, Self::File) + } +} + +/// The file’s Unix permission bitfield, with one entry per bit. +#[derive(Copy, Clone)] +#[rustfmt::skip] +pub struct Permissions { + pub user_read: bool, + pub user_write: bool, + pub user_execute: bool, + + pub group_read: bool, + pub group_write: bool, + pub group_execute: bool, + + pub other_read: bool, + pub other_write: bool, + pub other_execute: bool, + + pub sticky: bool, + pub setgid: bool, + pub setuid: bool, +} + +/// The file's `FileAttributes` field, available only on Windows. +#[derive(Copy, Clone)] +#[rustfmt::skip] +#[cfg(windows)] +pub struct Attributes { + pub archive: bool, + pub directory: bool, + pub readonly: bool, + pub hidden: bool, + pub system: bool, + pub reparse_point: bool, +} + +/// The three pieces of information that are displayed as a single column in +/// the details view. These values are fused together to make the output a +/// little more compressed. +#[derive(Copy, Clone)] +pub struct PermissionsPlus { + #[allow(unused)] + pub file_type: Type, + #[cfg(unix)] + pub permissions: Permissions, + #[cfg(windows)] + pub attributes: Attributes, + #[allow(unused)] + pub xattrs: bool, +} + +/// The permissions encoded as octal values +#[derive(Copy, Clone)] +pub struct OctalPermissions { + pub permissions: Permissions, +} + +/// A file’s number of hard links on the filesystem. +/// +/// Under Unix, a file can exist on the filesystem only once but appear in +/// multiple directories. However, it’s rare (but occasionally useful!) for a +/// regular file to have a link count greater than 1, so we highlight the +/// block count specifically for this case. +#[allow(unused)] +#[derive(Copy, Clone)] +pub struct Links { + /// The actual link count. + pub count: nlink_t, + + /// Whether this file is a regular file with more than one hard link. + pub multiple: bool, +} + +/// A file’s inode. Every directory entry on a Unix filesystem has an inode, +/// including directories and links, so this is applicable to everything exa +/// can deal with. +#[allow(unused)] +#[derive(Copy, Clone)] +pub struct Inode(pub ino_t); + +/// A file's size of allocated file system blocks. +#[derive(Copy, Clone)] +#[cfg(unix)] +pub enum Blocksize { + /// This file has the given number of blocks. + Some(u64), + + /// This file isn’t of a type that can take up blocks. + None, +} + +/// The ID of the user that owns a file. This will only ever be a number; +/// looking up the username is done in the `display` module. +#[allow(unused)] +#[derive(Copy, Clone)] +pub struct User(pub uid_t); + +/// The ID of the group that a file belongs to. +#[allow(unused)] +#[derive(Copy, Clone)] +pub struct Group(pub gid_t); + +/// A file’s size, in bytes. This is usually formatted by the `number_prefix` +/// crate into something human-readable. +#[derive(Copy, Clone)] +pub enum Size { + /// This file has a defined size. + Some(u64), + + /// This file has no size, or has a size but we aren’t interested in it. + /// + /// Under Unix, directory entries that aren’t regular files will still + /// have a file size. For example, a directory will just contain a list of + /// its files as its “contents” and will be specially flagged as being a + /// directory, rather than a file. However, seeing the “file size” of this + /// data is rarely useful — I can’t think of a time when I’ve seen it and + /// learnt something. So we discard it and just output “-” instead. + /// + /// See this answer for more: + None, + + /// This file is a block or character device, so instead of a size, print + /// out the file’s major and minor device IDs. + /// + /// This is what ls does as well. Without it, the devices will just have + /// file sizes of zero. + DeviceIDs(DeviceIDs), +} + +/// The major and minor device IDs that gets displayed for device files. +/// +/// You can see what these device numbers mean: +/// - +/// - +#[derive(Copy, Clone)] +pub struct DeviceIDs { + pub major: u32, + pub minor: u32, +} + +/// One of a file’s timestamps (created, accessed, or modified). +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct Time { + pub seconds: time_t, + pub nanoseconds: time_t, +} + +/// A file’s status in a Git repository. Whether a file is in a repository or +/// not is handled by the Git module, rather than having a “null” variant in +/// this enum. +#[derive(PartialEq, Eq, Copy, Clone)] +pub enum GitStatus { + /// This file hasn’t changed since the last commit. + NotModified, + + /// This file didn’t exist for the last commit, and is not specified in + /// the ignored files list. + New, + + /// A file that’s been modified since the last commit. + Modified, + + /// A deleted file. This can’t ever be shown, but it’s here anyway! + Deleted, + + /// A file that Git has tracked a rename for. + Renamed, + + /// A file that’s had its type (such as the file permissions) changed. + TypeChange, + + /// A file that’s ignored (that matches a line in .gitignore) + Ignored, + + /// A file that’s updated but unmerged. + Conflicted, +} + +/// A file’s complete Git status. It’s possible to make changes to a file, add +/// it to the staging area, then make *more* changes, so we need to list each +/// file’s status for both of these. +#[derive(Copy, Clone)] +pub struct Git { + pub staged: GitStatus, + pub unstaged: GitStatus, +} + +impl Default for Git { + /// Create a Git status for a file with nothing done to it. + fn default() -> Self { + Self { + staged: GitStatus::NotModified, + unstaged: GitStatus::NotModified, + } + } +} + +pub enum SecurityContextType<'a> { + SELinux(&'a str), + None, +} + +pub struct SecurityContext<'a> { + pub context: SecurityContextType<'a>, +} + +#[allow(dead_code)] +#[derive(PartialEq, Copy, Clone)] +pub enum SubdirGitRepoStatus { + NoRepo, + GitClean, + GitDirty, +} + +#[derive(Clone)] +pub struct SubdirGitRepo { + pub status: Option, + pub branch: Option, +} + +impl Default for SubdirGitRepo { + fn default() -> Self { + Self { + status: Some(SubdirGitRepoStatus::NoRepo), + branch: None, + } + } +} + +/// The user file flags on the file. This will only ever be a number; +/// looking up the flags is done in the `display` module. +pub struct Flags(pub flag_t); diff --git a/collector/compile-benchmarks/eza-0.21.2/src/fs/file.rs b/collector/compile-benchmarks/eza-0.21.2/src/fs/file.rs new file mode 100644 index 000000000..864ea0745 --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/src/fs/file.rs @@ -0,0 +1,1106 @@ +// SPDX-FileCopyrightText: 2024 Christina Sørensen +// SPDX-License-Identifier: EUPL-1.2 +// +// SPDX-FileCopyrightText: 2023-2024 Christina Sørensen, eza contributors +// SPDX-FileCopyrightText: 2014 Benjamin Sago +// SPDX-License-Identifier: MIT +//! Files, and methods and fields to access their metadata. + +#[cfg(unix)] +use std::collections::HashMap; +use std::fs::FileType; +use std::io; +#[cfg(unix)] +use std::os::unix::fs::{FileTypeExt, MetadataExt, PermissionsExt}; +#[cfg(windows)] +use std::os::windows::fs::MetadataExt; +use std::path::{Path, PathBuf}; +#[cfg(unix)] +use std::str; +#[cfg(unix)] +use std::sync::Mutex; +use std::sync::OnceLock; +use std::time::SystemTime; + +use chrono::prelude::*; + +use log::*; +#[cfg(unix)] +use std::sync::LazyLock; + +use crate::fs::dir::Dir; +use crate::fs::feature::xattr; +use crate::fs::feature::xattr::{Attribute, FileAttributes}; +use crate::fs::fields as f; +use crate::fs::fields::SecurityContextType; +use crate::fs::recursive_size::RecursiveSize; + +use super::mounts::all_mounts; +use super::mounts::MountedFs; + +// Maps (device_id, inode) => (size_in_bytes, size_in_blocks) +// Mutex::new is const but HashMap::new is not const requiring us to use lazy +// initialization. +#[allow(clippy::type_complexity)] +#[cfg(unix)] +static DIRECTORY_SIZE_CACHE: LazyLock>> = + LazyLock::new(|| Mutex::new(HashMap::new())); + +/// A **File** is a wrapper around one of Rust’s `PathBuf` values, along with +/// associated data about the file. +/// +/// Each file is definitely going to have its filename displayed at least +/// once, have its file extension extracted at least once, and have its metadata +/// information queried at least once, so it makes sense to do all this at the +/// start and hold on to all the information. +pub struct File<'dir> { + /// The filename portion of this file’s path, including the extension. + /// + /// This is used to compare against certain filenames (such as checking if + /// it’s “Makefile” or something) and to highlight only the filename in + /// colour when displaying the path. + pub name: String, + + /// The file’s name’s extension, if present, extracted from the name. + /// + /// This is queried many times over, so it’s worth caching it. + pub ext: Option, + + /// The path that begat this file. + /// + /// Even though the file’s name is extracted, the path needs to be kept + /// around, as certain operations involve looking up the file’s absolute + /// location (such as searching for compiled files) or using its original + /// path (following a symlink). + pub path: PathBuf, + + /// The cached filetype for this file + pub filetype: OnceLock>, + + /// A cached `metadata` (`stat`) call for this file. + /// + /// This too is queried multiple times, and is *not* cached by the OS, as + /// it could easily change between invocations — but exa is so short-lived + /// it’s better to just cache it. + pub metadata: OnceLock>, + + /// A reference to the directory that contains this file, if any. + /// + /// Filenames that get passed in on the command-line directly will have no + /// parent directory reference — although they technically have one on the + /// filesystem, we’ll never need to look at it, so it’ll be `None`. + /// However, *directories* that get passed in will produce files that + /// contain a reference to it, which is used in certain operations (such + /// as looking up compiled files). + pub parent_dir: Option<&'dir Dir>, + + /// Whether this is one of the two `--all all` directories, `.` and `..`. + /// + /// Unlike all other entries, these are not returned as part of the + /// directory’s children, and are in fact added specifically by exa; this + /// means that they should be skipped when recursing. + pub is_all_all: bool, + + /// Whether to dereference symbolic links when querying for information. + /// + /// For instance, when querying the size of a symbolic link, if + /// dereferencing is enabled, the size of the target will be displayed + /// instead. + pub deref_links: bool, + + /// The recursive directory size when `total_size` is used. + recursive_size: RecursiveSize, + + /// The extended attributes of this file. + extended_attributes: OnceLock>, + + /// The absolute value of this path, used to look up mount points. + absolute_path: OnceLock>, +} + +impl<'dir> File<'dir> { + pub fn from_args( + path: PathBuf, + parent_dir: PD, + filename: FN, + deref_links: bool, + total_size: bool, + filetype: Option, + ) -> File<'dir> + where + PD: Into>, + FN: Into>, + { + let parent_dir = parent_dir.into(); + let name = filename.into().unwrap_or_else(|| File::filename(&path)); + let ext = File::ext(&path); + + let is_all_all = false; + let recursive_size = if total_size { + RecursiveSize::Unknown + } else { + RecursiveSize::None + }; + + debug!("deref_links {}", deref_links); + + let filetype = match filetype { + Some(f) => OnceLock::from(Some(f)), + None => OnceLock::new(), + }; + + debug!("deref_links {}", deref_links); + + let mut file = File { + name, + ext, + path, + parent_dir, + is_all_all, + deref_links, + recursive_size, + filetype, + metadata: OnceLock::new(), + extended_attributes: OnceLock::new(), + absolute_path: OnceLock::new(), + }; + + if total_size { + file.recursive_size = file.recursive_directory_size(); + } + + file + } + + fn new_aa( + path: PathBuf, + parent_dir: &'dir Dir, + name: &'static str, + total_size: bool, + ) -> File<'dir> { + let ext = File::ext(&path); + + let is_all_all = true; + let parent_dir = Some(parent_dir); + let recursive_size = if total_size { + RecursiveSize::Unknown + } else { + RecursiveSize::None + }; + + let mut file = File { + name: name.into(), + ext, + path, + parent_dir, + is_all_all, + deref_links: false, + recursive_size, + metadata: OnceLock::new(), + absolute_path: OnceLock::new(), + extended_attributes: OnceLock::new(), + filetype: OnceLock::new(), + }; + + if total_size { + file.recursive_size = file.recursive_directory_size(); + } + + file + } + + pub fn new_aa_current(parent_dir: &'dir Dir, total_size: bool) -> File<'dir> { + File::new_aa(parent_dir.path.clone(), parent_dir, ".", total_size) + } + + pub fn new_aa_parent(path: PathBuf, parent_dir: &'dir Dir, total_size: bool) -> File<'dir> { + File::new_aa(path, parent_dir, "..", total_size) + } + + /// A file’s name is derived from its string. This needs to handle directories + /// such as `/` or `..`, which have no `file_name` component. So instead, just + /// use the last component as the name. + pub fn filename(path: &Path) -> String { + if let Some(back) = path.components().next_back() { + back.as_os_str().to_string_lossy().to_string() + } else { + // use the path as fallback + error!("Path {:?} has no last component", path); + path.display().to_string() + } + } + + /// Extract an extension from a file path, if one is present, in lowercase. + /// + /// The extension is the series of characters after the last dot. This + /// deliberately counts dotfiles, so the “.git” folder has the extension “git”. + /// + /// ASCII lowercasing is used because these extensions are only compared + /// against a pre-compiled list of extensions which are known to only exist + /// within ASCII, so it’s alright. + fn ext(path: &Path) -> Option { + let name = path.file_name().map(|f| f.to_string_lossy().to_string())?; + + name.rfind('.').map(|p| name[p + 1..].to_ascii_lowercase()) + } + + /// Read the extended attributes of a file path. + fn gather_extended_attributes(&self) -> Vec { + if xattr::ENABLED { + let attributes = if self.deref_links { + self.path.attributes() + } else { + self.path.symlink_attributes() + }; + match attributes { + Ok(xattrs) => xattrs, + Err(e) => { + error!( + "Error looking up extended attributes for {}: {}", + self.path.display(), + e + ); + Vec::new() + } + } + } else { + Vec::new() + } + } + + fn filetype(&self) -> Option<&std::fs::FileType> { + self.filetype + .get_or_init(|| self.metadata().as_ref().ok().map(|md| md.file_type())) + .as_ref() + } + + pub fn metadata(&self) -> Result<&std::fs::Metadata, &io::Error> { + self.metadata + .get_or_init(|| { + debug!("Statting file {:?}", &self.path); + std::fs::symlink_metadata(&self.path) + }) + .as_ref() + } + + /// Get the extended attributes of a file path on demand. + pub fn extended_attributes(&self) -> &Vec { + self.extended_attributes + .get_or_init(|| self.gather_extended_attributes()) + } + + /// Whether this file is a directory on the filesystem. + pub fn is_directory(&self) -> bool { + self.filetype().map_or(false, std::fs::FileType::is_dir) + } + + /// Whether this file is a directory, or a symlink pointing to a directory. + pub fn points_to_directory(&self) -> bool { + if self.is_directory() { + return true; + } + + if self.is_link() { + let target = self.link_target(); + if let FileTarget::Ok(target) = target { + return target.points_to_directory(); + } + } + + false + } + + /// If this file is a directory on the filesystem, then clone its + /// `PathBuf` for use in one of our own `Dir` values, and read a list of + /// its contents. + /// + /// Returns an IO error upon failure, but this shouldn’t be used to check + /// if a `File` is a directory or not! For that, just use `is_directory()`. + pub fn to_dir(&self) -> io::Result { + trace!("to_dir: reading dir"); + Dir::read_dir(self.path.clone()) + } + + /// Whether this file is a regular file on the filesystem — that is, not a + /// directory, a link, or anything else treated specially. + pub fn is_file(&self) -> bool { + self.filetype().map_or(false, std::fs::FileType::is_file) + } + + /// Whether this file is both a regular file *and* executable for the + /// current user. An executable file has a different purpose from an + /// executable directory, so they should be highlighted differently. + #[cfg(unix)] + pub fn is_executable_file(&self) -> bool { + let bit = modes::USER_EXECUTE; + if !self.is_file() { + return false; + } + let Ok(md) = self.metadata() else { + return false; + }; + (md.permissions().mode() & bit) == bit + } + + /// Whether this file is a symlink on the filesystem. + pub fn is_link(&self) -> bool { + self.filetype().map_or(false, FileType::is_symlink) + } + + /// Whether this file is a named pipe on the filesystem. + #[cfg(unix)] + pub fn is_pipe(&self) -> bool { + self.filetype().map_or(false, FileTypeExt::is_fifo) + } + + /// Whether this file is a char device on the filesystem. + #[cfg(unix)] + pub fn is_char_device(&self) -> bool { + self.filetype().map_or(false, FileTypeExt::is_char_device) + } + + /// Whether this file is a block device on the filesystem. + #[cfg(unix)] + pub fn is_block_device(&self) -> bool { + self.filetype().map_or(false, FileTypeExt::is_block_device) + } + + /// Whether this file is a socket on the filesystem. + #[cfg(unix)] + pub fn is_socket(&self) -> bool { + self.filetype().map_or(false, FileTypeExt::is_socket) + } + + /// Determine the full path resolving all symbolic links on demand. + pub fn absolute_path(&self) -> Option<&PathBuf> { + self.absolute_path + .get_or_init(|| { + if self.is_link() && self.link_target().is_broken() { + // workaround for broken symlinks to get absolute path for parent and then + // append name of file; std::fs::canonicalize requires all path components + // (including the last one) to exist + self.path + .parent() + .and_then(|parent| std::fs::canonicalize(parent).ok()) + .map(|p| p.join(self.name.clone())) + } else { + std::fs::canonicalize(&self.path).ok() + } + }) + .as_ref() + } + + /// Whether this file is a mount point + pub fn is_mount_point(&self) -> bool { + cfg!(any(target_os = "linux", target_os = "macos")) + && self.is_directory() + && self + .absolute_path() + .is_some_and(|p| all_mounts().contains_key(p)) + } + + /// The filesystem device and type for a mount point + pub fn mount_point_info(&self) -> Option<&MountedFs> { + if cfg!(any(target_os = "linux", target_os = "macos")) { + return self.absolute_path().and_then(|p| all_mounts().get(p)); + } + None + } + + /// Re-prefixes the path pointed to by this file, if it’s a symlink, to + /// make it an absolute path that can be accessed from whichever + /// directory exa is being run from. + fn reorient_target_path(&self, path: &Path) -> PathBuf { + if path.is_absolute() { + path.to_path_buf() + } else if let Some(dir) = self.parent_dir { + dir.join(path) + } else if let Some(parent) = self.path.parent() { + parent.join(path) + } else { + self.path.join(path) + } + } + + /// Again assuming this file is a symlink, follows that link and returns + /// the result of following it. + /// + /// For a working symlink that the user is allowed to follow, + /// this will be the `File` object at the other end, which can then have + /// its name, colour, and other details read. + /// + /// For a broken symlink, returns where the file *would* be, if it + /// existed. If this file cannot be read at all, returns the error that + /// we got when we tried to read it. + pub fn link_target(&self) -> FileTarget<'dir> { + // We need to be careful to treat the path actually pointed to by + // this file — which could be absolute or relative — to the path + // we actually look up and turn into a `File` — which needs to be + // absolute to be accessible from any directory. + debug!("Reading link {:?}", &self.path); + let path = match std::fs::read_link(&self.path) { + Ok(p) => p, + Err(e) => return FileTarget::Err(e), + }; + + let absolute_path = self.reorient_target_path(&path); + + // Use plain `metadata` instead of `symlink_metadata` - we *want* to + // follow links. + match std::fs::metadata(&absolute_path) { + Ok(metadata) => { + let ext = File::ext(&path); + let name = File::filename(&path); + let extended_attributes = OnceLock::new(); + let absolute_path_cell = OnceLock::from(Some(absolute_path)); + let file = File { + parent_dir: None, + path, + ext, + filetype: OnceLock::from(Some(metadata.file_type())), + metadata: OnceLock::from(Ok(metadata)), + name, + is_all_all: false, + deref_links: self.deref_links, + extended_attributes, + absolute_path: absolute_path_cell, + recursive_size: RecursiveSize::None, + }; + FileTarget::Ok(Box::new(file)) + } + Err(e) => { + error!("Error following link {:?}: {:#?}", &path, e); + FileTarget::Broken(path) + } + } + } + + /// Assuming this file is a symlink, follows that link and any further + /// links recursively, returning the result from following the trail. + /// + /// For a working symlink that the user is allowed to follow, + /// this will be the `File` object at the other end, which can then have + /// its name, colour, and other details read. + /// + /// For a broken symlink, returns where the file *would* be, if it + /// existed. If this file cannot be read at all, returns the error that + /// we got when we tried to read it. + pub fn link_target_recurse(&self) -> FileTarget<'dir> { + let target = self.link_target(); + if let FileTarget::Ok(f) = target { + if f.is_link() { + return f.link_target_recurse(); + } + return FileTarget::Ok(f); + } + target + } + + /// This file’s number of hard links. + /// + /// It also reports whether this is both a regular file, and a file with + /// multiple links. This is important, because a file with multiple links + /// is uncommon, while you come across directories and other types + /// with multiple links much more often. Thus, it should get highlighted + /// more attentively. + #[cfg(unix)] + pub fn links(&self) -> f::Links { + let count = self.metadata().map_or(0, MetadataExt::nlink); + + f::Links { + count, + multiple: self.is_file() && count > 1, + } + } + + /// This file’s inode. + #[cfg(unix)] + pub fn inode(&self) -> f::Inode { + f::Inode(self.metadata().map_or(0, MetadataExt::ino)) + } + + /// This actual size the file takes up on disk, in bytes. + #[cfg(unix)] + pub fn blocksize(&self) -> f::Blocksize { + if self.deref_links && self.is_link() { + match self.link_target() { + FileTarget::Ok(f) => f.blocksize(), + _ => f::Blocksize::None, + } + } else if self.is_directory() { + self.recursive_size.map_or(f::Blocksize::None, |_, blocks| { + f::Blocksize::Some(blocks * 512) + }) + } else if self.is_file() { + // Note that metadata.blocks returns the number of blocks + // for 512 byte blocks according to the POSIX standard + // even though the physical block size may be different. + f::Blocksize::Some(self.metadata().map_or(0, |md| md.blocks() * 512)) + } else { + // directory or symlinks + f::Blocksize::None + } + } + + /// The ID of the user that own this file. If dereferencing links, the links + /// may be broken, in which case `None` will be returned. + #[cfg(unix)] + pub fn user(&self) -> Option { + if self.is_link() && self.deref_links { + return match self.link_target_recurse() { + FileTarget::Ok(f) => f.user(), + _ => None, + }; + } + Some(f::User(self.metadata().map_or(0, MetadataExt::uid))) + } + + /// The ID of the group that owns this file. + #[cfg(unix)] + pub fn group(&self) -> Option { + if self.is_link() && self.deref_links { + return match self.link_target_recurse() { + FileTarget::Ok(f) => f.group(), + _ => None, + }; + } + Some(f::Group(self.metadata().map_or(0, MetadataExt::gid))) + } + + /// This file’s size, if it’s a regular file. + /// + /// For directories, the recursive size or no size is given depending on + /// flags. Although they do have a size on some filesystems, I’ve never + /// looked at one of those numbers and gained any information from it. + /// + /// Block and character devices return their device IDs, because they + /// usually just have a file size of zero. + /// + /// Links will return the size of their target (recursively through other + /// links) if dereferencing is enabled, otherwise None. + #[cfg(unix)] + pub fn size(&self) -> f::Size { + if self.deref_links && self.is_link() { + match self.link_target() { + FileTarget::Ok(f) => f.size(), + _ => f::Size::None, + } + } else if self.is_directory() { + self.recursive_size + .map_or(f::Size::None, |bytes, _| f::Size::Some(bytes)) + } else if self.is_char_device() || self.is_block_device() { + let device_id = self.metadata().map_or(0, MetadataExt::rdev); + + // MacOS and Linux have different arguments and return types for the + // functions major and minor. On Linux the try_into().unwrap() and + // the "as u32" cast are not needed. We turn off the warning to + // allow it to compile cleanly on Linux. + #[allow(trivial_numeric_casts)] + #[allow(clippy::unnecessary_cast, clippy::useless_conversion)] + f::Size::DeviceIDs(f::DeviceIDs { + major: libc::major( + device_id + .try_into() + .expect("Malformed device major ID when getting filesize"), + ) as u32, + minor: libc::minor( + device_id + .try_into() + .expect("Malformed device major ID when getting filesize"), + ) as u32, + }) + } else if self.is_file() { + f::Size::Some(self.metadata().map_or(0, std::fs::Metadata::len)) + } else { + // symlink + f::Size::None + } + } + + /// Returns the size of the file or indicates no size if it's a directory. + /// + /// For Windows platforms, the size of directories is not computed and will + /// return `Size::None`. + #[cfg(windows)] + pub fn size(&self) -> f::Size { + if self.is_directory() { + f::Size::None + } else { + f::Size::Some(self.metadata().map_or(0, std::fs::Metadata::len)) + } + } + + /// Calculate the total directory size recursively. If not a directory `None` + /// will be returned. The directory size is cached for recursive directory + /// listing. + #[cfg(unix)] + fn recursive_directory_size(&self) -> RecursiveSize { + if self.is_directory() { + let key = ( + self.metadata().map_or(0, MetadataExt::dev), + self.metadata().map_or(0, MetadataExt::ino), + ); + if let Some(size) = DIRECTORY_SIZE_CACHE.lock().unwrap().get(&key) { + return RecursiveSize::Some(size.0, size.1); + } + Dir::read_dir(self.path.clone()).map_or(RecursiveSize::Unknown, |dir| { + let mut size = 0; + let mut blocks = 0; + for file in dir.files(super::DotFilter::Dotfiles, None, false, false, true) { + match file.recursive_directory_size() { + RecursiveSize::Some(bytes, blks) => { + size += bytes; + blocks += blks; + } + RecursiveSize::Unknown => {} + RecursiveSize::None => { + size += file.metadata().map_or(0, MetadataExt::size); + blocks += file.metadata().map_or(0, MetadataExt::blocks); + } + } + } + DIRECTORY_SIZE_CACHE + .lock() + .unwrap() + .insert(key, (size, blocks)); + RecursiveSize::Some(size, blocks) + }) + } else { + RecursiveSize::None + } + } + + /// Windows version always returns None. The metadata for + /// `volume_serial_number` and `file_index` are marked unstable so we can + /// not cache the sizes. Without caching we could end up walking the + /// directory structure several times. + #[cfg(windows)] + fn recursive_directory_size(&self) -> RecursiveSize { + RecursiveSize::None + } + + /// Returns the same value as `self.metadata.len()` or the recursive size + /// of a directory when `total_size` is used. + #[inline] + pub fn length(&self) -> u64 { + self.recursive_size + .unwrap_bytes_or(self.metadata().map_or(0, std::fs::Metadata::len)) + } + + /// Is the file is using recursive size calculation + #[inline] + pub fn is_recursive_size(&self) -> bool { + !self.recursive_size.is_none() + } + + /// Determines if the directory is empty or not. + /// + /// For Unix platforms, this function first checks the link count to quickly + /// determine non-empty directories. On most UNIX filesystems the link count + /// is two plus the number of subdirectories. If the link count is less than + /// or equal to 2, it then checks the directory contents to determine if + /// it's truly empty. The naive approach used here checks the contents + /// directly, as certain filesystems make it difficult to infer emptiness + /// based on directory size alone. + #[cfg(unix)] + pub fn is_empty_dir(&self) -> bool { + if self.is_directory() { + if self.metadata().map_or(0, MetadataExt::nlink) > 2 { + // Directories will have a link count of two if they do not have any subdirectories. + // The '.' entry is a link to itself and the '..' is a link to the parent directory. + // A subdirectory will have a link to its parent directory increasing the link count + // above two. This will avoid the expensive read_dir call below when a directory + // has subdirectories. + false + } else { + self.is_empty_directory() + } + } else { + false + } + } + + /// Determines if the directory is empty or not. + /// + /// For Windows platforms, this function checks the directory contents directly + /// to determine if it's empty. Since certain filesystems on Windows make it + /// challenging to infer emptiness based on directory size, this approach is used. + #[cfg(windows)] + pub fn is_empty_dir(&self) -> bool { + if self.is_directory() { + self.is_empty_directory() + } else { + false + } + } + + /// Checks the contents of the directory to determine if it's empty. + /// + /// This function avoids counting '.' and '..' when determining if the directory is + /// empty. If any other entries are found, it returns `false`. + /// + /// The naive approach, as one would think that this info may have been cached. + /// but as mentioned in the size function comment above, different filesystems + /// make it difficult to get any info about a dir by it's size, so this may be it. + fn is_empty_directory(&self) -> bool { + trace!("is_empty_directory: reading dir"); + match Dir::read_dir(self.path.clone()) { + // . & .. are skipped, if the returned iterator has .next(), it's not empty + Ok(has_files) => has_files + .files(super::DotFilter::Dotfiles, None, false, false, false) + .next() + .is_none(), + Err(_) => false, + } + } + + /// Converts a `SystemTime` to a `NaiveDateTime` without panicking. + /// + /// Fixes #655 and #667 in `Self::modified_time`, `Self::accessed_time` and + /// `Self::created_time`. + fn systemtime_to_naivedatetime(st: SystemTime) -> Option { + let duration = st.duration_since(SystemTime::UNIX_EPOCH).ok()?; + + DateTime::from_timestamp( + duration.as_secs().try_into().ok()?, + (duration.as_nanos() % 1_000_000_000).try_into().ok()?, + ) + .map(|dt| dt.naive_local()) + } + + /// This file’s last modified timestamp, if available on this platform. + pub fn modified_time(&self) -> Option { + if self.is_link() && self.deref_links { + return match self.link_target_recurse() { + FileTarget::Ok(f) => f.modified_time(), + _ => None, + }; + } + self.metadata() + .ok() + .and_then(|md| md.modified().ok()) + .and_then(Self::systemtime_to_naivedatetime) + } + + /// This file’s last changed timestamp, if available on this platform. + #[cfg(unix)] + pub fn changed_time(&self) -> Option { + if self.is_link() && self.deref_links { + return match self.link_target_recurse() { + FileTarget::Ok(f) => f.changed_time(), + _ => None, + }; + } + let md = self.metadata(); + DateTime::from_timestamp( + md.map_or(0, MetadataExt::ctime), + md.map_or(0, |md| md.ctime_nsec() as u32), + ) + .map(|dt| dt.naive_local()) + } + + #[cfg(windows)] + pub fn changed_time(&self) -> Option { + self.modified_time() + } + + /// This file’s last accessed timestamp, if available on this platform. + pub fn accessed_time(&self) -> Option { + if self.is_link() && self.deref_links { + return match self.link_target_recurse() { + FileTarget::Ok(f) => f.accessed_time(), + _ => None, + }; + } + self.metadata() + .ok() + .and_then(|md| md.accessed().ok()) + .and_then(Self::systemtime_to_naivedatetime) + } + + /// This file’s created timestamp, if available on this platform. + pub fn created_time(&self) -> Option { + if self.is_link() && self.deref_links { + return match self.link_target_recurse() { + FileTarget::Ok(f) => f.created_time(), + _ => None, + }; + } + let btime = self.metadata().ok()?.created().ok()?; + Self::systemtime_to_naivedatetime(btime) + } + + /// This file’s ‘type’. + /// + /// This is used a the leftmost character of the permissions column. + /// The file type can usually be guessed from the colour of the file, but + /// ls puts this character there. + #[cfg(unix)] + pub fn type_char(&self) -> f::Type { + if self.is_file() { + f::Type::File + } else if self.is_directory() { + f::Type::Directory + } else if self.is_pipe() { + f::Type::Pipe + } else if self.is_link() { + f::Type::Link + } else if self.is_char_device() { + f::Type::CharDevice + } else if self.is_block_device() { + f::Type::BlockDevice + } else if self.is_socket() { + f::Type::Socket + } else { + f::Type::Special + } + } + + #[cfg(windows)] + pub fn type_char(&self) -> f::Type { + if self.is_file() { + f::Type::File + } else if self.is_directory() { + f::Type::Directory + } else { + f::Type::Special + } + } + + /// This file’s permissions, with flags for each bit. + #[cfg(unix)] + pub fn permissions(&self) -> Option { + if self.is_link() && self.deref_links { + // If the chain of links is broken, we instead fall through and + // return the permissions of the original link, as would have been + // done if we were not dereferencing. + return match self.link_target_recurse() { + FileTarget::Ok(f) => f.permissions(), + _ => None, + }; + } + let bits = self.metadata().map_or(0, MetadataExt::mode); + let has_bit = |bit| bits & bit == bit; + + Some(f::Permissions { + user_read: has_bit(modes::USER_READ), + user_write: has_bit(modes::USER_WRITE), + user_execute: has_bit(modes::USER_EXECUTE), + + group_read: has_bit(modes::GROUP_READ), + group_write: has_bit(modes::GROUP_WRITE), + group_execute: has_bit(modes::GROUP_EXECUTE), + + other_read: has_bit(modes::OTHER_READ), + other_write: has_bit(modes::OTHER_WRITE), + other_execute: has_bit(modes::OTHER_EXECUTE), + + sticky: has_bit(modes::STICKY), + setgid: has_bit(modes::SETGID), + setuid: has_bit(modes::SETUID), + }) + } + + #[cfg(windows)] + pub fn attributes(&self) -> Option { + let bits = self.metadata().ok()?.file_attributes(); + let has_bit = |bit| bits & bit == bit; + + // https://docs.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants + Some(f::Attributes { + directory: has_bit(0x10), + archive: has_bit(0x20), + readonly: has_bit(0x1), + hidden: has_bit(0x2), + system: has_bit(0x4), + reparse_point: has_bit(0x400), + }) + } + + /// This file’s security context field. + #[cfg(unix)] + pub fn security_context(&self) -> f::SecurityContext<'_> { + let context = match self + .extended_attributes() + .iter() + .find(|a| a.name == "security.selinux") + { + Some(attr) => match &attr.value { + None => SecurityContextType::None, + Some(value) => match str::from_utf8(value) { + Ok(v) => SecurityContextType::SELinux(v.trim_end_matches(char::from(0))), + Err(_) => SecurityContextType::None, + }, + }, + None => SecurityContextType::None, + }; + + f::SecurityContext { context } + } + + #[cfg(windows)] + pub fn security_context(&self) -> f::SecurityContext<'_> { + f::SecurityContext { + context: SecurityContextType::None, + } + } + + /// User file flags. + #[cfg(any( + target_os = "macos", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly" + ))] + pub fn flags(&self) -> f::Flags { + #[cfg(target_os = "dragonfly")] + use std::os::dragonfly::fs::MetadataExt; + #[cfg(target_os = "freebsd")] + use std::os::freebsd::fs::MetadataExt; + #[cfg(target_os = "macos")] + use std::os::macos::fs::MetadataExt; + #[cfg(target_os = "netbsd")] + use std::os::netbsd::fs::MetadataExt; + #[cfg(target_os = "openbsd")] + use std::os::openbsd::fs::MetadataExt; + f::Flags( + self.metadata() + .map(MetadataExt::st_flags) + .unwrap_or_default(), + ) + } + + #[cfg(windows)] + pub fn flags(&self) -> f::Flags { + f::Flags(self.metadata().map_or(0, |md| md.file_attributes())) + } + + #[cfg(not(any( + target_os = "macos", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly", + target_os = "windows" + )))] + pub fn flags(&self) -> f::Flags { + f::Flags(0) + } +} + +impl<'a> AsRef> for File<'a> { + fn as_ref(&self) -> &File<'a> { + self + } +} + +/// The result of following a symlink. +pub enum FileTarget<'dir> { + /// The symlink pointed at a file that exists. + Ok(Box>), + + /// The symlink pointed at a file that does not exist. Holds the path + /// where the file would be, if it existed. + Broken(PathBuf), + + /// There was an IO error when following the link. This can happen if the + /// file isn’t a link to begin with, but also if, say, we don’t have + /// permission to follow it. + Err(io::Error), + // Err is its own variant, instead of having the whole thing be inside an + // `io::Result`, because being unable to follow a symlink is not a serious + // error — we just display the error message and move on. +} + +impl<'dir> FileTarget<'dir> { + /// Whether this link doesn’t lead to a file, for whatever reason. This + /// gets used to determine how to highlight the link in grid views. + pub fn is_broken(&self) -> bool { + matches!(self, Self::Broken(_) | Self::Err(_)) + } +} + +/// More readable aliases for the permission bits exposed by libc. +#[allow(trivial_numeric_casts)] +#[cfg(unix)] +mod modes { + + // The `libc::mode_t` type’s actual type varies, but the value returned + // from `metadata.permissions().mode()` is always `u32`. + pub type Mode = u32; + + pub const USER_READ: Mode = libc::S_IRUSR as Mode; + pub const USER_WRITE: Mode = libc::S_IWUSR as Mode; + pub const USER_EXECUTE: Mode = libc::S_IXUSR as Mode; + + pub const GROUP_READ: Mode = libc::S_IRGRP as Mode; + pub const GROUP_WRITE: Mode = libc::S_IWGRP as Mode; + pub const GROUP_EXECUTE: Mode = libc::S_IXGRP as Mode; + + pub const OTHER_READ: Mode = libc::S_IROTH as Mode; + pub const OTHER_WRITE: Mode = libc::S_IWOTH as Mode; + pub const OTHER_EXECUTE: Mode = libc::S_IXOTH as Mode; + + pub const STICKY: Mode = libc::S_ISVTX as Mode; + pub const SETGID: Mode = libc::S_ISGID as Mode; + pub const SETUID: Mode = libc::S_ISUID as Mode; +} + +#[cfg(test)] +mod ext_test { + use super::File; + use std::path::Path; + + #[test] + fn extension() { + assert_eq!(Some("dat".to_string()), File::ext(Path::new("fester.dat"))); + } + + #[test] + fn dotfile() { + assert_eq!(Some("vimrc".to_string()), File::ext(Path::new(".vimrc"))); + } + + #[test] + fn no_extension() { + assert_eq!(None, File::ext(Path::new("jarlsberg"))); + } +} + +#[cfg(test)] +mod filename_test { + use super::File; + use std::path::Path; + + #[test] + fn file() { + assert_eq!("fester.dat", File::filename(Path::new("fester.dat"))); + } + + #[test] + fn no_path() { + assert_eq!("foo.wha", File::filename(Path::new("/var/cache/foo.wha"))); + } + + #[test] + fn here() { + assert_eq!(".", File::filename(Path::new("."))); + } + + #[test] + fn there() { + assert_eq!("..", File::filename(Path::new(".."))); + } + + #[test] + fn everywhere() { + assert_eq!("..", File::filename(Path::new("./.."))); + } + + #[test] + #[cfg(unix)] + fn topmost() { + assert_eq!("/", File::filename(Path::new("/"))); + } +} diff --git a/collector/compile-benchmarks/eza-0.21.2/src/fs/filter.rs b/collector/compile-benchmarks/eza-0.21.2/src/fs/filter.rs new file mode 100644 index 000000000..40c8858f8 --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/src/fs/filter.rs @@ -0,0 +1,424 @@ +// SPDX-FileCopyrightText: 2024 Christina Sørensen +// SPDX-License-Identifier: EUPL-1.2 +// +// SPDX-FileCopyrightText: 2023-2024 Christina Sørensen, eza contributors +// SPDX-FileCopyrightText: 2014 Benjamin Sago +// SPDX-License-Identifier: MIT +//! Filtering and sorting the list of files before displaying them. + +use std::cmp::Ordering; +use std::iter::FromIterator; +#[cfg(unix)] +use std::os::unix::fs::MetadataExt; + +use crate::fs::DotFilter; +use crate::fs::File; + +/// Flags used to manage the **file filter** process +#[derive(PartialEq, Eq, Debug, Clone)] +pub enum FileFilterFlags { + /// Whether to reverse the sorting order. This would sort the largest + /// files first, or files starting with Z, or the most-recently-changed + /// ones, depending on the sort field. + Reverse, + + /// Whether to only show directories. + OnlyDirs, + + /// Whether to only show files. + OnlyFiles, + + /// Whether to ignore symlinks + NoSymlinks, + + /// Whether to explicitly show symlinks + ShowSymlinks, + + /// Whether directories should be listed first, and other types of file + /// second. Some users prefer it like this. + ListDirsFirst, + + /// Whether directories should be listed as the last items, after other + /// types of file. Some users prefer it like this. + ListDirsLast, +} + +/// The **file filter** processes a list of files before displaying them to +/// the user, by removing files they don’t want to see, and putting the list +/// in the desired order. +/// +/// Usually a user does not want to see *every* file in the list. The most +/// common case is to remove files starting with `.`, which are designated +/// as ‘hidden’ files. +/// +/// The special files `.` and `..` files are not actually filtered out, but +/// need to be inserted into the list, in a special case. +/// +/// The filter also governs sorting the list. After being filtered, pairs of +/// files are compared and sorted based on the result, with the sort field +/// performing the comparison. +#[derive(PartialEq, Eq, Debug, Clone)] +pub struct FileFilter { + /// The metadata field to sort by. + pub sort_field: SortField, + + // Flags that the file filtering process follow + pub flags: Vec, + + /// Which invisible “dot” files to include when listing a directory. + /// + /// Files starting with a single “.” are used to determine “system” or + /// “configuration” files that should not be displayed in a regular + /// directory listing, and the directory entries “.” and “..” are + /// considered extra-special. + /// + /// This came about more or less by a complete historical accident, + /// when the original `ls` tried to hide `.` and `..`: + /// + /// [Linux History: How Dot Files Became Hidden Files](https://linux-audit.com/linux-history-how-dot-files-became-hidden-files/) + pub dot_filter: DotFilter, + + /// Glob patterns to ignore. Any file name that matches *any* of these + /// patterns won’t be displayed in the list. + pub ignore_patterns: IgnorePatterns, + + /// Whether to ignore Git-ignored patterns. + pub git_ignore: GitIgnore, + + /// Whether to ignore symlinks + pub no_symlinks: bool, + + /// Whether to explicitly show symlinks + pub show_symlinks: bool, +} + +impl FileFilter { + /// Remove every file in the given vector that does *not* pass the + /// filter predicate for files found inside a directory. + #[rustfmt::skip] + pub fn filter_child_files(&self, is_recurse: bool, files: &mut Vec>) { + use FileFilterFlags::{NoSymlinks, OnlyDirs, OnlyFiles, ShowSymlinks}; + + files.retain(|f| !self.ignore_patterns.is_ignored(&f.name)); + files.retain(|f| { + match ( + self.flags.contains(&OnlyDirs), + self.flags.contains(&OnlyFiles), + self.flags.contains(&NoSymlinks), + self.flags.contains(&ShowSymlinks), + ) { + (true, false, false, false) => f.is_directory(), + (true, false, true, false) => f.is_directory(), + (true, false, false, true) => f.is_directory() || f.points_to_directory(), + (false, true, false, false) => if is_recurse { true } else {f.is_file() }, + (false, true, false, true) => if is_recurse { true } else { f.is_file() || f.is_link() && !f.points_to_directory() + }, + (false, false, true, false) => !f.is_link(), + _ => true, + } + }); + } + + /// Remove every file in the given vector that does *not* pass the + /// filter predicate for file names specified on the command-line. + /// + /// The rules are different for these types of files than the other + /// type because the ignore rules can be used with globbing. For + /// example, running `exa -I='*. tmp' .vimrc` shouldn’t filter out the + /// dotfile, because it’s been directly specified. But running + /// `exa -I='*.ogg' music/*` should filter out the ogg files obtained + /// from the glob, even though the globbing is done by the shell! + pub fn filter_argument_files(&self, files: &mut Vec>) { + files.retain(|f| !self.ignore_patterns.is_ignored(&f.name)); + } + + /// Sort the files in the given vector based on the sort field option. + pub fn sort_files<'a, F>(&self, files: &mut [F]) + where + F: AsRef>, + { + files.sort_by(|a, b| self.sort_field.compare_files(a.as_ref(), b.as_ref())); + + if self.flags.contains(&FileFilterFlags::Reverse) { + files.reverse(); + } + + if self.flags.contains(&FileFilterFlags::ListDirsFirst) { + // This relies on the fact that `sort_by` is *stable*: it will keep + // adjacent elements next to each other. + files.sort_by(|a, b| { + b.as_ref() + .points_to_directory() + .cmp(&a.as_ref().points_to_directory()) + }); + } else if self.flags.contains(&FileFilterFlags::ListDirsLast) { + files.sort_by(|a, b| { + a.as_ref() + .points_to_directory() + .cmp(&b.as_ref().points_to_directory()) + }); + } + } +} + +/// User-supplied field to sort by. +#[derive(PartialEq, Eq, Debug, Copy, Clone)] +pub enum SortField { + /// Don’t apply any sorting. This is usually used as an optimisation in + /// scripts, where the order doesn’t matter. + Unsorted, + + /// The file name. This is the default sorting. + Name(SortCase), + + /// The file’s extension, with extensionless files being listed first. + Extension(SortCase), + + /// The file’s size, in bytes. + Size, + + /// The file’s inode, which usually corresponds to the order in which + /// files were created on the filesystem, more or less. + #[cfg(unix)] + FileInode, + + /// The time the file was modified (the “mtime”). + /// + /// As this is stored as a Unix timestamp, rather than a local time + /// instance, the time zone does not matter and will only be used to + /// display the timestamps, not compare them. + ModifiedDate, + + /// The time the file was accessed (the “atime”). + /// + /// Oddly enough, this field rarely holds the *actual* accessed time. + /// Recording a read time means writing to the file each time it’s read + /// slows the whole operation down, so many systems will only update the + /// timestamp in certain circumstances. This has become common enough that + /// it’s now expected behaviour! + /// + AccessedDate, + + /// The time the file was changed (the “ctime”). + /// + /// This field is used to mark the time when a file’s metadata + /// changed — its permissions, owners, or link count. + /// + /// In original Unix, this was, however, meant as creation time. + /// + ChangedDate, + + /// The time the file was created (the “btime” or “birthtime”). + CreatedDate, + + /// The type of the file: directories, links, pipes, regular, files, etc. + /// + /// Files are ordered according to the `PartialOrd` implementation of + /// `fs::fields::Type`, so changing that will change this. + FileType, + + /// The “age” of the file, which is the time it was modified sorted + /// backwards. The reverse of the `ModifiedDate` ordering! + /// + /// It turns out that listing the most-recently-modified files first is a + /// common-enough use case that it deserves its own variant. This would be + /// implemented by just using the modified date and setting the reverse + /// flag, but this would make reversing *that* output not work, which is + /// bad, even though that’s kind of nonsensical. So it’s its own variant + /// that can be reversed like usual. + ModifiedAge, + + /// The file's name, however if the name of the file begins with `.` + /// ignore the leading `.` and then sort as Name + NameMixHidden(SortCase), +} + +/// Whether a field should be sorted case-sensitively or case-insensitively. +/// This determines which of the `natord` functions to use. +/// +/// I kept on forgetting which one was sensitive and which one was +/// insensitive. Would a case-sensitive sort put capital letters first because +/// it takes the case of the letters into account, or intermingle them with +/// lowercase letters because it takes the difference between the two cases +/// into account? I gave up and just named these two variants after the +/// effects they have. +#[derive(PartialEq, Eq, Debug, Copy, Clone)] +pub enum SortCase { + /// Sort files case-sensitively with uppercase first, with ‘A’ coming + /// before ‘a’. + ABCabc, + + /// Sort files case-insensitively, with ‘A’ being equal to ‘a’. + AaBbCc, +} + +impl SortField { + /// Compares two files to determine the order they should be listed in, + /// depending on the search field. + /// + /// The `natord` crate is used here to provide a more *natural* sorting + /// order than just sorting character-by-character. This splits filenames + /// into groups between letters and numbers, and then sorts those blocks + /// together, so `file10` will sort after `file9`, instead of before it + /// because of the `1`. + pub fn compare_files(self, a: &File<'_>, b: &File<'_>) -> Ordering { + use self::SortCase::{ABCabc, AaBbCc}; + + #[rustfmt::skip] + return match self { + Self::Unsorted => Ordering::Equal, + + Self::Name(ABCabc) => natord::compare(&a.name, &b.name), + Self::Name(AaBbCc) => natord::compare_ignore_case(&a.name, &b.name), + + Self::Size => a.length().cmp(&b.length()), + + #[cfg(unix)] + Self::FileInode => { + a.metadata().map_or(0, MetadataExt::ino) + .cmp(&b.metadata().map_or(0, MetadataExt::ino)) + } + Self::ModifiedDate => a.modified_time().cmp(&b.modified_time()), + Self::AccessedDate => a.accessed_time().cmp(&b.accessed_time()), + Self::ChangedDate => a.changed_time().cmp(&b.changed_time()), + Self::CreatedDate => a.created_time().cmp(&b.created_time()), + Self::ModifiedAge => b.modified_time().cmp(&a.modified_time()), // flip b and a + Self::FileType => match a.type_char().cmp(&b.type_char()) { // todo: this recomputes + Ordering::Equal => natord::compare(&a.name, &b.name), + order => order, + }, + + Self::Extension(ABCabc) => match a.ext.cmp(&b.ext) { + Ordering::Equal => natord::compare(&a.name, &b.name), + order => order, + }, + + Self::Extension(AaBbCc) => match a.ext.cmp(&b.ext) { + Ordering::Equal => natord::compare_ignore_case(&a.name, &b.name), + order => order, + }, + + Self::NameMixHidden(ABCabc) => natord::compare( + Self::strip_dot(&a.name), + Self::strip_dot(&b.name) + ), + Self::NameMixHidden(AaBbCc) => natord::compare_ignore_case( + Self::strip_dot(&a.name), + Self::strip_dot(&b.name) + ), + }; + } + + fn strip_dot(n: &str) -> &str { + match n.strip_prefix('.') { + Some(s) => s, + None => n, + } + } +} + +/// The **ignore patterns** are a list of globs that are tested against +/// each filename, and if any of them match, that file isn’t displayed. +/// This lets a user hide, say, text files by ignoring `*.txt`. +#[derive(PartialEq, Eq, Default, Debug, Clone)] +pub struct IgnorePatterns { + patterns: Vec, +} + +impl FromIterator for IgnorePatterns { + fn from_iter(iter: I) -> Self + where + I: IntoIterator, + { + let patterns = iter.into_iter().collect(); + Self { patterns } + } +} + +impl IgnorePatterns { + /// Create a new list from the input glob strings, turning the inputs that + /// are valid glob patterns into an `IgnorePatterns`. The inputs that + /// don’t parse correctly are returned separately. + pub fn parse_from_iter<'a, I: IntoIterator>( + iter: I, + ) -> (Self, Vec) { + let iter = iter.into_iter(); + + // Almost all glob patterns are valid, so it’s worth pre-allocating + // the vector with enough space for all of them. + let mut patterns = match iter.size_hint() { + (_, Some(count)) => Vec::with_capacity(count), + _ => Vec::new(), + }; + + // Similarly, assume there won’t be any errors. + let mut errors = Vec::new(); + + for input in iter { + match glob::Pattern::new(input) { + Ok(pat) => patterns.push(pat), + Err(e) => errors.push(e), + } + } + + (Self { patterns }, errors) + } + + /// Create a new empty set of patterns that matches nothing. + pub fn empty() -> Self { + Self { + patterns: Vec::new(), + } + } + + /// Test whether the given file should be hidden from the results. + fn is_ignored(&self, file: &str) -> bool { + self.patterns.iter().any(|p| p.matches(file)) + } +} + +/// Whether to ignore or display files that Git would ignore. +#[derive(PartialEq, Eq, Debug, Copy, Clone)] +pub enum GitIgnore { + /// Ignore files that Git would ignore. + CheckAndIgnore, + + /// Display files, even if Git would ignore them. + Off, +} + +#[cfg(test)] +mod test_ignores { + use super::*; + + #[test] + fn empty_matches_nothing() { + let pats = IgnorePatterns::empty(); + assert!(!pats.is_ignored("nothing")); + assert!(!pats.is_ignored("test.mp3")); + } + + #[test] + fn ignores_a_glob() { + let (pats, fails) = IgnorePatterns::parse_from_iter(vec!["*.mp3"]); + assert!(fails.is_empty()); + assert!(!pats.is_ignored("nothing")); + assert!(pats.is_ignored("test.mp3")); + } + + #[test] + fn ignores_an_exact_filename() { + let (pats, fails) = IgnorePatterns::parse_from_iter(vec!["nothing"]); + assert!(fails.is_empty()); + assert!(pats.is_ignored("nothing")); + assert!(!pats.is_ignored("test.mp3")); + } + + #[test] + fn ignores_both() { + let (pats, fails) = IgnorePatterns::parse_from_iter(vec!["nothing", "*.mp3"]); + assert!(fails.is_empty()); + assert!(pats.is_ignored("nothing")); + assert!(pats.is_ignored("test.mp3")); + } +} diff --git a/collector/compile-benchmarks/eza-0.21.2/src/fs/mod.rs b/collector/compile-benchmarks/eza-0.21.2/src/fs/mod.rs new file mode 100644 index 000000000..495340955 --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/src/fs/mod.rs @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2024 Christina Sørensen +// SPDX-License-Identifier: EUPL-1.2 +// +// SPDX-FileCopyrightText: 2023-2024 Christina Sørensen, eza contributors +// SPDX-FileCopyrightText: 2014 Benjamin Sago +// SPDX-License-Identifier: MIT +mod dir; +pub use self::dir::{Dir, DotFilter}; + +mod file; +pub use self::file::{File, FileTarget}; + +pub mod dir_action; +pub mod feature; +pub mod fields; +pub mod filter; +pub mod mounts; +pub mod recursive_size; diff --git a/collector/compile-benchmarks/eza-0.21.2/src/fs/mounts/linux.rs b/collector/compile-benchmarks/eza-0.21.2/src/fs/mounts/linux.rs new file mode 100644 index 000000000..39b86e892 --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/src/fs/mounts/linux.rs @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: 2024 Christina Sørensen +// SPDX-License-Identifier: EUPL-1.2 +// +// SPDX-FileCopyrightText: 2023-2024 Christina Sørensen, eza contributors +// SPDX-FileCopyrightText: 2014 Benjamin Sago +// SPDX-License-Identifier: MIT +use crate::fs::mounts::{Error, MountedFs}; +use proc_mounts::MountList; + +/// Get a list of all mounted filesystems +pub fn mounts() -> Result, Error> { + Ok(MountList::new() + .map_err(Error::IOError)? + .0 + .iter() + .map(|mount| MountedFs { + dest: mount.dest.clone(), + fstype: mount.fstype.clone(), + source: mount.source.to_string_lossy().into(), + }) + .collect()) +} diff --git a/collector/compile-benchmarks/eza-0.21.2/src/fs/mounts/macos.rs b/collector/compile-benchmarks/eza-0.21.2/src/fs/mounts/macos.rs new file mode 100644 index 000000000..6514c6949 --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/src/fs/mounts/macos.rs @@ -0,0 +1,64 @@ +// SPDX-FileCopyrightText: 2024 Christina Sørensen +// SPDX-License-Identifier: EUPL-1.2 +// +// SPDX-FileCopyrightText: 2023-2024 Christina Sørensen, eza contributors +// SPDX-FileCopyrightText: 2014 Benjamin Sago +// SPDX-License-Identifier: MIT +use crate::fs::mounts::{Error, MountedFs}; +use libc::{__error, getfsstat, statfs, MNT_NOWAIT}; +use std::ffi::{CStr, OsStr}; +use std::os::raw::{c_char, c_int}; +use std::os::unix::ffi::OsStrExt; +use std::path::PathBuf; +use std::{mem, ptr}; + +/// Get a list of all mounted filesystem +pub fn mounts() -> Result, Error> { + // SAFETY: + // Calling external "C" function getfsstat. Passing a null pointer and zero + // bufsize will return the number of mounts. + let mut count: i32 = unsafe { getfsstat(ptr::null_mut(), 0, MNT_NOWAIT) }; + let mut mntbuf = Vec::::new(); + if count > 0 { + // SAFETY: Zero out buffer memory as we allocate. + mntbuf.resize_with(count as usize, || unsafe { mem::zeroed() }); + let bufsize = mntbuf.len() * mem::size_of::(); + // SAFETY: + // Calling external "C" function getfsstate with actual buffer now. The + // function takes a buffer size to not overflow. If the mount table + // changes size between calls we are protected by bufsize + count = unsafe { getfsstat(mntbuf.as_mut_ptr(), bufsize as c_int, MNT_NOWAIT) }; + // Resize if the mount table has shrunk since last call + if count >= 0 { + mntbuf.truncate(count as usize); + } + } + if count < 0 { + // SAFETY: Calling external "C" errno function to get the error number + return Err(Error::GetFSStatError(unsafe { *__error() })); + } + + let mut mounts = Vec::with_capacity(count as usize); + for mnt in &mntbuf { + let mount_point = OsStr::from_bytes( + // SAFETY: Converting null terminated "C" string + unsafe { CStr::from_ptr(mnt.f_mntonname.as_ptr().cast::()) }.to_bytes(), + ); + let dest = PathBuf::from(mount_point); + // SAFETY: Converting null terminated "C" string + let fstype = unsafe { CStr::from_ptr(mnt.f_fstypename.as_ptr().cast::()) } + .to_string_lossy() + .into(); + // SAFETY: Converting null terminated "C" string + let source = unsafe { CStr::from_ptr(mnt.f_mntfromname.as_ptr().cast::()) } + .to_string_lossy() + .into(); + mounts.push(MountedFs { + dest, + fstype, + source, + }); + } + + Ok(mounts) +} diff --git a/collector/compile-benchmarks/eza-0.21.2/src/fs/mounts/mod.rs b/collector/compile-benchmarks/eza-0.21.2/src/fs/mounts/mod.rs new file mode 100644 index 000000000..39d9740ac --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/src/fs/mounts/mod.rs @@ -0,0 +1,81 @@ +// SPDX-FileCopyrightText: 2024 Christina Sørensen +// SPDX-License-Identifier: EUPL-1.2 +// +// SPDX-FileCopyrightText: 2023-2024 Christina Sørensen, eza contributors +// SPDX-FileCopyrightText: 2014 Benjamin Sago +// SPDX-License-Identifier: MIT +use std::collections::HashMap; +use std::path::PathBuf; +use std::sync::OnceLock; + +#[cfg(target_os = "linux")] +mod linux; +#[cfg(target_os = "macos")] +mod macos; + +#[cfg(target_os = "linux")] +use linux::mounts; +#[cfg(target_os = "macos")] +use macos::mounts; + +/// Details of a mounted filesystem. +#[derive(Clone)] +pub struct MountedFs { + pub dest: PathBuf, + pub fstype: String, + pub source: String, +} + +#[derive(Debug)] +#[non_exhaustive] +pub enum Error { + #[cfg(target_os = "macos")] + GetFSStatError(i32), + #[cfg(target_os = "linux")] + IOError(std::io::Error), +} + +impl std::error::Error for Error {} + +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + // Allow unreachable_patterns for windows build + #[allow(unreachable_patterns)] + match self { + #[cfg(target_os = "macos")] + Error::GetFSStatError(err) => write!(f, "getfsstat failed: {err}"), + #[cfg(target_os = "linux")] + Error::IOError(err) => write!(f, "failed to read /proc/mounts: {err}"), + _ => write!(f, "Unknown error"), + } + } +} + +// A lazily initialised static map of all mounted file systems. +// +// The map contains a mapping from the mounted directory path to the +// corresponding mount information. If there's an error retrieving the mount +// list or if we're not running on Linux or Mac, the map will be empty. +// +// Initialise this at application start so we don't have to look the details +// up for every directory. Ideally this would only be done if the --mounts +// option is specified which will be significantly easier once the move +// to `clap` is complete. +pub(super) fn all_mounts() -> &'static HashMap { + static ALL_MOUNTS: OnceLock> = OnceLock::new(); + + ALL_MOUNTS.get_or_init(|| { + // Allow unused_mut for windows build + #[allow(unused_mut)] + let mut mount_map: HashMap = HashMap::new(); + + #[cfg(any(target_os = "linux", target_os = "macos"))] + if let Ok(mounts) = mounts() { + for mount in mounts { + mount_map.insert(mount.dest.clone(), mount); + } + } + + mount_map + }) +} diff --git a/collector/compile-benchmarks/eza-0.21.2/src/fs/recursive_size.rs b/collector/compile-benchmarks/eza-0.21.2/src/fs/recursive_size.rs new file mode 100644 index 000000000..fbf454989 --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/src/fs/recursive_size.rs @@ -0,0 +1,86 @@ +// SPDX-FileCopyrightText: 2024 Christina Sørensen +// SPDX-License-Identifier: EUPL-1.2 +// +// SPDX-FileCopyrightText: 2023-2024 Christina Sørensen, eza contributors +// SPDX-FileCopyrightText: 2014 Benjamin Sago +// SPDX-License-Identifier: MIT +/// Used to represent a the size of a recursive directory traversal. `None` +/// should be used when the file does not represent a directory or the recursive +/// size should not be calculated. +#[derive(Copy, Clone, Debug)] +pub enum RecursiveSize { + /// Size should not be computed + None, + /// Size should be computed but has not been computed yet + Unknown, + /// Size has been computed. First field is size in bytes and second field + /// is size in blocks + #[cfg_attr(target_family = "windows", allow(dead_code))] + Some(u64, u64), +} + +impl RecursiveSize { + /// Returns `true` if `None` + /// + /// # Examples + /// + /// ``` + /// use eza::fs::recursive_size::RecursiveSize; + /// + /// let x = RecursiveSize::None; + /// assert_eq!(x.is_none(), true); + /// + /// let x = RecursiveSize::Unknown; + /// assert_eq!(x.is_none(), false); + /// + /// let x = RecursiveSize::Some(0, 0); + /// assert_eq!(x.is_none(), false); + /// ``` + #[inline] + pub const fn is_none(&self) -> bool { + matches!(*self, Self::None) + } + + /// Returns the contained [`Some`] value or a provided default. + /// + /// # Examples + /// + /// ``` + /// use eza::fs::recursive_size::RecursiveSize; + /// + /// assert_eq!(RecursiveSize::None.unwrap_bytes_or(1), 1); + /// assert_eq!(RecursiveSize::Unknown.unwrap_bytes_or(1), 1); + /// assert_eq!(RecursiveSize::Some(2, 3).unwrap_bytes_or(1), 2); + /// ``` + #[inline] + pub const fn unwrap_bytes_or(self, default: u64) -> u64 { + match self { + Self::Some(bytes, _blocks) => bytes, + _ => default, + } + } + + /// Returns the provided default result (if None or Unknown), + /// or applies a function to the contained value (if Some). + /// + /// # Examples + /// + /// ``` + /// use eza::fs::recursive_size::RecursiveSize; + /// + /// assert_eq!(RecursiveSize::None.map_or(None, |s, _| Some(s * 2)), None); + /// assert_eq!(RecursiveSize::Unknown.map_or(None, |s, _| Some(s * 2)), None); + /// assert_eq!(RecursiveSize::Some(2, 3).map_or(None, |s, _| Some(s * 2)), Some(4)); + /// ``` + #[inline] + #[cfg_attr(target_family = "windows", allow(dead_code))] + pub fn map_or(self, default: U, f: F) -> U + where + F: FnOnce(u64, u64) -> U, + { + match self { + RecursiveSize::Some(bytes, blocks) => f(bytes, blocks), + _ => default, + } + } +} diff --git a/collector/compile-benchmarks/eza-0.21.2/src/info/filetype.rs b/collector/compile-benchmarks/eza-0.21.2/src/info/filetype.rs new file mode 100644 index 000000000..e908c8e8b --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/src/info/filetype.rs @@ -0,0 +1,443 @@ +// SPDX-FileCopyrightText: 2024 Christina Sørensen +// SPDX-License-Identifier: EUPL-1.2 +// +// SPDX-FileCopyrightText: 2023-2024 Christina Sørensen, eza contributors +// SPDX-FileCopyrightText: 2014 Benjamin Sago +// SPDX-License-Identifier: MIT +//! Tests for various types of file (video, image, compressed, etc). +//! +//! Currently this is dependent on the file’s name and extension, because +//! those are the only metadata that we have access to without reading the +//! file’s contents. +//! +//! # Contributors +//! Please keep these lists sorted. If you're using vim, :sort i + +use phf::{phf_map, Map}; + +use crate::fs::File; + +#[derive(Debug, Clone)] +pub enum FileType { + Image, + Video, + Music, + Lossless, // Lossless music, rather than any other kind of data... + Crypto, + Document, + Compressed, + Temp, + Compiled, + Build, // A “build file is something that can be run or activated somehow in order to + // kick off the build of a project. It’s usually only present in directories full of + // source code. + Source, +} + +/// Mapping from full filenames to file type. +const FILENAME_TYPES: Map<&'static str, FileType> = phf_map! { + /* Immediate file - kick off the build of a project */ + "Brewfile" => FileType::Build, + "bsconfig.json" => FileType::Build, + "BUILD" => FileType::Build, + "BUILD.bazel" => FileType::Build, + "build.gradle" => FileType::Build, + "build.sbt" => FileType::Build, + "build.xml" => FileType::Build, + "Cargo.toml" => FileType::Build, + "CMakeLists.txt" => FileType::Build, + "composer.json" => FileType::Build, + "configure" => FileType::Build, + "Containerfile" => FileType::Build, + "Dockerfile" => FileType::Build, + "Earthfile" => FileType::Build, + "flake.nix" => FileType::Build, + "Gemfile" => FileType::Build, + "GNUmakefile" => FileType::Build, + "Gruntfile.coffee" => FileType::Build, + "Gruntfile.js" => FileType::Build, + "jsconfig.json" => FileType::Build, + "Justfile" => FileType::Build, + "justfile" => FileType::Build, + "Makefile" => FileType::Build, + "makefile" => FileType::Build, + "meson.build" => FileType::Build, + "mix.exs" => FileType::Build, + "package.json" => FileType::Build, + "Pipfile" => FileType::Build, + "PKGBUILD" => FileType::Build, + "Podfile" => FileType::Build, + "pom.xml" => FileType::Build, + "Procfile" => FileType::Build, + "pyproject.toml" => FileType::Build, + "Rakefile" => FileType::Build, + "RoboFile.php" => FileType::Build, + "SConstruct" => FileType::Build, + "tsconfig.json" => FileType::Build, + "Vagrantfile" => FileType::Build, + "webpack.config.cjs" => FileType::Build, + "webpack.config.js" => FileType::Build, + "WORKSPACE" => FileType::Build, + /* Cryptology files */ + "id_dsa" => FileType::Crypto, + "id_ecdsa" => FileType::Crypto, + "id_ecdsa_sk" => FileType::Crypto, + "id_ed25519" => FileType::Crypto, + "id_ed25519_sk" => FileType::Crypto, + "id_rsa" => FileType::Crypto, +}; + +/// Mapping from lowercase file extension to file type. If an image, video, music, or lossless +/// extension is added also update the extension icon map. +const EXTENSION_TYPES: Map<&'static str, FileType> = phf_map! { + /* Immediate file - kick off the build of a project */ + "ninja" => FileType::Build, + /* Image files */ + "arw" => FileType::Image, + "avif" => FileType::Image, + "bmp" => FileType::Image, + "cbr" => FileType::Image, + "cbz" => FileType::Image, + "cr2" => FileType::Image, + "dvi" => FileType::Image, + "eps" => FileType::Image, + "fodg" => FileType::Image, + "gif" => FileType::Image, + "heic" => FileType::Image, + "heif" => FileType::Image, + "ico" => FileType::Image, + "j2c" => FileType::Image, + "j2k" => FileType::Image, + "jfi" => FileType::Image, + "jfif" => FileType::Image, + "jif" => FileType::Image, + "jp2" => FileType::Image, + "jpe" => FileType::Image, + "jpeg" => FileType::Image, + "jpf" => FileType::Image, + "jpg" => FileType::Image, + "jpx" => FileType::Image, + "jxl" => FileType::Image, + "kra" => FileType::Image, + "krz" => FileType::Image, + "nef" => FileType::Image, + "odg" => FileType::Image, + "orf" => FileType::Image, + "pbm" => FileType::Image, + "pgm" => FileType::Image, + "png" => FileType::Image, + "pnm" => FileType::Image, + "ppm" => FileType::Image, + "ps" => FileType::Image, + "psd" => FileType::Image, + "pxm" => FileType::Image, + "raw" => FileType::Image, + "qoi" => FileType::Image, + "svg" => FileType::Image, + "tif" => FileType::Image, + "tiff" => FileType::Image, + "webp" => FileType::Image, + "xcf" => FileType::Image, + "xpm" => FileType::Image, + /* Video files */ + "avi" => FileType::Video, + "flv" => FileType::Video, + "h264" => FileType::Video, + "heics" => FileType::Video, + "m2ts" => FileType::Video, + "m2v" => FileType::Video, + "m4v" => FileType::Video, + "mkv" => FileType::Video, + "mov" => FileType::Video, + "mp4" => FileType::Video, + "mpeg" => FileType::Video, + "mpg" => FileType::Video, + "ogm" => FileType::Video, + "ogv" => FileType::Video, + "video" => FileType::Video, + "vob" => FileType::Video, + "webm" => FileType::Video, + "wmv" => FileType::Video, + /* Music files */ + "aac" => FileType::Music, // Advanced Audio Coding + "m4a" => FileType::Music, + "mka" => FileType::Music, + "mp2" => FileType::Music, + "mp3" => FileType::Music, + "ogg" => FileType::Music, + "opus" => FileType::Music, + "wma" => FileType::Music, + /* Lossless music, rather than any other kind of data... */ + "aif" => FileType::Lossless, + "aifc" => FileType::Lossless, + "aiff" => FileType::Lossless, + "alac" => FileType::Lossless, + "ape" => FileType::Lossless, + "flac" => FileType::Lossless, + "pcm" => FileType::Lossless, + "wav" => FileType::Lossless, + "wv" => FileType::Lossless, + /* Cryptology files */ + "age" => FileType::Crypto, // age encrypted file + "asc" => FileType::Crypto, // GnuPG ASCII armored file + "cer" => FileType::Crypto, + "crt" => FileType::Crypto, + "csr" => FileType::Crypto, // PKCS#10 Certificate Signing Request + "gpg" => FileType::Crypto, // GnuPG encrypted file + "kbx" => FileType::Crypto, // GnuPG keybox + "md5" => FileType::Crypto, // MD5 checksum + "p12" => FileType::Crypto, // PKCS#12 certificate (Netscape) + "pem" => FileType::Crypto, // Privacy-Enhanced Mail certificate + "pfx" => FileType::Crypto, // PKCS#12 certificate (Microsoft) + "pgp" => FileType::Crypto, // PGP security key + "pub" => FileType::Crypto, // Public key + "sha1" => FileType::Crypto, // SHA-1 hash + "sha224" => FileType::Crypto, // SHA-224 hash + "sha256" => FileType::Crypto, // SHA-256 hash + "sha384" => FileType::Crypto, // SHA-384 hash + "sha512" => FileType::Crypto, // SHA-512 hash + "sig" => FileType::Crypto, // GnuPG signed file + "signature" => FileType::Crypto, // e-Filing Digital Signature File (India) + /* Document files */ + "djvu" => FileType::Document, + "doc" => FileType::Document, + "docx" => FileType::Document, + "eml" => FileType::Document, + "fodp" => FileType::Document, + "fods" => FileType::Document, + "fodt" => FileType::Document, + "fotd" => FileType::Document, + "gdoc" => FileType::Document, + "key" => FileType::Document, + "keynote" => FileType::Document, + "numbers" => FileType::Document, + "odp" => FileType::Document, + "ods" => FileType::Document, + "odt" => FileType::Document, + "pages" => FileType::Document, + "pdf" => FileType::Document, + "ppt" => FileType::Document, + "pptx" => FileType::Document, + "rtf" => FileType::Document, // Rich Text Format + "xls" => FileType::Document, + "xlsm" => FileType::Document, + "xlsx" => FileType::Document, + /* Compressed/archive files */ + "7z" => FileType::Compressed, // 7-Zip + "ar" => FileType::Compressed, + "arj" => FileType::Compressed, + "br" => FileType::Compressed, // Brotli + "bz" => FileType::Compressed, // bzip + "bz2" => FileType::Compressed, // bzip2 + "bz3" => FileType::Compressed, // bzip3 + "cpio" => FileType::Compressed, + "deb" => FileType::Compressed, // Debian + "dmg" => FileType::Compressed, + "gz" => FileType::Compressed, // gzip + "iso" => FileType::Compressed, + "lz" => FileType::Compressed, + "lz4" => FileType::Compressed, + "lzh" => FileType::Compressed, + "lzma" => FileType::Compressed, + "lzo" => FileType::Compressed, + "phar" => FileType::Compressed, // PHP PHAR + "qcow" => FileType::Compressed, + "qcow2" => FileType::Compressed, + "rar" => FileType::Compressed, + "rpm" => FileType::Compressed, + "tar" => FileType::Compressed, + "taz" => FileType::Compressed, + "tbz" => FileType::Compressed, + "tbz2" => FileType::Compressed, + "tc" => FileType::Compressed, + "tgz" => FileType::Compressed, + "tlz" => FileType::Compressed, + "txz" => FileType::Compressed, + "tz" => FileType::Compressed, + "xz" => FileType::Compressed, + "vdi" => FileType::Compressed, + "vhd" => FileType::Compressed, + "vhdx" => FileType::Compressed, + "vmdk" => FileType::Compressed, + "z" => FileType::Compressed, + "zip" => FileType::Compressed, + "zst" => FileType::Compressed, // Zstandard + /* Temporary files */ + "bak" => FileType::Temp, + "bk" => FileType::Temp, + "bkp" => FileType::Temp, + "crdownload" => FileType::Temp, + "download" => FileType::Temp, + "fcbak" => FileType::Temp, + "fcstd1" => FileType::Temp, + "fdmdownload"=> FileType::Temp, + "part" => FileType::Temp, + "swn" => FileType::Temp, + "swo" => FileType::Temp, + "swp" => FileType::Temp, + "tmp" => FileType::Temp, + /* Compiler output files */ + "a" => FileType::Compiled, // Unix static library + "bundle" => FileType::Compiled, // macOS application bundle + "class" => FileType::Compiled, // Java class file + "cma" => FileType::Compiled, // OCaml bytecode library + "cmi" => FileType::Compiled, // OCaml interface + "cmo" => FileType::Compiled, // OCaml bytecode object + "cmx" => FileType::Compiled, // OCaml bytecode object for inlining + "dll" => FileType::Compiled, // Windows dynamic link library + "dylib" => FileType::Compiled, // Mach-O dynamic library + "elc" => FileType::Compiled, // Emacs compiled lisp + "elf" => FileType::Compiled, // Executable and Linkable Format + "ko" => FileType::Compiled, // Linux kernel module + "lib" => FileType::Compiled, // Windows static library + "o" => FileType::Compiled, // Compiled object file + "obj" => FileType::Compiled, // Compiled object file + "pyc" => FileType::Compiled, // Python compiled code + "pyd" => FileType::Compiled, // Python dynamic module + "pyo" => FileType::Compiled, // Python optimized code + "so" => FileType::Compiled, // Unix shared library + "zwc" => FileType::Compiled, // zsh compiled file + /* Source code files */ + "applescript"=> FileType::Source, // Apple script + "as" => FileType::Source, // Action script + "asa" => FileType::Source, // asp + "awk" => FileType::Source, // awk + "c" => FileType::Source, // C/C++ + "c++" => FileType::Source, // C/C++ + "c++m" => FileType::Source, // C/C++ module + "cabal" => FileType::Source, // Cabal + "cc" => FileType::Source, // C/C++ + "ccm" => FileType::Source, // C/C++ module + "clj" => FileType::Source, // Clojure + "cp" => FileType::Source, // C/C++ Xcode + "cpp" => FileType::Source, // C/C++ + "cppm" => FileType::Source, // C/C++ module + "cr" => FileType::Source, // Crystal + "cs" => FileType::Source, // C# + "css" => FileType::Source, // css + "csx" => FileType::Source, // C# + "cu" => FileType::Source, // CUDA + "cxx" => FileType::Source, // C/C++ + "cxxm" => FileType::Source, // C/C++ module + "cypher" => FileType::Source, // Cypher (query language) + "d" => FileType::Source, // D + "dart" => FileType::Source, // Dart + "di" => FileType::Source, // D + "dpr" => FileType::Source, // Delphi Pascal + "el" => FileType::Source, // Lisp + "elm" => FileType::Source, // Elm + "erl" => FileType::Source, // Erlang + "ex" => FileType::Source, // Elixir + "exs" => FileType::Source, // Elixir + "f" => FileType::Source, // Fortran + "f90" => FileType::Source, // Fortran + "fcmacro" => FileType::Source, // FreeCAD macro + "fcscript" => FileType::Source, // FreeCAD script + "fnl" => FileType::Source, // Fennel + "for" => FileType::Source, // Fortran + "fs" => FileType::Source, // F# + "fsh" => FileType::Source, // Fragment shader + "fsi" => FileType::Source, // F# + "fsx" => FileType::Source, // F# + "gd" => FileType::Source, // GDScript + "go" => FileType::Source, // Go + "gradle" => FileType::Source, // Gradle + "groovy" => FileType::Source, // Groovy + "gvy" => FileType::Source, // Groovy + "h" => FileType::Source, // C/C++ header + "h++" => FileType::Source, // C/C++ header + "hh" => FileType::Source, // C/C++ header + "hpp" => FileType::Source, // C/C++ header + "hc" => FileType::Source, // HolyC + "hs" => FileType::Source, // Haskell + "htc" => FileType::Source, // JavaScript + "hxx" => FileType::Source, // C/C++ header + "inc" => FileType::Source, + "inl" => FileType::Source, // C/C++ Microsoft + "ino" => FileType::Source, // Arduino + "ipynb" => FileType::Source, // Jupyter Notebook + "ixx" => FileType::Source, // C/C++ module + "java" => FileType::Source, // Java + "jl" => FileType::Source, // Julia + "js" => FileType::Source, // JavaScript + "jsx" => FileType::Source, // React + "kt" => FileType::Source, // Kotlin + "kts" => FileType::Source, // Kotlin + "kusto" => FileType::Source, // Kusto (query language) + "less" => FileType::Source, // less + "lhs" => FileType::Source, // Haskell + "lisp" => FileType::Source, // Lisp + "ltx" => FileType::Source, // LaTeX + "lua" => FileType::Source, // Lua + "m" => FileType::Source, // Matlab + "malloy" => FileType::Source, // Malloy (query language) + "matlab" => FileType::Source, // Matlab + "ml" => FileType::Source, // OCaml + "mli" => FileType::Source, // OCaml + "mn" => FileType::Source, // Matlab + "nb" => FileType::Source, // Mathematica + "p" => FileType::Source, // Pascal + "pas" => FileType::Source, // Pascal + "php" => FileType::Source, // PHP + "pl" => FileType::Source, // Perl + "pm" => FileType::Source, // Perl + "pod" => FileType::Source, // Perl + "pp" => FileType::Source, // Puppet + "prql" => FileType::Source, // PRQL + "ps1" => FileType::Source, // PowerShell + "psd1" => FileType::Source, // PowerShell + "psm1" => FileType::Source, // PowerShell + "purs" => FileType::Source, // PureScript + "py" => FileType::Source, // Python + "r" => FileType::Source, // R + "rb" => FileType::Source, // Ruby + "rs" => FileType::Source, // Rust + "rq" => FileType::Source, // SPARQL (query language) + "sass" => FileType::Source, // Sass + "scala" => FileType::Source, // Scala + "scm" => FileType::Source, // Scheme + "scad" => FileType::Source, // OpenSCAD + "scss" => FileType::Source, // Sass + "sld" => FileType::Source, // Scheme Library Definition + "sql" => FileType::Source, // SQL + "ss" => FileType::Source, // Scheme Source + "swift" => FileType::Source, // Swift + "tcl" => FileType::Source, // TCL + "tex" => FileType::Source, // LaTeX + "ts" => FileType::Source, // TypeScript + "v" => FileType::Source, // V + "vb" => FileType::Source, // Visual Basic + "vsh" => FileType::Source, // Vertex shader + "zig" => FileType::Source, // Zig +}; + +impl FileType { + /// Lookup the file type based on the file's name, by the file name + /// lowercase extension, or if the file could be compiled from related + /// source code. + pub(crate) fn get_file_type(file: &File<'_>) -> Option { + // Case-insensitive readme is checked first for backwards compatibility. + if file.name.to_lowercase().starts_with("readme") { + return Some(Self::Build); + } + if let Some(file_type) = FILENAME_TYPES.get(&file.name) { + return Some(file_type.clone()); + } + if let Some(file_type) = file.ext.as_ref().and_then(|ext| EXTENSION_TYPES.get(ext)) { + return Some(file_type.clone()); + } + if file.name.ends_with('~') || (file.name.starts_with('#') && file.name.ends_with('#')) { + return Some(Self::Temp); + } + if let Some(dir) = file.parent_dir { + if file + .get_source_files() + .iter() + .any(|path| dir.contains(path)) + { + return Some(Self::Compiled); + } + } + None + } +} diff --git a/collector/compile-benchmarks/eza-0.21.2/src/info/mod.rs b/collector/compile-benchmarks/eza-0.21.2/src/info/mod.rs new file mode 100644 index 000000000..656e84f01 --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/src/info/mod.rs @@ -0,0 +1,13 @@ +// SPDX-FileCopyrightText: 2024 Christina Sørensen +// SPDX-License-Identifier: EUPL-1.2 +// +// SPDX-FileCopyrightText: 2023-2024 Christina Sørensen, eza contributors +// SPDX-FileCopyrightText: 2014 Benjamin Sago +// SPDX-License-Identifier: MIT +//! The “info” module contains routines that aren’t about probing the +//! filesystem nor displaying output to the user, but are internal “business +//! logic” routines that are performed on a file’s already-read metadata. +//! (This counts the file name as metadata.) + +pub mod filetype; +mod sources; diff --git a/collector/compile-benchmarks/eza-0.21.2/src/info/sources.rs b/collector/compile-benchmarks/eza-0.21.2/src/info/sources.rs new file mode 100644 index 000000000..4e93a8db2 --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/src/info/sources.rs @@ -0,0 +1,48 @@ +// SPDX-FileCopyrightText: 2024 Christina Sørensen +// SPDX-License-Identifier: EUPL-1.2 +// +// SPDX-FileCopyrightText: 2023-2024 Christina Sørensen, eza contributors +// SPDX-FileCopyrightText: 2014 Benjamin Sago +// SPDX-License-Identifier: MIT +use std::path::PathBuf; + +use crate::fs::File; + +impl<'a> File<'a> { + /// For this file, return a vector of alternate file paths that, if any of + /// them exist, mean that *this* file should be coloured as “compiled”. + /// + /// The point of this is to highlight compiled files such as `foo.js` when + /// their source file `foo.coffee` exists in the same directory. + /// For example, `foo.js` is perfectly valid without `foo.coffee`, so we + /// don’t want to always blindly highlight `*.js` as compiled. + /// (See also `FileType`) + pub fn get_source_files(&self) -> Vec { + if let Some(ext) = &self.ext { + match &ext[..] { + "css" => vec![self.path.with_extension("sass"), self.path.with_extension("scss"), // SASS, SCSS + self.path.with_extension("styl"), self.path.with_extension("less")], // Stylus, Less + "mjs" => vec![self.path.with_extension("mts")], // JavaScript ES Modules source + "cjs" => vec![self.path.with_extension("cts")], // JavaScript Commonjs Modules source + "js" => vec![self.path.with_extension("coffee"), self.path.with_extension("ts")], // CoffeeScript, TypeScript + "aux" | // TeX: auxiliary file + "bbl" | // BibTeX bibliography file + "bcf" | // biblatex control file + "blg" | // BibTeX log file + "fdb_latexmk" | // TeX latexmk file + "fls" | // TeX -recorder file + "headfootlength" | // TeX package autofancyhdr file + "lof" | // TeX list of figures + "log" | // TeX log file + "lot" | // TeX list of tables + "out" | // hyperref list of bookmarks + "toc" | // TeX table of contents + "xdv" => vec![self.path.with_extension("tex")], // XeTeX dvi + + _ => vec![], // No source files if none of the above + } + } else { + vec![] // No source files if there’s no extension, either! + } + } +} diff --git a/collector/compile-benchmarks/eza-0.21.2/src/logger.rs b/collector/compile-benchmarks/eza-0.21.2/src/logger.rs new file mode 100644 index 000000000..351890ef8 --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/src/logger.rs @@ -0,0 +1,74 @@ +// SPDX-FileCopyrightText: 2024 Christina Sørensen +// SPDX-License-Identifier: EUPL-1.2 +// +// SPDX-FileCopyrightText: 2023-2024 Christina Sørensen, eza contributors +// SPDX-FileCopyrightText: 2014 Benjamin Sago +// SPDX-License-Identifier: MIT +//! Debug error logging. + +use std::ffi::OsStr; + +use nu_ansi_term::{AnsiString as ANSIString, Color as Colour}; + +/// Sets the internal logger, changing the log level based on the value of an +/// environment variable. +pub fn configure>(ev: Option) { + let Some(ev) = ev else { return }; + + let env_var = ev.as_ref(); + if env_var.is_empty() { + return; + } + + if env_var == "trace" { + log::set_max_level(log::LevelFilter::Trace); + } else { + log::set_max_level(log::LevelFilter::Debug); + } + + let result = log::set_logger(GLOBAL_LOGGER); + if let Err(e) = result { + eprintln!("Failed to initialize logger: {e}"); + } +} + +#[derive(Debug)] +struct Logger; + +const GLOBAL_LOGGER: &Logger = &Logger; + +impl log::Log for Logger { + fn enabled(&self, _: &log::Metadata<'_>) -> bool { + true // no need to filter after using ‘set_max_level’. + } + + fn log(&self, record: &log::Record<'_>) { + let open = Colour::Fixed(243).paint("["); + let level = level(record.level()); + let close = Colour::Fixed(243).paint("]"); + + eprintln!( + "{}{} {}{} {}", + open, + level, + record.target(), + close, + record.args() + ); + } + + fn flush(&self) { + // no need to flush with ‘eprintln!’. + } +} + +fn level(level: log::Level) -> ANSIString<'static> { + #[rustfmt::skip] + return match level { + log::Level::Error => Colour::Red.paint("ERROR"), + log::Level::Warn => Colour::Yellow.paint("WARN"), + log::Level::Info => Colour::Cyan.paint("INFO"), + log::Level::Debug => Colour::Blue.paint("DEBUG"), + log::Level::Trace => Colour::Fixed(245).paint("TRACE"), + }; +} diff --git a/collector/compile-benchmarks/eza-0.21.2/src/main.rs b/collector/compile-benchmarks/eza-0.21.2/src/main.rs new file mode 100644 index 000000000..a235180e7 --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/src/main.rs @@ -0,0 +1,528 @@ +// SPDX-FileCopyrightText: 2024 Christina Sørensen +// SPDX-License-Identifier: EUPL-1.2 +// +// SPDX-FileCopyrightText: 2023-2024 Christina Sørensen, eza contributors +// SPDX-FileCopyrightText: 2014 Benjamin Sago +// SPDX-License-Identifier: MIT +#![warn(deprecated_in_future)] +#![warn(future_incompatible)] +#![warn(nonstandard_style)] +#![warn(rust_2018_compatibility)] +#![warn(rust_2018_idioms)] +#![warn(trivial_casts, trivial_numeric_casts)] +#![warn(unused)] +#![warn(clippy::all, clippy::pedantic)] +#![allow(clippy::cast_precision_loss)] +#![allow(clippy::cast_possible_truncation)] +#![allow(clippy::cast_possible_wrap)] +#![allow(clippy::cast_sign_loss)] +#![allow(clippy::enum_glob_use)] +#![allow(clippy::map_unwrap_or)] +#![allow(clippy::match_same_arms)] +#![allow(clippy::module_name_repetitions)] +#![allow(clippy::non_ascii_literal)] +#![allow(clippy::option_if_let_else)] +#![allow(clippy::too_many_lines)] +#![allow(clippy::unused_self)] +#![allow(clippy::upper_case_acronyms)] +#![allow(clippy::wildcard_imports)] + +use std::env; +use std::ffi::{OsStr, OsString}; +use std::io::{self, stdin, ErrorKind, IsTerminal, Read, Write}; +use std::path::{Component, PathBuf}; +use std::process::exit; + +use nu_ansi_term::{AnsiStrings as ANSIStrings, Style}; + +use crate::fs::feature::git::GitCache; +use crate::fs::filter::{FileFilterFlags::OnlyFiles, GitIgnore}; +use crate::fs::{Dir, File}; +use crate::options::stdin::FilesInput; +use crate::options::{vars, Options, OptionsResult, Vars}; +use crate::output::{details, escape, file_name, grid, grid_details, lines, Mode, View}; +use crate::theme::Theme; +use log::*; + +mod fs; +mod info; +mod logger; +mod options; +mod output; +mod theme; + +fn main() { + #[cfg(unix)] + unsafe { + libc::signal(libc::SIGPIPE, libc::SIG_DFL); + } + + logger::configure(env::var_os(vars::EZA_DEBUG).or_else(|| env::var_os(vars::EXA_DEBUG))); + + let stdout_istty = io::stdout().is_terminal(); + + let mut input = String::new(); + let args: Vec<_> = env::args_os().skip(1).collect(); + match Options::parse(args.iter().map(std::convert::AsRef::as_ref), &LiveVars) { + OptionsResult::Ok(options, mut input_paths) => { + // List the current directory by default. + // (This has to be done here, otherwise git_options won’t see it.) + if input_paths.is_empty() { + match &options.stdin { + FilesInput::Args => { + input_paths = vec![OsStr::new(".")]; + } + FilesInput::Stdin(separator) => { + stdin() + .read_to_string(&mut input) + .expect("Failed to read from stdin"); + input_paths.extend( + input + .split(&separator.clone().into_string().unwrap_or("\n".to_string())) + .map(std::ffi::OsStr::new) + .filter(|s| !s.is_empty()) + .collect::>(), + ); + } + } + } + + let git = git_options(&options, &input_paths); + let writer = io::stdout(); + let git_repos = git_repos(&options, &input_paths); + + let console_width = options.view.width.actual_terminal_width(); + let theme = options.theme.to_theme(stdout_istty); + let exa = Exa { + options, + writer, + input_paths, + theme, + console_width, + git, + git_repos, + }; + + info!("matching on exa.run"); + match exa.run() { + Ok(exit_status) => { + trace!("exa.run: exit Ok({exit_status})"); + exit(exit_status); + } + + Err(e) if e.kind() == ErrorKind::BrokenPipe => { + warn!("Broken pipe error: {e}"); + exit(exits::SUCCESS); + } + + Err(e) => { + eprintln!("{e}"); + trace!("exa.run: exit RUNTIME_ERROR"); + exit(exits::RUNTIME_ERROR); + } + } + } + + OptionsResult::Help(help_text) => { + print!("{help_text}"); + } + + OptionsResult::Version(version_str) => { + print!("{version_str}"); + } + + OptionsResult::InvalidOptions(error) => { + eprintln!("eza: {error}"); + + if let Some(s) = error.suggestion() { + eprintln!("{s}"); + } + + exit(exits::OPTIONS_ERROR); + } + } +} + +/// The main program wrapper. +pub struct Exa<'args> { + /// List of command-line options, having been successfully parsed. + pub options: Options, + + /// The output handle that we write to. + pub writer: io::Stdout, + + /// List of the free command-line arguments that should correspond to file + /// names (anything that isn’t an option). + pub input_paths: Vec<&'args OsStr>, + + /// The theme that has been configured from the command-line options and + /// environment variables. If colours are disabled, this is a theme with + /// every style set to the default. + pub theme: Theme, + + /// The detected width of the console. This is used to determine which + /// view to use. + pub console_width: Option, + + /// A global Git cache, if the option was passed in. + /// This has to last the lifetime of the program, because the user might + /// want to list several directories in the same repository. + pub git: Option, + + pub git_repos: bool, +} + +/// The “real” environment variables type. +/// Instead of just calling `var_os` from within the options module, +/// the method of looking up environment variables has to be passed in. +struct LiveVars; +impl Vars for LiveVars { + fn get(&self, name: &'static str) -> Option { + env::var_os(name) + } +} + +/// Create a Git cache populated with the arguments that are going to be +/// listed before they’re actually listed, if the options demand it. +fn git_options(options: &Options, args: &[&OsStr]) -> Option { + if options.should_scan_for_git() { + Some(args.iter().map(PathBuf::from).collect()) + } else { + None + } +} + +#[cfg(not(feature = "git"))] +fn git_repos(_options: &Options, _args: &[&OsStr]) -> bool { + return false; +} + +#[cfg(feature = "git")] +fn get_files_in_dir(paths: &mut Vec, path: PathBuf) { + let temp_paths = if path.is_dir() { + match path.read_dir() { + Err(_) => { + vec![path] + } + Ok(d) => d + .filter_map(|entry| entry.ok().map(|e| e.path())) + .collect::>(), + } + } else { + vec![path] + }; + paths.extend(temp_paths); +} + +#[cfg(feature = "git")] +fn git_repos(options: &Options, args: &[&OsStr]) -> bool { + let option_enabled = match options.view.mode { + Mode::Details(details::Options { + table: Some(ref table), + .. + }) + | Mode::GridDetails(grid_details::Options { + details: + details::Options { + table: Some(ref table), + .. + }, + .. + }) => table.columns.subdir_git_repos || table.columns.subdir_git_repos_no_stat, + _ => false, + }; + if option_enabled { + let paths: Vec = args.iter().map(PathBuf::from).collect::>(); + let mut files: Vec = Vec::new(); + for path in paths { + get_files_in_dir(&mut files, path); + } + let repos: Vec = files + .iter() + .map(git2::Repository::open) + .map(|repo| repo.is_ok()) + .collect(); + repos.contains(&true) + } else { + false + } +} + +impl<'args> Exa<'args> { + /// # Errors + /// + /// Will return `Err` if printing to stderr fails. + pub fn run(mut self) -> io::Result { + debug!("Running with options: {:#?}", self.options); + + let mut files = Vec::new(); + let mut dirs = Vec::new(); + let mut exit_status = 0; + + for file_path in &self.input_paths { + let f = File::from_args( + PathBuf::from(file_path), + None, + None, + self.options.view.deref_links, + self.options.view.total_size, + None, + ); + + // We don't know whether this file exists, so we have to try to get + // the metadata to verify. + if let Err(e) = f.metadata() { + exit_status = 2; + writeln!(io::stderr(), "{file_path:?}: {e}")?; + continue; + } + + if f.points_to_directory() && !self.options.dir_action.treat_dirs_as_files() { + trace!("matching on to_dir"); + match f.to_dir() { + Ok(d) => dirs.push(d), + Err(e) if e.kind() == ErrorKind::PermissionDenied => { + eprintln!("{file_path:?}: {e}"); + exit(exits::PERMISSION_DENIED); + } + Err(e) => writeln!(io::stderr(), "{file_path:?}: {e}")?, + } + } else { + files.push(f); + } + } + + // We want to print a directory’s name before we list it, *except* in + // the case where it’s the only directory, *except* if there are any + // files to print as well. (It’s a double negative) + + let no_files = files.is_empty(); + let is_only_dir = dirs.len() == 1 && no_files; + + self.options.filter.filter_argument_files(&mut files); + self.print_files(None, files)?; + + self.print_dirs(dirs, no_files, is_only_dir, exit_status) + } + + fn print_dirs( + &mut self, + dir_files: Vec, + mut first: bool, + is_only_dir: bool, + exit_status: i32, + ) -> io::Result { + let View { + file_style: file_name::Options { quote_style, .. }, + .. + } = self.options.view; + for dir in dir_files { + // Put a gap between directories, or between the list of files and + // the first directory. + if first { + first = false; + } else { + writeln!(&mut self.writer)?; + } + + if !is_only_dir { + let mut bits = Vec::new(); + escape( + dir.path.display().to_string(), + &mut bits, + Style::default(), + Style::default(), + quote_style, + ); + writeln!(&mut self.writer, "{}:", ANSIStrings(&bits))?; + } + + let mut children = Vec::new(); + let git_ignore = self.options.filter.git_ignore == GitIgnore::CheckAndIgnore; + for file in dir.files( + self.options.filter.dot_filter, + self.git.as_ref(), + git_ignore, + self.options.view.deref_links, + self.options.view.total_size, + ) { + children.push(file); + } + let recursing = self.options.dir_action.recurse_options().is_some(); + self.options + .filter + .filter_child_files(recursing, &mut children); + self.options.filter.sort_files(&mut children); + + if let Some(recurse_opts) = self.options.dir_action.recurse_options() { + let depth = dir + .path + .components() + .filter(|&c| c != Component::CurDir) + .count() + + 1; + let follow_links = self.options.view.follow_links; + if !recurse_opts.tree && !recurse_opts.is_too_deep(depth) { + let mut child_dirs = Vec::new(); + for child_dir in children.iter().filter(|f| { + (if follow_links { + f.points_to_directory() + } else { + f.is_directory() + }) && !f.is_all_all + }) { + match child_dir.to_dir() { + Ok(d) => child_dirs.push(d), + Err(e) => { + writeln!(io::stderr(), "{}: {}", child_dir.path.display(), e)?; + } + } + } + + self.print_files(Some(&dir), children)?; + match self.print_dirs(child_dirs, false, false, exit_status) { + Ok(_) => (), + Err(e) => return Err(e), + } + continue; + } + } + + self.print_files(Some(&dir), children)?; + } + + Ok(exit_status) + } + + /// Prints the list of files using whichever view is selected. + fn print_files(&mut self, dir: Option<&Dir>, mut files: Vec>) -> io::Result<()> { + if files.is_empty() { + return Ok(()); + } + let recursing = self.options.dir_action.recurse_options().is_some(); + let only_files = self.options.filter.flags.contains(&OnlyFiles); + if recursing && only_files { + files = files + .into_iter() + .filter(|f| !f.is_directory()) + .collect::>(); + } + let theme = &self.theme; + let View { + ref mode, + ref file_style, + .. + } = self.options.view; + + match (mode, self.console_width) { + (Mode::Grid(ref opts), Some(console_width)) => { + let filter = &self.options.filter; + let r = grid::Render { + files, + theme, + file_style, + opts, + console_width, + filter, + }; + r.render(&mut self.writer) + } + + (Mode::Grid(_), None) | (Mode::Lines, _) => { + let filter = &self.options.filter; + let r = lines::Render { + files, + theme, + file_style, + filter, + }; + r.render(&mut self.writer) + } + + (Mode::Details(ref opts), _) => { + let filter = &self.options.filter; + let recurse = self.options.dir_action.recurse_options(); + + let git_ignoring = self.options.filter.git_ignore == GitIgnore::CheckAndIgnore; + let git = self.git.as_ref(); + let git_repos = self.git_repos; + let r = details::Render { + dir, + files, + theme, + file_style, + opts, + recurse, + filter, + git_ignoring, + git, + git_repos, + }; + r.render(&mut self.writer) + } + + (Mode::GridDetails(ref opts), Some(console_width)) => { + let details = &opts.details; + let row_threshold = opts.row_threshold; + + let filter = &self.options.filter; + let git_ignoring = self.options.filter.git_ignore == GitIgnore::CheckAndIgnore; + let git = self.git.as_ref(); + let git_repos = self.git_repos; + + let r = grid_details::Render { + dir, + files, + theme, + file_style, + details, + filter, + row_threshold, + git_ignoring, + git, + console_width, + git_repos, + }; + r.render(&mut self.writer) + } + + (Mode::GridDetails(ref opts), None) => { + let opts = &opts.to_details_options(); + let filter = &self.options.filter; + let recurse = self.options.dir_action.recurse_options(); + let git_ignoring = self.options.filter.git_ignore == GitIgnore::CheckAndIgnore; + let git = self.git.as_ref(); + let git_repos = self.git_repos; + + let r = details::Render { + dir, + files, + theme, + file_style, + opts, + recurse, + filter, + git_ignoring, + git, + git_repos, + }; + r.render(&mut self.writer) + } + } + } +} + +mod exits { + + /// Exit code for when exa runs OK. + pub const SUCCESS: i32 = 0; + + /// Exit code for when there was at least one I/O error during execution. + pub const RUNTIME_ERROR: i32 = 1; + + /// Exit code for when the command-line options are invalid. + pub const OPTIONS_ERROR: i32 = 3; + + /// Exit code for missing file permissions + pub const PERMISSION_DENIED: i32 = 13; +} diff --git a/collector/compile-benchmarks/eza-0.21.2/src/options/config.rs b/collector/compile-benchmarks/eza-0.21.2/src/options/config.rs new file mode 100644 index 000000000..ea4248fb5 --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/src/options/config.rs @@ -0,0 +1,664 @@ +// SPDX-FileCopyrightText: 2024 Christina Sørensen +// SPDX-License-Identifier: EUPL-1.2 +// +// SPDX-FileCopyrightText: 2023-2024 Christina Sørensen, eza contributors +// SPDX-FileCopyrightText: 2014 Benjamin Sago +// SPDX-License-Identifier: MIT +use crate::theme::ThemeFileType as FileType; +use crate::theme::*; +use nu_ansi_term::{Color, Style}; +use serde::{Deserialize, Deserializer, Serialize}; +use serde_norway; +use std::collections::HashMap; +use std::path::PathBuf; + +#[derive(Debug, Eq, PartialEq)] +pub struct ThemeConfig { + // This is rather bare for now, will be expanded with config file + location: PathBuf, +} + +impl Default for ThemeConfig { + fn default() -> Self { + ThemeConfig { + location: dirs::config_dir() + .unwrap_or_default() + .join("eza") + .join("theme.yml"), + } + } +} + +trait FromOverride: Sized { + fn from(value: T, default: Self) -> Self; +} + +impl FromOverride> for Option +where + T: FromOverride + Default, +{ + fn from(value: Option, default: Option) -> Option { + match (value, default) { + (Some(value), Some(default)) => Some(FromOverride::from(value, default)), + (Some(value), None) => Some(FromOverride::from(value, T::default())), + (None, Some(default)) => Some(default), + (None, None) => None, + } + } +} + +#[rustfmt::skip] +fn color_from_str(s: &str) -> Option { + use Color::*; + match s { + // nothing + "" | "none" | "None" => None, + + // hardcoded colors + "default" | "Default" => Some(Default), + "black" | "Black" => Some(Black), + "darkgray" | "DarkGray" => Some(DarkGray), + "red" | "Red" => Some(Red), + "lightred" | "LightRed" => Some(LightRed), + "green" | "Green" => Some(Green), + "lightgreen" | "LightGreen" => Some(LightGreen), + "yellow" | "Yellow" => Some(Yellow), + "lightyellow" | "LightYellow" => Some(LightYellow), + "blue" | "Blue" => Some(Blue), + "lightblue" | "LightBlue" => Some(LightBlue), + "purple" | "Purple" => Some(Purple), + "lightpurple" | "LightPurple" => Some(LightPurple), + "magenta" | "Magenta" => Some(Magenta), + "lightmagenta" | "LightMagenta" => Some(LightMagenta), + "cyan" | "Cyan" => Some(Cyan), + "lightcyan" | "LightCyan" => Some(LightCyan), + "white" | "White" => Some(White), + "lightgray" | "LightGray" => Some(LightGray), + + // some other string + s => match s.chars().collect::>()[..] { + // #rrggbb hex color + ['#', r1, r2, g1, g2, b1, b2] => { + let Ok(r) = u8::from_str_radix(&format!("{r1}{r2}"), 16) + else { return None }; + let Ok(g) = u8::from_str_radix(&format!("{g1}{g2}"), 16) + else { return None }; + let Ok(b) = u8::from_str_radix(&format!("{b1}{b2}"), 16) + else { return None }; + Some(Rgb(r, g, b)) + }, + // #rgb shorthand hex color + ['#', r, g, b] => { + let Ok(r) = u8::from_str_radix(&format!("{r}{r}"), 16) + else { return None }; + let Ok(g) = u8::from_str_radix(&format!("{g}{g}"), 16) + else { return None }; + let Ok(b) = u8::from_str_radix(&format!("{b}{b}"), 16) + else { return None }; + Some(Rgb(r, g, b)) + }, + // 0-255 color code + [c1, c2] => { + let Ok(c) = str::parse::(&format!("{c1}{c2}")) + else { return None }; + Some(Fixed(c)) + }, + // unknown format + _ => None, + } + } +} + +#[rustfmt::skip] +fn deserialize_color<'de, D>(deserializer: D) -> Result, D::Error> +where D: Deserializer<'de> { + Ok(color_from_str(&String::deserialize(deserializer)?)) +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize, Default)] +pub struct StyleOverride { + /// The style's foreground color, if it has one. + #[serde(alias = "fg", deserialize_with = "deserialize_color", default)] + pub foreground: Option, + + /// The style's background color, if it has one. + #[serde(alias = "bg", deserialize_with = "deserialize_color", default)] + pub background: Option, + + /// Whether this style is bold. + #[serde(alias = "bold")] + pub is_bold: Option, + + /// Whether this style is dimmed. + #[serde(alias = "dimmed")] + pub is_dimmed: Option, + + /// Whether this style is italic. + #[serde(alias = "italic")] + pub is_italic: Option, + + /// Whether this style is underlined. + #[serde(alias = "underline")] + pub is_underline: Option, + + /// Whether this style is blinking. + #[serde(alias = "blink")] + pub is_blink: Option, + + /// Whether this style has reverse colors. + #[serde(alias = "reverse")] + pub is_reverse: Option, + + /// Whether this style is hidden. + #[serde(alias = "hidden")] + pub is_hidden: Option, + + /// Whether this style is struckthrough. + #[serde(alias = "strikethrough")] + pub is_strikethrough: Option, + + /// Wether this style is always displayed starting with a reset code to clear any remaining style artifacts + #[serde(alias = "prefix_reset")] + pub prefix_with_reset: Option, +} + +impl FromOverride for Style { + fn from(value: StyleOverride, default: Self) -> Self { + let mut style = default; + if value.foreground.is_some() { + style.foreground = value.foreground; + } + if value.background.is_some() { + style.background = value.background; + } + if let Some(bold) = value.is_bold { + style.is_bold = bold; + } + if let Some(dimmed) = value.is_dimmed { + style.is_dimmed = dimmed; + } + if let Some(italic) = value.is_italic { + style.is_italic = italic; + } + if let Some(underline) = value.is_underline { + style.is_underline = underline; + } + if let Some(blink) = value.is_blink { + style.is_blink = blink; + } + if let Some(reverse) = value.is_reverse { + style.is_reverse = reverse; + } + if let Some(hidden) = value.is_hidden { + style.is_hidden = hidden; + } + if let Some(strikethrough) = value.is_strikethrough { + style.is_strikethrough = strikethrough; + } + if let Some(reset) = value.prefix_with_reset { + style.prefix_with_reset = reset; + } + style + } +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)] +pub struct IconStyleOverride { + pub glyph: Option, + pub style: Option, +} + +impl FromOverride for char { + fn from(value: char, _default: char) -> char { + value + } +} + +impl FromOverride for IconStyle { + fn from(value: IconStyleOverride, default: Self) -> Self { + IconStyle { + glyph: FromOverride::from(value.glyph, default.glyph), + style: FromOverride::from(value.style, default.style), + } + } +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)] +pub struct FileNameStyleOverride { + pub icon: Option, + pub filename: Option, +} + +impl FromOverride for FileNameStyle { + fn from(value: FileNameStyleOverride, default: Self) -> Self { + FileNameStyle { + icon: FromOverride::from(value.icon, default.icon), + filename: FromOverride::from(value.filename, default.filename), + } + } +} + +impl FromOverride> for HashMap +where + T: FromOverride, + R: Clone + Eq + std::hash::Hash, + T: Clone + Eq + Default, +{ + fn from(value: HashMap, default: HashMap) -> HashMap { + let mut result = default.clone(); + for (r, s) in value { + let t = match default.get(&r) { + Some(t) => t.clone(), + None => T::default(), + }; + result.insert(r, FromOverride::from(s, t)); + } + result + } +} + +#[rustfmt::skip] +#[derive(Clone, Eq, Copy, Debug, PartialEq, Serialize, Deserialize)] +pub struct FileKindsOverride { + pub normal: Option, // fi + pub directory: Option, // di + pub symlink: Option, // ln + pub pipe: Option, // pi + pub block_device: Option, // bd + pub char_device: Option, // cd + pub socket: Option, // so + pub special: Option, // sp + pub executable: Option, // ex + pub mount_point: Option, // mp +} + +impl FromOverride for FileKinds { + fn from(value: FileKindsOverride, default: Self) -> Self { + FileKinds { + normal: FromOverride::from(value.normal, default.normal), + directory: FromOverride::from(value.directory, default.directory), + symlink: FromOverride::from(value.symlink, default.symlink), + pipe: FromOverride::from(value.pipe, default.pipe), + block_device: FromOverride::from(value.block_device, default.block_device), + char_device: FromOverride::from(value.char_device, default.char_device), + socket: FromOverride::from(value.socket, default.socket), + special: FromOverride::from(value.special, default.special), + executable: FromOverride::from(value.executable, default.executable), + mount_point: FromOverride::from(value.mount_point, default.mount_point), + } + } +} + +#[rustfmt::skip] +#[derive(Clone, Copy,Eq, Debug, Default, PartialEq, Serialize, Deserialize)] +pub struct PermissionsOverride { + pub user_read: Option, // ur + pub user_write: Option, // uw + pub user_execute_file: Option, // ux + pub user_execute_other: Option, // ue + + pub group_read: Option, // gr + pub group_write: Option, // gw + pub group_execute: Option, // gx + + pub other_read: Option, // tr + pub other_write: Option, // tw + pub other_execute: Option, // tx + + pub special_user_file: Option, // su + pub special_other: Option, // sf + + pub attribute: Option, // xa +} + +impl FromOverride for Permissions { + fn from(value: PermissionsOverride, default: Self) -> Self { + Permissions { + user_read: FromOverride::from(value.user_read, default.user_read), + user_write: FromOverride::from(value.user_write, default.user_write), + user_execute_file: FromOverride::from( + value.user_execute_file, + default.user_execute_file, + ), + user_execute_other: FromOverride::from( + value.user_execute_other, + default.user_execute_other, + ), + group_read: FromOverride::from(value.group_read, default.group_read), + group_write: FromOverride::from(value.group_write, default.group_write), + group_execute: FromOverride::from(value.group_execute, default.group_execute), + other_read: FromOverride::from(value.other_read, default.other_read), + other_write: FromOverride::from(value.other_write, default.other_write), + other_execute: FromOverride::from(value.other_execute, default.other_execute), + special_user_file: FromOverride::from( + value.special_user_file, + default.special_user_file, + ), + special_other: FromOverride::from(value.special_other, default.special_other), + attribute: FromOverride::from(value.attribute, default.attribute), + } + } +} + +#[rustfmt::skip] +#[derive(Clone, Copy, Eq, Debug, Default, PartialEq, Serialize, Deserialize)] +pub struct SizeOverride { + pub major: Option, // df + pub minor: Option, // ds + + pub number_byte: Option, // sn nb + pub number_kilo: Option, // sn nk + pub number_mega: Option, // sn nm + pub number_giga: Option, // sn ng + pub number_huge: Option, // sn nt + + pub unit_byte: Option, // sb ub + pub unit_kilo: Option, // sb uk + pub unit_mega: Option, // sb um + pub unit_giga: Option, // sb ug + pub unit_huge: Option, // sb ut +} + +impl FromOverride for Size { + fn from(value: SizeOverride, default: Self) -> Self { + Size { + major: FromOverride::from(value.major, default.major), + minor: FromOverride::from(value.minor, default.minor), + number_byte: FromOverride::from(value.number_byte, default.number_byte), + number_kilo: FromOverride::from(value.number_kilo, default.number_kilo), + number_mega: FromOverride::from(value.number_mega, default.number_mega), + number_giga: FromOverride::from(value.number_giga, default.number_giga), + number_huge: FromOverride::from(value.number_huge, default.number_huge), + unit_byte: FromOverride::from(value.unit_byte, default.unit_byte), + unit_kilo: FromOverride::from(value.unit_kilo, default.unit_kilo), + unit_mega: FromOverride::from(value.unit_mega, default.unit_mega), + unit_giga: FromOverride::from(value.unit_giga, default.unit_giga), + unit_huge: FromOverride::from(value.unit_huge, default.unit_huge), + } + } +} + +#[rustfmt::skip] +#[derive(Clone, Copy, Debug,Eq, Default, PartialEq, Serialize, Deserialize)] +pub struct UsersOverride { + pub user_you: Option, // uu + pub user_root: Option, // uR + pub user_other: Option, // un + pub group_yours: Option, // gu + pub group_other: Option, // gn + pub group_root: Option, // gR +} + +impl FromOverride for Users { + fn from(value: UsersOverride, default: Self) -> Self { + Users { + user_you: FromOverride::from(value.user_you, default.user_you), + user_root: FromOverride::from(value.user_root, default.user_root), + user_other: FromOverride::from(value.user_other, default.user_other), + group_yours: FromOverride::from(value.group_yours, default.group_yours), + group_other: FromOverride::from(value.group_other, default.group_other), + group_root: FromOverride::from(value.group_root, default.group_root), + } + } +} + +#[rustfmt::skip] +#[derive(Clone, Copy, Debug, Eq, Default, PartialEq, Serialize, Deserialize)] +pub struct LinksOverride { + pub normal: Option, // lc + pub multi_link_file: Option, // lm +} + +impl FromOverride for Links { + fn from(value: LinksOverride, default: Self) -> Self { + Links { + normal: FromOverride::from(value.normal, default.normal), + multi_link_file: FromOverride::from(value.multi_link_file, default.multi_link_file), + } + } +} + +#[rustfmt::skip] +#[derive(Clone, Copy, Debug,Eq, PartialEq, Serialize, Deserialize)] +pub struct GitOverride { + pub new: Option, // ga + pub modified: Option, // gm + pub deleted: Option, // gd + pub renamed: Option, // gv + pub typechange: Option, // gt + pub ignored: Option, // gi + pub conflicted: Option, // gc +} + +impl FromOverride for Git { + fn from(value: GitOverride, default: Self) -> Self { + Git { + new: FromOverride::from(value.new, default.new), + modified: FromOverride::from(value.modified, default.modified), + deleted: FromOverride::from(value.deleted, default.deleted), + renamed: FromOverride::from(value.renamed, default.renamed), + typechange: FromOverride::from(value.typechange, default.typechange), + ignored: FromOverride::from(value.ignored, default.ignored), + conflicted: FromOverride::from(value.conflicted, default.conflicted), + } + } +} + +#[rustfmt::skip] +#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)] +pub struct GitRepoOverride { + pub branch_main: Option, //Gm + pub branch_other: Option, //Go + pub git_clean: Option, //Gc + pub git_dirty: Option, //Gd +} + +impl FromOverride for GitRepo { + fn from(value: GitRepoOverride, default: Self) -> Self { + GitRepo { + branch_main: FromOverride::from(value.branch_main, default.branch_main), + branch_other: FromOverride::from(value.branch_other, default.branch_other), + git_clean: FromOverride::from(value.git_clean, default.git_clean), + git_dirty: FromOverride::from(value.git_dirty, default.git_dirty), + } + } +} + +#[derive(Clone, Copy, Debug, Eq, Default, PartialEq, Serialize, Deserialize)] +pub struct SELinuxContextOverride { + pub colon: Option, + pub user: Option, // Su + pub role: Option, // Sr + pub typ: Option, // St + pub range: Option, // Sl +} + +impl FromOverride for SELinuxContext { + fn from(value: SELinuxContextOverride, default: Self) -> Self { + SELinuxContext { + colon: FromOverride::from(value.colon, default.colon), + user: FromOverride::from(value.user, default.user), + role: FromOverride::from(value.role, default.role), + typ: FromOverride::from(value.typ, default.typ), + range: FromOverride::from(value.range, default.range), + } + } +} + +#[rustfmt::skip] +#[derive(Clone, Eq, Copy, Debug, PartialEq, Serialize, Deserialize)] +pub struct SecurityContextOverride { + pub none: Option, // Sn + pub selinux: Option, +} + +impl FromOverride for SecurityContext { + fn from(value: SecurityContextOverride, default: Self) -> Self { + SecurityContext { + none: FromOverride::from(value.none, default.none), + selinux: FromOverride::from(value.selinux, default.selinux), + } + } +} + +#[rustfmt::skip] +#[derive(Clone, Copy, Debug, Eq, Default, PartialEq, Serialize, Deserialize)] +pub struct FileTypeOverride { + pub image: Option, // im - image file + pub video: Option, // vi - video file + pub music: Option, // mu - lossy music + pub lossless: Option, // lo - lossless music + pub crypto: Option, // cr - related to cryptography + pub document: Option, // do - document file + pub compressed: Option, // co - compressed file + pub temp: Option, // tm - temporary file + pub compiled: Option, // cm - compilation artifact + pub build: Option, // bu - file that is used to build a project + pub source: Option, // sc - source code +} + +impl FromOverride for FileType { + fn from(value: FileTypeOverride, default: Self) -> Self { + FileType { + image: FromOverride::from(value.image, default.image), + video: FromOverride::from(value.video, default.video), + music: FromOverride::from(value.music, default.music), + lossless: FromOverride::from(value.lossless, default.lossless), + crypto: FromOverride::from(value.crypto, default.crypto), + document: FromOverride::from(value.document, default.document), + compressed: FromOverride::from(value.compressed, default.compressed), + temp: FromOverride::from(value.temp, default.temp), + compiled: FromOverride::from(value.compiled, default.compiled), + build: FromOverride::from(value.build, default.build), + source: FromOverride::from(value.source, default.source), + } + } +} + +#[rustfmt::skip] +#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] +pub struct UiStylesOverride { + pub colourful: Option, + + pub filekinds: Option, + pub perms: Option, + pub size: Option, + pub users: Option, + pub links: Option, + pub git: Option, + pub git_repo: Option, + pub security_context: Option, + pub file_type: Option, + + pub punctuation: Option, // xx + pub date: Option, // da + pub inode: Option, // in + pub blocks: Option, // bl + pub header: Option, // hd + pub octal: Option, // oc + pub flags: Option, // ff + + pub symlink_path: Option, // lp + pub control_char: Option, // cc + pub broken_symlink: Option, // or + pub broken_path_overlay: Option, // bO + + pub filenames: Option>, + pub extensions: Option>, +} + +impl FromOverride for UiStyles { + fn from(value: UiStylesOverride, default: Self) -> Self { + UiStyles { + colourful: value.colourful, + + filekinds: FromOverride::from(value.filekinds, default.filekinds), + perms: FromOverride::from(value.perms, default.perms), + size: FromOverride::from(value.size, default.size), + users: FromOverride::from(value.users, default.users), + links: FromOverride::from(value.links, default.links), + git: FromOverride::from(value.git, default.git), + git_repo: FromOverride::from(value.git_repo, default.git_repo), + security_context: FromOverride::from(value.security_context, default.security_context), + file_type: FromOverride::from(value.file_type, default.file_type), + + punctuation: FromOverride::from(value.punctuation, default.punctuation), + date: FromOverride::from(value.date, default.date), + inode: FromOverride::from(value.inode, default.inode), + blocks: FromOverride::from(value.blocks, default.blocks), + header: FromOverride::from(value.header, default.header), + octal: FromOverride::from(value.octal, default.octal), + flags: FromOverride::from(value.flags, default.flags), + + symlink_path: FromOverride::from(value.symlink_path, default.symlink_path), + control_char: FromOverride::from(value.control_char, default.control_char), + broken_symlink: FromOverride::from(value.broken_symlink, default.broken_symlink), + broken_path_overlay: FromOverride::from( + value.broken_path_overlay, + default.broken_path_overlay, + ), + + filenames: FromOverride::from(value.filenames, default.filenames), + extensions: FromOverride::from(value.extensions, default.extensions), + } + } +} +impl ThemeConfig { + pub fn from_path(path: PathBuf) -> Self { + ThemeConfig { location: path } + } + pub fn to_theme(&self) -> Option { + let ui_styles_override: Option = { + let file = std::fs::File::open(&self.location).ok()?; + serde_norway::from_reader(&file).ok() + }; + FromOverride::from(ui_styles_override, Some(UiStyles::default())) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parse_none_color_from_string() { + for case in &["", "none", "None"] { + assert_eq!(color_from_str(case), None); + } + } + + #[test] + fn parse_default_color_from_string() { + for case in &["default", "Default"] { + assert_eq!(color_from_str(case), Some(Color::Default)); + } + } + + #[test] + fn parse_fixed_color_from_string() { + for case in &["black", "Black"] { + assert_eq!(color_from_str(case), Some(Color::Black)); + } + } + + #[test] + fn parse_long_hex_color_from_string() { + for case in &["#ff00ff", "#FF00FF"] { + assert_eq!(color_from_str(case), Some(Color::Rgb(255, 0, 255))); + } + } + + #[test] + fn parse_short_hex_color_from_string() { + for case in ["#f0f", "#F0F"].iter() { + assert_eq!(color_from_str(case), Some(Color::Rgb(255, 0, 255))); + } + } + + #[test] + fn parse_color_code_from_string() { + for (s, c) in &[("10", 10), ("01", 1)] { + assert_eq!(color_from_str(s), Some(Color::Fixed(*c))); + } + } +} diff --git a/collector/compile-benchmarks/eza-0.21.2/src/options/dir_action.rs b/collector/compile-benchmarks/eza-0.21.2/src/options/dir_action.rs new file mode 100644 index 000000000..417d686ba --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/src/options/dir_action.rs @@ -0,0 +1,140 @@ +// SPDX-FileCopyrightText: 2024 Christina Sørensen +// SPDX-License-Identifier: EUPL-1.2 +// +// SPDX-FileCopyrightText: 2023-2024 Christina Sørensen, eza contributors +// SPDX-FileCopyrightText: 2014 Benjamin Sago +// SPDX-License-Identifier: MIT +//! Parsing the options for `DirAction`. + +use crate::options::parser::MatchedFlags; +use crate::options::{flags, NumberSource, OptionsError}; + +use crate::fs::dir_action::{DirAction, RecurseOptions}; + +impl DirAction { + /// Determine which action to perform when trying to list a directory. + /// There are three possible actions, and they overlap somewhat: the + /// `--tree` flag is another form of recursion, so those two are allowed + /// to both be present, but the `--list-dirs` flag is used separately. + pub fn deduce(matches: &MatchedFlags<'_>, can_tree: bool) -> Result { + let recurse = matches.has(&flags::RECURSE)?; + let as_file = matches.has(&flags::LIST_DIRS)?; + let tree = matches.has(&flags::TREE)?; + + if matches.is_strict() { + // Early check for --level when it wouldn’t do anything + if !recurse && !tree && matches.count(&flags::LEVEL) > 0 { + return Err(OptionsError::Useless2( + &flags::LEVEL, + &flags::RECURSE, + &flags::TREE, + )); + } else if recurse && as_file { + return Err(OptionsError::Conflict(&flags::RECURSE, &flags::LIST_DIRS)); + } else if tree && as_file { + return Err(OptionsError::Conflict(&flags::TREE, &flags::LIST_DIRS)); + } + } + + if tree && can_tree { + // Tree is only appropriate in details mode, so this has to + // examine the View, which should have already been deduced by now + Ok(Self::Recurse(RecurseOptions::deduce(matches, true)?)) + } else if recurse { + Ok(Self::Recurse(RecurseOptions::deduce(matches, false)?)) + } else if as_file { + Ok(Self::AsFile) + } else { + Ok(Self::List) + } + } +} + +impl RecurseOptions { + /// Determine which files should be recursed into, based on the `--level` + /// flag’s value, and whether the `--tree` flag was passed, which was + /// determined earlier. The maximum level should be a number, and this + /// will fail with an `Err` if it isn’t. + pub fn deduce(matches: &MatchedFlags<'_>, tree: bool) -> Result { + if let Some(level) = matches.get(&flags::LEVEL)? { + let arg_str = level.to_string_lossy(); + match arg_str.parse() { + Ok(l) => Ok(Self { + tree, + max_depth: Some(l), + }), + Err(e) => { + let source = NumberSource::Arg(&flags::LEVEL); + Err(OptionsError::FailedParse(arg_str.to_string(), source, e)) + } + } + } else { + Ok(Self { + tree, + max_depth: None, + }) + } + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::options::flags; + use crate::options::parser::Flag; + + macro_rules! test { + ($name:ident: $type:ident <- $inputs:expr; $stricts:expr => $result:expr) => { + #[test] + fn $name() { + use crate::options::parser::Arg; + use crate::options::test::parse_for_test; + use crate::options::test::Strictnesses::*; + + static TEST_ARGS: &[&Arg] = &[ + &flags::RECURSE, + &flags::LIST_DIRS, + &flags::TREE, + &flags::LEVEL, + ]; + for result in parse_for_test($inputs.as_ref(), TEST_ARGS, $stricts, |mf| { + $type::deduce(mf, true) + }) { + assert_eq!(result, $result); + } + } + }; + } + + // Default behaviour + test!(empty: DirAction <- []; Both => Ok(DirAction::List)); + + // Listing files as directories + test!(dirs_short: DirAction <- ["-d"]; Both => Ok(DirAction::AsFile)); + test!(dirs_long: DirAction <- ["--list-dirs"]; Both => Ok(DirAction::AsFile)); + + // Recursing + use self::DirAction::Recurse; + test!(rec_short: DirAction <- ["-R"]; Both => Ok(Recurse(RecurseOptions { tree: false, max_depth: None }))); + test!(rec_long: DirAction <- ["--recurse"]; Both => Ok(Recurse(RecurseOptions { tree: false, max_depth: None }))); + test!(rec_lim_short: DirAction <- ["-RL4"]; Both => Ok(Recurse(RecurseOptions { tree: false, max_depth: Some(4) }))); + test!(rec_lim_short_2: DirAction <- ["-RL=5"]; Both => Ok(Recurse(RecurseOptions { tree: false, max_depth: Some(5) }))); + test!(rec_lim_long: DirAction <- ["--recurse", "--level", "666"]; Both => Ok(Recurse(RecurseOptions { tree: false, max_depth: Some(666) }))); + test!(rec_lim_long_2: DirAction <- ["--recurse", "--level=0118"]; Both => Ok(Recurse(RecurseOptions { tree: false, max_depth: Some(118) }))); + test!(tree: DirAction <- ["--tree"]; Both => Ok(Recurse(RecurseOptions { tree: true, max_depth: None }))); + test!(rec_tree: DirAction <- ["--recurse", "--tree"]; Both => Ok(Recurse(RecurseOptions { tree: true, max_depth: None }))); + test!(rec_short_tree: DirAction <- ["-TR"]; Both => Ok(Recurse(RecurseOptions { tree: true, max_depth: None }))); + + // Overriding --list-dirs, --recurse, and --tree + test!(dirs_recurse: DirAction <- ["--list-dirs", "--recurse"]; Last => Ok(Recurse(RecurseOptions { tree: false, max_depth: None }))); + test!(dirs_tree: DirAction <- ["--list-dirs", "--tree"]; Last => Ok(Recurse(RecurseOptions { tree: true, max_depth: None }))); + test!(just_level: DirAction <- ["--level=4"]; Last => Ok(DirAction::List)); + + test!(dirs_recurse_2: DirAction <- ["--list-dirs", "--recurse"]; Complain => Err(OptionsError::Conflict(&flags::RECURSE, &flags::LIST_DIRS))); + test!(dirs_tree_2: DirAction <- ["--list-dirs", "--tree"]; Complain => Err(OptionsError::Conflict(&flags::TREE, &flags::LIST_DIRS))); + test!(just_level_2: DirAction <- ["--level=4"]; Complain => Err(OptionsError::Useless2(&flags::LEVEL, &flags::RECURSE, &flags::TREE))); + + // Overriding levels + test!(overriding_1: DirAction <- ["-RL=6", "-L=7"]; Last => Ok(Recurse(RecurseOptions { tree: false, max_depth: Some(7) }))); + test!(overriding_2: DirAction <- ["-RL=6", "-L=7"]; Complain => Err(OptionsError::Duplicate(Flag::Short(b'L'), Flag::Short(b'L')))); +} diff --git a/collector/compile-benchmarks/eza-0.21.2/src/options/error.rs b/collector/compile-benchmarks/eza-0.21.2/src/options/error.rs new file mode 100644 index 000000000..66e222805 --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/src/options/error.rs @@ -0,0 +1,134 @@ +// SPDX-FileCopyrightText: 2024 Christina Sørensen +// SPDX-License-Identifier: EUPL-1.2 +// +// SPDX-FileCopyrightText: 2023-2024 Christina Sørensen, eza contributors +// SPDX-FileCopyrightText: 2014 Benjamin Sago +// SPDX-License-Identifier: MIT +use std::ffi::OsString; +use std::fmt; +use std::num::ParseIntError; + +use crate::options::flags; +use crate::options::parser::{Arg, Flag, ParseError}; + +/// Something wrong with the combination of options the user has picked. +#[derive(PartialEq, Eq, Debug)] +pub enum OptionsError { + /// There was an error (from `getopts`) parsing the arguments. + Parse(ParseError), + + /// The user supplied an illegal choice to an Argument. + BadArgument(&'static Arg, OsString), + + /// The user supplied a set of options that are unsupported + Unsupported(String), + + /// An option was given twice or more in strict mode. + Duplicate(Flag, Flag), + + /// Two options were given that conflict with one another. + Conflict(&'static Arg, &'static Arg), + + /// An option was given that does nothing when another one either is or + /// isn’t present. + Useless(&'static Arg, bool, &'static Arg), + + /// An option was given that does nothing when either of two other options + /// are not present. + Useless2(&'static Arg, &'static Arg, &'static Arg), + + /// A very specific edge case where --tree can’t be used with --all twice. + TreeAllAll, + + /// A numeric option was given that failed to be parsed as a number. + FailedParse(String, NumberSource, ParseIntError), + + /// A glob ignore was given that failed to be parsed as a pattern. + FailedGlobPattern(String), +} + +/// The source of a string that failed to be parsed as a number. +#[derive(PartialEq, Eq, Debug)] +pub enum NumberSource { + /// It came... from a command-line argument! + Arg(&'static Arg), + + /// It came... from the environment! + Env(&'static str), +} + +impl From for OptionsError { + fn from(error: glob::PatternError) -> Self { + Self::FailedGlobPattern(error.to_string()) + } +} + +impl fmt::Display for NumberSource { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Arg(arg) => write!(f, "option {arg}"), + Self::Env(env) => write!(f, "environment variable {env}"), + } + } +} + +impl fmt::Display for OptionsError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use crate::options::parser::TakesValue; + + #[rustfmt::skip] + return match self { + Self::BadArgument(arg, attempt) => { + if let TakesValue::Necessary(Some(values)) = arg.takes_value { + write!( + f, + "Option {} has no {:?} setting ({})", + arg, + attempt, + Choices(values) + ) + } else { + write!(f, "Option {arg} has no {attempt:?} setting") + } + } + Self::Parse(e) => write!(f, "{e}"), + Self::Unsupported(e) => write!(f, "{e}"), + Self::Conflict(a, b) => write!(f, "Option {a} conflicts with option {b}"), + Self::Duplicate(a, b) if a == b => write!(f, "Flag {a} was given twice"), + Self::Duplicate(a, b) => write!(f, "Flag {a} conflicts with flag {b}"), + Self::Useless(a, false, b) => write!(f, "Option {a} is useless without option {b}"), + Self::Useless(a, true, b) => write!(f, "Option {a} is useless given option {b}"), + Self::Useless2(a, b1, b2) => write!(f, "Option {a} is useless without options {b1} or {b2}"), + Self::TreeAllAll => write!(f, "Option --tree is useless given --all --all"), + Self::FailedParse(s, n, e) => write!(f, "Value {s:?} not valid for {n}: {e}"), + Self::FailedGlobPattern(ref e) => write!(f, "Failed to parse glob pattern: {e}"), + }; + } +} + +impl OptionsError { + /// Try to second-guess what the user was trying to do, depending on what + /// went wrong. + pub fn suggestion(&self) -> Option<&'static str> { + // ‘ls -lt’ and ‘ls -ltr’ are common combinations + match self { + Self::BadArgument(time, r) if *time == &flags::TIME && r == "r" => { + Some("To sort oldest files last, try \"--sort oldest\", or just \"-sold\"") + } + Self::Parse(ParseError::NeedsValue { ref flag, .. }) if *flag == Flag::Short(b't') => { + Some("To sort newest files last, try \"--sort newest\", or just \"-snew\"") + } + _ => None, + } + } +} + +/// A list of legal choices for an argument-taking option. +#[derive(PartialEq, Eq, Debug)] +pub struct Choices(pub &'static [&'static str]); + +impl fmt::Display for Choices { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "choices: {}", self.0.join(", ")) + } +} diff --git a/collector/compile-benchmarks/eza-0.21.2/src/options/file_name.rs b/collector/compile-benchmarks/eza-0.21.2/src/options/file_name.rs new file mode 100644 index 000000000..b6c9b9ef1 --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/src/options/file_name.rs @@ -0,0 +1,140 @@ +// SPDX-FileCopyrightText: 2024 Christina Sørensen +// SPDX-License-Identifier: EUPL-1.2 +// +// SPDX-FileCopyrightText: 2023-2024 Christina Sørensen, eza contributors +// SPDX-FileCopyrightText: 2014 Benjamin Sago +// SPDX-License-Identifier: MIT +use crate::options::parser::MatchedFlags; +use crate::options::vars::{self, Vars}; +use crate::options::{flags, NumberSource, OptionsError}; + +use crate::output::file_name::{ + Absolute, Classify, EmbedHyperlinks, Options, QuoteStyle, ShowIcons, +}; + +impl Options { + pub fn deduce( + matches: &MatchedFlags<'_>, + vars: &V, + is_a_tty: bool, + ) -> Result { + let classify = Classify::deduce(matches)?; + let show_icons = ShowIcons::deduce(matches, vars)?; + + let quote_style = QuoteStyle::deduce(matches)?; + let embed_hyperlinks = EmbedHyperlinks::deduce(matches)?; + + let absolute = Absolute::deduce(matches)?; + + Ok(Self { + classify, + show_icons, + quote_style, + embed_hyperlinks, + absolute, + is_a_tty, + }) + } +} + +impl Classify { + fn deduce(matches: &MatchedFlags<'_>) -> Result { + let mode_opt = matches.get(&flags::CLASSIFY)?; + + match mode_opt { + Some(word) => match word.to_str() { + Some("always") => Ok(Self::AddFileIndicators), + Some("auto" | "automatic") => Ok(Self::AutomaticAddFileIndicators), + Some("never") => Ok(Self::JustFilenames), + _ => Err(OptionsError::BadArgument(&flags::CLASSIFY, word.into())), + }, + // No flag given, default to just filenames + None => Ok(Self::JustFilenames), + } + } +} + +impl ShowIcons { + pub fn deduce(matches: &MatchedFlags<'_>, vars: &V) -> Result { + enum AlwaysOrAuto { + Always, + Automatic, + } + + let force_icons = vars.get(vars::EZA_ICONS_AUTO).is_some(); + let mode_opt = matches.get(&flags::ICONS)?; + if !force_icons && !matches.has(&flags::ICONS)? && mode_opt.is_none() { + return Ok(Self::Never); + } + + let mode = match mode_opt { + Some(word) => match word.to_str() { + Some("always") => AlwaysOrAuto::Always, + Some("auto" | "automatic") => AlwaysOrAuto::Automatic, + Some("never") => return Ok(Self::Never), + None => AlwaysOrAuto::Automatic, + _ => return Err(OptionsError::BadArgument(&flags::ICONS, word.into())), + }, + None => AlwaysOrAuto::Automatic, + }; + + let width = if let Some(columns) = vars + .get_with_fallback(vars::EXA_ICON_SPACING, vars::EZA_ICON_SPACING) + .and_then(|s| s.into_string().ok()) + { + match columns.parse() { + Ok(width) => width, + Err(e) => { + let source = NumberSource::Env( + vars.source(vars::EXA_ICON_SPACING, vars::EZA_ICON_SPACING) + .unwrap(), + ); + return Err(OptionsError::FailedParse(columns, source, e)); + } + } + } else { + 1 + }; + + match mode { + AlwaysOrAuto::Always => Ok(Self::Always(width)), + AlwaysOrAuto::Automatic => Ok(Self::Automatic(width)), + } + } +} + +impl QuoteStyle { + pub fn deduce(matches: &MatchedFlags<'_>) -> Result { + if matches.has(&flags::NO_QUOTES)? { + Ok(Self::NoQuotes) + } else { + Ok(Self::QuoteSpaces) + } + } +} + +impl EmbedHyperlinks { + fn deduce(matches: &MatchedFlags<'_>) -> Result { + let flagged = matches.has(&flags::HYPERLINK)?; + + if flagged { + Ok(Self::On) + } else { + Ok(Self::Off) + } + } +} + +impl Absolute { + fn deduce(matches: &MatchedFlags<'_>) -> Result { + match matches.get(&flags::ABSOLUTE)? { + Some(word) => match word.to_str() { + Some("on" | "yes") => Ok(Self::On), + Some("follow") => Ok(Self::Follow), + Some("off" | "no") | None => Ok(Self::Off), + _ => Err(OptionsError::BadArgument(&flags::ABSOLUTE, word.into())), + }, + None => Ok(Self::Off), + } + } +} diff --git a/collector/compile-benchmarks/eza-0.21.2/src/options/filter.rs b/collector/compile-benchmarks/eza-0.21.2/src/options/filter.rs new file mode 100644 index 000000000..dbea01f20 --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/src/options/filter.rs @@ -0,0 +1,322 @@ +// SPDX-FileCopyrightText: 2024 Christina Sørensen +// SPDX-License-Identifier: EUPL-1.2 +// +// SPDX-FileCopyrightText: 2023-2024 Christina Sørensen, eza contributors +// SPDX-FileCopyrightText: 2014 Benjamin Sago +// SPDX-License-Identifier: MIT +//! Parsing the options for `FileFilter`. + +use crate::fs::filter::{ + FileFilter, FileFilterFlags, GitIgnore, IgnorePatterns, SortCase, SortField, +}; +use crate::fs::DotFilter; + +use crate::options::parser::MatchedFlags; +use crate::options::{flags, OptionsError}; + +impl FileFilter { + /// Determines which of all the file filter options to use. + pub fn deduce(matches: &MatchedFlags<'_>) -> Result { + use FileFilterFlags as FFF; + let mut filter_flags: Vec = vec![]; + + for (has, flag) in &[ + (matches.has(&flags::REVERSE)?, FFF::Reverse), + (matches.has(&flags::ONLY_DIRS)?, FFF::OnlyDirs), + (matches.has(&flags::ONLY_FILES)?, FFF::OnlyFiles), + (matches.has(&flags::NO_SYMLINKS)?, FFF::NoSymlinks), + (matches.has(&flags::SHOW_SYMLINKS)?, FFF::ShowSymlinks), + (matches.has(&flags::DIRS_LAST)?, FFF::ListDirsLast), + (matches.has(&flags::DIRS_FIRST)?, FFF::ListDirsFirst), + ] { + if *has { + filter_flags.push(flag.clone()); + } + } + + #[rustfmt::skip] + return Ok(Self { + no_symlinks: filter_flags.contains(&FFF::NoSymlinks), + show_symlinks: filter_flags.contains(&FFF::ShowSymlinks), + flags: filter_flags, + sort_field: SortField::deduce(matches)?, + dot_filter: DotFilter::deduce(matches)?, + ignore_patterns: IgnorePatterns::deduce(matches)?, + git_ignore: GitIgnore::deduce(matches)?, + }); + } +} + +impl SortField { + /// Determines which sort field to use based on the `--sort` argument. + /// This argument’s value can be one of several flags, listed above. + /// Returns the default sort field if none is given, or `Err` if the + /// value doesn’t correspond to a sort field we know about. + fn deduce(matches: &MatchedFlags<'_>) -> Result { + let Some(word) = matches.get(&flags::SORT)? else { + return Ok(Self::default()); + }; + + // Get String because we can’t match an OsStr + let Some(word) = word.to_str() else { + return Err(OptionsError::BadArgument(&flags::SORT, word.into())); + }; + + let field = match word { + "name" | "filename" => Self::Name(SortCase::AaBbCc), + "Name" | "Filename" => Self::Name(SortCase::ABCabc), + ".name" | ".filename" => Self::NameMixHidden(SortCase::AaBbCc), + ".Name" | ".Filename" => Self::NameMixHidden(SortCase::ABCabc), + "size" | "filesize" => Self::Size, + "ext" | "extension" => Self::Extension(SortCase::AaBbCc), + "Ext" | "Extension" => Self::Extension(SortCase::ABCabc), + + // “new” sorts oldest at the top and newest at the bottom; “old” + // sorts newest at the top and oldest at the bottom. I think this + // is the right way round to do this: “size” puts the smallest at + // the top and the largest at the bottom, doesn’t it? + "date" | "time" | "mod" | "modified" | "new" | "newest" => Self::ModifiedDate, + + // Similarly, “age” means that files with the least age (the + // newest files) get sorted at the top, and files with the most + // age (the oldest) at the bottom. + "age" | "old" | "oldest" => Self::ModifiedAge, + + "ch" | "changed" => Self::ChangedDate, + "acc" | "accessed" => Self::AccessedDate, + "cr" | "created" => Self::CreatedDate, + #[cfg(unix)] + "inode" => Self::FileInode, + "type" => Self::FileType, + "none" => Self::Unsorted, + _ => { + return Err(OptionsError::BadArgument(&flags::SORT, word.into())); + } + }; + + Ok(field) + } +} + +// I’ve gone back and forth between whether to sort case-sensitively or +// insensitively by default. The default string sort in most programming +// languages takes each character’s ASCII value into account, sorting +// “Documents” before “apps”, but there’s usually an option to ignore +// characters’ case, putting “apps” before “Documents”. +// +// The argument for following case is that it’s easy to forget whether an item +// begins with an uppercase or lowercase letter and end up having to scan both +// the uppercase and lowercase sub-lists to find the item you want. If you +// happen to pick the sublist it’s not in, it looks like it’s missing, which +// is worse than if you just take longer to find it. +// (https://ux.stackexchange.com/a/79266) +// +// The argument for ignoring case is that it makes exa sort files differently +// from shells. A user would expect a directory’s files to be in the same +// order if they used “exa ~/directory” or “exa ~/directory/*”, but exa sorts +// them in the first case, and the shell in the second case, so they wouldn’t +// be exactly the same if exa does something non-conventional. +// +// However, exa already sorts files differently: it uses natural sorting from +// the natord crate, sorting the string “2” before “10” because the number’s +// smaller, because that’s usually what the user expects to happen. Users will +// name their files with numbers expecting them to be treated like numbers, +// rather than lists of numeric characters. +// +// In the same way, users will name their files with letters expecting the +// order of the letters to matter, rather than each letter’s character’s ASCII +// value. So exa breaks from tradition and ignores case while sorting: +// “apps” first, then “Documents”. +// +// You can get the old behaviour back by sorting with `--sort=Name`. +impl Default for SortField { + fn default() -> Self { + Self::Name(SortCase::AaBbCc) + } +} + +impl DotFilter { + /// Determines the dot filter based on how many `--all` options were + /// given: one will show dotfiles, but two will show `.` and `..` too. + /// --almost-all is equivalent to --all, included for compatibility with + /// `ls -A`. + /// + /// It also checks for the `--tree` option, because of a special case + /// where `--tree --all --all` won’t work: listing the parent directory + /// in tree mode would loop onto itself! + /// + /// `--almost-all` binds stronger than multiple `--all` as we currently do not take the order + /// of arguments into account and it is the safer option (does not clash with `--tree`) + pub fn deduce(matches: &MatchedFlags<'_>) -> Result { + let all_count = matches.count(&flags::ALL); + let has_almost_all = matches.has(&flags::ALMOST_ALL)?; + + match (all_count, has_almost_all) { + (0, false) => Ok(Self::JustFiles), + + // either a single --all or at least one --almost-all is given + (1, _) | (0, true) => Ok(Self::Dotfiles), + // more than one --all + (c, _) => { + if matches.count(&flags::TREE) > 0 { + Err(OptionsError::TreeAllAll) + } else if matches.is_strict() && c > 2 { + Err(OptionsError::Conflict(&flags::ALL, &flags::ALL)) + } else { + Ok(Self::DotfilesAndDots) + } + } + } + } +} + +impl IgnorePatterns { + /// Determines the set of glob patterns to use based on the + /// `--ignore-glob` argument’s value. This is a list of strings + /// separated by pipe (`|`) characters, given in any order. + pub fn deduce(matches: &MatchedFlags<'_>) -> Result { + // If there are no inputs, we return a set of patterns that doesn’t + // match anything, rather than, say, `None`. + let Some(inputs) = matches.get(&flags::IGNORE_GLOB)? else { + return Ok(Self::empty()); + }; + + // Awkwardly, though, a glob pattern can be invalid, and we need to + // deal with invalid patterns somehow. + let (patterns, mut errors) = Self::parse_from_iter(inputs.to_string_lossy().split('|')); + + // It can actually return more than one glob error, + // but we only use one. (TODO) + match errors.pop() { + Some(e) => Err(e.into()), + None => Ok(patterns), + } + } +} + +impl GitIgnore { + pub fn deduce(matches: &MatchedFlags<'_>) -> Result { + if matches.has(&flags::GIT_IGNORE)? { + Ok(Self::CheckAndIgnore) + } else { + Ok(Self::Off) + } + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::options::flags; + use crate::options::parser::Flag; + use std::ffi::OsString; + + macro_rules! test { + ($name:ident: $type:ident <- $inputs:expr; $stricts:expr => $result:expr) => { + #[test] + fn $name() { + use crate::options::parser::Arg; + use crate::options::test::parse_for_test; + use crate::options::test::Strictnesses::*; + + static TEST_ARGS: &[&Arg] = &[ + &flags::SORT, + &flags::ALL, + &flags::ALMOST_ALL, + &flags::TREE, + &flags::IGNORE_GLOB, + &flags::GIT_IGNORE, + ]; + for result in parse_for_test($inputs.as_ref(), TEST_ARGS, $stricts, |mf| { + $type::deduce(mf) + }) { + assert_eq!(result, $result); + } + } + }; + } + + mod sort_fields { + use super::*; + + // Default behaviour + test!(empty: SortField <- []; Both => Ok(SortField::default())); + + // Sort field arguments + test!(one_arg: SortField <- ["--sort=mod"]; Both => Ok(SortField::ModifiedDate)); + test!(one_long: SortField <- ["--sort=size"]; Both => Ok(SortField::Size)); + test!(one_short: SortField <- ["-saccessed"]; Both => Ok(SortField::AccessedDate)); + test!(lowercase: SortField <- ["--sort", "name"]; Both => Ok(SortField::Name(SortCase::AaBbCc))); + test!(uppercase: SortField <- ["--sort", "Name"]; Both => Ok(SortField::Name(SortCase::ABCabc))); + test!(old: SortField <- ["--sort", "new"]; Both => Ok(SortField::ModifiedDate)); + test!(oldest: SortField <- ["--sort=newest"]; Both => Ok(SortField::ModifiedDate)); + test!(new: SortField <- ["--sort", "old"]; Both => Ok(SortField::ModifiedAge)); + test!(newest: SortField <- ["--sort=oldest"]; Both => Ok(SortField::ModifiedAge)); + test!(age: SortField <- ["-sage"]; Both => Ok(SortField::ModifiedAge)); + + test!(mix_hidden_lowercase: SortField <- ["--sort", ".name"]; Both => Ok(SortField::NameMixHidden(SortCase::AaBbCc))); + test!(mix_hidden_uppercase: SortField <- ["--sort", ".Name"]; Both => Ok(SortField::NameMixHidden(SortCase::ABCabc))); + + // Errors + test!(error: SortField <- ["--sort=colour"]; Both => Err(OptionsError::BadArgument(&flags::SORT, OsString::from("colour")))); + + // Overriding + test!(overridden: SortField <- ["--sort=cr", "--sort", "mod"]; Last => Ok(SortField::ModifiedDate)); + test!(overridden_2: SortField <- ["--sort", "none", "--sort=Extension"]; Last => Ok(SortField::Extension(SortCase::ABCabc))); + test!(overridden_3: SortField <- ["--sort=cr", "--sort", "mod"]; Complain => Err(OptionsError::Duplicate(Flag::Long("sort"), Flag::Long("sort")))); + test!(overridden_4: SortField <- ["--sort", "none", "--sort=Extension"]; Complain => Err(OptionsError::Duplicate(Flag::Long("sort"), Flag::Long("sort")))); + } + + mod dot_filters { + use super::*; + + // Default behaviour + test!(empty: DotFilter <- []; Both => Ok(DotFilter::JustFiles)); + + // --all + test!(all: DotFilter <- ["--all"]; Both => Ok(DotFilter::Dotfiles)); + test!(all_all: DotFilter <- ["--all", "-a"]; Both => Ok(DotFilter::DotfilesAndDots)); + test!(all_all_2: DotFilter <- ["-aa"]; Both => Ok(DotFilter::DotfilesAndDots)); + + test!(all_all_3: DotFilter <- ["-aaa"]; Last => Ok(DotFilter::DotfilesAndDots)); + test!(all_all_4: DotFilter <- ["-aaa"]; Complain => Err(OptionsError::Conflict(&flags::ALL, &flags::ALL))); + + // --all and --tree + test!(tree_a: DotFilter <- ["-Ta"]; Both => Ok(DotFilter::Dotfiles)); + test!(tree_aa: DotFilter <- ["-Taa"]; Both => Err(OptionsError::TreeAllAll)); + test!(tree_aaa: DotFilter <- ["-Taaa"]; Both => Err(OptionsError::TreeAllAll)); + + // --almost-all + test!(almost_all: DotFilter <- ["--almost-all"]; Both => Ok(DotFilter::Dotfiles)); + test!(almost_all_all: DotFilter <- ["-Aa"]; Both => Ok(DotFilter::Dotfiles)); + test!(almost_all_all_2: DotFilter <- ["-Aaa"]; Both => Ok(DotFilter::DotfilesAndDots)); + } + + mod ignore_patterns { + use super::*; + use std::iter::FromIterator; + + fn pat(string: &'static str) -> glob::Pattern { + glob::Pattern::new(string).unwrap() + } + + // Various numbers of globs + test!(none: IgnorePatterns <- []; Both => Ok(IgnorePatterns::empty())); + test!(one: IgnorePatterns <- ["--ignore-glob", "*.ogg"]; Both => Ok(IgnorePatterns::from_iter(vec![ pat("*.ogg") ]))); + test!(two: IgnorePatterns <- ["--ignore-glob=*.ogg|*.MP3"]; Both => Ok(IgnorePatterns::from_iter(vec![ pat("*.ogg"), pat("*.MP3") ]))); + test!(loads: IgnorePatterns <- ["-I*|?|.|*"]; Both => Ok(IgnorePatterns::from_iter(vec![ pat("*"), pat("?"), pat("."), pat("*") ]))); + + // Overriding + test!(overridden: IgnorePatterns <- ["-I=*.ogg", "-I", "*.mp3"]; Last => Ok(IgnorePatterns::from_iter(vec![ pat("*.mp3") ]))); + test!(overridden_2: IgnorePatterns <- ["-I", "*.OGG", "-I*.MP3"]; Last => Ok(IgnorePatterns::from_iter(vec![ pat("*.MP3") ]))); + test!(overridden_3: IgnorePatterns <- ["-I=*.ogg", "-I", "*.mp3"]; Complain => Err(OptionsError::Duplicate(Flag::Short(b'I'), Flag::Short(b'I')))); + test!(overridden_4: IgnorePatterns <- ["-I", "*.OGG", "-I*.MP3"]; Complain => Err(OptionsError::Duplicate(Flag::Short(b'I'), Flag::Short(b'I')))); + } + + mod git_ignores { + use super::*; + + test!(off: GitIgnore <- []; Both => Ok(GitIgnore::Off)); + test!(on: GitIgnore <- ["--git-ignore"]; Both => Ok(GitIgnore::CheckAndIgnore)); + } +} diff --git a/collector/compile-benchmarks/eza-0.21.2/src/options/flags.rs b/collector/compile-benchmarks/eza-0.21.2/src/options/flags.rs new file mode 100644 index 000000000..581fd7e65 --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/src/options/flags.rs @@ -0,0 +1,115 @@ +// SPDX-FileCopyrightText: 2024 Christina Sørensen +// SPDX-License-Identifier: EUPL-1.2 +// +// SPDX-FileCopyrightText: 2023-2024 Christina Sørensen, eza contributors +// SPDX-FileCopyrightText: 2014 Benjamin Sago +// SPDX-License-Identifier: MIT +#![cfg_attr(rustfmt, rustfmt_skip)] +use crate::options::parser::{Arg, Args, TakesValue, Values}; + +// exa options +pub static VERSION: Arg = Arg { short: Some(b'v'), long: "version", takes_value: TakesValue::Forbidden }; +pub static HELP: Arg = Arg { short: Some(b'?'), long: "help", takes_value: TakesValue::Forbidden }; + +// display options +pub static ONE_LINE: Arg = Arg { short: Some(b'1'), long: "oneline", takes_value: TakesValue::Forbidden }; +pub static LONG: Arg = Arg { short: Some(b'l'), long: "long", takes_value: TakesValue::Forbidden }; +pub static GRID: Arg = Arg { short: Some(b'G'), long: "grid", takes_value: TakesValue::Forbidden }; +pub static ACROSS: Arg = Arg { short: Some(b'x'), long: "across", takes_value: TakesValue::Forbidden }; +pub static RECURSE: Arg = Arg { short: Some(b'R'), long: "recurse", takes_value: TakesValue::Forbidden }; +pub static TREE: Arg = Arg { short: Some(b'T'), long: "tree", takes_value: TakesValue::Forbidden }; +pub static CLASSIFY: Arg = Arg { short: Some(b'F'), long: "classify", takes_value: TakesValue::Optional(Some(WHEN), "auto") }; +pub static DEREF_LINKS: Arg = Arg { short: Some(b'X'), long: "dereference", takes_value: TakesValue::Forbidden }; +pub static WIDTH: Arg = Arg { short: Some(b'w'), long: "width", takes_value: TakesValue::Necessary(None) }; +pub static NO_QUOTES: Arg = Arg { short: None, long: "no-quotes", takes_value: TakesValue::Forbidden }; +pub static ABSOLUTE: Arg = Arg { short: None, long: "absolute", takes_value: TakesValue::Optional(Some(ABSOLUTE_MODES), "on") }; +pub static FOLLOW_LINKS: Arg = Arg { short: None, long: "follow-symlinks", takes_value: TakesValue::Forbidden }; +const ABSOLUTE_MODES: &[&str] = &["on", "follow", "off"]; + +pub static COLOR: Arg = Arg { short: None, long: "color", takes_value: TakesValue::Optional(Some(WHEN), "auto") }; +pub static COLOUR: Arg = Arg { short: None, long: "colour", takes_value: TakesValue::Optional(Some(WHEN), "auto") }; +const WHEN: &[&str] = &["always", "auto", "never"]; + +pub static COLOR_SCALE: Arg = Arg { short: None, long: "color-scale", takes_value: TakesValue::Optional(Some(SCALES), "all") }; +pub static COLOUR_SCALE: Arg = Arg { short: None, long: "colour-scale", takes_value: TakesValue::Optional(Some(SCALES), "all") }; +pub static COLOR_SCALE_MODE: Arg = Arg { short: None, long: "color-scale-mode", takes_value: TakesValue::Necessary(Some(COLOR_SCALE_MODES))}; +pub static COLOUR_SCALE_MODE: Arg = Arg { short: None, long: "colour-scale-mode", takes_value: TakesValue::Necessary(Some(COLOR_SCALE_MODES))}; +const SCALES: Values = &["all", "size", "age"]; +const COLOR_SCALE_MODES: Values = &["fixed", "gradient"]; + +// filtering and sorting options +pub static ALL: Arg = Arg { short: Some(b'a'), long: "all", takes_value: TakesValue::Forbidden }; +pub static ALMOST_ALL: Arg = Arg { short: Some(b'A'), long: "almost-all", takes_value: TakesValue::Forbidden }; +pub static LIST_DIRS: Arg = Arg { short: Some(b'd'), long: "list-dirs", takes_value: TakesValue::Forbidden }; +pub static LEVEL: Arg = Arg { short: Some(b'L'), long: "level", takes_value: TakesValue::Necessary(None) }; +pub static REVERSE: Arg = Arg { short: Some(b'r'), long: "reverse", takes_value: TakesValue::Forbidden }; +pub static SORT: Arg = Arg { short: Some(b's'), long: "sort", takes_value: TakesValue::Necessary(Some(SORTS)) }; +pub static IGNORE_GLOB: Arg = Arg { short: Some(b'I'), long: "ignore-glob", takes_value: TakesValue::Necessary(None) }; +pub static GIT_IGNORE: Arg = Arg { short: None, long: "git-ignore", takes_value: TakesValue::Forbidden }; +pub static DIRS_FIRST: Arg = Arg { short: None, long: "group-directories-first", takes_value: TakesValue::Forbidden }; +pub static DIRS_LAST: Arg = Arg { short: None, long: "group-directories-last", takes_value: TakesValue::Forbidden }; +pub static ONLY_DIRS: Arg = Arg { short: Some(b'D'), long: "only-dirs", takes_value: TakesValue::Forbidden }; +pub static ONLY_FILES: Arg = Arg { short: Some(b'f'), long: "only-files", takes_value: TakesValue::Forbidden }; +pub static NO_SYMLINKS: Arg = Arg { short: None, long: "no-symlinks", takes_value: TakesValue::Forbidden }; +pub static SHOW_SYMLINKS: Arg = Arg { short: None, long: "show-symlinks", takes_value: TakesValue::Forbidden }; +const SORTS: Values = &[ "name", "Name", "size", "extension", + "Extension", "modified", "changed", "accessed", + "created", "inode", "type", "none" ]; + +// display options +pub static BINARY: Arg = Arg { short: Some(b'b'), long: "binary", takes_value: TakesValue::Forbidden }; +pub static BYTES: Arg = Arg { short: Some(b'B'), long: "bytes", takes_value: TakesValue::Forbidden }; +pub static GROUP: Arg = Arg { short: Some(b'g'), long: "group", takes_value: TakesValue::Forbidden }; +pub static NUMERIC: Arg = Arg { short: Some(b'n'), long: "numeric", takes_value: TakesValue::Forbidden }; +pub static HEADER: Arg = Arg { short: Some(b'h'), long: "header", takes_value: TakesValue::Forbidden }; +pub static ICONS: Arg = Arg { short: None, long: "icons", takes_value: TakesValue::Optional(Some(WHEN), "auto")}; +pub static INODE: Arg = Arg { short: Some(b'i'), long: "inode", takes_value: TakesValue::Forbidden }; +pub static LINKS: Arg = Arg { short: Some(b'H'), long: "links", takes_value: TakesValue::Forbidden }; +pub static MODIFIED: Arg = Arg { short: Some(b'm'), long: "modified", takes_value: TakesValue::Forbidden }; +pub static CHANGED: Arg = Arg { short: None, long: "changed", takes_value: TakesValue::Forbidden }; +pub static BLOCKSIZE: Arg = Arg { short: Some(b'S'), long: "blocksize", takes_value: TakesValue::Forbidden }; +pub static TOTAL_SIZE: Arg = Arg { short: None, long: "total-size", takes_value: TakesValue::Forbidden }; +pub static TIME: Arg = Arg { short: Some(b't'), long: "time", takes_value: TakesValue::Necessary(Some(TIMES)) }; +pub static ACCESSED: Arg = Arg { short: Some(b'u'), long: "accessed", takes_value: TakesValue::Forbidden }; +pub static CREATED: Arg = Arg { short: Some(b'U'), long: "created", takes_value: TakesValue::Forbidden }; +pub static TIME_STYLE: Arg = Arg { short: None, long: "time-style", takes_value: TakesValue::Necessary(Some(TIME_STYLES)) }; +pub static HYPERLINK: Arg = Arg { short: None, long: "hyperlink", takes_value: TakesValue::Forbidden }; +pub static MOUNTS: Arg = Arg { short: Some(b'M'), long: "mounts", takes_value: TakesValue::Forbidden }; +pub static SMART_GROUP: Arg = Arg { short: None, long: "smart-group", takes_value: TakesValue::Forbidden }; +const TIMES: Values = &["modified", "changed", "accessed", "created"]; +const TIME_STYLES: Values = &["default", "long-iso", "full-iso", "iso", "relative"]; + +// suppressing columns +pub static NO_PERMISSIONS: Arg = Arg { short: None, long: "no-permissions", takes_value: TakesValue::Forbidden }; +pub static NO_FILESIZE: Arg = Arg { short: None, long: "no-filesize", takes_value: TakesValue::Forbidden }; +pub static NO_USER: Arg = Arg { short: None, long: "no-user", takes_value: TakesValue::Forbidden }; +pub static NO_TIME: Arg = Arg { short: None, long: "no-time", takes_value: TakesValue::Forbidden }; + +// optional feature options +pub static GIT: Arg = Arg { short: None, long: "git", takes_value: TakesValue::Forbidden }; +pub static NO_GIT: Arg = Arg { short: None, long: "no-git", takes_value: TakesValue::Forbidden }; +pub static GIT_REPOS: Arg = Arg { short: None, long: "git-repos", takes_value: TakesValue::Forbidden }; +pub static GIT_REPOS_NO_STAT: Arg = Arg { short: None, long: "git-repos-no-status", takes_value: TakesValue::Forbidden }; +pub static EXTENDED: Arg = Arg { short: Some(b'@'), long: "extended", takes_value: TakesValue::Forbidden }; +pub static OCTAL: Arg = Arg { short: Some(b'o'), long: "octal-permissions", takes_value: TakesValue::Forbidden }; +pub static SECURITY_CONTEXT: Arg = Arg { short: Some(b'Z'), long: "context", takes_value: TakesValue::Forbidden }; +pub static STDIN: Arg = Arg { short: None, long: "stdin", takes_value: TakesValue::Forbidden }; +pub static FILE_FLAGS: Arg = Arg { short: Some(b'O'), long: "flags", takes_value: TakesValue::Forbidden }; + +pub static ALL_ARGS: Args = Args(&[ + &VERSION, &HELP, + + &ONE_LINE, &LONG, &GRID, &ACROSS, &RECURSE, &TREE, &CLASSIFY, &DEREF_LINKS, &FOLLOW_LINKS, + &COLOR, &COLOUR, &COLOR_SCALE, &COLOUR_SCALE, &COLOR_SCALE_MODE, &COLOUR_SCALE_MODE, + &WIDTH, &NO_QUOTES, &ABSOLUTE, + + &ALL, &ALMOST_ALL, &LIST_DIRS, &LEVEL, &REVERSE, &SORT, &DIRS_FIRST, &DIRS_LAST, + &IGNORE_GLOB, &GIT_IGNORE, &ONLY_DIRS, &ONLY_FILES, + + &BINARY, &BYTES, &GROUP, &NUMERIC, &HEADER, &ICONS, &INODE, &LINKS, &MODIFIED, &CHANGED, + &BLOCKSIZE, &TOTAL_SIZE, &TIME, &ACCESSED, &CREATED, &TIME_STYLE, &HYPERLINK, &MOUNTS, + &NO_PERMISSIONS, &NO_FILESIZE, &NO_USER, &NO_TIME, &SMART_GROUP, &NO_SYMLINKS, &SHOW_SYMLINKS, + + &GIT, &NO_GIT, &GIT_REPOS, &GIT_REPOS_NO_STAT, + &EXTENDED, &OCTAL, &SECURITY_CONTEXT, &STDIN, &FILE_FLAGS +]); diff --git a/collector/compile-benchmarks/eza-0.21.2/src/options/help.rs b/collector/compile-benchmarks/eza-0.21.2/src/options/help.rs new file mode 100644 index 000000000..f23a2dd45 --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/src/options/help.rs @@ -0,0 +1,179 @@ +// SPDX-FileCopyrightText: 2024 Christina Sørensen +// SPDX-License-Identifier: EUPL-1.2 +// +// SPDX-FileCopyrightText: 2023-2024 Christina Sørensen, eza contributors +// SPDX-FileCopyrightText: 2014 Benjamin Sago +// SPDX-License-Identifier: MIT +use std::fmt; + +use crate::fs::feature::xattr; +use crate::options::flags; +use crate::options::parser::MatchedFlags; + +static USAGE_PART1: &str = "Usage: + eza [options] [files...] + +META OPTIONS + -?, --help show list of command-line options + -v, --version show version of eza + +DISPLAY OPTIONS + -1, --oneline display one entry per line + -l, --long display extended file metadata as a table + -G, --grid display entries as a grid (default) + -x, --across sort the grid across, rather than downwards + -R, --recurse recurse into directories + -T, --tree recurse into directories as a tree + -X, --dereference dereference symbolic links when displaying information + -F, --classify=WHEN display type indicator by file names (always, auto, never) + --colo[u]r=WHEN when to use terminal colours (always, auto, never) + --colo[u]r-scale highlight levels of 'field' distinctly(all, age, size) + --colo[u]r-scale-mode use gradient or fixed colors in --color-scale (fixed, gradient) + --icons=WHEN when to display icons (always, auto, never) + --no-quotes don't quote file names with spaces + --hyperlink display entries as hyperlinks + --absolute display entries with their absolute path (on, follow, off) + --follow-symlinks drill down into symbolic links that point to directories + -w, --width COLS set screen width in columns + + +FILTERING AND SORTING OPTIONS + -a, --all show hidden and 'dot' files. Use this twice to also + show the '.' and '..' directories + -A, --almost-all equivalent to --all; included for compatibility with `ls -A` + -d, --list-dirs list directories as files; don't list their contents + -D, --only-dirs list only directories + -f, --only-files list only files + --show-symlinks explicitly show symbolic links (for use with --only-dirs | --only-files) + --no-symlinks do not show symbolic links + -L, --level DEPTH limit the depth of recursion + -r, --reverse reverse the sort order + -s, --sort SORT_FIELD which field to sort by + --group-directories-first list directories before other files + --group-directories-last list directories after other files + -I, --ignore-glob GLOBS glob patterns (pipe-separated) of files to ignore"; + +static GIT_FILTER_HELP: &str = " \ + --git-ignore ignore files mentioned in '.gitignore'"; + +static USAGE_PART2: &str = " \ + Valid sort fields: name, Name, extension, Extension, size, type, + created, modified, accessed, changed, inode, and none. + date, time, old, and new all refer to modified. + +LONG VIEW OPTIONS + -b, --binary list file sizes with binary prefixes + -B, --bytes list file sizes in bytes, without any prefixes + -g, --group list each file's group + --smart-group only show group if it has a different name from owner + -h, --header add a header row to each column + -H, --links list each file's number of hard links + -i, --inode list each file's inode number + -M, --mounts show mount details (Linux and Mac only) + -n, --numeric list numeric user and group IDs + -O, --flags list file flags (Mac, BSD, and Windows only) + -S, --blocksize show size of allocated file system blocks + -t, --time FIELD which timestamp field to list (modified, accessed, created) + -m, --modified use the modified timestamp field + -u, --accessed use the accessed timestamp field + -U, --created use the created timestamp field + --changed use the changed timestamp field + --time-style how to format timestamps (default, iso, long-iso, + full-iso, relative, or a custom style '+' + like '+%Y-%m-%d %H:%M') + --total-size show the size of a directory as the size of all + files and directories inside (unix only) + -o, --octal-permissions list each file's permission in octal format + --no-permissions suppress the permissions field + --no-filesize suppress the filesize field + --no-user suppress the user field + --no-time suppress the time field + --stdin read file names from stdin, one per line or other separator + specified in environment"; + +static GIT_VIEW_HELP: &str = " \ + --git list each file's Git status, if tracked or ignored + --no-git suppress Git status (always overrides --git, + --git-repos, --git-repos-no-status) + --git-repos list root of git-tree status + --git-repos-no-status list each git-repos branch name (much faster) + "; +static EXTENDED_HELP: &str = " \ + -@, --extended list each file's extended attributes and sizes"; +static SECATTR_HELP: &str = " \ + -Z, --context list each file's security context"; + +/// All the information needed to display the help text, which depends +/// on which features are enabled and whether the user only wants to +/// see one section’s help. +#[derive(PartialEq, Eq, Debug, Copy, Clone)] +pub struct HelpString; + +impl HelpString { + /// Determines how to show help, if at all, based on the user’s + /// command-line arguments. This one works backwards from the other + /// ‘deduce’ functions, returning Err if help needs to be shown. + /// + /// We don’t do any strict-mode error checking here: it’s OK to give + /// the --help or --long flags more than once. Actually checking for + /// errors when the user wants help is kind of petty! + pub fn deduce(matches: &MatchedFlags<'_>) -> Option { + if matches.count(&flags::HELP) > 0 { + Some(Self) + } else { + None + } + } +} + +impl fmt::Display for HelpString { + /// Format this help options into an actual string of help + /// text to be displayed to the user. + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + write!(f, "{USAGE_PART1}")?; + + if cfg!(feature = "git") { + write!(f, "\n{GIT_FILTER_HELP}")?; + } + + write!(f, "\n{USAGE_PART2}")?; + + if cfg!(feature = "git") { + write!(f, "\n{GIT_VIEW_HELP}")?; + } + + if xattr::ENABLED { + write!(f, "\n{EXTENDED_HELP}")?; + write!(f, "\n{SECATTR_HELP}")?; + } + + writeln!(f) + } +} + +#[cfg(test)] +mod test { + use crate::options::{Options, OptionsResult}; + use std::ffi::OsStr; + + #[test] + fn help() { + let args = vec![OsStr::new("--help")]; + let opts = Options::parse(args, &None); + assert!(matches!(opts, OptionsResult::Help(_))); + } + + #[test] + fn help_with_file() { + let args = vec![OsStr::new("--help"), OsStr::new("me")]; + let opts = Options::parse(args, &None); + assert!(matches!(opts, OptionsResult::Help(_))); + } + + #[test] + fn unhelpful() { + let args = vec![]; + let opts = Options::parse(args, &None); + assert!(!matches!(opts, OptionsResult::Help(_))); // no help when --help isn’t passed + } +} diff --git a/collector/compile-benchmarks/eza-0.21.2/src/options/mod.rs b/collector/compile-benchmarks/eza-0.21.2/src/options/mod.rs new file mode 100644 index 000000000..703e8a4d3 --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/src/options/mod.rs @@ -0,0 +1,296 @@ +// SPDX-FileCopyrightText: 2024 Christina Sørensen +// SPDX-License-Identifier: EUPL-1.2 +// +// SPDX-FileCopyrightText: 2023-2024 Christina Sørensen, eza contributors +// SPDX-FileCopyrightText: 2014 Benjamin Sago +// SPDX-License-Identifier: MIT +//! Parsing command-line strings into exa options. +//! +//! This module imports exa’s configuration types, such as `View` (the details +//! of displaying multiple files) and `DirAction` (what to do when encountering +//! a directory), and implements `deduce` methods on them so they can be +//! configured using command-line options. +//! +//! +//! ## Useless and overridden options +//! +//! Let’s say exa was invoked with just one argument: `exa --inode`. The +//! `--inode` option is used in the details view, where it adds the inode +//! column to the output. But because the details view is *only* activated with +//! the `--long` argument, adding `--inode` without it would not have any +//! effect. +//! +//! For a long time, exa’s philosophy was that the user should be warned +//! whenever they could be mistaken like this. If you tell exa to display the +//! inode, and it *doesn’t* display the inode, isn’t that more annoying than +//! having it throw an error back at you? +//! +//! However, this doesn’t take into account *configuration*. Say a user wants +//! to configure exa so that it lists inodes in the details view, but otherwise +//! functions normally. A common way to do this for command-line programs is to +//! define a shell alias that specifies the details they want to use every +//! time. For the inode column, the alias would be: +//! +//! `alias exa="exa --inode"` +//! +//! Using this alias means that although the inode column will be shown in the +//! details view, you’re now *only* allowed to use the details view, as any +//! other view type will result in an error. Oops! +//! +//! Another example is when an option is specified twice, such as `exa +//! --sort=Name --sort=size`. Did the user change their mind about sorting, and +//! accidentally specify the option twice? +//! +//! Again, exa rejected this case, throwing an error back to the user instead +//! of trying to guess how they want their output sorted. And again, this +//! doesn’t take into account aliases being used to set defaults. A user who +//! wants their files to be sorted case-insensitively may configure their shell +//! with the following: +//! +//! `alias exa="exa --sort=Name"` +//! +//! Just like the earlier example, the user now can’t use any other sort order, +//! because exa refuses to guess which one they meant. It’s *more* annoying to +//! have to go back and edit the command than if there were no error. +//! +//! Fortunately, there’s a heuristic for telling which options came from an +//! alias and which came from the actual command-line: aliased options are +//! nearer the beginning of the options array, and command-line options are +//! nearer the end. This means that after the options have been parsed, exa +//! needs to traverse them *backwards* to find the last-most-specified one. +//! +//! For example, invoking exa with `exa --sort=size` when that alias is present +//! would result in a full command-line of: +//! +//! `exa --sort=Name --sort=size` +//! +//! `--sort=size` should override `--sort=Name` because it’s closer to the end +//! of the arguments array. In fact, because there’s no way to tell where the +//! arguments came from — it’s just a heuristic — this will still work even +//! if no aliases are being used! +//! +//! Finally, this isn’t just useful when options could override each other. +//! Creating an alias `exal="exa --long --inode --header"` then invoking `exal +//! --grid --long` shouldn’t complain about `--long` being given twice when +//! it’s clear what the user wants. + +use std::ffi::OsStr; + +use crate::fs::dir_action::DirAction; +use crate::fs::filter::{FileFilter, GitIgnore}; +use crate::options::stdin::FilesInput; +use crate::output::{details, grid_details, Mode, View}; +use crate::theme::Options as ThemeOptions; + +mod dir_action; +mod file_name; +mod filter; + +mod error; +#[rustfmt::skip] +mod flags; +mod theme; +mod view; + +pub use self::error::{NumberSource, OptionsError}; + +mod help; +use self::help::HelpString; + +mod parser; +use self::parser::MatchedFlags; + +pub mod vars; +pub use self::vars::Vars; +pub mod config; +pub mod stdin; +mod version; + +use self::version::VersionString; + +/// These **options** represent a parsed, error-checked versions of the +/// user’s command-line options. +#[derive(Debug)] +pub struct Options { + /// The action to perform when encountering a directory rather than a + /// regular file. + pub dir_action: DirAction, + + /// How to sort and filter files before outputting them. + pub filter: FileFilter, + + /// The user’s preference of view to use (lines, grid, details, or + /// grid-details) along with the options on how to render file names. + /// If the view requires the terminal to have a width, and there is no + /// width, then the view will be downgraded. + pub view: View, + + /// The options to make up the styles of the UI and file names. + pub theme: ThemeOptions, + + /// Whether to read file names from stdin instead of the command-line + pub stdin: FilesInput, +} + +impl Options { + /// Parse the given iterator of command-line strings into an Options + /// struct and a list of free filenames, using the environment variables + /// for extra options. + #[allow(unused_results)] + pub fn parse<'args, I, V>(args: I, vars: &V) -> OptionsResult<'args> + where + I: IntoIterator, + V: Vars, + { + use crate::options::parser::{Matches, Strictness}; + + #[rustfmt::skip] + let strictness = match vars.get_with_fallback(vars::EZA_STRICT, vars::EXA_STRICT) { + None => Strictness::UseLastArguments, + Some(ref t) if t.is_empty() => Strictness::UseLastArguments, + Some(_) => Strictness::ComplainAboutRedundantArguments, + }; + + let Matches { flags, frees } = match flags::ALL_ARGS.parse(args, strictness) { + Ok(m) => m, + Err(pe) => return OptionsResult::InvalidOptions(OptionsError::Parse(pe)), + }; + + if let Some(help) = HelpString::deduce(&flags) { + return OptionsResult::Help(help); + } + + if let Some(version) = VersionString::deduce(&flags) { + return OptionsResult::Version(version); + } + + match Self::deduce(&flags, vars) { + Ok(options) => OptionsResult::Ok(options, frees), + Err(oe) => OptionsResult::InvalidOptions(oe), + } + } + + /// Whether the View specified in this set of options includes a Git + /// status column. It’s only worth trying to discover a repository if the + /// results will end up being displayed. + pub fn should_scan_for_git(&self) -> bool { + if self.filter.git_ignore == GitIgnore::CheckAndIgnore { + return true; + } + + match self.view.mode { + Mode::Details(details::Options { + table: Some(ref table), + .. + }) + | Mode::GridDetails(grid_details::Options { + details: + details::Options { + table: Some(ref table), + .. + }, + .. + }) => table.columns.git, + _ => false, + } + } + + /// Determines the complete set of options based on the given command-line + /// arguments, after they’ve been parsed. + fn deduce(matches: &MatchedFlags<'_>, vars: &V) -> Result { + if cfg!(not(feature = "git")) + && matches + .has_where_any(|f| f.matches(&flags::GIT) || f.matches(&flags::GIT_IGNORE)) + .is_some() + { + return Err(OptionsError::Unsupported(String::from( + "Options --git and --git-ignore can't be used because `git` feature was disabled in this build of exa" + ))); + } + let view = View::deduce(matches, vars)?; + let dir_action = DirAction::deduce(matches, matches!(view.mode, Mode::Details(_)))?; + let filter = FileFilter::deduce(matches)?; + let theme = ThemeOptions::deduce(matches, vars)?; + let stdin = FilesInput::deduce(matches, vars)?; + + Ok(Self { + dir_action, + filter, + view, + theme, + stdin, + }) + } +} + +/// The result of the `Options::parse` function. +/// +/// NOTE: We disallow the `large_enum_variant` lint here, because we're not +/// overly concerned about variant fragmentation. We can do this because we are +/// reasonably sure that the error variant will be rare, and only on faulty +/// program execution and thus boxing the large variant will be a waste of +/// resources, but should we come to use it more, we should reconsider. +/// +/// See +#[allow(clippy::large_enum_variant)] +#[derive(Debug)] +pub enum OptionsResult<'args> { + /// The options were parsed successfully. + Ok(Options, Vec<&'args OsStr>), + + /// There was an error parsing the arguments. + InvalidOptions(OptionsError), + + /// One of the arguments was `--help`, so display help. + Help(HelpString), + + /// One of the arguments was `--version`, so display the version number. + Version(VersionString), +} + +#[cfg(test)] +pub mod test { + use crate::options::parser::{Arg, MatchedFlags}; + use std::ffi::OsStr; + + #[derive(PartialEq, Eq, Debug, Copy, Clone)] + pub enum Strictnesses { + Last, + Complain, + Both, + } + + /// This function gets used by the other testing modules. + /// It can run with one or both strictness values: if told to run with + /// both, then both should resolve to the same result. + /// + /// It returns a vector with one or two elements in. + /// These elements can then be tested with `assert_eq` or what have you. + pub fn parse_for_test( + inputs: &[&str], + args: &'static [&'static Arg], + strictnesses: Strictnesses, + get: F, + ) -> Vec + where + F: Fn(&MatchedFlags<'_>) -> T, + { + use self::Strictnesses::*; + use crate::options::parser::{Args, Strictness}; + + let bits = inputs.iter().map(OsStr::new).collect::>(); + let mut result = Vec::new(); + + if strictnesses == Last || strictnesses == Both { + let results = Args(args).parse(bits.clone(), Strictness::UseLastArguments); + result.push(get(&results.unwrap().flags)); + } + + if strictnesses == Complain || strictnesses == Both { + let results = Args(args).parse(bits, Strictness::ComplainAboutRedundantArguments); + result.push(get(&results.unwrap().flags)); + } + + result + } +} diff --git a/collector/compile-benchmarks/eza-0.21.2/src/options/parser.rs b/collector/compile-benchmarks/eza-0.21.2/src/options/parser.rs new file mode 100644 index 000000000..8dbd7edd9 --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/src/options/parser.rs @@ -0,0 +1,821 @@ +// SPDX-FileCopyrightText: 2024 Christina Sørensen +// SPDX-License-Identifier: EUPL-1.2 +// +// SPDX-FileCopyrightText: 2023-2024 Christina Sørensen, eza contributors +// SPDX-FileCopyrightText: 2014 Benjamin Sago +// SPDX-License-Identifier: MIT +//! A general parser for command-line options. +//! +//! exa uses its own hand-rolled parser for command-line options. It supports +//! the following syntax: +//! +//! - Long options: `--inode`, `--grid` +//! - Long options with values: `--sort size`, `--level=4` +//! - Short options: `-i`, `-G` +//! - Short options with values: `-ssize`, `-L=4` +//! +//! These values can be mixed and matched: `exa -lssize --grid`. If you’ve used +//! other command-line programs, then hopefully it’ll work much like them. +//! +//! Because exa already has its own files for the help text, shell completions, +//! man page, and readme, so it can get away with having the options parser do +//! very little: all it really needs to do is parse a slice of strings. +//! +//! +//! ## UTF-8 and `OsStr` +//! +//! The parser uses `OsStr` as its string type. This is necessary for exa to +//! list files that have invalid UTF-8 in their names: by treating file paths +//! as bytes with no encoding, a file can be specified on the command-line and +//! be looked up without having to be encoded into a `str` first. +//! +//! It also avoids the overhead of checking for invalid UTF-8 when parsing +//! command-line options, as all the options and their values (such as +//! `--sort size`) are guaranteed to just be 8-bit ASCII. + +use std::ffi::{OsStr, OsString}; +use std::fmt; + +use crate::options::error::{Choices, OptionsError}; + +/// A **short argument** is a single ASCII character. +pub type ShortArg = u8; + +/// A **long argument** is a string. This can be a UTF-8 string, even though +/// the arguments will all be unchecked `OsString` values, because we don’t +/// actually store the user’s input after it’s been matched to a flag, we just +/// store which flag it was. +pub type LongArg = &'static str; + +/// A **list of values** that an option can have, to be displayed when the +/// user enters an invalid one or skips it. +/// +/// This is literally just help text, and won’t be used to validate a value to +/// see if it’s correct. +pub type Values = &'static [&'static str]; + +/// A **flag** is either of the two argument types, because they have to +/// be in the same array together. +#[derive(PartialEq, Eq, Debug, Copy, Clone)] +pub enum Flag { + Short(ShortArg), + Long(LongArg), +} + +impl Flag { + pub fn matches(&self, arg: &Arg) -> bool { + match self { + Self::Short(short) => arg.short == Some(*short), + Self::Long(long) => arg.long == *long, + } + } +} + +impl fmt::Display for Flag { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + match self { + Self::Short(short) => write!(f, "-{}", *short as char), + Self::Long(long) => write!(f, "--{long}"), + } + } +} + +/// Whether redundant arguments should be considered a problem. +#[derive(PartialEq, Eq, Debug, Copy, Clone)] +pub enum Strictness { + /// Throw an error when an argument doesn’t do anything, either because + /// it requires another argument to be specified, or because two conflict. + ComplainAboutRedundantArguments, + + /// Search the arguments list back-to-front, giving ones specified later + /// in the list priority over earlier ones. + UseLastArguments, +} + +/// Whether a flag takes a value. This is applicable to both long and short +/// arguments. +#[derive(PartialEq, Eq, Debug, Copy, Clone)] +pub enum TakesValue { + /// This flag has to be followed by a value. + /// If there’s a fixed set of possible values, they can be printed out + /// with the error text. + Necessary(Option), + + /// This flag will throw an error if there’s a value after it. + Forbidden, + + /// This flag may be followed by a value to override its defaults + Optional(Option, &'static str), +} + +/// An **argument** can be matched by one of the user’s input strings. +#[derive(PartialEq, Eq, Debug, Copy, Clone)] +pub struct Arg { + /// The short argument that matches it, if any. + pub short: Option, + + /// The long argument that matches it. This is non-optional; all flags + /// should at least have a descriptive long name. + pub long: LongArg, + + /// Whether this flag takes a value or not. + pub takes_value: TakesValue, +} + +impl fmt::Display for Arg { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + write!(f, "--{}", self.long)?; + + if let Some(short) = self.short { + write!(f, " (-{})", short as char)?; + } + + Ok(()) + } +} + +/// Literally just several args. +#[derive(PartialEq, Eq, Debug)] +pub struct Args(pub &'static [&'static Arg]); + +impl Args { + /// Iterates over the given list of command-line arguments and parses + /// them into a list of matched flags and free strings. + pub fn parse<'args, I>( + &self, + inputs: I, + strictness: Strictness, + ) -> Result, ParseError> + where + I: IntoIterator, + { + let mut parsing = true; + + // The results that get built up. + let mut result_flags = Vec::new(); + let mut frees: Vec<&OsStr> = Vec::new(); + + // Iterate over the inputs with “while let” because we need to advance + // the iterator manually whenever an argument that takes a value + // doesn’t have one in its string so it needs the next one. + let mut inputs = inputs.into_iter().peekable(); + while let Some(arg) = inputs.next() { + let bytes = os_str_to_bytes(arg); + + // Stop parsing if one of the arguments is the literal string “--”. + // This allows a file named “--arg” to be specified by passing in + // the pair “-- --arg”, without it getting matched as a flag that + // doesn’t exist. + if !parsing { + frees.push(arg); + } else if arg == "--" { + parsing = false; + } + // If the string starts with *two* dashes then it’s a long argument. + else if bytes.starts_with(b"--") { + let long_arg_name = bytes_to_os_str(&bytes[2..]); + + // If there’s an equals in it, then the string before the + // equals will be the flag’s name, and the string after it + // will be its value. + if let Some((before, after)) = split_on_equals(long_arg_name) { + let arg = self.lookup_long(before)?; + let flag = Flag::Long(arg.long); + match arg.takes_value { + TakesValue::Necessary(_) | TakesValue::Optional(_, _) => { + result_flags.push((flag, Some(after))); + } + TakesValue::Forbidden => return Err(ParseError::ForbiddenValue { flag }), + } + } + // If there’s no equals, then the entire string (apart from + // the dashes) is the argument name. + else { + let arg = self.lookup_long(long_arg_name)?; + let flag = Flag::Long(arg.long); + match arg.takes_value { + TakesValue::Forbidden => { + result_flags.push((flag, None)); + } + TakesValue::Necessary(values) => { + if let Some(next_arg) = inputs.next() { + result_flags.push((flag, Some(next_arg))); + } else { + return Err(ParseError::NeedsValue { flag, values }); + } + } + TakesValue::Optional(values, default) => match inputs.peek() { + Some(next_arg) if is_optional_arg(next_arg, values) => { + result_flags.push((flag, Some(inputs.next().unwrap()))); + } + _ => { + result_flags + .push((flag, Some(bytes_to_os_str(default.as_bytes())))); + } + }, + } + } + } + // If the string starts with *one* dash then it’s one or more + // short arguments. + else if bytes.starts_with(b"-") && arg != "-" { + let short_arg = bytes_to_os_str(&bytes[1..]); + + // If there’s an equals in it, then the argument immediately + // before the equals was the one that has the value, with the + // others (if any) as value-less short ones. + // + // -x=abc => ‘x=abc’ + // -abcdx=fgh => ‘a’, ‘b’, ‘c’, ‘d’, ‘x=fgh’ + // -x= => error + // -abcdx= => error + // + // There’s no way to give two values in a cluster like this: + // it’s an error if any of the first set of arguments actually + // takes a value. + if let Some((before, after)) = split_on_equals(short_arg) { + let (arg_with_value, other_args) = + os_str_to_bytes(before).split_last().unwrap(); + + // Process the characters immediately following the dash... + for byte in other_args { + let arg = self.lookup_short(*byte)?; + let flag = Flag::Short(*byte); + match arg.takes_value { + TakesValue::Forbidden => { + result_flags.push((flag, None)); + } + TakesValue::Optional(_, default) => { + result_flags + .push((flag, Some(bytes_to_os_str(default.as_bytes())))); + } + TakesValue::Necessary(values) => { + return Err(ParseError::NeedsValue { flag, values }); + } + } + } + + // ...then the last one and the value after the equals. + let arg = self.lookup_short(*arg_with_value)?; + let flag = Flag::Short(arg.short.unwrap()); + match arg.takes_value { + TakesValue::Necessary(_) | TakesValue::Optional(_, _) => { + result_flags.push((flag, Some(after))); + } + TakesValue::Forbidden => { + return Err(ParseError::ForbiddenValue { flag }); + } + } + } + // If there’s no equals, then every character is parsed as + // its own short argument. However, if any of the arguments + // takes a value, then the *rest* of the string is used as + // its value, and if there’s no rest of the string, then it + // uses the next one in the iterator. + // + // -a => ‘a’ + // -abc => ‘a’, ‘b’, ‘c’ + // -abxdef => ‘a’, ‘b’, ‘x=def’ + // -abx def => ‘a’, ‘b’, ‘x=def’ + // -abx => error + // + else { + for (index, byte) in bytes.iter().enumerate().skip(1) { + let arg = self.lookup_short(*byte)?; + let flag = Flag::Short(*byte); + match arg.takes_value { + TakesValue::Forbidden => { + result_flags.push((flag, None)); + } + TakesValue::Necessary(values) => { + if index < bytes.len() - 1 { + let remnants = &bytes[index + 1..]; + result_flags.push((flag, Some(bytes_to_os_str(remnants)))); + break; + } else if let Some(next_arg) = inputs.next() { + result_flags.push((flag, Some(next_arg))); + } else { + match arg.takes_value { + TakesValue::Forbidden | TakesValue::Optional(_, _) => { + unreachable!() + } + TakesValue::Necessary(_) => { + return Err(ParseError::NeedsValue { flag, values }); + } + } + } + } + TakesValue::Optional(values, default) => { + if index < bytes.len() - 1 { + let remnants = bytes_to_os_str(&bytes[index + 1..]); + if is_optional_arg(remnants, values) { + result_flags.push((flag, Some(remnants))); + } else { + return Err(ParseError::ForbiddenValue { flag }); + } + break; + } else if let Some(next_arg) = inputs.peek() { + if is_optional_arg(next_arg, values) { + result_flags.push((flag, Some(inputs.next().unwrap()))); + } else { + result_flags.push((flag, Some(OsStr::new(default)))); + } + } else { + match arg.takes_value { + TakesValue::Forbidden | TakesValue::Necessary(_) => { + unreachable!() + } + TakesValue::Optional(_, default) => { + result_flags.push((flag, Some(OsStr::new(default)))); + } + } + } + } + } + } + } + } + // Otherwise, it’s a free string, usually a file name. + else { + frees.push(arg); + } + } + + Ok(Matches { + frees, + flags: MatchedFlags { + flags: result_flags, + strictness, + }, + }) + } + + fn lookup_short(&self, short: ShortArg) -> Result<&Arg, ParseError> { + match self.0.iter().find(|arg| arg.short == Some(short)) { + Some(arg) => Ok(arg), + None => Err(ParseError::UnknownShortArgument { attempt: short }), + } + } + + fn lookup_long(&self, long: &OsStr) -> Result<&Arg, ParseError> { + match self.0.iter().find(|arg| arg.long == long) { + Some(arg) => Ok(arg), + None => Err(ParseError::UnknownArgument { + attempt: long.to_os_string(), + }), + } + } +} + +fn is_optional_arg(value: &OsStr, values: Option<&[&str]>) -> bool { + match (values, value.to_str()) { + (Some(values), Some(value)) => values.contains(&value), + _ => false, + } +} + +/// The **matches** are the result of parsing the user’s command-line strings. +#[derive(PartialEq, Eq, Debug)] +pub struct Matches<'args> { + /// The flags that were parsed from the user’s input. + pub flags: MatchedFlags<'args>, + + /// All the strings that weren’t matched as arguments, as well as anything + /// after the special “--” string. + pub frees: Vec<&'args OsStr>, +} + +#[derive(PartialEq, Eq, Debug)] +pub struct MatchedFlags<'args> { + /// The individual flags from the user’s input, in the order they were + /// originally given. + /// + /// Long and short arguments need to be kept in the same vector because + /// we usually want the one nearest the end to count, and to know this, + /// we need to know where they are in relation to one another. + flags: Vec<(Flag, Option<&'args OsStr>)>, + + /// Whether to check for duplicate or redundant arguments. + strictness: Strictness, +} + +impl<'a> MatchedFlags<'a> { + /// Whether the given argument was specified. + /// Returns `true` if it was, `false` if it wasn’t, and an error in + /// strict mode if it was specified more than once. + pub fn has(&self, arg: &'static Arg) -> Result { + self.has_where(|flag| flag.matches(arg)) + .map(|flag| flag.is_some()) + } + + /// Returns the first found argument that satisfies the predicate, or + /// nothing if none is found, or an error in strict mode if multiple + /// argument satisfy the predicate. + /// + /// You’ll have to test the resulting flag to see which argument it was. + pub fn has_where

(&self, predicate: P) -> Result, OptionsError> + where + P: Fn(&Flag) -> bool, + { + if self.is_strict() { + let all = self + .flags + .iter() + .filter(|tuple| tuple.1.is_none() && predicate(&tuple.0)) + .collect::>(); + + if all.len() < 2 { + Ok(all.first().map(|t| &t.0)) + } else { + Err(OptionsError::Duplicate(all[0].0, all[1].0)) + } + } else { + Ok(self.has_where_any(predicate)) + } + } + + /// Returns the first found argument that satisfies the predicate, or + /// nothing if none is found, with strict mode having no effect. + /// + /// You’ll have to test the resulting flag to see which argument it was. + pub fn has_where_any

(&self, predicate: P) -> Option<&Flag> + where + P: Fn(&Flag) -> bool, + { + self.flags + .iter() + .rev() + .find(|tuple| tuple.1.is_none() && predicate(&tuple.0)) + .map(|tuple| &tuple.0) + } + + // This code could probably be better. + // Both ‘has’ and ‘get’ immediately begin with a conditional, which makes + // me think the functionality could be moved to inside Strictness. + + /// Returns the value of the given argument if it was specified, nothing + /// if it wasn’t, and an error in strict mode if it was specified more + /// than once. + pub fn get(&self, arg: &'static Arg) -> Result, OptionsError> { + self.get_where(|flag| flag.matches(arg)) + } + + /// Returns the value of the argument that matches the predicate if it + /// was specified, nothing if it wasn’t, and an error in strict mode if + /// multiple arguments matched the predicate. + /// + /// It’s not possible to tell which flag the value belonged to from this. + pub fn get_where

(&self, predicate: P) -> Result, OptionsError> + where + P: Fn(&Flag) -> bool, + { + if self.is_strict() { + let those = self + .flags + .iter() + .filter(|tuple| tuple.1.is_some() && predicate(&tuple.0)) + .collect::>(); + + if those.len() < 2 { + Ok(those.first().copied().map(|t| t.1.unwrap())) + } else { + Err(OptionsError::Duplicate(those[0].0, those[1].0)) + } + } else { + let found = self + .flags + .iter() + .rev() + .find(|tuple| tuple.1.is_some() && predicate(&tuple.0)) + .map(|tuple| tuple.1.unwrap()); + Ok(found) + } + } + + // It’s annoying that ‘has’ and ‘get’ won’t work when accidentally given + // flags that do/don’t take values, but this should be caught by tests. + + /// Counts the number of occurrences of the given argument, even in + /// strict mode. + pub fn count(&self, arg: &Arg) -> usize { + self.flags + .iter() + .filter(|tuple| tuple.0.matches(arg)) + .count() + } + + /// Checks whether strict mode is on. This is usually done from within + /// ‘has’ and ‘get’, but it’s available in an emergency. + pub fn is_strict(&self) -> bool { + self.strictness == Strictness::ComplainAboutRedundantArguments + } +} + +/// A problem with the user’s input that meant it couldn’t be parsed into a +/// coherent list of arguments. +#[derive(PartialEq, Eq, Debug)] +pub enum ParseError { + /// A flag that has to take a value was not given one. + NeedsValue { flag: Flag, values: Option }, + + /// A flag that can’t take a value *was* given one. + ForbiddenValue { flag: Flag }, + + /// A short argument, either alone or in a cluster, was not + /// recognised by the program. + UnknownShortArgument { attempt: ShortArg }, + + /// A long argument was not recognised by the program. + /// We don’t have a known &str version of the flag, so + /// this may not be valid UTF-8. + UnknownArgument { attempt: OsString }, +} + +impl fmt::Display for ParseError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::NeedsValue { flag, values: None } => write!(f, "Flag {flag} needs a value"), + Self::NeedsValue { + flag, + values: Some(cs), + } => write!(f, "Flag {flag} needs a value ({})", Choices(cs)), + Self::ForbiddenValue { flag } => write!(f, "Flag {flag} cannot take a value"), + Self::UnknownShortArgument { attempt } => { + write!(f, "Unknown argument -{}", *attempt as char) + } + Self::UnknownArgument { attempt } => { + write!(f, "Unknown argument --{}", attempt.to_string_lossy()) + } + } + } +} + +#[cfg(unix)] +fn os_str_to_bytes(s: &OsStr) -> &[u8] { + use std::os::unix::ffi::OsStrExt; + + return s.as_bytes(); +} + +#[cfg(unix)] +fn bytes_to_os_str(b: &[u8]) -> &OsStr { + use std::os::unix::ffi::OsStrExt; + + return OsStr::from_bytes(b); +} + +#[cfg(windows)] +fn os_str_to_bytes(s: &OsStr) -> &[u8] { + return s.to_str().unwrap().as_bytes(); +} + +#[cfg(windows)] +fn bytes_to_os_str(b: &[u8]) -> &OsStr { + use std::str; + + return OsStr::new(str::from_utf8(b).unwrap()); +} + +/// Splits a string on its `=` character, returning the two substrings on +/// either side. Returns `None` if there’s no equals or a string is missing. +fn split_on_equals(input: &OsStr) -> Option<(&OsStr, &OsStr)> { + if let Some(index) = os_str_to_bytes(input).iter().position(|elem| *elem == b'=') { + let (before, after) = os_str_to_bytes(input).split_at(index); + + // The after string contains the = that we need to remove. + if !before.is_empty() && after.len() >= 2 { + return Some((bytes_to_os_str(before), bytes_to_os_str(&after[1..]))); + } + } + + None +} + +#[cfg(test)] +mod split_test { + use super::split_on_equals; + use std::ffi::{OsStr, OsString}; + + macro_rules! test_split { + ($name:ident: $input:expr => None) => { + #[test] + fn $name() { + assert_eq!(split_on_equals(&OsString::from($input)), None); + } + }; + + ($name:ident: $input:expr => $before:expr, $after:expr) => { + #[test] + fn $name() { + assert_eq!( + split_on_equals(&OsString::from($input)), + Some((OsStr::new($before), OsStr::new($after))) + ); + } + }; + } + + test_split!(empty: "" => None); + test_split!(letter: "a" => None); + + test_split!(just: "=" => None); + test_split!(intro: "=bbb" => None); + test_split!(denou: "aaa=" => None); + test_split!(equals: "aaa=bbb" => "aaa", "bbb"); + + test_split!(sort: "--sort=size" => "--sort", "size"); + test_split!(more: "this=that=other" => "this", "that=other"); +} + +#[cfg(test)] +mod parse_test { + use super::*; + + macro_rules! test { + ($name:ident: $inputs:expr => frees: $frees:expr, flags: $flags:expr) => { + #[test] + fn $name() { + let inputs: &[&'static str] = $inputs.as_ref(); + let inputs = inputs.iter().map(OsStr::new); + + let frees: &[&'static str] = $frees.as_ref(); + let frees = frees.iter().map(OsStr::new).collect(); + + let flags = <[_]>::into_vec(Box::new($flags)); + + let strictness = Strictness::UseLastArguments; // this isn’t even used + let got = Args(TEST_ARGS).parse(inputs, strictness); + let flags = MatchedFlags { flags, strictness }; + + let expected = Ok(Matches { frees, flags }); + assert_eq!(got, expected); + } + }; + + ($name:ident: $inputs:expr => error $error:expr) => { + #[test] + fn $name() { + use self::ParseError::*; + + let inputs = $inputs.iter().map(OsStr::new); + + let strictness = Strictness::UseLastArguments; // this isn’t even used + let got = Args(TEST_ARGS).parse(inputs, strictness); + assert_eq!(got, Err($error)); + } + }; + } + + const SUGGESTIONS: Values = &["example"]; + + #[rustfmt::skip] + static TEST_ARGS: &[&Arg] = &[ + &Arg { short: Some(b'l'), long: "long", takes_value: TakesValue::Forbidden }, + &Arg { short: Some(b'v'), long: "verbose", takes_value: TakesValue::Forbidden }, + &Arg { short: Some(b'c'), long: "count", takes_value: TakesValue::Necessary(None) }, + &Arg { short: Some(b't'), long: "type", takes_value: TakesValue::Necessary(Some(SUGGESTIONS))}, + &Arg { short: Some(b'o'), long: "optional", takes_value: TakesValue::Optional(Some(&["all", "some", "none"]), "all")} + ]; + + // Just filenames + test!(empty: [] => frees: [], flags: []); + test!(one_arg: ["exa"] => frees: [ "exa" ], flags: []); + + // Dashes and double dashes + test!(one_dash: ["-"] => frees: [ "-" ], flags: []); + test!(two_dashes: ["--"] => frees: [], flags: []); + test!(two_file: ["--", "file"] => frees: [ "file" ], flags: []); + test!(two_arg_l: ["--", "--long"] => frees: [ "--long" ], flags: []); + test!(two_arg_s: ["--", "-l"] => frees: [ "-l" ], flags: []); + + // Long args + test!(long: ["--long"] => frees: [], flags: [ (Flag::Long("long"), None) ]); + test!(long_then: ["--long", "4"] => frees: [ "4" ], flags: [ (Flag::Long("long"), None) ]); + test!(long_two: ["--long", "--verbose"] => frees: [], flags: [ (Flag::Long("long"), None), (Flag::Long("verbose"), None) ]); + + // Long args with values + test!(bad_equals: ["--long=equals"] => error ForbiddenValue { flag: Flag::Long("long") }); + test!(no_arg: ["--count"] => error NeedsValue { flag: Flag::Long("count"), values: None }); + test!(arg_equals: ["--count=4"] => frees: [], flags: [ (Flag::Long("count"), Some(OsStr::new("4"))) ]); + test!(arg_then: ["--count", "4"] => frees: [], flags: [ (Flag::Long("count"), Some(OsStr::new("4"))) ]); + + // Long args with values and suggestions + test!(no_arg_s: ["--type"] => error NeedsValue { flag: Flag::Long("type"), values: Some(SUGGESTIONS) }); + test!(arg_equals_s: ["--type=exa"] => frees: [], flags: [ (Flag::Long("type"), Some(OsStr::new("exa"))) ]); + test!(arg_then_s: ["--type", "exa"] => frees: [], flags: [ (Flag::Long("type"), Some(OsStr::new("exa"))) ]); + + // Short args + test!(short: ["-l"] => frees: [], flags: [ (Flag::Short(b'l'), None) ]); + test!(short_then: ["-l", "4"] => frees: [ "4" ], flags: [ (Flag::Short(b'l'), None) ]); + test!(short_two: ["-lv"] => frees: [], flags: [ (Flag::Short(b'l'), None), (Flag::Short(b'v'), None) ]); + test!(mixed: ["-v", "--long"] => frees: [], flags: [ (Flag::Short(b'v'), None), (Flag::Long("long"), None) ]); + + // Short args with values + test!(bad_short: ["-l=equals"] => error ForbiddenValue { flag: Flag::Short(b'l') }); + test!(short_none: ["-c"] => error NeedsValue { flag: Flag::Short(b'c'), values: None }); + test!(short_arg_eq: ["-c=4"] => frees: [], flags: [(Flag::Short(b'c'), Some(OsStr::new("4"))) ]); + test!(short_arg_then: ["-c", "4"] => frees: [], flags: [(Flag::Short(b'c'), Some(OsStr::new("4"))) ]); + test!(short_two_together: ["-lctwo"] => frees: [], flags: [(Flag::Short(b'l'), None), (Flag::Short(b'c'), Some(OsStr::new("two"))) ]); + test!(short_two_equals: ["-lc=two"] => frees: [], flags: [(Flag::Short(b'l'), None), (Flag::Short(b'c'), Some(OsStr::new("two"))) ]); + test!(short_two_next: ["-lc", "two"] => frees: [], flags: [(Flag::Short(b'l'), None), (Flag::Short(b'c'), Some(OsStr::new("two"))) ]); + + // Short args with values and suggestions + test!(short_none_s: ["-t"] => error NeedsValue { flag: Flag::Short(b't'), values: Some(SUGGESTIONS) }); + test!(short_two_together_s: ["-texa"] => frees: [], flags: [(Flag::Short(b't'), Some(OsStr::new("exa"))) ]); + test!(short_two_equals_s: ["-t=exa"] => frees: [], flags: [(Flag::Short(b't'), Some(OsStr::new("exa"))) ]); + test!(short_two_next_s: ["-t", "exa"] => frees: [], flags: [(Flag::Short(b't'), Some(OsStr::new("exa"))) ]); + + // Unknown args + test!(unknown_long: ["--quiet"] => error UnknownArgument { attempt: OsString::from("quiet") }); + test!(unknown_long_eq: ["--quiet=shhh"] => error UnknownArgument { attempt: OsString::from("quiet") }); + test!(unknown_short: ["-q"] => error UnknownShortArgument { attempt: b'q' }); + test!(unknown_short_2nd: ["-lq"] => error UnknownShortArgument { attempt: b'q' }); + test!(unknown_short_eq: ["-q=shhh"] => error UnknownShortArgument { attempt: b'q' }); + test!(unknown_short_2nd_eq: ["-lq=shhh"] => error UnknownShortArgument { attempt: b'q' }); + + // Optional args + test!(optional: ["--optional"] => frees: [], flags: [(Flag::Long("optional"), Some(OsStr::new("all")))]); + test!(optional_2: ["--optional", "-l"] => frees: [], flags: [ (Flag::Long("optional"), Some(OsStr::new("all"))), (Flag::Short(b'l'), None)]); + test!(optional_3: ["--optional", "path"] => frees: ["path"], flags: [(Flag::Long("optional"), Some(OsStr::new("all")))]); + test!(optional_with_eq: ["--optional=none"] => frees: [], flags: [(Flag::Long("optional"), Some(OsStr::new("none")))]); + test!(optional_wo_eq: ["--optional", "none"] => frees: [], flags: [(Flag::Long("optional"), Some(OsStr::new("none")))]); + test!(short_opt: ["-o"] => frees: [], flags: [(Flag::Short(b'o'), Some(OsStr::new("all")))]); + test!(short_opt_value: ["-onone"] => frees: [], flags: [(Flag::Short(b'o'), Some(OsStr::new("none")))]); + test!(short_forbidden: ["-opath"] => error ForbiddenValue { flag: Flag::Short(b'o') }); + test!(short_allowed: ["-o","path"] => frees: ["path"], flags: [(Flag::Short(b'o'), Some(OsStr::new("all")))]); +} + +#[cfg(test)] +mod matches_test { + use super::*; + + macro_rules! test { + ($name:ident: $input:expr, has $param:expr => $result:expr) => { + #[test] + fn $name() { + let flags = MatchedFlags { + flags: $input.to_vec(), + strictness: Strictness::UseLastArguments, + }; + + assert_eq!(flags.has(&$param), Ok($result)); + } + }; + } + + static VERBOSE: Arg = Arg { + short: Some(b'v'), + long: "verbose", + takes_value: TakesValue::Forbidden, + }; + static COUNT: Arg = Arg { + short: Some(b'c'), + long: "count", + takes_value: TakesValue::Necessary(None), + }; + + test!(short_never: [], has VERBOSE => false); + test!(short_once: [(Flag::Short(b'v'), None)], has VERBOSE => true); + test!(short_twice: [(Flag::Short(b'v'), None), (Flag::Short(b'v'), None)], has VERBOSE => true); + test!(long_once: [(Flag::Long("verbose"), None)], has VERBOSE => true); + test!(long_twice: [(Flag::Long("verbose"), None), (Flag::Long("verbose"), None)], has VERBOSE => true); + test!(long_mixed: [(Flag::Long("verbose"), None), (Flag::Short(b'v'), None)], has VERBOSE => true); + + #[test] + fn only_count() { + let everything = OsString::from("everything"); + + let flags = MatchedFlags { + flags: vec![(Flag::Short(b'c'), Some(&*everything))], + strictness: Strictness::UseLastArguments, + }; + + assert_eq!(flags.get(&COUNT), Ok(Some(&*everything))); + } + + #[test] + fn rightmost_count() { + let everything = OsString::from("everything"); + let nothing = OsString::from("nothing"); + + let flags = MatchedFlags { + flags: vec![ + (Flag::Short(b'c'), Some(&*everything)), + (Flag::Short(b'c'), Some(&*nothing)), + ], + strictness: Strictness::UseLastArguments, + }; + + assert_eq!(flags.get(&COUNT), Ok(Some(&*nothing))); + } + + #[test] + fn no_count() { + let flags = MatchedFlags { + flags: Vec::new(), + strictness: Strictness::UseLastArguments, + }; + + assert!(!flags.has(&COUNT).unwrap()); + } +} diff --git a/collector/compile-benchmarks/eza-0.21.2/src/options/stdin.rs b/collector/compile-benchmarks/eza-0.21.2/src/options/stdin.rs new file mode 100644 index 000000000..c94ab68d4 --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/src/options/stdin.rs @@ -0,0 +1,35 @@ +// SPDX-FileCopyrightText: 2024 Christina Sørensen +// SPDX-License-Identifier: EUPL-1.2 +// +// SPDX-FileCopyrightText: 2023-2024 Christina Sørensen, eza contributors +// SPDX-FileCopyrightText: 2014 Benjamin Sago +// SPDX-License-Identifier: MIT +use crate::options::parser::MatchedFlags; +use crate::options::vars::EZA_STDIN_SEPARATOR; +use crate::options::{flags, OptionsError, Vars}; +use std::ffi::OsString; +use std::io; +use std::io::IsTerminal; + +#[derive(Debug, PartialEq)] +pub enum FilesInput { + Stdin(OsString), + Args, +} + +impl FilesInput { + pub fn deduce(matches: &MatchedFlags<'_>, vars: &V) -> Result { + Ok( + if io::stdin().is_terminal() || !matches.has(&flags::STDIN)? { + FilesInput::Args + } else if matches.has(&flags::STDIN)? && !io::stdin().is_terminal() { + let separator = vars + .get(EZA_STDIN_SEPARATOR) + .unwrap_or(OsString::from("\n")); + FilesInput::Stdin(separator) + } else { + FilesInput::Args + }, + ) + } +} diff --git a/collector/compile-benchmarks/eza-0.21.2/src/options/theme.rs b/collector/compile-benchmarks/eza-0.21.2/src/options/theme.rs new file mode 100644 index 000000000..ba60a7957 --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/src/options/theme.rs @@ -0,0 +1,235 @@ +// SPDX-FileCopyrightText: 2024 Christina Sørensen +// SPDX-License-Identifier: EUPL-1.2 +// +// SPDX-FileCopyrightText: 2023-2024 Christina Sørensen, eza contributors +// SPDX-FileCopyrightText: 2014 Benjamin Sago +// SPDX-License-Identifier: MIT +use crate::options::parser::MatchedFlags; +use crate::options::{flags, vars, OptionsError, Vars}; +use crate::output::color_scale::ColorScaleOptions; +use crate::theme::{Definitions, Options, UseColours}; +use std::path::PathBuf; + +use super::config::ThemeConfig; + +impl Options { + pub fn deduce(matches: &MatchedFlags<'_>, vars: &V) -> Result { + let use_colours = UseColours::deduce(matches, vars)?; + let colour_scale = ColorScaleOptions::deduce(matches, vars)?; + let theme_config = ThemeConfig::deduce(vars); + + let definitions = if use_colours == UseColours::Never { + Definitions::default() + } else { + Definitions::deduce(vars) + }; + + Ok(Self { + use_colours, + colour_scale, + definitions, + theme_config, + }) + } +} + +impl ThemeConfig { + fn deduce(vars: &V) -> Option { + if let Some(path) = vars.get("EZA_CONFIG_DIR") { + let path = PathBuf::from(path); + let theme = path.join("theme.yml"); + if theme.exists() { + return Some(ThemeConfig::from_path(theme)); + } + let theme = path.join("theme.yaml"); + if theme.exists() { + return Some(ThemeConfig::from_path(theme)); + } + None + } else { + let path = dirs::config_dir().unwrap_or_default(); + let path = path.join("eza"); + let theme = path.join("theme.yml"); + if theme.exists() { + return Some(ThemeConfig::default()); + } + let theme = path.join("theme.yaml"); + if theme.exists() { + return Some(ThemeConfig::from_path(theme)); + } + None + } + } +} + +impl UseColours { + fn deduce(matches: &MatchedFlags<'_>, vars: &V) -> Result { + let default_value = match vars.get(vars::NO_COLOR) { + Some(_) => Self::Never, + None => Self::Automatic, + }; + + let Some(word) = + matches.get_where(|f| f.matches(&flags::COLOR) || f.matches(&flags::COLOUR))? + else { + return Ok(default_value); + }; + + if word == "always" { + Ok(Self::Always) + } else if word == "auto" || word == "automatic" { + Ok(Self::Automatic) + } else if word == "never" { + Ok(Self::Never) + } else { + Err(OptionsError::BadArgument(&flags::COLOR, word.into())) + } + } +} + +impl Definitions { + fn deduce(vars: &V) -> Self { + let ls = vars + .get(vars::LS_COLORS) + .map(|e| e.to_string_lossy().to_string()); + let exa = vars + .get_with_fallback(vars::EZA_COLORS, vars::EXA_COLORS) + .map(|e| e.to_string_lossy().to_string()); + Self { ls, exa } + } +} + +#[cfg(test)] +mod terminal_test { + use super::*; + use crate::options::flags; + use crate::options::parser::{Arg, Flag}; + use std::ffi::OsString; + + use crate::options::test::parse_for_test; + use crate::options::test::Strictnesses::*; + + static TEST_ARGS: &[&Arg] = &[ + &flags::COLOR, + &flags::COLOUR, + &flags::COLOR_SCALE, + &flags::COLOUR_SCALE, + ]; + + #[allow(unused_macro_rules)] + macro_rules! test { + ($name:ident: $type:ident <- $inputs:expr; $stricts:expr => $result:expr) => { + #[test] + fn $name() { + for result in parse_for_test($inputs.as_ref(), TEST_ARGS, $stricts, |mf| { + $type::deduce(mf) + }) { + assert_eq!(result, $result); + } + } + }; + + ($name:ident: $type:ident <- $inputs:expr, $env:expr; $stricts:expr => $result:expr) => { + #[test] + fn $name() { + let env = $env; + for result in parse_for_test($inputs.as_ref(), TEST_ARGS, $stricts, |mf| { + $type::deduce(mf, &env) + }) { + assert_eq!(result, $result); + } + } + }; + + ($name:ident: $type:ident <- $inputs:expr; $stricts:expr => err $result:expr) => { + #[test] + fn $name() { + for result in parse_for_test($inputs.as_ref(), TEST_ARGS, $stricts, |mf| { + $type::deduce(mf) + }) { + assert_eq!(result.unwrap_err(), $result); + } + } + }; + + ($name:ident: $type:ident <- $inputs:expr, $env:expr; $stricts:expr => err $result:expr) => { + #[test] + fn $name() { + let env = $env; + for result in parse_for_test($inputs.as_ref(), TEST_ARGS, $stricts, |mf| { + $type::deduce(mf, &env) + }) { + assert_eq!(result.unwrap_err(), $result); + } + } + }; + } + + struct MockVars { + ls: &'static str, + exa: &'static str, + no_color: &'static str, + } + + impl MockVars { + fn empty() -> MockVars { + MockVars { + ls: "", + exa: "", + no_color: "", + } + } + fn with_no_color() -> MockVars { + MockVars { + ls: "", + exa: "", + no_color: "true", + } + } + } + + // Test impl that just returns the value it has. + impl Vars for MockVars { + fn get(&self, name: &'static str) -> Option { + if name == vars::LS_COLORS && !self.ls.is_empty() { + Some(OsString::from(self.ls)) + } else if (name == vars::EZA_COLORS || name == vars::EXA_COLORS) && !self.exa.is_empty() + { + Some(OsString::from(self.exa)) + } else if name == vars::NO_COLOR && !self.no_color.is_empty() { + Some(OsString::from(self.no_color)) + } else { + None + } + } + } + + // Default + test!(empty: UseColours <- [], MockVars::empty(); Both => Ok(UseColours::Automatic)); + test!(empty_with_no_color: UseColours <- [], MockVars::with_no_color(); Both => Ok(UseColours::Never)); + + // --colour + test!(u_always: UseColours <- ["--colour=always"], MockVars::empty(); Both => Ok(UseColours::Always)); + test!(u_auto: UseColours <- ["--colour", "auto"], MockVars::empty(); Both => Ok(UseColours::Automatic)); + test!(u_never: UseColours <- ["--colour=never"], MockVars::empty(); Both => Ok(UseColours::Never)); + + // --color + test!(no_u_always: UseColours <- ["--color", "always"], MockVars::empty(); Both => Ok(UseColours::Always)); + test!(no_u_auto: UseColours <- ["--color=auto"], MockVars::empty(); Both => Ok(UseColours::Automatic)); + test!(no_u_never: UseColours <- ["--color", "never"], MockVars::empty(); Both => Ok(UseColours::Never)); + + // Errors + test!(no_u_error: UseColours <- ["--color=upstream"], MockVars::empty(); Both => err OptionsError::BadArgument(&flags::COLOR, OsString::from("upstream"))); // the error is for --color + test!(u_error: UseColours <- ["--colour=lovers"], MockVars::empty(); Both => err OptionsError::BadArgument(&flags::COLOR, OsString::from("lovers"))); // and so is this one! + + // Overriding + test!(overridden_1: UseColours <- ["--colour=auto", "--colour=never"], MockVars::empty(); Last => Ok(UseColours::Never)); + test!(overridden_2: UseColours <- ["--color=auto", "--colour=never"], MockVars::empty(); Last => Ok(UseColours::Never)); + test!(overridden_3: UseColours <- ["--colour=auto", "--color=never"], MockVars::empty(); Last => Ok(UseColours::Never)); + test!(overridden_4: UseColours <- ["--color=auto", "--color=never"], MockVars::empty(); Last => Ok(UseColours::Never)); + + test!(overridden_5: UseColours <- ["--colour=auto", "--colour=never"], MockVars::empty(); Complain => err OptionsError::Duplicate(Flag::Long("colour"), Flag::Long("colour"))); + test!(overridden_6: UseColours <- ["--color=auto", "--colour=never"], MockVars::empty(); Complain => err OptionsError::Duplicate(Flag::Long("color"), Flag::Long("colour"))); + test!(overridden_7: UseColours <- ["--colour=auto", "--color=never"], MockVars::empty(); Complain => err OptionsError::Duplicate(Flag::Long("colour"), Flag::Long("color"))); + test!(overridden_8: UseColours <- ["--color=auto", "--color=never"], MockVars::empty(); Complain => err OptionsError::Duplicate(Flag::Long("color"), Flag::Long("color"))); +} diff --git a/collector/compile-benchmarks/eza-0.21.2/src/options/vars.rs b/collector/compile-benchmarks/eza-0.21.2/src/options/vars.rs new file mode 100644 index 000000000..2e24ef061 --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/src/options/vars.rs @@ -0,0 +1,158 @@ +// SPDX-FileCopyrightText: 2024 Christina Sørensen +// SPDX-License-Identifier: EUPL-1.2 +// +// SPDX-FileCopyrightText: 2023-2024 Christina Sørensen, eza contributors +// SPDX-FileCopyrightText: 2014 Benjamin Sago +// SPDX-License-Identifier: MIT +use std::ffi::OsString; + +// General variables + +/// Environment variable used to colour files, both by their filesystem type +/// (symlink, socket, directory) and their file name or extension (image, +/// video, archive); +pub static LS_COLORS: &str = "LS_COLORS"; + +/// Environment variable used to override the width of the terminal, in +/// characters. +pub static COLUMNS: &str = "COLUMNS"; + +/// Environment variable used to datetime format. +pub static TIME_STYLE: &str = "TIME_STYLE"; + +/// Environment variable used to disable colors. +/// See: +pub static NO_COLOR: &str = "NO_COLOR"; + +// exa-specific variables + +/// Environment variable used to colour exa’s interface when colours are +/// enabled. This includes all the colours that `LS_COLORS` would recognise, +/// overriding them if necessary. It can also contain exa-specific codes. +pub static EXA_COLORS: &str = "EXA_COLORS"; +pub static EZA_COLORS: &str = "EZA_COLORS"; + +/// Environment variable used to switch on strict argument checking, such as +/// complaining if an argument was specified twice, or if two conflict. +/// This is meant to be so you don’t accidentally introduce the wrong +/// behaviour in a script, rather than for general command-line use. +/// Any non-empty value will turn strict mode on. +pub static EXA_STRICT: &str = "EXA_STRICT"; +pub static EZA_STRICT: &str = "EZA_STRICT"; + +/// Environment variable used to make exa print out debugging information as +/// it runs. Any non-empty value will turn debug mode on. +pub static EXA_DEBUG: &str = "EXA_DEBUG"; +pub static EZA_DEBUG: &str = "EZA_DEBUG"; + +/// Environment variable used to limit the grid-details view +/// (`--grid --long`) so it’s only activated if there’s at least the given +/// number of rows of output. +pub static EXA_GRID_ROWS: &str = "EXA_GRID_ROWS"; +pub static EZA_GRID_ROWS: &str = "EZA_GRID_ROWS"; + +/// Environment variable used to specify how many spaces to print between an +/// icon and its file name. Different terminals display icons differently, +/// with 1 space bringing them too close together or 2 spaces putting them too +/// far apart, so this may be necessary depending on how they are shown. +pub static EXA_ICON_SPACING: &str = "EXA_ICON_SPACING"; +pub static EZA_ICON_SPACING: &str = "EZA_ICON_SPACING"; + +pub static EXA_OVERRIDE_GIT: &str = "EXA_OVERRIDE_GIT"; +pub static EZA_OVERRIDE_GIT: &str = "EZA_OVERRIDE_GIT"; + +/// Enviroment variable used to set the minimum luminance in `color_scale`. It's value +/// can be between -100 and 100 +pub static EXA_MIN_LUMINANCE: &str = "EXA_MIN_LUMINANCE"; +pub static EZA_MIN_LUMINANCE: &str = "EZA_MIN_LUMINANCE"; + +/// Environment variable used to automate the same behavior as `--icons=auto` if set. +/// Any explicit use of `--icons=WHEN` overrides this behavior. +pub static EZA_ICONS_AUTO: &str = "EZA_ICONS_AUTO"; + +pub static EZA_STDIN_SEPARATOR: &str = "EZA_STDIN_SEPARATOR"; + +/// Environment variable used to choose how windows attributes are displayed. +/// Short will display a single character for each set attribute, long will +/// display a comma separated list of descriptions. +pub static EZA_WINDOWS_ATTRIBUTES: &str = "EZA_WINDOWS_ATTRIBUTES"; + +/// Mockable wrapper for `std::env::var_os`. +pub trait Vars { + fn get(&self, name: &'static str) -> Option; + + /// Get the variable `name` and if not set get the variable `fallback`. + fn get_with_fallback(&self, name: &'static str, fallback: &'static str) -> Option { + self.get(name).or_else(|| self.get(fallback)) + } + + /// Get the source of the value. If the variable `name` is set return + /// `Some(name)` else if the variable `fallback` is set return + /// `Some(fallback)` else `None`. + fn source(&self, name: &'static str, fallback: &'static str) -> Option<&'static str> { + match self.get(name) { + Some(_) => Some(name), + None => self.get(fallback).and(Some(fallback)), + } + } +} + +// Test impl that just returns the value it has. +#[cfg(test)] +impl Vars for Option { + fn get(&self, _name: &'static str) -> Option { + self.clone() + } +} + +#[cfg(test)] +#[allow(dead_code)] +pub struct MockVars { + columns: OsString, + colors: OsString, + no_colors: OsString, + strict: OsString, + debug: OsString, + grid_rows: OsString, + icon_spacing: OsString, + luminance: OsString, + icons: OsString, +} + +#[cfg(test)] +#[allow(dead_code)] +impl Vars for MockVars { + fn get(&self, name: &'static str) -> Option { + match name { + "EXA_STRICT" | "EZA_STRICT" => Some(self.strict.clone()), + "EZA_COLORS" | "LS_COLORS" | "EXA_COLORS" => Some(self.colors.clone()), + "EXA_DEBUG" | "EZA_DEBUG" => Some(self.debug.clone()), + "EXA_GRID_ROWS" | "EZA_GRID_ROWS" => Some(self.grid_rows.clone()), + "EXA_ICON_SPACING" | "EZA_ICON_SPACING" => Some(self.icon_spacing.clone()), + "EXA_MIN_LUMINANCE" | "EZA_MIN_LUMINANCE" => Some(self.luminance.clone()), + "EZA_ICONS_AUTO" => Some(self.icons.clone()), + "COLUMNS" => Some(self.columns.clone()), + "NO_COLOR" => Some(self.no_colors.clone()), + _ => None, + } + } +} + +#[cfg(test)] +#[allow(dead_code)] +impl MockVars { + pub fn set(&mut self, var: &'static str, value: &OsString) { + match var { + "EXA_STRICT" | "EZA_STRICT" => self.strict = value.clone(), + "EZA_COLORS" | "LS_COLORS" | "EXA_COLORS" => self.colors = value.clone(), + "EXA_DEBUG" | "EZA_DEBUG" => self.debug = value.clone(), + "EXA_GRID_ROWS" | "EZA_GRID_ROWS" => self.grid_rows = value.clone(), + "EXA_ICON_SPACING" | "EZA_ICON_SPACING" => self.icon_spacing = value.clone(), + "EXA_MIN_LUMINANCE" | "EZA_MIN_LUMINANCE" => self.luminance = value.clone(), + "EZA_ICONS_AUTO" => self.icons = value.clone(), + "COLUMNS" => self.columns = value.clone(), + "NO_COLOR" => self.no_colors = value.clone(), + _ => (), + }; + } +} diff --git a/collector/compile-benchmarks/eza-0.21.2/src/options/version.rs b/collector/compile-benchmarks/eza-0.21.2/src/options/version.rs new file mode 100644 index 000000000..b641d793d --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/src/options/version.rs @@ -0,0 +1,63 @@ +// SPDX-FileCopyrightText: 2024 Christina Sørensen +// SPDX-License-Identifier: EUPL-1.2 +// +// SPDX-FileCopyrightText: 2023-2024 Christina Sørensen, eza contributors +// SPDX-FileCopyrightText: 2014 Benjamin Sago +// SPDX-License-Identifier: MIT +//! Printing the version string. +//! +//! The code that works out which string to print is done in `build.rs`. + +use std::fmt; + +use crate::options::flags; +use crate::options::parser::MatchedFlags; + +#[derive(PartialEq, Eq, Debug, Copy, Clone)] +pub struct VersionString; +// There were options here once, but there aren’t anymore! + +impl VersionString { + /// Determines how to show the version, if at all, based on the user’s + /// command-line arguments. This one works backwards from the other + /// ‘deduce’ functions, returning Err if help needs to be shown. + /// + /// Like --help, this doesn’t check for errors. + pub fn deduce(matches: &MatchedFlags<'_>) -> Option { + if matches.count(&flags::VERSION) > 0 { + Some(Self) + } else { + None + } + } +} + +impl fmt::Display for VersionString { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + write!( + f, + "{}", + include_str!(concat!(env!("OUT_DIR"), "/version_string.txt")) + ) + } +} + +#[cfg(test)] +mod test { + use crate::options::{Options, OptionsResult}; + use std::ffi::OsStr; + + #[test] + fn version() { + let args = vec![OsStr::new("--version")]; + let opts = Options::parse(args, &None); + assert!(matches!(opts, OptionsResult::Version(_))); + } + + #[test] + fn version_with_file() { + let args = vec![OsStr::new("--version"), OsStr::new("me")]; + let opts = Options::parse(args, &None); + assert!(matches!(opts, OptionsResult::Version(_))); + } +} diff --git a/collector/compile-benchmarks/eza-0.21.2/src/options/view.rs b/collector/compile-benchmarks/eza-0.21.2/src/options/view.rs new file mode 100644 index 000000000..fc1d3ebbe --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/src/options/view.rs @@ -0,0 +1,797 @@ +// SPDX-FileCopyrightText: 2024 Christina Sørensen +// SPDX-License-Identifier: EUPL-1.2 +// +// SPDX-FileCopyrightText: 2023-2024 Christina Sørensen, eza contributors +// SPDX-FileCopyrightText: 2014 Benjamin Sago +// SPDX-License-Identifier: MIT +use std::ffi::OsString; + +use crate::fs::feature::xattr; +use crate::options::parser::MatchedFlags; +use crate::options::{flags, vars, NumberSource, OptionsError, Vars}; +use crate::output::color_scale::{ColorScaleMode, ColorScaleOptions}; +use crate::output::file_name::Options as FileStyle; +use crate::output::grid_details::{self, RowThreshold}; +use crate::output::table::{ + Columns, FlagsFormat, GroupFormat, Options as TableOptions, SizeFormat, TimeTypes, UserFormat, +}; +use crate::output::time::TimeFormat; +use crate::output::{details, grid, Mode, TerminalWidth, View}; + +impl View { + pub fn deduce(matches: &MatchedFlags<'_>, vars: &V) -> Result { + let mode = Mode::deduce(matches, vars)?; + let deref_links = matches.has(&flags::DEREF_LINKS)?; + let follow_links = matches.has(&flags::FOLLOW_LINKS)?; + let total_size = matches.has(&flags::TOTAL_SIZE)?; + let width = TerminalWidth::deduce(matches, vars)?; + let file_style = FileStyle::deduce(matches, vars, width.actual_terminal_width().is_some())?; + Ok(Self { + mode, + width, + file_style, + deref_links, + follow_links, + total_size, + }) + } +} + +impl Mode { + /// Determine which viewing mode to use based on the user’s options. + /// + /// As with the other options, arguments are scanned right-to-left and the + /// first flag found is matched, so `exa --oneline --long` will pick a + /// details view, and `exa --long --oneline` will pick the lines view. + /// + /// This is complicated a little by the fact that `--grid` and `--tree` + /// can also combine with `--long`, so care has to be taken to use the + pub fn deduce(matches: &MatchedFlags<'_>, vars: &V) -> Result { + let flag = matches.has_where_any(|f| { + f.matches(&flags::LONG) + || f.matches(&flags::ONE_LINE) + || f.matches(&flags::GRID) + || f.matches(&flags::TREE) + }); + + let Some(flag) = flag else { + Self::strict_check_long_flags(matches)?; + let grid = grid::Options::deduce(matches)?; + return Ok(Self::Grid(grid)); + }; + + if flag.matches(&flags::LONG) + || (flag.matches(&flags::TREE) && matches.has(&flags::LONG)?) + || (flag.matches(&flags::GRID) && matches.has(&flags::LONG)?) + { + let _ = matches.has(&flags::LONG)?; + let details = details::Options::deduce_long(matches, vars)?; + + let flag = + matches.has_where_any(|f| f.matches(&flags::GRID) || f.matches(&flags::TREE)); + + if flag.is_some() && flag.unwrap().matches(&flags::GRID) { + let _ = matches.has(&flags::GRID)?; + let row_threshold = RowThreshold::deduce(vars)?; + let grid_details = grid_details::Options { + details, + row_threshold, + }; + return Ok(Self::GridDetails(grid_details)); + } + + // the --tree case is handled by the DirAction parser later + return Ok(Self::Details(details)); + } + + Self::strict_check_long_flags(matches)?; + + if flag.matches(&flags::TREE) { + let _ = matches.has(&flags::TREE)?; + let details = details::Options::deduce_tree(matches, vars)?; + return Ok(Self::Details(details)); + } + + if flag.matches(&flags::ONE_LINE) { + let _ = matches.has(&flags::ONE_LINE)?; + return Ok(Self::Lines); + } + + let grid = grid::Options::deduce(matches)?; + Ok(Self::Grid(grid)) + } + + fn strict_check_long_flags(matches: &MatchedFlags<'_>) -> Result<(), OptionsError> { + // If --long hasn’t been passed, then check if we need to warn the + // user about flags that won’t have any effect. + if matches.is_strict() { + for option in &[ + &flags::BINARY, + &flags::BYTES, + &flags::INODE, + &flags::LINKS, + &flags::HEADER, + &flags::BLOCKSIZE, + &flags::TIME, + &flags::GROUP, + &flags::NUMERIC, + &flags::MOUNTS, + ] { + if matches.has(option)? { + return Err(OptionsError::Useless(option, false, &flags::LONG)); + } + } + + if matches.has(&flags::GIT)? && !matches.has(&flags::NO_GIT)? { + return Err(OptionsError::Useless(&flags::GIT, false, &flags::LONG)); + } else if matches.has(&flags::LEVEL)? + && !matches.has(&flags::RECURSE)? + && !matches.has(&flags::TREE)? + { + return Err(OptionsError::Useless2( + &flags::LEVEL, + &flags::RECURSE, + &flags::TREE, + )); + } + } + + Ok(()) + } +} + +impl grid::Options { + fn deduce(matches: &MatchedFlags<'_>) -> Result { + let grid = grid::Options { + across: matches.has(&flags::ACROSS)?, + }; + + Ok(grid) + } +} + +impl details::Options { + fn deduce_tree(matches: &MatchedFlags<'_>, vars: &V) -> Result { + let details = details::Options { + table: None, + header: false, + xattr: xattr::ENABLED && matches.has(&flags::EXTENDED)?, + secattr: xattr::ENABLED && matches.has(&flags::SECURITY_CONTEXT)?, + mounts: matches.has(&flags::MOUNTS)?, + color_scale: ColorScaleOptions::deduce(matches, vars)?, + follow_links: matches.has(&flags::FOLLOW_LINKS)?, + }; + + Ok(details) + } + + fn deduce_long(matches: &MatchedFlags<'_>, vars: &V) -> Result { + if matches.is_strict() { + if matches.has(&flags::ACROSS)? && !matches.has(&flags::GRID)? { + return Err(OptionsError::Useless(&flags::ACROSS, true, &flags::LONG)); + } else if matches.has(&flags::ONE_LINE)? { + return Err(OptionsError::Useless(&flags::ONE_LINE, true, &flags::LONG)); + } + } + + Ok(details::Options { + table: Some(TableOptions::deduce(matches, vars)?), + header: matches.has(&flags::HEADER)?, + xattr: xattr::ENABLED && matches.has(&flags::EXTENDED)?, + secattr: xattr::ENABLED && matches.has(&flags::SECURITY_CONTEXT)?, + mounts: matches.has(&flags::MOUNTS)?, + color_scale: ColorScaleOptions::deduce(matches, vars)?, + follow_links: matches.has(&flags::FOLLOW_LINKS)?, + }) + } +} + +impl TerminalWidth { + fn deduce(matches: &MatchedFlags<'_>, vars: &V) -> Result { + if let Some(width) = matches.get(&flags::WIDTH)? { + let arg_str = width.to_string_lossy(); + match arg_str.parse() { + Ok(w) => { + if w >= 1 { + Ok(Self::Set(w)) + } else { + Ok(Self::Automatic) + } + } + Err(e) => { + let source = NumberSource::Arg(&flags::WIDTH); + Err(OptionsError::FailedParse(arg_str.to_string(), source, e)) + } + } + } else if let Some(columns) = vars.get(vars::COLUMNS).and_then(|s| s.into_string().ok()) { + match columns.parse() { + Ok(width) => Ok(Self::Set(width)), + Err(e) => { + let source = NumberSource::Env(vars::COLUMNS); + Err(OptionsError::FailedParse(columns, source, e)) + } + } + } else { + Ok(Self::Automatic) + } + } +} + +impl RowThreshold { + fn deduce(vars: &V) -> Result { + if let Some(columns) = vars + .get_with_fallback(vars::EZA_GRID_ROWS, vars::EXA_GRID_ROWS) + .and_then(|s| s.into_string().ok()) + { + match columns.parse() { + Ok(rows) => Ok(Self::MinimumRows(rows)), + Err(e) => { + let source = NumberSource::Env( + vars.source(vars::EZA_GRID_ROWS, vars::EXA_GRID_ROWS) + .unwrap(), + ); + Err(OptionsError::FailedParse(columns, source, e)) + } + } + } else { + Ok(Self::AlwaysGrid) + } + } +} + +impl TableOptions { + fn deduce(matches: &MatchedFlags<'_>, vars: &V) -> Result { + let time_format = TimeFormat::deduce(matches, vars)?; + let size_format = SizeFormat::deduce(matches)?; + let user_format = UserFormat::deduce(matches)?; + let group_format = GroupFormat::deduce(matches)?; + let flags_format = FlagsFormat::deduce(vars); + let columns = Columns::deduce(matches, vars)?; + Ok(Self { + size_format, + time_format, + user_format, + group_format, + flags_format, + columns, + }) + } +} + +impl Columns { + fn deduce(matches: &MatchedFlags<'_>, vars: &V) -> Result { + let time_types = TimeTypes::deduce(matches)?; + + let no_git_env = vars + .get_with_fallback(vars::EXA_OVERRIDE_GIT, vars::EZA_OVERRIDE_GIT) + .is_some(); + + let git = matches.has(&flags::GIT)? && !matches.has(&flags::NO_GIT)? && !no_git_env; + let subdir_git_repos = + matches.has(&flags::GIT_REPOS)? && !matches.has(&flags::NO_GIT)? && !no_git_env; + let subdir_git_repos_no_stat = !subdir_git_repos + && matches.has(&flags::GIT_REPOS_NO_STAT)? + && !matches.has(&flags::NO_GIT)? + && !no_git_env; + + let blocksize = matches.has(&flags::BLOCKSIZE)?; + let group = matches.has(&flags::GROUP)?; + let inode = matches.has(&flags::INODE)?; + let links = matches.has(&flags::LINKS)?; + let octal = matches.has(&flags::OCTAL)?; + let security_context = xattr::ENABLED && matches.has(&flags::SECURITY_CONTEXT)?; + let file_flags = matches.has(&flags::FILE_FLAGS)?; + + let permissions = !matches.has(&flags::NO_PERMISSIONS)?; + let filesize = !matches.has(&flags::NO_FILESIZE)?; + let user = !matches.has(&flags::NO_USER)?; + + Ok(Self { + time_types, + inode, + links, + blocksize, + group, + git, + subdir_git_repos, + subdir_git_repos_no_stat, + octal, + security_context, + file_flags, + permissions, + filesize, + user, + }) + } +} + +impl SizeFormat { + /// Determine which file size to use in the file size column based on + /// the user’s options. + /// + /// The default mode is to use the decimal prefixes, as they are the + /// most commonly-understood, and don’t involve trying to parse large + /// strings of digits in your head. Changing the format to anything else + /// involves the `--binary` or `--bytes` flags, and these conflict with + /// each other. + fn deduce(matches: &MatchedFlags<'_>) -> Result { + let flag = matches.has_where(|f| f.matches(&flags::BINARY) || f.matches(&flags::BYTES))?; + + Ok(match flag { + Some(f) if f.matches(&flags::BINARY) => Self::BinaryBytes, + Some(f) if f.matches(&flags::BYTES) => Self::JustBytes, + _ => Self::DecimalBytes, + }) + } +} + +impl TimeFormat { + /// Determine how time should be formatted in timestamp columns. + fn deduce(matches: &MatchedFlags<'_>, vars: &V) -> Result { + let word = if let Some(w) = matches.get(&flags::TIME_STYLE)? { + w.to_os_string() + } else { + match vars.get(vars::TIME_STYLE) { + Some(ref t) if !t.is_empty() => t.clone(), + _ => return Ok(Self::DefaultFormat), + } + }; + + match word.to_string_lossy().as_ref() { + "default" => Ok(Self::DefaultFormat), + "relative" => Ok(Self::Relative), + "iso" => Ok(Self::ISOFormat), + "long-iso" => Ok(Self::LongISO), + "full-iso" => Ok(Self::FullISO), + fmt if fmt.starts_with('+') => { + let mut lines = fmt[1..].lines(); + + // line 1 will be None when: + // - there is nothing after `+` + // line 1 will be empty when: + // - `+` is followed immediately by `\n` + let empty_non_recent_format_msg = "Custom timestamp format is empty, \ + please supply a chrono format string after the plus sign."; + let non_recent = lines.next().expect(empty_non_recent_format_msg); + let non_recent = if non_recent.is_empty() { + panic!("{}", empty_non_recent_format_msg) + } else { + non_recent.to_owned() + }; + + // line 2 will be None when: + // - there is not a single `\n` + // - there is nothing after the first `\n` + // line 2 will be empty when: + // - there exist at least 2 `\n`, and no content between the 1st and 2nd `\n` + let empty_recent_format_msg = "Custom timestamp format for recent files is empty, \ + please supply a chrono format string at the second line."; + let recent = lines.next().map(|rec| { + if rec.is_empty() { + panic!("{}", empty_recent_format_msg) + } else { + rec.to_owned() + } + }); + + Ok(Self::Custom { non_recent, recent }) + } + _ => Err(OptionsError::BadArgument(&flags::TIME_STYLE, word)), + } + } +} + +impl UserFormat { + fn deduce(matches: &MatchedFlags<'_>) -> Result { + let flag = matches.has(&flags::NUMERIC)?; + Ok(if flag { Self::Numeric } else { Self::Name }) + } +} + +impl GroupFormat { + fn deduce(matches: &MatchedFlags<'_>) -> Result { + let flag = matches.has(&flags::SMART_GROUP)?; + Ok(if flag { Self::Smart } else { Self::Regular }) + } +} + +impl TimeTypes { + /// Determine which of a file’s time fields should be displayed for it + /// based on the user’s options. + /// + /// There are two separate ways to pick which fields to show: with a + /// flag (such as `--modified`) or with a parameter (such as + /// `--time=modified`). An error is signaled if both ways are used. + /// + /// It’s valid to show more than one column by passing in more than one + /// option, but passing *no* options means that the user just wants to + /// see the default set. + fn deduce(matches: &MatchedFlags<'_>) -> Result { + let possible_word = matches.get(&flags::TIME)?; + let modified = matches.has(&flags::MODIFIED)?; + let changed = matches.has(&flags::CHANGED)?; + let accessed = matches.has(&flags::ACCESSED)?; + let created = matches.has(&flags::CREATED)?; + + let no_time = matches.has(&flags::NO_TIME)?; + + #[rustfmt::skip] + let time_types = if no_time { + Self { + modified: false, + changed: false, + accessed: false, + created: false, + } + } else if let Some(word) = possible_word { + if modified { + return Err(OptionsError::Useless(&flags::MODIFIED, true, &flags::TIME)); + } else if changed { + return Err(OptionsError::Useless(&flags::CHANGED, true, &flags::TIME)); + } else if accessed { + return Err(OptionsError::Useless(&flags::ACCESSED, true, &flags::TIME)); + } else if created { + return Err(OptionsError::Useless(&flags::CREATED, true, &flags::TIME)); + } else if word == "mod" || word == "modified" { + Self { modified: true, changed: false, accessed: false, created: false } + } else if word == "ch" || word == "changed" { + Self { modified: false, changed: true, accessed: false, created: false } + } else if word == "acc" || word == "accessed" { + Self { modified: false, changed: false, accessed: true, created: false } + } else if word == "cr" || word == "created" { + Self { modified: false, changed: false, accessed: false, created: true } + } else { + return Err(OptionsError::BadArgument(&flags::TIME, word.into())); + } + } else if modified || changed || accessed || created { + Self { + modified, + changed, + accessed, + created, + } + } else { + Self::default() + }; + + Ok(time_types) + } +} + +impl ColorScaleOptions { + pub fn deduce(matches: &MatchedFlags<'_>, vars: &V) -> Result { + let min_luminance = + match vars.get_with_fallback(vars::EZA_MIN_LUMINANCE, vars::EXA_MIN_LUMINANCE) { + Some(var) => match var.to_string_lossy().parse() { + Ok(luminance) if (-100..=100).contains(&luminance) => luminance, + _ => 40, + }, + None => 40, + }; + + let mode = if let Some(w) = matches + .get(&flags::COLOR_SCALE_MODE)? + .or(matches.get(&flags::COLOUR_SCALE_MODE)?) + { + match w.to_str() { + Some("fixed") => ColorScaleMode::Fixed, + Some("gradient") => ColorScaleMode::Gradient, + _ => Err(OptionsError::BadArgument( + &flags::COLOR_SCALE_MODE, + w.to_os_string(), + ))?, + } + } else { + ColorScaleMode::Gradient + }; + + let mut options = ColorScaleOptions { + mode, + min_luminance, + size: false, + age: false, + }; + + let words = if let Some(w) = matches + .get(&flags::COLOR_SCALE)? + .or(matches.get(&flags::COLOUR_SCALE)?) + { + w.to_os_string() + } else { + return Ok(options); + }; + + for word in words.to_string_lossy().split(',') { + match word { + "all" => { + options.size = true; + options.age = true; + } + "age" => options.age = true, + "size" => options.size = true, + _ => Err(OptionsError::BadArgument( + &flags::COLOR_SCALE, + OsString::from(word), + ))?, + }; + } + + Ok(options) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::options::flags; + use crate::options::parser::{Arg, Flag}; + use std::ffi::OsString; + + use crate::options::test::parse_for_test; + use crate::options::test::Strictnesses::*; + + static TEST_ARGS: &[&Arg] = &[ + &flags::BINARY, + &flags::BYTES, + &flags::TIME_STYLE, + &flags::TIME, + &flags::MODIFIED, + &flags::CHANGED, + &flags::CREATED, + &flags::ACCESSED, + &flags::HEADER, + &flags::GROUP, + &flags::INODE, + &flags::GIT, + &flags::LINKS, + &flags::BLOCKSIZE, + &flags::LONG, + &flags::LEVEL, + &flags::GRID, + &flags::ACROSS, + &flags::ONE_LINE, + &flags::TREE, + &flags::NUMERIC, + ]; + + #[allow(unused_macro_rules)] + macro_rules! test { + ($name:ident: $type:ident <- $inputs:expr; $stricts:expr => $result:expr) => { + /// Macro that writes a test. + /// If testing both strictnesses, they’ll both be done in the same function. + #[test] + fn $name() { + for result in parse_for_test($inputs.as_ref(), TEST_ARGS, $stricts, |mf| { + $type::deduce(mf) + }) { + assert_eq!(result, $result); + } + } + }; + + ($name:ident: $type:ident <- $inputs:expr; $stricts:expr => err $result:expr) => { + /// Special macro for testing Err results. + /// This is needed because sometimes the Ok type doesn’t implement `PartialEq`. + #[test] + fn $name() { + for result in parse_for_test($inputs.as_ref(), TEST_ARGS, $stricts, |mf| { + $type::deduce(mf) + }) { + assert_eq!(result.unwrap_err(), $result); + } + } + }; + + ($name:ident: $type:ident <- $inputs:expr; $stricts:expr => like $pat:pat) => { + /// More general macro for testing against a pattern. + /// Instead of using `PartialEq`, this just tests if it matches a pat. + #[test] + fn $name() { + for result in parse_for_test($inputs.as_ref(), TEST_ARGS, $stricts, |mf| { + $type::deduce(mf) + }) { + println!("Testing {:?}", result); + match result { + $pat => assert!(true), + _ => assert!(false), + } + } + } + }; + + ($name:ident: $type:ident <- $inputs:expr, $vars:expr; $stricts:expr => err $result:expr) => { + /// Like above, but with $vars. + #[test] + fn $name() { + for result in parse_for_test($inputs.as_ref(), TEST_ARGS, $stricts, |mf| { + $type::deduce(mf, &$vars) + }) { + assert_eq!(result.unwrap_err(), $result); + } + } + }; + + ($name:ident: $type:ident <- $inputs:expr, $vars:expr; $stricts:expr => like $pat:pat) => { + /// Like further above, but with $vars. + #[test] + fn $name() { + for result in parse_for_test($inputs.as_ref(), TEST_ARGS, $stricts, |mf| { + $type::deduce(mf, &$vars) + }) { + println!("Testing {:?}", result); + match result { + $pat => assert!(true), + _ => assert!(false), + } + } + } + }; + } + + mod size_formats { + use super::*; + + // Default behaviour + test!(empty: SizeFormat <- []; Both => Ok(SizeFormat::DecimalBytes)); + + // Individual flags + test!(binary: SizeFormat <- ["--binary"]; Both => Ok(SizeFormat::BinaryBytes)); + test!(bytes: SizeFormat <- ["--bytes"]; Both => Ok(SizeFormat::JustBytes)); + + // Overriding + test!(both_1: SizeFormat <- ["--binary", "--binary"]; Last => Ok(SizeFormat::BinaryBytes)); + test!(both_2: SizeFormat <- ["--bytes", "--binary"]; Last => Ok(SizeFormat::BinaryBytes)); + test!(both_3: SizeFormat <- ["--binary", "--bytes"]; Last => Ok(SizeFormat::JustBytes)); + test!(both_4: SizeFormat <- ["--bytes", "--bytes"]; Last => Ok(SizeFormat::JustBytes)); + + test!(both_5: SizeFormat <- ["--binary", "--binary"]; Complain => err OptionsError::Duplicate(Flag::Long("binary"), Flag::Long("binary"))); + test!(both_6: SizeFormat <- ["--bytes", "--binary"]; Complain => err OptionsError::Duplicate(Flag::Long("bytes"), Flag::Long("binary"))); + test!(both_7: SizeFormat <- ["--binary", "--bytes"]; Complain => err OptionsError::Duplicate(Flag::Long("binary"), Flag::Long("bytes"))); + test!(both_8: SizeFormat <- ["--bytes", "--bytes"]; Complain => err OptionsError::Duplicate(Flag::Long("bytes"), Flag::Long("bytes"))); + } + + mod time_formats { + use super::*; + + // These tests use pattern matching because TimeFormat doesn’t + // implement PartialEq. + + // Default behaviour + test!(empty: TimeFormat <- [], None; Both => like Ok(TimeFormat::DefaultFormat)); + + // Individual settings + test!(default: TimeFormat <- ["--time-style=default"], None; Both => like Ok(TimeFormat::DefaultFormat)); + test!(iso: TimeFormat <- ["--time-style", "iso"], None; Both => like Ok(TimeFormat::ISOFormat)); + test!(relative: TimeFormat <- ["--time-style", "relative"], None; Both => like Ok(TimeFormat::Relative)); + test!(long_iso: TimeFormat <- ["--time-style=long-iso"], None; Both => like Ok(TimeFormat::LongISO)); + test!(full_iso: TimeFormat <- ["--time-style", "full-iso"], None; Both => like Ok(TimeFormat::FullISO)); + test!(custom_style: TimeFormat <- ["--time-style", "+%Y/%m/%d"], None; Both => like Ok(TimeFormat::Custom { recent: None, .. })); + test!(custom_style_multiline: TimeFormat <- ["--time-style", "+%Y/%m/%d\n--%m-%d"], None; Both => like Ok(TimeFormat::Custom { recent: Some(_), .. })); + test!(bad_custom_style: TimeFormat <- ["--time-style", "%Y/%m/%d"], None; Both => err OptionsError::BadArgument(&flags::TIME_STYLE, OsString::from("%Y/%m/%d"))); + + // Overriding + test!(actually: TimeFormat <- ["--time-style=default", "--time-style", "iso"], None; Last => like Ok(TimeFormat::ISOFormat)); + test!(actual_2: TimeFormat <- ["--time-style=default", "--time-style", "iso"], None; Complain => err OptionsError::Duplicate(Flag::Long("time-style"), Flag::Long("time-style"))); + + test!(nevermind: TimeFormat <- ["--time-style", "long-iso", "--time-style=full-iso"], None; Last => like Ok(TimeFormat::FullISO)); + test!(nevermore: TimeFormat <- ["--time-style", "long-iso", "--time-style=full-iso"], None; Complain => err OptionsError::Duplicate(Flag::Long("time-style"), Flag::Long("time-style"))); + + // Errors + test!(daily: TimeFormat <- ["--time-style=24-hour"], None; Both => err OptionsError::BadArgument(&flags::TIME_STYLE, OsString::from("24-hour"))); + + // `TIME_STYLE` environment variable is defined. + // If the time-style argument is not given, `TIME_STYLE` is used. + test!(use_env: TimeFormat <- [], Some("long-iso".into()); Both => like Ok(TimeFormat::LongISO)); + + // If the time-style argument is given, `TIME_STYLE` is overriding. + test!(override_env: TimeFormat <- ["--time-style=full-iso"], Some("long-iso".into()); Both => like Ok(TimeFormat::FullISO)); + } + + mod time_types { + use super::*; + + // Default behaviour + test!(empty: TimeTypes <- []; Both => Ok(TimeTypes::default())); + + // Modified + test!(modified: TimeTypes <- ["--modified"]; Both => Ok(TimeTypes { modified: true, changed: false, accessed: false, created: false })); + test!(m: TimeTypes <- ["-m"]; Both => Ok(TimeTypes { modified: true, changed: false, accessed: false, created: false })); + test!(time_mod: TimeTypes <- ["--time=modified"]; Both => Ok(TimeTypes { modified: true, changed: false, accessed: false, created: false })); + test!(t_m: TimeTypes <- ["-tmod"]; Both => Ok(TimeTypes { modified: true, changed: false, accessed: false, created: false })); + + // Changed + #[cfg(target_family = "unix")] + test!(changed: TimeTypes <- ["--changed"]; Both => Ok(TimeTypes { modified: false, changed: true, accessed: false, created: false })); + #[cfg(target_family = "unix")] + test!(time_ch: TimeTypes <- ["--time=changed"]; Both => Ok(TimeTypes { modified: false, changed: true, accessed: false, created: false })); + #[cfg(target_family = "unix")] + test!(t_ch: TimeTypes <- ["-t", "ch"]; Both => Ok(TimeTypes { modified: false, changed: true, accessed: false, created: false })); + + // Accessed + test!(acc: TimeTypes <- ["--accessed"]; Both => Ok(TimeTypes { modified: false, changed: false, accessed: true, created: false })); + test!(a: TimeTypes <- ["-u"]; Both => Ok(TimeTypes { modified: false, changed: false, accessed: true, created: false })); + test!(time_acc: TimeTypes <- ["--time", "accessed"]; Both => Ok(TimeTypes { modified: false, changed: false, accessed: true, created: false })); + test!(time_a: TimeTypes <- ["-t", "acc"]; Both => Ok(TimeTypes { modified: false, changed: false, accessed: true, created: false })); + + // Created + test!(cr: TimeTypes <- ["--created"]; Both => Ok(TimeTypes { modified: false, changed: false, accessed: false, created: true })); + test!(c: TimeTypes <- ["-U"]; Both => Ok(TimeTypes { modified: false, changed: false, accessed: false, created: true })); + test!(time_cr: TimeTypes <- ["--time=created"]; Both => Ok(TimeTypes { modified: false, changed: false, accessed: false, created: true })); + test!(t_cr: TimeTypes <- ["-tcr"]; Both => Ok(TimeTypes { modified: false, changed: false, accessed: false, created: true })); + + // Multiples + test!(time_uu: TimeTypes <- ["-u", "--modified"]; Both => Ok(TimeTypes { modified: true, changed: false, accessed: true, created: false })); + + // Errors + test!(time_tea: TimeTypes <- ["--time=tea"]; Both => err OptionsError::BadArgument(&flags::TIME, OsString::from("tea"))); + test!(t_ea: TimeTypes <- ["-tea"]; Both => err OptionsError::BadArgument(&flags::TIME, OsString::from("ea"))); + + // Overriding + test!(overridden: TimeTypes <- ["-tcr", "-tmod"]; Last => Ok(TimeTypes { modified: true, changed: false, accessed: false, created: false })); + test!(overridden_2: TimeTypes <- ["-tcr", "-tmod"]; Complain => err OptionsError::Duplicate(Flag::Short(b't'), Flag::Short(b't'))); + } + + mod views { + use super::*; + + use crate::output::grid::Options as GridOptions; + + // Default + test!(empty: Mode <- [], None; Both => like Ok(Mode::Grid(_))); + + // Grid views + test!(original_g: Mode <- ["-G"], None; Both => like Ok(Mode::Grid(GridOptions { across: false, .. }))); + test!(grid: Mode <- ["--grid"], None; Both => like Ok(Mode::Grid(GridOptions { across: false, .. }))); + test!(across: Mode <- ["--across"], None; Both => like Ok(Mode::Grid(GridOptions { across: true, .. }))); + test!(gracross: Mode <- ["-xG"], None; Both => like Ok(Mode::Grid(GridOptions { across: true, .. }))); + + // Lines views + test!(lines: Mode <- ["--oneline"], None; Both => like Ok(Mode::Lines)); + test!(prima: Mode <- ["-1"], None; Both => like Ok(Mode::Lines)); + + // Details views + test!(long: Mode <- ["--long"], None; Both => like Ok(Mode::Details(_))); + test!(ell: Mode <- ["-l"], None; Both => like Ok(Mode::Details(_))); + + // Grid-details views + test!(lid: Mode <- ["--long", "--grid"], None; Both => like Ok(Mode::GridDetails(_))); + test!(leg: Mode <- ["-lG"], None; Both => like Ok(Mode::GridDetails(_))); + + // Options that do nothing with --long + test!(long_across: Mode <- ["--long", "--across"], None; Last => like Ok(Mode::Details(_))); + + // Options that do nothing without --long + test!(just_header: Mode <- ["--header"], None; Last => like Ok(Mode::Grid(_))); + test!(just_group: Mode <- ["--group"], None; Last => like Ok(Mode::Grid(_))); + test!(just_inode: Mode <- ["--inode"], None; Last => like Ok(Mode::Grid(_))); + test!(just_links: Mode <- ["--links"], None; Last => like Ok(Mode::Grid(_))); + test!(just_blocks: Mode <- ["--blocksize"], None; Last => like Ok(Mode::Grid(_))); + test!(just_binary: Mode <- ["--binary"], None; Last => like Ok(Mode::Grid(_))); + test!(just_bytes: Mode <- ["--bytes"], None; Last => like Ok(Mode::Grid(_))); + test!(just_numeric: Mode <- ["--numeric"], None; Last => like Ok(Mode::Grid(_))); + + #[cfg(feature = "git")] + test!(just_git: Mode <- ["--git"], None; Last => like Ok(Mode::Grid(_))); + + test!(just_header_2: Mode <- ["--header"], None; Complain => err OptionsError::Useless(&flags::HEADER, false, &flags::LONG)); + test!(just_group_2: Mode <- ["--group"], None; Complain => err OptionsError::Useless(&flags::GROUP, false, &flags::LONG)); + test!(just_inode_2: Mode <- ["--inode"], None; Complain => err OptionsError::Useless(&flags::INODE, false, &flags::LONG)); + test!(just_links_2: Mode <- ["--links"], None; Complain => err OptionsError::Useless(&flags::LINKS, false, &flags::LONG)); + test!(just_blocks_2: Mode <- ["--blocksize"], None; Complain => err OptionsError::Useless(&flags::BLOCKSIZE, false, &flags::LONG)); + test!(just_binary_2: Mode <- ["--binary"], None; Complain => err OptionsError::Useless(&flags::BINARY, false, &flags::LONG)); + test!(just_bytes_2: Mode <- ["--bytes"], None; Complain => err OptionsError::Useless(&flags::BYTES, false, &flags::LONG)); + test!(just_numeric2: Mode <- ["--numeric"], None; Complain => err OptionsError::Useless(&flags::NUMERIC, false, &flags::LONG)); + + #[cfg(feature = "git")] + test!(just_git_2: Mode <- ["--git"], None; Complain => err OptionsError::Useless(&flags::GIT, false, &flags::LONG)); + + // Contradictions and combinations + test!(lgo: Mode <- ["--long", "--grid", "--oneline"], None; Both => like Ok(Mode::Lines)); + test!(lgt: Mode <- ["--long", "--grid", "--tree"], None; Both => like Ok(Mode::Details(_))); + test!(tgl: Mode <- ["--tree", "--grid", "--long"], None; Both => like Ok(Mode::GridDetails(_))); + test!(tlg: Mode <- ["--tree", "--long", "--grid"], None; Both => like Ok(Mode::GridDetails(_))); + test!(ot: Mode <- ["--oneline", "--tree"], None; Both => like Ok(Mode::Details(_))); + test!(og: Mode <- ["--oneline", "--grid"], None; Both => like Ok(Mode::Grid(_))); + test!(tg: Mode <- ["--tree", "--grid"], None; Both => like Ok(Mode::Grid(_))); + } +} diff --git a/collector/compile-benchmarks/eza-0.21.2/src/output/cell.rs b/collector/compile-benchmarks/eza-0.21.2/src/output/cell.rs new file mode 100644 index 000000000..5a05204d0 --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/src/output/cell.rs @@ -0,0 +1,276 @@ +// SPDX-FileCopyrightText: 2024 Christina Sørensen +// SPDX-License-Identifier: EUPL-1.2 +// +// SPDX-FileCopyrightText: 2023-2024 Christina Sørensen, eza contributors +// SPDX-FileCopyrightText: 2014 Benjamin Sago +// SPDX-License-Identifier: MIT +//! The `TextCell` type for the details and lines views. + +use std::iter::Sum; +use std::ops::{Add, Deref, DerefMut}; + +use nu_ansi_term::{AnsiString as ANSIString, AnsiStrings as ANSIStrings, Style}; +use unicode_width::UnicodeWidthStr; + +/// An individual cell that holds text in a table, used in the details and +/// lines views to store ANSI-terminal-formatted data before it is printed. +/// +/// A text cell is made up of zero or more strings coupled with the +/// pre-computed length of all the strings combined. When constructing details +/// or grid-details tables, the length will have to be queried multiple times, +/// so it makes sense to cache it. +/// +/// (This used to be called `Cell`, but was renamed because there’s a Rust +/// type by that name too.) +#[derive(PartialEq, Debug, Clone, Default)] +pub struct TextCell { + /// The contents of this cell, as a vector of ANSI-styled strings. + pub contents: TextCellContents, + + /// The Unicode “display width” of this cell. + pub width: DisplayWidth, +} + +impl Deref for TextCell { + type Target = TextCellContents; + + fn deref(&self) -> &Self::Target { + &self.contents + } +} + +impl TextCell { + /// Creates a new text cell that holds the given text in the given style, + /// computing the Unicode width of the text. + pub fn paint(style: Style, text: String) -> Self { + let width = DisplayWidth::from(&*text); + + Self { + contents: vec![style.paint(text)].into(), + width, + } + } + + /// Creates a new text cell that holds the given text in the given style, + /// computing the Unicode width of the text. (This could be merged with + /// `paint`, but.) + pub fn paint_str(style: Style, text: &'static str) -> Self { + let width = DisplayWidth::from(text); + + Self { + contents: vec![style.paint(text)].into(), + width, + } + } + + /// Creates a new “blank” text cell that contains a single hyphen in the + /// given style, which should be the “punctuation” style from a `Colours` + /// value. + /// + /// This is used in place of empty table cells, as it is easier to read + /// tabular data when there is *something* in each cell. + pub fn blank(style: Style) -> Self { + Self { + contents: vec![style.paint("-")].into(), + width: DisplayWidth::from(1), + } + } + + /// Adds the given number of unstyled spaces after this cell. + /// + /// This method allocates a `String` to hold the spaces. + pub fn add_spaces(&mut self, count: usize) { + (*self.width) += count; + + let spaces: String = " ".repeat(count); + self.contents.0.push(Style::default().paint(spaces)); + } + + /// Adds the contents of another `ANSIString` to the end of this cell. + pub fn push(&mut self, string: ANSIString<'static>, extra_width: usize) { + self.contents.0.push(string); + (*self.width) += extra_width; + } + + /// Adds all the contents of another `TextCell` to the end of this cell. + pub fn append(&mut self, other: Self) { + (*self.width) += *other.width; + self.contents.0.extend(other.contents.0); + } +} + +// I’d like to eventually abstract cells so that instead of *every* cell +// storing a vector, only variable-length cells would, and individual cells +// would just store an array of a fixed length (which would usually be just 1 +// or 2), which wouldn’t require a heap allocation. +// +// For examples, look at the `render_*` methods in the `Table` object in the +// details view: +// +// - `render_blocks`, `inode`, and `links` will always return a +// one-string-long TextCell; +// - `render_size` will return one or two strings in a TextCell, depending on +// the size and whether one is present; +// - `render_permissions` will return ten or eleven strings; +// - `filename` and `symlink_filename` in the output module root return six or +// five strings. +// +// In none of these cases are we dealing with a *truly variable* number of +// strings: it is only when the strings are concatenated together do we need a +// growable, heap-allocated buffer. +// +// So it would be nice to abstract the `TextCell` type so instead of a `Vec`, +// it can use anything of type `T: IntoIterator>`. +// This would allow us to still hold all the data, but allocate less. +// +// But exa still has bugs and I need to fix those first :( + +/// The contents of a text cell, as a vector of ANSI-styled strings. +/// +/// It’s possible to use this type directly in the case where you want a +/// `TextCell` but aren’t concerned with tracking its width, because it occurs +/// in the final cell of a table or grid and there’s no point padding it. This +/// happens when dealing with file names. +#[derive(PartialEq, Debug, Clone, Default)] +pub struct TextCellContents(Vec>); + +impl From>> for TextCellContents { + fn from(strings: Vec>) -> Self { + Self(strings) + } +} + +impl Deref for TextCellContents { + type Target = [ANSIString<'static>]; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +// No DerefMut implementation here — it would be publicly accessible, and as +// the contents only get changed in this module, the mutators in the struct +// above can just access the value directly. + +impl TextCellContents { + /// Produces an `ANSIStrings` value that can be used to print the styled + /// values of this cell as an ANSI-terminal-formatted string. + pub fn strings(&self) -> ANSIStrings<'_> { + ANSIStrings(&self.0) + } + + /// Calculates the width that a cell with these contents would take up, by + /// counting the number of characters in each unformatted ANSI string. + pub fn width(&self) -> DisplayWidth { + self.0 + .iter() + .map(|anstr| DisplayWidth::from(anstr.as_str())) + .sum() + } + + /// Promotes these contents to a full cell containing them alongside + /// their calculated width. + pub fn promote(self) -> TextCell { + TextCell { + width: self.width(), + contents: self, + } + } +} + +/// The Unicode “display width” of a string. +/// +/// This is related to the number of *graphemes* of a string, rather than the +/// number of *characters*, or *bytes*: although most characters are one +/// column wide, a few can be two columns wide, and this is important to note +/// when calculating widths for displaying tables in a terminal. +/// +/// This type is used to ensure that the width, rather than the length, is +/// used when constructing a `TextCell` — it’s too easy to write something +/// like `file_name.len()` and assume it will work! +/// +/// It has `From` impls that convert an input string or fixed with to values +/// of this type, and will `Deref` to the contained `usize` value. +#[derive(PartialEq, Eq, Debug, Clone, Copy, Default)] +pub struct DisplayWidth(usize); + +impl<'a> From<&'a str> for DisplayWidth { + fn from(input: &'a str) -> Self { + Self(UnicodeWidthStr::width(input)) + } +} + +impl From for DisplayWidth { + fn from(width: usize) -> Self { + Self(width) + } +} + +impl Deref for DisplayWidth { + type Target = usize; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for DisplayWidth { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl Add for DisplayWidth { + type Output = Self; + + fn add(self, rhs: Self) -> Self::Output { + Self(self.0 + rhs.0) + } +} + +impl Add for DisplayWidth { + type Output = Self; + + fn add(self, rhs: usize) -> Self::Output { + Self(self.0 + rhs) + } +} + +impl Sum for DisplayWidth { + fn sum(iter: I) -> Self + where + I: Iterator, + { + iter.fold(Self(0), Add::add) + } +} + +#[cfg(test)] +mod width_unit_test { + use super::DisplayWidth; + + #[test] + fn empty_string() { + let cell = DisplayWidth::from(""); + assert_eq!(*cell, 0); + } + + #[test] + fn test_string() { + let cell = DisplayWidth::from("Diss Playwidth"); + assert_eq!(*cell, 14); + } + + #[test] + fn addition() { + let cell_one = DisplayWidth::from("/usr/bin/"); + let cell_two = DisplayWidth::from("drinking"); + assert_eq!(*(cell_one + cell_two), 17); + } + + #[test] + fn addition_usize() { + let cell = DisplayWidth::from("/usr/bin/"); + assert_eq!(*(cell + 8), 17); + } +} diff --git a/collector/compile-benchmarks/eza-0.21.2/src/output/color_scale.rs b/collector/compile-benchmarks/eza-0.21.2/src/output/color_scale.rs new file mode 100644 index 000000000..a273b818d --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/src/output/color_scale.rs @@ -0,0 +1,262 @@ +// SPDX-FileCopyrightText: 2024 Christina Sørensen +// SPDX-License-Identifier: EUPL-1.2 +// +// SPDX-FileCopyrightText: 2023-2024 Christina Sørensen, eza contributors +// SPDX-FileCopyrightText: 2014 Benjamin Sago +// SPDX-License-Identifier: MIT +use log::trace; +use nu_ansi_term::{Color as Colour, Style}; +use palette::{FromColor, LinSrgb, Oklab, Srgb}; + +use crate::{ + fs::{dir_action::RecurseOptions, feature::git::GitCache, fields::Size, DotFilter, File}, + output::{table::TimeType, tree::TreeDepth}, +}; + +#[derive(PartialEq, Eq, Debug, Copy, Clone)] +pub struct ColorScaleOptions { + pub mode: ColorScaleMode, + pub min_luminance: isize, + pub size: bool, + pub age: bool, +} + +impl Default for ColorScaleOptions { + fn default() -> Self { + Self { + mode: ColorScaleMode::Fixed, + min_luminance: 50, + size: false, + age: false, + } + } +} + +#[derive(PartialEq, Eq, Debug, Copy, Clone)] +pub enum ColorScaleMode { + Fixed, + Gradient, +} + +#[derive(Copy, Clone, Debug)] +pub struct ColorScaleInformation { + pub options: ColorScaleOptions, + + pub accessed: Option, + pub changed: Option, + pub created: Option, + pub modified: Option, + + pub size: Option, +} + +impl ColorScaleInformation { + pub fn from_color_scale( + color_scale: ColorScaleOptions, + files: &[File<'_>], + dot_filter: DotFilter, + git: Option<&GitCache>, + git_ignoring: bool, + r: Option, + ) -> Option { + if color_scale.mode == ColorScaleMode::Fixed { + None + } else { + let mut information = Self { + options: color_scale, + accessed: None, + changed: None, + created: None, + modified: None, + size: None, + }; + + update_information_recursively( + &mut information, + files, + dot_filter, + git, + git_ignoring, + TreeDepth::root(), + r, + ); + + Some(information) + } + } + + pub fn adjust_style(&self, mut style: Style, value: f32, range: Option) -> Style { + if let (Some(fg), Some(range)) = (style.foreground, range) { + let mut ratio = ((value - range.min) / (range.max - range.min)).clamp(0.0, 1.0); + if ratio.is_nan() { + ratio = 1.0; + } + + style.foreground = Some(adjust_luminance( + fg, + ratio, + self.options.min_luminance as f32 / 100.0, + )); + } + + style + } + + pub fn apply_time_gradient(&self, style: Style, file: &File<'_>, time_type: TimeType) -> Style { + let range = match time_type { + TimeType::Modified => self.modified, + TimeType::Changed => self.changed, + TimeType::Accessed => self.accessed, + TimeType::Created => self.created, + }; + + if let Some(file_time) = time_type.get_corresponding_time(file) { + self.adjust_style(style, file_time.and_utc().timestamp_millis() as f32, range) + } else { + style + } + } +} + +fn update_information_recursively( + information: &mut ColorScaleInformation, + files: &[File<'_>], + dot_filter: DotFilter, + git: Option<&GitCache>, + git_ignoring: bool, + depth: TreeDepth, + r: Option, +) { + for file in files { + if information.options.age { + Extremes::update( + file.created_time() + .map(|x| x.and_utc().timestamp_millis() as f32), + &mut information.created, + ); + Extremes::update( + file.modified_time() + .map(|x| x.and_utc().timestamp_millis() as f32), + &mut information.modified, + ); + Extremes::update( + file.accessed_time() + .map(|x| x.and_utc().timestamp_millis() as f32), + &mut information.accessed, + ); + Extremes::update( + file.changed_time() + .map(|x| x.and_utc().timestamp_millis() as f32), + &mut information.changed, + ); + } + + if information.options.size { + let size = match file.size() { + Size::Some(size) => Some(size as f32), + _ => None, + }; + Extremes::update(size, &mut information.size); + } + + // We don't want to recurse into . and .., but still want to list them, therefore bypass + // the dot_filter. + if file.is_directory() + && r.is_some_and(|x| !x.is_too_deep(depth.0)) + && file.name != "." + && file.name != ".." + { + match file.to_dir() { + Ok(dir) => { + let files: Vec> = dir + .files(dot_filter, git, git_ignoring, false, false) + .collect(); + + update_information_recursively( + information, + &files, + dot_filter, + git, + git_ignoring, + depth.deeper(), + r, + ); + } + Err(e) => trace!("Unable to access directory {}: {}", file.name, e), + } + }; + } +} + +#[derive(Copy, Clone, Debug)] +pub struct Extremes { + max: f32, + min: f32, +} + +impl Extremes { + fn update(maybe_value: Option, maybe_range: &mut Option) { + match (maybe_value, maybe_range) { + (Some(value), Some(range)) => { + if value > range.max { + range.max = value; + } else if value < range.min { + range.min = value; + }; + } + (Some(value), rel) => { + let _ = rel.insert({ + Extremes { + max: value, + min: value, + } + }); + } + _ => (), + }; + } +} + +fn adjust_luminance(color: Colour, x: f32, min_l: f32) -> Colour { + let rgb_color = match color { + Colour::Rgb(r, g, b) => LinSrgb::new( + f32::from(r) / 255.0, + f32::from(g) / 255.0, + f32::from(b) / 255.0, + ), + + Colour::Black => LinSrgb::new(0.0, 0.0, 0.0), + + Colour::Green | Colour::LightGreen => LinSrgb::new(0.0, 1.0, 0.0), + + Colour::Yellow | Colour::LightYellow => LinSrgb::new(1.0, 1.0, 0.0), + + Colour::Blue | Colour::LightBlue => LinSrgb::new(0.0, 0.0, 1.0), + + Colour::Magenta | Colour::LightMagenta => LinSrgb::new(1.0, 0.0, 1.0), + + Colour::Cyan | Colour::LightCyan => LinSrgb::new(0.0, 1.0, 1.0), + + Colour::White => LinSrgb::new(1.0, 1.0, 1.0), + + Colour::LightGray => LinSrgb::new(0.5, 0.5, 0.5), + + Colour::LightRed | Colour::Red => LinSrgb::new(1.0, 0.0, 0.0), + + Colour::DarkGray => LinSrgb::new(0.25, 0.25, 0.25), + + Colour::LightPurple | Colour::Purple => LinSrgb::new(0.5, 0.0, 0.5), + + _ => LinSrgb::new(1.0, 1.0, 1.0), + }; + + let mut lab: Oklab = Oklab::from_color(rgb_color); + lab.l = (min_l + (1.0 - min_l) * (-4.0 * (1.0 - x)).exp()).clamp(0.0, 1.0); + + let adjusted_rgb: Srgb = Srgb::from_color(lab); + Colour::Rgb( + (adjusted_rgb.red * 255.0).round() as u8, + (adjusted_rgb.green * 255.0).round() as u8, + (adjusted_rgb.blue * 255.0).round() as u8, + ) +} diff --git a/collector/compile-benchmarks/eza-0.21.2/src/output/details.rs b/collector/compile-benchmarks/eza-0.21.2/src/output/details.rs new file mode 100644 index 000000000..343656f45 --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/src/output/details.rs @@ -0,0 +1,535 @@ +// SPDX-FileCopyrightText: 2024 Christina Sørensen +// SPDX-License-Identifier: EUPL-1.2 +// +// SPDX-FileCopyrightText: 2023-2024 Christina Sørensen, eza contributors +// SPDX-FileCopyrightText: 2014 Benjamin Sago +// SPDX-License-Identifier: MIT +//! The **Details** output view displays each file as a row in a table. +//! +//! It’s used in the following situations: +//! +//! - Most commonly, when using the `--long` command-line argument to display the +//! details of each file, which requires using a table view to hold all the data; +//! - When using the `--tree` argument, which uses the same table view to display +//! each file on its own line, with the table providing the tree characters; +//! - When using both the `--long` and `--grid` arguments, which constructs a +//! series of tables to fit all the data on the screen. +//! +//! You will probably recognise it from the `ls --long` command. It looks like +//! this: +//! +//! ```text +//! .rw-r--r-- 9.6k ben 29 Jun 16:16 Cargo.lock +//! .rw-r--r-- 547 ben 23 Jun 10:54 Cargo.toml +//! .rw-r--r-- 1.1k ben 23 Nov 2014 LICENCE +//! .rw-r--r-- 2.5k ben 21 May 14:38 README.md +//! .rw-r--r-- 382k ben 8 Jun 21:00 screenshot.png +//! drwxr-xr-x - ben 29 Jun 14:50 src +//! drwxr-xr-x - ben 28 Jun 19:53 target +//! ``` +//! +//! The table is constructed by creating a `Table` value, which produces a `Row` +//! value for each file. These rows can contain a vector of `Cell`s, or they can +//! contain depth information for the tree view, or both. These are described +//! below. +//! +//! +//! ## Constructing Detail Views +//! +//! When using the `--long` command-line argument, the details of each file are +//! displayed next to its name. +//! +//! The table holds a vector of all the column types. For each file and column, a +//! `Cell` value containing the ANSI-coloured text and Unicode width of each cell +//! is generated, with the row and column determined by indexing into both arrays. +//! +//! The column types vector does not actually include the filename. This is +//! because the filename is always the rightmost field, and as such, it does not +//! need to have its width queried or be padded with spaces. +//! +//! To illustrate the above: +//! +//! ```text +//! ┌─────────────────────────────────────────────────────────────────────────┐ +//! │ columns: [ Permissions, Size, User, Date(Modified) ] │ +//! ├─────────────────────────────────────────────────────────────────────────┤ +//! │ rows: cells: filename: │ +//! │ row 1: [ ".rw-r--r--", "9.6k", "ben", "29 Jun 16:16" ] Cargo.lock │ +//! │ row 2: [ ".rw-r--r--", "547", "ben", "23 Jun 10:54" ] Cargo.toml │ +//! │ row 3: [ "drwxr-xr-x", "-", "ben", "29 Jun 14:50" ] src │ +//! │ row 4: [ "drwxr-xr-x", "-", "ben", "28 Jun 19:53" ] target │ +//! └─────────────────────────────────────────────────────────────────────────┘ +//! ``` +//! +//! Each column in the table needs to be resized to fit its widest argument. This +//! means that we must wait until every row has been added to the table before it +//! can be displayed, in order to make sure that every column is wide enough. + +use std::io::{self, Write}; +use std::path::PathBuf; +use std::vec::IntoIter as VecIntoIter; + +use nu_ansi_term::Style; +use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; + +use log::*; + +use crate::fs::dir_action::RecurseOptions; +use crate::fs::feature::git::GitCache; +use crate::fs::feature::xattr::Attribute; +use crate::fs::fields::SecurityContextType; +use crate::fs::filter::FileFilter; +use crate::fs::{Dir, File}; +use crate::output::cell::TextCell; +use crate::output::color_scale::{ColorScaleInformation, ColorScaleOptions}; +use crate::output::file_name::Options as FileStyle; +use crate::output::table::{Options as TableOptions, Row as TableRow, Table}; +use crate::output::tree::{TreeDepth, TreeParams, TreeTrunk}; +use crate::theme::Theme; + +/// With the **Details** view, the output gets formatted into columns, with +/// each `Column` object showing some piece of information about the file, +/// such as its size, or its permissions. +/// +/// To do this, the results have to be written to a table, instead of +/// displaying each file immediately. Then, the width of each column can be +/// calculated based on the individual results, and the fields are padded +/// during output. +/// +/// Almost all the heavy lifting is done in a Table object, which handles the +/// columns for each row. +#[allow(clippy::struct_excessive_bools)] +/// This clearly isn't a state machine +#[derive(PartialEq, Eq, Debug)] +pub struct Options { + /// Options specific to drawing a table. + /// + /// Directories themselves can pick which columns are *added* to this + /// list, such as the Git column. + pub table: Option, + + /// Whether to show a header line or not. + pub header: bool, + + /// Whether to show each file’s extended attributes. + pub xattr: bool, + + /// Whether to show each file's security attribute. + pub secattr: bool, + + /// Whether to show a directory's mounted filesystem details + pub mounts: bool, + + pub color_scale: ColorScaleOptions, + + /// Whether to drill down into symbolic links that point to directories + pub follow_links: bool, +} + +pub struct Render<'a> { + pub dir: Option<&'a Dir>, + pub files: Vec>, + pub theme: &'a Theme, + pub file_style: &'a FileStyle, + pub opts: &'a Options, + + /// Whether to recurse through directories with a tree view, and if so, + /// which options to use. This field is only relevant here if the `tree` + /// field of the `RecurseOptions` is `true`. + pub recurse: Option, + + /// How to sort and filter the files after getting their details. + pub filter: &'a FileFilter, + + /// Whether we are skipping Git-ignored files. + pub git_ignoring: bool, + + pub git: Option<&'a GitCache>, + + pub git_repos: bool, +} + +#[rustfmt::skip] +struct Egg<'a> { + table_row: Option, + xattrs: &'a [Attribute], + errors: Vec<(io::Error, Option)>, + dir: Option

, + file: &'a File<'a>, +} + +impl<'a> AsRef> for Egg<'a> { + fn as_ref(&self) -> &File<'a> { + self.file + } +} + +impl<'a> Render<'a> { + pub fn render(mut self, w: &mut W) -> io::Result<()> { + let mut rows = Vec::new(); + + let color_scale_info = ColorScaleInformation::from_color_scale( + self.opts.color_scale, + &self.files, + self.filter.dot_filter, + self.git, + self.git_ignoring, + self.recurse, + ); + + if let Some(ref table) = self.opts.table { + match (self.git, self.dir) { + (Some(g), Some(d)) => { + if !g.has_anything_for(&d.path) { + self.git = None; + } + } + (Some(g), None) => { + if !self.files.iter().any(|f| g.has_anything_for(&f.path)) { + self.git = None; + } + } + (None, _) => { /* Keep Git how it is */ } + } + + let mut table = Table::new(table, self.git, self.theme, self.git_repos); + + if self.opts.header { + let header = table.header_row(); + table.add_widths(&header); + rows.push(self.render_header(header)); + } + + // This is weird, but I can’t find a way around it: + // https://internals.rust-lang.org/t/should-option-mut-t-implement-copy/3715/6 + let mut table = Some(table); + self.add_files_to_table( + &mut table, + &mut rows, + &self.files, + TreeDepth::root(), + color_scale_info, + ); + + for row in self.iterate_with_table(table.unwrap(), rows) { + writeln!(w, "{}", row.strings())?; + } + } else { + self.add_files_to_table( + &mut None, + &mut rows, + &self.files, + TreeDepth::root(), + color_scale_info, + ); + + for row in self.iterate(rows) { + writeln!(w, "{}", row.strings())?; + } + } + + Ok(()) + } + + /// Whether to show the extended attribute hint + pub fn show_xattr_hint(&self, file: &File<'_>) -> bool { + // Do not show the hint '@' if the only extended attribute is the security + // attribute and the security attribute column is active. + let xattr_count = file.extended_attributes().len(); + let selinux_ctx_shown = self.opts.secattr + && match file.security_context().context { + SecurityContextType::SELinux(_) => true, + SecurityContextType::None => false, + }; + xattr_count > 1 || (xattr_count == 1 && !selinux_ctx_shown) + } + + /// Adds files to the table, possibly recursively. This is easily + /// parallelisable, and uses a pool of threads. + fn add_files_to_table<'dir>( + &self, + table: &mut Option>, + rows: &mut Vec, + src: &[File<'dir>], + depth: TreeDepth, + color_scale_info: Option, + ) { + use crate::fs::feature::xattr; + + let mut file_eggs: Vec<_> = src + .par_iter() + .map(|file| { + let mut errors = Vec::new(); + + // There are three “levels” of extended attribute support: + // + // 1. If we’re compiling without that feature, then + // exa pretends all files have no attributes. + // 2. If the feature is enabled and the --extended flag + // has been specified, then display an @ in the + // permissions column for files with attributes, the + // names of all attributes and their values, and any + // errors encountered when getting them. + // 3. If the --extended flag *hasn’t* been specified, then + // display the @, but don’t display anything else. + // + // For a while, exa took a stricter approach to (3): + // if an error occurred while checking a file’s xattrs to + // see if it should display the @, exa would display that + // error even though the attributes weren’t actually being + // shown! This was confusing, as users were being shown + // errors for something they didn’t explicitly ask for, + // and just cluttered up the output. So now errors aren’t + // printed unless the user passes --extended to signify + // that they want to see them. + + let xattrs: &[Attribute] = if xattr::ENABLED && self.opts.xattr { + file.extended_attributes() + } else { + &[] + }; + + let table_row = table + .as_ref() + .map(|t| t.row_for_file(file, self.show_xattr_hint(file), color_scale_info)); + + let mut dir = None; + let follow_links = self.opts.follow_links; + if let Some(r) = self.recurse { + if (if follow_links { + file.points_to_directory() + } else { + file.is_directory() + }) && r.tree + && !r.is_too_deep(depth.0) + { + trace!("matching on to_dir"); + match file.to_dir() { + Ok(d) => { + dir = Some(d); + } + Err(e) => { + errors.push((e, None)); + } + } + } + }; + + Egg { + table_row, + xattrs, + errors, + dir, + file, + } + }) + .collect(); + + // this is safe because all entries have been initialized above + self.filter.sort_files(&mut file_eggs); + + for (tree_params, egg) in depth.iterate_over(file_eggs.into_iter()) { + let mut files = Vec::new(); + let errors = egg.errors; + + if let (Some(ref mut t), Some(row)) = (table.as_mut(), egg.table_row.as_ref()) { + t.add_widths(row); + } + + let file_name = self + .file_style + .for_file(egg.file, self.theme) + .with_link_paths() + .with_mount_details(self.opts.mounts) + .paint() + .promote(); + + debug!("file_name {:?}", file_name); + + let row = Row { + tree: tree_params, + cells: egg.table_row, + name: file_name, + }; + + rows.push(row); + + if let Some(ref dir) = egg.dir { + for file_to_add in dir.files( + self.filter.dot_filter, + self.git, + self.git_ignoring, + egg.file.deref_links, + egg.file.is_recursive_size(), + ) { + files.push(file_to_add); + } + + self.filter + .filter_child_files(self.recurse.is_some(), &mut files); + + if !files.is_empty() { + for xattr in egg.xattrs { + rows.push(self.render_xattr(xattr, TreeParams::new(depth.deeper(), false))); + } + + for (error, path) in errors { + rows.push(self.render_error( + &error, + TreeParams::new(depth.deeper(), false), + path, + )); + } + + self.add_files_to_table(table, rows, &files, depth.deeper(), color_scale_info); + continue; + } + } + + let count = egg.xattrs.len(); + for (index, xattr) in egg.xattrs.iter().enumerate() { + let params = + TreeParams::new(depth.deeper(), errors.is_empty() && index == count - 1); + let r = self.render_xattr(xattr, params); + rows.push(r); + } + + let count = errors.len(); + for (index, (error, path)) in errors.into_iter().enumerate() { + let params = TreeParams::new(depth.deeper(), index == count - 1); + let r = self.render_error(&error, params, path); + rows.push(r); + } + } + } + + pub fn render_header(&self, header: TableRow) -> Row { + Row { + tree: TreeParams::new(TreeDepth::root(), false), + cells: Some(header), + name: TextCell::paint_str(self.theme.ui.header.unwrap_or_default(), "Name"), + } + } + + fn render_error(&self, error: &io::Error, tree: TreeParams, path: Option) -> Row { + use crate::output::file_name::Colours; + + let error_message = if let Some(path) = path { + format!("<{}: {}>", path.display(), error) + } else { + format!("<{error}>") + }; + + // TODO: broken_symlink() doesn’t quite seem like the right name for + // the style that’s being used here. Maybe split it in two? + let name = TextCell::paint(self.theme.broken_symlink(), error_message); + Row { + cells: None, + name, + tree, + } + } + + fn render_xattr(&self, xattr: &Attribute, tree: TreeParams) -> Row { + let name = TextCell::paint( + self.theme.ui.perms.unwrap_or_default().attribute(), + format!("{xattr}"), + ); + Row { + cells: None, + name, + tree, + } + } + + pub fn iterate_with_table(&'a self, table: Table<'a>, rows: Vec) -> TableIter<'a> { + TableIter { + tree_trunk: TreeTrunk::default(), + total_width: table.widths().total(), + table, + inner: rows.into_iter(), + tree_style: self.theme.ui.punctuation.unwrap_or_default(), + } + } + + pub fn iterate(&'a self, rows: Vec) -> Iter { + Iter { + tree_trunk: TreeTrunk::default(), + inner: rows.into_iter(), + tree_style: self.theme.ui.punctuation.unwrap_or_default(), + } + } +} + +pub struct Row { + /// Vector of cells to display. + /// + /// Most of the rows will be used to display files’ metadata, so this will + /// almost always be `Some`, containing a vector of cells. It will only be + /// `None` for a row displaying an attribute or error, neither of which + /// have cells. + pub cells: Option, + + /// This file’s name, in coloured output. The name is treated separately + /// from the other cells, as it never requires padding. + pub name: TextCell, + + /// Information used to determine which symbols to display in a tree. + pub tree: TreeParams, +} + +#[rustfmt::skip] +pub struct TableIter<'a> { + inner: VecIntoIter, + table: Table<'a>, + + total_width: usize, + tree_style: Style, + tree_trunk: TreeTrunk, +} + +impl<'a> Iterator for TableIter<'a> { + type Item = TextCell; + + fn next(&mut self) -> Option { + self.inner.next().map(|row| { + let mut cell = if let Some(cells) = row.cells { + self.table.render(cells) + } else { + let mut cell = TextCell::default(); + cell.add_spaces(self.total_width); + cell + }; + + for tree_part in self.tree_trunk.new_row(row.tree) { + cell.push(self.tree_style.paint(tree_part.ascii_art()), 4); + } + + cell.append(row.name); + cell + }) + } +} + +pub struct Iter { + tree_trunk: TreeTrunk, + tree_style: Style, + inner: VecIntoIter, +} + +impl Iterator for Iter { + type Item = TextCell; + + fn next(&mut self) -> Option { + self.inner.next().map(|row| { + let mut cell = TextCell::default(); + + for tree_part in self.tree_trunk.new_row(row.tree) { + cell.push(self.tree_style.paint(tree_part.ascii_art()), 4); + } + + cell.append(row.name); + cell + }) + } +} diff --git a/collector/compile-benchmarks/eza-0.21.2/src/output/escape.rs b/collector/compile-benchmarks/eza-0.21.2/src/output/escape.rs new file mode 100644 index 000000000..69f136c96 --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/src/output/escape.rs @@ -0,0 +1,45 @@ +// SPDX-FileCopyrightText: 2024 Christina Sørensen +// SPDX-License-Identifier: EUPL-1.2 +// +// SPDX-FileCopyrightText: 2023-2024 Christina Sørensen, eza contributors +// SPDX-FileCopyrightText: 2014 Benjamin Sago +// SPDX-License-Identifier: MIT +use super::file_name::QuoteStyle; +use nu_ansi_term::{AnsiString as ANSIString, Style}; + +pub fn escape( + string: String, + bits: &mut Vec>, + good: Style, + bad: Style, + quote_style: QuoteStyle, +) { + let bits_starting_length = bits.len(); + let needs_quotes = string.contains(' ') || string.contains('\''); + let quote_bit = good.paint(if string.contains('\'') { "\"" } else { "\'" }); + + if string + .chars() + .all(|c| c >= 0x20 as char && c != 0x7f as char) + { + bits.push(good.paint(string)); + } else { + for c in string.chars() { + // The `escape_default` method on `char` is *almost* what we want here, but + // it still escapes non-ASCII UTF-8 characters, which are still printable. + + // TODO: This allocates way too much, + // hence the `all` check above. + if c >= 0x20 as char && c != 0x7f as char { + bits.push(good.paint(c.to_string())); + } else { + bits.push(bad.paint(c.escape_default().to_string())); + } + } + } + + if quote_style != QuoteStyle::NoQuotes && needs_quotes { + bits.insert(bits_starting_length, quote_bit.clone()); + bits.push(quote_bit); + } +} diff --git a/collector/compile-benchmarks/eza-0.21.2/src/output/file_name.rs b/collector/compile-benchmarks/eza-0.21.2/src/output/file_name.rs new file mode 100644 index 000000000..ae232a3d9 --- /dev/null +++ b/collector/compile-benchmarks/eza-0.21.2/src/output/file_name.rs @@ -0,0 +1,547 @@ +// SPDX-FileCopyrightText: 2024 Christina Sørensen +// SPDX-License-Identifier: EUPL-1.2 +// +// SPDX-FileCopyrightText: 2023-2024 Christina Sørensen, eza contributors +// SPDX-FileCopyrightText: 2014 Benjamin Sago +// SPDX-License-Identifier: MIT +use std::fmt::Debug; +use std::path::Path; + +use nu_ansi_term::{AnsiString as ANSIString, Style}; +use path_clean; +use unicode_width::UnicodeWidthStr; + +use crate::fs::{File, FileTarget}; +use crate::output::cell::TextCellContents; +use crate::output::escape; +use crate::output::icons::{icon_for_file, iconify_style}; +use crate::output::render::FiletypeColours; +use crate::theme::FileNameStyle; + +/// Basically a file name factory. +#[derive(Debug, Copy, Clone)] +pub struct Options { + /// Whether to append file class characters to file names. + pub classify: Classify, + + /// Whether to prepend icon characters before file names. + pub show_icons: ShowIcons, + + /// How to display file names with spaces (with or without quotes). + pub quote_style: QuoteStyle, + + /// Whether to make file names hyperlinks. + pub embed_hyperlinks: EmbedHyperlinks, + + /// Whether to display files with their absolute path. + pub absolute: Absolute, + + /// Whether we are in a console or redirecting the output + pub is_a_tty: bool, +} + +impl Options { + /// Create a new `FileName` that prints the given file’s name, painting it + /// with the remaining arguments. + pub fn for_file<'a, 'dir, C>( + self, + file: &'a File<'dir>, + colours: &'a C, + ) -> FileName<'a, 'dir, C> { + FileName { + file, + colours, + link_style: LinkStyle::JustFilenames, + options: self, + target: if file.is_link() { + Some(file.link_target()) + } else { + None + }, + mount_style: MountStyle::JustDirectoryNames, + } + } +} + +/// When displaying a file name, there needs to be some way to handle broken +/// links, depending on how long the resulting Cell can be. +#[derive(PartialEq, Debug, Copy, Clone)] +enum LinkStyle { + /// Just display the file names, but colour them differently if they’re + /// a broken link or can’t be followed. + JustFilenames, + + /// Display all files in their usual style, but follow each link with an + /// arrow pointing to their path, colouring the path differently if it’s + /// a broken link, and doing nothing if it can’t be followed. + FullLinkPaths, +} + +/// Whether to append file class characters to the file names. +#[derive(PartialEq, Eq, Debug, Default, Copy, Clone)] +pub enum Classify { + /// Just display the file names, without any characters. + #[default] + JustFilenames, + + /// Always add a character after the file name depending on what class of + /// file it is. + AddFileIndicators, + + // Like previous, but only when output is going to a terminal, not otherwise. + AutomaticAddFileIndicators, +} + +/// When displaying a directory name, there needs to be some way to handle +/// mount details, depending on how long the resulting Cell can be. +#[derive(PartialEq, Debug, Copy, Clone)] +enum MountStyle { + /// Just display the directory names. + JustDirectoryNames, + + /// Display mount points as directories and include information about + /// the filesystem that's mounted there. + MountInfo, +} + +/// Whether and how to show icons. +#[derive(PartialEq, Debug, Copy, Clone)] +pub enum ShowIcons { + /// Display icons next to file names, with the given number of spaces between + /// the icon and the file name, even when output isn’t going to a terminal. + Always(u32), + + /// Same as Always, but only when output is going to a terminal, not otherwise. + Automatic(u32), + + /// Never display them, even when output is going to a terminal. + Never, +} + +/// Whether to embed hyperlinks. +#[derive(PartialEq, Eq, Debug, Copy, Clone)] +pub enum EmbedHyperlinks { + Off, + On, +} + +/// Whether to show absolute paths +#[derive(PartialEq, Eq, Debug, Copy, Clone)] +pub enum Absolute { + On, + Follow, + Off, +} + +/// Whether or not to wrap file names with spaces in quotes. +#[derive(PartialEq, Debug, Copy, Clone)] +pub enum QuoteStyle { + /// Don't ever quote file names. + NoQuotes, + + /// Use single quotes for file names that contain spaces and no single quotes + /// Use double quotes for file names that contain single quotes. + QuoteSpaces, +} + +/// A **file name** holds all the information necessary to display the name +/// of the given file. This is used in all of the views. +pub struct FileName<'a, 'dir, C> { + /// A reference to the file that we’re getting the name of. + file: &'a File<'dir>, + + /// The colours used to paint the file name and its surrounding text. + colours: &'a C, + + /// The file that this file points to if it’s a link. + target: Option>, // todo: remove? + + /// How to handle displaying links. + link_style: LinkStyle, + + pub options: Options, + + /// How to handle displaying a mounted filesystem. + mount_style: MountStyle, +} + +impl<'a, 'dir, C> FileName<'a, 'dir, C> { + /// Sets the flag on this file name to display link targets with an + /// arrow followed by their path. + pub fn with_link_paths(mut self) -> Self { + if !self.file.deref_links { + self.link_style = LinkStyle::FullLinkPaths; + } + self + } + + /// Sets the flag on this file name to display mounted filesystem + ///details. + pub fn with_mount_details(mut self, enable: bool) -> Self { + self.mount_style = if enable { + MountStyle::MountInfo + } else { + MountStyle::JustDirectoryNames + }; + self + } +} + +impl<'a, 'dir, C: Colours> FileName<'a, 'dir, C> { + /// Paints the name of the file using the colours, resulting in a vector + /// of coloured cells that can be printed to the terminal. + /// + /// This method returns some `TextCellContents`, rather than a `TextCell`, + /// because for the last cell in a table, it doesn’t need to have its + /// width calculated. + pub fn paint(&self) -> TextCellContents { + let mut bits = Vec::new(); + let (icon_override, filename_style_override) = match self.colours.style_override(self.file) + { + Some(FileNameStyle { icon, filename }) => (icon, filename), + None => (None, None), + }; + + let spaces_count_opt = match self.options.show_icons { + ShowIcons::Always(spaces_count) => Some(spaces_count), + ShowIcons::Automatic(spaces_count) if self.options.is_a_tty => Some(spaces_count), + _ => None, + }; + + let should_add_classify_char = match self.options.classify { + Classify::AddFileIndicators => true, + Classify::AutomaticAddFileIndicators if self.options.is_a_tty => true, + _ => false, + }; + + if let Some(spaces_count) = spaces_count_opt { + let (style, icon) = match icon_override { + Some(icon_override) => ( + if let Some(style_override) = icon_override.style { + style_override + } else { + iconify_style(self.style()) + }, + icon_override + .glyph + .unwrap_or_else(|| icon_for_file(self.file)) + .to_string(), + ), + None => ( + iconify_style(self.style()), + icon_for_file(self.file).to_string(), + ), + }; + + bits.push(style.paint(icon)); + bits.push(style.paint(" ".repeat(spaces_count as usize))); + } + + if self.file.parent_dir.is_none() && self.options.absolute == Absolute::Off { + if let Some(parent) = self.file.path.parent() { + self.add_parent_bits(&mut bits, parent); + } + } + + if !self.file.name.is_empty() { + // The “missing file” colour seems like it should be used here, + // but it’s not! In a grid view, where there’s no space to display + // link targets, the filename has to have a different style to + // indicate this fact. But when showing targets, we can just + // colour the path instead (see below), and leave the broken + // link’s filename as the link colour. + for bit in self.escaped_file_name(filename_style_override) { + bits.push(bit); + } + } + + if let (LinkStyle::FullLinkPaths, Some(target)) = (self.link_style, self.target.as_ref()) { + match target { + FileTarget::Ok(target) => { + bits.push(Style::default().paint(" ")); + bits.push(self.colours.normal_arrow().paint("->")); + bits.push(Style::default().paint(" ")); + + if let Some(parent) = target.path.parent() { + self.add_parent_bits(&mut bits, parent); + } + + if !target.name.is_empty() { + let target_options = Options { + classify: Classify::JustFilenames, + quote_style: QuoteStyle::QuoteSpaces, + show_icons: ShowIcons::Never, + embed_hyperlinks: EmbedHyperlinks::Off, + is_a_tty: self.options.is_a_tty, + absolute: Absolute::Off, + }; + + let target_name = FileName { + file: target, + colours: self.colours, + target: None, + link_style: LinkStyle::FullLinkPaths, + options: target_options, + mount_style: MountStyle::JustDirectoryNames, + }; + + for bit in target_name.escaped_file_name(filename_style_override) { + bits.push(bit); + } + + if should_add_classify_char { + if let Some(class) = self.classify_char(target) { + bits.push(Style::default().paint(class)); + } + } + } + } + + FileTarget::Broken(broken_path) => { + bits.push(Style::default().paint(" ")); + bits.push(self.colours.broken_symlink().paint("->")); + bits.push(Style::default().paint(" ")); + + escape( + broken_path.display().to_string(), + &mut bits, + self.colours.broken_filename(), + self.colours.broken_control_char(), + self.options.quote_style, + ); + } + + FileTarget::Err(_) => { + // Do nothing — the error gets displayed on the next line + } + } + } else if should_add_classify_char { + if let Some(class) = self.classify_char(self.file) { + bits.push(Style::default().paint(class)); + } + } + + if self.mount_style == MountStyle::MountInfo { + if let Some(mount_details) = self.file.mount_point_info() { + // This is a filesystem mounted on the directory, output its details + bits.push(Style::default().paint(" [")); + bits.push(Style::default().paint(mount_details.source.clone())); + bits.push(Style::default().paint(" (")); + bits.push(Style::default().paint(mount_details.fstype.clone())); + bits.push(Style::default().paint(")]")); + } + } + + bits.into() + } + + /// Adds the bits of the parent path to the given bits vector. + /// The path gets its characters escaped based on the colours. + fn add_parent_bits(&self, bits: &mut Vec>, parent: &Path) { + let coconut = parent.components().count(); + + if coconut == 1 && parent.has_root() { + bits.push( + self.colours + .symlink_path() + .paint(std::path::MAIN_SEPARATOR.to_string()), + ); + } else if coconut >= 1 { + escape( + parent.to_string_lossy().to_string(), + bits, + self.colours.symlink_path(), + self.colours.control_char(), + self.options.quote_style, + ); + bits.push( + self.colours + .symlink_path() + .paint(std::path::MAIN_SEPARATOR.to_string()), + ); + } + } + + /// The character to be displayed after a file when classifying is on, if + /// the file’s type has one associated with it. + #[cfg(unix)] + pub(crate) fn classify_char(&self, file: &File<'_>) -> Option<&'static str> { + if file.is_executable_file() { + Some("*") + } else if file.is_directory() { + Some("/") + } else if file.is_pipe() { + Some("|") + } else if file.is_link() { + Some("@") + } else if file.is_socket() { + Some("=") + } else { + None + } + } + + #[cfg(windows)] + pub(crate) fn classify_char(&self, file: &File<'_>) -> Option<&'static str> { + if file.is_directory() { + Some("/") + } else if file.is_link() { + Some("@") + } else { + None + } + } + + /// Returns at least one ANSI-highlighted string representing this file’s + /// name using the given set of colours. + /// + /// If --hyperlink flag is provided, it will escape the filename accordingly. + /// + /// Ordinarily, this will be just one string: the file’s complete name, + /// coloured according to its file type. If the name contains control + /// characters such as newlines or escapes, though, we can’t just print them + /// to the screen directly, because then there’ll be newlines in weird places. + /// + /// So in that situation, those characters will be escaped and highlighted in + /// a different colour. + fn escaped_file_name<'unused>( + &self, + style_override: Option