diff options
| author | Andreas Fankhauser hiddenalpha.ch | 2026-03-02 01:35:32 +0100 |
|---|---|---|
| committer | Andreas Fankhauser hiddenalpha.ch | 2026-03-02 01:35:32 +0100 |
| commit | 9eb0f859ad53cadec82144c2cda57205e77b4682 (patch) | |
| tree | 67de3897cc3e06f9c3ad6b6ee8d570e936ac316b /src/quotprint.c | |
| parent | e28111ce46c7bf771c72ec02184cbb34e4aa7086 (diff) | |
| download | quotprint-9eb0f859ad53cadec82144c2cda57205e77b4682.tar.gz quotprint-9eb0f859ad53cadec82144c2cda57205e77b4682.zip | |
impl a decoder (WARN: basic functionality only)
Diffstat (limited to 'src/quotprint.c')
| -rw-r--r-- | src/quotprint.c | 177 |
1 files changed, 159 insertions, 18 deletions
diff --git a/src/quotprint.c b/src/quotprint.c index c5c7719..dbb06cc 100644 --- a/src/quotprint.c +++ b/src/quotprint.c @@ -39,7 +39,7 @@ #define STR_QUOT(S) STR_QUOT_(S) -typedef struct FalseSharingBarrier { void* _[64/sizeof(void*)]; } FalseSharingBarrier; +typedef struct FalseSharingBarrier { char _[64]; } FalseSharingBarrier; typedef struct Buf { @@ -255,7 +255,7 @@ mainOfInputThread( void*_ ){ } -INLINE(void*) +PRIVATE(void*) mainOfEncode( void*_ ){ DEFINE_App(this, _); int err, iSrc = 0, iDst = 0, lineBeg = 0; @@ -294,7 +294,9 @@ mainOfEncode( void*_ ){ goto onDstBufFull; } for(;;){ - if( iDst + 3 >= DST_CAP ) + /* MUST have some headroom at end so we can properly handle + * special-cases. */ + if( iDst + 8 >= DST_CAP ) { goto onDstBufFull; } if( iSrc >= LEN_SRC ) { goto onSrcBufDrained; } @@ -302,12 +304,11 @@ mainOfEncode( void*_ ){ #define IS_WHITESP(C) (C == 0x09 || C == 0x0A || C == 0x0D || C == 0x20) #define NEXT_SRC_CHR (BUF_SRC[iSrc]) #define IS_NEXT_CHR_PRESENT (iSrc + 1 < LEN_SRC) - #define IS_DST_SPACE_AT_LEAST(N) (iDst + (N) < DST_CAP) #define IS_BINARY_MODE ((this->flg & FLG_isBinary)==0) #define IS_EBCDIC_RISKY(C) (C == 0x21 || C == 0x23 || C == 0x24 || C == 0x34 \ || C == 0x40 || C == 0x5B || C == 0x5C || C == 0x5D || C == 0x5E || C == 0x60 \ || C == 0x7B || C == 0x7C || C == 0x7D || C == 0x7E) - int const isCrLfRaw = (c == 0x0D && IS_NEXT_CHR_PRESENT && IS_DST_SPACE_AT_LEAST(2) + int const isCrLfRaw = (c == 0x0D && IS_NEXT_CHR_PRESENT && NEXT_SRC_CHR == 0x0A && IS_BINARY_MODE); int const isRawOk = 0 /* A CRLF sequence if a special case, as it CAN be @@ -325,8 +326,7 @@ mainOfEncode( void*_ ){ * mode given, as I do NOT want to introduce yet-another-bullshit-flag*/ || (IS_EBCDIC_RISKY(c) && !IS_BINARY_MODE) /*space MUST NOT be at endOfLine, so check some more conditions*/ - || (c == 0x20 && IS_NEXT_CHR_PRESENT && IS_DST_SPACE_AT_LEAST(4) - && !IS_WHITESP(NEXT_SRC_CHR)) + || (c == 0x20 && IS_NEXT_CHR_PRESENT && !IS_WHITESP(NEXT_SRC_CHR)) ; #undef IS_WHITESP #undef NEXT_SRC_CHR @@ -382,7 +382,7 @@ mainOfEncode( void*_ ){ }onSrcBufDrained:{ err = pthread_mutex_lock(&this->mutxSrc); assert(!err); while( this->srcBufReadyToFill ) - { pthread_cond_wait(&this->condSrc, &this->mutxSrc); } + { err = pthread_cond_wait(&this->condSrc, &this->mutxSrc); assert(!err); } this->srcBufReadyToFill = BUF_SRC; LEN_SRC = 0; BUF_SRC = NULL; @@ -415,12 +415,156 @@ mainOfEncode( void*_ ){ } -INLINE(int) -quotPrintDecode( App*this ){ - assert(!"TODO_i5TKHBVqBa0lRhqy"); - /* Cited from RFC: when decoding a Quoted-Printable body, any - * trailing white space on a line must be deleted, as it will - * necessarily have been added by intermediate transport agents */ +PRIVATE(void*) +mainOfDecode( void*_ ){ + int err, iSrc = 0, iDst = 0; + DEFINE_App(this, _); + { + #define BUF_DST (this->dstBufReadyToFill) + #define DST_CAP BUF_CAP + #define LEN_DST (*(int*)(((uintptr_t)0) \ + + ((uintptr_t)(BUF_DST == this->dstBufOne.buf) * (uintptr_t)&this->dstBufOne.len) \ + + ((uintptr_t)(BUF_DST != this->dstBufOne.buf) * (uintptr_t)&this->dstBufTwo.len) \ + )) + #define BUF_SRC (this->srcBufReadyToConsume) + #define SRC_CAP BUF_CAP + #define LEN_SRC (*(int*)(((uintptr_t)0) \ + + ((uintptr_t)(BUF_SRC == this->srcBufOne.buf) * (uintptr_t)&this->srcBufOne.len) \ + + ((uintptr_t)(BUF_SRC == this->srcBufTwo.buf) * (uintptr_t)&this->srcBufTwo.len) \ + )) + }awaitBuffers:{ + LOGD("decoder - await DST buf\n"); + err = pthread_mutex_lock(&this->mutxDst); assert(!err); + while( !BUF_DST ) + pthread_cond_wait(&this->condDst, &this->mutxDst); + err = pthread_mutex_unlock(&this->mutxDst); assert(!err); + }{ + LOGD("decoder - await SRC buf\n"); + err = pthread_mutex_lock(&this->mutxSrc); assert(!err); + while( !BUF_SRC ) + pthread_cond_wait(&this->condSrc, &this->mutxSrc); + err = pthread_mutex_unlock(&this->mutxSrc); assert(!err); + }{ + LOGD("decoder - both buffers ready\n"); + if( BUF_SRC == &endOfFile ){ + /* input has ended. we MUST ensure that we really flush what + * we maybe still have in dst. So assert dst is full and handle it + * there. */ + /* TODO should we ensure that output always ends with CRLF? */ + goto onDstBufFull; + } + }{ + for(;;){ + if( iDst + 1 >= DST_CAP ) + { goto onDstBufFull; } + if( iSrc >= LEN_SRC ) + { goto onSrcBufDrained; } + uint_fast8_t const CHR0 = BUF_SRC[iSrc + 0]; + #define CHR1 ((uint_fast8_t)BUF_SRC[iSrc + 1]) + #define CHR2 ((uint_fast8_t)BUF_SRC[iSrc + 2]) + #define IS_HEX(C) (0 \ + || ((C) >= '0' && (C) <= '9') \ + || ((C) >= 'A' && (C) <= 'F') \ + || ((C) >= 'a' && (C) <= 'f') \ + ) + #define IS_WHITESP(C) (C == 0x09 || C == 0x0A || C == 0x0D || C == 0x20) + int const isSoftWrap = (CHR0 == '=' && (CHR1 == '\r' || CHR1 == '\n')); + if( isSoftWrap ){ + if( CHR1 == '\n' ){ iSrc += 2; } + else if( CHR1 == '\r' && CHR2 == '\n' ){ iSrc += 3; } + else if( CHR1 == '\r' ){ iSrc += 2; } + continue; + } + // TODO /* Cited from RFC: + // TODO * > when decoding a Quoted-Printable body, any trailing white space on + // TODO * > a line must be deleted, as it will necessarily have been added by + // TODO * > intermediate transport agents. + // TODO * TODO: Below impl does not work when that whitespace crosses a buffer + // TODO * boundary. */ + // TODO int trailSpsEnd; + // TODO for( trailSpsEnd = iSrc ;; ++trailSpsEnd){ + // TODO if( trailSpsEnd >= LEN_SRC ) assert(!"TODO_wSiBu8mbRajrnTZp"); + // TODO if( BUF_SRC[iSrc] == ); + // TODO } + if( CHR0 != '=' ){ + /*just a normal char*/ + BUF_DST[iDst++] = CHR0; + iSrc += 1; + continue; + } + if( !IS_HEX(CHR1) || !IS_HEX(CHR2) ){ + /*corrupt garbage. TODO: Should be handled with warning*/ + BUF_DST[iDst++] = CHR0; + BUF_DST[iDst++] = CHR1; + BUF_DST[iDst++] = CHR2; + iSrc += 3; + continue; + } + /* decode from escape */ + #define NIBL_OF_HEX_CHR(R, X) do{ \ + uint_fast8_t c = (X); \ + if( c >= '0' && c <= '9' ){ (R) = c - '0'; break; } \ + if( c >= 'A' && c <= 'F' ){ (R) = c - 'A' + 10; break; } \ + if( c >= 'a' && c <= 'f' ){ (R) = c - 'a' + 10; break; } \ + assert(!"TODO_KwZPRb3XfwLIGOyq"); \ + }while(0) + uint_fast8_t nibHi; NIBL_OF_HEX_CHR(nibHi, CHR1); + uint_fast8_t nibLo; NIBL_OF_HEX_CHR(nibLo, CHR2); + BUF_DST[iDst++] = (nibHi << 4) | nibLo; + iSrc += 3; + #undef CHR1 + #undef CHR2 + #undef IS_HEX + #undef IS_WHITESP + #undef NIBL_OF_HEX_CHR + } + }onSrcBufDrained:{ + err = pthread_mutex_lock(&this->mutxSrc); assert(!err); + while( this->srcBufReadyToFill ) + { err = pthread_cond_wait(&this->condSrc, &this->mutxSrc); assert(!err); } + this->srcBufReadyToFill = BUF_SRC; + LEN_SRC = 0; + BUF_SRC = NULL; + err = pthread_cond_broadcast(&this->condSrc); assert(!err); + err = pthread_mutex_unlock(&this->mutxSrc); assert(!err); + goto awaitBuffers; + }onDstBufFull:{ + /* hand over dst buf as soon consumer is ready to take it */ + err = pthread_mutex_lock(&this->mutxDst); assert(!err); + while( this->dstBufReadyToFlush ) + { err = pthread_cond_wait(&this->condDst, &this->mutxDst); assert(!err); } + LOGD("decoder - dstBufReadyToFlush := dstBuf%s\n", + (BUF_DST==this->dstBufOne.buf)?"One":"Two"); + LOGD("decoder - dstBuf%s.len := %d\n", (BUF_DST==this->dstBufOne.buf)?"One":"Two", iDst); + LOGD("decoder - dstBuf%s := NULL\n", (BUF_DST==this->dstBufOne.buf)?"One":"Two"); + this->dstBufReadyToFlush = BUF_DST; + LEN_DST = iDst; + BUF_DST = NULL; + err = pthread_cond_broadcast(&this->condDst); assert(!err); + if( BUF_SRC == &endOfFile ){ + /* src eof, so mark our eof too (as soon prev buffer is consumed) */ + while( this->dstBufReadyToFlush ) + { err = pthread_cond_wait(&this->condDst, &this->mutxDst); assert(!err); } + LOGD("decoder - src did report EOF. Therefore report EOF too\n"); + this->dstBufReadyToFlush = &endOfFile; + err = pthread_cond_broadcast(&this->condDst); assert(!err); + err = pthread_mutex_unlock(&this->mutxDst); assert(!err); + err = 0; goto resolveWithErr; + }else{ + err = pthread_mutex_unlock(&this->mutxDst); assert(!err); + } + goto awaitBuffers; + }resolveWithErr:{ + assert(err == 0); + return NULL; + }{ + #undef BUF_DST + #undef DST_CAP + #undef LEN_DST + #undef BUF_SRC + #undef SRC_CAP + #undef LEN_SRC + } } @@ -440,10 +584,7 @@ run( App*this ){ err = -err; goto resolveWithErr; } }/*launch a thread for processing*/{ - void *(*processor)(void*) = (this->flg & FLG_isEnc) - ? mainOfEncode - : NULL //TODO mainOfDecode - ; + void *(*processor)(void*) = (this->flg & FLG_isEnc) ? mainOfEncode : mainOfDecode; err = pthread_create(&this->thrdWr, NULL, processor, this); if( err ){ assert(err > 0); |
