    inline T median(T val0, T val1, T val2, T val3, T val4, T val5, T val6,
                    T val7, T val8, T val9, T val10, T val11, T val12, T val13,
                    T val14, T val15, T val16, T val17, T val18, T val19, T val20,
                    T val21, T val22, T val23, T val24, T val25, T val26, T val27,
                    T val28, T val29, T val30, T val31, T val32, T val33, T val34,
                    T val35, T val36, T val37, T val38, T val39, T val40, T val41,
                    T val42, T val43, T val44, T val45, T val46, T val47, T val48) {
      T tmp = std::min(val0,val32);
      val32 = std::max(val0,val32); val0 = tmp; tmp = std::min(val1,val33); val33 = std::max(val1,val33); val1 = tmp;
      tmp = std::min(val2,val34); val34 = std::max(val2,val34); val2 = tmp; tmp = std::min(val3,val35);
      val35 = std::max(val3,val35); val3 = tmp; tmp = std::min(val4,val36); val36 = std::max(val4,val36); val4 = tmp;
      tmp = std::min(val5,val37); val37 = std::max(val5,val37); val5 = tmp; tmp = std::min(val6,val38);
      val38 = std::max(val6,val38); val6 = tmp; tmp = std::min(val7,val39); val39 = std::max(val7,val39); val7 = tmp;
      tmp = std::min(val8,val40); val40 = std::max(val8,val40); val8 = tmp; tmp = std::min(val9,val41);
      val41 = std::max(val9,val41); val9 = tmp; tmp = std::min(val10,val42); val42 = std::max(val10,val42);
      val10 = tmp; tmp = std::min(val11,val43); val43 = std::max(val11,val43); val11 = tmp;
      tmp = std::min(val12,val44); val44 = std::max(val12,val44); val12 = tmp; tmp = std::min(val13,val45);
      val45 = std::max(val13,val45); val13 = tmp; tmp = std::min(val14,val46); val46 = std::max(val14,val46);
      val14 = tmp; tmp = std::min(val15,val47); val47 = std::max(val15,val47); val15 = tmp;
      tmp = std::min(val16,val48); val48 = std::max(val16,val48); val16 = tmp; tmp = std::min(val0,val16);
      val16 = std::max(val0,val16); val0 = tmp; tmp = std::min(val1,val17); val17 = std::max(val1,val17);
      val1 = tmp; tmp = std::min(val2,val18); val18 = std::max(val2,val18); val2 = tmp; tmp = std::min(val3,val19);
      val19 = std::max(val3,val19); val3 = tmp; tmp = std::min(val4,val20); val20 = std::max(val4,val20); val4 = tmp;
      tmp = std::min(val5,val21); val21 = std::max(val5,val21); val5 = tmp; tmp = std::min(val6,val22);
      val22 = std::max(val6,val22); val6 = tmp; tmp = std::min(val7,val23); val23 = std::max(val7,val23); val7 = tmp;
      tmp = std::min(val8,val24); val24 = std::max(val8,val24); val8 = tmp; tmp = std::min(val9,val25);
      val25 = std::max(val9,val25); val9 = tmp; tmp = std::min(val10,val26); val26 = std::max(val10,val26);
      val10 = tmp; tmp = std::min(val11,val27); val27 = std::max(val11,val27); val11 = tmp;
      tmp = std::min(val12,val28); val28 = std::max(val12,val28); val12 = tmp; tmp = std::min(val13,val29);
      val29 = std::max(val13,val29); val13 = tmp; tmp = std::min(val14,val30); val30 = std::max(val14,val30);
      val14 = tmp; tmp = std::min(val15,val31); val31 = std::max(val15,val31); val15 = tmp;
      tmp = std::min(val32,val48); val48 = std::max(val32,val48); val32 = tmp; tmp = std::min(val16,val32);
      val32 = std::max(val16,val32); val16 = tmp; tmp = std::min(val17,val33); val33 = std::max(val17,val33);
      val17 = tmp; tmp = std::min(val18,val34); val34 = std::max(val18,val34); val18 = tmp;
      tmp = std::min(val19,val35); val35 = std::max(val19,val35); val19 = tmp; tmp = std::min(val20,val36);
      val36 = std::max(val20,val36); val20 = tmp; tmp = std::min(val21,val37); val37 = std::max(val21,val37);
      val21 = tmp; tmp = std::min(val22,val38); val38 = std::max(val22,val38); val22 = tmp;
      tmp = std::min(val23,val39); val39 = std::max(val23,val39); val23 = tmp; tmp = std::min(val24,val40);
      val40 = std::max(val24,val40); val24 = tmp; tmp = std::min(val25,val41); val41 = std::max(val25,val41);
      val25 = tmp; tmp = std::min(val26,val42); val42 = std::max(val26,val42); val26 = tmp;
      tmp = std::min(val27,val43); val43 = std::max(val27,val43); val27 = tmp; tmp = std::min(val28,val44);
      val44 = std::max(val28,val44); val28 = tmp; tmp = std::min(val29,val45); val45 = std::max(val29,val45);
      val29 = tmp; tmp = std::min(val30,val46); val46 = std::max(val30,val46); val30 = tmp;
      tmp = std::min(val31,val47); val47 = std::max(val31,val47); val31 = tmp; tmp = std::min(val0,val8);
      val8 = std::max(val0,val8); val0 = tmp; tmp = std::min(val1,val9); val9 = std::max(val1,val9); val1 = tmp;
      tmp = std::min(val2,val10); val10 = std::max(val2,val10); val2 = tmp; tmp = std::min(val3,val11);
      val11 = std::max(val3,val11); val3 = tmp; tmp = std::min(val4,val12); val12 = std::max(val4,val12); val4 = tmp;
      tmp = std::min(val5,val13); val13 = std::max(val5,val13); val5 = tmp; tmp = std::min(val6,val14);
      val14 = std::max(val6,val14); val6 = tmp; tmp = std::min(val7,val15); val15 = std::max(val7,val15); val7 = tmp;
      tmp = std::min(val16,val24); val24 = std::max(val16,val24); val16 = tmp; tmp = std::min(val17,val25);
      val25 = std::max(val17,val25); val17 = tmp; tmp = std::min(val18,val26); val26 = std::max(val18,val26);
      val18 = tmp; tmp = std::min(val19,val27); val27 = std::max(val19,val27); val19 = tmp;
      tmp = std::min(val20,val28); val28 = std::max(val20,val28); val20 = tmp; tmp = std::min(val21,val29);
      val29 = std::max(val21,val29); val21 = tmp; tmp = std::min(val22,val30); val30 = std::max(val22,val30);
      val22 = tmp; tmp = std::min(val23,val31); val31 = std::max(val23,val31); val23 = tmp;
      tmp = std::min(val32,val40); val40 = std::max(val32,val40); val32 = tmp; tmp = std::min(val33,val41);
      val41 = std::max(val33,val41); val33 = tmp; tmp = std::min(val34,val42); val42 = std::max(val34,val42);
      val34 = tmp; tmp = std::min(val35,val43); val43 = std::max(val35,val43); val35 = tmp;
      tmp = std::min(val36,val44); val44 = std::max(val36,val44); val36 = tmp; tmp = std::min(val37,val45);
      val45 = std::max(val37,val45); val37 = tmp; tmp = std::min(val38,val46); val46 = std::max(val38,val46);
      val38 = tmp; tmp = std::min(val39,val47); val47 = std::max(val39,val47); val39 = tmp;
      tmp = std::min(val8,val32); val32 = std::max(val8,val32); val8 = tmp; tmp = std::min(val9,val33);
      val33 = std::max(val9,val33); val9 = tmp; tmp = std::min(val10,val34); val34 = std::max(val10,val34);
      val10 = tmp; tmp = std::min(val11,val35); val35 = std::max(val11,val35); val11 = tmp;
      tmp = std::min(val12,val36); val36 = std::max(val12,val36); val12 = tmp; tmp = std::min(val13,val37);
      val37 = std::max(val13,val37); val13 = tmp; tmp = std::min(val14,val38); val38 = std::max(val14,val38);
      val14 = tmp; tmp = std::min(val15,val39); val39 = std::max(val15,val39); val15 = tmp;
      tmp = std::min(val24,val48); val48 = std::max(val24,val48); val24 = tmp; tmp = std::min(val8,val16);
      val16 = std::max(val8,val16); val8 = tmp; tmp = std::min(val9,val17); val17 = std::max(val9,val17);
      val9 = tmp; tmp = std::min(val10,val18); val18 = std::max(val10,val18); val10 = tmp;
      tmp = std::min(val11,val19); val19 = std::max(val11,val19); val11 = tmp; tmp = std::min(val12,val20);
      val20 = std::max(val12,val20); val12 = tmp; tmp = std::min(val13,val21); val21 = std::max(val13,val21);
      val13 = tmp; tmp = std::min(val14,val22); val22 = std::max(val14,val22); val14 = tmp;
      tmp = std::min(val15,val23); val23 = std::max(val15,val23); val15 = tmp; tmp = std::min(val24,val32);
      val32 = std::max(val24,val32); val24 = tmp; tmp = std::min(val25,val33); val33 = std::max(val25,val33);
      val25 = tmp; tmp = std::min(val26,val34); val34 = std::max(val26,val34); val26 = tmp;
      tmp = std::min(val27,val35); val35 = std::max(val27,val35); val27 = tmp; tmp = std::min(val28,val36);
      val36 = std::max(val28,val36); val28 = tmp; tmp = std::min(val29,val37); val37 = std::max(val29,val37);
      val29 = tmp; tmp = std::min(val30,val38); val38 = std::max(val30,val38); val30 = tmp;
      tmp = std::min(val31,val39); val39 = std::max(val31,val39); val31 = tmp; tmp = std::min(val40,val48);
      val48 = std::max(val40,val48); val40 = tmp; tmp = std::min(val0,val4); val4 = std::max(val0,val4);
      val0 = tmp; tmp = std::min(val1,val5); val5 = std::max(val1,val5); val1 = tmp; tmp = std::min(val2,val6);
      val6 = std::max(val2,val6); val2 = tmp; tmp = std::min(val3,val7); val7 = std::max(val3,val7); val3 = tmp;
      tmp = std::min(val8,val12); val12 = std::max(val8,val12); val8 = tmp; tmp = std::min(val9,val13);
      val13 = std::max(val9,val13); val9 = tmp; tmp = std::min(val10,val14); val14 = std::max(val10,val14);
      val10 = tmp; tmp = std::min(val11,val15); val15 = std::max(val11,val15); val11 = tmp;
      tmp = std::min(val16,val20); val20 = std::max(val16,val20); val16 = tmp; tmp = std::min(val17,val21);
      val21 = std::max(val17,val21); val17 = tmp; tmp = std::min(val18,val22); val22 = std::max(val18,val22);
      val18 = tmp; tmp = std::min(val19,val23); val23 = std::max(val19,val23); val19 = tmp;
      tmp = std::min(val24,val28); val28 = std::max(val24,val28); val24 = tmp; tmp = std::min(val25,val29);
      val29 = std::max(val25,val29); val25 = tmp; tmp = std::min(val26,val30); val30 = std::max(val26,val30);
      val26 = tmp; tmp = std::min(val27,val31); val31 = std::max(val27,val31); val27 = tmp;
      tmp = std::min(val32,val36); val36 = std::max(val32,val36); val32 = tmp; tmp = std::min(val33,val37);
      val37 = std::max(val33,val37); val33 = tmp; tmp = std::min(val34,val38); val38 = std::max(val34,val38);
      val34 = tmp; tmp = std::min(val35,val39); val39 = std::max(val35,val39); val35 = tmp;
      tmp = std::min(val40,val44); val44 = std::max(val40,val44); val40 = tmp; tmp = std::min(val41,val45);
      val45 = std::max(val41,val45); val41 = tmp; tmp = std::min(val42,val46); val46 = std::max(val42,val46);
      val42 = tmp; tmp = std::min(val43,val47); val47 = std::max(val43,val47); val43 = tmp;
      tmp = std::min(val4,val32); val32 = std::max(val4,val32); val4 = tmp; tmp = std::min(val5,val33);
      val33 = std::max(val5,val33); val5 = tmp; tmp = std::min(val6,val34); val34 = std::max(val6,val34);
      val6 = tmp; tmp = std::min(val7,val35); val35 = std::max(val7,val35); val7 = tmp;
      tmp = std::min(val12,val40); val40 = std::max(val12,val40); val12 = tmp; tmp = std::min(val13,val41);
      val41 = std::max(val13,val41); val13 = tmp; tmp = std::min(val14,val42); val42 = std::max(val14,val42);
      val14 = tmp; tmp = std::min(val15,val43); val43 = std::max(val15,val43); val15 = tmp;
      tmp = std::min(val20,val48); val48 = std::max(val20,val48); val20 = tmp; tmp = std::min(val4,val16);
      val16 = std::max(val4,val16); val4 = tmp; tmp = std::min(val5,val17); val17 = std::max(val5,val17);
      val5 = tmp; tmp = std::min(val6,val18); val18 = std::max(val6,val18); val6 = tmp;
      tmp = std::min(val7,val19); val19 = std::max(val7,val19); val7 = tmp; tmp = std::min(val12,val24);
      val24 = std::max(val12,val24); val12 = tmp; tmp = std::min(val13,val25); val25 = std::max(val13,val25);
      val13 = tmp; tmp = std::min(val14,val26); val26 = std::max(val14,val26); val14 = tmp;
      tmp = std::min(val15,val27); val27 = std::max(val15,val27); val15 = tmp; tmp = std::min(val20,val32);
      val32 = std::max(val20,val32); val20 = tmp; tmp = std::min(val21,val33); val33 = std::max(val21,val33);
      val21 = tmp; tmp = std::min(val22,val34); val34 = std::max(val22,val34); val22 = tmp;
      tmp = std::min(val23,val35); val35 = std::max(val23,val35); val23 = tmp; tmp = std::min(val28,val40);
      val40 = std::max(val28,val40); val28 = tmp; tmp = std::min(val29,val41); val41 = std::max(val29,val41);
      val29 = tmp; tmp = std::min(val30,val42); val42 = std::max(val30,val42); val30 = tmp;
      tmp = std::min(val31,val43); val43 = std::max(val31,val43); val31 = tmp; tmp = std::min(val36,val48);
      val48 = std::max(val36,val48); val36 = tmp; tmp = std::min(val4,val8); val8 = std::max(val4,val8);
      val4 = tmp; tmp = std::min(val5,val9); val9 = std::max(val5,val9); val5 = tmp; tmp = std::min(val6,val10);
      val10 = std::max(val6,val10); val6 = tmp; tmp = std::min(val7,val11); val11 = std::max(val7,val11); val7 = tmp;
      tmp = std::min(val12,val16); val16 = std::max(val12,val16); val12 = tmp; tmp = std::min(val13,val17);
      val17 = std::max(val13,val17); val13 = tmp; tmp = std::min(val14,val18); val18 = std::max(val14,val18);
      val14 = tmp; tmp = std::min(val15,val19); val19 = std::max(val15,val19); val15 = tmp;
      tmp = std::min(val20,val24); val24 = std::max(val20,val24); val20 = tmp; tmp = std::min(val21,val25);
      val25 = std::max(val21,val25); val21 = tmp; tmp = std::min(val22,val26); val26 = std::max(val22,val26);
      val22 = tmp; tmp = std::min(val23,val27); val27 = std::max(val23,val27); val23 = tmp;
      tmp = std::min(val28,val32); val32 = std::max(val28,val32); val28 = tmp; tmp = std::min(val29,val33);
      val33 = std::max(val29,val33); val29 = tmp; tmp = std::min(val30,val34); val34 = std::max(val30,val34);
      val30 = tmp; tmp = std::min(val31,val35); val35 = std::max(val31,val35); val31 = tmp;
      tmp = std::min(val36,val40); val40 = std::max(val36,val40); val36 = tmp; tmp = std::min(val37,val41);
      val41 = std::max(val37,val41); val37 = tmp; tmp = std::min(val38,val42); val42 = std::max(val38,val42);
      val38 = tmp; tmp = std::min(val39,val43); val43 = std::max(val39,val43); val39 = tmp;
      tmp = std::min(val44,val48); val48 = std::max(val44,val48); val44 = tmp; tmp = std::min(val0,val2);
      val2 = std::max(val0,val2); val0 = tmp; tmp = std::min(val1,val3); val3 = std::max(val1,val3); val1 = tmp;
      tmp = std::min(val4,val6); val6 = std::max(val4,val6); val4 = tmp; tmp = std::min(val5,val7);
      val7 = std::max(val5,val7); val5 = tmp; tmp = std::min(val8,val10); val10 = std::max(val8,val10); val8 = tmp;
      tmp = std::min(val9,val11); val11 = std::max(val9,val11); val9 = tmp; tmp = std::min(val12,val14);
      val14 = std::max(val12,val14); val12 = tmp; tmp = std::min(val13,val15); val15 = std::max(val13,val15);
      val13 = tmp; tmp = std::min(val16,val18); val18 = std::max(val16,val18); val16 = tmp;
      tmp = std::min(val17,val19); val19 = std::max(val17,val19); val17 = tmp; tmp = std::min(val20,val22);
      val22 = std::max(val20,val22); val20 = tmp; tmp = std::min(val21,val23); val23 = std::max(val21,val23);
      val21 = tmp; tmp = std::min(val24,val26); val26 = std::max(val24,val26); val24 = tmp;
      tmp = std::min(val25,val27); val27 = std::max(val25,val27); val25 = tmp; tmp = std::min(val28,val30);
      val30 = std::max(val28,val30); val28 = tmp; tmp = std::min(val29,val31); val31 = std::max(val29,val31);
      val29 = tmp; tmp = std::min(val32,val34); val34 = std::max(val32,val34); val32 = tmp;
      tmp = std::min(val33,val35); val35 = std::max(val33,val35); val33 = tmp; tmp = std::min(val36,val38);
      val38 = std::max(val36,val38); val36 = tmp; tmp = std::min(val37,val39); val39 = std::max(val37,val39);
      val37 = tmp; tmp = std::min(val40,val42); val42 = std::max(val40,val42); val40 = tmp;
      tmp = std::min(val41,val43); val43 = std::max(val41,val43); val41 = tmp; tmp = std::min(val44,val46);
      val46 = std::max(val44,val46); val44 = tmp; tmp = std::min(val45,val47); val47 = std::max(val45,val47);
      val45 = tmp; tmp = std::min(val2,val32); val32 = std::max(val2,val32); val2 = tmp; tmp = std::min(val3,val33);
      val33 = std::max(val3,val33); val3 = tmp; tmp = std::min(val6,val36); val36 = std::max(val6,val36); val6 = tmp;
      tmp = std::min(val7,val37); val37 = std::max(val7,val37); val7 = tmp; tmp = std::min(val10,val40);
      val40 = std::max(val10,val40); val10 = tmp; tmp = std::min(val11,val41); val41 = std::max(val11,val41);
      val11 = tmp; tmp = std::min(val14,val44); val44 = std::max(val14,val44); val14 = tmp;
      tmp = std::min(val15,val45); val45 = std::max(val15,val45); val15 = tmp; tmp = std::min(val18,val48);
      val48 = std::max(val18,val48); val18 = tmp; tmp = std::min(val2,val16); val16 = std::max(val2,val16);
      val2 = tmp; tmp = std::min(val3,val17); val17 = std::max(val3,val17); val3 = tmp;
      tmp = std::min(val6,val20); val20 = std::max(val6,val20); val6 = tmp; tmp = std::min(val7,val21);
      val21 = std::max(val7,val21); val7 = tmp; tmp = std::min(val10,val24); val24 = std::max(val10,val24);
      val10 = tmp; tmp = std::min(val11,val25); val25 = std::max(val11,val25); val11 = tmp;
      tmp = std::min(val14,val28); val28 = std::max(val14,val28); val14 = tmp; tmp = std::min(val15,val29);
      val29 = std::max(val15,val29); val15 = tmp; tmp = std::min(val18,val32); val32 = std::max(val18,val32);
      val18 = tmp; tmp = std::min(val19,val33); val33 = std::max(val19,val33); val19 = tmp;
      tmp = std::min(val22,val36); val36 = std::max(val22,val36); val22 = tmp; tmp = std::min(val23,val37);
      val37 = std::max(val23,val37); val23 = tmp; tmp = std::min(val26,val40); val40 = std::max(val26,val40);
      val26 = tmp; tmp = std::min(val27,val41); val41 = std::max(val27,val41); val27 = tmp;
      tmp = std::min(val30,val44); val44 = std::max(val30,val44); val30 = tmp; tmp = std::min(val31,val45);
      val45 = std::max(val31,val45); val31 = tmp; tmp = std::min(val34,val48); val48 = std::max(val34,val48);
      val34 = tmp; tmp = std::min(val2,val8); val8 = std::max(val2,val8); val2 = tmp; tmp = std::min(val3,val9);
      val9 = std::max(val3,val9); val3 = tmp; tmp = std::min(val6,val12); val12 = std::max(val6,val12); val6 = tmp;
      tmp = std::min(val7,val13); val13 = std::max(val7,val13); val7 = tmp; tmp = std::min(val10,val16);
      val16 = std::max(val10,val16); val10 = tmp; tmp = std::min(val11,val17); val17 = std::max(val11,val17);
      val11 = tmp; tmp = std::min(val14,val20); val20 = std::max(val14,val20); val14 = tmp;
      tmp = std::min(val15,val21); val21 = std::max(val15,val21); val15 = tmp; tmp = std::min(val18,val24);
      val24 = std::max(val18,val24); val18 = tmp; tmp = std::min(val19,val25); val25 = std::max(val19,val25);
      val19 = tmp; tmp = std::min(val22,val28); val28 = std::max(val22,val28); val22 = tmp;
      tmp = std::min(val23,val29); val29 = std::max(val23,val29); val23 = tmp; tmp = std::min(val26,val32);
      val32 = std::max(val26,val32); val26 = tmp; tmp = std::min(val27,val33); val33 = std::max(val27,val33);
      val27 = tmp; tmp = std::min(val30,val36); val36 = std::max(val30,val36); val30 = tmp;
      tmp = std::min(val31,val37); val37 = std::max(val31,val37); val31 = tmp; tmp = std::min(val34,val40);
      val40 = std::max(val34,val40); val34 = tmp; tmp = std::min(val35,val41); val41 = std::max(val35,val41);
      val35 = tmp; tmp = std::min(val38,val44); val44 = std::max(val38,val44); val38 = tmp;
      tmp = std::min(val39,val45); val45 = std::max(val39,val45); val39 = tmp; tmp = std::min(val42,val48);
      val48 = std::max(val42,val48); val42 = tmp; tmp = std::min(val2,val4); val4 = std::max(val2,val4);
      val2 = tmp; tmp = std::min(val3,val5); val5 = std::max(val3,val5); val3 = tmp; tmp = std::min(val6,val8);
      val8 = std::max(val6,val8); val6 = tmp; tmp = std::min(val7,val9); val9 = std::max(val7,val9); val7 = tmp;
      tmp = std::min(val10,val12); val12 = std::max(val10,val12); val10 = tmp; tmp = std::min(val11,val13);
      val13 = std::max(val11,val13); val11 = tmp; tmp = std::min(val14,val16); val16 = std::max(val14,val16);
      val14 = tmp; tmp = std::min(val15,val17); val17 = std::max(val15,val17); val15 = tmp;
      tmp = std::min(val18,val20); val20 = std::max(val18,val20); val18 = tmp; tmp = std::min(val19,val21);
      val21 = std::max(val19,val21); val19 = tmp; tmp = std::min(val22,val24); val24 = std::max(val22,val24);
      val22 = tmp; tmp = std::min(val23,val25); val25 = std::max(val23,val25); val23 = tmp;
      tmp = std::min(val26,val28); val28 = std::max(val26,val28); val26 = tmp; tmp = std::min(val27,val29);
      val29 = std::max(val27,val29); val27 = tmp; tmp = std::min(val30,val32); val32 = std::max(val30,val32);
      val30 = tmp; tmp = std::min(val31,val33); val33 = std::max(val31,val33); val31 = tmp;
      tmp = std::min(val34,val36); val36 = std::max(val34,val36); val34 = tmp; tmp = std::min(val35,val37);
      val37 = std::max(val35,val37); val35 = tmp; tmp = std::min(val38,val40); val40 = std::max(val38,val40);
      val38 = tmp; tmp = std::min(val39,val41); val41 = std::max(val39,val41); val39 = tmp;
      tmp = std::min(val42,val44); val44 = std::max(val42,val44); val42 = tmp; tmp = std::min(val43,val45);
      val45 = std::max(val43,val45); val43 = tmp; tmp = std::min(val46,val48); val48 = std::max(val46,val48);
      val46 = tmp; val1 = std::max(val0,val1); val3 = std::max(val2,val3); val5 = std::max(val4,val5);
      val7 = std::max(val6,val7); val9 = std::max(val8,val9); val11 = std::max(val10,val11);
      val13 = std::max(val12,val13); val15 = std::max(val14,val15); val17 = std::max(val16,val17);
      val19 = std::max(val18,val19); val21 = std::max(val20,val21); val23 = std::max(val22,val23);
      val24 = std::min(val24,val25); val26 = std::min(val26,val27); val28 = std::min(val28,val29);
      val30 = std::min(val30,val31); val32 = std::min(val32,val33); val34 = std::min(val34,val35);
      val36 = std::min(val36,val37); val38 = std::min(val38,val39); val40 = std::min(val40,val41);
      val42 = std::min(val42,val43); val44 = std::min(val44,val45); val46 = std::min(val46,val47);
      val32 = std::max(val1,val32); val34 = std::max(val3,val34); val36 = std::max(val5,val36);
      val38 = std::max(val7,val38); val9 = std::min(val9,val40); val11 = std::min(val11,val42);
      val13 = std::min(val13,val44); val15 = std::min(val15,val46); val17 = std::min(val17,val48);
      val24 = std::max(val9,val24); val26 = std::max(val11,val26); val28 = std::max(val13,val28);
      val30 = std::max(val15,val30); val17 = std::min(val17,val32); val19 = std::min(val19,val34);
      val21 = std::min(val21,val36); val23 = std::min(val23,val38); val24 = std::max(val17,val24);
      val26 = std::max(val19,val26); val21 = std::min(val21,val28); val23 = std::min(val23,val30);
      val24 = std::max(val21,val24); val23 = std::min(val23,val26);
      return std::max(val23,val24);
    }


    CImg<T>& draw_triangle(int x0, int y0, const float z0,
                           int x1, int y1, const float z1,
                           int x2, int y2, const float z2,
                           const CImg<tc>& texture,
                           int tx0, int ty0,
                           int tx1, int ty1,
                           int tx2, int ty2,
                           const float opacity=1,
                           const float brightness=1) {
      if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
      if (texture._depth>1 || texture._spectrum<_spectrum)
        throw CImgArgumentException(_cimg_instance
                                    "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
                                    cimg_instance,
                                    texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
      if (is_overlapped(texture))
        return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness);

      float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2;
      if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,tx0,tx1,ty0,ty1);
      if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,tx0,tx2,ty0,ty2);
      if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,tx1,tx2,ty1,ty2);
      if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;

      const int
        w1 = width() - 1, h1 = height() - 1,
        dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
        dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
        cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
        hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
      const float
        diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1,
        txz0 = tx0*iz0, txz1 = tx1*iz1, txz2 = tx2*iz2,
        tyz0 = ty0*iz0, tyz1 = ty1*iz1, tyz2 = ty2*iz2,
        dtxz01 = txz1 - txz0, dtxz02 = txz2 - txz0, dtxz12 = txz2 - txz1,
        dtyz01 = tyz1 - tyz0, dtyz02 = tyz2 - tyz0, dtyz12 = tyz2 - tyz1;

      const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth;
      const float cbs = cimg::cut(brightness,0,2);
      cimg_init_scanline(opacity);

      for (int y = cy0; y<=cy2; ++y) {
        const int yy0 = y - y0, yy1 = y - y1;
        int
          xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
          xM = x0 + (dx02*yy0 + hdy02)/dy02;
        float
          izm = y<y1?(iz0 + diz01*yy0/dy01):(iz1 + diz12*yy1/dy12),
          izM = iz0 + diz02*yy0/dy02,
          txzm = y<y1?(txz0 + dtxz01*yy0/dy01):(txz1 + dtxz12*yy1/dy12),
          txzM = txz0 + dtxz02*yy0/dy02,
          tyzm = y<y1?(tyz0 + dtyz01*yy0/dy01):(tyz1 + dtyz12*yy1/dy12),
          tyzM = tyz0 + dtyz02*yy0/dy02;
        if (xm>xM) cimg::swap(xm,xM,txzm,txzM,tyzm,tyzM,izm,izM);
        if (xM>=0 && xm<=w1) {
          const int
            cxm = cimg::cut(xm,0,w1),
            cxM = cimg::cut(xM,0,w1);
          T *ptrd = data(cxm,y);
          const int dxmM = std::max(1,xM - xm);
          const float dizmM = izM - izm, dtxzmM = txzM - txzm, dtyzmM = tyzM - tyzm;

          for (int x = cxm; x<=cxM; ++x) {
            const int xxm = x - xm;
            const float
              iz = izm + dizmM*xxm/dxmM,
              txz = txzm + dtxzmM*xxm/dxmM,
              tyz = tyzm + dtyzmM*xxm/dxmM;
            const int
              tx = (int)cimg::round(txz/iz),
              ty = (int)cimg::round(tyz/iz);
            const tc *const color = &texture._atXY(tx,ty);
            cimg_forC(*this,c) {
              const Tfloat val = cbs<=1?color[c*twhd]*cbs:(2 - cbs)*color[c*twhd] + (cbs - 1)*_sc_maxval;
              ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
            }
            ++ptrd;
          }
        }
      }
      return *this;
    }


    bool is_sameY(const CImgDisplay& disp) const {
      return is_sameY(disp._height);
    }


    CImg<Tfloat> get_blur_patch(const float sigma_s, const float sigma_r, const unsigned int patch_size=3,
                                const unsigned int lookup_size=4, const float smoothness=0,
                                const bool is_fast_approx=true) const {
      return get_blur_patch(*this,sigma_s,sigma_r,patch_size,lookup_size,smoothness,is_fast_approx);
    }


      static double mp_list_set_Joff_v(_cimg_math_parser& mp) {
        if (!mp.imglist.width()) return cimg::type<double>::nan();
        const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
        CImg<T> &img = mp.imglist[ind];
        const int
          ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
          oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
        const longT
          off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(3),
          whd = (longT)img.width()*img.height()*img.depth();
        const double *ptrs = &_mp_arg(1) + 1;
        if (off>=0 && off<whd) {
          const unsigned int vsiz = (unsigned int)mp.opcode[4];
          T *ptrd = &img[off];
          cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
        }
        return cimg::type<double>::nan();
      }


    inline const char* basename(const char *const s, const char separator=cimg_file_separator)  {
      const char *p = 0, *np = s;
      while (np>=s && (p=np)) np = std::strchr(np,separator) + 1;
      return p;
    }


    CImg<T> get_max(const T& value) const {
      return (+*this).max(value);
    }


    CImg<T>& draw_circle(const int x0, const int y0, int radius,
                         const tc *const color, const float opacity=1) {
      if (is_empty()) return *this;
      if (radius<0 || x0 - radius>=width() || y0 + radius<0 || y0 - radius>=height()) return *this;
      if (!color)
        throw CImgArgumentException(_cimg_instance
                                    "draw_circle(): Specified color is (null).",
                                    cimg_instance);
      if (!radius) return draw_point(x0,y0,color,opacity);
      cimg_init_scanline(opacity);
      if (y0>=0 && y0<height()) cimg_draw_scanline(x0 - radius,x0 + radius,y0,color,opacity,1);
      for (int f = 1 - radius, ddFx = 0, ddFy = -(radius<<1), x = 0, y = radius; x<y; ) {
        if (f>=0) {
          const int x1 = x0 - x, x2 = x0 + x, y1 = y0 - y, y2 = y0 + y;
          if (y1>=0 && y1<height()) cimg_draw_scanline(x1,x2,y1,color,opacity,1);
          if (y2>=0 && y2<height()) cimg_draw_scanline(x1,x2,y2,color,opacity,1);
          f+=(ddFy+=2); --y;
        }
        const bool no_diag = y!=(x++);
        ++(f+=(ddFx+=2));
        const int x1 = x0 - y, x2 = x0 + y, y1 = y0 - x, y2 = y0 + x;
        if (no_diag) {
          if (y1>=0 && y1<height()) cimg_draw_scanline(x1,x2,y1,color,opacity,1);
          if (y2>=0 && y2<height()) cimg_draw_scanline(x1,x2,y2,color,opacity,1);
        }
      }
      return *this;
    }


    CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
                           const CImg<tp>& vertices, const CImgList<tf>& primitives,
                           const CImgList<tc>& colors,
                           const unsigned int render_type,
                           const bool is_double_sided, const float focale,
                           const float lightx, const float lighty, const float lightz,
                           const float specular_lightness, const float specular_shininess,
                           const float g_opacity, CImg<tz>& zbuffer) {
      return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg<floatT>::const_empty(),
                           render_type,is_double_sided,focale,lightx,lighty,lightz,
                           specular_lightness,specular_shininess,g_opacity,zbuffer);
    }


    CImg<T>& draw_image(const CImg<t>& sprite, const float opacity=1) {
      return draw_image(0,sprite,opacity);
    }


    T atNX(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const {
      return (pos<0 || pos>=width())?out_value:_data[pos].atX(x,y,z,c,out_value);
    }


    double trace() const {
      if (is_empty())
        throw CImgInstanceException(_cimg_instance
                                    "trace(): Empty instance.",
                                    cimg_instance);
      double res = 0;
      cimg_forX(*this,k) res+=(double)(*this)(k,k);
      return res;
    }


      static double mp_for(_cimg_math_parser& mp) {
        const ulongT
          mem_body = mp.opcode[1],
          mem_cond = mp.opcode[3];
        const CImg<ulongT>
          *const p_init = ++mp.p_code,
          *const p_cond = p_init + mp.opcode[4],
          *const p_body = p_cond + mp.opcode[5],
          *const p_post = p_body + mp.opcode[6],
          *const p_end = p_post + mp.opcode[7];
        const unsigned int vsiz = (unsigned int)mp.opcode[2];
        bool is_cond = false;
        if (mp.opcode[8]) { // Set default value for result and condition if necessary
          if (vsiz) CImg<doubleT>(&mp.mem[mem_body] + 1,vsiz,1,1,1,true).fill(cimg::type<double>::nan());
          else mp.mem[mem_body] = cimg::type<double>::nan();
        }
        if (mp.opcode[9]) mp.mem[mem_cond] = 0;
        const unsigned int _break_type = mp.break_type;
        mp.break_type = 0;

        for (mp.p_code = p_init; mp.p_code<p_cond; ++mp.p_code) { // Evaluate init
          mp.opcode._data = mp.p_code->_data;
          const ulongT target = mp.opcode[1];
          mp.mem[target] = _cimg_mp_defunc(mp);
        }

        if (!mp.break_type) do {
            for (mp.p_code = p_cond; mp.p_code<p_body; ++mp.p_code) { // Evaluate condition
              mp.opcode._data = mp.p_code->_data;
              const ulongT target = mp.opcode[1];
              mp.mem[target] = _cimg_mp_defunc(mp);
            }
            if (mp.break_type==1) break;

            is_cond = (bool)mp.mem[mem_cond];
            if (is_cond && !mp.break_type) {
              for (mp.p_code = p_body; mp.p_code<p_post; ++mp.p_code) { // Evaluate body
                mp.opcode._data = mp.p_code->_data;
                const ulongT target = mp.opcode[1];
                mp.mem[target] = _cimg_mp_defunc(mp);
              }
              if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;

              for (mp.p_code = p_post; mp.p_code<p_end; ++mp.p_code) { // Evaluate post-code
                mp.opcode._data = mp.p_code->_data;
                const ulongT target = mp.opcode[1];
                mp.mem[target] = _cimg_mp_defunc(mp);
              }
              if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
            }
          } while (is_cond);

        mp.break_type = _break_type;
        mp.p_code = p_end - 1;
        return mp.mem[mem_body];
      }


    CImgException& operator=(const CImgException& e) {
      const size_t size = std::strlen(e._message);
      _message = new char[size + 1];
      std::strncpy(_message,e._message,size);
      _message[size] = 0;
      return *this;
    }


    CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c,
         const char *const values, const bool repeat_values):_is_shared(false) {
      const size_t siz = safe_size(size_x,size_y,size_z,size_c);
      if (siz) {
        _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
        try { _data = new T[siz]; } catch (...) {
          _width = _height = _depth = _spectrum = 0; _data = 0;
          throw CImgInstanceException(_cimg_instance
                                      "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
                                      cimg_instance,
                                      cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
                                      size_x,size_y,size_z,size_c);
        }
        fill(values,repeat_values);
      } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
    }


    //! Insert \c n copies of the image \c img into the current image list, at position \c pos \newinstance.
    template<typename t>
    CImgList<T> get_insert(const unsigned int n, const CImg<t>& img, const unsigned int pos=~0U,


      static double mp_convolve(_cimg_math_parser &mp) {
        return _mp_correlate(mp,true);
      }


    CImg<Tfloat> get_invert(const bool use_LU=true) const {
      return CImg<Tfloat>(*this,false).invert(use_LU);
    }


    const CImg<T>& _save_raw(std::FILE *const file, const char *const filename, const bool is_multiplexed) const {
      if (!file && !filename)
        throw CImgArgumentException(_cimg_instance
                                    "save_raw(): Specified filename is (null).",
                                    cimg_instance);
      if (is_empty()) { cimg::fempty(file,filename); return *this; }

      std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
      if (pixel_type()==cimg::type<bool>::string()) { // Boolean data (bitwise)
        ulongT siz;
        const unsigned char *const buf = _bool2uchar(siz,is_multiplexed);
        cimg::fwrite(buf,siz,nfile);
        delete[] buf;
      } else { // Non boolean data
        if (!is_multiplexed || _spectrum==1) cimg::fwrite(_data,size(),nfile); // Non-multiplexed
        else { // Multiplexed
          CImg<T> buf(_spectrum);
          cimg_forXYZ(*this,x,y,z) {
            cimg_forC(*this,c) buf[c] = (*this)(x,y,z,c);
            cimg::fwrite(buf._data,_spectrum,nfile);
          }
        }
      }
      if (!file) cimg::fclose(nfile);
      return *this;
    }


      static double mp_deg2rad(_cimg_math_parser& mp) {
        return _mp_arg(2)*cimg::PI/180;
      }


      static signed char cut(const double val) {
        return val<(double)min()?min():val>(double)max()?max():(signed char)val; }


    bool is_shared() const {
      return _is_shared;
    }


    CImg<T>& load_raw(std::FILE *const file,
                      const unsigned int size_x=0, const unsigned int size_y=1,
                      const unsigned int size_z=1, const unsigned int size_c=1,
                      const bool is_multiplexed=false, const bool invert_endianness=false,
                      const ulongT offset=0) {
      return _load_raw(file,0,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset);
    }


    bool is_sameZC(const CImg<t>& img) const {
      return is_sameZC(img._depth,img._spectrum);
    }


      static void mp_check_list(_cimg_math_parser& mp, const char *const funcname) {
        if (!mp.imglist)
          throw CImgArgumentException("[" cimg_appname "_math_parser] "
                                      "CImg<%s>: Function '%s()': Invalid call with an empty image list.",
                                      pixel_type(),funcname);
      }


START_TEST(calibration_prop_parser)
{
#define DEFAULT_VALUES { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 }
	const float untouched[6] = DEFAULT_VALUES;
	struct parser_test_calibration {
		char *prop;
		bool success;
		float values[6];
	} tests[] = {
		{ "", false, DEFAULT_VALUES },
		{ "banana", false, DEFAULT_VALUES },
		{ "1 2 3 a 5 6", false, DEFAULT_VALUES },
		{ "2", false, DEFAULT_VALUES },
		{ "2 3 4 5 6", false, DEFAULT_VALUES },
		{ "1 2 3 4 5 6", true, DEFAULT_VALUES },
		{ "6.00012 3.244 4.238 5.2421 6.0134 8.860", true,
			{ 6.00012, 3.244, 4.238, 5.2421, 6.0134, 8.860 }},
		{ "0xff 2 3 4 5 6", false, DEFAULT_VALUES },
		{ NULL, false, DEFAULT_VALUES }
	};
	bool success;
	float calibration[6];
	int rc;
	int i;

	for (i = 0; tests[i].prop != NULL; i++) {
		memcpy(calibration, untouched, sizeof(calibration));

		success = parse_calibration_property(tests[i].prop,
						     calibration);
		ck_assert_int_eq(success, tests[i].success);
		if (success)
			rc = memcmp(tests[i].values,
				    calibration,
				    sizeof(calibration));
		else
			rc = memcmp(untouched,
				    calibration,
				    sizeof(calibration));
		ck_assert_int_eq(rc, 0);
	}

	memcpy(calibration, untouched, sizeof(calibration));

	success = parse_calibration_property(NULL, calibration);
	ck_assert(success == false);
	rc = memcmp(untouched, calibration, sizeof(calibration));
	ck_assert_int_eq(rc, 0);
}


evdev_configure_device(struct evdev_device *device)
{
	struct libevdev *evdev = device->evdev;
	enum evdev_device_udev_tags udev_tags;
	unsigned int tablet_tags;
	struct evdev_dispatch *dispatch;

	udev_tags = evdev_device_get_udev_tags(device, device->udev_device);

	if ((udev_tags & EVDEV_UDEV_TAG_INPUT) == 0 ||
	    (udev_tags & ~EVDEV_UDEV_TAG_INPUT) == 0) {
		evdev_log_info(device,
			       "not tagged as supported input device\n");
		return NULL;
	}

	evdev_log_info(device,
		 "is tagged by udev as:%s%s%s%s%s%s%s%s%s%s%s\n",
		 udev_tags & EVDEV_UDEV_TAG_KEYBOARD ? " Keyboard" : "",
		 udev_tags & EVDEV_UDEV_TAG_MOUSE ? " Mouse" : "",
		 udev_tags & EVDEV_UDEV_TAG_TOUCHPAD ? " Touchpad" : "",
		 udev_tags & EVDEV_UDEV_TAG_TOUCHSCREEN ? " Touchscreen" : "",
		 udev_tags & EVDEV_UDEV_TAG_TABLET ? " Tablet" : "",
		 udev_tags & EVDEV_UDEV_TAG_POINTINGSTICK ? " Pointingstick" : "",
		 udev_tags & EVDEV_UDEV_TAG_JOYSTICK ? " Joystick" : "",
		 udev_tags & EVDEV_UDEV_TAG_ACCELEROMETER ? " Accelerometer" : "",
		 udev_tags & EVDEV_UDEV_TAG_TABLET_PAD ? " TabletPad" : "",
		 udev_tags & EVDEV_UDEV_TAG_TRACKBALL ? " Trackball" : "",
		 udev_tags & EVDEV_UDEV_TAG_SWITCH ? " Switch" : "");

	/* Ignore pure accelerometers, but accept devices that are
	 * accelerometers with other axes */
	if (udev_tags == (EVDEV_UDEV_TAG_INPUT|EVDEV_UDEV_TAG_ACCELEROMETER)) {
		evdev_log_info(device,
			 "device is an accelerometer, ignoring\n");
		return NULL;
	}

	if (udev_tags & EVDEV_UDEV_TAG_ACCELEROMETER) {
		evdev_disable_accelerometer_axes(device);
	}

	if (evdev_device_is_joystick_or_gamepad(device)) {
		evdev_log_info(device,
			       "device is a joystick or a gamepad, ignoring\n");
		return NULL;
	}

	if (evdev_reject_device(device)) {
		evdev_log_info(device, "was rejected\n");
		return NULL;
	}

	if (!evdev_is_fake_mt_device(device))
		evdev_fix_android_mt(device);

	if (libevdev_has_event_code(evdev, EV_ABS, ABS_X)) {
		evdev_extract_abs_axes(device, udev_tags);

		if (evdev_is_fake_mt_device(device))
			udev_tags &= ~EVDEV_UDEV_TAG_TOUCHSCREEN;
	}

	if (evdev_device_has_model_quirk(device,
					 QUIRK_MODEL_DELL_CANVAS_TOTEM)) {
		dispatch = evdev_totem_create(device);
		device->seat_caps |= EVDEV_DEVICE_TABLET;
		evdev_log_info(device, "device is a totem\n");
		return dispatch;
	}

	/* libwacom assigns touchpad (or touchscreen) _and_ tablet to the
	   tablet touch bits, so make sure we don't initialize the tablet
	   interface for the touch device */
	tablet_tags = EVDEV_UDEV_TAG_TABLET |
		      EVDEV_UDEV_TAG_TOUCHPAD |
		      EVDEV_UDEV_TAG_TOUCHSCREEN;

	/* libwacom assigns tablet _and_ tablet_pad to the pad devices */
	if (udev_tags & EVDEV_UDEV_TAG_TABLET_PAD) {
		dispatch = evdev_tablet_pad_create(device);
		device->seat_caps |= EVDEV_DEVICE_TABLET_PAD;
		evdev_log_info(device, "device is a tablet pad\n");
		return dispatch;

	}

	if ((udev_tags & tablet_tags) == EVDEV_UDEV_TAG_TABLET) {
		dispatch = evdev_tablet_create(device);
		device->seat_caps |= EVDEV_DEVICE_TABLET;
		evdev_log_info(device, "device is a tablet\n");
		return dispatch;
	}

	if (udev_tags & EVDEV_UDEV_TAG_TOUCHPAD) {
		if (udev_tags & EVDEV_UDEV_TAG_TABLET)
			evdev_tag_tablet_touchpad(device);
		/* whether velocity should be averaged, false by default */
		device->use_velocity_averaging = evdev_need_velocity_averaging(device);
		dispatch = evdev_mt_touchpad_create(device);
		evdev_log_info(device, "device is a touchpad\n");
		return dispatch;
	}

	if (udev_tags & EVDEV_UDEV_TAG_MOUSE ||
	    udev_tags & EVDEV_UDEV_TAG_POINTINGSTICK) {
		evdev_tag_external_mouse(device, device->udev_device);
		evdev_tag_trackpoint(device, device->udev_device);
		device->dpi = evdev_read_dpi_prop(device);
		device->trackpoint_multiplier = evdev_get_trackpoint_multiplier(device);
		/* whether velocity should be averaged, false by default */
		device->use_velocity_averaging = evdev_need_velocity_averaging(device);

		device->seat_caps |= EVDEV_DEVICE_POINTER;

		evdev_log_info(device, "device is a pointer\n");

		/* want left-handed config option */
		device->left_handed.want_enabled = true;
		/* want natural-scroll config option */
		device->scroll.natural_scrolling_enabled = true;
		/* want button scrolling config option */
		if (libevdev_has_event_code(evdev, EV_REL, REL_X) ||
		    libevdev_has_event_code(evdev, EV_REL, REL_Y))
			device->scroll.want_button = 1;
	}

	if (udev_tags & EVDEV_UDEV_TAG_KEYBOARD) {
		device->seat_caps |= EVDEV_DEVICE_KEYBOARD;
		evdev_log_info(device, "device is a keyboard\n");

		/* want natural-scroll config option */
		if (libevdev_has_event_code(evdev, EV_REL, REL_WHEEL) ||
		    libevdev_has_event_code(evdev, EV_REL, REL_HWHEEL)) {
			device->scroll.natural_scrolling_enabled = true;
			device->seat_caps |= EVDEV_DEVICE_POINTER;
		}

		evdev_tag_keyboard(device, device->udev_device);
	}

	if (udev_tags & EVDEV_UDEV_TAG_TOUCHSCREEN) {
		device->seat_caps |= EVDEV_DEVICE_TOUCH;
		evdev_log_info(device, "device is a touch device\n");
	}

	if (udev_tags & EVDEV_UDEV_TAG_SWITCH) {
		if (libevdev_has_event_code(evdev, EV_SW, SW_LID)) {
			device->seat_caps |= EVDEV_DEVICE_SWITCH;
			device->tags |= EVDEV_TAG_LID_SWITCH;
			evdev_log_info(device, "device is a switch device\n");
		}

		if (libevdev_has_event_code(evdev, EV_SW, SW_TABLET_MODE)) {
		    if (evdev_device_has_model_quirk(device,
				 QUIRK_MODEL_TABLET_MODE_SWITCH_UNRELIABLE)) {
			    evdev_log_info(device,
				"device is an unreliable tablet mode switch, filtering events.\n");
			    libevdev_disable_event_code(device->evdev,
							EV_SW,
							SW_TABLET_MODE);
		    } else {
			    device->tags |= EVDEV_TAG_TABLET_MODE_SWITCH;
			    device->seat_caps |= EVDEV_DEVICE_SWITCH;
		    }
		}

		if (device->seat_caps & EVDEV_DEVICE_SWITCH)
		    evdev_log_info(device, "device is a switch device\n");
	}

	if (device->seat_caps & EVDEV_DEVICE_POINTER &&
	    libevdev_has_event_code(evdev, EV_REL, REL_X) &&
	    libevdev_has_event_code(evdev, EV_REL, REL_Y) &&
	    !evdev_init_accel(device, LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE)) {
		evdev_log_error(device,
				"failed to initialize pointer acceleration\n");
		return NULL;
	}

	if (evdev_device_has_model_quirk(device, QUIRK_MODEL_INVERT_HORIZONTAL_SCROLLING)) {
		device->scroll.invert_horizontal_scrolling = true;
	}

	return fallback_dispatch_create(&device->base);
}


evdev_left_handed_has(struct libinput_device *device)
{
	/* This is only hooked up when we have left-handed configuration, so we
	 * can hardcode 1 here */
	return 1;
}


START_TEST(trunkname_test)
{
	struct test {
		const char *path;
		const char *expected;
	} tests[] = {
		{ "foo.c", "foo" },
		{ "/path/to/foo.h", "foo" },
		{ "/path/to/foo", "foo" },
		{ "../bar.foo", "bar" },
		{ "./bar.foo.baz", "bar.foo" },
		{ "./", "" },
		{ "/", "" },
		{ "/bar/", "" },
		{ "/bar", "bar" },
		{ "", "" },
	};

	ARRAY_FOR_EACH(tests, t) {
		char *result = trunkname(t->path);
		ck_assert_str_eq(result, t->expected);
		free(result);
	}
}


evdev_fix_abs_resolution(struct evdev_device *device,
			 unsigned int xcode,
			 unsigned int ycode)
{
	struct libevdev *evdev = device->evdev;
	const struct input_absinfo *absx, *absy;
	size_t widthmm = 0, heightmm = 0;
	size_t xres = EVDEV_FAKE_RESOLUTION,
	       yres = EVDEV_FAKE_RESOLUTION;

	if (!(xcode == ABS_X && ycode == ABS_Y)  &&
	    !(xcode == ABS_MT_POSITION_X && ycode == ABS_MT_POSITION_Y)) {
		evdev_log_bug_libinput(device,
				       "invalid x/y code combination %d/%d\n",
				       xcode,
				       ycode);
		return 0;
	}

	absx = libevdev_get_abs_info(evdev, xcode);
	absy = libevdev_get_abs_info(evdev, ycode);

	if (absx->resolution != 0 || absy->resolution != 0)
		return 0;

	/* Note: we *do not* override resolutions if provided by the kernel.
	 * If a device needs this, add it to 60-evdev.hwdb. The libinput
	 * property is only for general size hints where we can make
	 * educated guesses but don't know better.
	 */
	if (!evdev_read_attr_res_prop(device, &xres, &yres) &&
	    evdev_read_attr_size_prop(device, &widthmm, &heightmm)) {
		xres = (absx->maximum - absx->minimum)/widthmm;
		yres = (absy->maximum - absy->minimum)/heightmm;
	}

	/* libevdev_set_abs_resolution() changes the absinfo we already
	   have a pointer to, no need to fetch it again */
	libevdev_set_abs_resolution(evdev, xcode, xres);
	libevdev_set_abs_resolution(evdev, ycode, yres);

	return xres == EVDEV_FAKE_RESOLUTION;
}


evdev_read_wheel_click_prop(struct evdev_device *device,
			    const char *prop,
			    double *angle)
{
	int val;

	*angle = DEFAULT_WHEEL_CLICK_ANGLE;
	prop = udev_device_get_property_value(device->udev_device, prop);
	if (!prop)
		return false;

	val = parse_mouse_wheel_click_angle_property(prop);
	if (val) {
		*angle = val;
		return true;
	}

	evdev_log_error(device,
		  "mouse wheel click angle is present but invalid, "
		  "using %d degrees instead\n",
		  DEFAULT_WHEEL_CLICK_ANGLE);

	return false;
}


evdev_device_suspend(struct evdev_device *device)
{
	struct libinput *libinput = evdev_libinput_context(device);

	evdev_notify_suspended_device(device);

	if (device->dispatch->interface->suspend)
		device->dispatch->interface->suspend(device->dispatch,
						     device);

	if (device->source) {
		libinput_remove_source(libinput, device->source);
		device->source = NULL;
	}

	if (device->mtdev) {
		mtdev_close_delete(device->mtdev);
		device->mtdev = NULL;
	}

	if (device->fd != -1) {
		close_restricted(libinput, device->fd);
		device->fd = -1;
	}
}


safe_atou(const char *str, unsigned int *val)
{
	return safe_atou_base(str, val, 10);
}


START_TEST(array_for_each)
{
	int ai[6];
	char ac[10];
	struct as {
		int a;
		char b;
		int *ptr;
	} as[32];

	for (size_t i = 0; i < 6; i++)
		ai[i] = 20 + i;
	for (size_t i = 0; i < 10; i++)
		ac[i] = 100 + i;
	for (size_t i = 0; i < 32; i++) {
		as[i].a = 10 + i;
		as[i].b = 20 + i;
		as[i].ptr = (int*)0xab + i;
	}

	int iexpected = 20;
	ARRAY_FOR_EACH(ai, entry) {
		ck_assert_int_eq(*entry, iexpected);
		++iexpected;
	}
	ck_assert_int_eq(iexpected, 26);

	int cexpected = 100;
	ARRAY_FOR_EACH(ac, entry) {
		ck_assert_int_eq(*entry, cexpected);
		++cexpected;
	}
	ck_assert_int_eq(cexpected, 110);

	struct as sexpected = {
		.a = 10,
		.b = 20,
		.ptr = (int*)0xab,
	};
	ARRAY_FOR_EACH(as, entry) {
		ck_assert_int_eq(entry->a, sexpected.a);
		ck_assert_int_eq(entry->b, sexpected.b);
		ck_assert_ptr_eq(entry->ptr, sexpected.ptr);
		++sexpected.a;
		++sexpected.b;
		++sexpected.ptr;
	}
	ck_assert_int_eq(sexpected.a, 42);
}


START_TEST(bitfield_helpers)
{
	/* This value has a bit set on all of the word boundaries we want to
	 * test: 0, 1, 7, 8, 31, 32, and 33
	 */
	unsigned char read_bitfield[] = { 0x83, 0x1, 0x0, 0x80, 0x3 };
	unsigned char write_bitfield[ARRAY_LENGTH(read_bitfield)] = {0};
	size_t i;

	/* Now check that the bitfield we wrote to came out to be the same as
	 * the bitfield we were writing from */
	for (i = 0; i < ARRAY_LENGTH(read_bitfield) * 8; i++) {
		switch (i) {
		case 0:
		case 1:
		case 7:
		case 8:
		case 31:
		case 32:
		case 33:
			ck_assert(bit_is_set(read_bitfield, i));
			set_bit(write_bitfield, i);
			break;
		default:
			ck_assert(!bit_is_set(read_bitfield, i));
			clear_bit(write_bitfield, i);
			break;
		}
	}

	ck_assert_int_eq(memcmp(read_bitfield,
				write_bitfield,
				sizeof(read_bitfield)),
			 0);
}


evdev_scroll_get_button(struct libinput_device *device)
{
	struct evdev_device *evdev = evdev_device(device);

	/* return the wanted configuration, even if it hasn't taken
	 * effect yet! */
	return evdev->scroll.want_button;
}


scale_axis(const struct input_absinfo *absinfo, double val, double to_range)
{
	return (val - absinfo->minimum) * to_range /
		(absinfo->maximum - absinfo->minimum + 1);
}


START_TEST(strstrip_test)
{
	struct strstrip_test {
		const char *string;
		const char *expected;
		const char *what;
	} tests[] = {
		{ "foo",		"foo",		"1234" },
		{ "\"bar\"",		"bar",		"\"" },
		{ "'bar'",		"bar",		"'" },
		{ "\"bar\"",		"\"bar\"",	"'" },
		{ "'bar'",		"'bar'",	"\"" },
		{ "\"bar\"",		"bar",		"\"" },
		{ "\"\"",		"",		"\"" },
		{ "\"foo\"bar\"",	"foo\"bar",	"\"" },
		{ "\"'foo\"bar\"",	"foo\"bar",	"\"'" },
		{ "abcfooabcbarbca",	"fooabcbar",	"abc" },
		{ "xxxxfoo",		"foo",		"x" },
		{ "fooyyyy",		"foo",		"y" },
		{ "xxxxfooyyyy",	"foo",		"xy" },
		{ "x xfooy y",		" xfooy ",	"xy" },
		{ " foo\n",		"foo",		" \n" },
		{ "",			"",		"abc" },
		{ "",			"",		"" },
		{ NULL , NULL, NULL }
	};
	struct strstrip_test *t = tests;

	while (t->string) {
		char *str;
		str = strstrip(t->string, t->what);
		ck_assert_str_eq(str, t->expected);
		free(str);
		t++;
	}
}


evdev_read_model_flags(struct evdev_device *device)
{
	const struct model_map {
		enum quirk quirk;
		enum evdev_device_model model;
	} model_map[] = {
#define MODEL(name) { QUIRK_MODEL_##name, EVDEV_MODEL_##name }
		MODEL(WACOM_TOUCHPAD),
		MODEL(SYNAPTICS_SERIAL_TOUCHPAD),
		MODEL(ALPS_SERIAL_TOUCHPAD),
		MODEL(LENOVO_T450_TOUCHPAD),
		MODEL(TRACKBALL),
		MODEL(APPLE_TOUCHPAD_ONEBUTTON),
		MODEL(LENOVO_SCROLLPOINT),
#undef MODEL
		{ 0, 0 },
	};
	const struct model_map *m = model_map;
	uint32_t model_flags = 0;
	uint32_t all_model_flags = 0;
	struct quirks_context *quirks;
	struct quirks *q;

	quirks = evdev_libinput_context(device)->quirks;
	q = quirks_fetch_for_device(quirks, device->udev_device);

	while (q && m->quirk) {
		bool is_set;

		/* Check for flag re-use */
		assert((all_model_flags & m->model) == 0);
		all_model_flags |= m->model;

		if (quirks_get_bool(q, m->quirk, &is_set)) {
			if (is_set) {
				evdev_log_debug(device,
						"tagged as %s\n",
						quirk_get_name(m->quirk));
				model_flags |= m->model;
			} else {
				evdev_log_debug(device,
						"untagged as %s\n",
						quirk_get_name(m->quirk));
				model_flags &= ~m->model;
			}
		}

		m++;
	}

	quirks_unref(q);

	if (parse_udev_flag(device,
			    device->udev_device,
			    "ID_INPUT_TRACKBALL")) {
		evdev_log_debug(device, "tagged as trackball\n");
		model_flags |= EVDEV_MODEL_TRACKBALL;
	}

	/**
	 * Device is 6 years old at the time of writing this and this was
	 * one of the few udev properties that wasn't reserved for private
	 * usage, so we need to keep this for backwards compat.
	 */
	if (parse_udev_flag(device,
			    device->udev_device,
			    "LIBINPUT_MODEL_LENOVO_X220_TOUCHPAD_FW81")) {
		evdev_log_debug(device, "tagged as trackball\n");
		model_flags |= EVDEV_MODEL_LENOVO_X220_TOUCHPAD_FW81;
	}

	if (parse_udev_flag(device, device->udev_device,
			    "LIBINPUT_TEST_DEVICE")) {
		evdev_log_debug(device, "is a test device\n");
		model_flags |= EVDEV_MODEL_TEST_DEVICE;
	}

	return model_flags;
}


safe_atod(const char *str, double *val)
{
	char *endptr;
	double v;
#ifdef HAVE_LOCALE_H
	locale_t c_locale;
#endif
	size_t slen = strlen(str);

	/* We don't have a use-case where we want to accept hex for a double
	 * or any of the other values strtod can parse */
	for (size_t i = 0; i < slen; i++) {
		char c = str[i];

		if (isdigit(c))
		       continue;
		switch(c) {
		case '+':
		case '-':
		case '.':
			break;
		default:
			return false;
		}
	}

#ifdef HAVE_LOCALE_H
	/* Create a "C" locale to force strtod to use '.' as separator */
	c_locale = newlocale(LC_NUMERIC_MASK, "C", (locale_t)0);
	if (c_locale == (locale_t)0)
		return false;

	errno = 0;
	v = strtod_l(str, &endptr, c_locale);
	freelocale(c_locale);
#else
	/* No locale support in provided libc, assume it already uses '.' */
	errno = 0;
	v = strtod(str, &endptr);
#endif
	if (errno > 0)
		return false;
	if (str == endptr)
		return false;
	if (*str != '\0' && *endptr != '\0')
		return false;
	if (v != 0.0 && !isnormal(v))
		return false;

	*val = v;
	return true;
}


START_TEST(safe_atou_test)
{
	struct atou_test tests[] = {
		{ "10", true, 10 },
		{ "20", true, 20 },
		{ "-1", false, 0 },
		{ "2147483647", true, 2147483647 },
		{ "-2147483648", false, 0},
		{ "0x0", false, 0 },
		{ "-10x10", false, 0 },
		{ "1x-99", false, 0 },
		{ "", false, 0 },
		{ "abd", false, 0 },
		{ "xabd", false, 0 },
		{ "0xaf", false, 0 },
		{ "0x0x", false, 0 },
		{ "x10", false, 0 },
		{ NULL, false, 0 }
	};
	unsigned int v;
	bool success;

	for (int i = 0; tests[i].str != NULL; i++) {
		v = 0xad;
		success = safe_atou(tests[i].str, &v);
		ck_assert(success == tests[i].success);
		if (success)
			ck_assert_int_eq(v, tests[i].val);
		else
			ck_assert_int_eq(v, 0xad);
	}
}


START_TEST(strjoin_test)
{
	struct strjoin_test {
		char *strv[10];
		const char *joiner;
		const char *result;
	} tests[] = {
		{ { "one", "two", "three", NULL }, " ", "one two three" },
		{ { "one", NULL }, "x", "one" },
		{ { "one", "two", NULL }, "x", "onextwo" },
		{ { "one", "two", NULL }, ",", "one,two" },
		{ { "one", "two", NULL }, ", ", "one, two" },
		{ { "one", "two", NULL }, "one", "oneonetwo" },
		{ { "one", "two", NULL }, NULL, NULL },
		{ { "", "", "", NULL }, " ", "  " },
		{ { "a", "b", "c", NULL }, "", "abc" },
		{ { "", "b", "c", NULL }, "x", "xbxc" },
		{ { "", "", "", NULL }, "", "" },
		{ { NULL }, NULL, NULL }
	};
	struct strjoin_test *t = tests;
	struct strjoin_test nulltest = { {NULL}, "x", NULL };

	while (t->strv[0]) {
		char *str;
		str = strv_join(t->strv, t->joiner);
		if (t->result == NULL)
			ck_assert(str == NULL);
		else
			ck_assert_str_eq(str, t->result);
		free(str);
		t++;
	}

	ck_assert(strv_join(nulltest.strv, "x") == NULL);
}


evdev_accel_config_set_speed(struct libinput_device *device, double speed)
{
	struct evdev_device *dev = evdev_device(device);

	if (!filter_set_speed(dev->pointer.filter, speed))
		return LIBINPUT_CONFIG_STATUS_INVALID;

	return LIBINPUT_CONFIG_STATUS_SUCCESS;
}


evdev_log_msg_ratelimit(struct evdev_device *device,
			struct ratelimit *ratelimit,
			enum libinput_log_priority priority,
			const char *format,
			...)
{
	va_list args;
	char buf[1024];

	enum ratelimit_state state;

	if (!is_logged(evdev_libinput_context(device), priority))
		return;

	state = ratelimit_test(ratelimit);
	if (state == RATELIMIT_EXCEEDED)
		return;

	/* Anything info and above is user-visible, use the device name */
	snprintf(buf,
		 sizeof(buf),
		 "%-7s - %s%s%s",
		 evdev_device_get_sysname(device),
		 (priority > LIBINPUT_LOG_PRIORITY_DEBUG) ?  device->log_prefix_name : "",
		 (priority > LIBINPUT_LOG_PRIORITY_DEBUG) ?  ": " : "",
		 format);

	va_start(args, format);
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
	log_msg_va(evdev_libinput_context(device), priority, buf, args);
#pragma GCC diagnostic pop
	va_end(args);

	if (state == RATELIMIT_THRESHOLD) {
		struct human_time ht = to_human_time(ratelimit->interval);
		evdev_log_msg(device,
			      priority,
			      "WARNING: log rate limit exceeded (%d msgs per %d%s). "
			      "Discarding future messages.\n",
			      ratelimit->burst,
			      ht.value,
			      ht.unit);

	}
}


START_TEST(streq_test)
{
	ck_assert(streq("", "") == true);
	ck_assert(streq(NULL, NULL) == true);
	ck_assert(streq("0.0.1", "") == false);
	ck_assert(streq("foo", NULL) == false);
	ck_assert(streq(NULL, "foo") == false);
	ck_assert(streq("0.0.1", "0.0.1") == true);
}


static int compGlobResult(const void *result1, const void *result2)  {
    struct tm time_tmp;
    time_t t1, t2;
    const char *r1 = *(char * const*)(result1);
    const char *r2 = *(char * const*)(result2);

    memset(&time_tmp, 0, sizeof(struct tm));
    strptime(r1 + _compData.prefix_len, _compData.dformat, &time_tmp);
    t1 = mktime(&time_tmp);

    memset(&time_tmp, 0, sizeof(struct tm));
    strptime(r2 + _compData.prefix_len, _compData.dformat, &time_tmp);
    t2 = mktime(&time_tmp);

    if (t1 < t2) return -1;
    if (t1 > t2) return  1;
    return 0;
}


static int sparse_copy(int src_fd, int dest_fd, const struct stat *sb,
                       const char *saveLog, const char *currLog)
{
    const int make_holes = is_probably_sparse(sb);
    size_t max_n_read = SIZE_MAX;
    int last_write_made_hole = 0;
    off_t total_n_read = 0;
    char buf[BUFSIZ + 1];

    while (max_n_read) {
        int make_hole = 0;
        size_t bytes_read;
        const ssize_t n_read = read (src_fd, buf, MIN (max_n_read, BUFSIZ));
        if (n_read < 0) {
            if (errno == EINTR) {
                continue;
            }
            message(MESS_ERROR, "error reading %s: %s\n",
                    currLog, strerror(errno));
            return 0;
        }

        if (n_read == 0)
            break;

        bytes_read = (size_t)n_read;

        max_n_read -= bytes_read;
        total_n_read += n_read;

        if (make_holes) {
            /* Sentinel required by is_nul().  */
            buf[bytes_read] = '\1';

            if ((make_hole = is_nul(buf, bytes_read))) {
                if (lseek (dest_fd, n_read, SEEK_CUR) < 0) {
                    message(MESS_ERROR, "error seeking %s: %s\n",
                            saveLog, strerror(errno));
                    return 0;
                }
            }
        }

        if (!make_hole) {
            if (full_write (dest_fd, buf, bytes_read) != bytes_read) {
                message(MESS_ERROR, "error writing to %s: %s\n",
                        saveLog, strerror(errno));
                return 0;
            }
        }

        last_write_made_hole = make_hole;
    }

    if (last_write_made_hole) {
        if (ftruncate(dest_fd, total_n_read) < 0) {
            message(MESS_ERROR, "error ftruncate %s: %s\n",
                    saveLog, strerror(errno));
            return 0;
        }
    }

    return 1;
}


static int movefd(int oldfd, int newfd)
{
    int rc;
    if (oldfd == newfd)
        /* avoid accidental close of newfd in case it is equal to oldfd */
        return 0;

    rc = dup2(oldfd, newfd);
    if (rc == 0)
        close(oldfd);

    return rc;
}


static int removeLogFile(const char *name, const struct logInfo *log)
{
    int fd = -1;
    int result = 0;
    message(MESS_DEBUG, "removing old log %s\n", name);

    if (log->flags & LOG_FLAG_SHRED) {
        fd = open(name, O_RDWR | O_NOFOLLOW);
        if (fd < 0) {
            message(MESS_ERROR, "error opening %s: %s\n",
                    name, strerror(errno));
            return 1;
        }
    }

    if (!debug && shred_file(fd, name, log)) {
        message(MESS_ERROR, "Failed to remove old log %s: %s\n",
                name, strerror(errno));
        result = 1;
    }

    if (fd != -1)
        close(fd);
    return result;
}


int switch_user_back(void) {
    return switch_user(save_euid, save_egid);
}


static int rotateSingleLog(const struct logInfo *log, unsigned logNum,
                           struct logState *state, struct logNames *rotNames)
{
    int hasErrors = 0;
    struct stat sb;
    char *savedContext = NULL;

    if (!state->doRotate)
        return 0;

    if (!hasErrors) {

        if (!(log->flags & (LOG_FLAG_COPYTRUNCATE | LOG_FLAG_COPY))) {
            if (setSecCtxByName(log->files[logNum], &savedContext) != 0) {
                /* error msg already printed */
                return 1;
            }
#ifdef WITH_ACL
            if ((prev_acl = acl_get_file(log->files[logNum], ACL_TYPE_ACCESS)) == NULL) {
                if (is_acl_well_supported(errno)) {
                    message(MESS_ERROR, "getting file ACL %s: %s\n",
                            log->files[logNum], strerror(errno));
                    hasErrors = 1;
                }
            }
#endif /* WITH_ACL */
            if (log->flags & LOG_FLAG_TMPFILENAME) {
                char *tmpFilename;

                if (asprintf(&tmpFilename, "%s%s", log->files[logNum], ".tmp") < 0) {
                    message_OOM();
                    restoreSecCtx(&savedContext);
                    return 1;
                }

                message(MESS_DEBUG, "renaming %s to %s\n", log->files[logNum],
                        tmpFilename);
                if (!debug && !hasErrors && rename(log->files[logNum], tmpFilename)) {
                    message(MESS_ERROR, "failed to rename %s to %s: %s\n",
                            log->files[logNum], tmpFilename,
                            strerror(errno));
                    hasErrors = 1;
                }

                free(tmpFilename);
            }
            else {
                message(MESS_DEBUG, "renaming %s to %s\n", log->files[logNum],
                        rotNames->finalName);
                if (!debug && !hasErrors &&
                        rename(log->files[logNum], rotNames->finalName)) {
                    message(MESS_ERROR, "failed to rename %s to %s: %s\n",
                            log->files[logNum], rotNames->finalName,
                            strerror(errno));
                    hasErrors = 1;
                }
            }

            if (!log->rotateCount) {
                const char *ext = "";
                if (log->compress_ext && (log->flags & LOG_FLAG_COMPRESS))
                    ext = log->compress_ext;

                free(rotNames->disposeName);
                if (asprintf(&rotNames->disposeName, "%s%s", rotNames->finalName, ext) < 0) {
                    message_OOM();
                    return 1;
                }

                message(MESS_DEBUG, "disposeName will be %s\n", rotNames->disposeName);
            }
        }

        if (!hasErrors && (log->flags & LOG_FLAG_CREATE) &&
                !(log->flags & (LOG_FLAG_COPYTRUNCATE | LOG_FLAG_COPY))) {
            int have_create_mode = 0;

            if (log->createUid == NO_UID)
                sb.st_uid = state->sb.st_uid;
            else
                sb.st_uid = log->createUid;

            if (log->createGid == NO_GID)
                sb.st_gid = state->sb.st_gid;
            else
                sb.st_gid = log->createGid;
            if (log->createMode == NO_MODE)
                sb.st_mode = state->sb.st_mode & 0777;
            else {
                sb.st_mode = log->createMode;
                have_create_mode = 1;
            }

            message(MESS_DEBUG, "creating new %s mode = 0%o uid = %d "
                    "gid = %d\n", log->files[logNum], (unsigned int) sb.st_mode,
                    (int) sb.st_uid, (int) sb.st_gid);

            if (!debug) {
                if (!hasErrors) {
                    int fd = createOutputFile(log->files[logNum], O_CREAT | O_RDWR,
                            &sb, prev_acl, have_create_mode);
#ifdef WITH_ACL
                    if (prev_acl) {
                        acl_free(prev_acl);
                        prev_acl = NULL;
                    }
#endif
                    if (fd < 0)
                        hasErrors = 1;
                    else {
                        close(fd);
                    }
                }
            }
        }

        restoreSecCtx(&savedContext);

        if (!hasErrors
                && (log->flags & (LOG_FLAG_COPYTRUNCATE | LOG_FLAG_COPY))
                && !(log->flags & LOG_FLAG_TMPFILENAME)) {
            hasErrors = copyTruncate(log->files[logNum], rotNames->finalName,
                                     &state->sb, log->flags, !log->rotateCount);
        }

#ifdef WITH_ACL
        if (prev_acl) {
            acl_free(prev_acl);
            prev_acl = NULL;
        }
#endif /* WITH_ACL */

    }
    return hasErrors;
}


static int removeLogFile(const char *name, const struct logInfo *log)
{
    int fd = -1;
    int result = 0;
    message(MESS_DEBUG, "removing old log %s\n", name);

    if (log->flags & LOG_FLAG_SHRED) {
        fd = open_logfile(name, log, 1);
        if (fd < 0) {
            message(MESS_ERROR, "error opening %s: %s\n",
                    name, strerror(errno));
            return 1;
        }
    }

    if (!debug && shred_file(fd, name, log)) {
        message(MESS_ERROR, "Failed to remove old log %s: %s\n",
                name, strerror(errno));
        result = 1;
    }

    if (fd != -1)
        close(fd);
    return result;
}


static int runScript(const struct logInfo *log, const char *logfn, const char *logrotfn, const char *script)
{
    int rc;
    pid_t pid;

    if (debug) {
        message(MESS_DEBUG, "running script with args %s %s: \"%s\"\n",
                logfn, logrotfn ? logrotfn : "", script);
        return 0;
    }

    pid = fork();

    if (pid == -1) {
        message(MESS_ERROR, "cannot fork: %s\n", strerror(errno));
        return 1;
    }

    if (pid == 0) {
        if (log->flags & LOG_FLAG_SU) {
            if (switch_user_back_permanently() != 0) {
                exit(1);
            }
        }
        execl("/bin/sh", "sh", "-c", (char *) script, "logrotate_script", (char *) logfn, (char *) logrotfn, (char *) NULL);
        message(MESS_ERROR, "cannot execute sub-shell: %s\n", strerror(errno));
        exit(1);
    }

    wait(&rc);
    return rc;
}


static int io_rsrc_data_alloc(struct io_ring_ctx *ctx, rsrc_put_fn *do_put,
			      u64 __user *utags, unsigned nr,
			      struct io_rsrc_data **pdata)
{
	struct io_rsrc_data *data;
	int ret = -ENOMEM;
	unsigned i;

	data = kzalloc(sizeof(*data), GFP_KERNEL);
	if (!data)
		return -ENOMEM;
	data->tags = (u64 **)io_alloc_page_table(nr * sizeof(data->tags[0][0]));
	if (!data->tags) {
		kfree(data);
		return -ENOMEM;
	}

	data->nr = nr;
	data->ctx = ctx;
	data->do_put = do_put;
	if (utags) {
		ret = -EFAULT;
		for (i = 0; i < nr; i++) {
			u64 *tag_slot = io_get_tag_slot(data, i);

			if (copy_from_user(tag_slot, &utags[i],
					   sizeof(*tag_slot)))
				goto fail;
		}
	}

	atomic_set(&data->refs, 1);
	init_completion(&data->done);
	*pdata = data;
	return 0;
fail:
	io_rsrc_data_free(data);
	return ret;


static int io_do_iopoll(struct io_ring_ctx *ctx, unsigned int *nr_events,
			long min)
{
	struct io_kiocb *req, *tmp;
	LIST_HEAD(done);
	bool spin;
	int ret;

	/*
	 * Only spin for completions if we don't have multiple devices hanging
	 * off our complete list, and we're under the requested amount.
	 */
	spin = !ctx->poll_multi_queue && *nr_events < min;

	ret = 0;
	list_for_each_entry_safe(req, tmp, &ctx->iopoll_list, inflight_entry) {
		struct kiocb *kiocb = &req->rw.kiocb;

		/*
		 * Move completed and retryable entries to our local lists.
		 * If we find a request that requires polling, break out
		 * and complete those lists first, if we have entries there.
		 */
		if (READ_ONCE(req->iopoll_completed)) {
			list_move_tail(&req->inflight_entry, &done);
			continue;
		}
		if (!list_empty(&done))
			break;

		ret = kiocb->ki_filp->f_op->iopoll(kiocb, spin);
		if (ret < 0)
			break;

		/* iopoll may have completed current req */
		if (READ_ONCE(req->iopoll_completed))
			list_move_tail(&req->inflight_entry, &done);

		if (ret && spin)
			spin = false;
		ret = 0;
	}

	if (!list_empty(&done))
		io_iopoll_complete(ctx, nr_events, &done);

	return ret;
}


static int io_rsrc_update_prep(struct io_kiocb *req,
				const struct io_uring_sqe *sqe)
{
	if (unlikely(req->flags & (REQ_F_FIXED_FILE | REQ_F_BUFFER_SELECT)))
		return -EINVAL;
	if (sqe->ioprio || sqe->rw_flags)
		return -EINVAL;

	req->rsrc_update.offset = READ_ONCE(sqe->off);
	req->rsrc_update.nr_args = READ_ONCE(sqe->len);
	if (!req->rsrc_update.nr_args)
		return -EINVAL;
	req->rsrc_update.arg = READ_ONCE(sqe->addr);
	return 0;


static int io_uring_create(unsigned entries, struct io_uring_params *p,
			   struct io_uring_params __user *params)
{
	struct io_ring_ctx *ctx;
	struct file *file;
	int ret;

	if (!entries)
		return -EINVAL;
	if (entries > IORING_MAX_ENTRIES) {
		if (!(p->flags & IORING_SETUP_CLAMP))
			return -EINVAL;
		entries = IORING_MAX_ENTRIES;
	}

	/*
	 * Use twice as many entries for the CQ ring. It's possible for the
	 * application to drive a higher depth than the size of the SQ ring,
	 * since the sqes are only used at submission time. This allows for
	 * some flexibility in overcommitting a bit. If the application has
	 * set IORING_SETUP_CQSIZE, it will have passed in the desired number
	 * of CQ ring entries manually.
	 */
	p->sq_entries = roundup_pow_of_two(entries);
	if (p->flags & IORING_SETUP_CQSIZE) {
		/*
		 * If IORING_SETUP_CQSIZE is set, we do the same roundup
		 * to a power-of-two, if it isn't already. We do NOT impose
		 * any cq vs sq ring sizing.
		 */
		if (!p->cq_entries)
			return -EINVAL;
		if (p->cq_entries > IORING_MAX_CQ_ENTRIES) {
			if (!(p->flags & IORING_SETUP_CLAMP))
				return -EINVAL;
			p->cq_entries = IORING_MAX_CQ_ENTRIES;
		}
		p->cq_entries = roundup_pow_of_two(p->cq_entries);
		if (p->cq_entries < p->sq_entries)
			return -EINVAL;
	} else {
		p->cq_entries = 2 * p->sq_entries;
	}

	ctx = io_ring_ctx_alloc(p);
	if (!ctx)
		return -ENOMEM;
	ctx->compat = in_compat_syscall();
	if (!capable(CAP_IPC_LOCK))
		ctx->user = get_uid(current_user());

	/*
	 * This is just grabbed for accounting purposes. When a process exits,
	 * the mm is exited and dropped before the files, hence we need to hang
	 * on to this mm purely for the purposes of being able to unaccount
	 * memory (locked/pinned vm). It's not used for anything else.
	 */
	mmgrab(current->mm);
	ctx->mm_account = current->mm;

	ret = io_allocate_scq_urings(ctx, p);
	if (ret)
		goto err;

	ret = io_sq_offload_create(ctx, p);
	if (ret)
		goto err;
	/* always set a rsrc node */
	ret = io_rsrc_node_switch_start(ctx);
	if (ret)
		goto err;
	io_rsrc_node_switch(ctx, NULL);

	memset(&p->sq_off, 0, sizeof(p->sq_off));
	p->sq_off.head = offsetof(struct io_rings, sq.head);
	p->sq_off.tail = offsetof(struct io_rings, sq.tail);
	p->sq_off.ring_mask = offsetof(struct io_rings, sq_ring_mask);
	p->sq_off.ring_entries = offsetof(struct io_rings, sq_ring_entries);
	p->sq_off.flags = offsetof(struct io_rings, sq_flags);
	p->sq_off.dropped = offsetof(struct io_rings, sq_dropped);
	p->sq_off.array = (char *)ctx->sq_array - (char *)ctx->rings;

	memset(&p->cq_off, 0, sizeof(p->cq_off));
	p->cq_off.head = offsetof(struct io_rings, cq.head);
	p->cq_off.tail = offsetof(struct io_rings, cq.tail);
	p->cq_off.ring_mask = offsetof(struct io_rings, cq_ring_mask);
	p->cq_off.ring_entries = offsetof(struct io_rings, cq_ring_entries);
	p->cq_off.overflow = offsetof(struct io_rings, cq_overflow);
	p->cq_off.cqes = offsetof(struct io_rings, cqes);
	p->cq_off.flags = offsetof(struct io_rings, cq_flags);

	p->features = IORING_FEAT_SINGLE_MMAP | IORING_FEAT_NODROP |
			IORING_FEAT_SUBMIT_STABLE | IORING_FEAT_RW_CUR_POS |
			IORING_FEAT_CUR_PERSONALITY | IORING_FEAT_FAST_POLL |
			IORING_FEAT_POLL_32BITS | IORING_FEAT_SQPOLL_NONFIXED |
			IORING_FEAT_EXT_ARG | IORING_FEAT_NATIVE_WORKERS |
			IORING_FEAT_RSRC_TAGS;

	if (copy_to_user(params, p, sizeof(*p))) {
		ret = -EFAULT;
		goto err;
	}

	file = io_uring_get_file(ctx);
	if (IS_ERR(file)) {
		ret = PTR_ERR(file);
		goto err;
	}

	/*
	 * Install ring fd as the very last thing, so we don't risk someone
	 * having closed it before we finish setup
	 */
	ret = io_uring_install_fd(ctx, file);
	if (ret < 0) {
		/* fput will clean it up */
		fput(file);
		return ret;
	}

	trace_io_uring_create(ret, ctx, p->sq_entries, p->cq_entries, p->flags);
	return ret;
err:
	io_ring_ctx_wait_and_kill(ctx);
	return ret;


static int io_write(struct io_kiocb *req, unsigned int issue_flags)
{
	struct iovec inline_vecs[UIO_FASTIOV], *iovec = inline_vecs;
	struct kiocb *kiocb = &req->rw.kiocb;
	struct iov_iter __iter, *iter = &__iter;
	struct io_async_rw *rw = req->async_data;
	ssize_t ret, ret2, io_size;
	bool force_nonblock = issue_flags & IO_URING_F_NONBLOCK;

	if (rw) {
		iter = &rw->iter;
		iovec = NULL;
	} else {
		ret = io_import_iovec(WRITE, req, &iovec, iter, !force_nonblock);
		if (ret < 0)
			return ret;
	}
	io_size = iov_iter_count(iter);
	req->result = io_size;

	/* Ensure we clear previously set non-block flag */
	if (!force_nonblock)
		kiocb->ki_flags &= ~IOCB_NOWAIT;
	else
		kiocb->ki_flags |= IOCB_NOWAIT;

	/* If the file doesn't support async, just async punt */
	if (force_nonblock && !io_file_supports_async(req, WRITE))
		goto copy_iov;

	/* file path doesn't support NOWAIT for non-direct_IO */
	if (force_nonblock && !(kiocb->ki_flags & IOCB_DIRECT) &&
	    (req->flags & REQ_F_ISREG))
		goto copy_iov;

	ret = rw_verify_area(WRITE, req->file, io_kiocb_ppos(kiocb), io_size);
	if (unlikely(ret))
		goto out_free;

	/*
	 * Open-code file_start_write here to grab freeze protection,
	 * which will be released by another thread in
	 * io_complete_rw().  Fool lockdep by telling it the lock got
	 * released so that it doesn't complain about the held lock when
	 * we return to userspace.
	 */
	if (req->flags & REQ_F_ISREG) {
		sb_start_write(file_inode(req->file)->i_sb);
		__sb_writers_release(file_inode(req->file)->i_sb,
					SB_FREEZE_WRITE);
	}
	kiocb->ki_flags |= IOCB_WRITE;

	if (req->file->f_op->write_iter)
		ret2 = call_write_iter(req->file, kiocb, iter);
	else if (req->file->f_op->write)
		ret2 = loop_rw_iter(WRITE, req, iter);
	else
		ret2 = -EINVAL;

	if (req->flags & REQ_F_REISSUE) {
		req->flags &= ~REQ_F_REISSUE;
		ret2 = -EAGAIN;
	}

	/*
	 * Raw bdev writes will return -EOPNOTSUPP for IOCB_NOWAIT. Just
	 * retry them without IOCB_NOWAIT.
	 */
	if (ret2 == -EOPNOTSUPP && (kiocb->ki_flags & IOCB_NOWAIT))
		ret2 = -EAGAIN;
	/* no retry on NONBLOCK nor RWF_NOWAIT */
	if (ret2 == -EAGAIN && (req->flags & REQ_F_NOWAIT))
		goto done;
	if (!force_nonblock || ret2 != -EAGAIN) {
		/* IOPOLL retry should happen for io-wq threads */
		if ((req->ctx->flags & IORING_SETUP_IOPOLL) && ret2 == -EAGAIN)
			goto copy_iov;
done:
		kiocb_done(kiocb, ret2, issue_flags);
	} else {
copy_iov:
		/* some cases will consume bytes even on error returns */
		iov_iter_reexpand(iter, iter->count + iter->truncated);
		iov_iter_revert(iter, io_size - iov_iter_count(iter));
		ret = io_setup_async_rw(req, iovec, inline_vecs, iter, false);
		return ret ?: -EAGAIN;
	}
out_free:
	/* it's reportedly faster than delegating the null check to kfree() */
	if (iovec)
		kfree(iovec);
	return ret;
}


static void io_flush_cached_locked_reqs(struct io_ring_ctx *ctx,
					struct io_comp_state *cs)
{
	spin_lock_irq(&ctx->completion_lock);
	list_splice_init(&ctx->locked_free_list, &cs->free_list);
	ctx->locked_free_nr = 0;
	spin_unlock_irq(&ctx->completion_lock);
}


static void io_req_task_queue_fail(struct io_kiocb *req, int ret)
{
	req->result = ret;
	req->io_task_work.func = io_req_task_cancel;
	io_req_task_work_add(req);
}


static bool io_alloc_file_tables(struct io_file_table *table, unsigned nr_files)
{
	size_t size = nr_files * sizeof(struct io_fixed_file);

	table->files = (struct io_fixed_file **)io_alloc_page_table(size);
	return !!table->files;


static int io_allocate_scq_urings(struct io_ring_ctx *ctx,
				  struct io_uring_params *p)
{
	struct io_rings *rings;
	size_t size, sq_array_offset;

	/* make sure these are sane, as we already accounted them */
	ctx->sq_entries = p->sq_entries;
	ctx->cq_entries = p->cq_entries;

	size = rings_size(p->sq_entries, p->cq_entries, &sq_array_offset);
	if (size == SIZE_MAX)
		return -EOVERFLOW;

	rings = io_mem_alloc(size);
	if (!rings)
		return -ENOMEM;

	ctx->rings = rings;
	ctx->sq_array = (u32 *)((char *)rings + sq_array_offset);
	rings->sq_ring_mask = p->sq_entries - 1;
	rings->cq_ring_mask = p->cq_entries - 1;
	rings->sq_ring_entries = p->sq_entries;
	rings->cq_ring_entries = p->cq_entries;

	size = array_size(sizeof(struct io_uring_sqe), p->sq_entries);
	if (size == SIZE_MAX) {
		io_mem_free(ctx->rings);
		ctx->rings = NULL;
		return -EOVERFLOW;
	}

	ctx->sq_sqes = io_mem_alloc(size);
	if (!ctx->sq_sqes) {
		io_mem_free(ctx->rings);
		ctx->rings = NULL;
		return -ENOMEM;
	}

	return 0;


static enum hrtimer_restart io_timeout_fn(struct hrtimer *timer)
{
	struct io_timeout_data *data = container_of(timer,
						struct io_timeout_data, timer);
	struct io_kiocb *req = data->req;
	struct io_ring_ctx *ctx = req->ctx;
	unsigned long flags;

	spin_lock_irqsave(&ctx->completion_lock, flags);
	list_del_init(&req->timeout.list);
	atomic_set(&req->ctx->cq_timeouts,
		atomic_read(&req->ctx->cq_timeouts) + 1);

	io_cqring_fill_event(ctx, req->user_data, -ETIME, 0);
	io_commit_cqring(ctx);
	spin_unlock_irqrestore(&ctx->completion_lock, flags);

	io_cqring_ev_posted(ctx);
	req_set_fail(req);
	io_put_req(req);
	return HRTIMER_NORESTART;


static enum hrtimer_restart io_link_timeout_fn(struct hrtimer *timer)
{
	struct io_timeout_data *data = container_of(timer,
						struct io_timeout_data, timer);
	struct io_kiocb *prev, *req = data->req;
	struct io_ring_ctx *ctx = req->ctx;
	unsigned long flags;

	spin_lock_irqsave(&ctx->completion_lock, flags);
	prev = req->timeout.head;
	req->timeout.head = NULL;

	/*
	 * We don't expect the list to be empty, that will only happen if we
	 * race with the completion of the linked work.
	 */
	if (prev) {
		io_remove_next_linked(prev);
		if (!req_ref_inc_not_zero(prev))
			prev = NULL;
	}
	spin_unlock_irqrestore(&ctx->completion_lock, flags);

	if (prev) {
		io_async_find_and_cancel(ctx, req, prev->user_data, -ETIME);
		io_put_req_deferred(prev, 1);
		io_put_req_deferred(req, 1);
	} else {
		io_req_complete_post(req, -ETIME, 0);
	}
	return HRTIMER_NORESTART;


static bool __io_cqring_overflow_flush(struct io_ring_ctx *ctx, bool force)
{
	unsigned long flags;
	bool all_flushed, posted;

	if (!force && __io_cqring_events(ctx) == ctx->cq_entries)
		return false;

	posted = false;
	spin_lock_irqsave(&ctx->completion_lock, flags);
	while (!list_empty(&ctx->cq_overflow_list)) {
		struct io_uring_cqe *cqe = io_get_cqe(ctx);
		struct io_overflow_cqe *ocqe;

		if (!cqe && !force)
			break;
		ocqe = list_first_entry(&ctx->cq_overflow_list,
					struct io_overflow_cqe, list);
		if (cqe)
			memcpy(cqe, &ocqe->cqe, sizeof(*cqe));
		else
			io_account_cq_overflow(ctx);

		posted = true;
		list_del(&ocqe->list);
		kfree(ocqe);
	}

	all_flushed = list_empty(&ctx->cq_overflow_list);
	if (all_flushed) {
		clear_bit(0, &ctx->check_cq_overflow);
		ctx->rings->sq_flags &= ~IORING_SQ_CQ_OVERFLOW;
	}

	if (posted)
		io_commit_cqring(ctx);
	spin_unlock_irqrestore(&ctx->completion_lock, flags);
	if (posted)
		io_cqring_ev_posted(ctx);
	return all_flushed;
}


static void mutt_decode_xbit (STATE *s, LOFF_T len, int istext, iconv_t cd)
{
  int c, ch;
  char bufi[BUFI_SIZE];
  size_t l = 0;

  if (istext)
  {
    state_set_prefix(s);

    while ((c = fgetc(s->fpin)) != EOF && len--)
    {
      if (c == '\r' && len)
      {
	if ((ch = fgetc(s->fpin)) == '\n')
	{
	  c = ch;
	  len--;
	}
	else
	  ungetc(ch, s->fpin);
      }

      bufi[l++] = c;
      if (l == sizeof (bufi))
	mutt_convert_to_state (cd, bufi, &l, s);
    }

    mutt_convert_to_state (cd, bufi, &l, s);
    mutt_convert_to_state (cd, 0, 0, s);

    state_reset_prefix (s);
  }
  else
    mutt_copy_bytes (s->fpin, s->fpout, len);
}


static void mutt_decode_quoted (STATE *s, LOFF_T len, int istext, iconv_t cd)
{
  char line[STRING];
  char decline[2*STRING];
  size_t l = 0;
  size_t linelen;      /* number of input bytes in `line' */
  size_t l3;

  int last;    /* store the last character in the input line */

  if (istext)
    state_set_prefix(s);

  while (len > 0)
  {
    last = 0;

    /*
     * It's ok to use a fixed size buffer for input, even if the line turns
     * out to be longer than this.  Just process the line in chunks.  This
     * really shouldn't happen according the MIME spec, since Q-P encoded
     * lines are at most 76 characters, but we should be liberal about what
     * we accept.
     */
    if (fgets (line, MIN ((ssize_t)sizeof (line), len + 1), s->fpin) == NULL)
      break;

    linelen = strlen(line);
    len -= linelen;

    /*
     * inspect the last character we read so we can tell if we got the
     * entire line.
     */
    last = linelen ? line[linelen - 1] : 0;

    /* chop trailing whitespace if we got the full line */
    if (last == '\n')
    {
      while (linelen > 0 && ISSPACE (line[linelen-1]))
        linelen--;
      line[linelen]=0;
    }

    /* decode and do character set conversion */
    qp_decode_line (decline + l, line, &l3, last);
    l += l3;
    mutt_convert_to_state (cd, decline, &l, s);
  }

  mutt_convert_to_state (cd, 0, 0, s);
  state_reset_prefix(s);
}


static int compat_x25_ioctl(struct socket *sock, unsigned int cmd,
				unsigned long arg)
{
	void __user *argp = compat_ptr(arg);
	int rc = -ENOIOCTLCMD;

	switch(cmd) {
	case TIOCOUTQ:
	case TIOCINQ:
		rc = x25_ioctl(sock, cmd, (unsigned long)argp);
		break;
	case SIOCGIFADDR:
	case SIOCSIFADDR:
	case SIOCGIFDSTADDR:
	case SIOCSIFDSTADDR:
	case SIOCGIFBRDADDR:
	case SIOCSIFBRDADDR:
	case SIOCGIFNETMASK:
	case SIOCSIFNETMASK:
	case SIOCGIFMETRIC:
	case SIOCSIFMETRIC:
		rc = -EINVAL;
		break;
	case SIOCADDRT:
	case SIOCDELRT:
		rc = -EPERM;
		if (!capable(CAP_NET_ADMIN))
			break;
		rc = x25_route_ioctl(cmd, argp);
		break;
	case SIOCX25GSUBSCRIP:
		rc = compat_x25_subscr_ioctl(cmd, argp);
		break;
	case SIOCX25SSUBSCRIP:
		rc = -EPERM;
		if (!capable(CAP_NET_ADMIN))
			break;
		rc = compat_x25_subscr_ioctl(cmd, argp);
		break;
	case SIOCX25GFACILITIES:
	case SIOCX25SFACILITIES:
	case SIOCX25GDTEFACILITIES:
	case SIOCX25SDTEFACILITIES:
	case SIOCX25GCALLUSERDATA:
	case SIOCX25SCALLUSERDATA:
	case SIOCX25GCAUSEDIAG:
	case SIOCX25SCAUSEDIAG:
	case SIOCX25SCUDMATCHLEN:
	case SIOCX25CALLACCPTAPPRV:
	case SIOCX25SENDCALLACCPT:
		rc = x25_ioctl(sock, cmd, (unsigned long)argp);
		break;
	default:
		rc = -ENOIOCTLCMD;
		break;
	}
	return rc;
}


static void __x25_destroy_socket(struct sock *sk)
{
	struct sk_buff *skb;

	x25_stop_heartbeat(sk);
	x25_stop_timer(sk);

	x25_remove_socket(sk);
	x25_clear_queues(sk);		/* Flush the queues */

	while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) {
		if (skb->sk != sk) {		/* A pending connection */
			/*
			 * Queue the unaccepted socket for death
			 */
			skb->sk->sk_state = TCP_LISTEN;
			sock_set_flag(skb->sk, SOCK_DEAD);
			x25_start_heartbeat(skb->sk);
			x25_sk(skb->sk)->state = X25_STATE_0;
		}

		kfree_skb(skb);
	}

	if (sk_has_allocations(sk)) {
		/* Defer: outstanding buffers */
		sk->sk_timer.expires  = jiffies + 10 * HZ;
		sk->sk_timer.function = x25_destroy_timer;
		add_timer(&sk->sk_timer);
	} else {
		/* drop last reference so sock_put will free */
		__sock_put(sk);
	}
}


static int x25_setsockopt(struct socket *sock, int level, int optname,
			  sockptr_t optval, unsigned int optlen)
{
	int opt;
	struct sock *sk = sock->sk;
	int rc = -ENOPROTOOPT;

	if (level != SOL_X25 || optname != X25_QBITINCL)
		goto out;

	rc = -EINVAL;
	if (optlen < sizeof(int))
		goto out;

	rc = -EFAULT;
	if (copy_from_sockptr(&opt, optval, sizeof(int)))
		goto out;

	if (opt)
		set_bit(X25_Q_BIT_FLAG, &x25_sk(sk)->flags);
	else
		clear_bit(X25_Q_BIT_FLAG, &x25_sk(sk)->flags);
	rc = 0;
out:
	return rc;
}


static int x25_create(struct net *net, struct socket *sock, int protocol,
		      int kern)
{
	struct sock *sk;
	struct x25_sock *x25;
	int rc = -EAFNOSUPPORT;

	if (!net_eq(net, &init_net))
		goto out;

	rc = -ESOCKTNOSUPPORT;
	if (sock->type != SOCK_SEQPACKET)
		goto out;

	rc = -EINVAL;
	if (protocol)
		goto out;

	rc = -ENOMEM;
	if ((sk = x25_alloc_socket(net, kern)) == NULL)
		goto out;

	x25 = x25_sk(sk);

	sock_init_data(sock, sk);

	x25_init_timers(sk);

	sock->ops    = &x25_proto_ops;
	sk->sk_protocol = protocol;
	sk->sk_backlog_rcv = x25_backlog_rcv;

	x25->t21   = sysctl_x25_call_request_timeout;
	x25->t22   = sysctl_x25_reset_request_timeout;
	x25->t23   = sysctl_x25_clear_request_timeout;
	x25->t2    = sysctl_x25_ack_holdback_timeout;
	x25->state = X25_STATE_0;
	x25->cudmatchlength = 0;
	set_bit(X25_ACCPT_APPRV_FLAG, &x25->flags);	/* normally no cud  */
							/* on call accept   */

	x25->facilities.winsize_in  = X25_DEFAULT_WINDOW_SIZE;
	x25->facilities.winsize_out = X25_DEFAULT_WINDOW_SIZE;
	x25->facilities.pacsize_in  = X25_DEFAULT_PACKET_SIZE;
	x25->facilities.pacsize_out = X25_DEFAULT_PACKET_SIZE;
	x25->facilities.throughput  = 0;	/* by default don't negotiate
						   throughput */
	x25->facilities.reverse     = X25_DEFAULT_REVERSE;
	x25->dte_facilities.calling_len = 0;
	x25->dte_facilities.called_len = 0;
	memset(x25->dte_facilities.called_ae, '\0',
			sizeof(x25->dte_facilities.called_ae));
	memset(x25->dte_facilities.calling_ae, '\0',
			sizeof(x25->dte_facilities.calling_ae));

	rc = 0;
out:
	return rc;
}


static int x25_device_event(struct notifier_block *this, unsigned long event,
			    void *ptr)
{
	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
	struct x25_neigh *nb;

	if (!net_eq(dev_net(dev), &init_net))
		return NOTIFY_DONE;

	if (dev->type == ARPHRD_X25) {
		switch (event) {
		case NETDEV_REGISTER:
		case NETDEV_POST_TYPE_CHANGE:
			x25_link_device_up(dev);
			break;
		case NETDEV_DOWN:
			nb = x25_get_neigh(dev);
			if (nb) {
				x25_link_terminated(nb);
				x25_neigh_put(nb);
			}
			x25_route_device_down(dev);
			break;
		case NETDEV_PRE_TYPE_CHANGE:
		case NETDEV_UNREGISTER:
			x25_link_device_down(dev);
			break;
		case NETDEV_CHANGE:
			if (!netif_carrier_ok(dev)) {
				nb = x25_get_neigh(dev);
				if (nb) {
					x25_link_terminated(nb);
					x25_neigh_put(nb);
				}
			}
			break;
		}
	}

	return NOTIFY_DONE;
}


ReadRankDims(mat_t *mat, matvar_t *matvar, enum matio_types data_type, mat_uint32_t nbytes,
             size_t *read_bytes)
{
    int err = MATIO_E_NO_ERROR;
    /* Rank and dimension */
    if ( data_type == MAT_T_INT32 ) {
        matvar->rank = nbytes / sizeof(mat_uint32_t);
        matvar->dims = (size_t *)malloc(matvar->rank * sizeof(*matvar->dims));
        if ( NULL != matvar->dims ) {
            int i;
            mat_uint32_t buf;

            for ( i = 0; i < matvar->rank; i++ ) {
                err = Read(&buf, sizeof(mat_uint32_t), 1, (FILE *)mat->fp, read_bytes);
                if ( MATIO_E_NO_ERROR == err ) {
                    if ( mat->byteswap ) {
                        matvar->dims[i] = Mat_uint32Swap(&buf);
                    } else {
                        matvar->dims[i] = buf;
                    }
                } else {
                    free(matvar->dims);
                    matvar->dims = NULL;
                    matvar->rank = 0;
                    return err;
                }
            }

            if ( matvar->rank % 2 != 0 ) {
                err = Read(&buf, sizeof(mat_uint32_t), 1, (FILE *)mat->fp, read_bytes);
                if ( err ) {
                    free(matvar->dims);
                    matvar->dims = NULL;
                    matvar->rank = 0;
                    return err;
                }
            }
        } else {
            matvar->rank = 0;
            err = MATIO_E_OUT_OF_MEMORY;
            Mat_Critical("Error allocating memory for dims");
        }
    }
    return err;
}


WriteCellArrayField(mat_t *mat, matvar_t *matvar)
{
    mat_uint32_t array_flags, nzmax = 0;
    int array_flags_type = MAT_T_UINT32, dims_array_type = MAT_T_INT32;
    int array_flags_size = 8, matrix_type = MAT_T_MATRIX;
    const mat_uint32_t pad4 = 0;
    const mat_uint8_t pad1 = 0;
    int nBytes, i;
    long start = 0, end = 0;

    if ( matvar == NULL || mat == NULL )
        return MATIO_E_BAD_ARGUMENT;

    fwrite(&matrix_type, 4, 1, (FILE *)mat->fp);
    fwrite(&pad4, 4, 1, (FILE *)mat->fp);
    if ( MAT_C_EMPTY == matvar->class_type ) {
        /* exit early if this is an empty data */
        return MATIO_E_NO_ERROR;
    }
    start = ftell((FILE *)mat->fp);

    /* Array Flags */
    array_flags = matvar->class_type & CLASS_TYPE_MASK;
    if ( matvar->isComplex )
        array_flags |= MAT_F_COMPLEX;
    if ( matvar->isGlobal )
        array_flags |= MAT_F_GLOBAL;
    if ( matvar->isLogical )
        array_flags |= MAT_F_LOGICAL;
    if ( matvar->class_type == MAT_C_SPARSE )
        nzmax = ((mat_sparse_t *)matvar->data)->nzmax;

    if ( mat->byteswap )
        array_flags = Mat_int32Swap((mat_int32_t *)&array_flags);
    fwrite(&array_flags_type, 4, 1, (FILE *)mat->fp);
    fwrite(&array_flags_size, 4, 1, (FILE *)mat->fp);
    fwrite(&array_flags, 4, 1, (FILE *)mat->fp);
    fwrite(&nzmax, 4, 1, (FILE *)mat->fp);
    /* Rank and Dimension */
    nBytes = matvar->rank * 4;
    fwrite(&dims_array_type, 4, 1, (FILE *)mat->fp);
    fwrite(&nBytes, 4, 1, (FILE *)mat->fp);
    for ( i = 0; i < matvar->rank; i++ ) {
        mat_int32_t dim;
        dim = matvar->dims[i];
        fwrite(&dim, 4, 1, (FILE *)mat->fp);
    }
    if ( matvar->rank % 2 != 0 )
        fwrite(&pad4, 4, 1, (FILE *)mat->fp);
    /* Name of variable */
    if ( !matvar->name ) {
        const mat_uint32_t array_name_type = MAT_T_INT8;
        fwrite(&array_name_type, 4, 1, (FILE *)mat->fp);
        fwrite(&pad4, 4, 1, (FILE *)mat->fp);
    } else if ( strlen(matvar->name) <= 4 ) {
        mat_uint32_t array_name_type = MAT_T_INT8;
        const mat_uint32_t array_name_len = (mat_uint32_t)strlen(matvar->name);
        array_name_type |= array_name_len << 16;
        fwrite(&array_name_type, 4, 1, (FILE *)mat->fp);
        fwrite(matvar->name, 1, array_name_len, (FILE *)mat->fp);
        for ( i = array_name_len; i < 4; i++ )
            fwrite(&pad1, 1, 1, (FILE *)mat->fp);
    } else {
        const mat_uint32_t array_name_type = MAT_T_INT8;
        const mat_uint32_t array_name_len = (mat_uint32_t)strlen(matvar->name);
        fwrite(&array_name_type, 4, 1, (FILE *)mat->fp);
        fwrite(&array_name_len, 4, 1, (FILE *)mat->fp);
        fwrite(matvar->name, 1, array_name_len, (FILE *)mat->fp);
        if ( array_name_len % 8 )
            for ( i = array_name_len % 8; i < 8; i++ )
                fwrite(&pad1, 1, 1, (FILE *)mat->fp);
    }

    WriteType(mat, matvar);
    end = ftell((FILE *)mat->fp);
    if ( start != -1L && end != -1L ) {
        nBytes = (int)(end - start);
        (void)fseek((FILE *)mat->fp, (long)-(nBytes + 4), SEEK_CUR);
        fwrite(&nBytes, 4, 1, (FILE *)mat->fp);
        (void)fseek((FILE *)mat->fp, end, SEEK_SET);
    } else {
        Mat_Critical("Couldn't determine file position");
    }

    return MATIO_E_NO_ERROR;
}


GetEmptyMatrixMaxBufSize(const char *name, int rank, size_t *size)
{
    int err = 0;
    size_t nBytes = 0, len, rank_size;
    size_t tag_size = 8, array_flags_size = 8;

    /* Add the Array Flags tag and space to the number of bytes */
    nBytes += tag_size + array_flags_size;

    /* Get size of variable name, pad it to an 8 byte block, and add it to nBytes */
    if ( NULL != name )
        len = strlen(name);
    else
        len = 4;

    if ( len <= 4 ) {
        nBytes += tag_size;
    } else {
        nBytes += tag_size;
        if ( len % 8 ) {
            err = Add(&len, len, 8 - len % 8);
            if ( err )
                return err;
        }

        err = Add(&nBytes, nBytes, len);
        if ( err )
            return err;
    }

    /* Add rank and dimensions, padded to an 8 byte block */
    err = Mul(&rank_size, rank, 4);
    if ( err )
        return err;
    if ( rank % 2 )
        err = Add(&nBytes, nBytes, tag_size + 4);
    else
        err = Add(&nBytes, nBytes, tag_size);
    if ( err )
        return err;

    err = Add(&nBytes, nBytes, rank_size);
    if ( err )
        return err;
    /* Data tag */
    err = Add(&nBytes, nBytes, tag_size);
    if ( err )
        return err;

    *size = nBytes;
    return MATIO_E_NO_ERROR;
}


GetCellArrayFieldBufSize(matvar_t *matvar, size_t *size)
{
    int err;
    size_t nBytes = 0, type_buf_size;
    size_t tag_size = 8, array_flags_size = 8;

    *size = 0;

    if ( matvar == NULL )
        return MATIO_E_BAD_ARGUMENT;

    /* Add the Array Flags tag and space to the number of bytes */
    nBytes += tag_size + array_flags_size;

    /* In an element of a cell array, the name is just a tag with 0 bytes */
    nBytes += tag_size;

    err = GetTypeBufSize(matvar, &type_buf_size);
    if ( err )
        return err;
    err = Add(&nBytes, nBytes, type_buf_size);
    if ( err )
        return err;

    *size = nBytes;
    return MATIO_E_NO_ERROR;
}