Home | Notes | Projects > My UNIX Shell
A UNIX shell program which is capable of running both the built-in commands and the external commands.
The shell is the primary means for user interaction with a Unix system. The shell is a program like any other program and has the capabilities built into the logic to implement the functionality users require.
Understand how the UNIX shell manages and manipulates commandline user inputs internally.
Understand how the built-in commands and external commands get executed.
Understand the essence of PATH
variable.
Understand how a child process is spawned and runs its own tasks.
Operating System
Ubuntu 22.04.1 LTS (Kernel version: 5.15.0-52-generic)
Compiler
GCC version 11.3.0 (Ubuntu 11.3.0-1ubuntu1~22.04)
Debugger
GDB version 12.0.90 (Ubuntu 12.0.90-0ubuntu1)
echo
- Display its argument to the console. It will assume a carriage return. If the option -n
is entered the carriage
return will not be issued.
PSI
- Change the display of the prompt. The initial prompt will be the $
.
cat
- Display the contents of a file.
cp
- Copy the contents of a file.
rm
- Delete the file.
mkdir
- Create a directory.
rmdir
- Delete the directory.
exit
- Exit the shell program.
User-defined command
For other external commands, search PATH
.
If an incorrect command is entered a “Usage” message will be displayed and the prompt will be redisplayed.
x1/******************************************************************************
2 * File Name : myshell.c
3 * Author : Kyungjae Lee
4 * Description : A UNIX shell program which is capable of running built-in
5 commands and external commands.
6 * Date Created : 02/20/2022
7 * Last Updated : 03/13/2022
8 *****************************************************************************/
9
10#include <stdio.h>
11#include <string.h> /* strcmp, strtok */
12#include <unistd.h>
13#include <errno.h> /* EEXIST, ENOENT */
14#include <dirent.h> /* DIR, opendir */
15#include <linux/limits.h> /* PATH_MAX */
16#include <stdlib.h> /* getenv */
17#include <unistd.h> /* fork, execv */
18#include <sys/wait.h> /* wait */
19#include <sys/stat.h> /* mkdir */
20
21#define MAX_LEN 1024
22#define MAX_ARGS 64
23#define DELIM " \t\n"
24
25/* function prototypes */
26void read_cmdline(char *p_line);
27void execute_cmdline(char *p_line, int *p_argc, char *p_argv[]);
28void parse_cmdline(char *p_line, int *p_argc, char *p_argv[]);
29void my_echo(char *p_line, int *p_argc, char *p_argv[]);
30void my_PS1(char *p_line);
31void my_cat(char *p_line, int *p_argc, char *p_argv[]);
32void my_cp(char *p_line, int *p_argc, char *p_argv[]);
33void my_mkdir(char *p_line, int *p_argc, char *p_argv[]);
34void my_rmdir(char *p_line, int *p_argc, char *p_argv[]);
35void my_rm(char *p_line, int *p_argc, char *p_argv[]);
36int is_file(char *name);
37void ext_cmd(char *p_line, int *p_argc, char *p_argv[]);
38void fork_exec_wait(char *p_buf, char *p_argv[]);
39char* search_path(char *p_cmd, char *p_buf, int size);
40char* search_cwd(char *p_cmd, char *p_buf, int size);
41void quit(char *message, int exit_status);
42void arg_check(int args, int argc, char *message, int exit_status);
43
44/*
45 * Variable Naming Conventions
46 * - prefix 'g_': global variables
47 * - prefix 'l_': local variables
48 * - prefix 'p_': function parameters
49 */
50
51/* global variables */
52extern int errno;
53int g_exitflag = 0;
54char g_prompt[MAX_LEN] = "$ ";
55
56
57int main(int argc, char *argv[])
58{
59 /* local variables */
60 int l_argc = 0;
61 char *l_argv[MAX_ARGS] = {0};
62 char l_cmdline[MAX_LEN] = {0};
63
64 /* mysh.c main loop */
65 while (1)
66 {
67 /* print the command prompt */
68 printf("%s", g_prompt);
69
70 /* read the command line */
71 read_cmdline(l_cmdline);
72
73 /* execute the command line */
74 execute_cmdline(l_cmdline, &l_argc, l_argv);
75
76 /* exit the program if user enters 'exit' command */
77 if (g_exitflag)
78 break;
79 }
80
81 return 0;
82} /* end main */
83
84
85/*
86 * Function : read_command
87 * Parameters : char *p_line
88 * Return type : void
89 * Description : Reads in and stores the command line into the passed
90 * character array, returns none.
91 */
92void read_cmdline(char *p_line)
93{
94 /* local variables */
95 int i = 0;
96
97 /* read in the command line from stdin */
98 fgets(p_line, MAX_LEN, stdin);
99
100 /* skip till the terminating '\n' character */
101 for (i = 0; p_line[i] != '\n'; ++i)
102 {
103 /* intentionally left blank */
104 }
105
106 /* replace the terminating '\n' character with '\0' character */
107 p_line[i] = '\0';
108
109 return;
110} /* end read_cmdline */
111
112
113/*
114 * Function : parse_cmdline
115 * Parameters : char *p_line, int *p_argc, char *p_argv[]
116 * Return type : void
117 * Description : Parses and tokenizes the command line.
118 */
119void parse_cmdline(char *p_line, int *p_argc, char *p_argv[])
120{
121 /* local variables */
122 int i = 0;
123 char *l_token;
124
125 /* tokenize the command line */
126 l_token = strtok(p_line, DELIM);
127
128 /* repeat storing tokenized command line pieces into 'p_argv' */
129 for (i = 0; l_token != NULL; ++i)
130 {
131 if (i < (MAX_ARGS - 1))
132 {
133 p_argv[i] = l_token;
134 l_token = strtok(NULL, DELIM);
135 }
136 }
137
138 /* update 'p_argc' */
139 *p_argc = i;
140
141 /* mark the end of 'p_argv' */
142 p_argv[i] = 0;
143
144 return;
145} /* end parse_cmdline */
146
147
148/*
149 * Function : execute_cmdline
150 * Parameters : char *p_line, int *p_argc, char *p_argv[]
151 * Return type : void
152 * Description : Executes command line. (Identifies the command from the
153 * read-in command line and calls the corresponding handler.
154 * Alerts the user if the command was not found.
155 */
156void execute_cmdline(char *p_line, int *p_argc, char *p_argv[])
157{
158 /* local variables */
159 char l_line[MAX_LEN] = {0};
160 char *l_token;
161
162 /* make a copy of the read-in command line */
163 strcpy(l_line, p_line);
164
165 /* tokenize the command part only */
166 l_token = strtok(l_line, DELIM);
167
168 /* 'echo' command handler */
169 if (!strcmp(l_token, "echo"))
170 {
171 my_echo(p_line, p_argc, p_argv);
172 }
173 /* 'PS1' command handler */
174 else if (!strncmp(p_line, "PS1=", 4))
175 {
176 my_PS1(p_line);
177 }
178 /* 'cat' command handler */
179 else if (!strcmp(l_token, "cat"))
180 {
181 my_cat(p_line, p_argc, p_argv);
182 }
183 /* 'cp' command handler */
184 else if (!strcmp(l_token, "cp"))
185 {
186 my_cp(p_line, p_argc, p_argv);
187 }
188 /* 'rm' command handler */
189 else if (!strcmp(l_token, "rm"))
190 {
191 my_rm(p_line, p_argc, p_argv);
192 }
193 /* 'mkdir' command handler */
194 else if (!strcmp(l_token, "mkdir"))
195 {
196 my_mkdir(p_line, p_argc, p_argv);
197 }
198 /* 'rmdir' command handler */
199 else if (!strcmp(l_token, "rmdir"))
200 {
201 my_rmdir(p_line, p_argc, p_argv);
202 }
203 /* 'exit' command handler */
204 else if (!strcmp(l_token, "exit"))
205 {
206 g_exitflag = 1;
207 }
208 /* if command does not match any of the above built-in commands */
209 else
210 {
211 /* call the external command handler */
212 ext_cmd(p_line, p_argc, p_argv);
213 }
214
215 return;
216} /* end execute_cmdline */
217
218
219/*
220 * Function : ext_cmd
221 * Parameters : char *p_line, int *p_argc, char *p_argv[]
222 * Return type : void
223 * Description : Handles the potential external commands, informs the user if
224 * the entered command was not found.
225 */
226void ext_cmd(char *p_line, int *p_argc, char *p_argv[])
227{
228 char l_buffer[PATH_MAX]; /* to store the path of the command if found */
229 int l_is_file = 0;
230
231 /* parse the command line */
232 parse_cmdline(p_line, p_argc, p_argv);
233
234 /* if an abolute path was entered */
235 if (*p_argv[0] == '/')
236 {
237 l_is_file = is_file(p_argv[0]);
238
239 /* if the path represents a file */
240 if (l_is_file == 1)
241 {
242 /* run the command */
243 fork_exec_wait(p_argv[0], p_argv);
244 }
245 /* if the path represents a directory */
246 else if (l_is_file == 0)
247 {
248 printf("%s: is a directory\n", p_argv[0]);
249 return;
250 }
251 /* if the path was not found */
252 else if (l_is_file == 2)
253 {
254 printf("%s: No such file or directory\n", p_argv[0]);
255 return;
256 }
257 }
258 /* search the PATH */
259 else if (search_path(p_argv[0], l_buffer, PATH_MAX))
260 {
261 /* run the command */
262 fork_exec_wait(l_buffer, p_argv);
263 }
264 /* search relative to the current working directory */
265 else if (search_cwd(p_argv[0], l_buffer, PATH_MAX))
266 {
267 l_is_file = is_file(l_buffer);
268
269 /* if the path represents a file */
270 if (l_is_file == 1)
271 {
272 /* run the command */
273 fork_exec_wait(l_buffer, p_argv);
274 }
275 /* if the path represents a directory */
276 else if (l_is_file == 0)
277 {
278 printf("%s: is a directory\n", p_argv[0]);
279 return;
280 }
281 /* if the path was not found */
282 else if (l_is_file == 2)
283 {
284 printf("%s: No such file or directory\n", p_argv[0]);
285 return;
286 }
287 }
288 else
289 {
290 printf("Command '%s' not found.\n", p_argv[0]);
291 }
292} /* end exit_cmd */
293
294
295/*
296 * Function : fork_exec_wait
297 * Parameters : char *p_buf, char *p_argv[]
298 * Return type : void
299 * Description : forks a child process to run the passed commnd. Child execs a
300 * new program, parent waits for the child process to die.
301 */
302void fork_exec_wait(char *p_buf, char *p_argv[])
303{
304 int l_status;
305 int pid;
306
307 /* fork error */
308 if ((pid = fork()) < 0)
309 {
310 quit("fork", 1);
311 }
312 /* child */
313 else if (pid == 0)
314 {
315 if ((execv(p_buf, &p_argv[0]) < 0))
316 {
317 exit(200);
318 }
319 }
320 /* parent */
321 else
322 {
323 wait(&l_status);
324 /*
325 fprintf(stderr, "Exit status: %d\n", WEXITSTATUS(l_status));
326 */
327 }
328} /* end fork_exec_wait */
329
330
331/*
332 * Function : search_path
333 * Parameters : char *p_cmd, char *p_buf, int size
334 * Return type : char*
335 * Description : Searches the command in the PATH, copy the path to the
336 * command to the buffer if found, returns the pointer to the
337 * buffer, NULL otherwise.
338 */
339char* search_path(char *p_cmd, char *p_buf, int size)
340{
341 char l_path[PATH_MAX] = {0};
342 char l_pathtok[PATH_MAX] = {0};
343 char *l_p;
344
345 if ((l_p = getenv("PATH")) == NULL)
346 {
347 /* environment variable 'PATH' not found */
348 printf("Environment variable 'PATH' not found.\n");
349 return NULL;
350 }
351
352 strcpy(l_path, l_p);
353
354 /* PATH search algorithm */
355 l_p = strtok(l_path, ":");
356
357 while (l_p)
358 {
359 strcpy(l_pathtok, l_p);
360 strcat(l_pathtok, "/");
361 strcat(l_pathtok, p_cmd);
362
363 if (access(l_pathtok, X_OK) == 0)
364 {
365 strcpy(p_buf, l_pathtok);
366 return p_buf;
367 }
368
369 l_p = strtok(NULL, ":");
370 }
371
372 /* command not found in the PATH variable */
373 return NULL;
374} /* end search_path */
375
376
377/*
378 * Function : search_cwd
379 * Parameters : char *p_cmd, char *p_buf, int size
380 * Return type : char*
381 * Description : Searches the command relative to the current working directory,
382 * returns the pointer to the where the found command's absolute
383 * path stored, NULL otherwise.
384 */
385char* search_cwd(char *p_cmd, char *p_buf, int size)
386{
387 char *l_p;
388
389 if ((getcwd(p_buf, PATH_MAX)) == NULL)
390 {
391 /* getcwd not successful */
392 return NULL;
393 }
394
395 strcat(p_buf, "/");
396 strcat(p_buf, p_cmd);
397
398 if (access(p_buf, X_OK) == 0)
399 {
400 return p_buf;
401 }
402 else
403 {
404 /* Command not found in the current working directory */
405 return NULL;
406 }
407} /* end search_cwd */
408
409
410/*
411 * Function : my_echo
412 * Parameters : char *p_line, int *p_argc, char *p_argv[]
413 * Return type : void
414 * Description : Displays its argument to the console.
415 */
416void my_echo(char *p_line, int *p_argc, char *p_argv[])
417{
418 /* local variables */
419 int i = 0;
420 char l_line[MAX_LEN] = {0};
421 char *l_token;
422
423 /* make a copy of read-in command line */
424 strcpy(l_line, p_line);
425
426 /* if argument is a string wrapped in a pair of double quotes */
427 if (strstr(l_line, "\"") != NULL)
428 {
429 /* tokenize the command line to check if option '-n' is entered */
430 l_token = strtok(l_line, " ");
431 l_token = strtok(NULL, " ");
432
433 /* if option '-n' is found */
434 if (!strcmp(l_token, "-n"))
435 {
436 /* make a copy of read-in command line */
437 strcpy(l_line, p_line);
438
439 /* tokenize command line to retrive double quote wrapped string */
440 l_token = strtok(l_line, "\"");
441 l_token = strtok(NULL, "\"");
442
443 /* print the token without '\n' at the end */
444 printf("%s", l_token);
445 }
446 /* if option '-n' is not found */
447 else
448 {
449 /* make a copy of read-in command line */
450 strcpy(l_line, p_line);
451
452 /* tokenize command line to retrive double quote wrapped string */
453 l_token = strtok(l_line, "\"");
454 l_token = strtok(NULL, "\"");
455
456 /* print the token with '\n' at the end */
457 printf("%s\n", l_token);
458 }
459 }
460 /* if argument is a string not wrapped in a pair of double quotes */
461 else
462 {
463 /* parse the command line */
464 parse_cmdline(p_line, p_argc, p_argv);
465
466 /* if argument is missing */
467 if (p_argv[1] == NULL)
468 {
469 puts("");
470 return;
471 }
472 /* when option '-n' is entered */
473 else if (!strcmp(p_argv[1], "-n"))
474 {
475 /* print the tokens without '\n' at the end */
476 for (i = 2; p_argv[i] != NULL; ++i)
477 {
478 printf("%s ", p_argv[i]);
479 }
480 }
481 /* when no option was entered */
482 else
483 {
484 /* print the tokens with 'new line' at the end */
485 for (i = 1; p_argv[i] != NULL; ++i)
486 {
487 printf("%s ", p_argv[i]);
488 }
489 puts("");
490 }
491 }
492
493 return;
494} /* end my_echo */
495
496
497/*
498 * Function : my_PS1
499 * Parameters : char *p_line
500 * Return type : void
501 * Description : Changes the display of the prompt.
502 */
503void my_PS1(char *p_line)
504{
505 /* local variables */
506 char l_line[MAX_LEN] = {0};
507 char *l_token;
508
509 /* make a copy of read-in command line */
510 strcpy(l_line, p_line);
511
512 /* if argument is a string wrapped in a pair of double quotes */
513 if (strstr(l_line, "\"") != NULL)
514 {
515 l_token = strtok(l_line, "\"");
516 l_token = strtok(NULL, "\"");
517 }
518 /* if argument is a string not wrapped in a pair of double quotes */
519 else
520 {
521 /* make a copy of read-in command line */
522 strcpy(l_line, p_line);
523
524 /* read in and discard 'PS1=' part */
525 l_token = strtok(l_line, " =\n");
526
527 /* if nothing follows 'PS1=' */
528 if ((l_token = strtok(NULL, " =\n")) == NULL)
529 {
530 /* Ubuntu linux allows an 'empty string' to be a prompt */
531 l_token = "";
532 } /* else, 'l_token' will contain a non-empty string that follows
533 'PS1=' that is not wrapped in a pair of double quotes */
534 }
535
536 /* set the new prompt to 'l_token' */
537 strcpy(g_prompt, l_token);
538
539 return;
540} /* end my_PS1 */
541
542
543/*
544 * Function : my_cat
545 * Parameters : char *p_line
546 * Return type : void
547 * Description : Displays the contents of a file.
548 */
549void my_cat(char *p_line, int *p_argc, char *p_argv[])
550{
551 /* local variables */
552 int i = 0;
553 FILE *l_fp;
554 char l_ch;
555
556 /* parse the command line */
557 parse_cmdline(p_line, p_argc, p_argv);
558
559 /* if argument is missing */
560 if (*p_argc == 1)
561 {
562 /* original 'cat' command, when there's no argument, echoes whatever
563 the user enters to the screen and repeats it until 'Ctrl-D' is keyed
564 in, but this program will simply output the error message and return
565 since the requirement doesn't specify this functionality */
566 printf("%s: missing file operand\n", p_argv[0]);
567
568 return;
569 }
570
571 /* display the contents of file */
572 for (i = 1; p_argv[i] != NULL; ++i)
573 {
574 /* open the file */
575 l_fp = fopen(p_argv[i], "r");
576
577 /* if file does not exist */
578 if (l_fp == NULL)
579 {
580 printf("%s: %s: No such file or directory\n", p_argv[0], p_argv[i]);
581
582 continue;
583 }
584
585 /* print the file contens to the screen */
586 l_ch = fgetc(l_fp);
587 while (l_ch != EOF)
588 {
589 printf("%c", l_ch);
590 l_ch = fgetc(l_fp);
591 }
592
593 /* close the file */
594 fclose(l_fp);
595 }
596
597
598 return;
599} /* end my_cat */
600
601
602/*
603 * Function : my_cp
604 * Parameters : char *p_line, int *p_argc, char *p_argv[]
605 * Return type : void
606 * Description : Copies the contents of a file.
607 */
608void my_cp(char *p_line, int *p_argc, char *p_argv[])
609{
610 /* local variables */
611 FILE *l_sfp; /* source file pointer */
612 FILE *l_dfp; /* destination file pointer */
613 char *l_sfilename; /* source file name */
614 char *l_dfilename; /* destination file name */
615 char l_ch;
616
617 /* parse the command line */
618 parse_cmdline(p_line, p_argc, p_argv);
619
620 /* if arguments are missing */
621 if (*p_argc == 1)
622 {
623 printf("%s: missing file operand\n", p_argv[0]);
624 return;
625 }
626 /* if only one argument was entered */
627 else if (*p_argc == 2)
628 {
629 printf("%s: missing destination file operand after '%s'\n",
630 p_argv[0], p_argv[1]);
631 return;
632 }
633
634 /* set source & destination file names */
635 l_sfilename = p_argv[1];
636 l_dfilename = p_argv[2];
637
638 /* open the source file */
639 l_sfp = fopen(l_sfilename, "r");
640
641 /* if source file does not exist */
642 if (l_sfp == NULL)
643 {
644 printf("%s: cannot stat '%s': No such file or directory\n",
645 p_argv[0], p_argv[1]);
646 return;
647 }
648
649 /* open the destination file */
650 l_dfp = fopen(l_dfilename, "w");
651
652 /* if the destination fail to open */
653 if (l_dfp == NULL)
654 {
655 printf("%s: unable to open destination file '%s'\n",
656 p_argv[0], p_argv[2]);
657 return;
658 }
659
660 /* copy the source file contents to the destination file */
661 l_ch = fgetc(l_sfp);
662 while (l_ch != EOF)
663 {
664 fputc(l_ch, l_dfp);
665 l_ch = fgetc(l_sfp);
666 }
667
668 /* close the files */
669 fclose(l_sfp);
670 fclose(l_dfp);
671
672 return;
673} /* end my_cp */
674
675
676/*
677 * Function : my_mkdir
678 * Parameters : char *p_line, int *p_argc, char *p_argv[]
679 * Return type : void
680 * Description : Creates a directory
681 */
682void my_mkdir(char *p_line, int *p_argc, char *p_argv[])
683{
684 /* local variables */
685 int i = 0;
686 char *l_path;
687 int l_ret = 0;
688
689 /* parse the command line */
690 parse_cmdline(p_line, p_argc, p_argv);
691
692 /* if arguments are missing */
693 if (*p_argc == 1)
694 {
695 printf("%s: missing operand\n", p_argv[0]);
696 return;
697 }
698
699 /* create directory */
700 for (i = 1; p_argv[i] != NULL; ++i)
701 {
702 l_path = p_argv[i];
703
704 l_ret = mkdir(l_path, 0775); /* default permission: 0775 */
705
706 /* if successful */
707 if (!l_ret)
708 {
709 /* report nothing */
710 }
711 /* if not successful */
712 else
713 {
714 /* if directory already exists */
715 if (errno == EEXIST)
716 {
717 printf("%s: cannot create directory '%s': File exists\n",
718 p_argv[0], p_argv[i]);
719 }
720 }
721
722 /* clear error flags */
723 errno = 0;
724 }
725
726 return;
727} /* end my_mkdir */
728
729
730/*
731 * Function : my_rmdir
732 * Parameters : char *p_line, int *p_argc, char *p_argv[]
733 * Return type : void
734 * Description : Removes a directory.
735 */
736void my_rmdir(char *p_line, int *p_argc, char *p_argv[])
737{
738 /* local variables */
739 int i = 0;
740 char *l_path;
741 int l_ret = 0;
742
743 /* parse the command line */
744 parse_cmdline(p_line, p_argc, p_argv);
745
746 /* if argument is missing */
747 if (*p_argc == 1)
748 {
749 printf("%s: missing operand\n", p_argv[0]);
750 return;
751 }
752
753 /* remove directory */
754 for (i = 1; p_argv[i] != NULL; ++i)
755 {
756 l_path = p_argv[i];
757
758 l_ret = rmdir(l_path);
759
760 /* if successful */
761 if (!l_ret)
762 {
763 /* report nothing */
764 }
765 /* if not successful */
766 else
767 {
768 /* if directory is not found */
769 if (errno == ENOENT)
770 {
771 printf("%s: failed to remove '%s': No such file or directory\n",
772 p_argv[0], p_argv[i]);
773 }
774 /* if it is not a directory */
775 else if (errno == ENOTDIR)
776 {
777 printf("%s: failed to remove '%s': Not a directory\n",
778 p_argv[0], p_argv[i]);
779 }
780 /* if directory is not empty */
781 else if (errno == ENOTEMPTY)
782 {
783 printf("%s: failed to remove '%s': Directory not empty\n",
784 p_argv[0], p_argv[i]);
785 }
786 }
787
788 /* clear error flags */
789 errno = 0;
790 }
791
792 return;
793} /* end my_rmdir */
794
795
796/*
797 * Function : my_rm
798 * Parameters : char *p_line, int *p_argc, char *p_argv[]
799 * Return type : void
800 * Description : Deletes a file.
801 */
802void my_rm(char *p_line, int *p_argc, char *p_argv[])
803{
804 /* local variables */
805 int i = 0;
806 char *l_path;
807 int l_ret = 0;
808
809 /* parse the command line */
810 parse_cmdline(p_line, p_argc, p_argv);
811
812 /* if argument is missing */
813 if (*p_argc == 1)
814 {
815 printf("%s: missing operand\n", p_argv[0]);
816 return;
817 }
818
819 /* loop through the arguments, remove files only */
820 for (i = 1; p_argv[i] != NULL; ++i)
821 {
822 l_path = p_argv[i];
823
824 /* if directory, do not remove and report the user */
825 if (is_file(l_path) == 0)
826 {
827 printf("%s: cannot remove '%s': Is a directory\n",
828 p_argv[0], l_path);
829 }
830 /* if file not found */
831 else if (is_file(l_path) == 2)
832 {
833 printf("%s: cannot remove '%s': No such file or directory\n",
834 p_argv[0], p_argv[i]);
835 }
836 /* if file, remove */
837 else if (is_file(l_path) == 1)
838 {
839 l_ret = remove(l_path);
840
841 /* if successful */
842 if (!l_ret)
843 {
844 /* report nothing */
845 }
846 else
847 {
848 /* 'errno' handling goes here (left blank since not required by
849 the requirement) */
850 }
851
852 /* clear error flags */
853 errno = 0;
854 }
855 }
856
857 return;
858} /* end my_rm */
859
860
861/*
862 * Function : is_file
863 * Parameters : char *name
864 * Return type : int
865 * Description : Determines if the entity with the passed name is a 'directory
866 * file' or an 'ordinary file' or does not exist and returns a
867 * corresponding integer value.
868 */
869int is_file(char *name)
870{
871 /* local variables */
872 DIR *l_dir = opendir(name);
873
874 /* if directory */
875 if (l_dir != NULL)
876 {
877 closedir(l_dir);
878 return 0;
879 }
880
881 /* if not directory */
882 if (errno == ENOTDIR)
883 {
884 return 1;
885 }
886
887 /* if not exist */
888 if (errno == ENOENT)
889 {
890 return 2;
891 }
892
893 return -1;
894} /* end is_file */
895
896
897/*
898 * Function : quit
899 * Parameters : char *message, int exit_status
900 * Return type : void
901 * Description : Prints a passed message and terminates the program.
902*/
903void quit(char *message, int exit_status)
904{
905 fprintf(stderr, message);
906 exit(exit_status);
907} /* end quit */
xxxxxxxxxx
1251/******************************************************************************
2 * File Name : myls.c
3 * Author : Kyungjae Lee
4 * Description : C program to read a directory and display the contents.
5 * Date Created : 02/20/2022
6 * Last Updated : 03/13/2022
7 *****************************************************************************/
8
9/* for DIR and struct dirent */
10
11
12
13
14/* function prototypes */
15void quit(char *message, int exit_status);
16
17
18int main(int argc, char **argv)
19{
20 DIR *dir; /* returned by opendir */
21 struct dirent *direntry; /* returned by readdir */
22 int i = 0;
23
24 /* if argument does not exist */
25 if (argc == 1)
26 {
27 argv[1] = "."; /* read current directory by default */
28 argv[2] = NULL; /* specify termination point */
29
30 /* check if directory open fails */
31 if ((dir = opendir(argv[1])) == NULL)
32 {
33 quit("opendir", 1);
34 }
35
36 /* until entries are exhauseted */
37 while ((direntry = readdir(dir)) != NULL)
38 {
39 /* ignore hidden files */
40 if (*(direntry->d_name) != '.')
41 {
42 printf("%s ", direntry->d_name);
43 }
44 }
45
46 closedir(dir);
47
48 puts("");
49 }
50 /* if arguments exist */
51 else if (!strcmp(argv[1], "-a"))
52 {
53 if (argc == 2)
54 {
55 argv[2] = "."; /* read current directory by default */
56 argv[3] = NULL; /* specify termination point */
57 }
58
59 i = 2;
60
61 while (argv[i])
62 {
63 /* check if directory open fails */
64 if ((dir = opendir(argv[i])) == NULL)
65 {
66 quit("opendir", 1);
67 }
68
69 /* until entries are exhauseted */
70 while ((direntry = readdir(dir)) != NULL)
71 {
72 printf("%s ", direntry->d_name);
73 }
74
75 closedir(dir);
76 i++;
77
78 puts("");
79 }
80 }
81 /* when used without option '-a' */
82 else
83 {
84 i = 1;
85
86 while (argv[i])
87 {
88 /* check if directory open fails */
89 if ((dir = opendir(argv[i])) == NULL)
90 {
91 quit("opendir", 1);
92 }
93
94 /* until entries are exhauseted */
95 while ((direntry = readdir(dir)) != NULL)
96 {
97 /* ignore hidden files */
98 if (*(direntry->d_name) != '.')
99 {
100 printf("%s ", direntry->d_name);
101 }
102 }
103
104 closedir(dir);
105 i++;
106
107 puts("");
108 }
109 }
110
111 return 0;
112} /* end main */
113
114
115/*
116 * Function : quit
117 * Parameters : char *message, int exit_status
118 * Return type : void
119 * Description : Prints a passed message and terminates the program.
120*/
121void quit(char *message, int exit_status)
122{
123 fprintf(stderr, message);
124 exit(exit_status);
125} /* end quit */