summaryrefslogtreecommitdiffstats
path: root/main.c
blob: 5baff9bd736dd4abfd75c4ac9e2d8609077107a6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
/*	Copyright (C) 2021 Gentoo-libre Install

	This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

	This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

	You should have received a copy of the GNU General Public License along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h> //just for ENOENT
#include <sys/stat.h> //just to check for folder
#include "main.h"

int main(int argc, char *argv[])
{
bool decode = false; //decoding is set to false by default (default is to encode)

//malloc the buffer to store the input string in
unsigned char *buffer = malloc(DEFAULT_SIZE*sizeof(unsigned char));
if (!buffer){fprintf(stderr,"Malloc Failed!\n"); exit(1);}
int size = DEFAULT_SIZE-1;//-1 to account for the added NULL char

//wrap encoded lines after COLS character (default 76).
int wrap = WRAP_CHAR;

//Go into stdin mode if there are no cli args
if (argc == 1){stdinMode(buffer, wrap, decode, argv[0]);}//stdinmode is given a pointer to the input string and addittionally the amount of chars to wrap whether default or user set

//arguments processing
int argvPos = 1;

while ((argv[argvPos]) && (argv[argvPos][0] == '-'))
{
if (argv[argvPos][1] == '-')
	{
	if ((memcmp(argv[argvPos],"--wrap=",7)) == 0)
		{
		wrap = atoi(argv[argvPos]+7);//overwrite the default wrap size with the new supplied one
		if (wrap < 0){printf("%s: invalid wrap size: \'%s\'\n", argv[0], argv[argvPos]+7); exit(1);}//if atoi() returns negative, the input wasn't a positive int
		goto nextArgument;
		}

	if ((memcmp(argv[argvPos],"--decode",8)) == 0)
		{
		decode = true;
		goto nextArgument;
		}

	if ((memcmp(argv[argvPos],"--help",6)) == 0)
        	{
		printf("Usage %s [OPTION]... [FILE]\n", argv[0]);
	        printf("Base64 encode FILE, or standard input, to standard output.\n");
		printf("With no FILE, or when FILE is -, read standard input\n\n");
		printf("Mandatory arguments to long options are mandatory for short options too.\n");
		printf("  -d, --decode\t\tdecode data.\n");
		printf("  -w, --wrap=COLS\twrap encoded lines after COLS character (default 76).\n");
                printf("\t\t\t  Use 0 to disable line wrapping\n\n");
		printf("Version: 0.1\n");
		exit(0);
		}
	printf("%s: unrecognized option -- \'%s\'\n", argv[0], argv[argvPos]);//if here is reached, the option wasn't valid
	exit(1);
	}

if ((memcmp(argv[argvPos],"-w",2)) == 0)
	{
	if (!argv[argvPos+1]){printf("%s: option requires an argument -- \'%c\'\n",argv[0],argv[argvPos][1]); exit(1);}//this triggers if -w hasn't been given a value
	wrap = atoi(argv[++argvPos]);//overwrite the default wrap size with the new supplied one
	if (!wrap){printf("%s: invalid wrap size: \'%s\'\n", argv[0], argv[argvPos]); exit(1);}
	goto nextArgument;
	}

if ((memcmp(argv[argvPos],"-d",2)) == 0)
	{
	decode = true;
	goto nextArgument;
	}

if (!argv[argvPos][1]){stdinMode(buffer, wrap, decode, argv[0]);}//if a '-' was detected, but the next char is NULL, '-' was inputted as a file, so stdinMode is activated
printf("%s: invalid option -- \'%c\'\n", argv[0], argv[argvPos][1]);//if here is reached, the option wasn't valid
exit(1);

nextArgument: ++argvPos;//get read to process the next argument
}

if (!argv[argvPos]){stdinMode(buffer, wrap, decode, argv[0]);}//if no more operands were provided after options, go into stdin mode
if (argv[argvPos+1]){printf("%s: extra operand \'%s\'\n", argv[0], argv[argvPos]); exit(1);}//if there are TWO and not ONE addittional argv arg's at this point, the operaterand to the left of the "FILE" is extra


//file processing mode
int result;
int i = 0; //keeps track of buffer to write
FILE *fp;
struct stat sb;//struct used by stat()

if (stat(argv[argvPos], &sb) == 0 && S_ISDIR(sb.st_mode)){fprintf(stderr, "%s: read error: Is a directory\n", argv[0]); free(buffer); exit(1);}//check if cli argv is a folder

fp = fopen(argv[argvPos],"rb");//rb for reading binary
if (!fp)//if file failed to open
	{
	if (errno == ENOENT){fprintf(stderr,"%s: %s: No such file or directory\n", argv[0], argv[argvPos]); free(buffer); exit(1);}//ENOENT is returned if there is no such file or directory
	fprintf(stderr, "%s: %s: Could not open\n", argv[0], argv[argvPos]); free(buffer); exit(1);//all other errors are blanket handled
	}

result = fread(buffer+i, 1, sizeof(unsigned char)*DEFAULT_SIZE-1, fp);//attempt to read up to DEFAULT_SIZE from file. (-1 to account for NULL char to be added)
if (!result){fclose(fp); free(buffer); exit(1);}//fread will fail if file is empty, so just exit.
i+=result;//add result to i so chars in buffer aren't overwritten next fread()

readAgain: if (result == (sizeof(unsigned char)*DEFAULT_SIZE-1))//if the file is longer than DEFAULT_SIZE, result will be the size of DEFAULT_SIZE
		{
		size += DEFAULT_SIZE;//increase size of buffer by DEFAULT_SIZE each time
		buffer = realloc(buffer,size*sizeof(unsigned char));
		if (!buffer){fprintf(stderr,"Realloc Failed!\n"); exit(1);}

		result = fread(buffer+i, 1, sizeof(unsigned char)*DEFAULT_SIZE-1, fp);//attempt to read up to DEFAULT_SIZE from file
	        if (!result){goto perfectSize;}//the previous fread() was the perfect size, so jump to processing
		i+=result;//add result to i so chars in buffer aren't overwritten next fread()
		goto readAgain;//repeat until result is less than DEFAULT_SIZE
		}

perfectSize: buffer[i] = '\0';//add null char since function expects it

if (!decode)
	{
	char *output = base64Encode(buffer, i);
	if (!output){fprintf(stderr, "Malloc Failed!\n"); exit(1);}
	if (wrap == 0){result = fwrite(output, 1, sizeof(unsigned char)*strlen(output), stdout); goto exit;}//if wrap is set to 0, do no wrapping.

	int charactersLeft = strlen(output);//the total number of characters to print is strlen(output_
	int x = 0;//position to print from in string so wrap printing works

	while (charactersLeft > wrap)//keep looping until all of the wrap length writes are done
		{
		result = fwrite(output+x, 1, sizeof(unsigned char)*wrap, stdout);//write wrap number of characters at a time
		if (!result){fprintf(stderr, "Fwrite Failed!\n"); exit(1);}//fwrite to stdout should not fail. It's best to just exit if it does.
		putchar('\n');//add a newline to finish off the wrapped line
		x += wrap;//increase the printing position to the next position
		charactersLeft -= wrap;//decrease charactersLeft by how many were printed
		}

	if (charactersLeft == 0){goto exit;}//if the printed number of characters was perfect, don't print an extra newline

	result = fwrite(output+x, 1, sizeof(unsigned char)*charactersLeft, stdout);//print any remaining characters that are smaller in number than wrap.
	if (!result){fprintf(stderr, "Fwrite Failed!\n"); exit(1);}//fwrite to stdout should not fail. It's best to just exit if it does.
	putchar('\n');//add a newline to finish off the wrapped line
	goto exit;
	}

if (decode)
	{
	int outputLength = base64Decode(buffer, i);
	if (!outputLength){fprintf(stderr,"%s: invalid input\n", argv[0]); free(buffer); exit(1);}//base64 returns -1 if input string was invalid
	result = fwrite(buffer, 1, sizeof(unsigned char)*outputLength, stdout);
	goto exit;
	}

exit: fclose(fp);//close the file
free(buffer);
return 0;
}