Usage:
./usort <options> <filename>
Name:
./usort - sort lines of text.
Description:
Write sorted concatenation of FILE to standard output. In the absence of options, lexicographical order is taken as default.
Ordering Options:
-n, --numeric sorts by numeric order.
-r, --reverse sorts in reverse order.
-h, --help displays this message and exits.
-o, --output=FILE writes result to FILE instead of standard output.
Review Goals:
I would like some feedback regarding:
- Non-idiomatic code
- Style
- Error-Checking
- Bad practices
- Further improvements
Code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdbool.h>
#include <string.h>
#include <getopt.h>
#include <stddef.h>
#define MAX 1000
#define MAXLINES 50000
#define OP_LIST "nrho:"
FILE *fp;
struct flags {
bool nflag ; /* Numeric flag */
bool rflag ; /* Reverse flag */
bool oflag ; /* Output to FILE flag */
};
static void parse_options (struct option long_options[], struct flags *opt_ptr, int argc, char *argv[], char *out_file);
static void usage(size_t status);
static int read_file(char *lines[], char *filename);
static void write_to_stdout(char *lines[], int num_lines, struct flags *opt_ptr);
static void write_to_FILE (char *lines[], int num_lines, char *optarg, struct flags *opt_ptr);
static int scmp(const void *s1, const void *s2);
static int numcmp(const void *s1, const void *s2);
static void *xcalloc(size_t size);
static void free_allocs(char *lines[], char *out_file, char *in_file);
int main(int argc, char *argv[]) {
static struct option long_options[] = {
{"numeric", no_argument, NULL, 'n'},
{"reverse", no_argument, NULL , 'r'},
{"output", required_argument, NULL, 'o'},
{"help", no_argument, NULL , 'h'},
{NULL, 0, NULL, 0}
};
char *in_file = xcalloc(256);
char *out_file = xcalloc(256);
struct flags options = {false, false, false};
struct flags *opt_ptr = &options;
parse_options(long_options, opt_ptr, argc, argv, out_file);
/* If there is no file argument, call usage */
if (optind >= argc) {
usage(EXIT_FAILURE);
}
/* If there is more or less than 1 argument, call usage */
else if ((optind + 1) != argc) {
usage(EXIT_FAILURE);
}
strcpy(in_file, argv[optind]);
char *lines[MAXLINES];
size_t i;
for (i = 0; i < MAXLINES; i++) {
lines[i] = xcalloc(MAX);
}
size_t num_lines;
num_lines = read_file(lines, in_file);
qsort(lines, num_lines, sizeof *lines, opt_ptr->nflag? numcmp: scmp);
opt_ptr->oflag ? write_to_FILE(lines, num_lines, out_file, opt_ptr) : write_to_stdout(lines, num_lines, opt_ptr);
free_allocs(lines, out_file, in_file);
return EXIT_SUCCESS;
}
/* ====================================================================================================================================================================================================== */
static void usage(size_t status) {
if (status != EXIT_FAILURE) {
fprintf(stderr, "\nUSAGE\n\tusort <options> <filename>\n\n");
fputs("NAME\n\tusort - sort lines of text.\n\n\
DESCRIPTION\n\tWrite sorted concatenation of FILE to standard output.\n\
In the absence of options, lexicographical order is taken as default.\n\n", stdout);
fputs("ORDERING OPTIONS:\n\n\
-n, --numeric sorts by numeric order.\n\n\
-r, --reverse sorts in reverse order.\n\n\
-h, --help displays this message and exits.\n\n\
-o, --output=FILE writes result to FILE instead of standard output.\n\n", stdout);
exit(status);
}
else {
fprintf(stderr, "The syntax of the command is incorrect.\n\
Try %s: -h for more information.\n", "usort");
exit(status);
}
}
/* ======================================================================================================================================================================================================*/
static void parse_options (struct option long_options[], struct flags *opt_ptr, int argc, char *argv[], char *out_file) {
int c;
while ((c = getopt_long(argc, argv, OP_LIST, long_options, NULL)) != -1) {
switch(c) {
case 'n':
opt_ptr->nflag = true;
break;
case 'r':
opt_ptr->rflag = true;
break;
case 'h':
usage(EXIT_SUCCESS);
break;
case 'o':
opt_ptr->oflag = true;
strcpy(out_file, optarg);
break;
/* case '?' */
default:
usage(EXIT_FAILURE);
break;
}
}
}
/* ====================================================================================================================================================================================================== */
static int read_file(char *lines[], char *filename) {
fp = fopen(filename, "r");
if (!fp)
{
perror("fopen");
usage(EXIT_FAILURE);
}
char buffer[MAX];
size_t i = 0;
while (fgets(buffer, sizeof(buffer), fp))
{
buffer[strcspn(buffer, "\n")] = 0;
strcpy(lines[i], buffer);
i++;
}
fclose(fp);
return i;
}
/* ====================================================================================================================================================================================================== */
static void write_to_stdout(char *lines[], int num_lines, struct flags *opt_ptr) {
int i;
if (opt_ptr->rflag) {
for (i = num_lines - 1; i >= 0; i--) {
fprintf(stdout, "%s\n", lines[i]);
}
}
else {
for (i = 0; i < num_lines; i++) {
fprintf(stdout, "%s\n", lines[i]);
}
}
}
/* ====================================================================================================================================================================================================== */
static void write_to_FILE (char *lines[], int num_lines, char *out_file, struct flags *opt_ptr) {
int i;
fp = fopen(out_file, "w");
if (!fp) {
perror("fopen");
exit(EXIT_FAILURE);
}
if (opt_ptr->rflag) {
for (i = num_lines - 1; i >= 0; i--) {
fprintf(fp, "%s\n", lines[i]);
}
}
else {
for (i = 0; i < num_lines; i++) {
fprintf(fp, "%s\n", lines[i]);
}
fclose(fp);
}
}
/* ============================================================================================================================================================== */
/* Compares s1 and s2 lexicographically */
static int scmp(const void *s1, const void *s2) {
const char *v1, *v2;
v1 = *(char **)s1;
v2 = *(char **)s2; //the comparison method gets passed 'pointers to pointers' (char**) by qsort...
return strcmp(v1, v2);
}
/* =============================================================================================================================================================== */
/* Compares s1 and s2 numerically */
static int numcmp(const void *s1, const void *s2) {
long *v1 = *(long * const *)s1;
long *v2 = *(long * const *)s2;
return (*v1 > *v2) - (*v1 < *v2);
}
/* =============================================================================================================================================================== */
/* A wrapper function for calloc, returns a void ptr on success, exits on failure */
static void *xcalloc(size_t size) {
if (size == 0) {
exit(EXIT_FAILURE);
}
void *ptr = calloc(1, size);
if (!ptr) {
perror("calloc ");
exit(EXIT_FAILURE);
}
return ptr;
}
/* ================================================================================================================================================================ */
static void free_allocs(char *lines[], char *out_file, char *in_file) {
size_t i;
for (i = 0; i < MAXLINES; i++) {
free(lines[i]);
}
free(in_file);
free(out_file);
}
How do I go about dealing with the magic numbers? What options are there to deduce the file size and the number of lines beforehand? Is allocating storage for 50000 lines beforehand a waste?
Edit: The deletion of the post was due to a bug @Martin found, after which I deemed the post off-topic for the site and self-deleted it. The question may remain closed until it's fixed.
numcmp
). \$\endgroup\$