Simple embedded shell with runtime pluggable commands.

Dependents:   DataBus2018

Implements a simple unix-like shell for embedded systems with a pluggable command architecture.

Committer:
shimniok
Date:
Mon Dec 24 18:35:29 2018 +0000
Revision:
23:b1e49cfcaef6
Parent:
21:5d7ac1f0b842
Child:
24:004e33abce37
add send command

Who changed what in which revision?

UserRevisionLine numberNew contents of line
shimniok 21:5d7ac1f0b842 1 #include "SimpleShell.h"
shimniok 21:5d7ac1f0b842 2 #include <ctype.h>
shimniok 21:5d7ac1f0b842 3 #include <string>
shimniok 21:5d7ac1f0b842 4
shimniok 21:5d7ac1f0b842 5 #define ESC 0x1b
shimniok 21:5d7ac1f0b842 6 #define UP 0x41
shimniok 21:5d7ac1f0b842 7 #define DOWN 0x42
shimniok 21:5d7ac1f0b842 8 #define RIGHT 0x43
shimniok 21:5d7ac1f0b842 9 #define LEFT 0x44
shimniok 21:5d7ac1f0b842 10 #define HOME 0x31
shimniok 21:5d7ac1f0b842 11 #define INS 0x32
shimniok 21:5d7ac1f0b842 12 #define DEL 0x33
shimniok 21:5d7ac1f0b842 13 #define PGUP 0x35
shimniok 21:5d7ac1f0b842 14 #define PGDN 0x36
shimniok 21:5d7ac1f0b842 15 #define TAIL 0x7e
shimniok 21:5d7ac1f0b842 16
shimniok 21:5d7ac1f0b842 17
shimniok 21:5d7ac1f0b842 18 char *SimpleShell::canon(char *path) {
shimniok 21:5d7ac1f0b842 19 static char result[MAXBUF];
shimniok 21:5d7ac1f0b842 20
shimniok 21:5d7ac1f0b842 21 if (path[0] != '/') {
shimniok 21:5d7ac1f0b842 22 strncpy(result, _cwd, MAXBUF);
shimniok 21:5d7ac1f0b842 23 strcat(result, "/");
shimniok 21:5d7ac1f0b842 24 }
shimniok 21:5d7ac1f0b842 25 strcat(result, path);
shimniok 21:5d7ac1f0b842 26
shimniok 21:5d7ac1f0b842 27 return result;
shimniok 21:5d7ac1f0b842 28 }
shimniok 21:5d7ac1f0b842 29
shimniok 21:5d7ac1f0b842 30
shimniok 23:b1e49cfcaef6 31 char *SimpleShell::basename(char *path) {
shimniok 23:b1e49cfcaef6 32 return path;
shimniok 23:b1e49cfcaef6 33 }
shimniok 23:b1e49cfcaef6 34
shimniok 23:b1e49cfcaef6 35
shimniok 23:b1e49cfcaef6 36
shimniok 21:5d7ac1f0b842 37 SimpleShell::SimpleShell()
shimniok 21:5d7ac1f0b842 38 {
shimniok 21:5d7ac1f0b842 39 lookupEnd = 0;
shimniok 21:5d7ac1f0b842 40
shimniok 21:5d7ac1f0b842 41 // Built-in shell commands
shimniok 21:5d7ac1f0b842 42 attach(callback(this, &SimpleShell::help), "help");
shimniok 21:5d7ac1f0b842 43 attach(callback(this, &SimpleShell::pwd), "pwd");
shimniok 21:5d7ac1f0b842 44 attach(callback(this, &SimpleShell::cat), "cat");
shimniok 21:5d7ac1f0b842 45 attach(callback(this, &SimpleShell::cd), "cd");
shimniok 21:5d7ac1f0b842 46 attach(callback(this, &SimpleShell::rm), "rm");
shimniok 21:5d7ac1f0b842 47 attach(callback(this, &SimpleShell::touch), "touch");
shimniok 21:5d7ac1f0b842 48 attach(callback(this, &SimpleShell::ls), "ls");
shimniok 23:b1e49cfcaef6 49 attach(callback(this, &SimpleShell::send), "send");
shimniok 21:5d7ac1f0b842 50 }
shimniok 21:5d7ac1f0b842 51
shimniok 21:5d7ac1f0b842 52
shimniok 21:5d7ac1f0b842 53 void SimpleShell::help(int argc, char **argv)
shimniok 21:5d7ac1f0b842 54 {
shimniok 21:5d7ac1f0b842 55 printf("Available commands: ");
shimniok 21:5d7ac1f0b842 56 for (int i=0; i < lookupEnd; i++) {
shimniok 21:5d7ac1f0b842 57 printf("%s ", lookup[i].command);
shimniok 21:5d7ac1f0b842 58 }
shimniok 21:5d7ac1f0b842 59 printf("\n\n");
shimniok 21:5d7ac1f0b842 60 }
shimniok 21:5d7ac1f0b842 61
shimniok 21:5d7ac1f0b842 62
shimniok 21:5d7ac1f0b842 63 void SimpleShell::cd(int argc, char **argv)
shimniok 21:5d7ac1f0b842 64 {
shimniok 21:5d7ac1f0b842 65 if (argc == 2) {
shimniok 21:5d7ac1f0b842 66 strncpy(_cwd, argv[1], MAXBUF);
shimniok 21:5d7ac1f0b842 67 } else {
shimniok 21:5d7ac1f0b842 68 puts("usage: cd directory");
shimniok 21:5d7ac1f0b842 69 }
shimniok 21:5d7ac1f0b842 70 return;
shimniok 21:5d7ac1f0b842 71 }
shimniok 21:5d7ac1f0b842 72
shimniok 21:5d7ac1f0b842 73
shimniok 21:5d7ac1f0b842 74 void SimpleShell::pwd(int argc, char **argv)
shimniok 21:5d7ac1f0b842 75 {
shimniok 21:5d7ac1f0b842 76 puts(_cwd);
shimniok 21:5d7ac1f0b842 77 return;
shimniok 21:5d7ac1f0b842 78 }
shimniok 21:5d7ac1f0b842 79
shimniok 21:5d7ac1f0b842 80
shimniok 21:5d7ac1f0b842 81 void SimpleShell::ls(int argc, char **argv)
shimniok 21:5d7ac1f0b842 82 {
shimniok 21:5d7ac1f0b842 83 DIR *d;
shimniok 21:5d7ac1f0b842 84 struct dirent *p;
shimniok 21:5d7ac1f0b842 85 char *path;
shimniok 21:5d7ac1f0b842 86
shimniok 21:5d7ac1f0b842 87 if (argc == 1) {
shimniok 21:5d7ac1f0b842 88 path = _cwd;
shimniok 21:5d7ac1f0b842 89 } else if (argc == 2) {
shimniok 21:5d7ac1f0b842 90 path = argv[1];
shimniok 21:5d7ac1f0b842 91 } else {
shimniok 21:5d7ac1f0b842 92 puts("usage: ls [directory]");
shimniok 21:5d7ac1f0b842 93 return;
shimniok 21:5d7ac1f0b842 94 }
shimniok 21:5d7ac1f0b842 95
shimniok 21:5d7ac1f0b842 96 int cols=0;
shimniok 21:5d7ac1f0b842 97 if ((d = opendir(path)) != NULL) {
shimniok 21:5d7ac1f0b842 98 while ((p = readdir(d)) != NULL) {
shimniok 21:5d7ac1f0b842 99 if (p->d_name && p->d_name[0] != 0xff) {
shimniok 21:5d7ac1f0b842 100 if (cols++ > 3) {
shimniok 21:5d7ac1f0b842 101 putc('\n', stdout);
shimniok 21:5d7ac1f0b842 102 cols = 0;
shimniok 21:5d7ac1f0b842 103 }
shimniok 21:5d7ac1f0b842 104 printf("%-15s ", p->d_name);
shimniok 21:5d7ac1f0b842 105 }
shimniok 21:5d7ac1f0b842 106 }
shimniok 21:5d7ac1f0b842 107 putc('\n', stdout);
shimniok 21:5d7ac1f0b842 108 if (cols < 3)
shimniok 21:5d7ac1f0b842 109 putc('\n', stdout);
shimniok 21:5d7ac1f0b842 110 closedir(d);
shimniok 21:5d7ac1f0b842 111 } else {
shimniok 21:5d7ac1f0b842 112 puts(path);
shimniok 21:5d7ac1f0b842 113 puts(": No such directory\n");
shimniok 21:5d7ac1f0b842 114 }
shimniok 21:5d7ac1f0b842 115
shimniok 21:5d7ac1f0b842 116 return;
shimniok 21:5d7ac1f0b842 117 }
shimniok 21:5d7ac1f0b842 118
shimniok 21:5d7ac1f0b842 119
shimniok 21:5d7ac1f0b842 120 void SimpleShell::rm(int argc, char **argv)
shimniok 21:5d7ac1f0b842 121 {
shimniok 21:5d7ac1f0b842 122 if (argc >= 2) {
shimniok 21:5d7ac1f0b842 123 for (int i=1; i < argc; i++) {
shimniok 21:5d7ac1f0b842 124 if (remove(canon(argv[i]))) {
shimniok 21:5d7ac1f0b842 125 printf("%s: cannot remove\n", argv[i]);
shimniok 21:5d7ac1f0b842 126 }
shimniok 21:5d7ac1f0b842 127 }
shimniok 21:5d7ac1f0b842 128 } else {
shimniok 21:5d7ac1f0b842 129 puts("usage: rm [file1 [file2 ...]]");
shimniok 21:5d7ac1f0b842 130 }
shimniok 21:5d7ac1f0b842 131 }
shimniok 21:5d7ac1f0b842 132
shimniok 21:5d7ac1f0b842 133
shimniok 21:5d7ac1f0b842 134 void SimpleShell::touch(int argc, char **argv)
shimniok 21:5d7ac1f0b842 135 {
shimniok 21:5d7ac1f0b842 136 FILE *fp;
shimniok 21:5d7ac1f0b842 137
shimniok 21:5d7ac1f0b842 138 if (argc >= 2) {
shimniok 21:5d7ac1f0b842 139 for (int i=1; i < argc; i++) {
shimniok 21:5d7ac1f0b842 140 if ((fp = fopen(canon(argv[i]), "w")) != NULL) {
shimniok 21:5d7ac1f0b842 141 fclose(fp);
shimniok 21:5d7ac1f0b842 142 } else {
shimniok 21:5d7ac1f0b842 143 printf("%s: cannot touch\n", argv[1]);
shimniok 21:5d7ac1f0b842 144 }
shimniok 21:5d7ac1f0b842 145 }
shimniok 21:5d7ac1f0b842 146 } else {
shimniok 21:5d7ac1f0b842 147 puts("usage: touch [file1 [file2 ...]]");
shimniok 21:5d7ac1f0b842 148 }
shimniok 21:5d7ac1f0b842 149 }
shimniok 21:5d7ac1f0b842 150
shimniok 21:5d7ac1f0b842 151
shimniok 21:5d7ac1f0b842 152 void SimpleShell::cat(int argc, char **argv)
shimniok 21:5d7ac1f0b842 153 {
shimniok 21:5d7ac1f0b842 154 FILE *fp;
shimniok 21:5d7ac1f0b842 155 //int status=0;
shimniok 21:5d7ac1f0b842 156 char *buf = new char[MAXBUF];
shimniok 21:5d7ac1f0b842 157
shimniok 21:5d7ac1f0b842 158 for (int i=1; i < argc; i++) {
shimniok 21:5d7ac1f0b842 159 //resolveDirectory(path, arg);
shimniok 21:5d7ac1f0b842 160 if ((fp = fopen(canon(argv[i]), "r")) != NULL) {
shimniok 21:5d7ac1f0b842 161 while (!feof(fp)) {
shimniok 21:5d7ac1f0b842 162 fgets(buf, MAXBUF-1, fp);
shimniok 21:5d7ac1f0b842 163 fputs(buf, stdout);
shimniok 21:5d7ac1f0b842 164 }
shimniok 21:5d7ac1f0b842 165 fclose(fp);
shimniok 21:5d7ac1f0b842 166 } else {
shimniok 21:5d7ac1f0b842 167 fputs(argv[i], stdout);
shimniok 21:5d7ac1f0b842 168 fputs(": No such file\n", stdout);
shimniok 21:5d7ac1f0b842 169 //status = 1;
shimniok 21:5d7ac1f0b842 170 }
shimniok 21:5d7ac1f0b842 171 }
shimniok 21:5d7ac1f0b842 172 delete[] buf;
shimniok 21:5d7ac1f0b842 173
shimniok 21:5d7ac1f0b842 174 return;
shimniok 21:5d7ac1f0b842 175 }
shimniok 21:5d7ac1f0b842 176
shimniok 21:5d7ac1f0b842 177
shimniok 23:b1e49cfcaef6 178 void SimpleShell::send(int argc, char **argv)
shimniok 23:b1e49cfcaef6 179 {
shimniok 23:b1e49cfcaef6 180 const char SOF=0x01;
shimniok 23:b1e49cfcaef6 181 const char ETX=0x03;
shimniok 23:b1e49cfcaef6 182 const char EOT=0x04;
shimniok 23:b1e49cfcaef6 183 const char ACK=0x07;
shimniok 23:b1e49cfcaef6 184 const char NACK=0x18;
shimniok 23:b1e49cfcaef6 185 FILE *fp;
shimniok 23:b1e49cfcaef6 186
shimniok 23:b1e49cfcaef6 187 int i = 1;
shimniok 23:b1e49cfcaef6 188 if (argc >= 2) {
shimniok 23:b1e49cfcaef6 189 if ((fp = fopen(canon(argv[i]), "r")) == 0) {
shimniok 23:b1e49cfcaef6 190 printf("%s: can't open file\n", basename(argv[i]));
shimniok 23:b1e49cfcaef6 191 } else {
shimniok 23:b1e49cfcaef6 192 }
shimniok 23:b1e49cfcaef6 193 } else {
shimniok 23:b1e49cfcaef6 194 puts("usage: send file1 [file2 ...]");
shimniok 23:b1e49cfcaef6 195 }
shimniok 23:b1e49cfcaef6 196 }
shimniok 23:b1e49cfcaef6 197
shimniok 23:b1e49cfcaef6 198
shimniok 21:5d7ac1f0b842 199 void SimpleShell::run()
shimniok 21:5d7ac1f0b842 200 {
shimniok 21:5d7ac1f0b842 201 bool done=false;
shimniok 21:5d7ac1f0b842 202 callback_t cb;
shimniok 21:5d7ac1f0b842 203 //int status; // TODO implement command status return
shimniok 21:5d7ac1f0b842 204 std::string x;
shimniok 21:5d7ac1f0b842 205
shimniok 21:5d7ac1f0b842 206 // Set current working directory
shimniok 21:5d7ac1f0b842 207 strncpy(_cwd, "/etc", MAXBUF);
shimniok 21:5d7ac1f0b842 208
shimniok 21:5d7ac1f0b842 209 printf("Type help for assistance.\n");
shimniok 21:5d7ac1f0b842 210 help(0, NULL);
shimniok 21:5d7ac1f0b842 211 while (!done) {
shimniok 21:5d7ac1f0b842 212 printPrompt();
shimniok 21:5d7ac1f0b842 213 readCommand();
shimniok 21:5d7ac1f0b842 214 if (argv[0]) { // skip blank command
shimniok 21:5d7ac1f0b842 215 if (cb = findCommand()) {
shimniok 21:5d7ac1f0b842 216 cb.call(argc, argv);
shimniok 21:5d7ac1f0b842 217 } else {
shimniok 21:5d7ac1f0b842 218 printf("command <%s> not found\n", argv[0]);
shimniok 21:5d7ac1f0b842 219 }
shimniok 21:5d7ac1f0b842 220 }
shimniok 21:5d7ac1f0b842 221 }
shimniok 21:5d7ac1f0b842 222 puts("exiting shell\n");
shimniok 21:5d7ac1f0b842 223
shimniok 21:5d7ac1f0b842 224 return;
shimniok 21:5d7ac1f0b842 225 }
shimniok 21:5d7ac1f0b842 226
shimniok 21:5d7ac1f0b842 227
shimniok 21:5d7ac1f0b842 228 void SimpleShell::attach(callback_t cb, char *command)
shimniok 21:5d7ac1f0b842 229 {
shimniok 21:5d7ac1f0b842 230 if (lookupEnd < MAXLOOKUP) {
shimniok 21:5d7ac1f0b842 231 lookup[lookupEnd].cb = cb;
shimniok 21:5d7ac1f0b842 232 lookup[lookupEnd].command = command;
shimniok 21:5d7ac1f0b842 233 lookupEnd++;
shimniok 21:5d7ac1f0b842 234 }
shimniok 21:5d7ac1f0b842 235
shimniok 21:5d7ac1f0b842 236 return;
shimniok 21:5d7ac1f0b842 237 }
shimniok 21:5d7ac1f0b842 238
shimniok 21:5d7ac1f0b842 239
shimniok 21:5d7ac1f0b842 240 SimpleShell::callback_t SimpleShell::findCommand()
shimniok 21:5d7ac1f0b842 241 {
shimniok 21:5d7ac1f0b842 242 SimpleShell::callback_t cb=NULL;
shimniok 21:5d7ac1f0b842 243
shimniok 21:5d7ac1f0b842 244 for (int i=0; i < lookupEnd; i++) {
shimniok 21:5d7ac1f0b842 245 if (strncmp(argv[0], lookup[i].command, MAXBUF) == 0) {
shimniok 21:5d7ac1f0b842 246 cb = lookup[i].cb;
shimniok 21:5d7ac1f0b842 247 break;
shimniok 21:5d7ac1f0b842 248 }
shimniok 21:5d7ac1f0b842 249 }
shimniok 21:5d7ac1f0b842 250
shimniok 21:5d7ac1f0b842 251 return cb;
shimniok 21:5d7ac1f0b842 252 }
shimniok 21:5d7ac1f0b842 253
shimniok 21:5d7ac1f0b842 254
shimniok 21:5d7ac1f0b842 255 void SimpleShell::printPrompt()
shimniok 21:5d7ac1f0b842 256 {
shimniok 21:5d7ac1f0b842 257 fputc('(', stdout);
shimniok 21:5d7ac1f0b842 258 fputs(_cwd, stdout);
shimniok 21:5d7ac1f0b842 259 fputs(")# ", stdout);
shimniok 21:5d7ac1f0b842 260
shimniok 21:5d7ac1f0b842 261 return;
shimniok 21:5d7ac1f0b842 262 }
shimniok 21:5d7ac1f0b842 263
shimniok 21:5d7ac1f0b842 264
shimniok 21:5d7ac1f0b842 265 void SimpleShell::readCommand()
shimniok 21:5d7ac1f0b842 266 {
shimniok 21:5d7ac1f0b842 267 int i=0;
shimniok 21:5d7ac1f0b842 268 char c;
shimniok 21:5d7ac1f0b842 269 bool done = false;
shimniok 21:5d7ac1f0b842 270 static char cmd[MAXBUF];
shimniok 21:5d7ac1f0b842 271
shimniok 21:5d7ac1f0b842 272 memset(cmd, 0, MAXBUF);
shimniok 21:5d7ac1f0b842 273 do {
shimniok 21:5d7ac1f0b842 274 cmd[i] = 0;
shimniok 21:5d7ac1f0b842 275 c = fgetc(stdin);
shimniok 21:5d7ac1f0b842 276 if (c == '\r') { // if return is hit, we're done, don't add \r to cmd
shimniok 21:5d7ac1f0b842 277 done = true;
shimniok 21:5d7ac1f0b842 278 } else if (c == ESC) { // keyboard escape codes (arrow keys, etc)
shimniok 21:5d7ac1f0b842 279 int c2 = getchar();
shimniok 21:5d7ac1f0b842 280 int c3 = getchar();
shimniok 21:5d7ac1f0b842 281
shimniok 21:5d7ac1f0b842 282 if (c2 == 0x4f && c3 == 0x46) {
shimniok 21:5d7ac1f0b842 283 printf("<END>");
shimniok 21:5d7ac1f0b842 284 } else if (c2 == 0x5b) {
shimniok 21:5d7ac1f0b842 285
shimniok 21:5d7ac1f0b842 286 if (c3 == UP) {
shimniok 21:5d7ac1f0b842 287 printf("<UP>");
shimniok 21:5d7ac1f0b842 288 } else if (c3 == DOWN) {
shimniok 21:5d7ac1f0b842 289 printf("<DOWN>");
shimniok 21:5d7ac1f0b842 290 } else if (c3 == RIGHT) {
shimniok 21:5d7ac1f0b842 291 printf("<RIGHT>");
shimniok 21:5d7ac1f0b842 292 } else if (c3 == LEFT) {
shimniok 21:5d7ac1f0b842 293 printf("<LEFT>");
shimniok 21:5d7ac1f0b842 294 } else if (c3 == HOME || c3 == INS || c3 == DEL ||
shimniok 21:5d7ac1f0b842 295 c3 == PGUP || c3 == PGDN) {
shimniok 21:5d7ac1f0b842 296 char c4 = getchar();
shimniok 21:5d7ac1f0b842 297 if (c4 == TAIL) {
shimniok 21:5d7ac1f0b842 298 if (c4 == HOME) {
shimniok 21:5d7ac1f0b842 299 printf("<HOME>");
shimniok 21:5d7ac1f0b842 300 } else if (c4 == INS) {
shimniok 21:5d7ac1f0b842 301 printf("<INS>");
shimniok 21:5d7ac1f0b842 302 } else if (c4 == DEL) {
shimniok 21:5d7ac1f0b842 303 printf("<DEL>");
shimniok 21:5d7ac1f0b842 304 } else if (c4 == PGUP) {
shimniok 21:5d7ac1f0b842 305 printf("<PGUP>");
shimniok 21:5d7ac1f0b842 306 } else if (c4 == PGDN) {
shimniok 21:5d7ac1f0b842 307 printf("<PGDN>");
shimniok 21:5d7ac1f0b842 308 }//if
shimniok 21:5d7ac1f0b842 309 }//if
shimniok 21:5d7ac1f0b842 310 }//if
shimniok 21:5d7ac1f0b842 311 }//if
shimniok 21:5d7ac1f0b842 312 //printf("\n");
shimniok 21:5d7ac1f0b842 313 } else if (i < MAXBUF-1) {
shimniok 21:5d7ac1f0b842 314 if (c == 0x7f || c == '\b') { // backspace or delete
shimniok 21:5d7ac1f0b842 315 if (i > 0) { // if we're at the beginning, do nothing
shimniok 21:5d7ac1f0b842 316 i--;
shimniok 21:5d7ac1f0b842 317 fputs("\b \b", stdout);
shimniok 21:5d7ac1f0b842 318 }
shimniok 21:5d7ac1f0b842 319 } else {
shimniok 21:5d7ac1f0b842 320 if (isprint(c))
shimniok 21:5d7ac1f0b842 321 fputc(c, stdout);
shimniok 21:5d7ac1f0b842 322 cmd[i++] = c;
shimniok 21:5d7ac1f0b842 323 }
shimniok 21:5d7ac1f0b842 324 }
shimniok 21:5d7ac1f0b842 325
shimniok 21:5d7ac1f0b842 326 } while (!done);
shimniok 21:5d7ac1f0b842 327 fputc('\n', stdout);
shimniok 21:5d7ac1f0b842 328
shimniok 21:5d7ac1f0b842 329 // remove leading/trailing whitespace
shimniok 21:5d7ac1f0b842 330 char *s = cmd;
shimniok 21:5d7ac1f0b842 331 while (isspace(*s)) {
shimniok 21:5d7ac1f0b842 332 s++;
shimniok 21:5d7ac1f0b842 333 }
shimniok 21:5d7ac1f0b842 334 for (int j=i; j >= 0 && isspace(cmd[j]); j--) {
shimniok 21:5d7ac1f0b842 335 cmd[j] = '\0';
shimniok 21:5d7ac1f0b842 336 }
shimniok 21:5d7ac1f0b842 337
shimniok 21:5d7ac1f0b842 338 // split into command and arguments
shimniok 21:5d7ac1f0b842 339 argc = 0;
shimniok 21:5d7ac1f0b842 340 char *t;
shimniok 21:5d7ac1f0b842 341
shimniok 21:5d7ac1f0b842 342 for (int i=0; i < MAXARGS; i++) {
shimniok 21:5d7ac1f0b842 343 argv[i] = NULL;
shimniok 21:5d7ac1f0b842 344 }
shimniok 21:5d7ac1f0b842 345 t = strtok(s, " ");
shimniok 21:5d7ac1f0b842 346 while (t && argc < 10) {
shimniok 21:5d7ac1f0b842 347 argv[argc++] = t;
shimniok 21:5d7ac1f0b842 348 t = strtok(NULL, " ");
shimniok 21:5d7ac1f0b842 349 }
shimniok 21:5d7ac1f0b842 350
shimniok 21:5d7ac1f0b842 351 return;
shimniok 21:5d7ac1f0b842 352 }