1 /*
2  * Copyright (c) 2011 Tim van der Molen <tim@kariliq.nl>
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
getrandom_inner(dest: &mut [u8]) -> Result<(), Error>16 
17 #ifdef __OpenBSD__
18 #include <sys/queue.h>
19 #else
20 #include "compat/queue.h"
21 #endif
22 
23 #include <limits.h>
24 #include <stdlib.h>
25 
26 #include "siren.h"
27 
28 #define MENU_NENTRIES_MAX UINT_MAX
29 
30 struct menu {
31 	struct menu_entry *active;
32 	struct menu_entry *selected;
33 	struct menu_entry *top;
34 	unsigned int	 nentries;
35 
36 	void		 (*free_entry_data)(void *);
37 	void		 (*get_entry_text)(const void *, char *, size_t);
38 	int		 (*search_entry_data)(const void *, const char *);
39 
40 	TAILQ_HEAD(menu_list, menu_entry) list;
41 };
42 
43 struct menu_entry {
44 	unsigned int	 index;
45 	void		*data;
46 	TAILQ_ENTRY(menu_entry) entries;
47 };
48 
49 void
50 menu_activate_entry(struct menu *m, struct menu_entry *e)
51 {
52 	m->active = e;
53 }
54 
55 static void
56 menu_adjust_scroll_offset(struct menu *m)
57 {
58 	unsigned int nrows;
59 
60 	if (m->nentries == 0)
61 		return;
62 
63 	nrows = screen_view_get_nrows();
64 
65 	/*
66 	 * If the selected entry is above the viewport, then move the selected
67 	 * entry to the top of the viewport.
68 	 */
69 	if (m->selected->index < m->top->index || nrows == 0)
70 		m->top = m->selected;
71 	/*
72 	 * If the selected entry is below the viewport, then move the selected
73 	 * entry to the bottom of the viewport.
74 	 */
75 	else if (m->selected->index >= m->top->index + nrows)
76 		do
77 			m->top = TAILQ_NEXT(m->top, entries);
78 		while (m->top->index != m->selected->index - nrows + 1);
79 	/*
80 	 * If the viewport extends below the last entry, then, if possible,
81 	 * move the last entry to the bottom of the viewport.
82 	 */
83 	else
84 		while (m->top->index > 0 &&
85 		    m->top->index + nrows > m->nentries)
86 			m->top = TAILQ_PREV(m->top, menu_list, entries);
87 }
88 
89 void
90 menu_free(struct menu *m)
91 {
92 	menu_remove_all_entries(m);
93 	free(m);
94 }
95 
96 struct menu_entry *
97 menu_get_active_entry(const struct menu *m)
98 {
99 	return m->active;
100 }
101 
102 void *
103 menu_get_entry_data(const struct menu_entry *e)
104 {
105 	return e->data;
106 }
107 
108 struct menu_entry *
109 menu_get_first_entry(const struct menu *m)
110 {
111 	return TAILQ_FIRST(&m->list);
112 }
113 
114 struct menu_entry *
115 menu_get_last_entry(const struct menu *m)
116 {
117 	return TAILQ_LAST(&m->list, menu_list);
118 }
119 
120 unsigned int
121 menu_get_nentries(const struct menu *m)
122 {
123 	return m->nentries;
124 }
125 
126 struct menu_entry *
127 menu_get_next_entry(const struct menu_entry *e)
128 {
129 	return TAILQ_NEXT(e, entries);
130 }
131 
132 struct menu_entry *
133 menu_get_prev_entry(const struct menu_entry *e)
134 {
135 	return TAILQ_PREV(e, menu_list, entries);
136 }
137 
138 struct menu_entry *
139 menu_get_selected_entry(const struct menu *m)
140 {
141 	return m->selected;
142 }
143 
144 void *
145 menu_get_selected_entry_data(const struct menu *m)
146 {
147 	return m->selected == NULL ? NULL : m->selected->data;
148 }
149 
150 struct menu *
151 menu_init(void (*free_entry_data)(void *),
152     void (*get_entry_text)(const void *, char *, size_t),
153     int (*search_entry_data)(const void *, const char *))
154 {
155 	struct menu *m;
156 
157 	m = xmalloc(sizeof *m);
158 	m->active = NULL;
159 	m->selected = NULL;
160 	m->top = NULL;
161 	m->nentries = 0;
162 	m->free_entry_data = free_entry_data;
163 	m->get_entry_text = get_entry_text;
164 	m->search_entry_data = search_entry_data;
165 	TAILQ_INIT(&m->list);
166 	return m;
167 }
168 
169 void
170 menu_insert_after(struct menu *m, struct menu_entry *le, void *data)
171 {
172 	struct menu_entry *e;
173 
174 	if (m->nentries == MENU_NENTRIES_MAX)
175 		return;
176 
177 	e = xmalloc(sizeof *e);
178 	e->data = data;
179 	e->index = le->index + 1;
180 
181 	TAILQ_INSERT_AFTER(&m->list, le, e, entries);
182 	m->nentries++;
183 
184 	/* Increment the index of the entries after the inserted entry. */
185 	while ((e = TAILQ_NEXT(e, entries)) != NULL)
186 		e->index++;
187 }
188 
189 void
190 menu_insert_before(struct menu *m, struct menu_entry *le, void *data)
191 {
192 	struct menu_entry *e;
193 
194 	if (m->nentries == MENU_NENTRIES_MAX)
195 		return;
196 
197 	e = xmalloc(sizeof *e);
198 	e->data = data;
199 	e->index = le->index;
200 
201 	TAILQ_INSERT_BEFORE(le, e, entries);
202 	m->nentries++;
203 
204 	/* Increment the index of the entries after the inserted entry. */
205 	while ((e = TAILQ_NEXT(e, entries)) != NULL)
206 		e->index++;
207 }
208 
209 void
210 menu_insert_head(struct menu *m, void *data)
211 {
212 	struct menu_entry *e;
213 
214 	if (m->nentries == MENU_NENTRIES_MAX)
215 		return;
216 
217 	e = xmalloc(sizeof *e);
218 	e->data = data;
219 	e->index = 0;
220 
221 	TAILQ_INSERT_HEAD(&m->list, e, entries);
222 	m->nentries++;
223 
224 	if (m->nentries == 1)
225 		/*
226 		 * This is the first entry in the menu: make it the top entry
227 		 * and the selected entry.
228 		 */
229 		m->top = m->selected = e;
230 
231 	/* Increment the index of the entries after the inserted entry. */
232 	while ((e = TAILQ_NEXT(e, entries)) != NULL)
233 		e->index++;
234 }
235 
236 void
237 menu_insert_tail(struct menu *m, void *data)
238 {
239 	struct menu_entry *e;
240 
241 	if (m->nentries == MENU_NENTRIES_MAX)
242 		return;
243 
244 	e = xmalloc(sizeof *e);
245 	e->data = data;
246 	e->index = m->nentries;
247 
248 	TAILQ_INSERT_TAIL(&m->list, e, entries);
249 	m->nentries++;
250 
251 	if (m->nentries == 1)
252 		/*
253 		 * This is the first entry in the menu: make it the top entry
254 		 * and the selected entry.
255 		 */
256 		m->top = m->selected = e;
257 }
258 
259 /* Move entry e before entry be. */
260 void
261 menu_move_entry_before(struct menu *m, struct menu_entry *be,
262     struct menu_entry *e)
263 {
264 	struct menu_entry *f;
265 
266 	/* Update the index of each relevant entry. */
267 	e->index = be->index;
268 	for (f = be; f != e; f = TAILQ_NEXT(f, entries))
269 		f->index++;
270 
271 	/* Move the entry to its new position. */
272 	TAILQ_REMOVE(&m->list, e, entries);
273 	TAILQ_INSERT_BEFORE(be, e, entries);
274 }
275 
276 void
277 menu_move_entry_down(struct menu *m, struct menu_entry *e)
278 {
279 	struct menu_entry *f;
280 
281 	if ((f = TAILQ_NEXT(e, entries)) != NULL)
282 		menu_move_entry_before(m, e, f);
283 }
284 
285 void
286 menu_move_entry_up(struct menu *m, struct menu_entry *e)
287 {
288 	struct menu_entry *f;
289 
290 	if ((f = TAILQ_PREV(e, menu_list, entries)) != NULL)
291 		menu_move_entry_before(m, f, e);
292 }
293 
294 void
295 menu_print(struct menu *m)
296 {
297 	struct menu_entry	*e;
298 	unsigned int		 bottomrow, nrows, percent, toprow;
299 	size_t			 bufsize;
300 	char			*buf;
301 
302 	menu_adjust_scroll_offset(m);
303 
304 	nrows = screen_view_get_nrows();
305 	if (m->nentries == 0) {
306 		toprow = 0;
307 		bottomrow = 0;
308 		percent = 100;
309 	} else {
310 		toprow = m->top->index + 1;
311 		if (nrows == 0) {
312 			bottomrow = 0;
313 			percent = 100 * toprow / m->nentries;
314 		} else {
315 			if (m->nentries < nrows)
316 				bottomrow = m->nentries;
317 			else
318 				bottomrow = toprow + nrows - 1;
319 			percent = 100 * bottomrow / m->nentries;
320 		}
321 	}
322 	screen_view_title_printf_right(" %u-%u/%u (%u%%)", toprow, bottomrow,
323 	    m->nentries, percent);
324 
325 	screen_view_print_begin();
326 	if (m->nentries > 0) {
327 		bufsize = screen_get_ncols() + 1;
328 		buf = xmalloc(bufsize);
329 		e = m->top;
330 		while (nrows-- > 0 && e != NULL) {
331 			m->get_entry_text(e->data, buf, bufsize);
332 			if (e == m->selected)
333 				screen_view_print_selected(buf);
334 			else if (e == m->active)
335 				screen_view_print_active(buf);
336 			else
337 				screen_view_print(buf);
338 			e = TAILQ_NEXT(e, entries);
339 		}
340 		free(buf);
341 	}
342 	screen_view_print_end();
343 }
344 
345 void
346 menu_remove_all_entries(struct menu *m)
347 {
348 	struct menu_entry *e;
349 
350 	while ((e = TAILQ_FIRST(&m->list)) != NULL) {
351 		TAILQ_REMOVE(&m->list, e, entries);
352 		if (m->free_entry_data != NULL)
353 			m->free_entry_data(e->data);
354 		free(e);
355 	}
356 
357 	m->active = NULL;
358 	m->selected = NULL;
359 	m->top = NULL;
360 	m->nentries = 0;
361 }
362 
363 void
364 menu_remove_entry(struct menu *m, struct menu_entry *e)
365 {
366 	struct menu_entry *f;
367 
368 	if (m->active == e)
369 		m->active = NULL;
370 	if (m->top == e)
371 		m->top = TAILQ_NEXT(m->top, entries);
372 	if (m->selected == e) {
373 		if (TAILQ_NEXT(m->selected, entries) != NULL)
374 			m->selected = TAILQ_NEXT(m->selected, entries);
375 		else
376 			m->selected = TAILQ_PREV(m->selected, menu_list,
377 			    entries);
378 	}
379 
380 	/* Decrement the index of the entries after the specified entry. */
381 	f = e;
382 	while ((f = TAILQ_NEXT(f, entries)) != NULL)
383 		f->index--;
384 
385 	TAILQ_REMOVE(&m->list, e, entries);
386 	m->nentries--;
387 
388 	if (m->free_entry_data != NULL)
389 		m->free_entry_data(e->data);
390 	free(e);
391 }
392 
393 void
394 menu_remove_selected_entry(struct menu *m)
395 {
396 	if (m->selected != NULL)
397 		menu_remove_entry(m, m->selected);
398 }
399 
400 void
401 menu_scroll_down(struct menu *m, enum menu_scroll scroll)
402 {
403 	unsigned int nrows, nscroll;
404 
405 	if (m->nentries == 0)
406 		return;
407 
408 	nrows = screen_view_get_nrows();
409 	switch (scroll) {
410 	case MENU_SCROLL_HALF_PAGE:
411 		nscroll = (nrows + 1) / 2;
412 		break;
413 	case MENU_SCROLL_PAGE:
414 		nscroll = nrows;
415 		break;
416 	case MENU_SCROLL_LINE:
417 	default:
418 		nscroll = 1;
419 		break;
420 	}
421 
422 	if (m->top->index + nrows >= m->nentries)
423 		/*
424 		 * The last entry already is visible, so we cannot scroll down
425 		 * farther. Select the last entry instead.
426 		 */
427 		m->selected = TAILQ_LAST(&m->list, menu_list);
428 	else {
429 		/*
430 		 * Scroll down the requested number of lines or just as far as
431 		 * possible.
432 		 */
433 		while (nscroll-- > 0 && m->top->index + nrows < m->nentries)
434 			m->top = TAILQ_NEXT(m->top, entries);
435 
436 		/*
437 		 * Select the top entry if the selected entry is no longer
438 		 * visible.
439 		 */
440 		if (m->selected->index < m->top->index)
441 			m->selected = m->top;
442 	}
443 }
444 
445 void
446 menu_scroll_up(struct menu *m, enum menu_scroll scroll)
447 {
448 	unsigned int nrows, nscroll;
449 
450 	if (m->nentries == 0)
451 		return;
452 
453 	nrows = screen_view_get_nrows();
454 	switch (scroll) {
455 	case MENU_SCROLL_HALF_PAGE:
456 		nscroll = (nrows + 1) / 2;
457 		break;
458 	case MENU_SCROLL_PAGE:
459 		nscroll = nrows;
460 		break;
461 	case MENU_SCROLL_LINE:
462 	default:
463 		nscroll = 1;
464 		break;
465 	}
466 
467 	if (m->top->index == 0)
468 		/*
469 		 * The first entry already is visible, so we cannot scroll up
470 		 * farther. Select the first entry instead.
471 		 */
472 		m->selected = TAILQ_FIRST(&m->list);
473 	else {
474 		/*
475 		 * Scroll up the requested number of lines or just as far as
476 		 * possible.
477 		 */
478 		while (nscroll-- > 0 && m->top->index > 0)
479 			m->top = TAILQ_PREV(m->top, menu_list, entries);
480 
481 		/*
482 		 * Select the bottom entry if the selected entry is no longer
483 		 * visible.
484 		 */
485 		while (m->selected->index >= m->top->index + nrows)
486 			m->selected = TAILQ_PREV(m->selected, menu_list,
487 			    entries);
488 	}
489 }
490 
491 void
492 menu_search_next(struct menu *m, const char *s)
493 {
494 	struct menu_entry *e;
495 
496 	if (m->selected != NULL && m->search_entry_data != NULL) {
497 		e = m->selected;
498 		do {
499 			if (TAILQ_NEXT(e, entries) != NULL)
500 				e = TAILQ_NEXT(e, entries);
501 			else {
502 				e = TAILQ_FIRST(&m->list);
503 				msg_info("Search wrapped to top");
504 			}
505 
506 			if (m->search_entry_data(e->data, s) == 0) {
507 				m->selected = e;
508 				return;
509 			}
510 		} while (e != m->selected);
511 	}
512 
513 	msg_errx("Not found");
514 }
515 
516 void
517 menu_search_prev(struct menu *m, const char *s)
518 {
519 	struct menu_entry *e;
520 
521 	if (m->selected != NULL && m->search_entry_data != NULL) {
522 		e = m->selected;
523 		do {
524 			if (TAILQ_PREV(e, menu_list, entries) != NULL)
525 				e = TAILQ_PREV(e, menu_list, entries);
526 			else {
527 				e = TAILQ_LAST(&m->list, menu_list);
528 				msg_info("Search wrapped to bottom");
529 			}
530 
531 			if (m->search_entry_data(e->data, s) == 0) {
532 				m->selected = e;
533 				return;
534 			}
535 		} while (e != m->selected);
536 	}
537 
538 	msg_errx("Not found");
539 }
540 
541 void
542 menu_select_active_entry(struct menu *m)
543 {
544 	if (m->active != NULL)
545 		m->selected = m->active;
546 }
547 
548 void
549 menu_select_entry(struct menu *m, struct menu_entry *e)
550 {
551 	m->selected = e;
552 }
553 
554 void
555 menu_select_first_entry(struct menu *m)
556 {
557 	m->selected = TAILQ_FIRST(&m->list);
558 }
559 
560 void
561 menu_select_last_entry(struct menu *m)
562 {
563 	m->selected = TAILQ_LAST(&m->list, menu_list);
564 }
565 
566 void
567 menu_select_next_entry(struct menu *m)
568 {
569 	if (m->selected != NULL && TAILQ_NEXT(m->selected, entries) != NULL)
570 		m->selected = TAILQ_NEXT(m->selected, entries);
571 }
572 
573 void
574 menu_select_prev_entry(struct menu *m)
575 {
576 	if (m->selected != NULL &&
577 	    TAILQ_PREV(m->selected, menu_list, entries) != NULL)
578 		m->selected = TAILQ_PREV(m->selected, menu_list, entries);
579 }
580