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