Implementation of unix utility tail in C

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;







up vote
3
down vote

favorite












I've written a simple implementation of tail as part of reading The C Programming Language by Kernighan & Ritchie.



The question states to write tail, which prints the last n lines of its input. By default, n is 10 but should be able to be specified by writing:
tail -n to print the last n lines. I don't think the program has to open and read files, as that hasn't been covered in the book as of yet.



I chose to take input as tail -n <number> and wrote the code for parsing the arguments in (hopefully) that would allow for addition of more arguments at a later date easier using the switch statement. However right now, if there were more arguments (say x and z) one could write tail -xzn 10 which would be valid, printing the last 10 lines of input - which maybe could be viewed as a problem.



#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAXLEN 1000

int tail(char *buffer, int n);
int read_line(char *line, int max);
void print_lines(char* buffer, int n);

static int lines_read = 0;

int main(int argc, char* argv)
int number_of_lines = 10;
char c;

while(--argc > 0 && (*++argv)[0] == '-')
while((c = *++argv[0]))
switch(c)
case 'n':
if(argc-1 > 0)
number_of_lines = atoi(*(argv+1));
break;




char *line_buffer[number_of_lines];

if(tail(line_buffer, number_of_lines) < 0)
printf("ERROR: failed to allocate memory for a line.n");
return -1;


printf("Output:n");
print_lines(line_buffer, number_of_lines);

return 0;


int tail(char *buffer, int n)
int characters_read = 0;
char *p, line[MAXLEN];

while((characters_read = read_line(line, MAXLEN)) > 0)
if((p = malloc(sizeof(char)*characters_read)) == NULL)
return -1;

strcpy(p, line);
buffer[lines_read++ % n] = p;


return 0;


int read_line(char *line, int max)
int c, chars_read = 0;

while((c = getchar()) != EOF && c != 'n' && ++chars_read < max-1)
*line++ = c;

*line = '';

return chars_read;


void print_lines(char *buffer, int n)
for(int i=0; i < (lines_read > n ? n : lines_read); i++)
printf("%sn", buffer[lines_read > n ? lines_read++ % n : i]);







share|improve this question





















  • <limits.h> has an implementation-defined LINE_MAX that you can piggyback off of instead of defining your own MAXLEN.
    – Andrew Piliser
    Jan 25 at 19:47










  • @AndrewPiliser: Do you have a reference for <limits.h> defining LINE_MAX? I can't seem to find it in my copy of the standard.
    – Jerry Coffin
    Jan 25 at 20:06










  • @JerryCoffin That might be a Linux-specific thing, I found it here: pubs.opengroup.org/onlinepubs/7908799/xsh/limits.h.html.
    – Andrew Piliser
    Jan 25 at 22:12
















up vote
3
down vote

favorite












I've written a simple implementation of tail as part of reading The C Programming Language by Kernighan & Ritchie.



The question states to write tail, which prints the last n lines of its input. By default, n is 10 but should be able to be specified by writing:
tail -n to print the last n lines. I don't think the program has to open and read files, as that hasn't been covered in the book as of yet.



I chose to take input as tail -n <number> and wrote the code for parsing the arguments in (hopefully) that would allow for addition of more arguments at a later date easier using the switch statement. However right now, if there were more arguments (say x and z) one could write tail -xzn 10 which would be valid, printing the last 10 lines of input - which maybe could be viewed as a problem.



#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAXLEN 1000

int tail(char *buffer, int n);
int read_line(char *line, int max);
void print_lines(char* buffer, int n);

static int lines_read = 0;

int main(int argc, char* argv)
int number_of_lines = 10;
char c;

while(--argc > 0 && (*++argv)[0] == '-')
while((c = *++argv[0]))
switch(c)
case 'n':
if(argc-1 > 0)
number_of_lines = atoi(*(argv+1));
break;




char *line_buffer[number_of_lines];

if(tail(line_buffer, number_of_lines) < 0)
printf("ERROR: failed to allocate memory for a line.n");
return -1;


