myexpand/myexpand.c
2018-04-23 15:38:47 +02:00

235 lines
7.2 KiB
C

/**
* @file myexpand.c
* @brief This module enables a user to expand tabs in different ways by choosing
* a custom tabstop value.
* @date 2018-03-10
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <assert.h>
#include <signal.h>
#include <string.h>
#define BUF_SIZE 1024
/**
* @details Variable which stores the program name.
*/
static char *pname;
/**
* @details Variable which stores the desired tabsize. Default is 8.
*/
static int tabstop = 8;
/**
* @details Variable which gets set when a SIGINT or SIGTERM is received.
*/
volatile sig_atomic_t quit = 0;
/**
* @details Signal handler.
* @param signal The number of the received signal.
* @return This function has no return value.
*/
static void handle_signal(int signal)
{
quit = 1;
}
/**
* @details Prints the synopsis.
* @param void This function takes no parameters.
* @return Exit code of the program. Always non-zero.
*/
static void usage(void)
{
fprintf(stderr, "Usage: myexpand [-t tabstop] [file...]\n");
exit(EXIT_FAILURE);
}
/**
* @details Safely parses the supplied -t parameter and converts the string
* into a long int.
* @param optarg The argument after -t.
* @return On success returns the correctly parsed integer for a tabstop.
* On error the exit code is non-zero.
*/
static long int strToInt(char *optarg)
{
long int result = 0;
char *ptr = NULL;
result = strtol(optarg, &ptr, 10);
if (errno == EINVAL) {
fprintf(stderr, "%s: Conversion error ocurred: %d\n", pname, errno);
exit(EXIT_FAILURE);
}
if (errno == ERANGE) {
fprintf(stderr, "%s: The value provided was out of range!\n", pname);
exit(EXIT_FAILURE);
}
if (result == 0 || *ptr != '\0') {
fprintf(stderr, "%s: Conversion error ocurred!\n", pname);
exit(EXIT_FAILURE);
}
return result;
}
/**
* @details Goes over the supplied text, converts tabs accordingly and prints
* the outcome to stdout.
* @param source A string containing the message to be converted.
* @return This function has no return value.
*/
static void convert(char *source)
{
int x = 0;
for (int i = 0; i < (int)strlen(source); i++) {
if (source[i] == '\n')
x = -1;
if (source[i] == '\t') {
int p = labs(x - tabstop);
for (int s = 0; s < p; s++) {
fprintf(stdout, " ");
}
x += p;
} else {
fprintf(stdout, "%c", source[i]);
}
x++;
if (x >= (tabstop))
x = 0;
}
}
/**
* @details Reads filenames from an array of strings and calls the convert
* function for each of them.
* @param size The size of the array holding the filenames.
* @param filenames The array containing the filenames.
* @return Non-zero on failure.
*/
static void readFiles(int size, char *filenames[])
{
for (int i = 0; i < size; i++) {
char *source = NULL;
FILE *fp = fopen(filenames[i], "rb");
if (fp == NULL) {
perror(pname);
exit(EXIT_FAILURE);
}
if (fseek(fp, 0L, SEEK_END) == 0) {
long bufsize = ftell(fp);
if (bufsize == -1)
perror(pname);
source = malloc(sizeof(char) * (bufsize + 1));
if (fseek(fp, 0L, SEEK_SET) != 0)
perror(pname);
size_t newLen = fread(source, sizeof(char), bufsize, fp);
if (ferror(fp) != 0) {
perror(pname);
} else {
source[newLen++] = '\0';
}
}
fclose(fp);
convert(source);
free(source);
}
}
/**
* The main entry point of the program.
* @details Reads the supplied command-line arguments and calls the required
* functions.
* @param argc The number of command-line parameters in argv.
* @param argv The array of command-line arguments, argc elements long.
* @return 0 on success, non-zero on failure.
*/
int main(int argc, char *argv[])
{
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = handle_signal;
sigaction(SIGINT, &sa, NULL);
while (quit != 1) {
pname = argv[0];
int opt_ind = 0;
int tflag = 0;
while ((opt_ind = getopt(argc, argv, "t:")) != -1) {
switch (opt_ind) {
case 't':
if (tflag != 0) {
usage();
} else {
tabstop = strToInt(optarg);
tflag = 1;
}
break;
case '?':
usage();
default:
assert(0);
}
}
if ((argc - optind) != 0) {
char *filenames[argc-optind];
for (int i = optind, c = 0; i < argc; i++, c++) {
filenames[c] = argv[i];
}
readFiles(argc-optind, filenames);
exit(EXIT_SUCCESS);
} else {
char buffer[BUF_SIZE];
size_t contentSize = 1;
char *content = malloc(sizeof(char) * BUF_SIZE);
if (content == NULL) {
perror(pname);
exit(EXIT_FAILURE);
}
content[0] = '\0';
while (fgets(buffer, BUF_SIZE, stdin)) {
char *old = content;
contentSize += strlen(buffer);
content = realloc(content, contentSize);
if (content == NULL) {
perror(pname);
free(old);
exit(EXIT_FAILURE);
}
strcat(content, buffer);
}
if (ferror(stdin)) {
free(content);
perror(pname);
exit(EXIT_FAILURE);
}
convert(content);
free(content);
exit(EXIT_SUCCESS);
}
}
exit(EXIT_FAILURE);
}