├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ ├── question-template.md │ └── refactoring-template.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ └── ccpp.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── Makefile ├── README.md ├── configure ├── configure_darwin ├── configure_others ├── fs-make-demo.png ├── grammar.dot ├── graphviz.svg ├── includes ├── errors.hpp ├── node.hpp ├── script_generator.hpp └── util.hpp ├── logo.svg ├── src ├── errors.c ├── grammar.y ├── lexer.l ├── main.cpp ├── node.cpp ├── script_generator.cpp └── util.cpp └── tests ├── .gitignore └── sample.json /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [ RaisinTen ] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: raisinten # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 12 | polar: # Replace with a single Polar username 13 | buy_me_a_coffee: raisinten # Replace with a single Buy Me a Coffee username 14 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 15 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question-template.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Question template 3 | about: Ask a question 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/refactoring-template.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Refactoring template 3 | about: Suggest subtle changes to improve the design 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change. 4 | 5 | Fixes # (issue) 6 | 7 | ## Type of change 8 | 9 | Please delete options that are not relevant. 10 | 11 | - [ ] new features 12 | - [ ] bug-fixes 13 | - [ ] code refactoring 14 | - [ ] writing tests 15 | - [ ] documentation improvements 16 | 17 | # How Has This Been Tested? 18 | 19 | Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration 20 | 21 | - [ ] Test A 22 | - [ ] Test B 23 | 24 | **Test Configuration**: 25 | * OS: 26 | * Hardware: 27 | 28 | # Checklist: 29 | 30 | - [ ] My code follows the style guidelines of this project 31 | - [ ] I have performed a self-review of my own code 32 | - [ ] I have commented my code, particularly in hard-to-understand areas 33 | - [ ] I have made corresponding changes to the documentation 34 | - [ ] My changes generate no new warnings 35 | - [ ] I have added tests that prove my fix is effective or that my feature works 36 | - [ ] New and existing unit tests pass locally with my changes 37 | -------------------------------------------------------------------------------- /.github/workflows/ccpp.yml: -------------------------------------------------------------------------------- 1 | name: C/C++ CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - '*' 7 | pull_request: 8 | branches: 9 | - '*' 10 | 11 | jobs: 12 | build: 13 | runs-on: ${{ matrix.os }} 14 | strategy: 15 | matrix: 16 | os: [ ubuntu-latest, macos-latest ] 17 | 18 | steps: 19 | - uses: actions/checkout@v2 20 | - name: Configure 21 | run: ./configure 22 | - name: Make 23 | run: make 24 | - name: Install 25 | run: make install 26 | - name: Running on sample.json 27 | run: | 28 | cd ./tests 29 | fs-make sample.json out-sample.sh 30 | . ./out-sample.sh 31 | tree -a project 32 | - name: Clean 33 | run: make clean 34 | - name: Uninstall 35 | run: make uninstall 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | fs-make 2 | *.o 3 | *.d 4 | *.yy.* 5 | *.tab.* 6 | tools 7 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at raisinten@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Note that, we have a [code of conduct](CODE_OF_CONDUCT.md). So, please don't forget to go through it before contributing to this project. If you have any question or want to contribute something to this project, please [create an issue](https://github.com/RaisinTen/fs-make/issues/new/choose) first. 4 | 5 | Fork the repo and clone it locally with: 6 | ```sh 7 | $ git clone https://github.com//fs-make 8 | $ cd fs-make 9 | ``` 10 | 11 | Build it like this: 12 | ```sh 13 | $ ./configure 14 | $ make 15 | $ make install 16 | ``` 17 | 18 | Make changes till you are satisfied and finally [make a pull request](https://github.com/RaisinTen/fs-make/pulls). 19 | 20 | Here are the types of contributions that are appreciated: 21 | * new features 22 | * bug-fixes 23 | * code refactoring 24 | * writing tests 25 | * documentation improvements 26 | 27 | ## Development Process 28 | 29 | ### Flex 30 | 31 | This project uses [Flex](www.gnu.org/software/flex/) which is a scanner generator for lexing in [C and C++](https://gcc.gnu.org/). The accepted tokens are described in [`lexer.l`](src/lexer.l) using regular expressions. We then use `flex` to generate a scanner in C (`lex.yy.c`) which breaks down the contents of the input file into the tokens we defined. 32 | 33 | ### Bison 34 | 35 | This project also uses [GNU Bison](www.gnu.org/software/bison/) which is used to generate a parser. The production rules of the grammar is defined in [`grammar.y`](src/grammar.y). We use `bison` to generate a parser in C, `grammar.tab.c` to group the tokens into various components according to the defined rules. These components together will make up what is called the [abstract syntax tree (AST)](https://en.wikipedia.org/wiki/Abstract_syntax_tree). 36 | 37 | ### Tree 38 | 39 | The [`tree`](https://en.wikipedia.org/wiki/Tree_(command)) command is a recursive directory listing command. After the folder structure is made, it helps in visualizing it. 40 | 41 | ### Build 42 | 43 | First, the [`configure script`](configure) installs all the dependencies. It generates a file, `tools` which stores references to the requirements. It is used by the `Makefile`. 44 | 45 | The [`Makefile`](Makefile) is used to build `fs-make` in the current directory by using the `make` command. It uses Automatic-Dependency Generation so that contributing by adding new files is completely hassle-free. No need to separately add recipes for newly added files. It automatically detects the new files and the files it depeneds on. 46 | 47 | The executable `fs-make` is installed to `/usr/local/bin` using the `make install` command. 48 | 49 | Similarly, the executable can be uninstalled via the `make uninstall` command. 50 | 51 | The build directory is cleaned using `make clean`. 52 | 53 | ### Usage 54 | 55 | After making the input file describing the folder structure, compile it into the bash script for making it using: 56 | ```sh 57 | $ fs-make infile outfile 58 | ``` 59 | 60 | ### CI/CD 61 | 62 | GitHub Actions is used to automate the software workflow. 63 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Darshan Sen 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 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # meta 2 | 3 | TARGET := fs-make 4 | VERSION := 1.0.0 5 | 6 | # dirs 7 | 8 | # in termux, PREFIX points to /data/data/com.termux/files/usr/ 9 | # else it is empty 10 | PREFIX := $(shell echo ${PREFIX}) 11 | # by default installs to /usr/local/bin 12 | BIN := /usr/local/bin 13 | ifneq ($(PREFIX),) 14 | # in linux-android, ${PREFIX}/usr/local/bin 15 | # doesn't exist; so instead install it to 16 | # ${PREFIX}/usr/bin/ 17 | BIN := $(PREFIX)/../usr/bin 18 | endif 19 | 20 | INCLUDES := ./includes 21 | SRC := ./src 22 | 23 | # files 24 | 25 | YFILES := $(wildcard $(SRC)/*.y) 26 | 27 | TABFILES := \ 28 | $(YFILES:.y=.tab.c) \ 29 | $(YFILES:.y=.tab.h) 30 | TABFILES := $(patsubst $(SRC)/%, %, $(TABFILES)) 31 | 32 | YYFILES := lex.yy.c 33 | 34 | CFILES := \ 35 | $(wildcard $(SRC)/*.c) \ 36 | $(YFILES:.y=.tab.c) \ 37 | $(YYFILES) 38 | 39 | CPPFILES := $(wildcard $(SRC)/*.cpp) 40 | 41 | OBJECTS := \ 42 | $(CFILES:.c=.o) \ 43 | $(CPPFILES:.cpp=.o) 44 | OBJECTS := $(patsubst $(SRC)/%, %, $(OBJECTS)) 45 | 46 | DEPS := $(OBJECTS:.o=.d) 47 | 48 | .PHONY : all clean install uninstall 49 | 50 | # colours 51 | 52 | PRE := \033[ 53 | 54 | NC := $(PRE)0m 55 | GREEN := $(PRE)1;32m 56 | YELLOW := $(PRE)1;33m 57 | BLUE := $(PRE)1;36m 58 | RED := $(PRE)1;31m 59 | 60 | # tools 61 | 62 | # generated by the configure script 63 | -include tools 64 | 65 | RM := rm 66 | IN := install 67 | 68 | # flags 69 | 70 | DEFINES := -D TARGET=\"$(TARGET)\" -D VERSION=\"$(VERSION)\" 71 | 72 | CFLAGS := $(DEFINES) -I $(INCLUDES) -Wall -Wextra -Wpedantic -g -MMD -MP -c 73 | FLEXFLAGS := 74 | BISONFLAGS := -d 75 | RMFLAGS := -f 76 | INFLAGS := 77 | TREEFLAGS := -a 78 | 79 | # recipes 80 | 81 | all: $(TARGET) 82 | @echo "$(GREEN)Build complete!$(NC)\n" 83 | @echo "$(BLUE)Now, install $(YELLOW)$(TARGET)$(BLUE) with: $(YELLOW)make install$(NC)" 84 | 85 | uninstall: 86 | @echo "$(RED)... uninstalling $(YELLOW)$(TARGET)$(RED) ...$(NC)\n" 87 | $(SUDO) $(RM) $(RMFLAGS) $(BIN)/$(TARGET) 88 | @echo "" 89 | @echo "$(RED)Uninstallation complete. :($(NC)\n" 90 | @echo "$(BLUE)Now, install $(YELLOW)$(TARGET)$(BLUE) with: $(YELLOW)make install$(NC)" 91 | 92 | install: 93 | @echo "$(GREEN)... installing $(YELLOW)$(TARGET)$(GREEN) ...$(NC)\n" 94 | $(SUDO) $(IN) $(INFLAGS) $(TARGET) $(BIN)/$(TARGET) 95 | @echo "" 96 | @echo "$(GREEN)Installation complete!$(NC)\n" 97 | @echo "$(BLUE)Now, run $(YELLOW)$(TARGET)$(BLUE) with: $(YELLOW)$(TARGET)$(NC)" 98 | 99 | $(TARGET): $(OBJECTS) 100 | @echo "$(BLUE)... making $(YELLOW)$@ $(BLUE)...$(NC)\n" 101 | $(CPP) -o $(TARGET) $^ 102 | @echo "" 103 | 104 | %.o: $(SRC)/%.c 105 | @echo "$(BLUE)... making $(YELLOW)$@ $(BLUE)...$(NC)\n" 106 | $(CC) $(CFLAGS) $< 107 | @echo "" 108 | 109 | %.o: %.c 110 | @echo "$(BLUE)... making $(YELLOW)$@ $(BLUE)...$(NC)\n" 111 | $(CC) $(CFLAGS) $< 112 | @echo "" 113 | 114 | %.o: $(SRC)/%.cpp 115 | @echo "$(BLUE)... making $(YELLOW)$@ $(BLUE)...$(NC)\n" 116 | $(CPP) $(CFLAGS) $< 117 | @echo "" 118 | 119 | lex.yy.c: $(SRC)/lexer.l 120 | @echo "$(BLUE)... making $(YELLOW)$@ $(BLUE)...$(NC)\n" 121 | $(FLEX) $(FLEXFLAGS) $< 122 | @echo "" 123 | 124 | grammar.tab.c grammar.tab.h: $(SRC)/grammar.y 125 | @echo "$(BLUE)... making $(YELLOW)grammar.tab.c $(BLUE)and $(YELLOW)grammar.tab.h $(BLUE)...$(NC)\n" 126 | $(BISON) $(BISONFLAGS) $< 127 | @echo "" 128 | 129 | clean: 130 | @echo "$(RED)... cleaning up ...$(NC)\n" 131 | $(RM) $(RMFLAGS) $(OBJECTS) $(DEPS) $(YYFILES) $(TABFILES) $(TARGET) 132 | @echo "" 133 | @echo "$(RED)Cleaning complete!$(NC)\n" 134 | @echo "$(BLUE)Now, build $(YELLOW)$(TARGET)$(BLUE) with: $(YELLOW)make$(NC)" 135 | 136 | # from the generated dependency files 137 | 138 | -include $(DEPS) 139 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | fs-make logo 5 |