printf("Output:n");
print_lines(line_buffer, number_of_lines);

return 0;


int tail(char *buffer, int n)
int characters_read = 0;
char *p, line[MAXLEN];

while((characters_read = read_line(line, MAXLEN)) > 0)
if((p = malloc(sizeof(char)*characters_read)) == NULL)
return -1;

strcpy(p, line);
buffer[lines_read++ % n] = p;


return 0;


int read_line(char *line, int max)
int c, chars_read = 0;

while((c = getchar()) != EOF && c != 'n' && ++chars_read < max-1)
*line++ = c;

*line = '';

return chars_read;


void print_lines(char *buffer, int n)
for(int i=0; i < (lines_read > n ? n : lines_read); i++)
printf("%sn", buffer[lines_read > n ? lines_read++ % n : i]);







share|improve this question





















  • <limits.h> has an implementation-defined LINE_MAX that you can piggyback off of instead of defining your own MAXLEN.
    – Andrew Piliser
    Jan 25 at 19:47










  • @AndrewPiliser: Do you have a reference for <limits.h> defining LINE_MAX? I can't seem to find it in my copy of the standard.
    – Jerry Coffin
    Jan 25 at 20:06










  • @JerryCoffin That might be a Linux-specific thing, I found it here: pubs.opengroup.org/onlinepubs/7908799/xsh/limits.h.html.
    – Andrew Piliser
    Jan 25 at 22:12












up vote
3
down vote

favorite









up vote
3
down vote

favorite











I've written a simple implementation of tail as part of reading The C Programming Language by Kernighan & Ritchie.



The question states to write tail, which prints the last n lines of its input. By default, n is 10 but should be able to be specified by writing:
tail -n to print the last n lines. I don't think the program has to open and read files, as that hasn't been covered in the book as of yet.



I chose to take input as tail -n <number> and wrote the code for parsing the arguments in (hopefully) that would allow for addition of more arguments at a later date easier using the switch statement. However right now, if there were more arguments (say x and z) one could write tail -xzn 10 which would be valid, printing the last 10 lines of input - which maybe could be viewed as a problem.



#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAXLEN 1000

int tail(char *buffer, int n);
int read_line(char *line, int max);
void print_lines(char* buffer, int n);

static int lines_read = 0;

int main(int argc, char* argv)
int number_of_lines = 10;
char c;

while(--argc > 0 && (*++argv)[0] == '-')
while((c = *++argv[0]))
switch(c)
case 'n':
if(argc-1 > 0)
number_of_lines = atoi(*(argv+1));
break;




char *line_buffer[number_of_lines];

if(tail(line_buffer, number_of_lines) < 0)
printf("ERROR: failed to allocate memory for a line.n");
return -1;


printf("Output:n");
print_lines(line_buffer, number_of_lines);

return 0;


int tail(char *buffer, int n)
int characters_read = 0;
char *p, line[MAXLEN];

while((characters_read = read_line(line, MAXLEN)) > 0)
if((p = malloc(sizeof(char)*characters_read)) == NULL)
return -1;

strcpy(p, line);
buffer[lines_read++ % n] = p;


return 0;


int read_line(char *line, int max)
int c, chars_read = 0;

while((c = getchar()) != EOF && c != 'n' && ++chars_read < max-1)
*line++ = c;

*line = '';

return chars_read;


void print_lines(char *buffer, int n)
for(int i=0; i < (lines_read > n ? n : lines_read); i++)
printf("%sn", buffer[lines_read > n ? lines_read++ % n : i]);







share|improve this question













I've written a simple implementation of tail as part of reading The C Programming Language by Kernighan & Ritchie.



The question states to write tail, which prints the last n lines of its input. By default, n is 10 but should be able to be specified by writing:
tail -n to print the last n lines. I don't think the program has to open and read files, as that hasn't been covered in the book as of yet.



I chose to take input as tail -n <number> and wrote the code for parsing the arguments in (hopefully) that would allow for addition of more arguments at a later date easier using the switch statement. However right now, if there were more arguments (say x and z) one could write tail -xzn 10 which would be valid, printing the last 10 lines of input - which maybe could be viewed as a problem.



