a44421a79fb36cc2036fe116b97ea3bc9590cd0c
braney
  Fri Dec 2 09:34:39 2011 -0800
removed rcsid (#295)
diff --git src/utils/jkShell/jkShell.c src/utils/jkShell/jkShell.c
old mode 100755
new mode 100644
index 13cb148..2742c0a
--- src/utils/jkShell/jkShell.c
+++ src/utils/jkShell/jkShell.c
@@ -1,1886 +1,1885 @@
 /* jkShell - Simple shell.  Reads a line. Executes a line. */
 #include "common.h"
 #include "hash.h"
 #include "dlist.h"
 #include "portable.h"
 #include "obscure.h"
 #include <process.h>
 #include <direct.h>
 #include <signal.h>
 #include <io.h>
 #include <fcntl.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <conio.h>
 
-static char const rcsid[] = "$Id: jkShell.c,v 1.2 2003/05/06 07:41:07 kate Exp $";
 
 boolean checkAbort();
 /* Check and clear abort */
 
 void maskControlC();
 /* Make Control C not kill us. */
 
 /* Begin with some general purpose functions that should probably
  * go into library some day. */
 
 boolean isDir(char *fileName)
 /* Returns TRUE if file is a directory. */
 {
 struct stat buf;
 int result;
 
 /* Get data associated with file */
 result = stat( fileName, &buf );
 
 /* Check if statistics are valid: */
 if( result != 0 )
     {
     return FALSE;
     }
 else
     {
     return ((buf.st_mode & _S_IFDIR) != 0);
     }
 }
 
 boolean fileExists(char *fileName)
 /* Returns TRUE if file exists. */
 {
 struct stat buf;
 return (stat(fileName, &buf) == 0);
 }
 
 boolean copyOpenFile(int s, int d)
 /* Copy contents of s to d. */
 {
 int sizeRead;
 char buf[8*1024];
 boolean ok = TRUE;
 
 for (;;)
     {
     sizeRead = read(s, buf, sizeof(buf));
     if (sizeRead > 0)
         {
         if (write(d, buf, sizeRead) < sizeRead)
             {
             fprintf(stderr, "Write failed.  Disk full?");
             ok = FALSE;
             break;
             }
         }
     if (sizeRead < sizeof(buf))
         break;
     }
 return ok;
 }
 
 boolean copyFile(char *source, char *dest)
 /* Do big copy from source to dest. Prints a warning and
  * returns FALSE if it fails. */
 {
 int s, d;
 boolean ok = TRUE;
 
 s = open(source, _O_RDONLY|_O_BINARY);
 if (s == -1)
     {
     warn("Couldn't open %s", source);
     return FALSE;
     }
 
 d = open(dest, _O_WRONLY|_O_CREAT|_O_TRUNC|_O_BINARY, _S_IREAD|_S_IWRITE );
 if (d == -1)
     {
     warn("Couldn't create %s", dest);
     close(s);
     return FALSE;
     }
 ok = copyOpenFile(s, d);
 close(s);
 close(d);
 return ok;
 }
 
 
 char *fileNameOnly(char *path)
 /* Returns file name part of path, ignoring
  * directory info. */
 {
 char *d1 = strrchr(path, '/');
 char *d2 = strrchr(path, '\\');
 char *d3 = strrchr(path, ':');
 char *dirEnd = NULL;
 
 /* Get Last directory character. */
 dirEnd = d1;
 if (d2 != NULL)
     {
     if (dirEnd == NULL || d2 > dirEnd)
         dirEnd = d2;
     }
 if (d3 != NULL)
     {
     if (dirEnd == NULL || d3 > dirEnd)
         dirEnd = d3;
     }
 if (dirEnd == NULL)
     return path;
 else
     return dirEnd+1;
 }
 
 
 enum specialKeys
     {
     skLeft = 0x4b,
     skRight = 0x4d,
     skUp = 0x48,
     skDown = 0x50,
     skDel = 0x53,
     skHome = 0x47,
     skEnd = 0x4f,
     skPageUp = 0x49,
     skPageDown = 0x51,
     skInsert = 0x52,
     };
 
 static void redrawLine(char *prompt, char *buf, int cursorPos, int blanks)
 /* Redraw line for line editor. */
 {
 int bufLen = strlen(buf);
 int extraChars = bufLen - cursorPos + blanks;
 int i;
 
 putch('\r');
 cputs(prompt);
 cputs(buf);
 for (i=0; i<blanks; ++i)
     putch(' ');
 if (extraChars)
     {
     for (i=0; i<extraChars; ++i)
         putch('\b');
     }
 }
 
 void editLine(struct dlList *history, char *prompt, char *buf, int cursorPos, int bufSize)
 /* Read a line from console allowing some editing. */
 {
 int promptSize = strlen(prompt);
 int bufCharCount = strlen(buf);
 int c;
 struct dlNode *curNode = NULL;
 if (cursorPos > bufCharCount)
     cursorPos = bufCharCount;
 
 redrawLine(prompt, buf, cursorPos, 0);
 for (;;)
     {
     c = getch();
     if (c == 0 || c == 0xE0)
         {
         c = getch();
         if (c == skLeft)
             {
             if (cursorPos > 0)
                 {
                 --cursorPos;
                 putch('\b');
                 }
             }
         else if (c == skRight)
             {
             if (cursorPos < bufCharCount)
                 {
                 ++cursorPos;
                 redrawLine(prompt, buf, cursorPos, 0);
                 }
             }
         else if (c == skDel)
             {
             if (cursorPos < bufCharCount)
                 {
                 int endChars = bufCharCount - cursorPos - 1;
                 memmove(buf+cursorPos, buf+cursorPos+1, endChars);
                 --bufCharCount;
                 buf[bufCharCount] = 0;
                 redrawLine(prompt, buf, cursorPos, 1);
                 }
             }
         else if (c == skUp)
             {
             boolean gotHistory = FALSE;
             if (history != NULL && history->tail->prev != NULL)
                 {
                 if (curNode == NULL)
                     {
                     curNode = history->tail;
                     gotHistory = TRUE;
                     }
                 else
                     {
                     struct dlNode *pn = curNode->prev;
                     if (pn->prev != NULL)
                         {
                         curNode = pn;
                         gotHistory = TRUE;
                         }
                     }
                 if (gotHistory)
                     {
                     int oldSize = bufCharCount;
                     int sizeDif;
                     strcpy(buf,curNode->val);
                     bufCharCount = cursorPos = strlen(buf);
                     sizeDif = oldSize - bufCharCount;
                     if (sizeDif < 0)
                         sizeDif = 0;
                     redrawLine(prompt, buf, cursorPos, sizeDif);
                     }
                 }
             }
         else if (c == skDown)
             {
             if (curNode != NULL && curNode->next != NULL && curNode->next->next != NULL)
                 {
                 int oldSize = bufCharCount;
                 int sizeDif;
                 curNode = curNode->next;
                 strcpy(buf,curNode->val);
                 bufCharCount = cursorPos = strlen(buf);
                 sizeDif = oldSize - bufCharCount;
                 if (sizeDif < 0)
                     sizeDif = 0;
                 redrawLine(prompt, buf, cursorPos, sizeDif);
                 }
             else
                 {
                 buf[0] = 0;
                 redrawLine(prompt, buf, 0, bufCharCount);
                 cursorPos = bufCharCount = 0;
                 }
             }
         else if (c == skHome)
             {
             cursorPos = 0;
             redrawLine(prompt, buf, cursorPos, 0);
             }
         else if (c == skEnd)
             {
             cursorPos = bufCharCount;
             redrawLine(prompt, buf, cursorPos, 0);
             }
         else
             {
             printf("\n0x%x\n", c);
             redrawLine(prompt, buf, cursorPos, 0);
             }
         }
     else if (c == '\r')
         return;
     else if (c == '\b')
         {
         if (cursorPos > 0)
             {
             if (cursorPos == bufCharCount)
                 {
                 putch('\b');
                 putch(' ');
                 putch('\b');
                 --cursorPos;
                 --bufCharCount;
                 buf[bufCharCount] = 0;
                 }
             else
                 {
                 int endChars = bufCharCount - cursorPos;
                 memmove(buf+cursorPos-1, buf+cursorPos, endChars);
                 --cursorPos;
                 --bufCharCount;
                 buf[bufCharCount] = 0;
                 redrawLine(prompt, buf, cursorPos, 1);
                 }
             }
         }
     else
         {
         if (bufCharCount < bufSize-1)
             {
             if (cursorPos == bufCharCount)
                 {
                 putch(c);
                 buf[bufCharCount] = c;
                 bufCharCount += 1;
                 buf[bufCharCount] = 0;
                 cursorPos += 1;
                 }
             else
                 {
                 int endChars = bufCharCount - cursorPos;
                 memmove(buf+cursorPos+1, buf+cursorPos, endChars);
                 buf[cursorPos] = c;
                 ++cursorPos;
                 ++bufCharCount;
                 buf[bufCharCount] = 0;
                 redrawLine(prompt, buf, cursorPos, 0);
                 }
             }
         }
     }
 }
 
 
 void enterLine(struct dlList *history, char *prompt, char *buf, int bufSize)
 /* Get a line from user letting them edit it and use arrow keys for command history. */
 {
 *buf = 0;
 editLine(history, prompt, buf, 0, bufSize);
 putch('\r');
 putch('\n');
 }
 
 
 boolean gotAbort = FALSE;       /* Set if got ^C abort. */
 
 int handleControlC(int sig)
 /* Signal handler for control C. */
 {
 gotAbort = TRUE;
 maskControlC();    /* Apparently at least in Windows after catching signal you have
                     * to reset signal handler. */
 return 0;
 }
 
 void clearAbort()
 /* Clear last abort. */
 {
 gotAbort = FALSE;
 }
 
 boolean checkAbort()
 /* Check and clear abort */
 {
 if (gotAbort)
     {
     clearAbort();
     return TRUE;
     }
 else
     return FALSE;
 }
 
 
 int commandLimit = 1000;     /* Max length of command line (here because Windows will
                              * crash if you give it a really long one.). */
 
 enum shellReturnCode
 /* Return code values. */
     {
     slcEof = 0,     /* End of file. */
     slcOk = 1,      /* A-ok. */
     slcError = 2,   /* Problem. */
     };
 
 int shellLine(struct shell *sh, char *line, FILE *f);
 /* Do processing of one line of user input. Returns
  * slcEof to quit. */
 
 void enterLine(struct dlList *history, char *prompt, char *buf, int bufSize);
 /* Get a line from user letting them edit it and use arrow keys for command history. */
 
 struct shell
 /* Holds data structures for a shell. */
     {
     char *inBuf;         /* Holds input line. */
     int inBufAlloc;      /* Allocated size of input line. */
     char *outBuf;         /* Holds output line. */
     int outBufAlloc;     /* Allocates size of output line. */
     struct dlList *history; /* Recent commands. */
     int historySize;        /* Current length of history. */
     int maxHistorySize;     /* Number of commands to keep. */
     int commandsSeen;       /* Number of commands seen. */
     struct hash *variables; /* Variable hash table. */
     struct hash *aliases;   /* Alias hash table. */
     char *prompt;           /* Prompt string. */
     };
 
 struct shell *newShell()
 /* Return a new shell. */
 {
 struct shell *sh;
 AllocVar(sh);
 sh->inBufAlloc = 1024;
 sh->inBuf = needMem(sh->inBufAlloc);
 sh->outBufAlloc = 64*1024;
 sh->outBuf = needMem(sh->outBufAlloc);
 sh->history = newDlList();
 sh->maxHistorySize = 200;
 sh->variables = newHash(0);
 sh->aliases = newHash(0);
 sh->prompt = "pong% ";
 return sh;
 }
 
 void freeShell(struct shell **pSh)
 /* Free up a shell. */
 {
 struct shell *sh;
 if ((sh = *pSh) != NULL)
     {
     freeMem(sh->inBuf);
     freeMem(sh->outBuf);
     freeDlListAndVals(&sh->history);
     freeHashAndVals(&sh->variables);    
     freeHashAndVals(&sh->aliases);
     }
 }
 
 struct slName *newTok(char *string, int size)
 /* Allocat slName for string of give size. */
 {
 struct slName *n = needMem(sizeof(*n) + size);
 memcpy(n->name, string, size);
 return n;
 }
 
 bool isNameChar[256];
 
 void makeIsNameChar()
 /* Make array of flags that say whether something is
  * an acceptable part of a word or not. */
 {
 int i;
 for (i=0; i<ArraySize(isNameChar); ++i)
     isNameChar[i] = TRUE;
 isNameChar[' '] = FALSE;
 isNameChar['\r'] = FALSE;
 isNameChar['\n'] = FALSE;
 isNameChar['\t'] = FALSE;
 isNameChar['#'] = FALSE;
 isNameChar['|'] = FALSE;
 isNameChar['='] = FALSE;
 isNameChar['>'] = FALSE;
 isNameChar['<'] = FALSE;
 isNameChar['&'] = FALSE;
 isNameChar[')'] = FALSE;
 isNameChar['('] = FALSE;
 isNameChar[0] = FALSE;
 }
 
 char *firstNonName(char *s)
 /* Return first non-name character in string */
 {
 char c;
 for (;;)
     {
     c = *s;
     if (!isNameChar[c])
         return s;
     ++s;
     }
 }
 
 struct slName *tokenize(char *text)
 /* Return a list of tokenized text. */
 {
 char c;
 char *s = text;
 struct slName *tokList = NULL, *tok;
 
 while((c = *s++) != 0)
     {
     if (isspace(c))
         {
         char *start = s-1;
         while (isspace(*s))
             ++s;
         tok = newTok(start, s-start);
         slAddHead(&tokList, tok);
         }
     else if (c == '"')
         {
         /* Go look for next (unescaped) quote */
         char *start = s;
         char lastC = c;
         while ((c = *s) != 0)
             {
             if (c == '"' && lastC != '\\')
                 {
                 break;
                 }
             lastC = c;
             ++s;
             }
         tok = newTok(start, s - start);
         slAddHead(&tokList, tok);
         if (c == '"')
             ++s;
         }        
     else if (isNameChar[c])
         {
         char *start = s-1;
         for (;;)
             {
             c = *s;
             if (!isNameChar[c])
                 break;
             ++s;
             }
         tok = newTok(start, s-start);
         slAddHead(&tokList, tok);
         }
     else
         {
         tok = newTok(&c, 1);
         slAddHead(&tokList, tok);
         }
     }
 slReverse(&tokList);
 return tokList;
 }
 
 struct slName *stripSpaceTokens(struct slName *tokList)
 /* Remove white space tokens from list. */
 {
 struct slName *newList = NULL, *tok, *next;
 
 for (tok = tokList; tok != NULL; tok = next)
     {
     next = tok->next;
     if (isspace(tok->name[0]))
         freeMem(tok);
     else
         slAddHead(&newList, tok);
     }
 slReverse(&newList);
 return newList;
 }
 
 void flattenOutput(struct shell *sh, struct slName *tokList, char *spacer)
 /* Copy token list to space-delimited sh->outBuf */
 {
 int totalSize = 1;  /* 1 extra for zero tag. */
 struct slName *tok;
 char *s;
 int len;
 int spaceLen = 0;
 
 if (spacer != NULL)
     spaceLen = strlen(spacer);
 for (tok = tokList; tok != NULL; tok = tok->next)
     totalSize += (strlen(tok->name) + spaceLen);
 if (totalSize > sh->outBufAlloc)
     {
     sh->outBuf = needMoreMem(sh->outBuf, 0, totalSize);
     sh->outBufAlloc = totalSize;
     }
 s = sh->outBuf;
 for (tok = tokList; tok != NULL; tok = tok->next)
     {
     len = strlen(tok->name);
     memcpy(s, tok->name, len);
     s += len;
     if (spacer != NULL && tok->next != NULL)
         {
         memcpy(s, spacer, spaceLen);
         s += spaceLen;
         }
     }
 *s++ = 0;
 }
 
 int doSystem(struct shell *sh, struct slName *tokList)
 /* Put all the tokens back together in a space delimited
  * string and do a system call on it. */
 {
 int retVal = slcOk;
 int sysRet;
 flattenOutput(sh, tokList, " ");
 sh->outBuf[commandLimit] = 0;
 
 sysRet = _spawnlp(_P_WAIT, tokList->name, sh->outBuf, NULL );
 
 //sysRet = system(sh->outBuf);
 /* Sadly the system call only returns -1 in case of
  * an internal error, not if there was a problem
  * with the underlying program. */
 if (sysRet != 0)
     {
     if (errno)
         perror(NULL);
     retVal = slcError;
     }
 return retVal;
 }
 
 char *findUnescaped(char *s, char target)
 /* Find first unescaped target char in s. */
 {
 char lastC = 0;
 char c;
 
 while ((c = *s) != 0)
     {
     if (c == target && lastC != '\\')
         return s;
     lastC = c;
     ++s;
     }
 return NULL;
 }
 
 
 struct dlNode *relBack(struct shell *sh, int val)
 /* Go back 'val' in history. Return NULL if can't go that
  * far back. */
 {
 struct dlNode *histNode;
 
 for (histNode = sh->history->tail; histNode->prev != NULL; histNode = histNode->prev)
     {
     if (++val >= 0)
         break;
     }
 if (histNode->next == NULL)
     {
     fprintf(stderr, "Sorry, that's before recorded history\n");
     histNode = NULL;
     }
 return histNode;
 }
 
 struct slName *lastRealNode(struct slName *tokList)
 /* Return last real node in list. */
 {
 struct slName *tok, *lastReal = NULL;
 for (tok = tokList; tok != NULL; tok = tok->next)
     {
     if (!isspace(tok->name[0]))
         lastReal = tok;
     }
 return lastReal;
 }
 
 boolean interpretHistory(struct shell *sh, char *firstBang)
 /* Expand out some sort of bang! history. */
 {
 char *s = firstBang+1;
 struct dlNode *histNode = NULL;
 char c = *s;
 char *hist = NULL;
 struct slName *tokList = NULL, *tok;
 boolean ok;
 
 if (c == '$')
     {
     histNode = relBack(sh, -1);
     if (histNode != NULL)
         {
         tokList = tokenize(histNode->val);
         tok = lastRealNode(tokList);
         if (tok != NULL)
             hist = tok->name;
         }
     }
 else if (c == '!')
     {
     histNode = relBack(sh, -1);
     }
 else if (c == '-')
     {
     int val = atoi(s);
     if (val < 0)
         {
         histNode = relBack(sh, val);
         }
     }
 else if (isdigit(c))
     {
     int val = atoi(s);
     histNode = relBack(sh, val - sh->commandsSeen - 1);
     }
 else
     {
     char *end = skipToSpaces(s);
     int size;
     if (end == NULL)
         size = strlen(s);
     else
         size = end - s;
     for (histNode = sh->history->tail; histNode->prev != NULL; histNode = histNode->prev)
         {
         char *command = histNode->val;
         if (strncmp(command, s, size) == 0)
             break;
         }
     if (histNode->prev == NULL)
         {
         fprintf(stderr, "!%s not found\n", s);
         histNode = NULL;
         }
     }
 if (hist == NULL && histNode != NULL && histNode->prev != NULL)
     {
     hist = histNode->val;
     }
 if (hist != NULL)
     {
     int oldLen = strlen(sh->inBuf);
     int histLen = strlen(hist);
     int beforeLen = firstBang - sh->inBuf;
     char *after = firstNonName(s);
     int afterStart = after - sh->inBuf;
     int afterLen = oldLen - afterStart;
     int newLen = beforeLen + histLen + afterLen;
     if (newLen >= sh->inBufAlloc)
         {
         sh->inBuf = needMoreMem(sh->inBuf, oldLen, newLen+1);
         sh->inBufAlloc = newLen+1;
         }
     memmove(sh->inBuf + beforeLen + histLen, sh->inBuf + afterStart, afterLen);
     memcpy(sh->inBuf + beforeLen, hist, histLen);
     sh->inBuf[newLen] = 0;
     printf("%s\n", sh->inBuf);
     ok = TRUE;
     }
 else
     ok = FALSE;
 slFreeList(&tokList);
 return ok;
 }
 
 boolean doHistory(struct shell *sh)
 /* Deal with command history - adding and interpreting. */
 {
 char *s = trimSpaces(sh->inBuf);        
 if (s != NULL)
     {
     /* Look for !.  If it's there deal with it before
      * adding to history. */
     char *bang = findUnescaped(s, '!');
     char *dupe;
     if (bang != NULL)
         {
         if (!interpretHistory(sh, bang))
             return FALSE;        
         }
     dupe = cloneString(sh->inBuf);
     dlAddValTail(sh->history, dupe);
     if (++sh->historySize == sh->maxHistorySize)
         {
         struct dlNode *old = dlPopHead(sh->history);
         freeMem(old->val);
         freeMem(old);
         sh->historySize -= 1;
         }
     ++sh->commandsSeen;
     }
 return TRUE;
 }
 
 struct slName *lookupAliases(struct shell *sh, struct slName *tokList)
 /* Look up first token to see if it's an alias. */
 {
 if (tokList == NULL)
     return NULL;
 else
     {
     char *command = tokList->name;
     struct hashEl *hel = hashLookup(sh->aliases, command);
     if (hel == NULL)
         return tokList;
     else
         {
         /* We got alias.  Dummy up token list with aliased
          * head, flatten it out, and retokenize. */
         char *alias = hel->val;
         struct slName *newHead = newSlName(alias);
         newHead->next = tokList->next;
         flattenOutput(sh, newHead, NULL);
         freeMem(newHead);
         slFreeList(&tokList);
         tokList = tokenize(sh->outBuf);
         return tokList;
         }
     }
 }
 
 struct slName *lookupVariables(struct shell *sh, struct slName *tokList)
 /* Look up variables (any tokens that start with $) */
 {
 struct slName *newList = NULL, *next, *tok;
 struct hashEl *hel;
 boolean gotAny = FALSE;
 char *varStart;
 
 for (tok = tokList; tok != NULL; tok = next)
     {
     next = tok->next;
     if ((varStart = findUnescaped(tok->name, '$')) != NULL)
         {
         /* Extract variable part of token, replace it, and put
          * back rest of token. */
         char *before = tok->name;              /* Start of part before var. */
         int beforeSize = varStart - before;
         char *after;                          /* Start of part after var. */
         char *colon;
         char colonCommand = 0;
         int afterSize;
         int varSize;
         char varNameBuf[256];
 
 
         varStart += 1;
         colon = findUnescaped(varStart, ':');
         if (colon == NULL)
             {
             after = varStart;
             while (isalpha(*after))
                 ++after;
             varSize = after-varStart;
             }
         else
             {
             after = colon+2;
             varSize = colon-varStart;
             colonCommand = colon[1];
             }
         afterSize = strlen(after);
         if (varSize >= sizeof(varNameBuf))
             warn("%s too long\n", varStart);
         memcpy(varNameBuf, varStart, varSize);
         varNameBuf[varSize] = 0;
         if ((hel = hashLookup(sh->variables, varNameBuf)) != NULL)
             {
             struct slName *t;
             if (beforeSize > 0)
                 {
                 t = newTok(before, beforeSize);
                 slAddHead(&newList, t);
                 }
             if (colonCommand == 0)
                 t = newSlName(hel->val);
             else 
                 {
                 char *s = hel->val;
                 char *suffix = strrchr(s, '.');
                 int prefixSize, suffixSize;
                 int wholeSize = strlen(s);
                 if (suffix == NULL)
                     {
                     suffix = s + wholeSize;
                     suffixSize = 0;
                     prefixSize = wholeSize;
                     }
                 else
                     {
                     prefixSize = suffix - s;
                     suffix += 1;
                     suffixSize = wholeSize - prefixSize - 1;
                     }
 
                 if (colonCommand == 'r')
                     t = newTok(s, prefixSize);
                 else if (colonCommand == 'e')
                     t = newTok(suffix, suffixSize);
                 else
                     t = newTok(s, wholeSize);
                 }
             slAddHead(&newList, t);
             if (afterSize > 0)
                 {
                 t = newTok(after, afterSize);
                 slAddHead(&newList, t);
                 } 
             freez(&tok);
             gotAny = TRUE;
             }
         else
             {
             slAddHead(&newList, tok);
             }
         }
     else
         {
         slAddHead(&newList, tok);
         }
     }
 slReverse(&newList);
 if (gotAny)
     {
     flattenOutput(sh, newList, NULL);
     slFreeList(&newList);
     newList = tokenize(sh->outBuf);
     }
 return newList;
 }
 
 
 boolean anyWild(char *s)
 /* Returns TRUE if any characters in string are wildcards. */
 {
 char c;
 while ((c = *s++) != 0)
     {
     if (c == '?' || c == '*')
         return TRUE;
     }
 return FALSE;
 }
 
 struct slName *lookupWildcards(struct shell *sh, struct slName *tokList)
 /* Expand wildcards */
 {
 struct slName *newList = NULL, *next, *tok;
 boolean gotDir = FALSE;
 boolean gotWild = FALSE;
 int origCount;
 int newCount;
 char *s;
 
 if (tokList == NULL)
     return NULL;
 origCount = slCount(tokList);
 
 for (tok = tokList; tok != NULL; tok = next)
     {
     next = tok->next;
     s = tok->name;
     if (anyWild(s))
         {
         char *pattern = fileNameOnly(s);
         char *dir;
         char endChar;
         struct slName *t, *nt, *dirList;
 
         gotWild = TRUE;
         /* Parse out directory if any and pattern. */
         if (pattern != s)
             {
             endChar = pattern[-1];
             pattern[-1] = 0;
             dir = s;
             gotDir = TRUE;
             }
         else
             {
             dir = ".";
             }
 
         /* Read directory and put it on new list in place
          * of the wildcard. */
         dirList = listDir(dir, pattern);
         if (gotDir)
             {
             char pathBuf[512];
             for (t = dirList; t != NULL; t = t->next)
                 {
                 sprintf(pathBuf, "%s%c%s", dir, endChar, t->name);
                 nt = newSlName(pathBuf);                
                 slAddHead(&newList, nt);
                 }
             slFreeList(&dirList);
             }
         else
             {
             for (t = dirList; t != NULL; t = nt)
                 {
                 nt = t->next;
                 slAddHead(&newList, t);
                 }
             }
         freeMem(tok);
         }
     else
         {
         slAddHead(&newList, tok);
         }
     }
 slReverse(&newList);
 newCount = slCount(newList);
 if (gotWild && (newCount == 0 || (newCount <= 1 && origCount > 1)))
     {
     fprintf(stderr, "No wildcard match.\n");
     slFreeList(&newList);
     }
 return newList;
 }
 
 
 int noHistoryShell(struct shell *sh, char *fileName)
 /* Interpret lines in file without putting onto
  * history or doing prompts. */
 {
 FILE *f = fopen(fileName, "r");
 int lineCount = 0;
 int retCode = slcOk;
 
 if (f == NULL)
     {
     fprintf(stderr, "Couldn't open %s\n", fileName);
     return slcError;
     }
 
 for (;;)
     {
     if (fgets(sh->inBuf, sh->inBufAlloc, f) == NULL)
         break;
     ++lineCount;
     retCode = shellLine(sh, trimSpaces(sh->inBuf), f);
     if (retCode != slcOk)
         break;
     }
 fclose(f);
 if (retCode == slcEof)
     return slcOk;
 else
     return retCode;
 }
 
 void setUsage()
 /* Explain usage of the set command. */
 {
 fprintf(stderr, "set - set a variable to a value\n"
                 "usage\n"
                 "    set var=val\n");
 }
 
 
 void forEachUsage()
 /* Explain usage of the foreach command. */
 {
 fprintf(stderr, "foreach - do something a number of times\n"
                 "usage\n"
                 "    foreach var (list)\n"
                 "        command1\n"
                 "        command2\n"
                 "          ...\n"
                 "    end\n");
 }
 
 int doForEach(struct shell *sh, struct slName *foreachToks, FILE *f)
 /* Interpret a foreach command. */
 {
 boolean isInteractive = (f == stdin);
 int tokCount = slCount(foreachToks);
 char *var;
 struct slName *tok, *valList, *next;
 struct slName *lineList = NULL, *line;
 boolean interpretIt = TRUE;
 char *s;
 int retCode = slcOk;
 
 /* Parse first line:
  *      foreach var ( ... ) */
 if (tokCount < 4)
     {
     forEachUsage();
     return slcError;
     }
 tok = foreachToks->next;
 var = tok->name;
 tok = tok->next;
 if (!sameString(tok->name, "("))
     {
     forEachUsage();
     return slcError;
     }
 valList = tok->next;
 while ((next = tok->next) != NULL)
     tok = next;
 if (!sameString(tok->name, ")"))
     {
     forEachUsage();
     return slcError;
     }
 
 /* Store away lines until end. */
 for (;;)
     {
     if (isInteractive)
         {
         enterLine(sh->history, "foreach: ", sh->inBuf, sh->inBufAlloc);
         if (!doHistory(sh))
             continue;
         }
     else if (fgets(sh->inBuf, sh->inBufAlloc, f) == NULL)
         {
         interpretIt = FALSE;
         fprintf(stderr, "foreach without end\n");
         retCode = slcError;
         break;
         }
     s = trimSpaces(sh->inBuf);
     if (sameWord(s, "end"))
         break;
     line = newSlName(sh->inBuf);
     slAddHead(&lineList, line);
     }
 slReverse(&lineList);
 if (interpretIt)
     {
     for (tok = valList; tok != NULL; tok = tok->next)
         {
         struct hashEl *hel;
 
         if (sameString(tok->name, ")"))
             break;
         hel = hashStore(sh->variables, var);
         if (hel != NULL)
             {
             freeMem(hel->val);
             hel->val = cloneString(tok->name);
             }
         for (line = lineList; line != NULL; line = line->next)
             {
             retCode = shellLine(sh, line->name, f);
             if (retCode != slcOk)
                 break;
             }
         if (retCode != slcOk)
             break;
         }
     }
 slFreeList(&lineList);
 return retCode;
 }
 
 
 boolean cmRename(char *a, char *b)
 /* Rename file a to b */
 {
 if (fileExists(b))
     remove(b);
 if (rename(a, b) != 0)
     {
     fprintf(stderr, "Couldn't rename %s to %s\n", a, b);
     return FALSE;
     }
 else
     return TRUE;
 }
 
 boolean cmCharCopy(char *source, char *dest)
 /* Copy source file to dest. */
 {
 int c;
 FILE *s, *d;
 boolean ok = TRUE;
 
 if ((s = fopen(source, "rb")) == NULL)
     {
     fprintf(stderr, "Couldn't open %s", source);
     perror("");
     return FALSE;
     }
 if ((d = fopen(dest, "wb")) == NULL)
     {
     fprintf(stderr, "Couldn't create %s", dest);
     perror("");
     fclose(s);
     return FALSE;
     }
 while ((c = getc(s)) != EOF)
     {
     if ((putc(c, d)) == EOF)
         {
         fprintf(stderr, "%s truncated", dest);
         perror("");
         ok = FALSE;
         break;
         }
     }
 fclose(s);
 fclose(d);
 
 return ok;
 }
 
 void cpMvUsage(boolean isMv)
 /* Print usage instructions for mv or copy command */
 {
 char *name
  = (isMv ? "mv" : "cp");
 if (isMv)
     {
     fprintf(stderr, "mv - move files\n");
     name = "mv";
     }
 else
     {
     fprintf(stderr, "cp - copy files\n");
     name = "cp";
     }
 fprintf(stderr,    "usage:\n"
                     "   %s oldName newName\n"
                     "or\n"
                     "   %s file(s) dir\n",
                     name, name);
 }
 
 
 int doCpMv(struct slName *tokLine, int tokCount, char *line, boolean isMv)
 /* Do mv or copy command (depending on cpMv function passed in. */
 {
 boolean (*cpMv)(char *a, char *b) = (isMv ? cmRename : copyFile);
 
 if (tokCount < 3)
     {
     cpMvUsage(isMv);
     return slcError;
     }
 else
     {
     struct slName *dest = lastRealNode(tokLine);
     if (isDir(dest->name))
         {
         char destPath[512];
         struct slName *source;
         char *sourcePath;
         for (source = tokLine->next; source != dest; source = source->next)
             {
             sourcePath = source->name;
             sprintf(destPath, "%s/%s", dest->name, fileNameOnly(sourcePath));
             if (!cpMv(sourcePath, destPath))
                 {
                 return slcError;
                 }
             }
         }
     else
         {
         if (tokCount != 3)
             {
             cpMvUsage(isMv);
             return slcError;
             }
         else
             {
             char *oldName, *newName;
             struct slName *tok = tokLine->next;
             oldName = tok->name;
             newName = dest->name;
             if (!cpMv(oldName, newName))
                 {
                 return slcError;
                 }
             }
         }
     }
 return slcOk;
 }
 
 struct fileInfo
 /* Full info about one file - used by ls. */
     {
     struct fileInfo *next;  /* Next in list. */
     char *name;             /* File name (not allocated here) */
     struct stat stat;       /* Result from stat system call. */
     };
 
 int cmpFiSize(const void *va, const void *vb)
 /* Compare two files by size. */
 {
 struct fileInfo **pA = (struct fileInfo **)va;
 struct fileInfo **pB = (struct fileInfo **)vb;
 struct fileInfo *a = *pA, *b = *pB;
 return a->stat.st_size- b->stat.st_size;
 }
 
 int cmpFiTime(const void *va, const void *vb)
 /* Compare two files by time. */
 {
 struct fileInfo **pA = (struct fileInfo **)va;
 struct fileInfo **pB = (struct fileInfo **)vb;
 struct fileInfo *a = *pA, *b = *pB;
 return a->stat.st_mtime - b->stat.st_mtime;
 }
 
 
 
 int doLs(struct slName *tokLine, int tokCount, char *line)
 /* List files. */
 {
 struct slName *tok;
 char *name;
 int result;
 boolean doLong = FALSE;
 boolean doOne = FALSE;
 boolean sortSize = FALSE;
 boolean sortTime = FALSE;
 struct fileInfo *fiList = NULL, *fi;
 int nameSize, maxNameSize = 0;
 int lineSize = 80;
 int perLine;
 int linePos = 0;
 struct slName *ourList = NULL;
 int fileCount = 0;
 double totalSize = 0;
 int retCode = slcOk;
 
 clearAbort();   /* This can take long enough that we check for ^C. */
 /* Collect options and count real files. */
 for (tok = tokLine->next; tok != NULL; tok = tok->next)
     {
     name = tok->name;
     if (name[0] == '-')
         {
         char *s = name+1;
         char c;
         while ((c = *s++) != 0)
             {
             c = tolower(c);
             if (c == 'l')
                 doLong = TRUE;
             else if (c == '1')
                 doOne = TRUE;
             else if (c == 's')
                 sortSize = TRUE;
             else if (c == 't')
                 sortTime = TRUE;
             }
         }
     else
         {
         ++fileCount;
         }
     }
 
 /* If no real files then list current directory. */
 if (fileCount == 0)
     {
     tok = ourList = listDir(".", "*");
     fileCount = slCount(ourList);
     if (checkAbort())
         {
         retCode = slcError;
         goto CLEANUP;
         }
         
     }
 else
     tok = tokLine->next;
     
 /* Collect info on all files. */
 for (; tok != NULL; tok = tok->next)
     {
     name = tok->name;
     if (name[0] != '-')
         {
         AllocVar(fi);
         result = stat(name, &fi->stat);
         if (result != 0)
             {
             fprintf(stderr, "%s ", name);
             perror("");
             freeMem(fi);
             }
         else
             {
             nameSize = strlen(name);
             if (nameSize > maxNameSize)
                 maxNameSize = nameSize;
             fi->name = name;
             slAddHead(&fiList, fi);
             }
         }
     }
 
 /* Second pass - sort and print info. */
 if (sortSize)
     slSort(&fiList, cmpFiSize);
 if (sortTime)
     slSort(&fiList, cmpFiTime);
 
 perLine = lineSize/(maxNameSize+2);
 if (perLine < 1)
     perLine = 1;
 
 for (fi = fiList; fi != NULL; fi = fi->next)
     {
     if (doLong)
         {
         /* Print something like:
            drwxr-xr-x  512 Feb  3 12:29:19 patJobs/ */
         struct tm *tm = localtime(&fi->stat.st_mtime);
         char dirChar = '-';
         char readChar = '-';
         char writeChar = '-';
         char exeChar = '-';
 
         if (fi->stat.st_mode & _S_IFDIR)
             dirChar = 'd';
         if (fi->stat.st_mode & _S_IREAD)
             readChar = 'r';
         if (fi->stat.st_mode & _S_IWRITE)
             writeChar = 'w';
         if (fi->stat.st_mode & _S_IEXEC)
             exeChar = 'x';
         printf("%c%c%c%c %10lu %02d/%02d/%d %02d:%02d:%02d %s\n", 
             dirChar, readChar, writeChar, exeChar,
             fi->stat.st_size, 
             tm->tm_mon, tm->tm_mday, tm->tm_year+1900,
             tm->tm_hour, tm->tm_min, tm->tm_sec,
             fi->name);
         totalSize += fi->stat.st_size;
         }
     else
         {
         char buf[256];
         char extra = ' ';
 //        if (fi->stat.st_mode & _S_IEXEC)
 //            extra = '*';
         if (fi->stat.st_mode & _S_IFDIR)
             extra = '/';
         if (doOne)
             extra = ' ';
         sprintf(buf, "%s%c", fi->name, extra);
         printf("%-*s", maxNameSize+1, buf);
         if (doOne)
             putchar('\n');
         else
             {
             if (++linePos == perLine)
                 {
                 putchar('\n');
                 linePos = 0;
                 }
             else
                 putchar(' ');
             }
         }
     if (checkAbort())
         {
         putchar('\n');
         retCode = slcError;
         goto CLEANUP;
         }
     }
 if (!doOne && !doLong && linePos != 0)
     putchar('\n');
 if (doLong)
     printf("%d files %1.0f bytes\n", fileCount, totalSize);
 CLEANUP:
     {
     slFreeList(&fiList);
     slFreeList(&ourList);
     return retCode;
     }
 }
 
 
 struct redirector
 /* This keeps track of input/output redirection. */
     {
     int old[3];     /* Old stdin (0) stdout (1) stderr (2) */
     int new[3];     /* New stdin, out, err. */
     };
 
 int cpToStdout(char *name)
 /* Copy file to standard out (unbuffered) */
 {
 int s,d;
 int sizeRead;
 char buf[8*1024];
 int bufSize = sizeof(buf);
 int retCode = slcOk;
 
 clearAbort();
 d = 1;      /* Standard out. */
 s = open(name, _O_RDONLY | _O_BINARY);
 if (s == -1)
     {
     warn("Couldn't open %s", name);
     return slcError;
     }
 for (;;)
     {
     sizeRead = read(s, buf, bufSize);
     if (sizeRead > 0)
         {
         if (write(d, buf, sizeRead) < sizeRead)
             {
             fprintf(stderr, "Write failed.  Disk full?");
             retCode = slcError;
             break;
             }
         }
     if (sizeRead < bufSize)
         break;
     if (checkAbort())
         {
         fprintf(stderr, "\nAborting cat from <control>C\n");
         retCode = slcError;
         break;
         }                
     }
 close(s);
 return retCode;
 }
 
 int doCat(struct slName *tokLine, int tokCount, char *line)
 /* List files. */
 {
 int retCode = slcOk;
 struct slName *tok;
 
 if (tokCount < 2)
     {
     fprintf(stderr, "cat - copy file(s) to standard output\n");
     return slcError;
     }
 fflush(stdout);
 for (tok = tokLine->next; tok != NULL; tok = tok->next)
     {
     if ((retCode = cpToStdout(tok->name)) != slcOk)
         break;
     }
 return retCode;
 }
 
 
 boolean redirectOne(struct redirector *rd, char *fileName, int fileIx, int openMode, int sMode)
 /* Redirect standard input, output, or error depending on fileIx. 
  * Returns FALSE on failure. */
 {
 int fd;
 if (rd->old[fileIx] != -1)
     {
     fprintf(stderr, "Only one < and one > allowed per command\n");
     return FALSE;
     }
 
 rd->new[fileIx] = fd = open(fileName, openMode, sMode);
 if (fd == -1)
     {
     fprintf(stderr, "Couldn't open %s\n", fileName);
     return FALSE;
     }
 if ((rd->old[fileIx] = _dup(fileIx)) < 0)
     {
     fprintf(stderr, "Couldn't dup %d, we're hosed!\n", fileIx);
     return FALSE;
     }
 if (_dup2(fd, fileIx) < 0)
     {
     fprintf(stderr, "Couldn't dup2 %s and %d, we're hosed\n", fileName, fileIx);
     return FALSE;
     }
 return TRUE;
 }
 
 struct slName *doRedirection(struct shell *sh, struct slName *tokList, struct redirector *rd)
 /* Handle redirection - do necessary duplications and such of file handles.
  * Remove redirection character and files from token list. */
 {
 struct slName *newList = NULL, *next, *tok;
 int i;
 
 for (i=0; i<3; ++i)
     rd->new[i] = rd->old[i] = -1;
 for (tok = tokList; tok != NULL; tok = next)
     {
     next = tok->next;
     if (tok->name[0] == '<')
         {
         freeMem(tok);
         tok = next;
         if (tok == NULL)
             {
             fprintf(stderr, "Need a file name after <\n");
             goto ABORT;
             }
         next = tok->next;
         if (!redirectOne(rd, tok->name, 0, _O_RDONLY, 0))
             goto ABORT;
         freeMem(tok);
         }
     else if (tok->name[0] == '>')
         {
         boolean errToo = FALSE;
         freeMem(tok);
         tok = next;
         if (tok == NULL)
             {
             fprintf(stderr, "Need a file name after >\n");
             goto ABORT;
             }
         next = tok->next;
         fflush(stdout);
         if (tok->name[0] == '&')
             {
             errToo = TRUE;
             freeMem(tok);
             tok = next;
             if (tok == NULL)
                 {
                 fprintf(stderr, "Need a file name after >&\n");
                 goto ABORT;
                 }
             next = tok->next;
             }
         if (!redirectOne(rd, tok->name, 1, _O_WRONLY|_O_CREAT|_O_TRUNC, _S_IREAD|_S_IWRITE ))
             goto ABORT;
         if (errToo)
             {
             fflush(stderr);
             rd->old[2] = _dup(2);
             dup2(rd->new[1], 2);
             }
         freeMem(tok);
         }
     else
         {
         slAddHead(&newList, tok);
         }
     }
 slReverse(&newList);
 return newList;
 
 ABORT:
     {
     slFreeList(&tok);
     slFreeList(&newList);
     return NULL;
     }
 }
 
 void undoRedirection(struct redirector *rd)
 /* Restore old standard in, out and stuff. */
 {
 int i;
 int oldFd, newFd;
 boolean gotAny = FALSE;
 
 if (rd->old[1] != -1)
     fflush(stdout);
 if (rd->old[2] != -1)
     fflush(stderr);
 
 for (i=0; i<3; ++i)
     {
     if ((oldFd = rd->old[i]) != -1)
         {
         newFd = rd->new[i];
         _dup2(oldFd, i);
         if (i != 2)
             _close(newFd);
         rd->old[i] = rd->new[i] = -1;
         gotAny = TRUE;
         }
     }                                                                   
 fflush(stdout);
 fflush(stderr);
 }
 
 int shellLine(struct shell *sh, char *line, FILE *f)
 /* Do processing of one line of user input. Return FALSE
  * to quit. */
 {
 struct slName *tokLine;  /* Tokenized line. */
 int retCode = slcOk;
 struct redirector rd;
 
 tokLine = tokenize(skipLeadingSpaces(line));
 tokLine = lookupAliases(sh, tokLine);
 tokLine = lookupVariables(sh, tokLine);
 tokLine = stripSpaceTokens(tokLine);
 tokLine = lookupWildcards(sh, tokLine);
 tokLine = doRedirection(sh, tokLine, &rd);
 if (tokLine != NULL)
     {
     char *command = tokLine->name;
     int tokCount = slCount(tokLine);
 
     if (sameWord(command, "quit") || sameWord(command, "exit"))
         retCode = slcEof;
     else if (sameString(command, "cd") && tokCount > 1)
         {
         struct slName *dirName = tokLine->next;
         char *dir = dirName->name;
         int err = chdir(dir);
         if (err < 0)
             {
             warn("Couldn't change directory to %s", dir);
             retCode = slcError;
             }
         }
     else if (sameString(command, "pwd"))
         {
         char line[512];
         getcwd(line, sizeof(line));
         puts(line);
         }
     else if (sameString(command, "history"))
         {
         int count = dlCount(sh->history);
         int start = sh->commandsSeen - count + 1;
         int i = start;
         struct dlNode *hNode;
         for (hNode = sh->history->head; hNode->next != NULL; hNode = hNode->next)
             {
             printf("%d %s\n", i, hNode->val);
             ++i;
             }
         }
     else if (sameString(command, "alias"))
         {
         if (tokCount < 3)
             {
             fprintf(stderr, "alias - creates a shortcut for a command\n"
                             "usage\n"
                             "   alias  shortcut  command\n");
             retCode = slcError;
             }
         else
             {
             struct slName *tok = tokLine->next;
             command = tok->name;
             flattenOutput(sh, tok->next, " ");
             hashAdd(sh->aliases, command, cloneString(sh->outBuf));
             }
         }
     else if (sameWord(command, "set"))
         {
         if (tokCount < 4)
             {
             setUsage();
             retCode = slcError;
             }
         else
             {
             struct slName *tok = tokLine->next;
             char *var = tok->name;
             tok = tok->next;
             if (!sameString(tok->name, "="))
                 setUsage();
             else
                 {
                 flattenOutput(sh, tok->next, " ");
                 hashAdd(sh->variables, var, cloneString(sh->outBuf));
                 }
             }
         }
     else if (sameString(command, "source"))
         {
         if (tokCount != 2)
             {
             fprintf(stderr, "source - execute a batch file\n"
                             "usage:\n"
                             "    source file\n");
             retCode = slcError;
             }
         else
             {
             retCode = noHistoryShell(sh, tokLine->next->name);
             }
         }
     else if (sameString(command, "foreach"))
         {
         retCode = doForEach(sh, tokLine, f);
         }
     else if (sameString(command, "rm"))
         {
         if (tokCount < 2)
             {
             fprintf(stderr, "rm - remove files\n"
                             "usage:\n"
                             "    rm file(s)\n");
             }
         else
             {
             struct slName *tok = tokLine;
             while ((tok = tok->next) != NULL)
                 {
                 if (remove(tok->name) < 0)
                     {
                     fprintf(stderr, "Couldn't remove %s.\n", tok->name);
                     break;
                     }
                 }
             }
         }
     else if (sameString(command, "mv"))
         {
         retCode = doCpMv(tokLine, tokCount, line, TRUE);
         }
     else if (sameString(command, "cp"))
         {
         retCode = doCpMv(tokLine, tokCount, line, FALSE);
         }
     else if (sameString(command, "ls"))
         {
         retCode = doLs(tokLine, tokCount, line);
         }
     else if (sameString(command, "cat"))
         {
         retCode = doCat(tokLine, tokCount, line);
         }
     else if (sameString(command, "echo"))
         {
         struct slName *tok;
         for (tok = tokLine->next; tok != NULL; tok = tok->next)
             {
             fputs(tok->name, stdout);
             if (tok->next != NULL)
                 fputc(' ', stdout);
             }
         fputc('\n', stdout);
         }
     else
         {
         retCode = doSystem(sh, tokLine);
         }
     }
 undoRedirection(&rd);
 slFreeList(&tokLine);
 return retCode;
 }
 
 void maskControlC()
 /* Make controlC not terminate us. */
 {
 signal(SIGINT, handleControlC);
 }
 
 void interactiveShell(struct shell *sh)
 /* Interpret shell on terminal input. */
 {
 maskControlC();
 for (;;)
     {
     enterLine(sh->history, sh->prompt, sh->inBuf, sh->inBufAlloc);
     if (!doHistory(sh))
         continue;
     if (shellLine(sh, sh->inBuf, stdin) == slcEof)
         break;
     }
 }
 
 
 int main(int argc, char *argv[])
 {
 struct shell *sh;
 char *cshrc = "C:\\csh.rc";
 
 makeIsNameChar();
 sh = newShell();
 
 if (fileExists(cshrc))
     noHistoryShell(sh, cshrc);
 if (argc == 2)
     {
     noHistoryShell(sh, argv[1]);
     }
 else
     {
     interactiveShell(sh);
     }
 return 0;
 }