6 |

7 | 8 | CI 10 | 11 | LICENSE MIT 13 |

14 | 15 | --- 16 | 17 | Make your folder structure from its description with **`fs-make (folder structure make)`**! 18 | 19 | ![example](https://github.com/RaisinTen/fs-make/raw/master/fs-make-demo.png) 20 | 21 | ## Syntax 22 | 23 | Note that this is **not JSON** but a similar data-format. Here's what the Abstract Syntax Tree looks like. 24 | 25 | * Files are represented by the file name placed between a pair of double quotes (`"`): 26 | ``` 27 | "file-name" 28 | ``` 29 | 30 | * Directories are represented by the directory name in double quotes (`"`) followed by a colon (`:`) and a comma-separated (`,`) set of file and directory representations between a pair of braces (`{}`): 31 | ``` 32 | "src": { 33 | "main.cpp", 34 | "CMakeLists.txt", 35 | "build": { 36 | "Makefile", 37 | "boink" 38 | }, 39 | "includes": { 40 | } 41 | } 42 | ``` 43 | When a directory contains only a single entity, the braces aren't necessary: 44 | ``` 45 | "i have one file": "one file" 46 | ``` 47 | 48 | * The input file content can be like any of these: 49 | * a file 50 | ``` 51 | "just a file" 52 | ``` 53 | * a directory 54 | ``` 55 | "lonely directory": { 56 | "stuff" 57 | } 58 | ``` 59 | * an unnamed set of entities like this: 60 | ``` 61 | {} 62 | ``` 63 | 64 | Here's what the Abstract Syntax Tree looks like: 65 | 66 | ![grammar graph](https://github.com/RaisinTen/fs-make/raw/master/graphviz.svg) 67 | 68 | ## Getting Started 69 | 70 | ### Dependencies 71 | 72 | The dependencies are: 73 | * `gcc` 74 | * `g++` 75 | * `flex` 76 | * `bison` 77 | * `tree` 78 | 79 | No manual installation is required. The `configure` script installs the dependencies using a package manager. If you are using: 80 | 81 | #### macOS 82 | 83 | Make sure that you have [Homebrew](https://brew.sh/) installed. 84 | 85 | #### Windows 86 | 87 | Install the [Windows Subsystem for Linux](https://docs.microsoft.com/en-us/windows/wsl/install-win10) or [Cygwin](https://cygwin.com/install.html) and make sure that you have [APT](https://salsa.debian.org/apt-team/apt) installed. 88 | 89 | #### Linux 90 | 91 | Make sure that you have [APT](https://salsa.debian.org/apt-team/apt) installed. 92 | 93 | ### Building 94 | 95 | Clone the repo with: 96 | ```sh 97 | $ git clone https://github.com/RaisinTen/fs-make.git 98 | $ cd fs-make 99 | ``` 100 | 101 | Update your package lists and build **`fs-make`** using: 102 | ```sh 103 | $ ./configure 104 | $ make 105 | $ make install 106 | ``` 107 | 108 | ## Try it out 109 | 110 | 1. Create a file describing the structure of your folder. 111 | 2. Use `fs-make` to build the script: 112 | ```sh 113 | $ fs-make 114 | ``` 115 | 3. Run the script to generate the folder structure: 116 | ```sh 117 | $ . 118 | ``` 119 | 4. To view it: 120 | ```sh 121 | $ tree -a 122 | ``` 123 | 124 | ## Code of Conduct 125 | 126 | Please refer to the [code of conduct](CODE_OF_CONDUCT.md) for the rules for interacting with this project. 127 | 128 | ## Contributing 129 | 130 | Please go through the [contributing](CONTRIBUTING.md) documentation to contribute to this project. 131 | 132 | ## License 133 | 134 | This project is licensed under the [MIT License](LICENSE). 135 | -------------------------------------------------------------------------------- /configure: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # colours 4 | 5 | export PRE="\033[" 6 | 7 | export NC="${PRE}0m" 8 | export GREEN="${PRE}1;32m" 9 | export YELLOW="${PRE}1;33m" 10 | export BLUE="${PRE}1;36m" 11 | export RED="${PRE}1;31m" 12 | 13 | # sudo 14 | 15 | export SUDO=$(command -v sudo) 16 | 17 | if [[ "$OSTYPE" == "darwin"* ]] 18 | then 19 | # this is for macOS users 20 | 21 | source configure_darwin 22 | else 23 | # this is for others 24 | 25 | source configure_others 26 | fi 27 | 28 | # writing paths of requirements to tools 29 | 30 | echo -e "\ 31 | CC := ${CC}\n\ 32 | CPP := ${CPP}\n\ 33 | FLEX := ${FLEX}\n\ 34 | BISON := ${BISON}\n\ 35 | TREE := ${TREE}\n\ 36 | SUDO := ${SUDO}\n\ 37 | " > tools 38 | 39 | # summary 40 | 41 | echo -e "${GREEN}Summary${NC}" 42 | echo -e "${YELLOW}CC${BLUE} found at: ${YELLOW}${CC}${NC}" 43 | echo -e "${YELLOW}CPP${BLUE} found at: ${YELLOW}${CPP}${NC}" 44 | echo -e "${YELLOW}FLEX${BLUE} found at: ${YELLOW}${FLEX}${NC}" 45 | echo -e "${YELLOW}BISON${BLUE} found at: ${YELLOW}${BISON}${NC}" 46 | echo -e "${YELLOW}TREE${BLUE} found at: ${YELLOW}${TREE}${NC}" 47 | echo -e "${YELLOW}SUDO${BLUE} found at: ${YELLOW}${SUDO}${NC}\n" 48 | 49 | # end of configuration 50 | 51 | echo -e "${GREEN}Configuration complete!${NC}\n" 52 | echo -e "${BLUE}Now, build ${YELLOW}fs-make ${BLUE}with: ${YELLOW}make${NC}" 53 | -------------------------------------------------------------------------------- /configure_darwin: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | PACKAGE_MANAGER="brew" 4 | 5 | # gcc-8 6 | 7 | command -v gcc-8 >/dev/null 2>&1 || { 8 | # not found, installing 9 | 10 | echo -e >&2 "${YELLOW}gcc-8 ${RED}is required but not found${NC}" 11 | echo -e >&2 "${BLUE}... installing ${YELLOW}gcc-8${BLUE} ... ${NC}\n" 12 | ${PACKAGE_MANAGER} install gcc 13 | echo -e >&2 "" 14 | } 15 | 16 | # g++-8 17 | 18 | command -v g++-8 >/dev/null 2>&1 || { 19 | # not found, installing 20 | 21 | echo -e >&2 "${YELLOW}g++-8 ${RED}is required but not found${NC}" 22 | echo -e >&2 "${BLUE}... installing ${YELLOW}g++-8${BLUE} ... ${NC}\n" 23 | ${PACKAGE_MANAGER} install gcc 24 | echo -e >&2 "" 25 | } 26 | 27 | # flex 28 | 29 | command -v flex >/dev/null 2>&1 || { 30 | # not found, installing 31 | 32 | echo -e >&2 "${YELLOW}flex ${RED}is required but not found${NC}" 33 | echo -e >&2 "${BLUE}... installing ${YELLOW}flex${BLUE} ... ${NC}\n" 34 | ${PACKAGE_MANAGER} install flex 35 | echo -e >&2 "" 36 | } 37 | 38 | # bison 39 | 40 | command -v bison >/dev/null 2>&1 || { 41 | # not found, installing 42 | 43 | echo -e >&2 "${YELLOW}bison ${RED}is required but not found${NC}" 44 | echo -e >&2 "${BLUE}... installing ${YELLOW}bison${BLUE} ... ${NC}\n" 45 | ${PACKAGE_MANAGER} install bison 46 | echo -e >&2 "" 47 | } 48 | 49 | # tree 50 | 51 | command -v tree >/dev/null 2>&1 || { 52 | # not found, installing 53 | 54 | echo -e >&2 "${YELLOW}tree ${RED}is required but not found${NC}" 55 | echo -e >&2 "${BLUE}... installing ${YELLOW}tree${BLUE} ... ${NC}\n" 56 | ${PACKAGE_MANAGER} install tree 57 | echo -e >&2 "" 58 | } 59 | 60 | # exporting paths 61 | 62 | export CC=$(command -v gcc-8) 63 | export CPP=$(command -v g++-8) 64 | export FLEX=$(command -v flex) 65 | export BISON=$(command -v bison) 66 | export TREE=$(command -v tree) 67 | -------------------------------------------------------------------------------- /configure_others: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | PACKAGE_MANAGER="apt" 4 | 5 | # gcc 6 | 7 | command -v gcc >/dev/null 2>&1 || { 8 | # not found, installing 9 | 10 | echo -e >&2 "${YELLOW}gcc ${RED}is required but not found${NC}" 11 | echo -e >&2 "${BLUE}... installing ${YELLOW}gcc${BLUE} ... ${NC}\n" 12 | ${SUDO} ${PACKAGE_MANAGER} install build-essential 13 | echo -e >&2 "" 14 | } 15 | 16 | # g++ 17 | 18 | command -v g++ >/dev/null 2>&1 || { 19 | # not found, installing 20 | 21 | echo -e >&2 "${YELLOW}g++ ${RED}is required but not found${NC}" 22 | echo -e >&2 "${BLUE}... installing ${YELLOW}g++${BLUE} ... ${NC}\n" 23 | echo >&2 "g++ is being installed" 24 | ${SUDO} ${PACKAGE_MANAGER} install build-essential 25 | echo -e >&2 "" 26 | } 27 | 28 | # flex 29 | 30 | command -v flex >/dev/null 2>&1 || { 31 | # not found, installing 32 | 33 | echo -e >&2 "${YELLOW}flex ${RED}is required but not found${NC}" 34 | echo -e >&2 "${BLUE}... installing ${YELLOW}flex${BLUE} ... ${NC}\n" 35 | ${SUDO} ${PACKAGE_MANAGER} install flex 36 | echo -e >&2 "" 37 | } 38 | 39 | # bison 40 | 41 | command -v bison >/dev/null 2>&1 || { 42 | # not found, installing 43 | 44 | echo -e >&2 "${YELLOW}bison ${RED}is required but not found${NC}" 45 | echo -e >&2 "${BLUE}... installing ${YELLOW}bison${BLUE} ... ${NC}\n" 46 | ${SUDO} ${PACKAGE_MANAGER} install bison 47 | echo -e >&2 "" 48 | } 49 | 50 | # tree 51 | 52 | command -v tree >/dev/null 2>&1 || { 53 | # not found, installing 54 | 55 | echo -e >&2 "${YELLOW}tree ${RED}is required but not found${NC}" 56 | echo -e >&2 "${BLUE}... installing ${YELLOW}tree${BLUE} ... ${NC}\n" 57 | ${SUDO} ${PACKAGE_MANAGER} install tree 58 | echo -e >&2 "" 59 | } 60 | 61 | # exporting paths 62 | 63 | export CC=$(command -v gcc) 64 | export CPP=$(command -v g++) 65 | export FLEX=$(command -v flex) 66 | export BISON=$(command -v bison) 67 | export TREE=$(command -v tree) 68 | -------------------------------------------------------------------------------- /fs-make-demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaisinTen/fs-make/71ebc60b0ce9a42fe04086aa47822a34b06fe976/fs-make-demo.png -------------------------------------------------------------------------------- /grammar.dot: -------------------------------------------------------------------------------- 1 | // Generated by GNU Bison 3.0.4. 2 | // Report bugs to . 3 | // Home page: . 4 | 5 | digraph "grammar.y" 6 | { 7 | node [fontname = courier, shape = box, colorscheme = paired6] 8 | edge [fontname = courier] 9 | 10 | 0 [label="State 0\n\l 0 $accept: . all $end\l"] 11 | 0 -> 1 [style=solid label="LBRACE"] 12 | 0 -> 2 [style=solid label="STR"] 13 | 0 -> 3 [style=dashed label="all"] 14 | 0 -> 4 [style=dashed label="dir"] 15 | 0 -> 5 [style=dashed label="file"] 16 | 1 [label="State 1\n\l 2 dir: LBRACE . files RBRACE\l"] 17 | 1 -> 2 [style=solid label="STR"] 18 | 1 -> 6 [style=dashed label="files"] 19 | 1 -> 7 [style=dashed label="collection"] 20 | 1 -> 8 [style=dashed label="file"] 21 | 1 -> "1R4" [style=solid] 22 | "1R4" [label="R4", fillcolor=3, shape=diamond, style=filled] 23 | 2 [label="State 2\n\l 8 file: STR .\l 9 | STR . COLON dir\l"] 24 | 2 -> 9 [style=solid label="COLON"] 25 | 2 -> "2R8" [style=solid] 26 | "2R8" [label="R8", fillcolor=3, shape=diamond, style=filled] 27 | 3 [label="State 3\n\l 0 $accept: all . $end\l"] 28 | 3 -> 10 [style=solid label="$end"] 29 | 4 [label="State 4\n\l 1 all: dir .\l"] 30 | 4 -> "4R1" [style=solid] 31 | "4R1" [label="R1", fillcolor=3, shape=diamond, style=filled] 32 | 5 [label="State 5\n\l 3 dir: file .\l"] 33 | 5 -> "5R3" [style=solid] 34 | "5R3" [label="R3", fillcolor=3, shape=diamond, style=filled] 35 | 6 [label="State 6\n\l 2 dir: LBRACE files . RBRACE\l"] 36 | 6 -> 11 [style=solid label="RBRACE"] 37 | 7 [label="State 7\n\l 5 files: collection .\l"] 38 | 7 -> "7R5" [style=solid] 39 | "7R5" [label="R5", fillcolor=3, shape=diamond, style=filled] 40 | 8 [label="State 8\n\l 6 collection: file .\l 7 | file . COMMA collection\l"] 41 | 8 -> 12 [style=solid label="COMMA"] 42 | 8 -> "8R6" [style=solid] 43 | "8R6" [label="R6", fillcolor=3, shape=diamond, style=filled] 44 | 9 [label="State 9\n\l 9 file: STR COLON . dir\l"] 45 | 9 -> 1 [style=solid label="LBRACE"] 46 | 9 -> 2 [style=solid label="STR"] 47 | 9 -> 13 [style=dashed label="dir"] 48 | 9 -> 5 [style=dashed label="file"] 49 | 10 [label="State 10\n\l 0 $accept: all $end .\l"] 50 | 10 -> "10R0" [style=solid] 51 | "10R0" [label="Acc", fillcolor=1, shape=diamond, style=filled] 52 | 11 [label="State 11\n\l 2 dir: LBRACE files RBRACE .\l"] 53 | 11 -> "11R2" [style=solid] 54 | "11R2" [label="R2", fillcolor=3, shape=diamond, style=filled] 55 | 12 [label="State 12\n\l 7 collection: file COMMA . collection\l"] 56 | 12 -> 2 [style=solid label="STR"] 57 | 12 -> 14 [style=dashed label="collection"] 58 | 12 -> 8 [style=dashed label="file"] 59 | 13 [label="State 13\n\l 9 file: STR COLON dir .\l"] 60 | 13 -> "13R9" [style=solid] 61 | "13R9" [label="R9", fillcolor=3, shape=diamond, style=filled] 62 | 14 [label="State 14\n\l 7 collection: file COMMA collection .\l"] 63 | 14 -> "14R7" [style=solid] 64 | "14R7" [label="R7", fillcolor=3, shape=diamond, style=filled] 65 | } 66 | -------------------------------------------------------------------------------- /graphviz.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | grammar.y 9 | 10 | 11 | 12 | 0 13 | 14 | State 0 15 |  0 $accept: . all $end 16 | 17 | 18 | 19 | 1 20 | 21 | State 1 22 |  2 dir: LBRACE . files RBRACE 23 | 24 | 25 | 26 | 0->1 27 | 28 | 29 | LBRACE 30 | 31 | 32 | 33 | 2 34 | 35 | State 2 36 |  8 file: STR . 37 |  9     | STR . COLON dir 38 | 39 | 40 | 41 | 0->2 42 | 43 | 44 | STR 45 | 46 | 47 | 48 | 3 49 | 50 | State 3 51 |  0 $accept: all . $end 52 | 53 | 54 | 55 | 0->3 56 | 57 | 58 | all 59 | 60 | 61 | 62 | 4 63 | 64 | State 4 65 |  1 all: dir . 66 | 67 | 68 | 69 | 0->4 70 | 71 | 72 | dir 73 | 74 | 75 | 76 | 5 77 | 78 | State 5 79 |  3 dir: file . 80 | 81 | 82 | 83 | 0->5 84 | 85 | 86 | file 87 | 88 | 89 | 90 | 1->2 91 | 92 | 93 | STR 94 | 95 | 96 | 97 | 6 98 | 99 | State 6 100 |  2 dir: LBRACE files . RBRACE 101 | 102 | 103 | 104 | 1->6 105 | 106 | 107 | files 108 | 109 | 110 | 111 | 7 112 | 113 | State 7 114 |  5 files: collection . 115 | 116 | 117 | 118 | 1->7 119 | 120 | 121 | collection 122 | 123 | 124 | 125 | 8 126 | 127 | State 8 128 |  6 collection: file . 129 |  7           | file . COMMA collection 130 | 131 | 132 | 133 | 1->8 134 | 135 | 136 | file 137 | 138 | 139 | 140 | 1R4 141 | 142 | R4 143 | 144 | 145 | 146 | 1->1R4 147 | 148 | 149 | 150 | 151 | 152 | 9 153 | 154 | State 9 155 |  9 file: STR COLON . dir 156 | 157 | 158 | 159 | 2->9 160 | 161 | 162 | COLON 163 | 164 | 165 | 166 | 2R8 167 | 168 | R8 169 | 170 | 171 | 172 | 2->2R8 173 | 174 | 175 | 176 | 177 | 178 | 10 179 | 180 | State 10 181 |  0 $accept: all $end . 182 | 183 | 184 | 185 | 3->10 186 | 187 | 188 | $end 189 | 190 | 191 | 192 | 4R1 193 | 194 | R1 195 | 196 | 197 | 198 | 4->4R1 199 | 200 | 201 | 202 | 203 | 204 | 5R3 205 | 206 | R3 207 | 208 | 209 | 210 | 5->5R3 211 | 212 | 213 | 214 | 215 | 216 | 11 217 | 218 | State 11 219 |  2 dir: LBRACE files RBRACE . 220 | 221 | 222 | 223 | 6->11 224 | 225 | 226 | RBRACE 227 | 228 | 229 | 230 | 7R5 231 | 232 | R5 233 | 234 | 235 | 236 | 7->7R5 237 | 238 | 239 | 240 | 241 | 242 | 12 243 | 244 | State 12 245 |  7 collection: file COMMA . collection 246 | 247 | 248 | 249 | 8->12 250 | 251 | 252 | COMMA 253 | 254 | 255 | 256 | 8R6 257 | 258 | R6 259 | 260 | 261 | 262 | 8->8R6 263 | 264 | 265 | 266 | 267 | 268 | 9->1 269 | 270 | 271 | LBRACE 272 | 273 | 274 | 275 | 9->2 276 | 277 | 278 | STR 279 | 280 | 281 | 282 | 9->5 283 | 284 | 285 | file 286 | 287 | 288 | 289 | 13 290 | 291 | State 13 292 |  9 file: STR COLON dir . 293 | 294 | 295 | 296 | 9->13 297 | 298 | 299 | dir 300 | 301 | 302 | 303 | 10R0 304 | 305 | Acc 306 | 307 | 308 | 309 | 10->10R0 310 | 311 | 312 | 313 | 314 | 315 | 11R2 316 | 317 | R2 318 | 319 | 320 | 321 | 11->11R2 322 | 323 | 324 | 325 | 326 | 327 | 12->2 328 | 329 | 330 | STR 331 | 332 | 333 | 334 | 12->8 335 | 336 | 337 | file 338 | 339 | 340 | 341 | 14 342 | 343 | State 14 344 |  7 collection: file COMMA collection . 345 | 346 | 347 | 348 | 12->14 349 | 350 | 351 | collection 352 | 353 | 354 | 355 | 13R9 356 | 357 | R9 358 | 359 | 360 | 361 | 13->13R9 362 | 363 | 364 | 365 | 366 | 367 | 14R7 368 | 369 | R7 370 | 371 | 372 | 373 | 14->14R7 374 | 375 | 376 | 377 | 378 | -------------------------------------------------------------------------------- /includes/errors.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef __cplusplus 4 | extern "C" 5 | { 6 | #endif 7 | 8 | extern void yyerror(const char* msg); // prints the message msg to stderr after an error is detected 9 | 10 | #ifdef __cplusplus 11 | } 12 | #endif 13 | -------------------------------------------------------------------------------- /includes/node.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct Node 4 | { 5 | struct Node* children; // pointer to a singly linked list of subdirectories and files 6 | struct Node* next; // stores a pointer to the next node in the current directory 7 | char* name; // name of the file or directory 8 | }; 9 | 10 | extern void clear(struct Node* node); // clears the memory allocated for node and its children 11 | -------------------------------------------------------------------------------- /includes/script_generator.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // called with the root to print generate the script 4 | extern void generate_script(const struct Node* node); 5 | 6 | // generate_script calls this on the underlying 7 | // directories and files to generate scripts 8 | // to make them 9 | extern void generate_script_node(struct Node* node); 10 | 11 | // generates codes for the respective commands 12 | extern void cd(const char* str); 13 | extern void mkdir_p(const char* str); 14 | extern void touch(const char* str); 15 | -------------------------------------------------------------------------------- /includes/util.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef __cplusplus 4 | extern "C" 5 | { 6 | #endif 7 | 8 | extern void strip_quotes(char* str); // removes the surrounding pair of quotes from str - the string literal 9 | 10 | extern void log_outfile(const char* format, ...); 11 | extern void log_stdout(const char* format, ...); 12 | extern void log_stderr(const char* format, ...); 13 | 14 | #ifdef __cplusplus 15 | } 16 | #endif 17 | -------------------------------------------------------------------------------- /src/errors.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "errors.hpp" 5 | #include "util.hpp" 6 | 7 | void yyerror(const char* msg) 8 | { 9 | log_stderr(msg); 10 | exit(EXIT_FAILURE); 11 | } 12 | -------------------------------------------------------------------------------- /src/grammar.y: -------------------------------------------------------------------------------- 1 | %{ 2 | #include 3 | #include 4 | 5 | #include "node.hpp" 6 | #include "errors.hpp" 7 | 8 | extern int yylex(); // main entry point for flex 9 | 10 | extern struct Node* root; // holds the entire AST 11 | 12 | %} 13 | 14 | /* 15 | * this union stores all the types 16 | * allowed for the symbols in the 17 | * grammar to be parsed 18 | */ 19 | %union 20 | { 21 | char cval; 22 | char* sval; 23 | struct Node* nval; 24 | } 25 | 26 | /* declaration of terminal symbols */ 27 | %token COMMA COLON LBRACE RBRACE 28 | %token STR 29 | 30 | /* declaration of non-terminal symbols */ 31 | %type all dir files collection file 32 | 33 | /* 34 | * here are the production rules 35 | * of the grammar, each of the form: 36 | 37 | non-terminal 38 | : { // code to form this particular part of the AST } 39 | | { // code to form this particular part of the AST } 40 | ... 41 | ; 42 | 43 | * In each production, the part before the colon (:) 44 | * is accessed using the $$ symbol and each symbol 45 | * of the list of terminals are accessed like this: 46 | * $1, $2, $3, ... 47 | * in their respective order of appearance 48 | */ 49 | 50 | %% 51 | all 52 | : dir { $$ = $1; root = $$; } 53 | ; 54 | 55 | dir 56 | : LBRACE files RBRACE { $$ = (struct Node*) malloc(sizeof(struct Node)); *($$) = (struct Node) {.name = NULL, .next = NULL, .children = $2}; } 57 | | file { $$ = (struct Node*) malloc(sizeof(struct Node)); *($$) = (struct Node) {.name = NULL, .next = NULL, .children = $1}; } 58 | ; 59 | 60 | files 61 | : { $$ = NULL; } 62 | | collection { $$ = $1; } 63 | ; 64 | 65 | collection 66 | : file { $$ = $1; } 67 | | file COMMA collection { $$ = $1; $$->next = $3; } 68 | ; 69 | 70 | file 71 | : STR { $$ = (struct Node*) malloc(sizeof(struct Node)); *($$) = (struct Node) {.name = $1, .next = NULL, .children = NULL}; } 72 | | STR COLON dir { $$ = (struct Node*) malloc(sizeof(struct Node)); *($$) = (struct Node) {.name = $1, .next = NULL, .children = $3}; } 73 | ; 74 | %% 75 | -------------------------------------------------------------------------------- /src/lexer.l: -------------------------------------------------------------------------------- 1 | %{ 2 | #include 3 | 4 | #include "util.hpp" 5 | #include "errors.hpp" 6 | #include "grammar.tab.h" 7 | %} 8 | 9 | %option noyywrap 10 | %option noinput 11 | %option nounput 12 | 13 | /* regular expressions for allowed tokens */ 14 | 15 | str \"(\\.|[^"\\])*\" 16 | /* 17 | * strings can be any 18 | * sequence of characters 19 | * with the exception that 20 | * backslashes(\) and 21 | * double quotes (") 22 | * need to be escaped by 23 | * placing a backslash 24 | * before them 25 | */ 26 | 27 | comma , 28 | colon : 29 | 30 | lbrace \{ 31 | rbrace \} 32 | 33 | whitespace [ \t\n\r] 34 | /* 35 | * whitespace is ignored 36 | * by the parser, it 37 | * consists of: 38 | * * space 39 | * * tabs 40 | * * newlines 41 | * * carriage returns 42 | */ 43 | 44 | /* here, we set the rules for what flex does 45 | * when it encounters each symbol in the form: 46 | {symbol-name} { // handle errors or fill in yylval with the data and return the symbol type } 47 | */ 48 | 49 | %% 50 | {whitespace} ; 51 | {str} { yylval.sval = strdup(yytext); strip_quotes(yylval.sval); return STR; } 52 | {lbrace} { return LBRACE; } 53 | {rbrace} { return RBRACE; } 54 | {colon} { return COLON; } 55 | {comma} { return COMMA; } 56 | . { char msg[64]; sprintf(msg, "Unknown character: %c", yytext[0]); yyerror(msg); } 57 | %% 58 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "util.hpp" 5 | #include "node.hpp" 6 | #include "script_generator.hpp" 7 | 8 | extern "C" 9 | { 10 | int yyparse(); 11 | } 12 | 13 | FILE* outfile; 14 | 15 | struct Node* root; // holds the entire AST (Abstract Syntax Tree) 16 | 17 | extern FILE* yyin; 18 | 19 | int main(int argc, char* argv[]) 20 | { 21 | FILE* infile; 22 | 23 | const char* Usage = 24 | "Usage: " TARGET " infile outfile\n" 25 | " infile: input file\n" 26 | " outfile: output file\n" 27 | ; 28 | 29 | if(argc == 2) 30 | { 31 | if(strcmp(argv[1], "-v") == 0 || strcmp(argv[1], "--version") == 0) 32 | { 33 | log_stdout(TARGET " " VERSION "\n"); 34 | } 35 | else if(strcmp(argv[1], "--help") == 0) 36 | { 37 | log_stdout(TARGET " " VERSION "\n"); 38 | log_stdout(Usage); 39 | } 40 | else 41 | { 42 | log_stderr("invalid argument: %s\n", argv[1]); 43 | log_stderr(Usage); 44 | 45 | return 1; 46 | } 47 | 48 | return 0; 49 | } 50 | 51 | if(argc != 3) 52 | { 53 | log_stderr("invalid arguments:"); 54 | for(int i = 1; i < argc; ++i) 55 | { 56 | log_stderr(" %s", argv[i]); 57 | } 58 | log_stderr("\n"); 59 | log_stdout(Usage); 60 | 61 | return 1; 62 | } 63 | 64 | if((infile = fopen(argv[1], "r")) == NULL) 65 | { 66 | log_stderr("%s: infile couldn't be open", argv[1]); 67 | return 1; 68 | } 69 | 70 | if((outfile = fopen(argv[2], "w")) == NULL) 71 | { 72 | log_stderr("%s: outfile couldn't be open", argv[2]); 73 | return 1; 74 | } 75 | 76 | yyin = infile; 77 | yyparse(); 78 | 79 | generate_script(root); 80 | 81 | clear(root); 82 | 83 | fclose(infile); 84 | fclose(outfile); 85 | 86 | return 0; 87 | } 88 | -------------------------------------------------------------------------------- /src/node.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "node.hpp" 5 | 6 | void clear(struct Node* node) 7 | { 8 | if(node) 9 | { 10 | clear(node->next); 11 | clear(node->children); 12 | 13 | free(node->name); 14 | 15 | node->next = nullptr; 16 | node->children = nullptr; 17 | node->name = nullptr; 18 | 19 | free(node); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/script_generator.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "script_generator.hpp" 4 | #include "node.hpp" 5 | #include "util.hpp" 6 | 7 | void generate_script(const struct Node* node) 8 | { 9 | if(node->children) 10 | { 11 | generate_script_node(node->children); 12 | } 13 | } 14 | 15 | void generate_script_node(struct Node* node) 16 | { 17 | while(node) 18 | { 19 | if(node->children) 20 | { 21 | // directory 22 | mkdir_p(node->name); 23 | cd(node->name); 24 | generate_script(node->children); 25 | cd(".."); 26 | } 27 | else 28 | { 29 | // file 30 | touch(node->name); 31 | } 32 | 33 | node = node->next; 34 | } 35 | } 36 | 37 | void cd(const char* str) 38 | { 39 | log_outfile("cd \"%s\"\n", str); 40 | } 41 | 42 | void mkdir_p(const char* str) 43 | { 44 | log_outfile("mkdir -p \"%s\"\n", str); 45 | } 46 | 47 | void touch(const char* str) 48 | { 49 | log_outfile("touch \"%s\"\n", str); 50 | } 51 | -------------------------------------------------------------------------------- /src/util.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "util.hpp" 4 | 5 | #include 6 | 7 | void strip_quotes(char* str) 8 | { 9 | const int N = strlen(str) - 3; 10 | int i = 0; 11 | while(i <= N) 12 | { 13 | str[i] = str[i + 1]; 14 | ++i; 15 | } 16 | str[i] = '\0'; 17 | } 18 | 19 | #include 20 | 21 | /* 22 | * outfile logger 23 | */ 24 | extern FILE* outfile; 25 | void log_outfile(const char* format, ...) 26 | { 27 | va_list args; 28 | va_start(args, format); 29 | vfprintf(outfile, format, args); 30 | va_end(args); 31 | } 32 | 33 | /* 34 | * stdout logger 35 | */ 36 | void log_stdout(const char* format, ...) 37 | { 38 | va_list args; 39 | va_start(args, format); 40 | vfprintf(stdout, format, args); 41 | va_end(args); 42 | } 43 | 44 | /* 45 | * stderr logger 46 | */ 47 | void log_stderr(const char* format, ...) 48 | { 49 | va_list args; 50 | va_start(args, format); 51 | vfprintf(stderr, format, args); 52 | va_end(args); 53 | } 54 | -------------------------------------------------------------------------------- /tests/.gitignore: -------------------------------------------------------------------------------- 1 | *.sh 2 | project 3 | -------------------------------------------------------------------------------- /tests/sample.json: -------------------------------------------------------------------------------- 1 | "project": { 2 | "README.md", 3 | "LICENSE", 4 | "CONTRIBUTING.md", 5 | "CODE_OF_CONDUCT.md", 6 | ".gitignore", 7 | "package.json", 8 | "package-lock.json", 9 | "build.gyp", 10 | "x": "y", 11 | "node_modules": { 12 | "x" 13 | }, 14 | "lib": { 15 | "index.js" 16 | }, 17 | "src": { 18 | "prog.cc", 19 | "includes": { 20 | } 21 | }, 22 | "build": { 23 | "Makefile", 24 | "Releases": { 25 | "prog.node" 26 | } 27 | }, 28 | "z": "a" 29 | } 30 | --------------------------------------------------------------------------------