listints and part of mywc done

This commit is contained in:
Peter 2021-09-27 01:08:26 +08:00
parent 0f05a2c1a5
commit deeb3836ad
5 changed files with 438 additions and 1 deletions

4
.gitignore vendored
View File

@ -4,5 +4,7 @@
*.out *.out
.vscode/ .vscode/
.gdb_history
concordance/ concordance/
etc/

View File

@ -0,0 +1,150 @@
// SOLUTION 1 - DYNAMIC MEMORY WITH realloc()
/*
* 1. Write a C program, named listints, which will print the integers requested
via a single command-line argument. The list of integers is to appear in
strictly increasing order, with each requested integer appearing once and only
once (even if duplicated in the request).
* Simple examples of its use are:
prompt> listints 8
will print: 8
prompt> listints 3,5,9
will print: 3 5 9
prompt> listints 1-10
will print: 1 2 3 4 5 6 7 8 9 10
prompt> listints 1-10,6
will print: 1 2 3 4 5 6 7 8 9 10
* 🌶 🌶 And some much more difficult examples:
prompt> listints 2000-2020,40-50
prompt> listints 1-10,2010-2020,3000000-3000010
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define RANGE_SEP '-'
#define LIST_SEP ','
typedef struct {
long start;
long end;
} RANGE;
int sort_range(const void *v1, const void *v2)
{
RANGE* range_1 = (RANGE*)v1;
RANGE* range_2 = (RANGE*)v2;
return (range_1->start - range_2->start);
}
char *substr(char *str, char *end)
{
size_t length = end - str;
char *substring = malloc(length + 1);
strncpy(substring,str,length);
*(substring+length) = '\0';
return substring;
}
RANGE convert_to_range(char *range_str)
{
char *sep = strchr(range_str, RANGE_SEP);
RANGE range;
if (sep != NULL) {
sep++;
range.start = atol(substr(range_str,sep-1));
range.end = atol(sep);
} else {
range.start = atol(range_str);
range.end = range.start;
}
if (range.end < range.start) {
fprintf(stderr, "ERROR: End range must be before start range.\n");
exit(EXIT_FAILURE);
}
return range;
}
RANGE *split_ranges(char *list, size_t *list_size)
{
size_t range_list_max = 1;
RANGE *range_list = malloc(sizeof(RANGE));
if (range_list == NULL) {
fprintf(stderr, "ERROR: Couldn't allocate memory.\n");
exit(EXIT_FAILURE);
}
RANGE *range = range_list;
char *end;
while ( (end = strchr(list, LIST_SEP)) != NULL ) {
// EXTRACT ONE RANGE.
char *element = substr(list, end);
// EXPAND MEMORY
if (*list_size >= range_list_max) {
range_list_max *= 2;
range_list = realloc(range_list, range_list_max * sizeof(RANGE));
if (range_list == NULL) {
fprintf(stderr, "ERROR: Couldn't allocate memory.\n");
exit(EXIT_FAILURE);
}
range = range_list + *list_size;
}
*range = convert_to_range(element);
range++;
*list_size = (size_t)(range - range_list);
list = end + 1;
}
qsort(range_list, *list_size, sizeof(RANGE), sort_range);
return range_list;
}
long max(long v1, long v2)
{
if (v1 < v2) {
return v2;
}
return v1;
}
void print_ranges(RANGE *range_list, size_t list_size)
{
long max_number = 0;
for (size_t i = 0; i < list_size; i++) {
RANGE *range = range_list + i;
for (size_t n = max(range->start, max_number); n <= range->end; n++)
{
max_number = n+1;
printf("%lu ",n);
}
}
putchar('\n');
}
int main(int argc, char *argv[])
{
if (argc != 2)
{
fprintf(stderr, "Usage: listints [INTEGER RANGE]\n");
exit(EXIT_FAILURE);
}
// STRING WITH A ',' APPENDED TO MAKE THE SPLIT WORK.
char *ext_str = malloc(strlen(argv[1]) + 1);
strcpy(ext_str,argv[1]);
strcat(ext_str,",");
size_t list_size = 0;
RANGE *range_list = split_ranges(ext_str, &list_size);
print_ranges(range_list, list_size);
return 0;
}

View File

@ -0,0 +1,145 @@
// SOLUTION 1 - NO DYNAMIC MEMORY ALLOCATION
/*
* 1. Write a C program, named listints, which will print the integers requested
via a single command-line argument. The list of integers is to appear in
strictly increasing order, with each requested integer appearing once and only
once (even if duplicated in the request).
* Simple examples of its use are:
prompt> listints 8
will print: 8
prompt> listints 3,5,9
will print: 3 5 9
prompt> listints 1-10
will print: 1 2 3 4 5 6 7 8 9 10
prompt> listints 1-10,6
will print: 1 2 3 4 5 6 7 8 9 10
* 🌶 🌶 And some much more difficult examples:
prompt> listints 2000-2020,40-50
prompt> listints 1-10,2010-2020,3000000-3000010
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define RANGE_SEP '-'
#define LIST_SEP ','
typedef struct {
long start;
long end;
} RANGE;
int sort_range(const void *v1, const void *v2)
{
RANGE* range_1 = (RANGE*)v1;
RANGE* range_2 = (RANGE*)v2;
return (range_1->start - range_2->start);
}
char *substr(char *str, char *end)
{
size_t length = end - str;
char *substring = malloc(length + 1);
strncpy(substring,str,length);
*(substring+length) = '\0';
return substring;
}
RANGE convert_to_range(char *range_str)
{
char *sep = strchr(range_str, RANGE_SEP);
RANGE range;
if (sep != NULL) {
sep++;
range.start = atol(substr(range_str,sep-1));
range.end = atol(sep);
} else {
range.start = atol(range_str);
range.end = range.start;
}
if (range.end < range.start) {
fprintf(stderr, "ERROR: End range must be before start range.\n");
exit(EXIT_FAILURE);
}
return range;
}
RANGE *split_ranges(char *list, size_t list_size)
{
RANGE *range_list = malloc(list_size * sizeof(RANGE));
if (range_list == NULL) {
fprintf(stderr, "ERROR: Couldn't allocate memory.\n");
exit(EXIT_FAILURE);
}
RANGE *range = range_list;
char *end;
while ( (end = strchr(list, LIST_SEP)) != NULL ) {
// EXTRACT ONE RANGE.
char *element = substr(list, end);
*range = convert_to_range(element);
range++;
list = end + 1;
}
qsort(range_list, list_size, sizeof(RANGE), sort_range);
return range_list;
}
long max(long v1, long v2)
{
if (v1 < v2) {
return v2;
}
return v1;
}
void print_ranges(RANGE *range_list, size_t list_size)
{
long max_number = 0;
for (size_t i = 0; i < list_size; i++) {
RANGE *range = range_list + i;
for (size_t n = max(range->start, max_number); n <= range->end; n++)
{
max_number = n+1;
printf("%lu ",n);
}
}
putchar('\n');
}
int main(int argc, char *argv[])
{
if (argc != 2)
{
fprintf(stderr, "Usage: listints [INTEGER RANGE]\n");
exit(EXIT_FAILURE);
}
// STRING WITH A ',' APPENDED TO MAKE THE SPLIT WORK.
char *ext_str = malloc(strlen(argv[1]) + 1);
strcpy(ext_str,argv[1]);
strcat(ext_str,",");
// COUNT RANGES REQUIRED.
size_t ranges = 0;
size_t i = 0;
char c;
while ((c = ext_str[i]) != '\0') {
i++;
if (c == ',') {
ranges++;
}
}
RANGE *range_list = split_ranges(ext_str, ranges);
print_ranges(range_list, ranges);
return 0;
}

110
Week 9/3_mywc/mywc.c Normal file
View File

@ -0,0 +1,110 @@
/*
* 3. Both Linux and macOS provide a standard command named wc (an abbreviation for
wordcount!) which determines the number of lines, words, and characters in a
named file. You can read about this command using the online documentation:
(man wc).
* For this task, you will develop your own version of the wc program named mywc.
* a. Firstly, write a function named counter() that calculates and prints out
the number of lines contained within a file. Your counter() function should
take one argument, a character array that provides a filename.
* b. Next, modify your mywc program and the counter() function to either count
the lines of a file, named on the command-line, or to count the input
"arriving" via the stdin stream. We often described such a program as a
filter.
* c. Next, extend your counter() function so that it now also counts, and
prints, the number of characters and words found in the file. Be careful with
the meaning of a "word" - it's just a sequence of any characters surrounded by
whitespace characters. Check your printed results against those of the
standard wc program.
* d. Next, observe that our counter() function is printing its three
results - as we can only return a single result. Modify the counter() function
so that its three calculated results are now "given" back to the calling
function through pointers passed as parameters. Now, the calling function will
have the results placed in its own local variables, and be able to print
(or use) the results itself.
* 4. 🌶 Now, using the standard getopt() function introduced in Lecture 17, add
support for command-line options to your mywc utility from Task-3.
* Add command-line options to request:
-c to report the number of characters,
-l to report the number of lines,
-L to report the maximum line length (as supported on Linux, but not macOS),
and
-w to report the number of words.
* Ensure that your program still works with meaningful defaults if no options
are provided on the command-line.
* Ensure that your program checks that the provided command-line arguments are
sensible - if they are not, issue an appropriate error message to stderr after
all options have been processed.
*/
#include <stdlib.h>
#include <stdbool.h>
#include <stdio.h>
#include <ctype.h>
void counter_stats(FILE *file, int *lines, int *words, int *characters)
{
char buffer[BUFSIZ];
// PROCESS IN CHUNKS
bool word = false;
while ( fgets(buffer, BUFSIZ, file) != NULL ) {
char *c = &buffer[0];
while ( *c != '\0' ) {
// COUNT WORDS
if (word && isspace(*c)) {
(*words)++;
word = false;
} else if (!word) {
word = true;
}
// COUNT CHARACTERS
(*characters)++;
// COUNT NEWLINES
if ( *c == '\n' ) {
(*lines)++;
}
c++;
}
}
}
// NUMBER OF LINES IN A FILE.
void counter(char *path, int *lines, int *words, int *characters)
{
FILE *file;
if (path == NULL) {
file = stdin;
} else {
file = fopen(path, "r");
}
if (file != NULL) {
counter_stats(file, lines, words, characters);
} else {
fprintf(stderr, "Error reading file.\n");
exit(EXIT_FAILURE);
}
}
int main(int argc, char *argv[])
{
char *file = argv[1];
// CHECK IF WE ARE ACCEPTING INPUT FROM A PIPE.
if (argc > 2) {
fprintf(stderr, "Usage: mywc [FILE]\n");
exit(EXIT_FAILURE);
}
int lines;
int words;
int characters;
counter(argv[1], &lines, &words, &characters);
printf( "LINES %d, WORDS %d, CHARACTERS %d\n",
lines, words, characters );
return 0;
}

