2025-05-01 17:55:58 -03:00
|
|
|
#include <getopt.h>
|
|
|
|
#include <limits.h>
|
|
|
|
#include <locale.h>
|
|
|
|
#include <stdbool.h>
|
2025-04-30 14:43:21 -03:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
2025-04-30 16:24:27 -03:00
|
|
|
#include <string.h>
|
2025-05-01 17:55:58 -03:00
|
|
|
|
|
|
|
#define FIELDS 5
|
2025-05-01 09:21:30 -03:00
|
|
|
|
|
|
|
bool mode_bytes = false;
|
|
|
|
bool mode_chars = false;
|
|
|
|
bool mode_lines = false;
|
|
|
|
bool mode_mllen = false;
|
|
|
|
bool mode_words = false;
|
2025-04-30 14:43:21 -03:00
|
|
|
|
2025-05-01 22:16:26 -03:00
|
|
|
static struct option long_options[] = {
|
|
|
|
{"bytes", no_argument, NULL, 'c'},
|
|
|
|
{"chars", no_argument, NULL, 'm'},
|
|
|
|
{"lines", no_argument, NULL, 'l'},
|
|
|
|
{"words", no_argument, NULL, 'w'},
|
|
|
|
{"max-line-length", no_argument, NULL, 'L'},
|
|
|
|
{"total", required_argument, NULL, 1000},
|
|
|
|
{"help", no_argument, NULL, 'h'},
|
|
|
|
{"version", no_argument, NULL, 'v'},
|
|
|
|
{0, 0, 0, 0}
|
|
|
|
};
|
|
|
|
|
2025-05-01 10:06:24 -03:00
|
|
|
enum { TOTAL };
|
|
|
|
enum { LINES, WORDS, CHARS, BYTES, MLLEN };
|
|
|
|
|
2025-04-30 16:55:53 -03:00
|
|
|
int intlen(int number) {
|
|
|
|
// return ceil(log10(number+1));
|
|
|
|
char buffer[10];
|
|
|
|
sprintf(buffer, "%d", number);
|
|
|
|
return strlen(buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
void wc(char **list, int count) {
|
2025-05-01 17:55:58 -03:00
|
|
|
setlocale(LC_ALL, "pt_BR.UTF-8");
|
2025-04-30 16:24:27 -03:00
|
|
|
char buffer[BUFSIZ];
|
2025-05-01 17:55:58 -03:00
|
|
|
size_t mblen;
|
2025-05-01 09:29:33 -03:00
|
|
|
/**
|
|
|
|
* Items = [
|
|
|
|
* [0] => lines,
|
|
|
|
* [1] => words,
|
|
|
|
* [2] => chars,
|
|
|
|
* [3] => bytes,
|
|
|
|
* [4] => max-line-length
|
|
|
|
* ]
|
|
|
|
*/
|
2025-05-01 09:40:56 -03:00
|
|
|
int **items = malloc(count * sizeof(int *));
|
2025-05-01 17:55:58 -03:00
|
|
|
if (!items) {
|
|
|
|
perror("Erro na alocação de items");
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
2025-05-01 09:40:56 -03:00
|
|
|
for (int i = 0; i < count; i++) {
|
2025-05-01 17:55:58 -03:00
|
|
|
items[i] = calloc(FIELDS, sizeof(int));
|
|
|
|
if (!items[i]) {
|
|
|
|
perror("Erro na alocação de items[i]");
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
2025-05-01 09:40:56 -03:00
|
|
|
}
|
2025-04-30 16:55:53 -03:00
|
|
|
for (int i = 1; i < count; i++) {
|
|
|
|
FILE *stream = fopen(list[i], "r");
|
|
|
|
if (stream == NULL) {
|
|
|
|
perror("Erro ao abrir o arquivo");
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
while (fgets(buffer, BUFSIZ, stream) != NULL) {
|
|
|
|
/* bytes */
|
2025-05-01 09:21:30 -03:00
|
|
|
if (mode_bytes)
|
2025-05-01 10:06:24 -03:00
|
|
|
items[i][BYTES] += strlen(buffer);
|
2025-04-30 16:55:53 -03:00
|
|
|
char *str;
|
|
|
|
/* words */
|
2025-05-01 09:21:30 -03:00
|
|
|
if (mode_words) {
|
|
|
|
str = strdup(buffer);
|
|
|
|
while (strtok_r(str, " \t\n", &str) != NULL)
|
2025-05-01 10:06:24 -03:00
|
|
|
items[i][WORDS]++;
|
2025-05-01 09:21:30 -03:00
|
|
|
}
|
2025-05-01 17:55:58 -03:00
|
|
|
/* chars */
|
|
|
|
if (mode_chars) {
|
|
|
|
str = strdup(buffer);
|
|
|
|
mblen = mbstowcs(NULL, str, 0);
|
|
|
|
if (mblen == (size_t)-1) {
|
|
|
|
perror("Erro ao converter multibyte");
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
if (mblen > INT_MAX) {
|
|
|
|
fprintf(stderr, "Erro valor maior que INT_MAX!\n");
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
items[i][CHARS] += (int)mblen;
|
|
|
|
}
|
|
|
|
/* max-line-lenght */
|
2025-05-01 09:21:30 -03:00
|
|
|
if (mode_mllen) {
|
|
|
|
str = strdup(buffer);
|
2025-05-01 17:55:58 -03:00
|
|
|
int len = strlen(str);
|
2025-05-01 09:21:30 -03:00
|
|
|
if (len > 0 && str[len-1] == '\n')
|
|
|
|
str[len-1] = '\0';
|
2025-05-01 17:55:58 -03:00
|
|
|
mblen = mbstowcs(NULL, str, 0);
|
|
|
|
if (mblen == (size_t)-1) {
|
|
|
|
perror("Erro ao converter multibyte");
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
if (mblen > INT_MAX) {
|
|
|
|
fprintf(stderr, "Erro valor maior que INT_MAX!\n");
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
items[i][MLLEN] = items[i][MLLEN] > (int)mblen ? items[i][MLLEN] : (int)mblen;
|
2025-05-01 09:21:30 -03:00
|
|
|
}
|
2025-04-30 16:55:53 -03:00
|
|
|
/* lines */
|
2025-05-01 09:21:30 -03:00
|
|
|
if (mode_lines)
|
2025-05-01 10:06:24 -03:00
|
|
|
items[i][LINES]++;
|
2025-04-30 16:55:53 -03:00
|
|
|
}
|
|
|
|
if (strcmp(list[i], "/dev/stdin") == 0)
|
|
|
|
list[i] = "";
|
2025-05-01 10:06:24 -03:00
|
|
|
items[TOTAL][LINES] += items[i][LINES];
|
|
|
|
items[TOTAL][WORDS] += items[i][WORDS];
|
|
|
|
items[TOTAL][BYTES] += items[i][BYTES];
|
2025-05-01 17:55:58 -03:00
|
|
|
items[TOTAL][CHARS] += items[i][CHARS];
|
2025-04-30 16:55:53 -03:00
|
|
|
fclose(stream);
|
2025-04-30 16:24:27 -03:00
|
|
|
}
|
2025-05-01 09:21:30 -03:00
|
|
|
for (int i = 1; i < count; i++)
|
2025-05-01 10:06:24 -03:00
|
|
|
items[TOTAL][MLLEN] = items[TOTAL][MLLEN] > items[i][MLLEN] ? items[TOTAL][MLLEN] : items[i][MLLEN];
|
2025-05-01 09:40:56 -03:00
|
|
|
/* Ordem das colunas */
|
2025-05-01 09:21:30 -03:00
|
|
|
for (int i = 1; i < count; i++) { /* -l lines -w words -m chars -c bytes -L max-line-length */
|
|
|
|
if (mode_lines)
|
2025-05-01 10:06:24 -03:00
|
|
|
printf("%*d ", intlen(items[TOTAL][LINES]), items[i][LINES]);
|
2025-05-01 09:21:30 -03:00
|
|
|
if (mode_words)
|
2025-05-01 10:06:24 -03:00
|
|
|
printf("%*d ", intlen(items[TOTAL][WORDS]), items[i][WORDS]);
|
2025-05-01 09:21:30 -03:00
|
|
|
if (mode_chars)
|
2025-05-01 10:06:24 -03:00
|
|
|
printf("%*d ", intlen(items[TOTAL][CHARS]), items[i][CHARS]);
|
2025-05-01 09:21:30 -03:00
|
|
|
if (mode_bytes)
|
2025-05-01 10:06:24 -03:00
|
|
|
printf("%*d ", intlen(items[TOTAL][BYTES]), items[i][BYTES]);
|
2025-05-01 09:21:30 -03:00
|
|
|
if (mode_mllen)
|
2025-05-01 10:06:24 -03:00
|
|
|
printf("%*d ", intlen(items[TOTAL][MLLEN]), items[i][MLLEN]);
|
2025-05-01 09:21:30 -03:00
|
|
|
printf("%s\n", list[i]);
|
|
|
|
}
|
|
|
|
if (count > 2) { /* Print total */
|
|
|
|
if (mode_lines)
|
2025-05-01 10:06:24 -03:00
|
|
|
printf("%*d ", intlen(items[TOTAL][LINES]), items[TOTAL][LINES]);
|
2025-05-01 09:21:30 -03:00
|
|
|
if (mode_words)
|
2025-05-01 10:06:24 -03:00
|
|
|
printf("%*d ", intlen(items[TOTAL][WORDS]), items[TOTAL][WORDS]);
|
2025-05-01 09:21:30 -03:00
|
|
|
if (mode_chars)
|
2025-05-01 10:06:24 -03:00
|
|
|
printf("%*d ", intlen(items[TOTAL][CHARS]), items[TOTAL][CHARS]);
|
2025-05-01 09:21:30 -03:00
|
|
|
if (mode_bytes)
|
2025-05-01 10:06:24 -03:00
|
|
|
printf("%*d ", intlen(items[TOTAL][BYTES]), items[TOTAL][BYTES]);
|
2025-05-01 09:21:30 -03:00
|
|
|
if (mode_mllen)
|
2025-05-01 10:06:24 -03:00
|
|
|
printf("%*d ", intlen(items[TOTAL][MLLEN]), items[TOTAL][MLLEN]);
|
2025-05-01 09:21:30 -03:00
|
|
|
printf("total\n");
|
|
|
|
}
|
2025-05-01 10:11:35 -03:00
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
free(items[i]);
|
|
|
|
}
|
|
|
|
free(items);
|
2025-04-30 16:55:53 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, char **argv) {
|
2025-05-01 22:16:26 -03:00
|
|
|
int opt, option_index;
|
2025-04-30 16:55:53 -03:00
|
|
|
char *list[2];
|
2025-05-01 22:16:26 -03:00
|
|
|
char *mode_total;
|
2025-05-01 09:29:33 -03:00
|
|
|
// short_options = "c, m, l, L, w"
|
|
|
|
// long_options = "bytes,chars,lines,max-line-length,total,help,version,words"
|
2025-05-01 22:16:26 -03:00
|
|
|
while ((opt = getopt_long(argc, argv, "cmlLwhv", long_options, &option_index)) != -1) {
|
|
|
|
if (opt == 0) {
|
|
|
|
continue;
|
|
|
|
}
|
2025-05-01 09:29:33 -03:00
|
|
|
switch (opt) {
|
|
|
|
case 'c':
|
|
|
|
mode_bytes = true;
|
|
|
|
break;
|
|
|
|
case 'm':
|
|
|
|
mode_chars = true;
|
|
|
|
break;
|
|
|
|
case 'l':
|
|
|
|
mode_lines = true;
|
|
|
|
break;
|
|
|
|
case 'L':
|
|
|
|
mode_mllen = true;
|
|
|
|
break;
|
|
|
|
case 'w':
|
|
|
|
mode_words = true;
|
|
|
|
break;
|
2025-05-01 22:16:26 -03:00
|
|
|
case 1000:
|
|
|
|
// auto, always, only, never
|
|
|
|
mode_total = optarg;
|
|
|
|
break;
|
|
|
|
case 'h':
|
|
|
|
printf("help\n");
|
|
|
|
exit(EXIT_SUCCESS);
|
|
|
|
case 'v':
|
|
|
|
printf("version\n");
|
|
|
|
exit(EXIT_SUCCESS);
|
2025-05-01 09:29:33 -03:00
|
|
|
default:
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
}
|
2025-05-01 09:21:30 -03:00
|
|
|
/* Default mode */
|
2025-05-01 22:16:26 -03:00
|
|
|
if ((mode_bytes || mode_chars || mode_lines || mode_mllen || mode_words) == false) {
|
2025-05-01 09:21:30 -03:00
|
|
|
mode_bytes = true;
|
|
|
|
mode_lines = true;
|
|
|
|
mode_words = true;
|
|
|
|
}
|
2025-04-30 16:55:53 -03:00
|
|
|
if (argc < 2) {
|
|
|
|
list[1] = "/dev/stdin";
|
|
|
|
wc(list, 2);
|
|
|
|
} else {
|
2025-05-01 09:29:33 -03:00
|
|
|
optind--;
|
|
|
|
wc(argv + optind, argc - optind);
|
2025-04-30 16:24:27 -03:00
|
|
|
}
|
2025-04-30 14:43:21 -03:00
|
|
|
return EXIT_SUCCESS;
|
|
|
|
}
|