Исправление firkax, (текущая версия) :
По-моему проще весь подобный изврат просто логировать как ошибки, ибо он однозначно вреден в любых необфусцированных исходниках. Все предыдущие случаи могли быть вполне реальными и уместными в коде. Но ладно, задача интересная как просто абстрактная теория. Вот:
/* (c) firk; feel free to use for anything */
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
static char * wise_gets(char const * * eoln_type, unsigned long long * p_ln) {
static char * buf;
static size_t bs, bp, bl;
static unsigned long long ln = 1;
static int eof;
size_t csz;
ssize_t rsz;
if(bp) {
bl -= bp;
if(bl) bcopy(buf+bp, buf, bl);
bp = 0;
}
if(eof && !bl) return NULL;
*p_ln = ln;
while(1) {
if(!eof) {
if(bl==bs) {
bs += 50 + bs/3;
if(bs<=bl) { fprintf(stderr, "line %llu too long // out of memory\n", ln); exit(-1); }
if(!buf) buf = malloc(bs); else buf = realloc(buf, bs);
if(!buf) { fprintf(stderr, "line %llu too long // out of memory\n", ln); exit(-1); }
}
csz = bs-bl;
if(((ssize_t)csz)<=0) csz/=2;
while((rsz = read(0, buf+bl, csz))<0) if(errno!=EINTR) { fprintf(stderr, "error reading input at line %llu, error %d (%s)\n", ln, errno, strerror(errno)); rsz = 0; break; }
if(!rsz) eof = 1; else bl += rsz;
}
for( ; bp<bl; bp++) {
if(buf[bp]!='\n') continue;
ln++;
if(!bp) { buf[bp++] = 0; *eoln_type = "\n"; return buf; }
if(buf[bp-1]=='\\') continue;
if(buf[bp-1]!='\r') { buf[bp++] = 0; *eoln_type = "\n"; return buf; }
if(bp==1 || buf[bp-2]!='\\') { buf[bp-1] = 0; bp++; *eoln_type = "\r\n"; return buf; }
}
if(eof) {
assert(bl<bs); /* eof results from failed read() so bs-bl>0 */
buf[bp] = 0; *eoln_type = NULL; return buf;
}
}
}
static void handle_line(char *b, char const *eoln, char const *def_eoln, char *pq, unsigned long ln, int warnq);
int main(int argc, char const * const * argv) {
char q;
unsigned long long ln;
int warnq;
char *b, *p;
char const *eoln, *def_eoln;
warnq = !(argc>=2 && !strcmp(argv[1],"--multistring"));
def_eoln = "\n";
q = 0;
while(b=wise_gets(&eoln,&ln)) {
if(eoln) def_eoln = eoln; else eoln = "";
handle_line(b, eoln, def_eoln, &q, ln, warnq);
}
if(q=='/') fprintf(stderr, "warning: unterminated multiline comment at EOF (last line is %lu)\n", ln);
else if(q) fprintf(stderr, "warning: unterminated quoted (%c) entity at EOF (last line is %lu)\n", q, ln);
return 0;
}
static char * skip_eolns(char * b) {
while(*b=='\\') {
if(b[1]=='\n') { b+=2; continue; }
if(b[1]=='\r' && b[2]=='\n') { b+=3; continue; }
break;
}
return b;
}
static void handle_line(char *b, char const *eoln, char const *def_eoln, char *pq, unsigned long ln, int warnq) {
size_t p, plen;
char c, q;
char q0;
char *bb;
for(p=0; (c=b[p])==' ' || c=='\t'; p++);
plen = p;
q = q0 = *pq;
while(c = b[p]) {
if(q=='/') {
p++;
if(c=='*' && *(bb=skip_eolns(b+p))=='/') { p = (bb-b)+1; q = 0; }
continue;
}
if(q) {
p++;
if(c==q) q=0;
else if(c=='\\') { if(b[p]) p++; }
continue;
}
if(c=='\'' || c=='"') { q=c; p++; continue; }
if(c=='/' && *(bb=skip_eolns(b+p+1))=='*') { q='/'; p=(bb-b)+1; continue; }
if(c!='/' || *(bb=skip_eolns(b+p+1))!='/') { p++; continue; }
/* found unquoted // */
if(q0) {
if(q0=='/') fprintf(stderr, "warning: can't safely reorder //-comment due to pending multiline-comment at line %lu beginning\n", ln);
else fprintf(stderr, "warning: can't safely reorder //-comment due to pending quoted (%c) entity at line %lu beginning\n", q, ln);
break;
}
/* here q0==0 && q==0 */
if(plen) fwrite(b, 1, plen, stdout);
fputs(b+p, stdout);
fputs(def_eoln, stdout);
while(p && (b[p-1]==' ' || b[p-1]=='\t')) p--;
if(p) { fwrite(b, 1, p, stdout); fputs(eoln, stdout); }
return;
}
/* found EOLN w/o // */
fputs(b, stdout); fputs(eoln, stdout);
*pq = q;
if(warnq && q && q!='/') fprintf(stderr, "warning: unterminated quoted (%c) entity at line %lu\n", q, ln);
}
Поддерживается \, поддерживаются строки любой длины на какую хватит памяти и нормально обрабатываются все errno из чтения. Правда я бы потестировал этот код на всякий случай на всяких разных данных.
А да, ещё проблема - если на входе встретится \0 то строка с ним будет обрезана на выходе.
Исправление firkax, :
По-моему проще весь подобный изврат просто логировать как ошибки, ибо он однозначно вреден в любых необфусцированных исходниках. Все предыдущие случаи могли быть вполне реальными и уместными в коде. Но ладно, задача интересная как просто абстрактная теория. Вот:
/* (c) firk; feel free to use for anything */
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
static char * wise_gets(char const * * eoln_type, unsigned long long * p_ln) {
static char * buf;
static size_t bs, bp, bl;
static unsigned long long ln = 1;
static int eof;
size_t csz;
ssize_t rsz;
if(bp) {
bl -= bp;
if(bl) bcopy(buf+bp, buf, bl);
bp = 0;
}
if(eof && !bl) return NULL;
*p_ln = ln;
while(1) {
if(!eof) {
if(bl==bs) {
bs += 50 + bs/3;
if(bs<=bl) { fprintf(stderr, "line %llu too long // out of memory\n", ln); exit(-1); }
if(!buf) buf = malloc(bs); else buf = realloc(buf, bs);
if(!buf) { fprintf(stderr, "line %llu too long // out of memory\n", ln); exit(-1); }
}
csz = bs-bl;
if(((ssize_t)csz)<=0) csz/=2;
while((rsz = read(0, buf+bl, csz))<0) if(errno!=EINTR) { fprintf(stderr, "error reading input at line %llu, error %d (%s)\n", ln, errno, strerror(errno)); rsz = 0; break; }
if(!rsz) eof = 1; else bl += rsz;
}
for( ; bp<bl; bp++) {
if(buf[bp]!='\n') continue;
ln++;
if(!bp) { buf[bp++] = 0; *eoln_type = "\n"; return buf; }
if(buf[bp-1]=='\\') continue;
if(buf[bp-1]!='\r') { buf[bp++] = 0; *eoln_type = "\n"; return buf; }
if(bp==1 || buf[bp-2]!='\\') { buf[bp-1] = 0; bp++; *eoln_type = "\r\n"; return buf; }
}
if(eof) {
assert(bl<bs); /* eof results from failed read() so bs-bl>0 */
buf[bp] = 0; *eoln_type = NULL; return buf;
}
}
}
static void handle_line(char *b, char const *eoln, char const *def_eoln, char *pq, unsigned long ln, int warnq);
int main(int argc, char const * const * argv) {
char q;
unsigned long long ln;
int warnq;
char *b, *p;
char const *eoln, *def_eoln;
warnq = !(argc>=2 && !strcmp(argv[1],"--multistring"));
def_eoln = "\n";
q = 0;
while(b=wise_gets(&eoln,&ln)) {
if(eoln) def_eoln = eoln; else eoln = "";
handle_line(b, eoln, def_eoln, &q, ln, warnq);
}
if(q=='/') fprintf(stderr, "warning: unterminated multiline comment at EOF (last line is %lu)\n", ln);
else if(q) fprintf(stderr, "warning: unterminated quoted (%c) entity at EOF (last line is %lu)\n", q, ln);
return 0;
}
static char * skip_eolns(char * b) {
while(*b=='\\') {
if(b[1]=='\n') { b+=2; continue; }
if(b[1]=='\r' && b[2]=='\n') { b+=3; continue; }
break;
}
return b;
}
static void handle_line(char *b, char const *eoln, char const *def_eoln, char *pq, unsigned long ln, int warnq) {
size_t p, plen;
char c, q;
char q0;
char *bb;
for(p=0; (c=b[p])==' ' || c=='\t'; p++);
plen = p;
q = q0 = *pq;
while(c = b[p]) {
if(q=='/') {
p++;
if(c=='*' && *(bb=skip_eolns(b+p))=='/') { p = (bb-b)+1; q = 0; }
continue;
}
if(q) {
p++;
if(c==q) q=0;
else if(c=='\\') { if(b[p]) p++; }
continue;
}
if(c=='\'' || c=='"') { q=c; p++; continue; }
if(c=='/' && *(bb=skip_eolns(b+p+1))=='*') { q='/'; p=(bb-b)+1; continue; }
if(c!='/' || *(bb=skip_eolns(b+p+1))!='/') { p++; continue; }
/* found unquoted // */
if(q0) {
if(q0=='/') fprintf(stderr, "warning: can't safely reorder //-comment due to pending multiline-comment at line %lu beginning\n", ln);
else fprintf(stderr, "warning: can't safely reorder //-comment due to pending quoted (%c) entity at line %lu beginning\n", q, ln);
break;
}
/* here q0==0 && q==0 */
if(plen) fwrite(b, 1, plen, stdout);
fputs(b+p, stdout);
fputs(def_eoln, stdout);
while(p && (b[p-1]==' ' || b[p-1]=='\t')) p--;
if(p) { fwrite(b, 1, p, stdout); fputs(eoln, stdout); }
return;
}
/* found EOLN w/o // */
fputs(b, stdout); fputs(eoln, stdout);
*pq = q;
if(warnq && q && q!='/') fprintf(stderr, "warning: unterminated quoted (%c) entity at line %lu\n", q, ln);
}
Поддерживается \, поддерживаются строки любой длины на какую хватит памяти и нормально обрабатываются все errno из чтения. Правда я бы потестировал этот код на всякий случай на всяких разных данных.
Исходная версия firkax, :
По-моему проще весь подобный изврат просто логировать как ошибки, ибо он однозначно вреден в любых необфусцированных исходниках. Все предыдущие случаи могли быть вполне реальными и уместными в коде. Но ладно, задача интересная как просто абстрактная теория. Вот:
/* (c) firk; feel free to use for anything */
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
static char * wise_gets(char const * * eoln_type, unsigned long long * p_ln) {
static char * buf;
static size_t bs, bp, bl;
static unsigned long long ln = 1;
static int eof;
size_t csz;
ssize_t rsz;
if(bp) {
bl -= bp;
if(bl) bcopy(buf+bp, buf, bl);
bp = 0;
}
if(eof && !bl) return NULL;
*p_ln = ln;
while(1) {
if(!eof) {
if(bl==bs) {
bs += 50 + bs/3;
if(bs<=bl) { fprintf(stderr, "line %llu too long // out of memory\n", ln); exit(-1); }
if(!buf) buf = malloc(bs); else buf = realloc(buf, bs);
if(!buf) { fprintf(stderr, "line %llu too long // out of memory\n", ln); exit(-1); }
}
csz = bs-bl;
if(((ssize_t)csz)<=0) csz/=2;
while((rsz = read(0, buf+bl, csz))<0) if(errno!=EINTR) { fprintf(stderr, "error reading input at line %llu, error %d (%s)\n", ln, errno, strerror(errno)); rsz = 0; break; }
if(!rsz) eof = 1; else bl += rsz;
}
for( ; bp<bl; bp++) {
if(buf[bp]!='\n') continue;
ln++;
if(!bp) { buf[bp++] = 0; *eoln_type = "\n"; return buf; }
if(buf[bp-1]=='\\') continue;
if(buf[bp-1]!='\r') { buf[bp++] = 0; *eoln_type = "\n"; return buf; }
if(bp==1 || buf[bp-2]!='\\') { buf[bp-1] = 0; bp++; *eoln_type = "\r\n"; return buf; }
}
if(eof) {
assert(bl<bs); /* eof results from failed read() so bs-bl>0 */
buf[bp] = 0; *eoln_type = NULL; return buf;
}
}
}
static void handle_line(char *b, char const *eoln, char const *def_eoln, char *pq, unsigned long ln, int warnq);
int main(int argc, char const * const * argv) {
char q;
unsigned long long ln;
int warnq;
char *b, *p;
char const *eoln, *def_eoln;
warnq = !(argc>=2 && !strcmp(argv[1],"--multistring"));
def_eoln = "\n";
q = 0;
while(b=wise_gets(&eoln,&ln)) {
if(eoln) def_eoln = eoln; else eoln = "";
handle_line(b, eoln, def_eoln, &q, ln, warnq);
}
if(q=='/') fprintf(stderr, "warning: unterminated multiline comment at EOF (last line is %lu)\n", ln);
else if(q) fprintf(stderr, "warning: unterminated quoted (%c) entity at EOF (last line is %lu)\n", q, ln);
return 0;
}
static char * skip_eolns(char * b) {
while(*b=='\\') {
if(b[1]=='\n') { b+=2; continue; }
if(b[1]=='\r' && b[2]=='\n') { b+=3; continue; }
break;
}
return b;
}
static void handle_line(char *b, char const *eoln, char const *def_eoln, char *pq, unsigned long ln, int warnq) {
size_t p, plen;
char c, q;
char q0;
char *bb;
for(p=0; (c=b[p])==' ' || c=='\t'; p++);
plen = p;
q = q0 = *pq;
while(c = b[p]) {
if(q=='/') {
p++;
if(c=='*' && *(bb=skip_eolns(b+p))=='/') { p = (bb-b)+1; q = 0; }
continue;
}
if(q) {
p++;
if(c==q) q=0;
else if(c=='\\') { if(b[p]) p++; }
continue;
}
if(c=='\'' || c=='"') { q=c; p++; continue; }
if(c=='/' && *(bb=skip_eolns(b+p+1))=='*') { q='/'; p=(bb-b)+1; continue; }
if(c!='/' || *(bb=skip_eolns(b+p+1))!='/') { p++; continue; }
/* found unquoted // */
if(q0) {
if(q0=='/') fprintf(stderr, "warning: can't safely reorder //-comment due to pending multiline-comment at line %lu beginning\n", ln);
else fprintf(stderr, "warning: can't safely reorder //-comment due to pending quoted (%c) entity at line %lu beginning\n", q, ln);
break;
}
/* here q0==0 && q==0 */
if(plen) fwrite(b, 1, plen, stdout);
fputs(b+p, stdout);
fputs(def_eoln, stdout);
while(p && (b[p-1]==' ' || b[p-1]=='\t')) p--;
if(p) { fwrite(b, 1, p, stdout); fputs(eoln, stdout); }
return;
}
/* found EOLN w/o // */
fputs(b, stdout); fputs(eoln, stdout);
*pq = q;
if(warnq && q && q!='/') fprintf(stderr, "warning: unterminated quoted (%c) entity at line %lu\n", q, ln);
}
Поддерживается \, поддерживаются строки любой длины на какую хватит памяти и нормально обрабатываются все errno из чтения. Правда я бы потестировал этот код на всякий случай на всяких разных данных на всякий случай.