Dec 16

公布期末设计Smallshell的完整源代码:

 

smalls.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
/* *************************************************
 *
 *          Small shell on Unix/Linux
 *
 *   This shell finished all the functions which
 *   are required. And I added new inner functions
 *   "help" and "exit" by myself.
 
 *   Enjoy it!
 *
 *   author: Longqianzhi 용성지
 *   Stu_ID: 12094817
 *   data:   Dec/27/2010
 *
 * *************************************************/
 
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <signal.h>
 
#define MAX_CMD_ARG 10
#define MAX_CMD_LIST 10
 
const char *prompt = "# ";
 
char* cmdlist[MAX_CMD_LIST];
char* cmdargs[MAX_CMD_ARG];
char  cmdline[BUFSIZ];
 
void fatal(char *str);
void execute_cmd(char *cmd);
void execute_cmdline(char* cmdline);
int makeargv(char *s, const char *delimiters,
                 char** argvp, int MAX_LIST);
void help();
void cd(char *path);
void catchint(int);
void join();                    /* Use one pipe to communication.*/
int join2();                    /* Use two pipe to communication.*/
int dpipe_test();               /* It's useless.Just for test.*/
void parse_filter(char *cmd);   /* A filter for argument*/
 
 
int main(int argc, char**argv){
    int i=0;
    static struct sigaction act;
 
    /*Cope with the signal*/
 
    act.sa_handler=catchint;
    sigfillset(&(act.sa_mask));
    sigaction(SIGINT,&act,NULL);
    sigaction(SIGQUIT,&act,NULL);
 
    while (1) {    
        fputs(prompt, stdout);
        fgets(cmdline, BUFSIZ, stdin);
        cmdline[ strlen(cmdline) -1] ='\0';
 
        execute_cmdline(cmdline);
        printf("\n");
    }
    return 0;
}
 
void fatal(char *str){
    perror(str);
    exit(1);
}
 
void execute_cmdline(char* cmdline){
    int count = 0;
    int i=0;
    count = makeargv(cmdline, "!", cmdlist, MAX_CMD_LIST);
 
    /* Execute the commands with different way.*/  
 
    /*Just a single command.*/
    if(count==1){
     
        /* First of all,try to make sure whether
         * the command is "cd".
         */
        if(cmdlist[0][0]=='c'&&cmdlist[0][1]=='d'){
            parse_filter(cmdlist[0]); 
            count = makeargv(cmdlist[0], " \t", cmdargs, MAX_CMD_ARG);
            if(!strcmp("cd",cmdargs[0])){
                if(chdir(cmdargs[1]))  fatal("cd");
                return;
            }
        }
     
        /* Fork a child process to run every
         * commands except "cd"
        */
          
        switch(fork())
        {
        case -1: fatal("fork error");
        case  0: execute_cmd(cmdlist[0]);
        default:
            wait(NULL);
            fflush(stdout);
        }
    }
     
    /*Execute two programs with a pipe.*/
    if(count==2){
        join();
    }
 
    /*Execute three programs with two pipes.*/
    if(count==3){
        join2();
    }
 
    /*There are too many commands*/
    if(count>=4){
        printf("Too many pipes!\n");
        return;
    }
}
 
/* *****************************************
 * Execute a single commands with arguments.
 *
 * *****************************************/
void execute_cmd(char *cmd){
    int i=0;
    int count = 0;
     
    parse_filter(cmd); 
    count = makeargv(cmd, " \t", cmdargs, MAX_CMD_ARG);
 
    /*
     * Recognize some inner functions
     * like cd,help,exit and so on.
     * Also you can add some new inner
     * functions by yourself here.
     *
     */
    if(!strcmp("help",cmdargs[0])){
        help();
        return ;
    }
 
    if(!strcmp("exit",cmdargs[0])){
        kill(getppid(),SIGKILL); /*kill parent process*/
        raise(SIGKILL); /*kill myself*/
        exit(0);
    }
 
    execvp(cmdargs[0], cmdargs);
    fatal("exec error");
}
 
/* *****************************************
 *  Count the arguments'number in a command.
 *
 * ***************************************/
int makeargv(char *s, const char *delimiters,
                    char** argvp, int MAX_LIST) {
 
    int i = 0;
    int numtokens = 0;
    char *snew = NULL;
 
    if( (s==NULL) || (delimiters==NULL) ) return -1;
 
    snew = s + strspn(s, delimiters);  
    if( (argvp[numtokens]=strtok(snew, delimiters)) == NULL )
    return numtokens;
     
    numtokens = 1;
    while(1){
        if( (argvp[numtokens]=strtok(NULL, delimiters)) == NULL)
            break;
        if(numtokens == (MAX_LIST-1)) return -1;
            numtokens++;
    }
    return numtokens;
}
 
/* *********************************
 * Inner fnuction: help().
 * Show some infomations to the stdin
 *
 ***********************************/