#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAXLEN 1000

int tail(char *buffer, int n);
int read_line(char *line, int max);
void print_lines(char* buffer, int n);

static int lines_read = 0;

int main(int argc, char* argv)
int number_of_lines = 10;
char c;

while(--argc > 0 && (*++argv)[0] == '-')
while((c = *++argv[0]))
switch(c)
case 'n':
if(argc-1 > 0)
number_of_lines = atoi(*(argv+1));
break;




char *line_buffer[number_of_lines];

if(tail(line_buffer, number_of_lines) < 0)
printf("ERROR: failed to allocate memory for a line.n");
return -1;


printf("Output:n");
print_lines(line_buffer, number_of_lines);

return 0;


int tail(char *buffer, int n)
int characters_read = 0;
char *p, line[MAXLEN];

while((characters_read = read_line(line, MAXLEN)) > 0)
if((p = malloc(sizeof(char)*characters_read)) == NULL)
return -1;

strcpy(p, line);
buffer[lines_read++ % n] = p;


return 0;


int read_line(char *line, int max)
int c, chars_read = 0;

while((c = getchar()) != EOF && c != 'n' && ++chars_read < max-1)
*line++ = c;

*line = '';

return chars_read;


void print_lines(char *buffer, int n)
for(int i=0; i < (lines_read > n ? n : lines_read); i++)
printf("%sn", buffer[lines_read > n ? lines_read++ % n : i]);









share|improve this question












share|improve this question




share|improve this question








edited Jan 25 at 18:39









200_success

123k14143401




123k14143401









asked Jan 25 at 18:05









bag

503




503











  • <limits.h> has an implementation-defined LINE_MAX that you can piggyback off of instead of defining your own MAXLEN.
    – Andrew Piliser
    Jan 25 at 19:47










  • @AndrewPiliser: Do you have a reference for <limits.h> defining LINE_MAX? I can't seem to find it in my copy of the standard.
    – Jerry Coffin
    Jan 25 at 20:06










  • @JerryCoffin That might be a Linux-specific thing, I found it here: pubs.opengroup.org/onlinepubs/7908799/xsh/limits.h.html.
    – Andrew Piliser
    Jan 25 at 22:12
















  • <limits.h> has an implementation-defined LINE_MAX that you can piggyback off of instead of defining your own MAXLEN.
    – Andrew Piliser
    Jan 25 at 19:47










  • @AndrewPiliser: Do you have a reference for <limits.h> defining LINE_MAX? I can't seem to find it in my copy of the standard.
    – Jerry Coffin
    Jan 25 at 20:06










  • @JerryCoffin That might be a Linux-specific thing, I found it here: pubs.opengroup.org/onlinepubs/7908799/xsh/limits.h.html.
    – Andrew Piliser
    Jan 25 at 22:12















<limits.h> has an implementation-defined LINE_MAX that you can piggyback off of instead of defining your own MAXLEN.
– Andrew Piliser
Jan 25 at 19:47




<limits.h> has an implementation-defined LINE_MAX that you can piggyback off of instead of defining your own MAXLEN.
– Andrew Piliser
Jan 25 at 19:47












@AndrewPiliser: Do you have a reference for <limits.h> defining LINE_MAX? I can't seem to find it in my copy of the standard.
– Jerry Coffin
Jan 25 at 20:06




@AndrewPiliser: Do you have a reference for <limits.h> defining LINE_MAX? I can't seem to find it in my copy of the standard.
– Jerry Coffin
Jan 25 at 20:06












@JerryCoffin That might be a Linux-specific thing, I found it here: pubs.opengroup.org/onlinepubs/7908799/xsh/limits.h.html.
– Andrew Piliser
Jan 25 at 22:12




@JerryCoffin That might be a Linux-specific thing, I found it here: pubs.opengroup.org/onlinepubs/7908799/xsh/limits.h.html.
– Andrew Piliser
Jan 25 at 22:12










