nav.c 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. /** @file nav.c
  2. * @brief Navigation
  3. *
  4. * Example:
  5. * <ACTION sym="nav">
  6. * pop
  7. * push /view_name
  8. * </ACTION>
  9. *
  10. * Possible navigation commands:
  11. * * push <view_name> - Push "view_name" view to new level of path and change
  12. * current path to it.
  13. * * pop [num] - Pop up path levels. Optional "num" argument specifies number
  14. * of levels to pop. Default is 1.
  15. * * top - Pop up to first path level.
  16. * * replace <view_name> - Replace current view by the specified one.
  17. * * exit - Exit klish.
  18. */
  19. #include <assert.h>
  20. #include <stdio.h>
  21. #include <unistd.h>
  22. #include <string.h>
  23. #include <stdlib.h>
  24. #include <errno.h>
  25. #include <faux/str.h>
  26. #include <faux/list.h>
  27. #include <faux/conv.h>
  28. #include <klish/kcontext.h>
  29. #include <klish/kentry.h>
  30. #include <klish/ksession.h>
  31. #include <klish/kpath.h>
  32. #include <klish/kscheme.h>
  33. int klish_nav(kcontext_t *context)
  34. {
  35. const char *script = NULL;
  36. ksession_t *session = NULL;
  37. kpath_t *path = NULL;
  38. const char *str = NULL;
  39. char *line = NULL;
  40. // Navigation is suitable only for command actions but not for
  41. // PTYPEs, CONDitions i.e. SERVICE_ACTIONS.
  42. assert(kcontext_type(context) == KCONTEXT_TYPE_ACTION);
  43. script = kcontext_script(context);
  44. if (faux_str_is_empty(script)) // No navigation commands. It's not an error.
  45. return 0;
  46. session = kcontext_session(context);
  47. assert(session);
  48. path = ksession_path(session);
  49. // Iterate lines from "script". Each line is navigation command.
  50. str = script;
  51. while ((line = faux_str_getline(str, &str))) {
  52. faux_argv_t *argv = faux_argv_new();
  53. ssize_t lnum = faux_argv_parse(argv, line);
  54. const char *nav_cmd = NULL;
  55. faux_str_free(line);
  56. if (lnum < 1) {
  57. faux_argv_free(argv);
  58. continue;
  59. }
  60. nav_cmd = faux_argv_index(argv, 0);
  61. // exit
  62. if (faux_str_casecmp(nav_cmd, "exit") == 0) {
  63. ksession_set_done(session, BOOL_TRUE);
  64. // "Exit" supposes another navigation commands have no
  65. // meaning. So break the loop.
  66. faux_argv_free(argv);
  67. break;
  68. // top
  69. } else if (faux_str_casecmp(nav_cmd, "top") == 0) {
  70. while (kpath_len(path) > 1) {
  71. if (!kpath_pop(path)) {
  72. faux_argv_free(argv);
  73. return -1;
  74. }
  75. }
  76. // pop [number_of_levels]
  77. } else if (faux_str_casecmp(nav_cmd, "pop") == 0) {
  78. size_t i = 0;
  79. size_t lnum = 1; // Default levels to pop
  80. const char *lnum_str = faux_argv_index(argv, 1);
  81. // Level number is specified
  82. if (lnum_str) {
  83. unsigned char val = 0;
  84. // 8 bit unsigned integer is enough
  85. if (!faux_conv_atouc(lnum_str, &val, 0)) {
  86. faux_argv_free(argv);
  87. return -1;
  88. }
  89. lnum = val;
  90. }
  91. // Don't pop upper than top level
  92. // Such "pop" means exit
  93. if (lnum > (kpath_len(path) - 1)) {
  94. ksession_set_done(session, BOOL_TRUE);
  95. faux_argv_free(argv);
  96. break;
  97. }
  98. // Pop levels
  99. for (i = 0; i < lnum; i++) {
  100. if (!kpath_pop(path)) {
  101. faux_argv_free(argv);
  102. return -1;
  103. }
  104. }
  105. // push <view_name>
  106. } else if (faux_str_casecmp(nav_cmd, "push") == 0) {
  107. const char *view_name = faux_argv_index(argv, 1);
  108. kentry_t *new_view = NULL;
  109. if (!view_name) {
  110. faux_argv_free(argv);
  111. return -1;
  112. }
  113. new_view = kscheme_find_entry_by_path(
  114. ksession_scheme(session), view_name);
  115. if (!new_view) {
  116. faux_argv_free(argv);
  117. return -1;
  118. }
  119. if (!kpath_push(path, klevel_new(new_view))) {
  120. faux_argv_free(argv);
  121. return -1;
  122. }
  123. // replace <view_name>
  124. } else if (faux_str_casecmp(nav_cmd, "replace") == 0) {
  125. const char *view_name = faux_argv_index(argv, 1);
  126. kentry_t *new_view = NULL;
  127. if (!view_name) {
  128. faux_argv_free(argv);
  129. return -1;
  130. }
  131. new_view = kscheme_find_entry_by_path(
  132. ksession_scheme(session), view_name);
  133. if (!new_view) {
  134. faux_argv_free(argv);
  135. return -1;
  136. }
  137. if (!kpath_pop(path)) {
  138. faux_argv_free(argv);
  139. return -1;
  140. }
  141. if (!kpath_push(path, klevel_new(new_view))) {
  142. faux_argv_free(argv);
  143. return -1;
  144. }
  145. // Unknown command
  146. } else {
  147. faux_argv_free(argv);
  148. return -1;
  149. }
  150. faux_argv_free(argv);
  151. }
  152. return 0;
  153. }