How to read parts [duplicate] - sef

This question already has answers here:
Why is “while( !feof(file) )” always wrong?
(5 answers)
Closed 2 years ago.
I have a problem with this program: I was able to make another draft program but it only reads the first three lines and stops. Unfortunately, the input file is not symmetrical so I should use while loops for some parts only to avoid repeating parts of the program. With strcpy I could copy the work done up to a certain point and then continue from that point on. I was trying but visual studio tells me to move the memory to the heap but I don't know how to do it ... Practically I should only extrapolate certain data from the input file and order them in an output file according to a precise line.
I also know there's a problemi with char buf[200], rec[200] because I have to allocate much more memory...

the first time in
while ( !feof(fd) ) {
fscanf(fd, "session %d (COPY MODE):\n\n", &session);
you call feof before any read so feof cannot work proprely, an easy change is to do
while (fscanf(fd, "session %d (COPY MODE):\n\n", &session) == 1) {
Out of that the mode can be something else that COPY MODE and the two \n after are not mandatory, you also do not check your next fscanf success and globaly the algorithm you use is wrong
A possible way to do the job also checking the validity of the input file is :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
FILE * fp = fopen("summary.txt", "r");
if (fp == NULL) {
perror("cannot open summary.txt");
exit(1);
}
int session;
char mode[64];
const char * separator = "_____________________________________________";
while (fscanf(fp, " session %d (%[^)]):", &session, mode) == 2) {
if (strcmp(mode, "COPY MODE") && strcmp(mode,"ONLINE FREE MODE"))
{
fprintf(stderr, "invalid file, invalid mode '%s'\n", mode);
exit(1);
}
char line[64];
char given[64];
char recognized[64];
int registration, registration_set = 0;
int sequences, sequences_set = 0;
for(;;) {
/* read next line, bypass possible newline(s) before */
if (fscanf(fp, " %63[^\n]", line) != 1) {
if (!feof(fp)) {
perror("reading file");
exit(1);
}
*line = 0;
}
else if (sscanf(line, "number of sequences: %d", &sequences) == 1) {
sequences_set = 1;
continue;
}
else if ((*given == 0) &&
(sscanf(line, "characters given: %63s", given) == 1)) {
if (!registration_set) {
fprintf(stderr,
"registration is missing before 'characters given: %s'\n",
given);
exit(1);
}
if (!sequences_set) {
fprintf(stderr,
"sequences is missing before 'characters given: %s'\n",
given);
exit(1);
}
continue;
}
else if ((*recognized == 0) &&
(sscanf(line, "characters recognized: %63s", recognized) == 1)) {
if (!*given) {
fprintf(stderr,
"given is missing before 'characters recognized: %s'\n",
recognized);
exit(1);
}
continue;
}
if (registration_set) {
if (!*given) {
fputs("invalid file, given is missing\n", stderr);
exit(1);
}
printf("%d %d %d %s %s\n", session, registration, sequences, given, recognized);
if (!*line || !strcmp(line, separator))
break;
}
if (sscanf(line, "registration %d:", &registration) != 1) {
fprintf(stderr,
"invalid file, 'registration <n>:' expected rather than '%s'\n",
line);
exit(1);
}
else {
registration_set = 1;
*given = *recognized = 0;
}
}
}
fclose(fp);
return 0;
}
Note the space at the beginning of the format of the fscanf, that allows to bypass character considered to be space including the newline
Compilation and execution :
pi#raspberrypi:/tmp $ gcc -Wall c.c
pi#raspberrypi:/tmp $ cat summary.txt
session 1 (COPY MODE):
number of sequences: 15
registration 1:
characters given: CALOR
registration 2:
characters given: CARINO
registration 3:
characters given: SUSHI
_____________________________________________
session 2 (COPY MODE):
registration 1:
number of sequences: 15
characters given: SUSHI
characters recognized: SUSHI
_____________________________________________
session 3 (ONLINE FREE MODE):
number of sequences: 15
registration 1:
characters given: PERA
characters recognized: PFRA
registration 2:
characters given: SALON
characters recognized: SALON
registration 3:
characters given: PERRO
characters recognized: PERRO
_____________________________________________
session 4 (ONLINE FREE MODE):
registration 1:
number of sequences: 7
characters given: TORTUGA
characters recognized: TORTUGA
registration 2:
number of sequences: 4
characters given: ANAEROBIO
characters recognized: ANAERPBIO
registration 3:
number of sequences: 4
characters given: PAPELES
characters recognized: PAPELEX
pi#raspberrypi:/tmp $ ./a.out
1 1 15 CALOR
1 2 15 CARINO
1 3 15 SUSHI
2 1 15 SUSHI SUSHI
3 1 15 PERA PFRA
3 2 15 SALON SALON
3 3 15 PERRO PERRO
4 1 7 TORTUGA TORTUGA
4 2 4 ANAEROBIO ANAERPBIO
4 3 4 PAPELES PAPELEX
pi#raspberrypi:/tmp $

Related

Extra characters and symbols outputted when doing substitution in C

When I run the code using the following key, extra characters are outputted...
TERMINAL WINDOW:
$ ./substitution abcdefghjklmnopqrsTUVWXYZI
plaintext: heTUXWVI ii ssTt
ciphertext: heUVYXWJ jj ttUuh|
This is the instructions (cs50 substitution problem)
Design and implement a program, substitution, that encrypts messages using a substitution cipher.
Implement your program in a file called substitution.c in a ~/pset2/substitution directory.
Your program must accept a single command-line argument, the key to use for the substitution. The key itself should be case-insensitive, so whether any character in the key is uppercase or lowercase should not affect the behavior of your program.
If your program is executed without any command-line arguments or with more than one command-line argument, your program should print an error message of your choice (with printf) and return from main a value of 1 (which tends to signify an error) immediately.
If the key is invalid (as by not containing 26 characters, containing any character that is not an alphabetic character, or not containing each letter exactly once), your program should print an error message of your choice (with printf) and return from main a value of 1 immediately.
Your program must output plaintext: (without a newline) and then prompt the user for a string of plaintext (using get_string).
Your program must output ciphertext: (without a newline) followed by the plaintext’s corresponding ciphertext, with each alphabetical character in the plaintext substituted for the corresponding character in the ciphertext; non-alphabetical characters should be outputted unchanged.
Your program must preserve case: capitalized letters must remain capitalized letters; lowercase letters must remain lowercase letters.
After outputting ciphertext, you should print a newline. Your program should then exit by returning 0 from main.
My code:
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int main(int argc,string argv[])
{
char alpha[26] = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};
string key = argv[1];
int totalchar = 0;
for (char c ='a'; c <= 'z'; c++)
{
for (int i = 0; i < strlen(key); i++)
{
if (tolower(key[i]) == c)
{
totalchar++;
}
}
}
//accept only singular 26 key
if (argc == 2 && totalchar == 26)
{
string plaint = get_string("plaintext: ");
int textlength =strlen(plaint);
char subchar[textlength];
for (int i= 0; i< textlength; i++)
{
for (int j =0; j<26; j++)
{
// substitute
if (tolower(plaint[i]) == alpha[j])
{
subchar[i] = tolower(key[j]);
// keep plaintext's case
if (plaint[i] >= 'A' && plaint[i] <= 'Z')
{
subchar[i] = (toupper(key[j]));
}
}
// if isn't char
if (!(isalpha(plaint[i])))
{
subchar[i] = plaint[i];
}
}
}
printf("ciphertext: %s\n", subchar);
return 0;
}
else
{
printf("invalid input\n");
return 1;
}
}
strcmp compares two strings. plaint[i] and alpha[j] are chars. The can be compared with "regular" comparison operators, like ==.

How do I use escapeshellarg() on Windows but "aimed for Linux" (and vice versa)?

If PHP is running on Windows, escapeshellarg() escapes file names (for example) in a certain way and then adds " (DOUBLE) quotes around it.
If PHP is running on Linux, escapeshellarg() uses Linux-based escaping and then adds ' (SINGLE) quotes around it.
In my situation, I'm generating a SHA256SUMS file on Windows, but aimed for Linux. Since I use escapeshellarg() to escape the file name, I end up with a file like:
cabcdccas12exdqdqadanacvdkjsc123ccfcfq3rdwcndwf2qefcf "cool filename with spaces.zip"
However, Linux tools probably expect:
cabcdccas12exdqdqadanacvdkjsc123ccfcfq3rdwcndwf2qefcf 'cool filename with spaces.zip'
Looking in the manual, there seems to be no way to do something like: escapeshellarg($blabla, TARGET_OS_LINUX); in order for it to use the rules for Linux instead of the OS running the script (Windows).
I can't just str_replace the quotes because it would not take into consideration all the platform-specific rules.
Also, yes, I need spaces in the file name (and any other cross-platform-valid character).
I sadly found no mention whatsoever about the preferred quote style on the only source of information I have for this: https://help.ubuntu.com/community/HowToSHA256SUM
Maybe the SHA256 security verification tools which read that SHA256SUMS file understand and can parse both kinds?
The behavior of escapeshellarg() is hard-coded depending on whether PHP is running on Windows or any other operating system. You should reimplement escapeshellarg() for consistent behavior.
Here is my attempt at reimplementing escapeshellarg() with a Windows/other-OS toggle in PHP:
<?php namespace polyfill;
const TARGET_OS_WINDOWS = 1;
const TARGET_OS_UNIX = 2;
function escapeshellarg(string $input, int $os_mode = 0): string
{
if (false !== strpos($input, "\x00"))
{
throw new \UnexpectedValueException(__FUNCTION__ . '(): Argument #1 ($input) must not contain any null bytes');
}
if ($os_mode == 0)
{
$os_mode = TARGET_OS_UNIX;
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN')
$os_mode = TARGET_OS_WINDOWS;
}
$maxlen = 4096;
if ($os_mode === TARGET_OS_WINDOWS) $maxlen = 8192;
if (strlen($input) > $maxlen - 2) return "";
if ($os_mode === TARGET_OS_WINDOWS)
{
$output =
str_replace(['"', '%', '!'],
[' ', ' ', ' '],
$input);
# https://bugs.php.net/bug.php?id=69646
if (substr($output, -1) === "\\")
{
$k = 0; $n = strlen($output) - 1;
for (; $n >= 0 && substr($output, $n, 1) === "\\"; $n--, $k++);
if ($k % 2) $output .= "\\";
}
$output = "\"$output\"";
}
else
{
$output = str_replace("'", "'\''", $input);
$output = "'$output'";
}
if (strlen($output) > $maxlen) return "";
return $output;
}
It should be almost functionally equivalent to the native PHP escapeshellarg(), except that:
it takes a second argument that sets whether you want the output in Windows mode or not Windows mode,
it raises an \UnexpectedValueException instead of some kind of PHP error if the input string contains null bytes,
it doesn't emit errors due to the input being too long, and
it has 4096 hard-coded as the maximum argument length on Unix-like platforms.
To use this replacement function:
# In Unix/Linux/macOS mode
\polyfill\escapeshellarg($blabla, \polyfill\TARGET_OS_UNIX);
# In Windows mode
\polyfill\escapeshellarg($blabla, \polyfill\TARGET_OS_WINDOWS);
# In auto-detect (running OS) mode
\polyfill\escapeshellarg($blabla);
Reference
Here is the full C implementation from PHP 7.3.10 (./ext/standard/exec.c):
PHPAPI zend_string *php_escape_shell_arg(char *str)
{
size_t x, y = 0;
size_t l = strlen(str);
zend_string *cmd;
uint64_t estimate = (4 * (uint64_t)l) + 3;
/* max command line length - two single quotes - \0 byte length */
if (l > cmd_max_len - 2 - 1) {
php_error_docref(NULL, E_ERROR, "Argument exceeds the allowed length of %zu bytes", cmd_max_len);
return ZSTR_EMPTY_ALLOC();
}
cmd = zend_string_safe_alloc(4, l, 2, 0); /* worst case */
#ifdef PHP_WIN32
ZSTR_VAL(cmd)[y++] = '"';
#else
ZSTR_VAL(cmd)[y++] = '\'';
#endif
for (x = 0; x < l; x++) {
int mb_len = php_mblen(str + x, (l - x));
/* skip non-valid multibyte characters */
if (mb_len < 0) {
continue;
} else if (mb_len > 1) {
memcpy(ZSTR_VAL(cmd) + y, str + x, mb_len);
y += mb_len;
x += mb_len - 1;
continue;
}
switch (str[x]) {
#ifdef PHP_WIN32
case '"':
case '%':
case '!':
ZSTR_VAL(cmd)[y++] = ' ';
break;
#else
case '\'':
ZSTR_VAL(cmd)[y++] = '\'';
ZSTR_VAL(cmd)[y++] = '\\';
ZSTR_VAL(cmd)[y++] = '\'';
#endif
/* fall-through */
default:
ZSTR_VAL(cmd)[y++] = str[x];
}
}
#ifdef PHP_WIN32
if (y > 0 && '\\' == ZSTR_VAL(cmd)[y - 1]) {
int k = 0, n = y - 1;
for (; n >= 0 && '\\' == ZSTR_VAL(cmd)[n]; n--, k++);
if (k % 2) {
ZSTR_VAL(cmd)[y++] = '\\';
}
}
ZSTR_VAL(cmd)[y++] = '"';
#else
ZSTR_VAL(cmd)[y++] = '\'';
#endif
ZSTR_VAL(cmd)[y] = '\0';
if (y > cmd_max_len + 1) {
php_error_docref(NULL, E_ERROR, "Escaped argument exceeds the allowed length of %zu bytes", cmd_max_len);
zend_string_release_ex(cmd, 0);
return ZSTR_EMPTY_ALLOC();
}
if ((estimate - y) > 4096) {
/* realloc if the estimate was way overill
* Arbitrary cutoff point of 4096 */
cmd = zend_string_truncate(cmd, y, 0);
}
ZSTR_LEN(cmd) = y;
return cmd;
}
// … [truncated] …
/* {{{ proto string escapeshellarg(string arg)
Quote and escape an argument for use in a shell command */
PHP_FUNCTION(escapeshellarg)
{
char *argument;
size_t argument_len;
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_STRING(argument, argument_len)
ZEND_PARSE_PARAMETERS_END();
if (argument) {
if (argument_len != strlen(argument)) {
php_error_docref(NULL, E_ERROR, "Input string contains NULL bytes");
return;
}
RETVAL_STR(php_escape_shell_arg(argument));
}
}
/* }}} */
The logic is fairly simple. Here are some equivalent functional test cases in prose:
The input string cannot contain NUL characters.
Applied to the input string,
in Windows mode,
Prepend a " character.
Replace all ", %, and ! characters with .
If the end consists of an odd number of \ characters, add one \ character to the end. (Bug #69646)
Append a " character.
in other platforms mode,
Prepend a ' character.
Replace all ' characters with '\''
Append a ' character.
On Windows, if the output is longer than 8192 characters, emit an E_ERROR and return an empty string.
On other platforms, if the output is longer than 4096 characters (or whatever the overridden maximum is at compile time), emit an E_ERROR and return an empty string.

scanning a line has strings and integer C

I have a project and im facing a problem in this code
void Show() {
system("cls");
FILE *AddedSc;
int sc1i,sc2i;
int sc1ii,sc2ii;
char TNi[100],TN2i[100];
char TNii[100],TN2ii[100];
AddedSc = fopen("addedscores.txt", "r");
printf(" - Choose teams from this list: \n\n");
printf(" 1. Brazil\n 2. Germany\n 3. Italy\n 4. KSA\n 5. Portogual\n 6. Australia\n 7. USA\n 8. Spin\n 9. England\n 10. Korea\n\n\n");
printf(" * Enter the two teams that you want to show their results: ");
printf("\n\n\nTeam A: ");
scanf("%s", TNi);
printf("Team B: ");
scanf("%s", TN2i);
while (fscanf(AddedSc,"%[^\n] %d %d %[^\n]", TNii, sc1ii, sc2ii,TN2ii) !=EOF) {
if (strcmp(TNi, TNii) == 0 && strcmp(TN2i, TN2ii) == 0)
printf("\n\n%s %d - %d %s", TNi, sc1ii, sc2ii, TN2i);
else if(strcmp(TN2i, TNii) == 0 && strcmp(TNi, TN2ii) == 0)
printf("\n\n%s %d - %d %s", TN2i, sc2ii, sc1ii, TNi);
else printf("Not found!");
}
fclose(AddedSc);
there is a file contains data like this
usa 2 0 italy
I want the user to enter the name of two teams and let the program to search in the file and compare what the user entered and print the result of the match on the screen ..
please help me in this....
The problem is the first format, %[^\n], which will eat up all the lines in the input. You can change it just %s as follows:
while (fscanf(AddedSc,"%s %d %d %s", TNii, sc1ii, sc2ii,TN2ii) != EOF)
And it will be safer to compare the result as:
while (fscanf(AddedSc,"%s %d %d %s", TNii, sc1ii, sc2ii,TN2ii) == 4)

How can I safely and simply read a line of text from a file or stdin?

Given that fgets only sometimes includes a linebreak, and fscanf is inherently unsafe, I would like a simple alternative to read text line-by-line from a file. Is this page a good place to find such a function?
Yes. The following function should satisfy this requirement without creating any damaging security flaws.
/* reads from [stream] into [buffer] until terminated by
* \r, \n or EOF, or [lastnullindex] is reached. Returns
* the number of characters read excluding the terminating
* character. [lastnullindex] refers to the uppermost index
* of the [buffer] array. If an error occurs or non-text
* characters (below space ' ' or above tilde '~') are
* detected, the buffer will be emptied and 0 returned.
*/
int readline(FILE *stream, char *buffer, int lastnullindex) {
if (!stream) return 0;
if (!buffer) return 0;
if (lastnullindex < 0) return 0;
int inch = EOF;
int chi = 0;
while (chi < lastnullindex) {
inch = fgetc(stream);
if (inch == EOF || inch == '\n' || inch == '\r') {
buffer[chi] = '\0';
break;
} else if (inch >= ' ' && inch <= '~') {
buffer[chi] = (char)inch;
chi++;
} else {
buffer[0] = '\0';
return 0;
}
}
if (chi < 0 || chi > lastnullindex) {
buffer[0] = '\0';
return 0;
} else {
buffer[chi] = '\0';
return chi;
}
}

linux terminal output

Hi I wrote a simple c prog to just accept a password while diplaying * to hide the input. But the * for the last character entered is not appearing at the right place.
the code is below
int main(){
int choice = 0;
char pass[8];
FILE *input;
FILE *output;
struct termios initial_settings, new_settings;
if(!isatty(fileno(stdout))){
fprintf(stderr,"Not a terminal \n");
}
input = fopen("/dev/tty","r");
output = fopen("/dev/tty","w");
if(!input || !output){
fprintf(stderr,"error opening");
exit(1);
}
tcgetattr(fileno(input),&initial_settings);
new_settings = initial_settings;
new_settings.c_lflag &= ~ICANON;
new_settings.c_lflag &= ~ECHO;
new_settings.c_cc[VMIN] = 1;
new_settings.c_cc[VTIME] = 0;
new_settings.c_lflag &= ~ISIG;
if(tcsetattr(fileno(input), TCSANOW, &new_settings) != 0) {
fprintf(stderr,"could not set attributes\n");
}
int count = 0;
char ch;
printf("Please enter the password: ");
while (count<8){
ch = fgetc(input);
if(ch == '\n' || ch == '\r'){
break;
}else{
fputc('*',stdout);
pass[count] = ch;
count++;
}
tcdrain(fileno(stdout));
}
fprintf(output,"you have entered :%s \n",pass);
tcsetattr(fileno(input),TCSANOW,&initial_settings);
exit(0);
}
The output is as follows:
Please enter the password:* * * * * * *
you have entered :12345678
* pasman#pasman-laptop:~$
Its an 8 character password & Notice that 7 *s appear as expected but the last * is appearing at the end of main.
You're mixing stdio and another stream, output, talking directly to the tty. They have different buffers, and get flushed at different times. You really should just use one of them.
It's because you break before you write the last *: so
add
fputc('*',stdout);
before
tcdrain(fileno(stdout));

Resources