/**************** It is a mistake to use (raw) curse terminal, consider using GNU readline instead *************/ /*An "MS-DOS" Command-line Interpreter (shell) for Unix You are to implement a simple shell for Unix that accepts MS-DOS commands. Users are familiar with MS-DOS commands, and like the output displayed similar to MS-DOS systems. Typically, they would like to type their favorite commands (dir, ch, del, ren) along with appropriate switches and wildcards (*), and have the interpreter perform the system calls and library functions. The result will be formatted and displayed in a manner similar to how MS-DOS displays the results. Your interpreter should prompt with "currentdir>", and accept the following commands (case insensitive): Key It keeps track of command lines typed at the prompt. Syntax: Key [-N | -E] -N number of commands to keep in the history list. Default is 20. -E quit keeping track of previous commands. Arrow Up/Down Using the Up or Down arrow keys should recall previous commands by scrolling through the history list. Selecting a command and hitting return should invoke the command. Dir It displays a list of files and subdirectories in a directory. The "*" wildcard can be used to select multiple files. Syntax: DIR [path] [filename] [-P] [-W] -P pauses after each screenful of information. -W uses wide list format. Example: /home/y>dir Directory of /home/y . 04-16-97 1:20p .. 04-16-97 1:20p UA0249 EXE 19,423 08-14-98 2:42p UA0249 TXT 2,399 06-23-98 1:01p UA0249 RPT 9,789 06-23-98 1:01p BROWSE COM 1,010 10-03-96 2:46p 4 file(s) 32621 bytes 2 dir(s) DEL It deletes one or more files. The "*" wildcard can be used to select multiple files. Syntax: DEL [path]filename [-P] /P prompts for confirmation before deleting each file. Example: /home/y>del dir.txt -p dir.txt, Delete (Y/N)? REN It renames a file/directory. Syntax: REN [path][directoryname1 | filename1] [directoryname2 | filename2] Example: /home/y>ren dir.txt dir.bak CD It displays the name of or changes the current directory. Syntax: CD [path], or CD [..] Example: /home/y>cd .. /home> EXIT Exit the MS-DOS shell. */ /* Anan Tongprasith */ #include #include #include #include #include #include #include #include /*Global variable*/ const char *del1="del",*exit1="exit",*cd1="cd",*ren1="ren",*key1="key",*dir1="dir"; char *myprompt="dos-",paramlist[10][50]; int maxline=21;maxhis=0,key=0,starthis=0,currentindex=0; /* This is for error number returned from system call.*/ extern int errno; /* Max history is 20 data + 1 space +1 position mark = total 22 record*/ int main() { int i,myx,myy,myinput,myindex=0,starty,hisindex=0; int x,maxparam;/*systemmax=22*/ char myhistory[22][100]; char mymesg[50],mycommand[50],*mymark="|:>"; /*Initialize curse terminal*/ initscr(); cbreak();echo(); nonl(); intrflush(stdscr,FALSE);keypad(stdscr,TRUE); setscrreg(0,25); scrollok(stdscr,TRUE); strcat(myprompt,getcwd(mymesg,50)); strcat(myprompt,&'>'); /*Set up the first mark in history array. This mark help prevent down arrow key going beyond current index.*/ strcpy(myhistory[currentindex],mymark); /* Say something */ addstr("Basic DOS-emulator");myy=mynewline(myy); addstr("This program is a part of CSE3320 Operating Systems."); myy=mynewline(myy); addstr("Written by Anan Tongprasith.");myy=mynewline(myy); /*Begin accepting command*/ addstr(myprompt); /*First prompt*/ getyx(stdscr,starty,myx); /*Get starting point*/ for(i=1;i<2210;i++) { myinput=getch(); /*Accepting input*/ switch (myinput) /*Determining input*/ { case 13: /*Enter key*/ getyx(stdscr,myy,myx); /* Using terminator to wrap up command*/ mycommand[myindex]='\0'; myy=mynewline(myy); /* Check if mycommand is null*/ if(strcmp(mycommand,&'\0')!=0) { /* Record history if key command is active */ if(key!=0) { /* Copy command into history index */ strcpy(myhistory[currentindex],mycommand); currentindex++; /* Goto record 0 if over 20+1+1 records*/ if(currentindex==22) { currentindex=0; } /* Calculate how to shift the whole set if full */ /* The recorded history cross over 22 and 0 */ if(currentindex<=starthis) { currentindex+=22; /*imaginary index*/ } /* If full, move the starting point*/ if(currentindex==starthis+maxhis) if(++starthis==22) starthis=0; currentindex=currentindex%22; /*real index*/ /* Put current position mark in place */ strcpy(myhistory[currentindex],mymark); hisindex=currentindex; } /* Separate parameters and execute the command*/ maxparam=separate(mycommand); if(execute(maxparam)==1) return 0; /*exit program*/ } myindex=0; /* New prompt on newcom line */ addstr(myprompt); refresh(); break; case 8: /*Backspace key*/ case 127: /*Delete key*/ case KEY_LEFT: /*Left arrow key*/ myindex-=1; if(myindex<0) { addstr(&'>');myindex=0; } break; case KEY_UP: /*Up arrow key pressed*/ /* Check if key command is active or not */ if(key!=0) { /* Clear current line */ getyx(stdscr,myy,myx); move(myy,0); clrtobot(); /* Put a newcom prompt in*/ addstr(myprompt); --hisindex; /* Circular history-index check */ /*Never go under starting index*/ if(hisindex-starthis==-1) hisindex=starthis; /* If go beyond 0 then circle back to 22 */ if(hisindex<0) hisindex=21; /*(1to22)=(0to21)*/ /*Put an old command in*/ if(strcmp(mymark,myhistory[hisindex])!=0) { strcpy(mycommand,myhistory[hisindex]); addstr(mycommand); myindex=strlen(mycommand); } refresh(); } break; case KEY_DOWN: /*Down arrow key pressed*/ /* Check if key command is active or not */ if(key!=0) { /* Clear current line */ getyx(stdscr,myy,myx); move(myy,0); clrtobot(); /* Put a newcom prompt in */ addstr(myprompt); /*Looking for current position*/ if(strcmp(myhistory[hisindex],mymark)!=0) { hisindex++; hisindex=hisindex%22; if(strcmp(myhistory[hisindex],mymark)!=0) { strcpy(mycommand,myhistory[hisindex]); addstr(mycommand); myindex=strlen(mycommand); } else { strcpy(mycommand,&'\0'); myindex=0; } } refresh(); } break; default: if (myinput<128) mycommand[myindex]=myinput; myindex++; } } } /* This function is for inserting a new line in. */ int mynewline(int myyy) { int mvy,mvx; getyx(stdscr,mvy,mvx); /* Scroll the terminal if maxline is met */ if(++mvy>maxline) { scroll(stdscr);mvy--; move(mvy,0);refresh(); return(mvy); } /* Just increase line number if maxline is not met */ move(mvy,0);refresh(); return mvy; } /* This function is for separating command and parameters */ int separate(char newcom[50]) { int i=0,j=0,k=0,linelength=strlen(newcom);char piece[50]; /* Big loop for the whole command line */ do { j=0; /* Check for spaces (or tabs) */ while(newcom[i]==' '||newcom[i]==' ') i++; /* Separation loop */ while (newcom[i]!=' '&&newcom[i]!='\0'&&newcom[i]!=' ') { piece[j]=newcom[i]; /*buffer=data*/ j++;i++; } /* Terminate the buffer before copying */ piece[j]='\0'; /* Copying the buffer into parameter list */ strcpy(paramlist[k],piece); *piece='\0'; /*reset buffer*/ k=k+1;i++; } while(i1) { mydel(max); } else { addstr("Usage: del filenames [-p]"); myy=mynewline(myy); } return 0; } /* Check if it is a cd command */ if(strcmp(paramlist[0],cd1)==0) { if(max==2) { mycd(); } else { addstr("Usage: cd pathname"); myy=mynewline(myy); } return 0; } /* Check if it is a ren command */ if(strcmp(paramlist[0],ren1)==0) { if(max==3) { myren(); } else { addstr("Usage: ren filename1 filename2"); myy=mynewline(myy); } return 0; } /* Check if it is a key command */ if(strcmp(paramlist[0],key1)==0) { /* If no parameter specified set it to -20 */ if(max==1) { max=2; strcpy(paramlist[1],buf); } if(max==2&¶mlist[1][0]=='-') { mykey(); } else { addstr("Usage: key [-n] [-e]"); myy=mynewline(myy); } return 0; } /* Check if it is an exit command*/ if(strcmp(paramlist[0],exit1)==0) { return 1; } addstr("Invalid command"); myy=mynewline(myy); return 0; } /* This function do the delete file job */ int mydel(int mycount) { int i=1;char myoption[3]="-p",myanswer[3]="y"; int ask=0,myyy,myxx; getyx(stdscr,myyy,myxx); /* Check for option -p */ if(strcmp(paramlist[mycount-1],myoption)==0) { ask=1;mycount--; /* Option -p but no filename */ if (mycount==1) { addstr("Usage: del filenames [-p]"); myyy=mynewline(myyy); return 0; } } for(i=1;i'); } } /*This function do the rename job*/ int myren() { int myy; if (link(paramlist[1],paramlist[2])==0) { /* Check if able to unlink the first file */ if(unlink(paramlist[1])!=0) { unlink(paramlist[2]); addstr("Error! cannot rename the file."); } } else switch(errno) { case EACCES: addstr("Error! permission denied."); break; case EDQUOT: addstr("Error! out of disk space."); break; case EEXIST: addstr(paramlist[2]); addstr(" already exists."); break; case ENOENT: addstr("Error! cannot find "); addstr(paramlist[1]); break; case EPERM: addstr("Error! cannot rename a directory"); break; default: addstr("Error!"); } myy=mynewline(myy); } /*This function process the key command */ int mykey() { char cancl[5]="-e";int newmax,oldkey,myy; /* Check for the flag -e */ if(strcmp(paramlist[1],cancl)==0) { key=0; return 0; } /* No -e flag */ else { strcpy(cancl,paramlist[1]); newmax=abs(atoi(cancl)); /* Check if new max is greater than the old one */ if(key0&&key<21) { /* if restart from -e clear the history */ if(oldkey==0) starthis=currentindex; sprintf(cancl,"%i",key); addstr("New maximum is ");addstr(cancl); sprintf(cancl,"%i",key); maxhis=key+1; /* n data + 1 mark */ myy=mynewline(myy); } else { addstr("Key number must be between 0 and 20."); myy=mynewline(myy); key=oldkey; } } else /* New max is less than the old one; stick with the old one */ { addstr("Maximum "); sprintf(cancl,"%i",key); addstr(cancl);addstr(" remains."); myy=mynewline(myy); } } } /* This function do the directory command */ int mydir(int mmax) { int dircount=0,filecount=0,i,w=0,p=0,myy,myx,myline=0; char buff[50],myday[3],mymon[3],myyear[3],myhour[3],mymin[3]; struct dirent *dp;struct stat fp;struct tm *mytime; DIR *dfd;long bytecount=0; /* Check for -p and -w flag */ if(mmax>1) { if(strcmp(paramlist[mmax-1],"-p")==0||strcmp(paramlist[mmax-2],"-p")==0) { p=1;mmax--; } if(strcmp(paramlist[mmax-1],"-w")==0||strcmp(paramlist[mmax-2],"-w")==0) { w=1;mmax--; } } /* If no name is specified, use "." */ if(mmax==1) strcpy(paramlist[1],&'.'); if(mmax>2) {addstr("One at a time please.");mynewline(myy);return 0;} /* Check if it is a file or a directory */ if((dfd=opendir(paramlist[1]))==NULL) { /* Happen to be a file */ if(lstat(paramlist[1],&fp)==0) { getcwd(buff,50); addstr("Directory of ");addstr(buff); myy=mynewline(myy); addstr(paramlist[1]); sprintf(buff,"%i",fp.st_size); bytecount+=fp.st_size; move(myy,20); addstr(buff); mytime=gmtime(&(fp.st_mtime)); if(mytime->tm_year<10) sprintf(myyear,"0%i",mytime->tm_year); else sprintf(myyear,"%i",mytime->tm_year); if(mytime->tm_mon<9) sprintf(mymon,"0%i",mytime->tm_mon+1); else sprintf(mymon,"%i",mytime->tm_mon+1); if(mytime->tm_mday<10) sprintf(myday,"0%i",mytime->tm_mday); else sprintf(myday,"%i",mytime->tm_mday); if(mytime->tm_hour<10) sprintf(myhour,"0%i",mytime->tm_hour); else sprintf(myhour,"%i",mytime->tm_hour); if(mytime->tm_min<10) sprintf(mymin,"0%i",mytime->tm_min); else sprintf(mymin,"%i",mytime->tm_min); sprintf(buff,"%s-%s-%s %s:%s",mymon,myday,myyear,myhour,mymin); move(myy,30); addstr(buff); myy=mynewline(myy); } else { addstr("Error! cannot open the directory."); myy=mynewline(myy); } } /* Happen to be a directory */ else { getcwd(buff,50);strcat(buff,&'/'); addstr("Directory of ");addstr(buff);addstr(paramlist[1]); myy=mynewline(myy); if(p==1) myline=check(myline); /* Read the directory entries */ while((dp=readdir(dfd))!=NULL) { lstat(dp->d_name,&fp); /* Found a directory */ if(S_ISDIR(fp.st_mode)) { dircount++; addstr(dp->d_name); /* No -w flag, full information return */ if(w==0) { move(myy,20); addstr(""); mytime=gmtime(&(fp.st_mtime)); if(mytime->tm_year<10) sprintf(myyear,"0%i",mytime->tm_year); else sprintf(myyear,"%i",mytime->tm_year); if(mytime->tm_mon<9) sprintf(mymon,"0%i",mytime->tm_mon+1); else sprintf(mymon,"%i",mytime->tm_mon+1); if(mytime->tm_mday<10) sprintf(myday,"0%i",mytime->tm_mday); else sprintf(myday,"%i",mytime->tm_mday); if(mytime->tm_hour<10) sprintf(myhour,"0%i",mytime->tm_hour); else sprintf(myhour,"%i",mytime->tm_hour); if(mytime->tm_min<10) sprintf(mymin,"0%i",mytime->tm_min); else sprintf(mymin,"%i",mytime->tm_min); sprintf(buff,"%s-%s-%s %s:%s",mymon,myday,myyear,myhour,mymin); move(myy,30); addstr(buff); myy=mynewline(myy); if(p==1) myline=check(myline); } else /* -w flag, spread into 4 columns */ { move(myy,20*w); if(w++>3) { myy=mynewline(myy); w=1; if(p==1) myline=check(myline); } } } /* A regular file is found */ if(S_ISREG(fp.st_mode)) { filecount++; addstr(dp->d_name); sprintf(buff,"%i",fp.st_size); bytecount+=fp.st_size; /* No -w flag, full information return */ if(w==0) { move(myy,20); addstr(buff); mytime=gmtime(&(fp.st_mtime)); if(mytime->tm_year<10) sprintf(myyear,"0%i",mytime->tm_year); else sprintf(myyear,"%i",mytime->tm_year); if(mytime->tm_mon<9) sprintf(mymon,"0%i",mytime->tm_mon+1); else sprintf(mymon,"%i",mytime->tm_mon+1); if(mytime->tm_mday<10) sprintf(myday,"0%i",mytime->tm_mday); else sprintf(myday,"%i",mytime->tm_mday); if(mytime->tm_hour<10) sprintf(myhour,"0%i",mytime->tm_hour); else sprintf(myhour,"%i",mytime->tm_hour); if(mytime->tm_min<10) sprintf(mymin,"0%i",mytime->tm_min); else sprintf(mymin,"%i",mytime->tm_min); sprintf(buff,"%s-%s-%s %s:%s",mymon,myday,myyear,myhour,mymin); move(myy,30); addstr(buff); myy=mynewline(myy); if(p==1) myline=check(myline); } else /* -w flag, spread into 4 colums */ { move(myy,w*20); if(w++>3) { w=1; myy=mynewline(myy); if(p==1) myline=check(myline); } } } } myy=mynewline(myy); if(p==1) myline=check(myline); /* Display the summary information */ sprintf(buff,"%i",filecount); addstr(buff);addstr(" file(s)"); sprintf(buff,"%i",bytecount); move(myy,20);addstr(buff);addstr(" bytes"); myy=mynewline(myy); if(p==1) myline=check(myline); sprintf(buff,"%i",dircount); addstr(buff);addstr(" dir(s)"); myy=mynewline(myy); if(p==1) myline=check(myline); closedir(dfd); } } /* This function helps stop the display when a whole page filled */ int check(int mline) { mline++; if(mline+1>maxline) { move(mline,0);addstr("Press any key to continue"); getch(); move(mline,0);clrtobot();refresh(); return 0; } return mline; }