Created
April 3, 2016 02:50
-
-
Save cympfh/70033b9347d5064172cffdb389c633b0 to your computer and use it in GitHub Desktop.
a shell
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <fcntl.h> | |
#include <sys/stat.h> | |
#include <sys/types.h> | |
#include <sys/wait.h> | |
#include <unistd.h> | |
// 標準のファイルディスクリプタ | |
#define STDIN 0 | |
#define STDOUT 1 | |
#define STDERR 2 | |
typedef struct process_ { | |
char **args; | |
char *infile; | |
char *outfile; | |
int outmode; //0->TRUNC, 1->APPEND | |
int pipe; // 直前のプロセスからのパイプなら非ゼロ | |
struct process_ *next; | |
} process; | |
//str.slice(a,b) | |
char*slice(char*str,int a,int b) { | |
char*str2=calloc(sizeof(char),b-a+1); | |
str2[b-a]='\0'; | |
for(--b;b>=a;--b)str2[b-a]=str[b]; | |
return str2; | |
} | |
// wordsの書き下し (debug) | |
void print_words(char**words) { | |
int i; | |
for (i=0; words[i]!=NULL;++i) { | |
printf("# %s\n", words[i]); | |
} | |
} | |
// prcの書き下し (debug) | |
void print_prc(process *prc) { | |
int i; | |
while (prc != NULL) { | |
printf("args = "); | |
for (i=0;prc->args[i];++i) printf("%s; ",prc->args[i]); | |
printf("\n"); | |
printf("infile = %s\n", prc->infile); | |
printf("outfile = %s (%s)\n", | |
prc->outfile, | |
(prc->outmode?"APPEND" : "TRUNC")); | |
printf("pipe? %d\n", prc->pipe); | |
prc = prc->next; | |
} | |
} | |
process* parse(char*line) { | |
int cur, s, word_cur, arg_cur; | |
char **words = calloc(sizeof(char*), strlen(line)); | |
process *prc, *cur_prc; | |
words[0] = NULL; | |
// 字句解析 | |
// wordsに単語を代入していく | |
word_cur = 0; | |
for (cur = 0; cur < strlen(line); ++cur) { | |
#if false | |
printf("| char %d - %c\n", cur, line[cur]); | |
#endif | |
if (line[cur] == '\"') { | |
s = cur + 1; | |
for (++cur; line[cur] != '\"'; ++cur) { | |
if (line[cur] == '\0') { | |
perror("double-quotation syntax error"); | |
return NULL; | |
} | |
} | |
words[word_cur] = slice(line, s, cur); | |
words[++word_cur] = NULL; | |
} | |
else if (line[cur] != ' ' && | |
line[cur] != '>' && | |
line[cur] != '<' && | |
line[cur] != '|' && | |
line[cur] != ';' && | |
line[cur] != '&') { //何も無い文字 | |
s = cur; | |
while (line[cur] != ' ' && | |
line[cur] != '>' && | |
line[cur] != '<' && | |
line[cur] != '|' && | |
line[cur] != ';' && | |
line[cur] != '&') { | |
if (line[cur] == '\0') break; | |
++cur; | |
} | |
words[word_cur] = slice(line, s, cur); | |
words[++word_cur] = NULL; | |
--cur; | |
} | |
else if (line[cur] == ' ') { | |
while(line[++cur] == ' ') { | |
if (line[cur] == '\0') break; | |
} | |
--cur; | |
} | |
else if (line[cur] == '>' && line[cur+1] == '>'){ | |
words[word_cur] = slice(line, cur, cur+2); | |
words[++word_cur] = NULL; | |
++cur; | |
} | |
else if (line[cur] == ' ' || | |
line[cur] == '>' || | |
line[cur] == '<' || | |
line[cur] == '|' || | |
line[cur] == ';' || | |
line[cur] == '&') { | |
words[word_cur] = slice(line, cur, cur+1); | |
words[++word_cur] = NULL; | |
} | |
else { | |
//unreachable | |
} | |
} | |
#if false | |
puts("words"); | |
print_words(words); | |
puts(""); | |
#endif | |
// wordsを構造体に代入してく | |
prc = calloc(sizeof(process), 1); | |
prc->args = calloc(sizeof(char*), cur); | |
prc->args[0] = NULL; | |
prc->infile = calloc(sizeof(char), strlen(line)); | |
prc->outfile = calloc(sizeof(char), strlen(line)); | |
prc->pipe = 0; | |
prc->next = NULL; | |
cur_prc = prc; | |
arg_cur = 0; | |
for (cur = 0; words[cur]; ++cur) { | |
#if false | |
printf("# prc %d - %s\n", cur, words[cur]); | |
#endif | |
if (!strcmp(words[cur], "<")) { | |
++cur; | |
strcpy(cur_prc->infile, words[cur]); | |
} | |
else if (!strcmp(words[cur], ">")) { | |
++cur; | |
strcpy(cur_prc->outfile, words[cur]); | |
cur_prc->outmode = 0; | |
} | |
else if (!strcmp(words[cur], ">>")) { | |
++cur; | |
strcpy(cur_prc->outfile, words[cur]); | |
cur_prc->outmode = 1; | |
} | |
else if (!strcmp(words[cur], ";")) { | |
if (!words[cur+1]) break; | |
cur_prc->next = calloc(sizeof(process), 1); | |
cur_prc = cur_prc->next; | |
cur_prc->args = calloc(sizeof(char*), cur); | |
cur_prc->args[0] = NULL; | |
cur_prc->infile = calloc(sizeof(char), strlen(line)); | |
cur_prc->outfile = calloc(sizeof(char), strlen(line)); | |
cur_prc->next = NULL; | |
cur_prc->pipe = 0; | |
arg_cur = 0; | |
} | |
else if (!strcmp(words[cur], "|")) { | |
cur_prc->next = calloc(sizeof(process), 1); | |
if (!words[cur+1]) break; //本来なら更に入力を受け付ける所だが今はそんな機能を豪華にしている場合ではない | |
cur_prc = cur_prc->next; | |
cur_prc->args = calloc(sizeof(char*), cur); | |
cur_prc->args[0] = NULL; | |
cur_prc->infile = calloc(sizeof(char), strlen(line)); | |
cur_prc->outfile = calloc(sizeof(char), strlen(line)); | |
cur_prc->next = NULL; | |
cur_prc->pipe = 1; | |
arg_cur = 0; | |
} | |
else { | |
cur_prc->args[arg_cur] = calloc(sizeof(char), strlen(words[cur])); | |
strcpy(cur_prc->args[arg_cur], words[cur]); | |
cur_prc->args[++arg_cur] = NULL; | |
} | |
} | |
#if false | |
puts("prc"); | |
print_prc(prc); | |
puts(""); | |
#endif | |
// words開放 | |
for (cur = 0; words[cur]; ++cur) { | |
free(words[cur]); | |
} | |
free(words); | |
return prc; | |
} | |
int exec_prcs(process *prc, char**envp) { | |
int s, i, j, | |
status, fd, | |
pd, pds[2]; | |
char path[64], *head; | |
pid_t pid; | |
// find path | |
for (i=0; ; ++i) { | |
if (envp[i][0] == 'P' && | |
envp[i][1] == 'A' && | |
envp[i][2] == 'T' && | |
envp[i][3] == 'H') break; | |
} | |
pd = 0; //パイプで直前から来るもの | |
while (prc) { | |
//find path | |
sprintf(path,"/%s", prc->args[0]); //絶対パス? | |
if (access(path, 0) != 0) { | |
for(s=0;envp[i][s]!='=';++s); | |
for (;;) { | |
for(j=s+1;envp[i][j]!=':'&&envp[i][j]!='\0';++j); | |
head = slice(envp[i], s+1, j); | |
// 相対パス? | |
sprintf(path,"%s/%s", head, prc->args[0]); | |
if (access(path, 0) == 0) break; | |
s = j; | |
if (envp[i][j] == '\0') { | |
perror(NULL); | |
return 1; | |
} | |
} | |
} | |
//printf("### execv %s\n", path); | |
// 必要なら次プロセスに投げる用のパイプ作ってからフォーク | |
pds[0] = 0; | |
if (prc->next && prc->next->pipe) { | |
if (pipe(pds) < 0) { | |
perror("pipe"); | |
return 1; | |
} | |
} | |
pid = fork(); | |
if (pid < 0) { | |
perror("fork"); | |
} | |
else if (pid > 0) { // parent | |
if (pds[0]) { | |
close(pds[1]); | |
} | |
//waitpid(pid, &status, 0); | |
if (prc->next == NULL) { //一番最後のプロセス | |
wait(&status); | |
} | |
if (status) { | |
// 非ゼロだったら報告 | |
printf("process end (status = %d)\n", status); | |
} | |
if (pds[0]) { | |
pd = pds[0]; //次回はココから読ませる | |
} | |
prc = prc->next; | |
} | |
else { // child | |
if (pds[0]) { | |
close(pds[0]); | |
} | |
if (prc->infile[0]) { | |
//printf("redirect : in = %d\n", prc->infile[0]); | |
fd = open(prc->infile, O_RDONLY); | |
if (fd < 0) { | |
perror("infile"); | |
exit(1); | |
} | |
if (dup2(fd, STDIN) < 0) { | |
perror("dup2(redirect, stdin)"); | |
exit(1); | |
} | |
} else if (prc->pipe) { | |
//printf("pipe from %d instead of STDIN", pd); | |
dup2(pd, STDIN); | |
} | |
if (prc->outfile[0]) { | |
fd = open(prc->outfile, O_WRONLY|O_CREAT| | |
(prc->outmode?O_APPEND:O_TRUNC), | |
S_IRWXU|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH); //rwxrw-rw- | |
if (fd < 0) { | |
perror("outfile"); | |
exit(1); | |
} | |
if (dup2(fd, STDOUT) < 0) { | |
perror("dup2(redirect, stdout)"); | |
exit(1); | |
} | |
} | |
else if(prc->next && prc->next->pipe) { | |
if (dup2(pds[1], STDOUT) < 0) { | |
perror("dup2(pipe, stdout)"); | |
exit(1); | |
} | |
} | |
execve(path, prc->args, envp); | |
exit(1); | |
} | |
} | |
return 0; | |
} | |
// callocしたやつ全部開放 | |
void delete(process *prc) { | |
int i; | |
if (prc == NULL) return; | |
for (i=0; prc->args[i]; ++i) { | |
free(prc->args[i]); | |
} | |
free(prc->args); | |
free(prc->infile); | |
free(prc->outfile); | |
delete(prc->next); | |
free(prc); | |
} | |
int main(int argc, char**argv, char**envp){ | |
char line[256]; | |
process *prc; | |
for (;printf("hash> ") , strcmp(gets(line), "exit");) { | |
prc = parse(line); | |
if (exec_prcs(prc, envp)){ | |
perror("exec error"); | |
} | |
delete(prc); | |
} | |
return 0; | |
} | |
// :vim set ft=c: |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment