├── .gitignore ├── LICENSE ├── LICENSE-THIRD-PARTY ├── README.rst ├── bin └── rootbox ├── install.sh ├── lib └── rootbox │ ├── LICENSE │ ├── LICENSE-THIRD-PARTY │ ├── argparser │ ├── LICENSE │ └── argparser │ ├── args.sh │ ├── box.sh │ ├── factory.sh │ ├── image.sh │ ├── imgtools.sh │ ├── init.sh │ ├── locations.sh │ ├── main.sh │ └── utils.sh ├── link.sh ├── remote-install.sh ├── update-argparser └── update-licenses /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | 1. Definitions 5 | -------------- 6 | 7 | 1.1. "Contributor" 8 | means each individual or legal entity that creates, contributes to 9 | the creation of, or owns Covered Software. 10 | 11 | 1.2. "Contributor Version" 12 | means the combination of the Contributions of others (if any) used 13 | by a Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | means Covered Software of a particular Contributor. 17 | 18 | 1.4. "Covered Software" 19 | means Source Code Form to which the initial Contributor has attached 20 | the notice in Exhibit A, the Executable Form of such Source Code 21 | Form, and Modifications of such Source Code Form, in each case 22 | including portions thereof. 23 | 24 | 1.5. "Incompatible With Secondary Licenses" 25 | means 26 | 27 | (a) that the initial Contributor has attached the notice described 28 | in Exhibit B to the Covered Software; or 29 | 30 | (b) that the Covered Software was made available under the terms of 31 | version 1.1 or earlier of the License, but not also under the 32 | terms of a Secondary License. 33 | 34 | 1.6. "Executable Form" 35 | means any form of the work other than Source Code Form. 36 | 37 | 1.7. "Larger Work" 38 | means a work that combines Covered Software with other material, in 39 | a separate file or files, that is not Covered Software. 40 | 41 | 1.8. "License" 42 | means this document. 43 | 44 | 1.9. "Licensable" 45 | means having the right to grant, to the maximum extent possible, 46 | whether at the time of the initial grant or subsequently, any and 47 | all of the rights conveyed by this License. 48 | 49 | 1.10. "Modifications" 50 | means any of the following: 51 | 52 | (a) any file in Source Code Form that results from an addition to, 53 | deletion from, or modification of the contents of Covered 54 | Software; or 55 | 56 | (b) any new file in Source Code Form that contains any Covered 57 | Software. 58 | 59 | 1.11. "Patent Claims" of a Contributor 60 | means any patent claim(s), including without limitation, method, 61 | process, and apparatus claims, in any patent Licensable by such 62 | Contributor that would be infringed, but for the grant of the 63 | License, by the making, using, selling, offering for sale, having 64 | made, import, or transfer of either its Contributions or its 65 | Contributor Version. 66 | 67 | 1.12. "Secondary License" 68 | means either the GNU General Public License, Version 2.0, the GNU 69 | Lesser General Public License, Version 2.1, the GNU Affero General 70 | Public License, Version 3.0, or any later versions of those 71 | licenses. 72 | 73 | 1.13. "Source Code Form" 74 | means the form of the work preferred for making modifications. 75 | 76 | 1.14. "You" (or "Your") 77 | means an individual or a legal entity exercising rights under this 78 | License. For legal entities, "You" includes any entity that 79 | controls, is controlled by, or is under common control with You. For 80 | purposes of this definition, "control" means (a) the power, direct 81 | or indirect, to cause the direction or management of such entity, 82 | whether by contract or otherwise, or (b) ownership of more than 83 | fifty percent (50%) of the outstanding shares or beneficial 84 | ownership of such entity. 85 | 86 | 2. License Grants and Conditions 87 | -------------------------------- 88 | 89 | 2.1. Grants 90 | 91 | Each Contributor hereby grants You a world-wide, royalty-free, 92 | non-exclusive license: 93 | 94 | (a) under intellectual property rights (other than patent or trademark) 95 | Licensable by such Contributor to use, reproduce, make available, 96 | modify, display, perform, distribute, and otherwise exploit its 97 | Contributions, either on an unmodified basis, with Modifications, or 98 | as part of a Larger Work; and 99 | 100 | (b) under Patent Claims of such Contributor to make, use, sell, offer 101 | for sale, have made, import, and otherwise transfer either its 102 | Contributions or its Contributor Version. 103 | 104 | 2.2. Effective Date 105 | 106 | The licenses granted in Section 2.1 with respect to any Contribution 107 | become effective for each Contribution on the date the Contributor first 108 | distributes such Contribution. 109 | 110 | 2.3. Limitations on Grant Scope 111 | 112 | The licenses granted in this Section 2 are the only rights granted under 113 | this License. No additional rights or licenses will be implied from the 114 | distribution or licensing of Covered Software under this License. 115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 116 | Contributor: 117 | 118 | (a) for any code that a Contributor has removed from Covered Software; 119 | or 120 | 121 | (b) for infringements caused by: (i) Your and any other third party's 122 | modifications of Covered Software, or (ii) the combination of its 123 | Contributions with other software (except as part of its Contributor 124 | Version); or 125 | 126 | (c) under Patent Claims infringed by Covered Software in the absence of 127 | its Contributions. 128 | 129 | This License does not grant any rights in the trademarks, service marks, 130 | or logos of any Contributor (except as may be necessary to comply with 131 | the notice requirements in Section 3.4). 132 | 133 | 2.4. Subsequent Licenses 134 | 135 | No Contributor makes additional grants as a result of Your choice to 136 | distribute the Covered Software under a subsequent version of this 137 | License (see Section 10.2) or under the terms of a Secondary License (if 138 | permitted under the terms of Section 3.3). 139 | 140 | 2.5. Representation 141 | 142 | Each Contributor represents that the Contributor believes its 143 | Contributions are its original creation(s) or it has sufficient rights 144 | to grant the rights to its Contributions conveyed by this License. 145 | 146 | 2.6. Fair Use 147 | 148 | This License is not intended to limit any rights You have under 149 | applicable copyright doctrines of fair use, fair dealing, or other 150 | equivalents. 151 | 152 | 2.7. Conditions 153 | 154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 155 | in Section 2.1. 156 | 157 | 3. Responsibilities 158 | ------------------- 159 | 160 | 3.1. Distribution of Source Form 161 | 162 | All distribution of Covered Software in Source Code Form, including any 163 | Modifications that You create or to which You contribute, must be under 164 | the terms of this License. You must inform recipients that the Source 165 | Code Form of the Covered Software is governed by the terms of this 166 | License, and how they can obtain a copy of this License. You may not 167 | attempt to alter or restrict the recipients' rights in the Source Code 168 | Form. 169 | 170 | 3.2. Distribution of Executable Form 171 | 172 | If You distribute Covered Software in Executable Form then: 173 | 174 | (a) such Covered Software must also be made available in Source Code 175 | Form, as described in Section 3.1, and You must inform recipients of 176 | the Executable Form how they can obtain a copy of such Source Code 177 | Form by reasonable means in a timely manner, at a charge no more 178 | than the cost of distribution to the recipient; and 179 | 180 | (b) You may distribute such Executable Form under the terms of this 181 | License, or sublicense it under different terms, provided that the 182 | license for the Executable Form does not attempt to limit or alter 183 | the recipients' rights in the Source Code Form under this License. 184 | 185 | 3.3. Distribution of a Larger Work 186 | 187 | You may create and distribute a Larger Work under terms of Your choice, 188 | provided that You also comply with the requirements of this License for 189 | the Covered Software. If the Larger Work is a combination of Covered 190 | Software with a work governed by one or more Secondary Licenses, and the 191 | Covered Software is not Incompatible With Secondary Licenses, this 192 | License permits You to additionally distribute such Covered Software 193 | under the terms of such Secondary License(s), so that the recipient of 194 | the Larger Work may, at their option, further distribute the Covered 195 | Software under the terms of either this License or such Secondary 196 | License(s). 197 | 198 | 3.4. Notices 199 | 200 | You may not remove or alter the substance of any license notices 201 | (including copyright notices, patent notices, disclaimers of warranty, 202 | or limitations of liability) contained within the Source Code Form of 203 | the Covered Software, except that You may alter any license notices to 204 | the extent required to remedy known factual inaccuracies. 205 | 206 | 3.5. Application of Additional Terms 207 | 208 | You may choose to offer, and to charge a fee for, warranty, support, 209 | indemnity or liability obligations to one or more recipients of Covered 210 | Software. However, You may do so only on Your own behalf, and not on 211 | behalf of any Contributor. You must make it absolutely clear that any 212 | such warranty, support, indemnity, or liability obligation is offered by 213 | You alone, and You hereby agree to indemnify every Contributor for any 214 | liability incurred by such Contributor as a result of warranty, support, 215 | indemnity or liability terms You offer. You may include additional 216 | disclaimers of warranty and limitations of liability specific to any 217 | jurisdiction. 218 | 219 | 4. Inability to Comply Due to Statute or Regulation 220 | --------------------------------------------------- 221 | 222 | If it is impossible for You to comply with any of the terms of this 223 | License with respect to some or all of the Covered Software due to 224 | statute, judicial order, or regulation then You must: (a) comply with 225 | the terms of this License to the maximum extent possible; and (b) 226 | describe the limitations and the code they affect. Such description must 227 | be placed in a text file included with all distributions of the Covered 228 | Software under this License. Except to the extent prohibited by statute 229 | or regulation, such description must be sufficiently detailed for a 230 | recipient of ordinary skill to be able to understand it. 231 | 232 | 5. Termination 233 | -------------- 234 | 235 | 5.1. The rights granted under this License will terminate automatically 236 | if You fail to comply with any of its terms. However, if You become 237 | compliant, then the rights granted under this License from a particular 238 | Contributor are reinstated (a) provisionally, unless and until such 239 | Contributor explicitly and finally terminates Your grants, and (b) on an 240 | ongoing basis, if such Contributor fails to notify You of the 241 | non-compliance by some reasonable means prior to 60 days after You have 242 | come back into compliance. Moreover, Your grants from a particular 243 | Contributor are reinstated on an ongoing basis if such Contributor 244 | notifies You of the non-compliance by some reasonable means, this is the 245 | first time You have received notice of non-compliance with this License 246 | from such Contributor, and You become compliant prior to 30 days after 247 | Your receipt of the notice. 248 | 249 | 5.2. If You initiate litigation against any entity by asserting a patent 250 | infringement claim (excluding declaratory judgment actions, 251 | counter-claims, and cross-claims) alleging that a Contributor Version 252 | directly or indirectly infringes any patent, then the rights granted to 253 | You by any and all Contributors for the Covered Software under Section 254 | 2.1 of this License shall terminate. 255 | 256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 257 | end user license agreements (excluding distributors and resellers) which 258 | have been validly granted by You or Your distributors under this License 259 | prior to termination shall survive termination. 260 | 261 | ************************************************************************ 262 | * * 263 | * 6. Disclaimer of Warranty * 264 | * ------------------------- * 265 | * * 266 | * Covered Software is provided under this License on an "as is" * 267 | * basis, without warranty of any kind, either expressed, implied, or * 268 | * statutory, including, without limitation, warranties that the * 269 | * Covered Software is free of defects, merchantable, fit for a * 270 | * particular purpose or non-infringing. The entire risk as to the * 271 | * quality and performance of the Covered Software is with You. * 272 | * Should any Covered Software prove defective in any respect, You * 273 | * (not any Contributor) assume the cost of any necessary servicing, * 274 | * repair, or correction. This disclaimer of warranty constitutes an * 275 | * essential part of this License. No use of any Covered Software is * 276 | * authorized under this License except under this disclaimer. * 277 | * * 278 | ************************************************************************ 279 | 280 | ************************************************************************ 281 | * * 282 | * 7. Limitation of Liability * 283 | * -------------------------- * 284 | * * 285 | * Under no circumstances and under no legal theory, whether tort * 286 | * (including negligence), contract, or otherwise, shall any * 287 | * Contributor, or anyone who distributes Covered Software as * 288 | * permitted above, be liable to You for any direct, indirect, * 289 | * special, incidental, or consequential damages of any character * 290 | * including, without limitation, damages for lost profits, loss of * 291 | * goodwill, work stoppage, computer failure or malfunction, or any * 292 | * and all other commercial damages or losses, even if such party * 293 | * shall have been informed of the possibility of such damages. This * 294 | * limitation of liability shall not apply to liability for death or * 295 | * personal injury resulting from such party's negligence to the * 296 | * extent applicable law prohibits such limitation. Some * 297 | * jurisdictions do not allow the exclusion or limitation of * 298 | * incidental or consequential damages, so this exclusion and * 299 | * limitation may not apply to You. * 300 | * * 301 | ************************************************************************ 302 | 303 | 8. Litigation 304 | ------------- 305 | 306 | Any litigation relating to this License may be brought only in the 307 | courts of a jurisdiction where the defendant maintains its principal 308 | place of business and such litigation shall be governed by laws of that 309 | jurisdiction, without reference to its conflict-of-law provisions. 310 | Nothing in this Section shall prevent a party's ability to bring 311 | cross-claims or counter-claims. 312 | 313 | 9. Miscellaneous 314 | ---------------- 315 | 316 | This License represents the complete agreement concerning the subject 317 | matter hereof. If any provision of this License is held to be 318 | unenforceable, such provision shall be reformed only to the extent 319 | necessary to make it enforceable. Any law or regulation which provides 320 | that the language of a contract shall be construed against the drafter 321 | shall not be used to construe this License against a Contributor. 322 | 323 | 10. Versions of the License 324 | --------------------------- 325 | 326 | 10.1. New Versions 327 | 328 | Mozilla Foundation is the license steward. Except as provided in Section 329 | 10.3, no one other than the license steward has the right to modify or 330 | publish new versions of this License. Each version will be given a 331 | distinguishing version number. 332 | 333 | 10.2. Effect of New Versions 334 | 335 | You may distribute the Covered Software under the terms of the version 336 | of the License under which You originally received the Covered Software, 337 | or under the terms of any subsequent version published by the license 338 | steward. 339 | 340 | 10.3. Modified Versions 341 | 342 | If you create software not governed by this License, and you want to 343 | create a new license for such software, you may create and use a 344 | modified version of this License if you rename the license and remove 345 | any references to the name of the license steward (except to note that 346 | such modified license differs from this License). 347 | 348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 349 | Licenses 350 | 351 | If You choose to distribute Source Code Form that is Incompatible With 352 | Secondary Licenses under the terms of this version of the License, the 353 | notice described in Exhibit B of this License must be attached. 354 | 355 | Exhibit A - Source Code Form License Notice 356 | ------------------------------------------- 357 | 358 | This Source Code Form is subject to the terms of the Mozilla Public 359 | License, v. 2.0. If a copy of the MPL was not distributed with this 360 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 361 | 362 | If it is not possible or desirable to put the notice in a particular 363 | file, then You may include the notice in a location (such as a LICENSE 364 | file in a relevant directory) where a recipient would be likely to look 365 | for such a notice. 366 | 367 | You may add additional accurate notices of copyright ownership. 368 | 369 | Exhibit B - "Incompatible With Secondary Licenses" Notice 370 | --------------------------------------------------------- 371 | 372 | This Source Code Form is "Incompatible With Secondary Licenses", as 373 | defined by the Mozilla Public License, v. 2.0. 374 | -------------------------------------------------------------------------------- /LICENSE-THIRD-PARTY: -------------------------------------------------------------------------------- 1 | ============================================== 2 | THIRD PARTY LICENSES 3 | ============================================== 4 | 5 | ********************************************** 6 | lib/rootbox/argparser 7 | ********************************************** 8 | 9 | The MIT License (MIT) 10 | 11 | Copyright © 2016 Ekeyme Mo 12 | 13 | Permission is hereby granted, free of charge, to any person obtaining a copy 14 | of this software and associated documentation files (the "Software"), to deal 15 | in the Software without restriction, including without limitation the rights 16 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 | copies of the Software, and to permit persons to whom the Software is 18 | furnished to do so, subject to the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be included in all 21 | copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 29 | SOFTWARE. 30 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Rootbox 2 | ======= 3 | 4 | **NO LONGER MAINTAINED:** Rootbox has largely been replaced by another, much better project 5 | I created: `IsolateKit `_. Documentation is 6 | definitely still lacking, but it's overall a large improvement over Rootbox. 7 | 8 | Rootbox is a tool that lets you create and distribute “boxes”: isolated 9 | environments designed for building code thanks to the power of chroots and mounts. 10 | 11 | Unlike full-blown containerization tools like Docker and rkt, Rootbox is 12 | intentionally **non-secure**. This allows it to be incredibly lightweight, 13 | ideally suited for creating portable, reproducible development environments. 14 | 15 | Want to know more? 16 | `Check out the docs! `_ 17 | -------------------------------------------------------------------------------- /bin/rootbox: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | set -ETe 8 | 9 | SCRIPTS=`cat <&2 27 | echo "Missing script: $script (expected to be found at $path)" >&2 28 | exit 1 29 | fi 30 | 31 | . "$path" 32 | done 33 | 34 | main "$@" 35 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | cd "`dirname $0`" 4 | prefix=${1:-/usr/local} 5 | 6 | set -ex 7 | 8 | rm -rf "$prefix/lib/rootbox/cmdarg" 9 | 10 | install -m 755 bin/rootbox "$prefix/bin/rootbox" 11 | 12 | for file in lib/rootbox/*.sh lib/rootbox/LICENSE* lib/rootbox/argparser/*; do 13 | install -Dm 644 $file "$prefix/$file" 14 | done 15 | -------------------------------------------------------------------------------- /lib/rootbox/LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | 1. Definitions 5 | -------------- 6 | 7 | 1.1. "Contributor" 8 | means each individual or legal entity that creates, contributes to 9 | the creation of, or owns Covered Software. 10 | 11 | 1.2. "Contributor Version" 12 | means the combination of the Contributions of others (if any) used 13 | by a Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | means Covered Software of a particular Contributor. 17 | 18 | 1.4. "Covered Software" 19 | means Source Code Form to which the initial Contributor has attached 20 | the notice in Exhibit A, the Executable Form of such Source Code 21 | Form, and Modifications of such Source Code Form, in each case 22 | including portions thereof. 23 | 24 | 1.5. "Incompatible With Secondary Licenses" 25 | means 26 | 27 | (a) that the initial Contributor has attached the notice described 28 | in Exhibit B to the Covered Software; or 29 | 30 | (b) that the Covered Software was made available under the terms of 31 | version 1.1 or earlier of the License, but not also under the 32 | terms of a Secondary License. 33 | 34 | 1.6. "Executable Form" 35 | means any form of the work other than Source Code Form. 36 | 37 | 1.7. "Larger Work" 38 | means a work that combines Covered Software with other material, in 39 | a separate file or files, that is not Covered Software. 40 | 41 | 1.8. "License" 42 | means this document. 43 | 44 | 1.9. "Licensable" 45 | means having the right to grant, to the maximum extent possible, 46 | whether at the time of the initial grant or subsequently, any and 47 | all of the rights conveyed by this License. 48 | 49 | 1.10. "Modifications" 50 | means any of the following: 51 | 52 | (a) any file in Source Code Form that results from an addition to, 53 | deletion from, or modification of the contents of Covered 54 | Software; or 55 | 56 | (b) any new file in Source Code Form that contains any Covered 57 | Software. 58 | 59 | 1.11. "Patent Claims" of a Contributor 60 | means any patent claim(s), including without limitation, method, 61 | process, and apparatus claims, in any patent Licensable by such 62 | Contributor that would be infringed, but for the grant of the 63 | License, by the making, using, selling, offering for sale, having 64 | made, import, or transfer of either its Contributions or its 65 | Contributor Version. 66 | 67 | 1.12. "Secondary License" 68 | means either the GNU General Public License, Version 2.0, the GNU 69 | Lesser General Public License, Version 2.1, the GNU Affero General 70 | Public License, Version 3.0, or any later versions of those 71 | licenses. 72 | 73 | 1.13. "Source Code Form" 74 | means the form of the work preferred for making modifications. 75 | 76 | 1.14. "You" (or "Your") 77 | means an individual or a legal entity exercising rights under this 78 | License. For legal entities, "You" includes any entity that 79 | controls, is controlled by, or is under common control with You. For 80 | purposes of this definition, "control" means (a) the power, direct 81 | or indirect, to cause the direction or management of such entity, 82 | whether by contract or otherwise, or (b) ownership of more than 83 | fifty percent (50%) of the outstanding shares or beneficial 84 | ownership of such entity. 85 | 86 | 2. License Grants and Conditions 87 | -------------------------------- 88 | 89 | 2.1. Grants 90 | 91 | Each Contributor hereby grants You a world-wide, royalty-free, 92 | non-exclusive license: 93 | 94 | (a) under intellectual property rights (other than patent or trademark) 95 | Licensable by such Contributor to use, reproduce, make available, 96 | modify, display, perform, distribute, and otherwise exploit its 97 | Contributions, either on an unmodified basis, with Modifications, or 98 | as part of a Larger Work; and 99 | 100 | (b) under Patent Claims of such Contributor to make, use, sell, offer 101 | for sale, have made, import, and otherwise transfer either its 102 | Contributions or its Contributor Version. 103 | 104 | 2.2. Effective Date 105 | 106 | The licenses granted in Section 2.1 with respect to any Contribution 107 | become effective for each Contribution on the date the Contributor first 108 | distributes such Contribution. 109 | 110 | 2.3. Limitations on Grant Scope 111 | 112 | The licenses granted in this Section 2 are the only rights granted under 113 | this License. No additional rights or licenses will be implied from the 114 | distribution or licensing of Covered Software under this License. 115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 116 | Contributor: 117 | 118 | (a) for any code that a Contributor has removed from Covered Software; 119 | or 120 | 121 | (b) for infringements caused by: (i) Your and any other third party's 122 | modifications of Covered Software, or (ii) the combination of its 123 | Contributions with other software (except as part of its Contributor 124 | Version); or 125 | 126 | (c) under Patent Claims infringed by Covered Software in the absence of 127 | its Contributions. 128 | 129 | This License does not grant any rights in the trademarks, service marks, 130 | or logos of any Contributor (except as may be necessary to comply with 131 | the notice requirements in Section 3.4). 132 | 133 | 2.4. Subsequent Licenses 134 | 135 | No Contributor makes additional grants as a result of Your choice to 136 | distribute the Covered Software under a subsequent version of this 137 | License (see Section 10.2) or under the terms of a Secondary License (if 138 | permitted under the terms of Section 3.3). 139 | 140 | 2.5. Representation 141 | 142 | Each Contributor represents that the Contributor believes its 143 | Contributions are its original creation(s) or it has sufficient rights 144 | to grant the rights to its Contributions conveyed by this License. 145 | 146 | 2.6. Fair Use 147 | 148 | This License is not intended to limit any rights You have under 149 | applicable copyright doctrines of fair use, fair dealing, or other 150 | equivalents. 151 | 152 | 2.7. Conditions 153 | 154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 155 | in Section 2.1. 156 | 157 | 3. Responsibilities 158 | ------------------- 159 | 160 | 3.1. Distribution of Source Form 161 | 162 | All distribution of Covered Software in Source Code Form, including any 163 | Modifications that You create or to which You contribute, must be under 164 | the terms of this License. You must inform recipients that the Source 165 | Code Form of the Covered Software is governed by the terms of this 166 | License, and how they can obtain a copy of this License. You may not 167 | attempt to alter or restrict the recipients' rights in the Source Code 168 | Form. 169 | 170 | 3.2. Distribution of Executable Form 171 | 172 | If You distribute Covered Software in Executable Form then: 173 | 174 | (a) such Covered Software must also be made available in Source Code 175 | Form, as described in Section 3.1, and You must inform recipients of 176 | the Executable Form how they can obtain a copy of such Source Code 177 | Form by reasonable means in a timely manner, at a charge no more 178 | than the cost of distribution to the recipient; and 179 | 180 | (b) You may distribute such Executable Form under the terms of this 181 | License, or sublicense it under different terms, provided that the 182 | license for the Executable Form does not attempt to limit or alter 183 | the recipients' rights in the Source Code Form under this License. 184 | 185 | 3.3. Distribution of a Larger Work 186 | 187 | You may create and distribute a Larger Work under terms of Your choice, 188 | provided that You also comply with the requirements of this License for 189 | the Covered Software. If the Larger Work is a combination of Covered 190 | Software with a work governed by one or more Secondary Licenses, and the 191 | Covered Software is not Incompatible With Secondary Licenses, this 192 | License permits You to additionally distribute such Covered Software 193 | under the terms of such Secondary License(s), so that the recipient of 194 | the Larger Work may, at their option, further distribute the Covered 195 | Software under the terms of either this License or such Secondary 196 | License(s). 197 | 198 | 3.4. Notices 199 | 200 | You may not remove or alter the substance of any license notices 201 | (including copyright notices, patent notices, disclaimers of warranty, 202 | or limitations of liability) contained within the Source Code Form of 203 | the Covered Software, except that You may alter any license notices to 204 | the extent required to remedy known factual inaccuracies. 205 | 206 | 3.5. Application of Additional Terms 207 | 208 | You may choose to offer, and to charge a fee for, warranty, support, 209 | indemnity or liability obligations to one or more recipients of Covered 210 | Software. However, You may do so only on Your own behalf, and not on 211 | behalf of any Contributor. You must make it absolutely clear that any 212 | such warranty, support, indemnity, or liability obligation is offered by 213 | You alone, and You hereby agree to indemnify every Contributor for any 214 | liability incurred by such Contributor as a result of warranty, support, 215 | indemnity or liability terms You offer. You may include additional 216 | disclaimers of warranty and limitations of liability specific to any 217 | jurisdiction. 218 | 219 | 4. Inability to Comply Due to Statute or Regulation 220 | --------------------------------------------------- 221 | 222 | If it is impossible for You to comply with any of the terms of this 223 | License with respect to some or all of the Covered Software due to 224 | statute, judicial order, or regulation then You must: (a) comply with 225 | the terms of this License to the maximum extent possible; and (b) 226 | describe the limitations and the code they affect. Such description must 227 | be placed in a text file included with all distributions of the Covered 228 | Software under this License. Except to the extent prohibited by statute 229 | or regulation, such description must be sufficiently detailed for a 230 | recipient of ordinary skill to be able to understand it. 231 | 232 | 5. Termination 233 | -------------- 234 | 235 | 5.1. The rights granted under this License will terminate automatically 236 | if You fail to comply with any of its terms. However, if You become 237 | compliant, then the rights granted under this License from a particular 238 | Contributor are reinstated (a) provisionally, unless and until such 239 | Contributor explicitly and finally terminates Your grants, and (b) on an 240 | ongoing basis, if such Contributor fails to notify You of the 241 | non-compliance by some reasonable means prior to 60 days after You have 242 | come back into compliance. Moreover, Your grants from a particular 243 | Contributor are reinstated on an ongoing basis if such Contributor 244 | notifies You of the non-compliance by some reasonable means, this is the 245 | first time You have received notice of non-compliance with this License 246 | from such Contributor, and You become compliant prior to 30 days after 247 | Your receipt of the notice. 248 | 249 | 5.2. If You initiate litigation against any entity by asserting a patent 250 | infringement claim (excluding declaratory judgment actions, 251 | counter-claims, and cross-claims) alleging that a Contributor Version 252 | directly or indirectly infringes any patent, then the rights granted to 253 | You by any and all Contributors for the Covered Software under Section 254 | 2.1 of this License shall terminate. 255 | 256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 257 | end user license agreements (excluding distributors and resellers) which 258 | have been validly granted by You or Your distributors under this License 259 | prior to termination shall survive termination. 260 | 261 | ************************************************************************ 262 | * * 263 | * 6. Disclaimer of Warranty * 264 | * ------------------------- * 265 | * * 266 | * Covered Software is provided under this License on an "as is" * 267 | * basis, without warranty of any kind, either expressed, implied, or * 268 | * statutory, including, without limitation, warranties that the * 269 | * Covered Software is free of defects, merchantable, fit for a * 270 | * particular purpose or non-infringing. The entire risk as to the * 271 | * quality and performance of the Covered Software is with You. * 272 | * Should any Covered Software prove defective in any respect, You * 273 | * (not any Contributor) assume the cost of any necessary servicing, * 274 | * repair, or correction. This disclaimer of warranty constitutes an * 275 | * essential part of this License. No use of any Covered Software is * 276 | * authorized under this License except under this disclaimer. * 277 | * * 278 | ************************************************************************ 279 | 280 | ************************************************************************ 281 | * * 282 | * 7. Limitation of Liability * 283 | * -------------------------- * 284 | * * 285 | * Under no circumstances and under no legal theory, whether tort * 286 | * (including negligence), contract, or otherwise, shall any * 287 | * Contributor, or anyone who distributes Covered Software as * 288 | * permitted above, be liable to You for any direct, indirect, * 289 | * special, incidental, or consequential damages of any character * 290 | * including, without limitation, damages for lost profits, loss of * 291 | * goodwill, work stoppage, computer failure or malfunction, or any * 292 | * and all other commercial damages or losses, even if such party * 293 | * shall have been informed of the possibility of such damages. This * 294 | * limitation of liability shall not apply to liability for death or * 295 | * personal injury resulting from such party's negligence to the * 296 | * extent applicable law prohibits such limitation. Some * 297 | * jurisdictions do not allow the exclusion or limitation of * 298 | * incidental or consequential damages, so this exclusion and * 299 | * limitation may not apply to You. * 300 | * * 301 | ************************************************************************ 302 | 303 | 8. Litigation 304 | ------------- 305 | 306 | Any litigation relating to this License may be brought only in the 307 | courts of a jurisdiction where the defendant maintains its principal 308 | place of business and such litigation shall be governed by laws of that 309 | jurisdiction, without reference to its conflict-of-law provisions. 310 | Nothing in this Section shall prevent a party's ability to bring 311 | cross-claims or counter-claims. 312 | 313 | 9. Miscellaneous 314 | ---------------- 315 | 316 | This License represents the complete agreement concerning the subject 317 | matter hereof. If any provision of this License is held to be 318 | unenforceable, such provision shall be reformed only to the extent 319 | necessary to make it enforceable. Any law or regulation which provides 320 | that the language of a contract shall be construed against the drafter 321 | shall not be used to construe this License against a Contributor. 322 | 323 | 10. Versions of the License 324 | --------------------------- 325 | 326 | 10.1. New Versions 327 | 328 | Mozilla Foundation is the license steward. Except as provided in Section 329 | 10.3, no one other than the license steward has the right to modify or 330 | publish new versions of this License. Each version will be given a 331 | distinguishing version number. 332 | 333 | 10.2. Effect of New Versions 334 | 335 | You may distribute the Covered Software under the terms of the version 336 | of the License under which You originally received the Covered Software, 337 | or under the terms of any subsequent version published by the license 338 | steward. 339 | 340 | 10.3. Modified Versions 341 | 342 | If you create software not governed by this License, and you want to 343 | create a new license for such software, you may create and use a 344 | modified version of this License if you rename the license and remove 345 | any references to the name of the license steward (except to note that 346 | such modified license differs from this License). 347 | 348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 349 | Licenses 350 | 351 | If You choose to distribute Source Code Form that is Incompatible With 352 | Secondary Licenses under the terms of this version of the License, the 353 | notice described in Exhibit B of this License must be attached. 354 | 355 | Exhibit A - Source Code Form License Notice 356 | ------------------------------------------- 357 | 358 | This Source Code Form is subject to the terms of the Mozilla Public 359 | License, v. 2.0. If a copy of the MPL was not distributed with this 360 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 361 | 362 | If it is not possible or desirable to put the notice in a particular 363 | file, then You may include the notice in a location (such as a LICENSE 364 | file in a relevant directory) where a recipient would be likely to look 365 | for such a notice. 366 | 367 | You may add additional accurate notices of copyright ownership. 368 | 369 | Exhibit B - "Incompatible With Secondary Licenses" Notice 370 | --------------------------------------------------------- 371 | 372 | This Source Code Form is "Incompatible With Secondary Licenses", as 373 | defined by the Mozilla Public License, v. 2.0. 374 | -------------------------------------------------------------------------------- /lib/rootbox/LICENSE-THIRD-PARTY: -------------------------------------------------------------------------------- 1 | ============================================== 2 | THIRD PARTY LICENSES 3 | ============================================== 4 | 5 | ********************************************** 6 | lib/rootbox/argparser 7 | ********************************************** 8 | 9 | The MIT License (MIT) 10 | 11 | Copyright © 2016 Ekeyme Mo 12 | 13 | Permission is hereby granted, free of charge, to any person obtaining a copy 14 | of this software and associated documentation files (the "Software"), to deal 15 | in the Software without restriction, including without limitation the rights 16 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 | copies of the Software, and to permit persons to whom the Software is 18 | furnished to do so, subject to the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be included in all 21 | copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 29 | SOFTWARE. 30 | -------------------------------------------------------------------------------- /lib/rootbox/argparser/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Ekeyme Mo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /lib/rootbox/argparser/argparser: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # -*- coding: utf-8 -*- 3 | 4 | ## 5 | # Copyright © 2016 Ekeyme Mo 6 | # 7 | # The MIT License (MIT) 8 | # 9 | # 10 | # This script, argparser, can help you write a much user-friendly command-line parsing code 11 | # in your bash shell script. You could just register what arguments requied in your 12 | # program. And then apgparser will finish the left: parses the comand-line paramters you 13 | # supplied, automatically generates usage and help message. 14 | # 15 | # Some conceptions of this script are a little bit like python argparser module, so you can visit 16 | # https://docs.python.org/3/library/argparse.html to make you easier to understand the conceptions. 17 | # 18 | 19 | 20 | # argparser initial function 21 | # This function initials some globals variables and defines some functions 22 | # requied to parse parameters from command line. 23 | # It accepts the following optional parameters to initail globals variables: 24 | # 25 | # - $1: program name, default script name 26 | # - prologue=*: text show before `usage` 27 | # - usage=*: message show how to use program(short help message), 28 | # default: generated from arguments added to parser 29 | # - desc=*: short message to show what program can do, show after `usage` 30 | # - epilog=*: text show after help message 31 | # - prefix_chars=*: the set of characters that prefix optional arguments (default: '-') 32 | # - help=*: full program help text, default: generated from prologue/usage/desc/epilog added to argparser 33 | # - add_help=*: whether to add -h/--help option to argparser, default: true 34 | # - nargs_extending_EOT=*: the character that indecates where the extending nargs(+/*) argument should stop, 35 | # default: '-' 36 | function argparser() 37 | { 38 | # Argparser reserved functions or variables. 39 | # Attributes in this array will not be unset(tear down) after 40 | # parser_parse() call. 41 | __argparser_reserved_attrs__=(argparser_subargs) 42 | 43 | # Argparser API functions or variables. 44 | # This is a fake array which is to indicate the argparser api. 45 | # Attributes in this array will be unset after parser_parse() call. 46 | __argparser_api_attrs__=(argparser argparser_add_arg argparser_parse argparser_DEBUG argparser_help \ 47 | argparser_store argparser_reserve_usage) 48 | 49 | # Functions and variables which will be unset after argparser_parser call 50 | __argparser_tear_down_attr__=( \ 51 | # variables defined by argparse, this is generated by command line scripts 52 | ARGPARSER_NAME ARGPARSER_UN_DEFINE __argparser_values__ __argparser_shift__num__ ARGPARSER_PA_NO_PARSE ARGPARSER_PA_PARSED ARGPARSER_OA_NO_PARSE ARGPARSER_OA_PARSED Argparser_prog_name Argparser_help Argparser_prefix_chars Argparser_nargs_extending_EOT Argparser_prologue Argparser_usage Argparser_desc Argparser_epilog Argparser_add_help _argparser_usage Argparser_argument_flag Argparser_argument_dest Argparser_argument_check Argparser_argument_action Argparser_argument_default Argparser_argument_const Argparser_argument_nargs Argparser_argument_desc Argparser_argument_metavar Argparser_argument_required Argparser_argument_choices _Argparser_argument_status __argparser_reserved_attrs__ __argparser_api_attrs__ __argparser_tear_down_attr__ \ 53 | # functions defined by argparse, this is generated by command line scripts 54 | __tear_down__ _argparser_check_value_narg_matching _argparser_choose_dest _argparser_choose_longest_argument_string _argparser_choose_metavar _argparser_choose_shortest_optional_string _argparser_cl_arg_amount_need2reserved_now _argparser_do_action _argparser_error _argparser_final_check_argparser_aguments _argparser_format_arg_help _argparser_format_argument_flag _argparser_format_usge _argparser_get_argparser_optional_arg_key _argparser_get_argparser_positional_arg_key _argparser_get_cl_arg_values _argparser_get_extending_values _argparser_globals_register _argparser_is_callback _argparser_is_long_optional_argument _argparser_is_optional_argment _argparser_is_positional_argment _argparser_parse_long_option _argparser_parse_positional_arg _argparser_parse_short_option _argparser_prepare_argparser_aguments _argparser_reset_values_and_shift_num _argparser_set_default2dest _argparser_set_value2dest _argparser_translate _argparser_trim_option_prefixs _argparser_usage argparser argparser_DEBUG argparser_add_arg argparser_check argparser_help argparser_parse argparser_reserve_usage argparser_store) 55 | 56 | 57 | # argparser name 58 | # for error message 59 | ARGPARSER_NAME=argparser 60 | 61 | # data structure like undef in perl 62 | # So empty `string '' != $ARGPARSER_UN_DEFINE` 63 | ARGPARSER_UN_DEFINE=_undef_ 64 | # Internal value exchanging variable; so if you assign it a value 65 | # you must get the value and assign value to another variable immediately, 66 | # or it would be overwrited by other operations. 67 | __argparser_values__=$ARGPARSER_UN_DEFINE 68 | # Internal value exchanging variable, like __argparser_values__, but just used to exchange 69 | # the shift num. 70 | __argparser_shift__num__=0 71 | 72 | # argparser app level info 73 | Argparser_prog_name=$(basename $0) 74 | Argparser_help=$ARGPARSER_UN_DEFINE 75 | Argparser_prefix_chars='-' 76 | Argparser_nargs_extending_EOT='-' 77 | Argparser_prologue='' 78 | Argparser_usage=$ARGPARSER_UN_DEFINE 79 | Argparser_desc='' 80 | Argparser_epilog='' 81 | Argparser_add_help=true 82 | 83 | # this is the stored string of usage 84 | # used by _usage() and _format_usage 85 | _argparser_usage=$ARGPARSER_UN_DEFINE 86 | 87 | # Argument level info: 88 | # array type, order is very important. we use the array index to get the 89 | # right info for the argument you registerd. 90 | Argparser_argument_flag=() 91 | Argparser_argument_dest=() 92 | Argparser_argument_check=() 93 | Argparser_argument_action=() 94 | Argparser_argument_default=() 95 | Argparser_argument_const=() 96 | Argparser_argument_nargs=() 97 | Argparser_argument_desc=() 98 | Argparser_argument_metavar=() 99 | Argparser_argument_required=() 100 | Argparser_argument_choices=() 101 | # argparser argument parsing status 102 | # 0: Positional Parameter(pp) do not show up in $@, so haven't been parsed 103 | # 2: pp have been parsed 104 | # 1: Optional Parameter(op) haven't been parsed 105 | # 3: op have been parsed 106 | ARGPARSER_PA_NO_PARSE=0 107 | ARGPARSER_PA_PARSED=2 108 | ARGPARSER_OA_NO_PARSE=1 109 | ARGPARSER_OA_PARSED=3 110 | _Argparser_argument_status=() 111 | 112 | # subarguments 113 | # sotre all arguments after `--` 114 | argparser_subargs=() 115 | 116 | # unset variables and functions after argparser_parse 117 | function __tear_down__() 118 | { 119 | set -- "${__argparser_tear_down_attr__[@]}" 120 | while (($# > 0)); do 121 | unset "$1" 122 | shift 123 | done 124 | } 125 | 126 | # - $1: program name 127 | # - $2: error message 128 | # - $3: error level, like warning, error, fatal, internal 129 | # - $4: no exit flag, if this value is set, program only prints error 130 | # error message and do not exit. 131 | function _argparser_error() 132 | { 133 | local prog=$1 134 | local message=$2 135 | local error_type=$(tr [A-Z] [a-z] <<< "${3:-error}") 136 | local no_exit=$4 137 | printf "$prog: $error_type: "'%s\n' "$message" >&2 138 | [[ $no_exit ]] || exit 1 139 | } 140 | 141 | # Turn on DEBUG model 142 | # This function implement DEBUG model by overwrite the argparser_parser function 143 | function argparser_DEBUG() 144 | { 145 | _argparser_old_parse_func=$(declare -f argparser_parse) 146 | [[ $_argparser_old_parse_func = '' ]] && \ 147 | _argparser_error "$ARGPARSER_NAME" "$FUNCNAME: debug model setup fail: argparser_parse function not found." internal 148 | 149 | # get value from variable name 150 | # - $1: variable name 151 | function __argparser_get_() 152 | { 153 | local variable=$1 154 | local v=$(declare -p "$variable" 2>/dev/null) 155 | v=${v#*=} 156 | v=${v#\'} 157 | v=${v%\'} 158 | printf "%s" "$v" 159 | } 160 | 161 | # the new argparser_parse 162 | function argparser_parse() 163 | { 164 | printf "%s\n%s\n" "$ARGPARSER_NAME debug info:" "----" 165 | # APP infomation 166 | printf "|APP level info:\n" 167 | printf " %s: '%s'\n" "Program name" "$Argparser_prog_name" 168 | printf " %s: '%s'\n" "Prefix chars" "$Argparser_prefix_chars" 169 | printf " %s: '%s'\n" "Nargs Extending EOT" "$Argparser_nargs_extending_EOT" 170 | printf " %s: '%s'\n" "Help adding" "$Argparser_add_help" 171 | printf " %s: '%s'\n" "Help prologue" "$Argparser_prologue" 172 | printf " %s: '%s'\n" "Help usage" "$Argparser_usage" 173 | printf " %s: '%s'\n" "Help desciption" "$Argparser_desc" 174 | printf " %s: '%s'\n" "Help string" "$Argparser_help" 175 | printf " %s: '%s'\n" "Help epilog" "$Argparser_epilog" 176 | # help doc 177 | printf "\n|APP help doc:\n" 178 | echo "$(argparser_help)" 179 | 180 | # argument infomation 181 | printf "\n|Argument level info:\n" 182 | local ___argparser_k_ ___argparser_arg_string_ ___argparser_dest_ \ 183 | ___dest_has_value_before_ ___argparser_stored_dest_ \ 184 | ___argparser_arg_desc_ 185 | # reserve the argparser_argument_* values avoiding unseting by argparser_parse 186 | local ___argparser_argument_flag_=("${Argparser_argument_flag[@]}") 187 | local ___argparser_argument_dest_=("${Argparser_argument_dest[@]}") 188 | local ___argparser_argument_default_=("${Argparser_argument_default[@]}") 189 | local ___argparser_argument_const_=("${Argparser_argument_const[@]}") 190 | local ___argparser_argument_nargs_=("${Argparser_argument_nargs[@]}") 191 | local ___argparser_argument_metavar_=("${Argparser_argument_metavar[@]}") 192 | 193 | # check whether the dest variable has value before 194 | for ___argparser_k_ in ${!___argparser_argument_flag_[@]}; do 195 | ___argparser_dest_=$(_argparser_choose_dest "${___argparser_argument_dest_[$___argparser_k_]}" \ 196 | "${___argparser_argument_flag_[$___argparser_k_]}") 197 | ___argparser_stored_dest_[$___argparser_k_]=$___argparser_dest_ 198 | if (declare -p "$___argparser_dest_" 2>/dev/null 1>/dev/null); then 199 | ___dest_has_value_before_[$___argparser_k_]='!' 200 | fi 201 | done 202 | 203 | # call back the old argparser_parse function and unset _argparser_old_parse_func 204 | eval "$_argparser_old_parse_func" 205 | argparser_parse "$@" 206 | unset _argparser_old_parse_func 207 | 208 | # print debug info after argparser_parse 209 | printf " %-15s %-5s %-10s %-10s %-10s %-13s %s\n" \ 210 | "argument" "nargs" "default" "const" "dest" "overwriting" "value" 211 | for ___argparser_k_ in ${!___argparser_argument_flag_[@]}; do 212 | ___argparser_arg_string_=${___argparser_argument_flag_[$___argparser_k_]} 213 | ___argparser_arg_string_=${___argparser_arg_string_//'|'/,} 214 | 215 | printf " %-15s %5s %-10s %-10s %-10s %-13s %s\n" \ 216 | "$___argparser_arg_string_" \ 217 | "${___argparser_argument_nargs_[$___argparser_k_]}" \ 218 | "${___argparser_argument_default_[$___argparser_k_]}" \ 219 | "${___argparser_argument_const_[$___argparser_k_]}" \ 220 | '$'"${___argparser_stored_dest_[$___argparser_k_]}" \ 221 | "${___dest_has_value_before_[$___argparser_k_]}" \ 222 | "$(__argparser_get_ "${___argparser_stored_dest_[$___argparser_k_]}")" 223 | done 224 | 225 | # subarguments 226 | printf "\n%s\n" "|Sub arguments: $(__argparser_get_ argparser_subargs)" 227 | 228 | unset -f __argparser_get_ 229 | } 230 | } 231 | 232 | # Define how a single command-line argument should be parsed. 233 | # argparser_add_arg arg_flag [arg_flag...] [options] 234 | # - arg_flag: either a name or a list of option strings 235 | # [options] 236 | # - const=*: the value produced if optional argument is encountered in standalone style(like only -f) 237 | # (optionals only) 238 | # - default=*: the value produced if the argument is absent from the command line 239 | # - nargs=*: number of command-line arguments that should be consumed 240 | # - metavar=*: a name for the argument in usage messages 241 | # - dest=*: an destination variable name to accept value parsed by argparser 242 | # - desc=*: description of this argument 243 | # - action=*: an callback to be called when this argument is encountered at the command line, 244 | # default `argparser_store` 245 | # - choices=*: a container of the allowable values for the argument, use RegulateExpression pattern 246 | # - required=*: whether or not the command-line option may be omitted (optionals only) 247 | # - check=*: an callable function use to check whether values of this argument gotten is valid 248 | function argparser_add_arg() 249 | { 250 | # fetch option name 251 | local _is_positional=false 252 | local _pattern='^[-_0-9a-zA-Z]+$' 253 | local arg_flag=$1 254 | shift 255 | if _argparser_is_positional_argment "$arg_flag"; then 256 | [[ $1 != '' && $1 != *=* ]] &&\ 257 | _argparser_error $ARGPARSER_NAME "positional argument name should be supplied just 1: $arg_flag $1." 258 | _is_positional=true 259 | else 260 | while (( $# > 0 )); do 261 | [[ $1 = *=* ]] && break 262 | _argparser_is_positional_argment "$1" && \ 263 | _argparser_error "$ARGPARSER_NAME" "$FUNCNAME: positional argument shouldn't mix in optional argument." 264 | [[ ! $1 =~ $_pattern ]] && \ 265 | _argparser_error "$ARGPARSER_NAME" "$FUNCNAME: argument string should only contain [-_0-9a-zA-Z]: $1." 266 | arg_flag="$arg_flag|$1" 267 | shift 268 | done 269 | fi 270 | Argparser_argument_flag[${#Argparser_argument_flag[@]}]=$arg_flag 271 | 272 | # fetch other option data 273 | local const=$ARGPARSER_UN_DEFINE 274 | local default=$ARGPARSER_UN_DEFINE 275 | local nargs=$ARGPARSER_UN_DEFINE 276 | local metavar=$ARGPARSER_UN_DEFINE 277 | local dest='' 278 | local desc='' 279 | local action='' 280 | local choices='' 281 | local required=false 282 | local check=argparser_check 283 | while (($# > 0)); do 284 | case $1 in 285 | const=*) const=${1#*=} 286 | ;; 287 | default=*) default=${1#*=} 288 | ;; 289 | nargs=*) nargs=${1#*=} 290 | ;; 291 | metavar=*) metavar=${1#*=} 292 | ;; 293 | dest=*) dest=${1#*=} 294 | ;; 295 | desc=*) desc=${1#*=} 296 | ;; 297 | action=*) action=${1#*=} 298 | ;; 299 | choices=*) choices=${1#*=} 300 | ;; 301 | required=*) required=${1#*=} 302 | ;; 303 | check=*) check=${1#*=} 304 | ;; 305 | *) _argparser_error $ARGPARSER_NAME "$FUNCNAME: unrecognized argument: $1" 306 | ;; 307 | esac 308 | shift 309 | done 310 | # dest must be an valid variable name 311 | _pattern='^[_a-zA-Z][_0-9a-zA-Z]*$' 312 | if [[ $dest != '' && ! $dest =~ $_pattern ]]; then 313 | _argparser_error $FUNCNAME "$arg_flag: dest must be a valid variable name: $dest." 314 | fi 315 | # action must be callable 316 | if [[ $action != '' ]] && ! _argparser_is_callback "$action"; then 317 | _argparser_error $FUNCNAME "$arg_flag: action must be callable: $action." 318 | fi 319 | # check must be callable 320 | if ! _argparser_is_callback "$check"; then 321 | _argparser_error $FUNCNAME "$arg_flag: check must be callable: $check." 322 | fi 323 | # required: 1.positional argument not support required; 2.only true or false to choose 324 | if [[ $required = true ]]; then 325 | [[ $_is_positional = true ]] && \ 326 | _argparser_error $FUNCNAME "positional argument $arg_flag do not support required option." 327 | elif [[ $required = false ]]; then 328 | : # do nothing 329 | else 330 | _argparser_error $FUNCNAME "$arg_flag: required only could be true or false." 331 | fi 332 | # const: positional argument not support const 333 | if [[ $const != $ARGPARSER_UN_DEFINE && $_is_positional = true ]]; then 334 | _argparser_error $FUNCNAME "positional argument $arg_flag do not support const option." 335 | fi 336 | # check nargs and set nargs depending on const/default 337 | # 1. nargs only support number, ?, *, +, remain these 5 types 338 | if [[ $nargs = $ARGPARSER_UN_DEFINE ]]; then 339 | if [[ $_is_positional = true ]]; then 340 | # define nargs of positional argument 341 | if [[ $default = $ARGPARSER_UN_DEFINE ]]; then 342 | nargs=1 343 | else 344 | nargs='?' 345 | fi 346 | else 347 | # define nargs of positional argument 348 | if [[ $const = $ARGPARSER_UN_DEFINE ]]; then 349 | nargs=1 350 | else 351 | nargs='?' 352 | fi 353 | fi 354 | else 355 | local zero_like_nargs_pattern='^(0|\?|\*|remain)$' 356 | local non_zero_nargs_pattern='^([1-9][0-9]*|\+)$' 357 | if [[ $nargs =~ $non_zero_nargs_pattern ]]; then 358 | # non zero like nargs do not support const for optional and default for positional argument 359 | if [[ $_is_positional = true ]]; then 360 | [[ $default != $ARGPARSER_UN_DEFINE ]] &&\ 361 | _argparser_error $FUNCNAME "positional argument $arg_flag: when default supplied, nargs must be 0|?|*|remain." 362 | else 363 | [[ $const != $ARGPARSER_UN_DEFINE ]] &&\ 364 | _argparser_error $FUNCNAME "optional argument $arg_flag: when const supplied, nargs must be 0|?|*." 365 | fi 366 | elif [[ $nargs =~ $zero_like_nargs_pattern ]]; then 367 | : # nothing 368 | else 369 | _argparser_error $FUNCNAME "$arg_flag: nargs must be in (?|*|+|remain|[number])." 370 | fi 371 | fi 372 | 373 | Argparser_argument_dest[${#Argparser_argument_dest[@]}]=$dest 374 | Argparser_argument_check[${#Argparser_argument_check[@]}]=$check 375 | Argparser_argument_action[${#Argparser_argument_action[@]}]=$action 376 | Argparser_argument_default[${#Argparser_argument_default[@]}]=$default 377 | Argparser_argument_const[${#Argparser_argument_const[@]}]=$const 378 | Argparser_argument_nargs[${#Argparser_argument_nargs[@]}]=$nargs 379 | Argparser_argument_desc[${#Argparser_argument_desc[@]}]=$desc 380 | Argparser_argument_metavar[${#Argparser_argument_metavar[@]}]=$metavar 381 | Argparser_argument_required[${#Argparser_argument_required[@]}]=$required 382 | Argparser_argument_choices[${#Argparser_argument_choices[@]}]=$choices 383 | } 384 | 385 | # parse the command line arguments and set the right value to dest... 386 | # it will tear down all the non reserved functions and variables in __argparser_tear_down_attr__ 387 | # used like: argparser_parse "$@" 388 | function argparser_parse() 389 | { 390 | _argparser_prepare_argparser_aguments 391 | # find hyphen command-line args(sub args) 392 | # use this type of ugly variable names for escaping local variable for _argparser_globals_register 393 | local ___argparser_args_=() 394 | while (($# > 0)); do 395 | if [[ $1 = '--' ]]; then 396 | shift 397 | break 398 | fi 399 | ___argparser_args_[${#___argparser_args_[@]}]=$1 400 | shift 401 | done 402 | argparser_subargs=("$@") 403 | set -- "${___argparser_args_[@]}" 404 | 405 | # parsing arguments 406 | while (($# > 0)); do 407 | if _argparser_is_optional_argment "$1"; then 408 | if _argparser_is_long_optional_argument "$1"; then 409 | _argparser_parse_long_option "$@" && shift $__argparser_shift__num__ 410 | else 411 | _argparser_parse_short_option "$@" && shift $__argparser_shift__num__ 412 | fi 413 | else 414 | _argparser_parse_positional_arg "$@" && shift $__argparser_shift__num__ 415 | fi 416 | done 417 | 418 | # final check unparsed args and dump all the dest 419 | _argparser_final_check_argparser_aguments 420 | _argparser_globals_register dump 421 | __tear_down__ 422 | } 423 | 424 | # reserve argparser usage to avoid __tear_down__ 425 | function argparser_reserve_usage() 426 | { 427 | printf "%s" "$(_argparser_usage)" 428 | } 429 | 430 | # check API 431 | # fake check, always return true 432 | # - $1...: values of the argparser argument gotten 433 | function argparser_check() { :; } 434 | 435 | # action API 436 | # action_callback dest option_string option_strings - - value 437 | # store|help|?version? 438 | # argparser action 439 | # - $1: dest 440 | # - $2: command-line argument string 441 | # - $3: argparser argument string 442 | # - $4: reserved 443 | # - $5: reserved 444 | # - $6: reserved 445 | # - $7...: values 446 | function argparser_store() 447 | { 448 | local dest=$1 449 | shift 6 450 | _argparser_globals_register register "$dest" "$@" 451 | } 452 | 453 | # format help string, print it and exit 454 | function argparser_help() 455 | { 456 | local help='' 457 | if [[ $Argparser_help = $ARGPARSER_UN_DEFINE ]]; then 458 | local prologue=$Argparser_prologue 459 | local usage=$(_argparser_usage) 460 | local app_desc=$Argparser_desc 461 | local args_help=$(_argparser_format_arg_help) 462 | local epilog=$Argparser_epilog 463 | if [[ $prologue != '' ]]; then 464 | prologue+=$'\n'$'\n' 465 | fi 466 | if [[ $usage != '' ]]; then 467 | usage+=$'\n' 468 | fi 469 | if [[ $app_desc != '' ]]; then 470 | app_desc+=$'\n' 471 | fi 472 | if [[ $args_help != '' ]]; then 473 | args_help=$'\n'$args_help$'\n' 474 | fi 475 | if [[ $epilog != '' ]]; then 476 | epilog=$'\n'$epilog 477 | fi 478 | help=$prologue$usage$app_desc$args_help$epilog 479 | else 480 | help=$Argparser_help 481 | fi 482 | help=$(_argparser_translate "$help" prog "$Argparser_prog_name") 483 | echo -e "$help" 484 | exit 485 | } 486 | 487 | # check whether $1 is callable 488 | function _argparser_is_callback() 489 | { 490 | hash "$1" 1>/dev/null 2>&1 491 | } 492 | 493 | function _argparser_is_optional_argment() 494 | { 495 | ((${#1} > 1)) && [[ $Argparser_prefix_chars =~ "${1:0:1}" ]] 496 | } 497 | 498 | function _argparser_is_positional_argment() 499 | { 500 | [[ ! $Argparser_prefix_chars =~ "${1:0:1}" ]] 501 | } 502 | 503 | function _argparser_is_long_optional_argument() 504 | { 505 | local prefix1=${1:0:1} 506 | local prefix2=${1:1:1} 507 | ((${#1} > 2)) && \ 508 | [[ $Argparser_prefix_chars =~ $prefix1 ]] &&\ 509 | [[ "$prefix1" = "$prefix2" ]] 510 | } 511 | 512 | # initial work befor argparser_parse 513 | # set all arguments status to NO_PARSE 514 | function _argparser_prepare_argparser_aguments() 515 | { 516 | local key argument_strings 517 | for ((key=0; key < ${#Argparser_argument_flag[@]}; key++)); do 518 | # set all arguments status to NO_PARSE 519 | argument_strings=${Argparser_argument_flag[$key]} 520 | if _argparser_is_positional_argment "$argument_strings"; then 521 | _Argparser_argument_status[$key]=$ARGPARSER_PA_NO_PARSE 522 | else 523 | _Argparser_argument_status[$key]=$ARGPARSER_OA_NO_PARSE 524 | fi 525 | done 526 | } 527 | 528 | # works after parsing the arguments 529 | # 1. set the default value if supplied 530 | # 2. check whether optional arguments are required 531 | function _argparser_final_check_argparser_aguments() 532 | { 533 | local key status default is_required nargs argument_strings 534 | for ((key=0; key < ${#_Argparser_argument_status[@]}; key++)); do 535 | status=${_Argparser_argument_status[$key]} 536 | is_required=${Argparser_argument_required[$key]} 537 | default=${Argparser_argument_default[$key]} 538 | argument_strings=${Argparser_argument_flag[$key]} 539 | if [[ $status = $ARGPARSER_OA_NO_PARSE ]]; then # optional argument 540 | if [[ $is_required = false ]]; then 541 | _argparser_set_default2dest "$key" "$ARGPARSER_OA_PARSED" 542 | else 543 | local short_option=$(_argparser_choose_shortest_optional_string "$argument_strings") 544 | _argparser_error "$Argparser_prog_name" "argument $short_option is required." 545 | fi 546 | elif [[ $status = $ARGPARSER_PA_NO_PARSE ]]; then 547 | nargs=${Argparser_argument_nargs[$key]} 548 | if [[ $nargs = ['?*'] ]] || [[ $nargs = remain ]]; then 549 | _argparser_set_default2dest "$key" "$ARGPARSER_PA_PARSED" 550 | else 551 | local metavar=$(_argparser_choose_metavar \ 552 | "${Argparser_argument_metavar[$key]}" \ 553 | "${Argparser_argument_dest[$key]}" \ 554 | "$argument_strings") 555 | _argparser_usage 556 | _argparser_error "$Argparser_prog_name" "argument $metavar is missing." 557 | fi 558 | fi 559 | done 560 | } 561 | 562 | # parse long optional arguments 563 | # this function get the right number values from command-line arguments, 564 | # according to nargs 565 | # and assign value to ___value__, assign shift num to __argparser_shift__num__ 566 | # - $1: must be long optional arguments 567 | # - $2...: the unshift command-line arguments 568 | # used by argparser_parse 569 | function _argparser_parse_long_option() 570 | { 571 | _argparser_reset_values_and_shift_num 572 | local shift_num=0 573 | local option_string=$1 574 | shift 575 | ((shift_num++)) 576 | 577 | local sticking_value=$ARGPARSER_UN_DEFINE 578 | if [[ $option_string = *=* ]]; then 579 | sticking_value=${option_string#*=} 580 | option_string=${option_string%%=*} 581 | fi 582 | local key=$(_argparser_get_argparser_optional_arg_key "$option_string" $ARGPARSER_OA_NO_PARSE) 583 | if [[ $key = '' ]]; then 584 | _argparser_error $Argparser_prog_name "unrecognized argument: $option_string" 585 | fi 586 | local nargs="${Argparser_argument_nargs[$key]}" 587 | if [[ $sticking_value = $ARGPARSER_UN_DEFINE ]]; then 588 | _argparser_get_cl_arg_values "$nargs" "$@" 589 | ((shift_num+=$__argparser_shift__num__)) 590 | else 591 | if [[ $nargs = 0 ]]; then 592 | _argparser_error $Argparser_prog_name "argument $option_string not need values: $option_string=$sticking_value" 593 | fi 594 | _argparser_get_cl_arg_values "$nargs" "$sticking_value" "$@" 595 | (( shift_num+=($__argparser_shift__num__ - 1) )) 596 | fi 597 | # set the values to $@ 598 | set -- "${__argparser_values__[@]}" 599 | # whether values count matches nargs 600 | _argparser_set_value2dest $key "$option_string" "$@" 601 | __argparser_shift__num__=$shift_num 602 | } 603 | 604 | # parse short optional argument 605 | # this function get the right number values from command-line arguments, 606 | # according to nargs 607 | # and assign value to ___value__, assign shift num to __argparser_shift__num__ 608 | # - $1: must be short optional arguments 609 | # - $2...: the unshift command-line arguments 610 | # used by argparser_parse 611 | function _argparser_parse_short_option() 612 | { 613 | _argparser_reset_values_and_shift_num 614 | local shift_num=0 615 | local command_line_1st_arg=$1 616 | shift 617 | ((shift_num++)) 618 | local sticking_value=$ARGPARSER_UN_DEFINE 619 | 620 | # parse short option like -abcValue 621 | local key nargs arg_string dest _option_string _arg_residues 622 | while ((${#command_line_1st_arg} > 2)); do 623 | _option_string=${command_line_1st_arg:0:2} 624 | _arg_residues=${command_line_1st_arg:2} 625 | key=$(_argparser_get_argparser_optional_arg_key "$_option_string" $ARGPARSER_OA_NO_PARSE) 626 | if [[ $key = '' ]]; then 627 | _argparser_error $Argparser_prog_name "unrecognized argument: $_option_string" 628 | fi 629 | nargs=${Argparser_argument_nargs[$key]} 630 | if [[ $nargs = 0 ]]; then 631 | _argparser_set_value2dest $key "$_option_string" 632 | # rebuilt command_line_1st_arg 633 | command_line_1st_arg='-'$_arg_residues 634 | else 635 | command_line_1st_arg=$_option_string 636 | sticking_value=$_arg_residues 637 | break 638 | fi 639 | done 640 | 641 | # parsing starts 642 | local option_string=$command_line_1st_arg 643 | key=$(_argparser_get_argparser_optional_arg_key "$option_string" $ARGPARSER_OA_NO_PARSE) 644 | if [[ $key = '' ]]; then 645 | _argparser_error $Argparser_prog_name "unrecognized argument: $option_string" 646 | fi 647 | nargs=${Argparser_argument_nargs[$key]} 648 | if [[ $sticking_value = $ARGPARSER_UN_DEFINE ]]; then 649 | _argparser_get_cl_arg_values "$nargs" "$@" 650 | ((shift_num+=$__argparser_shift__num__)) 651 | else 652 | if [[ $nargs = 0 ]]; then 653 | _argparser_error $Argparser_prog_name "$option_string not need values: $option_string=$sticking_value" 654 | fi 655 | _argparser_get_cl_arg_values "$nargs" "$sticking_value" "$@" 656 | (( shift_num+=($__argparser_shift__num__ - 1) )) 657 | fi 658 | # set the values to $@ 659 | set -- "${__argparser_values__[@]}" 660 | _argparser_set_value2dest $key "$option_string" "$@" 661 | __argparser_shift__num__=$shift_num 662 | } 663 | 664 | # parse positional argument 665 | # this function get the right number values from command-line arguments, 666 | # according to nargs 667 | # and assign values to ___value__, assign shift num to __argparser_shift__num__ 668 | # - $1: must be positional argument 669 | # - $2...: the unshift command-line arguments 670 | # used by argparser_parse 671 | function _argparser_parse_positional_arg() 672 | { 673 | local shift_num=0 674 | local key status 675 | key=$(_argparser_get_argparser_positional_arg_key "$ARGPARSER_PA_NO_PARSE") 676 | if [[ $key = '' ]]; then 677 | _argparser_error $Argparser_prog_name "unrecognized argument: $1" 678 | fi 679 | local nargs=${Argparser_argument_nargs[$key]} 680 | _argparser_get_cl_arg_values "$nargs" "$@" 681 | (($__argparser_shift__num__ == 0)) && \ 682 | _argparser_error $ARGPARSER_NAME "$FUNCNAME: shift number can not be 0." 'internal fatal' 683 | ((shift_num+=$__argparser_shift__num__)) 684 | set -- "${__argparser_values__[@]}" 685 | _argparser_set_value2dest $key - "$@" 686 | __argparser_shift__num__=$shift_num 687 | } 688 | 689 | # check the value number matching the nargs 690 | # - $1: nargs 691 | # - $2: error identifer: command-line optional string for optional argument; metavar for positional argument 692 | # - $3...: values 693 | function _argparser_check_value_narg_matching() 694 | { 695 | local nargs=$1 696 | local error_identifer=$2 697 | local ignore_pattern='^\*|\?|remain$' 698 | shift 2 699 | local pattern='^[1-9][0-9]*|0$' 700 | if [[ $nargs =~ $pattern ]]; then 701 | if (($# < $nargs)); then 702 | _argparser_usage 703 | _argparser_error $Argparser_prog_name "argument $error_identifer: expected $nargs arguments, but $# you supplied: $1... ." 704 | elif (($# > $nargs)); then 705 | _argparser_error $ARGPARSER_NAME "$FUNCNAME: argument $error_identifer nargs $nargs < $# value amount." internal 706 | fi 707 | elif [[ $nargs = '+' ]]; then # only used for optional argument 708 | if (($# < 1)); then 709 | _argparser_usage 710 | _argparser_error $Argparser_prog_name "argument $error_identifer expected at least 1 argument." 711 | fi 712 | elif [[ $nargs =~ $ignore_pattern ]]; then 713 | : # do nothing 714 | else 715 | _argparser_error $ARGPARSER_NAME "$FUNCNAME: argument $error_identifer: invalid nargs: $nargs." internal 716 | fi 717 | } 718 | 719 | # Assign values to the destination variable name of argument 720 | # - $1: key 721 | # - $2: -(positional)|command-line arg option string(optional) 722 | # - $?...: values 723 | function _argparser_set_value2dest() 724 | { 725 | local key=$1 726 | local error_identifer=$2 727 | shift 2 728 | local nargs=${Argparser_argument_nargs[$key]} 729 | local is_positional=false 730 | local parsed_status=$ARGPARSER_OA_PARSED 731 | if [[ $error_identifer = '-' ]]; then 732 | is_positional=true 733 | parsed_status=$ARGPARSER_PA_PARSED 734 | error_identifer=$(_argparser_choose_metavar \ 735 | "${Argparser_argument_metavar[$key]}" \ 736 | "${Argparser_argument_dest[$key]}" \ 737 | "${Argparser_argument_flag[$key]}") 738 | fi 739 | _argparser_check_value_narg_matching "$nargs" "$error_identifer" "$@" 740 | if (($# == 0)); then 741 | if [[ $is_positional = false ]]; then 742 | if [[ $nargs = remain ]]; then 743 | _argparser_do_action set "$key" "$error_identifer" 744 | _Argparser_argument_status[$key]=$parsed_status 745 | return 0 746 | fi 747 | local const=${Argparser_argument_const[$key]} 748 | if [[ $const = $ARGPARSER_UN_DEFINE ]]; then 749 | _argparser_do_action no_set "$key" "$error_identifer" 750 | else 751 | _argparser_do_action set "$key" "$error_identifer" "$const" 752 | fi 753 | else 754 | _argparser_error $ARGPARSER_NAME "$FUNCNAME: positional argument $error_identifer: value couldn't be empty." internal 755 | fi 756 | else 757 | local v 758 | # is in choices? 759 | local choices="${Argparser_argument_choices[$key]}" 760 | if [[ $choices != '' ]]; then 761 | local pattern='^('$choices')$' 762 | for v in "$@"; do 763 | [[ $v =~ $pattern ]] || \ 764 | _argparser_error $Argparser_prog_name "argument $error_identifer: $v not in [$choices]" 765 | done 766 | fi 767 | # check values 768 | local check="${Argparser_argument_check[$key]}" 769 | $check "$@" 770 | _argparser_do_action set "$key" "$error_identifer" "$@" 771 | fi 772 | # argument have been parsed 773 | _Argparser_argument_status[$key]=$parsed_status 774 | } 775 | 776 | # final check; we set the default value to the destination variables 777 | # - $1: key 778 | # - $2: parsed status you want to change 779 | function _argparser_set_default2dest() 780 | { 781 | local key=$1 782 | local parsed_status=$2 783 | local dest=$(_argparser_choose_dest "${Argparser_argument_dest[$key]}" "${Argparser_argument_flag[$key]}") 784 | local default=${Argparser_argument_default[$key]} 785 | if [[ $default != $ARGPARSER_UN_DEFINE ]]; then 786 | _argparser_globals_register register "$dest" "$default" 787 | fi 788 | _Argparser_argument_status[$key]=$parsed_status 789 | } 790 | 791 | # get the command-line-arg matching argparser optional argument index(key) 792 | # - $1: command line optional string 793 | # - $2...: status from _Argparser_argument_status 794 | function _argparser_get_argparser_optional_arg_key() 795 | { 796 | local cl_arg=$1 797 | shift 798 | local status=$* 799 | if [[ $status = '' ]]; then 800 | status=$ARGPARSER_OA_PARSED$ARGPARSER_OA_NO_PARSE 801 | fi 802 | local key _option_pattern 803 | for ((key=0; key < ${#_Argparser_argument_status[@]}; key++)); do 804 | if [[ ${_Argparser_argument_status[$key]} = [$status] ]]; then 805 | _option_pattern='^('${Argparser_argument_flag[$key]}')$' 806 | if [[ $cl_arg =~ $_option_pattern ]]; then 807 | echo $key 808 | return 809 | fi 810 | fi 811 | done 812 | } 813 | 814 | # get the first argparser positional argument index 815 | # - $1...: status from _Argparser_argument_status 816 | function _argparser_get_argparser_positional_arg_key() 817 | { 818 | local status=$* 819 | if [[ $status = '' ]]; then 820 | status=$ARGPARSER_PA_PARSED$ARGPARSER_PA_NO_PARSE 821 | fi 822 | for ((key=0; key < ${#_Argparser_argument_status[@]}; key++)); do 823 | if [[ ${_Argparser_argument_status[$key]} = [$status] ]]; then 824 | echo $key 825 | return 826 | fi 827 | done 828 | } 829 | 830 | # get values from command line arguments according to nargs 831 | # - $1: nargs 832 | # - $2...: command line args 833 | # set to __argparser_values__ 834 | function _argparser_get_cl_arg_values() 835 | { 836 | _argparser_reset_values_and_shift_num 837 | local values=() 838 | local shift_num=0 839 | local nargs=$1 840 | shift 841 | if [[ $nargs = '?' ]]; then 842 | if _argparser_is_positional_argment "$1"; then 843 | values=("$1") 844 | shift_num=1 845 | fi 846 | elif [[ $nargs = ['*+'] ]]; then 847 | _argparser_get_extending_values "$@" &&\ 848 | values=("${__argparser_values__[@]}") 849 | shift_num=$__argparser_shift__num__ 850 | elif [[ $nargs = 'remain' ]]; then 851 | values=("$@") 852 | shift_num=$# 853 | elif [[ $nargs > 0 ]]; then 854 | local value_len=0 855 | while (($# > 0)); do 856 | if _argparser_is_positional_argment "$1"; then 857 | values=("${values[@]}" "$1") 858 | value_len=${#values[@]} 859 | if [[ $value_len == $nargs ]]; then 860 | break 861 | fi 862 | shift 863 | else 864 | break 865 | fi 866 | done 867 | shift_num=$value_len 868 | elif [[ $nargs = 0 ]]; then 869 | : # do nothing 870 | else 871 | _argparser_error $ARGPARSER_NAME "$FUNCNAME: invalid nargs: $nargs." internal 872 | fi 873 | __argparser_values__=("${values[@]}") 874 | __argparser_shift__num__=$shift_num 875 | } 876 | 877 | # reset the internal exchanging variable __argparser_values__ and __argparser_shift__num__ 878 | # for safty reason 879 | function _argparser_reset_values_and_shift_num() 880 | { 881 | __argparser_values__=$ARGPARSER_UN_DEFINE 882 | __argparser_shift__num__=0 883 | } 884 | 885 | # get values for the nargs=*|+ arguments 886 | # this function have considerd how much arguments should left 887 | # for the un_hitting registerd positional arguments on command- 888 | # -line arguments. More to see the `_argparser_cl_arg_amount_need2reserved_now` function 889 | # - $1...: command-line args 890 | function _argparser_get_extending_values() 891 | { 892 | _argparser_reset_values_and_shift_num 893 | # scaning 894 | local has_extending_EOT=false 895 | local are_all_args_positional=true 896 | local shift_num=0 897 | local values=() 898 | # check if has_extending_EOT and are_all_args_positional 899 | while (($# > 0)); do 900 | if [[ $1 = $Argparser_nargs_extending_EOT ]]; then 901 | has_extending_EOT=true 902 | break 903 | elif _argparser_is_optional_argment "$1"; then 904 | are_all_args_positional=false 905 | break 906 | else 907 | values[${#values[@]}]=$1 908 | shift 909 | fi 910 | done 911 | # get values 912 | if [[ $has_extending_EOT = false && $are_all_args_positional = true ]]; then 913 | local reserved_num=$(_argparser_cl_arg_amount_need2reserved_now) 914 | local value_len=${#values[@]} 915 | if (($value_len > $reserved_num > 0)); then 916 | values=("${values[@]:0:$(($value_len - $reserved_num))}") 917 | fi 918 | fi 919 | shift_num=${#values[@]} 920 | if [[ $has_extending_EOT = true ]]; then 921 | ((shift_num++)) 922 | fi 923 | __argparser_shift__num__=$shift_num 924 | __argparser_values__=("${values[@]}") 925 | } 926 | 927 | # calculate how much argparser argument nargs(only number type nargs) needing to reserved 928 | # used by _argparser_get_extending_values 929 | function _argparser_cl_arg_amount_need2reserved_now() 930 | { 931 | local key nargs status 932 | local reserved_num=0 933 | local _pattern='^[0-9]+$' 934 | for ((key=0; key < ${#_Argparser_argument_status[@]}; key++)); do 935 | status=${_Argparser_argument_status[$key]} 936 | nargs=${Argparser_argument_nargs[$key]} 937 | if [[ $status = $ARGPARSER_PA_NO_PARSE ]] &&\ 938 | [[ $nargs =~ $_pattern ]]; then 939 | ((reserved_num+=$nargs)) 940 | fi 941 | done 942 | echo $reserved_num 943 | } 944 | 945 | function _argparser_format_arg_help() 946 | { 947 | local arg_flag key arg_desc arg_strings metavar dest _is_required 948 | for key in ${!Argparser_argument_flag[@]}; do 949 | arg_strings=${Argparser_argument_flag[$key]} 950 | dest=${Argparser_argument_dest[$key]} 951 | metavar=${Argparser_argument_metavar[$key]} 952 | nargs=${Argparser_argument_nargs[$key]} 953 | dest=$(_argparser_choose_dest "$dest" "$arg_strings") 954 | metavar=$(_argparser_choose_metavar "$metavar" "$dest" "$arg_strings") 955 | arg_flag=$(_argparser_format_argument_flag "$arg_strings" "$nargs" "$metavar") 956 | if [[ ${Argparser_argument_required[$key]} != false ]]; then 957 | _is_required='not required' 958 | else 959 | _is_required='required' 960 | fi 961 | # translate some user word 962 | arg_desc=$(_argparser_translate "${Argparser_argument_desc[$key]}" \ 963 | prog "$Argparser_prog_name" \ 964 | default "${Argparser_argument_default[$key]}" \ 965 | choices "${Argparser_argument_choices[$key]}" \ 966 | const "${Argparser_argument_const[$key]}" \ 967 | dest "$dest" \ 968 | metavar "$metavar" \ 969 | nargs "$nargs" \ 970 | required "$_is_required") 971 | printf "%-30s %-53s\n" "$arg_flag" "$arg_desc" 972 | done 973 | } 974 | 975 | # format description message for every registerd arugments 976 | # - $1: option_string 977 | # - $2: nargs 978 | # - $3: metavar 979 | function _argparser_format_argument_flag() 980 | { 981 | local LEFT_FLAG=' ' # left 2 space in the flags left side. 982 | local option_string=${1//'|'/ } 983 | local nargs=$2 984 | local metavar=$3 985 | 986 | set -- $option_string 987 | local last_one_option_str=${@:$#:1} 988 | local option_flag 989 | # add detail flag to the last one arg_string 990 | if _argparser_is_positional_argment "$last_one_option_str"; then 991 | echo "$LEFT_FLAG$metavar" 992 | return 993 | else 994 | local pattern='^[1-9][0-9]*$' 995 | local _metavar_string 996 | if [[ $nargs =~ $pattern ]]; then 997 | if (($nargs > 1)); then 998 | _metavar_string="=$metavar ..." 999 | else 1000 | _metavar_string="=$metavar" 1001 | fi 1002 | else 1003 | case $nargs in 1004 | '?') _metavar_string="=[$metavar]" 1005 | ;; 1006 | '*') _metavar_string="=[$metavar ...]" 1007 | ;; 1008 | '+') _metavar_string="=$metavar [...]" 1009 | ;; 1010 | 'remain') _metavar_string="=..." 1011 | ;; 1012 | '0') _metavar_string="=" 1013 | ;; 1014 | esac 1015 | fi 1016 | # concat option string and _metavar_string 1017 | if _argparser_is_long_optional_argument "$last_one_option_str"; then 1018 | [[ $_metavar_string = '=' ]] && _metavar_string='' 1019 | option_flag="$last_one_option_str$_metavar_string" 1020 | else 1021 | option_flag="$last_one_option_str ${_metavar_string#=}" 1022 | fi 1023 | fi 1024 | 1025 | # pop the last option string and join the left option string 1026 | set -- "${@:1:$(($# - 1))}" 1027 | local _opts='' 1028 | while (($# > 0)); do 1029 | _opts+="$1, " 1030 | shift 1031 | done 1032 | echo "$LEFT_FLAG$_opts$option_flag" 1033 | } 1034 | 1035 | # usage would formate only once 1036 | # it stored the formated usage string to _argparser_usage 1037 | function _argparser_usage() 1038 | { 1039 | if [[ $_argparser_usage = $ARGPARSER_UN_DEFINE ]]; then 1040 | _argparser_usage=$(_argparser_format_usge) 1041 | fi 1042 | printf '%s\n' "$_argparser_usage" 1043 | } 1044 | 1045 | # used by _argparser_usage 1046 | function _argparser_format_usge() 1047 | { 1048 | local usage 1049 | if [[ $Argparser_usage != $ARGPARSER_UN_DEFINE ]]; then 1050 | usage='usage: '$(_argparser_translate "$Argparser_usage" prog "$Argparser_prog_name") 1051 | echo -n "$usage" 1052 | return 1053 | fi 1054 | usage="usage: $Argparser_prog_name" 1055 | local _metavar_string one_arg_usage option_string 1056 | local pattern='^[1-9][0-9]*$' 1057 | local key nargs arg_strings metavar 1058 | for key in ${!Argparser_argument_flag[@]}; do 1059 | nargs=${Argparser_argument_nargs[$key]} 1060 | arg_strings=${Argparser_argument_flag[$key]} 1061 | metavar=$(_argparser_choose_metavar \ 1062 | "${Argparser_argument_metavar[$key]}" \ 1063 | "${Argparser_argument_dest[$key]}" \ 1064 | "$arg_strings") 1065 | if [[ $nargs =~ $pattern ]]; then 1066 | printf -v _metavar_string "$metavar %.0s" $(seq $nargs) 1067 | _metavar_string=${_metavar_string% } 1068 | else 1069 | case $nargs in 1070 | '?') _metavar_string="[$metavar]" 1071 | ;; 1072 | '*') _metavar_string="[$metavar ...]" 1073 | ;; 1074 | '+') _metavar_string="$metavar [$metavar ...]" 1075 | ;; 1076 | 'remain') _metavar_string="[...]" 1077 | ;; 1078 | '0') _metavar_string="" 1079 | ;; 1080 | esac 1081 | fi 1082 | if _argparser_is_optional_argment "$arg_strings"; then 1083 | local option_string=$(_argparser_choose_shortest_optional_string "$arg_strings") 1084 | one_arg_usage="$option_string $_metavar_string" 1085 | one_arg_usage=${one_arg_usage% } 1086 | if [[ $Argparser_argument_required[$key] != true ]]; then 1087 | one_arg_usage="[$one_arg_usage]" 1088 | fi 1089 | else 1090 | one_arg_usage=$_metavar_string 1091 | fi 1092 | usage+=" $one_arg_usage" 1093 | done 1094 | echo -n "$usage" 1095 | } 1096 | 1097 | # translate the string like %(prog)s 1098 | # - $1: string 1099 | # - ${2, 4..}: word of `%(word)s`, the word will be translate 1100 | # - ${3, 5..}: translating to string 1101 | function _argparser_translate() 1102 | { 1103 | local str=$1 1104 | if [[ $str = '' ]]; then 1105 | printf "%s" "" 1106 | return 1107 | fi 1108 | shift 1109 | local word to 1110 | while (($# > 0)); do 1111 | word="%(${1})s" 1112 | shift 1113 | to=$1 1114 | shift 1115 | str=${str//$word/$to} 1116 | done 1117 | str=${str//'\s'/'s'} 1118 | printf "%s" "$str" 1119 | } 1120 | 1121 | # - $1: Argparser_argument_flag 1122 | function _argparser_choose_shortest_optional_string() 1123 | { 1124 | local optional_strings=${1//'|'/ } 1125 | local shortest_one 1126 | set -- ${optional_strings[*]} 1127 | shortest_one=$1 1128 | shift 1129 | while (($# > 0)); do 1130 | if ((${#shortest_one} > ${#1})); then 1131 | shortest_one=$1 1132 | fi 1133 | shift 1134 | done 1135 | printf "%s" "$shortest_one" 1136 | } 1137 | 1138 | # - $1: set|no_set, whether or not set to dest by argparser_store 1139 | # - $2: key 1140 | # - $3: command-line argument string 1141 | function _argparser_do_action() 1142 | { 1143 | local to_set=$1 1144 | local key=$2 1145 | local cl_arg_string=$3 1146 | shift 3 1147 | local action=${Argparser_argument_action[$key]} 1148 | local dest=${Argparser_argument_dest[$key]} 1149 | local arg_strings=${Argparser_argument_flag[$key]} 1150 | dest=$(_argparser_choose_dest "$dest" "$arg_strings") 1151 | if [[ $action = '' ]]; then 1152 | if [[ $to_set = 'set' ]]; then 1153 | argparser_store "$dest" "$cl_arg_string" "$arg_strings" - - - "$@" 1154 | fi 1155 | else 1156 | $action "$dest" "$cl_arg_string" "$arg_strings" - - - "$@" 1157 | fi 1158 | } 1159 | 1160 | # - $1: metavar 1161 | # - $2: dest 1162 | # - $3: argparser argument strings 1163 | function _argparser_choose_metavar() 1164 | { 1165 | local metavar=$1 1166 | if [[ $metavar = $ARGPARSER_UN_DEFINE ]]; then 1167 | local dest=$2 1168 | if [[ $dest != '' ]]; then 1169 | metavar=$dest 1170 | else 1171 | local argument_string=$3 1172 | metavar=$(_argparser_choose_longest_argument_string "$argument_string") 1173 | fi 1174 | fi 1175 | printf "%s" "$metavar" 1176 | } 1177 | 1178 | # - $1: Argparser_argument_flag 1179 | function _argparser_choose_longest_argument_string() 1180 | { 1181 | local optional_strings=${1//'|'/ } 1182 | set -- ${optional_strings[*]} 1183 | local longest_one=$(_argparser_trim_option_prefixs "$1") 1184 | shift 1185 | local arg_string 1186 | while (($# > 0)); do 1187 | arg_string=$(_argparser_trim_option_prefixs "$1") 1188 | if ((${#longest_one} < ${#arg_string})); then 1189 | longest_one=$arg_string 1190 | fi 1191 | shift 1192 | done 1193 | printf "%s" "$longest_one" 1194 | } 1195 | 1196 | # - $1: dest 1197 | # - $2: argparser argument strings 1198 | function _argparser_choose_dest() 1199 | { 1200 | local dest=$1 1201 | if [[ $dest != '' ]]; then 1202 | echo "$dest" 1203 | else 1204 | shift 1205 | local arg_strings=${1//|/ } 1206 | local _pattern='^[_a-zA-Z][_0-9a-zA-Z]*$' 1207 | set -- ${arg_strings[*]} 1208 | dest='' 1209 | while (($# > 0)); do 1210 | _dest=$(_argparser_trim_option_prefixs "$1") 1211 | shift 1212 | if ((${#dest} < ${#_dest})) && [[ $_dest =~ $_pattern ]]; then 1213 | dest=$_dest 1214 | fi 1215 | done 1216 | echo "$dest" 1217 | fi 1218 | } 1219 | 1220 | # trim the prefix chars of optional argument 1221 | # like --option_name -> option_name 1222 | function _argparser_trim_option_prefixs() 1223 | { 1224 | if _argparser_is_optional_argment "$1"; then 1225 | if _argparser_is_long_optional_argument "$1"; then 1226 | echo "${1:2}" 1227 | else 1228 | echo "${1:1}" 1229 | fi 1230 | else 1231 | echo "$1" 1232 | fi 1233 | } 1234 | 1235 | 1236 | # register variable declaring literal into an globals scope 1237 | # - $1: action: register|dump|arbitrary|dry_dump 1238 | # - $2: variable name 1239 | # - $3...: values 1240 | function _argparser_globals_register() 1241 | { 1242 | # The global register's globals variable 1243 | ### __Globals_Register_Variable_Declaring_Literal__ ### 1244 | local action__=$1 1245 | shift 1246 | if [[ $action__ = 'register' || $action__ = 'arbitrary' ]]; then 1247 | local variable_name__=$1 1248 | local pattern__='^[_a-zA-Z][_0-9a-zA-Z]*$' 1249 | if [[ ! $1 =~ $pattern__ ]]; then 1250 | _argparser_error "$ARGPARSER_NAME" "$FUNCNAME $action__: invalid variable name: $1." internal 1251 | fi 1252 | shift 1253 | local v__ 1254 | local values__=() 1255 | while (($# > 0)); do 1256 | v__=\'${1//"'"/"'\''"}\' 1257 | values__[${#values__[@]}]=$v__ # push to array 1258 | shift 1259 | done 1260 | # arbitrarily set the value to variable 1261 | if [[ $action__ = 'arbitrary' ]]; then 1262 | eval $variable_name__=\("${values__[@]}"\) 1263 | return 0 1264 | fi 1265 | # register to global literal 1266 | local declaring_literal=$variable_name__=\("${values__[@]}"\)$'\n' 1267 | __Globals_Register_Variable_Declaring_Literal__+=$declaring_literal 1268 | elif [[ $action__ = 'dump' ]]; then 1269 | eval "$__Globals_Register_Variable_Declaring_Literal__" 1270 | unset __Globals_Register_Variable_Declaring_Literal__ 1271 | elif [[ $action__ = 'dry_dump' ]]; then 1272 | unset __Globals_Register_Variable_Declaring_Literal__ 1273 | else 1274 | _argparser_error "$ARGPARSER_NAME" "$FUNCNAME: invalid action__: $action__." internal 1275 | fi 1276 | } 1277 | 1278 | # here to do the real argparser works 1279 | # initial argparser 1280 | if (( $# > 0 )); then 1281 | if [[ $1 != *=* ]]; then 1282 | Argparser_prog_name=$1 1283 | shift 1284 | fi 1285 | while (( $# > 0 )); do 1286 | case $1 in 1287 | usage=*) Argparser_usage=${1#*=} 1288 | ;; 1289 | desc=*) Argparser_desc=${1#*=} 1290 | ;; 1291 | prefix_chars=*) Argparser_prefix_chars=${1#*=} 1292 | ;; 1293 | help=*) Argparser_help=${1#*=} 1294 | ;; 1295 | add_help=*) Argparser_add_help=${1#*=} 1296 | ;; 1297 | epilog=*) Argparser_epilog=${1#*=} 1298 | ;; 1299 | prologue=*) Argparser_prologue=${1#*=} 1300 | ;; 1301 | nargs_extending_EOT=*) Argparser_nargs_extending_EOT=${1#*=} 1302 | ;; 1303 | *) _argparser_error $ARGPARSER_NAME "unrecognized argument: $1" 1304 | ;; 1305 | esac 1306 | shift 1307 | done 1308 | fi 1309 | # add help option here 1310 | if [[ $Argparser_add_help = true ]]; then 1311 | argparser_add_arg -h --help desc="show this help message and exit" action=argparser_help nargs=0 1312 | fi 1313 | # -- initial argparser done 1314 | } 1315 | -------------------------------------------------------------------------------- /lib/rootbox/args.sh: -------------------------------------------------------------------------------- 1 | # This Source Code Form is subject to the terms of the Mozilla Public 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this 3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | . `dirname $0`/../lib/rootbox/argparser/argparser 6 | 7 | 8 | # HOW COMMANDS WORK: 9 | 10 | # A command is registered using register_command. There should be three 11 | # functions defined associated with the command: 12 | 13 | # ${command} - Runs the given command. 14 | # ${command}::ARGS - Registers the command's arguments with argparser. 15 | # #{command}::DESCR - Prints a description of the command to stdout. 16 | 17 | # After the arguments are parsed, the function named after the command (the 18 | # first one in the list) will be called, with all the arguments loaded into 19 | # variables. For instance, box.new will be called with the variable $name 20 | # pre-created, corresponding to the --name argument. 21 | 22 | 23 | collect_command_info() { 24 | # collect_command_info 25 | # Prints information about all the registered commands. 26 | 27 | for cmd in "${COMMANDS[@]}"; do 28 | printf ' %-28s - ' $cmd 29 | eval ${cmd}::DESCR 30 | done 31 | } 32 | 33 | 34 | ROOTBOX_HEADER=$(cat <" "$@" 92 | } 93 | 94 | 95 | show_licenses() { 96 | # show_licenses 97 | # Prints the licenses to stdout, as well as optionally printing the FULL 98 | # license text if requested. 99 | 100 | echo "Rootbox is (c) 2017 Ryan Gonzalez and is licensed under the \ 101 | Mozilla Public License 2.0." 102 | echo "Rootbox embeds argparser, which is (c) 2016 Ekeyme Mo and is licensed \ 103 | under the MIT license." 104 | 105 | while [ "$answer" != "y" ] && [ "$answer" != "n" ]; do 106 | echo -n "Show full license text? [y/n] " 107 | read answer 108 | done 109 | 110 | if [ "$answer" == "y" ]; then 111 | cd "`dirname $0`/../lib/rootbox" 112 | echo | cat LICENSE - LICENSE-THIRD-PARTY | less 113 | fi 114 | 115 | quit 116 | } 117 | 118 | 119 | init_argparser() { 120 | local prefix="$1" 121 | argparser "$prefix" "desc=$ROOTBOX_HEADER" 122 | } 123 | 124 | 125 | generic_command_setup() { 126 | add_bool_flag "D" "debug" "Print each shell command as it's executed." 127 | add_bool_flag "L" "license" "Show the license." 128 | add_value_flag "C" "colors" "Set whether or not Rootbox should print \ 129 | colored text. (Valid values: always, auto, never)" "auto" \ 130 | "choices=always|auto|never" 131 | 132 | disable_errors 133 | argparser_parse "$@" 134 | enable_errors 135 | 136 | [ "$debug" == "true" ] && enable_debug ||: 137 | [ "$license" == "true" ] && show_licenses ||: 138 | update_colors "$colors" 1 139 | } 140 | 141 | 142 | run_no_command() { 143 | # run_no_command 144 | # Runs rootbox without a command. 145 | 146 | ROOTBOX_HEADER+=$(cat < " 156 | generic_command_setup "$@" 157 | die "A command is required!" 158 | } 159 | 160 | 161 | run_command() { 162 | # run_command cmd 163 | # Runs the given command (what else did you think it'd do?). 164 | 165 | local cmd="$1" 166 | shift 167 | 168 | [[ " ${COMMANDS[@]} " =~ " $cmd " ]] || \ 169 | die "Invalid command '$cmd'; use --help for help" 170 | 171 | ROOTBOX_HEADER+=$(cat </dev/null 34 | 35 | [ -d /_factory ] && sudo -u user /bin/ash /_factory/_all.sh ||: 36 | EOF 37 | ` 38 | 39 | 40 | box_setup() { 41 | # box_setup 42 | # Sets up the box $tbox using the image factory location in $factory. 43 | 44 | [ -n "$factory" ] && setup_factories "$mpoint" "$factory" "$version" 45 | 46 | [ -f "$mpoint/$SETUP" ] && \ 47 | pwarn "The image '$version' this box is based on was created before a \ 48 | recent change in Rootbox and still contains old setup code. If you want to \ 49 | remove the code, simply remove and recreate the image. (Note that this change 50 | does NOT impact any boxes that have already been created, but it does impact 51 | the creation process!)" 52 | echo "$SETUP_CODE" > "$mpoint/$SETUP" 53 | 54 | safecall box_actual_setup \ 55 | "rm -rf `proper_quote "$mpoint/_factory"` `proper_quote "$mpoint/$SETUP"`" 56 | } 57 | 58 | 59 | fix_box_permissions() { 60 | local box="$1" 61 | 62 | sudo_perm_fix "$box" 63 | sudo_perm_fix "$box/binds" 664 64 | sudo_perm_fix "$box/image" 664 65 | } 66 | 67 | 68 | box.new() { 69 | require_init 70 | require_root 71 | 72 | local image="`image_path "v$version"`" 73 | local box="`box_path "$name"`" 74 | local tbox="$box.tmp" # Temporary box path. 75 | 76 | export box tbox factory version 77 | 78 | pdebug "@image='$image' box='$box' tbox='$tbox'" 79 | 80 | [ ! -d "$box" ] || die "Box '$name' has already been created" 81 | [ -f "$image" ] || die "Image $version is not yet installed" 82 | 83 | pnote "Creating box..." 84 | 85 | mkdir -p "$tbox" 86 | 87 | echo -e `join "\n" "${bind[@]}"` > "$tbox/binds" 88 | pdebug "binds: `cat "$tbox/binds" | tr '\n' ';'`" 89 | cp --sparse=always "$image" "$tbox/image" 90 | 91 | pnote "Setting up box..." 92 | echo "$version" > "$tbox/version" 93 | with_mount "$tbox/image" box_setup 94 | 95 | pnote "Saving box..." 96 | fix_box_permissions "$tbox" 97 | mv "$tbox" "$box" 98 | fix_box_permissions "$box" ||: 99 | 100 | pnote "Setup successful!" 101 | 102 | if [ "$run" == "true" ]; then 103 | "$0" box.run "$name" 104 | fi 105 | } 106 | 107 | 108 | box.new::DESCR() { 109 | echo "creates a new box using the given Alpine Linux image." 110 | } 111 | 112 | 113 | box.new::ARGS() { 114 | add_positional "name" "The name of the new box" 115 | add_positional "bind" "Set the given directory to be automatically bind \ 116 | mounted whenever the box is run. Can be passed multiple times." nargs=* 117 | add_value_flag "v" "version" "The Alpine Linux version to use" "$DEFAULT_VER" 118 | add_value_flag "f" "factory" "The location path of the image factory to use" \ 119 | "" 120 | add_bool_flag "r" "run" "Run the box after creation" 121 | } 122 | 123 | 124 | box.info() { 125 | require_init 126 | 127 | local box="`box_path "$name"`" 128 | [ -d "$box" ] || die "Box '$name' does not exist" 129 | 130 | declare -A info 131 | info[Name]="$name" 132 | info[Location]="`realpath "$box"`/" 133 | 134 | if [ -f "$box/version" ]; then 135 | info[Image]="`<"$box/version"`" 136 | else 137 | info[Image]="" 138 | fi 139 | 140 | info[Size]="`du -h "$box" | cut -f1`" 141 | info[Binds]="`sed '/^$/d' <"$box/binds" | tr '\n' ' '`" 142 | [ -n "${info[Binds]}" ] || info[Binds]="" 143 | 144 | keys=(Name Location Image Size Binds) 145 | 146 | for key in "${keys[@]}"; do 147 | printfln "${C_NOTE}%-10s$C_R $C_B%s" "$key:" "${info[$key]}" 148 | done 149 | } 150 | 151 | 152 | box.info::DESCR() { 153 | echo "prints information about the given box." 154 | } 155 | 156 | 157 | box.info::ARGS() { 158 | add_positional "name" "The name of the box for which to find info" 159 | } 160 | 161 | 162 | box.clone() { 163 | require_init 164 | 165 | local srcbox="`box_path "$source"`" 166 | [ -d "$srcbox" ] || die "Box '$source' does not exist" 167 | 168 | local dstbox="`box_path "$name"`" 169 | [ ! -d "$dstbox" ] || die "Box '$name' has already been created" 170 | 171 | pnote "Cloning box..." 172 | 173 | cp --sparse=always -r "$srcbox" "$dstbox" 174 | fix_box_permissions "$dstbox" 175 | 176 | pnote "Clone was successful!" 177 | } 178 | 179 | 180 | box.clone::DESCR() { 181 | echo "clones the given box." 182 | } 183 | 184 | 185 | box.clone::ARGS() { 186 | add_positional "source" "The name of the box to clone" 187 | add_positional "name" "The name of the new box" 188 | } 189 | 190 | 191 | box.dist() { 192 | require_init 193 | 194 | local box="`box_path "$name"`" 195 | [ -d "$box" ] || die "Box '$name' does not exist" 196 | 197 | pnote "Exporting box..." 198 | 199 | local taropts compress_ext 200 | case "$compress" in 201 | gzip) taropts=-z; compress_ext=.gz ;; 202 | bzip2) taropts=-y; compress_ext=.bz2 ;; 203 | none) ;; 204 | esac 205 | 206 | pdebug "@taropts='$taropts' compress_ext='$compress_ext'" 207 | 208 | [ "$output" == '$name.box' ] && output="$name.box$compress_ext" ||: 209 | 210 | bsdtar cf "$output" -C "$box" $taropts binds image 211 | sudo_perm_fix "$output" 664 212 | 213 | pnote "Successfully exported box to '$output'!" 214 | } 215 | 216 | 217 | box.dist::DESCR() { 218 | echo "exports the given box for distribution." 219 | } 220 | 221 | 222 | box.dist::ARGS() { 223 | add_positional "name" "The name of the box to export" 224 | add_value_flag "o" "output" "The output file" '$name.box' 225 | add_value_flag "c" "compress" "Compress the result with the given \ 226 | compression method (Valid values: none, bzip2, gzip)" "none" \ 227 | "choices=none|bzip2|gzip" 228 | } 229 | 230 | 231 | import_box() { 232 | pnote "Importing box..." 233 | tar xf "$path" -C "$tbox" 234 | } 235 | 236 | 237 | box.import() { 238 | require_init 239 | 240 | local box="`box_path "$name"`" 241 | local tbox="$box.tmp" 242 | export box 243 | 244 | [ ! -d "$box" ] || die "Box '$name' has already been created" 245 | 246 | pnote "Setting up import..." 247 | mkdir -p "$tbox" 248 | 249 | with_location "$loc" import_box dist.box 250 | fix_box_permissions "$tbox" 251 | mv "$tbox" "$box" 252 | 253 | pnote "Successfully imported box '$name'!" 254 | } 255 | 256 | 257 | box.import::DESCR() { 258 | echo "imports an exported box into the Rootbox workspace." 259 | } 260 | 261 | 262 | box.import::ARGS() { 263 | add_positional "loc" "The location path of the box to import" 264 | add_positional "name" "The name of the new box" 265 | } 266 | 267 | 268 | box.list() { 269 | require_init 270 | find "$BOXES" -mindepth 1 -maxdepth 1 -type d -printf '%f\n' | \ 271 | sed 's/.box$//' | sort 272 | } 273 | 274 | 275 | box.list::DESCR() { 276 | echo "lists all installed boxes." 277 | } 278 | 279 | 280 | box.list::ARGS() { 281 | : 282 | } 283 | 284 | 285 | box.update.binds() { 286 | require_init 287 | 288 | local box="`box_path "$name"`" 289 | [ -d "$box" ] || die "Box '$name' does not exist" 290 | 291 | [ -z "${!bind[@]}" ] && die "Need at least one bind to modify!" 292 | 293 | for spec in "${bind[@]}"; do 294 | if [[ "$spec" == ^* ]]; then 295 | spec=${spec#*^} 296 | if ! grep -q "$spec" "$box/binds"; then 297 | pwarn "Bind spec $spec was not present in the list of bind mounts; it \ 298 | will not be removed." 299 | else 300 | sed -i "/^$spec\$/d" "$box/binds" 301 | fi 302 | else 303 | echo "$spec" >> "$box/binds" 304 | fi 305 | done 306 | } 307 | 308 | 309 | box.update.binds::DESCR() { 310 | echo "updates the binds for the given box. (Use 'rootbox box.info' to see a \ 311 | list of the current binds.)" 312 | } 313 | 314 | 315 | box.update.binds::ARGS() { 316 | add_positional "name" "The name of the box to update" 317 | add_positional "bind" "A new bind mount to add. If it's prefixed with a caret 318 | (^), then it will be removed instead. Can be passed multiple times." nargs=* 319 | } 320 | 321 | 322 | run_factory_all() { 323 | in_chroot "$mpoint" user "/bin/ash /_factory/_all.sh" \ 324 | "Failed to run factory scripts" "$box/binds" 325 | } 326 | 327 | 328 | update_box_with_factory() { 329 | # update_box_with_factory 330 | # Loads the factory at $factory into the mounted image. 331 | 332 | setup_factories "$mpoint" "$factory" "$version" 333 | safecall run_factory_all "rm -rf `proper_quote "$mpoint/_factory"`" 334 | } 335 | 336 | 337 | box.update.factory() { 338 | require_init 339 | require_root 340 | 341 | local box="`box_path "$name"`" 342 | [ -d "$box" ] || die "Box '$name' does not exist" 343 | 344 | if [ ! -f "$box/version" ]; then 345 | pwarn "This box was created before a small change in the box format that \ 346 | remembers the image version used. For now, the image version will be assumed \ 347 | to be 3.5. If you want to set the version permanently, then just run \ 348 | 'rootbox box.update.version-override '" 349 | version=3.5 350 | else 351 | version="`<"$box/version"`" 352 | fi 353 | 354 | export factory version 355 | 356 | with_mount "$box/image" update_box_with_factory "$version" 357 | } 358 | 359 | 360 | box.update.factory::DESCR() { 361 | echo "updates the given box by running the given factory on it." 362 | } 363 | 364 | 365 | box.update.factory::ARGS() { 366 | add_positional "name" "The name of the box to update" 367 | add_positional "factory" "The factory to use" 368 | } 369 | 370 | 371 | box.update.version-override() { 372 | require_init 373 | 374 | local box="`box_path "$name"`" 375 | [ -d "$box" ] || die "Box '$name' does not exist" 376 | 377 | if [ -f "$box/version" ]; then 378 | orig="`<"$box/version"`" 379 | else 380 | orig="" 381 | fi 382 | 383 | echo "$version" > "$box/version" 384 | pnote "Version forcibly changed from $orig to $version." 385 | } 386 | 387 | 388 | box.update.version-override::DESCR() { 389 | echo "forcibly overrides the given box's version." 390 | } 391 | 392 | 393 | box.update.version-override::ARGS() { 394 | add_positional "name" "The name of the box to update" 395 | add_positional "version" "The new version to use" 396 | } 397 | 398 | 399 | box_run_command() { 400 | # box_run_command 401 | # Runs $command inside the mounted box $mpoint, with the proper bind mounts. 402 | 403 | if [ "$x11" == "true" ]; then 404 | pdebug "Using X11" 405 | [ -n "$DISPLAY" ] || die '$DISPLAY is empty!' 406 | 407 | while read -r cookie; do 408 | pdebug "X11 cookie: $cookie" 409 | xauth extract "$mpoint/root/.Xauthority" "$cookie" 410 | xauth extract "$mpoint/home/user/.Xauthority" "$cookie" 411 | sudo_perm_fix "$mpoint/home/user/.Xauthority" 644 412 | done <<<"`xauth list "$DISPLAY" | cut "-d " -f1`" 413 | 414 | bind+=("/tmp/.X11-unix///tmp/.X11-unix") 415 | command="export DISPLAY=$DISPLAY; $command" 416 | fi 417 | 418 | pdebug "@command='$command'" 419 | 420 | in_chroot "$mpoint" "$user" "$command" "" "$box/binds" "${bind[@]}" 421 | } 422 | 423 | 424 | box.run() { 425 | require_init 426 | require_root 427 | 428 | local box="`box_path "$name"`" 429 | [ -d "$box" ] || die "Box '$name' does not exist" 430 | 431 | export box command user x11 432 | with_mount "$box/image" box_run_command 433 | } 434 | 435 | 436 | box.run::DESCR() { 437 | echo "runs the command inside the given box." 438 | } 439 | 440 | box.run::ARGS() { 441 | add_positional "name" "The name of the box to run" 442 | add_positional "bind" "Bind mount the given directory inside the chroot \ 443 | before the command is run. Can be passed multiple times." nargs=* 444 | add_bool_flag "x" "x11" "Add X11 support upon startup" 445 | add_value_flag "c" "command" "The command to run" "$DEFAULT_COMMAND" 446 | add_value_flag "u" "user" "The user to use inside the chroot" "user" 447 | } 448 | 449 | 450 | box.remove() { 451 | require_init 452 | 453 | local box="`box_path "$name"`" 454 | [ -d "$box" ] || die "Box '$name' does not exist" 455 | 456 | rm -f "$box/binds" 457 | rm -f "$box/image" 458 | rm -f "$box/version" 459 | rmdir "$box" 460 | 461 | pnote "Successfully removed box '$name'." 462 | } 463 | 464 | 465 | box.remove::DESCR() { 466 | echo "deletes the given box." 467 | } 468 | 469 | 470 | box.remove::ARGS() { 471 | add_positional "name" "The name of the box to delete" 472 | } 473 | 474 | 475 | box_push() { 476 | dest="$mpoint/$dest" 477 | mkdir -p "`dirname "$dest"`" 478 | cp -r "$file" "$dest" 479 | } 480 | 481 | 482 | box.push() { 483 | require_init 484 | require_root 485 | 486 | [ -f "$file" ] || die "File '$file' does not exist" 487 | local box="`box_path "$box"`" 488 | [ -d "$box" ] || die "Box '$box' does not exist" 489 | [[ "$dest" = /* ]] || die "Destination path '$dest' should be absolute" 490 | 491 | export file dest 492 | with_mount "$box/image" box_push 493 | } 494 | 495 | 496 | box.push::DESCR() { 497 | echo "sends the given file to the given box." 498 | } 499 | 500 | 501 | box.push::ARGS() { 502 | add_positional "box" "The box to send the file into" 503 | add_positional "file" "The file to send into the box" 504 | add_positional "dest" "The destination path within the box" 505 | } 506 | 507 | 508 | box_pull() { 509 | file="$mpoint/$file" 510 | [ -f "$file" ] || die "Source path '$file' does not exist inside mount point" 511 | mkdir -p "`dirname "$dest"`" 512 | cp -r "$file" "$dest" 513 | } 514 | 515 | 516 | box.pull() { 517 | require_init 518 | require_root 519 | 520 | # [ -f "$file" ] || die "File '$file' does not exist" 521 | local box="`box_path "$box"`" 522 | [ -d "$box" ] || die "Box '$box' does not exist" 523 | [[ "$file" = /* ]] || die "Source path '$file' should be absolute" 524 | 525 | export file dest 526 | with_mount "$box/image" box_pull 527 | } 528 | 529 | 530 | box.pull::DESCR() { 531 | echo "retrieves the given file from inside the box" 532 | } 533 | 534 | 535 | box.pull::ARGS() { 536 | add_positional "box" "The box from which to get the file" 537 | add_positional "file" "The file to retrieve from the box" 538 | add_positional "dest" "The destination path" 539 | } 540 | 541 | 542 | register_command box.new 543 | register_command box.info 544 | register_command box.clone 545 | register_command box.dist 546 | register_command box.import 547 | register_command box.list 548 | register_command box.update.binds 549 | register_command box.update.factory 550 | register_command box.update.version-override 551 | register_command box.run 552 | register_command box.remove 553 | register_command box.push 554 | register_command box.pull 555 | -------------------------------------------------------------------------------- /lib/rootbox/factory.sh: -------------------------------------------------------------------------------- 1 | # This Source Code Form is subject to the terms of the Mozilla Public 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this 3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | 6 | DOWNLOADED_HASHES=() 7 | 8 | 9 | get_factory() { 10 | local sha="`sha1sum "$path" | tr ' ' '\t' | cut -f1`" 11 | pdebug "@path=$path,sha=$sha,DOWNLOADED_HASHES=${DOWNLOADED_HASHES[@]}" 12 | if [[ " ${DOWNLOADED_HASHES[@]} " =~ " $sha " ]]; then 13 | pnote "NOTE: skipping because this factory has already been downloaded..." 14 | return 0 15 | fi 16 | 17 | DOWNLOADED_HASHES+=("$sha") 18 | 19 | local tgt="$mpoint/_factory/`ls -1 "$mpoint/_factory" | wc -l`.sh" 20 | pdebug "@tgt='$tgt'" 21 | 22 | cp "$path" "$tgt" 23 | 24 | local vers="`lbgrep "#:IMAGES " "\(.*\)" "$tgt" ||:`" 25 | pdebug "vers='$vers' version='$version'" 26 | if [ -n "$vers" ]; then 27 | [[ " $vers " =~ " $version " ]] || \ 28 | die "Factory script $loc requires one of versions '$vers', but \ 29 | this box is being created with version '$version'." 30 | fi 31 | 32 | local nextloc 33 | while read -r nextloc; do 34 | [ -z "$nextloc" ] && continue 35 | pdebug "nextloc='$nextloc'" 36 | load_factory "$mpoint" "$nextloc" "$version" 37 | done <<<"`lbgrep "#:DEPENDS " "\(.*\)" "$tgt" | tac`" 38 | } 39 | 40 | 41 | load_factory() { 42 | # load_factory mpoint loc version 43 | # Loads the factory at loc, as well as its dependencies into the given 44 | # mountpoint (storing as /_factory/0.sh, /_factory/1.sh, etc., in reverse 45 | # order of dependencies). Also verify that the factory is compatible with the 46 | # given image version. If the factory's hash is already in DOWNLOADED_HASHES, 47 | # then it will not be re-downloaded. 48 | 49 | local mpoint="$1" 50 | local loc="$2" 51 | local version="$3" 52 | 53 | export loc mpoint version 54 | 55 | pnote "Loading factory $loc..." 56 | with_location "$loc" get_factory "factory.sh" 57 | } 58 | 59 | 60 | FACTORY_ALL_CODE=`cat < "$mpoint/_factory/_all.sh" 76 | load_factory "$mpoint" "$loc" "$version" 77 | } 78 | -------------------------------------------------------------------------------- /lib/rootbox/image.sh: -------------------------------------------------------------------------------- 1 | # This Source Code Form is subject to the terms of the Mozilla Public 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this 3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | # https://wiki.alpinelinux.org/wiki/Installing_Alpine_Linux_in_a_chroot 6 | 7 | DEFAULT_MIRROR="http://nl.alpinelinux.org/alpine/" 8 | 9 | # Ommiting the backup superblocks significantly decreases the size of the 10 | # sparse image file. 11 | MKFS_OPTS="-t ext4 -F -Osparse_super,^has_journal -Enum_backup_sb=0" 12 | 13 | 14 | create_tmp_image() { 15 | # create_tmp_image 16 | # Creates a bare, empty ext4 sparse image file of 128GB. 17 | # (On-disk, it will be more like 13MB or so.) 18 | 19 | pnote "Creating bare image..." 20 | 21 | truncate -s 128G "$tpath" 22 | mke2fs $MKFS_OPTS "$tpath" 23 | } 24 | 25 | 26 | RESOLV_CONF=`cat < "$mpoint/etc/resolv.conf" 69 | echo "$REPOS" > "$mpoint/etc/apk/repositories" 70 | 71 | mv "$tpath" "$path" 72 | } 73 | 74 | 75 | image.add() { 76 | require_init 77 | require_root 78 | 79 | local packages verstr 80 | 81 | if [ "$slim" == "true" ]; then 82 | packages="alpine-base sudo" 83 | verstr="${version}-nodev" 84 | else 85 | packages="alpine-base alpine-sdk" 86 | verstr="$version" 87 | fi 88 | 89 | pdebug "@packages='$packages' verstr='$verstr'" 90 | 91 | local path="`image_path v$verstr`" 92 | local tpath="$path.tmp" 93 | [ -f "$path" ] && quit "Version $verstr was already downloaded" 94 | rm -rf "$path" "$tpath" 95 | 96 | export version mirror path packages 97 | 98 | create_tmp_image 99 | with_mount "$tpath" "in_tmp image_setup" 100 | 101 | pnote "Image creation successful!" 102 | } 103 | 104 | 105 | image.add::DESCR() { 106 | echo "downloads and installs the requested Alpine Linux image." 107 | } 108 | 109 | 110 | image.add::ARGS() { 111 | add_positional "version" "The Alpine Linux version to use" 112 | add_value_flag "m" "mirror" "The Alpine Linux mirror to use" "$DEFAULT_MIRROR" 113 | add_bool_flag "s" "slim" "Don't install the development packages. The \ 114 | resulting image can later be referenced via version-nodev; e.g., 3.6-nodev." 115 | } 116 | 117 | 118 | image.list() { 119 | require_init 120 | find "$IMAGES" -maxdepth 1 -name '*.img' -printf '%f\n' | \ 121 | sed 's/alpine-v\([0-9].[0-9]\(-nodev\)*\).img/\1/' 122 | } 123 | 124 | 125 | image.list::DESCR() { 126 | echo "lists all the installed Alpine Linux images." 127 | } 128 | 129 | 130 | image.list::ARGS() { 131 | : 132 | } 133 | 134 | 135 | image.remove() { 136 | require_init 137 | require_root 138 | 139 | local path="`image_path v$version`" 140 | [ -f "$path" ] || die "Version $version is not yet installed" 141 | [ -d "$path.mnt" ] && umount_if_mounted "$path.mnt" 142 | rm -f "$path.tmp" ||: 143 | rm -f "$path" 144 | 145 | pnote "Successfully removed image '$version'." 146 | } 147 | 148 | 149 | image.remove::DESCR() { 150 | echo "deletes the requested Alpine Linux image." 151 | } 152 | 153 | 154 | image.remove::ARGS() { 155 | add_positional "version" "The Alpine Linux version to remove" 156 | } 157 | 158 | 159 | register_command image.add 160 | register_command image.list 161 | register_command image.remove 162 | -------------------------------------------------------------------------------- /lib/rootbox/imgtools.sh: -------------------------------------------------------------------------------- 1 | # This Source Code Form is subject to the terms of the Mozilla Public 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this 3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | 6 | image_path() { 7 | # image_path version 8 | # Prints the full path to the image for the given Alpine Linux version. 9 | echo "$IMAGES/alpine-$1.img" 10 | } 11 | 12 | 13 | box_path() { 14 | # box_path box 15 | # Prints the full path to the box with the given name. 16 | echo "$BOXES/$1.box" 17 | } 18 | 19 | 20 | imgmount() { 21 | # imgmount image target 22 | # Loop-mounts the given ext4 image to the target directory. 23 | image="$1" 24 | target="$2" 25 | 26 | pdebug "@image,target: $*" 27 | 28 | mount -o loop -t ext4 "$image" "$target" 29 | } 30 | 31 | 32 | umount_if_mounted() { 33 | # umount_if_mounted dir 34 | # If the given directory is a loop or bind mount, then unmount it, and delete 35 | # the directory. 36 | mount | grep -q "`realpath "$1"`" && umount -l "$1" 37 | # XXX: Special-case /dev. 38 | [ "`basename $1`" != "dev" ] && rmdir "$1" ||: 39 | } 40 | 41 | 42 | with_mount_safecall() { 43 | imgmount "$img" "$mpoint" 44 | eval "$block" 45 | } 46 | 47 | 48 | with_mount_del() { 49 | local mpoint="$1" 50 | umount_if_mounted "$mpoint" 51 | } 52 | 53 | 54 | with_mount() { 55 | # with_mount img block 56 | # Mounts the given image to an unspecified mount point, and calls the given 57 | # blocks with the environment variable $mpoint set to the mount point. 58 | local img="$1" 59 | local block="$2" 60 | 61 | local mpoint="`mktempd $MNT`" 62 | pdebug "@img,block: $* [mpoint=$mpoint]" 63 | [ -d "$mpoint" ] && umount_if_mounted "$mpoint" 64 | mkdir -p "$mpoint" 65 | 66 | export block img mpoint 67 | 68 | safecall with_mount_safecall "with_mount_del `proper_quote "$mpoint"`" 69 | } 70 | 71 | 72 | with_bind_safecall() { 73 | eval "$block" 74 | } 75 | 76 | 77 | with_bind_del() { 78 | local path="$1" 79 | umount_if_mounted "$path" 80 | } 81 | 82 | 83 | with_bind() { 84 | # with_bind root spec block 85 | # Mounts the given bind mount spec to $root/$bind_target, with bind_target 86 | # being taken from the spec, then calls the block. 87 | local root="$1" 88 | local spec="$2" 89 | local block="$3" 90 | 91 | pdebug "@root,spec,block: $*" 92 | 93 | local bind target 94 | split "$spec" "///" bind target 95 | 96 | pdebug "bind='$bind' target='$target'" 97 | [ -n "$bind" ] && [ -n "$target" ] || die "Invalid bind mount spec '$spec'" 98 | 99 | local path="$root/$target" 100 | pdebug "path='$path'" 101 | 102 | mkdir -p "$path" 103 | sudo mount --bind "$bind" "$path" 104 | 105 | export block 106 | safecall with_bind_safecall "with_bind_del `proper_quote "$path"`" 107 | } 108 | 109 | 110 | with_binds_impl() { 111 | case "${#rest[@]}" in 112 | "0") internal "with_binds given no arguments" ;; 113 | "1") 114 | local block="${rest[0]}" 115 | pdebug "@1 arg: " 116 | eval "$block" ;; 117 | "2") 118 | local spec="${rest[0]}" 119 | local block="${rest[1]}" 120 | pdebug "@2 args: spec=$spec " 121 | with_bind "$root" "$spec" "$block" ;; 122 | *) 123 | local first_spec="${rest[0]}" 124 | rest=("${rest[@]:1}") 125 | export rest 126 | 127 | pdebug "@* args: first_spec=$first_spec ..." 128 | with_bind "$root" "$first_spec" with_binds_impl ;; 129 | esac 130 | } 131 | 132 | 133 | with_binds() { 134 | # with_binds root specs... block 135 | # Like with_bind, but takes multiple bind specs. 136 | 137 | local root="$1" 138 | shift 139 | local rest=("$@") 140 | 141 | export root rest 142 | with_binds_impl 143 | } 144 | 145 | 146 | with_binds_unset_ifs() { 147 | # Like with_binds, but unsets IFS first. 148 | unset IFS 149 | with_binds "$@" 150 | } 151 | 152 | 153 | in_chroot_enter() { 154 | [ -n "$failure" ] && onfail=die || onfail="ndie 1" 155 | 156 | chroot "$mpoint" \ 157 | /usr/bin/env -i \ 158 | TERM="$TERM" \ 159 | PATH=/usr/local/bin:/usr/sbin/usr/bin:/sbin:/bin \ 160 | su -lc "$command" "$user" || $onfail "$failure" 161 | } 162 | 163 | 164 | in_chroot() { 165 | # in_chroot mpoint user command failure-message bind-file other-binds... 166 | # Runs the given command inside the chroot at mpoint under the given user, 167 | # using bind-file as the bind spec file as other-binds as a sequence of more 168 | # bind specs. Failure-message, if not empty, will be printing upon command 169 | # failure. 170 | 171 | pdebug "@mpoint,user,command,failure,bind: $*" 172 | 173 | local mpoint="$1" 174 | local user="$2" 175 | local command="$3" 176 | local failure="$4" 177 | local bind_file="$5" 178 | shift 5 179 | 180 | export mpoint user command failure 181 | 182 | IFS=$'\n' 183 | with_binds_unset_ifs "$mpoint" `< "$bind_file"` "$@" \ 184 | /dev///dev /sys///sys /proc///proc \ 185 | in_chroot_enter 186 | } 187 | -------------------------------------------------------------------------------- /lib/rootbox/init.sh: -------------------------------------------------------------------------------- 1 | # This Source Code Form is subject to the terms of the Mozilla Public 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this 3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | INIT_SPEC="version" 6 | 7 | WORKSPACE_HOME="" 8 | 9 | if [ -n "$SUDO_USER" ]; then 10 | WORKSPACE_HOME="`getent passwd $SUDO_USER | cut -d: -f6`" 11 | fi 12 | 13 | [ -z "$WORKSPACE_HOME" ] && WORKSPACE_HOME="$HOME" ||: 14 | 15 | WORKSPACE="$WORKSPACE_HOME/.rootbox" 16 | 17 | IMAGES="$WORKSPACE/images" 18 | BOXES="$WORKSPACE/boxes" 19 | MNT="$WORKSPACE/mnt" 20 | TMP="$WORKSPACE/tmp" 21 | 22 | 23 | remove_old_directory() { 24 | # remove_old_directory 25 | # Deletes the old workspace directory. 26 | 27 | [ "$force" == "false" ] && die "Workspace directory already exists" 28 | require_root "Root access is required to remove the old workspace directory." 29 | 30 | if [ -L "$WORKSPACE" ]; then 31 | to_remove=`realpath "$WORKSPACE"` 32 | rm "$WORKSPACE" 33 | else 34 | to_remove="$WORKSPACE" 35 | fi 36 | 37 | for mnt in $MNT/*; do 38 | umount_if_mounted "$mnt" 39 | done 40 | 41 | [ "`ls -A "$mnt"`" ] && die "Failed to clear mount directory $MNT!!" 42 | 43 | rm -rf "$to_remove" 44 | } 45 | 46 | 47 | init() { 48 | [ -e "$WORKSPACE" ] && remove_old_directory 49 | 50 | pnote "Setting up workspace..." 51 | 52 | mkdir -p "$dir" 53 | require_sparse "$dir" 54 | [ "$dir" != "$WORKSPACE" ] && ln -sf "$dir" "$WORKSPACE" 55 | 56 | mkdir -p "$IMAGES" 57 | mkdir -p "$BOXES" 58 | mkdir -p "$MNT" 59 | mkdir -p "$TMP" 60 | 61 | sudo_perm_fix "$WORKSPACE" 62 | [ "$dir" != "$WORKSPACE" ] && sudo_perm_fix "$dir" ||: 63 | 64 | pnote "Rootbox has been initailized." 65 | } 66 | 67 | 68 | init::DESCR() { 69 | echo "initailizes rootbox using the given arguments." 70 | } 71 | 72 | 73 | init::ARGS() { 74 | add_value_flag "d" "dir" "The data directory" "$WORKSPACE" 75 | add_bool_flag "f" "force" "Force initialization, even if rootbox has already \ 76 | been initailized (WARNING: this will delete your previous data directory!)" 77 | } 78 | 79 | 80 | register_command init 81 | -------------------------------------------------------------------------------- /lib/rootbox/locations.sh: -------------------------------------------------------------------------------- 1 | # This Source Code Form is subject to the terms of the Mozilla Public 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this 3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | 6 | with_location_pure_git() { 7 | pnote "Downloading repo $repo with branch $branch..." 8 | 9 | git init 10 | git config core.sparseCheckout true 11 | echo "$path" > .git/info/sparse-checkout 12 | 13 | git pull "$repo" "$branch" --depth=1 || die "Invalid Git location $loc" 14 | [ -f "$path" ] || internal "$path not created via sparse checkout" 15 | 16 | path="`realpath "$PWD/$path"`" 17 | export path 18 | 19 | cd "$origdir" 20 | eval "$wl_block" 21 | } 22 | 23 | 24 | with_location_git_hosted() { 25 | mkdir -p "`dirname $path`" 26 | case "$kind" in 27 | github) url="https://raw.githubusercontent.com/$repo/$branch/$path" ;; 28 | gitlab) url="https://gitlab.com/$repo/raw/$branch/`basename $repo`/$path" ;; 29 | *) internal "Invalid Git kind $kind" ;; 30 | esac 31 | 32 | pdebug "url='$url'" 33 | 34 | pnote "Downloading from $url..." 35 | download "$url" "$path" || die "Invalid Git location $kind:$loc" 36 | 37 | local path="$PWD/$path" 38 | export path 39 | 40 | pdebug "path='$path'" 41 | 42 | cd "$origdir" 43 | eval "$wl_block" 44 | } 45 | 46 | 47 | with_location_url() { 48 | download "$loc" "_download" 49 | path="$PWD/_download" 50 | export path 51 | 52 | cd "$origdir" 53 | eval "$wl_block" 54 | } 55 | 56 | 57 | with_location() { 58 | # with_location loc block default 59 | # Grabs the file from the given location and calls the given block, 60 | # with the environment variable $path set to the path to the file location. 61 | # If the location is a directory, then default will be appended as the default 62 | # file name. 63 | 64 | pdebug "@loc,wl_block,default: $*" 65 | 66 | local loc="$1" 67 | local wl_block="$2" 68 | local default="$3" 69 | local kind 70 | 71 | local origdir="$PWD" 72 | 73 | case "$loc" in 74 | git:*) 75 | loc="${loc#*:}" 76 | if [[ "$loc" == *://* ]]; then 77 | kind=git 78 | else 79 | kind=github 80 | fi ;; 81 | github:*) loc="${loc#*:}"; kind=github ;; 82 | gitlab:*) loc="${loc#*:}"; kind=gitlab ;; 83 | url:*) loc="${loc#*:}"; kind=url ;; 84 | file:*) loc="${loc#*:}"; kind=file ;; 85 | *) kind=file ;; 86 | esac 87 | 88 | pdebug "kind='$kind' loc='$loc'" 89 | 90 | case "$kind" in 91 | git*) 92 | local repo 93 | local path 94 | local branch 95 | 96 | if [[ "$loc" =~ /// ]]; then 97 | split "$loc" "///" repo path 98 | else 99 | repo="$loc" 100 | path="$default" 101 | fi 102 | 103 | pdebug "repo='$repo' path='$path'" 104 | 105 | if [[ "$repo" == *@@* ]]; then 106 | split "$repo" "@@" repo branch 107 | else 108 | branch=master 109 | fi 110 | 111 | pdebug "branch='$branch'" 112 | 113 | export loc repo path branch wl_block origdir 114 | case "$kind" in 115 | git) in_tmp with_location_pure_git ;; 116 | *) in_tmp with_location_git_hosted ;; 117 | esac ;; 118 | url) 119 | [[ "$loc" == */ ]] && loc="$loc/$default" ||: 120 | 121 | pdebug "loc='$loc'" 122 | 123 | export loc wl_block origdir 124 | in_tmp with_location_url ;; 125 | file) 126 | [ -d "$loc" ] && loc="$loc/$default" ||: 127 | [ -e "$loc" ] || die "Invalid file location $loc" 128 | 129 | local path="`realpath "$loc"`" 130 | 131 | pdebug "path='$path'" 132 | 133 | export path 134 | eval "$wl_block" ;; 135 | *) internal "invalid location kind $kind" ;; 136 | esac 137 | } 138 | -------------------------------------------------------------------------------- /lib/rootbox/main.sh: -------------------------------------------------------------------------------- 1 | # This Source Code Form is subject to the terms of the Mozilla Public 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this 3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | 6 | check_exe() { 7 | # check_exe exe [message] 8 | # Makes sure the given executable exists, otherwise raises an error message. 9 | 10 | local msg=${2:-"$1 is required"} 11 | which "$1" 2>&1 >/dev/null || die "$msg" 12 | } 13 | 14 | 15 | integrity_check() { 16 | check_exe curl 17 | check_exe cp 18 | check_exe less 19 | check_exe truncate 20 | check_exe mke2fs 21 | check_exe mount 22 | check_exe mkdir 23 | check_exe find 24 | check_exe sed 25 | check_exe tar "tar is required to be able to create images and unpack boxes" 26 | check_exe bsdtar "bsdtar is required to be able to distribute boxes" 27 | check_exe chmod 28 | check_exe chown 29 | check_exe awk 30 | check_exe tail 31 | check_exe df 32 | check_exe tr 33 | check_exe grep 34 | check_exe sha1sum 35 | 36 | (( ${BASH_VERSINFO[0]} >= 4 )) || die "bash >= 4.3 is requried" 37 | (( ${BASH_VERSINFO[1]} >= 3 )) || die "bash >= 4.3 is requried" 38 | } 39 | 40 | 41 | main() { 42 | enable_errors 43 | [ "$ROOTBOX_DEBUG" == "1" ] && enable_debug ||: 44 | 45 | integrity_check 46 | 47 | if [[ -n "$1" && "$1" != -* ]]; then 48 | run_command "$@" 49 | else 50 | run_no_command "$@" 51 | fi 52 | } 53 | -------------------------------------------------------------------------------- /lib/rootbox/utils.sh: -------------------------------------------------------------------------------- 1 | # This Source Code Form is subject to the terms of the Mozilla Public 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this 3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | 6 | ALL_VERS="3.4 3.5 3.6 3.7" 7 | DEFAULT_VER="3.7" 8 | 9 | SETUP=setup.sh 10 | 11 | 12 | # COLORS, PRINTING, ERRORS, DEBUGGING 13 | 14 | _add_color() { 15 | eval "$1='$2'" 16 | eval "_ALL_COLORS+=' $1'" 17 | } 18 | 19 | _add_raw_color() { 20 | _add_color $1 "\\033[$2m" 21 | } 22 | 23 | _add_raw_color C_R 0 # Reset 24 | _add_raw_color C_B 1 # Bold 25 | _add_raw_color CF_RED 31 26 | _add_raw_color CF_GREEN 32 27 | _add_raw_color CF_BLUE 34 28 | _add_raw_color CF_PURPLE 35 29 | _add_raw_color CF_CYAN 36 30 | _add_raw_color CF_R 39 31 | 32 | _add_color C_NOTE "$C_B$CF_CYAN" 33 | _add_color C_WARN "$C_B$CF_PURPLE" 34 | _add_color C_ERROR "$C_B$CF_RED" 35 | 36 | 37 | print() { 38 | # print text... 39 | # Prints the given text, evaluating escape sequences. 40 | echo -ne "$@" 41 | } 42 | 43 | 44 | println() { 45 | # println text... 46 | # Like print, but resets formatting afterwards and adds a newline. 47 | echo -e "$@$C_R" 48 | } 49 | 50 | 51 | printfln() { 52 | # printfln format text... 53 | # Like printf, but adds a newline, but resets formatting afterwards and adds 54 | # a newline. 55 | fm="$1" 56 | shift 57 | printf "$fm$C_R\n" "$@" 58 | } 59 | 60 | 61 | pnote() { 62 | # pnote text... 63 | # Prints the given text, using the C_NOTE formatting. 64 | println "$C_NOTE$@" >&2 65 | } 66 | 67 | 68 | pwarn() { 69 | # pwarn text... 70 | # Prints the given text, using the C_WARN formatting. 71 | println "$C_WARN$@" >&2 72 | } 73 | 74 | 75 | perror() { 76 | # Prints the given text, using the C_ERROR formatting. 77 | println "$C_ERROR$@" >&2 78 | } 79 | 80 | 81 | ndie() { 82 | # ndie err-code [message...] 83 | # Exits with the given error code. If message was given, print it first. 84 | 85 | ret=$1 86 | shift 87 | [ "$#" -ne 0 ] && println "$@" >&2 88 | disable_errors 89 | exit $ret 90 | } 91 | 92 | 93 | quit() { 94 | # quit message... 95 | # Quits the program with return code 0 using C_NOTE formatting and the given 96 | # message. 97 | ndie 0 "$C_NOTE$@" 98 | } 99 | 100 | 101 | die() { 102 | # die message... 103 | # Quits the program with return code 1 using C_ERROR formatting and the given 104 | # message. 105 | ndie 1 "$C_ERROR$@" 106 | } 107 | 108 | 109 | internal() { 110 | # internal message... 111 | # Like die, but prefixes the text with INTERNAL ERROR. 112 | die "INTERNAL ERROR: $@" 113 | } 114 | 115 | 116 | pdebug() { 117 | # Used to print debug messages. 118 | [ "$ROOTBOX_DEBUG" == "1" ] || return 0 119 | printfln "$C_NOTE% 25s$CF_R :: %s" "${FUNCNAME[1]}" "$@" >&2 120 | } 121 | 122 | 123 | cmd_fail() { 124 | # Used to print debug messages. 125 | printfln "ERROR: $C_ERROR% 25s$CF_R :: %s" "${FUNCNAME[1]}" "$@" >&2 126 | exit 1 127 | } 128 | 129 | 130 | enable_debug() { 131 | # Enables verbose debugging. 132 | ROOTBOX_DEBUG=1 133 | trap 'pdebug "| $BASH_COMMAND"' DEBUG 134 | } 135 | 136 | 137 | enable_errors() { 138 | # Enables verbose error handlers. 139 | trap 'cmd_fail "$BASH_COMMAND exited with 1"' ERR 140 | } 141 | 142 | 143 | disable_errors() { 144 | # Disables verbose error handles (to allow a silent exit). 145 | trap - ERR 146 | } 147 | 148 | 149 | update_colors() { 150 | # update_colors [value=always|auto|never] 151 | # Updates the current color settings to the given value. 152 | 153 | local value="$1" 154 | 155 | case "$value" in 156 | always) ;; 157 | auto) [ -t 1 ] || disable_colors ;; 158 | never) disable_colors ;; 159 | *) internal "Invalid color value '$value'" ;; 160 | esac 161 | } 162 | 163 | 164 | disable_colors() { 165 | # Disables any colors from being printed. 166 | unset $_ALL_COLORS 167 | } 168 | 169 | 170 | # SAFECALLS (a hacked-on bash variant of try ... finally). 171 | 172 | 173 | # Trapchains are a sequence of commands to be run given a certain signal. 174 | # They're like traps, but they allow commands to be stacked. 175 | 176 | 177 | update_trapchain() { 178 | # update_trapchain sig 179 | # Updates the current trapchain. 180 | 181 | local sig="$1" 182 | 183 | chain=TRAPCHAIN_$sig 184 | chainlen=\${#$chain} 185 | chainvar=$chain[@] 186 | if [ `eval "echo $chainlen"` -eq 0 ]; then 187 | trap - $sig 188 | else 189 | trap "`join ' && ' "${!chainvar}"`" $sig 190 | fi 191 | } 192 | 193 | 194 | trapchain() { 195 | # trapchain func sig 196 | # Like 'trap func sig', but doesn't overwrite previous handlers in the 197 | # trapchain. 198 | 199 | local func="$1" 200 | local sig="$2" 201 | 202 | chain=TRAPCHAIN_$sig 203 | eval "$chain=('$func' \"\${$chain[@]}\")" 204 | update_trapchain $sig 205 | } 206 | 207 | 208 | trapchain_pop() { 209 | # trapchain_pop sig 210 | # Pops and executes the last trapchain handler for the given signal. 211 | 212 | local sig="$1" 213 | 214 | chain=TRAPCHAIN_$sig 215 | last=$chain[0] 216 | local block="${!last}" 217 | eval "$chain=(\"\${$chain[@]:1}\")" 218 | 219 | update_trapchain $sig 220 | eval "$block" 221 | } 222 | 223 | 224 | safecall() { 225 | # safecall safe [del] 226 | # Roughly equivalent to: 227 | # try: 228 | # safe 229 | # finally: 230 | # del 231 | # If del is not specified, then safe is set to ${safe}_safecall, and del is 232 | # set to ${del}_safecall. 233 | 234 | if [ -z "$2" ]; then 235 | safe="$1_safecall" 236 | del="$1_del" 237 | else 238 | safe="$1" 239 | del="$2" 240 | fi 241 | 242 | trapchain "$del" EXIT 243 | eval "$safe" 244 | trapchain_pop EXIT 245 | } 246 | 247 | 248 | in_tmp_safecall() { 249 | cd "$dir" 250 | eval "$block" 251 | } 252 | 253 | 254 | in_tmp_del() { 255 | local dir="$1" 256 | rm -rf "$dir" 257 | } 258 | 259 | 260 | in_tmp() { 261 | # in_tmp block 262 | # Runs the given block inside a temporary directory. 263 | 264 | local block="$1" 265 | local dir=`mktempd "$TMP"` 266 | export block dir 267 | 268 | safecall in_tmp_safecall "in_tmp_del `proper_quote "$dir"`" 269 | } 270 | 271 | 272 | # MISC. 273 | 274 | 275 | split() { 276 | # split str sep vars... 277 | # Splits the given string by sep, and saves the result into the variables 278 | # given. 279 | 280 | local str="$1" 281 | local sep="$2" 282 | shift 2 283 | 284 | local script="BEGIN { FS=\"$sep\"; } "'{ print $1"\n"$2; }' 285 | IFS=$'\n' read -r -d '' "$@" <<< "`echo "$str" | awk "$script"`" ||: 286 | } 287 | 288 | 289 | join() { 290 | # join sep [args...] 291 | # Joins the given arguments using the given separator, and prints the result 292 | # to stdout. 293 | local sep="$1" 294 | shift 295 | local ret="${*/#/$sep}" 296 | echo ${ret#$sep} 297 | } 298 | 299 | 300 | lbgrep() { 301 | # lbgrep before match file 302 | # Matches the text using a pattern equivalent to `(?<=before)match`, printing 303 | # only the matching part (i.e. not the before part). 304 | 305 | local before="$1" 306 | local match="$2" 307 | local file="$3" 308 | 309 | grep -o "$before$match" "$file" | sed "s/^$before//g" 310 | } 311 | 312 | 313 | require_sparse() { 314 | # require_sparse dir 315 | # Ensures that the given path is on a file system that supports sparse files. 316 | 317 | dir="`realpath "$1"`" 318 | local sparse_test="$dir/.sparse-test" 319 | rm -f "$sparse_test" 320 | truncate -s 4MB "$sparse_test" 321 | local size="`du "$sparse_test" | cut -f1`" 322 | pdebug "size='$size'" 323 | (( $size == 0 )) || \ 324 | die "Rootbox requires the workspace directory to be on a file system that \ 325 | supports sparse files." 326 | rm -f "$sparse_test" 327 | } 328 | 329 | 330 | require_init() { 331 | # require_init 332 | # Ensures that rootbox has been initialized. 333 | [ -e "$WORKSPACE" ] || die "Run 'rootbox init' to initialize rootbox." 334 | require_sparse "$WORKSPACE" 335 | } 336 | 337 | 338 | require_root() { 339 | # require_root 340 | # If the executor is not root, then aborts with an error message. 341 | [ "$EUID" -eq 0 ] || die ${1:-"This must be run as root!"} 342 | } 343 | 344 | 345 | download() { 346 | # download url target 347 | # Downloads the given url to the target file. If target is not specified, 348 | # then it will be deduced from the URL. Exits on download failure. 349 | 350 | local url="$1" 351 | local target="$2" 352 | pdebug "@url,target: $*" 353 | 354 | [ -z "$target" ] && target=-O || target="-o$target" 355 | 356 | local ret=0 357 | curl "$target" -fL "$url" || ret=1 358 | echo 359 | return $ret 360 | } 361 | 362 | 363 | mktempd() { 364 | # mktempd dir 365 | # Like mktemp -d, but creates the temporary directory inside the given 366 | # directory. 367 | local dir="$1" 368 | mktemp -d "$dir/XXXXXXXXXX" 369 | } 370 | 371 | 372 | sudo_perm_fix() { 373 | # sudo_perm_fix path [perm=775] 374 | # If someone was executing this as sudo, then change the permissions of the 375 | # created files to the underlying user. 376 | local path="$1" 377 | local perm="${2:-775}" 378 | 379 | [ -n "$SUDO_USER" ] || return 0 380 | 381 | if [ -d "$path" ]; then 382 | chown -R "$SUDO_USER:$SUDO_USER" "$path" 383 | chmod -R "$perm" "$path" 384 | else 385 | chown "$SUDO_USER:$SUDO_USER" "$path" 386 | chmod "$perm" "$path" 387 | fi 388 | } 389 | 390 | 391 | proper_quote() { 392 | # proper_quote str 393 | # Escape and quote the string with single quotes. 394 | 395 | local str="$1" 396 | echo "$str" | sed "s/'/'\"'\"'/g;s/^/'/;s/$/'/" 397 | } 398 | -------------------------------------------------------------------------------- /link.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | cd "`dirname $0`" 4 | prefix=${1:-/usr/local} 5 | 6 | set -ex 7 | 8 | rm -rf "$prefix/lib/rootbox/cmdarg" 9 | 10 | ln -sf "$PWD/bin/rootbox" "$prefix/bin/rootbox" 11 | ln -sf "$PWD/lib/rootbox" "$prefix/lib/rootbox" 12 | -------------------------------------------------------------------------------- /remote-install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | set -e 8 | 9 | URL="https://github.com/project-rootbox/rootbox/archive/master.tar.gz" 10 | 11 | set -x 12 | 13 | cd ${TMP:-/tmp} 14 | curl -fL "$URL" -o rootbox.tar.gz 15 | tar xvf rootbox.tar.gz 16 | 17 | cd rootbox-master 18 | sh install.sh "$1" 19 | -------------------------------------------------------------------------------- /update-argparser: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd "`dirname $0`" 4 | . lib/rootbox/utils.sh 5 | 6 | mkdir -p lib/rootbox/argparser 7 | cd lib/rootbox/argparser 8 | 9 | ROOT=https://raw.githubusercontent.com/kirbyfan64/bash-argparser/fix_dot 10 | 11 | download $ROOT/argparser 12 | download $ROOT/LICENSE.md LICENSE 13 | # Grabbed from the argparser file itself 14 | sed -i 's/2016/2016 Ekeyme Mo /' LICENSE 15 | -------------------------------------------------------------------------------- /update-licenses: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cat > LICENSE-THIRD-PARTY <