This is the Message Centre for Afgncaap5

Necessary Info Storage

Post 1

Afgncaap5

The computer lab computers where I program seem INCAPABLE of simple actions like cutting and pasting, rendering it impossible for me to, say, put snippets of code into eMails so that I can call on the wisdom of programmers superior to myself.

So, since h2g2 never seems to have that type of problem....here's the code!

#include
#include
#include
#include
#include
#include
#include
#include

#define MAXARGS 64
#define BUFLEN 81

struct buffer_t{
char linebuf[BUFLEN+1];
char *arglist[MAXARGS];
};

struct buffer_t buffer;

int main(int argc, char * argv[]){
int pid, bkg;
char *(args[MAXARGS+1]);
char **args2;

while(1){
/*check_exit_status(); goes here*/

printf("JML>");
bkg = find_background(args);

if(fgets(buffer.linebuf, BUFLEN, stdin) == NULL) break;
if(!parse_args(buffer.linebuf, args)) continue;

/* if(find_pipe(args, &args2)){ run_piped_children(args,
args2, bkg); } */

else{
if((pid=fork()) == -1) perror("fork");
else if(pid == 0) childproc(args);
else parentproc(pid, bkg);
}//end else
}//end while
exit(0);
} // end main

int parse_args(args){
char whitespace[] = " \t\n\r";
int nargs;

nargs = 0;
buffer.arglist[nargs] = strtok(buffer.linebuf, whitespace);
while (buffer.arglist[nargs] != NULL){
buffer.arglist[++nargs] = strtok(NULL, whitespace);
}//end while
return 1;
}//end parse_args

int internal_command(args){
extern char **environ;
if(strcmp(buffer.arglist[0], "environ")==0){
char **envp = environ;
while (*envp){
printf("%s\n", *envp++);
}//end while
return 1;
}//end if

else if(strcmp(buffer.linebuf, "path")==0){
char *envpath;
envpath=getenv("PATH");
printf("PATH:\n");
printf("%s\n", envpath);
return 1;
}//end else if

else if(strcmp(buffer.linebuf, "exit")==0){
exit(0);
}//end else if

return 0;
}//end internal_command

int childproc(args){
int err;
int nargs=0;
int tempfd;
int i;
err=execvp(buffer.linebuf, buffer.arglist);

for (i = 0; buffer.arglist != NULL; i++)
if (strcmp(buffer.arglist[i], "<") == 0) {
i++;
tempfd = open(buffer.arglist[i], O_RDONLY, 0);
/*CHECK FOR ERRORS HERE*/
close(0);
dup2(tempfd, 0);
close(tempfd);
}
if (strcmp(buffer.arglist[i], ">") == 0) {
i++;
tempfd = open(buffer.arglist[i], O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR);
/*CHECK FOR ERRORS HERE*/
close(1);
dup2(tempfd, 1);
close(tempfd);
}
if (strcmp(buffer.arglist[i], ">>") == 0) {
//SHOULD APPEND, NOT CHANGE STANDARD ERROR! CHANGE QUICKLY!
i++;
tempfd = open(buffer.arglist[i], O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR);
/*CHECK FOR ERRORS HERE*/
close(2);
dup2(tempfd, 2);
close(tempfd);
}

return 1;
} // end childproc

int parentproc(pid, bkg){
pid = waitpid(-1, &status, WNOHANG);

if (pid == -1)
//status is requested for child process
; //Just so that the computer's not confused
else if (pid > 0)
//pid specifies the pid of the child process for which status is requested
;
else if (pid == 0)
//status is requested for any child process whose process group ID is
//equal to that of the calling process.
;
else if (pid < -1)
//status is requested for any child process whose process group ID is
//equal to the absolute value of pid.
;

/* How non-backgrounding did it...

while (wait(&bkg) != pid);
return 1;

*/
}//end parentproc

int find_background(args){
return 0;
} //end find_background


Necessary Info Storage

Post 2

Afgncaap5

To give you an idea of how crazy this is: I had to copy the text from this computer's NEdit program, then I had to SSH into another computer that runs an older copy of the same program, copy it into that NEdit program, copy it out of there, then paste it back into this computer's "Text Editor" program (different from NEdit) before I could copy it there and past it onto h2g2.

I don't know what Sun Microsystems (or possibly Linux) has against the copy/paste commands....but they need to stop it.smiley - silly

