2023-11-29 11:05:59 +00:00
# include <stdio.h>
# include <errno.h>
# include <string.h>
# include <stdlib.h>
# include <unistd.h>
2024-04-06 15:34:14 +00:00
# include <signal.h>
2023-11-29 11:05:59 +00:00
# include <sys/wait.h>
2024-05-27 10:29:18 +00:00
# include <limits.h>
2024-04-06 15:34:14 +00:00
# include <ctype.h>
2024-05-27 10:29:18 +00:00
2024-05-31 12:52:38 +00:00
# ifndef ARG_MAX
# define ARG_MAX 10000
# endif
2024-05-27 10:29:18 +00:00
int args ;
char * cmd [ ARG_MAX + 1 ] ;
2023-11-29 11:05:59 +00:00
2024-04-06 15:34:14 +00:00
char * I_flag ;
2024-03-31 13:45:49 +00:00
char t_flag ;
char r_flag ;
2024-04-06 15:34:14 +00:00
char nl_flag ;
2023-12-06 15:26:26 +00:00
int n_flag ;
2024-06-19 09:11:48 +00:00
size_t s_flag ;
2024-03-31 13:45:49 +00:00
2024-04-06 15:34:14 +00:00
enum {
NORMAL ,
CEOF ,
ERROR ,
I_FLAG
} ;
2024-05-27 10:29:18 +00:00
enum {
NONE ,
QUOTE
} ;
2023-12-06 15:26:26 +00:00
void clear_cmd ( void ) {
2024-06-19 09:11:48 +00:00
for ( int i = 0 ; i < args ; i + + ) {
if ( cmd [ i ] ! = NULL ) {
2023-12-06 15:26:26 +00:00
free ( cmd [ i ] ) ;
2024-06-19 09:11:48 +00:00
cmd [ i ] = NULL ;
}
}
2023-11-29 11:05:59 +00:00
2023-12-06 15:26:26 +00:00
args = 0 ;
2023-11-29 11:05:59 +00:00
}
2024-06-19 09:11:48 +00:00
int Iflag_push ( const char * str , char * * arg ) {
size_t arg_len = strlen ( * arg ) ;
size_t str_len = strlen ( str ) ;
size_t Iflag_len = strlen ( I_flag ) ;
char * buf = malloc ( arg_len - Iflag_len + str_len + 1 ) ;
if ( buf = = NULL ) {
fprintf ( stderr , " xargs: malloc failed \n " ) ;
return 1 ;
}
strcpy ( buf , * arg ) ;
/* Replace */
char * ptr = strstr ( buf , I_flag ) ;
memmove ( ptr + str_len , ptr + Iflag_len , strlen ( ptr + Iflag_len ) + 1 ) ;
strncpy ( ptr , str , str_len ) ;
free ( * arg ) ;
* arg = buf ;
return 0 ;
}
int add_arg ( const char * str , size_t chars , int flag ) {
2024-05-27 10:29:18 +00:00
if ( args > = ARG_MAX )
2024-04-06 15:34:14 +00:00
return ERROR ;
2023-12-06 15:26:26 +00:00
2024-05-27 10:29:18 +00:00
else if ( n_flag > 0 & & args > n_flag & & I_flag = = NULL )
2024-04-06 15:34:14 +00:00
return ERROR ;
2023-12-06 15:26:26 +00:00
2024-01-15 19:26:18 +00:00
else if ( s_flag > 0 & & chars > s_flag )
2024-04-06 15:34:14 +00:00
return ERROR ;
2024-06-19 09:11:48 +00:00
if ( ! flag & & I_flag ) {
for ( int i = 0 ; i < args ; i + + ) {
while ( 1 ) {
if ( strstr ( cmd [ i ] , I_flag ) ) {
if ( Iflag_push ( str , & cmd [ i ] ) )
return ERROR ;
}
else
break ;
}
}
2024-04-06 15:34:14 +00:00
return I_FLAG ;
}
2024-01-15 19:26:18 +00:00
2023-12-06 15:26:26 +00:00
cmd [ args ] = strdup ( str ) ;
args + + ;
2024-04-06 15:34:14 +00:00
return NORMAL ;
}
int is_correct ( char c ) {
if ( nl_flag )
return c = = ' \0 ' ;
else
return isspace ( c ) ;
2023-12-06 15:26:26 +00:00
}
2024-05-27 10:29:18 +00:00
int xargs ( void ) {
size_t arg_size = 0 ;
char * arg = malloc ( 1 ) ;
if ( arg = = NULL ) {
fprintf ( stderr , " xargs: malloc failed \n " ) ;
return ERROR ;
}
2023-12-06 15:26:26 +00:00
2024-05-27 10:29:18 +00:00
size_t index = 0 ;
int flag = NONE ;
int ret = NORMAL ;
2024-06-19 09:11:48 +00:00
size_t chars = 0 ;
2023-12-06 19:04:40 +00:00
int args_passed = 0 ;
2024-03-31 13:45:49 +00:00
2023-12-06 19:04:40 +00:00
while ( 1 ) {
int c = getchar ( ) ;
2023-12-07 13:06:30 +00:00
if ( c = = EOF ) {
2024-03-31 13:45:49 +00:00
if ( flag = = QUOTE ) {
2024-05-27 10:29:18 +00:00
fprintf ( stderr , " xargs: unterminated quote \n " ) ;
2024-04-06 15:34:14 +00:00
ret = ERROR ;
2024-03-31 13:45:49 +00:00
}
2024-04-06 15:34:14 +00:00
ret = CEOF ;
2023-12-07 13:06:30 +00:00
break ;
}
2024-06-18 12:35:50 +00:00
if ( flag = = NONE & & is_correct ( c ) & & index > 0 ) {
arg [ index ] = ' \0 ' ;
2024-06-21 22:06:32 +00:00
2024-06-18 12:35:50 +00:00
int r = add_arg ( arg , chars , 0 ) ;
2024-06-19 09:11:48 +00:00
if ( r = = ERROR ) {
ret = ERROR ;
2024-02-14 15:48:01 +00:00
break ;
2024-06-19 09:11:48 +00:00
}
2023-12-06 15:26:26 +00:00
2024-05-27 10:29:18 +00:00
else if ( r = = I_FLAG ) {
free ( arg ) ;
2024-04-06 15:34:14 +00:00
return NORMAL ;
2024-05-27 10:29:18 +00:00
}
2024-04-06 15:34:14 +00:00
2024-05-27 10:29:18 +00:00
index = 0 ;
arg_size = 0 ;
2023-12-06 19:04:40 +00:00
args_passed + + ;
2024-05-27 10:29:18 +00:00
free ( arg ) ;
2024-06-21 22:06:32 +00:00
2024-05-27 10:29:18 +00:00
arg = malloc ( 1 ) ;
if ( arg = = NULL ) {
fprintf ( stderr , " xargs: malloc failed \n " ) ;
return ERROR ;
}
2023-12-06 19:04:40 +00:00
}
2023-12-06 15:26:26 +00:00
2023-12-06 19:04:40 +00:00
else {
2024-03-31 13:45:49 +00:00
if ( c = = ' " ' | | c = = ' \' ' ) {
if ( flag = = QUOTE )
flag = NONE ;
else
flag = QUOTE ;
continue ;
}
2023-12-17 13:45:13 +00:00
2024-06-22 15:55:30 +00:00
else if ( is_correct ( c ) & & flag = = NONE )
2024-06-21 13:25:11 +00:00
continue ;
2024-06-21 22:06:32 +00:00
char * tmp = realloc ( arg , ( + + arg_size ) + 1 ) ;
2024-06-17 09:43:35 +00:00
if ( tmp = = NULL ) {
free ( arg ) ;
2024-05-27 10:29:18 +00:00
fprintf ( stderr , " xargs: realloc failed \n " ) ;
return ERROR ;
}
2024-06-18 12:35:50 +00:00
arg = tmp ;
2024-06-21 22:06:32 +00:00
arg [ index ] = ( char ) c ;
2024-05-27 10:29:18 +00:00
index + + ;
2024-01-15 19:26:18 +00:00
chars + + ;
2023-12-06 15:26:26 +00:00
}
2023-11-29 11:05:59 +00:00
}
2024-06-19 09:11:48 +00:00
if ( args_passed = = 0 & & ret ! = ERROR )
2024-04-06 15:34:14 +00:00
ret = CEOF ;
2023-12-06 15:26:26 +00:00
2024-05-27 10:29:18 +00:00
free ( arg ) ;
2023-12-07 12:47:32 +00:00
return ret ;
2023-11-29 11:05:59 +00:00
}
2024-05-27 10:29:18 +00:00
int spawn ( void ) {
2023-11-29 11:05:59 +00:00
if ( t_flag ) {
2023-12-06 15:26:26 +00:00
for ( int i = 0 ; i < args ; i + + )
2023-11-29 11:05:59 +00:00
fprintf ( stderr , " %s " , cmd [ i ] ) ;
fputc ( ' \n ' , stderr ) ;
}
pid_t pid ;
if ( ( pid = fork ( ) ) = = 0 ) {
2023-12-06 15:26:26 +00:00
execvp ( cmd [ 0 ] , cmd ) ;
2024-06-19 09:11:48 +00:00
fprintf ( stderr , " xargs: exec: %s \n " , strerror ( errno ) ) ;
2023-11-29 11:05:59 +00:00
exit ( 1 ) ;
}
int status = 0 ;
waitpid ( pid , & status , 0 ) ;
2024-03-31 13:45:49 +00:00
if ( status = = 255 )
status = 124 ;
else if ( status > = 0x180 )
status = 125 ;
2023-11-29 11:05:59 +00:00
return status ;
}
int main ( int argc , char * * argv ) {
2024-06-19 09:11:48 +00:00
/* For -s flag */
char * p = NULL ;
2023-11-29 11:05:59 +00:00
int opt ;
2024-04-06 15:34:14 +00:00
while ( ( opt = getopt ( argc , argv , " tn:s:rP:0I: " ) ) ! = - 1 ) {
2023-11-29 11:05:59 +00:00
switch ( opt ) {
case ' t ' :
t_flag = 1 ;
break ;
2023-12-06 15:26:26 +00:00
case ' n ' :
n_flag = atoi ( optarg ) ;
2024-06-19 09:11:48 +00:00
if ( n_flag < = 0 ) {
fprintf ( stderr , " xargs: -n: invalid number: %s \n " , optarg ) ;
return 1 ;
}
2023-12-06 15:26:26 +00:00
break ;
2024-01-15 19:26:18 +00:00
case ' s ' :
2024-06-19 09:11:48 +00:00
s_flag = strtoul ( optarg , & p , 0 ) ;
if ( s_flag < = 0 | | * p ) {
fprintf ( stderr , " xargs: -s: invalid number: %zu%s \n " , s_flag , ( p ) ? p : " " ) ;
return 1 ;
}
2024-01-15 19:27:41 +00:00
break ;
2024-01-15 19:26:18 +00:00
2023-12-06 15:26:26 +00:00
case ' r ' :
r_flag = 1 ;
break ;
2024-04-06 15:34:14 +00:00
case ' 0 ' :
nl_flag = 1 ;
break ;
case ' I ' :
I_flag = optarg ;
break ;
2023-11-29 11:05:59 +00:00
default :
2024-05-27 10:29:18 +00:00
printf ( " xargs [tnsrP0I] [cmd [arg1] [arg2...] \n \t -t Print the command before start \n \t -n Pass no more than N args \n \t -r Don't run command if input is empty \n \t -s Pass command line of no more than N bytes \n \t -0 NUL terminated input \n \t -I STR Replace STR within PROG ARGS with input line \n " ) ;
2023-11-29 11:05:59 +00:00
return 0 ;
}
}
argv + = optind ;
argc - = optind ;
2023-12-06 15:26:26 +00:00
int ret = 0 ;
while ( 1 ) {
2024-01-15 19:29:43 +00:00
2023-12-06 15:26:26 +00:00
/* Arg */
if ( argc ) {
for ( int i = 0 ; i < argc ; i + + )
2024-04-06 15:34:14 +00:00
if ( add_arg ( argv [ i ] , 0 , 1 ) )
2023-12-06 15:26:26 +00:00
break ;
}
2023-11-29 11:05:59 +00:00
2023-12-06 15:26:26 +00:00
else
2024-05-27 10:29:18 +00:00
add_arg ( " echo " , 0 , 1 ) ;
2023-12-06 15:26:26 +00:00
2024-05-27 10:29:18 +00:00
int stdin_stat = xargs ( ) ;
2024-06-19 09:11:48 +00:00
if ( stdin_stat = = ERROR )
ret = 1 ;
2023-11-29 11:05:59 +00:00
2023-12-06 15:26:26 +00:00
/* Check NULL */
for ( int i = 0 ; i < args ; i + + ) {
if ( cmd [ i ] = = NULL ) {
fprintf ( stderr , " xargs: strdup failed \n " ) ;
clear_cmd ( ) ;
return 1 ;
}
}
/* Run */
2024-04-06 15:34:14 +00:00
if ( stdin_stat = = CEOF & & I_flag = = NULL ) {
2023-12-06 19:04:40 +00:00
if ( ! r_flag )
2024-03-31 13:45:49 +00:00
ret = spawn ( ) ;
2023-12-06 19:04:40 +00:00
}
2024-04-06 15:34:14 +00:00
else if ( stdin_stat = = NORMAL )
2024-03-31 13:45:49 +00:00
ret = spawn ( ) ;
2023-12-06 15:26:26 +00:00
clear_cmd ( ) ;
if ( stdin_stat )
break ;
}
2023-11-29 11:05:59 +00:00
return ret ;
}