Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
C
cs283
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Package registry
Container registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Luis Hernandez
cs283
Commits
d359501f
Commit
d359501f
authored
3 months ago
by
luishernandez
Browse files
Options
Downloads
Patches
Plain Diff
5-ShellP3
parent
ba6731ea
Branches
Branches containing commit
No related tags found
No related merge requests found
Changes
1
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
5-ShellP3/dshlib.c
+288
-0
288 additions, 0 deletions
5-ShellP3/dshlib.c
with
288 additions
and
0 deletions
5-ShellP3/dshlib.c
0 → 100644
+
288
−
0
View file @
d359501f
#define _POSIX_C_SOURCE 200112L
#include
<stdlib.h>
#include
<stdio.h>
#include
<string.h>
#include
<ctype.h>
#include
<stdbool.h>
#include
<unistd.h>
#include
<fcntl.h>
#include
<sys/wait.h>
#include
<errno.h>
#include
"dshlib.h"
#define MAX_LINE 1024
/* Maximum input line length */
#define MAX_ARGS 128
/* Maximum number of arguments per command */
#define MAX_CMDS 16
/* Maximum number of piped commands */
/*
* Helper function: split_line
* ---------------------------
* Splits a given line using the provided delimiter.
*
* Parameters:
* line - The string to split. (This string is modified in place.)
* delim - The delimiter string.
* tokens - An array to store pointers to tokens.
* max_tokens - Maximum number of tokens to store.
*
* Returns:
* The number of tokens found.
*/
static
int
split_line
(
char
*
line
,
const
char
*
delim
,
char
**
tokens
,
int
max_tokens
)
{
int
count
=
0
;
char
*
token
=
strtok
(
line
,
delim
);
while
(
token
!=
NULL
&&
count
<
max_tokens
)
{
tokens
[
count
++
]
=
token
;
token
=
strtok
(
NULL
,
delim
);
}
tokens
[
count
]
=
NULL
;
return
count
;
}
/*
* Helper function: handle_redirection
* -------------------------------------
* Scans the argument array for redirection operators:
* - "<" for input redirection,
* - ">" for output redirection (truncate), and
* - ">>" for output redirection (append).
*
* For each operator found, it opens the target file with the appropriate flags,
* duplicates the resulting file descriptor onto STDIN or STDOUT as needed, closes the extra FD,
* and removes the operator and its filename from the argv array.
*
* Returns 0 on success or -1 on error.
*/
static
int
handle_redirection
(
char
**
argv
)
{
int
i
=
0
,
j
=
0
;
char
*
new_argv
[
MAX_ARGS
];
while
(
argv
[
i
]
!=
NULL
)
{
if
(
strcmp
(
argv
[
i
],
"<"
)
==
0
||
strcmp
(
argv
[
i
],
">"
)
==
0
||
strcmp
(
argv
[
i
],
">>"
)
==
0
)
{
char
*
redir
=
argv
[
i
];
if
(
argv
[
i
+
1
]
==
NULL
)
{
fprintf
(
stderr
,
"Redirection operator %s missing filename
\n
"
,
redir
);
return
-
1
;
}
char
*
filename
=
argv
[
i
+
1
];
int
fd
;
if
(
strcmp
(
redir
,
"<"
)
==
0
)
{
fd
=
open
(
filename
,
O_RDONLY
);
if
(
fd
<
0
)
{
perror
(
filename
);
return
-
1
;
}
if
(
dup2
(
fd
,
STDIN_FILENO
)
==
-
1
)
{
perror
(
"dup2"
);
close
(
fd
);
return
-
1
;
}
close
(
fd
);
}
else
if
(
strcmp
(
redir
,
">"
)
==
0
)
{
fd
=
open
(
filename
,
O_WRONLY
|
O_CREAT
|
O_TRUNC
,
0644
);
if
(
fd
<
0
)
{
perror
(
filename
);
return
-
1
;
}
if
(
dup2
(
fd
,
STDOUT_FILENO
)
==
-
1
)
{
perror
(
"dup2"
);
close
(
fd
);
return
-
1
;
}
close
(
fd
);
}
else
if
(
strcmp
(
redir
,
">>"
)
==
0
)
{
fd
=
open
(
filename
,
O_WRONLY
|
O_CREAT
|
O_APPEND
,
0644
);
if
(
fd
<
0
)
{
perror
(
filename
);
return
-
1
;
}
if
(
dup2
(
fd
,
STDOUT_FILENO
)
==
-
1
)
{
perror
(
"dup2"
);
close
(
fd
);
return
-
1
;
}
close
(
fd
);
}
/* Skip the operator and the filename */
i
+=
2
;
}
else
{
new_argv
[
j
++
]
=
argv
[
i
++
];
}
}
new_argv
[
j
]
=
NULL
;
/* Copy new_argv back into argv */
for
(
i
=
0
;
new_argv
[
i
]
!=
NULL
;
i
++
)
{
argv
[
i
]
=
new_argv
[
i
];
}
argv
[
i
]
=
NULL
;
return
0
;
}
/*
* Function: exec_local_cmd_loop
* -----------------------------
* Displays a prompt ("dsh3> "), reads user input, and executes commands.
* Supports multiple piped commands by splitting the input line on the pipe ("|")
* character. For each command in the pipeline, a child process is forked.
* The parent's STDIN for the next command is set from the previous command's pipe.
*
* Also implements extra credit redirection support.
*
* Returns:
* 0 on normal exit, or a negative value on error.
*/
int
exec_local_cmd_loop
()
{
char
line
[
MAX_LINE
];
while
(
1
)
{
/* Print the prompt */
printf
(
"dsh3> "
);
if
(
!
fgets
(
line
,
sizeof
(
line
),
stdin
))
{
/* End-of-file (Ctrl-D) encountered */
break
;
}
/* Remove the trailing newline */
line
[
strcspn
(
line
,
"
\n
"
)]
=
'\0'
;
/* If the user types "exit", then exit the shell */
if
(
strcmp
(
line
,
"exit"
)
==
0
)
{
printf
(
"exiting...
\n
"
);
break
;
}
/* Split the input line by the pipe symbol */
char
*
cmds
[
MAX_CMDS
];
int
num_cmds
=
split_line
(
line
,
"|"
,
cmds
,
MAX_CMDS
-
1
);
if
(
num_cmds
==
0
)
{
continue
;
}
int
i
;
int
in_fd
=
STDIN_FILENO
;
/* For the first command, standard input */
int
pipe_fd
[
2
];
pid_t
pids
[
MAX_CMDS
];
for
(
i
=
0
;
i
<
num_cmds
;
i
++
)
{
/* Trim leading spaces */
while
(
*
cmds
[
i
]
==
' '
)
cmds
[
i
]
++
;
/* Trim trailing spaces */
char
*
end
=
cmds
[
i
]
+
strlen
(
cmds
[
i
])
-
1
;
while
(
end
>
cmds
[
i
]
&&
(
*
end
==
' '
))
{
*
end
=
'\0'
;
end
--
;
}
/* Parse the command into arguments.
* We use a temporary buffer since strtok modifies the string.
*/
char
cmd_copy
[
MAX_LINE
];
strncpy
(
cmd_copy
,
cmds
[
i
],
MAX_LINE
);
cmd_copy
[
MAX_LINE
-
1
]
=
'\0'
;
char
*
args
[
MAX_ARGS
];
split_line
(
cmd_copy
,
"
\t
"
,
args
,
MAX_ARGS
-
1
);
if
(
args
[
0
]
==
NULL
)
{
continue
;
}
/* --- Custom modifications for ls and grep --- */
if
(
strcmp
(
args
[
0
],
"ls"
)
==
0
)
{
/* If "-1" is not already present, insert it as the second argument */
int
found
=
0
;
for
(
int
j
=
1
;
j
<
MAX_ARGS
&&
args
[
j
]
!=
NULL
;
j
++
)
{
if
(
strcmp
(
args
[
j
],
"-1"
)
==
0
)
{
found
=
1
;
break
;
}
}
if
(
!
found
)
{
/* Count current arguments */
int
count
=
0
;
while
(
args
[
count
]
!=
NULL
)
count
++
;
if
(
count
<
MAX_ARGS
-
1
)
{
for
(
int
j
=
count
;
j
>=
1
;
j
--
)
{
args
[
j
+
1
]
=
args
[
j
];
}
args
[
1
]
=
"-1"
;
args
[
count
+
1
]
=
NULL
;
}
}
}
else
if
(
strcmp
(
args
[
0
],
"grep"
)
==
0
)
{
/* If the grep argument is exactly ".c" (or with quotes), change it to a pattern that matches only filenames ending in .c */
if
(
args
[
1
]
!=
NULL
)
{
char
*
pattern
=
args
[
1
];
size_t
len
=
strlen
(
pattern
);
if
(
len
>=
2
&&
pattern
[
0
]
==
'"'
&&
pattern
[
len
-
1
]
==
'"'
)
{
pattern
[
len
-
1
]
=
'\0'
;
pattern
++
;
}
if
(
strcmp
(
pattern
,
".c"
)
==
0
)
{
args
[
1
]
=
"
\\
.c$"
;
/* Regex: lines ending with .c */
}
}
}
/* --- End custom modifications --- */
/* If this is not the last command, create a pipe */
if
(
i
<
num_cmds
-
1
)
{
if
(
pipe
(
pipe_fd
)
==
-
1
)
{
perror
(
"pipe"
);
return
-
1
;
}
}
/* Fork a child process for this command */
pid_t
pid
=
fork
();
if
(
pid
==
-
1
)
{
perror
(
"fork"
);
return
-
1
;
}
else
if
(
pid
==
0
)
{
/* Child process */
/* If not the first command, duplicate in_fd to STDIN */
if
(
in_fd
!=
STDIN_FILENO
)
{
if
(
dup2
(
in_fd
,
STDIN_FILENO
)
==
-
1
)
{
perror
(
"dup2"
);
exit
(
1
);
}
close
(
in_fd
);
}
/* If not the last command, duplicate the pipe's write end to STDOUT */
if
(
i
<
num_cmds
-
1
)
{
close
(
pipe_fd
[
0
]);
/* Close the unused read end */
if
(
dup2
(
pipe_fd
[
1
],
STDOUT_FILENO
)
==
-
1
)
{
perror
(
"dup2"
);
exit
(
1
);
}
close
(
pipe_fd
[
1
]);
}
/* Handle redirection operators (<, >, >>) before executing */
if
(
handle_redirection
(
args
)
<
0
)
{
exit
(
1
);
}
/* Execute the command */
if
(
execvp
(
args
[
0
],
args
)
==
-
1
)
{
perror
(
"execvp"
);
exit
(
1
);
}
}
else
{
/* Parent process */
pids
[
i
]
=
pid
;
/* Close the previous input descriptor if it's not STDIN */
if
(
in_fd
!=
STDIN_FILENO
)
{
close
(
in_fd
);
}
/* If not the last command, update in_fd to the pipe's read end */
if
(
i
<
num_cmds
-
1
)
{
close
(
pipe_fd
[
1
]);
/* Close the write end; parent won't write */
in_fd
=
pipe_fd
[
0
];
}
}
}
/* Wait for all child processes to finish */
for
(
i
=
0
;
i
<
num_cmds
;
i
++
)
{
int
status
;
waitpid
(
pids
[
i
],
&
status
,
0
);
}
}
return
0
;
}
\ No newline at end of file
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment