System Programming with C:Questions

Hello all,
I am now following the PPT from my gf's c++ class and trying to do the last homework. Basically it involves creating my own shell.I have completed commands like $pwd,$ls,$more etc., but frustrated when it comes to redirect stdin and stdout for calling programs. Below I attached two codes adopted from my whole code and the templates. There are problems with them.

CODE 1
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
/* ----------------------------------------------------------------- */
/* PROGRAM  shell.c                                                  */
/*    This program reads in an input line, parses the input line     */
/* into tokens, and use execvp() to execute the command.             */
/* ----------------------------------------------------------------- */

#include <unistd.h>     // getpid(), getcwd()
#include <sys/types.h>  // type definitions, e.g., pid_t
#include <sys/wait.h>   // wait()
#include <signal.h>     // signal name constants and kill()
#include <iostream>
#include <vector>
#include <cstring>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
/* ----------------------------------------------------------------- */
/* FUNCTION  parse:                                                  */
/*    This function takes an input line and parse it into tokens.    */
/* It first replaces all white spaces with zeros until it hits a     */
/* non-white space character which indicates the beginning of an     */
/* argument.  It saves the address to argv[], and then skips all     */
/* non-white spaces which constitute the argument.                   */
/* ----------------------------------------------------------------- */

void  parse(char *line, char **argv)
{
     while (*line != '\0') {       /* if not the end of line ....... */ 
          while (*line == ' ' || *line == '\t' || *line == '\n')
               *line++ = '\0';     /* replace white spaces with 0    */
          *argv++ = line;          /* save the argument position     */
          while (*line != '\0' && *line != ' ' && 
                 *line != '\t' && *line != '\n') 
               line++;             /* skip the argument until ...    */
     }
     *argv = '\0';                 /* mark the end of argument list  */
}

/* ----------------------------------------------------------------- */
/* FUNCTION execute:                                                 */
/*    This function receives a commend line argument list with the   */
/* first one being a file name followed by its arguments.  Then,     */
/* this function forks a child process to execute the command using  */
/* system call execvp().                                             */
/* ----------------------------------------------------------------- */
     
void  execute(char **argv)
{
     pid_t  pid;
     int    status;
     
     if ((pid = fork()) < 0) {     /* fork a child process           */
          printf("*** ERROR: forking child process failed\n");
          exit(1);
     }
     else if (pid == 0) {          /* for the child process:         */
          if (execvp(*argv, argv) < 0) {     /* execute the command  */
               printf("*** ERROR: exec failed\n");
               exit(1);
          }
     }
     else {                                  /* for the parent:      */
          while (wait(&status) != pid)       /* wait for completion  */
               ;
     }
}

/* ----------------------------------------------------------------- */
/*                  The main program starts here                     */
/* ----------------------------------------------------------------- */
     
int  main(void)
{
     char  line[1024];             /* the input line                 */
     char  *argv[64];              /* the command line argument      */
     int i;
     
     
     while (1) {                   /* repeat until done ....         */
          printf("Shell -> ");     /*   display a prompt             */
          cin.getline(line,1024);              /* Mark1 read in the command line     */
          printf("\n");
          parse(line, argv);       /*   parse the line               */
	  
	  cout << "input mode\n1 is read from file;\n" 
		   << "2 is write from file;\n"
		   << "0 is read and write from file\n";
	      
	      cin >> i;//scanf("%d",&i); //Mark2
	      cout << " you put in: " << i << endl;
          if (strcmp(argv[0], "exit") == 0)  /* is it an "exit"?     */
               return 0;            /*   exit if it is                */
          execute(argv);           /* otherwise, execute the command */
     }
}

[b]Q1: [/b]
if you compile and run it,Mark 1 is only valid once. If you put in like 1,2 3 then hit enter, Mark1 is no longer functional! Can someone give me an explanation?

Thanks.
Another question is in another post as the code is too long.
I wonder if I can attach a file here.
Last edited on
CODE 2:
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
#include <unistd.h>     // getpid(), getcwd()
#include <sys/types.h>  // type definitions, e.g., pid_t
#include <sys/wait.h>   // wait()
#include <signal.h>     // signal name constants and kill()
#include <iostream>
#include <vector>
#include <cstring>
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <fcntl.h>
//#include "aux.h"

using namespace std;




