Michael Shimniok / SimpleShell

Dependents:   DataBus2018

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers SimpleShell.cpp Source File

SimpleShell.cpp

00001 #include "SimpleShell.h"
00002 #include "ff.h"
00003 #include <ctype.h>
00004 #include <string>
00005 #include <list>
00006 
00007 #define ESC     0x1b
00008 #define UP      0x41
00009 #define DOWN    0x42
00010 #define RIGHT   0x43
00011 #define LEFT    0x44
00012 #define HOME    0x31
00013 #define INS     0x32
00014 #define DEL     0x33
00015 #define PGUP    0x35
00016 #define PGDN    0x36
00017 #define TAIL    0x7e
00018 
00019 
00020 SimpleShell::SimpleShell(char *home)
00021 {
00022     lookupEnd = 0;
00023 
00024     // Set home directory
00025     strncpy(_home, canon(home), MAXBUF);
00026 
00027     // Set current working directory
00028     strncpy(_cwd, _home, MAXBUF);
00029 
00030     // Built-in shell commands
00031     command(callback(this, &SimpleShell::help), "help");
00032     command(callback(this, &SimpleShell::pwd), "pwd");
00033     command(callback(this, &SimpleShell::cat), "cat");
00034     command(callback(this, &SimpleShell::cd), "cd");
00035     command(callback(this, &SimpleShell::rm), "rm");
00036     command(callback(this, &SimpleShell::touch), "touch");
00037     command(callback(this, &SimpleShell::ls), "ls");
00038     command(callback(this, &SimpleShell::send), "send");
00039 }
00040 
00041 
00042 bool SimpleShell::haswildcard(char *s) {
00043     bool result = strchr(s, '*') != NULL;
00044     return result;
00045 }
00046 
00047 
00048 /// Path stack representation
00049 typedef std::list<char*> path_t;
00050 
00051 char *SimpleShell::canon(char *path) {
00052     path_t pstack;
00053     static char tmp[MAXBUF*2];
00054     char *e;
00055 
00056     // if we're passed empty/null string, just send back cwd so nothing breaks
00057     if (path == NULL || strlen(path) == 0) {
00058         strcpy(tmp, _cwd);
00059         return tmp;
00060     }
00061 
00062     // relative path? add current working directory to path stack
00063     if (path[0] != '/') {
00064         strcpy(tmp, _cwd);
00065         strcat(tmp, "/");
00066         strcat(tmp, path);
00067     } else {
00068         strcpy(tmp, path);
00069     }
00070 
00071     // now canonicalize the path spec
00072     e = strtok(tmp+1, "/");
00073     while (e) {
00074         //printf("e = <%s>\n", e);
00075         if (strcmp("..", e) == 0) {
00076             // pop most recent directory
00077             if (!pstack.empty())
00078                 pstack.pop_back();
00079         } else if (strcmp(".", e) != 0) {
00080             // push this dir onto path
00081             if (strlen(e) > 0)
00082                 pstack.push_back(e);
00083         }
00084         e = strtok(NULL, "/");
00085     }
00086 
00087     static std::string result;
00088     result = "";
00089 
00090     for (path_t::iterator it = pstack.begin(); it != pstack.end(); it++) {
00091       result.append("/");
00092       result.append(*it);
00093     }
00094 
00095     // if empty, add a /
00096     if (result.size() < 2) {
00097       result.append("/");
00098     }
00099 
00100     strcpy(tmp, result.c_str());
00101 
00102     return tmp;
00103 }
00104 
00105 
00106 char *SimpleShell::basename(char *path) {
00107     char *result = strrchr(path, '/');
00108     
00109     if (result == NULL) {
00110         result = path;
00111     } else {
00112         if (result[0] == '/') result++;
00113     }
00114     
00115     return result;
00116 }
00117 
00118 
00119 void SimpleShell::help(int argc, char **argv)
00120 {
00121     printf("Available commands: ");
00122     for (int i=0; i < lookupEnd; i++) {
00123         printf("%s ", lookup[i].command);
00124     }
00125     printf("\n\n");
00126 }
00127 
00128 
00129 void SimpleShell::cd(int argc, char **argv)
00130 {
00131     if (argc == 1) {
00132         strncpy(_cwd, _home, MAXBUF);
00133     } else if (argc == 2) {
00134         strncpy(_cwd, canon(argv[1]), MAXBUF);
00135     } else {
00136         puts("usage: cd directory");
00137     }
00138     return;
00139 }
00140 
00141 
00142 void SimpleShell::pwd(int argc, char **argv)
00143 {
00144     puts(_cwd);
00145     return;
00146 }
00147 
00148 
00149 void SimpleShell::ls(int argc, char **argv)
00150 {
00151     DIR *d;
00152     struct dirent *p;
00153     char *path;
00154 
00155     if (argc == 1) {
00156         path = _cwd;
00157     } else if (argc == 2) {
00158         path = canon(argv[1]);
00159     } else {
00160         puts("usage: ls [directory]");
00161         return;
00162     }
00163 
00164     int cols=0;
00165     if ((d = opendir(path)) != NULL) {
00166         while ((p = readdir(d)) != NULL) {
00167             if (p->d_name && p->d_name[0] != 0xff) {
00168                 if (cols++ > 3) {
00169                     putc('\n', stdout);
00170                     cols = 0;
00171                 }
00172                 printf("%-15s  ", p->d_name);
00173             }
00174         }
00175         putc('\n', stdout);
00176         if (cols < 3)
00177             putc('\n', stdout);
00178         closedir(d);
00179     } else {
00180         puts(path);
00181         puts(": No such directory\n");
00182     }
00183 
00184     return;
00185 }
00186 
00187 
00188 char *SimpleShell::dirname(char *path)
00189 {
00190     char *found = strrchr(path, '/');
00191     char *result = new char[sizeof(path)];
00192 
00193     char *s = result;
00194     char *t = path;
00195     while (t < found) {
00196         *s++ = *t++;
00197     }
00198     *s = 0;
00199 
00200     return result;    
00201 }    
00202 
00203 
00204 /** Attempts to match filename pattern and name.
00205  * @param pattern is a pattern containing one '*'
00206  * @param name is the name to check for a match to the pattern
00207  * @returns 1 if match found, 0 if no match or bad pattern
00208  */ 
00209 int fnmatch(char *pattern, char *name)
00210 {
00211     char *p;
00212     char *n;
00213     int c;
00214 
00215     // only one * allowed
00216     p = pattern;
00217     c = 0;
00218     p = strchr(p, '*');
00219     while (p) {
00220         c++;
00221         p = strchr(p+1, '*');
00222     }
00223     if (c != 1) return 0;
00224 
00225     // match first part
00226     //puts("first");
00227     p = pattern;
00228     n = name;
00229     char *s = strchr(pattern, '*');
00230     while (p != s) {
00231         // mismatch before *?
00232         if (toupper(*p) != toupper(*n)) {
00233             return 0;
00234         }
00235         p++;
00236         n++;
00237     }
00238     // match last part in reverse
00239     //puts("second");
00240     p = strchr(pattern, 0)-1;
00241     n = strchr(name, 0)-1;
00242     while (p != s) {
00243         // mismatch before *?
00244         //printf("%c %c\n", *p, *n);
00245         if (toupper(*p) != toupper(*n)) {
00246             return 0;
00247         }
00248         p--;
00249         n--;
00250     }
00251 
00252     return 1;
00253 }
00254 
00255 
00256 // Run the specified callback on each matching filename
00257 char *SimpleShell::foreach(char *pattern)
00258 {
00259     DIR *d = 0;
00260     char *base;
00261     char *path;
00262     struct dirent *p;
00263 
00264     base = basename(pattern);
00265     path = dirname(pattern);
00266     printf("dir:<%s> base:<%s>\n", path, base);
00267     if ((d = opendir(path)) != NULL) {
00268         while ((p = readdir(d)) != NULL) {
00269             //printf("pattern:<%s> file:<%s>\n", base, p->d_name);
00270             if (fnmatch(base, p->d_name) == 1) {
00271                 char *next = new char[sizeof(base) + sizeof(p->d_name) + 2];
00272                 sprintf(next, "%s/%s", path, p->d_name);
00273                 printf("Removing %s...\n", next);
00274                 int stat = remove(next);
00275                 //int stat = f_unlink(next);
00276                 if (stat) {
00277                     printf("%s: could not remove. Error %d\n", next, stat);
00278                 }
00279                 delete[] next;
00280             }//if
00281         }//while
00282         closedir(d);
00283     } else {
00284         printf("%s: no such directory\n", path);
00285     }
00286        
00287     return 0;
00288 }
00289 
00290 
00291 void SimpleShell::rm(int argc, char **argv)
00292 {
00293     char *arg;
00294 
00295     if (argc >= 2) {
00296         for (int i=1; i < argc; i++) {
00297             arg = canon(argv[i]);
00298             if (haswildcard(argv[i])) {
00299                 foreach(arg);
00300             } else if (remove(canon(argv[i]))) {
00301                 printf("%s: cannot remove. Error %d\n", argv[i], errno);
00302             }
00303         }
00304     } else {
00305         puts("usage: rm [file1 [file2 ...]]");
00306     }
00307 }
00308 
00309 
00310 void SimpleShell::touch(int argc, char **argv)
00311 {
00312     FILE *fp;
00313 
00314     if (argc >= 2) {
00315         for (int i=1; i < argc; i++) {
00316             if ((fp = fopen(canon(argv[i]), "w")) != NULL) {
00317                 fclose(fp);
00318             } else {
00319                 printf("%s: cannot touch\n", argv[1]);
00320             }
00321         }
00322     } else {
00323         puts("usage: touch [file1 [file2 ...]]");
00324     }
00325 }
00326 
00327 
00328 void SimpleShell::cat(int argc, char **argv)
00329 {
00330     FILE *fp;
00331     //int status=0;
00332     char *buf = new char[MAXBUF];
00333 
00334     for (int i=1; i < argc; i++) {
00335         //resolveDirectory(path, arg);
00336         if ((fp = fopen(canon(argv[i]), "r")) != NULL) {
00337             while (!feof(fp)) {
00338                 fgets(buf, MAXBUF-1, fp);
00339                 fputs(buf, stdout);
00340             }
00341             fclose(fp);
00342         } else {
00343             fputs(argv[i], stdout);
00344             fputs(": No such file\n", stdout);
00345             //status = 1;
00346         }
00347     }
00348     delete[] buf;
00349 
00350     return;
00351 }
00352 
00353 
00354 void SimpleShell::send(int argc, char **argv)
00355 {
00356     const char SOF=0x01;
00357     const char ETX=0x03;
00358     const char EOT=0x04;
00359     const char ACK=0x07;
00360     const char NACK=0x18;
00361     char buf[1024];
00362     FILE *fp;
00363     char c;
00364 
00365     int i = 1;
00366     if (argc >= 2) {
00367         if ((fp = fopen(canon(argv[i]), "r")) == 0) {
00368             printf("%s: can't open file\n", basename(argv[i]));
00369         } else {
00370             puts("Ready; initiate receive on client.");
00371             c = getchar();
00372             if (c == SOF) {
00373                 puts(basename(argv[i]));
00374                 c = getchar();
00375                 if (c == ACK) {
00376                     while (!feof(fp)) {
00377                         fgets(buf, 1024, fp);
00378                         fputs(buf, stdout);
00379                     }
00380                     putchar(EOT);
00381                 } else if (c == NACK) {
00382                     puts("client error.");
00383                 } else {
00384                     puts("ACK/NACK expected");
00385                 }
00386             } else if (c == NACK) {
00387                 puts("server cancelled.");
00388             } else {
00389                 puts("SOF/NACK expected.");
00390             }
00391             fclose(fp);
00392         }
00393     } else {
00394         puts("usage: send file1 [file2 ...]");
00395     }
00396 }
00397 
00398 
00399 void SimpleShell::run()
00400 {
00401     bool done=false;
00402     callback_t cb;
00403     //int status; // TODO implement command status return
00404     std::string x;
00405 
00406     printf("Type help for assistance.\n");
00407     help(0, NULL);
00408     while (!done) {
00409         printPrompt();
00410         readCommand();
00411         if (argv[0]) { // skip blank command
00412             if (cb = findCommand()) {
00413                 cb.call(argc, argv);
00414             } else {
00415                 printf("command <%s> not found\n", argv[0]);
00416             }
00417         }
00418     }
00419     puts("exiting shell\n");
00420 
00421     return;
00422 }
00423 
00424 
00425 void SimpleShell::command(callback_t cb, char *command)
00426 {
00427     if (lookupEnd < MAXLOOKUP) {
00428         lookup[lookupEnd].cb = cb;
00429         lookup[lookupEnd].command = command;
00430         lookupEnd++;
00431     }
00432 
00433     return;
00434 }
00435 
00436 
00437 SimpleShell::callback_t SimpleShell::findCommand()
00438 {
00439     SimpleShell::callback_t cb=NULL;
00440 
00441     for (int i=0; i < lookupEnd; i++) {
00442         if (strncmp(argv[0], lookup[i].command, MAXBUF) == 0) {
00443             cb = lookup[i].cb;
00444             break;
00445         }
00446     }
00447 
00448     return cb;
00449 }
00450 
00451 
00452 void SimpleShell::printPrompt()
00453 {
00454     fputc('(', stdout);
00455     fputs(_cwd, stdout);
00456     fputs(")# ", stdout);
00457 
00458     return;
00459 }
00460 
00461 
00462 void SimpleShell::readCommand()
00463 {
00464     int i=0;
00465     char c;
00466     bool done = false;
00467     static char cmd[MAXBUF];
00468 
00469     memset(cmd, 0, MAXBUF);
00470     do {
00471         cmd[i] = 0;
00472         c = fgetc(stdin);
00473         if (c == '\r') { // if return is hit, we're done, don't add \r to cmd
00474             done = true;
00475         } else if (c == ESC) { // keyboard escape codes (arrow keys, etc)
00476             int c2 = getchar();
00477             int c3 = getchar();
00478 
00479             if (c2 == 0x4f && c3 == 0x46) {
00480                 printf("<END>");
00481             } else if (c2 == 0x5b) {
00482 
00483                 if (c3 == UP) {
00484                     printf("<UP>");
00485                 } else if (c3 == DOWN) {
00486                     printf("<DOWN>");
00487                 } else if (c3 == RIGHT) {
00488                     printf("<RIGHT>");
00489                 } else if (c3 == LEFT) {
00490                     printf("<LEFT>");
00491                 } else if (c3 == HOME || c3 == INS || c3 == DEL ||
00492                            c3 == PGUP || c3 == PGDN) {
00493                     char c4 = getchar();
00494                     if (c4 == TAIL) {
00495                         if (c4 == HOME) {
00496                             printf("<HOME>");
00497                         } else if (c4 == INS) {
00498                             printf("<INS>");
00499                         } else if (c4 == DEL) {
00500                             printf("<DEL>");
00501                         } else if (c4 == PGUP) {
00502                             printf("<PGUP>");
00503                         } else if (c4 == PGDN) {
00504                             printf("<PGDN>");
00505                         }//if
00506                     }//if
00507                 }//if
00508             }//if
00509             //printf("\n");
00510         } else if (i < MAXBUF-1) {
00511             if (c == 0x7f || c == '\b') { // backspace or delete
00512                 if (i > 0) { // if we're at the beginning, do nothing
00513                     i--;
00514                     fputs("\b \b", stdout);
00515                 }
00516             } else {
00517                 if (isprint(c))
00518                     fputc(c, stdout);
00519                 cmd[i++] = c;
00520             }
00521         }
00522 
00523     } while (!done);
00524     fputc('\n', stdout);
00525 
00526     // remove leading/trailing whitespace
00527     char *s = cmd;
00528     while (isspace(*s)) {
00529         s++;
00530     }
00531     for (int j=i; j >= 0 && isspace(cmd[j]); j--) {
00532         cmd[j] = '\0';
00533     }
00534 
00535     // split into command and arguments
00536     argc = 0;
00537     char *t;
00538 
00539     for (int i=0; i < MAXARGS; i++) {
00540         argv[i] = NULL;
00541     }
00542     t = strtok(s, " ");
00543     while (t && argc < MAXARGS) {
00544         argv[argc++] = t;
00545         t = strtok(NULL, " ");
00546     }
00547 
00548     return;
00549 }