source: mainline/uspace/app/newfile/newfile.c@ bb4d0b5

Last change on this file since bb4d0b5 was bb4d0b5, checked in by Jiri Svoboda <jiri@…>, 3 weeks ago

Allow user to decide whether to retry or abort when I/O error occurs.

  • Property mode set to 100644
File size: 6.7 KB
Line 
1/*
2 * Copyright (c) 2025 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup newfile
30 * @{
31 */
32/** @file Create new file.
33 */
34
35#include <capa.h>
36#include <errno.h>
37#include <fmgt.h>
38#include <io/console.h>
39#include <io/cons_event.h>
40#include <io/kbd_event.h>
41#include <stdbool.h>
42#include <stdio.h>
43#include <stdlib.h>
44#include <str.h>
45#include <str_error.h>
46
47#define NAME "newfile"
48
49static bool newfile_abort_query(void *);
50static void newfile_progress(void *, fmgt_progress_t *);
51static fmgt_error_action_t newfile_io_error_query(void *, fmgt_io_error_t *);
52
53static bool prog_upd = false;
54static bool nonint = false;
55static bool quiet = false;
56
57static console_ctrl_t *con;
58
59static fmgt_cb_t newfile_fmgt_cb = {
60 .abort_query = newfile_abort_query,
61 .io_error_query = newfile_io_error_query,
62 .progress = newfile_progress,
63};
64
65static void print_syntax(void)
66{
67 printf("Create new file.\n");
68 printf("Syntax: %s [<options] [<file-name>]\n", NAME);
69 printf("\t-h help\n");
70 printf("\t-n non-interactive\n");
71 printf("\t-p create sparse file\n");
72 printf("\t-q quiet\n");
73 printf("\t-size=<cap> file size (<number>[<kB>|<MB>|...])\n");
74}
75
76/** Called by fmgt to query for user abort.
77 *
78 * @param arg Argument (not used)
79 * @return @c true iff user requested abort
80 */
81static bool newfile_abort_query(void *arg)
82{
83 cons_event_t event;
84 kbd_event_t *ev;
85 errno_t rc;
86 usec_t timeout;
87
88 if (con == NULL)
89 return false;
90
91 timeout = 0;
92 rc = console_get_event_timeout(con, &event, &timeout);
93 if (rc != EOK)
94 return false;
95
96 if (event.type == CEV_KEY && event.ev.key.type == KEY_PRESS) {
97 ev = &event.ev.key;
98 if ((ev->mods & KM_ALT) == 0 &&
99 (ev->mods & KM_SHIFT) == 0 &&
100 (ev->mods & KM_CTRL) != 0) {
101 if (ev->key == KC_C)
102 return true;
103 }
104 }
105
106 return false;
107}
108
109/** Called by fmgt to give the user progress update.
110 *
111 * @param arg Argument (not used)
112 * @param progress Progress report
113 */
114static void newfile_progress(void *arg, fmgt_progress_t *progress)
115{
116 (void)arg;
117
118 if (!quiet) {
119 printf("\rWritten %s of %s (%s done).", progress->curf_procb,
120 progress->curf_totalb, progress->curf_percent);
121 fflush(stdout);
122 prog_upd = true;
123 }
124}
125
126/** Called by fmgt to let user choose I/O error recovery action.
127 *
128 * @param arg Argument (not used)
129 * @param err I/O error report
130 * @return Error recovery action.
131 */
132static fmgt_error_action_t newfile_io_error_query(void *arg,
133 fmgt_io_error_t *err)
134{
135 cons_event_t event;
136 kbd_event_t *ev;
137 errno_t rc;
138
139 (void)arg;
140
141 if (nonint)
142 return fmgt_er_abort;
143
144 if (prog_upd)
145 putchar('\n');
146
147 fprintf(stderr, "I/O error %s file '%s' (%s).\n",
148 err->optype == fmgt_io_write ? "writing" : "reading",
149 err->fname, str_error(err->rc));
150 fprintf(stderr, "[A]bort or [R]etry?\n");
151
152 if (con == NULL)
153 return fmgt_er_abort;
154
155 while (true) {
156 rc = console_get_event(con, &event);
157 if (rc != EOK)
158 return fmgt_er_abort;
159
160 if (event.type == CEV_KEY && event.ev.key.type == KEY_PRESS) {
161 ev = &event.ev.key;
162 if ((ev->mods & KM_ALT) == 0 &&
163 (ev->mods & KM_CTRL) == 0) {
164 if (ev->c == 'r' || ev->c == 'R')
165 return fmgt_er_retry;
166 if (ev->c == 'a' || ev->c == 'A')
167 return fmgt_er_abort;
168 }
169 }
170
171 if (event.type == CEV_KEY && event.ev.key.type == KEY_PRESS) {
172 ev = &event.ev.key;
173 if ((ev->mods & KM_ALT) == 0 &&
174 (ev->mods & KM_SHIFT) == 0 &&
175 (ev->mods & KM_CTRL) != 0) {
176 if (ev->key == KC_C)
177 return fmgt_er_abort;
178 }
179 }
180 }
181}
182
183int main(int argc, char *argv[])
184{
185 fmgt_t *fmgt = NULL;
186 errno_t rc;
187 int i;
188 bool sparse = false;
189 char *fsize = NULL;
190 char *fname = NULL;
191 capa_spec_t fcap;
192 uint64_t nbytes = 0;
193
194 con = console_init(stdin, stdout);
195
196 i = 1;
197 while (i < argc && argv[i][0] == '-') {
198 if (str_cmp(argv[i], "-h") == 0) {
199 print_syntax();
200 return 0;
201 } else if (str_cmp(argv[i], "-n") == 0) {
202 ++i;
203 nonint = true;
204 } else if (str_cmp(argv[i], "-p") == 0) {
205 ++i;
206 sparse = true;
207 } else if (str_cmp(argv[i], "-q") == 0) {
208 ++i;
209 quiet = true;
210 } else if (str_lcmp(argv[i], "-size=",
211 str_length("-size=")) == 0) {
212 fsize = argv[i] + str_length("-size=");
213 ++i;
214 } else {
215 printf("Invalid option '%s'.\n", argv[i]);
216 print_syntax();
217 goto error;
218 }
219 }
220
221 if (i < argc) {
222 fname = str_dup(argv[i++]);
223 if (fname == NULL) {
224 printf("Out of memory.\n");
225 goto error;
226 }
227 }
228
229 if (i < argc) {
230 printf("Unexpected argument.\n");
231 print_syntax();
232 goto error;
233 }
234
235 if (fname == NULL) {
236 rc = fmgt_new_file_suggest(&fname);
237 if (rc != EOK) {
238 printf("Out of memory.\n");
239 goto error;
240 }
241 }
242
243 (void)nonint;
244 (void)quiet;
245 (void)sparse;
246
247 if (fsize != NULL) {
248 rc = capa_parse(fsize, &fcap);
249 if (rc != EOK) {
250 printf("Invalid file size '%s'.\n", fsize);
251 goto error;
252 }
253
254 rc = capa_to_blocks(&fcap, cv_nom, 1, &nbytes);
255 if (rc != EOK) {
256 printf("File size too large '%s'.\n", fsize);
257 goto error;
258 }
259 }
260
261 rc = fmgt_create(&fmgt);
262 if (rc != EOK) {
263 printf("Out of memory.\n");
264 goto error;
265 }
266
267 fmgt_set_cb(fmgt, &newfile_fmgt_cb, NULL);
268
269 rc = fmgt_new_file(fmgt, fname, nbytes, sparse ? nf_sparse : nf_none);
270 if (prog_upd)
271 putchar('\n');
272 if (rc != EOK) {
273 printf("Error creating file: %s.\n", str_error(rc));
274 goto error;
275 }
276
277 free(fname);
278 fmgt_destroy(fmgt);
279 return 0;
280error:
281 if (fname != NULL)
282 free(fname);
283 if (fmgt != NULL)
284 fmgt_destroy(fmgt);
285 return 1;
286}
287
288/** @}
289 */
Note: See TracBrowser for help on using the repository browser.