Loading src/shell.cpp +45 −0 Changes for src/shell.cpp: 45 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -2290,6 +2290,33 @@ bool fresh::needsMoreInput(const std::string &text){ char c=text[p-1]; if(c==';' || c=='\n' || c=='|') return true; if(c=='&' && p>=2 && text[p-2]=='&') return true; // "if"/"elif"/"while" right before `pos` also opens a real command // position: their own CONDITION is a command like any other, and // can itself be a compound one, e.g. the kernel's own // scripts/setlocalversion: "if { git ... || git ...; } | read // dummy; then ...; fi". Without this, that "{" was never // recognized as an opener here (only after ';'/'\n'/'|'/"&&"), so // `depth` never actually reached 1 for it -- yet its own matching // "}" a few characters later (correctly ';'-preceded) still passed // this same check and decremented anyway, net-closing the "if" // itself far too early. That let needsMoreInput() -- and every // caller relying on it, from runScript()'s own top-level loop // through parse()'s recursive one -- believe the whole "if...fi" // was already complete right after "then", handing "echo ...", // "fi", and everything after to the shell as separate, unrelated, // top-level statements instead of one construct. Bounded to a // whole word (not e.g. "motif {") by requiring ITS OWN left side // to also land on a real boundary. { size_t wordEnd=p; size_t wordStart=wordEnd; while(wordStart>0 && text[wordStart-1]!=' ' && text[wordStart-1]!='\t' && text[wordStart-1]!=';' && text[wordStart-1]!='\n' && text[wordStart-1]!='|') --wordStart; std::string word=text.substr(wordStart,wordEnd-wordStart); if(word=="if" || word=="elif" || word=="while") return true; } return false; }; Loading Loading @@ -3661,9 +3688,27 @@ int fresh::Shell::parse(const std::string &input,bool applyErrexit){ // at all, and every ';'/'&&'/'||' inside the group -- starting // with the very first one -- was wrongly treated as a top-level // segment boundary instead. // "if"/"elif"/"while" right before this word also opens a real // command position -- their own CONDITION is a command like any // other, and can itself be a compound one, e.g. the kernel's own // scripts/setlocalversion: "if { git ... || git ...; } | read // dummy; then". Without this, that "{" was never recognized as an // opener at all (only "|"/"!" and the segment-start position // were), yet its OWN matching "}" right after the ";" INSIDE the // group still satisfied the ";"-preceded check below and // decremented `depth` anyway -- desyncing the count by one and // making the outer "if"'s own ";" before "then" look like a // top-level statement separator instead of still being inside the // if. Splitting the input right there is exactly the "if: expected // 'then'" / "then: command not found" / "fi: command not found" // failure this caused: "then"/"fi" ran as their own bogus, // unrelated top-level commands instead of ever reaching // IfCommand/WhileCommand at all. bool atStmtStart=(i==segstart || words[i-1].text==";" || words[i-1].text=="&&" || words[i-1].text=="||" || words[i-1].text=="|" || words[i-1].text=="if" || words[i-1].text=="elif" || words[i-1].text=="while" || (i==segstart+1 && words[segstart].text=="!")); if(atStmtStart && (w=="if" || w=="for" || w=="while" || w=="case" || w=="{")) ++depth; Loading Loading
src/shell.cpp +45 −0 Changes for src/shell.cpp: 45 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -2290,6 +2290,33 @@ bool fresh::needsMoreInput(const std::string &text){ char c=text[p-1]; if(c==';' || c=='\n' || c=='|') return true; if(c=='&' && p>=2 && text[p-2]=='&') return true; // "if"/"elif"/"while" right before `pos` also opens a real command // position: their own CONDITION is a command like any other, and // can itself be a compound one, e.g. the kernel's own // scripts/setlocalversion: "if { git ... || git ...; } | read // dummy; then ...; fi". Without this, that "{" was never // recognized as an opener here (only after ';'/'\n'/'|'/"&&"), so // `depth` never actually reached 1 for it -- yet its own matching // "}" a few characters later (correctly ';'-preceded) still passed // this same check and decremented anyway, net-closing the "if" // itself far too early. That let needsMoreInput() -- and every // caller relying on it, from runScript()'s own top-level loop // through parse()'s recursive one -- believe the whole "if...fi" // was already complete right after "then", handing "echo ...", // "fi", and everything after to the shell as separate, unrelated, // top-level statements instead of one construct. Bounded to a // whole word (not e.g. "motif {") by requiring ITS OWN left side // to also land on a real boundary. { size_t wordEnd=p; size_t wordStart=wordEnd; while(wordStart>0 && text[wordStart-1]!=' ' && text[wordStart-1]!='\t' && text[wordStart-1]!=';' && text[wordStart-1]!='\n' && text[wordStart-1]!='|') --wordStart; std::string word=text.substr(wordStart,wordEnd-wordStart); if(word=="if" || word=="elif" || word=="while") return true; } return false; }; Loading Loading @@ -3661,9 +3688,27 @@ int fresh::Shell::parse(const std::string &input,bool applyErrexit){ // at all, and every ';'/'&&'/'||' inside the group -- starting // with the very first one -- was wrongly treated as a top-level // segment boundary instead. // "if"/"elif"/"while" right before this word also opens a real // command position -- their own CONDITION is a command like any // other, and can itself be a compound one, e.g. the kernel's own // scripts/setlocalversion: "if { git ... || git ...; } | read // dummy; then". Without this, that "{" was never recognized as an // opener at all (only "|"/"!" and the segment-start position // were), yet its OWN matching "}" right after the ";" INSIDE the // group still satisfied the ";"-preceded check below and // decremented `depth` anyway -- desyncing the count by one and // making the outer "if"'s own ";" before "then" look like a // top-level statement separator instead of still being inside the // if. Splitting the input right there is exactly the "if: expected // 'then'" / "then: command not found" / "fi: command not found" // failure this caused: "then"/"fi" ran as their own bogus, // unrelated top-level commands instead of ever reaching // IfCommand/WhileCommand at all. bool atStmtStart=(i==segstart || words[i-1].text==";" || words[i-1].text=="&&" || words[i-1].text=="||" || words[i-1].text=="|" || words[i-1].text=="if" || words[i-1].text=="elif" || words[i-1].text=="while" || (i==segstart+1 && words[segstart].text=="!")); if(atStmtStart && (w=="if" || w=="for" || w=="while" || w=="case" || w=="{")) ++depth; Loading