int main(){
  char command[128];
  int status = 0;
  vector<char > row;
  vector<vector<char > > hist;
  cout << "welcome to my shell\tpiggly wiggly\n";
  while(1){
    char buf[128];
    if( getcwd(buf,128) == NULL ){
      perror("getcwd error\n");
    }
    
    else{
      cout << buf << "< ";
  }
    switch (status){
      case 0:{
	cout << "My Shell > ";
	cin.getline(command,128);
	
	
      }
      case 1:{
	status = 0;}
    }
    
    
    if ( strcmp(command,"!#") != 0){
	for(int i=0;command[i] != '\0';i++){
	  row.push_back(command[i]);}
	hist.push_back(row);
	row.clear();
    }
      
      
 
    
    char* com1 = strtok( command," ");
    
    char* temp = com1;
    vector<char *> arg;
    
    while(temp != NULL){
      
      arg.push_back(temp);
      //cout << temp << endl;
      temp = strtok(NULL," ");

    }
 
    int size = hist[hist.size()-1].size();
    char cmd[size];
    for(int j=0; j<size; j++){
      cmd[j] = hist[hist.size()-1][j];}
      cmd[size] = '\0';
      while(1){
	if(sizeof cmd == 0){
	  break;
	}
      

    
    
    /*here:
     * cmd stores the command you have just put in;
     * command is now only the first word
     * com1 is the same as command
     * arg is a vector of char[] storing the tokens of the command you put in
     */
    
    
    
    
    
    
  /*dealing with exit
   *
   * 
   * 
   * 
   * 
    */  
  if( !strcmp( com1,"exit") ){
    cerr << "are you sure you want to exit?\t press y to exit\n";
    char y_n[128];
    cin.getline(y_n,128);
    if(y_n[0] == 'y'){
      return 0;
    }
    
    else{
    }
  }
  
  else if( (!strncmp(com1, "./",2) )	){
    //cout << arg[1] << endl << arg[2] << endl << arg[3] << endl << arg[4] << endl;
    bool ok = true;
    int size_io = arg.size()-1;
    
    //cout << "input parameters: \t" << size_io << endl;
    if(size_io == 0){
      cout << "you can redirect stdin and stdout\n";
      system(com1);}
      
    else{
      
      if((size_io != 4) && (size_io != 2)){
	ok = false;
	cerr << "put in 2 or 4 arguments\n";
      }
      if(size_io == 4){
	if( !strcmp(arg[1],arg[3]) ){
	  ok = false;
	  cerr << "one input and one output\n";
	}
      }
 
      if( (strcmp(arg[1],"<")) &&(strcmp(arg[1],">"))){
	cerr << "No.2 term is not < or >\n";
	ok = false;
      }
      
      if(size_io == 4){
	if( (strcmp(arg[3],"<")) &&(strcmp(arg[3],">"))){
	  ok = false;
	  cerr << "No.4 term is not < or >\n";
	}
      }
      //cout << "size is " << size << endl;
      char in[size];
      char out[size];
      bool defin = false;
      bool defout = false;
      //cout << "getting in and out\n";
      
	if(!strcmp(arg[1],"<")){
	  strcpy(in,arg[2]);
	  //cout << " copy in from arg2\n";
	  defin = true;
	  
	}
	if(!strcmp(arg[1],">")){
	  strcpy(out,arg[2]);
	  //cout << " copy out from arg2\n";
	  defout = true;
	}
	
	
      
      
      if(size_io == 4){
	if(!strcmp(arg[3],"<")){
	  strcpy(in,arg[4]);
	  defin = true;
	  
	}
	if(!strcmp(arg[3],">")){
	  strcpy(out,arg[4]);
	  defout = true;
	}
      }
      if(defout == true){
	cout << "write to :\t" << out << endl;
	FILE * os = freopen(out,"a+",stdout);
      }
      if(defin == true){
	cout << "read from :\t" << in << endl;
	FILE * is = freopen(in,"r",stdin);
      }
      if (ok == true){

	char** argv = new char*[arg.size()+1];

      for ( int k = 0; k < 1; k++ ){
	argv[k] = arg[k];
	cerr << argv[k] << "\n";
      }
      argv[1] = '\0';
      //argv is the array version of vector args ending with NULL(2D array)
	argv[arg.size()] = NULL;
	pid_t pid;
	int sstatus;
	if ((pid = fork()) < 0) {     /* fork a child process           */
          printf("*** ERROR: forking child process failed\n");
          exit(1);
	}
	else if (pid == 0) {          /* for the child process:         */
          if (execvp(com1, argv) < 0) {     /* execute the command  */
               printf("*** ERROR: exec failed\n");
               exit(1);
          }
     }
     else {                                  /* for the parent:      */
          while (wait(&sstatus) != pid);       /* wait for completion  */
               
     }
	  
	  //execlp(com1,NULL);
	  
	  if(defin == true){
	    //fclose(stdin);
	    
	    freopen("/dev/stdin","r",stdin);
	    cerr << "stdin restored!\n";
	    
	  }
	  if(defout == true){
	    //fclose(stdout);
	    
	    freopen("/dev/stdout","w",stdout);
	    cerr << "stdout restored!\n";
	  }
	  
	
      }
    
      else{
	cerr << "the format is wrong; use:\n"
	    << "./a.out < input > output\n"
	    << "./a.out < input\n"
	    << "./a.out > output\n";
      }
	      
      
 
    

    
   
    //fclose(stdout);
  }
  perror(cmd);
  }
  
  /*dealing with command that isn't found by the above situations
   * such commands will be sent to the shell and do it the normal way
   * 
   * 
   * 
   * 
   * 
   */
  else {
    
    cout << ("normal shell command\n");
    system(cmd);
    
  }
  
    
    
    break;
  }
  //cout << " byebye\n";
  //return 0;
  
}
}
    




