@@ -11,6 +11,8 @@ mod decl {
1111 } ;
1212 use itertools:: Itertools ;
1313
14+ const MAXLINESIZE : usize = 76 ;
15+
1416 #[ pyattr( name = "Error" , once) ]
1517 fn error_type ( vm : & VirtualMachine ) -> PyTypeRef {
1618 vm. ctx . new_exception_type (
@@ -176,13 +178,285 @@ mod decl {
176178 // The 64 instead of the expected 63 is because
177179 // there are a few uuencodes out there that use
178180 // '`' as zero instead of space.
179- if !( 0x20 ..=0x60 ) . contains ( c) {
181+ if !( b' ' ..=( b' ' + 64 ) ) . contains ( c) {
180182 if [ b'\r' , b'\n' ] . contains ( c) {
181183 return Ok ( 0 ) ;
182184 }
183185 return Err ( vm. new_value_error ( "Illegal char" . to_string ( ) ) ) ;
184186 }
185- Ok ( ( * c - 0x20 ) & 0x3f )
187+ Ok ( ( * c - b' ' ) & 0x3f )
188+ }
189+
190+ #[ derive( FromArgs ) ]
191+ struct A2bQpArgs {
192+ #[ pyarg( any) ]
193+ data : ArgAsciiBuffer ,
194+ #[ pyarg( named, default = "false" ) ]
195+ header : bool ,
196+ }
197+ #[ pyfunction]
198+ fn a2b_qp ( args : A2bQpArgs ) -> PyResult < Vec < u8 > > {
199+ let s = args. data ;
200+ let header = args. header ;
201+ s. with_ref ( |buffer| {
202+ let len = buffer. len ( ) ;
203+ let mut out_data = Vec :: with_capacity ( len) ;
204+
205+ let mut idx = 0 ;
206+
207+ while idx < len {
208+ if buffer[ idx] == b'=' {
209+ idx += 1 ;
210+ if idx >= len {
211+ break ;
212+ }
213+ // Soft line breaks
214+ if ( buffer[ idx] == b'\n' ) || ( buffer[ idx] == b'\r' ) {
215+ if buffer[ idx] != b'\n' {
216+ while idx < len && buffer[ idx] != b'\n' {
217+ idx += 1 ;
218+ }
219+ }
220+ if idx < len {
221+ idx += 1 ;
222+ }
223+ } else if buffer[ idx] == b'=' {
224+ // roken case from broken python qp
225+ out_data. push ( b'=' ) ;
226+ idx += 1 ;
227+ } else if idx + 1 < len
228+ && ( ( buffer[ idx] >= b'A' && buffer[ idx] <= b'F' )
229+ || ( buffer[ idx] >= b'a' && buffer[ idx] <= b'f' )
230+ || ( buffer[ idx] >= b'0' && buffer[ idx] <= b'9' ) )
231+ && ( ( buffer[ idx + 1 ] >= b'A' && buffer[ idx + 1 ] <= b'F' )
232+ || ( buffer[ idx + 1 ] >= b'a' && buffer[ idx + 1 ] <= b'f' )
233+ || ( buffer[ idx + 1 ] >= b'0' && buffer[ idx + 1 ] <= b'9' ) )
234+ {
235+ // hexval
236+ if let ( Some ( ch1) , Some ( ch2) ) =
237+ ( unhex_nibble ( buffer[ idx] ) , unhex_nibble ( buffer[ idx + 1 ] ) )
238+ {
239+ out_data. push ( ch1 << 4 | ch2) ;
240+ }
241+ idx += 2 ;
242+ } else {
243+ out_data. push ( b'=' ) ;
244+ }
245+ } else if header && buffer[ idx] == b'_' {
246+ out_data. push ( b' ' ) ;
247+ idx += 1 ;
248+ } else {
249+ out_data. push ( buffer[ idx] ) ;
250+ idx += 1 ;
251+ }
252+ }
253+
254+ Ok ( out_data)
255+ } )
256+ }
257+
258+ #[ derive( FromArgs ) ]
259+ struct B2aQpArgs {
260+ #[ pyarg( any) ]
261+ data : ArgAsciiBuffer ,
262+ #[ pyarg( named, default = "false" ) ]
263+ quotetabs : bool ,
264+ #[ pyarg( named, default = "true" ) ]
265+ istext : bool ,
266+ #[ pyarg( named, default = "false" ) ]
267+ header : bool ,
268+ }
269+
270+ #[ pyfunction]
271+ fn b2a_qp ( args : B2aQpArgs ) -> PyResult < Vec < u8 > > {
272+ let s = args. data ;
273+ let quotetabs = args. quotetabs ;
274+ let istext = args. istext ;
275+ let header = args. header ;
276+ s. with_ref ( |buf| {
277+ let buflen = buf. len ( ) ;
278+ let mut linelen = 0 ;
279+ let mut odatalen = 0 ;
280+ let mut crlf = false ;
281+ let mut ch;
282+
283+ let mut inidx;
284+ let mut outidx;
285+
286+ inidx = 0 ;
287+ while inidx < buflen {
288+ if buf[ inidx] == b'\n' {
289+ break ;
290+ }
291+ inidx += 1 ;
292+ }
293+ if buflen > 0 && inidx < buflen && buf[ inidx - 1 ] == b'\r' {
294+ crlf = true ;
295+ }
296+
297+ inidx = 0 ;
298+ while inidx < buflen {
299+ let mut delta = 0 ;
300+ if ( buf[ inidx] > 126 )
301+ || ( buf[ inidx] == b'=' )
302+ || ( header && buf[ inidx] == b'_' )
303+ || ( buf[ inidx] == b'.'
304+ && linelen == 0
305+ && ( inidx + 1 == buflen
306+ || buf[ inidx + 1 ] == b'\n'
307+ || buf[ inidx + 1 ] == b'\r'
308+ || buf[ inidx + 1 ] == 0 ) )
309+ || ( !istext && ( ( buf[ inidx] == b'\r' ) || ( buf[ inidx] == b'\n' ) ) )
310+ || ( ( buf[ inidx] == b'\t' || buf[ inidx] == b' ' ) && ( inidx + 1 == buflen) )
311+ || ( ( buf[ inidx] < 33 )
312+ && ( buf[ inidx] != b'\r' )
313+ && ( buf[ inidx] != b'\n' )
314+ && ( quotetabs || ( ( buf[ inidx] != b'\t' ) && ( buf[ inidx] != b' ' ) ) ) )
315+ {
316+ if ( linelen + 3 ) >= MAXLINESIZE {
317+ linelen = 0 ;
318+ delta += if crlf { 3 } else { 2 } ;
319+ }
320+ linelen += 3 ;
321+ delta += 3 ;
322+ inidx += 1 ;
323+ } else if istext
324+ && ( ( buf[ inidx] == b'\n' )
325+ || ( ( inidx + 1 < buflen)
326+ && ( buf[ inidx] == b'\r' )
327+ && ( buf[ inidx + 1 ] == b'\n' ) ) )
328+ {
329+ linelen = 0 ;
330+ // Protect against whitespace on end of line
331+ if ( inidx != 0 ) && ( ( buf[ inidx - 1 ] == b' ' ) || ( buf[ inidx - 1 ] == b'\t' ) ) {
332+ delta += 2 ;
333+ }
334+ delta += if crlf { 2 } else { 1 } ;
335+ inidx += if buf[ inidx] == b'\r' { 2 } else { 1 } ;
336+ } else {
337+ if ( inidx + 1 != buflen)
338+ && ( buf[ inidx + 1 ] != b'\n' )
339+ && ( linelen + 1 ) >= MAXLINESIZE
340+ {
341+ linelen = 0 ;
342+ delta += if crlf { 3 } else { 2 } ;
343+ }
344+ linelen += 1 ;
345+ delta += 1 ;
346+ inidx += 1 ;
347+ }
348+ odatalen += delta;
349+ }
350+
351+ let mut out_data = Vec :: with_capacity ( odatalen) ;
352+ inidx = 0 ;
353+ outidx = 0 ;
354+ linelen = 0 ;
355+
356+ while inidx < buflen {
357+ if ( buf[ inidx] > 126 )
358+ || ( buf[ inidx] == b'=' )
359+ || ( header && buf[ inidx] == b'_' )
360+ || ( ( buf[ inidx] == b'.' )
361+ && ( linelen == 0 )
362+ && ( inidx + 1 == buflen
363+ || buf[ inidx + 1 ] == b'\n'
364+ || buf[ inidx + 1 ] == b'\r'
365+ || buf[ inidx + 1 ] == 0 ) )
366+ || ( !istext && ( ( buf[ inidx] == b'\r' ) || ( buf[ inidx] == b'\n' ) ) )
367+ || ( ( buf[ inidx] == b'\t' || buf[ inidx] == b' ' ) && ( inidx + 1 == buflen) )
368+ || ( ( buf[ inidx] < 33 )
369+ && ( buf[ inidx] != b'\r' )
370+ && ( buf[ inidx] != b'\n' )
371+ && ( quotetabs || ( ( buf[ inidx] != b'\t' ) && ( buf[ inidx] != b' ' ) ) ) )
372+ {
373+ if ( linelen + 3 ) >= MAXLINESIZE {
374+ // MAXLINESIZE = 76
375+ out_data. push ( b'=' ) ;
376+ outidx += 1 ;
377+ if crlf {
378+ out_data. push ( b'\r' ) ;
379+ outidx += 1 ;
380+ }
381+ out_data. push ( b'\n' ) ;
382+ outidx += 1 ;
383+ linelen = 0 ;
384+ }
385+ out_data. push ( b'=' ) ;
386+ outidx += 1 ;
387+
388+ ch = hex_nibble ( buf[ inidx] >> 4 ) ;
389+ if ( b'a' ..=b'f' ) . contains ( & ch) {
390+ ch -= b' ' ;
391+ }
392+ out_data. push ( ch) ;
393+ ch = hex_nibble ( buf[ inidx] & 0xf ) ;
394+ if ( b'a' ..=b'f' ) . contains ( & ch) {
395+ ch -= b' ' ;
396+ }
397+ out_data. push ( ch) ;
398+
399+ outidx += 2 ;
400+ inidx += 1 ;
401+ linelen += 3 ;
402+ } else if istext
403+ && ( ( buf[ inidx] == b'\n' )
404+ || ( ( inidx + 1 < buflen)
405+ && ( buf[ inidx] == b'\r' )
406+ && ( buf[ inidx + 1 ] == b'\n' ) ) )
407+ {
408+ linelen = 0 ;
409+ if ( outidx != 0 )
410+ && ( ( out_data[ outidx - 1 ] == b' ' ) || ( out_data[ outidx - 1 ] == b'\t' ) )
411+ {
412+ ch = hex_nibble ( out_data[ outidx - 1 ] >> 4 ) ;
413+ if ( b'a' ..=b'f' ) . contains ( & ch) {
414+ ch -= b' ' ;
415+ }
416+ out_data. push ( ch) ;
417+ ch = hex_nibble ( out_data[ outidx - 1 ] & 0xf ) ;
418+ if ( b'a' ..=b'f' ) . contains ( & ch) {
419+ ch -= b' ' ;
420+ }
421+ out_data. push ( ch) ;
422+ out_data[ outidx - 1 ] = b'=' ;
423+ outidx += 2 ;
424+ }
425+
426+ if crlf {
427+ out_data. push ( b'\r' ) ;
428+ outidx += 1 ;
429+ }
430+ out_data. push ( b'\n' ) ;
431+ outidx += 1 ;
432+ inidx += if buf[ inidx] == b'\r' { 2 } else { 1 } ;
433+ } else {
434+ if ( inidx + 1 != buflen) && ( buf[ inidx + 1 ] != b'\n' ) && ( linelen + 1 ) >= 76 {
435+ // MAXLINESIZE = 76
436+ out_data. push ( b'=' ) ;
437+ outidx += 1 ;
438+ if crlf {
439+ out_data. push ( b'\r' ) ;
440+ outidx += 1 ;
441+ }
442+ out_data. push ( b'\n' ) ;
443+ outidx += 1 ;
444+ linelen = 0 ;
445+ }
446+ linelen += 1 ;
447+ if header && buf[ inidx] == b' ' {
448+ out_data. push ( b'_' ) ;
449+ outidx += 1 ;
450+ inidx += 1 ;
451+ } else {
452+ out_data. push ( buf[ inidx] ) ;
453+ outidx += 1 ;
454+ inidx += 1 ;
455+ }
456+ }
457+ }
458+ Ok ( out_data)
459+ } )
186460 }
187461
188462 #[ pyfunction]
@@ -259,7 +533,7 @@ mod decl {
259533 let length = if b. is_empty ( ) {
260534 ( ( -0x20i32 ) & 0x3fi32 ) as usize
261535 } else {
262- ( ( b[ 0 ] - 0x20 ) & 0x3f ) as usize
536+ ( ( b[ 0 ] - b' ' ) & 0x3f ) as usize
263537 } ;
264538
265539 // Allocate the buffer
@@ -316,7 +590,7 @@ mod decl {
316590 if backtick && num != 0 {
317591 0x60
318592 } else {
319- 0x20 + num
593+ b' ' + num
320594 }
321595 }
322596
0 commit comments