View File

@ -0,0 +1,30 @@
Unix was born in 1969 out of the mind of a computer scientist at Bell
Laboratories, Ken Thompson. Thompson had been a researcher on the Multics
project, an experience which spoiled him for the primitive batch computing
that was the rule almost everywhere else. But the concept of timesharing
was still a novel one in the late 1960s; the first speculations on it had
been uttered barely ten years earlier by computer scientist John McCarthy
(also the inventor of the Lisp language), the first actual deployment had
been in 1962, seven years earlier, and timesharing operating systems were
still experimental and temperamental beasts.
Computer hardware was at that time more primitive than even people who
were there to see it can now easily recall. The most powerful machines
of the day had less computing power and internal memory than a typical
cellphone of today. Video display terminals were in their infancy
and would not be widely deployed for another six years. The standard
interactive device on the earliest timesharing systems was the ASR-33
teletype - a slow, noisy device that printed upper-case-only on big
rolls of yellow paper. The ASR-33 was the natural parent of the Unix
tradition of terse commands and sparse responses.
When Bell Labs withdrew from the Multics research consortium, Ken
Thompson was left with some Multics-inspired ideas about how to build a
file system. He was also left without a machine on which to play a game
he had written called Space Travel, a science-fiction simulation that
involved navigating a rocket through the solar system. Unix began its
life on a scavenged PDP-7 minicomputer, as a platform for the Space Travel
game and a testbed for Thompson's ideas about operating system design.
[adapted from https://www.catb.org/esr/writings/taoup/html/ch02s01.html]