Q2:
Again you can compile and run it.
Try in my shell type in:$./exe > output
./exe can be any executable that you have written that needs input and output. It will write the message to the file output instead of stdout.

Try:$./exe < input
this time ./exe will take command from the input file you named instead of from stdin

My basic method is:before I run a program in my shell
freopen(filename,"r",stdin)
then use
execvp()
to execute the program in the shell
after that
fclose(stdin)


Obviously this is dysfunctional; it used to be able to read and write to a file but will quit my shell after that. I modified the code but it is doing worse.
Can someone give some advice?

Thanks.
Last edited on
1
2
   cin.getline(line,1024);
   cin >> i; //The '\n' at the end of the buffer is not discarded 


Please minimize your second example.

sorry, but I have re-formatted. I believe these codes are compilable.

so let me show the result:



caven@dethklok:~/Dropbox/CLC/proj5$ g++ q2.cpp -o q2
caven@dethklok:~/Dropbox/CLC/proj5$ ./q2
welcome to my shell	piggly wiggly
/home/caven/Dropbox/CLC/proj5< My Shell > ./q2 > output
write to :	output
./q2
>
output
ls
pwd
exit
are you sure you want to exit?	 press y to exit
y
stdout restored!
./q2 > output: No such file or directory
l
sh: l: not found
exit
are you sure you want to exit?	 press y to exit
y
caven@dethklok:~/Dropbox/CLC/proj5$ more output
welcome to my shell	piggly wiggly
/home/caven/Dropbox/CLC/proj5< My Shell > aux.cpp
aux.cpp~
aux.h
aux.h~
input
input~
makefile
makefile~
model
model.cpp
model.cpp~
ouput
output
q1
q1.cpp
q1.cpp~
q2
q2.cpp
q2.cpp~
shell
shell_B.cpp
shell_B.cpp~
shell.cpp
shell.cpp~
temp
test
test.cpp
test.cpp~
try
v1.cpp
normal shell command
/home/caven/Dropbox/CLC/proj5< My Shell > /home/caven/Dropbox/CLC/proj5
normal shell command
/home/caven/Dropbox/CLC/proj5< My Shell > 



so you can see above that the output is working, just like we directly run from the terminal.
but my question is :
in the result:

are you sure you want to exit?	 press y to exit
y



I have exited from the executable that is run in my shell.but it didn't return to the shell interface. There must be something wrong, but I can't figure it out.

likewise i can do:

caven@dethklok:~/Dropbox/CLC/proj5$ ./q2
welcome to my shell	piggly wiggly
/home/caven/Dropbox/CLC/proj5< My Shell > ./q2 < input




then it will go into a dead loop! it won't do line 37 in code 2 but keeping print the shell interface starter
<home/caven/Dropbox/CLC/proj5<My Shell>




really confused by that...

looking forward if someone can give me a bit advice. thanks.
Last edited on
thanks, but I don't think this is for my situation. take the case of linux shell for example, if you try
$./exe > output


the stdout goes to the file output.

you are right that I can adopt the fprintf in my ./exe source code, but it is obvious that with the linux shell format I don't have to change my ./exe source code at all, right?

maybe I didn't specify my problem clearly. thanks.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int pid = fork();
if( pid==-1 ){
   perror("fork");
   return 1;
}
if( pid==0 ){
   //child
   //Redirecting output
   freopen(file, "w", stdout);
   execv( args[0], args );
   perror("execv");
}
else{
   //parent
   wait(NULL);
}


As you see, you don't switch back. It simply changes the streams in the child, while the parent is unaffected.
Last edited on
Topic archived. No new replies allowed.