1 Answer
1






active

oldest

votes

















up vote
1
down vote



accepted










  • Avoid globals. lines_read is naturally suited to return lines_read.


  • tail leaks memory. You shall free the line pointed by buffer[....] before reassigning it.


  • sizeof(char) is guaranteed to be 1.


I don't remember at which point K&R offer this exercise. In any case,



  • The line-reading loop is better expressed as fgets.


  • malloc/strcpy combination is a long way to say strdup.






share|improve this answer





















  • Is there a reason to use fgets instead of getline?
    – Andrew Piliser
    Jan 25 at 19:46










  • @AndrewPiliser: all the usual ones, such as it's already well documented, and tested, and everybody who knows C immediately recognizes what it is and what it does. There is another minor detail: his getline looks like if a line is too long, it'll read a single line as multiple lines, and give no warning that it's doing so. fgets can read partial lines, but gives you a warning when it does so (the buffer you read will have a new-line if and only if you read the entire line).
    – Jerry Coffin
    Jan 25 at 20:17










  • @AndrewPiliser I was not even sure that fgets is within the scope of this exercise. get line is definitely out.
    – vnp
    Jan 25 at 20:46










Your Answer




StackExchange.ifUsing("editor", function ()
return StackExchange.using("mathjaxEditing", function ()
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
);
);
, "mathjax-editing");

StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");

StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "196"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);

else
createEditor();

);

function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
convertImagesToLinks: false,
noModals: false,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);








 

draft saved


draft discarded


















StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f185990%2fimplementation-of-unix-utility-tail-in-c%23new-answer', 'question_page');

);

Post as a guest






























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes








up vote
1
down vote



accepted










  • Avoid globals. lines_read is naturally suited to return lines_read.


  • tail leaks memory. You shall free the line pointed by buffer[....] before reassigning it.


  • sizeof(char) is guaranteed to be 1.


I don't remember at which point K&R offer this exercise. In any case,



  • The line-reading loop is better expressed as fgets.


  • malloc/strcpy combination is a long way to say strdup.






share|improve this answer





















  • Is there a reason to use fgets instead of getline?
    – Andrew Piliser
    Jan 25 at 19:46










  • @AndrewPiliser: all the usual ones, such as it's already well documented, and tested, and everybody who knows C immediately recognizes what it is and what it does. There is another minor detail: his getline looks like if a line is too long, it'll read a single line as multiple lines, and give no warning that it's doing so. fgets can read partial lines, but gives you a warning when it does so (the buffer you read will have a new-line if and only if you read the entire line).
    – Jerry Coffin
    Jan 25 at 20:17










  • @AndrewPiliser I was not even sure that fgets is within the scope of this exercise. get line is definitely out.
    – vnp
    Jan 25 at 20:46














up vote
1
down vote



accepted










  • Avoid globals. lines_read is naturally suited to return lines_read.


  • tail leaks memory. You shall free the line pointed by buffer[....] before reassigning it.


  • sizeof(char) is guaranteed to be 1.


I don't remember at which point K&R offer this exercise. In any case,



  • The line-reading loop is better expressed as fgets.


  • malloc/strcpy combination is a long way to say strdup.






share|improve this answer





















  • Is there a reason to use fgets instead of getline?
    – Andrew Piliser
    Jan 25 at 19:46










  • @AndrewPiliser: all the usual ones, such as it's already well documented, and tested, and everybody who knows C immediately recognizes what it is and what it does. There is another minor detail: his getline looks like if a line is too long, it'll read a single line as multiple lines, and give no warning that it's doing so. fgets can read partial lines, but gives you a warning when it does so (the buffer you read will have a new-line if and only if you read the entire line).
    – Jerry Coffin
    Jan 25 at 20:17










  • @AndrewPiliser I was not even sure that fgets is within the scope of this exercise. get line is definitely out.
    – vnp
    Jan 25 at 20:46












up vote
1
down vote



accepted







up vote
1
down vote