Incidentally, if any tech savvy people out there can take a gander at that code, and tell me how to make it 1) background programs with an & at the end of a command line, 2) pipe the output of one program into the input of another, and 3) redirect the I/O of a command line, I'd much appreciate the assistance.

Not that I expect you to drop your whole life and work slavishly on this, mind you, but if you just *happen* to know the exact solution to the problem, it'd probably mean the difference between one and seven hours of sleep tonight.


Necessary Info Storage

Post 3

xyroth

if you are using unix in some form, and it sounds like you are, you have the problem of shortcut keys.

everyone who writes a new editing function does one of a small number of things.

1, copy the shortcut keys from their favourite editors.
2, come up with their own new set of shortcuts.
3, fail to impliment shortcuts entirely.

as vi and emacs use different shortcuts, as do a lot of other programs, you find incompatible sets of shortcuts, so what you expect to work doesn't carry across from one editor to another.

of course a lot of well written editors allow you to create a shortcut file with your own preferences, which gets around this problem.

another solution is to use the mouse. highlight what you want to copy, and then right click the mouse and this will usually give you a menu of options. you can then select from the small number of options available and copy or past at will. the mouse menu is often independent of the keyboard shortcut selections.

on the more substantive issue, it is not entirely clear what you are trying to do.

backgrounding a task can be done at invocation time by simply adding an "&" at the end of the line, for example:

mpg321 tune.mp3

would become...

mpg321 tune.mp3 &

as to doing it when the task is already running, you need to use a different shell (as the one running the program will not be accepting command line input).

you then use the command "bg" to put a task into the background and "fg" to bring it back.

as for piping, it is also fairly easy:

ls > dir.txt

would create a file called "dir.txt" and then fill it up with the listing of the directory.

ls >> dir.txt

would take the existing file "dir.txt" (creating it if it didn't exist) and append the listing of the directory at the end.

you can pipe to the next program using the split-vertical control character "|". for example:

ls | wc -l

would list the directory, pipe that output to the wc command, which would then count how many lines it had in it (-l, and when piped the ls command usually resorts to one file per line).

it is also possible to split the output so that standard out goes to one file, and standard error goes to another. i think it is done like this:

ls &1>output.txt &2>errors.txt

but I have not needed to do it for many moons so you need to verify that with someone else.

inputing from a file is easy as well. for example:

myprog <input.txt

would direct the program "myprog" to get its input from the file "input.txt" instead of standard in.

if you need to call a command from C, you would normally do something like:

strcpy(command,"ls -l");
system(command);

from within your program, and it would then present you with the long format listing (ls -l) of the current directory.

getting parameters from the command line is also fairly easy. for example:

if (strcmp(argv[i],"-v")==0) verbose=TRUE;

would check to see if the (i)th parameter was "-v" and if so set a flag called verbose to be true, whereas:

strcpy(param,argv[i]);

would copy the (i)th parameter into the string "param".

I hope this all helps.

I still can't quite figure out what you are trying to do in your program though.


Necessary Info Storage

Post 4

Afgncaap5

Long story short: I'm trying to do basically everything that you just described.smiley - winkeye

I'm trying to make myself a tiny little shell program that runs like a dumbed-down version of the Linux prompt. As such, at this shell's prompt the user will type (for instance) xclock & to run a backgrounded xclock program. Similar things go for I/O redirection and and piping.

Oh, and as for copying and pasting: I *was* using a mouse. The programs still wouldn't copy and paste (which is why I'm still not sure what was going wrong...there was no reason for that file to not copy and paste....but now that I have a copy saved here it should be fine).


Necessary Info Storage

Post 5

Traveller in Time Reporting Bugs -o-o- Broken the chain of Pliny -o-o- Hired

Traveller in Time smiley - tit trying to merge systems
"Reformulate the copy and paste story, it is the 'common base' on musof windows sytems making it easy.

Use 'common base' applications under Xwindows to copy and paste with the same ease smiley - biggrin "


Necessary Info Storage

Post 6

xyroth

why are you trying to do this?

if you are doing it for your users, why not just open a terminal for them and let them do the same thing under bash?

if you are doing it for yourself, you have set yourself quite a hard job. the basic idea of a command prompt is easy, you just read the input, evaluate what it means, and print the results, and then loop round again.

