Annotation of src/usr.bin/tmux/mode-tree.c, Revision 1.1
1.1 ! nicm 1: /* $OpenBSD$ */
! 2:
! 3: /*
! 4: * Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@gmail.com>
! 5: *
! 6: * Permission to use, copy, modify, and distribute this software for any
! 7: * purpose with or without fee is hereby granted, provided that the above
! 8: * copyright notice and this permission notice appear in all copies.
! 9: *
! 10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
! 11: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
! 12: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
! 13: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
! 14: * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
! 15: * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
! 16: * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
! 17: */
! 18:
! 19: #include <sys/types.h>
! 20:
! 21: #include <ctype.h>
! 22: #include <stdio.h>
! 23: #include <stdlib.h>
! 24: #include <string.h>
! 25:
! 26: #include "tmux.h"
! 27:
! 28: struct mode_tree_item;
! 29: TAILQ_HEAD(mode_tree_list, mode_tree_item);
! 30:
! 31: struct mode_tree_data {
! 32: struct window_pane *wp;
! 33: void *modedata;
! 34:
! 35: const char **sort_list;
! 36: u_int sort_size;
! 37: u_int sort_type;
! 38:
! 39: void (*buildcb)(void *, u_int, uint64_t *);
! 40: struct screen *(*drawcb)(void *, void *, u_int, u_int);
! 41:
! 42: struct mode_tree_list children;
! 43: struct mode_tree_list saved;
! 44:
! 45: struct mode_tree_line *line_list;
! 46: u_int line_size;
! 47:
! 48: u_int depth;
! 49:
! 50: u_int width;
! 51: u_int height;
! 52:
! 53: u_int offset;
! 54: u_int current;
! 55:
! 56: struct screen screen;
! 57: };
! 58:
! 59: struct mode_tree_item {
! 60: struct mode_tree_item *parent;
! 61: void *itemdata;
! 62: u_int line;
! 63:
! 64: uint64_t tag;
! 65: const char *name;
! 66: const char *text;
! 67:
! 68: int expanded;
! 69: int tagged;
! 70:
! 71: struct mode_tree_list children;
! 72: TAILQ_ENTRY(mode_tree_item) entry;
! 73: };
! 74:
! 75: struct mode_tree_line {
! 76: struct mode_tree_item *item;
! 77: u_int depth;
! 78: int last;
! 79: int flat;
! 80: };
! 81:
! 82: static void mode_tree_free_items(struct mode_tree_list *);
! 83:
! 84: static struct mode_tree_item *
! 85: mode_tree_find_item(struct mode_tree_list *mtl, uint64_t tag)
! 86: {
! 87: struct mode_tree_item *mti, *child;
! 88:
! 89: TAILQ_FOREACH(mti, mtl, entry) {
! 90: if (mti->tag == tag)
! 91: return (mti);
! 92: child = mode_tree_find_item(&mti->children, tag);
! 93: if (child != NULL)
! 94: return (child);
! 95: }
! 96: return (NULL);
! 97: }
! 98:
! 99: static void
! 100: mode_tree_free_item(struct mode_tree_item *mti)
! 101: {
! 102: mode_tree_free_items(&mti->children);
! 103:
! 104: free((void *)mti->name);
! 105: free((void *)mti->text);
! 106:
! 107: free(mti);
! 108: }
! 109:
! 110: static void
! 111: mode_tree_free_items(struct mode_tree_list *mtl)
! 112: {
! 113: struct mode_tree_item *mti, *mti1;
! 114:
! 115: TAILQ_FOREACH_SAFE(mti, mtl, entry, mti1) {
! 116: TAILQ_REMOVE(mtl, mti, entry);
! 117: mode_tree_free_item(mti);
! 118: }
! 119: }
! 120:
! 121: static void
! 122: mode_tree_clear_lines(struct mode_tree_data *mtd)
! 123: {
! 124: free(mtd->line_list);
! 125: mtd->line_list = NULL;
! 126: mtd->line_size = 0;
! 127: }
! 128:
! 129: static void
! 130: mode_tree_build_lines(struct mode_tree_data *mtd,
! 131: struct mode_tree_list *mtl, u_int depth)
! 132: {
! 133: struct mode_tree_item *mti;
! 134: struct mode_tree_line *line;
! 135: u_int i;
! 136: int flat = 1;
! 137:
! 138: mtd->depth = depth;
! 139: TAILQ_FOREACH(mti, mtl, entry) {
! 140: mtd->line_list = xreallocarray(mtd->line_list,
! 141: mtd->line_size + 1, sizeof *mtd->line_list);
! 142:
! 143: line = &mtd->line_list[mtd->line_size++];
! 144: line->item = mti;
! 145: line->depth = depth;
! 146: line->last = (mti == TAILQ_LAST(mtl, mode_tree_list));
! 147:
! 148: mti->line = (mtd->line_size - 1);
! 149: if (!TAILQ_EMPTY(&mti->children))
! 150: flat = 0;
! 151: if (mti->expanded)
! 152: mode_tree_build_lines(mtd, &mti->children, depth + 1);
! 153: }
! 154: TAILQ_FOREACH(mti, mtl, entry) {
! 155: for (i = 0; i < mtd->line_size; i++) {
! 156: line = &mtd->line_list[i];
! 157: if (line->item == mti)
! 158: line->flat = flat;
! 159: }
! 160: }
! 161: }
! 162:
! 163: static void
! 164: mode_tree_clear_tagged(struct mode_tree_list *mtl)
! 165: {
! 166: struct mode_tree_item *mti;
! 167:
! 168: TAILQ_FOREACH(mti, mtl, entry) {
! 169: mti->tagged = 0;
! 170: mode_tree_clear_tagged(&mti->children);
! 171: }
! 172: }
! 173:
! 174: void
! 175: mode_tree_up(struct mode_tree_data *mtd, int wrap)
! 176: {
! 177: if (mtd->current == 0) {
! 178: if (wrap) {
! 179: mtd->current = mtd->line_size - 1;
! 180: if (mtd->line_size >= mtd->height)
! 181: mtd->offset = mtd->line_size - mtd->height;
! 182: }
! 183: } else {
! 184: mtd->current--;
! 185: if (mtd->current < mtd->offset)
! 186: mtd->offset--;
! 187: }
! 188: }
! 189:
! 190: void
! 191: mode_tree_down(struct mode_tree_data *mtd, int wrap)
! 192: {
! 193: if (mtd->current == mtd->line_size - 1) {
! 194: if (wrap) {
! 195: mtd->current = 0;
! 196: mtd->offset = 0;
! 197: }
! 198: } else {
! 199: mtd->current++;
! 200: if (mtd->current > mtd->offset + mtd->height - 1)
! 201: mtd->offset++;
! 202: }
! 203: }
! 204:
! 205: void *
! 206: mode_tree_get_current(struct mode_tree_data *mtd)
! 207: {
! 208: return (mtd->line_list[mtd->current].item->itemdata);
! 209: }
! 210:
! 211: u_int
! 212: mode_tree_count_tagged(struct mode_tree_data *mtd)
! 213: {
! 214: struct mode_tree_item *mti;
! 215: u_int i, tagged;
! 216:
! 217: tagged = 0;
! 218: for (i = 0; i < mtd->line_size; i++) {
! 219: mti = mtd->line_list[i].item;
! 220: if (mti->tagged)
! 221: tagged++;
! 222: }
! 223: return (tagged);
! 224: }
! 225:
! 226: void
! 227: mode_tree_each_tagged(struct mode_tree_data *mtd, void (*cb)(void *, void *,
! 228: key_code), key_code key, int current)
! 229: {
! 230: struct mode_tree_item *mti;
! 231: u_int i;
! 232: int fired;
! 233:
! 234: fired = 0;
! 235: for (i = 0; i < mtd->line_size; i++) {
! 236: mti = mtd->line_list[i].item;
! 237: if (mti->tagged) {
! 238: fired = 1;
! 239: cb(mtd->modedata, mti->itemdata, key);
! 240: }
! 241: }
! 242: if (!fired && current) {
! 243: mti = mtd->line_list[mtd->current].item;
! 244: cb(mtd->modedata, mti->itemdata, key);
! 245: }
! 246: }
! 247:
! 248: struct mode_tree_data *
! 249: mode_tree_start(struct window_pane *wp, void (*buildcb)(void *, u_int,
! 250: uint64_t *), struct screen *(*drawcb)(void *, void *, u_int, u_int),
! 251: void *modedata, const char **sort_list, u_int sort_size, struct screen **s)
! 252: {
! 253: struct mode_tree_data *mtd;
! 254:
! 255: mtd = xcalloc(1, sizeof *mtd);
! 256: mtd->wp = wp;
! 257: mtd->modedata = modedata;
! 258:
! 259: mtd->sort_list = sort_list;
! 260: mtd->sort_size = sort_size;
! 261: mtd->sort_type = 0;
! 262:
! 263: mtd->buildcb = buildcb;
! 264: mtd->drawcb = drawcb;
! 265:
! 266: TAILQ_INIT(&mtd->children);
! 267:
! 268: *s = &mtd->screen;
! 269: screen_init(*s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0);
! 270: (*s)->mode &= ~MODE_CURSOR;
! 271:
! 272: return (mtd);
! 273: }
! 274:
! 275: void
! 276: mode_tree_build(struct mode_tree_data *mtd)
! 277: {
! 278: struct screen *s = &mtd->screen;
! 279: uint64_t tag;
! 280: u_int i;
! 281:
! 282: if (mtd->line_list != NULL)
! 283: tag = mtd->line_list[mtd->current].item->tag;
! 284: else
! 285: tag = 0;
! 286:
! 287: TAILQ_CONCAT(&mtd->saved, &mtd->children, entry);
! 288: TAILQ_INIT(&mtd->children);
! 289:
! 290: mtd->buildcb(mtd->modedata, mtd->sort_type, &tag);
! 291:
! 292: mode_tree_free_items(&mtd->saved);
! 293: TAILQ_INIT(&mtd->saved);
! 294:
! 295: mode_tree_clear_lines(mtd);
! 296: mode_tree_build_lines(mtd, &mtd->children, 0);
! 297:
! 298: for (i = 0; i < mtd->line_size; i++) {
! 299: if (mtd->line_list[i].item->tag == tag)
! 300: break;
! 301: }
! 302: if (i != mtd->line_size)
! 303: mtd->current = i;
! 304: else {
! 305: mtd->current = 0;
! 306: mtd->offset = 0;
! 307: }
! 308:
! 309: mtd->width = screen_size_x(s);
! 310: mtd->height = (screen_size_y(s) / 3) * 2;
! 311: if (mtd->height > mtd->line_size)
! 312: mtd->height = screen_size_y(s) / 2;
! 313: if (mtd->height < 10)
! 314: mtd->height = screen_size_y(s);
! 315: if (screen_size_y(s) - mtd->height < 2)
! 316: mtd->height = screen_size_y(s);
! 317: }
! 318:
! 319: void
! 320: mode_tree_free(struct mode_tree_data *mtd)
! 321: {
! 322: mode_tree_free_items(&mtd->children);
! 323: mode_tree_clear_lines(mtd);
! 324: screen_free(&mtd->screen);
! 325: free(mtd);
! 326: }
! 327:
! 328: void
! 329: mode_tree_resize(struct mode_tree_data *mtd, u_int sx, u_int sy)
! 330: {
! 331: struct screen *s = &mtd->screen;
! 332:
! 333: screen_resize(s, sx, sy, 0);
! 334:
! 335: mode_tree_build(mtd);
! 336: mode_tree_draw(mtd);
! 337:
! 338: mtd->wp->flags |= PANE_REDRAW;
! 339: }
! 340:
! 341: struct mode_tree_item *
! 342: mode_tree_add(struct mode_tree_data *mtd, struct mode_tree_item *parent,
! 343: void *itemdata, uint64_t tag, const char *name, const char *text,
! 344: int expanded)
! 345: {
! 346: struct mode_tree_item *mti, *saved;
! 347:
! 348: log_debug("%s: %llu, %s %s", __func__, (unsigned long long)tag,
! 349: name, text);
! 350:
! 351: mti = xcalloc(1, sizeof *mti);
! 352: mti->parent = parent;
! 353: mti->itemdata = itemdata;
! 354:
! 355: mti->tag = tag;
! 356: mti->name = xstrdup(name);
! 357: mti->text = xstrdup(text);
! 358:
! 359: saved = mode_tree_find_item(&mtd->saved, tag);
! 360: if (saved != NULL) {
! 361: if (parent == NULL || (parent != NULL && parent->expanded))
! 362: mti->tagged = saved->tagged;
! 363: mti->expanded = saved->expanded;
! 364: } else if (expanded == -1)
! 365: mti->expanded = 1;
! 366: else
! 367: mti->expanded = expanded;
! 368:
! 369: TAILQ_INIT(&mti->children);
! 370:
! 371: if (parent != NULL)
! 372: TAILQ_INSERT_TAIL(&parent->children, mti, entry);
! 373: else
! 374: TAILQ_INSERT_TAIL(&mtd->children, mti, entry);
! 375:
! 376: return (mti);
! 377: }
! 378:
! 379: void
! 380: mode_tree_remove(struct mode_tree_data *mtd, struct mode_tree_item *mti)
! 381: {
! 382: struct mode_tree_item *parent = mti->parent;
! 383:
! 384: if (parent != NULL)
! 385: TAILQ_REMOVE(&parent->children, mti, entry);
! 386: else
! 387: TAILQ_REMOVE(&mtd->children, mti, entry);
! 388: mode_tree_free_item(mti);
! 389: }
! 390:
! 391: void
! 392: mode_tree_draw(struct mode_tree_data *mtd)
! 393: {
! 394: struct window_pane *wp = mtd->wp;
! 395: struct screen *s = &mtd->screen, *box;
! 396: struct mode_tree_line *line;
! 397: struct mode_tree_item *mti;
! 398: struct options *oo = wp->window->options;
! 399: struct screen_write_ctx ctx;
! 400: struct grid_cell gc0, gc;
! 401: u_int w, h, i, j, sy, box_x, box_y;
! 402: char *text, *start, key[7];
! 403: const char *tag, *symbol;
! 404: size_t size;
! 405: int keylen;
! 406:
! 407: if (mtd->line_size == 0)
! 408: return;
! 409:
! 410: memcpy(&gc0, &grid_default_cell, sizeof gc0);
! 411: memcpy(&gc, &grid_default_cell, sizeof gc);
! 412: style_apply(&gc, oo, "mode-style");
! 413:
! 414: w = mtd->width;
! 415: h = mtd->height;
! 416:
! 417: screen_write_start(&ctx, NULL, s);
! 418: screen_write_clearscreen(&ctx, 8);
! 419:
! 420: if (mtd->line_size > 10)
! 421: keylen = 6;
! 422: else
! 423: keylen = 4;
! 424:
! 425: for (i = 0; i < mtd->line_size; i++) {
! 426: if (i < mtd->offset)
! 427: continue;
! 428: if (i > mtd->offset + h - 1)
! 429: break;
! 430:
! 431: line = &mtd->line_list[i];
! 432: mti = line->item;
! 433:
! 434: screen_write_cursormove(&ctx, 0, i - mtd->offset);
! 435:
! 436: if (i < 10)
! 437: snprintf(key, sizeof key, "(%c)", '0' + i);
! 438: else if (i < 36)
! 439: snprintf(key, sizeof key, "(M-%c)", 'a' + (i - 10));
! 440: else
! 441: *key = '\0';
! 442:
! 443: if (line->flat)
! 444: symbol = "";
! 445: else if (TAILQ_EMPTY(&mti->children))
! 446: symbol = " ";
! 447: else if (mti->expanded)
! 448: symbol = "- ";
! 449: else
! 450: symbol = "+ ";
! 451:
! 452: if (line->depth == 0)
! 453: start = xstrdup(symbol);
! 454: else {
! 455: size = (4 * line->depth) + 32;
! 456:
! 457: start = xcalloc(1, size);
! 458: for (j = 1; j < line->depth; j++) {
! 459: if (mti->parent != NULL &&
! 460: mtd->line_list[mti->parent->line].last)
! 461: strlcat(start, " ", size);
! 462: else
! 463: strlcat(start, "\001x\001 ", size);
! 464: }
! 465: if (line->last)
! 466: strlcat(start, "\001mq\001> ", size);
! 467: else
! 468: strlcat(start, "\001tq\001> ", size);
! 469: strlcat(start, symbol, size);
! 470: }
! 471:
! 472: if (mti->tagged)
! 473: tag = "*";
! 474: else
! 475: tag = "";
! 476: xasprintf(&text, "%-*s%s%s%s: %s", keylen, key, start,
! 477: mti->name, tag, mti->text);
! 478: free(start);
! 479:
! 480: if (mti->tagged) {
! 481: gc.attr ^= GRID_ATTR_BRIGHT;
! 482: gc0.attr ^= GRID_ATTR_BRIGHT;
! 483: }
! 484:
! 485: if (i != mtd->current) {
! 486: screen_write_puts(&ctx, &gc0, "%.*s", w, text);
! 487: screen_write_clearendofline(&ctx, 8);
! 488: } else
! 489: screen_write_puts(&ctx, &gc, "%-*.*s", w, w, text);
! 490: free(text);
! 491:
! 492: if (mti->tagged) {
! 493: gc.attr ^= GRID_ATTR_BRIGHT;
! 494: gc0.attr ^= GRID_ATTR_BRIGHT;
! 495: }
! 496: }
! 497:
! 498: sy = screen_size_y(s);
! 499: if (sy <= 4 || h <= 4 || sy - h <= 4 || w <= 4) {
! 500: screen_write_stop(&ctx);
! 501: return;
! 502: }
! 503:
! 504: line = &mtd->line_list[mtd->current];
! 505: mti = line->item;
! 506:
! 507: screen_write_cursormove(&ctx, 0, h);
! 508: screen_write_box(&ctx, w, sy - h);
! 509:
! 510: xasprintf(&text, " %s (sort: %s) ", mti->name,
! 511: mtd->sort_list[mtd->sort_type]);
! 512: if (w - 2 >= strlen(text)) {
! 513: screen_write_cursormove(&ctx, 1, h);
! 514: screen_write_puts(&ctx, &gc0, "%s", text);
! 515: }
! 516: free(text);
! 517:
! 518: box_x = w - 4;
! 519: box_y = sy - h - 2;
! 520:
! 521: box = mtd->drawcb(mtd->modedata, mti->itemdata, box_x, box_y);
! 522: if (box != NULL) {
! 523: screen_write_cursormove(&ctx, 2, h + 1);
! 524: screen_write_copy(&ctx, box, 0, 0, box_x, box_y, NULL, NULL);
! 525:
! 526: screen_free(box);
! 527: }
! 528:
! 529: screen_write_stop(&ctx);
! 530: }
! 531:
! 532: int
! 533: mode_tree_key(struct mode_tree_data *mtd, key_code *key, struct mouse_event *m)
! 534: {
! 535: struct mode_tree_line *line;
! 536: struct mode_tree_item *current, *parent;
! 537: u_int i, x, y;
! 538: int choice;
! 539: key_code tmp;
! 540:
! 541: if (*key == KEYC_MOUSEDOWN1_PANE) {
! 542: if (cmd_mouse_at(mtd->wp, m, &x, &y, 0) != 0) {
! 543: *key = KEYC_NONE;
! 544: return (0);
! 545: }
! 546: if (x > mtd->width || y > mtd->height) {
! 547: *key = KEYC_NONE;
! 548: return (0);
! 549: }
! 550: if (mtd->offset + y < mtd->line_size) {
! 551: mtd->current = mtd->offset + y;
! 552: *key = '\r';
! 553: return (0);
! 554: }
! 555: }
! 556:
! 557: line = &mtd->line_list[mtd->current];
! 558: current = line->item;
! 559:
! 560: choice = -1;
! 561: if (*key >= '0' && *key <= '9')
! 562: choice = (*key) - '0';
! 563: else if (((*key) & KEYC_MASK_MOD) == KEYC_ESCAPE) {
! 564: tmp = (*key) & KEYC_MASK_KEY;
! 565: if (tmp >= 'a' && tmp <= 'z')
! 566: choice = 10 + (tmp - 'a');
! 567: }
! 568: if (choice != -1) {
! 569: if ((u_int)choice > mtd->line_size - 1) {
! 570: *key = KEYC_NONE;
! 571: return (0);
! 572: }
! 573: mtd->current = choice;
! 574: *key = '\r';
! 575: return (0);
! 576: }
! 577:
! 578: switch (*key) {
! 579: case 'q':
! 580: case '\033': /* Escape */
! 581: return (1);
! 582: case KEYC_UP:
! 583: case 'k':
! 584: case KEYC_WHEELUP_PANE:
! 585: mode_tree_up(mtd, 1);
! 586: break;
! 587: case KEYC_DOWN:
! 588: case 'j':
! 589: case KEYC_WHEELDOWN_PANE:
! 590: mode_tree_down(mtd, 1);
! 591: break;
! 592: case KEYC_PPAGE:
! 593: case '\002': /* C-b */
! 594: for (i = 0; i < mtd->height; i++) {
! 595: if (mtd->current == 0)
! 596: break;
! 597: mode_tree_up(mtd, 1);
! 598: }
! 599: break;
! 600: case KEYC_NPAGE:
! 601: case '\006': /* C-f */
! 602: for (i = 0; i < mtd->height; i++) {
! 603: if (mtd->current == mtd->line_size - 1)
! 604: break;
! 605: mode_tree_down(mtd, 1);
! 606: }
! 607: break;
! 608: case KEYC_HOME:
! 609: mtd->current = 0;
! 610: mtd->offset = 0;
! 611: break;
! 612: case KEYC_END:
! 613: mtd->current = mtd->line_size - 1;
! 614: if (mtd->current > mtd->height - 1)
! 615: mtd->offset = mtd->current - mtd->height;
! 616: else
! 617: mtd->offset = 0;
! 618: break;
! 619: case 't':
! 620: /*
! 621: * Do not allow parents and children to both be tagged: untag
! 622: * all parents and children of current.
! 623: */
! 624: if (!current->tagged) {
! 625: parent = current->parent;
! 626: while (parent != NULL) {
! 627: parent->tagged = 0;
! 628: parent = parent->parent;
! 629: }
! 630: mode_tree_clear_tagged(¤t->children);
! 631: current->tagged = 1;
! 632: } else
! 633: current->tagged = 0;
! 634: mode_tree_down(mtd, 0);
! 635: break;
! 636: case 'T':
! 637: for (i = 0; i < mtd->line_size; i++)
! 638: mtd->line_list[i].item->tagged = 0;
! 639: break;
! 640: case '\024': /* C-t */
! 641: for (i = 0; i < mtd->line_size; i++) {
! 642: if (mtd->line_list[i].item->parent == NULL)
! 643: mtd->line_list[i].item->tagged = 1;
! 644: else
! 645: mtd->line_list[i].item->tagged = 0;
! 646: }
! 647: break;
! 648: case 'O':
! 649: mtd->sort_type++;
! 650: if (mtd->sort_type == mtd->sort_size)
! 651: mtd->sort_type = 0;
! 652: mode_tree_build(mtd);
! 653: break;
! 654: case KEYC_LEFT:
! 655: case '-':
! 656: if (line->flat || !current->expanded)
! 657: current = current->parent;
! 658: if (current == NULL)
! 659: mode_tree_up(mtd, 0);
! 660: else {
! 661: current->expanded = 0;
! 662: mtd->current = current->line;
! 663: mode_tree_build(mtd);
! 664: }
! 665: break;
! 666: case KEYC_RIGHT:
! 667: case '+':
! 668: if (line->flat || current->expanded)
! 669: mode_tree_down(mtd, 0);
! 670: else if (!line->flat) {
! 671: current->expanded = 1;
! 672: mode_tree_build(mtd);
! 673: }
! 674: break;
! 675: }
! 676: return (0);
! 677: }
! 678:
! 679: void
! 680: mode_tree_run_command(struct client *c, struct cmd_find_state *fs,
! 681: const char *template, const char *name)
! 682: {
! 683: struct cmdq_item *new_item;
! 684: struct cmd_list *cmdlist;
! 685: char *command, *cause;
! 686:
! 687: command = cmd_template_replace(template, name, 1);
! 688: if (command == NULL || *command == '\0')
! 689: return;
! 690:
! 691: cmdlist = cmd_string_parse(command, NULL, 0, &cause);
! 692: if (cmdlist == NULL) {
! 693: if (cause != NULL && c != NULL) {
! 694: *cause = toupper((u_char)*cause);
! 695: status_message_set(c, "%s", cause);
! 696: }
! 697: free(cause);
! 698: } else {
! 699: new_item = cmdq_get_command(cmdlist, fs, NULL, 0);
! 700: cmdq_append(c, new_item);
! 701: cmd_list_free(cmdlist);
! 702: }
! 703:
! 704: free(command);
! 705: }