(1)当输入一条指令后,将指令拆分成独立的符号;
(2)如果指令的最后一个符号是’&’,则(4)子进程执行指令的时候,父进程将不会wait()子进程执行完毕,继续接收下一条指令进行解释;
(3)History指令由主程序使用循环队列进行维护,最多只记录10条历史指令;'!!'执行最近执行过的指令,'! + 数字'执行对应的历史指令;
(4)如果是非history指令,fork()一个子进程,调用execvp()进行解释执行;
一、主程序
/*
** FILE: osh.c
** NOTE: 2016-01-11 created by Jack Liu
** DESC:
*/
#include<sys/types.h>
#include<sys/wait.h>
#include<unistd.h>
#include<signal.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include"history.h"
#define MAX_HISTORY 10
/* how many stokens */
int stoken_cmd_num( char *cpCmd );
/* split the command into stokens */
char **stoken_cmd( char *cpCmd, int *iTokenNum );
/* remember the command */
int do_history( char *cpCmd, PHISTORY_LIST pHistory );
/* when children terminate, recycle it */
void child_fun( int sig );
int
main( void )
{
char caCmd[ MAX_CMD ];
char **cppCmdStokens;
pid_t pid;
int iTokenNum;
int iConcurrent;
struct sigaction newSigAct, oldSigAct;
HISTORY_LIST history;
newSigAct.sa_handler = child_fun;
if( sigaction( SIGCHLD, &newSigAct, &oldSigAct ) < 0 )
{
perror( "sigaction" );
return -1;
}
if( history_init( &history, MAX_HISTORY ) < 0 )
{
fprintf( stderr, "fail to init history list\n" );
return -1;
}
while( 1 )
{
int i = 0;
iConcurrent = 0;
printf( "osh>" );
/*
** get the input command
*/
if( fgets( caCmd, MAX_CMD, stdin ) == NULL )
{ /* the user want to exit, ctrl + D */
putchar( '\n' );
break;
}
if( caCmd[ strlen( caCmd ) - 1 ] == '\n' )
caCmd[ strlen( caCmd ) - 1 ] = '\0';
if( caCmd[ 0 ] == '\0' ) /* just input enter, ignore it */
continue;
/*
** record the command
*/
if( do_history( caCmd, &history ) < 0 )
continue;
/*
** split the command
*/
if( strcmp( caCmd, "exit" ) == 0 )
{ /* the user want to exit, input exit */
break;
}
cppCmdStokens = stoken_cmd( caCmd, &iTokenNum );
if( cppCmdStokens == NULL )
{
fprintf( stderr, "couldn't recognize: %s\n", caCmd );
continue;
}
/*
** fork a child to exec the command
** or
** print the history commands
*/
if( cppCmdStokens[ iTokenNum - 2 ][ strlen( cppCmdStokens[ iTokenNum - 2 ] ) - 1 ] == '&' )
{
iConcurrent = 1; /* concurrent the command */
/*
** clear the '&' character
*/
cppCmdStokens[ iTokenNum - 2 ][ strlen( cppCmdStokens[ iTokenNum - 2 ] ) - 1 ] = '\0';
if( cppCmdStokens[ iTokenNum - 2 ][ 0 ] == '\0' )
{ /* '&' occupy a stoken, clear the stoken */
free( cppCmdStokens[ iTokenNum - 2 ] );
cppCmdStokens[ iTokenNum - 2 ] = NULL;
}
}
if( strcmp( cppCmdStokens[ 0 ], "history" ) == 0 )
{
history_print( &history );
}
else
{
if( ( pid = fork() ) < 0 )
{
fprintf( stderr, "fail to exec: %s\n", caCmd );
continue;
}
else if( pid == 0 )
{ /* child */
execvp( cppCmdStokens[ 0 ], cppCmdStokens );
fprintf( stderr, "fail to exec: %s\n", caCmd );
exit( -1 );
}
}
i = 0;
/*
** clear stokens
*/
while( cppCmdStokens[ i ] )
free( cppCmdStokens[ i++ ] );
free( cppCmdStokens );
if( !iConcurrent ) /* wait the child to exec */
waitpid( pid, NULL, 0 );
}
history_clear( &history );
return 0;
}
int
stoken_cmd_num( char *cpCmd )
{
char caTmpCmd[ MAX_CMD ];
char *cpIndex;
int iNum = 0;
if( cpCmd == NULL )
{
fprintf( stderr, "Illegal cmd\n" );
return -1;
}
strncpy( caTmpCmd, cpCmd, MAX_CMD );
cpIndex = strtok( caTmpCmd, " " );
while( cpIndex )
{
iNum++;
cpIndex = strtok( NULL, " " );
}
return iNum;
}
char **
stoken_cmd( char *cpCmd, int *iTokenNum )
{
char caTmpCmd[ MAX_CMD ];
char *cpIndex;
char *cpStoken;
char **cppStokens;
int iNum;
if( cpCmd == NULL )
{
fprintf( stderr, "Illegal command line\n" );
return NULL;
}
strncpy( caTmpCmd, cpCmd, MAX_CMD );
if( ( iNum = stoken_cmd_num( cpCmd ) ) > 0 )
{
/*
** the last element is NULL
*/
*iTokenNum = iNum + 1;
cppStokens = malloc( sizeof( char * ) * ( iNum + 1 ) );
if( cppStokens == NULL )
{
perror( "malloc" );
return NULL;
}
}
else
{
fprintf( stderr, "Illegal command line\n" );
return NULL;
}
memset( cppStokens, 0x00, sizeof( char * ) * ( iNum + 1 ) );
/*
** begin to split
*/
iNum = 0;
cpIndex = strtok( caTmpCmd, " " );
while( cpIndex )
{
if( ( cpStoken = malloc( strlen( cpIndex ) + 1 ) ) == NULL )
{
int i;
for( i = 0; i < iNum; i++ )
free( cppStokens[ i ] );
free( cppStokens );
perror( "malloc" );
return NULL;
}
memset( cpStoken, 0x00, strlen( cpIndex ) + 1 );
strcpy( cpStoken, cpIndex );
cppStokens[ iNum++ ] = cpStoken;
cpIndex = strtok( NULL, " " );
}
cppStokens[ iNum ] = NULL;
return cppStokens;
}
int
do_history( char *cpCmd, PHISTORY_LIST pHistory )
{
PHISTORY_RECORD pRecord;
int iIndex = -1;
char caTmpCmd[ MAX_CMD ] = { 0 };
if( cpCmd[ 0 ] == '!' )
{
memcpy( caTmpCmd, cpCmd + 1, strlen( cpCmd ) ); /* include the NULL-terminated */
if( strcmp( caTmpCmd, "!" ) != 0 )
iIndex = atoi( caTmpCmd );
else
iIndex = 1;
if( iIndex <= 0 || iIndex > pHistory->iLen )
{
fprintf( stderr, "no such history command\n" );
return -1;
}
pRecord = history_index( pHistory, iIndex );
if( pRecord == NULL )
{
fprintf( stderr, "no such history command\n" );
return -1;
}
strcpy( cpCmd, pRecord->cpCmd );
}
else
{ /* record the command */
pRecord = history_get_record( cpCmd );
history_pushback( pHistory, pRecord );
}
return 0;
}
void
child_fun( int sig )
{
waitpid( -1, NULL, WNOHANG );
}
二、history队列维护头文件
/*
** FILE: history.h
** NOTE: 2016-01-11 created by Jack Liu
** DESC: manage the historic commands up to 10
*/
#ifndef COM_JACKLIU_HISTORY_H
#define COM_JACKLIU_HISTORY_H
#define MAX_CMD 512
typedef struct history_record
{
char cpCmd[ MAX_CMD ];
struct history_record *prev;
struct history_record *next;
} HISTORY_RECORD, *PHISTORY_RECORD;
typedef struct history_list
{
PHISTORY_RECORD front;
PHISTORY_RECORD rear;
int iLen;
int iMax;
} HISTORY_LIST, *PHISTORY_LIST;
int history_init( PHISTORY_LIST list, int iMax );
PHISTORY_RECORD history_get_record( char *cpCmd );
int history_pushback( PHISTORY_LIST list, PHISTORY_RECORD record );
PHISTORY_RECORD history_pop( PHISTORY_LIST list );
PHISTORY_RECORD history_front( PHISTORY_LIST list );
PHISTORY_RECORD history_index( PHISTORY_LIST list, int iIndex );
int history_print( PHISTORY_LIST list );
int history_clear( PHISTORY_LIST list );
#endif
三、history队列维护实现
/*
** FILE: history.c
** NOTE: 2016-01-11 created by Jack Liu
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "history.h"
int
history_init( PHISTORY_LIST list, int iMax )
{
if( list == NULL )
{
fprintf( stderr, "history operation: init error, list argument illegal\n" );
return -1;
}
list->front = list->rear = NULL;
list->iLen = 0;
list->iMax = iMax;
return 0;
}
PHISTORY_RECORD
history_get_record( char *cpCmd )
{
PHISTORY_RECORD pRecord;
pRecord = malloc( sizeof( HISTORY_RECORD ) );
if( pRecord == NULL )
{
perror( "malloc" );
return NULL;
}
memset( pRecord, 0x00, sizeof( HISTORY_RECORD ) );
strcpy( pRecord->cpCmd, cpCmd );
return pRecord;
}
int
history_pushback( PHISTORY_LIST list, PHISTORY_RECORD pRecord )
{
if( list == NULL || pRecord == NULL )
{
fprintf( stderr, "illegal operation: list is empty\n" );
return -1;
}
if( list->rear == NULL )
{ /* first record */
list->front = pRecord;
pRecord->prev = pRecord->next = NULL;
}
else
{
list->rear->next = pRecord;
pRecord->prev = list->rear;
pRecord->next = NULL;
}
list->rear = pRecord;
list->iLen++;
if( list->iLen > list->iMax )
{
PHISTORY_RECORD pToFree = list->front;
list->front = list->front->next;
list->front->prev = NULL;
free( pToFree );
list->iLen = list->iMax;
}
return 0;
}
PHISTORY_RECORD
history_pop( PHISTORY_LIST list )
{
PHISTORY_RECORD pRecord;
if( list == NULL )
{
fprintf( stderr, "history operation: list is illegal\n" );
return NULL;
}
if( list->rear == NULL )
return NULL; /* the history list is emtpy */
pRecord = list->rear;
list->rear = list->rear->prev;
if( list->rear == NULL ) /* now list is empty */
list->front = NULL;
else
list->rear->next = NULL;
return pRecord;
}
PHISTORY_RECORD
history_front( PHISTORY_LIST list )
{
PHISTORY_RECORD pRecord;
if( list == NULL )
{
fprintf( stderr, "history operation: list is illegal\n" );
return NULL;
}
if( list->front == NULL )
return NULL; /* history list is empty */
pRecord = list->front;
list->front = list->front->next;
if( list->front == NULL ) /* now list is empty */
list->rear = NULL;
else
list->front->prev = NULL;
return pRecord;
}
PHISTORY_RECORD
history_index( PHISTORY_LIST list, int iIndex )
{
PHISTORY_RECORD pRecord;
int i;
if( list == NULL )
{
fprintf( stderr, "history operation: list is illegal\n" );
return NULL;
}
if( list->front == NULL )
{
fprintf( stderr, "history operation: list is emtpy\n" );
return NULL;
}
if( iIndex > list->iLen )
{
fprintf( stderr, "no such history command\n" );
return NULL;
}
i = 1;
pRecord = list->rear;
while( ++i <= iIndex )
pRecord = pRecord->prev;
return pRecord;
}
int
history_print( PHISTORY_LIST list )
{
PHISTORY_RECORD pRecord;
int i;
if( list == NULL )
{
fprintf( stderr, "history operation: history list is empty\n" );
return -1;
}
i = list->iLen;
pRecord = list->front;
while( i > 0 )
{
printf( "%02d %s\n", i--, pRecord->cpCmd );
pRecord = pRecord->next;
}
return 0;
}
int
history_clear( PHISTORY_LIST list )
{
if( list != NULL )
{
PHISTORY_RECORD pRecord;
while( ( pRecord = history_front( list ) ) )
free( pRecord );
}
return 0;
}
四、源代码