void help(){
    printf("\n  * *************************************** *");
    printf("\n  *                                         *");
    printf("\n  *         WELCOME TO SMALL SHELL !        *");
    printf("\n  *                                         *");
    printf("\n  * *************************************** *");
    printf("\n  *                                         *");
    printf("\n  *  There are 3 inner functions in shell.  *");
    printf("\n  *                                         *");
    printf("\n  *  1) cd :Change the current directory.   *");
    printf("\n  *  2) exit :Quit from the small shell.    *");
    printf("\n  *  3) help :Show this message for help.   *");
    printf("\n  *                                         *");
    printf("\n  *        IF YOU FIND ANY BUGS OR          *");
    printf("\n  *          HAVE ANY QUESTIONS             *");
    printf("\n  *                                         *");
    printf("\n  *    mail to me:longqianzhi@gmail.com     *");
    printf("\n  *                                         *");
    printf("\n  * *************************************** *\n");
    exit(0);
}
 
 
/* ***************************
 * Inner function:cd().
 * Change the current directory.
 *
 * ***************************/
  
void cd(char *path){
    if(chdir(path))
        fatal("cd");
}
 
 
/* ************************************
 * The function about act.sa_handler
 * If press the Ctrl+c, resume the shell.
 *
 * ************************************/
  
void catchint(int signo){
    printf("\nReceive a signo=%d\n",signo);
    printf("Returning to the shell\n");
 
    /* Init the cmdline Array.
     * If not,the shell will try to run
     * commands in the memory.
     */
    cmdline[0]='\0';
}
 
 
/* ************************************
 * An easy way to use double pipe.
 *
 * But this function is just for test and  
 * debug in the project,because I wrote 
 * a new one by myself. The new one is
 * called "join2".
 *
 * ***********************************/
 
int dpipe_test(){
 
    /*build command string with normal format.*/
 
    char newstr[128];
    strcpy(newstr,cmdlist[0]);
    strcat(newstr,"|");
    strcat(newstr,cmdlist[1]);
    strcat(newstr,"|");
    strcat(newstr,cmdlist[2]);
     
    /*call system shell to run the commmands*/
    system(newstr);
    return 0;
}
 
 
/* **************************************
 * Delete special character from argument.
 * If there is any special charater,
 * this function will change them to '\32'
 *
 * ***************************************/
  
void parse_filter(char *cmd){
    int i=0;
    int CMAX=strlen(cmd);
     
    while(cmd[i]!=' '&&i<CMAX) i++;
     
    if((++i)<CMAX){
        if (cmd[i++]=='-')  {  
            while(cmd[i]!=' '&&i<CMAX){
             
                if(cmd[i]<'0'||(cmd[i]>'9'&&cmd[i]<'A')||
                    (cmd[i]>'Z'&&cmd[i]<'a')||cmd[i]>'z'){
 
                    char tmp=cmd[i-1];         
                    cmd[i]=tmp;
                }
                i++;
            }
        }
    }
     
}
 
/* ***********************************
 * Link the child process's stdout to
 * grandchild process's stdin with pipe.
 *
 * ***********************************/
  
void join(){
    int p[2],status;
 
    switch (fork()){
        case -1:fatal("1st fork call in join()");
        case 0:break;
        default:wait(&status);
                return;
    }
     
    /* Try to make a new pipe.*/
    if(pipe(p)==-1) fatal("pipe call in join()");
 
    switch(fork()){
        case -1:fatal("2nd fork call in join()");
        case 0:
                dup2(p[1],1);
                close(p[0]);
                close(p[1]);
                execute_cmd(cmdlist[0]);
                fatal("1st execvp call in join()");
        default:
                dup2(p[0],0);
                close(p[0]);
                close(p[1]);
                execute_cmd(cmdlist[1]);
                fatal("2nd execvp call in join()");
    }
}
 
 
 
/* *************************************
 * Link three child/grandchild processes
 * one by one with two pipes.
 *
 * ************************************/
 
int join2(){
    int status,p1[2],p2[2];
     
    /*Test the double pipe.*/  
    if(dpipe_test()==0) return 0; 
 
    /* Grandparent process */
    switch(fork()){
        case -1:fatal("1st fork call");
        case 0:break;
        default:
               wait(&status);
               return status;
    }
 
 
    /* Parent process.Run the last command.*/
 
    if(pipe(p1)<0) fatal ("pipe call"); /*make a pipe.*/
 
    switch(fork()){
        case -1:fatal("2nd fork call");
        case 0:break;
        default:
               dup2(p1[0],0);
               close(p1[0]);
               close(p1[1]);
 
               execute_cmd(cmdlist[2]);
               fatal("1st exec call");
    }
     
    /* Try to make a new pipe.*/
 
    if(pipe(p2)<0) fatal("pipe call");
 
    /* Child process.Run the second command.*/
     
    switch(fork()){
        case -1:fatal("3th fork call");
        case 0:break;
        default:
               dup2(p1[1],1);
               dup2(p2[0],0);
               close(p1[0]);
               close(p1[1]);
               close(p2[0]);
               close(p2[1]);
 
               execute_cmd(cmdlist[1]);
               fatal("2nd exec call");
    }
 
    /* Grandchild process.Run the first command.*/
     
    dup2(p2[1],1);
    close(p2[0]);
    close(p2[1]);
 
    execute_cmd(cmdlist[0]);
    fatal("3th exec call");
 
}