235 lines
7.2 KiB
C
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);
|
|
}
|