1 #include <nm_core.h>
2 #include <nm_form.h>
3 #include <nm_menu.h>
4 #include <nm_utils.h>
5 #include <nm_vector.h>
6 #include <nm_window.h>
7 #include <nm_database.h>
8 #include <nm_network.h>
9
10 #include <time.h>
11 #include <glob.h>
12 #include <dirent.h>
13
14 static const int ROW_HEIGHT = 1;
15 static const int FIELD_HPAD = 2;
16 static const int FIELD_VPAD = 1;
17 static const int FORM_HPAD = 2;
18 static const int FORM_VPAD = 1;
19 static const int MIN_EDIT_SIZE = 18;
20 static const float FORM_RATIO = 0.80;
21
22 const char *nm_form_yes_no[] = {
23 "yes",
24 "no",
25 NULL
26 };
27
28 const char *nm_form_net_drv[] = {
29 "virtio-net-pci",
30 "rtl8139",
31 "e1000",
32 "vmxnet3",
33 NULL
34 };
35
36 const char *nm_form_drive_drv[] = {
37 "ide",
38 "nvme",
39 "scsi",
40 "virtio",
41 NULL
42 };
43
44 const char *nm_form_macvtap[] = {
45 "no",
46 "macvtap:bridge",
47 "macvtap:private",
48 NULL
49 };
50
51 const char *nm_form_usbtype[] = {
52 "EHCI",
53 "XHCI",
54 "NEC-XHCI",
55 NULL
56 };
57
58 const char *nm_form_displaytype[] = {
59 "qxl",
60 "virtio",
61 NULL
62 };
63
64 static int nm_append_path(nm_str_t *path);
65 static nm_field_t *nm_field_resize(nm_field_t *field, nm_form_data_t *form_data);
66 static nm_form_t *nm_form_redraw(nm_form_t *form);
67 static void nm_field_free_type(nm_field_data_t *field_data);
68
nm_field_new(nm_field_type_t type,nm_field_type_args_t type_args,int row,nm_form_data_t * form_data)69 nm_field_t *nm_field_new(
70 nm_field_type_t type, nm_field_type_args_t type_args,
71 int row, nm_form_data_t *form_data
72 )
73 {
74 nm_field_data_t *field_data = NULL;
75 nm_field_t *field = NULL;
76 int height = ROW_HEIGHT;
77 int top = (ROW_HEIGHT + form_data->field_vpad) * row;
78 int width;
79 int left;
80
81 field_data = (nm_field_data_t *)nm_alloc(sizeof(nm_field_data_t));
82
83 field_data->row = row;
84 field_data->children.n_memb = 0;
85 field_data->children.n_alloc = 0;
86 field_data->children.data = NULL;
87 field_data->on_change = NULL;
88
89 switch (type) {
90 case NM_FIELD_LABEL:
91 width = form_data->msg_len;
92 left = 0;
93 break;
94 default:
95 width = (form_data->form_len - form_data->msg_len - form_data->field_hpad);
96 left = (form_data->msg_len + form_data->field_hpad);
97 break;
98 }
99
100 field = new_field(height, width, top, left, 0, 0);
101 if (field == NULL)
102 nm_bug("%s: %s", __func__, strerror(errno));
103 set_field_userptr(field, field_data);
104
105 nm_set_field_type(field, type, type_args);
106
107 if (type == NM_FIELD_LABEL && form_data->color) {
108 set_field_fore(field, COLOR_PAIR(NM_COLOR_BLACK));
109 set_field_back(field, COLOR_PAIR(NM_COLOR_BLACK));
110 }
111
112 set_field_status(field, 0);
113 return field;
114 }
115
nm_field_label_new(int row,nm_form_data_t * form_data)116 nm_field_t *nm_field_label_new(int row, nm_form_data_t *form_data)
117 {
118 nm_field_type_args_t args = {0};
119 return nm_field_new(NM_FIELD_LABEL, args, row, form_data);
120 }
121
nm_field_default_new(int row,nm_form_data_t * form_data)122 nm_field_t *nm_field_default_new(int row, nm_form_data_t *form_data)
123 {
124 nm_field_type_args_t args = {0};
125 return nm_field_new(NM_FIELD_DEFAULT, args, row, form_data);
126 }
127
128 /* cppcheck-suppress unusedFunction */
nm_field_alnum_new(int row,nm_form_data_t * form_data,int min_width)129 nm_field_t *nm_field_alnum_new(int row, nm_form_data_t *form_data, int min_width)
130 {
131 nm_field_type_args_t args = {0};
132 args.alnum_arg.min_width = min_width;
133 return nm_field_new(NM_FIELD_ALNUM, args, row, form_data);
134 }
135
136 /* cppcheck-suppress unusedFunction */
nm_field_alpha_new(int row,nm_form_data_t * form_data,int min_width)137 nm_field_t *nm_field_alpha_new(int row, nm_form_data_t *form_data, int min_width)
138 {
139 nm_field_type_args_t args = {0};
140 args.alpha_arg.min_width = min_width;
141 return nm_field_new(NM_FIELD_ALPHA, args, row, form_data);
142 }
143
nm_field_enum_new(int row,nm_form_data_t * form_data,const char ** strings,int case_sens,int uniq_match)144 nm_field_t *nm_field_enum_new(
145 int row, nm_form_data_t *form_data,
146 const char **strings, int case_sens, int uniq_match)
147 {
148 nm_field_t *field;
149 char **strings_ = NULL;
150 nm_field_type_args_t args = {0};
151
152 if (strings) {
153 size_t count = 0;
154 for (char **p = (char **)strings; *p; p++)
155 count++;
156
157 strings_ = nm_calloc(count + 1, sizeof(char *));
158
159 for (size_t n = 0; n < count; n++)
160 strings_[n] = strdup(strings[n]);
161 strings_[count] = NULL;
162 }
163
164 args.enum_arg.strings = (const char **)strings_;
165 args.enum_arg.case_sens = case_sens;
166 args.enum_arg.uniq_match = uniq_match;
167
168 field = nm_field_new(NM_FIELD_ENUM, args, row, form_data);
169
170 if (!strings_)
171 field_opts_off(field, O_ACTIVE);
172
173 return field;
174 }
175
nm_field_integer_new(int row,nm_form_data_t * form_data,int prec,long min,long max)176 nm_field_t *nm_field_integer_new(
177 int row, nm_form_data_t *form_data, int prec, long min, long max)
178 {
179 nm_field_type_args_t args = {0};
180 args.integer_arg.prec = prec;
181 args.integer_arg.min = min;
182 args.integer_arg.max = max;
183 return nm_field_new(NM_FIELD_INTEGER, args, row, form_data);
184 }
185
186 /* cppcheck-suppress unusedFunction */
nm_field_numeric_new(int row,nm_form_data_t * form_data,int prec,double min,double max)187 nm_field_t *nm_field_numeric_new(
188 int row, nm_form_data_t *form_data, int prec, double min, double max)
189 {
190 nm_field_type_args_t args = {0};
191 args.numeric_arg.prec = prec;
192 args.numeric_arg.min = min;
193 args.numeric_arg.max = max;
194 return nm_field_new(NM_FIELD_NUMERIC, args, row, form_data);
195 }
196
nm_field_regexp_new(int row,nm_form_data_t * form_data,const char * exp)197 nm_field_t *nm_field_regexp_new(int row, nm_form_data_t *form_data, const char *exp)
198 {
199 nm_field_type_args_t args = {0};
200 args.regexp_arg.exp = strdup(exp);
201 return nm_field_new(NM_FIELD_REGEXP, args, row, form_data);
202 }
203
nm_set_field_type(nm_field_t * field,nm_field_type_t type,nm_field_type_args_t args)204 void nm_set_field_type(
205 nm_field_t *field, nm_field_type_t type, nm_field_type_args_t args)
206 {
207 nm_field_data_t *field_data = (nm_field_data_t *)field_userptr(field);
208 field_data->type = type;
209 field_data->type_args = args;
210
211 switch (type) {
212 case NM_FIELD_LABEL:
213 field_opts_off(field, O_ACTIVE);
214 break;
215 case NM_FIELD_DEFAULT:
216 field_opts_off(field, O_STATIC);
217 break;
218 case NM_FIELD_ALNUM:
219 field_opts_off(field, O_STATIC);
220 set_field_type(field, TYPE_ALNUM, args.alnum_arg.min_width);
221 break;
222 case NM_FIELD_ALPHA:
223 field_opts_off(field, O_STATIC);
224 set_field_type(field, TYPE_ALPHA, args.alpha_arg.min_width);
225 break;
226 case NM_FIELD_ENUM:
227 field_opts_off(field, O_STATIC);
228 set_field_type(
229 field, TYPE_ENUM,
230 args.enum_arg.strings,
231 args.enum_arg.case_sens,
232 args.enum_arg.uniq_match
233 );
234 break;
235 case NM_FIELD_INTEGER:
236 field_opts_off(field, O_STATIC);
237 set_field_type(
238 field, TYPE_INTEGER,
239 args.integer_arg.prec,
240 args.integer_arg.min,
241 args.integer_arg.max
242 );
243 break;
244 case NM_FIELD_NUMERIC:
245 field_opts_off(field, O_STATIC);
246 set_field_type(
247 field, TYPE_NUMERIC,
248 args.numeric_arg.prec,
249 args.numeric_arg.min,
250 args.numeric_arg.max
251 );
252 break;
253 case NM_FIELD_REGEXP:
254 field_opts_off(field, O_STATIC);
255 set_field_type(field, TYPE_REGEXP, args.regexp_arg.exp);
256 break;
257 }
258 }
259
nm_field_resize(nm_field_t * field,nm_form_data_t * form_data)260 static nm_field_t *nm_field_resize(nm_field_t *field, nm_form_data_t *form_data)
261 {
262 nm_field_data_t *field_data = (nm_field_data_t *)field_userptr(field);
263 nm_field_t *field_ = NULL;
264 int height = ROW_HEIGHT;
265 int top = (ROW_HEIGHT + form_data->field_vpad) * field_data->row;
266 int width;
267 int left;
268
269 switch (field_data->type) {
270 case NM_FIELD_LABEL:
271 width = form_data->msg_len;
272 left = 0;
273 break;
274 default:
275 width = (form_data->form_len - form_data->msg_len - form_data->field_hpad);
276 left = (form_data->msg_len + form_data->field_hpad);
277 break;
278 }
279
280 field_ = new_field(height, width, top, left, 0, 0);
281 if (field_ == NULL)
282 nm_bug("%s: %s", __func__, strerror(errno));
283
284 set_field_userptr(field_, field_data);
285 set_field_opts(field_, field_opts(field));
286 set_field_buffer(field_, 0, field_buffer(field, 0));
287 set_field_fore(field_, field_fore(field));
288 set_field_back(field_, field_back(field));
289 nm_set_field_type(field_, field_data->type, field_data->type_args);
290 set_field_status(field_, field_status(field));
291 //@TODO update children somehow
292
293 free_field(field);
294
295 return field_;
296 }
297
nm_field_free(nm_field_t * field)298 void nm_field_free(nm_field_t *field)
299 {
300 if (!field)
301 return;
302
303 nm_field_data_t *field_data = (nm_field_data_t *)field_userptr(field);
304 if (field_data) {
305 nm_field_free_type(field_data);
306 nm_vect_free(&field_data->children, NULL);
307 free(field_data);
308 }
309 free_field(field);
310 }
311
nm_fields_free(nm_field_t ** fields)312 void nm_fields_free(nm_field_t **fields)
313 {
314 for (; *fields; fields++) {
315 nm_field_free(*fields);
316 *fields = NULL;
317 }
318 }
319
nm_fields_unset_status(nm_field_t ** fields)320 void nm_fields_unset_status(nm_field_t **fields)
321 {
322 for (; *fields; fields++)
323 set_field_status(*fields, 0);
324 }
325
nm_form_data_new(nm_window_t * parent,void (* on_redraw)(nm_form_t *),size_t msg_len,size_t field_lines,int color)326 nm_form_data_t *nm_form_data_new(
327 nm_window_t *parent, void (*on_redraw)(nm_form_t *),
328 size_t msg_len, size_t field_lines, int color)
329 {
330 nm_form_data_t *form_data = nm_calloc(1, sizeof(nm_form_data_t));
331 form_data->parent_window = parent;
332 form_data->form_window = NULL;
333 form_data->on_redraw = on_redraw;
334 form_data->msg_len = msg_len;
335 form_data->field_lines = field_lines;
336 form_data->color = color;
337
338 form_data->field_hpad = FIELD_HPAD;
339 form_data->field_vpad = FIELD_VPAD;
340 form_data->form_hpad = FORM_HPAD;
341 form_data->form_vpad = FORM_VPAD;
342 form_data->form_ratio = FORM_RATIO;
343 form_data->min_edit_size = MIN_EDIT_SIZE;
344
345 form_data->w_rows = (ROW_HEIGHT + form_data->field_vpad) * field_lines \
346 + form_data->form_vpad;
347 form_data->w_start_y = NM_WINDOW_HEADER_HEIGHT;
348
349 return form_data;
350 }
351
nm_form_data_update(nm_form_data_t * form_data,size_t msg_len,size_t field_lines)352 int nm_form_data_update(nm_form_data_t *form_data, size_t msg_len, size_t field_lines)
353 {
354 size_t cols, rows;
355
356 getmaxyx(form_data->parent_window, rows, cols);
357
358 if (msg_len)
359 form_data->msg_len = msg_len;
360 if (field_lines)
361 form_data->field_lines = field_lines;
362
363 form_data->w_rows = (ROW_HEIGHT + form_data->field_vpad) * form_data->field_lines \
364 + form_data->form_vpad;
365 form_data->w_cols = cols * form_data->form_ratio;
366 form_data->form_len = form_data->w_cols \
367 - form_data->field_hpad - form_data->form_hpad;
368 form_data->w_start_x = ((1 - form_data->form_ratio) * cols) / 2;
369
370 if (form_data->w_cols < (form_data->msg_len + form_data->min_edit_size) ||
371 form_data->w_rows > rows - form_data->w_start_y - form_data->form_vpad) {
372 nm_warn(_(NM_MSG_SMALL_WIN));
373 return NM_ERR;
374 }
375
376 if (form_data->form_window)
377 delwin(form_data->form_window);
378
379 form_data->form_window = derwin(
380 form_data->parent_window,
381 form_data->w_rows, form_data->w_cols,
382 form_data->w_start_y, form_data->w_start_x
383 );
384
385 return NM_OK;
386 }
387
nm_form_data_free(nm_form_data_t * form_data)388 void nm_form_data_free(nm_form_data_t *form_data)
389 {
390 if (form_data) {
391 nm_vect_free(&form_data->h_lines, NULL);
392 if (form_data->form_window)
393 delwin(form_data->form_window);
394 free(form_data);
395 }
396 }
397
nm_form_new(nm_form_data_t * form_data,nm_field_t ** field)398 nm_form_t *nm_form_new(nm_form_data_t *form_data, nm_field_t **field)
399 {
400 nm_form_t* form;
401
402 form = new_form(field);
403 if (form == NULL)
404 nm_bug("%s: %s", __func__, strerror(errno));
405
406 set_form_userptr(form, form_data);
407 set_form_win(form, form_data->form_window);
408
409 return form;
410 }
411
nm_form_window_init()412 void nm_form_window_init()
413 {
414 nm_destroy_windows();
415 endwin();
416 refresh();
417 nm_create_windows();
418 }
419
nm_form_add_hline(nm_form_t * form,int y)420 void nm_form_add_hline(nm_form_t *form, int y)
421 {
422 nm_form_data_t *form_data = (nm_form_data_t *)form_userptr(form);
423
424 nm_vect_insert(&form_data->h_lines, &y, sizeof(y), NULL);
425 }
426
nm_form_post(nm_form_t * form)427 void nm_form_post(nm_form_t *form)
428 {
429 int rows, cols;
430 nm_form_data_t *form_data = (nm_form_data_t *)form_userptr(form);
431
432 if (form_data->color)
433 wbkgd(form_data->form_window, COLOR_PAIR(NM_COLOR_BLACK));
434
435 scale_form(form, &rows, &cols);
436 set_form_sub(form,
437 derwin(form_data->form_window,
438 rows, cols, form_data->form_vpad, form_data->form_hpad));
439 post_form(form);
440
441 for (size_t n = 0; n < form_data->h_lines.n_memb; n++) {
442 mvwhline(
443 form_data->form_window,
444 *(int *)nm_vect_at(&form_data->h_lines, n),
445 0, ACS_HLINE, getmaxx(form_data->form_window)
446 );
447 }
448
449 curs_set(1);
450 }
451
nm_form_draw(nm_form_t ** form)452 int nm_form_draw(nm_form_t **form)
453 {
454 nm_form_data_t *form_data = (nm_form_data_t *)form_userptr(*form);
455 int confirm = NM_ERR, rc = NM_OK;
456 int ch;
457 nm_str_t buf = NM_INIT_STR;
458
459 wtimeout(form_data->parent_window, 100);
460
461 while (confirm != NM_OK && (ch = wgetch(form_data->parent_window)) != NM_KEY_ESC) {
462 switch(ch) {
463 case KEY_DOWN:
464 form_driver(*form, REQ_VALIDATION);
465 form_driver(*form, REQ_NEXT_FIELD);
466 form_driver(*form, REQ_END_LINE);
467 break;
468
469 case KEY_UP:
470 form_driver(*form, REQ_VALIDATION);
471 form_driver(*form, REQ_PREV_FIELD);
472 form_driver(*form, REQ_END_LINE);
473 break;
474
475 case KEY_LEFT:
476 if (field_type(current_field(*form)) == TYPE_ENUM)
477 form_driver(*form, REQ_PREV_CHOICE);
478 else
479 form_driver(*form, REQ_PREV_CHAR);
480 break;
481
482 case KEY_RIGHT:
483 if (field_type(current_field(*form)) == TYPE_ENUM)
484 form_driver(*form, REQ_NEXT_CHOICE);
485 else
486 form_driver(*form, REQ_NEXT_CHAR);
487 break;
488
489 case KEY_HOME:
490 form_driver(*form, REQ_BEG_LINE);
491 break;
492
493 case KEY_END:
494 form_driver(*form, REQ_END_LINE);
495 break;
496
497 case KEY_BACKSPACE:
498 case 127:
499 form_driver(*form, REQ_DEL_PREV);
500 break;
501
502 case 0x9: /* TAB KEY */
503 if (field_type(current_field(*form)) != TYPE_REGEXP)
504 break;
505 {
506 form_driver(*form, REQ_NEXT_FIELD);
507 form_driver(*form, REQ_PREV_FIELD);
508 form_driver(*form, REQ_END_FIELD);
509
510 nm_get_field_buf(current_field(*form), &buf);
511
512 if (nm_append_path(&buf) == NM_OK) {
513 set_field_buffer(current_field(*form), 0, buf.data);
514 form_driver(*form, REQ_END_FIELD);
515 }
516 nm_str_trunc(&buf, 0);
517 }
518 break;
519
520 case KEY_PPAGE:
521 case KEY_NPAGE:
522 if (field_type(current_field(*form)) == TYPE_ENUM) {
523 int drop_ch = 0, x, y;
524 nm_window_t *drop;
525 nm_panel_t *panel;
526 nm_args_t *args = field_arg(current_field(*form));
527 nm_menu_data_t list = NM_INIT_MENU_DATA;
528 nm_vect_t values = NM_INIT_VECT;
529 ssize_t list_len = getmaxy(form_data->parent_window) - 4;
530 size_t max_len = 0;
531
532 for (ssize_t n = 0; n < args->count; n++) {
533 const char *keyword = args->kwds[n];
534 size_t key_len = strlen(keyword);
535 if (max_len < key_len) {
536 max_len = key_len;
537 }
538 nm_vect_insert_cstr(&values, keyword);
539 }
540
541 getyx(form_data->parent_window, y, x);
542 x = getbegx(form_sub(*form)) \
543 + form_data->msg_len + form_data->field_hpad;
544
545 list.highlight = 1;
546 list_len -= y;
547 if (list_len < args->count)
548 list.item_last = list_len;
549 else
550 list.item_last = list_len = args->count;
551 list.v = &values;
552
553 drop = newwin(list_len + 2, max_len + 4, y + 1, x);
554 keypad(drop, TRUE);
555 panel = new_panel(drop);
556 for(;;) {
557 werase(drop);
558 if (redraw_window) {
559 hide_panel(panel);
560 del_panel(panel);
561 delwin(drop);
562
563 ((nm_form_data_t *)form_userptr(*form))->on_redraw(*form);
564 *form = nm_form_redraw(*form);
565 wtimeout(form_data->parent_window, 100);
566
567 getyx(form_data->parent_window, y, x);
568 x = getbegx(form_sub(*form)) \
569 + form_data->msg_len + form_data->field_hpad;
570
571 drop = newwin(list_len + 2, max_len + 4, y + 1, x);
572 keypad(drop, TRUE);
573 panel = new_panel(drop);
574
575 update_panels();
576 doupdate();
577 redraw_window = 0;
578 }
579 nm_menu_scroll(&list, list_len, drop_ch);
580 nm_print_dropdown_menu(&list, drop);
581 drop_ch = wgetch(drop);
582 if (drop_ch == NM_KEY_ENTER || drop_ch == NM_KEY_ESC) {
583 break;
584 }
585 }
586
587 if (drop_ch == NM_KEY_ENTER) {
588 nm_args_t *cur_args = field_arg(current_field(*form));
589 set_field_buffer(current_field(*form), 0,
590 cur_args->kwds[(list.item_first + list.highlight) - 1]);
591 }
592
593 nm_vect_free(list.v, NULL);
594 hide_panel(panel);
595 update_panels();
596 doupdate();
597 curs_set(1);
598 del_panel(panel);
599 delwin(drop);
600 }
601 break;
602
603 case NM_KEY_ENTER:
604 confirm = NM_OK;
605 if (form_driver(*form, REQ_VALIDATION) != E_OK)
606 rc = NM_ERR;
607 break;
608
609 default:
610 form_driver(*form, ch);
611 break;
612 }
613
614 if (redraw_window) {
615 ((nm_form_data_t *)form_userptr(*form))->on_redraw(*form);
616 *form = nm_form_redraw(*form);
617 wtimeout(form_data->parent_window, 100);
618 redraw_window = 0;
619 }
620 }
621
622 nm_str_free(&buf);
623
624 if ((confirm == NM_OK) && (rc == NM_ERR)) {
625 confirm = NM_ERR;
626 NM_FORM_RESET();
627 nm_warn(_(NM_MSG_BAD_CTX));
628 }
629
630 return confirm;
631 }
632
nm_form_free(nm_form_t * form)633 void nm_form_free(nm_form_t *form)
634 {
635 if (form) {
636 unpost_form(form);
637 free_form(form);
638
639 curs_set(0);
640 }
641 }
642
nm_form_redraw(nm_form_t * form)643 static nm_form_t *nm_form_redraw(nm_form_t *form)
644 {
645 nm_form_t *form_;
646 nm_form_data_t *form_data = (nm_form_data_t *)form_userptr(form);
647 nm_field_t **fields = form_fields(form);
648 nm_field_t *cur_field = current_field(form);
649 int maxfield = form->maxfield;
650
651 form_driver(form, REQ_VALIDATION);
652
653 if (nm_form_data_update(form_data, form_data->msg_len, form_data->field_lines) != NM_OK) {
654 mvwaddstr(form_data->parent_window, 3, 1, _("Window too small"));
655 wrefresh(form_data->parent_window);
656 return form;
657 }
658
659 unpost_form(form);
660 free_form(form);
661
662 for (int n = 0; n < maxfield; n++) {
663 if(fields[n] == cur_field) {
664 fields[n] = nm_field_resize(fields[n], form_data);
665 cur_field = fields[n];
666 } else {
667 fields[n] = nm_field_resize(fields[n], form_data);
668 }
669 }
670
671 form_ = nm_form_new(form_data, fields);
672 if (form_ == NULL)
673 nm_bug("%s: %s", __func__, strerror(errno));
674
675 nm_form_post(form_);
676 if (cur_field) {
677 set_current_field(form_, cur_field);
678 form_driver(form_, REQ_END_LINE);
679 }
680
681 return form_;
682 }
683
nm_field_free_type(nm_field_data_t * field_data)684 static void nm_field_free_type(nm_field_data_t *field_data)
685 {
686 if (field_data->type == NM_FIELD_REGEXP)
687 free((char *)field_data->type_args.regexp_arg.exp);
688 else if (field_data->type == NM_FIELD_ENUM) {
689 char **strings = (char **)field_data->type_args.enum_arg.strings;
690 if (strings) {
691 for (char **s = strings; *s; s++)
692 free(*s);
693 free(strings);
694 }
695 }
696
697 field_data->type = NM_FIELD_DEFAULT;
698 field_data->type_args = (nm_field_type_args_t){0};
699 }
700
nm_get_field_buf(nm_field_t * f,nm_str_t * res)701 void nm_get_field_buf(nm_field_t *f, nm_str_t *res)
702 {
703 char *buf, *s;
704
705 if ((buf = field_buffer(f, 0)) == NULL)
706 nm_bug("%s: %s", __func__, strerror(errno));
707
708 s = strrchr(buf, 0x20);
709
710 if (s != NULL) {
711 while ((s > buf) && (s[-1] == 0x20))
712 --s;
713
714 *s = '\0';
715 }
716
717 nm_str_add_text(res, buf);
718 }
719
nm_append_path(nm_str_t * path)720 static int nm_append_path(nm_str_t *path)
721 {
722 int rc = NM_ERR;
723 glob_t res;
724 char *rp = NULL;
725 struct stat file_info;
726
727 memset(&file_info, 0, sizeof(file_info));
728
729 if (path->data[0] == '~') {
730 nm_str_t new_path = NM_INIT_STR;
731 const char *home;
732
733 if ((home = getenv("HOME")) == NULL)
734 return NM_ERR;
735
736 nm_str_format(&new_path, "%s%s", home,
737 (path->len > 1) ? path->data + 1 : "");
738 nm_str_trunc(path, 0);
739 nm_str_copy(path, &new_path);
740 nm_str_free(&new_path);
741 }
742
743 if (glob(path->data, 0, NULL, &res) != 0) {
744 nm_str_t tmp = NM_INIT_STR;
745
746 nm_str_format(&tmp, "%s*", path->data);
747 if (glob(tmp.data, 0, NULL, &res) == 0) {
748 nm_str_trunc(path, 0);
749 if ((rp = realpath(res.gl_pathv[0], NULL)) != NULL) {
750 nm_str_add_text(path, rp);
751 nm_str_free(&tmp);
752 rc = NM_OK;
753 if (stat(path->data, &file_info) != -1) {
754 if (S_ISDIR(file_info.st_mode) && path->len > 1)
755 nm_str_add_char(path, '/');
756 }
757 goto out;
758 }
759 }
760
761 nm_str_free(&tmp);
762 goto out;
763 }
764
765 if ((rp = realpath(res.gl_pathv[0], NULL)) != NULL) {
766 nm_str_trunc(path, 0);
767 nm_str_add_text(path, rp);
768 rc = NM_OK;
769 if (stat(path->data, &file_info) != -1) {
770 if (S_ISDIR(file_info.st_mode) && path->len > 1)
771 nm_str_add_char(path, '/');
772 }
773 }
774
775 out:
776 if (rp)
777 free(rp);
778 globfree(&res);
779
780 return rc;
781 }
782
nm_progress_bar(void * data)783 void *nm_progress_bar(void *data)
784 {
785 struct timespec ts;
786 int cols = getmaxx(action_window);
787 int x1 = 1, x2 = cols - 2;
788 int x1_v = 0, x2_v = 1;
789 nm_spinner_data_t *dp = data;
790
791 memset(&ts, 0, sizeof(ts));
792 ts.tv_nsec = 3e+7; /* 0.03sec */
793
794 curs_set(0);
795
796 for (;;) {
797 if (*dp->stop)
798 break;
799
800 NM_ERASE_TITLE(action, cols);
801 mvwaddch(action_window, 1, x1, x1_v ? '<' : '>');
802 mvwaddch(action_window, 1, x2, x2_v ? '<' : '>');
803 wrefresh(action_window);
804
805 (x1_v == 0) ? x1++ : x1--;
806 (x2_v == 0) ? x2++ : x2--;
807
808 if (x1 == cols - 2)
809 x1_v = 1;
810 if (x1 == 1)
811 x1_v = 0;
812
813 if (x2 == 1)
814 x2_v = 0;
815 if (x2 == cols - 2)
816 x2_v = 1;
817
818 nanosleep(&ts, NULL);
819 }
820
821 pthread_exit(NULL);
822 }
823
nm_file_progress(void * data)824 void *nm_file_progress(void *data)
825 {
826 struct timespec ts;
827 int cols = getmaxx(action_window);
828 nm_spinner_data_t *dp = data;
829 const nm_vm_t *vm = dp->ctx;
830 nm_str_t dst = NM_INIT_STR;
831 struct stat img_info, dst_info;
832
833 memset(&ts, 0x0, sizeof(ts));
834 ts.tv_nsec = 1e+8;
835
836 stat(vm->srcp.data, &img_info);
837
838 nm_str_format(&dst, "%s/%s/%s_a.img",
839 nm_cfg_get()->vm_dir.data, vm->name.data, vm->name.data);
840
841 curs_set(0);
842
843 for (;;) {
844 int64_t perc;
845 memset(&dst_info, 0x0, sizeof(dst_info));
846
847 if (*dp->stop)
848 break;
849
850 stat(dst.data, &dst_info);
851 perc = (dst_info.st_size * 100) / img_info.st_size;
852 NM_ERASE_TITLE(action, cols);
853 mvwprintw(action_window, 1, 2, "%d%% %ldmb/%ldmb",
854 perc, dst_info.st_size / 1024, img_info.st_size / 1024);
855 wrefresh(action_window);
856
857 nanosleep(&ts, NULL);
858 }
859
860 nm_str_free(&dst);
861 pthread_exit(NULL);
862 }
863
864 #if 0
865 void *nm_spinner(void *data)
866 {
867 const char spin_chars[] ="/-\\|";
868 nm_spinner_data_t *dp = data;
869 struct timespec ts;
870
871 memset(&ts, 0, sizeof(ts));
872 ts.tv_nsec = 3e+7; /* 0.03sec */
873
874 if (dp == NULL)
875 nm_bug(_("%s: NULL pointer"), __func__);
876
877 for (uint32_t i = 0 ;; i++) {
878 if (*dp->stop)
879 break;
880
881 curs_set(0);
882 mvaddch(dp->y, dp->x, spin_chars[i & 3]);
883 refresh();
884 nanosleep(&ts, NULL);
885 }
886
887 pthread_exit(NULL);
888 }
889 #endif
890
891 //@TODO Does this work at all?
nm_print_empty_fields(const nm_vect_t * v)892 int nm_print_empty_fields(const nm_vect_t *v)
893 {
894 nm_str_t msg = NM_INIT_STR;
895
896 if (v->n_memb == 0)
897 return NM_OK;
898
899 NM_FORM_RESET();
900
901 nm_str_alloc_text(&msg, _(NM_MSG_NULL_FLD));
902
903 for (size_t n = 0; n < v->n_memb; n++)
904 nm_str_append_format(&msg, " '%s'", (char *) v->data[n]);
905
906 nm_warn(msg.data);
907 nm_str_free(&msg);
908
909 return NM_ERR;
910 }
911
nm_form_name_used(const nm_str_t * name)912 int nm_form_name_used(const nm_str_t *name)
913 {
914 int rc = NM_OK;
915
916 nm_vect_t res = NM_INIT_VECT;
917 nm_str_t query = NM_INIT_STR;
918
919 nm_str_alloc_text(&query, "SELECT id FROM vms WHERE name='");
920 nm_str_add_str(&query, name);
921 nm_str_add_char(&query, '\'');
922
923 nm_db_select(query.data, &res);
924 if (res.n_memb > 0) {
925 rc = NM_ERR;
926 curs_set(0);
927 nm_warn(_(NM_MSG_NAME_BUSY));
928 }
929
930 nm_vect_free(&res, NULL);
931 nm_str_free(&query);
932
933 return rc;
934 }
935
nm_form_get_last_mac()936 uint64_t nm_form_get_last_mac()
937 {
938 uint64_t mac;
939 nm_vect_t res = NM_INIT_VECT;
940
941 nm_db_select("SELECT COALESCE(MAX(mac_addr), 'de:ad:be:ef:00:00') FROM ifaces", &res);
942
943 mac = nm_net_mac_s2n(res.data[0]);
944
945 nm_vect_free(&res, nm_str_vect_free_cb);
946
947 return mac;
948 }
949
nm_form_get_free_vnc()950 uint32_t nm_form_get_free_vnc()
951 {
952 uint32_t vnc;
953 nm_vect_t res = NM_INIT_VECT;
954
955 nm_db_select("\
956 SELECT DISTINCT vnc + 1 FROM vms \
957 UNION SELECT 0 EXCEPT SELECT DISTINCT vnc FROM vms \
958 ORDER BY vnc ASC LIMIT 1", &res);
959
960 vnc = nm_str_stoui(res.data[0], 10);
961
962 nm_vect_free(&res, nm_str_vect_free_cb);
963
964 return vnc;
965 }
966
nm_vm_free(nm_vm_t * vm)967 void nm_vm_free(nm_vm_t *vm)
968 {
969 nm_str_free(&vm->name);
970 nm_str_free(&vm->arch);
971 nm_str_free(&vm->cpus);
972 nm_str_free(&vm->memo);
973 nm_str_free(&vm->srcp);
974 nm_str_free(&vm->vncp);
975 nm_str_free(&vm->mach);
976 nm_str_free(&vm->cmdappend);
977 nm_str_free(&vm->group);
978 nm_str_free(&vm->usb_type);
979 nm_str_free(&vm->ifs.driver);
980 nm_str_free(&vm->drive.driver);
981 nm_str_free(&vm->drive.size);
982 }
983
nm_vm_free_boot(nm_vm_boot_t * vm)984 void nm_vm_free_boot(nm_vm_boot_t *vm)
985 {
986 nm_str_free(&vm->bios);
987 nm_str_free(&vm->initrd);
988 nm_str_free(&vm->kernel);
989 nm_str_free(&vm->cmdline);
990 nm_str_free(&vm->inst_path);
991 nm_str_free(&vm->debug_port);
992 }
993
994 /* vim:set ts=4 sw=4: */
995