accepted






  • Avoid globals. lines_read is naturally suited to return lines_read.


  • tail leaks memory. You shall free the line pointed by buffer[....] before reassigning it.


  • sizeof(char) is guaranteed to be 1.


I don't remember at which point K&R offer this exercise. In any case,



  • The line-reading loop is better expressed as fgets.


  • malloc/strcpy combination is a long way to say strdup.






share|improve this answer













  • Avoid globals. lines_read is naturally suited to return lines_read.


  • tail leaks memory. You shall free the line pointed by buffer[....] before reassigning it.


  • sizeof(char) is guaranteed to be 1.


I don't remember at which point K&R offer this exercise. In any case,



  • The line-reading loop is better expressed as fgets.


  • malloc/strcpy combination is a long way to say strdup.







share|improve this answer













share|improve this answer



share|improve this answer











answered Jan 25 at 19:33









vnp

36.6k12991




36.6k12991











  • Is there a reason to use fgets instead of getline?
    – Andrew Piliser
    Jan 25 at 19:46










  • @AndrewPiliser: all the usual ones, such as it's already well documented, and tested, and everybody who knows C immediately recognizes what it is and what it does. There is another minor detail: his getline looks like if a line is too long, it'll read a single line as multiple lines, and give no warning that it's doing so. fgets can read partial lines, but gives you a warning when it does so (the buffer you read will have a new-line if and only if you read the entire line).
    – Jerry Coffin
    Jan 25 at 20:17










  • @AndrewPiliser I was not even sure that fgets is within the scope of this exercise. get line is definitely out.
    – vnp
    Jan 25 at 20:46
















  • Is there a reason to use fgets instead of getline?
    – Andrew Piliser
    Jan 25 at 19:46










  • @AndrewPiliser: all the usual ones, such as it's already well documented, and tested, and everybody who knows C immediately recognizes what it is and what it does. There is another minor detail: his getline looks like if a line is too long, it'll read a single line as multiple lines, and give no warning that it's doing so. fgets can read partial lines, but gives you a warning when it does so (the buffer you read will have a new-line if and only if you read the entire line).
    – Jerry Coffin
    Jan 25 at 20:17










  • @AndrewPiliser I was not even sure that fgets is within the scope of this exercise. get line is definitely out.
    – vnp
    Jan 25 at 20:46















Is there a reason to use fgets instead of getline?
– Andrew Piliser
Jan 25 at 19:46




Is there a reason to use fgets instead of getline?
– Andrew Piliser
Jan 25 at 19:46












@AndrewPiliser: all the usual ones, such as it's already well documented, and tested, and everybody who knows C immediately recognizes what it is and what it does. There is another minor detail: his getline looks like if a line is too long, it'll read a single line as multiple lines, and give no warning that it's doing so. fgets can read partial lines, but gives you a warning when it does so (the buffer you read will have a new-line if and only if you read the entire line).
– Jerry Coffin
Jan 25 at 20:17




@AndrewPiliser: all the usual ones, such as it's already well documented, and tested, and everybody who knows C immediately recognizes what it is and what it does. There is another minor detail: his getline looks like if a line is too long, it'll read a single line as multiple lines, and give no warning that it's doing so. fgets can read partial lines, but gives you a warning when it does so (the buffer you read will have a new-line if and only if you read the entire line).
– Jerry Coffin
Jan 25 at 20:17












@AndrewPiliser I was not even sure that fgets is within the scope of this exercise. get line is definitely out.
– vnp
Jan 25 at 20:46




@AndrewPiliser I was not even sure that fgets is within the scope of this exercise. get line is definitely out.
– vnp
Jan 25 at 20:46












 

draft saved


draft discarded


























 


draft saved


draft discarded














StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f185990%2fimplementation-of-unix-utility-tail-in-c%23new-answer', 'question_page');

);

Post as a guest













































































Popular posts from this blog

Chat program with C++ and SFML

Function to Return a JSON Like Objects Using VBA Collections and Arrays

Will my employers contract hold up in court?