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