forked from TheAlgorithms/C
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: printf statement written in c (as minprintf) without using stdi…
…o.h li… (TheAlgorithms#820) * printf statement written in c (as minprintf) without using stdio.h library. * Added proper documentation in minprintf.h * Modified and more Documented code. * Requested changes commited * Referance links added * updating DIRECTORY.md * Renamed the file * updating DIRECTORY.md * Test file added * updating DIRECTORY.md * Requested changes commited * Requested changes commited Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com>
- Loading branch information
Showing
3 changed files
with
396 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,352 @@ | ||
/** | ||
* @file | ||
* @brief Implementation of a [function](https://www.geeksforgeeks.org/variable-length-argument-c) similar to `printf` | ||
* @details | ||
* `printf` statement rewritten (as `min_printf`) in C without using the `stdio.h` library | ||
* Syntax of `min_printf` is same as `printf` | ||
* Currently min_printf handles: | ||
* Integers, Doubles, floats, characters and strings | ||
* The format specifiers and escape sequence is the same as for `printf` | ||
* User can also specify the width and precision if required, just like in the case of `printf` | ||
* How to use it: | ||
* - First include min_printf.h in your code | ||
* - Then type `min_printf()`, and pass required parameters to it | ||
* - As already specified, it's syntax is same as printf | ||
* @author [Jaskarn Singh](https://github.com/Jaskarn7) | ||
*/ | ||
|
||
#ifndef MIN_PRINTF_H | ||
#define MIN_PRINTF_H | ||
|
||
#include <stdlib.h> /// for `malloc` and `free` functions | ||
#include <unistd.h> /// for `write` function | ||
#include <stdarg.h> /// for `va_start` and `va_arg` functions | ||
|
||
#define INT_MAX_LENGTH 10 /// used as standard length of string to store integers | ||
#define PRECISION_FOR_FLOAT 8 /// default precision for float or double if not specified | ||
|
||
/** | ||
* @brief struct used to store character in certain times | ||
*/ | ||
typedef struct buffer { | ||
char buffr_char; // Character will be stored in this variable | ||
int buf_size; // Checks if character is present in buffr_char or not, 0 if no and 1 if yes | ||
} Buffer; | ||
|
||
/** | ||
* @details | ||
* This function return ten to the power a(The parameter specified to it) like: | ||
* if the parameter specified is 4 i.e. -> power_of_ten(4) is called then | ||
* this function will return ten to the power four (10000); | ||
* @param a The power of ten which is to be returned | ||
* @return Ten to the power a | ||
*/ | ||
int power_of_ten(int a) | ||
{ | ||
int n = 1; ///< This number will be returned as ten to power of a | ||
for (int i = 1; i <= a; ++i) | ||
n *= 10 ; | ||
return n; | ||
} | ||
|
||
/** | ||
* @brief Checks if a character is a number | ||
* @param c character to be checked if it's a number or not | ||
* @return `true`(1) if the character is a number | ||
* @return `false`(0) if the character is NOT a number | ||
*/ | ||
int is_number(char *c) | ||
{ | ||
return (*c >= '0' && *c <= '9') ? 1 : 0; | ||
} | ||
|
||
/** | ||
* @brief Returns specific required next character | ||
* @param p pointer to a format string of `min_printf()` | ||
* @param buffer struct for checking if buffr_char character is present or not | ||
* @return character inside `buffer->buffr_char`, if `buffer->buf_size` is one | ||
* @return character at which p is pointing, if `buffer->buf_size` is zero | ||
*/ | ||
char get_ch(char *p, Buffer *buffer) | ||
{ | ||
if (buffer->buf_size) { | ||
buffer->buf_size = 0; ///< Since character is used, this sets `buffer->buf_size` to zero | ||
return buffer->buffr_char; // Returns character inside buffer->buffr_char | ||
} | ||
return *p++; | ||
} | ||
|
||
/** | ||
* @brief Stores character to the `buffer->buffr_char` | ||
* @param c character to be stored in the `buffer->buffr_char` | ||
* @param buffer struct where character will be stored | ||
*/ | ||
void unget_ch(char *c, Buffer *buffer) | ||
{ | ||
buffer->buffr_char = *c; // Character initializes inside buffer->buffr_char | ||
buffer->buf_size = 1; // Sets bufsize to one as new character is stored in buffr_char | ||
} | ||
|
||
|
||
/** | ||
* @brief Calculates the number of digits in a number | ||
* @param n number whose digits are to be counted | ||
* @return number of digits in n | ||
*/ | ||
int get_number_of_digits(int n) | ||
{ | ||
int digits = 0; // Stores encountered number of digits | ||
while (n > 0) { | ||
++digits; // Since number still contains a digit, so increment digit variable | ||
n /= 10; // Removes last digit from number | ||
} | ||
return digits; | ||
} | ||
|
||
/** | ||
* @brief Prints one character on screen | ||
* @param s character to be printed on the screen | ||
*/ | ||
void put_char(char s) | ||
{ | ||
/* buf used for storing character to be printed in an array (+1 for '\0')*/ | ||
char *buf = (char *) malloc(sizeof(char) + 1); | ||
*buf = s; | ||
*(buf + 1) = '\0'; | ||
write(1, buf, 1); | ||
free(buf); | ||
} | ||
|
||
/** | ||
* @brief Reverses a string using [two pointer algorithm](https://www.geeksforgeeks.org/program-reverse-array-using-pointers/?ref=rp) | ||
* @param p pointer to the string which is to be reversed | ||
*/ | ||
void reverse_str(char *p) | ||
{ | ||
char *l = p; // Points to first character of p | ||
char *h = p; // Will be used to point to last character of p | ||
char temp; // Temporarily stores a character, Used in swapping | ||
|
||
while (*h != '\0') | ||
++h; | ||
--h; // Now h point to last valid character of string | ||
|
||
/* Swap character which lower and higher are pointing until lower < higher. At that point string will be reversed.*/ | ||
while (l < h) { | ||
temp = *l; | ||
*l = *h; | ||
*h = temp; | ||
++l; // Increment lower to next character | ||
--h; // Decrement higher to previous character from current character | ||
} | ||
} | ||
|
||
/** | ||
* @details | ||
* The algorithm here is to first convert the number into | ||
* string and then reverse it be passing it to reverse_str function | ||
* and then printing on the screen | ||
* @param n Number to be printed | ||
* @param width Total characters to be printed (Prints ' ' if (size < width) | ||
* @param precision Total character of number to be printed (prints 0 before number if size of number < precision) | ||
* | ||
*/ | ||
void print_int_value(int n, int width, int precision) | ||
{ | ||
char *p = (char *) malloc(INT_MAX_LENGTH * sizeof(char) + 1); /* +1 for '\0' */ | ||
char *s = p; // Temporary pointer | ||
int size = 0; //!< Used to store number of digits in number | ||
|
||
while (n > 0) { | ||
*s++ = n % 10 + '0'; // Converts last digit of number to character and store it in p | ||
++size; // Increment size variable as one more digit is occured | ||
n /= 10; // Removes the last digit from the number n as we have successfully stored it in p | ||
} | ||
*s = '\0'; | ||
|
||
s = p; // Again point back s to starting of p | ||
|
||
reverse_str(p); | ||
|
||
/*! | ||
* The next two conditions check weather it is required to | ||
* add blanks before printing the number (ie: width)and is it specified how many | ||
* zeros to be printed before the number is printed (ie: precision) | ||
*/ | ||
if (width > 0 && size < width) | ||
for (int i = 0; i < (width - precision); ++i) | ||
put_char(' '); | ||
|
||
if (precision > 0 && precision > size) | ||
for (int i = 0; i < (precision - size); ++i) | ||
put_char('0'); | ||
|
||
/* Prints the number.*/ | ||
while (*s != '\0') | ||
put_char(*s++); | ||
|
||
free(p); | ||
} | ||
|
||
/** | ||
* @brief The algorithm here is also the same as the `print_int_value` function | ||
* | ||
* @details | ||
* First, the digits before decimal is printed by converting the double | ||
* to int. Then after printed a `.`, the double number is subtracted with | ||
* the integer value of the number, leaving us with 0 before the decimal. | ||
* Then, we multiply the number with 10 raised to the power precision ( | ||
* precision means how many digits to be printed after the decimal.) | ||
* By default, the precision is 8 if it is not specified. | ||
* Then, the remaining number is printed on the screen. | ||
* @param dval double number to be printed | ||
* @param width similar to width parameter of print_int_value() | ||
* @param precision tells the number of digits to be printed after the decimal (By default it is 8) | ||
*/ | ||
void print_double_value(double dval, int width, int precision) | ||
{ | ||
int ndigits = get_number_of_digits((int) dval); // Store number of digits before decimal in dval | ||
int reqd_blanks = width - (precision + 1) - ndigits; // Blanks to be printed before printing dval, just to cover the width | ||
|
||
print_int_value((int) dval, reqd_blanks, 0); // Prints the part before decimal | ||
|
||
put_char('.'); // Print decimal | ||
|
||
/*Deletes digits before decimal and makes them zero. For example: | ||
if dval = 1923.79022, them this will make dval = 0.79022 | ||
*/ | ||
dval = dval - (int) dval; | ||
|
||
dval *= power_of_ten(precision); // Brings precision number of digits after decimal to before decimal | ||
|
||
print_int_value((int) dval, 0, precision); // Prints the remaining number | ||
} | ||
|
||
/** | ||
* @details | ||
* First size of the string is calculated to check whether | ||
* width and precision are to be taken into account or not. | ||
* Then, the string is printed in accordingly. | ||
* @param p pointer to string to be printed | ||
* @param width if (width > sizeof string) then, blanks will be printed before sting to cover up the width | ||
* @param precision total characters of the string to be printed (prints the whole string if 0 or greater than size of string) | ||
*/ | ||
void print_string(char *p, int width, int precision) | ||
{ | ||
int size = 0; // Stores number of character in string | ||
char *s = p; // Temporary pointer | ||
|
||
/* Calculates size of string p*/ | ||
while (*s != '\0') { | ||
++size; | ||
++s; | ||
} | ||
|
||
s = p; // Point s to starting of p | ||
|
||
/* Checks how many characters to be printed. | ||
if precision is defined then size variable is changed to precision so that only precision | ||
number of characters were printed. | ||
*/ | ||
if (precision != 0 && precision < size) | ||
size = precision; | ||
|
||
/* Prints blanks to cover the width if required*/ | ||
for (int i = 0; i < (width - size); ++i) | ||
put_char(' '); | ||
|
||
/* Print the string.*/ | ||
for (int i = 0; i < size; ++i) | ||
put_char(*s++); | ||
|
||
} | ||
|
||
/** | ||
* @brief Takes width and precision specified from the format of the string | ||
* @param p pointer of the format string | ||
* @param width variable in which width will be stored | ||
* @param precision variable in which precision will be stored | ||
* @return character pointer to the current pointer of string p (used to update value of p) | ||
*/ | ||
char *get_width_and_precision(char *p, Buffer *buffer, int *width, int *precision) | ||
{ | ||
/* Skip % if p is pointing to it.*/ | ||
if (*p == '%') | ||
++p; | ||
|
||
/* Calculates the width specified. */ | ||
while (*p != '.' && is_number(p)) | ||
*width = *width * 10 + (*p++ - '0'); | ||
|
||
/* Calculates the precision specified.*/ | ||
if (*p == '.' /* Since a precision is always specified after a '.'. */) { | ||
while (is_number(++p)) | ||
*precision = *precision * 10 + (*p - '0'); | ||
unget_ch(p, buffer); // The non number will be stored in `buffer->buffr` | ||
} | ||
return p; | ||
} | ||
|
||
/** | ||
* min_printf is the function same as printf | ||
* @param fmt format of string | ||
* @param ... arguments passed according to the format | ||
*/ | ||
void min_printf(char *fmt, ...) | ||
{ | ||
va_list ap; // Points to each unnamed arg in turn | ||
char *p, *sval; // p will be used to point to fmt and sval will store string value | ||
char cval; // Stores character value | ||
int ival; // For integer values | ||
double dval; // For double or float values | ||
va_start(ap, fmt); // Makes ap points to first unnames argument | ||
|
||
/* Initializing the buffer for storing character. */ | ||
Buffer *buffer = (Buffer *) malloc(sizeof(Buffer)); | ||
buffer->buf_size = 0; // Initially set buffer size to zero as no character is inserted | ||
|
||
for (p = fmt; *p != '\0'; ++p) { | ||
|
||
/* If p != '%' then the character is printed to screen. */ | ||
if (*p != '%') { | ||
put_char(*p); | ||
continue; | ||
} | ||
|
||
int width = 0; // Stores width specified | ||
int precision = 0; // Stores precision specified | ||
|
||
/* Updates values of width, precision and p. */ | ||
p = get_width_and_precision(p, buffer, &width, &precision); | ||
|
||
/* Checks format of next argument.*/ | ||
switch (get_ch(p, buffer)) { | ||
case 'd': // Integer | ||
ival = va_arg(ap, int); | ||
print_int_value(ival, width, precision); | ||
break; | ||
case 'c': // Character | ||
cval = va_arg(ap, int); | ||
put_char(cval); | ||
break; | ||
case 'f': // Float or Double | ||
dval = va_arg(ap, double); | ||
|
||
// If precision is not specified then default value is applied | ||
if (precision == 0) | ||
precision = PRECISION_FOR_FLOAT; | ||
print_double_value(dval, width, precision); | ||
break; | ||
case 's': // String pointer | ||
sval = va_arg(ap, char *); | ||
print_string(sval, width, precision); | ||
break; | ||
default: | ||
put_char(*p); | ||
break; | ||
} | ||
} | ||
va_end(ap); | ||
} | ||
|
||
#endif /* MIN_PRINTF_H */ |
Oops, something went wrong.