1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <ctype.h>
18 #include <getopt.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <unistd.h>
23 
24 #include "dt_table.h"
25 #include "mkdtimg_core.h"
26 
27 
28 struct cfg_create_params {
29   const char *img_filename;
30   const char *cfg_filename;
31   const char *dtb_dir;
32 };
33 
34 static const char short_options[] = "d:";
35 static struct option options[] = {
36   { "dtb-dir",   required_argument, NULL, 'd' },
37   { 0,           0,                 NULL, 0 }
38 };
39 
40 
trim_line(char * line)41 static char *trim_line(char *line) {
42   /* Find the end of the string or the first of '#' */
43   char *end = line;
44   while (*end != '\0' && *end != '#') {
45     end++;
46   }
47   do {
48     end--;
49   } while (end >= line && isspace(*end));
50 
51   *(end + 1) = '\0';
52 
53   while (isspace(*line)) {
54     line++;
55   }
56   return line;
57 }
58 
parse_config_entry_count(FILE * cfg_fp)59 static int parse_config_entry_count(FILE *cfg_fp) {
60   int count = 0;
61 
62   /* Any line without prefix spaces is entry filename */
63   char line[1024];
64   while (fgets(line, sizeof(line), cfg_fp) != NULL) {
65     char c = line[0];
66     if (c == '\0' || isspace(c) || c == '#') continue;
67     count++;
68   }
69 
70   return count;
71 }
72 
output_img_with_config(FILE * img_fp,FILE * cfg_fp)73 static int output_img_with_config(FILE *img_fp, FILE *cfg_fp) {
74   int entry_count = parse_config_entry_count(cfg_fp);
75   struct dt_image_writer *writer = dt_image_writer_start(img_fp, entry_count);
76 
77   fseek(cfg_fp, 0, SEEK_SET); /* Reset the file pos to head */
78 
79   int is_entry = 0;
80   char line[1024];
81   while (fgets(line, sizeof(line), cfg_fp) != NULL) {
82     char *trimmed = trim_line(line);
83     if (trimmed[0] == '\0') {
84       /* empty line, pass */
85       continue;
86     }
87 
88     if (trimmed == line) {
89       /* This line is a file name,
90         because it start from the first char of the line */
91       if (dt_image_writer_add_entry(writer, trimmed) != 0) {
92         return -1;
93       }
94       is_entry = 1;
95       continue;
96     }
97 
98     char *option, *value;
99     if (parse_option(&option, &value, trimmed) != 0) {
100       fprintf(stderr, "Wrong syntax: %s\n", trimmed);
101       return -1;
102     }
103 
104     int ret = is_entry ?
105       set_entry_options(writer, option, value) :
106       set_global_options(writer, option, value);
107     if (ret != 0) {
108       fprintf(stderr, "Unknown option: %s\n", option);
109       return -1;
110     }
111   }
112 
113   if (dt_image_writer_end(writer) != 0) {
114     return -1;
115   }
116 
117   return 0;
118 }
119 
process_command_cfg_create(const struct cfg_create_params * params)120 static int process_command_cfg_create(const struct cfg_create_params *params) {
121   int ret = -1;
122   FILE *cfg_fp = NULL;
123   FILE *img_fp = NULL;
124 
125   cfg_fp = fopen(params->cfg_filename, "r");
126   if (cfg_fp == NULL) {
127     fprintf(stderr, "Can not open config file: %s\n", params->cfg_filename);
128     goto end;
129   }
130 
131   printf("create image file: %s...\n", params->img_filename);
132 
133   img_fp = fopen(params->img_filename, "wb");
134   if (img_fp == NULL) {
135     fprintf(stderr, "Can not create file: %s\n", params->img_filename);
136     goto end;
137   }
138 
139   if (params->dtb_dir != NULL) {
140     if (chdir(params->dtb_dir) != 0) {
141       fprintf(stderr, "Can not switch to directory: %s\n", params->dtb_dir);
142       goto end;
143     }
144   }
145 
146   ret = output_img_with_config(img_fp, cfg_fp);
147   if (ret < 0)
148     fprintf(stderr, "Can not output image with config: %s\n",
149             params->cfg_filename);
150 
151 end:
152   if (img_fp) {
153     fclose(img_fp);
154     if (ret < 0) unlink(params->img_filename);
155   }
156   if (cfg_fp) fclose(cfg_fp);
157 
158   return ret;
159 }
160 
handle_usage_cfg_create(FILE * out_fp,const char * prog_name)161 void handle_usage_cfg_create(FILE *out_fp, const char *prog_name) {
162   fprintf(out_fp, "  %s cfg_create <image_file> <config_file> (<option>...)\n\n", prog_name);
163   fprintf(out_fp,
164     "    options:\n"
165     "      -d, --dtb-dir            The path to load dtb files.\n"
166     "                               Default is load from the current path.\n");
167 }
168 
handle_command_cfg_create(int argc,char * argv[],int arg_start)169 int handle_command_cfg_create(int argc, char *argv[], int arg_start) {
170   if (argc - arg_start < 2) {
171     handle_usage_cfg_create(stderr, argv[0]);
172     return 1;
173   }
174 
175   struct cfg_create_params params;
176   memset(&params, 0, sizeof(params));
177   params.img_filename = argv[arg_start];
178   params.cfg_filename = argv[arg_start + 1];
179 
180   optind = arg_start + 2;
181   while (1) {
182     int c = getopt_long(argc, argv, short_options, options, NULL);
183     if (c == -1) {
184       break;
185     }
186     switch (c) {
187       case 'd':
188         params.dtb_dir = optarg;
189         break;
190       default:
191         /* Unknown option, return error */
192         return 1;
193     }
194   }
195 
196   return process_command_cfg_create(&params);
197 }
198