in practice, it is not quite as simple.

to enable them to background and foreground tasks, you are going to have to use system("ps -a >file.txt"); to give you the list of processes, and then system("fg process"); where process contains the information you got from listing the processes. as the commands are going to be the same as for a normal shell, it is usually easier just to start a shell and do the commands directly.

most of the people who might want to do what you are talking about will need root permissions anyway, and so will have the use of a basic shell.

as I say, why do you want to do it?


Necessary Info Storage

Post 7

Afgncaap5

"to enable them to background and foreground tasks, you are going to have to use system("ps -a >file.txt"); to give you the list of processes, and then system("fg process"); where process contains the information you got from listing the processes. as the commands are going to be the same as for a normal shell, it is usually easier just to start a shell and do the commands directly.

most of the people who might want to do what you are talking about will need root permissions anyway, and so will have the use of a basic shell."

Because I can.smiley - biggrinsmiley - scientist

Seriously, though, it's not an impossible task. I'm not going to make a full-fledged command prompt (signalling, for instance, is still something that I'm not even considering). Long story short, a professor gives programming challenges to me and a bunch of friends who enjoy these types of wacky challenges, and I've fallen behind in the latest batch. I know of three people who've gotten it to work perfectly out of the lot of us. I feel a need to be the fourth, for some reason.

So I'm struggling over it, losing sleep, and kicking myself for not getting it down just yet. It's a fun project, but it's driving me insane.


Necessary Info Storage

Post 8

xyroth

it should not be much of a problem.

you don't need to accept command parameters using argv and friends, except to help with passing debugging flags.

what you need to do is a basic read - eval - print loop.

this will take in the commands, check they are valid, and pass them to a routine which will call the right one.

I have already told you how to invoke the seperate commands.

if you are reading in your own command line, and still want to have command output being redirectable, if you stick to valid unix commands you can just pass the whole lot using the system command.

this should basically do the job, and is the standard method people use to write an interpreted language.

the main difference is that you will basically be writing a front end to an existing shell, so you can forget a lot of that messy evaluation, and let the system command do it for you.

unless the specification insists that your program must do all of the evaluation as well, or must be capable of operating as the shell, rather than on top of it.

if that is the case, you will have to impliment all the necessary extras to make it a stand-allone shell.


Necessary Info Storage

Post 9

Afgncaap5

Hmm....that helps, but I'm still stuck. I don't know how to proceed, and all I get now is a dumped core.

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#define MAXARGS 64
#define BUFLEN 81

struct buffer_t{
char linebuf[BUFLEN+1];
char *arglist[MAXARGS];
};

struct buffer_t buffer;

int main(int argc, char * argv[]){
int pid, bkg;
char *(args[MAXARGS+1]);
char **args2;

while(1){
check_exit_status();

printf("JML>");
bkg = find_background(args);

if(fgets(buffer.linebuf, BUFLEN, stdin) == NULL) break;
if(!parse_args(buffer.linebuf, args)) continue;

/* if(find_pipe(args, &args2)){ run_piped_children(args,
args2, bkg); } */

else{
if((pid=fork()) == -1) perror("fork");
else if(pid == 0) childproc(args);
else parentproc(pid, bkg);
}//end else
}//end while
exit(0);
} // end main

int find_pipe(*args, **args2){
int k;
int has_pipe = 0;//Change to 1 if has pipe, then return when done
int twopoint = 0;
int onepoint = 0;
for (k=0; buffer.arglist != NULL; k++){
if (strcomp(buffer.arglist[k], "|") == 0) {
has_pipe = 1;
k++; /*Here I make the not unreasonable, though unsafe,
*assumption that anyone who puts a pipe in a line
*is intending to have the pipe go somewhere. Silly
*me!
*/
}

if (has_pipe == 1){
args2[twopoint] = buffer.arglist[k];
}
else
args[onepoint] = buffer.arglist[k];

}//end for loop

return has_pipe;

}//end find_pipe

int parse_args(args){
char whitespace[] = " \t\n\r";
int nargs;

nargs = 0;
buffer.arglist[nargs] = strtok(buffer.linebuf, whitespace);
while (buffer.arglist[nargs] != NULL){
buffer.arglist[++nargs] = strtok(NULL, whitespace);
}//end while
return 1;
}//end parse_args

int check_exit_status(){ //FINISHED
int status, pid;
pid = waitpid(-1, &status, WCONTINUED);
if (pid > 0)
printf("Exited... \t%i", pid);
return;
}//end check_exit_status



int internal_command(args){
extern char **environ;
if(strcmp(buffer.arglist[0], "environ")==0){
char **envp = environ;
while (*envp){
printf("%s\n", *envp++);
}//end while
return 1;
}//end if

else if(strcmp(buffer.linebuf, "path")==0){
char *envpath;
envpath=getenv("PATH");
printf("PATH:\n");
printf("%s\n", envpath);
return 1;
}//end else if

else if(strcmp(buffer.linebuf, "exit")==0){
exit(0);
}//end else if

return 0;
}//end internal_command

int childproc(args){
int err;
int nargs=0;
int tempfd;
int i;
err=execvp(buffer.linebuf, buffer.arglist);

for (i = 0; buffer.arglist != NULL; i++)
if (strcmp(buffer.arglist[i], "<") == 0) {
i++;
tempfd = open(buffer.arglist[i], O_RDONLY, 0);
/*CHECK FOR ERRORS HERE*/
close(0);
dup2(tempfd, 0);
close(tempfd);
}
if (strcmp(buffer.arglist[i], ">") == 0) {
i++;
tempfd = open(buffer.arglist[i], O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR);
/*CHECK FOR ERRORS HERE*/
close(1);
dup2(tempfd, 1);
close(tempfd);
}
if (strcmp(buffer.arglist[i], ">>") == 0) {
//SHOULD APPEND, NOT CHANGE STANDARD ERROR! CHANGE QUICKLY!
i++;
tempfd = open(buffer.arglist[i], O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR);
/*CHECK FOR ERRORS HERE*/
close(2);
dup2(tempfd, 2);
close(tempfd);
}

return 1;
} // end childproc

int parentproc(pid, bkg){
int status = 0; // Not sure about status...
pid = waitpid(-1, &status, WNOHANG);

if (pid == -1)
//status is requested for child process
; //Just so that the computer's not confused
else if (pid > 0)
//pid specifies the pid of the child process for which status is requested
;
else if (pid == 0)
//status is requested for any child process whose process group ID is
//equal to that of the calling process.
;
else if (pid < -1)
//status is requested for any child process whose process group ID is
//equal to the absolute value of pid.
;

/* How non-backgrounding did it...

while (wait(&bkg) != pid);
return 1;

*/
}//end parentproc

int find_background(args){
//Returns 1 if the process is to be backgrounded,
//0 if it is not. Backgrounding is signified with
// an ampersand, "&".
int j;
for (j = 0; buffer.arglist != NULL; j++) {
if (strcmp(buffer.arglist[j], "&") == 0) {
return 1;
}
else
j++;
}
return 0;
} //end find_background


Necessary Info Storage

Post 10

xyroth

for debugging, you can often get the compiler and linker to help you out.

I use the Gnu Compiler, under dos, windows, cygwin and linux.

in all of these environments you can pass the parameter -Wall which will be very picky about what it will let through, and is very verbose about error reporting.

Another thing I do is allow the passing of the standard "-v" parameter at runtime to enable verbose debugging, and then use it to set a verbose flag, and if verbose is true print out much more output for debugging purposes.

this helps a lot.

so does compiling and linking as seperate tasks.

however I still can't see WHY you are doing it the way you are.

specifically, do you need to impliment a shell from scratch, to be used instead of the normal shell, or can you sit it on top of an existing shell?

if it can sit on top of an existing shell, then all the stuff to do with piping can just be passed through to the undelying shell.

if it is not a stand-alone shell implimentation, the algorithm is basically this:

start:

read line of input

do any error checking in addition to that done by the usual shell

pass the (possibly modified) commands to the underlying shell using system

go back to the start

if you are having to impliment a shell from scratch, you have a much bigger problem, which is well outside my level of expertise, as it fundamentally has to work with the kernel of the operating system at a very low level.


Key: Complain about this post

More Conversations for Afgncaap5

Write an Entry

"The Hitchhiker's Guide to the Galaxy is a wholly remarkable book. It has been compiled and recompiled many times and under many different editorships. It contains contributions from countless numbers of travellers and researchers."

Write an entry
Read more