SS Lab Manual
SS Lab Manual
SS Lab Manual
6th semester
SYSTEM SOFTWARE LABORATORY
Course Objective: Implement shell script, Unix system Programs and make use of LEX and
YACC to generate the grammar.
1 Execute various programs using Unix system calls and shell PO2,PO3,PO10
script
2 Implement programs using grammar driven tools such as LEX PO1,PO2,PO10
and YACC
3 Generate tokens and parser using regular expressions and PO1,PO2,PO3
. grammar.
The lexical analyzer reads the input source program from left to right one character at a time and
generates the sequence of tokens. Each token is a single logical cohesive unit such as identifier,
keywords, operators and punctuation marks. Then the parser to determine the syntax of the
source program can use these tokens. As the lexical analyzer scans the source program to
recognize the tokens it is also called as scanner.
Rules Section:
The translation rules of a Lex program are statements of the form:
p1 {action 1}
p2 {action 2}
p3 {action 3}
… …
where each p is a regular expression and each action is a program fragment describing what
action the lexical analyzer should take when a string pattern matches with p matches a lexeme.
How to install flex and bison in Ubuntu to run Lex and Yacc programs
YACC:
Yacc: a tool for automatically generating a parser given a grammar written in a yacc
specification (.y file)
A YACC Parser:
Definition Section:
• The definition section includes declarations of the tokens used in the grammar, the types
of values used on the parser stack, and other odds and ends.
– You don‟t have to specify the number of the token.
• It can also include a literal block, C code enclosed in %{ %}
• Declarations of grammar tokens
Eg: % token DIGIT
• Declares DIGIT to be a token
• Tokens specified in this section can be used as terminal in the second and third sections.
Rules Section:
Each rule consists of a grammar production and the associated semantic action.
• Since ASCII keyboards don‟t have a key, we use a colon between the left- and right-
hand sides of a rule, and we put a semicolon at the end of each rule.
Eg:
%%
statement: NAME „=„ expression
| expression { printf (“ = %d\n”, $1); }
;
expression: expression „+‟ NUMBER {$$=$1+$2;}
| expression „-‟ NUMBER {$$=$1-$3;}
| NUMBER {$$=$1;}
;
%%
• The symbol on the left-hand side of the first rule in the grammar is normally the start
symbol, though you can use a %start declaration in the definition section to override that.
This section can contain any C code and is copied, the verbatim, into the resulting parser.
We have provided the minimal set of functions necessary for a yacc- generated parser
using a lex-generated lexer to compile: main ( ) and yyerror( ).
The main routine keeps calling the parser until it reaches the end-of-file on yyin, the lex
input file.
The only other necessary routine is yylex( ) which is provided by our lexer.
PROGRAMS
1.a) Create an input text file in few lines which describes about your aim in life and develop
a LEX program to count the number of characters, words, spaces & no. of lines in the
input file.
%{
#include<stdio.h>
int wc=0,uc=0,sc=0,lc=0,tc=0,splc=0,dc=0,lnc=0;
%}
%%
[\n] {lnc++;wc++;}
[' '\t] {sc++;wc++;}
[A-Z] {uc++;}
[a-z] {lc++;}
[0-9] {dc++;}
. {splc++;}
%%
int main()
{
FILE *fp;
char filenm[20];
printf("\n Enter text File name:");
scanf("%s",filenm);
fp=fopen(filenm,"r");
if(fp==NULL)
printf("\nError in opening !!");
else
{
yyin=fp;
yylex();
printf("\n num of words = %d\n",wc);
printf("\n num of space count=%d\n",sc);
printf("\n num of uppercase = %d\n",uc);
printf("\n num of lowercase = %d\n",lc);
printf("\n num of spl char =%d\n",splc);
printf("\n num of digit count =%d\n",dc);
printf("\n num of line count =%d\n",lnc);
tc=uc+lc+sc+splc+dc;
printf("\n total num of chars =%d\n",tc);
}
}
%{
#include<stdio.h>
int fl=0,c=0,fla=0;
%}
%%
\/\* {if(!fl) {fl=1;c++;} else REJECT;}
\*\/ {if(fl) fl=0; else REJECT;}
\/\/ {if(!fla) {fla=1;c++;} else REJECT;}
[\n] {if(fla) fla=0; else REJECT;}
. {if(!fl&&!fla) REJECT;}
%%
int main(int argc,char *argv[])
{
if(argc!=3)
{
printf("Improper arguments.\nUsage is $./a.out <srcfilename>
<destfilename>\n");
exit(0);}
yyin=fopen(argv[1],"r");
yyout=fopen(argv[2],"w");
yylex();
printf("Number of comment statemensts is %d\n",c);
}
Commands to run and Output:
2. Develop a LEX program to recognize valid arithmetic expression in the list given below
and recognize the identifiers and operators present in the expression and print them
separately.
3.a) Create an input file which consists of C program to swap two numbers without using
temporary variables and for this file develop LEX program to recognize and count the
number of identifiers in a given input file.
%{
#include<stdio.h>
int fc=0,def=0,id=0;
char a[10000];
%}
%%
\( {fc=1;}
\) {fc=0;}
[\ \t]*int[\ ] {if(fc) def=1; else def=2;}
[\ \t]*char[\ ] {if(fc) def=1; else def=2;}
[\ \t]*float[\ ] {if(fc) def=1; else def=2;}
[\ \t]*double[\ ] {if(fc) def=1; else def=2;}
\*?[a-zA-Z]+[a-zA-Z0-9_]*;
{if(def){id++;def=0;strncat(a,yytext,yyleng);strcat(a," ");}}
\*?[a-zA-Z]+[a-zA-Z0-9_]*[,=]
{if(def){id++;strncat(a,yytext,yyleng);strcat(a," ");}}
\*?[a-zA-Z]+[a-zA-Z0-9_]*\[[0-9]*\][,=]? {if(def)
{id++;strncat(a,yytext,yyleng-1);strcat(a," ");}};{if(def) def=0;}
.|\n { ;}
%%
int main(int argc,char *argv[])
{
if(argc!=2)
{
printf("Improper Arguments specified.\n");
printf("Usage is $./a.out <filename>\n");
exit(0);
}
yyin=fopen(argv[1],"r");
yylex();
printf("Number of identifiers are %d.\nThey are : %s\n",id,a);
}
int yywrap()
{
return 1;
}
%{
#include<stdio.h>
#include<string.h>
FILE *ff,*fr;
char p[20],q[20],r[20],fname[20];
%}
%%
[a-zA-Z]+ {if(strcmp(p,yytext)==0)
fprintf(fr,q);
else
fprintf(fr,yytext);
}
\n {fprintf(fr,yytext);}
. {fprintf(fr,yytext);}
%%
int main(int argc,char *argv[])
{
strcpy(fname,argv[1]);
strcpy(p,argv[2]);
strcpy(q,argv[3]);
ff=fopen(fname,"r+");
fr=fopen("rep.txt","w+");
yyin=ff;
yylex();
return(0);
}
4.a) Create a file which lists the latest open source application useful for Computer Science
and Engineering and develop a LEX program to copy the content of this file with line
numbers to another file.
%{
#include<stdio.h>
#include<string.h>
char line[20];
int count=0,i=0;
FILE *out;
%}
%%
['\n'] {fprintf(yyout,"%d %s\n",++count,line);}
(.*) {strcpy(line,yytext);}
%%
main(int argc,char *argv[])
{
if(argc!=3)
{
printf("Improper arguments.\nUsage is $./a.out <srcfilename>
<destfilename>\n");
exit(0);
}
yyin=fopen(argv[1],"r");
yyout=fopen(argv[2],"w");
yylex();
}
b) From the list of students admission dates given below, check whether the admission
dates are in valid format [DD/MM/YYYY] by developing LEX program.
Student admission Dates
Student 1 24/05/2019
Student 2 30-02-2020
Student 3 23.05.2019
Student 4 24/6/2019
Student 5 28/04/20
%{
#include<stdio.h>
int i=0,yr=0,valid=0;
%}
%%
([0-2][0-9]|[3][0-1])\/((0(1|3|5|7|8))|(10|12))\/([1-2][0-9][0-9][0-
9]) {valid=1;}
([0-2][0-9]|30)\/((0(4|6|9))|11)\/([1-2][0-9][0-9][0-9])
{valid=1;}
([0-1][0-9]|2[0-8])\/02\/([1-2][0-9][0-9][0-9]) {valid=1;}
29\/02\/([1-2][0-9][0-9][0-9]) {while(yytext[i]!='/')
i++;i++;
while(yytext[i]!='/')i++;i++;
while(i<yyleng)
yr=(10*yr)+(yytext[i++]-'0');
if(yr%4==0||(yr%100==0&&yr%400!=0))
valid=1;}
.;
%%
int main()
{
printf("enter the date\n");
yylex();
if(valid==1)
printf("It is a valid date\n");
else
printf("It is invalid date\n");
}
Lex Part
%{
#include<stdio.h>
#include "y.tab.h"
extern int yylval;
%}
%%
[0-9]+ {yylval=yytext[0];
return NUMBER;
}
[\t] ;
\n {return 0;}
. {return yytext[0];}
%%
Yacc Part
%{
#include<stdio.h>
int k=0;
int i;
char sym[26];
FILE *fp;
%}
%token NUMBER
%left'+''-'
%left'*''/'
%nonassoc UMINUS
%%
state : exp {
printf("converted postfix expression is =>");
fp=fopen("postfix.txt","w");
for(i=0;i<k;i++)
{
fprintf(fp,"%c",sym[i]);
printf("%c",sym[i]);
}
fclose(fp);
}
;
exp:NUMBER {$$=$1;sym[k]=(char)$$;k++;}
|exp'+'exp {sym[k]='+';k++;}
|exp'-'exp {sym[k]='-';k++;}
|exp'*'exp {sym[k]='*';k++;}
|exp'/'exp {sym[k]='/';k++;}
;
%%
int yyerror(char *str)
{
printf("invalid character");
}
int main()
{
printf("enter expression=>");
yyparse();
return(0);
}
Lex Part
%{
#include "y.tab.h"
extern int yylval;
%}
%%
[0-9]+ {yylval=atoi(yytext);return NUMBER;}
[\t] ;
\n return 0;
. return yytext[0];
%%
Yacc Part
%{
#include<stdio.h>
#include<stdlib.h>
%}
%token NUMBER
%left'-''+'
%left'*''/'
%nonassoc UMINUS
%%
statement:expression{printf("value=%d\n",$$);}
;
expression:expression'+'expression {$$=$1+$3;}
|expression'-'expression {$$=$1-$3;}
|expression'*'expression {$$=$1*$3;}
|expression'/'expression
{if($3==0)
{
yyerror("\n divide by 0 error\n");
exit(0);
}
else
$$=$1/$3;
}
|'-'expression%prec UMINUS {$$=-$2;}
|'('expression')' {$$=$2;}
|NUMBER
;
%%
main()
{
printf("\n enter expression\n");
yyparse();
}
int yyerror(char *s)
{
printf("%s\n",s);
}
7 a) Write and Execute YACC program to recognize the grammar (anb, n >= 10).
Lex Part
%{
#include "y.tab.h"
%}
%%
[aA] {return A;}
[bB] {return B;}
. {return yytext[0];}
%%
Yacc Part
%{
#include<stdio.h>
#include<stdlib.h>
%}
%token A B;
%%
str:A A A A A A A A A A S
;
S:A S
|B
;
%%
int main()
{
printf("\n enter a string\n");
yyparse();
printf("\n valid string\n");
return;
}
yyerror()
{
printf("\n invalid string\n");
exit(0);
}
b) Write and Execute YACC Program to recognize the grammar (a nbmck, m,n,k>=0 and
m=n+k).
Lex Part
%{
#include "y.tab.h"
%}
%%
[aA] {return A;}
[bB] {return B;}
[cC] {return C;}
. {return yytext[0];}
%%
Yacc Part
%{
#include<stdio.h>
#include<stdlib.h>
%}
%token A B C;
%%
str:S Z
|S
|Z
;
S:A S B
|A B
Z:B Z C
|B C
;
%%
main()
{
printf("\n enter a string of the form (a^n)(b^m)(c^k) and m=n+k\n");
yyparse();
printf("\n string recognized\n");
return;
}
yyerror()
{
printf("\n string not recognized\n");
exit(0);
}
Shell:
The shell provides you with an interface to the UNIX system. It gathers input from you and
executes programs based on that input. When a program finishes executing, it displays that
program's output. A shell is an environment in which we can run our commands, programs, and
shell scripts. There are different flavors of shells, just as there are different flavors of operating
systems. Each flavor of shell has its own set of recognized commands and functions.
Shell Prompt:
The prompt, $, which is called command prompt, is issued by the shell. While the prompt is
displayed, you can type a command. The shell reads your input after you press Enter. It
determines the command you want executed by looking at the first word of your input. A word is
an unbroken set of characters. Spaces and tabs separate words.
Shell Types:
In UNIX there are two major types of shells:
1. The Bourne shell: if you are using a Bourne-type shell, the default prompt is the $ character.
2. The C shell: if you are using a C-type shell, the default prompt is the % character.
There are again various subcategories for Bourne Shell which are listed as follows:
Shell Scripts:
The basic concept of a shell script is a list of commands, which are listed in the order of
execution. A good shell script will have comments, preceded by a pound sign, #, describing the
steps. There are conditional tests, such as value A is greater than value B, loops allowing us to
go through massive amounts of data, files to read and store data, and variables to read and store
data, and the script may include functions. Shell scripts and functions are both interpreted. This
means they are not compiled. We are going to write a many scripts in the next several tutorials.
This would be a simple text file in which we would put our all the commands and several other
required constructs that tell the shell environment what to do and when to do it.
8. Write a C/C++ program which demonstrates interprocesscommunication between a
reader process and a writerprocess. Use mkfifo, open, read, write and close APIs in your
program.
Writer process
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
int fd;
char buf[1024];
/* create the FIFO (named pipe) */
char * myfifo = "/tmp/myfifo";
mkfifo(myfifo, 0666);
printf("Run Reader process to read the FIFO File\n");
fd = open(myfifo, O_WRONLY);
write(fd,"Hi", sizeof("Hi"));
/* write "Hi" to the FIFO */
close(fd);
unlink(myfifo);
return 0;
}
/* remove the FIFO */
Reader process
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#define MAX_BUF 1024
int main()
{
int fd;
/* A temp FIFO file is not created in reader */
char *myfifo = "/tmp/myfifo";
char buf[MAX_BUF];
/* open, read, and display the message from the FIFO */
fd = open(myfifo, O_RDONLY);
read(fd, buf, MAX_BUF);
printf("Writer: %s\n", buf);
close(fd);
return 0;
}
Commands to run and Output:
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
static void charatatime(char *);
int main(void)
{
pid_t pid;
if ((pid = fork()) < 0)
{
printf("fork error");
}
else if (pid == 0)
{
charatatime("output from child\n");
}
else
{
charatatime("output from parent\n");
}
return 0;
}
static void charatatime(char *str)
{
char *ptr;
int c;
setbuf(stdout, NULL); /* set unbuffered */
for (ptr = str; (c = *ptr++) != 0; )
putc(c, stdout);
}
10. Write a C/C++ POSIX compliant program to check the following limits:
(i) No. of clock ticks (ii) Max. no. of child processes (iii) Max. path length (iv) Max. no. of
Characters in a file name (v) Max. no. of open files/ process
#define _POSIX_SOURCE
#define _POSIX_C_SOURCE 199309L
#include<stdio.h>
#include<unistd.h>
int main()
{
int res;
if((res = sysconf(_SC_OPEN_MAX)) == -1)
perror("sysconf");
else
printf("OPEN_MAX:%d\n",res);
if((res = pathconf("/", _PC_NAME_MAX)) == -1)
perror("pathconf");
else
printf("max_path name:%d\n",res);
if((res = sysconf(_SC_CLK_TCK))== -1)
perror("sysconf");
else
printf("clock ticks:%d\n",res);
if((res = sysconf (_SC_CHILD_MAX)) == -1)
perror("sysconf");
else
printf("max_childs:%d\n",(res));
if((res = pathconf("/",_PC_PATH_MAX)) == -1)
perror("pathconf");
else
printf("max path name length:%d\n",res);
return 0;
}
#include<sys/wait.h>
#include<errno.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
int system(const char *cmdstring)
{
pid_t pid;
int status;
if (cmdstring == NULL)
return(1);
if ((pid = fork()) < 0)
{
status = -1;
}
else if (pid == 0)
{
/* child */
execl("/bin/sh", "sh", "-c", cmdstring, (char *)0);
_exit(127); /* execl error */
}
else
/* parent */
while (waitpid(pid, &status, 0) < 0)
{
if (errno != EINTR)
status = -1; /* error other than EINTR from waitpid() */
break;
}
return(status);
}
int main()
{
int status;
if ((status = system("date")) < 0)
printf("system() error");
if ((status = system("who")) < 0)
printf("system() error");
exit(0);
}
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>
#define INTERVAL 5
void callme(int sig_no)
{
alarm(INTERVAL);
printf("Hello!!\n");
}
int main()
{
struct sigaction action;
action.sa_handler=(void(*)(int))callme;
sigaction(SIGALRM,&action,0);
alarm(2);
sleep(5);
return 0;
}
if [ $# -eq 0 ]
then
echo "No argument"
exit
fi
echo "Num of arguments:$#"
echo "Arguments in reverse order"
for x in $*
do
y="$x $y"
done
echo $y
b) Write and Execute C program that creates a child process to read commands from the
standard input and execute them (a minimal implementation of a shell – like program).
You can assume that no arguments will be passed to the commands to be executed.
#include<stdio.h>
#include<sys/types.h>
#include<stdlib.h>
main()
{
pid_t p;
char cmd[20];
p=fork();
if(p<0)
{
printf("\n fork error");
exit(0);
}
else if(p>0)
{
printf("\n enter the command\n");
scanf("%s",cmd);
system(cmd);
}
exit(0);
}
#include<stdio.h>
#include <sys/wait.h>
#include<errno.h>
#include<stdlib.h>
int main()
{
pid_t pid;
if ((pid = fork()) < 0)
{
printf("fork error");
}
else if (pid == 0)
{ /* first child */
if ((pid = fork()) < 0)
printf("fork error");
else if (pid > 0)
exit(0);
sleep(2);
printf("second child, parent pid = %d\n", getppid()); exit(0);
}
if (waitpid(pid, NULL, 0) != pid) /* wait for first child */
printf("waitpid error");
exit(0);
}
b) Write and Execute C program to do the following: Using fork( ) create a child process.
The child process prints its own process-id and id of its parent and then exits. The parent
process waits for its child to finish (by executing the wait( )) and prints its own process-id
and the id of its child process and then exits.
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main()
{
int pid,id;
pid=fork();
if(pid==0)
{
printf("\n child process:report");
printf("\n my pid is:%d",getpid());
printf("\n my parent pid is:%d",getpid());
}
else
{
wait();
printf("\n parent process:report");
printf("\n my pid is:%d",getpid());
printf("\n my child pid is:%d",pid);
}
exit(0);
}
Commands to run and Output: