Tail -c linux command implementation in C - c

I need to write a tail in C language, where the input stream will be the argument in the console. The function should cut n characters from the input data. The command calling the program should be "echo" an example text "| ./a.out 4" - that is, the last 4 characters of the given input will be printed.
Unfortunately, my function does not print anything to me.
Thanks in advance. If there are any other, smarter solutions then I am open to suggestions.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define BUFFSIZE 1024
#define MAXLINES 100
char* tailFunction (const char* argv[])
{
char* buf, data;
int n = 0, i =0;
buf = malloc(sizeof(char) * MAXLINES);
n = atoi(argv[0]+1);
while (data != EOF)
{
data = getc(stdin);
buf[i] = data;
i++;
}
int x = strlen(buf) - n;
for ( ; x < strlen(buf) ; x++)
{
printf("%c", buf[x]);
}
free(buf);
return 0;
}
int main(int argc, const char *argv[])
{
if (argc !=2)
{
return -1;
}
if (argv < MAXLINES)
{
tailFunction(argv);
return 0;
}
else return -1;
}

Related

Creating a format string with a variable number of specifiers

I am trying to use va_list & its associated macros with vsprintf() to create a format string that has a variable number of specifiers. Here is an example program I wrote in which the number of specifiers can only be altered via the NUM_ARG macro:
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#define MAXBUF 4096
#define SPECIFIER "(%s)"
#define NUM_ARG 5
char *strmaker(int num_args, ...)
{
char form[MAXBUF] = { [0] = '\0' };
char *prnt = (char *) malloc(sizeof(char) * MAXBUF);
va_list strings;
for (int i = 0; i < num_args; ++i)
strcat(form, SPECIFIER);
va_start(strings, num_args);
vsprintf(prnt, form, strings);
va_end(strings);
return prnt;
}
int main(int argc, char *argv[])
{
if (argc != (NUM_ARG + 1))
return -1;
char *s = strmaker(NUM_ARG, argv[1], argv[2], argv[3], argv[4], argv[5]);
printf("%s\n", s);
free(s);
return 0;
}
However, this isn't exactly what I want to achieve. How could I do this with a variable number of arguments? How could a variable number of strings be passed to a function and used to initialise a va_list?
As far as I know, it is not possible to do that. If you are not so keen about using variadic functions and can redefine the function. The below code suits your need; Iterate through each item in the array and append to the string using snprintf.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXBUF 4096
#define SPECIFIER "(%s)"
char *strmaker(int num_args, char** strings)
{
char *prnt = (char *) malloc(sizeof(char) * MAXBUF);
int cur = 0;
/* Append the strings to the prnt buffer */
for (int i = 0; i < num_args; i++) {
int p_return = snprintf(prnt + cur, MAXBUF - cur, SPECIFIER, strings[i]); // If no error, return the number characters printed excluding nul (man page)
if (p_return >= MAXBUF - cur) // If buffer overflows (man page)
return prnt;
cur = cur + p_return; // Update the index location.
}
return prnt;
}
int main(int argc, char *argv[])
{
if (argc <= 1)
return -1;
char *s = strmaker(argc - 1, argv + 1);
printf("%s\n", s);
free(s);
return 0;
}
Terminal Session:
$ ./a.out 1 2 3
(1)(2)(3)
$ ./a.out 1 2 3 4 5 6 7
(1)(2)(3)(4)(5)(6)(7)
$ ./a.out Hello, This is stackoverflow, Bye
(Hello,)(This)(is)(stackoverflow,)(Bye)
Short answer is: You can't.
However you can work around it by using arrays of strings, possibly dynamically allocated. Then you could basically use the same technique you do now, but iterate over the array instead.
Perhaps something like this:
char *strmaker(size_t count, char *strings[])
{
// First get the length of all strings in the array
size_t result_length = 0;
for (size_t i = 0; i < count; ++i)
{
// +1 for space between the strings
// And for the last string adds space for the string null-terminator
result_length += strlen(strings[i]) + 1;
}
// Now allocate the string (using calloc to initialize memory to zero, same as the string null-terminator)
char *result = calloc(1, result_length);
// And not concatenate all strings in the array into one large string
for (size_t i = 0; i < count; ++i)
{
strcat(result, strings[i]);
if (i != count - 1)
{
strcat(result, " "); // Add space, except after last string
}
}
// Return the resulting string
return string;
}
int main(int argc, char *argv[])
{
// Create an array for all arguments
char **arguments = malloc(sizeof(char *) * argc - 1);
for (int a = 1; a < argc)
{
arguments[a - 1] = argv[a];
}
// Now create the single string
char *result = strmaker(argc - 1, arguments);
// ... and print it
printf("%s\n", result);
// Finally clean up after us
free(result);
free(arguments);
}
For the command-line arguments in argv you don't really need to create a new array to hold them, but it showcases how to create an array of string to pass to strmaker. You can use any strings you want instead of the command-line arguments.

Get suffixes in C

I'm trying to get the suffixes of an entered string, but i'm getting the prefixes how can I make to fix it?
The expected result for example with an entry string "Hello" is:
Hello
ello
ell
el
e
Now is returning:
Hello
Hell
Hel
He
H
Thanks
#include <errno.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char **suffix;
void panic(char *m) {
fprintf(stderr, "%s\n", m);
exit(0);
}
int main(int argc, char *argv[]) {
int n, i;
if (argc != 2)
panic("wrong parameters");
n = strlen(argv[1]);
suffix = (char **)malloc(n * sizeof(char *));
if (suffix == NULL)
error("Memoria no disponible");
for (i = 0; i < n; ++i) {
suffix[i] = (char *)malloc((n + 1 - i) * sizeof(char));
if (suffix[i] == NULL)
error("Memoria no disponible");
sprintf(suffix[i], "%s", argv[1]);
argv[1][n - 1 - i] = '\0';
}
for (i = 0; i < n; i++) {
printf("%d %s\n", i, suffix[i]);
}
return 0;
}
Just substitute these two statements
sprintf(suffix[i], "%s", argv[1]);
argv[1][n - 1 - i] = '\0';
for this one statement
sprintf(suffix[i], "%s", argv[1] + i );
Use Vlad from Moscow answer.
Something related you should now, only read argc and argv, never overwrite them. Although in theory you can do this, in practice it's both useless and dangerous.
Should always keep the code/logic as simple as you can.
The following proposed code can be halted with <ctrl-c> and/or EOF
#include <stdio.h>
#include <string.h>
int main( void )
{
char buffer[ 256 ];
// get the string
while( fgets( buffer, sizeof( buffer ), stdin ) )
{
//output string, dropping leading char at each loop iteration
size_t length = strlen( buffer );
for( size_t i=0; i<length; i++ )
{
printf( "%s\n", &buffer[i] );
}
}
}
here is a typical run of the program:
Note: the first line is from the user entering the string.
this is a string
this is a string
his is a string
is is a string
s is a string
is a string
is a string
s a string
a string
a string
string
string
tring
ring
ing
ng
g

Read raw bytes in argv[]

I was wondering if there was a way to read bytes (like this: \x00\x01\x02) from the command line in C.
For example:
#include <stdio.h>
int main(int argc, char *argv[]) {
printf("%s", argv[1]);
return 0;
}
user#UbuntuServer: ~/Code# gcc Program.c -o Program
user#UbuntuServer: ~/Code# ./Program "\x48\x69"
Hiuser#UbuntuServer: ~/Code# ./Program "\x48\x69\x0a"
Hi
user#UbuntuServer: ~/Code#
Thanks!
Unless you use a library to parse regex strings like that, you'll need to parse the hex manually. Check out this answer (which has slightly different syntax but a similar function):
Hexadecimal string to byte array in C
I would go for something like this:
int main(int argc, char **argv)
{
char *buf = malloc(strlen(argv[1]) / 4 + 1);
size_t i = 0;
for (char *tok = strtok(argv[1], "\\x"); tok; tok = strtok(NULL, "\\x"))
{
sscanf(tok, "%02hhx", buf + i);
i++;
}
buf[i] = '\0';
printf("%s", buf);
free(buf);
return 0;
}
I found the HEX to ASCII conversion functions on this thread, and modified it to suit my situation.
#include <stdio.h>
#include <string.h>
int hexToInt(char c) {
int first = c / 16 - 3;
int second = c % 16;
int result = first * 10 + second;
if(result > 9) {
result--;
}
return result;
}
int hexToASCII(char c, char d) {
int high = hexToInt(c) * 16;
int low = hexToInt(d);
return high + low;
}
int main(int argc, char *argv[]) {
char* hexString = argv[1];
char buf = 0;
for(int i = 0; i < strlen(hexString); i++) {
if(i % 2 != 0) {
printf("%c", hexToASCII(buf, hexString[i]));
} else {
buf = hexString[i];
}
}
return 0;
}

Taking input from command line as well as console(STDIN) in C

I am a beginner in coding and having difficulty trying to take input from both command line as well as console(STDIN). my program is supposed to search and replace a group of characters from a string. For example, concatenate cat gat : the output must be congatenate!
This is my code so far!
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
/*Function to replace a string with another string*/
char *rep_str(const char *s, const char *old, const char *new1)
{
char *ret;
int i, count = 0;
int newlen = strlen(new1);
int oldlen = strlen(old);
for (i = 0; s[i] != '\0'; i++)
{
if (strstr(&s[i], old) == &s[i])
{
count++;
i += oldlen - 1;
}
}
ret = (char *)malloc(i + count * (newlen - oldlen));
if (ret == NULL)
exit(EXIT_FAILURE);
i = 0;
while (*s)
{
if (strstr(s, old) == s) //compare the substring with the newstring
{
strcpy(&ret[i], new1);
i += newlen; //adding newlength to the new string
s += oldlen;//adding the same old length the old string
}
else
ret[i++] = *s++;
}
ret[i] = '\0';
return ret;
}
int main(int argc, char*agrv[])
{
char mystr[100], c[10], d[10];
scanf("%s", mystr);
scanf(" %s",c);
scanf(" %s",d);
char *newstr = NULL;
newstr = rep_str(mystr, c,d);
printf("%s\n", newstr);
free(newstr);
return 0;
}
as for now, it shows correct output for either console input or commandline input, bur not both!
kindly suggest the changes to be made!
You can have a check on the variable argc of function int main().
// Path of executable file is passed by default so value of 'argc' will be at least 1
if(argc > 1)
{
// do stuff to work on input from command line
}else{
// do stuff to work on input from STDIN
}
Instead of trying to parse input file through argc and argv, simply pass all the input file through stdin. This means that you will always use scanf to read input data.
At command line you will need to call using pipes, something like this:
$ cat file.txt | yourprogram

C char* array allocation

I was writing a function to parse command line as char* arguments array to another program but then I faced a problem allocating and/or reading the resulting buffer. I am stuck on that for about 2 days and 1000+ google searches later and I just can't figure it out all by myself.
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h> //for malloc, realloc
char** parse_cmdline(const char* cmdline) {
int wrdc = 0; //word count
int wrd_len = 0; //current word length
char** args = NULL; //result buffer, filled with arguments
int i; //counter of characters read from cmdline
for(i = 0; ; ++i){
if(cmdline[i] == '\n') {
if(wrd_len > 0) {
++wrdc;
args = realloc(args, wrdc * sizeof(char*));
memcpy((void*)&args[wrdc - 1], (void*) &cmdline[i - wrd_len], wrd_len);
printf("EOL found\n");
wrd_len = 0;
}
break;
}
else if(cmdline[i] == ' ') {
if(wrd_len > 0) {
++wrdc;
args = realloc(args, wrdc * sizeof(char*));
memcpy((void*)&args[wrdc - 1], (void*) &cmdline[i - wrd_len], wrd_len);
printf("space found!\n");
wrd_len = 0;
}
}
else if(cmdline[i] > 32 && cmdline[i] < 127) {
++wrd_len;
printf("char found !\n");
}
//FOR DEBUGGING
printf("i = %d, wrdc = %d, wrd_len = %d\n", i, wrdc, wrd_len);
}
printf("%d words in command\n", wrdc);
return args;
}
int main(int argc, char* argv[]) {
char buffer[200];
while(read(STDIN_FILENO, buffer, 200) > 0) {
char** data = parse_cmdline(buffer);
printf("%s\n", data[0]);
memset(buffer, 0, 200);
free(data);
}
return 0;
}
else if(cmdline[i] == ' ') {
if(wrd_len > 0) {
++wrdc;
args = realloc(args, wrdc * sizeof(char*));
memcpy((void*)&args[wrdc - 1], (void*) &cmdline[i - wrd_len], wrd_len);
printf("space found!\n");
wrd_len = 0;
}
}
Here the pointer stored in args[wrdc-1] is uninitialized and pointed to somewhere unknown. You shouldn't just memcpy() the cmdline into args[wrdc-1].
Allocate the memory for one single argument before memcpy():
args[wrdc-1] = calloc(wrd_len+1, sizeof(char));
Note the +1 and calloc() for terminating NULL character. Remember to free them in main().

Resources